xref: /netbsd-src/lib/libutil/passwd.c (revision 76dfffe33547c37f8bdd446e3e4ab0f3c16cea4b)
1 /*
2  * Copyright (c) 1987, 1993, 1994, 1995
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 #if defined(LIBC_SCCS) && !defined(lint)
35 static char rcsid[] = "$NetBSD: passwd.c,v 1.2 1996/06/02 19:25:43 ghudson Exp $";
36 #endif /* LIBC_SCCS and not lint */
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #include <sys/wait.h>
43 
44 #include <fcntl.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <pwd.h>
50 #include <errno.h>
51 #include <paths.h>
52 #include <signal.h>
53 #include <limits.h>
54 #include <util.h>
55 
56 static void	pw_cont __P((int sig));
57 
58 int
59 pw_lock(retries)
60 	int retries;
61 {
62 	int i, fd;
63 	mode_t old_mode;
64 
65 	/* Acquire the lock file. */
66 	old_mode = umask(0);
67 	fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL, 0600);
68 	for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) {
69 		sleep(1);
70 		fd = open(_PATH_MASTERPASSWD_LOCK, O_WRONLY|O_CREAT|O_EXCL,
71 			  0600);
72 	}
73 	umask(old_mode);
74 	return(fd);
75 }
76 
77 int
78 pw_mkdb()
79 {
80 	int pstat;
81 	pid_t pid;
82 
83 	pid = vfork();
84 	if (pid == 0) {
85 		execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p",
86 		      _PATH_MASTERPASSWD_LOCK, NULL);
87 		exit(1);
88 	}
89 	pid = waitpid(pid, &pstat, 0);
90 	if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
91 		return(-1);
92 	return(0);
93 }
94 
95 int
96 pw_abort()
97 {
98 	return(unlink(_PATH_MASTERPASSWD_LOCK));
99 }
100 
101 /* Everything below this point is intended for the convenience of programs
102  * which allow a user to interactively edit the passwd file.  Errors in the
103  * routines below will cause the process to abort. */
104 
105 static pid_t editpid = -1;
106 
107 static void
108 pw_cont(sig)
109 	int sig;
110 {
111 
112 	if (editpid != -1)
113 		kill(editpid, sig);
114 }
115 
116 void
117 pw_init()
118 {
119 	struct rlimit rlim;
120 
121 	/* Unlimited resource limits. */
122 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
123 	(void)setrlimit(RLIMIT_CPU, &rlim);
124 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
125 	(void)setrlimit(RLIMIT_STACK, &rlim);
126 	(void)setrlimit(RLIMIT_DATA, &rlim);
127 	(void)setrlimit(RLIMIT_RSS, &rlim);
128 
129 	/* Don't drop core (not really necessary, but GP's). */
130 	rlim.rlim_cur = rlim.rlim_max = 0;
131 	(void)setrlimit(RLIMIT_CORE, &rlim);
132 
133 	/* Turn off signals. */
134 	(void)signal(SIGALRM, SIG_IGN);
135 	(void)signal(SIGHUP, SIG_IGN);
136 	(void)signal(SIGINT, SIG_IGN);
137 	(void)signal(SIGPIPE, SIG_IGN);
138 	(void)signal(SIGQUIT, SIG_IGN);
139 	(void)signal(SIGTERM, SIG_IGN);
140 	(void)signal(SIGCONT, pw_cont);
141 }
142 
143 void
144 pw_edit(notsetuid, filename)
145 	int notsetuid;
146 	const char *filename;
147 {
148 	int pstat;
149 	char *p, *editor;
150 
151 	if (!filename)
152 		filename = _PATH_MASTERPASSWD_LOCK;
153 	if (!(editor = getenv("EDITOR")))
154 		editor = _PATH_VI;
155 	if (p = strrchr(editor, '/'))
156 		++p;
157 	else
158 		p = editor;
159 
160 	if (!(editpid = vfork())) {
161 		if (notsetuid) {
162 			setgid(getgid());
163 			setuid(getuid());
164 		}
165 		execlp(editor, p, filename, NULL);
166 		_exit(1);
167 	}
168 	for (;;) {
169 		editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
170 		if (editpid == -1)
171 			pw_error(editor, 1, 1);
172 		else if (WIFSTOPPED(pstat))
173 			raise(WSTOPSIG(pstat));
174 		else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
175 			break;
176 		else
177 			pw_error(editor, 1, 1);
178 	}
179 	editpid = -1;
180 }
181 
182 void
183 pw_prompt()
184 {
185 	int c;
186 
187 	(void)printf("re-edit the password file? [y]: ");
188 	(void)fflush(stdout);
189 	c = getchar();
190 	if (c != EOF && c != '\n')
191 		while (getchar() != '\n');
192 	if (c == 'n')
193 		pw_error(NULL, 0, 0);
194 }
195 
196 void
197 pw_copy(ffd, tfd, pw)
198 	int ffd, tfd;
199 	struct passwd *pw;
200 {
201 	FILE *from, *to;
202 	int done;
203 	char *p, buf[8192];
204 
205 	if (!(from = fdopen(ffd, "r")))
206 		pw_error(_PATH_MASTERPASSWD, 1, 1);
207 	if (!(to = fdopen(tfd, "w")))
208 		pw_error(_PATH_MASTERPASSWD_LOCK, 1, 1);
209 
210 	for (done = 0; fgets(buf, sizeof(buf), from);) {
211 		if (!strchr(buf, '\n')) {
212 			warnx("%s: line too long", _PATH_MASTERPASSWD);
213 			pw_error(NULL, 0, 1);
214 		}
215 		if (done) {
216 			(void)fprintf(to, "%s", buf);
217 			if (ferror(to))
218 				goto err;
219 			continue;
220 		}
221 		if (!(p = strchr(buf, ':'))) {
222 			warnx("%s: corrupted entry", _PATH_MASTERPASSWD);
223 			pw_error(NULL, 0, 1);
224 		}
225 		*p = '\0';
226 		if (strcmp(buf, pw->pw_name)) {
227 			*p = ':';
228 			(void)fprintf(to, "%s", buf);
229 			if (ferror(to))
230 				goto err;
231 			continue;
232 		}
233 		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
234 		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
235 		    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
236 		    pw->pw_dir, pw->pw_shell);
237 		done = 1;
238 		if (ferror(to))
239 			goto err;
240 	}
241 	if (!done)
242 		(void)fprintf(to, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
243 		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
244 		    pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
245 		    pw->pw_dir, pw->pw_shell);
246 
247 	if (ferror(to))
248 err:		pw_error(NULL, 1, 1);
249 	(void)fclose(to);
250 }
251 
252 int
253 pw_scan(bp, pw, flags)
254 	char *bp;
255 	struct passwd *pw;
256 	int *flags;
257 {
258 	long id;
259 	int root;
260 	char *p, *sh;
261 
262 	if (flags != (int *)NULL)
263 		*flags = 0;
264 
265 	if (!(pw->pw_name = strsep(&bp, ":")))		/* login */
266 		goto fmt;
267 	root = !strcmp(pw->pw_name, "root");
268 
269 	if (!(pw->pw_passwd = strsep(&bp, ":")))	/* passwd */
270 		goto fmt;
271 
272 	if (!(p = strsep(&bp, ":")))			/* uid */
273 		goto fmt;
274 	id = atol(p);
275 	if (root && id) {
276 		warnx("root uid should be 0");
277 		return (0);
278 	}
279 	if (id > USHRT_MAX) {
280 		warnx("%s > max uid value (%d)", p, USHRT_MAX);
281 		return (0);
282 	}
283 	pw->pw_uid = id;
284 	if ((*p == '\0') && (flags != (int *)NULL))
285 		*flags |= _PASSWORD_NOUID;
286 
287 	if (!(p = strsep(&bp, ":")))			/* gid */
288 		goto fmt;
289 	id = atol(p);
290 	if (id > USHRT_MAX) {
291 		warnx("%s > max gid value (%d)", p, USHRT_MAX);
292 		return (0);
293 	}
294 	pw->pw_gid = id;
295 	if ((*p == '\0') && (flags != (int *)NULL))
296 		*flags |= _PASSWORD_NOGID;
297 
298 	pw->pw_class = strsep(&bp, ":");		/* class */
299 	if (!(p = strsep(&bp, ":")))			/* change */
300 		goto fmt;
301 	pw->pw_change = atol(p);
302 	if ((*p == '\0') && (flags != (int *)NULL))
303 		*flags |= _PASSWORD_NOCHG;
304 	if (!(p = strsep(&bp, ":")))			/* expire */
305 		goto fmt;
306 	pw->pw_expire = atol(p);
307 	if ((*p == '\0') && (flags != (int *)NULL))
308 		*flags |= _PASSWORD_NOEXP;
309 	pw->pw_gecos = strsep(&bp, ":");		/* gecos */
310 	pw->pw_dir = strsep(&bp, ":");			/* directory */
311 	if (!(pw->pw_shell = strsep(&bp, ":")))		/* shell */
312 		goto fmt;
313 
314 	p = pw->pw_shell;
315 	if (root && *p)					/* empty == /bin/sh */
316 		for (setusershell();;) {
317 			if (!(sh = getusershell())) {
318 				warnx("warning, unknown root shell");
319 				break;
320 			}
321 			if (!strcmp(p, sh))
322 				break;
323 		}
324 
325 	if (p = strsep(&bp, ":")) {			/* too many */
326 fmt:		warnx("corrupted entry");
327 		return (0);
328 	}
329 
330 	return (1);
331 }
332 
333 void
334 pw_error(name, err, eval)
335 	const char *name;
336 	int err, eval;
337 {
338 	if (err)
339 		warn(name);
340 
341 	warnx("%s: unchanged", _PATH_MASTERPASSWD);
342 	pw_abort();
343 	exit(eval);
344 }
345 
346