Skip to content

Commit

Permalink
Adding RFC2818 compliance checks to axTLS. Completely new are the Sub…
Browse files Browse the repository at this point in the history
…jectAltName checks. The peer CN field fallback is implemented matching the OpenSSL behaviour. VERIFYHOST 0 == 1.
  • Loading branch information
okoeroo committed Nov 3, 2012
1 parent 74fe1b9 commit 1bc4e22
Showing 1 changed file with 111 additions and 7 deletions.
118 changes: 111 additions & 7 deletions lib/axtls.c
Expand Up @@ -48,6 +48,65 @@
/* The last #include file should be: */
#include "memdebug.h"


#define HOST_NOMATCH 0
#define HOST_MATCH 1

static int hostmatch(const char *hostname, const char *pattern)
{
const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
int wildcard_enabled;
size_t prefixlen, suffixlen;
pattern_wildcard = strchr(pattern, '*');
if(pattern_wildcard == NULL) {
return Curl_raw_equal(pattern, hostname) ? HOST_MATCH : HOST_NOMATCH;
}
/* We require at least 2 dots in pattern to avoid too wide wildcard
match. */
wildcard_enabled = 1;
pattern_label_end = strchr(pattern, '.');
if(pattern_label_end == NULL || strchr(pattern_label_end+1, '.') == NULL ||
pattern_wildcard > pattern_label_end ||
Curl_raw_nequal(pattern, "xn--", 4)) {
wildcard_enabled = 0;
}
if(!wildcard_enabled) {
return Curl_raw_equal(pattern, hostname) ? HOST_MATCH : HOST_NOMATCH;
}
hostname_label_end = strchr(hostname, '.');
if(hostname_label_end == NULL ||
!Curl_raw_equal(pattern_label_end, hostname_label_end)) {
return HOST_NOMATCH;
}
/* The wildcard must match at least one character, so the left-most
label of the hostname is at least as large as the left-most label
of the pattern. */
if(hostname_label_end - hostname < pattern_label_end - pattern) {
return HOST_NOMATCH;
}
prefixlen = pattern_wildcard - pattern;
suffixlen = pattern_label_end - (pattern_wildcard+1);
return Curl_raw_nequal(pattern, hostname, prefixlen) &&
Curl_raw_nequal(pattern_wildcard+1, hostname_label_end - suffixlen,
suffixlen) ?
HOST_MATCH : HOST_NOMATCH;
}

static int
cert_hostcheck(const char *match_pattern, const char *hostname)
{
if(!match_pattern || !*match_pattern ||
!hostname || !*hostname) /* sanity check */
return 0;

if(Curl_raw_equal(hostname, match_pattern)) /* trivial case */
return 1;

if(hostmatch(hostname,match_pattern) == HOST_MATCH)
return 1;
return 0;
}

/* SSL_read is opied from axTLS compat layer */
static int SSL_read(SSL *ssl, void *buf, int num)
{
Expand Down Expand Up @@ -150,7 +209,11 @@ Curl_axtls_connect(struct connectdata *conn,
int i, ssl_fcn_return;
const uint8_t *ssl_sessionid;
size_t ssl_idsize;
const char *x509;
const char *peer_CN;
uint32_t dns_altname_index;
const char *dns_altname;
int8_t found_subject_alt_names = 0;
int8_t found_subject_alt_name_matching_conn = 0;

/* Assuming users will not compile in custom key/cert to axTLS */
uint32_t client_option = SSL_NO_DEFAULT_KEY|SSL_SERVER_VERIFY_LATER;
Expand Down Expand Up @@ -296,19 +359,60 @@ Curl_axtls_connect(struct connectdata *conn,
/* Here, gtls.c does issuer verification. axTLS has no straightforward
* equivalent, so omitting for now.*/

/* See if common name was set in server certificate */
x509 = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
if(x509 == NULL)
infof(data, "error fetching CN from cert\n");

/* Here, gtls.c does the following
* 1) x509 hostname checking per RFC2818. axTLS doesn't support this, but
* it seems useful. Omitting for now.
* it seems useful. This is now implemented, by Oscar Koeroo
* 2) checks cert validity based on time. axTLS does this in ssl_verify_cert
* 3) displays a bunch of cert information. axTLS doesn't support most of
* this, but a couple fields are available.
*/


/* There is no (DNS) Altnames count in the version 1.4.8 API. There is a risk of an inifite loop */
for (dns_altname_index = 0; ; dns_altname_index++) {
dns_altname = ssl_get_cert_subject_alt_dnsname(ssl, dns_altname_index);
if (dns_altname == NULL) {
break;
}
found_subject_alt_names = 1;

infof(data, "\tComparing subject alt name DNS with hostname: %s <-> %s\n", dns_altname, conn->host.name);
if (cert_hostcheck(dns_altname, conn->host.name)) {
found_subject_alt_name_matching_conn = 1;
break;
}
}

/* RFC2818 checks */
if (found_subject_alt_names && !found_subject_alt_name_matching_conn) {
/* Break connection ! */
Curl_axtls_close(conn, sockindex);
failf(data, "\tsubjectAltName(s) do not match %s\n", conn->host.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
}
else if (found_subject_alt_names == 0) {
/* Per RFC2818, when no Subject Alt Names were available, examine the peer CN as a legacy fallback */
peer_CN = ssl_get_cert_dn(ssl, SSL_X509_CERT_COMMON_NAME);
if(peer_CN == NULL) {
/* Similar behaviour to the OpenSSL interface */
Curl_axtls_close(conn, sockindex);
failf(data, "unable to obtain common name from peer certificate");
return CURLE_PEER_FAILED_VERIFICATION;
}
else {
if(!cert_hostcheck((const char *)peer_CN, conn->host.name)) {
if(data->set.ssl.verifyhost > 1) {
/* Break connection ! */
Curl_axtls_close(conn, sockindex);
failf(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, "\tcommon name \"%s\" does not match \"%s\"\n", peer_CN, conn->host.dispname);
}
}
}

/* General housekeeping */
conn->ssl[sockindex].state = ssl_connection_complete;
conn->ssl[sockindex].ssl = ssl;
Expand Down

0 comments on commit 1bc4e22

Please sign in to comment.