Use more fine grained access control

Make it possible to add more permissions to an object than just visible
or not. Pass these permissions to the client. This way we can make a
difference between being able to see and read, modify or query an
object. More permissions can be added later when needed. Because the
permissions is set on the resource by the access control module, the
implementations can check if the right permission is set before doing
anything.
This commit is contained in:
Wim Taymans 2017-08-01 17:09:57 +02:00
parent c59bc457d4
commit 4f08dbcd24
14 changed files with 110 additions and 59 deletions

View file

@ -328,7 +328,7 @@ static const struct pw_node_events node_events = {
};
static void registry_event_global(void *object, uint32_t id, uint32_t parent_id,
static void registry_event_global(void *object, uint32_t id, uint32_t parent_id, uint32_t permissions,
uint32_t type, uint32_t version)
{
struct pw_registry_proxy *registry = object;

View file

@ -211,14 +211,14 @@ check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_glo
if (global->owner == NULL)
return true;
if (global->owner->client->ucred.uid == client->ucred.uid)
if (global->owner->ucred.uid == client->ucred.uid)
return true;
return false;
}
static bool
do_global_filter(struct pw_global *global, struct pw_client *client, void *data)
static uint32_t
do_permission(struct pw_global *global, struct pw_client *client, void *data)
{
if (global->type == client->core->type.link) {
struct pw_link *link = global->object;
@ -226,16 +226,16 @@ do_global_filter(struct pw_global *global, struct pw_client *client, void *data)
/* we must be able to see both nodes */
if (link->output
&& !check_global_owner(client->core, client, link->output->node->global))
return false;
return 0;
if (link->input
&& !check_global_owner(client->core, client, link->input->node->global))
return false;
return 0;
}
else if (!check_global_owner(client->core, client, global))
return false;
return 0;
return true;
return PW_PERM_RWX;
}
static DBusHandlerResult
@ -691,8 +691,8 @@ static struct impl *module_new(struct pw_core *core, struct pw_properties *prope
pw_signal_add(&core->global_added, &impl->global_added, on_global_added);
pw_signal_add(&core->global_removed, &impl->global_removed, on_global_removed);
core->global_filter = do_global_filter;
core->global_filter_data = impl;
core->permission_func = do_permission;
core->permission_data = impl;
return impl;

View file

@ -234,7 +234,7 @@ static struct pw_client *client_new(struct listener *l, int fd)
pw_signal_add(&client->busy_changed, &this->busy_changed, on_busy_changed);
pw_global_bind(protocol->core->global, client, PW_VERSION_CORE, 0);
pw_global_bind(protocol->core->global, client, PW_PERM_RWX, PW_VERSION_CORE, 0);
return client;

View file

@ -529,7 +529,7 @@ static bool core_demarshal_update_types_server(void *object, void *data, size_t
return true;
}
static void registry_marshal_global(void *object, uint32_t id, uint32_t parent_id,
static void registry_marshal_global(void *object, uint32_t id, uint32_t parent_id, uint32_t permissions,
uint32_t type, uint32_t version)
{
struct pw_resource *resource = object;
@ -541,6 +541,7 @@ static void registry_marshal_global(void *object, uint32_t id, uint32_t parent_i
spa_pod_builder_struct(b, &f,
SPA_POD_TYPE_INT, id,
SPA_POD_TYPE_INT, parent_id,
SPA_POD_TYPE_INT, permissions,
SPA_POD_TYPE_ID, type,
SPA_POD_TYPE_INT, version);
@ -827,18 +828,19 @@ static bool registry_demarshal_global(void *object, void *data, size_t size)
{
struct pw_proxy *proxy = object;
struct spa_pod_iter it;
uint32_t id, parent_id, type, version;
uint32_t id, parent_id, permissions, type, version;
if (!spa_pod_iter_struct(&it, data, size) ||
!pw_pod_remap_data(SPA_POD_TYPE_STRUCT, data, size, &proxy->remote->types) ||
!spa_pod_iter_get(&it,
SPA_POD_TYPE_INT, &id,
SPA_POD_TYPE_INT, &parent_id,
SPA_POD_TYPE_INT, &permissions,
SPA_POD_TYPE_ID, &type,
SPA_POD_TYPE_INT, &version, 0))
return false;
pw_proxy_notify(proxy, struct pw_registry_events, global, id, parent_id, type, version);
pw_proxy_notify(proxy, struct pw_registry_events, global, id, parent_id, permissions, type, version);
return true;
}

View file

@ -39,13 +39,13 @@ static void client_unbind_func(void *data)
static int
client_bind_func(struct pw_global *global,
struct pw_client *client,
struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
struct pw_client *this = global->object;
struct pw_resource *resource;
resource = pw_resource_new(client, id, global->type, version, 0, client_unbind_func);
resource = pw_resource_new(client, id, permissions, global->type, version, 0, client_unbind_func);
if (resource == NULL)
goto no_mem;
@ -116,7 +116,7 @@ struct pw_client *pw_client_new(struct pw_core *core,
this->info.props = this->properties ? &this->properties->dict : NULL;
this->global = pw_core_add_global(core, NULL, parent, core->type.client, PW_VERSION_CLIENT,
this->global = pw_core_add_global(core, this, parent, core->type.client, PW_VERSION_CLIENT,
client_bind_func, this);
return this;

View file

@ -43,15 +43,19 @@ struct impl {
/** \endcond */
static bool pw_global_is_visible(struct pw_global *global,
struct pw_client *client)
static inline uint32_t pw_global_permissions(struct pw_global *global,
struct pw_client *client)
{
struct pw_core *core = client->core;
return (core->global_filter == NULL ||
core->global_filter(global, client, core->global_filter_data));
if (core->permission_func == NULL)
return PW_PERM_RWX;
return core->permission_func(global, client, core->permission_data);
}
#define PW_PERM_IS_R(p) (((p)&PW_PERM_R) == PW_PERM_R)
static struct pw_global *find_global(struct pw_core *core, uint32_t id)
{
struct pw_global *global;
@ -69,11 +73,14 @@ static void registry_bind(void *object, uint32_t id,
struct pw_client *client = resource->client;
struct pw_core *core = resource->core;
struct pw_global *global;
uint32_t permissions;
if ((global = find_global(core, id)) == 0)
goto no_id;
if (!pw_global_is_visible(global, client))
permissions = pw_global_permissions(global, client);
if (!PW_PERM_IS_R(permissions))
goto no_id;
if (type != global->type)
@ -82,7 +89,7 @@ static void registry_bind(void *object, uint32_t id,
pw_log_debug("global %p: bind global id %d, iface %s to %d", global, id,
spa_type_map_get_type(core->type.map, type), new_id);
pw_global_bind(global, client, version, new_id);
pw_global_bind(global, client, permissions, version, new_id);
return;
@ -136,6 +143,7 @@ static void core_get_registry(void *object, uint32_t version, uint32_t new_id)
registry_resource = pw_resource_new(client,
new_id,
PW_PERM_RWX,
this->type.registry,
version,
0, destroy_registry_resource);
@ -149,10 +157,12 @@ static void core_get_registry(void *object, uint32_t version, uint32_t new_id)
spa_list_insert(this->registry_resource_list.prev, &registry_resource->link);
spa_list_for_each(global, &this->global_list, link) {
if (pw_global_is_visible(global, client)) {
uint32_t permissions = pw_global_permissions(global, client);
if (PW_PERM_IS_R(permissions)) {
pw_registry_resource_global(registry_resource,
global->id,
global->parent->id,
permissions,
global->type,
global->version);
}
@ -185,7 +195,7 @@ core_create_node(void *object,
if (factory == NULL)
goto no_factory;
node_resource = pw_resource_new(client, new_id, type, version, 0, NULL);
node_resource = pw_resource_new(client, new_id, PW_PERM_RWX, type, version, 0, NULL);
if (node_resource == NULL)
goto no_resource;
@ -274,13 +284,14 @@ static void core_unbind_func(void *data)
static int
core_bind_func(struct pw_global *global,
struct pw_client *client,
uint32_t permissions,
uint32_t version,
uint32_t id)
{
struct pw_core *this = global->object;
struct pw_resource *resource;
resource = pw_resource_new(client, id, global->type, version, 0, core_unbind_func);
resource = pw_resource_new(client, id, permissions, global->type, version, 0, core_unbind_func);
if (resource == NULL)
goto no_mem;
@ -328,7 +339,7 @@ struct pw_core *pw_core_new(struct pw_loop *main_loop, struct pw_properties *pro
this->main_loop = main_loop;
pw_type_init(&this->type);
pw_map_init(&this->objects, 128, 32);
pw_map_init(&this->globals, 128, 32);
spa_graph_init(&this->rt.graph);
spa_graph_scheduler_init(&this->rt.sched, &this->rt.graph);
@ -402,7 +413,7 @@ void pw_core_destroy(struct pw_core *core)
pw_data_loop_destroy(impl->data_loop);
pw_map_clear(&core->objects);
pw_map_clear(&core->globals);
pw_log_debug("core %p: free", core);
free(impl);
@ -424,7 +435,7 @@ void pw_core_destroy(struct pw_core *core)
*/
struct pw_global *
pw_core_add_global(struct pw_core *core,
struct pw_resource *owner,
struct pw_client *owner,
struct pw_global *parent,
uint32_t type,
uint32_t version,
@ -450,10 +461,10 @@ pw_core_add_global(struct pw_core *core,
pw_signal_init(&this->destroy_signal);
this->id = pw_map_insert_new(&core->objects, this);
this->id = pw_map_insert_new(&core->globals, this);
if (owner && owner->client)
parent = owner->client->global;
if (owner)
parent = owner->global;
if (parent == NULL)
parent = core->global;
if (parent == NULL)
@ -466,11 +477,16 @@ pw_core_add_global(struct pw_core *core,
pw_log_debug("global %p: new %u %s, owner %p", this, this->id,
spa_type_map_get_type(core->type.map, this->type), owner);
spa_list_for_each(registry, &core->registry_resource_list, link)
if (pw_global_is_visible(this, registry->client))
pw_registry_resource_global(registry, this->id, this->parent->id,
this->type, this->version);
spa_list_for_each(registry, &core->registry_resource_list, link) {
uint32_t permissions = pw_global_permissions(this, registry->client);
if (PW_PERM_IS_R(permissions))
pw_registry_resource_global(registry,
this->id,
this->parent->id,
permissions,
this->type,
this->version);
}
return this;
}
@ -488,7 +504,7 @@ pw_core_add_global(struct pw_core *core,
* \memberof pw_global
*/
int
pw_global_bind(struct pw_global *global, struct pw_client *client,
pw_global_bind(struct pw_global *global, struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
int res;
@ -499,7 +515,7 @@ pw_global_bind(struct pw_global *global, struct pw_client *client,
if (global->version < version)
goto wrong_version;
res = global->bind(global, client, version, id);
res = global->bind(global, client, permissions, version, id);
return res;
@ -532,11 +548,13 @@ void pw_global_destroy(struct pw_global *global)
pw_log_debug("global %p: destroy %u", global, global->id);
pw_signal_emit(&global->destroy_signal, global);
spa_list_for_each(registry, &core->registry_resource_list, link)
if (pw_global_is_visible(global, registry->client))
spa_list_for_each(registry, &core->registry_resource_list, link) {
uint32_t permissions = pw_global_permissions(global, registry->client);
if (PW_PERM_IS_R(permissions))
pw_registry_resource_global_remove(registry, global->id);
}
pw_map_remove(&core->objects, global->id);
pw_map_remove(&core->globals, global->id);
spa_list_remove(&global->link);
pw_signal_emit(&core->global_removed, core, global);

View file

@ -92,11 +92,17 @@ struct pw_global;
*/
typedef int (*pw_bind_func_t) (struct pw_global *global, /**< the global to bind */
struct pw_client *client, /**< client that binds */
uint32_t permissions, /**< permissions for the bind */
uint32_t version, /**< client interface version */
uint32_t id); /**< client proxy id */
typedef bool (*pw_global_filter_func_t) (struct pw_global *global,
struct pw_client *client, void *data);
#define PW_PERM_R 0400 /**< object can be seen and events can be received */
#define PW_PERM_W 0200 /**< methods can be called that modify the object */
#define PW_PERM_X 0100 /**< methods can be called on the object. The W flag must be
* present in order to call methods that modify the object. */
#define PW_PERM_RWX (PW_PERM_R|PW_PERM_W|PW_PERM_X)
typedef uint32_t (*pw_permission_func_t) (struct pw_global *global,
struct pw_client *client, void *data);
/** \page page_global Global
*
@ -122,7 +128,7 @@ typedef bool (*pw_global_filter_func_t) (struct pw_global *global,
*/
struct pw_global {
struct pw_core *core; /**< the core */
struct pw_resource *owner; /**< the owner of this object, NULL when the
struct pw_client *owner; /**< the owner of this object, NULL when the
* PipeWire server is the owner */
struct spa_list link; /**< link in core list of globals */
@ -157,12 +163,12 @@ struct pw_core {
struct pw_properties *properties; /**< properties of the core */
struct pw_type type; /**< type map and common types */
struct pw_type type; /**< type map and common types */
pw_global_filter_func_t global_filter;
void *global_filter_data;
pw_permission_func_t permission_func; /**< get permissions of an object */
void *permission_data; /**< data passed to permission function */
struct pw_map objects; /**< map of known objects */
struct pw_map globals; /**< map of globals */
struct spa_list protocol_list; /**< list of protocols */
struct spa_list remote_list; /**< list of remote connections */
@ -208,7 +214,7 @@ pw_core_update_properties(struct pw_core *core, const struct spa_dict *dict);
struct pw_global *
pw_core_add_global(struct pw_core *core,
struct pw_resource *owner,
struct pw_client *owner,
struct pw_global *parent,
uint32_t type,
uint32_t version,
@ -218,6 +224,7 @@ pw_core_add_global(struct pw_core *core,
int
pw_global_bind(struct pw_global *global,
struct pw_client *client,
uint32_t permissions,
uint32_t version,
uint32_t id);

View file

@ -365,10 +365,12 @@ struct pw_registry_events {
*
* \param id the global object id
* \param parent_id the parent global id
* \param permissions the permissions of the object
* \param type the type of the interface
* \param version the version of the interface
*/
void (*global) (void *object, uint32_t id, uint32_t parent_id, uint32_t type, uint32_t version);
void (*global) (void *object, uint32_t id, uint32_t parent_id,
uint32_t permissions, uint32_t type, uint32_t version);
/**
* Notify of a global object removal
*

View file

@ -940,13 +940,13 @@ static void link_unbind_func(void *data)
static int
link_bind_func(struct pw_global *global,
struct pw_client *client,
struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
struct pw_link *this = global->object;
struct pw_resource *resource;
resource = pw_resource_new(client, id, global->type, version, 0, link_unbind_func);
resource = pw_resource_new(client, id, permissions, global->type, version, 0, link_unbind_func);
if (resource == NULL)
goto no_mem;

View file

@ -89,13 +89,13 @@ static char *find_module(const char *path, const char *name)
static int
module_bind_func(struct pw_global *global,
struct pw_client *client,
struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
struct pw_module *this = global->object;
struct pw_resource *resource;
resource = pw_resource_new(client, id, global->type, version, 0, NULL);
resource = pw_resource_new(client, id, permissions, global->type, version, 0, NULL);
if (resource == NULL)
goto no_mem;

View file

@ -229,13 +229,13 @@ clear_info(struct pw_node *this)
static int
node_bind_func(struct pw_global *global,
struct pw_client *client,
struct pw_client *client, uint32_t permissions,
uint32_t version, uint32_t id)
{
struct pw_node *this = global->object;
struct pw_resource *resource;
resource = pw_resource_new(client, id, global->type, version, 0, node_unbind_func);
resource = pw_resource_new(client, id, permissions, global->type, version, 0, node_unbind_func);
if (resource == NULL)
goto no_mem;
@ -282,7 +282,8 @@ void pw_node_register(struct pw_node *this)
pw_loop_invoke(this->data_loop, do_node_add, 1, 0, NULL, false, this);
spa_list_insert(core->node_list.prev, &this->link);
this->global = pw_core_add_global(core, this->owner, impl->parent,
this->global = pw_core_add_global(core, this->owner ? this->owner->client : NULL,
impl->parent,
core->type.node, PW_VERSION_NODE,
node_bind_func, this);

View file

@ -31,6 +31,7 @@ struct impl {
struct pw_resource *pw_resource_new(struct pw_client *client,
uint32_t id,
uint32_t permissions,
uint32_t type,
uint32_t version,
size_t user_data_size,
@ -46,6 +47,7 @@ struct pw_resource *pw_resource_new(struct pw_client *client,
this = &impl->this;
this->core = client->core;
this->client = client;
this->permissions = permissions;
this->type = type;
this->version = version;

View file

@ -64,6 +64,7 @@ struct pw_resource {
struct pw_client *client; /**< owner client */
uint32_t id; /**< per client unique id, index in client objects */
uint32_t permissions; /**< resource permissions */
uint32_t type; /**< type of the client interface */
uint32_t version; /**< version of the client interface */
@ -85,6 +86,7 @@ struct pw_resource {
struct pw_resource *
pw_resource_new(struct pw_client *client, /**< the client owning the resource */
uint32_t id, /**< the remote per client id */
uint32_t permissions, /**< permissions on this resource */
uint32_t type, /**< interface of the resource */
uint32_t version, /**< requested interface version */
size_t user_data_size, /**< extra user data size */

View file

@ -40,6 +40,7 @@ struct data {
struct proxy_data {
uint32_t id;
uint32_t parent_id;
uint32_t permissions;
uint32_t version;
void *info;
};
@ -95,6 +96,9 @@ static void module_event_info(void *object, struct pw_module_info *info)
printf("\tid: %d\n", data->id);
printf("\tparent_id: %d\n", data->parent_id);
printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-',
data->permissions & PW_PERM_W ? 'w' : '-',
data->permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n", PW_TYPE_INTERFACE__Module, data->version);
if (print_all) {
printf("%c\tname: \"%s\"\n", MARK_CHANGE(0), info->name);
@ -129,6 +133,9 @@ static void node_event_info(void *object, struct pw_node_info *info)
printf("\tid: %d\n", data->id);
printf("\tparent_id: %d\n", data->parent_id);
printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-',
data->permissions & PW_PERM_W ? 'w' : '-',
data->permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n", PW_TYPE_INTERFACE__Node, data->version);
if (print_all) {
int i;
@ -178,6 +185,9 @@ static void client_event_info(void *object, struct pw_client_info *info)
printf("\tid: %d\n", data->id);
printf("\tparent_id: %d\n", data->parent_id);
printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-',
data->permissions & PW_PERM_W ? 'w' : '-',
data->permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n", PW_TYPE_INTERFACE__Client, data->version);
if (print_all) {
print_properties(info->props, MARK_CHANGE(0));
@ -209,6 +219,9 @@ static void link_event_info(void *object, struct pw_link_info *info)
printf("\tid: %d\n", data->id);
printf("\tparent_id: %d\n", data->parent_id);
printf("\tpermissions: %c%c%c\n", data->permissions & PW_PERM_R ? 'r' : '-',
data->permissions & PW_PERM_W ? 'w' : '-',
data->permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n", PW_TYPE_INTERFACE__Link, data->version);
if (print_all) {
printf("%c\toutput-node-id: %u\n", MARK_CHANGE(0), info->output_node_id);
@ -258,7 +271,7 @@ destroy_proxy (void *data)
static void registry_event_global(void *object, uint32_t id, uint32_t parent_id,
uint32_t type, uint32_t version)
uint32_t permissions, uint32_t type, uint32_t version)
{
struct pw_proxy *proxy = object;
struct data *data = proxy->object;
@ -287,6 +300,9 @@ static void registry_event_global(void *object, uint32_t id, uint32_t parent_id,
printf("added:\n");
printf("\tid: %u\n", id);
printf("\tparent_id: %d\n", parent_id);
printf("\tpermissions: %c%c%c\n", permissions & PW_PERM_R ? 'r' : '-',
permissions & PW_PERM_W ? 'w' : '-',
permissions & PW_PERM_X ? 'x' : '-');
printf("\ttype: %s (version %d)\n", spa_type_map_get_type(core->type.map, type), version);
return;
}
@ -299,6 +315,7 @@ static void registry_event_global(void *object, uint32_t id, uint32_t parent_id,
pd = proxy->user_data;
pd->id = id;
pd->parent_id = parent_id;
pd->permissions = permissions;
pd->version = version;
pw_proxy_add_listener(proxy, proxy, events);