SIP option to specify outbound TLS/SSL client protocol.
authorDavid Vossel <dvossel@digium.com>
Wed, 29 Apr 2009 21:13:43 +0000 (21:13 +0000)
committerDavid Vossel <dvossel@digium.com>
Wed, 29 Apr 2009 21:13:43 +0000 (21:13 +0000)
chan_sip allows for outbound TLS connections, but does not allow the user to specify what protocol to use (default was SSLv2, and still is if this new option is not specified).  This patch lets the user pick the SSL/TLS client method for outbound connections in sip.

(closes issue #14770)
Reported by: TheOldSaint

(closes issue #14768)
Reported by: TheOldSaint

Review: http://reviewboard.digium.com/r/240/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@191177 65c4cc65-6c06-0410-ace0-fbb531ad65f3

CHANGES
configs/sip.conf.sample
include/asterisk/tcptls.h
main/tcptls.c

diff --git a/CHANGES b/CHANGES
index 8bfdb3c..3c33582 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -23,6 +23,8 @@ SIP Changes
  * Added tlsprivatekey option to sip.conf.  This allows a separate .pem file
    to be used for holding a private key.  If tlsprivatekey is not specified,
    tlscertfile is searched for both public and private key.
+ * Added tlsclientmethod option to sip.conf.  This allows the protocol for
+   outbound client connections to be specified.
 
 Applications
 ------------
index b5e984f..37e3c47 100644 (file)
@@ -142,8 +142,12 @@ tcpbindaddr=0.0.0.0             ; IP address for TCP server to bind to (0.0.0.0
 
 ;tlscipher=<SSL cipher string>
 ;        A string specifying which SSL ciphers to use or not use
-;        A list of valid SSL cipher strings can be found at: 
+;        A list of valid SSL cipher strings can be found at:
 ;                http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+;
+;tlsclientmethod=tlsv1     ; values include tlsv1, sslv3, sslv2.
+                           ; Specify protocol for outbound client connections.
+                           ; If left unspecified, the default is sslv2.
 
 srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
                                 ; Note: Asterisk only uses the first host 
index 9496d97..b6cc9a3 100644 (file)
@@ -72,7 +72,13 @@ enum ast_ssl_flags {
        /*! Don't verify certificate when connecting to a server */
        AST_SSL_DONT_VERIFY_SERVER = (1 << 1),
        /*! Don't compare "Common Name" against IP or hostname */
-       AST_SSL_IGNORE_COMMON_NAME = (1 << 2)
+       AST_SSL_IGNORE_COMMON_NAME = (1 << 2),
+       /*! Use SSLv2 for outgoing client connections */
+       AST_SSL_SSLV2_CLIENT = (1 << 3),
+       /*! Use SSLv3 for outgoing client connections */
+       AST_SSL_SSLV3_CLIENT = (1 << 4),
+       /*! Use TLSv1 for outgoing client connections */
+       AST_SSL_TLSV1_CLIENT = (1 << 5)
 };
 
 struct ast_tls_config {
index 4609438..e51d7a2 100644 (file)
@@ -173,7 +173,7 @@ static void *handle_tls_connection(void *data)
                                        X509_NAME *name = X509_get_subject_name(peer);
                                        int pos = -1;
                                        int found = 0;
-                               
+
                                        for (;;) {
                                                /* Walk the certificate to check all available "Common Name" */
                                                /* XXX Probably should do a gethostbyname on the hostname and compare that as well */
@@ -229,7 +229,7 @@ void *ast_tcptls_server_root(void *data)
        socklen_t sinlen;
        struct ast_tcptls_session_instance *tcptls_session;
        pthread_t launched;
-       
+
        for (;;) {
                int i, flags;
 
@@ -261,7 +261,7 @@ void *ast_tcptls_server_root(void *data)
                memcpy(&tcptls_session->remote_address, &sin, sizeof(tcptls_session->remote_address));
 
                tcptls_session->client = 0;
-                       
+
                if (ast_pthread_create_detached_background(&launched, NULL, handle_tls_connection, tcptls_session)) {
                        ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
                        close(tcptls_session->fd);
@@ -283,7 +283,26 @@ static int __ssl_setup(struct ast_tls_config *cfg, int client)
        SSL_load_error_strings();
        SSLeay_add_ssl_algorithms();
 
-       if (!(cfg->ssl_ctx = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() ))) {
+       if (client) {
+               if (ast_test_flag(&cfg->flags, AST_SSL_SSLV2_CLIENT)) {
+                       cfg->ssl_ctx = SSL_CTX_new(SSLv2_client_method());
+               } else if (ast_test_flag(&cfg->flags, AST_SSL_SSLV3_CLIENT)) {
+                       cfg->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+               } else if (ast_test_flag(&cfg->flags, AST_SSL_TLSV1_CLIENT)) {
+                       cfg->ssl_ctx = SSL_CTX_new(TLSv1_client_method());
+               } else {
+                       /* SSLv23_client_method() sends SSLv2, this was the original
+                        * default for ssl clients before the option was given to
+                        * pick what protocol a client should use.  In order not
+                        * to break expected behavior it remains the default. */
+                       cfg->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+               }
+       } else {
+               /* SSLv23_server_method() supports TLSv1, SSLv2, and SSLv3 inbound connections. */
+               cfg->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+       }
+
+       if (!cfg->ssl_ctx) {
                ast_debug(1, "Sorry, SSL_CTX_new call returned null...\n");
                cfg->enabled = 0;
                return 0;
@@ -417,22 +436,22 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
 {
        int flags;
        int x = 1;
-       
+
        /* Do nothing if nothing has changed */
        if (!memcmp(&desc->old_address, &desc->local_address, sizeof(desc->old_address))) {
                ast_debug(1, "Nothing changed in %s\n", desc->name);
                return;
        }
-       
+
        desc->old_address = desc->local_address;
-       
+
        /* Shutdown a running server if there is one */
        if (desc->master != AST_PTHREADT_NULL) {
                pthread_cancel(desc->master);
                pthread_kill(desc->master, SIGURG);
                pthread_join(desc->master, NULL);
        }
-       
+
        if (desc->accept_fd != -1)
                close(desc->accept_fd);
 
@@ -447,7 +466,7 @@ void ast_tcptls_server_start(struct ast_tcptls_session_args *desc)
                ast_log(LOG_ERROR, "Unable to allocate socket for %s: %s\n", desc->name, strerror(errno));
                return;
        }
-       
+
        setsockopt(desc->accept_fd, SOL_SOCKET, SO_REUSEADDR, &x, sizeof(x));
        if (bind(desc->accept_fd, (struct sockaddr *) &desc->local_address, sizeof(desc->local_address))) {
                ast_log(LOG_ERROR, "Unable to bind %s to %s:%d: %s\n",
@@ -494,7 +513,7 @@ int ast_tls_read_conf(struct ast_tls_config *tls_cfg, struct ast_tcptls_session_
        if (!strcasecmp(varname, "tlsenable") || !strcasecmp(varname, "sslenable")) {
                tls_cfg->enabled = ast_true(value) ? 1 : 0;
                tls_desc->local_address.sin_family = AF_INET;
-       } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert")) {
+       } else if (!strcasecmp(varname, "tlscertfile") || !strcasecmp(varname, "sslcert") || !strcasecmp(varname, "tlscert")) {
                ast_free(tls_cfg->certfile);
                tls_cfg->certfile = ast_strdup(value);
        } else if (!strcasecmp(varname, "tlsprivatekey") || !strcasecmp(varname, "sslprivatekey")) {
@@ -518,6 +537,20 @@ int ast_tls_read_conf(struct ast_tls_config *tls_cfg, struct ast_tcptls_session_
                        ast_log(LOG_WARNING, "Invalid %s '%s'\n", varname, value);
        } else if (!strcasecmp(varname, "tlsbindport") || !strcasecmp(varname, "sslbindport")) {
                tls_desc->local_address.sin_port = htons(atoi(value));
+       } else if (!strcasecmp(varname, "tlsclientmethod") || !strcasecmp(varname, "sslclientmethod")) {
+               if (!strcasecmp(value, "tlsv1")) {
+                       ast_set_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
+               } else if (!strcasecmp(value, "sslv3")) {
+                       ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
+               } else if (!strcasecmp(value, "sslv2")) {
+                       ast_set_flag(&tls_cfg->flags, AST_SSL_SSLV2_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_TLSV1_CLIENT);
+                       ast_clear_flag(&tls_cfg->flags, AST_SSL_SSLV3_CLIENT);
+               }
        } else {
                return -1;
        }