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