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 || strlen(requiredPassword) == 0) 107 return true; 108 109 // crypt and check it 110 char* encryptedPassword = crypt(plainPassword, requiredPassword); 111 112 return (strcmp(encryptedPassword, requiredPassword) == 0); 113 } 114 115 116 /*! Checks whether the user needs to authenticate with a password, and, if 117 necessary, asks for it, and checks it. 118 \a passwd must always be given, \a spwd only if there exists an entry 119 for the user. 120 */ 121 status_t 122 authenticate_user(const char* prompt, passwd* passwd, spwd* spwd, int maxTries, 123 bool useStdio) 124 { 125 // check whether a password is need at all 126 if (verify_password(passwd, spwd, "")) 127 return B_OK; 128 129 while (true) { 130 // prompt the user for the password 131 char plainPassword[MAX_SHADOW_PWD_PASSWORD_LEN]; 132 status_t error = read_password(prompt, plainPassword, 133 sizeof(plainPassword), useStdio); 134 if (error != B_OK) 135 return error; 136 137 // check it 138 bool ok = verify_password(passwd, spwd, plainPassword); 139 memset(plainPassword, 0, sizeof(plainPassword)); 140 if (ok) 141 return B_OK; 142 143 fprintf(stderr, "Incorrect password.\n"); 144 if (--maxTries <= 0) 145 return B_PERMISSION_DENIED; 146 } 147 } 148 149 150 status_t 151 authenticate_user(const char* prompt, const char* user, passwd** _passwd, 152 spwd** _spwd, int maxTries, bool useStdio) 153 { 154 struct passwd* passwd = getpwnam(user); 155 struct spwd* spwd = getspnam(user); 156 157 status_t error = authenticate_user(prompt, passwd, spwd, maxTries, 158 useStdio); 159 if (error == B_OK) { 160 if (_passwd) 161 *_passwd = passwd; 162 if (_spwd) 163 *_spwd = spwd; 164 } 165 166 return error; 167 } 168