libdns lookups fails when nssswitch has no "dns" section
Closed, ResolvedPublic

Description

On a recent Arch Linux system, a user reports that their "hosts" line in
nsswitch.conf is:

    hosts: files resolve myhostname

apparently libnss_resolve.so.2 is provided by systemd on that system, which
hooks into systemd-resolved somehow for implementation of gethostbyname and
getaddrinfo.

This is a legitimate and functional setup.

however, dirmngr's libdns parses this file and apparently tries to guess what
each configuration means, and has hard-coded understandings of what each
directive means.

in particular, dirmngr/dns.c hard-codes an understanding of these strings, which
do not contain either "myhostname" or "resolve":

static const char *list[] = {

		[DNS_NSSCONF_HOSTS]    = "hosts",
		[DNS_NSSCONF_SUCCESS]  = "success",
		[DNS_NSSCONF_NOTFOUND] = "notfound",
		[DNS_NSSCONF_UNAVAIL]  = "unavail",
		[DNS_NSSCONF_TRYAGAIN] = "tryagain",
		[DNS_NSSCONF_CONTINUE] = "continue",
		[DNS_NSSCONF_RETURN]   = "return",
		[DNS_NSSCONF_FILES]    = "files",
		[DNS_NSSCONF_DNS]      = "dns",
		[DNS_NSSCONF_MDNS]     = "mdns",

};

As a result, libdns doesn't have any directives to look up the keyserver's
address with, so it fails.

It would be better to try to not fail :) I see two approaches:

if libdns tries to parse nsswitch.conf and never finds a "dns" element in its
"hosts:" line:

0) it implicitly assumes that there is a "dns" lookup at the end of the list,
and tries again from there

  1. it falls back to "standard-resolver"

Both of these situations would have worked for this user (i believe they chose
to explicitly set "standard-resolver" in dirmngr.conf as a workaround).

in both cases, dirmngr should probably emit a warning that it libdns is
operating in some sort of fallback mode.

Details

Version
2.1.18
dkg set Version to 2.1.18.Feb 8 2017, 12:03 AM
dkg added projects: dirmngr, gnupg, Bug Report.
dkg added a subscriber: dkg.
werner claimed this task.Feb 13 2017, 1:03 PM
werner added a subscriber: werner.

I guess the best solution is to handle this the same way as a missing
nsswitch file. Here is a non-tested patch; for a quick test the
change of the condition is sufficient.

diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c
index f0de357..956fe72 100644

  • a/dirmngr/dns-stuff.c

+++ b/dirmngr/dns-stuff.c
@@ -496,14 +496,15 @@ libdns_init (void)

fname = "/etc/nsswitch.conf";
err = libdns_error_to_gpg_error
  (dns_nssconf_loadpath (ld.resolv_conf, fname));
  • if (err)

+ if (err || !ld.resolv_conf->lookup[0])

{
  • log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err));
  • /* not fatal, nsswitch.conf is not used on all systems; assume
  • * classic behavior instead. Our dns library states "bf" which tries
  • * DNS then Files, which is not classic; FreeBSD
  • * /usr/src/lib/libc/net/gethostnamadr.c defines default_src[] which
  • * is Files then DNS, which is. */

+ if (err)
+ log_error ("failed to load '%s': %s\n", fname, gpg_strerror (err));
+ /* Not fatal, nsswitch.conf is not used on all systems;
+ * assume classic behavior instead. Note that some systemd
+ * based systems allow for custom keywords which are not
+ * known to us and thus lead to an empty result set; we then
+ * also fallback to classic behavior. */

if (opt_debug)
  log_debug ("dns: fallback resolution order, files then DNS\n");
ld.resolv_conf->lookup[0] = 'f';
dkg added a comment.Feb 13 2017, 5:26 PM

looks reasonable to me, though i haven't tried it myself (my nsswitch.conf
doesn't have the initial property reported).

Perhaps there should be an additional explicit log message for the
!ld.resolv_conf->lookup[0] case since dirmngr is falling back?

You would see that error message then with every first DNS call. My
understanding is that on systemd the unknown keywords are not an error but a
featyre of systemd-resolver(?).

dkg added a comment.Feb 13 2017, 7:39 PM

right, the configuration is not an error, but a different way of handling the
DNS lookups.

just to clarify: this change means that dirmngr will continue to use libdns in
the event of finding no understood directives in nsswitch.conf. it is *not* the
equivalent of falling back to standard-resolver. right? If that's correct,
then i agree that an extra warning is probably too much noise.

Right, the proposed chnage will not fallback to the standard resolver.

I need to modify the patch because it was too simple: Need to explicitly look
for an dns entry and append it to the list iff it is missing.

Fixed with commit dee026d7.

If no DNS method is found in nsswitch.conf we now append one. Using dirmngr w/o
DNS does not work anyway thus this seems to be the best solution.

dkg: Can we close this now that 2.1.20 is out?

dkg added a comment.Apr 4 2017, 12:29 AM

I don't have one of these systems handy to test with, but if the fix in dee026d7 does what it says it does, this sounds like it's probably OK to close in my book. if there are more problems, i'm sure we can re-open it.

werner reassigned this task from werner to justus.May 8 2017, 2:04 PM

Justus, will you please so kind and take care of this.

justus closed this task as Resolved.May 8 2017, 5:27 PM

This seems to work just fine on our archlinux box with the nsswitch configuration above.