xref: /freebsd/usr.sbin/pw/pw_user.c (revision 142b4309f42d5018a92ad5e08943fd1081a9ea57)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (C) 1996
5  *	David L. Nugent.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  */
29 
30 #include <sys/param.h>
31 #include <sys/wait.h>
32 
33 #include <assert.h>
34 #include <ctype.h>
35 #include <dirent.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <grp.h>
40 #include <pwd.h>
41 #include <libutil.h>
42 #include <login_cap.h>
43 #include <paths.h>
44 #include <string.h>
45 #include <sysexits.h>
46 #include <termios.h>
47 #include <unistd.h>
48 #include <spawn.h>
49 
50 #include "pw.h"
51 #include "bitmap.h"
52 #include "psdate.h"
53 
54 #define LOGNAMESIZE (MAXLOGNAME-1)
55 
56 extern char **environ;
57 static		char locked_str[] = "*LOCKED*";
58 
59 static struct passwd fakeuser = {
60 	"nouser",
61 	"*",
62 	-1,
63 	-1,
64 	0,
65 	"",
66 	"User &",
67 	"/nonexistent",
68 	"/bin/sh",
69 	0,
70 	0
71 };
72 
73 static int	 print_user(struct passwd *pwd, bool pretty, bool v7);
74 static uid_t	 pw_uidpolicy(struct userconf *cnf, intmax_t id);
75 static uid_t	 pw_gidpolicy(struct userconf *cnf, char *grname, char *nam,
76     gid_t prefer, bool dryrun);
77 static char	*pw_homepolicy(struct userconf * cnf, char *homedir,
78     const char *user);
79 static char	*pw_shellpolicy(struct userconf * cnf);
80 static char	*pw_password(struct userconf * cnf, char const * user);
81 static char	*shell_path(char const * path, char *shells[], char *sh);
82 static void	rmat(uid_t uid);
83 
84 static void
mkdir_home_parents(int dfd,const char * dir)85 mkdir_home_parents(int dfd, const char *dir)
86 {
87 	struct stat st;
88 	char *dirs, *tmp;
89 
90 	if (*dir != '/')
91 		errx(EX_DATAERR, "invalid base directory for home '%s'", dir);
92 
93 	dir++;
94 
95 	if (fstatat(dfd, dir, &st, 0) != -1) {
96 		if (S_ISDIR(st.st_mode))
97 			return;
98 		errx(EX_OSFILE, "root home `/%s' is not a directory", dir);
99 	}
100 
101 	dirs = strdup(dir);
102 	if (dirs == NULL)
103 		errx(EX_UNAVAILABLE, "out of memory");
104 
105 	tmp = strrchr(dirs, '/');
106 	if (tmp == NULL) {
107 		free(dirs);
108 		return;
109 	}
110 	tmp[0] = '\0';
111 
112 	tmp = dirs;
113 	if (fstatat(dfd, dirs, &st, 0) == -1) {
114 		while ((tmp = strchr(tmp + 1, '/')) != NULL) {
115 			*tmp = '\0';
116 			if (fstatat(dfd, dirs, &st, 0) == -1) {
117 				if (mkdirat(dfd, dirs, _DEF_DIRMODE) == -1)
118 					err(EX_OSFILE,  "'%s' (home parent) is not a directory", dirs);
119 			}
120 			*tmp = '/';
121 		}
122 	}
123 	if (fstatat(dfd, dirs, &st, 0) == -1) {
124 		if (mkdirat(dfd, dirs, _DEF_DIRMODE) == -1)
125 			err(EX_OSFILE,  "'%s' (home parent) is not a directory", dirs);
126 		fchownat(dfd, dirs, 0, 0, 0);
127 	}
128 
129 	free(dirs);
130 }
131 
132 static void
create_and_populate_homedir(struct userconf * cnf,struct passwd * pwd,const char * skeldir,mode_t homemode,bool update)133 create_and_populate_homedir(struct userconf *cnf, struct passwd *pwd,
134     const char *skeldir, mode_t homemode, bool update)
135 {
136 	int skelfd = -1;
137 
138 	/* Create home parents directories */
139 	mkdir_home_parents(conf.rootfd, pwd->pw_dir);
140 
141 	if (skeldir != NULL && *skeldir != '\0') {
142 		if (*skeldir == '/')
143 			skeldir++;
144 		skelfd = openat(conf.rootfd, skeldir, O_DIRECTORY|O_CLOEXEC);
145 	}
146 
147 	copymkdir(conf.rootfd, pwd->pw_dir, skelfd, homemode, pwd->pw_uid,
148 	    pwd->pw_gid, 0);
149 	pw_log(cnf, update ? M_MODIFY : M_ADD, W_USER, "%s(%ju) home %s made",
150 	    pwd->pw_name, (uintmax_t)pwd->pw_uid, pwd->pw_dir);
151 }
152 
153 static int
pw_set_passwd(struct passwd * pwd,int fd,bool precrypted,bool update)154 pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update)
155 {
156 	int		 b, istty;
157 	struct termios	 t, n;
158 	login_cap_t	*lc;
159 	char		line[_PASSWORD_LEN+1];
160 	char		*p;
161 
162 	if (fd == '-') {
163 		if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
164 			pwd->pw_passwd = "*";	/* No access */
165 			return (1);
166 		}
167 		return (0);
168 	}
169 
170 	if ((istty = isatty(fd))) {
171 		if (tcgetattr(fd, &t) == -1)
172 			istty = 0;
173 		else {
174 			n = t;
175 			n.c_lflag &= ~(ECHO);
176 			tcsetattr(fd, TCSANOW, &n);
177 			printf("%s%spassword for user %s:",
178 			    update ? "new " : "",
179 			    precrypted ? "encrypted " : "",
180 			    pwd->pw_name);
181 			fflush(stdout);
182 		}
183 	}
184 	b = read(fd, line, sizeof(line) - 1);
185 	if (istty) {	/* Restore state */
186 		tcsetattr(fd, TCSANOW, &t);
187 		fputc('\n', stdout);
188 		fflush(stdout);
189 	}
190 
191 	if (b < 0)
192 		err(EX_IOERR, "-%c file descriptor",
193 		    precrypted ? 'H' : 'h');
194 	line[b] = '\0';
195 	if ((p = strpbrk(line, "\r\n")) != NULL)
196 		*p = '\0';
197 	if (!*line)
198 		errx(EX_DATAERR, "empty password read on file descriptor %d",
199 		    fd);
200 	if (precrypted) {
201 		if (strchr(line, ':') != NULL)
202 			errx(EX_DATAERR, "bad encrypted password");
203 		pwd->pw_passwd = strdup(line);
204 	} else {
205 		lc = login_getpwclass(pwd);
206 		if (lc == NULL ||
207 				login_setcryptfmt(lc, "sha512", NULL) == NULL)
208 			warn("setting crypt(3) format");
209 		login_close(lc);
210 		pwd->pw_passwd = pw_pwcrypt(line);
211 	}
212 	return (1);
213 }
214 
215 static void
perform_chgpwent(const char * name,struct passwd * pwd,char * nispasswd)216 perform_chgpwent(const char *name, struct passwd *pwd, char *nispasswd)
217 {
218 	int rc;
219 	struct passwd *nispwd;
220 
221 	/* duplicate for nis so that chgpwent is not modifying before NIS */
222 	if (nispasswd && *nispasswd == '/')
223 		nispwd = pw_dup(pwd);
224 
225 	rc = chgpwent(name, pwd);
226 	if (rc == -1)
227 		errx(EX_IOERR, "user '%s' does not exist (NIS?)", pwd->pw_name);
228 	else if (rc != 0)
229 		err(EX_IOERR, "passwd file update");
230 
231 	if (nispasswd && *nispasswd == '/') {
232 		rc = chgnispwent(nispasswd, name, nispwd);
233 		if (rc == -1)
234 			warn("User '%s' not found in NIS passwd", pwd->pw_name);
235 		else if (rc != 0)
236 			warn("NIS passwd update");
237 		/* NOTE: NIS-only update errors are not fatal */
238 	}
239 }
240 
241 static void
pw_check_root(void)242 pw_check_root(void)
243 {
244 	if (!conf.altroot && geteuid() != 0)
245 		errx(EX_NOPERM, "you must be root");
246 }
247 
248 /*
249  * The M_LOCK and M_UNLOCK functions simply add or remove
250  * a "*LOCKED*" prefix from in front of the password to
251  * prevent it decoding correctly, and therefore prevents
252  * access. Of course, this only prevents access via
253  * password authentication (not ssh, kerberos or any
254  * other method that does not use the UNIX password) but
255  * that is a known limitation.
256  */
257 static int
pw_userlock(char * arg1,int mode)258 pw_userlock(char *arg1, int mode)
259 {
260 	struct passwd *pwd = NULL;
261 	char *passtmp = NULL;
262 	char *name;
263 	bool locked = false;
264 	uid_t id = (uid_t)-1;
265 
266 	pw_check_root();
267 
268 	if (arg1 == NULL)
269 		errx(EX_DATAERR, "username or id required");
270 
271 	name = arg1;
272 	if (arg1[strspn(name, "0123456789")] == '\0')
273 		id = pw_checkid(name, UID_MAX);
274 
275 	pwd = GETPWNAM(pw_checkname(name, 0));
276 	if (pwd == NULL && id != (uid_t)-1) {
277 		pwd = GETPWUID(id);
278 		if (pwd != NULL)
279 			name = pwd->pw_name;
280 	}
281 	if (pwd == NULL) {
282 		if (id == (uid_t)-1)
283 			errx(EX_NOUSER, "no such name or uid `%ju'", (uintmax_t) id);
284 		errx(EX_NOUSER, "no such user `%s'", name);
285 	}
286 
287 	if (name == NULL)
288 		name = pwd->pw_name;
289 
290 	if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str) -1) == 0)
291 		locked = true;
292 	if (mode == M_LOCK && locked)
293 		errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
294 	if (mode == M_UNLOCK && !locked)
295 		errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
296 
297 	if (mode == M_LOCK) {
298 		asprintf(&passtmp, "%s%s", locked_str, pwd->pw_passwd);
299 		if (passtmp == NULL)	/* disaster */
300 			errx(EX_UNAVAILABLE, "out of memory");
301 		pwd->pw_passwd = passtmp;
302 	} else {
303 		pwd->pw_passwd += sizeof(locked_str)-1;
304 	}
305 
306 	perform_chgpwent(name, pwd, NULL);
307 	free(passtmp);
308 
309 	return (EXIT_SUCCESS);
310 }
311 
312 static uid_t
pw_uidpolicy(struct userconf * cnf,intmax_t id)313 pw_uidpolicy(struct userconf * cnf, intmax_t id)
314 {
315 	struct passwd  *pwd;
316 	struct bitmap   bm;
317 	uid_t           uid = (uid_t) - 1;
318 
319 	/*
320 	 * Check the given uid, if any
321 	 */
322 	if (id >= 0) {
323 		uid = (uid_t) id;
324 
325 		if ((pwd = GETPWUID(uid)) != NULL && conf.checkduplicate)
326 			errx(EX_DATAERR, "uid `%ju' has already been allocated",
327 			    (uintmax_t)pwd->pw_uid);
328 		return (uid);
329 	}
330 	/*
331 	 * We need to allocate the next available uid under one of
332 	 * two policies a) Grab the first unused uid b) Grab the
333 	 * highest possible unused uid
334 	 */
335 	if (cnf->min_uid >= cnf->max_uid) {	/* Sanity
336 						 * claus^H^H^H^Hheck */
337 		cnf->min_uid = 1000;
338 		cnf->max_uid = 32000;
339 	}
340 	bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
341 
342 	/*
343 	 * Now, let's fill the bitmap from the password file
344 	 */
345 	SETPWENT();
346 	while ((pwd = GETPWENT()) != NULL)
347 		if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
348 			bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
349 	ENDPWENT();
350 
351 	/*
352 	 * Then apply the policy, with fallback to reuse if necessary
353 	 */
354 	if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
355 		uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
356 
357 	/*
358 	 * Another sanity check
359 	 */
360 	if (uid < cnf->min_uid || uid > cnf->max_uid)
361 		errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
362 	bm_dealloc(&bm);
363 	return (uid);
364 }
365 
366 static uid_t
pw_gidpolicy(struct userconf * cnf,char * grname,char * nam,gid_t prefer,bool dryrun)367 pw_gidpolicy(struct userconf *cnf, char *grname, char *nam, gid_t prefer, bool dryrun)
368 {
369 	struct group   *grp;
370 	gid_t           gid = (uid_t) - 1;
371 
372 	/*
373 	 * Check the given gid, if any
374 	 */
375 	SETGRENT();
376 	if (grname) {
377 		if ((grp = GETGRNAM(grname)) == NULL) {
378 			gid = pw_checkid(grname, GID_MAX);
379 			grp = GETGRGID(gid);
380 		}
381 		gid = grp->gr_gid;
382 	} else if ((grp = GETGRNAM(nam)) != NULL) {
383 		gid = grp->gr_gid;  /* Already created? Use it anyway... */
384 	} else {
385 		intmax_t		grid = -1;
386 
387 		/*
388 		 * We need to auto-create a group with the user's name. We
389 		 * can send all the appropriate output to our sister routine
390 		 * bit first see if we can create a group with gid==uid so we
391 		 * can keep the user and group ids in sync. We purposely do
392 		 * NOT check the gid range if we can force the sync. If the
393 		 * user's name dups an existing group, then the group add
394 		 * function will happily handle that case for us and exit.
395 		 */
396 		if (GETGRGID(prefer) == NULL)
397 			grid = prefer;
398 		if (dryrun) {
399 			gid = pw_groupnext(cnf, true);
400 		} else {
401 			if (grid == -1)
402 				grid =  pw_groupnext(cnf, true);
403 			groupadd(cnf, nam, grid, NULL, -1, false, false, false);
404 			if ((grp = GETGRNAM(nam)) != NULL)
405 				gid = grp->gr_gid;
406 		}
407 	}
408 	ENDGRENT();
409 	return (gid);
410 }
411 
412 static char *
pw_homepolicy(struct userconf * cnf,char * homedir,const char * user)413 pw_homepolicy(struct userconf * cnf, char *homedir, const char *user)
414 {
415 	static char     home[128];
416 
417 	if (homedir)
418 		return (homedir);
419 
420 	if (cnf->home == NULL || *cnf->home == '\0')
421 		errx(EX_CONFIG, "no base home directory set");
422 	snprintf(home, sizeof(home), "%s/%s", cnf->home, user);
423 
424 	return (home);
425 }
426 
427 static char *
shell_path(char const * path,char * shells[],char * sh)428 shell_path(char const * path, char *shells[], char *sh)
429 {
430 	if (sh != NULL && (*sh == '/' || *sh == '\0'))
431 		return sh;	/* specified full path or forced none */
432 	else {
433 		char           *p;
434 		char            paths[_UC_MAXLINE];
435 
436 		/*
437 		 * We need to search paths
438 		 */
439 		strlcpy(paths, path, sizeof(paths));
440 		for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
441 			int             i;
442 			static char     shellpath[256];
443 
444 			if (sh != NULL) {
445 				snprintf(shellpath, sizeof(shellpath), "%s/%s", p, sh);
446 				if (access(shellpath, X_OK) == 0)
447 					return shellpath;
448 			} else
449 				for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
450 					snprintf(shellpath, sizeof(shellpath), "%s/%s", p, shells[i]);
451 					if (access(shellpath, X_OK) == 0)
452 						return shellpath;
453 				}
454 		}
455 		if (sh == NULL)
456 			errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
457 		errx(EX_CONFIG, "no default shell available or defined");
458 		return NULL;
459 	}
460 }
461 
462 static char *
pw_shellpolicy(struct userconf * cnf)463 pw_shellpolicy(struct userconf * cnf)
464 {
465 
466 	return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default);
467 }
468 
469 #define	SALTSIZE	32
470 
471 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
472 
473 char *
pw_pwcrypt(char * password)474 pw_pwcrypt(char *password)
475 {
476 	int             i;
477 	char            salt[SALTSIZE + 1];
478 	char		*cryptpw;
479 	static char     buf[256];
480 	size_t		pwlen;
481 
482 	/*
483 	 * Calculate a salt value
484 	 */
485 	for (i = 0; i < SALTSIZE; i++)
486 		salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
487 	salt[SALTSIZE] = '\0';
488 
489 	cryptpw = crypt(password, salt);
490 	if (cryptpw == NULL)
491 		errx(EX_CONFIG, "crypt(3) failure");
492 	pwlen = strlcpy(buf, cryptpw, sizeof(buf));
493 	assert(pwlen < sizeof(buf));
494 	return (buf);
495 }
496 
497 static char *
pw_password(struct userconf * cnf,char const * user)498 pw_password(struct userconf * cnf, char const * user)
499 {
500 	int             i, l;
501 	char            pwbuf[32];
502 
503 	switch (cnf->default_password) {
504 	case P_NONE:		/* No password at all! */
505 		return "";
506 	case P_RANDOM:			/* Random password */
507 		l = (arc4random() % 8 + 8);	/* 8 - 16 chars */
508 		for (i = 0; i < l; i++)
509 			pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
510 		pwbuf[i] = '\0';
511 
512 		/*
513 		 * We give this information back to the user
514 		 */
515 		if (conf.fd == -1) {
516 			if (isatty(STDOUT_FILENO))
517 				printf("Password for '%s' is: ", user);
518 			printf("%s\n", pwbuf);
519 			fflush(stdout);
520 		}
521 		break;
522 	case P_YES:		/* user's name */
523 		strlcpy(pwbuf, user, sizeof(pwbuf));
524 		break;
525 	case P_NO:		/* No login - default */
526 				/* FALLTHROUGH */
527 	default:
528 		return "*";
529 	}
530 	return pw_pwcrypt(pwbuf);
531 }
532 
533 static int
print_user(struct passwd * pwd,bool pretty,bool v7)534 print_user(struct passwd * pwd, bool pretty, bool v7)
535 {
536 	int		j;
537 	char           *p;
538 	struct group   *grp = GETGRGID(pwd->pw_gid);
539 	char            uname[60] = "User &", office[60] = "[None]",
540 			wphone[60] = "[None]", hphone[60] = "[None]";
541 	char		acexpire[32] = "[None]", pwexpire[32] = "[None]";
542 	struct tm *    tptr;
543 
544 	if (!pretty) {
545 		p = v7 ? pw_make_v7(pwd) : pw_make(pwd);
546 		printf("%s\n", p);
547 		free(p);
548 		return (EXIT_SUCCESS);
549 	}
550 
551 	if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
552 		strlcpy(uname, p, sizeof(uname));
553 		if ((p = strtok(NULL, ",")) != NULL) {
554 			strlcpy(office, p, sizeof(office));
555 			if ((p = strtok(NULL, ",")) != NULL) {
556 				strlcpy(wphone, p, sizeof(wphone));
557 				if ((p = strtok(NULL, "")) != NULL) {
558 					strlcpy(hphone, p, sizeof(hphone));
559 				}
560 			}
561 		}
562 	}
563 	/*
564 	 * Handle '&' in gecos field
565 	 */
566 	if ((p = strchr(uname, '&')) != NULL) {
567 		int             l = strlen(pwd->pw_name);
568 		int             m = strlen(p);
569 
570 		memmove(p + l, p + 1, m);
571 		memmove(p, pwd->pw_name, l);
572 		*p = (char) toupper((unsigned char)*p);
573 	}
574 	if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
575 		strftime(acexpire, sizeof acexpire, "%c", tptr);
576 	if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
577 		strftime(pwexpire, sizeof pwexpire, "%c", tptr);
578 	printf("Login Name: %-15s   #%-12ju Group: %-15s   #%ju\n"
579 	       " Full Name: %s\n"
580 	       "      Home: %-26.26s      Class: %s\n"
581 	       "     Shell: %-26.26s     Office: %s\n"
582 	       "Work Phone: %-26.26s Home Phone: %s\n"
583 	       "Acc Expire: %-26.26s Pwd Expire: %s\n",
584 	       pwd->pw_name, (uintmax_t)pwd->pw_uid,
585 	       grp ? grp->gr_name : "(invalid)", (uintmax_t)pwd->pw_gid,
586 	       uname, pwd->pw_dir, pwd->pw_class,
587 	       pwd->pw_shell, office, wphone, hphone,
588 	       acexpire, pwexpire);
589         SETGRENT();
590 	j = 0;
591 	while ((grp=GETGRENT()) != NULL) {
592 		int     i = 0;
593 		if (grp->gr_mem != NULL) {
594 			while (grp->gr_mem[i] != NULL) {
595 				if (strcmp(grp->gr_mem[i], pwd->pw_name)==0) {
596 					printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
597 					break;
598 				}
599 				++i;
600 			}
601 		}
602 	}
603 	ENDGRENT();
604 	printf("%s", j ? "\n" : "");
605 	return (EXIT_SUCCESS);
606 }
607 
608 char *
pw_checkname(char * name,int gecos)609 pw_checkname(char *name, int gecos)
610 {
611 	char showch[8];
612 	const char *badchars, *ch, *showtype;
613 	int reject;
614 
615 	ch = name;
616 	reject = 0;
617 	if (gecos) {
618 		/* See if the name is valid as a gecos (comment) field. */
619 		badchars = ":";
620 		showtype = "gecos field";
621 	} else {
622 		/* See if the name is valid as a userid or group. */
623 		badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\";";
624 		showtype = "userid/group name";
625 		/* Userids and groups can not have a leading '-'. */
626 		if (*ch == '-')
627 			reject = 1;
628 	}
629 	if (!reject) {
630 		while (*ch) {
631 			if (strchr(badchars, *ch) != NULL ||
632 			    (!gecos && *ch < ' ') ||
633 			    *ch == 127) {
634 				reject = 1;
635 				break;
636 			}
637 			/* 8-bit characters are only allowed in GECOS fields */
638 			if (!gecos && (*ch & 0x80)) {
639 				reject = 1;
640 				break;
641 			}
642 			ch++;
643 		}
644 	}
645 	/*
646 	 * A `$' is allowed as the final character for userids and groups,
647 	 * mainly for the benefit of samba.
648 	 */
649 	if (reject && !gecos) {
650 		if (*ch == '$' && *(ch + 1) == '\0') {
651 			reject = 0;
652 			ch++;
653 		}
654 	}
655 	if (reject) {
656 		snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
657 		    ? "`%c'" : "0x%02x", *ch);
658 		errx(EX_DATAERR, "invalid character %s at position %td in %s",
659 		    showch, (ch - name), showtype);
660 	}
661 	if (!gecos && (ch - name) > LOGNAMESIZE)
662 		errx(EX_USAGE, "name too long `%s' (max is %d)", name,
663 		    LOGNAMESIZE);
664 
665 	return (name);
666 }
667 
668 static void
rmat(uid_t uid)669 rmat(uid_t uid)
670 {
671 	DIR            *d = opendir("/var/at/jobs");
672 
673 	if (d != NULL) {
674 		struct dirent  *e;
675 
676 		while ((e = readdir(d)) != NULL) {
677 			struct stat     st;
678 			pid_t		pid;
679 
680 			if (strncmp(e->d_name, ".lock", 5) != 0 &&
681 			    stat(e->d_name, &st) == 0 &&
682 			    !S_ISDIR(st.st_mode) &&
683 			    st.st_uid == uid) {
684 				const char *argv[] = {
685 					"/usr/sbin/atrm",
686 					e->d_name,
687 					NULL
688 				};
689 				if (posix_spawn(&pid, argv[0], NULL, NULL,
690 				    (char *const *) argv, environ)) {
691 					warn("Failed to execute '%s %s'",
692 					    argv[0], argv[1]);
693 				} else
694 					(void) waitpid(pid, NULL, 0);
695 			}
696 		}
697 		closedir(d);
698 	}
699 }
700 
701 int
pw_user_next(int argc,char ** argv,char * name __unused)702 pw_user_next(int argc, char **argv, char *name __unused)
703 {
704 	struct userconf *cnf = NULL;
705 	const char *cfg = NULL;
706 	int ch;
707 	bool quiet = false;
708 	uid_t next;
709 
710 	while ((ch = getopt(argc, argv, "C:q")) != -1) {
711 		switch (ch) {
712 		case 'C':
713 			cfg = optarg;
714 			break;
715 		case 'q':
716 			quiet = true;
717 			break;
718 		default:
719 			usage();
720 		}
721 	}
722 	argc -= optind;
723 	argv += optind;
724 	if (argc > 0)
725 		usage();
726 
727 	if (quiet)
728 		freopen(_PATH_DEVNULL, "w", stderr);
729 
730 	cnf = get_userconfig(cfg);
731 
732 	next = pw_uidpolicy(cnf, -1);
733 
734 	printf("%ju:", (uintmax_t)next);
735 	pw_groupnext(cnf, quiet);
736 
737 	return (EXIT_SUCCESS);
738 }
739 
740 int
pw_user_show(int argc,char ** argv,char * arg1)741 pw_user_show(int argc, char **argv, char *arg1)
742 {
743 	struct passwd *pwd = NULL;
744 	char *name = NULL;
745 	intmax_t id = -1;
746 	int ch;
747 	bool all = false;
748 	bool pretty = false;
749 	bool force = false;
750 	bool v7 = false;
751 	bool quiet = false;
752 
753 	if (arg1 != NULL) {
754 		if (arg1[strspn(arg1, "0123456789")] == '\0')
755 			id = pw_checkid(arg1, UID_MAX);
756 		else
757 			name = arg1;
758 	}
759 
760 	while ((ch = getopt(argc, argv, "C:qn:u:FPa7")) != -1) {
761 		switch (ch) {
762 		case 'C':
763 			/* ignore compatibility */
764 			break;
765 		case 'q':
766 			quiet = true;
767 			break;
768 		case 'n':
769 			name = optarg;
770 			break;
771 		case 'u':
772 			id = pw_checkid(optarg, UID_MAX);
773 			break;
774 		case 'F':
775 			force = true;
776 			break;
777 		case 'P':
778 			pretty = true;
779 			break;
780 		case 'a':
781 			all = true;
782 			break;
783 		case '7':
784 			v7 = true;
785 			break;
786 		default:
787 			usage();
788 		}
789 	}
790 	argc -= optind;
791 	argv += optind;
792 	if (argc > 0)
793 		usage();
794 
795 	if (quiet)
796 		freopen(_PATH_DEVNULL, "w", stderr);
797 
798 	if (all) {
799 		SETPWENT();
800 		while ((pwd = GETPWENT()) != NULL)
801 			print_user(pwd, pretty, v7);
802 		ENDPWENT();
803 		return (EXIT_SUCCESS);
804 	}
805 
806 	if (id < 0 && name == NULL)
807 		errx(EX_DATAERR, "username or id required");
808 
809 	pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
810 	if (pwd == NULL) {
811 		if (force) {
812 			pwd = &fakeuser;
813 		} else {
814 			if (name == NULL)
815 				errx(EX_NOUSER, "no such uid `%ju'",
816 				    (uintmax_t) id);
817 			errx(EX_NOUSER, "no such user `%s'", name);
818 		}
819 	}
820 
821 	return (print_user(pwd, pretty, v7));
822 }
823 
824 int
pw_user_del(int argc,char ** argv,char * arg1)825 pw_user_del(int argc, char **argv, char *arg1)
826 {
827 	struct userconf *cnf = NULL;
828 	struct passwd *pwd = NULL;
829 	struct group *gr, *grp;
830 	char *name = NULL;
831 	char grname[MAXLOGNAME];
832 	char *nispasswd = NULL;
833 	char file[MAXPATHLEN];
834 	char home[MAXPATHLEN];
835 	const char *cfg = NULL;
836 	struct stat st;
837 	intmax_t id = -1;
838 	int ch, rc;
839 	bool nis = false;
840 	bool deletehome = false;
841 	bool quiet = false;
842 
843 	if (arg1 != NULL) {
844 		if (arg1[strspn(arg1, "0123456789")] == '\0')
845 			id = pw_checkid(arg1, UID_MAX);
846 		else
847 			name = arg1;
848 	}
849 
850 	while ((ch = getopt(argc, argv, "C:qn:u:rYy:")) != -1) {
851 		switch (ch) {
852 		case 'C':
853 			cfg = optarg;
854 			break;
855 		case 'q':
856 			quiet = true;
857 			break;
858 		case 'n':
859 			name = optarg;
860 			break;
861 		case 'u':
862 			id = pw_checkid(optarg, UID_MAX);
863 			break;
864 		case 'r':
865 			deletehome = true;
866 			break;
867 		case 'y':
868 			nispasswd = optarg;
869 			break;
870 		case 'Y':
871 			nis = true;
872 			break;
873 		default:
874 			usage();
875 		}
876 	}
877 	argc -= optind;
878 	argv += optind;
879 	if (argc > 0)
880 		usage();
881 
882 	if (quiet)
883 		freopen(_PATH_DEVNULL, "w", stderr);
884 
885 	if (id < 0 && name == NULL)
886 		errx(EX_DATAERR, "username or id required");
887 
888 	cnf = get_userconfig(cfg);
889 
890 	if (nispasswd == NULL)
891 		nispasswd = cnf->nispasswd;
892 
893 	pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
894 	if (pwd == NULL) {
895 		if (name == NULL)
896 			errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id);
897 		errx(EX_NOUSER, "no such user `%s'", name);
898 	}
899 
900 	if (PWF._altdir == PWF_REGULAR &&
901 	    ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
902 		if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
903 			if (!nis && nispasswd && *nispasswd != '/')
904 				errx(EX_NOUSER, "Cannot remove NIS user `%s'",
905 				    name);
906 		} else {
907 			errx(EX_NOUSER, "Cannot remove non local user `%s'",
908 			    name);
909 		}
910 	}
911 
912 	id = pwd->pw_uid;
913 	if (name == NULL)
914 		name = pwd->pw_name;
915 
916 	if (strcmp(pwd->pw_name, "root") == 0)
917 		errx(EX_DATAERR, "cannot remove user 'root'");
918 
919 	if (!PWALTDIR()) {
920 		/* Remove crontabs */
921 		snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name);
922 		if (access(file, F_OK) == 0) {
923 			const char *argv[] = {
924 				"crontab",
925 				"-u",
926 				pwd->pw_name,
927 				"-r",
928 				NULL
929 			};
930 			pid_t pid;
931 
932 			if (posix_spawnp(&pid, argv[0], NULL, NULL,
933 						(char *const *) argv, environ)) {
934 				warn("Failed to execute '%s %s'",
935 						argv[0], argv[1]);
936 			} else
937 				(void) waitpid(pid, NULL, 0);
938 		}
939 	}
940 
941 	/*
942 	 * Save these for later, since contents of pwd may be
943 	 * invalidated by deletion
944 	 */
945 	snprintf(file, sizeof(file), "%s/%s", _PATH_MAILDIR, pwd->pw_name);
946 	strlcpy(home, pwd->pw_dir, sizeof(home));
947 	gr = GETGRGID(pwd->pw_gid);
948 	if (gr != NULL)
949 		strlcpy(grname, gr->gr_name, LOGNAMESIZE);
950 	else
951 		grname[0] = '\0';
952 
953 	rc = delpwent(pwd);
954 	if (rc == -1)
955 		err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
956 	else if (rc != 0)
957 		err(EX_IOERR, "passwd update");
958 
959 	if (nis && nispasswd && *nispasswd=='/') {
960 		rc = delnispwent(nispasswd, name);
961 		if (rc == -1)
962 			warnx("WARNING: user '%s' does not exist in NIS passwd",
963 			    pwd->pw_name);
964 		else if (rc != 0)
965 			warn("WARNING: NIS passwd update");
966 	}
967 
968 	grp = GETGRNAM(name);
969 	if (grp != NULL &&
970 	    (grp->gr_mem == NULL || *grp->gr_mem == NULL) &&
971 	    strcmp(name, grname) == 0)
972 		delgrent(GETGRNAM(name));
973 	SETGRENT();
974 	while ((grp = GETGRENT()) != NULL) {
975 		int i, j;
976 		char group[MAXLOGNAME];
977 		if (grp->gr_mem == NULL)
978 			continue;
979 
980 		for (i = 0; grp->gr_mem[i] != NULL; i++) {
981 			if (strcmp(grp->gr_mem[i], name) != 0)
982 				continue;
983 
984 			for (j = i; grp->gr_mem[j] != NULL; j++)
985 				grp->gr_mem[j] = grp->gr_mem[j+1];
986 			strlcpy(group, grp->gr_name, MAXLOGNAME);
987 			chggrent(group, grp);
988 		}
989 	}
990 	ENDGRENT();
991 
992 	pw_log(cnf, M_DELETE, W_USER, "%s(%ju) account removed", name,
993 	    (uintmax_t)id);
994 
995 	/* Remove mail file */
996 	if (PWALTDIR() != PWF_ALT)
997 		unlinkat(conf.rootfd, file + 1, 0);
998 
999 	/* Remove at jobs */
1000 	if (!PWALTDIR() && getpwuid(id) == NULL)
1001 		rmat(id);
1002 
1003 	/* Remove home directory and contents */
1004 	if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' &&
1005 	    GETPWUID(id) == NULL &&
1006 	    fstatat(conf.rootfd, home + 1, &st, 0) != -1) {
1007 		rm_r(conf.rootfd, home, id);
1008 		pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s"
1009 		    "removed", name, (uintmax_t)id, home,
1010 		     fstatat(conf.rootfd, home + 1, &st, 0) == -1 ? "" : "not "
1011 		     "completely ");
1012 	}
1013 
1014 	return (EXIT_SUCCESS);
1015 }
1016 
1017 int
pw_user_lock(int argc,char ** argv,char * arg1)1018 pw_user_lock(int argc, char **argv, char *arg1)
1019 {
1020 	int ch;
1021 
1022 	while ((ch = getopt(argc, argv, "Cq")) != -1) {
1023 		switch (ch) {
1024 		case 'C':
1025 		case 'q':
1026 			/* compatibility */
1027 			break;
1028 		default:
1029 			usage();
1030 		}
1031 	}
1032 	argc -= optind;
1033 	argv += optind;
1034 	if (argc > 0)
1035 		usage();
1036 
1037 	return (pw_userlock(arg1, M_LOCK));
1038 }
1039 
1040 int
pw_user_unlock(int argc,char ** argv,char * arg1)1041 pw_user_unlock(int argc, char **argv, char *arg1)
1042 {
1043 	int ch;
1044 
1045 	while ((ch = getopt(argc, argv, "Cq")) != -1) {
1046 		switch (ch) {
1047 		case 'C':
1048 		case 'q':
1049 			/* compatibility */
1050 			break;
1051 		default:
1052 			usage();
1053 		}
1054 	}
1055 	argc -= optind;
1056 	argv += optind;
1057 	if (argc > 0)
1058 		usage();
1059 
1060 	return (pw_userlock(arg1, M_UNLOCK));
1061 }
1062 
1063 static struct group *
group_from_name_or_id(char * name)1064 group_from_name_or_id(char *name)
1065 {
1066 	const char *errstr = NULL;
1067 	struct group *grp;
1068 	uintmax_t id;
1069 
1070 	if ((grp = GETGRNAM(name)) == NULL) {
1071 		id = strtounum(name, 0, GID_MAX, &errstr);
1072 		if (errstr)
1073 			errx(EX_NOUSER, "group `%s' does not exist", name);
1074 		grp = GETGRGID(id);
1075 		if (grp == NULL)
1076 			errx(EX_NOUSER, "group `%s' does not exist", name);
1077 	}
1078 
1079 	return (grp);
1080 }
1081 
1082 static void
split_groups(StringList ** groups,char * groupsstr)1083 split_groups(StringList **groups, char *groupsstr)
1084 {
1085 	struct group *grp;
1086 	char *p;
1087 	char tok[] = ", \t";
1088 
1089 	if (*groups == NULL)
1090 		*groups = sl_init();
1091 	for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) {
1092 		grp = group_from_name_or_id(p);
1093 		sl_add(*groups, newstr(grp->gr_name));
1094 	}
1095 }
1096 
1097 static void
validate_grname(struct userconf * cnf,char * group)1098 validate_grname(struct userconf *cnf, char *group)
1099 {
1100 	struct group *grp;
1101 
1102 	if (group == NULL || *group == '\0') {
1103 		cnf->default_group = "";
1104 		return;
1105 	}
1106 	grp = group_from_name_or_id(group);
1107 	cnf->default_group = newstr(grp->gr_name);
1108 }
1109 
1110 static mode_t
validate_mode(char * mode)1111 validate_mode(char *mode)
1112 {
1113 	mode_t m;
1114 	void *set;
1115 
1116 	if ((set = setmode(mode)) == NULL)
1117 		errx(EX_DATAERR, "invalid directory creation mode '%s'", mode);
1118 
1119 	m = getmode(set, _DEF_DIRMODE);
1120 	free(set);
1121 	return (m);
1122 }
1123 
1124 static long
validate_expire(char * str,int opt)1125 validate_expire(char *str, int opt)
1126 {
1127 	if (!numerics(str))
1128 		errx(EX_DATAERR, "-%c argument must be numeric "
1129 		     "when setting defaults: %s", (char)opt, str);
1130 	return strtol(str, NULL, 0);
1131 }
1132 
1133 static void
mix_config(struct userconf * cmdcnf,struct userconf * cfg)1134 mix_config(struct userconf *cmdcnf, struct userconf *cfg)
1135 {
1136 
1137 	if (cmdcnf->default_password < 0)
1138 		cmdcnf->default_password = cfg->default_password;
1139 	if (cmdcnf->reuse_uids == 0)
1140 		cmdcnf->reuse_uids = cfg->reuse_uids;
1141 	if (cmdcnf->reuse_gids == 0)
1142 		cmdcnf->reuse_gids = cfg->reuse_gids;
1143 	if (cmdcnf->nispasswd == NULL)
1144 		cmdcnf->nispasswd = cfg->nispasswd;
1145 	if (cmdcnf->dotdir == NULL)
1146 		cmdcnf->dotdir = cfg->dotdir;
1147 	if (cmdcnf->newmail == NULL)
1148 		cmdcnf->newmail = cfg->newmail;
1149 	if (cmdcnf->logfile == NULL)
1150 		cmdcnf->logfile = cfg->logfile;
1151 	if (cmdcnf->home == NULL)
1152 		cmdcnf->home = cfg->home;
1153 	if (cmdcnf->homemode == 0)
1154 		cmdcnf->homemode = cfg->homemode;
1155 	if (cmdcnf->shelldir == NULL)
1156 		cmdcnf->shelldir = cfg->shelldir;
1157 	if (cmdcnf->shells == NULL)
1158 		cmdcnf->shells = cfg->shells;
1159 	if (cmdcnf->shell_default == NULL)
1160 		cmdcnf->shell_default = cfg->shell_default;
1161 	if (cmdcnf->default_group == NULL)
1162 		cmdcnf->default_group = cfg->default_group;
1163 	if (cmdcnf->groups == NULL)
1164 		cmdcnf->groups = cfg->groups;
1165 	if (cmdcnf->default_class == NULL)
1166 		cmdcnf->default_class = cfg->default_class;
1167 	if (cmdcnf->min_uid == 0)
1168 		cmdcnf->min_uid = cfg->min_uid;
1169 	if (cmdcnf->max_uid == 0)
1170 		cmdcnf->max_uid = cfg->max_uid;
1171 	if (cmdcnf->min_gid == 0)
1172 		cmdcnf->min_gid = cfg->min_gid;
1173 	if (cmdcnf->max_gid == 0)
1174 		cmdcnf->max_gid = cfg->max_gid;
1175 	if (cmdcnf->expire_days < 0)
1176 		cmdcnf->expire_days = cfg->expire_days;
1177 	if (cmdcnf->password_days < 0)
1178 		cmdcnf->password_days = cfg->password_days;
1179 }
1180 
1181 int
pw_user_add(int argc,char ** argv,char * arg1)1182 pw_user_add(int argc, char **argv, char *arg1)
1183 {
1184 	struct userconf *cnf, *cmdcnf;
1185 	struct passwd *pwd;
1186 	struct group *grp;
1187 	struct stat st;
1188 	char args[] = "C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y";
1189 	char line[_PASSWORD_LEN+1], path[MAXPATHLEN];
1190 	char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname;
1191 	char *default_passwd, *name, *p;
1192 	const char *cfg = NULL;
1193 	login_cap_t *lc;
1194 	FILE *pfp, *fp;
1195 	intmax_t id = -1;
1196 	time_t now;
1197 	int rc, ch, fd = -1;
1198 	size_t i;
1199 	bool dryrun, nis, pretty, quiet, createhome, precrypted, genconf;
1200 
1201 	dryrun = nis = pretty = quiet = createhome = precrypted = false;
1202 	genconf = false;
1203 	gecos = homedir = skel = userid = groupid = default_passwd = NULL;
1204 	grname = name = NULL;
1205 
1206 	if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL)
1207 		err(EXIT_FAILURE, "calloc()");
1208 
1209 	cmdcnf->default_password = cmdcnf->expire_days = cmdcnf->password_days = -1;
1210 	now = time(NULL);
1211 
1212 	if (arg1 != NULL) {
1213 		if (arg1[strspn(arg1, "0123456789")] == '\0')
1214 			id = pw_checkid(arg1, UID_MAX);
1215 		else
1216 			name = pw_checkname(arg1, 0);
1217 	}
1218 
1219 	while ((ch = getopt(argc, argv, args)) != -1) {
1220 		switch (ch) {
1221 		case 'C':
1222 			cfg = optarg;
1223 			break;
1224 		case 'q':
1225 			quiet = true;
1226 			break;
1227 		case 'n':
1228 			name = pw_checkname(optarg, 0);
1229 			break;
1230 		case 'u':
1231 			userid = optarg;
1232 			break;
1233 		case 'c':
1234 			gecos = pw_checkname(optarg, 1);
1235 			break;
1236 		case 'd':
1237 			homedir = optarg;
1238 			break;
1239 		case 'e':
1240 			if (genconf)
1241 			    cmdcnf->expire_days = validate_expire(optarg, ch);
1242 			else
1243 			    cmdcnf->expire_days = parse_date(now, optarg);
1244 			break;
1245 		case 'p':
1246 			if (genconf)
1247 			    cmdcnf->password_days = validate_expire(optarg, ch);
1248 			else
1249 			    cmdcnf->password_days = parse_date(now, optarg);
1250 			break;
1251 		case 'g':
1252 			validate_grname(cmdcnf, optarg);
1253 			grname = optarg;
1254 			break;
1255 		case 'G':
1256 			split_groups(&cmdcnf->groups, optarg);
1257 			break;
1258 		case 'm':
1259 			createhome = true;
1260 			break;
1261 		case 'M':
1262 			cmdcnf->homemode = validate_mode(optarg);
1263 			break;
1264 		case 'k':
1265 			walk = skel = optarg;
1266 			if (*walk == '/')
1267 				walk++;
1268 			if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1269 				errx(EX_OSFILE, "skeleton `%s' does not "
1270 				    "exists", skel);
1271 			if (!S_ISDIR(st.st_mode))
1272 				errx(EX_OSFILE, "skeleton `%s' is not a "
1273 				    "directory", skel);
1274 			cmdcnf->dotdir = skel;
1275 			break;
1276 		case 's':
1277 			cmdcnf->shell_default = optarg;
1278 			break;
1279 		case 'o':
1280 			conf.checkduplicate = false;
1281 			break;
1282 		case 'L':
1283 			cmdcnf->default_class = pw_checkname(optarg, 0);
1284 			break;
1285 		case 'i':
1286 			groupid = optarg;
1287 			break;
1288 		case 'w':
1289 			default_passwd = optarg;
1290 			break;
1291 		case 'H':
1292 			if (fd != -1)
1293 				errx(EX_USAGE, "'-h' and '-H' are mutually "
1294 				    "exclusive options");
1295 			fd = pw_checkfd(optarg);
1296 			precrypted = true;
1297 			if (fd == '-')
1298 				errx(EX_USAGE, "-H expects a file descriptor");
1299 			break;
1300 		case 'h':
1301 			if (fd != -1)
1302 				errx(EX_USAGE, "'-h' and '-H' are mutually "
1303 				    "exclusive options");
1304 			fd = pw_checkfd(optarg);
1305 			break;
1306 		case 'D':
1307 			genconf = true;
1308 			break;
1309 		case 'b':
1310 			cmdcnf->home = optarg;
1311 			break;
1312 		case 'N':
1313 			dryrun = true;
1314 			break;
1315 		case 'P':
1316 			pretty = true;
1317 			break;
1318 		case 'y':
1319 			cmdcnf->nispasswd = optarg;
1320 			break;
1321 		case 'Y':
1322 			nis = true;
1323 			break;
1324 		default:
1325 			usage();
1326 		}
1327 	}
1328 	argc -= optind;
1329 	argv += optind;
1330 	if (argc > 0)
1331 		usage();
1332 
1333 	if (!dryrun)
1334 		pw_check_root();
1335 
1336 	if (quiet)
1337 		freopen(_PATH_DEVNULL, "w", stderr);
1338 
1339 	cnf = get_userconfig(cfg);
1340 
1341 	mix_config(cmdcnf, cnf);
1342 	if (default_passwd)
1343 		cmdcnf->default_password = passwd_val(default_passwd,
1344 		    cnf->default_password);
1345 	if (genconf) {
1346 		if (name != NULL)
1347 			errx(EX_DATAERR, "can't combine `-D' with `-n name'");
1348 		if (userid != NULL) {
1349 			if ((p = strtok(userid, ", \t")) != NULL)
1350 				cmdcnf->min_uid = pw_checkid(p, UID_MAX);
1351 			if (cmdcnf->min_uid == 0)
1352 				cmdcnf->min_uid = 1000;
1353 			if ((p = strtok(NULL, " ,\t")) != NULL)
1354 				cmdcnf->max_uid = pw_checkid(p, UID_MAX);
1355 			if (cmdcnf->max_uid == 0)
1356 				cmdcnf->max_uid = 32000;
1357 		}
1358 		if (groupid != NULL) {
1359 			if ((p = strtok(groupid, ", \t")) != NULL)
1360 				cmdcnf->min_gid = pw_checkid(p, GID_MAX);
1361 			if (cmdcnf->min_gid == 0)
1362 				cmdcnf->min_gid = 1000;
1363 			if ((p = strtok(NULL, " ,\t")) != NULL)
1364 				cmdcnf->max_gid = pw_checkid(p, GID_MAX);
1365 			if (cmdcnf->max_gid == 0)
1366 				cmdcnf->max_gid = 32000;
1367 		}
1368 		if (write_userconfig(cmdcnf, cfg))
1369 			return (EXIT_SUCCESS);
1370 		err(EX_IOERR, "config update");
1371 	}
1372 
1373 	if (userid)
1374 		id = pw_checkid(userid, UID_MAX);
1375 	if (id < 0 && name == NULL)
1376 		errx(EX_DATAERR, "user name or id required");
1377 
1378 	if (name == NULL)
1379 		errx(EX_DATAERR, "login name required");
1380 
1381 	if (GETPWNAM(name) != NULL)
1382 		errx(EX_DATAERR, "login name `%s' already exists", name);
1383 
1384 	if (!grname)
1385 		grname = cmdcnf->default_group;
1386 
1387 	pwd = &fakeuser;
1388 	pwd->pw_name = name;
1389 	pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : "";
1390 	pwd->pw_uid = pw_uidpolicy(cmdcnf, id);
1391 	pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name,
1392 	    (gid_t) pwd->pw_uid, dryrun);
1393 
1394 	/* cmdcnf->password_days and cmdcnf->expire_days hold unixtime here */
1395 	if (cmdcnf->password_days > 0)
1396 		pwd->pw_change = cmdcnf->password_days;
1397 	if (cmdcnf->expire_days > 0)
1398 		pwd->pw_expire = cmdcnf->expire_days;
1399 
1400 	pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name);
1401 	pwd->pw_shell = pw_shellpolicy(cmdcnf);
1402 	lc = login_getpwclass(pwd);
1403 	if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1404 		warn("setting crypt(3) format");
1405 	login_close(lc);
1406 	pwd->pw_passwd = pw_password(cmdcnf, pwd->pw_name);
1407 	if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1408 		warnx("WARNING: new account `%s' has a uid of 0 "
1409 		    "(superuser access!)", pwd->pw_name);
1410 	if (gecos)
1411 		pwd->pw_gecos = gecos;
1412 
1413 	if (fd != -1)
1414 		pw_set_passwd(pwd, fd, precrypted, false);
1415 
1416 	if (dryrun)
1417 		return (print_user(pwd, pretty, false));
1418 
1419 	if ((rc = addpwent(pwd)) != 0) {
1420 		if (rc == -1)
1421 			errx(EX_IOERR, "user '%s' already exists",
1422 			    pwd->pw_name);
1423 		else if (rc != 0)
1424 			err(EX_IOERR, "passwd file update");
1425 	}
1426 	if (nis && cmdcnf->nispasswd && *cmdcnf->nispasswd == '/') {
1427 		printf("%s\n", cmdcnf->nispasswd);
1428 		rc = addnispwent(cmdcnf->nispasswd, pwd);
1429 		if (rc == -1)
1430 			warnx("User '%s' already exists in NIS passwd",
1431 			    pwd->pw_name);
1432 		else if (rc != 0)
1433 			warn("NIS passwd update");
1434 		/* NOTE: we treat NIS-only update errors as non-fatal */
1435 	}
1436 
1437 	if (cmdcnf->groups != NULL) {
1438 		for (i = 0; i < cmdcnf->groups->sl_cur; i++) {
1439 			grp = GETGRNAM(cmdcnf->groups->sl_str[i]);
1440 			/* gr_add doesn't check if new member is already in group */
1441 			if (grp_has_member(grp, pwd->pw_name))
1442 				continue;
1443 			grp = gr_add(grp, pwd->pw_name);
1444 			/*
1445 			 * grp can only be NULL in 2 cases:
1446 			 * - the new member is already a member
1447 			 * - a problem with memory occurs
1448 			 * in both cases we want to skip now.
1449 			 */
1450 			if (grp == NULL)
1451 				continue;
1452 			chggrent(grp->gr_name, grp);
1453 			free(grp);
1454 		}
1455 	}
1456 
1457 	pwd = GETPWNAM(name);
1458 	if (pwd == NULL)
1459 		errx(EX_NOUSER, "user '%s' disappeared during update", name);
1460 
1461 	grp = GETGRGID(pwd->pw_gid);
1462 	pw_log(cnf, M_ADD, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1463 	       pwd->pw_name, (uintmax_t)pwd->pw_uid,
1464 	    grp ? grp->gr_name : "unknown",
1465 	       (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1466 	       pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1467 
1468 	/*
1469 	 * let's touch and chown the user's mail file. This is not
1470 	 * strictly necessary under BSD with a 0755 maildir but it also
1471 	 * doesn't hurt anything to create the empty mailfile
1472 	 */
1473 	if (PWALTDIR() != PWF_ALT) {
1474 		snprintf(path, sizeof(path), "%s/%s", _PATH_MAILDIR,
1475 		    pwd->pw_name);
1476 		/* Preserve contents & mtime */
1477 		close(openat(conf.rootfd, path +1, O_RDWR | O_CREAT, 0600));
1478 		fchownat(conf.rootfd, path + 1, pwd->pw_uid, pwd->pw_gid,
1479 		    AT_SYMLINK_NOFOLLOW);
1480 	}
1481 
1482 	/*
1483 	 * Let's create and populate the user's home directory. Note
1484 	 * that this also `works' for editing users if -m is used, but
1485 	 * existing files will *not* be overwritten.
1486 	 */
1487 	if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1488 	    *pwd->pw_dir == '/' && pwd->pw_dir[1])
1489 		create_and_populate_homedir(cmdcnf, pwd, cmdcnf->dotdir,
1490 		    cmdcnf->homemode, false);
1491 
1492 	if (!PWALTDIR() && cmdcnf->newmail && *cmdcnf->newmail &&
1493 	    (fp = fopen(cnf->newmail, "r")) != NULL) {
1494 		if ((pfp = popen(_PATH_SENDMAIL " -t", "w")) == NULL)
1495 			warn("sendmail");
1496 		else {
1497 			fprintf(pfp, "From: root\n" "To: %s\n"
1498 			    "Subject: Welcome!\n\n", pwd->pw_name);
1499 			while (fgets(line, sizeof(line), fp) != NULL) {
1500 				/* Do substitutions? */
1501 				fputs(line, pfp);
1502 			}
1503 			pclose(pfp);
1504 			pw_log(cnf, M_ADD, W_USER, "%s(%ju) new user mail sent",
1505 			    pwd->pw_name, (uintmax_t)pwd->pw_uid);
1506 		}
1507 		fclose(fp);
1508 	}
1509 
1510 	if (nis && nis_update() == 0)
1511 		pw_log(cnf, M_ADD, W_USER, "NIS maps updated");
1512 
1513 	return (EXIT_SUCCESS);
1514 }
1515 
1516 int
pw_user_mod(int argc,char ** argv,char * arg1)1517 pw_user_mod(int argc, char **argv, char *arg1)
1518 {
1519 	struct userconf *cnf;
1520 	struct passwd *pwd;
1521 	struct group *grp;
1522 	StringList *groups = NULL;
1523 	char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:";
1524 	const char *cfg = NULL;
1525 	char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell;
1526 	char *passwd, *class, *nispasswd;
1527 	login_cap_t *lc;
1528 	struct stat st;
1529 	intmax_t id = -1;
1530 	int ch, fd = -1;
1531 	size_t i, j;
1532 	bool quiet, createhome, pretty, dryrun, nis, edited;
1533 	bool precrypted;
1534 	mode_t homemode = 0;
1535 	time_t expire_time, password_time, now;
1536 
1537 	expire_time = password_time = -1;
1538 	gecos = homedir = grname = name = newname = skel = shell =NULL;
1539 	passwd = NULL;
1540 	class = nispasswd = NULL;
1541 	quiet = createhome = pretty = dryrun = nis = precrypted = false;
1542 	edited = false;
1543 	now = time(NULL);
1544 
1545 	if (arg1 != NULL) {
1546 		if (arg1[strspn(arg1, "0123456789")] == '\0')
1547 			id = pw_checkid(arg1, UID_MAX);
1548 		else
1549 			name = arg1;
1550 	}
1551 
1552 	while ((ch = getopt(argc, argv, args)) != -1) {
1553 		switch (ch) {
1554 		case 'C':
1555 			cfg = optarg;
1556 			break;
1557 		case 'q':
1558 			quiet = true;
1559 			break;
1560 		case 'n':
1561 			name = optarg;
1562 			break;
1563 		case 'u':
1564 			id = pw_checkid(optarg, UID_MAX);
1565 			break;
1566 		case 'c':
1567 			gecos = pw_checkname(optarg, 1);
1568 			break;
1569 		case 'd':
1570 			homedir = optarg;
1571 			break;
1572 		case 'e':
1573 			expire_time = parse_date(now, optarg);
1574 			break;
1575 		case 'p':
1576 			password_time = parse_date(now, optarg);
1577 			break;
1578 		case 'g':
1579 			group_from_name_or_id(optarg);
1580 			grname = optarg;
1581 			break;
1582 		case 'G':
1583 			split_groups(&groups, optarg);
1584 			break;
1585 		case 'm':
1586 			createhome = true;
1587 			break;
1588 		case 'M':
1589 			homemode = validate_mode(optarg);
1590 			break;
1591 		case 'l':
1592 			newname = optarg;
1593 			break;
1594 		case 'k':
1595 			walk = skel = optarg;
1596 			if (*walk == '/')
1597 				walk++;
1598 			if (fstatat(conf.rootfd, walk, &st, 0) == -1)
1599 				errx(EX_OSFILE, "skeleton `%s' does not "
1600 				    "exists", skel);
1601 			if (!S_ISDIR(st.st_mode))
1602 				errx(EX_OSFILE, "skeleton `%s' is not a "
1603 				    "directory", skel);
1604 			break;
1605 		case 's':
1606 			shell = optarg;
1607 			break;
1608 		case 'w':
1609 			passwd = optarg;
1610 			break;
1611 		case 'L':
1612 			class = pw_checkname(optarg, 0);
1613 			break;
1614 		case 'H':
1615 			if (fd != -1)
1616 				errx(EX_USAGE, "'-h' and '-H' are mutually "
1617 				    "exclusive options");
1618 			fd = pw_checkfd(optarg);
1619 			precrypted = true;
1620 			if (fd == '-')
1621 				errx(EX_USAGE, "-H expects a file descriptor");
1622 			break;
1623 		case 'h':
1624 			if (fd != -1)
1625 				errx(EX_USAGE, "'-h' and '-H' are mutually "
1626 				    "exclusive options");
1627 			fd = pw_checkfd(optarg);
1628 			break;
1629 		case 'N':
1630 			dryrun = true;
1631 			break;
1632 		case 'P':
1633 			pretty = true;
1634 			break;
1635 		case 'y':
1636 			nispasswd = optarg;
1637 			break;
1638 		case 'Y':
1639 			nis = true;
1640 			break;
1641 		default:
1642 			usage();
1643 		}
1644 	}
1645 	argc -= optind;
1646 	argv += optind;
1647 	if (argc > 0)
1648 		usage();
1649 
1650 	if (!dryrun)
1651 		pw_check_root();
1652 
1653 	if (quiet)
1654 		freopen(_PATH_DEVNULL, "w", stderr);
1655 
1656 	cnf = get_userconfig(cfg);
1657 
1658 	if (id < 0 && name == NULL)
1659 		errx(EX_DATAERR, "username or id required");
1660 
1661 	pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id);
1662 	if (pwd == NULL) {
1663 		if (name == NULL)
1664 			errx(EX_NOUSER, "no such uid `%ju'",
1665 			    (uintmax_t) id);
1666 		errx(EX_NOUSER, "no such user `%s'", name);
1667 	}
1668 
1669 	if (name == NULL)
1670 		name = pwd->pw_name;
1671 
1672 	if (nis && nispasswd == NULL)
1673 		nispasswd = cnf->nispasswd;
1674 
1675 	if (PWF._altdir == PWF_REGULAR &&
1676 	    ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) {
1677 		if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) {
1678 			if (!nis && nispasswd && *nispasswd != '/')
1679 				errx(EX_NOUSER, "Cannot modify NIS user `%s'",
1680 				    name);
1681 		} else {
1682 			errx(EX_NOUSER, "Cannot modify non local user `%s'",
1683 			    name);
1684 		}
1685 	}
1686 
1687 	if (newname) {
1688 		if (strcmp(pwd->pw_name, "root") == 0)
1689 			errx(EX_DATAERR, "can't rename `root' account");
1690 		if (strcmp(pwd->pw_name, newname) != 0) {
1691 			pwd->pw_name = pw_checkname(newname, 0);
1692 			edited = true;
1693 		}
1694 	}
1695 
1696 	if (id >= 0 && pwd->pw_uid != id) {
1697 		pwd->pw_uid = id;
1698 		edited = true;
1699 		if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
1700 			errx(EX_DATAERR, "can't change uid of `root' account");
1701 		if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
1702 			warnx("WARNING: account `%s' will have a uid of 0 "
1703 			    "(superuser access!)", pwd->pw_name);
1704 	}
1705 
1706 	if (grname && pwd->pw_uid != 0) {
1707 		grp = GETGRNAM(grname);
1708 		if (grp == NULL)
1709 			grp = GETGRGID(pw_checkid(grname, GID_MAX));
1710 		if (grp->gr_gid != pwd->pw_gid) {
1711 			pwd->pw_gid = grp->gr_gid;
1712 			edited = true;
1713 		}
1714 	}
1715 
1716 
1717 	if (password_time >= 0 && pwd->pw_change != password_time) {
1718 		pwd->pw_change = password_time;
1719 		edited = true;
1720 	}
1721 
1722 	if (expire_time >= 0 && pwd->pw_expire != expire_time) {
1723 		pwd->pw_expire = expire_time;
1724 		edited = true;
1725 	}
1726 
1727 	if (shell) {
1728 		shell = shell_path(cnf->shelldir, cnf->shells, shell);
1729 		if (shell == NULL)
1730 			shell = "";
1731 		if (strcmp(shell, pwd->pw_shell) != 0) {
1732 			pwd->pw_shell = shell;
1733 			edited = true;
1734 		}
1735 	}
1736 
1737 	if (class && strcmp(pwd->pw_class, class) != 0) {
1738 		pwd->pw_class = class;
1739 		edited = true;
1740 	}
1741 
1742 	if (homedir && strcmp(pwd->pw_dir, homedir) != 0) {
1743 		pwd->pw_dir = homedir;
1744 		edited = true;
1745 		if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) {
1746 			if (!createhome)
1747 				warnx("WARNING: home `%s' does not exist",
1748 				    pwd->pw_dir);
1749 		} else if (!S_ISDIR(st.st_mode)) {
1750 			warnx("WARNING: home `%s' is not a directory",
1751 			    pwd->pw_dir);
1752 		}
1753 	}
1754 
1755 	if (passwd && conf.fd == -1) {
1756 		lc = login_getpwclass(pwd);
1757 		if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1758 			warn("setting crypt(3) format");
1759 		login_close(lc);
1760 		cnf->default_password = passwd_val(passwd,
1761 		    cnf->default_password);
1762 		pwd->pw_passwd = pw_password(cnf, pwd->pw_name);
1763 		edited = true;
1764 	}
1765 
1766 	if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) {
1767 		pwd->pw_gecos = gecos;
1768 		edited = true;
1769 	}
1770 
1771 	if (fd != -1)
1772 		edited = pw_set_passwd(pwd, fd, precrypted, true);
1773 
1774 	if (dryrun)
1775 		return (print_user(pwd, pretty, false));
1776 
1777 	if (edited) /* Only updated this if required */
1778 		perform_chgpwent(name, pwd, nis ? nispasswd : NULL);
1779 	/* Now perform the needed changes concern groups */
1780 	if (groups != NULL) {
1781 		/* Delete User from groups using old name */
1782 		SETGRENT();
1783 		while ((grp = GETGRENT()) != NULL) {
1784 			if (grp->gr_mem == NULL)
1785 				continue;
1786 			for (i = 0; grp->gr_mem[i] != NULL; i++) {
1787 				if (strcmp(grp->gr_mem[i] , name) != 0)
1788 					continue;
1789 				for (j = i; grp->gr_mem[j] != NULL ; j++)
1790 					grp->gr_mem[j] = grp->gr_mem[j+1];
1791 				chggrent(grp->gr_name, grp);
1792 				break;
1793 			}
1794 		}
1795 		ENDGRENT();
1796 		/* Add the user to the needed groups */
1797 		for (i = 0; i < groups->sl_cur; i++) {
1798 			grp = GETGRNAM(groups->sl_str[i]);
1799 			grp = gr_add(grp, pwd->pw_name);
1800 			if (grp == NULL)
1801 				continue;
1802 			chggrent(grp->gr_name, grp);
1803 			free(grp);
1804 		}
1805 	}
1806 	/* In case of rename we need to walk over the different groups */
1807 	if (newname) {
1808 		SETGRENT();
1809 		while ((grp = GETGRENT()) != NULL) {
1810 			if (grp->gr_mem == NULL)
1811 				continue;
1812 			for (i = 0; grp->gr_mem[i] != NULL; i++) {
1813 				if (strcmp(grp->gr_mem[i], name) != 0)
1814 					continue;
1815 				grp->gr_mem[i] = newname;
1816 				chggrent(grp->gr_name, grp);
1817 				break;
1818 			}
1819 		}
1820 	}
1821 
1822 	/* go get a current version of pwd */
1823 	if (newname)
1824 		name = newname;
1825 	pwd = GETPWNAM(name);
1826 	if (pwd == NULL)
1827 		errx(EX_NOUSER, "user '%s' disappeared during update", name);
1828 	grp = GETGRGID(pwd->pw_gid);
1829 	pw_log(cnf, M_MODIFY, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1830 	    pwd->pw_name, (uintmax_t)pwd->pw_uid,
1831 	    grp ? grp->gr_name : "unknown",
1832 	    (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1833 	    pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1834 
1835 	/*
1836 	 * Let's create and populate the user's home directory. Note
1837 	 * that this also `works' for editing users if -m is used, but
1838 	 * existing files will *not* be overwritten.
1839 	 */
1840 	if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir &&
1841 	    *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
1842 		if (!skel)
1843 			skel = cnf->dotdir;
1844 		if (homemode == 0)
1845 			homemode = cnf->homemode;
1846 		create_and_populate_homedir(cnf, pwd, skel, homemode, true);
1847 	}
1848 
1849 	if (nis && nis_update() == 0)
1850 		pw_log(cnf, M_MODIFY, W_USER, "NIS maps updated");
1851 
1852 	return (EXIT_SUCCESS);
1853 }
1854