pulseaudio-10.99.1 (#1474559)

This commit is contained in:
Rex Dieter 2017-07-25 03:49:19 -05:00
parent 708651acb9
commit db7177be89
20 changed files with 9 additions and 6995 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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