c++: fix -Wbad-function-cast
[awesomized/libmemcached] / libmemcached / sasl.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011-2012 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2009 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38 #include "libmemcached/common.h"
39 #include <cassert>
40
41 #if defined(LIBMEMCACHED_WITH_SASL_SUPPORT) && LIBMEMCACHED_WITH_SASL_SUPPORT
42
43 #if defined(HAVE_LIBSASL) && HAVE_LIBSASL
44 #include <sasl/sasl.h>
45 #endif
46
47 #define CAST_SASL_CB(cb) reinterpret_cast<int(*)()>(reinterpret_cast<intptr_t>(cb))
48
49 #include <pthread.h>
50
51 void memcached_set_sasl_callbacks(memcached_st *shell,
52 const sasl_callback_t *callbacks)
53 {
54 Memcached* self= memcached2Memcached(shell);
55 if (self)
56 {
57 self->sasl.callbacks= const_cast<sasl_callback_t *>(callbacks);
58 self->sasl.is_allocated= false;
59 }
60 }
61
62 sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *shell)
63 {
64 Memcached* self= memcached2Memcached(shell);
65 if (self)
66 {
67 return self->sasl.callbacks;
68 }
69
70 return NULL;
71 }
72
73 /**
74 * Resolve the names for both ends of a connection
75 * @param fd socket to check
76 * @param laddr local address (out)
77 * @param raddr remote address (out)
78 * @return true on success false otherwise (errno contains more info)
79 */
80 static memcached_return_t resolve_names(memcached_instance_st& server, char *laddr, size_t laddr_length, char *raddr, size_t raddr_length)
81 {
82 char host[MEMCACHED_NI_MAXHOST];
83 char port[MEMCACHED_NI_MAXSERV];
84 struct sockaddr_storage saddr;
85 socklen_t salen= sizeof(saddr);
86
87 if (getsockname(server.fd, (struct sockaddr *)&saddr, &salen) < 0)
88 {
89 return memcached_set_error(server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT);
90 }
91
92 if (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0)
93 {
94 return memcached_set_error(server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT);
95 }
96
97 (void)snprintf(laddr, laddr_length, "%s;%s", host, port);
98 salen= sizeof(saddr);
99
100 if (getpeername(server.fd, (struct sockaddr *)&saddr, &salen) < 0)
101 {
102 return memcached_set_error(server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT);
103 }
104
105 if (getnameinfo((struct sockaddr *)&saddr, salen, host, sizeof(host),
106 port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0)
107 {
108 return memcached_set_error(server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT);
109 }
110
111 (void)snprintf(raddr, raddr_length, "%s;%s", host, port);
112
113 return MEMCACHED_SUCCESS;
114 }
115
116 extern "C" {
117
118 static void sasl_shutdown_function()
119 {
120 sasl_done();
121 }
122
123 static volatile int sasl_startup_state= SASL_OK;
124 pthread_mutex_t sasl_startup_state_LOCK= PTHREAD_MUTEX_INITIALIZER;
125 static pthread_once_t sasl_startup_once= PTHREAD_ONCE_INIT;
126 static void sasl_startup_function(void)
127 {
128 sasl_startup_state= sasl_client_init(NULL);
129
130 if (sasl_startup_state == SASL_OK)
131 {
132 (void)atexit(sasl_shutdown_function);
133 }
134 }
135
136 } // extern "C"
137
138 memcached_return_t memcached_sasl_authenticate_connection(memcached_instance_st* server)
139 {
140 if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0)
141 {
142 return MEMCACHED_NOT_SUPPORTED;
143 }
144
145 if (server == NULL)
146 {
147 return MEMCACHED_INVALID_ARGUMENTS;
148 }
149
150 /* SANITY CHECK: SASL can only be used with the binary protocol */
151 if (memcached_is_binary(server->root) == false)
152 {
153 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT,
154 memcached_literal_param("memcached_sasl_authenticate_connection() is not supported via the ASCII protocol"));
155 }
156
157 /* Try to get the supported mech from the server. Servers without SASL
158 * support will return UNKNOWN COMMAND, so we can just treat that
159 * as authenticated
160 */
161 protocol_binary_request_no_extras request= { };
162
163 initialize_binary_request(server, request.message.header);
164
165 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_LIST_MECHS;
166
167 if (memcached_io_write(server, request.bytes, sizeof(request.bytes), true) != sizeof(request.bytes))
168 {
169 return MEMCACHED_WRITE_FAILURE;
170 }
171 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
172
173 memcached_server_response_increment(server);
174
175 char mech[MEMCACHED_MAX_BUFFER];
176 memcached_return_t rc= memcached_response(server, mech, sizeof(mech), NULL);
177 if (memcached_failed(rc))
178 {
179 if (rc == MEMCACHED_PROTOCOL_ERROR)
180 {
181 /* If the server doesn't support SASL it will return PROTOCOL_ERROR.
182 * This error may also be returned for other errors, but let's assume
183 * that the server don't support SASL and treat it as success and
184 * let the client fail with the next operation if the error was
185 * caused by another problem....
186 */
187 rc= MEMCACHED_SUCCESS;
188 }
189
190 return rc;
191 }
192 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
193
194 /* set ip addresses */
195 char laddr[MEMCACHED_NI_MAXHOST + MEMCACHED_NI_MAXSERV];
196 char raddr[MEMCACHED_NI_MAXHOST + MEMCACHED_NI_MAXSERV];
197
198 if (memcached_failed(rc= resolve_names(*server, laddr, sizeof(laddr), raddr, sizeof(raddr))))
199 {
200 return rc;
201 }
202
203 int pthread_error;
204 if ((pthread_error= pthread_once(&sasl_startup_once, sasl_startup_function)) != 0)
205 {
206 return memcached_set_errno(*server, pthread_error, MEMCACHED_AT);
207 }
208
209 (void)pthread_mutex_lock(&sasl_startup_state_LOCK);
210 if (sasl_startup_state != SASL_OK)
211 {
212 const char *sasl_error_msg= sasl_errstring(sasl_startup_state, NULL, NULL);
213 return memcached_set_error(*server, MEMCACHED_AUTH_PROBLEM, MEMCACHED_AT,
214 memcached_string_make_from_cstr(sasl_error_msg));
215 }
216 (void)pthread_mutex_unlock(&sasl_startup_state_LOCK);
217
218 sasl_conn_t *conn;
219 int ret;
220 if ((ret= sasl_client_new("memcached", server->_hostname, laddr, raddr, server->root->sasl.callbacks, 0, &conn) ) != SASL_OK)
221 {
222 const char *sasl_error_msg= sasl_errstring(ret, NULL, NULL);
223
224 sasl_dispose(&conn);
225
226 return memcached_set_error(*server, MEMCACHED_AUTH_PROBLEM, MEMCACHED_AT,
227 memcached_string_make_from_cstr(sasl_error_msg));
228 }
229
230 const char *data;
231 const char *chosenmech;
232 unsigned int len;
233 ret= sasl_client_start(conn, mech, NULL, &data, &len, &chosenmech);
234 if (ret != SASL_OK and ret != SASL_CONTINUE)
235 {
236 const char *sasl_error_msg= sasl_errstring(ret, NULL, NULL);
237
238 sasl_dispose(&conn);
239
240 return memcached_set_error(*server, MEMCACHED_AUTH_PROBLEM, MEMCACHED_AT,
241 memcached_string_make_from_cstr(sasl_error_msg));
242 }
243 uint16_t keylen= (uint16_t)strlen(chosenmech);
244 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_AUTH;
245 request.message.header.request.keylen= htons(keylen);
246 request.message.header.request.bodylen= htonl(len + keylen);
247
248 do {
249 /* send the packet */
250
251 libmemcached_io_vector_st vector[]=
252 {
253 { request.bytes, sizeof(request.bytes) },
254 { chosenmech, keylen },
255 { data, len }
256 };
257
258 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
259 if (memcached_io_writev(server, vector, 3, true) == false)
260 {
261 rc= MEMCACHED_WRITE_FAILURE;
262 break;
263 }
264 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
265 memcached_server_response_increment(server);
266
267 /* read the response */
268 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
269 rc= memcached_response(server, NULL, 0, NULL);
270 if (rc != MEMCACHED_AUTH_CONTINUE)
271 {
272 break;
273 }
274 assert_msg(server->fd != INVALID_SOCKET, "Programmer error, invalid socket");
275
276 ret= sasl_client_step(conn, memcached_result_value(&server->root->result),
277 (unsigned int)memcached_result_length(&server->root->result),
278 NULL, &data, &len);
279
280 if (ret != SASL_OK && ret != SASL_CONTINUE)
281 {
282 rc= MEMCACHED_AUTH_PROBLEM;
283 break;
284 }
285
286 request.message.header.request.opcode= PROTOCOL_BINARY_CMD_SASL_STEP;
287 request.message.header.request.bodylen= htonl(len + keylen);
288 } while (true);
289
290 /* Release resources */
291 sasl_dispose(&conn);
292
293 return memcached_set_error(*server, rc, MEMCACHED_AT);
294 }
295
296 static int get_username(void *context, int id, const char **result, unsigned int *len)
297 {
298 if (!context || !result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME))
299 {
300 return SASL_BADPARAM;
301 }
302
303 *result= (char *)context;
304 if (len)
305 {
306 *len= (unsigned int)strlen(*result);
307 }
308
309 return SASL_OK;
310 }
311
312 static int get_password(sasl_conn_t *conn, void *context, int id,
313 sasl_secret_t **psecret)
314 {
315 if (!conn || ! psecret || id != SASL_CB_PASS)
316 {
317 return SASL_BADPARAM;
318 }
319
320 *psecret= (sasl_secret_t *)context;
321
322 return SASL_OK;
323 }
324
325 memcached_return_t memcached_set_sasl_auth_data(memcached_st *shell,
326 const char *username,
327 const char *password)
328 {
329 Memcached* ptr= memcached2Memcached(shell);
330 if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0)
331 {
332 return MEMCACHED_NOT_SUPPORTED;
333 }
334
335 if (ptr == NULL or username == NULL or password == NULL)
336 {
337 return MEMCACHED_INVALID_ARGUMENTS;
338 }
339
340 memcached_return_t ret;
341 if (memcached_failed(ret= memcached_behavior_set(ptr, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, 1)))
342 {
343 return memcached_set_error(*ptr, ret, MEMCACHED_AT, memcached_literal_param("Unable change to binary protocol which is required for SASL."));
344 }
345
346 memcached_destroy_sasl_auth_data(ptr);
347
348 sasl_callback_t *callbacks= libmemcached_xcalloc(ptr, 4, sasl_callback_t);
349 size_t password_length= strlen(password);
350 size_t username_length= strlen(username);
351 char *name= (char *)libmemcached_malloc(ptr, username_length +1);
352 sasl_secret_t *secret= (sasl_secret_t*)libmemcached_malloc(ptr, password_length +1 + sizeof(sasl_secret_t));
353
354 if (callbacks == NULL or name == NULL or secret == NULL)
355 {
356 libmemcached_free(ptr, callbacks);
357 libmemcached_free(ptr, name);
358 libmemcached_free(ptr, secret);
359 return memcached_set_error(*ptr, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
360 }
361
362 secret->len= password_length;
363 memcpy(secret->data, password, password_length);
364 secret->data[password_length]= 0;
365
366 callbacks[0].id= SASL_CB_USER;
367 callbacks[0].proc= CAST_SASL_CB(get_username);
368 callbacks[0].context= strncpy(name, username, username_length +1);
369 callbacks[1].id= SASL_CB_AUTHNAME;
370 callbacks[1].proc= CAST_SASL_CB(get_username);
371 callbacks[1].context= name;
372 callbacks[2].id= SASL_CB_PASS;
373 callbacks[2].proc= CAST_SASL_CB(get_password);
374 callbacks[2].context= secret;
375 callbacks[3].id= SASL_CB_LIST_END;
376
377 ptr->sasl.callbacks= callbacks;
378 ptr->sasl.is_allocated= true;
379
380 return MEMCACHED_SUCCESS;
381 }
382
383 memcached_return_t memcached_destroy_sasl_auth_data(memcached_st *shell)
384 {
385 if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0)
386 {
387 return MEMCACHED_NOT_SUPPORTED;
388 }
389
390 Memcached* ptr= memcached2Memcached(shell);
391 if (ptr == NULL)
392 {
393 return MEMCACHED_INVALID_ARGUMENTS;
394 }
395
396 if (ptr->sasl.callbacks == NULL)
397 {
398 return MEMCACHED_SUCCESS;
399 }
400
401 if (ptr->sasl.is_allocated)
402 {
403 libmemcached_free(ptr, ptr->sasl.callbacks[0].context);
404 libmemcached_free(ptr, ptr->sasl.callbacks[2].context);
405 libmemcached_free(ptr, (void*)ptr->sasl.callbacks);
406 ptr->sasl.is_allocated= false;
407 }
408
409 ptr->sasl.callbacks= NULL;
410
411 return MEMCACHED_SUCCESS;
412 }
413
414 memcached_return_t memcached_clone_sasl(memcached_st *clone, const memcached_st *source)
415 {
416 if (LIBMEMCACHED_WITH_SASL_SUPPORT == 0)
417 {
418 return MEMCACHED_NOT_SUPPORTED;
419 }
420
421 if (clone == NULL or source == NULL)
422 {
423 return MEMCACHED_INVALID_ARGUMENTS;
424 }
425
426 if (source->sasl.callbacks == NULL)
427 {
428 return MEMCACHED_SUCCESS;
429 }
430
431 /* Hopefully we are using our own callback mechanisms.. */
432 if (source->sasl.callbacks[0].id == SASL_CB_USER &&
433 source->sasl.callbacks[0].proc == CAST_SASL_CB(get_username) &&
434 source->sasl.callbacks[1].id == SASL_CB_AUTHNAME &&
435 source->sasl.callbacks[1].proc == CAST_SASL_CB(get_username) &&
436 source->sasl.callbacks[2].id == SASL_CB_PASS &&
437 source->sasl.callbacks[2].proc == CAST_SASL_CB(get_password) &&
438 source->sasl.callbacks[3].id == SASL_CB_LIST_END)
439 {
440 sasl_secret_t *secret= (sasl_secret_t *)source->sasl.callbacks[2].context;
441 return memcached_set_sasl_auth_data(clone,
442 (const char*)source->sasl.callbacks[0].context,
443 (const char*)secret->data);
444 }
445
446 /*
447 * But we're not. It may work if we know what the user tries to pass
448 * into the list, but if we don't know the ID we don't know how to handle
449 * the context...
450 */
451 ptrdiff_t total= 0;
452
453 while (source->sasl.callbacks[total].id != SASL_CB_LIST_END)
454 {
455 switch (source->sasl.callbacks[total].id)
456 {
457 case SASL_CB_USER:
458 case SASL_CB_AUTHNAME:
459 case SASL_CB_PASS:
460 break;
461 default:
462 /* I don't know how to deal with this... */
463 return MEMCACHED_NOT_SUPPORTED;
464 }
465
466 ++total;
467 }
468
469 sasl_callback_t *callbacks= libmemcached_xcalloc(clone, total +1, sasl_callback_t);
470 if (callbacks == NULL)
471 {
472 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
473 }
474 memcpy(callbacks, source->sasl.callbacks, (total + 1) * sizeof(sasl_callback_t));
475
476 /* Now update the context... */
477 for (ptrdiff_t x= 0; x < total; ++x)
478 {
479 if (callbacks[x].id == SASL_CB_USER || callbacks[x].id == SASL_CB_AUTHNAME)
480 {
481 callbacks[x].context= (sasl_callback_t*)libmemcached_malloc(clone, strlen((const char*)source->sasl.callbacks[x].context));
482
483 if (callbacks[x].context == NULL)
484 {
485 /* Failed to allocate memory, clean up previously allocated memory */
486 for (ptrdiff_t y= 0; y < x; ++y)
487 {
488 libmemcached_free(clone, clone->sasl.callbacks[y].context);
489 }
490
491 libmemcached_free(clone, callbacks);
492 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
493 }
494 strncpy((char*)callbacks[x].context, (const char*)source->sasl.callbacks[x].context, sizeof(callbacks[x].context));
495 }
496 else
497 {
498 sasl_secret_t *src= (sasl_secret_t *)source->sasl.callbacks[x].context;
499 sasl_secret_t *n= (sasl_secret_t*)libmemcached_malloc(clone, src->len + 1 + sizeof(*n));
500 if (n == NULL)
501 {
502 /* Failed to allocate memory, clean up previously allocated memory */
503 for (ptrdiff_t y= 0; y < x; ++y)
504 {
505 libmemcached_free(clone, clone->sasl.callbacks[y].context);
506 }
507
508 libmemcached_free(clone, callbacks);
509 return MEMCACHED_MEMORY_ALLOCATION_FAILURE;
510 }
511 memcpy(n, src, src->len + 1 + sizeof(*n));
512 callbacks[x].context= n;
513 }
514 }
515
516 clone->sasl.callbacks= callbacks;
517 clone->sasl.is_allocated= true;
518
519 return MEMCACHED_SUCCESS;
520 }
521
522 #else
523
524 void memcached_set_sasl_callbacks(memcached_st *, const sasl_callback_t *)
525 {
526 }
527
528 sasl_callback_t *memcached_get_sasl_callbacks(memcached_st *)
529 {
530 return NULL;
531 }
532
533 memcached_return_t memcached_set_sasl_auth_data(memcached_st *, const char *, const char *)
534 {
535 return MEMCACHED_NOT_SUPPORTED;
536 }
537
538 memcached_return_t memcached_clone_sasl(memcached_st *, const memcached_st *)
539 {
540 return MEMCACHED_NOT_SUPPORTED;
541 }
542
543 #endif