xref: /illumos-gate/usr/src/cmd/passmgmt/passmgmt.c (revision e4d060fb4c00d44cd578713eb9a921f594b733b8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <sys/types.h>
34 #include <shadow.h>
35 #include <pwd.h>
36 #include <string.h>
37 #include <signal.h>
38 #include <sys/stat.h>
39 #include <errno.h>
40 #include <time.h>
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <locale.h>
44 #include <fcntl.h>
45 #include <secdb.h>
46 #include <user_attr.h>
47 
48 #define	CMT_SIZE	(128+1)	/* Argument sizes + 1 (for '\0') */
49 #define	DIR_SIZE	(256+1)
50 #define	SHL_SIZE	(256+1)
51 #define	ENTRY_LENGTH	512	/* Max length of an /etc/passwd entry */
52 #define	UID_MIN		100	/* Lower bound of default UID */
53 
54 #define	M_MASK		01	/* Masks for the optn_mask variable */
55 #define	L_MASK		02	/* It keeps track of which options   */
56 #define	C_MASK		04	/* have been entered */
57 #define	H_MASK		010
58 #define	U_MASK		020
59 #define	G_MASK		040
60 #define	S_MASK		0100
61 #define	O_MASK		0200
62 #define	A_MASK		0400
63 #define	D_MASK		01000
64 #define	F_MASK		02000
65 #define	E_MASK		04000
66 
67 #define	UATTR_MASK	010000
68 
69 					/* flags for info_mask */
70 #define	LOGNAME_EXIST	01		/* logname exists */
71 #define	BOTH_FILES	02		/* touch both password files */
72 #define	WRITE_P_ENTRY	04		/* write out password entry */
73 #define	WRITE_S_ENTRY	010		/* write out shadow entry */
74 #define	NEED_DEF_UID	020		/* need default uid */
75 #define	FOUND		040		/* found the entry in password file */
76 #define	LOCKED		0100		/* did we lock the password file */
77 #define	UATTR_FILE	0200		/* touch user_attr file */
78 #define	BAD_ENT_MESSAGE	"%s: Bad entry found in /etc/passwd.  Run pwconv.\n"
79 
80 typedef struct kvopts {
81 	const char	option;
82 	const char	*key;
83 	char		*newvalue;
84 } kvopts_t;
85 
86 /* mapping of extensible keywords and options */
87 kvopts_t ua_opts[] =  {
88 { 'A',	USERATTR_AUTHS_KW },
89 { 'P',	USERATTR_PROFILES_KW },
90 { 'R',	USERATTR_ROLES_KW },
91 { 'T',	USERATTR_TYPE_KW },
92 { '\0', USERATTR_DEFAULTPROJ_KW },
93 { '\0',	USERATTR_LIMPRIV_KW },
94 { '\0',	USERATTR_DFLTPRIV_KW },
95 { '\0', USERATTR_LOCK_AFTER_RETRIES_KW },
96 { '\0', USERATTR_LABELVIEW },
97 { '\0', USERATTR_CLEARANCE },
98 { '\0', USERATTR_MINLABEL },
99 { '\0', USERATTR_IDLECMD_KW },
100 { '\0', USERATTR_IDLETIME_KW },
101 };
102 
103 #define	UA_KEYS		(sizeof (ua_opts)/sizeof (kvopts_t))
104 
105 
106 char defdir[] = "/home/";	/* default home directory for new user */
107 char pwdflr[] =	"x";		/* password string for /etc/passwd */
108 char lkstring[] = "*LK*";	/* lock string for shadow password */
109 char nullstr[] = "";		/* null string */
110 char	*msg;			/* pointer to error message	*/
111 
112 #define	DATMSK "DATEMSK=/etc/datemsk"
113 
114 #define	OUSERATTR_FILENAME	"/etc/ouser_attr"
115 #define	USERATTR_TEMP		"/etc/uatmp"
116 
117 struct uid_blk {
118 	struct uid_blk *link;
119 	uid_t low;		/* low bound for this uid block */
120 	uid_t high;		/* high bound for this uid block */
121 };
122 
123 extern userattr_t *fgetuserattr(FILE *);
124 
125 
126 /*
127  * Declare all functions that do not return integers.  This is here
128  * to get rid of some lint messages
129  */
130 
131 void	uid_bcom(struct uid_blk *), add_ublk(uid_t, struct uid_blk *),
132 	bad_perm(void),
133 	bad_usage(char *), bad_arg(char *), bad_uid(void), bad_pasf(void),
134 	file_error(void), bad_news(void), no_lock(void), add_uid(uid_t),
135 	rid_tmpf(void), ck_p_sz(struct passwd *), ck_s_sz(struct spwd *),
136 	bad_name(char *), bad_uattr(void);
137 
138 void file_copy(FILE *spf, long NIS_pos);
139 
140 static FILE *fp_ptemp, *fp_stemp, *fp_uatemp;
141 static int fd_ptemp, fd_stemp, fd_uatemp;
142 
143 /*
144  * The uid_blk structure is used in the search for the default
145  * uid.  Each uid_blk represent a range of uid(s) that are currently
146  * used on the system.
147  */
148 
149 
150 #ifndef att
151 /*
152  * getspnan routine that ONLY looks at the local shadow file
153  */
154 struct spwd *
155 local_getspnam(char *name)
156 {
157 	FILE *shadf;
158 	struct spwd *sp;
159 
160 	if ((shadf = fopen("/etc/shadow", "r")) == NULL)
161 		return (NULL);
162 
163 	while ((sp = fgetspent(shadf)) != NULL) {
164 		if (strcmp(sp->sp_namp, name) == 0)
165 			break;
166 	}
167 
168 	fclose(shadf);
169 
170 	return (sp);
171 }
172 #endif
173 
174 static void
175 putuserattrent(userattr_t *user, FILE *f)
176 {
177 	int		i, j;
178 	char		*key;
179 	char		*val;
180 	kv_t		*kv_pair;
181 
182 	/*
183 	 * Avoid trivial entries.  Those with no attributes or with
184 	 * only "type=normal".  This retains backward compatibility.
185 	 */
186 	if (user->attr == NULL)
187 		return;
188 
189 	kv_pair = user->attr->data;
190 
191 	for (i = j = 0; i < user->attr->length; i++) {
192 		key = kv_pair[i].key;
193 		val = kv_pair[i].value;
194 		if ((key == NULL) || (val == NULL))
195 			break;
196 		if (strlen(val) == 0 ||
197 		    (strcmp(key, USERATTR_TYPE_KW) == 0 &&
198 		    strcmp(val, USERATTR_TYPE_NORMAL_KW) == 0))
199 			continue;
200 		j++;
201 	}
202 	if (j == 0)
203 		return;
204 
205 	(void) fprintf(f, "%s:%s:%s:%s:", user->name, user->qualifier,
206 	    user->res1, user->res2);
207 
208 	for (i = j = 0; i < user->attr->length; i++) {
209 		key = kv_pair[i].key;
210 		val = kv_pair[i].value;
211 		if ((key == NULL) || (val == NULL))
212 			break;
213 		if (strlen(val) == 0)
214 			continue;
215 		if (j > 0)
216 			(void) fprintf(f, KV_DELIMITER);
217 		(void) fprintf(f, "%s=%s", key, val);
218 		j++;
219 	}
220 	(void) fprintf(f, "\n");
221 }
222 
223 static void
224 assign_attr(userattr_t *user, const char *newkey, char *val) {
225 
226 	int		i;
227 	char		*key;
228 	kv_t		*kv_pair;
229 	int		avail = -1;
230 
231 	if (user->attr != NULL) {
232 		kv_pair = user->attr->data;
233 		for (i = 0; i < user->attr->length; i++) {
234 			key = kv_pair[i].key;
235 			if (key == NULL) {
236 				avail = i;
237 				continue;
238 			} else if (strcmp(key, newkey) == 0) {
239 				kv_pair[i].value = strdup(val);
240 				return;
241 			}
242 		}
243 
244 		if (avail == -1)
245 			avail = user->attr->length++;
246 		kv_pair[avail].key = strdup(newkey);
247 		kv_pair[avail].value = strdup(val);
248 	}
249 }
250 
251 static void
252 unassign_role(userattr_t *user, char *rolelist, char *role) {
253 
254 	char *roleptr;
255 	char *templist;
256 	char *temprole;
257 	int  length;
258 
259 	roleptr = rolelist;
260 	templist = strdup(roleptr);
261 	temprole = strtok(templist, ",");
262 	while (temprole) {
263 		if (strcmp(temprole, role) == 0) {
264 
265 			length = strlen(role);
266 			roleptr += temprole - templist;
267 
268 			if (*(roleptr + length) == ',')
269 				length++;
270 			strcpy(roleptr, roleptr + length);
271 			length = strlen(roleptr) - 1;
272 			if (*(roleptr + length) == ',')
273 				*(roleptr + length) = '\0';
274 			assign_attr(user, USERATTR_ROLES_KW, rolelist);
275 			break;
276 		} else {
277 			temprole = strtok(NULL, ",");
278 		}
279 	}
280 }
281 
282 struct uid_blk *uid_sp;
283 char *prognamp;			/* program name */
284 extern int errno;
285 int optn_mask = 0, info_mask = 0;
286 extern int getdate_err;
287 
288 int
289 main(int argc, char **argv)
290 {
291 	int c, i;
292 	char *lognamp, *char_p;
293 	int end_of_file = 0;
294 	int error;
295 	long date = 0;
296 	FILE *pwf, *spf, *uaf;
297 
298 	struct passwd *pw_ptr1p, passwd_st;
299 	struct spwd *sp_ptr1p, shadow_st;
300 	userattr_t *ua_ptr1p, userattr_st;
301 	static kv_t ua_kv[KV_ADD_KEYS];
302 	kva_t ua_kva;
303 	struct stat statbuf;
304 	struct tm *tm_ptr;
305 	int NIS_entry_seen;		/* NIS scanning flag */
306 	/*
307 	 * NIS start pos, really pointer to first entry AFTER first
308 	 * NIS-referant entry
309 	 */
310 	long NIS_pos;
311 	long cur_pos;		/* Current pos, used with nis-pos above */
312 
313 	(void) setlocale(LC_ALL, "");
314 
315 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
316 #define	TEXT_DOMAIN "SYS_TEST"
317 #endif
318 	(void) textdomain(TEXT_DOMAIN);
319 
320 	tzset();
321 	/* Get program name */
322 	prognamp = argv[0];
323 
324 	/* Check identity */
325 	if (geteuid() != 0)
326 		bad_perm();
327 
328 	/* Lock the password file(s) */
329 
330 	if (lckpwdf() != 0)
331 		no_lock();
332 	info_mask |= LOCKED;		/* remember we locked */
333 
334 	/* initialize the two structures */
335 
336 	passwd_st.pw_passwd = pwdflr;		/* bogus password */
337 	passwd_st.pw_name = nullstr;		/* login name */
338 	passwd_st.pw_uid = -1;			/* no uid */
339 	passwd_st.pw_gid = 1;			/* default gid */
340 	passwd_st.pw_age = nullstr;		/* no aging info. */
341 	passwd_st.pw_comment = nullstr;	/* no comments */
342 	passwd_st.pw_gecos = nullstr;		/* no comments */
343 	passwd_st.pw_dir = nullstr;		/* no default directory */
344 	passwd_st.pw_shell = nullstr;		/* no default shell */
345 
346 	shadow_st.sp_namp = nullstr;	/* no name */
347 	shadow_st.sp_pwdp = lkstring;	/* locked password */
348 	shadow_st.sp_lstchg = -1;	/* no lastchanged date */
349 	shadow_st.sp_min = -1;	/* no min */
350 	shadow_st.sp_max = -1;	/* no max */
351 	shadow_st.sp_warn = -1; 	/* no warn */
352 	shadow_st.sp_inact = -1;	/* no inactive */
353 	shadow_st.sp_expire = -1;	/* no expire */
354 	shadow_st.sp_flag = 0;	/* no flag */
355 
356 	userattr_st.name = nullstr;
357 	userattr_st.qualifier = nullstr;
358 	userattr_st.res1 = nullstr;
359 	userattr_st.res2 = nullstr;
360 
361 	ua_kva.length = 1;
362 	ua_kv[0].key = USERATTR_TYPE_KW;
363 	ua_kv[0].value = USERATTR_TYPE_NORMAL_KW;
364 	ua_kva.data = ua_kv;
365 	userattr_st.attr = &ua_kva;
366 
367 	/* parse the command line */
368 
369 	while ((c = getopt(argc, argv,
370 			    "ml:c:h:u:g:s:f:e:k:A:P:R:T:oadK:")) != -1) {
371 
372 		switch (c) {
373 		case 'm':
374 			/* Modify */
375 
376 			if ((A_MASK|D_MASK|M_MASK) & optn_mask)
377 				bad_usage("Invalid combination of options");
378 
379 			optn_mask |= M_MASK;
380 			break;
381 
382 		case 'l' :
383 			/* Change logname */
384 
385 			if ((A_MASK|D_MASK|L_MASK) & optn_mask)
386 				bad_usage("Invalid combination of options");
387 
388 			if (strpbrk(optarg, ":\n") ||
389 			    strlen(optarg) == 0)
390 				bad_arg("Invalid argument to option -l");
391 
392 			optn_mask |= L_MASK;
393 			passwd_st.pw_name = optarg;
394 			shadow_st.sp_namp = optarg;
395 			userattr_st.name = optarg;
396 			break;
397 
398 		case 'f' :
399 			/* set inactive */
400 
401 			if ((D_MASK|F_MASK) & optn_mask)
402 				bad_usage("Invalid combination of options");
403 			if (((shadow_st.sp_inact =
404 			    strtol(optarg, &char_p, 10)) < (long)0) ||
405 			    (*char_p != '\0') ||
406 			    strlen(optarg) == 0)
407 				bad_arg("Invalid argument to option -f");
408 			if (shadow_st.sp_inact == 0)
409 				shadow_st.sp_inact = -1;
410 			optn_mask |= F_MASK;
411 			break;
412 
413 		case 'e' :
414 			/* set expire date */
415 
416 			if ((D_MASK|E_MASK) & optn_mask)
417 				bad_usage("Invalid combination of options");
418 
419 			if ((strlen(optarg)) < (size_t)2)
420 				shadow_st.sp_expire = -1;
421 			else {
422 				putenv(DATMSK);
423 				if ((tm_ptr =  getdate(optarg)) == NULL) {
424 					msg = "Invalid argument to option -e";
425 					bad_arg(msg);
426 				}
427 				if ((date =  mktime(tm_ptr)) < 0) {
428 					msg = "Invalid argument to option -e";
429 					bad_arg(msg);
430 				}
431 				shadow_st.sp_expire = (date / DAY);
432 				if (shadow_st.sp_expire <= DAY_NOW) {
433 					msg = "Invalid argument to option -e";
434 					bad_arg(msg);
435 				}
436 			}
437 
438 			optn_mask |= E_MASK;
439 			break;
440 
441 		case 'c' :
442 			/* The comment */
443 
444 			if ((D_MASK|C_MASK) & optn_mask)
445 				bad_usage("Invalid combination of options");
446 
447 			if (strlen(optarg) > (size_t)CMT_SIZE ||
448 			    strpbrk(optarg, ":\n"))
449 				bad_arg("Invalid argument to option -c");
450 
451 			    optn_mask |= C_MASK;
452 			    passwd_st.pw_comment = optarg;
453 			    passwd_st.pw_gecos = optarg;
454 			    break;
455 
456 		case 'h' :
457 			    /* The home directory */
458 
459 			    if ((D_MASK|H_MASK) & optn_mask)
460 				    bad_usage("Invalid combination of options");
461 
462 			    if (strlen(optarg) > (size_t)DIR_SIZE ||
463 				strpbrk(optarg, ":\n"))
464 				    bad_arg("Invalid argument to option -h");
465 
466 			    optn_mask |= H_MASK;
467 			    passwd_st.pw_dir = optarg;
468 			    break;
469 
470 		case 'u' :
471 			/* The uid */
472 
473 			if ((D_MASK|U_MASK) & optn_mask)
474 				bad_usage("Invalid combination of options");
475 
476 			optn_mask |= U_MASK;
477 			passwd_st.pw_uid = (uid_t)strtol(optarg, &char_p, 10);
478 			if ((*char_p != '\0') ||
479 			    (passwd_st.pw_uid < 0) ||
480 			    (strlen(optarg) == 0))
481 				bad_arg("Invalid argument to option -u");
482 
483 			break;
484 
485 		case 'g' :
486 			/* The gid */
487 
488 			if ((D_MASK|G_MASK) & optn_mask)
489 				bad_usage("Invalid combination of options");
490 
491 			optn_mask |= G_MASK;
492 			passwd_st.pw_gid = (gid_t)strtol(optarg, &char_p, 10);
493 
494 			if ((*char_p != '\0') || (passwd_st.pw_gid < 0) ||
495 			    (strlen(optarg) == 0))
496 				bad_arg("Invalid argument to option -g");
497 			break;
498 
499 		case 's' :
500 			/* The shell */
501 
502 			if ((D_MASK|S_MASK) & optn_mask)
503 				bad_usage("Invalid combination of options");
504 
505 			if (strlen(optarg) > (size_t)SHL_SIZE ||
506 			    strpbrk(optarg, ":\n"))
507 				bad_arg("Invalid argument to option -s");
508 
509 			optn_mask |= S_MASK;
510 			passwd_st.pw_shell = optarg;
511 			break;
512 
513 		case 'o' :
514 			/* Override unique uid	*/
515 
516 			if ((D_MASK|O_MASK) & optn_mask)
517 				bad_usage("Invalid combination of options");
518 
519 			optn_mask |= O_MASK;
520 			break;
521 
522 		case 'a' :
523 			/* Add */
524 
525 			if ((A_MASK|M_MASK|D_MASK|L_MASK) & optn_mask)
526 				bad_usage("Invalid combination of options");
527 
528 			optn_mask |= A_MASK;
529 			break;
530 
531 		case 'd' :
532 			/* Delete */
533 
534 			if ((D_MASK|M_MASK|L_MASK|C_MASK|
535 			    H_MASK|U_MASK|G_MASK|S_MASK|
536 			    O_MASK|A_MASK) & optn_mask)
537 				bad_usage("Invalid combination of options");
538 
539 			optn_mask |= D_MASK;
540 			break;
541 
542 		case 'K':
543 			if (D_MASK & optn_mask)
544 				bad_usage("Invalid combination of options");
545 
546 			char_p = strchr(optarg, '=');
547 			if (char_p == NULL)
548 				bad_usage("Missing value in -K option");
549 
550 			*char_p++ = '\0';
551 
552 			for (i = 0; i < UA_KEYS; i++) {
553 				if (strcmp(optarg, ua_opts[i].key) == 0) {
554 					ua_opts[i].newvalue = char_p;
555 					assign_attr(&userattr_st, optarg,
556 						char_p);
557 					break;
558 				}
559 			}
560 			if (i == UA_KEYS)
561 				bad_usage("bad key");
562 			optn_mask |= UATTR_MASK;
563 			break;
564 
565 		case '?' :
566 
567 			bad_usage("");
568 			break;
569 
570 		default :
571 			/* Extended User Attributes */
572 			{
573 			    int j;
574 
575 			    for (j = 0; j < UA_KEYS; j++) {
576 				if (ua_opts[j].option == (char)c) {
577 					if ((D_MASK) & optn_mask)
578 						bad_usage("Invalid combination"
579 							" of options");
580 					optn_mask |= UATTR_MASK;
581 					assign_attr(&userattr_st,
582 					    ua_opts[j].key, optarg);
583 					ua_opts[j].newvalue = optarg;
584 					break;
585 				}
586 			    }
587 			    break;
588 			}
589 		}
590 	}
591 
592 	/* check command syntax for the following errors */
593 	/* too few or too many arguments */
594 	/* no -a -m or -d option */
595 	/* -o without -u */
596 	/* -m with no other option */
597 
598 	if (optind == argc || argc > (optind+1) ||
599 	    !((A_MASK|M_MASK|D_MASK) & optn_mask) ||
600 	    ((optn_mask & O_MASK) && !(optn_mask & U_MASK)) ||
601 	    ((optn_mask & M_MASK) &&
602 	    !(optn_mask &
603 	    (L_MASK|C_MASK|H_MASK|U_MASK|G_MASK|S_MASK|F_MASK|
604 		E_MASK|UATTR_MASK))))
605 			bad_usage("Invalid command syntax");
606 
607 	/* null string argument or bad characters ? */
608 	if ((strlen(argv[optind]) == 0) || strpbrk(argv[optind], ":\n"))
609 		bad_arg("Invalid name");
610 
611 	lognamp = argv [optind];
612 
613 	/*
614 	 * if we are adding a new user or modifying an existing user
615 	 * (not the logname), then copy logname into the two data
616 	 *  structures
617 	 */
618 
619 	if ((A_MASK & optn_mask) ||
620 	    ((M_MASK & optn_mask) && !(optn_mask & L_MASK))) {
621 		passwd_st.pw_name = argv [optind];
622 		shadow_st.sp_namp = argv [optind];
623 		userattr_st.name = argv [optind];
624 	}
625 
626 	/* Put in directory if we are adding and we need a default */
627 
628 	if (!(optn_mask & H_MASK) && (optn_mask & A_MASK)) {
629 		if ((passwd_st.pw_dir = malloc((size_t)DIR_SIZE)) == NULL)
630 			file_error();
631 
632 		*passwd_st.pw_dir = '\0';
633 		(void) strcat(passwd_st.pw_dir, defdir);
634 		(void) strcat(passwd_st.pw_dir, lognamp);
635 	}
636 
637 	/* Check the number of password files we are touching */
638 
639 	if ((!((M_MASK & optn_mask) && !(L_MASK & optn_mask))) ||
640 		((M_MASK & optn_mask) && ((E_MASK & optn_mask) ||
641 			(F_MASK & optn_mask))))
642 		info_mask |= BOTH_FILES;
643 
644 	if ((D_MASK|L_MASK|UATTR_MASK) & optn_mask)
645 		info_mask |= UATTR_FILE;
646 
647 	/* Open the temporary file(s) with appropriate permission mask */
648 	/* and the appropriate owner */
649 
650 	if (stat(PASSWD, &statbuf) < 0)
651 		file_error();
652 
653 	fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
654 	if (fd_ptemp == -1) {
655 		if (errno == EEXIST) {
656 			if (unlink(PASSTEMP)) {
657 				msg = "%s: warning: cannot unlink %s\n";
658 				(void) fprintf(stderr, gettext(msg), prognamp,
659 						PASSTEMP);
660 			}
661 			fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY,
662 					statbuf.st_mode);
663 			if (fd_ptemp == -1) {
664 				file_error();
665 			}
666 
667 		} else
668 			file_error();
669 	}
670 	fp_ptemp = fdopen(fd_ptemp, "w");
671 	if (fp_ptemp == NULL)
672 		file_error();
673 	error = fchown(fd_ptemp, statbuf.st_uid, statbuf.st_gid);
674 	if (error == 0)
675 		error = fchmod(fd_ptemp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
676 	if (error != 0) {
677 		(void) fclose(fp_ptemp);
678 		if (unlink(PASSTEMP)) {
679 			msg = "%s: warning: cannot unlink %s\n";
680 			(void) fprintf(stderr, gettext(msg), prognamp,
681 				PASSTEMP);
682 		}
683 		file_error();
684 	}
685 
686 	if (info_mask & BOTH_FILES) {
687 		if (stat(SHADOW, &statbuf) < 0) {
688 			rid_tmpf();
689 			file_error();
690 		}
691 		fd_stemp = open(SHADTEMP, O_CREAT|O_EXCL|O_WRONLY,
692 		    statbuf.st_mode);
693 		if (fd_stemp == -1) {
694 			if (errno == EEXIST) {
695 				if (unlink(SHADTEMP)) {
696 					msg = "%s: warning: cannot unlink %s\n";
697 					(void) fprintf(stderr, gettext(msg),
698 						prognamp, SHADTEMP);
699 				}
700 				fd_stemp = open(SHADTEMP,
701 						O_CREAT|O_EXCL|O_WRONLY,
702 						statbuf.st_mode);
703 				if (fd_stemp == -1) {
704 					rid_tmpf();
705 					file_error();
706 				}
707 
708 			} else {
709 				rid_tmpf();
710 				file_error();
711 			}
712 		}
713 		fp_stemp = fdopen(fd_stemp, "w");
714 		if (fp_stemp == NULL) {
715 			rid_tmpf();
716 			file_error();
717 		}
718 		error = fchown(fd_stemp, statbuf.st_uid, statbuf.st_gid);
719 		if (error == 0)
720 			error = fchmod(fd_stemp, S_IRUSR);
721 		if (error != 0) {
722 			rid_tmpf();
723 			file_error();
724 		}
725 	}
726 
727 	if (info_mask & UATTR_FILE) {
728 		if (stat(USERATTR_FILENAME, &statbuf) < 0) {
729 			rid_tmpf();
730 			file_error();
731 		}
732 		fd_uatemp = open(USERATTR_TEMP, O_CREAT|O_EXCL|O_WRONLY,
733 		    statbuf.st_mode);
734 		if (fd_uatemp == -1) {
735 			if (errno == EEXIST) {
736 				if (unlink(USERATTR_TEMP)) {
737 					msg = "%s: warning: cannot unlink %s\n";
738 					(void) fprintf(stderr, gettext(msg),
739 						prognamp, USERATTR_TEMP);
740 				}
741 				fd_uatemp = open(USERATTR_TEMP,
742 					O_CREAT|O_EXCL|O_WRONLY,
743 						statbuf.st_mode);
744 				if (fd_uatemp == -1) {
745 					rid_tmpf();
746 					file_error();
747 				}
748 
749 			} else {
750 				rid_tmpf();
751 				file_error();
752 			}
753 		}
754 		fp_uatemp = fdopen(fd_uatemp, "w");
755 		if (fp_uatemp == NULL) {
756 			rid_tmpf();
757 			file_error();
758 		}
759 		error = fchown(fd_uatemp, statbuf.st_uid, statbuf.st_gid);
760 		if (error == 0)
761 			error = fchmod(fd_uatemp,
762 			    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
763 		if (error != 0) {
764 			rid_tmpf();
765 			file_error();
766 		}
767 	}
768 	/* Default uid needed ? */
769 
770 	if (!(optn_mask & U_MASK) && (optn_mask & A_MASK)) {
771 		/* mark it in the information mask */
772 		info_mask |= NEED_DEF_UID;
773 
774 		/* create the head of the uid number list */
775 		uid_sp = malloc(sizeof (struct uid_blk));
776 		if (uid_sp == NULL) {
777 			rid_tmpf();
778 			file_error();
779 		}
780 
781 		uid_sp->link = NULL;
782 		uid_sp->low = (UID_MIN -1);
783 		uid_sp->high = (UID_MIN -1);
784 	}
785 
786 	/*
787 	 * This next section is modified to allow for NIS passwd file
788 	 * conventions.  In the case where a password entry was being
789 	 * added to the password file, the original AT&T code read
790 	 * the entire password file in, noted any information needed, and
791 	 * copied the entries to a temporary file.  Then the new entry
792 	 * was added to the temporary file, and the temporary file was
793 	 * moved to be the real password file.
794 	 *
795 	 * The problem is, that with NIS compatability, we want to add new
796 	 * entries BEFORE the first NIS-referrant entry, so as not to have
797 	 * any surprises.  To accomplish this without extensively modifying
798 	 * the logic of the code below, as soon as a NIS-referrant entry is
799 	 * found we stop copying entries to the TEMP file and instead we
800 	 * remember
801 	 * the first NIS entry and where we found it, scan the rest of the
802 	 * password file without copying entries, then write the new entry, copy
803 	 * the stored password entry, then copy the rest of the password file.
804 	 */
805 
806 
807 	error = 0;
808 
809 	if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
810 		rid_tmpf();
811 		if (errno == ENOENT)
812 			bad_news();
813 		else
814 			file_error();
815 	}
816 
817 	NIS_entry_seen = 0;
818 	cur_pos = 0;
819 	/* The while loop for reading PASSWD entries */
820 	info_mask |= WRITE_P_ENTRY;
821 
822 	while (!end_of_file) {
823 		pw_ptr1p = fgetpwent(pwf);
824 		if (pw_ptr1p == NULL) {
825 			if (!feof(pwf)) {
826 				/* A real error - report it and exit */
827 				rid_tmpf();
828 				bad_pasf();
829 			}
830 			else
831 				break;
832 		}
833 
834 		if (!NIS_entry_seen)
835 			info_mask |= WRITE_P_ENTRY;
836 		else
837 			info_mask &= ~WRITE_P_ENTRY;
838 
839 		/*
840 		 * Set up the uid usage blocks to find the first
841 		 * available uid above UID_MIN, if needed
842 		 */
843 
844 		if (info_mask & NEED_DEF_UID)
845 			add_uid(pw_ptr1p->pw_uid);
846 
847 		/* Check for unique UID */
848 
849 		if (strcmp(lognamp, pw_ptr1p->pw_name) &&
850 		    (pw_ptr1p->pw_uid == passwd_st.pw_uid) &&
851 		    ((optn_mask & U_MASK) && !(optn_mask & O_MASK))) {
852 			rid_tmpf();		/* get rid of temp files */
853 			bad_uid();
854 		}
855 
856 		/* Check for unique new logname */
857 
858 		if (strcmp(lognamp, pw_ptr1p->pw_name) == 0 &&
859 		    optn_mask & L_MASK &&
860 		    strcmp(pw_ptr1p->pw_name, passwd_st.pw_name) == 0) {
861 			rid_tmpf();
862 #ifdef att
863 			if (!getspnam(pw_ptr1p->pw_name))
864 #else
865 			if (!local_getspnam(pw_ptr1p->pw_name))
866 #endif
867 				bad_pasf();
868 			else
869 				bad_name("logname already exists");
870 		}
871 
872 		if (strcmp(lognamp, pw_ptr1p->pw_name) == 0) {
873 
874 			/* no good if we want to add an existing logname */
875 			if (optn_mask & A_MASK) {
876 				rid_tmpf();
877 #ifdef att
878 				if (!getspnam(lognamp))
879 #else
880 				if (!local_getspnam(lognamp))
881 #endif
882 					bad_pasf();
883 				else
884 					bad_name("name already exists");
885 			}
886 
887 			/* remember we found it */
888 			info_mask |= FOUND;
889 
890 			/* Do not write it out on the fly */
891 			if (optn_mask & D_MASK)
892 				info_mask &= ~WRITE_P_ENTRY;
893 
894 			if (optn_mask & M_MASK) {
895 
896 #ifdef att
897 				if (!getspnam(lognamp))
898 #else
899 				if (!local_getspnam(lognamp))
900 #endif
901 				{
902 					rid_tmpf();
903 					bad_pasf();
904 				}
905 				if (optn_mask & L_MASK)
906 					pw_ptr1p->pw_name = passwd_st.pw_name;
907 
908 				if (optn_mask & U_MASK)
909 					pw_ptr1p->pw_uid = passwd_st.pw_uid;
910 
911 				if (optn_mask & G_MASK)
912 					pw_ptr1p->pw_gid = passwd_st.pw_gid;
913 
914 				if (optn_mask & C_MASK) {
915 					pw_ptr1p->pw_comment =
916 						passwd_st.pw_comment;
917 
918 					pw_ptr1p->pw_gecos =
919 						passwd_st.pw_comment;
920 				}
921 
922 				if (optn_mask & H_MASK)
923 					pw_ptr1p->pw_dir = passwd_st.pw_dir;
924 
925 				if (optn_mask & S_MASK)
926 					pw_ptr1p->pw_shell = passwd_st.pw_shell;
927 				ck_p_sz(pw_ptr1p);  /* check entry size */
928 			}
929 		}
930 
931 		if (optn_mask & A_MASK) {
932 			if (!NIS_entry_seen) {
933 				char	*p;
934 				p  = strchr("+-", pw_ptr1p->pw_name[0]);
935 				if (p != NULL) {
936 					/*
937 					 * Found first NIS entry.
938 					 * so remember it.
939 					 */
940 					NIS_pos = cur_pos;
941 					NIS_entry_seen = 1;
942 					info_mask &= ~WRITE_P_ENTRY;
943 				}
944 				else
945 					cur_pos = ftell(pwf);
946 			}
947 		}
948 
949 		if (info_mask & WRITE_P_ENTRY) {
950 			if (putpwent(pw_ptr1p, fp_ptemp)) {
951 				rid_tmpf();
952 				file_error();
953 			}
954 		}
955 	} /* end-of-while-loop */
956 
957 	if (error >= 1) {
958 		msg = "%s: Bad entry found in /etc/passwd.  Run pwconv.\n";
959 		fprintf(stderr, gettext(msg), prognamp);
960 	}
961 
962 	/* Cannot find the target entry and we are deleting or modifying */
963 
964 	if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
965 		rid_tmpf();
966 #ifdef att
967 		if (getspnam(lognamp) != NULL)
968 #else
969 		if (local_getspnam(lognamp) != NULL)
970 #endif
971 			bad_pasf();
972 		else
973 			bad_name("name does not exist");
974 	}
975 
976 	/* First available uid above UID_MIN is ... */
977 
978 	if (info_mask & NEED_DEF_UID)
979 		passwd_st.pw_uid = uid_sp->high + 1;
980 
981 	/* Write out the added entry now */
982 
983 	if (optn_mask & A_MASK) {
984 		ck_p_sz(&passwd_st);	/* Check entry size */
985 		if (putpwent(&passwd_st, fp_ptemp)) {
986 			rid_tmpf();
987 			file_error();
988 		}
989 		/*
990 		 * Now put out the rest of the password file, if needed.
991 		 */
992 		if (NIS_entry_seen) {
993 			int n;
994 			char buf[1024];
995 
996 			if (fseek(pwf, NIS_pos, SEEK_SET) < 0) {
997 				rid_tmpf();
998 				file_error();
999 			}
1000 			while ((n = fread(buf, sizeof (char), 1024, pwf)) > 0) {
1001 				if (fwrite(buf, sizeof (char), n, fp_ptemp)
1002 					!= n) {
1003 					rid_tmpf();
1004 					file_error();
1005 				}
1006 			}
1007 		}
1008 	}
1009 
1010 	(void) fclose(pwf);
1011 
1012 	/* flush and sync the file before closing it */
1013 	if (fflush(fp_ptemp) != 0 || fsync(fd_ptemp) != 0)
1014 		file_error();
1015 
1016 	/* Now we are done with PASSWD */
1017 	(void) fclose(fp_ptemp);
1018 
1019 	/* Do this if we are touching both password files */
1020 
1021 
1022 	if (info_mask & BOTH_FILES) {
1023 		info_mask &= ~FOUND;		/* Reset FOUND flag */
1024 
1025 		/* The while loop for reading SHADOW entries */
1026 		info_mask |= WRITE_S_ENTRY;
1027 
1028 		end_of_file = 0;
1029 		errno = 0;
1030 		error = 0;
1031 
1032 		NIS_entry_seen = 0;
1033 		cur_pos = 0;
1034 
1035 		if ((spf = fopen("/etc/shadow", "r")) == NULL) {
1036 			rid_tmpf();
1037 			file_error();
1038 		}
1039 
1040 		while (!end_of_file) {
1041 			sp_ptr1p = fgetspent(spf);
1042 			if (sp_ptr1p == NULL) {
1043 				if (!feof(spf)) {
1044 					rid_tmpf();
1045 					bad_pasf();
1046 				}
1047 				else
1048 					break;
1049 			}
1050 
1051 			if (!NIS_entry_seen)
1052 				info_mask |= WRITE_S_ENTRY;
1053 			else
1054 				info_mask &= ~WRITE_S_ENTRY;
1055 
1056 			/*
1057 			 * See if the new logname already exist in the
1058 			 * shadow passwd file
1059 			 */
1060 			if ((optn_mask & M_MASK) &&
1061 			    strcmp(lognamp, shadow_st.sp_namp) != 0 &&
1062 			    strcmp(sp_ptr1p->sp_namp, shadow_st.sp_namp) == 0) {
1063 				rid_tmpf();
1064 				bad_pasf();
1065 			}
1066 
1067 			if (strcmp(lognamp, sp_ptr1p->sp_namp) == 0) {
1068 				info_mask |= FOUND;
1069 				if (optn_mask & A_MASK) {
1070 					/* password file inconsistent */
1071 					rid_tmpf();
1072 					bad_pasf();
1073 				}
1074 
1075 				if (optn_mask & M_MASK) {
1076 					sp_ptr1p->sp_namp = shadow_st.sp_namp;
1077 					if (F_MASK & optn_mask)
1078 						sp_ptr1p->sp_inact =
1079 							shadow_st.sp_inact;
1080 					if (E_MASK & optn_mask)
1081 						sp_ptr1p->sp_expire =
1082 							shadow_st.sp_expire;
1083 
1084 					ck_s_sz(sp_ptr1p);
1085 				}
1086 
1087 				if (optn_mask & D_MASK)
1088 					info_mask &= ~WRITE_S_ENTRY;
1089 			}
1090 
1091 			if (optn_mask & A_MASK) {
1092 				if (!NIS_entry_seen) {
1093 					char	*p;
1094 					p = strchr("+-", sp_ptr1p->sp_namp[0]);
1095 					if (p != NULL) {
1096 						/*
1097 						 * Found first NIS entry.
1098 						 * so remember it.
1099 						 */
1100 						NIS_pos = cur_pos;
1101 						NIS_entry_seen = 1;
1102 						info_mask &= ~WRITE_S_ENTRY;
1103 					}
1104 					else
1105 						cur_pos = ftell(spf);
1106 				}
1107 			}
1108 
1109 			if (info_mask & WRITE_S_ENTRY) {
1110 				if (putspent(sp_ptr1p, fp_stemp)) {
1111 					rid_tmpf();
1112 					file_error();
1113 				}
1114 			}
1115 
1116 		} /* end-of-while-loop */
1117 
1118 		if (error >= 1) {
1119 
1120 			msg = BAD_ENT_MESSAGE;
1121 			fprintf(stderr, gettext(msg), prognamp);
1122 		}
1123 
1124 		/*
1125 		 * If we cannot find the entry and we are deleting or
1126 		 *  modifying
1127 		 */
1128 
1129 		if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
1130 			rid_tmpf();
1131 			bad_pasf();
1132 		}
1133 
1134 		if (optn_mask & A_MASK) {
1135 			ck_s_sz(&shadow_st);
1136 			if (putspent(&shadow_st, fp_stemp)) {
1137 				rid_tmpf();
1138 				file_error();
1139 			}
1140 
1141 			/*
1142 			 * Now put out the rest of the shadow file, if needed.
1143 			 */
1144 			if (NIS_entry_seen) {
1145 				file_copy(spf, NIS_pos);
1146 			}
1147 		}
1148 
1149 		/* flush and sync the file before closing it */
1150 		if (fflush(fp_stemp) != 0 || fsync(fd_stemp) != 0)
1151 			file_error();
1152 		(void) fclose(fp_stemp);
1153 
1154 		/* Done with SHADOW */
1155 		(void) fclose(spf);
1156 
1157 	} /* End of if info_mask */
1158 
1159 	if (info_mask & UATTR_FILE) {
1160 		info_mask &= ~FOUND;		/* Reset FOUND flag */
1161 
1162 		/* The while loop for reading USER_ATTR entries */
1163 		info_mask |= WRITE_S_ENTRY;
1164 
1165 		end_of_file = 0;
1166 		errno = 0;
1167 		error = 0;
1168 
1169 		NIS_entry_seen = 0;
1170 		cur_pos = 0;
1171 
1172 		if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
1173 			rid_tmpf();
1174 			file_error();
1175 		}
1176 
1177 		while (!end_of_file) {
1178 			ua_ptr1p = fgetuserattr(uaf);
1179 			if (ua_ptr1p == NULL) {
1180 				if (!feof(uaf)) {
1181 					rid_tmpf();
1182 					bad_uattr();
1183 				}
1184 				else
1185 					break;
1186 			}
1187 
1188 			if (ua_ptr1p->name[0] == '#') {
1189 				/*
1190 				 * If this is a comment, write it back as it
1191 				 * is.
1192 				 */
1193 				if (ua_ptr1p->qualifier[0] == '\0' &&
1194 				    ua_ptr1p->res1[0] == '\0' &&
1195 				    ua_ptr1p->res2[0] == '\0' &&
1196 				    (ua_ptr1p->attr == NULL ||
1197 				    ua_ptr1p->attr->length == 0))
1198 					(void) fprintf(fp_uatemp, "%s\n",
1199 					    ua_ptr1p->name);
1200 				else
1201 					/*
1202 					 * This is a commented user_attr entry;
1203 					 * reformat it, and write it back.
1204 					 */
1205 					putuserattrent(ua_ptr1p, fp_uatemp);
1206 				free_userattr(ua_ptr1p);
1207 				continue;
1208 			}
1209 
1210 			if (!NIS_entry_seen)
1211 				info_mask |= WRITE_S_ENTRY;
1212 			else
1213 				info_mask &= ~WRITE_S_ENTRY;
1214 
1215 			/*
1216 			 * See if the new logname already exist in the
1217 			 * user_attr file
1218 			 */
1219 			if ((optn_mask & M_MASK) &&
1220 			    strcmp(lognamp, userattr_st.name) != 0 &&
1221 			    strcmp(ua_ptr1p->name, userattr_st.name) == 0) {
1222 				rid_tmpf();
1223 				bad_pasf();
1224 			}
1225 
1226 			if (strcmp(lognamp, ua_ptr1p->name) == 0) {
1227 				info_mask |= FOUND;
1228 				if (optn_mask & A_MASK) {
1229 					/* password file inconsistent */
1230 					rid_tmpf();
1231 					bad_pasf();
1232 				}
1233 
1234 				if (optn_mask & M_MASK) {
1235 					int	j;
1236 					char	*value;
1237 
1238 					for (j = 0; j < UA_KEYS; j++) {
1239 						if (ua_opts[j].newvalue != NULL)
1240 							continue;
1241 						value =
1242 						    kva_match(ua_ptr1p->attr,
1243 							(char *)ua_opts[j].key);
1244 						if (value == NULL)
1245 							continue;
1246 						assign_attr(&userattr_st,
1247 						    ua_opts[j].key,
1248 						    value);
1249 					}
1250 					free_userattr(ua_ptr1p);
1251 					ua_ptr1p = &userattr_st;
1252 				}
1253 
1254 				if (optn_mask & D_MASK)
1255 					info_mask &= ~WRITE_S_ENTRY;
1256 			} else if (optn_mask & D_MASK) {
1257 				char *rolelist;
1258 
1259 				rolelist = kva_match(ua_ptr1p->attr,
1260 				    USERATTR_ROLES_KW);
1261 				if (rolelist) {
1262 					unassign_role(ua_ptr1p,
1263 					    rolelist, lognamp);
1264 				}
1265 			}
1266 
1267 			if (info_mask & WRITE_S_ENTRY) {
1268 				putuserattrent(ua_ptr1p, fp_uatemp);
1269 			}
1270 
1271 			if (!(optn_mask & M_MASK))
1272 				free_userattr(ua_ptr1p);
1273 		} /* end-of-while-loop */
1274 
1275 		if (error >= 1) {
1276 
1277 			msg = BAD_ENT_MESSAGE;
1278 			fprintf(stderr, gettext(msg), prognamp);
1279 		}
1280 
1281 		/*
1282 		 * Add entry in user_attr if masks is UATTR_MASK
1283 		 * We don't need to do anything for L_MASK if there's
1284 		 * no user_attr entry for the user being modified.
1285 		 */
1286 		if (!(info_mask & FOUND) && !(L_MASK & optn_mask) &&
1287 		    !(D_MASK & optn_mask)) {
1288 			putuserattrent(&userattr_st, fp_uatemp);
1289 		}
1290 
1291 		/* flush and sync the file before closing it */
1292 		if (fflush(fp_uatemp) != 0 || fsync(fd_uatemp) != 0)
1293 			file_error();
1294 		(void) fclose(fp_uatemp);
1295 
1296 		/* Done with USERATTR */
1297 		(void) fclose(uaf);
1298 
1299 	} /* End of if info_mask */
1300 	/* ignore all signals */
1301 
1302 	for (i = 1; i < NSIG; i++)
1303 		(void) sigset(i, SIG_IGN);
1304 
1305 	errno = 0;		/* For correcting sigset to SIGKILL */
1306 
1307 	if (unlink(OPASSWD) && access(OPASSWD, 0) == 0)
1308 		file_error();
1309 
1310 	if (link(PASSWD, OPASSWD) == -1)
1311 			file_error();
1312 
1313 
1314 	if (rename(PASSTEMP, PASSWD) == -1) {
1315 		if (link(OPASSWD, PASSWD))
1316 			bad_news();
1317 		file_error();
1318 	}
1319 
1320 
1321 	if (info_mask & BOTH_FILES) {
1322 
1323 		if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
1324 			if (rec_pwd())
1325 				bad_news();
1326 			else
1327 				file_error();
1328 		}
1329 
1330 		if (link(SHADOW, OSHADOW) == -1) {
1331 			if (rec_pwd())
1332 				bad_news();
1333 			else
1334 				file_error();
1335 		}
1336 
1337 
1338 		if (rename(SHADTEMP, SHADOW) == -1) {
1339 			if (rename(OSHADOW, SHADOW) == -1)
1340 				bad_news();
1341 
1342 			if (rec_pwd())
1343 				bad_news();
1344 			else
1345 				file_error();
1346 		}
1347 
1348 	}
1349 	if (info_mask & UATTR_FILE) {
1350 		if (unlink(OUSERATTR_FILENAME) &&
1351 		    access(OUSERATTR_FILENAME, 0) == 0) {
1352 			if (rec_pwd())
1353 				bad_news();
1354 			else
1355 				file_error();
1356 		}
1357 
1358 		if (link(USERATTR_FILENAME, OUSERATTR_FILENAME) == -1) {
1359 			if (rec_pwd())
1360 				bad_news();
1361 			else
1362 				file_error();
1363 		}
1364 
1365 
1366 		if (rename(USERATTR_TEMP, USERATTR_FILENAME) == -1) {
1367 			if (rename(OUSERATTR_FILENAME, USERATTR_FILENAME) == -1)
1368 				bad_news();
1369 
1370 			if (rec_pwd())
1371 				bad_news();
1372 			else
1373 				file_error();
1374 		}
1375 
1376 	}
1377 
1378 	ulckpwdf();
1379 
1380 	/*
1381 	 * Return 0 status, indicating success
1382 	 */
1383 	return (0);
1384 
1385 }  /* end of main */
1386 
1387 /* Try to recover the old password file */
1388 
1389 int
1390 rec_pwd(void)
1391 {
1392 	if (unlink(PASSWD) || link(OPASSWD, PASSWD))
1393 		return (-1);
1394 
1395 	return (0);
1396 }
1397 
1398 /* combine two uid_blk's */
1399 
1400 void
1401 uid_bcom(struct uid_blk *uid_p)
1402 {
1403 	struct uid_blk *uid_tp;
1404 
1405 	uid_tp = uid_p->link;
1406 	uid_p->high = uid_tp->high;
1407 	uid_p->link = uid_tp->link;
1408 
1409 	free(uid_tp);
1410 }
1411 
1412 /* add a new uid_blk */
1413 
1414 void
1415 add_ublk(uid_t num, struct uid_blk *uid_p)
1416 {
1417 	struct uid_blk *uid_tp;
1418 
1419 	uid_tp = malloc(sizeof (struct uid_blk));
1420 	if (uid_tp == NULL) {
1421 		rid_tmpf();
1422 		file_error();
1423 	}
1424 
1425 	uid_tp->high = uid_tp->low = num;
1426 	uid_tp->link = uid_p->link;
1427 	uid_p->link = uid_tp;
1428 }
1429 
1430 /*
1431  *	Here we are using a linked list of uid_blk to keep track of all
1432  *	the used uids.	Each uid_blk represents a range of used uid,
1433  *	with low represents the low inclusive end and high represents
1434  *	the high inclusive end.  In the beginning, we initialize a linked
1435  *	list of one uid_blk with low = high = (UID_MIN-1).  This was
1436  *	done in main().
1437  *	Each time we read in another used uid, we add it onto the linked
1438  *	list by either making a new uid_blk, decrementing the low of
1439  *	an existing uid_blk, incrementing the high of an existing
1440  *	uid_blk, or combining two existing uid_blks.  After we finished
1441  *	building this linked list, the first available uid above or
1442  *	equal to UID_MIN is the high of the first uid_blk in the linked
1443  *	list + 1.
1444  */
1445 /* add_uid() adds uid to the link list of used uids */
1446 void
1447 add_uid(uid_t uid)
1448 {
1449 	struct uid_blk *uid_p;
1450 	/* Only keep track of the ones above UID_MIN */
1451 
1452 	if (uid >= UID_MIN) {
1453 		uid_p = uid_sp;
1454 
1455 		while (uid_p != NULL) {
1456 
1457 			if (uid_p->link != NULL) {
1458 
1459 				if (uid >= uid_p->link->low)
1460 					uid_p = uid_p->link;
1461 
1462 				else if (uid >= uid_p->low &&
1463 						uid <= uid_p->high) {
1464 					uid_p = NULL;
1465 				}
1466 
1467 				else if (uid == (uid_p->high+1)) {
1468 
1469 					if (++uid_p->high ==
1470 						(uid_p->link->low - 1)) {
1471 						uid_bcom(uid_p);
1472 					}
1473 					uid_p = NULL;
1474 				}
1475 
1476 				else if (uid == (uid_p->link->low - 1)) {
1477 					uid_p->link->low --;
1478 					uid_p = NULL;
1479 				}
1480 
1481 				else if (uid < uid_p->link->low) {
1482 					add_ublk(uid, uid_p);
1483 					uid_p = NULL;
1484 				}
1485 			} /* if uid_p->link */
1486 
1487 			else {
1488 
1489 				if (uid == (uid_p->high + 1)) {
1490 					uid_p->high++;
1491 					uid_p = NULL;
1492 				} else if (uid >= uid_p->low &&
1493 					uid <= uid_p->high) {
1494 					uid_p = NULL;
1495 				} else {
1496 					add_ublk(uid, uid_p);
1497 					uid_p = NULL;
1498 				}
1499 			} /* else */
1500 		} /* while uid_p */
1501 
1502 	} /* if uid */
1503 }
1504 
1505 void
1506 bad_perm(void)
1507 {
1508 	(void) fprintf(stderr, gettext("%s: Permission denied\n"), prognamp);
1509 	exit(1);
1510 }
1511 
1512 void
1513 bad_usage(char *sp)
1514 {
1515 	if (strlen(sp) != 0)
1516 		(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(sp));
1517 	(void) fprintf(stderr, gettext("Usage:\n\
1518 %s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
1519 	    [-s shell] [-f inactive] [-e expire] name\n\
1520 %s -m  -c comment | -h homedir | -u uid [-o] | -g gid |\n\
1521 	    -s shell | -f inactive | -e expire	|  -l logname  name\n\
1522 %s -d name\n"), prognamp, prognamp, prognamp);
1523 	if (info_mask & LOCKED)
1524 		ulckpwdf();
1525 	exit(2);
1526 }
1527 
1528 void
1529 bad_arg(char *s)
1530 {
1531 	(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1532 
1533 	if (info_mask & LOCKED)
1534 		ulckpwdf();
1535 	exit(3);
1536 }
1537 
1538 void
1539 bad_name(char *s)
1540 {
1541 	(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1542 	ulckpwdf();
1543 	exit(9);
1544 }
1545 
1546 void
1547 bad_uid(void)
1548 {
1549 	(void) fprintf(stderr, gettext("%s: UID in use\n"), prognamp);
1550 
1551 	ulckpwdf();
1552 	exit(4);
1553 }
1554 
1555 void
1556 bad_pasf(void)
1557 {
1558 	msg = "%s: Inconsistent password files\n";
1559 	(void) fprintf(stderr, gettext(msg), prognamp);
1560 
1561 	ulckpwdf();
1562 	exit(5);
1563 }
1564 
1565 void
1566 bad_uattr(void)
1567 {
1568 	msg = "%s: Bad user_attr database\n";
1569 	(void) fprintf(stderr, gettext(msg), prognamp);
1570 
1571 	ulckpwdf();
1572 	exit(5);
1573 }
1574 
1575 void
1576 file_error(void)
1577 {
1578 	msg = "%s: Unexpected failure.	Password files unchanged\n";
1579 	(void) fprintf(stderr, gettext(msg), prognamp);
1580 
1581 	ulckpwdf();
1582 	exit(6);
1583 }
1584 
1585 void
1586 bad_news(void)
1587 {
1588 	msg = "%s: Unexpected failure.	Password file(s) missing\n";
1589 	(void) fprintf(stderr, gettext(msg), prognamp);
1590 
1591 	ulckpwdf();
1592 	exit(7);
1593 }
1594 
1595 void
1596 no_lock(void)
1597 {
1598 	msg = "%s: Password file(s) busy.  Try again later\n";
1599 	(void) fprintf(stderr, gettext(msg), prognamp);
1600 
1601 	exit(8);
1602 }
1603 
1604 /* Check for the size of the whole passwd entry */
1605 void
1606 ck_p_sz(struct passwd *pwp)
1607 {
1608 	char ctp[128];
1609 
1610 	/* Ensure that the combined length of the individual */
1611 	/* fields will fit in a passwd entry. The 1 accounts for the */
1612 	/* newline and the 6 accounts for the colons (:'s) */
1613 	if (((int)strlen(pwp->pw_name) + 1 +
1614 		sprintf(ctp, "%d", pwp->pw_uid) +
1615 		sprintf(ctp, "%d", pwp->pw_gid) +
1616 		(int)strlen(pwp->pw_comment) +
1617 		(int)strlen(pwp->pw_dir)	+
1618 		(int)strlen(pwp->pw_shell) + 6) > (ENTRY_LENGTH-1)) {
1619 		rid_tmpf();
1620 		bad_arg("New password entry too long");
1621 	}
1622 }
1623 
1624 /* Check for the size of the whole passwd entry */
1625 void
1626 ck_s_sz(struct spwd *ssp)
1627 {
1628 	char ctp[128];
1629 
1630 	/* Ensure that the combined length of the individual */
1631 	/* fields will fit in a shadow entry. The 1 accounts for the */
1632 	/* newline and the 7 accounts for the colons (:'s) */
1633 	if (((int)strlen(ssp->sp_namp) + 1 +
1634 		(int)strlen(ssp->sp_pwdp) +
1635 		sprintf(ctp, "%d", ssp->sp_lstchg) +
1636 		sprintf(ctp, "%d", ssp->sp_min) +
1637 		sprintf(ctp, "%d", ssp->sp_max) +
1638 		sprintf(ctp, "%d", ssp->sp_warn) +
1639 		sprintf(ctp, "%d", ssp->sp_inact) +
1640 		sprintf(ctp, "%d", ssp->sp_expire) + 7) > (ENTRY_LENGTH - 1)) {
1641 		rid_tmpf();
1642 		bad_arg("New password entry too long");
1643 	}
1644 }
1645 
1646 /* Get rid of the temp files */
1647 void
1648 rid_tmpf(void)
1649 {
1650 	(void) fclose(fp_ptemp);
1651 
1652 	if (unlink(PASSTEMP)) {
1653 		msg = "%s: warning: cannot unlink %s\n";
1654 		(void) fprintf(stderr, gettext(msg), prognamp, PASSTEMP);
1655 	}
1656 
1657 	if (info_mask & BOTH_FILES) {
1658 		(void) fclose(fp_stemp);
1659 
1660 		if (unlink(SHADTEMP)) {
1661 			msg = "%s: warning: cannot unlink %s\n";
1662 			(void) fprintf(stderr, gettext(msg), prognamp,
1663 				SHADTEMP);
1664 		}
1665 	}
1666 
1667 	if (info_mask & UATTR_FILE) {
1668 		(void) fclose(fp_uatemp);
1669 
1670 		if (unlink(USERATTR_TEMP)) {
1671 			msg = "%s: warning: cannot unlink %s\n";
1672 			(void) fprintf(stderr, gettext(msg), prognamp,
1673 				USERATTR_TEMP);
1674 		}
1675 	}
1676 }
1677 
1678 void
1679 file_copy(FILE *spf, long NIS_pos)
1680 {
1681 	int n;
1682 	char buf[1024];
1683 
1684 	if (fseek(spf, NIS_pos, SEEK_SET) < 0) {
1685 		rid_tmpf();
1686 		file_error();
1687 	}
1688 	while ((n = fread(buf, sizeof (char), 1024, spf)) > 0) {
1689 		if (fwrite(buf, sizeof (char), n, fp_stemp) != n) {
1690 			rid_tmpf();
1691 			file_error();
1692 		}
1693 	}
1694 }
1695