[Freeipa-devel] [PATCH] 0071 Recover from invalid cached credentials in ipasam

Alexander Bokovoy abokovoy at redhat.com
Tue Aug 21 14:15:11 UTC 2012


On Tue, 21 Aug 2012, Simo Sorce wrote:
>----- Original Message -----
>> On Tue, 21 Aug 2012, Simo Sorce wrote:
>> >----- Original Message -----
>> >> Hi,
>> >>
>> >> https://fedorahosted.org/freeipa/ticket/3009
>> >
>> >What prevents this patch from causing an infinite loop if we keep
>> >getting the same error back at each interaction ?
>>
>> The loop is triggered when kerberos credentials were obtained
>> successfully based on cached credentials in the ccache but SASL
>> operation denied them. At this point a code after notdone label will
>> wipe out content of the ccache and attempt to acquire credentials
>> online based on the content of samba's keytab.
>>
>> Obtained credentials will be put into the ccache for further cached
>> use.
>>
>> If any step in acquiring credentials fails, the callback returns with
>> LDAP_LOCAL_ERROR, effectively ending current SASL auth attempt. On
>> higher level smbldap API user retries several times (up to two dozen
>> times) to authenticate and on complete failure calls smb_panic().
>>
>> If credentials were acquired at previous step correctly SASL step
>> cannot fail
>> with LDAP_INVALID_CREDENTIALS, there will be another error message,
>> either LDAP_INAPPROPRIATE_AUTH or LDAP_INSUFFICIENT_ACCESS. In case
>> of
>> FreeIPA setup we shouldn't remaining security error,
>> LDAP_X_PROXY_AUTHZ_FAILURE. Any of those will get us out of the loop.
>>
>> Thus, this loop is run at most twice.
>
>Ok, then ACK.

I've rewrote the patch to use helper functions instead of looping.
-- 
/ Alexander Bokovoy
-------------- next part --------------
>From efa143dbd798951659a370543dcb218e93c05667 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Mon, 20 Aug 2012 13:26:20 +0300
Subject: [PATCH 3/4] Recover from invalid cached kerberos credentials in
 ipasam

When developing and testing in the same environment, multiple re-installs
may be needed. This means previously issued and cached Kerberos credentials
will become invalid upon new install.

ipasam passdb module for Samba uses Kerberos authentication when talking to
IPA LDAP server. Obtained Kerberos credentials are cached during their lifetime.
However, the ccache is not removed automatically and if IPA setup is made
again, cached credentials are used, only to discover that they are invalid.

With this change invalid correctly obtained cached credentials are recognized
and, if LDAP SASL bind fails, new credentials are requested from the KDC.

https://fedorahosted.org/freeipa/ticket/3009
---
 daemons/ipa-sam/ipa_sam.c | 116 +++++++++++++++++++++++++++++++---------------
 1 file changed, 78 insertions(+), 38 deletions(-)

diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c
index aa54429b5bec4b26906b2a34e59ff95299a67f80..059109374bd0e1aa1de118b4767b5692d0e483a2 100644
--- a/daemons/ipa-sam/ipa_sam.c
+++ b/daemons/ipa-sam/ipa_sam.c
@@ -3204,49 +3204,70 @@ static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *s
 	return ret;
 }
 
