summaryrefslogtreecommitdiff
path: root/usr/src/lib/pam_modules/krb5/krb5_authenticate.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/pam_modules/krb5/krb5_authenticate.c
downloadillumos-gate-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/pam_modules/krb5/krb5_authenticate.c')
-rw-r--r--usr/src/lib/pam_modules/krb5/krb5_authenticate.c781
1 files changed, 781 insertions, 0 deletions
diff --git a/usr/src/lib/pam_modules/krb5/krb5_authenticate.c b/usr/src/lib/pam_modules/krb5/krb5_authenticate.c
new file mode 100644
index 0000000000..862b2b4ac0
--- /dev/null
+++ b/usr/src/lib/pam_modules/krb5/krb5_authenticate.c
@@ -0,0 +1,781 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_impl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <libintl.h>
+#include <k5-int.h>
+#include "profile/prof_int.h"
+#include <netdb.h>
+#include <ctype.h>
+#include "utils.h"
+#include "krb5_repository.h"
+
+#define PAMTXD "SUNW_OST_SYSOSPAM"
+#define SLEEPTIME 4
+
+#define KRB5_DEFAULT_OPTIONS 0
+#define QUIET 0
+#define VERBOSE 1
+
+int forwardable_flag = 0;
+int renewable_flag = 0;
+int proxiable_flag = 0;
+int no_address_flag = 0;
+profile_options_boolean config_option[] = {
+ { "forwardable", &forwardable_flag, 0 },
+ { "renewable", &renewable_flag, 0 },
+ { "proxiable", &proxiable_flag, 0 },
+ { "no_addresses", &no_address_flag, 0 },
+ { NULL, NULL, 0 }
+};
+char *renew_timeval;
+char *life_timeval;
+profile_option_strings config_times[] = {
+ { "max_life", &life_timeval, 0 },
+ { "max_renewable_life", &renew_timeval, 0 },
+ { NULL, NULL, 0 }
+};
+char *realmdef[] = { "realms", NULL, NULL, NULL };
+char *appdef[] = { "appdefaults", "kinit", NULL };
+
+#define krb_realm (*(realmdef + 1))
+
+int attempt_krb5_auth(void *, krb5_module_data_t *, char *, char **,
+ boolean_t, boolean_t);
+void krb5_cleanup(pam_handle_t *, void *, int);
+
+extern errcode_t profile_get_options_boolean();
+extern errcode_t profile_get_options_string();
+extern int krb5_verifypw(pam_handle_t *, char *, char *, boolean_t, int);
+extern krb5_error_code krb5_verify_init_creds(krb5_context,
+ krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *,
+ krb5_verify_init_creds_opt *);
+
+/*
+ * pam_sm_authenticate - Authenticate user
+ */
+int
+pam_sm_authenticate(
+ pam_handle_t *pamh,
+ int flags,
+ int argc,
+ const char **argv)
+{
+ char *user;
+ struct pam_conv *pam_convp;
+ int err;
+ int result = PAM_AUTH_ERR;
+ /* pam.conf options */
+ int debug = 0;
+ int warn = 1;
+ /* return an error on password expire */
+ int err_on_exp = 0;
+ int invalid_user = 0;
+ int i;
+ char *firstpass = NULL;
+ char *password = NULL;
+ uid_t pw_uid;
+ krb5_module_data_t *kmd = NULL;
+ krb5_repository_data_t *krb5_data = NULL;
+ pam_repository_t *rep_data = NULL;
+
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], "debug") == 0) {
+ debug = 1;
+ } else if (strcmp(argv[i], "nowarn") == 0) {
+ warn = 0;
+ } else if (strcmp(argv[i], "err_on_exp") == 0) {
+ err_on_exp = 1;
+ } else {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth) unrecognized option %s"),
+ argv[i]);
+ }
+ }
+ if (flags & PAM_SILENT) warn = 0;
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): pam_sm_authenticate flags=%d",
+ flags);
+
+ err = pam_get_item(pamh, PAM_USER, (void**) &user);
+ if (err != PAM_SUCCESS)
+ return (err);
+
+ /* Prompt for user name if it is not already available */
+ if (user == NULL || !user[0]) {
+ if (debug)
+ syslog(LOG_DEBUG, "PAM-KRB5 (auth): user empty "
+ "or null");
+ if ((err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
+ return (err);
+
+ if (user == NULL || !user[0])
+ return (PAM_USER_UNKNOWN);
+ }
+
+ err = pam_get_item(pamh, PAM_CONV, (void**) &pam_convp);
+ if (err != PAM_SUCCESS)
+ return (err);
+
+ /* make sure a password entry exists for this user */
+ if (!get_pw_uid(user, &pw_uid)) {
+ invalid_user = 1;
+ }
+
+ /*
+ * pam_get_data could fail if we are being called for the first time
+ * or if the module is not found, PAM_NO_MODULE_DATA is not an error
+ */
+ err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd);
+ if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA))
+ return (PAM_AUTH_ERR);
+
+ if (kmd == NULL) {
+ kmd = calloc(1, sizeof (krb5_module_data_t));
+ if (kmd == NULL) {
+ result = PAM_BUF_ERR;
+ goto out;
+ }
+
+ err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup);
+ if (err != PAM_SUCCESS) {
+ free(kmd);
+ result = err;
+ goto out;
+ }
+ }
+
+ if (!kmd->env) {
+ char buffer[512];
+
+ if (snprintf(buffer, sizeof (buffer),
+ "%s=FILE:/tmp/krb5cc_%d",
+ KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) {
+ result = PAM_SYSTEM_ERR;
+ goto out;
+ }
+
+ /* we MUST copy this to the heap for the putenv to work! */
+ kmd->env = strdup(buffer);
+ if (!kmd->env) {
+ result = PAM_BUF_ERR;
+ goto out;
+ } else {
+ if (putenv(kmd->env)) {
+ result = PAM_SYSTEM_ERR;
+ goto out;
+ }
+ }
+ }
+
+ kmd->auth_status = PAM_AUTH_ERR;
+ kmd->debug = debug;
+ kmd->warn = warn;
+ kmd->err_on_exp = err_on_exp;
+ kmd->ccache = NULL;
+ kmd->kcontext = NULL;
+ kmd->password = NULL;
+ kmd->age_status = PAM_SUCCESS;
+ (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
+
+ /*
+ * For apps that already did krb5 auth exchange...
+ * Now that we've created the kmd structure, we can
+ * return SUCCESS. 'kmd' may be needed later by other
+ * PAM functions, thats why we wait until this point to
+ * return.
+ */
+ err = pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
+
+ if (rep_data != NULL) {
+ if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
+ if (debug)
+ syslog(LOG_DEBUG, "PAM-KRB5 (auth): wrong"
+ "repository found (%s), returning "
+ "PAM_IGNORE", rep_data->type);
+ return (PAM_IGNORE);
+ }
+ if (rep_data->scope_len == sizeof (krb5_repository_data_t)) {
+ krb5_data = (krb5_repository_data_t *)rep_data->scope;
+
+ if (krb5_data->flags ==
+ SUNW_PAM_KRB5_ALREADY_AUTHENTICATED &&
+ krb5_data->principal != NULL &&
+ strlen(krb5_data->principal)) {
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): Principal "
+ "%s already authenticated",
+ krb5_data->principal);
+ kmd->auth_status = PAM_SUCCESS;
+ return (PAM_SUCCESS);
+ }
+ }
+ }
+
+ /*
+ * if root key exists in the keytab, it's a random key so no
+ * need to prompt for pw and we just return IGNORE.
+ *
+ * note we don't need to force a prompt for pw as authtok_get
+ * is required to be stacked above this module.
+ */
+ if ((strcmp(user, ROOT_UNAME) == 0) &&
+ key_in_keytab(user, debug)) {
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): "
+ "key for '%s' in keytab, returning IGNORE", user);
+ result = PAM_IGNORE;
+ goto out;
+ }
+
+ err = pam_get_item(pamh, PAM_AUTHTOK, (void **) &firstpass);
+
+ if (firstpass != NULL && invalid_user)
+ goto out;
+
+ result = attempt_krb5_auth(pamh, kmd, user, &firstpass, 1, QUIET);
+ if (result != PAM_AUTH_ERR) {
+ goto out;
+ }
+
+ /*
+ * Get the password from the user
+ */
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): prompting for password");
+
+ /*
+ * one last ditch attempt to login to KRB5
+ *
+ * we have to prompt for password even if the user does not exist
+ * so as not to reveal the existence/non-existence of an account
+ */
+ result = attempt_krb5_auth(pamh, kmd, user, &password, 1, VERBOSE);
+
+ if (invalid_user)
+ goto out;
+
+ if (result == PAM_SUCCESS) {
+ /*
+ * Even if this password is expired, this saves
+ * us from having to enter it again!
+ */
+ (void) pam_set_item(pamh, PAM_AUTHTOK, password);
+ }
+
+out:
+
+ if (password != NULL)
+ (void) memset(password, 0, strlen(password));
+
+ if (invalid_user)
+ result = PAM_USER_UNKNOWN;
+
+ if (kmd) {
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): pam_sm_auth finalize"
+ " ccname env, result =%d, env ='%s',"
+ " age = %d, status = %d",
+ result, kmd->env ? kmd->env : "<null>",
+ kmd->age_status, kmd->auth_status);
+
+ if (kmd->env &&
+ !(kmd->age_status == PAM_NEW_AUTHTOK_REQD &&
+ kmd->auth_status == PAM_SUCCESS)) {
+
+
+ if (result == PAM_SUCCESS) {
+ /*
+ * Put ccname into the pamh so that login
+ * apps can pick this up when they run
+ * pam_getenvlist().
+ */
+ if ((result = pam_putenv(pamh, kmd->env))
+ != PAM_SUCCESS) {
+ /* should not happen but... */
+ syslog(LOG_ERR,
+ dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth):"
+ " pam_putenv failed: result: %d"),
+ result);
+ goto cleanupccname;
+ }
+ } else {
+ cleanupccname:
+ /* for lack of a Solaris unputenv() */
+ krb5_unsetenv(KRB5_ENV_CCNAME);
+ free(kmd->env);
+ kmd->env = NULL;
+ }
+ }
+ kmd->auth_status = result;
+ }
+
+ if (debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result));
+
+ return (result);
+}
+
+int
+attempt_krb5_auth(
+ void *pamh,
+ krb5_module_data_t *kmd,
+ char *user,
+ char **krb5_pass,
+ boolean_t verify_tik,
+ boolean_t verbose)
+{
+ krb5_principal me = NULL;
+ krb5_principal server = NULL;
+ krb5_creds *my_creds;
+ krb5_timestamp now;
+ krb5_error_code code = 0;
+ char kuser[2*MAXHOSTNAMELEN];
+ char passprompt[MAX_CANON];
+ krb5_deltat lifetime;
+ krb5_deltat rlife;
+ krb5_deltat krb5_max_duration;
+ int options = KRB5_DEFAULT_OPTIONS;
+ krb5_data tgtname = {
+ 0,
+ KRB5_TGS_NAME_SIZE,
+ KRB5_TGS_NAME
+ };
+ char krb5_auth_messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
+ krb5_get_init_creds_opt opts;
+ /*
+ * "result" should not be assigned PAM_SUCCESS unless
+ * authentication has succeeded and there are no other errors.
+ *
+ * "code" is sometimes used for PAM codes, sometimes for krb5
+ * codes. Be careful.
+ */
+ int result = PAM_AUTH_ERR;
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'",
+ user ? user : "<null>");
+
+ krb5_get_init_creds_opt_init(&opts);
+
+ /* need to free context with krb5_free_context */
+ if (code = krb5_init_context(&kmd->kcontext)) {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Error initializing "
+ "krb5: %s"),
+ error_message(code));
+ return (PAM_SYSTEM_ERR);
+ }
+
+ if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser,
+ 2*MAXHOSTNAMELEN)) != 0) {
+ /* get_kmd_kuser returns proper PAM error statuses */
+ return (code);
+ }
+
+ if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) {
+ krb5_free_context(kmd->kcontext);
+ kmd->kcontext = NULL;
+ return (PAM_AUTH_ERR);
+ }
+
+ /* call krb5_free_cred_contents() on error */
+ my_creds = &kmd->initcreds;
+
+ if ((code = krb5_copy_principal(kmd->kcontext, me, &my_creds->client)))
+ goto out_err;
+
+ if (code = krb5_build_principal_ext(kmd->kcontext, &server,
+ krb5_princ_realm(kmd->kcontext, me)->length,
+ krb5_princ_realm(kmd->kcontext, me)->data,
+ tgtname.length, tgtname.data,
+ krb5_princ_realm(kmd->kcontext, me)->length,
+ krb5_princ_realm(kmd->kcontext, me)->data, 0)) {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): attempt_krb5_auth: "
+ "krb5_build_princ_ext failed: %s"),
+ error_message(code));
+ goto out;
+ }
+
+ if (code = krb5_copy_principal(kmd->kcontext, server,
+ &my_creds->server)) {
+ goto out_err;
+ }
+
+ if (code = krb5_timeofday(kmd->kcontext, &now)) {
+ syslog(LOG_ERR, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): attempt_krb5_auth: "
+ "krb5_timeofday failed: %s"),
+ error_message(code));
+ goto out;
+ }
+
+ /*
+ * set the values for lifetime and rlife to be the maximum
+ * possible
+ */
+ krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60;
+ lifetime = krb5_max_duration;
+ rlife = krb5_max_duration;
+
+ /*
+ * Let us get the values for various options
+ * from Kerberos configuration file
+ */
+
+ krb_realm = krb5_princ_realm(kmd->kcontext, me)->data;
+ profile_get_options_boolean(kmd->kcontext->profile,
+ realmdef, config_option);
+ profile_get_options_boolean(kmd->kcontext->profile,
+ appdef, config_option);
+ profile_get_options_string(kmd->kcontext->profile,
+ realmdef, config_times);
+ profile_get_options_string(kmd->kcontext->profile,
+ appdef, config_times);
+
+ if (renew_timeval) {
+ code = krb5_string_to_deltat(renew_timeval, &rlife);
+ if (code != 0 || rlife == 0 || rlife > krb5_max_duration) {
+ syslog(LOG_ERR,
+ dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Bad max_renewable_life "
+ " value '%s' in Kerberos config file"),
+ renew_timeval);
+ result = PAM_SYSTEM_ERR;
+ goto out;
+ }
+ }
+ if (life_timeval) {
+ code = krb5_string_to_deltat(life_timeval, &lifetime);
+ if (code != 0 || lifetime == 0 ||
+ lifetime > krb5_max_duration) {
+ syslog(LOG_ERR,
+ dgettext(TEXT_DOMAIN, "PAM-KRB5 (auth): Bad "
+ "lifetime value '%s' in Kerberos config file"),
+ life_timeval);
+ result = PAM_SYSTEM_ERR;
+ goto out;
+ }
+ }
+ /* start timer when request gets to KDC */
+ my_creds->times.starttime = 0;
+ my_creds->times.endtime = now + lifetime;
+
+ if (options & KDC_OPT_RENEWABLE) {
+ my_creds->times.renew_till = now + rlife;
+ } else
+ my_creds->times.renew_till = 0;
+
+ krb5_get_init_creds_opt_set_tkt_life(&opts, lifetime);
+
+ if (proxiable_flag) { /* Set in config file */
+ if (kmd->debug)
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Proxiable tickets "
+ "requested"));
+ krb5_get_init_creds_opt_set_proxiable(&opts, TRUE);
+ }
+ if (forwardable_flag) {
+ if (kmd->debug)
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Forwardable tickets "
+ "requested"));
+ krb5_get_init_creds_opt_set_forwardable(&opts, TRUE);
+ }
+ if (renewable_flag) {
+ if (kmd->debug)
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Renewable tickets "
+ "requested"));
+ krb5_get_init_creds_opt_set_renew_life(&opts, rlife);
+ }
+ if (no_address_flag) {
+ if (kmd->debug)
+ syslog(LOG_DEBUG, dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): Addressless tickets "
+ "requested"));
+ krb5_get_init_creds_opt_set_address_list(&opts, NULL);
+ }
+
+ if (*krb5_pass == NULL) {
+ (void) strlcpy(passprompt, dgettext(TEXT_DOMAIN,
+ "Enter Kerberos password for "), MAX_CANON);
+ (void) strlcat(passprompt, kuser, MAX_CANON);
+ (void) strlcat(passprompt, ": ", MAX_CANON);
+ /*
+ * Upon success do not assign the resulting value to "result",
+ * because this value is returned from this function when the
+ * subsequent functions could fail which may allow unauthorized
+ * login.
+ */
+ code = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
+ passprompt, krb5_pass);
+ if (code != PAM_SUCCESS) {
+ /* Set correct return value for use below. */
+ result = code;
+ goto out;
+ }
+ }
+
+ /*
+ * mech_krb5 interprets empty passwords as NULL passwords
+ * and tries to read a password from stdin. Since we are in
+ * pam this is bad and should not be allowed.
+ */
+ if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) {
+ code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ } else {
+ code = krb5_get_init_creds_password(kmd->kcontext,
+ my_creds,
+ me,
+ *krb5_pass, /* clear text passwd */
+ NULL, /* prompter */
+ NULL, /* data */
+ 0, /* start time */
+ NULL, /* defaults to krbtgt@REALM */
+ &opts);
+ }
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): attempt_krb5_auth: "
+ "krb5_get_init_creds_password returns: %s",
+ code == 0 ? "SUCCESS" : error_message(code));
+
+ switch (code) {
+ case 0:
+ /* got a tgt, let's verify it */
+ if (verify_tik) {
+ krb5_verify_init_creds_opt vopts;
+
+ krb5_verify_init_creds_opt_init(&vopts);
+
+ code = krb5_verify_init_creds(kmd->kcontext,
+ my_creds,
+ NULL, /* defaults to host/localhost@REALM */
+ NULL,
+ NULL,
+ &vopts);
+
+ if (code) {
+ result = PAM_SYSTEM_ERR;
+ if (verbose) {
+ (void) snprintf(krb5_auth_messages[0],
+ sizeof (krb5_auth_messages[0]),
+ dgettext(TEXT_DOMAIN,
+ "authentication failed: "
+ "%s\n"),
+ error_message(code));
+ (void) __pam_display_msg(pamh,
+ PAM_TEXT_INFO, 1,
+ krb5_auth_messages, NULL);
+ }
+
+ syslog(LOG_ERR,
+ dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): "
+ "krb5_verify_init_creds failed: %s"),
+ error_message(code));
+ }
+ }
+ break;
+
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+ /*
+ * Since this principal is not part of the local
+ * Kerberos realm, we just return PAM_USER_UNKNOWN.
+ */
+ result = PAM_USER_UNKNOWN;
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG, "PAM-KRB5 (auth): attempt_krb5_auth:"
+ " User is not part of the local Kerberos"
+ " realm: %s", error_message(code));
+ break;
+
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ /*
+ * We could be trying the password from a previous
+ * pam authentication module, but we don't want to
+ * generate an error if the unix password is different
+ * than the Kerberos password...
+ */
+ if (verbose) {
+ (void) snprintf(krb5_auth_messages[0],
+ sizeof (krb5_auth_messages[0]),
+ dgettext(TEXT_DOMAIN,
+ "Kerberos authentication failed\n"));
+ (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
+ krb5_auth_messages, NULL);
+ }
+ break;
+
+ case KRB5KDC_ERR_KEY_EXP:
+ if (!kmd->err_on_exp) {
+ /*
+ * Request a tik for changepw service
+ * and it will tell us if pw is good or not.
+ */
+ code = krb5_verifypw(pamh, kuser, *krb5_pass,
+ 0, kmd->debug);
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): attempt_krb5_auth: "
+ "verifypw %s", error_message(code));
+
+ if (code == 0) {
+ /* pw is good, set age status for acct_mgmt */
+ kmd->age_status = PAM_NEW_AUTHTOK_REQD;
+ } else if ((code == 2) && verbose) {
+ /* bad password */
+ (void) snprintf(krb5_auth_messages[0],
+ sizeof (krb5_auth_messages[0]),
+ dgettext(TEXT_DOMAIN,
+ "password incorrect\n"));
+ (void) __pam_display_msg(pamh, PAM_TEXT_INFO, 1,
+ krb5_auth_messages, NULL);
+ }
+ }
+ break;
+
+ default:
+ result = PAM_SYSTEM_ERR;
+ if (kmd->debug)
+ syslog(LOG_DEBUG, "PAM-KRB5 (auth): error %d - %s",
+ code, error_message(code));
+ break;
+ }
+
+ if (code == 0) {
+ /*
+ * success for the entered pw
+ *
+ * we can't rely on the pw in PAM_AUTHTOK
+ * to be the (correct) krb5 one so
+ * store krb5 pw in module data for
+ * use in acct_mgmt
+ */
+ if (!(kmd->password = strdup(*krb5_pass))) {
+ syslog(LOG_ERR, "Cannot strdup password");
+ result = PAM_BUF_ERR;
+ goto out_err;
+ }
+ result = PAM_SUCCESS;
+ goto out;
+ }
+
+out_err:
+ /* jump (or reach) here if error and cred cache has been init */
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): clearing initcreds in "
+ "pam_authenticate()");
+
+ krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
+ (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
+
+out:
+ if (server)
+ krb5_free_principal(kmd->kcontext, server);
+ if (me)
+ krb5_free_principal(kmd->kcontext, me);
+ if (kmd->kcontext) {
+ krb5_free_context(kmd->kcontext);
+ kmd->kcontext = NULL;
+ }
+
+ if (kmd->debug)
+ syslog(LOG_DEBUG,
+ "PAM-KRB5 (auth): attempt_krb5_auth returning %d",
+ result);
+
+ return (kmd->auth_status = result);
+}
+
+/*ARGSUSED*/
+void
+krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status)
+{
+ krb5_module_data_t *kmd = (krb5_module_data_t *)data;
+
+ if (kmd == NULL)
+ return;
+
+ if (kmd->debug) {
+ syslog(LOG_DEBUG,
+ dgettext(TEXT_DOMAIN,
+ "PAM-KRB5 (auth): krb5_cleanup auth_status = %d"),
+ kmd->auth_status);
+ }
+
+ /*
+ * if pam_status is PAM_SUCCESS, clean up based on value in
+ * auth_status, otherwise just purge the context
+ */
+ if ((pam_status == PAM_SUCCESS) &&
+ (kmd->auth_status == PAM_SUCCESS) && kmd->ccache)
+ /* LINTED */
+ krb5_cc_close(kmd->kcontext, kmd->ccache);
+
+ if (kmd->password) {
+ (void) memset(kmd->password, 0, strlen(kmd->password));
+ free(kmd->password);
+ }
+
+ if ((pam_status != PAM_SUCCESS) ||
+ (kmd->auth_status != PAM_SUCCESS)) {
+ krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
+ (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
+ }
+
+ free(kmd);
+}