/* Function: CheckX509Hostname Used by System.Net.Security's Unix CertModule to identify if the certificate presented by the server is applicable to the hostname requested. Return values: 1 if the hostname is a match 0 if the hostname is not a match Any negative number indicates an error in the arguments. */ extern int32_t CryptoNative_CheckX509Hostname(X509* x509, const char* hostname, int32_t cchHostname) { if (!x509) return -2; if (cchHostname > 0 && !hostname) return -3; if (cchHostname < 0) return -4; int subjectNid = NID_commonName; int sanGenType = GEN_DNS; GENERAL_NAMES* san = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); char readSubject = 1; int success = 0; // RFC2818 says that if ANY dNSName alternative name field is present then // we should ignore the subject common name. if (san) { int i; int count = sk_GENERAL_NAME_num(san); for (i = 0; i < count; ++i) { GENERAL_NAME* sanEntry = sk_GENERAL_NAME_value(san, i); if (sanEntry->type != sanGenType) { continue; } readSubject = 0; if (CheckX509HostnameMatch(sanEntry->d.dNSName, hostname, cchHostname, 1)) { success = 1; break; } } GENERAL_NAMES_free(san); } if (readSubject && !success) { // This is a shared/interor pointer, do not free! X509_NAME* subject = X509_get_subject_name(x509); if (subject) { int i = -1; while ((i = X509_NAME_get_index_by_NID(subject, subjectNid, i)) >= 0) { // Shared/interior pointers, do not free! X509_NAME_ENTRY* nameEnt = X509_NAME_get_entry(subject, i); ASN1_STRING* cn = X509_NAME_ENTRY_get_data(nameEnt); if (CheckX509HostnameMatch(cn, hostname, cchHostname, 0)) { success = 1; break; } } } } return success; }
/* Function: CheckX509IpAddress Used by System.Net.Security's Unix CertModule to identify if the certificate presented by the server is applicable to the hostname (an IP address) requested. Return values: 1 if the hostname is a match 0 if the hostname is not a match Any negative number indicates an error in the arguments. */ extern int32_t CryptoNative_CheckX509IpAddress( X509* x509, const uint8_t* addressBytes, int32_t addressBytesLen, const char* hostname, int32_t cchHostname) { if (!x509) return -2; if (cchHostname > 0 && !hostname) return -3; if (cchHostname < 0) return -4; if (addressBytesLen < 0) return -5; if (!addressBytes) return -6; int subjectNid = NID_commonName; int sanGenType = GEN_IPADD; GENERAL_NAMES* san = X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL); int success = 0; if (san) { int i; int count = sk_GENERAL_NAME_num(san); for (i = 0; i < count; ++i) { GENERAL_NAME* sanEntry = sk_GENERAL_NAME_value(san, i); ASN1_OCTET_STRING* ipAddr; if (sanEntry->type != sanGenType) { continue; } ipAddr = sanEntry->d.iPAddress; if (!ipAddr || !ipAddr->data || ipAddr->length != addressBytesLen) { continue; } if (!memcmp(addressBytes, ipAddr->data, (size_t)addressBytesLen)) { success = 1; break; } } GENERAL_NAMES_free(san); } if (!success) { // This is a shared/interor pointer, do not free! X509_NAME* subject = X509_get_subject_name(x509); if (subject) { int i = -1; while ((i = X509_NAME_get_index_by_NID(subject, subjectNid, i)) >= 0) { // Shared/interior pointers, do not free! X509_NAME_ENTRY* nameEnt = X509_NAME_get_entry(subject, i); ASN1_STRING* cn = X509_NAME_ENTRY_get_data(nameEnt); if (CheckX509HostnameMatch(cn, hostname, cchHostname, 0)) { success = 1; break; } } } } return success; }
int32_t local_X509_check_host(X509* x509, const char* name, size_t namelen, unsigned int flags, char** peername) { assert(peername == NULL); assert(flags == X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); (void)flags; (void)peername; GENERAL_NAMES* san = (GENERAL_NAMES*)(X509_get_ext_d2i(x509, NID_subject_alt_name, NULL, NULL)); int readSubject = 1; int success = 0; // RFC2818 says that if ANY dNSName alternative name field is present then // we should ignore the subject common name. if (san != NULL) { int count = sk_GENERAL_NAME_num(san); for (int i = 0; i < count; ++i) { GENERAL_NAME* sanEntry = sk_GENERAL_NAME_value(san, i); if (sanEntry->type != GEN_DNS) { continue; } readSubject = 0; // A GEN_DNS name is supposed to be a V_ASN1_IA5STRING. // If it isn't, we don't know how to read it. if (CheckX509HostnameMatch(sanEntry->d.dNSName, name, (int)namelen, V_ASN1_IA5STRING)) { success = 1; break; } } GENERAL_NAMES_free(san); } if (readSubject) { assert(success == 0); // This is a shared/interor pointer, do not free! X509_NAME* subject = X509_get_subject_name(x509); if (subject != NULL) { int i = -1; while ((i = X509_NAME_get_index_by_NID(subject, NID_commonName, i)) >= 0) { // Shared/interior pointers, do not free! X509_NAME_ENTRY* nameEnt = X509_NAME_get_entry(subject, i); ASN1_STRING* cn = X509_NAME_ENTRY_get_data(nameEnt); // For compatibility with previous .NET Core builds, allow any type of // string for CN, provided it ended up with a single-byte encoding (otherwise // strncasecmp simply won't match). if (CheckX509HostnameMatch(cn, name, (int)namelen, cn->type)) { success = 1; break; } } } } return success; }