Implement prefix and suffix matching for TLS certificate verification.
This extends our language for specifying verification rules from, e.g. S.O=XYZCorp to also include two more operators S.O>=XYZ # Prefix S.O<=Corp # Suffix both of which would match against an Organization of XYZCorp (among others).
This commit is contained in:
parent
e39d2c702d
commit
70d078021f
|
@ -100,7 +100,7 @@ FDBLibTLSSession::~FDBLibTLSSession() {
|
|||
tls_free(tls_sctx);
|
||||
}
|
||||
|
||||
bool match_criteria(X509_NAME *name, int nid, const char *value, size_t len) {
|
||||
bool match_criteria(X509_NAME *name, int nid, const char *value, size_t len, MatchType mt) {
|
||||
unsigned char *name_entry_utf8 = NULL, *criteria_utf8 = NULL;
|
||||
int name_entry_utf8_len, criteria_utf8_len;
|
||||
ASN1_STRING *criteria = NULL;
|
||||
|
@ -127,9 +127,19 @@ bool match_criteria(X509_NAME *name, int nid, const char *value, size_t len) {
|
|||
goto err;
|
||||
if ((name_entry_utf8_len = ASN1_STRING_to_UTF8(&name_entry_utf8, name_entry->value)) < 1)
|
||||
goto err;
|
||||
if (criteria_utf8_len == name_entry_utf8_len &&
|
||||
memcmp(criteria_utf8, name_entry_utf8, criteria_utf8_len) == 0)
|
||||
rc = true;
|
||||
if (mt == MatchType::EXACT) {
|
||||
if (criteria_utf8_len == name_entry_utf8_len &&
|
||||
memcmp(criteria_utf8, name_entry_utf8, criteria_utf8_len) == 0)
|
||||
rc = true;
|
||||
} else if (mt == MatchType::PREFIX) {
|
||||
if (criteria_utf8_len <= name_entry_utf8_len &&
|
||||
memcmp(criteria_utf8, name_entry_utf8, criteria_utf8_len) == 0)
|
||||
rc = true;
|
||||
} else if (mt == MatchType::SUFFIX) {
|
||||
if (criteria_utf8_len <= name_entry_utf8_len &&
|
||||
memcmp(criteria_utf8, name_entry_utf8 + (name_entry_utf8_len - criteria_utf8_len), criteria_utf8_len) == 0)
|
||||
rc = true;
|
||||
}
|
||||
|
||||
err:
|
||||
ASN1_STRING_free(criteria);
|
||||
|
@ -177,7 +187,7 @@ std::tuple<bool,std::string> FDBLibTLSSession::check_verify(Reference<FDBLibTLSV
|
|||
goto err;
|
||||
}
|
||||
for (auto &pair: verify->subject_criteria) {
|
||||
if (!match_criteria(subject, pair.first, pair.second.c_str(), pair.second.size())) {
|
||||
if (!match_criteria(subject, pair.first, pair.second.criteria.c_str(), pair.second.criteria.size(), pair.second.match_type)) {
|
||||
reason = "FDBLibTLSCertSubjectMatchFailure";
|
||||
goto err;
|
||||
}
|
||||
|
@ -189,7 +199,7 @@ std::tuple<bool,std::string> FDBLibTLSSession::check_verify(Reference<FDBLibTLSV
|
|||
goto err;
|
||||
}
|
||||
for (auto &pair: verify->issuer_criteria) {
|
||||
if (!match_criteria(issuer, pair.first, pair.second.c_str(), pair.second.size())) {
|
||||
if (!match_criteria(issuer, pair.first, pair.second.criteria.c_str(), pair.second.criteria.size(), pair.second.match_type)) {
|
||||
reason = "FDBLibTLSCertIssuerMatchFailure";
|
||||
goto err;
|
||||
}
|
||||
|
@ -201,7 +211,7 @@ std::tuple<bool,std::string> FDBLibTLSSession::check_verify(Reference<FDBLibTLSV
|
|||
goto err;
|
||||
}
|
||||
for (auto &pair: verify->root_criteria) {
|
||||
if (!match_criteria(subject, pair.first, pair.second.c_str(), pair.second.size())) {
|
||||
if (!match_criteria(subject, pair.first, pair.second.criteria.c_str(), pair.second.criteria.size(), pair.second.match_type)) {
|
||||
reason = "FDBLibTLSRootSubjectMatchFailure";
|
||||
goto err;
|
||||
}
|
||||
|
|
|
@ -161,13 +161,18 @@ void FDBLibTLSVerify::parse_verify(std::string input) {
|
|||
if (eq == input.npos)
|
||||
throw std::runtime_error("parse_verify");
|
||||
|
||||
std::string term = input.substr(s, eq - s);
|
||||
MatchType mt = MatchType::EXACT;
|
||||
if (input[eq-1] == '>') mt = MatchType::PREFIX;
|
||||
if (input[eq-1] == '<') mt = MatchType::SUFFIX;
|
||||
std::string term = input.substr(s, eq - s - (mt == MatchType::EXACT ? 0 : 1));
|
||||
|
||||
if (term.find("Check.") == 0) {
|
||||
if (eq + 2 > input.size())
|
||||
throw std::runtime_error("parse_verify");
|
||||
if (eq + 2 != input.size() && input[eq + 2] != ',')
|
||||
throw std::runtime_error("parse_verify");
|
||||
if (mt != MatchType::EXACT)
|
||||
throw std::runtime_error("parse_verify: cannot prefix match Check");
|
||||
|
||||
bool* flag;
|
||||
|
||||
|
@ -187,7 +192,7 @@ void FDBLibTLSVerify::parse_verify(std::string input) {
|
|||
|
||||
s = eq + 3;
|
||||
} else {
|
||||
std::map<int, std::string>* criteria = &subject_criteria;
|
||||
std::map< int, Criteria >* criteria = &subject_criteria;
|
||||
|
||||
if (term.find('.') != term.npos) {
|
||||
auto scoped = splitPair(term, '.');
|
||||
|
@ -210,7 +215,7 @@ void FDBLibTLSVerify::parse_verify(std::string input) {
|
|||
if (remain == eq + 1)
|
||||
throw std::runtime_error("parse_verify");
|
||||
|
||||
criteria->insert(std::make_pair(abbrevToNID(term), unesc));
|
||||
criteria->insert(std::make_pair(abbrevToNID(term), Criteria(unesc, mt)));
|
||||
|
||||
if (remain != input.size() && input[remain] != ',')
|
||||
throw std::runtime_error("parse_verify");
|
||||
|
|
|
@ -29,6 +29,25 @@
|
|||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
enum class MatchType {
|
||||
EXACT,
|
||||
PREFIX,
|
||||
SUFFIX,
|
||||
};
|
||||
|
||||
struct Criteria {
|
||||
Criteria( const std::string& s ) : criteria(s), match_type(MatchType::EXACT) {}
|
||||
Criteria( const std::string& s, MatchType mt ) : criteria(s), match_type(mt) {}
|
||||
|
||||
std::string criteria;
|
||||
MatchType match_type;
|
||||
|
||||
bool operator==(const Criteria& c) const {
|
||||
return criteria == c.criteria && match_type == c.match_type;
|
||||
}
|
||||
};
|
||||
|
||||
struct FDBLibTLSVerify: ReferenceCounted<FDBLibTLSVerify> {
|
||||
FDBLibTLSVerify(std::string verify);
|
||||
|
@ -42,9 +61,9 @@ struct FDBLibTLSVerify: ReferenceCounted<FDBLibTLSVerify> {
|
|||
bool verify_cert;
|
||||
bool verify_time;
|
||||
|
||||
std::map<int, std::string> subject_criteria;
|
||||
std::map<int, std::string> issuer_criteria;
|
||||
std::map<int, std::string> root_criteria;
|
||||
std::map< int, Criteria > subject_criteria;
|
||||
std::map< int, Criteria > issuer_criteria;
|
||||
std::map< int, Criteria > root_criteria;
|
||||
};
|
||||
|
||||
#endif /* FDB_LIBTLS_VERIFY_H */
|
||||
|
|
|
@ -489,7 +489,7 @@ int FDBLibTLSPluginTest::client_server_test(const struct client_server_test* cst
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void logf(const char* event, void* uid, int is_error, ...) {
|
||||
static void logf(const char* event, void* uid, bool is_error, ...) {
|
||||
va_list args;
|
||||
|
||||
std::string log_type ("INFO");
|
||||
|
@ -1049,6 +1049,32 @@ const struct client_server_test client_server_tests[] = {
|
|||
.server_password = NULL,
|
||||
.server_verify = {""},
|
||||
},
|
||||
|
||||
// Prefix and Suffix Matching
|
||||
{
|
||||
.ca_path = "test-ca-1.pem",
|
||||
.client_success = true,
|
||||
.client_path = "test-client-2.pem",
|
||||
.client_password = NULL,
|
||||
.client_verify = {"O>=Apple Inc.,OU>=FDB"},
|
||||
.servername = NULL,
|
||||
.server_success = true,
|
||||
.server_path = "test-server-1.pem",
|
||||
.server_password = NULL,
|
||||
.server_verify = {"O<=Limited,OU<=Team"},
|
||||
},
|
||||
{
|
||||
.ca_path = "test-ca-1.pem",
|
||||
.client_success = false,
|
||||
.client_path = "test-client-2.pem",
|
||||
.client_password = NULL,
|
||||
.client_verify = {"O<=Apple Inc.,OU<=FDB"},
|
||||
.servername = NULL,
|
||||
.server_success = false,
|
||||
.server_path = "test-server-1.pem",
|
||||
.server_password = NULL,
|
||||
.server_verify = {"O>=Limited,OU>=Team"},
|
||||
},
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
struct FDBLibTLSVerifyTest {
|
||||
FDBLibTLSVerifyTest(std::string input):
|
||||
input(input), valid(false), verify_cert(true), verify_time(true), subject_criteria({}), issuer_criteria({}), root_criteria({}) {};
|
||||
FDBLibTLSVerifyTest(std::string input, bool verify_cert, bool verify_time, std::map<int, std::string> subject, std::map<int, std::string> issuer, std::map<int, std::string> root):
|
||||
FDBLibTLSVerifyTest(std::string input, bool verify_cert, bool verify_time, std::map<int, Criteria> subject, std::map<int, Criteria> issuer, std::map<int, Criteria> root):
|
||||
input(input), valid(true), verify_cert(verify_cert), verify_time(verify_time), subject_criteria(subject), issuer_criteria(issuer), root_criteria(root) {};
|
||||
~FDBLibTLSVerifyTest() {};
|
||||
|
||||
|
@ -47,9 +47,9 @@ struct FDBLibTLSVerifyTest {
|
|||
bool verify_cert;
|
||||
bool verify_time;
|
||||
|
||||
std::map<int, std::string> subject_criteria;
|
||||
std::map<int, std::string> issuer_criteria;
|
||||
std::map<int, std::string> root_criteria;
|
||||
std::map<int, Criteria> subject_criteria;
|
||||
std::map<int, Criteria> issuer_criteria;
|
||||
std::map<int, Criteria> root_criteria;
|
||||
};
|
||||
|
||||
static std::string printable( std::string const& val ) {
|
||||
|
@ -71,10 +71,10 @@ static std::string printable( std::string const& val ) {
|
|||
return s;
|
||||
}
|
||||
|
||||
static std::string criteriaToString(std::map<int, std::string> const& criteria) {
|
||||
static std::string criteriaToString(std::map<int, Criteria> const& criteria) {
|
||||
std::string s;
|
||||
for (auto &pair: criteria) {
|
||||
s += "{" + std::to_string(pair.first) + ":" + printable(pair.second) + "}";
|
||||
s += "{" + std::to_string(pair.first) + ":" + printable(pair.second.criteria) + "}";
|
||||
}
|
||||
return "{" + s + "}";
|
||||
}
|
||||
|
@ -180,6 +180,10 @@ int main(int argc, char **argv)
|
|||
{
|
||||
int failed = 0;
|
||||
|
||||
#define EXACT(x) Criteria(x, MatchType::EXACT)
|
||||
#define PREFIX(x) Criteria(x, MatchType::PREFIX)
|
||||
#define SUFFIX(x) Criteria(x, MatchType::SUFFIX)
|
||||
|
||||
std::vector<FDBLibTLSVerifyTest> tests = {
|
||||
FDBLibTLSVerifyTest("", true, true, {}, {}, {}),
|
||||
FDBLibTLSVerifyTest("Check.Valid=1", true, true, {}, {}, {}),
|
||||
|
@ -189,26 +193,28 @@ int main(int argc, char **argv)
|
|||
FDBLibTLSVerifyTest("Check.Valid=1,Check.Unexpired=0", true, false, {}, {}, {}),
|
||||
FDBLibTLSVerifyTest("Check.Unexpired=0,Check.Valid=0", false, false, {}, {}, {}),
|
||||
FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp\\, LLC", true, false,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp, LLC")}}, {{NID_countryName, EXACT("US")}}, {}),
|
||||
FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp\\= LLC", true, false,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp= LLC"}}, {{NID_countryName, "US"}}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp= LLC")}}, {{NID_countryName, EXACT("US")}}, {}),
|
||||
FDBLibTLSVerifyTest("Check.Unexpired=0,R.C=US,C=US,S.O=XYZCorp\\= LLC", true, false,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp= LLC"}}, {}, {{NID_countryName, "US"}}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp= LLC")}}, {}, {{NID_countryName, EXACT("US")}}),
|
||||
FDBLibTLSVerifyTest("Check.Unexpired=0,I.C=US,C=US,S.O=XYZCorp=LLC", true, false,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp=LLC")}}, {{NID_countryName, EXACT("US")}}, {}),
|
||||
FDBLibTLSVerifyTest("I.C=US,C=US,Check.Unexpired=0,S.O=XYZCorp=LLC", true, false,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp=LLC"}}, {{NID_countryName, "US"}}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp=LLC")}}, {{NID_countryName, EXACT("US")}}, {}),
|
||||
FDBLibTLSVerifyTest("I.C=US,C=US,S.O=XYZCorp\\, LLC", true, true,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}}, {{NID_countryName, "US"}}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp, LLC")}}, {{NID_countryName, EXACT("US")}}, {}),
|
||||
FDBLibTLSVerifyTest("I.C=US,C=US,S.O=XYZCorp\\, LLC,R.CN=abc", true, true,
|
||||
{{NID_countryName, "US"}, {NID_organizationName, "XYZCorp, LLC"}},
|
||||
{{NID_countryName, "US"}},
|
||||
{{NID_commonName, "abc"}}),
|
||||
FDBLibTLSVerifyTest("C=\\,S=abc", true, true, {{NID_countryName, ",S=abc"}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=\\61\\62\\63", true, true, {{NID_commonName, "abc"}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=a\\62c", true, true, {{NID_commonName, "abc"}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=a\\01c", true, true, {{NID_commonName, "a\001c"}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("S.subjectAltName=XYZCorp", true, true, {{NID_subject_alt_name, "XYZCorp"}}, {}, {}),
|
||||
{{NID_countryName, EXACT("US")}, {NID_organizationName, EXACT("XYZCorp, LLC")}},
|
||||
{{NID_countryName, EXACT("US")}},
|
||||
{{NID_commonName, EXACT("abc")}}),
|
||||
FDBLibTLSVerifyTest("C=\\,S=abc", true, true, {{NID_countryName, EXACT(",S=abc")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=\\61\\62\\63", true, true, {{NID_commonName, EXACT("abc")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=a\\62c", true, true, {{NID_commonName, EXACT("abc")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("CN=a\\01c", true, true, {{NID_commonName, EXACT("a\001c")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("S.subjectAltName=XYZCorp", true, true, {{NID_subject_alt_name, EXACT("XYZCorp")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("S.O>=XYZ", true, true, {{NID_organizationName, PREFIX("XYZ")}}, {}, {}),
|
||||
FDBLibTLSVerifyTest("S.O<=LLC", true, true, {{NID_organizationName, SUFFIX("LLC")}}, {}, {}),
|
||||
|
||||
// Invalid cases.
|
||||
FDBLibTLSVerifyTest("Check.Invalid=0"),
|
||||
|
@ -221,6 +227,10 @@ int main(int argc, char **argv)
|
|||
FDBLibTLSVerifyTest("CN=abc,Check.Expired=1"),
|
||||
};
|
||||
|
||||
#undef EXACT
|
||||
#undef PREFIX
|
||||
#undef SUFFIX
|
||||
|
||||
for (auto &test: tests)
|
||||
failed |= test.run();
|
||||
|
||||
|
|
Loading…
Reference in New Issue