xref: /haiku/src/bin/multiuser/multiuser_utils.cpp (revision cbe0a0c436162d78cc3f92a305b64918c839d079)
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