permissions: check permissions more

Pass client to some core functions to make it possible to check
permissions when iterating globals.
Check permissions of factory before using it.
Check permissions in link factory, only allow linking of nodes we can
see.
Check permissions in the autolink module, only try to link to nodes
we can see.
Make client permissions update behave like an atomic update of an
unordered set of permissions, which is perhaps a bit more intuitive.
This commit is contained in:
Wim Taymans 2018-01-24 10:42:40 +01:00
parent 1804e47a91
commit ab099d09dd
11 changed files with 79 additions and 38 deletions

View file

@ -36,6 +36,8 @@ struct spa_dict_item {
const char *value; const char *value;
}; };
#define SPA_DICT_ITEM_INIT(key,value) (struct spa_dict_item) { key, value }
struct spa_dict { struct spa_dict {
const struct spa_dict_item *items; const struct spa_dict_item *items;
uint32_t n_items; uint32_t n_items;

View file

@ -554,22 +554,21 @@ static void on_state_changed(void *_data, enum pw_remote_state old, enum pw_remo
case PW_REMOTE_STATE_CONNECTED: case PW_REMOTE_STATE_CONNECTED:
{ {
struct spa_dict_item items[3]; struct spa_dict_item items[5];
int i = 0;
/* set specific permissions on all existing objects without permissions */
items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_EXISTING, "r--");
/* set default permission for new objects and objects without
* specific permissions */
items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_DEFAULT, "---");
/* an example, set specific permissions on one object, this is the /* an example, set specific permissions on one object, this is the
* core object, we already have a binding to it that is not affected * core object, we already have a binding to it that is not affected
* by the removal of X permissions, only future bindings. */ * by the removal of X permissions, only future bindings. */
items[0].key = PW_CORE_PROXY_PERMISSIONS_GLOBAL; items[i++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_GLOBAL, "0:rw-");
items[0].value = "0:rw-";
/* set specific permissions on all existing objects without permissions */
items[1].key = PW_CORE_PROXY_PERMISSIONS_EXISTING;
items[1].value = "r--";
/* set default permission for new objects and objects without
* specific permissions */
items[2].key = PW_CORE_PROXY_PERMISSIONS_DEFAULT;
items[2].value = "---";
pw_core_proxy_permissions(pw_remote_get_core_proxy(data->remote), pw_core_proxy_permissions(pw_remote_get_core_proxy(data->remote),
&SPA_DICT_INIT(items, SPA_N_ELEMENTS(items))); &SPA_DICT_INIT(items, i));
make_node(data); make_node(data);
break; break;

View file

@ -185,6 +185,8 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod
struct pw_link *link; struct pw_link *link;
struct pw_port *target; struct pw_port *target;
struct link_data *ld; struct link_data *ld;
struct pw_global *global = pw_node_get_global(info->node);
struct pw_client *owner = pw_global_get_owner(global);
props = pw_node_get_properties(node); props = pw_node_get_properties(node);
@ -202,7 +204,7 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod
pw_log_debug("module %p: try to find and link to node '%d'", impl, path_id); pw_log_debug("module %p: try to find and link to node '%d'", impl, path_id);
target = pw_core_find_port(impl->core, port, path_id, NULL, 0, NULL, &error); target = pw_core_find_port(impl->core, owner, port, path_id, NULL, 0, NULL, &error);
if (target == NULL) if (target == NULL)
goto error; goto error;
@ -234,12 +236,8 @@ static void try_link_port(struct pw_node *node, struct pw_port *port, struct nod
error: error:
pw_log_error("module %p: can't link node '%s'", impl, error); pw_log_error("module %p: can't link node '%s'", impl, error);
{ if (owner)
struct pw_global *global = pw_node_get_global(info->node); pw_resource_error(pw_client_get_core_resource(owner), -EINVAL, error);
struct pw_client *owner = pw_global_get_owner(global);
if (owner)
pw_resource_error(pw_client_get_core_resource(owner), -EINVAL, error);
}
free(error); free(error);
return; return;
} }

View file

@ -1351,7 +1351,7 @@ static bool init_nodes(struct impl *impl)
make_audio_client(impl); make_audio_client(impl);
make_freewheel_client(impl); make_freewheel_client(impl);
pw_core_for_each_global(core, on_global, impl); pw_core_for_each_global(core, NULL, on_global, impl);
return true; return true;
} }

View file

