X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=bus%2Fbus.c;h=81d69b569721b583e71e49f761989ae4ebc3e7a5;hb=dbecdeabb20e0ce11121819c63373f0afba57c58;hp=43882c59e4b326814adebd6016632a1b406d2949;hpb=75742242000e782719bc1656f0a7da72b059e88e;p=platform%2Fupstream%2Fdbus.git diff --git a/bus/bus.c b/bus/bus.c index 43882c5..81d69b5 100644 --- a/bus/bus.c +++ b/bus/bus.c @@ -1,9 +1,9 @@ -/* -*- mode: C; c-file-style: "gnu" -*- */ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* bus.c message bus context object * - * Copyright (C) 2003 Red Hat, Inc. + * Copyright (C) 2003, 2004 Red Hat, Inc. * - * Licensed under the Academic Free License version 1.2 + * 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 @@ -17,10 +17,11 @@ * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ +#include #include "bus.h" #include "activation.h" #include "connection.h" @@ -29,16 +30,24 @@ #include "policy.h" #include "config-parser.h" #include "signals.h" +#include "selinux.h" +#include "dir-watch.h" #include #include +#include #include struct BusContext { int refcount; + DBusGUID uuid; + char *config_file; char *type; + char *servicehelper; char *address; char *pidfile; + char *user; + char *log_prefix; DBusLoop *loop; DBusList *servers; BusConnections *connections; @@ -46,8 +55,11 @@ struct BusContext BusRegistry *registry; BusPolicy *policy; BusMatchmaker *matchmaker; - DBusUserDatabase *user_database; BusLimits limits; + unsigned int fork : 1; + unsigned int syslog : 1; + unsigned int keep_umask : 1; + unsigned int allow_anonymous : 1; }; static dbus_int32_t server_data_slot = -1; @@ -173,7 +185,7 @@ new_connection_callback (DBusServer *server, * that we have to do this, people won't get it right * in general. */ - dbus_connection_disconnect (new_connection); + dbus_connection_close (new_connection); } dbus_connection_set_max_received_size (new_connection, @@ -181,7 +193,16 @@ new_connection_callback (DBusServer *server, dbus_connection_set_max_message_size (new_connection, context->limits.max_message_size); + + dbus_connection_set_max_received_unix_fds (new_connection, + context->limits.max_incoming_unix_fds); + + dbus_connection_set_max_message_unix_fds (new_connection, + context->limits.max_message_unix_fds); + dbus_connection_set_allow_anonymous (new_connection, + context->allow_anonymous); + /* on OOM, we won't have ref'd the connection so it will die. */ } @@ -202,9 +223,9 @@ setup_server (BusContext *context, BusServerData *bd; bd = dbus_new0 (BusServerData, 1); - if (!dbus_server_set_data (server, - server_data_slot, - bd, free_server_data)) + if (bd == NULL || !dbus_server_set_data (server, + server_data_slot, + bd, free_server_data)) { dbus_free (bd); BUS_SET_OOM (error); @@ -247,45 +268,28 @@ setup_server (BusContext *context, return TRUE; } -BusContext* -bus_context_new (const DBusString *config_file, - dbus_bool_t force_fork, - int print_addr_fd, - int print_pid_fd, - DBusError *error) +/* This code only gets executed the first time the + * config files are parsed. It is not executed + * when config files are reloaded. + */ +static dbus_bool_t +process_config_first_time_only (BusContext *context, + BusConfigParser *parser, + DBusError *error) { - BusContext *context; + DBusString log_prefix; DBusList *link; DBusList **addresses; - BusConfigParser *parser; - DBusString full_address; const char *user, *pidfile; char **auth_mechanisms; DBusList **auth_mechanisms_list; int len; - - _DBUS_ASSERT_ERROR_IS_CLEAR (error); + dbus_bool_t retval; - if (!_dbus_string_init (&full_address)) - { - BUS_SET_OOM (error); - return NULL; - } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - if (!dbus_server_allocate_data_slot (&server_data_slot)) - { - BUS_SET_OOM (error); - _dbus_string_free (&full_address); - return NULL; - } - - parser = NULL; - context = NULL; + retval = FALSE; auth_mechanisms = NULL; - - parser = bus_config_load (config_file, TRUE, error); - if (parser == NULL) - goto failed; /* Check for an existing pid file. Of course this is a race; * we'd have to use fcntl() locks on the pid file to @@ -297,55 +301,63 @@ bus_context_new (const DBusString *config_file, { DBusString u; DBusStat stbuf; - DBusError tmp_error; - dbus_error_init (&tmp_error); _dbus_string_init_const (&u, pidfile); - - if (_dbus_stat (&u, &stbuf, &tmp_error)) - { - dbus_set_error (error, DBUS_ERROR_FAILED, - "The pid file \"%s\" exists, if the message bus is not running, remove this file", - pidfile); - dbus_error_free (&tmp_error); - goto failed; - } + + if (_dbus_stat (&u, &stbuf, NULL)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "The pid file \"%s\" exists, if the message bus is not running, remove this file", + pidfile); + goto failed; + } } - - context = dbus_new0 (BusContext, 1); - if (context == NULL) + + /* keep around the pid filename so we can delete it later */ + context->pidfile = _dbus_strdup (pidfile); + + /* note that type may be NULL */ + context->type = _dbus_strdup (bus_config_parser_get_type (parser)); + if (bus_config_parser_get_type (parser) != NULL && context->type == NULL) + goto oom; + + user = bus_config_parser_get_user (parser); + if (user != NULL) { - BUS_SET_OOM (error); - goto failed; + context->user = _dbus_strdup (user); + if (context->user == NULL) + goto oom; } - - context->refcount = 1; - /* get our limits and timeout lengths */ - bus_config_parser_get_limits (parser, &context->limits); - - /* we need another ref of the server data slot for the context - * to own - */ - if (!dbus_server_allocate_data_slot (&server_data_slot)) - _dbus_assert_not_reached ("second ref of server data slot failed"); - - context->user_database = _dbus_user_database_new (); - if (context->user_database == NULL) + /* Set up the prefix for syslog messages */ + if (!_dbus_string_init (&log_prefix)) + goto oom; + if (context->type && !strcmp (context->type, "system")) { - BUS_SET_OOM (error); - goto failed; + if (!_dbus_string_append (&log_prefix, "[system] ")) + goto oom; } - - context->loop = _dbus_loop_new (); - if (context->loop == NULL) + else if (context->type && !strcmp (context->type, "session")) { - BUS_SET_OOM (error); - goto failed; + DBusCredentials *credentials; + + credentials = _dbus_credentials_new_from_current_process (); + if (!credentials) + goto oom; + if (!_dbus_string_append (&log_prefix, "[session ")) + goto oom; + if (!_dbus_credentials_to_string_append (credentials, &log_prefix)) + goto oom; + if (!_dbus_string_append (&log_prefix, "] ")) + goto oom; + _dbus_credentials_unref (credentials); } - + if (!_dbus_string_steal_data (&log_prefix, &context->log_prefix)) + goto oom; + _dbus_string_free (&log_prefix); + /* Build an array of auth mechanisms */ - + auth_mechanisms_list = bus_config_parser_get_mechanisms (parser); len = _dbus_list_get_length (auth_mechanisms_list); @@ -355,15 +367,15 @@ bus_context_new (const DBusString *config_file, auth_mechanisms = dbus_new0 (char*, len + 1); if (auth_mechanisms == NULL) - goto failed; - + goto oom; + i = 0; link = _dbus_list_get_first_link (auth_mechanisms_list); while (link != NULL) { auth_mechanisms[i] = _dbus_strdup (link->data); if (auth_mechanisms[i] == NULL) - goto failed; + goto oom; link = _dbus_list_get_next_link (auth_mechanisms_list, link); } } @@ -383,35 +395,87 @@ bus_context_new (const DBusString *config_file, server = dbus_server_listen (link->data, error); if (server == NULL) - goto failed; + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } else if (!setup_server (context, server, auth_mechanisms, error)) - goto failed; + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } if (!_dbus_list_append (&context->servers, server)) - { - BUS_SET_OOM (error); - goto failed; - } - + goto oom; + link = _dbus_list_get_next_link (addresses, link); } - /* note that type may be NULL */ - context->type = _dbus_strdup (bus_config_parser_get_type (parser)); - if (bus_config_parser_get_type (parser) != NULL && context->type == NULL) + context->fork = bus_config_parser_get_fork (parser); + context->syslog = bus_config_parser_get_syslog (parser); + context->keep_umask = bus_config_parser_get_keep_umask (parser); + context->allow_anonymous = bus_config_parser_get_allow_anonymous (parser); + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + retval = TRUE; + + failed: + dbus_free_string_array (auth_mechanisms); + return retval; + + oom: + BUS_SET_OOM (error); + dbus_free_string_array (auth_mechanisms); + return FALSE; +} + +/* This code gets executed every time the config files + * are parsed: both during BusContext construction + * and on reloads. This function is slightly screwy + * since it can do a "half reload" in out-of-memory + * situations. Realistically, unlikely to ever matter. + */ +static dbus_bool_t +process_config_every_time (BusContext *context, + BusConfigParser *parser, + dbus_bool_t is_reload, + DBusError *error) +{ + DBusString full_address; + DBusList *link; + DBusList **dirs; + BusActivation *new_activation; + char *addr; + const char *servicehelper; + char *s; + + dbus_bool_t retval; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + addr = NULL; + retval = FALSE; + + if (!_dbus_string_init (&full_address)) { BUS_SET_OOM (error); - goto failed; + return FALSE; } - + + /* get our limits and timeout lengths */ + bus_config_parser_get_limits (parser, &context->limits); + + if (context->policy) + bus_policy_unref (context->policy); + context->policy = bus_config_parser_steal_policy (parser); + _dbus_assert (context->policy != NULL); + /* We have to build the address backward, so that * later in the config file have priority */ link = _dbus_list_get_last_link (&context->servers); while (link != NULL) { - char *addr; - addr = dbus_server_get_address (link->data); if (addr == NULL) { @@ -435,22 +499,212 @@ bus_context_new (const DBusString *config_file, } dbus_free (addr); + addr = NULL; link = _dbus_list_get_prev_link (&context->servers, link); } + if (is_reload) + dbus_free (context->address); + if (!_dbus_string_copy_data (&full_address, &context->address)) { BUS_SET_OOM (error); goto failed; } - /* Note that we don't know whether the print_addr_fd is + /* get the service directories */ + dirs = bus_config_parser_get_service_dirs (parser); + + /* and the service helper */ + servicehelper = bus_config_parser_get_servicehelper (parser); + + s = _dbus_strdup(servicehelper); + if (s == NULL && servicehelper != NULL) + { + BUS_SET_OOM (error); + goto failed; + } + else + { + dbus_free(context->servicehelper); + context->servicehelper = s; + } + + /* Create activation subsystem */ + if (context->activation) + { + if (!bus_activation_reload (context->activation, &full_address, dirs, error)) + goto failed; + } + else + { + context->activation = bus_activation_new (context, &full_address, dirs, error); + } + + if (context->activation == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + retval = TRUE; + + failed: + _dbus_string_free (&full_address); + + if (addr) + dbus_free (addr); + + return retval; +} + +static dbus_bool_t +list_concat_new (DBusList **a, + DBusList **b, + DBusList **result) +{ + DBusList *link; + + *result = NULL; + + link = _dbus_list_get_first_link (a); + for (link = _dbus_list_get_first_link (a); link; link = _dbus_list_get_next_link (a, link)) + { + if (!_dbus_list_append (result, link->data)) + goto oom; + } + for (link = _dbus_list_get_first_link (b); link; link = _dbus_list_get_next_link (b, link)) + { + if (!_dbus_list_append (result, link->data)) + goto oom; + } + + return TRUE; +oom: + _dbus_list_clear (result); + return FALSE; +} + +static dbus_bool_t +process_config_postinit (BusContext *context, + BusConfigParser *parser, + DBusError *error) +{ + DBusHashTable *service_context_table; + DBusList *watched_dirs = NULL; + + service_context_table = bus_config_parser_steal_service_context_table (parser); + if (!bus_registry_set_service_context_table (context->registry, + service_context_table)) + { + BUS_SET_OOM (error); + return FALSE; + } + + _dbus_hash_table_unref (service_context_table); + + /* We need to monitor both the configuration directories and directories + * containing .service files. + */ + if (!list_concat_new (bus_config_parser_get_conf_dirs (parser), + bus_config_parser_get_service_dirs (parser), + &watched_dirs)) + { + BUS_SET_OOM (error); + return FALSE; + } + + bus_set_watched_dirs (context, &watched_dirs); + + _dbus_list_clear (&watched_dirs); + + return TRUE; +} + +BusContext* +bus_context_new (const DBusString *config_file, + ForceForkSetting force_fork, + DBusPipe *print_addr_pipe, + DBusPipe *print_pid_pipe, + DBusError *error) +{ + DBusString log_prefix; + BusContext *context; + BusConfigParser *parser; + + _DBUS_ASSERT_ERROR_IS_CLEAR (error); + + context = NULL; + parser = NULL; + + if (!dbus_server_allocate_data_slot (&server_data_slot)) + { + BUS_SET_OOM (error); + return NULL; + } + + context = dbus_new0 (BusContext, 1); + if (context == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + context->refcount = 1; + + _dbus_generate_uuid (&context->uuid); + + if (!_dbus_string_copy_data (config_file, &context->config_file)) + { + BUS_SET_OOM (error); + goto failed; + } + + context->loop = _dbus_loop_new (); + if (context->loop == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + + context->registry = bus_registry_new (context); + if (context->registry == NULL) + { + BUS_SET_OOM (error); + goto failed; + } + + parser = bus_config_load (config_file, TRUE, NULL, error); + if (parser == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + + if (!process_config_first_time_only (context, parser, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + if (!process_config_every_time (context, parser, FALSE, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + + /* we need another ref of the server data slot for the context + * to own + */ + if (!dbus_server_allocate_data_slot (&server_data_slot)) + _dbus_assert_not_reached ("second ref of server data slot failed"); + + /* Note that we don't know whether the print_addr_pipe is * one of the sockets we're using to listen on, or some * other random thing. But I think the answer is "don't do * that then" */ - if (print_addr_fd >= 0) + if (print_addr_pipe != NULL && _dbus_pipe_is_valid (print_addr_pipe)) { DBusString addr; const char *a = bus_context_get_address (context); @@ -472,32 +726,24 @@ bus_context_new (const DBusString *config_file, } bytes = _dbus_string_get_length (&addr); - if (_dbus_write (print_addr_fd, &addr, 0, bytes) != bytes) + if (_dbus_pipe_write (print_addr_pipe, &addr, 0, bytes, error) != bytes) { - dbus_set_error (error, DBUS_ERROR_FAILED, - "Printing message bus address: %s\n", - _dbus_strerror (errno)); + /* pipe write returns an error on failure but not short write */ + if (error != NULL && !dbus_error_is_set (error)) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Printing message bus address: did not write all bytes\n"); + } _dbus_string_free (&addr); goto failed; } - if (print_addr_fd > 2) - _dbus_close (print_addr_fd, NULL); - + if (!_dbus_pipe_is_stdout_or_stderr (print_addr_pipe)) + _dbus_pipe_close (print_addr_pipe, NULL); + _dbus_string_free (&addr); } - /* Create activation subsystem */ - - context->activation = bus_activation_new (context, &full_address, - bus_config_parser_get_service_dirs (parser), - error); - if (context->activation == NULL) - { - _DBUS_ASSERT_ERROR_IS_SET (error); - goto failed; - } - context->connections = bus_connections_new (context); if (context->connections == NULL) { @@ -505,115 +751,102 @@ bus_context_new (const DBusString *config_file, goto failed; } - context->registry = bus_registry_new (context); - if (context->registry == NULL) - { - BUS_SET_OOM (error); - goto failed; - } - context->matchmaker = bus_matchmaker_new (); if (context->matchmaker == NULL) { BUS_SET_OOM (error); goto failed; } - - context->policy = bus_config_parser_steal_policy (parser); - _dbus_assert (context->policy != NULL); - - /* Now become a daemon if appropriate */ - if (force_fork || bus_config_parser_get_fork (parser)) - { - DBusString u; - if (pidfile) - _dbus_string_init_const (&u, pidfile); - - if (!_dbus_become_daemon (pidfile ? &u : NULL, error)) - goto failed; - } - else + /* check user before we fork */ + if (context->user != NULL) { - /* Need to write PID file for ourselves, not for the child process */ - if (pidfile != NULL) + if (!_dbus_verify_daemon_user (context->user)) { - DBusString u; - - _dbus_string_init_const (&u, pidfile); - - if (!_dbus_write_pid_file (&u, _dbus_getpid (), error)) - goto failed; + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not get UID and GID for username \"%s\"", + context->user); + goto failed; } } - /* keep around the pid filename so we can delete it later */ - context->pidfile = _dbus_strdup (pidfile); + /* Now become a daemon if appropriate and write out pid file in any case */ + { + DBusString u; + + if (context->pidfile) + _dbus_string_init_const (&u, context->pidfile); + + if ((force_fork != FORK_NEVER && context->fork) || force_fork == FORK_ALWAYS) + { + _dbus_verbose ("Forking and becoming daemon\n"); + + if (!_dbus_become_daemon (context->pidfile ? &u : NULL, + print_pid_pipe, + error, + context->keep_umask)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + } + else + { + _dbus_verbose ("Fork not requested\n"); + + /* Need to write PID file and to PID pipe for ourselves, + * not for the child process. This is a no-op if the pidfile + * is NULL and print_pid_pipe is NULL. + */ + if (!_dbus_write_pid_to_file_and_pipe (context->pidfile ? &u : NULL, + print_pid_pipe, + _dbus_getpid (), + error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + } + } - /* Write PID if requested */ - if (print_pid_fd >= 0) - { - DBusString pid; - int bytes; + if (print_pid_pipe && _dbus_pipe_is_valid (print_pid_pipe) && + !_dbus_pipe_is_stdout_or_stderr (print_pid_pipe)) + _dbus_pipe_close (print_pid_pipe, NULL); - if (!_dbus_string_init (&pid)) - { - BUS_SET_OOM (error); - goto failed; - } - - if (!_dbus_string_append_int (&pid, _dbus_getpid ()) || - !_dbus_string_append (&pid, "\n")) - { - _dbus_string_free (&pid); - BUS_SET_OOM (error); - goto failed; - } + if (!bus_selinux_full_init ()) + { + bus_context_log (context, DBUS_SYSTEM_LOG_FATAL, "SELinux enabled but AVC initialization failed; check system log\n"); + } - bytes = _dbus_string_get_length (&pid); - if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes) - { - dbus_set_error (error, DBUS_ERROR_FAILED, - "Printing message bus PID: %s\n", - _dbus_strerror (errno)); - _dbus_string_free (&pid); - goto failed; - } + if (!process_config_postinit (context, parser, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } - if (print_pid_fd > 2) - _dbus_close (print_pid_fd, NULL); - - _dbus_string_free (&pid); + if (parser != NULL) + { + bus_config_parser_unref (parser); + parser = NULL; } /* Here we change our credentials if required, * as soon as we've set up our sockets and pidfile */ - user = bus_config_parser_get_user (parser); - if (user != NULL) + if (context->user != NULL) { - DBusCredentials creds; - DBusString u; - - _dbus_string_init_const (&u, user); + if (!_dbus_change_to_daemon_user (context->user, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } - if (!_dbus_credentials_from_username (&u, &creds) || - creds.uid < 0 || - creds.gid < 0) - { - dbus_set_error (error, DBUS_ERROR_FAILED, - "Could not get UID and GID for username \"%s\"", - user); - goto failed; - } - - if (!_dbus_change_identity (creds.uid, creds.gid, error)) - goto failed; +#ifdef HAVE_SELINUX + /* FIXME - why not just put this in full_init() below? */ + bus_selinux_audit_init (); +#endif } - - bus_config_parser_unref (parser); - _dbus_string_free (&full_address); - dbus_free_string_array (auth_mechanisms); + dbus_server_free_data_slot (&server_data_slot); return context; @@ -621,18 +854,63 @@ bus_context_new (const DBusString *config_file, failed: if (parser != NULL) bus_config_parser_unref (parser); - if (context != NULL) bus_context_unref (context); - _dbus_string_free (&full_address); - dbus_free_string_array (auth_mechanisms); - - dbus_server_free_data_slot (&server_data_slot); + if (server_data_slot >= 0) + dbus_server_free_data_slot (&server_data_slot); return NULL; } +dbus_bool_t +bus_context_get_id (BusContext *context, + DBusString *uuid) +{ + return _dbus_uuid_encode (&context->uuid, uuid); +} + +dbus_bool_t +bus_context_reload_config (BusContext *context, + DBusError *error) +{ + BusConfigParser *parser; + DBusString config_file; + dbus_bool_t ret; + + /* Flush the user database cache */ + _dbus_flush_caches (); + + ret = FALSE; + _dbus_string_init_const (&config_file, context->config_file); + parser = bus_config_load (&config_file, TRUE, NULL, error); + if (parser == NULL) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + + if (!process_config_every_time (context, parser, TRUE, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + if (!process_config_postinit (context, parser, error)) + { + _DBUS_ASSERT_ERROR_IS_SET (error); + goto failed; + } + ret = TRUE; + + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, "Reloaded configuration"); + failed: + if (!ret) + bus_context_log (context, DBUS_SYSTEM_LOG_INFO, "Unable to reload configuration: %s", error->message); + if (parser != NULL) + bus_config_parser_unref (parser); + return ret; +} + static void shutdown_server (BusContext *context, DBusServer *server) @@ -670,11 +948,13 @@ bus_context_shutdown (BusContext *context) } } -void +BusContext * bus_context_ref (BusContext *context) { _dbus_assert (context->refcount > 0); context->refcount += 1; + + return context; } void @@ -735,9 +1015,13 @@ bus_context_unref (BusContext *context) bus_matchmaker_unref (context->matchmaker); context->matchmaker = NULL; } - + + dbus_free (context->config_file); + dbus_free (context->log_prefix); dbus_free (context->type); dbus_free (context->address); + dbus_free (context->user); + dbus_free (context->servicehelper); if (context->pidfile) { @@ -751,9 +1035,6 @@ bus_context_unref (BusContext *context) dbus_free (context->pidfile); } - - _dbus_user_database_unref (context->user_database); - dbus_free (context); dbus_server_free_data_slot (&server_data_slot); @@ -773,6 +1054,12 @@ bus_context_get_address (BusContext *context) return context->address; } +const char* +bus_context_get_servicehelper (BusContext *context) +{ + return context->servicehelper; +} + BusRegistry* bus_context_get_registry (BusContext *context) { @@ -803,19 +1090,30 @@ bus_context_get_loop (BusContext *context) return context->loop; } -DBusUserDatabase* -bus_context_get_user_database (BusContext *context) +dbus_bool_t +bus_context_allow_unix_user (BusContext *context, + unsigned long uid) { - return context->user_database; + return bus_policy_allow_unix_user (context->policy, + uid); } +/* For now this is never actually called because the default + * DBusConnection behavior of 'same user that owns the bus can connect' + * is all it would do. + */ dbus_bool_t -bus_context_allow_user (BusContext *context, - unsigned long uid) +bus_context_allow_windows_user (BusContext *context, + const char *windows_sid) +{ + return bus_policy_allow_windows_user (context->policy, + windows_sid); +} + +BusPolicy * +bus_context_get_policy (BusContext *context) { - return bus_policy_allow_user (context->policy, - context->user_database, - uid); + return context->policy; } BusClientPolicy* @@ -889,6 +1187,38 @@ bus_context_get_reply_timeout (BusContext *context) return context->limits.reply_timeout; } +void +bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, ...) +{ + va_list args; + + if (!context->syslog) + return; + + va_start (args, msg); + + if (context->log_prefix) + { + DBusString full_msg; + + if (!_dbus_string_init (&full_msg)) + goto out; + if (!_dbus_string_append (&full_msg, context->log_prefix)) + goto oom_out; + if (!_dbus_string_append_printf_valist (&full_msg, msg, args)) + goto oom_out; + + _dbus_system_log (severity, "%s", full_msg); + oom_out: + _dbus_string_free (&full_msg); + } + else + _dbus_system_logv (severity, msg, args); + +out: + va_end (args); +} + /* * addressed_recipient is the recipient specified in the message. * @@ -911,20 +1241,44 @@ bus_context_check_security_policy (BusContext *context, DBusMessage *message, DBusError *error) { + const char *dest; BusClientPolicy *sender_policy; BusClientPolicy *recipient_policy; + dbus_int32_t toggles; + dbus_bool_t log; int type; dbus_bool_t requested_reply; + const char *sender_name; + const char *sender_loginfo; + const char *proposed_recipient_loginfo; type = dbus_message_get_type (message); + dest = dbus_message_get_destination (message); /* dispatch.c was supposed to ensure these invariants */ - /* FIXME this assertion is failing in make check */ - _dbus_assert (dbus_message_get_destination (message) != NULL || - type == DBUS_MESSAGE_TYPE_SIGNAL); + _dbus_assert (dest != NULL || + type == DBUS_MESSAGE_TYPE_SIGNAL || + (sender == NULL && !bus_connection_is_active (proposed_recipient))); _dbus_assert (type == DBUS_MESSAGE_TYPE_SIGNAL || addressed_recipient != NULL || - strcmp (dbus_message_get_destination (message), DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) == 0); + strcmp (dest, DBUS_SERVICE_DBUS) == 0); + + /* Used in logging below */ + if (sender != NULL) + { + sender_name = bus_connection_get_name (sender); + sender_loginfo = bus_connection_get_loginfo (sender); + } + else + { + sender_name = NULL; + sender_loginfo = "(bus)"; + } + + if (proposed_recipient != NULL) + proposed_recipient_loginfo = bus_connection_get_loginfo (proposed_recipient); + else + proposed_recipient_loginfo = "bus"; switch (type) { @@ -948,6 +1302,37 @@ 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)) + { + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, + "An SELinux policy prevents this sender " + "from sending this message to this recipient " + "(rejected message had sender \"%s\" interface \"%s\" " + "member \"%s\" error name \"%s\" destination \"%s\")", + sender_name ? sender_name : "(unset)", + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : "(unset)", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : "(unset)", + dbus_message_get_error_name (message) ? + dbus_message_get_error_name (message) : "(unset)", + dest ? dest : DBUS_SERVICE_DBUS); + _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); @@ -983,7 +1368,7 @@ bus_context_check_security_policy (BusContext *context, */ if (proposed_recipient == NULL && dbus_message_is_method_call (message, - DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, + DBUS_INTERFACE_DBUS, "Hello")) { _dbus_verbose ("security check allowing %s message\n", @@ -1047,85 +1432,145 @@ bus_context_check_security_policy (BusContext *context, (proposed_recipient != NULL && sender == NULL && recipient_policy == NULL) || (proposed_recipient == NULL && recipient_policy == NULL)); + log = FALSE; if (sender_policy && !bus_client_policy_check_can_send (sender_policy, - context->registry, proposed_recipient, - message)) + context->registry, + requested_reply, + proposed_recipient, + message, &toggles, &log)) { - const char *dest = dbus_message_get_destination (message); - dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, - "A security policy in place prevents this sender " - "from sending this message to this recipient, " - "see message bus configuration file (rejected message " - "had interface \"%s\" member \"%s\" error name \"%s\" destination \"%s\")", + const char *msg = "Rejected send message, %d matched rules; " + "type=\"%s\", sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" requested_reply=%d destination=\"%s\" (%s))"; + + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, msg, + toggles, + dbus_message_type_to_string (dbus_message_get_type (message)), + sender_name ? sender_name : "(unset)", + sender_loginfo, dbus_message_get_interface (message) ? dbus_message_get_interface (message) : "(unset)", dbus_message_get_member (message) ? dbus_message_get_member (message) : "(unset)", dbus_message_get_error_name (message) ? dbus_message_get_error_name (message) : "(unset)", - dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS); + requested_reply, + dest ? dest : DBUS_SERVICE_DBUS, + proposed_recipient_loginfo); + /* Needs to be duplicated to avoid calling malloc and having to handle OOM */ + if (addressed_recipient == proposed_recipient) + bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY, msg, + toggles, + dbus_message_type_to_string (dbus_message_get_type (message)), + sender_name ? sender_name : "(unset)", + sender_loginfo, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : "(unset)", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : "(unset)", + dbus_message_get_error_name (message) ? + dbus_message_get_error_name (message) : "(unset)", + requested_reply, + dest ? dest : DBUS_SERVICE_DBUS, + proposed_recipient_loginfo); _dbus_verbose ("security policy disallowing message due to sender policy\n"); return FALSE; } + if (log) + bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY, + "Would reject message, %d matched rules; " + "type=\"%s\", sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" requested_reply=%d destination=\"%s\" (%s))", + toggles, + dbus_message_type_to_string (dbus_message_get_type (message)), + sender_name ? sender_name : "(unset)", + sender_loginfo, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : "(unset)", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : "(unset)", + dbus_message_get_error_name (message) ? + dbus_message_get_error_name (message) : "(unset)", + requested_reply, + dest ? dest : DBUS_SERVICE_DBUS, + proposed_recipient_loginfo); + if (recipient_policy && !bus_client_policy_check_can_receive (recipient_policy, context->registry, requested_reply, sender, addressed_recipient, proposed_recipient, - message)) + message, &toggles)) { - const char *dest = dbus_message_get_destination (message); - dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, - "A security policy in place prevents this recipient " - "from receiving this message from this sender, " - "see message bus configuration file (rejected message " - "had interface \"%s\" member \"%s\" error name \"%s\" destination \"%s\" reply serial %u requested_reply=%d)", + const char *msg = "Rejected receive message, %d matched rules; " + "type=\"%s\" sender=\"%s\" (%s) interface=\"%s\" member=\"%s\" error name=\"%s\" reply serial=%u requested_reply=%d destination=\"%s\" (%s))"; + + dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED, msg, + toggles, + dbus_message_type_to_string (dbus_message_get_type (message)), + sender_name ? sender_name : "(unset)", + sender_loginfo, dbus_message_get_interface (message) ? dbus_message_get_interface (message) : "(unset)", dbus_message_get_member (message) ? dbus_message_get_member (message) : "(unset)", dbus_message_get_error_name (message) ? dbus_message_get_error_name (message) : "(unset)", - dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS, dbus_message_get_reply_serial (message), - requested_reply); + requested_reply, + dest ? dest : DBUS_SERVICE_DBUS, + proposed_recipient_loginfo); + /* Needs to be duplicated to avoid calling malloc and having to handle OOM */ + if (addressed_recipient == proposed_recipient) + bus_context_log (context, DBUS_SYSTEM_LOG_SECURITY, msg, + toggles, + dbus_message_type_to_string (dbus_message_get_type (message)), + sender_name ? sender_name : "(unset)", + sender_loginfo, + dbus_message_get_interface (message) ? + dbus_message_get_interface (message) : "(unset)", + dbus_message_get_member (message) ? + dbus_message_get_member (message) : "(unset)", + dbus_message_get_error_name (message) ? + dbus_message_get_error_name (message) : "(unset)", + dbus_message_get_reply_serial (message), + requested_reply, + dest ? dest : DBUS_SERVICE_DBUS, + proposed_recipient_loginfo); _dbus_verbose ("security policy disallowing message due to recipient policy\n"); return FALSE; } /* See if limits on size have been exceeded */ if (proposed_recipient && - dbus_connection_get_outgoing_size (proposed_recipient) > - context->limits.max_outgoing_bytes) + ((dbus_connection_get_outgoing_size (proposed_recipient) > context->limits.max_outgoing_bytes) || + (dbus_connection_get_outgoing_unix_fds (proposed_recipient) > context->limits.max_outgoing_unix_fds))) { - const char *dest = dbus_message_get_destination (message); dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED, "The destination service \"%s\" has a full message queue", dest ? dest : (proposed_recipient ? bus_connection_get_name (proposed_recipient) : - DBUS_SERVICE_ORG_FREEDESKTOP_DBUS)); + DBUS_SERVICE_DBUS)); _dbus_verbose ("security policy disallowing message due to full message queue\n"); return FALSE; } - if (type == DBUS_MESSAGE_TYPE_METHOD_CALL) + /* Record that we will allow a reply here in the future (don't + * bother if the recipient is the bus or this is an eavesdropping + * connection). Only the addressed recipient may reply. + */ + if (type == DBUS_MESSAGE_TYPE_METHOD_CALL && + sender && + addressed_recipient && + addressed_recipient == proposed_recipient && /* not eavesdropping */ + !bus_connections_expect_reply (bus_connection_get_connections (sender), + transaction, + sender, addressed_recipient, + message, error)) { - /* Record that we will allow a reply here in the future (don't - * bother if the recipient is the bus). Only the addressed recipient - * may reply. - */ - if (sender && addressed_recipient && - !bus_connections_expect_reply (bus_connection_get_connections (sender), - transaction, - sender, addressed_recipient, - message, error)) - { - _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n"); - return FALSE; - } + _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n"); + return FALSE; } _dbus_verbose ("security policy allowing message\n");