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
29extern const char *__progname;
30
31
32static 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
43static void
44print_usage_and_exit(bool error)
45{
46	fprintf(error ? stderr : stdout, kUsage, __progname);
47	exit(error ? 1 : 0);
48}
49
50
51int
52main(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