The branch, master has been updated via ed61c57e023 s4:dns_server: no-op dns updates with ACCESS_DENIED should be ignored via 76fec2668e7 s4:dns_server: correctly sign dns update responses with gss-tsig like Windows via db350bc573b s4:dns_server: dns_verify_tsig should return REFUSED on error via 5906ed94f2c s4:dns_server: also search DNS_QTYPE_TKEY in the answers section if it's the last section via ae7538af044 s4:dns_server: use tkey->algorithm if available in dns_sign_tsig() via bd0235cd515 s4:dns_server: use the client provided algorithm for the fake TSIG structure via 3467d149149 s4:dns_server: only allow gss-tsig and gss.microsoft.com for TSIG via fa0f23e69ea s4:dns_server: only allow gss-tsig and gss.microsoft.com for TKEY via a56627b0d12 s4:dns_server: failed dns updates should result in REFUSED for ACCESS_DENIED via 708a6fae697 python:tests/dns_tkey: add test_update_tsig_record_access_denied() via 753428a3b6c s4:selftest/tests: pass USERNAME_UNPRIV=$DOMAIN_USER to samba.tests.dns_tkey via 88457da00d4 python:tests/dns_base: add get_unpriv_creds() helper via 848318338b2 python:tests/dns_tkey: let test_update_tsig_windows() actually pass against windows 2022 via 8324d0739df python:tests/dns_base: let verify_packet() work against Windows via de4ed363d37 python:tests/dns_tkey: test bad and changing tsig algorithms via b9b03ca503c python:tests/dns_tkey: add gss.microsoft.com tsig updates via 3c7cb85eaf8 python:tests/dns_tkey: let us have test_update_gss_tsig_tkey_req_{additional,answers}() via 740bda87a80 python:tests/dns_tkey: test TKEY with gss-tsig, gss.microsoft.com and invalid algorithms via b0af60e7850 python:tests/dns_base: maintain a dict with tkey related state via 1b1e7e06cf6 python:tests/dns_base: let dns_transaction_udp() take allow_{remaining,truncated}=True via 27d92fa808c python:tests/dns_base: pass tkey_trans(expected_rcode) via cd747307d84 python:tests/dns_base: let tkey_trans() take tkey_req_in_answers via f8dfa9b33bd python:tests/dns_base: let tkey_trans() and sign_packet() take algorithm_name as argument via 6e997f93d53 python:tests/dns_tkey: make use of self.assert_echoed_dns_error() via ce591464cb1 python:tests/dns_base: add self.assert_echoed_dns_error() via c741d0f3969 python:tests/dns_base: let dns_transaction_tcp() handle short receives via c594cbad4af python:tests/dns_base: use ndr_deepcopy() and ndr_pack() in verify_packet() via ae23d512a72 python:tests/dns_base: generate a real signature in bad_sign_packet() via 319836ce9e6 lib/addns: remove unused kerberos/gssapi includes in dns.h from 096d3807b05 build: Make "samba4" public libraries provided (mostly) for OpenChange private
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit ed61c57e02309b738e73fb12877a0a565b627724 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 30 14:52:22 2024 +0200 s4:dns_server: no-op dns updates with ACCESS_DENIED should be ignored If the client does not have permissions to update the record, but the record already has the data the update tries to apply, it's a no-op that should result in success instead of failing. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Thu Jun 6 03:18:16 UTC 2024 on atb-devel-224 commit 76fec2668e73b9d15447abee551d5c04148aaf27 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 30 14:39:28 2024 +0200 s4:dns_server: correctly sign dns update responses with gss-tsig like Windows This means we no longer generate strange errors/warnings in the Windows event log nor in the nsupdate -g output. Note: this is a only difference between gss-tsig and the legacy gss.microsoft.com algorithms. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit db350bc573b378fb0615bdd8592cc9c62f6db146 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 30 14:42:53 2024 +0200 s4:dns_server: dns_verify_tsig should return REFUSED on error BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 5906ed94f2c5c68e83c63e7c201534eeb323cfe7 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 30 14:41:21 2024 +0200 s4:dns_server: also search DNS_QTYPE_TKEY in the answers section if it's the last section BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ae7538af04435658d2ba6dcab109beecb6c5f13e Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:38:24 2024 +0200 s4:dns_server: use tkey->algorithm if available in dns_sign_tsig() BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit bd0235cd515d5602ed9501bfc810a2487364ea10 Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:38:24 2024 +0200 s4:dns_server: use the client provided algorithm for the fake TSIG structure BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 3467d1491490830d61d16cb6278051daf48466fc Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:38:24 2024 +0200 s4:dns_server: only allow gss-tsig and gss.microsoft.com for TSIG BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit fa0f23e69eaf4f475bc9dc9aa0e23c7bd5208250 Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:38:24 2024 +0200 s4:dns_server: only allow gss-tsig and gss.microsoft.com for TKEY BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit a56627b0d125ef7b456bebe307087f324f1f0422 Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:36:40 2024 +0200 s4:dns_server: failed dns updates should result in REFUSED for ACCESS_DENIED BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 708a6fae6978e1462e1a53f4ee08f11b51a5637a Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 11:40:51 2024 +0200 python:tests/dns_tkey: add test_update_tsig_record_access_denied() This demonstrates that access_denied is only generated if the client really generates a change in the database. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 753428a3b6c488c4aacea04d2ddb9ea73244695a Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 11:39:56 2024 +0200 s4:selftest/tests: pass USERNAME_UNPRIV=$DOMAIN_USER to samba.tests.dns_tkey BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 88457da00d4110b419f7a7ccabcd542fa77e463f Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 11:39:56 2024 +0200 python:tests/dns_base: add get_unpriv_creds() helper BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 848318338b2972f331e067bf1c8d6c7dac0748c8 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:17:54 2024 +0200 python:tests/dns_tkey: let test_update_tsig_windows() actually pass against windows 2022 BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8324d0739dfdd0a081c403e298a9038ee7df681f Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:17:54 2024 +0200 python:tests/dns_base: let verify_packet() work against Windows BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit de4ed363d378f2065a4634f94af80ea0e3965c96 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 17:26:39 2024 +0200 python:tests/dns_tkey: test bad and changing tsig algorithms BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b9b03ca503c43c7ee06df6c331839bd47f9eac8c Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 17:18:34 2024 +0200 python:tests/dns_tkey: add gss.microsoft.com tsig updates BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 3c7cb85eaf8371be55a371601cc354440dab7a94 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 14:15:45 2024 +0200 python:tests/dns_tkey: let us have test_update_gss_tsig_tkey_req_{additional,answers}() Also test using the additional record in the answers section. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 740bda87a80b97816d892e8f7aae28759f6916ec Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 16:41:12 2024 +0200 python:tests/dns_tkey: test TKEY with gss-tsig, gss.microsoft.com and invalid algorithms BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b0af60e7850e656ef98edeac657c66b853080dab Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 14:10:52 2024 +0200 python:tests/dns_base: maintain a dict with tkey related state This will allow tests to backup the whole state and mix them. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 1b1e7e06cf6ebd283de73c351267d53b42663d2f Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 14:14:11 2024 +0200 python:tests/dns_base: let dns_transaction_udp() take allow_{remaining,truncated}=True BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 27d92fa808c6617353c36fdb230504e880f4925b Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 16:07:53 2024 +0200 python:tests/dns_base: pass tkey_trans(expected_rcode) BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit cd747307d845f3cff723a7916aeeb31458f19202 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 14:08:13 2024 +0200 python:tests/dns_base: let tkey_trans() take tkey_req_in_answers It's possible to put the additional into the answers section, so we should be able to test that. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit f8dfa9b33bdedffbe2e3b6e229ffae4beb3c712e Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:17:54 2024 +0200 python:tests/dns_base: let tkey_trans() and sign_packet() take algorithm_name as argument BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6e997f93d53ac45af79aec030bad73f51bdc5629 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:35:58 2024 +0200 python:tests/dns_tkey: make use of self.assert_echoed_dns_error() Failed DNS updates just echo the request flaged as response, all other elements are unchanged. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ce591464cb12ab00a5d5752a7cea5f909c3c3f1b Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:35:58 2024 +0200 python:tests/dns_base: add self.assert_echoed_dns_error() BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit c741d0f3969abe821e8ee2a10f848159eb2749fe Author: Stefan Metzmacher <me...@samba.org> Date: Fri May 31 08:07:24 2024 +0200 python:tests/dns_base: let dns_transaction_tcp() handle short receives With socket_wrapper we only get 1500 byte chunks... BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit c594cbad4af97031bb7b5b0eb2fb228b00acf646 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:16:40 2024 +0200 python:tests/dns_base: use ndr_deepcopy() and ndr_pack() in verify_packet() BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ae23d512a724650ae2de1178ac43deff8266aa56 Author: Stefan Metzmacher <me...@samba.org> Date: Wed May 29 13:11:24 2024 +0200 python:tests/dns_base: generate a real signature in bad_sign_packet() We just destroy the signature bytes but keep the header unchanged. This makes it easier to look at it in wireshark. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13019 Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 319836ce9e65d68aa98696f387c0d3e6d99e57b8 Author: Stefan Metzmacher <me...@samba.org> Date: Wed Jun 5 17:46:53 2024 +0200 lib/addns: remove unused kerberos/gssapi includes in dns.h Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Andrew Bartlett <abart...@samba.org> ----------------------------------------------------------------------- Summary of changes: lib/addns/dns.h | 2 - python/samba/tests/dns_base.py | 213 +++++++++++++++------- python/samba/tests/dns_tkey.py | 325 ++++++++++++++++++++++++++++++---- python/samba/tests/join.py | 2 +- source4/dns_server/dns_crypto.c | 49 ++++- source4/dns_server/dns_query.c | 27 ++- source4/dns_server/dns_update.c | 11 ++ source4/dns_server/dnsserver_common.c | 2 + source4/selftest/tests.py | 6 +- 9 files changed, 526 insertions(+), 111 deletions(-) Changeset truncated at 500 lines: diff --git a/lib/addns/dns.h b/lib/addns/dns.h index abf0906fdab..6e605cbec2e 100644 --- a/lib/addns/dns.h +++ b/lib/addns/dns.h @@ -27,8 +27,6 @@ #include "../replace/replace.h" #include "system/network.h" -#include "system/kerberos.h" -#include "system/gssapi.h" /* make sure we have included the correct config.h */ #ifndef NO_CONFIG_H /* for some tests */ diff --git a/python/samba/tests/dns_base.py b/python/samba/tests/dns_base.py index d320a0e9183..43a62b1ac57 100644 --- a/python/samba/tests/dns_base.py +++ b/python/samba/tests/dns_base.py @@ -20,6 +20,7 @@ from samba.tests import TestCaseInTempDir from samba.dcerpc import dns, dnsp from samba import gensec, tests from samba import credentials +from samba import NTSTATUSError import struct import samba.ndr as ndr import random @@ -76,6 +77,24 @@ class DNSTest(TestCaseInTempDir): self.assertEqual(p_opcode, opcode, "Expected OPCODE %s, got %s" % (opcode, p_opcode)) + def assert_dns_flags_equals(self, packet, flags): + "Helper function to check opcode" + p_flags = packet.operation & (~(dns.DNS_OPCODE|dns.DNS_RCODE)) + self.assertEqual(p_flags, flags, "Expected FLAGS %02x, got %02x" % + (flags, p_flags)) + + def assert_echoed_dns_error(self, request, response, response_p, rcode): + + request_p = ndr.ndr_pack(request) + + self.assertEqual(response.id, request.id) + self.assert_dns_rcode_equals(response, rcode) + self.assert_dns_opcode_equals(response, request.operation & dns.DNS_OPCODE) + self.assert_dns_flags_equals(response, + (request.operation | dns.DNS_FLAG_REPLY) & (~(dns.DNS_OPCODE|dns.DNS_RCODE))) + self.assertEqual(len(response_p), len(request_p)) + self.assertEqual(response_p[4:], request_p[4:]) + def make_name_packet(self, opcode, qid=None): "Helper creating a dns.name_packet" p = dns.name_packet() @@ -112,6 +131,8 @@ class DNSTest(TestCaseInTempDir): return self.creds.get_realm().lower() def dns_transaction_udp(self, packet, host, + allow_remaining=False, + allow_truncated=False, dump=False, timeout=None): "send a DNS query and read the reply" s = None @@ -128,8 +149,22 @@ class DNSTest(TestCaseInTempDir): recv_packet = s.recv(2048, 0) if dump: print(self.hexdump(recv_packet)) - response = ndr.ndr_unpack(dns.name_packet, recv_packet) + if allow_truncated: + # with allow_remaining + # we add some zero bytes + # in order to also parse truncated + # responses + recv_packet_p = recv_packet + 32*b"\x00" + allow_remaining = True + else: + recv_packet_p = recv_packet + response = ndr.ndr_unpack(dns.name_packet, recv_packet_p, + allow_remaining=allow_remaining) return (response, recv_packet) + except RuntimeError as re: + if s is not None: + s.close() + raise AssertionError(re) finally: if s is not None: s.close() @@ -151,11 +186,26 @@ class DNSTest(TestCaseInTempDir): tcp_packet += send_packet s.sendall(tcp_packet) - recv_packet = s.recv(0xffff + 2, 0) + recv_packet = b'' + length = None + for i in range(0, 2 + 0xffff): + if len(recv_packet) >= 2: + length, = struct.unpack('!H', recv_packet[0:2]) + remaining = 2 + length + else: + remaining = 2 + 12 + remaining -= len(recv_packet) + if remaining == 0: + break + recv_packet += s.recv(remaining, 0) if dump: print(self.hexdump(recv_packet)) response = ndr.ndr_unpack(dns.name_packet, recv_packet[2:]) + except RuntimeError as re: + if s is not None: + s.close() + raise AssertionError(re) finally: if s is not None: s.close() @@ -217,18 +267,41 @@ class DNSTKeyTest(DNSTest): self.creds.set_username(tests.env_get_var_value('USERNAME')) self.creds.set_password(tests.env_get_var_value('PASSWORD')) self.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) + + self.unpriv_creds = None + self.newrecname = "tkeytsig.%s" % self.get_dns_domain() - def tkey_trans(self, creds=None): + def get_unpriv_creds(self): + if self.unpriv_creds is not None: + return self.unpriv_creds + + self.unpriv_creds = credentials.Credentials() + self.unpriv_creds.guess(self.lp_ctx) + self.unpriv_creds.set_username(tests.env_get_var_value('USERNAME_UNPRIV')) + self.unpriv_creds.set_password(tests.env_get_var_value('PASSWORD_UNPRIV')) + self.unpriv_creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) + + return self.unpriv_creds + + def tkey_trans(self, creds=None, algorithm_name="gss-tsig", + tkey_req_in_answers=False, + expected_rcode=dns.DNS_RCODE_OK): "Do a TKEY transaction and establish a gensec context" if creds is None: creds = self.creds - self.key_name = "%s.%s" % (uuid.uuid4(), self.get_dns_domain()) + mech = 'spnego' + + tkey = {} + tkey['name'] = "%s.%s" % (uuid.uuid4(), self.get_dns_domain()) + tkey['creds'] = creds + tkey['mech'] = mech + tkey['algorithm'] = algorithm_name p = self.make_name_packet(dns.DNS_OPCODE_QUERY) - q = self.make_name_question(self.key_name, + q = self.make_name_question(tkey['name'], dns.DNS_QTYPE_TKEY, dns.DNS_QCLASS_IN) questions = [] @@ -236,30 +309,30 @@ class DNSTKeyTest(DNSTest): self.finish_name_packet(p, questions) r = dns.res_rec() - r.name = self.key_name + r.name = tkey['name'] r.rr_type = dns.DNS_QTYPE_TKEY r.rr_class = dns.DNS_QCLASS_IN r.ttl = 0 r.length = 0xffff rdata = dns.tkey_record() - rdata.algorithm = "gss-tsig" + rdata.algorithm = algorithm_name rdata.inception = int(time.time()) rdata.expiration = int(time.time()) + 60 * 60 rdata.mode = dns.DNS_TKEY_MODE_GSSAPI rdata.error = 0 rdata.other_size = 0 - self.g = gensec.Security.start_client(self.settings) - self.g.set_credentials(creds) - self.g.set_target_service("dns") - self.g.set_target_hostname(self.server) - self.g.want_feature(gensec.FEATURE_SIGN) - self.g.start_mech_by_name("spnego") + tkey['gensec'] = gensec.Security.start_client(self.settings) + tkey['gensec'].set_credentials(creds) + tkey['gensec'].set_target_service("dns") + tkey['gensec'].set_target_hostname(self.server) + tkey['gensec'].want_feature(gensec.FEATURE_SIGN) + tkey['gensec'].start_mech_by_name(tkey['mech']) finished = False client_to_server = b"" - (finished, server_to_client) = self.g.update(client_to_server) + (finished, server_to_client) = tkey['gensec'].update(client_to_server) self.assertFalse(finished) data = [x if isinstance(x, int) else ord(x) for x in list(server_to_client)] @@ -268,56 +341,76 @@ class DNSTKeyTest(DNSTest): r.rdata = rdata additional = [r] - p.arcount = 1 - p.additional = additional + if tkey_req_in_answers: + p.ancount = 1 + p.answers = additional + else: + p.arcount = 1 + p.additional = additional (response, response_packet) =\ self.dns_transaction_tcp(p, self.server_ip) + if expected_rcode != dns.DNS_RCODE_OK: + self.assert_echoed_dns_error(p, response, response_packet, expected_rcode) + return self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) tkey_record = response.answers[0].rdata server_to_client = bytes(tkey_record.key_data) - (finished, client_to_server) = self.g.update(server_to_client) + (finished, client_to_server) = tkey['gensec'].update(server_to_client) self.assertTrue(finished) + self.tkey = tkey + self.verify_packet(response, response_packet) def verify_packet(self, response, response_packet, request_mac=b""): + self.assertEqual(response.arcount, 1) self.assertEqual(response.additional[0].rr_type, dns.DNS_QTYPE_TSIG) + if self.tkey['algorithm'] == "gss-tsig": + gss_tsig = True + else: + gss_tsig = False + + request_mac_len = b"" + if len(request_mac) > 0 and gss_tsig: + request_mac_len = struct.pack('!H', len(request_mac)) + tsig_record = response.additional[0].rdata mac = bytes(tsig_record.mac) + self.assertEqual(tsig_record.original_id, response.id) + self.assertEqual(tsig_record.mac_size, len(mac)) + # Cut off tsig record from dns response packet for MAC verification # and reset additional record count. - key_name_len = len(self.key_name) + 2 - tsig_record_len = len(ndr.ndr_pack(tsig_record)) + key_name_len + 10 - - # convert str/bytes to a list (of string char or int) - # so it can be modified - response_packet_list = [x if isinstance(x, int) else ord(x) for x in response_packet] - del response_packet_list[-tsig_record_len:] - response_packet_list[11] = 0 - - # convert modified list (of string char or int) to str/bytes - response_packet_wo_tsig = bytes(response_packet_list) + response_copy = ndr.ndr_deepcopy(response) + response_copy.arcount = 0 + response_packet_wo_tsig = ndr.ndr_pack(response_copy) fake_tsig = dns.fake_tsig_rec() - fake_tsig.name = self.key_name + fake_tsig.name = self.tkey['name'] fake_tsig.rr_class = dns.DNS_QCLASS_ANY fake_tsig.ttl = 0 fake_tsig.time_prefix = tsig_record.time_prefix fake_tsig.time = tsig_record.time fake_tsig.algorithm_name = tsig_record.algorithm_name fake_tsig.fudge = tsig_record.fudge - fake_tsig.error = 0 - fake_tsig.other_size = 0 + fake_tsig.error = tsig_record.error + fake_tsig.other_size = tsig_record.other_size + fake_tsig.other_data = tsig_record.other_data fake_tsig_packet = ndr.ndr_pack(fake_tsig) - data = request_mac + response_packet_wo_tsig + fake_tsig_packet - self.g.check_packet(data, data, mac) + data = request_mac_len + request_mac + response_packet_wo_tsig + fake_tsig_packet + try: + self.tkey['gensec'].check_packet(data, data, mac) + except NTSTATUSError as nt: + raise AssertionError(nt) - def sign_packet(self, packet, key_name): + def sign_packet(self, packet, key_name, + algorithm_name="gss-tsig", + bad_sig=False): "Sign a packet, calculate a MAC and add TSIG record" packet_data = ndr.ndr_pack(packet) @@ -327,18 +420,35 @@ class DNSTKeyTest(DNSTest): fake_tsig.ttl = 0 fake_tsig.time_prefix = 0 fake_tsig.time = int(time.time()) - fake_tsig.algorithm_name = "gss-tsig" + fake_tsig.algorithm_name = algorithm_name fake_tsig.fudge = 300 fake_tsig.error = 0 fake_tsig.other_size = 0 fake_tsig_packet = ndr.ndr_pack(fake_tsig) data = packet_data + fake_tsig_packet - mac = self.g.sign_packet(data, data) + mac = self.tkey['gensec'].sign_packet(data, data) mac_list = [x if isinstance(x, int) else ord(x) for x in list(mac)] + if bad_sig: + if len(mac) > 8: + mac_list[-8] = mac_list[-8] ^ 0xff + if len(mac) > 7: + mac_list[-7] = ord('b') + if len(mac) > 6: + mac_list[-6] = ord('a') + if len(mac) > 5: + mac_list[-5] = ord('d') + if len(mac) > 4: + mac_list[-4] = ord('m') + if len(mac) > 3: + mac_list[-3] = ord('a') + if len(mac) > 2: + mac_list[-2] = ord('c') + if len(mac) > 1: + mac_list[-1] = mac_list[-1] ^ 0xff rdata = dns.tsig_record() - rdata.algorithm_name = "gss-tsig" + rdata.algorithm_name = algorithm_name rdata.time_prefix = 0 rdata.time = fake_tsig.time rdata.fudge = 300 @@ -363,33 +473,10 @@ class DNSTKeyTest(DNSTest): return mac def bad_sign_packet(self, packet, key_name): - """Add bad signature for a packet by bitflipping - the final byte in the MAC""" - - mac_list = [x if isinstance(x, int) else ord(x) for x in list("badmac")] - - rdata = dns.tsig_record() - rdata.algorithm_name = "gss-tsig" - rdata.time_prefix = 0 - rdata.time = int(time.time()) - rdata.fudge = 300 - rdata.original_id = packet.id - rdata.error = 0 - rdata.other_size = 0 - rdata.mac = mac_list - rdata.mac_size = len(mac_list) + """Add bad signature for a packet by + bitflipping and hardcoding bytes at the end of the MAC""" - r = dns.res_rec() - r.name = key_name - r.rr_type = dns.DNS_QTYPE_TSIG - r.rr_class = dns.DNS_QCLASS_ANY - r.ttl = 0 - r.length = 0xffff - r.rdata = rdata - - additional = [r] - packet.additional = additional - packet.arcount = 1 + return self.sign_packet(packet, key_name, bad_sig=True) def search_record(self, name): p = self.make_name_packet(dns.DNS_OPCODE_QUERY) diff --git a/python/samba/tests/dns_tkey.py b/python/samba/tests/dns_tkey.py index 69af14d6f10..f8417ea119d 100644 --- a/python/samba/tests/dns_tkey.py +++ b/python/samba/tests/dns_tkey.py @@ -19,6 +19,7 @@ import sys import optparse import samba.getopt as options +import samba.ndr as ndr from samba.dcerpc import dns from samba.tests.subunitrun import SubunitOptions, TestProgram from samba.tests.dns_base import DNSTKeyTest @@ -55,17 +56,34 @@ class TestDNSUpdates(DNSTKeyTest): self.server_ip = server_ip super().setUp() - def test_tkey(self): - "test DNS TKEY handshake" + def test_tkey_gss_tsig(self): + "test DNS TKEY handshake with gss-tsig" self.tkey_trans() + def test_tkey_gss_microsoft_com(self): + "test DNS TKEY handshake with gss.microsoft.com" + + self.tkey_trans(algorithm_name="gss.microsoft.com") + + def test_tkey_invalid_gss_TSIG(self): + "test DNS TKEY handshake with invalid gss-TSIG" + + self.tkey_trans(algorithm_name="gss-TSIG", + expected_rcode=dns.DNS_RCODE_REFUSED) + + def test_tkey_invalid_gss_MICROSOFT_com(self): + "test DNS TKEY handshake with invalid gss.MICROSOFT.com" + + self.tkey_trans(algorithm_name="gss.MICROSOFT.com", + expected_rcode=dns.DNS_RCODE_REFUSED) + def test_update_wo_tsig(self): "test DNS update without TSIG record" p = self.make_update_request() (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_REFUSED) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) @@ -78,10 +96,7 @@ class TestDNSUpdates(DNSTKeyTest): p = self.make_update_request() self.sign_packet(p, "badkey") (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTAUTH) - tsig_record = response.additional[0].rdata - self.assertEqual(tsig_record.error, dns.DNS_RCODE_BADKEY) - self.assertEqual(tsig_record.mac_size, 0) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) @@ -92,23 +107,149 @@ class TestDNSUpdates(DNSTKeyTest): self.tkey_trans() p = self.make_update_request() - self.bad_sign_packet(p, self.key_name) + self.bad_sign_packet(p, self.tkey['name']) (response, response_p) = self.dns_transaction_udp(p, self.server_ip) - self.assert_dns_rcode_equals(response, dns.DNS_RCODE_NOTAUTH) - tsig_record = response.additional[0].rdata - self.assertEqual(tsig_record.error, dns.DNS_RCODE_BADSIG) - self.assertEqual(tsig_record.mac_size, 0) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) rcode = self.search_record(self.newrecname) self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) - def test_update_tsig(self): - "test DNS update with correct TSIG record" + def test_update_tsig_bad_algorithm(self): + "test DNS update with a TSIG record with a bad algorithm" self.tkey_trans() + algorithm_name = "gss-TSIG" p = self.make_update_request() - mac = self.sign_packet(p, self.key_name) + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_echoed_dns_error(p, response, response_p, dns.DNS_RCODE_REFUSED) + + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_NXDOMAIN) + + def test_update_tsig_changed_algorithm1(self): + "test DNS update with a TSIG record with a changed algorithm" + + algorithm_name = "gss-tsig" + self.tkey_trans(algorithm_name=algorithm_name) + + # Now delete the record, it's most likely + # a no-op as it should not be there if the test + # runs the first time + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) + (response, response_p) = self.dns_transaction_udp(p, self.server_ip) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Now do an update with the algorithm_name + # changed in the requests TSIG message. + p = self.make_update_request() + algorithm_name = "gss.microsoft.com" + mac = self.sign_packet(p, self.tkey['name'], + algorithm_name=algorithm_name) + algorithm_name = "gss-tsig" + (response, response_p) = self.dns_transaction_udp(p, self.server_ip, + allow_remaining=True) + self.assert_dns_rcode_equals(response, dns.DNS_RCODE_OK) + self.verify_packet(response, response_p, mac) + + # Check the record is around + rcode = self.search_record(self.newrecname) + self.assert_rcode_equals(rcode, dns.DNS_RCODE_OK) + + # Now delete the record, with the original + # algorithm_name used in the tkey exchange + p = self.make_update_request(delete=True) + mac = self.sign_packet(p, self.tkey['name'], algorithm_name=algorithm_name) -- Samba Shared Repository