diff --git a/.travis.yml b/.travis.yml
index e0abce3..00c74ae 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+branches:
+ only:
+ - master
language: java
after_success:
- mvn clean test jacoco:report coveralls:report
diff --git a/README.md b/README.md
index 96fc463..ed294fd 100644
--- a/README.md
+++ b/README.md
@@ -145,11 +145,18 @@ The file from which the trust anchor should be loaded. There is no default.
It must be formatted like a DNS zone master file. It can only contain DS
or DNSKEY records.
-### org.jitsi.dnssec.digest_preference
+### org.jitsi.dnssec.digest\_preference
Defines the preferred DS record digest algorithm if a zone has registered
multiple DS records. The list is comma-separated, highest preference first.
If this property is not specified, the DS record with the highest [digest ID]
(http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml) is chosen.
To stay compliant with the RFCs, the mandatory digest IDs must be listed in
-this property. The GOST digest is not (yet) implemented.
+this property.
+
+The GOST digest is not (yet) implemented.
+
+### org.jitsi.dnssec.harden\_algo\_downgrade
+Prevent algorithm downgrade when multiple algorithms are advertised in a zones
+DS records. If `false`, allows any algorithm to validate the zone.
+Default is `true`.
diff --git a/src/main/java/org/jitsi/dnssec/validator/ValUtils.java b/src/main/java/org/jitsi/dnssec/validator/ValUtils.java
index 2d6eced..5550e67 100644
--- a/src/main/java/org/jitsi/dnssec/validator/ValUtils.java
+++ b/src/main/java/org/jitsi/dnssec/validator/ValUtils.java
@@ -73,6 +73,7 @@
*/
public class ValUtils {
public static final String DIGEST_PREFERENCE = "org.jitsi.dnssec.digest_preference";
+ public static final String DIGEST_HARDEN_DOWNGRADE = "org.jitsi.dnssec.harden_algo_downgrade";
private static final Logger logger = LoggerFactory.getLogger(ValUtils.class);
private static final Name WILDCARD = Name.fromConstantString("*");
@@ -80,6 +81,7 @@ public class ValUtils {
/** A local copy of the verifier object. */
private DnsSecVerifier verifier;
private int[] digestPreference = null;
+ private boolean digestHardenDowngrade = true;
/**
* Creates a new instance of this class.
@@ -89,8 +91,11 @@ public ValUtils() {
}
/**
- * Initialize the module. The only recognized configuration value is
- * {@link #DIGEST_PREFERENCE}.
+ * Initialize the module. The recognized configuration value are
+ *
+ * - {@link #DIGEST_PREFERENCE}
+ * - {@link #DIGEST_HARDEN_DOWNGRADE}
+ *
.
*
* @param config The configuration data for this module.
*/
@@ -106,6 +111,8 @@ public void init(Properties config) {
}
}
}
+
+ this.digestHardenDowngrade = Boolean.parseBoolean(config.getProperty(DIGEST_HARDEN_DOWNGRADE));
}
/**
@@ -198,7 +205,7 @@ public KeyEntry verifyNewDNSKEYs(SRRset dnskeyRrset, SRRset dsRrset, long badKey
int favoriteDigestID = this.favoriteDSDigestID(dsRrset);
for (Iterator> i = dsRrset.rrs(); i.hasNext();) {
DSRecord ds = (DSRecord)i.next();
- if (ds.getDigestID() != favoriteDigestID) {
+ if (this.digestHardenDowngrade && ds.getDigestID() != favoriteDigestID) {
continue;
}
diff --git a/src/test/java/org/jitsi/dnssec/unbound/rpl/Rpl.java b/src/test/java/org/jitsi/dnssec/unbound/rpl/Rpl.java
index 27706f2..ee6ad30 100644
--- a/src/test/java/org/jitsi/dnssec/unbound/rpl/Rpl.java
+++ b/src/test/java/org/jitsi/dnssec/unbound/rpl/Rpl.java
@@ -27,4 +27,5 @@ public class Rpl {
public Map checks;
public TreeMap nsec3iterations;
public String digestPreference;
+ public boolean hardenAlgoDowngrade;
}
diff --git a/src/test/java/org/jitsi/dnssec/unbound/rpl/RplParser.java b/src/test/java/org/jitsi/dnssec/unbound/rpl/RplParser.java
index 06a56c6..2cae5d0 100644
--- a/src/test/java/org/jitsi/dnssec/unbound/rpl/RplParser.java
+++ b/src/test/java/org/jitsi/dnssec/unbound/rpl/RplParser.java
@@ -129,6 +129,9 @@ else if (line.matches("\\s*val-nsec3-keysize-iterations:.*")) {
else if (line.matches("\\s*val-digest-preference:.*")) {
rpl.digestPreference = line.substring(line.indexOf("\"") + 1, line.length() - 1);
}
+ else if (line.matches("\\s*harden-algo-downgrade:.*")) {
+ rpl.hardenAlgoDowngrade = !"no".equalsIgnoreCase(line.split(":")[1].trim());
+ }
else if (line.startsWith("CONFIG_END")) {
state = ParseState.Zero;
}
diff --git a/src/test/java/org/jitsi/dnssec/unbound/rpl/UnboundTests.java b/src/test/java/org/jitsi/dnssec/unbound/rpl/UnboundTests.java
index 2cd1be4..ff29f20 100644
--- a/src/test/java/org/jitsi/dnssec/unbound/rpl/UnboundTests.java
+++ b/src/test/java/org/jitsi/dnssec/unbound/rpl/UnboundTests.java
@@ -27,6 +27,7 @@
import org.jitsi.dnssec.SRRset;
import org.jitsi.dnssec.TestBase;
+import org.jitsi.dnssec.validator.ValUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
@@ -58,9 +59,11 @@ public void runUnboundTest() throws ParseException, IOException {
}
if (rpl.digestPreference != null) {
- config.put("org.jitsi.dnssec.digest_preference", rpl.digestPreference);
+ config.put(ValUtils.DIGEST_PREFERENCE, rpl.digestPreference);
}
+ config.put(ValUtils.DIGEST_HARDEN_DOWNGRADE, Boolean.toString(rpl.hardenAlgoDowngrade));
+
for (Message m : rpl.replays) {
add(stripAdditional(m));
for (RRset set : m.getSectionRRsets(Section.AUTHORITY)) {
@@ -172,8 +175,8 @@ else if (s.getType() == Type.DNAME) {
for (Check c : rpl.checks.values()) {
Message s = resolver.send(c.query);
- assertEquals(c.response.getHeader().getFlag(Flags.AD), s.getHeader().getFlag(Flags.AD));
- assertEquals(Rcode.string(c.response.getRcode()), Rcode.string(s.getRcode()));
+ assertEquals("AD Flag must match", c.response.getHeader().getFlag(Flags.AD), s.getHeader().getFlag(Flags.AD));
+ assertEquals("RCode must match", Rcode.string(c.response.getRcode()), Rcode.string(s.getRcode()));
}
}
@@ -471,6 +474,11 @@ public void val_ds_sha2_downgrade_override() throws ParseException, IOException
runUnboundTest();
}
+ @Test
+ public void val_ds_sha2_lenient() throws ParseException, IOException {
+ runUnboundTest();
+ }
+
@Test
public void val_entds() throws ParseException, IOException {
runUnboundTest();
diff --git a/src/test/resources/unbound/val_ds_gost_downgrade.rpl b/src/test/resources/unbound/val_ds_gost_downgrade.rpl
index 00e6e4e..a3930bc 100644
--- a/src/test/resources/unbound/val_ds_gost_downgrade.rpl
+++ b/src/test/resources/unbound/val_ds_gost_downgrade.rpl
@@ -4,6 +4,10 @@ server:
trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
val-override-date: "20070916134226"
target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ harden-algo-downgrade: yes
stub-zone:
name: "."
@@ -231,7 +235,7 @@ ENTRY_END
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
-REPLY QR RD RA SERVFAIL
+REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.sub.example.com. IN A
SECTION ANSWER
diff --git a/src/test/resources/unbound/val_ds_sha2.rpl b/src/test/resources/unbound/val_ds_sha2.rpl
index 111382d..4af6693 100644
--- a/src/test/resources/unbound/val_ds_sha2.rpl
+++ b/src/test/resources/unbound/val_ds_sha2.rpl
@@ -4,6 +4,10 @@ server:
trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
val-override-date: "20070916134226"
target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-dsa: yes
+ fake-sha1: yes
+ trust-anchor-signaling: no
stub-zone:
name: "."
@@ -188,7 +192,7 @@ ENTRY_END
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
-REPLY QR RD RA AD NOERROR
+REPLY QR RD RA AD DO NOERROR
SECTION QUESTION
www.sub.example.com. IN A
SECTION ANSWER
diff --git a/src/test/resources/unbound/val_ds_sha2_downgrade.rpl b/src/test/resources/unbound/val_ds_sha2_downgrade.rpl
index 2418c87..b15f39b 100644
--- a/src/test/resources/unbound/val_ds_sha2_downgrade.rpl
+++ b/src/test/resources/unbound/val_ds_sha2_downgrade.rpl
@@ -4,6 +4,11 @@ server:
trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
val-override-date: "20070916134226"
target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-dsa: yes
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ harden-algo-downgrade: yes
stub-zone:
name: "."
@@ -211,7 +216,7 @@ ENTRY_END
STEP 10 CHECK_ANSWER
ENTRY_BEGIN
MATCH all
-REPLY QR RD RA SERVFAIL
+REPLY QR RD RA DO SERVFAIL
SECTION QUESTION
www.sub.example.com. IN A
SECTION ANSWER
diff --git a/src/test/resources/unbound/val_ds_sha2_lenient.rpl b/src/test/resources/unbound/val_ds_sha2_lenient.rpl
new file mode 100644
index 0000000..631facd
--- /dev/null
+++ b/src/test/resources/unbound/val_ds_sha2_lenient.rpl
@@ -0,0 +1,229 @@
+; config options
+; The island of trust is at example.com
+server:
+ trust-anchor: "example.com. 3600 IN DS 2854 3 1 46e4ffc6e9a4793b488954bd3f0cc6af0dfb201b"
+ val-override-date: "20070916134226"
+ target-fetch-policy: "0 0 0 0 0"
+ qname-minimisation: "no"
+ fake-dsa: yes
+ fake-sha1: yes
+ trust-anchor-signaling: no
+ harden-algo-downgrade: no
+
+stub-zone:
+ name: "."
+ stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET.
+CONFIG_END
+
+SCENARIO_BEGIN Test validator with SHA256 DS downgrade to SHA1 lenience
+
+; K.ROOT-SERVERS.NET.
+RANGE_BEGIN 0 100
+ ADDRESS 193.0.14.129
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+. IN NS
+SECTION ANSWER
+. IN NS K.ROOT-SERVERS.NET.
+SECTION ADDITIONAL
+K.ROOT-SERVERS.NET. IN A 193.0.14.129
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION AUTHORITY
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+RANGE_END
+
+; a.gtld-servers.net.
+RANGE_BEGIN 0 100
+ ADDRESS 192.5.6.30
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+com. IN NS
+SECTION ANSWER
+com. IN NS a.gtld-servers.net.
+SECTION ADDITIONAL
+a.gtld-servers.net. IN A 192.5.6.30
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ENTRY_END
+RANGE_END
+
+; ns.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.4
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN NS
+SECTION ANSWER
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response to DNSKEY priming query
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+example.com. IN DNSKEY
+SECTION ANSWER
+example.com. 3600 IN DNSKEY 256 3 3 ALXLUsWqUrY3JYER3T4TBJII s70j+sDS/UT2QRp61SE7S3E EXopNXoFE73JLRmvpi/UrOO/Vz4Se 6wXv/CYCKjGw06U4WRgR YXcpEhJROyNapmdIKSx hOzfLVE1gqA0PweZR8d tY3aNQSRn3sPpwJr6Mi /PqQKAMMrZ9ckJpf1+b QMOOvxgzz2U1GS18b3y ZKcgTMEaJzd/GZYzi/B N2DzQ0MsrSwYXfsNLFO Bbs8PJMW4LYIxeeOe6rUgkWOF 7CC9Dh/dduQ1QrsJhmZAEFfd6ByYV+ ;{id = 2854 (zsk), size = 1688b}
+example.com. 3600 IN RRSIG DNSKEY DSA 2 3600 20070926134150 20070829134150 2854 example.com. MCwCFBQRtlR4BEv9ohi+PGFjp+AHsJuHAhRCvz0shggvnvI88DFnBDCczHUcVA== ;{id = 2854}
+SECTION AUTHORITY
+example.com. IN NS ns.example.com.
+example.com. 3600 IN RRSIG NS 3 2 3600 20070926134150 20070829134150 2854 example.com. MC0CFQCN+qHdJxoI/2tNKwsb08pra/G7aAIUAWA5sDdJTbrXA1/3OaesGBAO3sI= ;{id = 2854}
+SECTION ADDITIONAL
+ns.example.com. IN A 1.2.3.4
+ns.example.com. 3600 IN RRSIG A 3 3 3600 20070926135752 20070829135752 2854 example.com. MC0CFQCMSWxVehgOQLoYclB9PIAbNP229AIUeH0vNNGJhjnZiqgIOKvs1EhzqAo= ;{id = 2854}
+ENTRY_END
+
+; response for delegation to sub.example.com.
+ENTRY_BEGIN
+MATCH opcode subdomain
+ADJUST copy_id copy_query
+REPLY QR NOERROR
+SECTION QUESTION
+sub.example.com. IN A
+SECTION ANSWER
+SECTION AUTHORITY
+sub.example.com. IN NS ns.sub.example.com.
+
+; Downgrade attack: false SHA2, correct SHA1
+
+; SHA256 DS for sub.example.com.
+;sub.example.com. 3600 IN DS 30899 5 2 51be8e847cc663f2775d0f2b6d15e41553c97ecb99b8dd667f18244e2f652033
+; BAD SHA256 DS
+sub.example.com. 3600 IN DS 30899 5 2 51be8e847cc663f2775d0f2b6d15e41553c97ecb99b8dd667f18244e2f652000
+
+; SHA1 DS for sub.example.com.
+sub.example.com. 3600 IN DS 30899 RSASHA1 1 f7ed618f24d5e5202927e1d27bc2e84a141cb4b3
+sub.example.com. 3600 IN RRSIG DS 3 3 3600 20070926135752 20070829135752 2854 example.com. ACqqpk1ow07XJvN1orEpiWOeqMLdDKQtTgWB8Mp6CF/9VTfHuWWmsu8= ;{id = 2854}
+
+SECTION ADDITIONAL
+ns.sub.example.com. IN A 1.2.3.6
+ENTRY_END
+
+RANGE_END
+
+; ns.sub.example.com.
+RANGE_BEGIN 0 100
+ ADDRESS 1.2.3.6
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+sub.example.com. IN NS
+SECTION ANSWER
+sub.example.com. IN NS ns.sub.example.com.
+sub.example.com. 3600 IN RRSIG NS 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. wcpHeBILHfo8C9uxMhcW03gcURZeUffiKdSTb50ZjzTHgMNhRyMfpcvSpXEd9548A9UTmWKeLZChfr5Z/glONw== ;{id = 30899}
+SECTION ADDITIONAL
+ns.sub.example.com. IN A 1.2.3.6
+ns.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. UF7shD/gt1FOp2UHgLTNbPzVykklSXFMEtJ1xD+Hholwf/PIzd7zoaIttIYibNa4fUXCqMg22H9P7MRhfmFe6g== ;{id = 30899}
+ENTRY_END
+
+; response to DNSKEY priming query
+; sub.example.com. 3600 IN DS 30899 RSASHA1 1 f7ed618f24d5e5202927e1d27bc2e84a141cb4b3
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+sub.example.com. IN DNSKEY
+SECTION ANSWER
+sub.example.com. 3600 IN DNSKEY 256 3 5 AQPQ41chR9DEHt/aIzIFAqanbDlRflJoRs5yz1jFsoRIT7dWf0r+PeDuewdxkszNH6wnU4QL8pfKFRh5PIYVBLK3 ;{id = 30899 (zsk), size = 512b}
+sub.example.com. 3600 IN RRSIG DNSKEY 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. uNGp99iznjD7oOX02XnQbDnbg75UwBHRvZSKYUorTKvPUnCWMHKdRsQ+mf+Fx3GZ+Fz9BVjoCmQqpnfgXLEYqw== ;{id = 30899}
+SECTION AUTHORITY
+sub.example.com. IN NS ns.sub.example.com.
+sub.example.com. 3600 IN RRSIG NS 5 3 3600 20070926134150 20070829134150 30899 sub.example.com. wcpHeBILHfo8C9uxMhcW03gcURZeUffiKdSTb50ZjzTHgMNhRyMfpcvSpXEd9548A9UTmWKeLZChfr5Z/glONw== ;{id = 30899}
+SECTION ADDITIONAL
+ns.sub.example.com. IN A 1.2.3.6
+ns.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. UF7shD/gt1FOp2UHgLTNbPzVykklSXFMEtJ1xD+Hholwf/PIzd7zoaIttIYibNa4fUXCqMg22H9P7MRhfmFe6g== ;{id = 30899}
+ENTRY_END
+
+; response to query of interest
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION ANSWER
+www.sub.example.com. IN A 11.11.11.11
+www.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. 0DqqRfRtm7VSEQ4mmBbzrKRqQAay3JAE8DPDGmjtokrrjN9F1G/HxozDV7bjdIh2EChlQea8FPwf/GepJMUVxg== ;{id = 30899}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA REFUSED
+SECTION QUESTION
+ns.sub.example.com. IN A
+ENTRY_END
+
+ENTRY_BEGIN
+MATCH opcode qtype qname
+ADJUST copy_id
+REPLY QR AA REFUSED
+SECTION QUESTION
+ns.sub.example.com. IN AAAA
+ENTRY_END
+
+RANGE_END
+
+STEP 1 QUERY
+ENTRY_BEGIN
+REPLY RD DO
+SECTION QUESTION
+www.sub.example.com. IN A
+ENTRY_END
+
+; recursion happens here.
+; must servfail, BOGUS
+STEP 10 CHECK_ANSWER
+ENTRY_BEGIN
+MATCH all
+REPLY QR RD RA AD DO NOERROR
+SECTION QUESTION
+www.sub.example.com. IN A
+SECTION ANSWER
+www.sub.example.com. 3600 IN A 11.11.11.11
+www.sub.example.com. 3600 IN RRSIG A 5 4 3600 20070926134150 20070829134150 30899 sub.example.com. 0DqqRfRtm7VSEQ4mmBbzrKRqQAay3JAE8DPDGmjtokrrjN9F1G/HxozDV7bjdIh2EChlQea8FPwf/GepJMUVxg== ;{id = 30899}
+SECTION AUTHORITY
+SECTION ADDITIONAL
+ENTRY_END
+
+SCENARIO_END