Skip Menu |

Subject: qualify_shortname default can be harmful in LAN setups
Date: Thu, 09 Jul 2020 16:31:04 -0400
To: describes a regression when Fedora changed the default of dns_canonicalize_hostname from "true" to "fallback" in their 1.18 package.  The user has a LAN setup using single-component hostnames resolved via /etc/hosts (with no presence in the DNS), and has set rdns=false.  This setup worked with dns_canonicalize_hostname=true because we don't qualify DNS-canonicalized results, but with dns_canonicalize_hostname=fallback, the single-component hostnames were qualified with the DNS search domain, which was some useless domain supplied by the ISP during DHCP.  This caused sshd to stop accepting tickets.

This is an immediate issue for Red Hat, and a time bomb for upstream as we are slated to change the dns_canonicalize_hostname default to "fallback" in 1.19.  Red Hat's response so far has been to change the qualify_shortname default to "", but I think this change alone will cause problems in different setups.  For instance, describe's a user experiencing a kprop failure after setting dns_canonicalize_hostname=false.

Whatever we do needs to account for the following use cases for krb5_sname_to_principal():

* TGS request service names (the most prominent use case, and the one we paid the most attention to when implementing dns_canonicalize_hostname=fallback).  For this use case qualify_shortname is mostly a user convenience, perhaps not a necessity.

* Acceptor names for server applications.  Probably the most common scenario is when the application calls gethostname() and then imports servicename+"@"+hostname (e.g. "host@small-gods" for sshd on my machine).  Here we want the local FQDN, but don't specifically know that, and we probably received only the local hostname without domain.  (Applications can get better behavior by importing just the service name, which causes the hostname part to be wildcarded, but I don't think many server application code bases have gotten that message.)

* Non-GSS applications using a keytab to authenticate using a service principal name as the client.  Within the tree, this case includes kprop, kpropd when acting as an iprop client, kadmin -k (with no -p option), and kinit -k (with no principal name argument).

* ksu looks for a ticket to {sn2princ with "host" and null hostname} as an alternative to a TGT, although this may currently be broken (see ticket 8619).

* KDB creation, where we create kadmin/hostname and kiprop/hostname principals.  There is a certain amount of slack here if this goes wrong; kadmin will fall back to kadmin/admin, and the administrator can simply create the kiprop principal for the master KDC when they create ones for the replicas (as they had to do before ticket 7979).
Measures I have considered so far:

* Implement canonicalization fallback for keytab search, as Heimdal does in its hostname canonicalization framework.  If we did this, we might be able to get away with a qualify_shortname="" default, sacrificing the TGS convenience.

* Using getaddrinfo(gethostname()) to construct the local FQDN, regardless of dns_canonicalize_hostname value, under the theory that the local hostname will be listed in /etc/hosts.  This wouldn't solve the GSS acceptor name case because we don't specifically know we're getting the local FQDN (unless we compared the supplied hostname to gethostname(), but that seems like it would create other edge cases).  Also, I checked a few hosts under my control and found that one of them did not list the local hostname in /etc/hosts (so "hostname -f" goes to DNS).

We might also consider delaying any transformation of the hostname until krb5_get_credentials/krb5_kt_et_entry time, so that we remember the original value.  In the LAN case, hostname.ispdomain probably does not resolve, whereas the hostname alone does, so early shortname qualification gets in the way of fallback.  Heimdal does this in its hostname canonicalization framework unless there is a single canonicalization rule of "nss".  (Well, more or less; it still removes trailing dots immediately.)
Some ancillary wrinkles:

* krb5_sname_to_principal() allows :port suffixes (used by MSSQLSvc principals), but the current fallback processing in get_creds.c does not.

* krb5_get_init_creds_keytab() iterates over the keytab to find the available enctypes so it can put those first in the request, and errors out if it doesn't find any.  This operation does not substitute the default realm for the referral realm like krb5_kt_get_entry() does.

* krb5_sname_to_principal() looks up the realm (in [domain_realm] or a hostrealm plugin module) of the first expanded hostname candidate.  The current fallback processing does not repeat this lookup.  If qualify_shortname is "", the lookup is unlikely to succeed for the local hostname or a short hostname.
* s4u2proxy (k5_get_proxy_cred_from_kdc()) does not do fallback on the service name.  (Neither does krb5_tkt_creds_step() or IAKERB, but that was known at the time of fallback implementation.)
* In the current get_creds.c fallback code, if the fallback happens (so that we try the DNS-canonicalized server name) and we find the service ticket in the cache, we store it back into the cache, creating a duplicate entry.
* krb5_get_credentials() ordinarily handles both checking the cache and storing into the cache.  For S4U2Self requests, it calls k5_get_proxy_cred_from_kdc(), which stores into the cache but does not check the cache, so repeated krb5_get_credentials() S4U2Self calls will result in duplicate cache entries.  (GSSAPI does its own cache check before making the S4U2Proxy request, and kvno -P uses the krb5_get_credentials_for_proxy() wrapper which does a cache check.  So this is purely an issue with the krb5_get_credentials() API.)
In the previous comment, I meant to say S4U2Proxy, not S4U2Self.  This wrinkle cannot be ironed out, because krb5_get_credentials() can only see the evidence ticket, not the  client name within, so it cannot check the cache.
Tickets 8930, 8931, and 8935 have solved most of the problems mentioned here.  (8931 actually solves the wrinkle that I previously said could not be ironed out; Isaac had the clever idea of checking the cache by second ticket rather than client name.)

It remains to be determined whether qualify_shortname should default to the primary DNS search domain or "".  Either setting should work much better with these changes.