xref: /freebsd/lib/libpam/modules/pam_unix/pam_unix.c (revision 3d55a6c083af31ed5ba3e418f39f902728a15f9a)
19a10bb17SJohn Polstra /*-
29a10bb17SJohn Polstra  * Copyright 1998 Juniper Networks, Inc.
39a10bb17SJohn Polstra  * All rights reserved.
49a10bb17SJohn Polstra  *
59a10bb17SJohn Polstra  * Redistribution and use in source and binary forms, with or without
69a10bb17SJohn Polstra  * modification, are permitted provided that the following conditions
79a10bb17SJohn Polstra  * are met:
89a10bb17SJohn Polstra  * 1. Redistributions of source code must retain the above copyright
99a10bb17SJohn Polstra  *    notice, this list of conditions and the following disclaimer.
109a10bb17SJohn Polstra  * 2. Redistributions in binary form must reproduce the above copyright
119a10bb17SJohn Polstra  *    notice, this list of conditions and the following disclaimer in the
129a10bb17SJohn Polstra  *    documentation and/or other materials provided with the distribution.
139a10bb17SJohn Polstra  *
149a10bb17SJohn Polstra  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159a10bb17SJohn Polstra  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169a10bb17SJohn Polstra  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179a10bb17SJohn Polstra  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189a10bb17SJohn Polstra  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199a10bb17SJohn Polstra  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209a10bb17SJohn Polstra  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219a10bb17SJohn Polstra  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229a10bb17SJohn Polstra  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239a10bb17SJohn Polstra  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249a10bb17SJohn Polstra  * SUCH DAMAGE.
259a10bb17SJohn Polstra  *
269a10bb17SJohn Polstra  *	$FreeBSD$
279a10bb17SJohn Polstra  */
289a10bb17SJohn Polstra 
299a10bb17SJohn Polstra #include <sys/types.h>
30d65b34dbSJohn Polstra #include <sys/time.h>
313d55a6c0SMark Murray #ifdef YP
323d55a6c0SMark Murray #include <rpc/rpc.h>
333d55a6c0SMark Murray #include <rpcsvc/yp_prot.h>
343d55a6c0SMark Murray #include <rpcsvc/ypclnt.h>
353d55a6c0SMark Murray #include <rpcsvc/yppasswd.h>
363d55a6c0SMark Murray #endif
37d65b34dbSJohn Polstra #include <login_cap.h>
389a10bb17SJohn Polstra #include <pwd.h>
399a10bb17SJohn Polstra #include <stdlib.h>
409a10bb17SJohn Polstra #include <string.h>
41d65b34dbSJohn Polstra #include <stdio.h>
429a10bb17SJohn Polstra #include <unistd.h>
439a10bb17SJohn Polstra 
443d55a6c0SMark Murray #include <pw_copy.h>
453d55a6c0SMark Murray #include <pw_util.h>
463d55a6c0SMark Murray 
473d55a6c0SMark Murray #ifdef YP
483d55a6c0SMark Murray #include <pw_yp.h>
493d55a6c0SMark Murray #include "yppasswd_private.h"
503d55a6c0SMark Murray #endif
513d55a6c0SMark Murray 
529a10bb17SJohn Polstra #define PAM_SM_AUTH
53d65b34dbSJohn Polstra #define PAM_SM_ACCOUNT
543d55a6c0SMark Murray #define	PAM_SM_SESSION
553d55a6c0SMark Murray #define	PAM_SM_PASSWORD
563d55a6c0SMark Murray 
579a10bb17SJohn Polstra #include <security/pam_modules.h>
589a10bb17SJohn Polstra 
599a10bb17SJohn Polstra #include "pam_mod_misc.h"
609a10bb17SJohn Polstra 
613d55a6c0SMark Murray #define USER_PROMPT		"Username: "
629a10bb17SJohn Polstra #define PASSWORD_PROMPT		"Password: "
633d55a6c0SMark Murray #define PASSWORD_PROMPT_EXPIRED	"\nPassword expired\nOld Password: "
643d55a6c0SMark Murray #define NEW_PASSWORD_PROMPT_1	"New Password: "
653d55a6c0SMark Murray #define NEW_PASSWORD_PROMPT_2	"New Password (again): "
663d55a6c0SMark Murray #define PASSWORD_HASH		"md5"
671642eb1aSMark Murray #define DEFAULT_WARN		(2L * 7L * 86400L)  /* Two weeks */
683d55a6c0SMark Murray #define	MAX_TRIES		3
691642eb1aSMark Murray 
703d55a6c0SMark Murray enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK, PAM_OPT_LOCAL_PASS, PAM_OPT_NIS_PASS };
711642eb1aSMark Murray 
721642eb1aSMark Murray static struct opttab other_options[] = {
731642eb1aSMark Murray 	{ "auth_as_self",	PAM_OPT_AUTH_AS_SELF },
741642eb1aSMark Murray 	{ "nullok",		PAM_OPT_NULLOK },
753d55a6c0SMark Murray 	{ "local_pass",		PAM_OPT_LOCAL_PASS },
763d55a6c0SMark Murray 	{ "nis_pass",		PAM_OPT_NIS_PASS },
771642eb1aSMark Murray 	{ NULL, 0 }
781642eb1aSMark Murray };
799a10bb17SJohn Polstra 
803d55a6c0SMark Murray #ifdef YP
813d55a6c0SMark Murray int pam_use_yp = 0;
823d55a6c0SMark Murray int yp_errno = YP_TRUE;
833d55a6c0SMark Murray #endif
843d55a6c0SMark Murray 
853d55a6c0SMark Murray char *tempname = NULL;
863d55a6c0SMark Murray static int local_passwd(const char *user, const char *pass);
873d55a6c0SMark Murray #ifdef YP
883d55a6c0SMark Murray static int yp_passwd(const char *user, const char *pass);
893d55a6c0SMark Murray #endif
903d55a6c0SMark Murray 
91d65b34dbSJohn Polstra /*
92d65b34dbSJohn Polstra  * authentication management
93d65b34dbSJohn Polstra  */
949a10bb17SJohn Polstra PAM_EXTERN int
951642eb1aSMark Murray pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
969a10bb17SJohn Polstra {
973d55a6c0SMark Murray 	login_cap_t *lc;
981642eb1aSMark Murray 	struct options options;
999a10bb17SJohn Polstra 	struct passwd *pwd;
1001642eb1aSMark Murray 	int retval;
1013d55a6c0SMark Murray 	const char *pass, *user;
1023d55a6c0SMark Murray 	char *encrypted, *password_prompt;
1039a10bb17SJohn Polstra 
1041642eb1aSMark Murray 	pam_std_option(&options, other_options, argc, argv);
1051642eb1aSMark Murray 
1061642eb1aSMark Murray 	PAM_LOG("Options processed");
1071642eb1aSMark Murray 
1081642eb1aSMark Murray 	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
1093d55a6c0SMark Murray 		pwd = getpwnam(getlogin());
1104448b21cSMark Murray 	else {
1111642eb1aSMark Murray 		retval = pam_get_user(pamh, &user, NULL);
1121642eb1aSMark Murray 		if (retval != PAM_SUCCESS)
1131642eb1aSMark Murray 			PAM_RETURN(retval);
1144448b21cSMark Murray 		pwd = getpwnam(user);
1154448b21cSMark Murray 	}
1161642eb1aSMark Murray 
1171642eb1aSMark Murray 	PAM_LOG("Got user: %s", user);
1181642eb1aSMark Murray 
1193d55a6c0SMark Murray 	lc = login_getclass(NULL);
1203d55a6c0SMark Murray 	password_prompt = login_getcapstr(lc, "passwd_prompt",
1213d55a6c0SMark Murray 	    PASSWORD_PROMPT, PASSWORD_PROMPT);
1223d55a6c0SMark Murray 	login_close(lc);
1233d55a6c0SMark Murray 	lc = NULL;
1243d55a6c0SMark Murray 
1254448b21cSMark Murray 	if (pwd != NULL) {
1261642eb1aSMark Murray 
1271642eb1aSMark Murray 		PAM_LOG("Doing real authentication");
1281642eb1aSMark Murray 
1291642eb1aSMark Murray 		if (pwd->pw_passwd[0] == '\0'
1301642eb1aSMark Murray 		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
13146efbac2SMark Murray 			/*
13246efbac2SMark Murray 			 * No password case. XXX Are we giving too much away
13346efbac2SMark Murray 			 * by not prompting for a password?
13446efbac2SMark Murray 			 */
1351642eb1aSMark Murray 			PAM_LOG("No password, and null password OK");
1361642eb1aSMark Murray 			PAM_RETURN(PAM_SUCCESS);
1371642eb1aSMark Murray 		}
13846efbac2SMark Murray 		else {
1393d55a6c0SMark Murray 			retval = pam_get_pass(pamh, &pass, password_prompt,
1401642eb1aSMark Murray 			    &options);
1411642eb1aSMark Murray 			if (retval != PAM_SUCCESS)
1421642eb1aSMark Murray 				PAM_RETURN(retval);
1431642eb1aSMark Murray 			PAM_LOG("Got password");
14446efbac2SMark Murray 		}
1453d55a6c0SMark Murray 		encrypted = crypt(pass, pwd->pw_passwd);
1463d55a6c0SMark Murray 		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
1479a10bb17SJohn Polstra 			encrypted = ":";
1489a10bb17SJohn Polstra 
1493d55a6c0SMark Murray 		PAM_LOG("Encrypted password 1 is: %s", encrypted);
1503d55a6c0SMark Murray 		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
1511642eb1aSMark Murray 
1529a10bb17SJohn Polstra 		retval = strcmp(encrypted, pwd->pw_passwd) == 0 ?
1539a10bb17SJohn Polstra 		    PAM_SUCCESS : PAM_AUTH_ERR;
1541642eb1aSMark Murray 	}
1551642eb1aSMark Murray 	else {
1561642eb1aSMark Murray 
1571642eb1aSMark Murray 		PAM_LOG("Doing dummy authentication");
1581642eb1aSMark Murray 
1599a10bb17SJohn Polstra 		/*
1601642eb1aSMark Murray 		 * User unknown.
1611642eb1aSMark Murray 		 * Encrypt a dummy password so as to not give away too much.
1629a10bb17SJohn Polstra 		 */
1633d55a6c0SMark Murray 		retval = pam_get_pass(pamh, &pass, password_prompt,
1641642eb1aSMark Murray 		    &options);
1651642eb1aSMark Murray 		if (retval != PAM_SUCCESS)
1661642eb1aSMark Murray 			PAM_RETURN(retval);
1671642eb1aSMark Murray 		PAM_LOG("Got password");
1683d55a6c0SMark Murray 		crypt(pass, "xx");
1699a10bb17SJohn Polstra 		retval = PAM_AUTH_ERR;
1709a10bb17SJohn Polstra 	}
1711642eb1aSMark Murray 
1729a10bb17SJohn Polstra 	/*
1739a10bb17SJohn Polstra 	 * The PAM infrastructure will obliterate the cleartext
1749a10bb17SJohn Polstra 	 * password before returning to the application.
1759a10bb17SJohn Polstra 	 */
1763d55a6c0SMark Murray 	if (retval != PAM_SUCCESS)
1773d55a6c0SMark Murray 		PAM_VERBOSE_ERROR("UNIX authentication refused");
1783d55a6c0SMark Murray 
1791642eb1aSMark Murray 	PAM_RETURN(retval);
1809a10bb17SJohn Polstra }
1819a10bb17SJohn Polstra 
1829a10bb17SJohn Polstra PAM_EXTERN int
1839a10bb17SJohn Polstra pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
1849a10bb17SJohn Polstra {
1853d55a6c0SMark Murray 	struct options options;
1863d55a6c0SMark Murray 
1873d55a6c0SMark Murray 	pam_std_option(&options, other_options, argc, argv);
1883d55a6c0SMark Murray 
1893d55a6c0SMark Murray 	PAM_LOG("Options processed");
1903d55a6c0SMark Murray 
1913d55a6c0SMark Murray 	PAM_RETURN(PAM_SUCCESS);
1929a10bb17SJohn Polstra }
1939294327dSJohn Polstra 
194d65b34dbSJohn Polstra /*
195d65b34dbSJohn Polstra  * account management
196d65b34dbSJohn Polstra  */
1973d55a6c0SMark Murray PAM_EXTERN int
1983d55a6c0SMark Murray pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
199d65b34dbSJohn Polstra {
2001642eb1aSMark Murray 	struct options options;
201d65b34dbSJohn Polstra 	struct passwd *pw;
202d65b34dbSJohn Polstra 	struct timeval tp;
2031642eb1aSMark Murray 	login_cap_t *lc;
204d65b34dbSJohn Polstra 	time_t warntime;
205d65b34dbSJohn Polstra 	int retval;
2061642eb1aSMark Murray 	const char *user;
2071642eb1aSMark Murray 	char buf[128];
2081642eb1aSMark Murray 
2091642eb1aSMark Murray 	pam_std_option(&options, other_options, argc, argv);
2101642eb1aSMark Murray 
2111642eb1aSMark Murray 	PAM_LOG("Options processed");
212d65b34dbSJohn Polstra 
213d65b34dbSJohn Polstra 	retval = pam_get_item(pamh, PAM_USER, (const void **)&user);
214d65b34dbSJohn Polstra 	if (retval != PAM_SUCCESS || user == NULL)
215d65b34dbSJohn Polstra 		/* some implementations return PAM_SUCCESS here */
2161642eb1aSMark Murray 		PAM_RETURN(PAM_USER_UNKNOWN);
217d65b34dbSJohn Polstra 
2181642eb1aSMark Murray 	pw = getpwnam(user);
2191642eb1aSMark Murray 	if (pw == NULL)
2201642eb1aSMark Murray 		PAM_RETURN(PAM_USER_UNKNOWN);
2211642eb1aSMark Murray 
2221642eb1aSMark Murray 	PAM_LOG("Got user: %s", user);
223d65b34dbSJohn Polstra 
224d65b34dbSJohn Polstra 	retval = PAM_SUCCESS;
225d65b34dbSJohn Polstra 	lc = login_getpwclass(pw);
226d65b34dbSJohn Polstra 
227d65b34dbSJohn Polstra 	if (pw->pw_change || pw->pw_expire)
228d65b34dbSJohn Polstra 		gettimeofday(&tp, NULL);
229d65b34dbSJohn Polstra 
230d65b34dbSJohn Polstra 	warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN,
231d65b34dbSJohn Polstra 	    DEFAULT_WARN);
232d65b34dbSJohn Polstra 
2331642eb1aSMark Murray 	PAM_LOG("Got login_cap");
2341642eb1aSMark Murray 
235d65b34dbSJohn Polstra 	if (pw->pw_change) {
236d65b34dbSJohn Polstra 		if (tp.tv_sec >= pw->pw_change)
237d65b34dbSJohn Polstra 			/* some implementations return PAM_AUTHTOK_EXPIRED */
238d65b34dbSJohn Polstra 			retval = PAM_NEW_AUTHTOK_REQD;
239d65b34dbSJohn Polstra 		else if (pw->pw_change - tp.tv_sec < warntime) {
240d65b34dbSJohn Polstra 			snprintf(buf, sizeof(buf),
241d65b34dbSJohn Polstra 			    "Warning: your password expires on %s",
242d65b34dbSJohn Polstra 			    ctime(&pw->pw_change));
243d65b34dbSJohn Polstra 			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
244d65b34dbSJohn Polstra 		}
245d65b34dbSJohn Polstra 	}
246d65b34dbSJohn Polstra 
247d65b34dbSJohn Polstra 	warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN,
248d65b34dbSJohn Polstra 	    DEFAULT_WARN);
249d65b34dbSJohn Polstra 
250d65b34dbSJohn Polstra 	if (pw->pw_expire) {
251d65b34dbSJohn Polstra 		if (tp.tv_sec >= pw->pw_expire)
252d65b34dbSJohn Polstra 			retval = PAM_ACCT_EXPIRED;
253d65b34dbSJohn Polstra 		else if (pw->pw_expire - tp.tv_sec < warntime) {
254d65b34dbSJohn Polstra 			snprintf(buf, sizeof(buf),
255d65b34dbSJohn Polstra 			    "Warning: your account expires on %s",
256d65b34dbSJohn Polstra 			    ctime(&pw->pw_expire));
257d65b34dbSJohn Polstra 			pam_prompt(pamh, PAM_ERROR_MSG, buf, NULL);
258d65b34dbSJohn Polstra 		}
259d65b34dbSJohn Polstra 	}
260d65b34dbSJohn Polstra 
261d65b34dbSJohn Polstra 	login_close(lc);
2621642eb1aSMark Murray 
2631642eb1aSMark Murray 	PAM_RETURN(retval);
264d65b34dbSJohn Polstra }
265d65b34dbSJohn Polstra 
2663d55a6c0SMark Murray /*
2673d55a6c0SMark Murray  * session management
2683d55a6c0SMark Murray  *
2693d55a6c0SMark Murray  * logging only
2703d55a6c0SMark Murray  */
2713d55a6c0SMark Murray PAM_EXTERN int
2723d55a6c0SMark Murray pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
2733d55a6c0SMark Murray {
2743d55a6c0SMark Murray 	struct options options;
2753d55a6c0SMark Murray 
2763d55a6c0SMark Murray 	pam_std_option(&options, other_options, argc, argv);
2773d55a6c0SMark Murray 
2783d55a6c0SMark Murray 	PAM_LOG("Options processed");
2793d55a6c0SMark Murray 
2803d55a6c0SMark Murray 	PAM_RETURN(PAM_SUCCESS);
2813d55a6c0SMark Murray }
2823d55a6c0SMark Murray 
2833d55a6c0SMark Murray PAM_EXTERN int
2843d55a6c0SMark Murray pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
2853d55a6c0SMark Murray {
2863d55a6c0SMark Murray 	struct options options;
2873d55a6c0SMark Murray 
2883d55a6c0SMark Murray 	pam_std_option(&options, other_options, argc, argv);
2893d55a6c0SMark Murray 
2903d55a6c0SMark Murray 	PAM_LOG("Options processed");
2913d55a6c0SMark Murray 
2923d55a6c0SMark Murray 	PAM_RETURN(PAM_SUCCESS);
2933d55a6c0SMark Murray }
2943d55a6c0SMark Murray 
2953d55a6c0SMark Murray /*
2963d55a6c0SMark Murray  * password management
2973d55a6c0SMark Murray  *
2983d55a6c0SMark Murray  * standard Unix and NIS password changing
2993d55a6c0SMark Murray  */
3003d55a6c0SMark Murray PAM_EXTERN int
3013d55a6c0SMark Murray pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
3023d55a6c0SMark Murray {
3033d55a6c0SMark Murray 	struct options options;
3043d55a6c0SMark Murray 	struct passwd *pwd;
3053d55a6c0SMark Murray 	int retval, retry, res, got;
3063d55a6c0SMark Murray 	const char *user, *pass;
3073d55a6c0SMark Murray 	char *new_pass, *new_pass_, *encrypted;
3083d55a6c0SMark Murray 
3093d55a6c0SMark Murray 	pam_std_option(&options, other_options, argc, argv);
3103d55a6c0SMark Murray 
3113d55a6c0SMark Murray 	PAM_LOG("Options processed");
3123d55a6c0SMark Murray 
3133d55a6c0SMark Murray 	if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL))
3143d55a6c0SMark Murray 		pwd = getpwnam(getlogin());
3153d55a6c0SMark Murray 	else {
3163d55a6c0SMark Murray 		retval = pam_get_user(pamh, &user, NULL);
3173d55a6c0SMark Murray 		if (retval != PAM_SUCCESS)
3183d55a6c0SMark Murray 			PAM_RETURN(retval);
3193d55a6c0SMark Murray 		pwd = getpwnam(user);
3203d55a6c0SMark Murray 	}
3213d55a6c0SMark Murray 
3223d55a6c0SMark Murray 	PAM_LOG("Got user: %s", user);
3233d55a6c0SMark Murray 
3243d55a6c0SMark Murray 	if (flags & PAM_PRELIM_CHECK) {
3253d55a6c0SMark Murray 
3263d55a6c0SMark Murray 		PAM_LOG("PRELIM round; checking user password");
3273d55a6c0SMark Murray 
3283d55a6c0SMark Murray 		if (pwd->pw_passwd[0] == '\0'
3293d55a6c0SMark Murray 		    && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) {
3303d55a6c0SMark Murray 			/*
3313d55a6c0SMark Murray 			 * No password case. XXX Are we giving too much away
3323d55a6c0SMark Murray 			 * by not prompting for a password?
3333d55a6c0SMark Murray 			 */
3343d55a6c0SMark Murray 			PAM_LOG("No password, and null password OK");
3353d55a6c0SMark Murray 			PAM_RETURN(PAM_SUCCESS);
3363d55a6c0SMark Murray 		}
3373d55a6c0SMark Murray 		else {
3383d55a6c0SMark Murray 			retval = pam_get_pass(pamh, &pass,
3393d55a6c0SMark Murray 			    PASSWORD_PROMPT_EXPIRED, &options);
3403d55a6c0SMark Murray 			if (retval != PAM_SUCCESS)
3413d55a6c0SMark Murray 				PAM_RETURN(retval);
3423d55a6c0SMark Murray 			PAM_LOG("Got password: %s", pass);
3433d55a6c0SMark Murray 		}
3443d55a6c0SMark Murray 		encrypted = crypt(pass, pwd->pw_passwd);
3453d55a6c0SMark Murray 		if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0')
3463d55a6c0SMark Murray 			encrypted = ":";
3473d55a6c0SMark Murray 
3483d55a6c0SMark Murray 		PAM_LOG("Encrypted password 1 is: %s", encrypted);
3493d55a6c0SMark Murray 		PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd);
3503d55a6c0SMark Murray 
3513d55a6c0SMark Murray 		if (strcmp(encrypted, pwd->pw_passwd) != 0)
3523d55a6c0SMark Murray 			PAM_RETURN(PAM_AUTH_ERR);
3533d55a6c0SMark Murray 
3543d55a6c0SMark Murray 		retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass);
3553d55a6c0SMark Murray 		pass = NULL;
3563d55a6c0SMark Murray 		if (retval != PAM_SUCCESS)
3573d55a6c0SMark Murray 			PAM_RETURN(retval);
3583d55a6c0SMark Murray 
3593d55a6c0SMark Murray 		PAM_LOG("Stashed old password");
3603d55a6c0SMark Murray 
3613d55a6c0SMark Murray 		retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass);
3623d55a6c0SMark Murray 		if (retval != PAM_SUCCESS)
3633d55a6c0SMark Murray 			PAM_RETURN(retval);
3643d55a6c0SMark Murray 
3653d55a6c0SMark Murray 		PAM_LOG("Voided old password");
3663d55a6c0SMark Murray 
3673d55a6c0SMark Murray 		PAM_RETURN(PAM_SUCCESS);
3683d55a6c0SMark Murray 	}
3693d55a6c0SMark Murray 	else if (flags & PAM_UPDATE_AUTHTOK) {
3703d55a6c0SMark Murray 		PAM_LOG("UPDATE round; checking user password");
3713d55a6c0SMark Murray 
3723d55a6c0SMark Murray 		retval = pam_get_item(pamh, PAM_OLDAUTHTOK,
3733d55a6c0SMark Murray 		    (const void **)&pass);
3743d55a6c0SMark Murray 		if (retval != PAM_SUCCESS)
3753d55a6c0SMark Murray 			PAM_RETURN(retval);
3763d55a6c0SMark Murray 
3773d55a6c0SMark Murray 		PAM_LOG("Got old password: %s", pass);
3783d55a6c0SMark Murray 
3793d55a6c0SMark Murray 		got = 0;
3803d55a6c0SMark Murray 		retry = 0;
3813d55a6c0SMark Murray 		while (retry++ < MAX_TRIES) {
3823d55a6c0SMark Murray 			new_pass = NULL;
3833d55a6c0SMark Murray 			retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
3843d55a6c0SMark Murray 			    NEW_PASSWORD_PROMPT_1, &new_pass);
3853d55a6c0SMark Murray 
3863d55a6c0SMark Murray 			if (new_pass == NULL)
3873d55a6c0SMark Murray 				new_pass = "";
3883d55a6c0SMark Murray 
3893d55a6c0SMark Murray 			if (retval == PAM_SUCCESS) {
3903d55a6c0SMark Murray 				new_pass_ = NULL;
3913d55a6c0SMark Murray 				retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF,
3923d55a6c0SMark Murray 				    NEW_PASSWORD_PROMPT_2, &new_pass_);
3933d55a6c0SMark Murray 
3943d55a6c0SMark Murray 				if (new_pass_ == NULL)
3953d55a6c0SMark Murray 					new_pass_ = "";
3963d55a6c0SMark Murray 
3973d55a6c0SMark Murray 				if (retval == PAM_SUCCESS) {
3983d55a6c0SMark Murray 					if (strcmp(new_pass, new_pass_) == 0) {
3993d55a6c0SMark Murray 						got = 1;
4003d55a6c0SMark Murray 						break;
4013d55a6c0SMark Murray 					}
4023d55a6c0SMark Murray 					else
4033d55a6c0SMark Murray 						PAM_VERBOSE_ERROR("Password mismatch");
4043d55a6c0SMark Murray 				}
4053d55a6c0SMark Murray 			}
4063d55a6c0SMark Murray 		}
4073d55a6c0SMark Murray 
4083d55a6c0SMark Murray 		if (!got) {
4093d55a6c0SMark Murray 			PAM_VERBOSE_ERROR("Unable to get valid password");
4103d55a6c0SMark Murray 			PAM_RETURN(PAM_PERM_DENIED);
4113d55a6c0SMark Murray 		}
4123d55a6c0SMark Murray 
4133d55a6c0SMark Murray 		PAM_LOG("Got new password: %s", new_pass);
4143d55a6c0SMark Murray 
4153d55a6c0SMark Murray #ifdef YP
4163d55a6c0SMark Murray 		/* If NIS is set in the passwd database, use it */
4173d55a6c0SMark Murray 		res = use_yp((char *)user, 0, 0);
4183d55a6c0SMark Murray 		if (res == USER_YP_ONLY) {
4193d55a6c0SMark Murray 			if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS,
4203d55a6c0SMark Murray 			    NULL))
4213d55a6c0SMark Murray 				retval = yp_passwd(user, new_pass);
4223d55a6c0SMark Murray 			else {
4233d55a6c0SMark Murray 				/* Reject 'local' flag if NIS is on and the user
4243d55a6c0SMark Murray 				 * is not local
4253d55a6c0SMark Murray 				 */
4263d55a6c0SMark Murray 				retval = PAM_PERM_DENIED;
4273d55a6c0SMark Murray 				PAM_LOG("Unknown local user: %s", user);
4283d55a6c0SMark Murray 			}
4293d55a6c0SMark Murray 		}
4303d55a6c0SMark Murray 		else if (res == USER_LOCAL_ONLY) {
4313d55a6c0SMark Murray 			if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
4323d55a6c0SMark Murray 				retval = local_passwd(user, new_pass);
4333d55a6c0SMark Murray 			else {
4343d55a6c0SMark Murray 				/* Reject 'nis' flag if user is only local */
4353d55a6c0SMark Murray 				retval = PAM_PERM_DENIED;
4363d55a6c0SMark Murray 				PAM_LOG("Unknown NIS user: %s", user);
4373d55a6c0SMark Murray 			}
4383d55a6c0SMark Murray 		}
4393d55a6c0SMark Murray 		else if (res == USER_YP_AND_LOCAL) {
4403d55a6c0SMark Murray 			if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL))
4413d55a6c0SMark Murray 				retval = yp_passwd(user, new_pass);
4423d55a6c0SMark Murray 			else
4433d55a6c0SMark Murray 				retval = local_passwd(user, new_pass);
4443d55a6c0SMark Murray 		}
4453d55a6c0SMark Murray 		else
4463d55a6c0SMark Murray 			retval = PAM_ABORT; /* Bad juju */
4473d55a6c0SMark Murray #else
4483d55a6c0SMark Murray 		retval = local_passwd(user, new_pass);
4493d55a6c0SMark Murray #endif
4503d55a6c0SMark Murray 
4513d55a6c0SMark Murray 		/* XXX wipe the mem as well */
4523d55a6c0SMark Murray 		pass = NULL;
4533d55a6c0SMark Murray 		new_pass = NULL;
4543d55a6c0SMark Murray 	}
4553d55a6c0SMark Murray 	else {
4563d55a6c0SMark Murray 		/* Very bad juju */
4573d55a6c0SMark Murray 		retval = PAM_ABORT;
4583d55a6c0SMark Murray 		PAM_LOG("Illegal 'flags'");
4593d55a6c0SMark Murray 	}
4603d55a6c0SMark Murray 
4613d55a6c0SMark Murray 	PAM_RETURN(retval);
4623d55a6c0SMark Murray }
4633d55a6c0SMark Murray 
4643d55a6c0SMark Murray /* Mostly stolen from passwd(1)'s local_passwd.c - markm */
4653d55a6c0SMark Murray 
4663d55a6c0SMark Murray static unsigned char itoa64[] =		/* 0 ... 63 => ascii - 64 */
4673d55a6c0SMark Murray 	"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
4683d55a6c0SMark Murray 
4693d55a6c0SMark Murray static void
4703d55a6c0SMark Murray to64(char *s, long v, int n)
4713d55a6c0SMark Murray {
4723d55a6c0SMark Murray 	while (--n >= 0) {
4733d55a6c0SMark Murray 		*s++ = itoa64[v&0x3f];
4743d55a6c0SMark Murray 		v >>= 6;
4753d55a6c0SMark Murray 	}
4763d55a6c0SMark Murray }
4773d55a6c0SMark Murray 
4783d55a6c0SMark Murray static int
4793d55a6c0SMark Murray local_passwd(const char *user, const char *pass)
4803d55a6c0SMark Murray {
4813d55a6c0SMark Murray 	login_cap_t * lc;
4823d55a6c0SMark Murray 	struct passwd *pwd;
4833d55a6c0SMark Murray 	struct timeval tv;
4843d55a6c0SMark Murray 	int pfd, tfd;
4853d55a6c0SMark Murray 	char *crypt_type, salt[32];
4863d55a6c0SMark Murray 
4873d55a6c0SMark Murray 	pwd = getpwnam(user);
4883d55a6c0SMark Murray 	if (pwd == NULL)
4893d55a6c0SMark Murray 		return(PAM_ABORT); /* Really bad things */
4903d55a6c0SMark Murray 
4913d55a6c0SMark Murray #ifdef YP
4923d55a6c0SMark Murray 	pwd = (struct passwd *)&local_password;
4933d55a6c0SMark Murray #endif
4943d55a6c0SMark Murray 	pw_init();
4953d55a6c0SMark Murray 
4963d55a6c0SMark Murray 	pwd->pw_change = 0;
4973d55a6c0SMark Murray 	lc = login_getclass(NULL);
4983d55a6c0SMark Murray 	crypt_type = login_getcapstr(lc, "passwd_format",
4993d55a6c0SMark Murray 		PASSWORD_HASH, PASSWORD_HASH);
5003d55a6c0SMark Murray 	if (login_setcryptfmt(lc, crypt_type, NULL) == NULL)
5013d55a6c0SMark Murray 		syslog(LOG_ERR, "cannot set password cipher");
5023d55a6c0SMark Murray 	login_close(lc);
5033d55a6c0SMark Murray 	/* Salt suitable for anything */
5043d55a6c0SMark Murray 	srandomdev();
5053d55a6c0SMark Murray 	gettimeofday(&tv, 0);
5063d55a6c0SMark Murray 	to64(&salt[0], random(), 3);
5073d55a6c0SMark Murray 	to64(&salt[3], tv.tv_usec, 3);
5083d55a6c0SMark Murray 	to64(&salt[6], tv.tv_sec, 2);
5093d55a6c0SMark Murray 	to64(&salt[8], random(), 5);
5103d55a6c0SMark Murray 	to64(&salt[13], random(), 5);
5113d55a6c0SMark Murray 	to64(&salt[17], random(), 5);
5123d55a6c0SMark Murray 	to64(&salt[22], random(), 5);
5133d55a6c0SMark Murray 	salt[27] = '\0';
5143d55a6c0SMark Murray 
5153d55a6c0SMark Murray 	pwd->pw_passwd = crypt(pass, salt);
5163d55a6c0SMark Murray 
5173d55a6c0SMark Murray 	pfd = pw_lock();
5183d55a6c0SMark Murray 	tfd = pw_tmp();
5193d55a6c0SMark Murray 	pw_copy(pfd, tfd, pwd);
5203d55a6c0SMark Murray 
5213d55a6c0SMark Murray 	if (!pw_mkdb((char *)user))
5223d55a6c0SMark Murray 		pw_error((char *)NULL, 0, 1);
5233d55a6c0SMark Murray 
5243d55a6c0SMark Murray 	return PAM_SUCCESS;
5253d55a6c0SMark Murray }
5263d55a6c0SMark Murray 
5273d55a6c0SMark Murray #ifdef YP
5283d55a6c0SMark Murray /* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of:
5293d55a6c0SMark Murray  * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
5303d55a6c0SMark Murray  * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de>
5313d55a6c0SMark Murray  * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu>
5323d55a6c0SMark Murray  */
5333d55a6c0SMark Murray int
5343d55a6c0SMark Murray yp_passwd(const char *user, const char *pass)
5353d55a6c0SMark Murray {
5363d55a6c0SMark Murray 	struct master_yppasswd master_yppasswd;
5373d55a6c0SMark Murray 	struct passwd *pwd;
5383d55a6c0SMark Murray 	struct rpc_err err;
5393d55a6c0SMark Murray 	struct timeval tv;
5403d55a6c0SMark Murray 	struct yppasswd yppasswd;
5413d55a6c0SMark Murray 	CLIENT *clnt;
5423d55a6c0SMark Murray 	login_cap_t *lc;
5433d55a6c0SMark Murray 	int    *status;
5443d55a6c0SMark Murray 	uid_t	uid;
5453d55a6c0SMark Murray 	char   *master, *sockname = YP_SOCKNAME, salt[32];
5463d55a6c0SMark Murray 
5473d55a6c0SMark Murray 	_use_yp = 1;
5483d55a6c0SMark Murray 
5493d55a6c0SMark Murray 	uid = getuid();
5503d55a6c0SMark Murray 
5513d55a6c0SMark Murray 	master = get_yp_master(1);
5523d55a6c0SMark Murray 	if (master == NULL)
5533d55a6c0SMark Murray 		return PAM_ABORT; /* Major disaster */
5543d55a6c0SMark Murray 
5553d55a6c0SMark Murray 	/*
5563d55a6c0SMark Murray 	 * It is presumed that by the time we get here, use_yp()
5573d55a6c0SMark Murray 	 * has been called and that we have verified that the user
5583d55a6c0SMark Murray 	 * actually exists. This being the case, the yp_password
5593d55a6c0SMark Murray 	 * stucture has already been filled in for us.
5603d55a6c0SMark Murray 	 */
5613d55a6c0SMark Murray 
5623d55a6c0SMark Murray 	/* Use the correct password */
5633d55a6c0SMark Murray 	pwd = (struct passwd *)&yp_password;
5643d55a6c0SMark Murray 
5653d55a6c0SMark Murray 	pwd->pw_change = 0;
5663d55a6c0SMark Murray 
5673d55a6c0SMark Murray 	/* Initialize password information */
5683d55a6c0SMark Murray 	if (suser_override) {
5693d55a6c0SMark Murray 		master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
5703d55a6c0SMark Murray 		master_yppasswd.newpw.pw_name = strdup(pwd->pw_name);
5713d55a6c0SMark Murray 		master_yppasswd.newpw.pw_uid = pwd->pw_uid;
5723d55a6c0SMark Murray 		master_yppasswd.newpw.pw_gid = pwd->pw_gid;
5733d55a6c0SMark Murray 		master_yppasswd.newpw.pw_expire = pwd->pw_expire;
5743d55a6c0SMark Murray 		master_yppasswd.newpw.pw_change = pwd->pw_change;
5753d55a6c0SMark Murray 		master_yppasswd.newpw.pw_fields = pwd->pw_fields;
5763d55a6c0SMark Murray 		master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
5773d55a6c0SMark Murray 		master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
5783d55a6c0SMark Murray 		master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
5793d55a6c0SMark Murray 		master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ?
5803d55a6c0SMark Murray 					strdup(pwd->pw_class) : "";
5813d55a6c0SMark Murray 		master_yppasswd.oldpass = "";
5823d55a6c0SMark Murray 		master_yppasswd.domain = yp_domain;
5833d55a6c0SMark Murray 	} else {
5843d55a6c0SMark Murray 		yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd);
5853d55a6c0SMark Murray 		yppasswd.newpw.pw_name = strdup(pwd->pw_name);
5863d55a6c0SMark Murray 		yppasswd.newpw.pw_uid = pwd->pw_uid;
5873d55a6c0SMark Murray 		yppasswd.newpw.pw_gid = pwd->pw_gid;
5883d55a6c0SMark Murray 		yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos);
5893d55a6c0SMark Murray 		yppasswd.newpw.pw_dir = strdup(pwd->pw_dir);
5903d55a6c0SMark Murray 		yppasswd.newpw.pw_shell = strdup(pwd->pw_shell);
5913d55a6c0SMark Murray 		yppasswd.oldpass = "";
5923d55a6c0SMark Murray 	}
5933d55a6c0SMark Murray 
5943d55a6c0SMark Murray 	if (login_setcryptfmt(lc, "md5", NULL) == NULL)
5953d55a6c0SMark Murray 		syslog(LOG_ERR, "cannot set password cipher");
5963d55a6c0SMark Murray 	login_close(lc);
5973d55a6c0SMark Murray 	/* Salt suitable for anything */
5983d55a6c0SMark Murray 	srandomdev();
5993d55a6c0SMark Murray 	gettimeofday(&tv, 0);
6003d55a6c0SMark Murray 	to64(&salt[0], random(), 3);
6013d55a6c0SMark Murray 	to64(&salt[3], tv.tv_usec, 3);
6023d55a6c0SMark Murray 	to64(&salt[6], tv.tv_sec, 2);
6033d55a6c0SMark Murray 	to64(&salt[8], random(), 5);
6043d55a6c0SMark Murray 	to64(&salt[13], random(), 5);
6053d55a6c0SMark Murray 	to64(&salt[17], random(), 5);
6063d55a6c0SMark Murray 	to64(&salt[22], random(), 5);
6073d55a6c0SMark Murray 	salt[27] = '\0';
6083d55a6c0SMark Murray 
6093d55a6c0SMark Murray 	if (suser_override)
6103d55a6c0SMark Murray 		master_yppasswd.newpw.pw_passwd = crypt(pass, salt);
6113d55a6c0SMark Murray 	else
6123d55a6c0SMark Murray 		yppasswd.newpw.pw_passwd = crypt(pass, salt);
6133d55a6c0SMark Murray 
6143d55a6c0SMark Murray 	if (suser_override) {
6153d55a6c0SMark Murray 		if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG,
6163d55a6c0SMark Murray 		    MASTER_YPPASSWDVERS, "unix")) == NULL) {
6173d55a6c0SMark Murray 			syslog(LOG_ERR,
6183d55a6c0SMark Murray 			    "Cannot contact rpc.yppasswdd on host %s: %s",
6193d55a6c0SMark Murray 			    master, clnt_spcreateerror(""));
6203d55a6c0SMark Murray 			return PAM_ABORT;
6213d55a6c0SMark Murray 		}
6223d55a6c0SMark Murray 	}
6233d55a6c0SMark Murray 	else {
6243d55a6c0SMark Murray 		if ((clnt = clnt_create(master, YPPASSWDPROG,
6253d55a6c0SMark Murray 		    YPPASSWDVERS, "udp")) == NULL) {
6263d55a6c0SMark Murray 			syslog(LOG_ERR,
6273d55a6c0SMark Murray 			    "Cannot contact rpc.yppasswdd on host %s: %s",
6283d55a6c0SMark Murray 			    master, clnt_spcreateerror(""));
6293d55a6c0SMark Murray 			return PAM_ABORT;
6303d55a6c0SMark Murray 		}
6313d55a6c0SMark Murray 	}
6323d55a6c0SMark Murray 	/*
6333d55a6c0SMark Murray 	 * The yppasswd.x file said `unix authentication required',
6343d55a6c0SMark Murray 	 * so I added it. This is the only reason it is in here.
6353d55a6c0SMark Murray 	 * My yppasswdd doesn't use it, but maybe some others out there
6363d55a6c0SMark Murray 	 * do. 					--okir
6373d55a6c0SMark Murray 	 */
6383d55a6c0SMark Murray 	clnt->cl_auth = authunix_create_default();
6393d55a6c0SMark Murray 
6403d55a6c0SMark Murray 	if (suser_override)
6413d55a6c0SMark Murray 		status = yppasswdproc_update_master_1(&master_yppasswd, clnt);
6423d55a6c0SMark Murray 	else
6433d55a6c0SMark Murray 		status = yppasswdproc_update_1(&yppasswd, clnt);
6443d55a6c0SMark Murray 
6453d55a6c0SMark Murray 	clnt_geterr(clnt, &err);
6463d55a6c0SMark Murray 
6473d55a6c0SMark Murray 	auth_destroy(clnt->cl_auth);
6483d55a6c0SMark Murray 	clnt_destroy(clnt);
6493d55a6c0SMark Murray 
6503d55a6c0SMark Murray 	if (err.re_status != RPC_SUCCESS || status == NULL || *status)
6513d55a6c0SMark Murray 		return PAM_ABORT;
6523d55a6c0SMark Murray 
6533d55a6c0SMark Murray 	return (err.re_status || status == NULL || *status)
6543d55a6c0SMark Murray 	    ? PAM_ABORT : PAM_SUCCESS;
6553d55a6c0SMark Murray }
6563d55a6c0SMark Murray #endif /* YP */
6573d55a6c0SMark Murray 
6589294327dSJohn Polstra PAM_MODULE_ENTRY("pam_unix");
659