xref: /illumos-gate/usr/src/cmd/passmgmt/passmgmt.c (revision 379728489ed47862c4927c75771e767b9476c9c4)
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  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved	*/
28 
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <shadow.h>
32 #include <pwd.h>
33 #include <string.h>
34 #include <signal.h>
35 #include <sys/stat.h>
36 #include <errno.h>
37 #include <time.h>
38 #include <unistd.h>
39 #include <stdlib.h>
40 #include <locale.h>
41 #include <fcntl.h>
42 #include <secdb.h>
43 #include <user_attr.h>
44 #include <nss.h>
45 
46 #define	CMT_SIZE	(128+1)	/* Argument sizes + 1 (for '\0') */
47 #define	DIR_SIZE	(256+1)
48 #define	SHL_SIZE	(256+1)
49 #define	ENTRY_LENGTH	512	/* Max length of an /etc/passwd entry */
50 #define	UID_MIN		100	/* Lower bound of default UID */
51 
52 #define	M_MASK		01	/* Masks for the optn_mask variable */
53 #define	L_MASK		02	/* It keeps track of which options   */
54 #define	C_MASK		04	/* have been entered */
55 #define	H_MASK		010
56 #define	U_MASK		020
57 #define	G_MASK		040
58 #define	S_MASK		0100
59 #define	O_MASK		0200
60 #define	A_MASK		0400
61 #define	D_MASK		01000
62 #define	F_MASK		02000
63 #define	E_MASK		04000
64 
65 #define	UATTR_MASK	010000
66 
67 					/* flags for info_mask */
68 #define	LOGNAME_EXIST	01		/* logname exists */
69 #define	BOTH_FILES	02		/* touch both password files */
70 #define	WRITE_P_ENTRY	04		/* write out password entry */
71 #define	WRITE_S_ENTRY	010		/* write out shadow entry */
72 #define	NEED_DEF_UID	020		/* need default uid */
73 #define	FOUND		040		/* found the entry in password file */
74 #define	LOCKED		0100		/* did we lock the password file */
75 #define	UATTR_FILE	0200		/* touch user_attr file */
76 #define	BAD_ENT_MESSAGE	"%s: Bad entry found in /etc/passwd.  Run pwconv.\n"
77 
78 typedef struct kvopts {
79 	const char	option;
80 	const char	*key;
81 	char		*newvalue;
82 } kvopts_t;
83 
84 /* mapping of extensible keywords and options */
85 kvopts_t ua_opts[] =  {
86 { 'A',	USERATTR_AUTHS_KW },
87 { 'P',	USERATTR_PROFILES_KW },
88 { 'R',	USERATTR_ROLES_KW },
89 { 'T',	USERATTR_TYPE_KW },
90 { '\0', USERATTR_DEFAULTPROJ_KW },
91 { '\0',	USERATTR_LIMPRIV_KW },
92 { '\0',	USERATTR_DFLTPRIV_KW },
93 { '\0', USERATTR_LOCK_AFTER_RETRIES_KW },
94 { '\0', USERATTR_ROLEAUTH_KW },
95 { '\0', USERATTR_LABELVIEW },
96 { '\0', USERATTR_CLEARANCE },
97 { '\0', USERATTR_MINLABEL },
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 
225 	int		i;
226 	char		*key;
227 	kv_t		*kv_pair;
228 	int		avail = -1;
229 
230 	if (user->attr != NULL) {
231 		kv_pair = user->attr->data;
232 		for (i = 0; i < user->attr->length; i++) {
233 			key = kv_pair[i].key;
234 			if (key == NULL) {
235 				avail = i;
236 				continue;
237 			} else if (strcmp(key, newkey) == 0) {
238 				kv_pair[i].value = strdup(val);
239 				return;
240 			}
241 		}
242 
243 		if (avail == -1)
244 			avail = user->attr->length++;
245 		kv_pair[avail].key = strdup(newkey);
246 		kv_pair[avail].value = strdup(val);
247 	}
248 }
249 
250 static void
251 unassign_role(userattr_t *user, char *rolelist, char *role)
252 {
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 =
555 					    _escape(char_p, KV_SPECIAL);
556 					assign_attr(&userattr_st, optarg,
557 					    char_p);
558 					break;
559 				}
560 			}
561 			if (i == UA_KEYS)
562 				bad_usage("bad key");
563 			optn_mask |= UATTR_MASK;
564 			break;
565 
566 		case '?' :
567 
568 			bad_usage("");
569 			break;
570 
571 		default :
572 			/* Extended User Attributes */
573 			{
574 				int j;
575 
576 				for (j = 0; j < UA_KEYS; j++) {
577 					if (ua_opts[j].option == (char)c) {
578 						if ((D_MASK) & optn_mask)
579 							bad_usage("Invalid "
580 							"combination of "
581 							" options");
582 						optn_mask |= UATTR_MASK;
583 						assign_attr(&userattr_st,
584 						    ua_opts[j].key,
585 						    _escape(optarg,
586 						    KV_SPECIAL));
587 						ua_opts[j].newvalue =
588 						    _escape(optarg, KV_SPECIAL);
589 						break;
590 					}
591 				}
592 				break;
593 			}
594 		}
595 	}
596 
597 	/* check command syntax for the following errors */
598 	/* too few or too many arguments */
599 	/* no -a -m or -d option */
600 	/* -o without -u */
601 	/* -m with no other option */
602 
603 	if (optind == argc || argc > (optind+1) ||
604 	    !((A_MASK|M_MASK|D_MASK) & optn_mask) ||
605 	    ((optn_mask & O_MASK) && !(optn_mask & U_MASK)) ||
606 	    ((optn_mask & M_MASK) &&
607 	    !(optn_mask &
608 	    (L_MASK|C_MASK|H_MASK|U_MASK|G_MASK|S_MASK|F_MASK|
609 	    E_MASK|UATTR_MASK))))
610 		bad_usage("Invalid command syntax");
611 
612 	/* null string argument or bad characters ? */
613 	if ((strlen(argv[optind]) == 0) || strpbrk(argv[optind], ":\n"))
614 		bad_arg("Invalid name");
615 
616 	lognamp = argv [optind];
617 
618 	/*
619 	 * if we are adding a new user or modifying an existing user
620 	 * (not the logname), then copy logname into the two data
621 	 *  structures
622 	 */
623 
624 	if ((A_MASK & optn_mask) ||
625 	    ((M_MASK & optn_mask) && !(optn_mask & L_MASK))) {
626 		passwd_st.pw_name = argv [optind];
627 		shadow_st.sp_namp = argv [optind];
628 		userattr_st.name = argv [optind];
629 	}
630 
631 	/* Put in directory if we are adding and we need a default */
632 
633 	if (!(optn_mask & H_MASK) && (optn_mask & A_MASK)) {
634 		if ((passwd_st.pw_dir = malloc((size_t)DIR_SIZE)) == NULL)
635 			file_error();
636 
637 		*passwd_st.pw_dir = '\0';
638 		(void) strcat(passwd_st.pw_dir, defdir);
639 		(void) strcat(passwd_st.pw_dir, lognamp);
640 	}
641 
642 	/* Check the number of password files we are touching */
643 
644 	if ((!((M_MASK & optn_mask) && !(L_MASK & optn_mask))) ||
645 	    ((M_MASK & optn_mask) && ((E_MASK & optn_mask) ||
646 	    (F_MASK & optn_mask))))
647 		info_mask |= BOTH_FILES;
648 
649 	if ((D_MASK|L_MASK|UATTR_MASK) & optn_mask)
650 		info_mask |= UATTR_FILE;
651 
652 	/* Open the temporary file(s) with appropriate permission mask */
653 	/* and the appropriate owner */
654 
655 	if (stat(PASSWD, &statbuf) < 0)
656 		file_error();
657 
658 	fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
659 	if (fd_ptemp == -1) {
660 		if (errno == EEXIST) {
661 			if (unlink(PASSTEMP)) {
662 				msg = "%s: warning: cannot unlink %s\n";
663 				(void) fprintf(stderr, gettext(msg), prognamp,
664 				    PASSTEMP);
665 			}
666 			fd_ptemp = open(PASSTEMP, O_CREAT|O_EXCL|O_WRONLY,
667 			    statbuf.st_mode);
668 			if (fd_ptemp == -1) {
669 				file_error();
670 			}
671 
672 		} else
673 			file_error();
674 	}
675 	fp_ptemp = fdopen(fd_ptemp, "w");
676 	if (fp_ptemp == NULL)
677 		file_error();
678 	error = fchown(fd_ptemp, statbuf.st_uid, statbuf.st_gid);
679 	if (error == 0)
680 		error = fchmod(fd_ptemp, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
681 	if (error != 0) {
682 		(void) fclose(fp_ptemp);
683 		if (unlink(PASSTEMP)) {
684 			msg = "%s: warning: cannot unlink %s\n";
685 			(void) fprintf(stderr, gettext(msg), prognamp,
686 			    PASSTEMP);
687 		}
688 		file_error();
689 	}
690 
691 	if (info_mask & BOTH_FILES) {
692 		if (stat(SHADOW, &statbuf) < 0) {
693 			rid_tmpf();
694 			file_error();
695 		}
696 		fd_stemp = open(SHADTEMP, O_CREAT|O_EXCL|O_WRONLY,
697 		    statbuf.st_mode);
698 		if (fd_stemp == -1) {
699 			if (errno == EEXIST) {
700 				if (unlink(SHADTEMP)) {
701 					msg = "%s: warning: cannot unlink %s\n";
702 					(void) fprintf(stderr, gettext(msg),
703 					    prognamp, SHADTEMP);
704 				}
705 				fd_stemp = open(SHADTEMP,
706 				    O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
707 				if (fd_stemp == -1) {
708 					rid_tmpf();
709 					file_error();
710 				}
711 
712 			} else {
713 				rid_tmpf();
714 				file_error();
715 			}
716 		}
717 		fp_stemp = fdopen(fd_stemp, "w");
718 		if (fp_stemp == NULL) {
719 			rid_tmpf();
720 			file_error();
721 		}
722 		error = fchown(fd_stemp, statbuf.st_uid, statbuf.st_gid);
723 		if (error == 0)
724 			error = fchmod(fd_stemp, S_IRUSR);
725 		if (error != 0) {
726 			rid_tmpf();
727 			file_error();
728 		}
729 	}
730 
731 	if (info_mask & UATTR_FILE) {
732 		if (stat(USERATTR_FILENAME, &statbuf) < 0) {
733 			rid_tmpf();
734 			file_error();
735 		}
736 		fd_uatemp = open(USERATTR_TEMP, O_CREAT|O_EXCL|O_WRONLY,
737 		    statbuf.st_mode);
738 		if (fd_uatemp == -1) {
739 			if (errno == EEXIST) {
740 				if (unlink(USERATTR_TEMP)) {
741 					msg = "%s: warning: cannot unlink %s\n";
742 					(void) fprintf(stderr, gettext(msg),
743 					    prognamp, USERATTR_TEMP);
744 				}
745 				fd_uatemp = open(USERATTR_TEMP,
746 				    O_CREAT|O_EXCL|O_WRONLY, statbuf.st_mode);
747 				if (fd_uatemp == -1) {
748 					rid_tmpf();
749 					file_error();
750 				}
751 
752 			} else {
753 				rid_tmpf();
754 				file_error();
755 			}
756 		}
757 		fp_uatemp = fdopen(fd_uatemp, "w");
758 		if (fp_uatemp == NULL) {
759 			rid_tmpf();
760 			file_error();
761 		}
762 		error = fchown(fd_uatemp, statbuf.st_uid, statbuf.st_gid);
763 		if (error == 0)
764 			error = fchmod(fd_uatemp,
765 			    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
766 		if (error != 0) {
767 			rid_tmpf();
768 			file_error();
769 		}
770 	}
771 	/* Default uid needed ? */
772 
773 	if (!(optn_mask & U_MASK) && (optn_mask & A_MASK)) {
774 		/* mark it in the information mask */
775 		info_mask |= NEED_DEF_UID;
776 
777 		/* create the head of the uid number list */
778 		uid_sp = malloc(sizeof (struct uid_blk));
779 		if (uid_sp == NULL) {
780 			rid_tmpf();
781 			file_error();
782 		}
783 
784 		uid_sp->link = NULL;
785 		uid_sp->low = (UID_MIN -1);
786 		uid_sp->high = (UID_MIN -1);
787 	}
788 
789 	/*
790 	 * This next section is modified to allow for NIS passwd file
791 	 * conventions.  In the case where a password entry was being
792 	 * added to the password file, the original AT&T code read
793 	 * the entire password file in, noted any information needed, and
794 	 * copied the entries to a temporary file.  Then the new entry
795 	 * was added to the temporary file, and the temporary file was
796 	 * moved to be the real password file.
797 	 *
798 	 * The problem is, that with NIS compatability, we want to add new
799 	 * entries BEFORE the first NIS-referrant entry, so as not to have
800 	 * any surprises.  To accomplish this without extensively modifying
801 	 * the logic of the code below, as soon as a NIS-referrant entry is
802 	 * found we stop copying entries to the TEMP file and instead we
803 	 * remember
804 	 * the first NIS entry and where we found it, scan the rest of the
805 	 * password file without copying entries, then write the new entry, copy
806 	 * the stored password entry, then copy the rest of the password file.
807 	 */
808 
809 
810 	error = 0;
811 
812 	if ((pwf = fopen("/etc/passwd", "r")) == NULL) {
813 		rid_tmpf();
814 		if (errno == ENOENT)
815 			bad_news();
816 		else
817 			file_error();
818 	}
819 
820 	NIS_entry_seen = 0;
821 	cur_pos = 0;
822 	/* The while loop for reading PASSWD entries */
823 	info_mask |= WRITE_P_ENTRY;
824 
825 	while (!end_of_file) {
826 		pw_ptr1p = fgetpwent(pwf);
827 		if (pw_ptr1p == NULL) {
828 			if (!feof(pwf)) {
829 				/* A real error - report it and exit */
830 				rid_tmpf();
831 				bad_pasf();
832 			}
833 			else
834 				break;
835 		}
836 
837 		if (!NIS_entry_seen)
838 			info_mask |= WRITE_P_ENTRY;
839 		else
840 			info_mask &= ~WRITE_P_ENTRY;
841 
842 		/*
843 		 * Set up the uid usage blocks to find the first
844 		 * available uid above UID_MIN, if needed
845 		 */
846 
847 		if (info_mask & NEED_DEF_UID)
848 			add_uid(pw_ptr1p->pw_uid);
849 
850 		/* Check for unique UID */
851 
852 		if (strcmp(lognamp, pw_ptr1p->pw_name) &&
853 		    (pw_ptr1p->pw_uid == passwd_st.pw_uid) &&
854 		    ((optn_mask & U_MASK) && !(optn_mask & O_MASK))) {
855 			rid_tmpf();		/* get rid of temp files */
856 			bad_uid();
857 		}
858 
859 		/* Check for unique new logname */
860 
861 		if (strcmp(lognamp, pw_ptr1p->pw_name) == 0 &&
862 		    optn_mask & L_MASK &&
863 		    strcmp(pw_ptr1p->pw_name, passwd_st.pw_name) == 0) {
864 			rid_tmpf();
865 #ifdef att
866 			if (!getspnam(pw_ptr1p->pw_name))
867 #else
868 			if (!local_getspnam(pw_ptr1p->pw_name))
869 #endif
870 				bad_pasf();
871 			else
872 				bad_name("logname already exists");
873 		}
874 
875 		if (strcmp(lognamp, pw_ptr1p->pw_name) == 0) {
876 
877 			/* no good if we want to add an existing logname */
878 			if (optn_mask & A_MASK) {
879 				rid_tmpf();
880 #ifdef att
881 				if (!getspnam(lognamp))
882 #else
883 				if (!local_getspnam(lognamp))
884 #endif
885 					bad_pasf();
886 				else
887 					bad_name("name already exists");
888 			}
889 
890 			/* remember we found it */
891 			info_mask |= FOUND;
892 
893 			/* Do not write it out on the fly */
894 			if (optn_mask & D_MASK)
895 				info_mask &= ~WRITE_P_ENTRY;
896 
897 			if (optn_mask & M_MASK) {
898 
899 #ifdef att
900 				if (!getspnam(lognamp))
901 #else
902 				if (!local_getspnam(lognamp))
903 #endif
904 				{
905 					rid_tmpf();
906 					bad_pasf();
907 				}
908 				if (optn_mask & L_MASK)
909 					pw_ptr1p->pw_name = passwd_st.pw_name;
910 
911 				if (optn_mask & U_MASK)
912 					pw_ptr1p->pw_uid = passwd_st.pw_uid;
913 
914 				if (optn_mask & G_MASK)
915 					pw_ptr1p->pw_gid = passwd_st.pw_gid;
916 
917 				if (optn_mask & C_MASK) {
918 					pw_ptr1p->pw_comment =
919 					    passwd_st.pw_comment;
920 
921 					pw_ptr1p->pw_gecos =
922 					    passwd_st.pw_comment;
923 				}
924 
925 				if (optn_mask & H_MASK)
926 					pw_ptr1p->pw_dir = passwd_st.pw_dir;
927 
928 				if (optn_mask & S_MASK)
929 					pw_ptr1p->pw_shell = passwd_st.pw_shell;
930 				ck_p_sz(pw_ptr1p);  /* check entry size */
931 			}
932 		}
933 
934 		if (optn_mask & A_MASK) {
935 			if (!NIS_entry_seen) {
936 				char	*p;
937 				p  = strchr("+-", pw_ptr1p->pw_name[0]);
938 				if (p != NULL) {
939 					/*
940 					 * Found first NIS entry.
941 					 * so remember it.
942 					 */
943 					NIS_pos = cur_pos;
944 					NIS_entry_seen = 1;
945 					info_mask &= ~WRITE_P_ENTRY;
946 				}
947 				else
948 					cur_pos = ftell(pwf);
949 			}
950 		}
951 
952 		if (info_mask & WRITE_P_ENTRY) {
953 			if (putpwent(pw_ptr1p, fp_ptemp)) {
954 				rid_tmpf();
955 				file_error();
956 			}
957 		}
958 	} /* end-of-while-loop */
959 
960 	if (error >= 1) {
961 		msg = "%s: Bad entry found in /etc/passwd.  Run pwconv.\n";
962 		fprintf(stderr, gettext(msg), prognamp);
963 	}
964 
965 	/* Cannot find the target entry and we are deleting or modifying */
966 
967 	if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
968 		rid_tmpf();
969 #ifdef att
970 		if (getspnam(lognamp) != NULL)
971 #else
972 		if (local_getspnam(lognamp) != NULL)
973 #endif
974 			bad_pasf();
975 		else
976 			bad_name("name does not exist");
977 	}
978 
979 	/* First available uid above UID_MIN is ... */
980 
981 	if (info_mask & NEED_DEF_UID)
982 		passwd_st.pw_uid = uid_sp->high + 1;
983 
984 	/* Write out the added entry now */
985 
986 	if (optn_mask & A_MASK) {
987 		ck_p_sz(&passwd_st);	/* Check entry size */
988 		if (putpwent(&passwd_st, fp_ptemp)) {
989 			rid_tmpf();
990 			file_error();
991 		}
992 		/*
993 		 * Now put out the rest of the password file, if needed.
994 		 */
995 		if (NIS_entry_seen) {
996 			int n;
997 			char buf[1024];
998 
999 			if (fseek(pwf, NIS_pos, SEEK_SET) < 0) {
1000 				rid_tmpf();
1001 				file_error();
1002 			}
1003 			while ((n = fread(buf, sizeof (char), 1024, pwf)) > 0) {
1004 				if (fwrite(buf, sizeof (char), n, fp_ptemp)
1005 				    != n) {
1006 					rid_tmpf();
1007 					file_error();
1008 				}
1009 			}
1010 		}
1011 	}
1012 
1013 	(void) fclose(pwf);
1014 
1015 	/* flush and sync the file before closing it */
1016 	if (fflush(fp_ptemp) != 0 || fsync(fd_ptemp) != 0)
1017 		file_error();
1018 
1019 	/* Now we are done with PASSWD */
1020 	(void) fclose(fp_ptemp);
1021 
1022 	/* Do this if we are touching both password files */
1023 
1024 
1025 	if (info_mask & BOTH_FILES) {
1026 		info_mask &= ~FOUND;		/* Reset FOUND flag */
1027 
1028 		/* The while loop for reading SHADOW entries */
1029 		info_mask |= WRITE_S_ENTRY;
1030 
1031 		end_of_file = 0;
1032 		errno = 0;
1033 		error = 0;
1034 
1035 		NIS_entry_seen = 0;
1036 		cur_pos = 0;
1037 
1038 		if ((spf = fopen("/etc/shadow", "r")) == NULL) {
1039 			rid_tmpf();
1040 			file_error();
1041 		}
1042 
1043 		while (!end_of_file) {
1044 			sp_ptr1p = fgetspent(spf);
1045 			if (sp_ptr1p == NULL) {
1046 				if (!feof(spf)) {
1047 					rid_tmpf();
1048 					bad_pasf();
1049 				}
1050 				else
1051 					break;
1052 			}
1053 
1054 			if (!NIS_entry_seen)
1055 				info_mask |= WRITE_S_ENTRY;
1056 			else
1057 				info_mask &= ~WRITE_S_ENTRY;
1058 
1059 			/*
1060 			 * See if the new logname already exist in the
1061 			 * shadow passwd file
1062 			 */
1063 			if ((optn_mask & M_MASK) &&
1064 			    strcmp(lognamp, shadow_st.sp_namp) != 0 &&
1065 			    strcmp(sp_ptr1p->sp_namp, shadow_st.sp_namp) == 0) {
1066 				rid_tmpf();
1067 				bad_pasf();
1068 			}
1069 
1070 			if (strcmp(lognamp, sp_ptr1p->sp_namp) == 0) {
1071 				info_mask |= FOUND;
1072 				if (optn_mask & A_MASK) {
1073 					/* password file inconsistent */
1074 					rid_tmpf();
1075 					bad_pasf();
1076 				}
1077 
1078 				if (optn_mask & M_MASK) {
1079 					sp_ptr1p->sp_namp = shadow_st.sp_namp;
1080 					if (F_MASK & optn_mask)
1081 						sp_ptr1p->sp_inact =
1082 						    shadow_st.sp_inact;
1083 					if (E_MASK & optn_mask)
1084 						sp_ptr1p->sp_expire =
1085 						    shadow_st.sp_expire;
1086 
1087 					ck_s_sz(sp_ptr1p);
1088 				}
1089 
1090 				if (optn_mask & D_MASK)
1091 					info_mask &= ~WRITE_S_ENTRY;
1092 			}
1093 
1094 			if (optn_mask & A_MASK) {
1095 				if (!NIS_entry_seen) {
1096 					char	*p;
1097 					p = strchr("+-", sp_ptr1p->sp_namp[0]);
1098 					if (p != NULL) {
1099 						/*
1100 						 * Found first NIS entry.
1101 						 * so remember it.
1102 						 */
1103 						NIS_pos = cur_pos;
1104 						NIS_entry_seen = 1;
1105 						info_mask &= ~WRITE_S_ENTRY;
1106 					}
1107 					else
1108 						cur_pos = ftell(spf);
1109 				}
1110 			}
1111 
1112 			if (info_mask & WRITE_S_ENTRY) {
1113 				if (putspent(sp_ptr1p, fp_stemp)) {
1114 					rid_tmpf();
1115 					file_error();
1116 				}
1117 			}
1118 
1119 		} /* end-of-while-loop */
1120 
1121 		if (error >= 1) {
1122 
1123 			msg = BAD_ENT_MESSAGE;
1124 			fprintf(stderr, gettext(msg), prognamp);
1125 		}
1126 
1127 		/*
1128 		 * If we cannot find the entry and we are deleting or
1129 		 *  modifying
1130 		 */
1131 
1132 		if (!(info_mask & FOUND) && (optn_mask & (D_MASK|M_MASK))) {
1133 			rid_tmpf();
1134 			bad_pasf();
1135 		}
1136 
1137 		if (optn_mask & A_MASK) {
1138 			ck_s_sz(&shadow_st);
1139 			if (putspent(&shadow_st, fp_stemp)) {
1140 				rid_tmpf();
1141 				file_error();
1142 			}
1143 
1144 			/*
1145 			 * Now put out the rest of the shadow file, if needed.
1146 			 */
1147 			if (NIS_entry_seen) {
1148 				file_copy(spf, NIS_pos);
1149 			}
1150 		}
1151 
1152 		/* flush and sync the file before closing it */
1153 		if (fflush(fp_stemp) != 0 || fsync(fd_stemp) != 0)
1154 			file_error();
1155 		(void) fclose(fp_stemp);
1156 
1157 		/* Done with SHADOW */
1158 		(void) fclose(spf);
1159 
1160 	} /* End of if info_mask */
1161 
1162 	if (info_mask & UATTR_FILE) {
1163 		info_mask &= ~FOUND;		/* Reset FOUND flag */
1164 
1165 		/* The while loop for reading USER_ATTR entries */
1166 		info_mask |= WRITE_S_ENTRY;
1167 
1168 		end_of_file = 0;
1169 		errno = 0;
1170 		error = 0;
1171 
1172 		NIS_entry_seen = 0;
1173 		cur_pos = 0;
1174 
1175 		if ((uaf = fopen(USERATTR_FILENAME, "r")) == NULL) {
1176 			rid_tmpf();
1177 			file_error();
1178 		}
1179 
1180 		while (!end_of_file) {
1181 			ua_ptr1p = fgetuserattr(uaf);
1182 			if (ua_ptr1p == NULL) {
1183 				if (!feof(uaf)) {
1184 					rid_tmpf();
1185 					bad_uattr();
1186 				}
1187 				else
1188 					break;
1189 			}
1190 
1191 			if (ua_ptr1p->name[0] == '#') {
1192 				/*
1193 				 * If this is a comment, write it back as it
1194 				 * is.
1195 				 */
1196 				if (ua_ptr1p->qualifier[0] == '\0' &&
1197 				    ua_ptr1p->res1[0] == '\0' &&
1198 				    ua_ptr1p->res2[0] == '\0' &&
1199 				    (ua_ptr1p->attr == NULL ||
1200 				    ua_ptr1p->attr->length == 0))
1201 					(void) fprintf(fp_uatemp, "%s\n",
1202 					    ua_ptr1p->name);
1203 				else
1204 					/*
1205 					 * This is a commented user_attr entry;
1206 					 * reformat it, and write it back.
1207 					 */
1208 					putuserattrent(ua_ptr1p, fp_uatemp);
1209 				free_userattr(ua_ptr1p);
1210 				continue;
1211 			}
1212 
1213 			if (!NIS_entry_seen)
1214 				info_mask |= WRITE_S_ENTRY;
1215 			else
1216 				info_mask &= ~WRITE_S_ENTRY;
1217 
1218 			/*
1219 			 * See if the new logname already exist in the
1220 			 * user_attr file
1221 			 */
1222 			if ((optn_mask & M_MASK) &&
1223 			    strcmp(lognamp, userattr_st.name) != 0 &&
1224 			    strcmp(ua_ptr1p->name, userattr_st.name) == 0) {
1225 				rid_tmpf();
1226 				bad_pasf();
1227 			}
1228 
1229 			if (strcmp(lognamp, ua_ptr1p->name) == 0) {
1230 				info_mask |= FOUND;
1231 				if (optn_mask & A_MASK) {
1232 					/* password file inconsistent */
1233 					rid_tmpf();
1234 					bad_pasf();
1235 				}
1236 
1237 				if (optn_mask & M_MASK) {
1238 					int	j;
1239 					char	*value;
1240 
1241 					for (j = 0; j < UA_KEYS; j++) {
1242 						if (ua_opts[j].newvalue != NULL)
1243 							continue;
1244 						value =
1245 						    kva_match(ua_ptr1p->attr,
1246 						    (char *)ua_opts[j].key);
1247 						if (value == NULL)
1248 							continue;
1249 						assign_attr(&userattr_st,
1250 						    ua_opts[j].key,
1251 						    value);
1252 					}
1253 					free_userattr(ua_ptr1p);
1254 					ua_ptr1p = &userattr_st;
1255 				}
1256 
1257 				if (optn_mask & D_MASK)
1258 					info_mask &= ~WRITE_S_ENTRY;
1259 			} else if (optn_mask & D_MASK) {
1260 				char *rolelist;
1261 
1262 				rolelist = kva_match(ua_ptr1p->attr,
1263 				    USERATTR_ROLES_KW);
1264 				if (rolelist) {
1265 					unassign_role(ua_ptr1p,
1266 					    rolelist, lognamp);
1267 				}
1268 			}
1269 
1270 			if (info_mask & WRITE_S_ENTRY) {
1271 				putuserattrent(ua_ptr1p, fp_uatemp);
1272 			}
1273 
1274 			if (!(optn_mask & M_MASK))
1275 				free_userattr(ua_ptr1p);
1276 		} /* end-of-while-loop */
1277 
1278 		if (error >= 1) {
1279 
1280 			msg = BAD_ENT_MESSAGE;
1281 			fprintf(stderr, gettext(msg), prognamp);
1282 		}
1283 
1284 		/*
1285 		 * Add entry in user_attr if masks is UATTR_MASK
1286 		 * We don't need to do anything for L_MASK if there's
1287 		 * no user_attr entry for the user being modified.
1288 		 */
1289 		if (!(info_mask & FOUND) && !(L_MASK & optn_mask) &&
1290 		    !(D_MASK & optn_mask)) {
1291 			putuserattrent(&userattr_st, fp_uatemp);
1292 		}
1293 
1294 		/* flush and sync the file before closing it */
1295 		if (fflush(fp_uatemp) != 0 || fsync(fd_uatemp) != 0)
1296 			file_error();
1297 		(void) fclose(fp_uatemp);
1298 
1299 		/* Done with USERATTR */
1300 		(void) fclose(uaf);
1301 
1302 	} /* End of if info_mask */
1303 	/* ignore all signals */
1304 
1305 	for (i = 1; i < NSIG; i++)
1306 		(void) sigset(i, SIG_IGN);
1307 
1308 	errno = 0;		/* For correcting sigset to SIGKILL */
1309 
1310 	if (unlink(OPASSWD) && access(OPASSWD, 0) == 0)
1311 		file_error();
1312 
1313 	if (link(PASSWD, OPASSWD) == -1)
1314 			file_error();
1315 
1316 
1317 	if (rename(PASSTEMP, PASSWD) == -1) {
1318 		if (link(OPASSWD, PASSWD))
1319 			bad_news();
1320 		file_error();
1321 	}
1322 
1323 
1324 	if (info_mask & BOTH_FILES) {
1325 
1326 		if (unlink(OSHADOW) && access(OSHADOW, 0) == 0) {
1327 			if (rec_pwd())
1328 				bad_news();
1329 			else
1330 				file_error();
1331 		}
1332 
1333 		if (link(SHADOW, OSHADOW) == -1) {
1334 			if (rec_pwd())
1335 				bad_news();
1336 			else
1337 				file_error();
1338 		}
1339 
1340 
1341 		if (rename(SHADTEMP, SHADOW) == -1) {
1342 			if (rename(OSHADOW, SHADOW) == -1)
1343 				bad_news();
1344 
1345 			if (rec_pwd())
1346 				bad_news();
1347 			else
1348 				file_error();
1349 		}
1350 
1351 	}
1352 	if (info_mask & UATTR_FILE) {
1353 		if (unlink(OUSERATTR_FILENAME) &&
1354 		    access(OUSERATTR_FILENAME, 0) == 0) {
1355 			if (rec_pwd())
1356 				bad_news();
1357 			else
1358 				file_error();
1359 		}
1360 
1361 		if (link(USERATTR_FILENAME, OUSERATTR_FILENAME) == -1) {
1362 			if (rec_pwd())
1363 				bad_news();
1364 			else
1365 				file_error();
1366 		}
1367 
1368 
1369 		if (rename(USERATTR_TEMP, USERATTR_FILENAME) == -1) {
1370 			if (rename(OUSERATTR_FILENAME, USERATTR_FILENAME) == -1)
1371 				bad_news();
1372 
1373 			if (rec_pwd())
1374 				bad_news();
1375 			else
1376 				file_error();
1377 		}
1378 
1379 	}
1380 
1381 	ulckpwdf();
1382 
1383 	/*
1384 	 * Return 0 status, indicating success
1385 	 */
1386 	return (0);
1387 
1388 }  /* end of main */
1389 
1390 /* Try to recover the old password file */
1391 
1392 int
1393 rec_pwd(void)
1394 {
1395 	if (unlink(PASSWD) || link(OPASSWD, PASSWD))
1396 		return (-1);
1397 
1398 	return (0);
1399 }
1400 
1401 /* combine two uid_blk's */
1402 
1403 void
1404 uid_bcom(struct uid_blk *uid_p)
1405 {
1406 	struct uid_blk *uid_tp;
1407 
1408 	uid_tp = uid_p->link;
1409 	uid_p->high = uid_tp->high;
1410 	uid_p->link = uid_tp->link;
1411 
1412 	free(uid_tp);
1413 }
1414 
1415 /* add a new uid_blk */
1416 
1417 void
1418 add_ublk(uid_t num, struct uid_blk *uid_p)
1419 {
1420 	struct uid_blk *uid_tp;
1421 
1422 	uid_tp = malloc(sizeof (struct uid_blk));
1423 	if (uid_tp == NULL) {
1424 		rid_tmpf();
1425 		file_error();
1426 	}
1427 
1428 	uid_tp->high = uid_tp->low = num;
1429 	uid_tp->link = uid_p->link;
1430 	uid_p->link = uid_tp;
1431 }
1432 
1433 /*
1434  *	Here we are using a linked list of uid_blk to keep track of all
1435  *	the used uids.	Each uid_blk represents a range of used uid,
1436  *	with low represents the low inclusive end and high represents
1437  *	the high inclusive end.  In the beginning, we initialize a linked
1438  *	list of one uid_blk with low = high = (UID_MIN-1).  This was
1439  *	done in main().
1440  *	Each time we read in another used uid, we add it onto the linked
1441  *	list by either making a new uid_blk, decrementing the low of
1442  *	an existing uid_blk, incrementing the high of an existing
1443  *	uid_blk, or combining two existing uid_blks.  After we finished
1444  *	building this linked list, the first available uid above or
1445  *	equal to UID_MIN is the high of the first uid_blk in the linked
1446  *	list + 1.
1447  */
1448 /* add_uid() adds uid to the link list of used uids */
1449 void
1450 add_uid(uid_t uid)
1451 {
1452 	struct uid_blk *uid_p;
1453 	/* Only keep track of the ones above UID_MIN */
1454 
1455 	if (uid >= UID_MIN) {
1456 		uid_p = uid_sp;
1457 
1458 		while (uid_p != NULL) {
1459 
1460 			if (uid_p->link != NULL) {
1461 
1462 				if (uid >= uid_p->link->low)
1463 					uid_p = uid_p->link;
1464 
1465 				else if (uid >= uid_p->low &&
1466 				    uid <= uid_p->high) {
1467 					uid_p = NULL;
1468 				}
1469 
1470 				else if (uid == (uid_p->high+1)) {
1471 
1472 					if (++uid_p->high ==
1473 					    (uid_p->link->low - 1)) {
1474 						uid_bcom(uid_p);
1475 					}
1476 					uid_p = NULL;
1477 				}
1478 
1479 				else if (uid == (uid_p->link->low - 1)) {
1480 					uid_p->link->low --;
1481 					uid_p = NULL;
1482 				}
1483 
1484 				else if (uid < uid_p->link->low) {
1485 					add_ublk(uid, uid_p);
1486 					uid_p = NULL;
1487 				}
1488 			} /* if uid_p->link */
1489 
1490 			else {
1491 
1492 				if (uid == (uid_p->high + 1)) {
1493 					uid_p->high++;
1494 					uid_p = NULL;
1495 				} else if (uid >= uid_p->low &&
1496 				    uid <= uid_p->high) {
1497 					uid_p = NULL;
1498 				} else {
1499 					add_ublk(uid, uid_p);
1500 					uid_p = NULL;
1501 				}
1502 			} /* else */
1503 		} /* while uid_p */
1504 
1505 	} /* if uid */
1506 }
1507 
1508 void
1509 bad_perm(void)
1510 {
1511 	(void) fprintf(stderr, gettext("%s: Permission denied\n"), prognamp);
1512 	exit(1);
1513 }
1514 
1515 void
1516 bad_usage(char *sp)
1517 {
1518 	if (strlen(sp) != 0)
1519 		(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(sp));
1520 	(void) fprintf(stderr, gettext("Usage:\n\
1521 %s -a [-c comment] [-h homedir] [-u uid [-o]] [-g gid] \n\
1522 	    [-s shell] [-f inactive] [-e expire] name\n\
1523 %s -m  -c comment | -h homedir | -u uid [-o] | -g gid |\n\
1524 	    -s shell | -f inactive | -e expire	|  -l logname  name\n\
1525 %s -d name\n"), prognamp, prognamp, prognamp);
1526 	if (info_mask & LOCKED)
1527 		ulckpwdf();
1528 	exit(2);
1529 }
1530 
1531 void
1532 bad_arg(char *s)
1533 {
1534 	(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1535 
1536 	if (info_mask & LOCKED)
1537 		ulckpwdf();
1538 	exit(3);
1539 }
1540 
1541 void
1542 bad_name(char *s)
1543 {
1544 	(void) fprintf(stderr, "%s: %s\n", prognamp, gettext(s));
1545 	ulckpwdf();
1546 	exit(9);
1547 }
1548 
1549 void
1550 bad_uid(void)
1551 {
1552 	(void) fprintf(stderr, gettext("%s: UID in use\n"), prognamp);
1553 
1554 	ulckpwdf();
1555 	exit(4);
1556 }
1557 
1558 void
1559 bad_pasf(void)
1560 {
1561 	msg = "%s: Inconsistent password files\n";
1562 	(void) fprintf(stderr, gettext(msg), prognamp);
1563 
1564 	ulckpwdf();
1565 	exit(5);
1566 }
1567 
1568 void
1569 bad_uattr(void)
1570 {
1571 	msg = "%s: Bad user_attr database\n";
1572 	(void) fprintf(stderr, gettext(msg), prognamp);
1573 
1574 	ulckpwdf();
1575 	exit(5);
1576 }
1577 
1578 void
1579 file_error(void)
1580 {
1581 	msg = "%s: Unexpected failure.	Password files unchanged\n";
1582 	(void) fprintf(stderr, gettext(msg), prognamp);
1583 
1584 	ulckpwdf();
1585 	exit(6);
1586 }
1587 
1588 void
1589 bad_news(void)
1590 {
1591 	msg = "%s: Unexpected failure.	Password file(s) missing\n";
1592 	(void) fprintf(stderr, gettext(msg), prognamp);
1593 
1594 	ulckpwdf();
1595 	exit(7);
1596 }
1597 
1598 void
1599 no_lock(void)
1600 {
1601 	msg = "%s: Password file(s) busy.  Try again later\n";
1602 	(void) fprintf(stderr, gettext(msg), prognamp);
1603 
1604 	exit(8);
1605 }
1606 
1607 /* Check for the size of the whole passwd entry */
1608 void
1609 ck_p_sz(struct passwd *pwp)
1610 {
1611 	char ctp[128];
1612 
1613 	/* Ensure that the combined length of the individual */
1614 	/* fields will fit in a passwd entry. The 1 accounts for the */
1615 	/* newline and the 6 accounts for the colons (:'s) */
1616 	if (((int)strlen(pwp->pw_name) + 1 +
1617 	    sprintf(ctp, "%d", pwp->pw_uid) +
1618 	    sprintf(ctp, "%d", pwp->pw_gid) +
1619 	    (int)strlen(pwp->pw_comment) +
1620 	    (int)strlen(pwp->pw_dir) +
1621 	    (int)strlen(pwp->pw_shell) + 6) > (ENTRY_LENGTH-1)) {
1622 		rid_tmpf();
1623 		bad_arg("New password entry too long");
1624 	}
1625 }
1626 
1627 /* Check for the size of the whole passwd entry */
1628 void
1629 ck_s_sz(struct spwd *ssp)
1630 {
1631 	char ctp[128];
1632 
1633 	/* Ensure that the combined length of the individual */
1634 	/* fields will fit in a shadow entry. The 1 accounts for the */
1635 	/* newline and the 7 accounts for the colons (:'s) */
1636 	if (((int)strlen(ssp->sp_namp) + 1 +
1637 	    (int)strlen(ssp->sp_pwdp) +
1638 	    sprintf(ctp, "%d", ssp->sp_lstchg) +
1639 	    sprintf(ctp, "%d", ssp->sp_min) +
1640 	    sprintf(ctp, "%d", ssp->sp_max) +
1641 	    sprintf(ctp, "%d", ssp->sp_warn) +
1642 	    sprintf(ctp, "%d", ssp->sp_inact) +
1643 	    sprintf(ctp, "%d", ssp->sp_expire) + 7) > (ENTRY_LENGTH - 1)) {
1644 		rid_tmpf();
1645 		bad_arg("New password entry too long");
1646 	}
1647 }
1648 
1649 /* Get rid of the temp files */
1650 void
1651 rid_tmpf(void)
1652 {
1653 	(void) fclose(fp_ptemp);
1654 
1655 	if (unlink(PASSTEMP)) {
1656 		msg = "%s: warning: cannot unlink %s\n";
1657 		(void) fprintf(stderr, gettext(msg), prognamp, PASSTEMP);
1658 	}
1659 
1660 	if (info_mask & BOTH_FILES) {
1661 		(void) fclose(fp_stemp);
1662 
1663 		if (unlink(SHADTEMP)) {
1664 			msg = "%s: warning: cannot unlink %s\n";
1665 			(void) fprintf(stderr, gettext(msg), prognamp,
1666 			    SHADTEMP);
1667 		}
1668 	}
1669 
1670 	if (info_mask & UATTR_FILE) {
1671 		(void) fclose(fp_uatemp);
1672 
1673 		if (unlink(USERATTR_TEMP)) {
1674 			msg = "%s: warning: cannot unlink %s\n";
1675 			(void) fprintf(stderr, gettext(msg), prognamp,
1676 			    USERATTR_TEMP);
1677 		}
1678 	}
1679 }
1680 
1681 void
1682 file_copy(FILE *spf, long NIS_pos)
1683 {
1684 	int n;
1685 	char buf[1024];
1686 
1687 	if (fseek(spf, NIS_pos, SEEK_SET) < 0) {
1688 		rid_tmpf();
1689 		file_error();
1690 	}
1691 	while ((n = fread(buf, sizeof (char), 1024, spf)) > 0) {
1692 		if (fwrite(buf, sizeof (char), n, fp_stemp) != n) {
1693 			rid_tmpf();
1694 			file_error();
1695 		}
1696 	}
1697 }
1698