Heimdal DB bridge plugin for KDC back end
authorGreg Hudson <ghudson@mit.edu>
Tue, 27 Oct 2009 14:24:01 +0000 (14:24 +0000)
committerGreg Hudson <ghudson@mit.edu>
Tue, 27 Oct 2009 14:24:01 +0000 (14:24 +0000)
Merge Luke's users/lhoward/heimmig branch to trunk.  Implements a
KDC back-end plugin which interfaces to a Heimdal HDB plugin.

ticket: 6578

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@23073 dc483132-0cff-0310-8789-dd5450dbe970

23 files changed:
src/configure.in
src/include/kdb_ext.h
src/include/krb5/authdata_plugin.h
src/kdc/do_as_req.c
src/kdc/do_tgs_req.c
src/kdc/kdc_authdata.c
src/kdc/kdc_util.c
src/kdc/kdc_util.h
src/kdc/policy.c
src/lib/kadm5/srv/svr_principal.c
src/lib/kdb/kdb5.c
src/lib/kdb/libkdb5.exports
src/plugins/authdata/greet_server/greet_auth.c
src/plugins/kdb/hdb/Makefile.in [new file with mode: 0644]
src/plugins/kdb/hdb/hdb.exports [new file with mode: 0644]
src/plugins/kdb/hdb/hdb.h [new file with mode: 0644]
src/plugins/kdb/hdb/hdb_asn1.h [new file with mode: 0644]
src/plugins/kdb/hdb/hdb_err.h [new file with mode: 0644]
src/plugins/kdb/hdb/kdb_hdb.c [new file with mode: 0644]
src/plugins/kdb/hdb/kdb_hdb.h [new file with mode: 0644]
src/plugins/kdb/hdb/kdb_marshal.c [new file with mode: 0644]
src/plugins/kdb/hdb/kdb_windc.c [new file with mode: 0644]
src/plugins/kdb/hdb/windc_plugin.h [new file with mode: 0644]

index 791215668e55d0945afc1aeea64cffe2f1263765..5dbdf25ba871c69c784ece14ccb5b2d6b4cc314e 100644 (file)
@@ -1096,6 +1096,7 @@ dnl       ccapi ccapi/lib ccapi/lib/unix ccapi/server ccapi/server/unix ccapi/test
        plugins/kdb/db2/libdb2/mpool
        plugins/kdb/db2/libdb2/recno
        plugins/kdb/db2/libdb2/test
+       plugins/kdb/hdb
        plugins/preauth/cksum_body plugins/preauth/encrypted_challenge
        plugins/preauth/wpse
        plugins/authdata/greet
index f51d4650046ee72146107bb10f1dc3fceab2bee0..ce2de9b1b2cfbf07c5fe2891d8c561ce94742a5c 100644 (file)
@@ -98,6 +98,7 @@ typedef struct _kdb_sign_auth_data_req {
     krb5_timestamp authtime;           /* Authtime of TGT */
     krb5_authdata **auth_data;         /* Authorization data from TGT */
     krb5_keyblock *session_key;                /* Reply session key */
+    krb5_keyblock *krbtgt_key;         /* Key used to decrypt TGT, valid for TGS-REQ only */
 } kdb_sign_auth_data_req;
 
 typedef struct _kdb_sign_auth_data_rep {
@@ -123,6 +124,7 @@ typedef struct _kdb_check_policy_as_req {
 typedef struct _kdb_check_policy_as_rep {
     krb5_magic magic;
     const char *status;
+    krb5_data e_data;
 } kdb_check_policy_as_rep;
 
 typedef struct _kdb_check_policy_tgs_req {
@@ -135,6 +137,7 @@ typedef struct _kdb_check_policy_tgs_req {
 typedef struct _kdb_check_policy_tgs_rep {
     krb5_magic magic;
     const char *status;
+    krb5_data e_data;
 } kdb_check_policy_tgs_rep;
 
 typedef struct _kdb_audit_as_req {
index a5c3e535526df899683f86e4f281afbd8900c680..471f01c05b2eba32f4b4a25476d7c9d42a361323 100644 (file)
@@ -152,6 +152,7 @@ typedef struct krb5plugin_authdata_server_ftable_v2 {
                                     struct _krb5_db_entry_new *tgs,
                                     krb5_keyblock *client_key,
                                     krb5_keyblock *server_key,
+                                    krb5_keyblock *tgs_key,
                                     krb5_data *req_pkt,
                                     krb5_kdc_req *request,
                                     krb5_const_principal for_user_princ,
index 737def8d2a03ed76ebc7c30fb37dbb171b737782..1feb468be91f8279a8b86987bbc43caca7f0d576 100644 (file)
@@ -108,7 +108,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     krb5_enctype useenctype;
     krb5_data e_data;
     register int i;
-    krb5_timestamp until, rtime;
+    krb5_timestamp rtime;
     char *cname = 0, *sname = 0;
     unsigned int c_flags = 0, s_flags = 0;
     krb5_principal_data client_princ;
@@ -265,7 +265,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
     authtime = kdc_time; /* for audit_as_request() */
 
     if ((errcode = validate_as_request(request, client, server,
-                                      kdc_time, &status))) {
+                                      kdc_time, &status, &e_data))) {
        if (!status) 
            status = "UNKNOWN_REASON";
        errcode += ERROR_TABLE_BASE_krb5;
@@ -339,14 +339,14 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
        enc_tkt_reply.times.starttime = request->from;
     } else
        enc_tkt_reply.times.starttime = kdc_time;
-    
-    until = (request->till == 0) ? kdc_infinity : request->till;
 
-    enc_tkt_reply.times.endtime =
-       min(until,
-           min(enc_tkt_reply.times.starttime + client.max_life,
-               min(enc_tkt_reply.times.starttime + server.max_life,
-                   enc_tkt_reply.times.starttime + max_life_for_realm)));
+    kdc_get_ticket_endtime(kdc_context,
+                          enc_tkt_reply.times.starttime,
+                          kdc_infinity,
+                          request->till,
+                          &client,
+                          &server,
+                          &enc_tkt_reply.times.endtime);
 
     if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
        !isflagset(client.attributes, KRB5_KDB_DISALLOW_RENEWABLE) &&
@@ -559,6 +559,7 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
                              &server,
                              &client_keyblock,
                              &server_keyblock,
+                             &server_keyblock,
                              req_pkt,
                              request,
                              NULL, /* for_user_princ */
index f05d25a92ae1c15e9a7bc4d124a24a915799a693..71013b584859c6a2d468124f6ab73e127ee30da0 100644 (file)
@@ -77,7 +77,7 @@ find_alternate_tgs(krb5_kdc_req *,krb5_db_entry *,
 
 static krb5_error_code 
 prepare_error_tgs(struct kdc_request_state *, krb5_kdc_req *,krb5_ticket *,int,
-                  krb5_principal,krb5_data **,const char *);
+                  krb5_principal,krb5_data **,const char *, krb5_data *);
 
 static krb5_int32
 prep_reprocess_req(krb5_kdc_req *,krb5_principal *);
@@ -88,6 +88,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
                 krb5_data **response)
 {
     krb5_keyblock * subkey = 0;
+    krb5_keyblock * tgskey = 0;
     krb5_kdc_req *request = 0;
     krb5_db_entry server;
     krb5_kdc_rep reply;
@@ -103,7 +104,7 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
     krb5_boolean more;
     krb5_timestamp kdc_time, authtime=0;
     krb5_keyblock session_key;
-    krb5_timestamp until, rtime;
+    krb5_timestamp rtime;
     krb5_keyblock *reply_key = NULL;
     krb5_keyblock *mkey_ptr;
     krb5_key_data  *server_key;
@@ -129,9 +130,11 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
     struct kdc_request_state *state = NULL;
     krb5_pa_data *pa_tgs_req; /*points into request*/
     krb5_data scratch;
+    krb5_data e_data; /* backend-provided error data */
 
     reply.padata = 0; /* For cleanup handler */
     reply_encpart.enc_padata = 0;
+    e_data.data = NULL;
 
     session_key.contents = NULL;
 
@@ -147,7 +150,8 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
         return retval;
     }
     errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket,
-                                  &krbtgt, &k_nprincs, &subkey, &pa_tgs_req);
+                                  &krbtgt, &k_nprincs, &tgskey,
+                                  &subkey, &pa_tgs_req);
     if (header_ticket && header_ticket->enc_part2 &&
         (errcode2 = krb5_unparse_name(kdc_context, 
                                       header_ticket->enc_part2->client,
@@ -281,7 +285,7 @@ tgt_again:
     }
     
     if ((retval = validate_tgs_request(request, server, header_ticket,
-                                       kdc_time, &status))) {
+                                       kdc_time, &status, &e_data))) {
     if (!status)
         status = "UNKNOWN_REASON";
         errcode = retval + ERROR_TABLE_BASE_krb5;
@@ -540,18 +544,22 @@ tgt_again:
     } else {
         /* not a renew request */
         enc_tkt_reply.times.starttime = kdc_time;
-        until = (request->till == 0) ? kdc_infinity : request->till;
-        enc_tkt_reply.times.endtime =
-            min(until, min(enc_tkt_reply.times.starttime + server.max_life,
-               min(enc_tkt_reply.times.starttime + max_life_for_realm,
-                   header_enc_tkt->times.endtime)));
+
+       kdc_get_ticket_endtime(kdc_context,
+                              enc_tkt_reply.times.starttime,
+                              header_enc_tkt->times.endtime,
+                              request->till,
+                              &client,
+                              &server,
+                              &enc_tkt_reply.times.endtime);
+
         if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
             (enc_tkt_reply.times.endtime < request->till) &&
             isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) {
             setflag(request->kdc_options, KDC_OPT_RENEWABLE);
             request->rtime =
                 min(request->till, header_enc_tkt->times.renew_till);
-        }
+       }
     }
     rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
 
@@ -716,6 +724,7 @@ tgt_again:
                               subkey != NULL ? subkey :
                               header_ticket->enc_part2->session,
                               &encrypting_key, /* U2U or server key */
+                              tgskey,
                               pkt,
                               request,
                               s4u_x509_user ?
@@ -974,7 +983,7 @@ cleanup:
             
         retval = prepare_error_tgs(state, request, header_ticket, errcode,
         nprincs ? server.princ : NULL,
-                   response, status);
+                   response, status, &e_data);
         if (got_err) {
             krb5_free_error_message (kdc_context, status);
             status = 0;
@@ -1009,10 +1018,13 @@ cleanup:
         free(s4u_name);
     if (subkey != NULL)
         krb5_free_keyblock(kdc_context, subkey);
+    if (tgskey != NULL)
+        krb5_free_keyblock(kdc_context, tgskey);
     if (reply.padata)
         krb5_free_pa_data(kdc_context, reply.padata);
     if (reply_encpart.enc_padata)
         krb5_free_pa_data(kdc_context, reply_encpart.enc_padata);
+    krb5_free_data_contents(kdc_context, &e_data);
 
     return retval;
 }
@@ -1021,7 +1033,8 @@ static krb5_error_code
 prepare_error_tgs (struct kdc_request_state *state,
                   krb5_kdc_req *request, krb5_ticket *ticket, int error,
                    krb5_principal canon_server,
-                   krb5_data **response, const char *status)
+                   krb5_data **response, const char *status,
+                  krb5_data *e_data)
 {
     krb5_error errpkt;
     krb5_error_code retval = 0;
@@ -1047,8 +1060,7 @@ prepare_error_tgs (struct kdc_request_state *state,
         free(errpkt.text.data);
         return ENOMEM;
     }
-    errpkt.e_data.length = 0;
-    errpkt.e_data.data = NULL;
+    errpkt.e_data = *e_data;
     if (state)
        retval = kdc_fast_handle_error(kdc_context, state, request, NULL, &errpkt);
     if (retval) {
index d598894d2a264e0c909bf322a0fcdd068658d246..4ccfcb98bf658f1fcc8f16d305ffbc4833a9762b 100644 (file)
@@ -56,6 +56,7 @@ typedef krb5_error_code (*authdata_proc_2)
      krb5_db_entry *krbtgt,
      krb5_keyblock *client_key,
      krb5_keyblock *server_key,
+     krb5_keyblock *krbtgt_key,
      krb5_data *req_pkt,
      krb5_kdc_req *request,
      krb5_const_principal for_user_princ,
@@ -75,6 +76,7 @@ static krb5_error_code handle_request_authdata
      krb5_db_entry *krbtgt,
      krb5_keyblock *client_key,
      krb5_keyblock *server_key,
+     krb5_keyblock *krbtgt_key,
      krb5_data *req_pkt,
      krb5_kdc_req *request,
      krb5_const_principal for_user_princ,
@@ -90,6 +92,7 @@ static krb5_error_code handle_tgt_authdata
      krb5_db_entry *krbtgt,
      krb5_keyblock *client_key,
      krb5_keyblock *server_key,
+     krb5_keyblock *krbtgt_key,
      krb5_data *req_pkt,
      krb5_kdc_req *request,
      krb5_const_principal for_user_princ,
@@ -382,6 +385,7 @@ handle_request_authdata (krb5_context context,
                         krb5_db_entry *krbtgt,
                         krb5_keyblock *client_key,
                         krb5_keyblock *server_key,
+                         krb5_keyblock *krbtgt_key,
                         krb5_data *req_pkt,
                         krb5_kdc_req *request,
                         krb5_const_principal for_user_princ,
@@ -455,6 +459,7 @@ handle_tgt_authdata (krb5_context context,
                     krb5_db_entry *krbtgt,
                     krb5_keyblock *client_key,
                     krb5_keyblock *server_key,
+                    krb5_keyblock *krbtgt_key,
                     krb5_data *req_pkt,
                     krb5_kdc_req *request,
                     krb5_const_principal for_user_princ,
@@ -526,6 +531,7 @@ handle_tgt_authdata (krb5_context context,
                            krbtgt,
                            client_key,
                            server_key, /* U2U or server key */
+                           krbtgt_key,
                            enc_tkt_reply->times.authtime,
                            tgs_req ? enc_tkt_request->authorization_data : NULL,
                            enc_tkt_reply->session,
@@ -562,6 +568,7 @@ handle_authdata (krb5_context context,
                 krb5_db_entry *krbtgt,
                 krb5_keyblock *client_key,
                 krb5_keyblock *server_key,
+                krb5_keyblock *krbtgt_key,
                 krb5_data *req_pkt,
                 krb5_kdc_req *request,
                 krb5_const_principal for_user_princ,
@@ -586,7 +593,7 @@ handle_authdata (krb5_context context,
        case AUTHDATA_SYSTEM_V2:
            code = (*asys->handle_authdata.v2)(context, flags,
                                              client, server, krbtgt,
-                                             client_key, server_key,
+                                             client_key, server_key, krbtgt_key,
                                              req_pkt, request, for_user_princ,
                                              enc_tkt_request,
                                              enc_tkt_reply);
index 9ad832e8ab5fe083081dd73dd119a02ad2c8b972..b2d8d13bd18583ff83e0e9a9003f659a012afb48 100644 (file)
@@ -230,6 +230,7 @@ krb5_error_code
 kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
                    krb5_data *pkt, krb5_ticket **ticket,
                    krb5_db_entry *krbtgt, int *nprincs,
+                   krb5_keyblock **tgskey,
                    krb5_keyblock **subkey,
                    krb5_pa_data **pa_tgs_req)
 {
@@ -243,10 +244,10 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
     krb5_auth_context    auth_context = NULL;
     krb5_authenticator * authenticator = NULL;
     krb5_checksum      * his_cksum = NULL;
-    krb5_keyblock      * key = NULL;
     krb5_kvno            kvno = 0;
 
     *nprincs = 0;
+    *tgskey = NULL;
 
     tmppa = find_pa_data(request->padata, KRB5_PADATA_AP_REQ);
     if (!tmppa)
@@ -289,13 +290,12 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
 #endif
 
     if ((retval = kdc_get_server_key(apreq->ticket, 0, foreign_server,
-                                    krbtgt, nprincs, &key, &kvno)))
+                                    krbtgt, nprincs, tgskey, &kvno)))
        goto cleanup_auth_context;
     /*
      * We do not use the KDB keytab because other parts of the TGS need the TGT key.
      */
-    retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
-    krb5_free_keyblock(kdc_context, key);
+    retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, *tgskey);
     if (retval) 
        goto cleanup_auth_context;
 
@@ -411,6 +411,10 @@ cleanup_auth_context:
     krb5_auth_con_free(kdc_context, auth_context);
 
 cleanup:
+    if (retval != 0) {
+       krb5_free_keyblock(kdc_context, *tgskey);
+       *tgskey = NULL;
+    }
     krb5_free_ap_req(kdc_context, apreq);
     return retval;
 }
@@ -932,7 +936,7 @@ fail:
 int
 validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
                    krb5_db_entry server, krb5_timestamp kdc_time,
-                   const char **status)
+                   const char **status, krb5_data *e_data)
 {
     int                errcode;
     
@@ -1042,7 +1046,7 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
      * Check against local policy
      */
     errcode = against_local_policy_as(request, client, server,
-                                     kdc_time, status); 
+                                     kdc_time, status, e_data);
     if (errcode)
        return errcode;
 
