summaryrefslogtreecommitdiff
path: root/source4/heimdal/kdc
diff options
context:
space:
mode:
Diffstat (limited to 'source4/heimdal/kdc')
-rw-r--r--source4/heimdal/kdc/524.c400
-rw-r--r--source4/heimdal/kdc/default_config.c24
-rw-r--r--source4/heimdal/kdc/digest.c17
-rw-r--r--source4/heimdal/kdc/headers.h6
-rw-r--r--source4/heimdal/kdc/kaserver.c4
-rw-r--r--source4/heimdal/kdc/kdc.h16
-rw-r--r--source4/heimdal/kdc/kdc_locl.h5
-rw-r--r--source4/heimdal/kdc/kerberos4.c794
-rw-r--r--source4/heimdal/kdc/kerberos5.c378
-rw-r--r--source4/heimdal/kdc/krb5tgs.c62
-rw-r--r--source4/heimdal/kdc/kx509.c25
-rw-r--r--source4/heimdal/kdc/pkinit.c809
-rw-r--r--source4/heimdal/kdc/process.c293
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;
}