res_pjsip: Make transport cipher option accept a comma separated list of cipher names.
authorRichard Mudgett <rmudgett@digium.com>
Thu, 2 Oct 2014 21:55:37 +0000 (21:55 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Thu, 2 Oct 2014 21:55:37 +0000 (21:55 +0000)
Improvements to the res_pjsip transport cipher option.

* Made the cipher option accept a comma separated list of OpenSSL cipher
names.  Users of realtime will be glad if they have more than one name to
list.

* Added the CLI command 'pjsip list ciphers' so a user can know what
OpenSSL names are available for the cipher option.

* Updated the cipher option online XML documentation to specify what is
expected for the value.

* Updated pjsip.conf.sample to not indicate that ALL is acceptable since
ALL does not imply a preference order for the ciphers and PJSIP does not
simply pass the string to OpenSSL for interpretation.

ASTERISK-24199 #close
Reported by: Joshua Colp

Review: https://reviewboard.asterisk.org/r/4018/
........

Merged revisions 424393 from http://svn.asterisk.org/svn/asterisk/branches/12
........

Merged revisions 424394 from http://svn.asterisk.org/svn/asterisk/branches/13

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

configs/samples/pjsip.conf.sample
res/res_pjsip.c
res/res_pjsip/config_transport.c

index c138815..8acd01a 100644 (file)
 ;bind=0.0.0.0
 ;cert_file=/path/mycert.crt
 ;priv_key_file=/path/mykey.key
-;cipher=ALL
+;cipher=ADH-AES256-SHA,ADH-AES128-SHA
 ;method=tlsv1
 
 
 ;ca_list_file=  ; File containing a list of certificates to read TLS ONLY
                 ; (default: "")
 ;cert_file=     ; Certificate file for endpoint TLS ONLY (default: "")
-;cipher=        ; Preferred Cryptography Cipher TLS ONLY (default: "")
+;cipher=        ; Preferred cryptography cipher names TLS ONLY (default: "")
 ;domain=        ; Domain the transport comes from (default: "")
 ;external_media_address=        ; External IP address to use in RTP handling
                                 ; (default: "")
index e6d0d0c..bbcf543 100644 (file)
                                        <description><para>
                                                This option only applies if <replaceable>media_encryption</replaceable> is
                                                set to <literal>dtls</literal>.
-                                       </para><para>
-                                               Many options for acceptable ciphers. See link for more:
-                                               http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
+                                       </para>
+                                       <para>Many options for acceptable ciphers. See link for more:</para>
+                                       <para>http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
                                        </para></description>
                                </configOption>
                                <configOption name="dtls_ca_file">
                                        <synopsis>Certificate file for endpoint (TLS ONLY)</synopsis>
                                </configOption>
                                <configOption name="cipher">
-                                       <synopsis>Preferred Cryptography Cipher (TLS ONLY)</synopsis>
-                                       <description><para>
-                                               Many options for acceptable ciphers see link for more:
-                                               http://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS
-                                       </para></description>
+                                       <synopsis>Preferred cryptography cipher names (TLS ONLY)</synopsis>
+                                       <description>
+                                       <para>Comma separated list of cipher names or numeric equivalents.
+                                               Numeric quivalents can be either decimal or hexadecimal (0xX).
+                                       </para>
+                                       <para>There are many cipher names.  Use the CLI command
+                                               <literal>pjsip list ciphers</literal> to see a list of cipher
+                                               names available for your installation.  See link for more:</para>
+                                       <para>http://www.openssl.org/docs/apps/ciphers.html#CIPHER_SUITE_NAMES
+                                       </para>
+                                       </description>
                                </configOption>
                                <configOption name="domain">
                                        <synopsis>Domain the transport comes from</synopsis>
index 9996ddd..fe7612f 100644 (file)
@@ -389,17 +389,19 @@ static int tls_method_to_str(const void *obj, const intptr_t *args, char **buf)
 /*! \brief Helper function which turns a cipher name into an identifier */
 static pj_ssl_cipher cipher_name_to_id(const char *name)
 {
-       pj_ssl_cipher ciphers[100], id = 0;
+       pj_ssl_cipher ciphers[100];
+       pj_ssl_cipher id = 0;
        unsigned int cipher_num = PJ_ARRAY_SIZE(ciphers);
        int pos;
+       const char *pos_name;
 
        if (pj_ssl_cipher_get_availables(ciphers, &cipher_num)) {
                return 0;
        }
 
        for (pos = 0; pos < cipher_num; ++pos) {
-               if (!pj_ssl_cipher_name(ciphers[pos]) ||
-                       strcmp(pj_ssl_cipher_name(ciphers[pos]), name)) {
+               pos_name = pj_ssl_cipher_name(ciphers[pos]);
+               if (!pos_name || strcmp(pos_name, name)) {
                        continue;
                }
 
@@ -410,60 +412,130 @@ static pj_ssl_cipher cipher_name_to_id(const char *name)
        return id;
 }
 
-/*! \brief Custom handler for TLS cipher setting */
-static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+/*!
+ * \internal
+ * \brief Add a new cipher to the transport's cipher list array.
+ *
+ * \param transport Which transport to add the cipher to.
+ * \param name Cipher identifier name.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int transport_cipher_add(struct ast_sip_transport *transport, const char *name)
 {
-       struct ast_sip_transport *transport = obj;
        pj_ssl_cipher cipher;
+       int idx;
 
-       if (ast_strlen_zero(var->value)) {
-               return 0;
-       }
-
-       if (transport->tls.ciphers_num == (SIP_TLS_MAX_CIPHERS - 1)) {
-               return -1;
-       }
-
-       cipher = cipher_name_to_id(var->value);
-
+       cipher = cipher_name_to_id(name);
        if (!cipher) {
                /* TODO: Check this over/tweak - it's taken from pjsua for now */
-               if (!strnicmp(var->value, "0x", 2)) {
-                       pj_str_t cipher_st = pj_str((char*)var->value + 2);
+               if (!strnicmp(name, "0x", 2)) {
+                       pj_str_t cipher_st = pj_str((char *) name + 2);
                        cipher = pj_strtoul2(&cipher_st, NULL, 16);
                } else {
-                       cipher = atoi(var->value);
+                       cipher = atoi(name);
                }
        }
 
        if (pj_ssl_cipher_is_supported(cipher)) {
+               for (idx = transport->tls.ciphers_num; idx--;) {
+                       if (transport->ciphers[idx] == cipher) {
+                               /* The cipher is already in the list. */
+                               return 0;
+                       }
+               }
                transport->ciphers[transport->tls.ciphers_num++] = cipher;
                return 0;
        } else {
-               ast_log(LOG_ERROR, "Cipher '%s' is unsupported\n", var->value);
+               ast_log(LOG_ERROR, "Cipher '%s' is unsupported\n", name);
                return -1;
        }
 }
 
-static int transport_tls_cipher_to_str(const void *obj, const intptr_t *args, char **buf)
+/*! \brief Custom handler for TLS cipher setting */
+static int transport_tls_cipher_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
 {
-       RAII_VAR(struct ast_str *, str, ast_str_create(MAX_OBJECT_FIELD), ast_free);
-       const struct ast_sip_transport *transport = obj;
-       int i;
+       struct ast_sip_transport *transport = obj;
+       char *parse;
+       char *name;
+       int res = 0;
+
+       parse = ast_strdupa(S_OR(var->value, ""));
+       while ((name = strsep(&parse, ","))) {
+               name = ast_strip(name);
+               if (ast_strlen_zero(name)) {
+                       continue;
+               }
+               if (ARRAY_LEN(transport->ciphers) <= transport->tls.ciphers_num) {
+                       ast_log(LOG_ERROR, "Too many ciphers specified\n");
+                       res = -1;
+                       break;
+               }
+               res |= transport_cipher_add(transport, name);
+       }
+       return res ? -1 : 0;
+}
+
+static void cipher_to_str(char **buf, const pj_ssl_cipher *ciphers, unsigned int cipher_num)
+{
+       struct ast_str *str;
+       int idx;
 
+       str = ast_str_create(128);
        if (!str) {
-               return -1;
+               *buf = NULL;
+               return;
        }
 
-       for (i = 0; i < transport->tls.ciphers_num; ++i) {
-               ast_str_append(&str, 0, "%s", pj_ssl_cipher_name(transport->ciphers[i]));
-               if (i < transport->tls.ciphers_num - 1) {
-                       ast_str_append(&str, 0, ",");
+       for (idx = 0; idx < cipher_num; ++idx) {
+               ast_str_append(&str, 0, "%s", pj_ssl_cipher_name(ciphers[idx]));
+               if (idx < cipher_num - 1) {
+                       ast_str_append(&str, 0, ", ");
                }
        }
 
        *buf = ast_strdup(ast_str_buffer(str));
-       return 0;
+       ast_free(str);
+}
+
+static int transport_tls_cipher_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+       const struct ast_sip_transport *transport = obj;
+
+       cipher_to_str(buf, transport->ciphers, transport->tls.ciphers_num);
+       return *buf ? 0 : -1;
+}
+
+static char *handle_pjsip_list_ciphers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       pj_ssl_cipher ciphers[100];
+       unsigned int cipher_num = PJ_ARRAY_SIZE(ciphers);
+       char *buf;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "pjsip list ciphers";
+               e->usage = "Usage: pjsip list ciphers\n"
+                       "       List available OpenSSL cipher names.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (pj_ssl_cipher_get_availables(ciphers, &cipher_num) || !cipher_num) {
+               buf = NULL;
+       } else {
+               cipher_to_str(&buf, ciphers, cipher_num);
+       }
+
+       if (!ast_strlen_zero(buf)) {
+               ast_cli(a->fd, "Available ciphers: '%s'\n", buf);
+       } else {
+               ast_cli(a->fd, "No available ciphers\n");
+       }
+       ast_free(buf);
+       return CLI_SUCCESS;
 }
 
 /*! \brief Custom handler for localnet setting */
@@ -638,6 +710,7 @@ static int cli_print_body(void *obj, void *arg, int flags)
 }
 
 static struct ast_cli_entry cli_commands[] = {
+       AST_CLI_DEFINE(handle_pjsip_list_ciphers, "List available OpenSSL cipher names"),
        AST_CLI_DEFINE(ast_sip_cli_traverse_objects, "List PJSIP Transports",
                .command = "pjsip list transports",
                .usage = "Usage: pjsip list transports\n"