static int amqp_ssl_socket_verify_hostname(void *base, const char *host) { struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base; unsigned char *utf8_value = NULL, *cp, ch; int pos, utf8_length, status = 0; ASN1_STRING *entry_string; X509_NAME_ENTRY *entry; X509_NAME *name; X509 *peer; peer = SSL_get_peer_certificate(self->ssl); if (!peer) { goto error; } name = X509_get_subject_name(peer); if (!name) { goto error; } pos = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if (0 > pos) { goto error; } entry = X509_NAME_get_entry(name, pos); if (!entry) { goto error; } entry_string = X509_NAME_ENTRY_get_data(entry); if (!entry_string) { goto error; } utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_string); if (0 > utf8_length) { goto error; } while (utf8_length > 0 && utf8_value[utf8_length - 1] == 0) { --utf8_length; } if (utf8_length >= 256) { goto error; } if ((size_t)utf8_length != strlen((char *)utf8_value)) { goto error; } for (cp = utf8_value; (ch = *cp) != '\0'; ++cp) { if (isascii(ch) && !isprint(ch)) { goto error; } } if (!amqp_hostcheck((char *)utf8_value, host)) { goto error; } exit: OPENSSL_free(utf8_value); return status; error: status = -1; goto exit; }
static void hostcheck_fail(const char *match_pattern, const char *url) { int ok; ok = amqp_hostcheck(match_pattern, url); if (ok) { fprintf(stderr, "Expected hostname check to fail, but didn't: %s (%s)\n", url, match_pattern); abort(); } fprintf(stdout, "ok: [fail] %s, %s\n", url, match_pattern); }
/** * Tries to find a match for hostname in the certificate's Subject Alternative * Name extension. * * Returns AMQP_HVR_MATCH_FOUND if a match was found. * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. * Returns AMQP_HVR_MALFORMED_CERTIFICATE if any of the hostnames had a NUL * character embedded in it. * Returns AMQP_HVR_NO_SAN_PRESENT if the SAN extension was not present in the * certificate. */ static amqp_hostname_validation_result amqp_matches_subject_alternative_name( const char *hostname, const X509 *server_cert) { amqp_hostname_validation_result result = AMQP_HVR_MATCH_NOT_FOUND; int i; int san_names_nb = -1; STACK_OF(GENERAL_NAME) *san_names = NULL; // Try to extract the names within the SAN extension from the certificate san_names = X509_get_ext_d2i((X509 *)server_cert, NID_subject_alt_name, NULL, NULL); if (san_names == NULL) { return AMQP_HVR_NO_SAN_PRESENT; } san_names_nb = sk_GENERAL_NAME_num(san_names); // Check each name within the extension for (i = 0; i < san_names_nb; i++) { const GENERAL_NAME *current_name = sk_GENERAL_NAME_value(san_names, i); if (current_name->type == GEN_DNS) { // Current name is a DNS name, let's check it char *dns_name = (char *)ASN1_STRING_data(current_name->d.dNSName); // Make sure there isn't an embedded NUL character in the DNS name if ((size_t)ASN1_STRING_length(current_name->d.dNSName) != strlen(dns_name)) { result = AMQP_HVR_MALFORMED_CERTIFICATE; break; } else { // Compare expected hostname with the DNS name if (amqp_hostcheck(dns_name, hostname) == AMQP_HCR_MATCH) { result = AMQP_HVR_MATCH_FOUND; break; } } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); return result; }
/** * Tries to find a match for hostname in the certificate's Common Name field. * * Returns AMQP_HVR_MATCH_FOUND if a match was found. * Returns AMQP_HVR_MATCH_NOT_FOUND if no matches were found. * Returns AMQP_HVR_MALFORMED_CERTIFICATE if the Common Name had a NUL character embedded in it. * Returns AMQP_HVR_ERROR if the Common Name could not be extracted. */ static amqp_hostname_validation_result amqp_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 AMQP_HVR_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 AMQP_HVR_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 AMQP_HVR_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 AMQP_HVR_MALFORMED_CERTIFICATE; } // Compare expected hostname with the CN if (amqp_hostcheck(common_name_str, hostname) == AMQP_HCR_MATCH) { return AMQP_HVR_MATCH_FOUND; } else { return AMQP_HVR_MATCH_NOT_FOUND; } }