- fix broken match_component()
- fix \0 in CN and subjAltname
- don't use CN if dNSName in subjAltname
- use most specific (=last) CN instead of the first
Index: libesmtp-1.0.4/smtp-tls.c
===================================================================
--- libesmtp-1.0.4.orig/smtp-tls.c
+++ libesmtp-1.0.4/smtp-tls.c
@@ -439,16 +439,24 @@ static int
 match_component (const char *dom, const char *edom,
                  const char *ref, const char *eref)
 {
+  int wildcard = 0;
+  
   while (dom < edom && ref < eref)
     {
       /* Accept a final '*' in the reference as a wildcard */
       if (*ref == '*' && ref + 1 == eref)
-        break;
+	{
+          wildcard = 1;
+          break;
+	}
       /* compare the domain name case insensitive */
       if (!(*dom == *ref || tolower (*dom) == tolower (*ref)))
         return 0;
       ref++, dom++;
     }
+  if (!wildcard && (dom < edom || ref < eref))
+    return 0;
+
   return 1;
 }
 
@@ -492,7 +500,6 @@ static int
 check_acceptable_security (smtp_session_t session, SSL *ssl)
 {
   X509 *cert;
-  char buf[256];
   int bits;
   long vfy_result;
   int ok;
@@ -541,65 +548,71 @@ check_acceptable_security (smtp_session_
     }
   else
     {
-      int i, j, extcount;
+      char buf[256] = {0};
+      STACK_OF(GENERAL_NAME) *altnames;
+      int hasaltname = 0;
 
-      extcount = X509_get_ext_count (cert);
-      for (i = 0; i < extcount; i++)
+      altnames = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL);
+      if (altnames != NULL)
 	{
-	  const char *extstr;
-	  X509_EXTENSION *ext = X509_get_ext (cert, i);
-
-	  extstr = OBJ_nid2sn (OBJ_obj2nid (X509_EXTENSION_get_object (ext)));
-	  if (strcmp (extstr, "subjectAltName") == 0)
+	  int i;
+	  for (i = 0; i < sk_GENERAL_NAME_num (altnames); ++i)
 	    {
-	      unsigned char *data;
-	      STACK_OF(CONF_VALUE) *val;
-	      CONF_VALUE *nval;
-	      X509V3_EXT_METHOD *meth;
-	      void *ext_str = NULL;
-	      int stack_len;
-
-	      meth = X509V3_EXT_get (ext);
-	      if (meth == NULL)
-		break;
-	      data = ext->value->data;
-#if (OPENSSL_VERSION_NUMBER > 0x00907000L)
-	      if (meth->it)
-		ext_str = ASN1_item_d2i (NULL, &data, ext->value->length,
-		                         ASN1_ITEM_ptr (meth->it));
-	      else
-#endif
-	      ext_str = meth->d2i (NULL, &data, ext->value->length);
-	      val = meth->i2v (meth, ext_str, NULL);
-	      stack_len = sk_CONF_VALUE_num (val);
-	      for (j = 0; j < stack_len; j++)
+	      GENERAL_NAME *name = sk_GENERAL_NAME_value (altnames, i);
+	      if (name->type == GEN_DNS)
 		{
-		  nval = sk_CONF_VALUE_value (val, j);
-		  if (strcmp (nval->name, "DNS") == 0
-		      && match_domain (session->host, nval->value))
+		  const ASN1_IA5STRING* ia5str = name->d.ia5;
+		  hasaltname = 1;
+		  if (strlen ((const char *)ia5str->data) == ia5str->length
+		      && match_domain (session->host, (const char *)ia5str->data))
+		    ok = 1;
+		  else
 		    {
-		      ok = 1;
-		      break;
+		      *buf = 0;
+		      strncat(buf, (const char *)ia5str->data, sizeof(buf)-1);
 		    }
 		}
+	      // TODO: handle GEN_IPADD
 	    }
-	  if (ok)
-	    break;
+	    sk_GENERAL_NAME_pop_free (altnames, GENERAL_NAME_free);
 	}
-      if (!ok)
+
+      if (!hasaltname)
 	{
-	  /* Matching by subjectAltName failed, try commonName */
-	  X509_NAME_get_text_by_NID (X509_get_subject_name (cert),
-				     NID_commonName, buf, sizeof buf);
-	  if (!match_domain (session->host, buf) != 0)
+	  X509_NAME *subj = X509_get_subject_name(cert);
+	  if (subj)
 	    {
-	      if (session->event_cb != NULL)
-		(*session->event_cb) (session, SMTP_EV_WRONG_PEER_CERTIFICATE,
-				      session->event_cb_arg, &ok, buf, ssl);
+	      ASN1_STRING *cn;
+	      int idx, i = -1;
+	      do
+		{
+		  idx = i;
+		}
+	      while((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) >= 0);
+
+	      if (idx >= 0 && (cn = X509_NAME_ENTRY_get_data (X509_NAME_get_entry (subj, idx))))
+		{
+		  unsigned char* str = NULL;
+		  int len = ASN1_STRING_to_UTF8 (&str, cn);
+		  if (str)
+		    {
+		      if (strlen((char*)str) == len && match_domain(session->host, (char*)str))
+			ok = 1;
+		      else
+			{
+			  *buf = 0;
+			  strncat(buf, (char *)str, sizeof(buf)-1);
+			}
+		      OPENSSL_free(str);
+		    }
+		}
 	    }
-	  else
-	    ok = 1;
 	}
+
+      if (!ok && session->event_cb != NULL)
+	(*session->event_cb) (session, SMTP_EV_WRONG_PEER_CERTIFICATE,
+	  session->event_cb_arg, &ok, buf, ssl);
+
       X509_free (cert);
     }
   return ok;