Compare commits

..

No commits in common. "a72a903d0be131daa93e14feaa7d4bcdce3e958e" and "7a15f36b1737389ac7716ebe1cec1c9a1ae68491" have entirely different histories.

15 changed files with 102 additions and 256 deletions

115
README
View File

@ -1,115 +0,0 @@
DNSSECValTool
-------------
This is a command line Java tool for doing DNSSEC response
validatation against a single authoritative DNS server.
usage: java -jar dnssecvaltool.jar [..options..]
server: the DNS server to query.
query: a name [type [flags]] string.
query_file: a list of queries, one query per line.
count: send up to'count' queries, then stop.
dnskey_file: a file containing DNSKEY RRs to trust.
dnskey_query: query 'server' for DNSKEY at given name to trust,
may repeat
error_file: write DNSSEC validation failure details to this file
The DNSSECValTool needs a server to query ('server'), a query or list
of queries ('query' or 'query_file'), and a set of DNSKEYs to trust
('dnskey_file' or 'dnskey_query') -- these keys MUST be the ones used
to sign everything in the responses.
By default it logs everything to stdout. DNSSEC validation errors
(which is most of the output) can be redirected to a file (which will
be appended to if it already exists).
Note that the DNSSECValTool will skip queries if the qname isn't a
subdomain (or matches) the names of the DNSKEYs that have been added.
query_file
----------
This is a file of one query per line, with a query formatted as:
qname [qtype] [qclass] [flags]
For example:
pietbarber.com ns +ad
blacka.com a IN +do
verisign.com
The DO bit is redundant since all queries will be made with the DO bit
set.
Note: at the moment, flags are ignored.
dnskey_file
-----------
The is a list of DNSKEYs in zone file format. It will ignore zone
file comments and non-DNSKEY records, so you can just use dig output:
dig @0 edu dnskey +dnssec > keys
dig @0 net dnskey +dnssec >> keys
dnskey_query
------------
For each one of these, do a DNSKEY query to the server for that name,
and add the resultant keys to the set of trusted keys.
Generating Queries
------------------
The query files are basically the same as those used by the
dnsreconciler tool, so similar techniques can be used to query names
out of ISFs, etc. Here is a little perl code that will generate
queries for domain.tld, domain_.tld, and nameserver.tld for "EDU"
only:
#! /usr/bin/perl
while (<>) {
# parse domain table lines
/^i A / && do {
@fields = split();
$dn = $fields[3];
($dom, $tld) = split(/\./, $dn, 2);
next if $tld ne "EDU";
print "$dn. A\n";
print "${dom}_.$tld. A\n";
};
# parse nameserver table lines
/^i B / && do {
@fields = split();
$ns = $fields[3];
print "$ns. A\n";
};
}
Examples
--------
1. Query "a.edu-servers.net", fetching the .edu keys directly from
that server. Use queries.txt for the queries, and log all DNSSEC
validation failures to 'dnssecvaltool_errors.log'.
java -jar dnssecvaltool.jar server=a.edu-servers.net \
dnskey_query=edu \
query_file=queries.txt \
error_file=dnssecvaltool_errors.log
2. Query localhost with a single query for edu/soa, using stored keys
in the file 'keys'. Validation failures will be logged to stdout.
java -jar dnssecvaltool.jar server=127.0.0.1 \
dnskey_file=keys \
query="edu soa"
3. Query "a.gov-servers.net", fetching the .gov keys directly from
that server, then query for nasa.gov/A.
java -jar dnssecvaltool.jar server=a.gov-servers.net \
dnskey_query=gov \
query="nasa.gov a"

View File

@ -1 +0,0 @@
version=1.0.3

View File

