Skip to content

Instantly share code, notes, and snippets.

@madsen
Last active June 18, 2019 06:33
Show Gist options
  • Save madsen/51d6160a906187b026d0 to your computer and use it in GitHub Desktop.
Save madsen/51d6160a906187b026d0 to your computer and use it in GitHub Desktop.
Patch Asterisk to allow verifying Twilio's secure SIP trunking

Twilio Secure Trunking with Asterisk

Twilio now allows TLS & SRTP connections with their Elastic SIP Trunking. However, there are some issues with the way they've done it that prevent Asterisk from being able to verify their server's certificate.

First of all, their documentation tells you to download the Thawte Primary Root CA, when the certificate I saw was signed with the Thawte Premium Server CA instead. This caused me to get ERROR[...] tcptls.c: Certificate did not verify: self signed certificate in certificate chain.

Downloading the Premium Server CA fixed the self signed issue, but then I started getting ERROR[...] tcptls.c: Certificate common name did not match (myhost.pstn.us1.twilio.com). Eventually, I figured out that Twilio is using a wildcard certificate for *.pstn.us1.twilio.com, but RFC 5922 section 7.2 specifically states that implementations MUST NOT accept wildcard certificates. Asterisk follows the RFC and will not accept the wildcard match.

Since setting tlsdontverifyserver=yes as Twilio's Asterisk Elastic SIP Trunking Interconnection Guide using Secure Trunking directs causes you to miss out on much of the benefit of TLS, I wanted to fix this. I can't fix Twilio's certificate, so I patched Asterisk to allow it.

This patch allows any certificate for .twilio.com to match any hostname ending with .twilio.com. Other domains are not affected. I didn't bother with true wildcard support, since it's against the RFC.

The patch is for Asterisk 13.5.0, although it shouldn't be hard to port to other versions.

After applying the patch and recompiling Asterisk, you can follow Twilio's Asterisk Guide, but make sure tlscafile or tlscapath points to the Thawte Premium Server CA. Then you can set tlsdontverifyserver=no and get actual secure trunking from Asterisk to Twilio.

It doesn't appear that Twilio verifies Asterisk's certificate, so origination from Twilio to Asterisk is still vulnerable to an active MITM.

--- old/main/tcptls.c 2015-09-27 11:17:38.207388448 -0500
+++ new/main/tcptls.c 2015-09-27 11:27:09.760105826 -0500
@@ -555,6 +555,12 @@
ao2_cleanup(i->private_data);
}
+static int is_twilio_hostname(const char* host)
+{
+ const size_t len = strlen(host);
+ return ((len > 11) && !strcasecmp(host + (len - 11), ".twilio.com"));
+}
+
/*! \brief
* creates a FILE * from the fd passed by the accept thread.
* This operation is potentially expensive (certificate verification),
@@ -635,6 +641,7 @@
X509_NAME *name = X509_get_subject_name(peer);
int pos = -1;
int found = 0;
+ const int twilio_hack = is_twilio_hostname(tcptls_session->parent->hostname);
for (;;) {
/* Walk the certificate to check all available "Common Name" */
@@ -652,7 +659,8 @@
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)) {
+ } else if (!strcasecmp(tcptls_session->parent->hostname, (char *) str2) ||
+ (twilio_hack && is_twilio_hostname((char *) str2))) {
found = 1;
}
ast_debug(3, "SSL Common Name compare s1='%s' s2='%s'\n", tcptls_session->parent->hostname, str2);
@maxguru
Copy link

maxguru commented Apr 19, 2018

Here is my attempt at porting the patch to 15.3.0.

--- main/tcptls.c	2018-04-17 13:07:06.562084585 -0700
+++ main/tcptls.c	2018-04-17 13:07:04.002080830 -0700
@@ -63,6 +63,12 @@
 	ao2_cleanup(i->private_data);
 }
 
+static int is_twilio_hostname(const char* host)
+{
+  const size_t len = strlen(host);
+  return ((len > 11) && !strcasecmp(host + (len - 11), ".twilio.com"));
+}
+
 #ifdef DO_SSL
 static int check_tcptls_cert_name(ASN1_STRING *cert_str, const char *hostname, const char *desc)
 {
@@ -80,6 +86,8 @@
 		ret = -1;
 	} else if (!strcasecmp(hostname, (char *) str)) {
 		ret = 0;
+	} else if((is_twilio_hostname(hostname) && is_twilio_hostname((char *) str))) {
+		ret = 0;
 	} else {
 		ret = -1;
 	}

@Mackey22
Copy link

Thank you both for this! Maxguru's port works for 13.21 as well if anyone else is looking for that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment