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
read_password(const char * prompt,char * password,size_t bufferSize,bool useStdio)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
verify_password(passwd * passwd,spwd * spwd,const char * plainPassword)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
authenticate_user(const char * prompt,passwd * passwd,spwd * spwd,int maxTries,bool useStdio)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
authenticate_user(const char * prompt,const char * user,passwd ** _passwd,spwd ** _spwd,int maxTries,bool useStdio)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
setup_environment(struct passwd * passwd,bool preserveEnvironment,bool chngdir)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