From 4a030f682ef48542ed324b28207f2c2b4847dbef Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 14 Dec 2016 10:30:29 +0100 Subject: [PATCH] dirmngr: Fix bugs in the standard resolver code. * dirmngr/dns-stuff.c: Include dirmngr-err.h to set the correct error source. (get_h_errno_as_gpg_error): New. (get_dns_cert_libdns): Fix error code. (getsrv_libdns): Add arg R_COUNT and return an error code. (getsrv_standard): Ditto. Fix handling of res_query errors and provide the correct size for the return buffer. (getsrv): Adjust for changed worker functions. (get_dns_cname_standard): Fix handling of res_query errors and provide the correct size for the return buffer. Signed-off-by: Werner Koch --- dirmngr/dns-stuff.c | 84 ++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/dirmngr/dns-stuff.c b/dirmngr/dns-stuff.c index a7953638c..bcf3639cc 100644 --- a/dirmngr/dns-stuff.c +++ b/dirmngr/dns-stuff.c @@ -56,6 +56,7 @@ # include #endif +#include "./dirmngr-err.h" #include "util.h" #include "host2net.h" #include "dns-stuff.h" @@ -168,6 +169,23 @@ free_dns_addrinfo (dns_addrinfo_t ai) } } +/* Return H_ERRNO mapped to a gpg-error code. Will never return 0. */ +static gpg_error_t +get_h_errno_as_gpg_error (void) +{ + gpg_err_code_t ec; + + switch (h_errno) + { + case HOST_NOT_FOUND: ec = GPG_ERR_UNKNOWN_HOST; break; + case TRY_AGAIN: ec = GPG_ERR_TRY_LATER; break; + case NO_RECOVERY: ec = GPG_ERR_SERVER_FAILED; break; + case NO_DATA: ec = GPG_ERR_NO_DATA; break; + default: ec = GPG_ERR_UNKNOWN_ERRNO; break; + } + return gpg_error (ec); +} + static gpg_error_t map_eai_to_gpg_error (int ec) @@ -670,7 +688,7 @@ get_dns_cert_libdns (const char *name, int want_certtype, void **r_key, size_t *r_keylen, unsigned char **r_fpr, size_t *r_fprlen, char **r_url) { - return gpg_error (ENOTSUP); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } @@ -922,17 +940,21 @@ priosort(const void *a,const void *b) } -/* Standard resolver based helper for getsrv. */ -static int -getsrv_standard (const char *name, struct srventry **list) +/* Libdns based helper for getsrv. Note that it is expected that NULL + * is stored at the address of LIST and 0 is stored at the address of + * R_COUNT. */ +static gpg_error_t +getsrv_libdns (const char *name, struct srventry **list, int *r_count) { - return gpg_error (ENOTSUP); + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } -/* libdns based helper for getsrv. */ +/* Standard resolver based helper for getsrv. Note that it is + * expected that NULL is stored at the address of LIST and 0 is stored + * at the address of R_COUNT. */ static int -getsrv_libdns (const char *name, struct srventry **list) +getsrv_standard (const char *name, struct srventry **list, int *r_count) { #ifdef HAVE_SYSTEM_RESOLVER union { @@ -949,14 +971,19 @@ getsrv_libdns (const char *name, struct srventry **list) /* Do not allow a query using the standard resolver in Tor mode. */ if (tor_mode) - return -1; + return gpg_error (GPG_ERR_NOT_ENABLED); my_unprotect (); - r = res_query (name, C_IN, T_SRV, answer, sizeof answer); + r = res_query (name, C_IN, T_SRV, answer, sizeof res.ans); my_protect (); - if (r < sizeof (HEADER) || r > sizeof answer - || header->rcode != NOERROR || !(count=ntohs (header->ancount))) - return 0; /* Error or no record found. */ + if (r < 0) + return get_h_errno_as_gpg_error (); + if (r < sizeof (HEADER)) + return gpg_error (GPG_ERR_SERVER_FAILED); + if (r > sizeof res.ans) + return gpg_error (GPG_ERR_SYSTEM_BUG); + if (header->rcode != NOERROR || !(count=ntohs (header->ancount))) + return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */ emsg = &answer[r]; pt = &answer[sizeof(HEADER)]; @@ -970,7 +997,7 @@ getsrv_libdns (const char *name, struct srventry **list) while (count-- > 0 && pt < emsg) { - struct srventry *srv = NULL; + struct srventry *srv; u16 type, class; struct srventry *newlist; @@ -1031,18 +1058,20 @@ getsrv_libdns (const char *name, struct srventry **list) goto fail; } - return srvcount; + *r_count = srvcount; + return 0; fail: xfree (*list); *list = NULL; - return -1; + return gpg_error (GPG_ERR_GENERAL); #else /*!HAVE_SYSTEM_RESOLVER*/ (void)name; (void)list; - return -1; + (void)r_count; + return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!HAVE_SYSTEM_RESOLVER*/ } @@ -1051,18 +1080,19 @@ getsrv_libdns (const char *name, struct srventry **list) int getsrv (const char *name, struct srventry **list) { + gpg_error_t err; int srvcount; int i; *list = NULL; - + srvcount = 0; if (!standard_resolver) - srvcount = getsrv_libdns (name, list); + err = getsrv_libdns (name, list, &srvcount); else - srvcount = getsrv_standard (name, list); + err = getsrv_standard (name, list, &srvcount); - if (srvcount <= 0) - return srvcount; + if (err) + return -1; /* Ugly. FIXME: Return an error code. */ /* Now we have an array of all the srv records. */ @@ -1172,9 +1202,15 @@ get_dns_cname_standard (const char *name, char **r_cname) if (tor_mode) return -1; - r = res_query (name, C_IN, T_CERT, answer, sizeof answer); - if (r < sizeof (HEADER) || r > sizeof answer) + my_unprotect (); + r = res_query (name, C_IN, T_CERT, answer, sizeof res.ans); + my_protect (); + if (r < 0) + return get_h_errno_as_gpg_error (); + if (r < sizeof (HEADER)) return gpg_error (GPG_ERR_SERVER_FAILED); + if (r > sizeof res.ans) + return gpg_error (GPG_ERR_SYSTEM_BUG); if (header->rcode != NOERROR || !(count=ntohs (header->ancount))) return gpg_error (GPG_ERR_NO_NAME); /* Error or no record found. */ if (count != 1) @@ -1221,7 +1257,7 @@ get_dns_cname_standard (const char *name, char **r_cname) (void)name; (void)r_cname; - return -1; + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); #endif /*!HAVE_SYSTEM_RESOLVER*/ }