Merge ~jamesh/pulseaudio:module-snap-policy into ~ubuntu-audio-dev/pulseaudio:ubuntu

Proposed by James Henstridge
Status: Merged
Merged at revision: c3261bbec962194c4e62b40874fbc0fe7f9b6a55
Proposed branch: ~jamesh/pulseaudio:module-snap-policy
Merge into: ~ubuntu-audio-dev/pulseaudio:ubuntu
Prerequisite: ~ubuntu-audio-dev/pulseaudio:ubuntu-pulse12.2-proposed
Diff against target: 1555 lines (+504/-215)
9 files modified
debian/changelog (+12/-0)
debian/control (+1/-0)
debian/patches/0409-pa-client-peer-credentials.patch (+104/-0)
debian/patches/0700-modules-add-snappy-policy-module.patch (+357/-105)
debian/patches/0701-enable-snap-policy-module.patch (+25/-0)
debian/patches/series (+3/-6)
debian/pulseaudio.install (+1/-1)
debian/rules (+1/-1)
dev/null (+0/-102)
Reviewer Review Type Date Requested Status
Ken VanDine Approve
Daniel van Vugt Needs Fixing
Review via email: mp+352558@code.launchpad.net

Commit message

Update snap policy to make access to audio recording conditional on plugging the "pulseaudio" or "audio-record" interfaces.

Description of the change

The current snap policy module cannot be enabled on desktop Ubuntu systems because it blocks audio recording to all snap confined applications, which breaks many of the snaps available on the store (e.g. Skype is not particularly useful if it can't access the microphone).

On the snapd side, the plan is to introduce new "audio-playback" and "audio-record" interfaces so we can identify apps that actually require microphone access.

On the PulseAudio side, the snap policy module needs to check what interfaces the client has plugged. We use snapd-glib to check whether either of the legacy "pulseaudio" or "audio-record" interfaces are plugged.

The implementation is complicated by the fact that snapd-glib depends on the GLib main loop, while pulse audio has its own incompatible event loop. So we instead run the GLib main loop in a separate thread and pass messages back and forward as needed.

I've also updated the default configuration file to actually enable the module too, since it should now function correctly.

To post a comment you must log in.
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) You can drop the prereq branch if it's convenient. It doesn't exist any more because 'ubuntu' is identical.

(2) I get a build failure using this branch:

modules/module-snap-policy.c: In function ‘module_snap_policy_LTX_pa__done’:
/usr/include/glib-2.0/glib/gmem.h:120:7: error: too few arguments to function ‘pa_asyncq_free’
       (destroy) (_ptr); \
       ^
modules/module-snap-policy.c:340:5: note: in expansion of macro ‘g_clear_pointer’
     g_clear_pointer(&u->results, pa_asyncq_free);
     ^~~~~~~~~~~~~~~
In file included from modules/module-snap-policy.c:31:
./pulsecore/asyncq.h:44:6: note: declared here
 void pa_asyncq_free(pa_asyncq* q, pa_free_cb_t free_cb);
      ^~~~~~~~~~~~~~

(3) You're now a member of ~ubuntu-audio-dev so no need to ask for a review next time.

review: Needs Fixing
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(4) Just make sure module-snap-policy doesn't affect deb users negatively at all.

Revision history for this message
James Henstridge (jamesh) wrote :

As discussed on IRC, I've dropped the trust-store specific patches (which haven't been built since February according to bug 1739469), keeping the patches necessary for the snap policy module to hook in.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(5) changelog needs to now mention the removal of trust store patches :)

review: Needs Fixing
Revision history for this message
Ken VanDine (ken-vandine) wrote :