@@ -1225,7 +1229,7 @@ fetch_asn1_field(unsigned char *astream, unsigned int level,
 int
 validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
                     krb5_ticket *ticket, krb5_timestamp kdc_time,
-                    const char **status)
+                    const char **status, krb5_data *e_data)
 {
     int                errcode;
     int                st_idx = 0;
@@ -1458,7 +1462,8 @@ validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
     /*
      * Check local policy
      */
-    errcode = against_local_policy_tgs(request, server, ticket, status);
+    errcode = against_local_policy_tgs(request, server, ticket,
+                                      status, e_data);
     if (errcode)
        return errcode;
     
@@ -1737,6 +1742,7 @@ sign_db_authdata (krb5_context context,
                  krb5_db_entry *krbtgt,
                  krb5_keyblock *client_key,
                  krb5_keyblock *server_key,
+                 krb5_keyblock *krbtgt_key,
                  krb5_timestamp authtime,
                  krb5_authdata **tgs_authdata,
                  krb5_keyblock *session_key,
@@ -1763,6 +1769,7 @@ sign_db_authdata (krb5_context context,
     req.authtime               = authtime;
     req.auth_data              = tgs_authdata;
     req.session_key            = session_key;
+    req.krbtgt_key             = krbtgt_key;
 
     req_data.data = (void *)&req;
     req_data.length = sizeof(req);
@@ -2166,9 +2173,9 @@ kdc_process_s4u2self_req(krb5_context context,
      * the TGT and that we have a global name service.
      */
     flags = 0;
-    switch (krb5_princ_type(kdc_context, request->server)) {
+    switch (krb5_princ_type(context, request->server)) {
     case KRB5_NT_SRV_HST:                  /* (1) */
-       if (krb5_princ_size(kdc_context, request->server) == 2)
+       if (krb5_princ_size(context, request->server) == 2)
            flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM;
        break;
     case KRB5_NT_ENTERPRISE_PRINCIPAL:     /* (2) */
@@ -2204,9 +2211,11 @@ kdc_process_s4u2self_req(krb5_context context,
      */
     if (is_local_principal((*s4u_x509_user)->user_id.user)) {
        krb5_db_entry no_server;
+       krb5_data e_data;
 
+       e_data.data = NULL;
        *nprincs = 1;
-       code = krb5_db_get_principal_ext(kdc_context,
+       code = krb5_db_get_principal_ext(context,
                                         (*s4u_x509_user)->user_id.user,
                                         KRB5_KDB_FLAG_INCLUDE_PAC,
                                         princ, nprincs, &more);
@@ -2227,8 +2236,9 @@ kdc_process_s4u2self_req(krb5_context context,
        memset(&no_server, 0, sizeof(no_server));
 
        code = validate_as_request(request, *princ,
-                                  no_server, kdc_time, status);
+                                  no_server, kdc_time, status, &e_data);
        if (code) {
+           krb5_free_data_contents(context, &e_data);
            return code;
        }
     }
@@ -2613,3 +2623,36 @@ add_pa_data_element(krb5_context context,
     return 0;
 }
 
+void
+kdc_get_ticket_endtime(krb5_context context,
+                      krb5_timestamp starttime,
+                      krb5_timestamp endtime,
+                      krb5_timestamp till,
+                      krb5_db_entry *client,
+                      krb5_db_entry *server,
+                      krb5_timestamp *out_endtime)
+{
+    krb5_timestamp until, life;
+
+    if (till == 0)
+       till = kdc_infinity;
+
+    until = min(till, endtime);
+
+    /* check for underflow */
+    life = (until < starttime) ? 0 : until - starttime;
+
+    if (client->max_life != 0)
+       life = min(life, client->max_life);
+    if (server->max_life != 0)
+       life = min(life, server->max_life);
+    if (max_life_for_realm != 0)
+       life = min(life, max_life_for_realm);
+
+    /* check for overflow */
+    if (starttime > kdc_infinity - life)
+       *out_endtime = kdc_infinity;
+    else
+       *out_endtime = starttime + life;
+}
+
index 07949225052ed224b0bc9b1a8d601d5a9e098c0a..84319f7b850b367d7022c750cb6476a47abaf83d 100644 (file)
@@ -66,7 +66,8 @@ krb5_error_code kdc_process_tgs_req
                   krb5_ticket **,
                   krb5_db_entry *krbtgt,
                   int *nprincs,
-                  krb5_keyblock **, krb5_pa_data **pa_tgs_req);
+                  krb5_keyblock **, krb5_keyblock **,
+                  krb5_pa_data **pa_tgs_req);
 
 krb5_error_code kdc_get_server_key (krb5_ticket *, unsigned int,
                                    krb5_boolean match_enctype,
@@ -75,7 +76,7 @@ krb5_error_code kdc_get_server_key (krb5_ticket *, unsigned int,
 
 int validate_as_request (krb5_kdc_req *, krb5_db_entry, 
                                          krb5_db_entry, krb5_timestamp,
-                                         const char **);
+                                         const char **, krb5_data *);
 
 int validate_forwardable(krb5_kdc_req *, krb5_db_entry, 
                         krb5_db_entry, krb5_timestamp,
@@ -83,7 +84,7 @@ int validate_forwardable(krb5_kdc_req *, krb5_db_entry,
 
 int validate_tgs_request (krb5_kdc_req *, krb5_db_entry, 
                                          krb5_ticket *, krb5_timestamp,
-                                         const char **);
+                                         const char **, krb5_data *);
 
 int fetch_asn1_field (unsigned char *, unsigned int, unsigned int,
                                 krb5_data *);
@@ -144,10 +145,11 @@ krb5_error_code closedown_network (void);
 /* policy.c */
 int against_local_policy_as (krb5_kdc_req *, krb5_db_entry,
                                        krb5_db_entry, krb5_timestamp,
-                                       const char **);
+                                       const char **, krb5_data *);
 
 int against_local_policy_tgs (krb5_kdc_req *, krb5_db_entry,
-                                       krb5_ticket *, const char **);
+                                       krb5_ticket *, const char **,
+                                       krb5_data *);
 
 /* kdc_preauth.c */
 krb5_boolean enctype_requires_etype_info_2(krb5_enctype enctype);
@@ -197,6 +199,7 @@ handle_authdata (krb5_context context,
                 krb5_db_entry *krbtgt,
                 krb5_keyblock *client_key,
                 krb5_keyblock *server_key,
+                krb5_keyblock *krbtgt_key,
                 krb5_data *req_pkt,
                 krb5_kdc_req *request,
                 krb5_const_principal for_user_princ,
@@ -236,6 +239,7 @@ krb5_error_code sign_db_authdata
                krb5_db_entry *krbtgt,
                krb5_keyblock *client_key,
                krb5_keyblock *server_key,
+               krb5_keyblock *krbtgt_key,
                krb5_timestamp authtime,
                krb5_authdata **tgs_authdata,
                krb5_keyblock *session_key,
@@ -296,7 +300,14 @@ validate_transit_path(krb5_context context,
        krb5_const_principal client,
                krb5_db_entry *server,
                      krb5_db_entry *krbtgt);
-
+void
+kdc_get_ticket_endtime(krb5_context context,
+       krb5_timestamp now,
+              krb5_timestamp endtime,
+              krb5_timestamp till,
+              krb5_db_entry *client,
+              krb5_db_entry *server,
+              krb5_timestamp *out_endtime);
 
 void
 log_as_req(const krb5_fulladdr *from,
index 58b26f73d051eef2bf0cdc2ec106f1ee6a0af60e..d4a70feb634dcd030a4d2a00413c8061471b873f 100644 (file)
@@ -60,7 +60,7 @@
 int
 against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
                        krb5_db_entry server, krb5_timestamp kdc_time,
-                       const char **status)
+                       const char **status, krb5_data *e_data)
 {
     krb5_error_code            code;
     kdb_check_policy_as_req    req;
@@ -98,6 +98,7 @@ against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
        return 0;
 
     *status = rep.status;
+    *e_data = rep.e_data;
 
     if (code != 0) {
        code -= ERROR_TABLE_BASE_krb5;
@@ -113,7 +114,8 @@ against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
  */
 krb5_error_code
 against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server,
-                        krb5_ticket *ticket, const char **status)
+                        krb5_ticket *ticket, const char **status,
+                        krb5_data *e_data)
 {
     krb5_error_code            code;
     kdb_check_policy_tgs_req   req;
@@ -154,6 +156,7 @@ against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server,
        return 0;
 
     *status = rep.status;
+    *e_data = rep.e_data;
 
     if (code != 0) {
        code -= ERROR_TABLE_BASE_krb5;
index 7ba89ecd5844b6245a8772d87418cd9d81517d6f..40eea875bc75fe0d6f31a31b4b1e8b28c969dca0 100644 (file)
@@ -899,8 +899,10 @@ kadm5_get_principal(void *server_handle, krb5_principal principal,
     ret = KADM5_OK;
 
 done:
-    if (ret && entry->principal)
+    if (ret && entry->principal) {
         krb5_free_principal(handle->context, entry->principal);
+        entry->principal = NULL;
+    }
     kdb_free_entry(handle, &kdb, &adb);
 
     return ret;
index 54c7eda682846b29afbd38e902ed1d42a7d207b3..8aef88aaf1f6c0d60a13155a69228ba316500a0d 100644 (file)
@@ -624,6 +624,7 @@ krb5_db_open(krb5_context kcontext, char **db_args, int mode)
     status = get_vftabl(kcontext, &v);
     if (status)
        goto clean_n_exit;
+    assert(v->init_module != NULL);
     status = v->init_module(kcontext, section, db_args, mode);
     get_errmsg(kcontext, status);
 
@@ -659,6 +660,10 @@ krb5_db_create(krb5_context kcontext, char **db_args)
     status = get_vftabl(kcontext, &v);
     if (status)
        goto clean_n_exit;
+    if (v->db_create == NULL) {
+       status = KRB5_KDB_DBTYPE_NOSUP;
+       goto clean_n_exit;
+    }
     status = v->db_create(kcontext, section, db_args);
     get_errmsg(kcontext, status);
 
@@ -679,6 +684,7 @@ krb5_db_fini(krb5_context kcontext)
        return 0;
 
     v = &kcontext->dal_handle->lib_handle->vftabl;
+    assert(v->fini_module != NULL);
     status = v->fini_module(kcontext);
     get_errmsg(kcontext, status);
 
@@ -707,6 +713,10 @@ krb5_db_destroy(krb5_context kcontext, char **db_args)
     status = get_vftabl(kcontext, &v);
     if (status)
        goto clean_n_exit;
+    if (v->db_destroy == NULL) {
+       status = KRB5_KDB_DBTYPE_NOSUP;
+       goto clean_n_exit;
+    }
     status = v->db_destroy(kcontext, section, db_args);
     get_errmsg(kcontext, status);
 
@@ -725,6 +735,8 @@ krb5_db_get_age(krb5_context kcontext, char *db_name, time_t * t)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_get_age == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_get_age(kcontext, db_name, t);
     get_errmsg(kcontext, status);
     return status;
@@ -739,6 +751,8 @@ krb5_db_set_option(krb5_context kcontext, int option, void *value)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_set_option == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_set_option(kcontext, option, value);
     get_errmsg(kcontext, status);
     return status;
@@ -753,6 +767,8 @@ krb5_db_lock(krb5_context kcontext, int lock_mode)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_lock == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_lock(kcontext, lock_mode);
     get_errmsg(kcontext, status);
     return status;
@@ -767,6 +783,8 @@ krb5_db_unlock(krb5_context kcontext)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_unlock == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_unlock(kcontext);
     get_errmsg(kcontext, status);
     return status;
@@ -784,6 +802,8 @@ krb5_db_get_principal(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_get_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_get_principal(kcontext, search_for, 0, entries, nentries,
                                 more);
     get_errmsg(kcontext, status);
@@ -803,8 +823,12 @@ krb5_db_get_principal_ext(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
-    return v->db_get_principal(kcontext, search_for, flags, entries, nentries,
-                              more);
+    if (v->db_get_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
+    status = v->db_get_principal(kcontext, search_for,
+                                flags, entries, nentries, more);
+    get_errmsg(kcontext, status);
+    return status;
 }
 
 krb5_error_code
@@ -816,6 +840,8 @@ krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_free_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_free_principal(kcontext, entry, count);
     get_errmsg(kcontext, status);
     return status;
@@ -912,6 +938,8 @@ krb5int_put_principal_no_log(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_put_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = extract_db_args_from_tl_data(kcontext, &entries->tl_data,
                                          &entries->n_tl_data,
                                          &db_args);
@@ -982,6 +1010,11 @@ krb5_db_put_principal(krb5_context kcontext,
         }
     }
 
+    if (v->db_put_principal == NULL) {
+       status = KRB5_KDB_DBTYPE_NOSUP;
+       goto err_lock;
+    }
+
     status = v->db_put_principal(kcontext, entries, nentries, db_args);
     get_errmsg(kcontext, status);
     if (status == 0 && fupd) {
@@ -1015,6 +1048,8 @@ krb5int_delete_principal_no_log(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_delete_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_delete_principal(kcontext, search_for, nentries);
     get_errmsg(kcontext, status);
     return status;
@@ -1062,6 +1097,9 @@ krb5_db_delete_principal(krb5_context kcontext,
        free(princ_name);
     }
 
+    if (v->db_delete_principal == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
+
     status = v->db_delete_principal(kcontext, search_for, nentries);
     get_errmsg(kcontext, status);
 
@@ -1089,6 +1127,8 @@ krb5_db_iterate(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_iterate == NULL)
+       return 0;
     status = v->db_iterate(kcontext, match_entry, func, func_arg);
     get_errmsg(kcontext, status);
     return status;
@@ -1103,6 +1143,8 @@ krb5_supported_realms(krb5_context kcontext, char **realms)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_supported_realms == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_supported_realms(kcontext, realms);
     get_errmsg(kcontext, status);
     return status;
@@ -1117,6 +1159,8 @@ krb5_free_supported_realms(krb5_context kcontext, char **realms)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_free_supported_realms == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_free_supported_realms(kcontext, realms);
     get_errmsg(kcontext, status);
     return status;
@@ -1181,6 +1225,8 @@ krb5_db_get_mkey_list(krb5_context kcontext, krb5_keylist_node ** keylist)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->get_master_key_list == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->get_master_key_list(kcontext, keylist);
     get_errmsg(kcontext, status);
     return status;
@@ -1233,6 +1279,8 @@ krb5_db_store_master_key(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->store_master_key == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->store_master_key(kcontext, keyfile, mname, kvno, key,
                                 master_pwd);
     get_errmsg(kcontext, status);
@@ -1252,6 +1300,8 @@ krb5_db_store_master_key_list(krb5_context kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->store_master_key_list == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->store_master_key_list(kcontext, keyfile, mname, keylist,
                                      master_pwd);
     get_errmsg(kcontext, status);
@@ -1379,6 +1429,8 @@ krb5_db_verify_master_key(krb5_context     kcontext,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->verify_master_key == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->verify_master_key(kcontext, mprinc, kvno, mkey);
     get_errmsg(kcontext, status);
     return status;
@@ -1457,6 +1509,12 @@ krb5_dbe_find_act_mkey(krb5_context         context,
     krb5_timestamp     now;
     krb5_boolean       found = FALSE;
 
+    if (act_mkey_list == NULL) {
+       *act_kvno = 0;
+       *act_mkey = NULL;
+       return 0;
+    }
+
     if ((retval = krb5_timeofday(context, &now)))
         return (retval);
 
@@ -2261,6 +2319,8 @@ krb5_db_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_create_policy == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_create_policy(kcontext, policy);
     get_errmsg(kcontext, status);
     return status;
@@ -2276,6 +2336,8 @@ krb5_db_get_policy(krb5_context kcontext, char *name,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_get_policy == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_get_policy(kcontext, name, policy, cnt);
     get_errmsg(kcontext, status);
     return status;
@@ -2290,6 +2352,8 @@ krb5_db_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_put_policy == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_put_policy(kcontext, policy);
     get_errmsg(kcontext, status);
     return status;
@@ -2305,6 +2369,8 @@ krb5_db_iter_policy(krb5_context kcontext, char *match_entry,
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_iter_policy == NULL)
+       return 0;
     status = v->db_iter_policy(kcontext, match_entry, func, data);
     get_errmsg(kcontext, status);
     return status;
@@ -2319,6 +2385,8 @@ krb5_db_delete_policy(krb5_context kcontext, char *policy)
     status = get_vftabl(kcontext, &v);
     if (status)
        return status;
+    if (v->db_delete_policy == NULL)
+       return KRB5_KDB_DBTYPE_NOSUP;
     status = v->db_delete_policy(kcontext, policy);
     get_errmsg(kcontext, status);
     return status;
@@ -2331,7 +2399,7 @@ krb5_db_free_policy(krb5_context kcontext, osa_policy_ent_t policy)
     kdb_vftabl *v;
 
     status = get_vftabl(kcontext, &v);
-    if (status)
+    if (status || v->db_free_policy == NULL)
        return;
     v->db_free_policy(kcontext, policy);
     get_errmsg(kcontext, status);
index a1fd4d5e83487e3b1e0f2c86407287edd395b754..b493df090f682069a0da359c979a8c99ae0d71bf 100644 (file)
@@ -56,6 +56,8 @@ krb5_dbe_update_mkey_aux
 krb5_dbe_update_mkvno
 krb5_dbe_update_mod_princ_data
 krb5_dbe_update_tl_data
+krb5_dbekd_def_encrypt_key_data
+krb5_dbekd_def_decrypt_key_data
 krb5_dbekd_decrypt_key_data
 krb5_dbekd_encrypt_key_data
 krb5_kt_kdb_ops
index 3f1e3d9eaa2e07bd1bbb2bca89d0021472ec1370..80a68a86f1d60ba253f675d4a3402aa168f17366 100644 (file)
@@ -155,6 +155,7 @@ greet_authdata(krb5_context context,
                krb5_db_entry *tgs,
                krb5_keyblock *client_key,
                krb5_keyblock *server_key,
+               krb5_keyblock *krbtgt_key,
                krb5_data *req_pkt,
                krb5_kdc_req *request,
                krb5_const_principal for_user_princ,
diff --git a/src/plugins/kdb/hdb/Makefile.in b/src/plugins/kdb/hdb/Makefile.in
new file mode 100644 (file)
index 0000000..3f2bccf
--- /dev/null
@@ -0,0 +1,64 @@
+thisconfigdir=../../..
+myfulldir=plugins/kdb/hdb
+mydir=plugins/kdb/hdb
+BUILDTOP=$(REL)..$(S)..$(S)..
+KRB5_RUN_ENV = @KRB5_RUN_ENV@
+KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ;
+PROG_LIBPATH=-L$(TOPLIBD)
+PROG_RPATH=$(KRB5_LIBDIR)
+MODULE_INSTALL_DIR = $(KRB5_DB_MODULE_DIR)
+DEFS=
+
+LOCALINCLUDES = -I../../../lib/kdb -I$(srcdir)/../../../lib/kdb
+DEFINES = -DPLUGIN -DSHLIBEXT=\"$(SHLIBEXT)\"
+
+LIBBASE=hdb
+LIBMAJOR=0
+LIBMINOR=0
+SO_EXT=.so
+RELDIR=../plugins/kdb/hdb
+# Depends on libk5crypto and libkrb5
+# Also on gssrpc, for xdr stuff.
+SHLIB_EXPDEPS = \
+       $(GSSRPC_DEPLIBS) \
+       $(TOPLIBD)/libkdb5$(SHLIBEXT) \
+       $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+       $(TOPLIBD)/libkrb5$(SHLIBEXT)
+SHLIB_EXPLIBS= $(GSSRPC_LIBS) -lkdb5 -lkrb5 -lcom_err -lk5crypto $(KDB5_DB_LIB) $(SUPPORT_LIB) $(LIBS) @DB_EXTRA_LIBS@
+
+SHLIB_DIRS=-L$(TOPLIBD)
+SHLIB_RDIRS=$(KRB5_LIBDIR)
+
+SRCS= \
+       $(srcdir)/kdb_hdb.c \
+       $(srcdir)/kdb_marshal.c \
+       $(srcdir)/kdb_windc.c
+
+STOBJLISTS=OBJS.ST $(DBOBJLISTS)
+STLIBOBJS= \
+       kdb_hdb.o \
+       kdb_marshal.o \
+       kdb_windc.o
+
+all-unix:: all-liblinks
+install-unix:: install-libs
+clean-unix:: clean-libs clean-libobjs
+
+$(DB_DEPS) $(DBOBJLISTS-k5) $(DBSHOBJLISTS): all-recurse
+
+clean::
+       $(RM) lib$(LIBBASE)$(SO_EXT)
+
+@libnover_frag@
+@libobj_frag@
+
+.depend-verify-db: depend-verify-db-$(DB_VERSION)
+depend-verify-db-k5:
+       @if test -r .depend-verify-db; then :; \
+               else (set -x; touch .depend-verify-db); fi
+depend-verify-db-sys:
+       @echo 1>&2 error: cannot build dependencies using system db package
+       @exit 1
+
+.d: .depend-verify-db
+
diff --git a/src/plugins/kdb/hdb/hdb.exports b/src/plugins/kdb/hdb/hdb.exports
new file mode 100644 (file)
index 0000000..f2b7c11
--- /dev/null
@@ -0,0 +1 @@
+kdb_function_table
diff --git a/src/plugins/kdb/hdb/hdb.h b/src/plugins/kdb/hdb/hdb.h
new file mode 100644 (file)
index 0000000..39fbec7
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden). 
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ *
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software 
+ *    without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE. 
+ */
+
+/* $Id: hdb.h 22198 2007-12-07 13:09:25Z lha $ */
+
+#ifndef __HDB_H__
+#define __HDB_H__
+
+#include "hdb_err.h"
+#include "hdb_asn1.h"
+
+struct hdb_dbinfo;
+
+enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
+
+/* flags for various functions */
+#define HDB_F_DECRYPT          1       /* decrypt keys */
+#define HDB_F_REPLACE          2       /* replace entry */
+#define HDB_F_GET_CLIENT       4       /* fetch client */
+#define HDB_F_GET_SERVER       8       /* fetch server */
+#define HDB_F_GET_KRBTGT       16      /* fetch krbtgt */
+#define HDB_F_GET_ANY          28      /* fetch any of client,server,krbtgt */
+#define HDB_F_CANON            32      /* want canonicalition */
+
+/* key usage for master key */
+#define HDB_KU_MKEY    0x484442
+
+typedef struct heim_context *heim_context;
+
+typedef struct hdb_master_key_data *hdb_master_key;
+
+typedef struct hdb_entry_ex {
+    void *ctx;
+    hdb_entry entry;
+    void (*free_entry)(heim_context, struct hdb_entry_ex *);
+} hdb_entry_ex;
+
+typedef struct HDB{
+    void *hdb_db;
+    void *hdb_dbc;
+    char *hdb_name;
+    int hdb_master_key_set;
+    hdb_master_key hdb_master_key;
+    int hdb_openp;
+
+    krb5_error_code (*hdb_open)(heim_context,
+                               struct HDB*,
+                               int,
+                               mode_t);
+    krb5_error_code (*hdb_close)(heim_context, 
+                                struct HDB*);
+    void           (*hdb_free)(heim_context,
+                               struct HDB*,
+                               hdb_entry_ex*);
+    krb5_error_code (*hdb_fetch)(heim_context,
+                                struct HDB*,
+                                const Principal *,
+                                unsigned,
+                                hdb_entry_ex*);
+    krb5_error_code (*hdb_store)(heim_context,
+                                struct HDB*,
+                                unsigned,
+                                hdb_entry_ex*);
+    krb5_error_code (*hdb_remove)(heim_context,
+                                 struct HDB*,
+                                 const Principal *);
+    krb5_error_code (*hdb_firstkey)(heim_context,
+                                   struct HDB*,
+                                   unsigned,
+                                   hdb_entry_ex*);
+    krb5_error_code (*hdb_nextkey)(heim_context,
+                                  struct HDB*,
+                                  unsigned,
+                                  hdb_entry_ex*);
+    krb5_error_code (*hdb_lock)(heim_context,
+                               struct HDB*,
+                               int operation);
+    krb5_error_code (*hdb_unlock)(heim_context,
+                                 struct HDB*);
+    krb5_error_code (*hdb_rename)(heim_context,
+                                 struct HDB*,
+                                 const char*);
+    krb5_error_code (*hdb__get)(heim_context,
+                               struct HDB*,
+                               heim_octet_string,
+                               heim_octet_string*);
+    krb5_error_code (*hdb__put)(heim_context,
+                               struct HDB*,
+                               int, 
+                               heim_octet_string,
+                               heim_octet_string);
+    krb5_error_code (*hdb__del)(heim_context, 
+                               struct HDB*,
+                               heim_octet_string);
+    krb5_error_code (*hdb_destroy)(heim_context,
+                                  struct HDB*);
+}HDB;
+
+#define HDB_INTERFACE_VERSION  4
+
+struct hdb_so_method {
+    int version;
+    const char *prefix;
+    krb5_error_code (*create)(heim_context, HDB **, const char *filename);
+};
+
+typedef krb5_error_code (*hdb_foreach_func_t)(heim_context, HDB*,
+                                             hdb_entry_ex*, void*);
+extern krb5_kt_ops hdb_kt_ops;
+
+#endif /* __HDB_H__ */
diff --git a/src/plugins/kdb/hdb/hdb_asn1.h b/src/plugins/kdb/hdb/hdb_asn1.h
new file mode 100644 (file)
index 0000000..87aa2a4
--- /dev/null
@@ -0,0 +1,586 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* Generated from ./hdb.asn1 */
+/* Do not edit */
+
+#ifndef __hdb_asn1_h__
+#define __hdb_asn1_h__
+
+#include <k5-int.h>
+#include <stddef.h>
+#include <time.h>
+
+#ifndef __asn1_common_definitions__
+#define __asn1_common_definitions__
+
+typedef struct heim_integer {
+  size_t length;
+  void *data;
+  int negative;
+} heim_integer;
+
+typedef struct heim_octet_string {
+  size_t length;
+  void *data;
+} heim_octet_string;
+
+typedef char *heim_general_string;
+
+typedef char *heim_utf8_string;
+
+typedef char *heim_printable_string;
+
+typedef char *heim_ia5_string;
+
+typedef struct heim_bmp_string {
+  size_t length;
+  uint16_t *data;
+} heim_bmp_string;
+
+typedef struct heim_universal_string {
+  size_t length;
+  uint32_t *data;
+} heim_universal_string;
+
+typedef char *heim_visible_string;
+
+typedef struct heim_oid {
+  size_t length;
+  unsigned *components;
+} heim_oid;
+
+typedef struct heim_bit_string {
+  size_t length;
+  void *data;
+} heim_bit_string;
+
+typedef struct heim_octet_string heim_any;
+typedef struct heim_octet_string heim_any_set;
+
+#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R)                  \
+  do {                                                         \
+    (BL) = length_##T((S));                                    \
+    (B) = malloc((BL));                                        \
+    if((B) == NULL) {                                          \
+      (R) = ENOMEM;                                            \
+    } else {                                                   \
+      (R) = encode_##T(((unsigned char*)(B)) + (BL) - 1, (BL), \
+                       (S), (L));                              \
+      if((R) != 0) {                                           \
+        free((B));                                             \
+        (B) = NULL;                                            \
+      }                                                        \
+    }                                                          \
+  } while (0)
+
+struct units;
+
+#endif
+
+typedef int NAME_TYPE;
+
+typedef struct PrincipalName {
+  NAME_TYPE name_type;
+  struct  {
+    unsigned int len;
+    heim_general_string *val;
+  } name_string;
+} PrincipalName;
+
+typedef heim_general_string Realm;
+
+typedef struct Principal {
+  PrincipalName name;
+  Realm realm;
+} Principal;
+
+typedef struct KDCOptions {
+  unsigned int reserved:1;
+  unsigned int forwardable:1;
+  unsigned int forwarded:1;
+  unsigned int proxiable:1;
+  unsigned int proxy:1;
+  unsigned int allow_postdate:1;
+  unsigned int postdated:1;
+  unsigned int unused7:1;
+  unsigned int renewable:1;
+  unsigned int unused9:1;
+  unsigned int unused10:1;
+  unsigned int unused11:1;
+  unsigned int request_anonymous:1;
+  unsigned int canonicalize:1;
+  unsigned int constrained_delegation:1;
+  unsigned int disable_transited_check:1;
+  unsigned int renewable_ok:1;
+  unsigned int enc_tkt_in_skey:1;
+  unsigned int renew:1;
+  unsigned int validate:1;
+} KDCOptions;
+
+typedef struct HostAddress {
+  krb5_ui_4 addr_type;
+  heim_octet_string address;
+} HostAddress;
+
+typedef struct HostAddresses {
+  unsigned int len;
+  HostAddress *val;
+} HostAddresses;
+
+typedef time_t KerberosTime;
+
+typedef krb5_enctype ENCTYPE;
+
+typedef struct EncryptionKey {
+  ENCTYPE keytype;
+  heim_octet_string keyvalue;
+} EncryptionKey;
+
+typedef struct _METHOD_DATA *METHOD_DATA;
+
+typedef struct KDC_REQ_BODY {
+  KDCOptions kdc_options;
+  PrincipalName *cname;
+  Realm realm;
+  PrincipalName *sname;
+  KerberosTime *from;
+  KerberosTime *till;
+  KerberosTime *rtime;
+  krb5_ui_4 nonce;
+  struct  {
+    unsigned int len;
+    ENCTYPE *val;
+  } etype;
+  HostAddresses *addresses;
+  krb5_pointer *enc_authorization_data;
+  struct  {
+    unsigned int len;
+    krb5_pointer *val;
+  } *additional_tickets;
+} KDC_REQ_BODY;
+
+typedef struct KDC_REQ {
+  krb5_ui_4 pvno;
+  krb5_ui_4 msg_type;
+  METHOD_DATA *padata;
+  KDC_REQ_BODY req_body;
+} KDC_REQ;
+
+enum { HDB_DB_FORMAT = 2 };
+
+enum { hdb_pw_salt = 3 };
+
+enum { hdb_afs3_salt = 10 };
+
+/*
+Salt ::= SEQUENCE {
+  type            [0] INTEGER (0..-1),
+  salt            [1] OCTET STRING,
+}
+*/
+
+typedef struct Salt {
+  unsigned int type;
+  heim_octet_string salt;
+} Salt;
+
+int    encode_Salt(unsigned char *, size_t, const Salt *, size_t *);
+int    decode_Salt(const unsigned char *, size_t, Salt *, size_t *);
+void   free_Salt  (Salt *);
+size_t length_Salt(const Salt *);
+int    copy_Salt  (const Salt *, Salt *);
+
+
+/*
+Key ::= SEQUENCE {
+  mkvno           [0] INTEGER (0..-1) OPTIONAL,
+  key             [1] EncryptionKey,
+  salt            [2] Salt OPTIONAL,
+}
+*/
+
+typedef struct Key {
+  unsigned int *mkvno;
+  EncryptionKey key;
+  Salt *salt;
+} Key;
+
+int    encode_Key(unsigned char *, size_t, const Key *, size_t *);
+int    decode_Key(const unsigned char *, size_t, Key *, size_t *);
+void   free_Key  (Key *);
+size_t length_Key(const Key *);
+int    copy_Key  (const Key *, Key *);
+
+
+/*
+Event ::= SEQUENCE {
+  time            [0] KerberosTime,
+  principal       [1] Principal OPTIONAL,
+}
+*/
+
+typedef struct Event {
+  KerberosTime time;
+  Principal *principal;
+} Event;
+
+int    encode_Event(unsigned char *, size_t, const Event *, size_t *);
+int    decode_Event(const unsigned char *, size_t, Event *, size_t *);
+void   free_Event  (Event *);
+size_t length_Event(const Event *);
+int    copy_Event  (const Event *, Event *);
+
+
+/*
+HDBFlags ::= BIT STRING {
+  initial(0),
+  forwardable(1),
+  proxiable(2),
+  renewable(3),
+  postdate(4),
+  server(5),
+  client(6),
+  invalid(7),
+  require-preauth(8),
+  change-pw(9),
+  require-hwauth(10),
+  ok-as-delegate(11),
+  user-to-user(12),
+  immutable(13),
+  trusted-for-delegation(14),
+  allow-kerberos4(15),
+  allow-digest(16)
+}
+*/
+
+typedef struct HDBFlags {
+  unsigned int initial:1;
+  unsigned int forwardable:1;
+  unsigned int proxiable:1;
+  unsigned int renewable:1;
+  unsigned int postdate:1;
+  unsigned int server:1;
+  unsigned int client:1;
+  unsigned int invalid:1;
+  unsigned int require_preauth:1;
+  unsigned int change_pw:1;
+  unsigned int require_hwauth:1;
+  unsigned int ok_as_delegate:1;
+  unsigned int user_to_user:1;
+  unsigned int immutable:1;
+  unsigned int trusted_for_delegation:1;
+  unsigned int allow_kerberos4:1;
+  unsigned int allow_digest:1;
+} HDBFlags;
+
+
+int    encode_HDBFlags(unsigned char *, size_t, const HDBFlags *, size_t *);
+int    decode_HDBFlags(const unsigned char *, size_t, HDBFlags *, size_t *);
+void   free_HDBFlags  (HDBFlags *);
+size_t length_HDBFlags(const HDBFlags *);
+int    copy_HDBFlags  (const HDBFlags *, HDBFlags *);
+unsigned HDBFlags2int(HDBFlags);
+HDBFlags int2HDBFlags(unsigned);
+const struct units * asn1_HDBFlags_units(void);
+
+/*
+GENERATION ::= SEQUENCE {
+  time            [0] KerberosTime,
+  usec            [1] INTEGER (0..-1),
+  gen             [2] INTEGER (0..-1),
+}
+*/
+
+typedef struct GENERATION {
+  KerberosTime time;
+  unsigned int usec;
+  unsigned int gen;
+} GENERATION;
+
+int    encode_GENERATION(unsigned char *, size_t, const GENERATION *, size_t *);
+int    decode_GENERATION(const unsigned char *, size_t, GENERATION *, size_t *);
+void   free_GENERATION  (GENERATION *);
+size_t length_GENERATION(const GENERATION *);
+int    copy_GENERATION  (const GENERATION *, GENERATION *);
+
+
+/*
+HDB-Ext-PKINIT-acl ::= SEQUENCE OF SEQUENCE {
+  subject         [0]   UTF8String,
+  issuer          [1]   UTF8String OPTIONAL,
+  anchor          [2]   UTF8String OPTIONAL,
+}
+*/
+
+typedef struct HDB_Ext_PKINIT_acl {
+  unsigned int len;
+  struct  {
+    heim_utf8_string subject;
+    heim_utf8_string *issuer;
+    heim_utf8_string *anchor;
+  } *val;
+} HDB_Ext_PKINIT_acl;
+
+int    encode_HDB_Ext_PKINIT_acl(unsigned char *, size_t, const HDB_Ext_PKINIT_acl *, size_t *);
+int    decode_HDB_Ext_PKINIT_acl(const unsigned char *, size_t, HDB_Ext_PKINIT_acl *, size_t *);
+void   free_HDB_Ext_PKINIT_acl  (HDB_Ext_PKINIT_acl *);
+size_t length_HDB_Ext_PKINIT_acl(const HDB_Ext_PKINIT_acl *);
+int    copy_HDB_Ext_PKINIT_acl  (const HDB_Ext_PKINIT_acl *, HDB_Ext_PKINIT_acl *);
+
+
+/*
+HDB-Ext-PKINIT-hash ::= SEQUENCE OF SEQUENCE {
+  digest-type     [0]   OBJECT IDENTIFIER,
+  digest          [1] OCTET STRING,
+}
+*/
+
+typedef struct HDB_Ext_PKINIT_hash {
+  unsigned int len;
+  struct  {
+    heim_oid digest_type;
+    heim_octet_string digest;
+  } *val;
+} HDB_Ext_PKINIT_hash;
+
+int    encode_HDB_Ext_PKINIT_hash(unsigned char *, size_t, const HDB_Ext_PKINIT_hash *, size_t *);
+int    decode_HDB_Ext_PKINIT_hash(const unsigned char *, size_t, HDB_Ext_PKINIT_hash *, size_t *);
+void   free_HDB_Ext_PKINIT_hash  (HDB_Ext_PKINIT_hash *);
+size_t length_HDB_Ext_PKINIT_hash(const HDB_Ext_PKINIT_hash *);
+int    copy_HDB_Ext_PKINIT_hash  (const HDB_Ext_PKINIT_hash *, HDB_Ext_PKINIT_hash *);
+
+
+/*
+HDB-Ext-Constrained-delegation-acl ::= SEQUENCE OF Principal
+*/
+
+typedef struct HDB_Ext_Constrained_delegation_acl {
+  unsigned int len;
+  Principal *val;
+} HDB_Ext_Constrained_delegation_acl;
+
+int    encode_HDB_Ext_Constrained_delegation_acl(unsigned char *, size_t, const HDB_Ext_Constrained_delegation_acl *, size_t *);
+int    decode_HDB_Ext_Constrained_delegation_acl(const unsigned char *, size_t, HDB_Ext_Constrained_delegation_acl *, size_t *);
+void   free_HDB_Ext_Constrained_delegation_acl  (HDB_Ext_Constrained_delegation_acl *);
+size_t length_HDB_Ext_Constrained_delegation_acl(const HDB_Ext_Constrained_delegation_acl *);
+int    copy_HDB_Ext_Constrained_delegation_acl  (const HDB_Ext_Constrained_delegation_acl *, HDB_Ext_Constrained_delegation_acl *);
+
+
+/*
+HDB-Ext-Lan-Manager-OWF ::= OCTET STRING
+*/
+
+typedef heim_octet_string HDB_Ext_Lan_Manager_OWF;
+
+int    encode_HDB_Ext_Lan_Manager_OWF(unsigned char *, size_t, const HDB_Ext_Lan_Manager_OWF *, size_t *);
+int    decode_HDB_Ext_Lan_Manager_OWF(const unsigned char *, size_t, HDB_Ext_Lan_Manager_OWF *, size_t *);
+void   free_HDB_Ext_Lan_Manager_OWF  (HDB_Ext_Lan_Manager_OWF *);
+size_t length_HDB_Ext_Lan_Manager_OWF(const HDB_Ext_Lan_Manager_OWF *);
+int    copy_HDB_Ext_Lan_Manager_OWF  (const HDB_Ext_Lan_Manager_OWF *, HDB_Ext_Lan_Manager_OWF *);
+
+
+/*
+HDB-Ext-Password ::= SEQUENCE {
+  mkvno           [0] INTEGER (0..-1) OPTIONAL,
+  password        OCTET STRING,
+}
+*/
+
+typedef struct HDB_Ext_Password {
+  unsigned int *mkvno;
+  heim_octet_string password;
+} HDB_Ext_Password;
+
+int    encode_HDB_Ext_Password(unsigned char *, size_t, const HDB_Ext_Password *, size_t *);
+int    decode_HDB_Ext_Password(const unsigned char *, size_t, HDB_Ext_Password *, size_t *);
+void   free_HDB_Ext_Password  (HDB_Ext_Password *);
+size_t length_HDB_Ext_Password(const HDB_Ext_Password *);
+int    copy_HDB_Ext_Password  (const HDB_Ext_Password *, HDB_Ext_Password *);
+
+
+/*
+HDB-Ext-Aliases ::= SEQUENCE {
+  case-insensitive   [0] BOOLEAN,
+  aliases            [1] SEQUENCE OF Principal,
+}
+*/
+
+typedef struct HDB_Ext_Aliases {
+  int case_insensitive;
+  struct  {
+    unsigned int len;
+    Principal *val;
+  } aliases;
+} HDB_Ext_Aliases;
+
+int    encode_HDB_Ext_Aliases(unsigned char *, size_t, const HDB_Ext_Aliases *, size_t *);
+int    decode_HDB_Ext_Aliases(const unsigned char *, size_t, HDB_Ext_Aliases *, size_t *);
+void   free_HDB_Ext_Aliases  (HDB_Ext_Aliases *);
+size_t length_HDB_Ext_Aliases(const HDB_Ext_Aliases *);
+int    copy_HDB_Ext_Aliases  (const HDB_Ext_Aliases *, HDB_Ext_Aliases *);
+
+
+/*
+HDB-extension ::= SEQUENCE {
+  mandatory       [0] BOOLEAN,
+  data            [1] CHOICE {
+    pkinit-acl               [0] HDB-Ext-PKINIT-acl,
+    pkinit-cert-hash         [1] HDB-Ext-PKINIT-hash,
+    allowed-to-delegate-to   [2] HDB-Ext-Constrained-delegation-acl,
+    lm-owf                   [4] HDB-Ext-Lan-Manager-OWF,
+    password                 [5] HDB-Ext-Password,
+    aliases                  [6] HDB-Ext-Aliases,
+    last-pw-change           [7] KerberosTime,
+    ...,
+  },
+  ...,
+}
+*/
+
+typedef struct HDB_extension {
+  int mandatory;
+  struct  {
+    enum {
+      choice_HDB_extension_data_asn1_ellipsis = 0,
+      choice_HDB_extension_data_pkinit_acl,
+      choice_HDB_extension_data_pkinit_cert_hash,
+      choice_HDB_extension_data_allowed_to_delegate_to,
+      choice_HDB_extension_data_lm_owf,
+      choice_HDB_extension_data_password,
+      choice_HDB_extension_data_aliases,
+      choice_HDB_extension_data_last_pw_change
+      /* ... */
+    } element;
+    union {
+      HDB_Ext_PKINIT_acl pkinit_acl;
+      HDB_Ext_PKINIT_hash pkinit_cert_hash;
+      HDB_Ext_Constrained_delegation_acl allowed_to_delegate_to;
+      HDB_Ext_Lan_Manager_OWF lm_owf;
+      HDB_Ext_Password password;
+      HDB_Ext_Aliases aliases;
+      KerberosTime last_pw_change;
+      heim_octet_string asn1_ellipsis;
+    } u;
+  } data;
+} HDB_extension;
+
+int    encode_HDB_extension(unsigned char *, size_t, const HDB_extension *, size_t *);
+int    decode_HDB_extension(const unsigned char *, size_t, HDB_extension *, size_t *);
+void   free_HDB_extension  (HDB_extension *);
+size_t length_HDB_extension(const HDB_extension *);
+int    copy_HDB_extension  (const HDB_extension *, HDB_extension *);
+
+
+/*
+HDB-extensions ::= SEQUENCE OF HDB-extension
+*/
+
+typedef struct HDB_extensions {
+  unsigned int len;
+  HDB_extension *val;
+} HDB_extensions;
+
+int    encode_HDB_extensions(unsigned char *, size_t, const HDB_extensions *, size_t *);
+int    decode_HDB_extensions(const unsigned char *, size_t, HDB_extensions *, size_t *);
+void   free_HDB_extensions  (HDB_extensions *);
+size_t length_HDB_extensions(const HDB_extensions *);
+int    copy_HDB_extensions  (const HDB_extensions *, HDB_extensions *);
+
+
+/*
+hdb_entry ::= SEQUENCE {
+  principal       [0] Principal OPTIONAL,
+  kvno            [1] INTEGER (0..-1),
+  keys            [2] SEQUENCE OF Key,
+  created-by      [3] Event,
+  modified-by     [4] Event OPTIONAL,
+  valid-start     [5] KerberosTime OPTIONAL,
+  valid-end       [6] KerberosTime OPTIONAL,
+  pw-end          [7] KerberosTime OPTIONAL,
+  max-life        [8] INTEGER (0..-1) OPTIONAL,
+  max-renew       [9] INTEGER (0..-1) OPTIONAL,
+  flags           [10] HDBFlags,
+  etypes          [11] SEQUENCE OF INTEGER (0..-1) OPTIONAL,
+  generation      [12] GENERATION OPTIONAL,
+  extensions      [13] HDB-extensions OPTIONAL,
+}
+*/
+
+typedef struct hdb_entry {
+  Principal *principal;
+  unsigned int kvno;
+  struct  {
+    unsigned int len;
+    Key *val;
+  } keys;
+  Event created_by;
+  Event *modified_by;
+  KerberosTime *valid_start;
+  KerberosTime *valid_end;
+  KerberosTime *pw_end;
+  unsigned int *max_life;
+  unsigned int *max_renew;
+  HDBFlags flags;
+  struct  {
+    unsigned int len;
+    unsigned int *val;
+  } *etypes;
+  GENERATION *generation;
+  HDB_extensions *extensions;
+} hdb_entry;
+
+int    encode_hdb_entry(unsigned char *, size_t, const hdb_entry *, size_t *);
+int    decode_hdb_entry(const unsigned char *, size_t, hdb_entry *, size_t *);
+void   free_hdb_entry  (hdb_entry *);
+size_t length_hdb_entry(const hdb_entry *);
+int    copy_hdb_entry  (const hdb_entry *, hdb_entry *);
+
+
+/*
+hdb_entry_alias ::= [APPLICATION 0] SEQUENCE {
+  principal       [0] Principal OPTIONAL,
+}
+*/
+
+typedef struct hdb_entry_alias {
+  Principal *principal;
+} hdb_entry_alias;
+
+int    encode_hdb_entry_alias(unsigned char *, size_t, const hdb_entry_alias *, size_t *);
+int    decode_hdb_entry_alias(const unsigned char *, size_t, hdb_entry_alias *, size_t *);
+void   free_hdb_entry_alias  (hdb_entry_alias *);
+size_t length_hdb_entry_alias(const hdb_entry_alias *);
+int    copy_hdb_entry_alias  (const hdb_entry_alias *, hdb_entry_alias *);
+
+
+#endif /* __hdb_asn1_h__ */
diff --git a/src/plugins/kdb/hdb/hdb_err.h b/src/plugins/kdb/hdb/hdb_err.h
new file mode 100644 (file)
index 0000000..a47797a
--- /dev/null
@@ -0,0 +1,32 @@
+/* Generated from hdb_err.et */
+/* $Id: hdb_err.et 15878 2005-08-11 13:17:22Z lha $ */
+
+#ifndef __hdb_err_h__
+#define __hdb_err_h__
+
+struct et_list;
+
+void initialize_hdb_error_table_r(struct et_list **);
+
+void initialize_hdb_error_table(void);
+#define init_hdb_err_tbl initialize_hdb_error_table
+
+typedef enum hdb_error_number{
+       HDB_ERR_UK_SERROR = 36150273,
+       HDB_ERR_UK_RERROR = 36150274,
+       HDB_ERR_NOENTRY = 36150275,
+       HDB_ERR_DB_INUSE = 36150276,
+       HDB_ERR_DB_CHANGED = 36150277,
+       HDB_ERR_RECURSIVELOCK = 36150278,
+       HDB_ERR_NOTLOCKED = 36150279,
+       HDB_ERR_BADLOCKMODE = 36150280,
+       HDB_ERR_CANT_LOCK_DB = 36150281,
+       HDB_ERR_EXISTS = 36150282,
+       HDB_ERR_BADVERSION = 36150283,
+       HDB_ERR_NO_MKEY = 36150284,
+       HDB_ERR_MANDATORY_OPTION = 36150285
+} hdb_error_number;
+
+#define ERROR_TABLE_BASE_hdb 36150272
+
+#endif /* __hdb_err_h__ */
diff --git a/src/plugins/kdb/hdb/kdb_hdb.c b/src/plugins/kdb/hdb/kdb_hdb.c
new file mode 100644 (file)
index 0000000..d22489e
--- /dev/null
@@ -0,0 +1,1426 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_hdb.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+static krb5_error_code
+kh_init(void)
+{
+    return 0;
+}
+
+static krb5_error_code
+kh_fini(void)
+{
+    return 0;
+}
+
+krb5_error_code
+kh_map_error(heim_error_code code)
+{
+    switch (code) {
+    case HDB_ERR_UK_SERROR:
+        code = KRB5_KDB_UK_SERROR;
+        break;
+    case HDB_ERR_UK_RERROR:
+        code = KRB5_KDB_UK_RERROR;
+        break;
+    case HDB_ERR_NOENTRY:
+        code = KRB5_KDB_NOENTRY;
+        break;
+    case HDB_ERR_DB_INUSE:
+        code = KRB5_KDB_DB_INUSE;
+        break;
+    case HDB_ERR_DB_CHANGED:
+        code = KRB5_KDB_DB_CHANGED;
+        break;
+    case HDB_ERR_RECURSIVELOCK:
+        code = KRB5_KDB_RECURSIVELOCK;
+        break;
+    case HDB_ERR_NOTLOCKED:
+        code = KRB5_KDB_NOTLOCKED;
+        break;
+    case HDB_ERR_BADLOCKMODE:
+        code = KRB5_KDB_BADLOCKMODE;
+        break;
+    case HDB_ERR_CANT_LOCK_DB:
+        code = KRB5_KDB_CANTLOCK_DB;
+        break;
+    case HDB_ERR_EXISTS:
+        code = EEXIST;
+        break;
+    case HDB_ERR_BADVERSION:
+        code = KRB5_KDB_BAD_VERSION;
+        break;
+    case HDB_ERR_NO_MKEY:
+        code = KRB5_KDB_NOMASTERKEY;
+        break;
+    case HDB_ERR_MANDATORY_OPTION:
+        code = KRB5_KDB_DBTYPE_NOSUP;
+        break;
+    default:
+        break;
+    }
+
+    return code;
+}
+
+static void
+kh_db_context_free(krb5_context context, kh_db_context *kh)
+{
+    if (kh != NULL) {
+        if (kh->hdb != NULL)
+            (*kh->hdb->hdb_destroy)(kh->hcontext, kh->hdb);
+        if (kh->hcontext != NULL)
+            (*kh->heim_free_context)(kh->hcontext);
+        if (kh->libkrb5 != NULL)
+            krb5int_close_plugin(kh->libkrb5);
+        if (kh->libhdb != NULL)
+            krb5int_close_plugin(kh->libhdb);
+        if (kh->windc != NULL)
+            (*kh->windc->fini)(kh->windc_ctx);
+        krb5int_close_plugin_dirs(&kh->windc_plugins);
+        krb5int_mutex_free(kh->lock);
+        free(kh);
+    }
+}
+
+static krb5_error_code
+kh_db_context_init(krb5_context context,
+                   const char *libdir,
+                   const char *filename,
+                   int mode,
+                   kh_db_context **pkh)
+{
+    kh_db_context *kh;
+    krb5_error_code code;
+    char *libhdb = NULL;
+    char *libkrb5 = NULL;
+    struct errinfo errinfo;
+    int *hdb_interface_version;
+
+    if (libdir == NULL)
+        return KRB5_KDB_DBTYPE_INIT; /* XXX */
+
+    memset(&errinfo, 0, sizeof(errinfo));
+
+    kh = k5alloc(sizeof(*kh), &code);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5int_mutex_alloc(&kh->lock);
+    if (code != 0)
+        goto cleanup;
+
+    if (asprintf(&libkrb5, "%s/libkrb5%s", libdir, SHLIBEXT) < 0) {
+        code = ENOMEM;
+        goto cleanup;
+    }
+
+#define GET_PLUGIN_FUNC(_lib, _sym, _member)     do { \
+    code = krb5int_get_plugin_func(kh->_lib, _sym, \
+                                   (void (**)())&kh->_member, &errinfo); \
+    if (code != 0) \
+        goto cleanup; \
+    } while (0)
+
+    /* libkrb5 */
+    code = krb5int_open_plugin(libkrb5, &kh->libkrb5, &errinfo);
+    if (code != 0)
+        goto cleanup;
+
+    GET_PLUGIN_FUNC(libkrb5, "krb5_init_context",     heim_init_context);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_free_context",     heim_free_context);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_free_principal",   heim_free_principal);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_free_addresses",   heim_free_addresses);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_pac_free",         heim_pac_free);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_pac_parse",        heim_pac_parse);
+    GET_PLUGIN_FUNC(libkrb5, "krb5_pac_verify",       heim_pac_verify);
+    GET_PLUGIN_FUNC(libkrb5, "_krb5_pac_sign",        heim_pac_sign);
+    
+    if (asprintf(&libhdb, "%s/libhdb%s", libdir, SHLIBEXT) < 0)
+        goto cleanup;
+
+    /* libhdb */
+    code = krb5int_open_plugin(libhdb, &kh->libhdb, &errinfo);
+    if (code != 0)
+        goto cleanup;
+
+    /*
+     * New versions of Heimdal export this symbol to mark the
+     * HDB ABI version.
+     */
+    if (krb5int_get_plugin_data(kh->libhdb, "hdb_interface_version",
+                                (void **)&hdb_interface_version,
+                                &errinfo) == 0 &&
+        *hdb_interface_version != HDB_INTERFACE_VERSION) {
+        code = KRB5_KDB_DBTYPE_NOSUP;
+        goto cleanup;
+    }
+
+    GET_PLUGIN_FUNC(libhdb,  "hdb_create",            hdb_create);
+    GET_PLUGIN_FUNC(libhdb,  "hdb_seal_key",          hdb_seal_key);
+    GET_PLUGIN_FUNC(libhdb,  "hdb_unseal_key",        hdb_unseal_key);
+    GET_PLUGIN_FUNC(libhdb,  "hdb_set_master_key",    hdb_set_master_key);
+    GET_PLUGIN_FUNC(libhdb,  "hdb_free_entry",        hdb_free_entry);
+
+    code = kh_map_error((*kh->heim_init_context)(&kh->hcontext));
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_map_error((*kh->hdb_create)(kh->hcontext, &kh->hdb, filename));
+    if (code != 0)
+        goto cleanup;
+
+    if (mode & KRB5_KDB_OPEN_RO)
+        kh->mode = O_RDONLY;
+    else
+        kh->mode = O_RDWR;
+
+    if (mode & KRB5_KDB_SRV_TYPE_KDC)
+        kh_hdb_windc_init(context, libdir, kh);
+
+cleanup:
+    if (code != 0) {
+        kh_db_context_free(context, kh);
+        kh = NULL;
+    }
+
+    krb5int_free_error(&errinfo, NULL);
+
+    *pkh = kh;
+
+    return code;
+}
+
+static krb5_error_code
+kh_init_module(krb5_context context,
+               char *conf_section,
+               char **db_args,
+               int mode)
+{
+    kdb5_dal_handle *dal_handle = context->dal_handle;
+    krb5_error_code code;
+    kh_db_context *kh;
+    char *libdir = NULL;
+    char *filename = NULL;
+
+    if (dal_handle->db_context != NULL) {
+        kh_db_context_free(context, dal_handle->db_context);
+        dal_handle->db_context = NULL;
+    }
+
+    code = profile_get_string(context->profile,
+                              KDB_MODULE_SECTION,
+                              conf_section,
+                              "heimdal_libdir",
+                              NULL,
+                              &libdir);
+    if (code != 0)
+        goto cleanup;
+
+    code = profile_get_string(context->profile,
+                              KDB_MODULE_SECTION,
+                              conf_section,
+                              "heimdal_dbname",
+                              NULL,
+                              &filename);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_db_context_init(context, libdir, filename, mode, &kh);
+    if (code != 0)
+        goto cleanup;
+
+    dal_handle->db_context = kh;
+
+cleanup:
+    if (libdir != NULL)
+        free(libdir);
+    if (filename != NULL)
+        free(filename);
+
+    return 0;
+}
+
+static krb5_error_code
+kh_fini_module(krb5_context context)
+{
+    kdb5_dal_handle *dal_handle = context->dal_handle;
+
+    kh_db_context_free(context, dal_handle->db_context);
+    dal_handle->db_context = NULL;
+
+    return 0;
+}
+
+/*
+ * Heimdal API and SPI wrappers.
+ */
+
+static krb5_error_code
+kh_hdb_open(krb5_context context,
+            kh_db_context *kh,
+            int oflag,
+            mode_t mode)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_open)(kh->hcontext, kh->hdb, oflag, mode);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_close(krb5_context context,kh_db_context *kh)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_close)(kh->hcontext, kh->hdb);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_fetch(krb5_context context,
+             kh_db_context *kh,
+             const Principal *princ,
+             unsigned int flags,
+             hdb_entry_ex *entry)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_fetch)(kh->hcontext, kh->hdb, princ, flags, entry);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_store(krb5_context context,
+             kh_db_context *kh,
+             unsigned int flags,
+             hdb_entry_ex *entry)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_store)(kh->hcontext, kh->hdb, flags, entry);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_remove(krb5_context context,
+              kh_db_context *kh,
+              const Principal *princ)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_remove)(kh->hcontext, kh->hdb, princ);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_firstkey(krb5_context context,
+                kh_db_context *kh,
+                unsigned int flags,
+                hdb_entry_ex *entry)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_firstkey)(kh->hcontext, kh->hdb, flags, entry);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_nextkey(krb5_context context,
+               kh_db_context *kh,
+               unsigned int flags,
+               hdb_entry_ex *entry)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_nextkey)(kh->hcontext, kh->hdb, flags, entry);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_lock(krb5_context context,
+            kh_db_context *kh,
+            int operation)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_lock)(kh->hcontext, kh->hdb, operation);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_unlock(krb5_context context,
+              kh_db_context *kh)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb->hdb_unlock)(kh->hcontext, kh->hdb);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_rename(krb5_context context,
+              kh_db_context *kh,
+              const char *name)
+{
+    heim_error_code hcode;
+
+    if (kh->hdb->hdb_rename == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    hcode = (*kh->hdb->hdb_rename)(kh->hcontext, kh->hdb, name);
+
+    return kh_map_error(hcode);
+}
+
+static HDB_extension *
+kh_hdb_find_extension(const hdb_entry *entry, unsigned int type)
+{
+    unsigned int i;
+    HDB_extension *ret = NULL;
+
+    if (entry->extensions != NULL) {
+        for (i = 0; i < entry->extensions->len; i++) {
+            if (entry->extensions->val[i].data.element == type) {
+                ret = &entry->extensions->val[i];
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
+static krb5_error_code
+kh_hdb_seal_key(krb5_context context,
+                kh_db_context *kh,
+                Key *key)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb_seal_key)(kh->hcontext, kh->hdb, key);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_unseal_key(krb5_context context,
+                  kh_db_context *kh,
+                  Key *key)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb_unseal_key)(kh->hcontext, kh->hdb, key);
+
+    return kh_map_error(hcode);
+}
+
+static krb5_error_code
+kh_hdb_set_master_key(krb5_context context,
+                      kh_db_context *kh,
+                      EncryptionKey *key)
+{
+    heim_error_code hcode;
+
+    hcode = (*kh->hdb_set_master_key)(kh->hcontext, kh->hdb, key);
+
+    return kh_map_error(hcode);
+}
+
+void
+kh_hdb_free_entry(krb5_context context,
+                  kh_db_context *kh,
+                  hdb_entry_ex *entry)
+{
+    (*kh->hdb_free_entry)(kh->hcontext, entry);
+}
+
+void
+kh_kdb_free_entry(krb5_context context,
+                  kh_db_context *kh,
+                  krb5_db_entry *entry)
+{
+    krb5_tl_data *tl_data_next = NULL;
+    krb5_tl_data *tl_data = NULL;
+    int i, j;
+
+    if (entry->e_data != NULL) {
+        assert(entry->e_length == sizeof(hdb_entry_ex));
+        kh_hdb_free_entry(context, kh, KH_DB_ENTRY(entry));
+        free(entry->e_data);
+    }
+
+    krb5_free_principal(context, entry->princ);
+
+    for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
+        tl_data_next = tl_data->tl_data_next;
+        if (tl_data->tl_data_contents != NULL)
+            free(tl_data->tl_data_contents);
+        free(tl_data);
+    }
+
+    if (entry->key_data != NULL) {
+        for (i = 0; i < entry->n_key_data; i++) {
+            for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
+                if (entry->key_data[i].key_data_length[j] != 0) {
+                    if (entry->key_data[i].key_data_contents[j] != NULL) {
+                        memset(entry->key_data[i].key_data_contents[j],
+                               0,
+                               entry->key_data[i].key_data_length[j]);
+                        free(entry->key_data[i].key_data_contents[j]);
+                    }
+                }
+                entry->key_data[i].key_data_contents[j] = NULL;
+                entry->key_data[i].key_data_length[j] = 0;
+                entry->key_data[i].key_data_type[j] = 0;
+            }
+        }
+        free(entry->key_data);
+    }
+
+    memset(entry, 0, sizeof(*entry));
+}
+
+static krb5_error_code
+kh_db_create(krb5_context context,
+             char *conf_section,
+             char **db_args)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    code = kh_hdb_open(context, kh, kh->mode, 0);
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_destroy(krb5_context context,
+              char *conf_section,
+              char **db_args)
+{
+    return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_get_age(krb5_context context,
+              char *db_name,
+              time_t *age)
+{
+    return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_set_option(krb5_context context,
+                  int option,
+                  void *value)
+{
+    return KRB5_KDB_DBTYPE_NOSUP;
+}
+
+static krb5_error_code
+kh_db_lock(krb5_context context, int kmode)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    enum hdb_lockop hmode;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    if (kmode & KRB5_DB_LOCKMODE_EXCLUSIVE)
+        hmode = HDB_WLOCK;
+    else
+        hmode = HDB_RLOCK;
+
+    code = kh_hdb_lock(context, kh, hmode);
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_unlock(krb5_context context)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    code = kh_hdb_unlock(context, kh);
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+krb5_error_code
+kh_get_principal(krb5_context context,
+                 kh_db_context *kh,
+                 krb5_const_principal princ,
+                 unsigned int hflags,
+                 krb5_db_entry *kentry)
+{
+    krb5_error_code code;
+    Principal *hprinc = NULL;
+    hdb_entry_ex *hentry = NULL;
+
+    code = kh_marshal_Principal(context, princ, &hprinc);
+    if (code != 0)
+        return code;
+
+    code = kh_hdb_open(context, kh, kh->mode, 0);
+    if (code != 0) {
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+
+    hentry = k5alloc(sizeof(*hentry), &code);
+    if (code != 0) {
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+
+    code = kh_hdb_fetch(context, kh, hprinc, hflags, hentry);
+    if (code != 0) {
+        kh_hdb_close(context, kh);
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+
+    code = kh_unmarshal_hdb_entry(context, &hentry->entry, kentry);
+    if (code == 0) {
+        kentry->e_length = sizeof(*hentry);
+        kentry->e_data = (krb5_octet *)hentry;
+    } else {
+        kh_hdb_free_entry(context, kh, hentry);
+        free(hentry);
+    }
+
+    kh_hdb_close(context, kh);
+    kh_free_Principal(context, hprinc);
+
+    return code;
+}
+
+static krb5_boolean
+kh_is_master_key_principal(krb5_context context,
+                           krb5_const_principal princ)
+{
+    return krb5_princ_size(context, princ) == 2 &&
+        data_eq_string(princ->data[0], "K") &&
+        data_eq_string(princ->data[1], "M");
+}
+
+static krb5_error_code
+kh_is_tgs_principal(krb5_context context,
+                    krb5_const_principal princ)
+{
+    return krb5_princ_size(context, princ) == 2 &&
+        data_eq_string(princ->data[0], KRB5_TGS_NAME);
+}
+
+static krb5_error_code
+kh_get_master_key_principal(krb5_context context,
+                            kh_db_context *kh,
+                            krb5_const_principal princ,
+                            krb5_db_entry *kentry,
+                            int *nentries)
+{
+    krb5_error_code code;
+    krb5_key_data *key_data;
+    krb5_timestamp now;
+
+    memset(kentry, 0, sizeof(*kentry));
+    *nentries = 0;
+
+    kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+    kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+    kentry->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+
+    if (princ == NULL)
+        code = krb5_parse_name(context, KRB5_KDB_M_NAME, &kentry->princ);
+    else
+        code = krb5_copy_principal(context, princ, &kentry->princ);
+    if (code != 0)
+        return code;
+
+    now = time(NULL);
+
+    code = krb5_dbe_update_mod_princ_data(context, kentry, now, kentry->princ);
+    if (code != 0) {
+        kh_kdb_free_entry(context, kh, kentry);
+        return code;
+    }
+
+    /* Return a dummy principal */
+    kentry->n_key_data = 1;
+    kentry->key_data = k5alloc(sizeof(krb5_key_data), &code);
+    if (code != 0) {
+        kh_kdb_free_entry(context, kh, kentry);
+        return code;
+    }
+
+    key_data = &kentry->key_data[0];
+
+    key_data->key_data_ver          = KRB5_KDB_V1_KEY_DATA_ARRAY;
+    key_data->key_data_kvno         = 1;
+    key_data->key_data_type[0]      = ENCTYPE_UNKNOWN;
+
+    *nentries = 1;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_db_get_principal(krb5_context context,
+                    krb5_const_principal princ,
+                    unsigned int kflags,
+                    krb5_db_entry *kentry,
+                    int *nentries,
+                    krb5_boolean *more)
+{
+    krb5_error_code code;
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    unsigned int hflags;
+
+    *nentries = 0;
+    *more = FALSE;
+    memset(kentry, 0, sizeof(*kentry));
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    if (kh_is_master_key_principal(context, princ))
+        return kh_get_master_key_principal(context, kh, princ,
+                                           kentry, nentries);
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    hflags = 0;
+    if (kflags & KRB5_KDB_FLAG_CANONICALIZE)
+        hflags |= HDB_F_CANON;
+    if (kflags & (KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY |
+                  KRB5_KDB_FLAG_INCLUDE_PAC))
+        hflags |= HDB_F_GET_CLIENT;
+    else if (kh_is_tgs_principal(context, princ))
+        hflags |= HDB_F_GET_KRBTGT;
+    else
+        hflags |= HDB_F_GET_ANY;
+
+    code = kh_get_principal(context, kh, princ, hflags, kentry);
+    switch (code) {
+    case 0:
+        *nentries = 1;
+        break;
+    case KRB5_KDB_NOENTRY:
+        code = 0;
+        break;
+    default:
+        break;
+    }
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_free_principal(krb5_context context,
+                     krb5_db_entry *entry,
+                     int count)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    int i;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    for (i = 0; i < count; i++)
+        kh_kdb_free_entry(context, kh, &entry[i]);
+
+    k5_mutex_unlock(kh->lock);
+
+    return 0;
+}
+
+static krb5_error_code
+kh_put_principal(krb5_context context,
+                 kh_db_context *kh,
+                 krb5_db_entry *kentry)
+{
+    krb5_error_code code;
+    hdb_entry_ex *hentry = NULL;
+    unsigned int hflags;
+
+    hflags = 0;
+
+    if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0)
+        hflags |= HDB_F_REPLACE;
+
+    hentry = k5alloc(sizeof(*hentry), &code);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_marshal_hdb_entry(context, kentry, &hentry->entry);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_hdb_open(context, kh, kh->mode, 0);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_hdb_store(context, kh, hflags, hentry);
+    if (code != 0) {
+        kh_hdb_close(context, kh);
+        goto cleanup;
+    }
+
+    if (kentry->e_data != NULL) {
+        assert(kentry->e_length == sizeof(hdb_entry_ex));
+        kh_hdb_free_entry(context, kh, KH_DB_ENTRY(kentry));
+        free(kentry->e_data);
+    }
+
+    kentry->e_length = sizeof(*hentry);
+    kentry->e_data = (krb5_octet *)hentry;
+    hentry = NULL;
+
+    kh_hdb_close(context, kh);
+
+cleanup:
+    if (hentry != NULL) {
+        kh_hdb_free_entry(context, kh, hentry);
+        free(hentry);
+    }
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_put_principal(krb5_context context,
+                    krb5_db_entry *entries,
+                    int *nentries,
+                    char **db_args)
+{
+    krb5_error_code code;
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    int i;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    for (i = 0; i < *nentries; i++) {
+        code = kh_put_principal(context, kh, &entries[i]);
+        if (code != 0)
+            break;
+    }
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_delete_principal(krb5_context context,
+                    kh_db_context *kh,
+                    krb5_const_principal princ)
+{
+    krb5_error_code code;
+    Principal *hprinc;
+
+    code = kh_marshal_Principal(context, princ, &hprinc);
+    if (code != 0)
+        return code;
+
+    code = kh_hdb_open(context, kh, kh->mode, 0);
+    if (code != 0) {
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+
+    code = kh_hdb_remove(context, kh, hprinc);
+
+    kh_hdb_close(context, kh);
+    kh_free_Principal(context, hprinc);
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_delete_principal(krb5_context context,
+                       krb5_const_principal princ,
+                       int *nentries)
+{
+    krb5_error_code code;
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    code = kh_delete_principal(context, kh, princ);
+
+    *nentries = (code == 0) ? 1 : 0;
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_db_iterate(krb5_context context,
+              char *match_entry,
+              int (*func)(krb5_pointer, krb5_db_entry *),
+              krb5_pointer func_arg)
+{
+    krb5_error_code code;
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    hdb_entry_ex hentry;
+    unsigned int hflags = HDB_F_GET_ANY;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    memset(&hentry, 0, sizeof(hentry));
+
+    code = kh_hdb_open(context, kh, kh->mode, 0);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_hdb_firstkey(context, kh, hflags, &hentry);
+    while (code == 0) {
+        krb5_db_entry kentry;
+
+        if (kh_unmarshal_hdb_entry(context, &hentry.entry, &kentry) == 0) {
+            code = (*func)(func_arg, &kentry);
+            kh_kdb_free_entry(context, kh, &kentry);
+        }
+
+        kh_hdb_free_entry(context, kh, &hentry);
+
+        if (code != 0)
+            break;
+
+        code = kh_hdb_nextkey(context, kh, hflags, &hentry);
+    }
+
+    if (code == KRB5_KDB_NOENTRY) {
+        krb5_db_entry kentry;
+        int nentries;
+
+        /* Return the fake master key principal */
+        if (kh_get_master_key_principal(context, kh, NULL,
+                                        &kentry, &nentries) == 0) {
+            code = (*func)(func_arg, &kentry);
+            kh_kdb_free_entry(context, kh, &kentry);
+        }
+
+        code = 0;
+    }
+
+    kh_hdb_close(context, kh);
+
+cleanup:
+    k5_mutex_unlock(kh->lock);
+
+    return 0;
+}
+
+static krb5_error_code
+kh_fetch_master_key(krb5_context context,
+                    krb5_principal name,
+                    krb5_keyblock *key,
+                    krb5_kvno *kvno,
+                    char *db_args)
+{
+    return 0;
+}
+
+static krb5_error_code
+kh_fetch_master_key_list(krb5_context context,
+                         krb5_principal mname,
+                         const krb5_keyblock *key,
+                         krb5_kvno kvno,
+                         krb5_keylist_node **mkeys_list)
+{
+    /* just create a dummy one so that the KDC doesn't balk */
+    krb5_keylist_node *mkey;
+    krb5_error_code code;
+
+    mkey = k5alloc(sizeof(*mkey), &code);
+    if (code != 0)
+        return code;
+
+    mkey->keyblock.magic = KV5M_KEYBLOCK;
+    mkey->keyblock.enctype = ENCTYPE_UNKNOWN;
+    mkey->kvno = 1;
+
+    *mkeys_list = mkey;
+
+    return 0;
+}
+
+static void *
+kh_db_alloc(krb5_context context, void *ptr, size_t size)
+{
+    return realloc(ptr, size);
+}
+
+static void
+kh_db_free(krb5_context context, void *ptr)
+{
+    free(ptr);
+}
+
+static krb5_error_code
+kh_set_master_key(krb5_context context,
+                  char *pwd,
+                  krb5_keyblock *kkey)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    EncryptionKey hkey;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    if (kkey->enctype == ENCTYPE_UNKNOWN)
+        return 0;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    KH_MARSHAL_KEY(kkey, &hkey);
+
+    code = kh_hdb_set_master_key(context, kh, &hkey);
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+static krb5_error_code
+kh_get_master_key(krb5_context context,
+                  krb5_keyblock **pkey)
+{
+    krb5_error_code code;
+    krb5_keyblock *key;
+
+    /*
+     * The Heimdal master key interface is opaque; we can't
+     * return the master key without poking into internal data
+     * structures that would make this bridge even more brittle.
+     * So, we just return a dummy key.
+     */
+    key = k5alloc(sizeof(krb5_keyblock), &code);
+    if (code != 0)
+        return code;
+
+    key->magic = KV5M_KEYBLOCK;
+    key->enctype = ENCTYPE_UNKNOWN;
+
+    *pkey = key;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_promote_db(krb5_context context,
+              char *conf_section,
+              char **db_args)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    char *name;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    if (kh->hdb->hdb_name == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    if (asprintf(&name, "%s~", kh->hdb->hdb_name) < 0)
+        return ENOMEM;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0) {
+        free(name);
+        return code;
+    }
+
+    code = kh_hdb_rename(context, kh, name);
+
+    k5_mutex_unlock(kh->lock);
+    free(name);
+
+    return code;
+}
+
+krb5_error_code
+kh_decrypt_key(krb5_context context,
+               kh_db_context *kh,
+               const krb5_key_data *key_data,
+               krb5_keyblock *kkey,
+               krb5_keysalt *keysalt)
+{
+    krb5_error_code code;
+    Key hkey;
+
+    memset(&hkey, 0, sizeof(hkey));
+
+    hkey.key.keytype = key_data->key_data_type[0];
+    hkey.key.keyvalue.data = k5alloc(key_data->key_data_length[0], &code);
+    if (code != 0)
+        return code;
+
+    memcpy(hkey.key.keyvalue.data, key_data->key_data_contents[0],
+           key_data->key_data_length[0]);
+    hkey.key.keyvalue.length = key_data->key_data_length[0];
+
+    code = kh_hdb_unseal_key(context, kh, &hkey);
+    if (code != 0) {
+        memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+        free(hkey.key.keyvalue.data);
+        return code;
+    }
+
+    kkey->magic     = KV5M_KEYBLOCK;
+    kkey->enctype   = hkey.key.keytype;
+    kkey->contents  = hkey.key.keyvalue.data;
+    kkey->length    = hkey.key.keyvalue.length;
+
+    if (keysalt != NULL) {
+        keysalt->type = key_data->key_data_type[1];
+        keysalt->data.data = k5alloc(key_data->key_data_length[1], &code);
+        if (code != 0) {
+            memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+            free(hkey.key.keyvalue.data);
+            return code;
+        }
+
+        memcpy(keysalt->data.data, key_data->key_data_contents[1],
+               key_data->key_data_length[1]);
+        keysalt->data.length = key_data->key_data_length[1];
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+kh_dbekd_decrypt_key_data(krb5_context context,
+                          const krb5_keyblock *mkey,
+                          const krb5_key_data *key_data,
+                          krb5_keyblock *kkey,
+                          krb5_keysalt *keysalt)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    if (mkey->enctype != ENCTYPE_UNKNOWN)
+        code = krb5_dbekd_def_decrypt_key_data(context, mkey, key_data,
+                                               kkey, keysalt);
+    else
+        code = kh_decrypt_key(context, kh, key_data, kkey, keysalt);
+
+    return code;
+}
+
+static krb5_error_code
+kh_encrypt_key(krb5_context context,
+               kh_db_context *kh,
+               const krb5_keyblock *kkey,
+               const krb5_keysalt *keysalt,
+               int keyver,
+               krb5_key_data *key_data)
+{
+    krb5_error_code code;
+    Key hkey;
+
+    memset(&hkey, 0, sizeof(hkey));
+    memset(key_data, 0, sizeof(*key_data));
+
+    hkey.key.keytype = kkey->enctype;
+    hkey.key.keyvalue.data = k5alloc(kkey->length, &code);
+    if (code != 0)
+        return code;
+
+    memcpy(hkey.key.keyvalue.data, kkey->contents, kkey->length);
+    hkey.key.keyvalue.length = kkey->length;
+
+    code = kh_hdb_seal_key(context, kh, &hkey);
+    if (code != 0) {
+        memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+        free(hkey.key.keyvalue.data);
+        return code;
+    }
+
+    key_data->key_data_ver          = KRB5_KDB_V1_KEY_DATA_ARRAY;
+    key_data->key_data_kvno         = keyver;
+    key_data->key_data_type[0]      = hkey.key.keytype;
+    key_data->key_data_contents[0]  = hkey.key.keyvalue.data;
+    key_data->key_data_length[0]    = hkey.key.keyvalue.length;
+
+    if (keysalt != NULL) {
+        key_data->key_data_type[1] = keysalt->type;
+        key_data->key_data_contents[1] = k5alloc(keysalt->data.length, &code);
+        if (code != 0) {
+            memset(hkey.key.keyvalue.data, 0, hkey.key.keyvalue.length);
+            free(hkey.key.keyvalue.data);
+            return code;
+        }
+
+        memcpy(key_data->key_data_contents[1],
+               keysalt->data.data, keysalt->data.length);
+        key_data->key_data_length[1] = keysalt->data.length;
+    }
+
+    return 0;
+}
+
+static krb5_error_code
+kh_dbekd_encrypt_key_data(krb5_context context,
+                          const krb5_keyblock *mkey,
+                          const krb5_keyblock *kkey,
+                          const krb5_keysalt *keysalt,
+                          int keyver,
+                          krb5_key_data *key_data)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    /* For migration */
+    if (mkey->enctype != ENCTYPE_UNKNOWN)
+        code = krb5_dbekd_def_encrypt_key_data(context, mkey, kkey,
+                                               keysalt, keyver, key_data);
+    else
+        code = kh_encrypt_key(context, kh, kkey, keysalt, keyver, key_data);
+
+    return code;
+}
+
+/*
+ * Invoke methods
+ */
+
+static krb5_error_code
+kh_db_check_allowed_to_delegate(krb5_context context,
+                                unsigned int method,
+                                const krb5_data *req_data,
+                                krb5_data *rep_data)
+{
+    kdb_check_allowed_to_delegate_req *req;
+    krb5_error_code code;
+    hdb_entry_ex *hentry;
+    HDB_extension *ext;
+    HDB_Ext_Constrained_delegation_acl *acl;
+    unsigned int i;
+
+    req = (kdb_check_allowed_to_delegate_req *)req_data->data;
+    hentry = KH_DB_ENTRY(req->server);
+    ext = kh_hdb_find_extension(&hentry->entry,
+                                choice_HDB_extension_data_allowed_to_delegate_to);
+
+    code = KRB5KDC_ERR_POLICY;
+
+    if (ext != NULL) {
+        acl = &ext->data.u.allowed_to_delegate_to;
+
+        for (i = 0; i < acl->len; i++) {
+            krb5_principal princ;
+
+            if (kh_unmarshal_Principal(context, &acl->val[i], &princ) == 0) {
+                if (krb5_principal_compare(context, req->proxy, princ)) {
+                    code = 0;
+                    krb5_free_principal(context, princ);
+                    break;
+                }
+                krb5_free_principal(context, princ);
+            }
+        }
+    }
+
+    return code;
+}
+
+static struct _kh_invoke_fn {
+    unsigned int method;
+    krb5_error_code (*function)(krb5_context, unsigned int,
+                                const krb5_data *, krb5_data *);
+} kh_invoke_vtable[] = {
+    { KRB5_KDB_METHOD_CHECK_POLICY_AS,           kh_db_check_policy_as },
+    { KRB5_KDB_METHOD_SIGN_AUTH_DATA,            kh_db_sign_auth_data },
+    { KRB5_KDB_METHOD_CHECK_ALLOWED_TO_DELEGATE, kh_db_check_allowed_to_delegate },
+};
+
+static krb5_error_code
+kh_db_invoke(krb5_context context,
+             unsigned int method,
+             const krb5_data *req,
+             krb5_data *rep)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    size_t i;
+    krb5_error_code code;
+
+    if (kh == NULL)
+        return KRB5_KDB_DBNOTINITED;
+
+    code = k5_mutex_lock(kh->lock);
+    if (code != 0)
+        return code;
+
+    code = KRB5_KDB_DBTYPE_NOSUP;
+
+    for (i = 0;
+         i < sizeof(kh_invoke_vtable) / sizeof(kh_invoke_vtable[0]);
+         i++) {
+        struct _kh_invoke_fn *fn = &kh_invoke_vtable[i];
+
+        if (fn->method == method) {
+            code = (*fn->function)(context, method, req, rep);
+            break;
+        }
+    }
+
+    k5_mutex_unlock(kh->lock);
+
+    return code;
+}
+
+kdb_vftabl kdb_function_table = {
+    1,
+    0,
+    kh_init,
+    kh_fini,
+    kh_init_module,
+    kh_fini_module,
+    kh_db_create,
+    kh_db_destroy,
+    kh_db_get_age,
+    kh_db_set_option,
+    kh_db_lock,
+    kh_db_unlock,
+    kh_db_get_principal,
+    kh_db_free_principal,
+    kh_db_put_principal,
+    kh_db_delete_principal,
+    kh_db_iterate,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    kh_db_alloc,
+    kh_db_free,
+    kh_set_master_key,
+    kh_get_master_key,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    kh_fetch_master_key,
+    NULL,
+    kh_fetch_master_key_list,
+    NULL,
+    NULL,
+    NULL,
+    kh_promote_db,
+    kh_dbekd_decrypt_key_data,
+    kh_dbekd_encrypt_key_data,
+    kh_db_invoke,
+};
+
diff --git a/src/plugins/kdb/hdb/kdb_hdb.h b/src/plugins/kdb/hdb/kdb_hdb.h
new file mode 100644 (file)
index 0000000..9cfbef6
--- /dev/null
@@ -0,0 +1,172 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_hdb.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#ifndef KRB5_KDB_HDB_H
+#define KRB5_KDB_HDB_H
+
+#include "k5-plugin.h"
+#include "hdb.h"
+#include "windc_plugin.h"
+
+typedef krb5_int32 heim_error_code;
+
+typedef struct _kh_db_context {
+    k5_mutex_t *lock;
+    heim_context hcontext;
+    HDB *hdb;
+    int mode;
+
+    /* libkrb5 APIs */
+    struct plugin_file_handle *libkrb5;
+    heim_error_code (*heim_init_context)(heim_context *);
+    void (*heim_free_context)(heim_context);
+    void (*heim_free_principal)(heim_context, Principal *);
+    heim_error_code (*heim_free_addresses)(heim_context, HostAddresses *);
+    void (*heim_pac_free)(heim_context, heim_pac);
+    heim_error_code (*heim_pac_parse)(heim_context, const void *,
+                                      size_t, heim_pac *);
+    heim_error_code (*heim_pac_verify)(heim_context, const heim_pac,
+                                       time_t, const Principal *,
+                                       const EncryptionKey *,
+                                       const EncryptionKey *);
+    heim_error_code (*heim_pac_sign)(heim_context, heim_pac,
+                                     time_t, Principal *,
+                                     const EncryptionKey *,
+                                     const EncryptionKey *,
+                                     heim_octet_string *);
+
+    /* libhdb APIs */
+    struct plugin_file_handle *libhdb;
+    heim_error_code (*hdb_create)(heim_context, HDB **, const char *);
+    heim_error_code (*hdb_seal_key)(heim_context, HDB *, Key *);
+    heim_error_code (*hdb_unseal_key)(heim_context, HDB *, Key *);
+    heim_error_code (*hdb_set_master_key)(heim_context, HDB *, EncryptionKey *);
+    void (*hdb_free_entry)(heim_context, hdb_entry_ex *);
+
+    /* widdc SPIs */
+    struct plugin_dir_handle windc_plugins;
+    krb5plugin_windc_ftable *windc;
+    void *windc_ctx;
+} kh_db_context;
+
+#define KH_DB_CONTEXT(_context)    \
+    ((kh_db_context *)(_context)->dal_handle->db_context)
+
+#define KH_DB_ENTRY(_entry)         \
+    ((hdb_entry_ex *)(_entry)->e_data)
+
+/* kdb_hdb.c */
+
+krb5_error_code
+kh_map_error(heim_error_code code);
+
+krb5_error_code
+kh_get_principal(krb5_context context,
+                 kh_db_context *kh,
+                 krb5_const_principal princ,
+                 unsigned int hflags,
+                 krb5_db_entry *kentry);
+
+void
+kh_kdb_free_entry(krb5_context context,
+                  kh_db_context *kh,
+                  krb5_db_entry *entry);
+
+krb5_error_code
+kh_decrypt_key(krb5_context context,
+               kh_db_context *kh,
+               const krb5_key_data *key_data,
+               krb5_keyblock *dbkey,
+               krb5_keysalt *keysalt);
+
+void
+kh_hdb_free_entry(krb5_context context,
+                  kh_db_context *kh,
+                  hdb_entry_ex *entry);
+
+/* kdb_marshal.c */
+
+#define KH_MARSHAL_KEY(_kkey, _hkey)        do {        \
+    (_hkey)->keytype            = (_kkey)->enctype;     \
+    (_hkey)->keyvalue.data      = (_kkey)->contents;    \
+    (_hkey)->keyvalue.length    = (_kkey)->length;      \
+    } while (0)
+
+krb5_error_code
+kh_marshal_Principal(krb5_context context,
+                     krb5_const_principal kprinc,
+                     Principal **out_hprinc);
+
+krb5_error_code
+kh_unmarshal_Principal(krb5_context context,
+                       const Principal *hprinc,
+                       krb5_principal *out_kprinc);
+
+void
+kh_free_Principal(krb5_context context,
+                  Principal *principal);
+
+void
+kh_free_Event(krb5_context context,
+              Event *event);
+
+void
+kh_free_HostAddresses(krb5_context context,
+                      HostAddresses *addrs);
+
+krb5_error_code
+kh_unmarshal_hdb_entry(krb5_context context,
+                       const hdb_entry *hentry,
+                       krb5_db_entry *kentry);
+
+krb5_error_code
+kh_marshal_hdb_entry(krb5_context context,
+                     const krb5_db_entry *kentry,
+                     hdb_entry *hentry);
+
+/* kdb_windc.c */
+
+krb5_error_code
+kh_db_sign_auth_data(krb5_context context,
+                     unsigned int method,
+                     const krb5_data *req_data,
+                     krb5_data *rep_data);
+
+krb5_error_code
+kh_db_check_policy_as(krb5_context context,
+                      unsigned int method,
+                      const krb5_data *req_data,
+                      krb5_data *rep_data);
+
+krb5_error_code
+kh_hdb_windc_init(krb5_context context,
+                  const char *libdir,
+                  kh_db_context *kh);
+
+#endif /* KRB5_KDB_HDB_H */
+
diff --git a/src/plugins/kdb/hdb/kdb_marshal.c b/src/plugins/kdb/hdb/kdb_marshal.c
new file mode 100644 (file)
index 0000000..17bbdc8
--- /dev/null
@@ -0,0 +1,810 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_marshal.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+void
+kh_free_Principal(krb5_context context,
+                  Principal *principal)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    if (principal != NULL)
+        (*kh->heim_free_principal)(kh->hcontext, principal);
+}
+
+void
+kh_free_Event(krb5_context context,
+              Event *event)
+{
+    kh_free_Principal(context, event->principal);
+}
+
+void
+kh_free_HostAddresses(krb5_context context,
+                      HostAddresses *addrs)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    if (addrs != NULL)
+        (*kh->heim_free_addresses)(kh->hcontext, addrs);
+}
+
+#if 0
+static krb5_error_code
+kh_marshal_octet_string(krb5_context context,
+                         const krb5_data *in_data,
+                         heim_octet_string *out_data)
+{
+    out_data->data = malloc(in_data->length);
+    if (out_data->data == NULL)
+        return ENOMEM;
+
+    memcpy(out_data->data, in_data->data, in_data->length);
+
+    out_data->length = in_data->length;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_octet_string_contents(krb5_context context,
+                                   const heim_octet_string *in_data,
+                                   krb5_data *out_data)
+{
+    out_data->magic = KV5M_DATA;
+    out_data->data = malloc(in_data->length);
+    if (out_data->data == NULL)
+        return ENOMEM;
+
+    memcpy(out_data->data, in_data->data, in_data->length);
+
+    out_data->length = in_data->length;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_octet_string(krb5_context context,
+                          heim_octet_string *in_data,
+                          krb5_data **out_data)
+{
+    krb5_error_code code;
+
+    *out_data = k5alloc(sizeof(krb5_data), &code);
+    if (code != 0) 
+        return code;
+
+    code = kh_unmarshal_octet_string_contents(context, in_data, *out_data);
+    if (code != 0) {
+        free(*out_data);
+        *out_data = NULL;
+        return code;
+    }
+
+    return 0;
+}
+#endif
+
+static krb5_error_code
+kh_marshal_general_string(krb5_context context,
+                          const krb5_data *in_data,
+                          heim_general_string *out_str)
+{
+    *out_str = malloc(in_data->length + 1);
+    if (*out_str == NULL)
+        return ENOMEM;
+
+    memcpy(*out_str, in_data->data, in_data->length);
+    (*out_str)[in_data->length] = '\0';
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_general_string_contents(krb5_context context,
+                                     const heim_general_string in_str,
+                                     krb5_data *out_data)
+{
+    out_data->magic = KV5M_DATA;
+    out_data->length = strlen(in_str);
+    out_data->data = malloc(out_data->length);
+    if (out_data->data == NULL)
+        return ENOMEM;
+
+    memcpy(out_data->data, in_str, out_data->length);
+    return 0;
+}
+
+#if 0
+static krb5_error_code
+kh_unmarshal_general_string(krb5_context context,
+                            const heim_general_string in_str,
+                            krb5_data **out_data)
+{
+    krb5_error_code code;
+
+    *out_data = k5alloc(sizeof(krb5_data), &code);
+    if (code != 0)
+        return code;
+
+    code = kh_unmarshal_general_string_contents(context, in_str, *out_data);
+    if (code != 0) {
+        free(*out_data);
+        *out_data = NULL;
+        return code;
+    }
+
+    return 0;
+}
+#endif
+
+krb5_error_code
+kh_marshal_Principal(krb5_context context,
+                     krb5_const_principal kprinc,
+                     Principal **out_hprinc)
+{
+    krb5_error_code code;
+    Principal *hprinc;
+    int i;
+
+    hprinc = k5alloc(sizeof(*hprinc), &code);
+    if (code != 0)
+        return code;
+
+    hprinc->name.name_type = kprinc->type;
+    hprinc->name.name_string.val = k5alloc(kprinc->length *
+                                           sizeof(heim_general_string),
+                                           &code);
+    if (code != 0) {
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+    for (i = 0; i < kprinc->length; i++) {
+        code = kh_marshal_general_string(context, &kprinc->data[i],
+                                          &hprinc->name.name_string.val[i]);
+        if (code != 0) {
+            kh_free_Principal(context, hprinc);
+            return code;
+        }
+        hprinc->name.name_string.len++;
+    }
+    code = kh_marshal_general_string(context, &kprinc->realm, &hprinc->realm);
+    if (code != 0) {
+        kh_free_Principal(context, hprinc);
+        return code;
+    }
+
+    *out_hprinc = hprinc;
+
+    return 0;
+}
+
+krb5_error_code
+kh_unmarshal_Principal(krb5_context context,
+                       const Principal *hprinc,
+                       krb5_principal *out_kprinc)
+{
+    krb5_error_code code;
+    krb5_principal kprinc;
+    unsigned int i;
+
+    kprinc = k5alloc(sizeof(*kprinc), &code);
+    if (code != 0)
+        return code;
+
+    kprinc->magic = KV5M_PRINCIPAL;
+    kprinc->type = hprinc->name.name_type;
+    kprinc->data = k5alloc(hprinc->name.name_string.len * sizeof(krb5_data),
+                           &code);
+    if (code != 0) {
+        krb5_free_principal(context, kprinc);
+        return code;
+    }
+    for (i = 0; i < hprinc->name.name_string.len; i++) {
+        code = kh_unmarshal_general_string_contents(context,
+                                                    hprinc->name.name_string.val[i],
+                                                    &kprinc->data[i]);
+        if (code != 0) {
+            krb5_free_principal(context, kprinc);
+            return code;
+        }
+        kprinc->length++;
+    }
+    code = kh_unmarshal_general_string_contents(context,
+                                                hprinc->realm,
+                                                &kprinc->realm);
+    if (code != 0) {
+        krb5_free_principal(context, kprinc);
+        return code;
+    }
+
+    *out_kprinc = kprinc;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_marshal_Event(krb5_context context,
+                 const krb5_db_entry *kentry,
+                 Event *event)
+{
+    krb5_error_code code;
+    krb5_timestamp mod_time = 0;
+    krb5_principal mod_princ = NULL;
+
+    memset(event, 0, sizeof(*event));
+
+    code = krb5_dbe_lookup_mod_princ_data(context, (krb5_db_entry *)kentry,
+                                          &mod_time, &mod_princ);
+    if (code != 0)
+        return code;
+
+    event->time = mod_time;
+
+    if (mod_princ != NULL) {
+        code = kh_marshal_Principal(context, mod_princ, &event->principal);
+        if (code != 0) {
+            krb5_free_principal(context, mod_princ);
+            return code;
+        }
+    }
+
+    krb5_free_principal(context, mod_princ);
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_Event(krb5_context context,
+                   const Event *event,
+                   krb5_db_entry *kentry)
+{
+    krb5_error_code code;
+    krb5_principal princ = NULL;
+
+    if (event->principal != NULL) {
+        code = kh_unmarshal_Principal(context, event->principal, &princ);
+        if (code != 0)
+            return code;
+    }
+
+    code = krb5_dbe_update_mod_princ_data(context, kentry,
+                                          event->time, princ);
+
+    krb5_free_principal(context, princ);
+
+    return code;
+}
+
+static krb5_error_code
+kh_marshal_HDBFlags(krb5_context context,
+                    krb5_flags kflags,
+                    HDBFlags *hflags)
+{
+    memset(hflags, 0, sizeof(*hflags));
+
+    if (kflags & KRB5_KDB_DISALLOW_TGT_BASED)
+        hflags->initial = 1;
+    if ((kflags & KRB5_KDB_DISALLOW_FORWARDABLE) == 0)
+        hflags->forwardable = 1;
+    if ((kflags & KRB5_KDB_DISALLOW_PROXIABLE) == 0)
+        hflags->proxiable = 1;
+    if ((kflags & KRB5_KDB_DISALLOW_RENEWABLE) == 0)
+        hflags->renewable = 1;
+    if ((kflags & KRB5_KDB_DISALLOW_POSTDATED) == 0)
+        hflags->postdate = 1;
+    if ((kflags & KRB5_KDB_DISALLOW_SVR) == 0)
+        hflags->server = 1;
+    hflags->client = 1;
+    if (kflags & KRB5_KDB_DISALLOW_ALL_TIX)
+        hflags->invalid = 1;
+    if (kflags & KRB5_KDB_REQUIRES_PRE_AUTH)
+        hflags->require_preauth = 1;
+    if (kflags & KRB5_KDB_PWCHANGE_SERVICE)
+        hflags->change_pw = 1;
+    if (kflags & KRB5_KDB_REQUIRES_HW_AUTH)
+        hflags->require_hwauth = 1;
+    if (kflags & KRB5_KDB_OK_AS_DELEGATE)
+        hflags->ok_as_delegate = 1;
+    /* hflags->user_to_user */
+    /* hflags->immutable */
+    if (kflags & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)
+        hflags->trusted_for_delegation = 1;
+    /* hflags->allow_kerberos4 */
+    /* hflags->allow_digest */
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_HDBFlags(krb5_context context,
+                      HDBFlags hflags,
+                      krb5_flags *kflags)
+{
+    *kflags = 0;
+
+    if (hflags.initial)
+        *kflags |= KRB5_KDB_DISALLOW_TGT_BASED;
+    if (!hflags.forwardable)
+        *kflags |= KRB5_KDB_DISALLOW_FORWARDABLE;
+    if (!hflags.proxiable)
+        *kflags |= KRB5_KDB_DISALLOW_PROXIABLE;
+    if (!hflags.renewable)
+        *kflags |= KRB5_KDB_DISALLOW_RENEWABLE;
+    if (!hflags.postdate)
+        *kflags |= KRB5_KDB_DISALLOW_POSTDATED;
+    if (!hflags.server)
+        *kflags |= KRB5_KDB_DISALLOW_SVR;
+    if (hflags.client)
+        ;
+    if (hflags.invalid)
+        *kflags |= KRB5_KDB_DISALLOW_ALL_TIX;
+    if (hflags.require_preauth)
+        *kflags |= KRB5_KDB_REQUIRES_PRE_AUTH;
+    if (hflags.change_pw)
+        *kflags |= KRB5_KDB_PWCHANGE_SERVICE;
+    if (hflags.require_hwauth)
+        *kflags |= KRB5_KDB_REQUIRES_HW_AUTH;
+    if (hflags.ok_as_delegate)
+        *kflags |= KRB5_KDB_OK_AS_DELEGATE;
+    if (hflags.user_to_user)
+        ;
+    if (hflags.immutable)
+        ;
+    if (hflags.trusted_for_delegation)
+        *kflags |= KRB5_KDB_OK_TO_AUTH_AS_DELEGATE;
+    if (hflags.allow_kerberos4)
+        ;
+    if (hflags.allow_digest)
+        ;
+    return 0;
+}
+
+static krb5_error_code
+kh_marshal_Key(krb5_context context,
+               const krb5_key_data *kkey,
+               Key *hkey)
+{
+    krb5_error_code code;
+
+    memset(hkey, 0, sizeof(*hkey));
+
+    hkey->key.keytype = kkey->key_data_type[0];
+    hkey->key.keyvalue.data = k5alloc(kkey->key_data_length[0], &code);
+    if (code != 0)
+        return code;
+    memcpy(hkey->key.keyvalue.data, kkey->key_data_contents[0],
+           kkey->key_data_length[0]);
+    hkey->key.keyvalue.length = kkey->key_data_length[0];
+
+    if (kkey->key_data_contents[1] != NULL) {
+        Salt *salt;
+
+        salt = k5alloc(sizeof(*salt), &code);
+        if (code != 0)
+            goto cleanup;
+
+        switch (kkey->key_data_type[1]) {
+        case KRB5_KDB_SALTTYPE_NORMAL:
+            salt->type = hdb_pw_salt;
+            break;
+        case KRB5_KDB_SALTTYPE_AFS3:
+            salt->type = hdb_afs3_salt;
+            break;
+        default:
+            salt->type = 0;
+            break;
+        }
+
+        salt->salt.data = k5alloc(kkey->key_data_length[1], &code);
+        if (code != 0) {
+            free(salt);
+            goto cleanup;
+        }
+        memcpy(salt->salt.data, kkey->key_data_contents[1],
+               kkey->key_data_length[1]);
+        salt->salt.length = kkey->key_data_length[1];
+
+        hkey->salt = salt;
+    }
+
+cleanup:
+    if (code != 0 && hkey->key.keyvalue.data != NULL)
+        free(hkey->key.keyvalue.data);
+
+    return code;
+}
+
+static krb5_error_code
+kh_unmarshal_Key(krb5_context context,
+                 const hdb_entry *hentry,
+                 const Key *hkey,
+                 krb5_key_data *kkey)
+{
+    memset(kkey, 0, sizeof(*kkey));
+
+    kkey->key_data_ver = KRB5_KDB_V1_KEY_DATA_ARRAY;
+    kkey->key_data_kvno = hentry->kvno;
+
+    kkey->key_data_type[0] = hkey->key.keytype;
+    kkey->key_data_contents[0] = malloc(hkey->key.keyvalue.length);
+    if (kkey->key_data_contents[0] == NULL)
+        return ENOMEM;
+
+    memcpy(kkey->key_data_contents[0], hkey->key.keyvalue.data,
+           hkey->key.keyvalue.length);
+    kkey->key_data_length[0] = hkey->key.keyvalue.length;
+
+    if (hkey->salt != NULL) {
+        switch (hkey->salt->type) {
+        case hdb_pw_salt:
+            kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
+            break;
+        case hdb_afs3_salt:
+            kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_AFS3;
+            break;
+        default:
+            kkey->key_data_type[1] = KRB5_KDB_SALTTYPE_SPECIAL;
+            break;
+        }
+
+        kkey->key_data_contents[1] = malloc(hkey->salt->salt.length);
+        if (kkey->key_data_contents[1] == NULL) {
+            memset(kkey->key_data_contents[0], 0, kkey->key_data_length[0]);
+            free(kkey->key_data_contents[0]);
+            return ENOMEM;
+        }
+        memcpy(kkey->key_data_contents[1], hkey->salt->salt.data,
+               hkey->salt->salt.length);
+        kkey->key_data_length[1] = hkey->salt->salt.length;
+    }
+
+    return 0;
+}
+
+/*
+ * Extension marshalers
+ */
+
+static krb5_error_code
+kh_marshal_HDB_extension_data_last_pw_change(krb5_context context,
+                                             const krb5_db_entry *kentry,
+                                             HDB_extension *hext)
+{
+    krb5_timestamp stamp;
+    krb5_error_code code;
+
+    code = krb5_dbe_lookup_last_pwd_change(context,
+                                           (krb5_db_entry *)kentry, &stamp);
+    if (code != 0)
+        return code;
+
+    hext->data.u.last_pw_change = stamp;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extension_data_last_pw_change(krb5_context context,
+                                               HDB_extension *hext,
+                                               krb5_db_entry *kentry)
+{
+    return krb5_dbe_update_last_pwd_change(context, kentry,
+                                           hext->data.u.last_pw_change);
+}
+
+typedef krb5_error_code (*kh_hdb_marshal_extension_fn)(krb5_context,
+                                                       const krb5_db_entry *,
+                                                       HDB_extension *);
+
+typedef krb5_error_code (*kh_hdb_unmarshal_extension_fn)(krb5_context,
+                                                         HDB_extension *,
+                                                         krb5_db_entry *);
+
+struct {
+    kh_hdb_marshal_extension_fn marshal;
+    kh_hdb_unmarshal_extension_fn unmarshal;
+} kh_hdb_extension_vtable[] = {
+    { NULL, NULL }, /* choice_HDB_extension_data_asn1_ellipsis */
+    { NULL, NULL }, /* choice_HDB_extension_data_pkinit_acl */
+    { NULL, NULL }, /* choice_HDB_extension_data_pkinit_cert_hash */
+    { NULL, NULL }, /* choice_HDB_extension_data_allowed_to_delegate_to */
+    { NULL, NULL }, /* choice_HDB_extension_data_lm_owf */
+    { NULL, NULL }, /* choice_HDB_extension_data_password */
+    { NULL, NULL }, /* choice_HDB_extension_data_aliases */
+    { kh_marshal_HDB_extension_data_last_pw_change,
+      kh_unmarshal_HDB_extension_data_last_pw_change }
+};
+
+static const size_t kh_hdb_extension_count =
+    sizeof(kh_hdb_extension_vtable) / sizeof(kh_hdb_extension_vtable[0]);
+
+static krb5_error_code
+kh_marshal_HDB_extension(krb5_context context,
+                         const krb5_db_entry *kentry,
+                         HDB_extension *hext)
+{
+    kh_hdb_marshal_extension_fn marshal = NULL;
+
+    if (hext->data.element < kh_hdb_extension_count)
+        marshal = kh_hdb_extension_vtable[hext->data.element].marshal;
+
+    if (marshal == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    return (*marshal)(context, kentry, hext);
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extension(krb5_context context,
+                           HDB_extension *hext,
+                           krb5_db_entry *kentry)
+{
+    kh_hdb_unmarshal_extension_fn unmarshal = NULL;
+
+    if (hext->data.element < kh_hdb_extension_count)
+        unmarshal = kh_hdb_extension_vtable[hext->data.element].unmarshal;
+
+    if (unmarshal == NULL)
+        return hext->mandatory ? KRB5_KDB_DBTYPE_NOSUP : 0;
+
+    return (*unmarshal)(context, hext, kentry);
+}
+
+static krb5_error_code
+kh_marshal_HDB_extensions(krb5_context context,
+                          const krb5_db_entry *kentry,
+                          HDB_extensions *hexts)
+{
+    unsigned int i;
+    krb5_error_code code;
+
+    hexts->val = k5alloc(kh_hdb_extension_count * sizeof(HDB_extension), &code);
+    if (code != 0)
+        return code;
+
+    hexts->len = 0;
+
+    for (i = 0; i < kh_hdb_extension_count; i++) {
+        HDB_extension *hext = &hexts->val[hexts->len];
+
+        hext->data.element = i;
+
+        code = kh_marshal_HDB_extension(context, kentry, hext);
+        if (code == KRB5_KDB_DBTYPE_NOSUP)
+            continue;
+        else if (code != 0)
+            break;
+
+        hexts->len++;
+    }
+
+    return code;
+}
+
+static krb5_error_code
+kh_unmarshal_HDB_extensions(krb5_context context,
+                            HDB_extensions *hexts,
+                            krb5_db_entry *kentry)
+{
+    unsigned int i;
+    krb5_error_code code = 0;
+
+    for (i = 0; i < hexts->len; i++) {
+        code = kh_unmarshal_HDB_extension(context, &hexts->val[i], kentry);
+        if (code != 0)
+            break;
+    }
+
+    return code;
+}
+
+krb5_error_code
+kh_marshal_hdb_entry(krb5_context context,
+                     const krb5_db_entry *kentry,
+                     hdb_entry *hentry)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    krb5_int16 kvno = 0;
+    int i;
+
+    memset(hentry, 0, sizeof(*hentry));
+
+    code = kh_marshal_Principal(context, kentry->princ, &hentry->principal);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_marshal_HDBFlags(context, kentry->attributes, &hentry->flags);
+    if (code != 0)
+        goto cleanup;
+
+    if (kentry->expiration) {
+        hentry->valid_end = k5alloc(sizeof(KerberosTime), &code);
+        if (code != 0)
+            goto cleanup;
+        *(hentry->valid_end) = kentry->expiration;
+    }
+    if (kentry->pw_expiration) {
+        hentry->pw_end = k5alloc(sizeof(KerberosTime), &code);
+        if (code != 0)
+            goto cleanup;
+        *(hentry->pw_end) = kentry->pw_expiration;
+    }
+    if (kentry->max_life) {
+        hentry->max_life = k5alloc(sizeof(unsigned int), &code);
+        if (code != 0)
+            goto cleanup;
+        *(hentry->max_life) = kentry->max_life;
+    }
+    if (kentry->max_renewable_life) {
+        hentry->max_renew = k5alloc(sizeof(unsigned int), &code);
+        if (code != 0)
+            goto cleanup;
+        *(hentry->max_renew) = kentry->max_renewable_life;
+    }
+
+    /* last_success */
+    /* last_failed */
+    /* fail_auth_count */
+    /* n_tl_data */
+
+    if ((kentry->attributes & KRB5_KDB_NEW_PRINC) == 0) {
+        hentry->modified_by = k5alloc(sizeof(Event), &code);
+        if (code != 0)
+            goto cleanup;
+        code = kh_marshal_Event(context, kentry, hentry->modified_by);
+    } else {
+        code = kh_marshal_Event(context, kentry, &hentry->created_by);
+    }
+    if (code != 0)
+        goto cleanup;
+
+    hentry->extensions = k5alloc(sizeof(HDB_extensions), &code);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_marshal_HDB_extensions(context, kentry, hentry->extensions);
+    if (code != 0)
+        goto cleanup;
+
+    hentry->keys.len = 0;
+    hentry->keys.val = k5alloc(kentry->n_key_data * sizeof(Key), &code);
+    if (code != 0)
+        goto cleanup;
+
+    for (i = 0; i < kentry->n_key_data; i++) {
+        code = kh_marshal_Key(context,
+                              &kentry->key_data[i],
+                              &hentry->keys.val[hentry->keys.len]);
+        if (code != 0)
+            goto cleanup;
+
+        if (kentry->key_data[i].key_data_kvno > kvno)
+            kvno = kentry->key_data[i].key_data_kvno;
+
+        hentry->keys.len++;
+    }
+
+    hentry->kvno = kvno;
+
+cleanup:
+    if (code != 0) {
+        hdb_entry_ex hext;
+
+        hext.ctx = NULL;
+        hext.entry = *hentry;
+        hext.free_entry = NULL;
+
+        kh_hdb_free_entry(context, kh, &hext);
+        memset(hentry, 0, sizeof(*hentry));
+    }
+
+    return code;
+}
+
+krb5_error_code
+kh_unmarshal_hdb_entry(krb5_context context,
+                       const hdb_entry *hentry,
+                       krb5_db_entry *kentry)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    krb5_error_code code;
+    unsigned int i;
+
+    memset(kentry, 0, sizeof(*kentry));
+
+    kentry->magic = KRB5_KDB_MAGIC_NUMBER;
+    kentry->len = KRB5_KDB_V1_BASE_LENGTH;
+
+    code = kh_unmarshal_Principal(context, hentry->principal, &kentry->princ);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_unmarshal_HDBFlags(context, hentry->flags, &kentry->attributes);
+    if (code != 0)
+        goto cleanup;
+
+    if (hentry->max_life != NULL)
+        kentry->max_life = *(hentry->max_life);
+    if (hentry->max_renew != NULL)
+        kentry->max_renewable_life = *(hentry->max_renew);
+    if (hentry->valid_end != NULL)
+        kentry->expiration = *(hentry->valid_end);
+    if (hentry->pw_end != NULL)
+        kentry->pw_expiration = *(hentry->pw_end);
+
+    /* last_success */
+    /* last_failed */
+    /* fail_auth_count */
+    /* n_tl_data */
+
+    code = kh_unmarshal_Event(context,
+                              hentry->modified_by ? hentry->modified_by :
+                                                    &hentry->created_by,
+                              kentry);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_unmarshal_HDB_extensions(context, hentry->extensions, kentry);
+    if (code != 0)
+        goto cleanup;
+
+    kentry->key_data = k5alloc(hentry->keys.len * sizeof(krb5_key_data), &code);
+    if (code != 0)
+        goto cleanup;
+
+    for (i = 0; i < hentry->keys.len; i++) {
+        code = kh_unmarshal_Key(context, hentry,
+                                &hentry->keys.val[i],
+                                &kentry->key_data[i]);
+        if (code != 0)
+            goto cleanup;
+
+        kentry->n_key_data++;
+    }
+
+cleanup:
+    if (code != 0)
+        kh_kdb_free_entry(context, kh, kentry);
+
+    return code;
+}
+
diff --git a/src/plugins/kdb/hdb/kdb_windc.c b/src/plugins/kdb/hdb/kdb_windc.c
new file mode 100644 (file)
index 0000000..a419d29
--- /dev/null
@@ -0,0 +1,615 @@
+/* -*- mode: c; indent-tabs-mode: nil -*- */
+/*
+ * plugins/kdb/hdb/kdb_windc.c
+ *
+ * Copyright 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+#include "kdb5.h"
+#include "kdb_hdb.h"
+
+/*
+ * WinDC helpers
+ */
+
+static krb5_error_code
+kh_windc_pac_generate(krb5_context context,
+                      kh_db_context *kh,
+                      struct hdb_entry_ex *hentry,
+                      heim_pac *pac)
+{
+    if (kh->windc == NULL || kh->windc->pac_generate == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    return kh_map_error((*kh->windc->pac_generate)(kh->windc_ctx,
+                                                   kh->hcontext,
+                                                   hentry,
+                                                   pac));
+}
+
+static krb5_error_code
+kh_windc_pac_verify(krb5_context context,
+                    kh_db_context *kh,
+                    const Principal *principal,
+                    struct hdb_entry_ex *client,
+                    struct hdb_entry_ex *server,
+                    heim_pac *pac)
+{
+    if (kh->windc == NULL || kh->windc->pac_verify == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    return kh_map_error((*kh->windc->pac_verify)(kh->windc_ctx,
+                                                 kh->hcontext,
+                                                 principal,
+                                                 client,
+                                                 server,
+                                                 pac));
+}
+
+static krb5_error_code
+kh_windc_client_access(krb5_context context,
+                       kh_db_context *kh,
+                       struct hdb_entry_ex *client,
+                       KDC_REQ *req,
+                       heim_octet_string *e_data)
+{
+    if (kh->windc == NULL || kh->windc->client_access == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP;
+
+    return kh_map_error((*kh->windc->client_access)(kh->windc_ctx,
+                                                    kh->hcontext,
+                                                    client,
+                                                    req,
+                                                    e_data));
+}
+
+static void
+kh_pac_free(krb5_context context, heim_pac pac)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    assert(kh->heim_pac_free != NULL);
+
+    if (pac != NULL)
+        (*kh->heim_pac_free)(kh->hcontext, pac);
+}
+
+static krb5_error_code
+kh_pac_parse(krb5_context context,
+             const void *data,
+             size_t len,
+             heim_pac *pac)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    return kh_map_error((*kh->heim_pac_parse)(kh->hcontext,
+                                              data,
+                                              len,
+                                              pac));
+}
+
+static krb5_error_code
+kh_pac_verify(krb5_context context,
+              const heim_pac pac,
+              time_t authtime,
+              const Principal *princ,
+              const EncryptionKey *server,
+              const EncryptionKey *krbtgt)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    assert(kh->heim_pac_verify != NULL);
+
+    return kh_map_error((*kh->heim_pac_verify)(kh->hcontext,
+                                               pac,
+                                               authtime,
+                                               princ,
+                                               server,
+                                               krbtgt));
+}
+
+static krb5_error_code
+kh_pac_sign(krb5_context context,
+            heim_pac pac,
+            time_t authtime,
+            Principal *princ,
+            const EncryptionKey *server,
+            const EncryptionKey *krbtgt,
+            heim_octet_string *data)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+
+    assert(kh->heim_pac_sign != NULL);
+
+    return kh_map_error((*kh->heim_pac_sign)(kh->hcontext,
+                                             pac,
+                                             authtime,
+                                             princ,
+                                             server,
+                                             krbtgt,
+                                             data));
+}
+
+/*
+ * Get local TGS key for the realm of the supplied principal.
+ */
+static krb5_error_code
+kh_get_tgs_key(krb5_context context,
+               kh_db_context *kh,
+               const krb5_principal princ,
+               krb5_keyblock *krbtgt_keyblock)
+{
+    krb5_error_code code;
+    krb5_principal tgsname = NULL;
+    krb5_key_data *krbtgt_key = NULL;
+    krb5_db_entry krbtgt;
+
+    memset(&krbtgt, 0, sizeof(krbtgt));
+    krbtgt_keyblock->contents = NULL;
+
+    code = krb5_build_principal_ext(context,
+                                    &tgsname,
+                                    princ->realm.length,
+                                    princ->realm.data,
+                                    KRB5_TGS_NAME_SIZE,
+                                    KRB5_TGS_NAME,
+                                    princ->realm.length,
+                                    princ->realm.data,
+                                    0);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_get_principal(context, kh, tgsname, HDB_F_GET_KRBTGT, &krbtgt);
+    if (code != 0)
+        goto cleanup;
+
+    code = krb5_dbe_find_enctype(context,
+                                 &krbtgt,
+                                 -1,    /* ignore enctype */
+                                 -1,    /* ignore salttype */
+                                 0,     /* highest kvno */
+                                 &krbtgt_key);
+    if (code != 0)
+        goto cleanup;
+    else if (krbtgt_key == NULL) {
+        code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+        goto cleanup;
+    }
+
+    code = kh_decrypt_key(context,
+                          KH_DB_CONTEXT(context),
+                          krbtgt_key,
+                          krbtgt_keyblock,
+                          NULL);
+    if (code != 0)
+        goto cleanup;
+
+cleanup:
+    kh_kdb_free_entry(context, KH_DB_CONTEXT(context), &krbtgt);
+    krb5_free_principal(context, tgsname);
+
+    return code;
+}
+
+krb5_error_code
+kh_db_sign_auth_data(krb5_context context,
+                     unsigned int method,
+                     const krb5_data *req_data,
+                     krb5_data *rep_data)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    kdb_sign_auth_data_req *req = (kdb_sign_auth_data_req *)req_data->data;
+    kdb_sign_auth_data_rep *rep = (kdb_sign_auth_data_rep *)rep_data->data;
+    heim_pac hpac = NULL;
+    heim_octet_string pac_data;
+    krb5_boolean is_as_req;
+    krb5_error_code code;
+    krb5_authdata **authdata = NULL;
+    Principal *client_hprinc = NULL;
+    EncryptionKey server_hkey;
+    EncryptionKey krbtgt_hkey;
+    krb5_keyblock krbtgt_kkey;
+
+    if (kh->windc == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP; /* short circuit */
+
+    memset(rep, 0, sizeof(*rep));
+    memset(&krbtgt_kkey, 0, sizeof(krbtgt_kkey));
+    pac_data.data = NULL;
+
+    is_as_req = ((req->flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
+
+    /* Prefer canonicalised name from client entry */
+    if (req->client != NULL) {
+        client_hprinc = KH_DB_ENTRY(req->client)->entry.principal;
+    } else {
+        code = kh_marshal_Principal(context, req->client_princ, &client_hprinc);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    KH_MARSHAL_KEY(req->server_key, &server_hkey);
+    KH_MARSHAL_KEY(req->krbtgt_key, &krbtgt_hkey);
+
+    if (!is_as_req) {
+        /* find the existing PAC, if present */
+        code = krb5int_find_authdata(context,
+                                     req->auth_data,
+                                     NULL,
+                                     KRB5_AUTHDATA_WIN2K_PAC,
+                                     &authdata);
+        if (code != 0)
+            goto cleanup;
+    }
+
+    if ((is_as_req && (req->flags & KRB5_KDB_FLAG_INCLUDE_PAC)) ||
+        (authdata == NULL && req->client != NULL)) {
+        code = kh_windc_pac_generate(context, kh,
+                                     KH_DB_ENTRY(req->client), &hpac);
+        if (code != 0)
+            goto cleanup;
+    } else if (authdata != NULL) {
+        assert(authdata[0] != NULL);
+
+        if (authdata[1] != NULL) {
+            code = KRB5KDC_ERR_BADOPTION; /* XXX */
+            goto cleanup;
+        }
+
+        pac_data.data   = authdata[0]->contents;
+        pac_data.length = authdata[0]->length;
+
+        code = kh_pac_parse(context, pac_data.data, pac_data.length, &hpac);
+        if (code != 0)
+            goto cleanup;
+
+        /*
+         * In the constrained delegation case, the PAC is from a service
+         * ticket rather than a TGT; we must verify the server and KDC
+         * signatures to assert that the server did not forge the PAC.
+         */
+        if (req->flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) {
+            code = kh_pac_verify(context, hpac, req->authtime,
+                                 client_hprinc, &server_hkey, &krbtgt_hkey);
+        } else {
+            code = kh_pac_verify(context, hpac, req->authtime,
+                                 client_hprinc, &krbtgt_hkey, NULL);
+        }
+        if (code != 0)
+            goto cleanup;
+
+        code = kh_windc_pac_verify(context, kh, client_hprinc,
+                                   req->client ?
+                                        KH_DB_ENTRY(req->client) : NULL,
+                                   KH_DB_ENTRY(req->server),
+                                   &hpac);
+        if (code != 0)
+            goto cleanup;
+    } else {
+        code = KRB5_KDB_DBTYPE_NOSUP;
+        goto cleanup;
+    }
+
+    /*
+     * In the cross-realm case, krbtgt_hkey refers to the cross-realm
+     * TGS key, so we need to explicitly lookup our TGS key.
+     */
+    if (req->flags & KRB5_KDB_FLAG_CROSS_REALM) {
+        assert(!is_as_req);
+
+        code = kh_get_tgs_key(context, kh, req->server->princ, &krbtgt_kkey);
+        if (code != 0)
+            goto cleanup;
+
+        KH_MARSHAL_KEY(&krbtgt_kkey, &krbtgt_hkey);
+    }
+
+    code = kh_pac_sign(context, hpac, req->authtime, client_hprinc,
+                       &server_hkey, &krbtgt_hkey, &pac_data);
+    if (code != 0)
+        goto cleanup;
+
+    if (authdata == NULL) {
+        authdata = k5alloc(2 * sizeof(krb5_authdata *), &code);
+        if (code != 0)
+            goto cleanup;
+
+        authdata[0] = k5alloc(sizeof(krb5_authdata), &code);
+        if (code != 0)
+            goto cleanup;
+
+        authdata[1] = NULL;
+    } else {
+        free(authdata[0]->contents);
+        authdata[0]->contents = NULL;
+        authdata[0]->length = 0;
+    }
+
+    /* take ownership of pac_data */
+    authdata[0]->magic    = KV5M_AUTHDATA;
+    authdata[0]->ad_type  = KRB5_AUTHDATA_WIN2K_PAC;
+    authdata[0]->contents = pac_data.data;
+    authdata[0]->length   = pac_data.length;
+
+    pac_data.data = NULL;
+
+    code = krb5_encode_authdata_container(context,
+                                          KRB5_AUTHDATA_IF_RELEVANT,
+                                          authdata,
+                                          &rep->auth_data);
+    if (code != 0)
+        goto cleanup;
+                                          
+cleanup:
+    if (req->client == NULL)
+        kh_free_Principal(context, client_hprinc);
+    kh_pac_free(context, hpac);
+    if (pac_data.data != NULL)
+        free(pac_data.data);
+    krb5_free_authdata(context, authdata);
+    krb5_free_keyblock_contents(context, &krbtgt_kkey);
+
+    return code;
+}
+
+static krb5_error_code
+kh_marshal_KDCOptions(krb5_context context,
+                      krb5_flags koptions,
+                      KDCOptions *hoptions)
+{
+    memset(hoptions, 0, sizeof(*hoptions));
+
+    if (koptions & KDC_OPT_FORWARDABLE)
+        hoptions->forwardable = 1;
+    if (koptions & KDC_OPT_FORWARDED)
+        hoptions->forwarded = 1;
+    if (koptions & KDC_OPT_PROXIABLE)
+        hoptions->proxiable = 1;
+    if (koptions & KDC_OPT_PROXY)
+        hoptions->proxy = 1;
+    if (koptions & KDC_OPT_ALLOW_POSTDATE)
+        hoptions->allow_postdate = 1;
+    if (koptions & KDC_OPT_POSTDATED)
+        hoptions->postdated = 1;
+    if (koptions & KDC_OPT_RENEWABLE)
+        hoptions->renewable = 1;
+    if (koptions & KDC_OPT_REQUEST_ANONYMOUS)
+        hoptions->request_anonymous = 1;
+    if (koptions & KDC_OPT_CANONICALIZE)
+        hoptions->canonicalize = 1;
+    if (koptions & KDC_OPT_DISABLE_TRANSITED_CHECK)
+        hoptions->disable_transited_check = 1;
+    if (koptions & KDC_OPT_RENEWABLE_OK)
+        hoptions->renewable_ok = 1;
+    if (koptions & KDC_OPT_ENC_TKT_IN_SKEY)
+        hoptions->enc_tkt_in_skey = 1;
+    if (koptions & KDC_OPT_RENEW)
+        hoptions->renew = 1;
+    if (koptions & KDC_OPT_VALIDATE)
+        hoptions->validate = 1;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_marshall_HostAddress(krb5_context context,
+                        krb5_address *kaddress,
+                        HostAddress *haddress)
+{
+    haddress->addr_type = kaddress->addrtype;
+    haddress->address.data = malloc(kaddress->length);
+    if (haddress->address.data == NULL)
+        return ENOMEM;
+
+    memcpy(haddress->address.data, kaddress->contents, kaddress->length);
+    haddress->address.length = kaddress->length;
+
+    return 0;
+}
+
+static krb5_error_code
+kh_marshall_HostAddresses(krb5_context context,
+                          krb5_address **kaddresses,
+                          HostAddresses **phaddresses)
+{
+    krb5_error_code code;
+    HostAddresses *haddresses;
+    int i;
+
+    *phaddresses = NULL;
+
+    if (kaddresses == NULL)
+        return 0;
+
+    for (i = 0; kaddresses[i] != NULL; i++)
+        ;
+
+    haddresses = k5alloc(sizeof(*haddresses), &code);
+    if (code != 0)
+        return code;
+
+    haddresses->len = 0;
+    haddresses->val = k5alloc(i * sizeof(HostAddress), &code);
+    if (code != 0)
+        return code;
+
+    for (i = 0; kaddresses[i] != NULL; i++) {
+        code = kh_marshall_HostAddress(context,
+                                       kaddresses[i],
+                                       &haddresses->val[i]);
+        if (code != 0)
+            break;
+
+        haddresses->len++;
+    }
+
+    if (code != 0) {
+        free(haddresses->val);
+        free(haddresses);
+    } else {
+        *phaddresses = haddresses;
+    }
+
+    return code;
+}
+
+krb5_error_code
+kh_db_check_policy_as(krb5_context context,
+                      unsigned int method,
+                      const krb5_data *req_data,
+                      krb5_data *rep_data)
+{
+    kh_db_context *kh = KH_DB_CONTEXT(context);
+    kdb_check_policy_as_req *req = (kdb_check_policy_as_req *)req_data->data;
+    kdb_check_policy_as_rep *rep = (kdb_check_policy_as_rep *)rep_data->data;
+    krb5_error_code code;
+    heim_octet_string e_data;
+    krb5_kdc_req *kkdcreq = req->request;
+    KDC_REQ hkdcreq;
+    Principal *hclient = NULL;
+    Principal *hserver = NULL;
+    time_t from, till, rtime;
+
+    if (kh->windc == NULL)
+        return KRB5_KDB_DBTYPE_NOSUP; /* short circuit */
+
+    memset(&hkdcreq, 0, sizeof(hkdcreq));
+
+    hkdcreq.pvno = KRB5_PVNO;
+    hkdcreq.msg_type = kkdcreq->msg_type;
+    hkdcreq.padata = NULL; /* FIXME */
+    code = kh_marshal_KDCOptions(context,
+                                 kkdcreq->kdc_options,
+                                 &hkdcreq.req_body.kdc_options);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_marshal_Principal(context, kkdcreq->client, &hclient);
+    if (code != 0)
+        goto cleanup;
+
+    code = kh_marshal_Principal(context, kkdcreq->server, &hserver);
+    if (code != 0)
+        goto cleanup;
+
+    hkdcreq.req_body.cname = &hclient->name;
+    hkdcreq.req_body.realm = hserver->realm;
+    hkdcreq.req_body.sname = &hserver->name;
+
+    from  = kkdcreq->from;  hkdcreq.req_body.from = &from;
+    till  = kkdcreq->till;  hkdcreq.req_body.till = &till;
+    rtime = kkdcreq->rtime; hkdcreq.req_body.rtime = &rtime;
+
+    hkdcreq.req_body.nonce     = kkdcreq->nonce;
+    hkdcreq.req_body.etype.len = kkdcreq->nktypes;
+    hkdcreq.req_body.etype.val = kkdcreq->ktype;
+
+    code = kh_marshall_HostAddresses(context,
+                                     kkdcreq->addresses,
+                                     &hkdcreq.req_body.addresses);
+    if (code != 0)
+        goto cleanup;
+
+    /* FIXME hkdcreq.req_body.enc_authorization_data */
+    /* FIXME hkdcreq.req_body.additional_tickets */
+
+    code = kh_windc_client_access(context, kh,
+                                  KH_DB_ENTRY(req->client),
+                                  &hkdcreq, &e_data);
+
+    rep->e_data.data   = e_data.data;
+    rep->e_data.length = e_data.length;
+
+cleanup:
+    kh_free_HostAddresses(context, hkdcreq.req_body.addresses);
+    kh_free_Principal(context, hclient);
+    kh_free_Principal(context, hserver);
+
+    return code;
+}
+
+krb5_error_code
+kh_hdb_windc_init(krb5_context context,
+                  const char *libdir,
+                  kh_db_context *kh)
+{
+    krb5_error_code code;
+    const char *objdirs[2];
+    void **tables = NULL;
+    int i;
+
+    memset(&kh->windc_plugins, 0, sizeof(kh->windc_plugins));
+
+    code = PLUGIN_DIR_OPEN(&kh->windc_plugins);
+    if (code != 0)
+        return code;
+
+    objdirs[0] = libdir;
+    objdirs[1] = NULL;
+
+    code = krb5int_open_plugin_dirs(objdirs, NULL,
+                                    &kh->windc_plugins,
+                                    &context->err);
+    if (code != 0)
+        return code;
+
+    code = krb5int_get_plugin_dir_data(&kh->windc_plugins,
+                                       "windc",
+                                       &tables,
+                                       &context->err);
+    if (code != 0)
+        return code;
+
+    code = KRB5_KDB_DBTYPE_NOSUP;
+
+    for (i = 0; tables[i] != NULL; i++) {
+        krb5plugin_windc_ftable *windc = tables[i];
+
+        if (windc->minor_version < KRB5_WINDC_PLUGIN_MINOR)
+            continue;
+
+        code = kh_map_error((*windc->init)(kh->hcontext, &kh->windc_ctx));
+        if (code != 0)
+            continue;
+
+        kh->windc = windc;
+        break;
+    }
+
+    if (tables != NULL)
+        krb5int_free_plugin_dir_data(tables);
+
+    return code;
+}
+
diff --git a/src/plugins/kdb/hdb/windc_plugin.h b/src/plugins/kdb/hdb/windc_plugin.h
new file mode 100644 (file)
index 0000000..7df2a21
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden). 
+ * All rights reserved. 
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions 
+ * are met: 
+ *
+ * 1. Redistributions of source code must retain the above copyright 
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright 
+ *    notice, this list of conditions and the following disclaimer in the 
+ *    documentation and/or other materials provided with the distribution. 
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors 
+ *    may be used to endorse or promote products derived from this software 
+ *    without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE. 
+ */
+
+/* $Id: windc_plugin.h 22693 2008-03-19 08:57:49Z lha $ */
+
+#ifndef HEIMDAL_WINDC_PLUGIN_H
+#define HEIMDAL_WINDC_PLUGIN_H 1
+
+/*
+ * The PAC generate function should allocate a heim_pac using
+ * heim_pac_init and fill in the PAC structure for the principal using
+ * heim_pac_add_buffer.
+ *
+ * The PAC verify function should verify all components in the PAC
+ * using heim_pac_get_types and heim_pac_get_buffer for all types.
+ *
+ * Check client access function check if the client is authorized.
+ */
+
+struct hdb_entry_ex;
+
+struct _heim_pac_data;
+typedef struct _heim_pac_data *heim_pac;
+
+typedef krb5_error_code 
+(*krb5plugin_windc_pac_generate)(void *, heim_context,
+                                struct hdb_entry_ex *, heim_pac *);
+
+typedef krb5_error_code 
+(*krb5plugin_windc_pac_verify)(void *, heim_context,
+                              const Principal *,
+                              struct hdb_entry_ex *, 
+                              struct hdb_entry_ex *,
+                              heim_pac *);
+typedef krb5_error_code 
+(*krb5plugin_windc_client_access)(
+    void *, heim_context, struct hdb_entry_ex *, KDC_REQ *, heim_octet_string *);
+
+
+#define KRB5_WINDC_PLUGIN_MINOR                3
+
+typedef struct krb5plugin_windc_ftable {
+    int                        minor_version;
+    krb5_error_code    (*init)(heim_context, void **);
+    void               (*fini)(void *);
+    krb5plugin_windc_pac_generate      pac_generate;
+    krb5plugin_windc_pac_verify                pac_verify;
+    krb5plugin_windc_client_access     client_access;
+} krb5plugin_windc_ftable;
+
+#endif /* HEIMDAL_WINDC_PLUGIN_H */
+