diff options
author | Nanang Izzuddin <nanang@teluu.com> | 2012-01-16 05:05:47 +0000 |
---|---|---|
committer | Nanang Izzuddin <nanang@teluu.com> | 2012-01-16 05:05:47 +0000 |
commit | 06d3f22a5f024613b692b6113eec1ca3122a2592 (patch) | |
tree | 48db3afcaef35c4dbfbdebd1a2dad41baa598fa3 | |
parent | 9a277148324b884961ee301376524f079dde0a81 (diff) |
Close #1014:
- Added configurable ciphers setting in SIP TLS transport and pjsua app.
- Added API pj_ssl_cipher_is_supported().
git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/1.x@3942 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r-- | pjlib/include/pj/ssl_sock.h | 24 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_common.c | 100 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_ossl.c | 57 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_symbian.cpp | 198 | ||||
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 74 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_transport_tls.h | 24 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transport_tls.c | 5 |
7 files changed, 334 insertions, 148 deletions
diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h index faf82f5e..0e8fb4a4 100644 --- a/pjlib/include/pj/ssl_sock.h +++ b/pjlib/include/pj/ssl_sock.h @@ -332,16 +332,38 @@ PJ_DECL(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], /** + * Check if the specified cipher is supported by SSL/TLS backend. + * + * @param cipher The cipher. + * + * @return PJ_TRUE when supported. + */ +PJ_DECL(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher); + + +/** * Get cipher name string. * * @param cipher The cipher. * - * @return The cipher name or NULL if cipher is not recognized. + * @return The cipher name or NULL if cipher is not recognized/ + * supported. */ PJ_DECL(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher); /** + * Get cipher ID from cipher name string. + * + * @param cipher_name The cipher name string. + * + * @return The cipher ID or PJ_TLS_UNKNOWN_CIPHER if the cipher + * name string is not recognized/supported. + */ +PJ_DECL(pj_ssl_cipher) pj_ssl_cipher_id(const char *cipher_name); + + +/** * This structure contains the callbacks to be called by the secure socket. */ typedef struct pj_ssl_sock_cb diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c index c70a7af1..67a8d63c 100644 --- a/pjlib/src/pj/ssl_sock_common.c +++ b/pjlib/src/pj/ssl_sock_common.c @@ -21,89 +21,6 @@ #include <pj/errno.h> #include <pj/string.h> -/* Cipher name structure */ -typedef struct cipher_name_t { - pj_ssl_cipher cipher; - const char *name; -} cipher_name_t; - -/* Cipher name constants */ -static cipher_name_t cipher_names[] = -{ - {PJ_TLS_NULL_WITH_NULL_NULL, "NULL"}, - - /* TLS/SSLv3 */ - {PJ_TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, - {PJ_TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, - {PJ_TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, - {PJ_TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, - {PJ_TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, - {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, - {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, - {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, - {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, - {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, - {PJ_TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, - {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, - {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, - {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, - {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, - {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, - - /* TLS (deprecated) */ - {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, - {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, - {PJ_TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, - {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, - {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, - {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, - {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, - {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, - {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, - {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, - {PJ_TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, - - /* SSLv3 */ - {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, - {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, - {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, - - /* SSLv2 */ - {PJ_SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, - {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, - {PJ_SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, - {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, - {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, - {PJ_SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, - {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"} -}; - - /* * Initialize the SSL socket configuration with the default values. */ @@ -129,23 +46,6 @@ PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) } -/* Get cipher name string */ -PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) -{ - unsigned i, n; - - n = PJ_ARRAY_SIZE(cipher_names); - for (i = 0; i < n; ++i) { - if (cipher == cipher_names[i].cipher) - return cipher_names[i].name; - } - - return NULL; -} - - - - PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings( pj_uint32_t verify_status, const char *error_strings[], diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c index 6ce44ad1..559b5134 100644 --- a/pjlib/src/pj/ssl_sock_ossl.c +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -39,6 +39,9 @@ /* Workaround for ticket #985 */ #define DELAYED_CLOSE_TIMEOUT 200 +/* Maximum ciphers */ +#define MAX_CIPHERS 100 + /* * Include OpenSSL headers */ @@ -269,8 +272,11 @@ static pj_str_t ssl_strerror(pj_status_t status, static int openssl_init_count; /* OpenSSL available ciphers */ -static pj_ssl_cipher openssl_ciphers[100]; static unsigned openssl_cipher_num; +static struct openssl_ciphers_t { + pj_ssl_cipher id; + const char *name; +} openssl_ciphers[MAX_CIPHERS]; /* OpenSSL application data index */ static int sslsock_idx; @@ -329,9 +335,9 @@ static pj_status_t init_openssl(void) for (i = 0; i < n; ++i) { SSL_CIPHER *c; c = sk_SSL_CIPHER_value(sk_cipher,i); - openssl_ciphers[i] = (pj_ssl_cipher) - (pj_uint32_t)c->id & 0x00FFFFFF; - //printf("%3u: %08x=%s\n", i+1, c->id, SSL_CIPHER_get_name(c)); + openssl_ciphers[i].id = (pj_ssl_cipher) + (pj_uint32_t)c->id & 0x00FFFFFF; + openssl_ciphers[i].name = SSL_CIPHER_get_name(c); } SSL_free(ssl); @@ -1705,18 +1711,57 @@ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], shutdown_openssl(); } - if (openssl_cipher_num == 0) + if (openssl_cipher_num == 0) { + *cipher_num = 0; return PJ_ENOTFOUND; + } *cipher_num = PJ_MIN(*cipher_num, openssl_cipher_num); for (i = 0; i < *cipher_num; ++i) - ciphers[i] = openssl_ciphers[i]; + ciphers[i] = openssl_ciphers[i].id; return PJ_SUCCESS; } +/* Get cipher name string */ +PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i; + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + for (i = 0; i < openssl_cipher_num; ++i) { + if (cipher == openssl_ciphers[i].id) + return openssl_ciphers[i].name; + } + + return NULL; +} + +/* Check if the specified cipher is supported by SSL/TLS backend. */ +PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) +{ + unsigned i; + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + for (i = 0; i < openssl_cipher_num; ++i) { + if (cipher == openssl_ciphers[i].id) + return PJ_TRUE; + } + + return PJ_FALSE; +} + + /* * Create SSL socket instance. */ diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp index 95c10077..47288fae 100644 --- a/pjlib/src/pj/ssl_sock_symbian.cpp +++ b/pjlib/src/pj/ssl_sock_symbian.cpp @@ -32,6 +32,104 @@ #define THIS_FILE "ssl_sock_symbian.cpp" + +/* Cipher name structure */ +typedef struct cipher_name_t { + pj_ssl_cipher cipher; + const char *name; +} cipher_name_t; + +/* Cipher name constants */ +static cipher_name_t cipher_names[] = +{ + {PJ_TLS_NULL_WITH_NULL_NULL, "NULL"}, + + /* TLS/SSLv3 */ + {PJ_TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, + {PJ_TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, + {PJ_TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, + {PJ_TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, + {PJ_TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, + {PJ_TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, + {PJ_TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, + {PJ_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, + {PJ_TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, + + /* TLS (deprecated) */ + {PJ_TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, + {PJ_TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, + {PJ_TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, + {PJ_TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, + {PJ_TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, + {PJ_TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, + {PJ_TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, + {PJ_TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, + + /* SSLv3 */ + {PJ_SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, + {PJ_SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, + {PJ_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, + + /* SSLv2 */ + {PJ_SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, + {PJ_SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, + {PJ_SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, + {PJ_SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, + {PJ_SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, + {PJ_SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, + {PJ_SSL_CK_DES_192_EDE3_CBC_WITH_MD5, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"} +}; + + +/* Get cipher name string */ +static const char* get_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i, n; + + n = PJ_ARRAY_SIZE(cipher_names); + for (i = 0; i < n; ++i) { + if (cipher == cipher_names[i].cipher) + return cipher_names[i].name; + } + + return "CIPHER_UNKNOWN"; +} + typedef void (*CPjSSLSocket_cb)(int err, void *key); class CPjSSLSocketReader : public CActive @@ -115,7 +213,8 @@ public: int Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, const TInetAddr &rem_addr, - const TDesC8 &servername = TPtrC8(NULL,0)); + const TDesC8 &servername = TPtrC8(NULL,0), + const TDesC8 &ciphers = TPtrC8(NULL,0)); int Send(CPjSSLSocket_cb cb, void *key, const TDesC8 &aDesc, TUint flags); int SendSync(const TDesC8 &aDesc, TUint flags); @@ -146,6 +245,7 @@ private: TBuf<32> ssl_proto_; TInetAddr rem_addr_; TPtrC8 servername_; + TPtrC8 ciphers_; TInetAddr local_addr_; TSockXfrLength sent_len_; @@ -186,7 +286,8 @@ private: int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, const TInetAddr &local_addr, const TInetAddr &rem_addr, - const TDesC8 &servername) + const TDesC8 &servername, + const TDesC8 &ciphers) { pj_status_t status; @@ -213,7 +314,11 @@ int CPjSSLSocket::Connect(CPjSSLSocket_cb cb, void *key, cb_ = cb; key_ = key; rem_addr_ = rem_addr; + + /* Note: the following members only keep the pointer, not the data */ servername_.Set(servername); + ciphers_.Set(ciphers); + rSock.Connect(rem_addr_, iStatus); SetActive(); state_ = SSL_STATE_CONNECTING; @@ -318,6 +423,8 @@ void CPjSSLSocket::RunL() if (servername_.Length() > 0) securesock_->SetOpt(KSoSSLDomainName, KSolInetSSL, servername_); + if (ciphers_.Length() > 0) + securesock_->SetAvailableCipherSuites(ciphers_); // FlushSessionCache() seems to also fire signals to all // completed AOs (something like CActiveScheduler::RunIfReady()) @@ -441,9 +548,8 @@ struct pj_ssl_sock_t pj_ssl_sock_proto proto; pj_time_val timeout; - unsigned ciphers_num; - pj_ssl_cipher *ciphers; pj_str_t servername; + pj_str_t ciphers; pj_ssl_cert_info remote_cert_info; }; @@ -579,15 +685,20 @@ static void update_certs_info(pj_ssl_sock_t *ssock) } +/* Available ciphers */ +static unsigned ciphers_num_ = 0; +static struct ciphers_t +{ + pj_ssl_cipher id; + const char *name; +} ciphers_[64]; + /* * Get cipher list supported by SSL/TLS backend. */ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], unsigned *cipher_num) { - /* Available ciphers */ - static pj_ssl_cipher ciphers_[64]; - static unsigned ciphers_num_ = 0; unsigned i; PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); @@ -605,25 +716,69 @@ PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables (pj_ssl_cipher ciphers[], ciphers_num_ = ciphers_buf.Length() / 2; if (ciphers_num_ > PJ_ARRAY_SIZE(ciphers_)) ciphers_num_ = PJ_ARRAY_SIZE(ciphers_); - for (i = 0; i < ciphers_num_; ++i) - ciphers_[i] = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + - ciphers_buf[i*2+1]); + for (i = 0; i < ciphers_num_; ++i) { + ciphers_[i].id = (pj_ssl_cipher)(ciphers_buf[i*2]*10 + + ciphers_buf[i*2+1]); + ciphers_[i].name = get_cipher_name(ciphers_[i].id); + } } delete secure_sock; } if (ciphers_num_ == 0) { + *cipher_num = 0; return PJ_ENOTFOUND; } *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); for (i = 0; i < *cipher_num; ++i) - ciphers[i] = ciphers_[i]; + ciphers[i] = ciphers_[i].id; return PJ_SUCCESS; } + +/* Get cipher name string */ +PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher) +{ + unsigned i; + + if (ciphers_num_ == 0) { + pj_ssl_cipher c[1]; + i = 0; + pj_ssl_cipher_get_availables(c, &i); + } + + for (i = 0; i < ciphers_num_; ++i) { + if (cipher == ciphers_[i].id) + return ciphers_[i].name; + } + + return NULL; +} + + +/* Check if the specified cipher is supported by SSL/TLS backend. */ +PJ_DEF(pj_bool_t) pj_ssl_cipher_is_supported(pj_ssl_cipher cipher) +{ + unsigned i; + + if (ciphers_num_ == 0) { + pj_ssl_cipher c[1]; + i = 0; + pj_ssl_cipher_get_availables(c, &i); + } + + for (i = 0; i < ciphers_num_; ++i) { + if (cipher == ciphers_[i].id) + return PJ_TRUE; + } + + return PJ_FALSE; +} + + /* * Create SSL socket instance. */ @@ -652,14 +807,15 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, ssock->cb = param->cb; ssock->user_data = param->user_data; ssock->timeout = param->timeout; - ssock->ciphers_num = param->ciphers_num; if (param->ciphers_num > 0) { - unsigned i; - ssock->ciphers = (pj_ssl_cipher*) - pj_pool_calloc(pool, param->ciphers_num, - sizeof(pj_ssl_cipher)); - for (i = 0; i < param->ciphers_num; ++i) - ssock->ciphers[i] = param->ciphers[i]; + /* Cipher list in Symbian is represented as array of two-octets. */ + ssock->ciphers.slen = param->ciphers_num*2; + ssock->ciphers.ptr = (char*)pj_pool_alloc(pool, ssock->ciphers.slen); + pj_uint8_t *c = (pj_uint8_t*)ssock->ciphers.ptr; + for (unsigned i = 0; i < param->ciphers_num; ++i) { + *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF00) >> 8; + *c++ = (pj_uint8_t)(param->ciphers[i] & 0xFF); + } } pj_strdup_with_null(pool, &ssock->servername, ¶m->server_name); @@ -1246,9 +1402,13 @@ PJ_DEF(pj_status_t) pj_ssl_sock_start_connect (pj_ssl_sock_t *ssock, TPtrC8 servername_((TUint8*)ssock->servername.ptr, ssock->servername.slen); + /* Convert cipher list to Symbian descriptor */ + TPtrC8 ciphers_((TUint8*)ssock->ciphers.ptr, + ssock->ciphers.slen); + /* Try to connect */ status = sock->Connect(&connect_cb, ssock, localaddr_, remaddr_, - servername_); + servername_, ciphers_); if (status != PJ_SUCCESS && status != PJ_EPENDING) { delete sock; return status; diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index 81e6d57f..0c3d3eb6 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -251,6 +251,8 @@ static void usage(void) puts (" May be specified multiple times"); puts (" --stun-srv=FORMAT Set STUN server host or domain. This option may be"); puts (" specified more than once. FORMAT is hostdom[:PORT]"); + +#if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0) puts (""); puts ("TLS Options:"); puts (" --use-tls Enable TLS transport (default=no)"); @@ -262,6 +264,9 @@ static void usage(void) puts (" --tls-verify-client Verify client's certificate (default=no)"); puts (" --tls-neg-timeout Specify TLS negotiation timeout (default=no)"); puts (" --tls-srv-name Specify TLS server name for multihosting server"); + puts (" --tls-cipher Specify prefered TLS cipher (optional)."); + puts (" May be specified multiple times"); +#endif puts (""); puts ("Media Options:"); @@ -529,7 +534,7 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_NOREFERSUB, OPT_ACCEPT_REDIRECT, OPT_USE_TLS, OPT_TLS_CA_FILE, OPT_TLS_CERT_FILE, OPT_TLS_PRIV_FILE, OPT_TLS_PASSWORD, OPT_TLS_VERIFY_SERVER, OPT_TLS_VERIFY_CLIENT, - OPT_TLS_NEG_TIMEOUT, OPT_TLS_SRV_NAME, + OPT_TLS_NEG_TIMEOUT, OPT_TLS_SRV_NAME, OPT_TLS_CIPHER, OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV, OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE, OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS, @@ -628,6 +633,7 @@ static pj_status_t parse_args(int argc, char *argv[], { "max-calls", 1, 0, OPT_MAX_CALLS}, { "duration", 1, 0, OPT_DURATION}, { "thread-cnt", 1, 0, OPT_THREAD_CNT}, +#if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0) { "use-tls", 0, 0, OPT_USE_TLS}, { "tls-ca-file",1, 0, OPT_TLS_CA_FILE}, { "tls-cert-file",1,0, OPT_TLS_CERT_FILE}, @@ -637,6 +643,8 @@ static pj_status_t parse_args(int argc, char *argv[], { "tls-verify-client", 0, 0, OPT_TLS_VERIFY_CLIENT}, { "tls-neg-timeout", 1, 0, OPT_TLS_NEG_TIMEOUT}, { "tls-srv-name", 1, 0, OPT_TLS_SRV_NAME}, + { "tls-cipher", 1, 0, OPT_TLS_CIPHER}, +#endif { "capture-dev", 1, 0, OPT_CAPTURE_DEV}, { "playback-dev", 1, 0, OPT_PLAYBACK_DEV}, { "capture-lat", 1, 0, OPT_CAPTURE_LAT}, @@ -1303,28 +1311,17 @@ static pj_status_t parse_args(int argc, char *argv[], } break; +#if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0) case OPT_USE_TLS: cfg->use_tls = PJ_TRUE; -#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0 - PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured")); - return -1; -#endif break; case OPT_TLS_CA_FILE: cfg->udp_cfg.tls_setting.ca_list_file = pj_str(pj_optarg); -#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0 - PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured")); - return -1; -#endif break; case OPT_TLS_CERT_FILE: cfg->udp_cfg.tls_setting.cert_file = pj_str(pj_optarg); -#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0 - PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured")); - return -1; -#endif break; case OPT_TLS_PRIV_FILE: @@ -1333,10 +1330,6 @@ static pj_status_t parse_args(int argc, char *argv[], case OPT_TLS_PASSWORD: cfg->udp_cfg.tls_setting.password = pj_str(pj_optarg); -#if !defined(PJSIP_HAS_TLS_TRANSPORT) || PJSIP_HAS_TLS_TRANSPORT==0 - PJ_LOG(1,(THIS_FILE, "Error: TLS support is not configured")); - return -1; -#endif break; case OPT_TLS_VERIFY_SERVER: @@ -1355,6 +1348,39 @@ static pj_status_t parse_args(int argc, char *argv[], case OPT_TLS_SRV_NAME: cfg->udp_cfg.tls_setting.server_name = pj_str(pj_optarg); break; + case OPT_TLS_CIPHER: + { + pj_ssl_cipher cipher; + + if (pj_ansi_strnicmp(pj_optarg, "0x", 2) == 0) { + pj_str_t cipher_st = pj_str(pj_optarg + 2); + cipher = pj_strtoul2(&cipher_st, NULL, 16); + } else { + cipher = atoi(pj_optarg); + } + + if (pj_ssl_cipher_is_supported(cipher)) { + static pj_ssl_cipher tls_ciphers[128]; + + tls_ciphers[cfg->udp_cfg.tls_setting.ciphers_num++] = cipher; + cfg->udp_cfg.tls_setting.ciphers = tls_ciphers; + } else { + pj_ssl_cipher ciphers[128]; + unsigned j, ciphers_cnt; + + ciphers_cnt = PJ_ARRAY_SIZE(ciphers); + pj_ssl_cipher_get_availables(ciphers, &ciphers_cnt); + + PJ_LOG(1,(THIS_FILE, "Cipher \"%s\" is not supported by " + "TLS/SSL backend.", pj_optarg)); + printf("Available TLS/SSL ciphers (%d):\n", ciphers_cnt); + for (j=0; j<ciphers_cnt; ++j) + printf("- 0x%06X: %s\n", ciphers[j], pj_ssl_cipher_name(ciphers[j])); + return -1; + } + } + break; +#endif /* PJSIP_HAS_TLS_TRANSPORT */ case OPT_CAPTURE_DEV: cfg->capture_dev = atoi(pj_optarg); @@ -1773,6 +1799,7 @@ static int write_settings(const struct app_config *config, pj_strcat2(&cfg, line); } +#if defined(PJSIP_HAS_TLS_TRANSPORT) && (PJSIP_HAS_TLS_TRANSPORT != 0) /* TLS */ if (config->use_tls) pj_strcat2(&cfg, "--use-tls\n"); @@ -1821,6 +1848,14 @@ static int write_settings(const struct app_config *config, pj_strcat2(&cfg, line); } + for (i=0; i<config->udp_cfg.tls_setting.ciphers_num; ++i) { + pj_ansi_sprintf(line, "--tls-cipher 0x%06X # %s\n", + config->udp_cfg.tls_setting.ciphers[i], + pj_ssl_cipher_name(config->udp_cfg.tls_setting.ciphers[i])); + pj_strcat2(&cfg, line); + } +#endif + pj_strcat2(&cfg, "\n#\n# Media settings:\n#\n"); /* SRTP */ @@ -3006,6 +3041,11 @@ static void on_transport_state(pjsip_transport *tp, const char *verif_msgs[32]; unsigned verif_msg_cnt; + /* Dump server TLS cipher */ + PJ_LOG(4,(THIS_FILE, "TLS cipher used: 0x%06X/%s", + ssl_sock_info->cipher, + pj_ssl_cipher_name(ssl_sock_info->cipher) )); + /* Dump server TLS certificate */ pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ", buf, sizeof(buf)); diff --git a/pjsip/include/pjsip/sip_transport_tls.h b/pjsip/include/pjsip/sip_transport_tls.h index f9426412..241b063b 100644 --- a/pjsip/include/pjsip/sip_transport_tls.h +++ b/pjsip/include/pjsip/sip_transport_tls.h @@ -26,6 +26,7 @@ */ #include <pjsip/sip_transport.h> +#include <pj/pool.h> #include <pj/ssl_sock.h> #include <pj/string.h> #include <pj/sock_qos.h> @@ -106,10 +107,19 @@ typedef struct pjsip_tls_setting int method; /** - * TLS cipher list string in OpenSSL format. If empty, then default - * cipher list of the backend will be used. + * Number of ciphers contained in the specified cipher preference. + * If this is set to zero, then default cipher list of the backend + * will be used. + * + * Default: 0 (zero). + */ + unsigned ciphers_num; + + /** + * Ciphers and order preference. The #pj_ssl_cipher_get_availables() + * can be used to check the available ciphers supported by backend. */ - pj_str_t ciphers; + pj_ssl_cipher *ciphers; /** * Optionally specify the server name instance to be contacted when @@ -246,7 +256,13 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool, pj_strdup_with_null(pool, &dst->cert_file, &src->cert_file); pj_strdup_with_null(pool, &dst->privkey_file, &src->privkey_file); pj_strdup_with_null(pool, &dst->password, &src->password); - pj_strdup_with_null(pool, &dst->ciphers, &src->ciphers); + if (src->ciphers_num) { + unsigned i; + dst->ciphers = (pj_ssl_cipher*) pj_pool_calloc(pool, src->ciphers_num, + sizeof(pj_ssl_cipher)); + for (i=0; i<src->ciphers_num; ++i) + dst->ciphers[i] = src->ciphers[i]; + } } diff --git a/pjsip/src/pjsip/sip_transport_tls.c b/pjsip/src/pjsip/sip_transport_tls.c index face6b88..bae3ffd1 100644 --- a/pjsip/src/pjsip/sip_transport_tls.c +++ b/pjsip/src/pjsip/sip_transport_tls.c @@ -293,6 +293,8 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start (pjsip_endpoint *endpt, ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; + ssock_param.ciphers_num = listener->tls_setting.ciphers_num; + ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, @@ -862,7 +864,6 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, ssock_param.cb.on_data_sent = &on_data_sent; ssock_param.async_cnt = 1; ssock_param.ioqueue = pjsip_endpt_get_ioqueue(listener->endpt); - PJ_TODO(synchronize_tls_cipher_type_with_ssl_sock_cipher_type); ssock_param.server_name = remote_name; ssock_param.timeout = listener->tls_setting.timeout; ssock_param.user_data = NULL; /* pending, must be set later */ @@ -872,6 +873,8 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, ssock_param.send_buffer_size = PJSIP_MAX_PKT_LEN; if (ssock_param.read_buffer_size < PJSIP_MAX_PKT_LEN) ssock_param.read_buffer_size = PJSIP_MAX_PKT_LEN; + ssock_param.ciphers_num = listener->tls_setting.ciphers_num; + ssock_param.ciphers = listener->tls_setting.ciphers; ssock_param.qos_type = listener->tls_setting.qos_type; ssock_param.qos_ignore_error = listener->tls_setting.qos_ignore_error; pj_memcpy(&ssock_param.qos_params, &listener->tls_setting.qos_params, |