r11536: Add a hook for client-principal access control to hdb-ldb, re-using
authorAndrew Bartlett <abartlet@samba.org>
Sun, 6 Nov 2005 14:15:34 +0000 (14:15 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:45:50 +0000 (13:45 -0500)
the code in auth/auth_sam.c for consistancy.  This will also allow us
to have one place for a backend directory hook.

I will use a very similar hook to add the PAC.

Andrew Bartlett

source/heimdal/kdc/kdc_locl.h
source/heimdal/kdc/kerberos5.c
source/heimdal/kdc/misc.c
source/heimdal/lib/hdb/hdb-protos.h
source/heimdal/lib/hdb/hdb.c
source/heimdal/lib/hdb/hdb.h
source/kdc/hdb-ldb.c

index b0501abb8d87cef7e91d94977e681e72c5dd1239..8658d33b68b6faa1cef4a80438b429a993a6bad4 100644 (file)
@@ -71,9 +71,18 @@ krb5_error_code
 _kdc_db_fetch(krb5_context, krb5_kdc_configuration *,
              krb5_principal, enum hdb_ent_type, hdb_entry **);
 
+krb5_error_code
+_kdc_db_fetch_ex(krb5_context context,
+                krb5_kdc_configuration *config,
+                krb5_principal principal, enum hdb_ent_type ent_type, 
+                hdb_entry_ex **h);
+
 void
 _kdc_free_ent(krb5_context context, hdb_entry *);
 
+void
+_kdc_free_ent_ex(krb5_context context, hdb_entry_ex *ent);
+
 void
 loop(krb5_context context, krb5_kdc_configuration *config);
 
index 1c02e66211d112d5f4a5bcc4cea827b3edf86ea1..0df090eef3fe678195f71a15fd1314a91ae21d28 100644 (file)
@@ -767,7 +767,8 @@ _kdc_as_rep(krb5_context context,
     KDC_REQ_BODY *b = &req->req_body;
     AS_REP rep;
     KDCOptions f = b->kdc_options;
-    hdb_entry *client = NULL, *server = NULL;
+    hdb_entry_ex *client = NULL;
+    hdb_entry *server = NULL;
     krb5_enctype cetype, setype;
     EncTicketPart et;
     EncKDCRepPart ek;
@@ -813,7 +814,7 @@ _kdc_as_rep(krb5_context context,
     kdc_log(context, config, 0, "AS-REQ %s from %s for %s", 
            client_name, from, server_name);
 
-    ret = _kdc_db_fetch(context, config, client_princ, HDB_ENT_TYPE_CLIENT, &client);
+    ret = _kdc_db_fetch_ex(context, config, client_princ, HDB_ENT_TYPE_CLIENT, &client);
     if(ret){
        kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name,
                krb5_get_err_text(context, ret));
@@ -830,12 +831,19 @@ _kdc_as_rep(krb5_context context,
     }
 
     ret = _kdc_check_flags(context, config, 
-                          client, client_name,
+                          &client->entry, client_name,
                           server, server_name,
                           TRUE);
     if(ret)
        goto out;
 
+    if (client->check_client_access) {
+       ret = client->check_client_access(context, client, 
+                                         b->addresses);
+       if(ret)
+           goto out;
+    }
+
     memset(&et, 0, sizeof(et));
     memset(&ek, 0, sizeof(ek));
 
@@ -875,7 +883,7 @@ _kdc_as_rep(krb5_context context,
            ret = _kdc_pk_check_client(context,
                                       config,
                                       client_princ, 
-                                      client,
+                                      &client->entry,
                                       pkp,
                                       &client_cert);
            if (ret) {
@@ -924,7 +932,7 @@ _kdc_as_rep(krb5_context context,
                goto out;
            }
            
-           ret = hdb_enctype2key(context, client, enc_data.etype, &pa_key);
+           ret = hdb_enctype2key(context, &client->entry, enc_data.etype, &pa_key);
            if(ret){
                char *estr;
                e_text = "No key matches pa-data";
@@ -974,7 +982,7 @@ _kdc_as_rep(krb5_context context,
                        krb5_get_err_text(context, ret));
                free(str);
 
-               if(hdb_next_enctype2key(context, client
+               if(hdb_next_enctype2key(context, &client->entry
                                        enc_data.etype, &pa_key) == 0)
                    goto try_next_key;
                e_text = "Failed to decrypt PA-DATA";
@@ -1030,7 +1038,7 @@ _kdc_as_rep(krb5_context context,
            goto out;
        }
     }else if (config->require_preauth
-             || client->flags.require_preauth
+             || client->entry.flags.require_preauth
              || server->flags.require_preauth) {
        METHOD_DATA method_data;
        PA_DATA *pa;
@@ -1058,10 +1066,10 @@ _kdc_as_rep(krb5_context context,
 
        /* XXX check ret */
        if (only_older_enctype_p(req))
-           ret = get_pa_etype_info(context, config, &method_data, client
+           ret = get_pa_etype_info(context, config, &method_data, &client->entry
                                    b->etype.val, b->etype.len); 
        /* XXX check ret */
-       ret = get_pa_etype_info2(context, config, &method_data, client
+       ret = get_pa_etype_info2(context, config, &method_data, &client->entry
                                 b->etype.val, b->etype.len);
 
        
@@ -1089,7 +1097,7 @@ _kdc_as_rep(krb5_context context,
     }
     
     ret = find_keys(context, config, 
-                   client, server, &ckey, &cetype, &skey, &setype,
+                   &client->entry, server, &ckey, &cetype, &skey, &setype,
                    b->etype.val, b->etype.len);
     if(ret) {
        kdc_log(context, config, 0, "Server/client has no support for etypes");
@@ -1154,19 +1162,19 @@ _kdc_as_rep(krb5_context context,
     
     rep.pvno = 5;
     rep.msg_type = krb_as_rep;
-    copy_Realm(&client->principal->realm, &rep.crealm);
+    copy_Realm(&client->entry.principal->realm, &rep.crealm);
     if (f.request_anonymous)
        make_anonymous_principalname (&rep.cname);
     else
        _krb5_principal2principalname(&rep.cname, 
-                                     client->principal);
+                                     client->entry.principal);
     rep.ticket.tkt_vno = 5;
     copy_Realm(&server->principal->realm, &rep.ticket.realm);
     _krb5_principal2principalname(&rep.ticket.sname, 
                                  server->principal);
 
     et.flags.initial = 1;
-    if(client->flags.forwardable && server->flags.forwardable)
+    if(client->entry.flags.forwardable && server->flags.forwardable)
        et.flags.forwardable = f.forwardable;
     else if (f.forwardable) {
        ret = KRB5KDC_ERR_POLICY;
@@ -1174,7 +1182,7 @@ _kdc_as_rep(krb5_context context,
                "Ticket may not be forwardable -- %s", client_name);
        goto out;
     }
-    if(client->flags.proxiable && server->flags.proxiable)
+    if(client->entry.flags.proxiable && server->flags.proxiable)
        et.flags.proxiable = f.proxiable;
     else if (f.proxiable) {
        ret = KRB5KDC_ERR_POLICY;
@@ -1182,7 +1190,7 @@ _kdc_as_rep(krb5_context context,
                "Ticket may not be proxiable -- %s", client_name);
        goto out;
     }
-    if(client->flags.postdate && server->flags.postdate)
+    if(client->entry.flags.postdate && server->flags.postdate)
        et.flags.may_postdate = f.allow_postdate;
     else if (f.allow_postdate){
        ret = KRB5KDC_ERR_POLICY;
@@ -1220,8 +1228,8 @@ _kdc_as_rep(krb5_context context,
 
        /* be careful not overflowing */
 
-       if(client->max_life)
-           t = start + min(t - start, *client->max_life);
+       if(client->entry.max_life)
+           t = start + min(t - start, *client->entry.max_life);
        if(server->max_life)
            t = start + min(t - start, *server->max_life);
 #if 0
@@ -1241,8 +1249,8 @@ _kdc_as_rep(krb5_context context,
            t = *b->rtime;
            if(t == 0)
                t = MAX_TIME;
-           if(client->max_renew)
-               t = start + min(t - start, *client->max_renew);
+           if(client->entry.max_renew)
+               t = start + min(t - start, *client->entry.max_renew);
            if(server->max_renew)
                t = start + min(t - start, *server->max_renew);
 #if 0
@@ -1278,16 +1286,16 @@ _kdc_as_rep(krb5_context context,
      */
     ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
     ek.last_req.len = 0;
-    if (client->pw_end
+    if (client->entry.pw_end
        && (config->kdc_warn_pwexpire == 0
-           || kdc_time + config->kdc_warn_pwexpire <= *client->pw_end)) {
+           || kdc_time + config->kdc_warn_pwexpire <= *client->entry.pw_end)) {
        ek.last_req.val[ek.last_req.len].lr_type  = LR_PW_EXPTIME;
-       ek.last_req.val[ek.last_req.len].lr_value = *client->pw_end;
+       ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
        ++ek.last_req.len;
     }
-    if (client->valid_end) {
+    if (client->entry.valid_end) {
        ek.last_req.val[ek.last_req.len].lr_type  = LR_ACCT_EXPTIME;
-       ek.last_req.val[ek.last_req.len].lr_value = *client->valid_end;
+       ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
        ++ek.last_req.len;
     }
     if (ek.last_req.len == 0) {
@@ -1296,15 +1304,15 @@ _kdc_as_rep(krb5_context context,
        ++ek.last_req.len;
     }
     ek.nonce = b->nonce;
-    if (client->valid_end || client->pw_end) {
+    if (client->entry.valid_end || client->entry.pw_end) {
        ALLOC(ek.key_expiration);
-       if (client->valid_end) {
-           if (client->pw_end)
-               *ek.key_expiration = min(*client->valid_end, *client->pw_end);
+       if (client->entry.valid_end) {
+           if (client->entry.pw_end)
+               *ek.key_expiration = min(*client->entry.valid_end, *client->entry.pw_end);
            else
-               *ek.key_expiration = *client->valid_end;
+               *ek.key_expiration = *client->entry.valid_end;
        } else
-           *ek.key_expiration = *client->pw_end;
+           *ek.key_expiration = *client->entry.pw_end;
     } else
        ek.key_expiration = NULL;
     ek.flags = et.flags;
@@ -1352,7 +1360,7 @@ _kdc_as_rep(krb5_context context,
 
     ret = encode_reply(context, config, 
                       &rep, &et, &ek, setype, server->kvno, &skey->key,
-                      client->kvno, reply_key, &e_text, reply);
+                      client->entry.kvno, reply_key, &e_text, reply);
     free_EncTicketPart(&et);
     free_EncKDCRepPart(&ek);
  out:
@@ -1381,7 +1389,7 @@ _kdc_as_rep(krb5_context context,
        krb5_free_principal(context, server_princ);
     free(server_name);
     if(client)
-       _kdc_free_ent(context, client);
+       _kdc_free_ent_ex(context, client);
     if(server)
        _kdc_free_ent(context, server);
     return ret;
index 5a251607b6a9b8064c02c909363e082e77558da7..b14bb50ea530f2c275ccc023f612184c9c7616f8 100644 (file)
@@ -82,3 +82,59 @@ _kdc_free_ent(krb5_context context, hdb_entry *ent)
     free (ent);
 }
 
+krb5_error_code
+_kdc_db_fetch_ex(krb5_context context,
+                krb5_kdc_configuration *config,
+                krb5_principal principal, enum hdb_ent_type ent_type, 
+                hdb_entry_ex **h)
+{
+    hdb_entry_ex *ent;
+    krb5_error_code ret = HDB_ERR_NOENTRY;
+    int i;
+
+    ent = malloc (sizeof (*ent));
+    if (ent == NULL)
+       return ENOMEM;
+    memset(ent, '\0', sizeof(*ent));
+
+    ent->entry.principal = principal;
+
+    for(i = 0; i < config->num_db; i++) {
+       ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
+       if (ret) {
+           kdc_log(context, config, 0, "Failed to open database: %s", 
+                   krb5_get_err_text(context, ret));
+           continue;
+       }
+       if (config->db[i]->hdb_fetch_ex) {
+               ret = config->db[i]->hdb_fetch_ex(context, 
+                                                 config->db[i],
+                                                 HDB_F_DECRYPT, 
+                                                 principal, 
+                                                 ent_type,
+                                                 ent);
+       } else {
+               ret = config->db[i]->hdb_fetch(context, 
+                                              config->db[i],
+                                              HDB_F_DECRYPT, 
+                                              principal, 
+                                              ent_type,
+                                              &ent->entry);
+       }
+       config->db[i]->hdb_close(context, config->db[i]);
+       if(ret == 0) {
+           *h = ent;
+           return 0;
+       }
+    }
+    free(ent);
+    return ret;
+}
+
+void
+_kdc_free_ent_ex(krb5_context context, hdb_entry_ex *ent)
+{
+    hdb_free_entry_ex (context, ent);
+    free (ent);
+}
+
index 799f013eba0775997ce20bd1f56c99998fef2389..7557b46bff6bb1280cafca35a857d3c375c14dfc 100644 (file)
@@ -119,6 +119,9 @@ hdb_free_entry (
        krb5_context /*context*/,
        hdb_entry */*ent*/);
 
+void
+hdb_free_entry_ex(krb5_context context, hdb_entry_ex *ent);
+
 void
 hdb_free_key (Key */*key*/);
 
index c66579fab0f0dde8ce3586471fe575dbb695a97f..e8161afbc1d99c3e37aae76336769b850061da4c 100644 (file)
@@ -144,6 +144,16 @@ hdb_free_entry(krb5_context context, hdb_entry *ent)
     free_hdb_entry(ent);
 }
 
+void
+hdb_free_entry_ex(krb5_context context, hdb_entry_ex *ent)
+{
+    if (ent->free_private) {
+           ent->free_private(context, ent);
+    }
+
+    free_hdb_entry(&ent->entry);
+}
+
 krb5_error_code
 hdb_foreach(krb5_context context,
            HDB *db,
index fe86f0ae722fa38484a105b0efaec52365e1200a..41cc03cf3679ed1703cf9bb4663beb99b3dbb7ff 100644 (file)
@@ -54,6 +54,23 @@ enum hdb_ent_type{ HDB_ENT_TYPE_CLIENT, HDB_ENT_TYPE_SERVER, HDB_ENT_TYPE_ANY };
 
 typedef struct hdb_master_key_data *hdb_master_key;
 
+typedef struct hdb_entry_ex {
+       struct hdb_entry entry;
+       void *private;
+
+       krb5_error_code (*free_private)(krb5_context, struct hdb_entry_ex *);
+       krb5_error_code (*check_client_access)(krb5_context, struct hdb_entry_ex *, HostAddresses *);
+       krb5_error_code (*authz_data_as_req)(krb5_context, struct hdb_entry_ex *, 
+                                            AuthorizationData *in, 
+                                            EncryptionKey *tgtkey,
+                                            AuthorizationData *out);
+       krb5_error_code (*authz_data_tgs_req)(krb5_context, struct hdb_entry_ex *, 
+                                             AuthorizationData *in, 
+                                             EncryptionKey *tgtkey,
+                                             EncryptionKey *servicekey,
+                                             AuthorizationData *out);
+} hdb_entry_ex;
+
 typedef struct HDB{
     void *hdb_db;
     void *hdb_dbc;
@@ -66,6 +83,8 @@ typedef struct HDB{
     krb5_error_code (*hdb_close)(krb5_context, struct HDB*);
     krb5_error_code (*hdb_fetch)(krb5_context,struct HDB*,unsigned hdb_flags, krb5_const_principal principal,
                                 enum hdb_ent_type ent_type, hdb_entry*);
+    krb5_error_code (*hdb_fetch_ex)(krb5_context,struct HDB*,unsigned hdb_flags, krb5_const_principal principal,
+                                enum hdb_ent_type ent_type, hdb_entry_ex*);
     krb5_error_code (*hdb_store)(krb5_context,struct HDB*,unsigned,hdb_entry*);
     krb5_error_code (*hdb_remove)(krb5_context, struct HDB*, hdb_entry*);
     krb5_error_code (*hdb_firstkey)(krb5_context, struct HDB*, 
@@ -82,7 +101,7 @@ typedef struct HDB{
     krb5_error_code (*hdb_destroy)(krb5_context, struct HDB*);
 }HDB;
 
-#define HDB_INTERFACE_VERSION  2
+#define HDB_INTERFACE_VERSION  3
 
 struct hdb_so_method {
     int version;
index 80e8cdc74e7d53dbba992bcca267385d14709f1a..87e82738983321dd10c0143c07d2f19da0487c44 100644 (file)
@@ -38,6 +38,7 @@
 #include "hdb.h"
 #include "lib/ldb/include/ldb.h"
 #include "system/iconv.h"
+#include "librpc/gen_ndr/netlogon.h"
 
 enum hdb_ldb_ent_type 
 { HDB_LDB_ENT_TYPE_CLIENT, HDB_LDB_ENT_TYPE_SERVER, 
@@ -72,6 +73,12 @@ static const char *realm_ref_attrs[] = {
        NULL
 };
 
+struct hdb_ldb_private {
+       struct ldb_context *samdb;
+       struct ldb_message **msg;
+       struct ldb_message **realm_ref_msg;
+};
+
 static KerberosTime ldb_msg_find_krb5time_ldap_time(struct ldb_message *msg, const char *attr, KerberosTime default_val)
 {
     const char *tmp;
@@ -611,10 +618,48 @@ static krb5_error_code LDB_rename(krb5_context context, HDB *db, const char *new
        return HDB_ERR_DB_INUSE;
 }
 
-static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
-                                krb5_const_principal principal,
-                                enum hdb_ent_type ent_type,
-                                hdb_entry *entry)
+static krb5_error_code hdb_ldb_free_private(krb5_context context, hdb_entry_ex *entry_ex)
+{
+       talloc_free(entry_ex->private);
+       return 0;
+}
+
+static krb5_error_code hdb_ldb_check_client_access(krb5_context context, hdb_entry_ex *entry_ex, 
+                                                  HostAddresses *addresses)
+{
+       krb5_error_code ret;
+       NTSTATUS nt_status;
+       TALLOC_CTX *tmp_ctx = talloc_new(entry_ex->private);
+       struct hdb_ldb_private *private = entry_ex->private;
+       char *name, *workstation = NULL;
+       if (!tmp_ctx) {
+               return ENOMEM;
+       }
+       
+       ret = krb5_unparse_name(context, entry_ex->entry.principal, &name);
+       if (ret != 0) {
+               talloc_free(tmp_ctx);
+       }
+       nt_status = authsam_account_ok(tmp_ctx, 
+                                      private->samdb, 
+                                      MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT,
+                                      private->msg,
+                                      private->realm_ref_msg,
+                                      workstation,
+                                      name);
+       free(name);
+       
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               return KRB5KDC_ERR_POLICY;
+       }
+       return 0;
+}
+
+
+static krb5_error_code LDB_fetch_ex(krb5_context context, HDB *db, unsigned flags,
+                                   krb5_const_principal principal,
+                                   enum hdb_ent_type ent_type,
+                                   hdb_entry_ex *entry_ex)
 {
        struct ldb_message **msg = NULL;
        struct ldb_message **realm_ref_msg = NULL;
@@ -631,10 +676,6 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
                return ENOMEM;
        }
 
-       /* Cludge, cludge cludge.  If the realm part of krbtgt/realm,
-        * is in our db, then direct the caller at our primary
-        * krgtgt */
-
        switch (ent_type) {
        case HDB_ENT_TYPE_CLIENT:
        {
@@ -662,7 +703,23 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
 
                ret = LDB_message2entry(context, db, mem_ctx, 
                                        principal, ldb_ent_type, 
-                                       msg[0], realm_ref_msg[0], entry);
+                                       msg[0], realm_ref_msg[0], &entry_ex->entry);
+
+               if (ret == 0) {
+                       struct hdb_ldb_private *private = talloc(db, struct hdb_ldb_private);
+                       if (!private) {
+                               hdb_free_entry(context, &entry_ex->entry);
+                               ret = ENOMEM;
+                       }
+                       private->msg = talloc_steal(private, msg);
+                       private->realm_ref_msg = talloc_steal(private, realm_ref_msg);
+                       private->samdb = (struct ldb_context *)db->hdb_db;
+                       
+                       entry_ex->private = private;
+                       entry_ex->free_private = hdb_ldb_free_private;
+                       entry_ex->check_client_access = hdb_ldb_check_client_access;
+               }
+
                talloc_free(mem_ctx);
                return ret;
        }
@@ -673,6 +730,10 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
                        if ((LDB_lookup_realm(context, (struct ldb_context *)db->hdb_db,
                                              mem_ctx, principal->name.name_string.val[1], &realm_fixed_msg) == 0)) {
                                /* us */
+                               /* Cludge, cludge cludge.  If the realm part of krbtgt/realm,
+                                * is in our db, then direct the caller at our primary
+                                * krgtgt */
+
                                const char *dnsdomain = ldb_msg_find_string(realm_fixed_msg[0], "dnsRoot", NULL);
                                char *realm_fixed = strupper_talloc(mem_ctx, dnsdomain);
                                if (!realm_fixed) {
@@ -741,7 +802,7 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
 
                        ret = LDB_message2entry(context, db, mem_ctx, 
                                                principal, ldb_ent_type, 
-                                               msg[0], realm_ref_msg[0], entry);
+                                               msg[0], realm_ref_msg[0], &entry_ex->entry);
                        talloc_free(mem_ctx);
                        return ret;
                        
@@ -784,7 +845,7 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
        } else {
                ret = LDB_message2entry(context, db, mem_ctx, 
                                        principal, ldb_ent_type, 
-                                       msg[0], realm_ref_msg[0], entry);
+                                       msg[0], realm_ref_msg[0], &entry_ex->entry);
                if (ret != 0) {
                        krb5_warnx(context, "LDB_fetch: message2entry failed\n");       
                }
@@ -794,6 +855,26 @@ static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
        return ret;
 }
 
+static krb5_error_code LDB_fetch(krb5_context context, HDB *db, unsigned flags,
+                                krb5_const_principal principal,
+                                enum hdb_ent_type ent_type,
+                                hdb_entry *entry)
+{
+       struct hdb_entry_ex entry_ex;
+       krb5_error_code ret;
+
+       memset(&entry_ex, '\0', sizeof(entry_ex));
+       ret = LDB_fetch_ex(context, db, flags, principal, ent_type, &entry_ex);
+       
+       if (ret == 0) {
+               if (entry_ex.free_private) {
+                       entry_ex.free_private(context, &entry_ex);
+               }
+               *entry = entry_ex.entry;
+       }
+       return ret;
+}
+
 static krb5_error_code LDB_store(krb5_context context, HDB *db, unsigned flags, hdb_entry *entry)
 {
        return HDB_ERR_DB_INUSE;
@@ -967,6 +1048,7 @@ krb5_error_code hdb_ldb_create(TALLOC_CTX *mem_ctx,
        (*db)->hdb_open = LDB_open;
        (*db)->hdb_close = LDB_close;
        (*db)->hdb_fetch = LDB_fetch;
+       (*db)->hdb_fetch_ex = LDB_fetch_ex;
        (*db)->hdb_store = LDB_store;
        (*db)->hdb_remove = LDB_remove;
        (*db)->hdb_firstkey = LDB_firstkey;