diff options
author | Nanang Izzuddin <nanang@teluu.com> | 2009-10-16 03:06:13 +0000 |
---|---|---|
committer | Nanang Izzuddin <nanang@teluu.com> | 2009-10-16 03:06:13 +0000 |
commit | 3a4825fe2ada73d7a916d351b2bdd36968dd61fb (patch) | |
tree | 05af6608b4ea74f0ae5c6a5cc407c6795d9af49a /pjlib | |
parent | 3e28e8f4926c01bed6cd95d4debc907da6c3a36e (diff) |
Ticket #957:
- Added SSL socket abstraction with OpenSSL backend.
- Updated cipher data type and added cipher constants (Symbian SSL socket has also been updated).
- Updated SIP TLS transport to allow setting certificate/credential (via file).
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2950 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib')
-rw-r--r-- | pjlib/include/pj/ssl_sock.h | 162 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_common.c | 101 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_ossl.c | 1780 | ||||
-rw-r--r-- | pjlib/src/pj/ssl_sock_symbian.cpp | 122 |
4 files changed, 2111 insertions, 54 deletions
diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h index 9836a3d6..ce319d4d 100644 --- a/pjlib/include/pj/ssl_sock.h +++ b/pjlib/include/pj/ssl_sock.h @@ -45,16 +45,140 @@ PJ_BEGIN_DECL * described more detail) in \ref PJ_ACTIVESOCK. */ + + /** + * This opaque structure describes the secure socket. + */ +typedef struct pj_ssl_sock_t pj_ssl_sock_t; + + /** - * Opaque declaration of certificate or endpoint credentials. This may contains - * certificate, private key, and trusted Certificate Authorities lists. + * Opaque declaration of endpoint certificate or credentials. This may contains + * certificate, private key, and trusted Certificate Authorities list. */ typedef struct pj_ssl_cert_t pj_ssl_cert_t; + /** - * This opaque structure describes the secure socket. + * Create credential from files. + * + * @param CA_file The file of trusted CA list. + * @param cert_file The file of certificate. + * @param privkey_file The file of private key. + * @param privkey_pass The password of private key, if any. + * + * @return PJ_SUCCESS when successful. */ -typedef struct pj_ssl_sock_t pj_ssl_sock_t; +PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, + const pj_str_t *CA_file, + const pj_str_t *cert_file, + const pj_str_t *privkey_file, + const pj_str_t *privkey_pass, + pj_ssl_cert_t **p_cert); + + +/** + * Cipher suites enumeration. + */ +typedef enum pj_ssl_cipher { + + /* NULL */ + TLS_NULL_WITH_NULL_NULL = 0x00000000, + + /* TLS/SSLv3 */ + TLS_RSA_WITH_NULL_MD5 = 0x00000001, + TLS_RSA_WITH_NULL_SHA = 0x00000002, + TLS_RSA_WITH_NULL_SHA256 = 0x0000003B, + TLS_RSA_WITH_RC4_128_MD5 = 0x00000004, + TLS_RSA_WITH_RC4_128_SHA = 0x00000005, + TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x0000000A, + TLS_RSA_WITH_AES_128_CBC_SHA = 0x0000002F, + TLS_RSA_WITH_AES_256_CBC_SHA = 0x00000035, + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003C, + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x0000003D, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x0000000D, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000010, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x00000013, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x00000016, + TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x00000030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x00000031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x00000032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x00000033, + TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x00000036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x00000037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x00000038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x00000039, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x0000003E, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x0000003F, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x00000040, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x00000067, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x00000068, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x00000069, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x0000006A, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x0000006B, + TLS_DH_anon_WITH_RC4_128_MD5 = 0x00000018, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x0000001B, + TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x00000034, + TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x0000003A, + TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x0000006C, + TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x0000006D, + + /* TLS (deprecated) */ + TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x00000003, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x00000006, + TLS_RSA_WITH_IDEA_CBC_SHA = 0x00000007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000008, + TLS_RSA_WITH_DES_CBC_SHA = 0x00000009, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0000000B, + TLS_DH_DSS_WITH_DES_CBC_SHA = 0x0000000C, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0000000E, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0x0000000F, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x00000011, + TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x00000012, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x00000014, + TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x00000015, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x00000017, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x00000019, + TLS_DH_anon_WITH_DES_CBC_SHA = 0x0000001A, + + /* SSLv3 */ + SSL_FORTEZZA_KEA_WITH_NULL_SHA = 0x0000001C, + SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA= 0x0000001D, + SSL_FORTEZZA_KEA_WITH_RC4_128_SHA = 0x0000001E, + + /* SSLv2 */ + SSL_CK_RC4_128_WITH_MD5 = 0x00010080, + SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x00020080, + SSL_CK_RC2_128_CBC_WITH_MD5 = 0x00030080, + SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x00040080, + SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x00050080, + SSL_CK_DES_64_CBC_WITH_MD5 = 0x00060040, + SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x000700C0 + +} pj_ssl_cipher; + + +/** + * Get cipher list supported by SSL/TLS backend. + * + * @param ciphers The ciphers buffer to receive cipher list. + * @param cipher_num Maximum number of ciphers to be received. + * + * @return PJ_SUCCESS when successful. + */ +PJ_DECL(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], + unsigned *cipher_num); + + +/** + * Get cipher name string. + * + * @param cipher The cipher. + * + * @return The cipher name or NULL if cipher is not recognized. + */ +PJ_DECL(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher); + /** * This structure contains the callbacks to be called by the secure socket. @@ -180,10 +304,10 @@ typedef enum pj_ssl_sock_proto { PJ_SSL_SOCK_PROTO_DEFAULT, /**< Default protocol of backend. */ PJ_SSL_SOCK_PROTO_TLS1, /**< TLSv1.0 protocol. */ - PJ_SSL_SOCK_PROTO_SSL2, /**< SSLv2.0 protocol. */ PJ_SSL_SOCK_PROTO_SSL3, /**< SSLv3.0 protocol. */ PJ_SSL_SOCK_PROTO_SSL23, /**< SSLv3.0 but can roll back to SSLv2.0. */ + PJ_SSL_SOCK_PROTO_SSL2, /**< SSLv2.0 protocol. */ PJ_SSL_SOCK_PROTO_DTLS1 /**< DTLSv1.0 protocol. */ } pj_ssl_sock_proto; @@ -203,10 +327,10 @@ typedef struct pj_ssl_sock_info */ pj_ssl_sock_proto proto; /** - * Describes cipher suite being used, this can be known only when - * connection is established. + * Describes cipher suite being used, this will only be set when connection + * is established. */ - pj_str_t cipher; + pj_ssl_cipher cipher; /** * Describes local address. */ @@ -218,6 +342,7 @@ typedef struct pj_ssl_sock_info } pj_ssl_sock_info; + /** * Definition of secure socket creation parameters. */ @@ -316,10 +441,27 @@ typedef struct pj_ssl_sock_param pj_size_t send_buffer_size; /** - * Cipher list string. If empty, then default cipher list of the backend + * Specify buffer size for receiving encrypted (and perhaps compressed) + * data on underlying socket. This setting is unused on Symbian, since + * SSL/TLS Symbian backend, CSecureSocket, can use application buffer + * directly. + * + * Default value is 1500. + */ + pj_size_t read_buffer_size; + + /** + * 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. */ - pj_str_t ciphers; + unsigned ciphers_num; + + /** + * Ciphers and order preference. If empty, then default cipher list and + * its default order of the backend will be used. + */ + pj_ssl_cipher *ciphers; /** * Security negotiation timeout. If this is set to zero (both sec and diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c index bcb249fb..30603795 100644 --- a/pjlib/src/pj/ssl_sock_common.c +++ b/pjlib/src/pj/ssl_sock_common.c @@ -17,12 +17,96 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pj/ssl_sock.h> +#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[] = +{ + {TLS_NULL_WITH_NULL_NULL, "NULL"}, + + /* TLS/SSLv3 */ + {TLS_RSA_WITH_NULL_MD5, "TLS_RSA_WITH_NULL_MD5"}, + {TLS_RSA_WITH_NULL_SHA, "TLS_RSA_WITH_NULL_SHA"}, + {TLS_RSA_WITH_NULL_SHA256, "TLS_RSA_WITH_NULL_SHA256"}, + {TLS_RSA_WITH_RC4_128_MD5, "TLS_RSA_WITH_RC4_128_MD5"}, + {TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA"}, + {TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"}, + {TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA"}, + {TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA"}, + {TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS_RSA_WITH_AES_256_CBC_SHA256, "TLS_RSA_WITH_AES_256_CBC_SHA256"}, + {TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"}, + {TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"}, + {TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"}, + {TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"}, + {TLS_DH_DSS_WITH_AES_128_CBC_SHA, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"}, + {TLS_DH_RSA_WITH_AES_128_CBC_SHA, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"}, + {TLS_DHE_DSS_WITH_AES_128_CBC_SHA, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"}, + {TLS_DH_DSS_WITH_AES_256_CBC_SHA, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"}, + {TLS_DH_RSA_WITH_AES_256_CBC_SHA, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"}, + {TLS_DHE_DSS_WITH_AES_256_CBC_SHA, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"}, + {TLS_DH_DSS_WITH_AES_128_CBC_SHA256, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"}, + {TLS_DH_RSA_WITH_AES_128_CBC_SHA256, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"}, + {TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"}, + {TLS_DH_DSS_WITH_AES_256_CBC_SHA256, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"}, + {TLS_DH_RSA_WITH_AES_256_CBC_SHA256, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"}, + {TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"}, + {TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"}, + {TLS_DH_anon_WITH_RC4_128_MD5, "TLS_DH_anon_WITH_RC4_128_MD5"}, + {TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"}, + {TLS_DH_anon_WITH_AES_128_CBC_SHA, "TLS_DH_anon_WITH_AES_128_CBC_SHA"}, + {TLS_DH_anon_WITH_AES_256_CBC_SHA, "TLS_DH_anon_WITH_AES_256_CBC_SHA"}, + {TLS_DH_anon_WITH_AES_128_CBC_SHA256, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"}, + {TLS_DH_anon_WITH_AES_256_CBC_SHA256, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"}, + + /* TLS (deprecated) */ + {TLS_RSA_EXPORT_WITH_RC4_40_MD5, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"}, + {TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"}, + {TLS_RSA_WITH_IDEA_CBC_SHA, "TLS_RSA_WITH_IDEA_CBC_SHA"}, + {TLS_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_RSA_WITH_DES_CBC_SHA, "TLS_RSA_WITH_DES_CBC_SHA"}, + {TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_DH_DSS_WITH_DES_CBC_SHA, "TLS_DH_DSS_WITH_DES_CBC_SHA"}, + {TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_DH_RSA_WITH_DES_CBC_SHA, "TLS_DH_RSA_WITH_DES_CBC_SHA"}, + {TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_DHE_DSS_WITH_DES_CBC_SHA, "TLS_DHE_DSS_WITH_DES_CBC_SHA"}, + {TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_DHE_RSA_WITH_DES_CBC_SHA, "TLS_DHE_RSA_WITH_DES_CBC_SHA"}, + {TLS_DH_anon_EXPORT_WITH_RC4_40_MD5, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"}, + {TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"}, + {TLS_DH_anon_WITH_DES_CBC_SHA, "TLS_DH_anon_WITH_DES_CBC_SHA"}, + + /* SSLv3 */ + {SSL_FORTEZZA_KEA_WITH_NULL_SHA, "SSL_FORTEZZA_KEA_WITH_NULL_SHA"}, + {SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA,"SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA"}, + {SSL_FORTEZZA_KEA_WITH_RC4_128_SHA, "SSL_FORTEZZA_KEA_WITH_RC4_128_SHA"}, + + /* SSLv2 */ + {SSL_CK_RC4_128_WITH_MD5, "SSL_CK_RC4_128_WITH_MD5"}, + {SSL_CK_RC4_128_EXPORT40_WITH_MD5, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"}, + {SSL_CK_RC2_128_CBC_WITH_MD5, "SSL_CK_RC2_128_CBC_WITH_MD5"}, + {SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"}, + {SSL_CK_IDEA_128_CBC_WITH_MD5, "SSL_CK_IDEA_128_CBC_WITH_MD5"}, + {SSL_CK_DES_64_CBC_WITH_MD5, "SSL_CK_DES_64_CBC_WITH_MD5"}, + {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. */ -PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) +PJ_DEF(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) { pj_bzero(param, sizeof(*param)); @@ -32,8 +116,9 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) param->async_cnt = 1; param->concurrency = -1; param->whole_data = PJ_TRUE; -#if PJ_SYMBIAN param->send_buffer_size = 8192; +#if !defined(PJ_SYMBIAN) || PJ_SYMBIAN==0 + param->read_buffer_size = 1500; #endif /* Security config */ @@ -41,3 +126,15 @@ PJ_DECL(void) pj_ssl_sock_param_default(pj_ssl_sock_param *param) } +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; +} diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c new file mode 100644 index 00000000..9c6c80c8 --- /dev/null +++ b/pjlib/src/pj/ssl_sock_ossl.c @@ -0,0 +1,1780 @@ +/* $Id$ */ +/* + * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <pj/ssl_sock.h> +#include <pj/activesock.h> +#include <pj/compat/socket.h> +#include <pj/assert.h> +#include <pj/errno.h> +#include <pj/list.h> +#include <pj/lock.h> +#include <pj/log.h> +#include <pj/math.h> +#include <pj/pool.h> +#include <pj/string.h> + + +/* Only build when PJ_HAS_SSL_SOCK is enabled */ +//#if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK!=0 + +#define THIS_FILE "ssl_sock_ossl.c" + +/* + * Include OpenSSL headers + */ +#include <openssl/bio.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + + +#ifdef _MSC_VER +# ifdef _DEBUG +# pragma comment( lib, "libeay32MTd") +# pragma comment( lib, "ssleay32MTd") +#else +# pragma comment( lib, "libeay32MT") +# pragma comment( lib, "ssleay32MT") +# endif +#endif + + +/* + * SSL/TLS state enumeration. + */ +enum ssl_state { + SSL_STATE_NULL, + SSL_STATE_HANDSHAKING, + SSL_STATE_ESTABLISHED +}; + +/* + * Structure of SSL socket read buffer. + */ +typedef struct read_data_t +{ + void *data; + pj_size_t len; +} read_data_t; + +/* + * Get the offset of pointer to read-buffer of SSL socket from read-buffer + * of active socket. Note that both SSL socket and active socket employ + * different but correlated read-buffers (as much as async_cnt for each), + * and to make it easier/faster to find corresponding SSL socket's read-buffer + * from known active socket's read-buffer, the pointer of corresponding + * SSL socket's read-buffer is stored right after the end of active socket's + * read-buffer. + */ +#define OFFSET_OF_READ_DATA_PTR(ssock, asock_rbuf) \ + (read_data_t**) \ + ((pj_int8_t*)(asock_rbuf) + \ + ssock->param.read_buffer_size) + +/* + * Structure of SSL socket write buffer. + */ +typedef struct write_data_t { + pj_ioqueue_op_key_t key; + pj_size_t record_len; + pj_ioqueue_op_key_t *app_key; + pj_size_t plain_data_len; + pj_size_t data_len; + union { + char content[1]; + const char *ptr; + } data; + unsigned flags; +} write_data_t; + +/* + * Structure of SSL socket write state. + */ +typedef struct write_state_t { + char *buf; + pj_size_t max_len; + char *start; + pj_size_t len; + write_data_t *last_data; +} write_state_t; + +/* + * Structure of write data pending. + */ +typedef struct write_pending_t { + PJ_DECL_LIST_MEMBER(struct write_pending_t); + write_data_t data; +} write_pending_t; + +/* + * Secure socket structure definition. + */ +struct pj_ssl_sock_t +{ + pj_pool_t *pool; + pj_ssl_sock_t *parent; + pj_ssl_sock_param param; + pj_ssl_cert_t *cert; + + pj_bool_t is_server; + enum ssl_state ssl_state; + pj_ioqueue_op_key_t handshake_op_key; + + pj_sock_t sock; + pj_activesock_t *asock; + + pj_sockaddr local_addr; + pj_sockaddr rem_addr; + int addr_len; + + pj_bool_t read_started; + pj_size_t read_size; + pj_uint32_t read_flags; + void **asock_rbuf; + read_data_t *ssock_rbuf; + + write_state_t write_state; + write_pending_t write_pending; + write_pending_t write_pending_empty; + pj_lock_t *write_mutex; /* protect write BIO and write_state */ + + SSL_CTX *ossl_ctx; + SSL *ossl_ssl; + BIO *ossl_rbio; + BIO *ossl_wbio; +}; + + +/* + * Certificate/credential structure definition. + */ +struct pj_ssl_cert_t +{ + pj_str_t CA_file; + pj_str_t cert_file; + pj_str_t privkey_file; + pj_str_t privkey_pass; +}; + + +static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock); + +/* + ******************************************************************* + * Static/internal functions. + ******************************************************************* + */ + +/* ssl_report_error() */ +static void ssl_report_error(const char *sender, int level, + pj_status_t status, + const char *format, ...) +{ + va_list marker; + + va_start(marker, format); + +#if PJ_LOG_MAX_LEVEL > 0 + if (status != PJ_SUCCESS) { + char err_format[PJ_ERR_MSG_SIZE + 512]; + int len; + + len = pj_ansi_snprintf(err_format, sizeof(err_format), + "%s: ", format); + pj_strerror(status, err_format+len, sizeof(err_format)-len); + + pj_log(sender, level, err_format, marker); + + } else { + unsigned long ssl_err; + + ssl_err = ERR_get_error(); + + if (ssl_err == 0) { + pj_log(sender, level, format, marker); + } else { + char err_format[512]; + int len; + + len = pj_ansi_snprintf(err_format, sizeof(err_format), + "%s: ", format); + ERR_error_string(ssl_err, err_format+len); + + pj_log(sender, level, err_format, marker); + } + } +#endif + + va_end(marker); +} + + + +/* OpenSSL library initialization counter */ +static int openssl_init_count; + +/* OpenSSL available ciphers */ +static pj_ssl_cipher openssl_ciphers[64]; +static unsigned openssl_cipher_num; + + +/* Initialize OpenSSL */ +static pj_status_t init_openssl(void) +{ + if (++openssl_init_count != 1) + return PJ_SUCCESS; + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + /* Init available ciphers */ + if (openssl_cipher_num == 0) { + SSL_METHOD *meth = NULL; + SSL_CTX *ctx; + SSL *ssl; + STACK_OF(SSL_CIPHER) *sk_cipher; + unsigned i, n; + + meth = (SSL_METHOD*)SSLv23_server_method(); + if (!meth) + meth = (SSL_METHOD*)TLSv1_server_method(); + if (!meth) + meth = (SSL_METHOD*)SSLv3_server_method(); + if (!meth) + meth = (SSL_METHOD*)SSLv2_server_method(); + pj_assert(meth); + + ctx=SSL_CTX_new(meth); + SSL_CTX_set_cipher_list(ctx, "ALL"); + + ssl = SSL_new(ctx); + sk_cipher = SSL_get_ciphers(ssl); + + n = sk_SSL_CIPHER_num(sk_cipher); + if (n > PJ_ARRAY_SIZE(openssl_ciphers)) + n = PJ_ARRAY_SIZE(openssl_ciphers); + + 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)); + } + + SSL_free(ssl); + SSL_CTX_free(ctx); + + openssl_cipher_num = n; + } + + return PJ_SUCCESS; +} + + +/* Shutdown OpenSSL */ +static void shutdown_openssl(void) +{ + if (--openssl_init_count != 0) + return; +} + + +/* SSL password callback. */ +static int password_cb(char *buf, int num, int rwflag, void *user_data) +{ + pj_ssl_cert_t *cert = (pj_ssl_cert_t*) user_data; + + PJ_UNUSED_ARG(rwflag); + + if(num < cert->privkey_pass.slen) + return 0; + + pj_memcpy(buf, cert->privkey_pass.ptr, cert->privkey_pass.slen); + return cert->privkey_pass.slen; +} + + +/* Create and initialize new SSL context */ +static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx) +{ + SSL_METHOD *ssl_method; + SSL_CTX *ctx; + pj_ssl_cert_t *cert; + int mode, rc; + + pj_assert(ssock && p_ctx); + + cert = ssock->cert; + + /* Make sure OpenSSL library has been initialized */ + init_openssl(); + + /* Determine SSL method to use */ + switch (ssock->param.proto) { + case PJ_SSL_SOCK_PROTO_DEFAULT: + case PJ_SSL_SOCK_PROTO_TLS1: + ssl_method = (SSL_METHOD*)TLSv1_method(); + break; + case PJ_SSL_SOCK_PROTO_SSL2: + ssl_method = (SSL_METHOD*)SSLv2_method(); + break; + case PJ_SSL_SOCK_PROTO_SSL3: + ssl_method = (SSL_METHOD*)SSLv3_method(); + break; + case PJ_SSL_SOCK_PROTO_SSL23: + ssl_method = (SSL_METHOD*)SSLv23_method(); + break; + case PJ_SSL_SOCK_PROTO_DTLS1: + ssl_method = (SSL_METHOD*)DTLSv1_method(); + break; + default: + ssl_report_error(THIS_FILE, 4, PJ_EINVAL, + "Error creating SSL context"); + return PJ_EINVAL; + } + + /* Create SSL context for the listener */ + ctx = SSL_CTX_new(ssl_method); + if (ctx == NULL) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error creating SSL context"); + return PJ_EINVAL; + } + + /* Apply credentials */ + if (cert) { + /* Load CA list if one is specified. */ + if (cert->CA_file.slen) { + + rc = SSL_CTX_load_verify_locations(ctx, cert->CA_file.ptr, NULL); + + if (rc != 1) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error loading/verifying CA list file '%s'", + cert->CA_file.ptr); + SSL_CTX_free(ctx); + return PJ_EINVAL; + } + + PJ_LOG(5,(THIS_FILE, "CA file successfully loaded from '%s'", + cert->CA_file.ptr)); + } + + /* Set password callback */ + if (cert->privkey_pass.slen) { + SSL_CTX_set_default_passwd_cb(ctx, password_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, cert); + } + + + /* Load certificate if one is specified */ + if (cert->cert_file.slen) { + + /* Load certificate chain from file into ctx */ + rc = SSL_CTX_use_certificate_chain_file(ctx, cert->cert_file.ptr); + + if(rc != 1) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error loading certificate chain file '%s'", + cert->cert_file.ptr); + SSL_CTX_free(ctx); + return PJ_EINVAL; + } + + PJ_LOG(5,(THIS_FILE, "TLS certificate successfully loaded from '%s'", + cert->cert_file.ptr)); + } + + + /* Load private key if one is specified */ + if (cert->privkey_file.slen) { + /* Adds the first private key found in file to ctx */ + rc = SSL_CTX_use_PrivateKey_file(ctx, cert->privkey_file.ptr, + SSL_FILETYPE_PEM); + + if(rc != 1) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error adding private key from '%s'", + cert->privkey_file.ptr); + SSL_CTX_free(ctx); + return PJ_EINVAL; + } + + PJ_LOG(5,(THIS_FILE, "Private key successfully loaded from '%s'", + cert->privkey_file.ptr)); + } + } + + + /* SSL verification options */ + if (ssock->param.verify_peer) { + mode = SSL_VERIFY_PEER; + } else { + mode = SSL_VERIFY_NONE; + } + + if (ssock->is_server && ssock->param.require_client_cert) + mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + + SSL_CTX_set_verify(ctx, mode, NULL); + + PJ_LOG(5,(THIS_FILE, "Verification mode set to %d", mode)); + + *p_ctx = ctx; + + return PJ_SUCCESS; +} + + +/* Destroy SSL context */ +static void destroy_ssl_ctx(SSL_CTX *ctx) +{ + SSL_CTX_free(ctx); + + /* Potentially shutdown OpenSSL library if this is the last + * context exists. + */ + shutdown_openssl(); +} + + +/* Reset SSL socket state */ +static void reset_ssl_sock_state(pj_ssl_sock_t *ssock) +{ + ssock->ssl_state = SSL_STATE_NULL; + + if (ssock->ossl_ssl) { + SSL_shutdown(ssock->ossl_ssl); + SSL_free(ssock->ossl_ssl); /* this will also close BIOs */ + ssock->ossl_ssl = NULL; + } + if (ssock->ossl_ctx) { + destroy_ssl_ctx(ssock->ossl_ctx); + ssock->ossl_ctx = NULL; + } + if (ssock->asock) { + pj_activesock_close(ssock->asock); + ssock->asock = NULL; + ssock->sock = PJ_INVALID_SOCKET; + } +} + + +/* Generate cipher list with user preference order in OpenSSL format */ +static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock) +{ + char buf[1024]; + pj_str_t cipher_list; + STACK_OF(SSL_CIPHER) *sk_cipher; + unsigned i; + int j, ret; + + if (ssock->param.ciphers_num == 0) + return PJ_SUCCESS; + + pj_strset(&cipher_list, buf, 0); + + /* Set SSL with ALL available ciphers */ + SSL_set_cipher_list(ssock->ossl_ssl, "ALL"); + + /* Generate user specified cipher list in OpenSSL format */ + sk_cipher = SSL_get_ciphers(ssock->ossl_ssl); + for (i = 0; i < ssock->param.ciphers_num; ++i) { + for (j = 0; j < sk_SSL_CIPHER_num(sk_cipher); ++j) { + SSL_CIPHER *c; + c = sk_SSL_CIPHER_value(sk_cipher, j); + if (ssock->param.ciphers[i] == (pj_ssl_cipher) + ((pj_uint32_t)c->id & 0x00FFFFFF)) + { + const char *c_name; + + c_name = SSL_CIPHER_get_name(c); + + /* Check buffer size */ + if (cipher_list.slen + pj_ansi_strlen(c_name) + 2 > sizeof(buf)) { + pj_assert(!"Insufficient temporary buffer for cipher"); + return PJ_ETOOMANY; + } + + /* Add colon separator */ + if (cipher_list.slen) + pj_strcat2(&cipher_list, ":"); + + /* Add the cipher */ + pj_strcat2(&cipher_list, c_name); + break; + } + } + } + + /* Put NULL termination in the generated cipher list */ + cipher_list.ptr[cipher_list.slen] = '\0'; + + /* Finally, set chosen cipher list */ + ret = SSL_set_cipher_list(ssock->ossl_ssl, buf); + if (ret < 1) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error setting cipher list"); + return PJ_EINVAL; + } + + return PJ_SUCCESS; +} + + +/* When handshake completed: + * - notify application + * - if handshake failed, reset SSL state + * - return PJ_FALSE when SSL socket instance is destroyed by application. + */ +static pj_bool_t on_handshake_complete(pj_ssl_sock_t *ssock, + pj_status_t status) +{ + /* Accepting */ + if (ssock->is_server) { + if (status != PJ_SUCCESS) { + /* Handshake failed in accepting, destroy our self silently. */ + pj_ssl_sock_close(ssock); + return PJ_FALSE; + } + /* Notify application the newly accepted SSL socket */ + if (ssock->param.cb.on_accept_complete) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_accept_complete) + (ssock->parent, ssock, (pj_sockaddr_t*)&ssock->rem_addr, + pj_sockaddr_get_len((pj_sockaddr_t*)&ssock->rem_addr)); + if (ret == PJ_FALSE) + return PJ_FALSE; + } + } + + /* Connecting */ + else { + if (ssock->param.cb.on_connect_complete) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_connect_complete)(ssock, status); + if (ret == PJ_FALSE) + return PJ_FALSE; + } + if (status != PJ_SUCCESS) { + /* Reset SSL socket state */ + reset_ssl_sock_state(ssock); + } + } + + return PJ_TRUE; +} + +/* Flush write BIO to network socket. Note that any access to write BIO + * MUST be serialized, so mutex protection must cover any call to OpenSSL + * API (that possibly generate data for write BIO) along with the call to + * this function (flushing all data in write BIO generated by above + * OpenSSL API call). + */ +static pj_status_t flush_write_bio(pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + pj_size_t orig_len, + unsigned flags) +{ + char *data; + pj_ssize_t len; + + write_state_t *write_st = &ssock->write_state; + write_data_t *wdata; + pj_size_t avail_len, needed_len, skipped_len = 0; + pj_status_t status; + + /* Check if there is data in write BIO, flush it if any */ + if (!BIO_pending(ssock->ossl_wbio)) + return PJ_SUCCESS; + + /* Get data and its length */ + len = BIO_get_mem_data(ssock->ossl_wbio, &data); + if (len == 0) + return PJ_SUCCESS; + + /* Calculate buffer size needed, and align it to 8 */ + needed_len = len + sizeof(write_data_t); + needed_len = ((needed_len + 7) >> 3) << 3; + + /* Check buffer availability */ + avail_len = write_st->max_len - write_st->len; + if (avail_len < needed_len) + return PJ_ENOMEM; + + /* More buffer availability check, note that the write data must be in + * a contigue buffer. + */ + if (write_st->len == 0) { + + write_st->start = write_st->buf; + wdata = (write_data_t*)write_st->start; + + } else { + + char *reg1, *reg2; + pj_size_t reg1_len, reg2_len; + + /* Unused slots may be wrapped/splitted into two regions, so let's + * analyze them if any region can hold the write data. + */ + reg1 = write_st->start + write_st->len; + if (reg1 >= write_st->buf + write_st->max_len) + reg1 -= write_st->max_len; + reg1_len = write_st->max_len - write_st->len; + if (reg1 + reg1_len > write_st->buf + write_st->max_len) { + reg1_len = write_st->buf + write_st->max_len - reg1; + reg2 = write_st->buf; + reg2_len = write_st->start - write_st->buf; + } else { + reg2 = NULL; + reg2_len = 0; + } + avail_len = PJ_MAX(reg1_len, reg2_len); + if (avail_len < needed_len) + return PJ_ENOMEM; + + /* Get write data pointer and update buffer length */ + if (reg1_len >= needed_len) { + wdata = (write_data_t*)reg1; + } else { + wdata = (write_data_t*)reg2; + /* Unused slot in region 1 is skipped as current write data + * doesn't fit it. + */ + skipped_len = reg1_len; + } + } + + /* Copy the data and set its properties into the buffer */ + pj_bzero(wdata, sizeof(write_data_t)); + wdata->app_key = send_key; + wdata->record_len = needed_len; + wdata->data_len = len; + wdata->plain_data_len = orig_len; + wdata->flags = flags; + pj_memcpy(&wdata->data, data, len); + + /* Send it */ + if (ssock->param.sock_type == pj_SOCK_STREAM()) { + status = pj_activesock_send(ssock->asock, &wdata->key, + wdata->data.content, &len, + flags); + } else { + status = pj_activesock_sendto(ssock->asock, &wdata->key, + wdata->data.content, &len, + flags, + (pj_sockaddr_t*)&ssock->rem_addr, + ssock->addr_len); + } + + /* Oh no, EWOULDBLOCK! */ + if (status == PJ_STATUS_FROM_OS(OSERR_EWOULDBLOCK)) { + /* Just return PJ_SUCCESS here, the pending data will be sent in next + * call of this function since the data is still stored in write BIO. + */ + return PJ_SUCCESS; + } + + /* Reset write BIO after flushed */ + BIO_reset(ssock->ossl_wbio); + + if (status == PJ_EPENDING) { + /* Update write state */ + pj_assert(skipped_len==0 || write_st->last_data); + write_st->len += needed_len + skipped_len; + if (write_st->last_data) + write_st->last_data->record_len += skipped_len; + write_st->last_data = wdata; + } + + return status; +} + +static pj_status_t do_handshake(pj_ssl_sock_t *ssock) +{ + pj_status_t status; + int err; + + pj_lock_acquire(ssock->write_mutex); + + /* Perform SSL handshake */ + err = SSL_do_handshake(ssock->ossl_ssl); + if (err < 0) { + err = SSL_get_error(ssock->ossl_ssl, err); + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) + { + /* Handshake fails */ + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "SSL_do_handshake()"); + pj_lock_release(ssock->write_mutex); + return PJ_ECANCELLED; + } + } + + /* SSL_do_handshake() may put some pending data into SSL write BIO, + * flush it if any. + */ + status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); + if (status != PJ_SUCCESS && status != PJ_EPENDING) { + pj_lock_release(ssock->write_mutex); + return status; + } + + pj_lock_release(ssock->write_mutex); + + /* Check if handshake has been completed */ + if (SSL_is_init_finished(ssock->ossl_ssl)) { + ssock->ssl_state = SSL_STATE_ESTABLISHED; + return PJ_SUCCESS; + } + + return PJ_EPENDING; +} + + +/* + ******************************************************************* + * Active socket callbacks. + ******************************************************************* + */ + +static pj_bool_t asock_on_data_read (pj_activesock_t *asock, + void *data, + pj_size_t size, + pj_status_t status, + pj_size_t *remainder) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + pj_size_t nwritten; + + /* Socket error or closed */ + if (data == NULL || size < 0) + goto on_error; + + /* Consume the whole data */ + nwritten = BIO_write(ssock->ossl_rbio, data, size); + if (nwritten < size) { + status = PJ_ENOMEM; + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "BIO_write()"); + goto on_error; + } + + /* Check if SSL handshake hasn't finished yet */ + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { + pj_bool_t ret = PJ_TRUE; + + if (status == PJ_SUCCESS) + status = do_handshake(ssock); + + /* Not pending is either success or failed */ + if (status != PJ_EPENDING) + ret = on_handshake_complete(ssock, status); + + return ret; + } + + /* See if there is any decrypted data for the application */ + if (ssock->read_started) { + do { + read_data_t *buf = *(OFFSET_OF_READ_DATA_PTR(ssock, data)); + void *data_ = (pj_int8_t*)buf->data + buf->len; + int size_ = ssock->read_size - buf->len; + + /* SSL_read() may write some data to BIO write when re-negotiation + * is on progress, so let's protect it with write mutex. + */ + pj_lock_acquire(ssock->write_mutex); + + size_ = SSL_read(ssock->ossl_ssl, data_, size_); + if (size_ > 0) { + pj_lock_release(ssock->write_mutex); + if (ssock->param.cb.on_data_read) { + pj_bool_t ret; + pj_size_t remainder_ = 0; + + buf->len += size_; + + ret = (*ssock->param.cb.on_data_read)(ssock, buf->data, + buf->len, status, + &remainder_); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + + /* Application may have left some data to be consumed + * later. + */ + buf->len = remainder_; + } + } else { + int err = SSL_get_error(ssock->ossl_ssl, size); + + /* SSL might just return SSL_ERROR_WANT_READ in + * re-negotiation. + */ + if (err != SSL_ERROR_NONE && err != SSL_ERROR_WANT_READ) + { + /* Reset SSL socket state, then return PJ_FALSE */ + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_read()"); + pj_lock_release(ssock->write_mutex); + reset_ssl_sock_state(ssock); + return PJ_FALSE; + } + + /* SSL may write something in case of re-negotiation */ + status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); + pj_lock_release(ssock->write_mutex); + if (status != PJ_SUCCESS && status != PJ_EPENDING) + goto on_error; + + /* If re-negotiation has been completed, start flushing + * delayed send. + */ + if (!SSL_renegotiate_pending(ssock->ossl_ssl)) { + status = flush_delayed_send(ssock); + if (status != PJ_SUCCESS && status != PJ_EPENDING) + goto on_error; + } + + break; + } + } while (1); + } + + return PJ_TRUE; + +on_error: + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) + return on_handshake_complete(ssock, status); + + if (ssock->read_started && ssock->param.cb.on_data_read) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_data_read)(ssock, NULL, 0, status, + remainder); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + } + + reset_ssl_sock_state(ssock); + return PJ_FALSE; +} + + +static pj_bool_t asock_on_data_sent (pj_activesock_t *asock, + pj_ioqueue_op_key_t *send_key, + pj_ssize_t sent) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(sent); + + if (ssock->ssl_state == SSL_STATE_HANDSHAKING) { + /* Initial handshaking */ + pj_status_t status; + + status = do_handshake(ssock); + /* Not pending is either success or failed */ + if (status != PJ_EPENDING) + return on_handshake_complete(ssock, status); + + } else if (send_key != &ssock->handshake_op_key) { + /* Some data has been sent, notify application */ + write_data_t *wdata = (write_data_t*)send_key; + if (ssock->param.cb.on_data_sent) { + pj_bool_t ret; + ret = (*ssock->param.cb.on_data_sent)(ssock, wdata->app_key, + wdata->plain_data_len); + if (!ret) { + /* We've been destroyed */ + return PJ_FALSE; + } + } + + /* Update write buffer state */ + pj_lock_acquire(ssock->write_mutex); + ssock->write_state.start += wdata->record_len; + ssock->write_state.len -= wdata->record_len; + if (ssock->write_state.last_data == wdata) { + pj_assert(ssock->write_state.len == 0); + ssock->write_state.last_data = NULL; + } + pj_lock_release(ssock->write_mutex); + + } else { + /* SSL re-negotiation is on-progress, just do nothing */ + } + + return PJ_TRUE; +} + + +static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock, + pj_sock_t newsock, + const pj_sockaddr_t *src_addr, + int src_addr_len) +{ + pj_ssl_sock_t *ssock_parent = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + pj_ssl_sock_t *ssock; + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + unsigned i; + pj_status_t status; + + PJ_UNUSED_ARG(src_addr_len); + + /* Create new SSL socket instance */ + status = pj_ssl_sock_create(ssock_parent->pool, &ssock_parent->param, + &ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Update new SSL socket attributes */ + ssock->sock = newsock; + ssock->parent = ssock_parent; + ssock->is_server = PJ_TRUE; + if (ssock_parent->cert) { + status = pj_ssl_sock_set_certificate(ssock, ssock->pool, + ssock_parent->cert); + if (status != PJ_SUCCESS) + goto on_return; + } + + /* Update local address */ + ssock->addr_len = src_addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + goto on_return; + + /* Set remote address */ + pj_sockaddr_cp(&ssock->rem_addr, src_addr); + + /* Create SSL context */ + status = create_ssl_ctx(ssock, &ssock->ossl_ctx); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create SSL instance */ + ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); + if (ssock->ossl_ssl == NULL) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error creating SSL connection object"); + status = PJ_EINVAL; + goto on_return; + } + + /* Set cipher list */ + status = set_cipher_list(ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Setup SSL BIOs */ + ssock->ossl_rbio = BIO_new(BIO_s_mem()); + ssock->ossl_wbio = BIO_new(BIO_s_mem()); + BIO_set_close(ssock->ossl_rbio, BIO_CLOSE); + BIO_set_close(ssock->ossl_wbio, BIO_CLOSE); + SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio); + + /* Prepare read buffer */ + ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, + ssock->param.async_cnt, + sizeof(void*)); + for (i = 0; i<ssock->param.async_cnt; ++i) { + ssock->asock_rbuf[i] = (void*) pj_pool_alloc( + ssock->pool, + ssock->param.read_buffer_size + + sizeof(read_data_t*)); + } + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_data_read = asock_on_data_read; + asock_cb.on_data_sent = asock_on_data_sent; + + status = pj_activesock_create(ssock->pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_return; + + /* Start read */ + status = pj_activesock_start_read2(ssock->asock, ssock->pool, + ssock->param.read_buffer_size, + ssock->asock_rbuf, + PJ_IOQUEUE_ALWAYS_ASYNC); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare write/send state */ + pj_assert(ssock->write_state.max_len == 0); + ssock->write_state.buf = (char*) + pj_pool_alloc(ssock->pool, + ssock->param.send_buffer_size); + ssock->write_state.max_len = ssock->param.send_buffer_size; + ssock->write_state.start = ssock->write_state.buf; + ssock->write_state.len = 0; + + /* Start SSL handshake */ + ssock->ssl_state = SSL_STATE_HANDSHAKING; + SSL_set_accept_state(ssock->ossl_ssl); + status = do_handshake(ssock); + if (status != PJ_EPENDING) + goto on_return; + + return PJ_TRUE; + +on_return: + return on_handshake_complete(ssock, status); +} + + +static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock, + pj_status_t status) +{ + pj_ssl_sock_t *ssock = (pj_ssl_sock_t*) + pj_activesock_get_user_data(asock); + unsigned i; + + if (status != PJ_SUCCESS) + goto on_return; + + /* Update local address */ + ssock->addr_len = sizeof(pj_sockaddr); + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create SSL context */ + status = create_ssl_ctx(ssock, &ssock->ossl_ctx); + if (status != PJ_SUCCESS) + goto on_return; + + /* Create SSL instance */ + ssock->ossl_ssl = SSL_new(ssock->ossl_ctx); + if (ssock->ossl_ssl == NULL) { + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, + "Error creating SSL connection object"); + status = PJ_EINVAL; + goto on_return; + } + + /* Set cipher list */ + status = set_cipher_list(ssock); + if (status != PJ_SUCCESS) + goto on_return; + + /* Setup SSL BIOs */ + ssock->ossl_rbio = BIO_new(BIO_s_mem()); + ssock->ossl_wbio = BIO_new(BIO_s_mem()); + BIO_set_close(ssock->ossl_rbio, BIO_CLOSE); + BIO_set_close(ssock->ossl_wbio, BIO_CLOSE); + SSL_set_bio(ssock->ossl_ssl, ssock->ossl_rbio, ssock->ossl_wbio); + + /* Prepare read buffer */ + ssock->asock_rbuf = (void**)pj_pool_calloc(ssock->pool, + ssock->param.async_cnt, + sizeof(void*)); + for (i = 0; i<ssock->param.async_cnt; ++i) { + ssock->asock_rbuf[i] = (void*) pj_pool_alloc( + ssock->pool, + ssock->param.read_buffer_size + + sizeof(read_data_t*)); + } + + /* Start read */ + status = pj_activesock_start_read2(ssock->asock, ssock->pool, + ssock->param.read_buffer_size, + ssock->asock_rbuf, + PJ_IOQUEUE_ALWAYS_ASYNC); + if (status != PJ_SUCCESS) + goto on_return; + + /* Prepare write/send state */ + pj_assert(ssock->write_state.max_len == 0); + ssock->write_state.buf = (char*) + pj_pool_alloc(ssock->pool, + ssock->param.send_buffer_size); + ssock->write_state.max_len = ssock->param.send_buffer_size; + ssock->write_state.start = ssock->write_state.buf; + ssock->write_state.len = 0; + + /* Start SSL handshake */ + ssock->ssl_state = SSL_STATE_HANDSHAKING; + SSL_set_connect_state(ssock->ossl_ssl); + status = do_handshake(ssock); + if (status != PJ_EPENDING) + goto on_return; + + return PJ_TRUE; + +on_return: + return on_handshake_complete(ssock, status); +} + + + +/* + ******************************************************************* + * API + ******************************************************************* + */ + +/* Load credentials from files. */ +PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files (pj_pool_t *pool, + const pj_str_t *CA_file, + const pj_str_t *cert_file, + const pj_str_t *privkey_file, + const pj_str_t *privkey_pass, + pj_ssl_cert_t **p_cert) +{ + pj_ssl_cert_t *cert; + + PJ_ASSERT_RETURN(pool && CA_file && cert_file && privkey_file, PJ_EINVAL); + + cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_strdup_with_null(pool, &cert->CA_file, CA_file); + pj_strdup_with_null(pool, &cert->cert_file, cert_file); + pj_strdup_with_null(pool, &cert->privkey_file, privkey_file); + pj_strdup_with_null(pool, &cert->privkey_pass, privkey_pass); + + *p_cert = cert; + + return PJ_SUCCESS; +} + + +/* Set SSL socket credentials. */ +PJ_DECL(pj_status_t) pj_ssl_sock_set_certificate( + pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_ssl_cert_t *cert) +{ + PJ_ASSERT_RETURN(ssock && pool && cert, PJ_EINVAL); + + ssock->cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t); + pj_strdup_with_null(pool, &ssock->cert->CA_file, &cert->CA_file); + pj_strdup_with_null(pool, &ssock->cert->cert_file, &cert->cert_file); + pj_strdup_with_null(pool, &ssock->cert->privkey_file, &cert->privkey_file); + pj_strdup_with_null(pool, &ssock->cert->privkey_pass, &cert->privkey_pass); + + return PJ_SUCCESS; +} + + +/* Get available ciphers. */ +PJ_DEF(pj_status_t) pj_ssl_cipher_get_availables(pj_ssl_cipher ciphers[], + unsigned *cipher_num) +{ + unsigned i; + + PJ_ASSERT_RETURN(ciphers && cipher_num, PJ_EINVAL); + + if (openssl_cipher_num == 0) { + init_openssl(); + shutdown_openssl(); + } + + if (openssl_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]; + + return PJ_SUCCESS; +} + + +/* + * Create SSL socket instance. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, + const pj_ssl_sock_param *param, + pj_ssl_sock_t **p_ssock) +{ + pj_ssl_sock_t *ssock; + pj_status_t status; + + PJ_ASSERT_RETURN(pool && param && p_ssock, PJ_EINVAL); + PJ_ASSERT_RETURN(param->sock_type == pj_SOCK_STREAM(), PJ_ENOTSUP); + + pool = pj_pool_create(pool->factory, "ssl%p", 512, 512, NULL); + + /* Create secure socket */ + ssock = PJ_POOL_ZALLOC_T(pool, pj_ssl_sock_t); + ssock->pool = pool; + ssock->sock = PJ_INVALID_SOCKET; + ssock->ssl_state = SSL_STATE_NULL; + pj_list_init(&ssock->write_pending); + pj_list_init(&ssock->write_pending_empty); + + /* Create secure socket mutex */ + status = pj_lock_create_recursive_mutex(pool, pool->obj_name, + &ssock->write_mutex); + if (status != PJ_SUCCESS) + return status; + + /* Init secure socket param */ + ssock->param = *param; + ssock->param.read_buffer_size = ((ssock->param.read_buffer_size+7)>>3)<<3; + if (param->ciphers_num > 0) { + unsigned i; + ssock->param.ciphers = (pj_ssl_cipher*) + pj_pool_calloc(pool, param->ciphers_num, + sizeof(pj_ssl_cipher)); + for (i = 0; i < param->ciphers_num; ++i) + ssock->param.ciphers[i] = param->ciphers[i]; + } + pj_strdup_with_null(pool, &ssock->param.servername, + ¶m->servername); + + /* Finally */ + *p_ssock = ssock; + + return PJ_SUCCESS; +} + + +/* + * Close the secure socket. This will unregister the socket from the + * ioqueue and ultimately close the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_close(pj_ssl_sock_t *ssock) +{ + pj_pool_t *pool; + + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + reset_ssl_sock_state(ssock); + pj_lock_destroy(ssock->write_mutex); + + pool = ssock->pool; + ssock->pool = NULL; + if (pool) + pj_pool_release(pool); + + return PJ_SUCCESS; +} + + +/* + * Associate arbitrary data with the secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_set_user_data(pj_ssl_sock_t *ssock, + void *user_data) +{ + PJ_ASSERT_RETURN(ssock, PJ_EINVAL); + + ssock->param.user_data = user_data; + return PJ_SUCCESS; +} + + +/* + * Retrieve the user data previously associated with this secure + * socket. + */ +PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) +{ + PJ_ASSERT_RETURN(ssock, NULL); + + return ssock->param.user_data; +} + + +/* + * Retrieve the local address and port used by specified SSL socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, + pj_ssl_sock_info *info) +{ + pj_bzero(info, sizeof(*info)); + + /* Established flag */ + info->established = (ssock->ssl_state == SSL_STATE_ESTABLISHED); + + /* Protocol */ + info->proto = ssock->param.proto; + + /* Local address */ + pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); + + if (info->established) { + /* Current cipher */ + const SSL_CIPHER *cipher; + + cipher = SSL_get_current_cipher(ssock->ossl_ssl); + info->cipher = (cipher->id & 0x00FFFFFF); + + /* Remote address */ + pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr); + } + + return PJ_SUCCESS; +} + + +/* + * Starts read operation on this secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + void **readbuf; + unsigned i; + + PJ_ASSERT_RETURN(ssock && pool && buff_size, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + readbuf = (void**) pj_pool_calloc(pool, ssock->param.async_cnt, + sizeof(void*)); + + for (i=0; i<ssock->param.async_cnt; ++i) { + readbuf[i] = pj_pool_alloc(pool, buff_size); + } + + return pj_ssl_sock_start_read2(ssock, pool, buff_size, + readbuf, flags); +} + + +/* + * Same as #pj_ssl_sock_start_read(), except that the application + * supplies the buffers for the read operation so that the acive socket + * does not have to allocate the buffers. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_read2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + unsigned i; + + PJ_ASSERT_RETURN(ssock && pool && buff_size && readbuf, PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + /* Create SSL socket read buffer */ + ssock->ssock_rbuf = (read_data_t*)pj_pool_calloc(pool, + ssock->param.async_cnt, + sizeof(read_data_t)); + + /* Store SSL socket read buffer pointer in the activesock read buffer */ + for (i=0; i<ssock->param.async_cnt; ++i) { + read_data_t **p_ssock_rbuf = + OFFSET_OF_READ_DATA_PTR(ssock, ssock->asock_rbuf[i]); + + ssock->ssock_rbuf[i].data = readbuf[i]; + ssock->ssock_rbuf[i].len = 0; + + *p_ssock_rbuf = &ssock->ssock_rbuf[i]; + } + + ssock->read_size = buff_size; + ssock->read_started = PJ_TRUE; + ssock->read_flags = flags; + + return PJ_SUCCESS; +} + + +/* + * Same as pj_ssl_sock_start_read(), except that this function is used + * only for datagram sockets, and it will trigger \a on_data_recvfrom() + * callback instead. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(flags); + + return PJ_ENOTSUP; +} + + +/* + * Same as #pj_ssl_sock_start_recvfrom() except that the recvfrom() + * operation takes the buffer from the argument rather than creating + * new ones. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_recvfrom2 (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + unsigned buff_size, + void *readbuf[], + pj_uint32_t flags) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(buff_size); + PJ_UNUSED_ARG(readbuf); + PJ_UNUSED_ARG(flags); + + return PJ_ENOTSUP; +} + +/* Write plain data to SSL and flush write BIO. Note that accessing + * write BIO must be serialized, so a call to this function must be + * protected by write mutex of SSL socket. + */ +static pj_status_t ssl_write(pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t size, + unsigned flags) +{ + pj_status_t status; + int nwritten; + + /* Write the plain data to SSL, after SSL encrypts it, write BIO will + * contain the secured data to be sent via socket. Note that re- + * negotitation may be on progress, so sending data should be delayed + * until re-negotiation is completed. + */ + nwritten = SSL_write(ssock->ossl_ssl, data, size); + + if (nwritten == size) { + /* All data written, flush write BIO to network socket */ + status = flush_write_bio(ssock, send_key, size, flags); + } else if (nwritten <= 0) { + /* SSL failed to process the data, it may just that re-negotiation + * is on progress. + */ + int err; + err = SSL_get_error(ssock->ossl_ssl, nwritten); + if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_NONE) { + /* Re-negotiation is on progress, flush re-negotiation data */ + status = flush_write_bio(ssock, &ssock->handshake_op_key, 0, 0); + if (status == PJ_SUCCESS || status == PJ_EPENDING) + status = PJ_EBUSY; + } else { + /* Some problem occured */ + ssl_report_error(THIS_FILE, 4, PJ_SUCCESS, "SSL_write()"); + status = PJ_ECANCELLED; + } + } else { + /* nwritten < *size, shouldn't happen, unless write BIO cannot hold + * the whole secured data, perhaps because of insufficient memory. + */ + status = PJ_ENOMEM; + } + + return status; +} + +/* Flush delayed data sending in the write pending list. Note that accessing + * write pending list must be serialized, so a call to this function must be + * protected by write mutex of SSL socket. + */ +static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock) +{ + while (!pj_list_empty(&ssock->write_pending)) { + write_pending_t *wp; + pj_status_t status; + + wp = ssock->write_pending.next; + + status = ssl_write(ssock, &wp->data.key, wp->data.data.ptr, + wp->data.plain_data_len, wp->data.flags); + if (status != PJ_SUCCESS) + return status; + + pj_list_erase(wp); + pj_list_push_back(&ssock->write_pending_empty, wp); + } + + return PJ_SUCCESS; +} + +/* Sending is delayed, push back the sending data into pending list. Note that + * accessing write pending list must be serialized, so a call to this function + * must be protected by write mutex of SSL socket. + */ +static pj_status_t delay_send (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t size, + unsigned flags) +{ + write_pending_t *wp; + + /* Init write pending instance */ + if (!pj_list_empty(&ssock->write_pending_empty)) { + wp = ssock->write_pending_empty.next; + pj_list_erase(wp); + } else { + wp = PJ_POOL_ZALLOC_T(ssock->pool, write_pending_t); + } + + wp->data.app_key = send_key; + wp->data.plain_data_len = size; + wp->data.data.ptr = data; + wp->data.flags = flags; + + pj_list_push_back(&ssock->write_pending, wp); + + /* Must return PJ_EPENDING */ + return PJ_EPENDING; +} + +/** + * Send data using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_send (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && data && size && (*size>0), PJ_EINVAL); + PJ_ASSERT_RETURN(ssock->ssl_state==SSL_STATE_ESTABLISHED, PJ_EINVALIDOP); + + pj_lock_acquire(ssock->write_mutex); + + /* Flush delayed send first. Sending data might be delayed when + * re-negotiation is on-progress. + */ + status = flush_delayed_send(ssock); + if (status == PJ_EBUSY) { + /* Re-negotiation is on progress, delay sending */ + status = delay_send(ssock, send_key, data, *size, flags); + goto on_return; + } else if (status != PJ_SUCCESS) { + goto on_return; + } + + /* Write data to SSL */ + status = ssl_write(ssock, send_key, data, *size, flags); + if (status == PJ_EBUSY) { + /* Re-negotiation is on progress, delay sending */ + status = delay_send(ssock, send_key, data, *size, flags); + } + +on_return: + pj_lock_release(ssock->write_mutex); + return status; +} + + +/** + * Send datagram using the socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_sendto (pj_ssl_sock_t *ssock, + pj_ioqueue_op_key_t *send_key, + const void *data, + pj_ssize_t *size, + unsigned flags, + const pj_sockaddr_t *addr, + int addr_len) +{ + PJ_UNUSED_ARG(ssock); + PJ_UNUSED_ARG(send_key); + PJ_UNUSED_ARG(data); + PJ_UNUSED_ARG(size); + PJ_UNUSED_ARG(flags); + PJ_UNUSED_ARG(addr); + PJ_UNUSED_ARG(addr_len); + + return PJ_ENOTSUP; +} + + +/** + * Starts asynchronous socket accept() operations on this secure socket. + */ +PJ_DEF(pj_status_t) pj_ssl_sock_start_accept (pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *localaddr, + int addr_len) +{ + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && pool && localaddr && addr_len, PJ_EINVAL); + + /* Create socket */ + status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, + &ssock->sock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Bind socket */ + status = pj_sock_bind(ssock->sock, localaddr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + /* Start listening to the address */ + status = pj_sock_listen(ssock->sock, PJ_SOMAXCONN); + if (status != PJ_SUCCESS) + goto on_error; + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_accept_complete = asock_on_accept_complete; + + status = pj_activesock_create(pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_error; + + /* Start accepting */ + status = pj_activesock_start_accept(ssock->asock, pool); + if (status != PJ_SUCCESS) + goto on_error; + + /* Update local address */ + ssock->addr_len = addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + pj_sockaddr_cp(&ssock->local_addr, localaddr); + + ssock->is_server = PJ_TRUE; + + return PJ_SUCCESS; + +on_error: + reset_ssl_sock_state(ssock); + return status; +} + + +/** + * Starts asynchronous socket connect() operation. + */ +PJ_DECL(pj_status_t) pj_ssl_sock_start_connect(pj_ssl_sock_t *ssock, + pj_pool_t *pool, + const pj_sockaddr_t *localaddr, + const pj_sockaddr_t *remaddr, + int addr_len) +{ + pj_activesock_cb asock_cb; + pj_activesock_cfg asock_cfg; + pj_status_t status; + + PJ_ASSERT_RETURN(ssock && pool && localaddr && remaddr && addr_len, + PJ_EINVAL); + + /* Create socket */ + status = pj_sock_socket(ssock->param.sock_af, ssock->param.sock_type, 0, + &ssock->sock); + if (status != PJ_SUCCESS) + goto on_error; + + /* Bind socket */ + status = pj_sock_bind(ssock->sock, localaddr, addr_len); + if (status != PJ_SUCCESS) + goto on_error; + + /* Create active socket */ + pj_activesock_cfg_default(&asock_cfg); + asock_cfg.async_cnt = ssock->param.async_cnt; + asock_cfg.concurrency = ssock->param.concurrency; + asock_cfg.whole_data = PJ_TRUE; + + pj_bzero(&asock_cb, sizeof(asock_cb)); + asock_cb.on_connect_complete = asock_on_connect_complete; + asock_cb.on_data_read = asock_on_data_read; + asock_cb.on_data_sent = asock_on_data_sent; + + status = pj_activesock_create(pool, + ssock->sock, + ssock->param.sock_type, + &asock_cfg, + ssock->param.ioqueue, + &asock_cb, + ssock, + &ssock->asock); + + if (status != PJ_SUCCESS) + goto on_error; + + status = pj_activesock_start_connect(ssock->asock, pool, remaddr, + addr_len); + + if (status == PJ_SUCCESS) + asock_on_connect_complete(ssock->asock, PJ_SUCCESS); + else if (status != PJ_EPENDING) + goto on_error; + + /* Update local address */ + ssock->addr_len = addr_len; + status = pj_sock_getsockname(ssock->sock, &ssock->local_addr, + &ssock->addr_len); + if (status != PJ_SUCCESS) + pj_sockaddr_cp(&ssock->local_addr, localaddr); + + /* Set remote address */ + pj_sockaddr_cp(&ssock->rem_addr, remaddr); + + /* Update SSL state */ + ssock->is_server = PJ_FALSE; + + return PJ_EPENDING; + +on_error: + reset_ssl_sock_state(ssock); + return status; +} + + +//#endif /* PJ_HAS_SSL_SOCK */ + diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp index 41d3e159..06165f37 100644 --- a/pjlib/src/pj/ssl_sock_symbian.cpp +++ b/pjlib/src/pj/ssl_sock_symbian.cpp @@ -20,6 +20,7 @@ #include <pj/compat/socket.h> #include <pj/assert.h> #include <pj/errno.h> +#include <pj/math.h> #include <pj/pool.h> #include <pj/sock.h> #include <pj/string.h> @@ -413,12 +414,57 @@ struct pj_ssl_sock_t pj_ssl_sock_proto proto; pj_time_val timeout; - pj_str_t ciphers; + unsigned ciphers_num; + pj_ssl_cipher *ciphers; pj_str_t servername; }; /* + * 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); + + if (ciphers_num_ == 0) { + RSocket sock; + CSecureSocket *secure_sock; + TPtrC16 proto(_L16("TLS1.0")); + + secure_sock = CSecureSocket::NewL(sock, proto); + if (secure_sock) { + TBuf8<128> ciphers_buf(0); + secure_sock->AvailableCipherSuites(ciphers_buf); + + 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]; + } + + delete secure_sock; + } + + if (ciphers_num_ == 0) { + return PJ_ENOTFOUND; + } + + *cipher_num = PJ_MIN(*cipher_num, ciphers_num_); + for (i = 0; i < *cipher_num; ++i) + ciphers[i] = ciphers_[i]; + + return PJ_SUCCESS; +} + +/* * Create SSL socket instance. */ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, @@ -444,7 +490,15 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, ssock->sock_type = param->sock_type; ssock->cb = param->cb; ssock->user_data = param->user_data; - pj_strdup_with_null(pool, &ssock->ciphers, ¶m->ciphers); + 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]; + } pj_strdup_with_null(pool, &ssock->servername, ¶m->servername); /* Finally */ @@ -453,6 +507,23 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool, return PJ_SUCCESS; } + +PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool, + const pj_str_t *CA_file, + const pj_str_t *cert_file, + const pj_str_t *privkey_file, + const pj_str_t *privkey_pass, + pj_ssl_cert_t **p_cert) +{ + PJ_UNUSED_ARG(pool); + PJ_UNUSED_ARG(CA_file); + PJ_UNUSED_ARG(cert_file); + PJ_UNUSED_ARG(privkey_file); + PJ_UNUSED_ARG(privkey_pass); + PJ_UNUSED_ARG(p_cert); + return PJ_ENOTSUP; +} + /* * Set SSL socket credential. */ @@ -521,36 +592,6 @@ PJ_DEF(void*) pj_ssl_sock_get_user_data(pj_ssl_sock_t *ssock) PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, pj_ssl_sock_info *info) { - const char *cipher_names[0x1B] = { - "TLS_RSA_WITH_NULL_MD5", - "TLS_RSA_WITH_NULL_SHA", - "TLS_RSA_EXPORT_WITH_RC4_40_MD5", - "TLS_RSA_WITH_RC4_128_MD5", - "TLS_RSA_WITH_RC4_128_SHA", - "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", - "TLS_RSA_WITH_IDEA_CBC_SHA", - "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_RSA_WITH_DES_CBC_SHA", - "TLS_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_DSS_WITH_DES_CBC_SHA", - "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_RSA_WITH_DES_CBC_SHA", - "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DHE_DSS_WITH_DES_CBC_SHA", - "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", - "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DHE_RSA_WITH_DES_CBC_SHA", - "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", - "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", - "TLS_DH_anon_WITH_RC4_128_MD5", - "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", - "TLS_DH_anon_WITH_DES_CBC_SHA", - "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA" - }; - PJ_ASSERT_RETURN(ssock && info, PJ_EINVAL); pj_bzero(info, sizeof(*info)); @@ -570,19 +611,16 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock, pj_sockaddr_cp(&info->local_addr, &ssock->local_addr); } - /* Remote address */ - pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, - (pj_sockaddr_t*)&ssock->rem_addr); - - /* Cipher suite */ if (info->established) { - TBuf8<8> cipher; + /* Cipher suite */ + TBuf8<4> cipher; if (ssock->sock->GetCipher(cipher) == KErrNone) { - TLex8 lex(cipher); - TUint cipher_code = cipher[1]; - if (cipher_code>=1 && cipher_code<=0x1B) - info->cipher = pj_str((char*)cipher_names[cipher_code-1]); + info->cipher = (pj_ssl_cipher)cipher[1]; } + + /* Remote address */ + pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr, + (pj_sockaddr_t*)&ssock->rem_addr); } /* Protocol */ |