Skip to content

Commit 85456f7

Browse files
On the PR-8 branch:
Applied all commits from the PR to allow for easier review and testing. Full commit message with comments will follow when merging to trunk. git-svn-id: https://svn.apache.org/repos/asf/serf/branches/PR-8@1926894 13f79535-47bb-0310-9956-ffa450edef68
1 parent 5c251c4 commit 85456f7

File tree

6 files changed

+433
-3
lines changed

6 files changed

+433
-3
lines changed

CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,11 @@ CheckNotFunction("X509_STORE_CTX_get0_chain" "NULL" "SERF_NO_SSL_X509_GET0_CHAIN
305305
CheckNotFunction("ASN1_STRING_get0_data" "NULL" "SERF_NO_SSL_ASN1_STRING_GET0_DATA"
306306
"openssl/asn1.h" "${OPENSSL_INCLUDE_DIR}"
307307
${OPENSSL_LIBRARIES} ${SERF_STANDARD_LIBRARIES})
308+
CheckFunction("OSSL_STORE_open_ex"
309+
"NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL"
310+
"SERF_HAVE_OSSL_STORE_OPEN_EX" "openssl/store.h"
311+
"${OPENSSL_INCLUDE_DIR}" ${OPENSSL_LIBRARIES}
312+
${SERF_STANDARD_LIBRARIES})
308313
CheckFunction("CRYPTO_set_locking_callback" "NULL" "SERF_HAVE_SSL_LOCKING_CALLBACKS"
309314
"openssl/crypto.h" "${OPENSSL_INCLUDE_DIR}"
310315
${OPENSSL_LIBRARIES} ${SERF_STANDARD_LIBRARIES})

SConstruct

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ if conf.CheckFunc('OpenSSL_version_num', ssl_includes):
612612
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_VERSION_NUM'])
613613
if conf.CheckFunc('SSL_set_alpn_protos', ssl_includes, 'C', 'NULL, NULL, 0'):
614614
env.Append(CPPDEFINES=['SERF_HAVE_OPENSSL_ALPN'])
615+
if conf.CheckFunc('OSSL_STORE_open_ex', ssl_includes, 'C',
616+
'NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL'):
617+
env.Append(CPPDEFINES=['SERF_HAVE_OSSL_STORE_OPEN_EX'])
615618
if conf.CheckType('OSSL_HANDSHAKE_STATE', ssl_includes):
616619
env.Append(CPPDEFINES=['SERF_HAVE_OSSL_HANDSHAKE_STATE'])
617620
env = conf.Finish()

buckets/ssl_buckets.c

Lines changed: 289 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@
4444
#ifndef OPENSSL_NO_OCSP /* requires openssl 0.9.7 or later */
4545
#include <openssl/ocsp.h>
4646
#endif
47+
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
48+
#include <openssl/store.h>
49+
#include <openssl/evp.h>
50+
#include <openssl/safestack.h>
51+
#include <openssl/ui.h>
52+
#ifndef sk_EVP_PKEY_new_null
53+
DEFINE_STACK_OF(EVP_PKEY)
54+
#endif
55+
#endif
4756

4857
#ifndef APR_ARRAY_PUSH
4958
#define APR_ARRAY_PUSH(ary,type) (*((type *)apr_array_push(ary)))
@@ -117,6 +126,8 @@
117126
*
118127
*/
119128

129+
static int ssl_x509_ex_data_idx = -1;
130+
120131
typedef struct bucket_list {
121132
serf_bucket_t *bucket;
122133
struct bucket_list *next;
@@ -177,12 +188,20 @@ struct serf_ssl_context_t {
177188
apr_pool_t *cert_pw_cache_pool;
178189
const char *cert_pw_success;
179190

191+
/* Cert uri callbacks */
192+
serf_ssl_need_cert_uri_t cert_uri_callback;
193+
void *cert_uri_userdata;
194+
apr_pool_t *cert_uri_cache_pool;
195+
const char *cert_uri_success;
196+
180197
/* Server cert callbacks */
181198
serf_ssl_need_server_cert_t server_cert_callback;
182199
serf_ssl_server_cert_chain_cb_t server_cert_chain_callback;
183200
void *server_cert_userdata;
184201

185202
const char *cert_path;
203+
const char *cert_uri;
204+
const char *cert_pw;
186205

187206
X509 *cached_cert;
188207
EVP_PKEY *cached_cert_pw;
@@ -1502,6 +1521,12 @@ static apr_status_t do_init_libraries(void* baton)
15021521
OpenSSL_add_all_algorithms();
15031522
#endif
15041523

1524+
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
1525+
if (ssl_x509_ex_data_idx < 0) {
1526+
ssl_x509_ex_data_idx = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1527+
}
1528+
#endif
1529+
15051530
#if APR_HAS_THREADS && defined(SERF_HAVE_SSL_LOCKING_CALLBACKS)
15061531
numlocks = CRYPTO_num_locks();
15071532
apr_pool_create(&ssl_pool, NULL);
@@ -1533,10 +1558,50 @@ static apr_status_t init_ssl_libraries(void)
15331558
return serf__init_once(&init_ctx, do_init_libraries, NULL);
15341559
}
15351560

1561+
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
1562+
1563+
static int ssl_pass_cb(UI *ui, UI_STRING *uis)
1564+
{
1565+
serf_ssl_context_t *ctx = UI_get0_user_data(ui);
1566+
1567+
const char *password;
1568+
apr_status_t status;
1569+
1570+
if (ctx->cert_pw_success) {
1571+
status = APR_SUCCESS;
1572+
password = ctx->cert_pw_success;
1573+
ctx->cert_pw_success = NULL;
1574+
}
1575+
else if (ctx->cert_pw_callback) {
1576+
status = ctx->cert_pw_callback(ctx->cert_pw_userdata,
1577+
ctx->cert_uri,
1578+
&password);
1579+
}
1580+
else {
1581+
return 0;
1582+
}
1583+
1584+
UI_set_result(ui, uis, password);
1585+
1586+
ctx->cert_pw = apr_pstrdup(ctx->pool, password);
1587+
1588+
return 1;
1589+
}
1590+
1591+
#endif
1592+
15361593
static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
15371594
{
15381595
serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
1596+
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
1597+
STACK_OF(X509) *leaves;
1598+
STACK_OF(X509) *intermediates;
1599+
STACK_OF(EVP_PKEY) *keys;
1600+
X509_STORE *requests;
1601+
UI_METHOD *ui_method;
1602+
#endif
15391603
apr_status_t status;
1604+
int retrying_success = 0;
15401605

15411606
serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
15421607
"Server requests a client certificate.\n");
@@ -1547,14 +1612,220 @@ static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
15471612
return 1;
15481613
}
15491614

1615+
#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
1616+
1617+
/* until further notice */
1618+
*cert = NULL;
1619+
*pkey = NULL;
1620+
1621+
leaves = sk_X509_new_null();
1622+
intermediates = sk_X509_new_null();
1623+
keys = sk_EVP_PKEY_new_null();
1624+
requests = X509_STORE_new();
1625+
1626+
ui_method = UI_create_method("passphrase");
1627+
UI_method_set_reader(ui_method, ssl_pass_cb);
1628+
1629+
while (ctx->cert_uri_callback) {
1630+
const char *cert_uri = NULL;
1631+
OSSL_STORE_CTX *store = NULL;
1632+
OSSL_STORE_INFO *info;
1633+
X509 *c;
1634+
STACK_OF(X509_NAME) *requested;
1635+
int type;
1636+
1637+
retrying_success = 0;
1638+
1639+
if (ctx->cert_uri_success) {
1640+
status = APR_SUCCESS;
1641+
cert_uri = ctx->cert_uri_success;
1642+
ctx->cert_uri_success = NULL;
1643+
retrying_success = 1;
1644+
} else {
1645+
status = ctx->cert_uri_callback(ctx->cert_uri_userdata, &cert_uri);
1646+
}
1647+
1648+
if (status || !cert_uri) {
1649+
break;
1650+
}
1651+
1652+
ctx->cert_uri = cert_uri;
1653+
1654+
/* server side request some certs? this list may be empty */
1655+
requested = SSL_get_client_CA_list(ssl);
1656+
1657+
store = OSSL_STORE_open_ex(cert_uri, NULL, NULL, ui_method, ctx, NULL,
1658+
NULL, NULL);
1659+
if (!store) {
1660+
int err = ERR_get_error();
1661+
serf__log(LOGLVL_ERROR, LOGCOMP_SSL, __FILE__, ctx->config,
1662+
"OpenSSL store error (%s): %d %d\n", cert_uri,
1663+
ERR_GET_LIB(err), ERR_GET_REASON(err));
1664+
break;
1665+
}
1666+
1667+
/* walk the store, what are we working with */
1668+
1669+
while (!OSSL_STORE_eof(store)) {
1670+
info = OSSL_STORE_load(store);
1671+
1672+
if (!info) {
1673+
break;
1674+
}
1675+
1676+
type = OSSL_STORE_INFO_get_type(info);
1677+
if (type == OSSL_STORE_INFO_CERT) {
1678+
X509 *c = OSSL_STORE_INFO_get1_CERT(info);
1679+
1680+
int n, i;
1681+
1682+
int is_ca = X509_check_ca(c);
1683+
1684+
/* split into leaves and intermediate certs */
1685+
if (is_ca) {
1686+
sk_X509_push(intermediates, c);
1687+
}
1688+
else {
1689+
sk_X509_push(leaves, c);
1690+
}
1691+
1692+
/* any cert with an issuer matching our requested CAs is also
1693+
* added to the requests list, except for leaf certs which are
1694+
* marked as requested with a flag so we can skip the chain
1695+
* check later. */
1696+
n = sk_X509_NAME_num(requested);
1697+
for (i = 0; i < n; ++i) {
1698+
X509_NAME *name = sk_X509_NAME_value(requested, i);
1699+
if (X509_NAME_cmp(name, X509_get_issuer_name(c)) == 0) {
1700+
if (is_ca) {
1701+
X509_STORE_add_cert(requests, c);
1702+
}
1703+
else {
1704+
X509_set_ex_data(c, ssl_x509_ex_data_idx,
1705+
(void *)1);
1706+
}
1707+
}
1708+
}
1709+
1710+
} else if (type == OSSL_STORE_INFO_PKEY) {
1711+
EVP_PKEY *k = OSSL_STORE_INFO_get1_PKEY(info);
1712+
1713+
sk_EVP_PKEY_push(keys, k);
1714+
}
1715+
1716+
OSSL_STORE_INFO_free(info);
1717+
}
1718+
1719+
/* FIXME: openssl error checking goes here */
1720+
1721+
OSSL_STORE_close(store);
1722+
1723+
/* walk the leaf certificates, choose the best one */
1724+
1725+
while ((c = sk_X509_pop(leaves))) {
1726+
1727+
EVP_PKEY *k = NULL;
1728+
int i, n, found = 0;
1729+
1730+
/* no key, skip */
1731+
n = sk_EVP_PKEY_num(keys);
1732+
for (i = 0; i < n; ++i) {
1733+
k = sk_EVP_PKEY_value(keys, i);
1734+
if (X509_check_private_key(c, k)) {
1735+
found = 1;
1736+
break;
1737+
}
1738+
}
1739+
if (!found) {
1740+
continue;
1741+
}
1742+
1743+
/* CAs requested? if so, skip non matches, if not, accept all */
1744+
if (sk_X509_NAME_num(requested) &&
1745+
!X509_get_ex_data(c, ssl_x509_ex_data_idx)) {
1746+
STACK_OF(X509) *chain;
1747+
1748+
chain = X509_build_chain(c, intermediates, requests, 0, NULL,
1749+
NULL);
1750+
1751+
if (!chain) {
1752+
continue;
1753+
}
1754+
1755+
sk_X509_pop_free(chain, X509_free);
1756+
}
1757+
1758+
/* no best candidate yet? we're in first place */
1759+
if (!*cert) {
1760+
EVP_PKEY_up_ref(k);
1761+
*cert = c; /* don't dup, we're returning this */
1762+
*pkey = k;
1763+
continue;
1764+
}
1765+
1766+
/* were we issued after the previous best? */
1767+
if (ASN1_TIME_compare(X509_get0_notBefore(*cert),
1768+
X509_get0_notBefore(c)) < 0) {
1769+
X509_free(*cert);
1770+
EVP_PKEY_free(*pkey);
1771+
EVP_PKEY_up_ref(k);
1772+
*cert = c; /* don't dup, we're returning this */
1773+
*pkey = k;
1774+
continue;
1775+
}
1776+
1777+
X509_free(c);
1778+
}
1779+
1780+
break;
1781+
}
1782+
1783+
sk_X509_pop_free(leaves, X509_free);
1784+
sk_X509_pop_free(intermediates, X509_free);
1785+
sk_EVP_PKEY_pop_free(keys, EVP_PKEY_free);
1786+
X509_STORE_free(requests);
1787+
UI_destroy_method(ui_method);
1788+
1789+
/* we settled on a cert and key, cache it for later */
1790+
1791+
if (*cert && *pkey) {
1792+
1793+
ctx->cached_cert = *cert;
1794+
ctx->cached_cert_pw = *pkey;
1795+
if (!retrying_success && ctx->cert_cache_pool) {
1796+
const char *c;
1797+
1798+
c = apr_pstrdup(ctx->cert_cache_pool, ctx->cert_uri);
1799+
1800+
apr_pool_userdata_setn(c, "serf:ssl:cert",
1801+
apr_pool_cleanup_null,
1802+
ctx->cert_cache_pool);
1803+
}
1804+
1805+
if (!retrying_success && ctx->cert_pw_cache_pool && ctx->cert_pw) {
1806+
const char *pw;
1807+
1808+
pw = apr_pstrdup(ctx->cert_pw_cache_pool,
1809+
ctx->cert_pw);
1810+
1811+
apr_pool_userdata_setn(pw, "serf:ssl:certpw",
1812+
apr_pool_cleanup_null,
1813+
ctx->cert_pw_cache_pool);
1814+
}
1815+
1816+
return 1;
1817+
}
1818+
1819+
#endif
1820+
15501821
while (ctx->cert_callback) {
15511822
const char *cert_path;
15521823
apr_file_t *cert_file;
15531824
BIO *bio;
15541825
BIO_METHOD *biom;
15551826
PKCS12 *p12;
15561827
int i;
1557-
int retrying_success = 0;
1828+
retrying_success = 0;
15581829

15591830
if (ctx->cert_file_success) {
15601831
status = APR_SUCCESS;
@@ -1704,6 +1975,22 @@ void serf_ssl_client_cert_password_set(
17041975
}
17051976

17061977

1978+
void serf_ssl_cert_uri_set(
1979+
serf_ssl_context_t *context,
1980+
serf_ssl_need_cert_uri_t callback,
1981+
void *data,
1982+
void *cache_pool)
1983+
{
1984+
context->cert_uri_callback = callback;
1985+
context->cert_uri_userdata = data;
1986+
context->cert_cache_pool = cache_pool;
1987+
if (context->cert_cache_pool) {
1988+
apr_pool_userdata_get((void**)&context->cert_uri_success,
1989+
"serf:ssl:certuri", cache_pool);
1990+
}
1991+
}
1992+
1993+
17071994
void serf_ssl_server_cert_callback_set(
17081995
serf_ssl_context_t *context,
17091996
serf_ssl_need_server_cert_t callback,
@@ -1780,6 +2067,7 @@ static serf_ssl_context_t *ssl_init_context(serf_bucket_alloc_t *allocator)
17802067

17812068
ssl_ctx->cert_callback = NULL;
17822069
ssl_ctx->cert_pw_callback = NULL;
2070+
ssl_ctx->cert_uri_callback = NULL;
17832071
ssl_ctx->server_cert_callback = NULL;
17842072
ssl_ctx->server_cert_chain_callback = NULL;
17852073

0 commit comments

Comments
 (0)