1 From b6854927aaf5e5970178ed9b0c6647bb759f2092 Mon Sep 17 00:00:00 2001
2 From: George Kiagiadakis <george.kiagiadakis@collabora.com>
3 Date: Tue, 16 Feb 2021 17:26:20 +0200
4 Subject: [PATCH] modules: add new access-seclabel module
6 This module allows access control based on the security label
7 of the client. It is tailored for use with the semantics of SMACK
9 Upstream-Status: Inappropriate [smack specific]
12 src/modules/meson.build | 10 ++
13 src/modules/module-access-seclabel.c | 220 +++++++++++++++++++++++++++
14 2 files changed, 230 insertions(+)
15 create mode 100644 src/modules/module-access-seclabel.c
17 diff --git a/src/modules/meson.build b/src/modules/meson.build
18 index f51aa29c..21b52d49 100644
19 --- a/src/modules/meson.build
20 +++ b/src/modules/meson.build
21 @@ -56,6 +56,16 @@ pipewire_module_echo_cancel = shared_library('pipewire-module-echo-cancel',
22 dependencies : [mathlib, dl_lib, pipewire_dep, webrtc_dep],
25 +pipewire_module_access_seclabel = shared_library('pipewire-module-access-seclabel',
26 + [ 'module-access-seclabel.c' ],
27 + c_args : pipewire_module_c_args,
28 + include_directories : [configinc, spa_inc],
30 + install_dir : modules_install_dir,
31 + install_rpath: modules_install_dir,
32 + dependencies : [mathlib, dl_lib, pipewire_dep],
35 pipewire_module_profiler = shared_library('pipewire-module-profiler',
36 [ 'module-profiler.c',
37 'module-profiler/protocol-native.c', ],
38 diff --git a/src/modules/module-access-seclabel.c b/src/modules/module-access-seclabel.c
40 index 00000000..3739f2e4
42 +++ b/src/modules/module-access-seclabel.c
46 + * Copyright © 2018 Wim Taymans
47 + * Copyright © 2021 Collabora Ltd.
48 + * @author George Kiagiadakis <george.kiagiadakis@collabora.com>
50 + * Permission is hereby granted, free of charge, to any person obtaining a
51 + * copy of this software and associated documentation files (the "Software"),
52 + * to deal in the Software without restriction, including without limitation
53 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
54 + * and/or sell copies of the Software, and to permit persons to whom the
55 + * Software is furnished to do so, subject to the following conditions:
57 + * The above copyright notice and this permission notice (including the next
58 + * paragraph) shall be included in all copies or substantial portions of the
61 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
62 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
63 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
64 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
65 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
66 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
67 + * DEALINGS IN THE SOFTWARE.
73 +#include <sys/types.h>
74 +#include <sys/stat.h>
81 +#include <spa/utils/result.h>
82 +#include <spa/utils/json.h>
84 +#include <pipewire/impl.h>
85 +#include <pipewire/private.h>
87 +#define NAME "access-seclabel"
89 +#define MODULE_USAGE "[ seclabel.allowed=<cmd-line> ] " \
90 + "[ seclabel.rejected=<cmd-line> ] " \
91 + "[ seclabel.restricted=<cmd-line> ] " \
93 +static const struct spa_dict_item module_props[] = {
94 + { PW_KEY_MODULE_AUTHOR, "George Kiagiadakis <george.kiagiadakis@collabora.com>" },
95 + { PW_KEY_MODULE_DESCRIPTION, "Perform access check based on the security label" },
96 + { PW_KEY_MODULE_USAGE, MODULE_USAGE },
97 + { PW_KEY_MODULE_VERSION, PACKAGE_VERSION },
101 + struct pw_context *context;
102 + struct pw_properties *properties;
104 + struct spa_hook context_listener;
105 + struct spa_hook module_listener;
108 +static int check_label(const char *label, const char *str)
112 + struct spa_json it[2];
114 + spa_json_init(&it[0], str, strlen(str));
115 + if ((res = spa_json_enter_array(&it[0], &it[1])) <= 0)
119 + while (spa_json_get_string(&it[1], key, sizeof(key)) > 0) {
120 + if (strcmp(label, key) == 0) {
130 +context_check_access(void *data, struct pw_impl_client *client)
132 + struct impl *impl = data;
133 + struct pw_permission permissions[1];
134 + struct spa_dict_item items[2];
135 + const struct pw_properties *props;
136 + const char *str, *access, *label = NULL;
139 + if ((props = pw_impl_client_get_properties(client)) != NULL) {
140 + if ((str = pw_properties_get(props, PW_KEY_ACCESS)) != NULL) {
141 + pw_log_info(NAME " client %p: has already access: '%s'", client, str);
144 + label = pw_properties_get(props, PW_KEY_SEC_LABEL);
148 + pw_log_info(NAME " client %p: has no security label", client);
152 + if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.allowed")) != NULL) {
153 + res = check_label(label, str);
155 + pw_log_warn(NAME" %p: client %p allowed check failed: %s",
156 + impl, client, spa_strerror(res));
157 + } else if (res > 0) {
158 + access = "allowed";
163 + if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.rejected")) != NULL) {
164 + res = check_label(label, str);
166 + pw_log_warn(NAME" %p: client %p rejected check failed: %s",
167 + impl, client, spa_strerror(res));
168 + } else if (res > 0) {
170 + access = "rejected";
175 + if (impl->properties && (str = pw_properties_get(impl->properties, "seclabel.restricted")) != NULL) {
176 + res = check_label(label, str);
178 + pw_log_warn(NAME" %p: client %p restricted check failed: %s",
179 + impl, client, spa_strerror(res));
181 + else if (res > 0) {
182 + pw_log_debug(NAME" %p: restricted client %p added", impl, client);
183 + access = "restricted";
184 + goto wait_permissions;
191 + pw_log_info(NAME" %p: client %p '%s' access granted", impl, client, access);
192 + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access);
193 + pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1));
195 + permissions[0] = PW_PERMISSION_INIT(PW_ID_ANY, PW_PERM_ALL);
196 + pw_impl_client_update_permissions(client, 1, permissions);
200 + pw_log_info(NAME " %p: client %p wait for '%s' permissions",
201 + impl, client, access);
202 + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access);
203 + pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1));
207 + pw_resource_error(pw_impl_client_get_core_resource(client), res, access);
208 + items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, access);
209 + pw_impl_client_update_properties(client, &SPA_DICT_INIT(items, 1));
213 +static const struct pw_context_events context_events = {
214 + PW_VERSION_CONTEXT_EVENTS,
215 + .check_access = context_check_access,
218 +static void module_destroy(void *data)
220 + struct impl *impl = data;
222 + spa_hook_remove(&impl->context_listener);
223 + spa_hook_remove(&impl->module_listener);
225 + if (impl->properties)
226 + pw_properties_free(impl->properties);
231 +static const struct pw_impl_module_events module_events = {
232 + PW_VERSION_IMPL_MODULE_EVENTS,
233 + .destroy = module_destroy,
237 +int pipewire__module_init(struct pw_impl_module *module, const char *args)
239 + struct pw_context *context = pw_impl_module_get_context(module);
240 + struct pw_properties *props;
243 + impl = calloc(1, sizeof(struct impl));
247 + pw_log_debug(NAME" module %p: new %s", impl, args);
250 + props = pw_properties_new_string(args);
254 + impl->context = context;
255 + impl->properties = props;
257 + pw_context_add_listener(context, &impl->context_listener, &context_events, impl);
258 + pw_impl_module_add_listener(module, &impl->module_listener, &module_events, impl);
260 + pw_impl_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props));