xref: /freebsd/usr.sbin/pw/pw_user.c (revision 830940567b49bb0c08dfaed40418999e76616909)
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 <ctype.h>
34 #include <err.h>
35 #include <fcntl.h>
36 #include <sys/param.h>
37 #include <dirent.h>
38 #include <paths.h>
39 #include <termios.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <sys/resource.h>
43 #include <unistd.h>
44 #include <utmp.h>
45 #include <login_cap.h>
46 #include "pw.h"
47 #include "bitmap.h"
48 
49 #if (MAXLOGNAME-1) > UT_NAMESIZE
50 #define LOGNAMESIZE UT_NAMESIZE
51 #else
52 #define LOGNAMESIZE (MAXLOGNAME-1)
53 #endif
54 
55 static		char locked_str[] = "*LOCKED*";
56 
57 static int      print_user(struct passwd * pwd, int pretty, int v7);
58 static uid_t    pw_uidpolicy(struct userconf * cnf, struct cargs * args);
59 static uid_t    pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
60 static time_t   pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
61 static time_t   pw_exppolicy(struct userconf * cnf, struct cargs * args);
62 static char    *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
63 static char    *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
64 static char    *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
65 static char    *shell_path(char const * path, char *shells[], char *sh);
66 static void     rmat(uid_t uid);
67 static void     rmopie(char const * name);
68 
69 /*-
70  * -C config      configuration file
71  * -q             quiet operation
72  * -n name        login name
73  * -u uid         user id
74  * -c comment     user name/comment
75  * -d directory   home directory
76  * -e date        account expiry date
77  * -p date        password expiry date
78  * -g grp         primary group
79  * -G grp1,grp2   additional groups
80  * -m [ -k dir ]  create and set up home
81  * -s shell       name of login shell
82  * -o             duplicate uid ok
83  * -L class       user class
84  * -l name        new login name
85  * -h fd          password filehandle
86  * -H fd          encrypted password filehandle
87  * -F             force print or add
88  *   Setting defaults:
89  * -D             set user defaults
90  * -b dir         default home root dir
91  * -e period      default expiry period
92  * -p period      default password change period
93  * -g group       default group
94  * -G             grp1,grp2.. default additional groups
95  * -L class       default login class
96  * -k dir         default home skeleton
97  * -s shell       default shell
98  * -w method      default password method
99  */
100 
101 int
102 pw_user(struct userconf * cnf, int mode, struct cargs * args)
103 {
104 	int	        rc, edited = 0;
105 	char           *p = NULL;
106 	char					 *passtmp;
107 	struct carg    *a_name;
108 	struct carg    *a_uid;
109 	struct carg    *arg;
110 	struct passwd  *pwd = NULL;
111 	struct group   *grp;
112 	struct stat     st;
113 	char            line[_PASSWORD_LEN+1];
114 	FILE	       *fp;
115 	mode_t dmode;
116 	char *dmode_c;
117 	void *set = NULL;
118 
119 	static struct passwd fakeuser =
120 	{
121 		NULL,
122 		"*",
123 		-1,
124 		-1,
125 		0,
126 		"",
127 		"User &",
128 		"/nonexistent",
129 		"/bin/sh",
130 		0
131 #if defined(__FreeBSD__)
132 		,0
133 #endif
134 	};
135 
136 
137 	/*
138 	 * With M_NEXT, we only need to return the
139 	 * next uid to stdout
140 	 */
141 	if (mode == M_NEXT)
142 	{
143 		uid_t next = pw_uidpolicy(cnf, args);
144 		if (getarg(args, 'q'))
145 			return next;
146 		printf("%ld:", (long)next);
147 		pw_group(cnf, mode, args);
148 		return EXIT_SUCCESS;
149 	}
150 
151 	/*
152 	 * We can do all of the common legwork here
153 	 */
154 
155 	if ((arg = getarg(args, 'b')) != NULL) {
156 		cnf->home = arg->val;
157 	}
158 
159 	if ((arg = getarg(args, 'M')) != NULL) {
160 		dmode_c = arg->val;
161 		if ((set = setmode(dmode_c)) == NULL)
162 			errx(EX_DATAERR, "invalid directory creation mode '%s'",
163 			    dmode_c);
164 		dmode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
165 		free(set);
166 		cnf->homemode = dmode;
167 	}
168 
169 	/*
170 	 * If we'll need to use it or we're updating it,
171 	 * then create the base home directory if necessary
172 	 */
173 	if (arg != NULL || getarg(args, 'm') != NULL) {
174 		int	l = strlen(cnf->home);
175 
176 		if (l > 1 && cnf->home[l-1] == '/')	/* Shave off any trailing path delimiter */
177 			cnf->home[--l] = '\0';
178 
179 		if (l < 2 || *cnf->home != '/')		/* Check for absolute path name */
180 			errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
181 
182 		if (stat(cnf->home, &st) == -1) {
183 			char	dbuf[MAXPATHLEN];
184 
185 			/*
186 			 * This is a kludge especially for Joerg :)
187 			 * If the home directory would be created in the root partition, then
188 			 * we really create it under /usr which is likely to have more space.
189 			 * But we create a symlink from cnf->home -> "/usr" -> cnf->home
190 			 */
191 			if (strchr(cnf->home+1, '/') == NULL) {
192 				strcpy(dbuf, "/usr");
193 				strncat(dbuf, cnf->home, MAXPATHLEN-5);
194 				if (mkdir(dbuf, cnf->homemode) != -1 || errno == EEXIST) {
195 					chown(dbuf, 0, 0);
196 					/*
197 					 * Skip first "/" and create symlink:
198 					 * /home -> usr/home
199 					 */
200 					symlink(dbuf+1, cnf->home);
201 				}
202 				/* If this falls, fall back to old method */
203 			}
204 			strlcpy(dbuf, cnf->home, sizeof(dbuf));
205 			p = dbuf;
206 			if (stat(dbuf, &st) == -1) {
207 				while ((p = strchr(++p, '/')) != NULL) {
208 					*p = '\0';
209 					if (stat(dbuf, &st) == -1) {
210 						if (mkdir(dbuf, cnf->homemode) == -1)
211 							goto direrr;
212 						chown(dbuf, 0, 0);
213 					} else if (!S_ISDIR(st.st_mode))
214 						errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
215 					*p = '/';
216 				}
217 			}
218 			if (stat(dbuf, &st) == -1) {
219 				if (mkdir(dbuf, cnf->homemode) == -1) {
220 				direrr:	err(EX_OSFILE, "mkdir '%s'", dbuf);
221 				}
222 				chown(dbuf, 0, 0);
223 			}
224 		} else if (!S_ISDIR(st.st_mode))
225 			errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
226 	}
227 
228 	if ((arg = getarg(args, 'e')) != NULL)
229 		cnf->expire_days = atoi(arg->val);
230 
231 	if ((arg = getarg(args, 'y')) != NULL)
232 		cnf->nispasswd = arg->val;
233 
234 	if ((arg = getarg(args, 'p')) != NULL && arg->val)
235 		cnf->password_days = atoi(arg->val);
236 
237 	if ((arg = getarg(args, 'g')) != NULL) {
238 		if (!*(p = arg->val))	/* Handle empty group list specially */
239 			cnf->default_group = "";
240 		else {
241 			if ((grp = GETGRNAM(p)) == NULL) {
242 				if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
243 					errx(EX_NOUSER, "group `%s' does not exist", p);
244 			}
245 			cnf->default_group = newstr(grp->gr_name);
246 		}
247 	}
248 	if ((arg = getarg(args, 'L')) != NULL)
249 		cnf->default_class = pw_checkname((u_char *)arg->val, 0);
250 
251 	if ((arg = getarg(args, 'G')) != NULL && arg->val) {
252 		int i = 0;
253 
254 		for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
255 			if ((grp = GETGRNAM(p)) == NULL) {
256 				if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
257 					errx(EX_NOUSER, "group `%s' does not exist", p);
258 			}
259 			if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
260 				cnf->groups[i++] = newstr(grp->gr_name);
261 		}
262 		while (i < cnf->numgroups)
263 			cnf->groups[i++] = NULL;
264 	}
265 
266 	if ((arg = getarg(args, 'k')) != NULL) {
267 		if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
268 			errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
269 	}
270 
271 	if ((arg = getarg(args, 's')) != NULL)
272 		cnf->shell_default = arg->val;
273 
274 	if ((arg = getarg(args, 'w')) != NULL)
275 		cnf->default_password = boolean_val(arg->val, cnf->default_password);
276 	if (mode == M_ADD && getarg(args, 'D')) {
277 		if (getarg(args, 'n') != NULL)
278 			errx(EX_DATAERR, "can't combine `-D' with `-n name'");
279 		if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
280 			if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
281 				cnf->min_uid = 1000;
282 			if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
283 				cnf->max_uid = 32000;
284 		}
285 		if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
286 			if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
287 				cnf->min_gid = 1000;
288 			if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
289 				cnf->max_gid = 32000;
290 		}
291 
292 		arg = getarg(args, 'C');
293 		if (write_userconfig(arg ? arg->val : NULL))
294 			return EXIT_SUCCESS;
295 		warn("config update");
296 		return EX_IOERR;
297 	}
298 
299 	if (mode == M_PRINT && getarg(args, 'a')) {
300 		int             pretty = getarg(args, 'P') != NULL;
301 		int		v7 = getarg(args, '7') != NULL;
302 
303 		SETPWENT();
304 		while ((pwd = GETPWENT()) != NULL)
305 			print_user(pwd, pretty, v7);
306 		ENDPWENT();
307 		return EXIT_SUCCESS;
308 	}
309 
310 	if ((a_name = getarg(args, 'n')) != NULL)
311 		pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0));
312 	a_uid = getarg(args, 'u');
313 
314 	if (a_uid == NULL) {
315 		if (a_name == NULL)
316 			errx(EX_DATAERR, "user name or id required");
317 
318 		/*
319 		 * Determine whether 'n' switch is name or uid - we don't
320 		 * really don't really care which we have, but we need to
321 		 * know.
322 		 */
323 		if (mode != M_ADD && pwd == NULL
324 		    && strspn(a_name->val, "0123456789") == strlen(a_name->val)
325 		    && atoi(a_name->val) > 0) {	/* Assume uid */
326 			(a_uid = a_name)->ch = 'u';
327 			a_name = NULL;
328 		}
329 	}
330 
331 	/*
332 	 * Update, delete & print require that the user exists
333 	 */
334 	if (mode == M_UPDATE || mode == M_DELETE ||
335 	    mode == M_PRINT  || mode == M_LOCK   || mode == M_UNLOCK) {
336 
337 		if (a_name == NULL && pwd == NULL)	/* Try harder */
338 			pwd = GETPWUID(atoi(a_uid->val));
339 
340 		if (pwd == NULL) {
341 			if (mode == M_PRINT && getarg(args, 'F')) {
342 				fakeuser.pw_name = a_name ? a_name->val : "nouser";
343 				fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1;
344 				return print_user(&fakeuser,
345 						  getarg(args, 'P') != NULL,
346 						  getarg(args, '7') != NULL);
347 			}
348 			if (a_name == NULL)
349 				errx(EX_NOUSER, "no such uid `%s'", a_uid->val);
350 			errx(EX_NOUSER, "no such user `%s'", a_name->val);
351 		}
352 
353 		if (a_name == NULL)	/* May be needed later */
354 			a_name = addarg(args, 'n', newstr(pwd->pw_name));
355 
356 		/*
357 		 * The M_LOCK and M_UNLOCK functions simply add or remove
358 		 * a "*LOCKED*" prefix from in front of the password to
359 		 * prevent it decoding correctly, and therefore prevents
360 		 * access. Of course, this only prevents access via
361 		 * password authentication (not ssh, kerberos or any
362 		 * other method that does not use the UNIX password) but
363 		 * that is a known limitation.
364 		 */
365 
366 		if (mode == M_LOCK) {
367 			if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0)
368 				errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
369 			passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str));
370 			if (passtmp == NULL)	/* disaster */
371 				errx(EX_UNAVAILABLE, "out of memory");
372 			strcpy(passtmp, locked_str);
373 			strcat(passtmp, pwd->pw_passwd);
374 			pwd->pw_passwd = passtmp;
375 			edited = 1;
376 		} else if (mode == M_UNLOCK) {
377 			if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0)
378 				errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
379 			pwd->pw_passwd += sizeof(locked_str)-1;
380 			edited = 1;
381 		} else if (mode == M_DELETE) {
382 			/*
383 			 * Handle deletions now
384 			 */
385 			char            file[MAXPATHLEN];
386 			char            home[MAXPATHLEN];
387 			uid_t           uid = pwd->pw_uid;
388 
389 			if (strcmp(pwd->pw_name, "root") == 0)
390 				errx(EX_DATAERR, "cannot remove user 'root'");
391 
392 			if (!PWALTDIR()) {
393 				/*
394 				 * Remove opie record from /etc/opiekeys
395 		        	 */
396 
397 				rmopie(pwd->pw_name);
398 
399 				/*
400 				 * Remove crontabs
401 				 */
402 				sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
403 				if (access(file, F_OK) == 0) {
404 					sprintf(file, "crontab -u %s -r", pwd->pw_name);
405 					system(file);
406 				}
407 			}
408 			/*
409 			 * Save these for later, since contents of pwd may be
410 			 * invalidated by deletion
411 			 */
412 			sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
413 			strlcpy(home, pwd->pw_dir, sizeof(home));
414 
415 			rc = delpwent(pwd);
416 			if (rc == -1)
417 				err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
418 			else if (rc != 0) {
419 				warn("passwd update");
420 				return EX_IOERR;
421 			}
422 
423 			if (cnf->nispasswd && *cnf->nispasswd=='/') {
424 				rc = delnispwent(cnf->nispasswd, a_name->val);
425 				if (rc == -1)
426 					warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name);
427 				else if (rc != 0)
428 					warn("WARNING: NIS passwd update");
429 				/* non-fatal */
430 			}
431 
432 			editgroups(a_name->val, NULL);
433 
434 			pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
435 
436 			if (!PWALTDIR()) {
437 				/*
438 				 * Remove mail file
439 				 */
440 				remove(file);
441 
442 				/*
443 				 * Remove at jobs
444 				 */
445 				if (getpwuid(uid) == NULL)
446 					rmat(uid);
447 
448 				/*
449 				 * Remove home directory and contents
450 				 */
451 				if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) {
452 					if (stat(home, &st) != -1) {
453 						rm_r(home, uid);
454 						pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved",
455 						       a_name->val, (long) uid, home,
456 						       stat(home, &st) == -1 ? "" : "not completely ");
457 					}
458 				}
459 			}
460 			return EXIT_SUCCESS;
461 		} else if (mode == M_PRINT)
462 			return print_user(pwd,
463 					  getarg(args, 'P') != NULL,
464 					  getarg(args, '7') != NULL);
465 
466 		/*
467 		 * The rest is edit code
468 		 */
469 		if ((arg = getarg(args, 'l')) != NULL) {
470 			if (strcmp(pwd->pw_name, "root") == 0)
471 				errx(EX_DATAERR, "can't rename `root' account");
472 			pwd->pw_name = pw_checkname((u_char *)arg->val, 0);
473 			edited = 1;
474 		}
475 
476 		if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) {
477 			pwd->pw_uid = (uid_t) atol(arg->val);
478 			edited = 1;
479 			if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
480 				errx(EX_DATAERR, "can't change uid of `root' account");
481 			if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
482 				warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
483 		}
484 
485 		if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) {	/* Already checked this */
486 			gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
487 			if (newgid != pwd->pw_gid) {
488 				edited = 1;
489 				pwd->pw_gid = newgid;
490 			}
491 		}
492 
493 		if ((arg = getarg(args, 'p')) != NULL) {
494 			if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
495 				if (pwd->pw_change != 0) {
496 					pwd->pw_change = 0;
497 					edited = 1;
498 				}
499 			}
500 			else {
501 				time_t          now = time(NULL);
502 				time_t          expire = parse_date(now, arg->val);
503 
504 				if (now == expire)
505 					errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
506 				if (pwd->pw_change != expire) {
507 					pwd->pw_change = expire;
508 					edited = 1;
509 				}
510 			}
511 		}
512 
513 		if ((arg = getarg(args, 'e')) != NULL) {
514 			if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
515 				if (pwd->pw_expire != 0) {
516 					pwd->pw_expire = 0;
517 					edited = 1;
518 				}
519 			}
520 			else {
521 				time_t          now = time(NULL);
522 				time_t          expire = parse_date(now, arg->val);
523 
524 				if (now == expire)
525 					errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
526 				if (pwd->pw_expire != expire) {
527 					pwd->pw_expire = expire;
528 					edited = 1;
529 				}
530 			}
531 		}
532 
533 		if ((arg = getarg(args, 's')) != NULL) {
534 			char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
535 			if (shell == NULL)
536 				shell = "";
537 			if (strcmp(shell, pwd->pw_shell) != 0) {
538 				pwd->pw_shell = shell;
539 				edited = 1;
540 			}
541 		}
542 
543 		if (getarg(args, 'L')) {
544 			if (cnf->default_class == NULL)
545 				cnf->default_class = "";
546 			if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
547 				pwd->pw_class = cnf->default_class;
548 				edited = 1;
549 			}
550 		}
551 
552 		if ((arg  = getarg(args, 'd')) != NULL) {
553 			if (strcmp(pwd->pw_dir, arg->val))
554 				edited = 1;
555 			if (stat(pwd->pw_dir = arg->val, &st) == -1) {
556 				if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
557 				  warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
558 			} else if (!S_ISDIR(st.st_mode))
559 				warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
560 		}
561 
562 		if ((arg = getarg(args, 'w')) != NULL &&
563 		    getarg(args, 'h') == NULL && getarg(args, 'H') == NULL) {
564 			login_cap_t *lc;
565 
566 			lc = login_getpwclass(pwd);
567 			if (lc == NULL ||
568 			    login_setcryptfmt(lc, "md5", NULL) == NULL)
569 				warn("setting crypt(3) format");
570 			login_close(lc);
571 			pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
572 			edited = 1;
573 		}
574 
575 	} else {
576 		login_cap_t *lc;
577 
578 		/*
579 		 * Add code
580 		 */
581 
582 		if (a_name == NULL)	/* Required */
583 			errx(EX_DATAERR, "login name required");
584 		else if ((pwd = GETPWNAM(a_name->val)) != NULL)	/* Exists */
585 			errx(EX_DATAERR, "login name `%s' already exists", a_name->val);
586 
587 		/*
588 		 * Now, set up defaults for a new user
589 		 */
590 		pwd = &fakeuser;
591 		pwd->pw_name = a_name->val;
592 		pwd->pw_class = cnf->default_class ? cnf->default_class : "";
593 		pwd->pw_uid = pw_uidpolicy(cnf, args);
594 		pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid);
595 		pwd->pw_change = pw_pwdpolicy(cnf, args);
596 		pwd->pw_expire = pw_exppolicy(cnf, args);
597 		pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
598 		pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
599 		lc = login_getpwclass(pwd);
600 		if (lc == NULL || login_setcryptfmt(lc, "md5", NULL) == NULL)
601 			warn("setting crypt(3) format");
602 		login_close(lc);
603 		pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
604 		edited = 1;
605 
606 		if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
607 			warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
608 	}
609 
610 	/*
611 	 * Shared add/edit code
612 	 */
613 	if ((arg = getarg(args, 'c')) != NULL) {
614 		char	*gecos = pw_checkname((u_char *)arg->val, 1);
615 		if (strcmp(pwd->pw_gecos, gecos) != 0) {
616 			pwd->pw_gecos = gecos;
617 			edited = 1;
618 		}
619 	}
620 
621 	if ((arg = getarg(args, 'h')) != NULL ||
622 	    (arg = getarg(args, 'H')) != NULL) {
623 		if (strcmp(arg->val, "-") == 0) {
624 			if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
625 				pwd->pw_passwd = "*";	/* No access */
626 				edited = 1;
627 			}
628 		} else {
629 			int             fd = atoi(arg->val);
630 			int		precrypt = (arg->ch == 'H');
631 			int             b;
632 			int             istty = isatty(fd);
633 			struct termios  t;
634 			login_cap_t	*lc;
635 
636 			if (istty) {
637 				if (tcgetattr(fd, &t) == -1)
638 					istty = 0;
639 				else {
640 					struct termios  n = t;
641 
642 					/* Disable echo */
643 					n.c_lflag &= ~(ECHO);
644 					tcsetattr(fd, TCSANOW, &n);
645 					printf("%s%spassword for user %s:",
646 					     (mode == M_UPDATE) ? "new " : "",
647 					     precrypt ? "encrypted " : "",
648 					     pwd->pw_name);
649 					fflush(stdout);
650 				}
651 			}
652 			b = read(fd, line, sizeof(line) - 1);
653 			if (istty) {	/* Restore state */
654 				tcsetattr(fd, TCSANOW, &t);
655 				fputc('\n', stdout);
656 				fflush(stdout);
657 			}
658 			if (b < 0) {
659 				warn("-%c file descriptor", precrypt ? 'H' :
660 				    'h');
661 				return EX_IOERR;
662 			}
663 			line[b] = '\0';
664 			if ((p = strpbrk(line, "\r\n")) != NULL)
665 				*p = '\0';
666 			if (!*line)
667 				errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
668 			if (precrypt) {
669 				if (strchr(line, ':') != NULL)
670 					return EX_DATAERR;
671 				pwd->pw_passwd = line;
672 			} else {
673 				lc = login_getpwclass(pwd);
674 				if (lc == NULL ||
675 				    login_setcryptfmt(lc, "md5", NULL) == NULL)
676 					warn("setting crypt(3) format");
677 				login_close(lc);
678 				pwd->pw_passwd = pw_pwcrypt(line);
679 			}
680 			edited = 1;
681 		}
682 	}
683 
684 	/*
685 	 * Special case: -N only displays & exits
686 	 */
687 	if (getarg(args, 'N') != NULL)
688 		return print_user(pwd,
689 				  getarg(args, 'P') != NULL,
690 				  getarg(args, '7') != NULL);
691 
692 	if (mode == M_ADD) {
693 		edited = 1;	/* Always */
694 		rc = addpwent(pwd);
695 		if (rc == -1) {
696 			warnx("user '%s' already exists", pwd->pw_name);
697 			return EX_IOERR;
698 		} else if (rc != 0) {
699 			warn("passwd file update");
700 			return EX_IOERR;
701 		}
702 		if (cnf->nispasswd && *cnf->nispasswd=='/') {
703 			rc = addnispwent(cnf->nispasswd, pwd);
704 			if (rc == -1)
705 				warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
706 			else
707 				warn("NIS passwd update");
708 			/* NOTE: we treat NIS-only update errors as non-fatal */
709 		}
710 	} else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) {
711 		if (edited) {	/* Only updated this if required */
712 			rc = chgpwent(a_name->val, pwd);
713 			if (rc == -1) {
714 				warnx("user '%s' does not exist (NIS?)", pwd->pw_name);
715 				return EX_IOERR;
716 			} else if (rc != 0) {
717 				warn("passwd file update");
718 				return EX_IOERR;
719 			}
720 			if ( cnf->nispasswd && *cnf->nispasswd=='/') {
721 				rc = chgnispwent(cnf->nispasswd, a_name->val, pwd);
722 				if (rc == -1)
723 					warn("User '%s' not found in NIS passwd", pwd->pw_name);
724 				else
725 					warn("NIS passwd update");
726 				/* NOTE: NIS-only update errors are not fatal */
727 			}
728 		}
729 	}
730 
731 	/*
732 	 * Ok, user is created or changed - now edit group file
733 	 */
734 
735 	if (mode == M_ADD || getarg(args, 'G') != NULL)
736 		editgroups(pwd->pw_name, cnf->groups);
737 
738 	/* go get a current version of pwd */
739 	pwd = GETPWNAM(a_name->val);
740 	if (pwd == NULL) {
741 		/* This will fail when we rename, so special case that */
742 		if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) {
743 			a_name->val = arg->val;		/* update new name */
744 			pwd = GETPWNAM(a_name->val);	/* refetch renamed rec */
745 		}
746 	}
747 	if (pwd == NULL)	/* can't go on without this */
748 		errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
749 
750 	grp = GETGRGID(pwd->pw_gid);
751 	pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s",
752 	       pwd->pw_name, (long) pwd->pw_uid,
753 	    grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
754 	       pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
755 
756 	/*
757 	 * If adding, let's touch and chown the user's mail file. This is not
758 	 * strictly necessary under BSD with a 0755 maildir but it also
759 	 * doesn't hurt anything to create the empty mailfile
760 	 */
761 	if (mode == M_ADD) {
762 		if (!PWALTDIR()) {
763 			sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
764 			close(open(line, O_RDWR | O_CREAT, 0600));	/* Preserve contents &
765 									 * mtime */
766 			chown(line, pwd->pw_uid, pwd->pw_gid);
767 		}
768 	}
769 
770 	/*
771 	 * Let's create and populate the user's home directory. Note
772 	 * that this also `works' for editing users if -m is used, but
773 	 * existing files will *not* be overwritten.
774 	 */
775 	if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
776 		copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid);
777 		pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
778 		       pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
779 	}
780 
781 
782 	/*
783 	 * Finally, send mail to the new user as well, if we are asked to
784 	 */
785 	if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
786 		FILE           *pfp = popen(_PATH_SENDMAIL " -t", "w");
787 
788 		if (pfp == NULL)
789 			warn("sendmail");
790 		else {
791 			fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
792 			while (fgets(line, sizeof(line), fp) != NULL) {
793 				/* Do substitutions? */
794 				fputs(line, pfp);
795 			}
796 			pclose(pfp);
797 			pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
798 			    pwd->pw_name, (long) pwd->pw_uid);
799 		}
800 		fclose(fp);
801 	}
802 
803 	return EXIT_SUCCESS;
804 }
805 
806 
807 static          uid_t
808 pw_uidpolicy(struct userconf * cnf, struct cargs * args)
809 {
810 	struct passwd  *pwd;
811 	uid_t           uid = (uid_t) - 1;
812 	struct carg    *a_uid = getarg(args, 'u');
813 
814 	/*
815 	 * Check the given uid, if any
816 	 */
817 	if (a_uid != NULL) {
818 		uid = (uid_t) atol(a_uid->val);
819 
820 		if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL)
821 			errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
822 	} else {
823 		struct bitmap   bm;
824 
825 		/*
826 		 * We need to allocate the next available uid under one of
827 		 * two policies a) Grab the first unused uid b) Grab the
828 		 * highest possible unused uid
829 		 */
830 		if (cnf->min_uid >= cnf->max_uid) {	/* Sanity
831 							 * claus^H^H^H^Hheck */
832 			cnf->min_uid = 1000;
833 			cnf->max_uid = 32000;
834 		}
835 		bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
836 
837 		/*
838 		 * Now, let's fill the bitmap from the password file
839 		 */
840 		SETPWENT();
841 		while ((pwd = GETPWENT()) != NULL)
842 			if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
843 				bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
844 		ENDPWENT();
845 
846 		/*
847 		 * Then apply the policy, with fallback to reuse if necessary
848 		 */
849 		if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
850 			uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
851 
852 		/*
853 		 * Another sanity check
854 		 */
855 		if (uid < cnf->min_uid || uid > cnf->max_uid)
856 			errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
857 		bm_dealloc(&bm);
858 	}
859 	return uid;
860 }
861 
862 
863 static          uid_t
864 pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
865 {
866 	struct group   *grp;
867 	gid_t           gid = (uid_t) - 1;
868 	struct carg    *a_gid = getarg(args, 'g');
869 
870 	/*
871 	 * If no arg given, see if default can help out
872 	 */
873 	if (a_gid == NULL && cnf->default_group && *cnf->default_group)
874 		a_gid = addarg(args, 'g', cnf->default_group);
875 
876 	/*
877 	 * Check the given gid, if any
878 	 */
879 	SETGRENT();
880 	if (a_gid != NULL) {
881 		if ((grp = GETGRNAM(a_gid->val)) == NULL) {
882 			gid = (gid_t) atol(a_gid->val);
883 			if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
884 				errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
885 		}
886 		gid = grp->gr_gid;
887 	} else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
888 		gid = grp->gr_gid;  /* Already created? Use it anyway... */
889 	} else {
890 		struct cargs    grpargs;
891 		char            tmp[32];
892 
893 		LIST_INIT(&grpargs);
894 		addarg(&grpargs, 'n', nam);
895 
896 		/*
897 		 * We need to auto-create a group with the user's name. We
898 		 * can send all the appropriate output to our sister routine
899 		 * bit first see if we can create a group with gid==uid so we
900 		 * can keep the user and group ids in sync. We purposely do
901 		 * NOT check the gid range if we can force the sync. If the
902 		 * user's name dups an existing group, then the group add
903 		 * function will happily handle that case for us and exit.
904 		 */
905 		if (GETGRGID(prefer) == NULL) {
906 			sprintf(tmp, "%lu", (unsigned long) prefer);
907 			addarg(&grpargs, 'g', tmp);
908 		}
909 		if (getarg(args, 'N'))
910 		{
911 			addarg(&grpargs, 'N', NULL);
912 			addarg(&grpargs, 'q', NULL);
913 			gid = pw_group(cnf, M_NEXT, &grpargs);
914 		}
915 		else
916 		{
917 			pw_group(cnf, M_ADD, &grpargs);
918 			if ((grp = GETGRNAM(nam)) != NULL)
919 				gid = grp->gr_gid;
920 		}
921 		a_gid = LIST_FIRST(&grpargs);
922 		while (a_gid != NULL) {
923 			struct carg    *t = LIST_NEXT(a_gid, list);
924 			LIST_REMOVE(a_gid, list);
925 			a_gid = t;
926 		}
927 	}
928 	ENDGRENT();
929 	return gid;
930 }
931 
932 
933 static          time_t
934 pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
935 {
936 	time_t          result = 0;
937 	time_t          now = time(NULL);
938 	struct carg    *arg = getarg(args, 'p');
939 
940 	if (arg != NULL) {
941 		if ((result = parse_date(now, arg->val)) == now)
942 			errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
943 	} else if (cnf->password_days > 0)
944 		result = now + ((long) cnf->password_days * 86400L);
945 	return result;
946 }
947 
948 
949 static          time_t
950 pw_exppolicy(struct userconf * cnf, struct cargs * args)
951 {
952 	time_t          result = 0;
953 	time_t          now = time(NULL);
954 	struct carg    *arg = getarg(args, 'e');
955 
956 	if (arg != NULL) {
957 		if ((result = parse_date(now, arg->val)) == now)
958 			errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
959 	} else if (cnf->expire_days > 0)
960 		result = now + ((long) cnf->expire_days * 86400L);
961 	return result;
962 }
963 
964 
965 static char    *
966 pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
967 {
968 	struct carg    *arg = getarg(args, 'd');
969 
970 	if (arg)
971 		return arg->val;
972 	else {
973 		static char     home[128];
974 
975 		if (cnf->home == NULL || *cnf->home == '\0')
976 			errx(EX_CONFIG, "no base home directory set");
977 		sprintf(home, "%s/%s", cnf->home, user);
978 		return home;
979 	}
980 }
981 
982 static char    *
983 shell_path(char const * path, char *shells[], char *sh)
984 {
985 	if (sh != NULL && (*sh == '/' || *sh == '\0'))
986 		return sh;	/* specified full path or forced none */
987 	else {
988 		char           *p;
989 		char            paths[_UC_MAXLINE];
990 
991 		/*
992 		 * We need to search paths
993 		 */
994 		strlcpy(paths, path, sizeof(paths));
995 		for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
996 			int             i;
997 			static char     shellpath[256];
998 
999 			if (sh != NULL) {
1000 				sprintf(shellpath, "%s/%s", p, sh);
1001 				if (access(shellpath, X_OK) == 0)
1002 					return shellpath;
1003 			} else
1004 				for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
1005 					sprintf(shellpath, "%s/%s", p, shells[i]);
1006 					if (access(shellpath, X_OK) == 0)
1007 						return shellpath;
1008 				}
1009 		}
1010 		if (sh == NULL)
1011 			errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
1012 		errx(EX_CONFIG, "no default shell available or defined");
1013 		return NULL;
1014 	}
1015 }
1016 
1017 
1018 static char    *
1019 pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
1020 {
1021 	char           *sh = newshell;
1022 	struct carg    *arg = getarg(args, 's');
1023 
1024 	if (newshell == NULL && arg != NULL)
1025 		sh = arg->val;
1026 	return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
1027 }
1028 
1029 #define	SALTSIZE	32
1030 
1031 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
1032 
1033 char           *
1034 pw_pwcrypt(char *password)
1035 {
1036 	int             i;
1037 	char            salt[SALTSIZE + 1];
1038 
1039 	static char     buf[256];
1040 
1041 	/*
1042 	 * Calculate a salt value
1043 	 */
1044 	for (i = 0; i < SALTSIZE; i++)
1045 		salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
1046 	salt[SALTSIZE] = '\0';
1047 
1048 	return strcpy(buf, crypt(password, salt));
1049 }
1050 
1051 
1052 static char    *
1053 pw_password(struct userconf * cnf, struct cargs * args, char const * user)
1054 {
1055 	int             i, l;
1056 	char            pwbuf[32];
1057 
1058 	switch (cnf->default_password) {
1059 	case -1:		/* Random password */
1060 		l = (arc4random() % 8 + 8);	/* 8 - 16 chars */
1061 		for (i = 0; i < l; i++)
1062 			pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
1063 		pwbuf[i] = '\0';
1064 
1065 		/*
1066 		 * We give this information back to the user
1067 		 */
1068 		if (getarg(args, 'h') == NULL && getarg(args, 'H') == NULL &&
1069 		    getarg(args, 'N') == NULL) {
1070 			if (isatty(STDOUT_FILENO))
1071 				printf("Password for '%s' is: ", user);
1072 			printf("%s\n", pwbuf);
1073 			fflush(stdout);
1074 		}
1075 		break;
1076 
1077 	case -2:		/* No password at all! */
1078 		return "";
1079 
1080 	case 0:		/* No login - default */
1081 	default:
1082 		return "*";
1083 
1084 	case 1:		/* user's name */
1085 		strlcpy(pwbuf, user, sizeof(pwbuf));
1086 		break;
1087 	}
1088 	return pw_pwcrypt(pwbuf);
1089 }
1090 
1091 
1092 static int
1093 print_user(struct passwd * pwd, int pretty, int v7)
1094 {
1095 	if (!pretty) {
1096 		char            buf[_UC_MAXLINE];
1097 
1098 		fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
1099 		fputs(buf, stdout);
1100 	} else {
1101 		int		j;
1102 		char           *p;
1103 		struct group   *grp = GETGRGID(pwd->pw_gid);
1104 		char            uname[60] = "User &", office[60] = "[None]",
1105 		                wphone[60] = "[None]", hphone[60] = "[None]";
1106 		char		acexpire[32] = "[None]", pwexpire[32] = "[None]";
1107 		struct tm *    tptr;
1108 
1109 		if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
1110 			strlcpy(uname, p, sizeof(uname));
1111 			if ((p = strtok(NULL, ",")) != NULL) {
1112 				strlcpy(office, p, sizeof(office));
1113 				if ((p = strtok(NULL, ",")) != NULL) {
1114 					strlcpy(wphone, p, sizeof(wphone));
1115 					if ((p = strtok(NULL, "")) != NULL) {
1116 						strlcpy(hphone, p,
1117 						    sizeof(hphone));
1118 					}
1119 				}
1120 			}
1121 		}
1122 		/*
1123 		 * Handle '&' in gecos field
1124 		 */
1125 		if ((p = strchr(uname, '&')) != NULL) {
1126 			int             l = strlen(pwd->pw_name);
1127 			int             m = strlen(p);
1128 
1129 			memmove(p + l, p + 1, m);
1130 			memmove(p, pwd->pw_name, l);
1131 			*p = (char) toupper((unsigned char)*p);
1132 		}
1133 		if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
1134 			strftime(acexpire, sizeof acexpire, "%c", tptr);
1135 		if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
1136 			strftime(pwexpire, sizeof pwexpire, "%c", tptr);
1137 		printf("Login Name: %-15s   #%-12ld Group: %-15s   #%ld\n"
1138 		       " Full Name: %s\n"
1139 		       "      Home: %-26.26s      Class: %s\n"
1140 		       "     Shell: %-26.26s     Office: %s\n"
1141 		       "Work Phone: %-26.26s Home Phone: %s\n"
1142 		       "Acc Expire: %-26.26s Pwd Expire: %s\n",
1143 		       pwd->pw_name, (long) pwd->pw_uid,
1144 		       grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
1145 		       uname, pwd->pw_dir, pwd->pw_class,
1146 		       pwd->pw_shell, office, wphone, hphone,
1147 		       acexpire, pwexpire);
1148 	        SETGRENT();
1149 		j = 0;
1150 		while ((grp=GETGRENT()) != NULL)
1151 		{
1152 			int     i = 0;
1153 			while (grp->gr_mem[i] != NULL)
1154 			{
1155 				if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
1156 				{
1157 					printf(j++ == 0 ? "    Groups: %s" : ",%s", grp->gr_name);
1158 					break;
1159 				}
1160 				++i;
1161 			}
1162 		}
1163 		ENDGRENT();
1164 		printf("%s", j ? "\n" : "");
1165 	}
1166 	return EXIT_SUCCESS;
1167 }
1168 
1169 char    *
1170 pw_checkname(u_char *name, int gecos)
1171 {
1172 	char showch[8];
1173 	u_char const *badchars, *ch, *showtype;
1174 	int reject;
1175 
1176 	ch = name;
1177 	reject = 0;
1178 	if (gecos) {
1179 		/* See if the name is valid as a gecos (comment) field. */
1180 		badchars = ":!@";
1181 		showtype = "gecos field";
1182 	} else {
1183 		/* See if the name is valid as a userid or group. */
1184 		badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
1185 		showtype = "userid/group name";
1186 		/* Userids and groups can not have a leading '-'. */
1187 		if (*ch == '-')
1188 			reject = 1;
1189 	}
1190 	if (!reject) {
1191 		while (*ch) {
1192 			if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
1193 			    *ch == 127) {
1194 				reject = 1;
1195 				break;
1196 			}
1197 			/* 8-bit characters are only allowed in GECOS fields */
1198 			if (!gecos && (*ch & 0x80)) {
1199 				reject = 1;
1200 				break;
1201 			}
1202 			ch++;
1203 		}
1204 	}
1205 	/*
1206 	 * A `$' is allowed as the final character for userids and groups,
1207 	 * mainly for the benefit of samba.
1208 	 */
1209 	if (reject && !gecos) {
1210 		if (*ch == '$' && *(ch + 1) == '\0') {
1211 			reject = 0;
1212 			ch++;
1213 		}
1214 	}
1215 	if (reject) {
1216 		snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
1217 		    ? "`%c'" : "0x%02x", *ch);
1218 		errx(EX_DATAERR, "invalid character %s at position %d in %s",
1219 		    showch, (ch - name), showtype);
1220 	}
1221 	if (!gecos && (ch - name) > LOGNAMESIZE)
1222 		errx(EX_DATAERR, "name too long `%s' (max is %d)", name,
1223 		    LOGNAMESIZE);
1224 	return (char *)name;
1225 }
1226 
1227 
1228 static void
1229 rmat(uid_t uid)
1230 {
1231 	DIR            *d = opendir("/var/at/jobs");
1232 
1233 	if (d != NULL) {
1234 		struct dirent  *e;
1235 
1236 		while ((e = readdir(d)) != NULL) {
1237 			struct stat     st;
1238 
1239 			if (strncmp(e->d_name, ".lock", 5) != 0 &&
1240 			    stat(e->d_name, &st) == 0 &&
1241 			    !S_ISDIR(st.st_mode) &&
1242 			    st.st_uid == uid) {
1243 				char            tmp[MAXPATHLEN];
1244 
1245 				sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
1246 				system(tmp);
1247 			}
1248 		}
1249 		closedir(d);
1250 	}
1251 }
1252 
1253 static void
1254 rmopie(char const * name)
1255 {
1256 	static const char etcopie[] = "/etc/opiekeys";
1257 	FILE   *fp = fopen(etcopie, "r+");
1258 
1259 	if (fp != NULL) {
1260 		char	tmp[1024];
1261 		off_t	atofs = 0;
1262 		int	length = strlen(name);
1263 
1264 		while (fgets(tmp, sizeof tmp, fp) != NULL) {
1265 			if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') {
1266 				if (fseek(fp, atofs, SEEK_SET) == 0) {
1267 					fwrite("#", 1, 1, fp);	/* Comment username out */
1268 				}
1269 				break;
1270 			}
1271 			atofs = ftell(fp);
1272 		}
1273 		/*
1274 		 * If we got an error of any sort, don't update!
1275 		 */
1276 		fclose(fp);
1277 	}
1278 }
1279 
1280