1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "multiuser_utils.h" 7 8 #include <errno.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <termios.h> 13 #include <unistd.h> 14 15 #include <AutoDeleter.h> 16 17 #include <user_group.h> 18 19 20 status_t 21 read_password(const char* prompt, char* password, size_t bufferSize, 22 bool useStdio) 23 { 24 FILE* in = stdin; 25 FILE* out = stdout; 26 27 // open tty 28 FILE* tty = NULL; 29 if (!useStdio) { 30 // TODO: Open tty with O_NOCTTY! 31 tty = fopen("/dev/tty", "w+"); 32 if (tty == NULL) { 33 fprintf(stderr, "Error: Failed to open tty: %s\n", strerror(errno)); 34 return errno; 35 } 36 37 in = tty; 38 out = tty; 39 } 40 CObjectDeleter<FILE, int> ttyCloser(tty, fclose); 41 42 // disable echo 43 int inFD = fileno(in); 44 struct termios termAttrs; 45 if (tcgetattr(inFD, &termAttrs) != 0) { 46 fprintf(in, "Error: Failed to get tty attributes: %s\n", 47 strerror(errno)); 48 return errno; 49 } 50 51 tcflag_t localFlags = termAttrs.c_lflag; 52 termAttrs.c_lflag &= ~ECHO; 53 54 if (tcsetattr(inFD, TCSANOW, &termAttrs) != 0) { 55 fprintf(in, "Error: Failed to set tty attributes: %s\n", 56 strerror(errno)); 57 return errno; 58 } 59 60 status_t error = B_OK; 61 62 // prompt and read pwd 63 fprintf(out, prompt); 64 fflush(out); 65 66 if (fgets(password, bufferSize, in) == NULL) { 67 fprintf(out, "\nError: Failed to read from tty: %s\n", strerror(errno)); 68 error = errno != 0 ? errno : B_ERROR; 69 } else 70 fputc('\n', out); 71 72 // chop off trailing newline 73 if (error == B_OK) { 74 size_t len = strlen(password); 75 if (len > 0 && password[len - 1] == '\n') 76 password[len - 1] = '\0'; 77 } 78 79 // restore the terminal attributes 80 termAttrs.c_lflag = localFlags; 81 tcsetattr(inFD, TCSANOW, &termAttrs); 82 83 return error; 84 } 85 86 87 bool 88 verify_password(passwd* passwd, spwd* spwd, const char* plainPassword) 89 { 90 if (passwd == NULL) 91 return false; 92 93 // check whether we need to check the shadow password 94 const char* requiredPassword = passwd->pw_passwd; 95 if (strcmp(requiredPassword, "x") == 0) { 96 if (spwd == NULL) { 97 // Mmh, we're suppose to check the shadow password, but we don't 98 // have it. Bail out. 99 return false; 100 } 101 102 requiredPassword = spwd->sp_pwdp; 103 } 104 105 // If no password is required, we're done. 106 if (requiredPassword == NULL || requiredPassword[0] == '\0') { 107 if (plainPassword == NULL || plainPassword[0] == '\0') 108 return true; 109 110 return false; 111 } 112 113 // crypt and check it 114 char* encryptedPassword = crypt(plainPassword, requiredPassword); 115 116 return (strcmp(encryptedPassword, requiredPassword) == 0); 117 } 118 119 120 /*! Checks whether the user needs to authenticate with a password, and, if 121 necessary, asks for it, and checks it. 122 \a passwd must always be given, \a spwd only if there exists an entry 123 for the user. 124 */ 125 status_t 126 authenticate_user(const char* prompt, passwd* passwd, spwd* spwd, int maxTries, 127 bool useStdio) 128 { 129 // check whether a password is need at all 130 if (verify_password(passwd, spwd, "")) 131 return B_OK; 132 133 while (true) { 134 // prompt the user for the password 135 char plainPassword[MAX_SHADOW_PWD_PASSWORD_LEN]; 136 status_t error = read_password(prompt, plainPassword, 137 sizeof(plainPassword), useStdio); 138 if (error != B_OK) 139 return error; 140 141 // check it 142 bool ok = verify_password(passwd, spwd, plainPassword); 143 explicit_bzero(plainPassword, sizeof(plainPassword)); 144 if (ok) 145 return B_OK; 146 147 fprintf(stderr, "Incorrect password.\n"); 148 if (--maxTries <= 0) 149 return B_PERMISSION_DENIED; 150 } 151 } 152 153 154 status_t 155 authenticate_user(const char* prompt, const char* user, passwd** _passwd, 156 spwd** _spwd, int maxTries, bool useStdio) 157 { 158 struct passwd* passwd = getpwnam(user); 159 struct spwd* spwd = getspnam(user); 160 161 status_t error = authenticate_user(prompt, passwd, spwd, maxTries, 162 useStdio); 163 if (error == B_OK) { 164 if (_passwd) 165 *_passwd = passwd; 166 if (_spwd) 167 *_spwd = spwd; 168 } 169 170 return error; 171 } 172 173 174 status_t 175 setup_environment(struct passwd* passwd, bool preserveEnvironment, bool chngdir) 176 { 177 const char* term = getenv("TERM"); 178 if (!preserveEnvironment) { 179 static char *empty[1]; 180 environ = empty; 181 } 182 183 // always preserve $TERM 184 if (term != NULL) 185 setenv("TERM", term, false); 186 if (passwd->pw_shell) 187 setenv("SHELL", passwd->pw_shell, true); 188 if (passwd->pw_dir) 189 setenv("HOME", passwd->pw_dir, true); 190 191 setenv("USER", passwd->pw_name, true); 192 193 pid_t pid = getpid(); 194 // If stdin is not open, don't bother trying to TIOCSPGRP. (This is the 195 // case when there is no PTY, e.g. for a noninteractive SSH session.) 196 if (fcntl(STDIN_FILENO, F_GETFD) != -1) { 197 if (ioctl(STDIN_FILENO, TIOCSPGRP, &pid) != 0) 198 return errno; 199 } 200 201 if (passwd->pw_gid && setgid(passwd->pw_gid) != 0) 202 return errno; 203 204 if (passwd->pw_uid && setuid(passwd->pw_uid) != 0) 205 return errno; 206 207 if (chngdir) { 208 const char* home = getenv("HOME"); 209 if (home == NULL) 210 return B_ENTRY_NOT_FOUND; 211 212 if (chdir(home) != 0) 213 return errno; 214 } 215 216 return B_OK; 217 } 218