pulseaudio-10.99.1 (#1474559)
This commit is contained in:
parent
708651acb9
commit
db7177be89
@ -1,52 +0,0 @@
|
||||
From 12a76717da7950497ea94bad0ec449c43325ef0a Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Tue, 7 Apr 2015 16:42:31 +0200
|
||||
Subject: [PATCH 01/18] tagstruct: add copy method
|
||||
|
||||
Add a method to copy a tagstruct. This will also reset the read pointer
|
||||
back to the beginning.
|
||||
---
|
||||
src/pulsecore/tagstruct.c | 13 +++++++++++++
|
||||
src/pulsecore/tagstruct.h | 2 ++
|
||||
2 files changed, 15 insertions(+)
|
||||
|
||||
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
|
||||
index 8a29957..c29e49b 100644
|
||||
--- a/src/pulsecore/tagstruct.c
|
||||
+++ b/src/pulsecore/tagstruct.c
|
||||
@@ -97,6 +97,19 @@ void pa_tagstruct_free(pa_tagstruct*t) {
|
||||
pa_xfree(t);
|
||||
}
|
||||
|
||||
+pa_tagstruct *pa_tagstruct_copy(pa_tagstruct*t) {
|
||||
+ pa_tagstruct*tc;
|
||||
+
|
||||
+ if (!(tc = pa_flist_pop(PA_STATIC_FLIST_GET(tagstructs))))
|
||||
+ tc = pa_xnew(pa_tagstruct, 1);
|
||||
+ tc->data = pa_xmemdup(t->data, t->length);
|
||||
+ tc->allocated = t->length;
|
||||
+ tc->rindex = 0;
|
||||
+ tc->type = PA_TAGSTRUCT_DYNAMIC;
|
||||
+
|
||||
+ return tc;
|
||||
+}
|
||||
+
|
||||
static inline void extend(pa_tagstruct*t, size_t l) {
|
||||
pa_assert(t);
|
||||
pa_assert(t->type != PA_TAGSTRUCT_FIXED);
|
||||
diff --git a/src/pulsecore/tagstruct.h b/src/pulsecore/tagstruct.h
|
||||
index 348c65d..e648d75 100644
|
||||
--- a/src/pulsecore/tagstruct.h
|
||||
+++ b/src/pulsecore/tagstruct.h
|
||||
@@ -64,6 +64,8 @@ pa_tagstruct *pa_tagstruct_new(void);
|
||||
pa_tagstruct *pa_tagstruct_new_fixed(const uint8_t* data, size_t length);
|
||||
void pa_tagstruct_free(pa_tagstruct*t);
|
||||
|
||||
+pa_tagstruct *pa_tagstruct_copy(pa_tagstruct*t);
|
||||
+
|
||||
int pa_tagstruct_eof(pa_tagstruct*t);
|
||||
const uint8_t* pa_tagstruct_data(pa_tagstruct*t, size_t *l);
|
||||
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
From 8f43f8ceae382fafcdf59533a5f3776b41c174cd Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Thu, 14 Jul 2016 17:35:54 +0200
|
||||
Subject: [PATCH 02/18] tagstruct: don't forget to copy the length
|
||||
|
||||
---
|
||||
src/pulsecore/tagstruct.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/src/pulsecore/tagstruct.c b/src/pulsecore/tagstruct.c
|
||||
index c29e49b..66ff8df 100644
|
||||
--- a/src/pulsecore/tagstruct.c
|
||||
+++ b/src/pulsecore/tagstruct.c
|
||||
@@ -104,6 +104,7 @@ pa_tagstruct *pa_tagstruct_copy(pa_tagstruct*t) {
|
||||
tc = pa_xnew(pa_tagstruct, 1);
|
||||
tc->data = pa_xmemdup(t->data, t->length);
|
||||
tc->allocated = t->length;
|
||||
+ tc->length = t->length;
|
||||
tc->rindex = 0;
|
||||
tc->type = PA_TAGSTRUCT_DYNAMIC;
|
||||
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
From 4701b6b12e0c950bf96f2cc1db97688894e5fe15 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Tue, 7 Apr 2015 16:44:45 +0200
|
||||
Subject: [PATCH 03/18] subscribe: fix typo
|
||||
|
||||
---
|
||||
src/pulsecore/core-subscribe.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
|
||||
index 61c779b..5a88b7e 100644
|
||||
--- a/src/pulsecore/core-subscribe.c
|
||||
+++ b/src/pulsecore/core-subscribe.c
|
||||
@@ -206,7 +206,7 @@ void pa_subscription_post(pa_core *c, pa_subscription_event_type_t t, uint32_t i
|
||||
pa_subscription_event *e;
|
||||
pa_assert(c);
|
||||
|
||||
- /* No need for queuing subscriptions of no one is listening */
|
||||
+ /* No need for queuing subscriptions if no one is listening */
|
||||
if (!c->subscriptions)
|
||||
return;
|
||||
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,200 +0,0 @@
|
||||
From ea28c5586af179bdc968a420a3d5e7ba42d00029 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Fri, 15 Jul 2016 12:34:31 +0200
|
||||
Subject: [PATCH 04/18] creds: add pid to pa_creds and use store it in
|
||||
pa_client
|
||||
|
||||
Add the pid to the credentials ancillary data and store it in the
|
||||
pa_client object when valid.
|
||||
Add a new hook to be notified when a pa_client it authenticated
|
||||
---
|
||||
src/modules/module-tunnel.c | 1 +
|
||||
src/pulse/context.c | 1 +
|
||||
src/pulsecore/client.h | 4 ++++
|
||||
src/pulsecore/core.h | 1 +
|
||||
src/pulsecore/creds.h | 1 +
|
||||
src/pulsecore/iochannel.c | 4 +++-
|
||||
src/pulsecore/protocol-native.c | 25 +++++++++++++++----------
|
||||
7 files changed, 26 insertions(+), 11 deletions(-)
|
||||
|
||||
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
|
||||
index e08816b..d7114ee 100644
|
||||
--- a/src/modules/module-tunnel.c
|
||||
+++ b/src/modules/module-tunnel.c
|
||||
@@ -1857,6 +1857,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
|
||||
|
||||
ucred.uid = getuid();
|
||||
ucred.gid = getgid();
|
||||
+ ucred.pid = getpid();
|
||||
|
||||
pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
|
||||
}
|
||||
diff --git a/src/pulse/context.c b/src/pulse/context.c
|
||||
index c39cbe7..445973e 100644
|
||||
--- a/src/pulse/context.c
|
||||
+++ b/src/pulse/context.c
|
||||
@@ -642,6 +642,7 @@ static void setup_context(pa_context *c, pa_iochannel *io) {
|
||||
|
||||
ucred.uid = getuid();
|
||||
ucred.gid = getgid();
|
||||
+ ucred.pid = getpid();
|
||||
|
||||
pa_pstream_send_tagstruct_with_creds(c->pstream, t, &ucred);
|
||||
}
|
||||
diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
|
||||
index eb8173d..e34fb1e 100644
|
||||
--- a/src/pulsecore/client.h
|
||||
+++ b/src/pulsecore/client.h
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <pulse/proplist.h>
|
||||
#include <pulsecore/core.h>
|
||||
#include <pulsecore/module.h>
|
||||
+#include <pulsecore/creds.h>
|
||||
|
||||
/* Every connection to the server should have a pa_client
|
||||
* attached. That way the user may generate a listing of all connected
|
||||
@@ -39,6 +40,9 @@ struct pa_client {
|
||||
pa_module *module;
|
||||
char *driver;
|
||||
|
||||
+ pa_creds creds;
|
||||
+ bool creds_valid;
|
||||
+
|
||||
pa_idxset *sink_inputs;
|
||||
pa_idxset *source_outputs;
|
||||
|
||||
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
|
||||
index 802111b..8418289 100644
|
||||
--- a/src/pulsecore/core.h
|
||||
+++ b/src/pulsecore/core.h
|
||||
@@ -114,6 +114,7 @@ typedef enum pa_core_hook {
|
||||
PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
|
||||
PA_CORE_HOOK_CLIENT_NEW,
|
||||
PA_CORE_HOOK_CLIENT_PUT,
|
||||
+ PA_CORE_HOOK_CLIENT_AUTH,
|
||||
PA_CORE_HOOK_CLIENT_UNLINK,
|
||||
PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
|
||||
PA_CORE_HOOK_CLIENT_SEND_EVENT,
|
||||
diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
|
||||
index 9fdbb4f..f489b0d 100644
|
||||
--- a/src/pulsecore/creds.h
|
||||
+++ b/src/pulsecore/creds.h
|
||||
@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cmsg_ancil_data;
|
||||
struct pa_creds {
|
||||
gid_t gid;
|
||||
uid_t uid;
|
||||
+ uid_t pid;
|
||||
};
|
||||
|
||||
/* Struct for handling ancillary data, i e, extra data that can be sent together with a message
|
||||
diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
|
||||
index 8ace297..32fe0f2 100644
|
||||
--- a/src/pulsecore/iochannel.c
|
||||
+++ b/src/pulsecore/iochannel.c
|
||||
@@ -323,13 +323,14 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
|
||||
|
||||
u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
|
||||
|
||||
- u->pid = getpid();
|
||||
if (ucred) {
|
||||
u->uid = ucred->uid;
|
||||
u->gid = ucred->gid;
|
||||
+ u->pid = ucred->pid;
|
||||
} else {
|
||||
u->uid = getuid();
|
||||
u->gid = getgid();
|
||||
+ u->pid = getpid();
|
||||
}
|
||||
|
||||
pa_zero(mh);
|
||||
@@ -439,6 +440,7 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l,
|
||||
|
||||
ancil_data->creds.gid = u.gid;
|
||||
ancil_data->creds.uid = u.uid;
|
||||
+ ancil_data->creds.pid = u.pid;
|
||||
ancil_data->creds_valid = true;
|
||||
}
|
||||
else if (cmh->cmsg_type == SCM_RIGHTS) {
|
||||
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
|
||||
index 13f4f62..8f07b2d 100644
|
||||
--- a/src/pulsecore/protocol-native.c
|
||||
+++ b/src/pulsecore/protocol-native.c
|
||||
@@ -2541,6 +2541,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
pa_tagstruct *reply;
|
||||
pa_mem_type_t shm_type;
|
||||
bool shm_on_remote = false, do_shm;
|
||||
+ const pa_creds *creds = NULL;
|
||||
|
||||
pa_native_connection_assert_ref(c);
|
||||
pa_assert(t);
|
||||
@@ -2578,13 +2579,19 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
|
||||
pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
|
||||
|
||||
+#ifdef HAVE_CREDS
|
||||
+ creds = pa_pdispatch_creds(pd);
|
||||
+#endif
|
||||
+
|
||||
+ if (creds) {
|
||||
+ c->client->creds = *creds;
|
||||
+ c->client->creds_valid = true;
|
||||
+ }
|
||||
+
|
||||
if (!c->authorized) {
|
||||
bool success = false;
|
||||
|
||||
-#ifdef HAVE_CREDS
|
||||
- const pa_creds *creds;
|
||||
-
|
||||
- if ((creds = pa_pdispatch_creds(pd))) {
|
||||
+ if (creds) {
|
||||
if (creds->uid == getuid())
|
||||
success = true;
|
||||
else if (c->options->auth_group) {
|
||||
@@ -2609,7 +2616,6 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
(unsigned long) creds->gid,
|
||||
(int) success);
|
||||
}
|
||||
-#endif
|
||||
|
||||
if (!success && c->options->auth_cookie) {
|
||||
const uint8_t *ac;
|
||||
@@ -2643,17 +2649,13 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
|
||||
do_shm = false;
|
||||
|
||||
-#ifdef HAVE_CREDS
|
||||
if (do_shm) {
|
||||
/* Only enable SHM if both sides are owned by the same
|
||||
* user. This is a security measure because otherwise data
|
||||
* private to the user might leak. */
|
||||
-
|
||||
- const pa_creds *creds;
|
||||
- if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
|
||||
+ if (!creds || getuid() != creds->uid)
|
||||
do_shm = false;
|
||||
}
|
||||
-#endif
|
||||
|
||||
pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
|
||||
pa_pstream_enable_shm(c->pstream, do_shm);
|
||||
@@ -2691,6 +2693,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
|
||||
ucred.uid = getuid();
|
||||
ucred.gid = getgid();
|
||||
+ ucred.pid = getpid();
|
||||
|
||||
pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
|
||||
}
|
||||
@@ -2711,6 +2714,8 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
|
||||
}
|
||||
|
||||
setup_srbchannel(c, shm_type);
|
||||
+
|
||||
+ pa_hook_fire(&c->protocol->core->hooks[PA_CORE_HOOK_CLIENT_AUTH], c->client);
|
||||
}
|
||||
|
||||
static void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,184 +0,0 @@
|
||||
From e158102a2e40e702af97e451c6445b6d851f88f3 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Tue, 7 Apr 2015 16:50:26 +0200
|
||||
Subject: [PATCH 05/18] access: Add access control hooks
|
||||
|
||||
Add hooks to core to check if certain operations are allowed.
|
||||
---
|
||||
src/Makefile.am | 1 +
|
||||
src/pulsecore/access.h | 105 +++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
src/pulsecore/core.c | 5 +++
|
||||
src/pulsecore/core.h | 3 ++
|
||||
4 files changed, 114 insertions(+)
|
||||
create mode 100644 src/pulsecore/access.h
|
||||
|
||||
diff --git a/src/Makefile.am b/src/Makefile.am
|
||||
index 498a386..13682c5 100644
|
||||
--- a/src/Makefile.am
|
||||
+++ b/src/Makefile.am
|
||||
@@ -948,6 +948,7 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \
|
||||
pulsecore/filter/lfe-filter.c pulsecore/filter/lfe-filter.h \
|
||||
pulsecore/filter/biquad.c pulsecore/filter/biquad.h \
|
||||
pulsecore/filter/crossover.c pulsecore/filter/crossover.h \
|
||||
+ pulsecore/access.h \
|
||||
pulsecore/asyncmsgq.c pulsecore/asyncmsgq.h \
|
||||
pulsecore/asyncq.c pulsecore/asyncq.h \
|
||||
pulsecore/auth-cookie.c pulsecore/auth-cookie.h \
|
||||
diff --git a/src/pulsecore/access.h b/src/pulsecore/access.h
|
||||
new file mode 100644
|
||||
index 0000000..534f66c
|
||||
--- /dev/null
|
||||
+++ b/src/pulsecore/access.h
|
||||
@@ -0,0 +1,105 @@
|
||||
+#ifndef fooaccesshfoo
|
||||
+#define fooaccesshfoo
|
||||
+
|
||||
+/***
|
||||
+ This file is part of PulseAudio.
|
||||
+
|
||||
+ Copyright 2004-2006 Lennart Poettering
|
||||
+ 2015 Wim Taymans <wtaymans@redhat.com>
|
||||
+
|
||||
+ PulseAudio is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU Lesser General Public License as
|
||||
+ published by the Free Software Foundation; either version 2.1 of the
|
||||
+ License, or (at your option) any later version.
|
||||
+
|
||||
+ PulseAudio is distributed in the hope that it will be useful, but
|
||||
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
+***/
|
||||
+
|
||||
+#include <sys/types.h>
|
||||
+
|
||||
+#include <pulsecore/client.h>
|
||||
+
|
||||
+typedef enum pa_access_hook {
|
||||
+ PA_ACCESS_HOOK_NONE = 0,
|
||||
+ /* context */
|
||||
+ PA_ACCESS_HOOK_VIEW_SERVER,
|
||||
+ PA_ACCESS_HOOK_EXIT_DAEMON,
|
||||
+ PA_ACCESS_HOOK_SET_DEFAULT_SINK,
|
||||
+ PA_ACCESS_HOOK_SET_DEFAULT_SOURCE,
|
||||
+ PA_ACCESS_HOOK_STAT,
|
||||
+
|
||||
+ /* introspection */
|
||||
+ PA_ACCESS_HOOK_VIEW_SINK,
|
||||
+ PA_ACCESS_HOOK_SET_SINK_VOLUME,
|
||||
+ PA_ACCESS_HOOK_SUSPEND_SINK,
|
||||
+ PA_ACCESS_HOOK_SET_SINK_PORT,
|
||||
+ PA_ACCESS_HOOK_SET_SINK_PORT_LATENCY_OFFSET,
|
||||
+ PA_ACCESS_HOOK_ENTER_PASSTHROUGH_SINK,
|
||||
+ PA_ACCESS_HOOK_LEAVE_PASSTHROUGH_SINK,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_SOURCE,
|
||||
+ PA_ACCESS_HOOK_SET_SOURCE_VOLUME,
|
||||
+ PA_ACCESS_HOOK_SUSPEND_SOURCE,
|
||||
+ PA_ACCESS_HOOK_SET_SOURCE_PORT,
|
||||
+ PA_ACCESS_HOOK_SET_SOURCE_PORT_LATENCY_OFFSET,
|
||||
+ PA_ACCESS_HOOK_ENTER_PASSTHROUGH_SOURCE,
|
||||
+ PA_ACCESS_HOOK_LEAVE_PASSTHROUGH_SOURCE,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_MODULE,
|
||||
+ PA_ACCESS_HOOK_LOAD_MODULE,
|
||||
+ PA_ACCESS_HOOK_UNLOAD_MODULE,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_CLIENT,
|
||||
+ PA_ACCESS_HOOK_KILL_CLIENT,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_CARD,
|
||||
+ PA_ACCESS_HOOK_SET_CARD_PROFILE,
|
||||
+ PA_ACCESS_HOOK_SUSPEND_CARD,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_SINK_INPUT,
|
||||
+ PA_ACCESS_HOOK_CREATE_SINK_INPUT,
|
||||
+ PA_ACCESS_HOOK_MOVE_SINK_INPUT,
|
||||
+ PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME,
|
||||
+ PA_ACCESS_HOOK_KILL_SINK_INPUT,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT,
|
||||
+ PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT,
|
||||
+ PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT,
|
||||
+ PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME,
|
||||
+ PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT,
|
||||
+
|
||||
+ /* sample cache */
|
||||
+ PA_ACCESS_HOOK_VIEW_SAMPLE,
|
||||
+ PA_ACCESS_HOOK_ADD_SAMPLE,
|
||||
+ PA_ACCESS_HOOK_REMOVE_SAMPLE,
|
||||
+ PA_ACCESS_HOOK_PLAY_SAMPLE,
|
||||
+ PA_ACCESS_HOOK_PLAY_FILE,
|
||||
+
|
||||
+ /* async */
|
||||
+ PA_ACCESS_HOOK_CONNECT_UPLOAD,
|
||||
+ PA_ACCESS_HOOK_CONNECT_PLAYBACK,
|
||||
+ PA_ACCESS_HOOK_CONNECT_RECORD,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT,
|
||||
+
|
||||
+ PA_ACCESS_HOOK_MAX
|
||||
+} pa_access_hook_t;
|
||||
+
|
||||
+typedef struct pa_access_data pa_access_data;
|
||||
+
|
||||
+struct pa_access_data {
|
||||
+ pa_access_hook_t hook;
|
||||
+ uint32_t client_index;
|
||||
+ uint32_t object_index;
|
||||
+ pa_subscription_event_type_t event;
|
||||
+ const char *name;
|
||||
+ void (*complete_cb) (pa_access_data *data, bool res);
|
||||
+};
|
||||
+
|
||||
+#endif
|
||||
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
|
||||
index 2a96dfa..e5d6879 100644
|
||||
--- a/src/pulsecore/core.c
|
||||
+++ b/src/pulsecore/core.c
|
||||
@@ -150,6 +150,9 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t
|
||||
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
|
||||
pa_hook_init(&c->hooks[j], c);
|
||||
|
||||
+ for (j = 0; j < PA_ACCESS_HOOK_MAX; j++)
|
||||
+ pa_hook_init(&c->access[j], c);
|
||||
+
|
||||
pa_random(&c->cookie, sizeof(c->cookie));
|
||||
|
||||
#ifdef SIGPIPE
|
||||
@@ -219,6 +222,8 @@ static void core_free(pa_object *o) {
|
||||
|
||||
for (j = 0; j < PA_CORE_HOOK_MAX; j++)
|
||||
pa_hook_done(&c->hooks[j]);
|
||||
+ for (j = 0; j < PA_ACCESS_HOOK_MAX; j++)
|
||||
+ pa_hook_done(&c->access[j]);
|
||||
|
||||
pa_xfree(c);
|
||||
}
|
||||
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
|
||||
index 8418289..152a53f 100644
|
||||
--- a/src/pulsecore/core.h
|
||||
+++ b/src/pulsecore/core.h
|
||||
@@ -49,6 +49,7 @@ typedef enum pa_suspend_cause {
|
||||
#include <pulsecore/source.h>
|
||||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/msgobject.h>
|
||||
+#include <pulsecore/access.h>
|
||||
|
||||
typedef enum pa_server_type {
|
||||
PA_SERVER_TYPE_UNSET,
|
||||
@@ -212,6 +213,8 @@ struct pa_core {
|
||||
|
||||
/* hooks */
|
||||
pa_hook hooks[PA_CORE_HOOK_MAX];
|
||||
+ /* access hooks */
|
||||
+ pa_hook access[PA_ACCESS_HOOK_MAX];
|
||||
};
|
||||
|
||||
PA_DECLARE_PUBLIC_CLASS(pa_core);
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,539 +0,0 @@
|
||||
From 1eec5a0d01a657536cf8afb14d295d0f050ddd1d Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Tue, 7 Apr 2015 17:01:58 +0200
|
||||
Subject: [PATCH 06/18] module-access: add example access module
|
||||
|
||||
Add an example access module that only allows access to the objects
|
||||
created by the owner client.
|
||||
|
||||
The code is structured in such a way that multiple profiles can be
|
||||
made and that a profile can be activated based on the properties of
|
||||
a client.
|
||||
|
||||
Events need special handling, we don't want to send events to clients
|
||||
about objects they are not allowed to see. We need to be careful because
|
||||
we can't inspect the owner of an object anymore in a _REMOVE event for
|
||||
that object. We fix this by keeping a list of all the objects for which
|
||||
we were allowed to notify a CHANGE or NEW event. We then only send
|
||||
REMOVE events to objects from this list.
|
||||
---
|
||||
src/Makefile.am | 7 +
|
||||
src/modules/module-access.c | 474 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 481 insertions(+)
|
||||
create mode 100644 src/modules/module-access.c
|
||||
|
||||
diff --git a/src/Makefile.am b/src/Makefile.am
|
||||
index 13682c5..7c66064 100644
|
||||
--- a/src/Makefile.am
|
||||
+++ b/src/Makefile.am
|
||||
@@ -1178,6 +1178,7 @@ modlibexec_LTLIBRARIES += \
|
||||
endif
|
||||
|
||||
modlibexec_LTLIBRARIES += \
|
||||
+ module-access.la \
|
||||
module-cli.la \
|
||||
module-cli-protocol-tcp.la \
|
||||
module-simple-protocol-tcp.la \
|
||||
@@ -1470,6 +1471,7 @@ endif
|
||||
|
||||
# These are generated by an M4 script
|
||||
SYMDEF_FILES = \
|
||||
+ module-access-symdef.h \
|
||||
module-cli-symdef.h \
|
||||
module-cli-protocol-tcp-symdef.h \
|
||||
module-cli-protocol-unix-symdef.h \
|
||||
@@ -1592,6 +1594,11 @@ module_simple_protocol_unix_la_CFLAGS = -DUSE_UNIX_SOCKETS -DUSE_PROTOCOL_SIMPLE
|
||||
module_simple_protocol_unix_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
module_simple_protocol_unix_la_LIBADD = $(MODULE_LIBADD) libprotocol-simple.la
|
||||
|
||||
+# Access control
|
||||
+module_access_la_SOURCES = modules/module-access.c
|
||||
+module_access_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
+module_access_la_LIBADD = $(MODULE_LIBADD)
|
||||
+
|
||||
# CLI protocol
|
||||
|
||||
module_cli_la_SOURCES = modules/module-cli.c
|
||||
diff --git a/src/modules/module-access.c b/src/modules/module-access.c
|
||||
new file mode 100644
|
||||
index 0000000..12deccb
|
||||
--- /dev/null
|
||||
+++ b/src/modules/module-access.c
|
||||
@@ -0,0 +1,474 @@
|
||||
+/***
|
||||
+ This file is part of PulseAudio.
|
||||
+
|
||||
+ Copyright 2004-2006 Lennart Poettering
|
||||
+ 2015 Wim Taymans <wtaymans@redhat.com>
|
||||
+
|
||||
+ PulseAudio is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU Lesser General Public License as published
|
||||
+ by the Free Software Foundation; either version 2.1 of the License,
|
||||
+ or (at your option) any later version.
|
||||
+
|
||||
+ PulseAudio is distributed in the hope that it will be useful, but
|
||||
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public License
|
||||
+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
+***/
|
||||
+
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <unistd.h>
|
||||
+#include <string.h>
|
||||
+#include <errno.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+#include <pulse/xmalloc.h>
|
||||
+
|
||||
+#include <pulsecore/core-error.h>
|
||||
+#include <pulsecore/module.h>
|
||||
+#include <pulsecore/core-util.h>
|
||||
+#include <pulsecore/modargs.h>
|
||||
+#include <pulsecore/log.h>
|
||||
+#include <pulsecore/sink-input.h>
|
||||
+#include <pulsecore/core-util.h>
|
||||
+
|
||||
+#include "module-access-symdef.h"
|
||||
+
|
||||
+PA_MODULE_AUTHOR("Wim Taymans");
|
||||
+PA_MODULE_DESCRIPTION("Controls access to server resources");
|
||||
+PA_MODULE_VERSION(PACKAGE_VERSION);
|
||||
+PA_MODULE_LOAD_ONCE(true);
|
||||
+PA_MODULE_USAGE("");
|
||||
+
|
||||
+static const char* const valid_modargs[] = {
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+typedef struct access_policy access_policy;
|
||||
+typedef struct event_item event_item;
|
||||
+typedef struct client_data client_data;
|
||||
+typedef struct userdata userdata;
|
||||
+
|
||||
+typedef pa_hook_result_t (*access_rule_t)(pa_core *c, pa_access_data *d, struct userdata *u);
|
||||
+
|
||||
+struct access_policy {
|
||||
+ uint32_t index;
|
||||
+ struct userdata *userdata;
|
||||
+
|
||||
+ access_rule_t rule[PA_ACCESS_HOOK_MAX];
|
||||
+};
|
||||
+
|
||||
+struct event_item {
|
||||
+ PA_LLIST_FIELDS(event_item);
|
||||
+
|
||||
+ int facility;
|
||||
+ uint32_t object_index;
|
||||
+};
|
||||
+struct client_data {
|
||||
+ uint32_t index;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ PA_LLIST_HEAD(event_item, events);
|
||||
+};
|
||||
+
|
||||
+struct userdata {
|
||||
+ pa_core *core;
|
||||
+
|
||||
+ pa_hook_slot *hook[PA_ACCESS_HOOK_MAX];
|
||||
+
|
||||
+ pa_idxset *policies;
|
||||
+ uint32_t default_policy;
|
||||
+
|
||||
+ pa_hashmap *clients;
|
||||
+ pa_hook_slot *client_put_slot;
|
||||
+ pa_hook_slot *client_proplist_changed_slot;
|
||||
+ pa_hook_slot *client_unlink_slot;
|
||||
+};
|
||||
+
|
||||
+static void add_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i;
|
||||
+
|
||||
+ i = pa_xnew0(event_item, 1);
|
||||
+ PA_LLIST_INIT(event_item, i);
|
||||
+ i->facility = facility;
|
||||
+ i->object_index = oidx;
|
||||
+
|
||||
+ PA_LLIST_PREPEND(event_item, cd->events, i);
|
||||
+}
|
||||
+
|
||||
+static event_item *find_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i;
|
||||
+
|
||||
+ PA_LLIST_FOREACH(i, cd->events) {
|
||||
+ if (i->facility == facility && i->object_index == oidx)
|
||||
+ return i;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static bool remove_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i = find_event(cd, facility, oidx);
|
||||
+ if (i) {
|
||||
+ PA_LLIST_REMOVE(event_item, cd->events, i);
|
||||
+ pa_xfree(i);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static client_data * client_data_new(struct userdata *u, uint32_t index, uint32_t policy) {
|
||||
+ client_data *cd;
|
||||
+
|
||||
+ cd = pa_xnew0(client_data, 1);
|
||||
+ cd->index = index;
|
||||
+ cd->policy = policy;
|
||||
+ pa_hashmap_put(u->clients, PA_UINT32_TO_PTR(index), cd);
|
||||
+ pa_log("new client %d with policy %d", index, policy);
|
||||
+
|
||||
+ return cd;
|
||||
+}
|
||||
+
|
||||
+static void client_data_free(client_data *cd) {
|
||||
+ event_item *e;
|
||||
+
|
||||
+ while ((e = cd->events)) {
|
||||
+ PA_LLIST_REMOVE(event_item, cd->events, e);
|
||||
+ pa_xfree(e);
|
||||
+ }
|
||||
+ pa_log("removed client %d", cd->index);
|
||||
+ pa_xfree(cd);
|
||||
+}
|
||||
+
|
||||
+static client_data * client_data_get(struct userdata *u, uint32_t index) {
|
||||
+ return pa_hashmap_get(u->clients, PA_UINT32_TO_PTR(index));
|
||||
+}
|
||||
+
|
||||
+static void client_data_remove(struct userdata *u, uint32_t index) {
|
||||
+ pa_hashmap_remove_and_free(u->clients, PA_UINT32_TO_PTR(index));
|
||||
+}
|
||||
+
|
||||
+/* rule checks if the operation on the object is performed by the owner of the object */
|
||||
+static pa_hook_result_t rule_check_owner (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_hook_result_t result = PA_HOOK_STOP;
|
||||
+ uint32_t idx = PA_INVALID_INDEX;
|
||||
+
|
||||
+ switch (d->hook) {
|
||||
+ case PA_ACCESS_HOOK_VIEW_CLIENT:
|
||||
+ case PA_ACCESS_HOOK_KILL_CLIENT: {
|
||||
+ idx = d->object_index;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ case PA_ACCESS_HOOK_VIEW_SINK_INPUT:
|
||||
+ case PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME:
|
||||
+ case PA_ACCESS_HOOK_KILL_SINK_INPUT: {
|
||||
+ const pa_sink_input *si = pa_idxset_get_by_index(c->sink_inputs, d->object_index);
|
||||
+ idx = (si && si->client) ? si->client->index : PA_INVALID_INDEX;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ case PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT:
|
||||
+ case PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME:
|
||||
+ case PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT: {
|
||||
+ const pa_source_output *so = pa_idxset_get_by_index(c->source_outputs, d->object_index);
|
||||
+ idx = (so && so->client) ? so->client->index : PA_INVALID_INDEX;
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ if (idx == d->client_index) {
|
||||
+ pa_log("allow operation %d/%d of same client %d", d->hook, d->object_index, idx);
|
||||
+ result = PA_HOOK_OK;
|
||||
+ }
|
||||
+ else
|
||||
+ pa_log("blocked operation %d/%d of client %d to client %d", d->hook, d->object_index, idx, d->client_index);
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+/* rule allows the operation */
|
||||
+static pa_hook_result_t rule_allow (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_log("allow operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+/* rule blocks the operation */
|
||||
+static pa_hook_result_t rule_block (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_log("blocked operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static access_policy *access_policy_new(struct userdata *u, bool allow_all) {
|
||||
+ access_policy *ap;
|
||||
+ int i;
|
||||
+
|
||||
+ ap = pa_xnew0(access_policy, 1);
|
||||
+ ap->userdata = u;
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++)
|
||||
+ ap->rule[i] = allow_all ? rule_allow : rule_block;
|
||||
+
|
||||
+ pa_idxset_put(u->policies, ap, &ap->index);
|
||||
+
|
||||
+ return ap;
|
||||
+}
|
||||
+
|
||||
+static void access_policy_free(access_policy *ap) {
|
||||
+ pa_idxset_remove_by_index(ap->userdata->policies, ap->index);
|
||||
+ pa_xfree(ap);
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t check_access (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ access_policy *ap;
|
||||
+ access_rule_t rule;
|
||||
+ client_data *cd = client_data_get(u, d->client_index);
|
||||
+
|
||||
+ /* unknown client */
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_STOP;
|
||||
+
|
||||
+ ap = pa_idxset_get_by_index(u->policies, cd->policy);
|
||||
+
|
||||
+ rule = ap->rule[d->hook];
|
||||
+ if (rule)
|
||||
+ return rule(c, d, u);
|
||||
+
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static const pa_access_hook_t event_hook[PA_SUBSCRIPTION_EVENT_FACILITY_MASK+1] = {
|
||||
+ [PA_SUBSCRIPTION_EVENT_SINK] = PA_ACCESS_HOOK_VIEW_SINK,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SOURCE] = PA_ACCESS_HOOK_VIEW_SOURCE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SINK_INPUT] = PA_ACCESS_HOOK_VIEW_SINK_INPUT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT] = PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_MODULE] = PA_ACCESS_HOOK_VIEW_MODULE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_CLIENT] = PA_ACCESS_HOOK_VIEW_CLIENT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE] = PA_ACCESS_HOOK_VIEW_SAMPLE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SERVER] = PA_ACCESS_HOOK_VIEW_SERVER,
|
||||
+ [PA_SUBSCRIPTION_EVENT_CARD] = PA_ACCESS_HOOK_VIEW_CARD
|
||||
+};
|
||||
+
|
||||
+
|
||||
+
|
||||
+static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ int facility;
|
||||
+ client_data *cd;
|
||||
+
|
||||
+ facility = d->event & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
|
||||
+
|
||||
+ cd = client_data_get (u, d->client_index);
|
||||
+ /* unknown client destination, block event */
|
||||
+ if (cd == NULL)
|
||||
+ goto block;
|
||||
+
|
||||
+ switch (d->event & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
||||
+ case PA_SUBSCRIPTION_EVENT_REMOVE:
|
||||
+ /* if the client saw this object before, let the event go through */
|
||||
+ if (remove_event(cd, facility, d->object_index)) {
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case PA_SUBSCRIPTION_EVENT_CHANGE:
|
||||
+ /* if the client saw this object before, let it go through */
|
||||
+ if (find_event(cd, facility, d->object_index)) {
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+
|
||||
+ /* fallthrough to do hook check and register event */
|
||||
+ case PA_SUBSCRIPTION_EVENT_NEW: {
|
||||
+ pa_access_data data = *d;
|
||||
+
|
||||
+ /* new object, check if the client is allowed to inspect it */
|
||||
+ data.hook = event_hook[facility];
|
||||
+ if (data.hook && pa_hook_fire(&c->access[data.hook], &data) == PA_HOOK_OK) {
|
||||
+ /* client can inspect the object, remember for later */
|
||||
+ add_event(cd, facility, d->object_index);
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+block:
|
||||
+ pa_log("blocked event %02x/%d for client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static uint32_t find_policy_for_client (struct userdata *u, pa_client *cl) {
|
||||
+ char *s;
|
||||
+
|
||||
+ s = pa_proplist_to_string(cl->proplist);
|
||||
+ pa_log ("client proplist %s", s);
|
||||
+ pa_xfree(s);
|
||||
+
|
||||
+ return u->default_policy;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+
|
||||
+ client_data_new(u, cl->index, policy);
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_proplist_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ client_data *cd;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ cd = client_data_get (u, cl->index);
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_OK;
|
||||
+
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+ cd->policy = policy;
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ client_data_remove(u, cl->index);
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int pa__init(pa_module*m) {
|
||||
+ pa_modargs *ma = NULL;
|
||||
+ struct userdata *u;
|
||||
+ int i;
|
||||
+ access_policy *ap;
|
||||
+
|
||||
+ pa_assert(m);
|
||||
+
|
||||
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
+ pa_log("Failed to parse module arguments");
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ u = pa_xnew0(struct userdata, 1);
|
||||
+ u->core = m->core;
|
||||
+ m->userdata = u;
|
||||
+
|
||||
+ u->policies = pa_idxset_new (NULL, NULL);
|
||||
+ u->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
|
||||
+ (pa_free_cb_t) client_data_free);
|
||||
+
|
||||
+ u->client_put_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) client_put_cb, u);
|
||||
+ u->client_proplist_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) client_proplist_changed_cb, u);
|
||||
+ u->client_unlink_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) client_unlink_cb, u);
|
||||
+
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++) {
|
||||
+ pa_hook_cb_t cb;
|
||||
+
|
||||
+ if (i == PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT)
|
||||
+ cb = (pa_hook_cb_t) filter_event;
|
||||
+ else
|
||||
+ cb = (pa_hook_cb_t) check_access;
|
||||
+
|
||||
+ u->hook[i] = pa_hook_connect(&u->core->access[i], PA_HOOK_EARLY - 1, cb, u);
|
||||
+ }
|
||||
+
|
||||
+ ap = access_policy_new(u, false);
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SERVER] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_MODULE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CARD] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_STAT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SAMPLE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_PLAY_SAMPLE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_PLAYBACK] = rule_allow;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_CLIENT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SINK_INPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SINK_INPUT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+
|
||||
+ u->default_policy = ap->index;
|
||||
+
|
||||
+ pa_modargs_free(ma);
|
||||
+ return 0;
|
||||
+
|
||||
+fail:
|
||||
+ pa__done(m);
|
||||
+
|
||||
+ if (ma)
|
||||
+ pa_modargs_free(ma);
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+void pa__done(pa_module*m) {
|
||||
+ struct userdata* u;
|
||||
+ int i;
|
||||
+
|
||||
+ pa_assert(m);
|
||||
+
|
||||
+ if (!(u = m->userdata))
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++) {
|
||||
+ if (u->hook[i])
|
||||
+ pa_hook_slot_free(u->hook[i]);
|
||||
+ }
|
||||
+
|
||||
+ if (u->policies)
|
||||
+ pa_idxset_free(u->policies, (pa_free_cb_t) access_policy_free);
|
||||
+
|
||||
+ if (u->client_put_slot)
|
||||
+ pa_hook_slot_free(u->client_put_slot);
|
||||
+ if (u->client_proplist_changed_slot)
|
||||
+ pa_hook_slot_free(u->client_proplist_changed_slot);
|
||||
+ if (u->client_unlink_slot)
|
||||
+ pa_hook_slot_free(u->client_unlink_slot);
|
||||
+
|
||||
+ if (u->clients)
|
||||
+ pa_hashmap_free(u->clients);
|
||||
+
|
||||
+ pa_xfree(u);
|
||||
+}
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,140 +0,0 @@
|
||||
From 8f43958cf8e0aa7ba45cddbae3952e831fc9a08f Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Thu, 14 Jul 2016 17:38:01 +0200
|
||||
Subject: [PATCH 07/18] module-access: add async handler for record
|
||||
|
||||
Use an async handler to check for record access. This also shows how
|
||||
we need to remember the result for when the access check is issued
|
||||
again after the async reply.
|
||||
---
|
||||
src/modules/module-access.c | 65 ++++++++++++++++++++++++++++++++++++++++++---
|
||||
1 file changed, 61 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/modules/module-access.c b/src/modules/module-access.c
|
||||
index 12deccb..290d052 100644
|
||||
--- a/src/modules/module-access.c
|
||||
+++ b/src/modules/module-access.c
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <pulse/xmalloc.h>
|
||||
+#include <pulse/rtclock.h>
|
||||
+#include <pulse/timeval.h>
|
||||
|
||||
#include <pulsecore/core-error.h>
|
||||
#include <pulsecore/module.h>
|
||||
@@ -71,11 +73,10 @@ struct event_item {
|
||||
int facility;
|
||||
uint32_t object_index;
|
||||
};
|
||||
-struct client_data {
|
||||
- uint32_t index;
|
||||
- uint32_t policy;
|
||||
|
||||
- PA_LLIST_HEAD(event_item, events);
|
||||
+struct async_cache {
|
||||
+ bool checked;
|
||||
+ bool granted;
|
||||
};
|
||||
|
||||
struct userdata {
|
||||
@@ -92,6 +93,20 @@ struct userdata {
|
||||
pa_hook_slot *client_unlink_slot;
|
||||
};
|
||||
|
||||
+struct client_data {
|
||||
+ struct userdata *u;
|
||||
+
|
||||
+ uint32_t index;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ struct async_cache cached[PA_ACCESS_HOOK_MAX];
|
||||
+ pa_time_event *time_event;
|
||||
+ pa_access_data *access_data;
|
||||
+
|
||||
+ PA_LLIST_HEAD(event_item, events);
|
||||
+};
|
||||
+
|
||||
+
|
||||
static void add_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
event_item *i;
|
||||
|
||||
@@ -123,12 +138,29 @@ static bool remove_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
+static void timeout_cb(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *data) {
|
||||
+ client_data *cd = data;
|
||||
+ struct userdata *u = cd->u;
|
||||
+ pa_access_data *d = cd->access_data;
|
||||
+
|
||||
+ pa_log("async check finished of operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ u->core->mainloop->time_restart(cd->time_event, NULL);
|
||||
+
|
||||
+ cd->cached[d->hook].checked = true;
|
||||
+ /* this should be granted or denied */
|
||||
+ cd->cached[d->hook].granted = true;
|
||||
+
|
||||
+ d->complete_cb (d, cd->cached[d->hook].granted);
|
||||
+}
|
||||
+
|
||||
static client_data * client_data_new(struct userdata *u, uint32_t index, uint32_t policy) {
|
||||
client_data *cd;
|
||||
|
||||
cd = pa_xnew0(client_data, 1);
|
||||
+ cd->u = u;
|
||||
cd->index = index;
|
||||
cd->policy = policy;
|
||||
+ cd->time_event = pa_core_rttime_new(u->core, PA_USEC_INVALID, timeout_cb, cd);
|
||||
pa_hashmap_put(u->clients, PA_UINT32_TO_PTR(index), cd);
|
||||
pa_log("new client %d with policy %d", index, policy);
|
||||
|
||||
@@ -143,6 +175,7 @@ static void client_data_free(client_data *cd) {
|
||||
pa_xfree(e);
|
||||
}
|
||||
pa_log("removed client %d", cd->index);
|
||||
+ cd->u->core->mainloop->time_free(cd->time_event);
|
||||
pa_xfree(cd);
|
||||
}
|
||||
|
||||
@@ -206,6 +239,27 @@ static pa_hook_result_t rule_block (pa_core *c, pa_access_data *d, struct userda
|
||||
return PA_HOOK_STOP;
|
||||
}
|
||||
|
||||
+
|
||||
+/* rule asynchronously checks the operation */
|
||||
+static pa_hook_result_t rule_check_async (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_usec_t now;
|
||||
+ client_data *cd = client_data_get(u, d->client_index);
|
||||
+
|
||||
+ pa_log("async check of operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+
|
||||
+ if (cd->cached[d->hook].checked)
|
||||
+ return cd->cached[d->hook].granted ? PA_HOOK_OK : PA_HOOK_STOP;
|
||||
+
|
||||
+ /* save access_data */
|
||||
+ cd->access_data = d;
|
||||
+
|
||||
+ now = pa_rtclock_now();
|
||||
+ pa_core_rttime_restart (u->core, cd->time_event, now + 2 * PA_USEC_PER_SEC);
|
||||
+
|
||||
+ return PA_HOOK_CANCEL;
|
||||
+}
|
||||
+
|
||||
+
|
||||
static access_policy *access_policy_new(struct userdata *u, bool allow_all) {
|
||||
access_policy *ap;
|
||||
int i;
|
||||
@@ -418,6 +472,9 @@ int pa__init(pa_module*m) {
|
||||
ap->rule[PA_ACCESS_HOOK_PLAY_SAMPLE] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_CONNECT_PLAYBACK] = rule_allow;
|
||||
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_RECORD] = rule_check_async;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CLIENT] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_KILL_CLIENT] = rule_check_owner;
|
||||
|
||||
ap->rule[PA_ACCESS_HOOK_CREATE_SINK_INPUT] = rule_allow;
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,90 +0,0 @@
|
||||
From 8fcf8065b8329ab9abe14ecddc1040e14adc6461 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Fri, 15 Jul 2016 12:36:45 +0200
|
||||
Subject: [PATCH 08/18] module-access: use the auth hook and pid
|
||||
|
||||
Connect to the client_auth hook and also check the pid from the
|
||||
credentials to find the right policy for a client.
|
||||
---
|
||||
src/modules/module-access.c | 30 ++++++++++++++++++++++++++++++
|
||||
1 file changed, 30 insertions(+)
|
||||
|
||||
diff --git a/src/modules/module-access.c b/src/modules/module-access.c
|
||||
index 290d052..c00612a 100644
|
||||
--- a/src/modules/module-access.c
|
||||
+++ b/src/modules/module-access.c
|
||||
@@ -89,6 +89,7 @@ struct userdata {
|
||||
|
||||
pa_hashmap *clients;
|
||||
pa_hook_slot *client_put_slot;
|
||||
+ pa_hook_slot *client_auth_slot;
|
||||
pa_hook_slot *client_proplist_changed_slot;
|
||||
pa_hook_slot *client_unlink_slot;
|
||||
};
|
||||
@@ -368,6 +369,9 @@ static uint32_t find_policy_for_client (struct userdata *u, pa_client *cl) {
|
||||
pa_log ("client proplist %s", s);
|
||||
pa_xfree(s);
|
||||
|
||||
+ if (cl->creds_valid) {
|
||||
+ pa_log ("client has trusted pid %d", cl->creds.pid);
|
||||
+ }
|
||||
return u->default_policy;
|
||||
}
|
||||
|
||||
@@ -381,6 +385,8 @@ static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata
|
||||
cl = (pa_client *) o;
|
||||
pa_assert(cl);
|
||||
|
||||
+ /* when we get here, the client just connected and is not yet authenticated
|
||||
+ * we should probably install a policy that denies all access */
|
||||
policy = find_policy_for_client(u, cl);
|
||||
|
||||
client_data_new(u, cl->index, policy);
|
||||
@@ -388,6 +394,27 @@ static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
+static pa_hook_result_t client_auth_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ client_data *cd;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ cd = client_data_get (u, cl->index);
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_OK;
|
||||
+
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+ cd->policy = policy;
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
static pa_hook_result_t client_proplist_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
pa_client *cl;
|
||||
client_data *cd;
|
||||
@@ -446,6 +473,7 @@ int pa__init(pa_module*m) {
|
||||
(pa_free_cb_t) client_data_free);
|
||||
|
||||
u->client_put_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) client_put_cb, u);
|
||||
+ u->client_auth_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_AUTH], PA_HOOK_EARLY, (pa_hook_cb_t) client_auth_cb, u);
|
||||
u->client_proplist_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) client_proplist_changed_cb, u);
|
||||
u->client_unlink_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) client_unlink_cb, u);
|
||||
|
||||
@@ -519,6 +547,8 @@ void pa__done(pa_module*m) {
|
||||
|
||||
if (u->client_put_slot)
|
||||
pa_hook_slot_free(u->client_put_slot);
|
||||
+ if (u->client_auth_slot)
|
||||
+ pa_hook_slot_free(u->client_auth_slot);
|
||||
if (u->client_proplist_changed_slot)
|
||||
pa_hook_slot_free(u->client_proplist_changed_slot);
|
||||
if (u->client_unlink_slot)
|
||||
--
|
||||
2.9.3
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,840 +0,0 @@
|
||||
From 70721d64d1bd4428d59cb4038eb56d9d79cf24fc Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Fri, 27 Jan 2017 16:41:41 +0100
|
||||
Subject: [PATCH 11/18] Add access checks
|
||||
|
||||
Put some access control checks in various core objects
|
||||
---
|
||||
src/pulsecore/card.c | 6 ++
|
||||
src/pulsecore/client.c | 3 +
|
||||
src/pulsecore/core-scache.c | 12 +++
|
||||
src/pulsecore/core-subscribe.c | 6 +-
|
||||
src/pulsecore/core.c | 176 ++++++++++++++++++++++++++++++++++----
|
||||
src/pulsecore/core.h | 4 +
|
||||
src/pulsecore/module.c | 18 ++++
|
||||
src/pulsecore/namereg.c | 27 +++++-
|
||||
src/pulsecore/protocol-native.c | 1 +
|
||||
src/pulsecore/sink-input.c | 27 ++++++
|
||||
src/pulsecore/sink.c | 22 +++++
|
||||
src/pulsecore/sound-file-stream.c | 3 +
|
||||
src/pulsecore/source-output.c | 21 +++++
|
||||
src/pulsecore/source.c | 21 +++++
|
||||
14 files changed, 325 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
|
||||
index 39f4261..5018d07 100644
|
||||
--- a/src/pulsecore/card.c
|
||||
+++ b/src/pulsecore/card.c
|
||||
@@ -296,6 +296,9 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
|
||||
pa_assert(profile);
|
||||
pa_assert(profile->card == c);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c->core, PA_ACCESS_HOOK_SET_CARD_PROFILE, c->index, 0, NULL))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (!c->set_profile) {
|
||||
pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
|
||||
return -PA_ERR_NOTIMPLEMENTED;
|
||||
@@ -378,6 +381,9 @@ int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
|
||||
pa_assert(c);
|
||||
pa_assert(cause != 0);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c->core, PA_ACCESS_HOOK_SUSPEND_CARD, c->index, 0, NULL))
|
||||
+ return -1;
|
||||
+
|
||||
suspend_cause = c->suspend_cause;
|
||||
|
||||
if (suspend)
|
||||
diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c
|
||||
index 24faa1f..fa10ae1 100644
|
||||
--- a/src/pulsecore/client.c
|
||||
+++ b/src/pulsecore/client.c
|
||||
@@ -112,6 +112,9 @@ void pa_client_free(pa_client *c) {
|
||||
void pa_client_kill(pa_client *c) {
|
||||
pa_assert(c);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c->core, PA_ACCESS_HOOK_KILL_CLIENT, c->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!c->kill) {
|
||||
pa_log_warn("kill() operation not implemented for client %u", c->index);
|
||||
return;
|
||||
diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c
|
||||
index 8d54720..4eaa997 100644
|
||||
--- a/src/pulsecore/core-scache.c
|
||||
+++ b/src/pulsecore/core-scache.c
|
||||
@@ -97,6 +97,9 @@ static pa_scache_entry* scache_add_item(pa_core *c, const char *name, bool *new_
|
||||
pa_assert(name);
|
||||
pa_assert(new_sample);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_ADD_SAMPLE, PA_INVALID_INDEX, 0, name))
|
||||
+ return NULL;
|
||||
+
|
||||
if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) {
|
||||
if (e->memchunk.memblock)
|
||||
pa_memblock_unref(e->memchunk.memblock);
|
||||
@@ -281,6 +284,9 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
|
||||
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
|
||||
return -1;
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_REMOVE_SAMPLE, PA_INVALID_INDEX, 0, name))
|
||||
+ return -1;
|
||||
+
|
||||
pa_assert_se(pa_idxset_remove_by_data(c->scache, e, NULL) == e);
|
||||
|
||||
pa_log_debug("Removed sample \"%s\"", name);
|
||||
@@ -293,6 +299,9 @@ int pa_scache_remove_item(pa_core *c, const char *name) {
|
||||
void pa_scache_free_all(pa_core *c) {
|
||||
pa_assert(c);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_REMOVE_SAMPLE, PA_INVALID_INDEX, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
pa_idxset_remove_all(c->scache, (pa_free_cb_t) free_entry);
|
||||
|
||||
if (c->scache_auto_unload_event) {
|
||||
@@ -314,6 +323,9 @@ int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t
|
||||
if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE)))
|
||||
return -1;
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_PLAY_SAMPLE, e->index, 0, NULL))
|
||||
+ return -1;
|
||||
+
|
||||
merged = pa_proplist_new();
|
||||
pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, name);
|
||||
pa_proplist_sets(merged, PA_PROP_EVENT_ID, name);
|
||||
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
|
||||
index 88d900b..e60af21 100644
|
||||
--- a/src/pulsecore/core-subscribe.c
|
||||
+++ b/src/pulsecore/core-subscribe.c
|
||||
@@ -169,8 +169,10 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
|
||||
|
||||
for (s = c->subscriptions; s; s = s->next) {
|
||||
|
||||
- if (!s->dead && pa_subscription_match_flags(s->mask, e->type))
|
||||
- s->callback(c, e->type, e->index, s->userdata);
|
||||
+ if (!s->dead && pa_subscription_match_flags(s->mask, e->type)) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT, e->index, e->type, NULL))
|
||||
+ s->callback(c, e->type, e->index, s->userdata);
|
||||
+ }
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
|
||||
index b068f5d..6b9fb8b 100644
|
||||
--- a/src/pulsecore/core.c
|
||||
+++ b/src/pulsecore/core.c
|
||||
@@ -257,6 +257,8 @@ int pa_core_exit(pa_core *c, bool force, int retval) {
|
||||
|
||||
if (c->disallow_exit && !force)
|
||||
return -1;
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_EXIT_DAEMON, PA_INVALID_INDEX, 0, NULL))
|
||||
+ return -1;
|
||||
|
||||
c->mainloop->quit(c->mainloop, retval);
|
||||
return 0;
|
||||
@@ -330,70 +332,212 @@ pa_mempool* pa_core_new_mempool(pa_core *c, pa_mem_type_t shm_type, bool per_cli
|
||||
return pa_mempool_new(shm_type, c->shm_size, per_client);
|
||||
}
|
||||
|
||||
+bool pa_core_check_access_sync(pa_core *c, pa_access_hook_t hook, uint32_t idx, pa_subscription_event_type_t event, const char *name) {
|
||||
+ pa_access_data data;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+
|
||||
+ if (c->current_client == NULL)
|
||||
+ return true;
|
||||
+
|
||||
+ data.hook = hook;
|
||||
+ data.client_index = c->current_client->index;
|
||||
+ data.object_index = idx;
|
||||
+ data.event = event;
|
||||
+ data.name = name;
|
||||
+ data.complete_cb = NULL;
|
||||
+
|
||||
+ return pa_hook_fire(&c->access[data.hook], &data) == PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+pa_hook_result_t pa_core_check_access(pa_core *c, pa_access_data *data) {
|
||||
+ pa_assert(c);
|
||||
+ pa_assert(data);
|
||||
+
|
||||
+ if (c->current_client == NULL)
|
||||
+ return PA_HOOK_OK;
|
||||
+
|
||||
+ data->client_index = c->current_client->index;
|
||||
+
|
||||
+ return pa_hook_fire(&c->access[data->hook], data);
|
||||
+}
|
||||
+
|
||||
/* FIXME: Should these be taking a ref during the copy? */
|
||||
|
||||
pa_idxset* pa_core_get_modules(pa_core *c) {
|
||||
- return pa_idxset_copy(c->modules, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_module *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->modules, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_MODULE, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_clients(pa_core *c) {
|
||||
- return pa_idxset_copy(c->clients, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_client *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->clients, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_CLIENT, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_cards(pa_core *c) {
|
||||
- return pa_idxset_copy(c->cards, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_card *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->cards, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_CARD, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_sinks(pa_core *c) {
|
||||
- return pa_idxset_copy(c->sinks, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_sink *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->sinks, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_sources(pa_core *c) {
|
||||
- return pa_idxset_copy(c->sources, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_source *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->sources, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_sink_inputs(pa_core *c) {
|
||||
- return pa_idxset_copy(c->sink_inputs, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_sink_input *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->sink_inputs, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK_INPUT, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_source_outputs(pa_core *c) {
|
||||
- return pa_idxset_copy(c->source_outputs, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_source_output *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->source_outputs, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_idxset* pa_core_get_scache(pa_core *c) {
|
||||
- return pa_idxset_copy(c->scache, NULL);
|
||||
+ pa_idxset *s;
|
||||
+ uint32_t idx;
|
||||
+ pa_scache_entry *e;
|
||||
+
|
||||
+ s = pa_idxset_new(NULL, NULL);
|
||||
+ PA_IDXSET_FOREACH(e, c->scache, idx) {
|
||||
+ if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SAMPLE, e->index, 0, NULL))
|
||||
+ pa_idxset_put(s, e, NULL);
|
||||
+ }
|
||||
+ return s;
|
||||
}
|
||||
|
||||
pa_module* pa_core_get_module(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->modules, idx);
|
||||
+ pa_module *e = pa_idxset_get_by_index(c->modules, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_MODULE, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_client* pa_core_get_client(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->clients, idx);
|
||||
+ pa_client *e = pa_idxset_get_by_index(c->clients, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_CLIENT, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_card* pa_core_get_card(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->cards, idx);
|
||||
+ pa_card *e = pa_idxset_get_by_index(c->cards, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_CARD, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_sink* pa_core_get_sink(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->sinks, idx);
|
||||
+ pa_sink *e = pa_idxset_get_by_index(c->sinks, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_source* pa_core_get_source(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->sources, idx);
|
||||
+ pa_source *e = pa_idxset_get_by_index(c->sources, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_sink_input* pa_core_get_sink_input(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->sink_inputs, idx);
|
||||
+ pa_sink_input *e = pa_idxset_get_by_index(c->sink_inputs, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK_INPUT, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_source_output* pa_core_get_source_output(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->source_outputs, idx);
|
||||
+ pa_source_output *e = pa_idxset_get_by_index(c->source_outputs, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
pa_scache_entry* pa_core_get_scache_entry(pa_core *c, uint32_t idx) {
|
||||
- return pa_idxset_get_by_index(c->scache, idx);
|
||||
+ pa_scache_entry *e = pa_idxset_get_by_index(c->scache, idx);
|
||||
+
|
||||
+ if (e == NULL || !pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SAMPLE, e->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
+ return e;
|
||||
}
|
||||
|
||||
const pa_sample_spec* pa_core_get_sample_spec(pa_core *c) {
|
||||
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
|
||||
index 87b055d..316a88e 100644
|
||||
--- a/src/pulsecore/core.h
|
||||
+++ b/src/pulsecore/core.h
|
||||
@@ -192,4 +192,8 @@ const pa_channel_map* pa_core_get_channel_map(pa_core *c);
|
||||
|
||||
uint32_t pa_core_get_cookie(pa_core *c);
|
||||
|
||||
+bool pa_core_check_access_sync(pa_core *c, pa_access_hook_t hook, uint32_t idx, pa_subscription_event_type_t event, const char *name);
|
||||
+
|
||||
+pa_hook_result_t pa_core_check_access(pa_core *c, pa_access_data *data);
|
||||
+
|
||||
#endif
|
||||
diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c
|
||||
index da2abe0..bcc1a0e 100644
|
||||
--- a/src/pulsecore/module.c
|
||||
+++ b/src/pulsecore/module.c
|
||||
@@ -120,6 +120,9 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) {
|
||||
if (c->disallow_module_loading)
|
||||
goto fail;
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_LOAD_MODULE, PA_INVALID_INDEX, 0, name))
|
||||
+ goto fail;
|
||||
+
|
||||
m = pa_xnew(pa_module, 1);
|
||||
m->name = pa_xstrdup(name);
|
||||
m->argument = pa_xstrdup(argument);
|
||||
@@ -280,6 +283,9 @@ void pa_module_unload(pa_module *m, bool force) {
|
||||
if (m->core->disallow_module_loading && !force)
|
||||
return;
|
||||
|
||||
+ if (!pa_core_check_access_sync(m->core, PA_ACCESS_HOOK_UNLOAD_MODULE, m->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!(m = pa_idxset_remove_by_data(m->core->modules, m, NULL)))
|
||||
return;
|
||||
|
||||
@@ -294,6 +300,9 @@ void pa_module_unload_by_index(pa_core *c, uint32_t idx, bool force) {
|
||||
if (c->disallow_module_loading && !force)
|
||||
return;
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_UNLOAD_MODULE, idx, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!(m = pa_idxset_remove_by_index(c->modules, idx)))
|
||||
return;
|
||||
|
||||
@@ -312,6 +321,9 @@ void pa_module_unload_all(pa_core *c) {
|
||||
if (pa_idxset_isempty(c->modules))
|
||||
return;
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_UNLOAD_MODULE, PA_INVALID_INDEX, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* Unload modules in reverse order by default */
|
||||
indices = pa_xnew(uint32_t, pa_idxset_size(c->modules));
|
||||
i = 0;
|
||||
@@ -357,6 +369,9 @@ void pa_module_unload_request(pa_module *m, bool force) {
|
||||
if (m->core->disallow_module_loading && !force)
|
||||
return;
|
||||
|
||||
+ if (!pa_core_check_access_sync(m->core, PA_ACCESS_HOOK_UNLOAD_MODULE, m->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
m->unload_requested = true;
|
||||
pa_hashmap_put(m->core->modules_pending_unload, m, m);
|
||||
|
||||
@@ -370,6 +385,9 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, bool force) {
|
||||
pa_module *m;
|
||||
pa_assert(c);
|
||||
|
||||
+ if (!pa_core_check_access_sync(c, PA_ACCESS_HOOK_UNLOAD_MODULE, idx, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!(m = pa_idxset_get_by_index(c->modules, idx)))
|
||||
return;
|
||||
|
||||
diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c
|
||||
index 699fa1b..fb68faf 100644
|
||||
--- a/src/pulsecore/namereg.c
|
||||
+++ b/src/pulsecore/namereg.c
|
||||
@@ -232,9 +232,20 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) {
|
||||
!pa_namereg_is_valid_name(name))
|
||||
return NULL;
|
||||
|
||||
- if ((e = pa_hashmap_get(c->namereg, name)))
|
||||
- if (e->type == type)
|
||||
+ if ((e = pa_hashmap_get(c->namereg, name)) && e->type == type) {
|
||||
+ if (type == PA_NAMEREG_SINK &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK, ((pa_sink*)e->data)->index, 0, NULL))
|
||||
return e->data;
|
||||
+ else if (type == PA_NAMEREG_SOURCE &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE, ((pa_source*)e->data)->index, 0, NULL))
|
||||
+ return e->data;
|
||||
+ else if (type == PA_NAMEREG_SAMPLE &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SAMPLE, ((pa_scache_entry*)e->data)->index, 0, NULL))
|
||||
+ return e->data;
|
||||
+ else if (type == PA_NAMEREG_CARD &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_CARD, ((pa_card*)e->data)->index, 0, NULL))
|
||||
+ return e->data;
|
||||
+ }
|
||||
|
||||
if (pa_atou(name, &idx) < 0)
|
||||
return NULL;
|
||||
@@ -257,6 +268,9 @@ pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) {
|
||||
if (s && !PA_SINK_IS_LINKED(pa_sink_get_state(s)))
|
||||
return NULL;
|
||||
|
||||
+ if (s && !pa_core_check_access_sync(c, PA_ACCESS_HOOK_SET_DEFAULT_SINK, s->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
if (c->default_sink != s) {
|
||||
c->default_sink = s;
|
||||
pa_core_hook_fire(c, PA_CORE_HOOK_DEFAULT_SINK_CHANGED, c->default_sink);
|
||||
@@ -272,6 +286,9 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) {
|
||||
if (s && !PA_SOURCE_IS_LINKED(pa_source_get_state(s)))
|
||||
return NULL;
|
||||
|
||||
+ if (s && !pa_core_check_access_sync(c, PA_ACCESS_HOOK_SET_DEFAULT_SOURCE, s->index, 0, NULL))
|
||||
+ return NULL;
|
||||
+
|
||||
if (c->default_source != s) {
|
||||
c->default_source = s;
|
||||
pa_core_hook_fire(c, PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED, c->default_source);
|
||||
@@ -288,7 +305,8 @@ pa_sink *pa_namereg_get_default_sink(pa_core *c) {
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
- if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)))
|
||||
+ if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink)) &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SINK, c->default_sink->index, 0, NULL))
|
||||
return c->default_sink;
|
||||
|
||||
sinks = pa_core_get_sinks(c);
|
||||
@@ -310,7 +328,8 @@ pa_source *pa_namereg_get_default_source(pa_core *c) {
|
||||
|
||||
pa_assert(c);
|
||||
|
||||
- if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)))
|
||||
+ if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source)) &&
|
||||
+ pa_core_check_access_sync(c, PA_ACCESS_HOOK_VIEW_SOURCE, c->default_source->index, 0, NULL))
|
||||
return c->default_source;
|
||||
|
||||
sources = pa_core_get_sources(c);
|
||||
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
|
||||
index 4cae6df..4ff97c4 100644
|
||||
--- a/src/pulsecore/protocol-native.c
|
||||
+++ b/src/pulsecore/protocol-native.c
|
||||
@@ -45,6 +45,7 @@
|
||||
#include <pulsecore/pdispatch.h>
|
||||
#include <pulsecore/pstream-util.h>
|
||||
#include <pulsecore/namereg.h>
|
||||
+#include <pulsecore/access.h>
|
||||
#include <pulsecore/core-scache.h>
|
||||
#include <pulsecore/core-subscribe.h>
|
||||
#include <pulsecore/log.h>
|
||||
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
|
||||
index 9a866b4..00c370a 100644
|
||||
--- a/src/pulsecore/sink-input.c
|
||||
+++ b/src/pulsecore/sink-input.c
|
||||
@@ -297,6 +297,9 @@ int pa_sink_input_new(
|
||||
pa_assert(data);
|
||||
pa_assert_ctl_context();
|
||||
|
||||
+ if (!pa_core_check_access_sync(core, PA_ACCESS_HOOK_CREATE_SINK_INPUT, PA_INVALID_INDEX, 0, NULL))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (data->client)
|
||||
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
|
||||
|
||||
@@ -811,6 +814,9 @@ void pa_sink_input_kill(pa_sink_input*i) {
|
||||
pa_assert_ctl_context();
|
||||
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_KILL_SINK_INPUT, i->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
i->kill(i);
|
||||
}
|
||||
|
||||
@@ -1238,6 +1244,9 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, bool s
|
||||
pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec));
|
||||
pa_assert(i->volume_writable);
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, i->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!absolute && pa_sink_flat_volume_enabled(i->sink)) {
|
||||
v = i->sink->reference_volume;
|
||||
pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map);
|
||||
@@ -1289,6 +1298,9 @@ void pa_sink_input_add_volume_factor(pa_sink_input *i, const char *key, const pa
|
||||
pa_assert(pa_cvolume_valid(volume_factor));
|
||||
pa_assert(volume_factor->channels == 1 || pa_cvolume_compatible(volume_factor, &i->sample_spec));
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, i->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
v = volume_factor_entry_new(key, volume_factor);
|
||||
if (!pa_cvolume_compatible(volume_factor, &i->sample_spec))
|
||||
pa_cvolume_set(&v->volume, i->sample_spec.channels, volume_factor->values[0]);
|
||||
@@ -1315,6 +1327,9 @@ int pa_sink_input_remove_volume_factor(pa_sink_input *i, const char *key) {
|
||||
pa_assert_ctl_context();
|
||||
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, i->index, 0, NULL))
|
||||
+ return -1;
|
||||
+
|
||||
if (pa_hashmap_remove_and_free(i->volume_factor_items, key) < 0)
|
||||
return -1;
|
||||
|
||||
@@ -1403,6 +1418,9 @@ void pa_sink_input_set_mute(pa_sink_input *i, bool mute, bool save) {
|
||||
pa_assert_ctl_context();
|
||||
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, i->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_mute = i->muted;
|
||||
|
||||
if (mute == old_mute) {
|
||||
@@ -1625,6 +1643,12 @@ bool pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest) {
|
||||
if (!pa_sink_input_may_move(i))
|
||||
return false;
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_MOVE_SINK_INPUT, i->index, 0, NULL))
|
||||
+ return false;
|
||||
+
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_VIEW_SINK, dest->index, 0, NULL))
|
||||
+ return false;
|
||||
+
|
||||
/* Make sure we're not creating a filter sink cycle */
|
||||
if (find_filter_sink_input(i, dest)) {
|
||||
pa_log_debug("Can't connect input to %s, as that would create a cycle.", dest->name);
|
||||
@@ -2332,6 +2356,9 @@ void pa_sink_input_set_volume_direct(pa_sink_input *i, const pa_cvolume *volume)
|
||||
pa_assert(i);
|
||||
pa_assert(volume);
|
||||
|
||||
+ if (!pa_core_check_access_sync(i->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, i->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_volume = i->volume;
|
||||
|
||||
if (pa_cvolume_equal(volume, &old_volume))
|
||||
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
|
||||
index a5cb1ee..891c55c 100644
|
||||
--- a/src/pulsecore/sink.c
|
||||
+++ b/src/pulsecore/sink.c
|
||||
@@ -839,6 +839,9 @@ int pa_sink_suspend(pa_sink *s, bool suspend, pa_suspend_cause_t cause) {
|
||||
pa_assert(PA_SINK_IS_LINKED(s->state));
|
||||
pa_assert(cause != 0);
|
||||
|
||||
+ if (!pa_core_check_access_sync (s->core, PA_ACCESS_HOOK_SUSPEND_SINK, s->index, 0, NULL))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (suspend) {
|
||||
s->suspend_cause |= cause;
|
||||
s->monitor_source->suspend_cause |= cause;
|
||||
@@ -1610,6 +1613,9 @@ bool pa_sink_is_passthrough(pa_sink *s) {
|
||||
void pa_sink_enter_passthrough(pa_sink *s) {
|
||||
pa_cvolume volume;
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_ENTER_PASSTHROUGH_SINK, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* disable the monitor in passthrough mode */
|
||||
if (s->monitor_source) {
|
||||
pa_log_debug("Suspending monitor source %s, because the sink is entering the passthrough mode.", s->monitor_source->name);
|
||||
@@ -1626,6 +1632,10 @@ void pa_sink_enter_passthrough(pa_sink *s) {
|
||||
|
||||
/* Called from main context */
|
||||
void pa_sink_leave_passthrough(pa_sink *s) {
|
||||
+
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_LEAVE_PASSTHROUGH_SINK, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* Unsuspend monitor */
|
||||
if (s->monitor_source) {
|
||||
pa_log_debug("Resuming monitor source %s, because the sink is leaving the passthrough mode.", s->monitor_source->name);
|
||||
@@ -2013,6 +2023,9 @@ void pa_sink_set_volume(
|
||||
pa_assert(volume || pa_sink_flat_volume_enabled(s));
|
||||
pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SINK_VOLUME, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* make sure we don't change the volume when a PASSTHROUGH input is connected ...
|
||||
* ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
|
||||
if (pa_sink_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
|
||||
@@ -2229,6 +2242,9 @@ void pa_sink_set_mute(pa_sink *s, bool mute, bool save) {
|
||||
pa_sink_assert_ref(s);
|
||||
pa_assert_ctl_context();
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SINK_VOLUME, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_muted = s->muted;
|
||||
|
||||
if (mute == old_muted) {
|
||||
@@ -3280,6 +3296,9 @@ void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency) {
|
||||
void pa_sink_set_port_latency_offset(pa_sink *s, int64_t offset) {
|
||||
pa_sink_assert_ref(s);
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SINK_PORT_LATENCY_OFFSET, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
s->port_latency_offset = offset;
|
||||
|
||||
if (PA_SINK_IS_LINKED(s->state))
|
||||
@@ -3332,6 +3351,9 @@ int pa_sink_set_port(pa_sink *s, const char *name, bool save) {
|
||||
if (!name)
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SINK_PORT, s->index, 0, name))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (!(port = pa_hashmap_get(s->ports, name)))
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c
|
||||
index 3ac935d..985de6b 100644
|
||||
--- a/src/pulsecore/sound-file-stream.c
|
||||
+++ b/src/pulsecore/sound-file-stream.c
|
||||
@@ -242,6 +242,9 @@ int pa_play_file(
|
||||
pa_assert(sink);
|
||||
pa_assert(fname);
|
||||
|
||||
+ if (!pa_core_check_access_sync (sink->core, PA_ACCESS_HOOK_PLAY_FILE, PA_INVALID_INDEX, 0, fname))
|
||||
+ return -1;
|
||||
+
|
||||
u = pa_msgobject_new(file_stream);
|
||||
u->parent.parent.free = file_stream_free;
|
||||
u->parent.process_msg = file_stream_process_msg;
|
||||
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
|
||||
index a265e6f..872ee27 100644
|
||||
--- a/src/pulsecore/source-output.c
|
||||
+++ b/src/pulsecore/source-output.c
|
||||
@@ -231,6 +231,9 @@ int pa_source_output_new(
|
||||
pa_assert(data);
|
||||
pa_assert_ctl_context();
|
||||
|
||||
+ if (!pa_core_check_access_sync(core, PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT, PA_INVALID_INDEX, 0, NULL))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (data->client)
|
||||
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
|
||||
|
||||
@@ -687,6 +690,9 @@ void pa_source_output_kill(pa_source_output*o) {
|
||||
pa_assert_ctl_context();
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT, o->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
o->kill(o);
|
||||
}
|
||||
|
||||
@@ -945,6 +951,9 @@ void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume,
|
||||
pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &o->sample_spec));
|
||||
pa_assert(o->volume_writable);
|
||||
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME, o->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
if (!absolute && pa_source_flat_volume_enabled(o->source)) {
|
||||
v = o->source->reference_volume;
|
||||
pa_cvolume_remap(&v, &o->source->channel_map, &o->channel_map);
|
||||
@@ -1056,6 +1065,9 @@ void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
|
||||
pa_assert_ctl_context();
|
||||
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
|
||||
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME, o->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_mute = o->muted;
|
||||
|
||||
if (mute == old_mute) {
|
||||
@@ -1275,6 +1287,12 @@ bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
|
||||
if (!pa_source_output_may_move(o))
|
||||
return false;
|
||||
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT, o->index, 0, NULL))
|
||||
+ return false;
|
||||
+
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_VIEW_SOURCE, dest->index, 0, NULL))
|
||||
+ return false;
|
||||
+
|
||||
/* Make sure we're not creating a filter source cycle */
|
||||
if (find_filter_source_output(o, dest)) {
|
||||
pa_log_debug("Can't connect output to %s, as that would create a cycle.", dest->name);
|
||||
@@ -1787,6 +1805,9 @@ void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *v
|
||||
pa_assert(o);
|
||||
pa_assert(volume);
|
||||
|
||||
+ if (!pa_core_check_access_sync(o->core, PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME, o->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_volume = o->volume;
|
||||
|
||||
if (pa_cvolume_equal(volume, &old_volume))
|
||||
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
|
||||
index 5f42965..449aeff 100644
|
||||
--- a/src/pulsecore/source.c
|
||||
+++ b/src/pulsecore/source.c
|
||||
@@ -763,6 +763,9 @@ int pa_source_suspend(pa_source *s, bool suspend, pa_suspend_cause_t cause) {
|
||||
if (s->monitor_of && cause != PA_SUSPEND_PASSTHROUGH)
|
||||
return -PA_ERR_NOTSUPPORTED;
|
||||
|
||||
+ if (!pa_core_check_access_sync (s->core, PA_ACCESS_HOOK_SUSPEND_SOURCE, s->index, 0, NULL))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (suspend)
|
||||
s->suspend_cause |= cause;
|
||||
else
|
||||
@@ -1196,6 +1199,9 @@ bool pa_source_is_passthrough(pa_source *s) {
|
||||
void pa_source_enter_passthrough(pa_source *s) {
|
||||
pa_cvolume volume;
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_ENTER_PASSTHROUGH_SOURCE, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* set the volume to NORM */
|
||||
s->saved_volume = *pa_source_get_volume(s, true);
|
||||
s->saved_save_volume = s->save_volume;
|
||||
@@ -1206,6 +1212,9 @@ void pa_source_enter_passthrough(pa_source *s) {
|
||||
|
||||
/* Called from main context */
|
||||
void pa_source_leave_passthrough(pa_source *s) {
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_LEAVE_PASSTHROUGH_SOURCE, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* Restore source volume to what it was before we entered passthrough mode */
|
||||
pa_source_set_volume(s, &s->saved_volume, true, s->saved_save_volume);
|
||||
|
||||
@@ -1587,6 +1596,9 @@ void pa_source_set_volume(
|
||||
pa_assert(volume || pa_source_flat_volume_enabled(s));
|
||||
pa_assert(!volume || volume->channels == 1 || pa_cvolume_compatible(volume, &s->sample_spec));
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SOURCE_VOLUME, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
/* make sure we don't change the volume in PASSTHROUGH mode ...
|
||||
* ... *except* if we're being invoked to reset the volume to ensure 0 dB gain */
|
||||
if (pa_source_is_passthrough(s) && (!volume || !pa_cvolume_is_norm(volume))) {
|
||||
@@ -1812,6 +1824,9 @@ void pa_source_set_mute(pa_source *s, bool mute, bool save) {
|
||||
pa_source_assert_ref(s);
|
||||
pa_assert_ctl_context();
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SOURCE_VOLUME, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
old_muted = s->muted;
|
||||
|
||||
if (mute == old_muted) {
|
||||
@@ -2571,6 +2586,9 @@ void pa_source_set_fixed_latency_within_thread(pa_source *s, pa_usec_t latency)
|
||||
void pa_source_set_port_latency_offset(pa_source *s, int64_t offset) {
|
||||
pa_source_assert_ref(s);
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SOURCE_PORT_LATENCY_OFFSET, s->index, 0, NULL))
|
||||
+ return;
|
||||
+
|
||||
s->port_latency_offset = offset;
|
||||
|
||||
if (PA_SOURCE_IS_LINKED(s->state))
|
||||
@@ -2609,6 +2627,9 @@ int pa_source_set_port(pa_source *s, const char *name, bool save) {
|
||||
if (!name)
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
+ if (!pa_core_check_access_sync(s->core, PA_ACCESS_HOOK_SET_SOURCE_PORT, s->index, 0, name))
|
||||
+ return -PA_ERR_ACCESS;
|
||||
+
|
||||
if (!(port = pa_hashmap_get(s->ports, name)))
|
||||
return -PA_ERR_NOENTITY;
|
||||
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,129 +0,0 @@
|
||||
From fe86adb46f57c8b8ef683591506cbe98fff0c071 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Fri, 27 Jan 2017 16:42:34 +0100
|
||||
Subject: [PATCH 12/18] protocol-native: add async access checks
|
||||
|
||||
---
|
||||
src/pulsecore/protocol-native.c | 91 +++++++++++++++++++++++++++++++++++++++--
|
||||
1 file changed, 87 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
|
||||
index 4ff97c4..a866802 100644
|
||||
--- a/src/pulsecore/protocol-native.c
|
||||
+++ b/src/pulsecore/protocol-native.c
|
||||
@@ -4850,14 +4850,97 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command,
|
||||
pa_pstream_send_simple_ack(c->pstream, tag);
|
||||
}
|
||||
|
||||
+typedef struct pa_protocol_native_access_data {
|
||||
+ pa_access_data d;
|
||||
+
|
||||
+ pa_pdispatch *pd;
|
||||
+ uint32_t command;
|
||||
+ uint32_t tag;
|
||||
+ pa_tagstruct *tc;
|
||||
+ void *userdata;
|
||||
+} pa_protocol_native_access_data;
|
||||
+
|
||||
+static const pa_pdispatch_cb_t access_ok_table[PA_COMMAND_MAX] = {
|
||||
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
|
||||
+ [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
|
||||
+ [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
|
||||
+ [PA_COMMAND_STAT] = command_stat,
|
||||
+};
|
||||
+
|
||||
+static void check_access_finish_cb(pa_access_data *data, bool res) {
|
||||
+ pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data;
|
||||
+ pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata);
|
||||
+ uint32_t command, tag;
|
||||
+
|
||||
+ if (!res || pa_tagstruct_getu32(d->tc, &command) < 0 ||
|
||||
+ pa_tagstruct_getu32(d->tc, &tag) < 0 ||
|
||||
+ command != d->command || tag != d->tag) {
|
||||
+ pa_pstream_send_error(c->pstream, d->tag, PA_ERR_ACCESS);
|
||||
+ goto finish;
|
||||
+ }
|
||||
+
|
||||
+ /* call the dispatcher again, hopefully this time, the access check will
|
||||
+ * fail or succeed immediately */
|
||||
+ access_ok_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata);
|
||||
+
|
||||
+finish:
|
||||
+ if (d->pd)
|
||||
+ pa_pdispatch_unref(d->pd);
|
||||
+ if (d->tc)
|
||||
+ pa_tagstruct_free(d->tc);
|
||||
+ pa_xfree(d);
|
||||
+}
|
||||
+
|
||||
+static const pa_access_hook_t access_table[PA_COMMAND_MAX] = {
|
||||
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = PA_ACCESS_HOOK_CONNECT_PLAYBACK,
|
||||
+ [PA_COMMAND_CREATE_RECORD_STREAM] = PA_ACCESS_HOOK_CONNECT_RECORD,
|
||||
+ [PA_COMMAND_CREATE_UPLOAD_STREAM] = PA_ACCESS_HOOK_CONNECT_UPLOAD,
|
||||
+ [PA_COMMAND_STAT] = PA_ACCESS_HOOK_STAT,
|
||||
+};
|
||||
+
|
||||
+static void check_command_access(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
||||
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
|
||||
+ pa_protocol_native_access_data *data;
|
||||
+ pa_hook_result_t res;
|
||||
+
|
||||
+ pa_native_connection_assert_ref(c);
|
||||
+
|
||||
+ if (access_table[command] == 0) {
|
||||
+ pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ data = pa_xnew0 (pa_protocol_native_access_data, 1);
|
||||
+ data->d.client_index = c->client->index;
|
||||
+ data->d.object_index = PA_INVALID_INDEX;
|
||||
+ data->d.event = 0;
|
||||
+ data->d.name = NULL;
|
||||
+ data->d.hook = access_table[command];
|
||||
+
|
||||
+ res = pa_core_check_access(c->protocol->core, &data->d);
|
||||
+ if (res == PA_HOOK_CANCEL) {
|
||||
+ /* async */
|
||||
+ data->d.complete_cb = check_access_finish_cb;
|
||||
+ data->pd = pd ? pa_pdispatch_ref (pd) : NULL;
|
||||
+ data->command = command;
|
||||
+ data->tag = tag;
|
||||
+ data->tc = t ? pa_tagstruct_copy (t) : NULL;
|
||||
+ data->userdata = userdata;
|
||||
+ } else {
|
||||
+ pa_xfree(data);
|
||||
+ access_ok_table[command](pd, command, tag, t, userdata);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+
|
||||
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
||||
[PA_COMMAND_ERROR] = NULL,
|
||||
[PA_COMMAND_TIMEOUT] = NULL,
|
||||
[PA_COMMAND_REPLY] = NULL,
|
||||
- [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
|
||||
+ [PA_COMMAND_CREATE_PLAYBACK_STREAM] = check_command_access,
|
||||
[PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
|
||||
[PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
|
||||
- [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
|
||||
+ [PA_COMMAND_CREATE_RECORD_STREAM] = check_command_access,
|
||||
[PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
|
||||
[PA_COMMAND_AUTH] = command_auth,
|
||||
[PA_COMMAND_REQUEST] = NULL,
|
||||
@@ -4865,10 +4948,10 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
||||
[PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
|
||||
[PA_COMMAND_LOOKUP_SINK] = command_lookup,
|
||||
[PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
|
||||
- [PA_COMMAND_STAT] = command_stat,
|
||||
+ [PA_COMMAND_STAT] = check_command_access,
|
||||
[PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
|
||||
[PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
|
||||
- [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
|
||||
+ [PA_COMMAND_CREATE_UPLOAD_STREAM] = check_command_access,
|
||||
[PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
|
||||
[PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
|
||||
[PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,249 +0,0 @@
|
||||
From 2c51c85d5c73b2217013a291cc80620e0b1ed268 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Fri, 27 Jan 2017 16:43:11 +0100
|
||||
Subject: [PATCH 13/18] core: add current_client
|
||||
|
||||
Add the current client to the core object and configure it in the
|
||||
various protocols whenever we start executing code for a pa_client.
|
||||
---
|
||||
src/pulsecore/cli.c | 5 +++++
|
||||
src/pulsecore/core-struct.h | 2 ++
|
||||
src/pulsecore/core-subscribe.c | 4 ++++
|
||||
src/pulsecore/core.c | 10 ++++++++++
|
||||
src/pulsecore/core.h | 3 +++
|
||||
src/pulsecore/protocol-dbus.c | 5 +++++
|
||||
src/pulsecore/protocol-esound.c | 4 ++++
|
||||
src/pulsecore/protocol-http.c | 4 ++++
|
||||
src/pulsecore/protocol-native.c | 3 +++
|
||||
src/pulsecore/protocol-simple.c | 2 ++
|
||||
10 files changed, 42 insertions(+)
|
||||
|
||||
diff --git a/src/pulsecore/cli.c b/src/pulsecore/cli.c
|
||||
index f942629..0546b06 100644
|
||||
--- a/src/pulsecore/cli.c
|
||||
+++ b/src/pulsecore/cli.c
|
||||
@@ -134,6 +134,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ pa_core_set_current_client(c->core, c->client);
|
||||
+
|
||||
/* Magic command, like they had in AT Hayes Modems! Those were the good days! */
|
||||
if (pa_streq(s, "/"))
|
||||
s = c->last_line;
|
||||
@@ -151,10 +153,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
||||
}
|
||||
else
|
||||
pa_cli_command_execute_line(c->core, s, buf, &c->fail);
|
||||
+
|
||||
c->defer_kill--;
|
||||
pa_ioline_puts(line, p = pa_strbuf_to_string_free(buf));
|
||||
pa_xfree(p);
|
||||
|
||||
+ pa_core_set_current_client(c->core, NULL);
|
||||
+
|
||||
if (c->kill_requested) {
|
||||
if (c->eof_callback)
|
||||
c->eof_callback(c, c->userdata);
|
||||
diff --git a/src/pulsecore/core-struct.h b/src/pulsecore/core-struct.h
|
||||
index e6aa374..8d58f1a 100644
|
||||
--- a/src/pulsecore/core-struct.h
|
||||
+++ b/src/pulsecore/core-struct.h
|
||||
@@ -98,6 +98,8 @@ struct pa_core {
|
||||
pa_hook hooks[PA_CORE_HOOK_MAX];
|
||||
/* access hooks */
|
||||
pa_hook access[PA_ACCESS_HOOK_MAX];
|
||||
+
|
||||
+ pa_client *current_client;
|
||||
};
|
||||
|
||||
#endif /* foocorestructhfoo */
|
||||
diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c
|
||||
index e60af21..a3f7ec7 100644
|
||||
--- a/src/pulsecore/core-subscribe.c
|
||||
+++ b/src/pulsecore/core-subscribe.c
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
struct pa_subscription {
|
||||
pa_core *core;
|
||||
+ pa_client *client;
|
||||
bool dead;
|
||||
|
||||
pa_subscription_cb_t callback;
|
||||
@@ -70,6 +71,7 @@ pa_subscription* pa_subscription_new(pa_core *c, pa_subscription_mask_t m, pa_su
|
||||
|
||||
s = pa_xnew(pa_subscription, 1);
|
||||
s->core = c;
|
||||
+ s->client = pa_core_get_current_client(c);
|
||||
s->dead = false;
|
||||
s->callback = callback;
|
||||
s->userdata = userdata;
|
||||
@@ -170,8 +172,10 @@ static void defer_cb(pa_mainloop_api *m, pa_defer_event *de, void *userdata) {
|
||||
for (s = c->subscriptions; s; s = s->next) {
|
||||
|
||||
if (!s->dead && pa_subscription_match_flags(s->mask, e->type)) {
|
||||
+ pa_core_set_current_client(c, s->client);
|
||||
if (pa_core_check_access_sync(c, PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT, e->index, e->type, NULL))
|
||||
s->callback(c, e->type, e->index, s->userdata);
|
||||
+ pa_core_set_current_client(c, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
|
||||
index 6b9fb8b..311d31f 100644
|
||||
--- a/src/pulsecore/core.c
|
||||
+++ b/src/pulsecore/core.c
|
||||
@@ -154,6 +154,8 @@ pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t
|
||||
for (j = 0; j < PA_ACCESS_HOOK_MAX; j++)
|
||||
pa_hook_init(&c->access[j], c);
|
||||
|
||||
+ c->current_client = NULL;
|
||||
+
|
||||
pa_random(&c->cookie, sizeof(c->cookie));
|
||||
|
||||
#ifdef SIGPIPE
|
||||
@@ -332,6 +334,14 @@ pa_mempool* pa_core_new_mempool(pa_core *c, pa_mem_type_t shm_type, bool per_cli
|
||||
return pa_mempool_new(shm_type, c->shm_size, per_client);
|
||||
}
|
||||
|
||||
+pa_client* pa_core_get_current_client(pa_core *c) {
|
||||
+ return c->current_client;
|
||||
+}
|
||||
+
|
||||
+void pa_core_set_current_client(pa_core *c, pa_client *client) {
|
||||
+ c->current_client = client;
|
||||
+}
|
||||
+
|
||||
bool pa_core_check_access_sync(pa_core *c, pa_access_hook_t hook, uint32_t idx, pa_subscription_event_type_t event, const char *name) {
|
||||
pa_access_data data;
|
||||
|
||||
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
|
||||
index 316a88e..42d1e11 100644
|
||||
--- a/src/pulsecore/core.h
|
||||
+++ b/src/pulsecore/core.h
|
||||
@@ -168,6 +168,9 @@ pa_mainloop_api* pa_core_get_mainloop(pa_core *c);
|
||||
pa_mempool* pa_core_get_mempool(pa_core *c);
|
||||
pa_mempool* pa_core_new_mempool(pa_core *c, pa_mem_type_t shm_type, bool per_client);
|
||||
|
||||
+pa_client* pa_core_get_current_client(pa_core *c);
|
||||
+void pa_core_set_current_client(pa_core *c, pa_client *client);
|
||||
+
|
||||
pa_idxset* pa_core_get_modules(pa_core *c);
|
||||
pa_idxset* pa_core_get_clients(pa_core *c);
|
||||
pa_idxset* pa_core_get_cards(pa_core *c);
|
||||
diff --git a/src/pulsecore/protocol-dbus.c b/src/pulsecore/protocol-dbus.c
|
||||
index 59afc1a..2ab0602 100644
|
||||
--- a/src/pulsecore/protocol-dbus.c
|
||||
+++ b/src/pulsecore/protocol-dbus.c
|
||||
@@ -494,6 +494,7 @@ static enum find_result_t find_handler(struct call_info *call_info) {
|
||||
static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessage *message, void *user_data) {
|
||||
pa_dbus_protocol *p = user_data;
|
||||
struct call_info call_info;
|
||||
+ pa_client *client;
|
||||
|
||||
pa_assert(connection);
|
||||
pa_assert(message);
|
||||
@@ -520,6 +521,9 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
|
||||
goto finish;
|
||||
}
|
||||
|
||||
+ client = pa_dbus_protocol_get_client(p, connection);
|
||||
+ pa_core_set_current_client(p->core, client);
|
||||
+
|
||||
switch (find_handler(&call_info)) {
|
||||
case FOUND_GET_PROPERTY:
|
||||
call_info.property_handler->get_cb(connection, message, call_info.iface_entry->userdata);
|
||||
@@ -586,6 +590,7 @@ static DBusHandlerResult handle_message_cb(DBusConnection *connection, DBusMessa
|
||||
default:
|
||||
pa_assert_not_reached();
|
||||
}
|
||||
+ pa_core_set_current_client(p->core, NULL);
|
||||
|
||||
finish:
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c
|
||||
index 0d76525..706a21f 100644
|
||||
--- a/src/pulsecore/protocol-esound.c
|
||||
+++ b/src/pulsecore/protocol-esound.c
|
||||
@@ -1284,7 +1284,9 @@ static void io_callback(pa_iochannel*io, void *userdata) {
|
||||
connection_assert_ref(c);
|
||||
pa_assert(io);
|
||||
|
||||
+ pa_core_set_current_client(c->protocol->core, c->client);
|
||||
do_work(c);
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
}
|
||||
|
||||
static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
|
||||
@@ -1293,7 +1295,9 @@ static void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata)
|
||||
connection_assert_ref(c);
|
||||
pa_assert(e);
|
||||
|
||||
+ pa_core_set_current_client(c->protocol->core, c->client);
|
||||
do_work(c);
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
}
|
||||
|
||||
static int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
|
||||
diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c
|
||||
index 64024df..6fbd553 100644
|
||||
--- a/src/pulsecore/protocol-http.c
|
||||
+++ b/src/pulsecore/protocol-http.c
|
||||
@@ -647,6 +647,8 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
||||
return;
|
||||
}
|
||||
|
||||
+ pa_core_set_current_client(c->protocol->core, c->client);
|
||||
+
|
||||
switch (c->state) {
|
||||
case STATE_REQUEST_LINE: {
|
||||
if (pa_startswith(s, "GET ")) {
|
||||
@@ -680,11 +682,13 @@ static void line_callback(pa_ioline *line, const char *s, void *userdata) {
|
||||
default:
|
||||
;
|
||||
}
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
html_response(c, 500, "Internal Server Error", NULL);
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
}
|
||||
|
||||
void pa_http_protocol_connect(pa_http_protocol *p, pa_iochannel *io, pa_module *m) {
|
||||
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
|
||||
index a866802..728c7e7 100644
|
||||
--- a/src/pulsecore/protocol-native.c
|
||||
+++ b/src/pulsecore/protocol-native.c
|
||||
@@ -5051,10 +5051,13 @@ static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_an
|
||||
pa_assert(packet);
|
||||
pa_native_connection_assert_ref(c);
|
||||
|
||||
+ pa_core_set_current_client(c->protocol->core, c->client);
|
||||
+
|
||||
if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0) {
|
||||
pa_log("invalid packet.");
|
||||
native_connection_unlink(c);
|
||||
}
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
}
|
||||
|
||||
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
|
||||
diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c
|
||||
index e1ead2f..8d4fbe4 100644
|
||||
--- a/src/pulsecore/protocol-simple.c
|
||||
+++ b/src/pulsecore/protocol-simple.c
|
||||
@@ -467,7 +467,9 @@ static void io_callback(pa_iochannel*io, void *userdata) {
|
||||
connection_assert_ref(c);
|
||||
pa_assert(io);
|
||||
|
||||
+ pa_core_set_current_client(c->protocol->core, c->client);
|
||||
do_work(c);
|
||||
+ pa_core_set_current_client(c->protocol->core, NULL);
|
||||
}
|
||||
|
||||
/*** socket_server callbacks ***/
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
From 9820db2433a37aa1f22d962bb89876c66bcc1185 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Mon, 30 Jan 2017 11:52:54 +0100
|
||||
Subject: [PATCH 14/18] core: ensure maincontext for current client
|
||||
|
||||
Make sure we can only access the current client from the main context.
|
||||
---
|
||||
src/pulsecore/core.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c
|
||||
index 311d31f..f14139f 100644
|
||||
--- a/src/pulsecore/core.c
|
||||
+++ b/src/pulsecore/core.c
|
||||
@@ -335,17 +335,20 @@ pa_mempool* pa_core_new_mempool(pa_core *c, pa_mem_type_t shm_type, bool per_cli
|
||||
}
|
||||
|
||||
pa_client* pa_core_get_current_client(pa_core *c) {
|
||||
+ pa_assert_ctl_context();
|
||||
return c->current_client;
|
||||
}
|
||||
|
||||
void pa_core_set_current_client(pa_core *c, pa_client *client) {
|
||||
- c->current_client = client;
|
||||
+ pa_assert_ctl_context();
|
||||
+ c->current_client = client;
|
||||
}
|
||||
|
||||
bool pa_core_check_access_sync(pa_core *c, pa_access_hook_t hook, uint32_t idx, pa_subscription_event_type_t event, const char *name) {
|
||||
pa_access_data data;
|
||||
|
||||
pa_assert(c);
|
||||
+ pa_assert_ctl_context();
|
||||
|
||||
if (c->current_client == NULL)
|
||||
return true;
|
||||
@@ -363,6 +366,7 @@ bool pa_core_check_access_sync(pa_core *c, pa_access_hook_t hook, uint32_t idx,
|
||||
pa_hook_result_t pa_core_check_access(pa_core *c, pa_access_data *data) {
|
||||
pa_assert(c);
|
||||
pa_assert(data);
|
||||
+ pa_assert_ctl_context();
|
||||
|
||||
if (c->current_client == NULL)
|
||||
return PA_HOOK_OK;
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,828 +0,0 @@
|
||||
From 46754adc0e09e7ea8a9c84183425924333c0d251 Mon Sep 17 00:00:00 2001
|
||||
From: Matthias Clasen <mclasen@redhat.com>
|
||||
Date: Fri, 15 Jul 2016 09:55:44 -0400
|
||||
Subject: [PATCH 15/18] Add flatpak access control
|
||||
|
||||
Add a module that talks to xdg-desktop-portal for access control
|
||||
in sandboxed applications. Currently, it asks the portal for
|
||||
access to microphone/speakers when a record or playback stream
|
||||
is opened or samples are played.
|
||||
|
||||
For non-sandboxed applications, the module imposes no restrictions.
|
||||
---
|
||||
src/Makefile.am | 9 +-
|
||||
src/modules/module-access.c | 4 +-
|
||||
src/modules/module-flatpak.c | 738 +++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 749 insertions(+), 2 deletions(-)
|
||||
create mode 100644 src/modules/module-flatpak.c
|
||||
|
||||
diff --git a/src/Makefile.am b/src/Makefile.am
|
||||
index 70bc848..0e0a480 100644
|
||||
--- a/src/Makefile.am
|
||||
+++ b/src/Makefile.am
|
||||
@@ -1175,7 +1175,8 @@ libavahi_wrap_la_LIBADD = $(AM_LIBADD) $(AVAHI_CFLAGS) libpulsecore-@PA_MAJORMIN
|
||||
if HAVE_DBUS
|
||||
# Serveral module (e.g. libalsa-util.la)
|
||||
modlibexec_LTLIBRARIES += \
|
||||
- module-console-kit.la
|
||||
+ module-console-kit.la \
|
||||
+ module-flatpak.la
|
||||
endif
|
||||
|
||||
modlibexec_LTLIBRARIES += \
|
||||
@@ -1473,6 +1474,7 @@ endif
|
||||
# These are generated by an M4 script
|
||||
SYMDEF_FILES = \
|
||||
module-access-symdef.h \
|
||||
+ module-flatpak-symdef.h \
|
||||
module-cli-symdef.h \
|
||||
module-cli-protocol-tcp-symdef.h \
|
||||
module-cli-protocol-unix-symdef.h \
|
||||
@@ -1600,6 +1602,11 @@ module_access_la_SOURCES = modules/module-access.c
|
||||
module_access_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
module_access_la_LIBADD = $(MODULE_LIBADD)
|
||||
|
||||
+module_flatpak_la_SOURCES = modules/module-flatpak.c
|
||||
+module_flatpak_la_CFLAGS = $(AM_CDFLAGS) $(DBUS_CFLAGS)
|
||||
+module_flatpak_la_LDFLAGS = $(MODULE_LDFLAGS)
|
||||
+module_flatpak_la_LIBADD = $(MODULE_LIBADD) $(DBUS_LIBS)
|
||||
+
|
||||
# CLI protocol
|
||||
|
||||
module_cli_la_SOURCES = modules/module-cli.c
|
||||
diff --git a/src/modules/module-access.c b/src/modules/module-access.c
|
||||
index 39e2f0e..dec1dbf 100644
|
||||
--- a/src/modules/module-access.c
|
||||
+++ b/src/modules/module-access.c
|
||||
@@ -498,9 +498,9 @@ int pa__init(pa_module*m) {
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_CARD] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_STAT] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_SAMPLE] = rule_allow;
|
||||
+
|
||||
ap->rule[PA_ACCESS_HOOK_PLAY_SAMPLE] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_CONNECT_PLAYBACK] = rule_allow;
|
||||
-
|
||||
ap->rule[PA_ACCESS_HOOK_CONNECT_RECORD] = rule_check_async;
|
||||
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_CLIENT] = rule_check_owner;
|
||||
@@ -508,11 +508,13 @@ int pa__init(pa_module*m) {
|
||||
|
||||
ap->rule[PA_ACCESS_HOOK_CREATE_SINK_INPUT] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SINK_INPUT] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_KILL_SINK_INPUT] = rule_check_owner;
|
||||
|
||||
ap->rule[PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT] = rule_allow;
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT] = rule_check_owner;
|
||||
|
||||
diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c
|
||||
new file mode 100644
|
||||
index 0000000..64bbe86
|
||||
--- /dev/null
|
||||
+++ b/src/modules/module-flatpak.c
|
||||
@@ -0,0 +1,738 @@
|
||||
+/***
|
||||
+ This file is part of PulseAudio.
|
||||
+
|
||||
+ Copyright 2016 Red Hat, Inc.
|
||||
+
|
||||
+ PulseAudio is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU Lesser General Public License as published
|
||||
+ by the Free Software Foundation; either version 2.1 of the License,
|
||||
+ or (at your option) any later version.
|
||||
+
|
||||
+ PulseAudio is distributed in the hope that it will be useful, but
|
||||
+ WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public License
|
||||
+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
||||
+***/
|
||||
+
|
||||
+#ifdef HAVE_CONFIG_H
|
||||
+#include <config.h>
|
||||
+#endif
|
||||
+
|
||||
+#include <unistd.h>
|
||||
+#include <string.h>
|
||||
+#include <errno.h>
|
||||
+#include <sys/types.h>
|
||||
+#include <fcntl.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+#include <pulse/xmalloc.h>
|
||||
+#include <pulse/rtclock.h>
|
||||
+#include <pulse/timeval.h>
|
||||
+
|
||||
+#include <pulsecore/core-error.h>
|
||||
+#include <pulsecore/core-struct.h>
|
||||
+#include <pulsecore/module.h>
|
||||
+#include <pulsecore/core-util.h>
|
||||
+#include <pulsecore/modargs.h>
|
||||
+#include <pulsecore/log.h>
|
||||
+#include <pulsecore/sink-input.h>
|
||||
+#include <pulsecore/core-util.h>
|
||||
+#include <pulsecore/dbus-shared.h>
|
||||
+
|
||||
+#include "module-access-symdef.h"
|
||||
+
|
||||
+PA_MODULE_AUTHOR("Matthias Clasen");
|
||||
+PA_MODULE_DESCRIPTION("Controls access to server resources for flatpak apps");
|
||||
+PA_MODULE_VERSION(PACKAGE_VERSION);
|
||||
+PA_MODULE_LOAD_ONCE(true);
|
||||
+PA_MODULE_USAGE("");
|
||||
+
|
||||
+static const char* const valid_modargs[] = {
|
||||
+ NULL,
|
||||
+};
|
||||
+
|
||||
+typedef struct access_policy access_policy;
|
||||
+typedef struct event_item event_item;
|
||||
+typedef struct client_data client_data;
|
||||
+typedef struct userdata userdata;
|
||||
+
|
||||
+typedef pa_hook_result_t (*access_rule_t)(pa_core *c, pa_access_data *d, struct userdata *u);
|
||||
+
|
||||
+struct access_policy {
|
||||
+ uint32_t index;
|
||||
+ struct userdata *userdata;
|
||||
+
|
||||
+ access_rule_t rule[PA_ACCESS_HOOK_MAX];
|
||||
+};
|
||||
+
|
||||
+struct event_item {
|
||||
+ PA_LLIST_FIELDS(event_item);
|
||||
+
|
||||
+ int facility;
|
||||
+ uint32_t object_index;
|
||||
+};
|
||||
+
|
||||
+struct async_cache {
|
||||
+ bool checked;
|
||||
+ bool granted;
|
||||
+};
|
||||
+
|
||||
+struct userdata {
|
||||
+ pa_core *core;
|
||||
+
|
||||
+ pa_hook_slot *hook[PA_ACCESS_HOOK_MAX];
|
||||
+
|
||||
+ pa_idxset *policies;
|
||||
+ uint32_t default_policy;
|
||||
+ uint32_t portal_policy;
|
||||
+
|
||||
+ pa_dbus_connection *connection;
|
||||
+ pa_hashmap *clients;
|
||||
+ pa_hook_slot *client_put_slot;
|
||||
+ pa_hook_slot *client_auth_slot;
|
||||
+ pa_hook_slot *client_proplist_changed_slot;
|
||||
+ pa_hook_slot *client_unlink_slot;
|
||||
+};
|
||||
+
|
||||
+struct client_data {
|
||||
+ struct userdata *u;
|
||||
+
|
||||
+ uint32_t index;
|
||||
+ uint32_t policy;
|
||||
+ pid_t pid;
|
||||
+
|
||||
+ struct async_cache cached[PA_ACCESS_HOOK_MAX];
|
||||
+ pa_access_data *access_data;
|
||||
+
|
||||
+ PA_LLIST_HEAD(event_item, events);
|
||||
+};
|
||||
+
|
||||
+
|
||||
+static void add_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i;
|
||||
+
|
||||
+ i = pa_xnew0(event_item, 1);
|
||||
+ PA_LLIST_INIT(event_item, i);
|
||||
+ i->facility = facility;
|
||||
+ i->object_index = oidx;
|
||||
+
|
||||
+ PA_LLIST_PREPEND(event_item, cd->events, i);
|
||||
+}
|
||||
+
|
||||
+static event_item *find_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i;
|
||||
+
|
||||
+ PA_LLIST_FOREACH(i, cd->events) {
|
||||
+ if (i->facility == facility && i->object_index == oidx)
|
||||
+ return i;
|
||||
+ }
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static bool remove_event(struct client_data *cd, int facility, uint32_t oidx) {
|
||||
+ event_item *i = find_event(cd, facility, oidx);
|
||||
+ if (i) {
|
||||
+ PA_LLIST_REMOVE(event_item, cd->events, i);
|
||||
+ pa_xfree(i);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+static client_data * client_data_new(struct userdata *u, uint32_t index, uint32_t policy, pid_t pid) {
|
||||
+ client_data *cd;
|
||||
+
|
||||
+ cd = pa_xnew0(client_data, 1);
|
||||
+ cd->u = u;
|
||||
+ cd->index = index;
|
||||
+ cd->policy = policy;
|
||||
+ cd->pid = pid;
|
||||
+ pa_hashmap_put(u->clients, PA_UINT32_TO_PTR(index), cd);
|
||||
+ pa_log("new client %d with pid %d, policy %d", index, pid, policy);
|
||||
+
|
||||
+ return cd;
|
||||
+}
|
||||
+
|
||||
+static void client_data_free(client_data *cd) {
|
||||
+ event_item *e;
|
||||
+
|
||||
+ while ((e = cd->events)) {
|
||||
+ PA_LLIST_REMOVE(event_item, cd->events, e);
|
||||
+ pa_xfree(e);
|
||||
+ }
|
||||
+ pa_log("removed client %d", cd->index);
|
||||
+ pa_xfree(cd);
|
||||
+}
|
||||
+
|
||||
+static client_data * client_data_get(struct userdata *u, uint32_t index) {
|
||||
+ return pa_hashmap_get(u->clients, PA_UINT32_TO_PTR(index));
|
||||
+}
|
||||
+
|
||||
+static void client_data_remove(struct userdata *u, uint32_t index) {
|
||||
+ pa_hashmap_remove_and_free(u->clients, PA_UINT32_TO_PTR(index));
|
||||
+}
|
||||
+
|
||||
+/* rule checks if the operation on the object is performed by the owner of the object */
|
||||
+static pa_hook_result_t rule_check_owner (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_hook_result_t result = PA_HOOK_STOP;
|
||||
+ uint32_t idx = PA_INVALID_INDEX;
|
||||
+
|
||||
+ switch (d->hook) {
|
||||
+ case PA_ACCESS_HOOK_VIEW_CLIENT:
|
||||
+ case PA_ACCESS_HOOK_KILL_CLIENT: {
|
||||
+ idx = d->object_index;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ case PA_ACCESS_HOOK_VIEW_SINK_INPUT:
|
||||
+ case PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME:
|
||||
+ case PA_ACCESS_HOOK_KILL_SINK_INPUT: {
|
||||
+ const pa_sink_input *si = pa_idxset_get_by_index(c->sink_inputs, d->object_index);
|
||||
+ idx = (si && si->client) ? si->client->index : PA_INVALID_INDEX;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ case PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT:
|
||||
+ case PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME:
|
||||
+ case PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT: {
|
||||
+ const pa_source_output *so = pa_idxset_get_by_index(c->source_outputs, d->object_index);
|
||||
+ idx = (so && so->client) ? so->client->index : PA_INVALID_INDEX;
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+ if (idx == d->client_index) {
|
||||
+ pa_log("allow operation %d/%d of same client %d", d->hook, d->object_index, idx);
|
||||
+ result = PA_HOOK_OK;
|
||||
+ } else
|
||||
+ pa_log("blocked operation %d/%d of client %d to client %d", d->hook, d->object_index, idx, d->client_index);
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+/* rule allows the operation */
|
||||
+static pa_hook_result_t rule_allow (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_log("allow operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+/* rule blocks the operation */
|
||||
+static pa_hook_result_t rule_block (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ pa_log("blocked operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static DBusHandlerResult portal_response(DBusConnection *connection, DBusMessage *msg, void *user_data)
|
||||
+{
|
||||
+ client_data *cd = user_data;
|
||||
+ pa_access_data *d = cd->access_data;
|
||||
+
|
||||
+ if (dbus_message_is_signal(msg, "org.freedesktop.portal.Request", "Response")) {
|
||||
+ uint32_t response = 2;
|
||||
+ DBusError error;
|
||||
+
|
||||
+ dbus_error_init(&error);
|
||||
+
|
||||
+ dbus_connection_remove_filter (connection, portal_response, cd);
|
||||
+
|
||||
+ if (!dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &response, DBUS_TYPE_INVALID)) {
|
||||
+ pa_log("failed to parse Response: %s\n", error.message);
|
||||
+ dbus_error_free(&error);
|
||||
+ }
|
||||
+
|
||||
+ cd->cached[d->hook].checked = true;
|
||||
+ cd->cached[d->hook].granted = response == 0 ? true : false;
|
||||
+
|
||||
+ pa_log("portal check result: %d\n", cd->cached[d->hook].granted);
|
||||
+
|
||||
+ d->complete_cb (d, cd->cached[d->hook].granted);
|
||||
+
|
||||
+ return DBUS_HANDLER_RESULT_HANDLED;
|
||||
+ }
|
||||
+
|
||||
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t rule_check_portal (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ client_data *cd = client_data_get(u, d->client_index);
|
||||
+ DBusMessage *m = NULL, *r = NULL;
|
||||
+ DBusError error;
|
||||
+ pid_t pid;
|
||||
+ DBusMessageIter msg_iter;
|
||||
+ DBusMessageIter dict_iter;
|
||||
+ const char *handle;
|
||||
+ const char *device;
|
||||
+
|
||||
+ if (cd->cached[d->hook].checked) {
|
||||
+ pa_log("returned cached answer for portal check: %d\n", cd->cached[d->hook].granted);
|
||||
+ return cd->cached[d->hook].granted ? PA_HOOK_OK : PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ pa_log("ask portal for operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+
|
||||
+ cd->access_data = d;
|
||||
+
|
||||
+ dbus_error_init(&error);
|
||||
+
|
||||
+ if (!(m = dbus_message_new_method_call("org.freedesktop.portal.Desktop",
|
||||
+ "/org/freedesktop/portal/desktop",
|
||||
+ "org.freedesktop.portal.Device",
|
||||
+ "AccessDevice"))) {
|
||||
+ return PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ if (d->hook == PA_ACCESS_HOOK_CONNECT_RECORD)
|
||||
+ device = "microphone";
|
||||
+ else if (d->hook == PA_ACCESS_HOOK_CONNECT_PLAYBACK ||
|
||||
+ d->hook == PA_ACCESS_HOOK_PLAY_SAMPLE)
|
||||
+ device = "speakers";
|
||||
+ else
|
||||
+ pa_assert_not_reached ();
|
||||
+
|
||||
+ pid = cd->pid;
|
||||
+ if (!dbus_message_append_args(m,
|
||||
+ DBUS_TYPE_UINT32, &pid,
|
||||
+ DBUS_TYPE_INVALID)) {
|
||||
+ dbus_message_unref(m);
|
||||
+ return PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ dbus_message_iter_init_append(m, &msg_iter);
|
||||
+ dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY, "s", &dict_iter);
|
||||
+ dbus_message_iter_append_basic (&dict_iter, DBUS_TYPE_STRING, &device);
|
||||
+ dbus_message_iter_close_container (&msg_iter, &dict_iter);
|
||||
+
|
||||
+ dbus_message_iter_open_container (&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter);
|
||||
+ dbus_message_iter_close_container (&msg_iter, &dict_iter);
|
||||
+
|
||||
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
|
||||
+ pa_log("Failed to call portal: %s\n", error.message);
|
||||
+ dbus_error_free(&error);
|
||||
+ dbus_message_unref(m);
|
||||
+ return PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ dbus_message_unref(m);
|
||||
+
|
||||
+ if (!dbus_message_get_args(r, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID)) {
|
||||
+ pa_log("Failed to parse AccessDevice result: %s\n", error.message);
|
||||
+ dbus_error_free(&error);
|
||||
+ dbus_message_unref(r);
|
||||
+ return PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ dbus_message_unref(r);
|
||||
+
|
||||
+ dbus_bus_add_match(pa_dbus_connection_get(u->connection),
|
||||
+ "type='signal',interface='org.freedesktop.portal.Request'",
|
||||
+ &error);
|
||||
+ dbus_connection_flush(pa_dbus_connection_get(u->connection));
|
||||
+ if (dbus_error_is_set(&error)) {
|
||||
+ pa_log("Failed to subscribe to Request signal: %s\n", error.message);
|
||||
+ dbus_error_free(&error);
|
||||
+ return PA_HOOK_STOP;
|
||||
+ }
|
||||
+
|
||||
+ dbus_connection_add_filter(pa_dbus_connection_get(u->connection), portal_response, cd, NULL);
|
||||
+
|
||||
+ return PA_HOOK_CANCEL;
|
||||
+}
|
||||
+
|
||||
+static access_policy *access_policy_new(struct userdata *u, bool allow_all) {
|
||||
+ access_policy *ap;
|
||||
+ int i;
|
||||
+
|
||||
+ ap = pa_xnew0(access_policy, 1);
|
||||
+ ap->userdata = u;
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++)
|
||||
+ ap->rule[i] = allow_all ? rule_allow : rule_block;
|
||||
+
|
||||
+ pa_idxset_put(u->policies, ap, &ap->index);
|
||||
+
|
||||
+ return ap;
|
||||
+}
|
||||
+
|
||||
+static void access_policy_free(access_policy *ap) {
|
||||
+ pa_idxset_remove_by_index(ap->userdata->policies, ap->index);
|
||||
+ pa_xfree(ap);
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t check_access (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ access_policy *ap;
|
||||
+ access_rule_t rule;
|
||||
+ client_data *cd = client_data_get(u, d->client_index);
|
||||
+
|
||||
+ /* unknown client */
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_STOP;
|
||||
+
|
||||
+ ap = pa_idxset_get_by_index(u->policies, cd->policy);
|
||||
+
|
||||
+ rule = ap->rule[d->hook];
|
||||
+ if (rule)
|
||||
+ return rule(c, d, u);
|
||||
+
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static const pa_access_hook_t event_hook[PA_SUBSCRIPTION_EVENT_FACILITY_MASK+1] = {
|
||||
+ [PA_SUBSCRIPTION_EVENT_SINK] = PA_ACCESS_HOOK_VIEW_SINK,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SOURCE] = PA_ACCESS_HOOK_VIEW_SOURCE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SINK_INPUT] = PA_ACCESS_HOOK_VIEW_SINK_INPUT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT] = PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_MODULE] = PA_ACCESS_HOOK_VIEW_MODULE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_CLIENT] = PA_ACCESS_HOOK_VIEW_CLIENT,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE] = PA_ACCESS_HOOK_VIEW_SAMPLE,
|
||||
+ [PA_SUBSCRIPTION_EVENT_SERVER] = PA_ACCESS_HOOK_VIEW_SERVER,
|
||||
+ [PA_SUBSCRIPTION_EVENT_CARD] = PA_ACCESS_HOOK_VIEW_CARD
|
||||
+};
|
||||
+
|
||||
+static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
+ int facility;
|
||||
+ client_data *cd;
|
||||
+
|
||||
+ facility = d->event & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
|
||||
+
|
||||
+ cd = client_data_get (u, d->client_index);
|
||||
+ /* unknown client destination, block event */
|
||||
+ if (cd == NULL)
|
||||
+ goto block;
|
||||
+
|
||||
+ switch (d->event & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
|
||||
+ case PA_SUBSCRIPTION_EVENT_REMOVE:
|
||||
+ /* if the client saw this object before, let the event go through */
|
||||
+ if (remove_event(cd, facility, d->object_index)) {
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case PA_SUBSCRIPTION_EVENT_CHANGE:
|
||||
+ /* if the client saw this object before, let it go through */
|
||||
+ if (find_event(cd, facility, d->object_index)) {
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+
|
||||
+ /* fallthrough to do hook check and register event */
|
||||
+ case PA_SUBSCRIPTION_EVENT_NEW: {
|
||||
+ pa_access_data data = *d;
|
||||
+
|
||||
+ /* new object, check if the client is allowed to inspect it */
|
||||
+ data.hook = event_hook[facility];
|
||||
+ if (data.hook && pa_hook_fire(&c->access[data.hook], &data) == PA_HOOK_OK) {
|
||||
+ /* client can inspect the object, remember for later */
|
||||
+ add_event(cd, facility, d->object_index);
|
||||
+ pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_OK;
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+block:
|
||||
+ pa_log("blocked event %02x/%d for client %d", d->event, d->object_index, d->client_index);
|
||||
+ return PA_HOOK_STOP;
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+client_is_sandboxed (pa_client *cl)
|
||||
+{
|
||||
+ char *path;
|
||||
+ char data[2048];
|
||||
+ int n;
|
||||
+ const char *state = NULL;
|
||||
+ const char *current;
|
||||
+ bool result;
|
||||
+ int fd;
|
||||
+ pid_t pid;
|
||||
+
|
||||
+ if (cl->creds_valid) {
|
||||
+ pa_log ("client has trusted pid %d", cl->creds.pid);
|
||||
+ }
|
||||
+ else {
|
||||
+ pa_log ("no trusted pid found, assuming not sandboxed\n");
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ pid = cl->creds.pid;
|
||||
+
|
||||
+ path = pa_sprintf_malloc("/proc/%u/cgroup", pid);
|
||||
+ fd = pa_open_cloexec(path, O_RDONLY, 0);
|
||||
+ free (path);
|
||||
+
|
||||
+ if (fd == -1)
|
||||
+ return false;
|
||||
+
|
||||
+ pa_loop_read(fd, &data, sizeof(data), NULL);
|
||||
+ close(fd);
|
||||
+
|
||||
+ result = false;
|
||||
+ while ((current = pa_split_in_place(data, "\n", &n, &state)) != NULL) {
|
||||
+ if (strncmp(current, "1:name=systemd:", strlen("1:name=systemd:")) == 0) {
|
||||
+ const char *p = strstr(current, "flatpak-");
|
||||
+ if (p && p - current < n) {
|
||||
+ pa_log("found a flatpak cgroup, assuming sandboxed\n");
|
||||
+ result = true;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+static uint32_t find_policy_for_client (struct userdata *u, pa_client *cl) {
|
||||
+ char *s;
|
||||
+
|
||||
+ s = pa_proplist_to_string(cl->proplist);
|
||||
+ pa_log ("client proplist %s", s);
|
||||
+ pa_xfree(s);
|
||||
+
|
||||
+ if (client_is_sandboxed (cl)) {
|
||||
+ pa_log("client is sandboxed, choosing portal policy\n");
|
||||
+ return u->portal_policy;
|
||||
+ }
|
||||
+ else {
|
||||
+ pa_log("client not sandboxed, choosing default policy\n");
|
||||
+ return u->default_policy;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+pa_log("client put\n");
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ /* when we get here, the client just connected and is not yet authenticated
|
||||
+ * we should probably install a policy that denies all access */
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+
|
||||
+ client_data_new(u, cl->index, policy, cl->creds.pid);
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_auth_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ client_data *cd;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ cd = client_data_get (u, cl->index);
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_OK;
|
||||
+
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+ cd->policy = policy;
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_proplist_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+ client_data *cd;
|
||||
+ uint32_t policy;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ cd = client_data_get (u, cl->index);
|
||||
+ if (cd == NULL)
|
||||
+ return PA_HOOK_OK;
|
||||
+
|
||||
+ policy = find_policy_for_client(u, cl);
|
||||
+ cd->policy = policy;
|
||||
+ cd->pid = cl->creds.pid;
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+static pa_hook_result_t client_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
|
||||
+ pa_client *cl;
|
||||
+
|
||||
+ pa_assert(c);
|
||||
+ pa_object_assert_ref(o);
|
||||
+
|
||||
+ cl = (pa_client *) o;
|
||||
+ pa_assert(cl);
|
||||
+
|
||||
+ client_data_remove(u, cl->index);
|
||||
+
|
||||
+ return PA_HOOK_OK;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+int pa__init(pa_module*m) {
|
||||
+ pa_modargs *ma = NULL;
|
||||
+ struct userdata *u;
|
||||
+ int i;
|
||||
+ access_policy *ap;
|
||||
+ DBusError error;
|
||||
+
|
||||
+ pa_assert(m);
|
||||
+
|
||||
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
+ pa_log("Failed to parse module arguments");
|
||||
+ goto fail;
|
||||
+ }
|
||||
+
|
||||
+ u = pa_xnew0(struct userdata, 1);
|
||||
+ u->core = m->core;
|
||||
+ m->userdata = u;
|
||||
+
|
||||
+ dbus_error_init(&error);
|
||||
+
|
||||
+ if (!(u->connection = pa_dbus_bus_get (u->core, DBUS_BUS_SESSION, &error))) {
|
||||
+ pa_log("Failed to connect to session bus: %s\n", error.message);
|
||||
+ dbus_error_free(&error);
|
||||
+ }
|
||||
+
|
||||
+ u->policies = pa_idxset_new (NULL, NULL);
|
||||
+ u->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, NULL,
|
||||
+ (pa_free_cb_t) client_data_free);
|
||||
+
|
||||
+ u->client_put_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PUT], PA_HOOK_EARLY, (pa_hook_cb_t) client_put_cb, u);
|
||||
+ u->client_auth_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_AUTH], PA_HOOK_EARLY, (pa_hook_cb_t) client_auth_cb, u);
|
||||
+ u->client_proplist_changed_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], PA_HOOK_EARLY, (pa_hook_cb_t) client_proplist_changed_cb, u);
|
||||
+ u->client_unlink_slot = pa_hook_connect(&u->core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) client_unlink_cb, u);
|
||||
+
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++) {
|
||||
+ pa_hook_cb_t cb;
|
||||
+
|
||||
+ if (i == PA_ACCESS_HOOK_FILTER_SUBSCRIBE_EVENT)
|
||||
+ cb = (pa_hook_cb_t) filter_event;
|
||||
+ else
|
||||
+ cb = (pa_hook_cb_t) check_access;
|
||||
+
|
||||
+ u->hook[i] = pa_hook_connect(&u->core->access[i], PA_HOOK_EARLY - 1, cb, u);
|
||||
+ }
|
||||
+
|
||||
+ ap = access_policy_new(u, false);
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SERVER] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_MODULE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CARD] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_STAT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SAMPLE] = rule_allow;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_PLAY_SAMPLE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_PLAYBACK] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_RECORD] = rule_allow;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CLIENT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_CLIENT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SINK_INPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SINK_INPUT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+
|
||||
+ u->default_policy = ap->index;
|
||||
+
|
||||
+ ap = access_policy_new(u, false);
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SERVER] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_MODULE] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CARD] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_STAT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SAMPLE] = rule_allow;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_PLAY_SAMPLE] = rule_check_portal;
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_PLAYBACK] = rule_check_portal;
|
||||
+ ap->rule[PA_ACCESS_HOOK_CONNECT_RECORD] = rule_check_portal;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_CLIENT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_CLIENT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SINK_INPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SINK_INPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SINK_INPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SINK_INPUT] = rule_check_owner;
|
||||
+
|
||||
+ ap->rule[PA_ACCESS_HOOK_CREATE_SOURCE_OUTPUT] = rule_allow;
|
||||
+ ap->rule[PA_ACCESS_HOOK_VIEW_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_MOVE_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME] = rule_check_owner;
|
||||
+ ap->rule[PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT] = rule_check_owner;
|
||||
+
|
||||
+ u->portal_policy = ap->index;
|
||||
+
|
||||
+ pa_modargs_free(ma);
|
||||
+ return 0;
|
||||
+
|
||||
+fail:
|
||||
+ pa__done(m);
|
||||
+
|
||||
+ if (ma)
|
||||
+ pa_modargs_free(ma);
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+void pa__done(pa_module*m) {
|
||||
+ struct userdata* u;
|
||||
+ int i;
|
||||
+
|
||||
+ pa_assert(m);
|
||||
+
|
||||
+ if (!(u = m->userdata))
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < PA_ACCESS_HOOK_MAX; i++) {
|
||||
+ if (u->hook[i])
|
||||
+ pa_hook_slot_free(u->hook[i]);
|
||||
+ }
|
||||
+
|
||||
+ if (u->policies)
|
||||
+ pa_idxset_free(u->policies, (pa_free_cb_t) access_policy_free);
|
||||
+
|
||||
+ if (u->client_put_slot)
|
||||
+ pa_hook_slot_free(u->client_put_slot);
|
||||
+ if (u->client_auth_slot)
|
||||
+ pa_hook_slot_free(u->client_auth_slot);
|
||||
+ if (u->client_proplist_changed_slot)
|
||||
+ pa_hook_slot_free(u->client_proplist_changed_slot);
|
||||
+ if (u->client_unlink_slot)
|
||||
+ pa_hook_slot_free(u->client_unlink_slot);
|
||||
+
|
||||
+ if (u->clients)
|
||||
+ pa_hashmap_free(u->clients);
|
||||
+
|
||||
+ if (u->connection)
|
||||
+ pa_dbus_connection_unref (u->connection);
|
||||
+
|
||||
+ pa_xfree(u);
|
||||
+}
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,26 +0,0 @@
|
||||
From 161ed876c3c810cb2de47516e11cf632591174bd Mon Sep 17 00:00:00 2001
|
||||
From: Matthias Clasen <mclasen@redhat.com>
|
||||
Date: Fri, 15 Jul 2016 15:10:20 -0400
|
||||
Subject: [PATCH 16/18] Make flatpak module load
|
||||
|
||||
It was using the wrong symver header.
|
||||
---
|
||||
src/modules/module-flatpak.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c
|
||||
index 64bbe86..cf428d4 100644
|
||||
--- a/src/modules/module-flatpak.c
|
||||
+++ b/src/modules/module-flatpak.c
|
||||
@@ -43,7 +43,7 @@
|
||||
#include <pulsecore/core-util.h>
|
||||
#include <pulsecore/dbus-shared.h>
|
||||
|
||||
-#include "module-access-symdef.h"
|
||||
+#include "module-flatpak-symdef.h"
|
||||
|
||||
PA_MODULE_AUTHOR("Matthias Clasen");
|
||||
PA_MODULE_DESCRIPTION("Controls access to server resources for flatpak apps");
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
From 6fc7131ec231865d2c1ce8267013d9cf78f36e68 Mon Sep 17 00:00:00 2001
|
||||
From: Matthias Clasen <mclasen@redhat.com>
|
||||
Date: Fri, 15 Jul 2016 16:05:36 -0400
|
||||
Subject: [PATCH 17/18] Make sure to set the pid in auth_cb
|
||||
|
||||
---
|
||||
src/modules/module-flatpak.c | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c
|
||||
index cf428d4..9375a9b 100644
|
||||
--- a/src/modules/module-flatpak.c
|
||||
+++ b/src/modules/module-flatpak.c
|
||||
@@ -510,7 +510,6 @@ static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata
|
||||
pa_client *cl;
|
||||
uint32_t policy;
|
||||
|
||||
-pa_log("client put\n");
|
||||
pa_assert(c);
|
||||
pa_object_assert_ref(o);
|
||||
|
||||
@@ -523,6 +522,8 @@ pa_log("client put\n");
|
||||
|
||||
client_data_new(u, cl->index, policy, cl->creds.pid);
|
||||
|
||||
+ pa_log("client put: policy %d, pid %u\n", policy, cl->creds.pid);
|
||||
+
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
@@ -543,6 +544,9 @@ static pa_hook_result_t client_auth_cb(pa_core *c, pa_object *o, struct userdata
|
||||
|
||||
policy = find_policy_for_client(u, cl);
|
||||
cd->policy = policy;
|
||||
+ cd->pid = cl->creds.pid;
|
||||
+
|
||||
+ pa_log("auth cb: policy %d, pid %u\n", cd->policy, cd->pid);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,278 +0,0 @@
|
||||
From e323416499a5c6c5b5261eabb59c5e6a7ffdc300 Mon Sep 17 00:00:00 2001
|
||||
From: Wim Taymans <wtaymans@redhat.com>
|
||||
Date: Mon, 13 Feb 2017 10:42:36 +0100
|
||||
Subject: [PATCH 18/18] Use permissive policy by default
|
||||
|
||||
Make a default permissive policy that allows everything when not
|
||||
sandboxed.
|
||||
Improve debug log
|
||||
---
|
||||
src/modules/module-flatpak.c | 69 ++++++++++++++++++++++----------------------
|
||||
1 file changed, 35 insertions(+), 34 deletions(-)
|
||||
|
||||
diff --git a/src/modules/module-flatpak.c b/src/modules/module-flatpak.c
|
||||
index 9375a9b..b223279 100644
|
||||
--- a/src/modules/module-flatpak.c
|
||||
+++ b/src/modules/module-flatpak.c
|
||||
@@ -87,8 +87,10 @@ struct userdata {
|
||||
pa_hook_slot *hook[PA_ACCESS_HOOK_MAX];
|
||||
|
||||
pa_idxset *policies;
|
||||
- uint32_t default_policy;
|
||||
+ uint32_t permissive_policy;
|
||||
+ uint32_t restricted_policy;
|
||||
uint32_t portal_policy;
|
||||
+ uint32_t default_policy;
|
||||
|
||||
pa_dbus_connection *connection;
|
||||
pa_hashmap *clients;
|
||||
@@ -152,7 +154,7 @@ static client_data * client_data_new(struct userdata *u, uint32_t index, uint32_
|
||||
cd->policy = policy;
|
||||
cd->pid = pid;
|
||||
pa_hashmap_put(u->clients, PA_UINT32_TO_PTR(index), cd);
|
||||
- pa_log("new client %d with pid %d, policy %d", index, pid, policy);
|
||||
+ pa_log_debug("new client %d with pid %d, policy %d", index, pid, policy);
|
||||
|
||||
return cd;
|
||||
}
|
||||
@@ -164,7 +166,7 @@ static void client_data_free(client_data *cd) {
|
||||
PA_LLIST_REMOVE(event_item, cd->events, e);
|
||||
pa_xfree(e);
|
||||
}
|
||||
- pa_log("removed client %d", cd->index);
|
||||
+ pa_log_debug("removed client %d", cd->index);
|
||||
pa_xfree(cd);
|
||||
}
|
||||
|
||||
@@ -207,23 +209,23 @@ static pa_hook_result_t rule_check_owner (pa_core *c, pa_access_data *d, struct
|
||||
break;
|
||||
}
|
||||
if (idx == d->client_index) {
|
||||
- pa_log("allow operation %d/%d of same client %d", d->hook, d->object_index, idx);
|
||||
+ pa_log_debug("allow operation %d/%d of same client %d", d->hook, d->object_index, idx);
|
||||
result = PA_HOOK_OK;
|
||||
} else
|
||||
- pa_log("blocked operation %d/%d of client %d to client %d", d->hook, d->object_index, idx, d->client_index);
|
||||
+ pa_log_debug("blocked operation %d/%d of client %d to client %d", d->hook, d->object_index, idx, d->client_index);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* rule allows the operation */
|
||||
static pa_hook_result_t rule_allow (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
- pa_log("allow operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ pa_log_debug("allow operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
/* rule blocks the operation */
|
||||
static pa_hook_result_t rule_block (pa_core *c, pa_access_data *d, struct userdata *u) {
|
||||
- pa_log("blocked operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ pa_log_debug("blocked operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
return PA_HOOK_STOP;
|
||||
}
|
||||
|
||||
@@ -241,14 +243,14 @@ static DBusHandlerResult portal_response(DBusConnection *connection, DBusMessage
|
||||
dbus_connection_remove_filter (connection, portal_response, cd);
|
||||
|
||||
if (!dbus_message_get_args(msg, &error, DBUS_TYPE_UINT32, &response, DBUS_TYPE_INVALID)) {
|
||||
- pa_log("failed to parse Response: %s\n", error.message);
|
||||
+ pa_log_error("failed to parse Response: %s\n", error.message);
|
||||
dbus_error_free(&error);
|
||||
}
|
||||
|
||||
cd->cached[d->hook].checked = true;
|
||||
cd->cached[d->hook].granted = response == 0 ? true : false;
|
||||
|
||||
- pa_log("portal check result: %d\n", cd->cached[d->hook].granted);
|
||||
+ pa_log_debug("portal check result: %d\n", cd->cached[d->hook].granted);
|
||||
|
||||
d->complete_cb (d, cd->cached[d->hook].granted);
|
||||
|
||||
@@ -269,11 +271,11 @@ static pa_hook_result_t rule_check_portal (pa_core *c, pa_access_data *d, struct
|
||||
const char *device;
|
||||
|
||||
if (cd->cached[d->hook].checked) {
|
||||
- pa_log("returned cached answer for portal check: %d\n", cd->cached[d->hook].granted);
|
||||
+ pa_log_debug("returned cached answer for portal check: %d\n", cd->cached[d->hook].granted);
|
||||
return cd->cached[d->hook].granted ? PA_HOOK_OK : PA_HOOK_STOP;
|
||||
}
|
||||
|
||||
- pa_log("ask portal for operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
+ pa_log_info("ask portal for operation %d/%d for client %d", d->hook, d->object_index, d->client_index);
|
||||
|
||||
cd->access_data = d;
|
||||
|
||||
@@ -311,7 +313,7 @@ static pa_hook_result_t rule_check_portal (pa_core *c, pa_access_data *d, struct
|
||||
dbus_message_iter_close_container (&msg_iter, &dict_iter);
|
||||
|
||||
if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->connection), m, -1, &error))) {
|
||||
- pa_log("Failed to call portal: %s\n", error.message);
|
||||
+ pa_log_error("Failed to call portal: %s\n", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(m);
|
||||
return PA_HOOK_STOP;
|
||||
@@ -320,7 +322,7 @@ static pa_hook_result_t rule_check_portal (pa_core *c, pa_access_data *d, struct
|
||||
dbus_message_unref(m);
|
||||
|
||||
if (!dbus_message_get_args(r, &error, DBUS_TYPE_OBJECT_PATH, &handle, DBUS_TYPE_INVALID)) {
|
||||
- pa_log("Failed to parse AccessDevice result: %s\n", error.message);
|
||||
+ pa_log_error("Failed to parse AccessDevice result: %s\n", error.message);
|
||||
dbus_error_free(&error);
|
||||
dbus_message_unref(r);
|
||||
return PA_HOOK_STOP;
|
||||
@@ -333,7 +335,7 @@ static pa_hook_result_t rule_check_portal (pa_core *c, pa_access_data *d, struct
|
||||
&error);
|
||||
dbus_connection_flush(pa_dbus_connection_get(u->connection));
|
||||
if (dbus_error_is_set(&error)) {
|
||||
- pa_log("Failed to subscribe to Request signal: %s\n", error.message);
|
||||
+ pa_log_error("Failed to subscribe to Request signal: %s\n", error.message);
|
||||
dbus_error_free(&error);
|
||||
return PA_HOOK_STOP;
|
||||
}
|
||||
@@ -407,7 +409,7 @@ static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct user
|
||||
case PA_SUBSCRIPTION_EVENT_REMOVE:
|
||||
/* if the client saw this object before, let the event go through */
|
||||
if (remove_event(cd, facility, d->object_index)) {
|
||||
- pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ pa_log_debug("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
break;
|
||||
@@ -415,7 +417,7 @@ static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct user
|
||||
case PA_SUBSCRIPTION_EVENT_CHANGE:
|
||||
/* if the client saw this object before, let it go through */
|
||||
if (find_event(cd, facility, d->object_index)) {
|
||||
- pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ pa_log_debug("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
|
||||
@@ -428,7 +430,7 @@ static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct user
|
||||
if (data.hook && pa_hook_fire(&c->access[data.hook], &data) == PA_HOOK_OK) {
|
||||
/* client can inspect the object, remember for later */
|
||||
add_event(cd, facility, d->object_index);
|
||||
- pa_log("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
+ pa_log_debug("pass event %02x/%d to client %d", d->event, d->object_index, d->client_index);
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
break;
|
||||
@@ -438,7 +440,7 @@ static pa_hook_result_t filter_event (pa_core *c, pa_access_data *d, struct user
|
||||
}
|
||||
|
||||
block:
|
||||
- pa_log("blocked event %02x/%d for client %d", d->event, d->object_index, d->client_index);
|
||||
+ pa_log_debug("blocked event %02x/%d for client %d", d->event, d->object_index, d->client_index);
|
||||
return PA_HOOK_STOP;
|
||||
}
|
||||
|
||||
@@ -455,10 +457,10 @@ client_is_sandboxed (pa_client *cl)
|
||||
pid_t pid;
|
||||
|
||||
if (cl->creds_valid) {
|
||||
- pa_log ("client has trusted pid %d", cl->creds.pid);
|
||||
+ pa_log_info ("client has trusted pid %d", cl->creds.pid);
|
||||
}
|
||||
else {
|
||||
- pa_log ("no trusted pid found, assuming not sandboxed\n");
|
||||
+ pa_log_info ("no trusted pid found, assuming not sandboxed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -479,7 +481,7 @@ client_is_sandboxed (pa_client *cl)
|
||||
if (strncmp(current, "1:name=systemd:", strlen("1:name=systemd:")) == 0) {
|
||||
const char *p = strstr(current, "flatpak-");
|
||||
if (p && p - current < n) {
|
||||
- pa_log("found a flatpak cgroup, assuming sandboxed\n");
|
||||
+ pa_log_info("found a flatpak cgroup, assuming sandboxed\n");
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
@@ -490,18 +492,12 @@ client_is_sandboxed (pa_client *cl)
|
||||
}
|
||||
|
||||
static uint32_t find_policy_for_client (struct userdata *u, pa_client *cl) {
|
||||
- char *s;
|
||||
-
|
||||
- s = pa_proplist_to_string(cl->proplist);
|
||||
- pa_log ("client proplist %s", s);
|
||||
- pa_xfree(s);
|
||||
-
|
||||
if (client_is_sandboxed (cl)) {
|
||||
- pa_log("client is sandboxed, choosing portal policy\n");
|
||||
+ pa_log_info("client is sandboxed, choosing portal policy\n");
|
||||
return u->portal_policy;
|
||||
}
|
||||
else {
|
||||
- pa_log("client not sandboxed, choosing default policy\n");
|
||||
+ pa_log_info("client not sandboxed, choosing default policy\n");
|
||||
return u->default_policy;
|
||||
}
|
||||
}
|
||||
@@ -522,7 +518,7 @@ static pa_hook_result_t client_put_cb(pa_core *c, pa_object *o, struct userdata
|
||||
|
||||
client_data_new(u, cl->index, policy, cl->creds.pid);
|
||||
|
||||
- pa_log("client put: policy %d, pid %u\n", policy, cl->creds.pid);
|
||||
+ pa_log_debug("client put: policy %d, pid %u\n", policy, cl->creds.pid);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
@@ -546,7 +542,7 @@ static pa_hook_result_t client_auth_cb(pa_core *c, pa_object *o, struct userdata
|
||||
cd->policy = policy;
|
||||
cd->pid = cl->creds.pid;
|
||||
|
||||
- pa_log("auth cb: policy %d, pid %u\n", cd->policy, cd->pid);
|
||||
+ pa_log_debug("auth cb: policy %d, pid %u\n", cd->policy, cd->pid);
|
||||
|
||||
return PA_HOOK_OK;
|
||||
}
|
||||
@@ -598,7 +594,7 @@ int pa__init(pa_module*m) {
|
||||
pa_assert(m);
|
||||
|
||||
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
||||
- pa_log("Failed to parse module arguments");
|
||||
+ pa_log_error("Failed to parse module arguments");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -609,7 +605,7 @@ int pa__init(pa_module*m) {
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (!(u->connection = pa_dbus_bus_get (u->core, DBUS_BUS_SESSION, &error))) {
|
||||
- pa_log("Failed to connect to session bus: %s\n", error.message);
|
||||
+ pa_log_error("Failed to connect to session bus: %s\n", error.message);
|
||||
dbus_error_free(&error);
|
||||
}
|
||||
|
||||
@@ -633,6 +629,9 @@ int pa__init(pa_module*m) {
|
||||
u->hook[i] = pa_hook_connect(&u->core->access[i], PA_HOOK_EARLY - 1, cb, u);
|
||||
}
|
||||
|
||||
+ ap = access_policy_new(u, true);
|
||||
+ u->permissive_policy = ap->index;
|
||||
+
|
||||
ap = access_policy_new(u, false);
|
||||
|
||||
ap->rule[PA_ACCESS_HOOK_VIEW_SINK] = rule_allow;
|
||||
@@ -662,7 +661,7 @@ int pa__init(pa_module*m) {
|
||||
ap->rule[PA_ACCESS_HOOK_SET_SOURCE_OUTPUT_VOLUME] = rule_check_owner;
|
||||
ap->rule[PA_ACCESS_HOOK_KILL_SOURCE_OUTPUT] = rule_check_owner;
|
||||
|
||||
- u->default_policy = ap->index;
|
||||
+ u->restricted_policy = ap->index;
|
||||
|
||||
ap = access_policy_new(u, false);
|
||||
|
||||
@@ -695,6 +694,8 @@ int pa__init(pa_module*m) {
|
||||
|
||||
u->portal_policy = ap->index;
|
||||
|
||||
+ u->default_policy = u->permissive_policy;
|
||||
+
|
||||
pa_modargs_free(ma);
|
||||
return 0;
|
||||
|
||||
--
|
||||
2.9.3
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
%global pa_major 10.0
|
||||
#global pa_minor 0
|
||||
%global pa_major 10.99
|
||||
%global pa_minor 1
|
||||
|
||||
#global snap 20141103
|
||||
#global gitrel 327
|
||||
@ -25,7 +25,7 @@
|
||||
Name: pulseaudio
|
||||
Summary: Improved Linux Sound Server
|
||||
Version: %{pa_major}%{?pa_minor:.%{pa_minor}}
|
||||
Release: 4%{?snap:.%{snap}git%{shortcommit}}%{?dist}
|
||||
Release: 1%{?snap:.%{snap}git%{shortcommit}}%{?dist}
|
||||
License: LGPLv2+
|
||||
URL: http://www.freedesktop.org/wiki/Software/PulseAudio
|
||||
%if 0%{?gitrel}
|
||||
@ -56,24 +56,6 @@ Patch3: pulseaudio-8.99.2-getaffinity.patch
|
||||
## upstream patches
|
||||
|
||||
## upstreamable patches
|
||||
Patch101: 0001-tagstruct-add-copy-method.patch
|
||||
Patch102: 0002-tagstruct-don-t-forget-to-copy-the-length.patch
|
||||
Patch103: 0003-subscribe-fix-typo.patch
|
||||
Patch104: 0004-creds-add-pid-to-pa_creds-and-use-store-it-in-pa_cli.patch
|
||||
Patch105: 0005-access-Add-access-control-hooks.patch
|
||||
Patch106: 0006-module-access-add-example-access-module.patch
|
||||
Patch107: 0007-module-access-add-async-handler-for-record.patch
|
||||
Patch108: 0008-module-access-use-the-auth-hook-and-pid.patch
|
||||
Patch109: 0009-pulsecore-Move-pa_core-structure-into-its-own-header.patch
|
||||
Patch110: 0010-Don-t-access-pa_core-structures-directly.patch
|
||||
Patch111: 0011-Add-access-checks.patch
|
||||
Patch112: 0012-protocol-native-add-async-access-checks.patch
|
||||
Patch113: 0013-core-add-current_client.patch
|
||||
Patch114: 0014-core-ensure-maincontext-for-current-client.patch
|
||||
Patch115: 0015-Add-flatpak-access-control.patch
|
||||
Patch116: 0016-Make-flatpak-module-load.patch
|
||||
Patch117: 0017-Make-sure-to-set-the-pid-in-auth_cb.patch
|
||||
Patch118: 0018-Use-permissive-policy-by-default.patch
|
||||
|
||||
BuildRequires: automake libtool
|
||||
BuildRequires: pkgconfig(bash-completion)
|
||||
@ -261,25 +243,6 @@ This package contains GDM integration hooks for the PulseAudio sound server.
|
||||
%patch2 -p1 -b .disable_flat_volumes
|
||||
%patch3 -p1 -b .affinity
|
||||
|
||||
%patch101 -p1 -b .101
|
||||
%patch102 -p1 -b .102
|
||||
%patch103 -p1 -b .103
|
||||
%patch104 -p1 -b .104
|
||||
%patch105 -p1 -b .105
|
||||
%patch106 -p1 -b .106
|
||||
%patch107 -p1 -b .107
|
||||
%patch108 -p1 -b .108
|
||||
%patch109 -p1 -b .109
|
||||
%patch110 -p1 -b .110
|
||||
%patch111 -p1 -b .111
|
||||
%patch112 -p1 -b .112
|
||||
%patch113 -p1 -b .113
|
||||
%patch114 -p1 -b .114
|
||||
%patch115 -p1 -b .115
|
||||
%patch116 -p1 -b .116
|
||||
%patch117 -p1 -b .117
|
||||
%patch118 -p1 -b .118
|
||||
|
||||
sed -i.no_consolekit -e \
|
||||
's/^load-module module-console-kit/#load-module module-console-kit/' \
|
||||
src/daemon/default.pa.in
|
||||
@ -622,6 +585,9 @@ exit 0
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Jul 25 2017 Rex Dieter <rdieter@fedoraproject.org> - 10.99.1-1
|
||||
- pulseaudio-10.99.1 (#1474559)
|
||||
|
||||
* Mon Feb 13 2017 Wim Taymans <wtaymans@redhat.com> - 10.0-4
|
||||
- Add flatpak access control
|
||||
|
||||
|
||||
6
sources
6
sources
@ -1,3 +1,3 @@
|
||||
SHA512 (pulseaudio-10.0.tar.xz) = 11d98b4b2000a41bdea92df253409452bc9b77d8bb309b6d14c439e3b902e3f90c69da00daff409e3859a54ad01c63a75be5723616bdcb492801d622a6406481
|
||||
SHA512 (pulseaudio-10.0.tar.xz.md5) = 6bacd8e56b8821eb839f3885535a0335f511a4ef4a9562c58a6d4c3db9ce474c1858bdadd099dc439c403f7697c795fccb9399fe87917291a251723796c8df6d
|
||||
SHA512 (pulseaudio-10.0.tar.xz.sha1) = c8ddc213c9d932da435caeef056dedf43d66e09ec8ffd7afc545c3a47a5c95c5888c03282870f6195cbad2bc07e49bdb106669f3af80e3e64f1fec53962ba3e5
|
||||
SHA512 (pulseaudio-10.99.1.tar.xz) = 410758da3cf3431b5810b9a5790d60ed8fe0bba58f621f4ca8e7ba66be8dcdd53cbd8284105ee6694b04f81a37791c3e8c5fe4af3ee034e89dff0b66fdbde006
|
||||
SHA512 (pulseaudio-10.99.1.tar.xz.md5) = ef645038fd76874b6f5841ef3d7559cf09f4b1d12834ac7520e09112e6c629a4955da321fd73dd3097c438915e191cf047bb2b141e61a886e1c9e7ca1393166b
|
||||
SHA512 (pulseaudio-10.99.1.tar.xz.sha1) = 9cfec7fd0a3ae13d2f1d61ae80485e48e4bf4d7d06169abc062063c289e155b2646fa0e14766e0d1145a58933b4310436e09959689ddf4a01d0739759282c52d
|
||||
|
||||
Loading…
Reference in New Issue
Block a user