xref: /onnv-gate/usr/src/lib/pam_modules/krb5/krb5_password.c (revision 11780:26d2e4a6a702)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52554Ssemery  * Common Development and Distribution License (the "License").
62554Ssemery  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
2211778SWill.Fiveash@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #include <kadm5/admin.h>
270Sstevel@tonic-gate #include <krb5.h>
280Sstevel@tonic-gate 
290Sstevel@tonic-gate #include <security/pam_appl.h>
300Sstevel@tonic-gate #include <security/pam_modules.h>
310Sstevel@tonic-gate #include <security/pam_impl.h>
320Sstevel@tonic-gate #include <syslog.h>
330Sstevel@tonic-gate #include <string.h>
340Sstevel@tonic-gate #include <stdio.h>
350Sstevel@tonic-gate #include <stdlib.h>
360Sstevel@tonic-gate #include <sys/types.h>
370Sstevel@tonic-gate #include <pwd.h>
380Sstevel@tonic-gate #include <libintl.h>
390Sstevel@tonic-gate #include <netdb.h>
400Sstevel@tonic-gate #include "utils.h"
410Sstevel@tonic-gate #include "krb5_repository.h"
420Sstevel@tonic-gate 
4311778SWill.Fiveash@Sun.COM extern int attempt_krb5_auth(pam_handle_t *, krb5_module_data_t *, char *,
4411778SWill.Fiveash@Sun.COM 	char **, boolean_t);
452941Ssemery extern int krb5_verifypw(char *, char *, int);
460Sstevel@tonic-gate 
470Sstevel@tonic-gate static void display_msg(pam_handle_t *, int, char *);
480Sstevel@tonic-gate static void display_msgs(pam_handle_t *, int, int,
490Sstevel@tonic-gate 		char msgs[][PAM_MAX_MSG_SIZE]);
500Sstevel@tonic-gate static int krb5_changepw(pam_handle_t *, char *, char *, char *, int);
510Sstevel@tonic-gate 
520Sstevel@tonic-gate /*
530Sstevel@tonic-gate  * set_ccname()
540Sstevel@tonic-gate  *
550Sstevel@tonic-gate  * set KRB5CCNAME shell var
560Sstevel@tonic-gate  */
570Sstevel@tonic-gate static void
set_ccname(pam_handle_t * pamh,krb5_module_data_t * kmd,int login_result,int debug)580Sstevel@tonic-gate set_ccname(
590Sstevel@tonic-gate 	pam_handle_t *pamh,
600Sstevel@tonic-gate 	krb5_module_data_t *kmd,
610Sstevel@tonic-gate 	int login_result,
620Sstevel@tonic-gate 	int debug)
630Sstevel@tonic-gate {
640Sstevel@tonic-gate 	int result;
650Sstevel@tonic-gate 
660Sstevel@tonic-gate 	if (debug)
673391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
680Sstevel@tonic-gate 		    "PAM-KRB5 (password): password: finalize"
690Sstevel@tonic-gate 		    " ccname env, login_result =%d, env ='%s'",
700Sstevel@tonic-gate 		    login_result, kmd->env ? kmd->env : "<null>");
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	if (kmd->env) {
730Sstevel@tonic-gate 
740Sstevel@tonic-gate 		if (login_result == PAM_SUCCESS) {
750Sstevel@tonic-gate 				/*
760Sstevel@tonic-gate 				 * Put ccname into the pamh so that login
770Sstevel@tonic-gate 				 * apps can pick this up when they run
780Sstevel@tonic-gate 				 * pam_getenvlist().
790Sstevel@tonic-gate 				 */
800Sstevel@tonic-gate 			if ((result = pam_putenv(pamh, kmd->env))
810Sstevel@tonic-gate 			    != PAM_SUCCESS) {
820Sstevel@tonic-gate 				/* should not happen but... */
833391Ssemery 				__pam_log(LOG_AUTH | LOG_ERR,
84*11780SWill.Fiveash@Sun.COM 				    "PAM-KRB5 (password):"
85*11780SWill.Fiveash@Sun.COM 				    " pam_putenv failed: result: %d",
860Sstevel@tonic-gate 				    result);
870Sstevel@tonic-gate 				goto cleanupccname;
880Sstevel@tonic-gate 			}
890Sstevel@tonic-gate 		} else {
900Sstevel@tonic-gate 		cleanupccname:
910Sstevel@tonic-gate 				/* for lack of a Solaris unputenv() */
920Sstevel@tonic-gate 			krb5_unsetenv(KRB5_ENV_CCNAME);
930Sstevel@tonic-gate 			free(kmd->env);
940Sstevel@tonic-gate 			kmd->env = NULL;
950Sstevel@tonic-gate 		}
960Sstevel@tonic-gate 	}
970Sstevel@tonic-gate }
980Sstevel@tonic-gate 
990Sstevel@tonic-gate /*
1000Sstevel@tonic-gate  * get_set_creds()
1010Sstevel@tonic-gate  *
1020Sstevel@tonic-gate  * do a krb5 login to get and set krb5 creds (needed after a pw change
1030Sstevel@tonic-gate  * on pw expire on login)
1040Sstevel@tonic-gate  */
1050Sstevel@tonic-gate static void
get_set_creds(pam_handle_t * pamh,krb5_module_data_t * kmd,char * user,char * newpass,int debug)1060Sstevel@tonic-gate get_set_creds(
1070Sstevel@tonic-gate 	pam_handle_t *pamh,
1080Sstevel@tonic-gate 	krb5_module_data_t *kmd,
1090Sstevel@tonic-gate 	char *user,
1100Sstevel@tonic-gate 	char *newpass,
1110Sstevel@tonic-gate 	int debug)
1120Sstevel@tonic-gate {
1130Sstevel@tonic-gate 	int login_result;
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate 	if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
1160Sstevel@tonic-gate 		return;
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	/*
1190Sstevel@tonic-gate 	 * if pw has expired, get/set krb5 creds ala auth mod
1200Sstevel@tonic-gate 	 *
1210Sstevel@tonic-gate 	 * pwchange verified user sufficiently, so don't request strict
1220Sstevel@tonic-gate 	 * tgt verification (will cause rcache perm issues possibly anyways)
1230Sstevel@tonic-gate 	 */
12411778SWill.Fiveash@Sun.COM 	login_result = attempt_krb5_auth(pamh, kmd, user, &newpass, 0);
1250Sstevel@tonic-gate 	if (debug)
1263391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
1270Sstevel@tonic-gate 		    "PAM-KRB5 (password): get_set_creds: login_result= %d",
1280Sstevel@tonic-gate 		    login_result);
1290Sstevel@tonic-gate 	/*
1300Sstevel@tonic-gate 	 * the krb5 login should not fail, but if so,
1310Sstevel@tonic-gate 	 * warn the user they have to kinit(1)
1320Sstevel@tonic-gate 	 */
1330Sstevel@tonic-gate 	if (login_result != PAM_SUCCESS) {
1340Sstevel@tonic-gate 		display_msg(pamh, PAM_TEXT_INFO,
135*11780SWill.Fiveash@Sun.COM 		    dgettext(TEXT_DOMAIN,
136*11780SWill.Fiveash@Sun.COM 		    "Warning: "
137*11780SWill.Fiveash@Sun.COM 		    "Could not cache Kerberos"
138*11780SWill.Fiveash@Sun.COM 		    " credentials, please run "
139*11780SWill.Fiveash@Sun.COM 		    "kinit(1) or re-login\n"));
1400Sstevel@tonic-gate 	}
1410Sstevel@tonic-gate 	set_ccname(pamh, kmd, login_result, debug);
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate  * This is the PAM Kerberos Password Change module
1450Sstevel@tonic-gate  *
1460Sstevel@tonic-gate  */
1470Sstevel@tonic-gate 
1480Sstevel@tonic-gate int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)1490Sstevel@tonic-gate pam_sm_chauthtok(
1500Sstevel@tonic-gate 	pam_handle_t		*pamh,
1510Sstevel@tonic-gate 	int			flags,
1520Sstevel@tonic-gate 	int			argc,
1530Sstevel@tonic-gate 	const char		**argv)
1540Sstevel@tonic-gate {
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	char			*user;
1572941Ssemery 	int			err, result = PAM_AUTHTOK_ERR;
1582941Ssemery 	char			*newpass = NULL;
1590Sstevel@tonic-gate 	char			*oldpass = NULL;
1600Sstevel@tonic-gate 	int			i;
1610Sstevel@tonic-gate 	int			debug = 0;
1620Sstevel@tonic-gate 	uid_t			pw_uid;
1630Sstevel@tonic-gate 	krb5_module_data_t	*kmd = NULL;
1640Sstevel@tonic-gate 	pam_repository_t	*rep_data = NULL;
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	for (i = 0; i < argc; i++) {
1670Sstevel@tonic-gate 		if (strcmp(argv[i], "debug") == 0)
1680Sstevel@tonic-gate 			debug = 1;
1690Sstevel@tonic-gate 		else
1703391Ssemery 			__pam_log(LOG_AUTH | LOG_ERR,
171*11780SWill.Fiveash@Sun.COM 			    "PAM-KRB5 (password): illegal option %s",
1720Sstevel@tonic-gate 			    argv[i]);
1730Sstevel@tonic-gate 	}
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 	if (debug)
1763391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
1770Sstevel@tonic-gate 		    "PAM-KRB5 (password): start: flags = %x",
1780Sstevel@tonic-gate 		    flags);
1790Sstevel@tonic-gate 
1802941Ssemery 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
1812941Ssemery 
1820Sstevel@tonic-gate 	if (rep_data != NULL) {
1830Sstevel@tonic-gate 		if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
1840Sstevel@tonic-gate 			if (debug)
1853391Ssemery 				__pam_log(LOG_AUTH | LOG_DEBUG,
186*11780SWill.Fiveash@Sun.COM 				    "PAM-KRB5 (auth): wrong"
187*11780SWill.Fiveash@Sun.COM 				    "repository found (%s), returning "
188*11780SWill.Fiveash@Sun.COM 				    "PAM_IGNORE", rep_data->type);
1890Sstevel@tonic-gate 			return (PAM_IGNORE);
1900Sstevel@tonic-gate 		}
1910Sstevel@tonic-gate 	}
1920Sstevel@tonic-gate 
1930Sstevel@tonic-gate 	if (flags & PAM_PRELIM_CHECK) {
1940Sstevel@tonic-gate 		/* Nothing to do here */
1950Sstevel@tonic-gate 		if (debug)
1963391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
1970Sstevel@tonic-gate 			    "PAM-KRB5 (password): prelim check");
1980Sstevel@tonic-gate 		return (PAM_IGNORE);
1990Sstevel@tonic-gate 	}
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/* make sure PAM framework is telling us to update passwords */
2020Sstevel@tonic-gate 	if (!(flags & PAM_UPDATE_AUTHTOK)) {
2033391Ssemery 		__pam_log(LOG_AUTH | LOG_ERR,
204*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password): bad flags: %d",
205*11780SWill.Fiveash@Sun.COM 		    flags);
2060Sstevel@tonic-gate 		return (PAM_SYSTEM_ERR);
2070Sstevel@tonic-gate 	}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	if ((err = pam_get_data(pamh, KRB5_DATA, (const void **)&kmd))
2110Sstevel@tonic-gate 	    != PAM_SUCCESS) {
2120Sstevel@tonic-gate 		if (debug)
2133391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
2140Sstevel@tonic-gate 			    "PAM-KRB5 (password): get mod data failed %d",
2150Sstevel@tonic-gate 			    err);
2160Sstevel@tonic-gate 		kmd = NULL;
2170Sstevel@tonic-gate 	}
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	if (flags & PAM_CHANGE_EXPIRED_AUTHTOK) {
2200Sstevel@tonic-gate 		/* let's make sure we know the krb5 pw has expired */
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 		if (debug)
2233391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
2240Sstevel@tonic-gate 			    "PAM-KRB5 (password): kmd age status %d",
2250Sstevel@tonic-gate 			    kmd ? kmd->age_status : -99);
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 		if (!kmd || kmd->age_status != PAM_NEW_AUTHTOK_REQD)
2280Sstevel@tonic-gate 			return (PAM_IGNORE);
2290Sstevel@tonic-gate 	}
2300Sstevel@tonic-gate 
2312941Ssemery 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
2320Sstevel@tonic-gate 
2333391Ssemery 	if (user == NULL || *user == '\0') {
2343391Ssemery 		__pam_log(LOG_AUTH | LOG_ERR,
235*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password): username is empty");
2362941Ssemery 		return (PAM_USER_UNKNOWN);
2370Sstevel@tonic-gate 	}
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	if (!get_pw_uid(user, &pw_uid)) {
2403391Ssemery 		__pam_log(LOG_AUTH | LOG_ERR,
2412941Ssemery 		    "PAM-KRB5 (password): can't get uid for %s", user);
2422941Ssemery 		return (PAM_USER_UNKNOWN);
2430Sstevel@tonic-gate 	}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/*
2460Sstevel@tonic-gate 	 * if root key exists in the keytab, it's a random key so no
2470Sstevel@tonic-gate 	 * need to prompt for pw and we just return IGNORE
2480Sstevel@tonic-gate 	 */
2490Sstevel@tonic-gate 	if ((strcmp(user, ROOT_UNAME) == 0) &&
2500Sstevel@tonic-gate 	    key_in_keytab(user, debug)) {
2510Sstevel@tonic-gate 		if (debug)
2523391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
2530Sstevel@tonic-gate 			    "PAM-KRB5 (password): "
2540Sstevel@tonic-gate 			    "key for '%s' in keytab, returning IGNORE", user);
2550Sstevel@tonic-gate 		result = PAM_IGNORE;
2560Sstevel@tonic-gate 		goto out;
2570Sstevel@tonic-gate 	}
2580Sstevel@tonic-gate 
2592941Ssemery 	(void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&newpass);
2600Sstevel@tonic-gate 
26111778SWill.Fiveash@Sun.COM 	/*
26211778SWill.Fiveash@Sun.COM 	 * If the preauth type done didn't use a passwd just ignore the error.
26311778SWill.Fiveash@Sun.COM 	 */
2642941Ssemery 	if (newpass == NULL)
26511778SWill.Fiveash@Sun.COM 		if (kmd && kmd->preauth_type == KRB_PKINIT)
26611778SWill.Fiveash@Sun.COM 			return (PAM_IGNORE);
26711778SWill.Fiveash@Sun.COM 		else
26811778SWill.Fiveash@Sun.COM 			return (PAM_SYSTEM_ERR);
2690Sstevel@tonic-gate 
2702941Ssemery 	(void) pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&oldpass);
2710Sstevel@tonic-gate 
2722941Ssemery 	if (oldpass == NULL)
27311778SWill.Fiveash@Sun.COM 		if (kmd && kmd->preauth_type == KRB_PKINIT)
27411778SWill.Fiveash@Sun.COM 			return (PAM_IGNORE);
27511778SWill.Fiveash@Sun.COM 		else
27611778SWill.Fiveash@Sun.COM 			return (PAM_SYSTEM_ERR);
2770Sstevel@tonic-gate 
2782941Ssemery 	result = krb5_verifypw(user, oldpass, debug);
2792941Ssemery 	if (debug)
2803391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
281*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password): verifypw %d", result);
2820Sstevel@tonic-gate 
2832941Ssemery 	/*
2842941Ssemery 	 * If it's a bad password or general failure, we are done.
2852941Ssemery 	 */
2862941Ssemery 	if (result != 0) {
28711778SWill.Fiveash@Sun.COM 		/*
28811778SWill.Fiveash@Sun.COM 		 * if the preauth type done didn't use a passwd just ignore the
28911778SWill.Fiveash@Sun.COM 		 * error.
29011778SWill.Fiveash@Sun.COM 		 */
29111778SWill.Fiveash@Sun.COM 		if (kmd && kmd->preauth_type == KRB_PKINIT)
29211778SWill.Fiveash@Sun.COM 			return (PAM_IGNORE);
29311778SWill.Fiveash@Sun.COM 
2942941Ssemery 		if (result == 2)
2952941Ssemery 			display_msg(pamh, PAM_ERROR_MSG, dgettext(TEXT_DOMAIN,
296*11780SWill.Fiveash@Sun.COM 			    "Old Kerberos password incorrect\n"));
2972941Ssemery 		return (PAM_AUTHTOK_ERR);
2980Sstevel@tonic-gate 	}
2990Sstevel@tonic-gate 
30011778SWill.Fiveash@Sun.COM 	/*
30111778SWill.Fiveash@Sun.COM 	 * If the old password verifies try to change it regardless of the
30211778SWill.Fiveash@Sun.COM 	 * preauth type and do not ignore the error.
30311778SWill.Fiveash@Sun.COM 	 */
3040Sstevel@tonic-gate 	result = krb5_changepw(pamh, user, oldpass, newpass, debug);
3050Sstevel@tonic-gate 	if (result == PAM_SUCCESS) {
3062941Ssemery 		display_msg(pamh, PAM_TEXT_INFO, dgettext(TEXT_DOMAIN,
3072941Ssemery 		    "Kerberos password successfully changed\n"));
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 		get_set_creds(pamh, kmd, user, newpass, debug);
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate out:
3130Sstevel@tonic-gate 	if (debug)
3143391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
315*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password): out: returns %d",
3160Sstevel@tonic-gate 		    result);
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	return (result);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate int
krb5_verifypw(char * princ_str,char * old_password,int debug)3220Sstevel@tonic-gate krb5_verifypw(
3230Sstevel@tonic-gate 	char 	*princ_str,
3240Sstevel@tonic-gate 	char	*old_password,
3250Sstevel@tonic-gate 	int debug)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	kadm5_ret_t		code;
3280Sstevel@tonic-gate 	krb5_principal 		princ = 0;
3290Sstevel@tonic-gate 	char 			admin_realm[1024];
3300Sstevel@tonic-gate 	char			kprinc[2*MAXHOSTNAMELEN];
3310Sstevel@tonic-gate 	char			*cpw_service;
3320Sstevel@tonic-gate 	void 			*server_handle;
3330Sstevel@tonic-gate 	krb5_context		context;
3340Sstevel@tonic-gate 	kadm5_config_params	params;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	(void) memset((char *)&params, 0, sizeof (params));
3370Sstevel@tonic-gate 
3388991SPeter.Shoults@Sun.COM 	if (code = krb5_init_secure_context(&context)) {
3390Sstevel@tonic-gate 		return (6);
3400Sstevel@tonic-gate 	}
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
343*11780SWill.Fiveash@Sun.COM 	    2*MAXHOSTNAMELEN)) != 0) {
3440Sstevel@tonic-gate 		return (code);
3450Sstevel@tonic-gate 	}
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 	/* Need to get a krb5_principal struct */
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	code = krb5_parse_name(context, kprinc, &princ);
3500Sstevel@tonic-gate 
3512941Ssemery 	if (code != 0)
3522941Ssemery 		return (6);
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate 	if (strlen(old_password) == 0) {
3550Sstevel@tonic-gate 		krb5_free_principal(context, princ);
3560Sstevel@tonic-gate 		return (5);
3570Sstevel@tonic-gate 	}
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	(void) strlcpy(admin_realm,
360*11780SWill.Fiveash@Sun.COM 	    krb5_princ_realm(context, princ)->data,
361*11780SWill.Fiveash@Sun.COM 	    sizeof (admin_realm));
3620Sstevel@tonic-gate 
3630Sstevel@tonic-gate 	params.mask |= KADM5_CONFIG_REALM;
3640Sstevel@tonic-gate 	params.realm = admin_realm;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 
3670Sstevel@tonic-gate 	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
3683391Ssemery 		__pam_log(LOG_AUTH | LOG_ERR,
369*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password): unable to get host based "
370*11780SWill.Fiveash@Sun.COM 		    "service name for realm %s\n",
371*11780SWill.Fiveash@Sun.COM 		    admin_realm);
3722941Ssemery 		krb5_free_principal(context, princ);
3730Sstevel@tonic-gate 		return (3);
3740Sstevel@tonic-gate 	}
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	code = kadm5_init_with_password(kprinc, old_password, cpw_service,
377*11780SWill.Fiveash@Sun.COM 	    &params, KADM5_STRUCT_VERSION,
378*11780SWill.Fiveash@Sun.COM 	    KADM5_API_VERSION_2, NULL,
379*11780SWill.Fiveash@Sun.COM 	    &server_handle);
3800Sstevel@tonic-gate 	if (code != 0) {
3810Sstevel@tonic-gate 		if (debug)
3823391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
3830Sstevel@tonic-gate 			    "PAM-KRB5: krb5_verifypw: init_with_pw"
3840Sstevel@tonic-gate 			    " failed: (%s)", error_message(code));
3850Sstevel@tonic-gate 		krb5_free_principal(context, princ);
3860Sstevel@tonic-gate 		return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
3870Sstevel@tonic-gate 	}
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	krb5_free_principal(context, princ);
3900Sstevel@tonic-gate 
3910Sstevel@tonic-gate 	(void) kadm5_destroy(server_handle);
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	return (0);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate  * Function: krb5_changepw
3980Sstevel@tonic-gate  *
3990Sstevel@tonic-gate  * Purpose: Initialize and call lower level routines to change a password
4000Sstevel@tonic-gate  *
4010Sstevel@tonic-gate  * Arguments:
4020Sstevel@tonic-gate  *
4030Sstevel@tonic-gate  *	princ_str	principal name to use, optional
4040Sstevel@tonic-gate  *	old_password 	old password
4050Sstevel@tonic-gate  *	new_password  	new password
4060Sstevel@tonic-gate  *
4070Sstevel@tonic-gate  * Returns:
4080Sstevel@tonic-gate  *                      exit status of PAM_SUCCESS for success
4092941Ssemery  *			else returns PAM failure
4100Sstevel@tonic-gate  *
4110Sstevel@tonic-gate  * Requires:
4120Sstevel@tonic-gate  *	Passwords cannot be more than 255 characters long.
4130Sstevel@tonic-gate  *
4140Sstevel@tonic-gate  * Modifies:
4150Sstevel@tonic-gate  *
4160Sstevel@tonic-gate  * Changes the principal's password.
4170Sstevel@tonic-gate  *
4180Sstevel@tonic-gate  */
4190Sstevel@tonic-gate static int
krb5_changepw(pam_handle_t * pamh,char * princ_str,char * old_password,char * new_password,int debug)4200Sstevel@tonic-gate krb5_changepw(
4210Sstevel@tonic-gate 	pam_handle_t *pamh,
4220Sstevel@tonic-gate 	char *princ_str,
4230Sstevel@tonic-gate 	char *old_password,
4240Sstevel@tonic-gate 	char *new_password,
4250Sstevel@tonic-gate 	int debug)
4260Sstevel@tonic-gate {
4270Sstevel@tonic-gate 	kadm5_ret_t		code;
4280Sstevel@tonic-gate 	krb5_principal 		princ = 0;
4290Sstevel@tonic-gate 	char 			msg_ret[1024], admin_realm[1024];
4300Sstevel@tonic-gate 	char			kprinc[2*MAXHOSTNAMELEN];
4310Sstevel@tonic-gate 	char			*cpw_service;
4320Sstevel@tonic-gate 	void 			*server_handle;
4330Sstevel@tonic-gate 	krb5_context		context;
4340Sstevel@tonic-gate 	kadm5_config_params	params;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	(void) memset((char *)&params, 0, sizeof (params));
4370Sstevel@tonic-gate 
4388991SPeter.Shoults@Sun.COM 	if (krb5_init_secure_context(&context) != 0)
4392941Ssemery 		return (PAM_SYSTEM_ERR);
4400Sstevel@tonic-gate 
4410Sstevel@tonic-gate 	if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
442*11780SWill.Fiveash@Sun.COM 	    2*MAXHOSTNAMELEN)) != 0) {
4430Sstevel@tonic-gate 		return (code);
4440Sstevel@tonic-gate 	}
4450Sstevel@tonic-gate 
4460Sstevel@tonic-gate 	/* Need to get a krb5_principal struct */
4470Sstevel@tonic-gate 
4480Sstevel@tonic-gate 	code = krb5_parse_name(context, kprinc, &princ);
4492941Ssemery 	if (code != 0)
4502941Ssemery 		return (PAM_SYSTEM_ERR);
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 	if (strlen(old_password) == 0) {
4530Sstevel@tonic-gate 		krb5_free_principal(context, princ);
4542941Ssemery 		return (PAM_AUTHTOK_ERR);
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 
4570Sstevel@tonic-gate 	(void) snprintf(admin_realm, sizeof (admin_realm), "%s",
458*11780SWill.Fiveash@Sun.COM 	    krb5_princ_realm(context, princ)->data);
4590Sstevel@tonic-gate 	params.mask |= KADM5_CONFIG_REALM;
4600Sstevel@tonic-gate 	params.realm = admin_realm;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
4643391Ssemery 		__pam_log(LOG_AUTH | LOG_ERR,
465*11780SWill.Fiveash@Sun.COM 		    "PAM-KRB5 (password):unable to get host based "
466*11780SWill.Fiveash@Sun.COM 		    "service name for realm %s\n",
467*11780SWill.Fiveash@Sun.COM 		    admin_realm);
4682941Ssemery 		return (PAM_SYSTEM_ERR);
4690Sstevel@tonic-gate 	}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 	code = kadm5_init_with_password(kprinc, old_password, cpw_service,
472*11780SWill.Fiveash@Sun.COM 	    &params, KADM5_STRUCT_VERSION,
473*11780SWill.Fiveash@Sun.COM 	    KADM5_API_VERSION_2, NULL,
474*11780SWill.Fiveash@Sun.COM 	    &server_handle);
4750Sstevel@tonic-gate 	free(cpw_service);
4760Sstevel@tonic-gate 	if (code != 0) {
4770Sstevel@tonic-gate 		if (debug)
4783391Ssemery 			__pam_log(LOG_AUTH | LOG_DEBUG,
4790Sstevel@tonic-gate 			    "PAM-KRB5 (password): changepw: "
4800Sstevel@tonic-gate 			    "init_with_pw failed:  (%s)", error_message(code));
4810Sstevel@tonic-gate 		krb5_free_principal(context, princ);
4822941Ssemery 		return ((code == KADM5_BAD_PASSWORD) ?
483*11780SWill.Fiveash@Sun.COM 		    PAM_AUTHTOK_ERR : PAM_SYSTEM_ERR);
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 
4860Sstevel@tonic-gate 	code = kadm5_chpass_principal_util(server_handle, princ,
487*11780SWill.Fiveash@Sun.COM 	    new_password,
488*11780SWill.Fiveash@Sun.COM 	    NULL /* don't need pw back */,
489*11780SWill.Fiveash@Sun.COM 	    msg_ret,
490*11780SWill.Fiveash@Sun.COM 	    sizeof (msg_ret));
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 	if (code) {
4930Sstevel@tonic-gate 		char msgs[2][PAM_MAX_MSG_SIZE];
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 		(void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, "%s",
496*11780SWill.Fiveash@Sun.COM 		    dgettext(TEXT_DOMAIN,
497*11780SWill.Fiveash@Sun.COM 		    "Kerberos password not changed: "));
4980Sstevel@tonic-gate 		(void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, "%s", msg_ret);
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 		display_msgs(pamh, PAM_ERROR_MSG, 2, msgs);
5010Sstevel@tonic-gate 	}
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	krb5_free_principal(context, princ);
5040Sstevel@tonic-gate 
5050Sstevel@tonic-gate 	(void) kadm5_destroy(server_handle);
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 	if (debug)
5083391Ssemery 		__pam_log(LOG_AUTH | LOG_DEBUG,
5090Sstevel@tonic-gate 		    "PAM-KRB5 (password): changepw: end %d", code);
5100Sstevel@tonic-gate 
5112941Ssemery 	if (code != 0)
5122941Ssemery 		return (PAM_AUTHTOK_ERR);
5130Sstevel@tonic-gate 
5142941Ssemery 	return (PAM_SUCCESS);
5150Sstevel@tonic-gate }
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate static void
display_msgs(pam_handle_t * pamh,int msg_style,int nmsg,char msgs[][PAM_MAX_MSG_SIZE])5180Sstevel@tonic-gate display_msgs(pam_handle_t *pamh,
5190Sstevel@tonic-gate 	int msg_style, int nmsg, char msgs[][PAM_MAX_MSG_SIZE])
5200Sstevel@tonic-gate {
5210Sstevel@tonic-gate 	(void) __pam_display_msg(pamh, msg_style, nmsg, msgs, NULL);
5220Sstevel@tonic-gate }
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate static void
display_msg(pam_handle_t * pamh,int msg_style,char * msg)5260Sstevel@tonic-gate display_msg(pam_handle_t *pamh, int msg_style, char *msg)
5270Sstevel@tonic-gate {
5280Sstevel@tonic-gate 	char pam_msg[1][PAM_MAX_MSG_SIZE];
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 	(void) snprintf(pam_msg[0], PAM_MAX_MSG_SIZE, "%s", msg);
5310Sstevel@tonic-gate 	display_msgs(pamh, msg_style, 1, pam_msg);
5320Sstevel@tonic-gate }
533