The package rpms/krb5.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/krb5.git/commit/?id=8be525213668e....
Change:
+%ifnarch s390x
Thanks.
Full change:
============
commit c59e4a1c673512e66b4f5cfe53a1c64f7dd6b635
Author: Robbie Harwood <rharwood(a)redhat.com>
Date: Fri Aug 7 19:03:02 2020 -0400
Expand dns_canonicalize_hostname=fallback support
diff --git a/Cache-S4U2Proxy-requests-by-second-ticket.patch
b/Cache-S4U2Proxy-requests-by-second-ticket.patch
new file mode 100644
index 0000000..01532bf
--- /dev/null
+++ b/Cache-S4U2Proxy-requests-by-second-ticket.patch
@@ -0,0 +1,327 @@
+From 158ce9222351216507da39d7bb4233469603e647 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris(a)gmail.com>
+Date: Tue, 21 Jul 2020 00:40:06 +0200
+Subject: [PATCH] Cache S4U2Proxy requests by second ticket
+
+krb5_get_credentials() does not know the client principal for an
+S4U2Proxy request until the end, because it is in the encrypted part
+of the evidence ticket. However, we can check the cache by second
+ticket, since all S4U2Proxy requests in a cache will generally be made
+with the same evidence ticket.
+
+In the ccache types, allow mcreds->client and mcreds->server to be
+NULL (as Heimdal does) to ignore them for the purpose of matching. In
+krb5int_construct_matching_creds(), set mcreds->client to NULL for
+S4U2Proxy requests. Add a cache check to
+k5_get_proxy_cred_from_kdc(), and remove the cache check from
+krb5_get_credentials_for_proxy() and the krb5 mech's
+get_credentials().
+
+In get_proxy_cred_from_kdc(), fix a bug where cross-realm S4U2Proxy
+would cache the evidence ticket used in the final request, rather than
+the original evidence ticket.
+
+[ghudson(a)mit.edu: debugged cache check and cross-realm caching;
+switched from new flag to null matching cred principals; wrote commit
+message]
+
+ticket: 8931 (new)
+(cherry picked from commit 148b317e1eb5df28dad96679cb4b8a07c62d4786)
+---
+ src/lib/gssapi/krb5/init_sec_context.c | 61 ++++++++++++--------------
+ src/lib/krb5/ccache/cc_retr.c | 13 +++---
+ src/lib/krb5/ccache/ccapi/stdcc_util.c | 30 ++++++-------
+ src/lib/krb5/ccache/ccfns.c | 3 +-
+ src/lib/krb5/krb/get_creds.c | 5 +++
+ src/lib/krb5/krb/s4u_creds.c | 58 ++++++++++++------------
+ src/tests/s4u2proxy.c | 3 ++
+ 7 files changed, 88 insertions(+), 85 deletions(-)
+
+diff --git a/src/lib/gssapi/krb5/init_sec_context.c
b/src/lib/gssapi/krb5/init_sec_context.c
+index 3f77157c9..823656037 100644
+--- a/src/lib/gssapi/krb5/init_sec_context.c
++++ b/src/lib/gssapi/krb5/init_sec_context.c
+@@ -165,44 +165,37 @@ static krb5_error_code get_credentials(context, cred, server, now,
+ goto cleanup;
+ }
+
+- /*
+- * For IAKERB or constrained delegation, only check the cache in this step.
+- * For IAKERB we will ask the server to make any necessary TGS requests;
+- * for constrained delegation we will adjust in_creds and make an S4U2Proxy
+- * request below if the cache lookup fails.
+- */
+- if (cred->impersonator != NULL || cred->iakerb_mech)
++ /* Try constrained delegation if we have proxy credentials. */
++ if (cred->impersonator != NULL) {
++ /* If we are trying to get a ticket to ourselves, we should use the
++ * the evidence ticket directly from cache. */
++ if (krb5_principal_compare(context, cred->impersonator,
++ server->princ)) {
++ flags |= KRB5_GC_CACHED;
++ } else {
++ memset(&mcreds, 0, sizeof(mcreds));
++ mcreds.magic = KV5M_CREDS;
++ mcreds.server = cred->impersonator;
++ mcreds.client = cred->name->princ;
++ code = krb5_cc_retrieve_cred(context, cred->ccache,
++ KRB5_TC_MATCH_AUTHDATA, &mcreds,
++ &evidence_creds);
++ if (code)
++ goto cleanup;
++
++ in_creds.client = cred->impersonator;
++ in_creds.second_ticket = evidence_creds.ticket;
++ flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
++ }
++ }
++
++ /* For IAKERB, only check the cache in this step. We will ask the server
++ * to make any necessary TGS requests. */
++ if (cred->iakerb_mech)
+ flags |= KRB5_GC_CACHED;
+
+ code = krb5_get_credentials(context, flags, cred->ccache,
+ &in_creds, &result_creds);
+-
+- /*
+- * Try constrained delegation if we have proxy credentials, unless
+- * we are trying to get a ticket to ourselves (in which case we could
+- * just use the evidence ticket directly from cache).
+- */
+- if (code == KRB5_CC_NOTFOUND && cred->impersonator != NULL &&
+- !cred->iakerb_mech &&
+- !krb5_principal_compare(context, cred->impersonator, server->princ)) {
+-
+- memset(&mcreds, 0, sizeof(mcreds));
+- mcreds.magic = KV5M_CREDS;
+- mcreds.server = cred->impersonator;
+- mcreds.client = cred->name->princ;
+- code = krb5_cc_retrieve_cred(context, cred->ccache,
+- KRB5_TC_MATCH_AUTHDATA, &mcreds,
+- &evidence_creds);
+- if (code)
+- goto cleanup;
+-
+- in_creds.client = cred->impersonator;
+- in_creds.second_ticket = evidence_creds.ticket;
+- flags = KRB5_GC_CANONICALIZE | KRB5_GC_CONSTRAINED_DELEGATION;
+- code = krb5_get_credentials(context, flags, cred->ccache,
+- &in_creds, &result_creds);
+- }
+-
+ if (code)
+ goto cleanup;
+
+diff --git a/src/lib/krb5/ccache/cc_retr.c b/src/lib/krb5/ccache/cc_retr.c
+index 2c50c9cce..4328b7d6f 100644
+--- a/src/lib/krb5/ccache/cc_retr.c
++++ b/src/lib/krb5/ccache/cc_retr.c
+@@ -58,15 +58,14 @@ static krb5_boolean
+ princs_match(krb5_context context, krb5_flags whichfields,
+ const krb5_creds *mcreds, const krb5_creds *creds)
+ {
+- krb5_principal_data princ;
+-
+- if (!krb5_principal_compare(context, mcreds->client, creds->client))
++ if (mcreds->client != NULL &&
++ !krb5_principal_compare(context, mcreds->client, creds->client))
+ return FALSE;
++ if (mcreds->server == NULL)
++ return TRUE;
+ if (whichfields & KRB5_TC_MATCH_SRV_NAMEONLY) {
+- /* Ignore the server realm. */
+- princ = *mcreds->server;
+- princ.realm = creds->server->realm;
+- return krb5_principal_compare(context, &princ, creds->server);
++ return krb5_principal_compare_any_realm(context, mcreds->server,
++ creds->server);
+ } else {
+ return krb5_principal_compare(context, mcreds->server, creds->server);
+ }
+diff --git a/src/lib/krb5/ccache/ccapi/stdcc_util.c
b/src/lib/krb5/ccache/ccapi/stdcc_util.c
+index 1f2a3865c..58aa81165 100644
+--- a/src/lib/krb5/ccache/ccapi/stdcc_util.c
++++ b/src/lib/krb5/ccache/ccapi/stdcc_util.c
+@@ -945,8 +945,13 @@ standard_fields_match(context, mcreds, creds)
+ krb5_context context;
+ const krb5_creds *mcreds, *creds;
+ {
+- return (krb5_principal_compare(context, mcreds->client,creds->client)
&&
+- krb5_principal_compare(context, mcreds->server,creds->server));
++ if (mcreds->client != NULL &&
++ !krb5_principal_compare(context, mcreds->client, creds->client))
++ return FALSE;
++ if (mcreds->server != NULL &&
++ !krb5_principal_compare(context, mcreds->server,creds->server))
++ return FALSE;
++ return TRUE;
+ }
+
+ /* only match the server name portion, not the server realm portion */
+@@ -956,19 +961,14 @@ srvname_match(context, mcreds, creds)
+ krb5_context context;
+ const krb5_creds *mcreds, *creds;
+ {
+- krb5_boolean retval;
+- krb5_principal_data p1, p2;
+-
+- retval = krb5_principal_compare(context, mcreds->client,creds->client);
+- if (retval != TRUE)
+- return retval;
+- /*
+- * Hack to ignore the server realm for the purposes of the compare.
+- */
+- p1 = *mcreds->server;
+- p2 = *creds->server;
+- p1.realm = p2.realm;
+- return krb5_principal_compare(context, &p1, &p2);
++ if (mcreds->client != NULL &&
++ !krb5_principal_compare(context, mcreds->client, creds->client))
++ return FALSE;
++ if (mcreds->server != NULL &&
++ !krb5_principal_compare_any_realm(context, mcreds->server,
++ creds->server))
++ return FALSE;
++ return TRUE;
+ }
+
+
+diff --git a/src/lib/krb5/ccache/ccfns.c b/src/lib/krb5/ccache/ccfns.c
+index 62a6983d8..59982b752 100644
+--- a/src/lib/krb5/ccache/ccfns.c
++++ b/src/lib/krb5/ccache/ccfns.c
+@@ -96,7 +96,8 @@ krb5_cc_retrieve_cred(krb5_context context, krb5_ccache cache,
+ TRACE_CC_RETRIEVE(context, cache, mcreds, ret);
+ if (ret != KRB5_CC_NOTFOUND)
+ return ret;
+- if (!krb5_is_referral_realm(&mcreds->server->realm))
++ if (mcreds->client == NULL || mcreds->server == NULL ||
++ !krb5_is_referral_realm(&mcreds->server->realm))
+ return ret;
+
+ /*
+diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
+index dc0aef667..b3f01be9b 100644
+--- a/src/lib/krb5/krb/get_creds.c
++++ b/src/lib/krb5/krb/get_creds.c
+@@ -102,6 +102,11 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags
options,
+ return KRB5_NO_2ND_TKT;
+ }
+
++ /* For S4U2Proxy requests we don't know the impersonated client in this
++ * API, but matching against the second ticket is good enough. */
++ if (options & KRB5_GC_CONSTRAINED_DELEGATION)
++ mcreds->client = NULL;
++
+ return 0;
+ }
+
+diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
+index 07c7a98be..d44f939f8 100644
+--- a/src/lib/krb5/krb/s4u_creds.c
++++ b/src/lib/krb5/krb/s4u_creds.c
+@@ -1122,6 +1122,13 @@ get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+ code = KRB5KRB_AP_WRONG_PRINC;
+ goto cleanup;
+ }
++
++ /* Put the original evidence ticket in the output creds. */
++ krb5_free_data_contents(context, &tkt->second_ticket);
++ code = krb5int_copy_data_contents(context, &in_creds->second_ticket,
++ &tkt->second_ticket);
++ if (code)
++ goto cleanup;
+ }
+
+ /* Note the authdata we asked for in the output creds. */
+@@ -1148,11 +1155,33 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags
options,
+ {
+ krb5_error_code code;
+ krb5_const_principal canonprinc;
+- krb5_creds copy, *creds;
++ krb5_creds mcreds, copy, *creds, *ncreds;
++ krb5_flags fields;
+ struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
+
+ *out_creds = NULL;
+
++ code = krb5int_construct_matching_creds(context, options, in_creds,
++ &mcreds, &fields);
++ if (code != 0)
++ return code;
++
++ ncreds = calloc(1, sizeof(*ncreds));
++ if (ncreds == NULL)
++ return ENOMEM;
++ ncreds->magic = KV5M_CRED;
++
++ code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
++ if (code) {
++ free(ncreds);
++ } else {
++ *out_creds = ncreds;
++ }
++
++ if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) ||
++ options & KRB5_GC_CACHED)
++ return code;
++
+ copy = *in_creds;
+ while ((code = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
+ canonprinc != NULL) {
+@@ -1198,9 +1227,6 @@ krb5_get_credentials_for_proxy(krb5_context context,
+ krb5_creds **out_creds)
+ {
+ krb5_error_code code;
+- krb5_creds mcreds;
+- krb5_creds *ncreds = NULL;
+- krb5_flags fields;
+ krb5_data *evidence_tkt_data = NULL;
+ krb5_creds s4u_creds;
+
+@@ -1222,30 +1248,6 @@ krb5_get_credentials_for_proxy(krb5_context context,
+ goto cleanup;
+ }
+
+- code = krb5int_construct_matching_creds(context, options, in_creds,
+- &mcreds, &fields);
+- if (code != 0)
+- goto cleanup;
+-
+- ncreds = calloc(1, sizeof(*ncreds));
+- if (ncreds == NULL) {
+- code = ENOMEM;
+- goto cleanup;
+- }
+- ncreds->magic = KV5M_CRED;
+-
+- code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
+- if (code != 0) {
+- free(ncreds);
+- ncreds = in_creds;
+- } else {
+- *out_creds = ncreds;
+- }
+-
+- if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE)
+- || options & KRB5_GC_CACHED)
+- goto cleanup;
+-
+ code = encode_krb5_ticket(evidence_tkt, &evidence_tkt_data);
+ if (code != 0)
+ goto cleanup;
+diff --git a/src/tests/s4u2proxy.c b/src/tests/s4u2proxy.c
+index 4adf6aca5..3786bad2c 100644
+--- a/src/tests/s4u2proxy.c
++++ b/src/tests/s4u2proxy.c
+@@ -124,6 +124,9 @@ main(int argc, char **argv)
+ KRB5_GC_CANONICALIZE, defcc,
+ &mcred, ev_ticket, &new_cred));
+
++ assert(data_eq(new_cred->second_ticket, ev_cred.ticket));
++ assert(new_cred->second_ticket.length != 0);
++
+ /* Store the new cred in the default ccache. */
+ check(krb5_cc_store_cred(context, defcc, new_cred));
+
diff --git a/Don-t-create-hostbased-principals-in-new-KDBs.patch
b/Don-t-create-hostbased-principals-in-new-KDBs.patch
new file mode 100644
index 0000000..867eb4a
--- /dev/null
+++ b/Don-t-create-hostbased-principals-in-new-KDBs.patch
@@ -0,0 +1,195 @@
+From 71070a0f0fa6424cfb37aef7c4ec43e99380a6aa Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Thu, 30 Jul 2020 12:14:27 -0400
+Subject: [PATCH] Don't create hostbased principals in new KDBs
+
+Unix-like platforms do not provide a simple method to find the
+fully-qualified local hostname as the machine is expected to appear to
+other hosts. Canonicalizing the gethostname() result with
+getaddrinfo() usually works, but potentially uses DNS. Now that
+dns_canonicalize_hostname=true is no longer the default, KDB creation
+would generally create the wrong host-based principals.
+
+kadmin/hostname is unnecessary because the client software can also
+use kadmin/admin, and kiprop/hostname is one of several principals
+that must be created for incremental propagation.
+
+ticket: 8935 (new)
+(cherry picked from commit ac2b693d0ec464e0bcda4953acd79f201169f396)
+---
+ src/kadmin/dbutil/kadm5_create.c | 52 ++-----------------
+ .../kdb/ldap/ldap_util/kdb5_ldap_realm.c | 35 +------------
+ src/tests/dejagnu/krb-standalone/kadmin.exp | 7 +--
+ src/tests/t_iprop.py | 1 +
+ src/tests/t_kadmin_acl.py | 1 +
+ 5 files changed, 12 insertions(+), 84 deletions(-)
+
+diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c
+index 4f254a387..42b45aa2d 100644
+--- a/src/kadmin/dbutil/kadm5_create.c
++++ b/src/kadmin/dbutil/kadm5_create.c
+@@ -139,60 +139,18 @@ int kadm5_create_magic_princs(kadm5_config_params *params,
+ static int add_admin_princs(void *handle, krb5_context context, char *realm)
+ {
+ krb5_error_code ret = 0;
+- char *service_name = 0, *kiprop_name = 0, *canonhost = 0;
+- char localname[MAXHOSTNAMELEN];
+-
+- if (gethostname(localname, MAXHOSTNAMELEN)) {
+- ret = errno;
+- perror("gethostname");
+- goto clean_and_exit;
+- }
+- ret = krb5_expand_hostname(context, localname, &canonhost);
+- if (ret) {
+- com_err(progname, ret, _("while canonicalizing local hostname"));
+- goto clean_and_exit;
+- }
+- if (asprintf(&service_name, "kadmin/%s", canonhost) < 0) {
+- ret = ENOMEM;
+- fprintf(stderr, _("Out of memory\n"));
+- goto clean_and_exit;
+- }
+- if (asprintf(&kiprop_name, "kiprop/%s", canonhost) < 0) {
+- ret = ENOMEM;
+- fprintf(stderr, _("Out of memory\n"));
+- goto clean_and_exit;
+- }
+-
+- if ((ret = add_admin_princ(handle, context,
+- service_name, realm,
+- KRB5_KDB_DISALLOW_TGT_BASED |
+- KRB5_KDB_LOCKDOWN_KEYS,
+- ADMIN_LIFETIME)))
+- goto clean_and_exit;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_ADMIN_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_LOCKDOWN_KEYS,
+ ADMIN_LIFETIME)))
+- goto clean_and_exit;
++ return ret;
+
+- if ((ret = add_admin_princ(handle, context,
+- KADM5_CHANGEPW_SERVICE, realm,
+- KRB5_KDB_DISALLOW_TGT_BASED |
+- KRB5_KDB_PWCHANGE_SERVICE |
+- KRB5_KDB_LOCKDOWN_KEYS,
+- CHANGEPW_LIFETIME)))
+- goto clean_and_exit;
+-
+- ret = add_admin_princ(handle, context, kiprop_name, realm, 0, 0);
+-
+-clean_and_exit:
+- krb5_free_string(context, canonhost);
+- free(service_name);
+- free(kiprop_name);
+-
+- return ret;
++ return add_admin_princ(handle, context, KADM5_CHANGEPW_SERVICE, realm,
++ KRB5_KDB_DISALLOW_TGT_BASED |
++ KRB5_KDB_PWCHANGE_SERVICE | KRB5_KDB_LOCKDOWN_KEYS,
++ CHANGEPW_LIFETIME);
+ }
+
+ /*
+diff --git a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
+index c21d19981..ae1afd4a9 100644
+--- a/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
++++ b/src/plugins/kdb/ldap/ldap_util/kdb5_ldap_realm.c
+@@ -307,29 +307,6 @@ create_fixed_special(krb5_context context, struct realm_info
*rinfo,
+
+ }
+
+-/* Create a special principal using one specified component and the
+- * canonicalized local hostname. */
+-static krb5_error_code
+-create_hostbased_special(krb5_context context, struct realm_info *rinfo,
+- krb5_keyblock *mkey, const char *comp1)
+-{
+- krb5_error_code ret;
+- krb5_principal princ = NULL;
+-
+- ret = krb5_sname_to_principal(context, NULL, comp1, KRB5_NT_SRV_HST,
+- &princ);
+- if (ret)
+- goto cleanup;
+- ret = krb5_set_principal_realm(context, princ, global_params.realm);
+- if (ret)
+- goto cleanup;
+- ret = kdb_ldap_create_principal(context, princ, TGT_KEY, rinfo, mkey);
+-
+-cleanup:
+- krb5_free_principal(context, princ);
+- return ret;
+-}
+-
+ /* Create all special principals for the realm. */
+ static krb5_error_code
+ create_special_princs(krb5_context context, krb5_principal master_princ,
+@@ -360,20 +337,10 @@ create_special_princs(krb5_context context, krb5_principal
master_princ,
+ if (ret)
+ return ret;
+
+- /* Create kadmin/admin and kadmin/<hostname>. */
++ /* Create kadmin/admin. */
+ rblock.max_life = ADMIN_LIFETIME;
+ rblock.flags = KRB5_KDB_DISALLOW_TGT_BASED;
+ ret = create_fixed_special(context, &rblock, mkey, "kadmin",
"admin");
+- if (ret)
+- return ret;
+- ret = create_hostbased_special(context, &rblock, mkey, "kadmin");
+- if (ret)
+- return ret;
+-
+- /* Create kiprop/<hostname>. */
+- rblock.max_life = global_params.max_life;
+- rblock.flags = 0;
+- ret = create_hostbased_special(context, &rblock, mkey, "kiprop");
+ if (ret)
+ return ret;
+
+diff --git a/src/tests/dejagnu/krb-standalone/kadmin.exp
b/src/tests/dejagnu/krb-standalone/kadmin.exp
+index 36a345258..fa50a61fb 100644
+--- a/src/tests/dejagnu/krb-standalone/kadmin.exp
++++ b/src/tests/dejagnu/krb-standalone/kadmin.exp
+@@ -1098,10 +1098,11 @@ proc kadmin_test { } {
+ return
+ }
+
+- # test fallback to kadmin/admin
+- if {![kadmin_delete_locked_down kadmin/$hostname] \
++ # test fallback to kadmin/hostname
++ if {![kadmin_add_rnd kadmin/$hostname] \
++ || ![kadmin_delete_locked_down kadmin/admin] \
+ || ![kadmin_list] \
+- || ![kadmin_add_rnd kadmin/$hostname -allow_tgs_req] \
++ || ![kadmin_add_rnd kadmin/admin -allow_tgs_req] \
+ || ![kadmin_list]} {
+ return
+ }
+diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
+index 371f3a22b..3bb0fd2e9 100755
+--- a/src/tests/t_iprop.py
++++ b/src/tests/t_iprop.py
+@@ -188,6 +188,7 @@ for realm in multidb_realms(kdc_conf=conf, create_user=False,
+
+ # Create the principal used to authenticate kpropd to kadmind.
+ kiprop_princ = 'kiprop/' + hostname
++ realm.addprinc(kiprop_princ)
+ realm.extract_keytab(kiprop_princ, realm.keytab)
+
+ # Create the initial replica databases.
+diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
+index 16faf0a9d..31a7fb871 100755
+--- a/src/tests/t_kadmin_acl.py
++++ b/src/tests/t_kadmin_acl.py
+@@ -331,6 +331,7 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw',
'-randkey', '-e', 'aes256-cts',
+ # Test authentication to kadmin/hostname.
+ mark('authentication to kadmin/hostname')
+ kadmin_hostname = 'kadmin/' + hostname
++realm.addprinc(kadmin_hostname)
+ realm.run([kadminl, 'delprinc', 'kadmin/admin'])
+ msgs = ('Getting initial credentials for user/admin(a)KRBTEST.COM',
+ 'Setting initial creds service to kadmin/admin',
diff --git a/Expand-dns_canonicalize_host-fallback-support.patch
b/Expand-dns_canonicalize_host-fallback-support.patch
new file mode 100644
index 0000000..eb82a81
--- /dev/null
+++ b/Expand-dns_canonicalize_host-fallback-support.patch
@@ -0,0 +1,1190 @@
+From c0e732f79dc5ea0c2066120bfe7ae8f6df82bf82 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Fri, 17 Jul 2020 22:57:45 -0400
+Subject: [PATCH] Expand dns_canonicalize_host=fallback support
+
+In krb5_sname_to_principal(), when using fallback, defer realm lookup
+and any kind of hostname canonicalization until use. Add a
+lightweight iterator k5_canonprinc() to yield the one or two possible
+candidates for a principal. In the iterator, don't yield the same
+hostname part twice.
+
+Add fallback processing to the stepwise TGS state machine, and remove
+it from krb5_get_credentials(). Add fallback processing to
+k5_get_proxy_cred_from_kdc().
+
+Add fallback processing to krb5_init_creds_set_keytab(), and use the
+principal we find in the keytab as the request client principal.
+Defer restart_init_creds_loop() to the first step call so that server
+principal is built using the correct realm.
+
+Add fallback processing to krb5_rd_req().
+
+ticket: 8930 (new)
+(cherry picked from commit 3fcc365a6f049730b3f47168f7112c03997c5c0b)
+---
+ src/include/k5-trace.h | 4 +-
+ src/kprop/kprop_util.c | 26 ++--
+ src/lib/krb5/krb/deps | 41 +++---
+ src/lib/krb5/krb/get_creds.c | 151 ++++++++++-----------
+ src/lib/krb5/krb/get_in_tkt.c | 7 +-
+ src/lib/krb5/krb/gic_keytab.c | 29 +++-
+ src/lib/krb5/krb/init_creds_ctx.h | 1 +
+ src/lib/krb5/krb/rd_req_dec.c | 36 ++++-
+ src/lib/krb5/krb/s4u_creds.c | 62 ++++++---
+ src/lib/krb5/os/os-proto.h | 30 +++++
+ src/lib/krb5/os/sn2princ.c | 215 +++++++++++++++++++++---------
+ src/tests/icred.c | 39 ++++--
+ src/tests/t_sn2princ.py | 65 ++++++---
+ 13 files changed, 465 insertions(+), 241 deletions(-)
+
+diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
+index 1da53dbb1..5a120f1a0 100644
+--- a/src/include/k5-trace.h
++++ b/src/include/k5-trace.h
+@@ -229,8 +229,8 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
+ salt, s2kparams)
+ #define TRACE_INIT_CREDS_IDENTIFIED_REALM(c, realm) \
+ TRACE(c, "Identified realm of client principal as {data}", realm)
+-#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, etypes) \
+- TRACE(c, "Looked up etypes in keytab: {etypes}", etypes)
++#define TRACE_INIT_CREDS_KEYTAB_LOOKUP(c, princ, etypes) \
++ TRACE(c, "Found entries for {princ} in keytab: {etypes}", princ, etypes)
+ #define TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(c, code) \
+ TRACE(c, "Couldn't lookup etypes in keytab: {kerr}", code)
+ #define TRACE_INIT_CREDS_PREAUTH(c) \
+diff --git a/src/kprop/kprop_util.c b/src/kprop/kprop_util.c
+index c32d174b9..c2b2e8764 100644
+--- a/src/kprop/kprop_util.c
++++ b/src/kprop/kprop_util.c
+@@ -73,26 +73,22 @@ sn2princ_realm(krb5_context context, const char *hostname, const char
*sname,
+ const char *realm, krb5_principal *princ_out)
+ {
+ krb5_error_code ret;
+- char *canonhost, localname[MAXHOSTNAMELEN];
++ krb5_principal princ;
+
+ *princ_out = NULL;
+ assert(sname != NULL && realm != NULL);
+
+- /* If hostname is NULL, use the local hostname. */
+- if (hostname == NULL) {
+- if (gethostname(localname, MAXHOSTNAMELEN) != 0)
+- return SOCKET_ERRNO;
+- hostname = localname;
+- }
+-
+- ret = krb5_expand_hostname(context, hostname, &canonhost);
++ ret = krb5_sname_to_principal(context, hostname, sname, KRB5_NT_SRV_HST,
++ &princ);
+ if (ret)
+ return ret;
+
+- ret = krb5_build_principal(context, princ_out, strlen(realm), realm, sname,
+- canonhost, (char *)NULL);
+- krb5_free_string(context, canonhost);
+- if (!ret)
+- (*princ_out)->type = KRB5_NT_SRV_HST;
+- return ret;
++ ret = krb5_set_principal_realm(context, princ, realm);
++ if (ret) {
++ krb5_free_principal(context, princ);
++ return ret;
++ }
++
++ *princ_out = princ;
++ return 0;
+ }
+diff --git a/src/lib/krb5/krb/deps b/src/lib/krb5/krb/deps
+index 439ca0272..6ac68bc19 100644
+--- a/src/lib/krb5/krb/deps
++++ b/src/lib/krb5/krb/deps
+@@ -499,12 +499,13 @@ get_in_tkt.so get_in_tkt.po $(OUTPRE)get_in_tkt.$(OBJEXT): \
+ gic_keytab.so gic_keytab.po $(OUTPRE)gic_keytab.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+- $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-json.h \
+- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+- $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
++ $(top_srcdir)/include/krb5/authdata_plugin.h
$(top_srcdir)/include/krb5/locate_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h gic_keytab.c init_creds_ctx.h \
+ int-proto.h
+@@ -940,13 +941,14 @@ rd_req.so rd_req.po $(OUTPRE)rd_req.$(OBJEXT):
$(BUILDTOP)/include/autoconf.h \
+ rd_req_dec.so rd_req_dec.po $(OUTPRE)rd_req_dec.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+- $(COM_ERR_DEPS) $(srcdir)/../rcache/memrcache.h $(top_srcdir)/include/k5-buf.h \
+- $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+- $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+- $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+- $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+- $(top_srcdir)/include/k5-utf8.h $(top_srcdir)/include/krb5.h \
+- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(srcdir)/../rcache/memrcache.h \
++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/k5-utf8.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ auth_con.h authdata.h int-proto.h rd_req_dec.c
+ rd_safe.so rd_safe.po $(OUTPRE)rd_safe.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+@@ -997,12 +999,13 @@ s4u_authdata.so s4u_authdata.po $(OUTPRE)s4u_authdata.$(OBJEXT): \
+ s4u_creds.so s4u_creds.po $(OUTPRE)s4u_creds.$(OBJEXT): \
+ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+- $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+- $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+- $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+- $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+- $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
+- $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
++ $(COM_ERR_DEPS) $(srcdir)/../os/os-proto.h $(top_srcdir)/include/k5-buf.h \
++ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
++ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
++ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
++ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
++ $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ int-proto.h s4u_creds.c
+ sendauth.so sendauth.po $(OUTPRE)sendauth.$(OBJEXT): \
+diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
+index e0a3b5cd8..dc0aef667 100644
+--- a/src/lib/krb5/krb/get_creds.c
++++ b/src/lib/krb5/krb/get_creds.c
+@@ -119,7 +119,7 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags
options,
+ * generate the next request. If it's time to advance to another state, any of
+ * the three functions can make a tail call to begin_<nextstate> to do so.
+ *
+- * The overall process is as follows:
++ * The general process is as follows:
+ * 1. Get a TGT for the service principal's realm (STATE_GET_TGT).
+ * 2. Make one or more referrals queries (STATE_REFERRALS).
+ * 3. In some cases, get a TGT for the fallback realm (STATE_GET_TGT again).
+@@ -129,6 +129,9 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags
options,
+ * getting_tgt_for field in the context keeps track of what state we will go to
+ * after successfully obtaining the TGT, and the end_get_tgt() function
+ * advances to the proper next state.
++ *
++ * If fallback DNS canonicalization is in use, the process can be repeated a
++ * second time for the second server principal canonicalization candidate.
+ */
+
+ enum state {
+@@ -153,6 +156,8 @@ struct _krb5_tkt_creds_context {
+ krb5_flags req_options; /* Caller-requested KRB5_GC_* options */
+ krb5_flags req_kdcopt; /* Caller-requested options as KDC options */
+ krb5_authdata **authdata; /* Caller-requested authdata */
++ struct canonprinc iter; /* Iterator over canonicalized server princs */
++ krb5_boolean referral_req; /* Server initially contained referral realm */
+
+ /* The following fields are used in multiple steps. */
+ krb5_creds *cur_tgt; /* TGT to be used for next query */
+@@ -484,7 +489,7 @@ try_fallback(krb5_context context, krb5_tkt_creds_context ctx)
+
+ /* If the request used a specified realm, make a non-referral request to
+ * that realm (in case it's a KDC which rejects KDC_OPT_CANONICALIZE). */
+- if (!krb5_is_referral_realm(&ctx->req_server->realm))
++ if (!ctx->referral_req)
+ return begin_non_referral(context, ctx);
+
+ if (ctx->server->length < 2) {
+@@ -1015,10 +1020,13 @@ check_cache(krb5_context context, krb5_tkt_creds_context ctx)
+ krb5_error_code code;
+ krb5_creds mcreds;
+ krb5_flags fields;
++ krb5_creds req_in_creds;
+
+- /* Perform the cache lookup. */
++ /* Check the cache for the originally requested server principal. */
++ req_in_creds = *ctx->in_creds;
++ req_in_creds.server = ctx->req_server;
+ code = krb5int_construct_matching_creds(context, ctx->req_options,
+- ctx->in_creds, &mcreds,
&fields);
++ &req_in_creds, &mcreds,
&fields);
+ if (code)
+ return code;
+ code = cache_get(context, ctx->ccache, fields, &mcreds,
&ctx->reply_creds);
+@@ -1044,12 +1052,9 @@ begin(krb5_context context, krb5_tkt_creds_context ctx)
+ {
+ krb5_error_code code;
+
+- code = check_cache(context, ctx);
+- if (code != 0 || ctx->state == STATE_COMPLETE)
+- return code;
+-
+ /* If the server realm is unspecified, start with the client realm. */
+- if (krb5_is_referral_realm(&ctx->server->realm)) {
++ ctx->referral_req = krb5_is_referral_realm(&ctx->server->realm);
++ if (ctx->referral_req) {
+ krb5_free_data_contents(context, &ctx->server->realm);
+ code = krb5int_copy_data_contents(context, &ctx->client->realm,
+ &ctx->server->realm);
+@@ -1072,6 +1077,7 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
+ {
+ krb5_error_code code;
+ krb5_tkt_creds_context ctx = NULL;
++ krb5_const_principal canonprinc;
+
+ TRACE_TKT_CREDS(context, in_creds, ccache);
+ ctx = k5alloc(sizeof(*ctx), &code);
+@@ -1089,14 +1095,28 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache,
+
+ ctx->state = STATE_BEGIN;
+
++ /* Copy the matching cred so we can modify it. Steal the copy of the
++ * service principal name to remember the original request server. */
+ code = krb5_copy_creds(context, in_creds, &ctx->in_creds);
+ if (code != 0)
+ goto cleanup;
+- ctx->client = ctx->in_creds->client;
+- ctx->server = ctx->in_creds->server;
+- code = krb5_copy_principal(context, ctx->server, &ctx->req_server);
++ ctx->req_server = ctx->in_creds->server;
++ ctx->in_creds->server = NULL;
++
++ /* Get the first canonicalization candidate for the requested server. */
++ ctx->iter.princ = ctx->req_server;
++
++ code = k5_canonprinc(context, &ctx->iter, &canonprinc);
++ if (code == 0 && canonprinc == NULL)
++ code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ if (code != 0)
+ goto cleanup;
++ code = krb5_copy_principal(context, canonprinc, &ctx->in_creds->server);
++ if (code != 0)
++ goto cleanup;
++
++ ctx->client = ctx->in_creds->client;
++ ctx->server = ctx->in_creds->server;
+ code = krb5_cc_dup(context, ccache, &ctx->ccache);
+ if (code != 0)
+ goto cleanup;
+@@ -1138,6 +1158,7 @@ krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context
ctx)
+ return;
+ krb5int_fast_free_state(context, ctx->fast_state);
+ krb5_free_creds(context, ctx->in_creds);
++ free_canonprinc(&ctx->iter);
+ krb5_cc_close(context, ctx->ccache);
+ krb5_free_principal(context, ctx->req_server);
+ krb5_free_authdata(context, ctx->authdata);
+@@ -1195,6 +1216,7 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context
ctx,
+ {
+ krb5_error_code code;
+ krb5_boolean no_input = (in == NULL || in->length == 0);
++ krb5_const_principal canonprinc;
+
+ *out = empty_data();
+ *realm = empty_data();
+@@ -1206,6 +1228,12 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context
ctx,
+ ctx->state == STATE_COMPLETE)
+ return EINVAL;
+
++ if (ctx->state == STATE_BEGIN) {
++ code = check_cache(context, ctx);
++ if (code != 0 || ctx->state == STATE_COMPLETE)
++ return code;
++ }
++
+ ctx->caller_out = out;
+ ctx->caller_realm = realm;
+ ctx->caller_flags = flags;
+@@ -1218,37 +1246,32 @@ krb5_tkt_creds_step(krb5_context context, krb5_tkt_creds_context
ctx,
+ }
+
+ if (ctx->state == STATE_BEGIN)
+- return begin(context, ctx);
++ code = begin(context, ctx);
+ else if (ctx->state == STATE_GET_TGT)
+- return step_get_tgt(context, ctx);
++ code = step_get_tgt(context, ctx);
+ else if (ctx->state == STATE_GET_TGT_OFFPATH)
+- return step_get_tgt_offpath(context, ctx);
++ code = step_get_tgt_offpath(context, ctx);
+ else if (ctx->state == STATE_REFERRALS)
+- return step_referrals(context, ctx);
++ code = step_referrals(context, ctx);
+ else if (ctx->state == STATE_NON_REFERRAL)
+- return step_non_referral(context, ctx);
++ code = step_non_referral(context, ctx);
+ else
+- return EINVAL;
+-}
++ code = EINVAL;
+
+-static krb5_error_code
+-try_get_creds(krb5_context context, krb5_flags options, krb5_ccache ccache,
+- krb5_creds *in_creds, krb5_creds *creds_out)
+-{
+- krb5_error_code code;
+- krb5_tkt_creds_context ctx = NULL;
++ /* Terminate on success or most errors. */
++ if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
++ return code;
+
+- code = krb5_tkt_creds_init(context, ccache, in_creds, options, &ctx);
++ /* Restart with the next server principal canonicalization candidate. */
++ code = k5_canonprinc(context, &ctx->iter, &canonprinc);
+ if (code)
+- goto cleanup;
+- code = krb5_tkt_creds_get(context, ctx);
+- if (code)
+- goto cleanup;
+- code = krb5_tkt_creds_get_creds(context, ctx, creds_out);
+-
+-cleanup:
+- krb5_tkt_creds_free(context, ctx);
+- return code;
++ return code;
++ if (canonprinc == NULL)
++ return KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
++ krb5_free_principal(context, ctx->in_creds->server);
++ code = krb5_copy_principal(context, canonprinc, &ctx->in_creds->server);
++ ctx->server = ctx->in_creds->server;
++ return begin(context, ctx);
+ }
+
+ krb5_error_code KRB5_CALLCONV
+@@ -1258,10 +1281,7 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
+ {
+ krb5_error_code code;
+ krb5_creds *ncreds = NULL;
+- krb5_creds canon_creds, store_creds;
+- krb5_principal_data canon_server;
+- krb5_data canon_components[2];
+- char *hostname = NULL, *canon_hostname = NULL;
++ krb5_tkt_creds_context ctx = NULL;
+
+ *out_creds = NULL;
+
+@@ -1277,59 +1297,22 @@ krb5_get_credentials(krb5_context context, krb5_flags options,
+ if (ncreds == NULL)
+ goto cleanup;
+
+- code = try_get_creds(context, options, ccache, in_creds, ncreds);
+- if (!code) {
+- *out_creds = ncreds;
+- return 0;
+- }
+-
+- /* Possibly try again with the canonicalized hostname, if the server is
+- * host-based and we are configured for fallback canonicalization. */
+- if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
++ /* Make and execute a krb5_tkt_creds context to get the credential. */
++ code = krb5_tkt_creds_init(context, ccache, in_creds, options, &ctx);
++ if (code != 0)
+ goto cleanup;
+- if (context->dns_canonicalize_hostname != CANONHOST_FALLBACK)
++ code = krb5_tkt_creds_get(context, ctx);
++ if (code != 0)
+ goto cleanup;
+- if (in_creds->server->type != KRB5_NT_SRV_HST ||
+- in_creds->server->length != 2)
++ code = krb5_tkt_creds_get_creds(context, ctx, ncreds);
++ if (code != 0)
+ goto cleanup;
+
+- hostname = k5memdup0(in_creds->server->data[1].data,
+- in_creds->server->data[1].length, &code);
+- if (hostname == NULL)
+- goto cleanup;
+- code = k5_expand_hostname(context, hostname, TRUE, &canon_hostname);
+- if (code)
+- goto cleanup;
+-
+- TRACE_GET_CREDS_FALLBACK(context, canon_hostname);
+-
+- /* Make shallow copies of in_creds and its server to alter the hostname. */
+- canon_components[0] = in_creds->server->data[0];
+- canon_components[1] = string2data(canon_hostname);
+- canon_server = *in_creds->server;
+- canon_server.data = canon_components;
+- canon_creds = *in_creds;
+- canon_creds.server = &canon_server;
+-
+- code = try_get_creds(context, options | KRB5_GC_NO_STORE, ccache,
+- &canon_creds, ncreds);
+- if (code)
+- goto cleanup;
+-
+- if (!(options & KRB5_GC_NO_STORE)) {
+- /* Store the creds under the originally requested server name. The
+- * ccache layer will also store them under the ticket server name. */
+- store_creds = *ncreds;
+- store_creds.server = in_creds->server;
+- (void)krb5_cc_store_cred(context, ccache, &store_creds);
+- }
+-
+ *out_creds = ncreds;
+ ncreds = NULL;
+
+ cleanup:
+- free(hostname);
+- free(canon_hostname);
+ krb5_free_creds(context, ncreds);
++ krb5_tkt_creds_free(context, ctx);
+ return code;
+ }
+diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c
+index cc0f70e83..09c4b8495 100644
+--- a/src/lib/krb5/krb/get_in_tkt.c
++++ b/src/lib/krb5/krb/get_in_tkt.c
+@@ -1051,9 +1051,6 @@ krb5_init_creds_init(krb5_context context,
+ ctx->request->kdc_options |= KDC_OPT_REQUEST_ANONYMOUS;
+ ctx->request->client->type = KRB5_NT_WELLKNOWN;
+ }
+- code = restart_init_creds_loop(context, ctx, FALSE);
+- if (code)
+- goto cleanup;
+
+ *pctx = ctx;
+ ctx = NULL;
+@@ -1859,6 +1856,10 @@ krb5_init_creds_step(krb5_context context,
+ }
+ if (code != 0 || ctx->complete)
+ goto cleanup;
++ } else {
++ code = restart_init_creds_loop(context, ctx, FALSE);
++ if (code)
++ goto cleanup;
+ }
+
+ code = init_creds_step_request(context, ctx, out);
+diff --git a/src/lib/krb5/krb/gic_keytab.c b/src/lib/krb5/krb/gic_keytab.c
+index 1d70cf46f..b2b4ac904 100644
+--- a/src/lib/krb5/krb/gic_keytab.c
++++ b/src/lib/krb5/krb/gic_keytab.c
+@@ -27,6 +27,7 @@
+
+ #include "k5-int.h"
+ #include "int-proto.h"
++#include "os-proto.h"
+ #include "init_creds_ctx.h"
+
+ static krb5_error_code
+@@ -85,7 +86,8 @@ get_as_key_keytab(krb5_context context,
+ /* Return the list of etypes available for client in keytab. */
+ static krb5_error_code
+ lookup_etypes_for_keytab(krb5_context context, krb5_keytab keytab,
+- krb5_principal client, krb5_enctype **etypes_out)
++ krb5_const_principal client,
++ krb5_enctype **etypes_out)
+ {
+ krb5_kt_cursor cursor;
+ krb5_keytab_entry entry;
+@@ -182,18 +184,37 @@ krb5_init_creds_set_keytab(krb5_context context,
+ {
+ krb5_enctype *etype_list;
+ krb5_error_code ret;
++ struct canonprinc iter = { ctx->request->client, .subst_defrealm = TRUE };
++ krb5_const_principal canonprinc;
++ krb5_principal copy;
+ char *name;
+
+ ctx->gak_fct = get_as_key_keytab;
+ ctx->gak_data = keytab;
+
+- ret = lookup_etypes_for_keytab(context, keytab, ctx->request->client,
+- &etype_list);
++ /* We may be authenticating as a host-based principal. If so, look for
++ * each canonicalization candidate in the keytab. */
++ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
++ canonprinc != NULL) {
++ ret = lookup_etypes_for_keytab(context, keytab, canonprinc,
++ &etype_list);
++ if (ret || etype_list != NULL)
++ break;
++ }
++ if (!ret && canonprinc != NULL) {
++ /* Authenticate as the principal we found in the keytab. */
++ ret = krb5_copy_principal(context, canonprinc, ©);
++ if (!ret) {
++ krb5_free_principal(context, ctx->request->client);
++ ctx->request->client = copy;
++ }
++ }
++ free_canonprinc(&iter);
+ if (ret) {
+ TRACE_INIT_CREDS_KEYTAB_LOOKUP_FAILED(context, ret);
+ return 0;
+ }
+- TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, etype_list);
++ TRACE_INIT_CREDS_KEYTAB_LOOKUP(context, ctx->request->client, etype_list);
+
+ /* Error out if we have no keys for the client principal. */
+ if (etype_list == NULL) {
+diff --git a/src/lib/krb5/krb/init_creds_ctx.h b/src/lib/krb5/krb/init_creds_ctx.h
+index 5bd67a1d8..17d55dd7c 100644
+--- a/src/lib/krb5/krb/init_creds_ctx.h
++++ b/src/lib/krb5/krb/init_creds_ctx.h
+@@ -22,6 +22,7 @@ struct _krb5_init_creds_context {
+ krb5_get_init_creds_opt opt_storage;
+ krb5_boolean identify_realm;
+ const krb5_data *subject_cert;
++ krb5_principal keytab_princ;
+ char *in_tkt_service;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
+index bc7fac455..013ca905c 100644
+--- a/src/lib/krb5/krb/rd_req_dec.c
++++ b/src/lib/krb5/krb/rd_req_dec.c
+@@ -33,6 +33,7 @@
+ #include "auth_con.h"
+ #include "authdata.h"
+ #include "int-proto.h"
++#include "os-proto.h"
+
+ /*
+ * essentially the same as krb_rd_req, but uses a decoded AP_REQ as
+@@ -351,9 +352,9 @@ try_one_princ(krb5_context context, const krb5_ap_req *req,
+ * Store the decrypting key in *keyblock_out if it is not NULL.
+ */
+ static krb5_error_code
+-decrypt_ticket(krb5_context context, const krb5_ap_req *req,
+- krb5_const_principal server, krb5_keytab keytab,
+- krb5_keyblock *keyblock_out)
++decrypt_try_server(krb5_context context, const krb5_ap_req *req,
++ krb5_const_principal server, krb5_keytab keytab,
++ krb5_keyblock *keyblock_out)
+ {
+ krb5_error_code ret;
+ krb5_keytab_entry ent;
+@@ -441,6 +442,35 @@ decrypt_ticket(krb5_context context, const krb5_ap_req *req,
+ #endif /* LEAN_CLIENT */
+ }
+
++static krb5_error_code
++decrypt_ticket(krb5_context context, const krb5_ap_req *req,
++ krb5_const_principal server, krb5_keytab keytab,
++ krb5_keyblock *keyblock_out)
++{
++ krb5_error_code ret, dret = 0;
++ struct canonprinc iter = { server, .no_hostrealm = TRUE };
++ krb5_const_principal canonprinc;
++
++ /* Don't try to canonicalize if we're going to ignore the hostname, or if
++ * server is null or has a wildcard hostname. */
++ if (context->ignore_acceptor_hostname || server == NULL ||
++ (server->length == 2 && server->data[1].length == 0))
++ return decrypt_try_server(context, req, server, keytab, keyblock_out);
++
++ /* Try each canonicalization candidate for server. If they all fail,
++ * return the error from the last attempt. */
++ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
++ canonprinc != NULL) {
++ dret = decrypt_try_server(context, req, canonprinc, keytab,
++ keyblock_out);
++ /* Only continue if we found no keytab entries matching canonprinc. */
++ if (dret != KRB5KRB_AP_ERR_NOKEY)
++ break;
++ }
++ free_canonprinc(&iter);
++ return (ret != 0) ? ret : dret;
++}
++
+ static krb5_error_code
+ rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
+ const krb5_ap_req *req, krb5_const_principal server,
+diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
+index d8f486dc6..07c7a98be 100644
+--- a/src/lib/krb5/krb/s4u_creds.c
++++ b/src/lib/krb5/krb/s4u_creds.c
+@@ -26,6 +26,7 @@
+
+ #include "k5-int.h"
+ #include "int-proto.h"
++#include "os-proto.h"
+
+ /* Convert ticket flags to necessary KDC options */
+ #define FLAGS2OPTS(flags) (flags & KDC_TKT_COMMON_MASK)
+@@ -984,10 +985,10 @@ get_target_realm_proxy_tgt(krb5_context context, const krb5_data
*realm,
+ return 0;
+ }
+
+-krb5_error_code
+-k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
+- krb5_ccache ccache, krb5_creds *in_creds,
+- krb5_creds **out_creds)
++static krb5_error_code
++get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
++ krb5_ccache ccache, krb5_creds *in_creds,
++ krb5_creds **out_creds)
+ {
+ krb5_error_code code;
+ krb5_flags flags, req_kdcopt = 0;
+@@ -1123,22 +1124,11 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags
options,
+ }
+ }
+
+- if (!krb5_principal_compare(context, in_creds->server, tkt->server)) {
+- krb5_free_principal(context, tkt->server);
+- tkt->server = NULL;
+- code = krb5_copy_principal(context, in_creds->server, &tkt->server);
+- if (code)
+- goto cleanup;
+- }
+-
+ /* Note the authdata we asked for in the output creds. */
+ code = krb5_copy_authdata(context, in_creds->authdata, &tkt->authdata);
+ if (code)
+ goto cleanup;
+
+- if (!(options & KRB5_GC_NO_STORE))
+- (void)krb5_cc_store_cred(context, ccache, tkt);
+-
+ *out_creds = tkt;
+ tkt = NULL;
+
+@@ -1151,6 +1141,48 @@ cleanup:
+ return code;
+ }
+
++krb5_error_code
++k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags options,
++ krb5_ccache ccache, krb5_creds *in_creds,
++ krb5_creds **out_creds)
++{
++ krb5_error_code code;
++ krb5_const_principal canonprinc;
++ krb5_creds copy, *creds;
++ struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
++
++ *out_creds = NULL;
++
++ copy = *in_creds;
++ while ((code = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
++ canonprinc != NULL) {
++ copy.server = (krb5_principal)canonprinc;
++ code = get_proxy_cred_from_kdc(context, options, ccache, ©,
++ &creds);
++ if (code != KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN)
++ break;
++ }
++ if (!code && canonprinc == NULL)
++ code = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
++ free_canonprinc(&iter);
++ if (code)
++ return code;
++
++ krb5_free_principal(context, creds->server);
++ creds->server = NULL;
++ code = krb5_copy_principal(context, in_creds->server, &creds->server);
++ if (code) {
++ krb5_free_creds(context, creds);
++ return code;
++ }
++
++ if (!(options & KRB5_GC_NO_STORE))
++ (void)krb5_cc_store_cred(context, ccache, creds);
++
++ *out_creds = creds;
++ return 0;
++}
++
+ /*
+ * Exported API for constrained delegation (S4U2Proxy).
+ *
+diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
+index a16a34b74..f1aa60a3e 100644
+--- a/src/lib/krb5/os/os-proto.h
++++ b/src/lib/krb5/os/os-proto.h
+@@ -83,6 +83,36 @@ struct sendto_callback_info {
+ void *data;
+ };
+
++/*
++ * Initialize with all zeros except for princ. Set no_hostrealm to disable
++ * host-to-realm lookup, which ordinarily happens after canonicalizing the host
++ * part. Set subst_defrealm to substitute the default realm for the referral
++ * realm after realm lookup (this has no effect if no_hostrealm is set). Free
++ * with free_canonprinc() when done.
++ */
++struct canonprinc {
++ krb5_const_principal princ;
++ krb5_boolean no_hostrealm;
++ krb5_boolean subst_defrealm;
++ int step;
++ char *canonhost;
++ char *realm;
++ krb5_principal_data copy;
++ krb5_data components[2];
++};
++
++/* Yield one or two candidate canonical principal names for iter, then NULL.
++ * Output names are valid for one iteration and must not be freed. */
++krb5_error_code k5_canonprinc(krb5_context context, struct canonprinc *iter,
++ krb5_const_principal *princ_out);
++
++static inline void
++free_canonprinc(struct canonprinc *iter)
++{
++ free(iter->canonhost);
++ free(iter->realm);
++}
++
+ krb5_error_code k5_expand_hostname(krb5_context context, const char *host,
+ krb5_boolean is_fallback,
+ char **canonhost_out);
+diff --git a/src/lib/krb5/os/sn2princ.c b/src/lib/krb5/os/sn2princ.c
+index a51761d0c..8b7214189 100644
+--- a/src/lib/krb5/os/sn2princ.c
++++ b/src/lib/krb5/os/sn2princ.c
+@@ -85,22 +85,18 @@ qualify_shortname(krb5_context context, const char *host)
+ return fqdn;
+ }
+
+-krb5_error_code
+-k5_expand_hostname(krb5_context context, const char *host,
+- krb5_boolean is_fallback, char **canonhost_out)
++static krb5_error_code
++expand_hostname(krb5_context context, const char *host, krb5_boolean use_dns,
++ char **canonhost_out)
+ {
+ struct addrinfo *ai = NULL, hint;
+ char namebuf[NI_MAXHOST], *qualified = NULL, *copy, *p;
+ int err;
+ const char *canonhost;
+- krb5_boolean use_dns;
+
+ *canonhost_out = NULL;
+
+ canonhost = host;
+- use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE ||
+- (is_fallback &&
+- context->dns_canonicalize_hostname == CANONHOST_FALLBACK));
+ if (use_dns) {
+ /* Try a forward lookup of the hostname. */
+ memset(&hint, 0, sizeof(hint));
+@@ -161,21 +157,135 @@ krb5_error_code KRB5_CALLCONV
+ krb5_expand_hostname(krb5_context context, const char *host,
+ char **canonhost_out)
+ {
+- return k5_expand_hostname(context, host, FALSE, canonhost_out);
++ int use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE);
++
++ return expand_hostname(context, host, use_dns, canonhost_out);
+ }
+
+-/* If hostname appears to have a :port or :instance trailer (used in MSSQLSvc
+- * principals), return a pointer to the separator. Otherwise return NULL. */
+-static const char *
+-find_trailer(const char *hostname)
++/* Split data into hostname and trailer (:port or :instance). Trailers are
++ * used in MSSQLSvc principals. */
++static void
++split_trailer(const krb5_data *data, krb5_data *host, krb5_data *trailer)
+ {
+- const char *p = strchr(hostname, ':');
++ char *p = memchr(data->data, ':', data->length);
++ unsigned int tlen = (p == NULL) ? 0 : data->length - (p - data->data);
+
+- /* Look for a single colon followed by one or more characters. An IPv6
+- * address will have more than one colon, so don't accept that. */
+- if (p == NULL || p[1] == '\0' || strchr(p + 1, ':') != NULL)
+- return NULL;
+- return p;
++ /* Make sure we have a single colon followed by one or more characters. An
++ * IPv6 address will have more than one colon, so don't accept that. */
++ if (p == NULL || tlen == 1 || memchr(p + 1, ':', tlen - 1) != NULL) {
++ *host = *data;
++ *trailer = empty_data();
++ } else {
++ *host = make_data(data->data, p - data->data);
++ *trailer = make_data(p, tlen);
++ }
++}
++
++static krb5_error_code
++canonicalize_princ(krb5_context context, struct canonprinc *iter,
++ krb5_boolean use_dns, krb5_const_principal *princ_out)
++{
++ krb5_error_code ret;
++ krb5_data host, trailer;
++ char *hostname = NULL, *canonhost = NULL, *combined = NULL;
++ char **hrealms = NULL;
++
++ *princ_out = NULL;
++
++ assert(iter->princ->length == 2);
++ split_trailer(&iter->princ->data[1], &host, &trailer);
++
++ hostname = k5memdup0(host.data, host.length, &ret);
++ if (hostname == NULL)
++ goto cleanup;
++
++ if (iter->princ->type == KRB5_NT_SRV_HST) {
++ /* Expand the hostname with or without DNS as specified. */
++ ret = expand_hostname(context, hostname, use_dns, &canonhost);
++ if (ret)
++ goto cleanup;
++ } else {
++ canonhost = strdup(hostname);
++ if (canonhost == NULL) {
++ ret = ENOMEM;
++ goto cleanup;
++ }
++ }
++
++ /* Add the trailer to the expanded hostname. */
++ if (asprintf(&combined, "%s%.*s", canonhost,
++ trailer.length, trailer.data) < 0) {
++ combined = NULL;
++ ret = ENOMEM;
++ goto cleanup;
++ }
++
++ /* Don't yield the same host part twice. */
++ if (iter->canonhost != NULL && strcmp(iter->canonhost, combined) ==
0)
++ goto cleanup;
++
++ free(iter->canonhost);
++ iter->canonhost = combined;
++ combined = NULL;
++
++ /* If the realm is unknown, look up the realm of the expanded hostname. */
++ if (iter->princ->realm.length == 0 && !iter->no_hostrealm) {
++ ret = krb5_get_host_realm(context, canonhost, &hrealms);
++ if (ret)
++ goto cleanup;
++ if (hrealms[0] == NULL) {
++ ret = KRB5_ERR_HOST_REALM_UNKNOWN;
++ goto cleanup;
++ }
++ free(iter->realm);
++ if (*hrealms[0] == '\0' && iter->subst_defrealm) {
++ ret = krb5_get_default_realm(context, &iter->realm);
++ if (ret)
++ goto cleanup;
++ } else {
++ iter->realm = strdup(hrealms[0]);
++ if (iter->realm == NULL) {
++ ret = ENOMEM;
++ goto cleanup;
++ }
++ }
++ }
++
++ iter->copy = *iter->princ;
++ if (iter->realm != NULL)
++ iter->copy.realm = string2data(iter->realm);
++ iter->components[0] = iter->princ->data[0];
++ iter->components[1] = string2data(iter->canonhost);
++ iter->copy.data = iter->components;
++ *princ_out = &iter->copy;
++
++cleanup:
++ free(hostname);
++ free(canonhost);
++ free(combined);
++ krb5_free_host_realm(context, hrealms);
++ return ret;
++}
++
++krb5_error_code
++k5_canonprinc(krb5_context context, struct canonprinc *iter,
++ krb5_const_principal *princ_out)
++{
++ int step = ++iter->step;
++
++ *princ_out = NULL;
++
++ /* If we're not doing fallback, the input principal is canonical. */
++ if (context->dns_canonicalize_hostname != CANONHOST_FALLBACK ||
++ iter->princ->type != KRB5_NT_SRV_HST || iter->princ->length != 2) {
++ *princ_out = (step == 1) ? iter->princ : NULL;
++ return 0;
++ }
++
++ /* Canonicalize without DNS at step 1, with DNS at step 2. */
++ if (step > 2)
++ return 0;
++ return canonicalize_princ(context, iter, step == 2, princ_out);
+ }
+
+ krb5_error_code KRB5_CALLCONV
+@@ -185,9 +295,10 @@ krb5_sname_to_principal(krb5_context context, const char *hostname,
+ {
+ krb5_error_code ret;
+ krb5_principal princ;
+- const char *realm, *trailer;
+- char **hrealms = NULL, *canonhost = NULL, *hostonly = NULL, *concat = NULL;
++ krb5_const_principal cprinc;
++ krb5_boolean use_dns;
+ char localname[MAXHOSTNAMELEN];
++ struct canonprinc iter = { NULL };
+
+ *princ_out = NULL;
+
+@@ -205,54 +316,26 @@ krb5_sname_to_principal(krb5_context context, const char
*hostname,
+ if (sname == NULL)
+ sname = "host";
+
+- /* If there is a trailer, remove it for now. */
+- trailer = find_trailer(hostname);
+- if (trailer != NULL) {
+- hostonly = k5memdup0(hostname, trailer - hostname, &ret);
+- if (hostonly == NULL)
+- goto cleanup;
+- hostname = hostonly;
+- }
+-
+- /* Canonicalize the hostname if appropriate. */
+- if (type == KRB5_NT_SRV_HST) {
+- ret = krb5_expand_hostname(context, hostname, &canonhost);
+- if (ret)
+- goto cleanup;
+- hostname = canonhost;
+- }
+-
+- /* Find the realm of the host. */
+- ret = krb5_get_host_realm(context, hostname, &hrealms);
++ /* Build an initial principal with what we have. */
++ ret = krb5_build_principal(context, &princ, 0, KRB5_REFERRAL_REALM,
++ sname, hostname, (char *)NULL);
+ if (ret)
+- goto cleanup;
+- if (hrealms[0] == NULL) {
+- ret = KRB5_ERR_HOST_REALM_UNKNOWN;
+- goto cleanup;
+- }
+- realm = hrealms[0];
+-
+- /* If there was a trailer, put it back on the end. */
+- if (trailer != NULL) {
+- if (asprintf(&concat, "%s%s", hostname, trailer) < 0) {
+- ret = ENOMEM;
+- goto cleanup;
+- }
+- hostname = concat;
+- }
+-
+- ret = krb5_build_principal(context, &princ, strlen(realm), realm, sname,
+- hostname, (char *)NULL);
+- if (ret)
+- goto cleanup;
+-
++ return ret;
+ princ->type = type;
+- *princ_out = princ;
+
+-cleanup:
+- free(hostonly);
+- free(canonhost);
+- free(concat);
+- krb5_free_host_realm(context, hrealms);
++ if (type == KRB5_NT_SRV_HST &&
++ context->dns_canonicalize_hostname == CANONHOST_FALLBACK) {
++ /* Delay canonicalization and realm lookup until use. */
++ *princ_out = princ;
++ return 0;
++ }
++
++ use_dns = (context->dns_canonicalize_hostname == CANONHOST_TRUE);
++ iter.princ = princ;
++ ret = canonicalize_princ(context, &iter, use_dns, &cprinc);
++ if (!ret)
++ ret = krb5_copy_principal(context, cprinc, princ_out);
++ free_canonprinc(&iter);
++ krb5_free_principal(context, princ);
+ return ret;
+ }
+diff --git a/src/tests/icred.c b/src/tests/icred.c
+index 55f929cd7..d6ce1d5d3 100644
+--- a/src/tests/icred.c
++++ b/src/tests/icred.c
+@@ -30,10 +30,7 @@
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+-/*
+- * This program exercises the init_creds APIs in ways kinit doesn't. Right now
+- * it is very simplistic, but it can be extended as needed.
+- */
++/* This program exercises the init_creds APIs in ways kinit doesn't. */
+
+ #include "k5-platform.h"
+ #include <krb5.h>
+@@ -56,10 +53,11 @@ check(krb5_error_code code)
+ int
+ main(int argc, char **argv)
+ {
+- const char *princstr, *password;
++ const char *ktname = NULL, *sname = NULL, *princstr, *password;
+ krb5_principal client;
+ krb5_init_creds_context icc;
+ krb5_get_init_creds_opt *opt;
++ krb5_keytab keytab = NULL;
+ krb5_creds creds;
+ krb5_boolean stepwise = FALSE;
+ krb5_preauthtype ptypes[64];
+@@ -69,8 +67,11 @@ main(int argc, char **argv)
+ check(krb5_init_context(&ctx));
+ check(krb5_get_init_creds_opt_alloc(ctx, &opt));
+
+- while ((c = getopt(argc, argv, "so:X:")) != -1) {
++ while ((c = getopt(argc, argv, "k:so:S:X:")) != -1) {
+ switch (c) {
++ case 'k':
++ ktname = optarg;
++ break;
+ case 's':
+ stepwise = TRUE;
+ break;
+@@ -78,6 +79,9 @@ main(int argc, char **argv)
+ assert(nptypes < 64);
+ ptypes[nptypes++] = atoi(optarg);
+ break;
++ case 'S':
++ sname = optarg;
++ break;
+ case 'X':
+ val = strchr(optarg, '=');
+ if (val != NULL)
+@@ -93,12 +97,20 @@ main(int argc, char **argv)
+
+ argc -= optind;
+ argv += optind;
+- if (argc != 2)
++ if (argc != 1 && argc != 2)
+ abort();
+ princstr = argv[0];
+ password = argv[1];
+
+- check(krb5_parse_name(ctx, princstr, &client));
++ if (sname != NULL) {
++ check(krb5_sname_to_principal(ctx, princstr, sname, KRB5_NT_SRV_HST,
++ &client));
++ } else {
++ check(krb5_parse_name(ctx, princstr, &client));
++ }
++
++ if (ktname != NULL)
++ check(krb5_kt_resolve(ctx, ktname, &keytab));
+
+ if (nptypes > 0)
+ krb5_get_init_creds_opt_set_preauth_list(opt, ptypes, nptypes);
+@@ -106,9 +118,16 @@ main(int argc, char **argv)
+ if (stepwise) {
+ /* Use the stepwise interface. */
+ check(krb5_init_creds_init(ctx, client, NULL, NULL, 0, NULL, &icc));
+- check(krb5_init_creds_set_password(ctx, icc, password));
++ if (keytab != NULL)
++ check(krb5_init_creds_set_keytab(ctx, icc, keytab));
++ if (password != NULL)
++ check(krb5_init_creds_set_password(ctx, icc, password));
+ check(krb5_init_creds_get(ctx, icc));
+ krb5_init_creds_free(ctx, icc);
++ } else if (keytab != NULL) {
++ check(krb5_get_init_creds_keytab(ctx, &creds, client, keytab, 0, NULL,
++ opt));
++ krb5_free_cred_contents(ctx, &creds);
+ } else {
+ /* Use the traditional one-shot interface. */
+ check(krb5_get_init_creds_password(ctx, &creds, client, password, NULL,
+@@ -116,6 +135,8 @@ main(int argc, char **argv)
+ krb5_free_cred_contents(ctx, &creds);
+ }
+
++ if (keytab != NULL)
++ krb5_kt_close(ctx, keytab);
+ krb5_get_init_creds_opt_free(ctx, opt);
+ krb5_free_principal(ctx, client);
+ krb5_free_context(ctx);
+diff --git a/src/tests/t_sn2princ.py b/src/tests/t_sn2princ.py
+index f3e187286..493fba219 100755
+--- a/src/tests/t_sn2princ.py
++++ b/src/tests/t_sn2princ.py
+@@ -85,28 +85,9 @@ if offline:
+ oname = 'ptr-mismatch.kerberos.org'
+ fname = 'www.kerberos.org'
+
+-# Test fallback canonicalization krb5_sname_to_principal() results
+-# (same as dns_canonicalize_hostname=false).
++# Test fallback canonicalization krb5_sname_to_principal() results.
+ mark('dns_canonicalize_host=fallback')
+-testfc(oname, oname, 'R1')
+-
+-# Test fallback canonicalization in krb5_get_credentials().
+-oprinc = 'host/' + oname
+-fprinc = 'host/' + fname
+-shutil.copy(realm.ccache, realm.ccache + '.save')
+-realm.addprinc(fprinc)
+-# oprinc doesn't exist, so we get the canonicalized fprinc as a fallback.
+-msgs = ('Falling back to canonicalized server hostname ' + fname,)
+-realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
+- expected_msg=fprinc, expected_trace=msgs)
+-realm.addprinc(oprinc)
+-# oprinc now exists, but we still get the fprinc ticket from the cache.
+-realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
+- expected_msg=fprinc)
+-# Without the cached result, we sould get oprinc in preference to fprinc.
+-os.rename(realm.ccache + '.save', realm.ccache)
+-realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
+- expected_msg=oprinc)
++testfc(oname, oname, '')
+
+ # Verify forward resolution before testing for it.
+ try:
+@@ -118,6 +99,48 @@ if canonname.lower() != fname:
+ skip_rest('sn2princ tests',
+ '%s forward resolves to %s, not %s' % (oname, canonname, fname))
+
++# Test fallback canonicalization in krb5_get_credentials().
++oprinc = 'host/' + oname
++fprinc = 'host/' + fname
++shutil.copy(realm.ccache, realm.ccache + '.save')
++# Test that we only try fprinc once if we enter it as input.
++out, trace = realm.run(['./gcred', 'srv-hst', fprinc + '@'],
++ env=fallback_canon, expected_code=1, return_trace=True)
++msg = 'Requesting tickets for %s@R1, referrals on' % fprinc
++if trace.count(msg) != 1:
++ fail('Expected one try for %s' % fprinc)
++# Create fprinc, and verify that we get it as the canonicalized
++# fallback for oprinc.
++realm.addprinc(fprinc)
++msgs = ('Getting credentials user@R1 -> %s@ using' % oprinc,
++ 'Requesting tickets for %s@R1' % oprinc,
++ 'Requesting tickets for %s@R1' % fprinc,
++ 'Received creds for desired service %s@R1' % fprinc)
++realm.run(['./gcred', 'srv-hst', oprinc + '@'],
env=fallback_canon,
++ expected_msg=fprinc, expected_trace=msgs)
++realm.addprinc(oprinc)
++# oprinc now exists, but we still get the fprinc ticket from the cache.
++realm.run(['./gcred', 'srv-hst', oprinc + '@'],
env=fallback_canon,
++ expected_msg=fprinc)
++# Without the cached result, we sould get oprinc in preference to fprinc.
++os.rename(realm.ccache + '.save', realm.ccache)
++realm.run(['./gcred', 'srv-hst', oprinc], env=fallback_canon,
++ expected_msg=oprinc)
++
++# Test fallback canonicalization for krb5_rd_req().
++realm.run([kadminl, 'ktadd', fprinc])
++msgs = ('Decrypted AP-REQ with server principal %s@R1' % fprinc,
++ 'AP-REQ ticket: user@R1 -> %s@R1' % fprinc)
++realm.run(['./rdreq', fprinc, oprinc + '@'], env=fallback_canon,
++ expected_trace=msgs)
++
++# Test fallback canonicalization for getting initial creds with a keytab.
++msgs = ('Getting initial credentials for %s@' % oprinc,
++ 'Found entries for %s@R1 in keytab' % fprinc,
++ 'Retrieving %s@R1 from ' % fprinc)
++realm.run(['./icred', '-k', realm.keytab, '-S', 'host',
oname],
++ env=fallback_canon, expected_trace=msgs)
++
+ # Test forward-only canonicalization (rdns=false).
+ mark('rdns=false')
+ testnr(oname, fname, 'R1')
diff --git a/Prevent-deletion-of-K-M.patch b/Prevent-deletion-of-K-M.patch
new file mode 100644
index 0000000..5e2c117
--- /dev/null
+++ b/Prevent-deletion-of-K-M.patch
@@ -0,0 +1,45 @@
+From 7986adf30dffdd16fec43f261a2fa1384e0b8b90 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Sat, 13 Jun 2020 21:55:54 -0400
+Subject: [PATCH] Prevent deletion of K/M
+
+In libkadm5srv, do not allow deletion of the master key principal, as
+it is very difficult to recover a KDB after doing so.
+
+ticket: 8913
+(cherry picked from commit 94b936a1bf0a8c67809597c5ea5400d8994d5dd8)
+---
+ src/lib/kadm5/srv/svr_principal.c | 4 ++++
+ src/tests/t_kadmin_acl.py | 6 ++++++
+ 2 files changed, 10 insertions(+)
+
+diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
+index 53ecbe1bc..c2412df31 100644
+--- a/src/lib/kadm5/srv/svr_principal.c
++++ b/src/lib/kadm5/srv/svr_principal.c
+@@ -537,6 +537,10 @@ kadm5_delete_principal(void *server_handle, krb5_principal
principal)
+ if (principal == NULL)
+ return EINVAL;
+
++ /* Deleting K/M is mostly unrecoverable, so don't allow it. */
++ if (krb5_principal_compare(handle->context, principal, master_princ))
++ return KADM5_PROTECT_PRINCIPAL;
++
+ if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+ return(ret);
+ ret = k5_kadm5_hook_remove(handle->context, handle->hook_handles,
+diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
+index 86eb59729..8946e8cc4 100755
+--- a/src/tests/t_kadmin_acl.py
++++ b/src/tests/t_kadmin_acl.py
+@@ -328,4 +328,10 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw',
'-randkey', 'none'],
+ realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey',
'-e', 'aes256-cts',
+ 'none'], expected_code=1, expected_msg=msg)
+
++# Test operations disallowed at the libkadm5 layer.
++realm.run([kadminl, 'delprinc', 'K/M'],
++ expected_code=1, expected_msg='Cannot change protected principal')
++realm.run([kadminl, 'cpw', '-pw', 'pw',
'kadmin/history'],
++ expected_code=1, expected_msg='Cannot change protected principal')
++
+ success('kadmin ACL enforcement')
diff --git a/Refactor-cache-checking-in-TGS-client-code.patch
b/Refactor-cache-checking-in-TGS-client-code.patch
new file mode 100644
index 0000000..86315ec
--- /dev/null
+++ b/Refactor-cache-checking-in-TGS-client-code.patch
@@ -0,0 +1,188 @@
+From 0cffa5904341460353fa7f99b5aa514fc27a10eb Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Thu, 23 Jul 2020 01:52:43 -0400
+Subject: [PATCH] Refactor cache checking in TGS client code
+
+(cherry picked from commit 8f2f0a2e8f65c4b39883129967301e3a8986218b)
+---
+ src/lib/krb5/krb/get_creds.c | 86 +++++++++++++++++++++---------------
+ src/lib/krb5/krb/int-proto.h | 6 +--
+ src/lib/krb5/krb/s4u_creds.c | 21 +--------
+ 3 files changed, 55 insertions(+), 58 deletions(-)
+
+diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c
+index b3f01be9b..32401bcb1 100644
+--- a/src/lib/krb5/krb/get_creds.c
++++ b/src/lib/krb5/krb/get_creds.c
+@@ -48,10 +48,10 @@
+ * and options. The fields of *mcreds will be aliased to the fields
+ * of in_creds, so the contents of *mcreds should not be freed.
+ */
+-krb5_error_code
+-krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
+- krb5_creds *in_creds, krb5_creds *mcreds,
+- krb5_flags *fields)
++static krb5_error_code
++construct_matching_creds(krb5_context context, krb5_flags options,
++ krb5_creds *in_creds, krb5_creds *mcreds,
++ krb5_flags *fields)
+ {
+ if (!in_creds || !in_creds->server || !in_creds->client)
+ return EINVAL;
+@@ -110,6 +110,50 @@ krb5int_construct_matching_creds(krb5_context context, krb5_flags
options,
+ return 0;
+ }
+
++/* Simple wrapper around krb5_cc_retrieve_cred which allocates the result
++ * container. */
++static krb5_error_code
++cache_get(krb5_context context, krb5_ccache ccache, krb5_flags flags,
++ krb5_creds *in_creds, krb5_creds **out_creds)
++{
++ krb5_error_code code;
++ krb5_creds *creds;
++
++ *out_creds = NULL;
++
++ creds = malloc(sizeof(*creds));
++ if (creds == NULL)
++ return ENOMEM;
++
++ code = krb5_cc_retrieve_cred(context, ccache, flags, in_creds, creds);
++ if (code != 0) {
++ free(creds);
++ return code;
++ }
++
++ *out_creds = creds;
++ return 0;
++}
++
++krb5_error_code
++k5_get_cached_cred(krb5_context context, krb5_flags options,
++ krb5_ccache ccache, krb5_creds *in_creds,
++ krb5_creds **creds_out)
++{
++ krb5_error_code code;
++ krb5_creds mcreds;
++ krb5_flags fields;
++
++ *creds_out = NULL;
++
++ code = construct_matching_creds(context, options, in_creds,
++ &mcreds, &fields);
++ if (code)
++ return code;
++
++ return cache_get(context, ccache, fields, &mcreds, creds_out);
++}
++
+ /*
+ * krb5_tkt_creds_step() is implemented using a tail call style. Every
+ * begin_*, step_*, or *_request function is responsible for returning an
+@@ -235,31 +279,6 @@ cleanup:
+ return code;
+ }
+
+-/* Simple wrapper around krb5_cc_retrieve_cred which allocates the result
+- * container. */
+-static krb5_error_code
+-cache_get(krb5_context context, krb5_ccache ccache, krb5_flags flags,
+- krb5_creds *in_creds, krb5_creds **out_creds)
+-{
+- krb5_error_code code;
+- krb5_creds *creds;
+-
+- *out_creds = NULL;
+-
+- creds = malloc(sizeof(*creds));
+- if (creds == NULL)
+- return ENOMEM;
+-
+- code = krb5_cc_retrieve_cred(context, ccache, flags, in_creds, creds);
+- if (code != 0) {
+- free(creds);
+- return code;
+- }
+-
+- *out_creds = creds;
+- return 0;
+-}
+-
+ /*
+ * Set up the request given by ctx->tgs_in_creds, using ctx->cur_tgt. KDC
+ * options for the requests are determined by ctx->cur_tgt->ticket_flags and
+@@ -1023,18 +1042,13 @@ static krb5_error_code
+ check_cache(krb5_context context, krb5_tkt_creds_context ctx)
+ {
+ krb5_error_code code;
+- krb5_creds mcreds;
+- krb5_flags fields;
+ krb5_creds req_in_creds;
+
+ /* Check the cache for the originally requested server principal. */
+ req_in_creds = *ctx->in_creds;
+ req_in_creds.server = ctx->req_server;
+- code = krb5int_construct_matching_creds(context, ctx->req_options,
+- &req_in_creds, &mcreds,
&fields);
+- if (code)
+- return code;
+- code = cache_get(context, ctx->ccache, fields, &mcreds,
&ctx->reply_creds);
++ code = k5_get_cached_cred(context, ctx->req_options, ctx->ccache,
++ &req_in_creds, &ctx->reply_creds);
+ if (code == 0) {
+ ctx->state = STATE_COMPLETE;
+ return 0;
+diff --git a/src/lib/krb5/krb/int-proto.h b/src/lib/krb5/krb/int-proto.h
+index fe61bebf5..5211044dc 100644
+--- a/src/lib/krb5/krb/int-proto.h
++++ b/src/lib/krb5/krb/int-proto.h
+@@ -79,9 +79,9 @@ clpreauth_otp_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable);
+
+ krb5_error_code
+-krb5int_construct_matching_creds(krb5_context context, krb5_flags options,
+- krb5_creds *in_creds, krb5_creds *mcreds,
+- krb5_flags *fields);
++k5_get_cached_cred(krb5_context context, krb5_flags options,
++ krb5_ccache ccache, krb5_creds *in_creds,
++ krb5_creds **creds_out);
+
+ #define IS_TGS_PRINC(p) ((p)->length == 2 && \
+ data_eq_string((p)->data[0], KRB5_TGS_NAME))
+diff --git a/src/lib/krb5/krb/s4u_creds.c b/src/lib/krb5/krb/s4u_creds.c
+index d44f939f8..eadb37cd0 100644
+--- a/src/lib/krb5/krb/s4u_creds.c
++++ b/src/lib/krb5/krb/s4u_creds.c
+@@ -1155,29 +1155,12 @@ k5_get_proxy_cred_from_kdc(krb5_context context, krb5_flags
options,
+ {
+ krb5_error_code code;
+ krb5_const_principal canonprinc;
+- krb5_creds mcreds, copy, *creds, *ncreds;
+- krb5_flags fields;
++ krb5_creds copy, *creds;
+ struct canonprinc iter = { in_creds->server, .no_hostrealm = TRUE };
+
+ *out_creds = NULL;
+
+- code = krb5int_construct_matching_creds(context, options, in_creds,
+- &mcreds, &fields);
+- if (code != 0)
+- return code;
+-
+- ncreds = calloc(1, sizeof(*ncreds));
+- if (ncreds == NULL)
+- return ENOMEM;
+- ncreds->magic = KV5M_CRED;
+-
+- code = krb5_cc_retrieve_cred(context, ccache, fields, &mcreds, ncreds);
+- if (code) {
+- free(ncreds);
+- } else {
+- *out_creds = ncreds;
+- }
+-
++ code = k5_get_cached_cred(context, options, ccache, in_creds, out_creds);
+ if ((code != KRB5_CC_NOTFOUND && code != KRB5_CC_NOT_KTYPE) ||
+ options & KRB5_GC_CACHED)
+ return code;
diff --git a/Try-kadmin-admin-first-in-libkadm5clnt.patch
b/Try-kadmin-admin-first-in-libkadm5clnt.patch
new file mode 100644
index 0000000..6249ff5
--- /dev/null
+++ b/Try-kadmin-admin-first-in-libkadm5clnt.patch
@@ -0,0 +1,159 @@
+From fae915ea7f2734cfd9ef3d5952f25638e675bb7c Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Mon, 27 Jul 2020 01:19:01 -0400
+Subject: [PATCH] Try kadmin/admin first in libkadm5clnt
+
+The MIT krb5 kadmin protocol originally used kadmin/admin as the
+service principal. Commits 493f0da5fbf92b0ac2f10e887706d1964d8a15e8
+and 5cfaec38a8e8f1c4b76228ba0a252987af797ca4 changed it to use
+kadmin/hostname preferentially, with kadmin/admin as a fallback, for
+interoperability with the Solaris SEAM administrative protocol.
+
+Change the preference order so that kadmin/admin is tried first, with
+kadmin/hostname as a fallback.
+
+ticket: 8934 (new)
+(cherry picked from commit 1d282badfbd6098e3db9d50d22d565c2ec3c8c47)
+---
+ doc/admin/admin_commands/kadmin_local.rst | 14 ++++++------
+ doc/admin/database.rst | 10 ++++-----
+ src/lib/kadm5/clnt/client_init.c | 26 +++++++++--------------
+ src/tests/t_kadmin_acl.py | 14 ++++++++++++
+ 4 files changed, 36 insertions(+), 28 deletions(-)
+
+diff --git a/doc/admin/admin_commands/kadmin_local.rst
b/doc/admin/admin_commands/kadmin_local.rst
+index fafa61365..33cf3a9cb 100644
+--- a/doc/admin/admin_commands/kadmin_local.rst
++++ b/doc/admin/admin_commands/kadmin_local.rst
+@@ -44,9 +44,9 @@ Kerberos principals, password policies, and service key tables
+ (keytabs).
+
+ The remote kadmin client uses Kerberos to authenticate to kadmind
+-using the service principal ``kadmin/ADMINHOST`` (where *ADMINHOST* is
+-the fully-qualified hostname of the admin server) or ``kadmin/admin``.
+-If the credentials cache contains a ticket for one of these
++using the service principal ``kadmin/admin`` or ``kadmin/ADMINHOST``
++(where *ADMINHOST* is the fully-qualified hostname of the admin
++server). If the credentials cache contains a ticket for one of these
+ principals, and the **-c** credentials_cache option is specified, that
+ ticket is used to authenticate to kadmind. Otherwise, the **-p** and
+ **-k** options are used to specify the client Kerberos principal name
+@@ -100,10 +100,10 @@ OPTIONS
+ fully anonymous operation.
+
+ **-c** *credentials_cache*
+- Use *credentials_cache* as the credentials cache. The
+- cache should contain a service ticket for the ``kadmin/ADMINHOST``
+- (where *ADMINHOST* is the fully-qualified hostname of the admin
+- server) or ``kadmin/admin`` service; it can be acquired with the
++ Use *credentials_cache* as the credentials cache. The cache
++ should contain a service ticket for the ``kadmin/admin`` or
++ ``kadmin/ADMINHOST`` (where *ADMINHOST* is the fully-qualified
++ hostname of the admin server) service; it can be acquired with the
+ :ref:`kinit(1)` program. If this option is not specified, kadmin
+ requests a new service ticket from the KDC, and stores it in its
+ own temporary ccache.
+diff --git a/doc/admin/database.rst b/doc/admin/database.rst
+index e62cef7a7..ca19a362a 100644
+--- a/doc/admin/database.rst
++++ b/doc/admin/database.rst
+@@ -26,8 +26,8 @@ local filesystem (or through LDAP). kadmin.local is necessary to set
+ up enough of the database to be able to use the remote version.
+
+ kadmin can authenticate to the admin server using the service
+-principal ``kadmin/HOST`` (where *HOST* is the hostname of the admin
+-server) or ``kadmin/admin``. If the credentials cache contains a
++principal ``kadmin/admin`` or ``kadmin/HOST`` (where *HOST* is the
++hostname of the admin server). If the credentials cache contains a
+ ticket for either service principal and the **-c** ccache option is
+ specified, that ticket is used to authenticate to KADM5. Otherwise,
+ the **-p** and **-k** options are used to specify the client Kerberos
+@@ -811,9 +811,9 @@ Both master and replica sides must have a principal named
+ ``kiprop/hostname`` (where *hostname* is the lowercase,
+ fully-qualified, canonical name for the host) registered in the
+ Kerberos database, and have keys for that principal stored in the
+-default keytab file (|keytab|). In release 1.13, the
+-``kiprop/hostname`` principal is created automatically for the master
+-KDC, but it must still be created for replica KDCs.
++default keytab file (|keytab|). The ``kiprop/hostname`` principal may
++have been created automatically for the master KDC, but it must always
++be created for replica KDCs.
+
+ On the master KDC side, the ``kiprop/hostname`` principal must be
+ listed in the kadmind ACL file :ref:`kadm5.acl(5)`, and given the
+diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c
+index aa08918e2..8d43ab97a 100644
+--- a/src/lib/kadm5/clnt/client_init.c
++++ b/src/lib/kadm5/clnt/client_init.c
+@@ -372,22 +372,10 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal
client,
+ {
+ kadm5_ret_t code;
+ krb5_ccache ccache = NULL;
+- char svcname[BUFSIZ];
++ char *svcname, svcbuf[BUFSIZ];
+
+ *server_out = NULL;
+
+- /* NULL svcname means use host-based. */
+- if (svcname_in == NULL) {
+- code = kadm5_get_admin_service_name(handle->context,
+- handle->params.realm,
+- svcname, sizeof(svcname));
+- if (code)
+- goto error;
+- } else {
+- strncpy(svcname, svcname_in, sizeof(svcname));
+- svcname[sizeof(svcname)-1] = '\0';
+- }
+-
+ /*
+ * Acquire a service ticket for svcname@realm for client, using password
+ * pass (which could be NULL), and create a ccache to store them in. If
+@@ -423,13 +411,19 @@ get_init_creds(kadm5_server_handle_t handle, krb5_principal
client,
+ }
+ handle->lhandle->cache_name = handle->cache_name;
+
++ svcname = (svcname_in != NULL) ? svcname_in : KADM5_ADMIN_SERVICE;
+ code = gic_iter(handle, init_type, ccache, client, pass, svcname, realm,
+ server_out);
+ if ((code == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN
+ || code == KRB5_CC_NOTFOUND) && svcname_in == NULL) {
+- /* Retry with old host-independent service principal. */
+- code = gic_iter(handle, init_type, ccache, client, pass,
+- KADM5_ADMIN_SERVICE, realm, server_out);
++ /* Retry with host-based service principal. */
++ code = kadm5_get_admin_service_name(handle->context,
++ handle->params.realm,
++ svcbuf, sizeof(svcbuf));
++ if (code)
++ goto error;
++ code = gic_iter(handle, init_type, ccache, client, pass, svcbuf, realm,
++ server_out);
+ }
+ /* Improved error messages */
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) code = KADM5_BAD_PASSWORD;
+diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
+index 8946e8cc4..16faf0a9d 100755
+--- a/src/tests/t_kadmin_acl.py
++++ b/src/tests/t_kadmin_acl.py
+@@ -328,6 +328,20 @@ realm.run([kadmin, '-c', realm.ccache, 'cpw',
'-randkey', 'none'],
+ realm.run([kadmin, '-c', realm.ccache, 'cpw', '-randkey',
'-e', 'aes256-cts',
+ 'none'], expected_code=1, expected_msg=msg)
+
++# Test authentication to kadmin/hostname.
++mark('authentication to kadmin/hostname')
++kadmin_hostname = 'kadmin/' + hostname
++realm.run([kadminl, 'delprinc', 'kadmin/admin'])
++msgs = ('Getting initial credentials for user/admin(a)KRBTEST.COM',
++ 'Setting initial creds service to kadmin/admin',
++ '/Server not found in Kerberos database',
++ 'Getting initial credentials for user/admin(a)KRBTEST.COM',
++ 'Setting initial creds service to ' + kadmin_hostname,
++ 'Decrypted AS reply')
++realm.run([kadmin, '-p', 'user/admin', 'listprincs'],
expected_code=1,
++ expected_msg="Operation requires ``list'' privilege",
++ input=password('user/admin'), expected_trace=msgs)
++
+ # Test operations disallowed at the libkadm5 layer.
+ realm.run([kadminl, 'delprinc', 'K/M'],
+ expected_code=1, expected_msg='Cannot change protected principal')
diff --git a/krb5.spec b/krb5.spec
index 7199f35..081299e 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: 18%{?dist}
+Release: 19%{?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}....
@@ -71,6 +71,12 @@ Patch32: Use-two-queues-for-concurrent-t_otp.py-daemons.patch
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: Prevent-deletion-of-K-M.patch
+Patch37: Try-kadmin-admin-first-in-libkadm5clnt.patch
+Patch38: Don-t-create-hostbased-principals-in-new-KDBs.patch
+Patch39: Expand-dns_canonicalize_host-fallback-support.patch
+Patch40: Cache-S4U2Proxy-requests-by-second-ticket.patch
+Patch41: Refactor-cache-checking-in-TGS-client-code.patch
License: MIT
URL:
https://web.mit.edu/kerberos/www/
@@ -632,6 +638,9 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
+* Fri Aug 07 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-19
+- Expand dns_canonicalize_hostname=fallback support
+
* Tue Aug 04 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-18
- Fix leak in KERB_AP_OPTIONS_CBT server support
commit 2091f2939975551a5bfbe8b53ee5d74fa588f03e
Author: Robbie Harwood <rharwood(a)redhat.com>
Date: Tue Aug 4 14:24:08 2020 -0400
Fix leak in KERB_AP_OPTIONS_CBT server support
diff --git a/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch
b/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch
new file mode 100644
index 0000000..54f2550
--- /dev/null
+++ b/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch
@@ -0,0 +1,59 @@
+From 044e2209586fd1935d9a637df76d52f48c4f3e6e Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Fri, 24 Jul 2020 16:05:24 -0400
+Subject: [PATCH] Fix leak in KERB_AP_OPTIONS_CBT server support
+
+In check_cbt(), use a local variable to hold the retrieved authdata
+list, and free it before returning.
+
+ticket: 8900
+(cherry picked from commit bf2ddff13c178e0c291f8fb382b040080d159e4f)
+---
+ src/lib/gssapi/krb5/accept_sec_context.c | 23 +++++++++++++----------
+ 1 file changed, 13 insertions(+), 10 deletions(-)
+
+diff --git a/src/lib/gssapi/krb5/accept_sec_context.c
b/src/lib/gssapi/krb5/accept_sec_context.c
+index 175a24c4e..3d5b84b15 100644
+--- a/src/lib/gssapi/krb5/accept_sec_context.c
++++ b/src/lib/gssapi/krb5/accept_sec_context.c
+@@ -433,27 +433,30 @@ static const uint8_t null_cb[CB_MD5_LEN];
+ /* Look for AP_OPTIONS in authdata. If present and the options include
+ * KERB_AP_OPTIONS_CBT, set *cbt_out to true. */
+ static krb5_error_code
+-check_cbt(krb5_context context, krb5_authdata **authdata,
++check_cbt(krb5_context context, krb5_authdata *const *authdata,
+ krb5_boolean *cbt_out)
+ {
+ krb5_error_code code;
++ krb5_authdata **ad;
+ uint32_t ad_ap_options;
+ const uint32_t KERB_AP_OPTIONS_CBT = 0x4000;
+
+ *cbt_out = FALSE;
+
+ code = krb5_find_authdata(context, NULL, authdata,
+- KRB5_AUTHDATA_AP_OPTIONS, &authdata);
+- if (code || authdata == NULL)
++ KRB5_AUTHDATA_AP_OPTIONS, &ad);
++ if (code || ad == NULL)
+ return code;
+- if (authdata[1] != NULL || authdata[0]->length != 4)
+- return KRB5KRB_AP_ERR_MSG_TYPE;
++ if (ad[1] != NULL || ad[0]->length != 4) {
++ code = KRB5KRB_AP_ERR_MSG_TYPE;
++ } else {
++ ad_ap_options = load_32_le(ad[0]->contents);
++ if (ad_ap_options & KERB_AP_OPTIONS_CBT)
++ *cbt_out = TRUE;
++ }
+
+- ad_ap_options = load_32_le(authdata[0]->contents);
+- if (ad_ap_options & KERB_AP_OPTIONS_CBT)
+- *cbt_out = TRUE;
+-
+- return 0;
++ krb5_free_authdata(context, ad);
++ return code;
+ }
+
+ /*
diff --git a/krb5.spec b/krb5.spec
index 4a61316..7199f35 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: 17%{?dist}
+Release: 18%{?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}....
@@ -70,6 +70,7 @@ Patch31: Add-channel-bindings-tests.patch
Patch32: Use-two-queues-for-concurrent-t_otp.py-daemons.patch
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
License: MIT
URL:
https://web.mit.edu/kerberos/www/
@@ -631,6 +632,9 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
+* Tue Aug 04 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-18
+- Fix leak in KERB_AP_OPTIONS_CBT server support
+
* Mon Aug 03 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-17
- Revert qualify_shortname removal
commit 4530bb6de9f097108be1cc64007685e68c7d3844
Author: Robbie Harwood <rharwood(a)redhat.com>
Date: Mon Aug 3 15:39:37 2020 -0400
Revert qualify_shortname removal
diff --git a/Add-channel-bindings-tests.patch b/Add-channel-bindings-tests.patch
index a152fa2..b758c79 100644
--- a/Add-channel-bindings-tests.patch
+++ b/Add-channel-bindings-tests.patch
@@ -1,4 +1,4 @@
-From 5f98c9d9ff16f3760ac26304cfdb87bf53bc8628 Mon Sep 17 00:00:00 2001
+From 3e92520c1417f22447751cd9172d5ab30c2e0ad8 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris(a)gmail.com>
Date: Fri, 20 Mar 2020 00:17:28 +0100
Subject: [PATCH] Add channel bindings tests
diff --git a/Add-client_aware_channel_bindings-option.patch
b/Add-client_aware_channel_bindings-option.patch
index e8b5374..012ce8d 100644
--- a/Add-client_aware_channel_bindings-option.patch
+++ b/Add-client_aware_channel_bindings-option.patch
@@ -1,4 +1,4 @@
-From bb7425941f5d84a53e30721c20fbfc714157f082 Mon Sep 17 00:00:00 2001
+From 2a08fe3d2d1972df4ffe37d4bb64b161889ff988 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris(a)gmail.com>
Date: Tue, 10 Mar 2020 13:13:17 +0100
Subject: [PATCH] Add client_aware_channel_bindings option
@@ -20,10 +20,10 @@ ticket: 8900
3 files changed, 98 insertions(+), 86 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
-index 1d2aa7f68..1d8ffc1e4 100644
+index a7e7a29d1..7f2879640 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
-@@ -383,6 +383,12 @@ The libdefaults section may contain any of the following relations:
+@@ -382,6 +382,12 @@ The libdefaults section may contain any of the following relations:
credentials will fail if the client machine does not have a
keytab. The default value is false.
diff --git a/Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch
b/Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch
index 716fa8a..4698963 100644
--- a/Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch
+++ b/Allow-gss_unwrap_iov-of-unpadded-RC4-tokens.patch
@@ -1,4 +1,4 @@
-From 297857dec4f82a802caa734670b57f0a18d942e2 Mon Sep 17 00:00:00 2001
+From bedbb5ee1ad821b91f00d30361985e6863c0e6ba Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson(a)mit.edu>
Date: Sat, 11 Jul 2020 21:57:30 -0400
Subject: [PATCH] Allow gss_unwrap_iov() of unpadded RC4 tokens
diff --git a/Default-dns_canonicalize_hostname-to-fallback.patch
b/Default-dns_canonicalize_hostname-to-fallback.patch
new file mode 100644
index 0000000..ef80329
--- /dev/null
+++ b/Default-dns_canonicalize_hostname-to-fallback.patch
@@ -0,0 +1,371 @@
+From 07179e38e5ee72e82ebc77a1c8d73e34905268b7 Mon Sep 17 00:00:00 2001
+From: Greg Hudson <ghudson(a)mit.edu>
+Date: Wed, 27 May 2020 18:48:35 -0400
+Subject: [PATCH] Default dns_canonicalize_hostname to "fallback"
+
+This change should mitigate some of the pain caused by the rdns=true
+default (generally associated with unwanted PTR records that cannot
+easily be changed), with a minimum of fallout.
+
+Update the documentation and tests accordingly. In test environments,
+disable qualify_shortname and use the uncanonicalized system hostname
+(lowercased) to match the initial sn2princ result.
+
+ticket: 8911 (new)
+---
+ doc/admin/appl_servers.rst | 14 +++---
+ doc/admin/conf_files/krb5_conf.rst | 9 ++--
+ doc/admin/princ_dns.rst | 44 +++++++++++--------
+ src/kadmin/testing/proto/krb5.conf.proto | 8 ++--
+ src/kadmin/testing/scripts/env-setup.shin | 4 +-
+ src/kadmin/testing/scripts/init_db | 3 +-
+ src/kadmin/testing/scripts/start_servers | 3 +-
+ .../testing/scripts/start_servers_local | 2 +-
+ .../kadm5/unit-test/api.current/init-v2.exp | 6 +--
+ src/lib/krb5/krb/init_ctx.c | 2 +-
+ src/tests/dejagnu/config/default.exp | 5 +--
+ src/tests/t_sn2princ.py | 5 ++-
+ src/util/k5test.py | 25 +++--------
+ 13 files changed, 58 insertions(+), 72 deletions(-)
+
+diff --git a/doc/admin/appl_servers.rst b/doc/admin/appl_servers.rst
+index 5232db9af..afdf30297 100644
+--- a/doc/admin/appl_servers.rst
++++ b/doc/admin/appl_servers.rst
+@@ -115,14 +115,12 @@ Getting DNS information correct
+ -------------------------------
+
+ Several aspects of Kerberos rely on name service. When a hostname is
+-used to name a service, the Kerberos library canonicalizes the
+-hostname using forward and reverse name resolution. (The reverse name
+-resolution step can be turned off using the **rdns** variable in
+-:ref:`libdefaults`.) The result of this canonicalization must match
+-the principal entry in the host's keytab, or authentication will fail.
+-
+-Each host's canonical name must be the fully-qualified host name
+-(including the domain), and each host's IP address must
++used to name a service, clients may canonicalize the hostname using
++forward and possibly reverse name resolution. The result of this
++canonicalization must match the principal entry in the host's keytab,
++or authentication will fail. To work with all client canonicalization
++configurations, each host's canonical name must be the fully-qualified
++host name (including the domain), and each host's IP address must
+ reverse-resolve to the canonical name.
+
+ Configuration of hostnames varies by operating system. On the
+diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
+index 1d2aa7f68..a7e7a29d1 100644
+--- a/doc/admin/conf_files/krb5_conf.rst
++++ b/doc/admin/conf_files/krb5_conf.rst
+@@ -188,11 +188,10 @@ The libdefaults section may contain any of the following
relations:
+ hostnames for use in service principal names. Setting this flag
+ to false can improve security by reducing reliance on DNS, but
+ means that short hostnames will not be canonicalized to
+- fully-qualified hostnames. The default value is true.
+-
+- If this option is set to ``fallback`` (new in release 1.18), DNS
+- canonicalization will only be performed the server hostname is not
+- found with the original name when requesting credentials.
++ fully-qualified hostnames. If this option is set to ``fallback`` (new
++ in release 1.18), DNS canonicalization will only be performed the
++ server hostname is not found with the original name when
++ requesting credentials. The default value is ``fallback``.
+
+ **dns_lookup_kdc**
+ Indicate whether DNS SRV records should be used to locate the KDCs
+diff --git a/doc/admin/princ_dns.rst b/doc/admin/princ_dns.rst
+index e1d823f27..32a269afc 100644
+--- a/doc/admin/princ_dns.rst
++++ b/doc/admin/princ_dns.rst
+@@ -31,27 +31,35 @@ based on rotating ``CNAME`` records in DNS.
+ Service principal canonicalization
+ ----------------------------------
+
+-MIT Kerberos clients currently always do forward resolution (looking
+-up the IPv4 and possibly IPv6 addresses using ``getaddrinfo()``) of
+-the hostname part of a host-based service principal to canonicalize
+-the hostname. They obtain the "canonical" name of the host when doing
+-so. By default, MIT Kerberos clients will also then do reverse DNS
+-resolution (looking up the hostname associated with the IPv4 or IPv6
+-address using ``getnameinfo()``) of the hostname. Using the
+-:ref:`krb5.conf(5)` setting::
++In the MIT krb5 client library, canonicalization of host-based service
++principals is controlled by the **dns_canonicalize_hostname**,
++**rnds**, and **qualify_shortname** variables in :ref:`libdefaults`.
+
+- [libdefaults]
+- rdns = false
++If **dns_canonicalize_hostname** is set to ``true`` (the default value
++before release 1.19), the client performs forward resolution by
++looking up the IPv4 and/or IPv6 addresses of the hostname using
++``getaddrinfo()``. This process will typically add a domain suffix to
++the hostname if needed, and follow CNAME records in the DNS. If
++**rdns** is also set to ``true`` (the default), the client will then
++perform a reverse lookup of the first returned Internet address using
++``getnameinfo()``, finding the name associated with the PTR record.
+
+-will disable reverse DNS lookup on clients. The default setting is
+-"true".
++If **dns_canonicalize_hostname** is set to ``false``, the hostname is
++not canonicalized using DNS. If the hostname has only one component
++(i.e. it contains no "." characters), the host's primary DNS search
++domain will be appended, if there is one. The **qualify_shortname**
++variable can be used to override or disable this suffix.
++
++If **dns_canonicalize_hostname** is set to ``fallback`` (the default
++value in release 1.19 and later), the hostname is initially treated
++according to the rules for ``dns_canonicalize_hostname=false``. If a
++ticket request fails because the service principal is unknown, it the
++hostname will be canonicalized according to the rules for
++``dns_canonicalize_hostname=true`` and the request will be retried.
++
++In all cases, the hostname is converted to lowercase, and any trailing
++dot is removed.
+
+-Operating system bugs may prevent a setting of ``rdns = false`` from
+-disabling reverse DNS lookup. Some versions of GNU libc have a bug in
+-``getaddrinfo()`` that cause them to look up ``PTR`` records even when
+-not required. MIT Kerberos releases krb5-1.10.2 and newer have a
+-workaround for this problem, as does the krb5-1.9.x series as of
+-release krb5-1.9.4.
+
+
+ Reverse DNS mismatches
+diff --git a/src/kadmin/testing/proto/krb5.conf.proto
b/src/kadmin/testing/proto/krb5.conf.proto
+index e710852d4..c0af716a5 100644
+--- a/src/kadmin/testing/proto/krb5.conf.proto
++++ b/src/kadmin/testing/proto/krb5.conf.proto
+@@ -2,19 +2,19 @@
+ default_realm = __REALM__
+ default_keytab_name = FILE:__K5ROOT__/keytab
+ dns_fallback = no
++ qualify_shortname = ""
+ plugin_base_dir = __PLUGIN_DIR__
+ allow_weak_crypto = true
+
+ [realms]
+ __REALM__ = {
+- kdc = __KDCHOST__:1750
+- admin_server = __KDCHOST__:1751
++ kdc = __HOSTNAME__:1750
++ admin_server = __HOSTNAME__:1751
+ database_module = foobar_db2_module_blah
+ }
+
+ [domain_realm]
+- __LOCALHOST__ = __REALM__
+- __KDCHOST__ = __REALM__
++ __HOSTNAME__ = __REALM__
+
+ [logging]
+ admin_server = FILE:__K5ROOT__/syslog
+diff --git a/src/kadmin/testing/scripts/env-setup.shin
b/src/kadmin/testing/scripts/env-setup.shin
+index 969c5340c..88f8ad1aa 100755
+--- a/src/kadmin/testing/scripts/env-setup.shin
++++ b/src/kadmin/testing/scripts/env-setup.shin
+@@ -71,8 +71,8 @@ BSDDB_DUMP=$TESTDIR/util/bsddb_dump; export BSDDB_DUMP
+ CLNTTCL=$TESTDIR/util/kadm5_clnt_tcl; export CLNTTCL
+ SRVTCL=$TESTDIR/util/kadm5_srv_tcl; export SRVTCL
+
+-QUALNAME=`$BUILDTOP/tests/resolve/resolve -q | tr '[A-Z]' '[a-z]'`
+-export QUALNAME
++HOSTNAME=`hostname | tr '[A-Z]' '[a-z]'`
++export HOSTNAME
+
+ KRB5_CONFIG=$K5ROOT/krb5.conf; export KRB5_CONFIG
+ KRB5_KDC_PROFILE=$K5ROOT/kdc.conf; export KRB5_KDC_PROFILE
+diff --git a/src/kadmin/testing/scripts/init_db b/src/kadmin/testing/scripts/init_db
+index e65826c96..216f62793 100755
+--- a/src/kadmin/testing/scripts/init_db
++++ b/src/kadmin/testing/scripts/init_db
+@@ -79,8 +79,7 @@ fi
+ # done
+
+ sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+- -e "s/__KDCHOST__/$QUALNAME/g" \
+- -e "s/__LOCALHOST__/$QUALNAME/g" \
++ -e "s/__HOSTNAME__/$HOSTNAME/g" \
+ -e "s#__MODDIR__#$MODDIR#g" \
+ < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf
+ sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+diff --git a/src/kadmin/testing/scripts/start_servers
b/src/kadmin/testing/scripts/start_servers
+index f23df0682..05519e4ee 100755
+--- a/src/kadmin/testing/scripts/start_servers
++++ b/src/kadmin/testing/scripts/start_servers
+@@ -36,8 +36,7 @@ if [ $local = 0 ]; then
+
+ # Fix up the local krb5.conf to point to the remote
+ sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+- -e "s/__KDCHOST__/$hostname/g" \
+- -e "s/__LOCALHOST__/$QUALNAME/g" \
++ -e "s/__HOSTNAME__/$HOSTNAME/g" \
+ -e "s#__MODDIR__#$TOP/../plugins/kdb#g"\
+ -e "s#__PLUGIN_DIR__#$TOP/../plugins#g"\
+ < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf
+diff --git a/src/kadmin/testing/scripts/start_servers_local
b/src/kadmin/testing/scripts/start_servers_local
+index 998ef9164..858e88031 100755
+--- a/src/kadmin/testing/scripts/start_servers_local
++++ b/src/kadmin/testing/scripts/start_servers_local
+@@ -79,7 +79,7 @@ cat - > /tmp/start_servers_local$$ <<\EOF
+ if { [catch {
+ source $env(STOP)/testing/tcl/util.t
+ set r $env(REALM)
+- set q $env(QUALNAME)
++ set q $env(HOSTNAME)
+ puts stdout [kadm5_init $env(SRVTCL) mrroot null \
+ [config_params {KADM5_CONFIG_REALM} $r] \
+ $KADM5_STRUCT_VERSION $KADM5_API_VERSION_3 server_handle]
+diff --git a/src/lib/kadm5/unit-test/api.current/init-v2.exp
b/src/lib/kadm5/unit-test/api.current/init-v2.exp
+index 7a353d4e9..47764c212 100644
+--- a/src/lib/kadm5/unit-test/api.current/init-v2.exp
++++ b/src/lib/kadm5/unit-test/api.current/init-v2.exp
+@@ -3,18 +3,14 @@ load_lib lib.t
+ api_exit
+ api_start
+
+-if ![info exists RESOLVE] {
+- set RESOLVE [findfile $objdir/../../../tests/resolve/resolve]
+-}
+ proc get_hostname { } {
+- global RESOLVE
+ global hostname
+
+ if {[info exists hostname]} {
+ return 1
+ }
+
+- catch "exec $RESOLVE -q >myname" exec_output
++ catch "exec hostname >myname" exec_output
+ if ![string match "" $exec_output] {
+ send_log "$exec_output\n"
+ verbose $exec_output
+diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
+index 9a4741fa6..0b8ae6714 100644
+--- a/src/lib/krb5/krb/init_ctx.c
++++ b/src/lib/krb5/krb/init_ctx.c
+@@ -237,7 +237,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
+ ctx->enforce_ok_as_delegate = tmp;
+
+ retval = get_tristate(ctx, KRB5_CONF_DNS_CANONICALIZE_HOSTNAME,
"fallback",
+- CANONHOST_FALLBACK, 1, &tmp);
++ CANONHOST_FALLBACK, CANONHOST_FALLBACK, &tmp);
+ if (retval)
+ goto cleanup;
+ ctx->dns_canonicalize_hostname = tmp;
+diff --git a/src/tests/dejagnu/config/default.exp b/src/tests/dejagnu/config/default.exp
+index 4d8c917cd..1e7777f1e 100644
+--- a/src/tests/dejagnu/config/default.exp
++++ b/src/tests/dejagnu/config/default.exp
+@@ -268,7 +268,6 @@ foreach i {
+ {KTUTIL $objdir/../../kadmin/ktutil/ktutil}
+ {KLIST $objdir/../../clients/klist/klist}
+ {KDESTROY $objdir/../../clients/kdestroy/kdestroy}
+- {RESOLVE $objdir/../resolve/resolve}
+ {T_INETD $objdir/t_inetd}
+ {KPROPLOG $objdir/../../kprop/kproplog}
+ {KPASSWD $objdir/../../clients/kpasswd/kpasswd}
+@@ -462,7 +461,6 @@ proc setup_runtime_env { } {
+ # 0 on failure.
+
+ proc get_hostname { } {
+- global RESOLVE
+ global hostname
+ global tmppwd
+
+@@ -472,7 +470,7 @@ proc get_hostname { } {
+
+ envstack_push
+ setup_runtime_env
+- catch "exec $RESOLVE -q >$tmppwd/hostname" exec_output
++ catch "exec hostname >$tmppwd/hostname" exec_output
+ envstack_pop
+ if ![string match "" $exec_output] {
+ verbose -log $exec_output
+@@ -710,6 +708,7 @@ proc setup_krb5_conf { {type client} } {
+ puts $conffile "\[libdefaults\]"
+ puts $conffile " default_realm = $REALMNAME"
+ puts $conffile " dns_lookup_kdc = false"
++ puts $conffile " qualify_shortname = \"\""
+ if [info exists allow_weak_crypto($type)] {
+ puts $conffile " allow_weak_crypto = $allow_weak_crypto($type)"
+ } else {
+diff --git a/src/tests/t_sn2princ.py b/src/tests/t_sn2princ.py
+index 26dcb91c2..f3e187286 100755
+--- a/src/tests/t_sn2princ.py
++++ b/src/tests/t_sn2princ.py
+@@ -2,7 +2,8 @@ from k5test import *
+
+ offline = (len(args) > 0 and args[0] != "no")
+
+-conf = {'domain_realm': {'kerberos.org': 'R1',
++conf = {'libdefaults': {'dns_canonicalize_hostname': 'true'},
++ 'domain_realm': {'kerberos.org': 'R1',
+ 'example.com': 'R2',
+ 'mit.edu': 'R3'}}
+ no_rdns_conf = {'libdefaults': {'rdns': 'false'}}
+@@ -28,7 +29,7 @@ def testbase(host, nametype, princhost, princrealm, env=None):
+ fail('Expected %s, got %s' % (expected, out))
+
+ def test(host, princhost, princrealm):
+- # Test with the host-based name type in the default environment.
++ # Test with the host-based name type with canonicalization enabled.
+ testbase(host, 'srv-hst', princhost, princrealm)
+
+ def testnc(host, princhost, princrealm):
+diff --git a/src/util/k5test.py b/src/util/k5test.py
+index eea92275d..5196cfa43 100644
+--- a/src/util/k5test.py
++++ b/src/util/k5test.py
+@@ -193,7 +193,10 @@ Scripts may use the following functions and variables:
+
+ * plugins: The plugin directory in the build tree (absolute path).
+
+-* hostname: This machine's fully-qualified domain name.
++* hostname: The local hostname as it will initially appear in
++ krb5_sname_to_principal() results. (Shortname qualification is
++ turned off in the test environment to make this value easy to
++ discover from Python.)
+
+ * null_input: A file opened to read /dev/null.
+
+@@ -525,23 +528,6 @@ def _find_srctop():
+ return os.path.abspath(root)
+
+
+-# Return the local hostname as it will be canonicalized by
+-# krb5_sname_to_principal. We can't simply use socket.getfqdn()
+-# because it explicitly prefers results containing periods and
+-# krb5_sname_to_principal doesn't care.
+-def _get_hostname():
+- hostname = socket.gethostname()
+- try:
+- ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
+- except socket.gaierror as e:
+- fail('Local hostname "%s" does not resolve: %s.' % (hostname,
e[1]))
+- (family, socktype, proto, canonname, sockaddr) = ai[0]
+- try:
+- name = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
+- except socket.gaierror:
+- return canonname.lower()
+- return name[0].lower()
+-
+ # Parse command line arguments, setting global option variables. Also
+ # sets the global variable args to the positional arguments, which may
+ # be used by the test script.
+@@ -1263,6 +1249,7 @@ _default_krb5_conf = {
+ 'libdefaults': {
+ 'default_realm': '$realm',
+ 'dns_lookup_kdc': 'false',
++ 'qualify_shortname': '',
+ 'plugin_base_dir': '$plugins'},
+ 'realms': {'$realm': {
+ 'kdc': '$hostname:$port0',
+@@ -1356,7 +1343,7 @@ buildtop = _find_buildtop()
+ srctop = _find_srctop()
+ plugins = os.path.join(buildtop, 'plugins')
+ runenv = _import_runenv()
+-hostname = _get_hostname()
++hostname = socket.gethostname().lower()
+ null_input = open(os.devnull, 'r')
+
+ # A DB pass is a tuple of: name, kdc_conf.
diff --git a/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch
b/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch
index b91c81b..2bdd0a3 100644
--- a/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch
+++ b/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch
@@ -1,4 +1,4 @@
-From 63b526ffbdd932dffd5bc1d4a2b3ef6300208fb8 Mon Sep 17 00:00:00 2001
+From 3f873868fb08b77da2d30e164a0ef6c71c17c607 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood(a)redhat.com>
Date: Wed, 15 Jul 2020 15:42:20 -0400
Subject: [PATCH] Ignore bad enctypes in krb5_string_to_keysalts()
diff --git a/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
b/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
index 5a1cad7..649caa4 100644
--- a/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
+++ b/Implement-GSS_C_CHANNEL_BOUND_FLAG.patch
@@ -1,4 +1,4 @@
-From bfc91774e1bf67b544d38abe10b41cdb76e30d8c Mon Sep 17 00:00:00 2001
+From 3ea1d6296ced3a998e79356f9be212e4c5e6a5d5 Mon Sep 17 00:00:00 2001
From: Alexander Scheel <ascheel(a)redhat.com>
Date: Wed, 5 Jul 2017 11:38:30 -0400
Subject: [PATCH] Implement GSS_C_CHANNEL_BOUND_FLAG
diff --git a/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
b/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
index 7757547..41cf5f0 100644
--- a/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
+++ b/Implement-KERB_AP_OPTIONS_CBT-server-side.patch
@@ -1,4 +1,4 @@
-From 49e5ab0f11287dd2fd87de02abf14e63e5040c5b Mon Sep 17 00:00:00 2001
+From 6407bf087fe53088d91efd09df736e979cd4e8db Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris(a)gmail.com>
Date: Mon, 9 Mar 2020 16:04:21 +0100
Subject: [PATCH] Implement KERB_AP_OPTIONS_CBT (server side)
diff --git a/Improve-negoex_parse_token-code-hygiene.patch
b/Improve-negoex_parse_token-code-hygiene.patch
index fd16725..f6b42a6 100644
--- a/Improve-negoex_parse_token-code-hygiene.patch
+++ b/Improve-negoex_parse_token-code-hygiene.patch
@@ -1,4 +1,4 @@
-From 409a8920e13cd14fec8533c1ff864de92d292178 Mon Sep 17 00:00:00 2001
+From c726a72c68244129eb08b840b92144acfa776573 Mon Sep 17 00:00:00 2001
From: Robbie Harwood <rharwood(a)redhat.com>
Date: Tue, 9 Jun 2020 16:23:37 -0400
Subject: [PATCH] Improve negoex_parse_token() code hygiene
diff --git a/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
b/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
index 2514e20..d0db74b 100644
--- a/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
+++ b/Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
@@ -1,4 +1,4 @@
-From 911f19b661cb13373ad3de4fb92015beb3647de7 Mon Sep 17 00:00:00 2001
+From c36e826c70cb5b3bff8bd4371d47884cea30b3f4 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris(a)gmail.com>
Date: Sat, 6 Jun 2020 11:03:37 +0200
Subject: [PATCH] Omit PA_FOR_USER if we can't compute its checksum
diff --git a/Pass-channel-bindings-through-SPNEGO.patch
b/Pass-channel-bindings-through-SPNEGO.patch
index 1dff308..5ab5c07 100644
--- a/Pass-channel-bindings-through-SPNEGO.patch
+++ b/Pass-channel-bindings-through-SPNEGO.patch
@@ -1,4 +1,4 @@
-From 20ec2ee13c34bfae03d62662b25f2f7708d1a418 Mon Sep 17 00:00:00 2001
+From ee79bd43005245d3e5a2d3ec6d61146945e77717 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris(a)gmail.com>
Date: Tue, 28 Apr 2020 18:15:55 +0200
Subject: [PATCH] Pass channel bindings through SPNEGO
diff --git a/Refactor-krb5-GSS-checksum-handling.patch
b/Refactor-krb5-GSS-checksum-handling.patch
index 6e41865..392d929 100644
--- a/Refactor-krb5-GSS-checksum-handling.patch
+++ b/Refactor-krb5-GSS-checksum-handling.patch
@@ -1,4 +1,4 @@
-From 5037c0fe698ad00a8e8f53fdcfc3d3b1c3537aba Mon Sep 17 00:00:00 2001
+From a34b7c50e62c19f80d39ece6a72017dac781df64 Mon Sep 17 00:00:00 2001
From: Alexander Scheel <ascheel(a)redhat.com>
Date: Fri, 30 Jun 2017 16:03:01 -0400
Subject: [PATCH] Refactor krb5 GSS checksum handling
diff --git a/Remove-resolver-test-utility.patch b/Remove-resolver-test-utility.patch
index d70f078..6602185 100644
--- a/Remove-resolver-test-utility.patch
+++ b/Remove-resolver-test-utility.patch
@@ -1,4 +1,4 @@
-From 3eb685873f5390a3af518ac86e99f6863750ada3 Mon Sep 17 00:00:00 2001
+From 85bb5fe5a11708b78e9f0bd3a3b34999b6c888a7 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson(a)mit.edu>
Date: Thu, 28 May 2020 18:41:02 -0400
Subject: [PATCH] Remove resolver test utility
diff --git a/Use-two-queues-for-concurrent-t_otp.py-daemons.patch
b/Use-two-queues-for-concurrent-t_otp.py-daemons.patch
index 7a66d35..33da6f5 100644
--- a/Use-two-queues-for-concurrent-t_otp.py-daemons.patch
+++ b/Use-two-queues-for-concurrent-t_otp.py-daemons.patch
@@ -1,4 +1,4 @@
-From ccc6e0fae1fc000e10e51ffa45cc59674038714a Mon Sep 17 00:00:00 2001
+From 8e08f01d73ddca1b828788710ec6bb3e0354727a Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson(a)mit.edu>
Date: Wed, 4 Mar 2020 17:18:51 -0500
Subject: [PATCH] Use two queues for concurrent t_otp.py daemons
diff --git a/krb5.spec b/krb5.spec
index c5818f4..4a61316 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: 16%{?dist}
+Release: 17%{?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}....
@@ -57,6 +57,7 @@ Patch17: Pass-gss_localname-through-SPNEGO.patch
Patch18: Omit-KDC-indicator-check-for-S4U2Self-requests.patch
Patch19: Fix-typo-in-in-in-the-ksu-man-page.patch
Patch21: Replace-gssrpc-tests-with-a-Python-script.patch
+Patch22: Default-dns_canonicalize_hostname-to-fallback.patch
Patch23: Remove-resolver-test-utility.patch
Patch24: Omit-PA_FOR_USER-if-we-can-t-compute-its-checksum.patch
Patch25: Improve-negoex_parse_token-code-hygiene.patch
@@ -630,6 +631,9 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
+* Mon Aug 03 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-17
+- Revert qualify_shortname removal
+
* Mon Aug 03 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-16
- Disable tests on s390x
- Resolves: #1863952
commit 8be525213668ed9f1b144e6fe4beaee4d135c1a2
Author: Robbie Harwood <rharwood(a)redhat.com>
Date: Mon Aug 3 19:30:15 2020 +0000
Disable tests on s390x
Resolves: #1863952
diff --git a/krb5.spec b/krb5.spec
index 9ce8b31..c5818f4 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: 15%{?dist}
+Release: 16%{?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}....
@@ -300,6 +300,12 @@ sphinx-build -a -b html -t pathsubs doc build-html
rm -fr build-html/_sources
%check
+
+# There are 0 test machines for this architecture, very few builders, and
+# they're not very well provisioned / maintained. I can't support it.
+# Patches welcome, but there's nothing I can do - it fails more than half the
+# time for no discernable reason.
+%ifnarch s390x
pushd src
# ugh. COPR doesn't expose the keyring, so try to cope.
@@ -310,6 +316,7 @@ keyctl list @u &>/dev/null || KEYCTL=:
# tests with a new one.
$KEYCTL session - make check OFFLINE=yes TMPDIR=%{_tmppath}
popd
+%endif
%install
[ "$RPM_BUILD_ROOT" != '/' ] && rm -rf --
"$RPM_BUILD_ROOT"
@@ -623,6 +630,10 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
+* Mon Aug 03 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-16
+- Disable tests on s390x
+- Resolves: #1863952
+
* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> -
1.18.2-15
- Second attempt - Rebuilt for
https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
commit d0cfa344c7bb9bd1cd31606d7a1c6978c3e7f30d
Author: Fedora Release Engineering <releng(a)fedoraproject.org>
Date: Sat Aug 1 03:47:16 2020 +0000
- Second attempt - Rebuilt for
https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
Signed-off-by: Fedora Release Engineering <releng(a)fedoraproject.org>
diff --git a/krb5.spec b/krb5.spec
index 6d25581..9ce8b31 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: 14%{?dist}
+Release: 15%{?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}....
@@ -623,6 +623,10 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
+* Sat Aug 01 2020 Fedora Release Engineering <releng(a)fedoraproject.org> -
1.18.2-15
+- Second attempt - Rebuilt for
+
https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild
+
* Fri Jul 31 2020 Robbie Harwood <rharwood(a)redhat.com> - 1.18.2-14
- Revert qualify_shortname changes