summaryrefslogtreecommitdiff
path: root/pjlib
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2010-02-24 05:43:34 +0000
committerNanang Izzuddin <nanang@teluu.com>2010-02-24 05:43:34 +0000
commitbb2fc905eb58b9ebdf66e89330599be996821db7 (patch)
treef6bedef48655a824a1393efbb667a3b8af560b63 /pjlib
parentdf622f00fa10e2cbcde9df6169ad628fe3e72226 (diff)
Ticket #1032:
- Initial version of server domain name verification: - Updated SSL certificate info, especially identities info - Updated verification mechanism as in the specifications in ticket desc. - Added server domain name info in pjsip_tx_data. - Added alternative API for acquiring transport and creating transport of transport factory to include pjsip_tx_data param. - Server identity match criteria: - full host name match - wild card not accepted - if identity is URI, it must be SIP/SIPS URI - Initial version of transport state notifications: - Added new API to set transport state callback in PJSIP and PJSUA. - Defined states: connected/disconnected, accepted/rejected, verification errors. - Minors: - Updated SSL socket test: dump verification result, test of requiring client cert, and few minors. - Updated test cert to include subjectAltName extensions. - Added SSL certificate dump function. - Updated max number of socket async operations in Symbian sample apps (RSocketServ::Connect()) to 32 (was default 8). git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3106 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjlib')
-rw-r--r--pjlib/build/Makefile3
-rw-r--r--pjlib/build/cacert.pem31
-rw-r--r--pjlib/build/pjlib.dsp4
-rw-r--r--pjlib/build/pjlib.vcproj4
-rw-r--r--pjlib/build/privkey.pem38
-rw-r--r--pjlib/include/pj/ssl_sock.h177
-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
11 files changed, 1014 insertions, 264 deletions
diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile
index a3482e00..1e0e88f9 100644
--- a/pjlib/build/Makefile
+++ b/pjlib/build/Makefile
@@ -26,7 +26,8 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \
pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \
- ssl_sock_common.o ssl_sock_ossl.o string.o timer.o types.o
+ ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \
+ string.o timer.o types.o
export PJLIB_CFLAGS += $(_CFLAGS)
###############################################################################
diff --git a/pjlib/build/cacert.pem b/pjlib/build/cacert.pem
index 0a2ee45b..cfce0920 100644
--- a/pjlib/build/cacert.pem
+++ b/pjlib/build/cacert.pem
@@ -1,21 +1,14 @@
-----BEGIN CERTIFICATE-----
-MIIDXzCCAkegAwIBAgIJAPqAwYU5OQLXMA0GCSqGSIb3DQEBBQUAMEYxCzAJBgNV
-BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMQ4wDAYDVQQKDAVwanNpcDESMBAG
-A1UEAwwJcGpzaXAubGFiMB4XDTA5MTAyMjE3MTczN1oXDTE5MTAyMDE3MTczN1ow
-RjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAoMBXBq
-c2lwMRIwEAYDVQQDDAlwanNpcC5sYWIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
-ggEKAoIBAQDWWvnL+oSC0Q6OwGLt2YuhXTEzIVv3B+SGQ7tajB6H3WXVeq+1NmU9
-Yzca33g4FRrU7n4smYmKLzm1aniBhNmJjA+t+gbyizKnLMaCLoG52tUoULcANGKU
-aGwlmvZFugDn2eVg6UfUfRzEGbV3q3a/PzSsOEPwsMeF3YMQJPhkoyPQLtWgUXgP
-89Nyq3XjGGtw/qmUgQjE8a6/P0yXc+myI0hmApmZ9nB3YmlB5W3q6WoU2gGhLXf4
-12rH/LgdnPhM4ijS554Kv9EcUDdQTTrm6bYg66tj+qTet7DolUOlTZ3vKpuCK3tt
-eK9CbNPVzsMsB3yCALSLzQ347pIwfLaJAgMBAAGjUDBOMB0GA1UdDgQWBBRE/VNp
-kNQmLEXKQ+NM4bOVj95zYTAfBgNVHSMEGDAWgBRE/VNpkNQmLEXKQ+NM4bOVj95z
-YTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCj/gzJKTOZDEBD+zr7
-lvbVctiYE9o8ObxZQsnl/6zI2V9H/2yc1sqQyjBupzw6c37ehvk30yIyUfD3ts87
-2xaJ5VgtgUI3FI5DQ+ASyQXmDawUEmXIGqHb2gDrXBQLd6uMpvNDNW+7TouuniyA
-F12JUCNITeXaVJ0c8d4A9J9DlszBfYUzI45yIQu1gbpnpH74Sp/hG77EMxrRau+x
-EFFmV7gAmkCgOBnXm8jTKqNre/GfLfO7w2xoLsubSLnK46U3iLGBIJJRVGu3UQuN
-k1o7CiIKf0SSWj1bQI99ipTj8obBKRqj1nSbgKF/U6FIfd8DGcVvbJCSAG2czzyA
-5tdA
+MIICNDCCAZ2gAwIBAgIJAIa9mZggMk2WMA0GCSqGSIb3DQEBBAUAMDMxEjAQBgNV
+BAMTCXBqc2lwLmxhYjEdMBsGCSqGSIb3DQEJARYOdGVzdEBwanNpcC5sYWIwHhcN
+MTAwMjEwMDkwNTQ0WhcNMjAwMjA4MDkwNTQ0WjAzMRIwEAYDVQQDEwlwanNpcC5s
+YWIxHTAbBgkqhkiG9w0BCQEWDnRlc3RAcGpzaXAubGFiMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDI9T0Pf+1gKOTOAGEpZ481Q6xfm5vz6n1+6udxzQtfPKlQ
+rPD5x5im2u3tmy6ABxZeY5tCdeikBPiGlc5bRIRng6KM8kidkg3gEhwhRUxHCMWb
+mBpkz7rFERf/pWAOCqYCiy1RT8QrK+XOFoFdJhdF85UPDEUw+pHEsYetTDs9RQID
+AQABo1AwTjBMBgNVHREERTBDgglwanNpcC5sYWKCDXNpcC5wanNpcC5sYWKBDnRl
+c3RAcGpzaXAubGFihhFzaXA6c2lwLnBqc2lwLmxhYocEfwAAATANBgkqhkiG9w0B
+AQQFAAOBgQCLPl/WF1QvjT36kVLH0nxfHwDOJuAzlh6nv9rYBviOLw9FTEMgW6hA
+oG55YSdVjTnMynTMOH/kVp4Vxlk46A8neE+/LI8RPh6lJh52vb+iPAtBpsQoq06T
++u4DfJcN8Y/jy+QAn78jryKjwKuZWfuWny9gxsLWMUbH5Bc6v6wfQQ==
-----END CERTIFICATE-----
diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp
index 6977f8a9..d63165eb 100644
--- a/pjlib/build/pjlib.dsp
+++ b/pjlib/build/pjlib.dsp
@@ -340,6 +340,10 @@ SOURCE=..\src\pj\ssl_sock_common.c
# End Source File
# Begin Source File
+SOURCE=..\src\pj\ssl_sock_dump.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pj\ssl_sock_ossl.c
# End Source File
# Begin Source File
diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj
index d151dfbb..9c3941bb 100644
--- a/pjlib/build/pjlib.vcproj
+++ b/pjlib/build/pjlib.vcproj
@@ -5649,6 +5649,10 @@
>
</File>
<File
+ RelativePath="..\src\pj\ssl_sock_dump.c"
+ >
+ </File>
+ <File
RelativePath="..\src\pj\ssl_sock_ossl.c"
>
</File>
diff --git a/pjlib/build/privkey.pem b/pjlib/build/privkey.pem
index 3241be20..44c4b4e6 100644
--- a/pjlib/build/privkey.pem
+++ b/pjlib/build/privkey.pem
@@ -1,27 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
-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
+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
-----END RSA PRIVATE KEY-----
diff --git a/pjlib/include/pj/ssl_sock.h b/pjlib/include/pj/ssl_sock.h
index 9c392143..f9fc69b0 100644
--- a/pjlib/include/pj/ssl_sock.h
+++ b/pjlib/include/pj/ssl_sock.h
@@ -60,17 +60,126 @@ typedef struct pj_ssl_sock_t pj_ssl_sock_t;
typedef struct pj_ssl_cert_t pj_ssl_cert_t;
+typedef enum pj_ssl_cert_verify_flag_t
+{
+ /**
+ * No error in verification.
+ */
+ PJ_SSL_CERT_ESUCCESS = 0,
+
+ /**
+ * The issuer certificate cannot be found.
+ */
+ PJ_SSL_CERT_EISSUER_NOT_FOUND = (1 << 0),
+
+ /**
+ * The certificate is untrusted.
+ */
+ PJ_SSL_CERT_EUNTRUSTED = (1 << 1),
+
+ /**
+ * The certificate has expired or not yet valid.
+ */
+ PJ_SSL_CERT_EVALIDITY_PERIOD = (1 << 2),
+
+ /**
+ * One or more fields of the certificate cannot be decoded due to
+ * invalid format.
+ */
+ PJ_SSL_CERT_EINVALID_FORMAT = (1 << 3),
+
+ /**
+ * The certificate cannot be used for the specified purpose.
+ */
+ PJ_SSL_CERT_EINVALID_PURPOSE = (1 << 4),
+
+ /**
+ * The issuer info in the certificate does not match to the (candidate)
+ * issuer certificate, e.g: issuer name not match to subject name
+ * of (candidate) issuer certificate.
+ */
+ PJ_SSL_CERT_EISSUER_MISMATCH = (1 << 5),
+
+ /**
+ * The CRL certificate cannot be found or cannot be read properly.
+ */
+ PJ_SSL_CERT_ECRL_FAILURE = (1 << 6),
+
+ /**
+ * The certificate has been revoked.
+ */
+ PJ_SSL_CERT_EREVOKED = (1 << 7),
+
+ /**
+ * The certificate chain length is too long.
+ */
+ PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8),
+
+ /**
+ * The server identity does not match to any identities specified in
+ * the certificate, e.g: subjectAltName extension, subject common name.
+ * This flag will only be set by application as SSL socket does not
+ * perform server identity verification.
+ */
+ PJ_SSL_CERT_EIDENTITY_NOT_MATCH = (1 << 30),
+
+ /**
+ * Unknown verification error.
+ */
+ PJ_SSL_CERT_EUNKNOWN = (1 << 31)
+
+} pj_ssl_cert_verify_flag_t;
+
+
+typedef enum pj_ssl_cert_name_type
+{
+ PJ_SSL_CERT_NAME_UNKNOWN = 0,
+ PJ_SSL_CERT_NAME_RFC822,
+ PJ_SSL_CERT_NAME_DNS,
+ PJ_SSL_CERT_NAME_URI,
+ PJ_SSL_CERT_NAME_IP
+} pj_ssl_cert_name_type;
+
/**
* Describe structure of certificate info.
*/
typedef struct pj_ssl_cert_info {
- pj_str_t subject; /**< Subject. */
- pj_str_t issuer; /**< Issuer. */
- unsigned version; /**< Certificate version. */
- pj_time_val validity_start; /**< Validity start. */
- pj_time_val validity_end; /**< Validity end. */
- pj_bool_t validity_use_gmt; /**< Flag if validity date/time
- use GMT. */
+
+ unsigned version; /**< Certificate version */
+
+ pj_uint8_t serial_no[20]; /**< Serial number, array of
+ octets, first index is
+ MSB */
+
+ struct {
+ pj_str_t cn; /**< Common name */
+ pj_str_t info; /**< One line subject, fields
+ are separated by slash */
+ } subject; /**< Subject */
+
+ struct {
+ pj_str_t cn; /**< Common name */
+ pj_str_t info; /**< One line subject, fields
+ are separated by slash.*/
+ } issuer; /**< Issuer */
+
+ struct {
+ pj_time_val start; /**< Validity start */
+ pj_time_val end; /**< Validity end */
+ pj_bool_t gmt; /**< Flag if validity date/time
+ use GMT */
+ } validity; /**< Validity */
+
+ struct {
+ unsigned cnt; /**< # of entry */
+ struct {
+ pj_ssl_cert_name_type type;
+ /**< Name type */
+ pj_str_t name; /**< The name */
+ } *entry; /**< Subject alt name entry */
+ } subj_alt_name; /**< Subject alternative
+ name extension */
+
} pj_ssl_cert_info;
@@ -93,6 +202,39 @@ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_files(pj_pool_t *pool,
pj_ssl_cert_t **p_cert);
+/**
+ * Dump SSL certificate info.
+ *
+ * @param ci The certificate info.
+ * @param prefix Prefix string for each line.
+ * @param buf The buffer where certificate info will be printed on.
+ * @param buf_size The buffer size.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_cert_info_dump(const pj_ssl_cert_info *ci,
+ const char *prefix,
+ char *buf,
+ pj_size_t buf_size);
+
+
+/**
+ * Get SSL certificate verification error messages from verification status.
+ *
+ * @param verify_status The SSL certificate verification status.
+ * @param error_strings Array of strings to receive the verification error
+ * messages.
+ * @param count On input it specifies maximum error messages should be
+ * retrieved. On output it specifies the number of error
+ * messages retrieved.
+ *
+ * @return PJ_SUCCESS when successful.
+ */
+PJ_DECL(pj_status_t) pj_ssl_cert_verify_error_st(pj_uint32_t verify_status,
+ const char *error_strings[],
+ unsigned *count);
+
+
/**
* Cipher suites enumeration.
*/
@@ -363,13 +505,18 @@ typedef struct pj_ssl_sock_info
/**
* Describes active local certificate info.
*/
- pj_ssl_cert_info local_cert_info;
+ pj_ssl_cert_info *local_cert_info;
/**
* Describes active remote certificate info.
*/
- pj_ssl_cert_info remote_cert_info;
-
+ pj_ssl_cert_info *remote_cert_info;
+
+ /**
+ * Status of peer certificate verification.
+ */
+ pj_uint32_t verify_status;
+
} pj_ssl_sock_info;
@@ -523,11 +670,11 @@ typedef struct pj_ssl_sock_param
pj_bool_t require_client_cert;
/**
- * When secure socket is acting as client (perform outgoing connection)
- * and it needs to verify server name (e.g: host or domain name) by
- * matching it to the name specified in the server certificate. This
- * setting is useful when the server is hosting multiple domains for
- * the same listening socket.
+ * Server name indication. When secure socket is acting as client
+ * (perform outgoing connection) and the server may host multiple
+ * 'virtual' servers at a single underlying network address, setting
+ * this will allow client to tell the server a name of the server
+ * it is contacting.
*
* Default value is zero/not-set.
*/
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)