Compare commits

...

22 Commits

Author SHA1 Message Date
Nanang Izzuddin d9e2552918 Merge branch 'schannel' of https://github.com/pjsip/pjproject into schannel 2024-03-20 10:24:23 +07:00
Nanang Izzuddin ae1c9ecb9b Shorten docs of certificate lookup on various places (missing commit). 2024-03-20 10:24:10 +07:00
Nanang Izzuddin 43cc47af90
Update ci-win.yml
insert 'schannel' into libvpx unit test
2024-03-18 15:50:26 +07:00
Nanang Izzuddin e9d470b3ed Merge branch 'schannel' of https://github.com/pjsip/pjproject into schannel 2024-03-18 13:57:21 +07:00
Nanang Izzuddin 30a23a5b59 Update SSL socket test: disable cert lookup by default. 2024-03-18 13:55:29 +07:00
Nanang Izzuddin 9f50c22cdd
Update ci-win.yml
Disabled DTLS for Schannel test
2024-03-18 13:08:59 +07:00
Nanang Izzuddin 2d55fbac52
Update ci-win.yml
Replace OpenSSL to Schannel in `windows-with-video-libvpx-unit-test-1`
2024-03-18 12:58:37 +07:00
Nanang Izzuddin 51379b9288 Modifications based on comments 2024-03-15 17:33:51 +07:00
Nanang Izzuddin f044db6d77 Add ssl_sock_schannel.c to PJLIB project (vs14) 2024-03-14 15:35:14 +07:00
Nanang Izzuddin 8fec1902c5 Update PJLIB SSL socket test for Schannel backend. 2024-03-14 15:16:42 +07:00
Nanang Izzuddin 5b31f80326 Implement TLS renego (experimental). 2024-03-07 14:52:29 +07:00
Nanang Izzuddin fcb55f5960 Retry using SCHANNEL_CRED when using SCH_CREDENTIALS fails. 2024-02-23 16:06:30 +07:00
Nanang Izzuddin 7e87d3c117 Slight modifications in TLS cert settings in PJSIP TLS transport & PJNATH TURN socket. 2024-02-23 12:53:28 +07:00
Nanang Izzuddin aed522b581 Fix compile error on non-Windows 2024-02-23 09:58:16 +07:00
Nanang Izzuddin 76fe133ac6 Update certificate settings, fix setting TLS protocol version 2024-02-23 09:48:58 +07:00
Nanang Izzuddin 68e565077c Minors: add comments about pj_status_t & SECURITY_STATUS mapping, cosmetics (aligning equal signs, etc) 2024-02-21 18:39:55 +07:00
Nanang Izzuddin 0095be7706 Add manual verification result, fix bug in setting up TLS protocol version 2024-02-20 18:23:36 +07:00
Nanang Izzuddin 3245fed529 Enable TLS protocol version setting, handling error code mapping & printing 2024-02-19 17:25:50 +07:00
Nanang Izzuddin 8cafc330b4 Implement TLS certificate setting: read cert from OS cert store. 2024-02-16 15:47:25 +07:00
Nanang Izzuddin e57d4a8eb9 Mem management: use preallocated send buffer instead of relying on system alloc/free, reuse self-signed cert. Minor: update log strings. 2024-02-16 10:37:52 +07:00
Nanang Izzuddin bb5853af3d Add getting cipher & cert info 2024-02-15 16:07:16 +07:00
Nanang Izzuddin e7e4ace40a Initial version (Working: connect, send/recv. Not working: param settings, cipher enum, cert info). 2024-02-15 10:04:22 +07:00
18 changed files with 2071 additions and 90 deletions

View File

