diff --git a/ChangeLog b/ChangeLog index 68ae9a1..b23fdf1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-12-14 Blacka + + * jdnssec-verifyzone: Add options to either fudge or ignore RRSIG + inception and expiration times. + 2010-12-06 David Blacka * jdnssec-verifyzone: Complete refactored the verification code to diff --git a/src/com/verisignlabs/dnssec/cl/VerifyZone.java b/src/com/verisignlabs/dnssec/cl/VerifyZone.java index 1f45982..ba3fd5b 100644 --- a/src/com/verisignlabs/dnssec/cl/VerifyZone.java +++ b/src/com/verisignlabs/dnssec/cl/VerifyZone.java @@ -50,7 +50,8 @@ public class VerifyZone { private static Logger log; - // A log formatter that strips away all of the noise that the default SimpleFormatter has + // A log formatter that strips away all of the noise that the default + // SimpleFormatter has private static class MyLogFormatter extends java.util.logging.Formatter { @Override @@ -58,7 +59,7 @@ public class VerifyZone { StringBuilder out = new StringBuilder(); String lvl = arg0.getLevel().getName(); - + out.append(lvl); out.append(": "); out.append(arg0.getMessage()); @@ -75,10 +76,11 @@ public class VerifyZone private static class CLIState { private Options opts; - // public boolean strict = false; - // public File keydir = null; - public String zonefile = null; - public String[] keyfiles = null; + public String zonefile = null; + public String[] keyfiles = null; + public int startfudge = 0; + public int expirefudge = 0; + public boolean ignoreTime = false; public CLIState() { @@ -102,7 +104,7 @@ public class VerifyZone OptionBuilder.withLongOpt("verbose"); OptionBuilder.withArgName("level"); OptionBuilder.withDescription("verbosity level -- 0 is silence, " - + "5 is debug information, 6 is trace information.\n" + "default is level 5."); + + "5 is debug information, 6 is trace information. default is level 5."); opts.addOption(OptionBuilder.create('v')); OptionBuilder.hasArg(); @@ -110,6 +112,22 @@ public class VerifyZone OptionBuilder.withLongOpt("alg-alias"); OptionBuilder.withDescription("Define an alias for an algorithm"); opts.addOption(OptionBuilder.create('A')); + + OptionBuilder.hasOptionalArg(); + OptionBuilder.withLongOpt("sig-start-fudge"); + OptionBuilder.withArgName("seconds"); + OptionBuilder.withDescription("'fudge' RRSIG inception times by 'seconds' seconds."); + opts.addOption(OptionBuilder.create('S')); + + OptionBuilder.hasOptionalArg(); + OptionBuilder.withLongOpt("sig-expire-fudge"); + OptionBuilder.withArgName("seconds"); + OptionBuilder.withDescription("'fudge' RRSIG expiration times by 'seconds' seconds."); + opts.addOption(OptionBuilder.create('E')); + + OptionBuilder.withLongOpt("ignore-time"); + OptionBuilder.withDescription("Ignore RRSIG inception and expiration time errors."); + opts.addOption(OptionBuilder.create()); } public void parseCommandLine(String[] args) @@ -153,6 +171,22 @@ public class VerifyZone org.xbill.DNS.Options.set("multiline"); } + if (cli.hasOption("ignore-time")) + { + ignoreTime = true; + } + + String optstr = null; + if ((optstr = cli.getOptionValue('S')) != null) + { + startfudge = parseInt(optstr, 0); + } + + if ((optstr = cli.getOptionValue('E')) != null) + { + expirefudge = parseInt(optstr, 0); + } + String[] optstrs = null; if ((optstrs = cli.getOptionValues('A')) != null) { @@ -207,7 +241,8 @@ public class VerifyZone // print our own usage statement: f.printHelp(out, 75, "jdnssec-verifyzone [..options..] zonefile " - + "[keyfile [keyfile...]]", null, opts, HelpFormatter.DEFAULT_LEFT_PAD, + + "[keyfile [keyfile...]]", null, opts, + HelpFormatter.DEFAULT_LEFT_PAD, HelpFormatter.DEFAULT_DESC_PAD, null); out.flush(); @@ -242,6 +277,9 @@ public class VerifyZone public static void execute(CLIState state) throws Exception { ZoneVerifier zoneverifier = new ZoneVerifier(); + zoneverifier.getVerifier().setStartFudge(state.startfudge); + zoneverifier.getVerifier().setExpireFudge(state.expirefudge); + zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime); List records = ZoneUtils.readZoneFile(state.zonefile, null); diff --git a/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java b/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java index 3495878..31ab5fb 100644 --- a/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java +++ b/src/com/verisignlabs/dnssec/security/DnsSecVerifier.java @@ -94,8 +94,7 @@ public class DnsSecVerifier implements Verifier for (Iterator i = l.iterator(); i.hasNext();) { DnsKeyPair p = (DnsKeyPair) i.next(); - if (p.getDNSKEYAlgorithm() == algorithm - && p.getDNSKEYFootprint() == keyid) + if (p.getDNSKEYAlgorithm() == algorithm && p.getDNSKEYFootprint() == keyid) { return p; } @@ -108,6 +107,7 @@ public class DnsSecVerifier implements Verifier private int mStartFudge = 0; private int mExpireFudge = 0; private boolean mVerifyAllSigs = false; + private boolean mIgnoreTime = false; private Logger log; @@ -153,8 +153,12 @@ public class DnsSecVerifier implements Verifier mVerifyAllSigs = v; } - private DnsKeyPair findCachedKey(Cache cache, Name name, int algorithm, - int footprint) + public void setIgnoreTime(boolean v) + { + mIgnoreTime = v; + } + + private DnsKeyPair findCachedKey(Cache cache, Name name, int algorithm, int footprint) { RRset[] keysets = cache.findAnyRecords(name, Type.KEY); if (keysets == null) return null; @@ -166,8 +170,7 @@ public class DnsSecVerifier implements Verifier Object o = i.next(); if (!(o instanceof DNSKEYRecord)) continue; DNSKEYRecord keyrec = (DNSKEYRecord) o; - if (keyrec.getAlgorithm() == algorithm - && keyrec.getFootprint() == footprint) + if (keyrec.getAlgorithm() == algorithm && keyrec.getFootprint() == footprint) { return new DnsKeyPair(keyrec, (PrivateKey) null); } @@ -176,8 +179,7 @@ public class DnsSecVerifier implements Verifier return null; } - private DnsKeyPair findKey(Cache cache, Name name, int algorithm, - int footprint) + private DnsKeyPair findKey(Cache cache, Name name, int algorithm, int footprint) { DnsKeyPair pair = mKeyStore.find(name, algorithm, footprint); if (pair == null && cache != null) @@ -194,17 +196,17 @@ public class DnsSecVerifier implements Verifier if (!rrset.getName().equals(sigrec.getName())) { log.fine("Signature name does not match RRset name"); - if (reasons != null) - reasons.add("Signature name does not match RRset name"); + if (reasons != null) reasons.add("Signature name does not match RRset name"); return DNSSEC.Failed; } if (rrset.getType() != sigrec.getTypeCovered()) { log.fine("Signature type does not match RRset type"); - if (reasons != null) - reasons.add("Signature type does not match RRset type"); + if (reasons != null) reasons.add("Signature type does not match RRset type"); } + if (mIgnoreTime) return DNSSEC.Secure; + Date now = new Date(); Date start = sigrec.getTimeSigned(); Date expire = sigrec.getExpire(); @@ -231,8 +233,7 @@ public class DnsSecVerifier implements Verifier } if (now.after(expire)) { - log.fine("Signature has expired (now = " + now + ", sig expires = " - + expire); + log.fine("Signature has expired (now = " + now + ", sig expires = " + expire); if (reasons != null) reasons.add("Signature has expired."); return DNSSEC.Failed; } @@ -254,14 +255,13 @@ public class DnsSecVerifier implements Verifier * could not be completed (usually because the public key was not * available). */ - public byte verifySignature(RRset rrset, RRSIGRecord sigrec, Cache cache, - List reasons) + public byte verifySignature(RRset rrset, RRSIGRecord sigrec, Cache cache, List reasons) { byte result = validateSignature(rrset, sigrec, reasons); if (result != DNSSEC.Secure) return result; - DnsKeyPair keypair = findKey(cache, sigrec.getSigner(), sigrec - .getAlgorithm(), sigrec.getFootprint()); + DnsKeyPair keypair = findKey(cache, sigrec.getSigner(), sigrec.getAlgorithm(), + sigrec.getFootprint()); if (keypair == null) { @@ -288,8 +288,7 @@ public class DnsSecVerifier implements Verifier if (!signer.verify(sig)) { - if (reasons != null) - reasons.add("Signature failed to verify cryptographically"); + if (reasons != null) reasons.add("Signature failed to verify cryptographically"); log.fine("Signature failed to verify cryptographically"); return DNSSEC.Failed; } @@ -304,8 +303,7 @@ public class DnsSecVerifier implements Verifier { log.severe("Security error: " + e); } - if (reasons != null) - reasons.add("Signature failed to verify due to exception"); + if (reasons != null) reasons.add("Signature failed to verify due to exception"); log.fine("Signature failed to verify due to exception"); return DNSSEC.Insecure; } diff --git a/src/com/verisignlabs/dnssec/security/ZoneVerifier.java b/src/com/verisignlabs/dnssec/security/ZoneVerifier.java index 19f9f13..7827c24 100644 --- a/src/com/verisignlabs/dnssec/security/ZoneVerifier.java +++ b/src/com/verisignlabs/dnssec/security/ZoneVerifier.java @@ -110,6 +110,11 @@ public class ZoneVerifier mBAcmp = new ByteArrayComparator(); } + public DnsSecVerifier getVerifier() + { + return mVerifier; + } + private static String key(Name n, int type) { return n.toString() + ':' + type; @@ -231,7 +236,8 @@ public class ZoneVerifier // All RRs at the zone apex are normal if (n.equals(mZoneName)) return NodeType.NORMAL; - // If the node is below a zone cut (either a delegation or DNAME), it is glue. + // If the node is below a zone cut (either a delegation or DNAME), it is + // glue. if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut)) { return NodeType.GLUE; @@ -284,7 +290,8 @@ public class ZoneVerifier for (int type : typeset) { if (type == Type.RRSIG) continue; - // at delegation points, only DS RRs are signed (and NSEC, but those are checked separately) + // at delegation points, only DS RRs are signed (and NSEC, but those are + // checked separately) if (ntype == NodeType.DELEGATION && type != Type.DS) continue; // otherwise, verify the RRset. String k = key(n, type); @@ -370,22 +377,6 @@ public class ZoneVerifier return result == DNSSEC.Secure ? 0 : 1; } - private String typesetToString(Set typeset) - { - if (typeset == null) return ""; - - StringBuilder sb = new StringBuilder(); - boolean first = true; - for (int type : typeset) - { - if (!first) sb.append(' '); - sb.append(Type.string(type)); - first = false; - } - - return sb.toString(); - } - private String typesToString(int[] types) { StringBuilder sb = new StringBuilder(); @@ -400,9 +391,23 @@ public class ZoneVerifier return sb.toString(); } + private String typesetToString(Set typeset) + { + if (typeset == null) return ""; + + int[] types = new int[typeset.size()]; + int i = 0; + for (int type : typeset) + { + types[i++] = type; + } + return typesToString(types); + } + private boolean checkTypeMap(Set typeset, int[] types) { - // a null typeset means that we are expecting the typemap of an ENT, which should be empty. + // a null typeset means that we are expecting the typemap of an ENT, which + // should be empty. if (typeset == null) return types.length == 0; Set compareTypeset = new HashSet(); @@ -446,7 +451,8 @@ public class ZoneVerifier private boolean shouldCheckENTs(Name n, Set typeset, NodeType ntype) { - // if we are just one (or zero) labels longer than the zonename, the node can't create a ENT + // if we are just one (or zero) labels longer than the zonename, the node + // can't create a ENT if (n.labels() <= mZoneName.labels() + 1) return false; // we probably won't ever get called for a GLUE node @@ -455,7 +461,8 @@ public class ZoneVerifier // if we aren't doing opt-out, then all possible ENTs must be checked. if (mDNSSECType == DNSSECType.NSEC3) return true; - // if we are opt-out, and the node is an insecure delegation, don't check ENTs. + // if we are opt-out, and the node is an insecure delegation, don't check + // ENTs. if (ntype == NodeType.DELEGATION && !typeset.contains(Type.DS)) { return false; @@ -492,7 +499,7 @@ public class ZoneVerifier { log.warning("Typemap for NSEC3 RR " + hashname + " for " + n + " did not match what was expected. Expected '" + typesetToString(typeset) - + "', got '" + typesToString(nsec3.getTypes())); + + "', got '" + typesToString(nsec3.getTypes()) + "'"); errors++; } @@ -520,7 +527,8 @@ public class ZoneVerifier for (Iterator> i = mNSECMap.entrySet().iterator(); i.hasNext();) { - // check the internal ordering of the previous NSEC record. This avoids looking at the last one, + // check the internal ordering of the previous NSEC record. This avoids + // looking at the last one, // which is different. if (lastNSEC != null) { @@ -536,7 +544,8 @@ public class ZoneVerifier Name n = entry.getKey(); MarkRRset rrset = entry.getValue(); - // check to see if the NSEC is marked. If not, it was not correlated to a signed node. + // check to see if the NSEC is marked. If not, it was not correlated to a + // signed node. if (!rrset.getMark()) { log.warning("NSEC RR for " + n + " appears to be extra."); @@ -545,7 +554,8 @@ public class ZoneVerifier NSECRecord nsec = (NSECRecord) rrset.first(); - // This is just a sanity check. If this isn't true, we are constructing the + // This is just a sanity check. If this isn't true, we are constructing + // the // nsec map incorrectly. if (!n.equals(nsec.getName())) { @@ -553,7 +563,8 @@ public class ZoneVerifier errors++; } - // If this is the first row, ensure that the owner name equals the zone name + // If this is the first row, ensure that the owner name equals the zone + // name if (lastNSEC == null && !n.equals(mZoneName)) { log.warning("The first NSEC in the chain does not match the zone name: name = " @@ -611,7 +622,8 @@ public class ZoneVerifier for (Iterator> i = mNSEC3Map.entrySet().iterator(); i.hasNext();) { - // check the internal ordering of the previous NSEC3 record. This avoids looking at the last one, + // check the internal ordering of the previous NSEC3 record. This avoids + // looking at the last one, // which is different. if (lastNSEC3 != null) { @@ -627,7 +639,8 @@ public class ZoneVerifier Name n = entry.getKey(); MarkRRset rrset = entry.getValue(); - // check to see if the NSEC is marked. If not, it was not correlated to a signed node. + // check to see if the NSEC is marked. If not, it was not correlated to a + // signed node. if (!rrset.getMark()) { log.warning("NSEC3 RR for " + n + " appears to be extra."); @@ -636,7 +649,8 @@ public class ZoneVerifier NSEC3Record nsec3 = (NSEC3Record) rrset.first(); - // This is just a sanity check. If this isn't true, we are constructing the + // This is just a sanity check. If this isn't true, we are constructing + // the // nsec3 map incorrectly. if (!n.equals(nsec3.getName())) { @@ -650,7 +664,8 @@ public class ZoneVerifier firstNSEC3 = nsec3; } else - // Check that the prior NSEC3's next hashed name equals this row's hashed owner name. + // Check that the prior NSEC3's next hashed name equals this row's hashed + // owner name. { if (compareNSEC3Hashes(nsec3.getName(), lastNSEC3.getNext()) != 0) {