From 605bb03185bded79d68d118fea0136100811bd28 Mon Sep 17 00:00:00 2001
From: Tatsuhiro Tsujikawa <tatsuhiro.t@gmail.com>
Date: Sun, 1 Apr 2012 21:58:17 +0900
Subject: [PATCH] Made hostname check conform to RFC 6125.

---
 lib/ssluse.c |   64 +++++++++++++++++++++++++++++++++------------------------
 1 files changed, 37 insertions(+), 27 deletions(-)

diff --git a/lib/ssluse.c b/lib/ssluse.c
index 74563c7..f559f55 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -1048,40 +1048,50 @@ static int asn1_output(const ASN1_UTCTIME *tm,
  * E.g.
  *  "foo.host.com" matches "*.host.com".
  *
- * We are a bit more liberal than RFC2818 describes in that we
- * accept multiple "*" in pattern (similar to what some other browsers do).
- * E.g.
- *  "abc.def.domain.com" should strickly not match "*.domain.com", but we
- *  don't consider "." to be important in CERT checking.
+ * We use the matching rule described in RFC6125, section 6.4.3.
+ * http://tools.ietf.org/html/rfc6125#section-6.4.3
  */
 #define HOST_NOMATCH 0
 #define HOST_MATCH   1
 
 static int hostmatch(const char *hostname, const char *pattern)
 {
-  for(;;) {
-    char c = *pattern++;
-
-    if(c == '\0')
-      return (*hostname ? HOST_NOMATCH : HOST_MATCH);
-
-    if(c == '*') {
-      c = *pattern;
-      if(c == '\0')      /* "*\0" matches anything remaining */
-        return HOST_MATCH;
-
-      while(*hostname) {
-        /* The only recursive function in libcurl! */
-        if(hostmatch(hostname++,pattern) == HOST_MATCH)
-          return HOST_MATCH;
-      }
-      break;
-    }
-
-    if(Curl_raw_toupper(c) != Curl_raw_toupper(*hostname++))
-      break;
+  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 strcasecmp(pattern, hostname) == 0 ? 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 ||
+     strncasecmp(pattern, "xn--", 4) == 0) {
+    wildcard_enabled = 0;
+  }
+  if(!wildcard_enabled) {
+    return strcasecmp(pattern, hostname) == 0 ? HOST_MATCH : HOST_NOMATCH;
+  }
+  hostname_label_end = strchr(hostname, '.');
+  if(hostname_label_end == NULL ||
+     strcasecmp(pattern_label_end, hostname_label_end) != 0) {
+    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;
   }
-  return HOST_NOMATCH;
+  prefixlen = pattern_wildcard - pattern;
+  suffixlen = pattern_label_end - (pattern_wildcard+1);
+  return strncasecmp(pattern, hostname, prefixlen) == 0 &&
+    strncasecmp(pattern_wildcard+1, hostname_label_end - suffixlen,
+                suffixlen) == 0 ?
+    HOST_MATCH : HOST_NOMATCH;
 }
 
 static int
-- 
1.7.9.1

