Skip to content
This repository has been archived by the owner on Feb 6, 2022. It is now read-only.

Commit

Permalink
Merge branch 'update'
Browse files Browse the repository at this point in the history
  • Loading branch information
ibauersachs committed May 25, 2019
2 parents 05cb5d3 + a6980e6 commit 742a8c2
Show file tree
Hide file tree
Showing 18 changed files with 1,434 additions and 127 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ A DNSSEC validating stub resolver for Java.
[![Build Status](https://travis-ci.org/ibauersachs/dnssecjava.svg?branch=master)](https://travis-ci.org/ibauersachs/dnssecjava)
[![Coverage Status](https://coveralls.io/repos/ibauersachs/dnssecjava/badge.svg)](https://coveralls.io/r/ibauersachs/dnssecjava)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.jitsi/dnssecjava/badge.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.jitsi%22%20AND%20a%3A%22dnssecjava%22)
[![Javadocs](http://javadoc.io/badge/org.jitsi/dnssecjava.svg)](https://javadoc.io/doc/org.jitsi/dnssecjava)

Is this library safe to use?
---------------------------
Expand All @@ -28,6 +29,12 @@ The Unbound prototype was stripped from all unnecessary parts, heavily
modified, complemented with more than 300 unit test and found bugs were fixed.

### Released versions
* 1.2.0:
- Fix CVE-2017-15105
- Add config option `org.jitsi.dnssec.harden_algo_downgrade`
- Fix handling of ENT in NSEC3 zones
- Fix returning YXDOMAIN RCode
- Requires dnsjava 2.1.9 for proper (EC)DSA signature validation
* 1.1.3:
- Replace jmockit with PowerMockito due to ever changing API (and there's a Debian package for PowerMockito)
- Use fixed versions for the dependencies
Expand Down
49 changes: 6 additions & 43 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<groupId>org.jitsi</groupId>
<artifactId>dnssecjava</artifactId>
<packaging>bundle</packaging>
<version>1.1.4-SNAPSHOT</version>
<version>1.2.0</version>
<name>dnssecjava</name>
<url>https://jitsi.org/dnssec</url>
<url>https://github.com/ibauersachs/dnssecjava</url>

<properties>
<powermock.version>1.6.6</powermock.version>
Expand All @@ -18,7 +18,7 @@
<dependency>
<groupId>dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<version>2.1.7</version>
<version>2.1.9</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
Expand Down Expand Up @@ -58,7 +58,7 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.6</version>
<version>2.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down Expand Up @@ -107,7 +107,6 @@
<failsOnError>true</failsOnError>
<maxAllowedViolations>0</maxAllowedViolations>
<failOnViolation>true</failOnViolation>
<linkXRef>false</linkXRef>
<consoleOutput>true</consoleOutput>
</configuration>
</execution>
Expand All @@ -116,7 +115,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.5.201505241946</version>
<version>0.8.4</version>
<executions>
<execution>
<id>prepare-agent</id>
Expand All @@ -137,42 +136,6 @@
<version>2.5.3</version>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. -->
<plugin>
<groupId>org.eclipse.m2e</groupId>
<artifactId>lifecycle-mapping</artifactId>
<version>1.0.0</version>
<configuration>
<lifecycleMappingMetadata>
<pluginExecutions>
<pluginExecution>
<pluginExecutionFilter>
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-checkstyle-plugin
</artifactId>
<versionRange>
[2.11,)
</versionRange>
<goals>
<goal>check</goal>
</goals>
</pluginExecutionFilter>
<action>
<ignore />
</action>
</pluginExecution>
</pluginExecutions>
</lifecycleMappingMetadata>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<organization>
<name>jitsi.org</name>
Expand All @@ -182,7 +145,7 @@
<url>https://github.com/ibauersachs/dnssecjava</url>
<connection>scm:git:https://github.com/ibauersachs/dnssecjava.git</connection>
<developerConnection>scm:git:https://github.com/ibauersachs/dnssecjava.git</developerConnection>
<tag>HEAD</tag>
<tag>dnssecjava-1.2.0</tag>
</scm>
<description>A DNSSEC validating stub resolver for Java.</description>
<licenses>
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/org/jitsi/dnssec/SRRset.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
*/
public class SRRset extends RRset {
private SecurityStatus securityStatus;
private Name ownerName;

/** Create a new, blank SRRset. */
public SRRset() {
Expand Down Expand Up @@ -114,4 +115,17 @@ public Name getSignerName() {

return null;
}

@Override
public Name getName() {
return this.ownerName == null ? super.getName() : this.ownerName;
}

/**
* Set the name of the records.
* @param ownerName the {@link Name} to override the original name with.
*/
public void setName(Name ownerName) {
this.ownerName = ownerName;
}
}
11 changes: 7 additions & 4 deletions src/main/java/org/jitsi/dnssec/validator/DnsSecVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.Iterator;
import java.util.List;

import org.jitsi.dnssec.SRRset;
import org.jitsi.dnssec.SecurityStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -52,6 +53,7 @@
import org.xbill.DNS.DNSSEC.DNSSECException;
import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset;
import org.xbill.DNS.Type;

/**
* A class for performing basic DNSSEC verification. The DNSJAVA package
Expand Down Expand Up @@ -112,8 +114,8 @@ private List<DNSKEYRecord> findKey(RRset dnskeyRrset, RRSIGRecord signature) {
* could not be completed (usually because the public key was not
* available).
*/
private SecurityStatus verifySignature(RRset rrset, RRSIGRecord sigrec,
RRset keyRrset) {
private SecurityStatus verifySignature(SRRset rrset, RRSIGRecord sigrec,
RRset keyRrset) {
List<DNSKEYRecord> keys = this.findKey(keyRrset, sigrec);
if (keys == null) {
logger.trace("could not find appropriate key");
Expand All @@ -130,10 +132,11 @@ private SecurityStatus verifySignature(RRset rrset, RRSIGRecord sigrec,
}

DNSSEC.verify(rrset, sigrec, key);
ValUtils.setCanonicalNsecOwner(rrset, sigrec);
return SecurityStatus.SECURE;
}
catch (DNSSECException e) {
logger.error("Failed to validate RRset", e);
logger.error("Failed to validate RRset {}/{}", rrset.getName(), Type.string(rrset.getType()), e);
status = SecurityStatus.BOGUS;
}
}
Expand All @@ -151,7 +154,7 @@ private SecurityStatus verifySignature(RRset rrset, RRSIGRecord sigrec,
* @return SecurityStatus.SECURE if the rrest verified positively,
* SecurityStatus.BOGUS otherwise.
*/
public SecurityStatus verify(RRset rrset, RRset keyRrset) {
public SecurityStatus verify(SRRset rrset, RRset keyRrset) {
Iterator<?> i = rrset.sigs();
if (!i.hasNext()) {
logger.info("RRset failed to verify due to lack of signatures");
Expand Down
91 changes: 59 additions & 32 deletions src/main/java/org/jitsi/dnssec/validator/ValUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,34 @@ public ValUtils() {
this.verifier = new DnsSecVerifier();
}

/**
* Set the owner name of NSEC RRsets to the canonical name, i.e. the name
* that is <b>not</b> expanded from a wildcard label.
* @param set The RRset to canonicalize.
* @param sig The signature that validated this RRset.
*/
public static void setCanonicalNsecOwner(SRRset set, RRSIGRecord sig) {
if (set.getType() != Type.NSEC) {
return;
}

Record nsec = set.first();
int fqdnLabelCount = nsec.getName().labels() - 1; // don't count the root label
if (nsec.getName().isWild()) {
--fqdnLabelCount; // don't count the wildcard label
}

if (sig.getLabels() == fqdnLabelCount) {
set.setName(nsec.getName());
}
else if (sig.getLabels() < fqdnLabelCount) {
set.setName(nsec.getName().wild(sig.getSigner().labels() - sig.getLabels()));
}
else {
throw new IllegalArgumentException("invalid nsec record");
}
}

/**
* Initialize the module. The recognized configuration value are
* <ul>
Expand Down Expand Up @@ -445,13 +473,14 @@ public static boolean strictSubdomain(Name domain1, Name domain2) {
* {@link NSECRecord#getNext()}).
*
* @param domain The name for which the closest encloser is queried.
* @param nsec The covering {@link NSECRecord} to check.
* @param owner The beginning of the covering {@link Name} to check.
* @param next The end of the covering {@link Name} to check.
* @return The closest encloser name of <code>domain</code> as defined by
* <code>nsec</code>.
* {@code owner} and {@code next}.
*/
public static Name closestEncloser(Name domain, NSECRecord nsec) {
Name n1 = longestCommonName(domain, nsec.getName());
Name n2 = longestCommonName(domain, nsec.getNext());
public static Name closestEncloser(Name domain, Name owner, Name next) {
Name n1 = longestCommonName(domain, owner);
Name n2 = longestCommonName(domain, next);

return (n1.labels() > n2.labels()) ? n1 : n2;
}
Expand All @@ -462,28 +491,29 @@ public static Name closestEncloser(Name domain, NSECRecord nsec) {
*
* @param domain The name for which the wildcard closest encloser is
* demanded.
* @param set The RRset containing {@code nsec} to check.
* @param nsec The covering NSEC that defines the encloser.
* @return The wildcard closest encloser name of <code>domain</code> as
* defined by <code>nsec</code>.
* @throws NameTooLongException If adding the wildcard label to the closest
* encloser results in an invalid name.
*/
public static Name nsecWildcard(Name domain, NSECRecord nsec) throws NameTooLongException {
Name origin = closestEncloser(domain, nsec);
public static Name nsecWildcard(Name domain, SRRset set, NSECRecord nsec) throws NameTooLongException {
Name origin = closestEncloser(domain, set.getName(), nsec.getNext());
return Name.concatenate(WILDCARD, origin);
}

/**
* Determine if the given NSEC proves a NameError (NXDOMAIN) for a given
* qname.
*
* @param set The RRset that contains the NSEC.
* @param nsec The NSEC to check.
* @param qname The qname to check against.
* @param signerName The signer of the NSEC RRset.
* @return true if the NSEC proves the condition.
*/
public static boolean nsecProvesNameError(NSECRecord nsec, Name qname, Name signerName) {
Name owner = nsec.getName();
public static boolean nsecProvesNameError(SRRset set, NSECRecord nsec, Name qname) {
Name owner = set.getName();
Name next = nsec.getNext();

// If NSEC owner == qname, then this NSEC proves that qname exists.
Expand All @@ -492,7 +522,7 @@ public static boolean nsecProvesNameError(NSECRecord nsec, Name qname, Name sign
}

// deny overreaching NSECs
if (!next.subdomain(signerName)) {
if (!next.subdomain(set.getSignerName())) {
return false;
}

Expand Down Expand Up @@ -539,29 +569,25 @@ else if (owner.compareTo(next) > 0) {
* Determine if a NSEC record proves the non-existence of a wildcard that
* could have produced qname.
*
* @param nsec The nsec to check.
* @param set The RRset of the NSEC record.
* @param nsec The nsec record to check.
* @param qname The qname to check against.
* @param signerName The signer of the NSEC RRset.
* @return true if the NSEC proves the condition.
*/
public static boolean nsecProvesNoWC(NSECRecord nsec, Name qname, Name signerName) {
int qnameLabels = qname.labels();
Name ce = closestEncloser(qname, nsec);
int ceLabels = ce.labels();

for (int i = qnameLabels - ceLabels; i > 0; i--) {
Name wcName = qname.wild(i);
if (nsecProvesNameError(nsec, wcName, signerName)) {
return true;
}
public static boolean nsecProvesNoWC(SRRset set, NSECRecord nsec, Name qname) {
Name ce = closestEncloser(qname, set.getName(), nsec.getNext());
int labelsToStrip = qname.labels() - ce.labels();
if (labelsToStrip > 0) {
Name wcName = qname.wild(labelsToStrip);
return nsecProvesNameError(set, nsec, wcName);
}

return false;
}

/**
* Container for responses of
* {@link ValUtils#nsecProvesNodata(NSECRecord, Name, int)}.
* {@link ValUtils#nsecProvesNodata(SRRset, NSECRecord, Name, int)}.
*/
public static class NsecProvesNodataResponse {
boolean result;
Expand All @@ -575,22 +601,23 @@ public static class NsecProvesNodataResponse {
* must still be provided proof that qname did not directly exist and that
* the wildcard is, in fact, *.closest_encloser.
*
* @param set The RRset of the NSEC record.
* @param nsec The NSEC to check
* @param qname The query name to check against.
* @param qtype The query type to check against.
* @return true if the NSEC proves the condition.
*/
public static NsecProvesNodataResponse nsecProvesNodata(NSECRecord nsec, Name qname, int qtype) {
public static NsecProvesNodataResponse nsecProvesNodata(SRRset set, NSECRecord nsec, Name qname, int qtype) {
NsecProvesNodataResponse result = new NsecProvesNodataResponse();
if (!nsec.getName().equals(qname)) {
if (!set.getName().equals(qname)) {
// empty-non-terminal checking.
// Done before wildcard, because this is an exact match,
// and would prevent a wildcard from matching.

// If the nsec is proving that qname is an ENT, the nsec owner will
// be less than qname, and the next name will be a child domain of
// the qname.
if (strictSubdomain(nsec.getNext(), qname) && nsec.getName().compareTo(qname) < 0) {
if (strictSubdomain(nsec.getNext(), qname) && set.getName().compareTo(qname) < 0) {
result.result = true;
return result;
}
Expand All @@ -600,9 +627,9 @@ public static NsecProvesNodataResponse nsecProvesNodata(NSECRecord nsec, Name qn
// have generated qname from the wildcard and b) the type map does
// not contain qtype. Note that this does NOT prove that this
// wildcard was the applicable wildcard.
if (nsec.getName().isWild()) {
if (set.getName().isWild()) {
// the is the purported closest encloser.
Name ce = new Name(nsec.getName(), 1);
Name ce = new Name(set.getName(), 1);

// The qname must be a strict subdomain of the closest encloser,
// and the qtype must be absent from the type map.
Expand Down Expand Up @@ -720,16 +747,16 @@ public JustifiedSecStatus nsecProvesNodataDsReply(Message request, SMessage resp
}

NSECRecord nsec = (NSECRecord)set.first();
ndp = ValUtils.nsecProvesNodata(nsec, qname, Type.DS);
ndp = ValUtils.nsecProvesNodata(set, nsec, qname, Type.DS);
if (ndp.result) {
hasValidNSEC = true;
if (ndp.wc != null && nsec.getName().isWild()) {
wcNsec = nsec;
}
}

if (ValUtils.nsecProvesNameError(nsec, qname, set.getSignerName())) {
ce = closestEncloser(qname, nsec);
if (ValUtils.nsecProvesNameError(set, nsec, qname)) {
ce = closestEncloser(qname, set.getName(), nsec.getNext());
}
}

Expand Down
Loading

0 comments on commit 742a8c2

Please sign in to comment.