diff options
author | Andrew Bartlett <abartlet@samba.org> | 2009-06-08 19:06:16 +1000 |
---|---|---|
committer | Andrew Bartlett <abartlet@samba.org> | 2009-06-12 07:45:48 +1000 |
commit | 9b261c008a395a323e0516f4cd3f3134aa050577 (patch) | |
tree | 91cf543ba7ccd560313bea52fa8678f0456e8485 /source4/heimdal/kdc | |
parent | 5cef57ff7d899773a084d23838b7f18a83f6e79d (diff) | |
download | samba-9b261c008a395a323e0516f4cd3f3134aa050577.tar.gz samba-9b261c008a395a323e0516f4cd3f3134aa050577.tar.bz2 samba-9b261c008a395a323e0516f4cd3f3134aa050577.zip |
s4:heimdal: import lorikeet-heimdal-200906080040 (commit 904d0124b46eed7a8ad6e5b73e892ff34b6865ba)
Also including the supporting changes required to pass make test
A number of heimdal functions and constants have changed since we last
imported a tree (for the better, but inconvenient for us).
Andrew Bartlett
Diffstat (limited to 'source4/heimdal/kdc')
-rw-r--r-- | source4/heimdal/kdc/524.c | 400 | ||||
-rw-r--r-- | source4/heimdal/kdc/default_config.c | 24 | ||||
-rw-r--r-- | source4/heimdal/kdc/digest.c | 17 | ||||
-rw-r--r-- | source4/heimdal/kdc/headers.h | 6 | ||||
-rw-r--r-- | source4/heimdal/kdc/kaserver.c | 4 | ||||
-rw-r--r-- | source4/heimdal/kdc/kdc.h | 16 | ||||
-rw-r--r-- | source4/heimdal/kdc/kdc_locl.h | 5 | ||||
-rw-r--r-- | source4/heimdal/kdc/kerberos4.c | 794 | ||||
-rw-r--r-- | source4/heimdal/kdc/kerberos5.c | 378 | ||||
-rw-r--r-- | source4/heimdal/kdc/krb5tgs.c | 62 | ||||
-rw-r--r-- | source4/heimdal/kdc/kx509.c | 25 | ||||
-rw-r--r-- | source4/heimdal/kdc/pkinit.c | 809 | ||||
-rw-r--r-- | source4/heimdal/kdc/process.c | 293 |
13 files changed, 1108 insertions, 1725 deletions
diff --git a/source4/heimdal/kdc/524.c b/source4/heimdal/kdc/524.c deleted file mode 100644 index d15310384a..0000000000 --- a/source4/heimdal/kdc/524.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * Copyright (c) 1997-2005 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$"); - -#include <krb5-v4compat.h> - -/* - * fetch the server from `t', returning the name in malloced memory in - * `spn' and the entry itself in `server' - */ - -static krb5_error_code -fetch_server (krb5_context context, - krb5_kdc_configuration *config, - const Ticket *t, - char **spn, - hdb_entry_ex **server, - const char *from) -{ - krb5_error_code ret; - krb5_principal sprinc; - - ret = _krb5_principalname2krb5_principal(context, &sprinc, - t->sname, t->realm); - if (ret) { - kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s", - krb5_get_err_text(context, ret)); - return ret; - } - ret = krb5_unparse_name(context, sprinc, spn); - if (ret) { - krb5_free_principal(context, sprinc); - kdc_log(context, config, 0, "krb5_unparse_name: %s", - krb5_get_err_text(context, ret)); - return ret; - } - ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER, - NULL, server); - krb5_free_principal(context, sprinc); - if (ret) { - kdc_log(context, config, 0, - "Request to convert ticket from %s for unknown principal %s: %s", - from, *spn, krb5_get_err_text(context, ret)); - if (ret == HDB_ERR_NOENTRY) - ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; - return ret; - } - return 0; -} - -static krb5_error_code -log_524 (krb5_context context, - krb5_kdc_configuration *config, - const EncTicketPart *et, - const char *from, - const char *spn) -{ - krb5_principal client; - char *cpn; - krb5_error_code ret; - - ret = _krb5_principalname2krb5_principal(context, &client, - et->cname, et->crealm); - if (ret) { - kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s", - krb5_get_err_text (context, ret)); - return ret; - } - ret = krb5_unparse_name(context, client, &cpn); - if (ret) { - krb5_free_principal(context, client); - kdc_log(context, config, 0, "krb5_unparse_name: %s", - krb5_get_err_text (context, ret)); - return ret; - } - kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn); - free(cpn); - krb5_free_principal(context, client); - return 0; -} - -static krb5_error_code -verify_flags (krb5_context context, - krb5_kdc_configuration *config, - const EncTicketPart *et, - const char *spn) -{ - if(et->endtime < kdc_time){ - kdc_log(context, config, 0, "Ticket expired (%s)", spn); - return KRB5KRB_AP_ERR_TKT_EXPIRED; - } - if(et->flags.invalid){ - kdc_log(context, config, 0, "Ticket not valid (%s)", spn); - return KRB5KRB_AP_ERR_TKT_NYV; - } - return 0; -} - -/* - * set the `et->caddr' to the most appropriate address to use, where - * `addr' is the address the request was received from. - */ - -static krb5_error_code -set_address (krb5_context context, - krb5_kdc_configuration *config, - EncTicketPart *et, - struct sockaddr *addr, - const char *from) -{ - krb5_error_code ret; - krb5_address *v4_addr; - - v4_addr = malloc (sizeof(*v4_addr)); - if (v4_addr == NULL) - return ENOMEM; - - ret = krb5_sockaddr2address(context, addr, v4_addr); - if(ret) { - free (v4_addr); - kdc_log(context, config, 0, "Failed to convert address (%s)", from); - return ret; - } - - if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) { - kdc_log(context, config, 0, "Incorrect network address (%s)", from); - krb5_free_address(context, v4_addr); - free (v4_addr); - return KRB5KRB_AP_ERR_BADADDR; - } - if(v4_addr->addr_type == KRB5_ADDRESS_INET) { - /* we need to collapse the addresses in the ticket to a - single address; best guess is to use the address the - connection came from */ - - if (et->caddr != NULL) { - free_HostAddresses(et->caddr); - } else { - et->caddr = malloc (sizeof (*et->caddr)); - if (et->caddr == NULL) { - krb5_free_address(context, v4_addr); - free(v4_addr); - return ENOMEM; - } - } - et->caddr->val = v4_addr; - et->caddr->len = 1; - } else { - krb5_free_address(context, v4_addr); - free(v4_addr); - } - return 0; -} - - -static krb5_error_code -encrypt_v4_ticket(krb5_context context, - krb5_kdc_configuration *config, - void *buf, - size_t len, - krb5_keyblock *skey, - EncryptedData *reply) -{ - krb5_crypto crypto; - krb5_error_code ret; - ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto); - if (ret) { - free(buf); - kdc_log(context, config, 0, "krb5_crypto_init failed: %s", - krb5_get_err_text(context, ret)); - return ret; - } - - ret = krb5_encrypt_EncryptedData(context, - crypto, - KRB5_KU_TICKET, - buf, - len, - 0, - reply); - krb5_crypto_destroy(context, crypto); - if(ret) { - kdc_log(context, config, 0, "Failed to encrypt data: %s", - krb5_get_err_text(context, ret)); - return ret; - } - return 0; -} - -static krb5_error_code -encode_524_response(krb5_context context, - krb5_kdc_configuration *config, - const char *spn, const EncTicketPart et, - const Ticket *t, hdb_entry_ex *server, - EncryptedData *ticket, int *kvno) -{ - krb5_error_code ret; - int use_2b; - size_t len; - - use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL); - if(use_2b) { - ASN1_MALLOC_ENCODE(EncryptedData, - ticket->cipher.data, ticket->cipher.length, - &t->enc_part, &len, ret); - - if (ret) { - kdc_log(context, config, 0, - "Failed to encode v4 (2b) ticket (%s)", spn); - return ret; - } - - ticket->etype = 0; - ticket->kvno = NULL; - *kvno = 213; /* 2b's use this magic kvno */ - } else { - unsigned char buf[MAX_KTXT_LEN + 4 * 4]; - Key *skey; - - if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) { - kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm, - t->realm); - return KRB5KDC_ERR_POLICY; - } - - ret = _kdc_encode_v4_ticket(context, config, - buf + sizeof(buf) - 1, sizeof(buf), - &et, &t->sname, &len); - if(ret){ - kdc_log(context, config, 0, - "Failed to encode v4 ticket (%s)", spn); - return ret; - } - ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); - if(ret){ - kdc_log(context, config, 0, - "no suitable DES key for server (%s)", spn); - return ret; - } - ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len, - &skey->key, ticket); - if(ret){ - kdc_log(context, config, 0, - "Failed to encrypt v4 ticket (%s)", spn); - return ret; - } - *kvno = server->entry.kvno; - } - - return 0; -} - -/* - * process a 5->4 request, based on `t', and received `from, addr', - * returning the reply in `reply' - */ - -krb5_error_code -_kdc_do_524(krb5_context context, - krb5_kdc_configuration *config, - const Ticket *t, krb5_data *reply, - const char *from, struct sockaddr *addr) -{ - krb5_error_code ret = 0; - krb5_crypto crypto; - hdb_entry_ex *server = NULL; - Key *skey; - krb5_data et_data; - EncTicketPart et; - EncryptedData ticket; - krb5_storage *sp; - char *spn = NULL; - unsigned char buf[MAX_KTXT_LEN + 4 * 4]; - size_t len; - int kvno = 0; - - if(!config->enable_524) { - ret = KRB5KDC_ERR_POLICY; - kdc_log(context, config, 0, - "Rejected ticket conversion request from %s", from); - goto out; - } - - ret = fetch_server (context, config, t, &spn, &server, from); - if (ret) { - goto out; - } - - ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey); - if(ret){ - kdc_log(context, config, 0, - "No suitable key found for server (%s) from %s", spn, from); - goto out; - } - ret = krb5_crypto_init(context, &skey->key, 0, &crypto); - if (ret) { - kdc_log(context, config, 0, "krb5_crypto_init failed: %s", - krb5_get_err_text(context, ret)); - goto out; - } - ret = krb5_decrypt_EncryptedData (context, - crypto, - KRB5_KU_TICKET, - &t->enc_part, - &et_data); - krb5_crypto_destroy(context, crypto); - if(ret){ - kdc_log(context, config, 0, - "Failed to decrypt ticket from %s for %s", from, spn); - goto out; - } - ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length, - &et, &len); - krb5_data_free(&et_data); - if(ret){ - kdc_log(context, config, 0, - "Failed to decode ticket from %s for %s", from, spn); - goto out; - } - - ret = log_524 (context, config, &et, from, spn); - if (ret) { - free_EncTicketPart(&et); - goto out; - } - - ret = verify_flags (context, config, &et, spn); - if (ret) { - free_EncTicketPart(&et); - goto out; - } - - ret = set_address (context, config, &et, addr, from); - if (ret) { - free_EncTicketPart(&et); - goto out; - } - - ret = encode_524_response(context, config, spn, et, t, - server, &ticket, &kvno); - free_EncTicketPart(&et); - - out: - /* make reply */ - memset(buf, 0, sizeof(buf)); - sp = krb5_storage_from_mem(buf, sizeof(buf)); - if (sp) { - krb5_store_int32(sp, ret); - if(ret == 0){ - krb5_store_int32(sp, kvno); - krb5_store_data(sp, ticket.cipher); - /* Aargh! This is coded as a KTEXT_ST. */ - krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR); - krb5_store_int32(sp, 0); /* mbz */ - free_EncryptedData(&ticket); - } - ret = krb5_storage_to_data(sp, reply); - reply->length = krb5_storage_seek(sp, 0, SEEK_CUR); - krb5_storage_free(sp); - } else - krb5_data_zero(reply); - if(spn) - free(spn); - if(server) - _kdc_free_ent (context, server); - return ret; -} diff --git a/source4/heimdal/kdc/default_config.c b/source4/heimdal/kdc/default_config.c index 60fbc92903..bf65af3cb9 100644 --- a/source4/heimdal/kdc/default_config.c +++ b/source4/heimdal/kdc/default_config.c @@ -84,6 +84,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) krb5_config_get_bool_default(context, NULL, c->enable_v4, "kdc", "enable-524", NULL); +#ifdef DIGEST c->enable_digest = krb5_config_get_bool_default(context, NULL, FALSE, @@ -110,7 +111,9 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) c->enable_digest = 0; } } +#endif +#ifdef KX509 c->enable_kx509 = krb5_config_get_bool_default(context, NULL, FALSE, @@ -129,6 +132,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) c->enable_kx509 = FALSE; } } +#endif c->check_ticket_addresses = krb5_config_get_bool_default(context, NULL, @@ -220,7 +224,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) "enable-pkinit", NULL); if (c->enable_pkinit) { - const char *user_id, *anchors, *ocsp_file; + const char *user_id, *anchors, *file; char **pool_list, **revoke_list; user_id = @@ -242,15 +246,23 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config) krb5_config_get_strings(context, NULL, "kdc", "pkinit_revoke", NULL); - ocsp_file = - krb5_config_get_string(context, NULL, - "kdc", "pkinit_kdc_ocsp", NULL); - if (ocsp_file) { - c->pkinit_kdc_ocsp_file = strdup(ocsp_file); + file = krb5_config_get_string(context, NULL, + "kdc", "pkinit_kdc_ocsp", NULL); + if (file) { + c->pkinit_kdc_ocsp_file = strdup(file); if (c->pkinit_kdc_ocsp_file == NULL) krb5_errx(context, 1, "out of memory"); } + file = krb5_config_get_string(context, NULL, + "kdc", "pkinit_kdc_friendly_name", NULL); + if (file) { + c->pkinit_kdc_friendly_name = strdup(file); + if (c->pkinit_kdc_friendly_name == NULL) + krb5_errx(context, 1, "out of memory"); + } + + _kdc_pk_initialize(context, c, user_id, anchors, pool_list, revoke_list); diff --git a/source4/heimdal/kdc/digest.c b/source4/heimdal/kdc/digest.c index 96986c1a87..d13507fc1f 100644 --- a/source4/heimdal/kdc/digest.c +++ b/source4/heimdal/kdc/digest.c @@ -34,7 +34,7 @@ #include "kdc_locl.h" #include <hex.h> -RCSID("$Id$"); +#ifdef DIGEST #define MS_CHAP_V2 0x20 #define CHAP_MD5 0x10 @@ -201,7 +201,7 @@ get_password_entry(krb5_context context, krb5_error_code _kdc_do_digest(krb5_context context, krb5_kdc_configuration *config, - const DigestREQ *req, krb5_data *reply, + const struct DigestREQ *req, krb5_data *reply, const char *from, struct sockaddr *addr) { krb5_error_code ret = 0; @@ -234,6 +234,7 @@ _kdc_do_digest(krb5_context context, memset(&ireq, 0, sizeof(ireq)); memset(&r, 0, sizeof(r)); memset(&rep, 0, sizeof(rep)); + memset(&res, 0, sizeof(res)); kdc_log(context, config, 0, "Digest request from %s", from); @@ -487,6 +488,7 @@ _kdc_do_digest(krb5_context context, hex_encode(buf.data, buf.length, &r.u.initReply.opaque); free(buf.data); + krb5_data_zero(&buf); if (r.u.initReply.opaque == NULL) { krb5_clear_error_message(context); ret = ENOMEM; @@ -539,8 +541,10 @@ _kdc_do_digest(krb5_context context, ret = decode_Checksum(buf.data, buf.length, &res, NULL); free(buf.data); + krb5_data_zero(&buf); if (ret) { - krb5_set_error_message(context, ret, "Failed to decode digest Checksum"); + krb5_set_error_message(context, ret, + "Failed to decode digest Checksum"); goto out; } @@ -582,6 +586,8 @@ _kdc_do_digest(krb5_context context, ret = krb5_verify_checksum(context, crypto, KRB5_KU_DIGEST_OPAQUE, buf.data, buf.length, &res); + free_Checksum(&res); + krb5_data_free(&buf); krb5_crypto_destroy(context, crypto); crypto = NULL; if (ret) @@ -1165,6 +1171,8 @@ _kdc_do_digest(krb5_context context, krb5_set_error_message(context, ret, "NTLM storage read flags"); goto out; } + krb5_storage_free(sp); + sp = NULL; krb5_data_free(&buf); if ((flags & NTLM_NEG_NTLM) == 0) { @@ -1450,9 +1458,12 @@ _kdc_do_digest(krb5_context context, free (client_name); krb5_data_free(&buf); krb5_data_free(&serverNonce); + free_Checksum(&res); free_DigestREP(&rep); free_DigestRepInner(&r); free_DigestReqInner(&ireq); return ret; } + +#endif /* DIGEST */ diff --git a/source4/heimdal/kdc/headers.h b/source4/heimdal/kdc/headers.h index 3635d3c56a..2240336e31 100644 --- a/source4/heimdal/kdc/headers.h +++ b/source4/heimdal/kdc/headers.h @@ -91,13 +91,19 @@ #include <parse_units.h> #include <krb5.h> #include <krb5_locl.h> +#ifdef DIGEST #include <digest_asn1.h> +#endif +#ifdef KX509 #include <kx509_asn1.h> +#endif #include <hdb.h> #include <hdb_err.h> #include <der.h> +#ifndef NO_NTLM #include <heimntlm.h> +#endif #include <windc_plugin.h> #undef ALLOC diff --git a/source4/heimdal/kdc/kaserver.c b/source4/heimdal/kdc/kaserver.c index 9226ae115d..3702ab9281 100644 --- a/source4/heimdal/kdc/kaserver.c +++ b/source4/heimdal/kdc/kaserver.c @@ -33,7 +33,7 @@ #include "kdc_locl.h" -RCSID("$Id$"); +#ifdef KRB4 #include <krb5-v4compat.h> #include <rx.h> @@ -949,3 +949,5 @@ out: krb5_storage_free (sp); return ret; } + +#endif /* KRB4 */ diff --git a/source4/heimdal/kdc/kdc.h b/source4/heimdal/kdc/kdc.h index 843bd5fa56..285a33af14 100644 --- a/source4/heimdal/kdc/kdc.h +++ b/source4/heimdal/kdc/kdc.h @@ -75,8 +75,10 @@ typedef struct krb5_kdc_configuration { krb5_boolean enable_pkinit; krb5_boolean pkinit_princ_in_cert; char *pkinit_kdc_ocsp_file; + char *pkinit_kdc_friendly_name; int pkinit_dh_min_bits; int pkinit_require_binding; + int pkinit_allow_proxy_certs; krb5_log_facility *logf; @@ -91,6 +93,20 @@ typedef struct krb5_kdc_configuration { } krb5_kdc_configuration; +struct krb5_kdc_service { + unsigned int flags; +#define KS_KRB5 1 +#define KS_NO_LENGTH 2 + krb5_error_code (*process)(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim); +}; + #include <kdc-protos.h> #endif diff --git a/source4/heimdal/kdc/kdc_locl.h b/source4/heimdal/kdc/kdc_locl.h index 8e34c50049..9b291ac896 100644 --- a/source4/heimdal/kdc/kdc_locl.h +++ b/source4/heimdal/kdc/kdc_locl.h @@ -42,6 +42,8 @@ #include "kdc.h" typedef struct pk_client_params pk_client_params; +struct DigestREQ; +struct Kx509Request; #include <kdc-private.h> extern sig_atomic_t exit_flag; @@ -52,9 +54,12 @@ extern krb5_addresses explicit_addresses; extern int enable_http; +#ifdef SUPPORT_DETACH + #define DETACH_IS_DEFAULT FALSE extern int detach_from_console; +#endif extern const struct units _kdc_digestunits[]; diff --git a/source4/heimdal/kdc/kerberos4.c b/source4/heimdal/kdc/kerberos4.c deleted file mode 100644 index 2bd2383940..0000000000 --- a/source4/heimdal/kdc/kerberos4.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - * Copyright (c) 1997 - 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 <krb5-v4compat.h> - -RCSID("$Id$"); - -#ifndef swap32 -static uint32_t -swap32(uint32_t x) -{ - return ((x << 24) & 0xff000000) | - ((x << 8) & 0xff0000) | - ((x >> 8) & 0xff00) | - ((x >> 24) & 0xff); -} -#endif /* swap32 */ - -int -_kdc_maybe_version4(unsigned char *buf, int len) -{ - return len > 0 && *buf == 4; -} - -static void -make_err_reply(krb5_context context, krb5_data *reply, - int code, const char *msg) -{ - _krb5_krb_cr_err_reply(context, "", "", "", - kdc_time, code, msg, reply); -} - -struct valid_princ_ctx { - krb5_kdc_configuration *config; - unsigned flags; -}; - -static krb5_boolean -valid_princ(krb5_context context, - void *funcctx, - krb5_principal princ) -{ - struct valid_princ_ctx *ctx = funcctx; - krb5_error_code ret; - char *s; - hdb_entry_ex *ent; - - ret = krb5_unparse_name(context, princ, &s); - if (ret) - return FALSE; - ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, &ent); - if (ret) { - kdc_log(context, ctx->config, 7, "Lookup %s failed: %s", s, - krb5_get_err_text (context, ret)); - free(s); - return FALSE; - } - kdc_log(context, ctx->config, 7, "Lookup %s succeeded", s); - free(s); - _kdc_free_ent(context, ent); - return TRUE; -} - -krb5_error_code -_kdc_db_fetch4(krb5_context context, - krb5_kdc_configuration *config, - const char *name, const char *instance, const char *realm, - unsigned flags, - hdb_entry_ex **ent) -{ - krb5_principal p; - krb5_error_code ret; - struct valid_princ_ctx ctx; - - ctx.config = config; - ctx.flags = flags; - - ret = krb5_425_conv_principal_ext2(context, name, instance, realm, - valid_princ, &ctx, 0, &p); - if(ret) - return ret; - ret = _kdc_db_fetch(context, config, p, flags, NULL, ent); - krb5_free_principal(context, p); - return ret; -} - -#define RCHECK(X, L) if(X){make_err_reply(context, reply, KFAILURE, "Packet too short"); goto L;} - -/* - * Process the v4 request in `buf, len' (received from `addr' - * (with string `from'). - * Return an error code and a reply in `reply'. - */ - -krb5_error_code -_kdc_do_version4(krb5_context context, - krb5_kdc_configuration *config, - unsigned char *buf, - size_t len, - krb5_data *reply, - const char *from, - struct sockaddr_in *addr) -{ - krb5_storage *sp; - krb5_error_code ret = EINVAL; - hdb_entry_ex *client = NULL, *server = NULL; - Key *ckey, *skey; - int8_t pvno; - int8_t msg_type; - int lsb; - char *name = NULL, *inst = NULL, *realm = NULL; - char *sname = NULL, *sinst = NULL; - int32_t req_time; - time_t max_life; - uint8_t life; - char client_name[256]; - char server_name[256]; - - if(!config->enable_v4) { - kdc_log(context, config, 0, - "Rejected version 4 request from %s", from); - make_err_reply(context, reply, KRB4ET_KDC_GEN_ERR, - "Function not enabled"); - return 0; - } - - sp = krb5_storage_from_mem(buf, len); - RCHECK(krb5_ret_int8(sp, &pvno), out); - if(pvno != 4){ - kdc_log(context, config, 0, - "Protocol version mismatch (krb4) (%d)", pvno); - make_err_reply(context, reply, KRB4ET_KDC_PKT_VER, "protocol mismatch"); - ret = KRB4ET_KDC_PKT_VER; - goto out; - } - RCHECK(krb5_ret_int8(sp, &msg_type), out); - lsb = msg_type & 1; - msg_type &= ~1; - switch(msg_type){ - case AUTH_MSG_KDC_REQUEST: { - krb5_data ticket, cipher; - krb5_keyblock session; - - krb5_data_zero(&ticket); - krb5_data_zero(&cipher); - - RCHECK(krb5_ret_stringz(sp, &name), out1); - RCHECK(krb5_ret_stringz(sp, &inst), out1); - RCHECK(krb5_ret_stringz(sp, &realm), out1); - RCHECK(krb5_ret_int32(sp, &req_time), out1); - if(lsb) - req_time = swap32(req_time); - RCHECK(krb5_ret_uint8(sp, &life), out1); - RCHECK(krb5_ret_stringz(sp, &sname), out1); - RCHECK(krb5_ret_stringz(sp, &sinst), out1); - snprintf (client_name, sizeof(client_name), - "%s.%s@%s", name, inst, realm); - snprintf (server_name, sizeof(server_name), - "%s.%s@%s", sname, sinst, config->v4_realm); - - kdc_log(context, config, 0, "AS-REQ (krb4) %s from %s for %s", - client_name, from, server_name); - - ret = _kdc_db_fetch4(context, config, name, inst, realm, - HDB_F_GET_CLIENT, &client); - if(ret) { - kdc_log(context, config, 0, "Client not found in database: %s: %s", - client_name, krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, - "principal unknown"); - goto out1; - } - ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm, - HDB_F_GET_SERVER, &server); - if(ret){ - kdc_log(context, config, 0, "Server not found in database: %s: %s", - server_name, krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, - "principal unknown"); - goto out1; - } - - ret = _kdc_check_flags (context, config, - client, client_name, - server, server_name, - TRUE); - if (ret) { - /* good error code? */ - make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP, - "operation not allowed"); - goto out1; - } - - if (config->enable_v4_per_principal && - client->entry.flags.allow_kerberos4 == 0) - { - kdc_log(context, config, 0, - "Per principal Kerberos 4 flag not turned on for %s", - client_name); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "allow kerberos4 flag required"); - goto out1; - } - - /* - * There's no way to do pre-authentication in v4 and thus no - * good error code to return if preauthentication is required. - */ - - if (config->require_preauth - || client->entry.flags.require_preauth - || server->entry.flags.require_preauth) { - kdc_log(context, config, 0, - "Pre-authentication required for v4-request: " - "%s for %s", - client_name, server_name); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "preauth required"); - goto out1; - } - - ret = _kdc_get_des_key(context, client, FALSE, FALSE, &ckey); - if(ret){ - kdc_log(context, config, 0, "no suitable DES key for client"); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "no suitable DES key for client"); - goto out1; - } - - ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); - if(ret){ - kdc_log(context, config, 0, "no suitable DES key for server"); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "no suitable DES key for server"); - goto out1; - } - - max_life = _krb5_krb_life_to_time(0, life); - if(client->entry.max_life) - max_life = min(max_life, *client->entry.max_life); - if(server->entry.max_life) - max_life = min(max_life, *server->entry.max_life); - - life = krb_time_to_life(kdc_time, kdc_time + max_life); - - ret = krb5_generate_random_keyblock(context, - ETYPE_DES_PCBC_NONE, - &session); - if (ret) { - make_err_reply(context, reply, KFAILURE, - "Not enough random i KDC"); - goto out1; - } - - ret = _krb5_krb_create_ticket(context, - 0, - name, - inst, - config->v4_realm, - addr->sin_addr.s_addr, - &session, - life, - kdc_time, - sname, - sinst, - &skey->key, - &ticket); - if (ret) { - krb5_free_keyblock_contents(context, &session); - make_err_reply(context, reply, KFAILURE, - "failed to create v4 ticket"); - goto out1; - } - - ret = _krb5_krb_create_ciph(context, - &session, - sname, - sinst, - config->v4_realm, - life, - server->entry.kvno % 255, - &ticket, - kdc_time, - &ckey->key, - &cipher); - krb5_free_keyblock_contents(context, &session); - krb5_data_free(&ticket); - if (ret) { - make_err_reply(context, reply, KFAILURE, - "Failed to create v4 cipher"); - goto out1; - } - - ret = _krb5_krb_create_auth_reply(context, - name, - inst, - realm, - req_time, - 0, - client->entry.pw_end ? *client->entry.pw_end : 0, - client->entry.kvno % 256, - &cipher, - reply); - krb5_data_free(&cipher); - - out1: - break; - } - case AUTH_MSG_APPL_REQUEST: { - struct _krb5_krb_auth_data ad; - int8_t kvno; - int8_t ticket_len; - int8_t req_len; - krb5_data auth; - int32_t address; - size_t pos; - krb5_principal tgt_princ = NULL; - hdb_entry_ex *tgt = NULL; - Key *tkey; - time_t max_end, actual_end, issue_time; - - memset(&ad, 0, sizeof(ad)); - krb5_data_zero(&auth); - - RCHECK(krb5_ret_int8(sp, &kvno), out2); - RCHECK(krb5_ret_stringz(sp, &realm), out2); - - ret = krb5_425_conv_principal(context, "krbtgt", realm, - config->v4_realm, - &tgt_princ); - if(ret){ - kdc_log(context, config, 0, - "Converting krbtgt principal (krb4): %s", - krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KFAILURE, - "Failed to convert v4 principal (krbtgt)"); - goto out2; - } - - ret = _kdc_db_fetch(context, config, tgt_princ, - HDB_F_GET_KRBTGT, NULL, &tgt); - if(ret){ - char *s; - s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not " - "found in database (krb4): krbtgt.%s@%s: %s", - realm, config->v4_realm, - krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KFAILURE, s); - free(s); - goto out2; - } - - if(tgt->entry.kvno % 256 != kvno){ - kdc_log(context, config, 0, - "tgs-req (krb4) with old kvno %d (current %d) for " - "krbtgt.%s@%s", kvno, tgt->entry.kvno % 256, - realm, config->v4_realm); - make_err_reply(context, reply, KRB4ET_KDC_AUTH_EXP, - "old krbtgt kvno used"); - goto out2; - } - - ret = _kdc_get_des_key(context, tgt, TRUE, FALSE, &tkey); - if(ret){ - kdc_log(context, config, 0, - "no suitable DES key for krbtgt (krb4)"); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "no suitable DES key for krbtgt"); - goto out2; - } - - RCHECK(krb5_ret_int8(sp, &ticket_len), out2); - RCHECK(krb5_ret_int8(sp, &req_len), out2); - - pos = krb5_storage_seek(sp, ticket_len + req_len, SEEK_CUR); - - auth.data = buf; - auth.length = pos; - - if (config->check_ticket_addresses) - address = addr->sin_addr.s_addr; - else - address = 0; - - ret = _krb5_krb_rd_req(context, &auth, "krbtgt", realm, - config->v4_realm, - address, &tkey->key, &ad); - if(ret){ - kdc_log(context, config, 0, "krb_rd_req: %d", ret); - make_err_reply(context, reply, ret, "failed to parse request"); - goto out2; - } - - RCHECK(krb5_ret_int32(sp, &req_time), out2); - if(lsb) - req_time = swap32(req_time); - RCHECK(krb5_ret_uint8(sp, &life), out2); - RCHECK(krb5_ret_stringz(sp, &sname), out2); - RCHECK(krb5_ret_stringz(sp, &sinst), out2); - snprintf (server_name, sizeof(server_name), - "%s.%s@%s", - sname, sinst, config->v4_realm); - snprintf (client_name, sizeof(client_name), - "%s.%s@%s", - ad.pname, ad.pinst, ad.prealm); - - kdc_log(context, config, 0, "TGS-REQ (krb4) %s from %s for %s", - client_name, from, server_name); - - if(strcmp(ad.prealm, realm)){ - kdc_log(context, config, 0, - "Can't hop realms (krb4) %s -> %s", realm, ad.prealm); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, - "Can't hop realms"); - goto out2; - } - - if (!config->enable_v4_cross_realm && strcmp(realm, config->v4_realm) != 0) { - kdc_log(context, config, 0, - "krb4 Cross-realm %s -> %s disabled", - realm, config->v4_realm); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, - "Can't hop realms"); - goto out2; - } - - if(strcmp(sname, "changepw") == 0){ - kdc_log(context, config, 0, - "Bad request for changepw ticket (krb4)"); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, - "Can't authorize password change based on TGT"); - goto out2; - } - - ret = _kdc_db_fetch4(context, config, ad.pname, ad.pinst, ad.prealm, - HDB_F_GET_CLIENT, &client); - if(ret && ret != HDB_ERR_NOENTRY) { - char *s; - s = kdc_log_msg(context, config, 0, - "Client not found in database: (krb4) %s: %s", - client_name, krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s); - free(s); - goto out2; - } - if (client == NULL && strcmp(ad.prealm, config->v4_realm) == 0) { - char *s; - s = kdc_log_msg(context, config, 0, - "Local client not found in database: (krb4) " - "%s", client_name); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s); - free(s); - goto out2; - } - - ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm, - HDB_F_GET_SERVER, &server); - if(ret){ - char *s; - s = kdc_log_msg(context, config, 0, - "Server not found in database (krb4): %s: %s", - server_name, krb5_get_err_text(context, ret)); - make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s); - free(s); - goto out2; - } - - ret = _kdc_check_flags (context, config, - client, client_name, - server, server_name, - FALSE); - if (ret) { - make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP, - "operation not allowed"); - goto out2; - } - - ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); - if(ret){ - kdc_log(context, config, 0, - "no suitable DES key for server (krb4)"); - make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, - "no suitable DES key for server"); - goto out2; - } - - max_end = _krb5_krb_life_to_time(ad.time_sec, ad.life); - max_end = min(max_end, _krb5_krb_life_to_time(kdc_time, life)); - if(server->entry.max_life) - max_end = min(max_end, kdc_time + *server->entry.max_life); - if(client && client->entry.max_life) - max_end = min(max_end, kdc_time + *client->entry.max_life); - life = min(life, krb_time_to_life(kdc_time, max_end)); - - issue_time = kdc_time; - actual_end = _krb5_krb_life_to_time(issue_time, life); - while (actual_end > max_end && life > 1) { - /* move them into the next earlier lifetime bracket */ - life--; - actual_end = _krb5_krb_life_to_time(issue_time, life); - } - if (actual_end > max_end) { - /* if life <= 1 and it's still too long, backdate the ticket */ - issue_time -= actual_end - max_end; - } - - { - krb5_data ticket, cipher; - krb5_keyblock session; - - krb5_data_zero(&ticket); - krb5_data_zero(&cipher); - - ret = krb5_generate_random_keyblock(context, - ETYPE_DES_PCBC_NONE, - &session); - if (ret) { - make_err_reply(context, reply, KFAILURE, - "Not enough random i KDC"); - goto out2; - } - - ret = _krb5_krb_create_ticket(context, - 0, - ad.pname, - ad.pinst, - ad.prealm, - addr->sin_addr.s_addr, - &session, - life, - issue_time, - sname, - sinst, - &skey->key, - &ticket); - if (ret) { - krb5_free_keyblock_contents(context, &session); - make_err_reply(context, reply, KFAILURE, - "failed to create v4 ticket"); - goto out2; - } - - ret = _krb5_krb_create_ciph(context, - &session, - sname, - sinst, - config->v4_realm, - life, - server->entry.kvno % 255, - &ticket, - issue_time, - &ad.session, - &cipher); - krb5_free_keyblock_contents(context, &session); - if (ret) { - make_err_reply(context, reply, KFAILURE, - "failed to create v4 cipher"); - goto out2; - } - - ret = _krb5_krb_create_auth_reply(context, - ad.pname, - ad.pinst, - ad.prealm, - req_time, - 0, - 0, - 0, - &cipher, - reply); - krb5_data_free(&cipher); - } - out2: - _krb5_krb_free_auth_data(context, &ad); - if(tgt_princ) - krb5_free_principal(context, tgt_princ); - if(tgt) - _kdc_free_ent(context, tgt); - break; - } - case AUTH_MSG_ERR_REPLY: - ret = EINVAL; - break; - default: - kdc_log(context, config, 0, "Unknown message type (krb4): %d from %s", - msg_type, from); - - make_err_reply(context, reply, KFAILURE, "Unknown message type"); - ret = EINVAL; - } - out: - if(name) - free(name); - if(inst) - free(inst); - if(realm) - free(realm); - if(sname) - free(sname); - if(sinst) - free(sinst); - if(client) - _kdc_free_ent(context, client); - if(server) - _kdc_free_ent(context, server); - krb5_storage_free(sp); - return ret; -} - -krb5_error_code -_kdc_encode_v4_ticket(krb5_context context, - krb5_kdc_configuration *config, - void *buf, size_t len, const EncTicketPart *et, - const PrincipalName *service, size_t *size) -{ - krb5_storage *sp; - krb5_error_code ret; - char name[40], inst[40], realm[40]; - char sname[40], sinst[40]; - - { - krb5_principal princ; - _krb5_principalname2krb5_principal(context, - &princ, - *service, - et->crealm); - ret = krb5_524_conv_principal(context, - princ, - sname, - sinst, - realm); - krb5_free_principal(context, princ); - if(ret) - return ret; - - _krb5_principalname2krb5_principal(context, - &princ, - et->cname, - et->crealm); - - ret = krb5_524_conv_principal(context, - princ, - name, - inst, - realm); - krb5_free_principal(context, princ); - } - if(ret) - return ret; - - sp = krb5_storage_emem(); - - krb5_store_int8(sp, 0); /* flags */ - krb5_store_stringz(sp, name); - krb5_store_stringz(sp, inst); - krb5_store_stringz(sp, realm); - { - unsigned char tmp[4] = { 0, 0, 0, 0 }; - int i; - if(et->caddr){ - for(i = 0; i < et->caddr->len; i++) - if(et->caddr->val[i].addr_type == AF_INET && - et->caddr->val[i].address.length == 4){ - memcpy(tmp, et->caddr->val[i].address.data, 4); - break; - } - } - krb5_storage_write(sp, tmp, sizeof(tmp)); - } - - if((et->key.keytype != ETYPE_DES_CBC_MD5 && - et->key.keytype != ETYPE_DES_CBC_MD4 && - et->key.keytype != ETYPE_DES_CBC_CRC) || - et->key.keyvalue.length != 8) - return -1; - krb5_storage_write(sp, et->key.keyvalue.data, 8); - - { - time_t start = et->starttime ? *et->starttime : et->authtime; - krb5_store_int8(sp, krb_time_to_life(start, et->endtime)); - krb5_store_int32(sp, start); - } - - krb5_store_stringz(sp, sname); - krb5_store_stringz(sp, sinst); - - { - krb5_data data; - krb5_storage_to_data(sp, &data); - krb5_storage_free(sp); - *size = (data.length + 7) & ~7; /* pad to 8 bytes */ - if(*size > len) - return -1; - memset((unsigned char*)buf - *size + 1, 0, *size); - memcpy((unsigned char*)buf - *size + 1, data.data, data.length); - krb5_data_free(&data); - } - return 0; -} - -krb5_error_code -_kdc_get_des_key(krb5_context context, - hdb_entry_ex *principal, krb5_boolean is_server, - krb5_boolean prefer_afs_key, Key **ret_key) -{ - Key *v5_key = NULL, *v4_key = NULL, *afs_key = NULL, *server_key = NULL; - int i; - krb5_enctype etypes[] = { ETYPE_DES_CBC_MD5, - ETYPE_DES_CBC_MD4, - ETYPE_DES_CBC_CRC }; - - for(i = 0; - i < sizeof(etypes)/sizeof(etypes[0]) - && (v5_key == NULL || v4_key == NULL || - afs_key == NULL || server_key == NULL); - ++i) { - Key *key = NULL; - while(hdb_next_enctype2key(context, &principal->entry, etypes[i], &key) == 0) { - if(key->salt == NULL) { - if(v5_key == NULL) - v5_key = key; - } else if(key->salt->type == hdb_pw_salt && - key->salt->salt.length == 0) { - if(v4_key == NULL) - v4_key = key; - } else if(key->salt->type == hdb_afs3_salt) { - if(afs_key == NULL) - afs_key = key; - } else if(server_key == NULL) - server_key = key; - } - } - - if(prefer_afs_key) { - if(afs_key) - *ret_key = afs_key; - else if(v4_key) - *ret_key = v4_key; - else if(v5_key) - *ret_key = v5_key; - else if(is_server && server_key) - *ret_key = server_key; - else - return KRB4ET_KDC_NULL_KEY; - } else { - if(v4_key) - *ret_key = v4_key; - else if(afs_key) - *ret_key = afs_key; - else if(v5_key) - *ret_key = v5_key; - else if(is_server && server_key) - *ret_key = server_key; - else - return KRB4ET_KDC_NULL_KEY; - } - - if((*ret_key)->key.keyvalue.length == 0) - return KRB4ET_KDC_NULL_KEY; - return 0; -} - diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c index c715e0812f..941a2e0572 100644 --- a/source4/heimdal/kdc/kerberos5.c +++ b/source4/heimdal/kdc/kerberos5.c @@ -90,7 +90,7 @@ _kdc_find_padata(const KDC_REQ *req, int *start, int type) */ krb5_boolean -_kdc_is_weak_expection(krb5_principal principal, krb5_enctype etype) +_kdc_is_weak_exception(krb5_principal principal, krb5_enctype etype) { if (principal->name.name_string.len > 0 && strcmp(principal->name.name_string.val[0], "afs") == 0 && @@ -139,7 +139,7 @@ _kdc_find_etype(krb5_context context, const hdb_entry_ex *princ, Key *key = NULL; if (krb5_enctype_valid(context, etypes[i]) != 0 && - !_kdc_is_weak_expection(princ->entry.principal, etypes[i])) + !_kdc_is_weak_exception(princ->entry.principal, etypes[i])) continue; while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) { @@ -260,7 +260,7 @@ _kdc_encode_reply(krb5_context context, KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek, krb5_enctype etype, int skvno, const EncryptionKey *skey, - int ckvno, const EncryptionKey *ckey, + int ckvno, const EncryptionKey *reply_key, const char **e_text, krb5_data *reply) { @@ -321,7 +321,7 @@ _kdc_encode_reply(krb5_context context, *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } - ret = krb5_crypto_init(context, ckey, 0, &crypto); + ret = krb5_crypto_init(context, reply_key, 0, &crypto); if (ret) { free(buf); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", @@ -394,18 +394,6 @@ older_enctype(krb5_enctype enctype) } } -static int -only_older_enctype_p(const KDC_REQ *req) -{ - int i; - - for(i = 0; i < req->req_body.etype.len; i++) { - if (!older_enctype(req->req_body.etype.val[i])) - return 0; - } - return 1; -} - /* * */ @@ -461,72 +449,23 @@ make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key) static krb5_error_code get_pa_etype_info(krb5_context context, krb5_kdc_configuration *config, - METHOD_DATA *md, hdb_entry *client, - ENCTYPE *etypes, unsigned int etypes_len) + METHOD_DATA *md, Key *ckey) { krb5_error_code ret = 0; - int i, j; - unsigned int n = 0; ETYPE_INFO pa; unsigned char *buf; size_t len; - pa.len = client->keys.len; - if(pa.len > UINT_MAX/sizeof(*pa.val)) - return ERANGE; - pa.val = malloc(pa.len * sizeof(*pa.val)); + pa.len = 1; + pa.val = calloc(1, sizeof(pa.val[0])); if(pa.val == NULL) return ENOMEM; - memset(pa.val, 0, pa.len * sizeof(*pa.val)); - - for(i = 0; i < client->keys.len; i++) { - for (j = 0; j < n; j++) - if (pa.val[j].etype == client->keys.val[i].key.keytype) - goto skip1; - for(j = 0; j < etypes_len; j++) { - if(client->keys.val[i].key.keytype == etypes[j]) { - if (krb5_enctype_valid(context, etypes[j]) != 0) - continue; - if (!older_enctype(etypes[j])) - continue; - if (n >= pa.len) - krb5_abortx(context, "internal error: n >= p.len"); - if((ret = make_etype_info_entry(context, - &pa.val[n++], - &client->keys.val[i])) != 0) { - free_ETYPE_INFO(&pa); - return ret; - } - break; - } - } - skip1:; - } - for(i = 0; i < client->keys.len; i++) { - /* already added? */ - for(j = 0; j < etypes_len; j++) { - if(client->keys.val[i].key.keytype == etypes[j]) - goto skip2; - } - if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0) - continue; - if (!older_enctype(etypes[j])) - continue; - if (n >= pa.len) - krb5_abortx(context, "internal error: n >= p.len"); - if((ret = make_etype_info_entry(context, - &pa.val[n++], - &client->keys.val[i])) != 0) { - free_ETYPE_INFO(&pa); - return ret; - } - skip2:; - } - if(n < pa.len) { - /* stripped out dups, newer enctypes, and not valid enctypes */ - pa.len = n; + ret = make_etype_info_entry(context, &pa.val[0], ckey); + if (ret) { + free_ETYPE_INFO(&pa); + return ret; } ASN1_MALLOC_ENCODE(ETYPE_INFO, buf, len, &pa, &len, ret); @@ -623,66 +562,22 @@ make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key) static krb5_error_code get_pa_etype_info2(krb5_context context, krb5_kdc_configuration *config, - METHOD_DATA *md, hdb_entry *client, - ENCTYPE *etypes, unsigned int etypes_len) + METHOD_DATA *md, Key *ckey) { krb5_error_code ret = 0; - int i, j; - unsigned int n = 0; ETYPE_INFO2 pa; unsigned char *buf; size_t len; - pa.len = client->keys.len; - if(pa.len > UINT_MAX/sizeof(*pa.val)) - return ERANGE; - pa.val = malloc(pa.len * sizeof(*pa.val)); + pa.len = 1; + pa.val = calloc(1, sizeof(pa.val[0])); if(pa.val == NULL) return ENOMEM; - memset(pa.val, 0, pa.len * sizeof(*pa.val)); - - for(i = 0; i < client->keys.len; i++) { - for (j = 0; j < n; j++) - if (pa.val[j].etype == client->keys.val[i].key.keytype) - goto skip1; - for(j = 0; j < etypes_len; j++) { - if(client->keys.val[i].key.keytype == etypes[j]) { - if (krb5_enctype_valid(context, etypes[j]) != 0) - continue; - if (n >= pa.len) - krb5_abortx(context, "internal error: n >= p.len"); - if((ret = make_etype_info2_entry(&pa.val[n++], - &client->keys.val[i])) != 0) { - free_ETYPE_INFO2(&pa); - return ret; - } - break; - } - } - skip1:; - } - /* send enctypes that the client doesn't know about too */ - for(i = 0; i < client->keys.len; i++) { - /* already added? */ - for(j = 0; j < etypes_len; j++) { - if(client->keys.val[i].key.keytype == etypes[j]) - goto skip2; - } - if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0) - continue; - if (n >= pa.len) - krb5_abortx(context, "internal error: n >= p.len"); - if((ret = make_etype_info2_entry(&pa.val[n++], - &client->keys.val[i])) != 0) { - free_ETYPE_INFO2(&pa); - return ret; - } - skip2:; - } - if(n < pa.len) { - /* stripped out dups, and not valid enctypes */ - pa.len = n; + ret = make_etype_info2_entry(&pa.val[0], ckey); + if (ret) { + free_ETYPE_INFO2(&pa); + return ret; } ASN1_MALLOC_ENCODE(ETYPE_INFO2, buf, len, &pa, &len, ret); @@ -712,10 +607,12 @@ log_as_req(krb5_context context, const KDC_REQ_BODY *b) { krb5_error_code ret; - struct rk_strpool *p = NULL; + struct rk_strpool *p; char *str; int i; + p = rk_strpoolprintf(NULL, "%s", "Client supported enctypes: "); + for (i = 0; i < b->etype.len; i++) { ret = krb5_enctype_to_string(context, b->etype.val[i], &str); if (ret == 0) { @@ -733,10 +630,6 @@ log_as_req(krb5_context context, 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; @@ -745,21 +638,26 @@ log_as_req(krb5_context context, if(ret == 0) { ret = krb5_enctype_to_string(context, setype, &set); if (ret == 0) { - kdc_log(context, config, 5, "Using %s/%s", cet, set); + p = rk_strpoolprintf(p, ", using %s/%s", cet, set); free(set); } free(cet); } if (ret != 0) - kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype); + p = rk_strpoolprintf(p, ", using enctypes %d/%d", + cetype, setype); } + str = rk_strpoolcollect(p); + kdc_log(context, config, 0, "%s", str); + free(str); + { char fixedstr[128]; unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(), fixedstr, sizeof(fixedstr)); if(*fixedstr) - kdc_log(context, config, 2, "Requested flags: %s", fixedstr); + kdc_log(context, config, 0, "Requested flags: %s", fixedstr); } } @@ -956,6 +854,17 @@ send_pac_p(krb5_context context, KDC_REQ *req) return TRUE; } +krb5_boolean +_kdc_is_anonymous(krb5_context context, krb5_principal principal) +{ + if (principal->name.name_type != KRB5_NT_WELLKNOWN || + principal->name.name_string.len != 2 || + strcmp(principal->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 || + strcmp(principal->name.name_string.val[1], KRB5_ANON_NAME) != 0) + return 0; + return 1; +} + /* * */ @@ -1039,6 +948,7 @@ _kdc_as_rep(krb5_context context, if (ret) goto out; } + ret = krb5_unparse_name(context, client_princ, &client_name); } if (ret) { @@ -1050,6 +960,28 @@ _kdc_as_rep(krb5_context context, kdc_log(context, config, 0, "AS-REQ %s from %s for %s", client_name, from, server_name); + /* + * + */ + + if (_kdc_is_anonymous(context, client_princ)) { + if (!b->kdc_options.request_anonymous) { + kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto out; + } + } else if (b->kdc_options.request_anonymous) { + kdc_log(context, config, 0, + "Request for a anonymous ticket with non " + "anonymous client name: %s", client_name); + ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; + goto out; + } + + /* + * + */ + ret = _kdc_db_fetch(context, config, client_princ, HDB_F_GET_CLIENT | flags, NULL, &client); if(ret){ @@ -1069,19 +1001,27 @@ _kdc_as_rep(krb5_context context, goto out; } - ret = _kdc_windc_client_access(context, client, req, &e_data); - if(ret) - goto out; + memset(&et, 0, sizeof(et)); + memset(&ek, 0, sizeof(ek)); - ret = _kdc_check_flags(context, config, - client, client_name, - server, server_name, - TRUE); - if(ret) + /* + * Find the client key for reply encryption and pa-type salt, Pick + * the client key upfront before the other keys because that is + * going to affect what enctypes we are going to use in + * ETYPE-INFO{,2}. + */ + + ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len, + &ckey, &cetype); + if (ret) { + kdc_log(context, config, 0, + "Client (%s) has no support for etypes", client_name); goto out; + } - memset(&et, 0, sizeof(et)); - memset(&ek, 0, sizeof(ek)); + /* + * Pre-auth processing + */ if(req->padata){ int i; @@ -1097,17 +1037,15 @@ _kdc_as_rep(krb5_context context, e_text = "No PKINIT PA found"; i = 0; - if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ))) - ; + pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ); if (pa == NULL) { i = 0; - if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN))) - ; + pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN); } if (pa) { char *client_cert = NULL; - ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp); + ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; kdc_log(context, config, 5, @@ -1127,11 +1065,12 @@ _kdc_as_rep(krb5_context context, e_text = "PKINIT certificate not allowed to " "impersonate principal"; _kdc_pk_free_client_param(context, pkp); - + kdc_log(context, config, 0, "%s", e_text); pkp = NULL; goto out; } + found_pa = 1; et.flags.pre_authent = 1; kdc_log(context, config, 0, @@ -1158,6 +1097,12 @@ _kdc_as_rep(krb5_context context, found_pa = 1; + if (b->kdc_options.request_anonymous) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + kdc_log(context, config, 0, "ENC-TS doesn't support anon"); + goto out; + } + ret = decode_EncryptedData(pa->padata_value.data, pa->padata_value.length, &enc_data, @@ -1206,6 +1151,11 @@ _kdc_as_rep(krb5_context context, &enc_data, &ts_data); krb5_crypto_destroy(context, crypto); + /* + * Since the user might have several keys with the same + * enctype but with diffrent salting, we need to try all + * the keys with the same enctype. + */ if(ret){ krb5_error_code ret2; ret2 = krb5_enctype_to_string(context, @@ -1298,6 +1248,7 @@ _kdc_as_rep(krb5_context context, goto out; } }else if (config->require_preauth + || b->kdc_options.request_anonymous /* hack to force anon */ || client->entry.flags.require_preauth || server->entry.flags.require_preauth) { METHOD_DATA method_data; @@ -1310,6 +1261,10 @@ _kdc_as_rep(krb5_context context, method_data.val = NULL; ret = realloc_method_data(&method_data); + if (ret) { + free_METHOD_DATA(&method_data); + goto out; + } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; pa->padata_value.length = 0; @@ -1317,12 +1272,20 @@ _kdc_as_rep(krb5_context context, #ifdef PKINIT ret = realloc_method_data(&method_data); + if (ret) { + free_METHOD_DATA(&method_data); + goto out; + } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ; pa->padata_value.length = 0; pa->padata_value.data = NULL; ret = realloc_method_data(&method_data); + if (ret) { + free_METHOD_DATA(&method_data); + goto out; + } pa = &method_data.val[method_data.len-1]; pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; pa->padata_value.length = 0; @@ -1330,22 +1293,37 @@ _kdc_as_rep(krb5_context context, #endif /* - * RFC4120 requires: - * - If the client only knows about old enctypes, then send - * both info replies (we send 'info' first in the list). - * - If the client is 'modern', because it knows about 'new' - * enctype types, then only send the 'info2' reply. + * If there is a client key, send ETYPE_INFO{,2} */ - - /* XXX check ret */ - if (only_older_enctype_p(req)) - ret = get_pa_etype_info(context, config, - &method_data, &client->entry, - b->etype.val, b->etype.len); - /* XXX check ret */ - ret = get_pa_etype_info2(context, config, &method_data, - &client->entry, b->etype.val, b->etype.len); - + if (ckey) { + + /* + * RFC4120 requires: + * - If the client only knows about old enctypes, then send + * both info replies (we send 'info' first in the list). + * - If the client is 'modern', because it knows about 'new' + * enctype types, then only send the 'info2' reply. + * + * Before we send the full list of etype-info data, we pick + * the client key we would have used anyway below, just pick + * that instead. + */ + + if (older_enctype(ckey->key.keytype)) { + ret = get_pa_etype_info(context, config, + &method_data, ckey); + if (ret) { + free_METHOD_DATA(&method_data); + goto out; + } + } + ret = get_pa_etype_info2(context, config, + &method_data, ckey); + if (ret) { + free_METHOD_DATA(&method_data); + goto out; + } + } ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); free_METHOD_DATA(&method_data); @@ -1363,20 +1341,26 @@ _kdc_as_rep(krb5_context context, } /* - * Find the client key (for preauth ENC-TS verification and reply - * encryption). Then the best encryption type for the KDC and - * last the best session key that shared between the client and - * KDC runtime enctypes. + * Verify flags after the user been required to prove its identity + * with in a preauth mech. */ - ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len, - &ckey, &cetype); - if (ret) { - kdc_log(context, config, 0, - "Client (%s) has no support for etypes", client_name); + ret = _kdc_check_flags(context, config, + client, client_name, + server, server_name, + TRUE); + if(ret) goto out; - } - + + ret = _kdc_windc_client_access(context, client, req, &e_data); + if(ret) + goto out; + + /* + * Selelct the best encryption type for the KDC with out regard to + * the client since the client never needs to read that data. + */ + ret = _kdc_get_preferred_key(context, config, server, server_name, &setype, &skey); @@ -1449,12 +1433,14 @@ _kdc_as_rep(krb5_context context, rep.pvno = 5; rep.msg_type = krb_as_rep; - copy_Realm(&client->entry.principal->realm, &rep.crealm); - if (f.request_anonymous) - _kdc_make_anonymous_principalname (&rep.cname); - else - _krb5_principal2principalname(&rep.cname, - client->entry.principal); + + ret = copy_Realm(&client->entry.principal->realm, &rep.crealm); + if (ret) + goto out; + ret = _krb5_principal2principalname(&rep.cname, client->entry.principal); + if (ret) + goto out; + rep.ticket.tkt_vno = 5; copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, @@ -1500,11 +1486,12 @@ _kdc_as_rep(krb5_context context, goto out; } - ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); + ret = copy_PrincipalName(&rep.cname, &et.cname); + if (ret) + goto out; + ret = copy_Realm(&rep.crealm, &et.crealm); if (ret) goto out; - copy_PrincipalName(&rep.cname, &et.cname); - copy_Realm(&rep.crealm, &et.crealm); { time_t start; @@ -1568,8 +1555,6 @@ _kdc_as_rep(krb5_context context, et.transited.tr_type = DOMAIN_X500_COMPRESS; krb5_data_zero(&et.transited.contents); - copy_EncryptionKey(&et.key, &ek.key); - /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus * incapable of correctly decoding SEQUENCE OF's of zero length. @@ -1637,12 +1622,12 @@ _kdc_as_rep(krb5_context context, rep.padata->len = 0; rep.padata->val = NULL; - reply_key = &ckey->key; #if PKINIT if (pkp) { + e_text = "Failed to build PK-INIT reply"; ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, - req, req_buffer, - &reply_key, rep.padata); + sessionetype, req, req_buffer, + &reply_key, &et.key, rep.padata); if (ret) goto out; ret = _kdc_add_inital_verified_cas(context, @@ -1651,10 +1636,25 @@ _kdc_as_rep(krb5_context context, &et); if (ret) goto out; - } + } else #endif + if (ckey) { + reply_key = &ckey->key; + ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); + if (ret) + goto out; + } else { + e_text = "Client have no reply key"; + ret = KRB5KDC_ERR_CLIENT_NOTYET; + goto out; + } + + ret = copy_EncryptionKey(&et.key, &ek.key); + if (ret) + goto out; - set_salt_padata (rep.padata, ckey->salt); + if (ckey) + set_salt_padata (rep.padata, ckey->salt); /* Add signing of alias referral */ if (f.canonicalize) { diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c index 4cf93e5a54..3abdb18ae4 100644 --- a/source4/heimdal/kdc/krb5tgs.c +++ b/source4/heimdal/kdc/krb5tgs.c @@ -107,7 +107,7 @@ _kdc_add_KRB5SignedPath(krb5_context context, hdb_entry_ex *krbtgt, krb5_enctype enctype, krb5_const_principal server, - KRB5SignedPathPrincipals *principals, + krb5_principals principals, EncTicketPart *tkt) { krb5_error_code ret; @@ -117,7 +117,7 @@ _kdc_add_KRB5SignedPath(krb5_context context, size_t size; if (server && principals) { - ret = add_KRB5SignedPathPrincipals(principals, server); + ret = add_Principals(principals, server); if (ret) return ret; } @@ -186,7 +186,7 @@ check_KRB5SignedPath(krb5_context context, krb5_kdc_configuration *config, hdb_entry_ex *krbtgt, EncTicketPart *tkt, - KRB5SignedPathPrincipals **delegated, + krb5_principals *delegated, int *signedpath) { krb5_error_code ret; @@ -255,7 +255,7 @@ check_KRB5SignedPath(krb5_context context, return ENOMEM; } - ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated); + ret = copy_Principals(*delegated, sp.delegated); if (ret) { free_KRB5SignedPath(&sp); free(*delegated); @@ -668,7 +668,7 @@ tgs_make_reply(krb5_context context, krb5_principal client_principal, hdb_entry_ex *krbtgt, krb5_enctype krbtgt_etype, - KRB5SignedPathPrincipals *spp, + krb5_principals spp, const krb5_data *rspac, const METHOD_DATA *enc_pa_data, const char **e_text, @@ -725,14 +725,13 @@ tgs_make_reply(krb5_context context, PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) || GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK), &tgt->transited, &et, - *krb5_princ_realm(context, client_principal), - *krb5_princ_realm(context, server->entry.principal), - *krb5_princ_realm(context, krbtgt->entry.principal)); + krb5_principal_get_realm(context, client_principal), + krb5_principal_get_realm(context, server->entry.principal), + krb5_principal_get_realm(context, krbtgt->entry.principal)); if(ret) goto out; - copy_Realm(krb5_princ_realm(context, server_principal), - &rep.ticket.realm); + copy_Realm(&server_principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, server_principal); copy_Realm(&tgt_name->realm, &rep.crealm); /* @@ -888,7 +887,7 @@ tgs_make_reply(krb5_context context, } if (krb5_enctype_valid(context, et.key.keytype) != 0 - && _kdc_is_weak_expection(server->entry.principal, et.key.keytype)) + && _kdc_is_weak_exception(server->entry.principal, et.key.keytype)) { krb5_enctype_enable(context, et.key.keytype); is_weak = 1; @@ -1035,7 +1034,7 @@ need_referral(krb5_context context, krb5_kdc_configuration *config, if (server->name.name_string.len == 1) name = server->name.name_string.val[0]; - if (server->name.name_string.len > 1) + else if (server->name.name_string.len > 1) name = server->name.name_string.val[1]; else return FALSE; @@ -1205,9 +1204,7 @@ tgs_parse_request(krb5_context context, krb5_keyblock *subkey; krb5_data ad; - ret = krb5_auth_con_getremotesubkey(context, - ac, - &subkey); + ret = krb5_auth_con_getremotesubkey(context, ac, &subkey); if(ret){ krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "Failed to get remote subkey: %s", @@ -1232,6 +1229,7 @@ tgs_parse_request(krb5_context context, goto out; } ret = krb5_crypto_init(context, subkey, 0, &crypto); + krb5_free_keyblock(context, subkey); if (ret) { krb5_auth_con_free(context, ac); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", @@ -1251,7 +1249,6 @@ tgs_parse_request(krb5_context context, ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ goto out; } - krb5_free_keyblock(context, subkey); ALLOC(*auth_data); if (*auth_data == NULL) { krb5_auth_con_free(context, ac); @@ -1365,8 +1362,7 @@ tgs_build_reply(krb5_context context, const char *from, const char **e_text, AuthorizationData **auth_data, - const struct sockaddr *from_addr, - int datagram_reply) + const struct sockaddr *from_addr) { krb5_error_code ret; krb5_principal cp = NULL, sp = NULL; @@ -1375,13 +1371,11 @@ tgs_build_reply(krb5_context context, hdb_entry_ex *server = NULL, *client = NULL; krb5_realm ref_realm = NULL; EncTicketPart *tgt = &ticket->ticket; - KRB5SignedPathPrincipals *spp = NULL; - Key *tkey; + krb5_principals spp = NULL; const EncryptionKey *ekey; krb5_keyblock sessionkey; krb5_kvno kvno; krb5_data rspac; - int cross_realm = 0; METHOD_DATA enc_pa_data; @@ -1392,6 +1386,8 @@ tgs_build_reply(krb5_context context, char opt_str[128]; int signedpath = 0; + Key *tkey; + memset(&sessionkey, 0, sizeof(sessionkey)); memset(&adtkt, 0, sizeof(adtkt)); krb5_data_zero(&rspac); @@ -1559,8 +1555,6 @@ server_lookup: kdc_log(context, config, 1, "Client not found in database: %s: %s", cpn, krb5_get_err_text(context, ret)); - - cross_realm = 1; } /* @@ -1578,9 +1572,10 @@ server_lookup: break; if(i == b->etype.len) { kdc_log(context, config, 0, - "Addition ticket have not matching etypes", spp); + "Addition ticket have not matching etypes"); krb5_clear_error_message(context); - return KRB5KDC_ERR_ETYPE_NOSUPP; + ret = KRB5KDC_ERR_ETYPE_NOSUPP; + goto out; } etype = b->etype.val[i]; kvno = 0; @@ -1592,7 +1587,7 @@ server_lookup: if(ret) { kdc_log(context, config, 0, "Server (%s) has no support for etypes", spn); - return ret; + goto out; } ekey = &skey->key; kvno = server->entry.kvno; @@ -1604,10 +1599,6 @@ server_lookup: } /* - * Validate authoriation data - */ - - /* * Check that service is in the same realm as the krbtgt. If it's * not the same, it's someone that is using a uni-directional trust * backward. @@ -1628,13 +1619,15 @@ server_lookup: goto out; } - /* check PAC if there is one */ + /* + * Validate authoriation data + */ ret = hdb_enctype2key(context, &krbtgt->entry, krbtgt_etype, &tkey); if(ret) { kdc_log(context, config, 0, - "Failed to find key for krbtgt PAC check"); + "Failed to find key for krbtgt PAC check"); goto out; } @@ -1672,7 +1665,7 @@ server_lookup: const PA_DATA *sdata; int i = 0; - sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF); + sdata = _kdc_find_padata(req, &i, KRB5_PADATA_FOR_USER); if (sdata) { krb5_crypto crypto; krb5_data datack; @@ -2044,8 +2037,7 @@ _kdc_tgs_rep(krb5_context context, from, &e_text, &auth_data, - from_addr, - datagram_reply); + from_addr); if (ret) { kdc_log(context, config, 0, "Failed building TGS-REP to %s", from); diff --git a/source4/heimdal/kdc/kx509.c b/source4/heimdal/kdc/kx509.c index 83e05b81c5..8f7f3a27fb 100644 --- a/source4/heimdal/kdc/kx509.c +++ b/source4/heimdal/kdc/kx509.c @@ -36,14 +36,14 @@ #include <rfc2459_asn1.h> #include <hx509.h> -RCSID("$Id$"); +#ifdef KX509 /* * */ krb5_error_code -_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size) +_kdc_try_kx509_request(void *ptr, size_t len, struct Kx509Request *req, size_t *size) { if (len < 4) return -1; @@ -97,16 +97,15 @@ calculate_reply_hash(krb5_context context, krb5_keyblock *key, Kx509Response *rep) { + krb5_error_code ret; HMAC_CTX ctx; HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, - key->keyvalue.data, key->keyvalue.length, + 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) { + ret = krb5_data_alloc(rep->hash, HMAC_size(&ctx)); + if (ret) { HMAC_CTX_cleanup(&ctx); krb5_set_error_message(context, ENOMEM, "malloc: out of memory"); return ENOMEM; @@ -208,7 +207,7 @@ build_certificate(krb5_context context, spki.subjectPublicKey.data = key->data; spki.subjectPublicKey.length = key->length * 8; - ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(), + ret = der_copy_oid(&asn1_oid_id_pkcs1_rsaEncryption, &spki.algorithm.algorithm); any.data = "\x05\x00"; @@ -289,7 +288,7 @@ out: krb5_error_code _kdc_do_kx509(krb5_context context, krb5_kdc_configuration *config, - const Kx509Request *req, krb5_data *reply, + const struct Kx509Request *req, krb5_data *reply, const char *from, struct sockaddr *addr) { krb5_error_code ret; @@ -385,8 +384,10 @@ _kdc_do_kx509(krb5_context context, if (ret) goto out; free_RSAPublicKey(&key); - if (size != req->pk_key.length) - ; + if (size != req->pk_key.length) { + ret = ASN1_EXTRA_DATA; + goto out; + } } ALLOC(rep.certificate); @@ -458,3 +459,5 @@ out: return 0; } + +#endif /* KX509 */ diff --git a/source4/heimdal/kdc/pkinit.c b/source4/heimdal/kdc/pkinit.c index 82358682d8..22734be811 100644 --- a/source4/heimdal/kdc/pkinit.c +++ b/source4/heimdal/kdc/pkinit.c @@ -47,14 +47,26 @@ RCSID("$Id$"); struct pk_client_params { enum krb5_pk_type type; - BIGNUM *dh_public_key; + enum { USE_RSA, USE_DH, USE_ECDH } keyex; + union { + struct { + BIGNUM *public_key; + DH *key; + } dh; +#ifdef HAVE_OPENSSL + struct { + EC_KEY *public_key; + EC_KEY *key; + } ecdh; +#endif + } u; hx509_cert cert; unsigned nonce; - DH *dh; EncryptionKey reply_key; char *dh_group_name; hx509_peer_info peer; hx509_certs client_anchors; + hx509_verify_ctx verify_ctx; }; struct pk_principal_mapping { @@ -155,29 +167,43 @@ out: } void -_kdc_pk_free_client_param(krb5_context context, - pk_client_params *client_params) +_kdc_pk_free_client_param(krb5_context context, pk_client_params *cp) { - if (client_params->cert) - hx509_cert_free(client_params->cert); - if (client_params->dh) - DH_free(client_params->dh); - if (client_params->dh_public_key) - BN_free(client_params->dh_public_key); - 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); + if (cp == NULL) + return; + if (cp->cert) + hx509_cert_free(cp->cert); + if (cp->verify_ctx) + hx509_verify_destroy_ctx(cp->verify_ctx); + if (cp->keyex == USE_DH) { + if (cp->u.dh.key) + DH_free(cp->u.dh.key); + if (cp->u.dh.public_key) + BN_free(cp->u.dh.public_key); + } +#ifdef HAVE_OPENSSL + if (cp->keyex == USE_ECDH) { + if (cp->u.ecdh.key) + EC_KEY_free(cp->u.ecdh.key); + if (cp->u.ecdh.public_key) + EC_KEY_free(cp->u.ecdh.public_key); + } +#endif + krb5_free_keyblock_contents(context, &cp->reply_key); + if (cp->dh_group_name) + free(cp->dh_group_name); + if (cp->peer) + hx509_peer_info_free(cp->peer); + if (cp->client_anchors) + hx509_certs_free(&cp->client_anchors); + memset(cp, 0, sizeof(*cp)); + free(cp); } static krb5_error_code -generate_dh_keyblock(krb5_context context, pk_client_params *client_params, - krb5_enctype enctype, krb5_keyblock *reply_key) +generate_dh_keyblock(krb5_context context, + pk_client_params *client_params, + krb5_enctype enctype) { unsigned char *dh_gen_key = NULL; krb5_keyblock key; @@ -186,36 +212,84 @@ generate_dh_keyblock(krb5_context context, pk_client_params *client_params, memset(&key, 0, sizeof(key)); - if (!DH_generate_key(client_params->dh)) { - ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, "Can't generate Diffie-Hellman keys"); - goto out; - } - if (client_params->dh_public_key == NULL) { - ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, "dh_public_key"); - goto out; - } + if (client_params->keyex == USE_DH) { - dh_gen_keylen = DH_size(client_params->dh); - size = BN_num_bytes(client_params->dh->p); - if (size < dh_gen_keylen) - size = dh_gen_keylen; + if (client_params->u.dh.public_key == NULL) { + ret = KRB5KRB_ERR_GENERIC; + krb5_set_error_message(context, ret, "public_key"); + goto out; + } - dh_gen_key = malloc(size); - if (dh_gen_key == NULL) { - ret = ENOMEM; - krb5_set_error_message(context, ret, "malloc: out of memory"); - goto out; - } - memset(dh_gen_key, 0, size - dh_gen_keylen); + if (!DH_generate_key(client_params->u.dh.key)) { + ret = KRB5KRB_ERR_GENERIC; + krb5_set_error_message(context, ret, + "Can't generate Diffie-Hellman keys"); + goto out; + } + + dh_gen_keylen = DH_size(client_params->u.dh.key); + size = BN_num_bytes(client_params->u.dh.key->p); + if (size < dh_gen_keylen) + size = dh_gen_keylen; + + dh_gen_key = malloc(size); + if (dh_gen_key == NULL) { + ret = ENOMEM; + krb5_set_error_message(context, ret, "malloc: out of memory"); + goto out; + } + memset(dh_gen_key, 0, size - dh_gen_keylen); + + dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), + client_params->u.dh.public_key, + client_params->u.dh.key); + if (dh_gen_keylen == -1) { + ret = KRB5KRB_ERR_GENERIC; + krb5_set_error_message(context, ret, + "Can't compute Diffie-Hellman key"); + goto out; + } + ret = 0; +#ifdef HAVE_OPENSSL + } else if (client_params->keyex == USE_ECDH) { + + if (client_params->u.ecdh.public_key == NULL) { + ret = KRB5KRB_ERR_GENERIC; + krb5_set_error_message(context, ret, "public_key"); + goto out; + } + + client_params->u.ecdh.key = EC_KEY_new(); + if (client_params->u.ecdh.key == NULL) { + ret = ENOMEM; + goto out; + } + EC_KEY_set_group(client_params->u.ecdh.key, + EC_KEY_get0_group(client_params->u.ecdh.public_key)); - dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen), - client_params->dh_public_key, - client_params->dh); - if (dh_gen_keylen == -1) { + if (EC_KEY_generate_key(client_params->u.ecdh.key) != 1) { + ret = ENOMEM; + goto out; + } + + size = (EC_GROUP_get_degree(EC_KEY_get0_group(client_params->u.ecdh.key)) + 7) / 8; + dh_gen_key = malloc(size); + if (dh_gen_key == NULL) { + ret = ENOMEM; + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + goto out; + } + + dh_gen_keylen = ECDH_compute_key(dh_gen_key, size, + EC_KEY_get0_public_key(client_params->u.ecdh.public_key), + client_params->u.ecdh.key, NULL); + ret = 0; +#endif /* HAVE_OPENSSL */ + } else { ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, "Can't compute Diffie-Hellman key"); + krb5_set_error_message(context, ret, + "Diffie-Hellman not selected keys"); goto out; } @@ -223,7 +297,7 @@ generate_dh_keyblock(krb5_context context, pk_client_params *client_params, enctype, dh_gen_key, dh_gen_keylen, NULL, NULL, - reply_key); + &client_params->reply_key); out: if (dh_gen_key) @@ -261,10 +335,12 @@ get_dh_param(krb5_context context, memset(&dhparam, 0, sizeof(dhparam)); - if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) { - krb5_set_error_message(context, KRB5_BADMSGTYPE, - "PKINIT invalid oid in clientPublicValue"); - return KRB5_BADMSGTYPE; + if ((dh_key_info->subjectPublicKey.length % 8) != 0) { + ret = KRB5_BADMSGTYPE; + krb5_set_error_message(context, ret, + "PKINIT: subjectPublicKey not aligned " + "to 8 bit boundary"); + goto out; } if (dh_key_info->algorithm.parameters == NULL) { @@ -284,15 +360,6 @@ get_dh_param(krb5_context context, goto out; } - if ((dh_key_info->subjectPublicKey.length % 8) != 0) { - ret = KRB5_BADMSGTYPE; - krb5_set_error_message(context, ret, - "PKINIT: subjectPublicKey not aligned " - "to 8 bit boundary"); - goto out; - } - - ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits, &dhparam.p, &dhparam.g, &dhparam.q, moduli, &client_params->dh_group_name); @@ -331,17 +398,17 @@ get_dh_param(krb5_context context, return ret; } - client_params->dh_public_key = integer_to_BN(context, - "subjectPublicKey", - &glue); + client_params->u.dh.public_key = integer_to_BN(context, + "subjectPublicKey", + &glue); der_free_heim_integer(&glue); - if (client_params->dh_public_key == NULL) { + if (client_params->u.dh.public_key == NULL) { ret = KRB5_BADMSGTYPE; goto out; } } - client_params->dh = dh; + client_params->u.dh.key = dh; dh = NULL; ret = 0; @@ -352,20 +419,88 @@ get_dh_param(krb5_context context, return ret; } +#ifdef HAVE_OPENSSL + +static krb5_error_code +get_ecdh_param(krb5_context context, + krb5_kdc_configuration *config, + SubjectPublicKeyInfo *dh_key_info, + pk_client_params *client_params) +{ + ECParameters ecp; + EC_KEY *public = NULL; + krb5_error_code ret; + const unsigned char *p; + size_t len; + int nid; + + if (dh_key_info->algorithm.parameters == NULL) { + krb5_set_error_message(context, KRB5_BADMSGTYPE, + "PKINIT missing algorithm parameter " + "in clientPublicValue"); + return KRB5_BADMSGTYPE; + } + + memset(&ecp, 0, sizeof(ecp)); + + ret = decode_ECParameters(dh_key_info->algorithm.parameters->data, + dh_key_info->algorithm.parameters->length, &ecp, &len); + if (ret) + goto out; + + if (ecp.element != choice_ECParameters_namedCurve) { + ret = KRB5_BADMSGTYPE; + goto out; + } + + if (der_heim_oid_cmp(&ecp.u.namedCurve, &asn1_oid_id_ec_group_secp256r1) == 0) + nid = NID_X9_62_prime256v1; + else { + ret = KRB5_BADMSGTYPE; + goto out; + } + + /* XXX verify group is ok */ + + public = EC_KEY_new_by_curve_name(nid); + + p = dh_key_info->subjectPublicKey.data; + len = dh_key_info->subjectPublicKey.length / 8; + if (o2i_ECPublicKey(&public, &p, len) == NULL) { + ret = KRB5_BADMSGTYPE; + krb5_set_error_message(context, ret, + "PKINIT failed to decode ECDH key"); + goto out; + } + client_params->u.ecdh.public_key = public; + public = NULL; + + out: + if (public) + EC_KEY_free(public); + free_ECParameters(&ecp); + return ret; +} + +#endif /* HAVE_OPENSSL */ + krb5_error_code _kdc_pk_rd_padata(krb5_context context, krb5_kdc_configuration *config, const KDC_REQ *req, const PA_DATA *pa, + hdb_entry_ex *client, pk_client_params **ret_params) { - pk_client_params *client_params; + pk_client_params *cp; krb5_error_code ret; heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL }; krb5_data eContent = { 0, NULL }; krb5_data signed_content = { 0, NULL }; const char *type = "unknown type"; + hx509_certs trust_anchors; int have_data = 0; + const HDB_Ext_PKINIT_cert *pc; *ret_params = NULL; @@ -375,20 +510,73 @@ _kdc_pk_rd_padata(krb5_context context, return 0; } - hx509_verify_set_time(kdc_identity->verify_ctx, kdc_time); - - client_params = calloc(1, sizeof(*client_params)); - if (client_params == NULL) { + cp = calloc(1, sizeof(*cp)); + if (cp == NULL) { krb5_clear_error_message(context); ret = ENOMEM; goto out; } + ret = hx509_certs_init(kdc_identity->hx509ctx, + "MEMORY:trust-anchors", + 0, NULL, &trust_anchors); + if (ret) { + krb5_set_error_message(context, ret, "failed to create trust anchors"); + goto out; + } + + ret = hx509_certs_merge(kdc_identity->hx509ctx, trust_anchors, + kdc_identity->anchors); + if (ret) { + hx509_certs_free(&trust_anchors); + krb5_set_error_message(context, ret, "failed to create verify context"); + goto out; + } + + /* Add any registered certificates for this client as trust anchors */ + ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); + if (ret == 0 && pc != NULL) { + hx509_cert cert; + unsigned int i; + + for (i = 0; i < pc->len; i++) { + ret = hx509_cert_init_data(kdc_identity->hx509ctx, + pc->val[i].cert.data, + pc->val[i].cert.length, + &cert); + if (ret) + continue; + hx509_certs_add(kdc_identity->hx509ctx, trust_anchors, cert); + hx509_cert_free(cert); + } + } + + ret = hx509_verify_init_ctx(kdc_identity->hx509ctx, &cp->verify_ctx); + if (ret) { + hx509_certs_free(&trust_anchors); + krb5_set_error_message(context, ret, "failed to create verify context"); + goto out; + } + + hx509_verify_set_time(cp->verify_ctx, kdc_time); + hx509_verify_attach_anchors(cp->verify_ctx, trust_anchors); + hx509_certs_free(&trust_anchors); + + if (config->pkinit_allow_proxy_certs) + hx509_verify_set_proxy_certificate(cp->verify_ctx, 1); + if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) { PA_PK_AS_REQ_Win2k r; type = "PK-INIT-Win2k"; + if (req->req_body.kdc_options.request_anonymous) { + ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; + krb5_set_error_message(context, ret, + "Anon not supported in RSA mode"); + goto out; + } + ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data, pa->padata_value.length, &r, @@ -406,7 +594,7 @@ _kdc_pk_rd_padata(krb5_context context, free_PA_PK_AS_REQ_Win2k(&r); if (ret) { krb5_set_error_message(context, ret, - "Can't decode PK-AS-REQ: %d", ret); + "Can't unwrap ContentInfo(win): %d", ret); goto out; } @@ -420,25 +608,35 @@ _kdc_pk_rd_padata(krb5_context context, &r, NULL); if (ret) { - krb5_set_error_message(context, ret, "Can't decode PK-AS-REQ: %d", ret); + krb5_set_error_message(context, ret, + "Can't decode PK-AS-REQ: %d", ret); goto out; } /* XXX look at r.kdcPkId */ if (r.trustedCertifiers) { ExternalPrincipalIdentifiers *edi = r.trustedCertifiers; - unsigned int i; + unsigned int i, maxedi; ret = hx509_certs_init(kdc_identity->hx509ctx, "MEMORY:client-anchors", 0, NULL, - &client_params->client_anchors); + &cp->client_anchors); if (ret) { - krb5_set_error_message(context, ret, "Can't allocate client anchors: %d", ret); + krb5_set_error_message(context, ret, + "Can't allocate client anchors: %d", + ret); goto out; } - for (i = 0; i < edi->len; i++) { + /* + * If the client sent more then 10 EDI, don't bother + * looking more then 10 of performance reasons. + */ + maxedi = edi->len; + if (maxedi > 10) + maxedi = 10; + for (i = 0; i < maxedi; i++) { IssuerAndSerialNumber iasn; hx509_query *q; hx509_cert cert; @@ -464,8 +662,10 @@ _kdc_pk_rd_padata(krb5_context context, } ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber); free_IssuerAndSerialNumber(&iasn); - if (ret) + if (ret) { + hx509_query_free(kdc_identity->hx509ctx, q); continue; + } ret = hx509_certs_find(kdc_identity->hx509ctx, kdc_identity->certs, @@ -475,7 +675,7 @@ _kdc_pk_rd_padata(krb5_context context, if (ret) continue; hx509_certs_add(kdc_identity->hx509ctx, - client_params->client_anchors, cert); + cp->client_anchors, cert); hx509_cert_free(cert); } } @@ -497,7 +697,7 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData()); + ret = der_heim_oid_cmp(&contentInfoOid, &asn1_oid_id_pkcs7_signedData); if (ret != 0) { ret = KRB5KRB_ERR_GENERIC; krb5_set_error_message(context, ret, @@ -514,9 +714,14 @@ _kdc_pk_rd_padata(krb5_context context, { hx509_certs signer_certs; + int flags = HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH; /* BTMM */ + + if (req->req_body.kdc_options.request_anonymous) + flags |= HX509_CMS_VS_ALLOW_ZERO_SIGNER; ret = hx509_cms_verify_signed(kdc_identity->hx509ctx, - kdc_identity->verify_ctx, + cp->verify_ctx, + flags, signed_content.data, signed_content.length, NULL, @@ -532,16 +737,18 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs, - &client_params->cert); - hx509_certs_free(&signer_certs); + if (signer_certs) { + ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs, + &cp->cert); + hx509_certs_free(&signer_certs); + } if (ret) goto out; } /* Signature is correct, now verify the signed message */ - if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 && - der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0) + if (der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkcs7_data) != 0 && + der_heim_oid_cmp(&eContentType, &asn1_oid_id_pkauthdata) != 0) { ret = KRB5_BADMSGTYPE; krb5_set_error_message(context, ret, "got wrong oid for pkauthdata"); @@ -556,7 +763,8 @@ _kdc_pk_rd_padata(krb5_context context, &ap, NULL); if (ret) { - krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret); + krb5_set_error_message(context, ret, + "Can't decode AuthPack: %d", ret); goto out; } @@ -568,12 +776,13 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - client_params->type = PKINIT_WIN2K; - client_params->nonce = ap.pkAuthenticator.nonce; + cp->type = PKINIT_WIN2K; + cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, "DH not supported for windows"); + krb5_set_error_message(context, ret, + "DH not supported for windows"); goto out; } free_AuthPack_Win2k(&ap); @@ -586,11 +795,21 @@ _kdc_pk_rd_padata(krb5_context context, &ap, NULL); if (ret) { - krb5_set_error_message(context, ret, "can't decode AuthPack: %d", ret); + krb5_set_error_message(context, ret, + "Can't decode AuthPack: %d", ret); free_AuthPack(&ap); goto out; } + if (req->req_body.kdc_options.request_anonymous && + ap.clientPublicValue == NULL) { + free_AuthPack(&ap); + ret = KRB5_KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED; + krb5_set_error_message(context, ret, + "Anon not supported in RSA mode"); + goto out; + } + ret = pk_check_pkauthenticator(context, &ap.pkAuthenticator, req); @@ -599,33 +818,55 @@ _kdc_pk_rd_padata(krb5_context context, goto out; } - client_params->type = PKINIT_27; - client_params->nonce = ap.pkAuthenticator.nonce; + cp->type = PKINIT_27; + cp->nonce = ap.pkAuthenticator.nonce; if (ap.clientPublicValue) { - ret = get_dh_param(context, config, - ap.clientPublicValue, client_params); + if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_dhpublicnumber) == 0) { + cp->keyex = USE_DH; + ret = get_dh_param(context, config, + ap.clientPublicValue, cp); +#ifdef HAVE_OPENSSL + } else if (der_heim_oid_cmp(&ap.clientPublicValue->algorithm.algorithm, &asn1_oid_id_ecPublicKey) == 0) { + cp->keyex = USE_ECDH; + ret = get_ecdh_param(context, config, + ap.clientPublicValue, cp); +#endif /* HAVE_OPENSSL */ + } else { + ret = KRB5_BADMSGTYPE; + krb5_set_error_message(context, ret, "PKINIT unknown DH mechanism"); + } if (ret) { free_AuthPack(&ap); goto out; } - } + } else + cp->keyex = USE_RSA; + ret = hx509_peer_info_alloc(kdc_identity->hx509ctx, + &cp->peer); + if (ret) { + free_AuthPack(&ap); + 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, + cp->peer, ap.supportedCMSTypes->val, ap.supportedCMSTypes->len); if (ret) { free_AuthPack(&ap); goto out; } + } else { + /* assume old client */ + hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, + hx509_crypto_des_rsdi_ede3_cbc()); + hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, + hx509_signature_rsa_with_sha1()); + hx509_peer_info_add_cms_alg(kdc_identity->hx509ctx, cp->peer, + hx509_signature_sha1()); } free_AuthPack(&ap); } else @@ -642,10 +883,10 @@ out: krb5_data_free(&eContent); der_free_oid(&eContentType); der_free_oid(&contentInfoOid); - if (ret) - _kdc_pk_free_client_param(context, client_params); - else - *ret_params = client_params; + if (ret) { + _kdc_pk_free_client_param(context, cp); + } else + *ret_params = cp; return ret; } @@ -670,11 +911,12 @@ BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer) static krb5_error_code pk_mk_pa_reply_enckey(krb5_context context, krb5_kdc_configuration *config, - pk_client_params *client_params, + pk_client_params *cp, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock *reply_key, - ContentInfo *content_info) + ContentInfo *content_info, + hx509_cert *kdc_cert) { const heim_oid *envelopedAlg = NULL, *sdAlg = NULL, *evAlg = NULL; krb5_error_code ret; @@ -685,13 +927,15 @@ pk_mk_pa_reply_enckey(krb5_context context, krb5_data_zero(&buf); krb5_data_zero(&signed_data); + *kdc_cert = NULL; + /* * If the message client is a win2k-type but it send pa data * 09-binding it expects a IETF (checksum) reply so there can be * no replay attacks. */ - switch (client_params->type) { + switch (cp->type) { case PKINIT_WIN2K: { int i = 0; if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL @@ -699,14 +943,14 @@ pk_mk_pa_reply_enckey(krb5_context context, { do_win2k = 1; } - sdAlg = oid_id_pkcs7_data(); - evAlg = oid_id_pkcs7_data(); - envelopedAlg = oid_id_rsadsi_des_ede3_cbc(); + sdAlg = &asn1_oid_id_pkcs7_data; + evAlg = &asn1_oid_id_pkcs7_data; + envelopedAlg = &asn1_oid_id_rsadsi_des_ede3_cbc; break; } case PKINIT_27: - sdAlg = oid_id_pkrkeydata(); - evAlg = oid_id_pkcs7_signedData(); + sdAlg = &asn1_oid_id_pkrkeydata; + evAlg = &asn1_oid_id_pkcs7_signedData; break; default: krb5_abortx(context, "internal pkinit error"); @@ -721,7 +965,7 @@ pk_mk_pa_reply_enckey(krb5_context context, krb5_clear_error_message(context); goto out; } - kp.nonce = client_params->nonce; + kp.nonce = cp->nonce; ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k, buf.data, buf.length, @@ -777,7 +1021,8 @@ pk_mk_pa_reply_enckey(krb5_context context, goto out; hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); - hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); + if (config->pkinit_kdc_friendly_name) + hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(kdc_identity->hx509ctx, kdc_identity->certs, @@ -794,19 +1039,19 @@ pk_mk_pa_reply_enckey(krb5_context context, buf.length, NULL, cert, - client_params->peer, - client_params->client_anchors, + cp->peer, + cp->client_anchors, kdc_identity->certpool, &signed_data); - hx509_cert_free(cert); + *kdc_cert = cert; } krb5_data_free(&buf); if (ret) goto out; - if (client_params->type == PKINIT_WIN2K) { - ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(), + if (cp->type == PKINIT_WIN2K) { + ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &signed_data, &buf); if (ret) @@ -816,8 +1061,8 @@ pk_mk_pa_reply_enckey(krb5_context context, } ret = hx509_cms_envelope_1(kdc_identity->hx509ctx, - 0, - client_params->cert, + HX509_CMS_EV_NO_KU_CHECK, + cp->cert, signed_data.data, signed_data.length, envelopedAlg, evAlg, &buf); @@ -826,9 +1071,14 @@ pk_mk_pa_reply_enckey(krb5_context context, ret = _krb5_pk_mk_ContentInfo(context, &buf, - oid_id_pkcs7_envelopedData(), + &asn1_oid_id_pkcs7_envelopedData, content_info); out: + if (ret && *kdc_cert) { + hx509_cert_free(*kdc_cert); + *kdc_cert = NULL; + } + krb5_data_free(&buf); krb5_data_free(&signed_data); return ret; @@ -840,9 +1090,8 @@ out: static krb5_error_code pk_mk_pa_reply_dh(krb5_context context, - DH *kdc_dh, - pk_client_params *client_params, - krb5_keyblock *reply_key, + krb5_kdc_configuration *config, + pk_client_params *cp, ContentInfo *content_info, hx509_cert *kdc_cert) { @@ -850,33 +1099,63 @@ pk_mk_pa_reply_dh(krb5_context context, krb5_data signed_data, buf; ContentInfo contentinfo; krb5_error_code ret; + hx509_cert cert; + hx509_query *q; size_t size; - heim_integer i; memset(&contentinfo, 0, sizeof(contentinfo)); memset(&dh_info, 0, sizeof(dh_info)); - krb5_data_zero(&buf); krb5_data_zero(&signed_data); + krb5_data_zero(&buf); *kdc_cert = NULL; - ret = BN_to_integer(context, kdc_dh->pub_key, &i); - if (ret) - return ret; + if (cp->keyex == USE_DH) { + DH *kdc_dh = cp->u.dh.key; + heim_integer i; - ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret); - if (ret) { - krb5_set_error_message(context, ret, "ASN.1 encoding of " - "DHPublicKey failed (%d)", ret); - return ret; - } - if (buf.length != size) - krb5_abortx(context, "Internal ASN.1 encoder error"); - - dh_info.subjectPublicKey.length = buf.length * 8; - dh_info.subjectPublicKey.data = buf.data; + ret = BN_to_integer(context, kdc_dh->pub_key, &i); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret); + der_free_heim_integer(&i); + if (ret) { + krb5_set_error_message(context, ret, "ASN.1 encoding of " + "DHPublicKey failed (%d)", ret); + return ret; + } + if (buf.length != size) + krb5_abortx(context, "Internal ASN.1 encoder error"); + + dh_info.subjectPublicKey.length = buf.length * 8; + dh_info.subjectPublicKey.data = buf.data; + krb5_data_zero(&buf); +#ifdef HAVE_OPENSSL + } else if (cp->keyex == USE_ECDH) { + unsigned char *p; + int len; + + len = i2o_ECPublicKey(cp->u.ecdh.key, NULL); + if (len <= 0) + abort(); + + p = malloc(len); + if (p == NULL) + abort(); + + dh_info.subjectPublicKey.length = len * 8; + dh_info.subjectPublicKey.data = p; + + len = i2o_ECPublicKey(cp->u.ecdh.key, &p); + if (len <= 0) + abort(); +#endif + } else + krb5_abortx(context, "no keyex selected ?"); - dh_info.nonce = client_params->nonce; + + dh_info.nonce = cp->nonce; ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size, ret); @@ -893,44 +1172,42 @@ pk_mk_pa_reply_dh(krb5_context context, * filled in above */ - { - hx509_query *q; - hx509_cert cert; - - ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); - if (ret) - goto out; - - hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); - hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); - - ret = hx509_certs_find(kdc_identity->hx509ctx, - kdc_identity->certs, - q, - &cert); - hx509_query_free(kdc_identity->hx509ctx, q); - if (ret) - goto out; - - ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx, - 0, - oid_id_pkdhkeydata(), - buf.data, - buf.length, - NULL, - cert, - client_params->peer, - client_params->client_anchors, - kdc_identity->certpool, - &signed_data); - *kdc_cert = cert; - } + ret = hx509_query_alloc(kdc_identity->hx509ctx, &q); + if (ret) + goto out; + + hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); + if (config->pkinit_kdc_friendly_name) + hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); + + ret = hx509_certs_find(kdc_identity->hx509ctx, + kdc_identity->certs, + q, + &cert); + hx509_query_free(kdc_identity->hx509ctx, q); if (ret) goto out; + + ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx, + 0, + &asn1_oid_id_pkdhkeydata, + buf.data, + buf.length, + NULL, + cert, + cp->peer, + cp->client_anchors, + kdc_identity->certpool, + &signed_data); + if (ret) { + kdc_log(context, config, 0, "Failed signing the DH* reply: %d", ret); + goto out; + } + *kdc_cert = cert; ret = _krb5_pk_mk_ContentInfo(context, &signed_data, - oid_id_pkcs7_signedData(), + &asn1_oid_id_pkcs7_signedData, content_info); if (ret) goto out; @@ -955,11 +1232,13 @@ pk_mk_pa_reply_dh(krb5_context context, krb5_error_code _kdc_pk_mk_pa_reply(krb5_context context, krb5_kdc_configuration *config, - pk_client_params *client_params, + pk_client_params *cp, const hdb_entry_ex *client, + krb5_enctype sessionetype, const KDC_REQ *req, const krb5_data *req_buffer, krb5_keyblock **reply_key, + krb5_keyblock *sessionkey, METHOD_DATA *md) { krb5_error_code ret; @@ -989,7 +1268,7 @@ _kdc_pk_mk_pa_reply(krb5_context context, } else enctype = ETYPE_DES3_CBC_SHA1; - if (client_params->type == PKINIT_27) { + if (cp->type == PKINIT_27) { PA_PK_AS_REP rep; const char *type, *other = ""; @@ -997,7 +1276,7 @@ _kdc_pk_mk_pa_reply(krb5_context context, pa_type = KRB5_PADATA_PK_AS_REP; - if (client_params->dh == NULL) { + if (cp->keyex == USE_RSA) { ContentInfo info; type = "enckey"; @@ -1005,18 +1284,19 @@ _kdc_pk_mk_pa_reply(krb5_context context, rep.element = choice_PA_PK_AS_REP_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, - &client_params->reply_key); + &cp->reply_key); if (ret) { free_PA_PK_AS_REP(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, - client_params, + cp, req, req_buffer, - &client_params->reply_key, - &info); + &cp->reply_key, + &info, + &kdc_cert); if (ret) { free_PA_PK_AS_REP(&rep); goto out; @@ -1034,32 +1314,52 @@ _kdc_pk_mk_pa_reply(krb5_context context, if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); + ret = krb5_generate_random_keyblock(context, sessionetype, + sessionkey); + if (ret) { + free_PA_PK_AS_REP(&rep); + goto out; + } + } else { ContentInfo info; - type = "dh"; - if (client_params->dh_group_name) - other = client_params->dh_group_name; + switch (cp->keyex) { + case USE_DH: type = "dh"; break; +#ifdef HAVE_OPENSSL + case USE_ECDH: type = "ecdh"; break; +#endif + default: krb5_abortx(context, "unknown keyex"); break; + } + + if (cp->dh_group_name) + other = cp->dh_group_name; rep.element = choice_PA_PK_AS_REP_dhInfo; - ret = generate_dh_keyblock(context, client_params, enctype, - &client_params->reply_key); + ret = generate_dh_keyblock(context, cp, enctype); if (ret) return ret; - ret = pk_mk_pa_reply_dh(context, client_params->dh, - client_params, - &client_params->reply_key, + ret = pk_mk_pa_reply_dh(context, config, + cp, &info, &kdc_cert); + if (ret) { + free_PA_PK_AS_REP(&rep); + krb5_set_error_message(context, ret, + "create pa-reply-dh " + "failed %d", ret); + goto out; + } ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data, rep.u.dhInfo.dhSignedData.length, &info, &size, ret); free_ContentInfo(&info); if (ret) { - krb5_set_error_message(context, ret, "encoding of Key ContentInfo " + krb5_set_error_message(context, ret, + "encoding of Key ContentInfo " "failed %d", ret); free_PA_PK_AS_REP(&rep); goto out; @@ -1067,17 +1367,23 @@ _kdc_pk_mk_pa_reply(krb5_context context, if (rep.u.encKeyPack.length != size) krb5_abortx(context, "Internal ASN.1 encoder error"); - } - if (ret) { - free_PA_PK_AS_REP(&rep); - goto out; + /* XXX KRB-FX-CF2 */ + ret = krb5_generate_random_keyblock(context, sessionetype, + sessionkey); + if (ret) { + free_PA_PK_AS_REP(&rep); + goto out; + } + + /* XXX Add PA-PKINIT-KX */ + } ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret); free_PA_PK_AS_REP(&rep); if (ret) { - krb5_set_error_message(context, ret, "encode PA-PK-AS-REP failed %d", - ret); + krb5_set_error_message(context, ret, + "encode PA-PK-AS-REP failed %d", ret); goto out; } if (len != size) @@ -1085,13 +1391,14 @@ _kdc_pk_mk_pa_reply(krb5_context context, kdc_log(context, config, 0, "PK-INIT using %s %s", type, other); - } else if (client_params->type == PKINIT_WIN2K) { + } else if (cp->type == PKINIT_WIN2K) { PA_PK_AS_REP_Win2k rep; ContentInfo info; - if (client_params->dh) { + if (cp->keyex != USE_RSA) { ret = KRB5KRB_ERR_GENERIC; - krb5_set_error_message(context, ret, "Windows PK-INIT doesn't support DH"); + krb5_set_error_message(context, ret, + "Windows PK-INIT doesn't support DH"); goto out; } @@ -1101,18 +1408,19 @@ _kdc_pk_mk_pa_reply(krb5_context context, rep.element = choice_PA_PK_AS_REP_encKeyPack; ret = krb5_generate_random_keyblock(context, enctype, - &client_params->reply_key); + &cp->reply_key); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; } ret = pk_mk_pa_reply_enckey(context, config, - client_params, + cp, req, req_buffer, - &client_params->reply_key, - &info); + &cp->reply_key, + &info, + &kdc_cert); if (ret) { free_PA_PK_AS_REP_Win2k(&rep); goto out; @@ -1140,13 +1448,19 @@ _kdc_pk_mk_pa_reply(krb5_context context, if (len != size) krb5_abortx(context, "Internal ASN.1 encoder error"); + ret = krb5_generate_random_keyblock(context, sessionetype, + sessionkey); + if (ret) + goto out; + } else krb5_abortx(context, "PK-INIT internal error"); ret = krb5_padata_add(context, md, pa_type, buf, len); if (ret) { - krb5_set_error_message(context, ret, "failed adding PA-PK-AS-REP %d", ret); + krb5_set_error_message(context, ret, + "Failed adding PA-PK-AS-REP %d", ret); free(buf); goto out; } @@ -1232,7 +1546,7 @@ out: hx509_cert_free(kdc_cert); if (ret == 0) - *reply_key = &client_params->reply_key; + *reply_key = &cp->reply_key; return ret; } @@ -1250,7 +1564,7 @@ match_rfc_san(krb5_context context, ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, client_cert, - oid_id_pkinit_san(), + &asn1_oid_id_pkinit_san, &list); if (ret) goto out; @@ -1311,7 +1625,7 @@ match_ms_upn_san(krb5_context context, ret = hx509_cert_find_subjectAltName_otherName(hx509ctx, client_cert, - oid_id_pkinit_ms_san(), + &asn1_oid_id_pkinit_ms_san, &list); if (ret) goto out; @@ -1363,16 +1677,25 @@ krb5_error_code _kdc_pk_check_client(krb5_context context, krb5_kdc_configuration *config, const hdb_entry_ex *client, - pk_client_params *client_params, + pk_client_params *cp, char **subject_name) { const HDB_Ext_PKINIT_acl *acl; + const HDB_Ext_PKINIT_cert *pc; krb5_error_code ret; hx509_name name; int i; + if (cp->cert == NULL) { + + *subject_name = strdup("anonymous client client"); + if (*subject_name == NULL) + return ENOMEM; + return 0; + } + ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx, - client_params->cert, + cp->cert, &name); if (ret) return ret; @@ -1386,10 +1709,33 @@ _kdc_pk_check_client(krb5_context context, "Trying to authorize PK-INIT subject DN %s", *subject_name); + ret = hdb_entry_get_pkinit_cert(&client->entry, &pc); + if (ret == 0 && pc) { + hx509_cert cert; + unsigned int i; + + for (i = 0; i < pc->len; i++) { + ret = hx509_cert_init_data(kdc_identity->hx509ctx, + pc->val[i].cert.data, + pc->val[i].cert.length, + &cert); + if (ret) + continue; + ret = hx509_cert_cmp(cert, cp->cert); + hx509_cert_free(cert); + if (ret == 0) { + kdc_log(context, config, 5, + "Found matching PK-INIT cert in hdb"); + return 0; + } + } + } + + if (config->pkinit_princ_in_cert) { ret = match_rfc_san(context, config, kdc_identity->hx509ctx, - client_params->cert, + cp->cert, client->entry.principal); if (ret == 0) { kdc_log(context, config, 5, @@ -1398,7 +1744,7 @@ _kdc_pk_check_client(krb5_context context, } ret = match_ms_upn_san(context, config, kdc_identity->hx509ctx, - client_params->cert, + cp->cert, client->entry.principal); if (ret == 0) { kdc_log(context, config, 5, @@ -1493,7 +1839,7 @@ add_principal_mapping(krb5_context context, krb5_error_code _kdc_add_inital_verified_cas(krb5_context context, krb5_kdc_configuration *config, - pk_client_params *params, + pk_client_params *cp, EncTicketPart *tkt) { AD_INITIAL_VERIFIED_CAS cas; @@ -1594,6 +1940,7 @@ _kdc_pk_initialize(krb5_context context, ret = _krb5_pk_load_id(context, &kdc_identity, + 0, user_id, anchors, pool, @@ -1618,7 +1965,8 @@ _kdc_pk_initialize(krb5_context context, } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); - hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); + if (config->pkinit_kdc_friendly_name) + hx509_query_match_friendly_name(q, config->pkinit_kdc_friendly_name); ret = hx509_certs_find(kdc_identity->hx509ctx, kdc_identity->certs, @@ -1627,23 +1975,30 @@ _kdc_pk_initialize(krb5_context context, hx509_query_free(kdc_identity->hx509ctx, q); if (ret == 0) { if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert, - oid_id_pkkdcekuoid(), 0)) - krb5_warnx(context, "WARNING Found KDC certificate " + &asn1_oid_id_pkkdcekuoid, 0)) { + hx509_name name; + char *str; + ret = hx509_cert_get_subject(cert, &name); + hx509_name_to_string(name, &str); + krb5_warnx(context, "WARNING Found KDC certificate (%s)" "is missing the PK-INIT KDC EKU, this is bad for " - "interoperability."); + "interoperability.", str); + hx509_name_free(&name); + free(str); + } hx509_cert_free(cert); } else krb5_warnx(context, "PKINIT: failed to find a signing " "certifiate with a public key"); } - ret = krb5_config_get_bool_default(context, - NULL, - FALSE, - "kdc", - "pkinit_allow_proxy_certificate", - NULL); - _krb5_pk_allow_proxy_certificate(kdc_identity, ret); + if (krb5_config_get_bool_default(context, + NULL, + FALSE, + "kdc", + "pkinit_allow_proxy_certificate", + NULL)) + config->pkinit_allow_proxy_certs = 1; file = krb5_config_get_string(context, NULL, diff --git a/source4/heimdal/kdc/process.c b/source4/heimdal/kdc/process.c index a27911914b..d3557ee6c9 100644 --- a/source4/heimdal/kdc/process.c +++ b/source4/heimdal/kdc/process.c @@ -34,8 +34,6 @@ #include "kdc_locl.h" -RCSID("$Id$"); - /* * */ @@ -49,6 +47,209 @@ krb5_kdc_update_time(struct timeval *tv) _kdc_now = *tv; } +static krb5_error_code +kdc_as_req(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + krb5_error_code ret; + KDC_REQ req; + size_t len; + + ret = decode_AS_REQ(req_buffer->data, req_buffer->length, &req, &len); + if (ret) + return ret; + + *claim = 1; + + ret = _kdc_as_rep(context, config, &req, req_buffer, + reply, from, addr, datagram_reply); + free_AS_REQ(&req); + return ret; +} + + +static krb5_error_code +kdc_tgs_req(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + krb5_error_code ret; + KDC_REQ req; + size_t len; + + ret = decode_TGS_REQ(req_buffer->data, req_buffer->length, &req, &len); + if (ret) + return ret; + + *claim = 1; + + ret = _kdc_tgs_rep(context, config, &req, reply, + from, addr, datagram_reply); + free_TGS_REQ(&req); + return ret; +} + +#ifdef DIGEST + +static krb5_error_code +kdc_digest(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + DigestREQ digestreq; + krb5_error_code ret; + size_t len; + + ret = decode_DigestREQ(req_buffer->data, req_buffer->length, + &digestreq, &len); + if (ret) + return ret; + + *claim = 1; + + ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr); + free_DigestREQ(&digestreq); + return ret; +} + +#endif + +#ifdef KX509 + +static krb5_error_code +kdc_kx509(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + Kx509Request kx509req; + krb5_error_code ret; + size_t len; + + ret = _kdc_try_kx509_request(req_buffer->data, req_buffer->length, + &kx509req, &len); + if (ret) + return ret; + + *claim = 1; + + ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr); + free_Kx509Request(&kx509req); + return ret; +} + +#endif + + +#ifdef KRB4 + +static krb5_error_code +kdc_524(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + krb5_error_code ret; + Ticket ticket; + size_t len; + + ret = decode_Ticket(req_buffer->data, req_buffer->length, &ticket, &len); + if (ret) + return ret; + + *claim = 1; + + ret = _kdc_do_524(context, config, &ticket, reply, from, addr); + free_Ticket(&ticket); + return ret; +} + +static krb5_error_code +kdc_krb4(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + if (_kdc_maybe_version4(req_buffer->data, req_buffer->length) == 0) + return -1; + + *claim = 1; + + return _kdc_do_version4(context, config, + req_buffer->data, req_buffer->length, + reply, from, + (struct sockaddr_in*)addr); +} + +static krb5_error_code +kdc_kaserver(krb5_context context, + krb5_kdc_configuration *config, + krb5_data *req_buffer, + krb5_data *reply, + const char *from, + struct sockaddr *addr, + int datagram_reply, + int *claim) +{ + if (config->enable_kaserver == 0) + return -1; + + *claim = 1; + + return _kdc_do_kaserver(context, config, + req_buffer->data, req_buffer->length, + reply, from, + (struct sockaddr_in*)addr); +} + +#endif /* KRB4 */ + + +static struct krb5_kdc_service services[] = { + { KS_KRB5, kdc_as_req }, + { KS_KRB5, kdc_tgs_req }, +#ifdef DIGEST + { 0, kdc_digest }, +#endif +#ifdef KX509 + { 0, kdc_kx509 }, +#endif +#ifdef KRB4 + { 0, kdc_524 }, + { KS_NO_LENGTH, kdc_krb4 }, + { 0, kdc_kaserver }, +#endif + { 0, NULL } +}; + /* * handle the request in `buf, len', from `addr' (or `from' as a string), * sending a reply in `reply'. @@ -65,50 +266,25 @@ krb5_kdc_process_request(krb5_context context, struct sockaddr *addr, int datagram_reply) { - KDC_REQ req; - Ticket ticket; - DigestREQ digestreq; - Kx509Request kx509req; krb5_error_code ret; - size_t i; - - if(decode_AS_REQ(buf, len, &req, &i) == 0){ - krb5_data req_buffer; + unsigned int i; + krb5_data req_buffer; + int claim = 0; + + req_buffer.data = buf; + req_buffer.length = len; - req_buffer.data = buf; - req_buffer.length = len; - - ret = _kdc_as_rep(context, config, &req, &req_buffer, - reply, from, addr, datagram_reply); - 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, datagram_reply); - free_TGS_REQ(&req); - return ret; - }else if(decode_Ticket(buf, len, &ticket, &i) == 0){ - ret = _kdc_do_524(context, config, &ticket, reply, from, addr); - free_Ticket(&ticket); - return ret; - }else if(decode_DigestREQ(buf, len, &digestreq, &i) == 0){ - 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 */ - ret = _kdc_do_version4(context, config, buf, len, reply, from, - (struct sockaddr_in*)addr); - return ret; - } else if (config->enable_kaserver) { - ret = _kdc_do_kaserver(context, config, buf, len, reply, from, - (struct sockaddr_in*)addr); - return ret; + for (i = 0; services[i].process != NULL; i++) { + ret = (*services[i].process)(context, config, &req_buffer, + reply, from, addr, datagram_reply, + &claim); + if (claim) { + if (services[i].flags & KS_NO_LENGTH) + *prependlength = 0; + return ret; + } } - + return -1; } @@ -129,25 +305,24 @@ krb5_kdc_process_krb5_request(krb5_context context, struct sockaddr *addr, int datagram_reply) { - KDC_REQ req; krb5_error_code ret; - size_t i; + unsigned int i; + krb5_data req_buffer; + int claim = 0; + + req_buffer.data = buf; + req_buffer.length = len; - if(decode_AS_REQ(buf, len, &req, &i) == 0){ - krb5_data req_buffer; - - req_buffer.data = buf; - req_buffer.length = len; - - ret = _kdc_as_rep(context, config, &req, &req_buffer, - reply, from, addr, datagram_reply); - 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, datagram_reply); - free_TGS_REQ(&req); - return ret; + for (i = 0; services[i].process != NULL; i++) { + if ((services[i].flags & KS_KRB5) == 0) + continue; + ret = (*services[i].process)(context, config, &req_buffer, + reply, from, addr, datagram_reply, + &claim); + if (claim) + return ret; } + return -1; } |