diff options
Diffstat (limited to 'src/kdc')
-rw-r--r-- | src/kdc/Makefile.in | 60 | ||||
-rw-r--r-- | src/kdc/do_as_req.c | 254 | ||||
-rw-r--r-- | src/kdc/do_tgs_req.c | 422 | ||||
-rw-r--r-- | src/kdc/extern.c | 1 | ||||
-rw-r--r-- | src/kdc/extern.h | 1 | ||||
-rw-r--r-- | src/kdc/kdc_authdata.c | 539 | ||||
-rw-r--r-- | src/kdc/kdc_preauth.c | 211 | ||||
-rw-r--r-- | src/kdc/kdc_util.c | 732 | ||||
-rw-r--r-- | src/kdc/kdc_util.h | 109 | ||||
-rw-r--r-- | src/kdc/main.c | 34 | ||||
-rw-r--r-- | src/kdc/network.c | 72 | ||||
-rw-r--r-- | src/kdc/policy.c | 101 |
12 files changed, 2134 insertions, 402 deletions
diff --git a/src/kdc/Makefile.in b/src/kdc/Makefile.in index c7ecf4bfc..b346c4627 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 8c65a637b..36b550250 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 3af539feb..381269880 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 fa3e0af43..2a2c1ae22 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 265053695..0da6b14fb 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 9fd37f2d6..315269c2a 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 c7655ab0d..51c16739d 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 4068ec622..a3628cf91 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 a38995516..0d8e36bfd 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 fabaca9b2..247826d4f 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 615049964..98f074cce 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 8c0b69272..58b26f73d 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; +} |