From b0ed4f1fdad9293612f5d070e094106df7a2c425 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Feb 13 2017 11:47:42 +0000 Subject: Add flatpak access control --- diff --git a/0001-tagstruct-add-copy-method.patch b/0001-tagstruct-add-copy-method.patch new file mode 100644 index 0000000..3b3da0b --- /dev/null +++ b/0001-tagstruct-add-copy-method.patch @@ -0,0 +1,52 @@ +From 12a76717da7950497ea94bad0ec449c43325ef0a Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0002-tagstruct-don-t-forget-to-copy-the-length.patch b/0002-tagstruct-don-t-forget-to-copy-the-length.patch new file mode 100644 index 0000000..95d76d3 --- /dev/null +++ b/0002-tagstruct-don-t-forget-to-copy-the-length.patch @@ -0,0 +1,24 @@ +From 8f43f8ceae382fafcdf59533a5f3776b41c174cd Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0003-subscribe-fix-typo.patch b/0003-subscribe-fix-typo.patch new file mode 100644 index 0000000..0f4d441 --- /dev/null +++ b/0003-subscribe-fix-typo.patch @@ -0,0 +1,25 @@ +From 4701b6b12e0c950bf96f2cc1db97688894e5fe15 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0004-creds-add-pid-to-pa_creds-and-use-store-it-in-pa_cli.patch b/0004-creds-add-pid-to-pa_creds-and-use-store-it-in-pa_cli.patch new file mode 100644 index 0000000..f740444 --- /dev/null +++ b/0004-creds-add-pid-to-pa_creds-and-use-store-it-in-pa_cli.patch @@ -0,0 +1,200 @@ +From ea28c5586af179bdc968a420a3d5e7ba42d00029 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + #include + #include ++#include + + /* 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 + diff --git a/0005-access-Add-access-control-hooks.patch b/0005-access-Add-access-control-hooks.patch new file mode 100644 index 0000000..436f653 --- /dev/null +++ b/0005-access-Add-access-control-hooks.patch @@ -0,0 +1,184 @@ +From e158102a2e40e702af97e451c6445b6d851f88f3 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 ++ ++ 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 . ++***/ ++ ++#include ++ ++#include ++ ++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 + #include + #include ++#include + + 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 + diff --git a/0006-module-access-add-example-access-module.patch b/0006-module-access-add-example-access-module.patch new file mode 100644 index 0000000..6e83cfb --- /dev/null +++ b/0006-module-access-add-example-access-module.patch @@ -0,0 +1,539 @@ +From 1eec5a0d01a657536cf8afb14d295d0f050ddd1d Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 ++ ++ 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 . ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + diff --git a/0007-module-access-add-async-handler-for-record.patch b/0007-module-access-add-async-handler-for-record.patch new file mode 100644 index 0000000..a729d74 --- /dev/null +++ b/0007-module-access-add-async-handler-for-record.patch @@ -0,0 +1,140 @@ +From 8f43958cf8e0aa7ba45cddbae3952e831fc9a08f Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + + #include ++#include ++#include + + #include + #include +@@ -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 + diff --git a/0008-module-access-use-the-auth-hook-and-pid.patch b/0008-module-access-use-the-auth-hook-and-pid.patch new file mode 100644 index 0000000..32e69ed --- /dev/null +++ b/0008-module-access-use-the-auth-hook-and-pid.patch @@ -0,0 +1,90 @@ +From 8fcf8065b8329ab9abe14ecddc1040e14adc6461 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0009-pulsecore-Move-pa_core-structure-into-its-own-header.patch b/0009-pulsecore-Move-pa_core-structure-into-its-own-header.patch new file mode 100644 index 0000000..ef13b29 --- /dev/null +++ b/0009-pulsecore-Move-pa_core-structure-into-its-own-header.patch @@ -0,0 +1,1619 @@ +From 6347cd3e72347d8698af0b48fc626fa43f4e218f Mon Sep 17 00:00:00 2001 +From: Arun Raghavan +Date: Mon, 4 Jul 2016 13:57:16 +0530 +Subject: [PATCH 09/18] pulsecore: Move pa_core structure into its own header + +The idea is to allow some parts of the code to use pa_core as an opaque +structure and access required members via API, over which we can then +perform some form of access control + +Signed-off-by: Arun Raghavan +--- + src/Makefile.am | 1 + + src/daemon/main.c | 1 + + src/daemon/server-lookup.c | 1 + + src/modules/alsa/alsa-sink.c | 1 + + src/modules/alsa/alsa-source.c | 1 + + src/modules/alsa/alsa-ucm.c | 1 + + src/modules/alsa/module-alsa-card.c | 1 + + src/modules/bluetooth/backend-native.c | 1 + + src/modules/bluetooth/bluez4-util.c | 1 + + src/modules/bluetooth/bluez5-util.c | 1 + + src/modules/bluetooth/module-bluetooth-policy.c | 1 + + src/modules/bluetooth/module-bluez4-device.c | 1 + + src/modules/bluetooth/module-bluez5-device.c | 1 + + src/modules/bluetooth/module-bluez5-discover.c | 1 + + src/modules/dbus/iface-card.c | 1 + + src/modules/dbus/iface-client.c | 1 + + src/modules/dbus/iface-core.c | 1 + + src/modules/dbus/iface-device-port.c | 1 + + src/modules/dbus/iface-device.c | 1 + + src/modules/dbus/iface-memstats.c | 1 + + src/modules/dbus/iface-module.c | 1 + + src/modules/dbus/iface-sample.c | 1 + + src/modules/dbus/iface-stream.c | 1 + + src/modules/dbus/module-dbus-protocol.c | 1 + + src/modules/echo-cancel/adrian.c | 1 + + src/modules/echo-cancel/module-echo-cancel.c | 1 + + src/modules/gconf/module-gconf.c | 1 + + src/modules/jack/module-jack-sink.c | 1 + + src/modules/jack/module-jack-source.c | 1 + + src/modules/module-access.c | 1 + + src/modules/module-allow-passthrough.c | 1 + + src/modules/module-always-sink.c | 1 + + src/modules/module-augment-properties.c | 1 + + src/modules/module-card-restore.c | 1 + + src/modules/module-cli.c | 1 + + src/modules/module-combine-sink.c | 1 + + src/modules/module-default-device-restore.c | 1 + + src/modules/module-device-manager.c | 1 + + src/modules/module-device-restore.c | 1 + + src/modules/module-equalizer-sink.c | 1 + + src/modules/module-esound-sink.c | 1 + + src/modules/module-filter-apply.c | 1 + + src/modules/module-filter-heuristics.c | 1 + + src/modules/module-intended-roles.c | 1 + + src/modules/module-ladspa-sink.c | 1 + + src/modules/module-lirc.c | 1 + + src/modules/module-loopback.c | 1 + + src/modules/module-match.c | 1 + + src/modules/module-mmkbd-evdev.c | 1 + + src/modules/module-native-protocol-fd.c | 1 + + src/modules/module-null-sink.c | 1 + + src/modules/module-null-source.c | 1 + + src/modules/module-pipe-sink.c | 1 + + src/modules/module-pipe-source.c | 1 + + src/modules/module-position-event-sounds.c | 1 + + src/modules/module-protocol-stub.c | 1 + + src/modules/module-rescue-streams.c | 1 + + src/modules/module-role-cork.c | 1 + + src/modules/module-role-ducking.c | 1 + + src/modules/module-rygel-media-server.c | 1 + + src/modules/module-sine-source.c | 1 + + src/modules/module-sine.c | 1 + + src/modules/module-stream-restore.c | 1 + + src/modules/module-suspend-on-idle.c | 1 + + src/modules/module-switch-on-connect.c | 1 + + src/modules/module-switch-on-port-available.c | 1 + + src/modules/module-systemd-login.c | 1 + + src/modules/module-tunnel-sink-new.c | 1 + + src/modules/module-tunnel-source-new.c | 1 + + src/modules/module-tunnel.c | 1 + + src/modules/module-udev-detect.c | 1 + + src/modules/module-virtual-sink.c | 1 + + src/modules/module-virtual-source.c | 1 + + src/modules/module-virtual-surround-sink.c | 1 + + src/modules/module-zeroconf-discover.c | 1 + + src/modules/module-zeroconf-publish.c | 1 + + src/modules/oss/module-oss.c | 1 + + src/modules/raop/module-raop-discover.c | 1 + + src/modules/raop/module-raop-sink.c | 1 + + src/modules/raop/raop_client.c | 1 + + src/modules/rtp/module-rtp-recv.c | 1 + + src/modules/rtp/module-rtp-send.c | 1 + + src/modules/stream-interaction.c | 1 + + src/modules/x11/module-x11-cork-request.c | 1 + + src/modules/x11/module-x11-xsmp.c | 1 + + src/pulsecore/card.c | 1 + + src/pulsecore/cli-command.c | 1 + + src/pulsecore/cli-text.c | 1 + + src/pulsecore/client.c | 1 + + src/pulsecore/core-scache.c | 1 + + src/pulsecore/core-struct.h | 103 ++++++++++++++++++++++++ + src/pulsecore/core-subscribe.c | 1 + + src/pulsecore/core.c | 1 + + src/pulsecore/core.h | 76 ----------------- + src/pulsecore/dbus-shared.c | 1 + + src/pulsecore/device-port.c | 1 + + src/pulsecore/module.c | 1 + + src/pulsecore/namereg.c | 1 + + src/pulsecore/play-memchunk.c | 1 + + src/pulsecore/protocol-esound.c | 1 + + src/pulsecore/protocol-http.c | 1 + + src/pulsecore/protocol-native.c | 1 + + src/pulsecore/protocol-simple.c | 1 + + src/pulsecore/shared.c | 1 + + src/pulsecore/sink-input.c | 1 + + src/pulsecore/sink.c | 1 + + src/pulsecore/sound-file-stream.c | 1 + + src/pulsecore/source-output.c | 1 + + src/pulsecore/source.c | 1 + + src/pulsecore/x11wrap.c | 1 + + 110 files changed, 211 insertions(+), 76 deletions(-) + create mode 100644 src/pulsecore/core-struct.h + +diff --git a/src/Makefile.am b/src/Makefile.am +index 7c66064..70bc848 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -960,6 +960,7 @@ libpulsecore_@PA_MAJORMINOR@_la_SOURCES = \ + pulsecore/core-scache.c pulsecore/core-scache.h \ + pulsecore/core-subscribe.c pulsecore/core-subscribe.h \ + pulsecore/core.c pulsecore/core.h \ ++ pulsecore/core-struct.h \ + pulsecore/hook-list.c pulsecore/hook-list.h \ + pulsecore/ltdl-helper.c pulsecore/ltdl-helper.h \ + pulsecore/modargs.c pulsecore/modargs.h \ +diff --git a/src/daemon/main.c b/src/daemon/main.c +index dc5e5bc..cb69f17 100644 +--- a/src/daemon/main.c ++++ b/src/daemon/main.c +@@ -73,6 +73,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/daemon/server-lookup.c b/src/daemon/server-lookup.c +index ca390c7..b5c53e4 100644 +--- a/src/daemon/server-lookup.c ++++ b/src/daemon/server-lookup.c +@@ -27,6 +27,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c +index 886c735..41ef1ae 100644 +--- a/src/modules/alsa/alsa-sink.c ++++ b/src/modules/alsa/alsa-sink.c +@@ -38,6 +38,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c +index b788df2..4a3f407 100644 +--- a/src/modules/alsa/alsa-source.c ++++ b/src/modules/alsa/alsa-source.c +@@ -33,6 +33,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/alsa/alsa-ucm.c b/src/modules/alsa/alsa-ucm.c +index b42c040..227ed7d 100644 +--- a/src/modules/alsa/alsa-ucm.c ++++ b/src/modules/alsa/alsa-ucm.c +@@ -40,6 +40,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c +index 4f1236e..b6ac865 100644 +--- a/src/modules/alsa/module-alsa-card.c ++++ b/src/modules/alsa/module-alsa-card.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c +index cf88126..f78cebc 100644 +--- a/src/modules/bluetooth/backend-native.c ++++ b/src/modules/bluetooth/backend-native.c +@@ -22,6 +22,7 @@ + #endif + + #include ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/bluez4-util.c b/src/modules/bluetooth/bluez4-util.c +index 542ce35..c582b95 100644 +--- a/src/modules/bluetooth/bluez4-util.c ++++ b/src/modules/bluetooth/bluez4-util.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c +index 7d63f35..dac176b 100644 +--- a/src/modules/bluetooth/bluez5-util.c ++++ b/src/modules/bluetooth/bluez5-util.c +@@ -26,6 +26,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/module-bluetooth-policy.c b/src/modules/bluetooth/module-bluetooth-policy.c +index df702cc..aa9fa4a 100644 +--- a/src/modules/bluetooth/module-bluetooth-policy.c ++++ b/src/modules/bluetooth/module-bluetooth-policy.c +@@ -26,6 +26,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/module-bluez4-device.c b/src/modules/bluetooth/module-bluez4-device.c +index ac4ed63..3f2e70f 100644 +--- a/src/modules/bluetooth/module-bluez4-device.c ++++ b/src/modules/bluetooth/module-bluez4-device.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/module-bluez5-device.c b/src/modules/bluetooth/module-bluez5-device.c +index 065fcaa..0dbe6f3 100644 +--- a/src/modules/bluetooth/module-bluez5-device.c ++++ b/src/modules/bluetooth/module-bluez5-device.c +@@ -30,6 +30,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/bluetooth/module-bluez5-discover.c b/src/modules/bluetooth/module-bluez5-discover.c +index 080e5d0..a5a0b45 100644 +--- a/src/modules/bluetooth/module-bluez5-discover.c ++++ b/src/modules/bluetooth/module-bluez5-discover.c +@@ -22,6 +22,7 @@ + #endif + + #include ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-card.c b/src/modules/dbus/iface-card.c +index 1b705ba..ffcbc7a 100644 +--- a/src/modules/dbus/iface-card.c ++++ b/src/modules/dbus/iface-card.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-client.c b/src/modules/dbus/iface-client.c +index 455ea45..3f3003f 100644 +--- a/src/modules/dbus/iface-client.c ++++ b/src/modules/dbus/iface-client.c +@@ -24,6 +24,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-core.c b/src/modules/dbus/iface-core.c +index 508913d..bfabd61 100644 +--- a/src/modules/dbus/iface-core.c ++++ b/src/modules/dbus/iface-core.c +@@ -28,6 +28,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-device-port.c b/src/modules/dbus/iface-device-port.c +index 4892443..96a2abd 100644 +--- a/src/modules/dbus/iface-device-port.c ++++ b/src/modules/dbus/iface-device-port.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + +diff --git a/src/modules/dbus/iface-device.c b/src/modules/dbus/iface-device.c +index 2c370a8..abaa42b 100644 +--- a/src/modules/dbus/iface-device.c ++++ b/src/modules/dbus/iface-device.c +@@ -21,6 +21,7 @@ + #include + #endif + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-memstats.c b/src/modules/dbus/iface-memstats.c +index cd1d7ea..6ed4b07 100644 +--- a/src/modules/dbus/iface-memstats.c ++++ b/src/modules/dbus/iface-memstats.c +@@ -23,6 +23,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-module.c b/src/modules/dbus/iface-module.c +index 222cd73..a6fdf50 100644 +--- a/src/modules/dbus/iface-module.c ++++ b/src/modules/dbus/iface-module.c +@@ -21,6 +21,7 @@ + #include + #endif + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-sample.c b/src/modules/dbus/iface-sample.c +index 61f2ba0..accdf46 100644 +--- a/src/modules/dbus/iface-sample.c ++++ b/src/modules/dbus/iface-sample.c +@@ -21,6 +21,7 @@ + #include + #endif + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c +index ade62ca..76bbeb0 100644 +--- a/src/modules/dbus/iface-stream.c ++++ b/src/modules/dbus/iface-stream.c +@@ -22,6 +22,7 @@ + #include + #endif + ++#include + #include + #include + #include +diff --git a/src/modules/dbus/module-dbus-protocol.c b/src/modules/dbus/module-dbus-protocol.c +index 8079d6b..abd340a 100644 +--- a/src/modules/dbus/module-dbus-protocol.c ++++ b/src/modules/dbus/module-dbus-protocol.c +@@ -30,6 +30,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/echo-cancel/adrian.c b/src/modules/echo-cancel/adrian.c +index 3c47fae..870a1fe 100644 +--- a/src/modules/echo-cancel/adrian.c ++++ b/src/modules/echo-cancel/adrian.c +@@ -29,6 +29,7 @@ + + #include + ++#include + #include + + #include "echo-cancel.h" +diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c +index dfd05b6..f3f24ed 100644 +--- a/src/modules/echo-cancel/module-echo-cancel.c ++++ b/src/modules/echo-cancel/module-echo-cancel.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/gconf/module-gconf.c b/src/modules/gconf/module-gconf.c +index 1e1b855..5baa1ae 100644 +--- a/src/modules/gconf/module-gconf.c ++++ b/src/modules/gconf/module-gconf.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/jack/module-jack-sink.c b/src/modules/jack/module-jack-sink.c +index 4d31493..22dbbad 100644 +--- a/src/modules/jack/module-jack-sink.c ++++ b/src/modules/jack/module-jack-sink.c +@@ -33,6 +33,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/jack/module-jack-source.c b/src/modules/jack/module-jack-source.c +index e45f304..ce5a096 100644 +--- a/src/modules/jack/module-jack-source.c ++++ b/src/modules/jack/module-jack-source.c +@@ -33,6 +33,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-access.c b/src/modules/module-access.c +index c00612a..39e2f0e 100644 +--- a/src/modules/module-access.c ++++ b/src/modules/module-access.c +@@ -34,6 +34,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-allow-passthrough.c b/src/modules/module-allow-passthrough.c +index 4b801e4..45e7a41 100644 +--- a/src/modules/module-allow-passthrough.c ++++ b/src/modules/module-allow-passthrough.c +@@ -26,6 +26,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c +index 12f63f7..a7f3568 100644 +--- a/src/modules/module-always-sink.c ++++ b/src/modules/module-always-sink.c +@@ -24,6 +24,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-augment-properties.c b/src/modules/module-augment-properties.c +index 541f0e7..4608d45 100644 +--- a/src/modules/module-augment-properties.c ++++ b/src/modules/module-augment-properties.c +@@ -28,6 +28,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c +index 3c0307b..ff6066f 100644 +--- a/src/modules/module-card-restore.c ++++ b/src/modules/module-card-restore.c +@@ -33,6 +33,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-cli.c b/src/modules/module-cli.c +index a69ad4a..b23519d 100644 +--- a/src/modules/module-cli.c ++++ b/src/modules/module-cli.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + + #include "module-cli-symdef.h" +diff --git a/src/modules/module-combine-sink.c b/src/modules/module-combine-sink.c +index 250240a..4fadbb9 100644 +--- a/src/modules/module-combine-sink.c ++++ b/src/modules/module-combine-sink.c +@@ -35,6 +35,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c +index d76e28e..4b169bb 100644 +--- a/src/modules/module-default-device-restore.c ++++ b/src/modules/module-default-device-restore.c +@@ -28,6 +28,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-device-manager.c b/src/modules/module-device-manager.c +index 2a0a67f..17406c3 100644 +--- a/src/modules/module-device-manager.c ++++ b/src/modules/module-device-manager.c +@@ -34,6 +34,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c +index 8d7b34b..f150dbd 100644 +--- a/src/modules/module-device-restore.c ++++ b/src/modules/module-device-restore.c +@@ -37,6 +37,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c +index 9c25f3f..8c50468 100644 +--- a/src/modules/module-equalizer-sink.c ++++ b/src/modules/module-equalizer-sink.c +@@ -46,6 +46,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c +index 2ce0c85..f304c97 100644 +--- a/src/modules/module-esound-sink.c ++++ b/src/modules/module-esound-sink.c +@@ -48,6 +48,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-filter-apply.c b/src/modules/module-filter-apply.c +index 364d68b..ceeb541 100644 +--- a/src/modules/module-filter-apply.c ++++ b/src/modules/module-filter-apply.c +@@ -26,6 +26,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-filter-heuristics.c b/src/modules/module-filter-heuristics.c +index 423e873..15baa1c 100644 +--- a/src/modules/module-filter-heuristics.c ++++ b/src/modules/module-filter-heuristics.c +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-intended-roles.c b/src/modules/module-intended-roles.c +index 60ec7e9..e083c11 100644 +--- a/src/modules/module-intended-roles.c ++++ b/src/modules/module-intended-roles.c +@@ -24,6 +24,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c +index c11fa5e..3602830 100644 +--- a/src/modules/module-ladspa-sink.c ++++ b/src/modules/module-ladspa-sink.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c +index d63fdbd..7c325da 100644 +--- a/src/modules/module-lirc.c ++++ b/src/modules/module-lirc.c +@@ -30,6 +30,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-loopback.c b/src/modules/module-loopback.c +index 9410c98..75da911 100644 +--- a/src/modules/module-loopback.c ++++ b/src/modules/module-loopback.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + + #include +diff --git a/src/modules/module-match.c b/src/modules/module-match.c +index 559687c..f371139 100644 +--- a/src/modules/module-match.c ++++ b/src/modules/module-match.c +@@ -36,6 +36,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c +index 6c9cf34..818a502 100644 +--- a/src/modules/module-mmkbd-evdev.c ++++ b/src/modules/module-mmkbd-evdev.c +@@ -32,6 +32,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-native-protocol-fd.c b/src/modules/module-native-protocol-fd.c +index 5fd7f6a..c951bc6 100644 +--- a/src/modules/module-native-protocol-fd.c ++++ b/src/modules/module-native-protocol-fd.c +@@ -24,6 +24,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c +index b8157e8..be3c98f 100644 +--- a/src/modules/module-null-sink.c ++++ b/src/modules/module-null-sink.c +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-null-source.c b/src/modules/module-null-source.c +index a75a04f..a791883 100644 +--- a/src/modules/module-null-source.c ++++ b/src/modules/module-null-source.c +@@ -31,6 +31,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c +index da65021..0b32f0d 100644 +--- a/src/modules/module-pipe-sink.c ++++ b/src/modules/module-pipe-sink.c +@@ -35,6 +35,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c +index f39fc55..4b651cc 100644 +--- a/src/modules/module-pipe-source.c ++++ b/src/modules/module-pipe-source.c +@@ -35,6 +35,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c +index fb9f5b3..ef03786 100644 +--- a/src/modules/module-position-event-sounds.c ++++ b/src/modules/module-position-event-sounds.c +@@ -32,6 +32,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-protocol-stub.c b/src/modules/module-protocol-stub.c +index 5a183c8..8fd4392 100644 +--- a/src/modules/module-protocol-stub.c ++++ b/src/modules/module-protocol-stub.c +@@ -32,6 +32,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c +index 60ac1c4..e844e16 100644 +--- a/src/modules/module-rescue-streams.c ++++ b/src/modules/module-rescue-streams.c +@@ -24,6 +24,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-role-cork.c b/src/modules/module-role-cork.c +index d71cc38..d11be8c 100644 +--- a/src/modules/module-role-cork.c ++++ b/src/modules/module-role-cork.c +@@ -23,6 +23,7 @@ + + #include + #include ++#include + #include + + #include "module-role-cork-symdef.h" +diff --git a/src/modules/module-role-ducking.c b/src/modules/module-role-ducking.c +index add2d36..af3e98c 100644 +--- a/src/modules/module-role-ducking.c ++++ b/src/modules/module-role-ducking.c +@@ -23,6 +23,7 @@ + + #include + #include ++#include + #include + + #include "module-role-ducking-symdef.h" +diff --git a/src/modules/module-rygel-media-server.c b/src/modules/module-rygel-media-server.c +index e2c2e6f..0737fa9 100644 +--- a/src/modules/module-rygel-media-server.c ++++ b/src/modules/module-rygel-media-server.c +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c +index cdeb2c0..a3be322 100644 +--- a/src/modules/module-sine-source.c ++++ b/src/modules/module-sine-source.c +@@ -32,6 +32,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c +index d56fae5..a95cf79 100644 +--- a/src/modules/module-sine.c ++++ b/src/modules/module-sine.c +@@ -25,6 +25,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c +index d1c2597..75ca729 100644 +--- a/src/modules/module-stream-restore.c ++++ b/src/modules/module-stream-restore.c +@@ -35,6 +35,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c +index a284f85..030de4d 100644 +--- a/src/modules/module-suspend-on-idle.c ++++ b/src/modules/module-suspend-on-idle.c +@@ -26,6 +26,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-switch-on-connect.c b/src/modules/module-switch-on-connect.c +index c844add..f3171e9 100644 +--- a/src/modules/module-switch-on-connect.c ++++ b/src/modules/module-switch-on-connect.c +@@ -25,6 +25,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c +index b9a0f3b..ee787cc 100644 +--- a/src/modules/module-switch-on-port-available.c ++++ b/src/modules/module-switch-on-port-available.c +@@ -23,6 +23,7 @@ + #endif + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-systemd-login.c b/src/modules/module-systemd-login.c +index d15bee5..62eeb4c 100644 +--- a/src/modules/module-systemd-login.c ++++ b/src/modules/module-systemd-login.c +@@ -32,6 +32,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-tunnel-sink-new.c b/src/modules/module-tunnel-sink-new.c +index 92f99df..82eb7c6 100644 +--- a/src/modules/module-tunnel-sink-new.c ++++ b/src/modules/module-tunnel-sink-new.c +@@ -30,6 +30,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-tunnel-source-new.c b/src/modules/module-tunnel-source-new.c +index e159c33..ff40140 100644 +--- a/src/modules/module-tunnel-source-new.c ++++ b/src/modules/module-tunnel-source-new.c +@@ -30,6 +30,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c +index d7114ee..9e5f316 100644 +--- a/src/modules/module-tunnel.c ++++ b/src/modules/module-tunnel.c +@@ -40,6 +40,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-udev-detect.c b/src/modules/module-udev-detect.c +index bb41a96..d36b136 100644 +--- a/src/modules/module-udev-detect.c ++++ b/src/modules/module-udev-detect.c +@@ -30,6 +30,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c +index 02cc1ac..286310e 100644 +--- a/src/modules/module-virtual-sink.c ++++ b/src/modules/module-virtual-sink.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c +index 36edf78..b3403a6 100644 +--- a/src/modules/module-virtual-source.c ++++ b/src/modules/module-virtual-source.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-virtual-surround-sink.c b/src/modules/module-virtual-surround-sink.c +index 6c7120a..4d0cee5 100644 +--- a/src/modules/module-virtual-surround-sink.c ++++ b/src/modules/module-virtual-surround-sink.c +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/module-zeroconf-discover.c b/src/modules/module-zeroconf-discover.c +index 96476b7..e628c6b 100644 +--- a/src/modules/module-zeroconf-discover.c ++++ b/src/modules/module-zeroconf-discover.c +@@ -35,6 +35,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/module-zeroconf-publish.c b/src/modules/module-zeroconf-publish.c +index 482881c..74018eb 100644 +--- a/src/modules/module-zeroconf-publish.c ++++ b/src/modules/module-zeroconf-publish.c +@@ -39,6 +39,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c +index 8a5a692..a53b725 100644 +--- a/src/modules/oss/module-oss.c ++++ b/src/modules/oss/module-oss.c +@@ -51,6 +51,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/raop/module-raop-discover.c b/src/modules/raop/module-raop-discover.c +index f083044..f4ac133 100644 +--- a/src/modules/raop/module-raop-discover.c ++++ b/src/modules/raop/module-raop-discover.c +@@ -36,6 +36,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c +index 7a97e83..2827b04 100644 +--- a/src/modules/raop/module-raop-sink.c ++++ b/src/modules/raop/module-raop-sink.c +@@ -41,6 +41,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/modules/raop/raop_client.c b/src/modules/raop/raop_client.c +index 864c558..bd4f90b 100644 +--- a/src/modules/raop/raop_client.c ++++ b/src/modules/raop/raop_client.c +@@ -40,6 +40,7 @@ + + #include + ++#include + #include + #include + #include +diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c +index 5977500..e77f3d0 100644 +--- a/src/modules/rtp/module-rtp-recv.c ++++ b/src/modules/rtp/module-rtp-recv.c +@@ -34,6 +34,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c +index 15a7436..ef5c526 100644 +--- a/src/modules/rtp/module-rtp-send.c ++++ b/src/modules/rtp/module-rtp-send.c +@@ -32,6 +32,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/modules/stream-interaction.c b/src/modules/stream-interaction.c +index 4184786..728398c 100644 +--- a/src/modules/stream-interaction.c ++++ b/src/modules/stream-interaction.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/modules/x11/module-x11-cork-request.c b/src/modules/x11/module-x11-cork-request.c +index 5c76711..a86a82e 100644 +--- a/src/modules/x11/module-x11-cork-request.c ++++ b/src/modules/x11/module-x11-cork-request.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + #include + + #include "module-x11-cork-request-symdef.h" +diff --git a/src/modules/x11/module-x11-xsmp.c b/src/modules/x11/module-x11-xsmp.c +index 7c6fb23..5f09d52 100644 +--- a/src/modules/x11/module-x11-xsmp.c ++++ b/src/modules/x11/module-x11-xsmp.c +@@ -31,6 +31,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c +index bc5b75b..39f4261 100644 +--- a/src/pulsecore/card.c ++++ b/src/pulsecore/card.c +@@ -30,6 +30,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c +index 9a73605..05c2b8f 100644 +--- a/src/pulsecore/cli-command.c ++++ b/src/pulsecore/cli-command.c +@@ -47,6 +47,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c +index af79a1e..9e32524 100644 +--- a/src/pulsecore/cli-text.c ++++ b/src/pulsecore/cli-text.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/client.c b/src/pulsecore/client.c +index 2e6af47..24faa1f 100644 +--- a/src/pulsecore/client.c ++++ b/src/pulsecore/client.c +@@ -29,6 +29,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/core-scache.c b/src/pulsecore/core-scache.c +index 026eeca..8d54720 100644 +--- a/src/pulsecore/core-scache.c ++++ b/src/pulsecore/core-scache.c +@@ -49,6 +49,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/core-struct.h b/src/pulsecore/core-struct.h +new file mode 100644 +index 0000000..e6aa374 +--- /dev/null ++++ b/src/pulsecore/core-struct.h +@@ -0,0 +1,103 @@ ++#ifndef foocorestructhfoo ++#define foocorestructhfoo ++ ++/*** ++ This file is part of PulseAudio. ++ ++ Copyright 2004-2006 Lennart Poettering ++ ++ 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 . ++***/ ++ ++#include "core.h" ++ ++/* The core structure of PulseAudio. Every PulseAudio daemon contains ++ * exactly one of these. It is used for storing kind of global ++ * variables for the daemon. */ ++ ++struct pa_core { ++ pa_msgobject parent; ++ ++ pa_core_state_t state; ++ ++ /* A random value which may be used to identify this instance of ++ * PulseAudio. Not cryptographically secure in any way. */ ++ uint32_t cookie; ++ ++ pa_mainloop_api *mainloop; ++ ++ /* idxset of all kinds of entities */ ++ pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache; ++ ++ /* Some hashmaps for all sorts of entities */ ++ pa_hashmap *namereg, *shared; ++ ++ /* The default sink/source */ ++ pa_source *default_source; ++ pa_sink *default_sink; ++ ++ pa_channel_map default_channel_map; ++ pa_sample_spec default_sample_spec; ++ uint32_t alternate_sample_rate; ++ unsigned default_n_fragments, default_fragment_size_msec; ++ unsigned deferred_volume_safety_margin_usec; ++ int deferred_volume_extra_delay_usec; ++ unsigned lfe_crossover_freq; ++ ++ pa_defer_event *module_defer_unload_event; ++ pa_hashmap *modules_pending_unload; /* pa_module -> pa_module (hashmap-as-a-set) */ ++ ++ pa_defer_event *subscription_defer_event; ++ PA_LLIST_HEAD(pa_subscription, subscriptions); ++ PA_LLIST_HEAD(pa_subscription_event, subscription_event_queue); ++ pa_subscription_event *subscription_event_last; ++ ++ /* The mempool is used for data we write to, it's readonly for the client. */ ++ pa_mempool *mempool; ++ ++ /* Shared memory size, as specified either by daemon configuration ++ * or PA daemon defaults (~ 64 MiB). */ ++ size_t shm_size; ++ ++ pa_silence_cache silence_cache; ++ ++ pa_time_event *exit_event; ++ pa_time_event *scache_auto_unload_event; ++ ++ int exit_idle_time, scache_idle_time; ++ ++ bool flat_volumes:1; ++ bool disallow_module_loading:1; ++ bool disallow_exit:1; ++ bool running_as_daemon:1; ++ bool realtime_scheduling:1; ++ bool avoid_resampling:1; ++ bool disable_remixing:1; ++ bool remixing_use_all_sink_channels:1; ++ bool disable_lfe_remixing:1; ++ bool deferred_volume:1; ++ ++ pa_resample_method_t resample_method; ++ int realtime_priority; ++ ++ pa_server_type_t server_type; ++ pa_cpu_info cpu_info; ++ ++ /* hooks */ ++ pa_hook hooks[PA_CORE_HOOK_MAX]; ++ /* access hooks */ ++ pa_hook access[PA_ACCESS_HOOK_MAX]; ++}; ++ ++#endif /* foocorestructhfoo */ +diff --git a/src/pulsecore/core-subscribe.c b/src/pulsecore/core-subscribe.c +index 5a88b7e..88d900b 100644 +--- a/src/pulsecore/core-subscribe.c ++++ b/src/pulsecore/core-subscribe.c +@@ -28,6 +28,7 @@ + #include + #include + ++#include + #include "core-subscribe.h" + + /* The subscription subsystem may be used to be notified whenever an +diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c +index e5d6879..b8dbde9 100644 +--- a/src/pulsecore/core.c ++++ b/src/pulsecore/core.c +@@ -31,6 +31,7 @@ + #include + + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index 152a53f..955db89 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -141,82 +141,6 @@ typedef enum pa_core_hook { + PA_CORE_HOOK_MAX + } pa_core_hook_t; + +-/* The core structure of PulseAudio. Every PulseAudio daemon contains +- * exactly one of these. It is used for storing kind of global +- * variables for the daemon. */ +- +-struct pa_core { +- pa_msgobject parent; +- +- pa_core_state_t state; +- +- /* A random value which may be used to identify this instance of +- * PulseAudio. Not cryptographically secure in any way. */ +- uint32_t cookie; +- +- pa_mainloop_api *mainloop; +- +- /* idxset of all kinds of entities */ +- pa_idxset *clients, *cards, *sinks, *sources, *sink_inputs, *source_outputs, *modules, *scache; +- +- /* Some hashmaps for all sorts of entities */ +- pa_hashmap *namereg, *shared; +- +- /* The default sink/source */ +- pa_source *default_source; +- pa_sink *default_sink; +- +- pa_channel_map default_channel_map; +- pa_sample_spec default_sample_spec; +- uint32_t alternate_sample_rate; +- unsigned default_n_fragments, default_fragment_size_msec; +- unsigned deferred_volume_safety_margin_usec; +- int deferred_volume_extra_delay_usec; +- unsigned lfe_crossover_freq; +- +- pa_defer_event *module_defer_unload_event; +- pa_hashmap *modules_pending_unload; /* pa_module -> pa_module (hashmap-as-a-set) */ +- +- pa_defer_event *subscription_defer_event; +- PA_LLIST_HEAD(pa_subscription, subscriptions); +- PA_LLIST_HEAD(pa_subscription_event, subscription_event_queue); +- pa_subscription_event *subscription_event_last; +- +- /* The mempool is used for data we write to, it's readonly for the client. */ +- pa_mempool *mempool; +- +- /* Shared memory size, as specified either by daemon configuration +- * or PA daemon defaults (~ 64 MiB). */ +- size_t shm_size; +- +- pa_silence_cache silence_cache; +- +- pa_time_event *exit_event; +- pa_time_event *scache_auto_unload_event; +- +- int exit_idle_time, scache_idle_time; +- +- bool flat_volumes:1; +- bool disallow_module_loading:1; +- bool disallow_exit:1; +- bool running_as_daemon:1; +- bool realtime_scheduling:1; +- bool disable_remixing:1; +- bool disable_lfe_remixing:1; +- bool deferred_volume:1; +- +- pa_resample_method_t resample_method; +- int realtime_priority; +- +- pa_server_type_t server_type; +- pa_cpu_info cpu_info; +- +- /* hooks */ +- pa_hook hooks[PA_CORE_HOOK_MAX]; +- /* access hooks */ +- pa_hook access[PA_ACCESS_HOOK_MAX]; +-}; +- + PA_DECLARE_PUBLIC_CLASS(pa_core); + #define PA_CORE(o) pa_core_cast(o) + +diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c +index 935b068..3309f87 100644 +--- a/src/pulsecore/dbus-shared.c ++++ b/src/pulsecore/dbus-shared.c +@@ -24,6 +24,7 @@ + + #include + ++#include + #include + + #include "dbus-shared.h" +diff --git a/src/pulsecore/device-port.c b/src/pulsecore/device-port.c +index 7c9ddf3..bc4d704 100644 +--- a/src/pulsecore/device-port.c ++++ b/src/pulsecore/device-port.c +@@ -21,6 +21,7 @@ + + #include "device-port.h" + #include ++#include + #include + + PA_DEFINE_PUBLIC_CLASS(pa_device_port, pa_object); +diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c +index ac15815..da2abe0 100644 +--- a/src/pulsecore/module.c ++++ b/src/pulsecore/module.c +@@ -31,6 +31,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c +index 47bfc08..dfdcc43 100644 +--- a/src/pulsecore/namereg.c ++++ b/src/pulsecore/namereg.c +@@ -30,6 +30,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/play-memchunk.c b/src/pulsecore/play-memchunk.c +index cc0bc16..3c7e5a6 100644 +--- a/src/pulsecore/play-memchunk.c ++++ b/src/pulsecore/play-memchunk.c +@@ -24,6 +24,7 @@ + #include + #include + ++#include + #include + #include + +diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c +index 4f83ac4..ead1d81 100644 +--- a/src/pulsecore/protocol-esound.c ++++ b/src/pulsecore/protocol-esound.c +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c +index 25a2cd0..bbb03c3 100644 +--- a/src/pulsecore/protocol-http.c ++++ b/src/pulsecore/protocol-http.c +@@ -30,6 +30,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c +index 8f07b2d..cbb6ae6 100644 +--- a/src/pulsecore/protocol-native.c ++++ b/src/pulsecore/protocol-native.c +@@ -45,6 +45,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c +index 923ec87..bd9136b 100644 +--- a/src/pulsecore/protocol-simple.c ++++ b/src/pulsecore/protocol-simple.c +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/shared.c b/src/pulsecore/shared.c +index 1b5eea9..2e63d0e 100644 +--- a/src/pulsecore/shared.c ++++ b/src/pulsecore/shared.c +@@ -22,6 +22,7 @@ + #endif + + #include ++#include + #include + + #include "shared.h" +diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c +index e9694f2..9a866b4 100644 +--- a/src/pulsecore/sink-input.c ++++ b/src/pulsecore/sink-input.c +@@ -30,6 +30,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index aa21822..20249ec 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/src/pulsecore/sound-file-stream.c b/src/pulsecore/sound-file-stream.c +index ddda456..3ac935d 100644 +--- a/src/pulsecore/sound-file-stream.c ++++ b/src/pulsecore/sound-file-stream.c +@@ -32,6 +32,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c +index 6714ea9..a265e6f 100644 +--- a/src/pulsecore/source-output.c ++++ b/src/pulsecore/source-output.c +@@ -30,6 +30,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 8ce7818..7a33d5b 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -33,6 +33,7 @@ + #include + #include + ++#include + #include + #include + #include +diff --git a/src/pulsecore/x11wrap.c b/src/pulsecore/x11wrap.c +index 0c040cf..76cb83b 100644 +--- a/src/pulsecore/x11wrap.c ++++ b/src/pulsecore/x11wrap.c +@@ -28,6 +28,7 @@ + #include + #include + #include ++#include + #include + #include + +-- +2.9.3 + diff --git a/0010-Don-t-access-pa_core-structures-directly.patch b/0010-Don-t-access-pa_core-structures-directly.patch new file mode 100644 index 0000000..9bc94a5 --- /dev/null +++ b/0010-Don-t-access-pa_core-structures-directly.patch @@ -0,0 +1,1639 @@ +From 420e714464f6ab6f943de7ef99b92b5d3f4814e2 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Tue, 7 Apr 2015 16:53:09 +0200 +Subject: [PATCH 10/18] Don't access pa_core structures directly + +First step to allowing access control. Make sure we only use the +methods to get core datastructures so that we can do access control +later. +--- + src/pulsecore/cli-command.c | 72 ++++++++++++-------- + src/pulsecore/cli-text.c | 85 ++++++++++++++++------- + src/pulsecore/core.c | 102 ++++++++++++++++++++++++++++ + src/pulsecore/core.h | 30 ++++++++ + src/pulsecore/namereg.c | 35 +++++++--- + src/pulsecore/protocol-esound.c | 29 ++++---- + src/pulsecore/protocol-http.c | 12 +++- + src/pulsecore/protocol-native.c | 147 ++++++++++++++++++++++------------------ + src/pulsecore/protocol-simple.c | 5 +- + src/pulsecore/sink.c | 6 +- + src/pulsecore/source.c | 6 +- + 11 files changed, 380 insertions(+), 149 deletions(-) + +diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c +index 05c2b8f..138881c 100644 +--- a/src/pulsecore/cli-command.c ++++ b/src/pulsecore/cli-command.c +@@ -47,7 +47,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -362,7 +361,7 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + pa_assert(buf); + pa_assert(fail); + +- mstat = pa_mempool_get_stat(c->mempool); ++ mstat = pa_mempool_get_stat(pa_core_get_mempool(c)); + + pa_strbuf_printf(buf, "Memory blocks currently allocated: %u, size: %s.\n", + (unsigned) pa_atomic_load(&mstat->n_allocated), +@@ -384,10 +383,10 @@ static int pa_cli_command_stat(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + pa_bytes_snprint(bytes, sizeof(bytes), (unsigned) pa_scache_total_size(c))); + + pa_strbuf_printf(buf, "Default sample spec: %s\n", +- pa_sample_spec_snprint(ss, sizeof(ss), &c->default_sample_spec)); ++ pa_sample_spec_snprint(ss, sizeof(ss), pa_core_get_sample_spec(c))); + + pa_strbuf_printf(buf, "Default channel map: %s\n", +- pa_channel_map_snprint(cm, sizeof(cm), &c->default_channel_map)); ++ pa_channel_map_snprint(cm, sizeof(cm), pa_core_get_channel_map(c))); + + def_sink = pa_namereg_get_default_sink(c); + def_source = pa_namereg_get_default_source(c); +@@ -437,7 +436,7 @@ static int pa_cli_command_load(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + return -1; + } + +- if (!pa_module_load(c, name, pa_tokenizer_get(t, 2))) { ++ if (!pa_module_load(c, name, pa_tokenizer_get(t, 2))) { + pa_strbuf_puts(buf, "Module load failed.\n"); + return -1; + } +@@ -462,7 +461,7 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bo + } + + if (pa_atou(i, &idx) >= 0) { +- if (!(m = pa_idxset_get_by_index(c->modules, idx))) { ++ if (!(m = pa_core_get_module(c, idx))) { + pa_strbuf_puts(buf, "Invalid module index.\n"); + return -1; + } +@@ -470,12 +469,15 @@ static int pa_cli_command_unload(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bo + pa_module_unload_request(m, false); + + } else { +- PA_IDXSET_FOREACH(m, c->modules, idx) ++ pa_idxset *modules = pa_core_get_modules(c); ++ PA_IDXSET_FOREACH(m, modules, idx) + if (pa_streq(i, m->name)) { + unloaded = true; + pa_module_unload_request(m, false); + } + ++ pa_idxset_free(modules, NULL); ++ + if (unloaded == false) { + pa_strbuf_printf(buf, "Module %s not loaded.\n", i); + return -1; +@@ -604,7 +606,7 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb + return -1; + } + +- if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) { ++ if (!(si = pa_core_get_sink_input(c, idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } +@@ -697,7 +699,7 @@ static int pa_cli_command_source_output_volume(pa_core *c, pa_tokenizer *t, pa_s + return -1; + } + +- if (!(so = pa_idxset_get_by_index(c->source_outputs, idx))) { ++ if (!(so = pa_core_get_source_output(c, idx))) { + pa_strbuf_puts(buf, "No source output found with this index.\n"); + return -1; + } +@@ -880,7 +882,7 @@ static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t + return -1; + } + +- if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { ++ if (!(si = pa_core_get_sink_input(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } +@@ -923,7 +925,7 @@ static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer + return -1; + } + +- if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) { ++ if (!(so = pa_core_get_source_output(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No source output found with this index.\n"); + return -1; + } +@@ -971,7 +973,7 @@ static int pa_cli_command_sink_input_mute(pa_core *c, pa_tokenizer *t, pa_strbuf + return -1; + } + +- if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { ++ if (!(si = pa_core_get_sink_input(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } +@@ -1011,7 +1013,7 @@ static int pa_cli_command_source_output_mute(pa_core *c, pa_tokenizer *t, pa_str + return -1; + } + +- if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) { ++ if (!(so = pa_core_get_source_output(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No source output found with this index.\n"); + return -1; + } +@@ -1065,7 +1067,7 @@ static int pa_cli_command_source_default(pa_core *c, pa_tokenizer *t, pa_strbuf + + static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool *fail) { + const char *n; +- pa_client *client; ++ pa_client *cl; + uint32_t idx; + + pa_core_assert_ref(c); +@@ -1083,12 +1085,12 @@ static int pa_cli_command_kill_client(pa_core *c, pa_tokenizer *t, pa_strbuf *bu + return -1; + } + +- if (!(client = pa_idxset_get_by_index(c->clients, idx))) { ++ if (!(cl = pa_core_get_client(c, idx))) { + pa_strbuf_puts(buf, "No client found by this index.\n"); + return -1; + } + +- pa_client_kill(client); ++ pa_client_kill(cl); + return 0; + } + +@@ -1112,7 +1114,7 @@ static int pa_cli_command_kill_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf + return -1; + } + +- if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx))) { ++ if (!(sink_input = pa_core_get_sink_input(c, idx))) { + pa_strbuf_puts(buf, "No sink input found by this index.\n"); + return -1; + } +@@ -1141,7 +1143,7 @@ static int pa_cli_command_kill_source_output(pa_core *c, pa_tokenizer *t, pa_str + return -1; + } + +- if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx))) { ++ if (!(source_output = pa_core_get_source_output(c, idx))) { + pa_strbuf_puts(buf, "No source output found by this index.\n"); + return -1; + } +@@ -1300,7 +1302,7 @@ static int pa_cli_command_vacuum(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bo + pa_assert(buf); + pa_assert(fail); + +- pa_mempool_vacuum(c->mempool); ++ pa_mempool_vacuum(pa_core_get_mempool(c)); + + return 0; + } +@@ -1331,7 +1333,7 @@ static int pa_cli_command_move_sink_input(pa_core *c, pa_tokenizer *t, pa_strbuf + return -1; + } + +- if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { ++ if (!(si = pa_core_get_sink_input(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No sink input found with this index.\n"); + return -1; + } +@@ -1374,7 +1376,7 @@ static int pa_cli_command_move_source_output(pa_core *c, pa_tokenizer *t, pa_str + return -1; + } + +- if (!(so = pa_idxset_get_by_index(c->source_outputs, (uint32_t) idx))) { ++ if (!(so = pa_core_get_source_output(c, (uint32_t) idx))) { + pa_strbuf_puts(buf, "No source output found with this index.\n"); + return -1; + } +@@ -1789,6 +1791,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + #ifdef HAVE_CTIME_R + char txt[256]; + #endif ++ pa_idxset *i; + + pa_core_assert_ref(c); + pa_assert(t); +@@ -1802,8 +1805,8 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + #else + pa_strbuf_printf(buf, "### Configuration dump generated at %s\n", ctime(&now)); + #endif +- +- PA_IDXSET_FOREACH(m, c->modules, idx) { ++ i = pa_core_get_modules(c); ++ PA_IDXSET_FOREACH(m, i, idx) { + + pa_strbuf_printf(buf, "load-module %s", m->name); + +@@ -1812,9 +1815,11 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + + pa_strbuf_puts(buf, "\n"); + } ++ pa_idxset_free(i, NULL); + + nl = false; +- PA_IDXSET_FOREACH(sink, c->sinks, idx) { ++ i = pa_core_get_sinks(c); ++ PA_IDXSET_FOREACH(sink, i, idx) { + + if (!nl) { + pa_strbuf_puts(buf, "\n"); +@@ -1825,9 +1830,11 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, false))); + pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED)); + } ++ pa_idxset_free(i, NULL); + + nl = false; +- PA_IDXSET_FOREACH(source, c->sources, idx) { ++ i = pa_core_get_sources(c); ++ PA_IDXSET_FOREACH(source, i, idx) { + + if (!nl) { + pa_strbuf_puts(buf, "\n"); +@@ -1838,9 +1845,11 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + pa_strbuf_printf(buf, "set-source-mute %s %s\n", source->name, pa_yes_no(pa_source_get_mute(source, false))); + pa_strbuf_printf(buf, "suspend-source %s %s\n", source->name, pa_yes_no(pa_source_get_state(source) == PA_SOURCE_SUSPENDED)); + } ++ pa_idxset_free(i, NULL); + + nl = false; +- PA_IDXSET_FOREACH(card, c->cards, idx) { ++ i = pa_core_get_cards(c); ++ PA_IDXSET_FOREACH(card, i, idx) { + + if (!nl) { + pa_strbuf_puts(buf, "\n"); +@@ -1849,6 +1858,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, bool + + pa_strbuf_printf(buf, "set-card-profile %s %s\n", card->name, card->active_profile->name); + } ++ pa_idxset_free(i, NULL); + + nl = false; + if ((sink = pa_namereg_get_default_sink(c))) { +@@ -1880,13 +1890,16 @@ static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *b + uint32_t s_idx, i_idx; + char v_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; + pa_channel_map *map; ++ pa_idxset *sinks, *sources; + + pa_core_assert_ref(c); + pa_assert(t); + pa_assert(buf); + pa_assert(fail); + +- PA_IDXSET_FOREACH(s, c->sinks, s_idx) { ++ sinks = pa_core_get_sinks(c); ++ ++ PA_IDXSET_FOREACH(s, sinks, s_idx) { + map = &s->channel_map; + pa_strbuf_printf(buf, "Sink %d: ", s_idx); + pa_strbuf_printf(buf, +@@ -1937,8 +1950,10 @@ static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *b + pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(i->save_volume)); + } + } ++ pa_idxset_free(sinks, NULL); + +- PA_IDXSET_FOREACH(so, c->sources, s_idx) { ++ sources = pa_core_get_sources(c); ++ PA_IDXSET_FOREACH(so, sources, s_idx) { + map = &so->channel_map; + pa_strbuf_printf(buf, "Source %d: ", s_idx); + pa_strbuf_printf(buf, +@@ -1989,6 +2004,7 @@ static int pa_cli_command_dump_volumes(pa_core *c, pa_tokenizer *t, pa_strbuf *b + pa_strbuf_printf(buf, "save = %s\n", pa_yes_no(o->save_volume)); + } + } ++ pa_idxset_free(sources, NULL); + + return 0; + } +diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c +index 9e32524..239ab5d 100644 +--- a/src/pulsecore/cli-text.c ++++ b/src/pulsecore/cli-text.c +@@ -32,7 +32,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -44,13 +43,17 @@ char *pa_module_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_module *m; + uint32_t idx = PA_IDXSET_INVALID; ++ pa_idxset *modules; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); ++ modules = pa_core_get_modules(c); ++ ++ pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(modules)); + +- PA_IDXSET_FOREACH(m, c->modules, idx) { ++ PA_IDXSET_FOREACH(m, modules, idx) { + char *t; + + pa_strbuf_printf(s, " index: %u\n" +@@ -68,36 +71,42 @@ char *pa_module_list_to_string(pa_core *c) { + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + } ++ pa_idxset_free(modules, NULL); + + return pa_strbuf_to_string_free(s); + } + + char *pa_client_list_to_string(pa_core *c) { + pa_strbuf *s; +- pa_client *client; ++ pa_client *cl; + uint32_t idx = PA_IDXSET_INVALID; ++ pa_idxset *clients; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(c->clients)); ++ clients = pa_core_get_clients(c); ++ ++ pa_strbuf_printf(s, "%u client(s) logged in.\n", pa_idxset_size(clients)); + +- PA_IDXSET_FOREACH(client, c->clients, idx) { ++ PA_IDXSET_FOREACH(cl, clients, idx) { + char *t; + pa_strbuf_printf( + s, + " index: %u\n" + "\tdriver: <%s>\n", +- client->index, +- client->driver); ++ cl->index, ++ cl->driver); + +- if (client->module) +- pa_strbuf_printf(s, "\towner module: %u\n", client->module->index); ++ if (cl->module) ++ pa_strbuf_printf(s, "\towner module: %u\n", cl->module->index); + +- t = pa_proplist_to_string_sep(client->proplist, "\n\t\t"); ++ t = pa_proplist_to_string_sep(cl->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + } ++ pa_idxset_free(clients, NULL); + + return pa_strbuf_to_string_free(s); + } +@@ -139,13 +148,17 @@ char *pa_card_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_card *card; + uint32_t idx = PA_IDXSET_INVALID; ++ pa_idxset *cards; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards)); ++ cards = pa_core_get_cards(c); ++ ++ pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(cards)); + +- PA_IDXSET_FOREACH(card, c->cards, idx) { ++ PA_IDXSET_FOREACH(card, cards, idx) { + char *t; + pa_sink *sink; + pa_source *source; +@@ -193,6 +206,7 @@ char *pa_card_list_to_string(pa_core *c) { + + append_port_list(s, card->ports); + } ++ pa_idxset_free(cards, NULL); + + return pa_strbuf_to_string_free(s); + } +@@ -235,15 +249,19 @@ char *pa_sink_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_sink *sink, *default_sink; + uint32_t idx = PA_IDXSET_INVALID; ++ pa_idxset *sinks; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); ++ sinks = pa_core_get_sinks(c); ++ ++ pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(sinks)); + + default_sink = pa_namereg_get_default_sink(c); + +- PA_IDXSET_FOREACH(sink, c->sinks, idx) { ++ PA_IDXSET_FOREACH(sink, sinks, idx) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], + cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], + v[PA_VOLUME_SNPRINT_VERBOSE_MAX], +@@ -345,6 +363,7 @@ char *pa_sink_list_to_string(pa_core *c) { + "\tactive port: <%s>\n", + sink->active_port->name); + } ++ pa_idxset_free(sinks, NULL); + + return pa_strbuf_to_string_free(s); + } +@@ -353,15 +372,19 @@ char *pa_source_list_to_string(pa_core *c) { + pa_strbuf *s; + pa_source *source, *default_source; + uint32_t idx = PA_IDXSET_INVALID; ++ pa_idxset *sources; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); ++ sources = pa_core_get_sources(c); ++ ++ pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(sources)); + + default_source = pa_namereg_get_default_source(c); + +- PA_IDXSET_FOREACH(source, c->sources, idx) { ++ PA_IDXSET_FOREACH(source, sources, idx) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], + cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], + v[PA_VOLUME_SNPRINT_VERBOSE_MAX], +@@ -460,6 +483,7 @@ char *pa_source_list_to_string(pa_core *c) { + "\tactive port: <%s>\n", + source->active_port->name); + } ++ pa_idxset_free(sources, NULL); + + return pa_strbuf_to_string_free(s); + } +@@ -474,13 +498,16 @@ char *pa_source_output_list_to_string(pa_core *c) { + [PA_SOURCE_OUTPUT_CORKED] = "CORKED", + [PA_SOURCE_OUTPUT_UNLINKED] = "UNLINKED" + }; ++ pa_idxset *source_outputs; + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u source output(s) available.\n", pa_idxset_size(c->source_outputs)); ++ source_outputs = pa_core_get_source_outputs(c); ++ ++ pa_strbuf_printf(s, "%u source output(s) available.\n", pa_idxset_size(source_outputs)); + +- PA_IDXSET_FOREACH(o, c->source_outputs, idx) { ++ PA_IDXSET_FOREACH(o, source_outputs, idx) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + pa_usec_t cl; + const char *cmn; +@@ -557,6 +584,7 @@ char *pa_source_output_list_to_string(pa_core *c) { + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + } ++ pa_idxset_free(source_outputs, NULL); + + return pa_strbuf_to_string_free(s); + } +@@ -572,13 +600,16 @@ char *pa_sink_input_list_to_string(pa_core *c) { + [PA_SINK_INPUT_CORKED] = "CORKED", + [PA_SINK_INPUT_UNLINKED] = "UNLINKED" + }; ++ pa_idxset *sink_inputs; + + pa_assert(c); + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); ++ sink_inputs = pa_core_get_sink_inputs(c); + +- PA_IDXSET_FOREACH(i, c->sink_inputs, idx) { ++ pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(sink_inputs)); ++ ++ PA_IDXSET_FOREACH(i, sink_inputs, idx) { + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + pa_usec_t cl; + const char *cmn; +@@ -653,23 +684,28 @@ char *pa_sink_input_list_to_string(pa_core *c) { + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + } ++ pa_idxset_free(sink_inputs, NULL); + + return pa_strbuf_to_string_free(s); + } + + char *pa_scache_list_to_string(pa_core *c) { + pa_strbuf *s; ++ pa_idxset *scache; ++ + pa_assert(c); + + s = pa_strbuf_new(); + +- pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0); ++ scache = pa_core_get_scache(c); ++ ++ pa_strbuf_printf(s, "%u cache entrie(s) available.\n", scache ? pa_idxset_size(scache) : 0); + +- if (c->scache) { ++ if (scache) { + pa_scache_entry *e; + uint32_t idx = PA_IDXSET_INVALID; + +- PA_IDXSET_FOREACH(e, c->scache, idx) { ++ PA_IDXSET_FOREACH(e, scache, idx) { + double l = 0; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; + const char *cmn; +@@ -711,6 +747,7 @@ char *pa_scache_list_to_string(pa_core *c) { + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); + } ++ pa_idxset_free(scache, NULL); + } + + return pa_strbuf_to_string_free(s); +diff --git a/src/pulsecore/core.c b/src/pulsecore/core.c +index b8dbde9..b068f5d 100644 +--- a/src/pulsecore/core.c ++++ b/src/pulsecore/core.c +@@ -262,6 +262,11 @@ int pa_core_exit(pa_core *c, bool force, int retval) { + return 0; + } + ++pa_hook_result_t pa_core_hook_fire(pa_core *c, pa_core_hook_t hook, void *data) { ++ pa_assert(c); ++ return pa_hook_fire(&c->hooks[hook], data); ++} ++ + void pa_core_maybe_vacuum(pa_core *c) { + pa_assert(c); + +@@ -305,3 +310,100 @@ void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) { + + c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true)); + } ++ ++pa_mainloop_api* pa_core_get_mainloop(pa_core *c) { ++ pa_assert(c); ++ pa_assert(c->mainloop); ++ ++ return c->mainloop; ++} ++ ++/* FIXME: Should we expose this at all? */ ++pa_mempool* pa_core_get_mempool(pa_core *c) { ++ pa_assert(c); ++ pa_assert(c->mempool); ++ ++ return c->mempool; ++} ++ ++pa_mempool* pa_core_new_mempool(pa_core *c, pa_mem_type_t shm_type, bool per_client) { ++ return pa_mempool_new(shm_type, c->shm_size, per_client); ++} ++ ++/* 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* pa_core_get_clients(pa_core *c) { ++ return pa_idxset_copy(c->clients, NULL); ++} ++ ++pa_idxset* pa_core_get_cards(pa_core *c) { ++ return pa_idxset_copy(c->cards, NULL); ++} ++ ++pa_idxset* pa_core_get_sinks(pa_core *c) { ++ return pa_idxset_copy(c->sinks, NULL); ++} ++ ++pa_idxset* pa_core_get_sources(pa_core *c) { ++ return pa_idxset_copy(c->sources, NULL); ++} ++ ++pa_idxset* pa_core_get_sink_inputs(pa_core *c) { ++ return pa_idxset_copy(c->sink_inputs, NULL); ++} ++ ++pa_idxset* pa_core_get_source_outputs(pa_core *c) { ++ return pa_idxset_copy(c->source_outputs, NULL); ++} ++ ++pa_idxset* pa_core_get_scache(pa_core *c) { ++ return pa_idxset_copy(c->scache, NULL); ++} ++ ++pa_module* pa_core_get_module(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->modules, idx); ++} ++ ++pa_client* pa_core_get_client(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->clients, idx); ++} ++ ++pa_card* pa_core_get_card(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->cards, idx); ++} ++ ++pa_sink* pa_core_get_sink(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->sinks, idx); ++} ++ ++pa_source* pa_core_get_source(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->sources, idx); ++} ++ ++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_source_output* pa_core_get_source_output(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->source_outputs, idx); ++} ++ ++pa_scache_entry* pa_core_get_scache_entry(pa_core *c, uint32_t idx) { ++ return pa_idxset_get_by_index(c->scache, idx); ++} ++ ++const pa_sample_spec* pa_core_get_sample_spec(pa_core *c) { ++ return &c->default_sample_spec; ++} ++ ++const pa_channel_map* pa_core_get_channel_map(pa_core *c) { ++ return &c->default_channel_map; ++} ++ ++uint32_t pa_core_get_cookie(pa_core *c) { ++ return c->cookie; ++} +diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h +index 955db89..87b055d 100644 +--- a/src/pulsecore/core.h ++++ b/src/pulsecore/core.h +@@ -158,8 +158,38 @@ int pa_core_exit(pa_core *c, bool force, int retval); + + void pa_core_maybe_vacuum(pa_core *c); + ++pa_hook_result_t pa_core_hook_fire(pa_core *c, pa_core_hook_t hook, void *data); ++ + /* wrapper for c->mainloop->time_*() RT time events */ + pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata); + void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec); + ++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_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); ++pa_idxset* pa_core_get_sinks(pa_core *c); ++pa_idxset* pa_core_get_sources(pa_core *c); ++pa_idxset* pa_core_get_sink_inputs(pa_core *c); ++pa_idxset* pa_core_get_source_outputs(pa_core *c); ++pa_idxset* pa_core_get_scache(pa_core *c); ++ ++pa_module* pa_core_get_module(pa_core *c, uint32_t idx); ++pa_client* pa_core_get_client(pa_core *c, uint32_t idx); ++pa_card* pa_core_get_card(pa_core *c, uint32_t idx); ++pa_sink* pa_core_get_sink(pa_core *c, uint32_t idx); ++pa_source* pa_core_get_source(pa_core *c, uint32_t idx); ++pa_sink_input* pa_core_get_sink_input(pa_core *c, uint32_t idx); ++pa_source_output* pa_core_get_source_output(pa_core *c, uint32_t idx); ++typedef struct pa_scache_entry pa_scache_entry; ++pa_scache_entry* pa_core_get_scache_entry(pa_core *c, uint32_t idx); ++ ++const pa_sample_spec* pa_core_get_sample_spec(pa_core *c); ++const pa_channel_map* pa_core_get_channel_map(pa_core *c); ++ ++uint32_t pa_core_get_cookie(pa_core *c); ++ + #endif +diff --git a/src/pulsecore/namereg.c b/src/pulsecore/namereg.c +index dfdcc43..699fa1b 100644 +--- a/src/pulsecore/namereg.c ++++ b/src/pulsecore/namereg.c +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -239,13 +240,13 @@ void* pa_namereg_get(pa_core *c, const char *name, pa_namereg_type_t type) { + return NULL; + + if (type == PA_NAMEREG_SINK) +- return pa_idxset_get_by_index(c->sinks, idx); ++ return pa_core_get_sink(c, idx); + else if (type == PA_NAMEREG_SOURCE) +- return pa_idxset_get_by_index(c->sources, idx); +- else if (type == PA_NAMEREG_SAMPLE && c->scache) +- return pa_idxset_get_by_index(c->scache, idx); ++ return pa_core_get_source(c, idx); ++ else if (type == PA_NAMEREG_SAMPLE) ++ return pa_core_get_scache_entry(c, idx); + else if (type == PA_NAMEREG_CARD) +- return pa_idxset_get_by_index(c->cards, idx); ++ return pa_core_get_card(c, idx); + + return NULL; + } +@@ -258,7 +259,7 @@ pa_sink* pa_namereg_set_default_sink(pa_core*c, pa_sink *s) { + + if (c->default_sink != s) { + c->default_sink = s; +- pa_hook_fire(&c->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], c->default_sink); ++ pa_core_hook_fire(c, PA_CORE_HOOK_DEFAULT_SINK_CHANGED, c->default_sink); + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + } + +@@ -273,7 +274,7 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { + + if (c->default_source != s) { + c->default_source = s; +- pa_hook_fire(&c->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], c->default_source); ++ pa_core_hook_fire(c, PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED, c->default_source); + pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX); + } + +@@ -283,41 +284,50 @@ pa_source* pa_namereg_set_default_source(pa_core*c, pa_source *s) { + pa_sink *pa_namereg_get_default_sink(pa_core *c) { + pa_sink *s, *best = NULL; + uint32_t idx; ++ pa_idxset *sinks; + + pa_assert(c); + + if (c->default_sink && PA_SINK_IS_LINKED(pa_sink_get_state(c->default_sink))) + return c->default_sink; + +- PA_IDXSET_FOREACH(s, c->sinks, idx) ++ sinks = pa_core_get_sinks(c); ++ ++ PA_IDXSET_FOREACH(s, sinks, idx) + if (PA_SINK_IS_LINKED(pa_sink_get_state(s))) + if (!best || s->priority > best->priority) + best = s; + ++ pa_idxset_free(sinks, NULL); ++ + return best; + } + + pa_source *pa_namereg_get_default_source(pa_core *c) { + pa_source *s, *best = NULL; + uint32_t idx; ++ pa_idxset *sources; + + pa_assert(c); + + if (c->default_source && PA_SOURCE_IS_LINKED(pa_source_get_state(c->default_source))) + return c->default_source; + ++ sources = pa_core_get_sources(c); ++ + /* First, try to find one that isn't a monitor */ +- PA_IDXSET_FOREACH(s, c->sources, idx) ++ PA_IDXSET_FOREACH(s, sources, idx) + if (!s->monitor_of && PA_SOURCE_IS_LINKED(pa_source_get_state(s))) + if (!best || + s->priority > best->priority) + best = s; + ++ + if (best) +- return best; ++ goto done; + + /* Then, fallback to a monitor */ +- PA_IDXSET_FOREACH(s, c->sources, idx) ++ PA_IDXSET_FOREACH(s, sources, idx) + if (PA_SOURCE_IS_LINKED(pa_source_get_state(s))) + if (!best || + s->priority > best->priority || +@@ -327,5 +337,8 @@ pa_source *pa_namereg_get_default_source(pa_core *c) { + s->monitor_of->priority > best->monitor_of->priority)) + best = s; + ++done: ++ pa_idxset_free(sources, NULL); ++ + return best; + } +diff --git a/src/pulsecore/protocol-esound.c b/src/pulsecore/protocol-esound.c +index ead1d81..0d76525 100644 +--- a/src/pulsecore/protocol-esound.c ++++ b/src/pulsecore/protocol-esound.c +@@ -41,7 +41,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -240,12 +239,12 @@ static void connection_unlink(connection *c) { + } + + if (c->defer_event) { +- c->protocol->core->mainloop->defer_free(c->defer_event); ++ pa_core_get_mainloop (c->protocol->core)->defer_free(c->defer_event); + c->defer_event = NULL; + } + + if (c->auth_timeout_event) { +- c->protocol->core->mainloop->time_free(c->auth_timeout_event); ++ pa_core_get_mainloop (c->protocol->core)->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + +@@ -293,7 +292,7 @@ static void connection_write(connection *c, const void *data, size_t length) { + size_t i; + pa_assert(c); + +- c->protocol->core->mainloop->defer_enable(c->defer_event, 1); ++ pa_core_get_mainloop (c->protocol->core)->defer_enable(c->defer_event, 1); + + connection_write_prepare(c, length); + +@@ -355,7 +354,7 @@ static int esd_proto_connect(connection *c, esd_proto_t request, const void *dat + } + + if (c->auth_timeout_event) { +- c->protocol->core->mainloop->time_free(c->auth_timeout_event); ++ pa_core_get_mainloop (c->protocol->core)->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + +@@ -614,6 +613,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da + uint32_t idx = PA_IDXSET_INVALID; + unsigned nsamples; + char terminator[sizeof(int32_t)*6+ESD_NAME_MAX]; ++ pa_idxset *scache; + + connection_assert_ref(c); + pa_assert(data); +@@ -624,7 +624,8 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da + + k = sizeof(int32_t)*5+ESD_NAME_MAX; + s = sizeof(int32_t)*6+ESD_NAME_MAX; +- nsamples = pa_idxset_size(c->protocol->core->scache); ++ scache = pa_core_get_scache(c->protocol->core); ++ nsamples = pa_idxset_size(scache); + t = s*(nsamples+1) + k*(c->protocol->n_player+1); + + connection_write_prepare(c, t); +@@ -690,7 +691,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da + + idx = PA_IDXSET_INVALID; + +- PA_IDXSET_FOREACH(ce, c->protocol->core->scache, idx) { ++ PA_IDXSET_FOREACH(ce, scache, idx) { + int32_t id, rate, lvolume, rvolume, format, len; + char name[ESD_NAME_MAX]; + pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }; +@@ -748,6 +749,7 @@ static int esd_proto_all_info(connection *c, esd_proto_t request, const void *da + t -= s; + } + } ++ pa_idxset_free(scache, NULL); + + pa_assert(t == s); + +@@ -817,7 +819,7 @@ static int esd_proto_sample_pan(connection *c, esd_proto_t request, const void * + volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE; + volume.channels = 2; + +- if ((ce = pa_idxset_get_by_index(c->protocol->core->scache, idx))) { ++ if ((ce = pa_core_get_scache_entry(c->protocol->core, idx))) { + pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }; + + pa_cvolume_remap(&volume, &stereo, &ce->channel_map); +@@ -866,7 +868,7 @@ static int esd_proto_sample_cache(connection *c, esd_proto_t request, const void + CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name."); + + pa_assert(!c->scache.memchunk.memblock); +- c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length); ++ c->scache.memchunk.memblock = pa_memblock_new(pa_core_get_mempool (c->protocol->core), (size_t) sc_length); + c->scache.memchunk.index = 0; + c->scache.memchunk.length = (size_t) sc_length; + c->scache.sample_spec = ss; +@@ -1150,7 +1152,7 @@ static int do_read(connection *c) { + } + + if (!c->playback.current_memblock) { +- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1)); ++ pa_assert_se(c->playback.current_memblock = pa_memblock_new(pa_core_get_mempool(c->protocol->core), (size_t) -1)); + c->playback.memblock_index = 0; + + space = pa_memblock_get_length(c->playback.current_memblock); +@@ -1237,7 +1239,7 @@ static int do_write(connection *c) { + static void do_work(connection *c) { + connection_assert_ref(c); + +- c->protocol->core->mainloop->defer_enable(c->defer_event, 0); ++ pa_core_get_mainloop(c->protocol->core)->defer_enable(c->defer_event, 0); + + if (c->dead) + return; +@@ -1273,6 +1275,7 @@ fail: + pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL); + } else + connection_unlink(c); ++ + } + + static void io_callback(pa_iochannel*io, void *userdata) { +@@ -1562,8 +1565,8 @@ void pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esou + else + c->auth_timeout_event = NULL; + +- c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c); +- p->core->mainloop->defer_enable(c->defer_event, 0); ++ c->defer_event = pa_core_get_mainloop(p->core)->defer_new(pa_core_get_mainloop(p->core), defer_callback, c); ++ pa_core_get_mainloop(p->core)->defer_enable(c->defer_event, 0); + + pa_idxset_put(p->connections, c, &c->index); + } +diff --git a/src/pulsecore/protocol-http.c b/src/pulsecore/protocol-http.c +index bbb03c3..64024df 100644 +--- a/src/pulsecore/protocol-http.c ++++ b/src/pulsecore/protocol-http.c +@@ -30,7 +30,6 @@ + #include + #include + +-#include + #include + #include + #include +@@ -458,6 +457,7 @@ static void handle_listen(struct connection *c) { + pa_source *source; + pa_sink *sink; + uint32_t idx; ++ pa_idxset *sinks, *sources; + + http_response(c, 200, "OK", MIME_HTML); + +@@ -471,7 +471,9 @@ static void handle_listen(struct connection *c) { + return; + } + +- PA_IDXSET_FOREACH(sink, c->protocol->core->sinks, idx) { ++ sinks = pa_core_get_sinks(c->protocol->core); ++ ++ PA_IDXSET_FOREACH(sink, sinks, idx) { + char *t, *m; + + t = escape_html(pa_strna(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); +@@ -484,13 +486,16 @@ static void handle_listen(struct connection *c) { + pa_xfree(t); + pa_xfree(m); + } ++ pa_idxset_free(sinks, NULL); + + pa_ioline_puts(c->line, + "

