diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c index 994b2c9f2..10ad05a91 100644 --- a/src/modules/module-flatpak.c +++ b/src/modules/module-flatpak.c @@ -50,36 +50,20 @@ struct impl { struct spa_list client_list; }; -struct resource; - struct client_info { struct spa_list link; struct impl *impl; struct pw_client *client; - struct spa_hook client_listener; - bool is_sandboxed; struct spa_list resources; - struct resource *core_resource; struct spa_list async_pending; -}; - -struct resource { - struct spa_list link; - struct client_info *cinfo; - struct pw_resource *resource; - struct spa_hook override; + bool camera_allowed; }; struct async_pending { struct spa_list link; - struct resource *resource; + struct client_info *cinfo; bool handled; char *handle; - char *factory_name; - uint32_t type; - uint32_t version; - struct pw_properties *properties; - uint32_t new_id; }; static struct client_info *find_client_info(struct impl *impl, struct pw_client *client) @@ -96,7 +80,7 @@ static struct client_info *find_client_info(struct impl *impl, struct pw_client static void close_request(struct async_pending *p) { DBusMessage *m = NULL; - struct impl *impl = p->resource->cinfo->impl; + struct impl *impl = p->cinfo->impl; pw_log_debug("pending %p: handle %s", p, p->handle); @@ -132,50 +116,34 @@ static void free_pending(struct async_pending *p) pw_log_debug("pending %p: handle %s", p, p->handle); spa_list_remove(&p->link); free(p->handle); - free(p->factory_name); - if (p->properties) - pw_properties_free(p->properties); free(p); } -static void free_resource(struct resource *r) -{ - spa_list_remove(&r->link); - free(r); -} - static void client_info_free(struct client_info *cinfo) { struct async_pending *p, *tp; - struct resource *r, *tr; spa_list_for_each_safe(p, tp, &cinfo->async_pending, link) free_pending(p); - spa_list_for_each_safe(r, tr, &cinfo->resources, link) - free_resource(r); - spa_hook_remove(&cinfo->client_listener); spa_list_remove(&cinfo->link); free(cinfo); } -static bool check_sandboxed(struct client_info *cinfo, char **error) +static int check_sandboxed(struct pw_client *client) { char root_path[2048]; - int root_fd, info_fd; + int root_fd, info_fd, res; const struct ucred *ucred; struct stat stat_buf; - ucred = pw_client_get_ucred(cinfo->client); - - cinfo->is_sandboxed = true; + ucred = pw_client_get_ucred(client); if (ucred) { pw_log_info("client has trusted pid %d", ucred->pid); } else { - cinfo->is_sandboxed = false; pw_log_info("no trusted pid found, assuming not sandboxed\n"); - return true; + return 0; } sprintf(root_path, "/proc/%u/root", ucred->pid); @@ -184,32 +152,32 @@ static bool check_sandboxed(struct client_info *cinfo, char **error) /* Not able to open the root dir shouldn't happen. Probably the app died and * we're failing due to /proc/$pid not existing. In that case fail instead * of treating this as privileged. */ - asprintf(error, "failed to open \"%s\": %m", root_path); - return false; + res = -errno; + pw_log_error("failed to open \"%s\": %m", root_path); + return res; } info_fd = openat (root_fd, ".flatpak-info", O_RDONLY | O_CLOEXEC | O_NOCTTY); close (root_fd); if (info_fd == -1) { if (errno == ENOENT) { pw_log_debug("no .flatpak-info, client on the host"); - cinfo->is_sandboxed = false; /* No file => on the host */ - return true; + return 0; } - asprintf(error, "error opening .flatpak-info: %m"); - return false; + res = -errno; + pw_log_error("error opening .flatpak-info: %m"); + return res; } if (fstat (info_fd, &stat_buf) != 0 || !S_ISREG (stat_buf.st_mode)) { - /* Some weird fd => failure */ + /* Some weird fd => failure, assume sandboxed */ close(info_fd); - asprintf(error, "error fstat .flatpak-info: %m"); - return false; + pw_log_error("error fstat .flatpak-info: %m"); } - return true; + return 1; } static bool -check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_global *global) +check_global_owner(struct pw_client *client, struct pw_global *global) { struct pw_client *owner; const struct ucred *owner_ucred, *client_ucred; @@ -219,48 +187,64 @@ check_global_owner(struct pw_core *core, struct pw_client *client, struct pw_glo owner = pw_global_get_owner(global); if (owner == NULL) - return true; + return false; owner_ucred = pw_client_get_ucred(owner); client_ucred = pw_client_get_ucred(client); if (owner_ucred == NULL || client_ucred == NULL) - return true; + return false; + /* same user can see eachothers objects */ return owner_ucred->uid == client_ucred->uid; } -static uint32_t -do_permission(struct pw_global *global, struct pw_client *client, void *data) +static int +set_global_permissions(void *data, struct pw_global *global) { - struct impl *impl = data; + struct client_info *cinfo = data; + struct impl *impl = cinfo->impl; + struct pw_client *client = cinfo->client; + const struct pw_properties *props; + const char *str; + struct spa_dict_item items[1]; + int n_items = 0; + char perms[16]; + bool allowed = false; - if (pw_global_get_type(global) == impl->type->link) { - struct pw_link *link = pw_global_get_object(global); - struct pw_port *port; - struct pw_node *node; + props = pw_global_get_properties(global); - port = pw_link_get_output(link); - node = pw_port_get_node(port); - /* we must be able to see both nodes */ - if (port && node && !check_global_owner(impl->core, client, pw_node_get_global(node))) - return 0; - - port = pw_link_get_input(link); - node = pw_port_get_node(port); - if (port && node && !check_global_owner(impl->core, client, pw_node_get_global(node))) - return 0; + if (pw_global_get_type(global) == impl->type->core) { + allowed = true; } - else if (!check_global_owner(impl->core, client, global)) - return 0; + else if (pw_global_get_type(global) == impl->type->factory) { + if (props && (str = pw_properties_get(props, "factory.name"))) { + if (strcmp(str, "client-node") == 0) + allowed = true; + } + } + else if (pw_global_get_type(global) == impl->type->node) { + if (props && (str = pw_properties_get(props, "media.class"))) { + if (strcmp(str, "Video/Source") == 0 && cinfo->camera_allowed) + allowed = true; + } + allowed |= check_global_owner(client, global); + } + else + allowed = check_global_owner(client, global); - return PW_PERM_RWX; + snprintf(perms, sizeof(perms), "%d:%c--", pw_global_get_id(global), allowed ? 'r' : '-'); + items[n_items++] = SPA_DICT_ITEM_INIT(PW_CORE_PROXY_PERMISSIONS_GLOBAL, perms); + pw_client_update_permissions(client, &SPA_DICT_INIT(items, n_items)); + + return 0; } static DBusHandlerResult portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data) { struct client_info *cinfo = user_data; + struct pw_client *client = cinfo->client; if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) { uint32_t response = 2; @@ -286,21 +270,17 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data) pw_log_debug("portal check result: %d", response); if (response == 0) { - pw_resource_do_parent(p->resource->resource, - &p->resource->override, - struct pw_core_proxy_methods, - create_object, - p->factory_name, - p->type, - p->version, - &p->properties->dict, - p->new_id); + /* allowed */ + cinfo->camera_allowed = true; + pw_log_debug("camera access allowed"); } else { - pw_resource_error(p->resource->resource, -EPERM, "not allowed"); - + cinfo->camera_allowed = false; + pw_log_debug("camera access not allowed"); } + pw_core_for_each_global(cinfo->impl->core, set_global_permissions, cinfo); + free_pending(p); - pw_client_set_busy(cinfo->client, false); + pw_client_set_busy(client, false); return DBUS_HANDLER_RESULT_HANDLED; } @@ -308,15 +288,8 @@ portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data) } -static void do_create_object(void *data, - const char *factory_name, - uint32_t type, - uint32_t version, - const struct spa_dict *props, - uint32_t new_id) +static void do_portal_check(struct client_info *cinfo) { - struct resource *resource = data; - struct client_info *cinfo = resource->cinfo; struct impl *impl = cinfo->impl; struct pw_client *client = cinfo->client; DBusMessage *m = NULL, *r = NULL; @@ -328,24 +301,7 @@ static void do_create_object(void *data, const char *device; struct async_pending *p; - if (!cinfo->is_sandboxed) { - pw_resource_do_parent(resource->resource, - &resource->override, - struct pw_core_proxy_methods, - create_object, - factory_name, - type, - version, - props, - new_id); - return; - } - if (strcmp(factory_name, "client-node") != 0) { - pw_log_error("can only allow client-node"); - goto not_allowed; - } - - pw_log_info("ask portal for client %p", cinfo->client); + pw_log_info("ask portal for client %p", client); pw_client_set_busy(client, true); dbus_error_init(&error); @@ -357,7 +313,7 @@ static void do_create_object(void *data, device = "camera"; - pid = pw_client_get_ucred(cinfo->client)->pid; + pid = pw_client_get_ucred(client)->pid; if (!dbus_message_append_args(m, DBUS_TYPE_UINT32, &pid, DBUS_TYPE_INVALID)) goto message_failed; @@ -388,14 +344,9 @@ static void do_create_object(void *data, dbus_connection_add_filter(impl->bus, portal_response, cinfo, NULL); p = calloc(1, sizeof(struct async_pending)); - p->resource = resource; + p->cinfo = cinfo; p->handle = strdup(handle); p->handled = false; - p->factory_name = strdup(factory_name); - p->type = type; - p->version = version; - p->properties = props ? pw_properties_new_dict(props) : NULL; - p->new_id = new_id; pw_log_debug("pending %p: handle %s", p, handle); spa_list_append(&cinfo->async_pending, &p->link); @@ -423,69 +374,48 @@ static void do_create_object(void *data, dbus_error_free(&error); goto not_allowed; not_allowed: - pw_resource_error(cinfo->core_resource->resource, -EPERM, "not allowed"); + pw_resource_error(pw_client_get_core_resource(client), -EPERM, "not allowed"); return; } -static const struct pw_core_proxy_methods core_override = { - PW_VERSION_CORE_PROXY_METHODS, - .create_object = do_create_object, -}; - -static void client_resource_impl(void *data, struct pw_resource *resource) -{ - struct client_info *cinfo = data; - struct impl *impl = cinfo->impl; - - if (pw_resource_get_type(resource) == impl->type->core) { - struct resource *r; - - r = calloc(1, sizeof(struct resource)); - r->cinfo = cinfo; - r->resource = resource; - spa_list_append(&cinfo->resources, &r->link); - - if (pw_resource_get_id(resource) == 0) - cinfo->core_resource = r; - - pw_log_debug("module %p: add core override", impl); - pw_resource_add_override(resource, - &r->override, - &core_override, - r); - } -} - -static const struct pw_client_events client_events = { - PW_VERSION_CLIENT_EVENTS, - .resource_impl = client_resource_impl, -}; - static void core_global_added(void *data, struct pw_global *global) { struct impl *impl = data; + struct client_info *cinfo; + int res; if (pw_global_get_type(global) == impl->type->client) { struct pw_client *client = pw_global_get_object(global); - struct client_info *cinfo; - char *error; + res = check_sandboxed(client); + if (res == 0) { + pw_log_debug("module %p: non sandboxed client %p", impl, client); + return; + } + + if (res < 0) { + pw_log_warn("module %p: client %p sandbox check failed: %s", + impl, client, spa_strerror(res)); + } + else { + pw_log_debug("module %p: sandboxed client %p added", impl, client); + } + + /* sandboxed clients are placed in a list and we do a portal check */ cinfo = calloc(1, sizeof(struct client_info)); cinfo->impl = impl; cinfo->client = client; - if (!check_sandboxed(cinfo, &error)) { - pw_log_warn("module %p: client %p sandbox check failed: %s", impl, client, error); - free(error); - } - spa_list_init(&cinfo->async_pending); - spa_list_init(&cinfo->resources); - pw_client_add_listener(client, &cinfo->client_listener, &client_events, cinfo); + spa_list_init(&cinfo->async_pending); spa_list_append(&impl->client_list, &cinfo->link); - pw_log_debug("module %p: client %p added", impl, client); + do_portal_check(cinfo); + } + else { + spa_list_for_each(cinfo, &impl->client_list, link) + set_global_permissions(cinfo, global); } } @@ -573,8 +503,6 @@ static int module_init(struct pw_module *module, struct pw_properties *propertie pw_core_add_listener(core, &impl->core_listener, &core_events, impl); pw_module_add_listener(module, &impl->module_listener, &module_events, impl); - pw_core_set_permission_callback(core, do_permission, impl); - return 0; error: diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c index 4393be23f..d4352a327 100644 --- a/src/modules/module-protocol-native.c +++ b/src/modules/module-protocol-native.c @@ -170,21 +170,20 @@ process_messages(struct client_data *data) struct pw_client *client = data->client; struct pw_core *core = client->core; uint8_t opcode; - uint32_t id; - uint32_t size; + uint32_t id, size; void *message; core->current_client = client; - while (pw_protocol_native_connection_get_next(conn, &opcode, &id, &message, &size)) { + /* when the client is busy processing an async action, stop processing messages + * for the client until it finishes the action */ + while (!data->busy) { struct pw_resource *resource; const struct pw_protocol_native_demarshal *demarshal; const struct pw_protocol_marshal *marshal; uint32_t permissions; - /* when the client is busy processing an async action, stop processing messages - * for the client until it finishes the action */ - if (data->busy) + if (!pw_protocol_native_connection_get_next(conn, &opcode, &id, &message, &size)) break; pw_log_trace("protocol-native %p: got message %d from %u", client->protocol, @@ -339,7 +338,7 @@ static struct pw_client *client_new(struct server *s, int fd) spa_list_append(&s->this.client_list, &client->protocol_link); pw_client_add_listener(client, &this->client_listener, &client_events, this); - pw_client_register(client, NULL, pw_module_get_global(pd->module), NULL); + pw_client_register(client, client, pw_module_get_global(pd->module), NULL); pw_global_bind(pw_core_get_global(core), client, PW_PERM_RWX, PW_VERSION_CORE, 0); diff --git a/src/pipewire/core.c b/src/pipewire/core.c index f57bb14d0..87885b048 100644 --- a/src/pipewire/core.c +++ b/src/pipewire/core.c @@ -257,7 +257,7 @@ static void core_update_types(void *object, uint32_t first_id, const char **type for (i = 0; i < n_types; i++, first_id++) { uint32_t this_id = spa_type_map_get_id(this->type.map, types[i]); if (!pw_map_insert_at(&client->types, first_id, PW_MAP_ID_TO_PTR(this_id))) - pw_log_error("can't add type for client"); + pw_log_error("can't add type %d->%d for client", first_id, this_id); } } @@ -486,14 +486,6 @@ void pw_core_add_listener(struct pw_core *core, spa_hook_list_append(&core->listener_list, listener, events, data); } -void pw_core_set_permission_callback(struct pw_core *core, - pw_permission_func_t callback, - void *data) -{ - core->permission_func = callback; - core->permission_data = data; -} - struct pw_type *pw_core_get_type(struct pw_core *core) { return &core->type; diff --git a/src/pipewire/core.h b/src/pipewire/core.h index ab0d81d90..b8f16b4ee 100644 --- a/src/pipewire/core.h +++ b/src/pipewire/core.h @@ -79,11 +79,6 @@ struct pw_core; * present in order to call methods that modify the object. */ #define PW_PERM_RWX (PW_PERM_R|PW_PERM_W|PW_PERM_X) -/** the permission function. It returns the allowed access permissions for \a global - * for \a client */ -typedef uint32_t (*pw_permission_func_t) (struct pw_global *global, - struct pw_client *client, void *data); - #define PW_PERM_IS_R(p) (((p)&PW_PERM_R) == PW_PERM_R) #define PW_PERM_IS_W(p) (((p)&PW_PERM_W) == PW_PERM_W) #define PW_PERM_IS_X(p) (((p)&PW_PERM_X) == PW_PERM_X) @@ -128,12 +123,6 @@ void pw_core_add_listener(struct pw_core *core, const struct pw_core_events *events, void *data); -/** Set a callback that will be called to check the permissions of a global - * object for a client */ -void pw_core_set_permission_callback(struct pw_core *core, - pw_permission_func_t callback, - void *data); - /** Get the type object of a core */ struct pw_type *pw_core_get_type(struct pw_core *core); diff --git a/src/pipewire/global.c b/src/pipewire/global.c index ee70fd9f9..e4ec1c0bc 100644 --- a/src/pipewire/global.c +++ b/src/pipewire/global.c @@ -37,12 +37,9 @@ struct global_impl { uint32_t pw_global_get_permissions(struct pw_global *global, struct pw_client *client) { - struct pw_core *core = client->core; uint32_t perms = PW_PERM_RWX; - if (core->permission_func != NULL) - perms &= core->permission_func(global, client, core->permission_data); - if ((perms & PW_PERM_R) && client->permission_func != NULL) + if (client->permission_func != NULL) perms &= client->permission_func(global, client, client->permission_data); return perms; @@ -121,9 +118,8 @@ pw_global_register(struct pw_global *global, spa_list_append(&core->global_list, &global->link); - spa_hook_list_call(&core->listener_list, struct pw_core_events, global_added, global); - pw_log_debug("global %p: add %u owner %p parent %p", global, global->id, owner, parent); + spa_hook_list_call(&core->listener_list, struct pw_core_events, global_added, global); spa_list_for_each(registry, &core->registry_resource_list, link) { uint32_t permissions = pw_global_get_permissions(global, registry->client); diff --git a/src/pipewire/private.h b/src/pipewire/private.h index b96d758a2..42ff949c6 100644 --- a/src/pipewire/private.h +++ b/src/pipewire/private.h @@ -68,6 +68,11 @@ struct pw_protocol { void *user_data; /**< user data for the implementation */ }; +/** the permission function. It returns the allowed access permissions for \a global + * for \a client */ +typedef uint32_t (*pw_permission_func_t) (struct pw_global *global, + struct pw_client *client, void *data); + struct pw_client { struct pw_core *core; /**< core object */ struct spa_list link; /**< link in core object client list */ @@ -127,9 +132,6 @@ struct pw_core { struct pw_type type; /**< type map and common types */ - pw_permission_func_t permission_func; /**< get permissions of an object */ - void *permission_data; /**< data passed to permission function */ - struct pw_map globals; /**< map of globals */ struct spa_list protocol_list; /**< list of protocols */ @@ -422,6 +424,7 @@ struct pw_control { void *user_data; }; + /** Find a good format between 2 ports */ int pw_core_find_format(struct pw_core *core, struct pw_port *output,