s4:kdc: split out a sdb_keys_free() helper function
[Samba.git] / source3 / torture / wbc_async.c
blob9560e3623bd5984c876ba08cf62df8c54c001a0c
1 /*
2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
6 ** NOTE! The following LGPL license applies to the wbclient
7 ** library. This does NOT imply that all of Samba is released
8 ** under the LGPL
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 3 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Library General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "replace.h"
25 #include "system/filesys.h"
26 #include "system/network.h"
27 #include <talloc.h>
28 #include <tevent.h>
29 #include "lib/async_req/async_sock.h"
30 #include "nsswitch/winbind_struct_protocol.h"
31 #include "nsswitch/libwbclient/wbclient.h"
32 #include "wbc_async.h"
33 #include "lib/util/blocking.h"
35 wbcErr map_wbc_err_from_errno(int error)
37 switch(error) {
38 case EPERM:
39 case EACCES:
40 return WBC_ERR_AUTH_ERROR;
41 case ENOMEM:
42 return WBC_ERR_NO_MEMORY;
43 case EIO:
44 default:
45 return WBC_ERR_UNKNOWN_FAILURE;
49 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
51 enum tevent_req_state state;
52 uint64_t error;
53 if (!tevent_req_is_error(req, &state, &error)) {
54 *pwbc_err = WBC_ERR_SUCCESS;
55 return false;
58 switch (state) {
59 case TEVENT_REQ_USER_ERROR:
60 *pwbc_err = error;
61 break;
62 case TEVENT_REQ_TIMED_OUT:
63 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
64 break;
65 case TEVENT_REQ_NO_MEMORY:
66 *pwbc_err = WBC_ERR_NO_MEMORY;
67 break;
68 default:
69 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
70 break;
72 return true;
75 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
77 wbcErr wbc_err;
79 if (tevent_req_is_wbcerr(req, &wbc_err)) {
80 return wbc_err;
83 return WBC_ERR_SUCCESS;
86 struct wbc_debug_ops {
87 void (*debug)(void *context, enum wbcDebugLevel level,
88 const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0);
89 void *context;
92 struct wb_context {
93 struct tevent_queue *queue;
94 int fd;
95 bool is_priv;
96 const char *dir;
97 struct wbc_debug_ops debug_ops;
100 static int make_nonstd_fd(int fd)
102 size_t i;
103 int sys_errno = 0;
104 int fds[3];
105 size_t num_fds = 0;
107 if (fd == -1) {
108 return -1;
110 while (fd < 3) {
111 fds[num_fds++] = fd;
112 fd = dup(fd);
113 if (fd == -1) {
114 sys_errno = errno;
115 break;
118 for (i=0; i<num_fds; i++) {
119 close(fds[i]);
121 if (fd == -1) {
122 errno = sys_errno;
124 return fd;
127 /****************************************************************************
128 Set a fd into blocking/nonblocking mode.
129 Set close on exec also.
130 ****************************************************************************/
132 static int make_safe_fd(int fd)
134 int result, flags;
135 int new_fd = make_nonstd_fd(fd);
137 if (new_fd == -1) {
138 goto fail;
141 result = set_blocking(new_fd, false);
142 if (result == -1) {
143 goto fail;
146 /* Socket should be closed on exec() */
147 #ifdef FD_CLOEXEC
148 result = flags = fcntl(new_fd, F_GETFD, 0);
149 if (flags >= 0) {
150 flags |= FD_CLOEXEC;
151 result = fcntl( new_fd, F_SETFD, flags );
153 if (result < 0) {
154 goto fail;
156 #endif
157 return new_fd;
159 fail:
160 if (new_fd != -1) {
161 int sys_errno = errno;
162 close(new_fd);
163 errno = sys_errno;
165 return -1;
168 /* Just put a prototype to avoid moving the whole function around */
169 static const char *winbindd_socket_dir(void);
171 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir)
173 struct wb_context *result;
175 result = talloc_zero(mem_ctx, struct wb_context);
176 if (result == NULL) {
177 return NULL;
179 result->queue = tevent_queue_create(result, "wb_trans");
180 if (result->queue == NULL) {
181 TALLOC_FREE(result);
182 return NULL;
184 result->fd = -1;
185 result->is_priv = false;
187 if (dir != NULL) {
188 result->dir = talloc_strdup(result, dir);
189 } else {
190 result->dir = winbindd_socket_dir();
192 if (result->dir == NULL) {
193 TALLOC_FREE(result);
194 return NULL;
196 return result;
199 struct wb_connect_state {
200 int dummy;
203 static void wbc_connect_connected(struct tevent_req *subreq);
205 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
206 struct tevent_context *ev,
207 struct wb_context *wb_ctx,
208 const char *dir)
210 struct tevent_req *result, *subreq;
211 struct wb_connect_state *state;
212 struct sockaddr_un sunaddr;
213 struct stat st;
214 char *path = NULL;
215 wbcErr wbc_err;
217 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
218 if (result == NULL) {
219 return NULL;
222 if (wb_ctx->fd != -1) {
223 close(wb_ctx->fd);
224 wb_ctx->fd = -1;
227 /* Check permissions on unix socket directory */
229 if (lstat(dir, &st) == -1) {
230 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
231 goto post_status;
234 if (!S_ISDIR(st.st_mode) ||
235 (st.st_uid != 0 && st.st_uid != geteuid())) {
236 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
237 goto post_status;
240 /* Connect to socket */
242 path = talloc_asprintf(mem_ctx, "%s/%s", dir,
243 WINBINDD_SOCKET_NAME);
244 if (path == NULL) {
245 goto nomem;
248 sunaddr.sun_family = AF_UNIX;
249 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
250 TALLOC_FREE(path);
252 /* If socket file doesn't exist, don't bother trying to connect
253 with retry. This is an attempt to make the system usable when
254 the winbindd daemon is not running. */
256 if ((lstat(sunaddr.sun_path, &st) == -1)
257 || !S_ISSOCK(st.st_mode)
258 || (st.st_uid != 0 && st.st_uid != geteuid())) {
259 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
260 goto post_status;
263 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
264 if (wb_ctx->fd == -1) {
265 wbc_err = map_wbc_err_from_errno(errno);
266 goto post_status;
269 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
270 (struct sockaddr *)(void *)&sunaddr,
271 sizeof(sunaddr), NULL, NULL, NULL);
272 if (subreq == NULL) {
273 goto nomem;
275 tevent_req_set_callback(subreq, wbc_connect_connected, result);
276 return result;
278 post_status:
279 tevent_req_error(result, wbc_err);
280 return tevent_req_post(result, ev);
281 nomem:
282 TALLOC_FREE(result);
283 return NULL;
286 static void wbc_connect_connected(struct tevent_req *subreq)
288 struct tevent_req *req = tevent_req_callback_data(
289 subreq, struct tevent_req);
290 int res, err;
292 res = async_connect_recv(subreq, &err);
293 TALLOC_FREE(subreq);
294 if (res == -1) {
295 tevent_req_error(req, map_wbc_err_from_errno(err));
296 return;
298 tevent_req_done(req);
301 static wbcErr wb_connect_recv(struct tevent_req *req)
303 return tevent_req_simple_recv_wbcerr(req);
306 static const char *winbindd_socket_dir(void)
308 if (nss_wrapper_enabled()) {
309 const char *env_dir;
311 env_dir = getenv("SELFTEST_WINBINDD_SOCKET_DIR");
312 if (env_dir != NULL) {
313 return env_dir;
317 return WINBINDD_SOCKET_DIR;
320 struct wb_open_pipe_state {
321 struct wb_context *wb_ctx;
322 struct tevent_context *ev;
323 bool need_priv;
324 struct winbindd_request wb_req;
327 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
328 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
329 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
330 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
332 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
333 struct tevent_context *ev,
334 struct wb_context *wb_ctx,
335 bool need_priv)
337 struct tevent_req *result, *subreq;
338 struct wb_open_pipe_state *state;
340 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
341 if (result == NULL) {
342 return NULL;
344 state->wb_ctx = wb_ctx;
345 state->ev = ev;
346 state->need_priv = need_priv;
348 if (wb_ctx->fd != -1) {
349 close(wb_ctx->fd);
350 wb_ctx->fd = -1;
353 subreq = wb_connect_send(state, ev, wb_ctx, wb_ctx->dir);
354 if (subreq == NULL) {
355 goto fail;
357 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
358 result);
359 return result;
361 fail:
362 TALLOC_FREE(result);
363 return NULL;
366 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
368 struct tevent_req *req = tevent_req_callback_data(
369 subreq, struct tevent_req);
370 struct wb_open_pipe_state *state = tevent_req_data(
371 req, struct wb_open_pipe_state);
372 wbcErr wbc_err;
374 wbc_err = wb_connect_recv(subreq);
375 TALLOC_FREE(subreq);
376 if (!WBC_ERROR_IS_OK(wbc_err)) {
377 state->wb_ctx->is_priv = true;
378 tevent_req_error(req, wbc_err);
379 return;
382 ZERO_STRUCT(state->wb_req);
383 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
384 state->wb_req.pid = getpid();
385 (void)snprintf(state->wb_req.client_name,
386 sizeof(state->wb_req.client_name),
387 "%s",
388 "TORTURE");
390 subreq = wb_simple_trans_send(state, state->ev, NULL,
391 state->wb_ctx->fd, &state->wb_req);
392 if (tevent_req_nomem(subreq, req)) {
393 return;
395 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
398 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
400 struct tevent_req *req = tevent_req_callback_data(
401 subreq, struct tevent_req);
402 struct wb_open_pipe_state *state = tevent_req_data(
403 req, struct wb_open_pipe_state);
404 struct winbindd_response *wb_resp;
405 int ret, err;
407 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
408 TALLOC_FREE(subreq);
409 if (ret == -1) {
410 tevent_req_error(req, map_wbc_err_from_errno(err));
411 return;
414 if (!state->need_priv) {
415 tevent_req_done(req);
416 return;
419 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
420 state->wb_req.pid = getpid();
422 subreq = wb_simple_trans_send(state, state->ev, NULL,
423 state->wb_ctx->fd, &state->wb_req);
424 if (tevent_req_nomem(subreq, req)) {
425 return;
427 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
430 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
432 struct tevent_req *req = tevent_req_callback_data(
433 subreq, struct tevent_req);
434 struct wb_open_pipe_state *state = tevent_req_data(
435 req, struct wb_open_pipe_state);
436 struct winbindd_response *wb_resp = NULL;
437 int ret, err;
439 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
440 TALLOC_FREE(subreq);
441 if (ret == -1) {
442 tevent_req_error(req, map_wbc_err_from_errno(err));
443 return;
446 close(state->wb_ctx->fd);
447 state->wb_ctx->fd = -1;
449 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
450 (char *)wb_resp->extra_data.data);
451 TALLOC_FREE(wb_resp);
452 if (tevent_req_nomem(subreq, req)) {
453 return;
455 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
458 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
460 struct tevent_req *req = tevent_req_callback_data(
461 subreq, struct tevent_req);
462 struct wb_open_pipe_state *state = tevent_req_data(
463 req, struct wb_open_pipe_state);
464 wbcErr wbc_err;
466 wbc_err = wb_connect_recv(subreq);
467 TALLOC_FREE(subreq);
468 if (!WBC_ERROR_IS_OK(wbc_err)) {
469 tevent_req_error(req, wbc_err);
470 return;
472 state->wb_ctx->is_priv = true;
473 tevent_req_done(req);
476 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
478 return tevent_req_simple_recv_wbcerr(req);
481 struct wb_trans_state {
482 struct wb_trans_state *prev, *next;
483 struct wb_context *wb_ctx;
484 struct tevent_context *ev;
485 struct winbindd_request *wb_req;
486 struct winbindd_response *wb_resp;
487 bool need_priv;
490 static bool closed_fd(int fd)
492 struct timeval tv;
493 fd_set r_fds;
494 int selret;
496 if (fd == -1) {
497 return true;
500 FD_ZERO(&r_fds);
501 FD_SET(fd, &r_fds);
502 ZERO_STRUCT(tv);
504 selret = select(fd+1, &r_fds, NULL, NULL, &tv);
505 if (selret == -1) {
506 return true;
508 if (selret == 0) {
509 return false;
511 return (FD_ISSET(fd, &r_fds));
514 static void wb_trans_trigger(struct tevent_req *req, void *private_data);
515 static void wb_trans_connect_done(struct tevent_req *subreq);
516 static void wb_trans_done(struct tevent_req *subreq);
517 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
519 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
520 struct tevent_context *ev,
521 struct wb_context *wb_ctx, bool need_priv,
522 struct winbindd_request *wb_req)
524 struct tevent_req *req;
525 struct wb_trans_state *state;
527 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
528 if (req == NULL) {
529 return NULL;
531 state->wb_ctx = wb_ctx;
532 state->ev = ev;
533 state->wb_req = wb_req;
534 state->need_priv = need_priv;
536 if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
537 NULL)) {
538 tevent_req_oom(req);
539 return tevent_req_post(req, ev);
541 return req;
544 static void wb_trans_trigger(struct tevent_req *req, void *private_data)
546 struct wb_trans_state *state = tevent_req_data(
547 req, struct wb_trans_state);
548 struct tevent_req *subreq;
550 if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
551 close(state->wb_ctx->fd);
552 state->wb_ctx->fd = -1;
555 if ((state->wb_ctx->fd == -1)
556 || (state->need_priv && !state->wb_ctx->is_priv)) {
557 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
558 state->need_priv);
559 if (tevent_req_nomem(subreq, req)) {
560 return;
562 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
563 return;
566 state->wb_req->pid = getpid();
568 subreq = wb_simple_trans_send(state, state->ev, NULL,
569 state->wb_ctx->fd, state->wb_req);
570 if (tevent_req_nomem(subreq, req)) {
571 return;
573 tevent_req_set_callback(subreq, wb_trans_done, req);
576 static bool wb_trans_retry(struct tevent_req *req,
577 struct wb_trans_state *state,
578 wbcErr wbc_err)
580 struct tevent_req *subreq;
582 if (WBC_ERROR_IS_OK(wbc_err)) {
583 return false;
586 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
588 * Winbind not around or we can't connect to the pipe. Fail
589 * immediately.
591 tevent_req_error(req, wbc_err);
592 return true;
596 * The transfer as such failed, retry after one second
599 if (state->wb_ctx->fd != -1) {
600 close(state->wb_ctx->fd);
601 state->wb_ctx->fd = -1;
604 subreq = tevent_wakeup_send(state, state->ev,
605 tevent_timeval_current_ofs(1, 0));
606 if (tevent_req_nomem(subreq, req)) {
607 return true;
609 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
610 return true;
613 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
615 struct tevent_req *req = tevent_req_callback_data(
616 subreq, struct tevent_req);
617 struct wb_trans_state *state = tevent_req_data(
618 req, struct wb_trans_state);
619 bool ret;
621 ret = tevent_wakeup_recv(subreq);
622 TALLOC_FREE(subreq);
623 if (!ret) {
624 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
625 return;
628 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
629 state->need_priv);
630 if (tevent_req_nomem(subreq, req)) {
631 return;
633 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
636 static void wb_trans_connect_done(struct tevent_req *subreq)
638 struct tevent_req *req = tevent_req_callback_data(
639 subreq, struct tevent_req);
640 struct wb_trans_state *state = tevent_req_data(
641 req, struct wb_trans_state);
642 wbcErr wbc_err;
644 wbc_err = wb_open_pipe_recv(subreq);
645 TALLOC_FREE(subreq);
647 if (wb_trans_retry(req, state, wbc_err)) {
648 return;
651 subreq = wb_simple_trans_send(state, state->ev, NULL,
652 state->wb_ctx->fd, state->wb_req);
653 if (tevent_req_nomem(subreq, req)) {
654 return;
656 tevent_req_set_callback(subreq, wb_trans_done, req);
659 static void wb_trans_done(struct tevent_req *subreq)
661 struct tevent_req *req = tevent_req_callback_data(
662 subreq, struct tevent_req);
663 struct wb_trans_state *state = tevent_req_data(
664 req, struct wb_trans_state);
665 int ret, err;
667 ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
668 TALLOC_FREE(subreq);
669 if ((ret == -1)
670 && wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
671 return;
674 tevent_req_done(req);
677 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
678 struct winbindd_response **presponse)
680 struct wb_trans_state *state = tevent_req_data(
681 req, struct wb_trans_state);
682 wbcErr wbc_err;
684 if (tevent_req_is_wbcerr(req, &wbc_err)) {
685 return wbc_err;
688 *presponse = talloc_move(mem_ctx, &state->wb_resp);
689 return WBC_ERR_SUCCESS;
692 /********************************************************************
693 * Debug wrapper functions, modeled (with lot's of code copied as is)
694 * after the tevent debug wrapper functions
695 ********************************************************************/
698 this allows the user to choose their own debug function
700 int wbcSetDebug(struct wb_context *wb_ctx,
701 void (*debug)(void *context,
702 enum wbcDebugLevel level,
703 const char *fmt,
704 va_list ap) PRINTF_ATTRIBUTE(3,0),
705 void *context)
707 wb_ctx->debug_ops.debug = debug;
708 wb_ctx->debug_ops.context = context;
709 return 0;
713 debug function for wbcSetDebugStderr
715 static void wbcDebugStderr(void *private_data,
716 enum wbcDebugLevel level,
717 const char *fmt,
718 va_list ap) PRINTF_ATTRIBUTE(3,0);
719 static void wbcDebugStderr(void *private_data,
720 enum wbcDebugLevel level,
721 const char *fmt, va_list ap)
723 if (level <= WBC_DEBUG_WARNING) {
724 vfprintf(stderr, fmt, ap);
729 convenience function to setup debug messages on stderr
730 messages of level WBC_DEBUG_WARNING and higher are printed
732 int wbcSetDebugStderr(struct wb_context *wb_ctx)
734 return wbcSetDebug(wb_ctx, wbcDebugStderr, wb_ctx);
738 * log a message
740 * The default debug action is to ignore debugging messages.
741 * This is the most appropriate action for a library.
742 * Applications using the library must decide where to
743 * redirect debugging messages
745 void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level,
746 const char *fmt, ...)
748 va_list ap;
749 if (!wb_ctx) {
750 return;
752 if (wb_ctx->debug_ops.debug == NULL) {
753 return;
755 va_start(ap, fmt);
756 wb_ctx->debug_ops.debug(wb_ctx->debug_ops.context, level, fmt, ap);
757 va_end(ap);