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