1/*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
7 * Reserved.  This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License').  You may not use this file
10 * except in compliance with the License.  Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <pwd.h>
28#include <errno.h>
29#include <string.h>
30#include "stringops.h"
31
32#define TEMP_FILE "/tmp/.pwtmp"
33
34#define _PASSWD_FILE "/etc/master.passwd"
35#define _COMPAT_FILE "/etc/passwd"
36#define _PASSWD_FIELDS 10
37#define BUFSIZE 8192
38
39extern void checkpasswd(char *, char *);
40
41char *
42_getline(FILE *fp)
43{
44	static char s[BUFSIZE];
45	int len;
46
47    s[0] = '\0';
48
49    fgets(s, BUFSIZE, fp);
50    if (s == NULL || s[0] == '\0') return NULL;
51
52	if (s[0] == '#') return s;
53
54	len = strlen(s) - 1;
55	s[len] = '\0';
56
57	return s;
58}
59
60struct passwd *
61parse_user(char *line)
62{
63	static struct passwd pw = {0};
64	char **tokens;
65	int i, len;
66
67	if (pw.pw_name != NULL) free(pw.pw_name);
68	pw.pw_name = NULL;
69	if (pw.pw_passwd != NULL) free(pw.pw_passwd);
70	pw.pw_passwd = NULL;
71	if (pw.pw_gecos != NULL) free(pw.pw_gecos);
72	pw.pw_gecos = NULL;
73	if (pw.pw_dir != NULL) free(pw.pw_dir);
74	pw.pw_dir = NULL;
75	if (pw.pw_shell != NULL) free(pw.pw_shell);
76	pw.pw_shell = NULL;
77
78	if (pw.pw_class != NULL) free(pw.pw_class);
79	pw.pw_class = NULL;
80
81	if (line == NULL) return (struct passwd *)NULL;
82	tokens = explode(line, ':');
83	len = listLength(tokens);
84
85	if (len != _PASSWD_FIELDS)
86	{
87		freeList(tokens);
88		return (struct passwd *)NULL;
89	}
90
91	i = 0;
92	pw.pw_name = tokens[i++];
93	pw.pw_passwd = tokens[i++];
94	pw.pw_uid = atoi(tokens[i]);
95	free(tokens[i++]);
96	pw.pw_gid = atoi(tokens[i]);
97	free(tokens[i++]);
98	pw.pw_class = tokens[i++];
99	pw.pw_change = atoi(tokens[i]);
100	free(tokens[i++]);
101	pw.pw_expire = atoi(tokens[i]);
102	free(tokens[i++]);
103	pw.pw_gecos = tokens[i++];
104	pw.pw_dir = tokens[i++];
105	pw.pw_shell = tokens[i++];
106
107	return &pw;
108}
109
110struct passwd *
111find_user(char *uname, FILE *fp)
112{
113	char *line;
114	struct passwd *pw;
115
116	rewind(fp);
117
118	while (NULL != (line = _getline(fp)))
119	{
120		if (line[0] == '#') continue;
121		pw = parse_user(line);
122		if (pw == (struct passwd *)NULL) continue;
123		if (!strcmp(uname, pw->pw_name)) return pw;
124	}
125
126	pw = parse_user(NULL);
127	return (struct passwd *)NULL;
128}
129
130void
131rewrite_file(char *pwname, FILE *fp, struct passwd *newpw)
132{
133	char *line;
134	struct passwd *pw;
135	FILE *tfp, *cfp;
136	char fname[256];
137
138	sprintf(fname, "%s.%d", TEMP_FILE, getpid());
139
140	tfp = fopen(fname, "w+");
141	if (tfp == NULL)
142	{
143		fprintf(stderr, "can't write temporary file \"%s\": ", fname);
144		perror("");
145		exit(1);
146	}
147
148	cfp = NULL;
149	if (!strcmp(pwname, _PASSWD_FILE))
150	{
151		cfp = fopen(_COMPAT_FILE, "w");
152		if (cfp == NULL)
153		{
154			fprintf(stderr, "warning: can't write compatability file \"%s\": ",
155				_COMPAT_FILE);
156			perror("");
157		}
158	}
159
160	if (cfp != NULL)
161	{
162		fprintf(cfp, "#\n");
163		fprintf(cfp, "# 4.3BSD-compatable User Database\n");
164		fprintf(cfp, "#\n");
165		fprintf(cfp, "# Note that this file is not consulted for login.\n");
166		fprintf(cfp, "# It only exisits for compatability with 4.3BSD utilities.\n");
167		fprintf(cfp, "#\n");
168		fprintf(cfp, "# This file is automatically re-written by various system utilities.\n");
169		fprintf(cfp, "# Do not edit this file.  Changes will be lost.\n");
170		fprintf(cfp, "#\n");
171	}
172
173	rewind(fp);
174
175	while (NULL != (line = _getline(fp)))
176	{
177		if (line[0] == '#')
178		{
179			fprintf(tfp, "%s", line);
180			continue;
181		}
182
183		pw = parse_user(line);
184		if (pw == (struct passwd *)NULL)
185		{
186			fprintf(stderr, "warning: bad format for entry: \"%s\"\n", line);
187			fprintf(tfp, "%s\n", line);
188			if (cfp != NULL) fprintf(cfp, "%s\n", line);
189			continue;
190		}
191
192		if (strcmp(newpw->pw_name, pw->pw_name))
193		{
194			fprintf(tfp, "%s\n", line);
195			if (cfp != NULL) fprintf(cfp, "%s\n", line);
196			continue;
197		}
198
199		fprintf(tfp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
200			newpw->pw_name, newpw->pw_passwd, newpw->pw_uid, newpw->pw_gid,
201			newpw->pw_class, newpw->pw_change, newpw->pw_expire,
202			newpw->pw_gecos, newpw->pw_dir, newpw->pw_shell);
203		if (cfp != NULL)
204		{
205			fprintf(cfp, "%s:",newpw->pw_name);
206			if ((newpw->pw_passwd == NULL) || (newpw->pw_passwd[0] == '\0'))
207				fprintf(cfp, ":");
208			else
209				fprintf(cfp, "*:");
210			fprintf(cfp, "%d:%d:%s:%s:%s\n",
211				newpw->pw_uid, newpw->pw_gid, newpw->pw_gecos,
212				newpw->pw_dir, newpw->pw_shell);
213		}
214	}
215
216	if (cfp != NULL) fclose(cfp);
217	fclose(fp);
218	if (unlink(pwname)  < 0)
219	{
220		fprintf(stderr, "can't update \"%s\": ", pwname);
221		perror("");
222	}
223
224	rewind(tfp);
225
226	fp = fopen(pwname, "w");
227	if (fp == NULL)
228	{
229		fprintf(stderr, "ERROR: lost file \"%s\"\n", pwname);
230		fprintf(stderr, "new passwd file is \"%s\"\n", fname);
231		perror("open");
232		exit(1);
233	}
234
235	while (NULL != (line = _getline(tfp)))
236	{
237		fprintf(fp, "%s", line);
238		if (line[0] != '#') fprintf(fp, "\n");
239	}
240	fclose(fp);
241	fclose(tfp);
242	unlink(fname);
243}
244
245int
246file_check_passwd(char *uname, char *locn)
247{
248	FILE *fp;
249	char *fname;
250	struct passwd *pw;
251
252	fname = _PASSWD_FILE;
253	if (locn != NULL) fname = locn;
254
255	if (access(fname,R_OK) || (fp = fopen(fname, "r")) == NULL)
256	{
257		fprintf(stderr, "can't read file \"%s\": ", fname);
258		perror("");
259		exit(1);
260	}
261
262
263	pw = find_user(uname, fp);
264	if (pw == (struct passwd *)NULL)
265	{
266		fprintf(stderr, "user %s not found in file %s\n", uname, fname);
267		exit(1);
268	}
269
270	checkpasswd(uname, pw->pw_passwd);
271	fclose(fp);
272
273	return 0;
274}
275