diff options
author | Florian Weimer <fweimer@redhat.com> | 2019-10-30 17:26:58 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2019-11-27 20:54:37 +0100 |
commit | 446997ff1433d33452b81dfa9e626b8dccf101a4 (patch) | |
tree | 9a0e13fb7ca042a6f05fa9310f862c494580da80 /resolv/tst-resolv-trustad.c | |
parent | 4a2ab5843a5cc4a5db1b3b79916a520ea8b115dc (diff) | |
download | glibc-446997ff1433d33452b81dfa9e626b8dccf101a4.tar.gz glibc-446997ff1433d33452b81dfa9e626b8dccf101a4.tar.xz glibc-446997ff1433d33452b81dfa9e626b8dccf101a4.zip |
resolv: Implement trust-ad option for /etc/resolv.conf [BZ #20358]
This introduces a concept of trusted name servers, for which the AD bit is passed through to applications. For untrusted name servers (the default), the AD bit in responses are cleared, to provide a safe default. This approach is very similar to the one suggested by Pavel Šimerda in <https://bugzilla.redhat.com/show_bug.cgi?id=1164339#c15>. The DNS test framework in support/ is enhanced with support for setting the AD bit in responses. Tested on x86_64-linux-gnu. Change-Id: Ibfe0f7c73ea221c35979842c5c3b6ed486495ccc
Diffstat (limited to 'resolv/tst-resolv-trustad.c')
-rw-r--r-- | resolv/tst-resolv-trustad.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/resolv/tst-resolv-trustad.c b/resolv/tst-resolv-trustad.c new file mode 100644 index 0000000000..fc9ae540a2 --- /dev/null +++ b/resolv/tst-resolv-trustad.c @@ -0,0 +1,200 @@ +/* Test the behavior of the trust-ad option. + Copyright (C) 2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <resolv.h> +#include <stdlib.h> +#include <string.h> +#include <support/check.h> +#include <support/check_nss.h> +#include <support/resolv_test.h> +#include <support/support.h> + +/* This controls properties of the response. volatile because + __res_send is incorrectly declared as __THROW. */ +static volatile unsigned char response_number; +static volatile bool response_ad_bit; +static volatile bool query_ad_bit; + +static void +response (const struct resolv_response_context *ctx, + struct resolv_response_builder *b, + const char *qname, uint16_t qclass, uint16_t qtype) +{ + TEST_COMPARE (qclass, C_IN); + TEST_COMPARE (qtype, T_A); + TEST_COMPARE_STRING (qname, "www.example"); + + HEADER header; + memcpy (&header, ctx->query_buffer, sizeof (header)); + TEST_COMPARE (header.ad, query_ad_bit); + + struct resolv_response_flags flags = { .ad = response_ad_bit, }; + resolv_response_init (b, flags); + resolv_response_add_question (b, qname, qclass, qtype); + resolv_response_section (b, ns_s_an); + resolv_response_open_record (b, qname, qclass, T_A, 0x12345678); + char addr[4] = { 192, 0, 2, response_number }; + resolv_response_add_data (b, addr, sizeof (addr)); + resolv_response_close_record (b); +} + +static void +check_answer (const unsigned char *buffer, size_t buffer_length, + bool expected_ad) +{ + HEADER header; + TEST_VERIFY (buffer_length > sizeof (header)); + memcpy (&header, buffer, sizeof (header)); + TEST_COMPARE (0, header.aa); + TEST_COMPARE (expected_ad, header.ad); + TEST_COMPARE (0, header.opcode); + TEST_COMPARE (1, header.qr); + TEST_COMPARE (0, header.rcode); + TEST_COMPARE (1, header.rd); + TEST_COMPARE (0, header.tc); + TEST_COMPARE (1, ntohs (header.qdcount)); + TEST_COMPARE (1, ntohs (header.ancount)); + TEST_COMPARE (0, ntohs (header.nscount)); + TEST_COMPARE (0, ntohs (header.arcount)); + + char *description = xasprintf ("response=%d ad=%d", + response_number, expected_ad); + char *expected = xasprintf ("name: www.example\n" + "address: 192.0.2.%d\n", response_number); + check_dns_packet (description, buffer, buffer_length, expected); + free (expected); + free (description); +} + +static int +do_test (void) +{ + struct resolv_test *aux = resolv_test_start + ((struct resolv_redirect_config) + { + .response_callback = response, + }); + + /* By default, the resolver is not trusted, and the AD bit is + cleared. */ + + static const unsigned char hand_crafted_query[] = + { + 10, 11, /* Transaction ID. */ + 1, 0x20, /* Query with RD, AD flags. */ + 0, 1, /* One question. */ + 0, 0, 0, 0, 0, 0, /* The other sections are empty. */ + 3, 'w', 'w', 'w', 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 0, + 0, T_A, /* A query. */ + 0, 1, /* Class IN. */ + }; + + ++response_number; + response_ad_bit = false; + + unsigned char buffer[512]; + memset (buffer, 255, sizeof (buffer)); + query_ad_bit = true; + int ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), + buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + + ++response_number; + memset (buffer, 255, sizeof (buffer)); + query_ad_bit = false; + ret = res_query ("www.example", C_IN, T_A, buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + response_ad_bit = true; + + response_ad_bit = true; + + ++response_number; + query_ad_bit = true; + ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), + buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + + ++response_number; + memset (buffer, 255, sizeof (buffer)); + query_ad_bit = false; + ret = res_query ("www.example", C_IN, T_A, buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + + /* No AD bit set in generated queries. */ + memset (buffer, 255, sizeof (buffer)); + ret = res_mkquery (QUERY, "www.example", C_IN, T_A, + (const unsigned char *) "", 0, NULL, + buffer, sizeof (buffer)); + HEADER header; + memcpy (&header, buffer, sizeof (header)); + TEST_VERIFY (!header.ad); + + /* With RES_TRUSTAD, the AD bit is passed through if it set in the + response. It is also included in queries. */ + + _res.options |= RES_TRUSTAD; + query_ad_bit = true; + + response_ad_bit = false; + + ++response_number; + memset (buffer, 255, sizeof (buffer)); + ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), + buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + + ++response_number; + memset (buffer, 255, sizeof (buffer)); + ret = res_query ("www.example", C_IN, T_A, buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, false); + + response_ad_bit = true; + + ++response_number; + memset (buffer, 0, sizeof (buffer)); + ret = res_send (hand_crafted_query, sizeof (hand_crafted_query), + buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, true); + + ++response_number; + memset (buffer, 0, sizeof (buffer)); + ret = res_query ("www.example", C_IN, T_A, buffer, sizeof (buffer)); + TEST_VERIFY (ret > 0); + check_answer (buffer, ret, true); + + /* AD bit set in generated queries. */ + memset (buffer, 0, sizeof (buffer)); + ret = res_mkquery (QUERY, "www.example", C_IN, T_A, + (const unsigned char *) "", 0, NULL, + buffer, sizeof (buffer)); + memcpy (&header, buffer, sizeof (header)); + TEST_VERIFY (header.ad); + + resolv_test_end (aux); + + return 0; +} + +#include <support/test-driver.c> |