chpass.c revision 93086
1/*-
2 * Copyright (c) 1988, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1988, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static const char sccsid[] = "From: @(#)chpass.c	8.4 (Berkeley) 4/2/94";
42#endif /* not lint */
43
44#include <sys/cdefs.h>
45__FBSDID("$FreeBSD: head/usr.bin/chpass/chpass.c 93086 2002-03-24 10:21:22Z markm $");
46
47#include <sys/param.h>
48#include <sys/stat.h>
49#include <sys/signal.h>
50#include <sys/time.h>
51#include <sys/resource.h>
52
53#include <ctype.h>
54#include <err.h>
55#include <errno.h>
56#include <fcntl.h>
57#include <pwd.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <unistd.h>
62
63#include <pw_scan.h>
64#include <pw_util.h>
65#include "pw_copy.h"
66#ifdef YP
67#include <rpcsvc/yp.h>
68int yp_errno = YP_TRUE;
69#include "pw_yp.h"
70#endif
71
72#include "chpass.h"
73#include "pathnames.h"
74
75char *tempname;
76uid_t uid;
77
78void	baduser(void);
79void	usage(void);
80
81char localhost[] = "localhost";
82
83int
84main(int argc, char *argv[])
85{
86	enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
87	struct passwd *pw = NULL, lpw, old_pw;
88	char *username = NULL;
89	int ch, pfd, tfd;
90	char *arg = NULL;
91#ifdef YP
92	int force_local = 0;
93	int force_yp = 0;
94#endif
95
96	op = EDITENTRY;
97#ifdef YP
98	while ((ch = getopt(argc, argv, "a:p:s:e:d:h:oly")) != -1)
99#else
100	while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
101#endif
102		switch(ch) {
103		case 'a':
104			op = LOADENTRY;
105			arg = optarg;
106			break;
107		case 's':
108			op = NEWSH;
109			arg = optarg;
110			break;
111		case 'p':
112			op = NEWPW;
113			arg = optarg;
114			break;
115		case 'e':
116			op = NEWEXP;
117			arg = optarg;
118			break;
119#ifdef YP
120		case 'h':
121#ifdef PARANOID
122			if (getuid()) {
123				warnx("Only the superuser can use the -h flag");
124			} else {
125#endif
126				yp_server = optarg;
127#ifdef PARANOID
128			}
129#endif
130			break;
131		case 'd':
132#ifdef PARANOID
133			if (getuid()) {
134				warnx("Only the superuser can use the -d flag");
135			} else {
136#endif
137				yp_domain = optarg;
138				if (yp_server == NULL)
139					yp_server = localhost;
140#ifdef PARANOID
141			}
142#endif
143			break;
144		case 'l':
145			_use_yp = 0;
146			force_local = 1;
147			break;
148		case 'y':
149			_use_yp = force_yp = 1;
150			break;
151		case 'o':
152			force_old++;
153			break;
154#endif
155		case '?':
156		default:
157			usage();
158		}
159	argc -= optind;
160	argv += optind;
161
162	uid = getuid();
163
164	if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
165		switch(argc) {
166#ifdef YP
167		case 0:
168			GETPWUID(uid)
169			get_yp_master(1); /* XXX just to set the suser flag */
170			break;
171		case 1:
172			GETPWNAM(*argv)
173			get_yp_master(1); /* XXX just to set the suser flag */
174#else
175		case 0:
176			if (!(pw = getpwuid(uid)))
177				errx(1, "unknown user: uid %lu",
178				    (unsigned long)uid);
179			break;
180		case 1:
181			if (!(pw = getpwnam(*argv)))
182				errx(1, "unknown user: %s", *argv);
183#endif
184			if (uid && uid != pw->pw_uid)
185				baduser();
186			break;
187		default:
188			usage();
189		}
190
191		/* Make a copy for later verification */
192		old_pw = *pw;
193		old_pw.pw_gecos = strdup(old_pw.pw_gecos);
194	}
195
196	if (op == NEWSH) {
197		/* protect p_shell -- it thinks NULL is /bin/sh */
198		if (!arg[0])
199			usage();
200		if (p_shell(arg, pw, (ENTRY *)NULL))
201			pw_error((char *)NULL, 0, 1);
202	}
203
204	if (op == NEWEXP) {
205		if (uid)	/* only root can change expire */
206			baduser();
207		if (p_expire(arg, pw, (ENTRY *)NULL))
208			pw_error((char *)NULL, 0, 1);
209	}
210
211	if (op == LOADENTRY) {
212		if (uid)
213			baduser();
214		pw = &lpw;
215		if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
216			exit(1);
217	}
218	username = pw->pw_name;
219
220	if (op == NEWPW) {
221		if (uid)
222			baduser();
223
224		if(strchr(arg, ':')) {
225			errx(1, "invalid format for password");
226		}
227		pw->pw_passwd = arg;
228	}
229
230	/*
231	 * The temporary file/file descriptor usage is a little tricky here.
232	 * 1:	Create a temporary file called tempname, get descriptor tfd.
233	 * 2:	Display() gets an fp for the temporary file, and copies the
234	 *	user's information into it.  It then gives the temporary file
235	 *	to the user and closes the fp, closing the underlying fd.
236	 * 3:	The user edits the temporary file some number of times.
237	 *	The results are stored in pw by edit().
238	 * 4:	Delete the temporary file.
239	 * 5:	Make a new temporary file, descriptor tfd.
240	 * 6:	Get a descriptor for the master.passwd file, pfd, and
241	 *	lock master.passwd.
242	 * 7:	Pw_copy() gets descriptors for master.passwd and the
243	 *	temporary file and copies the master password file into it,
244	 *	replacing the modified user's record with a new one.  We can't
245	 *	use the first temporary file for this because it was owned
246	 *	by the user.  Pass the new and old user info.  Check the
247	 *	entry for our user has not been changed by someone else by
248	 *	while the user was editing by comparing the old info to
249	 *	the entry freshly read from master.passwd. Pw_copy() closes
250	 *	its fp, flushing the data and closing the underlying file
251	 *	descriptor.  We can't close the master password fp, or we'd
252	 *	lose the lock.
253	 * 8:	Call pw_mkdb() (which renames the temporary file) and exit.
254	 *	The exit closes the master passwd fp/fd.
255	 */
256	pw_init();
257	tfd = pw_tmp();
258
259	if (op == EDITENTRY) {
260		display(tfd, pw);
261		edit(pw);
262		(void)unlink(tempname);
263		tfd = pw_tmp();
264	}
265
266#ifdef YP
267	if (_use_yp) {
268		yp_submit(pw);
269		(void)unlink(tempname);
270	} else {
271#endif /* YP */
272	pfd = pw_lock();
273	pw_copy(pfd, tfd, pw, (op == LOADENTRY) ? NULL : &old_pw);
274
275	if (!pw_mkdb(username))
276		pw_error((char *)NULL, 0, 1);
277#ifdef YP
278	}
279#endif /* YP */
280	exit(0);
281}
282
283void
284baduser(void)
285{
286	errx(1, "%s", strerror(EACCES));
287}
288
289void
290usage(void)
291{
292
293	(void)fprintf(stderr,
294#ifdef YP
295		"usage: chpass [-l] [-y] [-d domain [-h host]] [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n");
296#else
297		"usage: chpass [-a list] [-p encpass] [-s shell] [-e mmm dd yy] [user]\n");
298#endif
299	exit(1);
300}
301