int php_mongodb_matches_san_list(X509 *peer, const char *subject_name) /* {{{ */ { int i, len; unsigned char *cert_name = NULL; char ipbuffer[64]; GENERAL_NAMES *alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, 0, 0); int alt_name_count = sk_GENERAL_NAME_num(alt_names); for (i = 0; i < alt_name_count; i++) { GENERAL_NAME *san = sk_GENERAL_NAME_value(alt_names, i); if (san->type == GEN_DNS) { ASN1_STRING_to_UTF8(&cert_name, san->d.dNSName); if ((size_t)ASN1_STRING_length(san->d.dNSName) != strlen((const char*)cert_name)) { OPENSSL_free(cert_name); /* prevent null-byte poisoning*/ continue; } /* accommodate valid FQDN entries ending in "." */ len = strlen((const char*)cert_name); if (len && strcmp((const char *)&cert_name[len-1], ".") == 0) { cert_name[len-1] = '\0'; } if (php_mongodb_matches_wildcard_name(subject_name, (const char *)cert_name) == SUCCESS) { OPENSSL_free(cert_name); return SUCCESS; } OPENSSL_free(cert_name); } else if (san->type == GEN_IPADD) { if (san->d.iPAddress->length == 4) { sprintf(ipbuffer, "%d.%d.%d.%d", san->d.iPAddress->data[0], san->d.iPAddress->data[1], san->d.iPAddress->data[2], san->d.iPAddress->data[3] ); if (strcasecmp(subject_name, (const char*)ipbuffer) == 0) { return SUCCESS; } } /* No, we aren't bothering to check IPv6 addresses. Why? * Because IP SAN names are officially deprecated and are * not allowed by CAs starting in 2015. Deal with it. */ } } return FAILURE; }
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; }
bool PeerID::Certificate::checkAltNames(boost::asio::ip::address& address) const { GENERAL_NAMES* gens = static_cast<GENERAL_NAMES*>( X509_get_ext_d2i(x509_, NID_subject_alt_name, 0, 0)); BOOST_SCOPE_EXIT(gens){ GENERAL_NAMES_free(gens); }BOOST_SCOPE_EXIT_END; for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) { GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); if (gen->type == GEN_IPADD) { ASN1_OCTET_STRING* ip = gen->d.iPAddress; if (ip->type == V_ASN1_OCTET_STRING && ip->data) { return ( (address.is_v4() && ip->length == 4 && std::memcmp(address.to_v4().to_bytes().data(), ip->data, 4) == 0) || (address.is_v6() && ip->length == 16 && std::memcmp(address.to_v6().to_bytes().data(), ip->data, 16) == 0) ); } } } }
/* 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; }
static int crl_revoked_issuer_match(X509_CRL *crl, X509_NAME *nm, X509_REVOKED *rev) { int i; if (!rev->issuer) { if (!nm) return 1; if (!X509_NAME_cmp(nm, X509_CRL_get_issuer(crl))) return 1; return 0; } if (!nm) nm = X509_CRL_get_issuer(crl); for (i = 0; i < sk_GENERAL_NAME_num(rev->issuer); i++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(rev->issuer, i); if (gen->type != GEN_DIRNAME) continue; if (!X509_NAME_cmp(nm, gen->d.directoryName)) return 1; } return 0; }
std::unique_ptr<std::list<std::string>> SSLUtil::getSubjectAltName( const X509* cert) { #ifdef OPENSSL_GE_101 auto nameList = folly::make_unique<std::list<std::string>>(); GENERAL_NAMES* names = (GENERAL_NAMES*)X509_get_ext_d2i( (X509*)cert, NID_subject_alt_name, nullptr, nullptr); if (names) { auto guard = folly::makeGuard([names] { GENERAL_NAMES_free(names); }); size_t count = sk_GENERAL_NAME_num(names); CHECK(count < std::numeric_limits<int>::max()); for (int i = 0; i < (int)count; ++i) { GENERAL_NAME* generalName = sk_GENERAL_NAME_value(names, i); if (generalName->type == GEN_DNS) { ASN1_STRING* s = generalName->d.dNSName; const char* name = (const char*)ASN1_STRING_data(s); // I can't find any docs on what a negative return value here // would mean, so I'm going to ignore it. auto len = ASN1_STRING_length(s); DCHECK(len >= 0); if (size_t(len) != strlen(name)) { // Null byte(s) in the name; return an error rather than depending on // the caller to safely handle this case. return nullptr; } nameList->emplace_back(name); } } } return nameList; #else return nullptr; #endif }
static void setup_dp(X509 *x, DIST_POINT *dp) { X509_NAME *iname = NULL; int i; if (dp->reasons) { if (dp->reasons->length > 0) dp->dp_reasons = dp->reasons->data[0]; if (dp->reasons->length > 1) dp->dp_reasons |= (dp->reasons->data[1] << 8); dp->dp_reasons &= CRLDP_ALL_REASONS; } else dp->dp_reasons = CRLDP_ALL_REASONS; if (!dp->distpoint || (dp->distpoint->type != 1)) return; for (i = 0; i < sk_GENERAL_NAME_num(dp->CRLissuer); i++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->CRLissuer, i); if (gen->type == GEN_DIRNAME) { iname = gen->d.directoryName; break; } } if (!iname) iname = X509_get_issuer_name(x); DIST_POINT_set_dpname(dp->distpoint, iname); }
char * check_alt_names(char *host, char *fqdn, X509 *x509) { char *found, *buf; const GENERAL_NAMES *ans; const GENERAL_NAME *p; int n; ans = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); if (ans == NULL) return (NULL); n = sk_GENERAL_NAME_num(ans); found = NULL; while (n-- > 0 && found == NULL) { p = sk_GENERAL_NAME_value(ans, n); if (p == NULL || p->type != GEN_DNS) continue; if (ASN1_STRING_to_UTF8((u_char **)&buf, p->d.dNSName) <= 0) continue; if (fnmatch(buf, host, FNM_NOESCAPE|FNM_CASEFOLD) == 0 || (fqdn != NULL && fnmatch(buf, fqdn, FNM_NOESCAPE|FNM_CASEFOLD) == 0)) found = buf; OPENSSL_free(buf); } sk_GENERAL_NAME_free(ans); return (found); }
int subjectaltnameaddr(X509 *cert, int family, const struct in6_addr *addr) { int loc, i, l, n, r = 0; char *v; X509_EXTENSION *ex; STACK_OF(GENERAL_NAME) *alt; GENERAL_NAME *gn; debug(DBG_DBG, "subjectaltnameaddr"); loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); if (loc < 0) return r; ex = X509_get_ext(cert, loc); alt = X509V3_EXT_d2i(ex); if (!alt) return r; n = sk_GENERAL_NAME_num(alt); for (i = 0; i < n; i++) { gn = sk_GENERAL_NAME_value(alt, i); if (gn->type != GEN_IPADD) continue; r = -1; v = (char *)ASN1_STRING_data(gn->d.ia5); l = ASN1_STRING_length(gn->d.ia5); if (((family == AF_INET && l == sizeof(struct in_addr)) || (family == AF_INET6 && l == sizeof(struct in6_addr))) && !memcmp(v, &addr, l)) { r = 1; break; } } GENERAL_NAMES_free(alt); return r; }
/** * 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; }
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); }
static int verify_hostname(const char *hostname, X509 *cert, char *errbuf, size_t errlen) { int i; /* domainname is the "domain" we wan't to access (actually hostname * with first part of the DNS name removed) */ const char *domainname = strchr(hostname, '.'); if(domainname != NULL) { domainname++; if(strlen(domainname) == 0) domainname = NULL; } // First check commonName X509_NAME *subjectName; char commonName[256]; subjectName = X509_get_subject_name(cert); if(X509_NAME_get_text_by_NID(subjectName, NID_commonName, commonName, sizeof(commonName)) != -1) { if(!strcmp(commonName, hostname)) return 0; } // Then check altNames GENERAL_NAMES *names = X509_get_ext_d2i( cert, NID_subject_alt_name, 0, 0); if(names == NULL) { snprintf(errbuf, errlen, "SSL: No subjectAltName extension"); return -1; } const int num_names = sk_GENERAL_NAME_num(names); for(i = 0; i < num_names; ++i ) { GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i); unsigned char *dns; int match; if(name->type != GEN_DNS) continue; ASN1_STRING_to_UTF8(&dns, name->d.dNSName); if(dns[0] == '*' && dns[1] == '.') { match = domainname != NULL && !strcasecmp((char *)dns+2, domainname); } else { match = !strcasecmp((char *)dns, hostname); } OPENSSL_free(dns); if(match) return 0; } snprintf(errbuf, errlen, "SSL: Hostname mismatch"); return -1; }
/* 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 OpenSSLCertificate::parse() { if (!cert) { return; } // Subject name X509_NAME* subjectName = X509_get_subject_name(cert.get()); if (subjectName) { // Subject name ByteArray subjectNameData; subjectNameData.resize(256); X509_NAME_oneline(X509_get_subject_name(cert.get()), reinterpret_cast<char*>(subjectNameData.getData()), subjectNameData.getSize()); this->subjectName = std::string(reinterpret_cast<const char*>(subjectNameData.getData())); // Common name int cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, -1); while (cnLoc != -1) { X509_NAME_ENTRY* cnEntry = X509_NAME_get_entry(subjectName, cnLoc); ASN1_STRING* cnData = X509_NAME_ENTRY_get_data(cnEntry); commonNames.push_back(ByteArray(cnData->data, cnData->length).toString()); cnLoc = X509_NAME_get_index_by_NID(subjectName, NID_commonName, cnLoc); } } // subjectAltNames int subjectAltNameLoc = X509_get_ext_by_NID(cert.get(), NID_subject_alt_name, -1); if(subjectAltNameLoc != -1) { X509_EXTENSION* extension = X509_get_ext(cert.get(), subjectAltNameLoc); boost::shared_ptr<GENERAL_NAMES> generalNames(reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(extension)), GENERAL_NAMES_free); boost::shared_ptr<ASN1_OBJECT> xmppAddrObject(OBJ_txt2obj(ID_ON_XMPPADDR_OID, 1), ASN1_OBJECT_free); boost::shared_ptr<ASN1_OBJECT> dnsSRVObject(OBJ_txt2obj(ID_ON_DNSSRV_OID, 1), ASN1_OBJECT_free); for (int i = 0; i < sk_GENERAL_NAME_num(generalNames.get()); ++i) { GENERAL_NAME* generalName = sk_GENERAL_NAME_value(generalNames.get(), i); if (generalName->type == GEN_OTHERNAME) { OTHERNAME* otherName = generalName->d.otherName; if (OBJ_cmp(otherName->type_id, xmppAddrObject.get()) == 0) { // XmppAddr if (otherName->value->type != V_ASN1_UTF8STRING) { continue; } ASN1_UTF8STRING* xmppAddrValue = otherName->value->value.utf8string; addXMPPAddress(ByteArray(ASN1_STRING_data(xmppAddrValue), ASN1_STRING_length(xmppAddrValue)).toString()); } else if (OBJ_cmp(otherName->type_id, dnsSRVObject.get()) == 0) { // SRVName if (otherName->value->type != V_ASN1_IA5STRING) { continue; } ASN1_IA5STRING* srvNameValue = otherName->value->value.ia5string; addSRVName(ByteArray(ASN1_STRING_data(srvNameValue), ASN1_STRING_length(srvNameValue)).toString()); } } else if (generalName->type == GEN_DNS) { // DNSName addDNSName(ByteArray(ASN1_STRING_data(generalName->d.dNSName), ASN1_STRING_length(generalName->d.dNSName)).toString()); } } } }
/* * For each name in the cert. Iterate them. Call the callback. If one returns true, then consider it validated, * if none of them return true, the cert is considered invalid. */ static uint8_t s2n_verify_host_information(struct s2n_x509_validator *validator, struct s2n_connection *conn, X509 *public_cert) { uint8_t verified = 0; uint8_t san_found = 0; /* Check SubjectAltNames before CommonName as per RFC 6125 6.4.4 */ STACK_OF(GENERAL_NAME) *names_list = X509_get_ext_d2i(public_cert, NID_subject_alt_name, NULL, NULL); int n = sk_GENERAL_NAME_num(names_list); for (int i = 0; i < n && !verified; i++) { GENERAL_NAME *current_name = sk_GENERAL_NAME_value(names_list, i); if (current_name->type == GEN_DNS) { san_found = 1; const char *name = (const char *) ASN1_STRING_data(current_name->d.ia5); size_t name_len = (size_t) ASN1_STRING_length(current_name->d.ia5); verified = conn->verify_host_fn(name, name_len, conn->data_for_verify_host); } } GENERAL_NAMES_free(names_list); /* if no SubjectAltNames of type DNS found, go to the common name. */ if (!san_found) { X509_NAME *subject_name = X509_get_subject_name(public_cert); if (subject_name) { int next_idx = 0, curr_idx = -1; while ((next_idx = X509_NAME_get_index_by_NID(subject_name, NID_commonName, curr_idx)) >= 0) { curr_idx = next_idx; } if (curr_idx >= 0) { ASN1_STRING *common_name = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, curr_idx)); if (common_name) { char peer_cn[255]; static size_t peer_cn_size = sizeof(peer_cn); memset_check(&peer_cn, 0, peer_cn_size); // X520CommonName allows the following ANSI string types per RFC 5280 Appendix A.1 if (ASN1_STRING_type(common_name) == V_ASN1_TELETEXSTRING || ASN1_STRING_type(common_name) == V_ASN1_PRINTABLESTRING || ASN1_STRING_type(common_name) == V_ASN1_UNIVERSALSTRING || ASN1_STRING_type(common_name) == V_ASN1_UTF8STRING || ASN1_STRING_type(common_name) == V_ASN1_BMPSTRING ) { size_t len = (size_t) ASN1_STRING_length(common_name); lte_check(len, sizeof(peer_cn) - 1); memcpy_check(peer_cn, ASN1_STRING_data(common_name), len); verified = conn->verify_host_fn(peer_cn, len, conn->data_for_verify_host); } } } } } return verified; }
static void CheckCRL(X509 *x509) { int idx = -1; do { int critical = -1; STACK_OF(DIST_POINT) *crls = X509_get_ext_d2i(x509, NID_crl_distribution_points, &critical, &idx); if (crls == NULL) { if (critical >= 0) { /* Found but fails to parse */ SetError(ERR_INVALID); continue; } /* Not found */ break; } for (int i = 0; i < sk_DIST_POINT_num(crls); i++) { DIST_POINT *dp = sk_DIST_POINT_value(crls, i); if (dp->distpoint == NULL && dp->CRLissuer == NULL) { SetError(ERR_INVALID_CRL_DIST_POINT); } if (dp->distpoint != NULL && dp->distpoint->type == 0) { /* full name */ for (int j = 0; j < sk_GENERAL_NAME_num(dp->distpoint->name.fullname); j++) { GENERAL_NAME *gen = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, j); int type; ASN1_STRING *uri = GENERAL_NAME_get0_value(gen, &type); if (type == GEN_URI) { CheckValidURL(ASN1_STRING_get0_data(uri), ASN1_STRING_length(uri)); } else { SetInfo(INF_CRL_NOT_URL); } CheckGeneralNameType(gen); } } else { /* relative name */ SetWarning(WARN_CRL_RELATIVE); } } sk_DIST_POINT_pop_free(crls, DIST_POINT_free); } while (1); }
/* This code is based heavily on the example provided in "Secure Programming * Cookbook for C and C++". */ int _mosquitto_verify_certificate_hostname(X509 *cert, const char *hostname) { int i; char name[256]; X509_NAME *subj; bool have_san_dns = false; STACK_OF(GENERAL_NAME) *san; const GENERAL_NAME *nval; const unsigned char *data; unsigned char ipv6_addr[16]; unsigned char ipv4_addr[4]; int ipv6_ok; int ipv4_ok; #ifdef WIN32 ipv6_ok = InetPton(AF_INET6, hostname, &ipv6_addr); ipv4_ok = InetPton(AF_INET, hostname, &ipv4_addr); #else ipv6_ok = inet_pton(AF_INET6, hostname, &ipv6_addr); ipv4_ok = inet_pton(AF_INET, hostname, &ipv4_addr); #endif san = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if(san){ for(i=0; i<sk_GENERAL_NAME_num(san); i++){ nval = sk_GENERAL_NAME_value(san, i); if(nval->type == GEN_DNS){ data = ASN1_STRING_data(nval->d.dNSName); if(data && match_hostname((char *)data, hostname)){ return 1; } have_san_dns = true; }else if(nval->type == GEN_IPADD){ data = ASN1_STRING_data(nval->d.iPAddress); if(nval->d.iPAddress->length == 4 && ipv4_ok){ if(!memcmp(ipv4_addr, data, 4)){ return 1; } }else if(nval->d.iPAddress->length == 16 && ipv6_ok){ if(!memcmp(ipv6_addr, data, 16)){ return 1; } } } } if(have_san_dns){ /* Only check CN if subjectAltName DNS entry does not exist. */ return 0; } } subj = X509_get_subject_name(cert); if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){ name[sizeof(name) - 1] = '\0'; if (!strcasecmp(name, hostname)) return 1; } return 0; }
static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert, GENERAL_NAMES *names) { int num, i; num = sk_GENERAL_NAME_num(names); for (i = 0; i < num; i++) { const GENERAL_NAME *name; name = sk_GENERAL_NAME_value(names, i); add_alt_name(ctx, cert, name); } }
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; }
int subjectaltnameregexp(X509 *cert, int type, const char *exact, const regex_t *regex) { int loc, i, l, n, r = 0; char *s, *v; X509_EXTENSION *ex; STACK_OF(GENERAL_NAME) *alt; GENERAL_NAME *gn; debug(DBG_DBG, "subjectaltnameregexp"); loc = X509_get_ext_by_NID(cert, NID_subject_alt_name, -1); if (loc < 0) return r; ex = X509_get_ext(cert, loc); alt = X509V3_EXT_d2i(ex); if (!alt) return r; n = sk_GENERAL_NAME_num(alt); for (i = 0; i < n; i++) { gn = sk_GENERAL_NAME_value(alt, i); if (gn->type != type) continue; r = -1; v = (char *)ASN1_STRING_data(gn->d.ia5); l = ASN1_STRING_length(gn->d.ia5); if (l <= 0) continue; #ifdef DEBUG printfchars(NULL, gn->type == GEN_DNS ? "dns" : "uri", NULL, v, l); #endif if (exact) { if (memcmp(v, exact, l)) continue; } else { s = stringcopy((char *)v, l); if (!s) { debug(DBG_ERR, "malloc failed"); continue; } if (regexec(regex, s, 0, NULL, 0)) { free(s); continue; } free(s); } r = 1; break; } GENERAL_NAMES_free(alt); return r; }
static bool extract_x509_extension(X509 *cert, char *fieldname, char *out, int size) { bool retval = false; X509_EXTENSION *pExt; char *buf = 0; int length = 0; GENERAL_NAMES *extensions; int nid = OBJ_txt2nid(fieldname); extensions = (GENERAL_NAMES *)X509_get_ext_d2i(cert, nid, NULL, NULL); if ( extensions ) { int numalts; int i; /* get amount of alternatives, * RFC2459 claims there MUST be at least * one, but we don't depend on it... */ numalts = sk_GENERAL_NAME_num(extensions); /* loop through all alternatives */ for (i=0; i<numalts; i++) { /* get a handle to alternative name number i */ const GENERAL_NAME *name = sk_GENERAL_NAME_value (extensions, i ); switch (name->type) { case GEN_EMAIL: ASN1_STRING_to_UTF8((unsigned char**)&buf, name->d.ia5); if ( strlen (buf) != name->d.ia5->length ) { msg (D_TLS_ERRORS, "ASN1 ERROR: string contained terminating zero"); OPENSSL_free (buf); } else { strncpynt(out, buf, size); OPENSSL_free(buf); retval = true; } break; default: msg (D_TLS_ERRORS, "ASN1 ERROR: can not handle field type %i", name->type); break; } } sk_GENERAL_NAME_free (extensions); } return retval; }
/* Server certificate name check, logic adapted from libcurl */ static int _SSL_check_server_cert(SSL *ssl, const char *hostname) { X509 *cert; X509_NAME *subject; const GENERAL_NAME *altname; STACK_OF(GENERAL_NAME) *altnames; ASN1_STRING *tmp; int i, n, match = -1; const char *p; if (SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE || (cert = SSL_get_peer_certificate(ssl)) == NULL) { return (1); } /* Check subjectAltName */ if ((altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL)) != NULL) { n = sk_GENERAL_NAME_num(altnames); for (i = 0; i < n && match != 1; i++) { altname = sk_GENERAL_NAME_value(altnames, i); p = (char *)ASN1_STRING_data(altname->d.ia5); if (altname->type == GEN_DNS) { match = (ASN1_STRING_length(altname->d.ia5) == strlen(p) && match_pattern(hostname, p)); } } GENERAL_NAMES_free(altnames); } /* No subjectAltName, try CN */ if (match == -1 && (subject = X509_get_subject_name(cert)) != NULL) { for (i = -1; (n = X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0; ) { i = n; } if (i >= 0) { if ((tmp = X509_NAME_ENTRY_get_data( X509_NAME_get_entry(subject, i))) != NULL && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { p = (char *)ASN1_STRING_data(tmp); match = (ASN1_STRING_length(tmp) == strlen(p) && match_pattern(hostname, p)); } } } X509_free(cert); return (match > 0); }
int X509_check_issued(X509 *issuer, X509 *subject) { if(X509_NAME_cmp(X509_get_subject_name(issuer), X509_get_issuer_name(subject))) return X509_V_ERR_SUBJECT_ISSUER_MISMATCH; x509v3_cache_extensions(issuer); x509v3_cache_extensions(subject); if(subject->akid) { /* Check key ids (if present) */ if(subject->akid->keyid && issuer->skid && ASN1_OCTET_STRING_cmp(subject->akid->keyid, issuer->skid) ) return X509_V_ERR_AKID_SKID_MISMATCH; /* Check serial number */ if(subject->akid->serial && ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), subject->akid->serial)) return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; /* Check issuer name */ if(subject->akid->issuer) { /* Ugh, for some peculiar reason AKID includes * SEQUENCE OF GeneralName. So look for a DirName. * There may be more than one but we only take any * notice of the first. */ GENERAL_NAMES *gens; GENERAL_NAME *gen; X509_NAME *nm = NULL; int i; gens = subject->akid->issuer; for(i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); if(gen->type == GEN_DIRNAME) { nm = gen->d.dirn; break; } } if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer))) return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; } } if(subject->ex_flags & EXFLAG_PROXY) { if(ku_reject(issuer, KU_DIGITAL_SIGNATURE)) return X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE; } else if(ku_reject(issuer, KU_KEY_CERT_SIGN)) return X509_V_ERR_KEYUSAGE_NO_CERTSIGN; return X509_V_OK; }
/* 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; }
static int tls_match_altsubject(X509 *cert, const char *match) { GENERAL_NAME *gen; char *field, *tmp; void *ext; int i, found = 0; size_t len; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { gen = sk_GENERAL_NAME_value(ext, i); switch (gen->type) { case GEN_EMAIL: field = "EMAIL"; break; case GEN_DNS: field = "DNS"; break; case GEN_URI: field = "URI"; break; default: field = NULL; wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " "unsupported type=%d", gen->type); break; } if (!field) continue; wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", field, gen->d.ia5->data); len = os_strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + 1; tmp = os_malloc(len); if (tmp == NULL) continue; snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); if (strstr(tmp, match)) found++; os_free(tmp); } return found; }
/* 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; }
int X509_check_akid(X509 *issuer, AUTHORITY_KEYID *akid) { if(!akid) return X509_V_OK; /* Check key ids (if present) */ if(akid->keyid && issuer->skid && ASN1_OCTET_STRING_cmp(akid->keyid, issuer->skid) ) return X509_V_ERR_AKID_SKID_MISMATCH; /* Check serial number */ if(akid->serial && ASN1_INTEGER_cmp(X509_get_serialNumber(issuer), akid->serial)) return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; /* Check issuer name */ if(akid->issuer) { /* Ugh, for some peculiar reason AKID includes * SEQUENCE OF GeneralName. So look for a DirName. * There may be more than one but we only take any * notice of the first. */ GENERAL_NAMES *gens; GENERAL_NAME *gen; X509_NAME *nm = NULL; int i; gens = akid->issuer; for(i = 0; i < sk_GENERAL_NAME_num(gens); i++) { gen = sk_GENERAL_NAME_value(gens, i); if(gen->type == GEN_DIRNAME) { nm = gen->d.dirn; break; } } if(nm && X509_NAME_cmp(nm, X509_get_issuer_name(issuer))) return X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH; } return X509_V_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; }
static int openssl_xext_totable(lua_State* L, X509_EXTENSION *x) { lua_newtable(L); openssl_push_asn1object(L, x->object); lua_setfield(L, -2, "object"); PUSH_ASN1_OCTET_STRING(L, x->value); lua_setfield(L, -2, "value"); AUXILIAR_SET(L, -1, "critical", x->critical, boolean); switch (x->object->nid) { case NID_subject_alt_name: { int i; int n_general_names; STACK_OF(GENERAL_NAME) *values = X509V3_EXT_d2i(x); if (values == NULL) break; /* Push ret[oid] */ openssl_push_asn1object(L, x->object); lua_newtable(L); n_general_names = sk_GENERAL_NAME_num(values); for (i = 0; i < n_general_names; i++) { GENERAL_NAME *general_name = sk_GENERAL_NAME_value(values, i); openssl_push_general_name(L, general_name); lua_rawseti(L, -2, i + 1); } lua_settable(L, -3); } default: break; } return 1; };