diff options
author | Pavel Březina <pbrezina@redhat.com> | 2011-12-16 13:29:16 +0100 |
---|---|---|
committer | Stephen Gallagher <sgallagh@redhat.com> | 2011-12-16 14:46:18 -0500 |
commit | 2827b0d03f7b6bafa504d22a5d7ca39cbda048b3 (patch) | |
tree | 0681d8b030ec07d1a0f0ef523a131f406daad1e6 /src/responder/sudo/sudosrv_dp.c | |
parent | e9eeb4302e0e426c6cc1a4e65b95a6f7066e80b9 (diff) | |
download | sssd-2827b0d03f7b6bafa504d22a5d7ca39cbda048b3.tar.gz sssd-2827b0d03f7b6bafa504d22a5d7ca39cbda048b3.tar.xz sssd-2827b0d03f7b6bafa504d22a5d7ca39cbda048b3.zip |
SUDO Integration - responder
Diffstat (limited to 'src/responder/sudo/sudosrv_dp.c')
-rw-r--r-- | src/responder/sudo/sudosrv_dp.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/responder/sudo/sudosrv_dp.c b/src/responder/sudo/sudosrv_dp.c new file mode 100644 index 000000000..0c621f5d5 --- /dev/null +++ b/src/responder/sudo/sudosrv_dp.c @@ -0,0 +1,220 @@ +/* + Authors: + Pavel Březina <pbrezina@redhat.com> + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2011 Red Hat + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <talloc.h> +#include <tevent.h> +#include <dbus/dbus.h> +#include "sbus/sssd_dbus.h" + +#include "util/util.h" +#include "sbus/sbus_client.h" +#include "providers/data_provider.h" +#include "responder/common/responder.h" +#include "responder/sudo/sudosrv_private.h" + +struct sudo_dp_refresh_state { + dbus_uint16_t err_maj; + dbus_uint32_t err_min; +}; + +/* FIXME -- need to keep track of a running request + * and just queue a callback + * OR reuse the common dp requests + */ +static void sudosrv_dp_process_reply(DBusPendingCall *pending, void *ptr); + +struct tevent_req * sudosrv_dp_refresh_send(struct resp_ctx *rctx, + struct sss_domain_info *dom, + const char *username) +{ + struct be_conn *be_conn; + struct sudo_dp_refresh_state *state; + DBusMessage *msg; + dbus_bool_t dbret; + int ret; + const int timeout = SSS_CLI_SOCKET_TIMEOUT / 2; + struct tevent_req *req; + + /* Cache refresh requests need to be allocated on the responder context + * so that they don't go away if a client disconnects. The worst- + * case scenario here is that the cache is updated without any + * client expecting a response. + */ + req = tevent_req_create(rctx, &state, struct sudo_dp_refresh_state); + if (!req) return NULL; + + /* double check dp_ctx has actually been initialized. + * in some pathological cases it may happen that sudo starts up before + * dp connection code is actually able to establish a connection. + */ + ret = sss_dp_get_domain_conn(rctx, dom->name, &be_conn); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("The Data Provider connection for %s is not available! " + "This maybe a bug, it shouldn't happen!\n", + dom->name)); + ret = EIO; + goto error; + } + + msg = dbus_message_new_method_call(NULL, + DP_PATH, + DP_INTERFACE, + DP_METHOD_SUDOHANDLER); + if (msg == NULL) { + DEBUG(SSSDBG_FATAL_FAILURE, ("Out of memory?!\n")); + ret = ENOMEM; + goto error; + } + + if (username != NULL) { + dbret = dbus_message_append_args(msg, + DBUS_TYPE_STRING, &username, + DBUS_TYPE_INVALID); + if (!dbret) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to generate dbus reply\n")); + ret = EIO; + goto error; + } + } + + DEBUG(SSSDBG_TRACE_FUNC, ("Sending SUDOers refresh request\n")); + ret = sbus_conn_send(be_conn->conn, msg, + timeout, sudosrv_dp_process_reply, + req, NULL); + dbus_message_unref(msg); + + return req; + +error: + tevent_req_error(req, ret); + tevent_req_post(req, rctx->ev); + return req; +} + +static int sudosrv_dp_get_reply(DBusPendingCall *pending, + dbus_uint16_t *err_maj, + dbus_uint32_t *err_min, + char **err_msg) +{ + DBusMessage *reply; + DBusError dbus_error; + dbus_bool_t ret; + int type; + int err = EOK; + + dbus_error_init(&dbus_error); + + reply = dbus_pending_call_steal_reply(pending); + if (!reply) { + /* reply should never be null. This function shouldn't be called + * until reply is valid or timeout has occurred. If reply is NULL + * here, something is seriously wrong and we should bail out. + */ + DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n")); + + err = EIO; + goto done; + } + + type = dbus_message_get_type(reply); + switch (type) { + case DBUS_MESSAGE_TYPE_METHOD_RETURN: + ret = dbus_message_get_args(reply, &dbus_error, + DBUS_TYPE_UINT16, err_maj, + DBUS_TYPE_UINT32, err_min, + DBUS_TYPE_STRING, err_msg, + DBUS_TYPE_INVALID); + if (!ret) { + DEBUG(1,("Failed to parse message\n")); + if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error); + err = EIO; + goto done; + } + + DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n", + (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg)); + + break; + + case DBUS_MESSAGE_TYPE_ERROR: + if (strcmp(dbus_message_get_error_name(reply), + DBUS_ERROR_NO_REPLY) == 0) { + err = ETIME; + goto done; + } + DEBUG(0,("The Data Provider returned an error [%s]\n", + dbus_message_get_error_name(reply))); + /* Falling through to default intentionally*/ + default: + /* + * Timeout or other error occurred or something + * unexpected happened. + * It doesn't matter which, because either way we + * know that this connection isn't trustworthy. + * We'll destroy it now. + */ + + err = EIO; + } + +done: + dbus_pending_call_unref(pending); + dbus_message_unref(reply); + + return err; +} + +static void sudosrv_dp_process_reply(DBusPendingCall *pending, void *ptr) +{ + struct tevent_req *req; + errno_t ret; + char *err_msg; + struct sudo_dp_refresh_state *state; + + req = talloc_get_type(ptr, struct tevent_req); + state = tevent_req_data(req, struct sudo_dp_refresh_state); + + ret = sudosrv_dp_get_reply(pending, &state->err_maj, &state->err_min, &err_msg); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to get a reply from DP! " + "err_maj: %d err_min: %d err_msg: [%s]\n", + state->err_maj, state->err_min, err_msg ? err_msg : "none set")); + tevent_req_error(req, EIO); + return; + } + + tevent_req_done(req); +} + +errno_t sudosrv_dp_refresh_recv(struct tevent_req *req, + dbus_uint16_t *_err_maj, + dbus_uint32_t *_err_min) +{ + struct sudo_dp_refresh_state *state; + state = tevent_req_data(req, struct sudo_dp_refresh_state); + + if (_err_maj) *_err_maj = state->err_maj; + if (_err_min) *_err_min = state->err_min; + + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} |