pw_user.c revision 286986
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
29static const char rcsid[] =
30  "$FreeBSD: head/usr.sbin/pw/pw_user.c 286986 2015-08-21 09:28:20Z bapt $";
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
59static		char locked_str[] = "*LOCKED*";
60
61static 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
75static int	 print_user(struct passwd *pwd, bool pretty, bool v7);
76static uid_t	 pw_uidpolicy(struct userconf *cnf, intmax_t id);
77static uid_t	 pw_gidpolicy(struct userconf *cnf, char *grname, char *nam,
78    gid_t prefer, bool dryrun);
79static char	*pw_homepolicy(struct userconf * cnf, char *homedir,
80    const char *user);
81static char	*pw_shellpolicy(struct userconf * cnf);
82static char	*pw_password(struct userconf * cnf, char const * user,
83    bool dryrun);
84static char	*shell_path(char const * path, char *shells[], char *sh);
85static void	rmat(uid_t uid);
86static void	rmopie(char const * name);
87
88static void
89mkdir_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
150static void
151create_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
171static int
172pw_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
233static void
234perform_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 */
268static int
269pw_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
320static uid_t
321pw_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
374static uid_t
375pw_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
421static char *
422pw_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
436static char *
437shell_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
471static char *
472pw_shellpolicy(struct userconf * cnf)
473{
474
475	return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default);
476}
477
478#define	SALTSIZE	32
479
480static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
481
482char *
483pw_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
503static char *
504pw_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
541static int
542print_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
616char *
617pw_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
675static void
676rmat(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
701static void
702rmopie(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
731int
732pw_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
764int
765pw_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
842int
843pw_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
1021int
1022pw_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
1038int
1039pw_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
1055static struct group *
1056group_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
1074static void
1075split_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
1089static void
1090validate_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
1102static mode_t
1103validate_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
1116static void
1117mix_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
1164int
1165pw_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
1475int
1476pw_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		if (fstatat(conf.rootfd, pwd->pw_dir, &st, 0) == -1) {
1698			if (!createhome)
1699				warnx("WARNING: home `%s' does not exist",
1700				    pwd->pw_dir);
1701			else
1702				docreatehome = true;
1703		} else if (!S_ISDIR(st.st_mode)) {
1704			warnx("WARNING: home `%s' is not a directory",
1705			    pwd->pw_dir);
1706		}
1707	}
1708
1709	if (passwd && conf.fd == -1) {
1710		lc = login_getpwclass(pwd);
1711		if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL)
1712			warn("setting crypt(3) format");
1713		login_close(lc);
1714		cnf->default_password = boolean_val(passwd,
1715		    cnf->default_password);
1716		pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun);
1717		edited = true;
1718	}
1719
1720	if (gecos && strcmp(pwd->pw_gecos, gecos) != 0) {
1721		pwd->pw_gecos = gecos;
1722		edited = true;
1723	}
1724
1725	if (fd != -1)
1726		edited = pw_set_passwd(pwd, fd, precrypted, true);
1727
1728	if (dryrun)
1729		return (print_user(pwd, pretty, false));
1730
1731	if (edited) /* Only updated this if required */
1732		perform_chgpwent(name, pwd, nis ? nispasswd : NULL);
1733	/* Now perform the needed changes concern groups */
1734	if (groups != NULL) {
1735		/* Delete User from groups using old name */
1736		SETGRENT();
1737		while ((grp = GETGRENT()) != NULL) {
1738			if (grp->gr_mem == NULL)
1739				continue;
1740			for (i = 0; grp->gr_mem[i] != NULL; i++) {
1741				if (strcmp(grp->gr_mem[i] , name) != 0)
1742					continue;
1743				for (j = i; grp->gr_mem[j] != NULL ; j++)
1744					grp->gr_mem[j] = grp->gr_mem[j+1];
1745				chggrent(grp->gr_name, grp);
1746				break;
1747			}
1748		}
1749		ENDGRENT();
1750		/* Add the user to the needed groups */
1751		for (i = 0; i < groups->sl_cur; i++) {
1752			grp = GETGRNAM(groups->sl_str[i]);
1753			grp = gr_add(grp, pwd->pw_name);
1754			if (grp == NULL)
1755				continue;
1756			chggrent(grp->gr_name, grp);
1757			free(grp);
1758		}
1759	}
1760	/* In case of rename we need to walk over the different groups */
1761	if (newname) {
1762		SETGRENT();
1763		while ((grp = GETGRENT()) != NULL) {
1764			if (grp->gr_mem == NULL)
1765				continue;
1766			for (i = 0; grp->gr_mem[i] != NULL; i++) {
1767				if (strcmp(grp->gr_mem[i], name) != 0)
1768					continue;
1769				grp->gr_mem[i] = newname;
1770				chggrent(grp->gr_name, grp);
1771				break;
1772			}
1773		}
1774	}
1775
1776	/* go get a current version of pwd */
1777	if (newname)
1778		name = newname;
1779	pwd = GETPWNAM(name);
1780	if (pwd == NULL)
1781		errx(EX_NOUSER, "user '%s' disappeared during update", name);
1782	grp = GETGRGID(pwd->pw_gid);
1783	pw_log(cnf, M_UPDATE, W_USER, "%s(%ju):%s(%ju):%s:%s:%s",
1784	    pwd->pw_name, (uintmax_t)pwd->pw_uid,
1785	    grp ? grp->gr_name : "unknown",
1786	    (uintmax_t)(grp ? grp->gr_gid : (uid_t)-1),
1787	    pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
1788
1789	/*
1790	 * Let's create and populate the user's home directory. Note
1791	 * that this also `works' for editing users if -m is used, but
1792	 * existing files will *not* be overwritten.
1793	 */
1794	if (PWALTDIR() != PWF_ALT && docreatehome && pwd->pw_dir &&
1795	    *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
1796		if (!skel)
1797			skel = cnf->dotdir;
1798		if (homemode == 0)
1799			homemode = cnf->homemode;
1800		create_and_populate_homedir(cnf, pwd, skel, homemode, true);
1801	}
1802
1803	if (nis && nis_update() == 0)
1804		pw_log(cnf, M_UPDATE, W_USER, "NIS maps updated");
1805
1806	return (EXIT_SUCCESS);
1807}
1808