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