chpass.c revision 114594
1/*-
2 * Copyright (c) 1988, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * Portions of this software were developed for the FreeBSD Project by
8 * ThinkSec AS and NAI Labs, the Security Research Division of Network
9 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10 * ("CBOSS"), as part of the DARPA CHATS research program.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. All advertising materials mentioning features or use of this software
21 *    must display the following acknowledgement:
22 *	This product includes software developed by the University of
23 *	California, Berkeley and its contributors.
24 * 4. Neither the name of the University nor the names of its contributors
25 *    may be used to endorse or promote products derived from this software
26 *    without specific prior written permission.
27 *
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 * SUCH DAMAGE.
39 */
40
41#if 0
42#ifndef lint
43static const char copyright[] =
44"@(#) Copyright (c) 1988, 1993, 1994\n\
45	The Regents of the University of California.  All rights reserved.\n";
46#endif /* not lint */
47
48#ifndef lint
49static char sccsid[] = "@(#)chpass.c	8.4 (Berkeley) 4/2/94";
50#endif /* not lint */
51#endif
52#include <sys/cdefs.h>
53__FBSDID("$FreeBSD: head/usr.bin/chpass/chpass.c 114594 2003-05-03 19:44:46Z obrien $");
54
55#include <sys/param.h>
56#include <sys/stat.h>
57#include <sys/signal.h>
58#include <sys/time.h>
59#include <sys/resource.h>
60
61#include <ctype.h>
62#include <err.h>
63#include <errno.h>
64#include <fcntl.h>
65#include <pwd.h>
66#include <stdio.h>
67#include <stdlib.h>
68#include <string.h>
69#include <unistd.h>
70#ifdef YP
71#include <ypclnt.h>
72#endif
73
74#include <pw_scan.h>
75#include <libutil.h>
76
77#include "chpass.h"
78
79int master_mode;
80
81static void	baduser(void);
82static void	usage(void);
83
84int
85main(int argc, char *argv[])
86{
87	enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
88	struct passwd lpw, *old_pw, *pw;
89	int ch, pfd, tfd;
90	const char *password;
91	char *arg = NULL;
92	uid_t uid;
93#ifdef YP
94	struct ypclnt *ypclnt;
95	const char *yp_domain = NULL, *yp_host = NULL;
96#endif
97
98	pw = old_pw = NULL;
99	op = EDITENTRY;
100#ifdef YP
101	while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
102#else
103	while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
104#endif
105		switch (ch) {
106		case 'a':
107			op = LOADENTRY;
108			arg = optarg;
109			break;
110		case 's':
111			op = NEWSH;
112			arg = optarg;
113			break;
114		case 'p':
115			op = NEWPW;
116			arg = optarg;
117			break;
118		case 'e':
119			op = NEWEXP;
120			arg = optarg;
121			break;
122#ifdef YP
123		case 'd':
124			yp_domain = optarg;
125			break;
126		case 'h':
127			yp_host = optarg;
128			break;
129		case 'l':
130		case 'o':
131		case 'y':
132			/* compatibility */
133			break;
134#endif
135		case '?':
136		default:
137			usage();
138		}
139
140	argc -= optind;
141	argv += optind;
142
143	if (argc > 1)
144		usage();
145
146	uid = getuid();
147
148	if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
149		if (argc == 0) {
150			if ((pw = getpwuid(uid)) == NULL)
151				errx(1, "unknown user: uid %lu",
152				    (unsigned long)uid);
153		} else {
154			if ((pw = getpwnam(*argv)) == NULL)
155				errx(1, "unknown user: %s", *argv);
156			if (uid != 0 && uid != pw->pw_uid)
157				baduser();
158		}
159
160		/* Make a copy for later verification */
161		if ((pw = pw_dup(pw)) == NULL ||
162		    (old_pw = pw_dup(pw)) == NULL)
163			err(1, "pw_dup");
164	}
165
166#ifdef YP
167	if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
168		ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
169		master_mode = (ypclnt != NULL &&
170		    ypclnt_connect(ypclnt) != -1 &&
171		    ypclnt_havepasswdd(ypclnt) == 1);
172		ypclnt_free(ypclnt);
173	} else
174#endif
175	master_mode = (uid == 0);
176
177	if (op == NEWSH) {
178		/* protect p_shell -- it thinks NULL is /bin/sh */
179		if (!arg[0])
180			usage();
181		if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
182			exit(1);
183	}
184
185	if (op == NEWEXP) {
186		if (uid)	/* only root can change expire */
187			baduser();
188		if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
189			exit(1);
190	}
191
192	if (op == LOADENTRY) {
193		if (uid)
194			baduser();
195		pw = &lpw;
196		old_pw = NULL;
197		if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
198			exit(1);
199	}
200
201	if (op == NEWPW) {
202		if (uid)
203			baduser();
204
205		if (strchr(arg, ':'))
206			errx(1, "invalid format for password");
207		pw->pw_passwd = arg;
208	}
209
210	if (op == EDITENTRY) {
211		/*
212		 * We don't really need pw_*() here, but pw_edit() (used
213		 * by edit()) is just too useful...
214		 */
215		if (pw_init(NULL, NULL))
216			err(1, "pw_init()");
217		if ((tfd = pw_tmp(-1)) == -1) {
218			pw_fini();
219			err(1, "pw_tmp()");
220		}
221		free(pw);
222		pw = edit(pw_tempname(), old_pw);
223		pw_fini();
224		if (pw == NULL)
225			err(1, "edit()");
226		if (pw_equal(old_pw, pw))
227			errx(0, "user information unchanged");
228	}
229
230	if (old_pw && !master_mode) {
231		password = getpass("Password: ");
232		if (strcmp(crypt(password, old_pw->pw_passwd),
233		    old_pw->pw_passwd) != 0)
234			baduser();
235	} else {
236		password = "";
237	}
238
239	if (old_pw != NULL)
240		pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
241	switch (pw->pw_fields & _PWF_SOURCE) {
242#ifdef YP
243	case _PWF_NIS:
244		ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
245		if (ypclnt == NULL ||
246		    ypclnt_connect(ypclnt) == -1 ||
247		    ypclnt_passwd(ypclnt, pw, password) == -1) {
248			warnx("%s", ypclnt->error);
249			ypclnt_free(ypclnt);
250			exit(1);
251		}
252		ypclnt_free(ypclnt);
253		errx(0, "NIS user information updated");
254#endif /* YP */
255	case 0:
256	case _PWF_FILES:
257		if (pw_init(NULL, NULL))
258			err(1, "pw_init()");
259		if ((pfd = pw_lock()) == -1) {
260			pw_fini();
261			err(1, "pw_lock()");
262		}
263		if ((tfd = pw_tmp(-1)) == -1) {
264			pw_fini();
265			err(1, "pw_tmp()");
266		}
267		if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
268			pw_fini();
269			err(1, "pw_copy");
270		}
271		if (pw_mkdb(pw->pw_name) == -1) {
272			pw_fini();
273			err(1, "pw_mkdb()");
274		}
275		pw_fini();
276		errx(0, "user information updated");
277		break;
278	default:
279		errx(1, "unsupported passwd source");
280	}
281}
282
283static void
284baduser(void)
285{
286
287	errx(1, "%s", strerror(EACCES));
288}
289
290static void
291usage(void)
292{
293
294	(void)fprintf(stderr,
295	    "Usage: chpass%s %s [user]\n",
296#ifdef YP
297	    " [-d domain] [-h host]",
298#else
299	    "",
300#endif
301	    "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
302	exit(1);
303}
304