diff --git a/Add-three-kvno-options-from-Heimdal-kgetcred.patch b/Add-three-kvno-options-from-Heimdal-kgetcred.patch new file mode 100644 index 0000000..40a6318 --- /dev/null +++ b/Add-three-kvno-options-from-Heimdal-kgetcred.patch @@ -0,0 +1,404 @@ +From 538d787aa7c10894cc0426f54db0d8248efcf7c9 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Wed, 17 Jun 2020 20:48:38 -0400 +Subject: [PATCH] Add three kvno options from Heimdal kgetcred + +Add the flags --cached-only and --no-store, which pass the +corresponding options to krb5_get_credentials(). Add the option +--out-cache to write the retrieved credentials to a specified output +cache. + +Add a Python test script for kvno command-line options, including +tests for the new options. + +ticket: 8917 (new) +(cherry picked from commit 876bab8418d7dd134c9d9db812ee2118d5ad58f0) +--- + doc/user/user_commands/kvno.rst | 13 ++++ + src/clients/kvno/Makefile.in | 3 + + src/clients/kvno/kvno.c | 115 +++++++++++++++++++++++--------- + src/clients/kvno/t_kvno.py | 75 +++++++++++++++++++++ + src/man/kvno.man | 13 ++++ + 5 files changed, 187 insertions(+), 32 deletions(-) + create mode 100644 src/clients/kvno/t_kvno.py + +diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst +index 3892f0ca5..718313576 100644 +--- a/doc/user/user_commands/kvno.rst ++++ b/doc/user/user_commands/kvno.rst +@@ -74,6 +74,19 @@ OPTIONS + client principal with the X.509 certificate in *cert_file*. The + certificate file must be in PEM format. + ++**--cached-only** ++ Only retrieve credentials already present in the cache, not from ++ the KDC. ++ ++**--no-store** ++ Do not store retrieved credentials in the cache. If ++ **--out-cache** is also specified, credentials will still be ++ stored into the output credential cache. ++ ++**--out-cache** *ccache* ++ Initialize *ccache* and store all retrieved credentials into it. ++ Do not store acquired credentials in the input cache. ++ + **--u2u** *ccache* + Requests a user-to-user ticket. *ccache* must contain a local + krbtgt ticket for the server principal. The reported version +diff --git a/src/clients/kvno/Makefile.in b/src/clients/kvno/Makefile.in +index 1c3f79392..5ba877271 100644 +--- a/src/clients/kvno/Makefile.in ++++ b/src/clients/kvno/Makefile.in +@@ -26,6 +26,9 @@ kvno: kvno.o $(KRB5_BASE_DEPLIBS) + ##WIN32## link $(EXE_LINKOPTS) /out:$@ $** + ##WIN32## $(_VC_MANIFEST_EMBED_EXE) + ++check-pytests: kvno ++ $(RUNPYTEST) $(srcdir)/t_kvno.py $(PYTESTFLAGS) ++ + clean-unix:: + $(RM) kvno.o kvno + +diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c +index 2472c0cfe..9d85864f6 100644 +--- a/src/clients/kvno/kvno.c ++++ b/src/clients/kvno/kvno.c +@@ -44,14 +44,17 @@ xusage() + fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog); + fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | " + "[-F cert_file] [-P]]\n")); +- fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n")); ++ fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] " ++ "[--u2u ccache]\n")); ++ fprintf(stderr, _("\tservice1 service2 ...\n")); + exit(1); + } + + static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr, +- char *keytab_name, char *sname, int canon, int unknown, +- char *for_user, int for_user_enterprise, +- char *for_user_cert_file, int proxy, ++ char *keytab_name, char *sname, int cached_only, ++ int canon, int no_store, int unknown, char *for_user, ++ int for_user_enterprise, char *for_user_cert_file, ++ int proxy, const char *out_ccname, + const char *u2u_ccname); + + #include +@@ -61,18 +64,21 @@ static void extended_com_err_fn(const char *myprog, errcode_t code, + int + main(int argc, char *argv[]) + { +- enum { OPTION_U2U = 256 }; +- struct option lopts[] = { +- { "u2u", 1, NULL, OPTION_U2U }, +- { NULL, 0, NULL, 0 } +- }; ++ enum { OPTION_U2U = 256, OPTION_OUT_CACHE = 257 }; + const char *shopts = "uCc:e:hk:qPS:I:U:F:"; + int option; + char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL; + char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL; +- char *for_user_cert_file = NULL; ++ char *for_user_cert_file = NULL, *out_ccname = NULL; + int canon = 0, unknown = 0, proxy = 0, for_user_enterprise = 0; +- int impersonate = 0; ++ int impersonate = 0, cached_only = 0, no_store = 0; ++ struct option lopts[] = { ++ { "cached-only", 0, &cached_only, 1 }, ++ { "no-store", 0, &no_store, 1 }, ++ { "out-cache", 1, NULL, OPTION_OUT_CACHE }, ++ { "u2u", 1, NULL, OPTION_U2U }, ++ { NULL, 0, NULL, 0 } ++ }; + + setlocale(LC_ALL, ""); + set_com_err_hook(extended_com_err_fn); +@@ -135,6 +141,12 @@ main(int argc, char *argv[]) + case OPTION_U2U: + u2u_ccname = optarg; + break; ++ case OPTION_OUT_CACHE: ++ out_ccname = optarg; ++ break; ++ case 0: ++ /* If this option set a flag, do nothing else now. */ ++ break; + default: + xusage(); + break; +@@ -159,8 +171,9 @@ main(int argc, char *argv[]) + xusage(); + + do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name, +- sname, canon, unknown, for_user, for_user_enterprise, +- for_user_cert_file, proxy, u2u_ccname); ++ sname, cached_only, canon, no_store, unknown, for_user, ++ for_user_enterprise, for_user_cert_file, proxy, out_ccname, ++ u2u_ccname); + return 0; + } + +@@ -274,14 +287,16 @@ static krb5_error_code + kvno(const char *name, krb5_ccache ccache, krb5_principal me, + krb5_enctype etype, krb5_keytab keytab, const char *sname, + krb5_flags options, int unknown, krb5_principal for_user_princ, +- krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket) ++ krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket, ++ krb5_creds **creds_out) + { + krb5_error_code ret; + krb5_principal server = NULL; + krb5_ticket *ticket = NULL; +- krb5_creds in_creds, *out_creds = NULL; ++ krb5_creds in_creds, *creds = NULL; + char *princ = NULL; + ++ *creds_out = NULL; + memset(&in_creds, 0, sizeof(in_creds)); + + if (sname != NULL) { +@@ -321,13 +336,12 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + in_creds.client = for_user_princ; + in_creds.server = me; + ret = krb5_get_credentials_for_user(context, options, ccache, +- &in_creds, for_user_cert, +- &out_creds); ++ &in_creds, for_user_cert, &creds); + } else { + in_creds.client = me; + in_creds.server = server; + ret = krb5_get_credentials(context, options, ccache, &in_creds, +- &out_creds); ++ &creds); + } + + if (ret) { +@@ -336,7 +350,7 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + + /* We need a native ticket. */ +- ret = krb5_decode_ticket(&out_creds->ticket, &ticket); ++ ret = krb5_decode_ticket(&creds->ticket, &ticket); + if (ret) { + com_err(prog, ret, _("while decoding ticket for %s"), princ); + goto cleanup; +@@ -362,15 +376,15 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + + if (proxy) { +- in_creds.client = out_creds->client; +- out_creds->client = NULL; +- krb5_free_creds(context, out_creds); +- out_creds = NULL; ++ in_creds.client = creds->client; ++ creds->client = NULL; ++ krb5_free_creds(context, creds); ++ creds = NULL; + in_creds.server = server; + + ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE, + ccache, &in_creds, ticket, +- &out_creds); ++ &creds); + krb5_free_principal(context, in_creds.client); + if (ret) { + com_err(prog, ret, _("%s: constrained delegation failed"), +@@ -379,10 +393,13 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + } + ++ *creds_out = creds; ++ creds = NULL; ++ + cleanup: + krb5_free_principal(context, server); + krb5_free_ticket(context, ticket); +- krb5_free_creds(context, out_creds); ++ krb5_free_creds(context, creds); + krb5_free_unparsed_name(context, princ); + return ret; + } +@@ -428,19 +445,28 @@ cleanup: + + static void + do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, +- char *keytab_name, char *sname, int canon, int unknown, +- char *for_user, int for_user_enterprise, +- char *for_user_cert_file, int proxy, const char *u2u_ccname) ++ char *keytab_name, char *sname, int cached_only, int canon, ++ int no_store, int unknown, char *for_user, int for_user_enterprise, ++ char *for_user_cert_file, int proxy, const char *out_ccname, ++ const char *u2u_ccname) + { + krb5_error_code ret; +- int i, errors, flags; ++ int i, errors, flags, initialized = 0; + krb5_enctype etype; +- krb5_ccache ccache; ++ krb5_ccache ccache, out_ccache = NULL; + krb5_principal me; + krb5_keytab keytab = NULL; + krb5_principal for_user_princ = NULL; +- krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0; ++ krb5_flags options = 0; + krb5_data cert_data = empty_data(), *user_cert = NULL, *u2u_ticket = NULL; ++ krb5_creds *creds; ++ ++ if (canon) ++ options |= KRB5_GC_CANONICALIZE; ++ if (cached_only) ++ options |= KRB5_GC_CACHED; ++ if (no_store || out_ccname != NULL) ++ options |= KRB5_GC_NO_STORE; + + ret = krb5_init_context(&context); + if (ret) { +@@ -467,6 +493,14 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, + exit(1); + } + ++ if (out_ccname != NULL) { ++ ret = krb5_cc_resolve(context, out_ccname, &out_ccache); ++ if (ret) { ++ com_err(prog, ret, _("while resolving output ccache")); ++ exit(1); ++ } ++ } ++ + if (keytab_name != NULL) { + ret = krb5_kt_resolve(context, keytab_name, &keytab); + if (ret) { +@@ -513,8 +547,25 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, + errors = 0; + for (i = 0; i < count; i++) { + if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown, +- for_user_princ, user_cert, proxy, u2u_ticket) != 0) ++ for_user_princ, user_cert, proxy, u2u_ticket, &creds) != 0) { + errors++; ++ } else if (out_ccache != NULL) { ++ if (!initialized) { ++ ret = krb5_cc_initialize(context, out_ccache, creds->client); ++ if (ret) { ++ com_err(prog, ret, _("while initializing output ccache")); ++ exit(1); ++ } ++ initialized = 1; ++ } ++ ret = krb5_cc_store_cred(context, out_ccache, creds); ++ if (ret) { ++ com_err(prog, ret, _("while storing creds in output ccache")); ++ exit(1); ++ } ++ } ++ ++ krb5_free_creds(context, creds); + } + + if (keytab != NULL) +diff --git a/src/clients/kvno/t_kvno.py b/src/clients/kvno/t_kvno.py +new file mode 100644 +index 000000000..e98b90e8a +--- /dev/null ++++ b/src/clients/kvno/t_kvno.py +@@ -0,0 +1,75 @@ ++from k5test import * ++ ++realm = K5Realm() ++ ++def check_cache(ccache, expected_services): ++ # Fetch the klist output and skip past the header. ++ lines = realm.run([klist, '-c', ccache]).splitlines() ++ lines = lines[4:] ++ ++ # For each line not beginning with an indent, match against the ++ # expected service principals. ++ svcs = {x: True for x in expected_services} ++ for l in lines: ++ if not l.startswith('\t'): ++ svcprinc = l.split()[4] ++ if svcprinc in svcs: ++ del svcs[svcprinc] ++ else: ++ fail('unexpected service princ ' + svcprinc) ++ ++ if svcs: ++ fail('services not found in klist output: ' + ' '.join(svcs.keys())) ++ ++ ++mark('no options') ++realm.run([kvno, realm.user_princ], expected_msg='user@KRBTEST.COM: kvno = 1') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('-e') ++msgs = ('etypes requested in TGS request: camellia128-cts', ++ '/KDC has no support for encryption type') ++realm.run([kvno, '-e', 'camellia128-cts', realm.host_princ], ++ expected_code=1, expected_trace=msgs) ++ ++mark('--cached-only') ++realm.run([kvno, '--cached-only', realm.user_princ], expected_msg='kvno = 1') ++realm.run([kvno, '--cached-only', realm.host_princ], ++ expected_code=1, expected_msg='Matching credential not found') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('--no-store') ++realm.run([kvno, '--no-store', realm.host_princ], expected_msg='kvno = 1') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('--out-cache') # and multiple services ++out_ccache = os.path.join(realm.testdir, 'ccache.out') ++realm.run([kvno, '--out-cache', out_ccache, ++ realm.host_princ, realm.admin_princ]) ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++check_cache(out_ccache, [realm.host_princ, realm.admin_princ]) ++ ++mark('--out-cache --cached-only') # tests out-cache overwriting, and -q ++realm.run([kvno, '--out-cache', out_ccache, '--cached-only', realm.host_princ], ++ expected_code=1, expected_msg='Matching credential not found') ++out = realm.run([kvno, '-q', '--out-cache', out_ccache, '--cached-only', ++ realm.user_princ]) ++if out: ++ fail('unexpected kvno output with -q') ++check_cache(out_ccache, [realm.user_princ]) ++ ++mark('-U') # and -c ++svc_ccache = os.path.join(realm.testdir, 'ccache.svc') ++realm.run([kinit, '-k', '-c', svc_ccache, realm.host_princ]) ++realm.run([kvno, '-c', svc_ccache, '-U', 'user', realm.host_princ]) ++realm.run([klist, '-c', svc_ccache], expected_msg='for client user@') ++realm.run([kvno, '-c', svc_ccache, '-U', 'user', '--out-cache', out_ccache, ++ realm.host_princ]) ++out = realm.run([klist, '-c', out_ccache]) ++if ('Default principal: user@KRBTEST.COM' not in out): ++ fail('wrong default principal in klist output') ++ ++# More S4U options are tested in tests/gssapi/t_s4u.py. ++# --u2u is tested in tests/t_u2u.py. ++ ++success('kvno tests') +diff --git a/src/man/kvno.man b/src/man/kvno.man +index 005a2ec97..b9f6739eb 100644 +--- a/src/man/kvno.man ++++ b/src/man/kvno.man +@@ -95,6 +95,19 @@ Specifies that protocol transition is to be used, identifying the + client principal with the X.509 certificate in \fIcert_file\fP\&. The + certificate file must be in PEM format. + .TP ++\fB\-\-cached\-only\fP ++Only retrieve credentials already present in the cache, not from ++the KDC. ++.TP ++\fB\-\-no\-store\fP ++Do not store retrieved credentials in the cache. If ++\fB\-\-out\-cache\fP is also specified, credentials will still be ++stored into the output credential cache. ++.TP ++\fB\-\-out\-cache\fP \fIccache\fP ++Initialize \fIccache\fP and store all retrieved credentials into it. ++Do not store acquired credentials in the input cache. ++.TP + \fB\-\-u2u\fP \fIccache\fP + Requests a user\-to\-user ticket. \fIccache\fP must contain a local + krbtgt ticket for the server principal. The reported version diff --git a/Adjust-KDC-alias-helper-function-contract.patch b/Adjust-KDC-alias-helper-function-contract.patch new file mode 100644 index 0000000..7b7c62b --- /dev/null +++ b/Adjust-KDC-alias-helper-function-contract.patch @@ -0,0 +1,80 @@ +From 758f5031fe9d6c1e3eb33818bc6d57cf8b4a3a72 Mon Sep 17 00:00:00 2001 +From: Isaac Boukris +Date: Tue, 22 Sep 2020 01:11:39 +0300 +Subject: [PATCH] Adjust KDC alias helper function contract + +Change the name of is_client_alias() to is_client_db_alias(), and +change the contract so that the already-canonical principal name comes +from a DB entry (which is less flexible, but clearer since DB entries +always contain canonical principal names). Make the function +available outside of kdc_util.c. + +[ghudson@mit.edu: clarified commit message] + +(cherry picked from commit 9fb5f572dd6ce808b234cb60a573eac48136d7ca) +--- + src/kdc/kdc_util.c | 14 +++++++------- + src/kdc/kdc_util.h | 4 ++++ + 2 files changed, 11 insertions(+), 7 deletions(-) + +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index dcb2df8dc..6330387d0 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -1463,10 +1463,10 @@ cleanup: + return code; + } + +-/* Return true if princ canonicalizes to the same principal as canon. */ +-static krb5_boolean +-is_client_alias(krb5_context context, krb5_const_principal canon, +- krb5_const_principal princ) ++/* Return true if princ canonicalizes to the same principal as entry's. */ ++krb5_boolean ++is_client_db_alias(krb5_context context, const krb5_db_entry *entry, ++ krb5_const_principal princ) + { + krb5_error_code ret; + krb5_db_entry *self; +@@ -1475,7 +1475,7 @@ is_client_alias(krb5_context context, krb5_const_principal canon, + ret = krb5_db_get_principal(context, princ, + KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, &self); + if (!ret) { +- is_self = krb5_principal_compare(context, canon, self->princ); ++ is_self = krb5_principal_compare(context, entry->princ, self->princ); + krb5_db_free_principal(context, self); + } + +@@ -1535,7 +1535,7 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm, + + /* If the server is local, check that the request is for self. */ + if (!isflagset(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL) && +- !is_client_alias(kdc_context, server->princ, client_princ)) { ++ !is_client_db_alias(kdc_context, server, client_princ)) { + *status = "INVALID_S4U2SELF_REQUEST_SERVER_MISMATCH"; + return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */ + } +@@ -1728,7 +1728,7 @@ kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags, + } + + client_princ = *stkt_authdata_client; +- } else if (!is_client_alias(kdc_context, server->princ, server_princ)) { ++ } else if (!is_client_db_alias(kdc_context, server, server_princ)) { + *status = "EVIDENCE_TICKET_MISMATCH"; + return KRB5KDC_ERR_SERVER_NOMATCH; + } +diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h +index 384b21ad2..2c9d8cf69 100644 +--- a/src/kdc/kdc_util.h ++++ b/src/kdc/kdc_util.h +@@ -344,6 +344,10 @@ log_tgs_badtrans(krb5_context ctx, krb5_principal cprinc, + void + log_tgs_alt_tgt(krb5_context context, krb5_principal p); + ++krb5_boolean ++is_client_db_alias(krb5_context context, const krb5_db_entry *entry, ++ krb5_const_principal princ); ++ + /* FAST*/ + enum krb5_fast_kdc_flags { + KRB5_FAST_REPLY_KEY_USED = 0x1, diff --git a/Allow-aliases-when-matching-U2U-second-ticket.patch b/Allow-aliases-when-matching-U2U-second-ticket.patch new file mode 100644 index 0000000..8622ff8 --- /dev/null +++ b/Allow-aliases-when-matching-U2U-second-ticket.patch @@ -0,0 +1,65 @@ +From ccc5b9663e229f20421c01836aa5ecb06f1f2a48 Mon Sep 17 00:00:00 2001 +From: Isaac Boukris +Date: Tue, 22 Sep 2020 01:17:11 +0300 +Subject: [PATCH] Allow aliases when matching U2U second ticket + +In process_tgs_req() when verifying the user-to-user second ticket, +compare the canonical names of the request server and the second +ticket client. + +[ghudson@mit.edu: expanded commit message; trimmed tests] + +ticket: 8951 (new) +(cherry picked from commit afc494ef9418e6be7fbb887364efa6606b10034a) +--- + src/kdc/do_tgs_req.c | 2 +- + src/tests/t_u2u.py | 25 +++++++++++++++++++++++++ + 2 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c +index 463a9c0dd..74cd19e96 100644 +--- a/src/kdc/do_tgs_req.c ++++ b/src/kdc/do_tgs_req.c +@@ -666,7 +666,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + */ + krb5_enc_tkt_part *t2enc = request->second_ticket[st_idx]->enc_part2; + krb5_principal client2 = t2enc->client; +- if (!krb5_principal_compare(kdc_context, request->server, client2)) { ++ if (!is_client_db_alias(kdc_context, server, client2)) { + altcprinc = client2; + errcode = KRB5KDC_ERR_SERVER_NOMATCH; + status = "2ND_TKT_MISMATCH"; +diff --git a/src/tests/t_u2u.py b/src/tests/t_u2u.py +index 1ca6ac87e..4b8a82a2f 100644 +--- a/src/tests/t_u2u.py ++++ b/src/tests/t_u2u.py +@@ -32,4 +32,29 @@ realm.run([kvno, '--u2u', realm.ccache, realm.user_princ]) + + realm.run([klist]) + ++realm.stop() ++ ++# Load the test KDB module to test aliases ++testprincs = {'krbtgt/KRBTEST.COM': {'keys': 'aes128-cts'}, ++ 'user': {'keys': 'aes128-cts', 'flags': '+preauth'}, ++ 'WIN10': {'keys': 'aes128-cts'}} ++kdcconf = {'realms': {'$realm': {'database_module': 'test'}}, ++ 'dbmodules': {'test': {'db_library': 'test', ++ 'princs': testprincs, ++ 'alias': {'HOST/win10': 'WIN10'}}}} ++ ++realm = K5Realm(kdc_conf=kdcconf, create_kdb=False) ++realm.start_kdc() ++ ++# Create a second user principal and get tickets for it. ++u2u_ccache = 'FILE:' + os.path.join(realm.testdir, 'ccu2u') ++realm.extract_keytab('WIN10', realm.keytab) ++realm.kinit('WIN10', None, ['-k', '-c', u2u_ccache]) ++ ++realm.extract_keytab(realm.user_princ, realm.keytab) ++realm.kinit(realm.user_princ, None, ['-k']) ++ ++realm.run([kvno, '--u2u', u2u_ccache, 'HOST/win10'], expected_msg='kvno = 0') ++realm.run([kvno, '--u2u', u2u_ccache, 'WIN10'], expected_msg='kvno = 0') ++ + success('user-to-user tests') diff --git a/Avoid-passing-DB-entry-structures-in-KDC.patch b/Avoid-passing-DB-entry-structures-in-KDC.patch new file mode 100644 index 0000000..e5cff1a --- /dev/null +++ b/Avoid-passing-DB-entry-structures-in-KDC.patch @@ -0,0 +1,298 @@ +From dd8b146093d4bdf8a7d0c0eb8156b62d090448d7 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Wed, 30 Sep 2020 02:12:00 -0400 +Subject: [PATCH] Avoid passing DB entry structures in KDC + +When validating AS or TGS requests, pass pointers to DB entry +structures, not the structures themselves. + +(cherry picked from commit 7ccc08a889b40693b2ce7f108f2cdda51bc04bff) +--- + src/kdc/do_as_req.c | 4 ++-- + src/kdc/do_tgs_req.c | 2 +- + src/kdc/kdc_util.c | 34 +++++++++++++++++----------------- + src/kdc/kdc_util.h | 6 +++--- + src/kdc/tgs_policy.c | 35 ++++++++++++++++++----------------- + 5 files changed, 41 insertions(+), 40 deletions(-) + +diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c +index 9ae7b0a5e..c2dfea9b8 100644 +--- a/src/kdc/do_as_req.c ++++ b/src/kdc/do_as_req.c +@@ -663,8 +663,8 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, + au_state->stage = VALIDATE_POL; + + if ((errcode = validate_as_request(kdc_active_realm, +- state->request, *state->client, +- *state->server, state->kdc_time, ++ state->request, state->client, ++ state->server, state->kdc_time, + &state->status, &state->e_data))) { + errcode += ERROR_TABLE_BASE_krb5; + goto errout; +diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c +index 74cd19e96..d345797c4 100644 +--- a/src/kdc/do_tgs_req.c ++++ b/src/kdc/do_tgs_req.c +@@ -260,7 +260,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + goto cleanup; + + if ((retval = validate_tgs_request(kdc_active_realm, +- request, *server, header_ticket, ++ request, server, header_ticket, + kdc_time, &status, &e_data))) { + if (retval == KDC_ERR_POLICY || retval == KDC_ERR_BADOPTION) + au_state->violation = PROT_CONSTRAINT; +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index a4a05b9fa..b2042862a 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -612,8 +612,8 @@ check_anon(kdc_realm_t *kdc_active_realm, + KDC_OPT_ENC_TKT_IN_SKEY | KDC_OPT_CNAME_IN_ADDL_TKT) + int + validate_as_request(kdc_realm_t *kdc_active_realm, +- krb5_kdc_req *request, krb5_db_entry client, +- krb5_db_entry server, krb5_timestamp kdc_time, ++ krb5_kdc_req *request, krb5_db_entry *client, ++ krb5_db_entry *server, krb5_timestamp kdc_time, + const char **status, krb5_pa_data ***e_data) + { + krb5_error_code ret; +@@ -627,7 +627,7 @@ validate_as_request(kdc_realm_t *kdc_active_realm, + } + + /* The client must not be expired */ +- if (client.expiration && ts_after(kdc_time, client.expiration)) { ++ if (client->expiration && ts_after(kdc_time, client->expiration)) { + *status = "CLIENT EXPIRED"; + if (vague_errors) + return(KRB_ERR_GENERIC); +@@ -637,8 +637,8 @@ validate_as_request(kdc_realm_t *kdc_active_realm, + + /* The client's password must not be expired, unless the server is + a KRB5_KDC_PWCHANGE_SERVICE. */ +- if (client.pw_expiration && ts_after(kdc_time, client.pw_expiration) && +- !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { ++ if (client->pw_expiration && ts_after(kdc_time, client->pw_expiration) && ++ !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) { + *status = "CLIENT KEY EXPIRED"; + if (vague_errors) + return(KRB_ERR_GENERIC); +@@ -647,7 +647,7 @@ validate_as_request(kdc_realm_t *kdc_active_realm, + } + + /* The server must not be expired */ +- if (server.expiration && ts_after(kdc_time, server.expiration)) { ++ if (server->expiration && ts_after(kdc_time, server->expiration)) { + *status = "SERVICE EXPIRED"; + return(KDC_ERR_SERVICE_EXP); + } +@@ -656,8 +656,8 @@ validate_as_request(kdc_realm_t *kdc_active_realm, + * If the client requires password changing, then only allow the + * pwchange service. + */ +- if (isflagset(client.attributes, KRB5_KDB_REQUIRES_PWCHANGE) && +- !isflagset(server.attributes, KRB5_KDB_PWCHANGE_SERVICE)) { ++ if (isflagset(client->attributes, KRB5_KDB_REQUIRES_PWCHANGE) && ++ !isflagset(server->attributes, KRB5_KDB_PWCHANGE_SERVICE)) { + *status = "REQUIRED PWCHANGE"; + return(KDC_ERR_KEY_EXP); + } +@@ -665,37 +665,37 @@ validate_as_request(kdc_realm_t *kdc_active_realm, + /* Client and server must allow postdating tickets */ + if ((isflagset(request->kdc_options, KDC_OPT_ALLOW_POSTDATE) || + isflagset(request->kdc_options, KDC_OPT_POSTDATED)) && +- (isflagset(client.attributes, KRB5_KDB_DISALLOW_POSTDATED) || +- isflagset(server.attributes, KRB5_KDB_DISALLOW_POSTDATED))) { ++ (isflagset(client->attributes, KRB5_KDB_DISALLOW_POSTDATED) || ++ isflagset(server->attributes, KRB5_KDB_DISALLOW_POSTDATED))) { + *status = "POSTDATE NOT ALLOWED"; + return(KDC_ERR_CANNOT_POSTDATE); + } + + /* Check to see if client is locked out */ +- if (isflagset(client.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { ++ if (isflagset(client->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { + *status = "CLIENT LOCKED OUT"; + return(KDC_ERR_CLIENT_REVOKED); + } + + /* Check to see if server is locked out */ +- if (isflagset(server.attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { ++ if (isflagset(server->attributes, KRB5_KDB_DISALLOW_ALL_TIX)) { + *status = "SERVICE LOCKED OUT"; + return(KDC_ERR_S_PRINCIPAL_UNKNOWN); + } + + /* Check to see if server is allowed to be a service */ +- if (isflagset(server.attributes, KRB5_KDB_DISALLOW_SVR)) { ++ if (isflagset(server->attributes, KRB5_KDB_DISALLOW_SVR)) { + *status = "SERVICE NOT ALLOWED"; + return(KDC_ERR_MUST_USE_USER2USER); + } + +- if (check_anon(kdc_active_realm, client.princ, request->server) != 0) { ++ if (check_anon(kdc_active_realm, client->princ, request->server) != 0) { + *status = "ANONYMOUS NOT ALLOWED"; + return(KDC_ERR_POLICY); + } + + /* Perform KDB module policy checks. */ +- ret = krb5_db_check_policy_as(kdc_context, request, &client, &server, ++ ret = krb5_db_check_policy_as(kdc_context, request, client, server, + kdc_time, status, e_data); + if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP) + return errcode_to_protocol(ret); +@@ -1568,8 +1568,8 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm, + princ->pw_expiration = 0; + clear(princ->attributes, KRB5_KDB_REQUIRES_PWCHANGE); + +- code = validate_as_request(kdc_active_realm, request, *princ, +- no_server, kdc_time, status, &e_data); ++ code = validate_as_request(kdc_active_realm, request, princ, ++ &no_server, kdc_time, status, &e_data); + if (code) { + krb5_db_free_principal(kdc_context, princ); + krb5_free_pa_data(kdc_context, e_data); +diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h +index 42b7ee208..04007a8f5 100644 +--- a/src/kdc/kdc_util.h ++++ b/src/kdc/kdc_util.h +@@ -76,12 +76,12 @@ get_local_tgt(krb5_context context, const krb5_data *realm, + krb5_db_entry **storage_out, krb5_keyblock *kb_out); + + int +-validate_as_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry, +- krb5_db_entry, krb5_timestamp, ++validate_as_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *, ++ krb5_db_entry *, krb5_timestamp, + const char **, krb5_pa_data ***); + + int +-validate_tgs_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry, ++validate_tgs_request (kdc_realm_t *, krb5_kdc_req *, krb5_db_entry *, + krb5_ticket *, krb5_timestamp, + const char **, krb5_pa_data ***); + +diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c +index 554345ba5..3f4fa8499 100644 +--- a/src/kdc/tgs_policy.c ++++ b/src/kdc/tgs_policy.c +@@ -48,7 +48,7 @@ struct tgsflagrule { + }; + + /* Service principal TGS policy checking functions */ +-typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry, ++typedef int (check_tgs_svc_pol_fn)(krb5_kdc_req *, krb5_db_entry *, + krb5_ticket *, krb5_timestamp, + const char **); + +@@ -110,7 +110,7 @@ static const struct tgsflagrule svcdenyrules[] = { + * A service principal can forbid some TGS-REQ options. + */ + static int +-check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry server, ++check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry *server, + krb5_ticket *tkt, krb5_timestamp kdc_time, + const char **status) + { +@@ -122,7 +122,7 @@ check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry server, + r = &svcdenyrules[i]; + if (!(r->reqflags & req->kdc_options)) + continue; +- if (r->checkflag & server.attributes) { ++ if (r->checkflag & server->attributes) { + *status = r->status; + return r->err; + } +@@ -134,20 +134,20 @@ check_tgs_svc_deny_opts(krb5_kdc_req *req, krb5_db_entry server, + * A service principal can deny all TGS-REQs for it. + */ + static int +-check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry server, ++check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry *server, + krb5_ticket *tkt, krb5_timestamp kdc_time, + const char **status) + { +- if (server.attributes & KRB5_KDB_DISALLOW_ALL_TIX) { ++ if (server->attributes & KRB5_KDB_DISALLOW_ALL_TIX) { + *status = "SERVER LOCKED OUT"; + return KDC_ERR_S_PRINCIPAL_UNKNOWN; + } +- if ((server.attributes & KRB5_KDB_DISALLOW_SVR) && ++ if ((server->attributes & KRB5_KDB_DISALLOW_SVR) && + !(req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY)) { + *status = "SERVER NOT ALLOWED"; + return KDC_ERR_MUST_USE_USER2USER; + } +- if (server.attributes & KRB5_KDB_DISALLOW_TGT_BASED) { ++ if (server->attributes & KRB5_KDB_DISALLOW_TGT_BASED) { + if (krb5_is_tgs_principal(tkt->server)) { + *status = "TGT BASED NOT ALLOWED"; + return KDC_ERR_POLICY; +@@ -160,17 +160,17 @@ check_tgs_svc_deny_all(krb5_kdc_req *req, krb5_db_entry server, + * A service principal can require certain TGT flags. + */ + static int +-check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry server, ++check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry *server, + krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) + { +- if (server.attributes & KRB5_KDB_REQUIRES_HW_AUTH) { ++ if (server->attributes & KRB5_KDB_REQUIRES_HW_AUTH) { + if (!(tkt->enc_part2->flags & TKT_FLG_HW_AUTH)) { + *status = "NO HW PREAUTH"; + return KRB_ERR_GENERIC; + } + } +- if (server.attributes & KRB5_KDB_REQUIRES_PRE_AUTH) { ++ if (server->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) { + if (!(tkt->enc_part2->flags & TKT_FLG_PRE_AUTH)) { + *status = "NO PREAUTH"; + return KRB_ERR_GENERIC; +@@ -180,10 +180,10 @@ check_tgs_svc_reqd_flags(krb5_kdc_req *req, krb5_db_entry server, + } + + static int +-check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, ++check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry *server, krb5_ticket *tkt, + krb5_timestamp kdc_time, const char **status) + { +- if (server.expiration && ts_after(kdc_time, server.expiration)) { ++ if (server->expiration && ts_after(kdc_time, server->expiration)) { + *status = "SERVICE EXPIRED"; + return KDC_ERR_SERVICE_EXP; + } +@@ -191,8 +191,9 @@ check_tgs_svc_time(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, + } + + static int +-check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry server, krb5_ticket *tkt, +- krb5_timestamp kdc_time, const char **status) ++check_tgs_svc_policy(krb5_kdc_req *req, krb5_db_entry *server, ++ krb5_ticket *tkt, krb5_timestamp kdc_time, ++ const char **status) + { + int errcode; + size_t i; +@@ -317,7 +318,7 @@ check_tgs_tgt(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req, + + int + validate_tgs_request(kdc_realm_t *kdc_active_realm, +- krb5_kdc_req *request, krb5_db_entry server, ++ krb5_kdc_req *request, krb5_db_entry *server, + krb5_ticket *ticket, krb5_timestamp kdc_time, + const char **status, krb5_pa_data ***e_data) + { +@@ -367,8 +368,8 @@ validate_tgs_request(kdc_realm_t *kdc_active_realm, + } + + /* Perform KDB module policy checks. */ +- ret = krb5_db_check_policy_tgs(kdc_context, request, &server, +- ticket, status, e_data); ++ ret = krb5_db_check_policy_tgs(kdc_context, request, server, ticket, ++ status, e_data); + if (ret && ret != KRB5_PLUGIN_OP_NOTSUPP) + return errcode_to_protocol(ret); + diff --git a/Fix-minor-static-analysis-defects.patch b/Fix-minor-static-analysis-defects.patch new file mode 100644 index 0000000..653bce1 --- /dev/null +++ b/Fix-minor-static-analysis-defects.patch @@ -0,0 +1,106 @@ +From c3d96fca46cb2cc3ee9f4c2e2a4ed98bad3e310a Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Thu, 15 Oct 2020 18:15:29 -0400 +Subject: [PATCH] Fix minor static analysis defects + +Remove an unused variable in krb5_ldap_create(). Handle the return +value from krb5_dbe_get_string() in the certauth test plugin module. +Handle the return value from k5_expand_path_tokens() in +k5_rc_default(). Remove dead assignments in +krb5_get_credentials_for_user() and kg_accept_krb5(). + +[ghudson@mit.edu: squashed and edited commit message; simplified +k5_rc_default() change] + +(cherry picked from commit b27461141810fddd299764928649148c5d0e99f3) +--- + src/lib/gssapi/krb5/accept_sec_context.c | 4 +--- + src/lib/krb5/krb/s4u_creds.c | 1 - + src/lib/krb5/rcache/rc_base.c | 2 ++ + src/plugins/certauth/test/main.c | 3 +++ + src/plugins/kdb/ldap/libkdb_ldap/ldap_create.c | 4 ---- + 5 files changed, 6 insertions(+), 8 deletions(-) + +diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c +index 3d5b84b15..e2c5e2b59 100644 +--- a/src/lib/gssapi/krb5/accept_sec_context.c ++++ b/src/lib/gssapi/krb5/accept_sec_context.c +@@ -671,7 +671,7 @@ kg_accept_krb5(minor_status, context_handle, + krb5_auth_context auth_context = NULL; + krb5_ticket * ticket = NULL; + const gss_OID_desc *mech_used = NULL; +- OM_uint32 major_status = GSS_S_FAILURE; ++ OM_uint32 major_status; + OM_uint32 tmp_minor_status; + krb5_error krb_error_data; + krb5_data scratch; +@@ -878,8 +878,6 @@ kg_accept_krb5(minor_status, context_handle, + if (major_status != GSS_S_COMPLETE) + goto fail; + +- major_status = GSS_S_FAILURE; +- + if (exts->iakerb.conv && !exts->iakerb.verified) { + major_status = GSS_S_BAD_SIG; + goto fail; +diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c +index d8f486dc6..35a8843e5 100644 +--- a/src/lib/krb5/krb/s4u_creds.c ++++ b/src/lib/krb5/krb/s4u_creds.c +@@ -714,7 +714,6 @@ krb5_get_credentials_for_user(krb5_context context, krb5_flags options, + } else if (code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) { + goto cleanup; + } +- code = 0; + } + + /* Note the authdata we asked for in the output creds. */ +diff --git a/src/lib/krb5/rcache/rc_base.c b/src/lib/krb5/rcache/rc_base.c +index 5f456d1f3..f9a482318 100644 +--- a/src/lib/krb5/rcache/rc_base.c ++++ b/src/lib/krb5/rcache/rc_base.c +@@ -56,6 +56,8 @@ k5_rc_default(krb5_context context, krb5_rcache *rc_out) + &profstr) == 0 && profstr != NULL) { + ret = k5_expand_path_tokens(context, profstr, &rcname); + profile_release_string(profstr); ++ if (ret) ++ return ret; + ret = k5_rc_resolve(context, rcname, rc_out); + free(rcname); + return ret; +diff --git a/src/plugins/certauth/test/main.c b/src/plugins/certauth/test/main.c +index d4633b8cd..7e7a3ef4c 100644 +--- a/src/plugins/certauth/test/main.c ++++ b/src/plugins/certauth/test/main.c +@@ -171,6 +171,9 @@ test2_authorize(krb5_context context, krb5_certauth_moddata moddata, + + ret = krb5_dbe_get_string(context, (krb5_db_entry *)db_entry, "hwauth", + &strval); ++ if (ret) ++ goto cleanup; ++ + ret = (strval != NULL) ? KRB5_CERTAUTH_HWAUTH : 0; + krb5_dbe_free_string(context, strval); + +diff --git a/src/plugins/kdb/ldap/libkdb_ldap/ldap_create.c b/src/plugins/kdb/ldap/libkdb_ldap/ldap_create.c +index 5b57c799a..2d6605666 100644 +--- a/src/plugins/kdb/ldap/libkdb_ldap/ldap_create.c ++++ b/src/plugins/kdb/ldap/libkdb_ldap/ldap_create.c +@@ -55,7 +55,6 @@ krb5_ldap_create(krb5_context context, char *conf_section, char **db_args) + krb5_error_code status = 0; + krb5_ldap_realm_params *rparams = NULL; + krb5_ldap_context *ldap_context=NULL; +- krb5_boolean realm_obj_created = FALSE; + int mask = 0; + + /* Clear the global error string */ +@@ -109,9 +108,6 @@ krb5_ldap_create(krb5_context context, char *conf_section, char **db_args) + if ((status = krb5_ldap_create_realm(context, rparams, mask))) + goto cleanup; + +- /* We just created the Realm container. Here starts our transaction tracking */ +- realm_obj_created = TRUE; +- + /* verify realm object */ + if ((status = krb5_ldap_read_realm_params(context, + rparams->realm_name, diff --git a/Improve-KDC-alias-checking-for-S4U-requests.patch b/Improve-KDC-alias-checking-for-S4U-requests.patch new file mode 100644 index 0000000..3dbb119 --- /dev/null +++ b/Improve-KDC-alias-checking-for-S4U-requests.patch @@ -0,0 +1,124 @@ +From ed87237cdd70f72b309960a294a2bed26cef1579 Mon Sep 17 00:00:00 2001 +From: Isaac Boukris +Date: Fri, 4 Sep 2020 14:05:50 +0300 +Subject: [PATCH] Improve KDC alias checking for S4U requests + +When processing an S4U2Self request, check for DB aliases when +matching the TGT client against the request server. When processing +an S4U2Proxy request, check for DB aliases when matching the TGT +client against the evidence ticket server. + +[ghudson@mit.edu: minor edits; rewrote commit message] + +ticket: 8946 (new) +(cherry picked from commit 05deeebfc096970b5d9aa67a48b14106cf1b9b56) +--- + src/kdc/kdc_util.c | 74 ++++++++++++++++------------------------------ + 1 file changed, 25 insertions(+), 49 deletions(-) + +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index e3352f9cc..dcb2df8dc 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -1463,6 +1463,25 @@ cleanup: + return code; + } + ++/* Return true if princ canonicalizes to the same principal as canon. */ ++static krb5_boolean ++is_client_alias(krb5_context context, krb5_const_principal canon, ++ krb5_const_principal princ) ++{ ++ krb5_error_code ret; ++ krb5_db_entry *self; ++ krb5_boolean is_self = FALSE; ++ ++ ret = krb5_db_get_principal(context, princ, ++ KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY, &self); ++ if (!ret) { ++ is_self = krb5_principal_compare(context, canon, self->princ); ++ krb5_db_free_principal(context, self); ++ } ++ ++ return is_self; ++} ++ + /* + * Protocol transition (S4U2Self) + */ +@@ -1481,7 +1500,6 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm, + { + krb5_error_code code; + krb5_pa_data *pa_data; +- int flags; + krb5_db_entry *princ; + krb5_s4u_userid *id; + +@@ -1515,51 +1533,11 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm, + } + id = &(*s4u_x509_user)->user_id; + +- /* +- * We need to compare the client name in the TGT with the requested +- * server name. Supporting server name aliases without assuming a +- * global name service makes this difficult to do. +- * +- * The comparison below handles the following cases (note that the +- * term "principal name" below excludes the realm). +- * +- * (1) The requested service is a host-based service with two name +- * components, in which case we assume the principal name to +- * contain sufficient qualifying information. The realm is +- * ignored for the purpose of comparison. +- * +- * (2) The requested service name is an enterprise principal name: +- * the service principal name is compared with the unparsed +- * form of the client name (including its realm). +- * +- * (3) The requested service is some other name type: an exact +- * match is required. +- * +- * An alternative would be to look up the server once again with +- * FLAG_CANONICALIZE | FLAG_CLIENT_REFERRALS_ONLY set, do an exact +- * match between the returned name and client_princ. However, this +- * assumes that the client set FLAG_CANONICALIZE when requesting +- * the TGT and that we have a global name service. +- */ +- flags = 0; +- switch (krb5_princ_type(kdc_context, request->server)) { +- case KRB5_NT_SRV_HST: /* (1) */ +- if (krb5_princ_size(kdc_context, request->server) == 2) +- flags |= KRB5_PRINCIPAL_COMPARE_IGNORE_REALM; +- break; +- case KRB5_NT_ENTERPRISE_PRINCIPAL: /* (2) */ +- flags |= KRB5_PRINCIPAL_COMPARE_ENTERPRISE; +- break; +- default: /* (3) */ +- break; +- } +- +- if (!krb5_principal_compare_flags(kdc_context, +- request->server, +- client_princ, +- flags)) { +- *status = "INVALID_S4U2SELF_REQUEST"; +- return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error code */ ++ /* If the server is local, check that the request is for self. */ ++ if (!isflagset(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL) && ++ !is_client_alias(kdc_context, server->princ, client_princ)) { ++ *status = "INVALID_S4U2SELF_REQUEST_SERVER_MISMATCH"; ++ return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; /* match Windows error */ + } + + /* +@@ -1750,9 +1728,7 @@ kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags, + } + + client_princ = *stkt_authdata_client; +- } else if (!krb5_principal_compare(kdc_context, +- server->princ, /* after canon */ +- server_princ)) { ++ } else if (!is_client_alias(kdc_context, server->princ, server_princ)) { + *status = "EVIDENCE_TICKET_MISMATCH"; + return KRB5KDC_ERR_SERVER_NOMATCH; + } diff --git a/Minimize-usage-of-tgs_server-in-KDC.patch b/Minimize-usage-of-tgs_server-in-KDC.patch new file mode 100644 index 0000000..f08458e --- /dev/null +++ b/Minimize-usage-of-tgs_server-in-KDC.patch @@ -0,0 +1,316 @@ +From 5e79319edf3836d12dbc710ec1e2dd4405c9df35 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Fri, 25 Sep 2020 11:12:34 -0400 +Subject: [PATCH] Minimize usage of tgs_server in KDC + +Where possible, use the realm of the request server principal +(canonicalized via KDB lookup, if available) in preference to +tgs_server. This change facilitates alias realm support and potential +future support for serving multiple realms from the same KDB. + +S4U2Self local user testing currently uses the uncanonicalized request +realm after this change, which will require attention for alias realm +support. + +FAST armor ticket checking is unaffected by this change (it still +compares against tgs_server). This check poses no issue for realm +aliases, as both tgs_server and the armor ticket server should have +canonical realms, but it will require attention for multi-realm KDB +support. + +Remove is_local_principal() as it is no longer used. Add an +is_local_tgs_principal() helper and shorten is_cross_tgs_principal(). + +Move the header ticket lineage check from kdc_process_tgs_req() to +process_tgs_req(), where we have the canonical request server name and +a more natural indication of whether the request was an S4U2Self +request. + +(cherry picked from commit 90fedf8188fc47aa5a476a969af34671555df389) +--- + src/kdc/do_as_req.c | 21 ++++++-------- + src/kdc/do_tgs_req.c | 16 ++++++++--- + src/kdc/kdc_util.c | 68 ++++++++++---------------------------------- + src/kdc/kdc_util.h | 3 +- + src/kdc/tgs_policy.c | 16 ++++++----- + 5 files changed, 46 insertions(+), 78 deletions(-) + +diff --git a/src/kdc/do_as_req.c b/src/kdc/do_as_req.c +index c2dfea9b8..e0ac33649 100644 +--- a/src/kdc/do_as_req.c ++++ b/src/kdc/do_as_req.c +@@ -620,18 +620,6 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, + } + state->rock.client = state->client; + +- /* +- * 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(kdc_active_realm, state->client->princ)) { +- /* Entry is a referral to another realm */ +- state->status = "REFERRAL"; +- au_state->cl_realm = &state->client->princ->realm; +- errcode = KRB5KDC_ERR_WRONG_REALM; +- goto errout; +- } +- + au_state->stage = SRVC_PRINC; + + s_flags = 0; +@@ -651,6 +639,15 @@ process_as_req(krb5_kdc_req *request, krb5_data *req_pkt, + goto errout; + } + ++ /* If the KDB module returned a different realm for the client and server, ++ * we need to issue a client realm referral. */ ++ if (!data_eq(state->server->princ->realm, state->client->princ->realm)) { ++ state->status = "REFERRAL"; ++ au_state->cl_realm = &state->client->princ->realm; ++ errcode = KRB5KDC_ERR_WRONG_REALM; ++ goto errout; ++ } ++ + errcode = get_local_tgt(kdc_context, &state->request->server->realm, + state->server, &state->local_tgt, + &state->local_tgt_storage, &state->local_tgt_key); +diff --git a/src/kdc/do_tgs_req.c b/src/kdc/do_tgs_req.c +index d345797c4..8ea418e43 100644 +--- a/src/kdc/do_tgs_req.c ++++ b/src/kdc/do_tgs_req.c +@@ -268,7 +268,7 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + goto cleanup; + } + +- if (!is_local_principal(kdc_active_realm, header_ticket->server)) ++ if (!data_eq(header_server->princ->realm, sprinc->realm)) + setflag(c_flags, KRB5_KDB_FLAG_CROSS_REALM); + if (is_referral) + setflag(c_flags, KRB5_KDB_FLAG_ISSUING_REFERRAL); +@@ -295,6 +295,15 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + au_state->s4u2self_user = NULL; + } + ++ /* Aside from cross-realm S4U2Self requests, do not accept header tickets ++ * for local users issued by foreign realms. */ ++ if (s4u_x509_user == NULL && data_eq(cprinc->realm, sprinc->realm) && ++ isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM)) { ++ krb5_klog_syslog(LOG_INFO, _("PROCESS_TGS: failed lineage check")); ++ retval = KRB5KDC_ERR_POLICY; ++ goto cleanup; ++ } ++ + if (errcode) + goto cleanup; + +@@ -583,13 +592,12 @@ process_tgs_req(krb5_kdc_req *request, krb5_data *pkt, + + /* + * Only add the realm of the presented tgt to the transited list if +- * it is different than the local realm (cross-realm) and it is different ++ * it is different than the server realm (cross-realm) and it is different + * than the realm of the client (since the realm of the client is already + * implicitly part of the transited list and should not be explicitly + * listed). + */ +- /* realm compare is like strcmp, but knows how to deal with these args */ +- if (krb5_realm_compare(kdc_context, header_ticket->server, tgs_server) || ++ if (!isflagset(c_flags, KRB5_KDB_FLAG_CROSS_REALM) || + krb5_realm_compare(kdc_context, header_ticket->server, + enc_tkt_reply.client)) { + /* tgt issued by local realm or issued by realm of client */ +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index b2042862a..e0b65a87c 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -78,12 +78,6 @@ static krb5_error_code find_server_key(krb5_context, + krb5_kvno, krb5_keyblock **, + krb5_kvno *); + +-krb5_boolean +-is_local_principal(kdc_realm_t *kdc_active_realm, 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. +@@ -104,13 +98,16 @@ krb5_is_tgs_principal(krb5_const_principal principal) + krb5_boolean + is_cross_tgs_principal(krb5_const_principal principal) + { +- if (!krb5_is_tgs_principal(principal)) +- return FALSE; +- if (!data_eq(*krb5_princ_component(kdc_context, principal, 1), +- *krb5_princ_realm(kdc_context, principal))) +- return TRUE; +- else +- return FALSE; ++ return krb5_is_tgs_principal(principal) && ++ !data_eq(principal->data[1], principal->realm); ++} ++ ++/* Return true if princ is the name of a local TGS for any realm. */ ++krb5_boolean ++is_local_tgs_principal(krb5_const_principal principal) ++{ ++ return krb5_is_tgs_principal(principal) && ++ data_eq(principal->data[1], principal->realm); + } + + /* +@@ -143,17 +140,6 @@ comp_cksum(krb5_context kcontext, krb5_data *source, krb5_ticket *ticket, + return(0); + } + +-/* Return true if padata contains an entry of either S4U2Self type. */ +-static inline krb5_boolean +-has_s4u2self_padata(krb5_pa_data **padata) +-{ +- if (krb5int_find_pa_data(NULL, padata, KRB5_PADATA_FOR_USER) != NULL) +- return TRUE; +- if (krb5int_find_pa_data(NULL, padata, KRB5_PADATA_S4U_X509_USER) != NULL) +- return TRUE; +- return FALSE; +-} +- + /* If a header ticket is decrypted, *ticket_out is filled in even on error. */ + krb5_error_code + kdc_process_tgs_req(kdc_realm_t *kdc_active_realm, +@@ -170,7 +156,6 @@ kdc_process_tgs_req(kdc_realm_t *kdc_active_realm, + krb5_authdata **authdata = NULL; + krb5_data scratch1; + krb5_data * scratch = NULL; +- krb5_boolean foreign_server = FALSE; + krb5_auth_context auth_context = NULL; + krb5_authenticator * authenticator = NULL; + krb5_checksum * his_cksum = NULL; +@@ -199,19 +184,6 @@ kdc_process_tgs_req(kdc_realm_t *kdc_active_realm, + goto cleanup; + } + +- /* If the "server" principal in the ticket is not something +- in the local realm, then we must refuse to service the request +- if the client claims to be from the local realm. +- +- If we don't do this, then some other realm's nasty KDC can +- claim to be authenticating a client from our realm, and we'll +- give out tickets concurring with it! +- +- we set a flag here for checking below. +- */ +- foreign_server = !is_local_principal(kdc_active_realm, +- apreq->ticket->server); +- + if ((retval = krb5_auth_con_init(kdc_context, &auth_context))) + goto cleanup; + +@@ -265,15 +237,6 @@ kdc_process_tgs_req(kdc_realm_t *kdc_active_realm, + goto cleanup_authenticator; + } + +- /* make sure the client is of proper lineage (see above) */ +- if (foreign_server && !has_s4u2self_padata(request->padata) && +- is_local_principal(kdc_active_realm, 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; +- goto cleanup_authenticator; +- } +- + /* + * Check application checksum vs. tgs request + * +@@ -591,12 +554,12 @@ int + check_anon(kdc_realm_t *kdc_active_realm, + krb5_principal client, krb5_principal server) + { +- /* If restrict_anon is set, reject requests from anonymous to principals +- * other than the local TGT. */ ++ /* If restrict_anon is set, reject requests from anonymous clients to ++ * server principals other than local TGTs. */ + if (kdc_active_realm->realm_restrict_anon && + krb5_principal_compare_any_realm(kdc_context, client, + krb5_anonymous_principal()) && +- !krb5_principal_compare(kdc_context, server, tgs_server)) ++ !is_local_tgs_principal(server)) + return -1; + return 0; + } +@@ -1527,7 +1490,7 @@ kdc_process_s4u2self_req(kdc_realm_t *kdc_active_realm, + /* + * Do not attempt to lookup principals in foreign realms. + */ +- if (is_local_principal(kdc_active_realm, id->user)) { ++ if (data_eq(server->princ->realm, id->user->realm)) { + krb5_db_entry no_server; + krb5_pa_data **e_data = NULL; + +@@ -1663,8 +1626,7 @@ kdc_process_s4u2proxy_req(kdc_realm_t *kdc_active_realm, unsigned int flags, + */ + if (isflagset(flags, KRB5_KDB_FLAG_ISSUING_REFERRAL) || + !is_cross_tgs_principal(server->princ) || +- !krb5_principal_compare_any_realm(kdc_context, server->princ, +- tgs_server) || ++ !data_eq(server->princ->data[1], proxy->princ->realm) || + !krb5_principal_compare(kdc_context, client_princ, server_princ)) { + *status = "XREALM_EVIDENCE_TICKET_MISMATCH"; + return KRB5KDC_ERR_BADOPTION; +diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h +index 04007a8f5..a6bac4388 100644 +--- a/src/kdc/kdc_util.h ++++ b/src/kdc/kdc_util.h +@@ -37,10 +37,9 @@ + #include "reqstate.h" + + krb5_error_code check_hot_list (krb5_ticket *); +-krb5_boolean is_local_principal(kdc_realm_t *kdc_active_realm, +- krb5_const_principal princ1); + krb5_boolean krb5_is_tgs_principal (krb5_const_principal); + krb5_boolean is_cross_tgs_principal(krb5_const_principal); ++krb5_boolean is_local_tgs_principal(krb5_const_principal); + krb5_error_code + add_to_transited (krb5_data *, + krb5_data *, +diff --git a/src/kdc/tgs_policy.c b/src/kdc/tgs_policy.c +index 3f4fa8499..a5a00f0cc 100644 +--- a/src/kdc/tgs_policy.c ++++ b/src/kdc/tgs_policy.c +@@ -252,19 +252,21 @@ check_tgs_s4u2proxy(kdc_realm_t *kdc_active_realm, + } + + static int +-check_tgs_u2u(kdc_realm_t *kdc_active_realm, +- krb5_kdc_req *req, const char **status) ++check_tgs_u2u(kdc_realm_t *kdc_active_realm, krb5_kdc_req *req, ++ krb5_const_principal server_princ, const char **status) + { ++ krb5_const_principal second_server_princ; ++ + if (req->kdc_options & KDC_OPT_ENC_TKT_IN_SKEY) { + /* Check that second ticket is in request. */ + if (!req->second_ticket || !req->second_ticket[0]) { + *status = "NO_2ND_TKT"; + return KDC_ERR_BADOPTION; + } +- /* Check that second ticket is a TGT. */ +- if (!krb5_principal_compare(kdc_context, +- req->second_ticket[0]->server, +- tgs_server)) { ++ /* Check that second ticket is a TGT to the server realm. */ ++ second_server_princ = req->second_ticket[0]->server; ++ if (!is_local_tgs_principal(second_server_princ) || ++ !data_eq(second_server_princ->data[1], server_princ->realm)) { + *status = "2ND_TKT_NOT_TGS"; + return KDC_ERR_POLICY; + } +@@ -353,7 +355,7 @@ validate_tgs_request(kdc_realm_t *kdc_active_realm, + return(KRB_AP_ERR_REPEAT); + } + +- errcode = check_tgs_u2u(kdc_active_realm, request, status); ++ errcode = check_tgs_u2u(kdc_active_realm, request, server->princ, status); + if (errcode != 0) + return errcode; + diff --git a/Refactor-KDC-authdata-list-management-helpers.patch b/Refactor-KDC-authdata-list-management-helpers.patch new file mode 100644 index 0000000..00aed49 --- /dev/null +++ b/Refactor-KDC-authdata-list-management-helpers.patch @@ -0,0 +1,335 @@ +From 9335481c00cd15170adec244ccff0a00a014bbab Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Wed, 5 Feb 2020 18:46:11 -0500 +Subject: [PATCH] Refactor KDC authdata list management helpers + +Remove the unused concat_authorization_data(). Split merge_authdata() +into two helpers, one to destructively merge without filtering and one +to add copied elements while filtering out KDC-only authdata types. +Remove context parameters where they aren't needed (taking advantage +of knowledge that some libkrb5 functions don't use their context +parameters). + +(cherry picked from commit b2190fdc253de6024001e0f1ff9fe56c31042bb7) +--- + src/kdc/kdc_authdata.c | 138 +++++++++++++++++++---------------------- + src/kdc/kdc_util.c | 50 --------------- + src/kdc/kdc_util.h | 5 -- + 3 files changed, 64 insertions(+), 129 deletions(-) + +diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c +index 1ebe87246..010922c27 100644 +--- a/src/kdc/kdc_authdata.c ++++ b/src/kdc/kdc_authdata.c +@@ -108,7 +108,7 @@ unload_authdata_plugins(krb5_context context) + /* Return true if authdata should be filtered when copying from untrusted + * authdata. If desired_type is non-zero, look only for that type. */ + static krb5_boolean +-is_kdc_issued_authdatum(krb5_context context, krb5_authdata *authdata, ++is_kdc_issued_authdatum(krb5_authdata *authdata, + krb5_authdatatype desired_type) + { + krb5_boolean result = FALSE; +@@ -117,7 +117,7 @@ is_kdc_issued_authdatum(krb5_context context, krb5_authdata *authdata, + krb5_authdatatype *ad_types, *containee_types = NULL; + + if (authdata->ad_type == KRB5_AUTHDATA_IF_RELEVANT) { +- if (krb5int_get_authdata_containee_types(context, authdata, &count, ++ if (krb5int_get_authdata_containee_types(NULL, authdata, &count, + &containee_types) != 0) + goto cleanup; + ad_types = containee_types; +@@ -152,7 +152,7 @@ cleanup: + /* Return true if authdata contains any elements which should only come from + * the KDC. If desired_type is non-zero, look only for that type. */ + static krb5_boolean +-has_kdc_issued_authdata(krb5_context context, krb5_authdata **authdata, ++has_kdc_issued_authdata(krb5_authdata **authdata, + krb5_authdatatype desired_type) + { + int i; +@@ -160,7 +160,7 @@ has_kdc_issued_authdata(krb5_context context, krb5_authdata **authdata, + if (authdata == NULL) + return FALSE; + for (i = 0; authdata[i] != NULL; i++) { +- if (is_kdc_issued_authdatum(context, authdata[i], desired_type)) ++ if (is_kdc_issued_authdatum(authdata[i], desired_type)) + return TRUE; + } + return FALSE; +@@ -181,66 +181,71 @@ has_mandatory_for_kdc_authdata(krb5_context context, krb5_authdata **authdata) + return FALSE; + } + +-/* +- * Add the elements of in_authdata to out_authdata. If copy is false, +- * in_authdata is invalid on successful return. If ignore_kdc_issued is true, +- * KDC-issued authdata is not copied. +- */ ++/* Add elements from *new_elements to *existing_list, reallocating as ++ * necessary. On success, release *new_elements and set it to NULL. */ + static krb5_error_code +-merge_authdata(krb5_context context, krb5_authdata **in_authdata, +- krb5_authdata ***out_authdata, krb5_boolean copy, +- krb5_boolean ignore_kdc_issued) ++merge_authdata(krb5_authdata ***existing_list, krb5_authdata ***new_elements) + { +- krb5_error_code ret; +- size_t i, j, nadata = 0; +- krb5_authdata **in_copy = NULL, **authdata = *out_authdata; ++ size_t count = 0, ncount = 0; ++ krb5_authdata **list = *existing_list, **nlist = *new_elements; + +- if (in_authdata == NULL || in_authdata[0] == NULL) ++ if (nlist == NULL) + return 0; + +- if (authdata != NULL) { +- for (nadata = 0; authdata[nadata] != NULL; nadata++) +- ; +- } ++ for (count = 0; list != NULL && list[count] != NULL; count++); ++ for (ncount = 0; nlist[ncount] != NULL; ncount++); + +- for (i = 0; in_authdata[i] != NULL; i++) +- ; +- +- if (copy) { +- ret = krb5_copy_authdata(context, in_authdata, &in_copy); +- if (ret) +- return ret; +- in_authdata = in_copy; +- } +- +- authdata = realloc(authdata, (nadata + i + 1) * sizeof(krb5_authdata *)); +- if (authdata == NULL) { +- krb5_free_authdata(context, in_copy); ++ list = realloc(list, (count + ncount + 1) * sizeof(*list)); ++ if (list == NULL) + return ENOMEM; ++ ++ memcpy(list + count, nlist, ncount * sizeof(*nlist)); ++ list[count + ncount] = NULL; ++ free(nlist); ++ ++ if (list[0] == NULL) { ++ free(list); ++ list = NULL; + } + +- for (i = 0, j = 0; in_authdata[i] != NULL; i++) { +- if (ignore_kdc_issued && +- is_kdc_issued_authdatum(context, in_authdata[i], 0)) { +- free(in_authdata[i]->contents); +- free(in_authdata[i]); ++ *new_elements = NULL; ++ *existing_list = list; ++ return 0; ++} ++ ++/* Add a copy of new_elements to *existing_list, omitting KDC-issued ++ * authdata. */ ++static krb5_error_code ++add_filtered_authdata(krb5_authdata ***existing_list, ++ krb5_authdata **new_elements) ++{ ++ krb5_error_code ret; ++ krb5_authdata **copy; ++ size_t i, j; ++ ++ if (new_elements == NULL) ++ return 0; ++ ++ ret = krb5_copy_authdata(NULL, new_elements, ©); ++ if (ret) ++ return ret; ++ ++ /* Remove KDC-issued elements from copy. */ ++ j = 0; ++ for (i = 0; copy[i] != NULL; i++) { ++ if (is_kdc_issued_authdatum(copy[i], 0)) { ++ free(copy[i]->contents); ++ free(copy[i]); + } else { +- authdata[nadata + j++] = in_authdata[i]; ++ copy[j++] = copy[i]; + } + } ++ copy[j] = NULL; + +- authdata[nadata + j] = NULL; +- +- free(in_authdata); +- +- if (authdata[0] == NULL) { +- free(authdata); +- authdata = NULL; +- } +- +- *out_authdata = authdata; +- +- return 0; ++ /* Destructively merge the filtered copy into existing_list. */ ++ ret = merge_authdata(existing_list, ©); ++ krb5_free_authdata(NULL, copy); ++ return ret; + } + + /* Copy TGS-REQ authorization data into the ticket authdata. */ +@@ -289,10 +294,7 @@ copy_request_authdata(krb5_context context, krb5_keyblock *client_key, + goto cleanup; + } + +- /* Add a copy of the requested authdata to the ticket, ignoring KDC-issued +- * types. */ +- ret = merge_authdata(context, req->unenc_authdata, tkt_authdata, TRUE, +- TRUE); ++ ret = add_filtered_authdata(tkt_authdata, req->unenc_authdata); + + cleanup: + free(plaintext.data); +@@ -307,9 +309,7 @@ copy_tgt_authdata(krb5_context context, krb5_kdc_req *request, + if (has_mandatory_for_kdc_authdata(context, tgt_authdata)) + return KRB5KDC_ERR_POLICY; + +- /* Add a copy of the TGT authdata to the ticket, ignoring KDC-issued +- * types. */ +- return merge_authdata(context, tgt_authdata, tkt_authdata, TRUE, TRUE); ++ return add_filtered_authdata(tkt_authdata, tgt_authdata); + } + + /* Fetch authorization data from KDB module. */ +@@ -374,8 +374,7 @@ fetch_kdb_authdata(krb5_context context, unsigned int flags, + + /* Put the KDB authdata first in the ticket. A successful merge places the + * combined list in db_authdata and releases the old ticket authdata. */ +- ret = merge_authdata(context, enc_tkt_reply->authorization_data, +- &db_authdata, FALSE, FALSE); ++ ret = merge_authdata(&db_authdata, &enc_tkt_reply->authorization_data); + if (ret) + krb5_free_authdata(context, db_authdata); + else +@@ -404,8 +403,7 @@ make_signedpath_data(krb5_context context, krb5_const_principal client, + return ret; + + for (i = 0, j = 0; authdata[i] != NULL; i++) { +- if (is_kdc_issued_authdatum(context, authdata[i], +- KRB5_AUTHDATA_SIGNTICKET)) ++ if (is_kdc_issued_authdatum(authdata[i], KRB5_AUTHDATA_SIGNTICKET)) + continue; + + sign_authdata[j++] = authdata[i]; +@@ -635,12 +633,8 @@ make_signedpath(krb5_context context, krb5_const_principal for_user_princ, + if (ret) + goto cleanup; + +- /* Add the authdata to the ticket, without copying or filtering. */ +- ret = merge_authdata(context, if_relevant, +- &enc_tkt_reply->authorization_data, FALSE, FALSE); +- if (ret) +- goto cleanup; +- if_relevant = NULL; /* merge_authdata() freed */ ++ /* Add the signedpath authdata to the ticket. */ ++ ret = merge_authdata(&enc_tkt_reply->authorization_data, &if_relevant); + + cleanup: + free(sp.delegated); +@@ -665,7 +659,7 @@ free_deleg_path(krb5_context context, krb5_principal *deleg_path) + static krb5_boolean + has_pac(krb5_context context, krb5_authdata **authdata) + { +- return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_WIN2K_PAC); ++ return has_kdc_issued_authdata(authdata, KRB5_AUTHDATA_WIN2K_PAC); + } + + /* Verify AD-SIGNTICKET authdata if we need to, and insert an AD-SIGNEDPATH +@@ -746,11 +740,7 @@ add_auth_indicators(krb5_context context, krb5_data *const *auth_indicators, + goto cleanup; + + /* Add the wrapped authdata to the ticket, without copying or filtering. */ +- ret = merge_authdata(context, cammac, &enc_tkt_reply->authorization_data, +- FALSE, FALSE); +- if (ret) +- goto cleanup; +- cammac = NULL; /* merge_authdata() freed */ ++ ret = merge_authdata(&enc_tkt_reply->authorization_data, &cammac); + + cleanup: + krb5_free_data(context, der_indicators); +diff --git a/src/kdc/kdc_util.c b/src/kdc/kdc_util.c +index 6330387d0..a4a05b9fa 100644 +--- a/src/kdc/kdc_util.c ++++ b/src/kdc/kdc_util.c +@@ -78,56 +78,6 @@ static krb5_error_code find_server_key(krb5_context, + krb5_kvno, krb5_keyblock **, + krb5_kvno *); + +-/* +- * concatenate first two authdata arrays, returning an allocated replacement. +- * The replacement should be freed with krb5_free_authdata(). +- */ +-krb5_error_code +-concat_authorization_data(krb5_context context, +- krb5_authdata **first, krb5_authdata **second, +- krb5_authdata ***output) +-{ +- int i, j; +- krb5_authdata **ptr, **retdata; +- +- /* count up the entries */ +- i = 0; +- if (first) +- for (ptr = first; *ptr; ptr++) +- i++; +- if (second) +- for (ptr = second; *ptr; ptr++) +- i++; +- +- retdata = (krb5_authdata **)malloc((i+1)*sizeof(*retdata)); +- if (!retdata) +- return ENOMEM; +- retdata[i] = 0; /* null-terminated array */ +- for (i = 0, j = 0, ptr = first; j < 2 ; ptr = second, j++) +- while (ptr && *ptr) { +- /* now walk & copy */ +- retdata[i] = (krb5_authdata *)malloc(sizeof(*retdata[i])); +- if (!retdata[i]) { +- krb5_free_authdata(context, retdata); +- return ENOMEM; +- } +- *retdata[i] = **ptr; +- if (!(retdata[i]->contents = +- (krb5_octet *)malloc(retdata[i]->length))) { +- free(retdata[i]); +- retdata[i] = 0; +- krb5_free_authdata(context, retdata); +- return ENOMEM; +- } +- memcpy(retdata[i]->contents, (*ptr)->contents, retdata[i]->length); +- +- ptr++; +- i++; +- } +- *output = retdata; +- return 0; +-} +- + krb5_boolean + is_local_principal(kdc_realm_t *kdc_active_realm, krb5_const_principal princ1) + { +diff --git a/src/kdc/kdc_util.h b/src/kdc/kdc_util.h +index 2c9d8cf69..42b7ee208 100644 +--- a/src/kdc/kdc_util.h ++++ b/src/kdc/kdc_util.h +@@ -52,11 +52,6 @@ compress_transited (krb5_data *, + krb5_principal, + krb5_data *); + krb5_error_code +-concat_authorization_data (krb5_context, +- krb5_authdata **, +- krb5_authdata **, +- krb5_authdata ***); +-krb5_error_code + fetch_last_req_info (krb5_db_entry *, krb5_last_req_entry ***); + + krb5_error_code diff --git a/Unify-kvno-option-documentation.patch b/Unify-kvno-option-documentation.patch new file mode 100644 index 0000000..b6f5e01 --- /dev/null +++ b/Unify-kvno-option-documentation.patch @@ -0,0 +1,185 @@ +From 52e3695cc5ef00766e12adfe8ed276c2885e71bb Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Thu, 20 Aug 2020 17:49:29 -0400 +Subject: [PATCH] Unify kvno option documentation + +Add missing kvno options to the kvno.rst synopsis and option +descriptions, and to the kvno usage message. Remove mention of '-h' +(help text), from kvno.rst as it is an implicit option. Note that the +three new caching options were added in release 1.19. + +Indicate the two exclusions (-u/-S and --u2u with the S4U2Self options) +and dependency (-P on S4U2Self) where they are missing. + +Switch xusage() to print only a single localized string, rather than +running each line of output through localization separately. + +Leave kvno -C undocumented for now, as the semantics of +KRB5_GC_CANONICALIZE are minimally useful and likely to change. + +[ghudson@mit.edu: edited documentation and commit message] + +ticket: 7476 +tags: pullup +target_version: 1.18-next + +(cherry picked from commit becd1ad6830b526d08ddaf5b2b6f213154c6446c) +--- + doc/user/user_commands/kvno.rst | 24 +++++++++++++----------- + src/clients/kvno/kvno.c | 15 +++++++++------ + src/man/kvno.man | 24 +++++++++++++----------- + 3 files changed, 35 insertions(+), 28 deletions(-) + +diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst +index 718313576..65c44e1c0 100644 +--- a/doc/user/user_commands/kvno.rst ++++ b/doc/user/user_commands/kvno.rst +@@ -10,13 +10,9 @@ SYNOPSIS + [**-c** *ccache*] + [**-e** *etype*] + [**-q**] +-[**-h**] ++[**-u** | **-S** *sname*] + [**-P**] +-[**-S** *sname*] +-[**-I** *for_user*] +-[**-U** *for_user*] +-[**-F** *cert_file*] +-[**--u2u** *ccache*] ++[[{**-F** *cert_file* | {**-I** | **-U**} *for_user*} [**-P**]] | **--u2u** *ccache*] + *service1 service2* ... + + +@@ -39,13 +35,18 @@ OPTIONS + of all the services named on the command line. This is useful in + certain backward compatibility situations. + ++**-k** *keytab* ++ Decrypt the acquired tickets using *keytab* to confirm their ++ validity. ++ + **-q** + Suppress printing output when successful. If a service ticket + cannot be obtained, an error message will still be printed and + kvno will exit with nonzero status. + +-**-h** +- Prints a usage statement and exits. ++**-u** ++ Use the unknown name type in requested service principal names. ++ This option Cannot be used with *-S*. + + **-P** + Specifies that the *service1 service2* ... arguments are to be +@@ -76,16 +77,17 @@ OPTIONS + + **--cached-only** + Only retrieve credentials already present in the cache, not from +- the KDC. ++ the KDC. (Added in release 1.19.) + + **--no-store** + Do not store retrieved credentials in the cache. If + **--out-cache** is also specified, credentials will still be +- stored into the output credential cache. ++ stored into the output credential cache. (Added in release 1.19.) + + **--out-cache** *ccache* + Initialize *ccache* and store all retrieved credentials into it. +- Do not store acquired credentials in the input cache. ++ Do not store acquired credentials in the input cache. (Added in ++ release 1.19.) + + **--u2u** *ccache* + Requests a user-to-user ticket. *ccache* must contain a local +diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c +index 9d85864f6..c5f6bf700 100644 +--- a/src/clients/kvno/kvno.c ++++ b/src/clients/kvno/kvno.c +@@ -38,15 +38,18 @@ + static char *prog; + static int quiet = 0; + ++#define XUSAGE_BREAK "\n\t" ++ + static void + xusage() + { +- fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog); +- fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | " +- "[-F cert_file] [-P]]\n")); +- fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] " +- "[--u2u ccache]\n")); +- fprintf(stderr, _("\tservice1 service2 ...\n")); ++ fprintf(stderr, _("usage: %s [-c ccache] [-e etype] [-k keytab] [-q] " ++ "[-u | -S sname]" XUSAGE_BREAK ++ "[[{-F cert_file | {-I | -U} for_user} [-P]] | " ++ "--u2u ccache]" XUSAGE_BREAK ++ "[--cached-only] [--no-store] [--out-cache] " ++ "service1 service2 ...\n"), ++ prog); + exit(1); + } + +diff --git a/src/man/kvno.man b/src/man/kvno.man +index b9f6739eb..22318324d 100644 +--- a/src/man/kvno.man ++++ b/src/man/kvno.man +@@ -36,13 +36,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] + [\fB\-c\fP \fIccache\fP] + [\fB\-e\fP \fIetype\fP] + [\fB\-q\fP] +-[\fB\-h\fP] ++[\fB\-u\fP | \fB\-S\fP \fIsname\fP] + [\fB\-P\fP] +-[\fB\-S\fP \fIsname\fP] +-[\fB\-I\fP \fIfor_user\fP] +-[\fB\-U\fP \fIfor_user\fP] +-[\fB\-F\fP \fIcert_file\fP] +-[\fB\-\-u2u\fP \fIccache\fP] ++[[{\fB\-F\fP \fIcert_file\fP | {\fB\-I\fP | \fB\-U\fP} \fIfor_user\fP} [\fB\-P\fP]] | \fB\-\-u2u\fP \fIccache\fP] + \fIservice1 service2\fP ... + .SH DESCRIPTION + .sp +@@ -60,13 +56,18 @@ Specifies the enctype which will be requested for the session key + of all the services named on the command line. This is useful in + certain backward compatibility situations. + .TP ++\fB\-k\fP \fIkeytab\fP ++Decrypt the acquired tickets using \fIkeytab\fP to confirm their ++validity. ++.TP + \fB\-q\fP + Suppress printing output when successful. If a service ticket + cannot be obtained, an error message will still be printed and + kvno will exit with nonzero status. + .TP +-\fB\-h\fP +-Prints a usage statement and exits. ++\fB\-u\fP ++Use the unknown name type in requested service principal names. ++This option Cannot be used with \fI\-S\fP\&. + .TP + \fB\-P\fP + Specifies that the \fIservice1 service2\fP ... arguments are to be +@@ -97,16 +98,17 @@ certificate file must be in PEM format. + .TP + \fB\-\-cached\-only\fP + Only retrieve credentials already present in the cache, not from +-the KDC. ++the KDC. (Added in release 1.19.) + .TP + \fB\-\-no\-store\fP + Do not store retrieved credentials in the cache. If + \fB\-\-out\-cache\fP is also specified, credentials will still be +-stored into the output credential cache. ++stored into the output credential cache. (Added in release 1.19.) + .TP + \fB\-\-out\-cache\fP \fIccache\fP + Initialize \fIccache\fP and store all retrieved credentials into it. +-Do not store acquired credentials in the input cache. ++Do not store acquired credentials in the input cache. (Added in ++release 1.19.) + .TP + \fB\-\-u2u\fP \fIccache\fP + Requests a user\-to\-user ticket. \fIccache\fP must contain a local diff --git a/krb5.spec b/krb5.spec index 8f389f4..3cc61cf 100644 --- a/krb5.spec +++ b/krb5.spec @@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.18.2 # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces) -Release: 24%{?dist} +Release: 28%{?dist} # rharwood has trust path to signing key and verifies on check-in Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz @@ -72,6 +72,15 @@ Patch33: Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch Patch34: Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch Patch35: Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch Patch36: Fix-input-length-checking-in-SPNEGO-DER-decoding.patch +Patch37: Add-three-kvno-options-from-Heimdal-kgetcred.patch +Patch38: Unify-kvno-option-documentation.patch +Patch39: Improve-KDC-alias-checking-for-S4U-requests.patch +Patch40: Adjust-KDC-alias-helper-function-contract.patch +Patch41: Allow-aliases-when-matching-U2U-second-ticket.patch +Patch42: Refactor-KDC-authdata-list-management-helpers.patch +Patch43: Avoid-passing-DB-entry-structures-in-KDC.patch +Patch44: Minimize-usage-of-tgs_server-in-KDC.patch +Patch45: Fix-minor-static-analysis-defects.patch License: MIT URL: https://web.mit.edu/kerberos/www/ @@ -127,7 +136,6 @@ to install this package. %package libs Summary: The non-admin shared libraries used by Kerberos 5 Requires: openssl-libs >= 1:1.1.1d-4 -Requires: openssl-libs < 1:3.0.0 Requires: coreutils, gawk, grep, sed Requires: keyutils-libs >= 1.5.8 Requires: /etc/crypto-policies/back-ends/krb5.config @@ -633,6 +641,18 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog +* Fri Oct 23 2020 Robbie Harwood - 1.18.2-28 +- Fix minor static analysis defects + +* Wed Oct 21 2020 Robbie Harwood - 1.18.2-27 +- Fix build of previous + +* Wed Oct 21 2020 Robbie Harwood - 1.18.2-26 +- Cross-realm s4u fixes for samba (#1836630) + +* Thu Oct 15 2020 Robbie Harwood - 1.18.2-25 +- Unify kvno option documentation + * Fri Oct 02 2020 Robbie Harwood - 1.18.2-24 - Add md5 override to krad