[SCM] Samba Shared Repository - branch v3-5-test updated

Karolin Seeger kseeger at samba.org
Thu Jun 3 07:34:04 MDT 2010


The branch, v3-5-test has been updated
       via  89eea1f... s3: Allow previous password to be stored and use it to check tickets
      from  386a462... s3-samr: Fix crash bug in _samr_QueryUserInfo{2} level 18.

http://gitweb.samba.org/?p=samba.git;a=shortlog;h=v3-5-test


- Log -----------------------------------------------------------------
commit 89eea1fa9154c67ae4d3e729a8db7ad17ec9b9d7
Author: Matthieu Patou <mat at matws.net>
Date:   Fri May 21 11:57:29 2010 +0400

    s3: Allow previous password to be stored and use it to check tickets
    
    This patch is to fix bug 7099. It stores the current password in the
     previous password key when the password is changed. It also check the
     user ticket against previous password.
    
    Signed-off-by: Günther Deschner <gd at samba.org>
    
    Fix bug #7099 (Every Thursday at 11:08-11:15am Windows Client
    Connections break with Kerberos errors).

-----------------------------------------------------------------------

Summary of changes:
 source3/include/proto.h          |    1 +
 source3/include/secrets.h        |    1 +
 source3/libads/kerberos_verify.c |   95 +++++++++++++++++++++++---------------
 source3/passdb/secrets.c         |   81 ++++++++++++++++++++++++++++++--
 4 files changed, 137 insertions(+), 41 deletions(-)


Changeset truncated at 500 lines:

diff --git a/source3/include/proto.h b/source3/include/proto.h
index 04065b0..9da40b0 100644
--- a/source3/include/proto.h
+++ b/source3/include/proto.h
@@ -4713,6 +4713,7 @@ bool secrets_delete_machine_password(const char *domain);
 bool secrets_delete_machine_password_ex(const char *domain);
 bool secrets_delete_domain_sid(const char *domain);
 bool secrets_store_machine_password(const char *pass, const char *domain, enum netr_SchannelType sec_channel);
+char *secrets_fetch_prev_machine_password(const char *domain);
 char *secrets_fetch_machine_password(const char *domain,
 				     time_t *pass_last_set_time,
 				     enum netr_SchannelType *channel);
diff --git a/source3/include/secrets.h b/source3/include/secrets.h
index f369379..41d1af3 100644
--- a/source3/include/secrets.h
+++ b/source3/include/secrets.h
@@ -25,6 +25,7 @@
 */
 #define SECRETS_MACHINE_ACCT_PASS "SECRETS/$MACHINE.ACC"
 #define SECRETS_MACHINE_PASSWORD "SECRETS/MACHINE_PASSWORD"
+#define SECRETS_MACHINE_PASSWORD_PREV "SECRETS/MACHINE_PASSWORD.PREV"
 #define SECRETS_MACHINE_LAST_CHANGE_TIME "SECRETS/MACHINE_LAST_CHANGE_TIME"
 #define SECRETS_MACHINE_SEC_CHANNEL_TYPE "SECRETS/MACHINE_SEC_CHANNEL_TYPE"
 #define SECRETS_MACHINE_TRUST_ACCOUNT_NAME "SECRETS/SECRETS_MACHINE_TRUST_ACCOUNT_NAME"
