summaryrefslogtreecommitdiff
path: root/source4/heimdal/kdc
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2006-11-07 06:59:56 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 14:25:03 -0500
commit3c1e780ec7e16dc6667402bbc65708bf9a5c062f (patch)
tree2102bb577ea9f00751b8c869b0a5c756fc2ae8e5 /source4/heimdal/kdc
parent8b91594e0936bbaedf5430406fcf8df3ea406c10 (diff)
downloadsamba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.tar.gz
samba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.tar.bz2
samba-3c1e780ec7e16dc6667402bbc65708bf9a5c062f.zip
r19604: This is a massive commit, and I appologise in advance for it's size.
This merges Samba4 with lorikeet-heimdal, which itself has been tracking Heimdal CVS for the past couple of weeks. This is such a big change because Heimdal reorganised it's internal structures, with the mechglue merge, and because many of our 'wishes' have been granted: we now have DCE_STYLE GSSAPI, send_to_kdc hooks and many other features merged into the mainline code. We have adapted to upstream's choice of API in these cases. In gensec_gssapi and gensec_krb5, we either expect a valid PAC, or NO PAC. This matches windows behavour. We also have an option to require the PAC to be present (which allows us to automate the testing of this code). This also includes a restructure of how the kerberos dependencies are handled, due to the fallout of the merge. Andrew Bartlett (This used to be commit 4826f1735197c2a471d771495e6d4c1051b4c471)
Diffstat (limited to 'source4/heimdal/kdc')
-rw-r--r--source4/heimdal/kdc/524.c11
-rw-r--r--source4/heimdal/kdc/default_config.c3
-rw-r--r--source4/heimdal/kdc/digest.c712
-rw-r--r--source4/heimdal/kdc/headers.h7
-rw-r--r--source4/heimdal/kdc/kaserver.c5
-rw-r--r--source4/heimdal/kdc/kdc-private.h87
-rw-r--r--source4/heimdal/kdc/kdc-protos.h12
-rw-r--r--source4/heimdal/kdc/kdc.h11
-rw-r--r--source4/heimdal/kdc/kerberos4.c23
-rw-r--r--source4/heimdal/kdc/kerberos5.c1337
-rw-r--r--source4/heimdal/kdc/krb5tgs.c1781
-rw-r--r--source4/heimdal/kdc/misc.c38
-rwxr-xr-xsource4/heimdal/kdc/pkinit.c105
-rw-r--r--source4/heimdal/kdc/process.c31
14 files changed, 2911 insertions, 1252 deletions
diff --git a/source4/heimdal/kdc/524.c b/source4/heimdal/kdc/524.c
index d61b78d9b6..56c12efd60 100644
--- a/source4/heimdal/kdc/524.c
+++ b/source4/heimdal/kdc/524.c
@@ -33,7 +33,7 @@
#include "kdc_locl.h"
-RCSID("$Id: 524.c,v 1.37 2006/04/27 11:33:20 lha Exp $");
+RCSID("$Id: 524.c,v 1.40 2006/10/06 17:06:30 lha Exp $");
#include <krb5-v4compat.h>
@@ -53,7 +53,8 @@ fetch_server (krb5_context context,
krb5_error_code ret;
krb5_principal sprinc;
- ret = _krb5_principalname2krb5_principal(context, &sprinc, t->sname, t->realm);
+ 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));
@@ -66,7 +67,8 @@ fetch_server (krb5_context context,
krb5_get_err_text(context, ret));
return ret;
}
- ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER, server);
+ 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,
@@ -90,7 +92,8 @@ log_524 (krb5_context context,
char *cpn;
krb5_error_code ret;
- ret = _krb5_principalname2krb5_principal(context, &client, et->cname, et->crealm);
+ 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));
diff --git a/source4/heimdal/kdc/default_config.c b/source4/heimdal/kdc/default_config.c
index 5152fe9ab1..c4d9f51fd0 100644
--- a/source4/heimdal/kdc/default_config.c
+++ b/source4/heimdal/kdc/default_config.c
@@ -42,8 +42,9 @@
void
krb5_kdc_default_config(krb5_kdc_configuration *config)
{
+ memset(config, 0, sizeof(*config));
config->require_preauth = TRUE;
- config->kdc_warn_pwexpire = -1;
+ config->kdc_warn_pwexpire = 0;
config->encode_as_rep_as_tgs_rep = FALSE; /* bug compatibility */
config->check_ticket_addresses = TRUE;
config->allow_null_ticket_addresses = TRUE;
diff --git a/source4/heimdal/kdc/digest.c b/source4/heimdal/kdc/digest.c
new file mode 100644
index 0000000000..a5517fb896
--- /dev/null
+++ b/source4/heimdal/kdc/digest.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <digest_asn1.h>
+#include <hex.h>
+
+RCSID("$Id: digest.c,v 1.7 2006/10/22 20:11:44 lha Exp $");
+
+krb5_error_code
+_kdc_do_digest(krb5_context context,
+ krb5_kdc_configuration *config,
+ const DigestREQ *req, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret = 0;
+ krb5_ticket *ticket = NULL;
+ krb5_auth_context ac = NULL;
+ krb5_keytab id = NULL;
+ krb5_crypto crypto = NULL;
+ DigestReqInner ireq;
+ DigestRepInner r;
+ DigestREP rep;
+ krb5_flags ap_req_options;
+ krb5_data buf;
+ size_t size;
+ krb5_storage *sp = NULL;
+ Checksum res;
+ hdb_entry_ex *server = NULL, *user = NULL;
+ char *password = NULL;
+ krb5_data serverNonce;
+
+ if(!config->enable_digest) {
+ kdc_log(context, config, 0, "Rejected digest request from %s", from);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(reply);
+ krb5_data_zero(&serverNonce);
+ memset(&ireq, 0, sizeof(ireq));
+ memset(&r, 0, sizeof(r));
+ memset(&rep, 0, sizeof(rep));
+
+ kdc_log(context, config, 0, "Digest request from %s", from);
+
+ ret = krb5_kt_resolve(context, "HDB:", &id);
+ if (ret) {
+ kdc_log(context, config, 0, "Can't open database for digest");
+ goto out;
+ }
+
+ ret = krb5_rd_req(context,
+ &ac,
+ &req->apReq,
+ NULL,
+ id,
+ &ap_req_options,
+ &ticket);
+ if (ret)
+ goto out;
+
+ /* check the server principal in the ticket matches digest/R@R */
+ {
+ krb5_principal principal = NULL;
+ const char *p, *r;
+
+ ret = krb5_ticket_get_server(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = EINVAL;
+ krb5_set_error_string(context, "Wrong digest server principal used");
+ p = krb5_principal_get_comp_string(context, principal, 0);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ p = krb5_principal_get_comp_string(context, principal, 1);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ r = krb5_principal_get_realm(context, principal);
+ if (r == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, r) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_SERVER, NULL, &server);
+ if (ret)
+ goto out;
+
+ krb5_free_principal(context, principal);
+ }
+
+ /* check the client is allowed to do digest auth */
+ {
+ krb5_principal principal = NULL;
+ hdb_entry_ex *client;
+
+ ret = krb5_ticket_get_client(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_CLIENT, NULL, &client);
+ krb5_free_principal(context, principal);
+ if (ret)
+ goto out;
+
+ if (client->entry.flags.allow_digest == 0) {
+ krb5_set_error_string(context,
+ "Client is not permitted to use digest");
+ ret = KRB5KDC_ERR_POLICY;
+ _kdc_free_ent (context, client);
+ goto out;
+ }
+ _kdc_free_ent (context, client);
+ }
+
+ /* unpack request */
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getremotesubkey(context, ac, &key);
+ if (ret)
+ goto out;
+ if (key == NULL) {
+ krb5_set_error_string(context, "digest: remote subkey not found");
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ &req->innerReq, &buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
+ krb5_data_free(&buf);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode digest inner request");
+ goto out;
+ }
+
+ /*
+ * Process the inner request
+ */
+
+ switch (ireq.element) {
+ case choice_DigestReqInner_init: {
+ unsigned char server_nonce[16], identifier;
+
+ RAND_pseudo_bytes(&identifier, sizeof(identifier));
+ RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
+
+ server_nonce[0] = kdc_time & 0xff;
+ server_nonce[1] = (kdc_time >> 8) & 0xff;
+ server_nonce[2] = (kdc_time >> 16) & 0xff;
+ server_nonce[3] = (kdc_time >> 24) & 0xff;
+
+ r.element = choice_DigestRepInner_initReply;
+
+ hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
+ if (r.u.initReply.nonce == NULL) {
+ krb5_set_error_string(context, "Failed to decode server nonce");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ ret = krb5_store_stringz(sp, ireq.u.init.type);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ if (ireq.u.init.channel) {
+ char *s;
+
+ asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
+ ireq.u.init.channel->cb_type,
+ ireq.u.init.channel->cb_binding);
+ if (s == NULL) {
+ krb5_set_error_string(context, "Failed to allocate "
+ "channel binding");
+ ret = ENOMEM;
+ goto out;
+ }
+ free(r.u.initReply.nonce);
+ r.u.initReply.nonce = s;
+ }
+
+ ret = krb5_store_stringz(sp, r.u.initReply.nonce);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
+ r.u.initReply.identifier =
+ malloc(sizeof(*r.u.initReply.identifier));
+ if (r.u.initReply.identifier == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
+ if (*r.u.initReply.identifier == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_store_stringz(sp, *r.u.initReply.identifier);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ } else
+ r.u.initReply.identifier = NULL;
+
+ if (ireq.u.init.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ {
+ Key *key;
+ krb5_enctype enctype;
+
+ ret = _kdc_get_preferred_key(context,
+ config,
+ server,
+ "digest-service",
+ &enctype,
+ &key);
+ if (ret)
+ goto out;
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ 0,
+ buf.data,
+ buf.length,
+ &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
+ free_Checksum(&res);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode "
+ "checksum in digest request");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
+ free(buf.data);
+ if (r.u.initReply.opaque == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ break;
+ }
+ case choice_DigestReqInner_digestRequest: {
+ krb5_principal clientprincipal;
+ HDB *db;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ krb5_store_stringz(sp, ireq.u.digestRequest.type);
+
+ krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
+ if (ireq.u.digestRequest.identifier) {
+ ret = krb5_store_stringz(sp, *ireq.u.digestRequest.identifier);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+ if (ireq.u.digestRequest.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ buf.length = strlen(ireq.u.digestRequest.opaque);
+ buf.data = malloc(buf.length);
+ if (buf.data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
+ if (ret <= 0) {
+ krb5_set_error_string(context, "Failed to decode opaque");
+ ret = ENOMEM;
+ goto out;
+ }
+ buf.length = ret;
+
+ ret = decode_Checksum(buf.data, buf.length, &res, NULL);
+ free(buf.data);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode digest Checksum");
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
+ serverNonce.data = malloc(serverNonce.length);
+ if (serverNonce.data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * CHAP does the checksum of the raw nonce, but do it for all
+ * types, since we need to check the timestamp.
+ */
+ {
+ ssize_t ssize;
+
+ ssize = hex_decode(ireq.u.digestRequest.serverNonce,
+ serverNonce.data, serverNonce.length);
+ if (ssize <= 0) {
+ krb5_set_error_string(context, "Failed to decode serverNonce");
+ ret = ENOMEM;
+ goto out;
+ }
+ serverNonce.length = ssize;
+ }
+
+ {
+ Key *key;
+ krb5_enctype enctype;
+
+ ret = _kdc_get_preferred_key(context,
+ config,
+ server,
+ "digest-service",
+ &enctype,
+ &key);
+ if (ret)
+ goto out;
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ buf.data, buf.length, &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ /* verify time */
+ {
+ unsigned char *p = serverNonce.data;
+ uint32_t t;
+
+ if (serverNonce.length < 4) {
+ krb5_set_error_string(context, "server nonce too short");
+ ret = EINVAL;
+ goto out;
+ }
+ t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
+ if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
+ krb5_set_error_string(context, "time screw in server nonce ");
+ ret = EINVAL;
+ goto out;
+ }
+ }
+
+ /* get username */
+ ret = krb5_parse_name(context,
+ ireq.u.digestRequest.username,
+ &clientprincipal);
+ if (ret)
+ goto out;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, &db, &user);
+
+ krb5_free_principal(context, clientprincipal);
+ if (ret)
+ goto out;
+
+ ret = hdb_entry_get_password(context, db, &user->entry, &password);
+ if (ret || password == NULL) {
+ if (ret == 0) {
+ ret = EINVAL;
+ krb5_set_error_string(context, "password missing");
+ }
+ goto out;
+ }
+
+ if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char id;
+
+ if (ireq.u.digestRequest.identifier == NULL) {
+ krb5_set_error_string(context, "Identifier missing "
+ "from CHAP request");
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
+ krb5_set_error_string(context, "failed to decode identifier");
+ ret = EINVAL;
+ goto out;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, &id, 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Update(&ctx, serverNonce.data, serverNonce.length);
+ MD5_Final(md, &ctx);
+
+ r.element = choice_DigestRepInner_response;
+ hex_encode(md, sizeof(md), &r.u.response.responseData);
+ if (r.u.response.responseData == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+ } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *A1, *A2;
+
+ if (ireq.u.digestRequest.nonceCount == NULL)
+ goto out;
+ if (ireq.u.digestRequest.clientNonce == NULL)
+ goto out;
+ if (ireq.u.digestRequest.qop == NULL)
+ goto out;
+ if (ireq.u.digestRequest.realm == NULL)
+ goto out;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, ireq.u.digestRequest.username,
+ strlen(ireq.u.digestRequest.username));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.realm,
+ strlen(*ireq.u.digestRequest.realm));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Final(md, &ctx);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, md, sizeof(md));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ if (ireq.u.digestRequest.authid) {
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.authid,
+ strlen(*ireq.u.digestRequest.authid));
+ }
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A1);
+ if (A1 == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.uri,
+ strlen(*ireq.u.digestRequest.uri));
+
+ /* conf|int */
+ if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
+ static char conf_zeros[] = ":00000000000000000000000000000000";
+ MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
+ }
+
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A2);
+ if (A2 == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ free(A1);
+ goto out;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, A1, strlen(A2));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
+ strlen(*ireq.u.digestRequest.clientNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.qop,
+ strlen(*ireq.u.digestRequest.qop));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, A2, strlen(A2));
+
+ MD5_Final(md, &ctx);
+
+ r.element = choice_DigestRepInner_response;
+ hex_encode(md, sizeof(md), &r.u.response.responseData);
+
+ free(A1);
+ free(A2);
+
+ if (r.u.response.responseData == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ } else {
+ r.element = choice_DigestRepInner_error;
+ asprintf(&r.u.error.reason, "unsupported digest type %s",
+ ireq.u.digestRequest.type);
+ if (r.u.error.reason == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ }
+
+ break;
+ }
+ default:
+ r.element = choice_DigestRepInner_error;
+ r.u.error.reason = strdup("unknown operation");
+ if (r.u.error.reason == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ break;
+ }
+
+ ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode inner digest reply");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
+
+ ret = krb5_mk_rep (context, ac, &rep.apRep);
+ if (ret)
+ goto out;
+
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ buf.data, buf.length, 0,
+ &rep.innerRep);
+
+ ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode digest reply");
+ goto out;
+ }
+ if (size != reply->length)
+ krb5_abortx(context, "ASN1 internal error");
+
+
+out:
+ if (ac)
+ krb5_auth_con_free(context, ac);
+ if (ret)
+ krb5_warn(context, ret, "Digest request from %s failed", from);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if (id)
+ krb5_kt_close(context, id);
+ if (crypto)
+ krb5_crypto_destroy(context, crypto);
+ if (sp)
+ krb5_storage_free(sp);
+ if (user)
+ _kdc_free_ent (context, user);
+ if (server)
+ _kdc_free_ent (context, server);
+ if (password) {
+ memset(password, 0, strlen(password));
+ free (password);
+ }
+ krb5_data_free(&buf);
+ krb5_data_free(&serverNonce);
+ free_DigestREP(&rep);
+ free_DigestRepInner(&r);
+ free_DigestReqInner(&ireq);
+
+ return ret;
+}
diff --git a/source4/heimdal/kdc/headers.h b/source4/heimdal/kdc/headers.h
index 86f162aa94..87d713b076 100644
--- a/source4/heimdal/kdc/headers.h
+++ b/source4/heimdal/kdc/headers.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -32,7 +32,7 @@
*/
/*
- * $Id: headers.h,v 1.16 2005/04/24 13:49:00 lha Exp $
+ * $Id: headers.h,v 1.18 2006/10/17 02:22:17 lha Exp $
*/
#ifndef __HEADERS_H__
@@ -88,9 +88,10 @@
#include <parse_units.h>
#include <krb5.h>
#include <krb5_locl.h>
+#include <digest_asn1.h>
#include <hdb.h>
#include <hdb_err.h>
-#include <der.h> /* copy_octet_string */
+#include <der.h>
#undef ALLOC
#define ALLOC(X) ((X) = malloc(sizeof(*(X))))
diff --git a/source4/heimdal/kdc/kaserver.c b/source4/heimdal/kdc/kaserver.c
index c08a51b9cc..ac282717ed 100644
--- a/source4/heimdal/kdc/kaserver.c
+++ b/source4/heimdal/kdc/kaserver.c
@@ -33,7 +33,7 @@
#include "kdc_locl.h"
-RCSID("$Id: kaserver.c,v 1.35 2006/05/05 10:49:50 lha Exp $");
+RCSID("$Id: kaserver.c,v 1.36 2006/08/23 11:43:44 lha Exp $");
#include <krb5-v4compat.h>
#include <rx.h>
@@ -465,7 +465,8 @@ do_authenticate (krb5_context context,
client_name, from, server_name);
ret = _kdc_db_fetch4 (context, config, name, instance,
- config->v4_realm, HDB_F_GET_CLIENT, &client_entry);
+ config->v4_realm, HDB_F_GET_CLIENT,
+ &client_entry);
if (ret) {
kdc_log(context, config, 0, "Client not found in database: %s: %s",
client_name, krb5_get_err_text(context, ret));
diff --git a/source4/heimdal/kdc/kdc-private.h b/source4/heimdal/kdc/kdc-private.h
index 251e06b14a..8c2f56002d 100644
--- a/source4/heimdal/kdc/kdc-private.h
+++ b/source4/heimdal/kdc/kdc-private.h
@@ -5,6 +5,16 @@
#include <stdarg.h>
krb5_error_code
+_kdc_add_KRB5SignedPath (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ hdb_entry_ex */*krbtgt*/,
+ krb5_enctype /*enctype*/,
+ krb5_const_principal /*server*/,
+ KRB5SignedPathPrincipals */*principals*/,
+ EncTicketPart */*tkt*/);
+
+krb5_error_code
_kdc_as_rep (
krb5_context /*context*/,
krb5_kdc_configuration */*config*/,
@@ -12,7 +22,15 @@ _kdc_as_rep (
const krb5_data */*req_buffer*/,
krb5_data */*reply*/,
const char */*from*/,
- struct sockaddr */*from_addr*/);
+ struct sockaddr */*from_addr*/,
+ int /*datagram_reply*/);
+
+krb5_boolean
+_kdc_check_addresses (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ HostAddresses */*addresses*/,
+ const struct sockaddr */*from*/);
krb5_error_code
_kdc_check_flags (
@@ -30,6 +48,7 @@ _kdc_db_fetch (
krb5_kdc_configuration */*config*/,
krb5_const_principal /*principal*/,
unsigned /*flags*/,
+ HDB **/*db*/,
hdb_entry_ex **/*h*/);
krb5_error_code
@@ -52,6 +71,15 @@ _kdc_do_524 (
struct sockaddr */*addr*/);
krb5_error_code
+_kdc_do_digest (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const DigestREQ */*req*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/);
+
+krb5_error_code
_kdc_do_kaserver (
krb5_context /*context*/,
krb5_kdc_configuration */*config*/,
@@ -72,6 +100,21 @@ _kdc_do_version4 (
struct sockaddr_in */*addr*/);
krb5_error_code
+_kdc_encode_reply (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ KDC_REP */*rep*/,
+ const EncTicketPart */*et*/,
+ EncKDCRepPart */*ek*/,
+ krb5_enctype /*etype*/,
+ int /*skvno*/,
+ const EncryptionKey */*skey*/,
+ int /*ckvno*/,
+ const EncryptionKey */*ckey*/,
+ const char **/*e_text*/,
+ krb5_data */*reply*/);
+
+krb5_error_code
_kdc_encode_v4_ticket (
krb5_context /*context*/,
krb5_kdc_configuration */*config*/,
@@ -81,6 +124,24 @@ _kdc_encode_v4_ticket (
const PrincipalName */*service*/,
size_t */*size*/);
+krb5_error_code
+_kdc_find_etype (
+ krb5_context /*context*/,
+ const hdb_entry_ex */*princ*/,
+ krb5_enctype */*etypes*/,
+ unsigned /*len*/,
+ Key **/*ret_key*/,
+ krb5_enctype */*ret_etype*/);
+
+PA_DATA*
+_kdc_find_padata (
+ KDC_REQ */*req*/,
+ int */*start*/,
+ int /*type*/);
+
+void
+_kdc_fix_time (time_t **/*t*/);
+
void
_kdc_free_ent (
krb5_context /*context*/,
@@ -94,6 +155,28 @@ _kdc_get_des_key (
krb5_boolean /*prefer_afs_key*/,
Key **/*ret_key*/);
+krb5_error_code
+_kdc_get_preferred_key (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ hdb_entry_ex */*h*/,
+ const char */*name*/,
+ krb5_enctype */*enctype*/,
+ Key **/*key*/);
+
+void
+_kdc_log_timestamp (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const char */*type*/,
+ KerberosTime /*authtime*/,
+ KerberosTime */*starttime*/,
+ KerberosTime /*endtime*/,
+ KerberosTime */*renew_till*/);
+
+krb5_error_code
+_kdc_make_anonymous_principalname (PrincipalName */*pn*/);
+
int
_kdc_maybe_version4 (
unsigned char */*buf*/,
@@ -120,7 +203,7 @@ _kdc_pk_initialize (
const char */*user_id*/,
const char */*anchors*/,
char **/*pool*/,
- char **/*revoke*/);
+ char **/*revoke_list*/);
krb5_error_code
_kdc_pk_mk_pa_reply (
diff --git a/source4/heimdal/kdc/kdc-protos.h b/source4/heimdal/kdc/kdc-protos.h
index 5967f933f3..69bc871b01 100644
--- a/source4/heimdal/kdc/kdc-protos.h
+++ b/source4/heimdal/kdc/kdc-protos.h
@@ -41,25 +41,27 @@ void
krb5_kdc_default_config (krb5_kdc_configuration */*config*/);
int
-krb5_kdc_process_generic_request (
+krb5_kdc_process_krb5_request (
krb5_context /*context*/,
krb5_kdc_configuration */*config*/,
unsigned char */*buf*/,
size_t /*len*/,
krb5_data */*reply*/,
- krb5_boolean */*prependlength*/,
const char */*from*/,
- struct sockaddr */*addr*/);
+ struct sockaddr */*addr*/,
+ int /*datagram_reply*/);
int
-krb5_kdc_process_krb5_request (
+krb5_kdc_process_request (
krb5_context /*context*/,
krb5_kdc_configuration */*config*/,
unsigned char */*buf*/,
size_t /*len*/,
krb5_data */*reply*/,
+ krb5_boolean */*prependlength*/,
const char */*from*/,
- struct sockaddr */*addr*/);
+ struct sockaddr */*addr*/,
+ int /*datagram_reply*/);
#ifdef __cplusplus
}
diff --git a/source4/heimdal/kdc/kdc.h b/source4/heimdal/kdc/kdc.h
index 2948570e3a..043b6de47d 100644
--- a/source4/heimdal/kdc/kdc.h
+++ b/source4/heimdal/kdc/kdc.h
@@ -35,7 +35,7 @@
*/
/*
- * $Id: kdc.h,v 1.6 2006/05/03 12:03:29 lha Exp $
+ * $Id: kdc.h,v 1.9 2006/10/09 15:34:07 lha Exp $
*/
#ifndef __KDC_H__
@@ -65,10 +65,12 @@ typedef struct krb5_kdc_configuration {
char *v4_realm;
krb5_boolean enable_v4;
+ krb5_boolean enable_v4_cross_realm;
+ krb5_boolean enable_v4_per_principal;
+
krb5_boolean enable_kaserver;
-
+
krb5_boolean enable_524;
- krb5_boolean enable_v4_cross_realm;
krb5_boolean enable_pkinit;
krb5_boolean enable_pkinit_princ_in_cert;
@@ -78,6 +80,9 @@ typedef struct krb5_kdc_configuration {
int pkinit_dh_min_bits;
+ int enable_digest;
+ size_t max_datagram_reply_length;
+
} krb5_kdc_configuration;
#include <kdc-protos.h>
diff --git a/source4/heimdal/kdc/kerberos4.c b/source4/heimdal/kdc/kerberos4.c
index d7a3a9cb69..97e98d86ad 100644
--- a/source4/heimdal/kdc/kerberos4.c
+++ b/source4/heimdal/kdc/kerberos4.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -35,7 +35,7 @@
#include <krb5-v4compat.h>
-RCSID("$Id: kerberos4.c,v 1.60 2006/05/05 10:50:44 lha Exp $");
+RCSID("$Id: kerberos4.c,v 1.63 2006/10/08 13:43:27 lha Exp $");
#ifndef swap32
static uint32_t
@@ -80,7 +80,7 @@ valid_princ(krb5_context context,
ret = krb5_unparse_name(context, princ, &s);
if (ret)
return FALSE;
- ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, &ent);
+ 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));
@@ -111,7 +111,7 @@ _kdc_db_fetch4(krb5_context context,
valid_princ, &ctx, 0, &p);
if(ret)
return ret;
- ret = _kdc_db_fetch(context, config, p, flags, ent);
+ ret = _kdc_db_fetch(context, config, p, flags, NULL, ent);
krb5_free_principal(context, p);
return ret;
}
@@ -221,6 +221,17 @@ _kdc_do_version4(krb5_context context,
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, KERB_ERR_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.
@@ -372,7 +383,7 @@ _kdc_do_version4(krb5_context context,
}
ret = _kdc_db_fetch(context, config, tgt_princ,
- HDB_F_GET_KRBTGT, &tgt);
+ HDB_F_GET_KRBTGT, NULL, &tgt);
if(ret){
char *s;
s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not "
@@ -668,7 +679,7 @@ _kdc_encode_v4_ticket(krb5_context context,
if(ret)
return ret;
- _krb5_principalname2krb5_principal(context,
+ _krb5_principalname2krb5_principal(context,
&princ,
et->cname,
et->crealm);
diff --git a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
index a73c2c10b3..19287b31cc 100644
--- a/source4/heimdal/kdc/kerberos5.c
+++ b/source4/heimdal/kdc/kerberos5.c
@@ -33,12 +33,12 @@
#include "kdc_locl.h"
-RCSID("$Id: kerberos5.c,v 1.211 2006/04/27 12:01:09 lha Exp $");
+RCSID("$Id: kerberos5.c,v 1.223 2006/10/17 02:16:29 lha Exp $");
#define MAX_TIME ((time_t)((1U << 31) - 1))
-static void
-fix_time(time_t **t)
+void
+_kdc_fix_time(time_t **t)
{
if(*t == NULL){
ALLOC(*t);
@@ -65,13 +65,13 @@ set_salt_padata (METHOD_DATA *md, Salt *salt)
if (salt) {
realloc_method_data(md);
md->val[md->len - 1].padata_type = salt->type;
- copy_octet_string(&salt->salt,
- &md->val[md->len - 1].padata_value);
+ der_copy_octet_string(&salt->salt,
+ &md->val[md->len - 1].padata_value);
}
}
-static PA_DATA*
-find_padata(KDC_REQ *req, int *start, int type)
+PA_DATA*
+_kdc_find_padata(KDC_REQ *req, int *start, int type)
{
while(*start < req->padata->len){
(*start)++;
@@ -87,10 +87,10 @@ find_padata(KDC_REQ *req, int *start, int type)
* one, but preferring one that has default salt
*/
-static krb5_error_code
-find_etype(krb5_context context, const hdb_entry_ex *princ,
- krb5_enctype *etypes, unsigned len,
- Key **ret_key, krb5_enctype *ret_etype)
+krb5_error_code
+_kdc_find_etype(krb5_context context, const hdb_entry_ex *princ,
+ krb5_enctype *etypes, unsigned len,
+ Key **ret_key, krb5_enctype *ret_etype)
{
int i;
krb5_error_code ret = KRB5KDC_ERR_ETYPE_NOSUPP;
@@ -116,46 +116,8 @@ find_etype(krb5_context context, const hdb_entry_ex *princ,
return ret;
}
-static krb5_error_code
-find_keys(krb5_context context,
- krb5_kdc_configuration *config,
- const hdb_entry_ex *client,
- const char *client_name,
- const hdb_entry_ex *server,
- const char *server_name,
- Key **ckey,
- krb5_enctype *cetype,
- Key **skey,
- krb5_enctype *setype,
- krb5_enctype *etypes,
- unsigned num_etypes)
-{
- krb5_error_code ret;
-
- if(client){
- /* find client key */
- ret = find_etype(context, client, etypes, num_etypes, ckey, cetype);
- if (ret) {
- kdc_log(context, config, 0,
- "Client (%s) has no support for etypes", client_name);
- return ret;
- }
- }
-
- if(server){
- /* find server key */
- ret = find_etype(context, server, etypes, num_etypes, skey, setype);
- if (ret) {
- kdc_log(context, config, 0,
- "Server (%s) has no support for etypes", server_name);
- return ret;
- }
- }
- return 0;
-}
-
-static krb5_error_code
-make_anonymous_principalname (PrincipalName *pn)
+krb5_error_code
+_kdc_make_anonymous_principalname (PrincipalName *pn)
{
pn->name_type = KRB5_NT_PRINCIPAL;
pn->name_string.len = 1;
@@ -171,12 +133,12 @@ make_anonymous_principalname (PrincipalName *pn)
return 0;
}
-static void
-log_timestamp(krb5_context context,
- krb5_kdc_configuration *config,
- const char *type,
- KerberosTime authtime, KerberosTime *starttime,
- KerberosTime endtime, KerberosTime *renew_till)
+void
+_kdc_log_timestamp(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *type,
+ KerberosTime authtime, KerberosTime *starttime,
+ KerberosTime endtime, KerberosTime *renew_till)
{
char authtime_str[100], starttime_str[100],
endtime_str[100], renewtime_str[100];
@@ -248,15 +210,15 @@ log_patypes(krb5_context context,
*/
-static krb5_error_code
-encode_reply(krb5_context context,
- krb5_kdc_configuration *config,
- KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek,
- krb5_enctype etype,
- int skvno, EncryptionKey *skey,
- int ckvno, EncryptionKey *ckey,
- const char **e_text,
- krb5_data *reply)
+krb5_error_code
+_kdc_encode_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek,
+ krb5_enctype etype,
+ int skvno, const EncryptionKey *skey,
+ int ckvno, const EncryptionKey *ckey,
+ const char **e_text,
+ krb5_data *reply)
{
unsigned char *buf;
size_t buf_size;
@@ -795,10 +757,10 @@ _kdc_check_flags(krb5_context context,
* these checks
*/
-static krb5_boolean
-check_addresses(krb5_context context,
- krb5_kdc_configuration *config,
- HostAddresses *addresses, const struct sockaddr *from)
+krb5_boolean
+_kdc_check_addresses(krb5_context context,
+ krb5_kdc_configuration *config,
+ HostAddresses *addresses, const struct sockaddr *from)
{
krb5_error_code ret;
krb5_address addr;
@@ -843,13 +805,14 @@ _kdc_as_rep(krb5_context context,
const krb5_data *req_buffer,
krb5_data *reply,
const char *from,
- struct sockaddr *from_addr)
+ struct sockaddr *from_addr,
+ int datagram_reply)
{
KDC_REQ_BODY *b = &req->req_body;
AS_REP rep;
KDCOptions f = b->kdc_options;
hdb_entry_ex *client = NULL, *server = NULL;
- krb5_enctype cetype, setype;
+ krb5_enctype cetype, setype, sessionetype;
EncTicketPart et;
EncKDCRepPart ek;
krb5_principal client_princ = NULL, server_princ = NULL;
@@ -869,12 +832,15 @@ _kdc_as_rep(krb5_context context,
ret = KRB5KRB_ERR_GENERIC;
e_text = "No server in request";
} else{
- _krb5_principalname2krb5_principal (context, &server_princ,
- *(b->sname), b->realm);
+ _krb5_principalname2krb5_principal (context,
+ &server_princ,
+ *(b->sname),
+ b->realm);
ret = krb5_unparse_name(context, server_princ, &server_name);
}
if (ret) {
- kdc_log(context, config, 0, "AS-REQ malformed server name from %s", from);
+ kdc_log(context, config, 0,
+ "AS-REQ malformed server name from %s", from);
goto out;
}
@@ -882,12 +848,15 @@ _kdc_as_rep(krb5_context context,
ret = KRB5KRB_ERR_GENERIC;
e_text = "No client in request";
} else {
- _krb5_principalname2krb5_principal (context, &client_princ,
- *(b->cname), b->realm);
+ _krb5_principalname2krb5_principal (context,
+ &client_princ,
+ *(b->cname),
+ b->realm);
ret = krb5_unparse_name(context, client_princ, &client_name);
}
if (ret) {
- kdc_log(context, config, 0, "AS-REQ malformed client name from %s", from);
+ kdc_log(context, config, 0,
+ "AS-REQ malformed client name from %s", from);
goto out;
}
@@ -895,7 +864,7 @@ _kdc_as_rep(krb5_context context,
client_name, from, server_name);
ret = _kdc_db_fetch(context, config, client_princ,
- HDB_F_GET_CLIENT, &client);
+ HDB_F_GET_CLIENT, NULL, &client);
if(ret){
kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name,
krb5_get_err_text(context, ret));
@@ -904,7 +873,8 @@ _kdc_as_rep(krb5_context context,
}
ret = _kdc_db_fetch(context, config, server_princ,
- HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, &server);
+ HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ NULL, &server);
if(ret){
kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name,
krb5_get_err_text(context, ret));
@@ -943,11 +913,11 @@ _kdc_as_rep(krb5_context context,
e_text = "No PKINIT PA found";
i = 0;
- if ((pa = find_padata(req, &i, KRB5_PADATA_PK_AS_REQ)))
+ if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ)))
;
if (pa == NULL) {
i = 0;
- if((pa = find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN)))
+ if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN)))
;
}
if (pa) {
@@ -995,7 +965,7 @@ _kdc_as_rep(krb5_context context,
i = 0;
e_text = "No ENC-TS found";
- while((pa = find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
+ while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
krb5_data ts_data;
PA_ENC_TS_ENC p;
size_t len;
@@ -1056,7 +1026,7 @@ _kdc_as_rep(krb5_context context,
if(ret){
krb5_error_code ret2;
ret2 = krb5_enctype_to_string(context,
- pa_key->key.keytype, &str);
+ pa_key->key.keytype, &str);
if (ret2)
str = NULL;
kdc_log(context, config, 5,
@@ -1092,9 +1062,18 @@ _kdc_as_rep(krb5_context context,
}
free_PA_ENC_TS_ENC(&p);
if (abs(kdc_time - p.patimestamp) > context->max_skew) {
+ char client_time[100];
+
+ krb5_format_time(context, p.patimestamp,
+ client_time, sizeof(client_time), TRUE);
+
ret = KRB5KRB_AP_ERR_SKEW;
kdc_log(context, config, 0,
- "Too large time skew -- %s", client_name);
+ "Too large time skew, client time %s is out by %u > %u seconds -- %s",
+ client_time,
+ (unsigned)abs(kdc_time - p.patimestamp),
+ context->max_skew,
+ client_name);
/*
* the following is needed to make windows clients
* to retry using the timestamp in the error message
@@ -1162,7 +1141,7 @@ _kdc_as_rep(krb5_context context,
* 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.
- */
+ */
/* XXX check ret */
if (only_older_enctype_p(req))
@@ -1197,14 +1176,54 @@ _kdc_as_rep(krb5_context context,
goto out2;
}
- ret = find_keys(context, config,
- client, client_name,
- server, server_name,
- &ckey, &cetype, &skey, &setype,
- b->etype.val, b->etype.len);
- if(ret)
+ /*
+ * 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.
+ */
+
+ 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;
+ }
+ ret = _kdc_get_preferred_key(context, config,
+ server, server_name,
+ &setype, &skey);
+ if(ret)
+ goto out;
+
+ {
+ const krb5_enctype *p;
+ int i, j;
+
+ p = krb5_kerberos_enctypes(context);
+
+ sessionetype = ETYPE_NULL;
+
+ for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ for (j = 0; j < b->etype.len; j++) {
+ if (p[i] == b->etype.val[j]) {
+ sessionetype = p[i];
+ break;
+ }
+ }
+ }
+ if (sessionetype == ETYPE_NULL) {
+ kdc_log(context, config, 0,
+ "Client (%s) from %s has no common enctypes with KDC"
+ "to use for the session key",
+ client_name, from);
+ goto out;
+ }
+ }
+
{
struct rk_strpool *p = NULL;
char *str;
@@ -1268,9 +1287,9 @@ _kdc_as_rep(krb5_context context,
rep.msg_type = krb_as_rep;
copy_Realm(&client->entry.principal->realm, &rep.crealm);
if (f.request_anonymous)
- make_anonymous_principalname (&rep.cname);
+ _kdc_make_anonymous_principalname (&rep.cname);
else
- _krb5_principal2principalname(&rep.cname,
+ _krb5_principal2principalname(&rep.cname,
client->entry.principal);
rep.ticket.tkt_vno = 5;
copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
@@ -1304,14 +1323,14 @@ _kdc_as_rep(krb5_context context,
}
/* check for valid set of addresses */
- if(!check_addresses(context, config, b->addresses, from_addr)) {
+ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
ret = KRB5KRB_AP_ERR_BADADDR;
kdc_log(context, config, 0,
"Bad address list requested -- %s", client_name);
goto out;
}
- krb5_generate_random_keyblock(context, setype, &et.key);
+ krb5_generate_random_keyblock(context, sessionetype, &et.key);
copy_PrincipalName(&rep.cname, &et.cname);
copy_Realm(&rep.crealm, &et.crealm);
@@ -1327,7 +1346,7 @@ _kdc_as_rep(krb5_context context,
et.flags.invalid = 1;
et.flags.postdated = 1; /* XXX ??? */
}
- fix_time(&b->till);
+ _kdc_fix_time(&b->till);
t = *b->till;
/* be careful not overflowing */
@@ -1392,7 +1411,7 @@ _kdc_as_rep(krb5_context context,
ek.last_req.len = 0;
if (client->entry.pw_end
&& (config->kdc_warn_pwexpire == 0
- || kdc_time + config->kdc_warn_pwexpire <= *client->entry.pw_end)) {
+ || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME;
ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
++ek.last_req.len;
@@ -1472,15 +1491,37 @@ _kdc_as_rep(krb5_context context,
goto out;
}
- log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
- et.endtime, et.renew_till);
+ _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* do this as the last thing since this signs the EncTicketPart */
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ server,
+ setype,
+ NULL,
+ NULL,
+ &et);
+ if (ret)
+ goto out;
- ret = encode_reply(context, config,
- &rep, &et, &ek, setype, server->entry.kvno, &skey->key,
- client->entry.kvno, reply_key, &e_text, reply);
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, setype, server->entry.kvno,
+ &skey->key, client->entry.kvno,
+ reply_key, &e_text, reply);
free_EncTicketPart(&et);
free_EncKDCRepPart(&ek);
- out:
+ if (ret)
+ goto out;
+
+ /* */
+ if (datagram_reply && reply->length > config->max_datagram_reply_length) {
+ krb5_data_free(reply);
+ ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ e_text = "Reply packet too large";
+ }
+
+out:
free_AS_REP(&rep);
if(ret){
krb5_mk_error(context,
@@ -1494,7 +1535,7 @@ _kdc_as_rep(krb5_context context,
reply);
ret = 0;
}
- out2:
+out2:
#ifdef PKINIT
if (pkp)
_kdc_pk_free_client_param(context, pkp);
@@ -1511,1089 +1552,3 @@ _kdc_as_rep(krb5_context context,
_kdc_free_ent(context, server);
return ret;
}
-
-
-static krb5_error_code
-check_tgs_flags(krb5_context context,
- krb5_kdc_configuration *config,
- KDC_REQ_BODY *b, EncTicketPart *tgt, EncTicketPart *et)
-{
- KDCOptions f = b->kdc_options;
-
- if(f.validate){
- if(!tgt->flags.invalid || tgt->starttime == NULL){
- kdc_log(context, config, 0,
- "Bad request to validate ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- if(*tgt->starttime > kdc_time){
- kdc_log(context, config, 0,
- "Early request to validate ticket");
- return KRB5KRB_AP_ERR_TKT_NYV;
- }
- /* XXX tkt = tgt */
- et->flags.invalid = 0;
- }else if(tgt->flags.invalid){
- kdc_log(context, config, 0,
- "Ticket-granting ticket has INVALID flag set");
- return KRB5KRB_AP_ERR_TKT_INVALID;
- }
-
- if(f.forwardable){
- if(!tgt->flags.forwardable){
- kdc_log(context, config, 0,
- "Bad request for forwardable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.forwardable = 1;
- }
- if(f.forwarded){
- if(!tgt->flags.forwardable){
- kdc_log(context, config, 0,
- "Request to forward non-forwardable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.forwarded = 1;
- et->caddr = b->addresses;
- }
- if(tgt->flags.forwarded)
- et->flags.forwarded = 1;
-
- if(f.proxiable){
- if(!tgt->flags.proxiable){
- kdc_log(context, config, 0,
- "Bad request for proxiable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.proxiable = 1;
- }
- if(f.proxy){
- if(!tgt->flags.proxiable){
- kdc_log(context, config, 0,
- "Request to proxy non-proxiable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.proxy = 1;
- et->caddr = b->addresses;
- }
- if(tgt->flags.proxy)
- et->flags.proxy = 1;
-
- if(f.allow_postdate){
- if(!tgt->flags.may_postdate){
- kdc_log(context, config, 0,
- "Bad request for post-datable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.may_postdate = 1;
- }
- if(f.postdated){
- if(!tgt->flags.may_postdate){
- kdc_log(context, config, 0,
- "Bad request for postdated ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- if(b->from)
- *et->starttime = *b->from;
- et->flags.postdated = 1;
- et->flags.invalid = 1;
- }else if(b->from && *b->from > kdc_time + context->max_skew){
- kdc_log(context, config, 0, "Ticket cannot be postdated");
- return KRB5KDC_ERR_CANNOT_POSTDATE;
- }
-
- if(f.renewable){
- if(!tgt->flags.renewable){
- kdc_log(context, config, 0,
- "Bad request for renewable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- et->flags.renewable = 1;
- ALLOC(et->renew_till);
- fix_time(&b->rtime);
- *et->renew_till = *b->rtime;
- }
- if(f.renew){
- time_t old_life;
- if(!tgt->flags.renewable || tgt->renew_till == NULL){
- kdc_log(context, config, 0,
- "Request to renew non-renewable ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- old_life = tgt->endtime;
- if(tgt->starttime)
- old_life -= *tgt->starttime;
- else
- old_life -= tgt->authtime;
- et->endtime = *et->starttime + old_life;
- if (et->renew_till != NULL)
- et->endtime = min(*et->renew_till, et->endtime);
- }
-
- /* checks for excess flags */
- if(f.request_anonymous && !config->allow_anonymous){
- kdc_log(context, config, 0,
- "Request for anonymous ticket");
- return KRB5KDC_ERR_BADOPTION;
- }
- return 0;
-}
-
-static krb5_error_code
-fix_transited_encoding(krb5_context context,
- krb5_kdc_configuration *config,
- krb5_boolean check_policy,
- TransitedEncoding *tr,
- EncTicketPart *et,
- const char *client_realm,
- const char *server_realm,
- const char *tgt_realm)
-{
- krb5_error_code ret = 0;
- char **realms, **tmp;
- int num_realms;
- int i;
-
- switch (tr->tr_type) {
- case DOMAIN_X500_COMPRESS:
- break;
- case 0:
- /*
- * Allow empty content of type 0 because that is was Microsoft
- * generates in their TGT.
- */
- if (tr->contents.length == 0)
- break;
- kdc_log(context, config, 0,
- "Transited type 0 with non empty content");
- return KRB5KDC_ERR_TRTYPE_NOSUPP;
- default:
- kdc_log(context, config, 0,
- "Unknown transited type: %u", tr->tr_type);
- return KRB5KDC_ERR_TRTYPE_NOSUPP;
- }
-
- ret = krb5_domain_x500_decode(context,
- tr->contents,
- &realms,
- &num_realms,
- client_realm,
- server_realm);
- if(ret){
- krb5_warn(context, ret,
- "Decoding transited encoding");
- return ret;
- }
- if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
- /* not us, so add the previous realm to transited set */
- if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
- ret = ERANGE;
- goto free_realms;
- }
- tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
- if(tmp == NULL){
- ret = ENOMEM;
- goto free_realms;
- }
- realms = tmp;
- realms[num_realms] = strdup(tgt_realm);
- if(realms[num_realms] == NULL){
- ret = ENOMEM;
- goto free_realms;
- }
- num_realms++;
- }
- if(num_realms == 0) {
- if(strcmp(client_realm, server_realm))
- kdc_log(context, config, 0,
- "cross-realm %s -> %s", client_realm, server_realm);
- } else {
- size_t l = 0;
- char *rs;
- for(i = 0; i < num_realms; i++)
- l += strlen(realms[i]) + 2;
- rs = malloc(l);
- if(rs != NULL) {
- *rs = '\0';
- for(i = 0; i < num_realms; i++) {
- if(i > 0)
- strlcat(rs, ", ", l);
- strlcat(rs, realms[i], l);
- }
- kdc_log(context, config, 0,
- "cross-realm %s -> %s via [%s]",
- client_realm, server_realm, rs);
- free(rs);
- }
- }
- if(check_policy) {
- ret = krb5_check_transited(context, client_realm,
- server_realm,
- realms, num_realms, NULL);
- if(ret) {
- krb5_warn(context, ret, "cross-realm %s -> %s",
- client_realm, server_realm);
- goto free_realms;
- }
- et->flags.transited_policy_checked = 1;
- }
- et->transited.tr_type = DOMAIN_X500_COMPRESS;
- ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
- if(ret)
- krb5_warn(context, ret, "Encoding transited encoding");
- free_realms:
- for(i = 0; i < num_realms; i++)
- free(realms[i]);
- free(realms);
- return ret;
-}
-
-
-static krb5_error_code
-tgs_make_reply(krb5_context context,
- krb5_kdc_configuration *config,
- KDC_REQ_BODY *b,
- EncTicketPart *tgt,
- EncTicketPart *adtkt,
- AuthorizationData *auth_data,
- krb5_ticket *tgs_ticket,
- hdb_entry_ex *server,
- const char *server_name,
- hdb_entry_ex *client,
- krb5_principal client_principal,
- hdb_entry_ex *krbtgt,
- EncryptionKey *tgtkey,
- krb5_enctype cetype,
- const char **e_text,
- krb5_data *reply)
-{
- KDC_REP rep;
- EncKDCRepPart ek;
- EncTicketPart et;
- KDCOptions f = b->kdc_options;
- krb5_error_code ret;
- krb5_enctype etype;
- Key *skey;
- EncryptionKey *ekey;
- AuthorizationData *new_auth_data = NULL;
-
- if(adtkt) {
- int i;
- ekey = &adtkt->key;
- for(i = 0; i < b->etype.len; i++)
- if (b->etype.val[i] == adtkt->key.keytype)
- break;
- if(i == b->etype.len) {
- krb5_clear_error_string(context);
- return KRB5KDC_ERR_ETYPE_NOSUPP;
- }
- etype = b->etype.val[i];
- }else{
- ret = find_keys(context, config,
- NULL, NULL, server, server_name,
- NULL, NULL, &skey, &etype,
- b->etype.val, b->etype.len);
- if(ret)
- return ret;
- ekey = &skey->key;
- }
-
- memset(&rep, 0, sizeof(rep));
- memset(&et, 0, sizeof(et));
- memset(&ek, 0, sizeof(ek));
-
- rep.pvno = 5;
- rep.msg_type = krb_tgs_rep;
-
- et.authtime = tgt->authtime;
- fix_time(&b->till);
- et.endtime = min(tgt->endtime, *b->till);
- ALLOC(et.starttime);
- *et.starttime = kdc_time;
-
- ret = check_tgs_flags(context, config, b, tgt, &et);
- if(ret)
- goto out;
-
- /* We should check the transited encoding if:
- 1) the request doesn't ask not to be checked
- 2) globally enforcing a check
- 3) principal requires checking
- 4) we allow non-check per-principal, but principal isn't marked as allowing this
- 5) we don't globally allow this
- */
-
-#define GLOBAL_FORCE_TRANSITED_CHECK \
- (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
-#define GLOBAL_ALLOW_PER_PRINCIPAL \
- (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
-#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
- (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
-
-/* these will consult the database in future release */
-#define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
-#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
-
- ret = fix_transited_encoding(context, config,
- !f.disable_transited_check ||
- GLOBAL_FORCE_TRANSITED_CHECK ||
- PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
- !((GLOBAL_ALLOW_PER_PRINCIPAL &&
- 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));
- if(ret)
- goto out;
-
- copy_Realm(krb5_princ_realm(context, server->entry.principal),
- &rep.ticket.realm);
- _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal);
- copy_Realm(&tgt->crealm, &rep.crealm);
- if (f.request_anonymous)
- make_anonymous_principalname (&tgt->cname);
- else
- copy_PrincipalName(&tgt->cname, &rep.cname);
- rep.ticket.tkt_vno = 5;
-
- ek.caddr = et.caddr;
- if(et.caddr == NULL)
- et.caddr = tgt->caddr;
-
- {
- time_t life;
- life = et.endtime - *et.starttime;
- if(client && client->entry.max_life)
- life = min(life, *client->entry.max_life);
- if(server->entry.max_life)
- life = min(life, *server->entry.max_life);
- et.endtime = *et.starttime + life;
- }
- if(f.renewable_ok && tgt->flags.renewable &&
- et.renew_till == NULL && et.endtime < *b->till){
- et.flags.renewable = 1;
- ALLOC(et.renew_till);
- *et.renew_till = *b->till;
- }
- if(et.renew_till){
- time_t renew;
- renew = *et.renew_till - et.authtime;
- if(client && client->entry.max_renew)
- renew = min(renew, *client->entry.max_renew);
- if(server->entry.max_renew)
- renew = min(renew, *server->entry.max_renew);
- *et.renew_till = et.authtime + renew;
- }
-
- if(et.renew_till){
- *et.renew_till = min(*et.renew_till, *tgt->renew_till);
- *et.starttime = min(*et.starttime, *et.renew_till);
- et.endtime = min(et.endtime, *et.renew_till);
- }
-
- *et.starttime = min(*et.starttime, et.endtime);
-
- if(*et.starttime == et.endtime){
- ret = KRB5KDC_ERR_NEVER_VALID;
- goto out;
- }
- if(et.renew_till && et.endtime == *et.renew_till){
- free(et.renew_till);
- et.renew_till = NULL;
- et.flags.renewable = 0;
- }
-
- et.flags.pre_authent = tgt->flags.pre_authent;
- et.flags.hw_authent = tgt->flags.hw_authent;
- et.flags.anonymous = tgt->flags.anonymous;
- et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
-
-
- krb5_generate_random_keyblock(context, etype, &et.key);
-
- if (server->authz_data_tgs_req) {
- ret = server->authz_data_tgs_req(context, server,
- client_principal,
- tgs_ticket->ticket.authorization_data,
- tgs_ticket->ticket.authtime,
- tgtkey,
- ekey,
- &et.key,
- &new_auth_data);
- if (ret) {
- new_auth_data = NULL;
- }
- }
-
- /* XXX Check enc-authorization-data */
- et.authorization_data = new_auth_data;
-
- et.crealm = tgt->crealm;
- et.cname = tgt->cname;
-
- ek.key = et.key;
- /* MIT must have at least one last_req */
- ek.last_req.len = 1;
- ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
- ek.nonce = b->nonce;
- ek.flags = et.flags;
- ek.authtime = et.authtime;
- ek.starttime = et.starttime;
- ek.endtime = et.endtime;
- ek.renew_till = et.renew_till;
- ek.srealm = rep.ticket.realm;
- ek.sname = rep.ticket.sname;
-
- log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
- et.endtime, et.renew_till);
-
- /* It is somewhat unclear where the etype in the following
- encryption should come from. What we have is a session
- key in the passed tgt, and a list of preferred etypes
- *for the new ticket*. Should we pick the best possible
- etype, given the keytype in the tgt, or should we look
- at the etype list here as well? What if the tgt
- session key is DES3 and we want a ticket with a (say)
- CAST session key. Should the DES3 etype be added to the
- etype list, even if we don't want a session key with
- DES3? */
- ret = encode_reply(context, config,
- &rep, &et, &ek, etype, adtkt ? 0 : server->entry.kvno,
- ekey, 0, &tgt->key, e_text, reply);
- out:
- free_TGS_REP(&rep);
- free_TransitedEncoding(&et.transited);
- if(et.starttime)
- free(et.starttime);
- if(et.renew_till)
- free(et.renew_till);
- free_LastReq(&ek.last_req);
- memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
- free_EncryptionKey(&et.key);
- return ret;
-}
-
-static krb5_error_code
-tgs_check_authenticator(krb5_context context,
- krb5_kdc_configuration *config,
- krb5_auth_context ac,
- KDC_REQ_BODY *b,
- const char **e_text,
- krb5_keyblock *key)
-{
- krb5_authenticator auth;
- size_t len;
- unsigned char *buf;
- size_t buf_size;
- krb5_error_code ret;
- krb5_crypto crypto;
-
- krb5_auth_con_getauthenticator(context, ac, &auth);
- if(auth->cksum == NULL){
- kdc_log(context, config, 0, "No authenticator in request");
- ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
- goto out;
- }
- /*
- * according to RFC1510 it doesn't need to be keyed,
- * but according to the latest draft it needs to.
- */
- if (
-#if 0
-!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
- ||
-#endif
- !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
- kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
- auth->cksum->cksumtype);
- ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
- goto out;
- }
-
- /* XXX should not re-encode this */
- ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
- if(ret){
- kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
- if(buf_size != len) {
- free(buf);
- kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
- *e_text = "KDC internal error";
- ret = KRB5KRB_ERR_GENERIC;
- goto out;
- }
- ret = krb5_crypto_init(context, key, 0, &crypto);
- if (ret) {
- free(buf);
- kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
- krb5_get_err_text(context, ret));
- goto out;
- }
- ret = krb5_verify_checksum(context,
- crypto,
- KRB5_KU_TGS_REQ_AUTH_CKSUM,
- buf,
- len,
- auth->cksum);
- free(buf);
- krb5_crypto_destroy(context, crypto);
- if(ret){
- kdc_log(context, config, 0,
- "Failed to verify authenticator checksum: %s",
- krb5_get_err_text(context, ret));
- }
-out:
- free_Authenticator(auth);
- free(auth);
- return ret;
-}
-
-/*
- * return the realm of a krbtgt-ticket or NULL
- */
-
-static Realm
-get_krbtgt_realm(const PrincipalName *p)
-{
- if(p->name_string.len == 2
- && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
- return p->name_string.val[1];
- else
- return NULL;
-}
-
-static const char *
-find_rpath(krb5_context context, Realm crealm, Realm srealm)
-{
- const char *new_realm = krb5_config_get_string(context,
- NULL,
- "capaths",
- crealm,
- srealm,
- NULL);
- return new_realm;
-}
-
-
-static krb5_boolean
-need_referral(krb5_context context, krb5_principal server, krb5_realm **realms)
-{
- if(server->name.name_type != KRB5_NT_SRV_INST ||
- server->name.name_string.len != 2)
- return FALSE;
-
- return _krb5_get_host_realm_int(context, server->name.name_string.val[1],
- FALSE, realms) == 0;
-}
-
-static krb5_error_code
-tgs_rep2(krb5_context context,
- krb5_kdc_configuration *config,
- KDC_REQ_BODY *b,
- PA_DATA *tgs_req,
- krb5_data *reply,
- const char *from,
- const struct sockaddr *from_addr,
- time_t **csec,
- int **cusec)
-{
- krb5_ap_req ap_req;
- krb5_error_code ret;
- krb5_principal princ;
- krb5_auth_context ac = NULL;
- krb5_ticket *ticket = NULL;
- krb5_flags ap_req_options;
- krb5_flags verify_ap_req_flags;
- const char *e_text = NULL;
- krb5_crypto crypto;
-
- hdb_entry_ex *krbtgt = NULL;
- EncTicketPart *tgt;
- Key *tkey;
- krb5_enctype cetype;
- krb5_principal cp = NULL;
- krb5_principal sp = NULL;
- AuthorizationData *auth_data = NULL;
-
- *csec = NULL;
- *cusec = NULL;
-
- memset(&ap_req, 0, sizeof(ap_req));
- ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
- if(ret){
- kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
- krb5_get_err_text(context, ret));
- goto out2;
- }
-
- if(!get_krbtgt_realm(&ap_req.ticket.sname)){
- /* XXX check for ticket.sname == req.sname */
- kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
- ret = KRB5KDC_ERR_POLICY; /* ? */
- goto out2;
- }
-
- _krb5_principalname2krb5_principal(context, &princ,
- ap_req.ticket.sname,
- ap_req.ticket.realm);
-
- ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, &krbtgt);
-
- if(ret) {
- char *p;
- ret = krb5_unparse_name(context, princ, &p);
- if (ret != 0)
- p = "<unparse_name failed>";
- krb5_free_principal(context, princ);
- kdc_log(context, config, 0,
- "Ticket-granting ticket not found in database: %s: %s",
- p, krb5_get_err_text(context, ret));
- if (ret == 0)
- free(p);
- ret = KRB5KRB_AP_ERR_NOT_US;
- goto out2;
- }
-
- if(ap_req.ticket.enc_part.kvno &&
- *ap_req.ticket.enc_part.kvno != krbtgt->entry.kvno){
- char *p;
-
- ret = krb5_unparse_name (context, princ, &p);
- krb5_free_principal(context, princ);
- if (ret != 0)
- p = "<unparse_name failed>";
- kdc_log(context, config, 0,
- "Ticket kvno = %d, DB kvno = %d (%s)",
- *ap_req.ticket.enc_part.kvno,
- krbtgt->entry.kvno,
- p);
- if (ret == 0)
- free (p);
- ret = KRB5KRB_AP_ERR_BADKEYVER;
- goto out2;
- }
-
- ret = hdb_enctype2key(context, &krbtgt->entry,
- ap_req.ticket.enc_part.etype, &tkey);
- if(ret){
- char *str, *p;
- krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
- krb5_unparse_name(context, princ, &p);
- kdc_log(context, config, 0,
- "No server key with enctype %s found for %s", str, p);
- free(str);
- free(p);
- ret = KRB5KRB_AP_ERR_BADKEYVER;
- goto out2;
- }
-
- if (b->kdc_options.validate)
- verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
- else
- verify_ap_req_flags = 0;
-
- ret = krb5_verify_ap_req2(context,
- &ac,
- &ap_req,
- princ,
- &tkey->key,
- verify_ap_req_flags,
- &ap_req_options,
- &ticket,
- KRB5_KU_TGS_REQ_AUTH);
-
- krb5_free_principal(context, princ);
- if(ret) {
- kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
- krb5_get_err_text(context, ret));
- goto out2;
- }
-
- {
- krb5_authenticator auth;
-
- ret = krb5_auth_con_getauthenticator(context, ac, &auth);
- if (ret == 0) {
- *csec = malloc(sizeof(**csec));
- if (*csec == NULL) {
- krb5_free_authenticator(context, &auth);
- kdc_log(context, config, 0, "malloc failed");
- goto out2;
- }
- **csec = auth->ctime;
- *cusec = malloc(sizeof(**cusec));
- if (*cusec == NULL) {
- krb5_free_authenticator(context, &auth);
- kdc_log(context, config, 0, "malloc failed");
- goto out2;
- }
- **csec = auth->cusec;
- krb5_free_authenticator(context, &auth);
- }
- }
-
- cetype = ap_req.authenticator.etype;
-
- tgt = &ticket->ticket;
-
- ret = tgs_check_authenticator(context, config,
- ac, b, &e_text, &tgt->key);
- if (ret) {
- krb5_auth_con_free(context, ac);
- goto out2;
- }
-
- if (b->enc_authorization_data) {
- krb5_keyblock *subkey;
- krb5_data ad;
- 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",
- krb5_get_err_text(context, ret));
- goto out2;
- }
- if(subkey == NULL){
- ret = krb5_auth_con_getkey(context, ac, &subkey);
- if(ret) {
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "Failed to get session key: %s",
- krb5_get_err_text(context, ret));
- goto out2;
- }
- }
- if(subkey == NULL){
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0,
- "Failed to get key for enc-authorization-data");
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
- goto out2;
- }
- ret = krb5_crypto_init(context, subkey, 0, &crypto);
- if (ret) {
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
- krb5_get_err_text(context, ret));
- goto out2;
- }
- ret = krb5_decrypt_EncryptedData (context,
- crypto,
- KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY,
- b->enc_authorization_data,
- &ad);
- krb5_crypto_destroy(context, crypto);
- if(ret){
- krb5_auth_con_free(context, ac);
- kdc_log(context, config, 0, "Failed to decrypt enc-authorization-data");
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
- goto out2;
- }
- krb5_free_keyblock(context, subkey);
- ALLOC(auth_data);
- ret = decode_AuthorizationData(ad.data, ad.length, auth_data, NULL);
- if(ret){
- krb5_auth_con_free(context, ac);
- free(auth_data);
- auth_data = NULL;
- kdc_log(context, config, 0, "Failed to decode authorization data");
- ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
- goto out2;
- }
- }
-
- krb5_auth_con_free(context, ac);
-
- {
- PrincipalName *s;
- Realm r;
- char *spn = NULL, *cpn = NULL;
- hdb_entry_ex *server = NULL, *client = NULL;
- int nloop = 0;
- EncTicketPart adtkt;
- char opt_str[128];
-
- s = b->sname;
- r = b->realm;
- if(b->kdc_options.enc_tkt_in_skey){
- Ticket *t;
- hdb_entry_ex *uu;
- krb5_principal p;
- Key *uukey;
-
- if(b->additional_tickets == NULL ||
- b->additional_tickets->len == 0){
- ret = KRB5KDC_ERR_BADOPTION; /* ? */
- kdc_log(context, config, 0,
- "No second ticket present in request");
- goto out;
- }
- t = &b->additional_tickets->val[0];
- if(!get_krbtgt_realm(&t->sname)){
- kdc_log(context, config, 0,
- "Additional ticket is not a ticket-granting ticket");
- ret = KRB5KDC_ERR_POLICY;
- goto out2;
- }
- _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
- ret = _kdc_db_fetch(context, config, p,
- HDB_F_GET_CLIENT|HDB_F_GET_SERVER, &uu);
- krb5_free_principal(context, p);
- if(ret){
- if (ret == HDB_ERR_NOENTRY)
- ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
- goto out;
- }
- ret = hdb_enctype2key(context, &uu->entry,
- t->enc_part.etype, &uukey);
- if(ret){
- _kdc_free_ent(context, uu);
- ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
- goto out;
- }
- ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
- _kdc_free_ent(context, uu);
- if(ret)
- goto out;
- s = &adtkt.cname;
- r = adtkt.crealm;
- }
-
- _krb5_principalname2krb5_principal(context, &sp, *s, r);
- ret = krb5_unparse_name(context, sp, &spn);
- if (ret)
- goto out;
- _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
- ret = krb5_unparse_name(context, cp, &cpn);
- if (ret)
- goto out;
- unparse_flags (KDCOptions2int(b->kdc_options),
- asn1_KDCOptions_units(),
- opt_str, sizeof(opt_str));
- if(*opt_str)
- kdc_log(context, config, 0,
- "TGS-REQ %s from %s for %s [%s]",
- cpn, from, spn, opt_str);
- else
- kdc_log(context, config, 0,
- "TGS-REQ %s from %s for %s", cpn, from, spn);
- server_lookup:
- ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, &server);
-
- if(ret){
- const char *new_rlm;
- Realm req_rlm;
- krb5_realm *realms;
-
- if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
- if(nloop++ < 2) {
- new_rlm = find_rpath(context, tgt->crealm, req_rlm);
- if(new_rlm) {
- kdc_log(context, config, 5, "krbtgt for realm %s not found, trying %s",
- req_rlm, new_rlm);
- krb5_free_principal(context, sp);
- free(spn);
- krb5_make_principal(context, &sp, r,
- KRB5_TGS_NAME, new_rlm, NULL);
- ret = krb5_unparse_name(context, sp, &spn);
- if (ret)
- goto out;
- goto server_lookup;
- }
- }
- } else if(need_referral(context, sp, &realms)) {
- if (strcmp(realms[0], sp->realm) != 0) {
- kdc_log(context, config, 5,
- "Returning a referral to realm %s for "
- "server %s that was not found",
- realms[0], spn);
- krb5_free_principal(context, sp);
- free(spn);
- krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
- realms[0], NULL);
- ret = krb5_unparse_name(context, sp, &spn);
- if (ret)
- goto out;
- krb5_free_host_realm(context, realms);
- goto server_lookup;
- }
- krb5_free_host_realm(context, realms);
- }
- kdc_log(context, config, 0,
- "Server not found in database: %s: %s", spn,
- krb5_get_err_text(context, ret));
- if (ret == HDB_ERR_NOENTRY)
- ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
- goto out;
- }
-
- ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, &client);
- if(ret)
- kdc_log(context, config, 1, "Client not found in database: %s: %s",
- cpn, krb5_get_err_text(context, ret));
-
- /*
- * If the client belongs to the same realm as our krbtgt, it
- * should exist in the local database.
- *
- * If its not the same, check the "direction" on the krbtgt,
- * so its not a backward uni-directional trust.
- */
-
- if(strcmp(krb5_principal_get_realm(context, sp),
- krb5_principal_get_comp_string(context,
- krbtgt->entry.principal, 1)) == 0) {
- if(ret) {
- if (ret == HDB_ERR_NOENTRY)
- ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
- goto out;
- }
- } else {
- char *tpn;
- ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
- kdc_log(context, config, 0,
- "Request with wrong krbtgt: %s",
- (ret == 0) ? tpn : "<unknown>");
- if(ret == 0)
- free(tpn);
- ret = KRB5KRB_AP_ERR_NOT_US;
- goto out;
-
- }
-
- ret = _kdc_check_flags(context, config,
- client, cpn,
- server, spn,
- FALSE);
- if(ret)
- goto out;
-
- if((b->kdc_options.validate || b->kdc_options.renew) &&
- !krb5_principal_compare(context,
- krbtgt->entry.principal,
- server->entry.principal)){
- kdc_log(context, config, 0, "Inconsistent request.");
- ret = KRB5KDC_ERR_SERVER_NOMATCH;
- goto out;
- }
-
- /* check for valid set of addresses */
- if(!check_addresses(context, config, tgt->caddr, from_addr)) {
- ret = KRB5KRB_AP_ERR_BADADDR;
- kdc_log(context, config, 0, "Request from wrong address");
- goto out;
- }
-
- ret = tgs_make_reply(context,
- config,
- b,
- tgt,
- b->kdc_options.enc_tkt_in_skey ? &adtkt : NULL,
- auth_data,
- ticket,
- server,
- spn,
- client,
- cp,
- krbtgt,
- &tkey->key,
- cetype,
- &e_text,
- reply);
-
- out:
- free(spn);
- free(cpn);
-
- if(server)
- _kdc_free_ent(context, server);
- if(client)
- _kdc_free_ent(context, client);
- }
- out2:
- if(ret) {
- krb5_mk_error(context,
- ret,
- e_text,
- NULL,
- cp,
- sp,
- NULL,
- NULL,
- reply);
- free(*csec);
- free(*cusec);
- *csec = NULL;
- *cusec = NULL;
- }
- krb5_free_principal(context, cp);
- krb5_free_principal(context, sp);
- if (ticket)
- krb5_free_ticket(context, ticket);
- free_AP_REQ(&ap_req);
- if(auth_data){
- free_AuthorizationData(auth_data);
- free(auth_data);
- }
-
- if(krbtgt)
- _kdc_free_ent(context, krbtgt);
-
- return ret;
-}
-
-
-krb5_error_code
-_kdc_tgs_rep(krb5_context context,
- krb5_kdc_configuration *config,
- KDC_REQ *req,
- krb5_data *data,
- const char *from,
- struct sockaddr *from_addr)
-{
- krb5_error_code ret;
- int i = 0;
- PA_DATA *tgs_req = NULL;
- time_t *csec = NULL;
- int *cusec = NULL;
-
- if(req->padata == NULL){
- ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
- kdc_log(context, config, 0,
- "TGS-REQ from %s without PA-DATA", from);
- goto out;
- }
-
- tgs_req = find_padata(req, &i, KRB5_PADATA_TGS_REQ);
-
- if(tgs_req == NULL){
- ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
-
- kdc_log(context, config, 0,
- "TGS-REQ from %s without PA-TGS-REQ", from);
- goto out;
- }
- ret = tgs_rep2(context, config,
- &req->req_body, tgs_req, data, from, from_addr,
- &csec, &cusec);
-out:
- if(ret && data->data == NULL){
- krb5_mk_error(context,
- ret,
- NULL,
- NULL,
- NULL,
- NULL,
- csec,
- cusec,
- data);
- }
- free(csec);
- free(cusec);
- return 0;
-}
diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
new file mode 100644
index 0000000000..dcf29eb6e9
--- /dev/null
+++ b/source4/heimdal/kdc/krb5tgs.c
@@ -0,0 +1,1781 @@
+/*
+ * 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"
+
+RCSID("$Id: krb5tgs.c,v 1.16 2006/10/22 15:54:37 lha Exp $");
+
+/*
+ * return the realm of a krbtgt-ticket or NULL
+ */
+
+static Realm
+get_krbtgt_realm(const PrincipalName *p)
+{
+ if(p->name_string.len == 2
+ && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
+ return p->name_string.val[1];
+ else
+ return NULL;
+}
+
+/*
+ * The KDC might add a signed path to the ticket authorization data
+ * field. This is to avoid server impersonating clients and the
+ * request constrained delegation.
+ *
+ * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
+ * entry of type KRB5SignedPath.
+ */
+
+static krb5_error_code
+find_KRB5SignedPath(krb5_context context,
+ const AuthorizationData *ad,
+ krb5_data *data)
+{
+ AuthorizationData child;
+ krb5_error_code ret;
+ int pos;
+
+ if (ad == NULL || ad->len == 0)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ pos = ad->len - 1;
+
+ if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
+ ad->val[pos].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode "
+ "IF_RELEVANT with %d", ret);
+ return ret;
+ }
+
+ if (child.len != 1) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (data)
+ ret = der_copy_octet_string(&child.val[0].ad_data, data);
+ free_AuthorizationData(&child);
+ return ret;
+}
+
+krb5_error_code
+_kdc_add_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype enctype,
+ krb5_const_principal server,
+ KRB5SignedPathPrincipals *principals,
+ EncTicketPart *tkt)
+{
+ krb5_error_code ret;
+ KRB5SignedPath sp;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+ size_t size;
+
+ if (server && principals) {
+ ret = add_KRB5SignedPathPrincipals(principals, server);
+ if (ret)
+ goto out;
+ }
+
+ {
+ KRB5SignedPathData spd;
+
+ spd.encticket = *tkt;
+ spd.delegated = principals;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+ }
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ return ret;
+ }
+ }
+
+ /*
+ * Fill in KRB5SignedPath
+ */
+
+ sp.etype = enctype;
+ sp.delegated = principals;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
+ data.data, data.length, &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
+ free_Checksum(&sp.cksum);
+ if (ret)
+ goto out;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+
+ /*
+ * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
+ * authorization data field.
+ */
+
+ if (tkt->authorization_data == NULL) {
+ tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data));
+ if (tkt->authorization_data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ /* add the entry to the last element */
+ {
+ AuthorizationData ad = { 0, NULL };
+ AuthorizationDataElement ade;
+
+ ade.ad_type = KRB5_AUTHDATA_SIGNTICKET;
+ ade.ad_data = data;
+
+ ret = add_AuthorizationData(&ad, &ade);
+ krb5_data_free(&data);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, data.data, data.length,
+ &ad, &size, ret);
+ free_AuthorizationData(&ad);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+ ade.ad_data = data;
+
+ ret = add_AuthorizationData(tkt->authorization_data, &ade);
+ krb5_data_free(&data);
+ if (ret)
+ return ret;
+ }
+
+out:
+ return 0;
+}
+
+static krb5_error_code
+check_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ EncTicketPart *tkt,
+ KRB5SignedPathPrincipals **delegated,
+ int require_signedpath)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+
+ *delegated = NULL;
+
+ ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
+ if (ret == 0) {
+ KRB5SignedPathData spd;
+ KRB5SignedPath sp;
+ AuthorizationData *ad;
+ size_t size;
+
+ ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ return ret;
+
+ spd.encticket = *tkt;
+ /* the KRB5SignedPath is the last entry */
+ ad = spd.encticket.authorization_data;
+ if (--ad->len == 0)
+ spd.encticket.authorization_data = NULL;
+ spd.delegated = sp.delegated;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ ad->len++;
+ spd.encticket.authorization_data = ad;
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ }
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
+ data.data, data.length,
+ &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+
+ if (sp.delegated) {
+
+ *delegated = malloc(sizeof(*sp.delegated));
+ if (*delegated == NULL) {
+ free_KRB5SignedPath(&sp);
+ return ENOMEM;
+ }
+
+ ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ free(*delegated);
+ *delegated = NULL;
+ return ret;
+ }
+ }
+ free_KRB5SignedPath(&sp);
+
+ } else {
+ if (require_signedpath)
+ return KRB5KDC_ERR_BADOPTION;
+ }
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_tgs_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
+{
+ KDCOptions f = b->kdc_options;
+
+ if(f.validate){
+ if(!tgt->flags.invalid || tgt->starttime == NULL){
+ kdc_log(context, config, 0,
+ "Bad request to validate ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(*tgt->starttime > kdc_time){
+ kdc_log(context, config, 0,
+ "Early request to validate ticket");
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ /* XXX tkt = tgt */
+ et->flags.invalid = 0;
+ }else if(tgt->flags.invalid){
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket has INVALID flag set");
+ return KRB5KRB_AP_ERR_TKT_INVALID;
+ }
+
+ if(f.forwardable){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Bad request for forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwardable = 1;
+ }
+ if(f.forwarded){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Request to forward non-forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwarded = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.forwarded)
+ et->flags.forwarded = 1;
+
+ if(f.proxiable){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Bad request for proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxiable = 1;
+ }
+ if(f.proxy){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Request to proxy non-proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxy = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.proxy)
+ et->flags.proxy = 1;
+
+ if(f.allow_postdate){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for post-datable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.may_postdate = 1;
+ }
+ if(f.postdated){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for postdated ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(b->from)
+ *et->starttime = *b->from;
+ et->flags.postdated = 1;
+ et->flags.invalid = 1;
+ }else if(b->from && *b->from > kdc_time + context->max_skew){
+ kdc_log(context, config, 0, "Ticket cannot be postdated");
+ return KRB5KDC_ERR_CANNOT_POSTDATE;
+ }
+
+ if(f.renewable){
+ if(!tgt->flags.renewable){
+ kdc_log(context, config, 0,
+ "Bad request for renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.renewable = 1;
+ ALLOC(et->renew_till);
+ _kdc_fix_time(&b->rtime);
+ *et->renew_till = *b->rtime;
+ }
+ if(f.renew){
+ time_t old_life;
+ if(!tgt->flags.renewable || tgt->renew_till == NULL){
+ kdc_log(context, config, 0,
+ "Request to renew non-renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ old_life = tgt->endtime;
+ if(tgt->starttime)
+ old_life -= *tgt->starttime;
+ else
+ old_life -= tgt->authtime;
+ et->endtime = *et->starttime + old_life;
+ if (et->renew_till != NULL)
+ et->endtime = min(*et->renew_till, et->endtime);
+ }
+
+ /* checks for excess flags */
+ if(f.request_anonymous && !config->allow_anonymous){
+ kdc_log(context, config, 0,
+ "Request for anonymous ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_constrained_delegation(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client,
+ krb5_const_principal server)
+{
+ const HDB_Ext_Constrained_delegation_acl *acl;
+ krb5_error_code ret;
+ int i;
+
+ ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ if (acl) {
+ for (i = 0; i < acl->len; i++) {
+ if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
+ return 0;
+ }
+ }
+ kdc_log(context, config, 0,
+ "Bad request for constrained delegation");
+ return KRB5KDC_ERR_BADOPTION;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_flags (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *pstr)
+{
+ if(et->endtime < kdc_time){
+ kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+ if(et->flags.invalid){
+ kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fix_transited_encoding(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_boolean check_policy,
+ const TransitedEncoding *tr,
+ EncTicketPart *et,
+ const char *client_realm,
+ const char *server_realm,
+ const char *tgt_realm)
+{
+ krb5_error_code ret = 0;
+ char **realms, **tmp;
+ int num_realms;
+ int i;
+
+ switch (tr->tr_type) {
+ case DOMAIN_X500_COMPRESS:
+ break;
+ case 0:
+ /*
+ * Allow empty content of type 0 because that is was Microsoft
+ * generates in their TGT.
+ */
+ if (tr->contents.length == 0)
+ break;
+ kdc_log(context, config, 0,
+ "Transited type 0 with non empty content");
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ default:
+ kdc_log(context, config, 0,
+ "Unknown transited type: %u", tr->tr_type);
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ }
+
+ ret = krb5_domain_x500_decode(context,
+ tr->contents,
+ &realms,
+ &num_realms,
+ client_realm,
+ server_realm);
+ if(ret){
+ krb5_warn(context, ret,
+ "Decoding transited encoding");
+ return ret;
+ }
+ if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
+ /* not us, so add the previous realm to transited set */
+ if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
+ ret = ERANGE;
+ goto free_realms;
+ }
+ tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
+ if(tmp == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ realms = tmp;
+ realms[num_realms] = strdup(tgt_realm);
+ if(realms[num_realms] == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ num_realms++;
+ }
+ if(num_realms == 0) {
+ if(strcmp(client_realm, server_realm))
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s", client_realm, server_realm);
+ } else {
+ size_t l = 0;
+ char *rs;
+ for(i = 0; i < num_realms; i++)
+ l += strlen(realms[i]) + 2;
+ rs = malloc(l);
+ if(rs != NULL) {
+ *rs = '\0';
+ for(i = 0; i < num_realms; i++) {
+ if(i > 0)
+ strlcat(rs, ", ", l);
+ strlcat(rs, realms[i], l);
+ }
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s via [%s]",
+ client_realm, server_realm, rs);
+ free(rs);
+ }
+ }
+ if(check_policy) {
+ ret = krb5_check_transited(context, client_realm,
+ server_realm,
+ realms, num_realms, NULL);
+ if(ret) {
+ krb5_warn(context, ret, "cross-realm %s -> %s",
+ client_realm, server_realm);
+ goto free_realms;
+ }
+ et->flags.transited_policy_checked = 1;
+ }
+ et->transited.tr_type = DOMAIN_X500_COMPRESS;
+ ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
+ if(ret)
+ krb5_warn(context, ret, "Encoding transited encoding");
+ free_realms:
+ for(i = 0; i < num_realms; i++)
+ free(realms[i]);
+ free(realms);
+ return ret;
+}
+
+
+static krb5_error_code
+tgs_make_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ krb5_const_principal tgt_name,
+ const EncTicketPart *tgt,
+ const EncTicketPart *adtkt,
+ AuthorizationData *auth_data,
+ krb5_ticket *tgs_ticket,
+ hdb_entry_ex *server,
+ const char *server_name,
+ hdb_entry_ex *client,
+ krb5_principal client_principal,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ KRB5SignedPathPrincipals *spp,
+ EncryptionKey *tgtkey,
+ const char **e_text,
+ krb5_data *reply)
+{
+ KDC_REP rep;
+ EncKDCRepPart ek;
+ EncTicketPart et;
+ KDCOptions f = b->kdc_options;
+ krb5_error_code ret;
+ krb5_enctype etype;
+ Key *skey;
+ const EncryptionKey *ekey;
+ AuthorizationData *new_auth_data = NULL;
+
+ if(adtkt) {
+ int i;
+ ekey = &adtkt->key;
+ for(i = 0; i < b->etype.len; i++)
+ if (b->etype.val[i] == adtkt->key.keytype)
+ break;
+ if(i == b->etype.len) {
+ krb5_clear_error_string(context);
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
+ }
+ etype = b->etype.val[i];
+ }else{
+ ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
+ &skey, &etype);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Server (%s) has no support for etypes", server_name);
+ return ret;
+ }
+ ekey = &skey->key;
+ }
+
+ memset(&rep, 0, sizeof(rep));
+ memset(&et, 0, sizeof(et));
+ memset(&ek, 0, sizeof(ek));
+
+ rep.pvno = 5;
+ rep.msg_type = krb_tgs_rep;
+
+ et.authtime = tgt->authtime;
+ _kdc_fix_time(&b->till);
+ et.endtime = min(tgt->endtime, *b->till);
+ ALLOC(et.starttime);
+ *et.starttime = kdc_time;
+
+ ret = check_tgs_flags(context, config, b, tgt, &et);
+ if(ret)
+ goto out;
+
+ /* We should check the transited encoding if:
+ 1) the request doesn't ask not to be checked
+ 2) globally enforcing a check
+ 3) principal requires checking
+ 4) we allow non-check per-principal, but principal isn't marked as allowing this
+ 5) we don't globally allow this
+ */
+
+#define GLOBAL_FORCE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
+#define GLOBAL_ALLOW_PER_PRINCIPAL \
+ (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
+#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
+
+/* these will consult the database in future release */
+#define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
+#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
+
+ ret = fix_transited_encoding(context, config,
+ !f.disable_transited_check ||
+ GLOBAL_FORCE_TRANSITED_CHECK ||
+ PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
+ !((GLOBAL_ALLOW_PER_PRINCIPAL &&
+ 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));
+ if(ret)
+ goto out;
+
+ copy_Realm(krb5_princ_realm(context, server->entry.principal),
+ &rep.ticket.realm);
+ _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal);
+ copy_Realm(&tgt_name->realm, &rep.crealm);
+ if (f.request_anonymous)
+ _kdc_make_anonymous_principalname (&rep.cname);
+ else
+ copy_PrincipalName(&tgt_name->name, &rep.cname);
+ rep.ticket.tkt_vno = 5;
+
+ ek.caddr = et.caddr;
+ if(et.caddr == NULL)
+ et.caddr = tgt->caddr;
+
+ {
+ time_t life;
+ life = et.endtime - *et.starttime;
+ if(client && client->entry.max_life)
+ life = min(life, *client->entry.max_life);
+ if(server->entry.max_life)
+ life = min(life, *server->entry.max_life);
+ et.endtime = *et.starttime + life;
+ }
+ if(f.renewable_ok && tgt->flags.renewable &&
+ et.renew_till == NULL && et.endtime < *b->till){
+ et.flags.renewable = 1;
+ ALLOC(et.renew_till);
+ *et.renew_till = *b->till;
+ }
+ if(et.renew_till){
+ time_t renew;
+ renew = *et.renew_till - et.authtime;
+ if(client && client->entry.max_renew)
+ renew = min(renew, *client->entry.max_renew);
+ if(server->entry.max_renew)
+ renew = min(renew, *server->entry.max_renew);
+ *et.renew_till = et.authtime + renew;
+ }
+
+ if(et.renew_till){
+ *et.renew_till = min(*et.renew_till, *tgt->renew_till);
+ *et.starttime = min(*et.starttime, *et.renew_till);
+ et.endtime = min(et.endtime, *et.renew_till);
+ }
+
+ *et.starttime = min(*et.starttime, et.endtime);
+
+ if(*et.starttime == et.endtime){
+ ret = KRB5KDC_ERR_NEVER_VALID;
+ goto out;
+ }
+ if(et.renew_till && et.endtime == *et.renew_till){
+ free(et.renew_till);
+ et.renew_till = NULL;
+ et.flags.renewable = 0;
+ }
+
+ et.flags.pre_authent = tgt->flags.pre_authent;
+ et.flags.hw_authent = tgt->flags.hw_authent;
+ et.flags.anonymous = tgt->flags.anonymous;
+ et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
+
+
+ krb5_generate_random_keyblock(context, etype, &et.key);
+
+ if (server->authz_data_tgs_req) {
+ ret = server->authz_data_tgs_req(context, server,
+ client_principal,
+ tgs_ticket->ticket.authorization_data,
+ tgs_ticket->ticket.authtime,
+ tgtkey,
+ ekey,
+ &et.key,
+ &new_auth_data);
+ if (ret) {
+ new_auth_data = NULL;
+ }
+ }
+
+ /* XXX Check enc-authorization-data */
+ et.authorization_data = new_auth_data;
+
+ et.crealm = tgt->crealm;
+ et.cname = tgt_name->name;
+
+ ek.key = et.key;
+ /* MIT must have at least one last_req */
+ ek.last_req.len = 1;
+ ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
+ ek.nonce = b->nonce;
+ ek.flags = et.flags;
+ ek.authtime = et.authtime;
+ ek.starttime = et.starttime;
+ ek.endtime = et.endtime;
+ ek.renew_till = et.renew_till;
+ ek.srealm = rep.ticket.realm;
+ ek.sname = rep.ticket.sname;
+
+ _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* Don't sign cross realm tickets, they can't be checked anyway */
+ {
+ char *r = get_krbtgt_realm(&ek.sname);
+
+ if (r == NULL || strcmp(r, ek.srealm) == 0) {
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ krbtgt_etype,
+ NULL,
+ NULL,
+ &et);
+ if (ret)
+ goto out;
+ }
+ }
+
+ /* It is somewhat unclear where the etype in the following
+ encryption should come from. What we have is a session
+ key in the passed tgt, and a list of preferred etypes
+ *for the new ticket*. Should we pick the best possible
+ etype, given the keytype in the tgt, or should we look
+ at the etype list here as well? What if the tgt
+ session key is DES3 and we want a ticket with a (say)
+ CAST session key. Should the DES3 etype be added to the
+ etype list, even if we don't want a session key with
+ DES3? */
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, etype,
+ adtkt ? 0 : server->entry.kvno,
+ ekey, 0, &tgt->key, e_text, reply);
+out:
+ free_TGS_REP(&rep);
+ free_TransitedEncoding(&et.transited);
+ if(et.starttime)
+ free(et.starttime);
+ if(et.renew_till)
+ free(et.renew_till);
+ if(et.authorization_data) {
+ free_AuthorizationData(et.authorization_data);
+ free(et.authorization_data);
+ }
+ free_LastReq(&ek.last_req);
+ memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
+ free_EncryptionKey(&et.key);
+ return ret;
+}
+
+static krb5_error_code
+tgs_check_authenticator(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_auth_context ac,
+ KDC_REQ_BODY *b,
+ const char **e_text,
+ krb5_keyblock *key)
+{
+ krb5_authenticator auth;
+ size_t len;
+ unsigned char *buf;
+ size_t buf_size;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ krb5_auth_con_getauthenticator(context, ac, &auth);
+ if(auth->cksum == NULL){
+ kdc_log(context, config, 0, "No authenticator in request");
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+ /*
+ * according to RFC1510 it doesn't need to be keyed,
+ * but according to the latest draft it needs to.
+ */
+ if (
+#if 0
+!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
+ ||
+#endif
+ !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
+ kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
+ auth->cksum->cksumtype);
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+
+ /* XXX should not re-encode this */
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_TGS_REQ_AUTH_CKSUM,
+ buf,
+ len,
+ auth->cksum);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to verify authenticator checksum: %s",
+ krb5_get_err_text(context, ret));
+ }
+out:
+ free_Authenticator(auth);
+ free(auth);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const char *
+find_rpath(krb5_context context, Realm crealm, Realm srealm)
+{
+ const char *new_realm = krb5_config_get_string(context,
+ NULL,
+ "capaths",
+ crealm,
+ srealm,
+ NULL);
+ return new_realm;
+}
+
+
+static krb5_boolean
+need_referral(krb5_context context, krb5_principal server, krb5_realm **realms)
+{
+ if(server->name.name_type != KRB5_NT_SRV_INST ||
+ server->name.name_string.len != 2)
+ return FALSE;
+
+ return _krb5_get_host_realm_int(context, server->name.name_string.val[1],
+ FALSE, realms) == 0;
+}
+
+static krb5_error_code
+tgs_parse_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ PA_DATA *tgs_req,
+ hdb_entry_ex **krbtgt,
+ krb5_enctype *krbtgt_etype,
+ krb5_ticket **ticket,
+ const char **e_text,
+ const char *from,
+ const struct sockaddr *from_addr,
+ time_t **csec,
+ int **cusec,
+ AuthorizationData **auth_data,
+ EncryptionKey **tgtkey)
+{
+ krb5_ap_req ap_req;
+ krb5_error_code ret;
+ krb5_principal princ;
+ krb5_auth_context ac = NULL;
+ krb5_flags ap_req_options;
+ krb5_flags verify_ap_req_flags;
+ krb5_crypto crypto;
+ Key *tkey;
+
+ *auth_data = NULL;
+ *csec = NULL;
+ *cusec = NULL;
+
+ memset(&ap_req, 0, sizeof(ap_req));
+ ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ if(!get_krbtgt_realm(&ap_req.ticket.sname)){
+ /* XXX check for ticket.sname == req.sname */
+ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY; /* ? */
+ goto out;
+ }
+
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+
+ ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
+
+ if(ret) {
+ char *p;
+ ret = krb5_unparse_name(context, princ, &p);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ krb5_free_principal(context, princ);
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket not found in database: %s: %s",
+ p, krb5_get_err_text(context, ret));
+ if (ret == 0)
+ free(p);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ if(ap_req.ticket.enc_part.kvno &&
+ *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
+ char *p;
+
+ ret = krb5_unparse_name (context, princ, &p);
+ krb5_free_principal(context, princ);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ kdc_log(context, config, 0,
+ "Ticket kvno = %d, DB kvno = %d (%s)",
+ *ap_req.ticket.enc_part.kvno,
+ (*krbtgt)->entry.kvno,
+ p);
+ if (ret == 0)
+ free (p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ *krbtgt_etype = ap_req.ticket.enc_part.etype;
+
+ ret = hdb_enctype2key(context, &(*krbtgt)->entry,
+ ap_req.ticket.enc_part.etype, &tkey);
+ if(ret){
+ char *str, *p;
+ krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
+ krb5_unparse_name(context, princ, &p);
+ kdc_log(context, config, 0,
+ "No server key with enctype %s found for %s", str, p);
+ free(str);
+ free(p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ *tgtkey = &tkey->key;
+
+ if (b->kdc_options.validate)
+ verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
+ else
+ verify_ap_req_flags = 0;
+
+ ret = krb5_verify_ap_req2(context,
+ &ac,
+ &ap_req,
+ princ,
+ &tkey->key,
+ verify_ap_req_flags,
+ &ap_req_options,
+ ticket,
+ KRB5_KU_TGS_REQ_AUTH);
+
+ krb5_free_principal(context, princ);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ {
+ krb5_authenticator auth;
+
+ ret = krb5_auth_con_getauthenticator(context, ac, &auth);
+ if (ret == 0) {
+ *csec = malloc(sizeof(**csec));
+ if (*csec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **csec = auth->ctime;
+ *cusec = malloc(sizeof(**cusec));
+ if (*cusec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **cusec = auth->cusec;
+ krb5_free_authenticator(context, &auth);
+ }
+ }
+
+ ret = tgs_check_authenticator(context, config,
+ ac, b, e_text, &(*ticket)->ticket.key);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ goto out;
+ }
+
+ if (b->enc_authorization_data) {
+ krb5_keyblock *subkey;
+ krb5_data ad;
+ 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",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(subkey == NULL){
+ ret = krb5_auth_con_getkey(context, ac, &subkey);
+ if(ret) {
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get session key: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ }
+ if(subkey == NULL){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to get key for enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = krb5_crypto_init(context, subkey, 0, &crypto);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ 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_TGS_REQ_AUTH_DAT_SUBKEY,
+ b->enc_authorization_data,
+ &ad);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to decrypt enc-authorization-data");
+ 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);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ free(*auth_data);
+ *auth_data = NULL;
+ kdc_log(context, config, 0, "Failed to decode authorization data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ }
+
+ krb5_auth_con_free(context, ac);
+
+out:
+ free_AP_REQ(&ap_req);
+
+ return ret;
+}
+
+static krb5_error_code
+tgs_build_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ KDC_REQ_BODY *b,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ krb5_ticket *ticket,
+ krb5_data *reply,
+ const char *from,
+ const char **e_text,
+ AuthorizationData *auth_data,
+ EncryptionKey *tgtkey,
+ const struct sockaddr *from_addr)
+{
+ krb5_error_code ret;
+ krb5_principal cp = NULL, sp = NULL;
+ krb5_principal client_principal = NULL;
+ char *spn = NULL, *cpn = NULL;
+ hdb_entry_ex *server = NULL, *client = NULL;
+ EncTicketPart *tgt = &ticket->ticket;
+ KRB5SignedPathPrincipals *spp = NULL;
+
+ PrincipalName *s;
+ Realm r;
+ int nloop = 0;
+ EncTicketPart adtkt;
+ char opt_str[128];
+ int require_signedpath = 0;
+
+ memset(&adtkt, 0, sizeof(adtkt));
+
+ s = b->sname;
+ r = b->realm;
+
+ if(b->kdc_options.enc_tkt_in_skey){
+ Ticket *t;
+ hdb_entry_ex *uu;
+ krb5_principal p;
+ Key *uukey;
+
+ if(b->additional_tickets == NULL ||
+ b->additional_tickets->len == 0){
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ kdc_log(context, config, 0,
+ "No second ticket present in request");
+ goto out;
+ }
+ t = &b->additional_tickets->val[0];
+ if(!get_krbtgt_realm(&t->sname)){
+ kdc_log(context, config, 0,
+ "Additional ticket is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY;
+ goto out;
+ }
+ _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
+ ret = _kdc_db_fetch(context, config, p,
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
+ NULL, &uu);
+ krb5_free_principal(context, p);
+ if(ret){
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+ ret = hdb_enctype2key(context, &uu->entry,
+ t->enc_part.etype, &uukey);
+ if(ret){
+ _kdc_free_ent(context, uu);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+ ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
+ _kdc_free_ent(context, uu);
+ if(ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, spn);
+ if (ret)
+ goto out;
+
+ s = &adtkt.cname;
+ r = adtkt.crealm;
+ }
+
+ _krb5_principalname2krb5_principal(context, &sp, *s, r);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
+ ret = krb5_unparse_name(context, cp, &cpn);
+ if (ret)
+ goto out;
+ unparse_flags (KDCOptions2int(b->kdc_options),
+ asn1_KDCOptions_units(),
+ opt_str, sizeof(opt_str));
+ if(*opt_str)
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s [%s]",
+ cpn, from, spn, opt_str);
+ else
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s", cpn, from, spn);
+
+ /*
+ * Fetch server
+ */
+
+server_lookup:
+ ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server);
+
+ if(ret){
+ const char *new_rlm;
+ Realm req_rlm;
+ krb5_realm *realms;
+
+ if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
+ if(nloop++ < 2) {
+ new_rlm = find_rpath(context, tgt->crealm, req_rlm);
+ if(new_rlm) {
+ kdc_log(context, config, 5, "krbtgt for realm %s "
+ "not found, trying %s",
+ req_rlm, new_rlm);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r,
+ KRB5_TGS_NAME, new_rlm, NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ goto server_lookup;
+ }
+ }
+ } else if(need_referral(context, sp, &realms)) {
+ if (strcmp(realms[0], sp->realm) != 0) {
+ kdc_log(context, config, 5,
+ "Returning a referral to realm %s for "
+ "server %s that was not found",
+ realms[0], spn);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
+ realms[0], NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ krb5_free_host_realm(context, realms);
+ goto server_lookup;
+ }
+ krb5_free_host_realm(context, realms);
+ }
+ kdc_log(context, config, 0,
+ "Server not found in database: %s: %s", spn,
+ krb5_get_err_text(context, ret));
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client);
+ if(ret) {
+ const char *krbtgt_realm;
+
+ /*
+ * If the client belongs to the same realm as our krbtgt, it
+ * should exist in the local database.
+ *
+ */
+
+ krbtgt_realm =
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal, 1);
+
+ if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ kdc_log(context, config, 1, "Client no longer in database: %s",
+ cpn);
+ goto out;
+ }
+
+ kdc_log(context, config, 1, "Client not found in database: %s: %s",
+ cpn, krb5_get_err_text(context, ret));
+ }
+
+ /*
+ * Check that service is in the same realm as the krbtgt. If its
+ * not the same, its someone that is using a uni-directional trust
+ * backward.
+ */
+
+ if (strcmp(krb5_principal_get_realm(context, sp),
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal,
+ 1)) != 0) {
+ char *tpn;
+ ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
+ kdc_log(context, config, 0,
+ "Request with wrong krbtgt: %s",
+ (ret == 0) ? tpn : "<unknown>");
+ if(ret == 0)
+ free(tpn);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ client_principal = cp;
+
+ if (client) {
+ const PA_DATA *sdata;
+ int i = 0;
+
+ sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
+ if (sdata) {
+ krb5_crypto crypto;
+ krb5_data datack;
+ PA_S4U2Self self;
+ char *selfcpn = NULL;
+ const char *str;
+
+ ret = decode_PA_S4U2Self(sdata->padata_value.data,
+ sdata->padata_value.length,
+ &self, NULL);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
+ goto out;
+ }
+
+ ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ krb5_data_free(&datack);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_TGS_IMPERSONATE,
+ datack.data,
+ datack.length,
+ &self.cksum);
+ krb5_data_free(&datack);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ kdc_log(context, config, 0,
+ "krb5_verify_checksum failed for S4U2Self: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ self.name,
+ self.realm);
+ free_PA_S4U2Self(&self);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &selfcpn);
+ if (ret)
+ goto out;
+
+ /*
+ * Check that service doing the impersonating is
+ * requesting a ticket to it-self.
+ */
+ if (krb5_principal_compare(context, cp, sp) != TRUE) {
+ kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
+ "to impersonate some other user "
+ "(tried for user %s to service %s)",
+ cpn, selfcpn, spn);
+ free(selfcpn);
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ goto out;
+ }
+
+ /*
+ * If the service isn't trusted for authentication to
+ * delegation, remove the forward flag.
+ */
+
+ if (client->entry.flags.trusted_for_delegation) {
+ str = "[forwardable]";
+ } else {
+ b->kdc_options.forwardable = 0;
+ str = "";
+ }
+ kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
+ "service %s %s", cpn, selfcpn, spn, str);
+ free(selfcpn);
+ }
+ }
+
+ /*
+ * Constrained delegation
+ */
+
+ if (client != NULL
+ && b->additional_tickets != NULL
+ && b->additional_tickets->len != 0
+ && b->kdc_options.enc_tkt_in_skey == 0)
+ {
+ Key *clientkey;
+ Ticket *t;
+ char *str;
+
+ t = &b->additional_tickets->val[0];
+
+ ret = hdb_enctype2key(context, &client->entry,
+ t->enc_part.etype, &clientkey);
+ if(ret){
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+
+ ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "failed to decrypt ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ goto out;
+ }
+
+ /* check that ticket is valid */
+
+ if (adtkt.flags.forwardable == 0) {
+ kdc_log(context, config, 0,
+ "Missing forwardable flag on ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+
+ ret = check_constrained_delegation(context, config, client, sp);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "constrained delegation from %s to %s not allowed",
+ spn, cpn);
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ adtkt.cname,
+ adtkt.crealm);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &str);
+ if (ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, str);
+ if (ret) {
+ free(str);
+ goto out;
+ }
+
+ /*
+ * Check KRB5SignedPath in authorization data and add new entry to
+ * make sure servers can't fake a ticket to us.
+ */
+
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ &adtkt,
+ &spp,
+ 1);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check from service %s failed "
+ "for delegation to %s for client %s "
+ "from %s failed with %s",
+ spn, str, cpn, from, krb5_get_err_text(context, ret));
+ free(str);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "constrained delegation for %s "
+ "from %s to %s", str, cpn, spn);
+ free(str);
+
+ /*
+ * Also require that the KDC have issue the service's krbtgt
+ * used to do the request.
+ */
+ require_signedpath = 1;
+ }
+
+ /*
+ * Check flags
+ */
+
+ ret = _kdc_check_flags(context, config,
+ client, cpn,
+ server, spn,
+ FALSE);
+ if(ret)
+ goto out;
+
+ if((b->kdc_options.validate || b->kdc_options.renew) &&
+ !krb5_principal_compare(context,
+ krbtgt->entry.principal,
+ server->entry.principal)){
+ kdc_log(context, config, 0, "Inconsistent request.");
+ ret = KRB5KDC_ERR_SERVER_NOMATCH;
+ goto out;
+ }
+
+ /* check for valid set of addresses */
+ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ kdc_log(context, config, 0, "Request from wrong address");
+ goto out;
+ }
+
+ /* also check the krbtgt for signature */
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ tgt,
+ &spp,
+ require_signedpath);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check failed for %s (%s) from %s with %s",
+ spn, cpn, from, krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ ret = tgs_make_reply(context,
+ config,
+ b,
+ client_principal,
+ tgt,
+ b->kdc_options.enc_tkt_in_skey ? &adtkt : NULL,
+ auth_data,
+ ticket,
+ server,
+ spn,
+ client,
+ cp,
+ krbtgt,
+ krbtgt_etype,
+ spp,
+ tgtkey,
+ e_text,
+ reply);
+
+out:
+ free(spn);
+ free(cpn);
+
+ if(server)
+ _kdc_free_ent(context, server);
+ if(client)
+ _kdc_free_ent(context, client);
+
+ if (client_principal && client_principal != cp)
+ krb5_free_principal(context, client_principal);
+ if (cp)
+ krb5_free_principal(context, cp);
+ if (sp)
+ krb5_free_principal(context, sp);
+
+ free_EncTicketPart(&adtkt);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_tgs_rep(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ krb5_data *data,
+ const char *from,
+ struct sockaddr *from_addr)
+{
+ AuthorizationData *auth_data = NULL;
+ krb5_error_code ret;
+ int i = 0;
+ PA_DATA *tgs_req = NULL;
+
+ hdb_entry_ex *krbtgt = NULL;
+ krb5_ticket *ticket = NULL;
+ const char *e_text = NULL;
+ krb5_enctype krbtgt_etype = ETYPE_NULL;
+ EncryptionKey *tgtkey = NULL;
+
+
+ time_t *csec = NULL;
+ int *cusec = NULL;
+
+ if(req->padata == NULL){
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-DATA", from);
+ goto out;
+ }
+
+ tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
+
+ if(tgs_req == NULL){
+ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-TGS-REQ", from);
+ goto out;
+ }
+ ret = tgs_parse_request(context, config,
+ &req->req_body, tgs_req,
+ &krbtgt,
+ &krbtgt_etype,
+ &ticket,
+ &e_text,
+ from, from_addr,
+ &csec, &cusec,
+ &auth_data,
+ &tgtkey);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed parsing TGS-REQ from %s", from);
+ goto out;
+ }
+
+ ret = tgs_build_reply(context,
+ config,
+ req,
+ &req->req_body,
+ krbtgt,
+ krbtgt_etype,
+ ticket,
+ data,
+ from,
+ &e_text,
+ auth_data,
+ tgtkey,
+ from_addr);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed building TGS-REP to %s", from);
+ goto out;
+ }
+
+out:
+ if(ret && data->data == NULL){
+ krb5_mk_error(context,
+ ret,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ csec,
+ cusec,
+ data);
+ }
+ free(csec);
+ free(cusec);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if(krbtgt)
+ _kdc_free_ent(context, krbtgt);
+
+ if (auth_data) {
+ free_AuthorizationData(auth_data);
+ free(auth_data);
+ }
+
+ return 0;
+}
diff --git a/source4/heimdal/kdc/misc.c b/source4/heimdal/kdc/misc.c
index a61c647f71..b511e1a7a8 100644
--- a/source4/heimdal/kdc/misc.c
+++ b/source4/heimdal/kdc/misc.c
@@ -33,7 +33,7 @@
#include "kdc_locl.h"
-RCSID("$Id: misc.c,v 1.29 2006/04/27 11:33:21 lha Exp $");
+RCSID("$Id: misc.c,v 1.32 2006/08/28 14:41:49 lha Exp $");
struct timeval _kdc_now;
@@ -42,6 +42,7 @@ _kdc_db_fetch(krb5_context context,
krb5_kdc_configuration *config,
krb5_const_principal principal,
unsigned flags,
+ HDB **db,
hdb_entry_ex **h)
{
hdb_entry_ex *ent;
@@ -66,6 +67,8 @@ _kdc_db_fetch(krb5_context context,
ent);
config->db[i]->hdb_close(context, config->db[i]);
if(ret == 0) {
+ if (db)
+ *db = config->db[i];
*h = ent;
return 0;
}
@@ -81,3 +84,36 @@ _kdc_free_ent(krb5_context context, hdb_entry_ex *ent)
free (ent);
}
+/*
+ * Use the order list of preferred encryption types and sort the
+ * available keys and return the most preferred key.
+ */
+
+krb5_error_code
+_kdc_get_preferred_key(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *h,
+ const char *name,
+ krb5_enctype *enctype,
+ Key **key)
+{
+ const krb5_enctype *p;
+ krb5_error_code ret;
+ int i;
+
+ p = krb5_kerberos_enctypes(context);
+
+ for (i = 0; p[i] != ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ ret = hdb_enctype2key(context, &h->entry, p[i], key);
+ if (ret == 0) {
+ *enctype = p[i];
+ return 0;
+ }
+ }
+
+ krb5_set_error_string(context, "No valid kerberos key found for %s", name);
+ return EINVAL;
+}
+
diff --git a/source4/heimdal/kdc/pkinit.c b/source4/heimdal/kdc/pkinit.c
index c220e70ddd..e3d77c0621 100755
--- a/source4/heimdal/kdc/pkinit.c
+++ b/source4/heimdal/kdc/pkinit.c
@@ -33,7 +33,7 @@
#include "kdc_locl.h"
-RCSID("$Id: pkinit.c,v 1.65 2006/05/06 13:22:33 lha Exp $");
+RCSID("$Id: pkinit.c,v 1.72 2006/10/24 17:51:33 lha Exp $");
#ifdef PKINIT
@@ -156,7 +156,7 @@ pk_check_pkauthenticator(krb5_context context,
goto out;
}
- if (heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
+ if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
krb5_clear_error_string(context);
ret = KRB5KRB_ERR_GENERIC;
}
@@ -269,7 +269,7 @@ get_dh_param(krb5_context context,
memset(&dhparam, 0, sizeof(dhparam));
- if (heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
+ if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
krb5_set_error_string(context,
"PKINIT invalid oid in clientPublicValue");
return KRB5_BADMSGTYPE;
@@ -338,7 +338,7 @@ get_dh_param(krb5_context context,
client_params->dh_public_key = integer_to_BN(context,
"subjectPublicKey",
&glue);
- free_heim_integer(&glue);
+ der_free_heim_integer(&glue);
if (client_params->dh_public_key == NULL)
goto out;
}
@@ -426,7 +426,7 @@ _kdc_pk_rd_padata(krb5_context context,
krb5_data signed_content = { 0, NULL };
const char *type = "unknown type";
const heim_oid *pa_contentType;
- int have_data;
+ int have_data = 0;
*ret_params = NULL;
@@ -444,7 +444,6 @@ _kdc_pk_rd_padata(krb5_context context,
if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
PA_PK_AS_REQ_Win2k r;
- int have_data;
type = "PK-INIT-Win2k";
pa_contentType = oid_id_pkcs7_data();
@@ -502,7 +501,7 @@ _kdc_pk_rd_padata(krb5_context context,
goto out;
}
- ret = heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
+ ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
if (ret != 0) {
krb5_set_error_string(context, "PK-AS-REQ-Win2k invalid content "
"type oid");
@@ -542,7 +541,7 @@ _kdc_pk_rd_padata(krb5_context context,
}
/* Signature is correct, now verify the signed message */
- if (heim_oid_cmp(&eContentType, pa_contentType)) {
+ if (der_heim_oid_cmp(&eContentType, pa_contentType)) {
krb5_set_error_string(context, "got wrong oid for pkauthdata");
ret = KRB5_BADMSGTYPE;
goto out;
@@ -621,8 +620,8 @@ out:
if (signed_content.data)
free(signed_content.data);
krb5_data_free(&eContent);
- free_oid(&eContentType);
- free_oid(&contentInfoOid);
+ der_free_oid(&eContentType);
+ der_free_oid(&contentInfoOid);
if (ret)
_kdc_pk_free_client_param(context, client_params);
else
@@ -657,10 +656,11 @@ pk_mk_pa_reply_enckey(krb5_context context,
ContentInfo *content_info)
{
krb5_error_code ret;
- krb5_data buf, o;
+ krb5_data buf, signed_data;
size_t size;
krb5_data_zero(&buf);
+ krb5_data_zero(&signed_data);
switch (client_params->type) {
case PKINIT_COMPAT_WIN2K: {
@@ -678,6 +678,7 @@ pk_mk_pa_reply_enckey(krb5_context context,
buf.data, buf.length,
&kp, &size,ret);
free_ReplyKeyPack_Win2k(&kp);
+ break;
}
case PKINIT_COMPAT_27: {
krb5_crypto ascrypto;
@@ -724,21 +725,55 @@ pk_mk_pa_reply_enckey(krb5_context context,
if (buf.length != size)
krb5_abortx(context, "Internal ASN.1 encoder error");
+ {
+ 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,
+ oid_id_pkrkeydata(),
+ buf.data,
+ buf.length,
+ NULL,
+ cert,
+ kdc_identity->anchors,
+ kdc_identity->certpool,
+ &signed_data);
+ hx509_cert_free(cert);
+ }
+
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
client_params->cert,
- buf.data, buf.length, NULL,
- oid_id_pkcs7_signedData(), &o);
+ signed_data.data, signed_data.length, NULL,
+ oid_id_pkcs7_signedData(), &buf);
if (ret)
goto out;
ret = _krb5_pk_mk_ContentInfo(context,
- &o,
+ &buf,
oid_id_pkcs7_envelopedData(),
content_info);
- free_octet_string(&o);
-
- out:
+out:
krb5_data_free(&buf);
+ krb5_data_free(&signed_data);
return ret;
}
@@ -1195,6 +1230,7 @@ _kdc_pk_check_client(krb5_context context,
pk_client_params *client_params,
char **subject_name)
{
+ const HDB_Ext_PKINIT_acl *acl;
krb5_error_code ret;
hx509_name name;
int i;
@@ -1210,8 +1246,8 @@ _kdc_pk_check_client(krb5_context context,
if (ret)
return ret;
- kdc_log(context, config, 5,
- "Trying to authorize subject DN %s",
+ kdc_log(context, config, 0,
+ "Trying to authorize PK-INIT subject DN %s",
*subject_name);
if (config->enable_pkinit_princ_in_cert) {
@@ -1225,6 +1261,28 @@ _kdc_pk_check_client(krb5_context context,
}
}
+ ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
+ if (ret == 0 && acl != NULL) {
+ /*
+ * Cheat here and compare the generated name with the string
+ * and not the reverse.
+ */
+ for (i = 0; i < acl->len; i++) {
+ if (strcmp(*subject_name, acl->val[0].subject) != 0)
+ continue;
+
+ /* Don't support isser and anchor checking right now */
+ if (acl->val[0].issuer)
+ continue;
+ if (acl->val[0].anchor)
+ continue;
+
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT database ACL");
+ return 0;
+ }
+ }
+
for (i = 0; i < principal_mappings.len; i++) {
krb5_boolean b;
@@ -1235,11 +1293,14 @@ _kdc_pk_check_client(krb5_context context,
continue;
if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
continue;
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT FILE ACL");
return 0;
}
- free(*subject_name);
+ free(*subject_name);
*subject_name = NULL;
+
krb5_set_error_string(context, "PKINIT no matching principals");
return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
}
@@ -1282,7 +1343,7 @@ _kdc_pk_initialize(krb5_context context,
const char *user_id,
const char *anchors,
char **pool,
- char **revoke)
+ char **revoke_list)
{
const char *file;
krb5_error_code ret;
@@ -1305,7 +1366,7 @@ _kdc_pk_initialize(krb5_context context,
user_id,
anchors,
pool,
- revoke,
+ revoke_list,
NULL,
NULL,
NULL);
diff --git a/source4/heimdal/kdc/process.c b/source4/heimdal/kdc/process.c
index d0f8245bf9..ed5cb3d651 100644
--- a/source4/heimdal/kdc/process.c
+++ b/source4/heimdal/kdc/process.c
@@ -34,7 +34,7 @@
#include "kdc_locl.h"
-RCSID("$Id: process.c,v 1.3 2005/08/12 08:25:48 lha Exp $");
+RCSID("$Id: process.c,v 1.5 2006/10/09 15:37:39 lha Exp $");
/*
* handle the request in `buf, len', from `addr' (or `from' as a string),
@@ -42,17 +42,19 @@ RCSID("$Id: process.c,v 1.3 2005/08/12 08:25:48 lha Exp $");
*/
int
-krb5_kdc_process_generic_request(krb5_context context,
- krb5_kdc_configuration *config,
- unsigned char *buf,
- size_t len,
- krb5_data *reply,
- krb5_boolean *prependlength,
- const char *from,
- struct sockaddr *addr)
+krb5_kdc_process_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ krb5_boolean *prependlength,
+ const char *from,
+ struct sockaddr *addr,
+ int datagram_reply)
{
KDC_REQ req;
Ticket ticket;
+ DigestREQ digestreq;
krb5_error_code ret;
size_t i;
@@ -64,7 +66,7 @@ krb5_kdc_process_generic_request(krb5_context context,
req_buffer.length = len;
ret = _kdc_as_rep(context, config, &req, &req_buffer,
- reply, from, addr);
+ reply, from, addr, datagram_reply);
free_AS_REQ(&req);
return ret;
}else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
@@ -75,6 +77,10 @@ krb5_kdc_process_generic_request(krb5_context context,
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_maybe_version4(buf, len)){
*prependlength = FALSE; /* elbitapmoc sdrawkcab XXX */
_kdc_do_version4(context, config, buf, len, reply, from,
@@ -103,7 +109,8 @@ krb5_kdc_process_krb5_request(krb5_context context,
size_t len,
krb5_data *reply,
const char *from,
- struct sockaddr *addr)
+ struct sockaddr *addr,
+ int datagram_reply)
{
KDC_REQ req;
krb5_error_code ret;
@@ -117,7 +124,7 @@ krb5_kdc_process_krb5_request(krb5_context context,
req_buffer.length = len;
ret = _kdc_as_rep(context, config, &req, &req_buffer,
- reply, from, addr);
+ reply, from, addr, datagram_reply);
free_AS_REQ(&req);
return ret;
}else if(decode_TGS_REQ(buf, len, &req, &i) == 0){