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