pw_copy.c revision 104744
1155324Simp/*-
2155324Simp * Copyright (c) 1990, 1993, 1994
3213496Scognet *	The Regents of the University of California.  All rights reserved.
4155324Simp *
5155324Simp * Redistribution and use in source and binary forms, with or without
6155324Simp * modification, are permitted provided that the following conditions
7155324Simp * are met:
8155324Simp * 1. Redistributions of source code must retain the above copyright
9155324Simp *    notice, this list of conditions and the following disclaimer.
10155324Simp * 2. Redistributions in binary form must reproduce the above copyright
11155324Simp *    notice, this list of conditions and the following disclaimer in the
12155324Simp *    documentation and/or other materials provided with the distribution.
13155324Simp * 3. All advertising materials mentioning features or use of this software
14185265Simp *    must display the following acknowledgement:
15185265Simp *	This product includes software developed by the University of
16185265Simp *	California, Berkeley and its contributors.
17185265Simp * 4. Neither the name of the University nor the names of its contributors
18185265Simp *    may be used to endorse or promote products derived from this software
19185265Simp *    without specific prior written permission.
20185265Simp *
21185265Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24185265Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25155324Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26155324Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27155324Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28155324Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29155324Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30155324Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31155324Simp * SUCH DAMAGE.
32155324Simp *
33155324Simp * $FreeBSD: head/release/picobsd/tinyware/passwd/pw_copy.c 104744 2002-10-10 00:32:55Z alfred $
34155324Simp */
35155324Simp
36155324Simp#ifndef lint
37155324Simpstatic const char sccsid[] = "@(#)pw_copy.c	8.4 (Berkeley) 4/2/94";
38155324Simp#endif /* not lint */
39155324Simp
40155324Simp#include <sys/cdefs.h>
41155324Simp__FBSDID("$FreeBSD: head/release/picobsd/tinyware/passwd/pw_copy.c 104744 2002-10-10 00:32:55Z alfred $");
42155324Simp
43155324Simp/*
44155324Simp * This module is used to copy the master password file, replacing a single
45259364Sian * record, by chpass(1) and passwd(1).
46155324Simp */
47155324Simp
48155324Simp#include <err.h>
49213496Scognet#include <pwd.h>
50213496Scognet#include <stdio.h>
51155324Simp#include <string.h>
52155324Simp#include <unistd.h>
53155324Simp
54178366Scognet#if 0
55178366Scognet#include <pw_scan.h>
56259364Sian#endif
57213496Scognetextern int      pw_big_ids_warning;
58213496Scognetextern int      pw_scan(char *, struct passwd *);
59187599Simp
60155324Simp#include <pw_util.h>
61155324Simp
62155324Simpextern char *tempname;
63155324Simp
64155324Simp/* for use in pw_copy(). Compare a pw entry to a pw struct. */
65155324Simpstatic int
66155324Simppw_equal(char *buf, struct passwd *pw)
67235718Simp{
68266097Sian	struct passwd buf_pw;
69155324Simp	int len;
70161704Scognet
71266097Sian	len = strlen (buf);
72266097Sian	if (buf[len-1] == '\n')
73161704Scognet		buf[len-1] = '\0';
74266097Sian	return (strcmp(pw->pw_name, buf_pw.pw_name) == 0
75155324Simp	    && pw->pw_uid == buf_pw.pw_uid
76155324Simp	    && pw->pw_gid == buf_pw.pw_gid
77155324Simp	    && strcmp(pw->pw_class, buf_pw.pw_class) == 0
78236989Simp	    && (long)pw->pw_change == (long)buf_pw.pw_change
79155324Simp	    && (long)pw->pw_expire == (long)buf_pw.pw_expire
80155324Simp	    && strcmp(pw->pw_gecos, buf_pw.pw_gecos) == 0
81155324Simp	    && strcmp(pw->pw_dir, buf_pw.pw_dir) == 0
82155324Simp	    && strcmp(pw->pw_shell, buf_pw.pw_shell) == 0);
83164425Ssam}
84155324Simp
85155324Simpvoid
86155324Simppw_copy(int ffd, int tfd, struct passwd *pw, struct passwd *old_pw)
87155324Simp{
88155324Simp	FILE *from, *to;
89155324Simp	int done;
90155324Simp	char *p, buf[8192];
91254025Sjeff	char uidstr[20];
92155324Simp	char gidstr[20];
93155324Simp	char chgstr[20];
94155324Simp	char expstr[20];
95155324Simp
96155324Simp	snprintf(uidstr, sizeof(uidstr), "%lu", (unsigned long)pw->pw_uid);
97155324Simp	snprintf(gidstr, sizeof(gidstr), "%lu", (unsigned long)pw->pw_gid);
98155324Simp	snprintf(chgstr, sizeof(chgstr), "%ld", (long)pw->pw_change);
99155324Simp	snprintf(expstr, sizeof(expstr), "%ld", (long)pw->pw_expire);
100155324Simp
101155324Simp	if (!(from = fdopen(ffd, "r")))
102155324Simp		pw_error(_PATH_MASTERPASSWD, 1, 1);
103156828Simp	if (!(to = fdopen(tfd, "w")))
104236989Simp		pw_error(tempname, 1, 1);
105156828Simp
106156828Simp	for (done = 0; fgets(buf, sizeof(buf), from);) {
107156828Simp		if (!strchr(buf, '\n')) {
108156828Simp			warnx("%s: line too long", _PATH_MASTERPASSWD);
109213496Scognet			pw_error(NULL, 0, 1);
110213496Scognet		}
111213496Scognet		if (done) {
112213496Scognet			(void)fprintf(to, "%s", buf);
113213496Scognet			if (ferror(to))
114213496Scognet				goto err;
115213496Scognet			continue;
116213496Scognet		}
117213496Scognet		for (p = buf; *p != '\n'; p++)
118213496Scognet			if (*p != ' ' && *p != '\t')
119213496Scognet				break;
120213496Scognet		if (*p == '#' || *p == '\n') {
121213496Scognet			(void)fprintf(to, "%s", buf);
122155324Simp			if (ferror(to))
123155324Simp				goto err;
124155324Simp			continue;
125155324Simp		}
126155324Simp		if (!(p = strchr(buf, ':'))) {
127155324Simp			warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
128155324Simp			pw_error(NULL, 0, 1);
129155324Simp		}
130155324Simp		*p = '\0';
131155324Simp		if (strcmp(buf, pw->pw_name)) {
132155324Simp			*p = ':';
133155324Simp			(void)fprintf(to, "%s", buf);
134155324Simp			if (ferror(to))
135155324Simp				goto err;
136155324Simp			continue;
137155324Simp		}
138155324Simp		*p = ':';
139156828Simp		if (old_pw && !pw_equal(buf, old_pw)) {
140155324Simp			warnx("%s: entry for %s has changed",
141155324Simp			      _PATH_MASTERPASSWD, pw->pw_name);
142155324Simp			pw_error(NULL, 0, 1);
143155324Simp		}
144155324Simp		(void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
145155324Simp		    pw->pw_name, pw->pw_passwd,
146155324Simp		    pw->pw_fields & _PWF_UID ? uidstr : "",
147155324Simp		    pw->pw_fields & _PWF_GID ? gidstr : "",
148155324Simp		    pw->pw_class,
149155324Simp		    pw->pw_fields & _PWF_CHANGE ? chgstr : "",
150155324Simp		    pw->pw_fields & _PWF_EXPIRE ? expstr : "",
151155324Simp		    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
152155324Simp		done = 1;
153155324Simp		if (ferror(to))
154158746Scognet			goto err;
155155324Simp	}
156155324Simp	if (!done) {
157155324Simp#ifdef YP
158155324Simp	/* Ultra paranoid: shouldn't happen. */
159155324Simp		if (getuid())  {
160155324Simp			warnx("%s: not found in %s -- permission denied",
161155324Simp					pw->pw_name, _PATH_MASTERPASSWD);
162155324Simp			pw_error(NULL, 0, 1);
163155324Simp		} else
164155324Simp#endif /* YP */
165155324Simp		(void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
166155324Simp		    pw->pw_name, pw->pw_passwd,
167155324Simp		    pw->pw_fields & _PWF_UID ? uidstr : "",
168155324Simp		    pw->pw_fields & _PWF_GID ? gidstr : "",
169155324Simp		    pw->pw_class,
170155324Simp		    pw->pw_fields & _PWF_CHANGE ? chgstr : "",
171155324Simp		    pw->pw_fields & _PWF_EXPIRE ? expstr : "",
172155324Simp		    pw->pw_gecos, pw->pw_dir, pw->pw_shell);
173155324Simp	}
174155324Simp
175155324Simp	if (ferror(to))
176155324Simperr:		pw_error(NULL, 1, 1);
177155324Simp	(void)fclose(to);
178155324Simp}
179155324Simp
180155324Simp#include <sys/param.h>
181155324Simp
182155324Simp#include <err.h>
183155324Simp#include <errno.h>
184155324Simp#include <fcntl.h>
185155324Simp#include <pwd.h>
186155324Simp#include <stdio.h>
187155324Simp#include <string.h>
188155324Simp#include <stdlib.h>
189155324Simp#include <unistd.h>
190155324Simp
191155324Simp
192155324Simp/*
193155324Simp * Some software assumes that IDs are short.  We should emit warnings
194184310Sstas * for id's which can not be stored in a short, but we are more liberal
195184310Sstas * by default, warning for IDs greater than USHRT_MAX.
196184310Sstas *
197184310Sstas * If pw_big_ids_warning is anything other than -1 on entry to pw_scan()
198184310Sstas * it will be set based on the existance of PW_SCAN_BIG_IDS in the
199184310Sstas * environment.
200184310Sstas */
201184310Sstasint     pw_big_ids_warning = -1;
202184310Sstas
203184310Sstasint
204184310Sstaspw_scan(bp, pw)
205184310Sstas        char *bp;
206184310Sstas        struct passwd *pw;
207184310Sstas{
208184310Sstas        uid_t id;
209184310Sstas        int root;
210184310Sstas        char *p, *sh;
211184310Sstas
212184310Sstas        if (pw_big_ids_warning == -1)
213184310Sstas                pw_big_ids_warning = getenv("PW_SCAN_BIG_IDS") == NULL ? 1 : 0;
214184310Sstas
215184310Sstas        pw->pw_fields = 0;
216184310Sstas        if (!(pw->pw_name = strsep(&bp, ":")))          /* login */
217184310Sstas                goto fmt;
218184310Sstas        root = !strcmp(pw->pw_name, "root");
219184310Sstas        if(pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0'))
220184310Sstas                pw->pw_fields |= _PWF_NAME;
221184310Sstas
222184310Sstas        if (!(pw->pw_passwd = strsep(&bp, ":")))        /* passwd */
223184310Sstas                goto fmt;
224184310Sstas        if(pw->pw_passwd[0]) pw->pw_fields |= _PWF_PASSWD;
225184310Sstas
226184310Sstas        if (!(p = strsep(&bp, ":")))                    /* uid */
227184310Sstas                goto fmt;
228184310Sstas        if (p[0])
229184310Sstas                pw->pw_fields |= _PWF_UID;
230155324Simp        else {
231155324Simp                if (pw->pw_name[0] != '+' && pw->pw_name[0] != '-') {
232155324Simp                        warnx("no uid for user %s", pw->pw_name);
233155324Simp                        return (0);
234155324Simp                }
235213496Scognet        }
236266065Sian        id = strtoul(p, (char **)NULL, 10);
237265999Sian        if (errno == ERANGE) {
238155324Simp                warnx("%s > max uid value (%lu)", p, ULONG_MAX);
239155324Simp                return (0);
240155324Simp        }
241155324Simp        if (root && id) {
242155324Simp                warnx("root uid should be 0");
243155324Simp                return (0);
244155324Simp        }
245155324Simp        if (pw_big_ids_warning && id > USHRT_MAX) {
246155324Simp                warnx("%s > recommended max uid value (%u)", p, USHRT_MAX);
247238390Simp                /*return (0);*/ /* THIS SHOULD NOT BE FATAL! */
248238390Simp        }
249238390Simp        pw->pw_uid = id;
250238390Simp
251238390Simp        if (!(p = strsep(&bp, ":")))                    /* gid */
252238390Simp                goto fmt;
253238390Simp        if(p[0]) pw->pw_fields |= _PWF_GID;
254238390Simp        id = strtoul(p, (char **)NULL, 10);
255238390Simp        if (errno == ERANGE) {
256238390Simp                warnx("%s > max gid value (%u)", p, ULONG_MAX);
257238390Simp                return (0);
258238390Simp        }
259155324Simp        if (pw_big_ids_warning && id > USHRT_MAX) {
260155324Simp                warnx("%s > recommended max gid value (%u)", p, USHRT_MAX);
261155324Simp                /* return (0); This should not be fatal! */
262155324Simp        }
263259364Sian        pw->pw_gid = id;
264238389Simp
265155324Simp        pw->pw_class = strsep(&bp, ":");                /* class */
266265999Sian        if(pw->pw_class[0]) pw->pw_fields |= _PWF_CLASS;
267265999Sian
268155324Simp        if (!(p = strsep(&bp, ":")))                    /* change */
269155324Simp                goto fmt;
270213496Scognet        if(p[0]) pw->pw_fields |= _PWF_CHANGE;
271238389Simp        pw->pw_change = atol(p);
272155324Simp
273213496Scognet        if (!(p = strsep(&bp, ":")))                    /* expire */
274155324Simp                goto fmt;
275164432Simp        if(p[0]) pw->pw_fields |= _PWF_EXPIRE;
276155324Simp        pw->pw_expire = atol(p);
277155324Simp
278155324Simp        if (!(pw->pw_gecos = strsep(&bp, ":")))         /* gecos */
279213496Scognet                goto fmt;
280213496Scognet        if(pw->pw_gecos[0]) pw->pw_fields |= _PWF_GECOS;
281213496Scognet
282213496Scognet        if (!(pw->pw_dir = strsep(&bp, ":")))                   /* directory */
283155324Simp                goto fmt;
284235718Simp        if(pw->pw_dir[0]) pw->pw_fields |= _PWF_DIR;
285213496Scognet
286213496Scognet        if (!(pw->pw_shell = strsep(&bp, ":")))         /* shell */
287213496Scognet                goto fmt;
288155324Simp
289155324Simp        p = pw->pw_shell;
290238389Simp        if (root && *p)                                 /* empty == /bin/sh */
291238389Simp                for (setusershell();;) {
292238389Simp                        if (!(sh = getusershell())) {
293238389Simp                                warnx("warning, unknown root shell");
294238389Simp                                break;
295238389Simp                        }
296238389Simp                        if (!strcmp(p, sh))
297238389Simp                                break;
298238389Simp                }
299238389Simp        if(p[0]) pw->pw_fields |= _PWF_SHELL;
300238389Simp
301238389Simp        if ((p = strsep(&bp, ":"))) {                   /* too many */
302238389Simpfmt:            warnx("corrupted entry");
303238389Simp                return (0);
304238389Simp        }
305156828Simp        return (1);
306238389Simp}
307238389Simp