@ -178,25 +178,10 @@ jobs:
msbuild pjproject-vs14.sln /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true
shell: cmd
windows-with-video-libvpx-unit-test-1:
windows-with-video-libvpx-schannel-unit-test-1:
runs-on: windows-latest
steps:
- uses: actions/checkout@master
- name: get openssl
run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/openssl-1.1.1s-win.zip" -OutFile ".\openssl.zip"
shell: powershell
- name: expand openssl
run: |
Expand-Archive -LiteralPath .\openssl.zip -DestinationPath .; pwd
cd openssl_build
Add-Content ..\openssl_dir.txt $pwd.Path
shell: powershell
- name: check openssl folder
run: |
set /P OPENSSL_DIR=<openssl_dir.txt
dir "%OPENSSL_DIR%\include"
dir "%OPENSSL_DIR%\lib"
shell: cmd
- name: get vpx
run: Invoke-WebRequest -Uri "https://github.com/pjsip/third_party_libs/raw/main/vpx-1.12-win.zip" -Outfile "vpx.zip"
shell: powershell
@ -231,6 +216,8 @@ jobs:
run: |
cd pjlib/include/pj; cp config_site_test.h config_site.h
Add-Content config_site.h "#define PJ_HAS_SSL_SOCK 1"
Add-Content config_site.h "#define PJ_SSL_SOCK_IMP PJ_SSL_SOCK_IMP_SCHANNEL"
Add-Content config_site.h "#undef PJMEDIA_SRTP_HAS_DTLS"
Add-Content config_site.h "#define PJMEDIA_HAS_VIDEO 1"
Add-Content config_site.h "#define PJMEDIA_VIDEO_DEV_HAS_DSHOW 1"
Add-Content config_site.h "#define PJMEDIA_HAS_LIBYUV 1"
@ -258,7 +245,7 @@ jobs:
set /P SDL_DIR=<sdl_dir.txt
cd tests/pjsua/tools
set INCLUDE=%INCLUDE%;%OPENSSL_DIR%\include;%VPX_DIR%\include;%SDL_DIR%\include
set LIB=%LIB%;%OPENSSL_DIR%\lib;%VPX_DIR%\lib;%SDL_DIR%\lib\x86
set LIB=%LIB%;%VPX_DIR%\lib;%SDL_DIR%\lib\x86
call "%PROGRAMFILES%\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
msbuild cmp_wav.vcxproj /p:PlatformToolset=v143 /p:Configuration=Release /p:Platform=win32 /p:UseEnv=true
shell: cmd
@ -268,9 +255,8 @@ jobs:
python-version: '3.10'
- name: unit tests
run: |
$env:OPENSSL_DIR = Get-Content .\openssl_dir.txt
$env:SDL_DIR = Get-Content .\sdl_dir.txt
$env:PATH+=";$env:OPENSSL_DIR\bin;$env:SDL_DIR\lib\x86;"
$env:PATH+=";$env:SDL_DIR\lib\x86;"
cd tests/pjsua; python runall.py
cd ../../pjlib/bin; ./pjlib-test-i386-Win32-vc14-Release.exe --ci-mode
cd ../../pjlib-util/bin; ./pjlib-util-test-i386-Win32-vc14-Release.exe

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug-Dynamic|ARM">
@ -998,6 +998,7 @@
</ClCompile>
<ClCompile Include="..\src\pj\ssl_sock_ossl.c" />
<ClCompile Include="..\src\pj\ssl_sock_gtls.c" />
<ClCompile Include="..\src\pj\ssl_sock_schannel.c" />
<ClCompile Include="..\src\pj\string.c" />
<ClCompile Include="..\src\pj\symbols.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug-Dynamic|Win32'">true</ExcludedFromBuild>

View File