diff --git a/source3/libads/kerberos_verify.c b/source3/libads/kerberos_verify.c
index bf9bca6..68ba73c 100644
--- a/source3/libads/kerberos_verify.c
+++ b/source3/libads/kerberos_verify.c
@@ -307,8 +307,10 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
 {
 	krb5_error_code ret = 0;
 	bool auth_ok = False;
+	bool cont = true;
 	char *password_s = NULL;
-	krb5_data password;
+	/* Let's make some room for 2 password (old and new)*/
+	krb5_data passwords[2];
 	krb5_enctype enctypes[] = { 
 #if defined(ENCTYPE_ARCFOUR_HMAC)
 		ENCTYPE_ARCFOUR_HMAC,
@@ -318,12 +320,13 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
 		ENCTYPE_NULL
 	};
 	krb5_data packet;
-	int i;
+	int i, j;
 
 	*pp_tkt = NULL;
 	*keyblock = NULL;
 	*perr = 0;
 
+	ZERO_STRUCT(passwords);
 
 	if (!secrets_init()) {
 		DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n"));
@@ -338,8 +341,15 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
 		return False;
 	}
 
-	password.data = password_s;
-	password.length = strlen(password_s);
+	passwords[0].data = password_s;
+	passwords[0].length = strlen(password_s);
+
+	password_s = secrets_fetch_prev_machine_password(lp_workgroup());
+	if (password_s) {
+		DEBUG(10,("ads_secrets_verify_ticket: found previous password\n"));
+		passwords[1].data = password_s;
+		passwords[1].length = strlen(password_s);
+	}
 
 	/* CIFS doesn't use addresses in tickets. This would break NAT. JRA */
 
@@ -347,50 +357,61 @@ static krb5_error_code ads_secrets_verify_ticket(krb5_context context,
 	packet.data = (char *)ticket->data;
 
 	/* We need to setup a auth context with each possible encoding type in turn. */
-	for (i=0;enctypes[i];i++) {
-		krb5_keyblock *key = NULL;
+	for (j=0; j<2 && passwords[j].length; j++) {
 
-		if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
-			ret = ENOMEM;
-			goto out;
-		}
-	
-		if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i], false)) {
-			SAFE_FREE(key);
-			continue;
-		}
+		for (i=0;enctypes[i];i++) {
+			krb5_keyblock *key = NULL;
 
-		krb5_auth_con_setuseruserkey(context, auth_context, key);
+			if (!(key = SMB_MALLOC_P(krb5_keyblock))) {
+				ret = ENOMEM;
+				goto out;
+			}
 
-		if (!(ret = krb5_rd_req(context, &auth_context, &packet, 
-					NULL,
-					NULL, NULL, pp_tkt))) {
-			DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n",
-				(unsigned int)enctypes[i] ));
-			auth_ok = True;
-			krb5_copy_keyblock(context, key, keyblock);
-			krb5_free_keyblock(context, key);
-			break;
-		}
+			if (create_kerberos_key_from_string(context, host_princ, &passwords[j], key, enctypes[i], false)) {
+				SAFE_FREE(key);
+				continue;
+			}
+
+			krb5_auth_con_setuseruserkey(context, auth_context, key);
+
+			if (!(ret = krb5_rd_req(context, &auth_context, &packet,
+						NULL,
+						NULL, NULL, pp_tkt))) {
+				DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n",
+					(unsigned int)enctypes[i] ));
+				auth_ok = True;
+				cont = false;
+				krb5_copy_keyblock(context, key, keyblock);
+				krb5_free_keyblock(context, key);
+				break;
+			}
 
-		DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
-				("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
-				(unsigned int)enctypes[i], error_message(ret)));
+			DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
+					("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
+					(unsigned int)enctypes[i], error_message(ret)));
+
+			/* successfully decrypted but ticket is just not valid at the moment */
+			if (ret == KRB5KRB_AP_ERR_TKT_NYV ||
+			    ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
+			    ret == KRB5KRB_AP_ERR_SKEW) {
+				krb5_free_keyblock(context, key);
+				cont = false;
+				break;
+			}
 
-		/* successfully decrypted but ticket is just not valid at the moment */
-		if (ret == KRB5KRB_AP_ERR_TKT_NYV || 
-		    ret == KRB5KRB_AP_ERR_TKT_EXPIRED ||
-		    ret == KRB5KRB_AP_ERR_SKEW) {
 			krb5_free_keyblock(context, key);
+		}
+		if (!cont) {
+			/* If we found a valid pass then no need to try
+			 * the next one or we have invalid ticket so no need
+			 * to try next password*/
 			break;
 		}
-
-		krb5_free_keyblock(context, key);
-
 	}
 
  out:
-	SAFE_FREE(password_s);
+	SAFE_FREE(passwords[0].data);
+	SAFE_FREE(passwords[1].data);
 	*perr = ret;
 	return auth_ok;
 }
