1 /**	$MirOS: src/lib/libutil/passwd.c,v 1.2 2005/03/06 20:29:37 tg Exp $ */
2 /*	$OpenBSD: passwd.c,v 1.45 2004/11/04 18:44:59 millert Exp $	*/
3 
4 /*
5  * Copyright (c) 1987, 1993, 1994, 1995
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 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <sys/resource.h>
37 #include <sys/wait.h>
38 
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <pwd.h>
46 #include <err.h>
47 #include <errno.h>
48 #include <paths.h>
49 #include <signal.h>
50 #include <limits.h>
51 
52 #include "util.h"
53 
54 __RCSID("$MirOS: src/lib/libutil/passwd.c,v 1.2 2005/03/06 20:29:37 tg Exp $");
55 
56 static char pw_defdir[] = "/etc";
57 static char *pw_dir = pw_defdir;
58 static char *pw_lck;
59 
60 char *
pw_file(const char * nm)61 pw_file(const char *nm)
62 {
63 	const char *p = strrchr(nm, '/');
64 	char *new_nm;
65 
66 	if (p)
67 		p++;
68 	else
69 		p = nm;
70 
71 	if (asprintf(&new_nm, "%s/%s", pw_dir, p) == -1)
72 		return NULL;
73 	return new_nm;
74 }
75 
76 void
pw_setdir(const char * dir)77 pw_setdir(const char *dir)
78 {
79 	char *p;
80 
81 	if (strcmp (dir, pw_dir) == 0)
82 		return;
83 	if (pw_dir != pw_defdir)
84 		free(pw_dir);
85 	pw_dir = strdup(dir);
86 	if (pw_lck) {
87 		p = pw_file(pw_lck);
88 		free(pw_lck);
89 		pw_lck = p;
90 	}
91 }
92 
93 
94 int
pw_lock(int retries)95 pw_lock(int retries)
96 {
97 	int i, fd;
98 	mode_t old_mode;
99 	int save_errno;
100 
101 	if (!pw_lck)
102 		return (-1);
103 	/* Acquire the lock file.  */
104 	old_mode = umask(0);
105 	fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600);
106 	for (i = 0; i < retries && fd < 0 && errno == EEXIST; i++) {
107 		sleep(1);
108 		fd = open(pw_lck, O_WRONLY|O_CREAT|O_EXCL, 0600);
109 	}
110 	save_errno = errno;
111 	if (fd != -1 && fcntl(fd, F_SETFD, 1) == -1) {
112 		close(fd);
113 		fd = -1;
114 	}
115 	(void) umask(old_mode);
116 	errno = save_errno;
117 	return (fd);
118 }
119 
120 int
pw_mkdb(char * username,int flags)121 pw_mkdb(char *username, int flags)
122 {
123 	int pstat, ac;
124 	pid_t pid;
125 	char *av[8];
126 	struct stat sb;
127 
128 	if (pw_lck == NULL)
129 		return(-1);
130 
131 	/* A zero length passwd file is never ok */
132 	if (stat(pw_lck, &sb) == 0 && sb.st_size == 0) {
133 		warnx("%s is zero length", pw_lck);
134 		return (-1);
135 	}
136 
137 	ac = 0;
138 	av[ac++] = "pwd_mkdb";
139 	av[ac++] = "-d";
140 	av[ac++] = pw_dir;
141 	if (flags & _PASSWORD_SECUREONLY)
142 		av[ac++] = "-s";
143 	else if (!(flags & _PASSWORD_OMITV7))
144 		av[ac++] = "-p";
145 	if (username) {
146 		av[ac++] = "-u";
147 		av[ac++] = username;
148 	}
149 	av[ac++] = pw_lck;
150 	av[ac] = NULL;
151 
152 	pid = vfork();
153 	if (pid == -1)
154 		return (-1);
155 	if (pid == 0) {
156 		if (pw_lck)
157 			execv(_PATH_PWD_MKDB, av);
158 		_exit(1);
159 	}
160 	pid = waitpid(pid, &pstat, 0);
161 	if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
162 		return (-1);
163 	return (0);
164 }
165 
166 int
pw_abort(void)167 pw_abort(void)
168 {
169 	return (pw_lck ? unlink(pw_lck) : -1);
170 }
171 
172 /* Everything below this point is intended for the convenience of programs
173  * which allow a user to interactively edit the passwd file.  Errors in the
174  * routines below will cause the process to abort. */
175 
176 static pid_t editpid = -1;
177 
178 static void
pw_cont(int signo)179 pw_cont(int signo)
180 {
181 	int save_errno = errno;
182 
183 	if (editpid != -1)
184 		kill(editpid, signo);
185 	errno = save_errno;
186 }
187 
188 void
pw_init(void)189 pw_init(void)
190 {
191 	struct rlimit rlim;
192 
193 	/* Unlimited resource limits. */
194 	rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
195 	(void)setrlimit(RLIMIT_CPU, &rlim);
196 	(void)setrlimit(RLIMIT_TIME, &rlim);
197 	(void)setrlimit(RLIMIT_FSIZE, &rlim);
198 	(void)setrlimit(RLIMIT_STACK, &rlim);
199 	(void)setrlimit(RLIMIT_DATA, &rlim);
200 	(void)setrlimit(RLIMIT_RSS, &rlim);
201 
202 	/* Don't drop core (not really necessary, but GP's). */
203 	rlim.rlim_cur = rlim.rlim_max = 0;
204 	(void)setrlimit(RLIMIT_CORE, &rlim);
205 
206 	/* Turn off signals. */
207 	(void)signal(SIGALRM, SIG_IGN);
208 	(void)signal(SIGHUP, SIG_IGN);
209 	(void)signal(SIGINT, SIG_IGN);
210 	(void)signal(SIGPIPE, SIG_IGN);
211 	(void)signal(SIGQUIT, SIG_IGN);
212 	(void)signal(SIGTERM, SIG_IGN);
213 	(void)signal(SIGCONT, pw_cont);
214 
215 	if (!pw_lck)
216 		pw_lck = pw_file(_PATH_MASTERPASSWD_LOCK);
217 }
218 
219 void
pw_edit(int notsetuid,const char * filename)220 pw_edit(int notsetuid, const char *filename)
221 {
222 	int pstat;
223 	char *p;
224 	char * volatile editor;
225 	char *argp[] = {"sh", "-c", NULL, NULL};
226 
227 	if (!filename) {
228 		filename = pw_lck;
229 		if (!filename)
230 			return;
231 	}
232 
233 	if ((editor = getenv("EDITOR")) == NULL)
234 		editor = _PATH_VI;
235 
236 	if (asprintf(&p, "%s %s", editor, filename) == -1)
237 		return;
238 	argp[2] = p;
239 
240 	switch (editpid = vfork()) {
241 	case -1:			/* error */
242 		free(p);
243 		return;
244 	case 0:				/* child */
245 		if (notsetuid) {
246 			setgid(getgid());
247 			setuid(getuid());
248 		}
249 		execv(_PATH_BSHELL, argp);
250 		_exit(127);
251 	}
252 
253 	free(p);
254 	for (;;) {
255 		editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
256 		if (editpid == -1)
257 			pw_error(editor, 1, 1);
258 		else if (WIFSTOPPED(pstat))
259 			raise(WSTOPSIG(pstat));
260 		else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
261 			break;
262 		else
263 			pw_error(editor, 1, 1);
264 	}
265 	editpid = -1;
266 }
267 
268 void
pw_prompt(void)269 pw_prompt(void)
270 {
271 	int first, c;
272 
273 	(void)printf("re-edit the password file? [y]: ");
274 	(void)fflush(stdout);
275 	first = c = getchar();
276 	while (c != '\n' && c != EOF)
277 		c = getchar();
278 	switch (first) {
279 	case EOF:
280 		putchar('\n');
281 		/* FALLTHROUGH */
282 	case 'n':
283 	case 'N':
284 		pw_error(NULL, 0, 0);
285 		break;
286 	}
287 }
288 
289 static int
pw_equal(const struct passwd * pw1,const struct passwd * pw2)290 pw_equal(const struct passwd *pw1, const struct passwd *pw2)
291 {
292 	return (strcmp(pw1->pw_name, pw2->pw_name) == 0 &&
293 	    pw1->pw_uid == pw2->pw_uid &&
294 	    pw1->pw_gid == pw2->pw_gid &&
295 	    strcmp(pw1->pw_class, pw2->pw_class) == 0 &&
296 	    pw1->pw_change == pw2->pw_change &&
297 	    pw1->pw_expire == pw2->pw_expire &&
298 	    strcmp(pw1->pw_gecos, pw2->pw_gecos) == 0 &&
299 	    strcmp(pw1->pw_dir, pw2->pw_dir) == 0 &&
300 	    strcmp(pw1->pw_shell, pw2->pw_shell) == 0);
301 }
302 
303 void
pw_copy(int ffd,int tfd,const struct passwd * pw,const struct passwd * opw)304 pw_copy(int ffd, int tfd, const struct passwd *pw, const struct passwd *opw)
305 {
306 	struct passwd tpw;
307 	FILE   *from, *to;
308 	int	done;
309 	char   *p, *ep, buf[8192];
310 	char   *master = pw_file(_PATH_MASTERPASSWD);
311 
312 	if (!master)
313 		pw_error(NULL, 0, 1);
314 	if (!(from = fdopen(ffd, "r")))
315 		pw_error(master, 1, 1);
316 	if (!(to = fdopen(tfd, "w")))
317 		pw_error(pw_lck ? pw_lck : NULL, pw_lck ? 1 : 0, 1);
318 
319 	for (done = 0; fgets(buf, sizeof(buf), from);) {
320 		if ((ep = strchr(buf, '\n')) == NULL) {
321 			warnx("%s: line too long", master);
322 			pw_error(NULL, 0, 1);
323 		}
324 		if (done) {
325 			(void)fprintf(to, "%s", buf);
326 			if (ferror(to))
327 				goto err;
328 			continue;
329 		}
330 		if (!(p = strchr(buf, ':'))) {
331 			warnx("%s: corrupted entry", master);
332 			pw_error(NULL, 0, 1);
333 		}
334 		*p = '\0';
335 		if (strcmp(buf, opw ? opw->pw_name : pw->pw_name)) {
336 			*p = ':';
337 			(void)fprintf(to, "%s", buf);
338 			if (ferror(to))
339 				goto err;
340 			continue;
341 		}
342 		if (opw != NULL) {
343 			*p = ':';
344 			*ep = '\0';
345 			if (!pw_scan(buf, &tpw, NULL))
346 				pw_error(NULL, 0, 1);
347 			if (!pw_equal(&tpw, opw)) {
348 				warnx("%s: inconsistent entry", master);
349 				pw_error(NULL, 0, 1);
350 			}
351 		}
352 		(void)fprintf(to, "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n",
353 		    pw->pw_name, pw->pw_passwd, (u_int)pw->pw_uid,
354 		    (u_int)pw->pw_gid, pw->pw_class, (int64_t)pw->pw_change,
355 		    (int64_t)pw->pw_expire, pw->pw_gecos, pw->pw_dir,
356 		    pw->pw_shell);
357 		done = 1;
358 		if (ferror(to))
359 			goto err;
360 	}
361 	if (!done)
362 		(void)fprintf(to, "%s:%s:%d:%d:%s:%lld:%lld:%s:%s:%s\n",
363 		    pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
364 		    pw->pw_class, (int64_t)pw->pw_change,
365 		    (int64_t)pw->pw_expire, pw->pw_gecos,
366 		    pw->pw_dir, pw->pw_shell);
367 
368 	if (ferror(to))
369 err:
370 		pw_error(NULL, 0, 1);
371 	free(master);
372 	(void)fclose(to);
373 }
374 
375 int
pw_scan(char * bp,struct passwd * pw,int * flags)376 pw_scan(char *bp, struct passwd *pw, int *flags)
377 {
378 	u_long id;
379 	int root;
380 	char *p, *sh, *p2;
381 
382 	if (flags != (int *)NULL)
383 		*flags = 0;
384 
385 	if (!(p = strsep(&bp, ":")) || *p == '\0')	/* login */
386 		goto fmt;
387 	pw->pw_name = p;
388 	root = !strcmp(pw->pw_name, "root");
389 
390 	if (!(pw->pw_passwd = strsep(&bp, ":")))	/* passwd */
391 		goto fmt;
392 
393 	if (!(p = strsep(&bp, ":")))			/* uid */
394 		goto fmt;
395 	id = strtoul(p, &p2, 10);
396 	if (root && id) {
397 		warnx("root uid should be 0");
398 		return (0);
399 	}
400 	if (*p2 != '\0') {
401 		warnx("illegal uid field");
402 		return (0);
403 	}
404 	if (id > UID_MAX) {
405 		/* errno is set to ERANGE by strtoul(3) */
406 		warnx("uid greater than %u", UID_MAX-1);
407 		return (0);
408 	}
409 	pw->pw_uid = (uid_t)id;
410 	if ((*p == '\0') && (flags != (int *)NULL))
411 		*flags |= _PASSWORD_NOUID;
412 
413 	if (!(p = strsep(&bp, ":")))			/* gid */
414 		goto fmt;
415 	id = strtoul(p, &p2, 10);
416 	if (*p2 != '\0') {
417 		warnx("illegal gid field");
418 		return (0);
419 	}
420 	if (id > UID_MAX) {
421 		/* errno is set to ERANGE by strtoul(3) */
422 		warnx("gid greater than %u", UID_MAX-1);
423 		return (0);
424 	}
425 	pw->pw_gid = (gid_t)id;
426 	if ((*p == '\0') && (flags != (int *)NULL))
427 		*flags |= _PASSWORD_NOGID;
428 
429 	pw->pw_class = strsep(&bp, ":");		/* class */
430 	if (!(p = strsep(&bp, ":")))			/* change */
431 		goto fmt;
432 	pw->pw_change = atol(p);
433 	if ((*p == '\0') && (flags != (int *)NULL))
434 		*flags |= _PASSWORD_NOCHG;
435 	if (!(p = strsep(&bp, ":")))			/* expire */
436 		goto fmt;
437 	pw->pw_expire = atol(p);
438 	if ((*p == '\0') && (flags != (int *)NULL))
439 		*flags |= _PASSWORD_NOEXP;
440 	pw->pw_gecos = strsep(&bp, ":");		/* gecos */
441 	pw->pw_dir = strsep(&bp, ":");			/* directory */
442 	if (!(pw->pw_shell = strsep(&bp, ":")))		/* shell */
443 		goto fmt;
444 
445 	p = pw->pw_shell;
446 	if (root && *p) {				/* empty == /bin/sh */
447 		for (setusershell();;) {
448 			if (!(sh = getusershell())) {
449 				warnx("warning, unknown root shell");
450 				break;
451 			}
452 			if (!strcmp(p, sh))
453 				break;
454 		}
455 		endusershell();
456 	}
457 
458 	if ((p = strsep(&bp, ":"))) {			/* too many */
459 fmt:		warnx("corrupted entry");
460 		return (0);
461 	}
462 
463 	return (1);
464 }
465 
466 __dead void
pw_error(const char * name,int err,int eval)467 pw_error(const char *name, int err, int eval)
468 {
469 	char   *master = pw_file(_PATH_MASTERPASSWD);
470 
471 	if (err) {
472 		if (name)
473 			warn("%s", name);
474 		else
475 			warn(NULL);
476 	}
477 	if (master) {
478 		warnx("%s: unchanged", master);
479 		free(master);
480 	}
481 
482 	pw_abort();
483 	exit(eval);
484 }
485