@ -83,13 +83,13 @@ static void *create_object(void *_data,
core = pw_client_get_core(client); core = pw_client_get_core(client);
t = pw_core_get_type(core); t = pw_core_get_type(core);
global = pw_core_find_global(core, output_node_id); global = pw_core_find_global(core, client, output_node_id);
if (global == NULL || pw_global_get_type(global) != t->node) if (global == NULL || pw_global_get_type(global) != t->node)
goto no_output; goto no_output;
output_node = pw_global_get_object(global); output_node = pw_global_get_object(global);
global = pw_core_find_global(core, input_node_id); global = pw_core_find_global(core, client, input_node_id);
if (global == NULL || pw_global_get_type(global) != t->node) if (global == NULL || pw_global_get_type(global) != t->node)
goto no_input; goto no_input;

View file

@ -233,7 +233,7 @@ static int module_init(struct pw_module *module, struct pw_properties *propertie
spa_list_init(&impl->node_list); spa_list_init(&impl->node_list);
pw_core_for_each_global(core, on_global, impl); pw_core_for_each_global(core, NULL, on_global, impl);
pw_core_add_listener(core, &impl->core_listener, &core_events, impl); pw_core_add_listener(core, &impl->core_listener, &core_events, impl);
pw_module_add_listener(module, &impl->module_listener, &module_events, impl); pw_module_add_listener(module, &impl->module_listener, &module_events, impl);

View file

@ -426,14 +426,18 @@ int pw_client_update_permissions(struct pw_client *client, const struct spa_dict
const char *str; const char *str;
size_t len; size_t len;
struct permissions_update update = { client, 0 }; struct permissions_update update = { client, 0 };
uint32_t permissions_existing, permissions_default;
permissions_default = impl->permissions_default;
permissions_existing = -1;
for (i = 0; i < dict->n_items; i++) { for (i = 0; i < dict->n_items; i++) {
str = dict->items[i].value; str = dict->items[i].value;
if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_DEFAULT) == 0) { if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_DEFAULT) == 0) {
impl->permissions_default &= parse_mask(str); permissions_default &= parse_mask(str);
pw_log_debug("client %p: set default permissions to %08x", pw_log_debug("client %p: set default permissions to %08x",
client, impl->permissions_default); client, permissions_default);
} }
else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_GLOBAL) == 0) { else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_GLOBAL) == 0) {
struct pw_global *global; struct pw_global *global;
@ -445,22 +449,34 @@ int pw_client_update_permissions(struct pw_client *client, const struct spa_dict
continue; continue;
global_id = atoi(str); global_id = atoi(str);
global = pw_core_find_global(client->core, global_id); global = pw_core_find_global(client->core, client, global_id);
if (global == NULL) { if (global == NULL) {
pw_log_warn("client %p: invalid global %d", client, global_id); pw_log_warn("client %p: invalid global %d", client, global_id);
continue; continue;
} }
/* apply the specific updates in order. This is ok for now, we could add
* a field to the permission struct later to accumulate the changes
* and apply them out of this loop */
update.permissions = parse_mask(str + len); update.permissions = parse_mask(str + len);
update.only_new = false; update.only_new = false;
do_permissions(&update, global); do_permissions(&update, global);
} }
else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_EXISTING) == 0) { else if (strcmp(dict->items[i].key, PW_CORE_PROXY_PERMISSIONS_EXISTING) == 0) {
update.permissions = parse_mask(str); permissions_existing = parse_mask(str);
update.only_new = true; pw_log_debug("client %p: set existing permissions to %08x",
pw_core_for_each_global(client->core, do_permissions, &update); client, permissions_existing);
} }
} }
/* apply default and existing permissions after specific ones to make the
* permission update look like an atomic unordered set of changes. */
if (permissions_existing != -1) {
update.permissions = permissions_existing;
update.only_new = true;
pw_core_for_each_global(client->core, client, do_permissions, &update);
}
impl->permissions_default = permissions_default;
return 0; return 0;
} }

View file