@ -5,7 +5,7 @@
<property file="build.properties" />
<property file="VERSION" />
<property name="distname" value="dnssecvaltool-${version}" />
<property name="distname" value="dnssecreconciler-${version}" />
<property name="build.dir" value="build" />
<property name="build.dest" value="${build.dir}/classes" />
@ -40,14 +40,15 @@
<target name="jar" depends="usage,compile">
<jar destfile="${build.lib.dest}/dnssecvaltool.jar">
<jar destfile="${build.lib.dest}/dnssecreconciler.jar">
<zipfileset dir="${build.dest}" includes="**/*.class" />
<zipfileset src="${lib.dir}/dnsjava-2.1.3-vrsn-3.jar" />
<zipfileset src="${lib.dir}/dnsjava-2.0.8-vrsn-2.jar" />
<zipfileset src="${lib.dir}/log4j-1.2.15.jar" />
<zipfileset dir="${lib.dir}" prefix="lib" includes="**/*.properties" />
<manifest>
<attribute name="Main-Class"
value="com.verisign.cl.DNSSECValTool" />
value="com.verisign.cl.DNSSECReconciler" />
</manifest>
</jar>
</target>
@ -66,15 +67,6 @@
</javadoc>
</target>
<target name="dist" depends="usage,jar">
<property name="dprefix" value="dnssecvaltool-${version}" />
<property name="tarfile" value="${dprefix}.tar.gz" />
<tar destfile="${tarfile}" compression="gzip">
<tarfileset dir="${build.lib.dest}" prefix="${dprefix}"
includes="*.jar" />
<tarfileset dir="." prefix="${dprefix}" includes="README" />
</tar>
</target>
<target name="clean" depends="usage">
<delete dir="${build.dest}" />
@ -84,7 +76,7 @@
<target name="usage">
<echo message=" " />
<echo message="DNSSECValTool v. ${version} Build System" />
<echo message="DNSSECReconciler v. ${version} Build System" />
<echo message="--------------------------------" />
<echo message="Available Targets:" />
<echo message=" compile - compiles the source code" />

Binary file not shown.

Binary file not shown.

24
lib/log4j.properties Normal file
View File

@ -0,0 +1,24 @@
####################################################################
#
# L O G 4 j A P P E N D E R s
#
###################################################################
###################
# Write Output to Console (aka TTY)
#
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
####################################################################
#
# R O O T D E B U G G I N G L E V E L
#
###################################################################
######################
# Set root logger level to an (Appender)
#
log4j.rootLogger=FATAL, console

View File

