/* Check certificate identity. Returns zero if identity matches; 1 if * identity does not match, or <0 if the certificate had no identity. * If 'identity' is non-NULL, store the malloc-allocated identity in * *identity. Logic specified by RFC 2818 and RFC 3280. */ static int check_identity(const ne_uri *server, X509 *cert, char **identity) { STACK_OF(GENERAL_NAME) *names; int match = 0, found = 0; const char *hostname; hostname = server ? server->host : ""; names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (names) { int n; /* subjectAltName contains a sequence of GeneralNames */ for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) { GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n); /* handle dNSName and iPAddress name extensions only. */ if (nm->type == GEN_DNS) { char *name = dup_ia5string(nm->d.ia5); if (identity && !found) *identity = ne_strdup(name); match = ne__ssl_match_hostname(name, strlen(name), hostname); free(name); found = 1; } else if (nm->type == GEN_IPADD) { /* compare IP address with server IP address. */ ne_inet_addr *ia; if (nm->d.ip->length == 4) ia = ne_iaddr_make(ne_iaddr_ipv4, nm->d.ip->data); else if (nm->d.ip->length == 16) ia = ne_iaddr_make(ne_iaddr_ipv6, nm->d.ip->data); else ia = NULL; /* ne_iaddr_make returns NULL if address type is unsupported */ if (ia != NULL) { /* address type was supported. */ char buf[128]; match = strcmp(hostname, ne_iaddr_print(ia, buf, sizeof buf)) == 0; found = 1; ne_iaddr_free(ia); } else { NE_DEBUG(NE_DBG_SSL, "iPAddress name with unsupported " "address type (length %d), skipped.\n", nm->d.ip->length); } } else if (nm->type == GEN_URI) { char *name = dup_ia5string(nm->d.ia5); ne_uri uri; if (ne_uri_parse(name, &uri) == 0 && uri.host && uri.scheme) { ne_uri tmp; if (identity && !found) *identity = ne_strdup(name); found = 1; if (server) { /* For comparison purposes, all that matters is * host, scheme and port; ignore the rest. */ memset(&tmp, 0, sizeof tmp); tmp.host = uri.host; tmp.scheme = uri.scheme; tmp.port = uri.port; match = ne_uri_cmp(server, &tmp) == 0; } } ne_uri_free(&uri); free(name); } } /* free the whole stack. */ sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); } /* Check against the commonName if no DNS alt. names were found, * as per RFC3280. */ if (!found) { X509_NAME *subj = X509_get_subject_name(cert); X509_NAME_ENTRY *entry; ne_buffer *cname = ne_buffer_ncreate(30); int idx = -1, lastidx; /* find the most specific commonName attribute. */ do { lastidx = idx; idx = X509_NAME_get_index_by_NID(subj, NID_commonName, lastidx); } while (idx >= 0); if (lastidx < 0) { /* no commonName attributes at all. */ ne_buffer_destroy(cname); return -1; } /* extract the string from the entry */ entry = X509_NAME_get_entry(subj, lastidx); if (append_dirstring(cname, X509_NAME_ENTRY_get_data(entry))) { ne_buffer_destroy(cname); return -1; } if (identity) *identity = ne_strdup(cname->data); match = ne__ssl_match_hostname(cname->data, cname->used - 1, hostname); ne_buffer_destroy(cname); } NE_DEBUG(NE_DBG_SSL, "Identity match for '%s': %s\n", hostname, match ? "good" : "bad"); return match ? 0 : 1; }
/* static int check_identity(const nussl_uri *server, X509 *cert, char **identity) */ static int check_identity(const char *expected_hostname, X509 * cert, char **identity) { STACK_OF(GENERAL_NAME) * names; int match = 0, found = 0; char *found_hostname = NULL; /* hostname = server ? server->host : ""; */ names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); /* if expected_hostname is NULL, do not check subjectAltName, * we are only looking for the CN */ if (names && expected_hostname != NULL) { int n; /* subjectAltName contains a sequence of GeneralNames */ for (n = 0; n < sk_GENERAL_NAME_num(names) && !match; n++) { GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, n); /* handle dNSName and iPAddress name extensions only. */ if (nm->type == GEN_DNS) { found_hostname = dup_ia5string(nm->d.ia5); match = match_hostname(found_hostname, expected_hostname); if (match) { found = 1; if (identity) *identity = nussl_strdup(found_hostname); } } else if (nm->type == GEN_IPADD) { /* compare IP address with server IP address. */ nussl_inet_addr *ia; if (nm->d.ip->length == 4) ia = nussl_iaddr_make(nussl_iaddr_ipv4, nm->d.ip->data); else if (nm->d.ip->length == 16) ia = nussl_iaddr_make(nussl_iaddr_ipv6, nm->d.ip->data); else ia = NULL; /* nussl_iaddr_make returns NULL if address type is unsupported */ /* if (ia != NULL) { /\* address type was supported. *\/ */ /* char buf[128]; */ /* match = strcmp(hostname, */ /* nussl_iaddr_print(ia, buf, sizeof buf)) == 0; */ /* found = 1; */ /* nussl_iaddr_free(ia); */ /* } else { */ /* NUSSL_DEBUG(NUSSL_DBG_SSL, "iPAddress name with unsupported " */ /* "address type (length %d), skipped.\n", */ /* nm->d.ip->length); */ /* } */ } /* else if (nm->type == GEN_URI) { */ /* char *name = dup_ia5string(nm->d.ia5); */ /* nussl_uri uri; */ /* if (nussl_uri_parse(name, &uri) == 0 && uri.host && uri.scheme) { */ /* nussl_uri tmp; */ /* if (identity && !found) *identity = nussl_strdup(name); */ /* found = 1; */ /* if (server) { */ /* /\* For comparison purposes, all that matters is */ /* * host, scheme and port; ignore the rest. *\/ */ /* memset(&tmp, 0, sizeof tmp); */ /* tmp.host = uri.host; */ /* tmp.scheme = uri.scheme; */ /* tmp.port = uri.port; */ /* match = nussl_uri_cmp(server, &tmp) == 0; */ /* } */ /* } */ /* nussl_uri_free(&uri); */ /* nussl_free(name); */ /* } */ } } /* free the whole stack. */ if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); /* Check against the commonName if no DNS alt. names were found, * as per RFC3280. */ if (!found) { X509_NAME *subj = X509_get_subject_name(cert); X509_NAME_ENTRY *entry; nussl_buffer *cname = nussl_buffer_ncreate(30); int idx = -1, lastidx; /* find the most specific commonName attribute. */ do { lastidx = idx; idx = X509_NAME_get_index_by_NID(subj, NID_commonName, lastidx); } while (idx >= 0); /* check commonName attribute if present */ if (lastidx >= 0) { /* extract the string from the entry */ entry = X509_NAME_get_entry(subj, lastidx); if (append_dirstring(cname, X509_NAME_ENTRY_get_data(entry))) { nussl_buffer_destroy(cname); return -1; } found_hostname = nussl_strdup(cname->data); if (expected_hostname != NULL) match = match_hostname(found_hostname, expected_hostname); } nussl_buffer_destroy(cname); } if (found_hostname == NULL) return 1; /*NUSSL_DEBUG(NUSSL_DBG_SSL, "Identity match for '%s' (identity: %s): %s\n", expected_hostname, found_hostname, match ? "good" : "bad");*/ if (identity != NULL) *identity = nussl_strdup(found_hostname); nussl_free(found_hostname); return match ? 0 : 1; }