diff options
-rw-r--r-- | ChangeLog | 27 | ||||
-rw-r--r-- | bus/bus.c | 84 | ||||
-rw-r--r-- | bus/config-parser.c | 70 | ||||
-rw-r--r-- | bus/config-parser.h | 6 | ||||
-rw-r--r-- | bus/system.conf.in | 20 | ||||
-rw-r--r-- | dbus/dbus-auth.c | 78 | ||||
-rw-r--r-- | dbus/dbus-auth.h | 2 | ||||
-rw-r--r-- | dbus/dbus-internals.c | 77 | ||||
-rw-r--r-- | dbus/dbus-internals.h | 7 | ||||
-rw-r--r-- | dbus/dbus-server-debug-pipe.c | 9 | ||||
-rw-r--r-- | dbus/dbus-server-protected.h | 2 | ||||
-rw-r--r-- | dbus/dbus-server-unix.c | 7 | ||||
-rw-r--r-- | dbus/dbus-server.c | 33 | ||||
-rw-r--r-- | dbus/dbus-server.h | 2 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.c | 151 | ||||
-rw-r--r-- | dbus/dbus-sysdeps.h | 6 | ||||
-rw-r--r-- | dbus/dbus-transport.c | 16 | ||||
-rw-r--r-- | dbus/dbus-transport.h | 3 | ||||
-rw-r--r-- | doc/config-file.txt | 10 |
19 files changed, 574 insertions, 36 deletions
@@ -1,3 +1,30 @@ +2003-04-01 Havoc Pennington <hp@pobox.com> + + * dbus/dbus-server.c (dbus_server_set_auth_mechanisms): new + function + + * dbus/dbus-auth.c (_dbus_auth_set_mechanisms): new + + * dbus/dbus-internals.c (_dbus_dup_string_array): new function + + * dbus/dbus-sysdeps.c (_dbus_listen_unix_socket): chmod the + socket 0777, and unlink any existing socket. + + * bus/bus.c (bus_context_new): change our UID/GID and fork if + the configuration file so specifies; set up auth mechanism + restrictions + + * bus/config-parser.c (bus_config_parser_content): add support + for <fork> option and fill in code for <auth> + + * bus/system.conf.in: add <fork/> to default configuration, + and limit auth mechanisms to EXTERNAL + + * doc/config-file.txt (Elements): add <fork> + + * dbus/dbus-sysdeps.c (_dbus_become_daemon): new function + (_dbus_change_identity): new function + 2003-03-31 Havoc Pennington <hp@redhat.com> * dbus/dbus-sysdeps.c (_dbus_connect_unix_socket) @@ -141,8 +141,15 @@ free_rule_list_func (void *data) static dbus_bool_t setup_server (BusContext *context, DBusServer *server, + char **auth_mechanisms, DBusError *error) -{ +{ + if (!dbus_server_set_auth_mechanisms (server, (const char**) auth_mechanisms)) + { + BUS_SET_OOM (error); + return FALSE; + } + dbus_server_set_new_connection_function (server, new_connection_callback, context, NULL); @@ -181,6 +188,10 @@ bus_context_new (const DBusString *config_file, BusConfigParser *parser; DBusString full_address; const char *service_dirs[] = { NULL, NULL }; + const char *user; + char **auth_mechanisms; + DBusList **auth_mechanisms_list; + int len; _DBUS_ASSERT_ERROR_IS_CLEAR (error); @@ -189,6 +200,7 @@ bus_context_new (const DBusString *config_file, parser = NULL; context = NULL; + auth_mechanisms = NULL; parser = bus_config_load (config_file, error); if (parser == NULL) @@ -202,6 +214,36 @@ bus_context_new (const DBusString *config_file, } context->refcount = 1; + + /* Build an array of auth mechanisms */ + + auth_mechanisms_list = bus_config_parser_get_mechanisms (parser); + len = _dbus_list_get_length (auth_mechanisms_list); + + if (len > 0) + { + int i; + + auth_mechanisms = dbus_new0 (char*, len + 1); + if (auth_mechanisms == NULL) + goto failed; + + 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; + link = _dbus_list_get_next_link (auth_mechanisms_list, link); + } + } + else + { + auth_mechanisms = NULL; + } + + /* Listen on our addresses */ addresses = bus_config_parser_get_addresses (parser); @@ -213,7 +255,7 @@ bus_context_new (const DBusString *config_file, server = dbus_server_listen (link->data, error); if (server == NULL) goto failed; - else if (!setup_server (context, server, error)) + else if (!setup_server (context, server, auth_mechanisms, error)) goto failed; if (!_dbus_list_append (&context->servers, server)) @@ -225,6 +267,31 @@ bus_context_new (const DBusString *config_file, link = _dbus_list_get_next_link (addresses, link); } + /* Here we change our credentials if required, + * as soon as we've set up our sockets + */ + user = bus_config_parser_get_user (parser); + if (user != NULL) + { + DBusCredentials creds; + DBusString u; + + _dbus_string_init_const (&u, user); + + 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; + } + /* We have to build the address backward, so that * <listen> later in the config file have priority */ @@ -265,6 +332,8 @@ bus_context_new (const DBusString *config_file, BUS_SET_OOM (error); goto failed; } + + /* Create activation subsystem */ context->activation = bus_activation_new (context, &full_address, service_dirs, error); @@ -306,12 +375,20 @@ bus_context_new (const DBusString *config_file, goto failed; } + /* Now become a daemon if appropriate */ + if (bus_config_parser_get_fork (parser)) + { + if (!_dbus_become_daemon (error)) + goto failed; + } + bus_config_parser_unref (parser); _dbus_string_free (&full_address); + dbus_free_string_array (auth_mechanisms); return context; - failed: + failed: if (parser != NULL) bus_config_parser_unref (parser); @@ -319,6 +396,7 @@ bus_context_new (const DBusString *config_file, bus_context_unref (context); _dbus_string_free (&full_address); + dbus_free_string_array (auth_mechanisms); return NULL; } diff --git a/bus/config-parser.c b/bus/config-parser.c index 39239888..dc3cb4d6 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -37,7 +37,8 @@ typedef enum ELEMENT_POLICY, ELEMENT_LIMIT, ELEMENT_ALLOW, - ELEMENT_DENY + ELEMENT_DENY, + ELEMENT_FORK } ElementType; typedef struct @@ -84,6 +85,10 @@ struct BusConfigParser char *user; /**< user to run as */ DBusList *listen_on; /**< List of addresses to listen to */ + + DBusList *mechanisms; /**< Auth mechanisms */ + + unsigned int fork : 1; /**< TRUE to fork into daemon mode */ }; static const char* @@ -111,6 +116,8 @@ element_type_to_name (ElementType type) return "allow"; case ELEMENT_DENY: return "deny"; + case ELEMENT_FORK: + return "fork"; } _dbus_assert_not_reached ("bad element type"); @@ -195,9 +202,15 @@ merge_included (BusConfigParser *parser, included->user = NULL; } + if (included->fork) + parser->fork = TRUE; + while ((link = _dbus_list_pop_first_link (&included->listen_on))) _dbus_list_append_link (&parser->listen_on, link); + while ((link = _dbus_list_pop_first_link (&included->mechanisms))) + _dbus_list_append_link (&parser->mechanisms, link); + return TRUE; } @@ -409,6 +422,21 @@ start_busconfig_child (BusConfigParser *parser, return TRUE; } + else if (strcmp (element_name, "fork") == 0) + { + if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_FORK) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + parser->fork = TRUE; + + return TRUE; + } else if (strcmp (element_name, "listen") == 0) { if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error)) @@ -422,6 +450,19 @@ start_busconfig_child (BusConfigParser *parser, return TRUE; } + else if (strcmp (element_name, "auth") == 0) + { + if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error)) + return FALSE; + + if (push_element (parser, ELEMENT_AUTH) == NULL) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + return FALSE; + } + + return TRUE; + } else if (strcmp (element_name, "include") == 0) { Element *e; @@ -654,6 +695,7 @@ bus_config_parser_end_element (BusConfigParser *parser, case ELEMENT_LIMIT: case ELEMENT_ALLOW: case ELEMENT_DENY: + case ELEMENT_FORK: break; } @@ -715,6 +757,7 @@ bus_config_parser_content (BusConfigParser *parser, case ELEMENT_LIMIT: case ELEMENT_ALLOW: case ELEMENT_DENY: + case ELEMENT_FORK: if (all_whitespace (content)) return TRUE; else @@ -800,8 +843,19 @@ bus_config_parser_content (BusConfigParser *parser, case ELEMENT_AUTH: { + char *s; + e->had_content = TRUE; - /* FIXME */ + + if (!_dbus_string_copy_data (content, &s)) + goto nomem; + + if (!_dbus_list_append (&parser->mechanisms, + s)) + { + dbus_free (s); + goto nomem; + } } break; } @@ -851,6 +905,18 @@ bus_config_parser_get_addresses (BusConfigParser *parser) return &parser->listen_on; } +DBusList** +bus_config_parser_get_mechanisms (BusConfigParser *parser) +{ + return &parser->mechanisms; +} + +dbus_bool_t +bus_config_parser_get_fork (BusConfigParser *parser) +{ + return parser->fork; +} + #ifdef DBUS_BUILD_TESTS #include <stdio.h> diff --git a/bus/config-parser.h b/bus/config-parser.h index 8c66fa63..101b4c6f 100644 --- a/bus/config-parser.h +++ b/bus/config-parser.h @@ -55,8 +55,10 @@ dbus_bool_t bus_config_parser_finished (BusConfigParser *parser, DBusError *error); /* Functions for extracting the parse results */ -const char* bus_config_parser_get_user (BusConfigParser *parser); -DBusList** bus_config_parser_get_addresses (BusConfigParser *parser); +const char* bus_config_parser_get_user (BusConfigParser *parser); +DBusList** bus_config_parser_get_addresses (BusConfigParser *parser); +DBusList** bus_config_parser_get_mechanisms (BusConfigParser *parser); +dbus_bool_t bus_config_parser_get_fork (BusConfigParser *parser); /* Loader functions (backended off one of the XML parsers). Returns a * finished ConfigParser. diff --git a/bus/system.conf.in b/bus/system.conf.in index fe4e049a..7752b576 100644 --- a/bus/system.conf.in +++ b/bus/system.conf.in @@ -2,13 +2,29 @@ Add a system-local.conf and edit that rather than changing this file directly. --> +<!-- Note that there are any number of ways you can hose yourself + security-wise by screwing up this file; in particular, you + probably don't want to listen on any more addresses, add any more + auth mechanisms, run as a different user, etc. --> + <!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"> <busconfig> - <user>fixme</user> + + <!-- Run as special user --> + <user>messagebus</user> + + <!-- Fork into daemon mode --> + <fork/> + + <!-- Only allow socket-credentials-based authentication --> + <auth>EXTERNAL</auth> + + <!-- Only listen on a local socket --> <listen>unix:path=@EXPANDED_LOCALSTATEDIR@/@DBUS_SYSTEM_SOCKET@</listen> + <policy context="default"> - <!-- Deny everything --> + <!-- Deny everything then punch holes --> <deny send="*"/> <deny receive="*"/> <deny own="*"/> diff --git a/dbus/dbus-auth.c b/dbus/dbus-auth.c index fd4f60dc..e687dd66 100644 --- a/dbus/dbus-auth.c +++ b/dbus/dbus-auth.c @@ -159,6 +159,10 @@ struct DBusAuth DBusKeyring *keyring; /**< Keyring for cookie mechanism. */ int cookie_id; /**< ID of cookie to use */ DBusString challenge; /**< Challenge sent to client */ + + char **allowed_mechs; /**< Mechanisms we're allowed to use, + * or #NULL if we can use any + */ unsigned int needed_memory : 1; /**< We needed memory to continue since last * successful getting something done @@ -1134,13 +1138,19 @@ all_mechanisms[] = { }; static const DBusAuthMechanismHandler* -find_mech (const DBusString *name) +find_mech (const DBusString *name, + char **allowed_mechs) { int i; + if (allowed_mechs != NULL && + !_dbus_string_array_contains ((const char**) allowed_mechs, + _dbus_string_get_const_data (name))) + return NULL; + i = 0; while (all_mechanisms[i].mechanism != NULL) - { + { if (_dbus_string_equal_c_str (name, all_mechanisms[i].mechanism)) @@ -1259,7 +1269,7 @@ process_auth (DBusAuth *auth, &decoded_response, 0)) goto failed; - auth->mech = find_mech (&mech); + auth->mech = find_mech (&mech, auth->allowed_mechs); if (auth->mech != NULL) { _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n", @@ -1418,7 +1428,7 @@ record_mechanisms (DBusAuth *auth, if (!get_word (args, &next, &m)) goto nomem; - mech = find_mech (&m); + mech = find_mech (&m, auth->allowed_mechs); if (mech != NULL) { @@ -1462,11 +1472,32 @@ client_try_next_mechanism (DBusAuth *auth) { const DBusAuthMechanismHandler *mech; DBusString auth_command; + DBusAuthClient *client; - if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL) - return FALSE; + client = DBUS_AUTH_CLIENT (auth); + + /* Pop any mechs not in the list of allowed mechanisms */ + mech = NULL; + while (client->mechs_to_try != NULL) + { + mech = client->mechs_to_try->data; - mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data; + if (auth->allowed_mechs != NULL && + !_dbus_string_array_contains ((const char**) auth->allowed_mechs, + mech->mechanism)) + { + /* don't try this one after all */ + _dbus_verbose ("Mechanism %s isn't in the list of allowed mechanisms\n", + mech->mechanism); + mech = NULL; + _dbus_list_pop_first (& client->mechs_to_try); + } + else + break; /* we'll try this one */ + } + + if (mech == NULL) + return FALSE; if (!_dbus_string_init (&auth_command)) return FALSE; @@ -1859,11 +1890,44 @@ _dbus_auth_unref (DBusAuth *auth) _dbus_string_free (&auth->identity); _dbus_string_free (&auth->incoming); _dbus_string_free (&auth->outgoing); + + dbus_free_string_array (auth->allowed_mechs); + dbus_free (auth); } } /** + * Sets an array of authentication mechanism names + * that we are willing to use. + * + * @param auth the auth conversation + * @param mechanisms #NULL-terminated array of mechanism names + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_auth_set_mechanisms (DBusAuth *auth, + const char **mechanisms) +{ + char **copy; + + if (mechanisms != NULL) + { + copy = _dbus_dup_string_array (mechanisms); + if (copy == NULL) + return FALSE; + } + else + copy = NULL; + + dbus_free_string_array (auth->allowed_mechs); + + auth->allowed_mechs = copy; + + return TRUE; +} + +/** * @param auth the auth conversation object * @returns #TRUE if we're in a final state */ diff --git a/dbus/dbus-auth.h b/dbus/dbus-auth.h index 8309fe33..98e4369d 100644 --- a/dbus/dbus-auth.h +++ b/dbus/dbus-auth.h @@ -46,6 +46,8 @@ DBusAuth* _dbus_auth_server_new (void); DBusAuth* _dbus_auth_client_new (void); void _dbus_auth_ref (DBusAuth *auth); void _dbus_auth_unref (DBusAuth *auth); +dbus_bool_t _dbus_auth_set_mechanisms (DBusAuth *auth, + const char **mechanisms); DBusAuthState _dbus_auth_do_work (DBusAuth *auth); dbus_bool_t _dbus_auth_get_bytes_to_send (DBusAuth *auth, const DBusString **str); diff --git a/dbus/dbus-internals.c b/dbus/dbus-internals.c index 7c11b9f5..f1fbf963 100644 --- a/dbus/dbus-internals.c +++ b/dbus/dbus-internals.c @@ -107,18 +107,6 @@ * * Maximum value of type "int" */ -/** - * @def _DBUS_MAX_SUN_PATH_LENGTH - * - * Maximum length of the path to a UNIX domain socket, - * sockaddr_un::sun_path member. POSIX requires that all systems - * support at least 100 bytes here, including the nul termination. - * We use 99 for the max value to allow for the nul. - * - * We could probably also do sizeof (addr.sun_path) - * but this way we are the same on all platforms - * which is probably a good idea. - */ /** * @typedef DBusForeachFunction @@ -251,6 +239,71 @@ _dbus_strdup (const char *str) } /** + * Duplicates a string array. Result may be freed with + * dbus_free_string_array(). Returns #NULL if memory allocation fails. + * If the array to be duplicated is #NULL, returns #NULL. + * + * @param array array to duplicate. + * @returns newly-allocated copy. + */ +char** +_dbus_dup_string_array (const char **array) +{ + int len; + int i; + char **copy; + + if (array == NULL) + return NULL; + + for (len = 0; array[len] != NULL; ++len) + ; + + copy = dbus_new0 (char*, len + 1); + if (copy == NULL) + return NULL; + + i = 0; + while (i < len) + { + copy[i] = _dbus_strdup (array[i]); + if (copy[i] == NULL) + { + dbus_free_string_array (copy); + return NULL; + } + + ++i; + } + + return copy; +} + +/** + * Checks whether a string array contains the given string. + * + * @param array array to search. + * @param str string to look for + * @returns #TRUE if array contains string + */ +dbus_bool_t +_dbus_string_array_contains (const char **array, + const char *str) +{ + int i; + + i = 0; + while (array[i] != NULL) + { + if (strcmp (array[i], str) == 0) + return TRUE; + ++i; + } + + return FALSE; +} + +/** * Returns a string describing the given type. * * @param type the type to describe diff --git a/dbus/dbus-internals.h b/dbus/dbus-internals.h index dcf01e48..7e4138c9 100644 --- a/dbus/dbus-internals.h +++ b/dbus/dbus-internals.h @@ -128,12 +128,15 @@ do { #define _DBUS_ALIGN_ADDRESS(this, boundary) \ ((void*)_DBUS_ALIGN_VALUE(this, boundary)) -char* _dbus_strdup (const char *str); +char* _dbus_strdup (const char *str); +dbus_bool_t _dbus_string_array_contains (const char **array, + const char *str); +char** _dbus_dup_string_array (const char **array); + #define _DBUS_INT_MIN (-_DBUS_INT_MAX - 1) #define _DBUS_INT_MAX 2147483647 #define _DBUS_UINT_MAX 0xffffffff -#define _DBUS_MAX_SUN_PATH_LENGTH 99 #define _DBUS_ONE_KILOBYTE 1024 #define _DBUS_ONE_MEGABYTE 1024 * _DBUS_ONE_KILOBYTE #define _DBUS_ONE_HOUR_IN_MILLISECONDS (1000 * 60 * 60) diff --git a/dbus/dbus-server-debug-pipe.c b/dbus/dbus-server-debug-pipe.c index 76f734b4..c6063220 100644 --- a/dbus/dbus-server-debug-pipe.c +++ b/dbus/dbus-server-debug-pipe.c @@ -283,6 +283,15 @@ _dbus_transport_debug_pipe_new (const char *server_name, server_fd = -1; + if (!_dbus_transport_set_auth_mechanisms (server_transport, + (const char**) server->auth_mechanisms)) + { + dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); + _dbus_transport_unref (server_transport); + _dbus_transport_unref (client_transport); + return FALSE; + } + connection = _dbus_connection_new_for_transport (server_transport); _dbus_transport_unref (server_transport); server_transport = NULL; diff --git a/dbus/dbus-server-protected.h b/dbus/dbus-server-protected.h index 475bf3a2..78872291 100644 --- a/dbus/dbus-server-protected.h +++ b/dbus/dbus-server-protected.h @@ -75,6 +75,8 @@ struct DBusServer /**< Callback to invoke to free new_connection_data * when server is finalized or data is replaced. */ + + char **auth_mechanisms; /**< Array of allowed authentication mechanisms */ unsigned int disconnected : 1; /**< TRUE if we are disconnected. */ }; diff --git a/dbus/dbus-server-unix.c b/dbus/dbus-server-unix.c index e5e6d70e..c718923f 100644 --- a/dbus/dbus-server-unix.c +++ b/dbus/dbus-server-unix.c @@ -90,6 +90,13 @@ handle_new_client_fd (DBusServer *server, return FALSE; } + if (!_dbus_transport_set_auth_mechanisms (transport, + (const char **) server->auth_mechanisms)) + { + _dbus_transport_unref (transport); + return FALSE; + } + /* note that client_fd is now owned by the transport, and will be * closed on transport disconnection/finalization */ diff --git a/dbus/dbus-server.c b/dbus/dbus-server.c index 9f70649b..be74ead0 100644 --- a/dbus/dbus-server.c +++ b/dbus/dbus-server.c @@ -143,6 +143,8 @@ _dbus_server_finalize_base (DBusServer *server) _dbus_counter_unref (server->connection_counter); dbus_free (server->address); + + dbus_free_string_array (server->auth_mechanisms); } /** @@ -600,6 +602,37 @@ dbus_server_handle_watch (DBusServer *server, } /** + * Sets the authentication mechanisms that this server offers + * to clients, as a list of SASL mechanisms. This function + * only affects connections created *after* it is called. + * Pass #NULL instead of an array to use all available mechanisms. + * + * @param server the server + * @param mechanisms #NULL-terminated array of mechanisms + * @returns #FALSE if no memory + */ +dbus_bool_t +dbus_server_set_auth_mechanisms (DBusServer *server, + const char **mechanisms) +{ + char **copy; + + if (mechanisms != NULL) + { + copy = _dbus_dup_string_array (mechanisms); + if (copy == NULL) + return FALSE; + } + else + copy = NULL; + + dbus_free_string_array (server->auth_mechanisms); + server->auth_mechanisms = copy; + + return TRUE; +} + +/** * Sets the maximum number of connections that can be open at one * time for this server. If the maximum is reached, and another * client tries to connect, then the oldest unauthenticated client diff --git a/dbus/dbus-server.h b/dbus/dbus-server.h index e36ed86e..152c7f97 100644 --- a/dbus/dbus-server.h +++ b/dbus/dbus-server.h @@ -70,6 +70,8 @@ void dbus_server_set_max_connections (DBusServer * int dbus_server_get_max_connections (DBusServer *server); int dbus_server_get_n_connections (DBusServer *server); +dbus_bool_t dbus_server_set_auth_mechanisms (DBusServer *server, + const char **mechanisms); int dbus_server_allocate_data_slot (void); void dbus_server_free_data_slot (int slot); diff --git a/dbus/dbus-sysdeps.c b/dbus/dbus-sysdeps.c index cab970a0..71863ef6 100644 --- a/dbus/dbus-sysdeps.c +++ b/dbus/dbus-sysdeps.c @@ -313,6 +313,21 @@ _dbus_write_two (int fd, #endif /* !HAVE_WRITEV */ } +#define _DBUS_MAX_SUN_PATH_LENGTH 99 + +/** + * @def _DBUS_MAX_SUN_PATH_LENGTH + * + * Maximum length of the path to a UNIX domain socket, + * sockaddr_un::sun_path member. POSIX requires that all systems + * support at least 100 bytes here, including the nul termination. + * We use 99 for the max value to allow for the nul. + * + * We could probably also do sizeof (addr.sun_path) + * but this way we are the same on all platforms + * which is probably a good idea. + */ + /** * Creates a socket and connects it to the UNIX domain socket at the * given path. The connection fd is returned, and is set up as @@ -345,8 +360,7 @@ _dbus_connect_unix_socket (const char *path, _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; - strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH); - addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH-1] = '\0'; + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); if (connect (fd, (struct sockaddr*) &addr, sizeof (addr)) < 0) { @@ -377,7 +391,13 @@ _dbus_connect_unix_socket (const char *path, /** * Creates a socket and binds it to the given path, * then listens on the socket. The socket is - * set to be nonblocking. + * set to be nonblocking. + * + * @todo we'd like to be able to use the abstract namespace on linux + * (see "man 7 unix"). The question is whether to silently move all + * paths into that namespace if we can (I think that's best) or to + * require it to be specified explicitly in the dbus address. Also, + * need to sort out how to check for abstract namespace support. * * @param path the socket name * @param error return location for errors @@ -402,10 +422,27 @@ _dbus_listen_unix_socket (const char *path, return -1; } + /* FIXME discussed security implications of this with Nalin, + * and we couldn't think of where it would kick our ass, but + * it still seems a bit sucky. It also has non-security suckage; + * really we'd prefer to exit if the socket is already in use. + * But there doesn't seem to be a good way to do this. + * + * Just to be extra careful, I threw in the stat() - clearly + * the stat() can't *fix* any security issue, but it probably + * makes it harder to exploit. + */ + { + struct stat sb; + + if (stat (path, &sb) == 0 && + S_ISSOCK (sb.st_mode)) + unlink (path); + } + _DBUS_ZERO (addr); addr.sun_family = AF_UNIX; - strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH); - addr.sun_path[_DBUS_MAX_SUN_PATH_LENGTH-1] = '\0'; + strncpy (addr.sun_path, path, _DBUS_MAX_SUN_PATH_LENGTH - 1); if (bind (listen_fd, (struct sockaddr*) &addr, SUN_LEN (&addr)) < 0) { @@ -431,6 +468,13 @@ _dbus_listen_unix_socket (const char *path, close (listen_fd); return -1; } + + /* Try opening up the permissions, but if we can't, just go ahead + * and continue, maybe it will be good enough. + */ + if (chmod (path, 0777) < 0) + _dbus_warn ("Could not set mode 0777 on socket %s\n", + path); return listen_fd; } @@ -3063,4 +3107,101 @@ _dbus_print_backtrace (void) #endif } +/** + * Does the chdir, fork, setsid, etc. to become a daemon process. + * + * @param error return location for errors + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_become_daemon (DBusError *error) +{ + const char *s; + + /* This is so we don't prevent unmounting of devices. We divert + * all messages to syslog + */ + if (chdir ("/") < 0) + { + dbus_set_error (error, DBUS_ERROR_FAILED, + "Could not chdir() to root directory"); + return FALSE; + } + + s = _dbus_getenv ("DBUS_DEBUG_OUTPUT"); + if (s == NULL || *s == '\0') + { + int dev_null_fd; + + /* silently ignore failures here, if someone + * doesn't have /dev/null we may as well try + * to continue anyhow + */ + + dev_null_fd = open ("/dev/null", O_RDWR); + if (dev_null_fd >= 0) + { + dup2 (dev_null_fd, 0); + dup2 (dev_null_fd, 1); + dup2 (dev_null_fd, 2); + } + } + + /* Get a predictable umask */ + umask (022); + + switch (fork ()) + { + case -1: + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to fork daemon: %s", _dbus_strerror (errno)); + return FALSE; + break; + + case 0: + break; + + default: + _exit (0); + break; + } + + if (setsid () == -1) + _dbus_assert_not_reached ("setsid() failed"); + + return TRUE; +} + +/** + * Changes the user and group the bus is running as. + * + * @param uid the new user ID + * @param gid the new group ID + * @param error return location for errors + * @returns #FALSE on failure + */ +dbus_bool_t +_dbus_change_identity (unsigned long uid, + unsigned long gid, + DBusError *error) +{ + if (setuid (uid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set UID to %lu: %s", uid, + _dbus_strerror (errno)); + return FALSE; + } + + if (setgid (gid) < 0) + { + dbus_set_error (error, _dbus_error_from_errno (errno), + "Failed to set GID to %lu: %s", gid, + _dbus_strerror (errno)); + return FALSE; + } + + return TRUE; +} + /** @} end of sysdeps */ diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index ac4e828a..6a6a965b 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -217,6 +217,12 @@ dbus_bool_t _dbus_close (int fd, void _dbus_print_backtrace (void); +dbus_bool_t _dbus_become_daemon (DBusError *error); + +dbus_bool_t _dbus_change_identity (unsigned long uid, + unsigned long gid, + DBusError *error); + DBUS_END_DECLS; #endif /* DBUS_SYSDEPS_H */ diff --git a/dbus/dbus-transport.c b/dbus/dbus-transport.c index 96bc6b64..e56c8b0c 100644 --- a/dbus/dbus-transport.c +++ b/dbus/dbus-transport.c @@ -907,4 +907,20 @@ _dbus_transport_set_unix_user_function (DBusTransport *transport, transport->free_unix_user_data = free_data_function; } +/** + * Sets the SASL authentication mechanisms supported by this transport. + * + * @param transport the transport + * @param mechanisms the #NULL-terminated array of mechanisms + * + * @returns #FALSE if no memory + */ +dbus_bool_t +_dbus_transport_set_auth_mechanisms (DBusTransport *transport, + const char **mechanisms) +{ + return _dbus_auth_set_mechanisms (transport->auth, mechanisms); +} + + /** @} */ diff --git a/dbus/dbus-transport.h b/dbus/dbus-transport.h index 128f0fe3..910642b7 100644 --- a/dbus/dbus-transport.h +++ b/dbus/dbus-transport.h @@ -64,7 +64,8 @@ void _dbus_transport_set_unix_user_function (DBusTransport DBusFreeFunction free_data_function, void **old_data, DBusFreeFunction *old_free_data_function); - +dbus_bool_t _dbus_transport_set_auth_mechanisms (DBusTransport *transport, + const char **mechanisms); diff --git a/doc/config-file.txt b/doc/config-file.txt index 5502c82b..b8230aab 100644 --- a/doc/config-file.txt +++ b/doc/config-file.txt @@ -53,6 +53,15 @@ Elements: The last <user> entry in the file "wins", the others are ignored. + The user is changed after the bus has completed initialization. + So sockets etc. will be created before changing user, but no + data will be read from clients before changing user. + + <fork> + + If present, the bus daemon becomes a real daemon (forks + into the background, etc.) + <listen> Add an address that the bus should listen on. The @@ -72,6 +81,7 @@ Elements: Lists permitted authorization mechanisms. If this element doesn't exist, then all known mechanisms are allowed. If there are multiple <auth> elements, the last one wins (they are not merged). + The order in which mechanisms are listed is not meaningful. Example: <auth>EXTERNAL</auth> Example: <auth>DBUS_COOKIE_SHA1</auth> |