heimdal Add support for extracting a particular KVNO from the database
authorAndrew Bartlett <abartlet@samba.org>
Sat, 2 Oct 2010 00:29:24 +0000 (10:29 +1000)
committerAndrew Bartlett <abartlet@samba.org>
Sat, 2 Oct 2010 02:15:47 +0000 (12:15 +1000)
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)

12 files changed:
kdc/524.c
kdc/digest-service.c
kdc/digest.c
kdc/kerberos4.c
kdc/kerberos5.c
kdc/krb5tgs.c
kdc/misc.c
kdc/windc.c
kdc/windc_plugin.h
lib/hdb/hdb-keytab.c
lib/hdb/hdb.h
lib/hdb/keytab.c

index 2160ce49ece38dff39c21533de0ec48615aa343f..401311906180f4be1fe7bbc95d01b63fbf493bf1 100644 (file)
--- a/kdc/524.c
+++ b/kdc/524.c
@@ -68,7 +68,7 @@ fetch_server (krb5_context context,
        return ret;
     }
     ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER,
-                       NULL, server);
+                       NULL, NULL, server);
     krb5_free_principal(context, sprinc);
     if (ret) {
        kdc_log(context, config, 0,
index 5877a4b2ec20962f2245498b73ef548094524c08..3eaab27b6f0884309de3ce82d683da24e908468c 100644 (file)
@@ -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;
index 1a383fa205c8068ddad2bab0ae9420693c57c99d..70b45c2af6fb7b7b1db4e0de075e4c055aa72454 100644 (file)
@@ -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",
index b28e50b5f5d635de06f9ec27ee1ba387a72e80ab..89f04ed16f1a364b40577f81daa58f9bc48df893 100644 (file)
@@ -82,7 +82,7 @@ valid_princ(krb5_context context,
     ret = krb5_unparse_name(context, princ, &s);
     if (ret)
        return FALSE;
-    ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, &ent);
+    ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, NULL, &ent);
     if (ret) {
        kdc_log(context, ctx->config, 7, "Lookup %s failed: %s", s,
                krb5_get_err_text (context, ret));
@@ -113,7 +113,7 @@ _kdc_db_fetch4(krb5_context context,
                                       valid_princ, &ctx, 0, &p);
     if(ret)
        return ret;
-    ret = _kdc_db_fetch(context, config, p, flags, NULL, ent);
+    ret = _kdc_db_fetch(context, config, p, flags, NULL, NULL, ent);
     krb5_free_principal(context, p);
     return ret;
 }
@@ -372,7 +372,7 @@ _kdc_do_version4(krb5_context context,
        }
 
        ret = _kdc_db_fetch(context, config, tgt_princ,
-                           HDB_F_GET_KRBTGT, NULL, &tgt);
+                           HDB_F_GET_KRBTGT, NULL, NULL, &tgt);
        if(ret){
            char *s;
            s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not "
index c7803ee0eecf6b20129b17190b875cdaf085bcde..40e597befb51d910ee028058ec677e28451c70be 100644 (file)
@@ -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);
index 5958e456b5e26ca59d01d3f035ed4a7c52aaf3b1..71d99e2bee0dd0afdcfaa9d74bdf46b5ea2fe7d9 100644 (file)
@@ -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)
index 39f91dcf10fe0cadbe6a1a123e22bfdd8a8025a2..9feb99cdbc0967818edecace6ff2e182f82ad8cc 100644 (file)
@@ -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]);
index 6b3ba07bf981e67f459ee9a962989e2a6fcb816a..6efbeee9dd05daf0f650b70c3cbfb9eaaf7c1870 100644 (file)
@@ -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;
index 0ec8e066c7395292d7ed7cbf25a8cdbcd03bba2a..037fc8cbdabd7edefd8f7e5688b12318e27d628a 100644 (file)
@@ -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
index 1b74eab252947af3baf60edcabc012ce5586a5d8..393981e47d845d079b74123490171f14f4573d62 100644 (file)
@@ -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;
index 469a330812785b076eef79bd6440b9eb44200ea5..bcd190caa3162e13aff23b511347e8585eb66405 100644 (file)
@@ -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
@@ -122,8 +123,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
      */
index 9e0d8ded10235bf658f4f62addfb73e84e4a55a5..b8cc0d47ee85ba7536688c315fddc3b336cb6b3c 100644 (file)
@@ -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;