summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaciej Szmigiero <mail@maciej.szmigiero.name>2015-05-15 00:12:41 +0200
committerMaciej Szmigiero <mail@maciej.szmigiero.name>2015-05-15 00:12:41 +0200
commit2415a14ce92885169b514d334370e95aa6273c2b (patch)
tree3ef2e0198da538f5457a431eabbbcc6fc676c72a
parent1ba7845851f5b5cf952276dd7f1f1591c1190b23 (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--CHANGES4
-rw-r--r--include/asterisk/tcptls.h1
-rw-r--r--main/tcptls.c67
3 files changed, 58 insertions, 14 deletions
diff --git a/CHANGES b/CHANGES
index 58291cf75..12bbea487 100644
--- a/CHANGES
+++ b/CHANGES
@@ -111,6 +111,10 @@ Core
set execincludes=yes in asterisk.conf. Any other option set on the
command-line will now override the equivalent setting from asterisk.conf.
+ * 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.
+
Functions
------------------
diff --git a/include/asterisk/tcptls.h b/include/asterisk/tcptls.h
index 0e8d9d042..a3f3f2884 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 0b06d22ac..652282b59 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);