Commit b6ffea17 authored by Havoc Pennington's avatar Havoc Pennington

2003-03-20 Havoc Pennington <hp@redhat.com>

	* dbus/dbus-connection.c (dbus_connection_set_unix_user_function):
	new function
	(dbus_connection_get_unix_user): new function
parent 056d76d8
2003-03-20 Havoc Pennington <hp@redhat.com>
* dbus/dbus-connection.c (dbus_connection_set_unix_user_function):
new function
(dbus_connection_get_unix_user): new function
2003-03-20 Havoc Pennington <hp@pobox.com> 2003-03-20 Havoc Pennington <hp@pobox.com>
* bus/connection.c (bus_connection_send_oom_error): assert that * bus/connection.c (bus_connection_send_oom_error): assert that
......
...@@ -27,16 +27,23 @@ ...@@ -27,16 +27,23 @@
#include "connection.h" #include "connection.h"
#include "services.h" #include "services.h"
#include "utils.h" #include "utils.h"
#include "policy.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-internals.h> #include <dbus/dbus-internals.h>
struct BusContext struct BusContext
{ {
int refcount; int refcount;
char *address; char *address;
DBusServer *server; DBusServer *server;
BusConnections *connections; BusConnections *connections;
BusActivation *activation; BusActivation *activation;
BusRegistry *registry; BusRegistry *registry;
DBusList *default_rules; /**< Default policy rules */
DBusList *override_rules; /**< Override policy rules */
DBusHashTable *rules_by_uid; /**< per-UID policy rules */
DBusHashTable *rules_by_gid; /**< per-GID policy rules */
}; };
static dbus_bool_t static dbus_bool_t
...@@ -109,6 +116,14 @@ new_connection_callback (DBusServer *server, ...@@ -109,6 +116,14 @@ new_connection_callback (DBusServer *server,
/* on OOM, we won't have ref'd the connection so it will die. */ /* on OOM, we won't have ref'd the connection so it will die. */
} }
static void
free_rule_func (void *data)
{
BusPolicyRule *rule = data;
bus_policy_rule_unref (rule);
}
BusContext* BusContext*
bus_context_new (const char *address, bus_context_new (const char *address,
const char **service_dirs, const char **service_dirs,
...@@ -164,6 +179,24 @@ bus_context_new (const char *address, ...@@ -164,6 +179,24 @@ bus_context_new (const char *address,
goto failed; goto failed;
} }
context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_INT,
NULL,
free_rule_func);
if (context->rules_by_uid == NULL)
{
BUS_SET_OOM (error);
goto failed;
}
context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_INT,
NULL,
free_rule_func);
if (context->rules_by_gid == NULL)
{
BUS_SET_OOM (error);
goto failed;
}
dbus_server_set_new_connection_function (context->server, dbus_server_set_new_connection_function (context->server,
new_connection_callback, new_connection_callback,
context, NULL); context, NULL);
...@@ -260,6 +293,18 @@ bus_context_unref (BusContext *context) ...@@ -260,6 +293,18 @@ bus_context_unref (BusContext *context)
dbus_server_unref (context->server); dbus_server_unref (context->server);
context->server = NULL; context->server = NULL;
} }
if (context->rules_by_uid)
{
_dbus_hash_table_unref (context->rules_by_uid);
context->rules_by_uid = NULL;
}
if (context->rules_by_gid)
{
_dbus_hash_table_unref (context->rules_by_gid);
context->rules_by_gid = NULL;
}
dbus_free (context->address); dbus_free (context->address);
dbus_free (context); dbus_free (context);
......
...@@ -2210,6 +2210,64 @@ dbus_connection_handle_watch (DBusConnection *connection, ...@@ -2210,6 +2210,64 @@ dbus_connection_handle_watch (DBusConnection *connection,
return retval; return retval;
} }
/**
* Gets the UNIX user ID of the connection if any.
* Returns #TRUE if the uid is filled in.
* Always returns #FALSE on non-UNIX platforms.
*
* @param connection the connection
* @param uid return location for the user ID
* @returns #TRUE if uid is filled in with a valid user ID
*/
dbus_bool_t
dbus_connection_get_unix_user (DBusConnection *connection,
unsigned long *uid)
{
dbus_bool_t result;
dbus_mutex_lock (connection->mutex);
result = _dbus_transport_get_unix_user (connection->transport,
uid);
dbus_mutex_unlock (connection->mutex);
return result;
}
/**
* Sets a predicate function used to determine whether a given user ID
* is allowed to connect. When an incoming connection has
* authenticated with a particular user ID, this function is called;
* if it returns #TRUE, the connection is allowed to proceed,
* otherwise the connection is disconnected.
*
* If the function is set to #NULL (as it is by default), then
* only the same UID as the server process will be allowed to
* connect.
*
* @param connection the connection
* @param function the predicate
* @param data data to pass to the predicate
* @param free_data_function function to free the data
*/
void
dbus_connection_set_unix_user_function (DBusConnection *connection,
DBusAllowUnixUserFunction function,
void *data,
DBusFreeFunction free_data_function)
{
void *old_data = NULL;
DBusFreeFunction old_free_function = NULL;
dbus_mutex_lock (connection->mutex);
_dbus_transport_set_unix_user_function (connection->transport,
function, data, free_data_function,
&old_data, &old_free_function);
dbus_mutex_unlock (connection->mutex);
if (old_free_function != NULL)
(* old_free_function) (old_data);
}
/** /**
* Adds a message filter. Filters are handlers that are run on * Adds a message filter. Filters are handlers that are run on
* all incoming messages, prior to the normal handlers * all incoming messages, prior to the normal handlers
......
...@@ -69,13 +69,16 @@ typedef void (* DBusWatchToggledFunction) (DBusWatch *watch, ...@@ -69,13 +69,16 @@ typedef void (* DBusWatchToggledFunction) (DBusWatch *watch,
void *data); void *data);
typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch, typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch,
void *data); void *data);
typedef void (* DBusWakeupMainFunction) (void *data);
typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout, typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout,
void *data); void *data);
typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout, typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout,
void *data); void *data);
typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout, typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout,
void *data); void *data);
typedef void (* DBusWakeupMainFunction) (void *data);
typedef dbus_bool_t (* DBusAllowUnixUserFunction) (DBusConnection *connection,
unsigned long uid,
void *data);
DBusConnection* dbus_connection_open (const char *address, DBusConnection* dbus_connection_open (const char *address,
DBusResultCode *result); DBusResultCode *result);
...@@ -123,7 +126,12 @@ void dbus_connection_set_wakeup_main_function (DBusConnection ...@@ -123,7 +126,12 @@ void dbus_connection_set_wakeup_main_function (DBusConnection
dbus_bool_t dbus_connection_handle_watch (DBusConnection *connection, dbus_bool_t dbus_connection_handle_watch (DBusConnection *connection,
DBusWatch *watch, DBusWatch *watch,
unsigned int condition); unsigned int condition);
dbus_bool_t dbus_connection_get_unix_user (DBusConnection *connection,
unsigned long *uid);
void dbus_connection_set_unix_user_function (DBusConnection *connection,
DBusAllowUnixUserFunction function,
void *data,
DBusFreeFunction free_data_function);
int dbus_watch_get_fd (DBusWatch *watch); int dbus_watch_get_fd (DBusWatch *watch);
unsigned int dbus_watch_get_flags (DBusWatch *watch); unsigned int dbus_watch_get_flags (DBusWatch *watch);
......
...@@ -651,6 +651,14 @@ _dbus_read_credentials_unix_socket (int client_fd, ...@@ -651,6 +651,14 @@ _dbus_read_credentials_unix_socket (int client_fd,
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem; struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
#endif #endif
/* The POSIX spec certainly doesn't promise this, but
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
_dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid));
_dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid));
_dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid));
credentials->pid = -1; credentials->pid = -1;
credentials->uid = -1; credentials->uid = -1;
credentials->gid = -1; credentials->gid = -1;
...@@ -1353,6 +1361,14 @@ _dbus_credentials_from_uid_string (const DBusString *uid_str, ...@@ -1353,6 +1361,14 @@ _dbus_credentials_from_uid_string (const DBusString *uid_str,
void void
_dbus_credentials_from_current_process (DBusCredentials *credentials) _dbus_credentials_from_current_process (DBusCredentials *credentials)
{ {
/* The POSIX spec certainly doesn't promise this, but
* we need these assertions to fail as soon as we're wrong about
* it so we can do the porting fixups
*/
_dbus_assert (sizeof (pid_t) <= sizeof (credentials->pid));
_dbus_assert (sizeof (uid_t) <= sizeof (credentials->uid));
_dbus_assert (sizeof (gid_t) <= sizeof (credentials->gid));
credentials->pid = getpid (); credentials->pid = getpid ();
credentials->uid = getuid (); credentials->uid = getuid ();
credentials->gid = getgid (); credentials->gid = getgid ();
......
...@@ -86,6 +86,11 @@ struct DBusTransport ...@@ -86,6 +86,11 @@ struct DBusTransport
long max_live_messages_size; /**< Max total size of received messages. */ long max_live_messages_size; /**< Max total size of received messages. */
DBusCounter *live_messages_size; /**< Counter for size of all live messages. */ DBusCounter *live_messages_size; /**< Counter for size of all live messages. */
DBusAllowUnixUserFunction unix_user_function; /**< Function for checking whether a user is authorized. */
void *unix_user_data; /**< Data for unix_user_function */
DBusFreeFunction free_unix_user_data; /**< Function to free unix_user_data */
unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */ unsigned int disconnected : 1; /**< #TRUE if we are disconnected. */
unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */ unsigned int authenticated : 1; /**< Cache of auth state; use _dbus_transport_get_is_authenticated() to query value */
......
...@@ -127,6 +127,10 @@ _dbus_transport_init_base (DBusTransport *transport, ...@@ -127,6 +127,10 @@ _dbus_transport_init_base (DBusTransport *transport,
transport->receive_credentials_pending = server; transport->receive_credentials_pending = server;
transport->is_server = server; transport->is_server = server;
transport->unix_user_function = NULL;
transport->unix_user_data = NULL;
transport->free_unix_user_data = NULL;
/* Try to default to something that won't totally hose the system, /* Try to default to something that won't totally hose the system,
* but doesn't impose too much of a limitation. * but doesn't impose too much of a limitation.
*/ */
...@@ -155,6 +159,9 @@ _dbus_transport_finalize_base (DBusTransport *transport) ...@@ -155,6 +159,9 @@ _dbus_transport_finalize_base (DBusTransport *transport)
{ {
if (!transport->disconnected) if (!transport->disconnected)
_dbus_transport_disconnect (transport); _dbus_transport_disconnect (transport);
if (transport->free_unix_user_data != NULL)
(* transport->free_unix_user_data) (transport->unix_user_data);
_dbus_message_loader_unref (transport->loader); _dbus_message_loader_unref (transport->loader);
_dbus_auth_unref (transport->auth); _dbus_auth_unref (transport->auth);
...@@ -334,6 +341,8 @@ _dbus_transport_get_is_connected (DBusTransport *transport) ...@@ -334,6 +341,8 @@ _dbus_transport_get_is_connected (DBusTransport *transport)
* Returns #TRUE if we have been authenticated. Will return #TRUE * Returns #TRUE if we have been authenticated. Will return #TRUE
* even if the transport is disconnected. * even if the transport is disconnected.
* *
* @todo needs to drop connection->mutex when calling the unix_user_function
*
* @param transport the transport * @param transport the transport
* @returns whether we're authenticated * @returns whether we're authenticated
*/ */
...@@ -363,23 +372,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport) ...@@ -363,23 +372,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
if (transport->authenticated && transport->is_server) if (transport->authenticated && transport->is_server)
{ {
DBusCredentials auth_identity; DBusCredentials auth_identity;
DBusCredentials our_identity;
_dbus_credentials_from_current_process (&our_identity);
_dbus_auth_get_identity (transport->auth, &auth_identity); _dbus_auth_get_identity (transport->auth, &auth_identity);
if (!_dbus_credentials_match (&our_identity, if (transport->unix_user_function != NULL)
&auth_identity))
{ {
_dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n", /* FIXME we hold the connection lock here and should drop it */
auth_identity.uid, our_identity.uid); if (!(* transport->unix_user_function) (transport->connection,
_dbus_transport_disconnect (transport); auth_identity.uid,
return FALSE; transport->unix_user_data))
{
_dbus_verbose ("Client UID %d was rejected, disconnecting\n",
auth_identity.uid);
_dbus_transport_disconnect (transport);
return FALSE;
}
else
{
_dbus_verbose ("Client UID %d authorized\n", auth_identity.uid);
}
} }
else else
{ {
_dbus_verbose ("Client authorized as UID %d matching our UID %d\n", DBusCredentials our_identity;
auth_identity.uid, our_identity.uid);
_dbus_credentials_from_current_process (&our_identity);
if (!_dbus_credentials_match (&our_identity,
&auth_identity))
{
_dbus_verbose ("Client authorized as UID %d but our UID is %d, disconnecting\n",
auth_identity.uid, our_identity.uid);
_dbus_transport_disconnect (transport);
return FALSE;
}
else
{
_dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
auth_identity.uid, our_identity.uid);
}
} }
} }
...@@ -737,4 +768,62 @@ _dbus_transport_get_max_live_messages_size (DBusTransport *transport) ...@@ -737,4 +768,62 @@ _dbus_transport_get_max_live_messages_size (DBusTransport *transport)
return transport->max_live_messages_size; return transport->max_live_messages_size;
} }
/**
* See dbus_connection_get_unix_user().
*
* @param transport the transport
* @param uid return location for the user ID
* @returns #TRUE if uid is filled in with a valid user ID
*/
dbus_bool_t
_dbus_transport_get_unix_user (DBusTransport *transport,
unsigned long *uid)
{
DBusCredentials auth_identity;
*uid = _DBUS_INT_MAX; /* better than some root or system user in
* case of bugs in the caller. Caller should
* never use this value on purpose, however.
*/
if (!transport->authenticated)
return FALSE;
_dbus_auth_get_identity (transport->auth, &auth_identity);
if (auth_identity.uid >= 0)
{
*uid = auth_identity.uid;
return TRUE;
}
else
return FALSE;
}
/**
* See dbus_connection_set_unix_user_function().
*
* @param transport the transport
* @param function the predicate
* @param data data to pass to the predicate
* @param free_data_function function to free the data
* @param old_data the old user data to be freed
* @param old_free_data_function old free data function to free it with
*/
void
_dbus_transport_set_unix_user_function (DBusTransport *transport,
DBusAllowUnixUserFunction function,
void *data,
DBusFreeFunction free_data_function,
void **old_data,
DBusFreeFunction *old_free_data_function)
{
*old_data = transport->unix_user_data;
*old_free_data_function = transport->free_unix_user_data;
transport->unix_user_function = function;
transport->unix_user_data = data;
transport->free_unix_user_data = free_data_function;
}
/** @} */ /** @} */
...@@ -30,31 +30,41 @@ DBUS_BEGIN_DECLS; ...@@ -30,31 +30,41 @@ DBUS_BEGIN_DECLS;
typedef struct DBusTransport DBusTransport; typedef struct DBusTransport DBusTransport;
DBusTransport* _dbus_transport_open (const char *address, DBusTransport* _dbus_transport_open (const char *address,
DBusResultCode *result); DBusResultCode *result);
void _dbus_transport_ref (DBusTransport *transport); void _dbus_transport_ref (DBusTransport *transport);
void _dbus_transport_unref (DBusTransport *transport); void _dbus_transport_unref (DBusTransport *transport);
void _dbus_transport_disconnect (DBusTransport *transport); void _dbus_transport_disconnect (DBusTransport *transport);
dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport); dbus_bool_t _dbus_transport_get_is_connected (DBusTransport *transport);
dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport); dbus_bool_t _dbus_transport_get_is_authenticated (DBusTransport *transport);
dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport, dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport,
DBusWatch *watch, DBusWatch *watch,
unsigned int condition); unsigned int condition);
dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport, dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport,
DBusConnection *connection); DBusConnection *connection);
void _dbus_transport_messages_pending (DBusTransport *transport, void _dbus_transport_messages_pending (DBusTransport *transport,
int queue_length); int queue_length);
void _dbus_transport_do_iteration (DBusTransport *transport, void _dbus_transport_do_iteration (DBusTransport *transport,
unsigned int flags, unsigned int flags,
int timeout_milliseconds); int timeout_milliseconds);
DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport); DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport);
dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport); dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport);
void _dbus_transport_set_max_message_size (DBusTransport *transport, void _dbus_transport_set_max_message_size (DBusTransport *transport,
long size); long size);
long _dbus_transport_get_max_message_size (DBusTransport *transport); long _dbus_transport_get_max_message_size (DBusTransport *transport);
void _dbus_transport_set_max_live_messages_size (DBusTransport *transport, void _dbus_transport_set_max_live_messages_size (DBusTransport *transport,
long size); long size);
long _dbus_transport_get_max_live_messages_size (DBusTransport *transport); long _dbus_transport_get_max_live_messages_size (DBusTransport *transport);
dbus_bool_t _dbus_transport_get_unix_user (DBusTransport *transport,
unsigned long *uid);
void _dbus_transport_set_unix_user_function (DBusTransport *transport,
DBusAllowUnixUserFunction function,
void *data,
DBusFreeFunction free_data_function,
void **old_data,
DBusFreeFunction *old_free_data_function);
DBUS_END_DECLS; DBUS_END_DECLS;
......
...@@ -94,6 +94,8 @@ Elements: ...@@ -94,6 +94,8 @@ Elements:
own="servicename" own="servicename"
send_to="servicename" send_to="servicename"
receive_from="servicename" receive_from="servicename"
user="username"
group="groupname"
Examples: Examples:
<deny send="org.freedesktop.System.Reboot"/> <deny send="org.freedesktop.System.Reboot"/>
...@@ -101,6 +103,8 @@ Elements: ...@@ -101,6 +103,8 @@ Elements:
<deny own="org.freedesktop.System"/> <deny own="org.freedesktop.System"/>
<deny send_to="org.freedesktop.System"/> <deny send_to="org.freedesktop.System"/>
<deny receive_from="org.freedesktop.System"/> <deny receive_from="org.freedesktop.System"/>
<deny user="john"/>
<deny group="enemies"/>
send_to and receive_from mean that messages may not be sent to send_to and receive_from mean that messages may not be sent to
or received from the *owner* of the given service, not that or received from the *owner* of the given service, not that
...@@ -108,24 +112,32 @@ Elements: ...@@ -108,24 +112,32 @@ Elements:
a connection owns services A, B, C, and sending to A is denied, a connection owns services A, B, C, and sending to A is denied,
sending to B or C will not work either. sending to B or C will not work either.
For "servicename" or "messagename" the character "*" can be user and group denials mean that the given user or group may
substituted, meaning "any." Complex globs like "foo.bar.*" aren't not connect to the message bus.
allowed for now because they'd be work to implement and maybe
encourage sloppy security anyway.
FIXME should we allow send/send_to and receive/receive_from For "servicename" or "messagename" or "username" or "groupname"
to both be specified, in which case they would be ANDed together? the character "*" can be substituted, meaning "any." Complex globs
like "foo.bar.*" aren't allowed for now because they'd be work to
implement and maybe encourage sloppy security anyway.
It does not make sense to deny a user or group inside a <policy>
for a user or group; user/group denials can only be inside
context="default" or context="required" policies.
A single <deny> rule may specify both send and send_to, OR both
receive and receive_from. In this case, the denial applies only if
both attributes match the message being denied.
e.g. <deny send="foo.bar" send_to="foo.blah"/> would deny e.g. <deny send="foo.bar" send_to="foo.blah"/> would deny
messages of the given name AND to the given service. messages of the given name AND to the given service.
Probably need to see how hard/slow all this will be to implement.
<allow> <allow>
send="messagename" send="messagename"
receive="messagename" receive="messagename"
own="servicename" own="servicename"
send_to="servicename" send_to="servicename"
receive_from="servicename" receive_from="servicename"
user="username"
group="groupname"
Makes an exception to previous <deny> statements. Works Makes an exception to previous <deny> statements. Works
just like <deny> but with the inverse meaning. just like <deny> but with the inverse meaning.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment