Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added pjlib/build/clicert.der
Binary file not shown.
25 changes: 25 additions & 0 deletions pjlib/build/clicert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEPjCCAiagAwIBAgIBAjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlwanNp
cC5sYWIwHhcNMjYxMDA4MTAwNTEzWhcNMzAxMjA2MTAwNTEzWjAbMRkwFwYDVQQD
DBBjbGllbnQucGpzaXAubGFiMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAp/pwuY8gnNAkWTTTpEazOnmSM5ywqfCFW4h0sWlbyzpWVkPnyrYcn+SanQZk
wZYolpazWx9+q5GA7ilBkCogcG44Qu5HWytW25iKMLgcPf/JhgGpvwW1G1UQseNK
l3PtVA68jyvpTSFiIi1FtgC5Q9gkxq7IzfdvXd/1yiTdfKd1w3MOjF57RJchIZL+
x3mK2itYQ3RyZ80LN5DT4cmK3hS5j0rktJHJuRKZt9LKvef6O1upM9iEnSSaJA0i
30um8OO3mc5HCvX3VNYxw0dPUBHb9gQm4574N7/t0OtrIEMQkHpO+5BHHnB5dx2A
kX73kOxoudiRGsAfH2btpaDToQIDAQABo4GTMIGQMAwGA1UdEwEB/wQCMAAwDgYD
VR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMCMBsGA1UdEQQUMBKCEGNs
aWVudC5wanNpcC5sYWIwHQYDVR0OBBYEFGmTl3GUTyJdbS//27kRPoc9LmqgMB8G
A1UdIwQYMBaAFF6rnJ5vs8hTda7vbDQYWmekykW3MA0GCSqGSIb3DQEBCwUAA4IC
AQAgLvWuAIPpiinnZI2ROBcNd/u1S2W1kQ2qqhbwCpo/w5gaBjQyC+lhqJcPIrop
/8VLc3F2aa0PwLKEyx5PHSFXfjbZxI2RqPKE5+9nh5ROO7EkLZKwJSmQb9jQ6EmF
dFYMTbX42T+mvDOPLh/QembVfnyUDgzrteCTwO9pK6y4KHUh8DZdHYOOobrgJ2Ly
3y6G0kU4VDKtOOvAA2Lpjn/Dg0PtjMbRpYUdPGVs0Y93p3IgrRTSKD3NAECxtEZN
rnvTOjmY32J1C9jLA1hJa9nvNbON7sQglofJbhgygC4RU1ZsacArkG91dT7wTbOT
P8yGkuVfJxTtgvuUuI8hz8Y/6fF1MV3ayxNV/X6YSJdgukljQtzYtqsy26kFBLVq
m4EWdjtpTeW4YPYWYxhRhr4m8njBG68sfJTaOssoF0k67rehPBOGzLtF95qcFNls
xxQNrqbVWRdr+8a7QY3GT7tX0lfIk3Epo0VsjrF5OEM8/1dV2pSFGWbCVvMaKUPx
+zqzQKNCaUwqrQZCuMSGDOAmOb2VWhZZOwmKt+J0cMi5qI2uW5XpaVUBKM9bzPjs
Liqn1Q7t5XYZkC7j2JkHbn4k2b+s99Qz1AtGxnGnvarXyRbIE1bawC8br4jtucMs
WR0z5tKFtHBKCkiPPadxNiD7hrvlc8c0y8geRCZ807lAmQ==
-----END CERTIFICATE-----
30 changes: 30 additions & 0 deletions pjlib/build/cliprivkey.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQ9rtm/f1rrMTBckut
zVwvhAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEM+p44rDxIWJMaBG
UqePmlgEggTQQblz8oxV7H1FBxNQVPA8sn6Yae7/gV52JlvMmtLPFmMx9p5fXtkW
B4mQzHM5b2Va262XKJZJHd/WnfleKfNH3kfzALyTov+JyOiMmxu3PlNaDbnZoNNH
b6CCIzjxuzDqRfOsGtTZp1zYJu/EhDe2o2ChqBoDAy9Kp9FYr5zX+IQmqV0CKl7H
x5a0IfK6IUzY+z3pKHbrjRiA0eGHtA72qypYZbZ+JEdTk+IDcjXK62AcSMXf3BKm
8uXRhVYVgewmm7mnb51qhyfCSOg/h/f9kyBqHZ3L30WfBjZfhFFDXgOK4GpmWxhz
n2d03AWTrt1XMLMfeY1M/Fm7Os1oAR5uoq7xuO/7L14rkO9e+3SOtTD4mBuqd7gh
VHhitLc3odpCFY2Wm+hBHESkyOOzGFd9LLO5hPx4kSNXduT2xfEjSCF6tnbIMqGO
3Use2NtxZYjQ1axvDzaj1ANR/o/HiT9SsXSLbtZ3o9uJBLG+uhB88d/6+AnHzTdC
OYVykXxQ+xhagKlC5QCgyP+2MB66rLHV2aq0U/oHLv8zcCQ2Ywmy6tedCgQ+qy/p
IJFFTeBHtRN+8zfrVY5iXrZC0zrrV8paxjAE5ldUyQtl2CLvNBpibUshK16GsUJX
MNJCloQNdafECZwLZLh1vfT0cRX+74lJiaXzenc+vudXYoHJIqJfDxp4fpm+eVuj
dUBLKHuv9IWIdEcyO1pvBJwtPZG6hwMp42Rn1cEngpnr37Q2mSE5N+FWptwmA7t3
6tbtoglCv5Il2NFINv4l1YtIJPiiFw9Za2mF9ArX7xm0PvE6WA7iKirqQnoUlO6L
MQCuUZvNFn2CIefuj76MrSLvTIORYbOBrE2Rpys7ZlWbrDtPNsmW9WRcGYa3GBGp
/7TqYS6Ob1h9egiiht+OEKtUjLSTG6Ta1OH2e4fNOa920qpBUVQvVBZZ54Kug/WV
uriQtZ4QK4TBBC4GzceoT++Umc/SG3lzkYQO5q8Ertk0BbJ7LHPN/DDjB8xDJ70V
6VRUYoN4gSqhXAvkOqsV8SwcwDI3vKm0EusemjZBkTykl/eJD95sfbTNDwIJWN3z
3ib2djhXZ/++kvNOz3U/kAyb5uVjIPLEZtms3l5YCWtm+alaD1NxiEga7z0zD/+M
lGux/rlw6eO/oboZXlWNhpleZIlXSFERZxtuc55xY/rzvpWBpTc3Nw8OJeJXyXTx
yVWX0xKepKr0DVwDbljKUpFt+x8/noU9CHvxq6Z8L7WZsitw9cvCvEnvxu8PEkI/
isD5TtdSJWwiTrAAWYo/pJHmsXTWb/1K18VPaJ/53p5aHABgTUdxygmXmnLr1EHA
0l0SVjKqbB7d7kcyN/dQ+0bUcsi9mvBTHodzhQBRwL+I5jo+cfx2scSYFq8O0sYL
eQqdD4WRQ6Fcl0BOxlPil7wVjkM0At9hMWsCyhsEx2P6AmtzM6NBg3lpabOm0DU4
m3uLmNRWA1cRCeSWPEWDvlglWopCv04DMY8GQkt/G4TXhTk/C8ogouNbYRFbAJAB
rDwxSOTRNGTInH3fU1mTV1LfyY3TN6WGQWXS40dSiXm4kcVLHsad9wY+1b0k62pg
tzKAKMUXzWhJ0i9+w3G3xvf7GBtLkWJ2clHfdspTYAWsKilcoBsfx5c=
-----END ENCRYPTED PRIVATE KEY-----
15 changes: 10 additions & 5 deletions pjlib/src/pj/ssl_sock_gtls.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018-2018 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2018-2026 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2014-2017 Savoir-faire Linux.
* (https://www.savoirfairelinux.com)
*
Expand Down Expand Up @@ -1141,9 +1141,13 @@ static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
const gnutls_datum_t *certs;
unsigned int certslen = 0;
int ret = GNUTLS_CERT_INVALID;
pj_bool_t updated = PJ_FALSE;

pj_assert(ssock->ssl_state == SSL_STATE_ESTABLISHED);

/* Both raw chains live in info_pool, so release them together */
pj_pool_reset(ssock->info_pool);

/* Get active local certificate */
us = gnutls_certificate_get_ours(gssock->session);
if (!us)
Expand All @@ -1159,17 +1163,18 @@ static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
goto us_out;

tls_cert_get_info(ssock->pool, &ssock->local_cert_info, cert);
pj_pool_reset(ssock->info_pool);
tls_cert_get_chain_raw(ssock->info_pool, &ssock->local_cert_info, us, 1);
updated = PJ_TRUE;

us_out:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the raw chains now cleared at the top of the function, there's a new mismatch on this path. If gnutls_x509_crt_init() succeeds but both imports fail (e.g. on renegotiation), we jump here with cert != NULL, so local_cert_info is not zeroed. Subject, issuer, serial and SAN are kept from the previous handshake, but raw_chain is now empty. Before this PR, the stale raw chain at least matched the stale identity fields.

peer_out has the same issue for remote_cert_info. Suggest clearing the whole struct on any failure, not only when cert is NULL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, that mismatch was real. Changed it to track whether each half completed and zero the whole pj_ssl_cert_info otherwise, at both us_out and peer_out.

Before: the struct was zeroed only when cert was NULL, so the init-succeeded-but-both-imports-failed path kept stale identity fields next to an empty raw chain. After: each half either fully refreshes its struct or fully clears it. That also made the two raw_chain bzeros at the top redundant, so they're gone and the single pj_pool_reset() is all that's left there.

Tradeoff is the same one noted in the PR description, just wider: a failed import now reports empty identity fields too, instead of the previous handshake's values.

tls_last_error = ret;
if (cert)
gnutls_x509_crt_deinit(cert);
else
if (!updated)
pj_bzero(&ssock->local_cert_info, sizeof(pj_ssl_cert_info));

cert = NULL;
updated = PJ_FALSE;

/* Get active remote certificate */
certs = gnutls_certificate_get_peers(gssock->session, &certslen);
Expand All @@ -1187,15 +1192,15 @@ static void ssl_update_certs_info(pj_ssl_sock_t *ssock)
goto peer_out;

tls_cert_get_info(ssock->pool, &ssock->remote_cert_info, cert);
pj_pool_reset(ssock->info_pool);
tls_cert_get_chain_raw(ssock->info_pool, &ssock->remote_cert_info, certs,
certslen);
updated = PJ_TRUE;

peer_out:
tls_last_error = ret;
if (cert)
gnutls_x509_crt_deinit(cert);
else
if (!updated)
pj_bzero(&ssock->remote_cert_info, sizeof(pj_ssl_cert_info));
}

Expand Down
134 changes: 129 additions & 5 deletions pjlib/src/pjlib-test/ssl_sock.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2008-2026 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* This program is free software; you can redistribute it and/or modify
Expand Down Expand Up @@ -66,7 +66,9 @@ struct test_state
pj_pool_t *pool; /* pool */
pj_ioqueue_t *ioqueue; /* ioqueue */
pj_ssl_sock_t *accepted_ssock; /* accepted server-side socket */
struct test_state *accepted_st; /* state of the accepted socket */
pj_bool_t is_server; /* server role flag */
const char *cert_der; /* DER of this end's own certificate */
pj_bool_t is_verbose; /* verbose flag, e.g: cert info */
pj_bool_t echo; /* echo received data */
pj_status_t err; /* error flag */
Expand Down Expand Up @@ -117,6 +119,61 @@ static void dump_ssl_info(const pj_ssl_sock_info *si)
}
}

/* The local raw chain must hold the local certificate, in storage of its
* own rather than the peer chain's. der_name is the DER of the certificate
* this end presents, or NULL to compare the storage only. Backends that
* don't report the local raw chain, e.g. OpenSSL, leave cnt zero and are
* skipped.
*/
static pj_status_t check_raw_chain(const pj_ssl_sock_info *si,
pj_pool_t *pool,
const char *der_name)
{
const pj_ssl_cert_info *lci = si->local_cert_info;
const pj_ssl_cert_info *rci = si->remote_cert_info;
pj_str_t der_file;
pj_oshandle_t fd = NULL;
pj_ssize_t size;
pj_str_t der;
pj_status_t status;

if (!lci || lci->raw_chain.cnt == 0)
return PJ_SUCCESS;

if (rci && rci->raw_chain.cnt &&
lci->raw_chain.cert_raw == rci->raw_chain.cert_raw)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check only fails if the allocator returns the exact same address for both chains. It never checks that the local chain actually holds the local certificate. With PJ_POOL_DEBUG or a malloc-backed pool policy (e.g. under ASan), or if the remote chain lands at a different offset after the reset, the local cert_raw can still dangle while differing from the remote one, and the test passes on the unfixed code.

Could it compare the content instead, e.g. check that lci->raw_chain.cert_raw[0] matches the DER of the configured local certificate (or at least that it differs from rci->raw_chain.cert_raw[0])?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, it now compares lci->raw_chain.cert_raw[0] against cacert.der, which is already in pjlib/build/ and is byte-identical to the cacert.pem the test configures.

Worth flagging though: in echo_test the client reuses the server's certificate, so the content comparison can't catch this particular bug on its own. I checked that rather than assumed it, by disabling the array comparison and running the strengthened check against the old ssl_sock_gtls.c: it passes. The dangling local chain points at the peer entry, and the peer DER is the same bytes, so the content matches. The array comparison is what fires there, on both ends.

So both checks are in. The content one catches a chain overwritten with something else, and it would catch the dangle on any setup where the two ends present different certificates. Your allocator point still stands for the overlap half, and making the test independent of the pool handing back the same address needs a second certificate for the client. Say the word and I'll add one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please add a second certificate for the client. As you found, with the same cert on both ends the content check can't tell a dangling local chain from a correct one, and the overlap check depends on the pool returning the same address. With different certs on each side, the content check catches the bug no matter how the allocator behaves.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. clicert.pem plus cliprivkey.pem and clicert.der, a leaf issued by the existing cacert.pem CA with its own 2048-bit key, encrypted PKCS#8 with the same privkeypass and the same PBES2 parameters as privkey.pem, CN=client.pjsip.lab, EKU clientAuth, expiring inside the CA's window. The client loads it in the client_provide_cert case and check_raw_chain() takes the expected DER from the test state, so each end compares against the certificate it actually presents.

Before: both ends presented cacert.pem, so the content comparison matched a dangling local chain and only the storage comparison could fire, which is the allocator dependency you flagged. After: the content comparison is decisive. I disabled the storage comparison and ran the test against the unpatched ssl_sock_gtls.c: both the client and the accepted socket report invalid local raw chain on content alone.

One backend keeps the old arrangement. The Apple backends take the private key from the Keychain and only the server key is in privkey.p12, so TEST_CLI_OWN_CERT is 0 for PJ_SSL_SOCK_IMP_APPLE and PJ_SSL_SOCK_IMP_DARWIN, the client there keeps reusing the server certificate, and the storage comparison does the work as before. Tradeoff is three new files in pjlib/build/ and a test CA that now signs something, against a check that no longer depends on the pool returning the same address.

{
return PJ_EBUG;
}

if (!der_name)
return PJ_SUCCESS;

der_file = pj_str((char*)der_name);
size = (pj_ssize_t)pj_file_size(der_file.ptr);
if (size <= 0)
return PJ_ENOTFOUND;

status = pj_file_open(pool, der_file.ptr, PJ_O_RDONLY, &fd);
if (status != PJ_SUCCESS)
return status;

der.ptr = (char*)pj_pool_alloc(pool, size);
status = pj_file_read(fd, der.ptr, &size);
der.slen = size;
pj_file_close(fd);
if (status != PJ_SUCCESS)
return status;

if (lci->raw_chain.cert_raw[0].slen != der.slen ||
pj_memcmp(lci->raw_chain.cert_raw[0].ptr, der.ptr, der.slen))
{
return PJ_EBUG;
}

return PJ_SUCCESS;
}


static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
pj_status_t status)
Expand All @@ -138,6 +195,12 @@ static pj_bool_t ssl_on_connect_complete(pj_ssl_sock_t *ssock,
goto on_return;
}

status = check_raw_chain(&info, st->pool, st->cert_der);
if (status != PJ_SUCCESS) {
app_perror("...ERROR invalid local raw chain", status);
goto on_return;
}

pj_sockaddr_print((pj_sockaddr_t*)&info.local_addr, buf1, sizeof(buf1), 1);
pj_sockaddr_print((pj_sockaddr_t*)&info.remote_addr, buf2, sizeof(buf2), 1);
PJ_LOG(3, ("", "...Connected %s -> %s!", buf1, buf2));
Expand Down Expand Up @@ -218,13 +281,25 @@ static pj_bool_t ssl_on_accept_complete(pj_ssl_sock_t *ssock,

/* Track accepted socket for cleanup */
parent_st->accepted_ssock = newsock;
parent_st->accepted_st = 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;
}

status = check_raw_chain(&info, st->pool, st->cert_der);
if (status != PJ_SUCCESS) {
app_perror("...ERROR invalid local raw chain", status);
/* Report through the parent and keep echoing: closing from this
* callback leaves on_handshake_complete() using a released
* ssock->parent, and perf_test() waits for every client to finish.
*/
parent_st->err = status;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning PJ_TRUE here without starting a read leaves the server socket idle, and that doesn't fail cleanly everywhere:

  • perf_test() also uses ssl_on_accept_complete(), but its wait loop is while (clients_num) and it only checks state_serv.err afterwards. The server never echoes, so no client finishes and the test hangs until the CI runner times out.
  • In echo_test(), nothing closes the accepted socket. Its cleanup doesn't close state_serv.accepted_ssock (unlike client_non_ssl() and large_msg_test()), so the socket and its pool leak, and the ioqueue is destroyed with the key still registered.

Suggestion: record the error in parent_st->err but don't return early. Fall through to pj_ssl_sock_start_read2() so the connection runs to completion and the error is reported at the end. Also close state_serv.accepted_ssock in echo_test()'s cleanup.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, both parts.

Before: the callback set parent_st->err and returned PJ_TRUE without starting a read, so perf_test() sat in while (clients_num) until the runner timed out, and echo_test() left the accepted socket and its pool behind. After: it records the error, resets status, and falls through to pj_ssl_sock_start_read2() and the send loop, so the connection runs to completion and the error surfaces at the end.

For the cleanup I had to pick between closing state_serv.accepted_ssock unconditionally, as client_non_ssl() and large_msg_test() do, and guarding it. Unconditional is a double close here: in echo_test() the accepted socket closes itself from ssl_on_data_read() on EOF, and without a grp_lock pj_ssl_sock_close() runs ssl_on_destroy() synchronously and releases ssock->pool, which is where ssock lives, so the is_closing guard never gets a chance to run. The accept callback now stores the accepted socket's own test state in the parent, and echo_test() closes on !err && !done, matching the guard already used for ssock_cli. All three of the socket's self-close paths use exactly that condition.

status = PJ_SUCCESS;
}

pj_sockaddr_print(src_addr, buf, sizeof(buf), 1);
PJ_LOG(3, ("", "...Accepted connection from %s", buf));

Expand Down Expand Up @@ -691,6 +766,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
pj_sockaddr addr, listen_addr;
pj_ssl_cipher ciphers[1];
pj_ssl_cert_t *cert = NULL;
pj_ssl_cert_t *cli_cert = NULL;
pj_status_t status;

pool = pj_pool_create(mem, "ssl_echo", 256, 256, NULL);
Expand Down Expand Up @@ -731,6 +807,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
state_serv.echo = PJ_TRUE;
state_serv.is_server = PJ_TRUE;
state_serv.is_verbose = PJ_TRUE;
state_serv.cert_der = CERT_DER_FILE;

status = pj_ssl_sock_create(pool, &param, &ssock_serv);
if (status != PJ_SUCCESS) {
Expand Down Expand Up @@ -834,6 +911,7 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
state_cli.pool = pool;
state_cli.check_echo = PJ_TRUE;
state_cli.is_verbose = PJ_TRUE;
state_cli.cert_der = CERT_DER_FILE;

{
/* srand() should be done centrally (blp)
Expand Down Expand Up @@ -875,6 +953,8 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
}
#else
{
cli_cert = cert;

if (!client_provide_cert) {
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t null_str = pj_str("");
Expand All @@ -883,11 +963,12 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
/* Reset any certificate & private keys previously set */
status = pj_ssl_cert_load_from_files(pool, &ca_file, &null_str,
&null_str, &null_str, &cert);
&null_str, &null_str,
&cli_cert);

/* Also reset any direct backend objects */
pj_bzero(&cert_direct, sizeof(cert_direct));
pj_ssl_cert_load_direct(pool, &cert_direct, &cert);
pj_ssl_cert_load_direct(pool, &cert_direct, &cli_cert);
#else
pj_ssl_cert_buffer null_buf, ca_buf;

Expand All @@ -899,15 +980,53 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,
}

status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &null_buf,
&null_buf, &null_str, &cert);
&null_buf, &null_str,
&cli_cert);
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}

}
#if TEST_CLI_OWN_CERT
else {
/* A credential of its own, so each end's local certificate
* chain differs from the peer's in content as well as storage.
*/
pj_str_t ca_file = pj_str(CERT_CA_FILE);
pj_str_t cert_file = pj_str(CERT_CLI_FILE);
pj_str_t privkey_file = pj_str(CERT_CLI_PRIVKEY_FILE);
pj_str_t privkey_pass = pj_str(CERT_PRIVKEY_PASS);
pj_ssl_cert_t *own_cert = NULL;

#if (defined(TEST_LOAD_FROM_FILES) && TEST_LOAD_FROM_FILES==1)
status = pj_ssl_cert_load_from_files(pool, &ca_file, &cert_file,
&privkey_file, &privkey_pass,
&own_cert);
#else
pj_ssl_cert_buffer ca_buf, cert_buf, privkey_buf;

status = pj_ssl_sock_set_certificate(ssock_cli, pool, cert);
status = load_cert_to_buf(pool, &ca_file, &ca_buf);
if (status == PJ_SUCCESS)
status = load_cert_to_buf(pool, &cert_file, &cert_buf);
if (status == PJ_SUCCESS)
status = load_cert_to_buf(pool, &privkey_file, &privkey_buf);
if (status == PJ_SUCCESS) {
status = pj_ssl_cert_load_from_buffer(pool, &ca_buf, &cert_buf,
&privkey_buf,
&privkey_pass, &own_cert);
}
#endif
if (status != PJ_SUCCESS) {
goto on_return;
}

cli_cert = own_cert;
state_cli.cert_der = CERT_CLI_DER_FILE;
}
#endif

status = pj_ssl_sock_set_certificate(ssock_cli, pool, cli_cert);
if (status != PJ_SUCCESS) {
goto on_return;
}
Expand Down Expand Up @@ -978,6 +1097,11 @@ static int echo_test(pj_ssl_sock_proto srv_proto, pj_ssl_sock_proto cli_proto,

if (ssock_serv)
pj_ssl_sock_close(ssock_serv);
if (state_serv.accepted_st && !state_serv.accepted_st->err &&
!state_serv.accepted_st->done)
{
pj_ssl_sock_close(state_serv.accepted_ssock);
}
if (ssock_cli && !state_cli.err && !state_cli.done)
pj_ssl_sock_close(ssock_cli);
if (ioqueue)
Expand Down
20 changes: 20 additions & 0 deletions pjlib/src/pjlib-test/ssl_sock_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,29 @@
# define CERT_CA_FILE CERT_DIR "cacert.pem"
#endif
#define CERT_FILE CERT_DIR "cacert.pem"
#define CERT_DER_FILE CERT_DIR "cacert.der"
#define CERT_PRIVKEY_FILE CERT_DIR "privkey.pem"
#define CERT_PRIVKEY_PASS "privkeypass"

/* A second certificate, issued by the CA above, for the client to present
* when the server requires one. Having each end on its own certificate
* lets a test tell the local certificate chain from the peer's by content.
*/
#define CERT_CLI_FILE CERT_DIR "clicert.pem"
#define CERT_CLI_DER_FILE CERT_DIR "clicert.der"
#define CERT_CLI_PRIVKEY_FILE CERT_DIR "cliprivkey.pem"

/* The Apple backends take the private key from the Keychain, where only the
* server key is stored, so there the client keeps reusing the server
* certificate.
*/
#if (PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_DARWIN) || \
(PJ_SSL_SOCK_IMP == PJ_SSL_SOCK_IMP_APPLE)
# define TEST_CLI_OWN_CERT 0
#else
# define TEST_CLI_OWN_CERT 1
#endif

#define TEST_LOAD_FROM_FILES 1


Expand Down
Loading