loudmouth/lm-ssl-openssl.c
changeset 718 a1d2735ccda8
parent 704 d682ae8d7d3a
child 719 647363d68e7a
equal deleted inserted replaced
717:de936894985f 718:a1d2735ccda8
   116     /* As this callback doesn't get auxiliary pointer parameter we
   116     /* As this callback doesn't get auxiliary pointer parameter we
   117      * cannot really use this. However, we can retrieve results later. */
   117      * cannot really use this. However, we can retrieve results later. */
   118     return 1;
   118     return 1;
   119 }
   119 }
   120 
   120 
       
   121 static gboolean
       
   122 ssl_match_domain_name (const gchar *server, const gchar *domain)
       
   123 {
       
   124     if (domain[0]=='*' && domain[1]=='.') {
       
   125         /* leftmost part wildcard */
       
   126         ++domain;
       
   127 
       
   128         if (strchr(domain, '*') != NULL) {
       
   129             /* multiple wildcards not allowed */
       
   130             return FALSE;
       
   131         }
       
   132 
       
   133         server = strchr(server, '.'); /* eat the leftmost part */
       
   134         if (server == NULL) {
       
   135             return FALSE;
       
   136         }
       
   137 
       
   138         /* fall thru for wildcard match */
       
   139     }
       
   140 
       
   141     return (!strcasecmp(server, domain));
       
   142 }
       
   143 
   121 /* side effect: fills the ssl->fingerprint buffer */
   144 /* side effect: fills the ssl->fingerprint buffer */
   122 static gboolean
   145 static gboolean
   123 ssl_verify_certificate (LmSSL *ssl, const gchar *server)
   146 ssl_verify_certificate (LmSSL *ssl, const gchar *server)
   124 {
   147 {
   125     gboolean retval = TRUE;
   148     gboolean retval = TRUE;
       
   149     gboolean match_result = FALSE;
   126     LmSSLBase *base;
   150     LmSSLBase *base;
   127     long verify_res;
   151     long verify_res;
   128     int rc;
   152     int rc;
   129     const EVP_MD *digest = EVP_sha256();
   153     const EVP_MD *digest = EVP_sha256();
   130     unsigned int digest_len;
   154     unsigned int digest_len;
   217     crt_subj = X509_get_subject_name(srv_crt);
   241     crt_subj = X509_get_subject_name(srv_crt);
   218     cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);
   242     cn = (gchar *) g_malloc0(LM_SSL_CN_MAX + 1);
   219 
   243 
   220     /* FWB: deprecated call, can only get first entry */
   244     /* FWB: deprecated call, can only get first entry */
   221     if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {
   245     if (X509_NAME_get_text_by_NID(crt_subj, NID_commonName, cn, LM_SSL_CN_MAX) > 0) {
   222         gchar *domain = cn;
       
   223 
   246 
   224         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
   247         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
   225                "%s: server = '%s', cn = '%s'\n",
   248                "%s: server = '%s', cn = '%s'\n",
   226                __FILE__, server, cn);
   249                __FILE__, server, cn);
   227 
   250 
   228         if (domain != NULL) {
   251         if (cn != NULL && ssl_match_domain_name(server, cn) {
   229 
   252             match_result = TRUE;
   230             if ((cn[0] == '*') && (cn[1] == '.')) {
   253         } else {
   231                 /*
   254             /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */
   232                  * FWB: huh? ever tested?
   255         }
   233                  * server="sub.domain.tld";
   256     } else {
   234                  * cn="*.domain.tld";
   257         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
   235                  * domain=strstr(cn, server); ???
   258                "X509_NAME_get_text_by_NID() failed");
   236                  */
   259     }
   237                 /* domain = strstr (cn, server); */
   260 
   238                 server = strchr(server, '.') + 1;
   261     /* RFC6125: "...However, it is perfectly acceptable for the subject field to be empty,
   239                 domain = cn + 2;
   262      * as long as the certificate contains a subject alternative name ("subjectAltName")
       
   263      * extension that includes at least one subjectAltName entry"
       
   264      */
       
   265     if (!match_result) {
       
   266         /* FWB: CN doesn't match, try SANs */
       
   267         int subject_alt_names_nb = -1;
       
   268         int san_counter;
       
   269         STACK_OF(GENERAL_NAME) *subject_alt_names = NULL;
       
   270 
       
   271         // Try to extract the names within the SAN extension from the certificate
       
   272         subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL);
       
   273         if (subject_alt_names != NULL) {
       
   274 
       
   275             // Check each name within the extension
       
   276             subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names);
       
   277             for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) {
       
   278                 const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter);
       
   279                 if (current_name->type == GEN_DNS) {
       
   280                     // Current name is a DNS name, let's check it, it's ASCII
       
   281                     if (ssl_match_domain_name(server, (const char *)current_name->d.dNSName->data)) {
       
   282                         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data);
       
   283                         match_result = TRUE; /* break; */
       
   284                     } else {
       
   285                         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data);
       
   286                     }
       
   287                 }
   240             }
   288             }
   241 
   289 
   242             if (strncasecmp (server, domain, LM_SSL_CN_MAX) != 0) {
   290         }
   243                 /* FWB: CN doesn't match, try SANs */
   291 
   244                 int subject_alt_names_nb = -1;
   292         sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
   245                 int san_result = 0;
   293     }
   246                 int san_counter;
   294 
   247                 STACK_OF(GENERAL_NAME) *subject_alt_names = NULL;
   295     if (!match_result) {
   248 
   296         if (base->func (ssl,
   249                 /* g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: CN does not match server name\n", __FILE__); */
   297                         LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
   250                 // Try to extract the names within the SAN extension from the certificate
   298                         base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
   251                 subject_alt_names = X509_get_ext_d2i((X509 *) srv_crt, NID_subject_alt_name, NULL, NULL);
   299             retval = FALSE;
   252                 if (subject_alt_names != NULL) {
   300         }
   253 
       
   254                     // Check each name within the extension
       
   255                     subject_alt_names_nb = sk_GENERAL_NAME_num(subject_alt_names);
       
   256                     for (san_counter=0; san_counter<subject_alt_names_nb; san_counter++) {
       
   257                         const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(subject_alt_names, san_counter);
       
   258                         if (current_name->type == GEN_DNS) {
       
   259                             // Current name is a DNS name, let's check it, it's ASCII
       
   260                             if (strcasecmp(server, (char *)current_name->d.dNSName->data) == 0) {
       
   261                                 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s' - MATCH\n", __FILE__, current_name->d.dNSName->data);
       
   262                                 san_result = 1; /* break; */
       
   263                             } else {
       
   264                                 g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL, "%s: found SAN '%s'\n", __FILE__, current_name->d.dNSName->data);
       
   265                             }
       
   266                         }
       
   267                     }
       
   268 
       
   269                 }
       
   270                 sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
       
   271                 if (!san_result) goto cn_and_san_mismatch;
       
   272             } /* SAN */
       
   273         } else {
       
   274             cn_and_san_mismatch:
       
   275             if (base->func (ssl,
       
   276                             LM_SSL_STATUS_CERT_HOSTNAME_MISMATCH,
       
   277                             base->func_data) != LM_SSL_RESPONSE_CONTINUE) {
       
   278                 retval = FALSE;
       
   279             }
       
   280         }
       
   281     } else {
       
   282         g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
       
   283                "X509_NAME_get_text_by_NID() failed");
       
   284     }
   301     }
   285 
   302 
   286     g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
   303     g_log (LM_LOG_DOMAIN, LM_LOG_LEVEL_SSL,
   287            "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n",
   304            "%s:\n\tIssuer: %s\n\tSubject: %s\n\tFor: %s\n",
   288            __FILE__,
   305            __FILE__,