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