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