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>
* bus/connection.c (bus_connection_send_oom_error): assert that
......
......@@ -27,16 +27,23 @@
#include "connection.h"
#include "services.h"
#include "utils.h"
#include "policy.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-internals.h>
struct BusContext
{
int refcount;
char *address;
char *address;
DBusServer *server;
BusConnections *connections;
BusActivation *activation;
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
......@@ -109,6 +116,14 @@ new_connection_callback (DBusServer *server,
/* 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*
bus_context_new (const char *address,
const char **service_dirs,
......@@ -164,6 +179,24 @@ bus_context_new (const char *address,
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,
new_connection_callback,
context, NULL);
......@@ -260,6 +293,18 @@ bus_context_unref (BusContext *context)
dbus_server_unref (context->server);
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);
......
......@@ -2210,6 +2210,64 @@ dbus_connection_handle_watch (DBusConnection *connection,
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
* all incoming messages, prior to the normal handlers
......
......@@ -69,13 +69,16 @@ typedef void (* DBusWatchToggledFunction) (DBusWatch *watch,
void *data);
typedef void (* DBusRemoveWatchFunction) (DBusWatch *watch,
void *data);
typedef void (* DBusWakeupMainFunction) (void *data);
typedef dbus_bool_t (* DBusAddTimeoutFunction) (DBusTimeout *timeout,
void *data);
typedef void (* DBusTimeoutToggledFunction) (DBusTimeout *timeout,
void *data);
typedef void (* DBusRemoveTimeoutFunction) (DBusTimeout *timeout,
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,
DBusResultCode *result);
......@@ -123,7 +126,12 @@ void dbus_connection_set_wakeup_main_function (DBusConnection
dbus_bool_t dbus_connection_handle_watch (DBusConnection *connection,
DBusWatch *watch,
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);
unsigned int dbus_watch_get_flags (DBusWatch *watch);
......
......@@ -651,6 +651,14 @@ _dbus_read_credentials_unix_socket (int client_fd,
struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem;
#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->uid = -1;
credentials->gid = -1;
......@@ -1353,6 +1361,14 @@ _dbus_credentials_from_uid_string (const DBusString *uid_str,
void
_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->uid = getuid ();
credentials->gid = getgid ();
......
......@@ -86,6 +86,11 @@ struct DBusTransport
long max_live_messages_size; /**< Max total size of received 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 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,
transport->receive_credentials_pending = 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,
* but doesn't impose too much of a limitation.
*/
......@@ -155,6 +159,9 @@ _dbus_transport_finalize_base (DBusTransport *transport)
{
if (!transport->disconnected)
_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_auth_unref (transport->auth);
......@@ -334,6 +341,8 @@ _dbus_transport_get_is_connected (DBusTransport *transport)
* Returns #TRUE if we have been authenticated. Will return #TRUE
* even if the transport is disconnected.
*
* @todo needs to drop connection->mutex when calling the unix_user_function
*
* @param transport the transport
* @returns whether we're authenticated
*/
......@@ -363,23 +372,45 @@ _dbus_transport_get_is_authenticated (DBusTransport *transport)
if (transport->authenticated && transport->is_server)
{
DBusCredentials auth_identity;
DBusCredentials our_identity;
_dbus_credentials_from_current_process (&our_identity);
_dbus_auth_get_identity (transport->auth, &auth_identity);
if (!_dbus_credentials_match (&our_identity,
&auth_identity))
if (transport->unix_user_function != NULL)
{
_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;
/* FIXME we hold the connection lock here and should drop it */
if (!(* transport->unix_user_function) (transport->connection,
auth_identity.uid,
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
{
_dbus_verbose ("Client authorized as UID %d matching our UID %d\n",
auth_identity.uid, our_identity.uid);
DBusCredentials our_identity;
_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)
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;
typedef struct DBusTransport DBusTransport;
DBusTransport* _dbus_transport_open (const char *address,
DBusResultCode *result);
void _dbus_transport_ref (DBusTransport *transport);
void _dbus_transport_unref (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_authenticated (DBusTransport *transport);
dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport,
DBusWatch *watch,
unsigned int condition);
dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport,
DBusConnection *connection);
void _dbus_transport_messages_pending (DBusTransport *transport,
int queue_length);
void _dbus_transport_do_iteration (DBusTransport *transport,
unsigned int flags,
int timeout_milliseconds);
DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport);
dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport);
void _dbus_transport_set_max_message_size (DBusTransport *transport,
long size);
long _dbus_transport_get_max_message_size (DBusTransport *transport);
void _dbus_transport_set_max_live_messages_size (DBusTransport *transport,
long size);
long _dbus_transport_get_max_live_messages_size (DBusTransport *transport);
DBusTransport* _dbus_transport_open (const char *address,
DBusResultCode *result);
void _dbus_transport_ref (DBusTransport *transport);
void _dbus_transport_unref (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_authenticated (DBusTransport *transport);
dbus_bool_t _dbus_transport_handle_watch (DBusTransport *transport,
DBusWatch *watch,
unsigned int condition);
dbus_bool_t _dbus_transport_set_connection (DBusTransport *transport,
DBusConnection *connection);
void _dbus_transport_messages_pending (DBusTransport *transport,
int queue_length);
void _dbus_transport_do_iteration (DBusTransport *transport,
unsigned int flags,
int timeout_milliseconds);
DBusDispatchStatus _dbus_transport_get_dispatch_status (DBusTransport *transport);
dbus_bool_t _dbus_transport_queue_messages (DBusTransport *transport);
void _dbus_transport_set_max_message_size (DBusTransport *transport,
long size);
long _dbus_transport_get_max_message_size (DBusTransport *transport);
void _dbus_transport_set_max_live_messages_size (DBusTransport *transport,
long size);
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;
......
......@@ -94,6 +94,8 @@ Elements:
own="servicename"
send_to="servicename"
receive_from="servicename"
user="username"
group="groupname"
Examples:
<deny send="org.freedesktop.System.Reboot"/>
......@@ -101,6 +103,8 @@ Elements:
<deny own="org.freedesktop.System"/>
<deny send_to="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
or received from the *owner* of the given service, not that
......@@ -108,24 +112,32 @@ Elements:
a connection owns services A, B, C, and sending to A is denied,
sending to B or C will not work either.
For "servicename" or "messagename" 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.
user and group denials mean that the given user or group may
not connect to the message bus.
FIXME should we allow send/send_to and receive/receive_from
to both be specified, in which case they would be ANDed together?
For "servicename" or "messagename" or "username" or "groupname"
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
messages of the given name AND to the given service.
Probably need to see how hard/slow all this will be to implement.
<allow>
send="messagename"
receive="messagename"
own="servicename"
send_to="servicename"
receive_from="servicename"
user="username"
group="groupname"
Makes an exception to previous <deny> statements. Works
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