mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-22 10:10:14 -04:00
Add system test using SIG(0) and ACL matching
This adds a system test to verify that asynchronous SIG(0) validation correctly retains the ACL environment and network addresses of the caller, preventing unauthorized ACL bypass when evaluating match-clients and match-destinations.
This commit is contained in:
parent
adbe035808
commit
613a93478b
3 changed files with 177 additions and 0 deletions
41
bin/tests/system/sig0/ns1/named.conf.j2
Normal file
41
bin/tests/system/sig0/ns1/named.conf.j2
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
options {
|
||||
query-source address 10.53.0.1;
|
||||
notify-source 10.53.0.1;
|
||||
transfer-source 10.53.0.1;
|
||||
port @PORT@;
|
||||
pid-file "named.pid";
|
||||
listen-on { 10.53.0.1; };
|
||||
listen-on-v6 { none; };
|
||||
recursion no;
|
||||
notify no;
|
||||
};
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm @DEFAULT_HMAC@;
|
||||
};
|
||||
|
||||
controls {
|
||||
inet 10.53.0.2 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
view "v1" {
|
||||
match-clients { any; };
|
||||
zone "." {
|
||||
type hint;
|
||||
file "/dev/null";
|
||||
};
|
||||
};
|
||||
17
bin/tests/system/sig0/setup.sh
Normal file
17
bin/tests/system/sig0/setup.sh
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
# shellcheck source=conf.sh
|
||||
. ../conf.sh
|
||||
|
||||
key=$($KEYGEN -q -a RSASHA256 -b 2048 sig0.)
|
||||
119
bin/tests/system/sig0/tests_sig0.py
Normal file
119
bin/tests/system/sig0/tests_sig0.py
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
# See the COPYRIGHT file distributed with this work for additional
|
||||
# information regarding copyright ownership.
|
||||
|
||||
import base64
|
||||
import glob
|
||||
import os
|
||||
import struct
|
||||
import time
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import padding, rsa
|
||||
|
||||
import dns.flags
|
||||
import dns.message
|
||||
import dns.name
|
||||
import dns.rdata
|
||||
import dns.rdataclass
|
||||
import dns.rdatatype
|
||||
import dns.renderer
|
||||
import dns.rrset
|
||||
|
||||
import isctest
|
||||
|
||||
|
||||
def load_bind_private_key(filename):
|
||||
"""Parses a BIND 9 .private key file."""
|
||||
with open(filename, "r", encoding="utf-8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
data = {}
|
||||
for line in lines:
|
||||
if ":" in line:
|
||||
key, value = line.split(":", 1)
|
||||
data[key.strip()] = value.strip()
|
||||
|
||||
def b64int(k):
|
||||
return int.from_bytes(base64.b64decode(data[k]), byteorder="big")
|
||||
|
||||
rsa_key = rsa.RSAPrivateNumbers(
|
||||
p=b64int("Prime1"),
|
||||
q=b64int("Prime2"),
|
||||
d=b64int("PrivateExponent"),
|
||||
dmp1=b64int("Exponent1"),
|
||||
dmq1=b64int("Exponent2"),
|
||||
iqmp=b64int("Coefficient"),
|
||||
public_numbers=rsa.RSAPublicNumbers(
|
||||
e=b64int("PublicExponent"), n=b64int("Modulus")
|
||||
),
|
||||
).private_key(default_backend())
|
||||
|
||||
return rsa_key
|
||||
|
||||
|
||||
def make_sig0_query(key_file, key_name_str):
|
||||
private_key = load_bind_private_key(key_file)
|
||||
|
||||
qname = dns.name.from_text(".")
|
||||
query = dns.message.make_query(qname, dns.rdatatype.SOA)
|
||||
query.flags |= dns.flags.RD
|
||||
|
||||
# Render message to bytes (needed for signing)
|
||||
renderer = dns.renderer.Renderer()
|
||||
query.to_wire(renderer)
|
||||
msg_bytes = renderer.get_wire()
|
||||
|
||||
# SIG(0) Constants
|
||||
basename = os.path.basename(key_file)
|
||||
key_tag = int(basename.split("+")[2].split(".")[0])
|
||||
|
||||
now = int(time.time())
|
||||
expiration = now + 3600
|
||||
inception = now - 3600
|
||||
signer_name = dns.name.from_text(key_name_str)
|
||||
|
||||
# Construct SIG RDATA header (0=SIG(0), 8=RSASHA256, 0=Labels)
|
||||
sig_rdata_header = struct.pack(
|
||||
"!HBBIIIH", 0, 8, 0, 0, expiration, inception, key_tag
|
||||
)
|
||||
|
||||
sig_rdata_pre_sig = sig_rdata_header + signer_name.to_wire()
|
||||
|
||||
# Sign: ( SIG RDATA sans signature ) + ( Message )
|
||||
signature = private_key.sign(
|
||||
sig_rdata_pre_sig + msg_bytes, padding.PKCS1v15(), hashes.SHA256()
|
||||
)
|
||||
|
||||
# Create the SIG RR
|
||||
full_sig_rdata = sig_rdata_pre_sig + signature
|
||||
sig_rr = dns.rdata.from_wire(
|
||||
dns.rdataclass.ANY,
|
||||
dns.rdatatype.SIG,
|
||||
full_sig_rdata,
|
||||
0,
|
||||
len(full_sig_rdata),
|
||||
)
|
||||
sig_rrset = dns.rrset.from_rdata(qname, 0, sig_rr)
|
||||
query.additional.append(sig_rrset)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def test_sig0_acl_bypass():
|
||||
key_files = glob.glob("Ksig0.+*.private")
|
||||
assert len(key_files) == 1
|
||||
|
||||
query = make_sig0_query(key_files[0], "sig0.")
|
||||
|
||||
# Send the query
|
||||
res = isctest.query.tcp(query, "10.53.0.1")
|
||||
isctest.check.servfail(res)
|
||||
Loading…
Reference in a new issue