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/*	$OpenBSD: yppasswdd_mkpw.c,v 1.16 1997/11/17 23:56:20 gene Exp $	*/
25
26/*
27 * Copyright (c) 1994 Mats O Jansson <moj@stacken.kth.se>
28 * All rights reserved.
29 *
30 * Redistribution and use in source and binary forms, with or without
31 * modification, are permitted provided that the following conditions
32 * are met:
33 * 1. Redistributions of source code must retain the above copyright
34 *    notice, this list of conditions and the following disclaimer.
35 * 2. Redistributions in binary form must reproduce the above copyright
36 *    notice, this list of conditions and the following disclaimer in the
37 *    documentation and/or other materials provided with the distribution.
38 * 3. All advertising materials mentioning features or use of this software
39 *    must display the following acknowledgement:
40 *	This product includes software developed by Mats O Jansson
41 * 4. The name of the author may not be used to endorse or promote products
42 *    derived from this software without specific prior written permission.
43 *
44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
45 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
46 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
48 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
54 * SUCH DAMAGE.
55 */
56
57#include <sys/cdefs.h>
58#ifndef LINT
59__unused static char rcsid[] = "$OpenBSD: yppasswdd_mkpw.c,v 1.16 1997/11/17 23:56:20 gene Exp $";
60#endif
61
62#include <sys/param.h>
63#include <sys/types.h>
64#include <sys/stat.h>
65#include <stdio.h>
66#include <fcntl.h>
67#include <rpc/rpc.h>
68#include <rpcsvc/yppasswd.h>
69#include <db.h>
70#include <pwd.h>
71#include <stdlib.h>
72#include <unistd.h>
73#include <util.h>
74#include <ctype.h>
75#include <string.h>
76#include <syslog.h>
77#include <err.h>
78
79extern int noshell;
80extern int nogecos;
81extern int nopw;
82extern int make;
83extern char make_arg[];
84
85extern int pw_lock(int);
86extern int pw_mkdb(void);
87extern void pw_init(void);
88extern void pw_error(const char *, int, int);
89static void _pw_copy(int, int, struct passwd *);
90
91/* This is imported from OpenBSD's libutil because it's argument
92 * incompatible with NetBSD's. However, the NetBSD libutil is
93 * at least what the prototypes suggest is in System.framework,
94 * even though I can't find the code. I assume it will be there
95 * eventually. We need to use NetBSD's because it works with the
96 * pwd_mkdb binary that's shipped with Rhapsody. This is an area
97 * where OpenBSD diverges; however, we wanted to keep the OpenBSD
98 * rpc.yppasswdd because the rest of our YP code is from OpenBSD.
99 * What a mess.
100 */
101static void
102_pw_copy(ffd, tfd, pw)
103	int ffd, tfd;
104	struct passwd *pw;
105{
106	FILE   *from, *to;
107	int	done;
108	char   *p, buf[8192];
109
110	if (!(from = fdopen(ffd, "r")))
111		pw_error(_PATH_MASTERPASSWD, 1, 1);
112	if (!(to = fdopen(tfd, "w")))
113		pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1);
114
115	for (done = 0; fgets(buf, sizeof(buf), from);) {
116		if (!strchr(buf, '\n')) {
117			warnx("%s: line too long", _PATH_MASTERPASSWD);
118			pw_error(NULL, 0, 1);
119		}
120		if (done) {
121			(void)fprintf(to, "%s", buf);
122			if (ferror(to))
123				goto err;
124			continue;
125		}
126		if (!(p = strchr(buf, ':'))) {
127			warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
128			pw_error(NULL, 0, 1);
129		}
130		*p = '\0';
131		if (strcmp(buf, pw->pw_name)) {
132			*p = ':';
133			(void)fprintf(to, "%s", buf);
134			if (ferror(to))
135				goto err;
136			continue;
137		}
138		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
139		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
140		    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
141		    pw->pw_dir, pw->pw_shell);
142		done = 1;
143		if (ferror(to))
144			goto err;
145	}
146	if (!done)
147		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
148		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
149		    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
150		    pw->pw_dir, pw->pw_shell);
151
152	if (ferror(to))
153err:
154	pw_error(NULL, 0, 1);
155	(void)fclose(to);
156}
157
158
159int
160badchars(base)
161	char *base;
162{
163	int ampr = 0;
164	char *s;
165
166	for (s = base; *s; s++) {
167		if (*s == '&')
168			ampr++;
169		if (!isprint(*s))
170			return 1;
171		if (strchr(":\n\t\r", *s))
172			return 1;
173	}
174	if (ampr > 10)
175		return 1;
176	return 0;
177}
178
179int
180subst(s, from, to)
181	char *s;
182	char from, to;
183{
184	int	n = 0;
185
186	while (*s) {
187		if (*s == from) {
188			*s = to;
189			n++;
190		}
191		s++;
192	}
193	return (n);
194}
195
196int
197make_passwd(argp)
198	yppasswd *argp;
199{
200	struct passwd pw;
201	int     pfd, tfd;
202	char	buf[10], *bp = NULL, *p, *t;
203	int	n;
204	ssize_t cnt;
205	size_t	resid;
206	struct stat st;
207
208	pw_init();
209	pfd = open(_PATH_MASTERPASSWD, O_RDONLY);
210	if (pfd < 0)
211		goto fail;
212	if (fstat(pfd, &st))
213		goto fail;
214	p = bp = malloc((resid = st.st_size) + 1);
215	do {
216		cnt = read(pfd, p, resid);
217		if (cnt < 0)
218			goto fail;
219		p += cnt;
220		resid -= cnt;
221	} while (resid > 0);
222	close(pfd);
223	pfd = -1;
224	*p = '\0';		/* Buf oflow prevention */
225
226	p = bp;
227	subst(p, '\n', '\0');
228	for (n = 1; p < bp + st.st_size; n++, p = t) {
229		t = strchr(p, '\0') + 1;
230		/* Rhapsody allows the passwd file to have comments in it. */
231		if (p[0] == '#') {
232			continue;
233		}
234		cnt = subst(p, ':', '\0');
235		if (cnt != 9) {
236			syslog(LOG_WARNING, "bad entry at line %d of %s", n,
237			    _PATH_MASTERPASSWD);
238			continue;
239		}
240
241		if (strcmp(p, argp->newpw.pw_name) == 0)
242			break;
243	}
244	if (p >= bp + st.st_size)
245		goto fail;
246
247#define	EXPAND(e)	e = p; while (*p++) {}
248	EXPAND(pw.pw_name);
249	EXPAND(pw.pw_passwd);
250	pw.pw_uid = atoi(p); EXPAND(t);
251	pw.pw_gid = atoi(p); EXPAND(t);
252	EXPAND(pw.pw_class);
253	pw.pw_change = (time_t)atol(p); EXPAND(t);
254	pw.pw_expire = (time_t)atol(p); EXPAND(t);
255	EXPAND(pw.pw_gecos);
256	EXPAND(pw.pw_dir);
257	EXPAND(pw.pw_shell);
258
259	/* crypt() is broken under Rhapsody. It doesn't deal with
260	 * empty keys or salts like other Unices.
261	 */
262	if (pw.pw_passwd[0] != '\0' && argp->oldpass != NULL && argp->oldpass[0] != '\0') {
263		if (strcmp(crypt(argp->oldpass, pw.pw_passwd), pw.pw_passwd) != 0)
264			goto fail;
265	}
266
267	if (!nopw && badchars(argp->newpw.pw_passwd))
268		goto fail;
269	if (!nogecos && badchars(argp->newpw.pw_gecos))
270		goto fail;
271	if (!nogecos && badchars(argp->newpw.pw_shell))
272		goto fail;
273
274	/*
275	 * Get the new password.  Reset passwd change time to zero; when
276	 * classes are implemented, go and get the "offset" value for this
277	 * class and reset the timer.
278	 */
279	if (!nopw) {
280		pw.pw_passwd = argp->newpw.pw_passwd;
281		pw.pw_change = 0;
282	}
283	if (!nogecos)
284		pw.pw_gecos = argp->newpw.pw_gecos;
285	if (!noshell)
286		pw.pw_shell = argp->newpw.pw_shell;
287
288	for (n = 0, p = pw.pw_gecos; *p; p++)
289		if (*p == '&')
290			n = n + strlen(pw.pw_name) - 1;
291	if (strlen(pw.pw_name) + 1 + strlen(pw.pw_passwd) + 1 +
292	    strlen((sprintf(buf, "%d", pw.pw_uid), buf)) + 1 +
293	    strlen((sprintf(buf, "%d", pw.pw_gid), buf)) + 1 +
294	    strlen(pw.pw_gecos) + n + 1 + strlen(pw.pw_dir) + 1 +
295	    strlen(pw.pw_shell) >= 1023)
296		goto fail;
297
298	pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
299	if (pfd < 0) {
300		syslog(LOG_ERR, "cannot open %s", _PATH_MASTERPASSWD);
301		goto fail;
302	}
303
304	tfd = pw_lock(0);
305	if (tfd < 0)
306		goto fail;
307
308	_pw_copy(pfd, tfd, &pw);
309	pw_mkdb();
310	free(bp);
311
312	if (fork() == 0) {
313		chdir("/var/yp");
314		(void)umask(022);
315		system(make_arg);
316		exit(0);
317	}
318	return (0);
319
320fail:
321	if (bp)
322		free(bp);
323	if (pfd >= 0)
324		close(pfd);
325	return (1);
326}
327