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