Skip to content

Commit

Permalink
ZOOKEEPER-1112: Add support for C client for SASL authentication Patch
Browse files Browse the repository at this point in the history
…apache#1

This first patch implements the zookeeper sasl operations, extends the
zkServer.sh test script with the ability to start a sasl enabled
server and adds a test that checks the initial DIGEST-MD5 response.

It introduces no external requirements but provides zoo_sasl/zoo_asasl
functions that allow applications (or the addon from patch apache#2) to
communicate with the sasl server backend.

Patch apache#2 will add a simple api for sasl authentication, patch apache#3
includes a sasl enabled command line client.

(Forward-ported from https://reviews.apache.org/r/2252/ by Damien Diederen.)
  • Loading branch information
Tom Klonikowski authored and ztzg committed Aug 25, 2019
1 parent b5817fb commit eaf99d5
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 1 deletion.
1 change: 1 addition & 0 deletions zookeeper-client/zookeeper-client-c/include/proto.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extern "C" {
#define ZOO_CLOSE_OP -11
#define ZOO_SETAUTH_OP 100
#define ZOO_SETWATCHES_OP 101
#define ZOO_SASL_OP 102

#ifdef __cplusplus
}
Expand Down
38 changes: 38 additions & 0 deletions zookeeper-client/zookeeper-client-c/include/zookeeper.h
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,29 @@ ZOOAPI int zoo_aremove_all_watches(zhandle_t *zh, const char *path,
ZooWatcherType wtype, int local, void_completion_t *completion,
const void *data);

typedef struct zoo_sasl_conn zoo_sasl_conn_t;

typedef int (*sasl_completion_t)(int rc, zhandle_t *zh, zoo_sasl_conn_t *conn,
const char *serverin, int serverinlen);

/**
* \brief send a sasl request asynchronously.
*
* \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
* \param zh the connection handle obtained by a call to \ref zoo_sasl_connect
* \param clientout the token
* \param clientoutlen the token length
* \param cptr function to call with the server response
* \return ZMARSHALLINGERROR if sending failed, ZOK otherwise
*/
ZOOAPI int zoo_asasl(zhandle_t *zh, zoo_sasl_conn_t *conn, const char *clientout,
unsigned clientoutlen, sasl_completion_t cptr);

struct sasl_completion_ctx {
zhandle_t *zh;
zoo_sasl_conn_t *conn;
};

#ifdef THREADED
/**
* \brief create a node synchronously.
Expand Down Expand Up @@ -2288,7 +2311,22 @@ ZOOAPI int zoo_multi(zhandle_t *zh, int count, const zoo_op_t *ops, zoo_op_resul
*/
ZOOAPI int zoo_remove_watches(zhandle_t *zh, const char *path,
ZooWatcherType wtype, watcher_fn watcher, void *watcherCtx, int local);

/**
* \brief send a sasl request synchronously.
*
* \param zh the zookeeper handle obtained by a call to \ref zookeeper_init
* \param zh the connection handle obtained by a call to \ref zoo_sasl_connect
* \param clientout the token to send
* \param clientoutlen the token length
* \param serverin the received token
* \param serverinlen the token length
* \return
*/
ZOOAPI int zoo_sasl(zhandle_t *zh, zoo_sasl_conn_t *conn, const char *clientout,
unsigned clientoutlen, const char **serverin, unsigned *serverinlen);
#endif

#ifdef __cplusplus
}
#endif
Expand Down
4 changes: 4 additions & 0 deletions zookeeper-client/zookeeper-client-c/src/zk_adaptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,10 @@ struct sync_completion {
struct String_vector strs2;
struct Stat stat2;
} strs_stat;
struct {
char *token;
int token_len;
} sasl;
} u;
int complete;
#ifdef THREADED
Expand Down
127 changes: 127 additions & 0 deletions zookeeper-client/zookeeper-client-c/src/zookeeper.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ struct ACL_vector ZOO_CREATOR_ALL_ACL = { 1, _CREATOR_ALL_ACL_ACL};
#define COMPLETION_STRING 6
#define COMPLETION_MULTI 7
#define COMPLETION_STRING_STAT 8
#define COMPLETION_SASL 9

typedef struct _auth_completion_list {
void_completion_t completion;
Expand All @@ -215,6 +216,7 @@ typedef struct completion {
acl_completion_t acl_result;
string_completion_t string_result;
string_stat_completion_t string_stat_result;
sasl_completion_t sasl_result;
struct watcher_object_list *watcher_result;
};
completion_head_t clist; /* For multi-op */
Expand Down Expand Up @@ -1711,6 +1713,17 @@ void free_completions(zhandle_t *zh,int callCompletion,int reason)
destroy_completion_entry(cptr);
#else
abort_singlethreaded(zh);
#endif
} else if (cptr->c.sasl_result == SYNCHRONOUS_MARKER) {
#ifdef THREADED
struct sync_completion
*sc = (struct sync_completion*)cptr->data;
sc->rc = reason;
notify_sync_completion(sc);
zh->outstanding_sync--;
destroy_completion_entry(cptr);
#else
abort_singlethreaded(zh);
#endif
} else if (callCompletion) {
// Fake the response
Expand Down Expand Up @@ -2763,6 +2776,23 @@ static void deserialize_response(zhandle_t *zh, int type, int xid, int failed, i
}
cptr->c.void_result(rc, cptr->data);
break;
case COMPLETION_SASL:
LOG_DEBUG(LOGCALLBACK(zh), "Calling COMPLETION_SASL for xid=%#x failed=%d rc=%d",
cptr->xid, failed, rc);
if (failed) {
struct sasl_completion_ctx *sctx =
(struct sasl_completion_ctx *) cptr->data;
cptr->c.sasl_result(rc, sctx->zh, sctx->conn, NULL, 0);
} else {
struct sasl_completion_ctx *sctx =
(struct sasl_completion_ctx *) cptr->data;
struct SetSASLResponse res;
deserialize_SetSASLResponse(ia, "reply", &res);
cptr->c.sasl_result(rc, sctx->zh, sctx->conn,
res.token.buff, res.token.len);
deallocate_SetSASLResponse(&res);
}
break;
default:
LOG_DEBUG(LOGCALLBACK(zh), "Unsupported completion type=%d", cptr->c.type);
}
Expand Down Expand Up @@ -3082,6 +3112,9 @@ static completion_list_t* do_create_completion_entry(zhandle_t *zh, int xid,
c->c.void_result = (void_completion_t)dc;
c->c.clist = *clist;
break;
case COMPLETION_SASL:
c->c.sasl_result = (sasl_completion_t) dc;
break;
}
c->xid = xid;
c->watcher = wo;
Expand Down Expand Up @@ -3226,6 +3259,12 @@ static int add_multi_completion(zhandle_t *zh, int xid, void_completion_t dc,
return add_completion(zh, xid, COMPLETION_MULTI, dc, data, 0,0, clist);
}

static int add_sasl_completion(zhandle_t *zh, int xid, sasl_completion_t dc,
const void *data, completion_head_t *clist)
{
return add_completion(zh, xid, COMPLETION_SASL, dc, data, 0,0, clist);
}

int zookeeper_close(zhandle_t *zh)
{
int rc=ZOK;
Expand Down Expand Up @@ -4248,6 +4287,48 @@ static int aremove_watches(
free_duplicate_path(server_path, path);
return rc;
}

static int queue_sasl_request(zhandle_t *zh, const char *data, unsigned len, void* cptr,
void *ctx) {
struct oarchive *oa;
int rc;

struct RequestHeader h = { get_xid(), ZOO_SASL_OP };
struct GetSASLRequest req = { { len, len>0 ? (char *) data : "" } };

LOG_DEBUG(LOGCALLBACK(zh), "saslToken (client) length: %d", len);

oa = create_buffer_oarchive();
rc = serialize_RequestHeader(oa, "header", &h);
rc = rc < 0 ? rc : serialize_GetSASLRequest(oa, "req", &req);

enter_critical(zh);
rc = rc < 0 ? rc : add_sasl_completion(zh, h.xid, cptr, ctx, NULL);
rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
get_buffer_len(oa));
leave_critical(zh);
close_buffer_oarchive(&oa, 0);

LOG_DEBUG(LOGCALLBACK(zh), "Sending sasl token request xid=%#x to %s", h.xid, zoo_get_current_server(zh));
adaptor_send_queue(zh, 0);
return (rc < 0) ? ZMARSHALLINGERROR : ZOK;
}

int zoo_asasl(zhandle_t *zh, zoo_sasl_conn_t *conn, const char *clientout,
unsigned clientoutlen, sasl_completion_t cptr) {
int r;
struct sasl_completion_ctx *sctx =
(struct sasl_completion_ctx *) malloc(
sizeof(struct sasl_completion_ctx));
sctx->zh = zh;
sctx->conn = conn;

r = queue_sasl_request(zh, clientout, clientoutlen, cptr, sctx);
free(sctx);

return r;
}

void zoo_create_op_init(zoo_op_t *op, const char *path, const char *value,
int valuelen, const struct ACL_vector *acl, int mode,
char *path_buffer, int path_buffer_len)
Expand Down Expand Up @@ -4667,6 +4748,25 @@ static void process_sync_completion(zhandle_t *zh,
case COMPLETION_MULTI:
sc->rc = deserialize_multi(zh, cptr->xid, cptr, ia);
break;
case COMPLETION_SASL:
if (sc->rc==0) {
struct SetSASLResponse res;
int len;
deserialize_SetSASLResponse(ia, "reply", &res);
if (res.token.len <= sc->u.sasl.token_len) {
len = res.token.len;
} else {
len = sc->u.data.buff_len;
}
sc->u.sasl.token_len = len;
if (len == -1) {
sc->u.sasl.token = NULL;
} else {
memcpy(sc->u.sasl.token, res.token.buff, len);
}
deallocate_SetSASLResponse(&res);
}
break;
default:
LOG_DEBUG(LOGCALLBACK(zh), "Unsupported completion type=%d", cptr->c.type);
break;
Expand Down Expand Up @@ -5058,6 +5158,33 @@ int zoo_remove_all_watches(
return remove_watches(zh, path, wtype, NULL, NULL, local, 1);

}

int zoo_sasl(zhandle_t *zh, zoo_sasl_conn_t *conn, const char *clientout,
unsigned clientoutlen, const char **serverin, unsigned *serverinlen) {
int rc;
char buf[8192];

struct sync_completion *sc = alloc_sync_completion();
sc->u.sasl.token = buf;
sc->u.sasl.token_len = sizeof(buf);

rc = queue_sasl_request(zh, clientout, clientoutlen, SYNCHRONOUS_MARKER, sc);

if(rc==ZOK){
wait_sync_completion(sc);
rc = sc->rc;
if(rc == ZOK && sc->u.sasl.token_len > 0) {
*serverin = sc->u.sasl.token;
*serverinlen = sc->u.sasl.token_len;
} else {
serverinlen = 0;
*serverin = NULL;
}
}
free_sync_completion(sc);

return rc;
}
#endif

int zoo_aremove_watches(zhandle_t *zh, const char *path, ZooWatcherType wtype,
Expand Down
53 changes: 53 additions & 0 deletions zookeeper-client/zookeeper-client-c/tests/TestClient.cc
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
CPPUNIT_TEST(testGetChildren2);
CPPUNIT_TEST(testLastZxid);
CPPUNIT_TEST(testRemoveWatchers);
CPPUNIT_TEST(testSasl);
#endif
CPPUNIT_TEST_SUITE_END();

Expand Down Expand Up @@ -301,6 +302,12 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
CPPUNIT_ASSERT(system(cmd) == 0);
}

void startServerWithOpts(const char *opts) {
char cmd[1024];
sprintf(cmd, "%s start %s %s", ZKSERVER_CMD, getHostPorts(), opts);
CPPUNIT_ASSERT(system(cmd) == 0);
}

void stopServer() {
char cmd[1024];
sprintf(cmd, "%s stop %s", ZKSERVER_CMD, getHostPorts());
Expand Down Expand Up @@ -491,6 +498,19 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
count++;
}

static int saslDigestInitCompletion(int rc, zhandle_t *zh, zoo_sasl_conn_t *conn,
const char *serverin, int serverinlen) {
const char *realm = "realm";
const char *nonce = "nonce";
CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
// response should look like
// realm="zk-sasl-md5",nonce="4n7iytvP7E9GyRVvGQ8pATPPnXJ0GjOB5rmTzk3a",...
//LOG_DEBUG(("SASL Response: %s", serverin));
CPPUNIT_ASSERT(strstr(serverin, realm)!=NULL);
CPPUNIT_ASSERT(strstr(serverin, nonce)!=NULL);
return rc;
}

static void verifyCreateFails(const char *path, zhandle_t *zk) {
CPPUNIT_ASSERT_EQUAL((int)ZBADARGUMENTS, zoo_create(zk,
path, "", 0, &ZOO_OPEN_ACL_UNSAFE, 0, 0, 0));
Expand Down Expand Up @@ -1464,6 +1484,39 @@ class Zookeeper_simpleSystem : public CPPUNIT_NS::TestFixture
watcher_rw, ctx2, 1);
CPPUNIT_ASSERT_EQUAL((int)ZOK,rc);
}

void testSasl() {
int rc;
const char *saslopt = "-sasl";
count = 0;
watchctx_t ctx1, ctx2;

const char *serverin;
unsigned serverinlen;
const char *realm = "realm";
const char *nonce = "nonce";

stopServer();
startServerWithOpts(saslopt);

// zoo_set_log_stream(stdout);
// zoo_set_debug_level(ZOO_LOG_LEVEL_DEBUG);

zhandle_t *zk1 = createClient(&ctx1);
rc = zoo_sasl(zk1, NULL, (const char *) "", 0, &serverin, &serverinlen);
CPPUNIT_ASSERT_EQUAL((int) ZOK, rc);
// response should look like
// realm="zk-sasl-md5",nonce="4n7iytvP7E9GyRVvGQ8pATPPnXJ0GjOB5rmTzk3a",...
//LOG_DEBUG(("SASL Response: %s", serverin));
CPPUNIT_ASSERT(strstr(serverin, realm)!=NULL);
CPPUNIT_ASSERT(strstr(serverin, nonce)!=NULL);

zhandle_t *zk2 = createClient(&ctx2);
rc = zoo_asasl(zk2, NULL, (const char *) "", 0, saslDigestInitCompletion);
stopServer();
startServer();

}
};

volatile int Zookeeper_simpleSystem::count;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Server {
org.apache.zookeeper.server.auth.DigestLoginModule required
user_super="test";
};
23 changes: 22 additions & 1 deletion zookeeper-client/zookeeper-client-c/tests/zkServer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ ZOOPORT=22181

if [ "x$1" == "x" ]
then
echo "USAGE: $0 startClean|start|startReadOnly|startRequireSASLAuth|stop hostPorts"
echo "USAGE: $0 startClean|start|startReadOnly|startRequireSASLAuth|stop hostPorts [-sasl] [-verbose]"
exit 2
fi

Expand Down Expand Up @@ -110,6 +110,27 @@ fi

PROPERTIES="-Dzookeeper.extendedTypesEnabled=true -Dznode.container.checkIntervalMs=100"

for var in "$@"
do
if [[ "x$var" != "x0" && "x$var" != "x1" && "x$var" != "x2" ]]
then
if [ "$var" == "-sasl" ]
then
SASLCONFFILE=tests/jaas.digest.server.conf
if [ "x${base_dir}" != "x" ]
then
SASLCONFFILE="${base_dir}/zookeeper-client/zookeeper-client-c/$SASLCONFFILE"
fi
PROPERTIES="$PROPERTIES -Dzookeeper.authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider"
PROPERTIES="$PROPERTIES -Djava.security.auth.login.config=$SASLCONFFILE"
fi
if [ "$var" == "-verbose" ]
then
PROPERTIES="$PROPERTIES -Dzookeeper.root.logger=DEBUG,CONSOLE -Dzookeeper.console.threshold=DEBUG"
fi
fi
done

case $1 in
start|startClean)
if [ "x${base_dir}" == "x" ]
Expand Down

0 comments on commit eaf99d5

Please sign in to comment.