diff --git a/mk/rpki.mk b/mk/rpki.mk index 2df45fdd..9ebf4974 100644 --- a/mk/rpki.mk +++ b/mk/rpki.mk @@ -1,3 +1,179 @@ +###################################################################### +## helpers to generate keys, certs, and roas +###################################################################### +set_vars= \ + target='$@' && \ + top_srcdir='${top_srcdir}' && \ + target_dir=$${target%/*} +boilerplate= \ + ${set_vars} && \ + mkdir -p "$${target_dir}" && \ + TEST_LOG_NAME=$${target\#\#*/} \ + TEST_LOG_DIR=$${target_dir} \ + STRICT_CHECKS=0 \ + ${LOG_COMPILER} +create_object = ${boilerplate} bin/rpki-object/create_object/create_object +gen_key = ${boilerplate} bin/rpki-object/gen_key +KEYS = ${CERTS:.cer=.key} +CERTS = +ROAS = +${KEYS}: bin/rpki-object/gen_key + ${gen_key} "$${target}" 2048 +${CERTS}: bin/rpki-object/create_object/create_object + ${create_object} \ + -f "$${top_srcdir}"/"$${target%.cer}".options \ + CERT \ + serial=1 \ + notbefore=000101010101Z \ + notafter=21001231235959Z \ + crldp=rsync://invalid/invalid.crl \ + outputfilename="$${target}" +${ROAS}: bin/rpki-object/create_object/create_object + ${set_vars} && \ + eeopt=$${target%.roa}.options && \ + eekey=$$(sed -n -e 's/^subjkeyfile=\(.*\)/\1/p' \ + "$${top_srcdir}"/"$${eeopt}") && \ + ${create_object} \ + -f "$${top_srcdir}"/"$${target}".options \ + ROA \ + eecertlocation="$${target%.roa}".cer \ + eekeylocation="$${eekey}" \ + outputfilename="$${target}" +check_DATA += ${CERTS} ${KEYS} ${ROAS} +EXTRA_DIST += ${CERTS:.cer=.options} ${ROAS:=.options} +CLEANFILES += ${CERTS} ${KEYS} ${ROAS} + +###################################################################### +## roa-ee-munge test +###################################################################### +# see ticket #28 +TESTS += \ + tests/subsystem/roa-ee-munge/roa-ee-munge.tap +check_SCRIPTS += \ + tests/subsystem/roa-ee-munge/roa-ee-munge.tap +tests/subsystem/roa-ee-munge/roa-ee-munge.tap: \ + tests/subsystem/roa-ee-munge/roa-ee-munge.tap.in +MK_SUBST_FILES_EXEC += \ + tests/subsystem/roa-ee-munge/roa-ee-munge.tap +CERTS += \ + tests/subsystem/roa-ee-munge/ta-good.cer \ + tests/subsystem/roa-ee-munge/ta-bad.cer \ + tests/subsystem/roa-ee-munge/ee-good.cer \ + tests/subsystem/roa-ee-munge/ee-bad.cer +ROAS += \ + tests/subsystem/roa-ee-munge/ee-good.roa \ + tests/subsystem/roa-ee-munge/ee-bad.roa +tests/subsystem/roa-ee-munge/ta-good.cer: \ + tests/subsystem/roa-ee-munge/ta-good.options \ + tests/subsystem/roa-ee-munge/ta-good.key +tests/subsystem/roa-ee-munge/ta-bad.cer: \ + tests/subsystem/roa-ee-munge/ta-bad.options \ + tests/subsystem/roa-ee-munge/ta-bad.key +tests/subsystem/roa-ee-munge/ee-good.cer: \ + tests/subsystem/roa-ee-munge/ee-good.options \ + tests/subsystem/roa-ee-munge/ee-good.key +tests/subsystem/roa-ee-munge/ee-good.roa: \ + tests/subsystem/roa-ee-munge/ee-good.cer \ + tests/subsystem/roa-ee-munge/ee-good.key \ + tests/subsystem/roa-ee-munge/ee-good.roa.options +tests/subsystem/roa-ee-munge/ee-bad.cer: \ + tests/subsystem/roa-ee-munge/ee-bad.options \ + tests/subsystem/roa-ee-munge/ee-bad.key +tests/subsystem/roa-ee-munge/ee-bad.roa: \ + tests/subsystem/roa-ee-munge/ee-bad.cer \ + tests/subsystem/roa-ee-munge/ee-bad.key \ + tests/subsystem/roa-ee-munge/ee-bad.roa.options +clean-local: clean-roa-ee-munge +clean-roa-ee-munge: + rm -rf tests/subsystem/roa-ee-munge/roa-ee-munge.tap.cache + +###################################################################### +## evil twin tests +###################################################################### +EVIL_TWIN_TESTS = \ + tests/subsystem/evil-twin/evil-twin-ca-invalid-1.tap \ + tests/subsystem/evil-twin/evil-twin-ca-invalid-2.tap \ + tests/subsystem/evil-twin/evil-twin-ca-valid-1.tap \ + tests/subsystem/evil-twin/evil-twin-ca-valid-2.tap \ + tests/subsystem/evil-twin/evil-twin-ee-invalid.tap \ + tests/subsystem/evil-twin/evil-twin-ee-valid.tap +TESTS += ${EVIL_TWIN_TESTS} +EXTRA_DIST += ${EVIL_TWIN_TESTS} +check_SCRIPTS += \ + tests/subsystem/evil-twin/evil-twin-common.sh +tests/subsystem/evil-twin/evil-twin-common.sh: \ + tests/subsystem/evil-twin/evil-twin-common.sh.in +MK_SUBST_FILES += \ + tests/subsystem/evil-twin/evil-twin-common.sh +CERTS += \ + tests/subsystem/evil-twin/ta-good.cer \ + tests/subsystem/evil-twin/ta-evil.cer \ + tests/subsystem/evil-twin/ca-good.cer \ + tests/subsystem/evil-twin/ca-evil-invalid.cer \ + tests/subsystem/evil-twin/ca-evil-valid.cer \ + tests/subsystem/evil-twin/test1-ca.cer \ + tests/subsystem/evil-twin/test2-ee.cer \ + tests/subsystem/evil-twin/ee-good.cer \ + tests/subsystem/evil-twin/ee-evil-invalid.cer \ + tests/subsystem/evil-twin/ee-evil-valid.cer +ROAS += \ + tests/subsystem/evil-twin/test2-ee.roa \ + tests/subsystem/evil-twin/ee-good.roa \ + tests/subsystem/evil-twin/ee-evil-invalid.roa \ + tests/subsystem/evil-twin/ee-evil-valid.roa +tests/subsystem/evil-twin/ta-good.cer: \ + tests/subsystem/evil-twin/ta-good.options \ + tests/subsystem/evil-twin/ta-good.key +tests/subsystem/evil-twin/ta-evil.cer: \ + tests/subsystem/evil-twin/ta-evil.options \ + tests/subsystem/evil-twin/ta-evil.key +tests/subsystem/evil-twin/ca-good.cer: \ + tests/subsystem/evil-twin/ca-good.options \ + tests/subsystem/evil-twin/ca-good.key +tests/subsystem/evil-twin/ca-evil-invalid.cer: \ + tests/subsystem/evil-twin/ca-evil-invalid.options \ + tests/subsystem/evil-twin/ca-evil-invalid.key +tests/subsystem/evil-twin/ca-evil-valid.cer: \ + tests/subsystem/evil-twin/ca-evil-valid.options \ + tests/subsystem/evil-twin/ca-evil-valid.key +tests/subsystem/evil-twin/test1-ca.cer: \ + tests/subsystem/evil-twin/test1-ca.options \ + tests/subsystem/evil-twin/test1-ca.key +tests/subsystem/evil-twin/test2-ee.cer: \ + tests/subsystem/evil-twin/test2-ee.options \ + tests/subsystem/evil-twin/test2-ee.key +tests/subsystem/evil-twin/test2-ee.roa: \ + tests/subsystem/evil-twin/test2-ee.cer \ + tests/subsystem/evil-twin/test2-ee.key \ + tests/subsystem/evil-twin/test2-ee.roa.options +tests/subsystem/evil-twin/ee-good.cer: \ + tests/subsystem/evil-twin/ee-good.options \ + tests/subsystem/evil-twin/ee-good.key +tests/subsystem/evil-twin/ee-good.roa: \ + tests/subsystem/evil-twin/ee-good.cer \ + tests/subsystem/evil-twin/ee-good.key \ + tests/subsystem/evil-twin/ee-good.roa.options +tests/subsystem/evil-twin/ee-evil-invalid.cer: \ + tests/subsystem/evil-twin/ee-evil-invalid.options \ + tests/subsystem/evil-twin/ee-evil-invalid.key +tests/subsystem/evil-twin/ee-evil-invalid.roa: \ + tests/subsystem/evil-twin/ee-evil-invalid.cer \ + tests/subsystem/evil-twin/ee-evil-invalid.key \ + tests/subsystem/evil-twin/ee-evil-invalid.roa.options +tests/subsystem/evil-twin/ee-evil-valid.cer: \ + tests/subsystem/evil-twin/ee-evil-valid.options \ + tests/subsystem/evil-twin/ee-evil-valid.key +tests/subsystem/evil-twin/ee-evil-valid.roa: \ + tests/subsystem/evil-twin/ee-evil-valid.cer \ + tests/subsystem/evil-twin/ee-evil-valid.key \ + tests/subsystem/evil-twin/ee-evil-valid.roa.options +clean-local: clean-evil-twin +clean-evil-twin: + rm -rf ${EVIL_TWIN_TESTS:=.cache} + +###################################################################### +## chaser +###################################################################### pkglibexec_PROGRAMS += bin/rpki/chaser bin_rpki_chaser_LDADD = \ diff --git a/mk/tests-environment.mk b/mk/tests-environment.mk index 34dfb5e2..068b8c39 100644 --- a/mk/tests-environment.mk +++ b/mk/tests-environment.mk @@ -62,3 +62,5 @@ cat-logs: echo; \ fi; \ done + +EXTRA_DIST += tests/util.sh diff --git a/tests/subsystem/evil-twin/.gitignore b/tests/subsystem/evil-twin/.gitignore new file mode 100644 index 00000000..e1736a71 --- /dev/null +++ b/tests/subsystem/evil-twin/.gitignore @@ -0,0 +1,5 @@ +/*.cache/ +/*.cer +/*.key +/*.roa +/evil-twin-common.sh diff --git a/tests/subsystem/evil-twin/README.txt b/tests/subsystem/evil-twin/README.txt new file mode 100644 index 00000000..64b6d80d --- /dev/null +++ b/tests/subsystem/evil-twin/README.txt @@ -0,0 +1,21 @@ +This directory contains tests for vulnerabilities to the "evil twin" +attack. + +The goal of the evil twin attack is to make a good object look bad. +The malicious CA signs and publishes a certificate that reuses the +public key, subject, and SKI from a victim certificate. This new +certificate (the "evil twin" certificate) is either: + + * invalid because it uses resources not held by the malicious CA, or + + * valid but not a valid parent of the objects signed by the victim + certificate because the objects signed by the victim certificate + have resources outside of the evil twin certificate. + +Either way, if the RP software is buggy and considers the evil twin to +be the parent of objects that were actually signed by the victim +(because the subject, SKI, and public keys match), those good objects +would be incorrectly invalidated. + +The test scripts in this directory use different toy hierarchies to +cover a wide range of scenarios. diff --git a/tests/subsystem/evil-twin/ca-evil-invalid.options b/tests/subsystem/evil-twin/ca-evil-invalid.options new file mode 100644 index 00000000..428f5c8b --- /dev/null +++ b/tests/subsystem/evil-twin/ca-evil-invalid.options @@ -0,0 +1,12 @@ +type=CA +issuer=ta-evil +subject=ca-good +aia=rsync://invalid/ +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=0.0.0.0/16 +ipv6=::/32 +as=1-63 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-evil.cer +parentkeyfile=tests/subsystem/evil-twin/ta-evil.key +subjkeyfile=tests/subsystem/evil-twin/ca-good.key diff --git a/tests/subsystem/evil-twin/ca-evil-valid.options b/tests/subsystem/evil-twin/ca-evil-valid.options new file mode 100644 index 00000000..fc53577f --- /dev/null +++ b/tests/subsystem/evil-twin/ca-evil-valid.options @@ -0,0 +1,12 @@ +type=CA +issuer=ta-evil +subject=ca-good +aia=rsync://invalid/ +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=1.0.0.0/16 +ipv6=1::/32 +as=128-191 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-evil.cer +parentkeyfile=tests/subsystem/evil-twin/ta-evil.key +subjkeyfile=tests/subsystem/evil-twin/ca-good.key diff --git a/tests/subsystem/evil-twin/ca-good.options b/tests/subsystem/evil-twin/ca-good.options new file mode 100644 index 00000000..17aa40c6 --- /dev/null +++ b/tests/subsystem/evil-twin/ca-good.options @@ -0,0 +1,12 @@ +type=CA +issuer=ta-good +subject=ca-good +aia=rsync://invalid/ +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=0.0.0.0/16 +ipv6=::/32 +as=1-63 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-good.cer +parentkeyfile=tests/subsystem/evil-twin/ta-good.key +subjkeyfile=tests/subsystem/evil-twin/ca-good.key diff --git a/tests/subsystem/evil-twin/ee-evil-invalid.options b/tests/subsystem/evil-twin/ee-evil-invalid.options new file mode 100644 index 00000000..e9094d5e --- /dev/null +++ b/tests/subsystem/evil-twin/ee-evil-invalid.options @@ -0,0 +1,12 @@ +type=EE +issuer=ta-evil +subject=ee-good +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-evil.cer +parentkeyfile=tests/subsystem/evil-twin/ta-evil.key +subjkeyfile=tests/subsystem/evil-twin/ee-good.key diff --git a/tests/subsystem/evil-twin/ee-evil-invalid.roa.options b/tests/subsystem/evil-twin/ee-evil-invalid.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/evil-twin/ee-evil-invalid.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/evil-twin/ee-evil-valid.options b/tests/subsystem/evil-twin/ee-evil-valid.options new file mode 100644 index 00000000..597d5315 --- /dev/null +++ b/tests/subsystem/evil-twin/ee-evil-valid.options @@ -0,0 +1,12 @@ +type=EE +issuer=ta-evil +subject=ee-good +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=1.0.0.0/24 +ipv6=1::/48 +as=128-159 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-evil.cer +parentkeyfile=tests/subsystem/evil-twin/ta-evil.key +subjkeyfile=tests/subsystem/evil-twin/ee-good.key diff --git a/tests/subsystem/evil-twin/ee-evil-valid.roa.options b/tests/subsystem/evil-twin/ee-evil-valid.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/evil-twin/ee-evil-valid.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/evil-twin/ee-good.options b/tests/subsystem/evil-twin/ee-good.options new file mode 100644 index 00000000..1a01dbc7 --- /dev/null +++ b/tests/subsystem/evil-twin/ee-good.options @@ -0,0 +1,12 @@ +type=EE +issuer=ta-good +subject=ee-good +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ta-good.cer +parentkeyfile=tests/subsystem/evil-twin/ta-good.key +subjkeyfile=tests/subsystem/evil-twin/ee-good.key diff --git a/tests/subsystem/evil-twin/ee-good.roa.options b/tests/subsystem/evil-twin/ee-good.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/evil-twin/ee-good.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/evil-twin/evil-twin-ca-invalid-1.tap b/tests/subsystem/evil-twin/evil-twin-ca-invalid-1.tap new file mode 100755 index 00000000..1f5a7977 --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ca-invalid-1.tap @@ -0,0 +1,40 @@ +#!/bin/sh + +# This scenario uses the following forest: +# +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good CA (valid) Evil CA (invalid, this is the "evil twin" of Good CA) +# IPv4: 0.0.0.0/16 IPv4: 0.0.0.0/16 (outside of issuer resoures) +# IPv6: ::/32 IPv6: ::/32 (outside of issuer resources) +# AS: 1-63 AS: 1-63 (outside of issuer resources) +# | +# | +# Test1 CA (valid) +# IPv4: 0.0.0.0/24 +# IPv6: ::/48 +# AS: 1-31 +# +# The five objects above are added to the database one at a time. To +# ensure that the order in which the objects are added does not affect +# the outcome, all permutations are tried (the database is scrubbed +# between permutations). The result looks like this: +# +# 1. ta-good.cer ta-evil.cer ca-good.cer ca-evil.cer test1-ca.cer +# 2. ta-good.cer ta-evil.cer ca-good.cer test1-ca.cer ca-evil.cer +# 3. ta-good.cer ta-evil.cer ca-evil.cer ca-good.cer test1-ca.cer +# 4. ta-good.cer ta-evil.cer ca-evil.cer test1-ca.cer ca-good.cer +# ... +# 120. test1-ca.cer ca-evil.cer ca-good.cer ta-evil.cer ta-good.cer + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ca-good.cer ca-evil-invalid.cer test1-ca.cer" +exp="ta-good.cer ta-evil.cer ca-good.cer test1-ca.cer" + +run_tests "${files}" "${exp}" diff --git a/tests/subsystem/evil-twin/evil-twin-ca-invalid-2.tap b/tests/subsystem/evil-twin/evil-twin-ca-invalid-2.tap new file mode 100755 index 00000000..63a145fa --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ca-invalid-2.tap @@ -0,0 +1,32 @@ +#!/bin/sh + +# This scenario is the same as evil-twin-ca-invalid-1 except the Test1 +# CA certificate is replaced by a ROA: +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good CA (valid) Evil CA (invalid, this is the "evil twin" of Good CA) +# IPv4: 0.0.0.0/16 IPv4: 0.0.0.0/16 (outside of issuer resoures) +# IPv6: ::/32 IPv6: ::/32 (outside of issuer resources) +# AS: 1-63 AS: 1-63 (outside of issuer resources) +# | +# | +# Test2 ROA (valid) +# IPv4: 0.0.0.0/25 +# IPv6: ::/64 +# AS: 1 +# via Test2 EE (valid): +# IPv4: 0.0.0.0/24 +# IPv6: ::/48 +# AS: 1-31 + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ca-good.cer ca-evil-invalid.cer test2-ee.roa" +exp="ta-good.cer ta-evil.cer ca-good.cer test2-ee.roa test2-ee.roa.cer" + +run_tests "${files}" "${exp}" diff --git a/tests/subsystem/evil-twin/evil-twin-ca-valid-1.tap b/tests/subsystem/evil-twin/evil-twin-ca-valid-1.tap new file mode 100755 index 00000000..e3340593 --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ca-valid-1.tap @@ -0,0 +1,48 @@ +#!/bin/sh + +# This scenario is the same as evil-twin-ca-invalid-1 except the Evil +# CA certificate has its resources altered to be valid: +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good CA (valid) Evil CA (valid, this is the "evil twin" of Good CA) +# IPv4: 0.0.0.0/16 IPv4: 1.0.0.0/16 (modified resources to be valid) +# IPv6: ::/32 IPv6: 1::/32 (modified resources to be valid) +# AS: 1-63 AS: 128-191 (modified resources to be valid) +# | +# | +# Test1 CA (valid) +# IPv4: 0.0.0.0/24 +# IPv6: ::/48 +# AS: 1-31 + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ca-good.cer ca-evil-valid.cer test1-ca.cer" +exp=${files} + +# override testcase() to set xfail for cases that are known to fail +testcase() { + pass=true + # if the evil hierarchy is completely added before the test CA, or + # if the entire evil hierarchy and the test CA are added before + # the good hierarchy is completely added, then it will fail. + # stated another way, if the good hierarchy and test CA are added + # before the bad hierarchy is completely added, then it will pass. + case $(printf " %s " $4) in + *evil*evil*" test1-ca.cer "*) pass=false;; + *evil*" test1-ca.cer "*evil*good*) pass=false;; + *" test1-ca.cer "*evil*evil*good*) pass=false;; + esac + if "${pass}"; then + t4s_testcase "$@" + else + t4s_testcase --xfail "see ticket #29" "$@" + fi +} + +run_tests "${files}" "${exp}" diff --git a/tests/subsystem/evil-twin/evil-twin-ca-valid-2.tap b/tests/subsystem/evil-twin/evil-twin-ca-valid-2.tap new file mode 100755 index 00000000..21858deb --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ca-valid-2.tap @@ -0,0 +1,52 @@ +#!/bin/sh + +# This scenario is the same as evil-twin-ca-valid-1 except the Test1 +# CA certificate is replaced with a ROA as in evil-twin-ca-invalid-2: +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good CA (valid) Evil CA (valid, this is the "evil twin" of Good CA) +# IPv4: 0.0.0.0/16 IPv4: 1.0.0.0/16 (modified resources to be valid) +# IPv6: ::/32 IPv6: 1::/32 (modified resources to be valid) +# AS: 1-63 AS: 128-191 (modified resources to be valid) +# | +# | +# Test2 ROA (valid) +# IPv4: 0.0.0.0/25 +# IPv6: ::/64 +# AS: 1 +# via Test2 EE (valid): +# IPv4: 0.0.0.0/24 +# IPv6: ::/48 +# AS: 1-31 + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ca-good.cer ca-evil-valid.cer test2-ee.roa" +exp=${files}" test2-ee.roa.cer" + +# override testcase() to set xfail for cases that are known to fail +testcase() { + pass=true + # if the evil hierarchy is completely added before the test ROA, + # or if the entire evil hierarchy and the test ROA are added + # before the good hierarchy is completely added, then it will fail + # stated another way, if the good hierarchy and test ROA are added + # before the bad hierarchy is completely added, then it will pass. + case $(printf " %s " $4) in + *evil*evil*" test2-ee.roa "*) pass=false;; + *evil*" test2-ee.roa "*evil*good*) pass=false;; + *" test2-ee.roa "*evil*evil*good*) pass=false;; + esac + if "${pass}"; then + t4s_testcase "$@" + else + t4s_testcase --xfail "see ticket #29" "$@" + fi +} + +run_tests "${files}" "${exp}" diff --git a/tests/subsystem/evil-twin/evil-twin-common.sh.in b/tests/subsystem/evil-twin/evil-twin-common.sh.in new file mode 100644 index 00000000..5b39bd6b --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-common.sh.in @@ -0,0 +1,13 @@ +@SETUP_ENVIRONMENT@ + +t4s_setup + +u=${TESTS_TOP_SRCDIR}/tests/util.sh +. "${u}" || t4s_bailout "unable to load ${u}" + +cd "${TESTS_BUILDDIR}" || t4s_bailout "unable to cd to ${TESTS_BUILDDIR}" + +run_tests() { + test_perms "${0##*/}".cache "$@" + t4s_done +} diff --git a/tests/subsystem/evil-twin/evil-twin-ee-invalid.tap b/tests/subsystem/evil-twin/evil-twin-ee-invalid.tap new file mode 100755 index 00000000..febea937 --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ee-invalid.tap @@ -0,0 +1,35 @@ +#!/bin/sh + +# This scenario tests evil twin EE certificates rather than evil twin +# CA certificates: +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good ROA (valid) Evil ROA (OK sig & resources; invalid from bad EE) +# IPv4: 0.0.0.0/25 IPv4: 0.0.0.0/25 +# IPv6: ::/64 IPv6: ::/64 +# AS: 1 AS: 1 +# via Good EE (valid): via Evil EE (invalid, this is the "evil twin"): +# IPv4: 0.0.0.0/24 IPv4: 0.0.0.0/24 (outside of issuer resources) +# IPv6: ::/48 IPv6: ::/48 (outside of issuer resources) +# AS: 1-31 AS: 1-31 (outside of issuer resources) + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ee-good.roa ee-evil-invalid.roa" +exp="ta-good.cer ta-evil.cer ee-good.roa ee-good.roa.cer" +# This is an alternative accepted result, though it shouldn't be. +# There's a bug that causes ee-evil-invalid.roa to be "accepted" +# depending on the file add order. See tests/subsystem/roa-ee-munge +# and ticket #28. +exp2="ta-good.cer ta-evil.cer ee-evil-invalid.roa ee-good.roa.cer" +# This is another alternative that shouldn't be accepted. It's the +# same bug as above, except for some reason both ROAs are being +# accepted instead of just one or the other. +exp3="ta-good.cer ta-evil.cer ee-evil-invalid.roa ee-good.roa.cer ee-good.roa" + +run_tests "${files}" "${exp}" "${exp2}" "${exp3}" diff --git a/tests/subsystem/evil-twin/evil-twin-ee-valid.tap b/tests/subsystem/evil-twin/evil-twin-ee-valid.tap new file mode 100755 index 00000000..ba14ff02 --- /dev/null +++ b/tests/subsystem/evil-twin/evil-twin-ee-valid.tap @@ -0,0 +1,27 @@ +#!/bin/sh + +# This scenario is like evil-twin-ee-invalid except the resources in +# Evil EE have been modified so that the cert validates (the ROA must +# be left alone because the signature can't be forged): +# +# Good TA (valid) Evil TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good ROA (valid) Evil ROA (invalid due to resources in EE cert) +# IPv4: 0.0.0.0/25 IPv4: 0.0.0.0/25 +# IPv6: ::/64 IPv6: ::/64 +# AS: 1 AS: 1 +# via Good EE (valid): via Evil EE (valid, this is the "evil twin"): +# IPv4: 0.0.0.0/24 IPv4: 1.0.0.0/24 (modified resources to be valid) +# IPv6: ::/48 IPv6: 1::/48 (modified resources to be valid) +# AS: 1-31 AS: 128-159 (modified resources to be valid) + +. "${TESTS_BUILDDIR}"/evil-twin-common.sh || exit 1 + +files="ta-good.cer ta-evil.cer ee-good.roa ee-evil-valid.roa" +exp="ta-good.cer ta-evil.cer ee-good.roa ee-good.roa.cer" + +run_tests "${files}" "${exp}" diff --git a/tests/subsystem/evil-twin/ta-evil.options b/tests/subsystem/evil-twin/ta-evil.options new file mode 100644 index 00000000..15ab8bc8 --- /dev/null +++ b/tests/subsystem/evil-twin/ta-evil.options @@ -0,0 +1,9 @@ +type=CA +issuer=ta-evil +subject=ta-evil +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=1.0.0.0/8 +ipv6=1::/16 +as=128-255 +selfsigned=true +subjkeyfile=tests/subsystem/evil-twin/ta-evil.key diff --git a/tests/subsystem/evil-twin/ta-good.options b/tests/subsystem/evil-twin/ta-good.options new file mode 100644 index 00000000..aad272fc --- /dev/null +++ b/tests/subsystem/evil-twin/ta-good.options @@ -0,0 +1,9 @@ +type=CA +issuer=ta-good +subject=ta-good +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=0.0.0.0/8 +ipv6=::/16 +as=1-127 +selfsigned=true +subjkeyfile=tests/subsystem/evil-twin/ta-good.key diff --git a/tests/subsystem/evil-twin/test1-ca.options b/tests/subsystem/evil-twin/test1-ca.options new file mode 100644 index 00000000..c65b2796 --- /dev/null +++ b/tests/subsystem/evil-twin/test1-ca.options @@ -0,0 +1,12 @@ +type=CA +issuer=ca-good +subject=test1-ca +aia=rsync://invalid/ +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ca-good.cer +parentkeyfile=tests/subsystem/evil-twin/ca-good.key +subjkeyfile=tests/subsystem/evil-twin/test1-ca.key diff --git a/tests/subsystem/evil-twin/test2-ee.options b/tests/subsystem/evil-twin/test2-ee.options new file mode 100644 index 00000000..3483ded7 --- /dev/null +++ b/tests/subsystem/evil-twin/test2-ee.options @@ -0,0 +1,12 @@ +type=EE +issuer=ca-good +subject=test2-ee +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/evil-twin/ca-good.cer +parentkeyfile=tests/subsystem/evil-twin/ca-good.key +subjkeyfile=tests/subsystem/evil-twin/test2-ee.key diff --git a/tests/subsystem/evil-twin/test2-ee.roa.options b/tests/subsystem/evil-twin/test2-ee.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/evil-twin/test2-ee.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/roa-ee-munge/.gitignore b/tests/subsystem/roa-ee-munge/.gitignore new file mode 100644 index 00000000..bfb8f738 --- /dev/null +++ b/tests/subsystem/roa-ee-munge/.gitignore @@ -0,0 +1,5 @@ +/*.cache/ +/*.cer +/*.key +/*.roa +/roa-ee-munge.tap diff --git a/tests/subsystem/roa-ee-munge/ee-bad.options b/tests/subsystem/roa-ee-munge/ee-bad.options new file mode 100644 index 00000000..15c0333b --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ee-bad.options @@ -0,0 +1,12 @@ +type=EE +issuer=ta-bad +subject=ee-good +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/roa-ee-munge/ta-bad.cer +parentkeyfile=tests/subsystem/roa-ee-munge/ta-bad.key +subjkeyfile=tests/subsystem/roa-ee-munge/ee-good.key diff --git a/tests/subsystem/roa-ee-munge/ee-bad.roa.options b/tests/subsystem/roa-ee-munge/ee-bad.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ee-bad.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/roa-ee-munge/ee-good.options b/tests/subsystem/roa-ee-munge/ee-good.options new file mode 100644 index 00000000..85d6b38e --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ee-good.options @@ -0,0 +1,12 @@ +type=EE +issuer=ta-good +subject=ee-good +aia=rsync://invalid/ +sia=s:rsync://invalid/ +ipv4=0.0.0.0/24 +ipv6=::/48 +as=1-31 +selfsigned=false +parentcertfile=tests/subsystem/roa-ee-munge/ta-good.cer +parentkeyfile=tests/subsystem/roa-ee-munge/ta-good.key +subjkeyfile=tests/subsystem/roa-ee-munge/ee-good.key diff --git a/tests/subsystem/roa-ee-munge/ee-good.roa.options b/tests/subsystem/roa-ee-munge/ee-good.roa.options new file mode 100644 index 00000000..aa6c453d --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ee-good.roa.options @@ -0,0 +1,3 @@ +roaipv4=0.0.0.0/25 +roaipv6=::/64 +asid=1 diff --git a/tests/subsystem/roa-ee-munge/roa-ee-munge.tap.in b/tests/subsystem/roa-ee-munge/roa-ee-munge.tap.in new file mode 100755 index 00000000..2d6866c9 --- /dev/null +++ b/tests/subsystem/roa-ee-munge/roa-ee-munge.tap.in @@ -0,0 +1,52 @@ +#!/bin/sh + +# This script tests RPSTIR's reaction to the following situation: +# +# 1. empty the database +# 2. take a valid ROA, replace its EE with an "evil twin" +# (specifically, take the EE cert and re-sign it using a +# different CA that doesn't hold the resources mentioned in the +# EE cert) +# 3. add the modified ROA +# 4. add the original ROA +# 5. add the CA certs +# +# In this scenario the modified ROA and its embedded cert should not +# be accepted, even though the non-EE part of the modified ROA is +# identical to the non-EE part of the original ROA. Only the original +# ROA, its embedded cert, and the CAs should be reported as accepted. +# +# The object hierarchy in this scenario looks like this: +# +# Good TA (valid) Bad TA (valid) +# IPv4: 0.0.0.0/8 IPv4: 1.0.0.0/8 +# IPv6: ::/16 IPv6: 1::/16 +# AS: 1-127 AS: 128-255 +# | | +# | | +# Good ROA (valid) Bad ROA (OK sig & resources; invalid from bad EE) +# IPv4: 0.0.0.0/25 IPv4: 0.0.0.0/25 +# IPv6: ::/64 IPv6: ::/64 +# AS: 1 AS: 1 +# via Good EE (valid): via Bad EE (invalid, this is the "evil twin"): +# IPv4: 0.0.0.0/24 IPv4: 0.0.0.0/24 (outside of issuer resources) +# IPv6: ::/48 IPv6: ::/48 (outside of issuer resources) +# AS: 1-31 AS: 1-31 (outside of issuer resources) + +@SETUP_ENVIRONMENT@ + +t4s_setup + +u=${TESTS_TOP_SRCDIR}/tests/util.sh +. "${u}" || t4s_bailout "unable to load ${u}" + +cd "${TESTS_BUILDDIR}" || t4s_bailout "unable to cd to ${TESTS_BUILDDIR}" + +t4s_testcase --xfail "see ticket #28" "roa-ee-munge" ' + reset_add_check \ + "$1" \ + "ee-bad.roa ee-good.roa ta-good.cer ta-bad.cer" \ + "ee-good.roa ee-good.roa.cer ta-good.cer ta-bad.cer" +' "${0##*/}".cache + +t4s_done diff --git a/tests/subsystem/roa-ee-munge/ta-bad.options b/tests/subsystem/roa-ee-munge/ta-bad.options new file mode 100644 index 00000000..d9531743 --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ta-bad.options @@ -0,0 +1,9 @@ +type=CA +issuer=ta-bad +subject=ta-bad +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=1.0.0.0/8 +ipv6=1::/16 +as=128-255 +selfsigned=true +subjkeyfile=tests/subsystem/roa-ee-munge/ta-bad.key diff --git a/tests/subsystem/roa-ee-munge/ta-good.options b/tests/subsystem/roa-ee-munge/ta-good.options new file mode 100644 index 00000000..84f86609 --- /dev/null +++ b/tests/subsystem/roa-ee-munge/ta-good.options @@ -0,0 +1,9 @@ +type=CA +issuer=ta-good +subject=ta-good +sia=r:rsync://invalid/,m:rsync://invalid/invalid.mft +ipv4=0.0.0.0/8 +ipv6=::/16 +as=1-127 +selfsigned=true +subjkeyfile=tests/subsystem/roa-ee-munge/ta-good.key diff --git a/tests/util.sh b/tests/util.sh new file mode 100644 index 00000000..0cba3f6c --- /dev/null +++ b/tests/util.sh @@ -0,0 +1,192 @@ +# POSIX shell dot script with useful helper functions for various test +# scripts +# +# Variables used throughout these helper functions: +# * pfx_fn: short filename-safe (no whitespace) string to add to +# generated file names + +# called by the helper functions below when something goes wrong and +# the test can't continue. it uses t4s_bailout by default but you can +# redefine the function if you are not using tap4sh. +# +# args: +# * 1: a description of the problem +# +abort_test() { + t4s_bailout "$@" +} + +# wrapper around t4s_testcase used to run test cases. you can +# redefine this if tap4sh is not being used +testcase() { + t4s_testcase "$@" +} + +# wrapper around t4s_log used to log messages in test cases. you can +# redefine this if tap4sh is not being used. +testcase_log() { + t4s_log "$@" +} + +# Output each permutation of the given arguments, one per line. Each +# item is separated by a space. +# +# For example, 'permutations a b c' outputs: +# +# a b c +# a c b +# b a c +# b c a +# c a b +# c b a +# +# Note that this is run in a subshell to protect the caller's +# environment (and so that recursive calls don't mess up the caller's +# variables). +# +# args: +# * 1 through n: items to permute. these MUST NOT contain any IFS +# characters (whitespace) +# +permutations() ( + [ "$#" -gt 0 ] || { pecho ""; return 0; } + i=0 + for x in "$@"; do + i=$((i+1)) + j=0 + args= + for y in "$@"; do + j=$((j+1)) + [ "${i}" -ne "${j}" ] || continue + args=${args}\ ${y} + done + eval "permutations${args}" | while IFS= read -r line; do + pecho "${x}${line:+ ${line}}" + done + done +) + +# scrub the database and the cache dir to clean out data from a +# previous run, then copy the named files to the cache directory +# +# args: +# * 1: cache directory +# * 2 through n: files to copy +# +reset_state() { + ( + testcase_log "resetting state..." + cache_dir=$1; shift + try rm -rf "${cache_dir}" + try mkdir -p "${cache_dir}" + try run "${pfx_fn}rcli-x-t" rcli -x -t "${cache_dir}" -y + try cp "$@" "${cache_dir}" + ) || abort_test "unable to reset test state" +} + +# add all given files to the database in the given order. if any add +# fails an error message will be printed (via try) but the script +# won't exit -- it will continue adding the remaining files and the +# function will return non-0 +# +# args: +# * 1: cache directory +# * 2 through n: files from the cache directory to add. if the +# filename matches ta-*.cer then it is added as a trust anchor. +add() ( + cache_dir=$1; shift + ret=0 + for f in "$@"; do + testcase_log "adding file ${f}..." + case ${f} in + ta-*.cer) add_flag=-F;; + *) add_flag=-f;; + esac + # TODO: figure out how to distinguish invalid added object + # from an error during the add process and call abort_test if + # there's an error + (try run "${pfx_fn}rcli-${f}" \ + rcli -s -y "${add_flag}" "${cache_dir}"/"${f}") || ret=1 + done + exit "${ret}" +) + +reset_and_add() { + reset_state "$@" + add "$@" +} + +# print the sorted args, one arg per line +sort_args() { printf %s\\n "$@" | sort; } + +# check if the expected set of files (and only the expected set) were +# accepted +# +# args: +# * 1 through n: each argument is an alternative list (whitespace +# separated) of acceptable files. for example: +# check_accepted "foo bar" "baz bif" +# means that either (foo, bar) or (baz, bif) in the database are +# acceptable +# +check_accepted() { + check_accepted_valid=$( + try run "${pfx_fn}query_cert" query -n -t cert -d filename + try run "${pfx_fn}query_roa" query -n -t roa -d filename + ) || abort_test "unable to list valid files" + check_accepted_valid=$(sort_args ${check_accepted_valid}) + check_accepted_fail_part="expected files" + check_accepted_fail= + for check_accepted_expected in "$@"; do + check_accepted_expected=$(sort_args ${check_accepted_expected}) + [ "${check_accepted_expected}" = "${check_accepted_valid}" ] && exit 0 + check_accepted_fail=${check_accepted_fail}" + ${check_accepted_fail_part}: $(printf " %s" ${check_accepted_expected})" + check_accepted_fail_part=" or" + done + log "valid files differs from expected" + while IFS= read -r check_accepted_line; do + log "${check_accepted_line}" + done <