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