jamesh had fixed the changelog, it's been uploaded and merged

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/debian/changelog b/debian/changelog
index c797d51..e68a037 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
1pulseaudio (1:12.2-0ubuntu2) UNRELEASED; urgency=medium
2
3 * Update snap policy to make access to audio recording conditional on
4 plugging the "pulseaudio" or "audio-record" interfaces (LP: #1781428):
5 - 0700-modules-add-snappy-policy-module.patch: rewrite to query
6 snapd for the client's plugged interfaces.
7 - 0701-enable-snap-policy-module.patch: enable the module in the
8 default configuration.
9 - Build depend on libsnapd-glib-dev.
10
11 -- James Henstridge <james.henstridge@canonical.com> Tue, 07 Aug 2018 17:01:22 +0800
12
1pulseaudio (1:12.2-0ubuntu1) cosmic; urgency=medium13pulseaudio (1:12.2-0ubuntu1) cosmic; urgency=medium
214
3 * New upstream release 12.2 (LP: #1785551):15 * New upstream release 12.2 (LP: #1785551):
diff --git a/debian/control b/debian/control
index 3b2a1f4..e724169 100644
--- a/debian/control
+++ b/debian/control
@@ -33,6 +33,7 @@ Build-Depends: debhelper (>= 10),
33 libltdl-dev,33 libltdl-dev,
34 liborc-0.4-dev,34 liborc-0.4-dev,
35 libsamplerate0-dev,35 libsamplerate0-dev,
36 libsnapd-glib-dev,
36 libsndfile1-dev,37 libsndfile1-dev,
37 libsoxr-dev (>= 0.1.1),38 libsoxr-dev (>= 0.1.1),
38 libspeexdsp-dev (>= 1.2~rc1),39 libspeexdsp-dev (>= 1.2~rc1),
diff --git a/debian/patches/0409-Trust-store-patch.patch b/debian/patches/0409-Trust-store-patch.patch
39deleted file mode 10064440deleted file mode 100644
index 1607c62..0000000
--- a/debian/patches/0409-Trust-store-patch.patch
+++ /dev/null
@@ -1,572 +0,0 @@
1From ff36148e002232842ea0f07b148e5844c5d44ceb Mon Sep 17 00:00:00 2001
2From: David Henningsson <david.henningsson@canonical.com>
3Date: Wed, 22 Jul 2015 16:37:19 +0200
4Subject: [PATCH 4/5] Trust-store patch
5
6...includes some fixes elsewhere as well.
7
8Signed-off-by: David Henningsson <david.henningsson@canonical.com>
9---
10 configure.ac | 15 ++
11 src/Makefile.am | 24 +++
12 src/modules/trust-store/module-trust-store.c | 221 +++++++++++++++++++++++++++
13 src/modules/trust-store/truststore.cc | 74 +++++++++
14 src/modules/trust-store/truststore.h | 41 +++++
15 src/pulsecore/client.h | 4 +
16 src/pulsecore/creds.h | 1 +
17 src/pulsecore/iochannel.c | 2 +
18 src/pulsecore/protocol-native.c | 13 ++
19 src/pulsecore/tagstruct.c | 1 +
20 12 files changed, 399 insertions(+), 7 deletions(-)
21 create mode 100644 src/modules/trust-store/module-trust-store.c
22 create mode 100644 src/modules/trust-store/truststore.cc
23 create mode 100644 src/modules/trust-store/truststore.h
24
25Index: pulseaudio/configure.ac
26===================================================================
27--- pulseaudio.orig/configure.ac
28+++ pulseaudio/configure.ac
29@@ -1447,6 +1447,19 @@ AS_IF([test "x$ax_pthread_ok" = "xyes"],
30 AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
31
32
33+# Ubuntu touch trust store
34+
35+AC_ARG_ENABLE([trust-store],
36+ AS_HELP_STRING([--enable-trust-store], [Enable Ubuntu touch trust store]))
37+
38+AS_IF([test "x$enable_trust_store" != "xno"],
39+ [PKG_CHECK_MODULES(TRUST_STORE, [ trust-store ], [HAVE_TRUST_STORE=1], [HAVE_TRUST_STORE=0])],
40+ [HAVE_WEBRTC=0])
41+
42+AS_IF([test "x$enable_trust_store" = "xyes" && test "x$HAVE_TRUST_STORE" = "x0"],
43+ [AC_MSG_ERROR([*** Ubuntu touch trust store library not found])])
44+
45+AM_CONDITIONAL([HAVE_TRUST_STORE], [test "x$HAVE_TRUST_STORE" = "x1"])
46
47 ###################################
48 # Output #
49@@ -1619,6 +1632,7 @@ AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"],
50 AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no)
51 AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no)
52 AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no)
53+AS_IF([test "x$HAVE_TRUST_STORE" = "x1"], ENABLE_TRUST_STORE=yes, ENABLE_TRUST_STORE=no)
54 AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no)
55 AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no)
56 AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no)
57@@ -1683,6 +1697,7 @@ echo "
58 Enable speex (resampler, AEC): ${ENABLE_SPEEX}
59 Enable soxr (resampler): ${ENABLE_SOXR}
60 Enable WebRTC echo canceller: ${ENABLE_WEBRTC}
61+ Enable Ubuntu trust store: ${ENABLE_TRUST_STORE}
62 Enable gcov coverage: ${ENABLE_GCOV}
63 Enable unit tests: ${ENABLE_TESTS}
64 Database
65Index: pulseaudio/src/Makefile.am
66===================================================================
67--- pulseaudio.orig/src/Makefile.am
68+++ pulseaudio/src/Makefile.am
69@@ -1105,6 +1105,10 @@ if HAVE_WEBRTC
70 modlibexec_LTLIBRARIES += libwebrtc-util.la
71 endif
72
73+if HAVE_TRUST_STORE
74+modlibexec_LTLIBRARIES += libtruststore-util.la
75+endif
76+
77 if HAVE_ESOUND
78 modlibexec_LTLIBRARIES += \
79 libprotocol-esound.la
80@@ -1235,6 +1239,11 @@ modlibexec_LTLIBRARIES += \
81 module-role-ducking.la \
82 module-allow-passthrough.la
83
84+if HAVE_TRUST_STORE
85+modlibexec_LTLIBRARIES += \
86+ module-trust-store.la
87+endif
88+
89 if HAVE_ESOUND
90 modlibexec_LTLIBRARIES += \
91 module-esound-protocol-tcp.la \
92@@ -2025,6 +2034,20 @@ module_echo_cancel_la_CFLAGS += -DHAVE_W
93 module_echo_cancel_la_LIBADD += libwebrtc-util.la
94 endif
95
96+if HAVE_TRUST_STORE
97+# Separate helper library to keep C and C++ code apart, like we do for webrtc
98+libtruststore_util_la_SOURCES = modules/trust-store/truststore.cc
99+libtruststore_util_la_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11 $(SERVER_CFLAGS) $(TRUST_STORE_CFLAGS) \
100+ $(DBUS_CFLAGS) -DHAVE_TRUST_STORE=1
101+libtruststore_util_la_LIBADD = libpulsecore-@PA_MAJORMINOR@.la $(TRUST_STORE_LIBS) $(DBUS_LIBS)
102+libtruststore_util_la_LDFLAGS = -avoid-version
103+
104+module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c
105+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS)
106+module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la
107+module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1
108+endif
109+
110 # RTP modules
111 module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
112 module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS)
113Index: pulseaudio/src/modules/trust-store/module-trust-store.c
114===================================================================
115--- /dev/null
116+++ pulseaudio/src/modules/trust-store/module-trust-store.c
117@@ -0,0 +1,226 @@
118+/***
119+ This file is part of PulseAudio.
120+
121+ Copyright 2015 Canonical Ltd.
122+ Written by David Henningsson <david.henningsson@canonical.com>
123+
124+ PulseAudio is free software; you can redistribute it and/or modify
125+ it under the terms of the GNU Lesser General Public License as published
126+ by the Free Software Foundation; either version 2.1 of the License,
127+ or (at your option) any later version.
128+
129+ PulseAudio is distributed in the hope that it will be useful, but
130+ WITHOUT ANY WARRANTY; without even the implied warranty of
131+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
132+ General Public License for more details.
133+
134+ You should have received a copy of the GNU Lesser General Public License
135+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
136+***/
137+#ifdef HAVE_CONFIG_H
138+#include <config.h>
139+#endif
140+
141+#include <pulsecore/i18n.h>
142+#include <pulsecore/core.h>
143+#include <pulsecore/module.h>
144+#include <pulse/xmalloc.h>
145+#include <pulsecore/fdsem.h>
146+#include <pulsecore/thread.h>
147+#include <pulsecore/core-util.h>
148+#include <pulse/mainloop-api.h>
149+
150+PA_MODULE_AUTHOR("David Henningsson");
151+PA_MODULE_DESCRIPTION("Ubuntu touch trust store integration");
152+PA_MODULE_VERSION(PACKAGE_VERSION);
153+PA_MODULE_LOAD_ONCE(true);
154+
155+#include "truststore.h"
156+
157+#define REQUEST_GRANTED 1
158+#define REQUEST_DENIED -1
159+#define REQUEST_PENDING 0
160+
161+struct userdata;
162+
163+struct per_client {
164+ uint32_t index;
165+ char *appname;
166+ uid_t uid;
167+ pid_t pid;
168+ struct userdata *userdata;
169+ pa_dynarray *pending_requests;
170+ pa_atomic_t result;
171+};
172+
173+struct userdata {
174+ pa_core *core;
175+ pa_trust_store *ts;
176+ pa_hashmap *per_clients;
177+ pa_thread *thread;
178+ pa_fdsem *fdsem;
179+ pa_io_event *io_event;
180+ pa_hook_slot *connect_hook_slot;
181+};
182+
183+static struct per_client *per_client_new(struct userdata *u, uint32_t client_index) {
184+ struct per_client *pc;
185+ pa_client *client = pa_idxset_get_by_index(u->core->clients, client_index);
186+
187+ pa_assert(client);
188+ if (!client->creds_valid) {
189+ pa_log_warn("Client %d has no creds, cannot authenticate", client_index);
190+ return NULL;
191+ }
192+
193+ pc = pa_xnew0(struct per_client, 1);
194+ // TODO: Do we need something else than the application name here -
195+ // the application can probably spoof it
196+ pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME));
197+ pc->index = client_index;
198+ pc->uid = client->creds.uid;
199+ pc->pid = client->creds.pid;
200+ pc->pending_requests = pa_dynarray_new(NULL);
201+ pc->userdata = u;
202+
203+ pa_hashmap_put(u->per_clients, (void *) (size_t) client_index, pc);
204+
205+ return pc;
206+}
207+
208+static void per_client_free_from_hashmap(void *data) {
209+ struct per_client *pc = data;
210+ if (!pc)
211+ return;
212+ pa_xfree(pc->appname);
213+ pa_dynarray_free(pc->pending_requests);
214+ pa_xfree(pc);
215+}
216+
217+static void thread_func(void *data) {
218+ struct per_client *pc = data;
219+
220+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid,
221+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase
222+ /// can't be translated in this language, translate the whole sentence
223+ /// 'This app wants to record audio.'
224+ _("wants to record audio."));
225+
226+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED);
227+ pa_fdsem_post(pc->userdata->fdsem);
228+}
229+
230+static void check_queue(struct userdata *u) {
231+ struct per_client *pc;
232+ void *dummy;
233+
234+ pa_log_debug("Checking queue, size: %d, thread object: %s",
235+ pa_hashmap_size(u->per_clients), pa_yes_no(u->thread));
236+ if (u->thread) {
237+ pa_access_data *ad;
238+ unsigned i;
239+ int result;
240+ pc = pa_thread_get_data(u->thread);
241+ result = pa_atomic_load(&pc->result);
242+ if (result == REQUEST_PENDING)
243+ return; /* Still processing */
244+ pa_thread_free(u->thread);
245+ u->thread = NULL;
246+
247+ pa_log_debug("Letting client %d (%s) know the result (%s)",
248+ pc->index, pc->appname, result == REQUEST_GRANTED ? "granted" : "rejected");
249+ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) {
250+ ad->async_finish_cb(ad, result == REQUEST_GRANTED);
251+ }
252+ pa_hashmap_remove(u->per_clients, (void*) (size_t) pc->index);
253+ }
254+
255+ /* Find a client that needs asking */
256+ PA_HASHMAP_FOREACH(pc, u->per_clients, dummy) {
257+ if (u->thread)
258+ return;
259+ pa_assert(pa_atomic_load(&pc->result) == REQUEST_PENDING);
260+ pa_log_debug("Starting thread to ask about client %d (%s)", pc->index,
261+ pc->appname);
262+ u->thread = pa_thread_new("trust-store", thread_func, pc);
263+ }
264+}
265+
266+static void check_fdsem(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t ee,
267+ void *userdata) {
268+ struct userdata *u = userdata;
269+ pa_fdsem_after_poll(u->fdsem);
270+ check_queue(u);
271+ pa_fdsem_before_poll(u->fdsem);
272+}
273+
274+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) {
275+ struct per_client *pc = pa_hashmap_get(u->per_clients, (void*) (size_t) d->client_index);
276+ int r;
277+ if (!pc)
278+ pc = per_client_new(u, d->client_index);
279+
280+ r = pa_atomic_load(&pc->result);
281+ pa_log_debug("Checking permission to record for client %d (%s) ", d->client_index,
282+ r == REQUEST_GRANTED ? "granted" : r != REQUEST_PENDING ? "rejected" : "unknown");
283+ if (r == REQUEST_GRANTED) return PA_HOOK_OK;
284+ if (r != REQUEST_PENDING) return PA_HOOK_STOP;
285+
286+ pa_dynarray_append(pc->pending_requests, d);
287+ check_queue(u);
288+ return PA_HOOK_CANCEL;
289+}
290+
291+/* Test code - remove from final product */
292+static void test(struct userdata *u) {
293+ uint32_t dummy;
294+ pa_client *client;
295+
296+ PA_IDXSET_FOREACH(client, u->core->clients, dummy) {
297+ if (client->creds_valid)
298+ pa_trust_store_check(u->ts, "The evil app", client->creds.uid,
299+ client->creds.pid, "%1% wants to eat your laundry.");
300+ }
301+}
302+
303+int pa__init(pa_module *m) {
304+ struct userdata *u;
305+ pa_trust_store *ts = pa_trust_store_new();
306+ if (ts == NULL)
307+ return -1;
308+ u = pa_xnew0(struct userdata, 1);
309+ u->ts = ts;
310+ u->core = m->core;
311+ u->per_clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func,
312+ NULL, per_client_free_from_hashmap);
313+ m->userdata = u;
314+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD],
315+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u);
316+
317+ pa_assert_se(u->fdsem = pa_fdsem_new());
318+ pa_assert_se(u->io_event = m->core->mainloop->io_new(m->core->mainloop,
319+ pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u));
320+ pa_fdsem_before_poll(u->fdsem);
321+
322+ test(u);
323+ return 0;
324+}
325+
326+void pa__done(pa_module *m) {
327+ struct userdata *u = m->userdata;
328+ if (u) {
329+ if (u->connect_hook_slot)
330+ pa_hook_slot_free(u->connect_hook_slot);
331+ if (u->thread)
332+ pa_thread_free(u->thread);
333+ if (u->io_event)
334+ m->core->mainloop->io_free(u->io_event);
335+ if (u->fdsem)
336+ pa_fdsem_free(u->fdsem);
337+ if (u->per_clients)
338+ pa_hashmap_free(u->per_clients);
339+ if (u->ts)
340+ pa_trust_store_free(u->ts);
341+ pa_xfree(u);
342+ }
343+}
344Index: pulseaudio/src/modules/trust-store/truststore.cc
345===================================================================
346--- /dev/null
347+++ pulseaudio/src/modules/trust-store/truststore.cc
348@@ -0,0 +1,74 @@
349+#ifdef HAVE_CONFIG_H
350+#include <config.h>
351+#endif
352+
353+#include <memory>
354+#include <core/dbus/bus.h>
355+#include <core/trust/dbus_agent.h>
356+#include <core/trust/agent.h>
357+
358+#include <pulse/cdecl.h>
359+
360+PA_C_DECL_BEGIN
361+#include <pulsecore/core-util.h>
362+#include <pulse/xmalloc.h>
363+#include <pulsecore/log.h>
364+
365+#include "truststore.h"
366+PA_C_DECL_END
367+
368+class TrustStore {
369+public:
370+ std::shared_ptr<core::trust::Agent> agent;
371+};
372+
373+pa_trust_store* pa_trust_store_new() {
374+ try {
375+ auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
376+ auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio");
377+ auto ts = new TrustStore();
378+ ts->agent = agent;
379+ return (pa_trust_store *) ts;
380+ } catch(const std::exception &e) {
381+ pa_log_error("Could not create Ubuntu touch trust store connection: %s",
382+ e.what());
383+ } catch(...) {
384+ pa_log_error("Could not create Ubuntu touch trust store connection");
385+ return NULL;
386+ }
387+}
388+
389+void pa_trust_store_free(pa_trust_store *t) {
390+ pa_assert(t != NULL);
391+ auto ts = (TrustStore*) t;
392+ delete ts;
393+}
394+
395+bool pa_trust_store_check(pa_trust_store *t, const char *appname,
396+ uid_t uid, pid_t pid, const char *description) {
397+ auto ts = (TrustStore*) t;
398+ try {
399+
400+ core::trust::Agent::RequestParameters params {
401+ core::trust::Uid{uid},
402+ core::trust::Pid{pid},
403+ appname,
404+ core::trust::Feature{0},
405+ description
406+ };
407+ pa_log_debug("Asking Ubuntu touch trust store for permission (app: %s)",
408+ params.application.id.c_str());
409+ auto answer = ts->agent->authenticate_request_with_parameters(params);
410+ if (answer == core::trust::Request::Answer::granted) {
411+ pa_log_debug("Request granted.");
412+ return true;
413+ }
414+ pa_log_info("Request denied.");
415+ } catch(const std::exception &e) {
416+ pa_log_error("Could not ask Ubuntu touch trust store for permission: %s",
417+ e.what());
418+ } catch(...) {
419+ pa_log_error("Could not ask Ubuntu touch trust store for permission");
420+ }
421+ return false;
422+}
423Index: pulseaudio/src/modules/trust-store/truststore.h
424===================================================================
425--- /dev/null
426+++ pulseaudio/src/modules/trust-store/truststore.h
427@@ -0,0 +1,41 @@
428+/***
429+ This file is part of PulseAudio.
430+
431+ Copyright 2015 Canonical Ltd.
432+ Written by David Henningsson <david.henningsson@canonical.com>
433+
434+ PulseAudio is free software; you can redistribute it and/or modify
435+ it under the terms of the GNU Lesser General Public License as published
436+ by the Free Software Foundation; either version 2.1 of the License,
437+ or (at your option) any later version.
438+
439+ PulseAudio is distributed in the hope that it will be useful, but
440+ WITHOUT ANY WARRANTY; without even the implied warranty of
441+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
442+ General Public License for more details.
443+
444+ You should have received a copy of the GNU Lesser General Public License
445+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
446+***/
447+
448+#ifndef footruststorehfoo
449+#define footruststorehfoo
450+
451+#ifdef HAVE_CONFIG_H
452+#include <config.h>
453+#endif
454+
455+#include <pulsecore/creds.h>
456+
457+#ifdef HAVE_TRUST_STORE
458+PA_C_DECL_BEGIN
459+typedef struct pa_trust_store pa_trust_store;
460+
461+pa_trust_store* pa_trust_store_new();
462+void pa_trust_store_free(pa_trust_store *ts);
463+bool pa_trust_store_check(pa_trust_store *ts, const char *appname,
464+ uid_t uid, pid_t pid, const char *description);
465+PA_C_DECL_END
466+#endif
467+
468+#endif
469Index: pulseaudio/src/pulsecore/client.h
470===================================================================
471--- pulseaudio.orig/src/pulsecore/client.h
472+++ pulseaudio/src/pulsecore/client.h
473@@ -26,6 +26,7 @@
474 #include <pulse/proplist.h>
475 #include <pulsecore/core.h>
476 #include <pulsecore/module.h>
477+#include <pulsecore/creds.h>
478
479 /* Every connection to the server should have a pa_client
480 * attached. That way the user may generate a listing of all connected
481@@ -35,6 +36,9 @@ struct pa_client {
482 uint32_t index;
483 pa_core *core;
484
485+ pa_creds creds;
486+ bool creds_valid;
487+
488 pa_proplist *proplist;
489 pa_module *module;
490 char *driver;
491Index: pulseaudio/src/pulsecore/creds.h
492===================================================================
493--- pulseaudio.orig/src/pulsecore/creds.h
494+++ pulseaudio/src/pulsecore/creds.h
495@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cms
496 struct pa_creds {
497 gid_t gid;
498 uid_t uid;
499+ pid_t pid;
500 };
501
502 /* Struct for handling ancillary data, i e, extra data that can be sent together with a message
503Index: pulseaudio/src/pulsecore/iochannel.c
504===================================================================
505--- pulseaudio.orig/src/pulsecore/iochannel.c
506+++ pulseaudio/src/pulsecore/iochannel.c
507@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa
508
509 u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
510
511+ /* FIXME: Check whether ucred->pid should be used */
512 u->pid = getpid();
513 if (ucred) {
514 u->uid = ucred->uid;
515@@ -445,6 +446,7 @@ ssize_t pa_iochannel_read_with_ancil_dat
516
517 ancil_data->creds.gid = u.gid;
518 ancil_data->creds.uid = u.uid;
519+ ancil_data->creds.pid = u.pid;
520 ancil_data->creds_valid = true;
521 }
522 else if (cmh->cmsg_type == SCM_RIGHTS) {
523Index: pulseaudio/src/pulsecore/protocol-native.c
524===================================================================
525--- pulseaudio.orig/src/pulsecore/protocol-native.c
526+++ pulseaudio/src/pulsecore/protocol-native.c
527@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *p
528 do_shm = false;
529
530 #ifdef HAVE_CREDS
531+ {
532+ const pa_creds *creds;
533+ if ((creds = pa_pdispatch_creds(pd))) {
534+ c->client->creds = *creds;
535+ c->client->creds_valid = true;
536+ }
537+ }
538 if (do_shm) {
539 /* Only enable SHM if both sides are owned by the same
540 * user. This is a security measure because otherwise data
541@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access
542 } pa_protocol_native_access_data;
543
544 static void check_access_finish_cb(pa_access_data *data, bool res) {
545+ uint32_t command, tag;
546 pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data;
547 pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata);
548
549@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_ac
550 goto finish;
551 }
552
553+ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0);
554+ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0);
555+ pa_assert(command == d->command);
556+ pa_assert(tag == d->tag);
557+
558 /* call the dispatcher again, hopefully this time, the access check will
559 * fail or succeed immediately */
560 command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata);
561Index: pulseaudio/po/POTFILES.in
562===================================================================
563--- pulseaudio.orig/po/POTFILES.in
564+++ pulseaudio/po/POTFILES.in
565@@ -23,6 +23,7 @@ src/modules/jack/module-jack-sink.c
566 src/modules/jack/module-jack-source.c
567 src/modules/macosx/module-coreaudio-device.c
568 src/modules/module-allow-passthrough.c
569+src/modules/trust-store/module-trust-store.c
570 src/modules/module-always-sink.c
571 src/modules/module-always-source.c
572 src/modules/module-cli.c
diff --git a/debian/patches/0409-pa-client-peer-credentials.patch b/debian/patches/0409-pa-client-peer-credentials.patch
573new file mode 1006440new file mode 100644
index 0000000..d6d7ff6
--- /dev/null
+++ b/debian/patches/0409-pa-client-peer-credentials.patch
@@ -0,0 +1,104 @@
1From: David Henningsson <david.henningsson@canonical.com>
2Date: Wed, 22 Jul 2015 16:37:19 +0200
3Subject: [PATCH 4/5] Expose peer credentials on pa_client
4
5Signed-off-by: David Henningsson <david.henningsson@canonical.com>
6---
7 src/pulsecore/client.h | 4 ++++
8 src/pulsecore/creds.h | 1 +
9 src/pulsecore/iochannel.c | 2 ++
10 src/pulsecore/protocol-native.c | 13 +++++++++++++
11 4 files changed, 20 insertions(+)
12
13diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h
14index eb8173d..a038a0c 100644
15--- a/src/pulsecore/client.h
16+++ b/src/pulsecore/client.h
17@@ -26,6 +26,7 @@
18 #include <pulse/proplist.h>
19 #include <pulsecore/core.h>
20 #include <pulsecore/module.h>
21+#include <pulsecore/creds.h>
22
23 /* Every connection to the server should have a pa_client
24 * attached. That way the user may generate a listing of all connected
25@@ -35,6 +36,9 @@ struct pa_client {
26 uint32_t index;
27 pa_core *core;
28
29+ pa_creds creds;
30+ bool creds_valid;
31+
32 pa_proplist *proplist;
33 pa_module *module;
34 char *driver;
35diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h
36index 9fdbb4f..8bcf636 100644
37--- a/src/pulsecore/creds.h
38+++ b/src/pulsecore/creds.h
39@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cmsg_ancil_data;
40 struct pa_creds {
41 gid_t gid;
42 uid_t uid;
43+ pid_t pid;
44 };
45
46 /* Struct for handling ancillary data, i e, extra data that can be sent together with a message
47diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c
48index e25824b..3c83092 100644
49--- a/src/pulsecore/iochannel.c
50+++ b/src/pulsecore/iochannel.c
51@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l
52
53 u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
54
55+ /* FIXME: Check whether ucred->pid should be used */
56 u->pid = getpid();
57 if (ucred) {
58 u->uid = ucred->uid;
59@@ -445,6 +446,7 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l,
60
61 ancil_data->creds.gid = u.gid;
62 ancil_data->creds.uid = u.uid;
63+ ancil_data->creds.pid = u.pid;
64 ancil_data->creds_valid = true;
65 }
66 else if (cmh->cmsg_type == SCM_RIGHTS) {
67diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
68index f7dc338..baaa952 100644
69--- a/src/pulsecore/protocol-native.c
70+++ b/src/pulsecore/protocol-native.c
71@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta
72 do_shm = false;
73
74 #ifdef HAVE_CREDS
75+ {
76+ const pa_creds *creds;
77+ if ((creds = pa_pdispatch_creds(pd))) {
78+ c->client->creds = *creds;
79+ c->client->creds_valid = true;
80+ }
81+ }
82 if (do_shm) {
83 /* Only enable SHM if both sides are owned by the same
84 * user. This is a security measure because otherwise data
85@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access_data {
86 } pa_protocol_native_access_data;
87
88 static void check_access_finish_cb(pa_access_data *data, bool res) {
89+ uint32_t command, tag;
90 pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data;
91 pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata);
92
93@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_access_data *data, bool res) {
94 goto finish;
95 }
96
97+ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0);
98+ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0);
99+ pa_assert(command == d->command);
100+ pa_assert(tag == d->tag);
101+
102 /* call the dispatcher again, hopefully this time, the access check will
103 * fail or succeed immediately */
104 command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata);
diff --git a/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch b/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch
0deleted file mode 100644105deleted file mode 100644
index 721ce11..0000000
--- a/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch
+++ /dev/null
@@ -1,108 +0,0 @@
1From 90dbf1d25e12b9cfc86aecbd02f14a05fd5dca8c Mon Sep 17 00:00:00 2001
2From: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com>
3Date: Fri, 7 Aug 2015 09:08:02 +0200
4Subject: [PATCH] Add thread to activate trust-store interface
5
6---
7 src/modules/trust-store/module-trust-store.c | 14 +-------------
8 src/modules/trust-store/truststore.cc | 19 +++++++++++++++++++
9 2 files changed, 20 insertions(+), 13 deletions(-)
10
11Index: pulseaudio/src/modules/trust-store/module-trust-store.c
12===================================================================
13--- pulseaudio.orig/src/modules/trust-store/module-trust-store.c
14+++ pulseaudio/src/modules/trust-store/module-trust-store.c
15@@ -28,6 +28,7 @@
16 #include <pulsecore/fdsem.h>
17 #include <pulsecore/thread.h>
18 #include <pulsecore/core-util.h>
19+#include <pulsecore/dynarray.h>
20 #include <pulse/mainloop-api.h>
21
22 PA_MODULE_AUTHOR("David Henningsson");
23@@ -171,18 +172,6 @@ static pa_hook_result_t connect_record_h
24 return PA_HOOK_CANCEL;
25 }
26
27-/* Test code - remove from final product */
28-static void test(struct userdata *u) {
29- uint32_t dummy;
30- pa_client *client;
31-
32- PA_IDXSET_FOREACH(client, u->core->clients, dummy) {
33- if (client->creds_valid)
34- pa_trust_store_check(u->ts, "The evil app", client->creds.uid,
35- client->creds.pid, "%1% wants to eat your laundry.");
36- }
37-}
38-
39 int pa__init(pa_module *m) {
40 struct userdata *u;
41 pa_trust_store *ts = pa_trust_store_new();
42@@ -202,7 +191,6 @@ int pa__init(pa_module *m) {
43 pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u));
44 pa_fdsem_before_poll(u->fdsem);
45
46- test(u);
47 return 0;
48 }
49
50Index: pulseaudio/src/modules/trust-store/truststore.cc
51===================================================================
52--- pulseaudio.orig/src/modules/trust-store/truststore.cc
53+++ pulseaudio/src/modules/trust-store/truststore.cc
54@@ -4,6 +4,7 @@
55
56 #include <memory>
57 #include <core/dbus/bus.h>
58+#include <core/dbus/asio/executor.h>
59 #include <core/trust/dbus_agent.h>
60 #include <core/trust/agent.h>
61
62@@ -13,6 +14,7 @@ PA_C_DECL_BEGIN
63 #include <pulsecore/core-util.h>
64 #include <pulse/xmalloc.h>
65 #include <pulsecore/log.h>
66+#include <pulsecore/thread.h>
67
68 #include "truststore.h"
69 PA_C_DECL_END
70@@ -20,14 +22,27 @@ PA_C_DECL_END
71 class TrustStore {
72 public:
73 std::shared_ptr<core::trust::Agent> agent;
74+ std::shared_ptr<core::dbus::Bus> bus;
75+ pa_thread *thread;
76 };
77
78+static void thread_func(void *data) {
79+ class TrustStore *ts = (class TrustStore *) data;
80+
81+ ts->bus->run();
82+}
83+
84 pa_trust_store* pa_trust_store_new() {
85 try {
86 auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
87+ bus->install_executor(core::dbus::asio::make_executor(bus));
88+
89 auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio");
90 auto ts = new TrustStore();
91 ts->agent = agent;
92+ ts->bus = bus;
93+ ts->thread = pa_thread_new("trust-store-bus", thread_func, ts);
94+
95 return (pa_trust_store *) ts;
96 } catch(const std::exception &e) {
97 pa_log_error("Could not create Ubuntu touch trust store connection: %s",
98@@ -41,6 +56,10 @@ pa_trust_store* pa_trust_store_new() {
99 void pa_trust_store_free(pa_trust_store *t) {
100 pa_assert(t != NULL);
101 auto ts = (TrustStore*) t;
102+ if (ts->thread) {
103+ ts->bus->stop();
104+ pa_thread_free(ts->thread);
105+ }
106 delete ts;
107 }
108
diff --git a/debian/patches/0417-increase-timeout-check-apparmor.patch b/debian/patches/0417-increase-timeout-check-apparmor.patch
109deleted file mode 1006440deleted file mode 100644
index 2c746f7..0000000
--- a/debian/patches/0417-increase-timeout-check-apparmor.patch
+++ /dev/null
@@ -1,102 +0,0 @@
1Index: pulseaudio/src/modules/trust-store/module-trust-store.c
2===================================================================
3--- pulseaudio.orig/src/modules/trust-store/module-trust-store.c
4+++ pulseaudio/src/modules/trust-store/module-trust-store.c
5@@ -31,6 +31,8 @@
6 #include <pulsecore/dynarray.h>
7 #include <pulse/mainloop-api.h>
8
9+#include <sys/apparmor.h>
10+
11 PA_MODULE_AUTHOR("David Henningsson");
12 PA_MODULE_DESCRIPTION("Ubuntu touch trust store integration");
13 PA_MODULE_VERSION(PACKAGE_VERSION);
14@@ -75,9 +77,37 @@ static struct per_client *per_client_new
15 }
16
17 pc = pa_xnew0(struct per_client, 1);
18- // TODO: Do we need something else than the application name here -
19- // the application can probably spoof it
20- pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME));
21+
22+ pa_log_info("Client application name is: '%s'", pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME));
23+
24+ // ask apparmor about the context of the client, later this will be used to decide if the
25+ // app should be or not using the trust store
26+
27+ if (aa_is_enabled()) {
28+ char *context = NULL;
29+ char *mode = NULL;
30+
31+ /* XXX: The mode string is *NOT* be freed since it forms
32+ * part of the allocation returned in context.
33+ *
34+ * See aa_gettaskcon(2) for details.
35+ */
36+ if (aa_gettaskcon (client->creds.pid, &context, &mode) >= 0) {
37+ pc->appname = pa_xstrdup(context);
38+ pa_log_info("Client apparmor profile is: '%s'", context);
39+ } else {
40+ pa_log_error("AppArmo profile could not be retrieved.");
41+ }
42+
43+ if (context)
44+ free(context);
45+
46+ } else {
47+ // while we do leave the appname as empty if we fail, if apparmor is not present we should
48+ // assume that the app is not confined
49+ pc->appname = pa_xstrdup("unconfined");
50+ }
51+
52 pc->index = client_index;
53 pc->uid = client->creds.uid;
54 pc->pid = client->creds.pid;
55@@ -101,13 +131,28 @@ static void per_client_free_from_hashmap
56 static void thread_func(void *data) {
57 struct per_client *pc = data;
58
59- bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid,
60- /// TRANSLATORS: The app icon and name appears above this string. If the phrase
61- /// can't be translated in this language, translate the whole sentence
62- /// 'This app wants to record audio.'
63- _("wants to record audio."));
64+ // there are 3 possible values for the app name that we will consider at this point
65+ // * empty string: there was an error when retrieving the value and therefore we will
66+ // automatically deny access
67+ // * unconfined: the app is unconfined and therefore we will automatically grant access
68+ // * appname: we need the user to decide what to do.
69+
70+ if (pc->appname == NULL) {
71+ pa_log_info("Client apparmor could not retrieved.");
72+ pa_atomic_store(&pc->result, REQUEST_DENIED);
73+ } else if (pa_streq(pc->appname, "unconfined")) {
74+ pa_log_info("Connected client is unconfined.");
75+ pa_atomic_store(&pc->result, REQUEST_GRANTED);
76+ } else {
77+ pa_log_info("User needs to authorize the application..");
78+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid,
79+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase
80+ /// can't be translated in this language, translate the whole sentence
81+ /// 'This app wants to record audio.'
82+ _("wants to record audio."));
83+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED);
84+ }
85
86- pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED);
87 pa_fdsem_post(pc->userdata->fdsem);
88 }
89
90Index: pulseaudio/src/Makefile.am
91===================================================================
92--- pulseaudio.orig/src/Makefile.am
93+++ pulseaudio/src/Makefile.am
94@@ -2043,7 +2043,7 @@ libtruststore_util_la_LIBADD = libpulsec
95 libtruststore_util_la_LDFLAGS = -avoid-version
96
97 module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c
98-module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS)
99+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor
100 module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la
101 module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1
102 endif
diff --git a/debian/patches/0700-modules-add-snappy-policy-module.patch b/debian/patches/0700-modules-add-snappy-policy-module.patch
index 2425ec7..6ac2da7 100644
--- a/debian/patches/0700-modules-add-snappy-policy-module.patch
+++ b/debian/patches/0700-modules-add-snappy-policy-module.patch
@@ -1,56 +1,101 @@
1From a430ebc2271f5a07389ee25631a8ba5524371764 Mon Sep 17 00:00:00 20011From: James Henstridge <james.henstridge@canonical.com>
2From: Simon Fels <simon.fels@canonical.com>2Date: Tue, 7 Aug 2018 12:40:59 +0800
3Date: Tue, 17 May 2016 17:29:31 +02003Subject: [PATCH] modules: add snap policy module
4Subject: [PATCH] modules: add snappy policy module
54
5Co-authored-by: Simon Fels <simon.fels@canonical.com>
6---6---
7 configure.ac | 18 +++++++-7 configure.ac | 17 ++
8 src/Makefile.am | 18 ++++++++8 src/Makefile.am | 13 ++
9 src/modules/module-snappy-policy.c | 94 ++++++++++++++++++++++++++++++++++++++9 src/modules/module-snap-policy.c | 347 +++++++++++++++++++++++++++++++++++++++
10 3 files changed, 129 insertions(+), 1 deletion(-)10 3 files changed, 377 insertions(+)
11 create mode 100644 src/modules/module-snappy-policy.c11 create mode 100644 src/modules/module-snap-policy.c
1212
13Index: pulseaudio/src/Makefile.am13diff --git a/configure.ac b/configure.ac
14===================================================================14index c9c414f..97c2d9d 100644
15--- pulseaudio.orig/src/Makefile.am15--- a/configure.ac
16+++ pulseaudio/src/Makefile.am16+++ b/configure.ac
17@@ -1250,6 +1250,11 @@ modlibexec_LTLIBRARIES += \17@@ -1446,6 +1446,21 @@ AS_IF([test "x$os_is_win32" != "x1"],
18 AS_IF([test "x$ax_pthread_ok" = "xyes"],
19 AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
20
21+# Snappy support
22+
23+AC_ARG_ENABLE([snap],
24+ AS_HELP_STRING([--enable-snap], [Enable snap support]))
25+
26+have_apparmor=0
27+have_snapd_glib=0
28+AS_IF([test "x$enable_snap" != "xno"],
29+ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [have_apparmor=1])
30+ PKG_CHECK_MODULES(SNAPD_GLIB, [snapd-glib], [have_snapd_glib=1])])
31+
32+AS_IF([test "x$enable_snap" = "xyes" && test "x$have_apparmor" = "x0" -o "x$have-snapd_glib" = "x0"],
33+ [AC_MSG_ERROR([*** Snap module dependencies missing])])
34+
35+AM_CONDITIONAL([BUILD_SNAP], [test "x$enable_snap" = "xyes"])
36
37
38 ###################################
39@@ -1627,6 +1642,7 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], EN
40 AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no)
41 AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no)
42 AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no)
43+AS_IF([test "x$enable_snap" != "xno"], ENABLE_SNAP=yes, ENABLE_SNAP=no)
44
45 echo "
46 ---{ $PACKAGE_NAME $VERSION }---
47@@ -1683,6 +1699,7 @@ echo "
48 Enable speex (resampler, AEC): ${ENABLE_SPEEX}
49 Enable soxr (resampler): ${ENABLE_SOXR}
50 Enable WebRTC echo canceller: ${ENABLE_WEBRTC}
51+ Enable Snap support: ${ENABLE_SNAP}
52 Enable gcov coverage: ${ENABLE_GCOV}
53 Enable unit tests: ${ENABLE_TESTS}
54 Database
55diff --git a/src/Makefile.am b/src/Makefile.am
56index d623d0a..16b4d5d 100644
57--- a/src/Makefile.am
58+++ b/src/Makefile.am
59@@ -1240,6 +1240,11 @@ modlibexec_LTLIBRARIES += \
18 module-esound-sink.la60 module-esound-sink.la
19 endif61 endif
20 62
21+if HAVE_APPARMOR63+if BUILD_SNAP
22+modlibexec_LTLIBRARIES += \64+modlibexec_LTLIBRARIES += \
23+ module-snappy-policy.la65+ module-snap-policy.la
24+endif66+endif
25+67+
26 # See comment at librtp.la above68 # See comment at librtp.la above
27 if !OS_IS_WIN3269 if !OS_IS_WIN32
28 modlibexec_LTLIBRARIES += \70 modlibexec_LTLIBRARIES += \
29@@ -2048,6 +2053,14 @@ module_trust_store_la_LIBADD = $(MODULE_71@@ -2024,6 +2029,14 @@ module_echo_cancel_la_CFLAGS += -DHAVE_WEBRTC=1
30 module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=172 module_echo_cancel_la_LIBADD += libwebrtc-util.la
31 endif73 endif
32 74
33+# Snappy75+# Snappy
34+if HAVE_APPARMOR76+if BUILD_SNAP
35+module_snappy_policy_la_SOURCES = modules/module-snappy-policy.c77+module_snap_policy_la_SOURCES = modules/module-snap-policy.c
36+module_snappy_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor78+module_snap_policy_la_LDFLAGS = $(MODULE_LDFLAGS)
37+module_snappy_policy_la_LIBADD = $(MODULE_LIBADD)79+module_snap_policy_la_LIBADD = $(MODULE_LIBADD) $(APPARMOR_LIBS) $(SNAPD_GLIB_LIBS)
38+module_snappy_policy_la_CFLAGS = $(AM_CFLAGS) -DHAVE_SNAPPY=180+module_snap_policy_la_CFLAGS = $(AM_CFLAGS) $(APPARMOR_CFLAGS) $(SNAPD_GLIB_CFLAGS) -DPA_MODULE_NAME=module_snap_policy
39+endif81+endif
40+82+
41 # RTP modules83 # RTP modules
42 module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c84 module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c
43 module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS)85 module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS)
44Index: pulseaudio/src/modules/module-snappy-policy.c86diff --git a/src/modules/module-snap-policy.c b/src/modules/module-snap-policy.c
45===================================================================87new file mode 100644
88index 0000000..8660476
46--- /dev/null89--- /dev/null
47+++ pulseaudio/src/modules/module-snappy-policy.c90+++ b/src/modules/module-snap-policy.c
48@@ -0,0 +1,96 @@91@@ -0,0 +1,347 @@
49+/***92+/***
50+ This file is part of PulseAudio.93+ This file is part of PulseAudio.
51+94+
52+ Copyright 2016 Canonical Ltd.95+ Copyright 2018 Canonical Ltd.
53+ Written by Simon Fels <simon.fels@canonical.com>96+ Authors:
97+ Simon Fels <simon.fels@canonical.com>
98+ James Henstridge <james.henstridge@canonical.com>
54+99+
55+ PulseAudio is free software; you can redistribute it and/or modify100+ PulseAudio is free software; you can redistribute it and/or modify
56+ it under the terms of the GNU Lesser General Public License as published101+ it under the terms of the GNU Lesser General Public License as published
@@ -71,116 +116,323 @@ Index: pulseaudio/src/modules/module-snappy-policy.c
71+#endif116+#endif
72+117+
73+#include <sys/apparmor.h>118+#include <sys/apparmor.h>
74+#include <errno.h>119+#include <glib.h>
120+#include <snapd-glib/snapd-glib.h>
75+121+
76+#include <pulsecore/i18n.h>122+#include <pulsecore/asyncq.h>
77+#include <pulsecore/core.h>123+#include <pulsecore/core.h>
124+#include <pulsecore/idxset.h>
125+#include <pulsecore/hashmap.h>
78+#include <pulsecore/module.h>126+#include <pulsecore/module.h>
79+#include <pulse/xmalloc.h>127+#include <pulsecore/mutex.h>
80+#include <pulsecore/fdsem.h>
81+#include <pulsecore/thread.h>128+#include <pulsecore/thread.h>
82+#include <pulsecore/core-util.h>
83+#include <pulse/mainloop-api.h>129+#include <pulse/mainloop-api.h>
130+#include <pulse/xmalloc.h>
84+131+
85+#define SNAP_LABEL_PREFIX "snap."132+#define SNAP_LABEL_PREFIX "snap."
86+#define SNAP_LABEL_PREFIX_LENGTH 5133+#define SNAP_LABEL_PREFIX_LENGTH 5
87+134+
88+PA_MODULE_AUTHOR("Simon Fels");135+PA_MODULE_AUTHOR("Canonical Ltd");
89+PA_MODULE_DESCRIPTION("Ubuntu Snappy policy management");136+PA_MODULE_DESCRIPTION("Ubuntu Snap policy management");
90+PA_MODULE_VERSION(PACKAGE_VERSION);137+PA_MODULE_VERSION(PACKAGE_VERSION);
91+PA_MODULE_LOAD_ONCE(true);138+PA_MODULE_LOAD_ONCE(true);
92+139+
140+struct per_client {
141+ struct userdata *userdata;
142+ uint32_t index;
143+ char *snap_name;
144+ pa_dynarray *pending_requests; /* of pa_access_data */
145+ bool completed;
146+ bool grant_access;
147+};
148+
93+struct userdata {149+struct userdata {
94+ pa_core *core;150+ pa_io_event *io_event;
95+ pa_hook_slot *connect_hook_slot;151+ pa_hook_slot *connect_hook_slot;
152+
153+ pa_thread *thread;
154+ pa_mutex *mutex;
155+ pa_cond *cond;
156+
157+ pa_hashmap *clients; /* int => struct per_client */
158+ pa_asyncq *results; /* of struct per_client */
159+
160+ /* Data owned by glib thread */
161+ GMainContext *main_context;
162+ GMainLoop *main_loop;
163+ GCancellable *cancellable;
164+ SnapdClient *snapd;
96+};165+};
97+166+
98+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) {167+/* ---- Code running in glib thread ---- */
99+ pa_client *client = pa_idxset_get_by_index(u->core->clients, d->client_index);168+
100+ if (!client)169+static void check_interfaces_finish(GObject *source_object,
101+ return PA_HOOK_OK;170+ GAsyncResult *result,
102+171+ gpointer user_data)
103+ char *label = NULL;172+{
104+ char *mode = NULL;173+ struct per_client *pc = user_data;
105+ if (aa_gettaskcon(client->creds.pid, &label, &mode) < 0) {174+ struct userdata *u = pc->userdata;
106+ pa_log_warn("Failed to retrieve apparmor security label for pid %u: %s",175+ g_autoptr(GError) error = NULL;
107+ client->creds.pid, strerror(-errno));176+ g_autoptr(GPtrArray) plugs = NULL;
108+ return PA_HOOK_OK;177+ unsigned i;
178+
179+ pa_mutex_lock(u->mutex);
180+
181+ if (!snapd_client_get_interfaces_finish(u->snapd, result, &plugs, NULL, &error)) {
182+ pa_log_warn("snapd_client_get_interfaces failed: %s", error->message);
183+ goto end;
184+ }
185+
186+ /* determine pc->grant_access */
187+ for (i = 0; i < plugs->len; i++) {
188+ SnapdPlug *plug = plugs->pdata[i];
189+ const char *snap_name = snapd_plug_get_snap(plug);
190+ const char *iface = snapd_plug_get_interface(plug);
191+ const bool connected = snapd_plug_get_connections(plug)->len != 0;
192+
193+ /* We are only interested in connected plugs of our snap */
194+ if (!connected || strcmp(snap_name, pc->snap_name) != 0) {
195+ continue;
196+ }
197+ if (!strcmp(iface, "pulseaudio") || !strcmp(iface, "audio-record")) {
198+ pc->grant_access = true;
199+ break;
200+ }
201+ }
202+
203+end:
204+ pc->completed = true;
205+ pa_asyncq_push(u->results, pc, true);
206+ pa_mutex_unlock(u->mutex);
207+}
208+
209+static gboolean check_interfaces(void *data)
210+{
211+ struct per_client *pc = data;
212+ struct userdata *u = pc->userdata;
213+
214+ snapd_client_get_interfaces_async(u->snapd, u->cancellable,
215+ check_interfaces_finish, pc);
216+ return G_SOURCE_REMOVE;
217+}
218+
219+
220+static void thread_main(void *data) {
221+ struct userdata *u = data;
222+
223+ pa_mutex_lock(u->mutex);
224+
225+ u->main_context = g_main_context_new();
226+ g_main_context_push_thread_default(u->main_context);
227+ u->main_loop = g_main_loop_new(u->main_context, false);
228+ u->cancellable = g_cancellable_new();
229+ u->snapd = snapd_client_new();
230+
231+ /* Signal main thread that we've finished initialisation */
232+ pa_cond_signal(u->cond, false);
233+ pa_mutex_unlock(u->mutex);
234+
235+ pa_log_info("Starting GLib main loop");
236+ g_main_loop_run(u->main_loop);
237+ pa_log_info("GLib main loop exited");
238+
239+ g_cancellable_cancel(u->cancellable);
240+
241+ g_clear_object(&u->snapd);
242+ g_clear_object(&u->cancellable);
243+ g_main_context_pop_thread_default(u->main_context);
244+ g_clear_pointer(&u->main_loop, g_main_loop_unref);
245+ g_clear_pointer(&u->main_context, g_main_context_unref);
246+}
247+
248+static gboolean thread_quit(void *data)
249+{
250+ struct userdata *u = data;
251+
252+ g_main_loop_quit(u->main_loop);
253+ return G_SOURCE_REMOVE;
254+}
255+
256+/* ---- Code running in main thread ---- */
257+
258+static struct per_client *per_client_new(struct userdata *u,
259+ uint32_t client_index,
260+ char *snap_name) {
261+ struct per_client *pc = pa_xnew0(struct per_client, 1);
262+ pc->userdata = u;
263+ pc->index = client_index;
264+ pc->snap_name = snap_name;
265+ pc->pending_requests = pa_dynarray_new(NULL);
266+ pc->completed = false;
267+ pc->grant_access = false;
268+ return pc;
269+}
270+
271+static void per_client_free(struct per_client *pc) {
272+ if (!pc) return;
273+ pa_xfree(pc->snap_name);
274+ pa_dynarray_free(pc->pending_requests);
275+ pa_xfree(pc);
276+}
277+
278+static char *client_get_snap_name(pa_core *core, uint32_t client_index) {
279+ pa_client *client;
280+ char *context = NULL;
281+ char *snap_name = NULL;
282+ char *dot;
283+
284+ client = pa_idxset_get_by_index(core->clients, client_index);
285+ pa_assert(client != NULL);
286+ if (!client->creds_valid) {
287+ pa_log_warn("Client %u has no creds, cannot authenticate", client_index);
288+ goto end;
289+ }
290+
291+ /* If AppArmor is not enabled, then we can't identify the client */
292+ if (!aa_is_enabled()) {
293+ goto end;
294+ }
295+ if (aa_gettaskcon(client->creds.pid, &context, NULL) < 0) {
296+ pa_log_error("AppArmor profile could not be retrieved.");
297+ goto end;
109+ }298+ }
110+299+
111+ pa_hook_result_t decision = PA_HOOK_OK;300+ /* If the AppArmor context does not begin with "snap.", then this
301+ * is not a snap */
302+ if (strncmp(context, SNAP_LABEL_PREFIX, SNAP_LABEL_PREFIX_LENGTH) != 0) {
303+ goto end;
304+ }
112+305+
113+ // We only cancel the attempt of the client to start audio recording306+ dot = strchr(context+SNAP_LABEL_PREFIX_LENGTH, '.');
114+ // when we could successfully determine that the request is coming307+ if (dot == NULL) {
115+ // from an app which is part of a snap. Otherwise we continue to308+ pa_log_warn("Malformed snapd AppArmor profile name: %s", context);
116+ // work as normal.309+ goto end;
117+ if (label && strncmp(SNAP_LABEL_PREFIX, label, SNAP_LABEL_PREFIX_LENGTH) == 0)310+ }
118+ decision = PA_HOOK_CANCEL;311+ snap_name = pa_xstrndup(context+5, dot-context-SNAP_LABEL_PREFIX_LENGTH);
119+312+
120+ free(label);313+end:
314+ free(context);
315+ return snap_name;
316+}
317+
318+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d,
319+ struct userdata *u) {
320+ pa_hook_result_t result = PA_HOOK_STOP;
321+ struct per_client *pc = NULL;
322+ char *snap_name = NULL;
323+
324+ pa_mutex_lock(u->mutex);
325+ pc = pa_hashmap_get(u->clients, (void *)(size_t)d->client_index);
326+ if (pc) {
327+ if (pc->completed) {
328+ result = pc->grant_access ? PA_HOOK_OK : PA_HOOK_STOP;
329+ } else {
330+ /* A permission check for this snap is currently in progress */
331+ pa_dynarray_append(pc->pending_requests, d);
332+ result = PA_HOOK_CANCEL;
333+ }
334+ goto end;
335+ }
121+336+
122+ return decision;337+ snap_name = client_get_snap_name(core, d->client_index);
338+ if (!snap_name) {
339+ /* Not a snap, so allow access */
340+ result = PA_HOOK_OK;
341+ goto end;
342+ }
343+
344+ /* create new per client struct, and submit to glib thread */
345+ pc = per_client_new(u, d->client_index, snap_name);
346+ pa_dynarray_append(pc->pending_requests, d);
347+ pa_hashmap_put(u->clients, (void *) (size_t) d->client_index, pc);
348+ pa_log_info("Checking access for client %d (%s)", pc->index, pc->snap_name);
349+ g_main_context_invoke(u->main_context, check_interfaces, pc);
350+
351+ result = PA_HOOK_CANCEL;
352+
353+end:
354+ pa_mutex_unlock(u->mutex);
355+ return result;
356+}
357+
358+static void deliver_result(struct userdata *u, struct per_client *pc) {
359+ pa_access_data *ad;
360+ unsigned i;
361+
362+ pa_log_info("Access check for client %u (%s): %d",
363+ pc->index, pc->snap_name, pc->grant_access);
364+
365+ /* Call the hooks without holding the mutex, since this will
366+ * recurse into connect_record_hook. Access to pending_requests
367+ * should be safe here, since connect_record_hook wont alter the
368+ * array when the access check is complete. */
369+ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) {
370+ ad->async_finish_cb(ad, pc->grant_access);
371+ }
372+ pa_mutex_lock(u->mutex);
373+ pa_hashmap_remove_and_free(u->clients, (void *) (size_t) pc->index);
374+ pa_mutex_unlock(u->mutex);
375+}
376+
377+static void check_result(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
378+ struct userdata *u = userdata;
379+ struct per_client *pc;
380+
381+ pa_asyncq_read_after_poll(u->results);
382+ while ((pc = pa_asyncq_pop(u->results, false)) != NULL) {
383+ deliver_result(u, pc);
384+ }
385+ pa_asyncq_read_before_poll(u->results);
123+}386+}
124+387+
125+int pa__init(pa_module *m) {388+int pa__init(pa_module *m) {
126+ struct userdata *u;389+ struct userdata *u;
127+ u = pa_xnew0(struct userdata, 1);
128+ u->core = m->core;
129+390+
391+ u = pa_xnew0(struct userdata, 1);
130+ m->userdata = u;392+ m->userdata = u;
131+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD],393+ u->mutex = pa_mutex_new(false, false);
132+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u);394+ u->cond = pa_cond_new();
395+
396+ u->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func,
397+ pa_idxset_trivial_compare_func,
398+ NULL, (pa_free_cb_t) per_client_free);
399+
400+ u->results = pa_asyncq_new(0);
401+ u->io_event = m->core->mainloop->io_new(
402+ m->core->mainloop, pa_asyncq_read_fd(u->results), PA_IO_EVENT_INPUT,
403+ check_result, u);
404+ pa_asyncq_read_before_poll(u->results);
405+
406+ u->connect_hook_slot = pa_hook_connect(
407+ &m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], PA_HOOK_NORMAL,
408+ (pa_hook_cb_t) connect_record_hook, u);
409+
410+ /* Start glib thread and wait for it to finish initialising. */
411+ pa_mutex_lock(u->mutex);
412+ u->thread = pa_thread_new("snapd-glib", thread_main, u);
413+ pa_cond_wait(u->cond, u->mutex);
414+ pa_mutex_unlock(u->mutex);
133+415+
134+ return 0;416+ return 0;
135+}417+}
136+418+
137+void pa__done(pa_module *m) {419+void pa__done(pa_module *m) {
138+ struct userdata *u = m->userdata;420+ struct userdata *u = m->userdata;
139+ if (u) {421+ if (!u) return;
140+ if (u->connect_hook_slot)
141+ pa_hook_slot_free(u->connect_hook_slot);
142+ pa_xfree(u);
143+ }
144+}
145Index: pulseaudio/configure.ac
146===================================================================
147--- pulseaudio.orig/configure.ac
148+++ pulseaudio/configure.ac
149@@ -1446,6 +1446,19 @@ AS_IF([test "x$os_is_win32" != "x1"],
150 AS_IF([test "x$ax_pthread_ok" = "xyes"],
151 AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris]))
152
153+# Snappy support
154+422+
155+AC_ARG_ENABLE([snappy],423+ pa_hook_slot_free(u->connect_hook_slot);
156+ AS_HELP_STRING([--enable-snappy], [Enable snappy support]))424+ m->core->mainloop->io_free(u->io_event);
157+425+
158+AS_IF([test "x$enable_snappy" != "xno"],426+ /* Stop the glib thread and wait for it to exit */
159+ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [HAVE_APPARMOR=1], [HAVE_APPARMOR=0])],427+ g_main_context_invoke(u->main_context, thread_quit, u);
160+ [HAVE_APPARMOR=0])428+ pa_thread_join(u->thread);
429+ pa_thread_free(u->thread);
161+430+
162+AS_IF([test "x$enable_snappy" = "xyes" && test "x$HAVE_APPARMOR" = "x0"],431+ pa_asyncq_free(u->results, NULL); /* items in queue owned by u->clients */
163+ [AC_MSG_ERROR([*** Apparmor library not found])])432+ g_clear_pointer(&u->clients, pa_hashmap_free);
164+433+
165+AM_CONDITIONAL([HAVE_APPARMOR], [test "x$HAVE_APPARMOR" = "x1"])434+ g_clear_pointer(&u->cond, pa_cond_free);
166 435+ g_clear_pointer(&u->mutex, pa_mutex_free);
167 # Ubuntu touch trust store436+
168 437+ pa_xfree(u);
169@@ -1641,6 +1654,8 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x438+}
170 AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no)
171 AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no)
172 AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no)
173+AS_IF([test "x$enable_snappy" = "x1"], ENABLE_SNAPPY=yes, ENABLE_SNAPPY=no)
174+AS_IF([test "x$HAVE_APPARMOR" = "x1"], ENABLE_APPARMOR=yes, ENABLE_APPARMOR=no)
175
176 echo "
177 ---{ $PACKAGE_NAME $VERSION }---
178@@ -1698,6 +1713,8 @@ echo "
179 Enable soxr (resampler): ${ENABLE_SOXR}
180 Enable WebRTC echo canceller: ${ENABLE_WEBRTC}
181 Enable Ubuntu trust store: ${ENABLE_TRUST_STORE}
182+ Enable Snappy support: ${ENABLE_SNAPPY}
183+ Enable Apparmor: ${ENABLE_APPARMOR}
184 Enable gcov coverage: ${ENABLE_GCOV}
185 Enable unit tests: ${ENABLE_TESTS}
186 Database
diff --git a/debian/patches/0701-enable-snap-policy-module.patch b/debian/patches/0701-enable-snap-policy-module.patch
187new file mode 100644439new file mode 100644
index 0000000..233671b
--- /dev/null
+++ b/debian/patches/0701-enable-snap-policy-module.patch
@@ -0,0 +1,25 @@
1From: James Henstridge <james.henstridge@canonical.com>
2Date: Tue, 7 Aug 2018 16:54:00 +0800
3Subject: daemon: enable module-snap-policy module
4
5---
6 src/daemon/default.pa.in | 6 ++++++
7 1 file changed, 6 insertions(+)
8
9diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in
10index 78c42a1..633e0f0 100755
11--- a/src/daemon/default.pa.in
12+++ b/src/daemon/default.pa.in
13@@ -171,6 +171,12 @@ load-module module-position-event-sounds
14 ### Cork music/video streams when a phone stream is active
15 load-module module-role-cork
16
17+### Block audio recording for snap confined packages unless they have
18+### the "pulseaudio" or "audio-record" interfaces plugged.
19+.ifexists module-snap-policy@PA_SOEXT@
20+load-module module-snap-policy
21+.endif
22+
23 ### Modules to allow autoloading of filters (such as echo cancellation)
24 ### on demand. module-filter-heuristics tries to determine what filters
25 ### make sense, and module-filter-apply does the heavy-lifting of
diff --git a/debian/patches/series b/debian/patches/series
index 52c106a..16de29a 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -7,13 +7,10 @@
7memfd-glibc2.27.patch7memfd-glibc2.27.patch
80050-disable-volume-test.patch80050-disable-volume-test.patch
99
10# Ubuntu touch: support for trust-store10# Ubuntu Snap policy support
110406-tagstruct-add-copy-method.patch110406-tagstruct-add-copy-method.patch
120407-access-Add-access-control-hooks.patch120407-access-Add-access-control-hooks.patch
130408-protocol-native-add-access-checks.patch130408-protocol-native-add-access-checks.patch
140409-Trust-store-patch.patch140409-pa-client-peer-credentials.patch
150410-Add-thread-to-activate-trust-store-interface.patch
160417-increase-timeout-check-apparmor.patch
17
18# Ubuntu Snappy
190700-modules-add-snappy-policy-module.patch150700-modules-add-snappy-policy-module.patch
160701-enable-snap-policy-module.patch
diff --git a/debian/pulseaudio.install b/debian/pulseaudio.install
index 59419ae..0756e46 100755
--- a/debian/pulseaudio.install
+++ b/debian/pulseaudio.install
@@ -78,7 +78,7 @@ usr/lib/pulse-*/modules/module-virtual-sink.so
78usr/lib/pulse-*/modules/module-virtual-source.so78usr/lib/pulse-*/modules/module-virtual-source.so
79usr/lib/pulse-*/modules/module-switch-on-port-available.so79usr/lib/pulse-*/modules/module-switch-on-port-available.so
80usr/lib/pulse-*/modules/module-virtual-surround-sink.so80usr/lib/pulse-*/modules/module-virtual-surround-sink.so
81usr/lib/pulse-*/modules/module-snappy-policy.so81usr/lib/pulse-*/modules/module-snap-policy.so
82usr/lib/pulse-*/modules/module-x11*.so82usr/lib/pulse-*/modules/module-x11*.so
83usr/lib/pulse-*/modules/module-allow-passthrough.so83usr/lib/pulse-*/modules/module-allow-passthrough.so
84[linux-any] usr/lib/pulse-*/modules/module-systemd-login.so84[linux-any] usr/lib/pulse-*/modules/module-systemd-login.so
diff --git a/debian/rules b/debian/rules
index f92f0b2..697e3cc 100755
--- a/debian/rules
+++ b/debian/rules
@@ -22,7 +22,7 @@ DEB_CONFIGURE_EXTRA_FLAGS = --enable-x11 --disable-hal-compat \
22 --with-zsh-completion-dir=\$${datadir}/zsh/vendor-completions \22 --with-zsh-completion-dir=\$${datadir}/zsh/vendor-completions \
23 --with-bash-completion-dir=\$${datadir}/bash-completion/completions \23 --with-bash-completion-dir=\$${datadir}/bash-completion/completions \
24 --with-systemduserunitdir=\$${prefix}/lib/systemd/user \24 --with-systemduserunitdir=\$${prefix}/lib/systemd/user \
25 --enable-snappy \25 --enable-snap \
26 --disable-bluez4 \26 --disable-bluez4 \
27 --enable-gsettings \27 --enable-gsettings \
28 --disable-gconf28 --disable-gconf

Subscribers

People subscribed via source and target branches