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