xref: /openbsd-src/usr.bin/chpass/chpass.c (revision db3296cf5c1dd9058ceecc3a29fe4aaa0bd26000)
1 /*	$OpenBSD: chpass.c,v 1.28 2003/07/01 01:01:28 avsm Exp $	*/
2 /*	$NetBSD: chpass.c,v 1.8 1996/05/15 21:50:43 jtc Exp $	*/
3 
4 /*-
5  * Copyright (c) 1988, 1993, 1994
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #ifndef lint
34 static char copyright[] =
35 "@(#) Copyright (c) 1988, 1993, 1994\n\
36 	The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)chpass.c	8.4 (Berkeley) 4/2/94";
42 #else
43 static char rcsid[] = "$OpenBSD: chpass.c,v 1.28 2003/07/01 01:01:28 avsm Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include <sys/param.h>
48 #include <sys/resource.h>
49 #include <sys/stat.h>
50 #include <sys/time.h>
51 #include <sys/uio.h>
52 
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <paths.h>
57 #include <pwd.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62 #include <unistd.h>
63 #include <util.h>
64 
65 #include "chpass.h"
66 
67 extern char *__progname;
68 
69 enum { NEWSH, LOADENTRY, EDITENTRY } op;
70 uid_t uid;
71 #ifdef	YP
72 int	use_yp;
73 int	force_yp = 0;
74 #endif
75 
76 void	baduser(void);
77 void	kbintr(int);
78 void	usage(void);
79 
80 int
81 main(int argc, char *argv[])
82 {
83 	struct passwd *pw = NULL, lpw;
84 	int i, ch, pfd, tfd, dfd;
85 	char *arg = NULL;
86 	sigset_t fullset;
87 
88 #ifdef	YP
89 	use_yp = _yp_check(NULL);
90 #endif
91 
92 	op = EDITENTRY;
93 	while ((ch = getopt(argc, argv, "a:s:ly")) != -1)
94 		switch(ch) {
95 		case 'a':
96 			op = LOADENTRY;
97 			arg = optarg;
98 			break;
99 		case 's':
100 			op = NEWSH;
101 			arg = optarg;
102 			break;
103 #ifdef	YP
104 		case 'l':
105 			use_yp = 0;
106 			break;
107 		case 'y':
108 			if (!use_yp) {
109 				warnx("YP not in use.");
110 				usage();
111 			}
112 			force_yp = 1;
113 			break;
114 #endif
115 		case '?':
116 		default:
117 			usage();
118 		}
119 	argc -= optind;
120 	argv += optind;
121 
122 #ifdef	YP
123 	if (op == LOADENTRY && use_yp)
124 		errx(1, "cannot load using YP, use -l to load local.");
125 #endif
126 	uid = getuid();
127 
128 	if (op == EDITENTRY || op == NEWSH)
129 		switch(argc) {
130 		case 0:
131 			pw = getpwuid(uid);
132 #ifdef	YP
133 			if (pw && !force_yp)
134 				use_yp = 0;
135 			else if (use_yp)
136 				pw = ypgetpwuid(uid);
137 #endif	/* YP */
138 			if (!pw)
139 				errx(1, "unknown user: uid %u", uid);
140 			break;
141 		case 1:
142 			pw = getpwnam(*argv);
143 #ifdef	YP
144 			if (pw && !force_yp)
145 				use_yp = 0;
146 			else if (use_yp)
147 				pw = ypgetpwnam(*argv);
148 #endif	/* YP */
149 			if (!pw)
150 				errx(1, "unknown user: %s", *argv);
151 			if (uid && uid != pw->pw_uid)
152 				baduser();
153 			break;
154 		default:
155 			usage();
156 		}
157 
158 	if (op == NEWSH) {
159 		/* protect p_shell -- it thinks NULL is /bin/sh */
160 		if (!arg[0])
161 			usage();
162 		if (p_shell(arg, pw, NULL))
163 			pw_error(NULL, 0, 1);
164 	}
165 
166 	if (op == LOADENTRY) {
167 		if (uid)
168 			baduser();
169 		pw = &lpw;
170 		if (!pw_scan(arg, pw, NULL))
171 			exit(1);
172 	}
173 
174 	/* Edit the user passwd information if requested. */
175 	if (op == EDITENTRY) {
176 		char tempname[] = __CONCAT(_PATH_VARTMP,"pw.XXXXXXXXXX");
177 		int edit_status;
178 
179 		dfd = mkstemp(tempname);
180 		if (dfd == -1 || fcntl(dfd, F_SETFD, 1) == -1)
181 			pw_error(tempname, 1, 1);
182 		display(tempname, dfd, pw);
183 		edit_status = edit(tempname, pw);
184 		close(dfd);
185 		unlink(tempname);
186 
187 		switch (edit_status) {
188 		case EDIT_OK:
189 			break;
190 		case EDIT_NOCHANGE:
191 			pw_error(NULL, 0, 0);
192 			break;
193 		case EDIT_ERROR:
194 		default:
195 			pw_error(tempname, 1, 1);
196 			break;
197 		}
198 	}
199 
200 	/* Drop user's real uid and block all signals to avoid a DoS. */
201 	setuid(0);
202 	sigfillset(&fullset);
203 	sigdelset(&fullset, SIGINT);
204 	sigprocmask(SIG_BLOCK, &fullset, NULL);
205 
206 	/* Get the passwd lock file and open the passwd file for reading. */
207 	pw_init();
208 	for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
209 		if (i == 4)
210 			(void)fputs("Attempting lock password file, "
211 			    "please wait or press ^C to abort", stderr);
212 		(void)signal(SIGINT, kbintr);
213 		if (i % 16 == 0)
214 			fputc('.', stderr);
215 		usleep(250000);
216 		(void)signal(SIGINT, SIG_IGN);
217 	}
218 	if (i >= 4)
219 		fputc('\n', stderr);
220 	pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
221 	if (pfd == -1 || fcntl(pfd, F_SETFD, 1) == -1)
222 		pw_error(_PATH_MASTERPASSWD, 1, 1);
223 
224 #ifdef	YP
225 	if (use_yp) {
226 		if (pw_yp(pw, uid))
227 			pw_error(NULL, 0, 1);
228 		else {
229 			pw_abort();
230 			exit(0);
231 		}
232 	} else
233 #endif	/* YP */
234 	{
235 		/* Copy the passwd file to the lock file, updating pw. */
236 		pw_copy(pfd, tfd, pw);
237 
238 		/* Now finish the passwd file update. */
239 		if (pw_mkdb(pw->pw_name, 0) == -1)
240 			pw_error(NULL, 0, 1);
241 	}
242 
243 	exit(0);
244 }
245 
246 void
247 baduser(void)
248 {
249 
250 	errx(1, "%s", strerror(EACCES));
251 }
252 
253 void
254 kbintr(int signo)
255 {
256 	struct iovec iv[5];
257 
258 	iv[0].iov_base = "\n";
259 	iv[0].iov_len = 1;
260 	iv[1].iov_base = __progname;
261 	iv[1].iov_len = strlen(__progname);
262 	iv[2].iov_base = ": ";
263 	iv[2].iov_len = 2;
264 	iv[3].iov_base = _PATH_MASTERPASSWD;
265 	iv[3].iov_len = sizeof(_PATH_MASTERPASSWD) - 1;
266 	iv[4].iov_base = " unchanged\n";
267 	iv[4].iov_len = 11;
268 	writev(STDERR_FILENO, iv, 5);
269 
270 	_exit(1);
271 }
272 
273 void
274 usage(void)
275 {
276 
277 #ifdef	YP
278 	(void)fprintf(stderr,
279 	    "usage: %s [-l%s] [-a list] [-s newshell] [user]\n",
280 	    __progname, use_yp ? "y" : "");
281 #else
282 	(void)fprintf(stderr, "usage: %s [-a list] [-s newshell] [user]\n",
283 	    __progname);
284 #endif
285 	exit(1);
286 }
287