1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #ifdef THREADED
20 
21 #include <cppunit/extensions/HelperMacros.h>
22 #include "CppAssertHelper.h"
23 
24 #include <sys/socket.h>
25 #include <unistd.h>
26 
27 #include <zookeeper.h>
28 
29 #include "Util.h"
30 #include "WatchUtil.h"
31 
32 class Zookeeper_SASLAuth : public CPPUNIT_NS::TestFixture {
33     CPPUNIT_TEST_SUITE(Zookeeper_SASLAuth);
34     CPPUNIT_TEST(testServerRequireClientSASL);
35 #ifdef HAVE_CYRUS_SASL_H
36     CPPUNIT_TEST(testClientSASL);
37 #ifdef ZOO_IPV6_ENABLED
38     CPPUNIT_TEST(testClientSASLOverIPv6);
39 #endif/* ZOO_IPV6_ENABLED */
40     CPPUNIT_TEST(testClientSASLReadOnly);
41     CPPUNIT_TEST(testClientSASLPacketOrder);
42 #endif /* HAVE_CYRUS_SASL_H */
43     CPPUNIT_TEST_SUITE_END();
44     FILE *logfile;
45     static const char hostPorts[];
46     static const char jaasConf[];
watcher(zhandle_t *,int type,int state,const char * path,void * v)47     static void watcher(zhandle_t *, int type, int state, const char *path,void*v){
48         watchctx_t *ctx = (watchctx_t*)v;
49 
50         if (state == ZOO_CONNECTED_STATE || state == ZOO_READONLY_STATE) {
51             ctx->connected = true;
52         } else {
53             ctx->connected = false;
54         }
55         if (type != ZOO_SESSION_EVENT) {
56             evt_t evt;
57             evt.path = path;
58             evt.type = type;
59             ctx->putEvent(evt);
60         }
61     }
62 
63 public:
Zookeeper_SASLAuth()64     Zookeeper_SASLAuth() {
65       logfile = openlogfile("Zookeeper_SASLAuth");
66     }
67 
~Zookeeper_SASLAuth()68     ~Zookeeper_SASLAuth() {
69       if (logfile) {
70         fflush(logfile);
71         fclose(logfile);
72         logfile = 0;
73       }
74     }
75 
setUp()76     void setUp() {
77         zoo_set_log_stream(logfile);
78 
79         // Create SASL configuration file for server.
80         FILE *conff = fopen("Zookeeper_SASLAuth.jaas.conf", "wt");
81         CPPUNIT_ASSERT(conff);
82         size_t confLen = strlen(jaasConf);
83         CPPUNIT_ASSERT_EQUAL(fwrite(jaasConf, 1, confLen, conff), confLen);
84         CPPUNIT_ASSERT_EQUAL(fclose(conff), 0);
85         conff = NULL;
86 
87         // Create password file for client.
88         FILE *passf = fopen("Zookeeper_SASLAuth.password", "wt");
89         CPPUNIT_ASSERT(passf);
90         CPPUNIT_ASSERT(fputs("mypassword", passf) > 0);
91         CPPUNIT_ASSERT_EQUAL(fclose(passf), 0);
92         passf = NULL;
93     }
94 
startServer(bool useJaasConf=true,bool readOnly=false)95     void startServer(bool useJaasConf = true, bool readOnly = false) {
96         char cmd[1024];
97         sprintf(cmd, "%s startRequireSASLAuth %s %s",
98                 ZKSERVER_CMD,
99                 useJaasConf ? "Zookeeper_SASLAuth.jaas.conf" : "",
100                 readOnly ? "true" : "");
101         CPPUNIT_ASSERT(system(cmd) == 0);
102     }
103 
stopServer()104     void stopServer() {
105         char cmd[1024];
106         sprintf(cmd, "%s stop", ZKSERVER_CMD);
107         CPPUNIT_ASSERT(system(cmd) == 0);
108     }
109 
testServerRequireClientSASL()110     void testServerRequireClientSASL() {
111         startServer(false);
112 
113         watchctx_t ctx;
114         int rc = 0;
115         zhandle_t *zk = zookeeper_init(hostPorts, watcher, 10000, 0, &ctx, 0);
116         ctx.zh = zk;
117         CPPUNIT_ASSERT(zk);
118 
119         // Wait for handle to be connected.
120         CPPUNIT_ASSERT(ctx.waitForConnected(zk));
121 
122         char pathbuf[80];
123         struct Stat stat_a = {0};
124 
125         rc = zoo_create2(zk, "/serverRequireClientSASL", "", 0,
126                          &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_a);
127         CPPUNIT_ASSERT_EQUAL((int)ZSESSIONCLOSEDREQUIRESASLAUTH, rc);
128 
129         stopServer();
130     }
131 
132 #ifdef HAVE_CYRUS_SASL_H
133 
134     // We need to disable the deprecation warnings as Apple has
135     // decided to deprecate all of CyrusSASL's functions with OS 10.11
136     // (see MESOS-3030, ZOOKEEPER-4201). We are using GCC pragmas also
137     // for covering clang.
138 #ifdef __APPLE__
139 #pragma GCC diagnostic push
140 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
141 #endif
142 
testClientSASLHelper(const char * hostPorts,const char * path)143     void testClientSASLHelper(const char *hostPorts, const char *path) {
144         startServer();
145 
146         // Initialize Cyrus SASL.
147         CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK);
148 
149         // Initialize SASL parameters.
150         zoo_sasl_params_t sasl_params = { 0 };
151 
152         sasl_params.service = "zookeeper";
153         sasl_params.host = "zk-sasl-md5";
154         sasl_params.mechlist = "DIGEST-MD5";
155         sasl_params.callbacks = zoo_sasl_make_basic_callbacks(
156             "myuser", NULL, "Zookeeper_SASLAuth.password");
157 
158         // Connect.
159         watchctx_t ctx;
160         int rc = 0;
161         zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL,
162             &ctx, /*flags*/0, /*log_callback*/NULL, &sasl_params);
163         ctx.zh = zk;
164         CPPUNIT_ASSERT(zk);
165 
166         // Wait for SASL auth to complete and handle to be connected.
167         CPPUNIT_ASSERT(ctx.waitForConnected(zk));
168 
169         // Leave mark.
170         char pathbuf[80];
171         struct Stat stat_a = {0};
172         rc = zoo_create2(zk, path, "", 0,
173             &ZOO_OPEN_ACL_UNSAFE, 0, pathbuf, sizeof(pathbuf), &stat_a);
174         CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
175 
176         // Stop and restart the server to test automatic reconnect & re-auth.
177         stopServer();
178         CPPUNIT_ASSERT(ctx.waitForDisconnected(zk));
179         startServer();
180 
181         // Wait for automatic SASL re-auth to complete.
182         CPPUNIT_ASSERT(ctx.waitForConnected(zk));
183 
184         // Check mark left above.
185         rc = zoo_exists(zk, path, /*watch*/false, &stat_a);
186         CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
187 
188         stopServer();
189     }
190 
testClientSASL()191     void testClientSASL() {
192         testClientSASLHelper(hostPorts, "/clientSASL");
193     }
194 
testClientSASLOverIPv6()195     void testClientSASLOverIPv6() {
196         const char *ipAndPort = "::1:22181";
197 
198         testClientSASLHelper(ipAndPort, "/clientSASLOverIPv6");
199     }
200 
testClientSASLReadOnly()201     void testClientSASLReadOnly() {
202         startServer(/*useJaasConf*/ true, /*readOnly*/ true);
203 
204         // Initialize Cyrus SASL.
205         CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK);
206 
207         // Initialize SASL parameters.
208         zoo_sasl_params_t sasl_params = { 0 };
209 
210         sasl_params.service = "zookeeper";
211         sasl_params.host = "zk-sasl-md5";
212         sasl_params.mechlist = "DIGEST-MD5";
213         sasl_params.callbacks = zoo_sasl_make_basic_callbacks(
214             "myuser", NULL, "Zookeeper_SASLAuth.password");
215 
216         // Connect.
217         watchctx_t ctx;
218         int rc = 0;
219         zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL,
220             &ctx, /*flags*/ZOO_READONLY, /*log_callback*/NULL, &sasl_params);
221         ctx.zh = zk;
222         CPPUNIT_ASSERT(zk);
223 
224         // Wait for SASL auth to complete and handle to be connected.
225         CPPUNIT_ASSERT(ctx.waitForConnected(zk));
226 
227         // Assert can read.
228         char buf[1024];
229         int len = sizeof(buf);
230         rc = zoo_get(zk, "/", 0, buf, &len, 0);
231         CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
232 
233         // Assert can not write.
234         char path[1024];
235         rc = zoo_create(zk, "/test", "hello", 5, &ZOO_OPEN_ACL_UNSAFE, 0, path, sizeof(path));
236         CPPUNIT_ASSERT_EQUAL((int)ZNOTREADONLY, rc);
237 
238         stopServer();
239     }
240 
testClientSASLPacketOrder()241     void testClientSASLPacketOrder() {
242         startServer();
243 
244         // Initialize Cyrus SASL.
245         CPPUNIT_ASSERT_EQUAL(sasl_client_init(NULL), SASL_OK);
246 
247         // Initialize SASL parameters.
248         zoo_sasl_params_t sasl_params = { 0 };
249 
250         sasl_params.service = "zookeeper";
251         sasl_params.host = "zk-sasl-md5";
252         sasl_params.mechlist = "DIGEST-MD5";
253         sasl_params.callbacks = zoo_sasl_make_basic_callbacks(
254             "myuser", NULL, "Zookeeper_SASLAuth.password");
255 
256         // Connect.
257         watchctx_t ctx;
258         int rc = 0;
259         zhandle_t *zk = zookeeper_init_sasl(hostPorts, watcher, 10000, NULL,
260             &ctx, /*flags*/0, /*log_callback*/NULL, &sasl_params);
261         ctx.zh = zk;
262         CPPUNIT_ASSERT(zk);
263 
264         // No wait: try and queue a packet before SASL auth is complete.
265         char buf[1024];
266         int len = sizeof(buf);
267         rc = zoo_get(zk, "/", 0, buf, &len, 0);
268         CPPUNIT_ASSERT_EQUAL((int)ZOK, rc);
269 
270         stopServer();
271     }
272 
273 #ifdef __APPLE__
274 #pragma GCC diagnostic pop
275 #endif
276 
277 #endif /* HAVE_CYRUS_SASL_H */
278 };
279 
280 const char Zookeeper_SASLAuth::hostPorts[] = "127.0.0.1:22181";
281 
282 const char Zookeeper_SASLAuth::jaasConf[] =
283   "Server {\n"
284   "  org.apache.zookeeper.server.auth.DigestLoginModule required\n"
285   "    user_myuser=\"mypassword\";\n"
286   "};\n";
287 
288 CPPUNIT_TEST_SUITE_REGISTRATION(Zookeeper_SASLAuth);
289 
290 #endif /* THREADED */
291