summaryrefslogtreecommitdiff
path: root/pjlib/src
diff options
context:
space:
mode:
Diffstat (limited to 'pjlib/src')
-rw-r--r--pjlib/src/pj/ssl_sock_common.c95
-rw-r--r--pjlib/src/pj/ssl_sock_dump.c147
-rw-r--r--pjlib/src/pj/ssl_sock_ossl.c414
-rw-r--r--pjlib/src/pj/ssl_sock_symbian.cpp152
-rw-r--r--pjlib/src/pjlib-test/ssl_sock.c213
5 files changed, 817 insertions, 204 deletions
diff --git a/pjlib/src/pj/ssl_sock_common.c b/pjlib/src/pj/ssl_sock_common.c
index 127099c6..988a8b2a 100644
--- a/pjlib/src/pj/ssl_sock_common.c
+++ b/pjlib/src/pj/ssl_sock_common.c
@@ -17,6 +17,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <pj/ssl_sock.h>
+#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/string.h>
@@ -128,6 +129,7 @@ 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;
@@ -140,3 +142,96 @@ PJ_DEF(const char*) pj_ssl_cipher_name(pj_ssl_cipher cipher)
return NULL;
}
+
+
+
+
+PJ_DEF(pj_status_t) pj_ssl_cert_verify_error_st(pj_uint32_t verify_status,
+ const char *error_strings[],
+ unsigned *count)
+{
+ unsigned i = 0, shift_idx = 0;
+ unsigned unknown = 0;
+ pj_uint32_t errs;
+
+ PJ_ASSERT_RETURN(error_strings && count, PJ_EINVAL);
+
+ if (verify_status == PJ_SSL_CERT_ESUCCESS && *count) {
+ error_strings[0] = "OK";
+ *count = 1;
+ return PJ_SUCCESS;
+ }
+
+ errs = verify_status;
+
+ while (errs && i < *count) {
+ pj_uint32_t err;
+ const char *p = NULL;
+
+ if ((errs & 1) == 0) {
+ shift_idx++;
+ errs >>= 1;
+ continue;
+ }
+
+ err = (1 << shift_idx);
+
+ switch (err) {
+ case PJ_SSL_CERT_EISSUER_NOT_FOUND:
+ p = "The issuer certificate cannot be found";
+ break;
+ case PJ_SSL_CERT_EUNTRUSTED:
+ p = "The certificate is untrusted";
+ break;
+ case PJ_SSL_CERT_EVALIDITY_PERIOD:
+ p = "The certificate has expired or not yet valid";
+ break;
+ case PJ_SSL_CERT_EINVALID_FORMAT:
+ p = "One or more fields of the certificate cannot be decoded "
+ "due to invalid format";
+ break;
+ case PJ_SSL_CERT_EISSUER_MISMATCH:
+ p = "The issuer info in the certificate does not match to the "
+ "(candidate) issuer certificate";
+ break;
+ case PJ_SSL_CERT_ECRL_FAILURE:
+ p = "The CRL certificate cannot be found or cannot be read "
+ "properly";
+ break;
+ case PJ_SSL_CERT_EREVOKED:
+ p = "The certificate has been revoked";
+ break;
+ case PJ_SSL_CERT_EINVALID_PURPOSE:
+ p = "The certificate or CA certificate cannot be used for the "
+ "specified purpose";
+ break;
+ case PJ_SSL_CERT_ECHAIN_TOO_LONG:
+ p = "The certificate chain length is too long";
+ break;
+ case PJ_SSL_CERT_EIDENTITY_NOT_MATCH:
+ p = "The server identity does not match to any identities "
+ "specified in the certificate";
+ break;
+ case PJ_SSL_CERT_EUNKNOWN:
+ default:
+ unknown++;
+ break;
+ }
+
+ /* Set error string */
+ if (p)
+ error_strings[i++] = p;
+
+ /* Next */
+ shift_idx++;
+ errs >>= 1;
+ }
+
+ /* Unknown error */
+ if (unknown && i < *count)
+ error_strings[i++] = "Unknown verification error";
+
+ *count = i;
+
+ return PJ_SUCCESS;
+}
diff --git a/pjlib/src/pj/ssl_sock_dump.c b/pjlib/src/pj/ssl_sock_dump.c
new file mode 100644
index 00000000..45a6f7ee
--- /dev/null
+++ b/pjlib/src/pj/ssl_sock_dump.c
@@ -0,0 +1,147 @@
+/* $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/errno.h>
+#include <pj/os.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_dump.c"
+
+#define CHECK_BUF_LEN() \
+ if ((len < 0) || ((p+=len) >= end)) { \
+ *(p-1) = '\0'; \
+ return PJ_ETOOSMALL; \
+ }
+
+PJ_DEF(pj_status_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
+ const char *prefix,
+ char *buf,
+ pj_size_t buf_size)
+{
+ const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ pj_parsed_time pt1;
+ pj_parsed_time pt2;
+ unsigned i;
+ int len = 0;
+ char *p, *end;
+
+ p = buf;
+ end = buf + buf_size;
+
+ pj_time_decode(&ci->validity.start, &pt1);
+ pj_time_decode(&ci->validity.end, &pt2);
+
+ /* Version */
+ len = pj_ansi_snprintf(p, end-p, "%sVersion : v%d\n",
+ prefix, ci->version);
+ CHECK_BUF_LEN();
+
+ /* Serial number */
+ len = pj_ansi_snprintf(p, end-p, "%sSerial : ", prefix);
+ CHECK_BUF_LEN();
+
+ for (i = 0; i < sizeof(ci->serial_no) && !ci->serial_no[i]; ++i);
+ for (; i < sizeof(ci->serial_no); ++i) {
+ len = pj_ansi_snprintf(p, end-p, "%02X ", ci->serial_no[i]);
+ CHECK_BUF_LEN();
+ }
+ *(p-1) = '\n';
+
+ /* Subject */
+ len = pj_ansi_snprintf( p, end-p, "%sSubject : %.*s\n", prefix,
+ ci->subject.cn.slen,
+ ci->subject.cn.ptr);
+ CHECK_BUF_LEN();
+ len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", prefix,
+ ci->subject.info.slen,
+ ci->subject.info.ptr);
+ CHECK_BUF_LEN();
+
+ /* Issuer */
+ len = pj_ansi_snprintf( p, end-p, "%sIssuer : %.*s\n", prefix,
+ ci->issuer.cn.slen,
+ ci->issuer.cn.ptr);
+ CHECK_BUF_LEN();
+ len = pj_ansi_snprintf( p, end-p, "%s %.*s\n", prefix,
+ ci->issuer.info.slen,
+ ci->issuer.info.ptr);
+ CHECK_BUF_LEN();
+
+ /* Validity period */
+ len = pj_ansi_snprintf( p, end-p, "%sValid from : %s %4d-%02d-%02d "
+ "%02d:%02d:%02d.%03d %s\n", prefix,
+ wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day,
+ pt1.hour, pt1.min, pt1.sec, pt1.msec,
+ (ci->validity.gmt? "GMT":""));
+ CHECK_BUF_LEN();
+
+ len = pj_ansi_snprintf( p, end-p, "%sValid to : %s %4d-%02d-%02d "
+ "%02d:%02d:%02d.%03d %s\n", prefix,
+ wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day,
+ pt2.hour, pt2.min, pt2.sec, pt2.msec,
+ (ci->validity.gmt? "GMT":""));
+ CHECK_BUF_LEN();
+
+ /* Subject alternative name extension */
+ if (ci->subj_alt_name.cnt) {
+ unsigned i;
+
+ len = pj_ansi_snprintf(p, end-p, "%ssubjectAltName extension\n",
+ prefix);
+ CHECK_BUF_LEN();
+
+ for (i = 0; i < ci->subj_alt_name.cnt; ++i) {
+ const char *type = NULL;
+
+ switch(ci->subj_alt_name.entry[i].type) {
+ case PJ_SSL_CERT_NAME_RFC822:
+ type = "MAIL";
+ break;
+ case PJ_SSL_CERT_NAME_DNS:
+ type = " DNS";
+ break;
+ case PJ_SSL_CERT_NAME_URI:
+ type = " URI";
+ break;
+ case PJ_SSL_CERT_NAME_IP:
+ type = " IP";
+ break;
+ default:
+ break;
+ }
+ if (type) {
+ len = pj_ansi_snprintf( p, end-p, "%s %s : %.*s\n", prefix,
+ type,
+ ci->subj_alt_name.entry[i].name.slen,
+ ci->subj_alt_name.entry[i].name.ptr);
+ CHECK_BUF_LEN();
+ }
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+
+#endif /* PJ_HAS_SSL_SOCK */
+
diff --git a/pjlib/src/pj/ssl_sock_ossl.c b/pjlib/src/pj/ssl_sock_ossl.c
index 3b0e86fe..7fe0c435 100644
--- a/pjlib/src/pj/ssl_sock_ossl.c
+++ b/pjlib/src/pj/ssl_sock_ossl.c
@@ -45,6 +45,7 @@
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
+#include <openssl/x509v3.h>
#ifdef _MSC_VER
@@ -152,6 +153,7 @@ struct pj_ssl_sock_t
enum ssl_state ssl_state;
pj_ioqueue_op_key_t handshake_op_key;
pj_timer_entry timer;
+ pj_status_t verify_status;
pj_sock_t sock;
pj_activesock_t *asock;
@@ -207,13 +209,17 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
#define PJ_SSL_ERRNO_SPACE_SIZE PJ_ERRNO_SPACE_SIZE
-#define GET_SSL_STATUS(status) { \
- unsigned long e = ERR_get_error();\
- status = ERR_GET_LIB(e)*300 + ERR_GET_REASON(e);\
+#define STATUS_FROM_SSL_ERR(err, status) { \
+ status = ERR_GET_LIB(err)*300 + ERR_GET_REASON(err);\
pj_assert(status < PJ_SSL_ERRNO_SPACE_SIZE);\
if (status) status += PJ_SSL_ERRNO_START;\
}
+#define GET_SSL_STATUS(status) { \
+ unsigned long e = ERR_get_error();\
+ STATUS_FROM_SSL_ERR(e, status);\
+}
+
/*
* Get error string of OpenSSL.
*/
@@ -235,7 +241,11 @@ static pj_str_t ssl_strerror(pj_status_t status,
{
const char *tmp = NULL;
- tmp = ERR_reason_error_string(ssl_err);
+
+ if (ssl_err >= 300)
+ tmp = ERR_reason_error_string(ssl_err);
+ else
+ tmp = X509_verify_cert_error_string(ssl_err);
if (tmp) {
pj_ansi_strncpy(buf, tmp, bufsize);
@@ -263,6 +273,9 @@ static int openssl_reg_strerr;
static pj_ssl_cipher openssl_ciphers[100];
static unsigned openssl_cipher_num;
+/* OpenSSL application data index */
+static int sslsock_idx;
+
/* Initialize OpenSSL */
static pj_status_t init_openssl(void)
@@ -329,6 +342,9 @@ static pj_status_t init_openssl(void)
openssl_cipher_num = n;
}
+ /* Create OpenSSL application data index for SSL socket */
+ sslsock_idx = SSL_get_ex_new_index(0, "SSL socket", NULL, NULL, NULL);
+
return PJ_SUCCESS;
}
@@ -355,8 +371,107 @@ static int password_cb(char *buf, int num, int rwflag, void *user_data)
}
-/* Create and initialize new SSL context */
-static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
+/* SSL password callback. */
+static int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+ pj_ssl_sock_t *ssock;
+ SSL *ossl_ssl;
+ int err;
+
+ /* Get SSL instance */
+ ossl_ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+ SSL_get_ex_data_X509_STORE_CTX_idx());
+ pj_assert(ossl_ssl);
+
+ /* Get SSL socket instance */
+ ssock = SSL_get_ex_data(ossl_ssl, sslsock_idx);
+ pj_assert(ssock);
+
+ /* Store verification status */
+ err = X509_STORE_CTX_get_error(x509_ctx);
+ switch (err) {
+ case X509_V_OK:
+ break;
+
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_NOT_FOUND;
+ break;
+
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_FORMAT;
+ break;
+
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ ssock->verify_status |= PJ_SSL_CERT_EVALIDITY_PERIOD;
+ break;
+
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ ssock->verify_status |= PJ_SSL_CERT_ECRL_FAILURE;
+ break;
+
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ case X509_V_ERR_CERT_UNTRUSTED:
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ ssock->verify_status |= PJ_SSL_CERT_EUNTRUSTED;
+ break;
+
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ ssock->verify_status |= PJ_SSL_CERT_EISSUER_MISMATCH;
+ break;
+
+ case X509_V_ERR_CERT_REVOKED:
+ ssock->verify_status |= PJ_SSL_CERT_EREVOKED;
+ break;
+
+ case X509_V_ERR_INVALID_PURPOSE:
+ case X509_V_ERR_CERT_REJECTED:
+ case X509_V_ERR_INVALID_CA:
+ ssock->verify_status |= PJ_SSL_CERT_EINVALID_PURPOSE;
+ break;
+
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG: /* not really used */
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ ssock->verify_status |= PJ_SSL_CERT_ECHAIN_TOO_LONG;
+ break;
+
+ /* Unknown errors */
+ case X509_V_ERR_OUT_OF_MEM:
+ default:
+ ssock->verify_status |= PJ_SSL_CERT_EUNKNOWN;
+ break;
+ }
+
+ /* When verification is not requested just return ok here, however
+ * application can still get the verification status.
+ */
+ if (PJ_FALSE == ssock->param.verify_peer)
+ preverify_ok = 1;
+
+ return preverify_ok;
+}
+
+/* Setting SSL sock cipher list */
+static pj_status_t set_cipher_list(pj_ssl_sock_t *ssock);
+
+
+/* Create and initialize new SSL context and instance */
+static pj_status_t create_ssl(pj_ssl_sock_t *ssock)
{
SSL_METHOD *ssl_method;
SSL_CTX *ctx;
@@ -364,7 +479,7 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
int mode, rc;
pj_status_t status;
- pj_assert(ssock && p_ctx);
+ pj_assert(ssock);
cert = ssock->cert;
@@ -393,7 +508,7 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
return PJ_EINVAL;
}
- /* Create SSL context for the listener */
+ /* Create SSL context */
ctx = SSL_CTX_new(ssl_method);
if (ctx == NULL) {
GET_SSL_STATUS(status);
@@ -455,29 +570,55 @@ static pj_status_t create_ssl_ctx(pj_ssl_sock_t *ssock, SSL_CTX **p_ctx)
}
}
-
- /* SSL verification options */
- if (ssock->param.verify_peer) {
- mode = SSL_VERIFY_PEER;
- } else {
- mode = SSL_VERIFY_NONE;
+ /* Create SSL instance */
+ ssock->ossl_ctx = ctx;
+ ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
+ if (ssock->ossl_ssl == NULL) {
+ GET_SSL_STATUS(status);
+ return status;
}
+ /* Set SSL sock as application data of SSL instance */
+ SSL_set_ex_data(ssock->ossl_ssl, sslsock_idx, ssock);
+
+ /* SSL verification options */
+ mode = SSL_VERIFY_PEER;
if (ssock->is_server && ssock->param.require_client_cert)
- mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_PEER;
+ mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- SSL_CTX_set_verify(ctx, mode, NULL);
+ SSL_set_verify(ssock->ossl_ssl, mode, &verify_cb);
- *p_ctx = ctx;
+ /* Set cipher list */
+ status = set_cipher_list(ssock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* 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);
return PJ_SUCCESS;
}
-/* Destroy SSL context */
-static void destroy_ssl_ctx(SSL_CTX *ctx)
+/* Destroy SSL context and instance */
+static void destroy_ssl(pj_ssl_sock_t *ssock)
{
- SSL_CTX_free(ctx);
+ /* Destroy SSL instance */
+ if (ssock->ossl_ssl) {
+ SSL_shutdown(ssock->ossl_ssl);
+ SSL_free(ssock->ossl_ssl); /* this will also close BIOs */
+ ssock->ossl_ssl = NULL;
+ }
+
+ /* Destroy SSL context */
+ if (ssock->ossl_ctx) {
+ SSL_CTX_free(ssock->ossl_ctx);
+ ssock->ossl_ctx = NULL;
+ }
/* Potentially shutdown OpenSSL library if this is the last
* context exists.
@@ -491,15 +632,8 @@ 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;
- }
+ destroy_ssl(ssock);
+
if (ssock->asock) {
pj_activesock_close(ssock->asock);
ssock->asock = NULL;
@@ -639,42 +773,143 @@ static pj_bool_t parse_ossl_asn1_time(pj_time_val *tv, pj_bool_t *gmt,
}
-/* Get certificate info from OpenSSL X509 */
+/* Get Common Name field string from a general name string */
+static void get_cn_from_gen_name(const pj_str_t *gen_name, pj_str_t *cn)
+{
+ pj_str_t CN_sign = {"/CN=", 4};
+ char *p, *q;
+
+ pj_bzero(cn, sizeof(cn));
+
+ p = pj_strstr(gen_name, &CN_sign);
+ if (!p)
+ return;
+
+ p += 4; /* shift pointer to value part */
+ pj_strset(cn, p, gen_name->slen - (p - gen_name->ptr));
+ q = pj_strchr(cn, '/');
+ if (q)
+ cn->slen = q - p;
+}
+
+
+/* Get certificate info from OpenSSL X509, in case the certificate info
+ * hal already populated, this function will check if the contents need
+ * to be updated by inspecting the issuer and the serial number.
+ */
static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci, X509 *x)
{
- pj_ssl_cert_info info;
- char buf1[256];
- char buf2[256];
+ pj_bool_t update_needed;
+ char buf[512];
+ pj_uint8_t serial_no[64] = {0}; /* should be >= sizeof(ci->serial_no) */
+ pj_uint8_t *p;
+ unsigned len;
+ GENERAL_NAMES *names = NULL;
+
+ pj_assert(pool && ci && x);
+
+ /* Get issuer */
+ X509_NAME_oneline(X509_get_issuer_name(x), buf, sizeof(buf));
- pj_assert(pool && ci);
+ /* Get serial no */
+ p = (pj_uint8_t*) M_ASN1_STRING_data(X509_get_serialNumber(x));
+ len = M_ASN1_STRING_length(X509_get_serialNumber(x));
+ if (len > sizeof(ci->serial_no))
+ len = sizeof(ci->serial_no);
+ pj_memcpy(serial_no + sizeof(ci->serial_no) - len, p, len);
- if (!x) {
- pj_bzero(ci, sizeof(pj_ssl_cert_info));
+ /* Check if the contents need to be updated. */
+ update_needed = pj_strcmp2(&ci->issuer.info, buf) ||
+ pj_memcmp(ci->serial_no, serial_no, sizeof(ci->serial_no));
+ if (!update_needed)
return;
- }
- pj_bzero(&info, sizeof(info));
+ /* Update cert info */
+
+ pj_bzero(ci, sizeof(pj_ssl_cert_info));
+
+ /* Version */
+ ci->version = X509_get_version(x) + 1;
+
+ /* Issuer */
+ pj_strdup2(pool, &ci->issuer.info, buf);
+ get_cn_from_gen_name(&ci->issuer.info, &ci->issuer.cn);
+
+ /* Serial number */
+ pj_memcpy(ci->serial_no, serial_no, sizeof(ci->serial_no));
- /* Populate cert info */
- info.subject = pj_str(X509_NAME_oneline(X509_get_subject_name(x),buf1,
- sizeof(buf1)));
- info.issuer = pj_str(X509_NAME_oneline(X509_get_issuer_name(x), buf2,
- sizeof(buf2)));
- info.version = X509_get_version(x) + 1;
- parse_ossl_asn1_time(&info.validity_start, &info.validity_use_gmt,
+ /* Subject */
+ pj_strdup2(pool, &ci->subject.info,
+ X509_NAME_oneline(X509_get_subject_name(x),
+ buf, sizeof(buf)));
+ get_cn_from_gen_name(&ci->subject.info, &ci->subject.cn);
+
+ /* Validity */
+ parse_ossl_asn1_time(&ci->validity.start, &ci->validity.gmt,
X509_get_notBefore(x));
- parse_ossl_asn1_time(&info.validity_end, &info.validity_use_gmt,
+ parse_ossl_asn1_time(&ci->validity.end, &ci->validity.gmt,
X509_get_notAfter(x));
- /* Update certificate info */
- if (pj_strcmp(&ci->subject, &info.subject))
- pj_strdup(pool, &ci->subject, &info.subject);
- if (pj_strcmp(&ci->issuer, &info.issuer))
- pj_strdup(pool, &ci->issuer, &info.issuer);
- ci->version = info.version;
- ci->validity_start = info.validity_start;
- ci->validity_end = info.validity_end;
- ci->validity_use_gmt = info.validity_use_gmt;
+ /* Subject Alternative Name extension */
+ if (ci->version >= 3) {
+ names = (GENERAL_NAMES*) X509_get_ext_d2i(x, NID_subject_alt_name,
+ NULL, NULL);
+ }
+ if (names) {
+ unsigned i, cnt;
+
+ cnt = sk_GENERAL_NAME_num(names);
+ ci->subj_alt_name.entry = pj_pool_calloc(pool, cnt,
+ sizeof(*ci->subj_alt_name.entry));
+
+ for (i = 0; i < cnt; ++i) {
+ unsigned char *p = 0;
+ pj_ssl_cert_name_type type = PJ_SSL_CERT_NAME_UNKNOWN;
+ const GENERAL_NAME *name;
+
+ name = sk_GENERAL_NAME_value(names, i);
+
+ switch (name->type) {
+ case GEN_EMAIL:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_RFC822;
+ break;
+ case GEN_DNS:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_DNS;
+ break;
+ case GEN_URI:
+ len = ASN1_STRING_to_UTF8(&p, name->d.ia5);
+ type = PJ_SSL_CERT_NAME_URI;
+ break;
+ case GEN_IPADD:
+ p = ASN1_STRING_data(name->d.ip);
+ len = ASN1_STRING_length(name->d.ip);
+ type = PJ_SSL_CERT_NAME_IP;
+ break;
+ default:
+ break;
+ }
+
+ if (p && len && type != PJ_SSL_CERT_NAME_UNKNOWN) {
+ ci->subj_alt_name.entry[ci->subj_alt_name.cnt].type = type;
+ if (type == PJ_SSL_CERT_NAME_IP) {
+ int af = pj_AF_INET();
+ if (len == sizeof(pj_in6_addr)) af = pj_AF_INET6();
+ pj_inet_ntop2(af, p, buf, sizeof(buf));
+ pj_strdup2(pool,
+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+ buf);
+ } else {
+ pj_strdup2(pool,
+ &ci->subj_alt_name.entry[ci->subj_alt_name.cnt].name,
+ (char*)p);
+ OPENSSL_free(p);
+ }
+ ci->subj_alt_name.cnt++;
+ }
+ }
+ }
}
@@ -689,14 +924,22 @@ static void update_certs_info(pj_ssl_sock_t *ssock)
/* Active local certificate */
x = SSL_get_certificate(ssock->ossl_ssl);
- get_cert_info(ssock->pool, &ssock->local_cert_info, x);
- /* Don't free local's X509! */
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->local_cert_info, x);
+ /* Don't free local's X509! */
+ } else {
+ pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));
+ }
/* Active remote certificate */
x = SSL_get_peer_certificate(ssock->ossl_ssl);
- get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
- /* Free peer's X509 */
- X509_free(x);
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+ /* Free peer's X509 */
+ X509_free(x);
+ } else {
+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+ }
}
@@ -1231,29 +1474,10 @@ static pj_bool_t asock_on_accept_complete (pj_activesock_t *asock,
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) {
- GET_SSL_STATUS(status);
- goto on_return;
- }
-
- /* Set cipher list */
- status = set_cipher_list(ssock);
+ status = create_ssl(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,
@@ -1351,29 +1575,10 @@ static pj_bool_t asock_on_connect_complete (pj_activesock_t *asock,
goto on_return;
/* Create SSL context */
- status = create_ssl_ctx(ssock, &ssock->ossl_ctx);
+ status = create_ssl(ssock);
if (status != PJ_SUCCESS)
goto on_return;
- /* Create SSL instance */
- ssock->ossl_ssl = SSL_new(ssock->ossl_ctx);
- if (ssock->ossl_ssl == NULL) {
- GET_SSL_STATUS(status);
- 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,
@@ -1651,9 +1856,9 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_sockaddr_cp(&info->local_addr, &ssock->local_addr);
if (info->established) {
- /* Current cipher */
const SSL_CIPHER *cipher;
+ /* Current cipher */
cipher = SSL_get_current_cipher(ssock->ossl_ssl);
info->cipher = (cipher->id & 0x00FFFFFF);
@@ -1661,8 +1866,11 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
pj_sockaddr_cp(&info->remote_addr, &ssock->rem_addr);
/* Certificates info */
- info->local_cert_info = ssock->local_cert_info;
- info->remote_cert_info = ssock->remote_cert_info;
+ info->local_cert_info = &ssock->local_cert_info;
+ info->remote_cert_info = &ssock->remote_cert_info;
+
+ /* Verification status */
+ info->verify_status = ssock->verify_status;
}
return PJ_SUCCESS;
diff --git a/pjlib/src/pj/ssl_sock_symbian.cpp b/pjlib/src/pj/ssl_sock_symbian.cpp
index efcf1702..ab808f62 100644
--- a/pjlib/src/pj/ssl_sock_symbian.cpp
+++ b/pjlib/src/pj/ssl_sock_symbian.cpp
@@ -127,6 +127,11 @@ public:
return securesock_->CurrentCipherSuite(cipher);
return KErrNotFound;
}
+ const CX509Certificate *GetPeerCert() {
+ if (securesock_)
+ return securesock_->ServerCert();
+ return NULL;
+ }
private:
enum ssl_state state_;
@@ -409,6 +414,7 @@ typedef struct write_state_t {
*/
struct pj_ssl_sock_t
{
+ pj_pool_t *pool;
pj_ssl_sock_cb cb;
void *user_data;
@@ -434,9 +440,132 @@ struct pj_ssl_sock_t
unsigned ciphers_num;
pj_ssl_cipher *ciphers;
pj_str_t servername;
+ pj_ssl_cert_info remote_cert_info;
};
+static pj_str_t get_cert_name(pj_pool_t *pool,
+ const CX500DistinguishedName &name)
+{
+ TInt i;
+ char buf[1024];
+ TUint8 *p;
+ TInt l = sizeof(buf);
+
+ p = (TUint8*)buf;
+ for(i = 0; i < name.Count(); ++i) {
+ const CX520AttributeTypeAndValue &attr = name.Element(i);
+
+ /* Print element separator */
+ *p++ = '/';
+ if (0 == --l) break;
+
+ /* Print the type. */
+ TPtr8 type(p, l);
+ type.Copy(attr.Type());
+ p += type.Length();
+ l -= type.Length();
+ if (0 >= --l) break;
+
+ /* Print equal sign */
+ *p++ = '=';
+ if (0 == --l) break;
+
+ /* Print the value. Let's just get the raw data here */
+ TPtr8 value(p, l);
+ value.Copy(attr.EncodedValue().Mid(2));
+ p += value.Length();
+ l -= value.Length();
+ if (0 >= --l) break;
+ }
+
+ pj_str_t src, res;
+ pj_strset(&src, buf, sizeof(buf) - l);
+ pj_strdup(pool, &res, &src);
+
+ return res;
+}
+
+/* Get certificate info from CX509Certificate.
+ */
+static void get_cert_info(pj_pool_t *pool, pj_ssl_cert_info *ci,
+ const CX509Certificate *x)
+{
+ unsigned len;
+
+ pj_assert(pool && ci && x);
+
+ pj_bzero(ci, sizeof(*ci));
+
+ /* Version */
+ ci->version = x->Version();
+
+ /* Serial number */
+ len = x->SerialNumber().Length();
+ if (len > sizeof(ci->serial_no))
+ len = sizeof(ci->serial_no);
+ pj_memcpy(ci->serial_no + sizeof(ci->serial_no) - len,
+ x->SerialNumber().Ptr(), len);
+
+ /* Subject */
+ {
+ HBufC *subject = NULL;
+ TRAPD(err, subject = x->SubjectL());
+ if (err == KErrNone) {
+ TPtr16 ptr16(subject->Des());
+ len = ptr16.Length();
+ TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+ ptr8.Copy(ptr16);
+ pj_strset(&ci->subject.cn, (char*)ptr8.Ptr(), ptr8.Length());
+ }
+ ci->subject.info = get_cert_name(pool, x->SubjectName());
+ }
+
+ /* Issuer */
+ {
+ HBufC *issuer = NULL;
+ TRAPD(err, issuer = x->IssuerL());
+ if (err == KErrNone) {
+ TPtr16 ptr16(issuer->Des());
+ len = ptr16.Length();
+ TPtr8 ptr8((TUint8*)pj_pool_alloc(pool, len), len);
+ ptr8.Copy(ptr16);
+ pj_strset(&ci->issuer.cn, (char*)ptr8.Ptr(), ptr8.Length());
+ }
+ ci->issuer.info = get_cert_name(pool, x->IssuerName());
+ }
+
+ /* Validity */
+ const CValidityPeriod &valid_period = x->ValidityPeriod();
+ TTime base_time(TDateTime(1970, EJanuary, 0, 0, 0, 0, 0));
+ TTimeIntervalSeconds tmp_sec;
+ valid_period.Start().SecondsFrom(base_time, tmp_sec);
+ ci->validity.start.sec = tmp_sec.Int();
+ valid_period.Finish().SecondsFrom(base_time, tmp_sec);
+ ci->validity.end.sec = tmp_sec.Int();
+}
+
+
+/* Update certificates info. This function should be called after handshake
+ * or renegotiation successfully completed.
+ */
+static void update_certs_info(pj_ssl_sock_t *ssock)
+{
+ const CX509Certificate *x;
+
+ pj_assert(ssock && ssock->sock &&
+ ssock->sock->GetState() == CPjSSLSocket::SSL_STATE_ESTABLISHED);
+
+ /* Active remote certificate */
+ x = ssock->sock->GetPeerCert();
+ if (x) {
+ get_cert_info(ssock->pool, &ssock->remote_cert_info, x);
+ } else {
+ pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
+ }
+}
+
+
/*
* Get cipher list supported by SSL/TLS backend.
*/
@@ -504,6 +633,7 @@ PJ_DEF(pj_status_t) pj_ssl_sock_create (pj_pool_t *pool,
ssock->write_state.start = ssock->write_state.buf;
/* Init secure socket */
+ ssock->pool = pool;
ssock->sock_af = param->sock_af;
ssock->sock_type = param->sock_type;
ssock->cb = param->cb;
@@ -643,7 +773,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_get_info (pj_ssl_sock_t *ssock,
/* Remote address */
pj_sockaddr_cp((pj_sockaddr_t*)&info->remote_addr,
- (pj_sockaddr_t*)&ssock->rem_addr);
+ (pj_sockaddr_t*)&ssock->rem_addr);
+
+ /* Certificates info */
+ info->remote_cert_info = &ssock->remote_cert_info;
}
/* Protocol */
@@ -728,14 +861,20 @@ static void read_cb(int err, void *key)
ssock->read_state.read_buf->SetLength(0);
status = reader->Read(&read_cb, ssock, *ssock->read_state.read_buf,
ssock->read_state.flags);
- if (status != PJ_EPENDING) {
- /* Notify error */
- (*ssock->cb.on_data_read)(ssock, NULL, 0, status, NULL);
- }
}
+ /* Connection closed or something goes wrong */
if (status != PJ_SUCCESS && status != PJ_EPENDING) {
- /* Connection closed or something goes wrong */
+ /* Notify error */
+ if (ssock->cb.on_data_read) {
+ pj_bool_t ret = (*ssock->cb.on_data_read)(ssock, NULL, 0,
+ status, NULL);
+ if (!ret) {
+ /* We've been destroyed */
+ return;
+ }
+ }
+
delete ssock->read_state.read_buf;
delete ssock->read_state.orig_buf;
ssock->read_state.read_buf = NULL;
@@ -1001,6 +1140,7 @@ static void connect_cb(int err, void *key)
status = (err == KErrNone)? PJ_SUCCESS : PJ_RETURN_OS_ERROR(err);
if (status == PJ_SUCCESS) {
ssock->established = PJ_TRUE;
+ update_certs_info(ssock);
} else {
delete ssock->sock;
ssock->sock = NULL;
diff --git a/pjlib/src/pjlib-test/ssl_sock.c b/pjlib/src/pjlib-test/ssl_sock.c
index 8302e8f1..6e0d4515 100644
--- a/pjlib/src/pjlib-test/ssl_sock.c
+++ b/pjlib/src/pjlib-test/ssl_sock.c
@@ -22,7 +22,7 @@
#define CERT_DIR "../build/"
-#define CERT_CA_FILE NULL
+#define CERT_CA_FILE CERT_DIR "cacert.pem"
#define CERT_FILE CERT_DIR "cacert.pem"
#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem"
#define CERT_PRIVKEY_PASS ""
@@ -83,26 +83,40 @@ struct test_state
struct send_key send_key; /* send op key */
};
-static void dump_cert_info(const char *prefix, const pj_ssl_cert_info *ci)
+static void dump_ssl_info(const pj_ssl_sock_info *si)
{
- const char *wdays[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- pj_parsed_time pt1;
- pj_parsed_time pt2;
-
- pj_time_decode(&ci->validity_start, &pt1);
- pj_time_decode(&ci->validity_end, &pt2);
-
- PJ_LOG(3, ("", "%sSubject : %.*s", prefix, ci->subject.slen, ci->subject.ptr));
- PJ_LOG(3, ("", "%sIssuer : %.*s", prefix, ci->issuer.slen, ci->issuer.ptr));
- PJ_LOG(3, ("", "%sVersion : v%d", prefix, ci->version));
- PJ_LOG(3, ("", "%sValid from : %s %4d-%02d-%02d %02d:%02d:%02d.%03d %s",
- prefix, wdays[pt1.wday], pt1.year, pt1.mon+1, pt1.day,
- pt1.hour, pt1.min, pt1.sec, pt1.msec,
- (ci->validity_use_gmt? "GMT":"")));
- PJ_LOG(3, ("", "%sValid to : %s %4d-%02d-%02d %02d:%02d:%02d.%03d %s",
- prefix, wdays[pt2.wday], pt2.year, pt2.mon+1, pt2.day,
- pt2.hour, pt2.min, pt2.sec, pt2.msec,
- (ci->validity_use_gmt? "GMT":"")));
+ const char *tmp_st;
+
+ /* Print cipher name */
+ tmp_st = pj_ssl_cipher_name(si->cipher);
+ if (tmp_st == NULL)
+ tmp_st = "[Unknown]";
+ PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+
+ /* Print remote certificate info and verification result */
+ if (si->remote_cert_info && si->remote_cert_info->subject.info.slen)
+ {
+ char buf[2048];
+ const char *verif_msgs[32];
+ unsigned verif_msg_cnt;
+
+ /* Dump remote TLS certificate info */
+ PJ_LOG(3, ("", ".....Remote certificate info:"));
+ pj_ssl_cert_info_dump(si->remote_cert_info, " ", buf, sizeof(buf));
+ PJ_LOG(3,("", "\n%s", buf));
+
+ /* Dump remote TLS certificate verification result */
+ verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
+ pj_ssl_cert_verify_error_st(si->verify_status,
+ verif_msgs, &verif_msg_cnt);
+ PJ_LOG(3,("", ".....Remote certificate verification result: %s",
+ (verif_msg_cnt == 1? verif_msgs[0]:"")));
+ if (verif_msg_cnt > 1) {
+ unsigned i;
+ for (i = 0; i < verif_msg_cnt; ++i)
+ PJ_LOG(3,("", "..... - %s", verif_msgs[i]));
+ }
+ }
}
@@ -130,25 +144,8 @@ static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
- if (st->is_verbose) {
- const char *tmp_st;
-
- /* Print cipher name */
- tmp_st = pj_ssl_cipher_name(info.cipher);
- if (tmp_st == NULL)
- tmp_st = "[Unknown]";
- PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
-
- /* Print certificates info */
- if (info.local_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Local certificate info:"));
- dump_cert_info(".......", &info.local_cert_info);
- }
- if (info.remote_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Remote certificate info:"));
- dump_cert_info(".......", &info.remote_cert_info);
- }
- }
+ if (st->is_verbose)
+ dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
@@ -198,6 +195,8 @@ static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
pj_ssl_sock_get_user_data(ssock);
struct test_state *st;
void *read_buf[1];
+ pj_ssl_sock_info info;
+ char buf[64];
pj_status_t status;
PJ_UNUSED_ARG(src_addr_len);
@@ -207,36 +206,17 @@ static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,
*st = *parent_st;
pj_ssl_sock_set_user_data(newsock, st);
- if (st->is_verbose) {
- pj_ssl_sock_info info;
- char buf[64];
- const char *tmp_st;
-
- status = pj_ssl_sock_get_info(newsock, &info);
- if (status != PJ_SUCCESS) {
- app_perror("...ERROR pj_ssl_sock_get_info()", status);
- goto on_return;
- }
-
- pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
- PJ_LOG(3, ("", "...Accepted connection from %s", buf));
+ status = pj_ssl_sock_get_info(newsock, &info);
+ if (status != PJ_SUCCESS) {
+ app_perror("...ERROR pj_ssl_sock_get_info()", status);
+ goto on_return;
+ }
- /* Print cipher name */
- tmp_st = pj_ssl_cipher_name(info.cipher);
- if (tmp_st == NULL)
- tmp_st = "[Unknown]";
- PJ_LOG(3, ("", ".....Cipher: %s", tmp_st));
+ pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
+ PJ_LOG(3, ("", "...Accepted connection from %s", buf));
- /* Print certificates info */
- if (info.local_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Local certificate info:"));
- dump_cert_info(".......", &info.local_cert_info);
- }
- if (info.remote_cert_info.subject.slen) {
- PJ_LOG(3, ("", ".....Remote certificate info:"));
- dump_cert_info(".......", &info.remote_cert_info);
- }
- }
+ if (st->is_verbose)
+ dump_ssl_info(&info);
/* Start reading data */
read_buf[0] = st->read_buf;
@@ -460,6 +440,7 @@ static int https_client_test(unsigned ms_timeout)
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
+ param.proto = PJ_SSL_SOCK_PROTO_SSL23;
pj_time_val_normalize(&param.timeout);
status = pj_ssl_sock_create(pool, &param, &ssock);
@@ -512,7 +493,8 @@ on_return:
static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
- pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher)
+ pj_ssl_cipher srv_cipher, pj_ssl_cipher cli_cipher,
+ pj_bool_t req_client_cert, pj_bool_t client_provide_cert)
{
pj_pool_t *pool = NULL;
pj_ioqueue_t *ioqueue = NULL;
@@ -533,21 +515,6 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
- /* Set cert */
- {
- pj_str_t tmp1, tmp2, tmp3, tmp4;
-
- status = pj_ssl_cert_load_from_files(pool,
- pj_strset2(&tmp1, (char*)CERT_CA_FILE),
- pj_strset2(&tmp2, (char*)CERT_FILE),
- pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
- pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
- &cert);
- if (status != PJ_SUCCESS) {
- goto on_return;
- }
- }
-
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
@@ -562,10 +529,11 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
- /* SERVER */
+ /* === SERVER === */
param.proto = srv_proto;
param.user_data = &state_serv;
param.ciphers_num = (srv_cipher == -1)? 0 : 1;
+ param.require_client_cert = req_client_cert;
ciphers[0] = srv_cipher;
state_serv.pool = pool;
@@ -578,9 +546,24 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
- status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
- if (status != PJ_SUCCESS) {
- goto on_return;
+ /* Set server cert */
+ {
+ pj_str_t tmp1, tmp2, tmp3, tmp4;
+
+ status = pj_ssl_cert_load_from_files(pool,
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE),
+ pj_strset2(&tmp2, (char*)CERT_FILE),
+ pj_strset2(&tmp3, (char*)CERT_PRIVKEY_FILE),
+ pj_strset2(&tmp4, (char*)CERT_PRIVKEY_PASS),
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
}
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
@@ -596,7 +579,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_sockaddr_cp(&listen_addr, &info.local_addr);
}
- /* CLIENT */
+ /* === CLIENT === */
param.proto = cli_proto;
param.user_data = &state_cli;
param.ciphers_num = (cli_cipher == -1)? 0 : 1;
@@ -625,6 +608,28 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
+ /* Set cert for client */
+ {
+
+ if (!client_provide_cert) {
+ pj_str_t tmp1, tmp2;
+
+ pj_strset2(&tmp1, (char*)CERT_CA_FILE);
+ pj_strset2(&tmp2, NULL);
+ status = pj_ssl_cert_load_from_files(pool,
+ &tmp1, &tmp2, &tmp2, &tmp2,
+ &cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
if (status == PJ_SUCCESS) {
ssl_on_connect_complete(ssock_cli, PJ_SUCCESS);
@@ -1013,6 +1018,9 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
ssock_cli = pj_pool_calloc(pool, clients, sizeof(pj_ssl_sock_t*));
state_cli = pj_pool_calloc(pool, clients, sizeof(struct test_state));
+ /* Get start timestamp */
+ pj_gettimeofday(&start);
+
/* Setup clients */
for (i = 0; i < clients; ++i) {
param.user_data = &state_cli[i];
@@ -1064,9 +1072,6 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
}
}
- /* Get start timestamp */
- pj_gettimeofday(&start);
-
/* Wait until everything has been sent/received or error */
while (clients_num)
{
@@ -1150,28 +1155,46 @@ int ssl_sock_test(void)
PJ_LOG(3,("", "..echo test w/ TLSv1 and TLS_RSA_WITH_DES_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ SSLv23 and TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_SSL23, PJ_SSL_SOCK_PROTO_SSL23,
- TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA);
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
PJ_LOG(3,("", "..echo test w/ incompatible proto"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_SSL3,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_DES_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
- TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA);
+ TLS_RSA_WITH_DES_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_FALSE, PJ_FALSE);
+ if (ret == 0)
+ return PJ_EBUG;
+
+ PJ_LOG(3,("", "..echo test w/ client cert required but not provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
+ PJ_LOG(3,("", "..echo test w/ client cert required and provided"));
+ ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
+ TLS_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA,
+ PJ_TRUE, PJ_TRUE);
+ if (ret != 0)
+ return ret;
+
PJ_LOG(3,("", "..client non-SSL (handshake timeout 5 secs)"));
ret = client_non_ssl(5000);
if (ret != 0)