From c9609cdb37d8dfb0c07de69f7bc0acf023996036 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Fri, 11 Nov 2011 02:06:48 -0600 Subject: [PATCH] Initial patch for dealing with AD x-realm key rollover AD issues x-realm TGTs with kvno 0. On key x-realm trust key change we need to be able to try current and previous keys for trust, else we will have some failures. --- kdc/digest-service.c | 2 +- kdc/digest.c | 4 +- kdc/fast.c | 4 +- kdc/kerberos5.c | 9 ++-- kdc/krb5tgs.c | 91 +++++++++++++++++++++++++------------- kdc/misc.c | 6 +-- lib/hdb/hdb.c | 34 +++++++++++--- lib/hdb/libhdb-exports.def | 1 + lib/hdb/version-script.map | 1 + 9 files changed, 105 insertions(+), 47 deletions(-) diff --git a/kdc/digest-service.c b/kdc/digest-service.c index 575850c29..011586650 100644 --- a/kdc/digest-service.c +++ b/kdc/digest-service.c @@ -118,7 +118,7 @@ ntlm_service(void *ctx, const heim_idata *req, if (ret) goto failed; - ret = hdb_enctype2key(context, &user->entry, + ret = hdb_enctype2key(context, &user->entry, NULL, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_message(context, ret, "NTLM missing arcfour key"); diff --git a/kdc/digest.c b/kdc/digest.c index 5f0d27441..5c90e9e2a 100644 --- a/kdc/digest.c +++ b/kdc/digest.c @@ -883,7 +883,7 @@ _kdc_do_digest(krb5_context context, goto failed; } - ret = hdb_enctype2key(context, &user->entry, + ret = hdb_enctype2key(context, &user->entry, NULL, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_message(context, ret, @@ -1209,7 +1209,7 @@ _kdc_do_digest(krb5_context context, goto out; } - ret = hdb_enctype2key(context, &user->entry, + ret = hdb_enctype2key(context, &user->entry, NULL, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_message(context, ret, "NTLM missing arcfour key"); diff --git a/kdc/fast.c b/kdc/fast.c index 38e96a0fc..1a806d8f4 100644 --- a/kdc/fast.c +++ b/kdc/fast.c @@ -61,7 +61,7 @@ get_fastuser_crypto(kdc_request_t r, krb5_enctype enctype, krb5_crypto *crypto) ret = _kdc_get_preferred_key(r->context, r->config, fast_user, "fast-cookie", &enctype, &cookie_key); else - ret = hdb_enctype2key(r->context, &fast_user->entry, + ret = hdb_enctype2key(r->context, &fast_user->entry, NULL, enctype, &cookie_key); if (ret) goto out; @@ -438,7 +438,7 @@ _kdc_fast_unwrap_request(kdc_request_t r) goto out; } - ret = hdb_enctype2key(r->context, &armor_user->entry, + ret = hdb_enctype2key(r->context, &armor_user->entry, NULL, ap_req.ticket.enc_part.etype, &armor_key); if (ret) { diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index 9b4c660e9..65af92162 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -173,7 +173,7 @@ _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key, if (clientbest == (krb5_enctype)ETYPE_NULL) clientbest = p[i]; /* check target princ support */ - ret = hdb_enctype2key(context, &princ->entry, p[i], &key); + ret = hdb_enctype2key(context, &princ->entry, NULL, p[i], &key); if (ret) continue; if (is_preauth && !is_default_salt_p(&def_salt, key)) @@ -206,7 +206,8 @@ _kdc_find_etype(krb5_context context, krb5_boolean use_strongest_session_key, !_kdc_is_weak_exception(princ->entry.principal, etypes[i])) continue; - while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) { + while (hdb_next_enctype2key(context, &princ->entry, NULL, + etypes[i], &key) == 0) { if (key->key.keyvalue.length == 0) { ret = KRB5KDC_ERR_NULL_KEY; continue; @@ -552,7 +553,7 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa) goto out; } - ret = hdb_enctype2key(r->context, &r->client->entry, + ret = hdb_enctype2key(r->context, &r->client->entry, NULL, enc_data.etype, &pa_key); if(ret){ char *estr; @@ -608,7 +609,7 @@ pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa) krb5_free_error_message(r->context, msg); free(str); - if(hdb_next_enctype2key(r->context, &r->client->entry, + if(hdb_next_enctype2key(r->context, &r->client->entry, NULL, enc_data.etype, &pa_key) == 0) goto try_next_key; diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 4102af68a..06bd80783 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -139,7 +139,7 @@ _kdc_add_KRB5SignedPath(krb5_context context, { Key *key; - ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key); + ret = hdb_enctype2key(context, &krbtgt->entry, NULL, enctype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { @@ -226,7 +226,8 @@ check_KRB5SignedPath(krb5_context context, { Key *key; - ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key); + ret = hdb_enctype2key(context, &krbtgt->entry, NULL, /* XXX use correct kvno! */ + sp.etype, &key); if (ret == 0) ret = krb5_crypto_init(context, &key->key, 0, &crypto); if (ret) { @@ -1156,6 +1157,10 @@ tgs_parse_request(krb5_context context, krb5_flags ap_req_options; krb5_flags verify_ap_req_flags; krb5_crypto crypto; + krb5uint32 krbtgt_kvno; /* kvno used for the PA-TGS-REQ AP-REQ Ticket */ + krb5uint32 krbtgt_kvno_try; + int kvno_search_tries = 4; /* number of kvnos to try when tkt_vno == 0 */ + const Keys *krbtgt_keys;/* keyset for TGT tkt_vno */ Key *tkey; krb5_keyblock *subkey = NULL; unsigned usage; @@ -1186,20 +1191,52 @@ tgs_parse_request(krb5_context context, ap_req.ticket.sname, ap_req.ticket.realm); - ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt); + krbtgt_kvno = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0; + krbtgt_kvno_try = ap_req.ticket.enc_part.kvno ? *ap_req.ticket.enc_part.kvno : 0; + ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, + &krbtgt_kvno, NULL, krbtgt); - if(ret == HDB_ERR_NOT_FOUND_HERE) { + if (ret == HDB_ERR_NOT_FOUND_HERE) { + /* XXX Factor out this unparsing of the same princ all over */ char *p; ret = krb5_unparse_name(context, princ, &p); if (ret != 0) p = failed; krb5_free_principal(context, princ); - kdc_log(context, config, 5, "Ticket-granting ticket account %s does not have secrets at this KDC, need to proxy", p); + kdc_log(context, config, 5, + "Ticket-granting ticket account %s does not have secrets at " + "this KDC, need to proxy", p); if (ret == 0) free(p); ret = HDB_ERR_NOT_FOUND_HERE; goto out; - } else if(ret){ + } else if (ret == HDB_ERR_KVNO_NOT_FOUND) { + char *p; + ret = krb5_unparse_name(context, princ, &p); + if (ret != 0) + p = failed; + krb5_free_principal(context, princ); + kdc_log(context, config, 5, + "Ticket-granting ticket account %s does not have keys for " + "kvno %d at this KDC", p, krbtgt_kvno); + if (ret == 0) + free(p); + ret = HDB_ERR_KVNO_NOT_FOUND; + goto out; + } else if (ret == HDB_ERR_NO_MKEY) { + char *p; + ret = krb5_unparse_name(context, princ, &p); + if (ret != 0) + p = failed; + krb5_free_principal(context, princ); + kdc_log(context, config, 5, + "Missing master key for decrypting keys for ticket-granting " + "ticket account %s with kvno %d at this KDC", p, krbtgt_kvno); + if (ret == 0) + free(p); + ret = HDB_ERR_KVNO_NOT_FOUND; + goto out; + } else if (ret) { const char *msg = krb5_get_error_message(context, ret); char *p; ret = krb5_unparse_name(context, princ, &p); @@ -1215,30 +1252,17 @@ tgs_parse_request(krb5_context context, goto out; } - if(ap_req.ticket.enc_part.kvno && - *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){ - char *p; - - ret = krb5_unparse_name (context, princ, &p); - krb5_free_principal(context, princ); - if (ret != 0) - p = failed; - kdc_log(context, config, 0, - "Ticket kvno = %d, DB kvno = %d (%s)", - *ap_req.ticket.enc_part.kvno, - (*krbtgt)->entry.kvno, - p); - if (ret == 0) - free (p); - ret = KRB5KRB_AP_ERR_BADKEYVER; - goto out; - } - *krbtgt_etype = ap_req.ticket.enc_part.etype; - ret = hdb_enctype2key(context, &(*krbtgt)->entry, +next_kvno: + krbtgt_keys = hdb_kvno2keys(context, &(*krbtgt)->entry, krbtgt_kvno_try); + ret = hdb_enctype2key(context, &(*krbtgt)->entry, krbtgt_keys, ap_req.ticket.enc_part.etype, &tkey); - if(ret){ + if (ret && krbtgt_kvno == 0 && kvno_search_tries > 0) { + kvno_search_tries--; + krbtgt_kvno_try--; + goto next_kvno; + } else if (ret) { char *str = NULL, *p = NULL; krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str); @@ -1267,6 +1291,11 @@ tgs_parse_request(krb5_context context, &ap_req_options, ticket, KRB5_KU_TGS_REQ_AUTH); + if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY && kvno_search_tries > 0) { + kvno_search_tries--; + krbtgt_kvno_try--; + goto next_kvno; + } krb5_free_principal(context, princ); if(ret) { @@ -1554,7 +1583,7 @@ tgs_build_reply(krb5_context context, ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } - ret = hdb_enctype2key(context, &uu->entry, + ret = hdb_enctype2key(context, &uu->entry, NULL, t->enc_part.etype, &uukey); if(ret){ _kdc_free_ent(context, uu); @@ -1734,7 +1763,7 @@ server_lookup: * Validate authoriation data */ - ret = hdb_enctype2key(context, &krbtgt->entry, + ret = hdb_enctype2key(context, &krbtgt->entry, NULL, /* XXX use the right kvno! */ krbtgt_etype, &tkey_check); if(ret) { kdc_log(context, config, 0, @@ -1814,7 +1843,7 @@ server_lookup: "Failed to find key for krbtgt PAC signature"); goto out; } - ret = hdb_enctype2key(context, &krbtgt_out->entry, + ret = hdb_enctype2key(context, &krbtgt_out->entry, NULL, tkey_sign->key.keytype, &tkey_sign); if(ret) { kdc_log(context, config, 0, @@ -2065,6 +2094,8 @@ server_lookup: t = &b->additional_tickets->val[0]; ret = hdb_enctype2key(context, &client->entry, + hdb_kvno2keys(context, &client->entry, + t->enc_part.kvno ? * t->enc_part.kvno : 0), t->enc_part.etype, &clientkey); if(ret){ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ diff --git a/kdc/misc.c b/kdc/misc.c index f048bdbe1..2a4fb5414 100644 --- a/kdc/misc.c +++ b/kdc/misc.c @@ -143,7 +143,7 @@ _kdc_get_preferred_key(krb5_context context, for (i = 0; p[i] != (krb5_enctype)ETYPE_NULL; i++) { if (krb5_enctype_valid(context, p[i]) != 0) continue; - ret = hdb_enctype2key(context, &h->entry, p[i], key); + ret = hdb_enctype2key(context, &h->entry, NULL, p[i], key); if (ret != 0) continue; if (enctype != NULL) @@ -157,8 +157,8 @@ _kdc_get_preferred_key(krb5_context context, if (krb5_enctype_valid(context, h->entry.keys.val[i].key.keytype) != 0) continue; - ret = hdb_enctype2key(context, &h->entry, - h->entry.keys.val[i].key.keytype, key); + ret = hdb_enctype2key(context, &h->entry, NULL, + h->entry.keys.val[i].key.keytype, key); if (ret != 0) continue; if (enctype != NULL) diff --git a/lib/hdb/hdb.c b/lib/hdb/hdb.c index 5dc5a0957..6486840e0 100644 --- a/lib/hdb/hdb.c +++ b/lib/hdb/hdb.c @@ -92,19 +92,42 @@ static struct hdb_method dbmetod = { HDB_INTERFACE_VERSION, "", hdb_ndbm_create }; #endif +const Keys * +hdb_kvno2keys(krb5_context context, + const hdb_entry *e, + krb5_kvno kvno) +{ + HDB_Ext_KeySet *hist_keys; + HDB_extension *extp; + size_t i; + + if (kvno == 0) + return &e->keys; + + extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys); + if (extp == NULL) + return 0; + + hist_keys = &extp->data.u.hist_keys; + for (i = 0; i < hist_keys->len; i++) { + if (hist_keys->val[i].kvno == kvno) + return &hist_keys->val[i].keys; + } + + return NULL; +} krb5_error_code hdb_next_enctype2key(krb5_context context, const hdb_entry *e, + const Keys *keyset, krb5_enctype enctype, Key **key) { + const Keys *keys = keyset ? keyset : &e->keys; Key *k; - for (k = *key ? (*key) + 1 : e->keys.val; - k < e->keys.val + e->keys.len; - k++) - { + for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) { if(k->key.keytype == enctype){ *key = k; return 0; @@ -119,11 +142,12 @@ hdb_next_enctype2key(krb5_context context, krb5_error_code hdb_enctype2key(krb5_context context, hdb_entry *e, + const Keys *keyset, krb5_enctype enctype, Key **key) { *key = NULL; - return hdb_next_enctype2key(context, e, enctype, key); + return hdb_next_enctype2key(context, e, keyset, enctype, key); } void diff --git a/lib/hdb/libhdb-exports.def b/lib/hdb/libhdb-exports.def index 19a3e130a..0b96668b0 100644 --- a/lib/hdb/libhdb-exports.def +++ b/lib/hdb/libhdb-exports.def @@ -45,6 +45,7 @@ EXPORTS hdb_init_db hdb_interface_version DATA hdb_key2principal + hdb_kvno2keys hdb_list_builtin hdb_lock hdb_next_enctype2key diff --git a/lib/hdb/version-script.map b/lib/hdb/version-script.map index 3b68296f5..4b7144bbd 100644 --- a/lib/hdb/version-script.map +++ b/lib/hdb/version-script.map @@ -47,6 +47,7 @@ HEIMDAL_HDB_1.0 { hdb_get_dbinfo; hdb_init_db; hdb_key2principal; + hdb_kvno2keys; hdb_list_builtin; hdb_lock; hdb_next_enctype2key;