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