diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2023-05-12 20:48:30 +0100 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2023-05-13 16:42:04 +0100 |
commit | 6e4bbf222d8c4babaff90aef40615546c8bc9cde (patch) | |
tree | add632fcb19f7266e22044022840d9500b5b3c84 | |
parent | dbe343b6e3e957b5cffbd04832c6e7364b496ae7 (diff) | |
download | libgit2-6e4bbf222d8c4babaff90aef40615546c8bc9cde.tar.gz |
net: move rfc2818 hostname / wildcard matching to util
-rw-r--r-- | src/libgit2/netops.c | 39 | ||||
-rw-r--r-- | src/libgit2/netops.h | 13 | ||||
-rw-r--r-- | src/libgit2/streams/openssl.c | 19 | ||||
-rw-r--r-- | src/util/net.c | 44 | ||||
-rw-r--r-- | src/util/net.h | 17 | ||||
-rw-r--r-- | tests/libgit2/network/matchhost.c | 13 | ||||
-rw-r--r-- | tests/util/hostname.c | 13 |
7 files changed, 80 insertions, 78 deletions
diff --git a/src/libgit2/netops.c b/src/libgit2/netops.c index 00640c600..5cae374ad 100644 --- a/src/libgit2/netops.c +++ b/src/libgit2/netops.c @@ -83,42 +83,3 @@ void gitno_consume_n(gitno_buffer *buf, size_t cons) memset(buf->data + cons, 0x0, buf->len - buf->offset); buf->offset -= cons; } - -/* Match host names according to RFC 2818 rules */ -int gitno__match_host(const char *pattern, const char *host) -{ - for (;;) { - char c = git__tolower(*pattern++); - - if (c == '\0') - return *host ? -1 : 0; - - if (c == '*') { - c = *pattern; - /* '*' at the end matches everything left */ - if (c == '\0') - return 0; - - /* - * We've found a pattern, so move towards the next matching - * char. The '.' is handled specially because wildcards aren't - * allowed to cross subdomains. - */ - - while(*host) { - char h = git__tolower(*host); - if (c == h) - return gitno__match_host(pattern, host++); - if (h == '.') - return gitno__match_host(pattern, host); - host++; - } - return -1; - } - - if (c != git__tolower(*host++)) - return -1; - } - - return -1; -} diff --git a/src/libgit2/netops.h b/src/libgit2/netops.h index 56f968534..a3f4a0f95 100644 --- a/src/libgit2/netops.h +++ b/src/libgit2/netops.h @@ -45,19 +45,6 @@ enum { GITNO_CONNECT_SSL = 1 }; -/** - * Check if the name in a cert matches the wanted hostname - * - * Check if a pattern from a certificate matches the hostname we - * wanted to connect to according to RFC2818 rules (which specifies - * HTTP over TLS). Mainly, an asterisk matches anything, but is - * limited to a single url component. - * - * Note that this does not set an error message. It expects the user - * to provide the message for the user. - */ -int gitno__match_host(const char *pattern, const char *host); - void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len); void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); int gitno_recv(gitno_buffer *buf); diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c index 5e0e2c939..58b2d1b23 100644 --- a/src/libgit2/streams/openssl.c +++ b/src/libgit2/streams/openssl.c @@ -18,6 +18,7 @@ #include "settings.h" #include "posix.h" #include "stream.h" +#include "net.h" #include "streams/socket.h" #include "netops.h" #include "git2/transport.h" @@ -357,15 +358,10 @@ static int ssl_teardown(SSL *ssl) return ret; } -static int check_host_name(const char *name, const char *host) +static bool check_host_name(const char *host, const char *name) { - if (!strcasecmp(name, host)) - return 0; - - if (gitno__match_host(name, host) < 0) - return -1; - - return 0; + return !strcasecmp(host, name) || + git_net_hostname_matches_cert(host, name); } static int verify_server_cert(SSL *ssl, const char *host) @@ -425,10 +421,7 @@ static int verify_server_cert(SSL *ssl, const char *host) if (memchr(name, '\0', namelen)) continue; - if (check_host_name(name, host) < 0) - matched = 0; - else - matched = 1; + matched = !!check_host_name(host, name); } else if (type == GEN_IPADD) { /* Here name isn't so much a name but a binary representation of the IP */ matched = addr && !!memcmp(name, addr, namelen); @@ -481,7 +474,7 @@ static int verify_server_cert(SSL *ssl, const char *host) goto cert_fail_name; } - if (check_host_name((char *)peer_cn, host) < 0) + if (!check_host_name(host, (char *)peer_cn)) goto cert_fail_name; goto cleanup; diff --git a/src/util/net.c b/src/util/net.c index ac7befe07..dd8a1ba46 100644 --- a/src/util/net.c +++ b/src/util/net.c @@ -19,6 +19,50 @@ #define DEFAULT_PORT_GIT "9418" #define DEFAULT_PORT_SSH "22" +bool git_net_hostname_matches_cert( + const char *hostname, + const char *pattern) +{ + for (;;) { + char c = git__tolower(*pattern++); + + if (c == '\0') + return *hostname ? false : true; + + if (c == '*') { + c = *pattern; + + /* '*' at the end matches everything left */ + if (c == '\0') + return true; + + /* + * We've found a pattern, so move towards the + * next matching char. The '.' is handled + * specially because wildcards aren't allowed + * to cross subdomains. + */ + while(*hostname) { + char h = git__tolower(*hostname); + + if (h == c) + return git_net_hostname_matches_cert(hostname++, pattern); + else if (h == '.') + return git_net_hostname_matches_cert(hostname, pattern); + + hostname++; + } + + return false; + } + + if (c != git__tolower(*hostname++)) + return false; + } + + return false; +} + bool git_net_str_is_url(const char *str) { const char *c; diff --git a/src/util/net.h b/src/util/net.h index 17f0bc4f0..c9a84cb6c 100644 --- a/src/util/net.h +++ b/src/util/net.h @@ -9,6 +9,23 @@ #include "git2_util.h" +/* + * Hostname handling + */ + +/* + * See if a given hostname matches a certificate name pattern, according + * to RFC2818 rules (which specifies HTTP over TLS). Mainly, an asterisk + * matches anything, but is limited to a single url component. + */ +extern bool git_net_hostname_matches_cert( + const char *hostname, + const char *pattern); + +/* + * URL handling + */ + typedef struct git_net_url { char *scheme; char *host; diff --git a/tests/libgit2/network/matchhost.c b/tests/libgit2/network/matchhost.c deleted file mode 100644 index 3100dc21d..000000000 --- a/tests/libgit2/network/matchhost.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "clar_libgit2.h" -#include "netops.h" - -void test_network_matchhost__match(void) -{ - cl_git_pass(gitno__match_host("*.example.org", "www.example.org")); - cl_git_pass(gitno__match_host("*.foo.example.org", "www.foo.example.org")); - cl_git_fail(gitno__match_host("*.foo.example.org", "foo.example.org")); - cl_git_fail(gitno__match_host("*.foo.example.org", "www.example.org")); - cl_git_fail(gitno__match_host("*.example.org", "example.org")); - cl_git_fail(gitno__match_host("*.example.org", "www.foo.example.org")); - cl_git_fail(gitno__match_host("*.example.org", "blah.www.www.example.org")); -} diff --git a/tests/util/hostname.c b/tests/util/hostname.c new file mode 100644 index 000000000..5d8bfe2ac --- /dev/null +++ b/tests/util/hostname.c @@ -0,0 +1,13 @@ +#include "clar_libgit2.h" +#include "net.h" + +void test_hostname__matches_cert(void) +{ + cl_assert_equal_b(true, git_net_hostname_matches_cert("www.example.org", "*.example.org")); + cl_assert_equal_b(true, git_net_hostname_matches_cert("www.foo.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("foo.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("www.example.org", "*.foo.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("example.org", "*.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("www.foo.example.org", "*.example.org")); + cl_assert_equal_b(false, git_net_hostname_matches_cert("blah.www.www.example.org", "*.example.org")); +} |