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