diff options
-rw-r--r-- | source3/include/proto.h | 1 | ||||
-rw-r--r-- | source3/include/secrets.h | 1 | ||||
-rw-r--r-- | source3/libads/kerberos_verify.c | 95 | ||||
-rw-r--r-- | source3/passdb/machine_account_secrets.c | 81 |
4 files changed, 137 insertions, 41 deletions
diff --git a/source3/include/proto.h b/source3/include/proto.h index cb6856734e..6ccefe1a39 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -4645,6 +4645,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 b51fd22bfa..624b1465bb 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 4d7bb8d20b..c07259394b 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/machine_account_secrets.c b/source3/passdb/machine_account_secrets.c index 4a1c3faa87..db99d010ec 100644 --- a/source3/passdb/machine_account_secrets.c +++ b/source3/passdb/machine_account_secrets.c @@ -161,6 +161,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 @@ -300,21 +317,42 @@ bool secrets_fetch_trust_account_password(const char *domain, uint8 ret_pwd[16], } /************************************************************************ - 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; } @@ -334,8 +372,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, @@ -345,6 +403,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; @@ -358,6 +420,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. |