@ -51,7 +51,7 @@ static void registry_bind(void *object, uint32_t id,
struct pw_global *global; struct pw_global *global;
uint32_t permissions; uint32_t permissions;
if ((global = pw_core_find_global(core, id)) == NULL) if ((global = pw_core_find_global(core, NULL, id)) == NULL)
goto no_id; goto no_id;
permissions = pw_global_get_permissions(global, client); permissions = pw_global_get_permissions(global, client);
@ -198,7 +198,10 @@ core_create_object(void *object,
struct pw_properties *properties; struct pw_properties *properties;
factory = pw_core_find_factory(client->core, factory_name); factory = pw_core_find_factory(client->core, factory_name);
if (factory == NULL) if (factory == NULL || factory->global == NULL)
goto no_factory;
if (!PW_PERM_IS_R(pw_global_get_permissions(factory->global, client)))
goto no_factory; goto no_factory;
if (factory->info.type != type) if (factory->info.type != type)
@ -544,21 +547,34 @@ int pw_core_update_properties(struct pw_core *core, const struct spa_dict *dict)
} }
int pw_core_for_each_global(struct pw_core *core, int pw_core_for_each_global(struct pw_core *core,
struct pw_client *client,
int (*callback) (void *data, struct pw_global *global), int (*callback) (void *data, struct pw_global *global),
void *data) void *data)
{ {
struct pw_global *g, *t; struct pw_global *g, *t;
int res; int res;
spa_list_for_each_safe(g, t, &core->global_list, link) spa_list_for_each_safe(g, t, &core->global_list, link) {
if (client && !PW_PERM_IS_R(pw_global_get_permissions(g, client)))
continue;
if ((res = callback(data, g)) != 0) if ((res = callback(data, g)) != 0)
return res; return res;
}
return 0; return 0;
} }
struct pw_global *pw_core_find_global(struct pw_core *core, uint32_t id) struct pw_global *pw_core_find_global(struct pw_core *core, struct pw_client *client, uint32_t id)
{ {
return pw_map_lookup(&core->globals, id); struct pw_global *global;
global = pw_map_lookup(&core->globals, id);
if (global == NULL)
return NULL;
if (client && !PW_PERM_IS_R(pw_global_get_permissions(global, client)))
return NULL;
return global;
} }
/** Find a port to link with /** Find a port to link with
@ -575,6 +591,7 @@ struct pw_global *pw_core_find_global(struct pw_core *core, uint32_t id)
* \memberof pw_core * \memberof pw_core
*/ */
struct pw_port *pw_core_find_port(struct pw_core *core, struct pw_port *pw_core_find_port(struct pw_core *core,
struct pw_client *client,
struct pw_port *other_port, struct pw_port *other_port,
uint32_t id, uint32_t id,
struct pw_properties *props, struct pw_properties *props,
@ -597,6 +614,9 @@ struct pw_port *pw_core_find_port(struct pw_core *core,
if (other_port->node == n) if (other_port->node == n)
continue; continue;
if (!PW_PERM_IS_R(pw_global_get_permissions(n->global, client)))
continue;
pw_log_debug("node id \"%d\"", n->global->id); pw_log_debug("node id \"%d\"", n->global->id);
if (have_id) { if (have_id) {
@ -774,7 +794,8 @@ int pw_core_find_format(struct pw_core *core,
* *
* \memberof pw_core * \memberof pw_core
*/ */
struct pw_factory *pw_core_find_factory(struct pw_core *core, const char *name) struct pw_factory *pw_core_find_factory(struct pw_core *core,
const char *name)
{ {
struct pw_factory *factory; struct pw_factory *factory;

View file

@ -159,16 +159,20 @@ struct pw_loop *pw_core_get_main_loop(struct pw_core *core);
* 0 to fetch the next item, any other value stops the iteration and returns * 0 to fetch the next item, any other value stops the iteration and returns
* the value. When all callbacks return 0, this function returns 0 when all * the value. When all callbacks return 0, this function returns 0 when all
* globals are iterated. */ * globals are iterated. */
int pw_core_for_each_global(struct pw_core *core, int pw_core_for_each_global(struct pw_core *core, /**< the core */
struct pw_client *client, /**< optional client */
int (*callback) (void *data, struct pw_global *global), int (*callback) (void *data, struct pw_global *global),
void *data); void *data);
/** Find a core global by id */ /** Find a core global by id */
struct pw_global *pw_core_find_global(struct pw_core *core, uint32_t id); struct pw_global *pw_core_find_global(struct pw_core *core, /**< the core */
struct pw_client *client, /**< optional client */
uint32_t id /**< the global id */);
/** Find a factory by name */ /** Find a factory by name */
struct pw_factory * struct pw_factory *
pw_core_find_factory(struct pw_core *core, const char *name); pw_core_find_factory(struct pw_core *core /**< the core */,
const char *name /**< the factory name */);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -434,6 +434,7 @@ int pw_core_find_format(struct pw_core *core,
/** Find a ports compatible with \a other_port and the format filters */ /** Find a ports compatible with \a other_port and the format filters */
struct pw_port * struct pw_port *
pw_core_find_port(struct pw_core *core, pw_core_find_port(struct pw_core *core,
struct pw_client *client,
struct pw_port *other_port, struct pw_port *other_port,
uint32_t id, uint32_t id,
struct pw_properties *props, struct pw_properties *props,

View file

@ -1001,7 +1001,7 @@ static bool do_export_node(struct data *data, const char *cmd, char *args, char
goto no_remote; goto no_remote;
} }
global = pw_core_find_global(data->core, atoi(a[0])); global = pw_core_find_global(data->core, NULL, atoi(a[0]));
if (global == NULL) { if (global == NULL) {
asprintf(error, "object %d does not exist", atoi(a[0])); asprintf(error, "object %d does not exist", atoi(a[0]));
return false; return false;