diff options
Diffstat (limited to 'source4/heimdal/kdc')
-rw-r--r-- | source4/heimdal/kdc/digest.c | 542 | ||||
-rw-r--r-- | source4/heimdal/kdc/headers.h | 9 | ||||
-rw-r--r-- | source4/heimdal/kdc/kdc-private.h | 56 | ||||
-rw-r--r-- | source4/heimdal/kdc/kdc.h | 6 | ||||
-rw-r--r-- | source4/heimdal/kdc/kdc_locl.h | 4 | ||||
-rw-r--r-- | source4/heimdal/kdc/kerberos5.c | 273 | ||||
-rw-r--r-- | source4/heimdal/kdc/krb5tgs.c | 338 | ||||
-rw-r--r-- | source4/heimdal/kdc/kx509.c | 370 | ||||
-rwxr-xr-x | source4/heimdal/kdc/pkinit.c | 202 | ||||
-rw-r--r-- | source4/heimdal/kdc/process.c | 11 | ||||
-rw-r--r-- | source4/heimdal/kdc/windc.c | 108 | ||||
-rw-r--r-- | source4/heimdal/kdc/windc_plugin.h | 80 |
12 files changed, 1701 insertions, 298 deletions
diff --git a/source4/heimdal/kdc/digest.c b/source4/heimdal/kdc/digest.c index a5517fb896..2c012a2ead 100644 --- a/source4/heimdal/kdc/digest.c +++ b/source4/heimdal/kdc/digest.c @@ -32,10 +32,112 @@ */ #include "kdc_locl.h" -#include <digest_asn1.h> #include <hex.h> -RCSID("$Id: digest.c,v 1.7 2006/10/22 20:11:44 lha Exp $"); +RCSID("$Id: digest.c,v 1.19 2006/12/28 17:03:51 lha Exp $"); + +#define CHAP_MD5 0x10 +#define DIGEST_MD5 0x08 +#define NTLM_V2 0x04 +#define NTLM_V1_SESSION 0x02 +#define NTLM_V1 0x01 + +const struct units _kdc_digestunits[] = { + {"chap-md5", 1U << 4}, + {"digest-md5", 1U << 3}, + {"ntlm-v2", 1U << 2}, + {"ntlm-v1-session", 1U << 1}, + {"ntlm-v1", 1U << 0}, + {NULL, 0} +}; + + +static krb5_error_code +get_digest_key(krb5_context context, + krb5_kdc_configuration *config, + hdb_entry_ex *server, + krb5_crypto *crypto) +{ + krb5_error_code ret; + krb5_enctype enctype; + Key *key; + + ret = _kdc_get_preferred_key(context, + config, + server, + "digest-service", + &enctype, + &key); + if (ret) + return ret; + return krb5_crypto_init(context, &key->key, 0, crypto); +} + +/* + * + */ + +static char * +get_ntlm_targetname(krb5_context context, + hdb_entry_ex *client) +{ + char *targetname, *p; + + targetname = strdup(krb5_principal_get_realm(context, + client->entry.principal)); + if (targetname == NULL) + return NULL; + + p = strchr(targetname, '.'); + if (p) + *p = '\0'; + + strupr(targetname); + return targetname; +} + +static krb5_error_code +fill_targetinfo(krb5_context context, + char *targetname, + hdb_entry_ex *client, + krb5_data *data) +{ + struct ntlm_targetinfo ti; + krb5_error_code ret; + struct ntlm_buf d; + krb5_principal p; + const char *str; + + memset(&ti, 0, sizeof(ti)); + + ti.domainname = targetname; + p = client->entry.principal; + str = krb5_principal_get_comp_string(context, p, 0); + if (str != NULL && + (strcmp("host", str) == 0 || + strcmp("ftp", str) == 0 || + strcmp("imap", str) == 0 || + strcmp("pop", str) == 0 || + strcmp("smtp", str))) + { + str = krb5_principal_get_comp_string(context, p, 1); + ti.dnsservername = rk_UNCONST(str); + } + + ret = heim_ntlm_encode_targetinfo(&ti, 1, &d); + if (ret) + return ret; + + data->data = d.data; + data->length = d.length; + + return 0; +} + + +/* + * + */ krb5_error_code _kdc_do_digest(krb5_context context, @@ -57,11 +159,13 @@ _kdc_do_digest(krb5_context context, krb5_storage *sp = NULL; Checksum res; hdb_entry_ex *server = NULL, *user = NULL; - char *password = NULL; + hdb_entry_ex *client = NULL; + char *client_name = NULL, *password = NULL; krb5_data serverNonce; if(!config->enable_digest) { - kdc_log(context, config, 0, "Rejected digest request from %s", from); + kdc_log(context, config, 0, + "Rejected digest request (disabled) from %s", from); return KRB5KDC_ERR_POLICY; } @@ -125,6 +229,7 @@ _kdc_do_digest(krb5_context context, krb5_free_principal(context, principal); goto out; } + krb5_clear_error_string(context); ret = _kdc_db_fetch(context, config, principal, HDB_F_GET_SERVER, NULL, &server); @@ -137,12 +242,17 @@ _kdc_do_digest(krb5_context context, /* check the client is allowed to do digest auth */ { krb5_principal principal = NULL; - hdb_entry_ex *client; ret = krb5_ticket_get_client(context, ticket, &principal); if (ret) goto out; + ret = krb5_unparse_name(context, principal, &client_name); + if (ret) { + krb5_free_principal(context, principal); + goto out; + } + ret = _kdc_db_fetch(context, config, principal, HDB_F_GET_CLIENT, NULL, &client); krb5_free_principal(context, principal); @@ -150,13 +260,15 @@ _kdc_do_digest(krb5_context context, goto out; if (client->entry.flags.allow_digest == 0) { + kdc_log(context, config, 0, + "Client %s tried to use digest " + "but is not allowed to", + client_name); krb5_set_error_string(context, "Client is not permitted to use digest"); ret = KRB5KDC_ERR_POLICY; - _kdc_free_ent (context, client); goto out; } - _kdc_free_ent (context, client); } /* unpack request */ @@ -192,6 +304,9 @@ _kdc_do_digest(krb5_context context, goto out; } + kdc_log(context, config, 0, "Valid digest request from %s (%s)", + client_name, from); + /* * Process the inner request */ @@ -289,22 +404,9 @@ _kdc_do_digest(krb5_context context, goto out; } - { - Key *key; - krb5_enctype enctype; - - ret = _kdc_get_preferred_key(context, - config, - server, - "digest-service", - &enctype, - &key); - if (ret) - goto out; - ret = krb5_crypto_init(context, &key->key, 0, &crypto); - if (ret) - goto out; - } + ret = get_digest_key(context, config, server, &crypto); + if (ret) + goto out; ret = krb5_create_checksum(context, crypto, @@ -337,6 +439,9 @@ _kdc_do_digest(krb5_context context, goto out; } + kdc_log(context, config, 0, "Digest %s init request successful from %s", + ireq.u.init.type, from); + break; } case choice_DigestReqInner_digestRequest: { @@ -349,7 +454,11 @@ _kdc_do_digest(krb5_context context, krb5_set_error_string(context, "out of memory"); goto out; } - krb5_store_stringz(sp, ireq.u.digestRequest.type); + ret = krb5_store_stringz(sp, ireq.u.digestRequest.type); + if (ret) { + krb5_clear_error_string(context); + goto out; + } krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce); if (ireq.u.digestRequest.identifier) { @@ -421,22 +530,9 @@ _kdc_do_digest(krb5_context context, serverNonce.length = ssize; } - { - Key *key; - krb5_enctype enctype; - - ret = _kdc_get_preferred_key(context, - config, - server, - "digest-service", - &enctype, - &key); - if (ret) - goto out; - ret = krb5_crypto_init(context, &key->key, 0, &crypto); - if (ret) - goto out; - } + ret = get_digest_key(context, config, server, &crypto); + if (ret) + goto out; ret = krb5_verify_checksum(context, crypto, KRB5_KU_DIGEST_OPAQUE, @@ -493,6 +589,11 @@ _kdc_do_digest(krb5_context context, unsigned char md[MD5_DIGEST_LENGTH]; char id; + if ((config->digests_allowed & CHAP_MD5) == 0) { + kdc_log(context, config, 0, "Digest CHAP MD5 not allowed"); + goto out; + } + if (ireq.u.digestRequest.identifier == NULL) { krb5_set_error_string(context, "Identifier missing " "from CHAP request"); @@ -524,6 +625,11 @@ _kdc_do_digest(krb5_context context, unsigned char md[MD5_DIGEST_LENGTH]; char *A1, *A2; + if ((config->digests_allowed & DIGEST_MD5) == 0) { + kdc_log(context, config, 0, "Digest SASL MD5 not allowed"); + goto out; + } + if (ireq.u.digestRequest.nonceCount == NULL) goto out; if (ireq.u.digestRequest.clientNonce == NULL) @@ -627,6 +733,358 @@ _kdc_do_digest(krb5_context context, r.u.error.code = EINVAL; } + kdc_log(context, config, 0, "Digest %s request successful %s", + ireq.u.digestRequest.type, from); + + break; + } + case choice_DigestReqInner_ntlmInit: + + if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) { + kdc_log(context, config, 0, "NTLM not allowed"); + goto out; + } + + + r.element = choice_DigestRepInner_ntlmInitReply; + + r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE; + + if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) { + kdc_log(context, config, 0, "NTLM client have no unicode"); + goto out; + } + + if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM) + r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM; + else { + kdc_log(context, config, 0, "NTLM client doesn't support NTLM"); + goto out; + } + + r.u.ntlmInitReply.flags |= + NTLM_NEG_TARGET_DOMAIN | + NTLM_ENC_128; + +#define ALL \ + NTLM_NEG_SIGN| \ + NTLM_NEG_SEAL| \ + NTLM_NEG_ALWAYS_SIGN| \ + NTLM_NEG_NTLM2_SESSION| \ + NTLM_NEG_KEYEX + + r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL)); + +#undef ALL + + r.u.ntlmInitReply.targetname = + get_ntlm_targetname(context, client); + if (r.u.ntlmInitReply.targetname == NULL) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + r.u.ntlmInitReply.challange.data = malloc(8); + if (r.u.ntlmInitReply.challange.data == NULL) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + r.u.ntlmInitReply.challange.length = 8; + if (RAND_bytes(r.u.ntlmInitReply.challange.data, + r.u.ntlmInitReply.challange.length) != 1) + { + krb5_set_error_string(context, "out of random error"); + ret = ENOMEM; + goto out; + } + /* XXX fix targetinfo */ + ALLOC(r.u.ntlmInitReply.targetinfo); + if (r.u.ntlmInitReply.targetinfo == NULL) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + + ret = fill_targetinfo(context, + r.u.ntlmInitReply.targetname, + client, + r.u.ntlmInitReply.targetinfo); + if (ret) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + + /* + * Save data encryted in opaque for the second part of the + * ntlm authentication + */ + sp = krb5_storage_emem(); + if (sp == NULL) { + ret = ENOMEM; + krb5_set_error_string(context, "out of memory"); + goto out; + } + + ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8); + if (ret != 8) { + ret = ENOMEM; + krb5_set_error_string(context, "storage write challange"); + goto out; + } + ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags); + if (ret) { + krb5_clear_error_string(context); + goto out; + } + + ret = krb5_storage_to_data(sp, &buf); + if (ret) { + krb5_clear_error_string(context); + goto out; + } + + ret = get_digest_key(context, config, server, &crypto); + if (ret) + goto out; + + ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, + buf.data, buf.length, &r.u.ntlmInitReply.opaque); + krb5_data_free(&buf); + krb5_crypto_destroy(context, crypto); + crypto = NULL; + if (ret) + goto out; + + kdc_log(context, config, 0, "NTLM init from %s", from); + + break; + + case choice_DigestReqInner_ntlmRequest: { + krb5_principal clientprincipal; + unsigned char sessionkey[16]; + unsigned char challange[8]; + uint32_t flags; + Key *key = NULL; + int version; + + r.element = choice_DigestRepInner_ntlmResponse; + r.u.ntlmResponse.success = 0; + r.u.ntlmResponse.flags = 0; + r.u.ntlmResponse.sessionkey = NULL; + r.u.ntlmResponse.tickets = NULL; + + /* get username */ + ret = krb5_parse_name(context, + ireq.u.ntlmRequest.username, + &clientprincipal); + if (ret) + goto out; + + ret = _kdc_db_fetch(context, config, clientprincipal, + HDB_F_GET_CLIENT, NULL, &user); + krb5_free_principal(context, clientprincipal); + if (ret) { + krb5_set_error_string(context, "NTLM user %s not in database", + ireq.u.ntlmRequest.username); + goto out; + } + + ret = get_digest_key(context, config, server, &crypto); + if (ret) + goto out; + + ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE, + ireq.u.ntlmRequest.opaque.data, + ireq.u.ntlmRequest.opaque.length, &buf); + krb5_crypto_destroy(context, crypto); + crypto = NULL; + if (ret) + goto out; + + sp = krb5_storage_from_data(&buf); + if (sp == NULL) { + ret = ENOMEM; + krb5_set_error_string(context, "out of memory"); + goto out; + } + + ret = krb5_storage_read(sp, challange, sizeof(challange)); + if (ret != sizeof(challange)) { + krb5_set_error_string(context, "NTLM storage read challange"); + ret = ENOMEM; + goto out; + } + ret = krb5_ret_uint32(sp, &flags); + if (ret) { + krb5_set_error_string(context, "NTLM storage read flags"); + goto out; + } + krb5_data_free(&buf); + + if ((flags & NTLM_NEG_NTLM) == 0) { + ret = EINVAL; + krb5_set_error_string(context, "NTLM not negotiated"); + goto out; + } + + ret = hdb_enctype2key(context, &user->entry, + ETYPE_ARCFOUR_HMAC_MD5, &key); + if (ret) { + krb5_set_error_string(context, "NTLM missing arcfour key"); + goto out; + } + + /* check if this is NTLMv2 */ + if (ireq.u.ntlmRequest.ntlm.length != 24) { + struct ntlm_buf infotarget, answer; + char *targetname; + + if ((config->digests_allowed & NTLM_V2) == 0) { + kdc_log(context, config, 0, "NTLM v2 not allowed"); + goto out; + } + + version = 2; + + targetname = get_ntlm_targetname(context, client); + if (targetname == NULL) { + krb5_set_error_string(context, "out of memory"); + ret = ENOMEM; + goto out; + } + + answer.length = ireq.u.ntlmRequest.ntlm.length; + answer.data = ireq.u.ntlmRequest.ntlm.data; + + ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data, + key->key.keyvalue.length, + ireq.u.ntlmRequest.username, + targetname, + 0, + challange, + &answer, + &infotarget, + sessionkey); + free(targetname); + if (ret) { + krb5_set_error_string(context, "NTLM v2 verify failed"); + goto out; + } + + /* XXX verify infotarget matches client (checksum ?) */ + + free(infotarget.data); + /* */ + + } else { + struct ntlm_buf answer; + + version = 1; + + if (flags & NTLM_NEG_NTLM2_SESSION) { + char sessionhash[MD5_DIGEST_LENGTH]; + MD5_CTX md5ctx; + + if ((config->digests_allowed & NTLM_V1_SESSION) == 0) { + kdc_log(context, config, 0, "NTLM v1-session not allowed"); + goto out; + } + + if (ireq.u.ntlmRequest.lm.length != 24) { + krb5_set_error_string(context, "LM hash have wrong length " + "for NTLM session key"); + ret = EINVAL; + goto out; + } + + MD5_Init(&md5ctx); + MD5_Update(&md5ctx, challange, sizeof(challange)); + MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8); + MD5_Final(sessionhash, &md5ctx); + memcpy(challange, sessionhash, sizeof(challange)); + } else { + if ((config->digests_allowed & NTLM_V1) == 0) { + kdc_log(context, config, 0, "NTLM v1 not allowed"); + goto out; + } + } + + ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, + key->key.keyvalue.length, + challange, &answer); + if (ret) { + krb5_set_error_string(context, "NTLM missing arcfour key"); + goto out; + } + + if (ireq.u.ntlmRequest.ntlm.length != answer.length || + memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0) + { + free(answer.data); + ret = EINVAL; + krb5_set_error_string(context, "NTLM hash mismatch"); + goto out; + } + free(answer.data); + + { + MD4_CTX ctx; + + MD4_Init(&ctx); + MD4_Update(&ctx, + key->key.keyvalue.data, key->key.keyvalue.length); + MD4_Final(sessionkey, &ctx); + } + } + + if (ireq.u.ntlmRequest.sessionkey) { + unsigned char masterkey[MD4_DIGEST_LENGTH]; + RC4_KEY rc4; + size_t len; + + if ((flags & NTLM_NEG_KEYEX) == 0) { + krb5_set_error_string(context, + "NTLM client failed to neg key " + "exchange but still sent key"); + goto out; + } + + len = ireq.u.ntlmRequest.sessionkey->length; + if (len != sizeof(masterkey)){ + krb5_set_error_string(context, + "NTLM master key wrong length: %lu", + (unsigned long)len); + goto out; + } + + RC4_set_key(&rc4, sizeof(sessionkey), sessionkey); + + RC4(&rc4, sizeof(masterkey), + ireq.u.ntlmRequest.sessionkey->data, + masterkey); + memset(&rc4, 0, sizeof(rc4)); + + r.u.ntlmResponse.sessionkey = + malloc(sizeof(*r.u.ntlmResponse.sessionkey)); + if (r.u.ntlmResponse.sessionkey == NULL) { + krb5_set_error_string(context, "out of memory"); + goto out; + } + + ret = krb5_data_copy(r.u.ntlmResponse.sessionkey, + masterkey, sizeof(masterkey)); + if (ret) { + krb5_set_error_string(context, "out of memory"); + goto out; + } + } + + r.u.ntlmResponse.success = 1; + kdc_log(context, config, 0, "NTLM version %d successful for %s", + version, ireq.u.ntlmRequest.username); + break; } default: @@ -698,10 +1156,14 @@ out: _kdc_free_ent (context, user); if (server) _kdc_free_ent (context, server); + if (client) + _kdc_free_ent (context, client); if (password) { memset(password, 0, strlen(password)); free (password); } + if (client_name) + free (client_name); krb5_data_free(&buf); krb5_data_free(&serverNonce); free_DigestREP(&rep); diff --git a/source4/heimdal/kdc/headers.h b/source4/heimdal/kdc/headers.h index 87d713b076..56ddc8090b 100644 --- a/source4/heimdal/kdc/headers.h +++ b/source4/heimdal/kdc/headers.h @@ -32,7 +32,7 @@ */ /* - * $Id: headers.h,v 1.18 2006/10/17 02:22:17 lha Exp $ + * $Id: headers.h,v 1.22 2007/01/04 00:15:34 lha Exp $ */ #ifndef __HEADERS_H__ @@ -72,6 +72,9 @@ #ifdef HAVE_ARPA_INET_H #include <arpa/inet.h> #endif +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif #ifdef HAVE_NETDB_H #include <netdb.h> #endif @@ -89,10 +92,14 @@ #include <krb5.h> #include <krb5_locl.h> #include <digest_asn1.h> +#include <kx509_asn1.h> #include <hdb.h> #include <hdb_err.h> #include <der.h> +#include <heimntlm.h> +#include <windc_plugin.h> + #undef ALLOC #define ALLOC(X) ((X) = malloc(sizeof(*(X)))) #undef ALLOC_SEQ diff --git a/source4/heimdal/kdc/kdc-private.h b/source4/heimdal/kdc/kdc-private.h index 6d4fd2a29b..d896bd10e9 100644 --- a/source4/heimdal/kdc/kdc-private.h +++ b/source4/heimdal/kdc/kdc-private.h @@ -15,6 +15,13 @@ _kdc_add_KRB5SignedPath ( EncTicketPart */*tkt*/); krb5_error_code +_kdc_add_inital_verified_cas ( + krb5_context /*context*/, + krb5_kdc_configuration */*config*/, + pk_client_params */*params*/, + EncTicketPart */*tkt*/); + +krb5_error_code _kdc_as_rep ( krb5_context /*context*/, krb5_kdc_configuration */*config*/, @@ -90,6 +97,15 @@ _kdc_do_kaserver ( struct sockaddr_in */*addr*/); krb5_error_code +_kdc_do_kx509 ( + krb5_context /*context*/, + krb5_kdc_configuration */*config*/, + const Kx509Request */*req*/, + krb5_data */*reply*/, + const char */*from*/, + struct sockaddr */*addr*/); + +krb5_error_code _kdc_do_version4 ( krb5_context /*context*/, krb5_kdc_configuration */*config*/, @@ -183,6 +199,20 @@ _kdc_maybe_version4 ( int /*len*/); krb5_error_code +_kdc_pac_generate ( + krb5_context /*context*/, + hdb_entry_ex */*client*/, + krb5_pac */*pac*/); + +krb5_error_code +_kdc_pac_verify ( + krb5_context /*context*/, + const krb5_principal /*client_principal*/, + hdb_entry_ex */*client*/, + hdb_entry_ex */*server*/, + krb5_pac */*pac*/); + +krb5_error_code _kdc_pk_check_client ( krb5_context /*context*/, krb5_kdc_configuration */*config*/, @@ -230,6 +260,30 @@ _kdc_tgs_rep ( KDC_REQ */*req*/, krb5_data */*data*/, const char */*from*/, - struct sockaddr */*from_addr*/); + struct sockaddr */*from_addr*/, + int /*datagram_reply*/); + +krb5_error_code +_kdc_tkt_add_if_relevant_ad ( + krb5_context /*context*/, + EncTicketPart */*tkt*/, + int /*type*/, + const krb5_data */*data*/); + +krb5_error_code +_kdc_try_kx509_request ( + void */*ptr*/, + size_t /*len*/, + Kx509Request */*req*/, + size_t */*size*/); + +krb5_error_code +_kdc_windc_client_access ( + krb5_context /*context*/, + struct hdb_entry_ex */*client*/, + KDC_REQ */*req*/); + +krb5_error_code +_kdc_windc_init (krb5_context /*context*/); #endif /* __kdc_private_h__ */ diff --git a/source4/heimdal/kdc/kdc.h b/source4/heimdal/kdc/kdc.h index 043b6de47d..ea9eb7125e 100644 --- a/source4/heimdal/kdc/kdc.h +++ b/source4/heimdal/kdc/kdc.h @@ -35,7 +35,7 @@ */ /* - * $Id: kdc.h,v 1.9 2006/10/09 15:34:07 lha Exp $ + * $Id: kdc.h,v 1.11 2006/12/28 21:06:56 lha Exp $ */ #ifndef __KDC_H__ @@ -81,8 +81,12 @@ typedef struct krb5_kdc_configuration { int pkinit_dh_min_bits; int enable_digest; + int digests_allowed; + size_t max_datagram_reply_length; + int enable_kx509; + } krb5_kdc_configuration; #include <kdc-protos.h> diff --git a/source4/heimdal/kdc/kdc_locl.h b/source4/heimdal/kdc/kdc_locl.h index ca8672c062..ed3010b673 100644 --- a/source4/heimdal/kdc/kdc_locl.h +++ b/source4/heimdal/kdc/kdc_locl.h @@ -32,7 +32,7 @@ */ /* - * $Id: kdc_locl.h,v 1.74 2005/12/12 12:23:33 lha Exp $ + * $Id: kdc_locl.h,v 1.76 2006/12/26 17:18:14 lha Exp $ */ #ifndef __KDC_LOCL_H__ @@ -55,6 +55,8 @@ extern int enable_http; extern int detach_from_console; +extern const struct units _kdc_digestunits[]; + #define _PATH_KDC_CONF HDB_DB_DIR "/kdc.conf" #define DEFAULT_LOG_DEST "0-1/FILE:" HDB_DB_DIR "/kdc.log" diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index dd88e2ea50..bf727ee739 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -33,7 +33,7 @@ #include "kdc_locl.h" -RCSID("$Id: kerberos5.c,v 1.225 2006/11/10 03:36:32 lha Exp $"); +RCSID("$Id: kerberos5.c,v 1.231 2007/01/04 13:27:27 lha Exp $"); #define MAX_TIME ((time_t)((1U << 31) - 1)) @@ -635,6 +635,69 @@ get_pa_etype_info2(krb5_context context, } /* + * + */ + +static void +log_as_req(krb5_context context, + krb5_kdc_configuration *config, + krb5_enctype cetype, + krb5_enctype setype, + const KDC_REQ_BODY *b) +{ + krb5_error_code ret; + struct rk_strpool *p = NULL; + char *str; + int i; + + for (i = 0; i < b->etype.len; i++) { + ret = krb5_enctype_to_string(context, b->etype.val[i], &str); + if (ret == 0) { + p = rk_strpoolprintf(p, "%s", str); + free(str); + } else + p = rk_strpoolprintf(p, "%d", b->etype.val[i]); + if (p && i + 1 < b->etype.len) + p = rk_strpoolprintf(p, ", "); + if (p == NULL) { + kdc_log(context, config, 0, "out of memory"); + return; + } + } + if (p == NULL) + p = rk_strpoolprintf(p, "no encryption types"); + + str = rk_strpoolcollect(p); + kdc_log(context, config, 0, "Client supported enctypes: %s", str); + free(str); + + { + char *cet; + char *set; + + ret = krb5_enctype_to_string(context, cetype, &cet); + if(ret == 0) { + ret = krb5_enctype_to_string(context, setype, &set); + if (ret == 0) { + kdc_log(context, config, 5, "Using %s/%s", cet, set); + free(set); + } + free(cet); + } + if (ret != 0) + kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype); + } + + { + char str[128]; + unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), + str, sizeof(str)); + if(*str) + kdc_log(context, config, 2, "Requested flags: %s", str); + } +} + +/* * verify the flags on `client' and `server', returning 0 * if they are OK and generating an error messages and returning * and error code otherwise. @@ -798,6 +861,39 @@ _kdc_check_addresses(krb5_context context, return result; } +/* + * + */ + +static krb5_boolean +send_pac_p(krb5_context context, KDC_REQ *req) +{ + krb5_error_code ret; + PA_PAC_REQUEST pacreq; + PA_DATA *pa; + int i = 0; + + pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST); + if (pa == NULL) + return TRUE; + + ret = decode_PA_PAC_REQUEST(pa->padata_value.data, + pa->padata_value.length, + &pacreq, + NULL); + if (ret) + return TRUE; + i = pacreq.include_pac; + free_PA_PAC_REQUEST(&pacreq); + if (i == 0) + return FALSE; + return TRUE; +} + +/* + * + */ + krb5_error_code _kdc_as_rep(krb5_context context, krb5_kdc_configuration *config, @@ -882,6 +978,10 @@ _kdc_as_rep(krb5_context context, goto out; } + ret = _kdc_windc_client_access(context, client, req); + if(ret) + goto out; + ret = _kdc_check_flags(context, config, client, client_name, server, server_name, @@ -889,13 +989,6 @@ _kdc_as_rep(krb5_context context, if(ret) goto out; - if (client->check_client_access) { - ret = client->check_client_access(context, client, - b->addresses); - if(ret) - goto out; - } - memset(&et, 0, sizeof(et)); memset(&ek, 0, sizeof(ek)); @@ -1224,57 +1317,7 @@ _kdc_as_rep(krb5_context context, } } - { - struct rk_strpool *p = NULL; - char *str; - int i; - - for (i = 0; i < b->etype.len; i++) { - ret = krb5_enctype_to_string(context, b->etype.val[i], &str); - if (ret == 0) { - p = rk_strpoolprintf(p, "%s", str); - free(str); - } else - p = rk_strpoolprintf(p, "%d", b->etype.val[i]); - if (p && i + 1 < b->etype.len) - p = rk_strpoolprintf(p, ", "); - if (p == NULL) { - kdc_log(context, config, 0, "out of memory"); - goto out; - } - } - if (p == NULL) - p = rk_strpoolprintf(p, "no encryption types"); - - str = rk_strpoolcollect(p); - kdc_log(context, config, 0, "Client supported enctypes: %s", str); - free(str); - } - { - char *cet; - char *set; - - ret = krb5_enctype_to_string(context, cetype, &cet); - if(ret == 0) { - ret = krb5_enctype_to_string(context, setype, &set); - if (ret == 0) { - kdc_log(context, config, 5, "Using %s/%s", cet, set); - free(set); - } - free(cet); - } - if (ret != 0) - kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype); - } - - { - char str[128]; - unparse_flags(KDCOptions2int(f), asn1_KDCOptions_units(), - str, sizeof(str)); - if(*str) - kdc_log(context, config, 2, "Requested flags: %s", str); - } - + log_as_req(context, config, cetype, setype, b); if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { @@ -1330,7 +1373,9 @@ _kdc_as_rep(krb5_context context, goto out; } - krb5_generate_random_keyblock(context, sessionetype, &et.key); + ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); + if (ret) + goto out; copy_PrincipalName(&rep.cname, &et.cname); copy_Realm(&rep.crealm, &et.crealm); @@ -1469,6 +1514,12 @@ _kdc_as_rep(krb5_context context, &reply_key, rep.padata); if (ret) goto out; + ret = _kdc_add_inital_verified_cas(context, + config, + pkp, + &et); + if (ret) + goto out; } #endif @@ -1479,16 +1530,37 @@ _kdc_as_rep(krb5_context context, rep.padata = NULL; } - /* Add the PAC, via a HDB abstraction */ - if (client->authz_data_as_req) { - ret = client->authz_data_as_req(context, client, - req->padata, - et.authtime, - &skey->key, - &et.key, - &et.authorization_data); - if (ret) - goto out; + /* Add the PAC */ + if (send_pac_p(context, req)) { + krb5_pac p = NULL; + krb5_data data; + + ret = _kdc_pac_generate(context, client, &p); + if (ret) { + kdc_log(context, config, 0, "PAC generation failed for -- %s", + client_name); + goto out; + } + if (p != NULL) { + ret = _krb5_pac_sign(context, p, et.authtime, + client->entry.principal, + &skey->key, /* Server key */ + &skey->key, /* FIXME: should be krbtgt key */ + &data); + krb5_pac_free(context, p); + if (ret) { + kdc_log(context, config, 0, "PAC signing failed for -- %s", + client_name); + goto out; + } + + ret = _kdc_tkt_add_if_relevant_ad(context, &et, + KRB5_AUTHDATA_WIN2K_PAC, + &data); + krb5_data_free(&data); + if (ret) + goto out; + } } _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, @@ -1552,3 +1624,64 @@ out2: _kdc_free_ent(context, server); return ret; } + +/* + * Add the AuthorizationData `data´ of `type´ to the last element in + * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT + */ + +krb5_error_code +_kdc_tkt_add_if_relevant_ad(krb5_context context, + EncTicketPart *tkt, + int type, + const krb5_data *data) +{ + krb5_error_code ret; + size_t size; + + if (tkt->authorization_data == NULL) { + tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data)); + if (tkt->authorization_data == NULL) { + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + } + + /* add the entry to the last element */ + { + AuthorizationData ad = { 0, NULL }; + AuthorizationDataElement ade; + + ade.ad_type = type; + ade.ad_data = *data; + + ret = add_AuthorizationData(&ad, &ade); + if (ret) { + krb5_set_error_string(context, "add AuthorizationData failed"); + return ret; + } + + ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT; + + ASN1_MALLOC_ENCODE(AuthorizationData, + ade.ad_data.data, ade.ad_data.length, + &ad, &size, ret); + free_AuthorizationData(&ad); + if (ret) { + krb5_set_error_string(context, "ASN.1 encode of " + "AuthorizationData failed"); + return ret; + } + if (ade.ad_data.length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + ret = add_AuthorizationData(tkt->authorization_data, &ade); + der_free_octet_string(&ade.ad_data); + if (ret) { + krb5_set_error_string(context, "add AuthorizationData failed"); + return ret; + } + } + + return 0; +} diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index dcf29eb6e9..a056839e5f 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,7 +33,7 @@ #include "kdc_locl.h" -RCSID("$Id: krb5tgs.c,v 1.16 2006/10/22 15:54:37 lha Exp $"); +RCSID("$Id: krb5tgs.c,v 1.25 2007/01/04 12:49:45 lha Exp $"); /* * return the realm of a krbtgt-ticket or NULL @@ -119,7 +119,7 @@ _kdc_add_KRB5SignedPath(krb5_context context, if (server && principals) { ret = add_KRB5SignedPathPrincipals(principals, server); if (ret) - goto out; + return ret; } { @@ -131,7 +131,7 @@ _kdc_add_KRB5SignedPath(krb5_context context, ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length, &spd, &size, ret); if (ret) - goto out; + return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); } @@ -159,12 +159,12 @@ _kdc_add_KRB5SignedPath(krb5_context context, krb5_crypto_destroy(context, crypto); free(data.data); if (ret) - goto out; + return ret; ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret); free_Checksum(&sp.cksum); if (ret) - goto out; + return ret; if (data.length != size) krb5_abortx(context, "internal asn.1 encoder error"); @@ -174,46 +174,11 @@ _kdc_add_KRB5SignedPath(krb5_context context, * authorization data field. */ - if (tkt->authorization_data == NULL) { - tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data)); - if (tkt->authorization_data == NULL) { - ret = ENOMEM; - goto out; - } - } - - /* add the entry to the last element */ - { - AuthorizationData ad = { 0, NULL }; - AuthorizationDataElement ade; - - ade.ad_type = KRB5_AUTHDATA_SIGNTICKET; - ade.ad_data = data; - - ret = add_AuthorizationData(&ad, &ade); - krb5_data_free(&data); - if (ret) - return ret; - - ASN1_MALLOC_ENCODE(AuthorizationData, data.data, data.length, - &ad, &size, ret); - free_AuthorizationData(&ad); - if (ret) - return ret; - if (data.length != size) - krb5_abortx(context, "internal asn.1 encoder error"); - - ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT; - ade.ad_data = data; - - ret = add_AuthorizationData(tkt->authorization_data, &ade); - krb5_data_free(&data); - if (ret) - return ret; - } + ret = _kdc_tkt_add_if_relevant_ad(context, tkt, + KRB5_AUTHDATA_SIGNTICKET, &data); + krb5_data_free(&data); -out: - return 0; + return ret; } static krb5_error_code @@ -307,6 +272,87 @@ check_KRB5SignedPath(krb5_context context, return 0; } +/* + * + */ + +static krb5_error_code +check_PAC(krb5_context context, + krb5_kdc_configuration *config, + const krb5_principal client_principal, + hdb_entry_ex *client, + hdb_entry_ex *server, + const EncryptionKey *server_key, + const EncryptionKey *krbtgt_key, + EncTicketPart *tkt, + krb5_data *rspac, + int *require_signedpath) +{ + AuthorizationData *ad = tkt->authorization_data; + unsigned i, j; + krb5_error_code ret; + + if (ad == NULL || ad->len == 0) + return 0; + + for (i = 0; i < ad->len; i++) { + AuthorizationData child; + + if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT) + continue; + + ret = decode_AuthorizationData(ad->val[i].ad_data.data, + ad->val[i].ad_data.length, + &child, + NULL); + if (ret) { + krb5_set_error_string(context, "Failed to decode " + "IF_RELEVANT with %d", ret); + return ret; + } + for (j = 0; j < child.len; j++) { + + if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) { + krb5_pac pac; + + /* Found PAC */ + ret = krb5_pac_parse(context, + child.val[j].ad_data.data, + child.val[j].ad_data.length, + &pac); + free_AuthorizationData(&child); + if (ret) + return ret; + + ret = krb5_pac_verify(context, pac, tkt->authtime, + client_principal, + krbtgt_key, NULL); + if (ret) { + krb5_pac_free(context, pac); + return ret; + } + + ret = _kdc_pac_verify(context, client_principal, + client, server, &pac); + if (ret) { + krb5_pac_free(context, pac); + return ret; + } + *require_signedpath = 0; + + ret = _krb5_pac_sign(context, pac, tkt->authtime, + client_principal, + server_key, krbtgt_key, rspac); + + krb5_pac_free(context, pac); + + return ret; + } + } + free_AuthorizationData(&child); + } + return 0; +} /* * @@ -610,9 +656,10 @@ tgs_make_reply(krb5_context context, KDC_REQ_BODY *b, krb5_const_principal tgt_name, const EncTicketPart *tgt, - const EncTicketPart *adtkt, + const EncryptionKey *ekey, + const krb5_keyblock *sessionkey, + krb5_kvno kvno, AuthorizationData *auth_data, - krb5_ticket *tgs_ticket, hdb_entry_ex *server, const char *server_name, hdb_entry_ex *client, @@ -620,7 +667,7 @@ tgs_make_reply(krb5_context context, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, KRB5SignedPathPrincipals *spp, - EncryptionKey *tgtkey, + const krb5_data *rspac, const char **e_text, krb5_data *reply) { @@ -629,32 +676,6 @@ tgs_make_reply(krb5_context context, EncTicketPart et; KDCOptions f = b->kdc_options; krb5_error_code ret; - krb5_enctype etype; - Key *skey; - const EncryptionKey *ekey; - AuthorizationData *new_auth_data = NULL; - - if(adtkt) { - int i; - ekey = &adtkt->key; - for(i = 0; i < b->etype.len; i++) - if (b->etype.val[i] == adtkt->key.keytype) - break; - if(i == b->etype.len) { - krb5_clear_error_string(context); - return KRB5KDC_ERR_ETYPE_NOSUPP; - } - etype = b->etype.val[i]; - }else{ - ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, - &skey, &etype); - if(ret) { - kdc_log(context, config, 0, - "Server (%s) has no support for etypes", server_name); - return ret; - } - ekey = &skey->key; - } memset(&rep, 0, sizeof(rep)); memset(&et, 0, sizeof(et)); @@ -768,26 +789,47 @@ tgs_make_reply(krb5_context context, et.flags.anonymous = tgt->flags.anonymous; et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate; - - krb5_generate_random_keyblock(context, etype, &et.key); - - if (server->authz_data_tgs_req) { - ret = server->authz_data_tgs_req(context, server, - client_principal, - tgs_ticket->ticket.authorization_data, - tgs_ticket->ticket.authtime, - tgtkey, - ekey, - &et.key, - &new_auth_data); - if (ret) { - new_auth_data = NULL; + if (auth_data) { + /* XXX Check enc-authorization-data */ + et.authorization_data = calloc(1, sizeof(*et.authorization_data)); + if (et.authorization_data == NULL) { + ret = ENOMEM; + goto out; + } + ret = copy_AuthorizationData(auth_data, et.authorization_data); + if (ret) + goto out; + + /* Filter out type KRB5SignedPath */ + ret = find_KRB5SignedPath(context, et.authorization_data, NULL); + if (ret == 0) { + if (et.authorization_data->len == 1) { + free_AuthorizationData(et.authorization_data); + free(et.authorization_data); + et.authorization_data = NULL; + } else { + AuthorizationData *ad = et.authorization_data; + free_AuthorizationDataElement(&ad->val[ad->len - 1]); + ad->len--; } + } } - /* XXX Check enc-authorization-data */ - et.authorization_data = new_auth_data; + if(rspac->length) { + /* + * No not need to filter out the any PAC from the + * auth_data since its signed by the KDC. + */ + ret = _kdc_tkt_add_if_relevant_ad(context, &et, + KRB5_AUTHDATA_WIN2K_PAC, + rspac); + if (ret) + goto out; + } + ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key); + if (ret) + goto out; et.crealm = tgt->crealm; et.cname = tgt_name->name; @@ -795,6 +837,10 @@ tgs_make_reply(krb5_context context, /* MIT must have at least one last_req */ ek.last_req.len = 1; ek.last_req.val = calloc(1, sizeof(*ek.last_req.val)); + if (ek.last_req.val == NULL) { + ret = ENOMEM; + goto out; + } ek.nonce = b->nonce; ek.flags = et.flags; ek.authtime = et.authtime; @@ -817,7 +863,7 @@ tgs_make_reply(krb5_context context, krbtgt, krbtgt_etype, NULL, - NULL, + spp, &et); if (ret) goto out; @@ -835,8 +881,8 @@ tgs_make_reply(krb5_context context, etype list, even if we don't want a session key with DES3? */ ret = _kdc_encode_reply(context, config, - &rep, &et, &ek, etype, - adtkt ? 0 : server->entry.kvno, + &rep, &et, &ek, et.key.keytype, + kvno, ekey, 0, &tgt->key, e_text, reply); out: free_TGS_REP(&rep); @@ -973,8 +1019,7 @@ tgs_parse_request(krb5_context context, const struct sockaddr *from_addr, time_t **csec, int **cusec, - AuthorizationData **auth_data, - EncryptionKey **tgtkey) + AuthorizationData **auth_data) { krb5_ap_req ap_req; krb5_error_code ret; @@ -1060,8 +1105,6 @@ tgs_parse_request(krb5_context context, ret = KRB5KRB_AP_ERR_BADKEYVER; goto out; } - - *tgtkey = &tkey->key; if (b->kdc_options.validate) verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID; @@ -1201,8 +1244,8 @@ tgs_build_reply(krb5_context context, const char *from, const char **e_text, AuthorizationData *auth_data, - EncryptionKey *tgtkey, - const struct sockaddr *from_addr) + const struct sockaddr *from_addr, + int datagram_reply) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL; @@ -1211,6 +1254,10 @@ tgs_build_reply(krb5_context context, hdb_entry_ex *server = NULL, *client = NULL; EncTicketPart *tgt = &ticket->ticket; KRB5SignedPathPrincipals *spp = NULL; + const EncryptionKey *ekey; + krb5_keyblock sessionkey; + krb5_kvno kvno; + krb5_data rspac; PrincipalName *s; Realm r; @@ -1219,7 +1266,9 @@ tgs_build_reply(krb5_context context, char opt_str[128]; int require_signedpath = 0; + memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); + krb5_data_zero(&rspac); s = b->sname; r = b->realm; @@ -1436,7 +1485,7 @@ server_lookup: ret = krb5_verify_checksum(context, crypto, - KRB5_KU_TGS_IMPERSONATE, + KRB5_KU_OTHER_CKSUM, datack.data, datack.length, &self.cksum); @@ -1617,6 +1666,67 @@ server_lookup: goto out; } + /* + * Select enctype, return key and kvno. + */ + + { + krb5_enctype etype; + + if(b->kdc_options.enc_tkt_in_skey) { + int i; + ekey = &adtkt.key; + for(i = 0; i < b->etype.len; i++) + if (b->etype.val[i] == adtkt.key.keytype) + break; + if(i == b->etype.len) { + krb5_clear_error_string(context); + return KRB5KDC_ERR_ETYPE_NOSUPP; + } + etype = b->etype.val[i]; + kvno = 0; + } else { + Key *skey; + + ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len, + &skey, &etype); + if(ret) { + kdc_log(context, config, 0, + "Server (%s) has no support for etypes", spp); + return ret; + } + ekey = &skey->key; + kvno = server->entry.kvno; + } + + ret = krb5_generate_random_keyblock(context, etype, &sessionkey); + if (ret) + goto out; + } + + /* check PAC if there is one */ + { + Key *tkey; + + ret = hdb_enctype2key(context, &krbtgt->entry, + krbtgt_etype, &tkey); + if(ret) { + kdc_log(context, config, 0, + "Failed to find key for krbtgt PAC check"); + goto out; + } + + ret = check_PAC(context, config, client_principal, + client, server, ekey, &tkey->key, + tgt, &rspac, &require_signedpath); + if (ret) { + kdc_log(context, config, 0, + "check_PAC check failed for %s (%s) from %s with %s", + spn, cpn, from, krb5_get_err_text(context, ret)); + goto out; + } + } + /* also check the krbtgt for signature */ ret = check_KRB5SignedPath(context, config, @@ -1640,9 +1750,10 @@ server_lookup: b, client_principal, tgt, - b->kdc_options.enc_tkt_in_skey ? &adtkt : NULL, + ekey, + &sessionkey, + kvno, auth_data, - ticket, server, spn, client, @@ -1650,7 +1761,7 @@ server_lookup: krbtgt, krbtgt_etype, spp, - tgtkey, + &rspac, e_text, reply); @@ -1658,6 +1769,8 @@ out: free(spn); free(cpn); + krb5_data_free(&rspac); + krb5_free_keyblock_contents(context, &sessionkey); if(server) _kdc_free_ent(context, server); if(client) @@ -1685,7 +1798,8 @@ _kdc_tgs_rep(krb5_context context, KDC_REQ *req, krb5_data *data, const char *from, - struct sockaddr *from_addr) + struct sockaddr *from_addr, + int datagram_reply) { AuthorizationData *auth_data = NULL; krb5_error_code ret; @@ -1696,8 +1810,6 @@ _kdc_tgs_rep(krb5_context context, krb5_ticket *ticket = NULL; const char *e_text = NULL; krb5_enctype krbtgt_etype = ETYPE_NULL; - EncryptionKey *tgtkey = NULL; - time_t *csec = NULL; int *cusec = NULL; @@ -1726,8 +1838,7 @@ _kdc_tgs_rep(krb5_context context, &e_text, from, from_addr, &csec, &cusec, - &auth_data, - &tgtkey); + &auth_data); if (ret) { kdc_log(context, config, 0, "Failed parsing TGS-REQ from %s", from); @@ -1745,14 +1856,21 @@ _kdc_tgs_rep(krb5_context context, from, &e_text, auth_data, - tgtkey, - from_addr); + from_addr, + datagram_reply); if (ret) { kdc_log(context, config, 0, "Failed building TGS-REP to %s", from); goto out; } + /* */ + if (datagram_reply && data->length > config->max_datagram_reply_length) { + krb5_data_free(data); + ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; + e_text = "Reply packet too large"; + } + out: if(ret && data->data == NULL){ krb5_mk_error(context, diff --git a/source4/heimdal/kdc/kx509.c b/source4/heimdal/kdc/kx509.c new file mode 100644 index 0000000000..d817338f73 --- /dev/null +++ b/source4/heimdal/kdc/kx509.c @@ -0,0 +1,370 @@ +/* + * 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. + */ + +#include "kdc_locl.h" +#include <hex.h> + +RCSID("$Id: kx509.c,v 1.1 2006/12/28 21:03:53 lha Exp $"); + +/* + * + */ + +krb5_error_code +_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size) +{ + if (len < 4) + return -1; + if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0) + return -1; + return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size); +} + +/* + * + */ + +static const char version_2_0[4] = {0 , 0, 2, 0}; + +static krb5_error_code +verify_req_hash(krb5_context context, + const Kx509Request *req, + krb5_keyblock *key) +{ + unsigned char digest[SHA_DIGEST_LENGTH]; + HMAC_CTX ctx; + + if (req->pk_hash.length != sizeof(digest)) { + krb5_set_error_string(context, "pk-hash have wrong length: %lu", + (unsigned long)req->pk_hash.length); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, + key->keyvalue.data, key->keyvalue.length, + EVP_sha1(), NULL); + if (sizeof(digest) != HMAC_size(&ctx)) + krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509"); + HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); + HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length); + HMAC_Final(&ctx, digest, 0); + HMAC_CTX_cleanup(&ctx); + + if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) { + krb5_set_error_string(context, "pk-hash is not correct"); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + return 0; +} + +static krb5_error_code +calculate_reply_hash(krb5_context context, + krb5_keyblock *key, + Kx509Response *rep) +{ + HMAC_CTX ctx; + + HMAC_CTX_init(&ctx); + + HMAC_Init_ex(&ctx, + key->keyvalue.data, key->keyvalue.length, + EVP_sha1(), NULL); + rep->hash->length = HMAC_size(&ctx); + rep->hash->data = malloc(rep->hash->length); + if (rep->hash->data == NULL) { + HMAC_CTX_cleanup(&ctx); + krb5_set_error_string(context, "out of memory"); + return ENOMEM; + } + + HMAC_Update(&ctx, version_2_0, sizeof(version_2_0)); + if (rep->error_code) { + int32_t t = *rep->error_code; + do { + unsigned char p = (t & 0xff); + HMAC_Update(&ctx, &p, 1); + t >>= 8; + } while (t); + } + if (rep->certificate) + HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length); + if (rep->e_text) + HMAC_Update(&ctx, *rep->e_text, strlen(*rep->e_text)); + + HMAC_Final(&ctx, rep->hash->data, 0); + HMAC_CTX_cleanup(&ctx); + + return 0; +} + +/* + * Build a certifate for `principal´ that will expire at `endtime´. + */ + +static krb5_error_code +build_certificate(krb5_context context, + krb5_kdc_configuration *config, + const krb5_data *key, + time_t endtime, + krb5_principal principal, + krb5_data *certificate) +{ + /* XXX write code here to generate certificates */ + FILE *in, *out; + krb5_error_code ret; + const char *program; + char *str, *strkey; + char tstr[64]; + pid_t pid; + + snprintf(tstr, sizeof(tstr), "%lu", (unsigned long)endtime); + + ret = base64_encode(key->data, key->length, &strkey); + if (ret < 0) { + krb5_set_error_string(context, "failed to base64 encode key"); + return ENOMEM; + } + + program = krb5_config_get_string(context, + NULL, + "kdc", + "kx509_cert_program", + NULL); + if (program == NULL) { + free(strkey); + krb5_set_error_string(context, "no certificate program configured"); + return ENOENT; + } + + ret = krb5_unparse_name(context, principal, &str); + if (ret) { + free(strkey); + return ret; + } + + pid = pipe_execv(&in, &out, NULL, program, str, tstr, NULL); + free(str); + if (pid <= 0) { + free(strkey); + krb5_set_error_string(context, + "Failed to run the cert program %s", + program); + return ret; + } + fprintf(in, "%s\n", strkey); + fclose(in); + free(strkey); + + { + unsigned buf[1024 * 10]; + size_t len; + + len = fread(buf, 1, sizeof(buf), out); + fclose(out); + if(len == 0) { + krb5_set_error_string(context, + "Certificate program returned no data"); + return KRB5KDC_ERR_PREAUTH_FAILED; + } + ret = krb5_data_copy(certificate, buf, len); + if (ret) { + krb5_set_error_string(context, "Failed To copy certificate"); + return ret; + } + } + kill(pid, SIGKILL); + waitpid(pid, NULL, 0); + return 0; +} + +/* + * + */ + +krb5_error_code +_kdc_do_kx509(krb5_context context, + krb5_kdc_configuration *config, + const Kx509Request *req, krb5_data *reply, + const char *from, struct sockaddr *addr) +{ + krb5_error_code ret; + krb5_ticket *ticket = NULL; + krb5_flags ap_req_options; + krb5_auth_context ac = NULL; + krb5_keytab id = NULL; + krb5_principal sprincipal = NULL, cprincipal = NULL; + char *cname = NULL; + Kx509Response rep; + size_t size; + krb5_keyblock *key = NULL; + + krb5_data_zero(reply); + memset(&rep, 0, sizeof(rep)); + + if(!config->enable_kx509) { + kdc_log(context, config, 0, + "Rejected kx509 request (disabled) from %s", from); + return KRB5KDC_ERR_POLICY; + } + + kdc_log(context, config, 0, "Kx509 request from %s", from); + + ret = krb5_kt_resolve(context, "HDB:", &id); + if (ret) { + kdc_log(context, config, 0, "Can't open database for digest"); + goto out; + } + + ret = krb5_rd_req(context, + &ac, + &req->authenticator, + NULL, + id, + &ap_req_options, + &ticket); + if (ret) + goto out; + + ret = krb5_ticket_get_client(context, ticket, &cprincipal); + if (ret) + goto out; + + ret = krb5_unparse_name(context, cprincipal, &cname); + if (ret) + goto out; + + /* verify server principal */ + + ret = krb5_sname_to_principal(context, NULL, "kca_service", + KRB5_NT_UNKNOWN, &sprincipal); + if (ret) + goto out; + + { + krb5_principal principal = NULL; + + ret = krb5_ticket_get_server(context, ticket, &principal); + if (ret) + goto out; + + ret = krb5_principal_compare(context, sprincipal, principal); + krb5_free_principal(context, principal); + if (ret != TRUE) { + ret = KRB5KDC_ERR_SERVER_NOMATCH; + krb5_set_error_string(context, + "User %s used wrong Kx509 service principal", + cname); + goto out; + } + } + + ret = krb5_auth_con_getkey(context, ac, &key); + if (ret || key == NULL) { + krb5_set_error_string(context, "Kx509 can't get session key"); + goto out; + } + + ret = verify_req_hash(context, req, key); + if (ret) + goto out; + + ALLOC(rep.certificate); + if (rep.certificate == NULL) + goto out; + krb5_data_zero(rep.certificate); + ALLOC(rep.hash); + if (rep.hash == NULL) + goto out; + krb5_data_zero(rep.hash); + + ret = build_certificate(context, config, &req->pk_key, + krb5_ticket_get_endtime(context, ticket), + cprincipal, rep.certificate); + if (ret) + goto out; + + ret = calculate_reply_hash(context, key, &rep); + if (ret) + goto out; + + /* + * Encode reply, [ version | Kx509Response ] + */ + + { + krb5_data data; + + ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep, + &size, ret); + if (ret) { + krb5_set_error_string(context, "Failed to encode kx509 reply"); + goto out; + } + if (size != data.length) + krb5_abortx(context, "ASN1 internal error"); + + ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0)); + if (ret) { + free(data.data); + goto out; + } + memcpy(reply->data, version_2_0, sizeof(version_2_0)); + memcpy(((unsigned char *)reply->data) + sizeof(version_2_0), + data.data, data.length); + free(data.data); + } + + kdc_log(context, config, 0, "Successful Kx509 request for %s", cname); + +out: + if (ac) + krb5_auth_con_free(context, ac); + if (ret) + krb5_warn(context, ret, "Kx509 request from %s failed", from); + if (ticket) + krb5_free_ticket(context, ticket); + if (id) + krb5_kt_close(context, id); + if (sprincipal) + krb5_free_principal(context, sprincipal); + if (cprincipal) + krb5_free_principal(context, cprincipal); + if (key) + krb5_free_keyblock (context, key); + if (cname) + free(cname); + free_Kx509Response(&rep); + + return 0; +} diff --git a/source4/heimdal/kdc/pkinit.c b/source4/heimdal/kdc/pkinit.c index 6657ab7c44..418a38d030 100755 --- a/source4/heimdal/kdc/pkinit.c +++ b/source4/heimdal/kdc/pkinit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan + * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,7 +33,7 @@ #include "kdc_locl.h" -RCSID("$Id: pkinit.c,v 1.74 2006/11/10 03:37:43 lha Exp $"); +RCSID("$Id: pkinit.c,v 1.86 2007/01/04 12:54:09 lha Exp $"); #ifdef PKINIT @@ -68,6 +68,8 @@ struct pk_client_params { DH *dh; EncryptionKey reply_key; char *dh_group_name; + hx509_peer_info peer; + hx509_certs client_anchors; }; struct pk_principal_mapping { @@ -180,6 +182,10 @@ _kdc_pk_free_client_param(krb5_context context, krb5_free_keyblock_contents(context, &client_params->reply_key); if (client_params->dh_group_name) free(client_params->dh_group_name); + if (client_params->peer) + hx509_peer_info_free(client_params->peer); + if (client_params->client_anchors) + hx509_certs_free(&client_params->client_anchors); memset(client_params, 0, sizeof(*client_params)); free(client_params); } @@ -302,8 +308,10 @@ get_dh_param(krb5_context context, ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits, &dhparam.p, &dhparam.g, &dhparam.q, moduli, &client_params->dh_group_name); - if (ret) + if (ret) { + /* XXX send back proposal of better group */ goto out; + } dh = DH_new(); if (dh == NULL) { @@ -354,64 +362,6 @@ get_dh_param(krb5_context context, return ret; } -#if 0 -/* - * XXX We only need this function if there are several certs for the - * KDC to choose from, and right now, we can't handle that so punt for - * now. - * - * If client has sent a list of CA's trusted by him, make sure our - * CA is in the list. - * - */ - -static void -verify_trusted_ca(PA_PK_AS_REQ_19 *r) -{ - - if (r.trustedCertifiers != NULL) { - X509_NAME *kdc_issuer; - X509 *kdc_cert; - - kdc_cert = sk_X509_value(kdc_identity->cert, 0); - kdc_issuer = X509_get_issuer_name(kdc_cert); - - /* XXX will work for heirarchical CA's ? */ - /* XXX also serial_number should be compared */ - - ret = KRB5_KDC_ERR_KDC_NOT_TRUSTED; - for (i = 0; i < r.trustedCertifiers->len; i++) { - TrustedCA_19 *ca = &r.trustedCertifiers->val[i]; - - switch (ca->element) { - case choice_TrustedCA_19_caName: { - X509_NAME *name; - unsigned char *p; - - p = ca->u.caName.data; - name = d2i_X509_NAME(NULL, &p, ca->u.caName.length); - if (name == NULL) /* XXX should this be a failure instead ? */ - break; - if (X509_NAME_cmp(name, kdc_issuer) == 0) - ret = 0; - X509_NAME_free(name); - break; - } - case choice_TrustedCA_19_issuerAndSerial: - /* IssuerAndSerialNumber issuerAndSerial */ - break; - default: - break; - } - if (ret == 0) - break; - } - if (ret) - goto out; - } -} -#endif /* 0 */ - krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, @@ -483,7 +433,61 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - /* XXX look at r.trustedCertifiers and r.kdcPkId */ + /* XXX look at r.kdcPkId */ + if (r.trustedCertifiers) { + ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; + unsigned int i; + + ret = hx509_certs_init(kdc_identity->hx509ctx, + "MEMORY:client-anchors", + 0, NULL, + &client_params->client_anchors); + if (ret) { + krb5_set_error_string(context, "Can't allocate client anchors: %d", ret); + goto out; + + } + for (i = 0; i < edi->len; i++) { + IssuerAndSerialNumber iasn; + hx509_query *q; + hx509_cert cert; + size_t size; + + if (edi->val[i].issuerAndSerialNumber == NULL) + continue; + + ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); + if (ret) { + krb5_set_error_string(context, + "Failed to allocate hx509_query"); + goto out; + } + + ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data, + edi->val[i].issuerAndSerialNumber->length, + &iasn, + &size); + if (ret || size != 0) { + hx509_query_free(kdc_identity->hx509ctx, q); + continue; + } + ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); + free_IssuerAndSerialNumber(&iasn); + if (ret) + continue; + + ret = hx509_certs_find(kdc_identity->hx509ctx, + kdc_identity->certs, + q, + &cert); + hx509_query_free(kdc_identity->hx509ctx, q); + if (ret) + continue; + hx509_certs_add(kdc_identity->hx509ctx, + client_params->client_anchors, cert); + hx509_cert_free(cert); + } + } ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack, &contentInfoOid, @@ -611,6 +615,23 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } } + + if (ap.supportedCMSTypes) { + ret = hx509_peer_info_alloc(kdc_identity->hx509ctx, + &client_params->peer); + if (ret) { + free_AuthPack(&ap); + goto out; + } + ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx, + client_params->peer, + ap.supportedCMSTypes->val, + ap.supportedCMSTypes->len); + if (ret) { + free_AuthPack(&ap); + goto out; + } + } free_AuthPack(&ap); } else krb5_abortx(context, "internal pkinit error"); @@ -752,7 +773,8 @@ pk_mk_pa_reply_enckey(krb5_context context, buf.length, NULL, cert, - kdc_identity->anchors, + client_params->peer, + client_params->client_anchors, kdc_identity->certpool, &signed_data); hx509_cert_free(cert); @@ -864,7 +886,8 @@ pk_mk_pa_reply_dh(krb5_context context, buf.length, NULL, cert, - kdc_identity->anchors, + client_params->peer, + client_params->client_anchors, kdc_identity->certpool, &signed_data); *kdc_cert = cert; @@ -948,8 +971,12 @@ _kdc_pk_mk_pa_reply(krb5_context context, rep.element = choice_PA_PK_AS_REP_encKeyPack; - krb5_generate_random_keyblock(context, enctype, - &client_params->reply_key); + ret = krb5_generate_random_keyblock(context, enctype, + &client_params->reply_key); + if (ret) { + free_PA_PK_AS_REP(&rep); + goto out; + } ret = pk_mk_pa_reply_enckey(context, client_params, req, @@ -1039,8 +1066,12 @@ _kdc_pk_mk_pa_reply(krb5_context context, pa_type = KRB5_PADATA_PK_AS_REP_19; rep.element = choice_PA_PK_AS_REP_encKeyPack; - krb5_generate_random_keyblock(context, enctype, - &client_params->reply_key); + ret = krb5_generate_random_keyblock(context, enctype, + &client_params->reply_key); + if (ret) { + free_PA_PK_AS_REP_Win2k(&rep); + goto out; + } ret = pk_mk_pa_reply_enckey(context, client_params, req, @@ -1337,6 +1368,35 @@ add_principal_mapping(krb5_context context, return 0; } +krb5_error_code +_kdc_add_inital_verified_cas(krb5_context context, + krb5_kdc_configuration *config, + pk_client_params *params, + EncTicketPart *tkt) +{ + AD_INITIAL_VERIFIED_CAS cas; + krb5_error_code ret; + krb5_data data; + size_t size; + + memset(&cas, 0, sizeof(cas)); + + /* XXX add CAs to cas here */ + + ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length, + &cas, &size, ret); + if (ret) + return ret; + if (data.length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + ret = _kdc_tkt_add_if_relevant_ad(context, tkt, + ad_initial_verified_cas, &data); + krb5_data_free(&data); + return ret; +} + + krb5_error_code _kdc_pk_initialize(krb5_context context, @@ -1372,7 +1432,7 @@ _kdc_pk_initialize(krb5_context context, NULL, NULL); if (ret) { - krb5_warn(context, ret, "PKINIT: failed to load"); + krb5_warn(context, ret, "PKINIT: "); config->enable_pkinit = 0; return ret; } @@ -1411,7 +1471,7 @@ _kdc_pk_initialize(krb5_context context, NULL, FALSE, "kdc", - "pki-allow-proxy-certificate", + "pkinit_allow_proxy_certificate", NULL); _krb5_pk_allow_proxy_certificate(kdc_identity, ret); @@ -1419,7 +1479,7 @@ _kdc_pk_initialize(krb5_context context, NULL, HDB_DB_DIR "/pki-mapping", "kdc", - "pki-mappings-file", + "pkinit_mappings_file", NULL); f = fopen(file, "r"); if (f == NULL) { diff --git a/source4/heimdal/kdc/process.c b/source4/heimdal/kdc/process.c index ed5cb3d651..a64efaa05d 100644 --- a/source4/heimdal/kdc/process.c +++ b/source4/heimdal/kdc/process.c @@ -34,7 +34,7 @@ #include "kdc_locl.h" -RCSID("$Id: process.c,v 1.5 2006/10/09 15:37:39 lha Exp $"); +RCSID("$Id: process.c,v 1.7 2006/12/28 21:09:35 lha Exp $"); /* * handle the request in `buf, len', from `addr' (or `from' as a string), @@ -55,6 +55,7 @@ krb5_kdc_process_request(krb5_context context, KDC_REQ req; Ticket ticket; DigestREQ digestreq; + Kx509Request kx509req; krb5_error_code ret; size_t i; @@ -70,7 +71,7 @@ krb5_kdc_process_request(krb5_context context, free_AS_REQ(&req); return ret; }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){ - ret = _kdc_tgs_rep(context, config, &req, reply, from, addr); + ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply); free_TGS_REQ(&req); return ret; }else if(decode_Ticket(buf, len, &ticket, &i) == 0){ @@ -81,6 +82,10 @@ krb5_kdc_process_request(krb5_context context, ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr); free_DigestREQ(&digestreq); return ret; + } else if (_kdc_try_kx509_request(buf, len, &kx509req, &i) == 0) { + ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr); + free_Kx509Request(&kx509req); + return ret; } else if(_kdc_maybe_version4(buf, len)){ *prependlength = FALSE; /* elbitapmoc sdrawkcab XXX */ _kdc_do_version4(context, config, buf, len, reply, from, @@ -128,7 +133,7 @@ krb5_kdc_process_krb5_request(krb5_context context, free_AS_REQ(&req); return ret; }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){ - ret = _kdc_tgs_rep(context, config, &req, reply, from, addr); + ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply); free_TGS_REQ(&req); return ret; } diff --git a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c new file mode 100644 index 0000000000..41e4ad1bbc --- /dev/null +++ b/source4/heimdal/kdc/windc.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 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. + */ + +#include "kdc_locl.h" + +RCSID("$Id: windc.c,v 1.3 2007/01/04 11:10:06 lha Exp $"); + +static krb5plugin_windc_ftable *windcft; +static void *windcctx; + +/* + * Pick the first WINDC module that we find. + */ + +krb5_error_code +_kdc_windc_init(krb5_context context) +{ + struct krb5_plugin *list = NULL, *e; + krb5_error_code ret; + + ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "windc", &list); + if(ret != 0 || list == NULL) + return 0; + + for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) { + + windcft = _krb5_plugin_get_symbol(e); + if (windcft->minor_version < KRB5_WINDC_PLUGING_MINOR) + continue; + + (*windcft->init)(context, &windcctx); + break; + } + if (e == NULL) { + _krb5_plugin_free(list); + krb5_set_error_string(context, "Did not find any WINDC plugin"); + windcft = NULL; + return ENOENT; + } + + return 0; +} + + +krb5_error_code +_kdc_pac_generate(krb5_context context, + hdb_entry_ex *client, + krb5_pac *pac) +{ + *pac = NULL; + if (windcft == NULL) + return 0; + return (windcft->pac_generate)(windcctx, context, client, pac); +} + +krb5_error_code +_kdc_pac_verify(krb5_context context, + const krb5_principal client_principal, + hdb_entry_ex *client, + hdb_entry_ex *server, + krb5_pac *pac) +{ + if (windcft == NULL) { + krb5_set_error_string(context, "Can't verify WINDC, no function"); + return EINVAL; + } + return (windcft->pac_verify)(windcctx, context, client_principal, client, server, pac); +} + +krb5_error_code +_kdc_windc_client_access(krb5_context context, + struct hdb_entry_ex *client, + KDC_REQ *req) +{ + if (windcft == NULL) + return 0; + return (windcft->client_access)(windcctx, context, client, req); +} diff --git a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h new file mode 100644 index 0000000000..a3b7534480 --- /dev/null +++ b/source4/heimdal/kdc/windc_plugin.h @@ -0,0 +1,80 @@ +/* + * 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,v 1.2 2007/01/04 11:13:51 lha Exp $ */ + +#ifndef HEIMDAL_KRB5_PAC_PLUGIN_H +#define HEIMDAL_KRB5_PAC_PLUGIN_H 1 + +#include <krb5.h> + +/* + * The PAC generate function should allocate a krb5_pac using + * krb5_pac_init and fill in the PAC structure for the principal using + * krb5_pac_add_buffer. + * + * The PAC verify function should verify all components in the PAC + * using krb5_pac_get_types and krb5_pac_get_buffer for all types. + * + * Check client access function check if the client is authorized. + */ + +struct hdb_entry_ex; + +typedef krb5_error_code +(*krb5plugin_windc_pac_generate)(void *, krb5_context, + struct hdb_entry_ex *, krb5_pac *); + +typedef krb5_error_code +(*krb5plugin_windc_pac_verify)(void *, krb5_context, + const krb5_principal, + struct hdb_entry_ex *, struct hdb_entry_ex *, krb5_pac *); + +typedef krb5_error_code +(*krb5plugin_windc_client_access)( + void *, krb5_context, struct hdb_entry_ex *, KDC_REQ *); + + +#define KRB5_WINDC_PLUGING_MINOR 2 + +typedef struct krb5plugin_windc_ftable { + int minor_version; + krb5_error_code (*init)(krb5_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_KRB5_PAC_PLUGIN_H */ + |