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