aboutsummaryrefslogtreecommitdiff
path: root/resolv/tst-resolv-trustad.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-10-30 17:26:58 +0100
committerFlorian Weimer <fweimer@redhat.com>2019-11-27 20:54:37 +0100
commit446997ff1433d33452b81dfa9e626b8dccf101a4 (patch)
tree9a0e13fb7ca042a6f05fa9310f862c494580da80 /resolv/tst-resolv-trustad.c
parent4a2ab5843a5cc4a5db1b3b79916a520ea8b115dc (diff)
downloadglibc-446997ff1433d33452b81dfa9e626b8dccf101a4.zip
glibc-446997ff1433d33452b81dfa9e626b8dccf101a4.tar.gz
glibc-446997ff1433d33452b81dfa9e626b8dccf101a4.tar.bz2
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.c200
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 0000000..fc9ae54
--- /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>