/* Authors: Pavel Březina Copyright (C) 2016 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 . */ #include #include "sbus/sssd_dbus.h" #include "util/util.h" struct sbus_talloc_msg { DBusMessage *msg; dbus_int32_t data_slot; bool in_talloc_destructor; }; static int sbus_talloc_msg_destructor(struct sbus_talloc_msg *talloc_msg) { talloc_msg->in_talloc_destructor = true; if (talloc_msg->msg == NULL) { return 0; } /* There may exist more references to this message but this talloc * context is no longer valid. We remove dbus message data to invoke * dbus destructor now. */ dbus_message_set_data(talloc_msg->msg, talloc_msg->data_slot, NULL, NULL); dbus_message_unref(talloc_msg->msg); return 0; } static void sbus_msg_data_destructor(void *ctx) { struct sbus_talloc_msg *talloc_msg; talloc_msg = talloc_get_type(ctx, struct sbus_talloc_msg); dbus_message_free_data_slot(&talloc_msg->data_slot); if (!talloc_msg->in_talloc_destructor) { /* References to this message dropped to zero but through * dbus_message_unref(), not by calling talloc_free(). We need to free * the talloc context and avoid running talloc desctuctor. */ talloc_set_destructor(talloc_msg, NULL); talloc_free(talloc_msg); } } errno_t sbus_talloc_bound_message(TALLOC_CTX *mem_ctx, DBusMessage *msg) { struct sbus_talloc_msg *talloc_msg; dbus_int32_t data_slot = -1; DBusFreeFunction free_fn; dbus_bool_t bret; /* Create a talloc context that will unreference this message when * the parent context is freed. */ talloc_msg = talloc(mem_ctx, struct sbus_talloc_msg); if (talloc_msg == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to bound D-Bus message with talloc context!\n"); return ENOMEM; } /* Allocate a dbus message data slot that will contain point to the * talloc context so we can pick up cases when the dbus message is * freed through dbus api. */ bret = dbus_message_allocate_data_slot(&data_slot); if (!bret) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to allocate data slot!\n"); talloc_free(talloc_msg); return ENOMEM; } free_fn = sbus_msg_data_destructor; bret = dbus_message_set_data(msg, data_slot, talloc_msg, free_fn); if (!bret) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to set message data!\n"); talloc_free(talloc_msg); dbus_message_free_data_slot(&data_slot); return ENOMEM; } talloc_msg->msg = msg; talloc_msg->data_slot = data_slot; talloc_msg->in_talloc_destructor = false; talloc_set_destructor(talloc_msg, sbus_talloc_msg_destructor); return EOK; } errno_t sbus_error_to_errno(DBusError *error) { static struct { const char *name; errno_t ret; } list[] = { { SBUS_ERROR_INTERNAL, ERR_INTERNAL }, { SBUS_ERROR_NOT_FOUND, ENOENT }, { SBUS_ERROR_UNKNOWN_DOMAIN, ERR_DOMAIN_NOT_FOUND }, { SBUS_ERROR_DP_FATAL, ERR_TERMINATED }, { SBUS_ERROR_DP_OFFLINE, ERR_OFFLINE }, { SBUS_ERROR_DP_NOTSUP, ENOTSUP }, { NULL, ERR_INTERNAL } }; int i; if (!dbus_error_is_set(error)) { return EOK; } for (i = 0; list[i].name != NULL; i++) { if (dbus_error_has_name(error, list[i].name)) { return list[i].ret; } } return EIO; } errno_t sbus_check_reply(DBusMessage *reply) { dbus_bool_t bret; DBusError error; errno_t ret; dbus_error_init(&error); switch (dbus_message_get_type(reply)) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: ret = EOK; goto done; case DBUS_MESSAGE_TYPE_ERROR: bret = dbus_set_error_from_message(&error, reply); if (bret == false) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read error from message\n"); ret = EIO; goto done; } DEBUG(SSSDBG_CRIT_FAILURE, "D-Bus error [%s]: %s\n", error.name, (error.message == NULL ? "(null)" : error.message)); ret = sbus_error_to_errno(&error); goto done; default: DEBUG(SSSDBG_CRIT_FAILURE, "Unexpected D-Bus message type?\n"); ret = ERR_INTERNAL; goto done; } done: dbus_error_free(&error); return ret; } DBusMessage *sbus_create_message_valist(TALLOC_CTX *mem_ctx, const char *bus, const char *path, const char *iface, const char *method, int first_arg_type, va_list va) { DBusMessage *msg; dbus_bool_t bret; errno_t ret; msg = dbus_message_new_method_call(bus, path, iface, method); if (msg == NULL) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create message\n"); return NULL; } bret = dbus_message_append_args_valist(msg, first_arg_type, va); if (!bret) { DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n"); ret = EIO; goto done; } ret = sbus_talloc_bound_message(mem_ctx, msg); done: if (ret != EOK) { dbus_message_unref(msg); } return msg; } DBusMessage *_sbus_create_message(TALLOC_CTX *mem_ctx, const char *bus, const char *path, const char *iface, const char *method, int first_arg_type, ...) { DBusMessage *msg; va_list va; va_start(va, first_arg_type); msg = sbus_create_message_valist(mem_ctx, bus, path, iface, method, first_arg_type, va); va_end(va); return msg; } errno_t sbus_parse_message_valist(DBusMessage *msg, bool check_reply, int first_arg_type, va_list va) { DBusError error; dbus_bool_t bret; errno_t ret; if (check_reply) { ret = sbus_check_reply(msg); if (ret != EOK) { return ret; } } dbus_error_init(&error); bret = dbus_message_get_args_valist(msg, &error, first_arg_type, va); if (bret == false) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message\n"); ret = EIO; goto done; } ret = sbus_error_to_errno(&error); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse D-Bus message [%s]: %s\n", error.name, error.message); goto done; } done: dbus_error_free(&error); return ret; } errno_t _sbus_parse_message(DBusMessage *msg, bool check_reply, int first_arg_type, ...) { errno_t ret; va_list va; va_start(va, first_arg_type); ret = sbus_parse_message_valist(msg, check_reply, first_arg_type, va); va_end(va); return ret; }