/* * dnssecjava - a DNSSEC validating stub resolver for Java * Copyright (c) 2013-2015 Ingo Bauersachs * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package org.jitsi.dnssec; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.powermock.api.mockito.PowerMockito.doReturn; import static org.powermock.api.mockito.PowerMockito.spy; import static org.powermock.api.mockito.PowerMockito.when; import static org.powermock.api.mockito.PowerMockito.whenNew; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.powermock.reflect.Whitebox; import org.xbill.DNS.DClass; import org.xbill.DNS.DNSKEYRecord; import org.xbill.DNS.DNSSEC; import org.xbill.DNS.DNSSEC.DNSSECException; import org.xbill.DNS.Flags; import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.RRset; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; import org.xbill.DNS.Section; import org.xbill.DNS.Type; @RunWith(PowerMockRunner.class) @PrepareForTest(DNSKEYRecord.class) public class TestPriming extends TestBase { @Test public void testDnskeyPrimeResponseWithEmptyAnswerIsBad() throws IOException { Message message = new Message(); message.addRecord(Record.newRecord(Name.root, Type.DNSKEY, DClass.IN), Section.QUESTION); add("./DNSKEY", message); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_rrset:.", getReason(response)); } @Test public void testRootDnskeyPrimeResponseWithNxDomainIsBad() throws IOException { Message message = new Message(); message.addRecord(Record.newRecord(Name.root, Type.DNSKEY, DClass.IN), Section.QUESTION); message.getHeader().setRcode(Rcode.NXDOMAIN); add("./DNSKEY", message); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_rrset:.", getReason(response)); } @Test public void testDnskeyPrimeResponseWithInvalidSignatureIsBad() throws IOException, NumberFormatException, DNSSECException { Message m = resolver.send(createMessage("./DNSKEY")); Message message = messageFromString(m.toString().replaceAll("(.*\\sRRSIG\\sDNSKEY\\s(\\d+\\s+){6}.*\\.)(.*)", "$1 YXNkZg==")); add("./DNSKEY", message); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_ds_match", getReason(response)); } @Test @PrepareMocks("prepareTestDnskeyPrimeResponseWithMismatchedFootprintIsBad") public void testDnskeyPrimeResponseWithMismatchedFootprintIsBad() throws Exception { Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_ds_match", getReason(response)); } public void prepareTestDnskeyPrimeResponseWithMismatchedFootprintIsBad() throws Exception { DNSKEYRecord emptyDnskeyRecord = spy(Whitebox.invokeConstructor(DNSKEYRecord.class)); when(emptyDnskeyRecord.getFootprint()).thenReturn(-1); whenNew(DNSKEYRecord.class).withNoArguments().thenReturn(emptyDnskeyRecord); } @Test @PrepareMocks("prepareTestDnskeyPrimeResponseWithMismatchedAlgorithmIsBad") public void testDnskeyPrimeResponseWithMismatchedAlgorithmIsBad() throws IOException, NumberFormatException, DNSSECException { Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_ds_match", getReason(response)); } public void prepareTestDnskeyPrimeResponseWithMismatchedAlgorithmIsBad() throws Exception { DNSKEYRecord emptyDnskeyRecord = spy(Whitebox.invokeConstructor(DNSKEYRecord.class)); when(emptyDnskeyRecord.getAlgorithm()).thenReturn(-1); whenNew(DNSKEYRecord.class).withNoArguments().thenReturn(emptyDnskeyRecord); } @Test public void testDnskeyPrimeResponseWithWeirdHashIsBad() throws Exception { spy(DNSSEC.class); doReturn(new byte[]{1, 2, 3}).when(DNSSEC.class, "generateDSDigest", any(DNSKEYRecord.class), anyInt()); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:.:dnskey.no_ds_match", getReason(response)); } @Test public void testDsPrimeResponseWithEmptyAnswerIsBad() throws IOException { Message message = new Message(); message.addRecord(Record.newRecord(Name.fromString("ch."), Type.DS, DClass.IN), Section.QUESTION); add("ch./DS", message); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:ch.:failed.ds.nonsec:ch.", getReason(response)); } @Test public void testDsPrimeResponseWithNxDomainForTld() throws IOException { Message message = new Message(); message.addRecord(Record.newRecord(Name.fromString("ch."), Type.DS, DClass.IN), Section.QUESTION); message.getHeader().setRcode(Rcode.NXDOMAIN); add("ch./DS", message); Message response = resolver.send(createMessage("www.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:ch.:failed.ds.nonsec:ch.", getReason(response)); } @Test public void testDsNoDataWhenNsecIsFromChildApex() throws IOException { Message nsec = resolver.send(createMessage("1.sub.ingotronic.ch./NSEC")); Record delegationNsec = null; Record delegationNsecSig = null; for (RRset set : nsec.getSectionRRsets(Section.AUTHORITY)) { if (set.getName().toString().startsWith("sub.ingotronic.ch") && set.getType() == Type.NSEC) { delegationNsec = set.first(); delegationNsecSig = (Record)set.sigs().next(); break; } } Message m = createMessage("sub.ingotronic.ch./DS"); m.getHeader().setRcode(Rcode.NOERROR); m.addRecord(delegationNsec, Section.AUTHORITY); m.addRecord(delegationNsecSig, Section.AUTHORITY); add("sub.ingotronic.ch./DS", m); Message response = resolver.send(createMessage("sub.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus.badkey:sub.ingotronic.ch.:failed.ds.nsec", getReason(response)); } @Test public void testDsNoDataWhenNsecOnEntIsBad() throws IOException { Message m = resolver.send(createMessage("e.ingotronic.ch./DS")); Message message = messageFromString(m.toString().replaceAll("(.*\\sRRSIG\\sNSEC\\s(\\d+\\s+){6}.*\\.)(.*)", "$1 YXNkZg==")); add("e.ingotronic.ch./DS", message); Message response = resolver.send(createMessage("a.e.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus:failed.ds.nsec.ent", getReason(response)); } @Test public void testDsNoDataWhenOnInsecureDelegationWithWrongNsec() throws IOException { Message nsec = resolver.send(createMessage("alias.ingotronic.ch./NSEC")); Record delegationNsec = null; Record delegationNsecSig = null; for (RRset set : nsec.getSectionRRsets(Section.ANSWER)) { if (set.getName().toString().startsWith("alias.ingotronic.ch") && set.getType() == Type.NSEC) { delegationNsec = set.first(); delegationNsecSig = (Record)set.sigs().next(); break; } } Message m = createMessage("unsigned.ingotronic.ch./DS"); m.getHeader().setRcode(Rcode.NOERROR); m.addRecord(delegationNsec, Section.AUTHORITY); m.addRecord(delegationNsecSig, Section.AUTHORITY); add("unsigned.ingotronic.ch./DS", m); Message response = resolver.send(createMessage("www.unsigned.ingotronic.ch./A")); assertFalse("AD flag must not be set", response.getHeader().getFlag(Flags.AD)); assertEquals(Rcode.SERVFAIL, response.getRcode()); assertEquals("validate.bogus:failed.ds.unknown", getReason(response)); } }