-static void bind_callback_cleanup(struct ipasam_sasl_interact_priv *data, krb5_error_code rc)
-{
+
+static void bind_callback_cleanup_creds(struct ipasam_sasl_interact_priv *datap) {
+	krb5_free_cred_contents(datap->context, &datap->creds);
+
+	if (datap->options) {
+		krb5_get_init_creds_opt_free(datap->context, datap->options);
+		datap->options = NULL;
+	}
+}
+
+static void bind_callback_cleanup(struct ipasam_sasl_interact_priv *datap, krb5_error_code rc) {
 	const char *errstring = NULL;
 
-	if (!data->context) {
+	if (!datap->context) {
 		return;
 	}
 
 	if (rc) {
-		errstring = krb5_get_error_message(data->context, rc);
+		errstring = krb5_get_error_message(datap->context, rc);
 		DEBUG(0,("kerberos error: code=%d, message=%s\n", rc, errstring));
-		krb5_free_error_message(data->context, errstring);
+		krb5_free_error_message(datap->context, errstring);
 	}
 
-	krb5_free_cred_contents(data->context, &data->creds);
+	bind_callback_cleanup_creds(datap);
 
-	if (data->options) {
-		krb5_get_init_creds_opt_free(data->context, data->options);
-		data->options = NULL;
+	if (datap->keytab) {
+		krb5_kt_close(datap->context, datap->keytab);
+		datap->keytab = NULL;
 	}
 
-	if (data->keytab) {
-		krb5_kt_close(data->context, data->keytab);
-		data->keytab = NULL;
+	if (datap->ccache) {
+		krb5_cc_close(datap->context, datap->ccache);
+		datap->ccache = NULL;
 	}
 
-	if (data->ccache) {
-		krb5_cc_close(data->context, data->ccache);
-		data->ccache = NULL;
+	if (datap->principal) {
+		krb5_free_principal(datap->context, datap->principal);
+		datap->principal = NULL;
 	}
 
-	if (data->principal) {
-		krb5_free_principal(data->context, data->principal);
-		data->principal = NULL;
+	krb5_free_context(datap->context);
+	datap->context = NULL;
+}
+
+static krb5_error_code bind_callback_obtain_creds(struct ipasam_sasl_interact_priv *datap) {
+	krb5_error_code rc;
+
+	rc = krb5_get_init_creds_opt_alloc(datap->context, &datap->options);
+	if (rc) {
+		return rc;
+	}
+
+	rc = krb5_get_init_creds_opt_set_out_ccache(datap->context, datap->options, datap->ccache);
+	if (rc) {
+		return rc;
 	}
 
-	krb5_free_context(data->context);
-	data->context = NULL;
+	rc = krb5_get_init_creds_keytab(datap->context, &datap->creds, datap->principal, datap->keytab,
+					0, NULL, datap->options);
+	return rc;
 }
 
 extern const char * lp_dedicated_keytab_file(void);
-static int bind_callback(LDAP *ldap_struct, struct smbldap_state *ldap_state, void* ipasam_priv)
-{
+static int bind_callback(LDAP *ldap_struct, struct smbldap_state *ldap_state, void* ipasam_priv) {
 	krb5_error_code rc;
 	krb5_creds *out_creds = NULL;
 	krb5_creds in_creds;
@@ -3311,20 +3332,7 @@ static int bind_callback(LDAP *ldap_struct, struct smbldap_state *ldap_state, vo
 	krb5_free_principal(data.context, in_creds.client);
 
 	if (rc) {
-		rc = krb5_get_init_creds_opt_alloc(data.context, &data.options);
-		if (rc) {
-			bind_callback_cleanup(&data, rc);
-			return LDAP_LOCAL_ERROR;
-		}
-
-		rc = krb5_get_init_creds_opt_set_out_ccache(data.context, data.options, data.ccache);
-		if (rc) {
-			bind_callback_cleanup(&data, rc);
-			return LDAP_LOCAL_ERROR;
-		}
-
-		rc = krb5_get_init_creds_keytab(data.context, &data.creds, data.principal, data.keytab,
-						0, NULL, data.options);
+		rc = bind_callback_obtain_creds(&data);
 		if (rc) {
 			bind_callback_cleanup(&data, rc);
 			return LDAP_LOCAL_ERROR;
@@ -3336,8 +3344,40 @@ static int bind_callback(LDAP *ldap_struct, struct smbldap_state *ldap_state, vo
 					   NULL, NULL,
 					   LDAP_SASL_QUIET,
 					   ldap_sasl_interact, &data);
-	if (ret != LDAP_SUCCESS) {
-		DEBUG(0, ("bind_callback: cannot perform interactive SASL bind with GSSAPI\n"));
+
+	/* By now we have 'ret' for LDAP result and 'rc' for Kerberos result
+	 * if ret is LDAP_INVALID_CREDENTIALS, LDAP server rejected our ccache. There may be several issues:
+	 *
+	 * 1. Credentials are invalid due to outdated ccache leftover from previous install
+	 *    Wipe out old ccache and start again
+	 *
+	 * 2. Key in the keytab is not enough to obtain ticket for cifs/FQDN at REALM service
+	 *    Cannot continue without proper keytab
+	 *
+	 * Only process (1) because (2) and other errors will be taken care of by smbd after multiple retries.
+	 *
+	 * Since both smbd and winbindd will use this passdb module, on startup both will try to access the same
+	 * ccache. It may happen that if ccache was missing or contained invalid cached credentials, that one of
+	 * them will complain loudly about missing ccache file at the time when the other one will be creating
+	 * a new ccache file by the above call of bind_callback_obtain_creds(). This is expected and correct behavior.
+	 *
+	 */
+	if ((ret == LDAP_INVALID_CREDENTIALS) && (rc == 0)) {
+		bind_callback_cleanup_creds(&data);
+		rc = bind_callback_obtain_creds(&data);
+		if (rc) {
+			bind_callback_cleanup(&data, rc);
+			return LDAP_LOCAL_ERROR;
+		}
+		ret = ldap_sasl_interactive_bind_s(ldap_struct,
+						   NULL, "GSSAPI",
+						   NULL, NULL,
+						   LDAP_SASL_QUIET,
+						   ldap_sasl_interact, &data);
+	}
+
+	if (LDAP_SECURITY_ERROR(ret)) {
+		DEBUG(0, ("bind_callback: cannot perform interactive SASL bind with GSSAPI. LDAP security error is %d\n", ret));
 	}
 
 	if (out_creds) {
-- 
1.7.11.4



More information about the Freeipa-devel mailing list