diff --git a/source3/passdb/secrets.c b/source3/passdb/secrets.c
index 67216a7..4eca9db 100644
--- a/source3/passdb/secrets.c
+++ b/source3/passdb/secrets.c
@@ -324,6 +324,23 @@ static const char *machine_last_change_time_keystr(const char *domain)
 
 
 /**
+ * Form a key for fetching the machine previous trust account password
+ *
+ * @param domain domain name
+ *
+ * @return keystring
+ **/
+static const char *machine_prev_password_keystr(const char *domain)
+{
+	char *keystr;
+
+	keystr = talloc_asprintf_strupper_m(talloc_tos(), "%s/%s",
+					    SECRETS_MACHINE_PASSWORD_PREV, domain);
+	SMB_ASSERT(keystr != NULL);
+	return keystr;
+}
+
+/**
  * Form a key for fetching the machine trust account password
  *
  * @param domain domain name
@@ -571,21 +588,42 @@ bool secrets_store_trusted_domain_password(const char* domain, const char* pwd,
 }
 
 /************************************************************************
- Routine to delete the plaintext machine account password
+ Routine to delete the old plaintext machine account password if any
+************************************************************************/
+
+static bool secrets_delete_prev_machine_password(const char *domain)
+{
+	char *oldpass = (char *)secrets_fetch(machine_prev_password_keystr(domain), NULL);
+	if (oldpass == NULL) {
+		return true;
+	}
+	SAFE_FREE(oldpass);
+	return secrets_delete(machine_prev_password_keystr(domain));
+}
+
+/************************************************************************
+ Routine to delete the plaintext machine account password and old
+ password if any
 ************************************************************************/
 
 bool secrets_delete_machine_password(const char *domain)
 {
+	if (!secrets_delete_prev_machine_password(domain)) {
+		return false;
+	}
 	return secrets_delete(machine_password_keystr(domain));
 }
 
 /************************************************************************
- Routine to delete the plaintext machine account password, sec channel type and
- last change time from secrets database
+ Routine to delete the plaintext machine account password, old password,
+ sec channel type and last change time from secrets database
 ************************************************************************/
 
 bool secrets_delete_machine_password_ex(const char *domain)
 {
+	if (!secrets_delete_prev_machine_password(domain)) {
+		return false;
+	}
 	if (!secrets_delete(machine_password_keystr(domain))) {
 		return false;
 	}
@@ -605,8 +643,28 @@ bool secrets_delete_domain_sid(const char *domain)
 }
 
 /************************************************************************
+ Routine to store the previous machine password (by storing the current password
+ as the old)
+************************************************************************/
+
+static bool secrets_store_prev_machine_password(const char *domain)
+{
+	char *oldpass;
+	bool ret;
+
+	oldpass = (char *)secrets_fetch(machine_password_keystr(domain), NULL);
+	if (oldpass == NULL) {
+		return true;
+	}
+	ret = secrets_store(machine_prev_password_keystr(domain), oldpass, strlen(oldpass)+1);
+	SAFE_FREE(oldpass);
+	return ret;
+}
+
+/************************************************************************
  Routine to set the plaintext machine account password for a realm
-the password is assumed to be a null terminated ascii string
+ the password is assumed to be a null terminated ascii string.
+ Before storing
 ************************************************************************/
 
 bool secrets_store_machine_password(const char *pass, const char *domain,
@@ -616,6 +674,10 @@ bool secrets_store_machine_password(const char *pass, const char *domain,
 	uint32 last_change_time;
 	uint32 sec_channel_type;
 
+	if (!secrets_store_prev_machine_password(domain)) {
+		return false;
+	}
+
 	ret = secrets_store(machine_password_keystr(domain), pass, strlen(pass)+1);
 	if (!ret)
 		return ret;
@@ -629,6 +691,17 @@ bool secrets_store_machine_password(const char *pass, const char *domain,
 	return ret;
 }
 
+
+/************************************************************************
+ Routine to fetch the previous plaintext machine account password for a realm
+ the password is assumed to be a null terminated ascii string.
+************************************************************************/
+
+char *secrets_fetch_prev_machine_password(const char *domain)
+{
+	return (char *)secrets_fetch(machine_prev_password_keystr(domain), NULL);
+}
+
 /************************************************************************
  Routine to fetch the plaintext machine account password for a realm
  the password is assumed to be a null terminated ascii string.


-- 
Samba Shared Repository


More information about the samba-cvs mailing list