diff -Nru pulseaudio-12.2/debian/changelog pulseaudio-12.2/debian/changelog --- pulseaudio-12.2/debian/changelog 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/changelog 2018-08-07 17:01:22.000000000 +0800 @@ -1,3 +1,19 @@ +pulseaudio (1:12.2-0ubuntu2) cosmic; urgency=medium + + * Update snap policy to make access to audio recording conditional on + plugging the "pulseaudio" or "audio-record" interfaces (LP: #1781428): + - 0700-modules-add-snappy-policy-module.patch: rewrite to query + snapd for the client's plugged interfaces. + - 0701-enable-snap-policy-module.patch: enable the module in the + default configuration. + - Build depend on libsnapd-glib-dev. + * Remove module-trust-store patch set: + - 0409-Trust-store-patch.patch: trimmed down to pulsecore changes. + - 0410-Add-thread-to-activate-trust-store-interface.patch: removed. + - 0417-increase-timeout-check-apparmor.patch: removed. + + -- James Henstridge Tue, 07 Aug 2018 17:01:22 +0800 + pulseaudio (1:12.2-0ubuntu1) cosmic; urgency=medium * New upstream release 12.2 (LP: #1785551): diff -Nru pulseaudio-12.2/debian/control pulseaudio-12.2/debian/control --- pulseaudio-12.2/debian/control 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/control 2018-08-07 17:01:22.000000000 +0800 @@ -33,6 +33,7 @@ libltdl-dev, liborc-0.4-dev, libsamplerate0-dev, + libsnapd-glib-dev, libsndfile1-dev, libsoxr-dev (>= 0.1.1), libspeexdsp-dev (>= 1.2~rc1), diff -Nru pulseaudio-12.2/debian/patches/0409-pa-client-peer-credentials.patch pulseaudio-12.2/debian/patches/0409-pa-client-peer-credentials.patch --- pulseaudio-12.2/debian/patches/0409-pa-client-peer-credentials.patch 1970-01-01 08:00:00.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0409-pa-client-peer-credentials.patch 2018-08-07 17:01:22.000000000 +0800 @@ -0,0 +1,104 @@ +From: David Henningsson +Date: Wed, 22 Jul 2015 16:37:19 +0200 +Subject: [PATCH 4/5] Expose peer credentials on pa_client + +Signed-off-by: David Henningsson +--- + src/pulsecore/client.h | 4 ++++ + src/pulsecore/creds.h | 1 + + src/pulsecore/iochannel.c | 2 ++ + src/pulsecore/protocol-native.c | 13 +++++++++++++ + 4 files changed, 20 insertions(+) + +diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h +index eb8173d..a038a0c 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 +@@ -35,6 +36,9 @@ struct pa_client { + uint32_t index; + pa_core *core; + ++ pa_creds creds; ++ bool creds_valid; ++ + pa_proplist *proplist; + pa_module *module; + char *driver; +diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h +index 9fdbb4f..8bcf636 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; ++ pid_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 e25824b..3c83092 100644 +--- a/src/pulsecore/iochannel.c ++++ b/src/pulsecore/iochannel.c +@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l + + u = (struct ucred*) CMSG_DATA(&cmsg.hdr); + ++ /* FIXME: Check whether ucred->pid should be used */ + u->pid = getpid(); + if (ucred) { + u->uid = ucred->uid; +@@ -445,6 +446,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 f7dc338..baaa952 100644 +--- a/src/pulsecore/protocol-native.c ++++ b/src/pulsecore/protocol-native.c +@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta + do_shm = false; + + #ifdef HAVE_CREDS ++ { ++ const pa_creds *creds; ++ if ((creds = pa_pdispatch_creds(pd))) { ++ c->client->creds = *creds; ++ c->client->creds_valid = true; ++ } ++ } + if (do_shm) { + /* Only enable SHM if both sides are owned by the same + * user. This is a security measure because otherwise data +@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access_data { + } pa_protocol_native_access_data; + + static void check_access_finish_cb(pa_access_data *data, bool res) { ++ uint32_t command, tag; + pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data; + pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata); + +@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_access_data *data, bool res) { + goto finish; + } + ++ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0); ++ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0); ++ pa_assert(command == d->command); ++ pa_assert(tag == d->tag); ++ + /* call the dispatcher again, hopefully this time, the access check will + * fail or succeed immediately */ + command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata); diff -Nru pulseaudio-12.2/debian/patches/0409-Trust-store-patch.patch pulseaudio-12.2/debian/patches/0409-Trust-store-patch.patch --- pulseaudio-12.2/debian/patches/0409-Trust-store-patch.patch 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0409-Trust-store-patch.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,572 +0,0 @@ -From ff36148e002232842ea0f07b148e5844c5d44ceb Mon Sep 17 00:00:00 2001 -From: David Henningsson -Date: Wed, 22 Jul 2015 16:37:19 +0200 -Subject: [PATCH 4/5] Trust-store patch - -...includes some fixes elsewhere as well. - -Signed-off-by: David Henningsson ---- - configure.ac | 15 ++ - src/Makefile.am | 24 +++ - src/modules/trust-store/module-trust-store.c | 221 +++++++++++++++++++++++++++ - src/modules/trust-store/truststore.cc | 74 +++++++++ - src/modules/trust-store/truststore.h | 41 +++++ - src/pulsecore/client.h | 4 + - src/pulsecore/creds.h | 1 + - src/pulsecore/iochannel.c | 2 + - src/pulsecore/protocol-native.c | 13 ++ - src/pulsecore/tagstruct.c | 1 + - 12 files changed, 399 insertions(+), 7 deletions(-) - create mode 100644 src/modules/trust-store/module-trust-store.c - create mode 100644 src/modules/trust-store/truststore.cc - create mode 100644 src/modules/trust-store/truststore.h - -Index: pulseaudio/configure.ac -=================================================================== ---- pulseaudio.orig/configure.ac -+++ pulseaudio/configure.ac -@@ -1447,6 +1447,19 @@ AS_IF([test "x$ax_pthread_ok" = "xyes"], - AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) - - -+# Ubuntu touch trust store -+ -+AC_ARG_ENABLE([trust-store], -+ AS_HELP_STRING([--enable-trust-store], [Enable Ubuntu touch trust store])) -+ -+AS_IF([test "x$enable_trust_store" != "xno"], -+ [PKG_CHECK_MODULES(TRUST_STORE, [ trust-store ], [HAVE_TRUST_STORE=1], [HAVE_TRUST_STORE=0])], -+ [HAVE_WEBRTC=0]) -+ -+AS_IF([test "x$enable_trust_store" = "xyes" && test "x$HAVE_TRUST_STORE" = "x0"], -+ [AC_MSG_ERROR([*** Ubuntu touch trust store library not found])]) -+ -+AM_CONDITIONAL([HAVE_TRUST_STORE], [test "x$HAVE_TRUST_STORE" = "x1"]) - - ################################### - # Output # -@@ -1619,6 +1632,7 @@ AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"], - AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no) - AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no) - AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no) -+AS_IF([test "x$HAVE_TRUST_STORE" = "x1"], ENABLE_TRUST_STORE=yes, ENABLE_TRUST_STORE=no) - AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no) - AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no) - AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no) -@@ -1683,6 +1697,7 @@ echo " - Enable speex (resampler, AEC): ${ENABLE_SPEEX} - Enable soxr (resampler): ${ENABLE_SOXR} - Enable WebRTC echo canceller: ${ENABLE_WEBRTC} -+ Enable Ubuntu trust store: ${ENABLE_TRUST_STORE} - Enable gcov coverage: ${ENABLE_GCOV} - Enable unit tests: ${ENABLE_TESTS} - Database -Index: pulseaudio/src/Makefile.am -=================================================================== ---- pulseaudio.orig/src/Makefile.am -+++ pulseaudio/src/Makefile.am -@@ -1105,6 +1105,10 @@ if HAVE_WEBRTC - modlibexec_LTLIBRARIES += libwebrtc-util.la - endif - -+if HAVE_TRUST_STORE -+modlibexec_LTLIBRARIES += libtruststore-util.la -+endif -+ - if HAVE_ESOUND - modlibexec_LTLIBRARIES += \ - libprotocol-esound.la -@@ -1235,6 +1239,11 @@ modlibexec_LTLIBRARIES += \ - module-role-ducking.la \ - module-allow-passthrough.la - -+if HAVE_TRUST_STORE -+modlibexec_LTLIBRARIES += \ -+ module-trust-store.la -+endif -+ - if HAVE_ESOUND - modlibexec_LTLIBRARIES += \ - module-esound-protocol-tcp.la \ -@@ -2025,6 +2034,20 @@ module_echo_cancel_la_CFLAGS += -DHAVE_W - module_echo_cancel_la_LIBADD += libwebrtc-util.la - endif - -+if HAVE_TRUST_STORE -+# Separate helper library to keep C and C++ code apart, like we do for webrtc -+libtruststore_util_la_SOURCES = modules/trust-store/truststore.cc -+libtruststore_util_la_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11 $(SERVER_CFLAGS) $(TRUST_STORE_CFLAGS) \ -+ $(DBUS_CFLAGS) -DHAVE_TRUST_STORE=1 -+libtruststore_util_la_LIBADD = libpulsecore-@PA_MAJORMINOR@.la $(TRUST_STORE_LIBS) $(DBUS_LIBS) -+libtruststore_util_la_LDFLAGS = -avoid-version -+ -+module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c -+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la -+module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 -+endif -+ - # RTP modules - module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c - module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS) -Index: pulseaudio/src/modules/trust-store/module-trust-store.c -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/trust-store/module-trust-store.c -@@ -0,0 +1,226 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2015 Canonical Ltd. -+ Written by David Henningsson -+ -+ 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 -+ -+PA_MODULE_AUTHOR("David Henningsson"); -+PA_MODULE_DESCRIPTION("Ubuntu touch trust store integration"); -+PA_MODULE_VERSION(PACKAGE_VERSION); -+PA_MODULE_LOAD_ONCE(true); -+ -+#include "truststore.h" -+ -+#define REQUEST_GRANTED 1 -+#define REQUEST_DENIED -1 -+#define REQUEST_PENDING 0 -+ -+struct userdata; -+ -+struct per_client { -+ uint32_t index; -+ char *appname; -+ uid_t uid; -+ pid_t pid; -+ struct userdata *userdata; -+ pa_dynarray *pending_requests; -+ pa_atomic_t result; -+}; -+ -+struct userdata { -+ pa_core *core; -+ pa_trust_store *ts; -+ pa_hashmap *per_clients; -+ pa_thread *thread; -+ pa_fdsem *fdsem; -+ pa_io_event *io_event; -+ pa_hook_slot *connect_hook_slot; -+}; -+ -+static struct per_client *per_client_new(struct userdata *u, uint32_t client_index) { -+ struct per_client *pc; -+ pa_client *client = pa_idxset_get_by_index(u->core->clients, client_index); -+ -+ pa_assert(client); -+ if (!client->creds_valid) { -+ pa_log_warn("Client %d has no creds, cannot authenticate", client_index); -+ return NULL; -+ } -+ -+ pc = pa_xnew0(struct per_client, 1); -+ // TODO: Do we need something else than the application name here - -+ // the application can probably spoof it -+ pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); -+ pc->index = client_index; -+ pc->uid = client->creds.uid; -+ pc->pid = client->creds.pid; -+ pc->pending_requests = pa_dynarray_new(NULL); -+ pc->userdata = u; -+ -+ pa_hashmap_put(u->per_clients, (void *) (size_t) client_index, pc); -+ -+ return pc; -+} -+ -+static void per_client_free_from_hashmap(void *data) { -+ struct per_client *pc = data; -+ if (!pc) -+ return; -+ pa_xfree(pc->appname); -+ pa_dynarray_free(pc->pending_requests); -+ pa_xfree(pc); -+} -+ -+static void thread_func(void *data) { -+ struct per_client *pc = data; -+ -+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, -+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase -+ /// can't be translated in this language, translate the whole sentence -+ /// 'This app wants to record audio.' -+ _("wants to record audio.")); -+ -+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); -+ pa_fdsem_post(pc->userdata->fdsem); -+} -+ -+static void check_queue(struct userdata *u) { -+ struct per_client *pc; -+ void *dummy; -+ -+ pa_log_debug("Checking queue, size: %d, thread object: %s", -+ pa_hashmap_size(u->per_clients), pa_yes_no(u->thread)); -+ if (u->thread) { -+ pa_access_data *ad; -+ unsigned i; -+ int result; -+ pc = pa_thread_get_data(u->thread); -+ result = pa_atomic_load(&pc->result); -+ if (result == REQUEST_PENDING) -+ return; /* Still processing */ -+ pa_thread_free(u->thread); -+ u->thread = NULL; -+ -+ pa_log_debug("Letting client %d (%s) know the result (%s)", -+ pc->index, pc->appname, result == REQUEST_GRANTED ? "granted" : "rejected"); -+ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) { -+ ad->async_finish_cb(ad, result == REQUEST_GRANTED); -+ } -+ pa_hashmap_remove(u->per_clients, (void*) (size_t) pc->index); -+ } -+ -+ /* Find a client that needs asking */ -+ PA_HASHMAP_FOREACH(pc, u->per_clients, dummy) { -+ if (u->thread) -+ return; -+ pa_assert(pa_atomic_load(&pc->result) == REQUEST_PENDING); -+ pa_log_debug("Starting thread to ask about client %d (%s)", pc->index, -+ pc->appname); -+ u->thread = pa_thread_new("trust-store", thread_func, pc); -+ } -+} -+ -+static void check_fdsem(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t ee, -+ void *userdata) { -+ struct userdata *u = userdata; -+ pa_fdsem_after_poll(u->fdsem); -+ check_queue(u); -+ pa_fdsem_before_poll(u->fdsem); -+} -+ -+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) { -+ struct per_client *pc = pa_hashmap_get(u->per_clients, (void*) (size_t) d->client_index); -+ int r; -+ if (!pc) -+ pc = per_client_new(u, d->client_index); -+ -+ r = pa_atomic_load(&pc->result); -+ pa_log_debug("Checking permission to record for client %d (%s) ", d->client_index, -+ r == REQUEST_GRANTED ? "granted" : r != REQUEST_PENDING ? "rejected" : "unknown"); -+ if (r == REQUEST_GRANTED) return PA_HOOK_OK; -+ if (r != REQUEST_PENDING) return PA_HOOK_STOP; -+ -+ pa_dynarray_append(pc->pending_requests, d); -+ check_queue(u); -+ return PA_HOOK_CANCEL; -+} -+ -+/* Test code - remove from final product */ -+static void test(struct userdata *u) { -+ uint32_t dummy; -+ pa_client *client; -+ -+ PA_IDXSET_FOREACH(client, u->core->clients, dummy) { -+ if (client->creds_valid) -+ pa_trust_store_check(u->ts, "The evil app", client->creds.uid, -+ client->creds.pid, "%1% wants to eat your laundry."); -+ } -+} -+ -+int pa__init(pa_module *m) { -+ struct userdata *u; -+ pa_trust_store *ts = pa_trust_store_new(); -+ if (ts == NULL) -+ return -1; -+ u = pa_xnew0(struct userdata, 1); -+ u->ts = ts; -+ u->core = m->core; -+ u->per_clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, -+ NULL, per_client_free_from_hashmap); -+ m->userdata = u; -+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], -+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u); -+ -+ pa_assert_se(u->fdsem = pa_fdsem_new()); -+ pa_assert_se(u->io_event = m->core->mainloop->io_new(m->core->mainloop, -+ pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u)); -+ pa_fdsem_before_poll(u->fdsem); -+ -+ test(u); -+ return 0; -+} -+ -+void pa__done(pa_module *m) { -+ struct userdata *u = m->userdata; -+ if (u) { -+ if (u->connect_hook_slot) -+ pa_hook_slot_free(u->connect_hook_slot); -+ if (u->thread) -+ pa_thread_free(u->thread); -+ if (u->io_event) -+ m->core->mainloop->io_free(u->io_event); -+ if (u->fdsem) -+ pa_fdsem_free(u->fdsem); -+ if (u->per_clients) -+ pa_hashmap_free(u->per_clients); -+ if (u->ts) -+ pa_trust_store_free(u->ts); -+ pa_xfree(u); -+ } -+} -Index: pulseaudio/src/modules/trust-store/truststore.cc -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/trust-store/truststore.cc -@@ -0,0 +1,74 @@ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+#include -+#include -+#include -+ -+#include -+ -+PA_C_DECL_BEGIN -+#include -+#include -+#include -+ -+#include "truststore.h" -+PA_C_DECL_END -+ -+class TrustStore { -+public: -+ std::shared_ptr agent; -+}; -+ -+pa_trust_store* pa_trust_store_new() { -+ try { -+ auto bus = std::make_shared(core::dbus::WellKnownBus::session); -+ auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio"); -+ auto ts = new TrustStore(); -+ ts->agent = agent; -+ return (pa_trust_store *) ts; -+ } catch(const std::exception &e) { -+ pa_log_error("Could not create Ubuntu touch trust store connection: %s", -+ e.what()); -+ } catch(...) { -+ pa_log_error("Could not create Ubuntu touch trust store connection"); -+ return NULL; -+ } -+} -+ -+void pa_trust_store_free(pa_trust_store *t) { -+ pa_assert(t != NULL); -+ auto ts = (TrustStore*) t; -+ delete ts; -+} -+ -+bool pa_trust_store_check(pa_trust_store *t, const char *appname, -+ uid_t uid, pid_t pid, const char *description) { -+ auto ts = (TrustStore*) t; -+ try { -+ -+ core::trust::Agent::RequestParameters params { -+ core::trust::Uid{uid}, -+ core::trust::Pid{pid}, -+ appname, -+ core::trust::Feature{0}, -+ description -+ }; -+ pa_log_debug("Asking Ubuntu touch trust store for permission (app: %s)", -+ params.application.id.c_str()); -+ auto answer = ts->agent->authenticate_request_with_parameters(params); -+ if (answer == core::trust::Request::Answer::granted) { -+ pa_log_debug("Request granted."); -+ return true; -+ } -+ pa_log_info("Request denied."); -+ } catch(const std::exception &e) { -+ pa_log_error("Could not ask Ubuntu touch trust store for permission: %s", -+ e.what()); -+ } catch(...) { -+ pa_log_error("Could not ask Ubuntu touch trust store for permission"); -+ } -+ return false; -+} -Index: pulseaudio/src/modules/trust-store/truststore.h -=================================================================== ---- /dev/null -+++ pulseaudio/src/modules/trust-store/truststore.h -@@ -0,0 +1,41 @@ -+/*** -+ This file is part of PulseAudio. -+ -+ Copyright 2015 Canonical Ltd. -+ Written by David Henningsson -+ -+ 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 . -+***/ -+ -+#ifndef footruststorehfoo -+#define footruststorehfoo -+ -+#ifdef HAVE_CONFIG_H -+#include -+#endif -+ -+#include -+ -+#ifdef HAVE_TRUST_STORE -+PA_C_DECL_BEGIN -+typedef struct pa_trust_store pa_trust_store; -+ -+pa_trust_store* pa_trust_store_new(); -+void pa_trust_store_free(pa_trust_store *ts); -+bool pa_trust_store_check(pa_trust_store *ts, const char *appname, -+ uid_t uid, pid_t pid, const char *description); -+PA_C_DECL_END -+#endif -+ -+#endif -Index: pulseaudio/src/pulsecore/client.h -=================================================================== ---- pulseaudio.orig/src/pulsecore/client.h -+++ pulseaudio/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 -@@ -35,6 +36,9 @@ struct pa_client { - uint32_t index; - pa_core *core; - -+ pa_creds creds; -+ bool creds_valid; -+ - pa_proplist *proplist; - pa_module *module; - char *driver; -Index: pulseaudio/src/pulsecore/creds.h -=================================================================== ---- pulseaudio.orig/src/pulsecore/creds.h -+++ pulseaudio/src/pulsecore/creds.h -@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cms - struct pa_creds { - gid_t gid; - uid_t uid; -+ pid_t pid; - }; - - /* Struct for handling ancillary data, i e, extra data that can be sent together with a message -Index: pulseaudio/src/pulsecore/iochannel.c -=================================================================== ---- pulseaudio.orig/src/pulsecore/iochannel.c -+++ pulseaudio/src/pulsecore/iochannel.c -@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa - - u = (struct ucred*) CMSG_DATA(&cmsg.hdr); - -+ /* FIXME: Check whether ucred->pid should be used */ - u->pid = getpid(); - if (ucred) { - u->uid = ucred->uid; -@@ -445,6 +446,7 @@ ssize_t pa_iochannel_read_with_ancil_dat - - 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) { -Index: pulseaudio/src/pulsecore/protocol-native.c -=================================================================== ---- pulseaudio.orig/src/pulsecore/protocol-native.c -+++ pulseaudio/src/pulsecore/protocol-native.c -@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *p - do_shm = false; - - #ifdef HAVE_CREDS -+ { -+ const pa_creds *creds; -+ if ((creds = pa_pdispatch_creds(pd))) { -+ c->client->creds = *creds; -+ c->client->creds_valid = true; -+ } -+ } - if (do_shm) { - /* Only enable SHM if both sides are owned by the same - * user. This is a security measure because otherwise data -@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access - } pa_protocol_native_access_data; - - static void check_access_finish_cb(pa_access_data *data, bool res) { -+ uint32_t command, tag; - pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data; - pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata); - -@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_ac - goto finish; - } - -+ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0); -+ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0); -+ pa_assert(command == d->command); -+ pa_assert(tag == d->tag); -+ - /* call the dispatcher again, hopefully this time, the access check will - * fail or succeed immediately */ - command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata); -Index: pulseaudio/po/POTFILES.in -=================================================================== ---- pulseaudio.orig/po/POTFILES.in -+++ pulseaudio/po/POTFILES.in -@@ -23,6 +23,7 @@ src/modules/jack/module-jack-sink.c - src/modules/jack/module-jack-source.c - src/modules/macosx/module-coreaudio-device.c - src/modules/module-allow-passthrough.c -+src/modules/trust-store/module-trust-store.c - src/modules/module-always-sink.c - src/modules/module-always-source.c - src/modules/module-cli.c diff -Nru pulseaudio-12.2/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch pulseaudio-12.2/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch --- pulseaudio-12.2/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,108 +0,0 @@ -From 90dbf1d25e12b9cfc86aecbd02f14a05fd5dca8c Mon Sep 17 00:00:00 2001 -From: Alfonso Sanchez-Beato -Date: Fri, 7 Aug 2015 09:08:02 +0200 -Subject: [PATCH] Add thread to activate trust-store interface - ---- - src/modules/trust-store/module-trust-store.c | 14 +------------- - src/modules/trust-store/truststore.cc | 19 +++++++++++++++++++ - 2 files changed, 20 insertions(+), 13 deletions(-) - -Index: pulseaudio/src/modules/trust-store/module-trust-store.c -=================================================================== ---- pulseaudio.orig/src/modules/trust-store/module-trust-store.c -+++ pulseaudio/src/modules/trust-store/module-trust-store.c -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - #include - - PA_MODULE_AUTHOR("David Henningsson"); -@@ -171,18 +172,6 @@ static pa_hook_result_t connect_record_h - return PA_HOOK_CANCEL; - } - --/* Test code - remove from final product */ --static void test(struct userdata *u) { -- uint32_t dummy; -- pa_client *client; -- -- PA_IDXSET_FOREACH(client, u->core->clients, dummy) { -- if (client->creds_valid) -- pa_trust_store_check(u->ts, "The evil app", client->creds.uid, -- client->creds.pid, "%1% wants to eat your laundry."); -- } --} -- - int pa__init(pa_module *m) { - struct userdata *u; - pa_trust_store *ts = pa_trust_store_new(); -@@ -202,7 +191,6 @@ int pa__init(pa_module *m) { - pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u)); - pa_fdsem_before_poll(u->fdsem); - -- test(u); - return 0; - } - -Index: pulseaudio/src/modules/trust-store/truststore.cc -=================================================================== ---- pulseaudio.orig/src/modules/trust-store/truststore.cc -+++ pulseaudio/src/modules/trust-store/truststore.cc -@@ -4,6 +4,7 @@ - - #include - #include -+#include - #include - #include - -@@ -13,6 +14,7 @@ PA_C_DECL_BEGIN - #include - #include - #include -+#include - - #include "truststore.h" - PA_C_DECL_END -@@ -20,14 +22,27 @@ PA_C_DECL_END - class TrustStore { - public: - std::shared_ptr agent; -+ std::shared_ptr bus; -+ pa_thread *thread; - }; - -+static void thread_func(void *data) { -+ class TrustStore *ts = (class TrustStore *) data; -+ -+ ts->bus->run(); -+} -+ - pa_trust_store* pa_trust_store_new() { - try { - auto bus = std::make_shared(core::dbus::WellKnownBus::session); -+ bus->install_executor(core::dbus::asio::make_executor(bus)); -+ - auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio"); - auto ts = new TrustStore(); - ts->agent = agent; -+ ts->bus = bus; -+ ts->thread = pa_thread_new("trust-store-bus", thread_func, ts); -+ - return (pa_trust_store *) ts; - } catch(const std::exception &e) { - pa_log_error("Could not create Ubuntu touch trust store connection: %s", -@@ -41,6 +56,10 @@ pa_trust_store* pa_trust_store_new() { - void pa_trust_store_free(pa_trust_store *t) { - pa_assert(t != NULL); - auto ts = (TrustStore*) t; -+ if (ts->thread) { -+ ts->bus->stop(); -+ pa_thread_free(ts->thread); -+ } - delete ts; - } - diff -Nru pulseaudio-12.2/debian/patches/0417-increase-timeout-check-apparmor.patch pulseaudio-12.2/debian/patches/0417-increase-timeout-check-apparmor.patch --- pulseaudio-12.2/debian/patches/0417-increase-timeout-check-apparmor.patch 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0417-increase-timeout-check-apparmor.patch 1970-01-01 08:00:00.000000000 +0800 @@ -1,102 +0,0 @@ -Index: pulseaudio/src/modules/trust-store/module-trust-store.c -=================================================================== ---- pulseaudio.orig/src/modules/trust-store/module-trust-store.c -+++ pulseaudio/src/modules/trust-store/module-trust-store.c -@@ -31,6 +31,8 @@ - #include - #include - -+#include -+ - PA_MODULE_AUTHOR("David Henningsson"); - PA_MODULE_DESCRIPTION("Ubuntu touch trust store integration"); - PA_MODULE_VERSION(PACKAGE_VERSION); -@@ -75,9 +77,37 @@ static struct per_client *per_client_new - } - - pc = pa_xnew0(struct per_client, 1); -- // TODO: Do we need something else than the application name here - -- // the application can probably spoof it -- pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); -+ -+ pa_log_info("Client application name is: '%s'", pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); -+ -+ // ask apparmor about the context of the client, later this will be used to decide if the -+ // app should be or not using the trust store -+ -+ if (aa_is_enabled()) { -+ char *context = NULL; -+ char *mode = NULL; -+ -+ /* XXX: The mode string is *NOT* be freed since it forms -+ * part of the allocation returned in context. -+ * -+ * See aa_gettaskcon(2) for details. -+ */ -+ if (aa_gettaskcon (client->creds.pid, &context, &mode) >= 0) { -+ pc->appname = pa_xstrdup(context); -+ pa_log_info("Client apparmor profile is: '%s'", context); -+ } else { -+ pa_log_error("AppArmo profile could not be retrieved."); -+ } -+ -+ if (context) -+ free(context); -+ -+ } else { -+ // while we do leave the appname as empty if we fail, if apparmor is not present we should -+ // assume that the app is not confined -+ pc->appname = pa_xstrdup("unconfined"); -+ } -+ - pc->index = client_index; - pc->uid = client->creds.uid; - pc->pid = client->creds.pid; -@@ -101,13 +131,28 @@ static void per_client_free_from_hashmap - static void thread_func(void *data) { - struct per_client *pc = data; - -- bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, -- /// TRANSLATORS: The app icon and name appears above this string. If the phrase -- /// can't be translated in this language, translate the whole sentence -- /// 'This app wants to record audio.' -- _("wants to record audio.")); -+ // there are 3 possible values for the app name that we will consider at this point -+ // * empty string: there was an error when retrieving the value and therefore we will -+ // automatically deny access -+ // * unconfined: the app is unconfined and therefore we will automatically grant access -+ // * appname: we need the user to decide what to do. -+ -+ if (pc->appname == NULL) { -+ pa_log_info("Client apparmor could not retrieved."); -+ pa_atomic_store(&pc->result, REQUEST_DENIED); -+ } else if (pa_streq(pc->appname, "unconfined")) { -+ pa_log_info("Connected client is unconfined."); -+ pa_atomic_store(&pc->result, REQUEST_GRANTED); -+ } else { -+ pa_log_info("User needs to authorize the application.."); -+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, -+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase -+ /// can't be translated in this language, translate the whole sentence -+ /// 'This app wants to record audio.' -+ _("wants to record audio.")); -+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); -+ } - -- pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); - pa_fdsem_post(pc->userdata->fdsem); - } - -Index: pulseaudio/src/Makefile.am -=================================================================== ---- pulseaudio.orig/src/Makefile.am -+++ pulseaudio/src/Makefile.am -@@ -2043,7 +2043,7 @@ libtruststore_util_la_LIBADD = libpulsec - libtruststore_util_la_LDFLAGS = -avoid-version - - module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c --module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) -+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor - module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la - module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 - endif diff -Nru pulseaudio-12.2/debian/patches/0700-modules-add-snappy-policy-module.patch pulseaudio-12.2/debian/patches/0700-modules-add-snappy-policy-module.patch --- pulseaudio-12.2/debian/patches/0700-modules-add-snappy-policy-module.patch 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0700-modules-add-snappy-policy-module.patch 2018-08-07 17:01:22.000000000 +0800 @@ -1,56 +1,101 @@ -From a430ebc2271f5a07389ee25631a8ba5524371764 Mon Sep 17 00:00:00 2001 -From: Simon Fels -Date: Tue, 17 May 2016 17:29:31 +0200 -Subject: [PATCH] modules: add snappy policy module +From: James Henstridge +Date: Tue, 7 Aug 2018 12:40:59 +0800 +Subject: [PATCH] modules: add snap policy module +Co-authored-by: Simon Fels --- - configure.ac | 18 +++++++- - src/Makefile.am | 18 ++++++++ - src/modules/module-snappy-policy.c | 94 ++++++++++++++++++++++++++++++++++++++ - 3 files changed, 129 insertions(+), 1 deletion(-) - create mode 100644 src/modules/module-snappy-policy.c + configure.ac | 17 ++ + src/Makefile.am | 13 ++ + src/modules/module-snap-policy.c | 347 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 377 insertions(+) + create mode 100644 src/modules/module-snap-policy.c -Index: pulseaudio/src/Makefile.am -=================================================================== ---- pulseaudio.orig/src/Makefile.am -+++ pulseaudio/src/Makefile.am -@@ -1250,6 +1250,11 @@ modlibexec_LTLIBRARIES += \ +diff --git a/configure.ac b/configure.ac +index c9c414f..97c2d9d 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1446,6 +1446,21 @@ AS_IF([test "x$os_is_win32" != "x1"], + AS_IF([test "x$ax_pthread_ok" = "xyes"], + AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) + ++# Snappy support ++ ++AC_ARG_ENABLE([snap], ++ AS_HELP_STRING([--enable-snap], [Enable snap support])) ++ ++have_apparmor=0 ++have_snapd_glib=0 ++AS_IF([test "x$enable_snap" != "xno"], ++ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [have_apparmor=1]) ++ PKG_CHECK_MODULES(SNAPD_GLIB, [snapd-glib], [have_snapd_glib=1])]) ++ ++AS_IF([test "x$enable_snap" = "xyes" && test "x$have_apparmor" = "x0" -o "x$have-snapd_glib" = "x0"], ++ [AC_MSG_ERROR([*** Snap module dependencies missing])]) ++ ++AM_CONDITIONAL([BUILD_SNAP], [test "x$enable_snap" = "xyes"]) + + + ################################### +@@ -1627,6 +1642,7 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], EN + AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no) + AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no) + AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no) ++AS_IF([test "x$enable_snap" != "xno"], ENABLE_SNAP=yes, ENABLE_SNAP=no) + + echo " + ---{ $PACKAGE_NAME $VERSION }--- +@@ -1683,6 +1699,7 @@ echo " + Enable speex (resampler, AEC): ${ENABLE_SPEEX} + Enable soxr (resampler): ${ENABLE_SOXR} + Enable WebRTC echo canceller: ${ENABLE_WEBRTC} ++ Enable Snap support: ${ENABLE_SNAP} + Enable gcov coverage: ${ENABLE_GCOV} + Enable unit tests: ${ENABLE_TESTS} + Database +diff --git a/src/Makefile.am b/src/Makefile.am +index d623d0a..16b4d5d 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -1240,6 +1240,11 @@ modlibexec_LTLIBRARIES += \ module-esound-sink.la endif -+if HAVE_APPARMOR ++if BUILD_SNAP +modlibexec_LTLIBRARIES += \ -+ module-snappy-policy.la ++ module-snap-policy.la +endif + # See comment at librtp.la above if !OS_IS_WIN32 modlibexec_LTLIBRARIES += \ -@@ -2048,6 +2053,14 @@ module_trust_store_la_LIBADD = $(MODULE_ - module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 +@@ -2024,6 +2029,14 @@ module_echo_cancel_la_CFLAGS += -DHAVE_WEBRTC=1 + module_echo_cancel_la_LIBADD += libwebrtc-util.la endif +# Snappy -+if HAVE_APPARMOR -+module_snappy_policy_la_SOURCES = modules/module-snappy-policy.c -+module_snappy_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor -+module_snappy_policy_la_LIBADD = $(MODULE_LIBADD) -+module_snappy_policy_la_CFLAGS = $(AM_CFLAGS) -DHAVE_SNAPPY=1 ++if BUILD_SNAP ++module_snap_policy_la_SOURCES = modules/module-snap-policy.c ++module_snap_policy_la_LDFLAGS = $(MODULE_LDFLAGS) ++module_snap_policy_la_LIBADD = $(MODULE_LIBADD) $(APPARMOR_LIBS) $(SNAPD_GLIB_LIBS) ++module_snap_policy_la_CFLAGS = $(AM_CFLAGS) $(APPARMOR_CFLAGS) $(SNAPD_GLIB_CFLAGS) -DPA_MODULE_NAME=module_snap_policy +endif + # RTP modules module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS) -Index: pulseaudio/src/modules/module-snappy-policy.c -=================================================================== +diff --git a/src/modules/module-snap-policy.c b/src/modules/module-snap-policy.c +new file mode 100644 +index 0000000..8660476 --- /dev/null -+++ pulseaudio/src/modules/module-snappy-policy.c -@@ -0,0 +1,96 @@ ++++ b/src/modules/module-snap-policy.c +@@ -0,0 +1,347 @@ +/*** + This file is part of PulseAudio. + -+ Copyright 2016 Canonical Ltd. -+ Written by Simon Fels ++ Copyright 2018 Canonical Ltd. ++ Authors: ++ Simon Fels ++ James Henstridge + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published @@ -71,116 +116,323 @@ +#endif + +#include -+#include ++#include ++#include + -+#include ++#include +#include ++#include ++#include +#include -+#include -+#include ++#include +#include -+#include +#include ++#include + +#define SNAP_LABEL_PREFIX "snap." +#define SNAP_LABEL_PREFIX_LENGTH 5 + -+PA_MODULE_AUTHOR("Simon Fels"); -+PA_MODULE_DESCRIPTION("Ubuntu Snappy policy management"); ++PA_MODULE_AUTHOR("Canonical Ltd"); ++PA_MODULE_DESCRIPTION("Ubuntu Snap policy management"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(true); + ++struct per_client { ++ struct userdata *userdata; ++ uint32_t index; ++ char *snap_name; ++ pa_dynarray *pending_requests; /* of pa_access_data */ ++ bool completed; ++ bool grant_access; ++}; ++ +struct userdata { -+ pa_core *core; ++ pa_io_event *io_event; + pa_hook_slot *connect_hook_slot; ++ ++ pa_thread *thread; ++ pa_mutex *mutex; ++ pa_cond *cond; ++ ++ pa_hashmap *clients; /* int => struct per_client */ ++ pa_asyncq *results; /* of struct per_client */ ++ ++ /* Data owned by glib thread */ ++ GMainContext *main_context; ++ GMainLoop *main_loop; ++ GCancellable *cancellable; ++ SnapdClient *snapd; +}; + -+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) { -+ pa_client *client = pa_idxset_get_by_index(u->core->clients, d->client_index); -+ if (!client) -+ return PA_HOOK_OK; -+ -+ char *label = NULL; -+ char *mode = NULL; -+ if (aa_gettaskcon(client->creds.pid, &label, &mode) < 0) { -+ pa_log_warn("Failed to retrieve apparmor security label for pid %u: %s", -+ client->creds.pid, strerror(-errno)); -+ return PA_HOOK_OK; -+ } -+ -+ pa_hook_result_t decision = PA_HOOK_OK; -+ -+ // We only cancel the attempt of the client to start audio recording -+ // when we could successfully determine that the request is coming -+ // from an app which is part of a snap. Otherwise we continue to -+ // work as normal. -+ if (label && strncmp(SNAP_LABEL_PREFIX, label, SNAP_LABEL_PREFIX_LENGTH) == 0) -+ decision = PA_HOOK_CANCEL; ++/* ---- Code running in glib thread ---- */ ++ ++static void check_interfaces_finish(GObject *source_object, ++ GAsyncResult *result, ++ gpointer user_data) ++{ ++ struct per_client *pc = user_data; ++ struct userdata *u = pc->userdata; ++ g_autoptr(GError) error = NULL; ++ g_autoptr(GPtrArray) plugs = NULL; ++ unsigned i; ++ ++ pa_mutex_lock(u->mutex); ++ ++ if (!snapd_client_get_interfaces_finish(u->snapd, result, &plugs, NULL, &error)) { ++ pa_log_warn("snapd_client_get_interfaces failed: %s", error->message); ++ goto end; ++ } ++ ++ /* determine pc->grant_access */ ++ for (i = 0; i < plugs->len; i++) { ++ SnapdPlug *plug = plugs->pdata[i]; ++ const char *snap_name = snapd_plug_get_snap(plug); ++ const char *iface = snapd_plug_get_interface(plug); ++ const bool connected = snapd_plug_get_connections(plug)->len != 0; ++ ++ /* We are only interested in connected plugs of our snap */ ++ if (!connected || strcmp(snap_name, pc->snap_name) != 0) { ++ continue; ++ } ++ if (!strcmp(iface, "pulseaudio") || !strcmp(iface, "audio-record")) { ++ pc->grant_access = true; ++ break; ++ } ++ } + -+ free(label); ++end: ++ pc->completed = true; ++ pa_asyncq_push(u->results, pc, true); ++ pa_mutex_unlock(u->mutex); ++} + -+ return decision; ++static gboolean check_interfaces(void *data) ++{ ++ struct per_client *pc = data; ++ struct userdata *u = pc->userdata; ++ ++ snapd_client_get_interfaces_async(u->snapd, u->cancellable, ++ check_interfaces_finish, pc); ++ return G_SOURCE_REMOVE; ++} ++ ++ ++static void thread_main(void *data) { ++ struct userdata *u = data; ++ ++ pa_mutex_lock(u->mutex); ++ ++ u->main_context = g_main_context_new(); ++ g_main_context_push_thread_default(u->main_context); ++ u->main_loop = g_main_loop_new(u->main_context, false); ++ u->cancellable = g_cancellable_new(); ++ u->snapd = snapd_client_new(); ++ ++ /* Signal main thread that we've finished initialisation */ ++ pa_cond_signal(u->cond, false); ++ pa_mutex_unlock(u->mutex); ++ ++ pa_log_info("Starting GLib main loop"); ++ g_main_loop_run(u->main_loop); ++ pa_log_info("GLib main loop exited"); ++ ++ g_cancellable_cancel(u->cancellable); ++ ++ g_clear_object(&u->snapd); ++ g_clear_object(&u->cancellable); ++ g_main_context_pop_thread_default(u->main_context); ++ g_clear_pointer(&u->main_loop, g_main_loop_unref); ++ g_clear_pointer(&u->main_context, g_main_context_unref); ++} ++ ++static gboolean thread_quit(void *data) ++{ ++ struct userdata *u = data; ++ ++ g_main_loop_quit(u->main_loop); ++ return G_SOURCE_REMOVE; ++} ++ ++/* ---- Code running in main thread ---- */ ++ ++static struct per_client *per_client_new(struct userdata *u, ++ uint32_t client_index, ++ char *snap_name) { ++ struct per_client *pc = pa_xnew0(struct per_client, 1); ++ pc->userdata = u; ++ pc->index = client_index; ++ pc->snap_name = snap_name; ++ pc->pending_requests = pa_dynarray_new(NULL); ++ pc->completed = false; ++ pc->grant_access = false; ++ return pc; ++} ++ ++static void per_client_free(struct per_client *pc) { ++ if (!pc) return; ++ pa_xfree(pc->snap_name); ++ pa_dynarray_free(pc->pending_requests); ++ pa_xfree(pc); ++} ++ ++static char *client_get_snap_name(pa_core *core, uint32_t client_index) { ++ pa_client *client; ++ char *context = NULL; ++ char *snap_name = NULL; ++ char *dot; ++ ++ client = pa_idxset_get_by_index(core->clients, client_index); ++ pa_assert(client != NULL); ++ if (!client->creds_valid) { ++ pa_log_warn("Client %u has no creds, cannot authenticate", client_index); ++ goto end; ++ } ++ ++ /* If AppArmor is not enabled, then we can't identify the client */ ++ if (!aa_is_enabled()) { ++ goto end; ++ } ++ if (aa_gettaskcon(client->creds.pid, &context, NULL) < 0) { ++ pa_log_error("AppArmor profile could not be retrieved."); ++ goto end; ++ } ++ ++ /* If the AppArmor context does not begin with "snap.", then this ++ * is not a snap */ ++ if (strncmp(context, SNAP_LABEL_PREFIX, SNAP_LABEL_PREFIX_LENGTH) != 0) { ++ goto end; ++ } ++ ++ dot = strchr(context+SNAP_LABEL_PREFIX_LENGTH, '.'); ++ if (dot == NULL) { ++ pa_log_warn("Malformed snapd AppArmor profile name: %s", context); ++ goto end; ++ } ++ snap_name = pa_xstrndup(context+5, dot-context-SNAP_LABEL_PREFIX_LENGTH); ++ ++end: ++ free(context); ++ return snap_name; ++} ++ ++static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, ++ struct userdata *u) { ++ pa_hook_result_t result = PA_HOOK_STOP; ++ struct per_client *pc = NULL; ++ char *snap_name = NULL; ++ ++ pa_mutex_lock(u->mutex); ++ pc = pa_hashmap_get(u->clients, (void *)(size_t)d->client_index); ++ if (pc) { ++ if (pc->completed) { ++ result = pc->grant_access ? PA_HOOK_OK : PA_HOOK_STOP; ++ } else { ++ /* A permission check for this snap is currently in progress */ ++ pa_dynarray_append(pc->pending_requests, d); ++ result = PA_HOOK_CANCEL; ++ } ++ goto end; ++ } ++ ++ snap_name = client_get_snap_name(core, d->client_index); ++ if (!snap_name) { ++ /* Not a snap, so allow access */ ++ result = PA_HOOK_OK; ++ goto end; ++ } ++ ++ /* create new per client struct, and submit to glib thread */ ++ pc = per_client_new(u, d->client_index, snap_name); ++ pa_dynarray_append(pc->pending_requests, d); ++ pa_hashmap_put(u->clients, (void *) (size_t) d->client_index, pc); ++ pa_log_info("Checking access for client %d (%s)", pc->index, pc->snap_name); ++ g_main_context_invoke(u->main_context, check_interfaces, pc); ++ ++ result = PA_HOOK_CANCEL; ++ ++end: ++ pa_mutex_unlock(u->mutex); ++ return result; ++} ++ ++static void deliver_result(struct userdata *u, struct per_client *pc) { ++ pa_access_data *ad; ++ unsigned i; ++ ++ pa_log_info("Access check for client %u (%s): %d", ++ pc->index, pc->snap_name, pc->grant_access); ++ ++ /* Call the hooks without holding the mutex, since this will ++ * recurse into connect_record_hook. Access to pending_requests ++ * should be safe here, since connect_record_hook wont alter the ++ * array when the access check is complete. */ ++ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) { ++ ad->async_finish_cb(ad, pc->grant_access); ++ } ++ pa_mutex_lock(u->mutex); ++ pa_hashmap_remove_and_free(u->clients, (void *) (size_t) pc->index); ++ pa_mutex_unlock(u->mutex); ++} ++ ++static void check_result(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { ++ struct userdata *u = userdata; ++ struct per_client *pc; ++ ++ pa_asyncq_read_after_poll(u->results); ++ while ((pc = pa_asyncq_pop(u->results, false)) != NULL) { ++ deliver_result(u, pc); ++ } ++ pa_asyncq_read_before_poll(u->results); +} + +int pa__init(pa_module *m) { + struct userdata *u; -+ u = pa_xnew0(struct userdata, 1); -+ u->core = m->core; + ++ u = pa_xnew0(struct userdata, 1); + m->userdata = u; -+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], -+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u); ++ u->mutex = pa_mutex_new(false, false); ++ u->cond = pa_cond_new(); ++ ++ u->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, ++ pa_idxset_trivial_compare_func, ++ NULL, (pa_free_cb_t) per_client_free); ++ ++ u->results = pa_asyncq_new(0); ++ u->io_event = m->core->mainloop->io_new( ++ m->core->mainloop, pa_asyncq_read_fd(u->results), PA_IO_EVENT_INPUT, ++ check_result, u); ++ pa_asyncq_read_before_poll(u->results); ++ ++ u->connect_hook_slot = pa_hook_connect( ++ &m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], PA_HOOK_NORMAL, ++ (pa_hook_cb_t) connect_record_hook, u); ++ ++ /* Start glib thread and wait for it to finish initialising. */ ++ pa_mutex_lock(u->mutex); ++ u->thread = pa_thread_new("snapd-glib", thread_main, u); ++ pa_cond_wait(u->cond, u->mutex); ++ pa_mutex_unlock(u->mutex); + + return 0; +} + +void pa__done(pa_module *m) { + struct userdata *u = m->userdata; -+ if (u) { -+ if (u->connect_hook_slot) -+ pa_hook_slot_free(u->connect_hook_slot); -+ pa_xfree(u); -+ } -+} -Index: pulseaudio/configure.ac -=================================================================== ---- pulseaudio.orig/configure.ac -+++ pulseaudio/configure.ac -@@ -1446,6 +1446,19 @@ AS_IF([test "x$os_is_win32" != "x1"], - AS_IF([test "x$ax_pthread_ok" = "xyes"], - AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) - -+# Snappy support ++ if (!u) return; + -+AC_ARG_ENABLE([snappy], -+ AS_HELP_STRING([--enable-snappy], [Enable snappy support])) ++ pa_hook_slot_free(u->connect_hook_slot); ++ m->core->mainloop->io_free(u->io_event); + -+AS_IF([test "x$enable_snappy" != "xno"], -+ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [HAVE_APPARMOR=1], [HAVE_APPARMOR=0])], -+ [HAVE_APPARMOR=0]) ++ /* Stop the glib thread and wait for it to exit */ ++ g_main_context_invoke(u->main_context, thread_quit, u); ++ pa_thread_join(u->thread); ++ pa_thread_free(u->thread); + -+AS_IF([test "x$enable_snappy" = "xyes" && test "x$HAVE_APPARMOR" = "x0"], -+ [AC_MSG_ERROR([*** Apparmor library not found])]) ++ pa_asyncq_free(u->results, NULL); /* items in queue owned by u->clients */ ++ g_clear_pointer(&u->clients, pa_hashmap_free); + -+AM_CONDITIONAL([HAVE_APPARMOR], [test "x$HAVE_APPARMOR" = "x1"]) - - # Ubuntu touch trust store - -@@ -1641,6 +1654,8 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x - AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no) - AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no) - AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no) -+AS_IF([test "x$enable_snappy" = "x1"], ENABLE_SNAPPY=yes, ENABLE_SNAPPY=no) -+AS_IF([test "x$HAVE_APPARMOR" = "x1"], ENABLE_APPARMOR=yes, ENABLE_APPARMOR=no) - - echo " - ---{ $PACKAGE_NAME $VERSION }--- -@@ -1698,6 +1713,8 @@ echo " - Enable soxr (resampler): ${ENABLE_SOXR} - Enable WebRTC echo canceller: ${ENABLE_WEBRTC} - Enable Ubuntu trust store: ${ENABLE_TRUST_STORE} -+ Enable Snappy support: ${ENABLE_SNAPPY} -+ Enable Apparmor: ${ENABLE_APPARMOR} - Enable gcov coverage: ${ENABLE_GCOV} - Enable unit tests: ${ENABLE_TESTS} - Database ++ g_clear_pointer(&u->cond, pa_cond_free); ++ g_clear_pointer(&u->mutex, pa_mutex_free); ++ ++ pa_xfree(u); ++} diff -Nru pulseaudio-12.2/debian/patches/0701-enable-snap-policy-module.patch pulseaudio-12.2/debian/patches/0701-enable-snap-policy-module.patch --- pulseaudio-12.2/debian/patches/0701-enable-snap-policy-module.patch 1970-01-01 08:00:00.000000000 +0800 +++ pulseaudio-12.2/debian/patches/0701-enable-snap-policy-module.patch 2018-08-07 17:01:22.000000000 +0800 @@ -0,0 +1,25 @@ +From: James Henstridge +Date: Tue, 7 Aug 2018 16:54:00 +0800 +Subject: daemon: enable module-snap-policy module + +--- + src/daemon/default.pa.in | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in +index 78c42a1..633e0f0 100755 +--- a/src/daemon/default.pa.in ++++ b/src/daemon/default.pa.in +@@ -171,6 +171,12 @@ load-module module-position-event-sounds + ### Cork music/video streams when a phone stream is active + load-module module-role-cork + ++### Block audio recording for snap confined packages unless they have ++### the "pulseaudio" or "audio-record" interfaces plugged. ++.ifexists module-snap-policy@PA_SOEXT@ ++load-module module-snap-policy ++.endif ++ + ### Modules to allow autoloading of filters (such as echo cancellation) + ### on demand. module-filter-heuristics tries to determine what filters + ### make sense, and module-filter-apply does the heavy-lifting of diff -Nru pulseaudio-12.2/debian/patches/series pulseaudio-12.2/debian/patches/series --- pulseaudio-12.2/debian/patches/series 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/patches/series 2018-08-07 17:01:22.000000000 +0800 @@ -7,13 +7,10 @@ memfd-glibc2.27.patch 0050-disable-volume-test.patch -# Ubuntu touch: support for trust-store +# Ubuntu Snap policy support 0406-tagstruct-add-copy-method.patch 0407-access-Add-access-control-hooks.patch 0408-protocol-native-add-access-checks.patch -0409-Trust-store-patch.patch -0410-Add-thread-to-activate-trust-store-interface.patch -0417-increase-timeout-check-apparmor.patch - -# Ubuntu Snappy +0409-pa-client-peer-credentials.patch 0700-modules-add-snappy-policy-module.patch +0701-enable-snap-policy-module.patch diff -Nru pulseaudio-12.2/debian/pulseaudio.install pulseaudio-12.2/debian/pulseaudio.install --- pulseaudio-12.2/debian/pulseaudio.install 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/pulseaudio.install 2018-08-07 17:01:22.000000000 +0800 @@ -78,7 +78,7 @@ usr/lib/pulse-*/modules/module-virtual-source.so usr/lib/pulse-*/modules/module-switch-on-port-available.so usr/lib/pulse-*/modules/module-virtual-surround-sink.so -usr/lib/pulse-*/modules/module-snappy-policy.so +usr/lib/pulse-*/modules/module-snap-policy.so usr/lib/pulse-*/modules/module-x11*.so usr/lib/pulse-*/modules/module-allow-passthrough.so [linux-any] usr/lib/pulse-*/modules/module-systemd-login.so diff -Nru pulseaudio-12.2/debian/rules pulseaudio-12.2/debian/rules --- pulseaudio-12.2/debian/rules 2018-08-06 13:42:13.000000000 +0800 +++ pulseaudio-12.2/debian/rules 2018-08-07 17:01:22.000000000 +0800 @@ -22,7 +22,7 @@ --with-zsh-completion-dir=\$${datadir}/zsh/vendor-completions \ --with-bash-completion-dir=\$${datadir}/bash-completion/completions \ --with-systemduserunitdir=\$${prefix}/lib/systemd/user \ - --enable-snappy \ + --enable-snap \ --disable-bluez4 \ --enable-gsettings \ --disable-gconf