\n" + "

Sources

\n" + "

\n"); + +- PA_IDXSET_FOREACH(source, c->protocol->core->sources, idx) { ++ sources = pa_core_get_sources(c->protocol->core); ++ ++ PA_IDXSET_FOREACH(source, sources, idx) { + char *t, *m; + + if (source->monitor_of) +@@ -507,6 +512,7 @@ static void handle_listen(struct connection *c) { + pa_xfree(t); + + } ++ pa_idxset_free(sources, NULL); + + pa_ioline_puts(c->line, + "

\n" +diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c +index cbb6ae6..4cae6df 100644 +--- a/src/pulsecore/protocol-native.c ++++ b/src/pulsecore/protocol-native.c +@@ -45,7 +45,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -1204,7 +1203,7 @@ static void native_connection_unlink(pa_native_connection *c) { + pa_pstream_unlink(c->pstream); + + if (c->auth_timeout_event) { +- c->protocol->core->mainloop->time_free(c->auth_timeout_event); ++ pa_core_get_mainloop(c->protocol->core)->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + +@@ -1875,7 +1874,7 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + int ret = PA_ERR_INVALID; + uint8_t n_formats = 0; + pa_format_info *format; +- pa_idxset *formats = NULL; ++ pa_idxset *sinks = NULL, *formats = NULL; + uint32_t i; + + pa_native_connection_assert_ref(c); +@@ -2014,8 +2013,9 @@ static void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, u + } + + if (sink_index != PA_INVALID_INDEX) { ++ sinks = pa_core_get_sinks(c->protocol->core); + +- if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) { ++ if (!(sink = pa_idxset_get_by_index(sinks, sink_index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + goto finish; + } +@@ -2105,6 +2105,8 @@ finish: + pa_proplist_free(p); + if (formats) + pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free); ++ if (sinks) ++ pa_idxset_free(sinks, NULL); + } + + static void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +@@ -2203,7 +2205,7 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin + int ret = PA_ERR_INVALID; + uint8_t n_formats = 0; + pa_format_info *format; +- pa_idxset *formats = NULL; ++ pa_idxset *sources = NULL, *sink_inputs = NULL, *formats = NULL; + uint32_t i; + + pa_native_connection_assert_ref(c); +@@ -2333,8 +2335,9 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin + } + + if (source_index != PA_INVALID_INDEX) { ++ sources = pa_core_get_sources(c->protocol->core); + +- if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) { ++ if (!(source = pa_idxset_get_by_index(sources, source_index))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + goto finish; + } +@@ -2348,8 +2351,9 @@ static void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uin + } + + if (direct_on_input_idx != PA_INVALID_INDEX) { ++ sink_inputs = pa_core_get_sink_inputs(c->protocol->core); + +- if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) { ++ if (!(direct_on_input = pa_idxset_get_by_index(sink_inputs, direct_on_input_idx))) { + pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY); + goto finish; + } +@@ -2419,6 +2423,10 @@ finish: + pa_proplist_free(p); + if (formats) + pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free); ++ if (sources) ++ pa_idxset_free(sources, NULL); ++ if (sink_inputs) ++ pa_idxset_free(sink_inputs, NULL); + } + + static void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +@@ -2475,7 +2483,7 @@ static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) { + return; + } + +- if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) { ++ if (!(c->rw_mempool = pa_core_new_mempool(c->protocol->core, shm_type, true))) { + pa_log_warn("Disabling srbchannel, reason: Failed to allocate shared " + "writable memory pool."); + return; +@@ -2490,7 +2498,7 @@ static void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) { + } + pa_mempool_set_is_remote_writable(c->rw_mempool, true); + +- srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool); ++ srb = pa_srbchannel_new(pa_core_get_mainloop(c->protocol->core), c->rw_mempool); + if (!srb) { + pa_log_debug("Failed to create srbchannel"); + goto fail; +@@ -2634,14 +2642,14 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + + c->authorized = true; + if (c->auth_timeout_event) { +- c->protocol->core->mainloop->time_free(c->auth_timeout_event); ++ pa_core_get_mainloop(c->protocol->core)->time_free(c->auth_timeout_event); + c->auth_timeout_event = NULL; + } + } + + /* Enable shared memory and memfd support if possible */ + do_shm = +- pa_mempool_is_shared(c->protocol->core->mempool) && ++ pa_mempool_is_shared(pa_core_get_mempool(c->protocol->core)) && + c->is_local; + + pa_log_debug("SHM possible: %s", pa_yes_no(do_shm)); +@@ -2668,7 +2676,7 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + * 64-bit kernel. Thus influence them to use the POSIX shared memory model + * instead. Check commit 451d1d676237c81 for further details. */ + do_memfd = +- c->version >= 32 && do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool); ++ c->version >= 32 && do_shm && pa_mempool_is_memfd_backed(pa_core_get_mempool(c->protocol->core)); + + shm_type = PA_MEM_TYPE_PRIVATE; + if (do_shm) { +@@ -2710,13 +2718,13 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) { + const char *reason; + +- if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason)) ++ if (pa_pstream_register_memfd_mempool(c->pstream, pa_core_get_mempool(c->protocol->core), &reason)) + pa_log("Failed to register memfd mempool. Reason: %s", reason); + } + + setup_srbchannel(c, shm_type); + +- pa_hook_fire(&c->protocol->core->hooks[PA_CORE_HOOK_CLIENT_AUTH], c->client); ++ pa_core_hook_fire(c->protocol->core, 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) { +@@ -2842,7 +2850,7 @@ static void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + +- stat = pa_mempool_get_stat(c->protocol->core->mempool); ++ stat = pa_mempool_get_stat(pa_core_get_mempool(c->protocol->core)); + + reply = reply_new(tag); + pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated)); +@@ -3059,7 +3067,7 @@ static void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag + CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID); + + if (sink_index != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index); ++ sink = pa_core_get_sink(c->protocol->core, sink_index); + else + sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK); + +@@ -3522,31 +3530,31 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p + + if (command == PA_COMMAND_GET_SINK_INFO) { + if (idx != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ sink = pa_core_get_sink(c->protocol->core, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + } else if (command == PA_COMMAND_GET_SOURCE_INFO) { + if (idx != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx); ++ source = pa_core_get_source(c->protocol->core, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + } else if (command == PA_COMMAND_GET_CARD_INFO) { + if (idx != PA_INVALID_INDEX) +- card = pa_idxset_get_by_index(c->protocol->core->cards, idx); ++ card = pa_core_get_card(c->protocol->core, idx); + else + card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD); + } else if (command == PA_COMMAND_GET_CLIENT_INFO) +- client = pa_idxset_get_by_index(c->protocol->core->clients, idx); ++ client = pa_core_get_client(c->protocol->core, idx); + else if (command == PA_COMMAND_GET_MODULE_INFO) +- module = pa_idxset_get_by_index(c->protocol->core->modules, idx); ++ module = pa_core_get_module(c->protocol->core, idx); + else if (command == PA_COMMAND_GET_SINK_INPUT_INFO) +- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ si = pa_core_get_sink_input(c->protocol->core, idx); + else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO) +- so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); ++ so = pa_core_get_source_output(c->protocol->core, idx); + else { + pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO); + if (idx != PA_INVALID_INDEX) +- sce = pa_idxset_get_by_index(c->protocol->core->scache, idx); ++ sce = pa_core_get_scache_entry(c->protocol->core, idx); + else + sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE); + } +@@ -3596,22 +3604,22 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t + reply = reply_new(tag); + + if (command == PA_COMMAND_GET_SINK_INFO_LIST) +- i = c->protocol->core->sinks; ++ i = pa_core_get_sinks(c->protocol->core); + else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST) +- i = c->protocol->core->sources; ++ i = pa_core_get_sources(c->protocol->core); + else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) +- i = c->protocol->core->clients; ++ i = pa_core_get_clients(c->protocol->core); + else if (command == PA_COMMAND_GET_CARD_INFO_LIST) +- i = c->protocol->core->cards; ++ i = pa_core_get_cards(c->protocol->core); + else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) +- i = c->protocol->core->modules; ++ i = pa_core_get_modules(c->protocol->core); + else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) +- i = c->protocol->core->sink_inputs; ++ i = pa_core_get_sink_inputs(c->protocol->core); + else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) +- i = c->protocol->core->source_outputs; ++ i = pa_core_get_source_outputs(c->protocol->core); + else { + pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST); +- i = c->protocol->core->scache; ++ i = pa_core_get_scache(c->protocol->core); + } + + if (i) { +@@ -3635,9 +3643,12 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t + scache_fill_tagstruct(c, reply, p); + } + } ++ ++ pa_idxset_free(i, NULL); + } + + pa_pstream_send_tagstruct(c->pstream, reply); ++ return; + } + + static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) { +@@ -3658,6 +3669,9 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); + ++ def_sink = pa_namereg_get_default_sink(c->protocol->core); ++ def_source = pa_namereg_get_default_source(c->protocol->core); ++ + reply = reply_new(tag); + pa_tagstruct_puts(reply, PACKAGE_NAME); + pa_tagstruct_puts(reply, PACKAGE_VERSION); +@@ -3670,18 +3684,16 @@ static void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t + pa_tagstruct_puts(reply, h); + pa_xfree(h); + +- fixup_sample_spec(c, &fixed_ss, &c->protocol->core->default_sample_spec); ++ fixup_sample_spec(c, &fixed_ss, pa_core_get_sample_spec(c->protocol->core)); + pa_tagstruct_put_sample_spec(reply, &fixed_ss); + +- def_sink = pa_namereg_get_default_sink(c->protocol->core); + pa_tagstruct_puts(reply, def_sink ? def_sink->name : NULL); +- def_source = pa_namereg_get_default_source(c->protocol->core); + pa_tagstruct_puts(reply, def_source ? def_source->name : NULL); + +- pa_tagstruct_putu32(reply, c->protocol->core->cookie); ++ pa_tagstruct_putu32(reply, pa_core_get_cookie(c->protocol->core)); + + if (c->version >= 15) +- pa_tagstruct_put_channel_map(reply, &c->protocol->core->default_channel_map); ++ pa_tagstruct_put_channel_map(reply, pa_core_get_channel_map(c->protocol->core)); + + pa_pstream_send_tagstruct(c->pstream, reply); + } +@@ -3766,24 +3778,24 @@ static void command_set_volume( + + case PA_COMMAND_SET_SINK_VOLUME: + if (idx != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ sink = pa_core_get_sink(c->protocol->core, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + break; + + case PA_COMMAND_SET_SOURCE_VOLUME: + if (idx != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx); ++ source = pa_core_get_source(c->protocol->core, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + break; + + case PA_COMMAND_SET_SINK_INPUT_VOLUME: +- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ si = pa_core_get_sink_input(c->protocol->core, idx); + break; + + case PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME: +- so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); ++ so = pa_core_get_source_output(c->protocol->core, idx); + break; + + default: +@@ -3861,7 +3873,7 @@ static void command_set_mute( + + case PA_COMMAND_SET_SINK_MUTE: + if (idx != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ sink = pa_core_get_sink(c->protocol->core, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + +@@ -3869,18 +3881,18 @@ static void command_set_mute( + + case PA_COMMAND_SET_SOURCE_MUTE: + if (idx != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx); ++ source = pa_core_get_source(c->protocol->core, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + + break; + + case PA_COMMAND_SET_SINK_INPUT_MUTE: +- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ si = pa_core_get_sink_input(c->protocol->core, idx); + break; + + case PA_COMMAND_SET_SOURCE_OUTPUT_MUTE: +- so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); ++ so = pa_core_get_source_output(c->protocol->core, idx); + break; + + default: +@@ -4425,7 +4437,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + if (command == PA_COMMAND_KILL_CLIENT) { + pa_client *client; + +- client = pa_idxset_get_by_index(c->protocol->core->clients, idx); ++ client = pa_core_get_client(c->protocol->core, idx); + CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY); + + pa_native_connection_ref(c); +@@ -4434,7 +4446,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + } else if (command == PA_COMMAND_KILL_SINK_INPUT) { + pa_sink_input *s; + +- s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ s = pa_core_get_sink_input(c->protocol->core, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + + pa_native_connection_ref(c); +@@ -4444,7 +4456,7 @@ static void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + + pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT); + +- s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); ++ s = pa_core_get_source_output(c->protocol->core, idx); + CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY); + + pa_native_connection_ref(c); +@@ -4500,7 +4512,7 @@ static void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t t + } + + CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS); +- m = pa_idxset_get_by_index(c->protocol->core->modules, idx); ++ m = pa_core_get_module(c->protocol->core, idx); + CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY); + + pa_module_unload_request(m, false); +@@ -4533,10 +4545,10 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag + pa_sink_input *si = NULL; + pa_sink *sink = NULL; + +- si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx); ++ si = pa_core_get_sink_input(c->protocol->core, idx); + + if (idx_device != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device); ++ sink = pa_core_get_sink(c->protocol->core, idx_device); + else + sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK); + +@@ -4552,10 +4564,10 @@ static void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag + + pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT); + +- so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx); ++ so = pa_core_get_source_output(c->protocol->core, idx); + + if (idx_device != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device); ++ source = pa_core_get_source(c->protocol->core, idx_device); + else + source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE); + +@@ -4605,7 +4617,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa + pa_sink *sink = NULL; + + if (idx != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ sink = pa_core_get_sink(c->protocol->core, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + +@@ -4636,7 +4648,7 @@ static void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa + pa_source *source; + + if (idx != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx); ++ source = pa_core_get_source(c->protocol->core, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + +@@ -4676,12 +4688,17 @@ static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, + CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID); + + if (idx != PA_INVALID_INDEX) +- m = pa_idxset_get_by_index(c->protocol->core->modules, idx); +- else +- PA_IDXSET_FOREACH(m, c->protocol->core->modules, idx) ++ m = pa_core_get_module(c->protocol->core, idx); ++ else { ++ pa_idxset *modules = pa_core_get_modules (c->protocol->core); ++ ++ PA_IDXSET_FOREACH(m, modules, idx) + if (pa_streq(name, m->name)) + break; + ++ pa_idxset_free (modules, NULL); ++ } ++ + CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION); + CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID); + +@@ -4717,7 +4734,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_ + CHECK_VALIDITY(c->pstream, profile_name, tag, PA_ERR_INVALID); + + if (idx != PA_INVALID_INDEX) +- card = pa_idxset_get_by_index(c->protocol->core->cards, idx); ++ card = pa_core_get_card(c->protocol->core, idx); + else + card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD); + +@@ -4761,7 +4778,7 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, + pa_sink *sink; + + if (idx != PA_INVALID_INDEX) +- sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx); ++ sink = pa_core_get_sink(c->protocol->core, idx); + else + sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK); + +@@ -4777,7 +4794,7 @@ static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, + pa_assert(command == PA_COMMAND_SET_SOURCE_PORT); + + if (idx != PA_INVALID_INDEX) +- source = pa_idxset_get_by_index(c->protocol->core->sources, idx); ++ source = pa_core_get_source(c->protocol->core, idx); + else + source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE); + +@@ -4818,7 +4835,7 @@ static void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, + CHECK_VALIDITY(c->pstream, port_name, tag, PA_ERR_INVALID); + + if (idx != PA_INVALID_INDEX) +- card = pa_idxset_get_by_index(c->protocol->core->cards, idx); ++ card = pa_core_get_card(c->protocol->core, idx); + else + card = pa_namereg_get(c->protocol->core, card_name, PA_NAMEREG_CARD); + +@@ -5003,7 +5020,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o + pa_memblock_ref(u->memchunk.memblock); + u->length = 0; + } else { +- u->memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, u->length); ++ u->memchunk.memblock = pa_memblock_new(pa_core_get_mempool(c->protocol->core), u->length); + u->memchunk.index = u->memchunk.length = 0; + } + } +@@ -5181,7 +5198,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati + + c->rw_mempool = NULL; + +- c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool); ++ c->pstream = pa_pstream_new(pa_core_get_mainloop(p->core), io, pa_core_get_mempool(p->core)); + pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c); + pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c); + pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c); +@@ -5189,7 +5206,7 @@ void pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_nati + pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c); + pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c); + +- c->pdispatch = pa_pdispatch_new(p->core->mainloop, true, command_table, PA_COMMAND_MAX); ++ c->pdispatch = pa_pdispatch_new(pa_core_get_mainloop(p->core), true, command_table, PA_COMMAND_MAX); + + c->record_streams = pa_idxset_new(NULL, NULL); + c->output_streams = pa_idxset_new(NULL, NULL); +diff --git a/src/pulsecore/protocol-simple.c b/src/pulsecore/protocol-simple.c +index bd9136b..e1ead2f 100644 +--- a/src/pulsecore/protocol-simple.c ++++ b/src/pulsecore/protocol-simple.c +@@ -34,7 +34,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -169,7 +168,7 @@ static int do_read(connection *c) { + } + + if (!c->playback.current_memblock) { +- pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1)); ++ pa_assert_se(c->playback.current_memblock = pa_memblock_new(pa_core_get_mempool(c->protocol->core), (size_t) -1)); + c->playback.memblock_index = 0; + + space = pa_memblock_get_length(c->playback.current_memblock); +@@ -737,7 +736,7 @@ int pa_simple_options_parse(pa_simple_options *o, pa_core *c, pa_modargs *ma) { + pa_assert(PA_REFCNT_VALUE(o) >= 1); + pa_assert(ma); + +- o->sample_spec = c->default_sample_spec; ++ o->sample_spec = *pa_core_get_sample_spec(c); + if (pa_modargs_get_sample_spec_and_channel_map(ma, &o->sample_spec, &o->channel_map, PA_CHANNEL_MAP_DEFAULT) < 0) { + pa_log("Failed to parse sample type specification."); + return -1; +diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c +index 20249ec..a5cb1ee 100644 +--- a/src/pulsecore/sink.c ++++ b/src/pulsecore/sink.c +@@ -2892,17 +2892,21 @@ int pa_sink_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) { + pa_sink *sink; + uint32_t idx; + int ret = 0; ++ pa_idxset *sinks; + + pa_core_assert_ref(c); + pa_assert_ctl_context(); + pa_assert(cause != 0); + +- PA_IDXSET_FOREACH(sink, c->sinks, idx) { ++ sinks = pa_core_get_sinks(c); ++ ++ PA_IDXSET_FOREACH(sink, sinks, idx) { + int r; + + if ((r = pa_sink_suspend(sink, suspend, cause)) < 0) + ret = r; + } ++ pa_idxset_free(sinks, NULL); + + return ret; + } +diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c +index 7a33d5b..5f42965 100644 +--- a/src/pulsecore/source.c ++++ b/src/pulsecore/source.c +@@ -2254,12 +2254,15 @@ int pa_source_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) { + pa_source *source; + uint32_t idx; + int ret = 0; ++ pa_idxset *sources; + + pa_core_assert_ref(c); + pa_assert_ctl_context(); + pa_assert(cause != 0); + +- for (source = PA_SOURCE(pa_idxset_first(c->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(c->sources, &idx))) { ++ sources = pa_core_get_sources(c); ++ ++ PA_IDXSET_FOREACH(source, sources, idx) { + int r; + + if (source->monitor_of) +@@ -2268,6 +2271,7 @@ int pa_source_suspend_all(pa_core *c, bool suspend, pa_suspend_cause_t cause) { + if ((r = pa_source_suspend(source, suspend, cause)) < 0) + ret = r; + } ++ pa_idxset_free(sources, NULL); + + return ret; + } +-- +2.9.3 + diff --git a/0011-Add-access-checks.patch b/0011-Add-access-checks.patch new file mode 100644 index 0000000..fb9ec60 --- /dev/null +++ b/0011-Add-access-checks.patch @@ -0,0 +1,840 @@ +From 70721d64d1bd4428d59cb4038eb56d9d79cf24fc Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + #include + #include ++#include + #include + #include + #include +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 + diff --git a/0012-protocol-native-add-async-access-checks.patch b/0012-protocol-native-add-async-access-checks.patch new file mode 100644 index 0000000..2a746c3 --- /dev/null +++ b/0012-protocol-native-add-async-access-checks.patch @@ -0,0 +1,129 @@ +From fe86adb46f57c8b8ef683591506cbe98fff0c071 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0013-core-add-current_client.patch b/0013-core-add-current_client.patch new file mode 100644 index 0000000..425cc18 --- /dev/null +++ b/0013-core-add-current_client.patch @@ -0,0 +1,249 @@ +From 2c51c85d5c73b2217013a291cc80620e0b1ed268 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0014-core-ensure-maincontext-for-current-client.patch b/0014-core-ensure-maincontext-for-current-client.patch new file mode 100644 index 0000000..9a058ad --- /dev/null +++ b/0014-core-ensure-maincontext-for-current-client.patch @@ -0,0 +1,47 @@ +From 9820db2433a37aa1f22d962bb89876c66bcc1185 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/0015-Add-flatpak-access-control.patch b/0015-Add-flatpak-access-control.patch new file mode 100644 index 0000000..1fcbff7 --- /dev/null +++ b/0015-Add-flatpak-access-control.patch @@ -0,0 +1,828 @@ +From 46754adc0e09e7ea8a9c84183425924333c0d251 Mon Sep 17 00:00:00 2001 +From: Matthias Clasen +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 . ++***/ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 + diff --git a/0016-Make-flatpak-module-load.patch b/0016-Make-flatpak-module-load.patch new file mode 100644 index 0000000..7c034ed --- /dev/null +++ b/0016-Make-flatpak-module-load.patch @@ -0,0 +1,26 @@ +From 161ed876c3c810cb2de47516e11cf632591174bd Mon Sep 17 00:00:00 2001 +From: Matthias Clasen +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 + #include + +-#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 + diff --git a/0017-Make-sure-to-set-the-pid-in-auth_cb.patch b/0017-Make-sure-to-set-the-pid-in-auth_cb.patch new file mode 100644 index 0000000..8e1f413 --- /dev/null +++ b/0017-Make-sure-to-set-the-pid-in-auth_cb.patch @@ -0,0 +1,43 @@ +From 6fc7131ec231865d2c1ce8267013d9cf78f36e68 Mon Sep 17 00:00:00 2001 +From: Matthias Clasen +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 + diff --git a/0018-Use-permissive-policy-by-default.patch b/0018-Use-permissive-policy-by-default.patch new file mode 100644 index 0000000..41948ae --- /dev/null +++ b/0018-Use-permissive-policy-by-default.patch @@ -0,0 +1,278 @@ +From e323416499a5c6c5b5261eabb59c5e6a7ffdc300 Mon Sep 17 00:00:00 2001 +From: Wim Taymans +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 + diff --git a/pulseaudio.spec b/pulseaudio.spec index 9181aae..797d602 100644 --- a/pulseaudio.spec +++ b/pulseaudio.spec @@ -25,7 +25,7 @@ Name: pulseaudio Summary: Improved Linux Sound Server Version: %{pa_major}%{?pa_minor:.%{pa_minor}} -Release: 3%{?snap:.%{snap}git%{shortcommit}}%{?dist} +Release: 4%{?snap:.%{snap}git%{shortcommit}}%{?dist} License: LGPLv2+ URL: http://www.freedesktop.org/wiki/Software/PulseAudio %if 0%{?gitrel} @@ -56,6 +56,24 @@ 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) @@ -243,6 +261,25 @@ 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 @@ -389,6 +426,7 @@ exit 0 %{_libdir}/pulse-%{pa_major}/modules/module-dbus-protocol.so %{_libdir}/pulse-%{pa_major}/modules/module-filter-apply.so %{_libdir}/pulse-%{pa_major}/modules/module-filter-heuristics.so +%{_libdir}/pulse-%{pa_major}/modules/module-flatpak.so %{_libdir}/pulse-%{pa_major}/modules/module-device-manager.so %{_libdir}/pulse-%{pa_major}/modules/module-loopback.so %{_libdir}/pulse-%{pa_major}/modules/module-esound-compat-spawnfd.so @@ -583,6 +621,9 @@ exit 0 %changelog +* Mon Feb 13 2017 Wim Taymans - 10.0-4 +- Add flatpak access control + * Sat Feb 11 2017 Fedora Release Engineering - 10.0-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild