summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Szmigiero <mail@maciej.szmigiero.name>2015-05-15 00:12:41 +0200
committerMichael Kuron <m.kuron@gmx.de>2016-11-15 20:56:43 +0100
commit7b96e8cc3d3db3d30921905203520b4e08b527b8 (patch)
tree346586fd945b5b2e8a1329225ed67ad49d0ebc93
parenta58d3597019f8cc5a503f140671232ae3f8d9119 (diff)
Add X.509 subject alternative name support to TLS certificate
verification. This way one X.509 certificate can be used for hosts that can be reached under multiple DNS names or for multiple hosts. Signed-off-by: Maciej Szmigiero <mail@maciej.szmigiero.name> ASTERISK-25063 #close Change-Id: I13302c80490a0b44c43f1b45376c9bd7b15a538f
-rw-r--r--CHANGES6
-rw-r--r--include/asterisk/tcptls.h1
-rw-r--r--main/tcptls.c67
3 files changed, 60 insertions, 14 deletions
diff --git a/CHANGES b/CHANGES
index 463c88427..612711e1d 100644
--- a/CHANGES
+++ b/CHANGES
@@ -54,6 +54,12 @@ Queue
* A new dialplan variable, ABANDONED, is set when the call is not answered
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 ----------
------------------------------------------------------------------------------
diff --git a/include/asterisk/tcptls.h b/include/asterisk/tcptls.h
index e1a632cca..3c5f4504c 100644
--- a/include/asterisk/tcptls.h
+++ b/include/asterisk/tcptls.h
@@ -65,6 +65,7 @@
#ifdef DO_SSL
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/x509v3.h>
#else
/* declare dummy types so we can define a pointer to them */
typedef struct {} SSL;
diff --git a/main/tcptls.c b/main/tcptls.c
index 34baf9a0e..bccb03d85 100644
--- a/main/tcptls.c
+++ b/main/tcptls.c
@@ -555,6 +555,34 @@ static void session_instance_destructor(void *obj)
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
* creates a FILE * from the fd passed by the accept thread.
* 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)) {
ASN1_STRING *str;
- unsigned char *str2;
X509_NAME *name = X509_get_subject_name(peer);
+ STACK_OF(GENERAL_NAME) *alt_names;
int pos = -1;
int found = 0;
@@ -643,25 +671,36 @@ static void *handle_tcptls_connection(void *data)
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 (!check_tcptls_cert_name(str, tcptls_session->parent->hostname, "common name")) {
+ found = 1;
+ 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);
- 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;
+ 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;
+ }
}
- ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
- OPENSSL_free(str2);
- }
- if (found) {
- break;
+
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
}
}
+
if (!found) {
ast_log(LOG_ERROR, "Certificate common name did not match (%s)\n", tcptls_session->parent->hostname);
X509_free(peer);