From 44ed933284589134603913b05f55ca55e8c5a566 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Tue, 1 Apr 2003 05:33:01 +0000 Subject: 2003-04-01 Havoc Pennington * 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 option and fill in code for * bus/system.conf.in: add to default configuration, and limit auth mechanisms to EXTERNAL * doc/config-file.txt (Elements): add * dbus/dbus-sysdeps.c (_dbus_become_daemon): new function (_dbus_change_identity): new function --- dbus/dbus-auth.c | 78 ++++++++++++++++++++-- dbus/dbus-auth.h | 2 + dbus/dbus-internals.c | 77 +++++++++++++++++---- dbus/dbus-internals.h | 7 +- dbus/dbus-server-debug-pipe.c | 9 +++ dbus/dbus-server-protected.h | 2 + dbus/dbus-server-unix.c | 7 ++ dbus/dbus-server.c | 33 +++++++++ dbus/dbus-server.h | 2 + dbus/dbus-sysdeps.c | 151 ++++++++++++++++++++++++++++++++++++++++-- dbus/dbus-sysdeps.h | 6 ++ dbus/dbus-transport.c | 16 +++++ dbus/dbus-transport.h | 3 +- 13 files changed, 366 insertions(+), 27 deletions(-) (limited to 'dbus') 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,10 +1890,43 @@ _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 @@ -250,6 +238,71 @@ _dbus_strdup (const char *str) return copy; } +/** + * 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. * 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); } /** @@ -599,6 +601,37 @@ dbus_server_handle_watch (DBusServer *server, return (* server->vtable->handle_watch) (server, watch, condition); } +/** + * 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 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); -- cgit