From 4f08dbcd2477b5f04307568fd07cf90ef423445e Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 1 Aug 2017 17:09:57 +0200 Subject: [PATCH] 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. --- src/gst/gstpipewiredeviceprovider.c | 2 +- src/modules/module-flatpak.c | 18 ++--- src/modules/module-protocol-native.c | 2 +- .../module-protocol-native/protocol-native.c | 8 ++- src/pipewire/client.c | 6 +- src/pipewire/core.c | 68 ++++++++++++------- src/pipewire/core.h | 23 ++++--- src/pipewire/interfaces.h | 4 +- src/pipewire/link.c | 4 +- src/pipewire/module.c | 4 +- src/pipewire/node.c | 7 +- src/pipewire/resource.c | 2 + src/pipewire/resource.h | 2 + src/tools/pipewire-monitor.c | 19 +++++- 14 files changed, 110 insertions(+), 59 deletions(-) diff --git a/src/gst/gstpipewiredeviceprovider.c b/src/gst/gstpipewiredeviceprovider.c index fe62e8f9a..b52cff48b 100644 --- a/src/gst/gstpipewiredeviceprovider.c +++ b/src/gst/gstpipewiredeviceprovider.c @@ -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; diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c index 297cad50e..f6a712c6b 100644 --- a/src/modules/module-flatpak.c +++ b/src/modules/module-flatpak.c @@ -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; diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index b045d9bb6..e7c8c6cab 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -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; diff --git a/src/modules/module-protocol-native/protocol-native.c b/src/modules/module-protocol-native/protocol-native.c index 594a1492c..b7e9e2d7d 100644 --- a/src/modules/module-protocol-native/protocol-native.c +++ b/src/modules/module-protocol-native/protocol-native.c @@ -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; } diff --git a/src/pipewire/client.c b/src/pipewire/client.c index b72582146..90cd53134 100644 --- a/src/pipewire/client.c +++ b/src/pipewire/client.c @@ -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; diff --git a/src/pipewire/core.c b/src/pipewire/core.c index 2a0301479..75ce60231 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -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, ®istry_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); diff --git a/src/pipewire/core.h b/src/pipewire/core.h index 4edb0f5ab..1392b5559 100644 --- a/src/pipewire/core.h +++ b/src/pipewire/core.h @@ -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); diff --git a/src/pipewire/interfaces.h b/src/pipewire/interfaces.h index 634b2fd8b..ee38dabb3 100644 --- a/src/pipewire/interfaces.h +++ b/src/pipewire/interfaces.h @@ -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 * diff --git a/src/pipewire/link.c b/src/pipewire/link.c index 258a56211..ffbdb7f7d 100644 --- a/src/pipewire/link.c +++ b/src/pipewire/link.c @@ -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; diff --git a/src/pipewire/module.c b/src/pipewire/module.c index d212f7fac..39cdafaee 100644 --- a/src/pipewire/module.c +++ b/src/pipewire/module.c @@ -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; diff --git a/src/pipewire/node.c b/src/pipewire/node.c index efda00918..fce9f8cef 100644 --- a/src/pipewire/node.c +++ b/src/pipewire/node.c @@ -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); diff --git a/src/pipewire/resource.c b/src/pipewire/resource.c index 245d5e96e..dbdea341a 100644 --- a/src/pipewire/resource.c +++ b/src/pipewire/resource.c @@ -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; diff --git a/src/pipewire/resource.h b/src/pipewire/resource.h index 8e85f9014..7d8bcefef 100644 --- a/src/pipewire/resource.h +++ b/src/pipewire/resource.h @@ -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 */ diff --git a/src/tools/pipewire-monitor.c b/src/tools/pipewire-monitor.c index c9b8d36da..8fb5863d2 100644 --- a/src/tools/pipewire-monitor.c +++ b/src/tools/pipewire-monitor.c @@ -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);