xref: /haiku/src/bin/multiuser/passwd.cpp (revision ef916ff3b5680299e79d6cd2316daaecfe729ece)
1 /*
2  * Copyright 2008-2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include <errno.h>
8 #include <getopt.h>
9 #include <pwd.h>
10 #include <shadow.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <termios.h>
15 #include <time.h>
16 #include <unistd.h>
17 
18 #include <OS.h>
19 
20 #include <RegistrarDefs.h>
21 #include <user_group.h>
22 #include <util/KMessage.h>
23 
24 #include <AutoDeleter.h>
25 
26 #include "multiuser_utils.h"
27 
28 
29 extern const char *__progname;
30 
31 
32 static const char* kUsage =
33 	"Usage: %s [ <options> ] [ <user name> ]\n"
34 	"Change the password of the specified user.\n"
35 	"\n"
36 	"Options:\n"
37 	"  -d\n"
38 	"    Delete the password for the specified user.\n"
39 	"  -h, --help\n"
40 	"    Print usage info.\n"
41 	;
42 
43 static void
print_usage_and_exit(bool error)44 print_usage_and_exit(bool error)
45 {
46 	fprintf(error ? stderr : stdout, kUsage, __progname);
47 	exit(error ? 1 : 0);
48 }
49 
50 
51 int
main(int argc,const char * const * argv)52 main(int argc, const char* const* argv)
53 {
54 	bool deletePassword = false;
55 
56 	while (true) {
57 		static struct option sLongOptions[] = {
58 			{ "help", no_argument, 0, 'h' },
59 			{ 0, 0, 0, 0 }
60 		};
61 
62 		opterr = 0; // don't print errors
63 		int c = getopt_long(argc, (char**)argv, "dh", sLongOptions, NULL);
64 		if (c == -1)
65 			break;
66 
67 
68 		switch (c) {
69 			case 'd':
70 				deletePassword = true;
71 				break;
72 
73 			case 'h':
74 				print_usage_and_exit(false);
75 				break;
76 
77 			default:
78 				print_usage_and_exit(true);
79 				break;
80 		}
81 	}
82 
83 	if (optind + 1 < argc)
84 		print_usage_and_exit(true);
85 
86 	const char* user = optind < argc ? argv[optind] : NULL;
87 
88 	if (geteuid() != 0) {
89 		fprintf(stderr, "Error: You need to be root.\n");
90 		exit(1);
91 	}
92 
93 	// this is a set-uid tool -- get the real UID
94 	uid_t uid = getuid();
95 
96 	if (deletePassword) {
97 		if (uid != 0) {
98 			fprintf(stderr, "Error: Only root can delete users' passwords.\n");
99 			exit(1);
100 		}
101 
102 		if (user == NULL) {
103 			fprintf(stderr, "Error: A user must be specified.\n");
104 			exit(1);
105 		}
106 	}
107 
108 	// get the passwd entry
109 	struct passwd* passwd;
110 	if (user != NULL) {
111 		passwd = getpwnam(user);
112 		if (passwd == NULL) {
113 			fprintf(stderr, "Error: No user with name \"%s\".\n", user);
114 			exit(1);
115 		}
116 
117 		if (uid != 0 && passwd->pw_uid != uid) {
118 			fprintf(stderr, "Error: Only root can change the passwd for other "
119 				"users.\n");
120 			exit(1);
121 		}
122 	} else {
123 		passwd = getpwuid(uid);
124 		if (passwd == NULL) {
125 			fprintf(stderr, "Error: Ugh! Couldn't get passwd entry for uid "
126 				"%d.\n", uid);
127 			exit(1);
128 		}
129 
130 		user = passwd->pw_name;
131 	}
132 
133 	// if not root, the user needs to authenticate
134 	if (uid != 0) {
135 		if (authenticate_user("old password: ", passwd, getspnam(user), 1,
136 				false) != B_OK) {
137 			exit(1);
138 		}
139 	}
140 
141 	char password[LINE_MAX];
142 	char* encryptedPassword;
143 
144 	if (deletePassword) {
145 		password[0] = '\0';
146 		encryptedPassword = password;
147 	} else {
148 		// read new password
149 		if (read_password("new password: ", password, sizeof(password), false)
150 				!= B_OK) {
151 			exit(1);
152 		}
153 
154 		if (strlen(password) >= MAX_SHADOW_PWD_PASSWORD_LEN) {
155 			fprintf(stderr, "Error: The password is too long.\n");
156 			exit(1);
157 		}
158 
159 		// read password again
160 		char repeatedPassword[LINE_MAX];
161 		if (read_password("repeat new password: ", repeatedPassword,
162 				sizeof(repeatedPassword), false) != B_OK) {
163 			exit(1);
164 		}
165 
166 		// passwords need to match
167 		if (strcmp(password, repeatedPassword) != 0) {
168 			fprintf(stderr, "Error: passwords don't match\n");
169 			exit(1);
170 		}
171 
172 		explicit_bzero(repeatedPassword, sizeof(repeatedPassword));
173 
174 		// crypt it
175 		encryptedPassword = crypt(password, NULL);
176 		explicit_bzero(password, sizeof(password));
177 	}
178 
179 	// prepare request for the registrar
180 	KMessage message(BPrivate::B_REG_UPDATE_USER);
181 	if (message.AddInt32("uid", passwd->pw_uid) != B_OK
182 		|| message.AddInt32("last changed", time(NULL)) != B_OK
183 		|| message.AddString("password", "x") != B_OK
184 		|| message.AddString("shadow password", encryptedPassword) != B_OK) {
185 		fprintf(stderr, "Error: Failed to construct message!\n");
186 		exit(1);
187 	}
188 
189 	// send the request
190 	KMessage reply;
191 	status_t error = send_authentication_request_to_registrar(message, reply);
192 	if (error != B_OK) {
193 		fprintf(stderr, "Error: Failed to set the password: %s\n",
194 			strerror(error));
195 		exit(1);
196 	}
197 
198 	return 0;
199 }
200