static int check_alt_names(X509 *cert, const char *hostname) { STACK_OF(GENERAL_NAME) *alt_names; int i, num; int ret = 1; union { struct in_addr v4; struct in6_addr v6; } ip; unsigned ip_size = 0; /* check whether @hostname is an ip address */ if (strchr(hostname, ':') != NULL) { ip_size = 16; ret = inet_pton(AF_INET6, hostname, &ip.v6); } else { ip_size = 4; ret = inet_pton(AF_INET, hostname, &ip.v4); } if (ret == 0) return -1; ret = -1; alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (!alt_names) return ret; num = sk_GENERAL_NAME_num(alt_names); tdsdump_log(TDS_DBG_INFO1, "Alt names number %d\n", num); for (i = 0; i < num; ++i) { const char *altptr; size_t altlen; const GENERAL_NAME *name = sk_GENERAL_NAME_value(alt_names, i); if (!name) continue; altptr = (const char *) ASN1_STRING_data(name->d.ia5); altlen = (size_t) ASN1_STRING_length(name->d.ia5); if (name->type == GEN_DNS && ip_size == 0) { ret = 0; if (!check_name_match(name->d.dNSName, hostname)) continue; } else if (name->type == GEN_IPADD && ip_size != 0) { ret = 0; if (altlen != ip_size || memcmp(altptr, &ip, altlen) != 0) continue; } else { continue; } sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); return 1; } sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); return ret; }
static int verify_hostname(X509 *cert, const char *hostname) { int len; X509_NAME *subj; char cname[1000]; int i, found; STACK_OF(GENERAL_NAME) *subj_alt_names; /* try the DNS subjectAltNames */ found = 0; if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) { int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names); for (i = 0; !found && i < num_subj_alt_names; i++) { GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i); if (subj_alt_name->type == GEN_DNS && strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length && host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data))) found = 1; } sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free); } if (found) return 0; /* try the common name */ if (!(subj = X509_get_subject_name(cert))) return error("cannot get certificate subject"); if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0) return error("cannot get certificate common name"); if (strlen(cname) == (size_t)len && host_matches(hostname, cname)) return 0; return error("certificate owner '%s' does not match hostname '%s'", cname, hostname); }
/* Gets information about the peer's X509 cert as a tsi_peer object. */ static tsi_result peer_from_x509(X509* cert, int include_certificate_type, tsi_peer* peer) { /* TODO(jboeuf): Maybe add more properties. */ GENERAL_NAMES* subject_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); int subject_alt_name_count = (subject_alt_names != NULL) ? sk_GENERAL_NAME_num(subject_alt_names) : 0; size_t property_count = (include_certificate_type ? 1 : 0) + 1 /* common name */ + subject_alt_name_count; tsi_result result = tsi_construct_peer(property_count, peer); if (result != TSI_OK) return result; do { if (include_certificate_type) { result = tsi_construct_string_peer_property_from_cstring( TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, &peer->properties[0]); if (result != TSI_OK) break; } result = peer_property_from_x509_common_name( cert, &peer->properties[include_certificate_type ? 1 : 0]); if (result != TSI_OK) break; if (subject_alt_name_count != 0) { result = add_subject_alt_names_properties_to_peer(peer, subject_alt_names, subject_alt_name_count); if (result != TSI_OK) break; } } while (0); if (subject_alt_names != NULL) { sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); } if (result != TSI_OK) tsi_peer_destruct(peer); return result; }
/** * Tries to find a match for hostname in the certificate's Subject Alternative Name extension. * * Returns MatchFound if a match was found. * Returns MatchNotFound if no matches were found. * Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. * Returns NoSANPresent if the SAN extension was not present in the certificate. */ static HostnameValidationResult matches_subject_alternative_name(const char *hostname, const X509 *server_cert) { HostnameValidationResult result = MatchNotFound; int i; int san_names_nb = -1; STACK_OF(GENERAL_NAME) *san_names = NULL; // Try to extract the names within the SAN extension from the certificate san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL); if (san_names == NULL) { return NoSANPresent; } san_names_nb = sk_GENERAL_NAME_num(san_names); // Check each name within the extension for (i=0; i<san_names_nb; i++) { const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); if (current_name->type == GEN_DNS) { // Current name is a DNS name, let's check it result = validate_name(hostname, current_name->d.dNSName); if (result != MatchNotFound) { break; } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); return result; }
/* Make sure we do the right thing. Add here if you convert ones in tree */ int main(int argc, char **argv) { ASN1_INTEGER_free(NULL); ASN1_OBJECT_free(NULL); ASN1_OCTET_STRING_free(NULL); BIO_free_all(NULL); DIST_POINT_free(NULL); EVP_PKEY_free(NULL); GENERAL_NAME_free(NULL); GENERAL_SUBTREE_free(NULL); NAME_CONSTRAINTS_free(NULL); sk_GENERAL_NAME_pop_free(NULL, GENERAL_NAME_free); sk_X509_NAME_ENTRY_pop_free(NULL, X509_NAME_ENTRY_free); X509_NAME_ENTRY_free(NULL); printf("PASS\n"); return (0); }
/* based on verify_extract_name from tls_client.c in postfix */ static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname) { int gen_index, gen_count; gboolean matched = FALSE, has_dns_name = FALSE; const char *cert_dns_name; char *cert_subject_cn; const GENERAL_NAME *gn; STACK_OF(GENERAL_NAME) * gens; GString *alt_names; alt_names = g_string_new(""); /* Verify the dNSName(s) in the peer certificate against the hostname. */ gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); if (gens) { gen_count = sk_GENERAL_NAME_num(gens); for (gen_index = 0; gen_index < gen_count && !matched; ++gen_index) { gn = sk_GENERAL_NAME_value(gens, gen_index); if (gn->type != GEN_DNS) continue; /* Even if we have an invalid DNS name, we still ultimately ignore the CommonName, because subjectAltName:DNS is present (though malformed). */ has_dns_name = TRUE; cert_dns_name = tls_dns_name(gn); if (cert_dns_name && *cert_dns_name) { g_string_append_printf(alt_names, " '%s'", cert_dns_name); matched = match_hostname(cert_dns_name, hostname); } } /* Free stack *and* member GENERAL_NAME objects */ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } if (has_dns_name) { if (! matched) { /* The CommonName in the issuer DN is obsolete when SubjectAltName is available. */ g_warning("None of the Subject Alt Names%s in the certificate match hostname '%s'", alt_names->str, hostname); } g_string_free(alt_names, TRUE); return matched; } else { /* No subjectAltNames, look at CommonName */ g_string_free(alt_names, TRUE); cert_subject_cn = tls_text_name(X509_get_subject_name(cert), NID_commonName); if (cert_subject_cn && *cert_subject_cn) { matched = match_hostname(cert_subject_cn, hostname); if (! matched) { g_warning("SSL certificate common name '%s' doesn't match host name '%s'", cert_subject_cn, hostname); } } else { g_warning("No subjectAltNames and no valid common name in certificate"); } free(cert_subject_cn); } return matched; }
void AC_ATT_HOLDER_free(AC_ATT_HOLDER *a) { if (a == NULL) return; sk_GENERAL_NAME_pop_free(a->grantor, GENERAL_NAME_free); sk_AC_ATTRIBUTE_pop_free(a->attributes, AC_ATTRIBUTE_free); OPENSSL_free(a); }
int eventer_ssl_get_san_values(eventer_ssl_ctx_t *ctx, X509_STORE_CTX *x509ctx) { STACK_OF(GENERAL_NAME) * altnames; X509 *peer; int pos = 0; if(!x509ctx) return 0; peer = X509_STORE_CTX_get_current_cert(x509ctx); altnames = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL); if (altnames) { int i; int numalts = sk_GENERAL_NAME_num(altnames); char cn[4096]; mtev_boolean written = mtev_false; memset(cn, 0, 4096); for (i = 0; i < numalts; i++) { const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); if (check->type != GEN_DNS) { continue; } ASN1_STRING *data = check->d.dNSName; if (written) { /* Leave space for comma, space, data, and null byte */ if (data->length + pos > (int)sizeof(cn) - 3) { continue; } cn[pos] = ','; cn[pos+1] = ' '; pos+=2; } else { /* Leave space for data and null byte */ if (data->length + pos > (int)sizeof(cn) - 1) { continue; } written = mtev_true; } memcpy(cn+pos, data->data, data->length); cn[data->length+pos] = '\0'; pos = strlen(cn); } if (pos > 0) { if (ctx->san_list != NULL) { free(ctx->san_list); } ctx->san_list = strdup(cn); } sk_GENERAL_NAME_pop_free(altnames, GENERAL_NAME_free); } return 1; }
/* return an array of (RFC 6125 coined) DNS-IDs and CN-IDs in a certificate */ BOOL SSL_X509_getIDs(apr_pool_t *p, X509 *x509, apr_array_header_t **ids) { STACK_OF(GENERAL_NAME) *names; BIO *bio; X509_NAME *subj; char **cpp; int i, n; if (!x509 || !(*ids = apr_array_make(p, 0, sizeof(char *)))) { *ids = NULL; return FALSE; } /* First, the DNS-IDs (dNSName entries in the subjectAltName extension) */ if ((names = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL)) && (bio = BIO_new(BIO_s_mem()))) { GENERAL_NAME *name; for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { name = sk_GENERAL_NAME_value(names, i); if (name->type == GEN_DNS) { ASN1_STRING_print_ex(bio, name->d.ia5, ASN1_STRFLGS_ESC_CTRL| ASN1_STRFLGS_UTF8_CONVERT); n = BIO_pending(bio); if (n > 0) { cpp = (char **)apr_array_push(*ids); *cpp = apr_palloc(p, n+1); n = BIO_read(bio, *cpp, n); (*cpp)[n] = NUL; } } } BIO_free(bio); } if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); /* Second, the CN-IDs (commonName attributes in the subject DN) */ subj = X509_get_subject_name(x509); i = -1; while ((i = X509_NAME_get_index_by_NID(subj, NID_commonName, i)) != -1) { cpp = (char **)apr_array_push(*ids); *cpp = SSL_X509_NAME_ENTRY_to_string(p, X509_NAME_get_entry(subj, i)); } return apr_is_empty_array(*ids) ? FALSE : TRUE; }
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_cert_get_altnames(struct tls *ctx, struct tls_cert *cert, X509 *x509_cert) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; GENERAL_NAME *altname; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return 0; count = sk_GENERAL_NAME_num(altname_stack); if (count == 0) { rv = 0; goto out; } cert->subject_alt_names = calloc(sizeof (struct tls_cert_general_name), count); if (cert->subject_alt_names == NULL) { tls_set_error(ctx, "calloc"); goto out; } for (i = 0; i < count; i++) { altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type == GEN_DNS) { rv = tls_load_alt_ia5string(ctx, altname->d.dNSName, cert, TLS_CERT_GNAME_DNS, 1, UB_GNAME_DNS, "dns"); } else if (altname->type == GEN_EMAIL) { rv = tls_load_alt_ia5string(ctx, altname->d.rfc822Name, cert, TLS_CERT_GNAME_EMAIL, 1, UB_GNAME_EMAIL, "email"); } else if (altname->type == GEN_URI) { rv = tls_load_alt_ia5string(ctx, altname->d.uniformResourceIdentifier, cert, TLS_CERT_GNAME_URI, 1, UB_GNAME_URI, "uri"); } else if (altname->type == GEN_IPADD) { rv = tls_load_alt_ipaddr(ctx, altname->d.iPAddress, cert); } else { /* ignore unknown types */ rv = 0; } if (rv < 0) goto out; } rv = 0; out: sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; }
int get_subjectaltname(X509* cert, char* buf, int buf_len){ /* Copy "," separated dNSName values of the subjectAltName extension, to pBuf */ GENERAL_NAMES *gens; GENERAL_NAME *gen; int i; char *sub_str= buf; int space_taken=0; int space_left= buf_len; gens = X509_get_ext_d2i(cert , NID_subject_alt_name, NULL, NULL); for(i = 0; i < sk_GENERAL_NAME_num(gens); i++){ gen = sk_GENERAL_NAME_value(gens, i); syslog(LOG_INFO,"1sub_str"); if((gen->type == GEN_DNS)||(gen->type == GEN_URI)){ if(0 < space_left) space_taken= copy_csv_to_buffer(sub_str, (char*)gen->d.ia5->data, buf_len, space_left); space_left= space_left -space_taken; } if(gen->type == GEN_IPADD) { if(0 < space_left) { const int oline_len = 40; char oline[oline_len]; oline[0]='\0'; ip_to_string(oline, oline_len, gen); space_taken= copy_csv_to_buffer(sub_str, oline, buf_len, space_left); space_left= space_left - space_taken; } } syslog(LOG_INFO,"2 sub_str: %s space_taken:%d space_left:%d",sub_str, space_taken, space_left); } sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); return CERT_OK; }
/** * Tries to find a match for hostname in the certificate's Subject Alternative Name extension. * * Returns MatchFound if a match was found. * Returns MatchNotFound if no matches were found. * Returns MalformedCertificate if any of the hostnames had a NUL character embedded in it. * Returns NoSANPresent if the SAN extension was not present in the certificate. */ static HostnameValidationResult matches_subject_alternative_name( const char *hostname, const X509 *server_cert) { HostnameValidationResult result = MatchNotFound; int i; int san_names_nb = -1; STACK_OF(GENERAL_NAME) *san_names = NULL; // Try to extract the names within the SAN extension from the certificate san_names = X509_get_ext_d2i((X509 *) server_cert, NID_subject_alt_name, NULL, NULL); if (san_names == NULL) { return NoSANPresent; } san_names_nb = sk_GENERAL_NAME_num(san_names); // Check each name within the extension for (i = 0; i < san_names_nb; i++) { const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); if (current_name->type == GEN_DNS) { // Current name is a DNS name, let's check it char *dns_name = (char *) ASN1_STRING_data(current_name->d.dNSName); // Make sure there isn't an embedded NUL character in the DNS name if (ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { result = MalformedCertificate; break; } else { // Compare expected hostname with the DNS name if (hostmatch(hostname, dns_name) == CURL_HOST_MATCH) { result = MatchFound; break; } } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); return result; }
/** * Tries to find a match for hostname in the certificate's Subject Alternative * Name extension. * * Returns AMQP_HVR_MATCH_FOUND if a match was found. * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL * character embedded in it. * Returns AMQP_HVR_NO_SAN_PRESENT if the SAN extension was not present in the * certificate. */ static amqp_hostname_validation_result amqp_matches_subject_alternative_name( const char *hostname, const X509 *server_cert) { amqp_hostname_validation_result result = AMQP_HVR_MATCH_NOT_FOUND; int i; int san_names_nb = -1; STACK_OF(GENERAL_NAME) *san_names = NULL; // Try to extract the names within the SAN extension from the certificate san_names = X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL); if (san_names == NULL) { return AMQP_HVR_NO_SAN_PRESENT; } san_names_nb = sk_GENERAL_NAME_num(san_names); // Check each name within the extension for (i = 0; i < san_names_nb; i++) { const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); if (current_name->type == GEN_DNS) { // Current name is a DNS name, let's check it char *dns_name = (char *)ASN1_STRING_data(current_name->d.dNSName); // Make sure there isn't an embedded NUL character in the DNS name if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { result = AMQP_HVR_MALFORMED_CERTIFICATE; break; } else { // Compare expected hostname with the DNS name if (amqp_hostcheck(dns_name, hostname) == AMQP_HCR_MATCH) { result = AMQP_HVR_MATCH_FOUND; break; } } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); return result; }
int openssl_cert_match_name(SSL *ssl, const char *verify_name) { X509 *cert; STACK_OF(GENERAL_NAME) *gnames; const GENERAL_NAME *gn; const char *dnsname; bool dns_names = FALSE; unsigned int i, count; int ret; cert = SSL_get_peer_certificate(ssl); i_assert(cert != NULL); /* verify against SubjectAltNames */ gnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); count = gnames == NULL ? 0 : sk_GENERAL_NAME_num(gnames); for (i = 0; i < count; i++) { gn = sk_GENERAL_NAME_value(gnames, i); if (gn->type == GEN_DNS) { dns_names = TRUE; dnsname = get_general_dns_name(gn); if (openssl_hostname_equals(dnsname, verify_name)) break; } } sk_GENERAL_NAME_pop_free(gnames, GENERAL_NAME_free); /* verify against CommonName only when there wasn't any DNS SubjectAltNames */ if (dns_names) ret = i < count ? 0 : -1; else if (openssl_hostname_equals(get_cname(cert), verify_name)) ret = 0; else ret = -1; X509_free(cert); return ret; }
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static gboolean rspamd_tls_check_subject_altname (X509 *cert, const char *name) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; int addrlen, type; int count, i; union { struct in_addr ip4; struct in6_addr ip6; } addrbuf; gboolean ret = FALSE; altname_stack = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) { return FALSE; } if (inet_pton (AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton (AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num (altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value (altname_stack, i); if (altname->type != type) { continue; } if (type == GEN_DNS) { unsigned char *data; int format, len; format = ASN1_STRING_type (altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_data (altname->d.dNSName); len = ASN1_STRING_length (altname->d.dNSName); if (len < 0 || len != (gint)strlen (data)) { ret = FALSE; break; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp (data, " ") == 0) { ret = FALSE; break; } if (rspamd_tls_match_name (data, name)) { ret = TRUE; break; } } } else if (type == GEN_IPADD) { unsigned char *data; int datalen; datalen = ASN1_STRING_length (altname->d.iPAddress); data = ASN1_STRING_data (altname->d.iPAddress); if (datalen < 0) { ret = FALSE; break; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp (data, &addrbuf, addrlen) == 0) { ret = TRUE; break; } } } sk_GENERAL_NAME_pop_free (altname_stack, GENERAL_NAME_free); return ret; }
static int32_t matches_subject_alternative_name( const char *hostname, const X509 *server_cert) { int32_t result = MatchNotFound; int32_t i; int32_t san_names_nb = -1; int32_t hostname_is_domain; const char *subdomain_offset; size_t dns_name_sz; size_t hostname_sz = strlen(hostname); STACK_OF(GENERAL_NAME) *san_names = 0; san_names = X509_get_ext_d2i( (X509 *) server_cert, NID_subject_alt_name, 0, 0); if (san_names == 0) return NoSANPresent; san_names_nb = sk_GENERAL_NAME_num(san_names); for (i=0; i<san_names_nb; i++) { const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); if (current_name->type == GEN_DNS) { char *dns_name = (char *)ASN1_STRING_data(current_name->d.dNSName); dns_name_sz = strlen(dns_name); if (ASN1_STRING_length(current_name->d.dNSName) != dns_name_sz) { result = MalformedCertificate; break; } else { if (strcasecmp(hostname, dns_name) == 0) { result = MatchFound; break; } if (dns_name_sz <= 2) continue; if (dns_name[0] == '*' && dns_name[1] == '.') { // Wildcard subdomain. subdomain_offset = strchr(hostname, '.'); if (!subdomain_offset) continue; hostname_is_domain = strchr(subdomain_offset, '.') ? 0 : 1; if (hostname_is_domain) { if (strcasecmp(hostname, dns_name + 2) == 0) { result = MatchFound; break; } } else { if (hostname_sz - (subdomain_offset - hostname) > 0) { if (strcasecmp( subdomain_offset + 1, dns_name + 2) == 0) { result = MatchFound; break; } } } } } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); return result; }
static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, const TLS_CLIENT_START_PROPS *props) { int i; int r; int matched = 0; const char *dnsname; const GENERAL_NAME *gn; STACK_OF(GENERAL_NAME) * gens; /* * On exit both peer_CN and issuer_CN should be set. */ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext); /* * Is the certificate trust chain valid and trusted? */ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; if (TLS_CERT_IS_TRUSTED(TLScontext) && props->tls_level >= TLS_LEV_VERIFY) { /* * Verify the dNSName(s) in the peer certificate against the nexthop * and hostname. * * If DNS names are present, we use the first matching (or else simply * the first) DNS name as the subject CN. The CommonName in the * issuer DN is obsolete when SubjectAltName is available. This * yields much less surprising logs, because we log the name we * verified or a name we checked and failed to match. * * XXX: The nexthop and host name may both be the same network address * rather than a DNS name. In this case we really should be looking * for GEN_IPADD entries, not GEN_DNS entries. * * XXX: In ideal world the caller who used the address to build the * connection would tell us that the nexthop is the connection * address, but if that is not practical, we can parse the nexthop * again here. */ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); if (gens) { r = sk_GENERAL_NAME_num(gens); for (i = 0; i < r && !matched; ++i) { gn = sk_GENERAL_NAME_value(gens, i); if (gn->type != GEN_DNS) continue; /* * Even if we have an invalid DNS name, we still ultimately * ignore the CommonName, because subjectAltName:DNS is * present (though malformed). Replace any previous peer_CN * if empty or we get a match. * * We always set at least an empty peer_CN if the ALTNAME cert * flag is set. If not, we set peer_CN from the cert * CommonName below, so peer_CN is always non-null on return. */ TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME; dnsname = tls_dns_name(gn, TLScontext); if (dnsname && *dnsname) { matched = match_hostname(dnsname, props); if (TLScontext->peer_CN && (matched || *TLScontext->peer_CN == 0)) { acl_myfree(TLScontext->peer_CN); TLScontext->peer_CN = 0; } } if (TLScontext->peer_CN == 0) TLScontext->peer_CN = acl_mystrdup(dnsname ? dnsname : ""); } /* * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME * objects */ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } /* * No subjectAltNames, peer_CN is taken from CommonName. */ if (TLScontext->peer_CN == 0) { TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); if (*TLScontext->peer_CN) matched = match_hostname(TLScontext->peer_CN, props); } if (matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; /* * - Matched: Trusted and peername matches - Trusted: Signed by * trusted CA(s), but peername not matched - Untrusted: Can't verify * the trust chain, reason already logged. */ if (TLScontext->log_level >= 2) acl_msg_info("%s: %s subject_CN=%s, issuer_CN=%s", props->namaddr, TLS_CERT_IS_MATCHED(TLScontext) ? "Matched" : TLS_CERT_IS_TRUSTED(TLScontext) ? "Trusted" : "Untrusted", TLScontext->peer_CN, TLScontext->issuer_CN); } else TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); /* * Give them a clue. Problems with trust chain verification were logged * when the session was first negotiated, before the session was stored * into the cache. We don't want mystery failures, so log the fact the * real problem is to be found in the past. */ if (TLScontext->session_reused && !TLS_CERT_IS_TRUSTED(TLScontext) && TLScontext->log_level >= 1) acl_msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); }
void OsSSL::logConnectParams(const OsSysLogFacility facility, ///< callers facility const OsSysLogPriority priority, ///< log priority const char* callerMsg, ///< Identifies circumstances of connection SSL* connection ///< SSL connection to be described ) { if (connection) { char* subjectStr = NULL; char* issuerStr = NULL; UtlString altNames; // Extract the subject and issuer information about the peer // and the certificate validation result. Neither of these // are meaningful without the other. // (note various dynamically allocated items - freed below) int validity = SSL_get_verify_result(connection); X509* peer_cert = SSL_get_peer_certificate(connection); if (peer_cert) { subjectStr = X509_NAME_oneline(X509_get_subject_name(peer_cert),0,0); issuerStr = X509_NAME_oneline(X509_get_issuer_name(peer_cert),0,0); // Look for the subjectAltName URI or DNS attributes GENERAL_NAMES* names; names = (GENERAL_NAMES*)X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); for(int i = 0; i < sk_GENERAL_NAME_num(names); i++) { GENERAL_NAME* name = sk_GENERAL_NAME_value(names, i); switch (name->type) { case GEN_DNS: { ASN1_IA5STRING* uri = name->d.uniformResourceIdentifier; if (!altNames.isNull()) { altNames.append(","); } altNames.append((const char*)(uri->data),uri->length); } break; case GEN_URI: { ASN1_IA5STRING* uri = name->d.uniformResourceIdentifier; if (!altNames.isNull()) { altNames.append(","); } altNames.append((const char*)(uri->data),uri->length); } break; default: // don't care about any other values break; } } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); } // Get the name of the encryption applied to the connection const char* cipher = SSL_get_cipher(connection); OsSysLog::add(FAC_KERNEL, PRI_DEBUG, "%s SSL Connection:\n" " status: %s\n" " peer: '%s'\n" " alt names: %s\n" " cipher: '%s'\n" " issuer: '%s'", callerMsg, validity == X509_V_OK ? "Verified" : "NOT VERIFIED", subjectStr ? subjectStr : "", altNames.isNull() ? "" : altNames.data(), cipher ? cipher : "", issuerStr ? issuerStr : "" ); // Release the various dynamic things if (subjectStr) { OPENSSL_free(subjectStr); } if (issuerStr) { OPENSSL_free(issuerStr); } if (peer_cert) { X509_free(peer_cert); } } else { OsSysLog::add(FAC_KERNEL, PRI_ERR, "OsSSL::logConnectParams called by %s with NULL connection", callerMsg ); } }
static void verify_extract_name(TLS_SESS_STATE *TLScontext, X509 *peercert, const TLS_CLIENT_START_PROPS *props) { int i; int r; int matched = 0; int dnsname_match; int verify_peername = 0; int log_certmatch; int verbose; const char *dnsname; const GENERAL_NAME *gn; general_name_stack_t *gens; /* * On exit both peer_CN and issuer_CN should be set. */ TLScontext->issuer_CN = tls_issuer_CN(peercert, TLScontext); /* * Is the certificate trust chain valid and trusted? */ if (SSL_get_verify_result(TLScontext->con) == X509_V_OK) TLScontext->peer_status |= TLS_CERT_FLAG_TRUSTED; /* * With fingerprint or dane we may already be done. Otherwise, verify the * peername if using traditional PKI or DANE with trust-anchors. */ if (!TLS_CERT_IS_MATCHED(TLScontext) && TLS_CERT_IS_TRUSTED(TLScontext) && TLS_MUST_TRUST(props->tls_level)) verify_peername = 1; /* Force cert processing so we can log the data? */ log_certmatch = TLScontext->log_mask & TLS_LOG_CERTMATCH; /* Log cert details when processing? */ verbose = log_certmatch || (TLScontext->log_mask & TLS_LOG_VERBOSE); if (verify_peername || log_certmatch) { /* * Verify the dNSName(s) in the peer certificate against the nexthop * and hostname. * * If DNS names are present, we use the first matching (or else simply * the first) DNS name as the subject CN. The CommonName in the * issuer DN is obsolete when SubjectAltName is available. This * yields much less surprising logs, because we log the name we * verified or a name we checked and failed to match. * * XXX: The nexthop and host name may both be the same network address * rather than a DNS name. In this case we really should be looking * for GEN_IPADD entries, not GEN_DNS entries. * * XXX: In ideal world the caller who used the address to build the * connection would tell us that the nexthop is the connection * address, but if that is not practical, we can parse the nexthop * again here. */ gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0); if (gens) { r = sk_GENERAL_NAME_num(gens); for (i = 0; i < r; ++i) { gn = sk_GENERAL_NAME_value(gens, i); if (gn->type != GEN_DNS) continue; /* * Even if we have an invalid DNS name, we still ultimately * ignore the CommonName, because subjectAltName:DNS is * present (though malformed). Replace any previous peer_CN * if empty or we get a match. * * We always set at least an empty peer_CN if the ALTNAME cert * flag is set. If not, we set peer_CN from the cert * CommonName below, so peer_CN is always non-null on return. */ TLScontext->peer_status |= TLS_CERT_FLAG_ALTNAME; dnsname = tls_dns_name(gn, TLScontext); if (dnsname && *dnsname) { if ((dnsname_match = match_servername(dnsname, props)) != 0) matched++; /* Keep the first matched name. */ if (TLScontext->peer_CN && ((dnsname_match && matched == 1) || *TLScontext->peer_CN == 0)) { myfree(TLScontext->peer_CN); TLScontext->peer_CN = 0; } if (verbose) msg_info("%s: %ssubjectAltName: %s", props->namaddr, dnsname_match ? "Matched " : "", dnsname); } if (TLScontext->peer_CN == 0) TLScontext->peer_CN = mystrdup(dnsname ? dnsname : ""); if (matched && !log_certmatch) break; } if (verify_peername && matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; /* * (Sam Rushing, Ironport) Free stack *and* member GENERAL_NAME * objects */ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } /* * No subjectAltNames, peer_CN is taken from CommonName. */ if (TLScontext->peer_CN == 0) { TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); if (*TLScontext->peer_CN) matched = match_servername(TLScontext->peer_CN, props); if (verify_peername && matched) TLScontext->peer_status |= TLS_CERT_FLAG_MATCHED; if (verbose) msg_info("%s %sCommonName %s", props->namaddr, matched ? "Matched " : "", TLScontext->peer_CN); } else if (verbose) { char *tmpcn = tls_peer_CN(peercert, TLScontext); /* * Though the CommonName was superceded by a subjectAltName, log * it when certificate match debugging was requested. */ msg_info("%s CommonName %s", TLScontext->namaddr, tmpcn); myfree(tmpcn); } } else TLScontext->peer_CN = tls_peer_CN(peercert, TLScontext); /* * Give them a clue. Problems with trust chain verification are logged * when the session is first negotiated, before the session is stored * into the cache. We don't want mystery failures, so log the fact the * real problem is to be found in the past. */ if (!TLS_CERT_IS_TRUSTED(TLScontext) && (TLScontext->log_mask & TLS_LOG_UNTRUSTED)) { if (TLScontext->session_reused == 0) tls_log_verify_error(TLScontext); else msg_info("%s: re-using session with untrusted certificate, " "look for details earlier in the log", props->namaddr); } }
apr_hash_t *serf_ssl_cert_certificate( const serf_ssl_certificate_t *cert, apr_pool_t *pool) { apr_hash_t *tgt = apr_hash_make(pool); unsigned int md_size, i; unsigned char md[EVP_MAX_MD_SIZE]; BIO *bio; STACK_OF(GENERAL_NAME) *names; /* sha1 fingerprint */ if (X509_digest(cert->ssl_cert, EVP_sha1(), md, &md_size)) { const char hex[] = "0123456789ABCDEF"; char fingerprint[EVP_MAX_MD_SIZE * 3]; for (i=0; i<md_size; i++) { fingerprint[3*i] = hex[(md[i] & 0xf0) >> 4]; fingerprint[(3*i)+1] = hex[(md[i] & 0x0f)]; fingerprint[(3*i)+2] = ':'; } if (md_size > 0) fingerprint[(3*(md_size-1))+2] = '\0'; else fingerprint[0] = '\0'; apr_hash_set(tgt, "sha1", APR_HASH_KEY_STRING, apr_pstrdup(pool, fingerprint)); } /* set expiry dates */ bio = BIO_new(BIO_s_mem()); if (bio) { ASN1_TIME *notBefore, *notAfter; char buf[256]; memset (buf, 0, sizeof (buf)); notBefore = X509_get_notBefore(cert->ssl_cert); if (ASN1_TIME_print(bio, notBefore)) { BIO_read(bio, buf, 255); apr_hash_set(tgt, "notBefore", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); } memset (buf, 0, sizeof (buf)); notAfter = X509_get_notAfter(cert->ssl_cert); if (ASN1_TIME_print(bio, notAfter)) { BIO_read(bio, buf, 255); apr_hash_set(tgt, "notAfter", APR_HASH_KEY_STRING, apr_pstrdup(pool, buf)); } } BIO_free(bio); /* Get subjectAltNames */ names = X509_get_ext_d2i(cert->ssl_cert, NID_subject_alt_name, NULL, NULL); if (names) { int names_count = sk_GENERAL_NAME_num(names); apr_array_header_t *san_arr = apr_array_make(pool, names_count, sizeof(char*)); apr_hash_set(tgt, "subjectAltName", APR_HASH_KEY_STRING, san_arr); for (i = 0; i < names_count; i++) { char *p = NULL; GENERAL_NAME *nm = sk_GENERAL_NAME_value(names, i); switch (nm->type) { case GEN_DNS: p = apr_pstrmemdup(pool, nm->d.ia5->data, nm->d.ia5->length); break; default: /* Don't know what to do - skip. */ break; } if (p) { APR_ARRAY_PUSH(san_arr, char*) = p; } } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); } return tgt; }
/** @brief Extracts data from a certificate store object. @param pCertificate [in] The certificate to examine. @param pIssuer [out, optional] The issuer name of the certificate. @param pSubject [out, optional] The subject name of the certificate. @param pSubjectUri [out, optional] The subject's URI of the certificate. @param pSubjectIP [out, optional] The subject's IP of the certificate. @param pSubjectDNS [out, optional] The subject's DNS name of the certificate. @param pCertThumbprint [out, optional] The thumbprint of the certificate. @param pSubjectHash [out, optional] The hash code of the certificate. @param pCertRawLength [out, optional] The length of the DER encoded data. can be smaller than the total length of pCertificate in case of chain certificate or garbage follow. */ OpcUa_StatusCode OpcUa_P_OpenSSL_PKI_ExtractCertificateData( OpcUa_ByteString* a_pCertificate, OpcUa_ByteString* a_pIssuer, OpcUa_ByteString* a_pSubject, OpcUa_ByteString* a_pSubjectUri, OpcUa_ByteString* a_pSubjectIP, OpcUa_ByteString* a_pSubjectDNS, OpcUa_ByteString* a_pCertThumbprint, OpcUa_UInt32* a_pSubjectHash, OpcUa_UInt32* a_pCertRawLength) { X509* pX509Cert = OpcUa_Null; char* pName = OpcUa_Null; GENERAL_NAMES* pNames = OpcUa_Null; const unsigned char* p; OpcUa_InitializeStatus(OpcUa_Module_P_OpenSSL, "PKI_ExtractCertificateData"); OpcUa_ReturnErrorIfArgumentNull(a_pCertificate); if(a_pIssuer != OpcUa_Null) { a_pIssuer->Data = OpcUa_Null; a_pIssuer->Length = 0; } if(a_pSubject != OpcUa_Null) { a_pSubject->Data = OpcUa_Null; a_pSubject->Length = 0; } if(a_pSubjectUri != OpcUa_Null) { a_pSubjectUri->Data = OpcUa_Null; a_pSubjectUri->Length = 0; } if(a_pSubjectIP != OpcUa_Null) { a_pSubjectIP->Data = OpcUa_Null; a_pSubjectIP->Length = 0; } if(a_pSubjectDNS != OpcUa_Null) { a_pSubjectDNS->Data = OpcUa_Null; a_pSubjectDNS->Length = 0; } if(a_pCertThumbprint != OpcUa_Null) { a_pCertThumbprint->Data = OpcUa_Null; a_pCertThumbprint->Length = 0; } if(a_pSubjectHash != OpcUa_Null) { *a_pSubjectHash = 0; } if(a_pCertRawLength != OpcUa_Null) { *a_pCertRawLength = 0; } /* convert openssl X509 certificate to DER encoded bytestring certificate */ p = a_pCertificate->Data; if(!(pX509Cert = d2i_X509((X509**)OpcUa_Null, &p, a_pCertificate->Length))) { uStatus = OpcUa_Bad; OpcUa_GotoErrorIfBad(uStatus); } if(a_pIssuer != OpcUa_Null) { pName = X509_NAME_oneline(X509_get_issuer_name(pX509Cert), NULL, 0); OpcUa_GotoErrorIfAllocFailed(pName); a_pIssuer->Length = strlen(pName)+1; a_pIssuer->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pIssuer->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pIssuer->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pIssuer->Data, a_pIssuer->Length, pName, a_pIssuer->Length); OpcUa_GotoErrorIfBad(uStatus); OPENSSL_free(pName); pName = OpcUa_Null; } if(a_pSubject != OpcUa_Null) { pName = X509_NAME_oneline(X509_get_subject_name(pX509Cert), NULL, 0); OpcUa_GotoErrorIfAllocFailed(pName); a_pSubject->Length = strlen(pName)+1; a_pSubject->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubject->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pSubject->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pSubject->Data, a_pSubject->Length, pName, a_pSubject->Length); OpcUa_GotoErrorIfBad(uStatus); OPENSSL_free(pName); pName = OpcUa_Null; } if(a_pSubjectUri != OpcUa_Null || a_pSubjectIP != OpcUa_Null || a_pSubjectDNS != OpcUa_Null) { pNames = X509_get_ext_d2i(pX509Cert, NID_subject_alt_name, OpcUa_Null, OpcUa_Null); if (pNames != OpcUa_Null) { int num; for (num = 0; num < sk_GENERAL_NAME_num(pNames); num++) { GENERAL_NAME *value = sk_GENERAL_NAME_value(pNames, num); switch (value->type) { case GEN_URI: if (a_pSubjectUri != OpcUa_Null && a_pSubjectUri->Data == OpcUa_Null) { a_pSubjectUri->Length = value->d.ia5->length+1; a_pSubjectUri->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectUri->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pSubjectUri->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectUri->Data, a_pSubjectUri->Length, value->d.ia5->data, a_pSubjectUri->Length); OpcUa_GotoErrorIfBad(uStatus); } break; case GEN_IPADD: if (a_pSubjectIP != OpcUa_Null && a_pSubjectIP->Data == OpcUa_Null) { a_pSubjectIP->Length = value->d.ip->length; a_pSubjectIP->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectIP->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pSubjectIP->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectIP->Data, a_pSubjectIP->Length, value->d.ip->data, a_pSubjectIP->Length); OpcUa_GotoErrorIfBad(uStatus); } break; case GEN_DNS: if (a_pSubjectDNS != OpcUa_Null && a_pSubjectDNS->Data == OpcUa_Null) { a_pSubjectDNS->Length = value->d.ia5->length+1; a_pSubjectDNS->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pSubjectDNS->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pSubjectDNS->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pSubjectDNS->Data, a_pSubjectDNS->Length, value->d.ia5->data, a_pSubjectDNS->Length); OpcUa_GotoErrorIfBad(uStatus); } break; } } sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free); pNames = OpcUa_Null; } } if(a_pCertThumbprint != OpcUa_Null) { /* update pX509Cert->sha1_hash */ X509_check_purpose(pX509Cert, -1, 0); a_pCertThumbprint->Length = sizeof(pX509Cert->sha1_hash); a_pCertThumbprint->Data = (OpcUa_Byte*)OpcUa_P_Memory_Alloc(a_pCertThumbprint->Length*sizeof(OpcUa_Byte)); OpcUa_GotoErrorIfAllocFailed(a_pCertThumbprint->Data); uStatus = OpcUa_P_Memory_MemCpy(a_pCertThumbprint->Data, a_pCertThumbprint->Length, pX509Cert->sha1_hash, a_pCertThumbprint->Length); OpcUa_GotoErrorIfBad(uStatus); } if(a_pSubjectHash != OpcUa_Null) { *a_pSubjectHash = X509_NAME_hash(X509_get_subject_name(pX509Cert)); } if(a_pCertRawLength != OpcUa_Null) { *a_pCertRawLength = (OpcUa_UInt32)(p - a_pCertificate->Data); } X509_free(pX509Cert); OpcUa_ReturnStatusCode; OpcUa_BeginErrorHandling; if(a_pIssuer != OpcUa_Null && a_pIssuer->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pIssuer->Data); a_pIssuer->Data = OpcUa_Null; a_pIssuer->Length = 0; } if(a_pSubject != OpcUa_Null && a_pSubject->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pSubject->Data); a_pSubject->Data = OpcUa_Null; a_pSubject->Length = 0; } if(a_pSubjectUri != OpcUa_Null && a_pSubjectUri->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pSubjectUri->Data); a_pSubjectUri->Data = OpcUa_Null; a_pSubjectUri->Length = 0; } if(a_pSubjectIP != OpcUa_Null && a_pSubjectIP->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pSubjectIP->Data); a_pSubjectIP->Data = OpcUa_Null; a_pSubjectIP->Length = 0; } if(a_pSubjectDNS != OpcUa_Null && a_pSubjectDNS->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pSubjectDNS->Data); a_pSubjectDNS->Data = OpcUa_Null; a_pSubjectDNS->Length = 0; } if(a_pCertThumbprint != OpcUa_Null && a_pCertThumbprint->Data != OpcUa_Null) { OpcUa_P_Memory_Free(a_pCertThumbprint->Data); a_pCertThumbprint->Data = OpcUa_Null; a_pCertThumbprint->Length = 0; } if (pName != OpcUa_Null) { OPENSSL_free(pName); } if (pNames != OpcUa_Null) { sk_GENERAL_NAME_pop_free(pNames, GENERAL_NAME_free); } if(pX509Cert != OpcUa_Null) { X509_free(pX509Cert); } OpcUa_FinishErrorHandling; }
/*! \brief * creates a FILE * from the fd passed by the accept thread. * This operation is potentially expensive (certificate verification), * so we do it in the child thread context. * * \note must decrement ref count before returning NULL on error */ static void *handle_tcptls_connection(void *data) { struct ast_tcptls_session_instance *tcptls_session = data; #ifdef DO_SSL int (*ssl_setup)(SSL *) = (tcptls_session->client) ? SSL_connect : SSL_accept; int ret; char err[256]; #endif /* TCP/TLS connections are associated with external protocols, and * should not be allowed to execute 'dangerous' functions. This may * need to be pushed down into the individual protocol handlers, but * this seems like a good general policy. */ if (ast_thread_inhibit_escalations()) { ast_log(LOG_ERROR, "Failed to inhibit privilege escalations; killing connection\n"); ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); return NULL; } tcptls_session->stream_cookie = tcptls_stream_alloc(); if (!tcptls_session->stream_cookie) { ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); return NULL; } /* * open a FILE * as appropriate. */ if (!tcptls_session->parent->tls_cfg) { tcptls_session->f = tcptls_stream_fopen(tcptls_session->stream_cookie, NULL, tcptls_session->fd, -1); if (tcptls_session->f) { if (setvbuf(tcptls_session->f, NULL, _IONBF, 0)) { ast_tcptls_close_session_file(tcptls_session); } } } #ifdef DO_SSL else if ( (tcptls_session->ssl = SSL_new(tcptls_session->parent->tls_cfg->ssl_ctx)) ) { SSL_set_fd(tcptls_session->ssl, tcptls_session->fd); if ((ret = ssl_setup(tcptls_session->ssl)) <= 0) { ast_log(LOG_ERROR, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); } else if ((tcptls_session->f = tcptls_stream_fopen(tcptls_session->stream_cookie, tcptls_session->ssl, tcptls_session->fd, -1))) { if ((tcptls_session->client && !ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_DONT_VERIFY_SERVER)) || (!tcptls_session->client && ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_VERIFY_CLIENT))) { X509 *peer; long res; peer = SSL_get_peer_certificate(tcptls_session->ssl); if (!peer) { ast_log(LOG_ERROR, "No peer SSL certificate to verify\n"); ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); return NULL; } res = SSL_get_verify_result(tcptls_session->ssl); if (res != X509_V_OK) { ast_log(LOG_ERROR, "Certificate did not verify: %s\n", X509_verify_cert_error_string(res)); X509_free(peer); ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); return NULL; } if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { ASN1_STRING *str; X509_NAME *name = X509_get_subject_name(peer); STACK_OF(GENERAL_NAME) *alt_names; int pos = -1; int found = 0; for (;;) { /* Walk the certificate to check all available "Common Name" */ /* XXX Probably should do a gethostbyname on the hostname and compare that as well */ pos = X509_NAME_get_index_by_NID(name, NID_commonName, pos); if (pos < 0) { break; } str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos)); if (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) { found = 1; break; } } if (!found) { alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL); if (alt_names != NULL) { int alt_names_count = sk_GENERAL_NAME_num(alt_names); for (pos = 0; pos < alt_names_count; pos++) { const GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, pos); if (alt_name->type != GEN_DNS) { continue; } if (!check_tcptls_cert_name(alt_name->d.dNSName, tcptls_session->parent->hostname, "alt name")) { found = 1; break; } } sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); } } if (!found) { ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); X509_free(peer); ast_tcptls_close_session_file(tcptls_session); ao2_ref(tcptls_session, -1); return NULL; } } X509_free(peer); } } if (!tcptls_session->f) { /* no success opening descriptor stacking */ SSL_free(tcptls_session->ssl); } } #endif /* DO_SSL */ if (!tcptls_session->f) { ast_tcptls_close_session_file(tcptls_session); ast_log(LOG_WARNING, "FILE * open failed!\n"); #ifndef DO_SSL if (tcptls_session->parent->tls_cfg) { ast_log(LOG_ERROR, "Attempted a TLS connection without OpenSSL support. This will not work!\n"); } #endif ao2_ref(tcptls_session, -1); return NULL; } if (tcptls_session->parent->worker_fn) { return tcptls_session->parent->worker_fn(tcptls_session); } else { return tcptls_session; } }
static int get_subjectAltName(X509 *s_ctx) { int error_code = 0; GENERAL_NAMES *gens = NULL; GENERAL_NAME *gen = NULL; char *altname; unsigned char p[5], *ip; gens = X509_get_ext_d2i(s_ctx, NID_subject_alt_name, NULL, NULL); if(NULL == gens) { error_code = 0; goto end; } int gen_num = sk_GENERAL_NAME_num(gens); int i=0; for(i=0; i <= gen_num; ++i) { gen = sk_GENERAL_NAME_value(gens, i); if(NULL == gen) { continue; } if (gen->type == GEN_DNS ||gen->type == GEN_EMAIL ||gen->type == GEN_URI ) { // make sure if the data is terminated by '\0'. if (gen->d.ia5->data[gen->d.ia5->length] != '\0') { continue; } altname = (char *)malloc(gen->d.ia5->length + 1); strncpy(altname, (char *) gen->d.ia5->data, (gen->d.ia5->length + 1)); switch (gen->type) { case GEN_EMAIL: printf("email:%s\n", altname); break; case GEN_DNS: printf("DNS:%s\n", altname); break; case GEN_URI: default: printf("URI:%s\n", altname); break; } } else if (gen->type == GEN_IPADD) { if (gen->d.ip->length != 4) { continue; } ip = p; ip = gen->d.ip->data; printf("IP:%u.%u.%u.%u\n", ip[0], ip[1], ip[2], ip[3]); } if(altname) { free(altname); altname = NULL; } } end: if(gens) { // GENERAL_NAMES_free(gens); sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free); } return error_code; }
static int get_alt(str* res, int local, int type, sip_msg_t* msg) { static char buf[1024]; int n, found = 0; STACK_OF(GENERAL_NAME)* names = 0; GENERAL_NAME* nm; X509* cert; struct tcp_connection* c; str text; struct ip_addr ip; if (get_cert(&cert, &c, msg, local) < 0) return -1; names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (!names) { DBG("Cannot get certificate alternative subject\n"); goto err; } for (n = 0; n < sk_GENERAL_NAME_num(names); n++) { nm = sk_GENERAL_NAME_value(names, n); if (nm->type != type) continue; switch(type) { case GEN_EMAIL: case GEN_DNS: case GEN_URI: text.s = (char*)nm->d.ia5->data; text.len = nm->d.ia5->length; if (text.len >= 1024) { ERR("Alternative subject text too long\n"); goto err; } memcpy(buf, text.s, text.len); res->s = buf; res->len = text.len; found = 1; break; case GEN_IPADD: ip.len = nm->d.iPAddress->length; ip.af = (ip.len == 16) ? AF_INET6 : AF_INET; memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len); text.s = ip_addr2a(&ip); text.len = strlen(text.s); memcpy(buf, text.s, text.len); res->s = buf; res->len = text.len; found = 1; break; } break; } if (!found) goto err; if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!local) X509_free(cert); tcpconn_put(c); return 0; err: if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!local) X509_free(cert); tcpconn_put(c); return -1; }
static int set_dist_point_name(DIST_POINT_NAME **pdp, X509V3_CTX *ctx, CONF_VALUE *cnf) { STACK_OF(GENERAL_NAME) *fnm = NULL; STACK_OF(X509_NAME_ENTRY) *rnm = NULL; if (!strncmp(cnf->name, "fullname", 9)) { fnm = gnames_from_sectname(ctx, cnf->value); if (!fnm) goto err; } else if (!strcmp(cnf->name, "relativename")) { int ret; STACK_OF(CONF_VALUE) *dnsect; X509_NAME *nm; nm = X509_NAME_new(); if (!nm) return -1; dnsect = X509V3_get_section(ctx, cnf->value); if (!dnsect) { X509V3err(X509V3_F_SET_DIST_POINT_NAME, X509V3_R_SECTION_NOT_FOUND); return -1; } ret = X509V3_NAME_from_section(nm, dnsect, MBSTRING_ASC); X509V3_section_free(ctx, dnsect); rnm = nm->entries; nm->entries = NULL; X509_NAME_free(nm); if (!ret || sk_X509_NAME_ENTRY_num(rnm) <= 0) goto err; /* Since its a name fragment can't have more than one * RDNSequence */ if (sk_X509_NAME_ENTRY_value(rnm, sk_X509_NAME_ENTRY_num(rnm) - 1)->set) { X509V3err(X509V3_F_SET_DIST_POINT_NAME, X509V3_R_INVALID_MULTIPLE_RDNS); goto err; } } else return 0; if (*pdp) { X509V3err(X509V3_F_SET_DIST_POINT_NAME, X509V3_R_DISTPOINT_ALREADY_SET); goto err; } *pdp = DIST_POINT_NAME_new(); if (!*pdp) goto err; if (fnm) { (*pdp)->type = 0; (*pdp)->name.fullname = fnm; } else { (*pdp)->type = 1; (*pdp)->name.relativename = rnm; } return 1; err: if (fnm) sk_GENERAL_NAME_pop_free(fnm, GENERAL_NAME_free); if (rnm) sk_X509_NAME_ENTRY_pop_free(rnm, X509_NAME_ENTRY_free); return -1; }
static int imap_check_server_identity(SSL *ssl, const char *host, ImapUserCb user_cb, void *user_arg) { long vfy_result; X509 *cert; X509_NAME *subj; int ok, host_len; gchar *colon; int has_extension_with_dns_name = 0; STACK_OF(GENERAL_NAME) *altnames; if(!host) return 0; /* Check whether the certificate matches the server. */ cert = SSL_get_peer_certificate(ssl); if(!cert) return 0; colon = strchr(host, ':'); host_len = colon ? colon - host : (int)strlen(host); ok = 0; altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altnames) { int i; for (i=0; i< sk_GENERAL_NAME_num(altnames); i++) { const GENERAL_NAME *name = sk_GENERAL_NAME_value(altnames, i); /* We handle only GEN_DNS. GEN_IP (certificates for IP numbers) are too weird to be real in IMAP case. */ if (name->type == GEN_DNS) { const ASN1_IA5STRING *ia5 = name->d.ia5; const char *name = (const char*)ia5->data; has_extension_with_dns_name = 1; if (strlen(name) == (size_t)ia5->length && host_matches_domain(host, name, host_len)) { /* printf("verified name using extension\n"); */ ok = 1; break; } } } sk_GENERAL_NAME_pop_free(altnames, GENERAL_NAME_free); } if (!has_extension_with_dns_name) { char data[256]; size_t name_len; if( (subj = X509_get_subject_name(cert)) && (name_len = X509_NAME_get_text_by_NID(subj, NID_commonName, data, sizeof(data)))){ data[sizeof(data)-1] = 0; /* Remember to check whether there was no truncation or NUL characters embedded in the text. */ if(name_len == strlen(data) && host_matches_domain(host, data, host_len)) { ok =1; /* printf("verified name using common name\n"); */ } } } X509_free(cert); if(ok) vfy_result = SSL_get_verify_result(ssl); else vfy_result = X509_V_ERR_APPLICATION_VERIFICATION; if(vfy_result == X509_V_OK) return 1; /* There was a problem with the verification, one has to leave it up * to the application what to do with this. */ ok = 0; if(user_cb) user_cb(IME_TLS_VERIFY_ERROR, user_arg, &ok, vfy_result, ssl); return ok; }
/* See RFC 5280 section 4.2.1.6 for SubjectAltName details. */ static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; union tls_addr addrbuf; int addrlen, type; int count, i; int rv = -1; altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (altname_stack == NULL) return -1; if (inet_pton(AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num(altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type != type) continue; if (type == GEN_DNS) { const void *data; int format, len; format = ASN1_STRING_type(altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_get0_data(altname->d.dNSName); len = ASN1_STRING_length(altname->d.dNSName); if (len < 0 || len != (int)strlen(data)) { tls_set_errorx(ctx, "error verifying name '%s': " "NUL byte in subjectAltName, " "probably a malicious certificate", name); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp(data, " ") == 0) { tls_set_error(ctx, "error verifying name '%s': " "a dNSName of \" \" must not be " "used", name); rv = -2; break; } if (tls_match_name(data, name) == 0) { rv = 0; break; } } else { #ifdef DEBUG fprintf(stdout, "%s: unhandled subjectAltName " "dNSName encoding (%d)\n", getprogname(), format); #endif } } else if (type == GEN_IPADD) { const unsigned char *data; int datalen; datalen = ASN1_STRING_length(altname->d.iPAddress); data = ASN1_STRING_get0_data(altname->d.iPAddress); if (datalen < 0) { tls_set_errorx(ctx, "Unexpected negative length for an " "IP address: %d", datalen); rv = -2; break; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp(data, &addrbuf, addrlen) == 0) { rv = 0; break; } } } sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; }
int tlsops_alt(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[1024]; int type = GEN_URI, my = 0, n, found = 0, ind_local; STACK_OF(GENERAL_NAME)* names = 0; GENERAL_NAME* nm; X509* cert; struct tcp_connection* c; str text; struct ip_addr ip; ind_local = param->pvn.u.isname.name.n; if (ind_local & CERT_PEER) { my = 0; ind_local = ind_local ^ CERT_PEER; } else if (ind_local & CERT_LOCAL) { my = 1; ind_local = ind_local ^ CERT_LOCAL; } else { LM_CRIT("could not determine certificate\n"); return pv_get_null(msg, param, res); } switch(ind_local) { case COMP_E: type = GEN_EMAIL; break; case COMP_HOST: type = GEN_DNS; break; case COMP_URI: type = GEN_URI; break; case COMP_IP: type = GEN_IPADD; break; default: LM_CRIT("ind_local=%d\n", ind_local); return pv_get_null(msg, param, res); } if (get_cert(&cert, &c, msg, my) < 0) return -1; names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (!names) { LM_ERR("cannot get certificate alternative subject\n"); goto err; } for (n = 0; n < sk_GENERAL_NAME_num(names); n++) { nm = sk_GENERAL_NAME_value(names, n); if (nm->type != type) continue; switch(type) { case GEN_EMAIL: case GEN_DNS: case GEN_URI: text.s = (char*)nm->d.ia5->data; text.len = nm->d.ia5->length; if (text.len >= 1024) { LM_ERR("alternative subject text too long\n"); goto err; } memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; found = 1; break; case GEN_IPADD: ip.len = nm->d.iPAddress->length; ip.af = (ip.len == 16) ? AF_INET6 : AF_INET; memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len); text.s = ip_addr2a(&ip); text.len = strlen(text.s); memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; found = 1; break; } break; } if (!found) goto err; if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!my) X509_free(cert); tcpconn_put(c); return 0; err: if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); if (!my) X509_free(cert); tcpconn_put(c); return pv_get_null(msg, param, res); }
/* Get the validated names for the connection peer. * * Usually, the names in the altNames will be easier to parse and use than commonName * Returns * - true if the connection peer is validated by a trusted authority * - false if not, in which case no names are returned. */ bool OsSSL::peerIdentity( SSL* connection ///< SSL connection to be described ,UtlSList* altNames /**< UtlStrings for verfied subjectAltNames * are added to this - caller must free them. */ ,UtlString* commonName ///< the Subject name is returned here ) { bool peerCertTrusted = false; # ifdef TEST_DEBUG UtlString debugMsg; # endif if (altNames) { altNames->destroyAll(); } if (commonName) { commonName->remove(0); } if (connection) { // Extract the subject and issuer information about the peer // and the certificate validation result. Neither of these // are meaningful without the other. // (note various dynamically allocated items - freed below) X509* peer_cert = SSL_get_peer_certificate(connection); if (peer_cert) { if (X509_V_OK == SSL_get_verify_result(connection)) { peerCertTrusted = true; char* subjectStr = X509_NAME_oneline(X509_get_subject_name(peer_cert),NULL,0); // @TODO this should also enforce any extendedKeyUsage limitations # ifdef TEST_DEBUG debugMsg.append("OsSSL::peerIdentity verified"); # endif if (subjectStr) { // this should always be true, I think... if (commonName) { commonName->append(subjectStr); } # ifdef TEST_DEBUG debugMsg.append(" '"); debugMsg.append(subjectStr); debugMsg.append("'"); # endif OPENSSL_free(subjectStr); } if (altNames) { // Look for the subjectAltName attributes GENERAL_NAMES* names; names = (GENERAL_NAMES*)X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); for(int i = 0; i < sk_GENERAL_NAME_num(names); i++) { GENERAL_NAME* name = sk_GENERAL_NAME_value(names, i); ASN1_IA5STRING* nameValue; UtlString* normalizedName; switch (name->type) { case GEN_DNS: case GEN_URI: nameValue = name->d.uniformResourceIdentifier; normalizedName = new UtlString((const char*)(nameValue->data),nameValue->length); // @TODO: We should parse this value before adjusting the case, // but that requires doing it at a higher level in the stack // where we can parse a URL, and we don't yet have selection // based on type anyway. normalizedName->toLower(); # ifdef TEST_DEBUG debugMsg.append(" '"); debugMsg.append(*normalizedName); debugMsg.append("'"); # endif altNames->append(normalizedName); break; default: // don't care about any other values break; } } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); } # ifdef TEST_DEBUG OsSysLog::add(FAC_KERNEL, PRI_DEBUG, "%s", debugMsg.data()); # endif } else { OsSysLog::add(FAC_KERNEL, PRI_ERR, "OsSSL::peerIdentity peer not validated"); } X509_free(peer_cert); } else { OsSysLog::add(FAC_KERNEL, PRI_WARNING, "OsSSL::peerIdentity no peer certificate"); } } else { OsSysLog::add(FAC_KERNEL, PRI_CRIT, "OsSSL::peerIdentity called with NULL connection"); } return peerCertTrusted; }
static void CheckSAN(X509 *x509, CertType type) { int idx = -1; bool bSanFound = false; bool bSanName = false; bool bSanRequired = false; bool bCommonNameFound = false; ASN1_STRING *commonName = NULL; enum { SAN_TYPE_NOT_ALLOWED, SAN_TYPE_ALLOWED, SAN_TYPE_WARN } name_type_allowed[GEN_RID+1]; for (int i = 0; i < GEN_RID+1; i++) { name_type_allowed[i] = SAN_TYPE_NOT_ALLOWED; } if (GetBit(cert_info, CERT_INFO_SERV_AUTH) || GetBit(cert_info, CERT_INFO_ANY_EKU) || GetBit(cert_info, CERT_INFO_NO_EKU)) { name_type_allowed[GEN_DNS] = SAN_TYPE_ALLOWED; name_type_allowed[GEN_IPADD] = SAN_TYPE_ALLOWED; bSanRequired = true; } if (GetBit(cert_info, CERT_INFO_EMAIL) || GetBit(cert_info, CERT_INFO_ANY_EKU) || GetBit(cert_info, CERT_INFO_NO_EKU)) { name_type_allowed[GEN_EMAIL] = SAN_TYPE_ALLOWED; bSanRequired = true; } if (GetBit(cert_info, CERT_INFO_CLIENT_AUTH)) { /* * DNS and IP address doesn't make sense for a TLS client that * doesn't also do server authentication. */ if (name_type_allowed[GEN_DNS] == SAN_TYPE_NOT_ALLOWED) { name_type_allowed[GEN_DNS] = SAN_TYPE_WARN; name_type_allowed[GEN_IPADD] = SAN_TYPE_WARN; } name_type_allowed[GEN_EMAIL] = SAN_TYPE_ALLOWED; } if (GetBit(warnings, WARN_UNKNOWN_EKU) && !GetBit(cert_info, CERT_INFO_SERV_AUTH) && !GetBit(cert_info, CERT_INFO_ANY_EKU)) { /* * If it's a certificate with an unknown EKU that isn't * also valid for server auth, allow the other types */ name_type_allowed[GEN_OTHERNAME] = SAN_TYPE_ALLOWED; name_type_allowed[GEN_X400] = SAN_TYPE_ALLOWED; name_type_allowed[GEN_EDIPARTY] = SAN_TYPE_ALLOWED; name_type_allowed[GEN_URI] = SAN_TYPE_ALLOWED; } X509_NAME *subject = X509_get_subject_name(x509); for (int i = 0; i < X509_NAME_entry_count(subject); i++) { X509_NAME_ENTRY *ne = X509_NAME_get_entry(subject, i); ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(ne); if (OBJ_cmp(obj_commonName, obj) == 0) { commonName = X509_NAME_ENTRY_get_data(ne); break; } } do { int critical = -1; GENERAL_NAMES *names = X509_get_ext_d2i(x509, NID_subject_alt_name, &critical, &idx); if (names == NULL) { if (critical >= 0) { /* Found but fails to parse */ SetError(ERR_INVALID); bSanFound = true; continue; } /* Not found */ break; } for (int i = 0; i < sk_GENERAL_NAME_num(names); i++) { GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i); int type; ASN1_STRING *name_s = GENERAL_NAME_get0_value(name, &type); if (type > GEN_RID || type < 0) { SetError(ERR_INVALID); } else if (name_type_allowed[type] == SAN_TYPE_NOT_ALLOWED) { SetError(ERR_SAN_TYPE); } else if (name_type_allowed[type] == SAN_TYPE_WARN) { SetWarning(WARN_TLS_CLIENT_DNS); } if (type == GEN_DNS) { for (int j = i+1; j < sk_GENERAL_NAME_num(names); j++) { GENERAL_NAME *name2 = sk_GENERAL_NAME_value(names, j); int type2; ASN1_STRING *name2_s = GENERAL_NAME_get0_value(name2, &type2); if (type == type2 && ASN1_STRING_cmpcase(name_s, name2_s) == 0) { SetWarning(WARN_DUPLICATE_SAN); } } char *s = malloc(name_s->length + 1); strncpy(s, (char *)name_s->data, name_s->length); s[name_s->length] = '\0'; unsigned char buf[sizeof(struct in6_addr)]; if (inet_pton(AF_INET, s, buf) == 1 || inet_pton(AF_INET6, s, buf) == 1) { SetError(ERR_IP_IN_DNSNAME); } free(s); } if ((type == GEN_DNS || type == GEN_EMAIL) && commonName != NULL) { if (ASN1_STRING_cmpcase(name_s, commonName) == 0) { bCommonNameFound = true; } } if (type == GEN_IPADD) { int af = AF_UNSPEC; if (name_s->length == 4) { af = AF_INET; } else if (name_s->length == 16) { af = AF_INET6; } else { SetError(ERR_IP_FAMILY); } if (af != AF_UNSPEC && commonName != NULL) { unsigned char buf[sizeof(struct in6_addr)]; char *s = malloc(commonName->length + 1); strncpy(s, (char *)commonName->data, commonName->length); s[commonName->length] = '\0'; inet_pton(af, s, buf); /* We want to compare them binary, the string version is not standard. */ if (memcmp(buf, name_s->data, name_s->length) == 0) { bCommonNameFound = true; } free(s); } } CheckGeneralNameType(name); bSanName = true; } sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); bSanFound = true; } while (1); if (!bSanFound && bSanRequired) { /* Required by CAB base 7.1.4.2.1 */ if (type == SubscriberCertificate) { SetError(ERR_NO_SUBJECT_ALT_NAME); } } if (bSanFound && !bSanName) { SetError(ERR_SAN_WITHOUT_NAME); } if (commonName != NULL && bSanFound && !bCommonNameFound) { // SetError(ERR_CN_NOT_IN_SAN); } }