summaryrefslogtreecommitdiffstats
path: root/vespalib/src/tests/net
diff options
context:
space:
mode:
authorTor Brede Vekterli <vekterli@verizonmedia.com>2020-11-10 14:08:12 +0000
committerTor Brede Vekterli <vekterli@verizonmedia.com>2020-11-10 14:08:12 +0000
commitaded443066847841b349afc7abea627b47778484 (patch)
treefef4b116efd79a444f46d7a09370b2d216b1b8da /vespalib/src/tests/net
parent40cd2c7b371fd8c0b300dd251408eca5fb28bd40 (diff)
Add basic exact matching support for X509 URI SANs
Adds extraction of X509 URI peer credentials during the handshake process as well as a new SAN_URI field to the transport security options peer policy section. This implementation is NOT conformant with RFC 2459 since we don't currently support case insensitive matching of scheme, host etc., but it's good enough for our purposes for now.
Diffstat (limited to 'vespalib/src/tests/net')
-rw-r--r--vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp10
-rw-r--r--vespalib/src/tests/net/tls/policy_checking_certificate_verifier/policy_checking_certificate_verifier_test.cpp56
-rw-r--r--vespalib/src/tests/net/tls/transport_options/transport_options_reading_test.cpp2
3 files changed, 53 insertions, 15 deletions
diff --git a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
index 4586beef910..7dacbd89503 100644
--- a/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
+++ b/vespalib/src/tests/net/tls/openssl_impl/openssl_impl_test.cpp
@@ -583,10 +583,11 @@ TEST_F("Exception during verification callback processing breaks handshake", Cer
EXPECT_FALSE(f.handshake());
}
-TEST_F("Certificate verification callback observes CN and DNS SANs", CertFixture) {
+TEST_F("Certificate verification callback observes CN, DNS SANs and URI SANs", CertFixture) {
auto ck = f.create_ca_issued_peer_cert(
{{"rockets.wile.example.com"}},
- {{"DNS:crash.wile.example.com"}, {"DNS:burn.wile.example.com"}});
+ {{"DNS:crash.wile.example.com"}, {"DNS:burn.wile.example.com"},
+ {"URI:foo://bar.baz/zoid"}});
fprintf(stderr, "certs:\n%s%s\n", f.root_ca.cert->to_pem().c_str(), ck.cert->to_pem().c_str());
@@ -600,6 +601,8 @@ TEST_F("Certificate verification callback observes CN and DNS SANs", CertFixture
ASSERT_EQUAL(2u, creds.dns_sans.size());
EXPECT_EQUAL("crash.wile.example.com", creds.dns_sans[0]);
EXPECT_EQUAL("burn.wile.example.com", creds.dns_sans[1]);
+ ASSERT_EQUAL(1u, server_cb->creds.uri_sans.size());
+ EXPECT_EQUAL("foo://bar.baz/zoid", server_cb->creds.uri_sans[0]);
}
TEST_F("Last occurring CN is given to verification callback if multiple CNs are present", CertFixture) {
@@ -616,7 +619,7 @@ TEST_F("Last occurring CN is given to verification callback if multiple CNs are
}
// TODO we are likely to want IPADDR SANs at some point
-TEST_F("Only DNS SANs are enumerated", CertFixture) {
+TEST_F("Only DNS and URI SANs are enumerated", CertFixture) {
auto ck = f.create_ca_issued_peer_cert({}, {"IP:127.0.0.1"});
f.reset_client_with_cert_opts(ck, std::make_shared<PrintingCertificateCallback>());
@@ -624,6 +627,7 @@ TEST_F("Only DNS SANs are enumerated", CertFixture) {
f.reset_server_with_cert_opts(ck, server_cb);
ASSERT_TRUE(f.handshake());
EXPECT_EQUAL(0u, server_cb->creds.dns_sans.size());
+ EXPECT_EQUAL(0u, server_cb->creds.uri_sans.size());
}
// We don't test too many combinations of peer policies here, only that
diff --git a/vespalib/src/tests/net/tls/policy_checking_certificate_verifier/policy_checking_certificate_verifier_test.cpp b/vespalib/src/tests/net/tls/policy_checking_certificate_verifier/policy_checking_certificate_verifier_test.cpp
index a9e823bf3ab..9a7e1b1b585 100644
--- a/vespalib/src/tests/net/tls/policy_checking_certificate_verifier/policy_checking_certificate_verifier_test.cpp
+++ b/vespalib/src/tests/net/tls/policy_checking_certificate_verifier/policy_checking_certificate_verifier_test.cpp
@@ -8,7 +8,7 @@ using namespace vespalib;
using namespace vespalib::net::tls;
bool glob_matches(vespalib::stringref pattern, vespalib::stringref string_to_check) {
- auto glob = HostGlobPattern::create_from_glob(pattern);
+ auto glob = CredentialMatchPattern::create_from_glob(pattern);
return glob->matches(string_to_check);
}
@@ -61,12 +61,25 @@ TEST("special extended regex characters are ignored") {
}
// TODO CN + SANs
+PeerCredentials creds_with_sans(std::vector<vespalib::string> dns_sans, std::vector<vespalib::string> uri_sans) {
+ PeerCredentials creds;
+ creds.dns_sans = std::move(dns_sans);
+ creds.uri_sans = std::move(uri_sans);
+ return creds;
+}
+
PeerCredentials creds_with_dns_sans(std::vector<vespalib::string> dns_sans) {
PeerCredentials creds;
creds.dns_sans = std::move(dns_sans);
return creds;
}
+PeerCredentials creds_with_uri_sans(std::vector<vespalib::string> uri_sans) {
+ PeerCredentials creds;
+ creds.uri_sans = std::move(uri_sans);
+ return creds;
+}
+
PeerCredentials creds_with_cn(vespalib::stringref cn) {
PeerCredentials creds;
creds.common_name = cn;
@@ -93,7 +106,7 @@ TEST("Non-empty policies do not allow all authenticated peers") {
EXPECT_FALSE(allow_not_all.allows_all_authenticated());
}
-TEST("SAN requirement without glob pattern is matched as exact string") {
+TEST("DNS SAN requirement without glob pattern is matched as exact string") {
auto authorized = authorized_peers({policy_with({required_san_dns("hello.world")})});
EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"hello.world"}})));
EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"foo.bar"}})));
@@ -103,7 +116,7 @@ TEST("SAN requirement without glob pattern is matched as exact string") {
EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"hello.world.bar"}})));
}
-TEST("SAN requirement can include glob wildcards") {
+TEST("DNS SAN requirement can include glob wildcards") {
auto authorized = authorized_peers({policy_with({required_san_dns("*.w?rld")})});
EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"hello.world"}})));
EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"greetings.w0rld"}})));
@@ -111,23 +124,40 @@ TEST("SAN requirement can include glob wildcards") {
EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"world"}})));
}
+// FIXME make this RFC 2459-compliant with subdomain matching, case insensitity for host etc
+TEST("URI SAN requirement is matched as exact string in cheeky, pragmatic violation of RFC 2459") {
+ auto authorized = authorized_peers({policy_with({required_san_uri("foo://bar.baz/zoid")})});
+ EXPECT_TRUE(verify(authorized, creds_with_uri_sans({{"foo://bar.baz/zoid"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"foo://bar.baz/zoi"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"oo://bar.baz/zoid"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"bar://bar.baz/zoid"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"foo://bar.baz"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"foo://.baz/zoid"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_uri_sans({{"foo://BAR.baz/zoid"}})));
+}
+
TEST("multi-SAN policy requires all SANs to be present in certificate") {
auto authorized = authorized_peers({policy_with({required_san_dns("hello.world"),
- required_san_dns("foo.bar")})});
- EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"hello.world"}, {"foo.bar"}})));
- // Need both
- EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"hello.world"}})));
- EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"foo.bar"}})));
+ required_san_dns("foo.bar"),
+ required_san_uri("foo://bar/baz")})});
+ EXPECT_TRUE(verify(authorized, creds_with_sans({{"hello.world"}, {"foo.bar"}}, {{"foo://bar/baz"}})));
+ // Need all
+ EXPECT_FALSE(verify(authorized, creds_with_sans({{"hello.world"}, {"foo.bar"}}, {})));
+ EXPECT_FALSE(verify(authorized, creds_with_sans({{"hello.world"}}, {{"foo://bar/baz"}})));
+ EXPECT_FALSE(verify(authorized, creds_with_sans({{"hello.world"}}, {})));
+ EXPECT_FALSE(verify(authorized, creds_with_sans({{"foo.bar"}}, {})));
+ EXPECT_FALSE(verify(authorized, creds_with_sans({}, {{"foo://bar/baz"}})));
// OK with more SANs that strictly required
- EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"hello.world"}, {"foo.bar"}, {"baz.blorg"}})));
+ EXPECT_TRUE(verify(authorized, creds_with_sans({{"hello.world"}, {"foo.bar"}, {"baz.blorg"}},
+ {{"foo://bar/baz"}, {"hello://world/"}})));
}
-TEST("wildcard SAN in certificate is not treated as a wildcard match by policy") {
+TEST("wildcard DNS SAN in certificate is not treated as a wildcard match by policy") {
auto authorized = authorized_peers({policy_with({required_san_dns("hello.world")})});
EXPECT_FALSE(verify(authorized, creds_with_dns_sans({{"*.world"}})));
}
-TEST("wildcard SAN in certificate is still matched by wildcard policy SAN") {
+TEST("wildcard DNS SAN in certificate is still matched by wildcard policy SAN") {
auto authorized = authorized_peers({policy_with({required_san_dns("*.world")})});
EXPECT_TRUE(verify(authorized, creds_with_dns_sans({{"*.world"}})));
}
@@ -141,7 +171,8 @@ struct MultiPolicyMatchFixture {
MultiPolicyMatchFixture::MultiPolicyMatchFixture()
: authorized(authorized_peers({policy_with({required_san_dns("hello.world")}),
policy_with({required_san_dns("foo.bar")}),
- policy_with({required_san_dns("zoid.berg")})}))
+ policy_with({required_san_dns("zoid.berg")}),
+ policy_with({required_san_uri("zoid://be.rg/")})}))
{}
MultiPolicyMatchFixture::~MultiPolicyMatchFixture() = default;
@@ -150,6 +181,7 @@ TEST_F("peer verifies if it matches at least 1 policy of multiple", MultiPolicyM
EXPECT_TRUE(verify(f.authorized, creds_with_dns_sans({{"hello.world"}})));
EXPECT_TRUE(verify(f.authorized, creds_with_dns_sans({{"foo.bar"}})));
EXPECT_TRUE(verify(f.authorized, creds_with_dns_sans({{"zoid.berg"}})));
+ EXPECT_TRUE(verify(f.authorized, creds_with_uri_sans({{"zoid://be.rg/"}})));
}
TEST_F("peer verifies if it matches multiple policies", MultiPolicyMatchFixture) {
diff --git a/vespalib/src/tests/net/tls/transport_options/transport_options_reading_test.cpp b/vespalib/src/tests/net/tls/transport_options/transport_options_reading_test.cpp
index 00459a4e69c..a2bced3f7b4 100644
--- a/vespalib/src/tests/net/tls/transport_options/transport_options_reading_test.cpp
+++ b/vespalib/src/tests/net/tls/transport_options/transport_options_reading_test.cpp
@@ -111,10 +111,12 @@ TEST("can parse single peer policy with multiple requirements") {
const char* json = R"({
"required-credentials":[
{"field": "SAN_DNS", "must-match": "hello.world"},
+ {"field": "SAN_URI", "must-match": "foo://bar/baz"},
{"field": "CN", "must-match": "goodbye.moon"}
]
})";
EXPECT_EQUAL(authorized_peers({policy_with({required_san_dns("hello.world"),
+ required_san_uri("foo://bar/baz"),
required_cn("goodbye.moon")})}),
parse_policies(json).authorized_peers());
}