Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

RFC2818 checks for axTLS #45

Closed
wants to merge 1 commit into from

3 participants

@okoeroo

Adding RFC2818 compliance checks to axTLS.

Completely new are the SubjectAltName checks. The peer CN field fallback is implemented mimicking the OpenSSL behaviour. A setting of VERIFYHOST 0 or 1 is equal to each other and only effects the behaviour of the peer certificates most significants CN field checking.

To implement a proper hostname matching rule with wildcard support, I've taken the liberty to do a one-to-one copy of the static hostcheck function from the OpenSSL backend as new static functions in axTLS.

@okoeroo okoeroo Adding RFC2818 compliance checks to axTLS. Completely new are the Sub…
…jectAltName checks. The peer CN field fallback is implemented matching the OpenSSL behaviour. VERIFYHOST 0 == 1.
1bc4e22
@okoeroo okoeroo closed this
@camster444

Hi there, I'm the developer of axTLS. Could you give me a patch of the work you've done here? I'll see if I can port the changes in.

@bagder
Owner

Hi, the patch for the above commit can be found here: https://github.com/okoeroo/curl/commit/1bc4e229dd508cb31025f3151d8b0b2a2b87e249.patch

You'll see that it uses existing host check functions present in libcurl. The current curl git version is slightly modified since that commit.

@okoeroo

Hi,
I haven't touched to axTLS code, that is what you are asking. I've reused the hostname matching code already present in the OpenSSL backend for libcurl and reused it for hostname machine rules in the axTLS backend.

In the end I've implemented a crawl through the SubjectAltNames, and in case these are absent I grab the common name. I then match the host libcurl is connecting to with this information to make the check work.

To the best of my reading axTLS does not feature a function that does hostname matching with the certificate from the host. OpenSSL also doesn't feature such a function, GnuTLS does. With PolarSSL this check is implicit for example. Thus I don't expect a change in axTLS to be required.

Does this answer your question?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Nov 3, 2012
  1. @okoeroo

    Adding RFC2818 compliance checks to axTLS. Completely new are the Sub…

    okoeroo authored
    …jectAltName checks. The peer CN field fallback is implemented matching the OpenSSL behaviour. VERIFYHOST 0 == 1.
This page is out of date. Refresh to see the latest.
Showing with 111 additions and 7 deletions.
  1. +111 −7 lib/axtls.c
View
118 lib/axtls.c
@@ -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)
{
@@ -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;
@@ -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;
Something went wrong with that request. Please try again.