diff options
author | René Korthaus <[email protected]> | 2017-04-20 09:54:19 +0200 |
---|---|---|
committer | René Korthaus <[email protected]> | 2017-04-20 17:12:23 +0200 |
commit | 289cc25709b081cd112d47db66c4f2fbf4609583 (patch) | |
tree | fbb5cfad1fc6c94cad08ebc8bafd724a953425af /src/lib/utils/parsing.cpp | |
parent | 8ee030e6d4f3b7b449aab3c1cca1a3837a5143e5 (diff) |
Complete wildcard handling for X.509 certificates
Hostname validation is used to make sure the certificate
hostname matches the hostname of the connected host.
RFC 6125 allows one wildcard in the left-most label of
a hostname. Up to now, we only supported only the wildcard
as the left-most label, e.g., www.example.com would
match *.example.com, but www.example.com would not
match www*.example.com, although it is permitted.
Also adds test vectors from RFC 6125 as well as the
OpenSSL test suite.
Diffstat (limited to 'src/lib/utils/parsing.cpp')
-rw-r--r-- | src/lib/utils/parsing.cpp | 88 |
1 files changed, 84 insertions, 4 deletions
diff --git a/src/lib/utils/parsing.cpp b/src/lib/utils/parsing.cpp index cd0c2409e..7583767f0 100644 --- a/src/lib/utils/parsing.cpp +++ b/src/lib/utils/parsing.cpp @@ -2,6 +2,7 @@ * Various string utils and parsing functions * (C) 1999-2007,2013,2014,2015 Jack Lloyd * (C) 2015 Simon Warta (Kullo GmbH) +* (C) 2017 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -12,6 +13,7 @@ #include <botan/loadstor.h> #include <limits> #include <set> +#include <algorithm> namespace Botan { @@ -337,22 +339,100 @@ std::string replace_char(const std::string& str, char from_char, char to_char) bool host_wildcard_match(const std::string& issued, const std::string& host) { if(issued == host) + { return true; + } - if(issued.size() > 2 && issued[0] == '*' && issued[1] == '.') + if(std::count(issued.begin(), issued.end(), '*') > 1) + { + return false; + } + + // first try to match the base, then the left-most label + // which can contain exactly one wildcard at any position + if(issued.size() > 2) { size_t host_i = host.find('.'); if(host_i == std::string::npos || host_i == host.size() - 1) + { return false; + } + + size_t issued_i = issued.find('.'); + if(issued_i == std::string::npos || issued_i == issued.size() - 1) + { + return false; + } const std::string host_base = host.substr(host_i + 1); - const std::string issued_base = issued.substr(2); + const std::string issued_base = issued.substr(issued_i + 1); + + // if anything but the left-most label doesn't equal, + // we are already out here + if(host_base != issued_base) + { + return false; + } + + // compare the left-most labels + std::string host_prefix = host.substr(0, host_i); + + if(host_prefix.empty()) + { + return false; + } + + const std::string issued_prefix = issued.substr(0, issued_i); + + // if split_on would work on strings with less than 2 items, + // the if/else block would not be necessary + if(issued_prefix == "*") + { + return true; + } + + std::vector<std::string> p; - if(host_base == issued_base) + if(issued_prefix[0] == '*') + { + p = std::vector<std::string>{"", issued_prefix.substr(1, issued_prefix.size())}; + } + else if(issued_prefix[issued_prefix.size()-1] == '*') + { + p = std::vector<std::string>{issued_prefix.substr(0, issued_prefix.size() - 1), ""}; + } + else + { + p = split_on(issued_prefix, '*'); + } + + if(p.size() != 2) + { + return false; + } + + // match anything before and after the wildcard character + const std::string first = p[0]; + const std::string last = p[1]; + + if(host_prefix.substr(0, first.size()) == first) + { + host_prefix.erase(0, first.size()); + } + + // nothing to match anymore + if(last.empty()) + { return true; } + if(host_prefix.size() >= last.size() && + host_prefix.substr(host_prefix.size() - last.size(), last.size()) == last) + { + return true; + } + } + return false; } - } |