- Added support for wildcard certificates
 - Added support for multi-domain certificates through the X509 Subject Alternative Name extension

diff --git a/library/x509parse.c b/library/x509parse.c
index ec4fffc..af98843 100644
--- a/library/x509parse.c
+++ b/library/x509parse.c
@@ -785,6 +785,102 @@
 }
 
 /*
+ * SubjectAltName ::= GeneralNames
+ *
+ * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ *
+ * GeneralName ::= CHOICE {
+ *      otherName                       [0]     OtherName,
+ *      rfc822Name                      [1]     IA5String,
+ *      dNSName                         [2]     IA5String,
+ *      x400Address                     [3]     ORAddress,
+ *      directoryName                   [4]     Name,
+ *      ediPartyName                    [5]     EDIPartyName,
+ *      uniformResourceIdentifier       [6]     IA5String,
+ *      iPAddress                       [7]     OCTET STRING,
+ *      registeredID                    [8]     OBJECT IDENTIFIER }
+ *
+ * OtherName ::= SEQUENCE {
+ *      type-id    OBJECT IDENTIFIER,
+ *      value      [0] EXPLICIT ANY DEFINED BY type-id }
+ *
+ * EDIPartyName ::= SEQUENCE {
+ *      nameAssigner            [0]     DirectoryString OPTIONAL,
+ *      partyName               [1]     DirectoryString }
+ *
+ * NOTE: PolarSSL only parses and uses dNSName at this point.
+ */
+static int x509_get_subject_alt_name( unsigned char **p,
+                                      const unsigned char *end,
+                                      x509_sequence *subject_alt_name )
+{
+    int ret;
+    size_t len, tag_len;
+    asn1_buf *buf;
+    unsigned char tag;
+    asn1_sequence *cur = subject_alt_name;
+
+    /* Get main sequence tag */
+    if( ( ret = asn1_get_tag( p, end, &len,
+            ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 )
+        return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
+
+    if( *p + len != end )
+        return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
+                POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
+
+    while( *p < end )
+    {
+        if( ( end - *p ) < 1 )
+            return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
+                    POLARSSL_ERR_ASN1_OUT_OF_DATA );
+
+        tag = **p;
+        (*p)++;
+        if( ( ret = asn1_get_len( p, end, &tag_len ) ) != 0 )
+            return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret );
+
+        if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC )
+            return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
+                    POLARSSL_ERR_ASN1_UNEXPECTED_TAG );
+
+        if( tag != ( ASN1_CONTEXT_SPECIFIC | 2 ) )
+        {
+            *p += tag_len;
+            continue;
+        }
+
+        buf = &(cur->buf);
+        buf->tag = tag;
+        buf->p = *p;
+        buf->len = tag_len;
+        *p += buf->len;
+
+        /* Allocate and assign next pointer */
+        if (*p < end)
+        {
+            cur->next = (asn1_sequence *) malloc(
+                 sizeof( asn1_sequence ) );
+
+            if( cur->next == NULL )
+                return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
+                        POLARSSL_ERR_ASN1_MALLOC_FAILED );
+
+            cur = cur->next;
+        }
+    }
+
+    /* Set final sequence entry's next pointer to NULL */
+    cur->next = NULL;
+
+    if( *p != end )
+        return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS +
+                POLARSSL_ERR_ASN1_LENGTH_MISMATCH );
+
+    return( 0 );
+}
+
+/*
  * X.509 v3 extensions
  *
  * TODO: Perform all of the basic constraints tests required by the RFC
@@ -892,6 +988,15 @@
                 return ( ret );
             crt->ext_types |= EXT_EXTENDED_KEY_USAGE;
         }
+        else if( ( OID_SIZE( OID_SUBJECT_ALT_NAME ) == extn_oid.len ) &&
+                memcmp( extn_oid.p, OID_SUBJECT_ALT_NAME, extn_oid.len ) == 0 )
+        {
+            /* Parse extended key usage */
+            if( ( ret = x509_get_subject_alt_name( p, end_ext_octet,
+                    &crt->subject_alt_names ) ) != 0 )
+                return ( ret );
+            crt->ext_types |= EXT_SUBJECT_ALT_NAME;
+        }
         else
         {
             /* No parser found, skip extension */
@@ -2866,6 +2971,35 @@
     return flags;
 }
 
+int x509_wildcard_verify( const char *cn, x509_name *name )
+{
+    size_t i;
+    size_t cn_idx = 0;
+
+    if( name->val.len < 3 || name->val.p[0] != '*' || name->val.p[1] != '.' )
+        return( 0 );
+
+    for( i = 0; i < strlen( cn ); ++i )
+    {
+        if( cn[i] == '.' )
+        {
+            cn_idx = i;
+            break;
+        }
+    }
+
+    if( cn_idx == 0 )
+        return( 0 );
+
+    if( memcmp( name->val.p + 1, cn + cn_idx, name->val.len - 1 ) == 0 &&
+        strlen( cn ) - cn_idx == name->val.len - 1 )
+    {
+        return( 1 );
+    }
+
+    return( 0 );
+}
+
 /*
  * Verify the certificate validity
  */
@@ -2882,6 +3016,7 @@
     x509_cert *parent;
     x509_name *name;
     unsigned char hash[64];
+    x509_sequence *cur = NULL;
 
     *flags = 0;
 
@@ -2895,16 +3030,39 @@
 
         while( name != NULL )
         {
-            if( memcmp( name->oid.p, OID_CN,  3 ) == 0 &&
-                memcmp( name->val.p, cn, cn_len ) == 0 &&
-                name->val.len == cn_len )
-                break;
+            if( memcmp( name->oid.p, OID_CN,  3 ) == 0 )
+            {
+                if( memcmp( name->val.p, cn, cn_len ) == 0 &&
+                    name->val.len == cn_len )
+                    break;
+
+                if( memcmp( name->val.p, "*.", 2 ) == 0 &&
+                    x509_wildcard_verify( cn, name ) )
+                    break;
+            }
 
             name = name->next;
         }
 
         if( name == NULL )
-            *flags |= BADCERT_CN_MISMATCH;
+        {
+            if( crt->ext_types & EXT_SUBJECT_ALT_NAME )
+            {
+                cur = &crt->subject_alt_names;
+
+                while( cur != NULL )
+                {
+                    if( memcmp( cn, cur->buf.p, cn_len ) == 0 &&
+                        cur->buf.len == cn_len )
+                        break;
+
+                    cur = cur->next;
+                }
+            }
+
+            if( cur == NULL )
+                *flags |= BADCERT_CN_MISMATCH;
+        }
     }
 
     /*