@ -1077,6 +1077,9 @@
/** Using Apple's Network framework */
#define PJ_SSL_SOCK_IMP_APPLE 4
/** Using Windows's Schannel */
#define PJ_SSL_SOCK_IMP_SCHANNEL 5
/**
* Select which SSL socket implementation to use. Currently pjlib supports
* PJ_SSL_SOCK_IMP_OPENSSL, which uses OpenSSL, and PJ_SSL_SOCK_IMP_GNUTLS,

View File

@ -117,6 +117,11 @@ typedef enum pj_ssl_cert_verify_flag_t
*/
PJ_SSL_CERT_ECHAIN_TOO_LONG = (1 << 8),
/**
* The certificate signature is created using a weak hashing algorithm.
*/
PJ_SSL_CERT_EWEAK_SIGNATURE = (1 << 9),
/**
* The server identity does not match to any identities specified in
* the certificate, e.g: subjectAltName extension, subject common name.
@ -145,6 +150,59 @@ typedef enum pj_ssl_cert_name_type
PJ_SSL_CERT_NAME_IP
} pj_ssl_cert_name_type;
/**
* Field type for looking up SSL certificate in the certificate stores.
*/
typedef enum pj_ssl_cert_lookup_type
{
/**
* No certificate to be looked up.
*/
PJ_SSL_CERT_LOOKUP_NONE,
/**
* Lookup by subject, this will lookup any first certificate whose
* subject containing the specified keyword. Note that subject may not
* be unique in the store, so the lookup may end up selecting a wrong
* certificate.
*/
PJ_SSL_CERT_LOOKUP_SUBJECT,
/**
* Lookup by fingerprint/thumbprint (SHA1 hash), this will lookup
* any first certificate whose fingerprint matching the specified
* keyword. The keyword is an array of hash octets.
*/
PJ_SSL_CERT_LOOKUP_FINGERPRINT,
/**
* Lookup by friendly name, this will lookup any first certificate
* whose friendly name containing the specified keyword. Note that
* friendly name may not be unique in the store, so the lookup may end up
* selecting a wrong certificate.
*/
PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME
} pj_ssl_cert_lookup_type;
/**
* Describe structure of certificate lookup criteria.
*/
typedef struct pj_ssl_cert_lookup_criteria
{
/**
* Certificate field type to look.
*/
pj_ssl_cert_lookup_type type;
/*
* Keyword to match on the field specified in \a type.
*/
pj_str_t keyword;
} pj_ssl_cert_lookup_criteria;
/**
* Describe structure of certificate info.
*/
@ -273,6 +331,30 @@ PJ_DECL(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
const pj_str_t *privkey_pass,
pj_ssl_cert_t **p_cert);
/**
* Create credential from OS certificate store, this function will lookup
* certificate using the specified criterias.
*
* Currently this is used by Windows Schannel backend only, it will lookup
* in the Current User store first, if no certificate with the specified
* criteria is not found, it will lookup in the Local Machine store.
*
* Note that for manual verification (e.g: when pj_ssl_sock_param.verify_peer
* is disabled), the backend will provide pre-verification result against
* trusted CA certificates in Current User store only (will not check CA
* certificates in the Local Machine store).
*
* @param pool The pool.
* @param criteria The lookup criteria.
* @param p_cert Pointer to credential instance to be created.
*
* @return PJ_SUCCESS when successful.
*/
PJ_DECL(pj_status_t) pj_ssl_cert_load_from_store(
pj_pool_t *pool,
const pj_ssl_cert_lookup_criteria *criteria,
pj_ssl_cert_t **p_cert);
/**
* Dump SSL certificate info.
*

View File

@ -513,7 +513,8 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
PJ_SUCCESS, &remainder);
PJ_ASSERT_ON_FAIL(
!asock->stream_oriented || remainder <= r->size, {
!ret || !asock->stream_oriented || remainder <= r->size,
{
PJ_LOG(2, ("",
"App bug! Invalid remainder length from "
"activesock on_data_read()."));
@ -589,7 +590,8 @@ static void ioqueue_on_read_complete(pj_ioqueue_key_t *key,
ret = (*asock->cb.on_data_read)(asock, r->pkt, r->size,
status, &remainder);
PJ_ASSERT_ON_FAIL(
!asock->stream_oriented || remainder <= r->size, {
!ret || !asock->stream_oriented || remainder <= r->size,
{
PJ_LOG(2, ("",
"App bug! Invalid remainder length from "
"activesock on_data_read()."));

View File

@ -173,6 +173,10 @@ PJ_DEF(pj_status_t) pj_ssl_cert_get_verify_status_strings(
case PJ_SSL_CERT_ECHAIN_TOO_LONG:
p = "The certificate chain length is too long";
break;
case PJ_SSL_CERT_EWEAK_SIGNATURE:
p = "The certificate signature is created using a weak hashing "
"algorithm";
break;
case PJ_SSL_CERT_EIDENTITY_NOT_MATCH:
p = "The server identity does not match to any identities "
"specified in the certificate";

View File

@ -52,9 +52,22 @@ static pj_bool_t asock_on_data_sent (pj_activesock_t *asock,
*******************************************************************
*/
static pj_size_t next_pow2(pj_size_t n)
{
/* Next 32-bit power of two */
pj_size_t power = 1;
while (power < n && power < 0x8000000) {
power <<= 1;
}
return power;
}
static pj_status_t circ_init(pj_pool_factory *factory,
circ_buf_t *cb, pj_size_t cap)
{
/* Round-up cap */
cap = next_pow2(cap);
cb->cap = cap;
cb->readp = 0;
cb->writep = 0;
@ -68,17 +81,24 @@ static pj_status_t circ_init(pj_pool_factory *factory,
/* Allocate circular buffer */
cb->buf = pj_pool_alloc(cb->pool, cap);
if (!cb->buf) {
pj_pool_release(cb->pool);
pj_pool_secure_release(&cb->pool);
return PJ_ENOMEM;
}
return PJ_SUCCESS;
}
static void circ_reset(circ_buf_t* cb)
{
cb->readp = 0;
cb->writep = 0;
cb->size = 0;
}
static void circ_deinit(circ_buf_t *cb)
{
if (cb->pool) {
pj_pool_release(cb->pool);
pj_pool_secure_release(&cb->pool);
cb->pool = NULL;
}
}
@ -104,6 +124,8 @@ static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
pj_size_t tbc = PJ_MIN(size_after, len);
pj_size_t rem = len - tbc;
pj_assert(cb->size >= len);
pj_memcpy(dst, cb->buf + cb->readp, tbc);
pj_memcpy(dst + tbc, cb->buf, rem);
@ -113,6 +135,21 @@ static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len)
cb->size -= len;
}
/* Cancel previous read, partially or fully.
* Should be called in the same mutex block as circ_read().
*/
static void circ_read_cancel(circ_buf_t* cb, pj_size_t len)
{
pj_assert(cb->cap - cb->size >= len);
if (cb->readp < len)
cb->readp = cb->cap - (len - cb->readp);
else
cb->readp -= len;
cb->size += len;
}
static pj_status_t circ_write(circ_buf_t *cb,
const pj_uint8_t *src, pj_size_t len)
{
@ -121,14 +158,8 @@ static pj_status_t circ_write(circ_buf_t *cb,
/* Minimum required capacity */
pj_size_t min_cap = len + cb->size;
/* Next 32-bit power of two */
min_cap--;
min_cap |= min_cap >> 1;
min_cap |= min_cap >> 2;
min_cap |= min_cap >> 4;
min_cap |= min_cap >> 8;
min_cap |= min_cap >> 16;
min_cap++;
/* Round-up minimum capacity */
min_cap = next_pow2(min_cap);
/* Create a new pool to hold a bigger buffer, using the same factory */
pj_pool_t *pool = pj_pool_create(cb->pool->factory, "tls-circ%p",
@ -153,7 +184,7 @@ static pj_status_t circ_write(circ_buf_t *cb,
cb->size = old_size;
/* Release the previous pool */
pj_pool_release(cb->pool);
pj_pool_secure_release(&cb->pool);
/* Update circular buffer members */
cb->pool = pool;
@ -1737,7 +1768,7 @@ static pj_status_t ssl_send (pj_ssl_sock_t *ssock,
unsigned flags)
{
pj_status_t status;
int nwritten;
int nwritten = 0;
/* Write the plain data to SSL, after SSL encrypts it, the buffer will
* contain the secured data to be sent via socket. Note that re-
@ -2241,8 +2272,9 @@ static void wipe_buf(pj_str_t *buf)
}
PJ_DEF(void) pj_ssl_cert_wipe_keys(pj_ssl_cert_t *cert)
{
{
if (cert) {
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
wipe_buf(&cert->CA_file);
wipe_buf(&cert->CA_path);
wipe_buf(&cert->cert_file);
@ -2251,6 +2283,10 @@ PJ_DEF(void) pj_ssl_cert_wipe_keys(pj_ssl_cert_t *cert)
wipe_buf(&cert->CA_buf);
wipe_buf(&cert->cert_buf);
wipe_buf(&cert->privkey_buf);
#else
cert->criteria.type = PJ_SSL_CERT_LOOKUP_NONE;
wipe_buf(&cert->criteria.keyword);
#endif
}
}
@ -2274,6 +2310,7 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool,
const pj_str_t *privkey_pass,
pj_ssl_cert_t **p_cert)
{
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
pj_ssl_cert_t *cert;
PJ_ASSERT_RETURN(pool && (CA_file || CA_path) && cert_file &&
@ -2294,6 +2331,16 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_files2(pj_pool_t *pool,
*p_cert = cert;
return PJ_SUCCESS;
#else
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(CA_file);
PJ_UNUSED_ARG(CA_path);
PJ_UNUSED_ARG(cert_file);
PJ_UNUSED_ARG(privkey_file);
PJ_UNUSED_ARG(privkey_pass);
PJ_UNUSED_ARG(p_cert);
return PJ_ENOTSUP;
#endif
}
PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
@ -2303,6 +2350,7 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
const pj_str_t *privkey_pass,
pj_ssl_cert_t **p_cert)
{
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
pj_ssl_cert_t *cert;
PJ_ASSERT_RETURN(pool && CA_buf && cert_buf && privkey_buf, PJ_EINVAL);
@ -2316,8 +2364,44 @@ PJ_DEF(pj_status_t) pj_ssl_cert_load_from_buffer(pj_pool_t *pool,
*p_cert = cert;
return PJ_SUCCESS;
#else
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(CA_buf);
PJ_UNUSED_ARG(cert_buf);
PJ_UNUSED_ARG(privkey_buf);
PJ_UNUSED_ARG(privkey_pass);
PJ_UNUSED_ARG(p_cert);
return PJ_ENOTSUP;
#endif
}
PJ_DEF(pj_status_t) pj_ssl_cert_load_from_store(
pj_pool_t *pool,
const pj_ssl_cert_lookup_criteria *criteria,
pj_ssl_cert_t **p_cert)
{
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL)
pj_ssl_cert_t *cert;
PJ_ASSERT_RETURN(pool && criteria && p_cert, PJ_EINVAL);
cert = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
pj_memcpy(&cert->criteria, criteria, sizeof(*criteria));
pj_strdup_with_null(pool, &cert->criteria.keyword, &criteria->keyword);
*p_cert = cert;
return PJ_SUCCESS;
#else
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(criteria);
PJ_UNUSED_ARG(p_cert);
return PJ_ENOTSUP;
#endif
}
/* Set SSL socket credentials. */
PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
pj_ssl_sock_t *ssock,
@ -2330,6 +2414,8 @@ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
cert_ = PJ_POOL_ZALLOC_T(pool, pj_ssl_cert_t);
pj_memcpy(cert_, cert, sizeof(pj_ssl_cert_t));
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
pj_strdup_with_null(pool, &cert_->CA_file, &cert->CA_file);
pj_strdup_with_null(pool, &cert_->CA_path, &cert->CA_path);
pj_strdup_with_null(pool, &cert_->cert_file, &cert->cert_file);
@ -2339,6 +2425,10 @@ PJ_DEF(pj_status_t) pj_ssl_sock_set_certificate(
pj_strdup(pool, &cert_->CA_buf, &cert->CA_buf);
pj_strdup(pool, &cert_->cert_buf, &cert->cert_buf);
pj_strdup(pool, &cert_->privkey_buf, &cert->privkey_buf);
#else
pj_strdup_with_null(pool, &cert_->criteria.keyword,
&cert->criteria.keyword);
#endif
ssock->cert = cert_;

View File

@ -152,6 +152,7 @@ struct pj_ssl_sock_t
*/
struct pj_ssl_cert_t
{
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
pj_str_t CA_file;
pj_str_t CA_path;
pj_str_t cert_file;
@ -162,6 +163,9 @@ struct pj_ssl_cert_t
pj_ssl_cert_buffer CA_buf;
pj_ssl_cert_buffer cert_buf;
pj_ssl_cert_buffer privkey_buf;
#else
pj_ssl_cert_lookup_criteria criteria;
#endif
};
/* ssl available ciphers */
@ -205,9 +209,11 @@ static pj_status_t flush_delayed_send(pj_ssl_sock_t *ssock);
static pj_status_t circ_init(pj_pool_factory *factory,
circ_buf_t *cb, pj_size_t cap);
static void circ_deinit(circ_buf_t *cb);
static void circ_reset(circ_buf_t* cb);
static pj_bool_t circ_empty(const circ_buf_t *cb);
static pj_size_t circ_size(const circ_buf_t *cb);
static void circ_read(circ_buf_t *cb, pj_uint8_t *dst, pj_size_t len);
static void circ_read_cancel(circ_buf_t* cb, pj_size_t len);
static pj_status_t circ_write(circ_buf_t *cb,
const pj_uint8_t *src, pj_size_t len);

File diff suppressed because it is too large Load Diff

View File

@ -540,6 +540,39 @@ static pj_status_t load_cert_to_buf(pj_pool_t *pool, const pj_str_t *file_name,
}
#endif
static pj_status_t load_cert_from_store(pj_pool_t *pool,
pj_ssl_cert_t **p_cert)
{
#if 0
/* To test loading certificate from the store, follow these steps:
* 1. Install the certificate & the private-key pair to the store,
* and optionally set the friendly-name for it.
* 2. Update the lookup criteria below (field & keyword).
*/
pj_ssl_cert_lookup_criteria crit = {0};
/* Lookup by subject */
crit.type = PJ_SSL_CERT_LOOKUP_SUBJECT;
pj_cstr(&crit.keyword, "test.pjsip.org");
/* Lookup by friendly-name */
//crit.type = PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME;
//pj_cstr(&crit.keyword, "schannel-test");
/* Lookup by fingerprint */
//crit.type = PJ_SSL_CERT_LOOKUP_FINGERPRINT;
//pj_cstr(&crit.keyword, "\x08\x3a\x6c\xdc\xd0\x19\x59\xec\x28\xc3"
// "\x81\xb8\xc0\x21\x09\xe9\xd5\xf6\x57\x7d");
return pj_ssl_cert_load_from_store(pool, &crit, p_cert);
#else
/* Set no certificate, Schannel will use self-signed cert */
PJ_UNUSED_ARG(pool);
PJ_UNUSED_ARG(p_cert);
return PJ_ENOTFOUND;
#endif
}
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_bool_t req_client_cert, pj_bool_t client_provide_cert)
@ -602,6 +635,19 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
}
/* Set server cert */
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL)
/* Schannel backend currently can only load certificates from
* OS cert store. If the certificate loading fails, we'll skip setting
* certificate, so the SSL socket will create & use a self-signed cert.
*/
status = load_cert_from_store(pool, &cert);
if (status == PJ_SUCCESS) {
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
#else
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
@ -643,6 +689,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
}
#endif
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
if (status != PJ_SUCCESS) {
@ -686,9 +733,22 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
/* Set cert for client */
{
/* Set cert for client.
* Reusing certificate for server above, but if client_provide_cert
* is not set, override the certificate with CA certificate.
*/
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL)
if (client_provide_cert) {
status = load_cert_from_store(pool, &cert);
if (status == PJ_SUCCESS)
status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
#else
{
if (!client_provide_cert) {
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t null_str = pj_str("");
@ -720,6 +780,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
goto on_return;
}
}
#endif
status = pj_ssl_sock_start_connect(ssock_cli, pool, &addr, &listen_addr, pj_sockaddr_get_len(&addr));
if (status == PJ_SUCCESS) {
@ -926,7 +987,41 @@ static int client_non_ssl(unsigned ms_timeout)
goto on_return;
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
pj_time_val_normalize(&param.timeout);
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.is_server = PJ_TRUE;
state_serv.is_verbose = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set cert */
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL)
/* Schannel backend currently can only load certificates from
* OS cert store. If the certificate loading fails, we'll skip setting
* certificate, so the SSL socket will create & use a self-signed cert.
*/
status = load_cert_from_store(pool, &cert);
if (status == PJ_SUCCESS) {
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
#else
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
@ -964,31 +1059,11 @@ static int client_non_ssl(unsigned ms_timeout)
}
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_timeout;
pj_time_val_normalize(&param.timeout);
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.is_server = PJ_TRUE;
state_serv.is_verbose = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
#endif
/* Init bind address */
{
@ -1261,7 +1336,49 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
goto on_return;
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_handshake_timeout;
pj_time_val_normalize(&param.timeout);
/* Init default bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.echo = PJ_TRUE;
state_serv.is_server = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
/* Set cert */
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_SCHANNEL)
/* Schannel backend currently can only load certificates from
* OS cert store. If the certificate loading fails, we'll skip setting
* certificate, so the SSL socket will create & use a self-signed cert.
*/
status = load_cert_from_store(pool, &cert);
if (status == PJ_SUCCESS) {
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
}
#else
{
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_FILE);
@ -1299,39 +1416,11 @@ static int perf_test(unsigned clients, unsigned ms_handshake_timeout)
}
}
pj_ssl_sock_param_default(&param);
param.cb.on_accept_complete2 = &ssl_on_accept_complete;
param.cb.on_connect_complete = &ssl_on_connect_complete;
param.cb.on_data_read = &ssl_on_data_read;
param.cb.on_data_sent = &ssl_on_data_sent;
param.ioqueue = ioqueue;
param.timer_heap = timer;
param.timeout.sec = 0;
param.timeout.msec = ms_handshake_timeout;
pj_time_val_normalize(&param.timeout);
/* Init default bind address */
{
pj_str_t tmp_st;
pj_sockaddr_init(PJ_AF_INET, &addr, pj_strset2(&tmp_st, "127.0.0.1"), 0);
}
/* SERVER */
param.user_data = &state_serv;
state_serv.pool = pool;
state_serv.echo = PJ_TRUE;
state_serv.is_server = PJ_TRUE;
status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
goto on_return;
}
status = pj_ssl_sock_set_certificate(ssock_serv, pool, cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
#endif
status = pj_ssl_sock_start_accept(ssock_serv, pool, &addr, pj_sockaddr_get_len(&addr));
if (status != PJ_SUCCESS) {
@ -1535,6 +1624,15 @@ int ssl_sock_test(void)
* which require SSL server, for now.
*/
/* Schannel backend notes:
* - currently it does not support ciphers settings, so we exclude tests
* whose ciphers setting is specified.
* - TLS protocol older than 1.0 is not supported, TLS 1.0 & 1.1 will be
* disabled soon, TLS 1.3 is supported since Windows 11, so for now
* we only include tests with TLS 1.2.
*/
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
PJ_LOG(3,("", "..echo test w/ TLSv1 and PJ_TLS_RSA_WITH_AES_256_CBC_SHA cipher"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1, PJ_SSL_SOCK_PROTO_TLS1,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
@ -1548,6 +1646,7 @@ int ssl_sock_test(void)
PJ_FALSE, PJ_FALSE);
if (ret != 0)
return ret;
#endif
PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2 vs client TLSv1.2"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_2,
@ -1556,6 +1655,7 @@ int ssl_sock_test(void)
if (ret != 0)
return ret;
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
PJ_LOG(3,("", "..echo test w/ compatible proto: server TLSv1.2+1.3 vs client TLSv1.3"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2 | PJ_SSL_SOCK_PROTO_TLS1_3, PJ_SSL_SOCK_PROTO_TLS1_3,
-1, -1,
@ -1569,9 +1669,10 @@ int ssl_sock_test(void)
PJ_FALSE, PJ_FALSE);
if (ret == 0)
return PJ_EBUG;
#endif
/* We can't set min/max proto for TLS protocol higher than 1.0. */
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_DARWIN)
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_DARWIN && PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
PJ_LOG(3,("", "..echo test w/ incompatible proto: server TLSv1.2 vs client TLSv1.3"));
ret = echo_test(PJ_SSL_SOCK_PROTO_TLS1_2, PJ_SSL_SOCK_PROTO_TLS1_3,
-1, -1,
@ -1584,7 +1685,7 @@ int ssl_sock_test(void)
* deprecated and we only have sec_protocol_options_append_tls_ciphersuite(),
* but there's no API to remove certain or all ciphers.
*/
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE)
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_APPLE && PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
PJ_LOG(3,("", "..echo test w/ incompatible ciphers"));
ret = echo_test(PJ_SSL_SOCK_PROTO_DEFAULT, PJ_SSL_SOCK_PROTO_DEFAULT,
PJ_TLS_RSA_WITH_DES_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
@ -1593,6 +1694,7 @@ int ssl_sock_test(void)
return PJ_EBUG;
#endif
#if (PJ_SSL_SOCK_IMP != PJ_SSL_SOCK_IMP_SCHANNEL)
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,
PJ_TLS_RSA_WITH_AES_256_CBC_SHA, PJ_TLS_RSA_WITH_AES_256_CBC_SHA,
@ -1606,6 +1708,7 @@ int ssl_sock_test(void)
PJ_TRUE, PJ_TRUE);
if (ret != 0)
return ret;
#endif
#if WITH_BENCHMARK
PJ_LOG(3,("", "..performance test"));

View File

@ -223,6 +223,14 @@ typedef struct pj_turn_sock_tls_cfg
*/
pj_str_t password;
/**
* Lookup certificate from OS certificate store with specified criteria.
*
* Currently only used by TLS backend Windows Schannel, please check
* pj_ssl_cert_load_from_store() for more info.
*/
pj_ssl_cert_lookup_criteria cert_lookup;
/**
* The ssl socket parameter.
* These fields are used by TURN TLS:

View File

@ -1389,12 +1389,21 @@ static void turn_on_state(pj_turn_session *sess,
&turn_sock->setting.tls_cfg.privkey_buf,
&turn_sock->setting.tls_cfg.password,
&turn_sock->cert);
} else if (turn_sock->setting.tls_cfg.cert_lookup.type !=
PJ_SSL_CERT_LOOKUP_NONE &&
turn_sock->setting.tls_cfg.cert_lookup.keyword.slen)
{
status = pj_ssl_cert_load_from_store(
turn_sock->pool,
&turn_sock->setting.tls_cfg.cert_lookup,
&turn_sock->cert);
}
if (status != PJ_SUCCESS) {
turn_sock_destroy(turn_sock, status);
pj_grp_lock_release(turn_sock->grp_lock);
return;
}
if (turn_sock->cert) {
pj_turn_sock_tls_cfg_wipe_keys(&turn_sock->setting.tls_cfg);
}

View File

@ -184,6 +184,14 @@ typedef enum pj_ssl_cert_verify_flag_t
PJ_SSL_CERT_EUNKNOWN = 1 << 31
} pj_ssl_cert_verify_flag_t;
typedef enum pj_ssl_cert_lookup_type
{
PJ_SSL_CERT_LOOKUP_NONE,
PJ_SSL_CERT_LOOKUP_SUBJECT,
PJ_SSL_CERT_LOOKUP_FINGERPRINT,
PJ_SSL_CERT_LOOKUP_FRIENDLY_NAME
} pj_ssl_cert_lookup_type;
typedef enum pj_ice_sess_trickle
{
PJ_ICE_SESS_TRICKLE_DISABLED,

View File

@ -2,7 +2,7 @@ pj/types.h pj_status_t pj_constants_ pj_uint8_t pj_int32_t pj_uint32_t pj_uint
pj/file_io.h pj_file_access
pj/log.h pj_log_decoration
pj/sock_qos.h pj_qos_type pj_qos_flag pj_qos_wmm_prio pj_qos_params
pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t
pj/ssl_sock.h pj_ssl_cipher pj_ssl_sock_proto pj_ssl_cert_name_type pj_ssl_cert_verify_flag_t pj_ssl_cert_lookup_type
pjnath/ice_session.h pj_ice_sess_trickle
pjnath/nat_detect.h pj_stun_nat_type

View File

@ -188,6 +188,14 @@ typedef struct pjsip_tls_setting
*/
pj_ssl_cert_buffer privkey_buf;
/**
* Lookup certificate from OS certificate store with specified criteria.
*
* Currently only used by TLS backend Windows Schannel, please check
* pj_ssl_cert_load_from_store() for more info.
*/
pj_ssl_cert_lookup_criteria cert_lookup;
/**
* Password to open private key.
*/
@ -474,6 +482,9 @@ PJ_INLINE(void) pjsip_tls_setting_copy(pj_pool_t *pool,
pj_strdup(pool, &dst->cert_buf, &src->cert_buf);
pj_strdup(pool, &dst->privkey_buf, &src->privkey_buf);
pj_strdup_with_null(pool, &dst->cert_lookup.keyword,
&src->cert_lookup.keyword);
if (src->ciphers_num) {
unsigned i;
dst->ciphers = (pj_ssl_cipher*) pj_pool_calloc(pool, src->ciphers_num,

View File

@ -174,6 +174,25 @@ struct TlsConfig : public PersistentObject
*/
string privKeyBuf;
/**
* Lookup certificate from OS certificate store, this setting will
* specify the field type to lookup.
*
* Currently only used by Windows Schannel backend, see also
* \a pj_ssl_cert_load_from_store() for more info.
*/
pj_ssl_cert_lookup_type certLookupType;
/**
* Lookup certificate from OS certificate store, this setting will
* specify the keyword to match on the field specified in
* \a certLookupType above.
*
* Currently only used by Windows Schannel backend, see also
* \a pj_ssl_cert_load_from_store() for more info.
*/
string certLookupKeyword;
/**
* TLS protocol method from #pjsip_ssl_method. In the future, this field
* might be deprecated in favor of <b>proto</b> field. For now, this field

View File

@ -647,6 +647,16 @@ PJ_DEF(pj_status_t) pjsip_tls_transport_start2( pjsip_endpoint *endpt,
&listener->cert);
if (status != PJ_SUCCESS)
goto on_error;
} else if (listener->tls_setting.cert_lookup.type !=
PJ_SSL_CERT_LOOKUP_NONE &&
listener->tls_setting.cert_lookup.keyword.slen)
{
status = pj_ssl_cert_load_from_store(
pool,
&listener->tls_setting.cert_lookup,
&listener->cert);
if (status != PJ_SUCCESS)
goto on_error;
}
/* Register to transport manager */

View File

@ -192,6 +192,8 @@ pjsip_tls_setting TlsConfig::toPj() const
ts.ca_buf = str2Pj(this->CaBuf);
ts.cert_buf = str2Pj(this->certBuf);
ts.privkey_buf = str2Pj(this->privKeyBuf);
ts.cert_lookup.type = this->certLookupType;
ts.cert_lookup.keyword = str2Pj(this->certLookupKeyword);
ts.method = this->method;
ts.ciphers_num = (unsigned)this->ciphers.size();
ts.proto = this->proto;
@ -221,6 +223,8 @@ void TlsConfig::fromPj(const pjsip_tls_setting &prm)
this->CaBuf = pj2Str(prm.ca_buf);
this->certBuf = pj2Str(prm.cert_buf);
this->privKeyBuf = pj2Str(prm.privkey_buf);
this->certLookupType= prm.cert_lookup.type;
this->certLookupKeyword = pj2Str(prm.cert_lookup.keyword);
this->method = (pjsip_ssl_method)prm.method;
this->proto = prm.proto;
// The following will only work if sizeof(enum)==sizeof(int)
@ -256,6 +260,8 @@ void TlsConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error)
NODE_READ_NUM_T ( this_node, pj_qos_type, qosType);
readQosParams ( this_node, qosParams);
NODE_READ_BOOL ( this_node, qosIgnoreError);
NODE_READ_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType);
NODE_READ_STRING ( this_node, certLookupKeyword);
}
void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
@ -278,6 +284,8 @@ void TlsConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
NODE_WRITE_NUM_T ( this_node, pj_qos_type, qosType);
writeQosParams ( this_node, qosParams);
NODE_WRITE_BOOL ( this_node, qosIgnoreError);
NODE_WRITE_NUM_T ( this_node, pj_ssl_cert_lookup_type, certLookupType);
NODE_WRITE_STRING ( this_node, certLookupKeyword);
}
///////////////////////////////////////////////////////////////////////////////