diff options
63 files changed, 4233 insertions, 205 deletions
diff --git a/Makefile.in b/Makefile.in index abe54c60..e207e4b0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -256,6 +256,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -310,6 +312,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -399,6 +402,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -1,3 +1,51 @@ +D-Bus 1.9.12 (2015-02-19) +== + +The “monster lasagna” release. + +Dependencies: + +• Ducktype and yelp-tools are now required to build complete documentation. + +Enhancements: + +• D-Bus Specification version 0.26 + · GetConnectionCredentials can return LinuxSecurityLabel or WindowsSID + · document the BecomeMonitor method + +• On Linux, add LinuxSecurityLabel to GetConnectionCredentials + (fd.o #89041; Tyler Hicks, Simon McVittie) + +• On Linux, add support for AppArmor mediation of message sending and + receiving and name ownership (paralleling existing SELinux mediation + support), and eavesdropping (a new check, currently AppArmor-specific) + (fd.o #75113; John Johansen, Tyler Hicks, Simon McVittie) + +• In dbus-send and dbus-monitor, pretty-print \0-terminated bytestrings + that have printable ASCII contents; we previously only did this for + unterminated bytestrings (fd.o #89109, Simon McVittie) + +• Add a guide to designing good D-Bus APIs (fd.o #88994, Philip Withnall) + +• On Windows, add WindowsSID to GetConnectionCredentials + (fd.o #54445, Ralf Habacker) + +• Improve clarity of dbus-monitor --profile output and add more columns + (fd.o #89165, Ralf Habacker) + +• Add a man page for dbus-test-tool, and build it under CMake as well + as Autotools (fd.o#89086, Simon McVittie) + +• If dbus-daemon was compiled with --enable-verbose, add a D-Bus API + to control it at runtime, overriding the DBUS_VERBOSE environment variable + (fd.o #88896, Ralf Habacker) + +Fixes: + +• Reduce the number of file descriptors used in the fd-passing test, + avoiding failure under the default Linux fd limit, and automatically + skip it if the rlimit is too small (fd.o #88998, Simon McVittie) + D-Bus 1.9.10 (2015-02-09) == diff --git a/bus/Makefile.am b/bus/Makefile.am index c0bc1549..9d3cb006 100644 --- a/bus/Makefile.am +++ b/bus/Makefile.am @@ -4,6 +4,7 @@ dbus_daemon_execdir = $(DBUS_DAEMONDIR) DBUS_BUS_LIBS = \ $(XML_LIBS) \ $(SELINUX_LIBS) \ + $(APPARMOR_LIBS) \ $(THREAD_LIBS) \ $(ADT_LIBS) \ $(NETWORK_libs) \ @@ -18,6 +19,7 @@ DBUS_LAUNCHER_LIBS = \ AM_CPPFLAGS = \ -I$(top_srcdir) \ $(XML_CFLAGS) \ + $(APPARMOR_CFLAGS) \ -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \ -DDBUS_COMPILATION \ -DDBUS_STATIC_BUILD \ @@ -69,6 +71,8 @@ BUS_SOURCES= \ activation.c \ activation.h \ activation-exit-codes.h \ + apparmor.c \ + apparmor.h \ bus.c \ bus.h \ config-parser.c \ diff --git a/bus/Makefile.in b/bus/Makefile.in index 6bbcecd6..f162f171 100644 --- a/bus/Makefile.in +++ b/bus/Makefile.in @@ -136,21 +136,21 @@ am__installdirs = "$(DESTDIR)$(dbus_daemon_execdir)" \ PROGRAMS = $(dbus_daemon_exec_PROGRAMS) $(libexec_PROGRAMS) \ $(noinst_PROGRAMS) am__dbus_daemon_SOURCES_DIST = activation.c activation.h \ - activation-exit-codes.h bus.c bus.h config-parser.c \ - config-parser.h config-parser-common.c config-parser-common.h \ - connection.c connection.h desktop-file.c desktop-file.h \ - dir-watch-default.c dir-watch-inotify.c dir-watch-kqueue.c \ - dir-watch.h dispatch.c dispatch.h driver.c driver.h \ - expirelist.c expirelist.h policy.c policy.h selinux.h \ - selinux.c services.c services.h signals.c signals.h stats.c \ - stats.h test.c test.h utils.c utils.h config-loader-expat.c \ - main.c + activation-exit-codes.h apparmor.c apparmor.h bus.c bus.h \ + config-parser.c config-parser.h config-parser-common.c \ + config-parser-common.h connection.c connection.h \ + desktop-file.c desktop-file.h dir-watch-default.c \ + dir-watch-inotify.c dir-watch-kqueue.c dir-watch.h dispatch.c \ + dispatch.h driver.c driver.h expirelist.c expirelist.h \ + policy.c policy.h selinux.h selinux.c services.c services.h \ + signals.c signals.h stats.c stats.h test.c test.h utils.c \ + utils.h config-loader-expat.c main.c @DBUS_BUS_ENABLE_INOTIFY_FALSE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-default.$(OBJEXT) @DBUS_BUS_ENABLE_INOTIFY_TRUE@@DBUS_BUS_ENABLE_KQUEUE_FALSE@am__objects_1 = dir-watch-inotify.$(OBJEXT) @DBUS_BUS_ENABLE_KQUEUE_TRUE@am__objects_1 = \ @DBUS_BUS_ENABLE_KQUEUE_TRUE@ dir-watch-kqueue.$(OBJEXT) am__objects_2 = config-loader-expat.$(OBJEXT) -am__objects_3 = activation.$(OBJEXT) bus.$(OBJEXT) \ +am__objects_3 = activation.$(OBJEXT) apparmor.$(OBJEXT) bus.$(OBJEXT) \ config-parser.$(OBJEXT) config-parser-common.$(OBJEXT) \ connection.$(OBJEXT) desktop-file.$(OBJEXT) $(am__objects_1) \ dispatch.$(OBJEXT) driver.$(OBJEXT) expirelist.$(OBJEXT) \ @@ -162,7 +162,7 @@ dbus_daemon_OBJECTS = $(am_dbus_daemon_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) dbus_daemon_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) AM_V_lt = $(am__v_lt_@AM_V@) @@ -195,15 +195,15 @@ dbus_daemon_launch_helper_test_OBJECTS = \ dbus_daemon_launch_helper_test_DEPENDENCIES = \ $(top_builddir)/dbus/libdbus-internal.la $(am__DEPENDENCIES_3) am__test_bus_SOURCES_DIST = activation.c activation.h \ - activation-exit-codes.h bus.c bus.h config-parser.c \ - config-parser.h config-parser-common.c config-parser-common.h \ - connection.c connection.h desktop-file.c desktop-file.h \ - dir-watch-default.c dir-watch-inotify.c dir-watch-kqueue.c \ - dir-watch.h dispatch.c dispatch.h driver.c driver.h \ - expirelist.c expirelist.h policy.c policy.h selinux.h \ - selinux.c services.c services.h signals.c signals.h stats.c \ - stats.h test.c test.h utils.c utils.h config-loader-expat.c \ - test-main.c + activation-exit-codes.h apparmor.c apparmor.h bus.c bus.h \ + config-parser.c config-parser.h config-parser-common.c \ + config-parser-common.h connection.c connection.h \ + desktop-file.c desktop-file.h dir-watch-default.c \ + dir-watch-inotify.c dir-watch-kqueue.c dir-watch.h dispatch.c \ + dispatch.h driver.c driver.h expirelist.c expirelist.h \ + policy.c policy.h selinux.h selinux.c services.c services.h \ + signals.c signals.h stats.c stats.h test.c test.h utils.c \ + utils.h config-loader-expat.c test-main.c am_test_bus_OBJECTS = $(am__objects_3) test-main.$(OBJEXT) test_bus_OBJECTS = $(am_test_bus_OBJECTS) test_bus_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la \ @@ -328,6 +328,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -382,6 +384,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -471,6 +474,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -535,6 +539,7 @@ dbus_daemon_execdir = $(DBUS_DAEMONDIR) DBUS_BUS_LIBS = \ $(XML_LIBS) \ $(SELINUX_LIBS) \ + $(APPARMOR_LIBS) \ $(THREAD_LIBS) \ $(ADT_LIBS) \ $(NETWORK_libs) \ @@ -549,6 +554,7 @@ DBUS_LAUNCHER_LIBS = \ AM_CPPFLAGS = \ -I$(top_srcdir) \ $(XML_CFLAGS) \ + $(APPARMOR_CFLAGS) \ -DDBUS_SYSTEM_CONFIG_FILE=\""$(configdir)/system.conf"\" \ -DDBUS_COMPILATION \ -DDBUS_STATIC_BUILD \ @@ -583,6 +589,8 @@ BUS_SOURCES = \ activation.c \ activation.h \ activation-exit-codes.h \ + apparmor.c \ + apparmor.h \ bus.c \ bus.h \ config-parser.c \ @@ -976,6 +984,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper-bin.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation-helper.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/activation.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apparmor.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bus.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-loader-expat.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config-parser-common.Po@am__quote@ diff --git a/bus/apparmor.c b/bus/apparmor.c new file mode 100644 index 00000000..a1b3621a --- /dev/null +++ b/bus/apparmor.c @@ -0,0 +1,1128 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * apparmor.c AppArmor security checks for D-Bus + * + * Based on selinux.c + * + * Copyright © 2014-2015 Canonical, Ltd. + * + * Licensed under the Academic Free License version 2.1 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <config.h> +#include "apparmor.h" + +#ifdef HAVE_APPARMOR + +#include <dbus/dbus-internals.h> +#include <dbus/dbus-string.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/apparmor.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <syslog.h> +#include <unistd.h> + +#ifdef HAVE_LIBAUDIT +#include <cap-ng.h> +#include <libaudit.h> +#endif /* HAVE_LIBAUDIT */ + +#include "connection.h" +#include "utils.h" + +/* Store the value telling us if AppArmor D-Bus mediation is enabled. */ +static dbus_bool_t apparmor_enabled = FALSE; + +typedef enum { + APPARMOR_DISABLED, + APPARMOR_ENABLED, + APPARMOR_REQUIRED +} AppArmorConfigMode; + +/* Store the value of the AppArmor mediation mode in the bus configuration */ +static AppArmorConfigMode apparmor_config_mode = APPARMOR_ENABLED; + +#ifdef HAVE_LIBAUDIT +static int audit_fd = -1; +#endif + +/* The AppArmor context, consisting of a label and a mode. */ +struct BusAppArmorConfinement +{ + int refcount; /* Reference count */ + + char *label; /* AppArmor confinement label */ + const char *mode; /* AppArmor confinement mode (freed by freeing *label) */ +}; + +static BusAppArmorConfinement *bus_con = NULL; + +/** + * Callers of this function give up ownership of the *label and *mode + * pointers. + * + * Additionally, the responsibility of freeing *label and *mode becomes the + * responsibility of the bus_apparmor_confinement_unref() function. However, it + * does not free *mode because libapparmor's aa_getcon(), and libapparmor's + * other related functions, allocate a single buffer for *label and *mode and + * then separate the two char arrays with a NUL char. See the aa_getcon(2) man + * page for more details. + */ +static BusAppArmorConfinement* +bus_apparmor_confinement_new (char *label, + const char *mode) +{ + BusAppArmorConfinement *confinement; + + confinement = dbus_new0 (BusAppArmorConfinement, 1); + if (confinement != NULL) + { + confinement->refcount = 1; + confinement->label = label; + confinement->mode = mode; + } + + return confinement; +} + +void +bus_apparmor_audit_init (void) +{ +#ifdef HAVE_LIBAUDIT + audit_fd = audit_open (); + + if (audit_fd < 0) + { + /* If kernel doesn't support audit, bail out */ + if (errno == EINVAL || errno == EPROTONOSUPPORT || errno == EAFNOSUPPORT) + return; + /* If user bus, bail out */ + if (errno == EPERM && getuid () != 0) + return; + _dbus_warn ("Failed opening connection to the audit subsystem"); + } +#endif /* HAVE_LIBAUDIT */ +} + +/* + * Return TRUE on successful check, FALSE on OOM. + * Set *is_supported to whether AA has D-Bus features. + */ +static dbus_bool_t +_bus_apparmor_detect_aa_dbus_support (dbus_bool_t *is_supported) +{ + int mask_file; + DBusString aa_dbus; + char *aa_securityfs = NULL; + dbus_bool_t retval = FALSE; + + *is_supported = FALSE; + + if (!_dbus_string_init (&aa_dbus)) + return FALSE; + + if (aa_find_mountpoint (&aa_securityfs) != 0) + goto out; + + /* + * John Johansen has confirmed that the mainline kernel will not have + * the apparmorfs/features/dbus/mask file until the mainline kernel + * has AppArmor getpeersec support. + */ + if (!_dbus_string_append (&aa_dbus, aa_securityfs) || + !_dbus_string_append (&aa_dbus, "/features/dbus/mask")) + goto out; + + /* We need to open() the flag file, not just stat() it, because AppArmor + * does not mediate stat() in the apparmorfs. If you have a + * dbus-daemon inside an LXC container, with insufficiently broad + * AppArmor privileges to do its own AppArmor mediation, the desired + * result is that it behaves as if AppArmor was not present; but a stat() + * here would succeed, and result in it trying and failing to do full + * mediation. https://bugs.launchpad.net/ubuntu/+source/dbus/+bug/1238267 */ + mask_file = open (_dbus_string_get_const_data (&aa_dbus), + O_RDONLY | O_CLOEXEC); + if (mask_file != -1) + { + *is_supported = TRUE; + close (mask_file); + } + + retval = TRUE; + +out: + free (aa_securityfs); + _dbus_string_free (&aa_dbus); + + return retval; +} + +static dbus_bool_t +modestr_is_complain (const char *mode) +{ + if (mode && strcmp (mode, "complain") == 0) + return TRUE; + return FALSE; +} + +static void +log_message (dbus_bool_t allow, const char *op, DBusString *data) +{ + const char *mstr; + + if (allow) + mstr = "ALLOWED"; + else + mstr = "DENIED"; + +#ifdef HAVE_LIBAUDIT + if (audit_fd >= 0) + { + DBusString avc; + + capng_get_caps_process (); + if (!capng_have_capability (CAPNG_EFFECTIVE, CAP_AUDIT_WRITE)) + goto syslog; + + if (!_dbus_string_init (&avc)) + goto syslog; + + if (!_dbus_string_append_printf (&avc, + "apparmor=\"%s\" operation=\"dbus_%s\" %s\n", + mstr, op, _dbus_string_get_const_data (data))) + { + _dbus_string_free (&avc); + goto syslog; + } + + /* FIXME: need to change this to show real user */ + audit_log_user_avc_message (audit_fd, AUDIT_USER_AVC, + _dbus_string_get_const_data (&avc), + NULL, NULL, NULL, getuid ()); + _dbus_string_free (&avc); + return; + } + +syslog: +#endif /* HAVE_LIBAUDIT */ + + syslog (LOG_USER | LOG_NOTICE, "apparmor=\"%s\" operation=\"dbus_%s\" %s\n", + mstr, op, _dbus_string_get_const_data (data)); +} + +static dbus_bool_t +_dbus_append_pair_uint (DBusString *auxdata, const char *name, + unsigned long value) +{ + return _dbus_string_append (auxdata, " ") && + _dbus_string_append (auxdata, name) && + _dbus_string_append (auxdata, "=") && + _dbus_string_append_uint (auxdata, value); +} + +static dbus_bool_t +_dbus_append_pair_str (DBusString *auxdata, const char *name, const char *value) +{ + return _dbus_string_append (auxdata, " ") && + _dbus_string_append (auxdata, name) && + _dbus_string_append (auxdata, "=\"") && + _dbus_string_append (auxdata, value) && + _dbus_string_append (auxdata, "\""); +} + +static dbus_bool_t +_dbus_append_mask (DBusString *auxdata, uint32_t mask) +{ + const char *mask_str; + + /* Only one permission bit can be set */ + if (mask == AA_DBUS_SEND) + mask_str = "send"; + else if (mask == AA_DBUS_RECEIVE) + mask_str = "receive"; + else if (mask == AA_DBUS_BIND) + mask_str = "bind"; + else + return FALSE; + + return _dbus_append_pair_str (auxdata, "mask", mask_str); +} + +static dbus_bool_t +is_unconfined (const char *con, const char *mode) +{ + /* treat con == NULL as confined as it is going to result in a denial */ + if ((!mode && con && strcmp (con, "unconfined") == 0) || + strcmp (mode, "unconfined") == 0) + { + return TRUE; + } + + return FALSE; +} + +static dbus_bool_t +query_append (DBusString *query, const char *buffer) +{ + if (!_dbus_string_append_byte (query, '\0')) + return FALSE; + + if (buffer && !_dbus_string_append (query, buffer)) + return FALSE; + + return TRUE; +} + +static dbus_bool_t +build_common_query (DBusString *query, const char *con, const char *bustype) +{ + /** + * libapparmor's aa_query_label() function scribbles over the first + * AA_QUERY_CMD_LABEL_SIZE bytes of the query string with a private value. + */ + return _dbus_string_insert_bytes (query, 0, AA_QUERY_CMD_LABEL_SIZE, 0) && + _dbus_string_append (query, con) && + _dbus_string_append_byte (query, '\0') && + _dbus_string_append_byte (query, AA_CLASS_DBUS) && + _dbus_string_append (query, bustype ? bustype : ""); +} + +static dbus_bool_t +build_service_query (DBusString *query, + const char *con, + const char *bustype, + const char *name) +{ + return build_common_query (query, con, bustype) && + query_append (query, name); +} + +static dbus_bool_t +build_message_query (DBusString *query, + const char *src_con, + const char *bustype, + const char *name, + const char *dst_con, + const char *path, + const char *interface, + const char *member) +{ + return build_common_query (query, src_con, bustype) && + query_append (query, name) && + query_append (query, dst_con) && + query_append (query, path) && + query_append (query, interface) && + query_append (query, member); +} + +static dbus_bool_t +build_eavesdrop_query (DBusString *query, const char *con, const char *bustype) +{ + return build_common_query (query, con, bustype); +} + +static void +set_error_from_query_errno (DBusError *error, int error_number) +{ + dbus_set_error (error, _dbus_error_from_errno (error_number), + "Failed to query AppArmor policy: %s", + _dbus_strerror (error_number)); +} + +static void +set_error_from_denied_message (DBusError *error, + DBusConnection *sender, + DBusConnection *proposed_recipient, + dbus_bool_t requested_reply, + const char *msgtype, + const char *path, + const char *interface, + const char *member, + const char *error_name, + const char *destination) +{ + const char *proposed_recipient_loginfo; + const char *unset = "(unset)"; + + proposed_recipient_loginfo = proposed_recipient ? + bus_connection_get_loginfo (proposed_recipient) : + "bus"; + + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "An AppArmor policy prevents this sender from sending this " + "message to this recipient; type=\"%s\", " + "sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" " + "error name=\"%s\" requested_reply=\"%d\" " + "destination=\"%s\" (%s)", + msgtype, + bus_connection_get_name (sender), + bus_connection_get_loginfo (sender), + interface ? interface : unset, + member ? member : unset, + error_name ? error_name : unset, + requested_reply, + destination, + proposed_recipient_loginfo); +} +#endif /* HAVE_APPARMOR */ + +/** + * Do early initialization; determine whether AppArmor is enabled. + * Return TRUE on successful check (whether AppArmor is actually + * enabled or not) or FALSE on OOM. + */ +dbus_bool_t +bus_apparmor_pre_init (void) +{ +#ifdef HAVE_APPARMOR + apparmor_enabled = FALSE; + + if (!aa_is_enabled ()) + return TRUE; + + if (!_bus_apparmor_detect_aa_dbus_support (&apparmor_enabled)) + return FALSE; +#endif + + return TRUE; +} + +dbus_bool_t +bus_apparmor_set_mode_from_config (const char *mode, DBusError *error) +{ +#ifdef HAVE_APPARMOR + if (mode != NULL) + { + if (strcmp (mode, "disabled") == 0) + apparmor_config_mode = APPARMOR_DISABLED; + else if (strcmp (mode, "enabled") == 0) + apparmor_config_mode = APPARMOR_ENABLED; + else if (strcmp (mode, "required") == 0) + apparmor_config_mode = APPARMOR_REQUIRED; + else + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Mode attribute on <apparmor> must have value " + "\"required\", \"enabled\" or \"disabled\", " + "not \"%s\"", mode); + return FALSE; + } + } + + return TRUE; +#else + if (mode == NULL || strcmp (mode, "disabled") == 0 || + strcmp (mode, "enabled") == 0) + return TRUE; + + dbus_set_error (error, DBUS_ERROR_FAILED, + "Mode attribute on <apparmor> must have value \"enabled\" or " + "\"disabled\" but cannot be \"%s\" when D-Bus is built " + "without AppArmor support", mode); + return FALSE; +#endif +} + +/** + * Verify that the config mode is compatible with the kernel's AppArmor + * support. If AppArmor mediation will be enabled, determine the bus + * confinement label. + */ +dbus_bool_t +bus_apparmor_full_init (DBusError *error) +{ +#ifdef HAVE_APPARMOR + char *label, *mode; + + if (apparmor_enabled) + { + if (apparmor_config_mode == APPARMOR_DISABLED) + { + apparmor_enabled = FALSE; + return TRUE; + } + + if (bus_con == NULL) + { + if (aa_getcon (&label, &mode) == -1) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Error getting AppArmor context of bus: %s", + _dbus_strerror (errno)); + return FALSE; + } + + bus_con = bus_apparmor_confinement_new (label, mode); + if (bus_con == NULL) + { + BUS_SET_OOM (error); + free (label); + return FALSE; + } + } + } + else + { + if (apparmor_config_mode == APPARMOR_REQUIRED) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "AppArmor mediation required but not present"); + return FALSE; + } + else if (apparmor_config_mode == APPARMOR_ENABLED) + { + return TRUE; + } + } +#endif + + return TRUE; +} + +void +bus_apparmor_shutdown (void) +{ +#ifdef HAVE_APPARMOR + if (!apparmor_enabled) + return; + + _dbus_verbose ("AppArmor shutdown\n"); + + bus_apparmor_confinement_unref (bus_con); + bus_con = NULL; + +#ifdef HAVE_LIBAUDIT + audit_close (audit_fd); +#endif /* HAVE_LIBAUDIT */ + +#endif /* HAVE_APPARMOR */ +} + +dbus_bool_t +bus_apparmor_enabled (void) +{ +#ifdef HAVE_APPARMOR + return apparmor_enabled; +#else + return FALSE; +#endif +} + +void +bus_apparmor_confinement_unref (BusAppArmorConfinement *confinement) +{ +#ifdef HAVE_APPARMOR + if (!apparmor_enabled) + return; + + _dbus_assert (confinement != NULL); + _dbus_assert (confinement->refcount > 0); + + confinement->refcount -= 1; + + if (confinement->refcount == 0) + { + /** + * Do not free confinement->mode, as libapparmor does a single malloc for + * both confinement->label and confinement->mode. + */ + free (confinement->label); + dbus_free (confinement); + } +#endif +} + +void +bus_apparmor_confinement_ref (BusAppArmorConfinement *confinement) +{ +#ifdef HAVE_APPARMOR + if (!apparmor_enabled) + return; + + _dbus_assert (confinement != NULL); + _dbus_assert (confinement->refcount > 0); + + confinement->refcount += 1; +#endif /* HAVE_APPARMOR */ +} + +BusAppArmorConfinement* +bus_apparmor_init_connection_confinement (DBusConnection *connection, + DBusError *error) +{ +#ifdef HAVE_APPARMOR + BusAppArmorConfinement *confinement; + char *label, *mode; + int fd; + + if (!apparmor_enabled) + return NULL; + + _dbus_assert (connection != NULL); + + if (!dbus_connection_get_socket (connection, &fd)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Failed to get socket file descriptor of connection"); + return NULL; + } + + if (aa_getpeercon (fd, &label, &mode) == -1) + { + if (errno == ENOMEM) + BUS_SET_OOM (error); + else + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to get AppArmor confinement information of socket peer: %s", + _dbus_strerror (errno)); + return NULL; + } + + confinement = bus_apparmor_confinement_new (label, mode); + if (confinement == NULL) + { + BUS_SET_OOM (error); + free (label); + return NULL; + } + + return confinement; +#else + return NULL; +#endif /* HAVE_APPARMOR */ +} + +/** + * Returns true if the given connection can acquire a service, + * using the tasks security context + * + * @param connection connection that wants to own the service + * @param bustype name of the bus + * @param service_name the name of the service to acquire + * @param error the reason for failure when FALSE is returned + * @returns TRUE if acquire is permitted + */ +dbus_bool_t +bus_apparmor_allows_acquire_service (DBusConnection *connection, + const char *bustype, + const char *service_name, + DBusError *error) +{ + +#ifdef HAVE_APPARMOR + BusAppArmorConfinement *con = NULL; + DBusString qstr, auxdata; + dbus_bool_t free_auxdata = FALSE; + dbus_bool_t allow = FALSE, audit = TRUE; + unsigned long pid; + int res, serrno = 0; + + if (!apparmor_enabled) + return TRUE; + + _dbus_assert (connection != NULL); + + con = bus_connection_dup_apparmor_confinement (connection); + + if (is_unconfined (con->label, con->mode)) + { + allow = TRUE; + audit = FALSE; + goto out; + } + + if (!_dbus_string_init (&qstr)) + goto oom; + + if (!build_service_query (&qstr, con->label, bustype, service_name)) + { + _dbus_string_free (&qstr); + goto oom; + } + + res = aa_query_label (AA_DBUS_BIND, + _dbus_string_get_data (&qstr), + _dbus_string_get_length (&qstr), + &allow, &audit); + _dbus_string_free (&qstr); + if (res == -1) + { + serrno = errno; + set_error_from_query_errno (error, serrno); + goto audit; + } + + /* Don't fail operations on profiles in complain mode */ + if (modestr_is_complain (con->mode)) + allow = TRUE; + + if (!allow) + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection \"%s\" is not allowed to own the service " + "\"%s\" due to AppArmor policy", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : "(inactive)", + service_name); + + if (!audit) + goto out; + + audit: + if (!_dbus_string_init (&auxdata)) + goto oom; + free_auxdata = TRUE; + + if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) + goto oom; + + if (!_dbus_append_pair_str (&auxdata, "name", service_name)) + goto oom; + + if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) + goto oom; + + if (!_dbus_append_mask (&auxdata, AA_DBUS_BIND)) + goto oom; + + if (connection && dbus_connection_get_unix_process_id (connection, &pid) && + !_dbus_append_pair_uint (&auxdata, "pid", pid)) + goto oom; + + if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) + goto oom; + + log_message (allow, "bind", &auxdata); + + out: + if (con != NULL) + bus_apparmor_confinement_unref (con); + if (free_auxdata) + _dbus_string_free (&auxdata); + return allow; + + oom: + if (error != NULL && !dbus_error_is_set (error)) + BUS_SET_OOM (error); + allow = FALSE; + goto out; + +#else + return TRUE; +#endif /* HAVE_APPARMOR */ +} + +/** + * Check if Apparmor security controls allow the message to be sent to a + * particular connection based on the security context of the sender and + * that of the receiver. The destination connection need not be the + * addressed recipient, it could be an "eavesdropper" + * + * @param sender the sender of the message. + * @param proposed_recipient the connection the message is to be sent to. + * @param requested_reply TRUE if the message is a reply requested by + * proposed_recipient + * @param bustype name of the bus + * @param msgtype message type (DBUS_MESSAGE_TYPE_METHOD_CALL, etc.) + * @param path object path the message should be sent to + * @param interface the type of the object instance + * @param member the member of the object + * @param error_name the name of the error if the message type is error + * @param destination name that the message should be sent to + * @param source name that the message should be sent from + * @param error the reason for failure when FALSE is returned + * @returns TRUE if the message is permitted + */ +dbus_bool_t +bus_apparmor_allows_send (DBusConnection *sender, + DBusConnection *proposed_recipient, + dbus_bool_t requested_reply, + const char *bustype, + int msgtype, + const char *path, + const char *interface, + const char *member, + const char *error_name, + const char *destination, + const char *source, + DBusError *error) +{ +#ifdef HAVE_APPARMOR + BusAppArmorConfinement *src_con = NULL, *dst_con = NULL; + DBusString qstr, auxdata; + dbus_bool_t src_allow = FALSE, dst_allow = FALSE; + dbus_bool_t src_audit = TRUE, dst_audit = TRUE; + dbus_bool_t free_auxdata = FALSE; + unsigned long pid; + int len, res, src_errno = 0, dst_errno = 0; + uint32_t src_perm = AA_DBUS_SEND, dst_perm = AA_DBUS_RECEIVE; + const char *msgtypestr = dbus_message_type_to_string(msgtype); + + if (!apparmor_enabled) + return TRUE; + + _dbus_assert (sender != NULL); + + src_con = bus_connection_dup_apparmor_confinement (sender); + + if (proposed_recipient) + { + dst_con = bus_connection_dup_apparmor_confinement (proposed_recipient); + } + else + { + dst_con = bus_con; + bus_apparmor_confinement_ref (dst_con); + } + + /* map reply messages to initial send and receive permission. That is + * permission to receive a message from X grants permission to reply to X. + * And permission to send a message to Y grants permission to receive a reply + * from Y. Note that this only applies to requested replies. Unrequested + * replies still require a policy query. + */ + if (requested_reply) + { + /* ignore requested reply messages and let dbus reply mapping handle them + * as the send was already allowed + */ + src_allow = TRUE; + dst_allow = TRUE; + goto out; + } + + if (is_unconfined (src_con->label, src_con->mode)) + { + src_allow = TRUE; + src_audit = FALSE; + } + else + { + if (!_dbus_string_init (&qstr)) + goto oom; + + if (!build_message_query (&qstr, src_con->label, bustype, destination, + dst_con->label, path, interface, member)) + { + _dbus_string_free (&qstr); + goto oom; + } + + res = aa_query_label (src_perm, + _dbus_string_get_data (&qstr), + _dbus_string_get_length (&qstr), + &src_allow, &src_audit); + _dbus_string_free (&qstr); + if (res == -1) + { + src_errno = errno; + set_error_from_query_errno (error, src_errno); + goto audit; + } + } + + if (is_unconfined (dst_con->label, dst_con->mode)) + { + dst_allow = TRUE; + dst_audit = FALSE; + } + else + { + if (!_dbus_string_init (&qstr)) + goto oom; + + if (!build_message_query (&qstr, dst_con->label, bustype, source, + src_con->label, path, interface, member)) + { + _dbus_string_free (&qstr); + goto oom; + } + + res = aa_query_label (dst_perm, + _dbus_string_get_data (&qstr), + _dbus_string_get_length (&qstr), + &dst_allow, &dst_audit); + _dbus_string_free (&qstr); + if (res == -1) + { + dst_errno = errno; + set_error_from_query_errno (error, dst_errno); + goto audit; + } + } + + /* Don't fail operations on profiles in complain mode */ + if (modestr_is_complain (src_con->mode)) + src_allow = TRUE; + if (modestr_is_complain (dst_con->mode)) + dst_allow = TRUE; + + if (!src_allow || !dst_allow) + set_error_from_denied_message (error, + sender, + proposed_recipient, + requested_reply, + msgtypestr, + path, + interface, + member, + error_name, + destination); + + /* Don't audit the message if one of the following conditions is true: + * 1) The AppArmor query indicates that auditing should not happen. + * 2) The message is a reply type. Reply message are not audited because + * the AppArmor policy language does not have the notion of a reply + * message. Unrequested replies will be silently discarded if the sender + * does not have permission to send to the receiver or if the receiver + * does not have permission to receive from the sender. + */ + if ((!src_audit && !dst_audit) || + (msgtype == DBUS_MESSAGE_TYPE_METHOD_RETURN || + msgtype == DBUS_MESSAGE_TYPE_ERROR)) + goto out; + + audit: + if (!_dbus_string_init (&auxdata)) + goto oom; + free_auxdata = TRUE; + + if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) + goto oom; + + if (path && !_dbus_append_pair_str (&auxdata, "path", path)) + goto oom; + + if (interface && !_dbus_append_pair_str (&auxdata, "interface", interface)) + goto oom; + + if (member && !_dbus_append_pair_str (&auxdata, "member", member)) + goto oom; + + if (error_name && !_dbus_append_pair_str (&auxdata, "error_name", error_name)) + goto oom; + + len = _dbus_string_get_length (&auxdata); + + if (src_audit) + { + if (!_dbus_append_mask (&auxdata, src_perm)) + goto oom; + + if (destination && !_dbus_append_pair_str (&auxdata, "name", destination)) + goto oom; + + if (sender && dbus_connection_get_unix_process_id (sender, &pid) && + !_dbus_append_pair_uint (&auxdata, "pid", pid)) + goto oom; + + if (src_con->label && + !_dbus_append_pair_str (&auxdata, "label", src_con->label)) + goto oom; + + if (proposed_recipient && + dbus_connection_get_unix_process_id (proposed_recipient, &pid) && + !_dbus_append_pair_uint (&auxdata, "peer_pid", pid)) + goto oom; + + if (dst_con->label && + !_dbus_append_pair_str (&auxdata, "peer_label", dst_con->label)) + goto oom; + + if (src_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (src_errno))) + goto oom; + + if (dst_errno && + !_dbus_append_pair_str (&auxdata, "peer_info", strerror (dst_errno))) + goto oom; + + log_message (src_allow, msgtypestr, &auxdata); + } + if (dst_audit) + { + _dbus_string_set_length (&auxdata, len); + + if (source && !_dbus_append_pair_str (&auxdata, "name", source)) + goto oom; + + if (!_dbus_append_mask (&auxdata, dst_perm)) + goto oom; + + if (proposed_recipient && + dbus_connection_get_unix_process_id (proposed_recipient, &pid) && + !_dbus_append_pair_uint (&auxdata, "pid", pid)) + goto oom; + + if (dst_con->label && + !_dbus_append_pair_str (&auxdata, "label", dst_con->label)) + goto oom; + + if (sender && dbus_connection_get_unix_process_id (sender, &pid) && + !_dbus_append_pair_uint (&auxdata, "peer_pid", pid)) + goto oom; + + if (src_con->label && + !_dbus_append_pair_str (&auxdata, "peer_label", src_con->label)) + goto oom; + + if (dst_errno && !_dbus_append_pair_str (&auxdata, "info", strerror (dst_errno))) + goto oom; + + if (src_errno && + !_dbus_append_pair_str (&auxdata, "peer_info", strerror (src_errno))) + goto oom; + + log_message (dst_allow, msgtypestr, &auxdata); + } + + out: + if (src_con != NULL) + bus_apparmor_confinement_unref (src_con); + if (dst_con != NULL) + bus_apparmor_confinement_unref (dst_con); + if (free_auxdata) + _dbus_string_free (&auxdata); + + return src_allow && dst_allow; + + oom: + if (error != NULL && !dbus_error_is_set (error)) + BUS_SET_OOM (error); + src_allow = FALSE; + dst_allow = FALSE; + goto out; + +#else + return TRUE; +#endif /* HAVE_APPARMOR */ +} + +/** + * Check if Apparmor security controls allow the connection to eavesdrop on + * other connections. + * + * @param connection the connection attempting to eavesdrop. + * @param bustype name of the bus + * @param error the reason for failure when FALSE is returned + * @returns TRUE if eavesdropping is permitted + */ +dbus_bool_t +bus_apparmor_allows_eavesdropping (DBusConnection *connection, + const char *bustype, + DBusError *error) +{ +#ifdef HAVE_APPARMOR + BusAppArmorConfinement *con = NULL; + DBusString qstr, auxdata; + dbus_bool_t allow = FALSE, audit = TRUE; + dbus_bool_t free_auxdata = FALSE; + unsigned long pid; + int res, serrno = 0; + + if (!apparmor_enabled) + return TRUE; + + con = bus_connection_dup_apparmor_confinement (connection); + + if (is_unconfined (con->label, con->mode)) + { + allow = TRUE; + audit = FALSE; + goto out; + } + + if (!_dbus_string_init (&qstr)) + goto oom; + + if (!build_eavesdrop_query (&qstr, con->label, bustype)) + { + _dbus_string_free (&qstr); + goto oom; + } + + res = aa_query_label (AA_DBUS_EAVESDROP, + _dbus_string_get_data (&qstr), + _dbus_string_get_length (&qstr), + &allow, &audit); + _dbus_string_free (&qstr); + if (res == -1) + { + serrno = errno; + set_error_from_query_errno (error, serrno); + goto audit; + } + + /* Don't fail operations on profiles in complain mode */ + if (modestr_is_complain (con->mode)) + allow = TRUE; + + if (!allow) + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "Connection \"%s\" is not allowed to eavesdrop due to " + "AppArmor policy", + bus_connection_is_active (connection) ? + bus_connection_get_name (connection) : "(inactive)"); + + if (!audit) + goto out; + + audit: + if (!_dbus_string_init (&auxdata)) + goto oom; + free_auxdata = TRUE; + + if (!_dbus_append_pair_str (&auxdata, "bus", bustype ? bustype : "unknown")) + goto oom; + + if (serrno && !_dbus_append_pair_str (&auxdata, "info", strerror (serrno))) + goto oom; + + if (!_dbus_append_pair_str (&auxdata, "mask", "eavesdrop")) + goto oom; + + if (connection && dbus_connection_get_unix_process_id (connection, &pid) && + !_dbus_append_pair_uint (&auxdata, "pid", pid)) + goto oom; + + if (con->label && !_dbus_append_pair_str (&auxdata, "label", con->label)) + goto oom; + + log_message (allow, "eavesdrop", &auxdata); + + out: + if (con != NULL) + bus_apparmor_confinement_unref (con); + if (free_auxdata) + _dbus_string_free (&auxdata); + + return allow; + + oom: + if (error != NULL && !dbus_error_is_set (error)) + BUS_SET_OOM (error); + allow = FALSE; + goto out; + +#else + return TRUE; +#endif /* HAVE_APPARMOR */ +} diff --git a/bus/apparmor.h b/bus/apparmor.h new file mode 100644 index 00000000..4a47aecc --- /dev/null +++ b/bus/apparmor.h @@ -0,0 +1,66 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * apparmor.c AppArmor security checks for D-Bus + * + * Authors: John Johansen <john.johansen@canonical.com> + * Tyler Hicks <tyhicks@canonical.com> + * Based on: selinux.h by Matthew Rickard + * + * Licensed under the Academic Free License version 2.1 + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef BUS_APPARMOR_H +#define BUS_APPARMOR_H + +#include <dbus/dbus.h> +#include "bus.h" + +void bus_apparmor_audit_init (void); +dbus_bool_t bus_apparmor_pre_init (void); +dbus_bool_t bus_apparmor_set_mode_from_config (const char *mode, + DBusError *error); +dbus_bool_t bus_apparmor_full_init (DBusError *error); +void bus_apparmor_shutdown (void); +dbus_bool_t bus_apparmor_enabled (void); + +void bus_apparmor_confinement_unref (BusAppArmorConfinement *confinement); +void bus_apparmor_confinement_ref (BusAppArmorConfinement *confinement); +BusAppArmorConfinement* bus_apparmor_init_connection_confinement (DBusConnection *connection, + DBusError *error); + +dbus_bool_t bus_apparmor_allows_acquire_service (DBusConnection *connection, + const char *bustype, + const char *service_name, + DBusError *error); +dbus_bool_t bus_apparmor_allows_send (DBusConnection *sender, + DBusConnection *proposed_recipient, + dbus_bool_t requested_reply, + const char *bustype, + int msgtype, + const char *path, + const char *interface, + const char *member, + const char *error_name, + const char *destination, + const char *source, + DBusError *error); + +dbus_bool_t bus_apparmor_allows_eavesdropping (DBusConnection *connection, + const char *bustype, + DBusError *error); + +#endif /* BUS_APPARMOR_H */ @@ -34,6 +34,7 @@ #include "config-parser.h" #include "signals.h" #include "selinux.h" +#include "apparmor.h" #include "dir-watch.h" #include <dbus/dbus-list.h> #include <dbus/dbus-hash.h> @@ -933,6 +934,20 @@ bus_context_new (const DBusString *config_file, bus_context_log (context, DBUS_SYSTEM_LOG_FATAL, "SELinux enabled but D-Bus initialization failed; check system log\n"); } + if (!bus_apparmor_full_init (error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + + if (bus_apparmor_enabled ()) + { + /* Only print AppArmor mediation message when syslog support is enabled */ + if (context->syslog) + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, + "AppArmor D-Bus mediation is enabled\n"); + } + if (!process_config_postinit (context, parser, error)) { _DBUS_ASSERT_ERROR_IS_SET (error); @@ -960,6 +975,9 @@ bus_context_new (const DBusString *config_file, /* FIXME - why not just put this in full_init() below? */ bus_selinux_audit_init (); #endif +#ifdef HAVE_APPARMOR + bus_apparmor_audit_init (); +#endif } dbus_server_free_data_slot (&server_data_slot); @@ -1503,7 +1521,7 @@ bus_context_check_security_policy (BusContext *context, DBusMessage *message, DBusError *error) { - const char *dest; + const char *src, *dest; BusClientPolicy *sender_policy; BusClientPolicy *recipient_policy; dbus_int32_t toggles; @@ -1512,6 +1530,7 @@ bus_context_check_security_policy (BusContext *context, dbus_bool_t requested_reply; type = dbus_message_get_type (message); + src = dbus_message_get_sender (message); dest = dbus_message_get_destination (message); /* dispatch.c was supposed to ensure these invariants */ @@ -1544,30 +1563,6 @@ bus_context_check_security_policy (BusContext *context, if (sender != NULL) { - /* First verify the SELinux access controls. If allowed then - * go on with the standard checks. - */ - if (!bus_selinux_allows_send (sender, proposed_recipient, - dbus_message_type_to_string (dbus_message_get_type (message)), - dbus_message_get_interface (message), - dbus_message_get_member (message), - dbus_message_get_error_name (message), - dest ? dest : DBUS_SERVICE_DBUS, error)) - { - if (error != NULL && !dbus_error_is_set (error)) - { - /* don't syslog this, just set the error: avc_has_perm should - * have already written to either the audit log or syslog */ - complain_about_message (context, DBUS_ERROR_ACCESS_DENIED, - "An SELinux policy prevents this sender from sending this " - "message to this recipient", - 0, message, sender, proposed_recipient, FALSE, FALSE, error); - _dbus_verbose ("SELinux security check denying send to service\n"); - } - - return FALSE; - } - if (bus_connection_is_active (sender)) { sender_policy = bus_connection_get_policy (sender); @@ -1598,6 +1593,51 @@ bus_context_check_security_policy (BusContext *context, } else { + sender_policy = NULL; + } + + /* First verify the SELinux access controls. If allowed then + * go on with the standard checks. + */ + if (!bus_selinux_allows_send (sender, proposed_recipient, + dbus_message_type_to_string (dbus_message_get_type (message)), + dbus_message_get_interface (message), + dbus_message_get_member (message), + dbus_message_get_error_name (message), + dest ? dest : DBUS_SERVICE_DBUS, error)) + { + if (error != NULL && !dbus_error_is_set (error)) + { + /* don't syslog this, just set the error: avc_has_perm should + * have already written to either the audit log or syslog */ + complain_about_message (context, DBUS_ERROR_ACCESS_DENIED, + "An SELinux policy prevents this sender from sending this " + "message to this recipient", + 0, message, sender, proposed_recipient, FALSE, FALSE, error); + _dbus_verbose ("SELinux security check denying send to service\n"); + } + + return FALSE; + } + + /* next verify AppArmor access controls. If allowed then + * go on with the standard checks. + */ + if (!bus_apparmor_allows_send (sender, proposed_recipient, + requested_reply, + bus_context_get_type (context), + dbus_message_get_type (message), + dbus_message_get_path (message), + dbus_message_get_interface (message), + dbus_message_get_member (message), + dbus_message_get_error_name (message), + dest ? dest : DBUS_SERVICE_DBUS, + src ? src : DBUS_SERVICE_DBUS, + error)) + return FALSE; + + if (!bus_connection_is_active (sender)) + { /* Policy for inactive connections is that they can only send * the hello message to the bus driver */ @@ -38,6 +38,7 @@ typedef struct BusClientPolicy BusClientPolicy; typedef struct BusPolicyRule BusPolicyRule; typedef struct BusRegistry BusRegistry; typedef struct BusSELinuxID BusSELinuxID; +typedef struct BusAppArmorConfinement BusAppArmorConfinement; typedef struct BusService BusService; typedef struct BusOwner BusOwner; typedef struct BusTransaction BusTransaction; diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c index c522ff49..5db6b289 100644 --- a/bus/config-parser-common.c +++ b/bus/config-parser-common.c @@ -127,6 +127,10 @@ bus_config_parser_element_name_to_type (const char *name) { return ELEMENT_ALLOW_ANONYMOUS; } + else if (strcmp (name, "apparmor") == 0) + { + return ELEMENT_APPARMOR; + } return ELEMENT_NONE; } @@ -181,6 +185,8 @@ bus_config_parser_element_type_to_name (ElementType type) return "keep_umask"; case ELEMENT_ALLOW_ANONYMOUS: return "allow_anonymous"; + case ELEMENT_APPARMOR: + return "apparmor"; } _dbus_assert_not_reached ("bad element type"); diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h index 186bf4cf..382a0141 100644 --- a/bus/config-parser-common.h +++ b/bus/config-parser-common.h @@ -49,7 +49,8 @@ typedef enum ELEMENT_STANDARD_SYSTEM_SERVICEDIRS, ELEMENT_KEEP_UMASK, ELEMENT_SYSLOG, - ELEMENT_ALLOW_ANONYMOUS + ELEMENT_ALLOW_ANONYMOUS, + ELEMENT_APPARMOR } ElementType; ElementType bus_config_parser_element_name_to_type (const char *element_name); diff --git a/bus/config-parser.c b/bus/config-parser.c index ee2d4e7d..58048a50 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -28,6 +28,7 @@ #include "utils.h" #include "policy.h" #include "selinux.h" +#include "apparmor.h" #include <dbus/dbus-list.h> #include <dbus/dbus-internals.h> #include <dbus/dbus-misc.h> @@ -1136,6 +1137,27 @@ start_busconfig_child (BusConfigParser *parser, return TRUE; } + else if (element_type == ELEMENT_APPARMOR) + { + Element *e; + const char *mode; + + if ((e = push_element (parser, ELEMENT_APPARMOR)) == NULL) + { + BUS_SET_OOM (error); + return FALSE; + } + + if (!locate_attributes (parser, "apparmor", + attribute_names, + attribute_values, + error, + "mode", &mode, + NULL)) + return FALSE; + + return bus_apparmor_set_mode_from_config (mode, error); + } else { dbus_set_error (error, DBUS_ERROR_FAILED, @@ -2074,6 +2096,7 @@ bus_config_parser_end_element (BusConfigParser *parser, case ELEMENT_STANDARD_SESSION_SERVICEDIRS: case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS: case ELEMENT_ALLOW_ANONYMOUS: + case ELEMENT_APPARMOR: break; } @@ -2373,6 +2396,7 @@ bus_config_parser_content (BusConfigParser *parser, case ELEMENT_ALLOW_ANONYMOUS: case ELEMENT_SELINUX: case ELEMENT_ASSOCIATE: + case ELEMENT_APPARMOR: if (all_whitespace (content)) return TRUE; else diff --git a/bus/connection.c b/bus/connection.c index 64da1292..690f3a5d 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -30,6 +30,7 @@ #include "signals.h" #include "expirelist.h" #include "selinux.h" +#include "apparmor.h" #include <dbus/dbus-list.h> #include <dbus/dbus-hash.h> #include <dbus/dbus-timeout.h> @@ -99,6 +100,7 @@ typedef struct char *cached_loginfo_string; BusSELinuxID *selinux_id; + BusAppArmorConfinement *apparmor_confinement; long connection_tv_sec; /**< Time when we connected (seconds component) */ long connection_tv_usec; /**< Time when we connected (microsec component) */ @@ -439,6 +441,9 @@ free_connection_data (void *data) if (d->selinux_id) bus_selinux_id_unref (d->selinux_id); + + if (d->apparmor_confinement) + bus_apparmor_confinement_unref (d->apparmor_confinement); dbus_free (d->cached_loginfo_string); @@ -714,6 +719,19 @@ bus_connections_setup_connection (BusConnections *connections, goto out; } + d->apparmor_confinement = bus_apparmor_init_connection_confinement (connection, + &error); + if (dbus_error_is_set (&error)) + { + /* This is a bit bogus because we pretend all errors + * are OOM; this is done because we know that in bus.c + * an OOM error disconnects the connection, which is + * the same thing we want on any other error. + */ + dbus_error_free (&error); + goto out; + } + if (!dbus_connection_set_watch_functions (connection, add_connection_watch, remove_connection_watch, @@ -801,6 +819,10 @@ bus_connections_setup_connection (BusConnections *connections, if (d->selinux_id) bus_selinux_id_unref (d->selinux_id); d->selinux_id = NULL; + + if (d->apparmor_confinement) + bus_apparmor_confinement_unref (d->apparmor_confinement); + d->apparmor_confinement = NULL; if (!dbus_connection_set_watch_functions (connection, NULL, NULL, NULL, @@ -1201,6 +1223,19 @@ bus_connection_get_selinux_id (DBusConnection *connection) return d->selinux_id; } +BusAppArmorConfinement* +bus_connection_dup_apparmor_confinement (DBusConnection *connection) +{ + BusConnectionData *d; + + d = BUS_CONNECTION_DATA (connection); + + _dbus_assert (d != NULL); + + bus_apparmor_confinement_ref (d->apparmor_confinement); + return d->apparmor_confinement; +} + /** * Checks whether the connection is registered with the message bus. * diff --git a/bus/connection.h b/bus/connection.h index dca22633..8c68d0a0 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -54,6 +54,7 @@ BusActivation* bus_connection_get_activation (DBusConnection BusMatchmaker* bus_connection_get_matchmaker (DBusConnection *connection); const char * bus_connection_get_loginfo (DBusConnection *connection); BusSELinuxID* bus_connection_get_selinux_id (DBusConnection *connection); +BusAppArmorConfinement* bus_connection_dup_apparmor_confinement (DBusConnection *connection); dbus_bool_t bus_connections_check_limits (BusConnections *connections, DBusConnection *requesting_completion, DBusError *error); diff --git a/bus/driver.c b/bus/driver.c index ceebb6f2..aab922ae 100644 --- a/bus/driver.c +++ b/bus/driver.c @@ -24,6 +24,7 @@ #include <config.h> #include "activation.h" +#include "apparmor.h" #include "connection.h" #include "driver.h" #include "dispatch.h" @@ -34,10 +35,12 @@ #include "utils.h" #include <dbus/dbus-asv-util.h> +#include <dbus/dbus-connection-internal.h> #include <dbus/dbus-string.h> #include <dbus/dbus-internals.h> #include <dbus/dbus-message.h> #include <dbus/dbus-marshal-recursive.h> +#include <dbus/dbus-marshal-validate.h> #include <string.h> static DBusConnection * @@ -1108,9 +1111,10 @@ bus_driver_handle_add_match (DBusConnection *connection, DBusError *error) { BusMatchRule *rule; - const char *text; + const char *text, *bustype; DBusString str; BusMatchmaker *matchmaker; + BusContext *context; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -1143,6 +1147,12 @@ bus_driver_handle_add_match (DBusConnection *connection, if (rule == NULL) goto failed; + context = bus_transaction_get_context (transaction); + bustype = context ? bus_context_get_type (context) : NULL; + if (bus_match_rule_get_client_is_eavesdropping (rule) && + !bus_apparmor_allows_eavesdropping (connection, bustype, error)) + goto failed; + matchmaker = bus_connection_get_matchmaker (connection); if (!bus_matchmaker_add_rule (matchmaker, rule)) @@ -1646,6 +1656,7 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection, DBusMessageIter reply_iter; DBusMessageIter array_iter; unsigned long ulong_val; + char *s; const char *service; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -1680,6 +1691,45 @@ bus_driver_handle_get_connection_credentials (DBusConnection *connection, goto oom; } + if (dbus_connection_get_windows_user (conn, &s)) + { + DBusString str; + dbus_bool_t result; + + if (s == NULL) + goto oom; + + _dbus_string_init_const (&str, s); + result = _dbus_validate_utf8 (&str, 0, _dbus_string_get_length (&str)); + _dbus_string_free (&str); + if (result) + { + if (!_dbus_asv_add_string (&array_iter, "WindowsSID", s)) + { + dbus_free (s); + goto oom; + } + } + dbus_free (s); + } + + if (_dbus_connection_get_linux_security_label (conn, &s)) + { + if (s == NULL) + goto oom; + + /* use the GVariant bytestring convention for strings of unknown + * encoding: include the \0 in the payload, for zero-copy reading */ + if (!_dbus_asv_add_byte_array (&array_iter, "LinuxSecurityLabel", + s, strlen (s) + 1)) + { + dbus_free (s); + goto oom; + } + + dbus_free (s); + } + if (!_dbus_asv_close (&reply_iter, &array_iter)) goto oom; @@ -1746,6 +1796,72 @@ bus_driver_handle_reload_config (DBusConnection *connection, return FALSE; } +#ifdef DBUS_ENABLE_VERBOSE_MODE +static dbus_bool_t +bus_driver_handle_enable_verbose (DBusConnection *connection, + BusTransaction *transaction, + DBusMessage *message, + DBusError *error) +{ + DBusMessage *reply = NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + goto oom; + + if (! bus_transaction_send_from_driver (transaction, connection, reply)) + goto oom; + + _dbus_set_verbose(TRUE); + + dbus_message_unref (reply); + return TRUE; + + oom: + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + BUS_SET_OOM (error); + + if (reply) + dbus_message_unref (reply); + return FALSE; +} + +static dbus_bool_t +bus_driver_handle_disable_verbose (DBusConnection *connection, + BusTransaction *transaction, + DBusMessage *message, + DBusError *error) +{ + DBusMessage *reply = NULL; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + reply = dbus_message_new_method_return (message); + if (reply == NULL) + goto oom; + + if (! bus_transaction_send_from_driver (transaction, connection, reply)) + goto oom; + + _dbus_set_verbose(FALSE); + + dbus_message_unref (reply); + return TRUE; + + oom: + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + BUS_SET_OOM (error); + + if (reply) + dbus_message_unref (reply); + return FALSE; +} +#endif + static dbus_bool_t bus_driver_handle_get_id (DBusConnection *connection, BusTransaction *transaction, @@ -1808,6 +1924,8 @@ bus_driver_handle_become_monitor (DBusConnection *connection, DBusError *error) { char **match_rules = NULL; + const char *bustype; + BusContext *context; BusMatchRule *rule; DBusList *rules = NULL; DBusList *iter; @@ -1822,6 +1940,11 @@ bus_driver_handle_become_monitor (DBusConnection *connection, if (!bus_driver_check_message_is_for_us (message, error)) goto out; + context = bus_transaction_get_context (transaction); + bustype = context ? bus_context_get_type (context) : NULL; + if (!bus_apparmor_allows_eavesdropping (connection, bustype, error)) + goto out; + if (!bus_driver_check_caller_is_privileged (connection, transaction, message, error)) goto out; @@ -2018,6 +2141,14 @@ static const MessageHandler monitoring_message_handlers[] = { { NULL, NULL, NULL, NULL } }; +#ifdef DBUS_ENABLE_VERBOSE_MODE +static const MessageHandler verbose_message_handlers[] = { + { "EnableVerbose", "", "", bus_driver_handle_enable_verbose}, + { "DisableVerbose", "", "", bus_driver_handle_disable_verbose}, + { NULL, NULL, NULL, NULL } +}; +#endif + #ifdef DBUS_ENABLE_STATS static const MessageHandler stats_message_handlers[] = { { "GetStats", "", "a{sv}", bus_stats_handle_get_stats }, @@ -2050,6 +2181,9 @@ static InterfaceHandler interface_handlers[] = { " </signal>\n" }, { DBUS_INTERFACE_INTROSPECTABLE, introspectable_message_handlers, NULL }, { DBUS_INTERFACE_MONITORING, monitoring_message_handlers, NULL }, +#ifdef DBUS_ENABLE_VERBOSE_MODE + { DBUS_INTERFACE_VERBOSE, verbose_message_handlers, NULL }, +#endif #ifdef DBUS_ENABLE_STATS { BUS_INTERFACE_STATS, stats_message_handlers, NULL }, #endif @@ -39,6 +39,7 @@ #include <unistd.h> /* for write() and STDERR_FILENO */ #endif #include "selinux.h" +#include "apparmor.h" static BusContext *context; @@ -614,6 +615,12 @@ main (int argc, char **argv) exit (1); } + if (!bus_apparmor_pre_init ()) + { + _dbus_warn ("AppArmor pre-initialization failed: out of memory\n"); + exit (1); + } + dbus_error_init (&error); context = bus_context_new (&config_file, flags, &print_addr_pipe, &print_pid_pipe, @@ -649,6 +656,7 @@ main (int argc, char **argv) bus_context_shutdown (context); bus_context_unref (context); bus_selinux_shutdown (); + bus_apparmor_shutdown (); return 0; } diff --git a/bus/services.c b/bus/services.c index 584485b1..8e625867 100644 --- a/bus/services.c +++ b/bus/services.c @@ -36,6 +36,7 @@ #include "policy.h" #include "bus.h" #include "selinux.h" +#include "apparmor.h" struct BusService { @@ -458,6 +459,12 @@ bus_registry_acquire_service (BusRegistry *registry, _dbus_string_get_const_data (service_name)); goto out; } + + if (!bus_apparmor_allows_acquire_service (connection, + (registry->context ? + bus_context_get_type (registry->context) : NULL), + _dbus_string_get_const_data (service_name), error)) + goto out; if (!bus_client_policy_check_can_own (policy, service_name)) { diff --git a/bus/session.conf.in b/bus/session.conf.in index cfe9544f..d2e3f2d0 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -25,6 +25,9 @@ <allow own="*"/> </policy> + <!-- Enable AppArmor mediation when it is available --> + <apparmor mode="enabled"/> + <!-- Config files are placed here that among other things, further restrict the above policy for specific services. --> <includedir>session.d</includedir> diff --git a/bus/signals.c b/bus/signals.c index 4390028f..260dd243 100644 --- a/bus/signals.c +++ b/bus/signals.c @@ -411,6 +411,15 @@ bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule, } dbus_bool_t +bus_match_rule_get_client_is_eavesdropping (BusMatchRule *rule) +{ + if (rule->flags & BUS_MATCH_CLIENT_IS_EAVESDROPPING) + return TRUE; + else + return FALSE; +} + +dbus_bool_t bus_match_rule_set_path (BusMatchRule *rule, const char *path, dbus_bool_t is_namespace) diff --git a/bus/signals.h b/bus/signals.h index d19fc7c5..0edfb07e 100644 --- a/bus/signals.h +++ b/bus/signals.h @@ -73,6 +73,8 @@ dbus_bool_t bus_match_rule_set_arg (BusMatchRule *rule, void bus_match_rule_set_client_is_eavesdropping (BusMatchRule *rule, dbus_bool_t is_eavesdropping); +dbus_bool_t bus_match_rule_get_client_is_eavesdropping (BusMatchRule *rule); + BusMatchRule* bus_match_rule_parse (DBusConnection *matches_go_to, const DBusString *rule_text, DBusError *error); diff --git a/bus/system.conf.in b/bus/system.conf.in index ac78c734..fc472bd7 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -97,6 +97,9 @@ send_interface="org.freedesktop.DBus.Debug.Stats"/> </policy> + <!-- Enable AppArmor mediation when it is available --> + <apparmor mode="enabled"/> + <!-- Config files are placed here that among other things, punch holes in the above policy for specific services. --> <includedir>system.d</includedir> diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b997f8b1..45b7d06d 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -63,6 +63,8 @@ if (NOT DBUS_DATADIR) SET(DBUS_DATADIR ${DATADIR}) endif() +set(DBUS_PREFIX ${DBUS_INSTALL_DIR}) + set(prefix ${DBUS_INSTALL_DIR}) set(exec_prefix ${prefix}) set(EXPANDED_LIBDIR ${DBUS_INSTALL_DIR}/lib) diff --git a/cmake/bus/CMakeLists.txt b/cmake/bus/CMakeLists.txt index a3528c7f..40f66909 100644 --- a/cmake/bus/CMakeLists.txt +++ b/cmake/bus/CMakeLists.txt @@ -37,6 +37,8 @@ endif (DBUS_BUS_ENABLE_INOTIFY) set (BUS_SOURCES ${BUS_DIR}/activation.c ${BUS_DIR}/activation.h + ${BUS_DIR}/apparmor.c + ${BUS_DIR}/apparmor.h ${BUS_DIR}/bus.c ${BUS_DIR}/bus.h ${BUS_DIR}/config-parser.c diff --git a/cmake/bus/CMakeLists.txt.orig b/cmake/bus/CMakeLists.txt.orig new file mode 100644 index 00000000..a3528c7f --- /dev/null +++ b/cmake/bus/CMakeLists.txt.orig @@ -0,0 +1,203 @@ +add_definitions(-DDBUS_COMPILATION) + +SET(EFENCE "") +SET(BUS_DIR ${CMAKE_SOURCE_DIR}/../bus) + +# config files for installation +CONFIGURE_FILE( "${BUS_DIR}/session.conf.in" "${CMAKE_CURRENT_BINARY_DIR}/session.conf" IMMEDIATE @ONLY) +FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/session.d) + +if(NOT WIN32) + CONFIGURE_FILE( "system.conf.cmake" "${CMAKE_CURRENT_BINARY_DIR}/system.conf" IMMEDIATE @ONLY) + FILE(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/system.d) +endif() + +# copy services for local daemon start to local service dir data/dbus-1/services +SET (SERVICE_FILES test/data/valid-service-files) +FILE(GLOB FILES "${CMAKE_SOURCE_DIR}/../${SERVICE_FILES}/*.service.in" ) +FOREACH(FILE ${FILES}) + GET_FILENAME_COMPONENT(FILENAME ${FILE} NAME_WE) + SET (TARGET ${CMAKE_BINARY_DIR}/data/dbus-1/services/${FILENAME}.service) + IF (CONFIG_VERBOSE) + MESSAGE("FROM: ${FILE}\nTO: ${TARGET}\n") + ENDIF (CONFIG_VERBOSE) + configure_file(${FILE} ${TARGET} ) +ENDFOREACH(FILE) + +SET (XML_SOURCES ${BUS_DIR}/config-loader-expat.c) + +if (DBUS_BUS_ENABLE_INOTIFY) + set (DIR_WATCH_SOURCE ${BUS_DIR}/dir-watch-inotify.c) +elseif (DBUS_BUS_ENABLE_KQUEUE) + set (DIR_WATCH_SOURCE ${BUS_DIR}/dir-watch-kqueue.c) +else (DBUS_BUS_ENABLE_INOTIFY) + set (DIR_WATCH_SOURCE ${BUS_DIR}/dir-watch-default.c) +endif (DBUS_BUS_ENABLE_INOTIFY) + +set (BUS_SOURCES + ${BUS_DIR}/activation.c + ${BUS_DIR}/activation.h + ${BUS_DIR}/bus.c + ${BUS_DIR}/bus.h + ${BUS_DIR}/config-parser.c + ${BUS_DIR}/config-parser.h + ${BUS_DIR}/config-parser-common.c + ${BUS_DIR}/config-parser-common.h +# ${BUS_DIR}/config-parser-trivial.c + ${BUS_DIR}/connection.c + ${BUS_DIR}/connection.h + ${BUS_DIR}/desktop-file.c + ${BUS_DIR}/desktop-file.h + ${BUS_DIR}/dir-watch.h + ${BUS_DIR}/dispatch.c + ${BUS_DIR}/dispatch.h + ${BUS_DIR}/driver.c + ${BUS_DIR}/driver.h + ${BUS_DIR}/expirelist.c + ${BUS_DIR}/expirelist.h + ${BUS_DIR}/policy.c + ${BUS_DIR}/policy.h + ${BUS_DIR}/selinux.h + ${BUS_DIR}/selinux.c + ${BUS_DIR}/services.c + ${BUS_DIR}/services.h + ${BUS_DIR}/signals.c + ${BUS_DIR}/signals.h + ${BUS_DIR}/test.c + ${BUS_DIR}/test.h + ${BUS_DIR}/utils.c + ${BUS_DIR}/utils.h + ${XML_SOURCES} + ${DIR_WATCH_SOURCE} +) +if(DBUS_ENABLE_STATS) + list(APPEND BUS_SOURCES + ${BUS_DIR}/stats.c + ${BUS_DIR}/stats.h + ) +endif(DBUS_ENABLE_STATS) + +include_directories( + ${CMAKE_BINARY_DIR} + ${CMAKE_SOURCE_DIR}/.. + ${XML_INCLUDE_DIR} +) + +add_executable(dbus-daemon ${BUS_SOURCES} ${BUS_DIR}/main.c) +target_link_libraries(dbus-daemon ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY}) +set_target_properties(dbus-daemon PROPERTIES OUTPUT_NAME ${DBUS_DAEMON_NAME}) +set_target_properties(dbus-daemon PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS}) + +install_targets(/bin dbus-daemon) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/session.conf DESTINATION etc/dbus-1) +install(DIRECTORY DESTINATION etc/dbus-1/session.d) +install(DIRECTORY DESTINATION share/dbus-1/services) + +if(NOT WIN32) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/system.conf DESTINATION etc/dbus-1) + install(DIRECTORY DESTINATION etc/dbus-1/system.d) + install(DIRECTORY DESTINATION share/dbus-1/system-services) + install(DIRECTORY DESTINATION var/run/dbus) +endif() + +if (DBUS_SERVICE) + set (dbus_service_SOURCES + ${BUS_DIR}/bus-service-win.c + # TODO: add additional files + # ${BUS_DIR}/service-main.c + # ${BUS_SOURCES} + ) + + add_executable(dbus-service ${dbus_service_SOURCES} ) + target_link_libraries(dbus-service ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY}) + set_target_properties(dbus-service PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS}) + install_targets(/bin dbus-service ) +endif (DBUS_SERVICE) + +if (DBUS_ENABLE_EMBEDDED_TESTS) + set(SOURCES ${BUS_SOURCES} ${BUS_DIR}/test-main.c) + add_test_executable(test-bus "${SOURCES}" ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY}) + set_target_properties(test-bus PROPERTIES COMPILE_FLAGS ${DBUS_INTERNAL_CLIENT_DEFINITIONS}) + if (NOT WIN32) + set(test_bus_system_SOURCES + ${XML_SOURCES} + ${BUS_DIR}/config-parser-common.c + ${BUS_DIR}/config-parser-trivial.c + ${BUS_DIR}/utils.c + ${BUS_DIR}/test-system.c + ) + add_test_executable(test-bus-system "${test_bus_system_SOURCES}" ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY} ${DBUS_BUS_LIBS}) + endif() +endif (DBUS_ENABLE_EMBEDDED_TESTS) + +if(MSVC) + project_source_group(${GROUP_CODE} bus_test_SOURCES dummy) +endif(MSVC) + +## mop up the gcov files +#clean-local: +# /bin/rm *.bb *.bbg *.da *.gcov || true + +#install-data-hook: +# $(mkinstalldirs) $(DESTDIR)/$(localstatedir)/run/dbus +# $(mkinstalldirs) $(DESTDIR)/$(configdir)/system.d +# $(mkinstalldirs) $(DESTDIR)/$(datadir)/dbus-1/services + +##install_file(${configdir}/system.d FILE + + +set(LAUNCH_HELPER_SOURCES ${XML_SOURCES} + ${BUS_DIR}/config-parser-common.c + ${BUS_DIR}/config-parser-trivial.c + ${BUS_DIR}/desktop-file.c + ${BUS_DIR}/utils.c + ${BUS_DIR}/activation-helper.c + +) + +if(NOT WIN32) +# TODO PENDING(kdab) fix build on windows (activation-helper.c) + add_executable(dbus-daemon-launch-helper ${LAUNCH_HELPER_SOURCES} ${BUS_DIR}/activation-helper-bin.c ) + target_link_libraries(dbus-daemon-launch-helper ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY} ) + + add_executable(dbus-daemon-launch-helper-test ${LAUNCH_HELPER_SOURCES} ${BUS_DIR}/activation-helper-bin.c) + set_target_properties(dbus-daemon-launch-helper-test PROPERTIES COMPILE_FLAGS "-DACTIVATION_LAUNCHER_TEST") + target_link_libraries(dbus-daemon-launch-helper-test ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY} ) + + set (SOURCES ${LAUNCH_HELPER_SOURCES} ${BUS_DIR}/test-launch-helper.c) + add_test_executable(test-bus-launch-helper "${SOURCES}" ${DBUS_INTERNAL_LIBRARIES} ${XML_LIBRARY}) + set_target_properties(test-bus-launch-helper PROPERTIES COMPILE_FLAGS "-DACTIVATION_LAUNCHER_TEST -DACTIVATION_LAUNCHER_DO_OOM") +endif(NOT WIN32) + +#### Init scripts fun +#SCRIPT_IN_FILES=messagebus.in +# rc.messagebus.in + +## Red Hat start +#if DBUS_INIT_SCRIPTS_RED_HAT + +#initddir=$(sysconfdir)/rc.d/init.d + +#initd_SCRIPTS= +# messagebus + +#endif +# ## Red Hat end + +## Slackware start +#if DBUS_INIT_SCRIPTS_SLACKWARE + +#initddir=$(sysconfdir)/rc.d/ + +#initd_SCRIPTS= +# rc.messagebus + +#endif +## Slackware end + +#MAN_IN_FILES=dbus-daemon.1.in +#man_MANS = dbus-daemon.1 + +#### Extra dist + +#EXTRA_DIST=$(CONFIG_IN_FILES) $(SCRIPT_IN_FILES) $(man_MANS) $(MAN_IN_FILES) diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index f7180528..cd4720c9 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -17,6 +17,7 @@ #cmakedefine DBUS_CONSOLE_AUTH_DIR "@DBUS_CONSOLE_AUTH_DIR@" #cmakedefine DBUS_DATADIR "@DBUS_DATADIR@" #cmakedefine DBUS_BINDIR "@DBUS_BINDIR@" +#cmakedefine DBUS_PREFIX "@DBUS_PREFIX@" #cmakedefine DBUS_SYSTEM_CONFIG_FILE "@DBUS_SYSTEM_CONFIG_FILE@" #cmakedefine DBUS_SESSION_CONFIG_FILE "@DBUS_SESSION_CONFIG_FILE@" #cmakedefine DBUS_DAEMON_NAME "@DBUS_DAEMON_NAME@" diff --git a/cmake/doc/CMakeLists.txt b/cmake/doc/CMakeLists.txt index 807af60c..2bb720bd 100644 --- a/cmake/doc/CMakeLists.txt +++ b/cmake/doc/CMakeLists.txt @@ -137,17 +137,20 @@ configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-daemon.1.xml.in ${CMAKE_BINARY_DI configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-launch.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-launch.1.xml) configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-monitor.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml) configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-send.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-send.1.xml) +configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-test-tool.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-test-tool.1.xml) configure_file(${CMAKE_SOURCE_DIR}/../doc/dbus-uuidgen.1.xml.in ${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-cleanup-sockets.1.xml html-nochunks) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-daemon.1.xml html-nochunks) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-launch.1.xml html-nochunks) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml html-nochunks) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-send.1.xml html-nochunks) +DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-test-tool.1.xml html-nochunks) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml html-nochunks) if (UNIX) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-daemon.1.xml man) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-monitor.1.xml man) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-send.1.xml man) + DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-test-tool.1.xml man) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-launch.1.xml man) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-uuidgen.1.xml man) DOCBOOK(${CMAKE_BINARY_DIR}/doc/dbus-cleanup-sockets.1.xml man) diff --git a/cmake/test/CMakeLists.txt b/cmake/test/CMakeLists.txt index 477beb40..c5e73bca 100644 --- a/cmake/test/CMakeLists.txt +++ b/cmake/test/CMakeLists.txt @@ -58,6 +58,10 @@ set (manual-tcp_SOURCES ${CMAKE_SOURCE_DIR}/../test/manual-tcp.c ) +set (manual-paths_SOURCES + ${CMAKE_SOURCE_DIR}/../test/manual-paths.c +) + add_helper_executable(manual-dir-iter ${manual-dir-iter_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) add_helper_executable(test-service ${test-service_SOURCES} dbus-testutils) add_helper_executable(test-names ${test-names_SOURCES} dbus-testutils) @@ -69,6 +73,9 @@ add_helper_executable(test-exit ${test-exit_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) add_helper_executable(test-segfault ${test-segfault_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) add_helper_executable(test-sleep-forever ${test-sleep-forever_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) add_test_executable(manual-tcp ${manual-tcp_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) +if(WIN32) + add_helper_executable(manual-paths ${manual-paths_SOURCES} ${DBUS_INTERNAL_LIBRARIES}) +endif() if(DBUS_WITH_GLIB) message(STATUS "with glib test apps") diff --git a/cmake/tools/CMakeLists.txt b/cmake/tools/CMakeLists.txt index d4eeb708..525dc057 100644 --- a/cmake/tools/CMakeLists.txt +++ b/cmake/tools/CMakeLists.txt @@ -4,6 +4,8 @@ set (dbus_send_SOURCES ../../tools/dbus-print-message.c ../../tools/dbus-print-message.h ../../tools/dbus-send.c + ../../tools/tool-common.c + ../../tools/tool-common.h ) set (dbus_monitor_SOURCES @@ -14,6 +16,15 @@ set (dbus_monitor_SOURCES ../../tools/tool-common.h ) +set (dbus_test_tool_SOURCES + ../../tools/dbus-echo.c + ../../tools/dbus-spam.c + ../../tools/tool-common.c + ../../tools/tool-common.h + ../../tools/test-tool.c + ../../tools/test-tool.h +) + if (WIN32) set (dbus_launch_SOURCES ../../tools/dbus-launch-win.c @@ -39,6 +50,10 @@ add_executable(dbus-send ${dbus_send_SOURCES}) target_link_libraries(dbus-send ${DBUS_LIBRARIES}) install_targets(/bin dbus-send ) +add_executable(dbus-test-tool ${dbus_test_tool_SOURCES}) +target_link_libraries(dbus-test-tool ${DBUS_LIBRARIES}) +install_targets(/bin dbus-test-tool ) + add_executable(dbus-launch ${dbus_launch_SOURCES}) target_link_libraries(dbus-launch ) if (DBUS_BUILD_X11) diff --git a/config.h.in b/config.h.in index 78e7a02d..94fb8f96 100644 --- a/config.h.in +++ b/config.h.in @@ -156,6 +156,9 @@ /* Define to 1 if you have the <alloca.h> header file. */ #undef HAVE_ALLOCA_H +/* AppArmor Support */ +#undef HAVE_APPARMOR + /* Define to 1 if you have backtrace(). */ #undef HAVE_BACKTRACE @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for dbus 1.9.10. +# Generated by GNU Autoconf 2.69 for dbus 1.9.12. # # Report bugs to <https://bugs.freedesktop.org/enter_bug.cgi?product=dbus>. # @@ -591,8 +591,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='dbus' PACKAGE_TARNAME='dbus' -PACKAGE_VERSION='1.9.10' -PACKAGE_STRING='dbus 1.9.10' +PACKAGE_VERSION='1.9.12' +PACKAGE_STRING='dbus 1.9.12' PACKAGE_BUGREPORT='https://bugs.freedesktop.org/enter_bug.cgi?product=dbus' PACKAGE_URL='' @@ -680,6 +680,10 @@ DBUS_CAN_UPLOAD_DOCS_TRUE DBUS_XML_DOCS_ENABLED_FALSE DBUS_XML_DOCS_ENABLED_TRUE XMLTO +DBUS_DUCKTYPE_DOCS_ENABLED_FALSE +DBUS_DUCKTYPE_DOCS_ENABLED_TRUE +YELP_BUILD +DUCKTYPE DBUS_HAVE_XSLTPROC_FALSE DBUS_HAVE_XSLTPROC_TRUE XSLTPROC @@ -715,6 +719,8 @@ HAVE_LINUX_EPOLL_FALSE HAVE_LINUX_EPOLL_TRUE DBUS_BUS_ENABLE_INOTIFY_FALSE DBUS_BUS_ENABLE_INOTIFY_TRUE +APPARMOR_LIBS +APPARMOR_CFLAGS HAVE_SELINUX_FALSE HAVE_SELINUX_TRUE THREAD_LIBS @@ -916,8 +922,10 @@ enable_asserts enable_checks enable_xml_docs enable_doxygen_docs +enable_ducktype_docs enable_abstract_sockets enable_selinux +enable_apparmor enable_libaudit enable_inotify enable_kqueue @@ -971,6 +979,8 @@ DBUS_GLIB_CFLAGS DBUS_GLIB_LIBS PYTHON THREAD_LIBS +APPARMOR_CFLAGS +APPARMOR_LIBS SYSTEMD_CFLAGS SYSTEMD_LIBS VALGRIND_CFLAGS @@ -1516,7 +1526,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures dbus 1.9.10 to adapt to many kinds of systems. +\`configure' configures dbus 1.9.12 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1590,7 +1600,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of dbus 1.9.10:";; + short | recursive ) echo "Configuration of dbus 1.9.12:";; esac cat <<\_ACEOF @@ -1624,9 +1634,11 @@ Optional Features: --enable-checks include sanity checks on public API --enable-xml-docs build XML documentation (requires xmlto) --enable-doxygen-docs build DOXYGEN documentation (requires Doxygen) + --enable-ducktype-docs build Ducktype documentation (requires Ducktype) --enable-abstract-sockets use abstract socket namespace (linux only) --enable-selinux build with SELinux support + --enable-apparmor build with AppArmor support --enable-libaudit build audit daemon support for SELinux --enable-inotify build with inotify support (linux only) --enable-kqueue build with kqueue support @@ -1713,6 +1725,10 @@ Some influential environment variables: linker flags for DBUS_GLIB, overriding pkg-config PYTHON the Python interpreter THREAD_LIBS + APPARMOR_CFLAGS + C compiler flags for APPARMOR, overriding pkg-config + APPARMOR_LIBS + linker flags for APPARMOR, overriding pkg-config SYSTEMD_CFLAGS C compiler flags for SYSTEMD, overriding pkg-config SYSTEMD_LIBS @@ -1789,7 +1805,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -dbus configure 1.9.10 +dbus configure 1.9.12 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2565,7 +2581,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by dbus $as_me 1.9.10, which was +It was created by dbus $as_me 1.9.12, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -3508,7 +3524,7 @@ fi # Define the identity of the package. PACKAGE='dbus' - VERSION='1.9.10' + VERSION='1.9.12' cat >>confdefs.h <<_ACEOF @@ -3804,16 +3820,16 @@ _ACEOF # ## increment if the interface has additions, changes, removals. -LT_CURRENT=14 +LT_CURRENT=15 ## increment any time the source changes; set to ## 0 if you increment CURRENT -LT_REVISION=3 +LT_REVISION=0 ## increment if any interfaces have been added; set to 0 ## if any interfaces have been changed or removed. removal has ## precedence over adding, so set to 0 if both happened. -LT_AGE=11 +LT_AGE=12 @@ -3821,8 +3837,8 @@ LT_AGE=11 DBUS_MAJOR_VERSION=1 DBUS_MINOR_VERSION=9 -DBUS_MICRO_VERSION=10 -DBUS_VERSION=1.9.10 +DBUS_MICRO_VERSION=12 +DBUS_VERSION=1.9.12 @@ -16811,6 +16827,13 @@ else enable_doxygen_docs=auto fi +# Check whether --enable-ducktype-docs was given. +if test "${enable_ducktype_docs+set}" = set; then : + enableval=$enable_ducktype_docs; enable_ducktype_docs=$enableval +else + enable_ducktype_docs=auto +fi + # Check whether --enable-abstract-sockets was given. if test "${enable_abstract_sockets+set}" = set; then : enableval=$enable_abstract_sockets; enable_abstract_sockets=$enableval @@ -16825,6 +16848,13 @@ else enable_selinux=auto fi +# Check whether --enable-apparmor was given. +if test "${enable_apparmor+set}" = set; then : + enableval=$enable_apparmor; enable_apparmor=$enableval +else + enable_apparmor=auto +fi + # Check whether --enable-libaudit was given. if test "${enable_libaudit+set}" = set; then : enableval=$enable_libaudit; enable_libaudit=$enableval @@ -20150,6 +20180,95 @@ else SELINUX_LIBS= fi +# AppArmor detection +if test x$enable_apparmor = xno; then : + have_apparmor=no +else + + +pkg_failed=no +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for APPARMOR" >&5 +$as_echo_n "checking for APPARMOR... " >&6; } + +if test -n "$APPARMOR_CFLAGS"; then + pkg_cv_APPARMOR_CFLAGS="$APPARMOR_CFLAGS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libapparmor >= 2.8.95\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libapparmor >= 2.8.95") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_APPARMOR_CFLAGS=`$PKG_CONFIG --cflags "libapparmor >= 2.8.95" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi +if test -n "$APPARMOR_LIBS"; then + pkg_cv_APPARMOR_LIBS="$APPARMOR_LIBS" + elif test -n "$PKG_CONFIG"; then + if test -n "$PKG_CONFIG" && \ + { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"libapparmor >= 2.8.95\""; } >&5 + ($PKG_CONFIG --exists --print-errors "libapparmor >= 2.8.95") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + pkg_cv_APPARMOR_LIBS=`$PKG_CONFIG --libs "libapparmor >= 2.8.95" 2>/dev/null` +else + pkg_failed=yes +fi + else + pkg_failed=untried +fi + + + +if test $pkg_failed = yes; then + +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi + if test $_pkg_short_errors_supported = yes; then + APPARMOR_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "libapparmor >= 2.8.95" 2>&1` + else + APPARMOR_PKG_ERRORS=`$PKG_CONFIG --print-errors "libapparmor >= 2.8.95" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$APPARMOR_PKG_ERRORS" >&5 + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + have_apparmor=no +elif test $pkg_failed = untried; then + have_apparmor=no +else + APPARMOR_CFLAGS=$pkg_cv_APPARMOR_CFLAGS + APPARMOR_LIBS=$pkg_cv_APPARMOR_LIBS + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + have_apparmor=yes +fi + + if test x$enable_apparmor = xauto && test x$have_apparmor = xno; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Sufficiently new AppArmor library not found" >&5 +$as_echo "$as_me: WARNING: Sufficiently new AppArmor library not found" >&2;} +fi + if test x$enable_apparmor != xauto && test x$have_apparmor = xno; then : + as_fn_error $? "AppArmor explicitly required, and AppArmor library not found" "$LINENO" 5 +fi + +fi + +if test x$have_apparmor = xyes; then : + +$as_echo "#define HAVE_APPARMOR 1" >>confdefs.h + +fi + # inotify checks if test x$enable_inotify = xno ; then have_inotify=no; @@ -22211,6 +22330,145 @@ else fi +### Ducktype/Yelp documentation + +# Extract the first word of "ducktype", so it can be a program name with args. +set dummy ducktype; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_DUCKTYPE+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $DUCKTYPE in + [\\/]* | ?:[\\/]*) + ac_cv_path_DUCKTYPE="$DUCKTYPE" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_DUCKTYPE="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_DUCKTYPE" && ac_cv_path_DUCKTYPE="no" + ;; +esac +fi +DUCKTYPE=$ac_cv_path_DUCKTYPE +if test -n "$DUCKTYPE"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUCKTYPE" >&5 +$as_echo "$DUCKTYPE" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +# Extract the first word of "yelp-build", so it can be a program name with args. +set dummy yelp-build; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_path_YELP_BUILD+:} false; then : + $as_echo_n "(cached) " >&6 +else + case $YELP_BUILD in + [\\/]* | ?:[\\/]*) + ac_cv_path_YELP_BUILD="$YELP_BUILD" # Let the user override the test with a path. + ;; + *) + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_path_YELP_BUILD="$as_dir/$ac_word$ac_exec_ext" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_path_YELP_BUILD" && ac_cv_path_YELP_BUILD="no" + ;; +esac +fi +YELP_BUILD=$ac_cv_path_YELP_BUILD +if test -n "$YELP_BUILD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YELP_BUILD" >&5 +$as_echo "$YELP_BUILD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build Ducktype documentation" >&5 +$as_echo_n "checking whether to build Ducktype documentation... " >&6; } + +if test "$DUCKTYPE" = "no"; then : + have_ducktype=no +else + have_ducktype=yes +fi +if test "$YELP_BUILD" = "no"; then : + have_yelp_build=no +else + have_yelp_build=yes +fi + +if test "$enable_ducktype_docs" = "auto"; then : + + if test "$have_ducktype" = "no" || test "$have_yelp_build" = "no"; then : + + enable_ducktype_docs=no + +else + + enable_ducktype_docs=yes + +fi + +fi + +if test "$enable_ducktype_docs" = "yes"; then : + + if test "$have_ducktype" = "no"; then : + + as_fn_error $? "Building Ducktype docs explicitly required, but ducktype not found" "$LINENO" 5 + +fi + if test "$have_yelp_build" = "no"; then : + + as_fn_error $? "Building Ducktype docs explicitly required, but yelp-build not found" "$LINENO" 5 + +fi + +fi + + if test "$enable_ducktype_docs" = "yes"; then + DBUS_DUCKTYPE_DOCS_ENABLED_TRUE= + DBUS_DUCKTYPE_DOCS_ENABLED_FALSE='#' +else + DBUS_DUCKTYPE_DOCS_ENABLED_TRUE='#' + DBUS_DUCKTYPE_DOCS_ENABLED_FALSE= +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_ducktype_docs" >&5 +$as_echo "$enable_ducktype_docs" >&6; } + ### XML Documentation # Extract the first word of "xmlto", so it can be a program name with args. @@ -22289,7 +22547,8 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_xml_docs" >&5 $as_echo "$enable_xml_docs" >&6; } - if test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes; then + if test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes && + test x$enable_ducktype_docs = xyes; then DBUS_CAN_UPLOAD_DOCS_TRUE= DBUS_CAN_UPLOAD_DOCS_FALSE='#' else @@ -22930,7 +23189,7 @@ $as_echo "#define DBUS_ENABLE_STATS 1" >>confdefs.h fi -ac_config_files="$ac_config_files Doxyfile dbus/versioninfo.rc dbus/dbus-arch-deps.h bus/system.conf bus/session.conf bus/example-system-enable-stats.conf bus/example-session-disable-stats.conf bus/messagebus bus/messagebus-config bus/org.freedesktop.dbus-session.plist bus/rc.messagebus bus/dbus.service bus/dbus.socket Makefile dbus/Makefile bus/Makefile tools/Makefile test/Makefile test/name-test/Makefile doc/Makefile doc/dbus-cleanup-sockets.1.xml doc/dbus-daemon.1.xml doc/dbus-launch.1.xml doc/dbus-monitor.1.xml doc/dbus-run-session.1.xml doc/dbus-send.1.xml doc/dbus-uuidgen.1.xml dbus-1.pc dbus-1-uninstalled.pc" +ac_config_files="$ac_config_files Doxyfile dbus/versioninfo.rc dbus/dbus-arch-deps.h bus/system.conf bus/session.conf bus/example-system-enable-stats.conf bus/example-session-disable-stats.conf bus/messagebus bus/messagebus-config bus/org.freedesktop.dbus-session.plist bus/rc.messagebus bus/dbus.service bus/dbus.socket Makefile dbus/Makefile bus/Makefile tools/Makefile test/Makefile test/name-test/Makefile doc/Makefile doc/dbus-cleanup-sockets.1.xml doc/dbus-daemon.1.xml doc/dbus-launch.1.xml doc/dbus-monitor.1.xml doc/dbus-run-session.1.xml doc/dbus-send.1.xml doc/dbus-test-tool.1.xml doc/dbus-uuidgen.1.xml dbus-1.pc dbus-1-uninstalled.pc" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -23146,6 +23405,10 @@ if test -z "${DBUS_HAVE_XSLTPROC_TRUE}" && test -z "${DBUS_HAVE_XSLTPROC_FALSE}" as_fn_error $? "conditional \"DBUS_HAVE_XSLTPROC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${DBUS_DUCKTYPE_DOCS_ENABLED_TRUE}" && test -z "${DBUS_DUCKTYPE_DOCS_ENABLED_FALSE}"; then + as_fn_error $? "conditional \"DBUS_DUCKTYPE_DOCS_ENABLED\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${DBUS_XML_DOCS_ENABLED_TRUE}" && test -z "${DBUS_XML_DOCS_ENABLED_FALSE}"; then as_fn_error $? "conditional \"DBUS_XML_DOCS_ENABLED\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 @@ -23567,7 +23830,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by dbus $as_me 1.9.10, which was +This file was extended by dbus $as_me 1.9.12, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -23633,7 +23896,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -dbus config.status 1.9.10 +dbus config.status 1.9.12 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -24252,6 +24515,7 @@ do "doc/dbus-monitor.1.xml") CONFIG_FILES="$CONFIG_FILES doc/dbus-monitor.1.xml" ;; "doc/dbus-run-session.1.xml") CONFIG_FILES="$CONFIG_FILES doc/dbus-run-session.1.xml" ;; "doc/dbus-send.1.xml") CONFIG_FILES="$CONFIG_FILES doc/dbus-send.1.xml" ;; + "doc/dbus-test-tool.1.xml") CONFIG_FILES="$CONFIG_FILES doc/dbus-test-tool.1.xml" ;; "doc/dbus-uuidgen.1.xml") CONFIG_FILES="$CONFIG_FILES doc/dbus-uuidgen.1.xml" ;; "dbus-1.pc") CONFIG_FILES="$CONFIG_FILES dbus-1.pc" ;; "dbus-1-uninstalled.pc") CONFIG_FILES="$CONFIG_FILES dbus-1-uninstalled.pc" ;; @@ -25953,7 +26217,9 @@ echo " 32-bit int: ${DBUS_INT32_TYPE} 16-bit int: ${DBUS_INT16_TYPE} Doxygen: ${DOXYGEN:-not found} - xmlto: ${XMLTO:-not found}" + xmlto: ${XMLTO:-not found} + ducktype: ${DUCKTYPE:-not found} + yelp-build: ${YELP_BUILD:-not found}" echo " Rebuilding generated files: ${USE_MAINTAINER_MODE} @@ -25968,11 +26234,13 @@ echo " Building checks: ${enable_checks} Building bus stats API: ${enable_stats} Building SELinux support: ${have_selinux} + Building AppArmor support: ${have_apparmor} Building inotify support: ${have_inotify} Building kqueue support: ${have_kqueue} Building systemd support: ${have_systemd} Building X11 code: ${have_x11} Building Doxygen docs: ${enable_doxygen_docs} + Building Ducktype docs: ${enable_ducktype_docs} Building XML docs: ${enable_xml_docs} Building launchd support: ${have_launchd} Init scripts style: ${with_init_scripts} diff --git a/configure.ac b/configure.ac index ee87caa0..b393a42f 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_PREREQ([2.63]) m4_define([dbus_major_version], [1]) m4_define([dbus_minor_version], [9]) -m4_define([dbus_micro_version], [10]) +m4_define([dbus_micro_version], [12]) m4_define([dbus_version], [dbus_major_version.dbus_minor_version.dbus_micro_version]) AC_INIT([dbus],[dbus_version],[https://bugs.freedesktop.org/enter_bug.cgi?product=dbus],[dbus]) @@ -33,16 +33,16 @@ AC_DEFINE_UNQUOTED(DBUS_DAEMON_NAME,"dbus-daemon",[Name of executable]) # ## increment if the interface has additions, changes, removals. -LT_CURRENT=14 +LT_CURRENT=15 ## increment any time the source changes; set to ## 0 if you increment CURRENT -LT_REVISION=3 +LT_REVISION=0 ## increment if any interfaces have been added; set to 0 ## if any interfaces have been changed or removed. removal has ## precedence over adding, so set to 0 if both happened. -LT_AGE=11 +LT_AGE=12 AC_SUBST(LT_CURRENT) AC_SUBST(LT_REVISION) @@ -150,8 +150,16 @@ AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion chec AC_ARG_ENABLE(checks, AS_HELP_STRING([--enable-checks],[include sanity checks on public API]),enable_checks=$enableval,enable_checks=yes) AC_ARG_ENABLE(xml-docs, AS_HELP_STRING([--enable-xml-docs],[build XML documentation (requires xmlto)]),enable_xml_docs=$enableval,enable_xml_docs=auto) AC_ARG_ENABLE(doxygen-docs, AS_HELP_STRING([--enable-doxygen-docs],[build DOXYGEN documentation (requires Doxygen)]),enable_doxygen_docs=$enableval,enable_doxygen_docs=auto) +AC_ARG_ENABLE([ducktype-docs], + AS_HELP_STRING([--enable-ducktype-docs], + [build Ducktype documentation (requires Ducktype)]), + [enable_ducktype_docs=$enableval], [enable_ducktype_docs=auto]) AC_ARG_ENABLE(abstract-sockets, AS_HELP_STRING([--enable-abstract-sockets],[use abstract socket namespace (linux only)]),enable_abstract_sockets=$enableval,enable_abstract_sockets=auto) AC_ARG_ENABLE(selinux, AS_HELP_STRING([--enable-selinux],[build with SELinux support]),enable_selinux=$enableval,enable_selinux=auto) +AC_ARG_ENABLE([apparmor], + [AS_HELP_STRING([--enable-apparmor], [build with AppArmor support])], + [enable_apparmor=$enableval], + [enable_apparmor=auto]) AC_ARG_ENABLE(libaudit,AS_HELP_STRING([--enable-libaudit],[build audit daemon support for SELinux]),enable_libaudit=$enableval,enable_libaudit=auto) AC_ARG_ENABLE(inotify, AS_HELP_STRING([--enable-inotify],[build with inotify support (linux only)]),enable_inotify=$enableval,enable_inotify=auto) AC_ARG_ENABLE(kqueue, AS_HELP_STRING([--enable-kqueue],[build with kqueue support]),enable_kqueue=$enableval,enable_kqueue=auto) @@ -1041,6 +1049,22 @@ else SELINUX_LIBS= fi +# AppArmor detection +AS_IF([test x$enable_apparmor = xno], + [have_apparmor=no], + [ + PKG_CHECK_MODULES([APPARMOR], [libapparmor >= 2.8.95], + [have_apparmor=yes], [have_apparmor=no]) + + AS_IF([test x$enable_apparmor = xauto && test x$have_apparmor = xno], + [AC_MSG_WARN([Sufficiently new AppArmor library not found])]) + AS_IF([test x$enable_apparmor != xauto && test x$have_apparmor = xno], + [AC_MSG_ERROR([AppArmor explicitly required, and AppArmor library not found])]) + ]) + +AS_IF([test x$have_apparmor = xyes], + [AC_DEFINE([HAVE_APPARMOR], [1], [AppArmor Support])]) + # inotify checks if test x$enable_inotify = xno ; then have_inotify=no; @@ -1421,6 +1445,36 @@ AC_MSG_RESULT($enable_doxygen_docs) AC_CHECK_PROGS([XSLTPROC], [xsltproc]) AM_CONDITIONAL([DBUS_HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"]) +### Ducktype/Yelp documentation + +AC_PATH_PROG([DUCKTYPE],[ducktype],[no]) +AC_PATH_PROG([YELP_BUILD],[yelp-build],[no]) + +AC_MSG_CHECKING([whether to build Ducktype documentation]) + +AS_IF([test "$DUCKTYPE" = "no"],[have_ducktype=no],[have_ducktype=yes]) +AS_IF([test "$YELP_BUILD" = "no"],[have_yelp_build=no],[have_yelp_build=yes]) + +AS_IF([test "$enable_ducktype_docs" = "auto"],[ + AS_IF([test "$have_ducktype" = "no" || test "$have_yelp_build" = "no"],[ + enable_ducktype_docs=no + ],[ + enable_ducktype_docs=yes + ]) +]) + +AS_IF([test "$enable_ducktype_docs" = "yes"],[ + AS_IF([test "$have_ducktype" = "no"],[ + AC_MSG_ERROR([Building Ducktype docs explicitly required, but ducktype not found]) + ]) + AS_IF([test "$have_yelp_build" = "no"],[ + AC_MSG_ERROR([Building Ducktype docs explicitly required, but yelp-build not found]) + ]) +]) + +AM_CONDITIONAL([DBUS_DUCKTYPE_DOCS_ENABLED],[test "$enable_ducktype_docs" = "yes"]) +AC_MSG_RESULT([$enable_ducktype_docs]) + ### XML Documentation AC_PATH_PROG(XMLTO, xmlto, no) @@ -1451,7 +1505,8 @@ AM_CONDITIONAL(DBUS_XML_DOCS_ENABLED, test x$enable_xml_docs = xyes) AC_MSG_RESULT($enable_xml_docs) AM_CONDITIONAL(DBUS_CAN_UPLOAD_DOCS, - [test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes]) + [test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes && + test x$enable_ducktype_docs = xyes]) #### Have to go $localstatedir->$prefix/var->/usr/local/var @@ -1789,6 +1844,7 @@ doc/dbus-launch.1.xml doc/dbus-monitor.1.xml doc/dbus-run-session.1.xml doc/dbus-send.1.xml +doc/dbus-test-tool.1.xml doc/dbus-uuidgen.1.xml dbus-1.pc dbus-1-uninstalled.pc @@ -1817,7 +1873,9 @@ echo " 32-bit int: ${DBUS_INT32_TYPE} 16-bit int: ${DBUS_INT16_TYPE} Doxygen: ${DOXYGEN:-not found} - xmlto: ${XMLTO:-not found}" + xmlto: ${XMLTO:-not found} + ducktype: ${DUCKTYPE:-not found} + yelp-build: ${YELP_BUILD:-not found}" echo " Rebuilding generated files: ${USE_MAINTAINER_MODE} @@ -1832,11 +1890,13 @@ echo " Building checks: ${enable_checks} Building bus stats API: ${enable_stats} Building SELinux support: ${have_selinux} + Building AppArmor support: ${have_apparmor} Building inotify support: ${have_inotify} Building kqueue support: ${have_kqueue} Building systemd support: ${have_systemd} Building X11 code: ${have_x11} Building Doxygen docs: ${enable_doxygen_docs} + Building Ducktype docs: ${enable_ducktype_docs} Building XML docs: ${enable_xml_docs} Building launchd support: ${have_launchd} Init scripts style: ${with_init_scripts} diff --git a/dbus/Makefile.in b/dbus/Makefile.in index f542c6ab..08136755 100644 --- a/dbus/Makefile.in +++ b/dbus/Makefile.in @@ -462,6 +462,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -516,6 +518,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -605,6 +608,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ diff --git a/dbus/dbus-asv-util.c b/dbus/dbus-asv-util.c index 583e41fa..d3ac5e9c 100644 --- a/dbus/dbus-asv-util.c +++ b/dbus/dbus-asv-util.c @@ -258,3 +258,57 @@ _dbus_asv_add_string (DBusMessageIter *arr_iter, return TRUE; } + +/** + * Create a new entry in an a{sv} (map from string to variant) + * with a byte array value. + * + * If this function fails, the a{sv} must be abandoned, for instance + * with _dbus_asv_abandon(). + * + * @param arr_iter the iterator which is appending to the array + * @param key a UTF-8 key for the map + * @param value the value + * @param n_elements the number of elements to append + * @returns #TRUE on success, or #FALSE if not enough memory + */ +dbus_bool_t +_dbus_asv_add_byte_array (DBusMessageIter *arr_iter, + const char *key, + const void *value, + int n_elements) +{ + DBusMessageIter entry_iter; + DBusMessageIter var_iter; + DBusMessageIter byte_array_iter; + + if (!_dbus_asv_open_entry (arr_iter, &entry_iter, key, "ay", &var_iter)) + return FALSE; + + if (!dbus_message_iter_open_container (&var_iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, + &byte_array_iter)) + { + _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter); + return FALSE; + } + + if (!dbus_message_iter_append_fixed_array (&byte_array_iter, DBUS_TYPE_BYTE, + &value, n_elements)) + { + dbus_message_iter_abandon_container (&var_iter, &byte_array_iter); + _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter); + return FALSE; + } + + if (!dbus_message_iter_close_container (&var_iter, &byte_array_iter)) + { + _dbus_asv_abandon_entry (arr_iter, &entry_iter, &var_iter); + return FALSE; + } + + if (!_dbus_asv_close_entry (arr_iter, &entry_iter, &var_iter)) + return FALSE; + + return TRUE; +} diff --git a/dbus/dbus-asv-util.h b/dbus/dbus-asv-util.h index 0337260a..277ab807 100644 --- a/dbus/dbus-asv-util.h +++ b/dbus/dbus-asv-util.h @@ -42,5 +42,9 @@ dbus_bool_t _dbus_asv_add_uint32 (DBusMessageIter *arr_iter, dbus_bool_t _dbus_asv_add_string (DBusMessageIter *arr_iter, const char *key, const char *value); +dbus_bool_t _dbus_asv_add_byte_array (DBusMessageIter *arr_iter, + const char *key, + const void *value, + int n_elements); #endif diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index 64ad2b06..1503d5f1 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -1102,20 +1102,23 @@ handle_server_data_external_mech (DBusAuth *auth, auth->desired_identity)) return FALSE; - /* also copy process ID from the socket credentials + /* also copy misc process info from the socket credentials */ if (!_dbus_credentials_add_credential (auth->authorized_identity, DBUS_CREDENTIAL_UNIX_PROCESS_ID, auth->credentials)) return FALSE; - /* also copy audit data from the socket credentials - */ if (!_dbus_credentials_add_credential (auth->authorized_identity, DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, auth->credentials)) return FALSE; - + + if (!_dbus_credentials_add_credential (auth->authorized_identity, + DBUS_CREDENTIAL_LINUX_SECURITY_LABEL, + auth->credentials)) + return FALSE; + if (!send_ok (auth)) return FALSE; diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h index 28974040..64ef3365 100644 --- a/dbus/dbus-connection-internal.h +++ b/dbus/dbus-connection-internal.h @@ -107,6 +107,9 @@ void _dbus_connection_set_pending_fds_function (DBusConnectio DBusPendingFdsChangeFunction callback, void *data); +dbus_bool_t _dbus_connection_get_linux_security_label (DBusConnection *connection, + char **label_p); + /* if DBUS_ENABLE_STATS */ void _dbus_connection_get_stats (DBusConnection *connection, dbus_uint32_t *in_messages, diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c index b574207d..8952b75f 100644 --- a/dbus/dbus-connection.c +++ b/dbus/dbus-connection.c @@ -5322,6 +5322,32 @@ dbus_connection_set_unix_user_function (DBusConnection *connection, (* old_free_function) (old_data); } +/* Same calling convention as dbus_connection_get_windows_user */ +dbus_bool_t +_dbus_connection_get_linux_security_label (DBusConnection *connection, + char **label_p) +{ + dbus_bool_t result; + + _dbus_assert (connection != NULL); + _dbus_assert (label_p != NULL); + + CONNECTION_LOCK (connection); + + if (!_dbus_transport_try_to_authenticate (connection->transport)) + result = FALSE; + else + result = _dbus_transport_get_linux_security_label (connection->transport, + label_p); +#ifndef __linux__ + _dbus_assert (!result); +#endif + + CONNECTION_UNLOCK (connection); + + return result; +} + /** * Gets the Windows user SID of the connection if known. Returns * #TRUE if the ID is filled in. Always returns #FALSE on non-Windows diff --git a/dbus/dbus-credentials.c b/dbus/dbus-credentials.c index 7325125c..151bb00f 100644 --- a/dbus/dbus-credentials.c +++ b/dbus/dbus-credentials.c @@ -50,6 +50,7 @@ struct DBusCredentials { dbus_uid_t unix_uid; dbus_pid_t pid; char *windows_sid; + char *linux_security_label; void *adt_audit_data; dbus_int32_t adt_audit_data_size; }; @@ -79,6 +80,7 @@ _dbus_credentials_new (void) creds->unix_uid = DBUS_UID_UNSET; creds->pid = DBUS_PID_UNSET; creds->windows_sid = NULL; + creds->linux_security_label = NULL; creds->adt_audit_data = NULL; creds->adt_audit_data_size = 0; @@ -133,6 +135,7 @@ _dbus_credentials_unref (DBusCredentials *credentials) if (credentials->refcount == 0) { dbus_free (credentials->windows_sid); + dbus_free (credentials->linux_security_label); dbus_free (credentials->adt_audit_data); dbus_free (credentials); } @@ -193,6 +196,30 @@ _dbus_credentials_add_windows_sid (DBusCredentials *credentials, } /** + * Add a Linux security label, as used by LSMs such as SELinux, Smack and + * AppArmor, to the credentials. + * + * @param credentials the object + * @param label the label + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_credentials_add_linux_security_label (DBusCredentials *credentials, + const char *label) +{ + char *copy; + + copy = _dbus_strdup (label); + if (copy == NULL) + return FALSE; + + dbus_free (credentials->linux_security_label); + credentials->linux_security_label = copy; + + return TRUE; +} + +/** * Add ADT audit data to the credentials. * * @param credentials the object @@ -236,6 +263,8 @@ _dbus_credentials_include (DBusCredentials *credentials, return credentials->unix_uid != DBUS_UID_UNSET; case DBUS_CREDENTIAL_WINDOWS_SID: return credentials->windows_sid != NULL; + case DBUS_CREDENTIAL_LINUX_SECURITY_LABEL: + return credentials->linux_security_label != NULL; case DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID: return credentials->adt_audit_data != NULL; } @@ -284,6 +313,19 @@ _dbus_credentials_get_windows_sid (DBusCredentials *credentials) } /** + * Gets the Linux security label (as used by LSMs) from the credentials, + * or #NULL if the credentials object doesn't contain a security label. + * + * @param credentials the object + * @returns the security label + */ +const char * +_dbus_credentials_get_linux_security_label (DBusCredentials *credentials) +{ + return credentials->linux_security_label; +} + +/** * Gets the ADT audit data in the credentials, or #NULL if * the credentials object doesn't contain ADT audit data. * @@ -329,6 +371,10 @@ _dbus_credentials_are_superset (DBusCredentials *credentials, (possible_subset->windows_sid == NULL || (credentials->windows_sid && strcmp (possible_subset->windows_sid, credentials->windows_sid) == 0)) && + (possible_subset->linux_security_label == NULL || + (credentials->linux_security_label != NULL && + strcmp (possible_subset->linux_security_label, + credentials->linux_security_label) == 0)) && (possible_subset->adt_audit_data == NULL || (credentials->adt_audit_data && memcmp (possible_subset->adt_audit_data, credentials->adt_audit_data, @@ -348,6 +394,7 @@ _dbus_credentials_are_empty (DBusCredentials *credentials) credentials->pid == DBUS_PID_UNSET && credentials->unix_uid == DBUS_UID_UNSET && credentials->windows_sid == NULL && + credentials->linux_security_label == NULL && credentials->adt_audit_data == NULL; } @@ -388,6 +435,9 @@ _dbus_credentials_add_credentials (DBusCredentials *credentials, DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, other_credentials) && _dbus_credentials_add_credential (credentials, + DBUS_CREDENTIAL_LINUX_SECURITY_LABEL, + other_credentials) && + _dbus_credentials_add_credential (credentials, DBUS_CREDENTIAL_WINDOWS_SID, other_credentials); } @@ -427,6 +477,13 @@ _dbus_credentials_add_credential (DBusCredentials *credentials, if (!_dbus_credentials_add_windows_sid (credentials, other_credentials->windows_sid)) return FALSE; } + else if (which == DBUS_CREDENTIAL_LINUX_SECURITY_LABEL && + other_credentials->linux_security_label != NULL) + { + if (!_dbus_credentials_add_linux_security_label (credentials, + other_credentials->linux_security_label)) + return FALSE; + } else if (which == DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID && other_credentials->adt_audit_data != NULL) { @@ -449,6 +506,8 @@ _dbus_credentials_clear (DBusCredentials *credentials) credentials->unix_uid = DBUS_UID_UNSET; dbus_free (credentials->windows_sid); credentials->windows_sid = NULL; + dbus_free (credentials->linux_security_label); + credentials->linux_security_label = NULL; dbus_free (credentials->adt_audit_data); credentials->adt_audit_data = NULL; credentials->adt_audit_data_size = 0; @@ -540,6 +599,15 @@ _dbus_credentials_to_string_append (DBusCredentials *credentials, else join = FALSE; + if (credentials->linux_security_label != NULL) + { + if (!_dbus_string_append_printf (string, "%slsm='%s'", + join ? " " : "", + credentials->linux_security_label)) + goto oom; + join = TRUE; + } + return TRUE; oom: return FALSE; diff --git a/dbus/dbus-credentials.h b/dbus/dbus-credentials.h index abcc4bb3..ab74eac8 100644 --- a/dbus/dbus-credentials.h +++ b/dbus/dbus-credentials.h @@ -34,6 +34,7 @@ typedef enum { DBUS_CREDENTIAL_UNIX_PROCESS_ID, DBUS_CREDENTIAL_UNIX_USER_ID, DBUS_CREDENTIAL_ADT_AUDIT_DATA_ID, + DBUS_CREDENTIAL_LINUX_SECURITY_LABEL, DBUS_CREDENTIAL_WINDOWS_SID } DBusCredentialType; @@ -47,6 +48,8 @@ dbus_bool_t _dbus_credentials_add_unix_uid (DBusCredentials dbus_uid_t uid); dbus_bool_t _dbus_credentials_add_windows_sid (DBusCredentials *credentials, const char *windows_sid); +dbus_bool_t _dbus_credentials_add_linux_security_label (DBusCredentials *credentials, + const char *label); dbus_bool_t _dbus_credentials_add_adt_audit_data (DBusCredentials *credentials, void *audit_data, dbus_int32_t size); @@ -55,6 +58,7 @@ dbus_bool_t _dbus_credentials_include (DBusCredentials dbus_pid_t _dbus_credentials_get_pid (DBusCredentials *credentials); dbus_uid_t _dbus_credentials_get_unix_uid (DBusCredentials *credentials); const char* _dbus_credentials_get_windows_sid (DBusCredentials *credentials); +const char * _dbus_credentials_get_linux_security_label (DBusCredentials *credentials); void * _dbus_credentials_get_adt_audit_data (DBusCredentials *credentials); dbus_int32_t _dbus_credentials_get_adt_audit_data_size (DBusCredentials *credentials); dbus_bool_t _dbus_credentials_are_superset (DBusCredentials *credentials, diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index 575a0875..92a108c8 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -363,6 +363,16 @@ _dbus_is_verbose_real (void) return verbose; } +void _dbus_set_verbose (dbus_bool_t state) +{ + verbose = state; +} + +dbus_bool_t _dbus_get_verbose (void) +{ + return verbose; +} + /** * Prints a warning message to stderr * if the user has enabled verbose mode. diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index 6b487f5d..8dea10f1 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -99,6 +99,8 @@ void _dbus_verbose_real (const char *format, #endif void _dbus_verbose_reset_real (void); dbus_bool_t _dbus_is_verbose_real (void); +dbus_bool_t _dbus_get_verbose (void); +void _dbus_set_verbose (dbus_bool_t state); # define _dbus_verbose_reset _dbus_verbose_reset_real # define _dbus_is_verbose _dbus_is_verbose_real diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h index 51c3da8f..7ab91035 100644 --- a/dbus/dbus-shared.h +++ b/dbus/dbus-shared.h @@ -89,6 +89,8 @@ typedef enum /** The monitoring interface exported by the dbus-daemon */ #define DBUS_INTERFACE_MONITORING "org.freedesktop.DBus.Monitoring" +/** The verbose interface exported by the dbus-daemon */ +#define DBUS_INTERFACE_VERBOSE "org.freedesktop.DBus.Verbose" /** The interface supported by introspectable objects */ #define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable" /** The interface supported by objects with properties */ diff --git a/dbus/dbus-sysdeps-unix.c b/dbus/dbus-sysdeps-unix.c index dcfddd17..8010df13 100644 --- a/dbus/dbus-sysdeps-unix.c +++ b/dbus/dbus-sysdeps-unix.c @@ -1666,6 +1666,105 @@ write_credentials_byte (int server_fd, } } +/* return FALSE on OOM, TRUE otherwise, even if no credentials were found */ +static dbus_bool_t +add_linux_security_label_to_credentials (int client_fd, + DBusCredentials *credentials) +{ +#if defined(__linux__) && defined(SO_PEERSEC) + DBusString buf; + socklen_t len = 1024; + dbus_bool_t oom = FALSE; + + if (!_dbus_string_init_preallocated (&buf, len) || + !_dbus_string_set_length (&buf, len)) + return FALSE; + + while (getsockopt (client_fd, SOL_SOCKET, SO_PEERSEC, + _dbus_string_get_data (&buf), &len) < 0) + { + int e = errno; + + _dbus_verbose ("getsockopt failed with %s, len now %lu\n", + _dbus_strerror (e), (unsigned long) len); + + if (e != ERANGE || len <= _dbus_string_get_length (&buf)) + { + _dbus_verbose ("Failed to getsockopt(SO_PEERSEC): %s\n", + _dbus_strerror (e)); + goto out; + } + + /* If not enough space, len is updated to be enough. + * Try again with a large enough buffer. */ + if (!_dbus_string_set_length (&buf, len)) + { + oom = TRUE; + goto out; + } + + _dbus_verbose ("will try again with %lu\n", (unsigned long) len); + } + + if (len <= 0) + { + _dbus_verbose ("getsockopt(SO_PEERSEC) yielded <= 0 bytes: %lu\n", + (unsigned long) len); + goto out; + } + + if (len > _dbus_string_get_length (&buf)) + { + _dbus_verbose ("%lu > %d", (unsigned long) len, + _dbus_string_get_length (&buf)); + _dbus_assert_not_reached ("getsockopt(SO_PEERSEC) overflowed"); + } + + if (_dbus_string_get_byte (&buf, len - 1) == 0) + { + /* the kernel included the trailing \0 in its count, + * but DBusString always has an extra \0 after the data anyway */ + _dbus_verbose ("subtracting trailing \\0\n"); + len--; + } + + if (!_dbus_string_set_length (&buf, len)) + { + _dbus_assert_not_reached ("shortening string should not lead to OOM"); + oom = TRUE; + goto out; + } + + if (strlen (_dbus_string_get_const_data (&buf)) != len) + { + /* LSM people on the linux-security-module@ mailing list say this + * should never happen: the label should be a bytestring with + * an optional trailing \0 */ + _dbus_verbose ("security label from kernel had an embedded \\0, " + "ignoring it\n"); + goto out; + } + + _dbus_verbose ("getsockopt(SO_PEERSEC): %lu bytes excluding \\0: %s\n", + (unsigned long) len, + _dbus_string_get_const_data (&buf)); + + if (!_dbus_credentials_add_linux_security_label (credentials, + _dbus_string_get_const_data (&buf))) + { + oom = TRUE; + goto out; + } + +out: + _dbus_string_free (&buf); + return !oom; +#else + /* no error */ + return TRUE; +#endif +} + /** * Reads a single byte which must be nul (an error occurs otherwise), * and reads unix credentials if available. Clears the credentials @@ -1993,6 +2092,12 @@ _dbus_read_credentials_socket (int client_fd, } } + if (!add_linux_security_label_to_credentials (client_fd, credentials)) + { + _DBUS_SET_OOM (error); + return FALSE; + } + return TRUE; } diff --git a/dbus/dbus-sysdeps-win.c b/dbus/dbus-sysdeps-win.c index dbd9043d..50258151 100644 --- a/dbus/dbus-sysdeps-win.c +++ b/dbus/dbus-sysdeps-win.c @@ -981,10 +981,10 @@ static BOOL is_winxp_sp3_or_lower() /** Gets our SID * @param sid points to sid buffer, need to be freed with LocalFree() - * @param process_id the process id for which the sid should be returned + * @param process_id the process id for which the sid should be returned (use 0 for current process) * @returns process sid */ -static dbus_bool_t +dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id) { HANDLE process_token = INVALID_HANDLE_VALUE; @@ -993,7 +993,13 @@ _dbus_getsid(char **sid, dbus_pid_t process_id) PSID psid; int retval = FALSE; - HANDLE process_handle = OpenProcess(is_winxp_sp3_or_lower() ? PROCESS_QUERY_INFORMATION : PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id); + HANDLE process_handle; + if (process_id == 0) + process_handle = GetCurrentProcess(); + else if (is_winxp_sp3_or_lower()) + process_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, process_id); + else + process_handle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id); if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token)) { diff --git a/dbus/dbus-sysdeps-win.h b/dbus/dbus-sysdeps-win.h index 02e7a83f..3efdf72e 100644 --- a/dbus/dbus-sysdeps-win.h +++ b/dbus/dbus-sysdeps-win.h @@ -88,6 +88,7 @@ dbus_bool_t _dbus_get_install_root(char *prefix, int len); void _dbus_threads_windows_init_global (void); void _dbus_threads_windows_ensure_ctor_linked (void); +dbus_bool_t _dbus_getsid(char **sid, dbus_pid_t process_id); #endif /** @} end of sysdeps-win.h */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index f63e0ced..5ceab0a9 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -1425,6 +1425,33 @@ _dbus_transport_set_unix_user_function (DBusTransport *transport, transport->free_unix_user_data = free_data_function; } +dbus_bool_t +_dbus_transport_get_linux_security_label (DBusTransport *transport, + char **label_p) +{ + DBusCredentials *auth_identity; + + *label_p = NULL; + + if (!transport->authenticated) + return FALSE; + + auth_identity = _dbus_auth_get_identity (transport->auth); + + if (_dbus_credentials_include (auth_identity, + DBUS_CREDENTIAL_LINUX_SECURITY_LABEL)) + { + /* If no memory, we are supposed to return TRUE and set NULL */ + *label_p = _dbus_strdup (_dbus_credentials_get_linux_security_label (auth_identity)); + + return TRUE; + } + else + { + return FALSE; + } +} + /** * See dbus_connection_get_windows_user(). * diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 39c74c46..843f2312 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -87,6 +87,9 @@ void _dbus_transport_set_unix_user_function (DBusTransport DBusFreeFunction *old_free_data_function); dbus_bool_t _dbus_transport_get_windows_user (DBusTransport *transport, char **windows_sid_p); +dbus_bool_t _dbus_transport_get_linux_security_label (DBusTransport *transport, + char **label_p); + void _dbus_transport_set_windows_user_function (DBusTransport *transport, DBusAllowWindowsUserFunction function, void *data, diff --git a/doc/Makefile.am b/doc/Makefile.am index b9a4c106..f875dc25 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -7,6 +7,7 @@ man_pages = \ dbus-monitor.1 \ dbus-run-session.1 \ dbus-send.1 \ + dbus-test-tool.1 \ dbus-uuidgen.1 \ $(NULL) @@ -30,6 +31,7 @@ STATIC_DOCS = \ dbus-specification.xml \ dbus-test-plan.xml \ dbus-tutorial.xml \ + dbus-api-design.duck \ dcop-howto.txt \ introspect.xsl \ $(DTDS) @@ -50,8 +52,24 @@ STATIC_HTML = \ diagram.svg \ $(NULL) +# Static HTML helper files generated by yelp-build. +YELP_STATIC_HTML = \ + yelp.js \ + C.css \ + jquery.js \ + jquery.syntax.js \ + jquery.syntax.brush.html.js \ + jquery.syntax.core.js \ + jquery.syntax.layout.yelp.js \ + $(NULL) + dist_html_DATA += $(STATIC_HTML) +# Content HTML files generated by yelp-build. +YELP_HTML = \ + dbus-api-design.html \ + $(NULL) + XMLTO_HTML = \ dbus-faq.html \ dbus-specification.html \ @@ -84,6 +102,16 @@ dbus.devhelp: $(srcdir)/doxygen_to_devhelp.xsl doxygen.stamp $(XSLTPROC) -o $@ $< api/xml/index.xml endif +if DBUS_DUCKTYPE_DOCS_ENABLED +html_DATA += $(YELP_HTML) $(YELP_STATIC_HTML) + +%.page: %.duck + $(DUCKTYPE) -o $@ $< +%.html: %.page + $(YELP_BUILD) html $< +$(YELP_STATIC_HTML): $(YELP_HTML) +endif + # this assumes CREATE_SUBDIRS isn't set to YES in Doxyfile # (which it isn't currently) install-data-local:: doxygen.stamp @@ -112,13 +140,15 @@ BONUS_FILES = \ $(top_srcdir)/COPYING \ $(top_srcdir)/ChangeLog -dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) +dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) $(YELP_HTML) $(YELP_STATIC_HTML) $(AM_V_at)rm -rf $@ $@.tmp $(AM_V_GEN)$(MKDIR_P) $@.tmp/api $(AM_V_at)cd $(srcdir) && cp $(STATIC_DOCS) @abs_builddir@/$@.tmp $(AM_V_at)cd $(srcdir) && cp $(dist_doc_DATA) @abs_builddir@/$@.tmp $(AM_V_at)cd $(srcdir) && cp $(STATIC_HTML) @abs_builddir@/$@.tmp $(AM_V_at)cp $(XMLTO_HTML) @abs_builddir@/$@.tmp + $(AM_V_at)cp $(YELP_HTML) @abs_builddir@/$@.tmp + $(AM_V_at)cp $(YELP_STATIC_HTML) @abs_builddir@/$@.tmp $(AM_V_at)cp $(MAN_HTML_FILES) @abs_builddir@/$@.tmp $(AM_V_at)cp $(MAN_XML_FILES) @abs_builddir@/$@.tmp $(AM_V_at)cp $(BONUS_FILES) @abs_builddir@/$@.tmp @@ -142,7 +172,7 @@ maintainer-upload-docs: dbus-docs.tar.gz dbus-docs else maintainer-upload-docs: @echo "Can't upload documentation! Re-run configure with" - @echo " --enable-doxygen-docs --enable-xml-docs" + @echo " --enable-doxygen-docs --enable-xml-docs --enable-ducktype-docs" @echo "and ensure that man2html is installed." @false endif @@ -151,6 +181,8 @@ CLEANFILES = \ $(man1_MANS) \ $(MAN_XML_FILES) \ $(XMLTO_HTML) \ + $(YELP_HTML) \ + $(YELP_STATIC_HTML) \ $(NULL) clean-local: diff --git a/doc/Makefile.in b/doc/Makefile.in index 412da9bc..ab554382 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -80,14 +80,16 @@ build_triplet = @build@ host_triplet = @host@ @DBUS_XML_DOCS_ENABLED_TRUE@am__append_1 = $(XMLTO_HTML) @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_HAVE_XSLTPROC_TRUE@am__append_2 = dbus.devhelp +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@am__append_3 = $(YELP_HTML) $(YELP_STATIC_HTML) subdir = doc DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(srcdir)/dbus-cleanup-sockets.1.xml.in \ $(srcdir)/dbus-daemon.1.xml.in $(srcdir)/dbus-launch.1.xml.in \ $(srcdir)/dbus-monitor.1.xml.in \ $(srcdir)/dbus-run-session.1.xml.in \ - $(srcdir)/dbus-send.1.xml.in $(srcdir)/dbus-uuidgen.1.xml.in \ - $(dist_doc_DATA) $(dist_html_DATA) TODO + $(srcdir)/dbus-send.1.xml.in $(srcdir)/dbus-test-tool.1.xml.in \ + $(srcdir)/dbus-uuidgen.1.xml.in $(dist_doc_DATA) \ + $(dist_html_DATA) TODO ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/as-ac-expand.m4 \ $(top_srcdir)/m4/compiler.m4 $(top_srcdir)/m4/libtool.m4 \ @@ -102,7 +104,7 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = dbus-cleanup-sockets.1.xml dbus-daemon.1.xml \ dbus-launch.1.xml dbus-monitor.1.xml dbus-run-session.1.xml \ - dbus-send.1.xml dbus-uuidgen.1.xml + dbus-send.1.xml dbus-test-tool.1.xml dbus-uuidgen.1.xml CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -162,6 +164,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -216,6 +220,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -305,6 +310,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -372,6 +378,7 @@ man_pages = \ dbus-monitor.1 \ dbus-run-session.1 \ dbus-send.1 \ + dbus-test-tool.1 \ dbus-uuidgen.1 \ $(NULL) @@ -390,6 +397,7 @@ STATIC_DOCS = \ dbus-specification.xml \ dbus-test-plan.xml \ dbus-tutorial.xml \ + dbus-api-design.duck \ dcop-howto.txt \ introspect.xsl \ $(DTDS) @@ -399,7 +407,7 @@ EXTRA_DIST = \ doxygen_to_devhelp.xsl \ $(STATIC_DOCS) -html_DATA = $(am__append_1) $(am__append_2) +html_DATA = $(am__append_1) $(am__append_2) $(am__append_3) dist_html_DATA = $(STATIC_HTML) # diagram.png/diagram.svg aren't really HTML, but must go in the same @@ -409,6 +417,24 @@ STATIC_HTML = \ diagram.svg \ $(NULL) + +# Static HTML helper files generated by yelp-build. +YELP_STATIC_HTML = \ + yelp.js \ + C.css \ + jquery.js \ + jquery.syntax.js \ + jquery.syntax.brush.html.js \ + jquery.syntax.core.js \ + jquery.syntax.layout.yelp.js \ + $(NULL) + + +# Content HTML files generated by yelp-build. +YELP_HTML = \ + dbus-api-design.html \ + $(NULL) + XMLTO_HTML = \ dbus-faq.html \ dbus-specification.html \ @@ -433,6 +459,8 @@ CLEANFILES = \ $(man1_MANS) \ $(MAN_XML_FILES) \ $(XMLTO_HTML) \ + $(YELP_HTML) \ + $(YELP_STATIC_HTML) \ $(NULL) all: all-am @@ -480,6 +508,8 @@ dbus-run-session.1.xml: $(top_builddir)/config.status $(srcdir)/dbus-run-session cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ dbus-send.1.xml: $(top_builddir)/config.status $(srcdir)/dbus-send.1.xml.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +dbus-test-tool.1.xml: $(top_builddir)/config.status $(srcdir)/dbus-test-tool.1.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ dbus-uuidgen.1.xml: $(top_builddir)/config.status $(srcdir)/dbus-uuidgen.1.xml.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ @@ -774,6 +804,12 @@ uninstall-man: uninstall-man1 @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_HAVE_XSLTPROC_TRUE@dbus.devhelp: $(srcdir)/doxygen_to_devhelp.xsl doxygen.stamp @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_HAVE_XSLTPROC_TRUE@ $(XSLTPROC) -o $@ $< api/xml/index.xml +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@%.page: %.duck +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@ $(DUCKTYPE) -o $@ $< +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@%.html: %.page +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@ $(YELP_BUILD) html $< +@DBUS_DOXYGEN_DOCS_ENABLED_TRUE@@DBUS_DUCKTYPE_DOCS_ENABLED_TRUE@$(YELP_STATIC_HTML): $(YELP_HTML) + # this assumes CREATE_SUBDIRS isn't set to YES in Doxyfile # (which it isn't currently) @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@install-data-local:: doxygen.stamp @@ -792,13 +828,15 @@ uninstall-man: uninstall-man1 @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@ rmdir --ignore-fail-on-non-empty $(DESTDIR)$(apidir) || \ @DBUS_DOXYGEN_DOCS_ENABLED_TRUE@ rmdir $(DESTDIR)$(apidir) -@DBUS_CAN_UPLOAD_DOCS_TRUE@dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) +@DBUS_CAN_UPLOAD_DOCS_TRUE@dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) $(YELP_HTML) $(YELP_STATIC_HTML) @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)rm -rf $@ $@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_GEN)$(MKDIR_P) $@.tmp/api @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cd $(srcdir) && cp $(STATIC_DOCS) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cd $(srcdir) && cp $(dist_doc_DATA) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cd $(srcdir) && cp $(STATIC_HTML) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(XMLTO_HTML) @abs_builddir@/$@.tmp +@DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(YELP_HTML) @abs_builddir@/$@.tmp +@DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(YELP_STATIC_HTML) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(MAN_HTML_FILES) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(MAN_XML_FILES) @abs_builddir@/$@.tmp @DBUS_CAN_UPLOAD_DOCS_TRUE@ $(AM_V_at)cp $(BONUS_FILES) @abs_builddir@/$@.tmp @@ -815,7 +853,7 @@ uninstall-man: uninstall-man1 @DBUS_CAN_UPLOAD_DOCS_TRUE@ cd $(srcdir) && scp -p $(DTDS) $(SPECIFICATION_SERVER):$(SPECIFICATION_PATH)/ @DBUS_CAN_UPLOAD_DOCS_FALSE@maintainer-upload-docs: @DBUS_CAN_UPLOAD_DOCS_FALSE@ @echo "Can't upload documentation! Re-run configure with" -@DBUS_CAN_UPLOAD_DOCS_FALSE@ @echo " --enable-doxygen-docs --enable-xml-docs" +@DBUS_CAN_UPLOAD_DOCS_FALSE@ @echo " --enable-doxygen-docs --enable-xml-docs --enable-ducktype-docs" @DBUS_CAN_UPLOAD_DOCS_FALSE@ @echo "and ensure that man2html is installed." @DBUS_CAN_UPLOAD_DOCS_FALSE@ @false diff --git a/doc/busconfig.dtd b/doc/busconfig.dtd index 0cc519b4..8c5ac334 100644 --- a/doc/busconfig.dtd +++ b/doc/busconfig.dtd @@ -11,7 +11,8 @@ include | policy | limit | - selinux)*> + selinux | + apparmor)*> <!ELEMENT user (#PCDATA)> <!ELEMENT listen (#PCDATA)> @@ -63,3 +64,7 @@ <!ATTLIST associate own CDATA #REQUIRED context CDATA #REQUIRED> + +<!ELEMENT apparmor EMPTY> +<!ATTLIST apparmor + mode (required|enabled|disabled) "enabled"> diff --git a/doc/dbus-api-design.duck b/doc/dbus-api-design.duck new file mode 100644 index 00000000..be3ea9fd --- /dev/null +++ b/doc/dbus-api-design.duck @@ -0,0 +1,888 @@ += D-Bus API Design Guidelines +@link[guide >index] +@credit[type="author copyright"] + @name Philip Withnall + @email philip.withnall@collabora.co.uk + @years 2015 +@desc Guidelines for writing high quality D-Bus APIs +@revision[date=2015-02-05 status=draft] + +[synopsis] + [title] + Summary + + The most common use for D-Bus is in implementing a service which will be + consumed by multiple client programs, and hence all interfaces exported on the + bus form a public API. Designing a D-Bus API is like designing any other API: + there is a lot of flexibility, but there are design patterns to follow and + anti-patterns to avoid. + + This guide aims to explain the best practices for writing D-Bus APIs. These + have been refined over several years of use of D-Bus in many projects. + Pointers will be given for implementing APIs using common D-Bus + libraries like + $link[>>https://developer.gnome.org/gio/stable/gdbus-convenience.html](GDBus), + but detailed implementation instructions are left to the libraries’ + documentation. Note that you should $em(not) use dbus-glib to implement D-Bus + services as it is deprecated and unmaintained. Most services should also avoid + libdbus (dbus-1), which is a low-level library and is awkward to use + correctly: it is designed to be used via a language binding such as + $link[>>http://qt-project.org/doc/qt-4.8/qtdbus.html](QtDBus). + + For documentation on D-Bus itself, see the + $link[>>http://dbus.freedesktop.org/doc/dbus-specification.html](D-Bus + specification). + +[links section] + +== APIs + [id="apis"] + +A D-Bus API is a specification of one or more interfaces, which will be +implemented by objects exposed by a service on the bus. Typically an API is +designed as a set of $link[>#interface-files](interface files), and the +implementation of the service follows those files. Some projects, however, +choose to define the API in the code for the service, and to export XML +interface files from the running service +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable](using +D-Bus introspection). Both are valid approaches. + +For simplicity, this document uses the XML descriptions of D-Bus interfaces as +the canonical representation. + +== Interface files + [id="interface-files"] + +A D-Bus interface file is an XML file which describes one or more D-Bus +interfaces, and is the best way of describing a D-Bus API in a machine +readable way. The format is described in the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format](D-Bus +specification), and is supported by tools such as $cmd(gdbus-codegen). + +Interface files for public API should be installed to +$code($var($$(datadir$))/dbus-1/interfaces) so that other services can load +them. Private APIs should not be installed. There should be one file installed +per D-Bus interface, named after the interface, containing a single top-level +$code(<node>) element with a single $code(<interface>) beneath it. For example, +interface $code(com.example.MyService1.Manager) would be described by file +$file($var($$(datadir$))/dbus-1/interfaces/com.example.MyService1.Manager.xml): + +[listing] + [title] + Example D-Bus Interface XML + [desc] + A brief example interface XML document. + [code mime="application/xml"] + <!DOCTYPE node PUBLIC + "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" + "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" > + <node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="com.example.MyService1.InterestingInterface"> + <method name="AddContact"> + <arg name="name" direction="in" type="s"> + <doc:doc><doc:summary>Name of new contact</doc:summary></doc:doc> + </arg> + <arg name="email" direction="in" type="s"> + <doc:doc><doc:summary>E-mail address of new contact</doc:summary></doc:doc> + </arg> + <arg name="id" direction="out" type="u"> + <doc:doc><doc:summary>ID of newly added contact</doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Adds a new contact to the address book with their name and + e-mail address. + </doc:para> + </doc:description> + </doc:doc> + </method> + </interface> + </node> + +If an interface defined by service A needs to be used by client B, client B +should declare a build time dependency on service A, and use the installed copy +of the interface file for any code generation it has to do. It should $em(not) +have a local copy of the interface, as that could then go out of sync with the +canonical copy in service A’s git repository. + +== API versioning + [id="api-versioning"] + +$link[>>http://ometer.com/parallel.html](Just like C APIs), D-Bus interfaces +should be designed to be usable in parallel with API-incompatible versions. This +is achieved by including a version number in each interface name, service name +and object path which is incremented on every backwards-incompatible change. + +Version numbers should be included in all APIs from the first release, as that +means moving to a new version is as simple as incrementing the number, rather +than inserting a number everywhere, which takes more effort. + +New API can be added to a D-Bus interface without incrementing the version +number, as such additions are still backwards-compatible. However, clients +should gracefully handle the $code(org.freedesktop.DBus.Error.UnknownMethod) +error reply from all D-Bus method calls if they want to run against older +versions of the service which don’t implement new methods. (This also prevents +use of generated bindings; any method which a client wants to gracefully fall +back from should be called using a generic D-Bus method invocation rather than +a specific generated binding.) + +When API is broken, changed or removed, the service’s version number must be +bumped; for example, from $code(com.example.MyService1) +to $code(com.example.MyService2). If backwards compatibility is maintained in +the service by implementing both the old and new interfaces, the service can +own $em(both) well-known names and clients can use whichever they support. + +As discussed in $link[>#annotations], new or deprecated APIs should be marked in +the interface XML using annotations. + +Note, however, that supporting multiple interface versions simultaneously +requires that $em(object paths) are versioned as well, so objects $em(must not) +be put on the bus at the root path (‘/’). This is for technical reasons: signals +sent from a D-Bus service have the originating service name overwritten by its +unique name (e.g. $code(com.example.MyService2) is overwritten by $code(:1:15)). +If object paths are shared between objects implementing two versions of the +service’s interface, client programs cannot tell which object a signal has come +from. The solution is to include the version number in all object paths, for +example $code(/com/example/MyService1/Manager) instead of $code(/) or +$code(/com/example/MyService/Manager). + +In summary, version numbers should be included in all service names, interface +names and object paths: +[list] +* $code(com.example.MyService1) +* $code(com.example.MyService1.InterestingInterface) +* $code(com.example.MyService1.OtherInterface) +* $code(/com/example/MyService1/Manager) +* $code(/com/example/MyService1/OtherObject) + +== API design + [id="api-design"] + +D-Bus API design is broadly the same as C API design, but there are a few +additional points to bear in mind which arise both from D-Bus’ features +(explicit errors, signals and properties), and from its implementation as an IPC +system. + +D-Bus method calls are much more expensive than C function calls, typically +taking on the order of milliseconds to complete a round trip. Therefore, the +design should minimize the number of method calls needed to perform an +operation. + +The type system is very expressive, especially compared to C’s, and APIs should +take full advantage of it. + +Similarly, its support for signals and properties differentiates it from normal +C APIs, and well written D-Bus APIs make full use of these features where +appropriate. They can be coupled with standard interfaces defined in the D-Bus +specification to allow for consistent access to properties and objects in a +hierarchy. + +=== Minimizing Round Trips + [id="round-trips"] + +Each D-Bus method call is a round trip from a client program to a service and +back again, which is expensive — taking on the order of a millisecond. One of +the top priorities in D-Bus API design is to minimize the number of round trips +needed by clients. This can be achieved by a combination of providing specific +convenience APIs and designing APIs which operate on large data sets in a single +call, rather than requiring as many calls as elements in the data set. + +Consider an address book API, $code(com.example.MyService1.AddressBook). It +might have an $code(AddContact(ss$) → (u$)) method to add a contact (taking name +and e-mail address parameters and returning a unique contact ID), and a +$code(RemoveContact(u$)) method to remove one by ID. In the normal case of +operating on single contacts in the address book, these calls are optimal. +However, if the user wants to import a list of contacts, or delete multiple +contacts simultaneously, one call has to be made per contact — this could take +a long time for large contact lists. + +Instead of the $code(AddContact) and $code(RemoveContact) methods, the interface +could have an $code(UpdateContacts(a(ss$)au$) → (au$)) method, which takes an array +of structs containing the new contacts’ details, and an array of IDs of the +contacts to delete, and returns an array of IDs for the new contacts. This +reduces the number of round trips needed to one. + +Adding convenience APIs to replace a series of common method calls with a single +method call specifically for that task is best done after the API has been +profiled and bottlenecks identified, otherwise it could lead to premature +optimization. One area where convenience methods can typically be added +is during initialization of a client, where multiple method calls are needed to +establish its state with the service. + +=== Taking Advantage of the Type System + [id="type-system"] + +D-Bus’ type system is similar to Python’s, but with a terser syntax which may be +unfamiliar to C developers. The key to using the type system effectively is +to expose as much structure in types as possible. In particular, sending +structured strings over D-Bus should be avoided, as they need to be built and +parsed; both are complex operations which are prone to bugs. + +For APIs being used in constrained situations, enumerated values should be +transmitted as unsigned integers. For APIs which need to be extended by third +parties or which are used in more loosely coupled systems, enumerated values +should be strings in some defined format. + +Transmitting values as integers means string parsing and matching can be +avoided, the messages are more compact, and typos can be more easily avoided by +developers (if, for example, C enums are used in the implementation). + +Transmitting values as strings means additional values can be defined by third +parties without fear of conflicting over integer values; for instance by using +the same reverse-domain-name namespacing as D-Bus interfaces. + +In both cases, the interface documentation should describe the meaning of each +value. It should state whether the type can be extended in future and, if so, +how the service and client should handle unrecognized values — typically by +considering them equal to an ‘unknown’ or ‘failure’ value. Conventionally, zero +is used as the ‘unknown’ value. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + <!-- + Status: + + Status of the object. + Valid statuses: ‘unknown’, ‘ready’, ‘complete’. + --> + <property name="Status" type="s" access="read" /> + + Use: + [code style="valid" mime="application/xml"] + <!-- + Status: + + Status of the object. + Valid statuses: 0 = Unknown, 1 = Ready, 2 = Complete. + Unrecognized statuses should be considered equal to Unknown. + --> + <property name="Status" type="u" access="read" /> + +Similarly, enumerated values should be used instead of booleans, as they allow +extra values to be added in future, and there is no ambiguity about the sense of +the boolean value. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + <!-- + MoveAddressBook: + @direction: %TRUE to move it up in the list, %FALSE to move it down + + Move this address book up or down in the user’s list of address books. + Higher address books have their contacts displayed first in search + results. + --> + <method name="MoveAddressBook"> + <arg name="direction" type="b" direction="in" /> + </method> + + Be more explicit than a boolean: + [code style="valid" mime="application/xml"] + <!-- + MoveAddressBook: + @direction: 0 = Move it up in the list, 1 = Move it down + + Move this address book up or down in the user’s list of address books. + Higher address books have their contacts displayed first in search + results. + + Unrecognized enum values for @direction will result in the address book + not moving. + --> + <method name="MoveAddressBook"> + <arg name="direction" type="u" direction="in" /> + </method> + +Enumerated values should also be used instead of $em(human readable) strings, +which should not be sent over the bus if possible. The service and client could +be running in different locales, and hence interpret any human readable strings +differently, or present them to the user in the wrong language. Transmit an +enumerated value and convert it to a human readable string in the client. + +In situations where a service has received a human readable string from +somewhere else, it should pass it on unmodified to the client, ideally with its +locale alongside. Passing human readable information to a client is better than +passing nothing. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + <!-- + ProgressNotification: + @progress_message: Human readable progress message string. + + Emitted whenever significant progress is made with some example + operation. The @progress_message can be displayed in a UI dialogue to + please the user. + --> + <signal name="ProgressNotification"> + <arg name="progress_message" type="s" /> + </method> + + The progress should be reported as an enumerated value: + [code style="valid" mime="application/xml"] + <!-- + ProgressNotification: + @progress_state: 0 = Preparing, 1 = In progress, 2 = Finished + + Emitted whenever significant progress is made with some example + operation. The @progress_state is typically converted to a human readable + string and presented to the user. Unrecognized @progress_state values + should be treated as state 1, in progress. + --> + <signal name="ProgressNotification"> + <arg name="progress_state" type="u" /> + </method> + +D-Bus has none of the problems of signed versus unsigned integers which C has +(specifically, it does not do implicit sign conversion), so integer types should +always be chosen to be an appropriate size and signedness for the data they +could possibly contain. Typically, unsigned values are more frequently needed +than signed values. + +Structures can be used almost anywhere in a D-Bus type, and arrays of structures +are particularly useful. Structures should be used wherever data fields are +related. Note, however, that structures are not extensible in future, so always +consider $link[>#extensibility]. + +[example] + For example, instead of several identically-indexed arrays containing + different properties of the same set of items: + [code style="invalid" mime="application/xml"] + <!-- + AddContacts: + @names: Array of contact names to add. + @emails: Corresponding array of contact e-mail addresses. + @ids: Returned array of the IDs of the new contacts. This will be the + same length as @names. + + Add zero or more contacts to the address book, using their names and + e-mail addresses. @names and @emails must be the same length. + --> + <method name="AddContacts"> + <arg name="names" type="as" direction="in" /> + <arg name="emails" type="as" direction="in" /> + <arg name="ids" type="au" direction="out" /> + </method> + + The arrays can be combined into a single array of structures: + [code style="invalid" mime="application/xml"] + <!-- + AddContacts: + @details: Array of (contact name, contact e-mail address) to add. + @ids: Returned array of the IDs of the new contacts. This will be the + same length as @details. + + Add zero or more contacts to the address book, using their names and + e-mail addresses. + --> + <method name="AddContacts"> + <arg name="details" type="a(ss)" direction="in" /> + <arg name="ids" type="au" direction="out" /> + </method> + +Note that D-Bus arrays are automatically transmitted with their length, so there +is no need to null-terminate them or encode their length separately. + +[comment] + FIXME: Mention maybe types and the extended kdbus/GVariant type system once + that’s stable and round-trip-ability is no longer a concern. + +=== Extensibility + [id="extensibility"] + +Some D-Bus APIs have very well-defined use cases, and will never need extension. +Others are used in more loosely coupled systems which may change over time, and +hence should be designed to be extensible from the beginning without the need +to break API in future. This is a trade off between having a more complex API, +and being able to easily extend it in future. + +The key tool for extensibility in D-Bus is $code(a{sv}), the dictionary mapping +strings to variants. If well-defined namespaced strings are used as the +dictionary keys, arbitrary D-Bus peers can add whatever information they need +into the dictionary. Any other peer which understands it can query and retrieve +the information; other peers will ignore it. + +The canonical example of an extensible API using $code(a{sv}) is +$link[>>http://telepathy.freedesktop.org/spec/](Telepathy). It uses $code(a{sv}) +values as the final element in structures to allow them to be extended in +future. + +A secondary tool is the use of flag fields in method calls. The set of accepted +flags is entirely under the control of the interface designer and, as with +enumerated types, can be extended in future without breaking API. Adding more +flags allows the functionality of the method call to be tweaked. + +=== Using Signals, Properties and Errors + [id="using-the-features"] + +D-Bus method calls are explicitly asynchronous due to the latency inherent in +IPC. This means that peers should not block on receiving a reply from a method +call; they should schedule other work (in a main loop) and handle the reply when +it is received. Even though method replies may take a while, the caller is +$em(guaranteed) to receive exactly one reply eventually. This reply could be the +return value from the method, an error from the method, or an error from the +D-Bus daemon indicating the service failed in some way (e.g. due to crashing). + +Because of this, service implementations should be careful to always reply +exactly once to each method call. Replying at the end of a long-running +operation is correct — the client will patiently wait until the operation has +finished and the reply is received. + +Note that D-Bus client bindings may implement synthetic timeouts of several +tens of seconds, unless explicitly disabled for a call. For very long-running +operations, you should disable the client bindings’ timeout and make it clear +in the client’s UI that the application has not frozen and is simply running a +long operation. + +An anti-pattern to avoid in this situation is to start a long-running operation +when a method call is received, then to never reply to the method call and +instead notify the client of completion of the operation via a signal. This +approach is incorrect as signal emissions do not have a one-to-one relationship +with method calls — the signal could theoretically be emitted multiple times, or +never, which the client would not be able to handle. + +Similarly, use a D-Bus error reply to signify failure of an operation triggered +by a method call, rather than using a custom error code in the method’s +reply. This means that a reply always indicates success, and an error always +indicates failure — rather than a reply indicating either depending on its +parameters, and having to return dummy values in the other parameters. Using +D-Bus error replies also means such failures can be highlighted in debugging +tools, simplifying debugging. + +Clients should handle all possible standard and documented D-Bus errors for each +method call. IPC inherently has more potential failures than normal C function +calls, and clients should be prepared to handle all of them gracefully. + +=== Using Standard Interfaces + [id="standard-interfaces"] + +Use standard D-Bus interfaces where possible. + +==== Properties + [id="interface-properties"] + +The D-Bus specification defines the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties]($code(org.freedesktop.DBus.Properties)) +interface, which should be used by all objects to notify clients of changes +to their property values, with the $code(PropertiesChanged) signal. This signal +eliminates the need for individual $code($var(PropertyName)Changed) signals, and +allows multiple properties to be notified in a single signal emission, reducing +IPC round trips. Similarly, the $code(Get) and $code(Set) methods can be used to +manipulate properties on an object, eliminating redundant +$code(Get$var(PropertyName)) and $code(Set$var(PropertyName)) methods. + +[example] + For example, consider an object implementing an interface + $code(com.example.MyService1.SomeInterface) with methods: + [list] + * $code(GetName($) → (s$)) + * $code(SetName(s$) → ($)) + * $code(GetStatus($) → (u$)) + * $code(RunOperation(ss$) → (u$)) + and signals: + [list] + * $code(NameChanged(u$)) + * $code(StatusChanged(u$)) + + The interface could be cut down to a single method: + [list] + * $code(RunOperation(ss$) → (u$)) + The object could then implement the $code(org.freedesktop.DBus.Properties) + interface and define properties: + [list] + * $code(Name) of type $code(s), read–write + * $code(Status) of type $code(u), read-only + + The $code(NameChanged) and $code(StatusChanged) signals would be replaced by + $code(org.freedesktop.DBus.Properties.PropertiesChanged). + +==== Object Manager + [id="interface-object-manager"] + +The specification also defines the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager]($code(org.freedesktop.DBus.ObjectManager)) +interface, which should be used whenever a service needs to expose a variable +number of objects of the same class in a flat or tree-like structure, and +clients are expected to be interested in most or all of the objects. For +example, this could be used by an address book service which exposes multiple +address books, each as a separate object. The $code(GetManagedObjects) method +allows the full object tree to be queried, returning all the objects’ properties +too, eliminating the need for further IPC round trips to query the properties. + +If clients are not expected to be interested in most of the exposed objects, +$code(ObjectManager) should $em(not) be used, as it will send all of the objects +to each client anyway, wasting bus bandwidth. A file manager, therefore, should +not expose the entire file system hierarchy using $code(ObjectManager). + +[example] + For example, consider an object implementing an interface + $code(com.example.MyService1.AddressBookManager) with methods: + [list] + * $code(GetAddressBooks($) → (ao$)) + and signals: + [list] + * $code(AddressBookAdded(o$)) + * $code(AddressBookRemoved(o$)) + + If the manager object is at path + $code(/com/example/MyService1/AddressBookManager), each address book is a + child object, e.g. + $code(/com/example/MyService1/AddressBookManager/SomeAddressBook). + + The interface could be eliminated, and the + $code(org.freedesktop.DBus.ObjectManager) interface implemented on the manager + object instead. + + Calls to $code(GetAddressBooks) would become calls to $code(GetManagedObjects) + and emissions of the $code(AddressBookAdded) and $code(AddressBookRemoved) + signals would become emissions of $code(InterfacesAdded) and + $code(InterfacesRemoved). + +=== Naming Conventions + [id="naming-conventions"] + +All D-Bus names, from service names through to method parameters, follow a set +of conventions. Following these conventions makes APIs more natural to use and +consistent with all other services on the system. + +Services use reverse-domain-name notation, based on the domain name of the +project providing the service (all in lower case), plus a unique name for the +service (in camel case). + +[example] + For example, version 2 of an address book application called $code(ContactDex) + provided by a software vendor whose website is $code(chocolateteapot.com) + would use service name $code(com.chocolateteapot.ContactDex2). + +Almost all names use camel case with no underscores, as in the examples below. +Method and signal parameters are an exception, using +$code(lowercase_with_underscores). Type information is never encoded in the +parameter name (i.e. $em(not) +$link[>>http://en.wikipedia.org/wiki/Hungarian_notation](Hungarian notation)). + +[example] + [terms] + - Service Name + * $code(com.example.MyService1) + - Interface Name + * $code(com.example.MyService1.SomeInterface) + - Object Path (Root Object) + * $code(/com/example/MyService1) + - Object Path (Child Object) + * $code(/com/example/MyService1/SomeChild) + - Object Path (Grandchild Object) + * $code(/com/example/MyService1/AnotherChild/Grandchild1) + - Method Name + * $code(com.example.MyService1.SomeInterface.MethodName) + - Signal Name + * $code(com.example.MyService1.SomeInterface.SignalName) + - Property Name + * $code(com.example.MyService1.SomeInterface.PropertyName) + +See also: $link[>#api-versioning]. + +== Code generation + [id="code-generation"] + +Rather than manually implementing both the server and client sides of a D-Bus +interface, it is often easier to write the interface XML description and use a +tool such as +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen)) +to generate type-safe C APIs, then build the implementation using those. This +avoids the tedious and error-prone process of writing code to build and read +D-Bus parameter variants for each method call. + +Use of code generators is beyond the scope of this guide; for more information, +see the +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen) +manual). + +== Annotations + [id="annotations"] + +Annotations may be added to the interface XML to expose metadata on the API +which can be used by documentation or code generation tools to modify their +output. Some standard annotations are given in the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format](D-Bus +specification), but further annotations may be defined by specific tools. + +For example, $cmd(gdbus-codegen) defines several useful annotations, listed on +its man page. + +The following annotations are the most useful: + +[terms] +- $code(org.freedesktop.DBus.Deprecated) +* Mark a symbol as deprecated. This should be used whenever the API is changed, + specifying the version introducing the deprecation, the reason for + deprecation, and a replacement symbol to use. +- $code(org.gtk.GDBus.Since) +* Mark a symbol as having been added to the API after the initial release. This + should include the version the symbol was first added in. +- $code(org.gtk.GDBus.C.Name) and $code(org.freedesktop.DBus.GLib.CSymbol) +* Both used interchangeably to hint at a C function name to use when generating + code for a symbol. Use of this annotation can make generated bindings a lot + more pleasant to use. +- $code(org.freedesktop.DBus.Property.EmitsChangedSignal) +* Indicate whether a property is expected to emit change signals. This can + affect code generation, but is also useful documentation, as client programs + then know when to expect property change notifications and when they have to + requery. + +== Documentation + [id="documentation"] + +Also just like C APIs, D-Bus APIs must be documented. There are several methods +for documenting the interfaces, methods, properties and signals in a D-Bus +interface XML file, each unfortunately with their own downsides. You should +choose the method which best matches the tooling and workflow you are using. + +=== XML Comments + +XML comments containing documentation in the +$link[>>https://developer.gnome.org/gtk-doc-manual/stable/documenting_syntax.html.en](gtk-doc +format) is the recommended format for use with +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen)). +Using $cmd(gdbus-codegen), these comments can be extracted, converted to DocBook +format and included in the project’s API manual. For example: + +[listing] + [title] + Documentation Comments in D-Bus Interface XML + [desc] + Example gtk-doc–style documentation comments in the introspection XML for + the $code(org.freedesktop.DBus.Properties) interface. + [code mime="application/xml"] + <!-- + org.freedesktop.DBus.Properties: + @short_description: Standard property getter/setter interface + + Interface for all objects which expose properties on the bus, allowing + those properties to be got, set, and signals emitted to notify of changes + to the property values. + --> + <interface name="org.freedesktop.DBus.Properties"> + <!-- + Get: + @interface_name: Name of the interface the property is defined on. + @property_name: Name of the property to get. + @value: Property value, wrapped in a variant. + + Retrieves the value of the property at @property_name on + @interface_name on this object. If @interface_name is an empty string, + all interfaces will be searched for @property_name; if multiple + properties match, the result is undefined. + + If @interface_name or @property_name do not exist, a + #org.freedesktop.DBus.Error.InvalidArgs error is returned. + --> + <method name="Get"> + <arg type="s" name="interface_name" direction="in"/> + <arg type="s" name="property_name" direction="in"/> + <arg type="v" name="value" direction="out"/> + </method> + + <!-- + PropertiesChanged: + @interface_name: Name of the interface the properties changed on. + @changed_properties: Map of property name to updated value for the + changed properties. + @invalidated_properties: List of names of other properties which have + changed, but whose updated values are not notified. + + Emitted when one or more properties change values on @interface_name. + A property may be listed in @changed_properties or + @invalidated_properties depending on whether the service wants to + broadcast the property’s new value. If a value is large or infrequently + used, the service might not want to broadcast it, and will wait for + clients to request it instead. + --> + <signal name="PropertiesChanged"> + <arg type="s" name="interface_name"/> + <arg type="a{sv}" name="changed_properties"/> + <arg type="as" name="invalidated_properties"/> + </signal> + </interface> + +[comment] + FIXME: This is only present to fix + $link[>>https://github.com/projectmallard/mallard-ducktype/issues/2]. + +=== XML Annotations + +Documentation can also be added as annotation elements in the XML. This format +is also supported by $cmd(gdbus-codegen), but gtk-doc comments are preferred. +For example: + +[listing] + [title] + Documentation Annotations in D-Bus Interface XML + [desc] + Example GDBus documentation annotations in the introspection XML for + the $code(org.freedesktop.DBus.Properties) interface. + [code mime="application/xml"] + <interface name="org.freedesktop.DBus.Properties"> + <annotation name="org.gtk.GDBus.DocString" value="Interface for all + objects which expose properties on the bus, allowing those properties to + be got, set, and signals emitted to notify of changes to the property + values."/> + <annotation name="org.gtk.GDBus.DocString.Short" + value="Standard property getter/setter interface"/> + + <method name="Get"> + <annotation name="org.gtk.GDBus.DocString" value="Retrieves the value of + the property at @property_name on @interface_name on this object. If + @interface_name is an empty string, all interfaces will be searched + for @property_name; if multiple properties match, the result is + undefined. + + If @interface_name or @property_name do not exist, a + #org.freedesktop.DBus.Error.InvalidArgs error is returned."/> + + <arg type="s" name="interface_name" direction="in"> + <annotation name="org.gtk.GDBus.DocString" + value="Name of the interface the property is defined on."/> + </arg> + + <arg type="s" name="property_name" direction="in"> + <annotation name="org.gtk.GDBus.DocString" + value="Name of the property to get."/> + </arg> + + <arg type="v" name="value" direction="out"> + <annotation name="org.gtk.GDBus.DocString" + value="Property value, wrapped in a variant."/> + </arg> + </method> + + <signal name="PropertiesChanged"> + <annotation name="org.gtk.GDBus.DocString" value="Emitted when one or + more properties change values on @interface_name. A property may be + listed in @changed_properties or @invalidated_properties depending on + whether the service wants to broadcast the property’s new value. If a + value is large or infrequently used, the service might not want to + broadcast it, and will wait for clients to request it instead."/> + + <arg type="s" name="interface_name"> + <annotation name="org.gtk.GDBus.DocString" + value="Name of the interface the properties changed on."/> + </arg> + + <arg type="a{sv}" name="changed_properties"> + <annotation name="org.gtk.GDBus.DocString" + value="Map of property name to updated value for the changed + properties."/> + </arg> + + <arg type="as" name="invalidated_properties"> + <annotation name="org.gtk.GDBus.DocString" + value="List of names of other properties which have changed, but + whose updated values are not notified."/> + </arg> + </signal> + </interface> + +[comment] + FIXME: This is only present to fix + $link[>>https://github.com/projectmallard/mallard-ducktype/issues/2]. + +== Service files + [id="service-files"] + +Each D-Bus service must install a $file(.service) file describing its service +name and the command to run to start the service. This allows for service +activation (see the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-starting-services](D-Bus +specification)). + +Service files must be named after the service’s well-known name, for example +file $file(com.example.MyService1.service) for well-known name +$code(com.example.MyService1). Files must be installed in +$file($var($$(datadir$))/dbus-1/services) for the session bus and +$file($var($$(datadir$))/dbus-1/system-services) for the system bus. Note, however, +that services on the system bus almost always need a +$link[>#security-policies](security policy) as well. + +== Security Policies + [id="security-policies"] + +At a high level, the D-Bus security model is: +[list] +* There is a system bus, and zero or more session buses. +* Any process may connect to the system bus. The system bus limits which can own + names or send method calls, and only processes running as privileged users can + receive unicast messages not addressed to them. Every process may receive + broadcasts. +* Each session bus has an owner (a user). Only its owner may connect; on + general-purpose Linux, a session bus is not treated as a privilege boundary, + so there is no further privilege separation between processes on it. + +Full coverage of securing D-Bus services is beyond the scope of this guide, +however there are some steps which you can take when designing an API to ease +security policy implementation. + +D-Bus security policies are written as XML files in +$file($var($$(sysconfdir$)/dbus-1/system.d)) and +$file($var($$(sysconfdir$)/dbus-1/session.d)) and use an allow/deny model, where +each message (method call, signal emission, etc.) can be allowed or denied +according to the sum of all policy rules which match it. Each $code(<allow>) or +$code(<deny>) rule in the policy should have the $code(own), +$code(send_destination) or $code(receive_sender) attribute set. + +When designing an API, bear in mind the need to write and install such a +security policy, and consider splitting up methods or providing more restricted +versions which accept constrained parameters, so that they can be exposed with +less restrictive security policies if needed by less trusted clients. + +Secondly, the default D-Bus security policy for the system bus is restrictive +enough to allow sensitive data, such as passwords, to be safely sent over the +bus in unicast messages (including unicast signals); so there is no need to +complicate APIs by implementing extra security. Note, however, that sensitive +data must $em(not) be sent in broadcast signals, as they can be seen by all +peers on the bus. The default policy for the session bus is not restrictive, but +it is typically not a security boundary. + +== Debugging + [id="debugging"] + +Debugging services running on D-Bus can be tricky, especially if they are +launched via service activation and hence in an environment out of your control. + +Three main tools are available: D-Bus Monitor, Bustle and D-Feet. + +=== D-Bus Monitor + [id="dbus-monitor"] + +$link[>>http://dbus.freedesktop.org/doc/dbus-monitor.1.html]($cmd(dbus-monitor)) +is a core D-Bus tool, and allows eavesdropping on the session or system bus, +printing all messages it sees. The messages may be filtered using a standard +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules](D-Bus +match rule) to make the stream more manageable. + +Previous versions of D-Bus have required the security policy for the system bus +to be manually relaxed to allow eavesdropping on all messages. This meant that +debugging it was difficult and insecure. The latest versions of D-Bus add +support for monitor-only connections for the root user, which means that +$cmd(dbus-monitor) can be run as root to painlessly monitor all messages on the +system bus without modifying its security policy. + +=== Bustle + [id="bustle"] + +$link[>>http://willthompson.co.uk/bustle/](Bustle) is a graphical version of +$cmd(dbus-monitor), with a UI focused on profiling D-Bus performance by plotting +messages on a timeline. It is ideal for finding bottlenecks in IPC performance +between a service and client. + +=== D-Feet + [id="d-feet"] + +$link[>>https://wiki.gnome.org/Apps/DFeet](D-Feet) is an introspection tool for +D-Bus, which displays all peers on the bus graphically, with their objects, +interfaces, methods, signals and properties listed for examination. It is useful +for debugging all kinds of issues related to presence of services on the bus +and the objects they expose. diff --git a/doc/dbus-daemon.1.xml.in b/doc/dbus-daemon.1.xml.in index 78f0fd08..59cc016f 100644 --- a/doc/dbus-daemon.1.xml.in +++ b/doc/dbus-daemon.1.xml.in @@ -809,6 +809,31 @@ Right now the default will be the security context of the bus itself.</para> <para>If two <associate> elements specify the same name, the element appearing later in the configuration file will be used.</para> +<itemizedlist remap='TP'> + + <listitem><para><emphasis remap='I'><apparmor></emphasis></para></listitem> + + +</itemizedlist> + +<para>The <apparmor> element is used to configure AppArmor mediation on +the bus. It can contain one attribute that specifies the mediation mode:</para> + +<literallayout remap='.nf'> + <apparmor mode="(enabled|disabled|required)"/> +</literallayout> <!-- .fi --> + +<para>The default mode is "enabled". In "enabled" mode, AppArmor mediation +will be performed if AppArmor support is available in the kernel. If it is not +available, dbus-daemon will start but AppArmor mediation will not occur. In +"disabled" mode, AppArmor mediation is disabled. In "required" mode, AppArmor +mediation will be enabled if AppArmor support is available, otherwise +dbus-daemon will refuse to start.</para> + +<para>The AppArmor mediation mode of the bus cannot be changed after the bus +starts. Modifying the mode in the configuration file and sending a SIGHUP +signal to the daemon has no effect on the mediation mode.</para> + </refsect1> <refsect1 id='selinux'><title>SELinux</title> @@ -876,6 +901,37 @@ itself will be used.</para> </refsect1> +<refsect1 id='apparmor'><title>AppArmor</title> +<para>The AppArmor confinement context is stored when applications connect to +the bus. The confinement context consists of a label and a confinement mode. +When a security decision is required, the daemon uses the confinement context +to query the AppArmor policy to determine if the action should be allowed or +denied and if the action should be audited.</para> + +<para>The daemon performs AppArmor security checks in three places.</para> + +<para>First, any time a message is routed from one connection to another +connection, the bus daemon will check permissions with the label of the first +connection as source, label and/or connection name of the second connection as +target, along with the bus name, the path name, the interface name, and the +member name. Reply messages, such as method_return and error messages, are +implicitly allowed if they are in response to a message that has already been +allowed.</para> + +<para>Second, any time a connection asks to own a name, the bus daemon will +check permissions with the label of the connection as source, the requested +name as target, along with the bus name.</para> + +<para>Third, any time a connection attempts to eavesdrop, the bus daemon will +check permissions with the label of the connection as the source, along with +the bus name.</para> + +<para>AppArmor rules for bus mediation are not stored in the bus configuration +files. They are stored in the application's AppArmor profile. Please see +<emphasis remap='I'>apparmor.d(5)</emphasis> for more details.</para> + +</refsect1> + <refsect1 id='debugging'><title>DEBUGGING</title> <para>If you're trying to figure out where your messages are going or why you aren't getting messages, there are several things you can try.</para> diff --git a/doc/dbus-specification.xml b/doc/dbus-specification.xml index 2f9fef8b..ce3929e2 100644 --- a/doc/dbus-specification.xml +++ b/doc/dbus-specification.xml @@ -6,8 +6,8 @@ <article id="index"> <articleinfo> <title>D-Bus Specification</title> - <releaseinfo>Version 0.25</releaseinfo> - <date>(not yet released)</date> + <releaseinfo>Version 0.26</releaseinfo> + <date>2015-02-19</date> <authorgroup> <author> <firstname>Havoc</firstname> @@ -72,10 +72,11 @@ <revhistory> <revision> <revnumber>0.26</revnumber> - <date>(not yet released)</date> - <authorinitials>n/a</authorinitials> + <date>2015-02-19</date> + <authorinitials>smcv, rh</authorinitials> <revremark> - see <ulink url='http://cgit.freedesktop.org/dbus/dbus/log/doc/dbus-specification.xml'>commit log</ulink> + GetConnectionCredentials can return LinuxSecurityLabel or + WindowsSID; add privileged BecomeMonitor method </revremark> </revision> <revision> @@ -6005,6 +6006,65 @@ this concept. On Unix, this is the process ID defined by POSIX.</entry> </row> + <row> + <entry>WindowsSID</entry> + <entry>STRING</entry> + <entry>The Windows security identifier in its string form, + e.g. "S-1-5-21-3623811015-3361044348-30300820-1013" for + a domain or local computer user or "S-1-5-18" for the + LOCAL_SYSTEM user</entry> + </row> + + <row> + <entry>LinuxSecurityLabel</entry> + <entry>ARRAY of BYTE</entry> + <entry> + <para>On Linux systems, the security label that would result + from the SO_PEERSEC getsockopt call. The array contains + the non-zero bytes of the security label in an unspecified + ASCII-compatible encoding<footnote> + <para>It could be ASCII or UTF-8, but could also be + ISO Latin-1 or any other encoding.</para> + </footnote>, followed by a single zero byte.</para> + <para> + For example, the SELinux context + <literal>system_u:system_r:init_t:s0</literal> + (a string of length 27) would be encoded as 28 bytes + ending with ':', 's', '0', '\x00'.<footnote> + <para>Note that this is not the same as the older + GetConnectionSELinuxContext method, which does + not append the zero byte. Always appending the + zero byte allows callers to read the string + from the message payload without copying.</para> + </footnote> + </para> + <para> + On SELinux systems this is the SELinux context, as output + by <literal>ps -Z</literal> or <literal>ls -Z</literal>. + Typical values might include + <literal>system_u:system_r:init_t:s0</literal>, + <literal>unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023</literal>, + or + <literal>unconfined_u:unconfined_r:chrome_sandbox_t:s0-s0:c0.c1023</literal>. + </para> + <para> + On Smack systems, this is the Smack label. + Typical values might include + <literal>_</literal>, <literal>*</literal>, + <literal>User</literal>, <literal>System</literal> + or <literal>System::Shared</literal>. + </para> + <para> + On AppArmor systems, this is the AppArmor context, + a composite string encoding the AppArmor label (one or more + profiles) and the enforcement mode. + Typical values might include <literal>unconfined</literal>, + <literal>/usr/bin/firefox (enforce)</literal> or + <literal>user1 (complain)</literal>. + </para> + </entry> + </row> + </tbody> </tgroup> </informaltable> diff --git a/doc/dbus-test-tool.1.xml.in b/doc/dbus-test-tool.1.xml.in new file mode 100644 index 00000000..091dee14 --- /dev/null +++ b/doc/dbus-test-tool.1.xml.in @@ -0,0 +1,325 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN" "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> +<refentry id="dbus-test-tool.1"> + <refentryinfo> + <copyright> + <year>2015</year> + <holder>Collabora Ltd.</holder> + </copyright> + <legalnotice> + <para>This man page is distributed under the same terms as + dbus-test-tool (GPL-2+). There is NO WARRANTY, to the extent + permitted by law.</para> + </legalnotice> + </refentryinfo> + + <refmeta> + <refentrytitle>dbus-test-tool</refentrytitle> + <manvolnum>1</manvolnum> + <refmiscinfo class="manual">User Commands</refmiscinfo> + <refmiscinfo class="source">D-Bus</refmiscinfo> + <refmiscinfo class="version">@DBUS_VERSION@</refmiscinfo> + </refmeta> + <refnamediv> + <refname>dbus-test-tool</refname> + <refpurpose>D-Bus traffic generator and test tool</refpurpose> + </refnamediv> + + <refsynopsisdiv id="synopsis"> + <cmdsynopsis> + <command>dbus-test-tool</command> + <arg choice="plain">black-hole</arg> + <group choice="opt"> + <arg choice="plain">--session</arg> + <arg choice="plain">--system</arg> + </group> + <arg choice="opt">--name=<replaceable>NAME</replaceable></arg> + <arg choice="opt">--no-read</arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>dbus-test-tool</command> + <arg choice="plain">echo</arg> + <group choice="opt"> + <arg choice="plain">--session</arg> + <arg choice="plain">--system</arg> + </group> + <arg choice="opt">--name=<replaceable>NAME</replaceable></arg> + <arg choice="opt">--sleep=<replaceable>MS</replaceable></arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>dbus-test-tool</command> + <arg choice="plain">spam</arg> + <group choice="opt"> + <arg choice="plain">--session</arg> + <arg choice="plain">--system</arg> + </group> + <arg choice="opt">--dest=<replaceable>NAME</replaceable></arg> + <arg choice="opt">--count=<replaceable>N</replaceable></arg> + <arg choice="opt">--flood</arg> + <arg choice="opt">--ignore-errors</arg> + <arg choice="opt">--messages-per-conn=<replaceable>N</replaceable></arg> + <arg choice="opt">--no-reply</arg> + <arg choice="opt">--queue=<replaceable>N</replaceable></arg> + <arg choice="opt">--seed=<replaceable>SEED</replaceable></arg> + <group choice="opt"> + <arg choice="plain">--string</arg> + <arg choice="plain">--bytes</arg> + <arg choice="plain">--empty</arg> + </group> + <group choice="opt"> + <arg choice="plain">--payload=<replaceable>S</replaceable></arg> + <arg choice="plain">--stdin</arg> + <arg choice="plain">--message-stdin</arg> + <arg choice="plain">--random-size</arg> + </group> + </cmdsynopsis> + </refsynopsisdiv> + + <refsect1 id="description"> + <title>DESCRIPTION</title> + <para><command>dbus-test-tool</command> is a multi-purpose tool + for debugging and profiling D-Bus.</para> + + <para><command>dbus-test-tool black-hole</command> + connects to D-Bus, optionally requests a name, then does not + reply to messages. It normally reads and discards messages from + its D-Bus socket, but can be configured to sleep forever without + reading.</para> + + <para><command>dbus-test-tool echo</command> + connects to D-Bus, optionally requests a name, then sends back an + empty reply to every method call, after an optional delay.</para> + + <para><command>dbus-test-tool spam</command> + connects to D-Bus and makes repeated method calls, + normally named <literal>com.example.Spam</literal>.</para> + </refsect1> + + <refsect1 id="options"> + <title>OPTIONS</title> + <refsect2> + <title>Common options</title> + <variablelist remap="TP"> + + <varlistentry> + <term><option>--session</option></term> + <listitem> + <para>Connect to the session bus. This is the default.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--system</option></term> + <listitem> + <para>Connect to the system bus.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect2> + + <refsect2> + <title>black-hole mode</title> + <variablelist remap="TP"> + + <varlistentry> + <term><option>--name=</option><replaceable>NAME</replaceable></term> + <listitem> + <para>Before proceeding, request ownership of the well-known + bus name <replaceable>NAME</replaceable>, for example + <literal>com.example.NoReply</literal>. By default, + no name is requested, and the tool can only be addressed by + a unique bus name such as <literal>:1.23</literal>.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--no-read</option></term> + <listitem> + <para>Do not read from the D-Bus socket.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect2> + + <refsect2> + <title>echo mode</title> + <variablelist remap="TP"> + + <varlistentry> + <term><option>--name=</option><replaceable>NAME</replaceable></term> + <listitem> + <para>Before proceeding, request ownership of the well-known + bus name <replaceable>NAME</replaceable>, for example + <literal>com.example.Echo</literal>. By default, + no name is requested, and the tool can only be addressed by + a unique bus name such as <literal>:1.23</literal>.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--sleep=</option><replaceable>MS</replaceable></term> + <listitem> + <para>Block for <replaceable>MS</replaceable> milliseconds + before replying to a method call.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect2> + + <refsect2> + <title>spam mode</title> + <variablelist remap="TP"> + + <varlistentry> + <term><option>--dest=</option><replaceable>NAME</replaceable></term> + <listitem> + <para>Send method calls to the well-known or unique + bus name <replaceable>NAME</replaceable>. + The default is the dbus-daemon, + <literal>org.freedesktop.DBus</literal>.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--count=</option><replaceable>N</replaceable></term> + <listitem> + <para>Send <replaceable>N</replaceable> method calls in total. + The default is 1.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--queue=</option><replaceable>N</replaceable></term> + <listitem> + <para>Send <replaceable>N</replaceable> method calls before + waiting for any replies, then send one new call per reply + received, keeping <replaceable>N</replaceable> method calls + "in flight" at all times until the number of messages specified + with the <option>--count</option> option have been sent. + The default is 1, unless <option>--flood</option> + is used.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--flood</option></term> + <listitem> + <para>Send all messages without waiting for a reply, + equivalent to <option>--queue</option> with an arbitrarily + large <replaceable>N</replaceable>.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--no-reply</option></term> + <listitem> + <para>Set the "no reply desired" flag on the messages. + This implies <option>--flood</option>, since it disables + the replies that would be used for a finite + <option>--queue</option> length.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--messages-per-conn=</option><replaceable>N</replaceable></term> + <listitem> + <para>If given, send <replaceable>N</replaceable> method calls + on the same connection, then disconnect and reconnect. + The default is to use the same connection for all method + calls.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--string</option></term> + <listitem> + <para>The payload of each message is a UTF-8 string. This is the + default. The actual string used is given by the + <option>--payload</option> or <option>--stdin</option> + option, defaulting to "hello, world!".</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--bytes</option></term> + <listitem> + <para>The payload of each message is a byte-array. + The actual bytes used are given by the + <option>--payload</option> or <option>--stdin</option> + option, defaulting to the ASCII encoding of + "hello, world!".</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--empty</option></term> + <listitem> + <para>The messages have no payload.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--payload=</option><replaceable>S</replaceable></term> + <listitem> + <para>Use <replaceable>S</replaceable> as the + <option>--string</option> or <option>--bytes</option> + in the messages. The default is "hello, world!".</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--stdin</option></term> + <listitem> + <para>Read from standard input until end-of-file is reached, + and use that as the <option>--string</option> or + <option>--bytes</option> in the messages.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--message-stdin</option></term> + <listitem> + <para>Read a complete binary D-Bus method call message from + standard input, and use that for each method call.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--random-size</option></term> + <listitem> + <para>Read whitespace-separated ASCII decimal numbers from + standard input, choose one at random for each message, + and send a message whose payload is a string of that + length.</para> + </listitem> + </varlistentry> + + <varlistentry> + <term><option>--seed=</option><replaceable>SEED</replaceable></term> + <listitem> + <para>Use <replaceable>SEED</replaceable> as the seed + for the pseudorandom number generator, to have somewhat + repeatable sequences of random messages.</para> + </listitem> + </varlistentry> + + </variablelist> + </refsect2> + </refsect1> + + <refsect1 id="bugs"> + <title>BUGS</title> + <para>Please send bug reports to the D-Bus bug tracker or mailing list. + See <ulink url="http://www.freedesktop.org/software/dbus/">http://www.freedesktop.org/software/dbus/</ulink>.</para> + </refsect1> + + <refsect1 id="see_also"> + <title>SEE ALSO</title> + <para><citerefentry><refentrytitle>dbus-send</refentrytitle><manvolnum>1</manvolnum></citerefentry></para> + </refsect1> +</refentry> diff --git a/test/Makefile.am b/test/Makefile.am index c2a55c9e..5d49b68e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -130,6 +130,10 @@ manual_dir_iter_SOURCES = manual-dir-iter.c manual_dir_iter_CPPFLAGS = $(static_cppflags) manual_dir_iter_LDADD = $(top_builddir)/dbus/libdbus-internal.la +manual_paths_SOURCES = manual-paths.c +manual_paths_CPPFLAGS = $(static_cppflags) +manual_paths_LDADD = $(top_builddir)/dbus/libdbus-internal.la + manual_tcp_SOURCES = manual-tcp.c manual_tcp_CPPFLAGS = $(static_cppflags) manual_tcp_LDADD = $(top_builddir)/dbus/libdbus-internal.la @@ -151,6 +155,10 @@ installable_manual_tests = \ manual-tcp \ $(NULL) +if DBUS_WIN +installable_manual_tests += manual-paths +endif + if DBUS_WITH_GLIB installable_tests += \ test-corrupt \ diff --git a/test/Makefile.in b/test/Makefile.in index 01a0879e..afad10f6 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -90,16 +90,17 @@ host_triplet = @host@ # or libdbus-testutils-internal and libdbus-internal - never both in the # same binary. @DBUS_WITH_DBUS_GLIB_TRUE@am__append_2 = libdbus-testutils.la -@DBUS_ENABLE_EMBEDDED_TESTS_FALSE@TESTS = $(am__EXEEXT_8) +@DBUS_ENABLE_EMBEDDED_TESTS_FALSE@TESTS = $(am__EXEEXT_9) @DBUS_ENABLE_EMBEDDED_TESTS_TRUE@TESTS = ../bus/test-bus$(EXEEXT) \ @DBUS_ENABLE_EMBEDDED_TESTS_TRUE@ ../dbus/test-dbus$(EXEEXT) \ @DBUS_ENABLE_EMBEDDED_TESTS_TRUE@ $(am__append_3) \ -@DBUS_ENABLE_EMBEDDED_TESTS_TRUE@ $(am__EXEEXT_8) +@DBUS_ENABLE_EMBEDDED_TESTS_TRUE@ $(am__EXEEXT_9) @DBUS_ENABLE_EMBEDDED_TESTS_TRUE@@DBUS_UNIX_TRUE@am__append_3 = ../bus/test-bus-launch-helper$(EXEEXT) \ @DBUS_ENABLE_EMBEDDED_TESTS_TRUE@@DBUS_UNIX_TRUE@ ../bus/test-bus-system$(EXEEXT) -noinst_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_6) -testexec_PROGRAMS = $(am__EXEEXT_7) -@DBUS_WITH_GLIB_TRUE@am__append_4 = \ +noinst_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_7) +testexec_PROGRAMS = $(am__EXEEXT_8) +@DBUS_WIN_TRUE@am__append_4 = manual-paths +@DBUS_WITH_GLIB_TRUE@am__append_5 = \ @DBUS_WITH_GLIB_TRUE@ test-corrupt \ @DBUS_WITH_GLIB_TRUE@ test-dbus-daemon \ @DBUS_WITH_GLIB_TRUE@ test-dbus-daemon-eavesdrop \ @@ -115,16 +116,16 @@ testexec_PROGRAMS = $(am__EXEEXT_7) @DBUS_WITH_GLIB_TRUE@ test-uid-permissions \ @DBUS_WITH_GLIB_TRUE@ $(NULL) -@DBUS_WITH_GLIB_TRUE@am__append_5 = \ +@DBUS_WITH_GLIB_TRUE@am__append_6 = \ @DBUS_WITH_GLIB_TRUE@ manual-authz \ @DBUS_WITH_GLIB_TRUE@ $(NULL) -@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_6 = $(installable_tests) @DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_7 = $(installable_tests) -@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_8 = $(installable_tests) $(installable_manual_tests) -@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_9 = $(installable_test_meta) \ +@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_8 = $(installable_tests) +@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_9 = $(installable_tests) $(installable_manual_tests) +@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_10 = $(installable_test_meta) \ @DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@ $(installable_test_meta_with_config) -@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_10 = $(installable_tests) $(installable_manual_tests) +@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__append_11 = $(installable_tests) $(installable_manual_tests) subdir = test DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ $(top_srcdir)/depcomp $(top_srcdir)/test-driver @@ -188,13 +189,14 @@ libdbus_testutils_la_OBJECTS = $(am_libdbus_testutils_la_OBJECTS) @DBUS_WITH_GLIB_TRUE@ test-uid-permissions$(EXEEXT) am__EXEEXT_3 = test-shell$(EXEEXT) test-printf$(EXEEXT) \ $(am__EXEEXT_2) -@DBUS_WITH_GLIB_TRUE@am__EXEEXT_4 = manual-authz$(EXEEXT) -am__EXEEXT_5 = manual-dir-iter$(EXEEXT) manual-tcp$(EXEEXT) \ - $(am__EXEEXT_4) -@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_6 = $(am__EXEEXT_3) \ -@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@ $(am__EXEEXT_5) -@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_7 = $(am__EXEEXT_3) \ -@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@ $(am__EXEEXT_5) +@DBUS_WIN_TRUE@am__EXEEXT_4 = manual-paths$(EXEEXT) +@DBUS_WITH_GLIB_TRUE@am__EXEEXT_5 = manual-authz$(EXEEXT) +am__EXEEXT_6 = manual-dir-iter$(EXEEXT) manual-tcp$(EXEEXT) \ + $(am__EXEEXT_4) $(am__EXEEXT_5) +@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_7 = $(am__EXEEXT_3) \ +@DBUS_ENABLE_INSTALLED_TESTS_FALSE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@ $(am__EXEEXT_6) +@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_8 = $(am__EXEEXT_3) \ +@DBUS_ENABLE_INSTALLED_TESTS_TRUE@@DBUS_ENABLE_MODULAR_TESTS_TRUE@ $(am__EXEEXT_6) am__installdirs = "$(DESTDIR)$(testexecdir)" \ "$(DESTDIR)$(testmetadir)" PROGRAMS = $(noinst_PROGRAMS) $(testexec_PROGRAMS) @@ -207,6 +209,9 @@ am_manual_dir_iter_OBJECTS = \ manual_dir_iter_OBJECTS = $(am_manual_dir_iter_OBJECTS) manual_dir_iter_DEPENDENCIES = \ $(top_builddir)/dbus/libdbus-internal.la +am_manual_paths_OBJECTS = manual_paths-manual-paths.$(OBJEXT) +manual_paths_OBJECTS = $(am_manual_paths_OBJECTS) +manual_paths_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la am_manual_tcp_OBJECTS = manual_tcp-manual-tcp.$(OBJEXT) manual_tcp_OBJECTS = $(am_manual_tcp_OBJECTS) manual_tcp_DEPENDENCIES = $(top_builddir)/dbus/libdbus-internal.la @@ -331,8 +336,9 @@ am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libdbus_testutils_internal_la_SOURCES) \ $(libdbus_testutils_la_SOURCES) $(manual_authz_SOURCES) \ - $(manual_dir_iter_SOURCES) $(manual_tcp_SOURCES) \ - $(test_corrupt_SOURCES) $(test_dbus_daemon_SOURCES) \ + $(manual_dir_iter_SOURCES) $(manual_paths_SOURCES) \ + $(manual_tcp_SOURCES) $(test_corrupt_SOURCES) \ + $(test_dbus_daemon_SOURCES) \ $(test_dbus_daemon_eavesdrop_SOURCES) test-exit.c \ $(test_fdpass_SOURCES) $(test_loopback_SOURCES) \ $(test_marshal_SOURCES) $(test_monitor_SOURCES) test-names.c \ @@ -345,8 +351,8 @@ SOURCES = $(libdbus_testutils_internal_la_SOURCES) \ DIST_SOURCES = $(am__libdbus_testutils_internal_la_SOURCES_DIST) \ $(am__libdbus_testutils_la_SOURCES_DIST) \ $(manual_authz_SOURCES) $(manual_dir_iter_SOURCES) \ - $(manual_tcp_SOURCES) $(test_corrupt_SOURCES) \ - $(test_dbus_daemon_SOURCES) \ + $(manual_paths_SOURCES) $(manual_tcp_SOURCES) \ + $(test_corrupt_SOURCES) $(test_dbus_daemon_SOURCES) \ $(test_dbus_daemon_eavesdrop_SOURCES) test-exit.c \ $(test_fdpass_SOURCES) $(test_loopback_SOURCES) \ $(test_marshal_SOURCES) $(test_monitor_SOURCES) test-names.c \ @@ -580,7 +586,7 @@ am__set_TESTS_bases = \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` RECHECK_LOGS = $(TEST_LOGS) -@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_8 = $(am__EXEEXT_3) +@DBUS_ENABLE_MODULAR_TESTS_TRUE@am__EXEEXT_9 = $(am__EXEEXT_3) TEST_SUITE_LOG = test-suite.log TEST_EXTENSIONS = @EXEEXT@ .test LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver @@ -631,6 +637,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -685,6 +693,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -774,6 +783,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -916,19 +926,22 @@ test_syslog_LDADD = libdbus-testutils-internal.la $(GLIB_LIBS) manual_dir_iter_SOURCES = manual-dir-iter.c manual_dir_iter_CPPFLAGS = $(static_cppflags) manual_dir_iter_LDADD = $(top_builddir)/dbus/libdbus-internal.la +manual_paths_SOURCES = manual-paths.c +manual_paths_CPPFLAGS = $(static_cppflags) +manual_paths_LDADD = $(top_builddir)/dbus/libdbus-internal.la manual_tcp_SOURCES = manual-tcp.c manual_tcp_CPPFLAGS = $(static_cppflags) manual_tcp_LDADD = $(top_builddir)/dbus/libdbus-internal.la EXTRA_DIST = dbus-test-runner $(in_data) $(static_data) testexecdir = $(libexecdir)/installed-tests/dbus testmetadir = $(datadir)/installed-tests/dbus -testmeta_DATA = $(am__append_9) -installable_tests = test-shell test-printf $(NULL) $(am__append_4) +testmeta_DATA = $(am__append_10) +installable_tests = test-shell test-printf $(NULL) $(am__append_5) installable_manual_tests = manual-dir-iter manual-tcp $(NULL) \ - $(am__append_5) + $(am__append_4) $(am__append_6) installable_test_meta = $(installable_tests:=.test) installable_test_meta_with_config = $(installable_tests:=_with_config.test) -installcheck_tests = $(am__append_7) +installcheck_tests = $(am__append_8) installcheck_environment = \ XDG_RUNTIME_DIR=@abs_top_builddir@/test/XDG_RUNTIME_DIR \ DBUS_TEST_DAEMON=$(DESTDIR)$(DBUS_DAEMONDIR)/dbus-daemon$(EXEEXT) \ @@ -1274,6 +1287,10 @@ manual-dir-iter$(EXEEXT): $(manual_dir_iter_OBJECTS) $(manual_dir_iter_DEPENDENC @rm -f manual-dir-iter$(EXEEXT) $(AM_V_CCLD)$(LINK) $(manual_dir_iter_OBJECTS) $(manual_dir_iter_LDADD) $(LIBS) +manual-paths$(EXEEXT): $(manual_paths_OBJECTS) $(manual_paths_DEPENDENCIES) $(EXTRA_manual_paths_DEPENDENCIES) + @rm -f manual-paths$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(manual_paths_OBJECTS) $(manual_paths_LDADD) $(LIBS) + manual-tcp$(EXEEXT): $(manual_tcp_OBJECTS) $(manual_tcp_DEPENDENCIES) $(EXTRA_manual_tcp_DEPENDENCIES) @rm -f manual-tcp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(manual_tcp_OBJECTS) $(manual_tcp_LDADD) $(LIBS) @@ -1389,6 +1406,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdbus_testutils_internal_la-test-utils.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manual_authz-manual-authz.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manual_dir_iter-manual-dir-iter.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manual_paths-manual-paths.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/manual_tcp-manual-tcp.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-exit.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-segfault.Po@am__quote@ @@ -1481,6 +1499,20 @@ manual_dir_iter-manual-dir-iter.obj: manual-dir-iter.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_dir_iter_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o manual_dir_iter-manual-dir-iter.obj `if test -f 'manual-dir-iter.c'; then $(CYGPATH_W) 'manual-dir-iter.c'; else $(CYGPATH_W) '$(srcdir)/manual-dir-iter.c'; fi` +manual_paths-manual-paths.o: manual-paths.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_paths_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT manual_paths-manual-paths.o -MD -MP -MF $(DEPDIR)/manual_paths-manual-paths.Tpo -c -o manual_paths-manual-paths.o `test -f 'manual-paths.c' || echo '$(srcdir)/'`manual-paths.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/manual_paths-manual-paths.Tpo $(DEPDIR)/manual_paths-manual-paths.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='manual-paths.c' object='manual_paths-manual-paths.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_paths_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o manual_paths-manual-paths.o `test -f 'manual-paths.c' || echo '$(srcdir)/'`manual-paths.c + +manual_paths-manual-paths.obj: manual-paths.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_paths_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT manual_paths-manual-paths.obj -MD -MP -MF $(DEPDIR)/manual_paths-manual-paths.Tpo -c -o manual_paths-manual-paths.obj `if test -f 'manual-paths.c'; then $(CYGPATH_W) 'manual-paths.c'; else $(CYGPATH_W) '$(srcdir)/manual-paths.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/manual_paths-manual-paths.Tpo $(DEPDIR)/manual_paths-manual-paths.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='manual-paths.c' object='manual_paths-manual-paths.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_paths_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o manual_paths-manual-paths.obj `if test -f 'manual-paths.c'; then $(CYGPATH_W) 'manual-paths.c'; else $(CYGPATH_W) '$(srcdir)/manual-paths.c'; fi` + manual_tcp-manual-tcp.o: manual-tcp.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(manual_tcp_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT manual_tcp-manual-tcp.o -MD -MP -MF $(DEPDIR)/manual_tcp-manual-tcp.Tpo -c -o manual_tcp-manual-tcp.o `test -f 'manual-tcp.c' || echo '$(srcdir)/'`manual-tcp.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/manual_tcp-manual-tcp.Tpo $(DEPDIR)/manual_tcp-manual-tcp.Po diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index 6b0e9b8a..26786fc2 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -28,6 +28,8 @@ #include "test-utils-glib.h" +#include <string.h> + #ifdef DBUS_UNIX # include <unistd.h> # include <sys/types.h> @@ -305,7 +307,8 @@ test_creds (Fixture *f, enum { SEEN_UNIX_USER = 1, SEEN_PID = 2, - SEEN_WINDOWS_SID = 4 + SEEN_WINDOWS_SID = 4, + SEEN_LINUX_SECURITY_LABEL = 8 } seen = 0; if (m == NULL) @@ -371,6 +374,28 @@ test_creds (Fixture *f, g_assert_not_reached (); #endif } + else if (g_strcmp0 (name, "WindowsSID") == 0) + { +#ifdef G_OS_WIN32 + gchar *sid; + guint32 result; + char *self_sid; + + g_assert (!(seen & SEEN_WINDOWS_SID)); + g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==, + DBUS_TYPE_STRING); + dbus_message_iter_get_basic (&var_iter, &sid); + g_message ("%s of this process is %s", name, sid); + if (_dbus_getsid (&self_sid, 0)) + { + g_assert_cmpstr (self_sid, ==, sid); + LocalFree(self_sid); + } + seen |= SEEN_WINDOWS_SID; +#else + g_assert_not_reached (); +#endif + } else if (g_strcmp0 (name, "ProcessID") == 0) { guint32 u32; @@ -389,6 +414,27 @@ test_creds (Fixture *f, #endif seen |= SEEN_PID; } + else if (g_strcmp0 (name, "LinuxSecurityLabel") == 0) + { +#ifdef __linux__ + gchar *label; + int len; + DBusMessageIter ay_iter; + + g_assert (!(seen & SEEN_LINUX_SECURITY_LABEL)); + g_assert_cmpuint (dbus_message_iter_get_arg_type (&var_iter), ==, + DBUS_TYPE_ARRAY); + dbus_message_iter_recurse (&var_iter, &ay_iter); + g_assert_cmpuint (dbus_message_iter_get_arg_type (&ay_iter), ==, + DBUS_TYPE_BYTE); + dbus_message_iter_get_fixed_array (&ay_iter, &label, &len); + g_message ("%s of this process is %s", name, label); + g_assert_cmpuint (strlen (label) + 1, ==, len); + seen |= SEEN_LINUX_SECURITY_LABEL; +#else + g_assert_not_reached (); +#endif + } dbus_message_iter_next (&arr_iter); } @@ -402,9 +448,7 @@ test_creds (Fixture *f, #endif #ifdef G_OS_WIN32 - /* FIXME: when implemented: g_assert (seen & SEEN_WINDOWS_SID); - */ #endif } diff --git a/test/fdpass.c b/test/fdpass.c index fa958da8..96f9427f 100644 --- a/test/fdpass.c +++ b/test/fdpass.c @@ -57,9 +57,11 @@ _DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS <= 253); /* Arbitrary; included here to avoid relying on the default. */ #define MAX_INCOMING_UNIX_FDS (MAX_MESSAGE_UNIX_FDS * 4) -/* Arbitrary, except that MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES must be +/* Arbitrary, except that MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES should be * less than the process's file descriptor limit. */ -#define SOME_MESSAGES 50 +#define SOME_MESSAGES 20 +/* To cover some situations on Linux we want this to be true. */ +_DBUS_STATIC_ASSERT (MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES > 256); /* Linux won't allow more than 253 fds per sendmsg(). */ #define TOO_MANY_FDS 255 @@ -809,6 +811,25 @@ main (int argc, { test_init (&argc, &argv); +#ifdef HAVE_GETRLIMIT + { + struct rlimit lim; + + if (getrlimit (RLIMIT_NOFILE, &lim) < 0) + g_error ("Failed to get RLIMIT_NOFILE limit: %s", g_strerror (errno)); + + if (lim.rlim_cur != RLIM_INFINITY && + /* only run if we have a fairly generous margin of error + * for stdout, stderr, duplicates, the D-Bus connection, etc. */ + lim.rlim_cur < 2 * MAX_MESSAGE_UNIX_FDS * SOME_MESSAGES) + { + g_message ("not enough RLIMIT_NOFILE to run this test"); + /* Autotools exit code for "all skipped" */ + return 77; + } + } +#endif + g_test_add ("/relay", Fixture, NULL, setup, test_relay, teardown); g_test_add ("/limit", Fixture, NULL, setup, diff --git a/test/manual-paths.c b/test/manual-paths.c new file mode 100644 index 00000000..4ce3ffc7 --- /dev/null +++ b/test/manual-paths.c @@ -0,0 +1,73 @@ +/* + * Simple manual paths check + * + * syntax: manual-paths + * +*/ + +#include "config.h" +#include "dbus/dbus-list.h" +#include "dbus/dbus-internals.h" +#include "dbus/dbus-sysdeps.h" + +#include <stdio.h> + +dbus_bool_t print_install_root() +{ + char runtime_prefix[1000]; + + if (!_dbus_get_install_root(runtime_prefix, sizeof(runtime_prefix))) + { + fprintf(stderr, "dbus_get_install_root() failed\n"); + return FALSE; + } + fprintf(stdout, "dbus_get_install_root() returned '%s'\n", runtime_prefix); + return TRUE; +} + +dbus_bool_t print_service_dirs() +{ + DBusList *dirs; + DBusList *link; + dirs = NULL; + + if (!_dbus_get_standard_session_servicedirs (&dirs)) + _dbus_assert_not_reached ("couldn't get standard dirs"); + + while ((link = _dbus_list_pop_first_link (&dirs))) + { + printf ("default service dir: %s\n", (char *)link->data); + dbus_free (link->data); + _dbus_list_free_link (link); + } + dbus_free (dirs); + return TRUE; +} + +dbus_bool_t print_replace_install_prefix(const char *s) +{ + const char *s2 = _dbus_replace_install_prefix(s); + if (!s2) + return FALSE; + + fprintf(stdout, "replaced '%s' by '%s'\n", s, s2); + return TRUE; +} + +int +main (int argc, char **argv) +{ + if (!print_install_root()) + return -1; + + if (!print_service_dirs()) + return -2; + + if (!print_replace_install_prefix(DBUS_BINDIR "/dbus-daemon")) + return -3; + + if (!print_replace_install_prefix("c:\\Windows\\System32\\testfile")) + return -4; + + return 0; +} diff --git a/test/name-test/Makefile.in b/test/name-test/Makefile.in index 62357838..0b6ba20a 100644 --- a/test/name-test/Makefile.in +++ b/test/name-test/Makefile.in @@ -410,6 +410,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -464,6 +466,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -553,6 +556,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ diff --git a/tools/Makefile.am b/tools/Makefile.am index 294bbc63..80025b82 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -29,7 +29,10 @@ endif dbus_send_SOURCES= \ dbus-print-message.c \ dbus-print-message.h \ - dbus-send.c + dbus-send.c \ + tool-common.c \ + tool-common.h \ + $(NULL) dbus_monitor_SOURCES = \ dbus-monitor.c \ diff --git a/tools/Makefile.in b/tools/Makefile.in index 2d8a1ea6..054d17a1 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -136,7 +136,7 @@ dbus_run_session_OBJECTS = $(am_dbus_run_session_OBJECTS) @DBUS_WIN_FALSE@dbus_run_session_DEPENDENCIES = \ @DBUS_WIN_FALSE@ $(top_builddir)/dbus/libdbus-1.la am_dbus_send_OBJECTS = dbus-print-message.$(OBJEXT) \ - dbus-send.$(OBJEXT) + dbus-send.$(OBJEXT) tool-common.$(OBJEXT) dbus_send_OBJECTS = $(am_dbus_send_OBJECTS) dbus_send_DEPENDENCIES = $(top_builddir)/dbus/libdbus-1.la am_dbus_test_tool_OBJECTS = dbus-echo.$(OBJEXT) dbus-spam.$(OBJEXT) \ @@ -245,6 +245,8 @@ ACLOCAL = @ACLOCAL@ ADT_LIBS = @ADT_LIBS@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_CFLAGS = @APPARMOR_CFLAGS@ +APPARMOR_LIBS = @APPARMOR_LIBS@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ @@ -299,6 +301,7 @@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOXYGEN = @DOXYGEN@ DSYMUTIL = @DSYMUTIL@ +DUCKTYPE = @DUCKTYPE@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -388,6 +391,7 @@ X_CFLAGS = @X_CFLAGS@ X_EXTRA_LIBS = @X_EXTRA_LIBS@ X_LIBS = @X_LIBS@ X_PRE_LIBS = @X_PRE_LIBS@ +YELP_BUILD = @YELP_BUILD@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ @@ -462,7 +466,10 @@ AM_LDFLAGS = @R_DYNAMIC_LDFLAG@ dbus_send_SOURCES = \ dbus-print-message.c \ dbus-print-message.h \ - dbus-send.c + dbus-send.c \ + tool-common.c \ + tool-common.h \ + $(NULL) dbus_monitor_SOURCES = \ dbus-monitor.c \ diff --git a/tools/dbus-monitor.c b/tools/dbus-monitor.c index ad776904..14ceae90 100644 --- a/tools/dbus-monitor.c +++ b/tools/dbus-monitor.c @@ -74,7 +74,7 @@ GetSystemTimeAsFileTime (LPFILETIME ftp) static int gettimeofday (struct timeval *__p, - void *__t) + void *__t) { union { unsigned long long ns100; /*time since 1 Jan 1601 in 100ns units */ @@ -91,8 +91,8 @@ gettimeofday (struct timeval *__p, static DBusHandlerResult monitor_filter_func (DBusConnection *connection, - DBusMessage *message, - void *user_data) + DBusMessage *message, + void *user_data) { print_message (message, FALSE); @@ -107,14 +107,6 @@ monitor_filter_func (DBusConnection *connection, return DBUS_HANDLER_RESULT_HANDLED; } -#ifdef __APPLE__ -#define PROFILE_TIMED_FORMAT "%s\t%lu\t%d" -#elif defined(__NetBSD__) -#include <inttypes.h> -#define PROFILE_TIMED_FORMAT "%s\t%" PRId64 "\t%d" -#else -#define PROFILE_TIMED_FORMAT "%s\t%lu\t%lu" -#endif #define TRAP_NULL_STRING(str) ((str) ? (str) : "<none>") typedef enum @@ -130,23 +122,30 @@ typedef enum } ProfileAttributeFlags; static void +profile_print_headers (void) +{ + printf ("#type\ttimestamp\tserial\tsender\tdestination\tpath\tinterface\tmember\n"); + printf ("#\t\t\t\t\tin_reply_to\n"); +} + +static void profile_print_with_attrs (const char *type, DBusMessage *message, struct timeval *t, ProfileAttributeFlags attrs) { - printf (PROFILE_TIMED_FORMAT, type, t->tv_sec, t->tv_usec); + printf ("%s\t%lu.%06lu", type, (unsigned long) t->tv_sec, (unsigned long) t->tv_usec); if (attrs & PROFILE_ATTRIBUTE_FLAG_SERIAL) printf ("\t%u", dbus_message_get_serial (message)); - if (attrs & PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL) - printf ("\t%u", dbus_message_get_reply_serial (message)); - if (attrs & PROFILE_ATTRIBUTE_FLAG_SENDER) printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_sender (message))); if (attrs & PROFILE_ATTRIBUTE_FLAG_DESTINATION) printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_destination (message))); + if (attrs & PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL) + printf ("\t%u", dbus_message_get_reply_serial (message)); + if (attrs & PROFILE_ATTRIBUTE_FLAG_PATH) printf ("\t%s", TRAP_NULL_STRING (dbus_message_get_path (message))); @@ -165,6 +164,7 @@ profile_print_with_attrs (const char *type, DBusMessage *message, static void print_message_profile (DBusMessage *message) { + static dbus_bool_t first = TRUE; struct timeval t; if (gettimeofday (&t, NULL) < 0) @@ -173,45 +173,56 @@ print_message_profile (DBusMessage *message) return; } + if (first) + { + profile_print_headers (); + first = FALSE; + } + switch (dbus_message_get_type (message)) { case DBUS_MESSAGE_TYPE_METHOD_CALL: - profile_print_with_attrs ("mc", message, &t, - PROFILE_ATTRIBUTE_FLAG_SERIAL | - PROFILE_ATTRIBUTE_FLAG_SENDER | - PROFILE_ATTRIBUTE_FLAG_PATH | - PROFILE_ATTRIBUTE_FLAG_INTERFACE | - PROFILE_ATTRIBUTE_FLAG_MEMBER); - break; + profile_print_with_attrs ("mc", message, &t, + PROFILE_ATTRIBUTE_FLAG_SERIAL | + PROFILE_ATTRIBUTE_FLAG_SENDER | + PROFILE_ATTRIBUTE_FLAG_DESTINATION | + PROFILE_ATTRIBUTE_FLAG_PATH | + PROFILE_ATTRIBUTE_FLAG_INTERFACE | + PROFILE_ATTRIBUTE_FLAG_MEMBER); + break; case DBUS_MESSAGE_TYPE_METHOD_RETURN: - profile_print_with_attrs ("mr", message, &t, - PROFILE_ATTRIBUTE_FLAG_SERIAL | - PROFILE_ATTRIBUTE_FLAG_DESTINATION | - PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL); - break; + profile_print_with_attrs ("mr", message, &t, + PROFILE_ATTRIBUTE_FLAG_SERIAL | + PROFILE_ATTRIBUTE_FLAG_SENDER | + PROFILE_ATTRIBUTE_FLAG_DESTINATION | + PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL); + break; case DBUS_MESSAGE_TYPE_ERROR: - profile_print_with_attrs ("err", message, &t, - PROFILE_ATTRIBUTE_FLAG_SERIAL | - PROFILE_ATTRIBUTE_FLAG_DESTINATION | - PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL); - break; + profile_print_with_attrs ("err", message, &t, + PROFILE_ATTRIBUTE_FLAG_SERIAL | + PROFILE_ATTRIBUTE_FLAG_SENDER | + PROFILE_ATTRIBUTE_FLAG_DESTINATION | + PROFILE_ATTRIBUTE_FLAG_REPLY_SERIAL); + break; case DBUS_MESSAGE_TYPE_SIGNAL: - profile_print_with_attrs ("sig", message, &t, - PROFILE_ATTRIBUTE_FLAG_SERIAL | - PROFILE_ATTRIBUTE_FLAG_PATH | - PROFILE_ATTRIBUTE_FLAG_INTERFACE | - PROFILE_ATTRIBUTE_FLAG_MEMBER); - break; + profile_print_with_attrs ("sig", message, &t, + PROFILE_ATTRIBUTE_FLAG_SERIAL | + PROFILE_ATTRIBUTE_FLAG_SENDER | + PROFILE_ATTRIBUTE_FLAG_DESTINATION | + PROFILE_ATTRIBUTE_FLAG_PATH | + PROFILE_ATTRIBUTE_FLAG_INTERFACE | + PROFILE_ATTRIBUTE_FLAG_MEMBER); + break; default: - printf (PROFILE_TIMED_FORMAT "\n", "tun", t.tv_sec, t.tv_usec); - break; + printf ("%s\t%lu.%06lu", "tun", (unsigned long) t.tv_sec, (unsigned long) t.tv_usec); + break; } } static DBusHandlerResult -profile_filter_func (DBusConnection *connection, - DBusMessage *message, - void *user_data) +profile_filter_func (DBusConnection *connection, + DBusMessage *message, + void *user_data) { print_message_profile (message); @@ -431,7 +442,7 @@ main (int argc, char *argv[]) usage (argv[0], 1); } else if (!strcmp (arg, "--help")) - usage (argv[0], 0); + usage (argv[0], 0); else if (!strcmp (arg, "--monitor")) { filter_func = monitor_filter_func; @@ -453,9 +464,9 @@ main (int argc, char *argv[]) binary_mode = BINARY_MODE_PCAP; } else if (!strcmp (arg, "--")) - continue; + continue; else if (arg[0] == '-') - usage (argv[0], 1); + usage (argv[0], 1); else { unsigned int filter_len; numFilters++; @@ -483,12 +494,12 @@ main (int argc, char *argv[]) if (connection) { if (!dbus_bus_register (connection, &error)) - { + { fprintf (stderr, "Failed to register connection to bus at %s: %s\n", - address, error.message); + address, error.message); dbus_error_free (&error); exit (1); - } + } } } else @@ -548,7 +559,7 @@ main (int argc, char *argv[]) dbus_bus_add_match (connection, filters[i] + offset, &error); } - if (dbus_error_is_set (&error)) + if (dbus_error_is_set (&error)) { fprintf (stderr, "Failed to setup match \"%s\": %s\n", filters[i], error.message); diff --git a/tools/dbus-print-message.c b/tools/dbus-print-message.c index 6f02ea38..216ebaeb 100644 --- a/tools/dbus-print-message.c +++ b/tools/dbus-print-message.c @@ -37,6 +37,8 @@ #include <stdlib.h> #include "config.h" +#include "tool-common.h" + static const char* type_to_name (int message_type) { @@ -65,7 +67,9 @@ indent (int depth) } static void -print_hex (unsigned char *bytes, unsigned int len, int depth) +print_hex (const unsigned char *bytes, + int len, + int depth) { unsigned int i, columns; @@ -110,47 +114,45 @@ print_hex (unsigned char *bytes, unsigned int len, int depth) static void print_ay (DBusMessageIter *iter, int depth) { - /* Not using DBusString because it's not public API. It's 2009, and I'm - * manually growing a string chunk by chunk. - */ - unsigned char *bytes = malloc (DEFAULT_SIZE + 1); - unsigned int len = 0; - unsigned int max = DEFAULT_SIZE; + /* True if every byte in the bytestring (so far) is printable + * ASCII, with one exception: the last byte is also allowed to be \0. */ dbus_bool_t all_ascii = TRUE; - int current_type; - - while ((current_type = dbus_message_iter_get_arg_type (iter)) - != DBUS_TYPE_INVALID) - { - unsigned char val; - - dbus_message_iter_get_basic (iter, &val); - bytes[len] = val; - len++; + const unsigned char *bytes; + int len; + int i; - if (val < 32 || val > 126) - all_ascii = FALSE; + dbus_message_iter_get_fixed_array (iter, &bytes, &len); - if (len == max) + for (i = 0; i < len; i++) + { + if ((bytes[i] < 32 || bytes[i] > 126) && + (i < len - 1 || bytes[i] != '\0')) { - max *= 2; - bytes = realloc (bytes, max + 1); + all_ascii = FALSE; + break; } - - dbus_message_iter_next (iter); } - if (all_ascii) + if (all_ascii && len > 0 && bytes[len - 1] == '\0') + { + printf ("array of bytes \"%s\" + \\0\n", bytes); + } + else if (all_ascii) { - bytes[len] = '\0'; - printf ("array of bytes \"%s\"\n", bytes); + unsigned char *copy = dbus_malloc (len + 1); + + if (copy == NULL) + tool_oom ("copying bytestring"); + + memcpy (copy, bytes, len); + copy[len] = '\0'; + printf ("array of bytes \"%s\"\n", copy); + dbus_free (copy); } else { print_hex (bytes, len, depth); } - - free (bytes); } static void |