summaryrefslogtreecommitdiffstats
path: root/src/kdc
diff options
context:
space:
mode:
Diffstat (limited to 'src/kdc')
-rw-r--r--src/kdc/Makefile.in60
-rw-r--r--src/kdc/do_as_req.c254
-rw-r--r--src/kdc/do_tgs_req.c422
-rw-r--r--src/kdc/extern.c1
-rw-r--r--src/kdc/extern.h1
-rw-r--r--src/kdc/kdc_authdata.c539
-rw-r--r--src/kdc/kdc_preauth.c211
-rw-r--r--src/kdc/kdc_util.c732
-rw-r--r--src/kdc/kdc_util.h109
-rw-r--r--src/kdc/main.c34
-rw-r--r--src/kdc/network.c72
-rw-r--r--src/kdc/policy.c101
12 files changed, 2134 insertions, 402 deletions
diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in
index c7ecf4bfcf..b346c4627f 100644
--- a/src/kdc/Makefile.in
+++ b/src/kdc/Makefile.in
@@ -92,7 +92,8 @@ $(OUTPRE)dispatch.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
- $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_ext.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
$(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
$(SRCTOP)/include/socket-utils.h dispatch.c extern.h \
kdc_util.h
@@ -104,10 +105,10 @@ $(OUTPRE)do_as_req.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h do_as_req.c extern.h \
- kdc_util.h policy.h
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ do_as_req.c extern.h kdc_util.h policy.h
$(OUTPRE)do_tgs_req.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/adm_proto.h \
@@ -115,7 +116,8 @@ $(OUTPRE)do_tgs_req.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
- $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_ext.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
$(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
$(SRCTOP)/include/socket-utils.h do_tgs_req.c extern.h \
kdc_util.h policy.h
@@ -127,10 +129,10 @@ $(OUTPRE)kdc_util.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h extern.h kdc_util.c \
- kdc_util.h
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ extern.h kdc_util.c kdc_util.h
$(OUTPRE)kdc_preauth.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/adm_proto.h \
@@ -138,7 +140,8 @@ $(OUTPRE)kdc_preauth.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
- $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_ext.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
$(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
$(SRCTOP)/include/socket-utils.h extern.h kdc_preauth.c \
kdc_util.h
@@ -150,10 +153,11 @@ $(OUTPRE)main.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/kdb_kt.h $(SRCTOP)/include/krb5.h \
- $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
- $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
- extern.h kdc5_err.h kdc_util.h main.c
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/kdb_kt.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+ $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+ $(SRCTOP)/include/socket-utils.h extern.h kdc5_err.h \
+ kdc_util.h main.c
$(OUTPRE)network.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/adm_proto.h \
@@ -163,10 +167,10 @@ $(OUTPRE)network.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h extern.h kdc5_err.h \
- kdc_util.h network.c
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ extern.h kdc5_err.h kdc_util.h network.c
$(OUTPRE)policy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
@@ -174,9 +178,10 @@ $(OUTPRE)policy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h kdc_util.h policy.c
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ extern.h kdc_util.h policy.c
$(OUTPRE)extern.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-buf.h \
@@ -194,10 +199,10 @@ $(OUTPRE)replay.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
$(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
$(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
- $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
- $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
- $(SRCTOP)/include/socket-utils.h extern.h kdc_util.h \
- replay.c
+ $(SRCTOP)/include/kdb_ext.h $(SRCTOP)/include/krb5.h \
+ $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+ $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+ extern.h kdc_util.h replay.c
$(OUTPRE)kdc_authdata.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
$(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/adm_proto.h \
@@ -205,7 +210,8 @@ $(OUTPRE)kdc_authdata.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
$(SRCTOP)/include/k5-gmt_mktime.h $(SRCTOP)/include/k5-int-pkinit.h \
$(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
$(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
- $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/authdata_plugin.h \
+ $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_ext.h \
+ $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/authdata_plugin.h \
$(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
$(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
extern.h kdc_authdata.c kdc_util.h
diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c
index 8c65a637bd..36b550250c 100644
--- a/src/kdc/do_as_req.c
+++ b/src/kdc/do_as_req.c
@@ -27,6 +27,33 @@
*
* KDC Routines to deal with AS_REQ's
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "k5-int.h"
#include "com_err.h"
@@ -56,7 +83,8 @@
#endif /* APPLE_PKINIT */
static krb5_error_code prepare_error_as (krb5_kdc_req *, int, krb5_data *,
- krb5_data **, const char *);
+ krb5_principal, krb5_data **,
+ const char *);
/*ARGSUSED*/
krb5_error_code
@@ -73,17 +101,17 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
krb5_boolean more;
krb5_timestamp kdc_time, authtime;
krb5_keyblock session_key;
- krb5_keyblock encrypting_key;
const char *status;
- krb5_key_data *server_key, *client_key;
+ krb5_key_data *server_key, *client_key;
+ krb5_keyblock server_keyblock, client_keyblock;
krb5_enctype useenctype;
-#ifdef KRBCONF_KDC_MODIFIES_KDB
krb5_boolean update_client = 0;
-#endif /* KRBCONF_KDC_MODIFIES_KDB */
krb5_data e_data;
register int i;
krb5_timestamp until, rtime;
char *cname = 0, *sname = 0;
+ unsigned int c_flags = 0, s_flags = 0;
+ krb5_principal_data client_princ;
void *pa_context = NULL;
int did_log = 0;
const char *emsg = 0;
@@ -95,8 +123,11 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
ticket_reply.enc_part.ciphertext.data = 0;
e_data.data = 0;
- encrypting_key.contents = 0;
+ server_keyblock.contents = NULL;
+ client_keyblock.contents = NULL;
+ reply.padata = 0;
memset(&reply, 0, sizeof(reply));
+
session_key.contents = 0;
enc_tkt_reply.authorization_data = NULL;
@@ -121,13 +152,33 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
limit_string(sname);
+ /*
+ * We set KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY as a hint
+ * to the backend to return naming information in lieu
+ * of cross realm TGS entries.
+ */
+ setflag(c_flags, KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY);
+ /*
+ * Note that according to the referrals draft we should
+ * always canonicalize enterprise principal names.
+ */
+ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE) ||
+ krb5_princ_type(kdc_context,
+ request->client) == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
+ }
+ if (include_pac_p(kdc_context, request)) {
+ setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
+ }
c_nprincs = 1;
- if ((errcode = get_principal(kdc_context, request->client,
- &client, &c_nprincs, &more))) {
+ if ((errcode = krb5_db_get_principal_ext(kdc_context, request->client,
+ c_flags, &client, &c_nprincs,
+ &more))) {
status = "LOOKING_UP_CLIENT";
c_nprincs = 0;
goto errout;
}
+
if (more) {
status = "NON-UNIQUE_CLIENT";
errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
@@ -141,12 +192,40 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
#endif
goto errout;
}
-
+
+ /*
+ * If the backend returned a principal that is not in the local
+ * realm, then we need to refer the client to that realm.
+ */
+ if (!is_local_principal(client.princ)) {
+ /* Entry is a referral to another realm */
+ status = "REFERRAL";
+ errcode = KRB5KDC_ERR_WRONG_REALM;
+ goto errout;
+ }
+
+#if 0
+ /*
+ * Turn off canonicalization if client is marked DES only
+ * (unless enterprise principal name was requested)
+ */
+ if (isflagset(client.attributes, KRB5_KDB_NON_MS_PRINCIPAL) &&
+ krb5_princ_type(kdc_context,
+ request->client) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ clear(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
+ }
+#endif
+
+ s_flags = 0;
+ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
+ setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
+ }
s_nprincs = 1;
- if ((errcode = get_principal(kdc_context, request->server, &server,
- &s_nprincs, &more))) {
- status = "LOOKING_UP_SERVER";
- goto errout;
+ if ((errcode = krb5_db_get_principal_ext(kdc_context, request->server,
+ s_flags, &server,
+ &s_nprincs, &more))) {
+ status = "LOOKING_UP_SERVER";
+ goto errout;
}
if (more) {
status = "NON-UNIQUE_SERVER";
@@ -162,9 +241,10 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
status = "TIMEOFDAY";
goto errout;
}
+ authtime = kdc_time; /* for audit_as_request() */
if ((errcode = validate_as_request(request, client, server,
- kdc_time, &status))) {
+ kdc_time, &status))) {
if (!status)
status = "UNKNOWN_REASON";
errcode += ERROR_TABLE_BASE_krb5;
@@ -189,9 +269,22 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
goto errout;
}
- ticket_reply.server = request->server;
+ /*
+ * Canonicalization is only effective if we are issuing a TGT
+ * (the intention is to allow support for Windows "short" realm
+ * aliases, nothing more).
+ */
+ if (isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE) &&
+ krb5_is_tgs_principal(request->server) &&
+ krb5_is_tgs_principal(server.princ)) {
+ ticket_reply.server = server.princ;
+ } else {
+ ticket_reply.server = request->server;
+ }
enc_tkt_reply.flags = 0;
+ enc_tkt_reply.times.authtime = authtime;
+
setflag(enc_tkt_reply.flags, TKT_FLG_INITIAL);
/* It should be noted that local policy may affect the */
@@ -208,12 +301,17 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
setflag(enc_tkt_reply.flags, TKT_FLG_MAY_POSTDATE);
enc_tkt_reply.session = &session_key;
- enc_tkt_reply.client = request->client;
+ if (isflagset(c_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
+ client_princ = *(client.princ);
+ } else {
+ client_princ = *(request->client);
+ /* The realm is always canonicalized */
+ client_princ.realm = *(krb5_princ_realm(context, client.princ));
+ }
+ enc_tkt_reply.client = &client_princ;
enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
- enc_tkt_reply.times.authtime = kdc_time;
-
if (isflagset(request->kdc_options, KDC_OPT_POSTDATED)) {
setflag(enc_tkt_reply.flags, TKT_FLG_POSTDATED);
setflag(enc_tkt_reply.flags, TKT_FLG_INVALID);
@@ -269,6 +367,9 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
errcode = check_padata(kdc_context, &client, req_pkt, request,
&enc_tkt_reply, &pa_context, &e_data);
if (errcode) {
+ if (errcode == KRB5KDC_ERR_PREAUTH_FAILED)
+ get_preauth_hint_list(request, &client, &server, &e_data);
+
#ifdef KRBCONF_KDC_MODIFIES_KDB
/*
* Note: this doesn't work if you're using slave servers!!!
@@ -282,8 +383,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
}
}
client.last_failed = kdc_time;
- update_client = 1;
#endif
+ update_client = 1;
status = "PREAUTH_FAILED";
#ifdef KRBCONF_VAGUE_ERRORS
errcode = KRB5KRB_ERR_GENERIC;
@@ -304,9 +405,10 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
goto errout;
}
- errcode = handle_authdata(kdc_context, &client, req_pkt, request, &enc_tkt_reply);
- if (errcode) {
- krb5_klog_syslog(LOG_INFO, "AS_REQ : handle_authdata (%d)", errcode);
+ if ((errcode = validate_forwardable(request, client, server,
+ kdc_time, &status))) {
+ errcode += ERROR_TABLE_BASE_krb5;
+ goto errout;
}
ticket_reply.enc_part2 = &enc_tkt_reply;
@@ -326,21 +428,13 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
/* convert server.key into a real key (it may be encrypted
in the database) */
if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
- server_key, &encrypting_key,
+ /* server_keyblock is later used to generate auth data signatures */
+ server_key, &server_keyblock,
NULL))) {
status = "DECRYPT_SERVER_KEY";
goto errout;
}
- errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key, &ticket_reply);
- krb5_free_keyblock_contents(kdc_context, &encrypting_key);
- encrypting_key.contents = 0;
- if (errcode) {
- status = "ENCRYPTING_TICKET";
- goto errout;
- }
- ticket_reply.enc_part.kvno = server_key->key_data_kvno;
-
/*
* Find the appropriate client key. We search in the order specified
* by request keytype list.
@@ -364,16 +458,16 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
/* convert client.key_data into a real key */
if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
- client_key, &encrypting_key,
+ client_key, &client_keyblock,
NULL))) {
status = "DECRYPT_CLIENT_KEY";
goto errout;
}
- encrypting_key.enctype = useenctype;
+ client_keyblock.enctype = useenctype;
/* Start assembling the response */
reply.msg_type = KRB5_AS_REP;
- reply.client = request->client;
+ reply.client = enc_tkt_reply.client; /* post canonicalization */
reply.ticket = &ticket_reply;
reply_encpart.session = &session_key;
if ((errcode = fetch_last_req_info(&client, &reply_encpart.last_req))) {
@@ -391,10 +485,12 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
reply_encpart.times.authtime = authtime = kdc_time;
reply_encpart.caddrs = enc_tkt_reply.caddrs;
+ reply_encpart.enc_padata = NULL;
- /* Fetch the padata info to be returned */
+ /* Fetch the padata info to be returned (do this before
+ authdata to handle possible replacement of reply key */
errcode = return_padata(kdc_context, &client, req_pkt, request,
- &reply, client_key, &encrypting_key, &pa_context);
+ &reply, client_key, &client_keyblock, &pa_context);
if (errcode) {
status = "KDC_RETURN_PADATA";
goto errout;
@@ -405,16 +501,48 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
reply.client->realm.data, reply.client->data->data);
#endif /* APPLE_PKINIT */
+ errcode = return_svr_referral_data(kdc_context,
+ &server, &reply_encpart);
+ if (errcode) {
+ status = "KDC_RETURN_ENC_PADATA";
+ goto errout;
+ }
+
+ if (find_pa_data(reply.padata, KRB5_PADATA_PK_AS_REP))
+ c_flags |= KRB5_KDB_FLAG_PKINIT;
+
+ errcode = handle_authdata(kdc_context,
+ c_flags,
+ &client,
+ &server,
+ &server,
+ &client_keyblock,
+ &server_keyblock,
+ req_pkt,
+ request,
+ NULL, /* for_user_princ */
+ NULL, /* enc_tkt_request */
+ &enc_tkt_reply);
+ if (errcode) {
+ krb5_klog_syslog(LOG_INFO, "AS_REQ : handle_authdata (%d)", errcode);
+ status = "HANDLE_AUTHDATA";
+ goto errout;
+ }
+
+ errcode = krb5_encrypt_tkt_part(kdc_context, &server_keyblock, &ticket_reply);
+ if (errcode) {
+ status = "ENCRYPTING_TICKET";
+ goto errout;
+ }
+ ticket_reply.enc_part.kvno = server_key->key_data_kvno;
+
/* now encode/encrypt the response */
- reply.enc_part.enctype = encrypting_key.enctype;
+ reply.enc_part.enctype = client_keyblock.enctype;
errcode = krb5_encode_kdc_rep(kdc_context, KRB5_AS_REP, &reply_encpart,
- 0, &encrypting_key, &reply, response);
- krb5_free_keyblock_contents(kdc_context, &encrypting_key);
- encrypting_key.contents = 0;
+ 0, &client_keyblock, &reply, response);
reply.enc_part.kvno = client_key->key_data_kvno;
-
if (errcode) {
status = "ENCODE_KDC_REP";
goto errout;
@@ -434,8 +562,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt,
*/
client.last_success = kdc_time;
client.fail_auth_count = 0;
- update_client = 1;
#endif /* KRBCONF_KDC_MODIFIES_KDB */
+ update_client = 1;
goto egress;
@@ -444,6 +572,10 @@ errout:
/* fall through */
egress:
+ if (update_client) {
+ audit_as_request(request, &client, &server, authtime, errcode);
+ }
+
if (pa_context)
free_padata_context(kdc_context, &pa_context);
@@ -463,8 +595,9 @@ egress:
if (errcode < 0 || errcode > 128)
errcode = KRB_ERR_GENERIC;
- errcode = prepare_error_as(request, errcode, &e_data, response,
- status);
+ errcode = prepare_error_as(request, errcode, &e_data,
+ c_nprincs ? client.princ : NULL,
+ response, status);
status = 0;
}
if (emsg)
@@ -472,14 +605,16 @@ egress:
if (enc_tkt_reply.authorization_data != NULL)
krb5_free_authdata(kdc_context, enc_tkt_reply.authorization_data);
- if (encrypting_key.contents)
- krb5_free_keyblock_contents(kdc_context, &encrypting_key);
- if (reply.padata)
+ if (server_keyblock.contents != NULL)
+ krb5_free_keyblock_contents(kdc_context, &server_keyblock);
+ if (client_keyblock.contents != NULL)
+ krb5_free_keyblock_contents(kdc_context, &client_keyblock);
+ if (reply.padata != NULL)
krb5_free_pa_data(kdc_context, reply.padata);
- if (cname)
+ if (cname != NULL)
free(cname);
- if (sname)
+ if (sname != NULL)
free(sname);
if (c_nprincs) {
#ifdef KRBCONF_KDC_MODIFIES_KDB
@@ -501,9 +636,9 @@ egress:
}
if (s_nprincs)
krb5_db_free_principal(kdc_context, &server, s_nprincs);
- if (session_key.contents)
+ if (session_key.contents != NULL)
krb5_free_keyblock_contents(kdc_context, &session_key);
- if (ticket_reply.enc_part.ciphertext.data) {
+ if (ticket_reply.enc_part.ciphertext.data != NULL) {
memset(ticket_reply.enc_part.ciphertext.data , 0,
ticket_reply.enc_part.ciphertext.length);
free(ticket_reply.enc_part.ciphertext.data);
@@ -516,7 +651,8 @@ egress:
static krb5_error_code
prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data,
- krb5_data **response, const char *status)
+ krb5_principal canon_client, krb5_data **response,
+ const char *status)
{
krb5_error errpkt;
krb5_error_code retval;
@@ -530,8 +666,12 @@ prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data,
return(retval);
errpkt.error = error;
errpkt.server = request->server;
- errpkt.client = request->client;
- errpkt.text.length = strlen(status)+1;
+
+ if (error == KRB5KDC_ERR_WRONG_REALM)
+ errpkt.client = canon_client;
+ else
+ errpkt.client = request->client;
+ errpkt.text.length = strlen(status) + 1;
if (!(errpkt.text.data = strdup(status)))
return ENOMEM;
@@ -539,11 +679,11 @@ prepare_error_as (krb5_kdc_req *request, int error, krb5_data *e_data,
free(errpkt.text.data);
return ENOMEM;
}
- if (e_data && e_data->data) {
+ if (e_data != NULL&& e_data->data != NULL) {
errpkt.e_data = *e_data;
} else {
errpkt.e_data.length = 0;
- errpkt.e_data.data = 0;
+ errpkt.e_data.data = NULL;
}
retval = krb5_mk_error(kdc_context, &errpkt, scratch);
diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c
index 3af539feb6..3812698808 100644
--- a/src/kdc/do_tgs_req.c
+++ b/src/kdc/do_tgs_req.c
@@ -26,6 +26,33 @@
*
* KDC Routines to deal with TGS_REQ's
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "k5-int.h"
#include "com_err.h"
@@ -49,8 +76,8 @@ static void find_alternate_tgs (krb5_kdc_req *, krb5_db_entry *,
krb5_boolean *, int *);
static krb5_error_code prepare_error_tgs (krb5_kdc_req *, krb5_ticket *,
- int, krb5_data **,
- const char *);
+ int, krb5_principal,
+ krb5_data **, const char *);
/*ARGSUSED*/
krb5_error_code
@@ -83,9 +110,17 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
register int i;
int firstpass = 1;
const char *status = 0;
+ krb5_enc_tkt_part *header_enc_tkt = NULL; /* ticket granting or evidence ticket */
+ krb5_db_entry client, krbtgt;
+ int c_nprincs = 0, k_nprincs = 0;
+ krb5_pa_for_user *for_user = NULL; /* protocol transition request */
+ krb5_authdata **kdc_issued_auth_data = NULL; /* auth data issued by KDC */
+ unsigned int c_flags = 0, s_flags = 0; /* client/server KDB flags */
+ char *s4u_name = NULL;
+ krb5_boolean is_referral;
const char *emsg = NULL;
- session_key.contents = 0;
+ session_key.contents = NULL;
retval = decode_krb5_tgs_req(pkt, &request);
if (retval)
@@ -106,8 +141,8 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
limit_string(sname);
/* errcode = kdc_process_tgs_req(request, from, pkt, &req_authdat); */
- errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket, &subkey);
-
+ errcode = kdc_process_tgs_req(request, from, pkt, &header_ticket,
+ &krbtgt, &k_nprincs, &subkey);
if (header_ticket && header_ticket->enc_part2 &&
(errcode2 = krb5_unparse_name(kdc_context,
header_ticket->enc_part2->client,
@@ -128,6 +163,14 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
status="UNEXPECTED NULL in header_ticket";
goto cleanup;
}
+
+ /*
+ * Pointer to the encrypted part of the header ticket, which may be
+ * replaced to point to the encrypted part of the evidence ticket
+ * if constrained delegation is used. This simplifies the number of
+ * special cases for constrained delegation.
+ */
+ header_enc_tkt = header_ticket->enc_part2;
/*
* We've already dealt with the AP_REQ authentication, so we can
@@ -135,14 +178,22 @@ process_tgs_req(krb5_data *pkt, const krb5_fulladdr *from,
* decrypted with the session key.
*/
- authtime = header_ticket->enc_part2->times.authtime;
-
/* XXX make sure server here has the proper realm...taken from AP_REQ
header? */
nprincs = 1;
- if ((errcode = get_principal(kdc_context, request->server, &server,
- &nprincs, &more))) {
+ if (isflagset(request->kdc_options, KDC_OPT_CANONICALIZE)) {
+ setflag(c_flags, KRB5_KDB_FLAG_CANONICALIZE);
+ setflag(s_flags, KRB5_KDB_FLAG_CANONICALIZE);
+ }
+
+ errcode = krb5_db_get_principal_ext(kdc_context,
+ request->server,
+ s_flags,
+ &server,
+ &nprincs,
+ &more);
+ if (errcode) {
status = "LOOKING_UP_SERVER";
nprincs = 0;
goto cleanup;
@@ -184,13 +235,28 @@ tgt_again:
}
if ((retval = validate_tgs_request(request, server, header_ticket,
- kdc_time, &status))) {
+ kdc_time, &status))) {
if (!status)
status = "UNKNOWN_REASON";
errcode = retval + ERROR_TABLE_BASE_krb5;
goto cleanup;
}
+ if (!is_local_principal(header_enc_tkt->client))
+ setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM);
+
+ is_referral = krb5_is_tgs_principal(server.princ) &&
+ !krb5_principal_compare(kdc_context, tgs_server, server.princ);
+
+ /* Check for protocol transition */
+ errcode = kdc_process_s4u2self_req(kdc_context, request, header_enc_tkt->client,
+ &server, header_enc_tkt->session, kdc_time,
+ &for_user, &client, &c_nprincs, &status);
+ if (errcode)
+ goto cleanup;
+ if (for_user != NULL)
+ setflag(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION);
+
/*
* We pick the session keytype here....
*
@@ -203,17 +269,23 @@ tgt_again:
* to anything else.
*/
useenctype = 0;
- if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
+ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY |
+ KDC_OPT_CNAME_IN_ADDL_TKT)) {
krb5_keyblock * st_sealing_key;
krb5_kvno st_srv_kvno;
krb5_enctype etype;
+ krb5_db_entry st_client;
+ int st_nprincs = 0;
/*
* Get the key for the second ticket, and decrypt it.
*/
if ((errcode = kdc_get_server_key(request->second_ticket[st_idx],
- &st_sealing_key,
- &st_srv_kvno))) {
+ c_flags,
+ &st_client,
+ &st_nprincs,
+ &st_sealing_key,
+ &st_srv_kvno))) {
status = "2ND_TKT_SERVER";
goto cleanup;
}
@@ -222,6 +294,7 @@ tgt_again:
krb5_free_keyblock(kdc_context, st_sealing_key);
if (errcode) {
status = "2ND_TKT_DECRYPT";
+ krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
goto cleanup;
}
@@ -229,6 +302,7 @@ tgt_again:
if (!krb5_c_valid_enctype(etype)) {
status = "BAD_ETYPE_IN_2ND_TKT";
errcode = KRB5KDC_ERR_ETYPE_NOSUPP;
+ krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
goto cleanup;
}
@@ -238,6 +312,34 @@ tgt_again:
break;
}
}
+
+ if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
+ /* Do constrained delegation protocol and authorization checks */
+ errcode = kdc_process_s4u2proxy_req(kdc_context,
+ request,
+ request->second_ticket[st_idx]->enc_part2,
+ &st_client,
+ header_ticket->enc_part2->client,
+ request->server,
+ &status);
+ if (errcode)
+ goto cleanup;
+
+ setflag(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
+
+ assert(krb5_is_tgs_principal(header_ticket->server));
+
+ /* From now on, use evidence ticket as header ticket */
+ header_enc_tkt = request->second_ticket[st_idx]->enc_part2;
+
+ assert(c_nprincs == 0); /* assured by kdc_process_s4u2self_req() */
+
+ client = st_client;
+ c_nprincs = st_nprincs;
+ } else {
+ /* "client" is not used for user2user */
+ krb5_db_free_principal(kdc_context, &st_client, st_nprincs);
+ }
}
/*
@@ -261,24 +363,35 @@ tgt_again:
goto cleanup;
}
- ticket_reply.server = request->server; /* XXX careful for realm... */
+ authtime = header_enc_tkt->times.authtime;
+
+ if (is_referral)
+ ticket_reply.server = server.princ;
+ else
+ ticket_reply.server = request->server; /* XXX careful for realm... */
enc_tkt_reply.flags = 0;
enc_tkt_reply.times.starttime = 0;
+ if (isflagset(server.attributes, KRB5_KDB_OK_AS_DELEGATE) &&
+ !is_referral) {
+ /* Ensure that we are not returning a referral */
+ setflag(enc_tkt_reply.flags, TKT_FLG_OK_AS_DELEGATE);
+ }
+
/*
* Fix header_ticket's starttime; if it's zero, fill in the
* authtime's value.
*/
- if (!(header_ticket->enc_part2->times.starttime))
- header_ticket->enc_part2->times.starttime =
- header_ticket->enc_part2->times.authtime;
+ if (!(header_enc_tkt->times.starttime))
+ header_enc_tkt->times.starttime = header_enc_tkt->times.authtime;
/* don't use new addresses unless forwarded, see below */
- enc_tkt_reply.caddrs = header_ticket->enc_part2->caddrs;
+ enc_tkt_reply.caddrs = header_enc_tkt->caddrs;
/* noaddrarray[0] = 0; */
reply_encpart.caddrs = 0; /* optional...don't put it in */
+ reply_encpart.enc_padata = NULL;
/* It should be noted that local policy may affect the */
/* processing of any of these flags. For example, some */
@@ -286,7 +399,17 @@ tgt_again:
if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE))
setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
-
+ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
+ if (!krb5_is_tgs_principal(server.princ) &&
+ is_local_principal(server.princ)) {
+ if (isflagset(server.attributes, KRB5_KDB_OK_TO_AUTH_AS_DELEGATE))
+ setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+ else
+ clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+ }
+ if (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
+ clear(enc_tkt_reply.flags, TKT_FLG_FORWARDABLE);
+ }
if (isflagset(request->kdc_options, KDC_OPT_FORWARDED)) {
setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
@@ -295,7 +418,7 @@ tgt_again:
enc_tkt_reply.caddrs = request->addresses;
reply_encpart.caddrs = request->addresses;
}
- if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_FORWARDED))
+ if (isflagset(header_enc_tkt->flags, TKT_FLG_FORWARDED))
setflag(enc_tkt_reply.flags, TKT_FLG_FORWARDED);
if (isflagset(request->kdc_options, KDC_OPT_PROXIABLE))
@@ -321,6 +444,7 @@ tgt_again:
enc_tkt_reply.times.starttime = kdc_time;
if (isflagset(request->kdc_options, KDC_OPT_VALIDATE)) {
+ assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
/* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
to the caller */
ticket_reply = *(header_ticket);
@@ -331,6 +455,7 @@ tgt_again:
if (isflagset(request->kdc_options, KDC_OPT_RENEW)) {
krb5_deltat old_life;
+ assert(isflagset(c_flags, KRB5_KDB_FLAGS_S4U) == 0);
/* BEWARE of allocation hanging off of ticket & enc_part2, it belongs
to the caller */
ticket_reply = *(header_ticket);
@@ -349,15 +474,13 @@ tgt_again:
enc_tkt_reply.times.endtime =
min(until, min(enc_tkt_reply.times.starttime + server.max_life,
min(enc_tkt_reply.times.starttime + max_life_for_realm,
- header_ticket->enc_part2->times.endtime)));
+ header_enc_tkt->times.endtime)));
if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE_OK) &&
(enc_tkt_reply.times.endtime < request->till) &&
- isflagset(header_ticket->enc_part2->flags,
- TKT_FLG_RENEWABLE)) {
+ isflagset(header_enc_tkt->flags, TKT_FLG_RENEWABLE)) {
setflag(request->kdc_options, KDC_OPT_RENEWABLE);
request->rtime =
- min(request->till,
- header_ticket->enc_part2->times.renew_till);
+ min(request->till, header_enc_tkt->times.renew_till);
}
}
rtime = (request->rtime == 0) ? kdc_infinity : request->rtime;
@@ -368,7 +491,7 @@ tgt_again:
setflag(enc_tkt_reply.flags, TKT_FLG_RENEWABLE);
enc_tkt_reply.times.renew_till =
min(rtime,
- min(header_ticket->enc_part2->times.renew_till,
+ min(header_enc_tkt->times.renew_till,
enc_tkt_reply.times.starttime +
min(server.max_renewable_life,
max_renewable_life_for_realm)));
@@ -379,15 +502,15 @@ tgt_again:
/*
* Set authtime to be the same as header_ticket's
*/
- enc_tkt_reply.times.authtime = header_ticket->enc_part2->times.authtime;
+ enc_tkt_reply.times.authtime = header_enc_tkt->times.authtime;
/*
* Propagate the preauthentication flags through to the returned ticket.
*/
- if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_PRE_AUTH))
+ if (isflagset(header_enc_tkt->flags, TKT_FLG_PRE_AUTH))
setflag(enc_tkt_reply.flags, TKT_FLG_PRE_AUTH);
- if (isflagset(header_ticket->enc_part2->flags, TKT_FLG_HW_AUTH))
+ if (isflagset(header_enc_tkt->flags, TKT_FLG_HW_AUTH))
setflag(enc_tkt_reply.flags, TKT_FLG_HW_AUTH);
/* starttime is optional, and treated as authtime if not present.
@@ -395,49 +518,130 @@ tgt_again:
if (enc_tkt_reply.times.starttime == enc_tkt_reply.times.authtime)
enc_tkt_reply.times.starttime = 0;
- /* assemble any authorization data */
- if (request->authorization_data.ciphertext.data) {
- krb5_data scratch;
+ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION)) {
+ errcode = krb5_unparse_name(kdc_context, for_user->user, &s4u_name);
+ } else if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
+ errcode = krb5_unparse_name(kdc_context, header_enc_tkt->client, &s4u_name);
+ } else {
+ errcode = 0;
+ }
+ if (errcode) {
+ status = "UNPARSING S4U CLIENT";
+ goto cleanup;
+ }
- scratch.length = request->authorization_data.ciphertext.length;
- if (!(scratch.data =
- malloc(request->authorization_data.ciphertext.length))) {
- status = "AUTH_NOMEM";
- errcode = ENOMEM;
+ if (isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY)) {
+ krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2;
+ encrypting_key = *(t2enc->session);
+ } else {
+ /*
+ * Find the server key
+ */
+ if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
+ -1, /* ignore keytype */
+ -1, /* Ignore salttype */
+ 0, /* Get highest kvno */
+ &server_key))) {
+ status = "FINDING_SERVER_KEY";
goto cleanup;
}
-
- if ((errcode = krb5_c_decrypt(kdc_context,
- header_ticket->enc_part2->session,
- KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
- 0, &request->authorization_data,
- &scratch))) {
- free(scratch.data);
- status = "AUTH_ENCRYPT_FAIL";
+ /* convert server.key into a real key (it may be encrypted
+ * in the database) */
+ if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
+ &master_keyblock,
+ server_key, &encrypting_key,
+ NULL))) {
+ status = "DECRYPT_SERVER_KEY";
goto cleanup;
}
+ }
- /* scratch now has the authorization data, so we decode it */
- errcode = decode_krb5_authdata(&scratch, &(request->unenc_authdata));
- free(scratch.data);
- if (errcode) {
- status = "AUTH_DECODE";
- goto cleanup;
+ if (isflagset(c_flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
+ /*
+ * Don't allow authorization data to be disabled if constrained
+ * delegation is requested. We don't want to deny the server
+ * the ability to validate that delegation was used.
+ */
+ clear(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED);
+ }
+ if (isflagset(server.attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED) == 0) {
+ /*
+ * If we are not doing protocol transition/constrained delegation
+ * and there was no authorization data included, try to lookup
+ * the client principal as it may be mapped to a local account.
+ *
+ * Always validate authorization data for constrained delegation
+ * because we must validate the KDC signatures.
+ */
+ if (!isflagset(c_flags, KRB5_KDB_FLAGS_S4U) &&
+ header_enc_tkt->authorization_data == NULL) {
+
+ /* Generate authorization data so we can include it in ticket */
+ setflag(c_flags, KRB5_KDB_FLAG_INCLUDE_PAC);
+ /* Map principals from foreign (possibly non-AD) realms */
+ setflag(c_flags, KRB5_KDB_FLAG_MAP_PRINCIPALS);
+
+ assert(c_nprincs == 0); /* should not have been looked up already */
+
+ c_nprincs = 1;
+ errcode = krb5_db_get_principal_ext(kdc_context,
+ header_enc_tkt->client,
+ c_flags,
+ &client,
+ &c_nprincs,
+ &more);
+ /*
+ * We can ignore errors because the principal may be a
+ * valid cross-realm principal for which we have no local
+ * mapping. But we do want to check that at most one entry
+ * was returned.
+ */
+ if (errcode == 0 && (more || c_nprincs > 1)) {
+ errcode = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
+ goto cleanup;
+ } else if (errcode) {
+ c_nprincs = 0;
+ }
}
+ }
- if ((errcode =
- concat_authorization_data(request->unenc_authdata,
- header_ticket->enc_part2->authorization_data,
- &enc_tkt_reply.authorization_data))) {
- status = "CONCAT_AUTH";
+ enc_tkt_reply.authorization_data = NULL;
+
+ if (isflagset(c_flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
+ is_local_principal(header_enc_tkt->client))
+ enc_tkt_reply.client = for_user->user;
+ else
+ enc_tkt_reply.client = header_enc_tkt->client;
+
+ errcode = handle_authdata(kdc_context,
+ c_flags,
+ (c_nprincs != 0) ? &client : NULL,
+ &server,
+ (k_nprincs != 0) ? &krbtgt : NULL,
+ subkey != NULL ? subkey :
+ header_ticket->enc_part2->session,
+ &encrypting_key, /* U2U or server key */
+ pkt,
+ request,
+ for_user ? for_user->user : NULL,
+ header_enc_tkt,
+ &enc_tkt_reply);
+ if (errcode) {
+ krb5_klog_syslog(LOG_INFO, "TGS_REQ : handle_authdata (%d)", errcode);
+ status = "HANDLE_AUTHDATA";
+ goto cleanup;
+ }
+
+ if (is_referral && isflagset(s_flags, KRB5_KDB_FLAG_CANONICALIZE)) {
+ errcode = return_svr_referral_data(kdc_context,
+ &server, &reply_encpart);
+ if (errcode) {
+ status = "KDC_RETURN_ENC_PADATA";
goto cleanup;
}
- } else
- enc_tkt_reply.authorization_data =
- header_ticket->enc_part2->authorization_data;
+ }
enc_tkt_reply.session = &session_key;
- enc_tkt_reply.client = header_ticket->enc_part2->client;
enc_tkt_reply.transited.tr_type = KRB5_DOMAIN_X500_COMPRESS;
enc_tkt_reply.transited.tr_contents = empty_string; /* equivalent of "" */
@@ -453,11 +657,11 @@ tgt_again:
if (realm_compare(header_ticket->server, tgs_server) ||
realm_compare(header_ticket->server, enc_tkt_reply.client)) {
/* tgt issued by local realm or issued by realm of client */
- enc_tkt_reply.transited = header_ticket->enc_part2->transited;
+ enc_tkt_reply.transited = header_enc_tkt->transited;
} else {
/* tgt issued by some other realm and not the realm of the client */
/* assemble new transited field into allocated storage */
- if (header_ticket->enc_part2->transited.tr_type !=
+ if (header_enc_tkt->transited.tr_type !=
KRB5_DOMAIN_X500_COMPRESS) {
status = "BAD_TRTYPE";
errcode = KRB5KDC_ERR_TRTYPE_NOSUPP;
@@ -470,7 +674,7 @@ tgt_again:
enc_tkt_transited.tr_contents.length = 0;
enc_tkt_reply.transited = enc_tkt_transited;
if ((errcode =
- add_to_transited(&header_ticket->enc_part2->transited.tr_contents,
+ add_to_transited(&header_enc_tkt->transited.tr_contents,
&enc_tkt_reply.transited.tr_contents,
header_ticket->server,
enc_tkt_reply.client,
@@ -480,14 +684,23 @@ tgt_again:
}
newtransited = 1;
}
+ if (isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) {
+ errcode = validate_transit_path(kdc_context, header_enc_tkt->client,
+ &server,
+ (k_nprincs != 0) ? &krbtgt : NULL);
+ if (errcode) {
+ status = "NON_TRANSITIVE";
+ goto cleanup;
+ }
+ }
if (!isflagset (request->kdc_options, KDC_OPT_DISABLE_TRANSITED_CHECK)) {
unsigned int tlen;
char *tdots;
- errcode = krb5_check_transited_list (kdc_context,
- &enc_tkt_reply.transited.tr_contents,
- krb5_princ_realm (kdc_context, header_ticket->enc_part2->client),
- krb5_princ_realm (kdc_context, request->server));
+ errcode = kdc_check_transited_list (kdc_context,
+ &enc_tkt_reply.transited.tr_contents,
+ krb5_princ_realm (kdc_context, header_enc_tkt->client),
+ krb5_princ_realm (kdc_context, request->server));
tlen = enc_tkt_reply.transited.tr_contents.length;
tdots = tlen > 125 ? "..." : "";
tlen = tlen > 125 ? 125 : tlen;
@@ -553,47 +766,24 @@ tgt_again:
ticket_reply.enc_part.kvno = 0;
ticket_reply.enc_part.enctype = t2enc->session->enctype;
- if ((errcode = krb5_encrypt_tkt_part(kdc_context, t2enc->session,
- &ticket_reply))) {
- status = "2ND_TKT_ENCRYPT";
- goto cleanup;
- }
st_idx++;
} else {
- /*
- * Find the server key
- */
- if ((errcode = krb5_dbe_find_enctype(kdc_context, &server,
- -1, /* ignore keytype */
- -1, /* Ignore salttype */
- 0, /* Get highest kvno */
- &server_key))) {
- status = "FINDING_SERVER_KEY";
- goto cleanup;
- }
- /* convert server.key into a real key (it may be encrypted
- * in the database) */
- if ((errcode = krb5_dbekd_decrypt_key_data(kdc_context,
- &master_keyblock,
- server_key, &encrypting_key,
- NULL))) {
- status = "DECRYPT_SERVER_KEY";
- goto cleanup;
- }
- errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
- &ticket_reply);
- krb5_free_keyblock_contents(kdc_context, &encrypting_key);
- if (errcode) {
- status = "TKT_ENCRYPT";
- goto cleanup;
- }
ticket_reply.enc_part.kvno = server_key->key_data_kvno;
}
+ errcode = krb5_encrypt_tkt_part(kdc_context, &encrypting_key,
+ &ticket_reply);
+ if (!isflagset(request->kdc_options, KDC_OPT_ENC_TKT_IN_SKEY))
+ krb5_free_keyblock_contents(kdc_context, &encrypting_key);
+ if (errcode) {
+ status = "TKT_ENCRYPT";
+ goto cleanup;
+ }
+
/* Start assembling the response */
reply.msg_type = KRB5_TGS_REP;
reply.padata = 0; /* always */
- reply.client = header_ticket->enc_part2->client;
+ reply.client = enc_tkt_reply.client;
reply.enc_part.kvno = 0; /* We are using the session key */
reply.ticket = &ticket_reply;
@@ -603,7 +793,7 @@ tgt_again:
/* copy the time fields EXCEPT for authtime; its location
is used for ktime */
reply_encpart.times = enc_tkt_reply.times;
- reply_encpart.times.authtime = header_ticket->enc_part2->times.authtime;
+ reply_encpart.times.authtime = header_enc_tkt->times.authtime;
/* starttime is optional, and treated as authtime if not present.
so we can nuke it if it matches */
@@ -666,6 +856,7 @@ cleanup:
errcode = KRB_ERR_GENERIC;
retval = prepare_error_tgs(request, header_ticket, errcode,
+ nprincs ? server.princ : NULL,
response, status);
if (got_err) {
krb5_free_error_message (kdc_context, status);
@@ -673,21 +864,31 @@ cleanup:
}
}
- if (header_ticket)
+ if (header_ticket != NULL)
krb5_free_ticket(kdc_context, header_ticket);
- if (request)
+ if (request != NULL)
krb5_free_kdc_req(kdc_context, request);
- if (cname)
+ if (cname != NULL)
free(cname);
- if (sname)
+ if (sname != NULL)
free(sname);
- if (nprincs)
+ if (nprincs != 0)
krb5_db_free_principal(kdc_context, &server, 1);
- if (session_key.contents)
+ if (session_key.contents != NULL)
krb5_free_keyblock_contents(kdc_context, &session_key);
if (newtransited)
free(enc_tkt_reply.transited.tr_contents.data);
- if (subkey)
+ if (k_nprincs)
+ krb5_db_free_principal(kdc_context, &krbtgt, k_nprincs);
+ if (c_nprincs)
+ krb5_db_free_principal(kdc_context, &client, c_nprincs);
+ if (for_user != NULL)
+ krb5_free_pa_for_user(kdc_context, for_user);
+ if (kdc_issued_auth_data != NULL)
+ krb5_free_authdata(kdc_context, kdc_issued_auth_data);
+ if (s4u_name != NULL)
+ free(s4u_name);
+ if (subkey != NULL)
krb5_free_keyblock(kdc_context, subkey);
return retval;
@@ -695,6 +896,7 @@ cleanup:
static krb5_error_code
prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error,
+ krb5_principal canon_server,
krb5_data **response, const char *status)
{
krb5_error errpkt;
@@ -712,7 +914,7 @@ prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error,
if (ticket && ticket->enc_part2)
errpkt.client = ticket->enc_part2->client;
else
- errpkt.client = 0;
+ errpkt.client = NULL;
errpkt.text.length = strlen(status) + 1;
if (!(errpkt.text.data = strdup(status)))
return ENOMEM;
@@ -722,7 +924,7 @@ prepare_error_tgs (krb5_kdc_req *request, krb5_ticket *ticket, int error,
return ENOMEM;
}
errpkt.e_data.length = 0;
- errpkt.e_data.data = 0;
+ errpkt.e_data.data = NULL;
retval = krb5_mk_error(kdc_context, &errpkt, scratch);
free(errpkt.text.data);
diff --git a/src/kdc/extern.c b/src/kdc/extern.c
index fa3e0af43b..2a2c1ae22e 100644
--- a/src/kdc/extern.c
+++ b/src/kdc/extern.c
@@ -37,6 +37,7 @@ krb5_data empty_string = {0, 0, ""};
krb5_timestamp kdc_infinity = KRB5_INT32_MAX; /* XXX */
krb5_rcache kdc_rcache = (krb5_rcache) NULL;
krb5_keyblock psr_key;
+krb5_int32 max_dgram_reply_size = MAX_DGRAM_SIZE;
volatile int signal_requests_exit = 0; /* gets set when signal hits */
volatile int signal_requests_hup = 0; /* ditto */
diff --git a/src/kdc/extern.h b/src/kdc/extern.h
index 2650536955..0da6b14fbb 100644
--- a/src/kdc/extern.h
+++ b/src/kdc/extern.h
@@ -90,6 +90,7 @@ extern krb5_data empty_string; /* an empty string */
extern krb5_timestamp kdc_infinity; /* greater than all other timestamps */
extern krb5_rcache kdc_rcache; /* replay cache */
extern krb5_keyblock psr_key; /* key for predicted sam response */
+extern krb5_int32 max_dgram_reply_size; /* maximum datagram size */
extern volatile int signal_requests_exit;
extern volatile int signal_requests_hup;
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 9fd37f2d6b..315269c2af 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -2,6 +2,7 @@
* kdc/kdc_authdata.c
*
* Copyright (C) 2007 Apple Inc. All Rights Reserved.
+ * Copyright (C) 2008 by the Massachusetts Institute of Technology.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
@@ -42,109 +43,95 @@ static const char *objdirs[] = { KRB5_AUTHDATA_PLUGIN_BUNDLE_DIR, LIBDIR "/krb5/
static const char *objdirs[] = { LIBDIR "/krb5/plugins/authdata", NULL };
#endif
-typedef krb5_error_code (*authdata_proc)
+/* MIT Kerberos 1.6 (V0) authdata plugin callback */
+typedef krb5_error_code (*authdata_proc_0)
(krb5_context, krb5_db_entry *client,
krb5_data *req_pkt,
krb5_kdc_req *request,
krb5_enc_tkt_part * enc_tkt_reply);
-
+/* MIT Kerberos 1.7 (V1) authdata plugin callback */
+typedef krb5_error_code (*authdata_proc_1)
+ (krb5_context, unsigned int flags,
+ krb5_db_entry *client, krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
typedef krb5_error_code (*init_proc)
(krb5_context, void **);
typedef void (*fini_proc)
(krb5_context, void *);
+/* Internal authdata system for copying TGS-REQ authdata to ticket */
+static krb5_error_code handle_request_authdata
+ (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
+
+/* Internal authdata system for handling KDC-issued authdata */
+static krb5_error_code handle_tgt_authdata
+ (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
+
typedef struct _krb5_authdata_systems {
const char *name;
+#define AUTHDATA_SYSTEM_UNKNOWN -1
+#define AUTHDATA_SYSTEM_V0 0
+#define AUTHDATA_SYSTEM_V1 1
int type;
+#define AUTHDATA_FLAG_CRITICAL 0x1
int flags;
void *plugin_context;
init_proc init;
fini_proc fini;
- authdata_proc handle_authdata;
+ union {
+ authdata_proc_1 v1;
+ authdata_proc_0 v0;
+ } handle_authdata;
} krb5_authdata_systems;
-#undef GREET_PREAUTH
-
-#ifdef GREET_PREAUTH
-static krb5_error_code
-greet_init(krb5_context ctx, void **blob)
-{
- *blob = "hello";
- return 0;
-}
-
-static void
-greet_fini(krb5_context ctx, void *blob)
-{
-}
-
-static krb5_error_code
-greet_authdata(krb5_context ctx, krb5_db_entry *client,
- krb5_data *req_pkt,
- krb5_kdc_req *request,
- krb5_enc_tkt_part * enc_tkt_reply)
-{
-#define GREET_SIZE (20)
-
- char *p;
- krb5_authdata *a;
- size_t count;
- krb5_authdata **new_ad;
-
- krb5_klog_syslog (LOG_DEBUG, "in greet_authdata");
-
- p = calloc(1, GREET_SIZE);
- a = calloc(1, sizeof(*a));
-
- if (p == NULL || a == NULL) {
- free(p);
- free(a);
- return ENOMEM;
- }
- strlcpy(p, "hello", GREET_SIZE);
- a->magic = KV5M_AUTHDATA;
- a->ad_type = -42;
- a->length = GREET_SIZE;
- a->contents = p;
- if (enc_tkt_reply->authorization_data == 0) {
- count = 0;
- } else {
- for (count = 0; enc_tkt_reply->authorization_data[count] != 0; count++)
- ;
- }
- new_ad = realloc(enc_tkt_reply->authorization_data,
- (count+2) * sizeof(krb5_authdata *));
- if (new_ad == NULL) {
- free(p);
- free(a);
- return ENOMEM;
- }
- enc_tkt_reply->authorization_data = new_ad;
- new_ad[count] = a;
- new_ad[count+1] = NULL;
- return 0;
-}
-#endif
-
static krb5_authdata_systems static_authdata_systems[] = {
-#ifdef GREET_PREAUTH
- { "greeting", 0, 0, 0, greet_init, greet_fini, greet_authdata },
-#endif
- { "[end]", -1,}
+ { "tgs_req", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_request_authdata } },
+ { "tgt", AUTHDATA_SYSTEM_V1, AUTHDATA_FLAG_CRITICAL, NULL, NULL, NULL, { handle_tgt_authdata } },
};
static krb5_authdata_systems *authdata_systems;
static int n_authdata_systems;
static struct plugin_dir_handle authdata_plugins;
+/* Load both v0 and v1 authdata plugins */
krb5_error_code
load_authdata_plugins(krb5_context context)
{
- void **authdata_plugins_ftables = NULL;
- struct krb5plugin_authdata_ftable_v0 *ftable = NULL;
+ void **authdata_plugins_ftables_v0 = NULL;
+ void **authdata_plugins_ftables_v1 = NULL;
size_t module_count;
- int i, k;
+ size_t i, k;
init_proc server_init_proc = NULL;
+ krb5_error_code code;
/* Attempt to load all of the authdata plugins we can find. */
PLUGIN_DIR_INIT(&authdata_plugins);
@@ -156,23 +143,41 @@ load_authdata_plugins(krb5_context context)
}
/* Get the method tables provided by the loaded plugins. */
- authdata_plugins_ftables = NULL;
+ authdata_plugins_ftables_v0 = NULL;
+ authdata_plugins_ftables_v1 = NULL;
n_authdata_systems = 0;
+
if (krb5int_get_plugin_dir_data(&authdata_plugins,
+ "authdata_server_1",
+ &authdata_plugins_ftables_v1, &context->err) != 0 ||
+ krb5int_get_plugin_dir_data(&authdata_plugins,
"authdata_server_0",
- &authdata_plugins_ftables, &context->err) != 0) {
- return KRB5_PLUGIN_NO_HANDLE;
+ &authdata_plugins_ftables_v0, &context->err) != 0) {
+ code = KRB5_PLUGIN_NO_HANDLE;
+ goto cleanup;
}
/* Count the valid modules. */
module_count = sizeof(static_authdata_systems)
/ sizeof(static_authdata_systems[0]);
- if (authdata_plugins_ftables != NULL) {
- for (i = 0; authdata_plugins_ftables[i] != NULL; i++) {
- ftable = authdata_plugins_ftables[i];
- if ((ftable->authdata_proc != NULL)) {
+
+ if (authdata_plugins_ftables_v1 != NULL) {
+ struct krb5plugin_authdata_ftable_v1 *ftable;
+
+ for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) {
+ ftable = authdata_plugins_ftables_v1[i];
+ if (ftable->authdata_proc != NULL)
+ module_count++;
+ }
+ }
+
+ if (authdata_plugins_ftables_v0 != NULL) {
+ struct krb5plugin_authdata_ftable_v0 *ftable;
+
+ for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
+ ftable = authdata_plugins_ftables_v0[i];
+ if (ftable->authdata_proc != NULL)
module_count++;
- }
}
}
@@ -180,16 +185,14 @@ load_authdata_plugins(krb5_context context)
* leave room for a terminator entry. */
authdata_systems = calloc(module_count + 1, sizeof(krb5_authdata_systems));
if (authdata_systems == NULL) {
- krb5int_free_plugin_dir_data(authdata_plugins_ftables);
- return ENOMEM;
+ code = ENOMEM;
+ goto cleanup;
}
/* Add the locally-supplied mechanisms to the dynamic list first. */
for (i = 0, k = 0;
i < sizeof(static_authdata_systems) / sizeof(static_authdata_systems[0]);
i++) {
- if (static_authdata_systems[i].type == -1)
- break;
authdata_systems[k] = static_authdata_systems[i];
/* Try to initialize the authdata system. If it fails, we'll remove it
* from the list of systems we'll be using. */
@@ -202,13 +205,53 @@ load_authdata_plugins(krb5_context context)
k++;
}
- /* Now add the dynamically-loaded mechanisms to the list. */
- if (authdata_plugins_ftables != NULL) {
- for (i = 0; authdata_plugins_ftables[i] != NULL; i++) {
+ /* Add dynamically loaded V1 plugins */
+ if (authdata_plugins_ftables_v1 != NULL) {
+ struct krb5plugin_authdata_ftable_v1 *ftable;
+
+ for (i = 0; authdata_plugins_ftables_v1[i] != NULL; i++) {
+ krb5_error_code initerr;
+ void *pctx = NULL;
+
+ ftable = authdata_plugins_ftables_v1[i];
+ if ((ftable->authdata_proc == NULL)) {
+ continue;
+ }
+ server_init_proc = ftable->init_proc;
+ if ((server_init_proc != NULL) &&
+ ((initerr = (*server_init_proc)(context, &pctx)) != 0)) {
+ const char *emsg;
+ emsg = krb5_get_error_message(context, initerr);
+ if (emsg) {
+ krb5_klog_syslog(LOG_ERR,
+ "authdata %s failed to initialize: %s",
+ ftable->name, emsg);
+ krb5_free_error_message(context, emsg);
+ }
+ memset(&authdata_systems[k], 0, sizeof(authdata_systems[k]));
+
+ continue;
+ }
+
+ authdata_systems[k].name = ftable->name;
+ authdata_systems[k].type = AUTHDATA_SYSTEM_V1;
+ authdata_systems[k].init = server_init_proc;
+ authdata_systems[k].fini = ftable->fini_proc;
+ authdata_systems[k].handle_authdata.v1 = ftable->authdata_proc;
+ authdata_systems[k].plugin_context = pctx;
+ k++;
+ }
+ }
+
+ /* Add dynamically loaded V0 plugins */
+ if (authdata_plugins_ftables_v0 != NULL) {
+ struct krb5plugin_authdata_ftable_v0 *ftable;
+
+ for (i = 0; authdata_plugins_ftables_v0[i] != NULL; i++) {
krb5_error_code initerr;
void *pctx = NULL;
- ftable = authdata_plugins_ftables[i];
+ ftable = authdata_plugins_ftables_v0[i];
if ((ftable->authdata_proc == NULL)) {
continue;
}
@@ -229,19 +272,28 @@ load_authdata_plugins(krb5_context context)
}
authdata_systems[k].name = ftable->name;
+ authdata_systems[k].type = AUTHDATA_SYSTEM_V0;
authdata_systems[k].init = server_init_proc;
authdata_systems[k].fini = ftable->fini_proc;
- authdata_systems[k].handle_authdata = ftable->authdata_proc;
+ authdata_systems[k].handle_authdata.v0 = ftable->authdata_proc;
authdata_systems[k].plugin_context = pctx;
k++;
}
- krb5int_free_plugin_dir_data(authdata_plugins_ftables);
}
+
n_authdata_systems = k;
/* Add the end-of-list marker. */
authdata_systems[k].name = "[end]";
- authdata_systems[k].type = -1;
- return 0;
+ authdata_systems[k].type = AUTHDATA_SYSTEM_UNKNOWN;
+ code = 0;
+
+cleanup:
+ if (authdata_plugins_ftables_v1 != NULL)
+ krb5int_free_plugin_dir_data(authdata_plugins_ftables_v1);
+ if (authdata_plugins_ftables_v0 != NULL)
+ krb5int_free_plugin_dir_data(authdata_plugins_ftables_v0);
+
+ return code;
}
krb5_error_code
@@ -264,33 +316,296 @@ unload_authdata_plugins(krb5_context context)
return 0;
}
+/* Merge authdata. If copy == 0, in_authdata is invalid on return */
+static krb5_error_code
+merge_authdata (krb5_context context,
+ krb5_authdata **in_authdata,
+ krb5_authdata ***out_authdata,
+ krb5_boolean copy)
+{
+ size_t i, nadata = 0;
+ krb5_authdata **authdata = *out_authdata;
+
+ if (in_authdata == NULL || in_authdata[0] == NULL)
+ return 0;
+
+ if (authdata != NULL) {
+ for (nadata = 0; authdata[nadata] != NULL; nadata++)
+ ;
+ }
+
+ for (i = 0; in_authdata[i] != NULL; i++)
+ ;
+
+ if (authdata == NULL) {
+ authdata = (krb5_authdata **)calloc(i + 1, sizeof(krb5_authdata *));
+ } else {
+ authdata = (krb5_authdata **)realloc(authdata,
+ ((nadata + i + 1) * sizeof(krb5_authdata *)));
+ }
+ if (authdata == NULL)
+ return ENOMEM;
+
+ if (copy) {
+ krb5_error_code code;
+ krb5_authdata **tmp;
+
+ code = krb5_copy_authdata(context, in_authdata, &tmp);
+ if (code != 0)
+ return code;
+
+ in_authdata = tmp;
+ }
+
+ for (i = 0; in_authdata[i] != NULL; i++)
+ authdata[nadata + i] = in_authdata[i];
+
+ authdata[nadata + i] = NULL;
+
+ free(in_authdata);
+
+ *out_authdata = authdata;
+
+ return 0;
+}
+
+/* Handle copying TGS-REQ authorization data into reply */
+static krb5_error_code
+handle_request_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply)
+{
+ krb5_error_code code;
+ krb5_data scratch;
+
+ if (request->msg_type != KRB5_TGS_REQ ||
+ request->authorization_data.ciphertext.data == NULL)
+ return 0;
+
+ assert(enc_tkt_request != NULL);
+
+ scratch.length = request->authorization_data.ciphertext.length;
+ scratch.data = malloc(scratch.length);
+ if (scratch.data == NULL)
+ return ENOMEM;
+
+ code = krb5_c_decrypt(context,
+ enc_tkt_request->session,
+ KRB5_KEYUSAGE_TGS_REQ_AD_SESSKEY,
+ 0, &request->authorization_data,
+ &scratch);
+ if (code != 0) {
+ free(scratch.data);
+ return code;
+ }
+
+ /* scratch now has the authorization data, so we decode it, and make
+ * it available to subsequent authdata plugins */
+ code = decode_krb5_authdata(&scratch, &request->unenc_authdata);
+ if (code != 0) {
+ free(scratch.data);
+ return code;
+ }
+
+ free(scratch.data);
+
+ code = merge_authdata(context, request->unenc_authdata,
+ &enc_tkt_reply->authorization_data, TRUE /* copy */);
+
+ return code;
+}
+
+/* Handle backend-managed authorization data */
+static krb5_error_code
+handle_tgt_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply)
+{
+ krb5_error_code code;
+ krb5_authdata **db_authdata = NULL;
+ krb5_db_entry ad_entry;
+ int ad_nprincs = 0;
+ krb5_boolean tgs_req = (request->msg_type == KRB5_TGS_REQ);
+ krb5_const_principal actual_client;
+
+ /*
+ * Check whether KDC issued authorization data should be included.
+ * A server can explicitly disable the inclusion of authorization
+ * data by setting the KRB5_KDB_NO_AUTH_DATA_REQUIRED flag on its
+ * principal entry. Otherwise authorization data will be included
+ * if it was present in the TGT, the client is from another realm
+ * or protocol transition/constrained delegation was used, or, in
+ * the AS-REQ case, if the pre-auth data indicated the PAC should
+ * be present.
+ *
+ * We permit sign_authorization_data() to return a krb5_db_entry
+ * representing the principal associated with the authorization
+ * data, in case that principal is not local to our realm and we
+ * need to perform additional checks (such as disabling delegation
+ * for cross-realm protocol transition below).
+ */
+ if (tgs_req) {
+ assert(enc_tkt_request != NULL);
+
+ if (isflagset(server->attributes, KRB5_KDB_NO_AUTH_DATA_REQUIRED))
+ return 0;
+
+ if (enc_tkt_request->authorization_data == NULL &&
+ !isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM | KRB5_KDB_FLAGS_S4U))
+ return 0;
+
+ assert(enc_tkt_reply->times.authtime == enc_tkt_request->times.authtime);
+ } else {
+ if (!isflagset(flags, KRB5_KDB_FLAG_INCLUDE_PAC))
+ return 0;
+ }
+
+ /*
+ * We have this special case for protocol transition, because for
+ * cross-realm protocol transition the ticket reply client will
+ * not be changed until the final hop.
+ */
+ if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION))
+ actual_client = for_user_princ;
+ else
+ actual_client = enc_tkt_reply->client;
+
+ /*
+ * If the backend does not implement the sign authdata method, then
+ * just copy the TGT authorization data into the reply, except for
+ * the constrained delegation case (which requires special handling
+ * because it will promote untrusted auth data to KDC issued auth
+ * data; this requires backend-specific code)
+ *
+ * Presently this interface does not support using request auth data
+ * to influence (eg. possibly restrict) the reply auth data.
+ */
+ code = sign_db_authdata(context,
+ flags,
+ actual_client,
+ client,
+ server,
+ krbtgt,
+ client_key,
+ server_key, /* U2U or server key */
+ enc_tkt_reply->times.authtime,
+ tgs_req ? enc_tkt_request->authorization_data : NULL,
+ &db_authdata,
+ &ad_entry,
+ &ad_nprincs);
+ if (code == KRB5_KDB_DBTYPE_NOSUP) {
+ assert(ad_nprincs == 0);
+ assert(db_authdata == NULL);
+
+ if (isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION))
+ return KRB5KDC_ERR_POLICY;
+
+ if (tgs_req)
+ return merge_authdata(context, enc_tkt_request->authorization_data,
+ &enc_tkt_reply->authorization_data, TRUE);
+ else
+ return 0;
+ }
+
+ if (ad_nprincs != 0) {
+ if (isflagset(flags, KRB5_KDB_FLAG_PROTOCOL_TRANSITION) &&
+ isflagset(ad_entry.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))
+ clear(enc_tkt_reply->flags, TKT_FLG_FORWARDABLE);
+
+ krb5_db_free_principal(context, &ad_entry, ad_nprincs);
+
+ if (ad_nprincs != 1) {
+ if (db_authdata != NULL)
+ krb5_free_authdata(context, db_authdata);
+ return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
+ }
+ }
+
+ if (db_authdata != NULL) {
+ code = merge_authdata(context, db_authdata,
+ &enc_tkt_reply->authorization_data,
+ FALSE);
+ if (code != 0)
+ krb5_free_authdata(context, db_authdata);
+ }
+
+ return code;
+}
+
krb5_error_code
-handle_authdata (krb5_context context, krb5_db_entry *client,
- krb5_data *req_pkt, krb5_kdc_req *request,
+handle_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
krb5_enc_tkt_part *enc_tkt_reply)
{
- krb5_error_code retval = 0;
+ krb5_error_code code = 0;
int i;
- const char *emsg;
-
- krb5_klog_syslog (LOG_DEBUG, "handling authdata");
+ assert(enc_tkt_reply->authorization_data == NULL);
for (i = 0; i < n_authdata_systems; i++) {
const krb5_authdata_systems *asys = &authdata_systems[i];
- if (asys->handle_authdata && asys->type != -1) {
- retval = asys->handle_authdata(context, client, req_pkt,
- request, enc_tkt_reply);
- if (retval) {
- emsg = krb5_get_error_message (context, retval);
- krb5_klog_syslog (LOG_INFO,
- "authdata (%s) handling failure: %s",
- asys->name, emsg);
- krb5_free_error_message (context, emsg);
- } else {
- krb5_klog_syslog (LOG_DEBUG, ".. .. ok");
- }
+
+ switch (asys->type) {
+ case AUTHDATA_SYSTEM_V0:
+ /* V0 was only in AS-REQ code path */
+ if (request->msg_type != KRB5_AS_REQ)
+ continue;
+
+ code = (*asys->handle_authdata.v0)(context, client, req_pkt,
+ request, enc_tkt_reply);
+ break;
+ case AUTHDATA_SYSTEM_V1:
+ code = (*asys->handle_authdata.v1)(context, flags,
+ client, server, krbtgt,
+ client_key, server_key,
+ req_pkt, request, for_user_princ,
+ enc_tkt_request,
+ enc_tkt_reply);
+ break;
+ default:
+ code = 0;
+ break;
+ }
+ if (code != 0) {
+ const char *emsg;
+
+ emsg = krb5_get_error_message (context, code);
+ krb5_klog_syslog (LOG_INFO,
+ "authdata (%s) handling failure: %s",
+ asys->name, emsg);
+ krb5_free_error_message (context, emsg);
+
+ if (asys->flags & AUTHDATA_FLAG_CRITICAL)
+ break;
}
}
- return 0;
+ return code;
}
+
diff --git a/src/kdc/kdc_preauth.c b/src/kdc/kdc_preauth.c
index c7655ab0d9..51c16739d7 100644
--- a/src/kdc/kdc_preauth.c
+++ b/src/kdc/kdc_preauth.c
@@ -51,6 +51,33 @@
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "k5-int.h"
#include "kdc_util.h"
@@ -311,6 +338,27 @@ static krb5_preauth_systems static_preauth_systems[] = {
0,
0
},
+ {
+ "pac-request",
+ KRB5_PADATA_PAC_REQUEST,
+ PA_PSEUDO,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ },
+#if 0
+ {
+ "server-referral",
+ KRB5_PADATA_SERVER_REFERRAL,
+ PA_PSEUDO,
+ 0,
+ 0,
+ return_server_referral
+ },
+#endif
{ "[end]", -1,}
};
@@ -323,7 +371,7 @@ load_preauth_plugins(krb5_context context)
{
void **preauth_plugins_ftables;
struct krb5plugin_preauth_server_ftable_v1 *ftable;
- int module_count, i, j, k;
+ size_t module_count, i, j, k;
void *plugin_context;
preauth_server_init_proc server_init_proc = NULL;
char **kdc_realm_names = NULL;
@@ -381,7 +429,7 @@ load_preauth_plugins(krb5_context context)
krb5int_free_plugin_dir_data(preauth_plugins_ftables);
return ENOMEM;
}
- for (i = 0; i < kdc_numrealms; i++) {
+ for (i = 0; i < (size_t)kdc_numrealms; i++) {
kdc_realm_names[i] = kdc_realmlist[i]->realm_name;
}
kdc_realm_names[i] = NULL;
@@ -1102,11 +1150,12 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
krb5_klog_syslog (LOG_INFO, "no valid preauth type found: %s", emsg);
krb5_free_error_message(context, emsg);
}
+
/* The following switch statement allows us
* to return some preauth system errors back to the client.
*/
switch(retval) {
- case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case 0: /* in case of PA-PAC-REQUEST with no PA-ENC-TIMESTAMP */
case KRB5KRB_AP_ERR_SKEW:
case KRB5KDC_ERR_ETYPE_NOSUPP:
/* rfc 4556 */
@@ -1130,6 +1179,7 @@ check_padata (krb5_context context, krb5_db_entry *client, krb5_data *req_pkt,
/* This value is shared with KRB5KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED. */
/* case KRB5KDC_ERR_KEY_TOO_WEAK: */
return retval;
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
default:
return KRB5KDC_ERR_PREAUTH_FAILED;
}
@@ -1293,7 +1343,7 @@ verify_enc_timestamp(krb5_context context, krb5_db_entry *client,
krb5_timestamp timenow;
krb5_error_code decrypt_err = 0;
- scratch.data = pa->contents;
+ scratch.data = (char *)pa->contents;
scratch.length = pa->length;
enc_ts_data.data = 0;
@@ -1599,7 +1649,7 @@ etype_info_as_rep_helper(krb5_context context, krb5_pa_data * padata,
if (retval)
goto cleanup;
- tmp_padata->contents = scratch->data;
+ tmp_padata->contents = (krb5_octet *)scratch->data;
tmp_padata->length = scratch->length;
*send_pa = tmp_padata;
@@ -1769,7 +1819,7 @@ return_sam_data(krb5_context context, krb5_pa_data *in_padata,
* all this once.
*/
- scratch.data = in_padata->contents;
+ scratch.data = (char *)in_padata->contents;
scratch.length = in_padata->length;
if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
@@ -2079,7 +2129,7 @@ get_sam_edata(krb5_context context, krb5_kdc_req *request,
if (retval) goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = scratch->data;
+ pa_data->contents = (krb5_octet *)scratch->data;
pa_data->length = scratch->length;
retval = 0;
@@ -2247,7 +2297,7 @@ sc.sam_challenge_label.length = strlen(sc.sam_challenge_label.data);
if (retval) goto cleanup;
pa_data->magic = KV5M_PA_DATA;
pa_data->pa_type = KRB5_PADATA_SAM_CHALLENGE;
- pa_data->contents = scratch->data;
+ pa_data->contents = (krb5_octet *)scratch->data;
pa_data->length = scratch->length;
retval = 0;
@@ -2278,7 +2328,7 @@ verify_sam_response(krb5_context context, krb5_db_entry *client,
krb5_timestamp timenow;
char *princ_req = 0, *princ_psr = 0;
- scratch.data = pa->contents;
+ scratch.data = (char *)pa->contents;
scratch.length = pa->length;
if ((retval = decode_krb5_sam_response(&scratch, &sr))) {
@@ -2847,3 +2897,146 @@ cleanup:
}
#endif /* APPLE_PKINIT */
+
+/*
+ * Returns TRUE if the PAC should be included
+ */
+krb5_boolean
+include_pac_p(krb5_context context, krb5_kdc_req *request)
+{
+ krb5_error_code code;
+ krb5_pa_data **padata;
+ krb5_boolean retval = TRUE; /* default is to return PAC */
+ krb5_data data;
+ krb5_pa_pac_req *req = NULL;
+
+ if (request->padata == NULL) {
+ return retval;
+ }
+
+ for (padata = request->padata; *padata != NULL; padata++) {
+ if ((*padata)->pa_type == KRB5_PADATA_PAC_REQUEST) {
+ data.data = (char *)(*padata)->contents;
+ data.length = (*padata)->length;
+
+ code = decode_krb5_pa_pac_req(&data, &req);
+ if (code == 0) {
+ retval = req->include_pac;
+ krb5_free_pa_pac_req(context, req);
+ req = NULL;
+ }
+ break;
+ }
+ }
+
+ return retval;
+}
+
+krb5_error_code
+return_svr_referral_data(krb5_context context,
+ krb5_db_entry *server,
+ krb5_enc_kdc_rep_part *reply_encpart)
+{
+ krb5_error_code code;
+ krb5_tl_data tl_data;
+ krb5_pa_data *pa_data;
+
+ /* This should be initialized and only used for Win2K compat */
+ assert(reply_encpart->enc_padata == NULL);
+
+ tl_data.tl_data_type = KRB5_TL_SVR_REFERRAL_DATA;
+
+ code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
+ if (code || tl_data.tl_data_length == 0)
+ return 0; /* no server referrals to return */
+
+ pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data));
+ if (pa_data == NULL)
+ return ENOMEM;
+
+ pa_data->magic = KV5M_PA_DATA;
+ pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
+ pa_data->length = tl_data.tl_data_length;
+ pa_data->contents = malloc(pa_data->length);
+ if (pa_data->contents == NULL) {
+ free(pa_data);
+ return ENOMEM;
+ }
+ memcpy(pa_data->contents, tl_data.tl_data_contents, tl_data.tl_data_length);
+
+ reply_encpart->enc_padata = (krb5_pa_data **)calloc(2, sizeof(krb5_pa_data *));
+ if (reply_encpart->enc_padata == NULL) {
+ free(pa_data->contents);
+ free(pa_data);
+ return ENOMEM;
+ }
+
+ reply_encpart->enc_padata[0] = pa_data;
+ reply_encpart->enc_padata[1] = NULL;
+
+ return 0;
+}
+
+#if 0
+static krb5_error_code return_server_referral(krb5_context context,
+ krb5_pa_data * padata,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_kdc_req *request, krb5_kdc_rep *reply,
+ krb5_key_data *client_key,
+ krb5_keyblock *encrypting_key,
+ krb5_pa_data **send_pa)
+{
+ krb5_error_code code;
+ krb5_tl_data tl_data;
+ krb5_pa_data *pa_data;
+ krb5_enc_data enc_data;
+ krb5_data plain;
+ krb5_data *enc_pa_data;
+
+ *send_pa = NULL;
+
+ tl_data.tl_data_type = KRB5_TL_SERVER_REFERRAL;
+
+ code = krb5_dbe_lookup_tl_data(context, server, &tl_data);
+ if (code || tl_data.tl_data_length == 0)
+ return 0; /* no server referrals to return */
+
+ plain.length = tl_data.tl_data_length;
+ plain.data = tl_data.tl_data_contents;
+
+ /* Encrypt ServerReferralData */
+ code = krb5_encrypt_helper(context, encrypting_key,
+ KRB5_KEYUSAGE_PA_SERVER_REFERRAL_DATA,
+ &plain, &enc_data);
+ if (code)
+ return code;
+
+ /* Encode ServerReferralData into PA-SERVER-REFERRAL-DATA */
+ code = encode_krb5_enc_data(&enc_data, &enc_pa_data);
+ if (code) {
+ krb5_free_data_contents(context, &enc_data.ciphertext);
+ return code;
+ }
+
+ krb5_free_data_contents(context, &enc_data.ciphertext);
+
+ /* Return PA-SERVER-REFERRAL-DATA */
+ pa_data = (krb5_pa_data *)malloc(sizeof(*pa_data));
+ if (pa_data == NULL) {
+ krb5_free_data(context, enc_pa_data);
+ return ENOMEM;
+ }
+
+ pa_data->magic = KV5M_PA_DATA;
+ pa_data->pa_type = KRB5_PADATA_SVR_REFERRAL_INFO;
+ pa_data->length = enc_pa_data->length;
+ pa_data->contents = enc_pa_data->data;
+
+ free(enc_pa_data); /* don't free contents */
+
+ *send_pa = pa_data;
+
+ return 0;
+}
+#endif
diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c
index 4068ec622f..a3628cf91f 100644
--- a/src/kdc/kdc_util.c
+++ b/src/kdc/kdc_util.c
@@ -26,6 +26,33 @@
*
* Utility functions for the KDC implementation.
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "k5-int.h"
#include "kdc_util.h"
@@ -135,19 +162,22 @@ concat_authorization_data(krb5_authdata **first, krb5_authdata **second,
}
krb5_boolean
-realm_compare(krb5_principal princ1, krb5_principal princ2)
+realm_compare(krb5_const_principal princ1, krb5_const_principal princ2)
{
- krb5_data *realm1 = krb5_princ_realm(kdc_context, princ1);
- krb5_data *realm2 = krb5_princ_realm(kdc_context, princ2);
+ return krb5_realm_compare(kdc_context, princ1, princ2);
+}
- return data_eq(*realm1, *realm2);
+krb5_boolean
+is_local_principal(krb5_const_principal princ1)
+{
+ return krb5_realm_compare(kdc_context, princ1, tgs_server);
}
/*
* Returns TRUE if the kerberos principal is the name of a Kerberos ticket
* service.
*/
-krb5_boolean krb5_is_tgs_principal(krb5_principal principal)
+krb5_boolean krb5_is_tgs_principal(krb5_const_principal principal)
{
if ((krb5_princ_size(kdc_context, principal) > 0) &&
data_eq_string (*krb5_princ_component(kdc_context, principal, 0),
@@ -186,12 +216,29 @@ comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket,
return(0);
}
+krb5_pa_data *
+find_pa_data(krb5_pa_data **padata, krb5_preauthtype pa_type)
+{
+ krb5_pa_data **tmppa;
+
+ if (padata == NULL)
+ return NULL;
+
+ for (tmppa = padata; *tmppa != NULL; tmppa++) {
+ if ((*tmppa)->pa_type == pa_type)
+ break;
+ }
+
+ return *tmppa;
+}
+
krb5_error_code
kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
krb5_data *pkt, krb5_ticket **ticket,
+ krb5_db_entry *krbtgt, int *nprincs,
krb5_keyblock **subkey)
{
- krb5_pa_data ** tmppa;
+ krb5_pa_data * tmppa;
krb5_ap_req * apreq;
krb5_error_code retval;
krb5_data scratch1;
@@ -200,23 +247,20 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
krb5_auth_context auth_context = NULL;
krb5_authenticator * authenticator = NULL;
krb5_checksum * his_cksum = NULL;
-/* krb5_keyblock * key = NULL;*/
-/* krb5_kvno kvno = 0;*/
+ krb5_keyblock * key = NULL;
+ krb5_kvno kvno = 0;
- if (!request->padata)
- return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
- for (tmppa = request->padata; *tmppa; tmppa++) {
- if ((*tmppa)->pa_type == KRB5_PADATA_AP_REQ)
- break;
- }
- if (!*tmppa) /* cannot find any AP_REQ */
+ *nprincs = 0;
+
+ tmppa = find_pa_data(request->padata, KRB5_PADATA_AP_REQ);
+ if (!tmppa)
return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
- scratch1.length = (*tmppa)->length;
- scratch1.data = (char *)(*tmppa)->contents;
+ scratch1.length = tmppa->length;
+ scratch1.data = (char *)tmppa->contents;
if ((retval = decode_krb5_ap_req(&scratch1, &apreq)))
return retval;
-
+
if (isflagset(apreq->ap_options, AP_OPTS_USE_SESSION_KEY) ||
isflagset(apreq->ap_options, AP_OPTS_MUTUAL_REQUIRED)) {
krb5_klog_syslog(LOG_INFO, "TGS_REQ: SESSION KEY or MUTUAL");
@@ -234,9 +278,7 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
we set a flag here for checking below.
*/
- if (!data_eq(*krb5_princ_realm(kdc_context, apreq->ticket->server),
- *krb5_princ_realm(kdc_context, tgs_server)))
- foreign_server = TRUE;
+ foreign_server = !is_local_principal(apreq->ticket->server);
if ((retval = krb5_auth_con_init(kdc_context, &auth_context)))
goto cleanup;
@@ -250,21 +292,15 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
goto cleanup_auth_context;
#endif
-/*
- if ((retval = kdc_get_server_key(apreq->ticket, &key, &kvno)))
+ if ((retval = kdc_get_server_key(apreq->ticket, 0, krbtgt, nprincs, &key, &kvno)))
goto cleanup_auth_context;
-*/
-
/*
- * XXX This is currently wrong but to fix it will require making a
- * new keytab for groveling over the kdb.
+ * We do not use the KDB keytab because other parts of the TGS need the TGT key.
*/
-/*
retval = krb5_auth_con_setuseruserkey(kdc_context, auth_context, key);
krb5_free_keyblock(kdc_context, key);
if (retval)
goto cleanup_auth_context;
-*/
if ((retval = krb5_rd_req_decoded_anyflag(kdc_context, &auth_context, apreq,
apreq->ticket->server,
@@ -322,11 +358,8 @@ kdc_process_tgs_req(krb5_kdc_req *request, const krb5_fulladdr *from,
}
/* make sure the client is of proper lineage (see above) */
- if (foreign_server) {
- krb5_data *tkt_realm = krb5_princ_realm(kdc_context,
- (*ticket)->enc_part2->client);
- krb5_data *tgs_realm = krb5_princ_realm(kdc_context, tgs_server);
- if (data_eq(*tkt_realm, *tgs_realm)) {
+ if (foreign_server && !find_pa_data(request->padata, KRB5_PADATA_FOR_USER)) {
+ if (is_local_principal((*ticket)->enc_part2->client)) {
/* someone in a foreign realm claiming to be local */
krb5_klog_syslog(LOG_INFO, "PROCESS_TGS: failed lineage check");
retval = KRB5KDC_ERR_POLICY;
@@ -374,39 +407,40 @@ cleanup:
* much else. -- tlyu
*/
krb5_error_code
-kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
+kdc_get_server_key(krb5_ticket *ticket, unsigned int flags,
+ krb5_db_entry *server,
+ int *nprincs, krb5_keyblock **key, krb5_kvno *kvno)
{
krb5_error_code retval;
- krb5_db_entry server;
krb5_boolean more;
- int nprincs;
krb5_key_data * server_key;
- nprincs = 1;
+ *nprincs = 1;
- if ((retval = get_principal(kdc_context, ticket->server,
- &server, &nprincs,
- &more))) {
+ retval = krb5_db_get_principal_ext(kdc_context,
+ ticket->server,
+ flags,
+ server,
+ nprincs,
+ &more);
+ if (retval) {
return(retval);
}
if (more) {
- krb5_db_free_principal(kdc_context, &server, nprincs);
return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
- } else if (nprincs != 1) {
+ } else if (*nprincs != 1) {
char *sname;
- krb5_db_free_principal(kdc_context, &server, nprincs);
if (!krb5_unparse_name(kdc_context, ticket->server, &sname)) {
- limit_string(sname);
krb5_klog_syslog(LOG_ERR,"TGS_REQ: UNKNOWN SERVER: server='%s'",
sname);
free(sname);
}
return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN);
}
- retval = krb5_dbe_find_enctype(kdc_context, &server,
+ retval = krb5_dbe_find_enctype(kdc_context, server,
ticket->enc_part.enctype, -1,
- ticket->enc_part.kvno, &server_key);
+ (krb5_int32)ticket->enc_part.kvno, &server_key);
if (retval)
goto errout;
if (!server_key) {
@@ -418,14 +452,9 @@ kdc_get_server_key(krb5_ticket *ticket, krb5_keyblock **key, krb5_kvno *kvno)
retval = krb5_dbekd_decrypt_key_data(kdc_context, &master_keyblock,
server_key,
*key, NULL);
- if (retval) {
- free(*key);
- *key = NULL;
- }
} else
retval = ENOMEM;
errout:
- krb5_db_free_principal(kdc_context, &server, nprincs);
return retval;
}
@@ -711,7 +740,7 @@ add_to_transited(krb5_data *tgt_trans, krb5_data *new_trans,
/* Note that the second test here is an unsigned comparison,
so the first half (or a cast) is also required. */
- assert(nlst < 0 || nlst < sizeof(next));
+ assert(nlst < 0 || nlst < (int)sizeof(next));
if ((nlst < 0 || next[nlst] != '.') &&
(next[0] != '/') &&
(pl = subrealm(exp, realm))) {
@@ -900,7 +929,21 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
*status = "POSTDATE NOT ALLOWED";
return(KDC_ERR_CANNOT_POSTDATE);
}
-
+
+ /*
+ * A Windows KDC will return KDC_ERR_PREAUTH_REQUIRED instead of
+ * KDC_ERR_POLICY in the following case:
+ *
+ * - KDC_OPT_FORWARDABLE is set in KDCOptions but local
+ * policy has KRB5_KDB_DISALLOW_FORWARDABLE set for the
+ * client, and;
+ * - KRB5_KDB_REQUIRES_PRE_AUTH is set for the client but
+ * preauthentication data is absent in the request.
+ *
+ * Hence, this check most be done after the check for preauth
+ * data, and is now performed by validate_forwardable().
+ */
+#if 0
/* Client and server must allow forwardable tickets */
if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
(isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
@@ -908,6 +951,7 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
*status = "FORWARDABLE NOT ALLOWED";
return(KDC_ERR_POLICY);
}
+#endif
/* Client and server must allow renewable tickets */
if (isflagset(request->kdc_options, KDC_OPT_RENEWABLE) &&
@@ -928,7 +972,7 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
/* Check to see if client is locked out */
if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
*status = "CLIENT LOCKED OUT";
- return(KDC_ERR_C_PRINCIPAL_UNKNOWN);
+ return(KDC_ERR_CLIENT_REVOKED);
}
/* Check to see if server is locked out */
@@ -940,13 +984,13 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
/* Check to see if server is allowed to be a service */
if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
*status = "SERVICE NOT ALLOWED";
- return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+ return(KDC_ERR_MUST_USE_USER2USER);
}
/*
* Check against local policy
*/
- errcode = against_local_policy_as(request, server, client,
+ errcode = against_local_policy_as(request, client, server,
kdc_time, status);
if (errcode)
return errcode;
@@ -954,6 +998,21 @@ validate_as_request(register krb5_kdc_req *request, krb5_db_entry client,
return 0;
}
+int
+validate_forwardable(krb5_kdc_req *request, krb5_db_entry client,
+ krb5_db_entry server, krb5_timestamp kdc_time,
+ const char **status)
+{
+ *status = NULL;
+ if (isflagset(request->kdc_options, KDC_OPT_FORWARDABLE) &&
+ (isflagset(client.attributes, KRB5_KDB_DISALLOW_FORWARDABLE) ||
+ isflagset(server.attributes, KRB5_KDB_DISALLOW_FORWARDABLE))) {
+ *status = "FORWARDABLE NOT ALLOWED";
+ return(KDC_ERR_POLICY);
+ } else
+ return 0;
+}
+
#define ASN1_ID_CLASS (0xc0)
#define ASN1_ID_TYPE (0x20)
#define ASN1_ID_TAG (0x1f)
@@ -1061,7 +1120,7 @@ fetch_asn1_field(unsigned char *astream, unsigned int level,
lastlevel = tag;
if (levels == level) {
/* in our context-dependent class, is this the one we're looking for ? */
- if (tag == field) {
+ if (tag == (int)field) {
/* return length and data */
astream++;
savelen = *astream;
@@ -1108,8 +1167,7 @@ fetch_asn1_field(unsigned char *astream, unsigned int level,
KDC_OPT_ALLOW_POSTDATE | KDC_OPT_POSTDATED | \
KDC_OPT_RENEWABLE | KDC_OPT_RENEWABLE_OK | \
KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_RENEW | \
- KDC_OPT_VALIDATE)
-
+ KDC_OPT_VALIDATE | KDC_OPT_CANONICALIZE | KDC_OPT_CNAME_IN_ADDL_TKT)
#define NO_TGT_OPTION (KDC_OPT_FORWARDED | KDC_OPT_PROXY | KDC_OPT_RENEW | \
KDC_OPT_VALIDATE)
@@ -1277,7 +1335,7 @@ validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
/* Server must be allowed to be a service */
if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) {
*status = "SERVER NOT ALLOWED";
- return(KDC_ERR_S_PRINCIPAL_UNKNOWN);
+ return(KDC_ERR_MUST_USE_USER2USER);
}
/* Check the hot list */
@@ -1323,6 +1381,14 @@ validate_tgs_request(register krb5_kdc_req *request, krb5_db_entry server,
}
st_idx++;
}
+ if (isflagset(request->kdc_options, KDC_OPT_CNAME_IN_ADDL_TKT)) {
+ if (!request->second_ticket ||
+ !request->second_ticket[st_idx]) {
+ *status = "NO_2ND_TKT";
+ return(KDC_ERR_BADOPTION);
+ }
+ st_idx++;
+ }
/* Check for hardware preauthentication */
if (isflagset(server.attributes, KRB5_KDB_REQUIRES_HW_AUTH) &&
@@ -1603,6 +1669,549 @@ get_principal (krb5_context kcontext,
more);
}
+
+krb5_error_code
+sign_db_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgs_authdata,
+ krb5_authdata ***ret_authdata,
+ krb5_db_entry *ad_entry,
+ int *ad_nprincs)
+{
+ krb5_error_code code;
+ kdb_sign_auth_data_req req;
+ kdb_sign_auth_data_rep rep;
+ krb5_data req_data;
+ krb5_data rep_data;
+
+ *ret_authdata = NULL;
+ if (ad_entry != NULL) {
+ assert(ad_nprincs != NULL);
+ memset(ad_entry, 0, sizeof(*ad_entry));
+ *ad_nprincs = 0;
+ }
+
+ memset(&req, 0, sizeof(req));
+ memset(&rep, 0, sizeof(rep));
+
+ req.flags = flags;
+ req.client_princ = client_princ;
+ req.client = client;
+ req.server = server;
+ req.krbtgt = krbtgt;
+ req.client_key = client_key;
+ req.server_key = server_key;
+ req.authtime = authtime;
+ req.auth_data = tgs_authdata;
+
+ rep.entry = ad_entry;
+ rep.nprincs = 0;
+
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+
+ rep_data.data = (void *)&rep;
+ rep_data.length = sizeof(rep);
+
+ code = krb5_db_invoke(context,
+ KRB5_KDB_METHOD_SIGN_AUTH_DATA,
+ &req_data,
+ &rep_data);
+
+ *ret_authdata = rep.auth_data;
+ *ad_nprincs = rep.nprincs;
+
+ return code;
+}
+
+static krb5_error_code
+verify_s4u2self_checksum(krb5_context context,
+ krb5_keyblock *key,
+ krb5_pa_for_user *req)
+{
+ krb5_error_code code;
+ int i;
+ krb5_int32 name_type;
+ char *p;
+ krb5_data data;
+ krb5_boolean valid = FALSE;
+
+ if (!krb5_c_is_keyed_cksum(req->cksum.checksum_type)) {
+ return KRB5KRB_AP_ERR_INAPP_CKSUM;
+ }
+
+ /*
+ * Checksum is over name type and string components of
+ * client principal name and auth_package.
+ */
+ data.length = 4;
+ for (i = 0; i < krb5_princ_size(context, req->user); i++) {
+ data.length += krb5_princ_component(context, req->user, i)->length;
+ }
+ data.length += krb5_princ_realm(context, req->user)->length;
+ data.length += req->auth_package.length;
+
+ p = data.data = malloc(data.length);
+ if (data.data == NULL) {
+ return ENOMEM;
+ }
+
+ name_type = krb5_princ_type(context, req->user);
+ p[0] = (name_type >> 0 ) & 0xFF;
+ p[1] = (name_type >> 8 ) & 0xFF;
+ p[2] = (name_type >> 16) & 0xFF;
+ p[3] = (name_type >> 24) & 0xFF;
+ p += 4;
+
+ for (i = 0; i < krb5_princ_size(context, req->user); i++) {
+ memcpy(p, krb5_princ_component(context, req->user, i)->data,
+ krb5_princ_component(context, req->user, i)->length);
+ p += krb5_princ_component(context, req->user, i)->length;
+ }
+
+ memcpy(p, krb5_princ_realm(context, req->user)->data,
+ krb5_princ_realm(context, req->user)->length);
+ p += krb5_princ_realm(context, req->user)->length;
+
+ memcpy(p, req->auth_package.data, req->auth_package.length);
+ p += req->auth_package.length;
+
+ code = krb5_c_verify_checksum(context,
+ key,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ &data,
+ &req->cksum,
+ &valid);
+
+ if (code == 0 && valid == FALSE)
+ code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+
+ free(data.data);
+
+ return code;
+}
+
+/*
+ * Protocol transition validation code based on AS-REQ
+ * validation code
+ */
+static int
+validate_s4u2self_request(krb5_kdc_req *request,
+ const krb5_db_entry *client,
+ krb5_timestamp kdc_time,
+ const char **status)
+{
+ int errcode;
+ krb5_db_entry server = { 0 };
+
+ /* The client's password must not be expired, unless the server is
+ a KRB5_KDC_PWCHANGE_SERVICE. */
+ if (client->pw_expiration && client->pw_expiration < kdc_time) {
+ *status = "CLIENT KEY EXPIRED";
+ return KDC_ERR_KEY_EXP;
+ }
+
+ /* The client must not be expired */
+ if (client->expiration && client->expiration < kdc_time) {
+ *status = "CLIENT EXPIRED";
+ return KDC_ERR_NAME_EXP;
+ }
+
+ /*
+ * If the client requires password changing, then return an
+ * error; S4U2Self cannot be used to change a password.
+ */
+ if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE)) {
+ *status = "REQUIRED PWCHANGE";
+ return KDC_ERR_KEY_EXP;
+ }
+
+ /* Check to see if client is locked out */
+ if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) {
+ *status = "CLIENT LOCKED OUT";
+ return KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ }
+
+ /*
+ * Check against local policy
+ */
+ errcode = against_local_policy_as(request, *client, server,
+ kdc_time, status);
+ if (errcode)
+ return errcode;
+
+ return 0;
+}
+
+/*
+ * Protocol transition (S4U2Self)
+ */
+krb5_error_code
+kdc_process_s4u2self_req(krb5_context context,
+ krb5_kdc_req *request,
+ krb5_const_principal client_princ,
+ const krb5_db_entry *server,
+ krb5_keyblock *subkey,
+ krb5_timestamp kdc_time,
+ krb5_pa_for_user **for_user,
+ krb5_db_entry *princ,
+ int *nprincs,
+ const char **status)
+{
+ krb5_error_code code;
+ krb5_pa_data **pa_data;
+ krb5_data req_data;
+ krb5_boolean more;
+
+ *nprincs = 0;
+ memset(princ, 0, sizeof(*princ));
+
+ if (request->padata == NULL) {
+ return 0;
+ }
+
+ for (pa_data = request->padata; *pa_data != NULL; pa_data++) {
+ if ((*pa_data)->pa_type == KRB5_PADATA_FOR_USER)
+ break;
+ }
+ if (*pa_data == NULL) {
+ return 0;
+ }
+
+#if 0
+ /*
+ * Ignore request if the server principal is a TGS, not so much
+ * to avoid unconstrained tickets being issued (as that would
+ * require knowing the TGS key anyway) but so that we do not
+ * block the server referral path.
+ */
+ if (krb5_is_tgs_principal(server->princ)) {
+ return 0;
+ }
+#endif
+
+ *status = "PROCESS_S4U2SELF_REQUEST";
+
+ req_data.length = (*pa_data)->length;
+ req_data.data = (char *)(*pa_data)->contents;
+
+ code = decode_krb5_pa_for_user(&req_data, for_user);
+ if (code) {
+ return code;
+ }
+
+ if (krb5_princ_type(context, (*for_user)->user) !=
+ KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ *status = "INVALID_S4U2SELF_REQUEST";
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ code = verify_s4u2self_checksum(context, subkey, *for_user);
+ if (code) {
+ *status = "INVALID_S4U2SELF_CHECKSUM";
+ krb5_free_pa_for_user(kdc_context, *for_user);
+ *for_user = NULL;
+ return code;
+ }
+ if (!krb5_principal_compare_flags(context, request->server, client_princ,
+ KRB5_PRINCIPAL_COMPARE_ENTERPRISE)) {
+ *status = "INVALID_S4U2SELF_REQUEST";
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ /*
+ * Protocol transition is mutually exclusive with renew/forward/etc
+ * as well as user-to-user and constrained delegation.
+ *
+ * We can assert from this check that the header ticket was a TGT, as
+ * that is validated previously in validate_tgs_request().
+ */
+ if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT)) {
+ return KRB5KDC_ERR_BADOPTION;
+ }
+
+ /*
+ * Do not attempt to lookup principals in foreign realms.
+ */
+ if (is_local_principal((*for_user)->user)) {
+ *nprincs = 1;
+ code = krb5_db_get_principal_ext(kdc_context,
+ (*for_user)->user,
+ KRB5_KDB_FLAG_INCLUDE_PAC,
+ princ, nprincs, &more);
+ if (code) {
+ *status = "LOOKING_UP_S4U2SELF_PRINCIPAL";
+ *nprincs = 0;
+ return code; /* caller can free for_user */
+ }
+
+ if (more) {
+ *status = "NON_UNIQUE_S4U2SELF_PRINCIPAL";
+ return KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
+ } else if (*nprincs != 1) {
+ *status = "UNKNOWN_S4U2SELF_PRINCIPAL";
+ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ }
+
+ code = validate_s4u2self_request(request, princ, kdc_time, status);
+ if (code) {
+ return code;
+ }
+ }
+
+ *status = NULL;
+
+ return 0;
+}
+
+static krb5_boolean
+check_constrained_delegation_acl(krb5_context context,
+ krb5_tl_data *tl_data,
+ krb5_const_principal spn)
+{
+ krb5_principal acl;
+ krb5_boolean ret;
+
+ assert(tl_data->tl_data_contents[tl_data->tl_data_length] == '\0');
+
+ if (krb5_parse_name_flags(context,
+ (char *)tl_data->tl_data_contents,
+ KRB5_PRINCIPAL_PARSE_NO_REALM,
+ &acl) != 0)
+ return FALSE;
+
+ ret = krb5_principal_compare_flags(context, acl, spn, KRB5_PRINCIPAL_COMPARE_IGNORE_REALM);
+
+ krb5_free_principal(context, acl);
+
+ return ret;
+}
+
+static krb5_error_code
+check_allowed_to_delegate_to(krb5_context context,
+ const krb5_db_entry *server,
+ krb5_const_principal proxy)
+{
+ krb5_tl_data *tl_data;
+ krb5_boolean allowed = FALSE;
+
+ /* Can't get a TGT (otherwise it would be unconstrained delegation) */
+ if (krb5_is_tgs_principal(proxy)) {
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ /* Must be in same realm -- ACLs are non-qualified SPNs */
+ if (!krb5_realm_compare(kdc_context, server->princ, proxy)) {
+ return KRB5_IN_TKT_REALM_MISMATCH; /* XXX */
+ }
+
+ for (tl_data = server->tl_data; tl_data != NULL; tl_data = tl_data->tl_data_next) {
+ if (tl_data->tl_data_type == KRB5_TL_CONSTRAINED_DELEGATION_ACL) {
+ if (check_constrained_delegation_acl(context, tl_data, proxy)) {
+ allowed = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (allowed == FALSE) {
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+kdc_process_s4u2proxy_req(krb5_context context,
+ krb5_kdc_req *request,
+ const krb5_enc_tkt_part *t2enc,
+ const krb5_db_entry *server,
+ krb5_const_principal server_princ,
+ krb5_const_principal proxy_princ,
+ const char **status)
+{
+ krb5_error_code errcode;
+
+ /*
+ * Constrained delegation is mutually exclusive with renew/forward/etc.
+ * We can assert from this check that the header ticket was a TGT, as
+ * that is validated previously in validate_tgs_request().
+ */
+ if (request->kdc_options & (NO_TGT_OPTION | KDC_OPT_ENC_TKT_IN_SKEY)) {
+ return KRB5KDC_ERR_BADOPTION;
+ }
+
+ /* Ensure that evidence ticket server matches TGT client */
+ if (!krb5_principal_compare(kdc_context,
+ server->princ, /* after canon */
+ server_princ)) {
+ return KRB5KDC_ERR_SERVER_NOMATCH;
+ }
+
+ if (!isflagset(t2enc->flags, TKT_FLG_FORWARDABLE)) {
+ *status = "EVIDENCE_TKT_NOT_FORWARDABLE";
+ return KRB5_TKT_NOT_FORWARDABLE;
+ }
+
+ /* Backend policy check */
+ errcode = check_allowed_to_delegate_to(kdc_context,
+ server, proxy_princ);
+ if (errcode) {
+ *status = "NOT_ALLOWED_TO_DELEGATE";
+ return errcode;
+ }
+
+ return 0;
+}
+
+krb5_error_code
+kdc_check_transited_list(krb5_context context,
+ const krb5_data *trans,
+ const krb5_data *realm1,
+ const krb5_data *realm2)
+{
+ krb5_error_code code;
+ kdb_check_transited_realms_req req;
+ krb5_data req_data;
+ krb5_data rep_data;
+
+ /* First check using krb5.conf */
+ code = krb5_check_transited_list(kdc_context, trans, realm1, realm2);
+ if (code)
+ return code;
+
+ memset(&req, 0, sizeof(req));
+
+ req.tr_contents = trans;
+ req.client_realm = realm1;
+ req.server_realm = realm2;
+
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+
+ rep_data.data = NULL;
+ rep_data.length = 0;
+
+ code = krb5_db_invoke(context,
+ KRB5_KDB_METHOD_CHECK_TRANSITED_REALMS,
+ &req_data,
+ &rep_data);
+ if (code == KRB5_KDB_DBTYPE_NOSUP) {
+ code = 0;
+ }
+
+ assert(rep_data.length == 0);
+
+ return code;
+}
+
+krb5_error_code
+audit_as_request(krb5_kdc_req *request,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code errcode)
+{
+ krb5_error_code code;
+ kdb_audit_as_req req;
+ krb5_data req_data;
+ krb5_data rep_data;
+
+ memset(&req, 0, sizeof(req));
+
+ req.request = request;
+ req.client = client;
+ req.server = server;
+ req.authtime = authtime;
+ req.error_code = errcode;
+
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+
+ rep_data.data = NULL;
+ rep_data.length = 0;
+
+ code = krb5_db_invoke(kdc_context,
+ KRB5_KDB_METHOD_AUDIT_AS,
+ &req_data,
+ &rep_data);
+ if (code == KRB5_KDB_DBTYPE_NOSUP) {
+ return 0;
+ }
+
+ assert(rep_data.length == 0);
+
+ return code;
+}
+
+krb5_error_code
+audit_tgs_request(krb5_kdc_req *request,
+ krb5_const_principal client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code errcode)
+{
+ krb5_error_code code;
+ kdb_audit_tgs_req req;
+ krb5_data req_data;
+ krb5_data rep_data;
+
+ memset(&req, 0, sizeof(req));
+
+ req.request = request;
+ req.client = client;
+ req.server = server;
+ req.authtime = authtime;
+ req.error_code = errcode;
+
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+
+ rep_data.data = NULL;
+ rep_data.length = 0;
+
+ code = krb5_db_invoke(kdc_context,
+ KRB5_KDB_METHOD_AUDIT_TGS,
+ &req_data,
+ &rep_data);
+ if (code == KRB5_KDB_DBTYPE_NOSUP) {
+ return 0;
+ }
+
+ assert(rep_data.length == 0);
+
+ return code;
+}
+
+krb5_error_code
+validate_transit_path(krb5_context context,
+ krb5_const_principal client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt)
+{
+ /* Incoming */
+ if (isflagset(server->attributes, KRB5_KDB_TRUST_NON_TRANSITIVE)) {
+ return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
+ }
+
+ /* Outgoing */
+ if (isflagset(krbtgt->attributes, KRB5_KDB_TRUST_NON_TRANSITIVE) &&
+ (!krb5_principal_compare(context, server->princ, krbtgt->princ) ||
+ !krb5_realm_compare(context, client, krbtgt->princ))) {
+ return KRB5KDC_ERR_PATH_NOT_ACCEPTED;
+ }
+
+ return 0;
+}
+
+
/* Main logging routines for ticket requests.
There are a few simple cases -- unparseable requests mainly --
@@ -1722,3 +2331,4 @@ log_tgs_alt_tgt(krb5_principal p)
}
/* OpenSolaris: audit_krb5kdc_tgs_req_alt_tgt(...) */
}
+
diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h
index a389955168..0d8e36bfd2 100644
--- a/src/kdc/kdc_util.h
+++ b/src/kdc/kdc_util.h
@@ -31,6 +31,7 @@
#define __KRB5_KDC_UTIL__
#include "kdb.h"
+#include "kdb_ext.h"
typedef struct _krb5_fulladdr {
krb5_address * address;
@@ -38,8 +39,9 @@ typedef struct _krb5_fulladdr {
} krb5_fulladdr;
krb5_error_code check_hot_list (krb5_ticket *);
-krb5_boolean realm_compare (krb5_principal, krb5_principal);
-krb5_boolean krb5_is_tgs_principal (krb5_principal);
+krb5_boolean realm_compare (krb5_const_principal, krb5_const_principal);
+krb5_boolean is_local_principal(krb5_const_principal princ1);
+krb5_boolean krb5_is_tgs_principal (krb5_const_principal);
krb5_error_code add_to_transited (krb5_data *,
krb5_data *,
krb5_principal,
@@ -62,16 +64,22 @@ krb5_error_code kdc_process_tgs_req
const krb5_fulladdr *,
krb5_data *,
krb5_ticket **,
+ krb5_db_entry *krbtgt,
+ int *nprincs,
krb5_keyblock **);
-krb5_error_code kdc_get_server_key (krb5_ticket *,
- krb5_keyblock **,
- krb5_kvno *);
+krb5_error_code kdc_get_server_key (krb5_ticket *, unsigned int,
+ krb5_db_entry *, int *,
+ krb5_keyblock **, krb5_kvno *);
int validate_as_request (krb5_kdc_req *, krb5_db_entry,
krb5_db_entry, krb5_timestamp,
const char **);
+int validate_forwardable(krb5_kdc_req *, krb5_db_entry,
+ krb5_db_entry, krb5_timestamp,
+ const char **);
+
int validate_tgs_request (krb5_kdc_req *, krb5_db_entry,
krb5_ticket *, krb5_timestamp,
const char **);
@@ -164,13 +172,26 @@ krb5_error_code return_padata
krb5_error_code free_padata_context
(krb5_context context, void **padata_context);
+krb5_pa_data *find_pa_data
+ (krb5_pa_data **padata, krb5_preauthtype pa_type);
+
/* kdc_authdata.c */
krb5_error_code load_authdata_plugins(krb5_context context);
krb5_error_code unload_authdata_plugins(krb5_context context);
-krb5_error_code handle_authdata (krb5_context context, krb5_db_entry *client,
- krb5_data *req_pkt, krb5_kdc_req *request,
- krb5_enc_tkt_part *enc_tkt_reply);
+krb5_error_code
+handle_authdata (krb5_context context,
+ unsigned int flags,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_data *req_pkt,
+ krb5_kdc_req *request,
+ krb5_const_principal for_user_princ,
+ krb5_enc_tkt_part *enc_tkt_request,
+ krb5_enc_tkt_part *enc_tkt_reply);
/* replay.c */
krb5_boolean kdc_check_lookaside (krb5_data *, krb5_data **);
@@ -188,6 +209,77 @@ get_principal (krb5_context kcontext,
krb5_const_principal search_for,
krb5_db_entry *entries, int *nentries, krb5_boolean *more);
+krb5_boolean
+include_pac_p(krb5_context context, krb5_kdc_req *request);
+
+krb5_error_code return_svr_referral_data
+ (krb5_context context,
+ krb5_db_entry *server,
+ krb5_enc_kdc_rep_part *reply_encpart);
+
+krb5_error_code sign_db_authdata
+ (krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgs_authdata,
+ krb5_authdata ***ret_authdata,
+ krb5_db_entry *ad_entry,
+ int *ad_nprincs);
+
+krb5_error_code kdc_process_s4u2self_req
+ (krb5_context context,
+ krb5_kdc_req *request,
+ krb5_const_principal client_princ,
+ const krb5_db_entry *server,
+ krb5_keyblock *subkey,
+ krb5_timestamp kdc_time,
+ krb5_pa_for_user **s4u2_req,
+ krb5_db_entry *princ,
+ int *nprincs,
+ const char **status);
+
+krb5_error_code kdc_process_s4u2proxy_req
+ (krb5_context context,
+ krb5_kdc_req *request,
+ const krb5_enc_tkt_part *t2enc,
+ const krb5_db_entry *server,
+ krb5_const_principal server_princ,
+ krb5_const_principal proxy_princ,
+ const char **status);
+
+krb5_error_code kdc_check_transited_list
+ (krb5_context context,
+ const krb5_data *trans,
+ const krb5_data *realm1,
+ const krb5_data *realm2);
+
+krb5_error_code audit_as_request
+ (krb5_kdc_req *request,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code errcode);
+
+krb5_error_code audit_tgs_request
+ (krb5_kdc_req *request,
+ krb5_const_principal client,
+ krb5_db_entry *server,
+ krb5_timestamp authtime,
+ krb5_error_code errcode);
+
+krb5_error_code
+validate_transit_path(krb5_context context,
+ krb5_const_principal client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt);
+
+
void
log_as_req(const krb5_fulladdr *from,
krb5_kdc_req *request, krb5_kdc_rep *reply,
@@ -203,6 +295,7 @@ log_tgs_req(const krb5_fulladdr *from,
void log_tgs_alt_tgt(krb5_principal p);
+
#define isflagset(flagfield, flag) (flagfield & (flag))
#define setflag(flagfield, flag) (flagfield |= (flag))
#define clear(flagfield, flag) (flagfield &= ~(flag))
diff --git a/src/kdc/main.c b/src/kdc/main.c
index fabaca9b2a..247826d4f5 100644
--- a/src/kdc/main.c
+++ b/src/kdc/main.c
@@ -1,7 +1,6 @@
/*
* kdc/main.c
*
- * Portions Copyright (C) 2007 Apple Inc.
* Copyright 1990,2001,2008 by the Massachusetts Institute of Technology.
*
* Export of this software from the United States of America may
@@ -26,6 +25,33 @@
*
* Main procedure body for the KDC server process.
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 <stdio.h>
#include <syslog.h>
@@ -312,7 +338,7 @@ init_realm(char *progname, kdc_realm_t *rdp, char *realm,
*/
seed.length = rdp->realm_mkey.length;
- seed.data = rdp->realm_mkey.contents;
+ seed.data = (char *)rdp->realm_mkey.contents;
if ((kret = krb5_c_random_add_entropy(rdp->realm_context,
KRB5_C_RANDSOURCE_TRUSTEDPARTY, &seed)))
@@ -424,6 +450,10 @@ initialize_realms(krb5_context kcontext, int argc, char **argv)
hierarchy[1] = "kdc_tcp_ports";
if (krb5_aprof_get_string(aprof, hierarchy, TRUE, &default_tcp_ports))
default_tcp_ports = 0;
+ hierarchy[1] = "kdc_max_dgram_reply_size";
+ if (krb5_aprof_get_int32(aprof, hierarchy, TRUE, &max_dgram_reply_size))
+ max_dgram_reply_size = MAX_DGRAM_SIZE;
+
/* aprof_init can return 0 with aprof == NULL */
if (aprof)
krb5_aprof_finish(aprof);
diff --git a/src/kdc/network.c b/src/kdc/network.c
index 6150499649..98f074cce7 100644
--- a/src/kdc/network.c
+++ b/src/kdc/network.c
@@ -527,26 +527,28 @@ setup_tcp_listener_ports(struct socksetup *data)
/* Sockets are created, prepare to listen on them. */
if (s4 >= 0) {
- FD_SET(s4, &sstate.rfds);
- if (s4 >= sstate.max)
- sstate.max = s4 + 1;
if (add_tcp_listener_fd(data, s4) == 0)
close(s4);
- else
+ else {
+ FD_SET(s4, &sstate.rfds);
+ if (s4 >= sstate.max)
+ sstate.max = s4 + 1;
krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
s4, paddr((struct sockaddr *)&sin4));
+ }
}
#ifdef KRB5_USE_INET6
if (s6 >= 0) {
- FD_SET(s6, &sstate.rfds);
- if (s6 >= sstate.max)
- sstate.max = s6 + 1;
if (add_tcp_listener_fd(data, s6) == 0) {
close(s6);
s6 = -1;
- } else
+ } else {
+ FD_SET(s6, &sstate.rfds);
+ if (s6 >= sstate.max)
+ sstate.max = s6 + 1;
krb5_klog_syslog(LOG_INFO, "listening on fd %d: tcp %s",
s6, paddr((struct sockaddr *)&sin6));
+ }
if (s4 < 0)
krb5_klog_syslog(LOG_INFO,
"assuming IPv6 socket accepts IPv4");
@@ -665,9 +667,6 @@ setup_udp_port_1(struct socksetup *data, struct sockaddr *addr,
return 1;
}
}
- FD_SET (sock, &sstate.rfds);
- if (sock >= sstate.max)
- sstate.max = sock + 1;
krb5_klog_syslog (LOG_INFO, "listening on fd %d: udp %s%s", sock,
paddr((struct sockaddr *)addr),
pktinfo ? " (pktinfo)" : "");
@@ -675,6 +674,9 @@ setup_udp_port_1(struct socksetup *data, struct sockaddr *addr,
close(sock);
return 1;
}
+ FD_SET (sock, &sstate.rfds);
+ if (sock >= sstate.max)
+ sstate.max = sock + 1;
}
return 0;
}
@@ -1154,6 +1156,38 @@ send_to_from(int s, void *buf, size_t len, int flags,
#endif
}
+static krb5_error_code
+make_too_big_error (krb5_data **out)
+{
+ krb5_error errpkt;
+ krb5_error_code retval;
+ krb5_data *scratch;
+
+ memset(&errpkt, 0, sizeof(errpkt));
+
+ retval = krb5_us_timeofday(kdc_context, &errpkt.stime, &errpkt.susec);
+ if (retval)
+ return retval;
+ errpkt.error = KRB_ERR_RESPONSE_TOO_BIG;
+ errpkt.server = tgs_server;
+ errpkt.client = NULL;
+ errpkt.text.length = 0;
+ errpkt.text.data = 0;
+ errpkt.e_data.length = 0;
+ errpkt.e_data.data = 0;
+ scratch = malloc(sizeof(*scratch));
+ if (scratch == NULL)
+ return ENOMEM;
+ retval = krb5_mk_error(kdc_context, &errpkt, scratch);
+ if (retval) {
+ free(scratch);
+ return retval;
+ }
+
+ *out = scratch;
+ return 0;
+}
+
static void process_packet(struct connection *conn, const char *prog,
int selflags)
{
@@ -1208,6 +1242,16 @@ static void process_packet(struct connection *conn, const char *prog,
}
if (response == NULL)
return;
+ if (response->length > max_dgram_reply_size) {
+ krb5_free_data(kdc_context, response);
+ retval = make_too_big_error(&response);
+ if (retval) {
+ krb5_klog_syslog(LOG_ERR,
+ "error constructing KRB_ERR_RESPONSE_TOO_BIG error: %s",
+ error_message(retval));
+ return;
+ }
+ }
cc = send_to_from(port_fd, response->data, (socklen_t) response->length, 0,
(struct sockaddr *)&saddr, saddr_len,
(struct sockaddr *)&daddr, daddr_len);
@@ -1554,7 +1598,13 @@ listen_and_process(const char *prog)
while (!signal_requests_exit) {
if (signal_requests_hup) {
+ int k;
+
krb5_klog_reopen(kdc_context);
+ for (k = 0; k < kdc_numrealms; k++)
+ krb5_db_invoke(kdc_realmlist[k]->realm_context,
+ KRB5_KDB_METHOD_REFRESH_POLICY,
+ NULL, NULL);
signal_requests_hup = 0;
}
diff --git a/src/kdc/policy.c b/src/kdc/policy.c
index 8c0b692724..58b26f73d0 100644
--- a/src/kdc/policy.c
+++ b/src/kdc/policy.c
@@ -25,15 +25,49 @@
*
* Policy decision routines for KDC.
*/
+/*
+ * Copyright (c) 2006-2008, Novell, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * The copyright holder's name is not used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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 "k5-int.h"
#include "kdc_util.h"
+#include "extern.h"
int
against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
krb5_db_entry server, krb5_timestamp kdc_time,
const char **status)
{
+ krb5_error_code code;
+ kdb_check_policy_as_req req;
+ kdb_check_policy_as_rep rep;
+ krb5_data req_data;
+ krb5_data rep_data;
+
#if 0
/* An AS request must include the addresses field */
if (request->addresses == 0) {
@@ -41,8 +75,37 @@ against_local_policy_as(register krb5_kdc_req *request, krb5_db_entry client,
return KRB5KDC_ERR_POLICY;
}
#endif
-
- return 0; /* not against policy */
+
+ memset(&req, 0, sizeof(req));
+ memset(&rep, 0, sizeof(rep));
+
+ req.request = request;
+ req.client = &client;
+ req.server = &server;
+ req.kdc_time = kdc_time;
+
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+
+ rep_data.data = (void *)&rep;
+ rep_data.length = sizeof(rep);
+
+ code = krb5_db_invoke(kdc_context,
+ KRB5_KDB_METHOD_CHECK_POLICY_AS,
+ &req_data,
+ &rep_data);
+ if (code == KRB5_KDB_DBTYPE_NOSUP)
+ return 0;
+
+ *status = rep.status;
+
+ if (code != 0) {
+ code -= ERROR_TABLE_BASE_krb5;
+ if (code < 0 || code > 128)
+ code = KRB_ERR_GENERIC;
+ }
+
+ return code;
}
/*
@@ -52,6 +115,12 @@ krb5_error_code
against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server,
krb5_ticket *ticket, const char **status)
{
+ krb5_error_code code;
+ kdb_check_policy_tgs_req req;
+ kdb_check_policy_tgs_rep rep;
+ krb5_data req_data;
+ krb5_data rep_data;
+
#if 0
/*
* For example, if your site wants to disallow ticket forwarding,
@@ -63,13 +132,35 @@ against_local_policy_tgs(register krb5_kdc_req *request, krb5_db_entry server,
return KRB5KDC_ERR_POLICY;
}
#endif
-
- return 0; /* not against policy */
-}
+ memset(&req, 0, sizeof(req));
+ memset(&rep, 0, sizeof(rep));
+
+ req.request = request;
+ req.server = &server;
+ req.ticket = ticket;
+ req_data.data = (void *)&req;
+ req_data.length = sizeof(req);
+ rep_data.data = (void *)&rep;
+ rep_data.length = sizeof(rep);
+ code = krb5_db_invoke(kdc_context,
+ KRB5_KDB_METHOD_CHECK_POLICY_TGS,
+ &req_data,
+ &rep_data);
+ if (code == KRB5_KDB_DBTYPE_NOSUP)
+ return 0;
+ *status = rep.status;
+ if (code != 0) {
+ code -= ERROR_TABLE_BASE_krb5;
+ if (code < 0 || code > 128)
+ code = KRB_ERR_GENERIC;
+ }
+
+ return code;
+}