From: Greg Hudson Date: Tue, 27 Oct 2009 14:24:01 +0000 (+0000) Subject: Heimdal DB bridge plugin for KDC back end X-Git-Tag: krb5-1.8-alpha1~259 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2a5ccaf5a2456e8cfc3f774df307386404bfbec3;p=krb5.git Heimdal DB bridge plugin for KDC back end 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 --- diff --git a/src/configure.in b/src/configure.in index 791215668..5dbdf25ba 100644 --- a/src/configure.in +++ b/src/configure.in @@ -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 diff --git a/src/include/kdb_ext.h b/src/include/kdb_ext.h index f51d46500..ce2de9b1b 100644 --- a/src/include/kdb_ext.h +++ b/src/include/kdb_ext.h @@ -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 { diff --git a/src/include/krb5/authdata_plugin.h b/src/include/krb5/authdata_plugin.h index a5c3e5355..471f01c05 100644 --- a/src/include/krb5/authdata_plugin.h +++ b/src/include/krb5/authdata_plugin.h @@ -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, diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c index 737def8d2..1feb468be 100644 --- a/src/kdc/do_as_req.c +++ b/src/kdc/do_as_req.c @@ -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 */ diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c index f05d25a92..71013b584 100644 --- a/src/kdc/do_tgs_req.c +++ b/src/kdc/do_tgs_req.c @@ -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) { diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c index d598894d2..4ccfcb98b 100644 --- a/src/kdc/kdc_authdata.c +++ b/src/kdc/kdc_authdata.c @@ -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); diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c index 9ad832e8a..b2d8d13bd 100644 --- a/src/kdc/kdc_util.c +++ b/src/kdc/kdc_util.c @@ -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; +} + diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h index 079492250..84319f7b8 100644 --- a/src/kdc/kdc_util.h +++ b/src/kdc/kdc_util.h @@ -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, diff --git a/src/kdc/policy.c b/src/kdc/policy.c index 58b26f73d..d4a70feb6 100644 --- a/src/kdc/policy.c +++ b/src/kdc/policy.c @@ -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; diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c index 7ba89ecd5..40eea875b 100644 --- a/src/lib/kadm5/srv/svr_principal.c +++ b/src/lib/kadm5/srv/svr_principal.c @@ -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; diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index 54c7eda68..8aef88aaf 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -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); diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index a1fd4d5e8..b493df090 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -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 diff --git a/src/plugins/authdata/greet_server/greet_auth.c b/src/plugins/authdata/greet_server/greet_auth.c index 3f1e3d9ea..80a68a86f 100644 --- a/src/plugins/authdata/greet_server/greet_auth.c +++ b/src/plugins/authdata/greet_server/greet_auth.c @@ -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 index 000000000..3f2bccf40 --- /dev/null +++ b/src/plugins/kdb/hdb/Makefile.in @@ -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 index 000000000..f2b7c1119 --- /dev/null +++ b/src/plugins/kdb/hdb/hdb.exports @@ -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 index 000000000..39fbec7e5 --- /dev/null +++ b/src/plugins/kdb/hdb/hdb.h @@ -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 index 000000000..87aa2a4cb --- /dev/null +++ b/src/plugins/kdb/hdb/hdb_asn1.h @@ -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 +#include +#include + +#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 index 000000000..a47797a13 --- /dev/null +++ b/src/plugins/kdb/hdb/hdb_err.h @@ -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 index 000000000..d22489eec --- /dev/null +++ b/src/plugins/kdb/hdb/kdb_hdb.c @@ -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 +#endif + +#include +#include +#include +#include +#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 index 000000000..9cfbef6a3 --- /dev/null +++ b/src/plugins/kdb/hdb/kdb_hdb.h @@ -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 index 000000000..17bbdc808 --- /dev/null +++ b/src/plugins/kdb/hdb/kdb_marshal.c @@ -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 +#endif + +#include +#include +#include +#include +#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 index 000000000..a419d29de --- /dev/null +++ b/src/plugins/kdb/hdb/kdb_windc.c @@ -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 +#endif + +#include +#include +#include +#include +#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 index 000000000..7df2a2147 --- /dev/null +++ b/src/plugins/kdb/hdb/windc_plugin.h @@ -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 */ +