/* * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved. * Distributed under the terms of the MIT License. */ #include "multiuser_utils.h" #include #include #include #include #include #include #include #include status_t read_password(const char* prompt, char* password, size_t bufferSize, bool useStdio) { FILE* in = stdin; FILE* out = stdout; // open tty FileCloser tty; if (!useStdio) { // TODO: Open tty with O_NOCTTY! tty.SetTo(fopen("/dev/tty", "w+")); if (!tty.IsSet()) { fprintf(stderr, "Error: Failed to open tty: %s\n", strerror(errno)); return errno; } in = tty.Get(); out = tty.Get(); } // disable echo int inFD = fileno(in); struct termios termAttrs; if (tcgetattr(inFD, &termAttrs) != 0) { fprintf(in, "Error: Failed to get tty attributes: %s\n", strerror(errno)); return errno; } tcflag_t localFlags = termAttrs.c_lflag; termAttrs.c_lflag &= ~ECHO; if (tcsetattr(inFD, TCSANOW, &termAttrs) != 0) { fprintf(in, "Error: Failed to set tty attributes: %s\n", strerror(errno)); return errno; } status_t error = B_OK; // prompt and read pwd fputs(prompt, out); fflush(out); if (fgets(password, bufferSize, in) == NULL) { fprintf(out, "\nError: Failed to read from tty: %s\n", strerror(errno)); error = errno != 0 ? errno : B_ERROR; } else fputc('\n', out); // chop off trailing newline if (error == B_OK) { size_t len = strlen(password); if (len > 0 && password[len - 1] == '\n') password[len - 1] = '\0'; } // restore the terminal attributes termAttrs.c_lflag = localFlags; tcsetattr(inFD, TCSANOW, &termAttrs); return error; } bool verify_password(passwd* passwd, spwd* spwd, const char* plainPassword) { if (passwd == NULL) return false; // check whether we need to check the shadow password const char* requiredPassword = passwd->pw_passwd; if (strcmp(requiredPassword, "x") == 0) { if (spwd == NULL) { // Mmh, we're suppose to check the shadow password, but we don't // have it. Bail out. return false; } requiredPassword = spwd->sp_pwdp; } // If no password is required, we're done. if (requiredPassword == NULL || requiredPassword[0] == '\0') { if (plainPassword == NULL || plainPassword[0] == '\0') return true; return false; } // crypt and check it char* encryptedPassword = crypt(plainPassword, requiredPassword); return (strcmp(encryptedPassword, requiredPassword) == 0); } /*! Checks whether the user needs to authenticate with a password, and, if necessary, asks for it, and checks it. \a passwd must always be given, \a spwd only if there exists an entry for the user. */ status_t authenticate_user(const char* prompt, passwd* passwd, spwd* spwd, int maxTries, bool useStdio) { // check whether a password is need at all if (verify_password(passwd, spwd, "")) return B_OK; while (true) { // prompt the user for the password char plainPassword[MAX_SHADOW_PWD_PASSWORD_LEN]; status_t error = read_password(prompt, plainPassword, sizeof(plainPassword), useStdio); if (error != B_OK) return error; // check it bool ok = verify_password(passwd, spwd, plainPassword); explicit_bzero(plainPassword, sizeof(plainPassword)); if (ok) return B_OK; fprintf(stderr, "Incorrect password.\n"); if (--maxTries <= 0) return B_PERMISSION_DENIED; } } status_t authenticate_user(const char* prompt, const char* user, passwd** _passwd, spwd** _spwd, int maxTries, bool useStdio) { struct passwd* passwd = getpwnam(user); struct spwd* spwd = getspnam(user); status_t error = authenticate_user(prompt, passwd, spwd, maxTries, useStdio); if (error == B_OK) { if (_passwd) *_passwd = passwd; if (_spwd) *_spwd = spwd; } return error; } status_t setup_environment(struct passwd* passwd, bool preserveEnvironment, bool chngdir) { const char* term = getenv("TERM"); if (!preserveEnvironment) { static char *empty[1]; environ = empty; } // always preserve $TERM if (term != NULL) setenv("TERM", term, false); if (passwd->pw_shell) setenv("SHELL", passwd->pw_shell, true); if (passwd->pw_dir) setenv("HOME", passwd->pw_dir, true); setenv("USER", passwd->pw_name, true); pid_t pid = getpid(); // If stdin is not open, don't bother trying to TIOCSPGRP. (This is the // case when there is no PTY, e.g. for a noninteractive SSH session.) if (fcntl(STDIN_FILENO, F_GETFD) != -1) { if (ioctl(STDIN_FILENO, TIOCSPGRP, &pid) != 0) return errno; } if (passwd->pw_gid && setgid(passwd->pw_gid) != 0) return errno; if (passwd->pw_uid && setuid(passwd->pw_uid) != 0) return errno; if (chngdir) { const char* home = getenv("HOME"); if (home == NULL) return B_ENTRY_NOT_FOUND; if (chdir(home) != 0) return errno; } return B_OK; }