@ -4,16 +4,14 @@ import java.io.*;
import java.net.SocketTimeoutException;
import java.util.*;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.xbill.DNS.*;
import com.verisign.tat.dnssec.CaptiveValidator;
import com.verisign.tat.dnssec.SecurityStatus;
import com.verisign.tat.dnssec.Util;
public class DNSSECValTool {
public class DNSSECReconciler {
/**
* Invoke with java -jar dnssecreconciler.jar server=127.0.0.1 \
@ -34,9 +32,8 @@ public class DNSSECValTool {
public List<String> dnskeyNames;
public String errorFile;
public long count = 0;
public boolean debug = false;
DNSSECValTool() {
DNSSECReconciler() {
validator = new CaptiveValidator();
}
@ -203,12 +200,6 @@ public class DNSSECValTool {
}
// Log our set of trusted keys
List<String> trustedKeys = validator.listTrustedKeys();
if (trustedKeys.size() == 0) {
System.err.println("ERROR: no trusted keys found/provided.");
return;
}
for (String key : validator.listTrustedKeys()) {
System.out.println("Trusted Key: " + key);
}
@ -224,20 +215,11 @@ public class DNSSECValTool {
Name zone = zoneFromQuery(query);
// Skip queries in zones that we don't have keys for
if (zone == null) {
if (debug) {
System.out.println("DEBUG: skipping query " + queryToString(query));
}
query = nextQuery();
continue;
}
if (debug) {
System.out.println("DEBUG: querying for: " + queryToString(query));
}
Message response = resolve(query);
if (response == null) {
System.out.println("ERROR: No response for query: " + queryToString(query));
continue;
}
byte result = validator.validateMessage(response, zone.toString());
@ -266,7 +248,6 @@ public class DNSSECValTool {
errorCount++;
break;
case SecurityStatus.SECURE:
if (debug) System.out.println("DEBUG: response for " + queryToString(query) + " was valid.");
validCount++;
break;
}
@ -277,7 +258,6 @@ public class DNSSECValTool {
}
if (count > 0 && total >= count) {
if (debug) System.out.println("DEBUG: reached maximum number of queries, exiting");
break;
}
@ -290,25 +270,21 @@ public class DNSSECValTool {
}
private static void usage() {
System.err.println("usage: java -jar dnssecvaltool.jar [..options..]");
System.err.println("usage: java -jar dnssecreconiler.jar [..options..]");
System.err.println(" server: the DNS server to query.");
System.err.println(" query: a name [type [flags]] string.");
System.err.println(" query_file: a list of queries, one query per line.");
System.err.println(" count: send up to'count' queries, then stop.");
System.err.println(" dnskey_file: a file containing DNSKEY RRs to trust.");
System.err.println(" dnskey_query: query 'server' for DNSKEY at given name to trust, may repeat.");
System.err.println(" error_file: write DNSSEC validation failure details to this file.");
System.err.println(" dnskey_query: query 'server' for DNSKEY at given name to trust, may repeat");
System.err.println(" error_file: write DNSSEC validation failure details to this file");
}
public static void main(String[] argv) {
// Set up Log4J to just log to console.
BasicConfigurator.configure();
// And raise the log level quite high
Logger rootLogger = Logger.getRootLogger();
rootLogger.setLevel(Level.FATAL);
PropertyConfigurator.configure("lib/log4j.properties");
DNSSECValTool dr = new DNSSECValTool();
DNSSECReconciler dr = new DNSSECReconciler();
try {
// Parse the command line options
@ -341,9 +317,6 @@ public class DNSSECValTool {
dr.dnskeyNames = new ArrayList<String>();
}
dr.dnskeyNames.add(optarg);
} else if (opt.equals("debug")) {
dr.debug = Boolean.parseBoolean(optarg);
rootLogger.setLevel(Level.TRACE);
} else {
System.err.println("Unrecognized option: " + opt);
usage();

View File

@ -26,7 +26,6 @@ package com.verisign.tat.dnssec;
import org.apache.log4j.Logger;
import org.xbill.DNS.*;
import org.xbill.DNS.utils.base64;
import java.io.IOException;
@ -378,13 +377,6 @@ public class CaptiveValidator {
// If so, an additional check will need to be made in the authority
// section.
wc = ValUtils.rrsetWildcard(rrsets[i]);
// if the wildcard expansion equals the orig name, then we
// have the actual wildcard record and no actual wildcard
// expansion happened, so we shouldn't do the extra
// validation.
if (wc.equals(rrsets[i].getName())) {
wc = null;
}
// Notice a DNAME that should be followed by an unsigned CNAME.
if ((qtype != Type.DNAME) && (rrsets[i].getType() == Type.DNAME)) {
@ -454,8 +446,8 @@ public class CaptiveValidator {
// If after all this, we still haven't proven the positive wildcard
// response, fail.
if ((wc != null) && !wcNSEC_ok) {
mErrorList.add("Positive response was wildcard expansion " +
"and did not prove original data did not exist.");
// log.debug("positive response was wildcard expansion and "
// + "did not prove original data did not exist");
m.setStatus(SecurityStatus.BOGUS);
return;
@ -586,7 +578,7 @@ public class CaptiveValidator {
return;
}
if (nsec3s != null && nsec3s.size() > 0) {
if (nsec3s.size() > 0) {
byte status = NSEC3ValUtils.proveNoDS(nsec3s, delegation, nsec3zone, mErrorList);
if (status != SecurityStatus.SECURE) {
@ -952,7 +944,7 @@ public class CaptiveValidator {
}
ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
log.debug("Response was classified as a " + subtype);
switch (subtype) {
case POSITIVE:
log.trace("Validating a positive response");
@ -1004,18 +996,6 @@ public class CaptiveValidator {
return validateMessage(sm, z);
}
public byte validateMessage(byte[] messagebytes, String zone)
throws IOException {
Message message = new Message(messagebytes);
return validateMessage(message, zone);
}
public byte validateMessage(String b64messagebytes, String zone)
throws IOException {
byte[] messagebytes = base64.fromString(b64messagebytes);
return validateMessage(messagebytes, zone);
}
public List<String> listTrustedKeys() {
return mTrustedKeys.listTrustAnchors();
}

View File

@ -46,53 +46,46 @@ public class DnsSecVerifier {
private Logger log = Logger.getLogger(this.getClass());
/**
* This is a mapping of DNSSEC algorithm numbers to JCA algorithm
* identifiers.
* This is a mapping of DNSSEC algorithm numbers/private identifiers to JCA
* algorithm identifiers.
*/
private HashMap<Integer, AlgEntry> mAlgorithmMap;
/**
* This is a mapping of DNSSEC private (DNS name) identifiers to JCA
* algorithm identifiers.
*/
private HashMap<Name, AlgEntry> mPrivateAlgorithmMap;
public DnsSecVerifier() {
mAlgorithmMap = new HashMap<Integer, AlgEntry>();
mPrivateAlgorithmMap = new HashMap<Name, AlgEntry>();
// set the default algorithm map.
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.RSAMD5), new AlgEntry(
"MD5withRSA", DNSSEC.Algorithm.RSAMD5, false));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.DSA), new AlgEntry("SHA1withDSA",
DNSSEC.Algorithm.DSA, true));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.RSASHA1), new AlgEntry(
"SHA1withRSA", DNSSEC.Algorithm.RSASHA1, false));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.DSA_NSEC3_SHA1), new AlgEntry(
"SHA1withDSA", DNSSEC.Algorithm.DSA, true));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.RSA_NSEC3_SHA1), new AlgEntry(
"SHA1withRSA", DNSSEC.Algorithm.RSASHA1, false));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.RSASHA256), new AlgEntry(
"SHA256withRSA", DNSSEC.Algorithm.RSASHA256, false));
mAlgorithmMap.put(Integer.valueOf(DNSSEC.Algorithm.RSASHA512), new AlgEntry(
"SHA512withRSA", DNSSEC.Algorithm.RSASHA512, false));
mAlgorithmMap.put(new Integer(DNSSEC.RSAMD5), new AlgEntry(
"MD5withRSA", DNSSEC.RSAMD5, false));
mAlgorithmMap.put(new Integer(DNSSEC.DSA), new AlgEntry("SHA1withDSA",
DNSSEC.DSA, true));
mAlgorithmMap.put(new Integer(DNSSEC.RSASHA1), new AlgEntry(
"SHA1withRSA", DNSSEC.RSASHA1, false));
mAlgorithmMap.put(new Integer(DNSSEC.DSA_NSEC3_SHA1), new AlgEntry(
"SHA1withDSA", DNSSEC.DSA, true));
mAlgorithmMap.put(new Integer(DNSSEC.RSA_NSEC3_SHA1), new AlgEntry(
"SHA1withRSA", DNSSEC.RSASHA1, false));
mAlgorithmMap.put(new Integer(DNSSEC.RSASHA256), new AlgEntry(
"SHA256withRSA", DNSSEC.RSASHA256, false));
mAlgorithmMap.put(new Integer(DNSSEC.RSASHA512), new AlgEntry(
"SHA512withRSA", DNSSEC.RSASHA512, false));
}
private boolean isDSA(int algorithm) {
// shortcut the standard algorithms
if (algorithm == DNSSEC.Algorithm.DSA) {
if (algorithm == DNSSEC.DSA) {
return true;
}
if (algorithm == DNSSEC.Algorithm.RSASHA1) {
if (algorithm == DNSSEC.RSASHA1) {
return false;
}
if (algorithm == DNSSEC.Algorithm.RSAMD5) {
if (algorithm == DNSSEC.RSAMD5) {
return false;
}
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(Integer.valueOf(algorithm));
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(new Integer(algorithm));
if (entry != null) {
return entry.isDSA;
@ -114,8 +107,8 @@ public class DnsSecVerifier {
"dns.algorithm.");
for (Util.ConfigEntry entry : aliases) {
Integer alg_alias = Integer.valueOf(Util.parseInt(entry.key, -1));
Integer alg_orig = Integer.valueOf(Util.parseInt(entry.value, -1));
Integer alg_alias = new Integer(Util.parseInt(entry.key, -1));
Integer alg_orig = new Integer(Util.parseInt(entry.value, -1));
if (!mAlgorithmMap.containsKey(alg_orig)) {
log.warn("Unable to alias " + alg_alias
@ -159,7 +152,7 @@ public class DnsSecVerifier {
* @return A List contains a one or more DNSKEYRecord objects, or null if a
* matching DNSKEY could not be found.
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
private List<DNSKEYRecord> findKey(RRset dnskey_rrset, RRSIGRecord signature) {
if (!signature.getSigner().equals(dnskey_rrset.getName())) {
log.trace("findKey: could not find appropriate key because "
@ -202,12 +195,12 @@ public class DnsSecVerifier {
* The rrset that the signature belongs to.
* @param sigrec
* The signature record to check.
* @return A value of SecurityStatus.SECURE if it looks OK, SecurityStatus.BOGUS if it looks
* @return A value of DNSSEC.Secure if it looks OK, DNSSEC.Faile if it looks
* bad.
*/
private byte checkSignature(RRset rrset, RRSIGRecord sigrec) {
if ((rrset == null) || (sigrec == null)) {
return SecurityStatus.BOGUS;
return DNSSEC.Failed;
}
if (!rrset.getName().equals(sigrec.getName())) {
@ -243,7 +236,7 @@ public class DnsSecVerifier {
}
public PublicKey parseDNSKEY(DNSKEYRecord key) {
AlgEntry ae = (AlgEntry) mAlgorithmMap.get(Integer.valueOf(key
AlgEntry ae = (AlgEntry) mAlgorithmMap.get(new Integer(key
.getAlgorithm()));
if (key.getAlgorithm() != ae.dnssecAlg) {
@ -368,7 +361,7 @@ public class DnsSecVerifier {
* @return SecurityStatus.SECURE if the rrest verified positively,
* SecurityStatus.BOGUS otherwise.
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
public byte verify(RRset rrset, RRset key_rrset) {
Iterator i = rrset.sigs();
@ -404,7 +397,7 @@ public class DnsSecVerifier {
* The DNSKEY to verify with.
* @return SecurityStatus.SECURE if the rrset verified, BOGUS otherwise.
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
public byte verify(RRset rrset, DNSKEYRecord dnskey) {
// Iterate over RRSIGS
Iterator i = rrset.sigs();
@ -436,24 +429,24 @@ public class DnsSecVerifier {
}
public boolean supportsAlgorithm(int algorithm) {
return mAlgorithmMap.containsKey(Integer.valueOf(algorithm));
return mAlgorithmMap.containsKey(new Integer(algorithm));
}
public boolean supportsAlgorithm(Name private_id) {
return mPrivateAlgorithmMap.containsKey(private_id);
return mAlgorithmMap.containsKey(private_id);
}
public int baseAlgorithm(int algorithm) {
switch (algorithm) {
case DNSSEC.Algorithm.RSAMD5:
case DNSSEC.Algorithm.RSASHA1:
case DNSSEC.RSAMD5:
case DNSSEC.RSASHA1:
return RSA;
case DNSSEC.Algorithm.DSA:
case DNSSEC.DSA:
return DSA;
}
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(Integer.valueOf(algorithm));
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(new Integer(algorithm));
if (entry == null) {
return UNKNOWN;
@ -472,7 +465,7 @@ public class DnsSecVerifier {
try {
AlgEntry entry = (AlgEntry) mAlgorithmMap
.get(Integer.valueOf(algorithm));
.get(new Integer(algorithm));
if (entry == null) {
log.info("DNSSEC algorithm " + algorithm + " not recognized.");

View File

@ -137,6 +137,17 @@ public class NSEC3ValUtils {
}
}
private static byte[] hash(Name name, NSEC3Record nsec3) {
try {
return nsec3.hashName(name);
} catch (NoSuchAlgorithmException e) {
st_log.warn("Did not recognize hash algorithm: "
+ nsec3.getHashAlgorithm());
return null;
}
}
/**
* Given the name of a closest encloser, return the name *.closest_encloser.
*
@ -447,7 +458,7 @@ public class NSEC3ValUtils {
return -1;
}
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
private static boolean validIterations(NSEC3Parameters nsec3params,
RRset dnskey_rrset, DnsSecVerifier verifier) {
// for now, we return the maximum iterations based simply on the key

View File

@ -31,7 +31,6 @@ import java.util.*;
* A version of the RRset class overrides the standard security status.
*/
public class SRRset extends RRset {
private static final long serialVersionUID = 1L;
private SecurityStatus mSecurityStatus;
/** Create a new, blank SRRset. */
@ -44,7 +43,7 @@ public class SRRset extends RRset {
* Create a new SRRset from an existing RRset. This SRRset will contain that
* same internal Record objects as the original RRset.
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
public SRRset(RRset r) {
this();

View File

@ -23,16 +23,12 @@
package com.verisign.tat.dnssec;
import java.io.Serializable;
/**
* Codes for DNSSEC security statuses.
*
* @author davidb
*/
public class SecurityStatus implements Serializable {
private static final long serialVersionUID = 1L;
public class SecurityStatus {
public static final byte INVALID = -1;
/**

View File

@ -35,7 +35,6 @@ import org.xbill.DNS.utils.base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.security.SignatureException;
import java.security.interfaces.DSAParams;
@ -179,7 +178,7 @@ public class SignUtils {
* @return the canonical wire line format of the rrset. This is the second
* part of data to be signed.
*/
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
public static byte[] generateCanonicalRRsetData(RRset rrset, long ttl,
int labels) {
DNSOutput image = new DNSOutput();
@ -457,8 +456,7 @@ public class SignUtils {
* useful for comparing RDATA portions of DNS records in doing DNSSEC
* canonical ordering.
*/
public static class ByteArrayComparator implements Comparator<byte[]>, Serializable {
private static final long serialVersionUID = 1L;
public static class ByteArrayComparator implements Comparator<byte[]> {
private int mOffset = 0;
private boolean mDebug = false;

View File

@ -86,10 +86,6 @@ public class TrustAnchorStore {
public List<String> listTrustAnchors() {
List<String> res = new ArrayList<String>();
if (mMap == null) {
return res;
}
for (Map.Entry<String, SRRset> entry : mMap.entrySet()) {
for (Iterator<Record> i = entry.getValue().rrs(); i.hasNext();) {
DNSKEYRecord r = (DNSKEYRecord) i.next();

View File

@ -339,7 +339,7 @@ public class ValUtils {
return false;
}
@SuppressWarnings("rawtypes")
@SuppressWarnings("unchecked")
public static RRSIGRecord rrsetFirstSig(RRset rrset) {
for (Iterator i = rrset.sigs(); i.hasNext();) {
return (RRSIGRecord) i.next();