Merge "Add X.509 subject alternative name support to TLS certificate verification." into 13

This commit is contained in:
Joshua Colp
2016-11-16 13:14:42 -06:00
committed by Gerrit Code Review
3 changed files with 62 additions and 16 deletions

View File

@@ -54,6 +54,12 @@ Queue
* A new dialplan variable, ABANDONED, is set when the call is not answered * A new dialplan variable, ABANDONED, is set when the call is not answered
by an agent. by an agent.
Core
------------------
* The TLS core in Asterisk now supports X.509 certificate subject alternative
names. This way one X.509 certificate can be used for hosts that can be
reached under multiple DNS names or for multiple hosts.
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ---------- --- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
------------------------------------------------------------------------------ ------------------------------------------------------------------------------

View File

@@ -65,6 +65,7 @@
#ifdef DO_SSL #ifdef DO_SSL
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/x509v3.h>
#else #else
/* declare dummy types so we can define a pointer to them */ /* declare dummy types so we can define a pointer to them */
typedef struct {} SSL; typedef struct {} SSL;

View File

@@ -555,6 +555,34 @@ static void session_instance_destructor(void *obj)
ao2_cleanup(i->private_data); ao2_cleanup(i->private_data);
} }
#ifdef DO_SSL
static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, const char *desc)
{
unsigned char *str;
int ret;
ret = ASN1_STRING_to_UTF8(&str, cert_str);
if (ret < 0 || !str) {
return -1;
}
if (strlen((char *) str) != ret) {
ast_log(LOG_WARNING, "Invalid certificate %s length (contains NULL bytes?)\n", desc);
ret = -1;
} else if (!strcasecmp(hostname, (char *) str)) {
ret = 0;
} else {
ret = -1;
}
ast_debug(3, "SSL %s compare s1='%s' s2='%s'\n", desc, hostname, str);
OPENSSL_free(str);
return ret;
}
#endif
/*! \brief /*! \brief
* creates a FILE * from the fd passed by the accept thread. * creates a FILE * from the fd passed by the accept thread.
* This operation is potentially expensive (certificate verification), * This operation is potentially expensive (certificate verification),
@@ -631,8 +659,8 @@ static void *handle_tcptls_connection(void *data)
} }
if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) { if (!ast_test_flag(&tcptls_session->parent->tls_cfg->flags, AST_SSL_IGNORE_COMMON_NAME)) {
ASN1_STRING *str; ASN1_STRING *str;
unsigned char *str2;
X509_NAME *name = X509_get_subject_name(peer); X509_NAME *name = X509_get_subject_name(peer);
STACK_OF(GENERAL_NAME) *alt_names;
int pos = -1; int pos = -1;
int found = 0; int found = 0;
@@ -643,25 +671,36 @@ static void *handle_tcptls_connection(void *data)
if (pos < 0) { if (pos < 0) {
break; 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) { str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, pos));
if (strlen((char *) str2) != ret) { if (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) {
ast_log(LOG_WARNING, "Invalid certificate common name length (contains NULL bytes?)\n"); found = 1;
} 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; break;
} }
} }
if (!found) {
alt_names = X509_get_ext_d2i(peer, NID_subject_alt_name, NULL, NULL);
if (alt_names != NULL) {
int alt_names_count = sk_GENERAL_NAME_num(alt_names);
for (pos = 0; pos < alt_names_count; pos++) {
const GENERAL_NAME *alt_name = sk_GENERAL_NAME_value(alt_names, pos);
if (alt_name->type != GEN_DNS) {
continue;
}
if (!check_tcptls_cert_name(alt_name->d.dNSName, tcptls_session->parent->hostname, "alt name")) {
found = 1;
break;
}
}
sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
}
}
if (!found) { if (!found) {
ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname); ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
X509_free(peer); X509_free(peer);