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