xref: /illumos-gate/usr/src/cmd/passwd/passwd.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
31 /*	  All Rights Reserved	*/
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 /*
36  * passwd is a program whose sole purpose is to manage
37  * the password file, map, or table. It allows system administrator
38  * to add, change and display password attributes.
39  * Non privileged user can change password or display
40  * password attributes which corresponds to their login name.
41  */
42 
43 #include <stdio.h>
44 #include <pwd.h>
45 #include <sys/types.h>
46 #include <errno.h>
47 #include <unistd.h>
48 #include <stdlib.h>
49 #include <locale.h>
50 #include <stdarg.h>
51 #include <errno.h>
52 #include <string.h>
53 #include <security/pam_appl.h>
54 #include <security/pam_modules.h>
55 #include <security/pam_impl.h>
56 #include <rpcsvc/nis.h>
57 #undef GROUP
58 #include <syslog.h>
59 #include <userdefs.h>
60 #include <passwdutil.h>
61 
62 #include <nss_dbdefs.h>
63 
64 #include <deflt.h>
65 
66 #undef	GROUP
67 #include <bsm/adt.h>
68 #include <bsm/adt_event.h>
69 
70 /*
71  * flags indicate password attributes to be modified
72  */
73 
74 #define	LFLAG 0x001		/* lock user's password  */
75 #define	DFLAG 0x002		/* delete user's  password */
76 #define	MFLAG 0x004		/* set max field -- # of days passwd is valid */
77 #define	NFLAG 0x008		/* set min field -- # of days between */
78 				/* password changes */
79 #define	SFLAG 0x010		/* display password attributes */
80 #define	FFLAG 0x020		/* expire  user's password */
81 #define	AFLAG 0x040		/* display password attributes for all users */
82 #define	SAFLAG (SFLAG|AFLAG)	/* display password attributes for all users */
83 #define	WFLAG 0x100		/* warn user to change passwd */
84 #define	OFLAG 0x200		/* domain name */
85 #define	EFLAG 0x400		/* change shell */
86 #define	GFLAG 0x800		/* change gecos information */
87 #define	HFLAG 0x1000		/* change home directory */
88 #define	XFLAG 0x2000		/* no login */
89 #define	UFLAG 0x4000		/* unlock user's password */
90 
91 #define	NONAGEFLAG	(EFLAG | GFLAG | HFLAG)
92 #define	AGEFLAG	(LFLAG | FFLAG | MFLAG | NFLAG | WFLAG | XFLAG | UFLAG)
93 #define	MUTEXFLAG	(DFLAG | LFLAG | XFLAG | UFLAG | SAFLAG)
94 
95 
96 /*
97  * exit code
98  */
99 
100 #define	SUCCESS	0	/* succeeded */
101 #define	NOPERM	1	/* No permission */
102 #define	BADOPT	2	/* Invalid combination of option */
103 #define	FMERR	3	/* File/table manipulation error */
104 #define	FATAL	4	/* Old file/table can not be recovered */
105 #define	FBUSY	5	/* Lock file/table busy */
106 #define	BADSYN	6	/* Incorrect syntax */
107 #define	BADAGE	7	/* Aging is disabled  */
108 #define	NOMEM	8	/* No memory */
109 #define	SYSERR	9	/* System error */
110 #define	EXPIRED	10	/* Account expired */
111 
112 /*
113  * define error messages
114  */
115 #define	MSG_NP		"Permission denied"
116 #define	MSG_BS		"Invalid combination of options"
117 #define	MSG_FE		"Unexpected failure. Password file/table unchanged."
118 #define	MSG_FF		"Unexpected failure. Password file/table missing."
119 #define	MSG_FB		"Password file/table busy. Try again later."
120 #define	MSG_NV  	"Invalid argument to option"
121 #define	MSG_AD		"Password aging is disabled"
122 #define	MSG_RS		"Cannot change from restricted shell %s\n"
123 #define	MSG_NM		"Out of memory."
124 #define	MSG_UNACCEPT	"%s is unacceptable as a new shell\n"
125 #define	MSG_UNAVAIL	"warning: %s is unavailable on this machine\n"
126 #define	MSG_COLON	"':' is not allowed.\n"
127 #define	MSG_MAXLEN	"Maximum number of characters allowed is %d."
128 #define	MSG_CONTROL	"Control characters are not allowed.\n"
129 #define	MSG_SHELL_UNCHANGED	"Login shell unchanged.\n"
130 #define	MSG_GECOS_UNCHANGED	"Finger information unchanged.\n"
131 #define	MSG_DIR_UNCHANGED	"Homedir information unchanged.\n"
132 #define	MSG_NAME	"\nName [%s]: "
133 #define	MSG_HOMEDIR	"\nHome Directory [%s]: "
134 #define	MSG_OLDSHELL	"Old shell: %s\n"
135 #define	MSG_NEWSHELL	"New shell: "
136 #define	MSG_AGAIN	"\nPlease try again\n"
137 #define	MSG_INPUTHDR	"Default values are printed inside of '[]'.\n" \
138 			"To accept the default, type <return>.\n" \
139 			"To have a blank entry, type the word 'none'.\n"
140 #define	MSG_UNKNOWN	"%s: User unknown: %s\n"
141 #define	MSG_ACCOUNT_EXP	"User account has expired: %s\n"
142 #define	MSG_AUTHTOK_EXP	"Your password has been expired for too long.\n" \
143 			"Please contact the system administrator.\n"
144 #define	MSG_NIS_HOMEDIR	"-h does not apply to NIS"
145 #define	MSG_CUR_PASS	"Enter existing login password: "
146 #define	MSG_CUR_PASS_UNAME	"Enter %s's existing login password: "
147 #define	MSG_SUCCESS	"%s: password information changed for %s\n"
148 #define	MSG_SORRY	"%s: Sorry, wrong passwd\n"
149 #define	MSG_INFO	"%s: Changing password for %s\n"
150 
151 
152 /*
153  * return code from ckarg() routine
154  */
155 #define	FAIL 		-1
156 
157 /*
158  *  defind password file name
159  */
160 #define	PASSWD 			"/etc/passwd"
161 
162 #define	MAX_INPUT_LEN		512
163 
164 #define	DEF_ATTEMPTS	3
165 
166 /* Number of characters in that make up an encrypted password (for now) */
167 #define	NUMCP			13
168 
169 #ifdef DEBUG
170 #define	dprintf1	printf
171 #else
172 #define	dprintf1(w, x)
173 #endif
174 
175 extern int	optind;
176 
177 static int		retval = SUCCESS;
178 static int		pam_retval = PAM_SUCCESS;
179 static uid_t		uid;
180 static char		*prognamep;
181 static long		maxdate;	/* password aging information */
182 static int		passwd_conv();
183 static struct pam_conv	pam_conv = {passwd_conv, NULL};
184 static pam_handle_t	*pamh;		/* Authentication handle */
185 static char		*usrname;	/* user whose attribute we update */
186 static adt_session_data_t *ah;  /* audit session handle */
187 static adt_event_data_t *event = NULL; /* event to be generated */
188 
189 static pam_repository_t	auth_rep;
190 static pwu_repository_t	repository;
191 static pwu_repository_t	__REPFILES = { "files", NULL, 0 };
192 
193 /*
194  * Function Declarations
195  */
196 extern	nis_name	nis_local_directory(void);
197 
198 extern	void		setusershell(void);
199 extern	char		*getusershell(void);
200 extern	void		endusershell(void);
201 
202 static	void		passwd_exit(int retcode);
203 static	void		rusage(void);
204 static	int		ckuid(void);
205 static	int		ckarg(int argc, char **argv, attrlist **attributes);
206 
207 static	int		get_namelist(pwu_repository_t, char ***, int *);
208 static	int		get_namelist_files(char ***, int *);
209 static	int		get_namelist_nisplus(char ***, int *);
210 static	int		get_attr(char *, pwu_repository_t *, attrlist **);
211 static	void		display_attr(char *, attrlist *);
212 static	void		free_attr(attrlist *);
213 static	void		attrlist_add(attrlist **, attrtype, char *);
214 static	void		attrlist_reorder(attrlist **);
215 static	char		*userinput(char *, pwu_repository_t *, attrtype);
216 static	char		*getresponse(char *);
217 
218 /*
219  * main():
220  *	The main routine will call ckarg() to parse the command line
221  *	arguments and call the appropriate functions to perform the
222  *	tasks specified by the arguments. It allows system
223  * 	administrator to add, change and display password attributes.
224  * 	Non privileged user can change password or display
225  * 	password attributes which corresponds to their login name.
226  */
227 
228 void
229 main(argc, argv)
230 	int argc;
231 	char *argv[];
232 {
233 
234 	int	flag;
235 	char	**namelist;
236 	int	num_user;
237 	int	i;
238 	attrlist *attributes = NULL;
239 	char	*input;
240 	int	tries = 1;
241 	int	updated_reps;
242 
243 
244 	if (prognamep = strrchr(argv[0], '/'))
245 		++prognamep;
246 	else
247 		prognamep = argv[0];
248 
249 	auth_rep.type = NULL;
250 	auth_rep.scope = NULL;
251 	repository.type = NULL;
252 	repository.scope = NULL;
253 	repository.scope_len = 0;
254 
255 
256 	/* initialization for variables, set locale and textdomain  */
257 	i = 0;
258 	flag = 0;
259 
260 	uid = getuid();		/* get the user id */
261 	(void) setlocale(LC_ALL, "");
262 
263 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
264 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
265 #endif
266 	(void) textdomain(TEXT_DOMAIN);
267 
268 	/*
269 	 * ckarg() parses the arguments. In case of an error,
270 	 * it sets the retval and returns FAIL (-1).
271 	 */
272 
273 	flag = ckarg(argc, argv, &attributes);
274 	dprintf1("flag is %0x\n", flag);
275 	if (flag == FAIL)
276 		passwd_exit(retval);
277 
278 	argc -= optind;
279 
280 	if (argc < 1) {
281 		if ((usrname = getlogin()) == NULL) {
282 			struct passwd *pass = getpwuid(uid);
283 			if (pass != NULL)
284 				usrname = pass->pw_name;
285 			else {
286 				rusage();
287 				exit(NOPERM);
288 			}
289 		} else if (flag == 0) {
290 			/*
291 			 * If flag is zero, change passwd.
292 			 * Otherwise, it will display or
293 			 * modify password aging attributes
294 			 */
295 			(void) fprintf(stderr, gettext(MSG_INFO), prognamep,
296 			    usrname);
297 		}
298 	} else
299 		usrname = argv[optind];
300 
301 	if (pam_start("passwd", usrname, &pam_conv, &pamh) != PAM_SUCCESS)
302 		passwd_exit(NOPERM);
303 
304 	auth_rep.type = repository.type;
305 	auth_rep.scope = repository.scope;
306 	auth_rep.scope_len = repository.scope_len;
307 
308 	if (auth_rep.type != NULL) {
309 		if (pam_set_item(pamh, PAM_REPOSITORY, (void *)&auth_rep)
310 		    != PAM_SUCCESS) {
311 			passwd_exit(NOPERM);
312 		}
313 	}
314 
315 	if (flag ==  SAFLAG) {	/* display password attributes for all users */
316 		retval = get_namelist(repository, &namelist, &num_user);
317 		if (retval != SUCCESS)
318 			(void) passwd_exit(retval);
319 
320 		if (num_user == 0) {
321 			(void) fprintf(stderr, "%s: %s\n", prognamep,
322 					gettext(MSG_FF));
323 			passwd_exit(FATAL);
324 		}
325 		i = 0;
326 		while (namelist[i] != NULL) {
327 			(void) get_attr(namelist[i], &repository,
328 			    &attributes);
329 			(void) display_attr(namelist[i], attributes);
330 			(void) free(namelist[i]);
331 			(void) free_attr(attributes);
332 			i++;
333 		}
334 		(void) free(namelist);
335 		passwd_exit(SUCCESS);
336 	} else if (flag == SFLAG) { /* display password attributes by user */
337 		if (get_attr(usrname, &repository, &attributes) ==
338 		    PWU_SUCCESS) {
339 			(void) display_attr(usrname, attributes);
340 			(void) free_attr(attributes);
341 		}
342 		passwd_exit(SUCCESS);
343 		/* NOT REACHED */
344 	}
345 
346 
347 	switch (pam_authenticate(pamh, 0)) {
348 	case PAM_SUCCESS:
349 		break;
350 	case PAM_USER_UNKNOWN:
351 		(void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
352 		    usrname);
353 		passwd_exit(NOPERM);
354 		break;
355 	case PAM_PERM_DENIED:
356 		passwd_exit(NOPERM);
357 		break;
358 	case PAM_AUTH_ERR:
359 		(void) fprintf(stderr, gettext(MSG_SORRY), prognamep);
360 		passwd_exit(NOPERM);
361 		break;
362 	default:
363 		/* system error */
364 		passwd_exit(FMERR);
365 		break;
366 	}
367 
368 	if (flag == 0) {			/* changing user password */
369 		int	chk_authtok = 0;	/* check password strength */
370 
371 		dprintf1("call pam_chauthtok() repository name =%s\n",
372 		    repository.type);
373 
374 		/* Set up for Audit */
375 		if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
376 			perror("adt_start_session");
377 			passwd_exit(SYSERR);
378 		}
379 		if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
380 			perror("adt_alloc_event");
381 			passwd_exit(NOMEM);
382 		}
383 		if (argc >= 1) {
384 			/* save target user */
385 			event->adt_passwd.username = usrname;
386 		}
387 
388 		/* Don't check account expiration when invoked by root */
389 		if (ckuid() != SUCCESS) {
390 			pam_retval = pam_acct_mgmt(pamh, PAM_SILENT);
391 			switch (pam_retval) {
392 			case PAM_ACCT_EXPIRED:
393 				(void) fprintf(stderr,
394 				    gettext(MSG_ACCOUNT_EXP), usrname);
395 				passwd_exit(EXPIRED);
396 				break;
397 			case PAM_AUTHTOK_EXPIRED:
398 				(void) fprintf(stderr,
399 				    gettext(MSG_AUTHTOK_EXP));
400 				passwd_exit(NOPERM);
401 				break;
402 			case PAM_NEW_AUTHTOK_REQD:
403 				/* valid error when changing passwords */
404 				break;
405 			case PAM_SUCCESS:
406 				/* Ok to change password */
407 				break;
408 			default:
409 				passwd_exit(NOPERM);
410 			}
411 		}
412 
413 
414 		pam_retval = PAM_AUTHTOK_ERR;
415 		tries = 1;
416 		if (ckuid() == SUCCESS) {
417 			/* bypass password strength checks */
418 			chk_authtok = PAM_NO_AUTHTOK_CHECK;
419 		}
420 
421 		while (pam_retval == PAM_AUTHTOK_ERR && tries <= DEF_ATTEMPTS) {
422 			if (tries > 1)
423 				(void) printf(gettext(MSG_AGAIN));
424 			pam_retval = pam_chauthtok(pamh, chk_authtok);
425 			if (pam_retval == PAM_TRY_AGAIN) {
426 				(void) sleep(1);
427 				pam_retval = pam_chauthtok(pamh, chk_authtok);
428 			}
429 			tries++;
430 		}
431 
432 		switch (pam_retval) {
433 		case PAM_SUCCESS:
434 			retval = SUCCESS;
435 			break;
436 		case PAM_AUTHTOK_DISABLE_AGING:
437 			retval = BADAGE;
438 			break;
439 		case PAM_AUTHTOK_LOCK_BUSY:
440 			retval = FBUSY;
441 			break;
442 		case PAM_TRY_AGAIN:
443 			retval = FBUSY;
444 			break;
445 		case PAM_AUTHTOK_ERR:
446 		case PAM_AUTHTOK_RECOVERY_ERR:
447 		default:
448 			retval = NOPERM;
449 			break;
450 		}
451 
452 		(void) passwd_exit(retval);
453 		/* NOT REACHED */
454 	} else {		/* changing attributes */
455 		switch (flag) {
456 		case EFLAG:		/* changing user password attributes */
457 			input = userinput(usrname, &repository, ATTR_SHELL);
458 			if (input)
459 				attrlist_add(&attributes, ATTR_SHELL, input);
460 			else
461 				(void) printf(gettext(MSG_SHELL_UNCHANGED));
462 			break;
463 		case GFLAG:
464 			input = userinput(usrname, &repository, ATTR_GECOS);
465 			if (input)
466 				attrlist_add(&attributes, ATTR_GECOS, input);
467 			else
468 				(void) printf(gettext(MSG_GECOS_UNCHANGED));
469 			break;
470 		case HFLAG:
471 			input = userinput(usrname, &repository, ATTR_HOMEDIR);
472 			if (input)
473 				attrlist_add(&attributes, ATTR_HOMEDIR, input);
474 			else
475 				(void) printf(gettext(MSG_DIR_UNCHANGED));
476 			break;
477 		}
478 
479 		if (attributes != NULL) {
480 			retval = __set_authtoken_attr(usrname,
481 			    pamh->ps_item[PAM_AUTHTOK].pi_addr,
482 			    NULL, &repository, attributes, &updated_reps);
483 			switch (retval) {
484 			case PWU_SUCCESS:
485 				for (i = 1; i <= REP_LAST; i <<= 1) {
486 					if ((updated_reps & i) == 0)
487 						continue;
488 					(void) printf(gettext(MSG_SUCCESS),
489 					    prognamep, usrname);
490 				}
491 				retval = SUCCESS;
492 				break;
493 			case PWU_AGING_DISABLED:
494 				retval = BADAGE;
495 				break;
496 			default:
497 				retval = NOPERM;
498 				break;
499 			}
500 		} else {
501 			retval = SUCCESS; /* nothing to change won't fail */
502 		}
503 		(void) passwd_exit(retval);
504 	}
505 }
506 
507 /*
508  * Get a line of input from the user.
509  *
510  * If the line is empty, or the input equals 'oldval', NULL is returned.
511  * therwise, a malloced string containing the input (minus the trailing
512  * newline) is returned.
513  */
514 char *
515 getresponse(char *oldval)
516 {
517 	char    resp[MAX_INPUT_LEN];
518 	char    *retval = NULL;
519 	int	resplen;
520 
521 	(void) fgets(resp, sizeof (resp) - 1, stdin);
522 	resplen = strlen(resp) - 1;
523 	if (resp[resplen] == '\n')
524 		resp[resplen] = '\0';
525 	if (*resp != '\0' && strcmp(resp, oldval) != 0)
526 		retval = strdup(resp);
527 	return (retval);
528 }
529 
530 /*
531  * char *userinput(item)
532  *
533  * user conversation function. The old value of attribute "item" is
534  * displayed while the user is asked to provide a new value.
535  *
536  * returns a malloc()-ed string if the user actualy provided input
537  * or NULL if the user simply hit return or the input equals the old
538  * value (not changed).
539  */
540 char *
541 userinput(char *name, pwu_repository_t *rep, attrtype type)
542 {
543 	attrlist oldattr;
544 	char *oldval;			/* shorthand for oldattr.data.val_s */
545 	char *valid;			/* points to valid shells */
546 	char *response;
547 	char *cp;
548 
549 	oldattr.type = type;
550 	oldattr.next = NULL;
551 
552 	if (__get_authtoken_attr(name, rep, &oldattr) != PWU_SUCCESS)
553 		passwd_exit(FMERR);
554 
555 	oldval = oldattr.data.val_s;
556 
557 	if (type == ATTR_SHELL) {
558 		/* No current shell: set DEFSHL as default choice */
559 		if (*oldval == '\0') {
560 			free(oldval);
561 			oldval = strdup(DEFSHL);
562 		}
563 
564 		if (ckuid() != SUCCESS) {
565 			/* User must currently have a valid shell */
566 			setusershell();
567 			valid = getusershell();
568 			while (valid && strcmp(valid, oldval) != 0)
569 				valid = getusershell();
570 			endusershell();
571 
572 			if (valid == NULL) {
573 				(void) fprintf(stderr, gettext(MSG_RS), oldval);
574 				free(oldval);
575 				return (NULL);
576 			}
577 		}
578 		(void) printf(gettext(MSG_OLDSHELL), oldval);
579 		(void) printf(gettext(MSG_NEWSHELL));
580 		(void) fflush(stdout);
581 
582 		response = getresponse(oldval);
583 		free(oldval); /* We don't need the old value anymore */
584 
585 		if (response == NULL || *response == '\0')
586 			return (NULL);
587 
588 		/* Make sure new shell is listed */
589 		setusershell();
590 		valid = getusershell();
591 		while (valid) {
592 			char *cp;
593 
594 			/* Allow user to give shell without path */
595 			if (*response == '/') {
596 				cp = valid;
597 			} else {
598 				if ((cp = strrchr(valid, '/')) == NULL)
599 					cp = valid;
600 				else
601 					cp++;
602 			}
603 			if (strcmp(cp, response) == 0) {
604 				if (*response != '/') {
605 					/* take shell name including path */
606 					free(response);
607 					response = strdup(valid);
608 				}
609 				break;
610 			}
611 			valid = getusershell();
612 		}
613 		endusershell();
614 
615 		if (valid == NULL) {    /* No valid shell matches */
616 			(void) fprintf(stderr, gettext(MSG_UNACCEPT), response);
617 			return (NULL);
618 		}
619 
620 		if (access(response, X_OK) < 0)
621 			(void) fprintf(stderr, gettext(MSG_UNAVAIL), response);
622 		return (response);
623 		/* NOT REACHED */
624 	}
625 	/*
626 	 * if type == SHELL, we have returned by now. Only GECOS and
627 	 * HOMEDIR get to this point.
628 	 */
629 	(void) printf(gettext(MSG_INPUTHDR));
630 
631 	/*
632 	 * PRE: oldval points to malloced string with Old Value
633 	 * INV: oldval remains unchanged
634 	 * POST:response points to valid string or NULL.
635 	 */
636 	for (;;) {
637 		if (type == ATTR_GECOS)
638 			(void) printf(gettext(MSG_NAME), oldval);
639 		else if (type == ATTR_HOMEDIR)
640 			(void) printf(gettext(MSG_HOMEDIR), oldval);
641 
642 		response = getresponse(oldval);
643 
644 		if (response && strcmp(response, "none") == 0)
645 			*response = '\0';
646 
647 		/* No-change or empty string are OK */
648 		if (response == NULL || *response == '\0')
649 			break;
650 
651 		/* Check for illegal characters */
652 		if (strchr(response, ':')) {
653 			(void) fprintf(stderr, "%s", gettext(MSG_COLON));
654 			free(response);
655 		} else if (strlen(response) > MAX_INPUT_LEN - 1) {
656 			(void) fprintf(stderr, gettext(MSG_MAXLEN),
657 			    MAX_INPUT_LEN);
658 			free(response);
659 		} else {
660 			/* don't allow control characters */
661 			for (cp = response; *cp >= 040; cp++)
662 				;
663 			if (*cp != '\0') {
664 				(void) fprintf(stderr, gettext(MSG_CONTROL));
665 				free(response);
666 			} else
667 				break;	/* response is a valid string */
668 		}
669 		/*
670 		 * We only get here if the input was invalid.
671 		 * In that case, we again ask the user for input.
672 		 */
673 	}
674 	free(oldval);
675 	return (response);
676 }
677 /*
678  * ckarg():
679  *	This function parses and verifies the
680  * 	arguments.  It takes three parameters:
681  * 	argc => # of arguments
682  * 	argv => pointer to an argument
683  * 	attrlist => pointer to list of password attributes
684  */
685 
686 static int
687 ckarg(int argc, char **argv, attrlist **attributes)
688 {
689 	extern char	*optarg;
690 	char		*char_p;
691 	int	opt;
692 	int	flag;
693 
694 	flag = 0;
695 
696 	while ((opt = getopt(argc, argv, "r:aldefghsux:n:w:D:N")) != EOF) {
697 		switch (opt) {
698 
699 		case 'r': /* Repository Specified */
700 			/* repository: this option should be specified first */
701 
702 			if (repository.type != NULL) {
703 				(void) fprintf(stderr, gettext(
704 			"Repository is already defined or specified.\n"));
705 				rusage();
706 				retval = BADSYN;
707 				return (FAIL);
708 			}
709 			if (strcmp(optarg, "nisplus") == 0) {
710 				repository.type = optarg;
711 				repository.scope = nis_local_directory();
712 				if (repository.scope != NULL) {
713 					repository.scope_len =
714 					    strlen(repository.scope)+ 1;
715 				}
716 			} else if (strcmp(optarg, "nis") == 0) {
717 				repository.type = optarg;
718 			} else if (strcmp(optarg, "ldap") == 0) {
719 				repository.type = optarg;
720 			} else if (strcmp(optarg, "files") == 0) {
721 				repository.type = optarg;
722 			} else {
723 				(void) fprintf(stderr,
724 				    gettext("invalid repository: %s\n"),
725 				    optarg);
726 				rusage();
727 				retval = BADSYN;
728 				return (FAIL);
729 			}
730 			break;
731 
732 		case 'd': /* Delete Auth Token */
733 			/* if no repository the default for -d is files */
734 			if (repository.type == NULL)
735 				repository = __REPFILES;
736 
737 			/*
738 			 * Delete the password - only privileged processes
739 			 * can execute this for FILES
740 			 */
741 			if (IS_FILES(repository) == FALSE) {
742 				(void) fprintf(stderr, gettext(
743 				    "-d only applies to files repository\n"));
744 				rusage();	/* exit */
745 				retval = BADSYN;
746 				return (FAIL);
747 			}
748 
749 			if (ckuid() != SUCCESS) {
750 				retval = NOPERM;
751 				return (FAIL);
752 			}
753 			if (flag & (LFLAG|SAFLAG|DFLAG|XFLAG|UFLAG)) {
754 				rusage();
755 				retval = BADOPT;
756 				return (FAIL);
757 			}
758 			flag |= DFLAG;
759 			attrlist_add(attributes, ATTR_PASSWD, NULL);
760 			break;
761 
762 		case 'N': /* set account to be "no login" */
763 
764 			/* if no repository the default for -N is files */
765 			if (repository.type == NULL)
766 				repository = __REPFILES;
767 
768 			if (IS_FILES(repository) == FALSE &&
769 			    IS_NISPLUS(repository) == FALSE) {
770 				(void) fprintf(stderr, gettext(
771 			"-N only applies to files or nisplus repository\n"));
772 				rusage();	/* exit */
773 				retval = BADOPT;
774 				return (FAIL);
775 			}
776 
777 			/*
778 			 * Only privileged processes can execute this
779 			 * for FILES
780 			 */
781 			if (IS_FILES(repository) &&
782 				((retval = ckuid()) != SUCCESS))
783 				return (FAIL);
784 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
785 				rusage();	/* exit */
786 				retval = BADOPT;
787 				return (FAIL);
788 			}
789 			flag |= XFLAG;
790 			attrlist_add(attributes, ATTR_NOLOGIN_ACCOUNT, NULL);
791 			break;
792 
793 		case 'l': /* lock the password */
794 
795 			/* if no repository the default for -l is files */
796 			if (repository.type == NULL)
797 				repository = __REPFILES;
798 
799 			if (IS_FILES(repository) == FALSE &&
800 			    IS_NISPLUS(repository) == FALSE) {
801 				(void) fprintf(stderr, gettext(
802 			"-l only applies to files or nisplus repository\n"));
803 				rusage();	/* exit */
804 				retval = BADOPT;
805 				return (FAIL);
806 			}
807 
808 			/*
809 			 * Only privileged processes can execute this
810 			 * for FILES
811 			 */
812 			if (IS_FILES(repository) &&
813 				((retval = ckuid()) != SUCCESS))
814 				return (FAIL);
815 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
816 				rusage();	/* exit */
817 				retval = BADOPT;
818 				return (FAIL);
819 			}
820 			flag |= LFLAG;
821 			attrlist_add(attributes, ATTR_LOCK_ACCOUNT, NULL);
822 			break;
823 
824 		case 'u': /* unlock the password */
825 
826 			/* if no repository the default for -u is files */
827 			if (repository.type == NULL)
828 				repository = __REPFILES;
829 
830 			if (IS_FILES(repository) == FALSE &&
831 			    IS_NISPLUS(repository) == FALSE) {
832 				(void) fprintf(stderr, gettext(
833 			"-u only applies to files or nisplus repository\n"));
834 				rusage();	/* exit */
835 				retval = BADOPT;
836 				return (FAIL);
837 			}
838 
839 			/*
840 			 * Only privileged processes can execute this
841 			 * for FILES
842 			 */
843 			if (IS_FILES(repository) &&
844 				((retval = ckuid()) != SUCCESS))
845 				return (FAIL);
846 			if (flag & (MUTEXFLAG|NONAGEFLAG)) {
847 				rusage();	/* exit */
848 				retval = BADOPT;
849 				return (FAIL);
850 			}
851 			flag |= UFLAG;
852 			attrlist_add(attributes, ATTR_UNLOCK_ACCOUNT, NULL);
853 			attrlist_add(attributes, ATTR_RST_FAILED_LOGINS, NULL);
854 			break;
855 
856 		case 'x': /* set the max date */
857 
858 			/* if no repository the default for -x is files */
859 			if (repository.type == NULL)
860 				repository = __REPFILES;
861 
862 			if (IS_FILES(repository) == FALSE &&
863 			    IS_NISPLUS(repository) == FALSE) {
864 				(void) fprintf(stderr, gettext(
865 			"-x only applies to files or nisplus repository\n"));
866 				rusage();	/* exit */
867 				retval = BADSYN;
868 				return (FAIL);
869 			}
870 
871 			/*
872 			 * Only privileged process can execute this
873 			 * for FILES
874 			 */
875 			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
876 				retval = NOPERM;
877 				return (FAIL);
878 			}
879 			if (flag & (SAFLAG|MFLAG|NONAGEFLAG)) {
880 				retval = BADOPT;
881 				return (FAIL);
882 			}
883 			flag |= MFLAG;
884 			if ((int)strlen(optarg)  <= 0 ||
885 			    (maxdate = strtol(optarg, &char_p, 10)) < -1 ||
886 			    *char_p != '\0') {
887 				(void) fprintf(stderr, "%s: %s -x\n",
888 					prognamep, gettext(MSG_NV));
889 				retval = BADSYN;
890 				return (FAIL);
891 			}
892 			attrlist_add(attributes, ATTR_MAX, optarg);
893 			break;
894 
895 		case 'n': /* set the min date */
896 
897 			/* if no repository the default for -n is files */
898 			if (repository.type == NULL)
899 				repository = __REPFILES;
900 
901 			if (IS_FILES(repository) == FALSE &&
902 			    IS_NISPLUS(repository) == FALSE) {
903 				(void) fprintf(stderr, gettext(
904 			"-n only applies to files or nisplus repository\n"));
905 				rusage();	/* exit */
906 				retval = BADSYN;
907 				return (FAIL);
908 			}
909 
910 			/*
911 			 * Only privileged process can execute this
912 			 * for FILES
913 			 */
914 			if (IS_FILES(repository) &&
915 				((retval = ckuid()) != SUCCESS))
916 				return (FAIL);
917 			if (flag & (SAFLAG|NFLAG|NONAGEFLAG)) {
918 				retval = BADOPT;
919 				return (FAIL);
920 			}
921 			flag |= NFLAG;
922 			if ((int)strlen(optarg)  <= 0 ||
923 			    (strtol(optarg, &char_p, 10)) < 0 ||
924 			    *char_p != '\0') {
925 				(void) fprintf(stderr, "%s: %s -n\n",
926 					prognamep, gettext(MSG_NV));
927 				retval = BADSYN;
928 				return (FAIL);
929 			}
930 			attrlist_add(attributes, ATTR_MIN, optarg);
931 			break;
932 
933 		case 'w': /* set the warning field */
934 
935 			/* if no repository the default for -w is files */
936 			if (repository.type == NULL)
937 				repository = __REPFILES;
938 
939 			if (IS_FILES(repository) == FALSE &&
940 			    IS_NISPLUS(repository) == FALSE) {
941 				(void) fprintf(stderr, gettext(
942 			"-w only applies to files or nisplus repository\n"));
943 				rusage();	/* exit */
944 				retval = BADSYN;
945 				return (FAIL);
946 			}
947 
948 			/*
949 			 * Only privileged process can execute this
950 			 * for FILES
951 			 */
952 			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
953 				retval = NOPERM;
954 				return (FAIL);
955 			}
956 			if (flag & (SAFLAG|WFLAG|NONAGEFLAG)) {
957 				retval = BADOPT;
958 				return (FAIL);
959 			}
960 			flag |= WFLAG;
961 			if ((int)strlen(optarg)  <= 0 ||
962 			    (strtol(optarg, &char_p, 10)) < 0 ||
963 			    *char_p != '\0') {
964 				(void) fprintf(stderr, "%s: %s -w\n",
965 					prognamep, gettext(MSG_NV));
966 				retval = BADSYN;
967 				return (FAIL);
968 			}
969 			attrlist_add(attributes, ATTR_WARN, optarg);
970 			break;
971 
972 		case 's': /* display password attributes */
973 
974 			/* if no repository the default for -s is files */
975 			if (repository.type == NULL)
976 				repository = __REPFILES;
977 
978 
979 			/* display password attributes */
980 			if (IS_FILES(repository) == FALSE &&
981 			    IS_NISPLUS(repository) == FALSE) {
982 				(void) fprintf(stderr, gettext(
983 			"-s only applies to files or nisplus repository\n"));
984 				rusage();	/* exit */
985 				retval = BADSYN;
986 				return (FAIL);
987 			}
988 
989 			/*
990 			 * Only privileged process can execute this
991 			 * for FILES
992 			 */
993 			if (IS_FILES(repository) &&
994 				((retval = ckuid()) != SUCCESS))
995 				return (FAIL);
996 			if (flag && (flag != AFLAG)) {
997 				retval = BADOPT;
998 				return (FAIL);
999 			}
1000 			flag |= SFLAG;
1001 			break;
1002 
1003 		case 'a': /* display password attributes */
1004 
1005 			/* if no repository the default for -a is files */
1006 			if (repository.type == NULL)
1007 				repository = __REPFILES;
1008 
1009 			if (IS_FILES(repository) == FALSE &&
1010 			    IS_NISPLUS(repository) == FALSE) {
1011 				(void) fprintf(stderr, gettext(
1012 			"-a only applies to files or nisplus repository\n"));
1013 				rusage();	/* exit */
1014 				retval = BADSYN;
1015 				return (FAIL);
1016 			}
1017 
1018 			/*
1019 			 * Only privileged process can execute this
1020 			 * for FILES
1021 			 */
1022 			if (IS_FILES(repository) &&
1023 				((retval = ckuid()) != SUCCESS))
1024 				return (FAIL);
1025 			if (flag && (flag != SFLAG)) {
1026 				retval = BADOPT;
1027 				return (FAIL);
1028 			}
1029 			flag |= AFLAG;
1030 			break;
1031 
1032 		case 'f': /* expire password attributes	*/
1033 
1034 			/* if no repository the default for -f is files */
1035 			if (repository.type == NULL)
1036 				repository = __REPFILES;
1037 
1038 			if (IS_FILES(repository) == FALSE &&
1039 			    IS_NISPLUS(repository) == FALSE) {
1040 				(void) fprintf(stderr, gettext(
1041 			"-f only applies to files or nisplus repository\n"));
1042 				rusage();	/* exit */
1043 				retval = BADSYN;
1044 				return (FAIL);
1045 			}
1046 
1047 			/*
1048 			 * Only privileged process can execute this
1049 			 * for FILES
1050 			 */
1051 			if (IS_FILES(repository) &&
1052 				((retval = ckuid()) != SUCCESS))
1053 				return (FAIL);
1054 			if (flag & (SAFLAG|FFLAG|NONAGEFLAG)) {
1055 				retval = BADOPT;
1056 				return (FAIL);
1057 			}
1058 			flag |= FFLAG;
1059 			attrlist_add(attributes, ATTR_EXPIRE_PASSWORD, NULL);
1060 			break;
1061 
1062 		case 'D': /* domain name specified */
1063 			if (IS_NISPLUS(repository) == FALSE) {
1064 				(void) fprintf(stderr, gettext(
1065 				    "-D only applies to nisplus repository\n"));
1066 				rusage();	/* exit */
1067 				retval = BADSYN;
1068 				return (FAIL);
1069 			}
1070 
1071 			if (flag & AFLAG) {
1072 				retval = BADOPT;
1073 				return (FAIL);
1074 			}
1075 			/* It is cleaner not to set this flag */
1076 			/* flag |= OFLAG; */
1077 
1078 			/* get domain from optarg */
1079 			repository.scope = optarg;
1080 			if (repository.scope != NULL) {
1081 				repository.scope_len =
1082 				    strlen(repository.scope)+1;
1083 			}
1084 			break;
1085 
1086 		case 'e': /* change login shell */
1087 
1088 			/* if no repository the default for -e is files */
1089 			if (repository.type == NULL)
1090 				repository = __REPFILES;
1091 
1092 			/*
1093 			 * Only privileged process can execute this
1094 			 * for FILES
1095 			 */
1096 			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1097 				retval = NOPERM;
1098 				return (FAIL);
1099 			}
1100 			if (flag & (EFLAG|SAFLAG|AGEFLAG)) {
1101 				retval = BADOPT;
1102 				return (FAIL);
1103 			}
1104 			flag |= EFLAG;
1105 			break;
1106 
1107 		case 'g': /* change gecos information */
1108 
1109 			/* if no repository the default for -g is files */
1110 			if (repository.type == NULL)
1111 				repository = __REPFILES;
1112 
1113 			/*
1114 			 * Only privileged process can execute this
1115 			 * for FILES
1116 			 */
1117 			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1118 				retval = NOPERM;
1119 				return (FAIL);
1120 			}
1121 			if (flag & (GFLAG|SAFLAG|AGEFLAG)) {
1122 				retval = BADOPT;
1123 				return (FAIL);
1124 			}
1125 			flag |= GFLAG;
1126 			break;
1127 
1128 		case 'h': /* change home dir */
1129 
1130 			/* if no repository the default for -h is files */
1131 			if (repository.type == NULL)
1132 				repository = __REPFILES;
1133 			/*
1134 			 * Only privileged process can execute this
1135 			 * for FILES
1136 			 */
1137 			if (IS_FILES(repository) && (ckuid() != SUCCESS)) {
1138 				retval = NOPERM;
1139 				return (FAIL);
1140 			}
1141 			if (IS_NIS(repository)) {
1142 				(void) fprintf(stderr, "%s\n",
1143 				    gettext(MSG_NIS_HOMEDIR));
1144 				retval = BADSYN;
1145 				return (FAIL);
1146 			}
1147 
1148 			if (flag & (HFLAG|SAFLAG|AGEFLAG)) {
1149 				retval = BADOPT;
1150 				return (FAIL);
1151 			}
1152 			flag |= HFLAG;
1153 			break;
1154 
1155 		case '?':
1156 			rusage();
1157 			retval = BADOPT;
1158 			return (FAIL);
1159 		}
1160 	}
1161 
1162 	argc -= optind;
1163 	if (argc > 1) {
1164 		rusage();
1165 		retval = BADSYN;
1166 		return (FAIL);
1167 	}
1168 
1169 	/* Make sure (EXPIRE comes after (MAX comes after MIN)) */
1170 	attrlist_reorder(attributes);
1171 
1172 	/* If no options are specified or only the show option */
1173 	/* is specified, return because no option error checking */
1174 	/* is needed */
1175 	if (!flag || (flag == SFLAG))
1176 		return (flag);
1177 
1178 	/* AFLAG must be used with SFLAG */
1179 	if (flag == AFLAG) {
1180 		rusage();
1181 		retval = BADSYN;
1182 		return (FAIL);
1183 	}
1184 
1185 	if (flag != SAFLAG && argc < 1) {
1186 		/*
1187 		 * user name is not specified (argc<1), it can't be
1188 		 * aging info update.
1189 		 */
1190 		if (!(flag & NONAGEFLAG)) {
1191 			rusage();
1192 			retval = BADSYN;
1193 			return (FAIL);
1194 		}
1195 	}
1196 
1197 	/* user name(s) may not be specified when SAFLAG is used. */
1198 	if (flag == SAFLAG && argc >= 1) {
1199 		rusage();
1200 		retval = BADSYN;
1201 		return (FAIL);
1202 	}
1203 
1204 	/*
1205 	 * If aging is being turned off (maxdate == -1), mindate may not
1206 	 * be specified.
1207 	 */
1208 	if ((maxdate == -1) && (flag & NFLAG)) {
1209 		(void) fprintf(stderr, "%s: %s -n\n",
1210 				prognamep, gettext(MSG_NV));
1211 		retval = BADOPT;
1212 		return (FAIL);
1213 	}
1214 
1215 	return (flag);
1216 }
1217 
1218 /*
1219  *
1220  * ckuid():
1221  *	This function returns SUCCESS if the caller is root, else
1222  *	it returns NOPERM.
1223  *
1224  */
1225 
1226 static int
1227 ckuid()
1228 {
1229 	if (uid != 0) {
1230 		return (retval = NOPERM);
1231 	}
1232 	return (SUCCESS);
1233 }
1234 
1235 
1236 /*
1237  * get_attr()
1238  */
1239 int
1240 get_attr(char *username, pwu_repository_t *repository, attrlist **attributes)
1241 {
1242 	int res;
1243 
1244 	attrlist_add(attributes, ATTR_PASSWD, NULL);
1245 	attrlist_add(attributes, ATTR_LSTCHG, "0");
1246 	attrlist_add(attributes, ATTR_MIN, "0");
1247 	attrlist_add(attributes, ATTR_MAX, "0");
1248 	attrlist_add(attributes, ATTR_WARN, "0");
1249 
1250 	res = __get_authtoken_attr(username, repository, *attributes);
1251 
1252 	if (res == PWU_SUCCESS) {
1253 		retval = SUCCESS;
1254 		return (PWU_SUCCESS);
1255 	}
1256 
1257 	if (res == PWU_NOT_FOUND)
1258 		(void) fprintf(stderr, gettext(MSG_UNKNOWN), prognamep,
1259 		    username);
1260 
1261 	retval = NOPERM;
1262 	passwd_exit(retval);
1263 	/*NOTREACHED*/
1264 }
1265 
1266 /*
1267  * display_attr():
1268  * This function prints out the password attributes of a usr
1269  * onto standand output.
1270  */
1271 void
1272 display_attr(char *usrname, attrlist *attributes)
1273 {
1274 	char	*status;
1275 	char	*passwd;
1276 	long	lstchg;
1277 	int	min, max, warn;
1278 
1279 	while (attributes) {
1280 		switch (attributes->type) {
1281 		case ATTR_PASSWD:
1282 			passwd = attributes->data.val_s;
1283 			if (passwd == NULL || *passwd == '\0')
1284 				status = "NP  ";
1285 			else if (strncmp(passwd, LOCKSTRING,
1286 			    sizeof (LOCKSTRING)-1) == 0)
1287 				status = "LK  ";
1288 			else if (strncmp(passwd, NOLOGINSTRING,
1289 			    sizeof (NOLOGINSTRING)-1) == 0)
1290 				status = "NL  ";
1291 			else
1292 				status = "PS  ";
1293 			break;
1294 		case ATTR_LSTCHG:
1295 			lstchg = attributes->data.val_i * DAY;
1296 			break;
1297 		case ATTR_MIN:
1298 			min = attributes->data.val_i;
1299 			break;
1300 		case ATTR_MAX:
1301 			max = attributes->data.val_i;
1302 			break;
1303 		case ATTR_WARN:
1304 			warn = attributes->data.val_i;
1305 			break;
1306 		}
1307 		attributes = attributes->next;
1308 	}
1309 	(void) fprintf(stdout, "%-8s  ", usrname);
1310 
1311 	if (status)
1312 		(void) fprintf(stdout, "%s  ", status);
1313 
1314 	if (max != -1) {
1315 		if (lstchg == 0) {
1316 			(void) fprintf(stdout, "00/00/00  ");
1317 		} else {
1318 			struct tm *tmp;
1319 			tmp = gmtime(&lstchg);
1320 			(void) fprintf(stdout, "%.2d/%.2d/%.2d  ",
1321 			    tmp->tm_mon + 1,
1322 			    tmp->tm_mday,
1323 			    tmp->tm_year % 100);
1324 		}
1325 		(void) fprintf(stdout, (min >= 0) ? "%4d  " : "      ", min);
1326 		(void) fprintf(stdout, "%4d  ", max);
1327 		(void) fprintf(stdout, (warn > 0) ? "%4d  " : "      ", warn);
1328 	}
1329 	(void) fprintf(stdout, "\n");
1330 }
1331 
1332 void
1333 free_attr(attrlist *attributes)
1334 {
1335 	while (attributes) {
1336 		if (attributes->type == ATTR_PASSWD)
1337 			free(attributes->data.val_s);
1338 		attributes = attributes->next;
1339 	}
1340 }
1341 
1342 /*
1343  *
1344  * get_namelist_files():
1345  *	This function gets a list of user names on the system from
1346  *	the /etc/passwd file.
1347  *
1348  */
1349 int
1350 get_namelist_files(namelist_p, num_user)
1351 	char ***namelist_p;
1352 	int *num_user;
1353 {
1354 	FILE		*pwfp;
1355 	struct passwd	*pwd;
1356 	int		max_user;
1357 	int		nuser;
1358 	char	**nl;
1359 
1360 	nuser = 0;
1361 	errno = 0;
1362 	pwd = NULL;
1363 
1364 	if ((pwfp = fopen(PASSWD, "r")) == NULL)
1365 		return (NOPERM);
1366 
1367 	/*
1368 	 * find out the actual number of entries in the PASSWD file
1369 	 */
1370 	max_user = 1;			/* need one slot for terminator NULL */
1371 	while ((pwd = fgetpwent(pwfp)) != NULL)
1372 		max_user++;
1373 
1374 	/*
1375 	 *	reset the file stream pointer
1376 	 */
1377 	rewind(pwfp);
1378 
1379 	nl = (char **)calloc(max_user, (sizeof (char *)));
1380 	if (nl == NULL) {
1381 		(void) fclose(pwfp);
1382 		return (FMERR);
1383 	}
1384 
1385 	while ((pwd = fgetpwent(pwfp)) != NULL) {
1386 		if ((nl[nuser] = strdup(pwd->pw_name)) == NULL) {
1387 			(void) fclose(pwfp);
1388 			return (FMERR);
1389 		}
1390 		nuser++;
1391 	}
1392 
1393 	nl[nuser] = NULL;
1394 	*num_user = nuser;
1395 	*namelist_p = nl;
1396 	(void) fclose(pwfp);
1397 	return (SUCCESS);
1398 }
1399 
1400 /*
1401  * get_namelist_nisplus
1402  *
1403  */
1404 
1405 /*
1406  * Our private version of the switch frontend for getspent.  We want to
1407  * search just the nisplus sp file, so we want to bypass normal nsswitch.conf
1408  * based processing.  This implementation compatible with version 2 of the
1409  * name service switch.
1410  */
1411 #define	NSS_NISPLUS_ONLY	"nisplus"
1412 
1413 extern int str2spwd(const char *, int, void *, char *, int);
1414 
1415 static DEFINE_NSS_DB_ROOT(db_root);
1416 static DEFINE_NSS_GETENT(context);
1417 
1418 static void
1419 _np_nss_initf_shadow(p)
1420 	nss_db_params_t	*p;
1421 {
1422 	p->name	= NSS_DBNAM_SHADOW;
1423 	p->config_name    = NSS_DBNAM_PASSWD;	/* Use config for "passwd" */
1424 	p->default_config = NSS_NISPLUS_ONLY;   /* Use nisplus only */
1425 	p->flags = NSS_USE_DEFAULT_CONFIG;
1426 }
1427 
1428 static void
1429 _np_setspent()
1430 {
1431 	nss_setent(&db_root, _np_nss_initf_shadow, &context);
1432 }
1433 
1434 static void
1435 _np_endspent()
1436 {
1437 	nss_endent(&db_root, _np_nss_initf_shadow, &context);
1438 	nss_delete(&db_root);
1439 }
1440 
1441 static struct spwd *
1442 _np_getspent_r(result, buffer, buflen)
1443 	struct spwd	*result;
1444 	char		*buffer;
1445 	int		buflen;
1446 {
1447 	nss_XbyY_args_t arg;
1448 	char		*nam;
1449 
1450 	/* In getXXent_r(), protect the unsuspecting caller from +/- entries */
1451 
1452 	do {
1453 		NSS_XbyY_INIT(&arg, result, buffer, buflen, str2spwd);
1454 			/* No key to fill in */
1455 		(void) nss_getent(&db_root, _np_nss_initf_shadow, &context,
1456 		    &arg);
1457 	} while (arg.returnval != 0 &&
1458 			(nam = ((struct spwd *)arg.returnval)->sp_namp) != 0 &&
1459 			(*nam == '+' || *nam == '-'));
1460 
1461 	return (struct spwd *)NSS_XbyY_FINI(&arg);
1462 }
1463 
1464 static nss_XbyY_buf_t *buffer;
1465 
1466 static struct spwd *
1467 _np_getspent()
1468 {
1469 	nss_XbyY_buf_t	*b;
1470 
1471 	b = NSS_XbyY_ALLOC(&buffer, sizeof (struct spwd), NSS_BUFLEN_SHADOW);
1472 
1473 	return (b == 0 ? 0 : _np_getspent_r(b->result, b->buffer, b->buflen));
1474 }
1475 
1476 int
1477 get_namelist_nisplus(char ***namelist_p, int *num_user)
1478 {
1479 	int nuser = 0;
1480 	int alloced = 100;
1481 	char **nl;
1482 	struct spwd *p;
1483 
1484 
1485 	if ((nl = calloc(alloced, sizeof (*nl))) == NULL)
1486 		return (FMERR);
1487 
1488 	(void) _np_setspent();
1489 	while ((p = _np_getspent()) != NULL) {
1490 		if ((nl[nuser] = strdup(p->sp_namp)) == NULL) {
1491 			_np_endspent();
1492 			return (FMERR);
1493 		}
1494 		if (++nuser == alloced) {
1495 			alloced += 100;
1496 			nl = realloc(nl, alloced * (sizeof (*nl)));
1497 			if (nl == NULL) {
1498 				_np_endspent();
1499 				return (FMERR);
1500 			}
1501 		}
1502 	}
1503 	(void) _np_endspent();
1504 	nl[nuser] = NULL;
1505 
1506 	*namelist_p = nl;
1507 	*num_user = nuser;		/* including NULL */
1508 
1509 	return (SUCCESS);
1510 }
1511 
1512 int
1513 get_namelist(pwu_repository_t repository, char ***namelist, int *num_user)
1514 {
1515 	if (IS_NISPLUS(repository))
1516 		return (get_namelist_nisplus(namelist, num_user));
1517 	else if (IS_FILES(repository))
1518 		return (get_namelist_files(namelist, num_user));
1519 
1520 	rusage();
1521 	return (BADSYN);
1522 }
1523 
1524 /*
1525  *
1526  * passwd_exit():
1527  *	This function will call exit() with appropriate exit code
1528  *	according to the input "retcode" value.
1529  *	It also calls pam_end() to clean-up buffers before exit.
1530  *
1531  */
1532 
1533 void
1534 passwd_exit(retcode)
1535 	int	retcode;
1536 {
1537 
1538 	if (pamh)
1539 		(void) pam_end(pamh, pam_retval);
1540 
1541 	switch (retcode) {
1542 	case SUCCESS:
1543 			break;
1544 	case NOPERM:
1545 			(void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1546 			break;
1547 	case BADOPT:
1548 			(void) fprintf(stderr, "%s\n", gettext(MSG_BS));
1549 			break;
1550 	case FMERR:
1551 			(void) fprintf(stderr, "%s\n", gettext(MSG_FE));
1552 			break;
1553 	case FATAL:
1554 			(void) fprintf(stderr, "%s\n", gettext(MSG_FF));
1555 			break;
1556 	case FBUSY:
1557 			(void) fprintf(stderr, "%s\n", gettext(MSG_FB));
1558 			break;
1559 	case BADSYN:
1560 			(void) fprintf(stderr, "%s\n", gettext(MSG_NV));
1561 			break;
1562 	case BADAGE:
1563 			(void) fprintf(stderr, "%s\n", gettext(MSG_AD));
1564 			break;
1565 	case NOMEM:
1566 			(void) fprintf(stderr, "%s\n", gettext(MSG_NM));
1567 			break;
1568 	default:
1569 			(void) fprintf(stderr, "%s\n", gettext(MSG_NP));
1570 			retcode = NOPERM;
1571 			break;
1572 	}
1573 	/* write password record */
1574 	if (event != NULL) {
1575 		if (adt_put_event(event,
1576 		    retcode == SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
1577 		    retcode == SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM +
1578 		    pam_retval) != 0) {
1579 			adt_free_event(event);
1580 			(void) adt_end_session(ah);
1581 			perror("adt_put_event");
1582 			exit(retcode);
1583 		}
1584 		adt_free_event(event);
1585 	}
1586 	(void) adt_end_session(ah);
1587 	exit(retcode);
1588 }
1589 
1590 /*
1591  *
1592  * passwd_conv():
1593  *	This is the conv (conversation) function called from
1594  *	a PAM authentication module to print error messages
1595  *	or garner information from the user.
1596  *
1597  */
1598 
1599 /*ARGSUSED*/
1600 static int
1601 passwd_conv(num_msg, msg, response, appdata_ptr)
1602 	int num_msg;
1603 	struct pam_message **msg;
1604 	struct pam_response **response;
1605 	void *appdata_ptr;
1606 {
1607 	struct pam_message	*m;
1608 	struct pam_response	*r;
1609 	char 			*temp;
1610 	int			k, i;
1611 
1612 	if (num_msg <= 0)
1613 		return (PAM_CONV_ERR);
1614 
1615 	*response = (struct pam_response *)calloc(num_msg,
1616 						sizeof (struct pam_response));
1617 	if (*response == NULL)
1618 		return (PAM_BUF_ERR);
1619 
1620 	k = num_msg;
1621 	m = *msg;
1622 	r = *response;
1623 	while (k--) {
1624 
1625 		switch (m->msg_style) {
1626 
1627 		case PAM_PROMPT_ECHO_OFF:
1628 			temp = getpassphrase(m->msg);
1629 			if (temp != NULL) {
1630 				r->resp = strdup(temp);
1631 				(void) memset(temp, 0, strlen(temp));
1632 				if (r->resp == NULL) {
1633 					/* free responses */
1634 					r = *response;
1635 					for (i = 0; i < num_msg; i++, r++) {
1636 						if (r->resp)
1637 							free(r->resp);
1638 					}
1639 					free(*response);
1640 					*response = NULL;
1641 					return (PAM_BUF_ERR);
1642 				}
1643 			}
1644 			m++;
1645 			r++;
1646 			break;
1647 
1648 		case PAM_PROMPT_ECHO_ON:
1649 			if (m->msg != NULL) {
1650 				(void) fputs(m->msg, stdout);
1651 			}
1652 			r->resp = (char *)calloc(PAM_MAX_RESP_SIZE,
1653 							sizeof (char));
1654 			if (r->resp == NULL) {
1655 				/* free responses */
1656 				r = *response;
1657 				for (i = 0; i < num_msg; i++, r++) {
1658 					if (r->resp)
1659 						free(r->resp);
1660 				}
1661 				free(*response);
1662 				*response = NULL;
1663 				return (PAM_BUF_ERR);
1664 			}
1665 			if (fgets(r->resp, PAM_MAX_RESP_SIZE-1, stdin)) {
1666 				int len = strlen(r->resp);
1667 				if (r->resp[len-1] == '\n')
1668 					r->resp[len-1] = '\0';
1669 			}
1670 			m++;
1671 			r++;
1672 			break;
1673 
1674 		case PAM_ERROR_MSG:
1675 			if (m->msg != NULL) {
1676 				(void) fputs(m->msg, stderr);
1677 				(void) fputs("\n", stderr);
1678 			}
1679 			m++;
1680 			r++;
1681 			break;
1682 		case PAM_TEXT_INFO:
1683 			if (m->msg != NULL) {
1684 				(void) fputs(m->msg, stdout);
1685 				(void) fputs("\n", stdout);
1686 			}
1687 			m++;
1688 			r++;
1689 			break;
1690 
1691 		default:
1692 			break;
1693 		}
1694 	}
1695 	return (PAM_SUCCESS);
1696 }
1697 
1698 /*
1699  * 		Utilities Functions
1700  */
1701 
1702 /*
1703  * int attrlist_add(attrlist **l, attrtype type, char *val)
1704  * add an item, with type "type" and value "val", at the tail of list l.
1705  * This functions exits the application on OutOfMem error.
1706  */
1707 void
1708 attrlist_add(attrlist **l, attrtype type, char *val)
1709 {
1710 	attrlist **w;
1711 
1712 	/* tail insert */
1713 	for (w = l; *w != NULL; w = &(*w)->next)
1714 		;
1715 
1716 	if ((*w = malloc(sizeof (**w))) == NULL)
1717 		passwd_exit(NOMEM);
1718 
1719 	(*w)->type = type;
1720 	(*w)->next = NULL;
1721 
1722 	switch (type) {
1723 	case ATTR_MIN:
1724 	case ATTR_WARN:
1725 	case ATTR_MAX:
1726 		(*w)->data.val_i = atoi(val);
1727 		break;
1728 	default:
1729 		(*w)->data.val_s = val;
1730 		break;
1731 	}
1732 }
1733 
1734 /*
1735  * attrlist_reorder(attrlist **l)
1736  * Make sure that
1737  * 	- if EXPIRE and MAX or MIN is set, EXPIRE comes after MAX/MIN
1738  *	- if both MIN and MAX are set, MAX comes before MIN.
1739  */
1740 
1741 static void
1742 attrlist_reorder(attrlist **l)
1743 {
1744 	attrlist	**w;
1745 	attrlist	*exp = NULL;	/* ATTR_EXPIRE_PASSWORD, if found */
1746 	attrlist	*max = NULL;	/* ATTR_MAX, if found */
1747 
1748 	if (*l == NULL || (*l)->next == NULL)
1749 		return;		/* order of list with <= one item is ok */
1750 
1751 	/*
1752 	 * We simply walk the list, take off the EXPIRE and MAX items if
1753 	 * they appear, and put them (first MAX, them EXPIRE) at the end
1754 	 * of the list.
1755 	 */
1756 	w = l;
1757 	while (*w != NULL) {
1758 		if ((*w)->type == ATTR_EXPIRE_PASSWORD) {
1759 			exp = *w;
1760 			*w = (*w)->next;
1761 		} else if ((*w)->type == ATTR_MAX) {
1762 			max = *w;
1763 			*w = (*w)->next;
1764 		} else
1765 			w = &(*w)->next;
1766 	}
1767 
1768 	/* 'w' points to the address of the 'next' field of the last element */
1769 
1770 	if (max) {
1771 		*w = max;
1772 		w = &max->next;
1773 	}
1774 	if (exp) {
1775 		*w = exp;
1776 		w = &exp->next;
1777 	}
1778 	*w = NULL;
1779 }
1780 
1781 void
1782 rusage()
1783 {
1784 
1785 #define	MSG(a) (void) fprintf(stderr, gettext((a)));
1786 
1787 	MSG("usage:\n");
1788 	MSG("\tpasswd [-r files | -r nis | -r nisplus | -r ldap] [name]\n");
1789 	MSG("\tpasswd [-r files] [-egh] [name]\n");
1790 	MSG("\tpasswd [-r files] -sa\n");
1791 	MSG("\tpasswd [-r files] -s [name]\n");
1792 	MSG("\tpasswd [-r files] [-d|-l|-N|-u] [-f] [-n min] [-w warn] "
1793 	    "[-x max] name\n");
1794 	MSG("\tpasswd -r nis [-eg] [name]\n");
1795 	MSG("\tpasswd -r nisplus [-egh] [-D domainname] [name]\n");
1796 	MSG("\tpasswd -r nisplus -sa\n");
1797 	MSG("\tpasswd -r nisplus [-D domainname] -s [name]\n");
1798 	MSG("\tpasswd -r nisplus [-D domainname] [-l|-N|-u] [-f] [-n min] "
1799 	    "[-w warn]\n");
1800 	MSG("\t\t[-x max] name\n");
1801 	MSG("\tpasswd -r ldap [-egh] [name]\n");
1802 #undef MSG
1803 }
1804