diff options
author | Andrew Bartlett <abartlet@samba.org> | 2007-01-10 01:57:32 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 14:37:20 -0500 |
commit | f7242f643763ccb6e10801af4ce53d0873e2d3e1 (patch) | |
tree | cd06665f49d12795e23699e6666d85da1f64d7bd /source4/heimdal/kdc | |
parent | 08976cb3d2adfe5ea90ed53e6aa6fa8161649f7a (diff) | |
download | samba-f7242f643763ccb6e10801af4ce53d0873e2d3e1.tar.gz samba-f7242f643763ccb6e10801af4ce53d0873e2d3e1.tar.bz2 samba-f7242f643763ccb6e10801af4ce53d0873e2d3e1.zip |
r20640: Commit part 2/2
Update Heimdal to match current lorikeet-heimdal. This includes
integrated PAC hooks, so Samba doesn't have to handle this any more.
This also brings in the PKINIT code, hence so many new files.
Andrew Bartlett
(This used to be commit 351f7040f7bb73b9a60b22b564686f7c2f98a729)
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 */ + |