Commit 6e4bbf22 by Edward Thomson

net: move rfc2818 hostname / wildcard matching to util

parent dbe343b6
......@@ -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;
}
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
#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"));
}
#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"));
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment