chpass.c revision 225736
1145132Sanholt/*-
2145132Sanholt * Copyright (c) 1988, 1993, 1994
3152909Sanholt *	The Regents of the University of California.  All rights reserved.
4145132Sanholt * Copyright (c) 2002 Networks Associates Technology, Inc.
5145132Sanholt * All rights reserved.
6182080Srnoland *
7152909Sanholt * Portions of this software were developed for the FreeBSD Project by
8152909Sanholt * ThinkSec AS and NAI Labs, the Security Research Division of Network
9152909Sanholt * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
10152909Sanholt * ("CBOSS"), as part of the DARPA CHATS research program.
11152909Sanholt *
12152909Sanholt * Redistribution and use in source and binary forms, with or without
13152909Sanholt * modification, are permitted provided that the following conditions
14182080Srnoland * are met:
15152909Sanholt * 1. Redistributions of source code must retain the above copyright
16152909Sanholt *    notice, this list of conditions and the following disclaimer.
17152909Sanholt * 2. Redistributions in binary form must reproduce the above copyright
18182080Srnoland *    notice, this list of conditions and the following disclaimer in the
19152909Sanholt *    documentation and/or other materials provided with the distribution.
20152909Sanholt * 3. All advertising materials mentioning features or use of this software
21152909Sanholt *    must display the following acknowledgement:
22152909Sanholt *	This product includes software developed by the University of
23152909Sanholt *	California, Berkeley and its contributors.
24152909Sanholt * 4. Neither the name of the University nor the names of its contributors
25152909Sanholt *    may be used to endorse or promote products derived from this software
26182080Srnoland *    without specific prior written permission.
27152909Sanholt *
28145132Sanholt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29152909Sanholt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30152909Sanholt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31145132Sanholt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32152909Sanholt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33152909Sanholt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34152909Sanholt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35152909Sanholt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36152909Sanholt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37145132Sanholt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38145132Sanholt * SUCH DAMAGE.
39145132Sanholt */
40145132Sanholt
41145132Sanholt#if 0
42182080Srnoland#ifndef lint
43145132Sanholtstatic const char copyright[] =
44145132Sanholt"@(#) Copyright (c) 1988, 1993, 1994\n\
45145132Sanholt	The Regents of the University of California.  All rights reserved.\n";
46182080Srnoland#endif /* not lint */
47183573Srnoland
48183573Srnoland#ifndef lint
49183573Srnolandstatic char sccsid[] = "@(#)chpass.c	8.4 (Berkeley) 4/2/94";
50145132Sanholt#endif /* not lint */
51145132Sanholt#endif
52183573Srnoland#include <sys/cdefs.h>
53182080Srnoland__FBSDID("$FreeBSD: stable/9/usr.bin/chpass/chpass.c 162633 2006-09-25 15:06:24Z marck $");
54183573Srnoland
55145132Sanholt#include <sys/param.h>
56145132Sanholt
57145132Sanholt#include <err.h>
58145132Sanholt#include <errno.h>
59145132Sanholt#include <pwd.h>
60145132Sanholt#include <stdio.h>
61189049Srnoland#include <stdlib.h>
62189049Srnoland#include <string.h>
63189049Srnoland#include <unistd.h>
64145132Sanholt#ifdef YP
65145132Sanholt#include <ypclnt.h>
66145132Sanholt#endif
67183573Srnoland
68183573Srnoland#include <pw_scan.h>
69183573Srnoland#include <libutil.h>
70145132Sanholt
71183573Srnoland#include "chpass.h"
72183573Srnoland
73145132Sanholtint master_mode;
74145132Sanholt
75182080Srnolandstatic void	baduser(void);
76145132Sanholtstatic void	usage(void);
77145132Sanholt
78189049Srnolandint
79189049Srnolandmain(int argc, char *argv[])
80189049Srnoland{
81189049Srnoland	enum { NEWSH, LOADENTRY, EDITENTRY, NEWPW, NEWEXP } op;
82189049Srnoland	struct passwd lpw, *old_pw, *pw;
83183573Srnoland	int ch, pfd, tfd;
84183573Srnoland	const char *password;
85183573Srnoland	char *arg = NULL;
86183573Srnoland	uid_t uid;
87183573Srnoland#ifdef YP
88189049Srnoland	struct ypclnt *ypclnt;
89189049Srnoland	const char *yp_domain = NULL, *yp_host = NULL;
90183573Srnoland#endif
91189049Srnoland
92183573Srnoland	pw = old_pw = NULL;
93183573Srnoland	op = EDITENTRY;
94183573Srnoland#ifdef YP
95189049Srnoland	while ((ch = getopt(argc, argv, "a:p:s:e:d:h:loy")) != -1)
96189049Srnoland#else
97183573Srnoland	while ((ch = getopt(argc, argv, "a:p:s:e:")) != -1)
98183573Srnoland#endif
99183573Srnoland		switch (ch) {
100189049Srnoland		case 'a':
101183573Srnoland			op = LOADENTRY;
102183573Srnoland			arg = optarg;
103183573Srnoland			break;
104183573Srnoland		case 's':
105189049Srnoland			op = NEWSH;
106189049Srnoland			arg = optarg;
107189049Srnoland			break;
108189049Srnoland		case 'p':
109189049Srnoland			op = NEWPW;
110183573Srnoland			arg = optarg;
111183573Srnoland			break;
112183573Srnoland		case 'e':
113183573Srnoland			op = NEWEXP;
114183573Srnoland			arg = optarg;
115183573Srnoland			break;
116183573Srnoland#ifdef YP
117183573Srnoland		case 'd':
118183573Srnoland			yp_domain = optarg;
119183573Srnoland			break;
120183573Srnoland		case 'h':
121183573Srnoland			yp_host = optarg;
122189049Srnoland			break;
123189049Srnoland		case 'l':
124183573Srnoland		case 'o':
125183573Srnoland		case 'y':
126182080Srnoland			/* compatibility */
127145132Sanholt			break;
128145132Sanholt#endif
129145132Sanholt		case '?':
130145132Sanholt		default:
131182080Srnoland			usage();
132182080Srnoland		}
133145132Sanholt
134145132Sanholt	argc -= optind;
135145132Sanholt	argv += optind;
136189049Srnoland
137189049Srnoland	if (argc > 1)
138189049Srnoland		usage();
139145132Sanholt
140145132Sanholt	uid = getuid();
141182080Srnoland
142145132Sanholt	if (op == EDITENTRY || op == NEWSH || op == NEWPW || op == NEWEXP) {
143182080Srnoland		if (argc == 0) {
144145132Sanholt			if ((pw = getpwuid(uid)) == NULL)
145145132Sanholt				errx(1, "unknown user: uid %lu",
146145132Sanholt				    (unsigned long)uid);
147145132Sanholt		} else {
148183573Srnoland			if ((pw = getpwnam(*argv)) == NULL)
149145132Sanholt				errx(1, "unknown user: %s", *argv);
150145132Sanholt			if (uid != 0 && uid != pw->pw_uid)
151182080Srnoland				baduser();
152182080Srnoland		}
153189049Srnoland
154207066Srnoland		/* Make a copy for later verification */
155182080Srnoland		if ((pw = pw_dup(pw)) == NULL ||
156182080Srnoland		    (old_pw = pw_dup(pw)) == NULL)
157145132Sanholt			err(1, "pw_dup");
158189049Srnoland	}
159183573Srnoland
160189049Srnoland#ifdef YP
161145132Sanholt	if (pw != NULL && (pw->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
162145132Sanholt		ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
163145132Sanholt		master_mode = (ypclnt != NULL &&
164145132Sanholt		    ypclnt_connect(ypclnt) != -1 &&
165189049Srnoland		    ypclnt_havepasswdd(ypclnt) == 1);
166145132Sanholt		ypclnt_free(ypclnt);
167182080Srnoland	} else
168171394Skib#endif
169182080Srnoland	master_mode = (uid == 0);
170145132Sanholt
171145132Sanholt	if (op == NEWSH) {
172145132Sanholt		/* protect p_shell -- it thinks NULL is /bin/sh */
173182080Srnoland		if (!arg[0])
174145132Sanholt			usage();
175145132Sanholt		if (p_shell(arg, pw, (ENTRY *)NULL) == -1)
176189049Srnoland			exit(1);
177207066Srnoland	}
178145132Sanholt
179189049Srnoland	if (op == NEWEXP) {
180189049Srnoland		if (uid)	/* only root can change expire */
181189049Srnoland			baduser();
182189049Srnoland		if (p_expire(arg, pw, (ENTRY *)NULL) == -1)
183189049Srnoland			exit(1);
184189049Srnoland	}
185189049Srnoland
186182080Srnoland	if (op == LOADENTRY) {
187183573Srnoland		if (uid)
188183573Srnoland			baduser();
189145132Sanholt		pw = &lpw;
190183573Srnoland		old_pw = NULL;
191183573Srnoland		if (!__pw_scan(arg, pw, _PWSCAN_WARN|_PWSCAN_MASTER))
192183573Srnoland			exit(1);
193183573Srnoland	}
194183573Srnoland
195145132Sanholt	if (op == NEWPW) {
196190021Srnoland		if (uid)
197145132Sanholt			baduser();
198207066Srnoland
199183573Srnoland		if (strchr(arg, ':'))
200183573Srnoland			errx(1, "invalid format for password");
201183573Srnoland		pw->pw_passwd = arg;
202183573Srnoland	}
203183573Srnoland
204145132Sanholt	if (op == EDITENTRY) {
205145132Sanholt		/*
206207066Srnoland		 * We don't really need pw_*() here, but pw_edit() (used
207189049Srnoland		 * by edit()) is just too useful...
208182080Srnoland		 */
209189049Srnoland		if (pw_init(NULL, NULL))
210189049Srnoland			err(1, "pw_init()");
211189049Srnoland		if ((tfd = pw_tmp(-1)) == -1) {
212190021Srnoland			pw_fini();
213145132Sanholt			err(1, "pw_tmp()");
214145132Sanholt		}
215145132Sanholt		free(pw);
216145132Sanholt		pw = edit(pw_tempname(), old_pw);
217145132Sanholt		pw_fini();
218145132Sanholt		if (pw == NULL)
219145132Sanholt			err(1, "edit()");
220145132Sanholt		/*
221182080Srnoland		 * pw_equal does not check for crypted passwords, so we
222145132Sanholt		 * should do it explicitly
223145132Sanholt		 */
224145132Sanholt		if (pw_equal(old_pw, pw) &&
225182080Srnoland		    strcmp(old_pw->pw_passwd, pw->pw_passwd) == 0)
226145132Sanholt			errx(0, "user information unchanged");
227145132Sanholt	}
228145132Sanholt
229182080Srnoland	if (old_pw && !master_mode) {
230145132Sanholt		password = getpass("Password: ");
231145132Sanholt		if (strcmp(crypt(password, old_pw->pw_passwd),
232207066Srnoland		    old_pw->pw_passwd) != 0)
233145132Sanholt			baduser();
234145132Sanholt	} else {
235182080Srnoland		password = "";
236145132Sanholt	}
237145132Sanholt
238145132Sanholt	if (old_pw != NULL)
239145132Sanholt		pw->pw_fields |= (old_pw->pw_fields & _PWF_SOURCE);
240145132Sanholt	switch (pw->pw_fields & _PWF_SOURCE) {
241182080Srnoland#ifdef YP
242145132Sanholt	case _PWF_NIS:
243145132Sanholt		ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_host);
244145132Sanholt		if (ypclnt == NULL ||
245182080Srnoland		    ypclnt_connect(ypclnt) == -1 ||
246189049Srnoland		    ypclnt_passwd(ypclnt, pw, password) == -1) {
247182080Srnoland			warnx("%s", ypclnt->error);
248189049Srnoland			ypclnt_free(ypclnt);
249145132Sanholt			exit(1);
250145132Sanholt		}
251145132Sanholt		ypclnt_free(ypclnt);
252145132Sanholt		errx(0, "NIS user information updated");
253145132Sanholt#endif /* YP */
254182080Srnoland	case 0:
255182080Srnoland	case _PWF_FILES:
256145132Sanholt		if (pw_init(NULL, NULL))
257182080Srnoland			err(1, "pw_init()");
258145132Sanholt		if ((pfd = pw_lock()) == -1) {
259145132Sanholt			pw_fini();
260182080Srnoland			err(1, "pw_lock()");
261145132Sanholt		}
262189049Srnoland		if ((tfd = pw_tmp(-1)) == -1) {
263145132Sanholt			pw_fini();
264145132Sanholt			err(1, "pw_tmp()");
265145132Sanholt		}
266145132Sanholt		if (pw_copy(pfd, tfd, pw, old_pw) == -1) {
267145132Sanholt			pw_fini();
268145132Sanholt			err(1, "pw_copy");
269145132Sanholt		}
270145132Sanholt		if (pw_mkdb(pw->pw_name) == -1) {
271145132Sanholt			pw_fini();
272145132Sanholt			err(1, "pw_mkdb()");
273145132Sanholt		}
274145132Sanholt		pw_fini();
275145132Sanholt		errx(0, "user information updated");
276145132Sanholt		break;
277145132Sanholt	default:
278145132Sanholt		errx(1, "unsupported passwd source");
279145132Sanholt	}
280145132Sanholt}
281145132Sanholt
282145132Sanholtstatic void
283145132Sanholtbaduser(void)
284145132Sanholt{
285145132Sanholt
286145132Sanholt	errx(1, "%s", strerror(EACCES));
287145132Sanholt}
288145132Sanholt
289145132Sanholtstatic void
290145132Sanholtusage(void)
291145132Sanholt{
292145132Sanholt
293145132Sanholt	(void)fprintf(stderr,
294145132Sanholt	    "usage: chpass%s %s [user]\n",
295145132Sanholt#ifdef YP
296145132Sanholt	    " [-d domain] [-h host]",
297145132Sanholt#else
298145132Sanholt	    "",
299145132Sanholt#endif
300145132Sanholt	    "[-a list] [-p encpass] [-s shell] [-e mmm dd yy]");
301145132Sanholt	exit(1);
302145132Sanholt}
303145132Sanholt