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;