1/*
2 * Copyright (c) 1999-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include <ctype.h>
24#include <err.h>
25#include <errno.h>
26#include <fcntl.h>
27#include <pwd.h>
28#include <signal.h>
29#include <stdbool.h>
30#include <stdlib.h>
31#include <stdio.h>
32#include <string.h>
33#include <sysexits.h>
34#include <unistd.h>
35#include <sys/time.h>
36#include <sys/stat.h>
37#include "passwd.h"
38
39#define _PASSWD_FILE "/etc/master.passwd"
40#define _COMPAT_FILE "/etc/passwd"
41#define _PASSWD_FIELDS 10
42#define BUFSIZE 8192
43
44void getpasswd(char *, int, int, int, int, char *, char **, char**, char **);
45
46static struct passwd *
47parse_user(char *line, size_t len)
48{
49	static struct passwd pw;
50	int i,j;
51	char *tokens[_PASSWD_FIELDS];
52	char *token = NULL;
53	bool comment = true;
54
55	free(pw.pw_name);
56	free(pw.pw_passwd);
57	free(pw.pw_class);
58	free(pw.pw_gecos);
59	free(pw.pw_dir);
60	free(pw.pw_shell);
61	memset(&pw, 0, sizeof(pw));
62
63	if (line == NULL) return NULL;
64
65	memset(&tokens, 0, sizeof(char *) * _PASSWD_FIELDS);
66
67	for (i = 0, j = 0; i < len && j < _PASSWD_FIELDS; ++i) {
68		int c = line[i];
69		if (!isspace(c) && c != '#') {
70			comment = false;
71		}
72		if (!comment && token == NULL) {
73			// start a new token
74			token = &line[i];
75		} else if (token && (c == ':' || c == '\n')) {
76			// end the current token
77			// special case for empty token
78			while (token[0] == ':' && token < &line[i]) {
79				tokens[j++] = strdup("");
80				++token;
81			}
82			tokens[j++] = strndup(token, &line[i] - token);
83			token = NULL;
84		}
85	}
86
87	if (comment || j != _PASSWD_FIELDS) return NULL;
88
89	j = 0;
90	pw.pw_name = tokens[j++];
91	pw.pw_passwd = tokens[j++];
92	pw.pw_uid = atoi(tokens[j]);
93	free(tokens[j++]);
94	pw.pw_gid = atoi(tokens[j]);
95	free(tokens[j++]);
96	pw.pw_class = tokens[j++];
97	pw.pw_change = atoi(tokens[j]);
98	free(tokens[j++]);
99	pw.pw_expire = atoi(tokens[j]);
100	free(tokens[j++]);
101	pw.pw_gecos = tokens[j++];
102	pw.pw_dir = tokens[j++];
103	pw.pw_shell = tokens[j++];
104
105	return &pw;
106}
107
108static struct passwd *
109find_user(FILE *fp, char *uname)
110{
111	size_t len;
112	char *line;
113
114	rewind(fp);
115
116	while ((line = fgetln(fp, &len)) != NULL) {
117		struct passwd *pw = parse_user(line, len);
118		if (pw && strcmp(uname, pw->pw_name) == 0) {
119			return pw;
120		}
121	}
122	return NULL;
123}
124
125static void
126rewrite_file(char *path, FILE *fp, struct passwd *newpw)
127{
128	int fd;
129	char *line;
130	size_t len;
131	FILE *tfp = NULL;
132	char *tempname = NULL; // temporary master.passwd file
133
134	asprintf(&tempname, "%s.XXXXXX", path);
135
136	fd = mkstemp(tempname);
137	if (fd == -1) {
138		err(EXIT_FAILURE, "%s", tempname);
139	}
140	tfp = fdopen(fd, "w+");
141	if (tfp == NULL || fchmod(fd, S_IRUSR | S_IWUSR) != 0) {
142		int save = errno;
143		unlink(tempname);
144		errno = save;
145		err(EXIT_FAILURE, "%s", tempname);
146	}
147
148	while ((line = fgetln(fp, &len)) != NULL) {
149		struct passwd *pw = parse_user(line, len);
150
151		// if this is not the entry we're looking for or if parsing
152		// failed (likely a comment) then print the entry as is.
153		if (pw == NULL || strcmp(newpw->pw_name, pw->pw_name) != 0) {
154			fwrite(line, sizeof(char), len, tfp);
155		} else {
156			fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
157				newpw->pw_name,
158				newpw->pw_passwd,
159				newpw->pw_uid,
160				newpw->pw_gid,
161				newpw->pw_class,
162				newpw->pw_change,
163				newpw->pw_expire,
164				newpw->pw_gecos,
165				newpw->pw_dir,
166				newpw->pw_shell);
167		}
168	}
169
170	// Move the temporary file into place.
171	if (fclose(tfp) != 0 || rename(tempname, path) != 0) {
172		int save = errno;
173		unlink(tempname);
174		errno = save;
175		err(EXIT_FAILURE, "%s", tempname);
176	}
177
178	free(tempname);
179}
180
181int
182file_passwd(char *uname, char *locn)
183{
184	char *ne, *oc, *nc;
185	int fd;
186	FILE *fp;
187	uid_t uid;
188	char *fname;
189	struct passwd *pw;
190	struct passwd newpw;
191
192	fname = _PASSWD_FILE;
193	if (locn != NULL) fname = locn;
194
195	fd = open(fname, O_RDONLY | O_EXLOCK);
196	if (fd == -1) {
197		err(EXIT_FAILURE, "%s", fname);
198	}
199
200	fp = fdopen(fd, "r");
201	if (fp == NULL) {
202		err(EXIT_FAILURE, "%s", fname);
203	}
204
205	pw = find_user(fp, uname);
206	if (pw == NULL) {
207		errx(EXIT_FAILURE, "user %s not found in %s", uname, fname);
208	}
209
210	uid = getuid();
211	if (uid != 0 && uid != pw->pw_uid) {
212		errno = EACCES;
213		err(EXIT_FAILURE, "%s", uname);
214	}
215
216	// Get the password
217	getpasswd(uname, (uid == 0), 5, 0, 0, pw->pw_passwd, &ne, &oc, &nc);
218
219	newpw.pw_name = strdup(pw->pw_name);
220	newpw.pw_passwd = strdup(ne);
221	newpw.pw_uid = pw->pw_uid;
222	newpw.pw_gid = pw->pw_gid;
223	newpw.pw_class = strdup(pw->pw_class);
224	newpw.pw_change = pw->pw_change;
225	newpw.pw_expire = pw->pw_expire;
226	newpw.pw_gecos = strdup(pw->pw_gecos);
227	newpw.pw_dir = strdup(pw->pw_dir);
228	newpw.pw_shell = strdup(pw->pw_shell);
229
230	// Rewrite the file
231	rewind(fp);
232	rewrite_file(fname, fp, &newpw);
233
234	fclose(fp);
235
236	return 0;
237}
238