xref: /openbsd-src/usr.bin/chpass/chpass.c (revision 8445c53715e7030056b779e8ab40efb7820981f2)
1 /*	$OpenBSD: chpass.c,v 1.20 2001/08/27 02:57:07 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.20 2001/08/27 02:57:07 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 <err.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <pwd.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include <util.h>
67 
68 #include "chpass.h"
69 #include "pathnames.h"
70 
71 char tempname[] = __CONCAT(_PATH_VARTMP,"pw.XXXXXXXX");
72 enum { NEWSH, LOADENTRY, EDITENTRY } op;
73 uid_t uid;
74 
75 extern char *__progname;
76 
77 #ifdef	YP
78 int use_yp;
79 int force_yp = 0;
80 extern struct passwd *ypgetpwnam(), *ypgetpwuid();
81 int _yp_check __P((char **));
82 int pw_yp __P((struct passwd *, uid_t));
83 #endif
84 
85 void	baduser __P((void));
86 void	tempcleanup __P((void));
87 void	kbintr __P((int));
88 void	usage __P((void));
89 
90 int
91 main(argc, argv)
92 	int argc;
93 	char **argv;
94 {
95 	struct passwd *pw, lpw;
96 	int i, ch, pfd, tfd, dfd;
97 	char *arg;
98 	sigset_t fullset;
99 
100 #ifdef	YP
101 	use_yp = _yp_check(NULL);
102 #endif
103 
104 	op = EDITENTRY;
105 	while ((ch = getopt(argc, argv, "a:s:ly")) != -1)
106 		switch(ch) {
107 		case 'a':
108 			op = LOADENTRY;
109 			arg = optarg;
110 			break;
111 		case 's':
112 			op = NEWSH;
113 			arg = optarg;
114 			break;
115 #ifdef	YP
116 		case 'l':
117 			use_yp = 0;
118 			break;
119 		case 'y':
120 			if (!use_yp) {
121 				warnx("YP not in use.");
122 				usage();
123 			}
124 			force_yp = 1;
125 			break;
126 #endif
127 		case '?':
128 		default:
129 			usage();
130 		}
131 	argc -= optind;
132 	argv += optind;
133 
134 #ifdef	YP
135 	if (op == LOADENTRY && use_yp)
136 		errx(1, "cannot load entry using NIS.\n\tUse the -l flag to load local.");
137 #endif
138 	uid = getuid();
139 
140 	if (op == EDITENTRY || op == NEWSH)
141 		switch(argc) {
142 		case 0:
143 			pw = getpwuid(uid);
144 #ifdef	YP
145 			if (pw && !force_yp)
146 				use_yp = 0;
147 			else if (use_yp)
148 				pw = ypgetpwuid(uid);
149 #endif	/* YP */
150 			if (!pw)
151 				errx(1, "unknown user: uid %u\n", uid);
152 			break;
153 		case 1:
154 			pw = getpwnam(*argv);
155 #ifdef	YP
156 			if (pw && !force_yp)
157 				use_yp = 0;
158 			else if (use_yp)
159 				pw = ypgetpwnam(*argv);
160 #endif	/* YP */
161 			if (!pw)
162 				errx(1, "unknown user: %s", *argv);
163 			if (uid && uid != pw->pw_uid)
164 				baduser();
165 			break;
166 		default:
167 			usage();
168 		}
169 
170 	if (op == NEWSH) {
171 		/* protect p_shell -- it thinks NULL is /bin/sh */
172 		if (!arg[0])
173 			usage();
174 		if (p_shell(arg, pw, NULL))
175 			pw_error(NULL, 0, 1);
176 	}
177 
178 	if (op == LOADENTRY) {
179 		if (uid)
180 			baduser();
181 		pw = &lpw;
182 		if (!pw_scan(arg, pw, NULL))
183 			exit(1);
184 	}
185 
186 	/* Edit the user passwd information if requested. */
187 	if (op == EDITENTRY) {
188 		dfd = mkstemp(tempname);
189 		if (dfd == -1 || fcntl(dfd, F_SETFD, 1) == -1)
190 			pw_error(tempname, 1, 1);
191 		atexit(tempcleanup);
192 		display(tempname, dfd, pw);
193 		edit(tempname, pw);
194 	}
195 
196 	/* Drop user's real uid and block all signals to avoid a DoS. */
197 	setuid(0);
198 	sigfillset(&fullset);
199 	sigdelset(&fullset, SIGINT);
200 	sigprocmask(SIG_BLOCK, &fullset, NULL);
201 
202 	/* Get the passwd lock file and open the passwd file for reading. */
203 	pw_init();
204 	for (i = 1; (tfd = pw_lock(0)) == -1; i++) {
205 		if (i == 4)
206 			(void)fputs("Attempting lock password file, "
207 			    "please wait or press ^C to abort", stderr);
208 		(void)signal(SIGINT, kbintr);
209 		if (i % 16 == 0)
210 			fputc('.', stderr);
211 		usleep(250000);
212 		(void)signal(SIGINT, SIG_IGN);
213 	}
214 	if (i >= 4)
215 		fputc('\n', stderr);
216 	pfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
217 	if (pfd == -1 || fcntl(pfd, F_SETFD, 1) == -1)
218 		pw_error(_PATH_MASTERPASSWD, 1, 1);
219 
220 #ifdef	YP
221 	if (use_yp) {
222 		if (pw_yp(pw, uid))
223 			pw_error(NULL, 0, 1);
224 		else {
225 			pw_abort();
226 			exit(0);
227 		}
228 	} else
229 #endif	/* YP */
230 	{
231 		/* Copy the passwd file to the lock file, updating pw. */
232 		pw_copy(pfd, tfd, pw);
233 
234 		/* Now finish the passwd file update. */
235 		if (pw_mkdb(pw->pw_name, 0) == -1)
236 			pw_error(NULL, 0, 1);
237 	}
238 
239 	exit(0);
240 }
241 
242 void
243 baduser()
244 {
245 
246 	errx(1, "%s", strerror(EACCES));
247 }
248 
249 void
250 tempcleanup()
251 {
252 
253 	unlink(tempname);
254 }
255 
256 void
257 kbintr(signo)
258 	int signo;
259 {
260 	struct iovec iv[5];
261 
262 	iv[0].iov_base = "\n";
263 	iv[0].iov_len = 1;
264 	iv[1].iov_base = __progname;
265 	iv[1].iov_len = strlen(__progname);
266 	iv[2].iov_base = ": ";
267 	iv[2].iov_len = 2;
268 	iv[3].iov_base = _PATH_MASTERPASSWD;
269 	iv[3].iov_len = sizeof(_PATH_MASTERPASSWD) - 1;
270 	iv[4].iov_base = " unchanged\n";
271 	iv[4].iov_len = 11;
272 	writev(STDERR_FILENO, iv, 5);
273 
274 	if (op == EDITENTRY)
275 		unlink(tempname);
276 
277 	_exit(1);
278 }
279 
280 void
281 usage()
282 {
283 
284 #ifdef	YP
285 	(void)fprintf(stderr,
286 	    "usage: %s [-l%s] [-a list] [-s newshell] [user]\n",
287 	    __progname, use_yp ? "y" : "");
288 #else
289 	(void)fprintf(stderr, "usage: %s [-a list] [-s newshell] [user]\n",
290 	    __progname);
291 #endif
292 	exit(1);
293 }
294