org.xbill.DNS.dnssec.DnsSecVerifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.apache.servicemix.bundles.dnsjava
Show all versions of org.apache.servicemix.bundles.dnsjava
This OSGi bundle wraps ${pkgArtifactId} ${pkgVersion} jar file.
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2005 VeriSign. All rights reserved.
// Copyright (c) 2013-2021 Ingo Bauersachs
package org.xbill.DNS.dnssec;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.xbill.DNS.DNSKEYRecord;
import org.xbill.DNS.DNSSEC;
import org.xbill.DNS.DNSSEC.DNSSECException;
import org.xbill.DNS.DNSSEC.KeyMismatchException;
import org.xbill.DNS.DNSSEC.SignatureExpiredException;
import org.xbill.DNS.DNSSEC.SignatureNotYetValidException;
import org.xbill.DNS.ExtendedErrorCodeOption;
import org.xbill.DNS.RRSIGRecord;
import org.xbill.DNS.RRset;
import org.xbill.DNS.Record;
import org.xbill.DNS.Type;
/**
* A class for performing basic DNSSEC verification. The DNSJAVA package contains a similar class.
* This is a reimplementation that allows us to have finer control over the validation process.
*
* @since 3.5
*/
@Slf4j
final class DnsSecVerifier {
/**
* Find the matching DNSKEY(s) to an RRSIG within a DNSKEY rrset. Normally this will only return
* one DNSKEY. It can return more than one, since KeyID/Footprints are not guaranteed to be
* unique.
*
* @param dnskeyRrset The DNSKEY rrset to search.
* @param signature The RRSIG to match against.
* @return A List that contains one or more DNSKEYRecord objects; empty if a matching DNSKEY could
* not be found.
*/
private List findKey(RRset dnskeyRrset, RRSIGRecord signature) {
if (!signature.getSigner().equals(dnskeyRrset.getName())) {
log.trace(
"Could not find appropriate key because incorrect keyset was supplied. Wanted: {}, got: {}",
signature.getSigner(),
dnskeyRrset.getName());
return Collections.emptyList();
}
int keyid = signature.getFootprint();
int alg = signature.getAlgorithm();
List res = new ArrayList<>(dnskeyRrset.size());
for (Record r : dnskeyRrset.rrs()) {
DNSKEYRecord dnskey = (DNSKEYRecord) r;
if (dnskey.getAlgorithm() == alg && dnskey.getFootprint() == keyid) {
res.add(dnskey);
}
}
return res;
}
/**
* Verify an RRset against a particular signature.
*
* @param rrset The RRset to verify.
* @param sigrec The signature record that signs the RRset.
* @param keyRrset The keys used to create the signature record.
* @param date The date against which to verify the signature.
* @return {@link SecurityStatus#SECURE} if the signature verified, {@link SecurityStatus#BOGUS}
* if it did not verify (for any reason), and {@link SecurityStatus#UNCHECKED} if verification
* could not be completed (usually because the public key was not available).
*/
private JustifiedSecStatus verifySignature(
SRRset rrset, RRSIGRecord sigrec, RRset keyRrset, Instant date) {
if (!rrset.getName().subdomain(keyRrset.getName())) {
log.debug("Signer name is off-tree");
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.DNSSEC_BOGUS,
R.get("dnskey.key_offtree", keyRrset.getName(), rrset.getName()));
}
List keys = this.findKey(keyRrset, sigrec);
if (keys.isEmpty()) {
log.trace("Could not find appropriate key");
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.DNSKEY_MISSING,
R.get("dnskey.no_key", sigrec.getSigner()));
}
for (DNSKEYRecord key : keys) {
try {
DNSSEC.verify(rrset, sigrec, key, date);
ValUtils.setCanonicalNsecOwner(rrset, sigrec);
return new JustifiedSecStatus(SecurityStatus.SECURE, -1, null);
} catch (KeyMismatchException kme) {
return new JustifiedSecStatus(
SecurityStatus.BOGUS, ExtendedErrorCodeOption.DNSSEC_BOGUS, R.get("dnskey.no_match"));
} catch (SignatureExpiredException e) {
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.SIGNATURE_EXPIRED,
R.get("dnskey.expired"));
} catch (SignatureNotYetValidException e) {
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.SIGNATURE_NOT_YET_VALID,
R.get("dnskey.not_yet_valid"));
} catch (DNSSECException e) {
log.error(
"Failed to validate RRset {}/{}", rrset.getName(), Type.string(rrset.getType()), e);
return new JustifiedSecStatus(
SecurityStatus.BOGUS, ExtendedErrorCodeOption.DNSSEC_BOGUS, R.get("dnskey.invalid"));
}
}
return new JustifiedSecStatus(SecurityStatus.UNCHECKED, -1, null);
}
/**
* Verifies an RRset. This routine does not modify the RRset. This RRset is presumed to be
* verifiable, and the correct DNSKEY rrset is presumed to have been found.
*
* @param rrset The RRset to verify.
* @param keyRrset The keys to verify the signatures in the RRset to check.
* @param date The date against which to verify the rrset.
* @return SecurityStatus.SECURE if the rrest verified positively, SecurityStatus.BOGUS otherwise.
*/
public JustifiedSecStatus verify(SRRset rrset, RRset keyRrset, Instant date) {
List sigs = rrset.sigs();
if (sigs.isEmpty()) {
log.info("RRset failed to verify due to lack of signatures");
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.RRSIGS_MISSING,
R.get("validate.bogus.missingsig"));
}
JustifiedSecStatus res =
new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.RRSIGS_MISSING,
R.get("validate.bogus.missingsig"));
for (RRSIGRecord sigrec : sigs) {
res = this.verifySignature(rrset, sigrec, keyRrset, date);
if (res.status == SecurityStatus.SECURE) {
return res;
}
}
log.info("RRset failed to verify: all signatures were BOGUS");
return res;
}
/**
* Verify an RRset against a single DNSKEY. Use this when you must be certain that an RRset signed
* and verifies with a particular DNSKEY (as opposed to a particular DNSKEY rrset).
*
* @param rrset The rrset to verify.
* @param dnskey The DNSKEY to verify with.
* @param date The date against which to verify the rrset.
* @return SecurityStatus.SECURE if the rrset verified, BOGUS otherwise.
*/
public JustifiedSecStatus verify(RRset rrset, DNSKEYRecord dnskey, Instant date) {
List sigs = rrset.sigs();
if (sigs.isEmpty()) {
log.info("RRset failed to verify due to lack of signatures");
return new JustifiedSecStatus(
SecurityStatus.BOGUS,
ExtendedErrorCodeOption.RRSIGS_MISSING,
R.get("dnskey.no_sigs", rrset.getName()));
}
DNSSECException lastException = null;
for (RRSIGRecord sigrec : sigs) {
// Skip RRSIGs that do not match our given key's footprint.
if (sigrec.getFootprint() != dnskey.getFootprint()) {
continue;
}
try {
DNSSEC.verify(rrset, sigrec, dnskey, date);
return new JustifiedSecStatus(SecurityStatus.SECURE, -1, null);
} catch (DNSSECException e) {
log.error("Failed to validate RRset", e);
lastException = e;
}
}
log.info("RRset failed to verify: all signatures were BOGUS");
int edeReason = ExtendedErrorCodeOption.DNSSEC_BOGUS;
String reason = "dnskey.invalid";
if (lastException instanceof SignatureExpiredException) {
edeReason = ExtendedErrorCodeOption.SIGNATURE_EXPIRED;
} else if (lastException instanceof SignatureNotYetValidException) {
edeReason = ExtendedErrorCodeOption.SIGNATURE_NOT_YET_VALID;
}
return new JustifiedSecStatus(SecurityStatus.BOGUS, edeReason, reason);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy