Skip to content

Commit d7f9717

Browse files
authored
ZOOKEEPER-4929: Make c client side cert optional in connecting to tls server
Reviewers: kezhuw Author: eseabrook1 Closes #2257 from eseabrook1/relax_client_certificate_requirements
1 parent 178076a commit d7f9717

5 files changed

Lines changed: 88 additions & 27 deletions

File tree

zookeeper-client/zookeeper-client-c/include/zookeeper.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,38 @@ ZOOAPI zhandle_t *zookeeper_init(const char *host, watcher_fn fn,
537537
int recv_timeout, const clientid_t *clientid, void *context, int flags);
538538

539539
#ifdef HAVE_OPENSSL_H
540+
/**
541+
* \brief create a handle to communicate with zookeeper using SSL.
542+
*
543+
* This method creates a new handle and a zookeeper session that corresponds
544+
* to that handle. Session establishment is asynchronous, meaning that the
545+
* session should not be considered established until (and unless) an
546+
* event of state ZOO_CONNECTED_STATE is received.
547+
* \param host comma separated host:port pairs, each corresponding to a zk
548+
* server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002"
549+
* \param cert SSL certificate string. Two formats are supported: server CA
550+
* only e.g. "/path/to/server_ca.cer" and server CA with client certificate e.g.
551+
* "/path/to/server_ca.cer,/path/to/client_cert.pem,/path/to/client_cert.key,client_cert_password".
552+
* \param fn the global watcher callback function. When notifications are
553+
* triggered this function will be invoked.
554+
* \param recv_timeout the timeout for the zookeeper session.
555+
* \param clientid the id of a previously established session that this
556+
* client will be reconnecting to. Pass 0 if not reconnecting to a previous
557+
* session. Clients can access the session id of an established, valid,
558+
* connection by calling \ref zoo_client_id. If the session corresponding to
559+
* the specified clientid has expired, or if the clientid is invalid for
560+
* any reason, the returned zhandle_t will be invalid -- the zhandle_t
561+
* state will indicate the reason for failure (typically
562+
* ZOO_EXPIRED_SESSION_STATE).
563+
* \param context the handback object that will be associated with this instance
564+
* of zhandle_t. Application can access it (for example, in the watcher
565+
* callback) using \ref zoo_get_context. The object is not used by zookeeper
566+
* internally and can be null.
567+
* \param flags reserved for future use. Should be set to zero.
568+
* \return a pointer to the opaque zhandle structure. If it fails to create
569+
* a new zhandle the function returns NULL and the errno variable
570+
* indicates the reason.
571+
*/
540572
ZOOAPI zhandle_t *zookeeper_init_ssl(const char *host, const char *cert, watcher_fn fn,
541573
int recv_timeout, const clientid_t *clientid, void *context, int flags);
542574
#endif

zookeeper-client/zookeeper-client-c/src/zookeeper.c

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2777,27 +2777,30 @@ static int init_ssl_for_socket(zsock_t *fd, zhandle_t *zh, int fail_on_error) {
27772777
errno = EINVAL;
27782778
return ZBADARGUMENTS;
27792779
}
2780-
/*CLIENT CA FILE (With Certificate Chain)*/
2781-
if (SSL_CTX_use_certificate_chain_file(*ctx, fd->cert->cert) != 1) {
2782-
SSL_CTX_free(*ctx);
2783-
LOG_ERROR(LOGCALLBACK(zh), "Failed to load client certificate chain from %s", fd->cert->cert);
2784-
errno = EINVAL;
2785-
return ZBADARGUMENTS;
2786-
}
2787-
/*CLIENT PRIVATE KEY*/
2788-
SSL_CTX_set_default_passwd_cb_userdata(*ctx, fd->cert->passwd);
2789-
if (SSL_CTX_use_PrivateKey_file(*ctx, fd->cert->key, SSL_FILETYPE_PEM) != 1) {
2790-
SSL_CTX_free(*ctx);
2791-
LOG_ERROR(LOGCALLBACK(zh), "Failed to load client private key from %s", fd->cert->key);
2792-
errno = EINVAL;
2793-
return ZBADARGUMENTS;
2794-
}
2795-
/*CHECK*/
2796-
if (SSL_CTX_check_private_key(*ctx) != 1) {
2797-
SSL_CTX_free(*ctx);
2798-
LOG_ERROR(LOGCALLBACK(zh), "SSL_CTX_check_private_key failed");
2799-
errno = EINVAL;
2800-
return ZBADARGUMENTS;
2780+
if (fd->cert->cert != NULL && fd->cert->passwd != NULL && fd->cert->key != NULL)
2781+
{
2782+
/*CLIENT CA FILE (With Certificate Chain)*/
2783+
if (SSL_CTX_use_certificate_chain_file(*ctx, fd->cert->cert) != 1) {
2784+
SSL_CTX_free(*ctx);
2785+
LOG_ERROR(LOGCALLBACK(zh), "Failed to load client certificate chain from %s", fd->cert->cert);
2786+
errno = EINVAL;
2787+
return ZBADARGUMENTS;
2788+
}
2789+
/*CLIENT PRIVATE KEY*/
2790+
SSL_CTX_set_default_passwd_cb_userdata(*ctx, fd->cert->passwd);
2791+
if (SSL_CTX_use_PrivateKey_file(*ctx, fd->cert->key, SSL_FILETYPE_PEM) != 1) {
2792+
SSL_CTX_free(*ctx);
2793+
LOG_ERROR(LOGCALLBACK(zh), "Failed to load client private key from %s", fd->cert->key);
2794+
errno = EINVAL;
2795+
return ZBADARGUMENTS;
2796+
}
2797+
/*CHECK*/
2798+
if (SSL_CTX_check_private_key(*ctx) != 1) {
2799+
SSL_CTX_free(*ctx);
2800+
LOG_ERROR(LOGCALLBACK(zh), "SSL_CTX_check_private_key failed");
2801+
errno = EINVAL;
2802+
return ZBADARGUMENTS;
2803+
}
28012804
}
28022805
/*MULTIPLE HANDSHAKE*/
28032806
SSL_CTX_set_mode(*ctx, SSL_MODE_AUTO_RETRY);

zookeeper-client/zookeeper-client-c/tests/TestClient.cc

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
212212
#endif
213213
#ifdef HAVE_OPENSSL_H
214214
CPPUNIT_TEST(testSSL);
215+
CPPUNIT_TEST(testSSLNoClientCert);
216+
217+
// Order after above two tests as it changes ssl.clientAuth
218+
CPPUNIT_TEST(testSSLServerOnly);
215219
#endif
216220
CPPUNIT_TEST(testCreate);
217221
CPPUNIT_TEST(testCreateContainer);
@@ -314,6 +318,12 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
314318
CPPUNIT_ASSERT(system(cmd) == 0);
315319
}
316320

321+
void startServer(const char *envs) {
322+
char cmd[1024];
323+
sprintf(cmd, "%s %s start %s", envs, ZKSERVER_CMD, getHostPorts());
324+
CPPUNIT_ASSERT(system(cmd) == 0);
325+
}
326+
317327
void stopServer() {
318328
char cmd[1024];
319329
sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts());
@@ -879,15 +889,28 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
879889
}
880890

881891
#ifdef HAVE_OPENSSL_H
882-
void testSSL() {
892+
int checkSSL(const char *path, const char *certs) {
883893
watchctx_t ctx;
884894
zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);
885-
zhandle_t *zk = createSSLClient("127.0.0.1:22281", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password", &ctx);
895+
zhandle_t *zk = createSSLClient("127.0.0.1:22281", certs, &ctx);
886896
CPPUNIT_ASSERT(zk);
887897
int rc = 0;
888-
rc = zoo_create(zk, "/ssl", NULL, -1,
889-
&ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
890-
CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
898+
return zoo_create(zk, path, NULL, -1, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0);
899+
}
900+
901+
void testSSL() {
902+
CPPUNIT_ASSERT_EQUAL((int) ZOK, checkSSL("/ssl", "/tmp/certs/server.crt,/tmp/certs/client.crt,/tmp/certs/clientkey.pem,password"));
903+
}
904+
905+
void testSSLNoClientCert() {
906+
CPPUNIT_ASSERT(ZOK != checkSSL("/ssl-no-client-cert", "/tmp/certs/server.crt"));
907+
}
908+
909+
void testSSLServerOnly() {
910+
stopServer();
911+
startServer("CLIENT_AUTH=none");
912+
913+
CPPUNIT_ASSERT_EQUAL((int) ZOK, checkSSL("/ssl-server-only", "/tmp/certs/server.crt"));
891914
}
892915
#endif
893916

zookeeper-client/zookeeper-client-c/tests/zkServer.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ ZOOPORT=${ZOOPORT:-"22181"}
2424
# Some tests are setting the maxClientConnections. When it is not set, we fallback to default 100
2525
ZKMAXCNXNS=${ZKMAXCNXNS:-"100"}
2626

27+
CLIENT_AUTH=${CLIENT_AUTH:-"need"}
28+
2729
EXTRA_JVM_ARGS=${EXTRA_JVM_ARGS:-""}
2830

2931
if [[ -z $1 ]]; then
@@ -163,7 +165,7 @@ case $1 in
163165
)
164166

165167
# ===== prepare the configs
166-
sed "s#TMPDIR#$tmp_dir#g;s#CERTDIR#$certs_dir#g;s#MAXCLIENTCONNECTIONS#$ZKMAXCNXNS#g;s#CLIENTPORT#$ZOOPORT#g" "$tests_dir/zoo.cfg" >"$tmp_dir/zoo.cfg"
168+
sed "s#TMPDIR#$tmp_dir#g;s#CERTDIR#$certs_dir#g;s#MAXCLIENTCONNECTIONS#$ZKMAXCNXNS#g;s#CLIENTPORT#$ZOOPORT#g;s#CLIENT_AUTH#$CLIENT_AUTH#g" "$tests_dir/zoo.cfg" >"$tmp_dir/zoo.cfg"
167169
if [[ -n $read_only ]]; then
168170
# we can put the new server to read-only mode by starting only a single instance of a three node server
169171
{

zookeeper-client/zookeeper-client-c/tests/zoo.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ localSessionsEnabled=true
88
clientPort=CLIENTPORT
99
secureClientPort=22281
1010
serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
11+
ssl.clientAuth=CLIENT_AUTH
1112
ssl.keyStore.location=CERTDIR/server.jks
1213
ssl.keyStore.password=password
1314
ssl.trustStore.location=CERTDIR/servertrust.jks

0 commit comments

Comments
 (0)