diff options
Diffstat (limited to 'nsswitch/libwbclient/wbc_async.c')
-rw-r--r-- | nsswitch/libwbclient/wbc_async.c | 774 |
1 files changed, 0 insertions, 774 deletions
diff --git a/nsswitch/libwbclient/wbc_async.c b/nsswitch/libwbclient/wbc_async.c deleted file mode 100644 index bb95b91740..0000000000 --- a/nsswitch/libwbclient/wbc_async.c +++ /dev/null @@ -1,774 +0,0 @@ -/* - Unix SMB/CIFS implementation. - Infrastructure for async winbind requests - Copyright (C) Volker Lendecke 2008 - - ** NOTE! The following LGPL license applies to the wbclient - ** library. This does NOT imply that all of Samba is released - ** under the LGPL - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 3 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -#include "replace.h" -#include "system/filesys.h" -#include "system/network.h" -#include <talloc.h> -#include <tevent.h> -#include "lib/async_req/async_sock.h" -#include "nsswitch/winbind_struct_protocol.h" -#include "nsswitch/libwbclient/wbclient.h" -#include "nsswitch/libwbclient/wbc_async.h" - -wbcErr map_wbc_err_from_errno(int error) -{ - switch(error) { - case EPERM: - case EACCES: - return WBC_ERR_AUTH_ERROR; - case ENOMEM: - return WBC_ERR_NO_MEMORY; - case EIO: - default: - return WBC_ERR_UNKNOWN_FAILURE; - } -} - -bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err) -{ - enum tevent_req_state state; - uint64_t error; - if (!tevent_req_is_error(req, &state, &error)) { - *pwbc_err = WBC_ERR_SUCCESS; - return false; - } - - switch (state) { - case TEVENT_REQ_USER_ERROR: - *pwbc_err = error; - break; - case TEVENT_REQ_TIMED_OUT: - *pwbc_err = WBC_ERR_UNKNOWN_FAILURE; - break; - case TEVENT_REQ_NO_MEMORY: - *pwbc_err = WBC_ERR_NO_MEMORY; - break; - default: - *pwbc_err = WBC_ERR_UNKNOWN_FAILURE; - break; - } - return true; -} - -wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req) -{ - wbcErr wbc_err; - - if (tevent_req_is_wbcerr(req, &wbc_err)) { - return wbc_err; - } - - return WBC_ERR_SUCCESS; -} - -struct wbc_debug_ops { - void (*debug)(void *context, enum wbcDebugLevel level, - const char *fmt, va_list ap) PRINTF_ATTRIBUTE(3,0); - void *context; -}; - -struct wb_context { - struct tevent_queue *queue; - int fd; - bool is_priv; - const char *dir; - struct wbc_debug_ops debug_ops; -}; - -static int make_nonstd_fd(int fd) -{ - int i; - int sys_errno = 0; - int fds[3]; - int num_fds = 0; - - if (fd == -1) { - return -1; - } - while (fd < 3) { - fds[num_fds++] = fd; - fd = dup(fd); - if (fd == -1) { - sys_errno = errno; - break; - } - } - for (i=0; i<num_fds; i++) { - close(fds[i]); - } - if (fd == -1) { - errno = sys_errno; - } - return fd; -} - -/**************************************************************************** - Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available, - else - if SYSV use O_NDELAY - if BSD use FNDELAY - Set close on exec also. -****************************************************************************/ - -static int make_safe_fd(int fd) -{ - int result, flags; - int new_fd = make_nonstd_fd(fd); - - if (new_fd == -1) { - goto fail; - } - - /* Socket should be nonblocking. */ - -#ifdef O_NONBLOCK -#define FLAG_TO_SET O_NONBLOCK -#else -#ifdef SYSV -#define FLAG_TO_SET O_NDELAY -#else /* BSD */ -#define FLAG_TO_SET FNDELAY -#endif -#endif - - if ((flags = fcntl(new_fd, F_GETFL)) == -1) { - goto fail; - } - - flags |= FLAG_TO_SET; - if (fcntl(new_fd, F_SETFL, flags) == -1) { - goto fail; - } - -#undef FLAG_TO_SET - - /* Socket should be closed on exec() */ -#ifdef FD_CLOEXEC - result = flags = fcntl(new_fd, F_GETFD, 0); - if (flags >= 0) { - flags |= FD_CLOEXEC; - result = fcntl( new_fd, F_SETFD, flags ); - } - if (result < 0) { - goto fail; - } -#endif - return new_fd; - - fail: - if (new_fd != -1) { - int sys_errno = errno; - close(new_fd); - errno = sys_errno; - } - return -1; -} - -/* Just put a prototype to avoid moving the whole function around */ -static const char *winbindd_socket_dir(void); - -struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx, const char* dir) -{ - struct wb_context *result; - - result = talloc(mem_ctx, struct wb_context); - if (result == NULL) { - return NULL; - } - result->queue = tevent_queue_create(result, "wb_trans"); - if (result->queue == NULL) { - TALLOC_FREE(result); - return NULL; - } - result->fd = -1; - result->is_priv = false; - - if (dir != NULL) { - result->dir = talloc_strdup(result, dir); - } else { - result->dir = winbindd_socket_dir(); - } - if (result->dir == NULL) { - TALLOC_FREE(result); - return NULL; - } - return result; -} - -struct wb_connect_state { - int dummy; -}; - -static void wbc_connect_connected(struct tevent_req *subreq); - -static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct wb_context *wb_ctx, - const char *dir) -{ - struct tevent_req *result, *subreq; - struct wb_connect_state *state; - struct sockaddr_un sunaddr; - struct stat st; - char *path = NULL; - wbcErr wbc_err; - - result = tevent_req_create(mem_ctx, &state, struct wb_connect_state); - if (result == NULL) { - return NULL; - } - - if (wb_ctx->fd != -1) { - close(wb_ctx->fd); - wb_ctx->fd = -1; - } - - /* Check permissions on unix socket directory */ - - if (lstat(dir, &st) == -1) { - wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE; - goto post_status; - } - - if (!S_ISDIR(st.st_mode) || - (st.st_uid != 0 && st.st_uid != geteuid())) { - wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE; - goto post_status; - } - - /* Connect to socket */ - - path = talloc_asprintf(mem_ctx, "%s/%s", dir, - WINBINDD_SOCKET_NAME); - if (path == NULL) { - goto nomem; - } - - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)); - TALLOC_FREE(path); - - /* If socket file doesn't exist, don't bother trying to connect - with retry. This is an attempt to make the system usable when - the winbindd daemon is not running. */ - - if ((lstat(sunaddr.sun_path, &st) == -1) - || !S_ISSOCK(st.st_mode) - || (st.st_uid != 0 && st.st_uid != geteuid())) { - wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE; - goto post_status; - } - - wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0)); - if (wb_ctx->fd == -1) { - wbc_err = map_wbc_err_from_errno(errno); - goto post_status; - } - - subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd, - (struct sockaddr *)(void *)&sunaddr, - sizeof(sunaddr)); - if (subreq == NULL) { - goto nomem; - } - tevent_req_set_callback(subreq, wbc_connect_connected, result); - return result; - - post_status: - tevent_req_error(result, wbc_err); - return tevent_req_post(result, ev); - nomem: - TALLOC_FREE(result); - return NULL; -} - -static void wbc_connect_connected(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - int res, err; - - res = async_connect_recv(subreq, &err); - TALLOC_FREE(subreq); - if (res == -1) { - tevent_req_error(req, map_wbc_err_from_errno(err)); - return; - } - tevent_req_done(req); -} - -static wbcErr wb_connect_recv(struct tevent_req *req) -{ - return tevent_req_simple_recv_wbcerr(req); -} - -static const char *winbindd_socket_dir(void) -{ -#ifdef SOCKET_WRAPPER - const char *env_dir; - - env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR); - if (env_dir) { - return env_dir; - } -#endif - - return WINBINDD_SOCKET_DIR; -} - -struct wb_open_pipe_state { - struct wb_context *wb_ctx; - struct tevent_context *ev; - bool need_priv; - struct winbindd_request wb_req; -}; - -static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq); -static void wb_open_pipe_ping_done(struct tevent_req *subreq); -static void wb_open_pipe_getpriv_done(struct tevent_req *subreq); -static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq); - -static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct wb_context *wb_ctx, - bool need_priv) -{ - struct tevent_req *result, *subreq; - struct wb_open_pipe_state *state; - - result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state); - if (result == NULL) { - return NULL; - } - state->wb_ctx = wb_ctx; - state->ev = ev; - state->need_priv = need_priv; - - if (wb_ctx->fd != -1) { - close(wb_ctx->fd); - wb_ctx->fd = -1; - } - - subreq = wb_connect_send(state, ev, wb_ctx, wb_ctx->dir); - if (subreq == NULL) { - goto fail; - } - tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done, - result); - return result; - - fail: - TALLOC_FREE(result); - return NULL; -} - -static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_open_pipe_state *state = tevent_req_data( - req, struct wb_open_pipe_state); - wbcErr wbc_err; - - wbc_err = wb_connect_recv(subreq); - TALLOC_FREE(subreq); - if (!WBC_ERROR_IS_OK(wbc_err)) { - state->wb_ctx->is_priv = true; - tevent_req_error(req, wbc_err); - return; - } - - ZERO_STRUCT(state->wb_req); - state->wb_req.cmd = WINBINDD_INTERFACE_VERSION; - state->wb_req.pid = getpid(); - - subreq = wb_simple_trans_send(state, state->ev, NULL, - state->wb_ctx->fd, &state->wb_req); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req); -} - -static void wb_open_pipe_ping_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_open_pipe_state *state = tevent_req_data( - req, struct wb_open_pipe_state); - struct winbindd_response *wb_resp; - int ret, err; - - ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err); - TALLOC_FREE(subreq); - if (ret == -1) { - tevent_req_error(req, map_wbc_err_from_errno(err)); - return; - } - - if (!state->need_priv) { - tevent_req_done(req); - return; - } - - state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR; - state->wb_req.pid = getpid(); - - subreq = wb_simple_trans_send(state, state->ev, NULL, - state->wb_ctx->fd, &state->wb_req); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req); -} - -static void wb_open_pipe_getpriv_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_open_pipe_state *state = tevent_req_data( - req, struct wb_open_pipe_state); - struct winbindd_response *wb_resp = NULL; - int ret, err; - - ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err); - TALLOC_FREE(subreq); - if (ret == -1) { - tevent_req_error(req, map_wbc_err_from_errno(err)); - return; - } - - close(state->wb_ctx->fd); - state->wb_ctx->fd = -1; - - subreq = wb_connect_send(state, state->ev, state->wb_ctx, - (char *)wb_resp->extra_data.data); - TALLOC_FREE(wb_resp); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req); -} - -static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_open_pipe_state *state = tevent_req_data( - req, struct wb_open_pipe_state); - wbcErr wbc_err; - - wbc_err = wb_connect_recv(subreq); - TALLOC_FREE(subreq); - if (!WBC_ERROR_IS_OK(wbc_err)) { - tevent_req_error(req, wbc_err); - return; - } - state->wb_ctx->is_priv = true; - tevent_req_done(req); -} - -static wbcErr wb_open_pipe_recv(struct tevent_req *req) -{ - return tevent_req_simple_recv_wbcerr(req); -} - -struct wb_trans_state { - struct wb_trans_state *prev, *next; - struct wb_context *wb_ctx; - struct tevent_context *ev; - struct winbindd_request *wb_req; - struct winbindd_response *wb_resp; - bool need_priv; -}; - -static bool closed_fd(int fd) -{ - struct timeval tv; - fd_set r_fds; - int selret; - - if (fd < 0 || fd >= FD_SETSIZE) { - return true; - } - - FD_ZERO(&r_fds); - FD_SET(fd, &r_fds); - ZERO_STRUCT(tv); - - selret = select(fd+1, &r_fds, NULL, NULL, &tv); - if (selret == -1) { - return true; - } - if (selret == 0) { - return false; - } - return (FD_ISSET(fd, &r_fds)); -} - -static void wb_trans_trigger(struct tevent_req *req, void *private_data); -static void wb_trans_connect_done(struct tevent_req *subreq); -static void wb_trans_done(struct tevent_req *subreq); -static void wb_trans_retry_wait_done(struct tevent_req *subreq); - -struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx, - struct tevent_context *ev, - struct wb_context *wb_ctx, bool need_priv, - struct winbindd_request *wb_req) -{ - struct tevent_req *req; - struct wb_trans_state *state; - - req = tevent_req_create(mem_ctx, &state, struct wb_trans_state); - if (req == NULL) { - return NULL; - } - state->wb_ctx = wb_ctx; - state->ev = ev; - state->wb_req = wb_req; - state->need_priv = need_priv; - - if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger, - NULL)) { - tevent_req_nomem(NULL, req); - return tevent_req_post(req, ev); - } - return req; -} - -static void wb_trans_trigger(struct tevent_req *req, void *private_data) -{ - struct wb_trans_state *state = tevent_req_data( - req, struct wb_trans_state); - struct tevent_req *subreq; - - if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) { - close(state->wb_ctx->fd); - state->wb_ctx->fd = -1; - } - - if ((state->wb_ctx->fd == -1) - || (state->need_priv && !state->wb_ctx->is_priv)) { - subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx, - state->need_priv); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_trans_connect_done, req); - return; - } - - state->wb_req->pid = getpid(); - - subreq = wb_simple_trans_send(state, state->ev, NULL, - state->wb_ctx->fd, state->wb_req); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_trans_done, req); -} - -static bool wb_trans_retry(struct tevent_req *req, - struct wb_trans_state *state, - wbcErr wbc_err) -{ - struct tevent_req *subreq; - - if (WBC_ERROR_IS_OK(wbc_err)) { - return false; - } - - if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) { - /* - * Winbind not around or we can't connect to the pipe. Fail - * immediately. - */ - tevent_req_error(req, wbc_err); - return true; - } - - /* - * The transfer as such failed, retry after one second - */ - - if (state->wb_ctx->fd != -1) { - close(state->wb_ctx->fd); - state->wb_ctx->fd = -1; - } - - subreq = tevent_wakeup_send(state, state->ev, - tevent_timeval_current_ofs(1, 0)); - if (tevent_req_nomem(subreq, req)) { - return true; - } - tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req); - return true; -} - -static void wb_trans_retry_wait_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_trans_state *state = tevent_req_data( - req, struct wb_trans_state); - bool ret; - - ret = tevent_wakeup_recv(subreq); - TALLOC_FREE(subreq); - if (!ret) { - tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE); - return; - } - - subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx, - state->need_priv); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_trans_connect_done, req); -} - -static void wb_trans_connect_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_trans_state *state = tevent_req_data( - req, struct wb_trans_state); - wbcErr wbc_err; - - wbc_err = wb_open_pipe_recv(subreq); - TALLOC_FREE(subreq); - - if (wb_trans_retry(req, state, wbc_err)) { - return; - } - - subreq = wb_simple_trans_send(state, state->ev, NULL, - state->wb_ctx->fd, state->wb_req); - if (tevent_req_nomem(subreq, req)) { - return; - } - tevent_req_set_callback(subreq, wb_trans_done, req); -} - -static void wb_trans_done(struct tevent_req *subreq) -{ - struct tevent_req *req = tevent_req_callback_data( - subreq, struct tevent_req); - struct wb_trans_state *state = tevent_req_data( - req, struct wb_trans_state); - int ret, err; - - ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err); - TALLOC_FREE(subreq); - if ((ret == -1) - && wb_trans_retry(req, state, map_wbc_err_from_errno(err))) { - return; - } - - tevent_req_done(req); -} - -wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, - struct winbindd_response **presponse) -{ - struct wb_trans_state *state = tevent_req_data( - req, struct wb_trans_state); - wbcErr wbc_err; - - if (tevent_req_is_wbcerr(req, &wbc_err)) { - return wbc_err; - } - - *presponse = talloc_move(mem_ctx, &state->wb_resp); - return WBC_ERR_SUCCESS; -} - -/******************************************************************** - * Debug wrapper functions, modeled (with lot's of code copied as is) - * after the tevent debug wrapper functions - ********************************************************************/ - -/* - this allows the user to choose their own debug function -*/ -int wbcSetDebug(struct wb_context *wb_ctx, - void (*debug)(void *context, - enum wbcDebugLevel level, - const char *fmt, - va_list ap) PRINTF_ATTRIBUTE(3,0), - void *context) -{ - wb_ctx->debug_ops.debug = debug; - wb_ctx->debug_ops.context = context; - return 0; -} - -/* - debug function for wbcSetDebugStderr -*/ -static void wbcDebugStderr(void *private_data, - enum wbcDebugLevel level, - const char *fmt, - va_list ap) PRINTF_ATTRIBUTE(3,0); -static void wbcDebugStderr(void *private_data, - enum wbcDebugLevel level, - const char *fmt, va_list ap) -{ - if (level <= WBC_DEBUG_WARNING) { - vfprintf(stderr, fmt, ap); - } -} - -/* - convenience function to setup debug messages on stderr - messages of level WBC_DEBUG_WARNING and higher are printed -*/ -int wbcSetDebugStderr(struct wb_context *wb_ctx) -{ - return wbcSetDebug(wb_ctx, wbcDebugStderr, wb_ctx); -} - -/* - * log a message - * - * The default debug action is to ignore debugging messages. - * This is the most appropriate action for a library. - * Applications using the library must decide where to - * redirect debugging messages -*/ -void wbcDebug(struct wb_context *wb_ctx, enum wbcDebugLevel level, - const char *fmt, ...) -{ - va_list ap; - if (!wb_ctx) { - return; - } - if (wb_ctx->debug_ops.debug == NULL) { - return; - } - va_start(ap, fmt); - wb_ctx->debug_ops.debug(wb_ctx->debug_ops.context, level, fmt, ap); - va_end(ap); -} |