diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/responder/ifp/org.freedesktop.sssd.infopipe.conf | 3 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_connection.c | 35 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_introspect.c | 330 | ||||
-rw-r--r-- | src/sbus/sssd_dbus_private.h | 6 | ||||
-rw-r--r-- | src/tests/sbus_tests.c | 49 |
5 files changed, 420 insertions, 3 deletions
diff --git a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf index fea847cee..56e460aa1 100644 --- a/src/responder/ifp/org.freedesktop.sssd.infopipe.conf +++ b/src/responder/ifp/org.freedesktop.sssd.infopipe.conf @@ -16,6 +16,9 @@ <!-- Right now, this will be handled by a limited ACL within the InfoPipe Daemon. --> <policy context="default"> + <allow send_destination="org.freedesktop.sssd.infopipe" + send_interface="org.freedesktop.DBus.Introspectable"/> + <allow send_interface="org.freedesktop.sssd.infopipe"/> </policy> diff --git a/src/sbus/sssd_dbus_connection.c b/src/sbus/sssd_dbus_connection.c index 150b5469e..64777fbe8 100644 --- a/src/sbus/sssd_dbus_connection.c +++ b/src/sbus/sssd_dbus_connection.c @@ -378,6 +378,8 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, const struct sbus_interface_meta *interface; struct sbus_request *dbus_req = NULL; sbus_msg_handler_fn handler_fn = NULL; + void *handler_data = NULL; /* Must be a talloc pointer! */ + struct sbus_introspect_ctx *ictx = NULL; DBusHandlerResult result; int ret; @@ -425,19 +427,46 @@ DBusHandlerResult sbus_message_handler(DBusConnection *dbus_conn, dbus_message_unref(reply); result = DBUS_HANDLER_RESULT_HANDLED; } + } else { + /* Special case: check for Introspection request + * This is usually only useful for system bus connections + */ + if (strcmp(msg_interface, DBUS_INTROSPECT_INTERFACE) == 0 && + strcmp(msg_method, DBUS_INTROSPECT_METHOD) == 0) { + DEBUG(SSSDBG_TRACE_LIBS, "Got introspection request\n"); + ictx = talloc(intf_p->conn, struct sbus_introspect_ctx); + if (ictx == NULL) { + result = sbus_reply_internal_error(message, intf_p->conn); + } else { + handler_fn = sbus_introspect; + ictx->iface = interface; + handler_data = ictx; + } + } } if (handler_fn) { dbus_req = sbus_new_request(intf_p->conn, intf_p->intf, message); if (!dbus_req) { + talloc_zfree(handler_data); ret = ENOMEM; } else { dbus_req->method = method; - ret = handler_fn(dbus_req, intf_p->intf->instance_data); + if (handler_data) { + /* If the handler uses private instance data, make + * sure they go away when the request does + */ + talloc_steal(dbus_req, handler_data); + } else { + /* If no custom handler data is set, pass on the + * interface data + */ + handler_data = intf_p->intf->instance_data; + } + ret = handler_fn(dbus_req, handler_data); } if (ret != EOK) { - if (dbus_req) - talloc_free(dbus_req); + talloc_free(dbus_req); result = sbus_reply_internal_error(message, intf_p->conn); } else { result = DBUS_HANDLER_RESULT_HANDLED; diff --git a/src/sbus/sssd_dbus_introspect.c b/src/sbus/sssd_dbus_introspect.c new file mode 100644 index 000000000..ffc1f4d38 --- /dev/null +++ b/src/sbus/sssd_dbus_introspect.c @@ -0,0 +1,330 @@ +/* + Authors: + Jakub Hrozek <jhrozek@redhat.com> + + Copyright (C) 2014 Red Hat + + SBUS: Interface introspection + + 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 <sys/time.h> +#include <dbus/dbus.h> + +#include "util/util.h" +#include "sbus/sssd_dbus.h" +#include "sbus/sssd_dbus_private.h" +#include "sbus/sssd_dbus_meta.h" + +#define SSS_INTROSPECT_DOCTYPE \ + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \ + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" + +#define SSS_INTROSPECT_INTERFACE_INTROSPECTABLE \ + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \ + " <method name=\"Introspect\">\n" \ + " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \ + " </method>\n" \ + " </interface>\n" + + +struct introspect_ctx { + FILE *f; + char *buf; + size_t size; + + const struct sbus_interface_meta *iface; +}; + +static int introspect_ctx_destructor(struct introspect_ctx *ictx) +{ + free(ictx->buf); + ictx->buf = NULL; + return 0; +} + +static errno_t introspect_begin(struct introspect_ctx *ictx) +{ + errno_t ret; + + ictx->f = open_memstream(&ictx->buf, &ictx->size); + if (ictx->f == NULL) { + return ENOMEM; + } + + ret = fputs(SSS_INTROSPECT_DOCTYPE, ictx->f); + if (ret < 0) return EIO; + ret = fputs("<node>\n", ictx->f); + if (ret < 0) return EIO; + + ret = fprintf(ictx->f, " <interface name=\"%s\">\n", ictx->iface->name); + if (ret <= 0) return EIO; + + return EOK; +} + +static errno_t introspect_add_arg(struct introspect_ctx *ictx, + const struct sbus_arg_meta *a, + const char *direction) +{ + errno_t ret; + + ret = fprintf(ictx->f, + " <arg type=\"%s\" name=\"%s\"", + a->type, a->name); + if (ret <= 0) return EIO; + + if (direction) { + ret = fprintf(ictx->f, " direction=\"%s\"", direction); + if (ret <= 0) return EIO; + } + + ret = fprintf(ictx->f, "/>\n"); + if (ret <= 0) return EIO; + + return EOK; +} + +#define introspect_add_in_arg(i, a) introspect_add_arg(i, a, "in"); +#define introspect_add_out_arg(i, a) introspect_add_arg(i, a, "out"); +#define introspect_add_sig_arg(i, a) introspect_add_arg(i, a, NULL); + +static errno_t introspect_add_meth(struct introspect_ctx *ictx, + const struct sbus_method_meta *m) +{ + errno_t ret; + int i; + + ret = fprintf(ictx->f, " <method name=\"%s\">\n", m->name); + if (ret <= 0) return EIO; + + if (m->in_args != NULL) { + for (i = 0; m->in_args[i].name != NULL; i++) { + ret = introspect_add_in_arg(ictx, &m->in_args[i]); + if (ret != EOK) { + continue; + } + } + } + + if (m->out_args != NULL) { + for (i = 0; m->out_args[i].name != NULL; i++) { + ret = introspect_add_out_arg(ictx, &m->out_args[i]); + if (ret != EOK) { + continue; + } + } + } + + ret = fputs(" </method>\n", ictx->f); + if (ret < 0) return EIO; + + return EOK; +} + +static errno_t introspect_add_methods(struct introspect_ctx *ictx) +{ + errno_t ret; + int i; + + if (ictx->iface->methods == NULL) { + /* An interface with no methods */ + return EOK; + } + + for (i = 0; ictx->iface->methods[i].name != NULL; i++) { + ret = introspect_add_meth(ictx, &ictx->iface->methods[i]); + if (ret != EOK) { + continue; + } + } + + return EOK; +} + +static errno_t introspect_add_sig(struct introspect_ctx *ictx, + const struct sbus_signal_meta *s) +{ + errno_t ret; + int i; + + ret = fprintf(ictx->f, " <signal name=\"%s\">\n", s->name); + if (ret <= 0) return EIO; + + if (s->args != NULL) { + for (i = 0; s->args[i].name != NULL; i++) { + ret = introspect_add_sig_arg(ictx, &s->args[i]); + if (ret != EOK) { + continue; + } + } + } + + ret = fputs(" </signal>\n", ictx->f); + if (ret < 0) return EIO; + + return EOK; +} + +static errno_t introspect_add_signals(struct introspect_ctx *ictx) +{ + errno_t ret; + int i; + + if (ictx->iface->signals == NULL) { + /* An interface with no signals */ + return EOK; + } + + for (i = 0; ictx->iface->signals[i].name != NULL; i++) { + ret = introspect_add_sig(ictx, &ictx->iface->signals[i]); + if (ret != EOK) { + continue; + } + } + + return EOK; +} + +static errno_t introspect_add_prop(struct introspect_ctx *ictx, + const struct sbus_property_meta *p) +{ + errno_t ret; + + ret = fprintf(ictx->f, " <property name=\"%s\" type=\"%s\" access=\"%s\"/>\n", + p->name, p->type, + p->flags & SBUS_PROPERTY_WRITABLE ? "readwrite" : "read"); + if (ret <= 0) return EIO; + + return EOK; +} + +static errno_t introspect_add_properties(struct introspect_ctx *ictx) +{ + errno_t ret; + int i; + + if (ictx->iface->properties == NULL) { + /* An interface with no properties */ + return EOK; + } + + for (i = 0; ictx->iface->properties[i].name != NULL; i++) { + ret = introspect_add_prop(ictx, &ictx->iface->properties[i]); + if (ret != EOK) { + continue; + } + } + + return EOK; +} + +static errno_t introspect_finish(struct introspect_ctx *ictx) +{ + errno_t ret; + + ret = fputs(" </interface>\n", ictx->f); + if (ret < 0) return EIO; + + ret = fputs(SSS_INTROSPECT_INTERFACE_INTROSPECTABLE, ictx->f); + if (ret < 0) return EIO; + + ret = fputs("</node>\n", ictx->f); + if (ret < 0) return EIO; + + fflush(ictx->f); + return EOK; +} + +static char *sbus_introspect_xml(TALLOC_CTX *mem_ctx, + const struct sbus_interface_meta *iface) +{ + struct introspect_ctx *ictx; + char *buf_out = NULL; + errno_t ret; + + ictx = talloc_zero(mem_ctx, struct introspect_ctx); + if (ictx == NULL) { + return NULL; + } + ictx->iface = iface; + talloc_set_destructor(ictx, introspect_ctx_destructor); + + ret = introspect_begin(ictx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "introspect_begin failed: %d\n", ret); + goto done; + } + + ret = introspect_add_methods(ictx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "introspect_add_methods failed: %d\n", ret); + goto done; + } + + ret = introspect_add_signals(ictx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "introspect_add_signals failed: %d\n", ret); + goto done; + } + + ret = introspect_add_properties(ictx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "introspect_add_properties failed: %d\n", ret); + goto done; + } + + ret = introspect_finish(ictx); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, "introspect_finish failed: %d\n", ret); + goto done; + } + + buf_out = talloc_memdup(mem_ctx, ictx->buf, ictx->size + 1); + DEBUG(SSSDBG_TRACE_LIBS, "Introspection: \n%s\n", buf_out); +done: + talloc_free(ictx); + return buf_out; +} + +int sbus_introspect(struct sbus_request *dbus_req, void *pvt) +{ + char *xml; + DBusError dberr; + const struct sbus_interface_meta *iface; + struct sbus_introspect_ctx *ictx; + + ictx = talloc_get_type(pvt, struct sbus_introspect_ctx); + if (ictx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Invalid pointer!\n"); + return sbus_request_return_and_finish(dbus_req, DBUS_TYPE_INVALID); + } + iface = ictx->iface; + + xml = sbus_introspect_xml(dbus_req, iface); + if (xml == NULL) { + dbus_error_init(&dberr); + dbus_set_error_const(&dberr, + DBUS_ERROR_NO_MEMORY, + "Failed to generate introspection data\n"); + return sbus_request_fail_and_finish(dbus_req, &dberr); + } + + return sbus_request_return_and_finish(dbus_req, + DBUS_TYPE_STRING, &xml, + DBUS_TYPE_INVALID); + +} diff --git a/src/sbus/sssd_dbus_private.h b/src/sbus/sssd_dbus_private.h index 88230cfca..929caeccb 100644 --- a/src/sbus/sssd_dbus_private.h +++ b/src/sbus/sssd_dbus_private.h @@ -98,4 +98,10 @@ struct sbus_request * sbus_new_request(struct sbus_connection *conn, struct sbus_interface *intf, DBusMessage *message); +struct sbus_introspect_ctx { + const struct sbus_interface_meta *iface; +}; + +int sbus_introspect(struct sbus_request *dbus_req, void *pvt); + #endif /* _SSSD_DBUS_PRIVATE_H_ */ diff --git a/src/tests/sbus_tests.c b/src/tests/sbus_tests.c index e4cab454d..194ba60ef 100644 --- a/src/tests/sbus_tests.c +++ b/src/tests/sbus_tests.c @@ -44,6 +44,23 @@ #define PILOT_BLINK "Blink" #define PILOT_EAT "Eat" +#define PILOT_IFACE_INTROSPECT \ + "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n" \ + "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" \ + "<node>\n" \ + " <interface name=\"test.Pilot\">\n" \ + " <method name=\"Blink\">\n" \ + " </method>\n" \ + " <method name=\"Eat\">\n" \ + " </method>\n" \ + " </interface>\n" \ + " <interface name=\"org.freedesktop.DBus.Introspectable\">\n" \ + " <method name=\"Introspect\">\n" \ + " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \ + " </method>\n" \ + " </interface>\n" \ + "</node>\n" + /* our vtable */ struct pilot_vtable { struct sbus_vtable vtable; @@ -276,6 +293,37 @@ START_TEST(test_request_parse_bad_args) } END_TEST +START_TEST(test_introspection) +{ + TALLOC_CTX *ctx; + DBusConnection *client; + DBusError error = DBUS_ERROR_INIT; + DBusMessage *reply; + char *xml; + + ctx = talloc_new(NULL); + client = test_dbus_setup_mock(ctx, NULL, pilot_test_server_init, NULL); + + reply = test_dbus_call_sync(client, + "/test/leela", + DBUS_INTROSPECT_INTERFACE, + DBUS_INTROSPECT_METHOD, + &error, + DBUS_TYPE_INVALID); /* bad agruments */ + + ck_assert(reply != NULL); + ck_assert(!dbus_error_is_set(&error)); + ck_assert(dbus_message_get_args(reply, NULL, + DBUS_TYPE_STRING, &xml, + DBUS_TYPE_INVALID)); + ck_assert_str_eq(PILOT_IFACE_INTROSPECT, xml); + + dbus_message_unref(reply); + + talloc_free(ctx); +} +END_TEST + TCase *create_sbus_tests(void) { TCase *tc = tcase_create("tests"); @@ -283,6 +331,7 @@ TCase *create_sbus_tests(void) tcase_add_test(tc, test_raw_handler); tcase_add_test(tc, test_request_parse_ok); tcase_add_test(tc, test_request_parse_bad_args); + tcase_add_test(tc, test_introspection); return tc; } |