resolv: Fix a domain search list check

Message ID 20220113162100.24013-1-ppavlu@suse.cz
State New, archived
Headers
Series resolv: Fix a domain search list check |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent
dj/TryBot-32bit success Build for i686

Commit Message

Petr Pavlu Jan. 13, 2022, 4:21 p.m. UTC
  From: Petr Pavlu <petr.pavlu@suse.com>

Fix a domain search list check in resolv_conf_matches() which verifies
that a resolv_conf extended state matches a given __res_state. The check
counts a length of entries referenced by the resp->dnsrch array and
tests whether it exceeds the combined storage space for the search list,
but wrongly uses a size of resp->dnsrch instead of resp->defdname.

Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
---

Notes:
    The following is an artificial example which demonstrates the issue:
    > $ cat test.c
    > #include <arpa/inet.h>
    > #include <netdb.h>
    > #include <netinet/in.h>
    > #include <resolv.h>
    > #include <stdio.h>
    > #include <string.h>
    > #include <sys/socket.h>
    > #include <sys/types.h>
    >
    > int main(int argc, char *argv[])
    > {
    >   if (argc < 2) {
    >     fprintf(stderr, "Usage: %s hostname\n", argv[0]);
    >     return 1;
    >   }
    >   const char *hostname = argv[1];
    >
    >   if (res_init() == -1) {
    >     fprintf(stderr, "res_init() failed\n");
    >     return 1;
    >   }
    >
    >   printf("System search domains:\n");
    >   for (size_t i = 0; i < sizeof(_res.dnsrch) / sizeof(_res.dnsrch[0]); i++)
    >     if (_res.dnsrch[i] != NULL)
    >       printf("  %zu: %s\n", i, _res.dnsrch[i]);
    >
    >   printf("Custom search domains:\n");
    >   memset(_res.dnsrch, 0, sizeof(_res.dnsrch));
    >   memset(_res.defdname, 0, sizeof(_res.defdname));
    >   _res.dnsrch[0] = _res.defdname;
    >   snprintf(_res.defdname, sizeof(_res.defdname), "%s",
    >     "012345678901234567890123456789012345678901234567890123456789.example.org");
    >   for (size_t i = 0; i < sizeof(_res.dnsrch) / sizeof(_res.dnsrch[0]); i++)
    >     if (_res.dnsrch[i] != NULL)
    >       printf("  %zu: %s\n", i, _res.dnsrch[i]);
    >
    >   struct addrinfo hints, *result;
    >   memset(&hints, 0, sizeof(hints));
    >   hints.ai_family = AF_INET;
    >   hints.ai_socktype = SOCK_STREAM;
    >   int s = getaddrinfo(hostname, NULL, &hints, &result);
    >   if (s != 0) {
    >     fprintf(stderr, "getaddrinfo() failed: %s\n", gai_strerror(s));
    >     return 1;
    >   }
    >
    >   printf("Resolved addresses:\n");
    >   for (struct addrinfo *p = result; p != NULL; p = p->ai_next) {
    >     struct sockaddr_in *h = (struct sockaddr_in *) p->ai_addr;
    >     printf("  %s\n", inet_ntoa(h->sin_addr));
    >   }
    >   freeaddrinfo(result);
    >
    >   return 0;
    > }
    > $ gcc -Wall -pedantic -lresolv test.c
    > $ cat /etc/resolv
    > search 012345678901234567890123456789012345678901234567890123456789.example.org gnu.org
    > [...]
    
    Previous behaviour:
    > $ ./a.out savannah
    > System search domains:
    >   0: 012345678901234567890123456789012345678901234567890123456789.example.org
    >   1: gnu.org
    > Custom search domains:
    >   0: 012345678901234567890123456789012345678901234567890123456789.example.org
    > Resolved addresses:
    >   209.51.188.72
    
    Updated version which recognizes that _res was modified:
    > $ ./a.out savannah
    > System search domains:
    >   0: 012345678901234567890123456789012345678901234567890123456789.example.org
    >   1: gnu.org
    > Custom search domains:
    >   0: 012345678901234567890123456789012345678901234567890123456789.example.org
    > getaddrinfo() failed: Name or service not known

 resolv/resolv_conf.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
  

Patch

diff --git a/resolv/resolv_conf.c b/resolv/resolv_conf.c
index 6dd552bb47..ef872dbc14 100644
--- a/resolv/resolv_conf.c
+++ b/resolv/resolv_conf.c
@@ -310,7 +310,7 @@  resolv_conf_matches (const struct __res_state *resp,
                exceeds MAXDNSRCH, or if the combined storage space for
                the search list exceeds what can be stored in
                resp->defdname.  */
-            if (i == MAXDNSRCH || search_list_size > sizeof (resp->dnsrch))
+            if (i == MAXDNSRCH || search_list_size > sizeof (resp->defdname))
               break;
             /* Otherwise, a mismatch indicates a match failure.  */
             return false;