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