// This function taken from openvpn-2.2.1 and tidied up a bit. static int setenv_x509(X509_NAME *x509, const char *type) { int i, n; int fn_nid; ASN1_OBJECT *fn; ASN1_STRING *val; X509_NAME_ENTRY *ent; const char *objbuf; uint8_t *buf; char *name_expand; size_t name_expand_size; n=X509_NAME_entry_count (x509); for(i=0; i<n; ++i) { if(!(ent=X509_NAME_get_entry (x509, i)) || !(fn=X509_NAME_ENTRY_get_object(ent)) || !(val=X509_NAME_ENTRY_get_data(ent)) || (fn_nid=OBJ_obj2nid(fn))==NID_undef || !(objbuf=OBJ_nid2sn(fn_nid))) continue; buf=(uint8_t *)1; /* bug in OpenSSL 0.9.6b ASN1_STRING_to_UTF8 requires this workaround */ if(ASN1_STRING_to_UTF8(&buf, val)<=0) continue; name_expand_size=64+strlen(objbuf); if(!(name_expand=(char *)malloc_w(name_expand_size, __func__))) return -1; snprintf(name_expand, name_expand_size, "X509_%s_%s", type, objbuf); sanitise(name_expand); sanitise((char*)buf); setenv(name_expand, (char*)buf, 1); free (name_expand); OPENSSL_free (buf); } return 0; }
int cnregexp(X509 *cert, const char *exact, const regex_t *regex) { int loc, l; char *v, *s; X509_NAME *nm; X509_NAME_ENTRY *e; ASN1_STRING *t; nm = X509_get_subject_name(cert); loc = -1; for (;;) { loc = X509_NAME_get_index_by_NID(nm, NID_commonName, loc); if (loc == -1) break; e = X509_NAME_get_entry(nm, loc); t = X509_NAME_ENTRY_get_data(e); v = (char *) ASN1_STRING_data(t); l = ASN1_STRING_length(t); if (l < 0) continue; if (exact) { if (l == strlen(exact) && !strncasecmp(exact, v, l)) return 1; } 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); return 1; } } return 0; }
/** * Tries to find a match for hostname in the certificate's Common Name field. * * Returns MatchFound if a match was found. * Returns MatchNotFound if no matches were found. * Returns MalformedCertificate if the Common Name had a NUL character embedded in it. * Returns Error if the Common Name could not be extracted. */ static HostnameValidationResult matches_common_name(const char *hostname, const X509 *server_cert) { int common_name_loc = -1; X509_NAME_ENTRY *common_name_entry = NULL; ASN1_STRING *common_name_asn1 = NULL; char *common_name_str = NULL; // Find the position of the CN field in the Subject field of the certificate common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1); if (common_name_loc < 0) { return Error; } // Extract the CN field common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc); if (common_name_entry == NULL) { return Error; } // Convert the CN field to a C string common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); if (common_name_asn1 == NULL) { return Error; } common_name_str = (char *) ASN1_STRING_data(common_name_asn1); // Make sure there isn't an embedded NUL character in the CN if ((size_t)ASN1_STRING_length(common_name_asn1) != strlen(common_name_str)) { return MalformedCertificate; } // Compare expected hostname with the CN if (Curl_cert_hostcheck(common_name_str, hostname) == CURL_HOST_MATCH) { return MatchFound; } else { return MatchNotFound; } }
string CryptoManager::getNameEntryByNID(X509_NAME* name, int nid) noexcept{ int i = X509_NAME_get_index_by_NID(name, nid, -1); if (i == -1) { return Util::emptyString; } X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i); ASN1_STRING* str = X509_NAME_ENTRY_get_data(entry); if (!str) { return Util::emptyString; } unsigned char* buf = 0; i = ASN1_STRING_to_UTF8(&buf, str); if (i < 0) { return Util::emptyString; } std::string out((char*)buf, i); OPENSSL_free(buf); return out; }
static int tls_cert_get_dname_string(struct tls *ctx, X509_NAME *name, int nid, const char **str_p, int minchars, int maxchars, const char *desc) { int loc, len; X509_NAME_ENTRY *ne; ASN1_STRING *a1str; *str_p = NULL; loc = X509_NAME_get_index_by_NID(name, nid, -1); if (loc < 0) return 0; ne = X509_NAME_get_entry(name, loc); if (!ne) return 0; a1str = X509_NAME_ENTRY_get_data(ne); if (!a1str) return 0; len = tls_parse_asn1string(ctx, a1str, str_p, minchars, maxchars, desc); if (len < 0) return -1; return 0; }
MONO_API int mono_btls_x509_name_get_entry_value (MonoBtlsX509Name *name, int index, int *tag, unsigned char **str) { X509_NAME_ENTRY *entry; ASN1_STRING *data; *str = NULL; *tag = 0; if (index >= X509_NAME_entry_count (name->name)) return 0; entry = X509_NAME_get_entry (name->name, index); if (!entry) return 0; data = X509_NAME_ENTRY_get_data (entry); if (!data) return 0; *tag = data->type; return ASN1_STRING_to_UTF8 (str, data); }
/* Gets the subject CN from an X509 cert. */ static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, size_t* utf8_size) { int common_name_index = -1; X509_NAME_ENTRY* common_name_entry = NULL; ASN1_STRING* common_name_asn1 = NULL; X509_NAME* subject_name = X509_get_subject_name(cert); int utf8_returned_size = 0; if (subject_name == NULL) { gpr_log(GPR_ERROR, "Could not get subject name from certificate."); return TSI_NOT_FOUND; } common_name_index = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); if (common_name_index == -1) { gpr_log(GPR_ERROR, "Could not get common name of subject from certificate."); return TSI_NOT_FOUND; } common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); if (common_name_entry == NULL) { gpr_log(GPR_ERROR, "Could not get common name entry from certificate."); return TSI_INTERNAL_ERROR; } common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); if (common_name_asn1 == NULL) { gpr_log(GPR_ERROR, "Could not get common name entry asn1 from certificate."); return TSI_INTERNAL_ERROR; } utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1); if (utf8_returned_size < 0) { gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); return TSI_OUT_OF_RESOURCES; } *utf8_size = utf8_returned_size; return TSI_OK; }
/** * Tries to find a match for hostname in the certificate's Common Name field. * * Returns MatchFound if a match was found. * Returns MatchNotFound if no matches were found. * Returns MalformedCertificate if the Common Name had a NUL character embedded in it. * Returns Error if the Common Name could not be extracted. */ static HostnameValidationResult matches_common_name(const char *hostname, const X509 *server_cert) { int common_name_loc = -1; X509_NAME_ENTRY *common_name_entry = NULL; ASN1_STRING *common_name_asn1 = NULL; // Find the position of the CN field in the Subject field of the certificate common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name((X509 *) server_cert), NID_commonName, -1); if (common_name_loc < 0) { return Error; } // Extract the CN field common_name_entry = X509_NAME_get_entry(X509_get_subject_name((X509 *) server_cert), common_name_loc); if (common_name_entry == NULL) { return Error; } common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); if (common_name_asn1 == NULL) { return Error; } // validate the names return validate_name(hostname, common_name_asn1); }
/* tls_text_name - extract certificate property value by name */ static char *tls_text_name(X509_NAME *name, int nid) { int pos; X509_NAME_ENTRY *entry; ASN1_STRING *entry_str; int utf8_length; unsigned char *utf8_value; char *result; if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) { return NULL; } entry = X509_NAME_get_entry(name, pos); g_return_val_if_fail(entry != NULL, NULL); entry_str = X509_NAME_ENTRY_get_data(entry); g_return_val_if_fail(entry_str != NULL, NULL); /* Convert everything into UTF-8. It's up to OpenSSL to do something reasonable when converting ASCII formats that contain non-ASCII content. */ if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) { g_warning("Error decoding ASN.1 type=%d", ASN1_STRING_type(entry_str)); return NULL; } if (has_internal_nul((char *)utf8_value, utf8_length)) { g_warning("NUL character in hostname in certificate"); OPENSSL_free(utf8_value); return NULL; } result = g_strdup((char *) utf8_value); OPENSSL_free(utf8_value); return result; }
/* Function: GetX509NameInfo Used by System.Security.Cryptography.X509Certificates' OpenSslX509CertificateReader as the entire implementation of X509Certificate2.GetNameInfo. Return values: NULL if the certificate is invalid or no name information could be found, otherwise a pointer to a memory-backed BIO structure which contains the answer to the GetNameInfo query */ BIO* GetX509NameInfo( X509* x509, int nameType, int forIssuer) { static const char szOidUpn[] = "1.3.6.1.4.1.311.20.2.3"; if (!x509 || !x509->cert_info || nameType < NAME_TYPE_SIMPLE || nameType > NAME_TYPE_URL) { return NULL; } // Algorithm behaviors (pseudocode). When forIssuer is true, replace "Subject" with "Issuer" and // SAN (Subject Alternative Names) with IAN (Issuer Alternative Names). // // SimpleName: Subject[CN] ?? Subject[OU] ?? Subject[O] ?? Subject[E] ?? Subject.Rdns.FirstOrDefault() ?? SAN.Entries.FirstOrDefault(type == GEN_EMAIL); // EmailName: SAN.Entries.FirstOrDefault(type == GEN_EMAIL) ?? Subject[E]; // UpnName: SAN.Entries.FirsOrDefaultt(type == GEN_OTHER && entry.AsOther().OID == szOidUpn).AsOther().Value; // DnsName: SAN.Entries.FirstOrDefault(type == GEN_DNS) ?? Subject[CN]; // DnsFromAlternativeName: SAN.Entries.FirstOrDefault(type == GEN_DNS); // UrlName: SAN.Entries.FirstOrDefault(type == GEN_URI); if (nameType == NAME_TYPE_SIMPLE) { X509_NAME* name = forIssuer ? x509->cert_info->issuer : x509->cert_info->subject; if (name) { ASN1_STRING* cn = NULL; ASN1_STRING* ou = NULL; ASN1_STRING* o = NULL; ASN1_STRING* e = NULL; ASN1_STRING* firstRdn = NULL; // Walk the list backwards because it is stored in stack order for (int i = X509_NAME_entry_count(name) - 1; i >= 0; --i) { X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i); if (!entry) { continue; } ASN1_OBJECT* oid = X509_NAME_ENTRY_get_object(entry); ASN1_STRING* str = X509_NAME_ENTRY_get_data(entry); if (!oid || !str) { continue; } int nid = OBJ_obj2nid(oid); if (nid == NID_commonName) { // CN wins, so no need to keep looking. cn = str; break; } else if (nid == NID_organizationalUnitName) { ou = str; } else if (nid == NID_organizationName) { o = str; } else if (nid == NID_pkcs9_emailAddress) { e = str; } else if (!firstRdn) { firstRdn = str; } } ASN1_STRING* answer = cn; // If there was no CN, but there was something, then perform fallbacks. if (!answer && firstRdn) { answer = ou; if (!answer) { answer = o; } if (!answer) { answer = e; } if (!answer) { answer = firstRdn; } } if (answer) { BIO* b = BIO_new(BIO_s_mem()); ASN1_STRING_print_ex(b, answer, 0); return b; } } } if (nameType == NAME_TYPE_SIMPLE || nameType == NAME_TYPE_DNS || nameType == NAME_TYPE_DNSALT || nameType == NAME_TYPE_EMAIL || nameType == NAME_TYPE_UPN || nameType == NAME_TYPE_URL) { int expectedType = -1; switch (nameType) { case NAME_TYPE_DNS: case NAME_TYPE_DNSALT: expectedType = GEN_DNS; break; case NAME_TYPE_SIMPLE: case NAME_TYPE_EMAIL: expectedType = GEN_EMAIL; break; case NAME_TYPE_UPN: expectedType = GEN_OTHERNAME; break; case NAME_TYPE_URL: expectedType = GEN_URI; break; } STACK_OF(GENERAL_NAME)* altNames = X509_get_ext_d2i(x509, forIssuer ? NID_issuer_alt_name : NID_subject_alt_name, NULL, NULL); if (altNames) { int i; for (i = 0; i < sk_GENERAL_NAME_num(altNames); ++i) { GENERAL_NAME* altName = sk_GENERAL_NAME_value(altNames, i); if (altName && altName->type == expectedType) { ASN1_STRING* str = NULL; switch (nameType) { case NAME_TYPE_DNS: case NAME_TYPE_DNSALT: str = altName->d.dNSName; break; case NAME_TYPE_SIMPLE: case NAME_TYPE_EMAIL: str = altName->d.rfc822Name; break; case NAME_TYPE_URL: str = altName->d.uniformResourceIdentifier; break; case NAME_TYPE_UPN: { OTHERNAME* value = altName->d.otherName; if (value) { // Enough more padding than szOidUpn that a \0 won't accidentally align char localOid[sizeof(szOidUpn) + 3]; int cchLocalOid = 1 + OBJ_obj2txt(localOid, sizeof(localOid), value->type_id, 1); if (sizeof(szOidUpn) == cchLocalOid && 0 == strncmp(localOid, szOidUpn, sizeof(szOidUpn))) { //OTHERNAME->ASN1_TYPE->union.field str = value->value->value.asn1_string; } } break; } } if (str) { BIO* b = BIO_new(BIO_s_mem()); ASN1_STRING_print_ex(b, str, 0); sk_GENERAL_NAME_free(altNames); return b; } } } sk_GENERAL_NAME_free(altNames); } } if (nameType == NAME_TYPE_EMAIL || nameType == NAME_TYPE_DNS) { X509_NAME* name = forIssuer ? x509->cert_info->issuer : x509->cert_info->subject; int expectedNid = NID_undef; switch (nameType) { case NAME_TYPE_EMAIL: expectedNid = NID_pkcs9_emailAddress; break; case NAME_TYPE_DNS: expectedNid = NID_commonName; break; } if (name) { // Walk the list backwards because it is stored in stack order for (int i = X509_NAME_entry_count(name) - 1; i >= 0; --i) { X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, i); if (!entry) { continue; } ASN1_OBJECT* oid = X509_NAME_ENTRY_get_object(entry); ASN1_STRING* str = X509_NAME_ENTRY_get_data(entry); if (!oid || !str) { continue; } int nid = OBJ_obj2nid(oid); if (nid == expectedNid) { BIO* b = BIO_new(BIO_s_mem()); ASN1_STRING_print_ex(b, str, 0); return b; } } } } return NULL; }
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); } }
static char *tls_text_name(X509_NAME *name, int nid, const char *label, const TLS_SESS_STATE *TLScontext, int gripe) { const char *myname = "tls_text_name"; int pos; X509_NAME_ENTRY *entry; ASN1_STRING *entry_str; int asn1_type; int utf8_length; unsigned char *utf8_value; int ch; unsigned char *cp; if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) { if (gripe != DONT_GRIPE) { msg_warn("%s: %s: peer certificate has no %s", myname, TLScontext->namaddr, label); tls_print_errors(); } return (0); } #if 0 /* * If the match is required unambiguous, insist that that no other values * be present. */ if (X509_NAME_get_index_by_NID(name, nid, pos) >= 0) { msg_warn("%s: %s: multiple %ss in peer certificate", myname, TLScontext->namaddr, label); return (0); } #endif if ((entry = X509_NAME_get_entry(name, pos)) == 0) { /* This should not happen */ msg_warn("%s: %s: error reading peer certificate %s entry", myname, TLScontext->namaddr, label); tls_print_errors(); return (0); } if ((entry_str = X509_NAME_ENTRY_get_data(entry)) == 0) { /* This should not happen */ msg_warn("%s: %s: error reading peer certificate %s data", myname, TLScontext->namaddr, label); tls_print_errors(); return (0); } /* * XXX Convert everything into UTF-8. This is a super-set of ASCII, so we * don't have to bother with separate code paths for ASCII-like content. * If the payload is ASCII then we won't waste lots of CPU cycles * converting it into UTF-8. It's up to OpenSSL to do something * reasonable when converting ASCII formats that contain non-ASCII * content. * * XXX Don't bother optimizing the string length error check. It is not * worth the complexity. */ asn1_type = ASN1_STRING_type(entry_str); if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) { msg_warn("%s: %s: error decoding peer %s of ASN.1 type=%d", myname, TLScontext->namaddr, label, asn1_type); tls_print_errors(); return (0); } /* * No returns without cleaning up. A good optimizer will replace multiple * blocks of identical code by jumps to just one such block. */ #define TLS_TEXT_NAME_RETURN(x) do { \ char *__tls_text_name_temp = (x); \ OPENSSL_free(utf8_value); \ return (__tls_text_name_temp); \ } while (0) /* * Remove trailing null characters. They would give false alarms with the * length check and with the embedded null check. */ #define TRIM0(s, l) do { while ((l) > 0 && (s)[(l)-1] == 0) --(l); } while (0) TRIM0(utf8_value, utf8_length); /* * Enforce the length limit, because the caller will copy the result into * a fixed-length buffer. */ if (utf8_length >= CCERT_BUFSIZ) { msg_warn("%s: %s: peer %s too long: %d", myname, TLScontext->namaddr, label, utf8_length); TLS_TEXT_NAME_RETURN(0); } /* * Reject embedded nulls in ASCII or UTF-8 names. OpenSSL is responsible * for producing properly-formatted UTF-8. */ if (utf8_length != strlen((char *) utf8_value)) { msg_warn("%s: %s: NULL character in peer %s", myname, TLScontext->namaddr, label); TLS_TEXT_NAME_RETURN(0); } /* * Reject non-printable ASCII characters in UTF-8 content. * * Note: the code below does not find control characters in illegal UTF-8 * sequences. It's OpenSSL's job to produce valid UTF-8, and reportedly, * it does validation. */ for (cp = utf8_value; (ch = *cp) != 0; cp++) { if (ISASCII(ch) && !ISPRINT(ch)) { msg_warn("%s: %s: non-printable content in peer %s", myname, TLScontext->namaddr, label); TLS_TEXT_NAME_RETURN(0); } } TLS_TEXT_NAME_RETURN(mystrdup((char *) utf8_value)); }
static int verifypeer(const struct tls_info *info, SSL *ssl) { X509 *x=NULL; X509_NAME *subj=NULL; int nentries, j; char domain[256]; char *p; char errmsg[1000]; if (!info->peer_verify_domain) return (1); if (info->isserver) { x=SSL_get_peer_certificate(ssl); if (x) subj=X509_get_subject_name(x); } else { STACK_OF(X509) *peer_cert_chain=SSL_get_peer_cert_chain(ssl); if (peer_cert_chain && peer_cert_chain->stack.num > 0) { X509 *xx=(X509 *)peer_cert_chain->stack.data[0]; if (xx) subj=X509_get_subject_name(xx); } } nentries=0; if (subj) nentries=X509_NAME_entry_count(subj); domain[0]=0; for (j=0; j<nentries; j++) { const char *obj_name; X509_NAME_ENTRY *e; ASN1_OBJECT *o; ASN1_STRING *d; int dlen; unsigned char *ddata; e=X509_NAME_get_entry(subj, j); if (!e) continue; o=X509_NAME_ENTRY_get_object(e); d=X509_NAME_ENTRY_get_data(e); if (!o || !d) continue; obj_name=OBJ_nid2sn(OBJ_obj2nid(o)); dlen=ASN1_STRING_length(d); ddata=ASN1_STRING_data(d); if (strcasecmp(obj_name, "CN") == 0) { if (dlen >= sizeof(domain)-1) dlen=sizeof(domain)-1; memcpy(domain, ddata, dlen); domain[dlen]=0; } } if (x) X509_free(x); p=domain; if (*p == '*') { int pl, l; pl=strlen(++p); l=strlen(info->peer_verify_domain); if (*p == '.' && pl <= l && strcasecmp(info->peer_verify_domain+l-pl, p) == 0) return (1); } else if (strcasecmp(info->peer_verify_domain, p) == 0) return (1); strcpy(errmsg, "couriertls: Mismatched SSL certificate: CN="); strcat(errmsg, domain); strcat(errmsg, " (expected "); strncat(errmsg, info->peer_verify_domain, 256); strcat(errmsg, ")"); (*info->tls_err_msg)(errmsg, info->app_data); return (0); }
/* * Open a ssl connection to the host listed in rc->hostname * Returns negative on error, with an errmsg in rc->errmsg. */ static int runssl( struct sec_handle * rh, in_port_t port, char *src_ip, char *ssl_fingerprint_file, char *ssl_cert_file, char *ssl_key_file, char *ssl_ca_cert_file, char *ssl_cipher_list, int ssl_check_certificate_host) { int my_socket; in_port_t my_port; struct tcp_conn *rc = rh->rc; int err; X509 *remote_cert; sockaddr_union sin; socklen_t_equiv len; if (!ssl_key_file) { security_seterror(&rh->sech, _("ssl-key-file must be set")); return -1; } if (!ssl_cert_file) { security_seterror(&rh->sech, _("ssl-cert-file must be set")); return -1; } my_socket = stream_client(src_ip, rc->hostname, port, STREAM_BUFSIZE, STREAM_BUFSIZE, &my_port, 0); if(my_socket < 0) { security_seterror(&rh->sech, "%s", strerror(errno)); return -1; } rc->read = rc->write = my_socket; len = sizeof(sin); if (getpeername(my_socket, (struct sockaddr *)&sin, &len) < 0) { security_seterror(&rh->sech, _("getpeername returned: %s\n"), strerror(errno)); return -1; } copy_sockaddr(&rc->peer, &sin); init_ssl(); /* Create an SSL_CTX structure */ rc->ctx = SSL_CTX_new(SSLv3_client_method()); if (!rc->ctx) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_CTX_set_mode(rc->ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(rc->ctx, ssl_cipher_list) == 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } /* Load the private-key corresponding to the remote certificate */ g_debug("Loading ssl-key-file private-key %s", ssl_key_file); if (SSL_CTX_use_PrivateKey_file(rc->ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Load the me certificate into the SSL_CTX structure */ g_debug("Loading ssl-cert-file certificate %s", ssl_cert_file); if (SSL_CTX_use_certificate_file(rc->ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Check if the remote certificate and private-key matches */ if (ssl_cert_file) { if (!SSL_CTX_check_private_key(rc->ctx)) { security_seterror(&rh->sech, _("Private key does not match the certificate public key")); return -1; } } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ /* This will allow this remote to verify the me's */ /* certificate. */ g_debug("Loading ssl-ca-cert-file ca %s", ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(rc->ctx, ssl_ca_cert_file, NULL)) { security_seterror(&rh->sech, "%s", ERR_error_string(ERR_get_error(), NULL)); return -1; } } else { g_debug(_("no ssl-ca-cert-file defined")); } /* Set flag in context to require peer (me) certificate */ /* verification */ if (ssl_ca_cert_file) { g_debug("Enabling certification verification"); SSL_CTX_set_verify(rc->ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify_depth(rc->ctx, 1); } else { g_debug("Not enabling certification verification"); } /* ----------------------------------------------- */ rc->ssl = SSL_new(rc->ctx); if (!rc->ssl) { security_seterror(&rh->sech, _("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } SSL_set_connect_state(rc->ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(rc->ssl, my_socket); /* Perform SSL Handshake on the SSL remote */ err = SSL_connect(rc->ssl); if (err == -1) { security_seterror(&rh->sech, _("SSL_connect failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return -1; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate(rc->ssl); if (remote_cert == NULL) { security_seterror(&rh->sech, _("server have no certificate")); return -1; } else { char *str; str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); if (ssl_check_certificate_host) { int loc = -1; char *errmsg = NULL; X509_NAME *x509_name = X509_get_subject_name(remote_cert); loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); char *cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&rc->peer, &errmsg) < 0) { security_seterror(&rh->sech, _("Common name of certicate (%s) doesn't resolv to IP (%s): %s"), cert_hostname, str_sockaddr(&rc->peer), errmsg); amfree(errmsg); return -1; } auth_debug(1, _("Certificate common name (%s) resolve to IP (%s)\n"), cert_hostname, str_sockaddr(&rc->peer)); } else { security_seterror(&rh->sech, _("Certificate have no common name")); g_debug("Certificate have no common name"); return -1; } } /* if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } */ if (ssl_fingerprint_file) { g_debug(_("run_ssl: Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { security_seterror(&rh->sech, "%s", str); amfree(str); return -1; } } X509_free (remote_cert); } g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); return 0; }
// Certificate chain verification callback: return 1 if verified, // 0 if remote cannot be verified (fail handshake). // static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) { if (!preverify_ok || X509_STORE_CTX_get_error_depth(ctx) != 0) // already failed, or not at peer cert in chain return preverify_ok; X509 *cert = X509_STORE_CTX_get_current_cert(ctx); SSL *ssn = (SSL *) X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); if (!ssn) { pn_transport_logf(NULL, "Error: unexpected error - SSL session info not available for peer verify!"); return 0; // fail connection } pn_transport_t *transport = (pn_transport_t *)SSL_get_ex_data(ssn, ssl_ex_data_index); if (!transport) { pn_transport_logf(NULL, "Error: unexpected error - SSL context info not available for peer verify!"); return 0; // fail connection } pni_ssl_t *ssl = transport->ssl; if (ssl->domain->verify_mode != PN_SSL_VERIFY_PEER_NAME) return preverify_ok; if (!ssl->peer_hostname) { pn_transport_logf(transport, "Error: configuration error: PN_SSL_VERIFY_PEER_NAME configured, but no peer hostname set!"); return 0; // fail connection } ssl_log(transport, "Checking identifying name in peer cert against '%s'", ssl->peer_hostname); bool matched = false; /* first check any SubjectAltName entries, as per RFC2818 */ GENERAL_NAMES *sans = (GENERAL_NAMES *) X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (sans) { int name_ct = sk_GENERAL_NAME_num( sans ); int i; for (i = 0; !matched && i < name_ct; ++i) { GENERAL_NAME *name = sk_GENERAL_NAME_value( sans, i ); if (name->type == GEN_DNS) { ASN1_STRING *asn1 = name->d.dNSName; if (asn1 && asn1->data && asn1->length) { unsigned char *str; int len = ASN1_STRING_to_UTF8( &str, asn1 ); if (len >= 0) { ssl_log(transport, "SubjectAltName (dns) from peer cert = '%.*s'", len, str ); matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len ); OPENSSL_free( str ); } } } } GENERAL_NAMES_free( sans ); } /* if no general names match, try the CommonName from the subject */ X509_NAME *name = X509_get_subject_name(cert); int i = -1; while (!matched && (i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) { X509_NAME_ENTRY *ne = X509_NAME_get_entry(name, i); ASN1_STRING *name_asn1 = X509_NAME_ENTRY_get_data(ne); if (name_asn1) { unsigned char *str; int len = ASN1_STRING_to_UTF8( &str, name_asn1); if (len >= 0) { ssl_log(transport, "commonName from peer cert = '%.*s'", len, str); matched = match_dns_pattern( ssl->peer_hostname, (const char *)str, len ); OPENSSL_free(str); } } } if (!matched) { ssl_log(transport, "Error: no name matching %s found in peer cert - rejecting handshake.", ssl->peer_hostname); preverify_ok = 0; #ifdef X509_V_ERR_APPLICATION_VERIFICATION X509_STORE_CTX_set_error( ctx, X509_V_ERR_APPLICATION_VERIFICATION ); #endif } else { ssl_log(transport, "Name from peer cert matched - peer is valid."); } return preverify_ok; }
int tlsops_comp(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { static char buf[1024]; X509* cert; struct tcp_connection* c; X509_NAME* name; X509_NAME_ENTRY* e; ASN1_STRING* asn1; int nid = NID_commonName, index, my = 0, issuer = 0, ind_local; char* elem; str text; text.s = 0; /* copy callback value as we modify it */ ind_local = param->pvn.u.isname.name.n; DBG("DEBUG:tlsops:tlsops_comp: ind_local = %x", ind_local); 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); } if (ind_local & CERT_SUBJECT) { issuer = 0; ind_local = ind_local ^ CERT_SUBJECT; } else if (ind_local & CERT_ISSUER) { issuer = 1; ind_local = ind_local ^ CERT_ISSUER; } else { LM_CRIT("could not determine subject or issuer\n"); return pv_get_null(msg, param, res); } switch(ind_local) { case COMP_CN: nid = NID_commonName; break; case COMP_O: nid = NID_organizationName; break; case COMP_OU: nid = NID_organizationalUnitName; break; case COMP_C: nid = NID_countryName; break; case COMP_ST: nid = NID_stateOrProvinceName; break; case COMP_L: nid = NID_localityName; break; default: nid = NID_undef; } if (get_cert(&cert, &c, msg, my) < 0) return -1; name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert); if (!name) { LM_ERR("cannot extract subject or issuer name from peer" " certificate\n"); goto err; } if (nid == NID_undef) { /* dump the whole cert info into buf */ X509_NAME_oneline(name, buf, sizeof(buf)); res->rs.s = buf; res->rs.len = strlen(buf); res->flags = PV_VAL_STR; } else { index = X509_NAME_get_index_by_NID(name, nid, -1); if (index == -1) { switch(ind_local) { case COMP_CN: elem = "CommonName"; break; case COMP_O: elem = "OrganizationName"; break; case COMP_OU: elem = "OrganizationalUnitUname"; break; case COMP_C: elem = "CountryName"; break; case COMP_ST: elem = "StateOrProvinceName"; break; case COMP_L: elem = "LocalityName"; break; default: elem = "Unknown"; break; } DBG("DEBUG:tlsops:tlsops_comp: element %s not found in " "certificate subject/issuer\n", elem); goto err; } e = X509_NAME_get_entry(name, index); asn1 = X509_NAME_ENTRY_get_data(e); text.len = ASN1_STRING_to_UTF8((unsigned char**)(void*)&text.s, asn1); if (text.len < 0 || text.len >= 1024) { LM_ERR("failed to convert ASN1 string\n"); goto err; } memcpy(buf, text.s, text.len); res->rs.s = buf; res->rs.len = text.len; res->flags = PV_VAL_STR; OPENSSL_free(text.s); } if (!my) X509_free(cert); tcpconn_put(c); return 0; err: if (text.s) OPENSSL_free(text.s); if (!my) X509_free(cert); tcpconn_put(c); return pv_get_null(msg, param, res); }
/* cf. RFC2818 and RFC2459 */ static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert) { STACK_OF(GENERAL_NAME) *altnames; X509_NAME *subjname; ASN1_STRING *subjasn1; char *subjstr = NULL; int addrlen = 0; int i, altdns = 0; char addrbuf[sizeof(struct in6_addr)]; int ret; /* Allow GEN_IP in the certificate only if we actually connected by IP address rather than by name. */ if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0) addrlen = 4; else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0) addrlen = 16; else if (vpninfo->hostname[0] == '[' && vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') { char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1]; *p = 0; if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0) addrlen = 16; *p = ']'; } altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) { const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i); if (this->type == GEN_DNS) { char *str; int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5); if (len < 0) continue; altdns = 1; /* We don't like names with embedded NUL */ if (strlen(str) != len) continue; if (!match_hostname(vpninfo->hostname, str)) { vpn_progress(vpninfo, PRG_TRACE, _("Matched DNS altname '%s'\n"), str); GENERAL_NAMES_free(altnames); OPENSSL_free(str); return 0; } else { vpn_progress(vpninfo, PRG_TRACE, _("No match for altname '%s'\n"), str); } OPENSSL_free(str); } else if (this->type == GEN_IPADD && addrlen) { char host[80]; int family; if (this->d.ip->length == 4) { family = AF_INET; } else if (this->d.ip->length == 16) { family = AF_INET6; } else { vpn_progress(vpninfo, PRG_ERR, _("Certificate has GEN_IPADD altname with bogus length %d\n"), this->d.ip->length); continue; } /* We only do this for the debug messages */ inet_ntop(family, this->d.ip->data, host, sizeof(host)); if (this->d.ip->length == addrlen && !memcmp(addrbuf, this->d.ip->data, addrlen)) { vpn_progress(vpninfo, PRG_TRACE, _("Matched %s address '%s'\n"), (family == AF_INET6) ? "IPv6" : "IPv4", host); GENERAL_NAMES_free(altnames); return 0; } else { vpn_progress(vpninfo, PRG_TRACE, _("No match for %s address '%s'\n"), (family == AF_INET6) ? "IPv6" : "IPv4", host); } } else if (this->type == GEN_URI) { char *str; char *url_proto, *url_host, *url_path, *url_host2; int url_port; int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5); if (len < 0) continue; /* We don't like names with embedded NUL */ if (strlen(str) != len) continue; if (internal_parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) { OPENSSL_free(str); continue; } if (!url_proto || strcasecmp(url_proto, "https")) goto no_uri_match; if (url_port != vpninfo->port) goto no_uri_match; /* Leave url_host as it was so that it can be freed */ url_host2 = url_host; if (addrlen == 16 && vpninfo->hostname[0] != '[' && url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') { /* Cope with https://[IPv6]/ when the hostname is bare IPv6 */ url_host[strlen(url_host)-1] = 0; url_host2++; } if (strcasecmp(vpninfo->hostname, url_host2)) goto no_uri_match; if (url_path) { vpn_progress(vpninfo, PRG_TRACE, _("URI '%s' has non-empty path; ignoring\n"), str); goto no_uri_match_silent; } vpn_progress(vpninfo, PRG_TRACE, _("Matched URI '%s'\n"), str); free(url_proto); free(url_host); free(url_path); OPENSSL_free(str); GENERAL_NAMES_free(altnames); return 0; no_uri_match: vpn_progress(vpninfo, PRG_TRACE, _("No match for URI '%s'\n"), str); no_uri_match_silent: free(url_proto); free(url_host); free(url_path); OPENSSL_free(str); } } GENERAL_NAMES_free(altnames); /* According to RFC2818, we don't use the legacy subject name if there was an altname with DNS type. */ if (altdns) { vpn_progress(vpninfo, PRG_ERR, _("No altname in peer cert matched '%s'\n"), vpninfo->hostname); return -EINVAL; } subjname = X509_get_subject_name(peer_cert); if (!subjname) { vpn_progress(vpninfo, PRG_ERR, _("No subject name in peer cert!\n")); return -EINVAL; } /* Find the _last_ (most specific) commonName */ i = -1; while (1) { int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i); if (j >= 0) i = j; else break; } subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i)); i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1); if (!subjstr || strlen(subjstr) != i) { vpn_progress(vpninfo, PRG_ERR, _("Failed to parse subject name in peer cert\n")); return -EINVAL; } ret = 0; if (match_hostname(vpninfo->hostname, subjstr)) { vpn_progress(vpninfo, PRG_ERR, _("Peer cert subject mismatch ('%s' != '%s')\n"), subjstr, vpninfo->hostname); ret = -EINVAL; } else { vpn_progress(vpninfo, PRG_TRACE, _("Matched peer certificate subject name '%s'\n"), subjstr); } OPENSSL_free(subjstr); return ret; }
/** check if a provided cert matches a passed hostname */ bool _mongoc_openssl_check_cert (SSL *ssl, const char *host, bool allow_invalid_hostname) { X509 *peer; X509_NAME *subject_name; X509_NAME_ENTRY *entry; ASN1_STRING *entry_data; int length; int idx; int r = 0; long verify_status; size_t addrlen = 0; unsigned char addr4[sizeof (struct in_addr)]; unsigned char addr6[sizeof (struct in6_addr)]; int i; int n_sans = -1; int target = GEN_DNS; STACK_OF (GENERAL_NAME) *sans = NULL; ENTRY; BSON_ASSERT (ssl); BSON_ASSERT (host); if (allow_invalid_hostname) { RETURN (true); } /** if the host looks like an IP address, match that, otherwise we assume we * have a DNS name */ if (inet_pton (AF_INET, host, &addr4)) { target = GEN_IPADD; addrlen = sizeof addr4; } else if (inet_pton (AF_INET6, host, &addr6)) { target = GEN_IPADD; addrlen = sizeof addr6; } peer = SSL_get_peer_certificate (ssl); if (!peer) { MONGOC_WARNING ("SSL Certification verification failed: %s", ERR_error_string (ERR_get_error (), NULL)); RETURN (false); } verify_status = SSL_get_verify_result (ssl); if (verify_status == X509_V_OK) { /* gets a stack of alt names that we can iterate through */ sans = (STACK_OF (GENERAL_NAME) *) X509_get_ext_d2i ( (X509 *) peer, NID_subject_alt_name, NULL, NULL); if (sans) { n_sans = sk_GENERAL_NAME_num (sans); /* loop through the stack, or until we find a match */ for (i = 0; i < n_sans && !r; i++) { const GENERAL_NAME *name = sk_GENERAL_NAME_value (sans, i); /* skip entries that can't apply, I.e. IP entries if we've got a * DNS host */ if (name->type == target) { const char *check; check = (const char *) ASN1_STRING_get0_data (name->d.ia5); length = ASN1_STRING_length (name->d.ia5); switch (target) { case GEN_DNS: /* check that we don't have an embedded null byte */ if ((length == bson_strnlen (check, length)) && _mongoc_openssl_hostcheck (check, host)) { r = 1; } break; case GEN_IPADD: if (length == addrlen) { if (length == sizeof addr6 && !memcmp (check, &addr6, length)) { r = 1; } else if (length == sizeof addr4 && !memcmp (check, &addr4, length)) { r = 1; } } break; default: BSON_ASSERT (0); break; } } } GENERAL_NAMES_free (sans); } else { subject_name = X509_get_subject_name (peer); if (subject_name) { i = -1; /* skip to the last common name */ while ((idx = X509_NAME_get_index_by_NID ( subject_name, NID_commonName, i)) >= 0) { i = idx; } if (i >= 0) { entry = X509_NAME_get_entry (subject_name, i); entry_data = X509_NAME_ENTRY_get_data (entry); if (entry_data) { char *check; /* TODO: I've heard tell that old versions of SSL crap out * when calling ASN1_STRING_to_UTF8 on already utf8 data. * Check up on that */ length = ASN1_STRING_to_UTF8 ((unsigned char **) &check, entry_data); if (length >= 0) { /* check for embedded nulls */ if ((length == bson_strnlen (check, length)) && _mongoc_openssl_hostcheck (check, host)) { r = 1; } OPENSSL_free (check); } } } } } }
std::vector<Wt::WSslCertificate::DnAttribute> getDnAttributes(struct X509_name_st *sn) { std::vector<Wt::WSslCertificate::DnAttribute> retval; if (!sn) return retval; int entries = X509_NAME_entry_count(sn); for (int i = 0; i < entries; ++i) { X509_NAME_ENTRY *entry = X509_NAME_get_entry(sn, i); ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(entry); ASN1_STRING *data = X509_NAME_ENTRY_get_data(entry); int nid = OBJ_obj2nid(obj); std::string value; { char *s; ASN1_STRING_to_UTF8((unsigned char **)(&s), data); value = s; OPENSSL_free(s); } Wt::WSslCertificate::DnAttributeName name; bool knownAttribute = true; switch (nid) { case NID_commonName: name = Wt::WSslCertificate::CommonName; break; case NID_countryName: name = Wt::WSslCertificate::CountryName; break; case NID_localityName: name = Wt::WSslCertificate::LocalityName; break; case NID_stateOrProvinceName: name = Wt::WSslCertificate::StateOrProvinceName; break; case NID_organizationName: name = Wt::WSslCertificate::OrganizationName; break; case NID_organizationalUnitName: name = Wt::WSslCertificate::OrganizationalUnitName; break; case NID_givenName: name = Wt::WSslCertificate::GivenName; break; case NID_surname: name = Wt::WSslCertificate::Surname; break; case NID_initials: name = Wt::WSslCertificate::Initials; break; case NID_serialNumber: name = Wt::WSslCertificate::SerialNumber; break; case NID_title: name = Wt::WSslCertificate::Title; break; default: // extra unknown attributes; ignore them knownAttribute = false; break; } if (knownAttribute) { Wt::WSslCertificate::DnAttribute dna(name, value); retval.push_back(dna); } } return retval; }
/*! \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; unsigned char *str2; X509_NAME *name = X509_get_subject_name(peer); 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)); ret = ASN1_STRING_to_UTF8(&str2, str); if (ret < 0) { continue; } if (str2) { if (strlen((char *) str2) != ret) { ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n"); } else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) { found = 1; } ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); OPENSSL_free(str2); } if (found) { break; } } 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 do_x509_check(X509 *x, const unsigned char *chk, size_t chklen, unsigned int flags, int check_type) { STACK_OF(GENERAL_NAME) *gens = NULL; X509_NAME *name = NULL; int i; int cnid; int alt_type; equal_fn equal; if (check_type == GEN_EMAIL) { cnid = NID_pkcs9_emailAddress; alt_type = V_ASN1_IA5STRING; equal = equal_email; } else if (check_type == GEN_DNS) { cnid = NID_commonName; alt_type = V_ASN1_IA5STRING; if (flags & X509_CHECK_FLAG_NO_WILDCARDS) equal = equal_nocase; else equal = equal_wildcard; } else { cnid = 0; alt_type = V_ASN1_OCTET_STRING; equal = equal_case; } if (chklen == 0) chklen = strlen((const char *)chk); gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); if (gens) { int rv = 0; for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) { GENERAL_NAME *gen; ASN1_STRING *cstr; gen = sk_GENERAL_NAME_value(gens, i); if(gen->type != check_type) continue; if (check_type == GEN_EMAIL) cstr = gen->d.rfc822Name; else if (check_type == GEN_DNS) cstr = gen->d.dNSName; else cstr = gen->d.iPAddress; if (do_check_string(cstr, alt_type, equal, chk, chklen)) { rv = 1; break; } } GENERAL_NAMES_free(gens); if (rv) return 1; if (!(flags & X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT) || !cnid) return 0; } i = -1; name = X509_get_subject_name(x); while((i = X509_NAME_get_index_by_NID(name, cnid, i)) >= 0) { X509_NAME_ENTRY *ne; ASN1_STRING *str; ne = X509_NAME_get_entry(name, i); str = X509_NAME_ENTRY_get_data(ne); if (do_check_string(str, -1, equal, chk, chklen)) return 1; } return 0; }
/* * This function is based on verifyhost in libcurl - I hope that it is correct. */ static int verify_ssl_host_name(X509 *server_cert, unsigned char *host) { unsigned char ipv4_address[4]; #ifdef SUPPORT_IPV6 unsigned char ipv6_address[16]; #endif unsigned char *address = NULL; int address_len = 0; int type = GEN_DNS; STACK_OF(GENERAL_NAME) *altnames; if (!numeric_ip_address(host, ipv4_address)) { address = ipv4_address; address_len = 4; type = GEN_IPADD; } #ifdef SUPPORT_IPV6 if (!numeric_ipv6_address(host, ipv6_address, NULL)) { address = ipv6_address; address_len = 16; type = GEN_IPADD; } #endif #if 1 altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); if (altnames) { int retval = 1; int i; int n_altnames = sk_GENERAL_NAME_num(altnames); for (i = 0; i < n_altnames; i++) { const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i); const unsigned char *altname_ptr; int altname_len; if (altname->type != type) { if (altname->type == GEN_IPADD || altname->type == GEN_DNS || altname->type == GEN_URI) retval = S_INVALID_CERTIFICATE; continue; } altname_ptr = ASN1_STRING_data(altname->d.ia5); altname_len = ASN1_STRING_length(altname->d.ia5); if (type == GEN_IPADD) { if (altname_len == address_len && !memcmp(altname_ptr, address, address_len)) { retval = 0; break; } } else { if (altname_len == (int)strlen(cast_const_char altname_ptr) && !check_host_name(altname_ptr, host)) { retval = 0; break; } } retval = S_INVALID_CERTIFICATE; } sk_GENERAL_NAME_free(altnames); if (retval != 1) return retval; } #endif { unsigned char *nulstr = cast_uchar ""; unsigned char *peer_CN = nulstr; X509_NAME *name; int j, i = -1; retval = 1; name = X509_get_subject_name(server_cert); if (name) while ((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) i = j; if (i >= 0) { ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); if (tmp) { if (ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { j = ASN1_STRING_length(tmp); if (j >= 0) { peer_CN = OPENSSL_malloc(j + 1); if (peer_CN) { memcpy(peer_CN, ASN1_STRING_data(tmp), j); peer_CN[j] = '\0'; } } } else { j = ASN1_STRING_to_UTF8(&peer_CN, tmp); } if (peer_CN && (int)strlen(cast_const_char peer_CN) != j) { retval = S_INVALID_CERTIFICATE; } } } if (peer_CN && peer_CN != nulstr) { if (retval == 1 && !check_host_name(peer_CN, host)) retval = 0; OPENSSL_free(peer_CN); } if (retval != 1) return retval; } return S_INVALID_CERTIFICATE; }
static void CheckNameEntryValid(X509_NAME_ENTRY *ne) { ASN1_STRING *data = X509_NAME_ENTRY_get_data(ne); ASN1_OBJECT *obj = X509_NAME_ENTRY_get_object(ne); int nid = OBJ_obj2nid(obj); size_t char_len; if (CheckStringValid(data, &char_len)) { bool bChecked = false; for (size_t i = 0; i < sizeof(size_limits)/sizeof(size_limits[0]); i++) { if (OBJ_cmp(*size_limits[i].obj, obj) == 0) { if (char_len > size_limits[i].max || char_len < size_limits[i].min) { SetError(size_limits[i].error); } bChecked = true; break; } } if (!bChecked) { SetInfo(INF_NAME_ENTRY_LENGTH_NOT_CHECKED); } } if (nid == NID_pkcs9_emailAddress || nid == NID_domainComponent) { if (data->type != V_ASN1_IA5STRING) { SetError(ERR_INVALID_NAME_ENTRY_TYPE); } } else if (nid == NID_pkcs9_unstructuredName && data->type == V_ASN1_IA5STRING) { /* PKCS#9 unstructuredName may be IA5String or DirectoryString */ } else { /* It should be a DirectoryString, which is one of the below */ if ((data->type != V_ASN1_PRINTABLESTRING) && (data->type != V_ASN1_UTF8STRING) && (data->type != V_ASN1_T61STRING) && (data->type != V_ASN1_UNIVERSALSTRING) && (data->type != V_ASN1_BMPSTRING)) { SetError(ERR_INVALID_NAME_ENTRY_TYPE); } else if ((data->type != V_ASN1_PRINTABLESTRING) && (data->type != V_ASN1_UTF8STRING)) { /* RFC5280 says it MUST be PrintableString or UTF8String, with exceptions. */ SetWarning(WARN_NON_PRINTABLE_STRING); } } if (nid == NID_countryName && data->type != V_ASN1_PRINTABLESTRING) { SetError(ERR_INVALID_NAME_ENTRY_TYPE); } if (nid == NID_dnQualifier && data->type != V_ASN1_PRINTABLESTRING) { SetError(ERR_INVALID_NAME_ENTRY_TYPE); } if (nid == NID_serialNumber && data->type != V_ASN1_PRINTABLESTRING) { SetError(ERR_INVALID_NAME_ENTRY_TYPE); } return; }
/* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead. Matching is performed using the matching rules specified by [RFC2459]. If more than one identity of a given type is present in the certificate (e.g., more than one dNSName name, a match in any one of the set is considered acceptable.) Names may contain the wildcard character * which is considered to match any single domain name component or component fragment. E.g., *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com. In some cases, the URI is specified as an IP address rather than a hostname. In this case, the iPAddress subjectAltName must be present in the certificate and must exactly match the IP in the URI. */ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) { bool matched = FALSE; /* no alternative match yet */ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ int addrlen = 0; struct SessionHandle *data = conn->data; STACK_OF(GENERAL_NAME) *altnames; #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, conn->host.name, &addr)) { target = GEN_IPADD; addrlen = sizeof(struct in6_addr); } else #endif if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) { target = GEN_IPADD; addrlen = sizeof(struct in_addr); } /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL); if(altnames) { 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(altnames); /* loop through all alternatives while none has matched */ for (i=0; (i<numalts) && !matched; i++) { /* get a handle to alternative name number i */ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); /* only check alternatives of the same type the target is */ if(check->type == target) { /* get data and length */ const char *altptr = (char *)ASN1_STRING_data(check->d.ia5); int altlen; switch(target) { case GEN_DNS: /* name/pattern comparison */ /* The OpenSSL man page explicitly says: "In general it cannot be assumed that the data returned by ASN1_STRING_data() is null terminated or does not contain embedded nulls." But also that "The actual format of the data will depend on the actual string type itself: for example for and IA5String the data will be ASCII" Gisle researched the OpenSSL sources: "I checked the 0.9.6 and 0.9.8 sources before my patch and it always 0-terminates an IA5String." */ if (cert_hostcheck(altptr, conn->host.name)) matched = TRUE; break; case GEN_IPADD: /* IP address comparison */ /* compare alternative IP address if the data chunk is the same size our server IP address is */ altlen = ASN1_STRING_length(check->d.ia5); if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) matched = TRUE; break; } } } GENERAL_NAMES_free(altnames); } if(matched) /* an alternative name matched the server hostname */ infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname); else { /* we have to look to the last occurence of a commonName in the distinguished one to get the most significant one. */ int j,i=-1 ; /* The following is done because of a bug in 0.9.6b */ unsigned char *nulstr = (unsigned char *)""; unsigned char *peer_CN = nulstr; X509_NAME *name = X509_get_subject_name(server_cert) ; if (name) while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0) i=j; /* we have the name entry and we will now convert this to a string that we can use for comparison. Doing this we support BMPstring, UTF8 etc. */ if (i>=0) { ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i)); /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input is already UTF-8 encoded. We check for this case and copy the raw string manually to avoid the problem. This code can be made conditional in the future when OpenSSL has been fixed. Work-around brought by Alexis S. L. Carvalho. */ if (tmp && ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { j = ASN1_STRING_length(tmp); if (j >= 0) { peer_CN = OPENSSL_malloc(j+1); if (peer_CN) { memcpy(peer_CN, ASN1_STRING_data(tmp), j); peer_CN[j] = '\0'; } } } else /* not a UTF8 name */ j = ASN1_STRING_to_UTF8(&peer_CN, tmp); } if (peer_CN == nulstr) peer_CN = NULL; if (!peer_CN) { if(data->set.ssl.verifyhost > 1) { failf(data, "SSL: unable to obtain common name from peer certificate"); return CURLE_SSL_PEER_CERTIFICATE; } else { /* Consider verifyhost == 1 as an "OK" for a missing CN field, but we output a note about the situation */ infof(data, "\t common name: WARNING couldn't obtain\n"); } } else if(!cert_hostcheck((const char *)peer_CN, conn->host.name)) { if(data->set.ssl.verifyhost > 1) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, conn->host.dispname); OPENSSL_free(peer_CN); return CURLE_SSL_PEER_CERTIFICATE ; } else infof(data, "\t common name: %s (does not match '%s')\n", peer_CN, conn->host.dispname); } else { infof(data, "\t common name: %s (matched)\n", peer_CN); OPENSSL_free(peer_CN); } } return CURLE_OK; }
static int verify_server_cert(SSL *ssl, const char *host) { X509 *cert; X509_NAME *peer_name; ASN1_STRING *str; unsigned char *peer_cn = NULL; int matched = -1, type = GEN_DNS; GENERAL_NAMES *alts; struct in6_addr addr6; struct in_addr addr4; void *addr; int i = -1,j; if (SSL_get_verify_result(ssl) != X509_V_OK) { giterr_set(GITERR_SSL, "The SSL certificate is invalid"); return GIT_ECERTIFICATE; } /* Try to parse the host as an IP address to see if it is */ if (p_inet_pton(AF_INET, host, &addr4)) { type = GEN_IPADD; addr = &addr4; } else { if(p_inet_pton(AF_INET6, host, &addr6)) { type = GEN_IPADD; addr = &addr6; } } cert = SSL_get_peer_certificate(ssl); if (!cert) { giterr_set(GITERR_SSL, "the server did not provide a certificate"); return -1; } /* Check the alternative names */ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (alts) { int num; num = sk_GENERAL_NAME_num(alts); for (i = 0; i < num && matched != 1; i++) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i); const char *name = (char *) ASN1_STRING_data(gn->d.ia5); size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5); /* Skip any names of a type we're not looking for */ if (gn->type != type) continue; if (type == GEN_DNS) { /* If it contains embedded NULs, don't even try */ if (memchr(name, '\0', namelen)) continue; if (check_host_name(name, host) < 0) matched = 0; else matched = 1; } else if (type == GEN_IPADD) { /* Here name isn't so much a name but a binary representation of the IP */ matched = !!memcmp(name, addr, namelen); } } } GENERAL_NAMES_free(alts); if (matched == 0) goto cert_fail_name; if (matched == 1) return 0; /* If no alternative names are available, check the common name */ peer_name = X509_get_subject_name(cert); if (peer_name == NULL) goto on_error; if (peer_name) { /* Get the index of the last CN entry */ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0) i = j; } if (i < 0) goto on_error; str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i)); if (str == NULL) goto on_error; /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) { int size = ASN1_STRING_length(str); if (size > 0) { peer_cn = OPENSSL_malloc(size + 1); GITERR_CHECK_ALLOC(peer_cn); memcpy(peer_cn, ASN1_STRING_data(str), size); peer_cn[size] = '\0'; } else { goto cert_fail_name; } } else { int size = ASN1_STRING_to_UTF8(&peer_cn, str); GITERR_CHECK_ALLOC(peer_cn); if (memchr(peer_cn, '\0', size)) goto cert_fail_name; } if (check_host_name((char *)peer_cn, host) < 0) goto cert_fail_name; OPENSSL_free(peer_cn); return 0; on_error: OPENSSL_free(peer_cn); return ssl_set_error(ssl, 0); cert_fail_name: OPENSSL_free(peer_cn); giterr_set(GITERR_SSL, "hostname does not match certificate"); return GIT_ECERTIFICATE; }
/*! \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 /* * open a FILE * as appropriate. */ if (!tcptls_session->parent->tls_cfg) { tcptls_session->f = fdopen(tcptls_session->fd, "w+"); setvbuf(tcptls_session->f, NULL, _IONBF, 0); } #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_verb(2, "Problem setting up ssl connection: %s\n", ERR_error_string(ERR_get_error(), err)); } else { #if defined(HAVE_FUNOPEN) /* the BSD interface */ tcptls_session->f = funopen(tcptls_session->ssl, ssl_read, ssl_write, NULL, ssl_close); #elif defined(HAVE_FOPENCOOKIE) /* the glibc/linux interface */ static const cookie_io_functions_t cookie_funcs = { ssl_read, ssl_write, NULL, ssl_close }; tcptls_session->f = fopencookie(tcptls_session->ssl, "w+", cookie_funcs); #else /* could add other methods here */ ast_debug(2, "no tcptls_session->f methods attempted!"); #endif 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_WARNING, "No peer SSL certificate\n"); 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)); if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { ASN1_STRING *str; unsigned char *str2; X509_NAME *name = X509_get_subject_name(peer); 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)); ASN1_STRING_to_UTF8(&str2, str); if (str2) { if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2)) found = 1; ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2); OPENSSL_free(str2); } if (found) break; } if (!found) { ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); if (peer) X509_free(peer); close(tcptls_session->fd); fclose(tcptls_session->f); ao2_ref(tcptls_session, -1); return NULL; } } if (peer) X509_free(peer); } } if (!tcptls_session->f) /* no success opening descriptor stacking */ SSL_free(tcptls_session->ssl); } #endif /* DO_SSL */ if (!tcptls_session->f) { close(tcptls_session->fd); ast_log(LOG_WARNING, "FILE * open failed!\n"); ao2_ref(tcptls_session, -1); return NULL; } if (tcptls_session && tcptls_session->parent->worker_fn) return tcptls_session->parent->worker_fn(tcptls_session); else return tcptls_session; }
int ma_tls_verify_server_cert(MARIADB_TLS *ctls) { X509 *cert; MYSQL *mysql; X509_NAME *x509sn; int cn_pos; X509_NAME_ENTRY *cn_entry; ASN1_STRING *cn_asn1; const char *cn_str; SSL *ssl; MARIADB_PVIO *pvio; if (!ctls || !ctls->ssl) return 1; ssl= (SSL *)ctls->ssl; mysql= (MYSQL *)SSL_get_app_data(ssl); pvio= mysql->net.pvio; if (!mysql->host) { pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "Invalid (empty) hostname"); return 1; } if (!(cert= SSL_get_peer_certificate(ssl))) { pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "Unable to get server certificate"); return 1; } x509sn= X509_get_subject_name(cert); if ((cn_pos= X509_NAME_get_index_by_NID(x509sn, NID_commonName, -1)) < 0) goto error; if (!(cn_entry= X509_NAME_get_entry(x509sn, cn_pos))) goto error; if (!(cn_asn1 = X509_NAME_ENTRY_get_data(cn_entry))) goto error; cn_str = (char *)ASN1_STRING_data(cn_asn1); /* Make sure there is no embedded \0 in the CN */ if ((size_t)ASN1_STRING_length(cn_asn1) != strlen(cn_str)) goto error; if (strcmp(cn_str, mysql->host)) goto error; X509_free(cert); return 0; error: X509_free(cert); pvio->set_error(mysql, CR_SSL_CONNECTION_ERROR, SQLSTATE_UNKNOWN, ER(CR_SSL_CONNECTION_ERROR), "Validation of SSL server certificate failed"); return 1; }
/* * Setup to handle new incoming connections */ static void ssl_accept( const struct security_driver *driver, char * (*conf_fn)(char *, void *), int in, int out, void (*fn)(security_handle_t *, pkt_t *), void *datap) { sockaddr_union sin; socklen_t_equiv len = sizeof(struct sockaddr); struct tcp_conn *rc; char hostname[NI_MAXHOST]; int result; char *errmsg = NULL; int err; X509 *remote_cert; char *str; X509_NAME *x509_name; char *cert_hostname; SSL_CTX *ctx; SSL *ssl; int loc; char *ssl_dir = getconf_str(CNF_SSL_DIR); char *ssl_fingerprint_file = conf_fn("ssl_fingerprint_file", datap); char *ssl_cert_file = conf_fn("ssl_cert_file", datap); char *ssl_key_file = conf_fn("ssl_key_file", datap); char *ssl_ca_cert_file = conf_fn("ssl_ca_cert_file", datap); char *ssl_cipher_list = conf_fn("ssl_cipher_list", datap); int ssl_check_host = atoi(conf_fn("ssl_check_host", datap)); int ssl_check_certificate_host = atoi(conf_fn("ssl_check_certificate_host", datap)); if (getpeername(in, (struct sockaddr *)&sin, &len) < 0) { g_debug(_("getpeername returned: %s"), strerror(errno)); return; } if ((result = getnameinfo((struct sockaddr *)&sin, len, hostname, NI_MAXHOST, NULL, 0, 0) != 0)) { g_debug(_("getnameinfo failed: %s"), gai_strerror(result)); return; } if (ssl_check_host && check_name_give_sockaddr(hostname, (struct sockaddr *)&sin, &errmsg) < 0) { amfree(errmsg); return; } if (ssl_dir) { if (!ssl_cert_file || ssl_cert_file == '\0') { ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir); } if (!ssl_key_file || ssl_key_file == '\0') { ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir); } if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') { ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir); } } if (!ssl_cert_file) { g_debug(_("ssl-cert-file must be set in amanda-remote.conf")); return; } if (!ssl_key_file) { g_debug(_("ssl-key-file must be set in amanda-remote.conf")); return; } if (!ssl_ca_cert_file) { g_debug(_("ssl_ca_cert_file must be set in amanda-remote.conf")); return; } len = sizeof(sin); init_ssl(); /* Create a SSL_CTX structure */ ctx = SSL_CTX_new(SSLv3_server_method()); if (!ctx) { g_debug(_("SSL_CTX_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); if (ssl_cipher_list) { g_debug("Set ssl_cipher_list to %s", ssl_cipher_list); if (SSL_CTX_set_cipher_list(ctx, ssl_cipher_list) == 0) { g_debug(_("SSL_CTX_set_cipher_list failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } } /* Load the me certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-cert-file certificate %s"), ssl_cert_file); if (SSL_CTX_use_certificate_file(ctx, ssl_cert_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Load the private-key corresponding to the me certificate */ g_debug(_("Loading ssl-key-file private-key %s"), ssl_key_file); if (SSL_CTX_use_PrivateKey_file(ctx, ssl_key_file, SSL_FILETYPE_PEM) <= 0) { g_debug(_("Load ssl-key-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } if (ssl_ca_cert_file) { /* Load the RSA CA certificate into the SSL_CTX structure */ g_debug(_("Loading ssl-ca-cert-file ca certificate %s"), ssl_ca_cert_file); if (!SSL_CTX_load_verify_locations(ctx, ssl_ca_cert_file, NULL)) { g_debug(_("Load ssl-ca-cert-file failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Set to require peer (remote) certificate verification */ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); /* Set the verification depth to 1 */ SSL_CTX_set_verify_depth(ctx,1); } ssl = SSL_new(ctx); if (!ssl) { g_debug(_("SSL_new failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } SSL_set_accept_state(ssl); /* Assign the socket into the SSL structure (SSL and socket without BIO) */ SSL_set_fd(ssl, in); /* Perform SSL Handshake on the SSL me */ err = SSL_accept(ssl); if (err == -1) { g_debug(_("SSL_accept failed: %s"), ERR_error_string(ERR_get_error(), NULL)); return; } /* Get the me's certificate (optional) */ remote_cert = SSL_get_peer_certificate (ssl); if (remote_cert == NULL) { g_debug(_("remote doesn't sent a certificate")); return; } x509_name = X509_get_subject_name(remote_cert); str = X509_NAME_oneline(X509_get_subject_name(remote_cert), 0, 0); auth_debug(1, _("\t subject: %s\n"), str); amfree (str); str = X509_NAME_oneline(X509_get_issuer_name(remote_cert), 0, 0); auth_debug(1, _("\t issuer: %s\n"), str); amfree(str); loc = -1; loc = X509_NAME_get_index_by_NID(x509_name, NID_commonName, loc); if (loc != -1) { X509_NAME_ENTRY *x509_entry = X509_NAME_get_entry(x509_name, loc); ASN1_STRING *asn1_string = X509_NAME_ENTRY_get_data(x509_entry); cert_hostname = (char *)ASN1_STRING_data(asn1_string); auth_debug(1, "common_name: %s\n", cert_hostname); if (ssl_check_certificate_host && check_name_give_sockaddr((char*)cert_hostname, (struct sockaddr *)&sin, &errmsg) < 0) { g_debug("Common name of certicate (%s) doesn't resolv to IP (%s)", cert_hostname, str_sockaddr(&sin)); amfree(errmsg); X509_free(remote_cert); return; } } else { g_debug("Certificate have no common name"); X509_free(remote_cert); return; } if (ssl_dir) { if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') { struct stat statbuf; ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, cert_hostname); if (stat(ssl_fingerprint_file, &statbuf) == -1) { g_free(ssl_fingerprint_file); ssl_fingerprint_file = NULL; } } } if (ssl_fingerprint_file) { g_debug(_("Loading ssl-fingerprint-file %s"), ssl_fingerprint_file); str = validate_fingerprints(remote_cert, ssl_fingerprint_file); if (str) { g_debug("%s", str); amfree(str); X509_free(remote_cert); return; } } X509_free(remote_cert); rc = sec_tcp_conn_get(hostname, 0); rc->recv_security_ok = &bsd_recv_security_ok; rc->prefix_packet = &bsd_prefix_packet; rc->need_priv_port = 0; copy_sockaddr(&rc->peer, &sin); rc->read = in; rc->write = out; rc->accept_fn = fn; rc->driver = driver; rc->conf_fn = conf_fn; rc->datap = datap; rc->ctx = ctx; rc->ssl = ssl; strncpy(rc->hostname, cert_hostname, sizeof(rc->hostname)-1); g_debug(_("SSL_cipher: %s"), SSL_get_cipher(rc->ssl)); sec_tcp_conn_read(rc); }
static int do_name_ex(char_io *io_ch, void *arg, X509_NAME *n, int indent, unsigned long flags) { int i, prev = -1, orflags, cnt; int fn_opt, fn_nid; ASN1_OBJECT *fn; ASN1_STRING *val; X509_NAME_ENTRY *ent; char objtmp[80]; const char *objbuf; int outlen, len; char *sep_dn, *sep_mv, *sep_eq; int sep_dn_len, sep_mv_len, sep_eq_len; if (indent < 0) indent = 0; outlen = indent; if (!do_indent(io_ch, arg, indent)) return -1; switch (flags & XN_FLAG_SEP_MASK) { case XN_FLAG_SEP_MULTILINE: sep_dn = "\n"; sep_dn_len = 1; sep_mv = " + "; sep_mv_len = 3; break; case XN_FLAG_SEP_COMMA_PLUS: sep_dn = ","; sep_dn_len = 1; sep_mv = "+"; sep_mv_len = 1; indent = 0; break; case XN_FLAG_SEP_CPLUS_SPC: sep_dn = ", "; sep_dn_len = 2; sep_mv = " + "; sep_mv_len = 3; indent = 0; break; case XN_FLAG_SEP_SPLUS_SPC: sep_dn = "; "; sep_dn_len = 2; sep_mv = " + "; sep_mv_len = 3; indent = 0; break; default: return -1; } if (flags & XN_FLAG_SPC_EQ) { sep_eq = " = "; sep_eq_len = 3; } else { sep_eq = "="; sep_eq_len = 1; } fn_opt = flags & XN_FLAG_FN_MASK; cnt = X509_NAME_entry_count(n); for (i = 0; i < cnt; i++) { if (flags & XN_FLAG_DN_REV) ent = X509_NAME_get_entry(n, cnt - i - 1); else ent = X509_NAME_get_entry(n, i); if (prev != -1) { if (prev == ent->set) { if (!io_ch(arg, sep_mv, sep_mv_len)) return -1; outlen += sep_mv_len; } else { if (!io_ch(arg, sep_dn, sep_dn_len)) return -1; outlen += sep_dn_len; if (!do_indent(io_ch, arg, indent)) return -1; outlen += indent; } } prev = ent->set; fn = X509_NAME_ENTRY_get_object(ent); val = X509_NAME_ENTRY_get_data(ent); fn_nid = OBJ_obj2nid(fn); if (fn_opt != XN_FLAG_FN_NONE) { int objlen, fld_len; if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) { OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1); fld_len = 0; /* XXX: what should this be? */ objbuf = objtmp; } else { if (fn_opt == XN_FLAG_FN_SN) { fld_len = FN_WIDTH_SN; objbuf = OBJ_nid2sn(fn_nid); } else if (fn_opt == XN_FLAG_FN_LN) { fld_len = FN_WIDTH_LN; objbuf = OBJ_nid2ln(fn_nid); } else { fld_len = 0; /* XXX: what should this be? */ objbuf = ""; } } objlen = strlen(objbuf); if (!io_ch(arg, objbuf, objlen)) return -1; if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) { if (!do_indent(io_ch, arg, fld_len - objlen)) return -1; outlen += fld_len - objlen; } if (!io_ch(arg, sep_eq, sep_eq_len)) return -1; outlen += objlen + sep_eq_len; } /* * If the field name is unknown then fix up the DER dump flag. We * might want to limit this further so it will DER dump on anything * other than a few 'standard' fields. */ if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS)) orflags = ASN1_STRFLGS_DUMP_ALL; else orflags = 0; len = do_print_ex(io_ch, arg, flags | orflags, val); if (len < 0) return -1; outlen += len; } return outlen; }
void add_assoc_name_entry(lua_State*L, char * key, X509_NAME * name, int shortname) /* {{{ */ { int i, j = -1, last = -1, obj_cnt = 0; char *sname; int nid; X509_NAME_ENTRY * ne; ASN1_STRING * str = NULL; ASN1_OBJECT * obj; char* p; lua_newtable(L); p=X509_NAME_oneline(name,NULL,0); lua_pushstring(L, p); lua_rawseti(L, -2, 0); OPENSSL_free(p); for (i = 0; i < X509_NAME_entry_count(name); i++) { unsigned char *to_add; int to_add_len; int tindex = 0; //int utf8 = 0; ne = X509_NAME_get_entry(name, i); obj = X509_NAME_ENTRY_get_object(ne); nid = OBJ_obj2nid(obj); obj_cnt = 0; if (shortname) { sname = (char *) OBJ_nid2sn(nid); } else { sname = (char *) OBJ_nid2ln(nid); } lua_newtable(L); last = -1; for (;;) { j = X509_NAME_get_index_by_OBJ(name, obj, last); if (j < 0) { if (last != -1) break; } else { obj_cnt++; ne = X509_NAME_get_entry(name, j); str = X509_NAME_ENTRY_get_data(ne); /* Some Certificate not stardand if (ASN1_STRING_type(str) != V_ASN1_UTF8STRING) { to_add_len = ASN1_STRING_to_UTF8(&to_add, str); } */ to_add = ASN1_STRING_data(str); to_add_len = ASN1_STRING_length(str); tindex++; lua_pushlstring(L,(char *)to_add, to_add_len); lua_rawseti(L,-2,tindex); } last = j; } i = last; if (obj_cnt > 1) { lua_setfield(L,-2,sname); } else { lua_pop(L,1); if (obj_cnt && str && to_add_len > -1) { lua_pushlstring(L,(char *)to_add, to_add_len); lua_setfield(L,-2, sname); } } } if (key != NULL) { lua_setfield(L,-2,key); } }