From f469fc6d4922d796f5c61bf43e3efc018e37b680 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sat, 2 Oct 2010 10:29:24 +1000 Subject: [PATCH] heimdal Add support for extracting a particular KVNO from the database This should allow master key rollover. (but the real reason is to allow multiple krbtgt accounts, as used by Active Directory to implement RODC support) Signed-off-by: Love Hornquist Astrand --- kdc/digest-service.c | 2 +- kdc/digest.c | 10 ++++----- kdc/kerberos5.c | 5 +++-- kdc/krb5tgs.c | 51 ++++++++++++++++++++++++++++++++------------ kdc/misc.c | 28 +++++++++++++++++++----- kdc/windc.c | 3 ++- kdc/windc_plugin.h | 1 + lib/hdb/hdb-keytab.c | 19 ++++++++++++++--- lib/hdb/hdb.h | 13 ++++++++++- lib/hdb/keytab.c | 16 ++++++++++---- 10 files changed, 112 insertions(+), 36 deletions(-) diff --git a/kdc/digest-service.c b/kdc/digest-service.c index 5877a4b2e..3eaab27b6 100644 --- a/kdc/digest-service.c +++ b/kdc/digest-service.c @@ -111,7 +111,7 @@ ntlm_service(void *ctx, const heim_idata *req, krb5_principal_set_type(context, client, KRB5_NT_NTLM); ret = _kdc_db_fetch(context, config, client, - HDB_F_GET_CLIENT, NULL, &user); + HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, client); if (ret) goto failed; diff --git a/kdc/digest.c b/kdc/digest.c index 1a383fa20..70b45c2af 100644 --- a/kdc/digest.c +++ b/kdc/digest.c @@ -177,7 +177,7 @@ get_password_entry(krb5_context context, return ret; ret = _kdc_db_fetch(context, config, clientprincipal, - HDB_F_GET_CLIENT, &db, &user); + HDB_F_GET_CLIENT, NULL, &db, &user); krb5_free_principal(context, clientprincipal); if (ret) return ret; @@ -292,7 +292,7 @@ _kdc_do_digest(krb5_context context, krb5_clear_error_message(context); ret = _kdc_db_fetch(context, config, principal, - HDB_F_GET_SERVER, NULL, &server); + HDB_F_GET_SERVER, NULL, NULL, &server); if (ret) goto out; @@ -314,7 +314,7 @@ _kdc_do_digest(krb5_context context, } ret = _kdc_db_fetch(context, config, principal, - HDB_F_GET_CLIENT, NULL, &client); + HDB_F_GET_CLIENT, NULL, NULL, &client); krb5_free_principal(context, principal); if (ret) goto out; @@ -874,7 +874,7 @@ _kdc_do_digest(krb5_context context, goto failed; ret = _kdc_db_fetch(context, config, clientprincipal, - HDB_F_GET_CLIENT, NULL, &user); + HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, clientprincipal); if (ret) { krb5_set_error_message(context, ret, @@ -1158,7 +1158,7 @@ _kdc_do_digest(krb5_context context, goto failed; ret = _kdc_db_fetch(context, config, clientprincipal, - HDB_F_GET_CLIENT, NULL, &user); + HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, clientprincipal); if (ret) { krb5_set_error_message(context, ret, "NTLM user %s not in database", diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index c7803ee0e..40e597bef 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -988,7 +988,8 @@ _kdc_as_rep(krb5_context context, */ ret = _kdc_db_fetch(context, config, client_princ, - HDB_F_GET_CLIENT | flags, &clientdb, &client); + HDB_F_GET_CLIENT | flags, NULL, + &clientdb, &client); if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg); @@ -999,7 +1000,7 @@ _kdc_as_rep(krb5_context context, ret = _kdc_db_fetch(context, config, server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, - NULL, &server); + NULL, NULL, &server); if(ret){ const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg); diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 5958e456b..71d99e2be 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -281,8 +281,10 @@ check_PAC(krb5_context context, const krb5_principal client_principal, hdb_entry_ex *client, hdb_entry_ex *server, + hdb_entry_ex *krbtgt, const EncryptionKey *server_key, - const EncryptionKey *krbtgt_key, + const EncryptionKey *krbtgt_check_key, + const EncryptionKey *krbtgt_sign_key, EncTicketPart *tkt, krb5_data *rspac, int *signedpath) @@ -326,14 +328,14 @@ check_PAC(krb5_context context, ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal, - krbtgt_key, NULL); + krbtgt_check_key, NULL); if (ret) { krb5_pac_free(context, pac); return ret; } ret = _kdc_pac_verify(context, client_principal, - client, server, &pac, &signed_pac); + client, server, krbtgt, &pac, &signed_pac); if (ret) { krb5_pac_free(context, pac); return ret; @@ -349,7 +351,7 @@ check_PAC(krb5_context context, *signedpath = 1; ret = _krb5_pac_sign(context, pac, tkt->authtime, client_principal, - server_key, krbtgt_key, rspac); + server_key, krbtgt_sign_key, rspac); } krb5_pac_free(context, pac); @@ -1166,7 +1168,7 @@ 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, NULL, krbtgt); + ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, ap_req.ticket.enc_part.kvno, NULL, krbtgt); if(ret) { const char *msg = krb5_get_error_message(context, ret); @@ -1464,6 +1466,8 @@ tgs_build_reply(krb5_context context, krb5_kvno kvno; krb5_data rspac; + hdb_entry_ex *krbtgt_out = NULL; + METHOD_DATA enc_pa_data; PrincipalName *s; @@ -1473,7 +1477,8 @@ tgs_build_reply(krb5_context context, char opt_str[128]; int signedpath = 0; - Key *tkey; + Key *tkey_check; + Key *tkey_sign; memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); @@ -1505,7 +1510,7 @@ tgs_build_reply(krb5_context context, } _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm); ret = _kdc_db_fetch(context, config, p, - HDB_F_GET_CLIENT|HDB_F_GET_SERVER, + HDB_F_GET_KRBTGT, t->enc_part.kvno, NULL, &uu); krb5_free_principal(context, p); if(ret){ @@ -1558,7 +1563,7 @@ tgs_build_reply(krb5_context context, server_lookup: ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER | HDB_F_CANON, - NULL, &server); + NULL, NULL, &server); if(ret){ const char *new_rlm, *msg; @@ -1619,7 +1624,7 @@ server_lookup: } ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT | HDB_F_CANON, - &clientdb, &client); + NULL, &clientdb, &client); if(ret) { const char *krbtgt_realm, *msg; @@ -1714,15 +1719,31 @@ server_lookup: */ ret = hdb_enctype2key(context, &krbtgt->entry, - krbtgt_etype, &tkey); + krbtgt_etype, &tkey_check); if(ret) { kdc_log(context, config, 0, "Failed to find key for krbtgt PAC check"); goto out; } + /* Now refetch the krbtgt, but get the current kvno (the sign check may have been on an old kvno) */ + ret = _kdc_db_fetch(context, config, krbtgt->entry.principal, HDB_F_GET_KRBTGT, NULL, NULL, &krbtgt_out); + if (ret) { + kdc_log(context, config, 0, + "Failed to find krbtgt in DB for krbtgt PAC signature"); + goto out; + } + + ret = hdb_enctype2key(context, &krbtgt_out->entry, + krbtgt_etype, &tkey_sign); + if(ret) { + kdc_log(context, config, 0, + "Failed to find key for krbtgt PAC signature"); + goto out; + } + ret = check_PAC(context, config, cp, - client, server, ekey, &tkey->key, + client, server, krbtgt, ekey, &tkey_check->key, &tkey_sign->key, tgt, &rspac, &signedpath); if (ret) { const char *msg = krb5_get_error_message(context, ret); @@ -1824,7 +1845,7 @@ server_lookup: krb5_pac p = NULL; krb5_data_free(&rspac); ret = _kdc_db_fetch(context, config, client_principal, HDB_F_GET_CLIENT | HDB_F_CANON, - &s4u2self_impersonated_clientdb, &s4u2self_impersonated_client); + NULL, &s4u2self_impersonated_clientdb, &s4u2self_impersonated_client); if (ret) { const char *msg; @@ -1850,7 +1871,7 @@ server_lookup: if (p != NULL) { ret = _krb5_pac_sign(context, p, ticket->ticket.authtime, s4u2self_impersonated_client->entry.principal, - ekey, &tkey->key, + ekey, &tkey_sign->key, &rspac); krb5_pac_free(context, p); if (ret) { @@ -2080,7 +2101,7 @@ server_lookup: spn, client, cp, - krbtgt, + krbtgt_out, krbtgt_etype, spp, &rspac, @@ -2094,6 +2115,8 @@ out: krb5_data_free(&rspac); krb5_free_keyblock_contents(context, &sessionkey); + if(krbtgt_out) + _kdc_free_ent(context, krbtgt_out); if(server) _kdc_free_ent(context, server); if(client) diff --git a/kdc/misc.c b/kdc/misc.c index 39f91dcf1..9feb99cdb 100644 --- a/kdc/misc.c +++ b/kdc/misc.c @@ -40,12 +40,19 @@ _kdc_db_fetch(krb5_context context, krb5_kdc_configuration *config, krb5_const_principal principal, unsigned flags, + krb5int32 *kvno_ptr, HDB **db, hdb_entry_ex **h) { hdb_entry_ex *ent; krb5_error_code ret; int i; + unsigned kvno = 0; + + if (kvno_ptr) { + kvno = *kvno_ptr; + flags |= HDB_F_KVNO_SPECIFIED; + } ent = calloc (1, sizeof (*ent)); if (ent == NULL) { @@ -84,11 +91,22 @@ _kdc_db_fetch(krb5_context context, continue; } - ret = config->db[i]->hdb_fetch(context, - config->db[i], - principal, - flags | HDB_F_DECRYPT, - ent); + if (config->db[i]->hdb_fetch_kvno) { + ret = config->db[i]->hdb_fetch_kvno(context, + config->db[i], + principal, + flags | HDB_F_DECRYPT, + kvno, + ent); + } else { + flags &= ~HDB_F_KVNO_SPECIFIED; + ret = config->db[i]->hdb_fetch(context, + config->db[i], + principal, + flags | HDB_F_DECRYPT, + ent); + } + krb5_free_principal(context, enterprise_principal); config->db[i]->hdb_close(context, config->db[i]); diff --git a/kdc/windc.c b/kdc/windc.c index 6b3ba07bf..6efbeee9d 100644 --- a/kdc/windc.c +++ b/kdc/windc.c @@ -86,6 +86,7 @@ _kdc_pac_verify(krb5_context context, const krb5_principal client_principal, hdb_entry_ex *client, hdb_entry_ex *server, + hdb_entry_ex *krbtgt, krb5_pac *pac, int *verified) { @@ -95,7 +96,7 @@ _kdc_pac_verify(krb5_context context, return 0; ret = windcft->pac_verify(windcctx, context, - client_principal, client, server, pac); + client_principal, client, server, krbtgt, pac); if (ret == 0) *verified = 1; return ret; diff --git a/kdc/windc_plugin.h b/kdc/windc_plugin.h index 0ec8e066c..037fc8cbd 100644 --- a/kdc/windc_plugin.h +++ b/kdc/windc_plugin.h @@ -60,6 +60,7 @@ typedef krb5_error_code const krb5_principal, struct hdb_entry_ex *, struct hdb_entry_ex *, + struct hdb_entry_ex *, krb5_pac *); typedef krb5_error_code diff --git a/lib/hdb/hdb-keytab.c b/lib/hdb/hdb-keytab.c index 1b74eab25..393981e47 100644 --- a/lib/hdb/hdb-keytab.c +++ b/lib/hdb/hdb-keytab.c @@ -117,13 +117,18 @@ hkt_open(krb5_context context, HDB * db, int flags, mode_t mode) } static krb5_error_code -hkt_fetch(krb5_context context, HDB * db, krb5_const_principal principal, - unsigned flags, hdb_entry_ex * entry) +hkt_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal, + unsigned flags, unsigned kvno, hdb_entry_ex * entry) { hdb_keytab k = (hdb_keytab)db->hdb_db; krb5_error_code ret; krb5_keytab_entry ktentry; + if (!(flags & HDB_F_KVNO_SPECIFIED)) { + /* Preserve previous behaviour if no kvno specified */ + kvno = 0; + } + memset(&ktentry, 0, sizeof(ktentry)); entry->entry.flags.server = 1; @@ -143,7 +148,7 @@ hkt_fetch(krb5_context context, HDB * db, krb5_const_principal principal, * enctypes should work. */ - ret = krb5_kt_get_entry(context, k->keytab, principal, 0, 0, &ktentry); + ret = krb5_kt_get_entry(context, k->keytab, principal, kvno, 0, &ktentry); if (ret) { ret = HDB_ERR_NOENTRY; goto out; @@ -165,6 +170,13 @@ hkt_fetch(krb5_context context, HDB * db, krb5_const_principal principal, return ret; } +static krb5_error_code +hkt_fetch(krb5_context context, HDB * db, krb5_const_principal principal, + unsigned flags, hdb_entry_ex * entry) +{ + return hkt_fetch_kvno(context, db, principal, flags & ~HDB_F_KVNO_SPECIFIED, 0, entry); +} + static krb5_error_code hkt_store(krb5_context context, HDB * db, unsigned flags, hdb_entry_ex * entry) @@ -210,6 +222,7 @@ hdb_keytab_create(krb5_context context, HDB ** db, const char *arg) (*db)->hdb_open = hkt_open; (*db)->hdb_close = hkt_close; (*db)->hdb_fetch = hkt_fetch; + (*db)->hdb_fetch_kvno = hkt_fetch_kvno; (*db)->hdb_store = hkt_store; (*db)->hdb_remove = NULL; (*db)->hdb_firstkey = hkt_firstkey; diff --git a/lib/hdb/hdb.h b/lib/hdb/hdb.h index 948322bc1..18ed298c3 100644 --- a/lib/hdb/hdb.h +++ b/lib/hdb/hdb.h @@ -54,6 +54,7 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK }; #define HDB_F_GET_ANY 28 /* fetch any of client,server,krbtgt */ #define HDB_F_CANON 32 /* want canonicalition */ #define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */ +#define HDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */ /* hdb_capability_flags */ #define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1 @@ -129,8 +130,18 @@ typedef struct HDB{ * should be fetch: client, server, krbtgt. */ krb5_error_code (*hdb_fetch)(krb5_context, struct HDB*, - krb5_const_principal, unsigned, + krb5_const_principal, unsigned, hdb_entry_ex*); + /** + * Fetch an entry from the backend + * + * Fetch an entry from the backend, flags are what type of entry + * should be fetch: client, server, krbtgt. + * knvo (if specified and flags HDB_F_KVNO_SPECIFIED set) is the kvno to get + */ + krb5_error_code (*hdb_fetch_kvno)(krb5_context, struct HDB*, + krb5_const_principal, unsigned, unsigned, + hdb_entry_ex*); /** * Store an entry to database */ diff --git a/lib/hdb/keytab.c b/lib/hdb/keytab.c index 9e0d8ded1..b8cc0d47e 100644 --- a/lib/hdb/keytab.c +++ b/lib/hdb/keytab.c @@ -210,10 +210,18 @@ hdb_get_entry(krb5_context context, (*db->hdb_destroy)(context, db); goto out2; } - ret = (*db->hdb_fetch)(context, db, principal, - HDB_F_DECRYPT| - HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, - &ent); + + if (*db->hdb_fetch_kvno) { + ret = (*db->hdb_fetch_kvno)(context, db, principal, + HDB_F_DECRYPT|HDB_F_KVNO_SPECIFIED| + HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, + kvno, &ent); + } else { + ret = (*db->hdb_fetch)(context, db, principal, + HDB_F_DECRYPT| + HDB_F_GET_CLIENT|HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, + &ent); + } if(ret == HDB_ERR_NOENTRY) { ret = KRB5_KT_NOTFOUND;