summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-04-01 05:33:01 +0000
committerHavoc Pennington <hp@redhat.com>2003-04-01 05:33:01 +0000
commit44ed933284589134603913b05f55ca55e8c5a566 (patch)
tree7091c28eba6a2d93cd02ca80c39b3175ccca06f5
parent8dfe82beb530aefce505a9bf915a749647e7183f (diff)
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
-rw-r--r--ChangeLog27
-rw-r--r--bus/bus.c84
-rw-r--r--bus/config-parser.c70
-rw-r--r--bus/config-parser.h6
-rw-r--r--bus/system.conf.in20
-rw-r--r--dbus/dbus-auth.c78
-rw-r--r--dbus/dbus-auth.h2
-rw-r--r--dbus/dbus-internals.c77
-rw-r--r--dbus/dbus-internals.h7
-rw-r--r--dbus/dbus-server-debug-pipe.c9
-rw-r--r--dbus/dbus-server-protected.h2
-rw-r--r--dbus/dbus-server-unix.c7
-rw-r--r--dbus/dbus-server.c33
-rw-r--r--dbus/dbus-server.h2
-rw-r--r--dbus/dbus-sysdeps.c151
-rw-r--r--dbus/dbus-sysdeps.h6
-rw-r--r--dbus/dbus-transport.c16
-rw-r--r--dbus/dbus-transport.h3
-rw-r--r--doc/config-file.txt10
19 files changed, 574 insertions, 36 deletions
diff --git a/ChangeLog b/ChangeLog
index 4e987f41..602341af 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)
diff --git a/bus/bus.c b/bus/bus.c
index ca9802c0..1ca4feb8 100644
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -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>