more progress -- still not compiling
This commit is contained in:
parent
4273d3cfb8
commit
ac3dbc68e0
@ -28,13 +28,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package se.rfc.unbound.validator;
|
package se.rfc.unbound;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
import org.xbill.DNS.*;
|
import org.xbill.DNS.*;
|
||||||
import org.xbill.DNS.security.*;
|
import org.xbill.DNS.security.*;
|
||||||
|
|
||||||
@ -43,11 +42,8 @@ import se.rfc.unbound.Util;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A class for performing basic DNSSEC verification. The DNSJAVA package
|
* A class for performing basic DNSSEC verification. The DNSJAVA package
|
||||||
* contains a similar class. This is a reimplementation that allows us to have
|
* contains a similar class. This is a re-implementation that allows us to have
|
||||||
* finer control over the validation process.
|
* finer control over the validation process.
|
||||||
*
|
|
||||||
* @author davidb
|
|
||||||
* @version $Revision$
|
|
||||||
*/
|
*/
|
||||||
public class DnsSecVerifier
|
public class DnsSecVerifier
|
||||||
{
|
{
|
||||||
@ -61,8 +57,6 @@ public class DnsSecVerifier
|
|||||||
*/
|
*/
|
||||||
private HashMap mAlgorithmMap;
|
private HashMap mAlgorithmMap;
|
||||||
|
|
||||||
private Logger log = Logger.getLogger(this.getClass());
|
|
||||||
|
|
||||||
private static class AlgEntry
|
private static class AlgEntry
|
||||||
{
|
{
|
||||||
public String jcaName;
|
public String jcaName;
|
||||||
@ -121,15 +115,15 @@ public class DnsSecVerifier
|
|||||||
|
|
||||||
if (!mAlgorithmMap.containsKey(alg_orig))
|
if (!mAlgorithmMap.containsKey(alg_orig))
|
||||||
{
|
{
|
||||||
log.warn("Unable to alias " + alg_alias + " to unknown algorithm "
|
// log.warn("Unable to alias " + alg_alias + " to unknown algorithm "
|
||||||
+ alg_orig);
|
// + alg_orig);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAlgorithmMap.containsKey(alg_alias))
|
if (mAlgorithmMap.containsKey(alg_alias))
|
||||||
{
|
{
|
||||||
log.warn("Algorithm alias " + alg_alias
|
// log.warn("Algorithm alias " + alg_alias
|
||||||
+ " is already defined and cannot be redefined");
|
// + " is already defined and cannot be redefined");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,11 +135,11 @@ public class DnsSecVerifier
|
|||||||
{
|
{
|
||||||
Integer alg = (Integer) i.next();
|
Integer alg = (Integer) i.next();
|
||||||
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(alg);
|
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(alg);
|
||||||
if (entry == null)
|
// if (entry == null)
|
||||||
log.warn("DNSSEC alg " + alg + " has a null entry!");
|
// log.warn("DNSSEC alg " + alg + " has a null entry!");
|
||||||
else
|
// else
|
||||||
log.debug("DNSSEC alg " + alg + " maps to " + entry.jcaName
|
// log.debug("DNSSEC alg " + alg + " maps to " + entry.jcaName
|
||||||
+ " (" + entry.dnssecAlg + ")");
|
// + " (" + entry.dnssecAlg + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +157,9 @@ public class DnsSecVerifier
|
|||||||
{
|
{
|
||||||
if (!signature.getSigner().equals(dnskey_rrset.getName()))
|
if (!signature.getSigner().equals(dnskey_rrset.getName()))
|
||||||
{
|
{
|
||||||
log.trace("findKey: could not find appropriate key because "
|
// log.trace("findKey: could not find appropriate key because "
|
||||||
+ "incorrect keyset was supplied. Wanted: " + signature.getSigner()
|
// + "incorrect keyset was supplied. Wanted: " + signature.getSigner()
|
||||||
+ ", got: " + dnskey_rrset.getName());
|
// + ", got: " + dnskey_rrset.getName());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +179,8 @@ public class DnsSecVerifier
|
|||||||
|
|
||||||
if (res.size() == 0)
|
if (res.size() == 0)
|
||||||
{
|
{
|
||||||
log.trace("findKey: could not find a key matching "
|
// log.trace("findKey: could not find a key matching "
|
||||||
+ "the algorithm and footprint in supplied keyset. ");
|
// + "the algorithm and footprint in supplied keyset. ");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@ -206,12 +200,12 @@ public class DnsSecVerifier
|
|||||||
if (rrset == null || sigrec == null) return DNSSEC.Failed;
|
if (rrset == null || sigrec == null) return DNSSEC.Failed;
|
||||||
if (!rrset.getName().equals(sigrec.getName()))
|
if (!rrset.getName().equals(sigrec.getName()))
|
||||||
{
|
{
|
||||||
log.debug("Signature name does not match RRset name");
|
// log.debug("Signature name does not match RRset name");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
if (rrset.getType() != sigrec.getTypeCovered())
|
if (rrset.getType() != sigrec.getTypeCovered())
|
||||||
{
|
{
|
||||||
log.debug("Signature type does not match RRset type");
|
// log.debug("Signature type does not match RRset type");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,14 +214,14 @@ public class DnsSecVerifier
|
|||||||
Date expire = sigrec.getExpire();
|
Date expire = sigrec.getExpire();
|
||||||
if (now.before(start))
|
if (now.before(start))
|
||||||
{
|
{
|
||||||
log.debug("Signature is not yet valid");
|
// log.debug("Signature is not yet valid");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (now.after(expire))
|
if (now.after(expire))
|
||||||
{
|
{
|
||||||
log.debug("Signature has expired (now = " + now + ", sig expires = "
|
// log.debug("Signature has expired (now = " + now + ", sig expires = "
|
||||||
+ expire);
|
// + expire);
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,8 +265,8 @@ public class DnsSecVerifier
|
|||||||
|
|
||||||
if (pk == null)
|
if (pk == null)
|
||||||
{
|
{
|
||||||
log.warn("Could not convert DNSKEY record to a JCA public key: "
|
// log.warn("Could not convert DNSKEY record to a JCA public key: "
|
||||||
+ key);
|
// + key);
|
||||||
return SecurityStatus.UNCHECKED;
|
return SecurityStatus.UNCHECKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,20 +288,20 @@ public class DnsSecVerifier
|
|||||||
}
|
}
|
||||||
if (!signer.verify(sig))
|
if (!signer.verify(sig))
|
||||||
{
|
{
|
||||||
log.info("Signature failed to verify cryptographically");
|
// log.info("Signature failed to verify cryptographically");
|
||||||
log.debug("Failed signature: " + sigrec);
|
// log.debug("Failed signature: " + sigrec);
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
log.trace("Signature verified: " + sigrec);
|
// log.trace("Signature verified: " + sigrec);
|
||||||
return SecurityStatus.SECURE;
|
return SecurityStatus.SECURE;
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
log.error("I/O error", e);
|
// log.error("I/O error", e);
|
||||||
}
|
}
|
||||||
catch (GeneralSecurityException e)
|
catch (GeneralSecurityException e)
|
||||||
{
|
{
|
||||||
log.error("Security error", e);
|
// log.error("Security error", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Since I'm not sure what would cause an exception here (failure
|
// FIXME: Since I'm not sure what would cause an exception here (failure
|
||||||
@ -334,7 +328,7 @@ public class DnsSecVerifier
|
|||||||
|
|
||||||
if (keys == null)
|
if (keys == null)
|
||||||
{
|
{
|
||||||
log.trace("could not find appropriate key");
|
// log.trace("could not find appropriate key");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,7 +359,7 @@ public class DnsSecVerifier
|
|||||||
|
|
||||||
if (!i.hasNext())
|
if (!i.hasNext())
|
||||||
{
|
{
|
||||||
log.info("RRset failed to verify due to lack of signatures");
|
// log.info("RRset failed to verify due to lack of signatures");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,7 +372,7 @@ public class DnsSecVerifier
|
|||||||
if (res == SecurityStatus.SECURE) return res;
|
if (res == SecurityStatus.SECURE) return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("RRset failed to verify: all signatures were BOGUS");
|
// log.info("RRset failed to verify: all signatures were BOGUS");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,7 +392,7 @@ public class DnsSecVerifier
|
|||||||
Iterator i = rrset.sigs();
|
Iterator i = rrset.sigs();
|
||||||
if (!i.hasNext())
|
if (!i.hasNext())
|
||||||
{
|
{
|
||||||
log.info("RRset failed to verify due to lack of signatures");
|
// log.info("RRset failed to verify due to lack of signatures");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +408,7 @@ public class DnsSecVerifier
|
|||||||
if (res == SecurityStatus.SECURE) return res;
|
if (res == SecurityStatus.SECURE) return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("RRset failed to verify: all signatures were BOGUS");
|
// log.info("RRset failed to verify: all signatures were BOGUS");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,7 +449,7 @@ public class DnsSecVerifier
|
|||||||
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(new Integer(algorithm));
|
AlgEntry entry = (AlgEntry) mAlgorithmMap.get(new Integer(algorithm));
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
{
|
{
|
||||||
log.info("DNSSEC algorithm " + algorithm + " not recognized.");
|
// log.info("DNSSEC algorithm " + algorithm + " not recognized.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// TODO: should we cache the instance?
|
// TODO: should we cache the instance?
|
||||||
@ -463,7 +457,7 @@ public class DnsSecVerifier
|
|||||||
}
|
}
|
||||||
catch (NoSuchAlgorithmException e)
|
catch (NoSuchAlgorithmException e)
|
||||||
{
|
{
|
||||||
log.error("error getting Signature object", e);
|
// log.error("error getting Signature object", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
@ -32,13 +32,11 @@ package se.rfc.unbound;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
import org.xbill.DNS.*;
|
import org.xbill.DNS.*;
|
||||||
import org.xbill.DNS.utils.base32;
|
import org.xbill.DNS.utils.base32;
|
||||||
|
|
||||||
import se.rfc.unbound.validator.DnsSecVerifier;
|
import se.rfc.unbound.SignUtils.ByteArrayComparator;
|
||||||
import se.rfc.unbound.validator.SignUtils;
|
|
||||||
import se.rfc.unbound.validator.SignUtils.ByteArrayComparator;
|
|
||||||
|
|
||||||
public class NSEC3ValUtils
|
public class NSEC3ValUtils
|
||||||
{
|
{
|
||||||
@ -49,8 +47,6 @@ public class NSEC3ValUtils
|
|||||||
// parameters. The idea is to hash and compare for each group independently,
|
// parameters. The idea is to hash and compare for each group independently,
|
||||||
// instead of having to skip NSEC3 RRs with the wrong parameters.
|
// instead of having to skip NSEC3 RRs with the wrong parameters.
|
||||||
|
|
||||||
// The logger to use in static methods.
|
|
||||||
private static Logger st_log = Logger.getLogger(NSEC3ValUtils.class);
|
|
||||||
|
|
||||||
private static Name asterisk_label = Name.fromConstantString("*");
|
private static Name asterisk_label = Name.fromConstantString("*");
|
||||||
|
|
||||||
@ -204,7 +200,7 @@ public class NSEC3ValUtils
|
|||||||
}
|
}
|
||||||
catch (NoSuchAlgorithmException e)
|
catch (NoSuchAlgorithmException e)
|
||||||
{
|
{
|
||||||
st_log.debug("Did not recognize hash algorithm: " + params.alg);
|
// st_log.debug("Did not recognize hash algorithm: " + params.alg);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -391,8 +387,8 @@ public class NSEC3ValUtils
|
|||||||
|
|
||||||
if (candidate == null)
|
if (candidate == null)
|
||||||
{
|
{
|
||||||
st_log.debug("proveClosestEncloser: could not find a "
|
// st_log.debug("proveClosestEncloser: could not find a "
|
||||||
+ "candidate for the closest encloser.");
|
// + "candidate for the closest encloser.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -400,7 +396,7 @@ public class NSEC3ValUtils
|
|||||||
{
|
{
|
||||||
if (proveDoesNotExist)
|
if (proveDoesNotExist)
|
||||||
{
|
{
|
||||||
st_log.debug("proveClosestEncloser: proved that qname existed!");
|
// st_log.debug("proveClosestEncloser: proved that qname existed!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// otherwise, we need to nothing else to prove that qname is its own
|
// otherwise, we need to nothing else to prove that qname is its own
|
||||||
@ -414,13 +410,13 @@ public class NSEC3ValUtils
|
|||||||
if (candidate.ce_nsec3.hasType(Type.NS)
|
if (candidate.ce_nsec3.hasType(Type.NS)
|
||||||
&& !candidate.ce_nsec3.hasType(Type.SOA))
|
&& !candidate.ce_nsec3.hasType(Type.SOA))
|
||||||
{
|
{
|
||||||
st_log.debug("proveClosestEncloser: closest encloser "
|
// st_log.debug("proveClosestEncloser: closest encloser "
|
||||||
+ "was a delegation!");
|
// + "was a delegation!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (candidate.ce_nsec3.hasType(Type.DNAME))
|
if (candidate.ce_nsec3.hasType(Type.DNAME))
|
||||||
{
|
{
|
||||||
st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
|
// st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,8 +431,8 @@ public class NSEC3ValUtils
|
|||||||
bac);
|
bac);
|
||||||
if (candidate.nc_nsec3 == null)
|
if (candidate.nc_nsec3 == null)
|
||||||
{
|
{
|
||||||
st_log.debug("Could not find proof that the "
|
// st_log.debug("Could not find proof that the "
|
||||||
+ "closest encloser was the closest encloser");
|
// + "closest encloser was the closest encloser");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,8 +520,8 @@ public class NSEC3ValUtils
|
|||||||
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
||||||
if (nsec3params == null)
|
if (nsec3params == null)
|
||||||
{
|
{
|
||||||
st_log.debug("Could not find a single set of " +
|
// st_log.debug("Could not find a single set of " +
|
||||||
"NSEC3 parameters (multiple parameters present).");
|
// "NSEC3 parameters (multiple parameters present).");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +538,7 @@ public class NSEC3ValUtils
|
|||||||
|
|
||||||
if (ce == null)
|
if (ce == null)
|
||||||
{
|
{
|
||||||
st_log.debug("proveNameError: failed to prove a closest encloser.");
|
// st_log.debug("proveNameError: failed to prove a closest encloser.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,8 +553,8 @@ public class NSEC3ValUtils
|
|||||||
bac);
|
bac);
|
||||||
if (nsec3 == null)
|
if (nsec3 == null)
|
||||||
{
|
{
|
||||||
st_log.debug("proveNameError: could not prove that the "
|
// st_log.debug("proveNameError: could not prove that the "
|
||||||
+ "applicable wildcard did not exist.");
|
// + "applicable wildcard did not exist.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,8 +649,8 @@ public class NSEC3ValUtils
|
|||||||
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
||||||
if (nsec3params == null)
|
if (nsec3params == null)
|
||||||
{
|
{
|
||||||
st_log.debug("could not find a single set of "
|
// st_log.debug("could not find a single set of "
|
||||||
+ "NSEC3 parameters (multiple parameters present)");
|
// + "NSEC3 parameters (multiple parameters present)");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ByteArrayComparator bac = new ByteArrayComparator();
|
ByteArrayComparator bac = new ByteArrayComparator();
|
||||||
@ -669,13 +665,13 @@ public class NSEC3ValUtils
|
|||||||
{
|
{
|
||||||
if (nsec3.hasType(qtype))
|
if (nsec3.hasType(qtype))
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
|
// st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (nsec3.hasType(Type.CNAME))
|
if (nsec3.hasType(Type.CNAME))
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: Matching NSEC3 proved "
|
// st_log.debug("proveNodata: Matching NSEC3 proved "
|
||||||
+ "that a CNAME existed!");
|
// + "that a CNAME existed!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -695,8 +691,8 @@ public class NSEC3ValUtils
|
|||||||
// problem.
|
// problem.
|
||||||
if (ce == null)
|
if (ce == null)
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: did not match qname, "
|
// st_log.debug("proveNodata: did not match qname, "
|
||||||
+ "nor found a proven closest encloser.");
|
// + "nor found a proven closest encloser.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -714,7 +710,7 @@ public class NSEC3ValUtils
|
|||||||
{
|
{
|
||||||
if (nsec3.hasType(qtype))
|
if (nsec3.hasType(qtype))
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: matching wildcard had qtype!");
|
// st_log.debug("proveNodata: matching wildcard had qtype!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -723,16 +719,16 @@ public class NSEC3ValUtils
|
|||||||
// Case 5.
|
// Case 5.
|
||||||
if (qtype != Type.DS)
|
if (qtype != Type.DS)
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: could not find matching NSEC3, "
|
// st_log.debug("proveNodata: could not find matching NSEC3, "
|
||||||
+ "nor matching wildcard, and qtype is not DS -- no more options.");
|
// + "nor matching wildcard, and qtype is not DS -- no more options.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to make sure that the covering NSEC3 is opt-in.
|
// We need to make sure that the covering NSEC3 is opt-in.
|
||||||
if (!ce.nc_nsec3.getOptInFlag())
|
if (!ce.nc_nsec3.getOptInFlag())
|
||||||
{
|
{
|
||||||
st_log.debug("proveNodata: covering NSEC3 was not "
|
// st_log.debug("proveNodata: covering NSEC3 was not "
|
||||||
+ "opt-in in an opt-in DS NOERROR/NODATA case.");
|
// + "opt-in in an opt-in DS NOERROR/NODATA case.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,7 +754,7 @@ public class NSEC3ValUtils
|
|||||||
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
||||||
if (nsec3params == null)
|
if (nsec3params == null)
|
||||||
{
|
{
|
||||||
st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
|
// st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -779,10 +775,10 @@ public class NSEC3ValUtils
|
|||||||
|
|
||||||
if (candidate.nc_nsec3 == null)
|
if (candidate.nc_nsec3 == null)
|
||||||
{
|
{
|
||||||
st_log.debug("proveWildcard: did not find a covering NSEC3 "
|
// st_log.debug("proveWildcard: did not find a covering NSEC3 "
|
||||||
+ "that covered the next closer name to " + qname + " from "
|
// + "that covered the next closer name to " + qname + " from "
|
||||||
+ candidate.closestEncloser + " (derived from wildcard " + wildcard
|
// + candidate.closestEncloser + " (derived from wildcard " + wildcard
|
||||||
+ ")");
|
// + ")");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -812,8 +808,8 @@ public class NSEC3ValUtils
|
|||||||
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
|
||||||
if (nsec3params == null)
|
if (nsec3params == null)
|
||||||
{
|
{
|
||||||
st_log.debug("couldn't find a single set of " +
|
// st_log.debug("couldn't find a single set of " +
|
||||||
"NSEC3 parameters (multiple parameters present).");
|
// "NSEC3 parameters (multiple parameters present).");
|
||||||
return SecurityStatus.BOGUS;
|
return SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
ByteArrayComparator bac = new ByteArrayComparator();
|
ByteArrayComparator bac = new ByteArrayComparator();
|
||||||
|
@ -271,7 +271,13 @@ public class SMessage
|
|||||||
h.setRcode(mHeader.getRcode());
|
h.setRcode(mHeader.getRcode());
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
{
|
{
|
||||||
if (Flags.isFlag(i)) h.setFlag(i, mHeader.getFlag(i));
|
if (Flags.isFlag(i)) {
|
||||||
|
if (mHeader.getFlag(i)) {
|
||||||
|
h.setFlag(i);
|
||||||
|
} else {
|
||||||
|
h.unsetFlag(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all the records. -- this will set the counts correctly in the
|
// Add all the records. -- this will set the counts correctly in the
|
||||||
|
@ -73,23 +73,23 @@ public SRRset(RRset r)
|
|||||||
* contained RRsig records as well.
|
* contained RRsig records as well.
|
||||||
* @return The cloned SRRset.
|
* @return The cloned SRRset.
|
||||||
*/
|
*/
|
||||||
public SRRset cloneSRRset(long withNewTTL)
|
// public SRRset cloneSRRset(long withNewTTL)
|
||||||
{
|
// {
|
||||||
SRRset nr = new SRRset();
|
// SRRset nr = new SRRset();
|
||||||
|
//
|
||||||
for (Iterator i = rrs(); i.hasNext();)
|
// for (Iterator i = rrs(); i.hasNext();)
|
||||||
{
|
// {
|
||||||
nr.addRR(((Record) i.next()).withTTL(withNewTTL));
|
// nr.addRR(((Record) i.next()).withTTL(withNewTTL));
|
||||||
}
|
// }
|
||||||
for (Iterator i = sigs(); i.hasNext();)
|
// for (Iterator i = sigs(); i.hasNext();)
|
||||||
{
|
// {
|
||||||
nr.addRR(((Record) i.next()).withTTL(withNewTTL));
|
// nr.addRR(((Record) i.next()).withTTL(withNewTTL));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
nr.mSecurityStatus = mSecurityStatus;
|
// nr.mSecurityStatus = mSecurityStatus;
|
||||||
|
//
|
||||||
return nr;
|
// return nr;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public SRRset cloneSRRsetNoSigs()
|
public SRRset cloneSRRsetNoSigs()
|
||||||
{
|
{
|
||||||
@ -103,6 +103,8 @@ public SRRset(RRset r)
|
|||||||
|
|
||||||
return nr;
|
return nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current security status (generally: UNCHECKED, BOGUS, or
|
* Return the current security status (generally: UNCHECKED, BOGUS, or
|
||||||
* SECURE).
|
* SECURE).
|
||||||
@ -125,11 +127,19 @@ public SRRset(RRset r)
|
|||||||
* Set the current security status for this SRRset. This status will be
|
* Set the current security status for this SRRset. This status will be
|
||||||
* shared amongst all copies of this SRRset (created with cloneSRRset())
|
* shared amongst all copies of this SRRset (created with cloneSRRset())
|
||||||
*/
|
*/
|
||||||
public void setSecurityStatus(int status)
|
public void setSecurityStatus(byte status)
|
||||||
{
|
{
|
||||||
mSecurityStatus.setStatus(status);
|
mSecurityStatus.setStatus(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int totalSize() {
|
||||||
|
int num_sigs = 0;
|
||||||
|
for (Iterator i = sigs(); i.hasNext(); ) {
|
||||||
|
num_sigs++;
|
||||||
|
}
|
||||||
|
return size() + num_sigs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The total number of records (data + sigs) in the SRRset.
|
* @return The total number of records (data + sigs) in the SRRset.
|
||||||
*/
|
*/
|
||||||
@ -138,6 +148,12 @@ public SRRset(RRset r)
|
|||||||
return totalSize();
|
return totalSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RRSIGRecord firstSig() {
|
||||||
|
for (Iterator i = sigs(); i.hasNext(); ) {
|
||||||
|
return (RRSIGRecord) i.next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @return true if this RRset has RRSIG records that cover data records.
|
* @return true if this RRset has RRSIG records that cover data records.
|
||||||
* (i.e., RRSIG SRRsets return false)
|
* (i.e., RRSIG SRRsets return false)
|
||||||
@ -158,12 +174,12 @@ public SRRset(RRset r)
|
|||||||
return sig.getSigner();
|
return sig.getSigner();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTTL(long ttl)
|
// public void setTTL(long ttl)
|
||||||
{
|
// {
|
||||||
if (ttl < 0)
|
// if (ttl < 0)
|
||||||
{
|
// {
|
||||||
throw new IllegalArgumentException("ttl can't be less than zero, stupid! was " + ttl);
|
// throw new IllegalArgumentException("ttl can't be less than zero, stupid! was " + ttl);
|
||||||
}
|
// }
|
||||||
super.setTTL(ttl);
|
// super.setTTL(ttl);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
474
src/se/rfc/unbound/SignUtils.java
Normal file
474
src/se/rfc/unbound/SignUtils.java
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
/*
|
||||||
|
* $Id$
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005 VeriSign, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package se.rfc.unbound;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.SignatureException;
|
||||||
|
import java.security.interfaces.DSAParams;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import org.xbill.DNS.DNSKEYRecord;
|
||||||
|
import org.xbill.DNS.DNSOutput;
|
||||||
|
import org.xbill.DNS.Name;
|
||||||
|
import org.xbill.DNS.RRSIGRecord;
|
||||||
|
import org.xbill.DNS.RRset;
|
||||||
|
import org.xbill.DNS.Record;
|
||||||
|
import org.xbill.DNS.utils.base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class contains a bunch of utility methods that are generally useful in
|
||||||
|
* signing and verifying rrsets.
|
||||||
|
*
|
||||||
|
* @author David Blacka (original)
|
||||||
|
* @author $Author$
|
||||||
|
* @version $Revision$
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class SignUtils
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class implements a basic comparitor for byte arrays. It is primarily
|
||||||
|
* useful for comparing RDATA portions of DNS records in doing DNSSEC
|
||||||
|
* canonical ordering.
|
||||||
|
*
|
||||||
|
* @author David Blacka (original)
|
||||||
|
*/
|
||||||
|
public static class ByteArrayComparator implements Comparator
|
||||||
|
{
|
||||||
|
private int mOffset = 0;
|
||||||
|
private boolean mDebug = false;
|
||||||
|
|
||||||
|
public ByteArrayComparator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayComparator(int offset, boolean debug)
|
||||||
|
{
|
||||||
|
mOffset = offset;
|
||||||
|
mDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(Object o1, Object o2) throws ClassCastException
|
||||||
|
{
|
||||||
|
byte[] b1 = (byte[]) o1;
|
||||||
|
byte[] b2 = (byte[]) o2;
|
||||||
|
|
||||||
|
for (int i = mOffset; i < b1.length && i < b2.length; i++)
|
||||||
|
{
|
||||||
|
if (b1[i] != b2[i])
|
||||||
|
{
|
||||||
|
if (mDebug)
|
||||||
|
{
|
||||||
|
System.out.println("offset " + i + " differs (this is "
|
||||||
|
+ (i - mOffset) + " bytes in from our offset.)");
|
||||||
|
}
|
||||||
|
return (b1[i] & 0xFF) - (b2[i] & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b1.length - b2.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private static final int DSA_SIGNATURE_LENGTH = 20;
|
||||||
|
private static final int ASN1_INT = 0x02;
|
||||||
|
private static final int ASN1_SEQ = 0x30;
|
||||||
|
|
||||||
|
public static final int RR_NORMAL = 0;
|
||||||
|
public static final int RR_DELEGATION = 1;
|
||||||
|
public static final int RR_GLUE = 2;
|
||||||
|
public static final int RR_INVALID = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate from some basic information a prototype SIG RR containing
|
||||||
|
* everything but the actual signature itself.
|
||||||
|
*
|
||||||
|
* @param rrset the RRset being signed.
|
||||||
|
* @param signer the name of the signing key
|
||||||
|
* @param alg the algorithm of the signing key
|
||||||
|
* @param keyid the keyid (or footprint) of the signing key
|
||||||
|
* @param start the SIG inception time.
|
||||||
|
* @param expire the SIG expiration time.
|
||||||
|
* @param sig_ttl the TTL of the resulting SIG record.
|
||||||
|
* @return a prototype signature based on the RRset and key information.
|
||||||
|
*/
|
||||||
|
public static RRSIGRecord generatePreRRSIG(RRset rrset, Name signer,
|
||||||
|
int alg, int keyid, Date start, Date expire, long sig_ttl)
|
||||||
|
{
|
||||||
|
return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
|
||||||
|
.getType(), alg, rrset.getTTL(), expire, start, keyid, signer, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate from some basic information a prototype SIG RR containing
|
||||||
|
* everything but the actual signature itself.
|
||||||
|
*
|
||||||
|
* @param rrset the RRset being signed.
|
||||||
|
* @param key the public KEY RR counterpart to the key being used to sign
|
||||||
|
* the RRset
|
||||||
|
* @param start the SIG inception time.
|
||||||
|
* @param expire the SIG expiration time.
|
||||||
|
* @param sig_ttl the TTL of the resulting SIG record.
|
||||||
|
* @return a prototype signature based on the RRset and key information.
|
||||||
|
*/
|
||||||
|
public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
|
||||||
|
Date start, Date expire, long sig_ttl)
|
||||||
|
{
|
||||||
|
return generatePreRRSIG(rrset, key.getName(), key.getAlgorithm(), key
|
||||||
|
.getFootprint(), start, expire, sig_ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate from some basic information a prototype SIG RR containing
|
||||||
|
* everything but the actual signature itself.
|
||||||
|
*
|
||||||
|
* @param rec the DNS record being signed (forming an entire RRset).
|
||||||
|
* @param key the public KEY RR counterpart to the key signing the record.
|
||||||
|
* @param start the SIG inception time.
|
||||||
|
* @param expire the SIG expiration time.
|
||||||
|
* @param sig_ttl the TTL of the result SIG record.
|
||||||
|
* @return a prototype signature based on the Record and key information.
|
||||||
|
*/
|
||||||
|
public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
|
||||||
|
Date start, Date expire, long sig_ttl)
|
||||||
|
{
|
||||||
|
return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
|
||||||
|
.getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
|
||||||
|
.getFootprint(), key.getName(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the binary image of the prototype SIG RR.
|
||||||
|
*
|
||||||
|
* @param presig the SIG RR prototype.
|
||||||
|
* @return the RDATA portion of the prototype SIG record. This forms the
|
||||||
|
* first part of the data to be signed.
|
||||||
|
*/
|
||||||
|
private static byte[] generatePreSigRdata(RRSIGRecord presig)
|
||||||
|
{
|
||||||
|
// Generate the binary image;
|
||||||
|
DNSOutput image = new DNSOutput();
|
||||||
|
|
||||||
|
// precalc some things
|
||||||
|
int start_time = (int) (presig.getTimeSigned().getTime() / 1000);
|
||||||
|
int expire_time = (int) (presig.getExpire().getTime() / 1000);
|
||||||
|
Name signer = presig.getSigner();
|
||||||
|
|
||||||
|
// first write out the partial SIG record (this is the SIG RDATA
|
||||||
|
// minus the actual signature.
|
||||||
|
image.writeU16(presig.getTypeCovered());
|
||||||
|
image.writeU8(presig.getAlgorithm());
|
||||||
|
image.writeU8(presig.getLabels());
|
||||||
|
image.writeU32((int) presig.getOrigTTL());
|
||||||
|
image.writeU32(expire_time);
|
||||||
|
image.writeU32(start_time);
|
||||||
|
image.writeU16(presig.getFootprint());
|
||||||
|
image.writeByteArray(signer.toWireCanonical());
|
||||||
|
|
||||||
|
return image.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the canonical wire line format of the RRset.
|
||||||
|
*
|
||||||
|
* @param rrset the RRset to convert.
|
||||||
|
* @param ttl the TTL to use when canonicalizing -- this is generally the
|
||||||
|
* TTL of the signature if there is a pre-existing signature. If
|
||||||
|
* not it is just the ttl of the rrset itself.
|
||||||
|
* @param labels the labels field of the signature, or 0.
|
||||||
|
* @return the canonical wire line format of the rrset. This is the second
|
||||||
|
* part of data to be signed.
|
||||||
|
*/
|
||||||
|
public static byte[] generateCanonicalRRsetData(RRset rrset, long ttl,
|
||||||
|
int labels)
|
||||||
|
{
|
||||||
|
DNSOutput image = new DNSOutput();
|
||||||
|
|
||||||
|
if (ttl == 0) ttl = rrset.getTTL();
|
||||||
|
Name n = rrset.getName();
|
||||||
|
if (labels == 0)
|
||||||
|
{
|
||||||
|
labels = n.labels();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// correct for Name()'s conception of label count.
|
||||||
|
labels++;
|
||||||
|
}
|
||||||
|
boolean wildcardName = false;
|
||||||
|
if (n.labels() != labels)
|
||||||
|
{
|
||||||
|
n = n.wild(n.labels() - labels);
|
||||||
|
wildcardName = true;
|
||||||
|
// log.trace("Detected wildcard expansion: " + rrset.getName() + " changed to " + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now convert load the wire format records in the RRset into a
|
||||||
|
// list of byte arrays.
|
||||||
|
ArrayList canonical_rrs = new ArrayList();
|
||||||
|
for (Iterator i = rrset.rrs(); i.hasNext();)
|
||||||
|
{
|
||||||
|
Record r = (Record) i.next();
|
||||||
|
if (r.getTTL() != ttl || wildcardName)
|
||||||
|
{
|
||||||
|
// If necessary, we need to create a new record with a new ttl or ownername.
|
||||||
|
// In the TTL case, this avoids changing the ttl in the response.
|
||||||
|
r = Record.newRecord(n, r.getType(), r.getDClass(), ttl, r
|
||||||
|
.rdataToWireCanonical());
|
||||||
|
}
|
||||||
|
byte[] wire_fmt = r.toWireCanonical();
|
||||||
|
canonical_rrs.add(wire_fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the records into the correct ordering.
|
||||||
|
// Caculate the offset where the RDATA begins (we have to skip
|
||||||
|
// past the length byte)
|
||||||
|
|
||||||
|
int offset = rrset.getName().toWireCanonical().length + 10;
|
||||||
|
ByteArrayComparator bac = new ByteArrayComparator(offset, false);
|
||||||
|
|
||||||
|
Collections.sort(canonical_rrs, bac);
|
||||||
|
|
||||||
|
for (Iterator i = canonical_rrs.iterator(); i.hasNext();)
|
||||||
|
{
|
||||||
|
byte[] wire_fmt_rec = (byte[]) i.next();
|
||||||
|
image.writeByteArray(wire_fmt_rec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return image.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an RRset and the prototype signature, generate the canonical data
|
||||||
|
* that is to be signed.
|
||||||
|
*
|
||||||
|
* @param rrset the RRset to be signed.
|
||||||
|
* @param presig a prototype SIG RR created using the same RRset.
|
||||||
|
* @return a block of data ready to be signed.
|
||||||
|
*/
|
||||||
|
public static byte[] generateSigData(RRset rrset, RRSIGRecord presig)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] rrset_data = generateCanonicalRRsetData(rrset, presig.getOrigTTL(), presig.getLabels());
|
||||||
|
|
||||||
|
return generateSigData(rrset_data, presig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an RRset and the prototype signature, generate the canonical data
|
||||||
|
* that is to be signed.
|
||||||
|
*
|
||||||
|
* @param rrset_data the RRset converted into canonical wire line format (as
|
||||||
|
* per the canonicalization rules in RFC 2535).
|
||||||
|
* @param presig the prototype signature based on the same RRset represented
|
||||||
|
* in <code>rrset_data</code>.
|
||||||
|
* @return a block of data ready to be signed.
|
||||||
|
*/
|
||||||
|
public static byte[] generateSigData(byte[] rrset_data, RRSIGRecord presig)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
byte[] sig_rdata = generatePreSigRdata(presig);
|
||||||
|
|
||||||
|
ByteArrayOutputStream image = new ByteArrayOutputStream(sig_rdata.length
|
||||||
|
+ rrset_data.length);
|
||||||
|
|
||||||
|
image.write(sig_rdata);
|
||||||
|
image.write(rrset_data);
|
||||||
|
|
||||||
|
return image.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given the acutal signature an the prototype signature, combine them and
|
||||||
|
* return the fully formed SIGRecord.
|
||||||
|
*
|
||||||
|
* @param signature the cryptographic signature, in DNSSEC format.
|
||||||
|
* @param presig the prototype SIG RR to add the signature to.
|
||||||
|
* @return the fully formed SIG RR.
|
||||||
|
*/
|
||||||
|
public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
|
||||||
|
{
|
||||||
|
return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
|
||||||
|
.getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
|
||||||
|
.getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
|
||||||
|
.getFootprint(), presig.getSigner(), signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from a RFC 2536 formatted DSA signature to a JCE (ASN.1)
|
||||||
|
* formatted signature.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* ASN.1 format = ASN1_SEQ . seq_length . ASN1_INT . Rlength . R . ANS1_INT .
|
||||||
|
* Slength . S
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* The integers R and S may have a leading null byte to force the integer
|
||||||
|
* positive.
|
||||||
|
*
|
||||||
|
* @param signature the RFC 2536 formatted DSA signature.
|
||||||
|
* @return The ASN.1 formatted DSA signature.
|
||||||
|
* @throws SignatureException if there was something wrong with the RFC 2536
|
||||||
|
* formatted signature.
|
||||||
|
*/
|
||||||
|
public static byte[] convertDSASignature(byte[] signature)
|
||||||
|
throws SignatureException
|
||||||
|
{
|
||||||
|
if (signature.length != 41)
|
||||||
|
throw new SignatureException("RFC 2536 signature not expected length.");
|
||||||
|
|
||||||
|
byte r_pad = 0;
|
||||||
|
byte s_pad = 0;
|
||||||
|
|
||||||
|
// handle initial null byte padding.
|
||||||
|
if (signature[1] < 0) r_pad++;
|
||||||
|
if (signature[21] < 0) s_pad++;
|
||||||
|
|
||||||
|
// ASN.1 length = R length + S length + (2 + 2 + 2), where each 2
|
||||||
|
// is for a ASN.1 type-length byte pair of which there are three
|
||||||
|
// (SEQ, INT, INT).
|
||||||
|
byte sig_length = (byte) (40 + r_pad + s_pad + 6);
|
||||||
|
|
||||||
|
byte sig[] = new byte[sig_length];
|
||||||
|
byte pos = 0;
|
||||||
|
|
||||||
|
sig[pos++] = ASN1_SEQ;
|
||||||
|
sig[pos++] = (byte) (sig_length - 2); // all but the SEQ type+length.
|
||||||
|
sig[pos++] = ASN1_INT;
|
||||||
|
sig[pos++] = (byte) (20 + r_pad);
|
||||||
|
|
||||||
|
// copy the value of R, leaving a null byte if necessary
|
||||||
|
if (r_pad == 1) sig[pos++] = 0;
|
||||||
|
|
||||||
|
System.arraycopy(signature, 1, sig, pos, 20);
|
||||||
|
pos += 20;
|
||||||
|
|
||||||
|
sig[pos++] = ASN1_INT;
|
||||||
|
sig[pos++] = (byte) (20 + s_pad);
|
||||||
|
|
||||||
|
// copy the value of S, leaving a null byte if necessary
|
||||||
|
if (s_pad == 1) sig[pos++] = 0;
|
||||||
|
|
||||||
|
System.arraycopy(signature, 21, sig, pos, 20);
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts from a JCE (ASN.1) formatted DSA signature to a RFC 2536
|
||||||
|
* compliant signature.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* rfc2536 format = T . R . S
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* where T is a number between 0 and 8, which is based on the DSA key
|
||||||
|
* length, and R & S are formatted to be exactly 20 bytes each (no leading
|
||||||
|
* null bytes).
|
||||||
|
*
|
||||||
|
* @param params the DSA parameters associated with the DSA key used to
|
||||||
|
* generate the signature.
|
||||||
|
* @param signature the ASN.1 formatted DSA signature.
|
||||||
|
* @return a RFC 2536 formatted DSA signature.
|
||||||
|
* @throws SignatureException if something is wrong with the ASN.1 format.
|
||||||
|
*/
|
||||||
|
public static byte[] convertDSASignature(DSAParams params, byte[] signature)
|
||||||
|
throws SignatureException
|
||||||
|
{
|
||||||
|
if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT)
|
||||||
|
{
|
||||||
|
throw new SignatureException(
|
||||||
|
"Invalid ASN.1 signature format: expected SEQ, INT");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte r_pad = (byte) (signature[3] - 20);
|
||||||
|
|
||||||
|
if (signature[24 + r_pad] != ASN1_INT)
|
||||||
|
{
|
||||||
|
throw new SignatureException(
|
||||||
|
"Invalid ASN.1 signature format: expected SEQ, INT, INT");
|
||||||
|
}
|
||||||
|
|
||||||
|
// log.trace("(start) ASN.1 DSA Sig:\n" + base64.toString(signature));
|
||||||
|
|
||||||
|
byte s_pad = (byte) (signature[25 + r_pad] - 20);
|
||||||
|
|
||||||
|
byte[] sig = new byte[41]; // all rfc2536 signatures are 41 bytes.
|
||||||
|
|
||||||
|
// Calculate T:
|
||||||
|
sig[0] = (byte) ((params.getP().bitLength() - 512) / 64);
|
||||||
|
|
||||||
|
// copy R value
|
||||||
|
if (r_pad >= 0)
|
||||||
|
{
|
||||||
|
System.arraycopy(signature, 4 + r_pad, sig, 1, 20);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// R is shorter than 20 bytes, so right justify the number
|
||||||
|
// (r_pad is negative here, remember?).
|
||||||
|
Arrays.fill(sig, 1, 1 - r_pad, (byte) 0);
|
||||||
|
System.arraycopy(signature, 4, sig, 1 - r_pad, 20 + r_pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy S value
|
||||||
|
if (s_pad >= 0)
|
||||||
|
{
|
||||||
|
System.arraycopy(signature, 26 + r_pad + s_pad, sig, 21, 20);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// S is shorter than 20 bytes, so right justify the number
|
||||||
|
// (s_pad is negative here).
|
||||||
|
Arrays.fill(sig, 21, 21 - s_pad, (byte) 0);
|
||||||
|
System.arraycopy(signature, 26 + r_pad, sig, 21 - s_pad, 20 + s_pad);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if (r_pad < 0 || s_pad < 0)
|
||||||
|
// {
|
||||||
|
// log.trace("(finish ***) RFC 2536 DSA Sig:\n" + base64.toString(sig));
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// log.trace("(finish) RFC 2536 DSA Sig:\n" + base64.toString(sig));
|
||||||
|
// }
|
||||||
|
|
||||||
|
return sig;
|
||||||
|
}
|
||||||
|
}
|
@ -28,17 +28,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package se.rfc.unbound.validator;
|
package se.rfc.unbound;
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
import org.apache.log4j.Logger;
|
|
||||||
import org.xbill.DNS.*;
|
import org.xbill.DNS.*;
|
||||||
|
|
||||||
import se.rfc.unbound.*;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a collection of routines encompassing the logic of validating
|
* This is a collection of routines encompassing the logic of validating
|
||||||
* different message types.
|
* different message types.
|
||||||
@ -74,9 +71,6 @@ public class ValUtils
|
|||||||
/** A response to a qtype=ANY query. */
|
/** A response to a qtype=ANY query. */
|
||||||
public static final int ANY = 6;
|
public static final int ANY = 6;
|
||||||
|
|
||||||
private Logger log = Logger.getLogger(this.getClass());
|
|
||||||
private static Logger st_log = Logger.getLogger(ValUtils.class);
|
|
||||||
|
|
||||||
/** A local copy of the verifier object. */
|
/** A local copy of the verifier object. */
|
||||||
private DnsSecVerifier mVerifier;
|
private DnsSecVerifier mVerifier;
|
||||||
|
|
||||||
@ -131,7 +125,7 @@ public class ValUtils
|
|||||||
if (rrsets[i].getType() == Type.CNAME) return CNAME;
|
if (rrsets[i].getType() == Type.CNAME) return CNAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
st_log.warn("Failed to classify response message:\n" + m);
|
// st_log.warn("Failed to classify response message:\n" + m);
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,10 +139,10 @@ public class ValUtils
|
|||||||
* @return a signer name, if the response is signed (even partially), or
|
* @return a signer name, if the response is signed (even partially), or
|
||||||
* null if the response isn't signed.
|
* null if the response isn't signed.
|
||||||
*/
|
*/
|
||||||
public Name findSigner(SMessage m, Request request)
|
public Name findSigner(SMessage m)
|
||||||
{
|
{
|
||||||
int subtype = classifyResponse(m);
|
int subtype = classifyResponse(m);
|
||||||
Name qname = request.getQName();
|
Name qname = m.getQName();
|
||||||
|
|
||||||
SRRset[] rrsets;
|
SRRset[] rrsets;
|
||||||
|
|
||||||
@ -182,8 +176,8 @@ public class ValUtils
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
default :
|
default :
|
||||||
log.debug("findSigner: could not find signer name "
|
// log.debug("findSigner: could not find signer name "
|
||||||
+ "for unknown type response.");
|
// + "for unknown type response.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,87 +216,87 @@ public class ValUtils
|
|||||||
* this sort of thing is checked before fetching the matching DNSKEY
|
* this sort of thing is checked before fetching the matching DNSKEY
|
||||||
* rrset.
|
* rrset.
|
||||||
*/
|
*/
|
||||||
public KeyEntry verifyNewDNSKEYs(SRRset dnskey_rrset, SRRset ds_rrset)
|
// public KeyEntry verifyNewDNSKEYs(SRRset dnskey_rrset, SRRset ds_rrset)
|
||||||
{
|
// {
|
||||||
if (!dnskey_rrset.getName().equals(ds_rrset.getName()))
|
// if (!dnskey_rrset.getName().equals(ds_rrset.getName()))
|
||||||
{
|
// {
|
||||||
log.debug("DNSKEY RRset did not match DS RRset by name!");
|
//// log.debug("DNSKEY RRset did not match DS RRset by name!");
|
||||||
return KeyEntry
|
// return KeyEntry
|
||||||
.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
|
// .newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// as long as this is false, we can consider this DS rrset to be
|
// // as long as this is false, we can consider this DS rrset to be
|
||||||
// equivalent to no DS rrset.
|
// // equivalent to no DS rrset.
|
||||||
boolean hasUsefulDS = false;
|
// boolean hasUsefulDS = false;
|
||||||
|
//
|
||||||
for (Iterator i = ds_rrset.rrs(); i.hasNext();)
|
// for (Iterator i = ds_rrset.rrs(); i.hasNext();)
|
||||||
{
|
// {
|
||||||
DSRecord ds = (DSRecord) i.next();
|
// DSRecord ds = (DSRecord) i.next();
|
||||||
|
//
|
||||||
// Check to see if we can understand this DS.
|
// // Check to see if we can understand this DS.
|
||||||
if (!supportsDigestID(ds.getDigestID())
|
// if (!supportsDigestID(ds.getDigestID())
|
||||||
|| !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
|
// || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Once we see a single DS with a known digestID and algorithm, we
|
// // Once we see a single DS with a known digestID and algorithm, we
|
||||||
// cannot return INSECURE (with a "null" KeyEntry).
|
// // cannot return INSECURE (with a "null" KeyEntry).
|
||||||
hasUsefulDS = true;
|
// hasUsefulDS = true;
|
||||||
|
//
|
||||||
DNSKEY : for (Iterator j = dnskey_rrset.rrs(); j.hasNext();)
|
// DNSKEY : for (Iterator j = dnskey_rrset.rrs(); j.hasNext();)
|
||||||
{
|
// {
|
||||||
DNSKEYRecord dnskey = (DNSKEYRecord) j.next();
|
// DNSKEYRecord dnskey = (DNSKEYRecord) j.next();
|
||||||
|
//
|
||||||
// Skip DNSKEYs that don't match the basic criteria.
|
// // Skip DNSKEYs that don't match the basic criteria.
|
||||||
if (ds.getFootprint() != dnskey.getFootprint()
|
// if (ds.getFootprint() != dnskey.getFootprint()
|
||||||
|| ds.getAlgorithm() != dnskey.getAlgorithm())
|
// || ds.getAlgorithm() != dnskey.getAlgorithm())
|
||||||
{
|
// {
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Convert the candidate DNSKEY into a hash using the same DS hash
|
// // Convert the candidate DNSKEY into a hash using the same DS hash
|
||||||
// algorithm.
|
// // algorithm.
|
||||||
byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
|
// byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
|
||||||
byte[] ds_hash = ds.getDigest();
|
// byte[] ds_hash = ds.getDigest();
|
||||||
|
//
|
||||||
// see if there is a length mismatch (unlikely)
|
// // see if there is a length mismatch (unlikely)
|
||||||
if (key_hash.length != ds_hash.length)
|
// if (key_hash.length != ds_hash.length)
|
||||||
{
|
// {
|
||||||
continue DNSKEY;
|
// continue DNSKEY;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
for (int k = 0; k < key_hash.length; k++)
|
// for (int k = 0; k < key_hash.length; k++)
|
||||||
{
|
// {
|
||||||
if (key_hash[k] != ds_hash[k]) continue DNSKEY;
|
// if (key_hash[k] != ds_hash[k]) continue DNSKEY;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// Otherwise, we have a match! Make sure that the DNSKEY verifies
|
// // Otherwise, we have a match! Make sure that the DNSKEY verifies
|
||||||
// *with this key*.
|
// // *with this key*.
|
||||||
byte res = mVerifier.verify(dnskey_rrset, dnskey);
|
// byte res = mVerifier.verify(dnskey_rrset, dnskey);
|
||||||
if (res == SecurityStatus.SECURE)
|
// if (res == SecurityStatus.SECURE)
|
||||||
{
|
// {
|
||||||
log.trace("DS matched DNSKEY.");
|
//// log.trace("DS matched DNSKEY.");
|
||||||
dnskey_rrset.setSecurityStatus(SecurityStatus.SECURE);
|
// dnskey_rrset.setSecurityStatus(SecurityStatus.SECURE);
|
||||||
return KeyEntry.newKeyEntry(dnskey_rrset);
|
// return KeyEntry.newKeyEntry(dnskey_rrset);
|
||||||
}
|
// }
|
||||||
// If it didn't validate with the DNSKEY, try the next one!
|
// // If it didn't validate with the DNSKEY, try the next one!
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
// None of the DS's worked out.
|
// // None of the DS's worked out.
|
||||||
|
//
|
||||||
// If no DSs were understandable, then this is OK.
|
// // If no DSs were understandable, then this is OK.
|
||||||
if (!hasUsefulDS)
|
// if (!hasUsefulDS)
|
||||||
{
|
// {
|
||||||
log.debug("No usuable DS records were found -- treating as insecure.");
|
//// log.debug("No usuable DS records were found -- treating as insecure.");
|
||||||
return KeyEntry.newNullKeyEntry(ds_rrset.getName(), ds_rrset
|
// return KeyEntry.newNullKeyEntry(ds_rrset.getName(), ds_rrset
|
||||||
.getDClass(), ds_rrset.getTTL());
|
// .getDClass(), ds_rrset.getTTL());
|
||||||
}
|
// }
|
||||||
// If any were understandable, then it is bad.
|
// // If any were understandable, then it is bad.
|
||||||
log.debug("Failed to match any usable DS to a DNSKEY.");
|
//// log.debug("Failed to match any usable DS to a DNSKEY.");
|
||||||
return KeyEntry.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
|
// return KeyEntry.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a DNSKEY record, generate the DS record from it.
|
* Given a DNSKEY record, generate the DS record from it.
|
||||||
@ -327,18 +321,17 @@ public class ValUtils
|
|||||||
md = MessageDigest.getInstance("SHA");
|
md = MessageDigest.getInstance("SHA");
|
||||||
return md.digest(os.toByteArray());
|
return md.digest(os.toByteArray());
|
||||||
case DSRecord.SHA256_DIGEST_ID:
|
case DSRecord.SHA256_DIGEST_ID:
|
||||||
SHA256 sha = new SHA256();
|
md = MessageDigest.getInstance("SHA256");
|
||||||
sha.setData(os.toByteArray());
|
return md.digest(os.toByteArray());
|
||||||
return sha.getDigest();
|
|
||||||
default :
|
default :
|
||||||
st_log.warn("Unknown DS algorithm: " + ds_alg);
|
// st_log.warn("Unknown DS algorithm: " + ds_alg);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (NoSuchAlgorithmException e)
|
catch (NoSuchAlgorithmException e)
|
||||||
{
|
{
|
||||||
st_log.error("Error using DS algorithm: " + ds_alg, e);
|
// st_log.error("Error using DS algorithm: " + ds_alg, e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,7 +372,7 @@ public class ValUtils
|
|||||||
* @param rrset The SRRset to update.
|
* @param rrset The SRRset to update.
|
||||||
* @param security The security status.
|
* @param security The security status.
|
||||||
*/
|
*/
|
||||||
public static void setRRsetSecurity(SRRset rrset, int security)
|
public static void setRRsetSecurity(SRRset rrset, byte security)
|
||||||
{
|
{
|
||||||
if (rrset == null) return;
|
if (rrset == null) return;
|
||||||
|
|
||||||
@ -405,7 +398,7 @@ public class ValUtils
|
|||||||
* (ans_rrset.getSecurityStatus() != SecurityStatus.SECURE) { return; }
|
* (ans_rrset.getSecurityStatus() != SecurityStatus.SECURE) { return; }
|
||||||
* key_rrset = ke.getRRset();
|
* key_rrset = ke.getRRset();
|
||||||
*/
|
*/
|
||||||
public static void setMessageSecurity(SMessage m, int security)
|
public static void setMessageSecurity(SMessage m, byte security)
|
||||||
{
|
{
|
||||||
if (m == null) return;
|
if (m == null) return;
|
||||||
|
|
||||||
@ -441,21 +434,21 @@ public class ValUtils
|
|||||||
|
|
||||||
if (rrset.getSecurityStatus() == SecurityStatus.SECURE)
|
if (rrset.getSecurityStatus() == SecurityStatus.SECURE)
|
||||||
{
|
{
|
||||||
log.trace("verifySRRset: rrset <" + rrset_name
|
// log.trace("verifySRRset: rrset <" + rrset_name
|
||||||
+ "> previously found to be SECURE");
|
// + "> previously found to be SECURE");
|
||||||
return SecurityStatus.SECURE;
|
return SecurityStatus.SECURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte status = mVerifier.verify(rrset, key_rrset);
|
byte status = mVerifier.verify(rrset, key_rrset);
|
||||||
if (status != SecurityStatus.SECURE)
|
if (status != SecurityStatus.SECURE)
|
||||||
{
|
{
|
||||||
log.debug("verifySRRset: rrset <" + rrset_name + "> found to be BAD");
|
// log.debug("verifySRRset: rrset <" + rrset_name + "> found to be BAD");
|
||||||
status = SecurityStatus.BOGUS;
|
status = SecurityStatus.BOGUS;
|
||||||
}
|
}
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
log.trace("verifySRRset: rrset <" + rrset_name + "> found to be SECURE");
|
// log.trace("verifySRRset: rrset <" + rrset_name + "> found to be SECURE");
|
||||||
}
|
// }
|
||||||
|
|
||||||
rrset.setSecurityStatus(status);
|
rrset.setSecurityStatus(status);
|
||||||
return status;
|
return status;
|
||||||
@ -477,6 +470,12 @@ public class ValUtils
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static RRSIGRecord rrsetFirstSig(RRset rrset) {
|
||||||
|
for (Iterator i = rrset.sigs(); i.hasNext(); ) {
|
||||||
|
return (RRSIGRecord) i.next();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Determine by looking at a signed RRset whether or not the rrset name was
|
* Determine by looking at a signed RRset whether or not the rrset name was
|
||||||
* the result of a wildcard expansion.
|
* the result of a wildcard expansion.
|
||||||
@ -488,7 +487,7 @@ public class ValUtils
|
|||||||
public static boolean rrsetIsWildcardExpansion(RRset rrset)
|
public static boolean rrsetIsWildcardExpansion(RRset rrset)
|
||||||
{
|
{
|
||||||
if (rrset == null) return false;
|
if (rrset == null) return false;
|
||||||
RRSIGRecord rrsig = (RRSIGRecord) rrset.firstSig();
|
RRSIGRecord rrsig = rrsetFirstSig(rrset);
|
||||||
|
|
||||||
if (rrset.getName().labels() - 1 > rrsig.getLabels())
|
if (rrset.getName().labels() - 1 > rrsig.getLabels())
|
||||||
{
|
{
|
||||||
@ -510,7 +509,7 @@ public class ValUtils
|
|||||||
public static Name rrsetWildcard(RRset rrset)
|
public static Name rrsetWildcard(RRset rrset)
|
||||||
{
|
{
|
||||||
if (rrset == null) return null;
|
if (rrset == null) return null;
|
||||||
RRSIGRecord rrsig = (RRSIGRecord) rrset.firstSig();
|
RRSIGRecord rrsig = rrsetFirstSig(rrset);
|
||||||
|
|
||||||
// if the RRSIG label count is shorter than the number of actual labels,
|
// if the RRSIG label count is shorter than the number of actual labels,
|
||||||
// then this rrset was synthesized from a wildcard.
|
// then this rrset was synthesized from a wildcard.
|
||||||
|
Loading…
Reference in New Issue
Block a user