Merge ~ubuntu-audio-dev/pulseaudio:snap-policy-bionic into ~ubuntu-audio-dev/pulseaudio:ubuntu-bionic
- Git
- lp:~ubuntu-audio-dev/pulseaudio
- snap-policy-bionic
- Merge into ubuntu-bionic
Proposed by
James Henstridge
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 635d02e2aabf13996cebf97de46ffce87d99ee5c | ||||
Proposed branch: | ~ubuntu-audio-dev/pulseaudio:snap-policy-bionic | ||||
Merge into: | ~ubuntu-audio-dev/pulseaudio:ubuntu-bionic | ||||
Diff against target: |
1633 lines (+556/-229) 9 files modified
debian/changelog (+16/-0) debian/control (+1/-0) debian/patches/0409-pa-client-peer-credentials.patch (+104/-0) debian/patches/0700-modules-add-snappy-policy-module.patch (+400/-119) debian/patches/0701-enable-snap-policy-module.patch (+30/-0) debian/patches/series (+3/-6) debian/pulseaudio.install (+1/-1) debian/rules (+1/-1) dev/null (+0/-102) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Audio Development Team | Pending | ||
Review via email: mp+353962@code.launchpad.net |
Commit message
Backport the snap policy module changes to Bionic.
Description of the change
This is a backport of the changes in Cosmic's pulseaudio 1:12.2-0ubuntu2 and 1:12.2-0ubuntu3.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/debian/changelog b/debian/changelog |
2 | index d7ba18f..56cbc22 100644 |
3 | --- a/debian/changelog |
4 | +++ b/debian/changelog |
5 | @@ -1,3 +1,19 @@ |
6 | +pulseaudio (1:11.1-1ubuntu7.5) bionic; urgency=medium |
7 | + |
8 | + * Update snap policy to make access to audio recording conditional on |
9 | + plugging the "pulseaudio" or "audio-record" interfaces (LP: #1781428): |
10 | + - 0700-modules-add-snappy-policy-module.patch: rewrite to query |
11 | + snapd for the client's plugged interfaces. |
12 | + - 0701-enable-snap-policy-module.patch: enable the module in the |
13 | + default configuration. |
14 | + - Build depend on libsnapd-glib-dev. |
15 | + * Remove module-trust-store patch set: |
16 | + - 0409-Trust-store-patch.patch: trimmed down to pulsecore changes. |
17 | + - 0410-Add-thread-to-activate-trust-store-interface.patch: removed. |
18 | + - 0417-increase-timeout-check-apparmor.patch: removed. |
19 | + |
20 | + -- James Henstridge <james.henstridge@canonical.com> Wed, 05 Nov 2019 17:16:25 +0800 |
21 | + |
22 | pulseaudio (1:11.1-1ubuntu7.4) bionic; urgency=medium |
23 | |
24 | [Hui Wang] |
25 | diff --git a/debian/control b/debian/control |
26 | index 2d9d9a9..a226566 100644 |
27 | --- a/debian/control |
28 | +++ b/debian/control |
29 | @@ -29,6 +29,7 @@ Build-Depends: debhelper (>= 10), |
30 | libltdl-dev (>= 2.2.6a-2), |
31 | liborc-0.4-dev (>= 1:0.4.11), |
32 | libsamplerate0-dev, |
33 | + libsnapd-glib-dev (>= 1.44), |
34 | libsndfile1-dev (>= 1.0.20), |
35 | libspeexdsp-dev (>= 1.2~rc1), |
36 | libssl-dev, |
37 | diff --git a/debian/patches/0409-Trust-store-patch.patch b/debian/patches/0409-Trust-store-patch.patch |
38 | deleted file mode 100644 |
39 | index 60277af..0000000 |
40 | --- a/debian/patches/0409-Trust-store-patch.patch |
41 | +++ /dev/null |
42 | @@ -1,582 +0,0 @@ |
43 | -From ff36148e002232842ea0f07b148e5844c5d44ceb Mon Sep 17 00:00:00 2001 |
44 | -From: David Henningsson <david.henningsson@canonical.com> |
45 | -Date: Wed, 22 Jul 2015 16:37:19 +0200 |
46 | -Subject: [PATCH 4/5] Trust-store patch |
47 | - |
48 | -...includes some fixes elsewhere as well. |
49 | - |
50 | -Signed-off-by: David Henningsson <david.henningsson@canonical.com> |
51 | ---- |
52 | - configure.ac | 15 ++ |
53 | - src/Makefile.am | 24 +++ |
54 | - src/modules/trust-store/module-trust-store.c | 221 +++++++++++++++++++++++++++ |
55 | - src/modules/trust-store/truststore.cc | 74 +++++++++ |
56 | - src/modules/trust-store/truststore.h | 41 +++++ |
57 | - src/pulsecore/client.h | 4 + |
58 | - src/pulsecore/creds.h | 1 + |
59 | - src/pulsecore/iochannel.c | 2 + |
60 | - src/pulsecore/protocol-native.c | 13 ++ |
61 | - src/pulsecore/tagstruct.c | 1 + |
62 | - 12 files changed, 399 insertions(+), 7 deletions(-) |
63 | - create mode 100644 src/modules/trust-store/module-trust-store.c |
64 | - create mode 100644 src/modules/trust-store/truststore.cc |
65 | - create mode 100644 src/modules/trust-store/truststore.h |
66 | - |
67 | -Index: pulseaudio/configure.ac |
68 | -=================================================================== |
69 | ---- pulseaudio.orig/configure.ac |
70 | -+++ pulseaudio/configure.ac |
71 | -@@ -1413,6 +1413,19 @@ AS_IF([test "x$ax_pthread_ok" = "xyes"], |
72 | - AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) |
73 | - |
74 | - |
75 | -+# Ubuntu touch trust store |
76 | -+ |
77 | -+AC_ARG_ENABLE([trust-store], |
78 | -+ AS_HELP_STRING([--enable-trust-store], [Enable Ubuntu touch trust store])) |
79 | -+ |
80 | -+AS_IF([test "x$enable_trust_store" != "xno"], |
81 | -+ [PKG_CHECK_MODULES(TRUST_STORE, [ trust-store ], [HAVE_TRUST_STORE=1], [HAVE_TRUST_STORE=0])], |
82 | -+ [HAVE_WEBRTC=0]) |
83 | -+ |
84 | -+AS_IF([test "x$enable_trust_store" = "xyes" && test "x$HAVE_TRUST_STORE" = "x0"], |
85 | -+ [AC_MSG_ERROR([*** Ubuntu touch trust store library not found])]) |
86 | -+ |
87 | -+AM_CONDITIONAL([HAVE_TRUST_STORE], [test "x$HAVE_TRUST_STORE" = "x1"]) |
88 | - |
89 | - ################################### |
90 | - # Output # |
91 | -@@ -1580,6 +1593,7 @@ AS_IF([test "x$HAVE_ADRIAN_EC" = "x1"], |
92 | - AS_IF([test "x$HAVE_SPEEX" = "x1"], ENABLE_SPEEX=yes, ENABLE_SPEEX=no) |
93 | - AS_IF([test "x$HAVE_SOXR" = "x1"], ENABLE_SOXR=yes, ENABLE_SOXR=no) |
94 | - AS_IF([test "x$HAVE_WEBRTC" = "x1"], ENABLE_WEBRTC=yes, ENABLE_WEBRTC=no) |
95 | -+AS_IF([test "x$HAVE_TRUST_STORE" = "x1"], ENABLE_TRUST_STORE=yes, ENABLE_TRUST_STORE=no) |
96 | - AS_IF([test "x$HAVE_TDB" = "x1"], ENABLE_TDB=yes, ENABLE_TDB=no) |
97 | - AS_IF([test "x$HAVE_GDBM" = "x1"], ENABLE_GDBM=yes, ENABLE_GDBM=no) |
98 | - AS_IF([test "x$HAVE_SIMPLEDB" = "x1"], ENABLE_SIMPLEDB=yes, ENABLE_SIMPLEDB=no) |
99 | -@@ -1643,6 +1657,7 @@ echo " |
100 | - Enable speex (resampler, AEC): ${ENABLE_SPEEX} |
101 | - Enable soxr (resampler): ${ENABLE_SOXR} |
102 | - Enable WebRTC echo canceller: ${ENABLE_WEBRTC} |
103 | -+ Enable Ubuntu trust store: ${ENABLE_TRUST_STORE} |
104 | - Enable gcov coverage: ${ENABLE_GCOV} |
105 | - Enable unit tests: ${ENABLE_TESTS} |
106 | - Database |
107 | -Index: pulseaudio/src/Makefile.am |
108 | -=================================================================== |
109 | ---- pulseaudio.orig/src/Makefile.am |
110 | -+++ pulseaudio/src/Makefile.am |
111 | -@@ -1097,6 +1097,10 @@ if HAVE_WEBRTC |
112 | - modlibexec_LTLIBRARIES += libwebrtc-util.la |
113 | - endif |
114 | - |
115 | -+if HAVE_TRUST_STORE |
116 | -+modlibexec_LTLIBRARIES += libtruststore-util.la |
117 | -+endif |
118 | -+ |
119 | - if HAVE_ESOUND |
120 | - modlibexec_LTLIBRARIES += \ |
121 | - libprotocol-esound.la |
122 | -@@ -1226,6 +1230,11 @@ modlibexec_LTLIBRARIES += \ |
123 | - module-role-ducking.la \ |
124 | - module-allow-passthrough.la |
125 | - |
126 | -+if HAVE_TRUST_STORE |
127 | -+modlibexec_LTLIBRARIES += \ |
128 | -+ module-trust-store.la |
129 | -+endif |
130 | -+ |
131 | - if HAVE_ESOUND |
132 | - modlibexec_LTLIBRARIES += \ |
133 | - module-esound-protocol-tcp.la \ |
134 | -@@ -1535,6 +1544,7 @@ SYMDEF_FILES = \ |
135 | - module-intended-roles-symdef.h \ |
136 | - module-suspend-on-idle-symdef.h \ |
137 | - module-echo-cancel-symdef.h \ |
138 | -+ module-trust-store-symdef.h \ |
139 | - module-hal-detect-symdef.h \ |
140 | - module-udev-detect-symdef.h \ |
141 | - module-systemd-login-symdef.h \ |
142 | -@@ -2077,6 +2087,20 @@ module_echo_cancel_la_CFLAGS += -DHAVE_W |
143 | - module_echo_cancel_la_LIBADD += libwebrtc-util.la |
144 | - endif |
145 | - |
146 | -+if HAVE_TRUST_STORE |
147 | -+# Separate helper library to keep C and C++ code apart, like we do for webrtc |
148 | -+libtruststore_util_la_SOURCES = modules/trust-store/truststore.cc |
149 | -+libtruststore_util_la_CXXFLAGS = $(AM_CXXFLAGS) -std=c++11 $(SERVER_CFLAGS) $(TRUST_STORE_CFLAGS) \ |
150 | -+ $(DBUS_CFLAGS) -DHAVE_TRUST_STORE=1 |
151 | -+libtruststore_util_la_LIBADD = libpulsecore-@PA_MAJORMINOR@.la $(TRUST_STORE_LIBS) $(DBUS_LIBS) |
152 | -+libtruststore_util_la_LDFLAGS = -avoid-version |
153 | -+ |
154 | -+module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c |
155 | -+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) |
156 | -+module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la |
157 | -+module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 |
158 | -+endif |
159 | -+ |
160 | - # RTP modules |
161 | - module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c |
162 | - module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS) |
163 | -Index: pulseaudio/src/modules/trust-store/module-trust-store.c |
164 | -=================================================================== |
165 | ---- /dev/null |
166 | -+++ pulseaudio/src/modules/trust-store/module-trust-store.c |
167 | -@@ -0,0 +1,228 @@ |
168 | -+/*** |
169 | -+ This file is part of PulseAudio. |
170 | -+ |
171 | -+ Copyright 2015 Canonical Ltd. |
172 | -+ Written by David Henningsson <david.henningsson@canonical.com> |
173 | -+ |
174 | -+ PulseAudio is free software; you can redistribute it and/or modify |
175 | -+ it under the terms of the GNU Lesser General Public License as published |
176 | -+ by the Free Software Foundation; either version 2.1 of the License, |
177 | -+ or (at your option) any later version. |
178 | -+ |
179 | -+ PulseAudio is distributed in the hope that it will be useful, but |
180 | -+ WITHOUT ANY WARRANTY; without even the implied warranty of |
181 | -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
182 | -+ General Public License for more details. |
183 | -+ |
184 | -+ You should have received a copy of the GNU Lesser General Public License |
185 | -+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. |
186 | -+***/ |
187 | -+#ifdef HAVE_CONFIG_H |
188 | -+#include <config.h> |
189 | -+#endif |
190 | -+ |
191 | -+#include <pulsecore/i18n.h> |
192 | -+#include <pulsecore/core.h> |
193 | -+#include <pulsecore/module.h> |
194 | -+#include <pulse/xmalloc.h> |
195 | -+#include <pulsecore/fdsem.h> |
196 | -+#include <pulsecore/thread.h> |
197 | -+#include <pulsecore/core-util.h> |
198 | -+#include <pulse/mainloop-api.h> |
199 | -+ |
200 | -+#include "module-trust-store-symdef.h" |
201 | -+ |
202 | -+PA_MODULE_AUTHOR("David Henningsson"); |
203 | -+PA_MODULE_DESCRIPTION("Ubuntu touch trust store integration"); |
204 | -+PA_MODULE_VERSION(PACKAGE_VERSION); |
205 | -+PA_MODULE_LOAD_ONCE(true); |
206 | -+ |
207 | -+#include "truststore.h" |
208 | -+ |
209 | -+#define REQUEST_GRANTED 1 |
210 | -+#define REQUEST_DENIED -1 |
211 | -+#define REQUEST_PENDING 0 |
212 | -+ |
213 | -+struct userdata; |
214 | -+ |
215 | -+struct per_client { |
216 | -+ uint32_t index; |
217 | -+ char *appname; |
218 | -+ uid_t uid; |
219 | -+ pid_t pid; |
220 | -+ struct userdata *userdata; |
221 | -+ pa_dynarray *pending_requests; |
222 | -+ pa_atomic_t result; |
223 | -+}; |
224 | -+ |
225 | -+struct userdata { |
226 | -+ pa_core *core; |
227 | -+ pa_trust_store *ts; |
228 | -+ pa_hashmap *per_clients; |
229 | -+ pa_thread *thread; |
230 | -+ pa_fdsem *fdsem; |
231 | -+ pa_io_event *io_event; |
232 | -+ pa_hook_slot *connect_hook_slot; |
233 | -+}; |
234 | -+ |
235 | -+static struct per_client *per_client_new(struct userdata *u, uint32_t client_index) { |
236 | -+ struct per_client *pc; |
237 | -+ pa_client *client = pa_idxset_get_by_index(u->core->clients, client_index); |
238 | -+ |
239 | -+ pa_assert(client); |
240 | -+ if (!client->creds_valid) { |
241 | -+ pa_log_warn("Client %d has no creds, cannot authenticate", client_index); |
242 | -+ return NULL; |
243 | -+ } |
244 | -+ |
245 | -+ pc = pa_xnew0(struct per_client, 1); |
246 | -+ // TODO: Do we need something else than the application name here - |
247 | -+ // the application can probably spoof it |
248 | -+ pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); |
249 | -+ pc->index = client_index; |
250 | -+ pc->uid = client->creds.uid; |
251 | -+ pc->pid = client->creds.pid; |
252 | -+ pc->pending_requests = pa_dynarray_new(NULL); |
253 | -+ pc->userdata = u; |
254 | -+ |
255 | -+ pa_hashmap_put(u->per_clients, (void *) (size_t) client_index, pc); |
256 | -+ |
257 | -+ return pc; |
258 | -+} |
259 | -+ |
260 | -+static void per_client_free_from_hashmap(void *data) { |
261 | -+ struct per_client *pc = data; |
262 | -+ if (!pc) |
263 | -+ return; |
264 | -+ pa_xfree(pc->appname); |
265 | -+ pa_dynarray_free(pc->pending_requests); |
266 | -+ pa_xfree(pc); |
267 | -+} |
268 | -+ |
269 | -+static void thread_func(void *data) { |
270 | -+ struct per_client *pc = data; |
271 | -+ |
272 | -+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, |
273 | -+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase |
274 | -+ /// can't be translated in this language, translate the whole sentence |
275 | -+ /// 'This app wants to record audio.' |
276 | -+ _("wants to record audio.")); |
277 | -+ |
278 | -+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); |
279 | -+ pa_fdsem_post(pc->userdata->fdsem); |
280 | -+} |
281 | -+ |
282 | -+static void check_queue(struct userdata *u) { |
283 | -+ struct per_client *pc; |
284 | -+ void *dummy; |
285 | -+ |
286 | -+ pa_log_debug("Checking queue, size: %d, thread object: %s", |
287 | -+ pa_hashmap_size(u->per_clients), pa_yes_no(u->thread)); |
288 | -+ if (u->thread) { |
289 | -+ pa_access_data *ad; |
290 | -+ unsigned i; |
291 | -+ int result; |
292 | -+ pc = pa_thread_get_data(u->thread); |
293 | -+ result = pa_atomic_load(&pc->result); |
294 | -+ if (result == REQUEST_PENDING) |
295 | -+ return; /* Still processing */ |
296 | -+ pa_thread_free(u->thread); |
297 | -+ u->thread = NULL; |
298 | -+ |
299 | -+ pa_log_debug("Letting client %d (%s) know the result (%s)", |
300 | -+ pc->index, pc->appname, result == REQUEST_GRANTED ? "granted" : "rejected"); |
301 | -+ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) { |
302 | -+ ad->async_finish_cb(ad, result == REQUEST_GRANTED); |
303 | -+ } |
304 | -+ pa_hashmap_remove(u->per_clients, (void*) (size_t) pc->index); |
305 | -+ } |
306 | -+ |
307 | -+ /* Find a client that needs asking */ |
308 | -+ PA_HASHMAP_FOREACH(pc, u->per_clients, dummy) { |
309 | -+ if (u->thread) |
310 | -+ return; |
311 | -+ pa_assert(pa_atomic_load(&pc->result) == REQUEST_PENDING); |
312 | -+ pa_log_debug("Starting thread to ask about client %d (%s)", pc->index, |
313 | -+ pc->appname); |
314 | -+ u->thread = pa_thread_new("trust-store", thread_func, pc); |
315 | -+ } |
316 | -+} |
317 | -+ |
318 | -+static void check_fdsem(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t ee, |
319 | -+ void *userdata) { |
320 | -+ struct userdata *u = userdata; |
321 | -+ pa_fdsem_after_poll(u->fdsem); |
322 | -+ check_queue(u); |
323 | -+ pa_fdsem_before_poll(u->fdsem); |
324 | -+} |
325 | -+ |
326 | -+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) { |
327 | -+ struct per_client *pc = pa_hashmap_get(u->per_clients, (void*) (size_t) d->client_index); |
328 | -+ int r; |
329 | -+ if (!pc) |
330 | -+ pc = per_client_new(u, d->client_index); |
331 | -+ |
332 | -+ r = pa_atomic_load(&pc->result); |
333 | -+ pa_log_debug("Checking permission to record for client %d (%s) ", d->client_index, |
334 | -+ r == REQUEST_GRANTED ? "granted" : r != REQUEST_PENDING ? "rejected" : "unknown"); |
335 | -+ if (r == REQUEST_GRANTED) return PA_HOOK_OK; |
336 | -+ if (r != REQUEST_PENDING) return PA_HOOK_STOP; |
337 | -+ |
338 | -+ pa_dynarray_append(pc->pending_requests, d); |
339 | -+ check_queue(u); |
340 | -+ return PA_HOOK_CANCEL; |
341 | -+} |
342 | -+ |
343 | -+/* Test code - remove from final product */ |
344 | -+static void test(struct userdata *u) { |
345 | -+ uint32_t dummy; |
346 | -+ pa_client *client; |
347 | -+ |
348 | -+ PA_IDXSET_FOREACH(client, u->core->clients, dummy) { |
349 | -+ if (client->creds_valid) |
350 | -+ pa_trust_store_check(u->ts, "The evil app", client->creds.uid, |
351 | -+ client->creds.pid, "%1% wants to eat your laundry."); |
352 | -+ } |
353 | -+} |
354 | -+ |
355 | -+int pa__init(pa_module *m) { |
356 | -+ struct userdata *u; |
357 | -+ pa_trust_store *ts = pa_trust_store_new(); |
358 | -+ if (ts == NULL) |
359 | -+ return -1; |
360 | -+ u = pa_xnew0(struct userdata, 1); |
361 | -+ u->ts = ts; |
362 | -+ u->core = m->core; |
363 | -+ u->per_clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func, |
364 | -+ NULL, per_client_free_from_hashmap); |
365 | -+ m->userdata = u; |
366 | -+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], |
367 | -+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u); |
368 | -+ |
369 | -+ pa_assert_se(u->fdsem = pa_fdsem_new()); |
370 | -+ pa_assert_se(u->io_event = m->core->mainloop->io_new(m->core->mainloop, |
371 | -+ pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u)); |
372 | -+ pa_fdsem_before_poll(u->fdsem); |
373 | -+ |
374 | -+ test(u); |
375 | -+ return 0; |
376 | -+} |
377 | -+ |
378 | -+void pa__done(pa_module *m) { |
379 | -+ struct userdata *u = m->userdata; |
380 | -+ if (u) { |
381 | -+ if (u->connect_hook_slot) |
382 | -+ pa_hook_slot_free(u->connect_hook_slot); |
383 | -+ if (u->thread) |
384 | -+ pa_thread_free(u->thread); |
385 | -+ if (u->io_event) |
386 | -+ m->core->mainloop->io_free(u->io_event); |
387 | -+ if (u->fdsem) |
388 | -+ pa_fdsem_free(u->fdsem); |
389 | -+ if (u->per_clients) |
390 | -+ pa_hashmap_free(u->per_clients); |
391 | -+ if (u->ts) |
392 | -+ pa_trust_store_free(u->ts); |
393 | -+ pa_xfree(u); |
394 | -+ } |
395 | -+} |
396 | -Index: pulseaudio/src/modules/trust-store/truststore.cc |
397 | -=================================================================== |
398 | ---- /dev/null |
399 | -+++ pulseaudio/src/modules/trust-store/truststore.cc |
400 | -@@ -0,0 +1,74 @@ |
401 | -+#ifdef HAVE_CONFIG_H |
402 | -+#include <config.h> |
403 | -+#endif |
404 | -+ |
405 | -+#include <memory> |
406 | -+#include <core/dbus/bus.h> |
407 | -+#include <core/trust/dbus_agent.h> |
408 | -+#include <core/trust/agent.h> |
409 | -+ |
410 | -+#include <pulse/cdecl.h> |
411 | -+ |
412 | -+PA_C_DECL_BEGIN |
413 | -+#include <pulsecore/core-util.h> |
414 | -+#include <pulse/xmalloc.h> |
415 | -+#include <pulsecore/log.h> |
416 | -+ |
417 | -+#include "truststore.h" |
418 | -+PA_C_DECL_END |
419 | -+ |
420 | -+class TrustStore { |
421 | -+public: |
422 | -+ std::shared_ptr<core::trust::Agent> agent; |
423 | -+}; |
424 | -+ |
425 | -+pa_trust_store* pa_trust_store_new() { |
426 | -+ try { |
427 | -+ auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); |
428 | -+ auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio"); |
429 | -+ auto ts = new TrustStore(); |
430 | -+ ts->agent = agent; |
431 | -+ return (pa_trust_store *) ts; |
432 | -+ } catch(const std::exception &e) { |
433 | -+ pa_log_error("Could not create Ubuntu touch trust store connection: %s", |
434 | -+ e.what()); |
435 | -+ } catch(...) { |
436 | -+ pa_log_error("Could not create Ubuntu touch trust store connection"); |
437 | -+ return NULL; |
438 | -+ } |
439 | -+} |
440 | -+ |
441 | -+void pa_trust_store_free(pa_trust_store *t) { |
442 | -+ pa_assert(t != NULL); |
443 | -+ auto ts = (TrustStore*) t; |
444 | -+ delete ts; |
445 | -+} |
446 | -+ |
447 | -+bool pa_trust_store_check(pa_trust_store *t, const char *appname, |
448 | -+ uid_t uid, pid_t pid, const char *description) { |
449 | -+ auto ts = (TrustStore*) t; |
450 | -+ try { |
451 | -+ |
452 | -+ core::trust::Agent::RequestParameters params { |
453 | -+ core::trust::Uid{uid}, |
454 | -+ core::trust::Pid{pid}, |
455 | -+ appname, |
456 | -+ core::trust::Feature{0}, |
457 | -+ description |
458 | -+ }; |
459 | -+ pa_log_debug("Asking Ubuntu touch trust store for permission (app: %s)", |
460 | -+ params.application.id.c_str()); |
461 | -+ auto answer = ts->agent->authenticate_request_with_parameters(params); |
462 | -+ if (answer == core::trust::Request::Answer::granted) { |
463 | -+ pa_log_debug("Request granted."); |
464 | -+ return true; |
465 | -+ } |
466 | -+ pa_log_info("Request denied."); |
467 | -+ } catch(const std::exception &e) { |
468 | -+ pa_log_error("Could not ask Ubuntu touch trust store for permission: %s", |
469 | -+ e.what()); |
470 | -+ } catch(...) { |
471 | -+ pa_log_error("Could not ask Ubuntu touch trust store for permission"); |
472 | -+ } |
473 | -+ return false; |
474 | -+} |
475 | -Index: pulseaudio/src/modules/trust-store/truststore.h |
476 | -=================================================================== |
477 | ---- /dev/null |
478 | -+++ pulseaudio/src/modules/trust-store/truststore.h |
479 | -@@ -0,0 +1,41 @@ |
480 | -+/*** |
481 | -+ This file is part of PulseAudio. |
482 | -+ |
483 | -+ Copyright 2015 Canonical Ltd. |
484 | -+ Written by David Henningsson <david.henningsson@canonical.com> |
485 | -+ |
486 | -+ PulseAudio is free software; you can redistribute it and/or modify |
487 | -+ it under the terms of the GNU Lesser General Public License as published |
488 | -+ by the Free Software Foundation; either version 2.1 of the License, |
489 | -+ or (at your option) any later version. |
490 | -+ |
491 | -+ PulseAudio is distributed in the hope that it will be useful, but |
492 | -+ WITHOUT ANY WARRANTY; without even the implied warranty of |
493 | -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
494 | -+ General Public License for more details. |
495 | -+ |
496 | -+ You should have received a copy of the GNU Lesser General Public License |
497 | -+ along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. |
498 | -+***/ |
499 | -+ |
500 | -+#ifndef footruststorehfoo |
501 | -+#define footruststorehfoo |
502 | -+ |
503 | -+#ifdef HAVE_CONFIG_H |
504 | -+#include <config.h> |
505 | -+#endif |
506 | -+ |
507 | -+#include <pulsecore/creds.h> |
508 | -+ |
509 | -+#ifdef HAVE_TRUST_STORE |
510 | -+PA_C_DECL_BEGIN |
511 | -+typedef struct pa_trust_store pa_trust_store; |
512 | -+ |
513 | -+pa_trust_store* pa_trust_store_new(); |
514 | -+void pa_trust_store_free(pa_trust_store *ts); |
515 | -+bool pa_trust_store_check(pa_trust_store *ts, const char *appname, |
516 | -+ uid_t uid, pid_t pid, const char *description); |
517 | -+PA_C_DECL_END |
518 | -+#endif |
519 | -+ |
520 | -+#endif |
521 | -Index: pulseaudio/src/pulsecore/client.h |
522 | -=================================================================== |
523 | ---- pulseaudio.orig/src/pulsecore/client.h |
524 | -+++ pulseaudio/src/pulsecore/client.h |
525 | -@@ -26,6 +26,7 @@ |
526 | - #include <pulse/proplist.h> |
527 | - #include <pulsecore/core.h> |
528 | - #include <pulsecore/module.h> |
529 | -+#include <pulsecore/creds.h> |
530 | - |
531 | - /* Every connection to the server should have a pa_client |
532 | - * attached. That way the user may generate a listing of all connected |
533 | -@@ -35,6 +36,9 @@ struct pa_client { |
534 | - uint32_t index; |
535 | - pa_core *core; |
536 | - |
537 | -+ pa_creds creds; |
538 | -+ bool creds_valid; |
539 | -+ |
540 | - pa_proplist *proplist; |
541 | - pa_module *module; |
542 | - char *driver; |
543 | -Index: pulseaudio/src/pulsecore/creds.h |
544 | -=================================================================== |
545 | ---- pulseaudio.orig/src/pulsecore/creds.h |
546 | -+++ pulseaudio/src/pulsecore/creds.h |
547 | -@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cms |
548 | - struct pa_creds { |
549 | - gid_t gid; |
550 | - uid_t uid; |
551 | -+ pid_t pid; |
552 | - }; |
553 | - |
554 | - /* Struct for handling ancillary data, i e, extra data that can be sent together with a message |
555 | -Index: pulseaudio/src/pulsecore/iochannel.c |
556 | -=================================================================== |
557 | ---- pulseaudio.orig/src/pulsecore/iochannel.c |
558 | -+++ pulseaudio/src/pulsecore/iochannel.c |
559 | -@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa |
560 | - |
561 | - u = (struct ucred*) CMSG_DATA(&cmsg.hdr); |
562 | - |
563 | -+ /* FIXME: Check whether ucred->pid should be used */ |
564 | - u->pid = getpid(); |
565 | - if (ucred) { |
566 | - u->uid = ucred->uid; |
567 | -@@ -445,6 +446,7 @@ ssize_t pa_iochannel_read_with_ancil_dat |
568 | - |
569 | - ancil_data->creds.gid = u.gid; |
570 | - ancil_data->creds.uid = u.uid; |
571 | -+ ancil_data->creds.pid = u.pid; |
572 | - ancil_data->creds_valid = true; |
573 | - } |
574 | - else if (cmh->cmsg_type == SCM_RIGHTS) { |
575 | -Index: pulseaudio/src/pulsecore/protocol-native.c |
576 | -=================================================================== |
577 | ---- pulseaudio.orig/src/pulsecore/protocol-native.c |
578 | -+++ pulseaudio/src/pulsecore/protocol-native.c |
579 | -@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *p |
580 | - do_shm = false; |
581 | - |
582 | - #ifdef HAVE_CREDS |
583 | -+ { |
584 | -+ const pa_creds *creds; |
585 | -+ if ((creds = pa_pdispatch_creds(pd))) { |
586 | -+ c->client->creds = *creds; |
587 | -+ c->client->creds_valid = true; |
588 | -+ } |
589 | -+ } |
590 | - if (do_shm) { |
591 | - /* Only enable SHM if both sides are owned by the same |
592 | - * user. This is a security measure because otherwise data |
593 | -@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access |
594 | - } pa_protocol_native_access_data; |
595 | - |
596 | - static void check_access_finish_cb(pa_access_data *data, bool res) { |
597 | -+ uint32_t command, tag; |
598 | - pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data; |
599 | - pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata); |
600 | - |
601 | -@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_ac |
602 | - goto finish; |
603 | - } |
604 | - |
605 | -+ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0); |
606 | -+ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0); |
607 | -+ pa_assert(command == d->command); |
608 | -+ pa_assert(tag == d->tag); |
609 | -+ |
610 | - /* call the dispatcher again, hopefully this time, the access check will |
611 | - * fail or succeed immediately */ |
612 | - command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata); |
613 | -Index: pulseaudio/po/POTFILES.in |
614 | -=================================================================== |
615 | ---- pulseaudio.orig/po/POTFILES.in |
616 | -+++ pulseaudio/po/POTFILES.in |
617 | -@@ -23,6 +23,7 @@ src/modules/jack/module-jack-sink.c |
618 | - src/modules/jack/module-jack-source.c |
619 | - src/modules/macosx/module-coreaudio-device.c |
620 | - src/modules/module-allow-passthrough.c |
621 | -+src/modules/trust-store/module-trust-store.c |
622 | - src/modules/module-always-sink.c |
623 | - src/modules/module-cli.c |
624 | - src/modules/module-combine.c |
625 | diff --git a/debian/patches/0409-pa-client-peer-credentials.patch b/debian/patches/0409-pa-client-peer-credentials.patch |
626 | new file mode 100644 |
627 | index 0000000..65ba9fd |
628 | --- /dev/null |
629 | +++ b/debian/patches/0409-pa-client-peer-credentials.patch |
630 | @@ -0,0 +1,104 @@ |
631 | +From: David Henningsson <david.henningsson@canonical.com> |
632 | +Date: Wed, 22 Jul 2015 16:37:19 +0200 |
633 | +Subject: [PATCH 4/5] Expose peer credentials on pa_client |
634 | + |
635 | +Signed-off-by: David Henningsson <david.henningsson@canonical.com> |
636 | +--- |
637 | + src/pulsecore/client.h | 4 ++++ |
638 | + src/pulsecore/creds.h | 1 + |
639 | + src/pulsecore/iochannel.c | 2 ++ |
640 | + src/pulsecore/protocol-native.c | 13 +++++++++++++ |
641 | + 4 files changed, 20 insertions(+) |
642 | + |
643 | +diff --git a/src/pulsecore/client.h b/src/pulsecore/client.h |
644 | +index eb8173d..a038a0c 100644 |
645 | +--- a/src/pulsecore/client.h |
646 | ++++ b/src/pulsecore/client.h |
647 | +@@ -26,6 +26,7 @@ |
648 | + #include <pulse/proplist.h> |
649 | + #include <pulsecore/core.h> |
650 | + #include <pulsecore/module.h> |
651 | ++#include <pulsecore/creds.h> |
652 | + |
653 | + /* Every connection to the server should have a pa_client |
654 | + * attached. That way the user may generate a listing of all connected |
655 | +@@ -35,6 +36,9 @@ struct pa_client { |
656 | + uint32_t index; |
657 | + pa_core *core; |
658 | + |
659 | ++ pa_creds creds; |
660 | ++ bool creds_valid; |
661 | ++ |
662 | + pa_proplist *proplist; |
663 | + pa_module *module; |
664 | + char *driver; |
665 | +diff --git a/src/pulsecore/creds.h b/src/pulsecore/creds.h |
666 | +index 9fdbb4f..8bcf636 100644 |
667 | +--- a/src/pulsecore/creds.h |
668 | ++++ b/src/pulsecore/creds.h |
669 | +@@ -41,6 +41,7 @@ typedef struct pa_cmsg_ancil_data pa_cmsg_ancil_data; |
670 | + struct pa_creds { |
671 | + gid_t gid; |
672 | + uid_t uid; |
673 | ++ pid_t pid; |
674 | + }; |
675 | + |
676 | + /* Struct for handling ancillary data, i e, extra data that can be sent together with a message |
677 | +diff --git a/src/pulsecore/iochannel.c b/src/pulsecore/iochannel.c |
678 | +index 8973375..cd0b81d 100644 |
679 | +--- a/src/pulsecore/iochannel.c |
680 | ++++ b/src/pulsecore/iochannel.c |
681 | +@@ -323,6 +323,7 @@ ssize_t pa_iochannel_write_with_creds(pa_iochannel*io, const void*data, size_t l |
682 | + |
683 | + u = (struct ucred*) CMSG_DATA(&cmsg.hdr); |
684 | + |
685 | ++ /* FIXME: Check whether ucred->pid should be used */ |
686 | + u->pid = getpid(); |
687 | + if (ucred) { |
688 | + u->uid = ucred->uid; |
689 | +@@ -445,6 +446,7 @@ ssize_t pa_iochannel_read_with_ancil_data(pa_iochannel*io, void*data, size_t l, |
690 | + |
691 | + ancil_data->creds.gid = u.gid; |
692 | + ancil_data->creds.uid = u.uid; |
693 | ++ ancil_data->creds.pid = u.pid; |
694 | + ancil_data->creds_valid = true; |
695 | + } |
696 | + else if (cmh->cmsg_type == SCM_RIGHTS) { |
697 | +diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c |
698 | +index 67b5c7a..a2f74a0 100644 |
699 | +--- a/src/pulsecore/protocol-native.c |
700 | ++++ b/src/pulsecore/protocol-native.c |
701 | +@@ -2668,6 +2668,13 @@ static void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_ta |
702 | + do_shm = false; |
703 | + |
704 | + #ifdef HAVE_CREDS |
705 | ++ { |
706 | ++ const pa_creds *creds; |
707 | ++ if ((creds = pa_pdispatch_creds(pd))) { |
708 | ++ c->client->creds = *creds; |
709 | ++ c->client->creds_valid = true; |
710 | ++ } |
711 | ++ } |
712 | + if (do_shm) { |
713 | + /* Only enable SHM if both sides are owned by the same |
714 | + * user. This is a security measure because otherwise data |
715 | +@@ -5631,6 +5638,7 @@ typedef struct pa_protocol_native_access_data { |
716 | + } pa_protocol_native_access_data; |
717 | + |
718 | + static void check_access_finish_cb(pa_access_data *data, bool res) { |
719 | ++ uint32_t command, tag; |
720 | + pa_protocol_native_access_data *d = (pa_protocol_native_access_data *) data; |
721 | + pa_native_connection *c = PA_NATIVE_CONNECTION(d->userdata); |
722 | + |
723 | +@@ -5639,6 +5647,11 @@ static void check_access_finish_cb(pa_access_data *data, bool res) { |
724 | + goto finish; |
725 | + } |
726 | + |
727 | ++ pa_assert_se(pa_tagstruct_getu32(d->tc, &command) >= 0); |
728 | ++ pa_assert_se(pa_tagstruct_getu32(d->tc, &tag) >= 0); |
729 | ++ pa_assert(command == d->command); |
730 | ++ pa_assert(tag == d->tag); |
731 | ++ |
732 | + /* call the dispatcher again, hopefully this time, the access check will |
733 | + * fail or succeed immediately */ |
734 | + command_table[d->command](d->pd, d->command, d->tag, d->tc, d->userdata); |
735 | 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 |
736 | deleted file mode 100644 |
737 | index 311a0fe..0000000 |
738 | --- a/debian/patches/0410-Add-thread-to-activate-trust-store-interface.patch |
739 | +++ /dev/null |
740 | @@ -1,108 +0,0 @@ |
741 | -From 90dbf1d25e12b9cfc86aecbd02f14a05fd5dca8c Mon Sep 17 00:00:00 2001 |
742 | -From: Alfonso Sanchez-Beato <alfonso.sanchez-beato@canonical.com> |
743 | -Date: Fri, 7 Aug 2015 09:08:02 +0200 |
744 | -Subject: [PATCH] Add thread to activate trust-store interface |
745 | - |
746 | ---- |
747 | - src/modules/trust-store/module-trust-store.c | 14 +------------- |
748 | - src/modules/trust-store/truststore.cc | 19 +++++++++++++++++++ |
749 | - 2 files changed, 20 insertions(+), 13 deletions(-) |
750 | - |
751 | -Index: pulseaudio/src/modules/trust-store/module-trust-store.c |
752 | -=================================================================== |
753 | ---- pulseaudio.orig/src/modules/trust-store/module-trust-store.c |
754 | -+++ pulseaudio/src/modules/trust-store/module-trust-store.c |
755 | -@@ -28,6 +28,7 @@ |
756 | - #include <pulsecore/fdsem.h> |
757 | - #include <pulsecore/thread.h> |
758 | - #include <pulsecore/core-util.h> |
759 | -+#include <pulsecore/dynarray.h> |
760 | - #include <pulse/mainloop-api.h> |
761 | - |
762 | - #include "module-trust-store-symdef.h" |
763 | -@@ -173,18 +174,6 @@ static pa_hook_result_t connect_record_h |
764 | - return PA_HOOK_CANCEL; |
765 | - } |
766 | - |
767 | --/* Test code - remove from final product */ |
768 | --static void test(struct userdata *u) { |
769 | -- uint32_t dummy; |
770 | -- pa_client *client; |
771 | -- |
772 | -- PA_IDXSET_FOREACH(client, u->core->clients, dummy) { |
773 | -- if (client->creds_valid) |
774 | -- pa_trust_store_check(u->ts, "The evil app", client->creds.uid, |
775 | -- client->creds.pid, "%1% wants to eat your laundry."); |
776 | -- } |
777 | --} |
778 | -- |
779 | - int pa__init(pa_module *m) { |
780 | - struct userdata *u; |
781 | - pa_trust_store *ts = pa_trust_store_new(); |
782 | -@@ -204,7 +193,6 @@ int pa__init(pa_module *m) { |
783 | - pa_fdsem_get(u->fdsem), PA_IO_EVENT_INPUT, check_fdsem, u)); |
784 | - pa_fdsem_before_poll(u->fdsem); |
785 | - |
786 | -- test(u); |
787 | - return 0; |
788 | - } |
789 | - |
790 | -Index: pulseaudio/src/modules/trust-store/truststore.cc |
791 | -=================================================================== |
792 | ---- pulseaudio.orig/src/modules/trust-store/truststore.cc |
793 | -+++ pulseaudio/src/modules/trust-store/truststore.cc |
794 | -@@ -4,6 +4,7 @@ |
795 | - |
796 | - #include <memory> |
797 | - #include <core/dbus/bus.h> |
798 | -+#include <core/dbus/asio/executor.h> |
799 | - #include <core/trust/dbus_agent.h> |
800 | - #include <core/trust/agent.h> |
801 | - |
802 | -@@ -13,6 +14,7 @@ PA_C_DECL_BEGIN |
803 | - #include <pulsecore/core-util.h> |
804 | - #include <pulse/xmalloc.h> |
805 | - #include <pulsecore/log.h> |
806 | -+#include <pulsecore/thread.h> |
807 | - |
808 | - #include "truststore.h" |
809 | - PA_C_DECL_END |
810 | -@@ -20,14 +22,27 @@ PA_C_DECL_END |
811 | - class TrustStore { |
812 | - public: |
813 | - std::shared_ptr<core::trust::Agent> agent; |
814 | -+ std::shared_ptr<core::dbus::Bus> bus; |
815 | -+ pa_thread *thread; |
816 | - }; |
817 | - |
818 | -+static void thread_func(void *data) { |
819 | -+ class TrustStore *ts = (class TrustStore *) data; |
820 | -+ |
821 | -+ ts->bus->run(); |
822 | -+} |
823 | -+ |
824 | - pa_trust_store* pa_trust_store_new() { |
825 | - try { |
826 | - auto bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session); |
827 | -+ bus->install_executor(core::dbus::asio::make_executor(bus)); |
828 | -+ |
829 | - auto agent = core::trust::dbus::create_multi_user_agent_for_bus_connection(bus, "PulseAudio"); |
830 | - auto ts = new TrustStore(); |
831 | - ts->agent = agent; |
832 | -+ ts->bus = bus; |
833 | -+ ts->thread = pa_thread_new("trust-store-bus", thread_func, ts); |
834 | -+ |
835 | - return (pa_trust_store *) ts; |
836 | - } catch(const std::exception &e) { |
837 | - pa_log_error("Could not create Ubuntu touch trust store connection: %s", |
838 | -@@ -41,6 +56,10 @@ pa_trust_store* pa_trust_store_new() { |
839 | - void pa_trust_store_free(pa_trust_store *t) { |
840 | - pa_assert(t != NULL); |
841 | - auto ts = (TrustStore*) t; |
842 | -+ if (ts->thread) { |
843 | -+ ts->bus->stop(); |
844 | -+ pa_thread_free(ts->thread); |
845 | -+ } |
846 | - delete ts; |
847 | - } |
848 | - |
849 | diff --git a/debian/patches/0417-increase-timeout-check-apparmor.patch b/debian/patches/0417-increase-timeout-check-apparmor.patch |
850 | deleted file mode 100644 |
851 | index 9571a8a..0000000 |
852 | --- a/debian/patches/0417-increase-timeout-check-apparmor.patch |
853 | +++ /dev/null |
854 | @@ -1,102 +0,0 @@ |
855 | -Index: pulseaudio/src/modules/trust-store/module-trust-store.c |
856 | -=================================================================== |
857 | ---- pulseaudio.orig/src/modules/trust-store/module-trust-store.c |
858 | -+++ pulseaudio/src/modules/trust-store/module-trust-store.c |
859 | -@@ -31,6 +31,8 @@ |
860 | - #include <pulsecore/dynarray.h> |
861 | - #include <pulse/mainloop-api.h> |
862 | - |
863 | -+#include <sys/apparmor.h> |
864 | -+ |
865 | - #include "module-trust-store-symdef.h" |
866 | - |
867 | - PA_MODULE_AUTHOR("David Henningsson"); |
868 | -@@ -77,9 +79,37 @@ static struct per_client *per_client_new |
869 | - } |
870 | - |
871 | - pc = pa_xnew0(struct per_client, 1); |
872 | -- // TODO: Do we need something else than the application name here - |
873 | -- // the application can probably spoof it |
874 | -- pc->appname = pa_xstrdup(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); |
875 | -+ |
876 | -+ pa_log_info("Client application name is: '%s'", pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)); |
877 | -+ |
878 | -+ // ask apparmor about the context of the client, later this will be used to decide if the |
879 | -+ // app should be or not using the trust store |
880 | -+ |
881 | -+ if (aa_is_enabled()) { |
882 | -+ char *context = NULL; |
883 | -+ char *mode = NULL; |
884 | -+ |
885 | -+ /* XXX: The mode string is *NOT* be freed since it forms |
886 | -+ * part of the allocation returned in context. |
887 | -+ * |
888 | -+ * See aa_gettaskcon(2) for details. |
889 | -+ */ |
890 | -+ if (aa_gettaskcon (client->creds.pid, &context, &mode) >= 0) { |
891 | -+ pc->appname = pa_xstrdup(context); |
892 | -+ pa_log_info("Client apparmor profile is: '%s'", context); |
893 | -+ } else { |
894 | -+ pa_log_error("AppArmo profile could not be retrieved."); |
895 | -+ } |
896 | -+ |
897 | -+ if (context) |
898 | -+ free(context); |
899 | -+ |
900 | -+ } else { |
901 | -+ // while we do leave the appname as empty if we fail, if apparmor is not present we should |
902 | -+ // assume that the app is not confined |
903 | -+ pc->appname = pa_xstrdup("unconfined"); |
904 | -+ } |
905 | -+ |
906 | - pc->index = client_index; |
907 | - pc->uid = client->creds.uid; |
908 | - pc->pid = client->creds.pid; |
909 | -@@ -103,13 +133,28 @@ static void per_client_free_from_hashmap |
910 | - static void thread_func(void *data) { |
911 | - struct per_client *pc = data; |
912 | - |
913 | -- bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, |
914 | -- /// TRANSLATORS: The app icon and name appears above this string. If the phrase |
915 | -- /// can't be translated in this language, translate the whole sentence |
916 | -- /// 'This app wants to record audio.' |
917 | -- _("wants to record audio.")); |
918 | -+ // there are 3 possible values for the app name that we will consider at this point |
919 | -+ // * empty string: there was an error when retrieving the value and therefore we will |
920 | -+ // automatically deny access |
921 | -+ // * unconfined: the app is unconfined and therefore we will automatically grant access |
922 | -+ // * appname: we need the user to decide what to do. |
923 | -+ |
924 | -+ if (pc->appname == NULL) { |
925 | -+ pa_log_info("Client apparmor could not retrieved."); |
926 | -+ pa_atomic_store(&pc->result, REQUEST_DENIED); |
927 | -+ } else if (pa_streq(pc->appname, "unconfined")) { |
928 | -+ pa_log_info("Connected client is unconfined."); |
929 | -+ pa_atomic_store(&pc->result, REQUEST_GRANTED); |
930 | -+ } else { |
931 | -+ pa_log_info("User needs to authorize the application.."); |
932 | -+ bool result = pa_trust_store_check(pc->userdata->ts, pc->appname, pc->uid, pc->pid, |
933 | -+ /// TRANSLATORS: The app icon and name appears above this string. If the phrase |
934 | -+ /// can't be translated in this language, translate the whole sentence |
935 | -+ /// 'This app wants to record audio.' |
936 | -+ _("wants to record audio.")); |
937 | -+ pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); |
938 | -+ } |
939 | - |
940 | -- pa_atomic_store(&pc->result, result ? REQUEST_GRANTED : REQUEST_DENIED); |
941 | - pa_fdsem_post(pc->userdata->fdsem); |
942 | - } |
943 | - |
944 | -Index: pulseaudio/src/Makefile.am |
945 | -=================================================================== |
946 | ---- pulseaudio.orig/src/Makefile.am |
947 | -+++ pulseaudio/src/Makefile.am |
948 | -@@ -2096,7 +2096,7 @@ libtruststore_util_la_LIBADD = libpulsec |
949 | - libtruststore_util_la_LDFLAGS = -avoid-version |
950 | - |
951 | - module_trust_store_la_SOURCES = modules/trust-store/module-trust-store.c |
952 | --module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) |
953 | -+module_trust_store_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor |
954 | - module_trust_store_la_LIBADD = $(MODULE_LIBADD) libtruststore-util.la |
955 | - module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 |
956 | - endif |
957 | diff --git a/debian/patches/0700-modules-add-snappy-policy-module.patch b/debian/patches/0700-modules-add-snappy-policy-module.patch |
958 | index 774374d..03d61b6 100644 |
959 | --- a/debian/patches/0700-modules-add-snappy-policy-module.patch |
960 | +++ b/debian/patches/0700-modules-add-snappy-policy-module.patch |
961 | @@ -1,68 +1,107 @@ |
962 | -From a430ebc2271f5a07389ee25631a8ba5524371764 Mon Sep 17 00:00:00 2001 |
963 | -From: Simon Fels <simon.fels@canonical.com> |
964 | -Date: Tue, 17 May 2016 17:29:31 +0200 |
965 | -Subject: [PATCH] modules: add snappy policy module |
966 | +From: James Henstridge <james.henstridge@canonical.com> |
967 | +Date: Tue, 7 Aug 2018 12:40:59 +0800 |
968 | +Subject: [PATCH] modules: add snap policy module |
969 | |
970 | +Forwarded: not-needed |
971 | + |
972 | +This patch allows pulseaudio to limit audio recording to snaps with |
973 | +the audio-recording interface connected. We will not pursue upstreaming |
974 | +this patch as the longer term solution will probably use PipeWire. |
975 | + |
976 | +Co-authored-by: Simon Fels <simon.fels@canonical.com> |
977 | --- |
978 | - configure.ac | 18 +++++++- |
979 | - src/Makefile.am | 18 ++++++++ |
980 | - src/modules/module-snappy-policy.c | 94 ++++++++++++++++++++++++++++++++++++++ |
981 | - 3 files changed, 129 insertions(+), 1 deletion(-) |
982 | - create mode 100644 src/modules/module-snappy-policy.c |
983 | + configure.ac | 17 ++ |
984 | + src/Makefile.am | 13 ++ |
985 | + src/modules/module-snap-policy.c | 384 +++++++++++++++++++++++++++++++++++++++ |
986 | + 3 files changed, 414 insertions(+) |
987 | + create mode 100644 src/modules/module-snap-policy.c |
988 | |
989 | -Index: pulseaudio/src/Makefile.am |
990 | -=================================================================== |
991 | ---- pulseaudio.orig/src/Makefile.am |
992 | -+++ pulseaudio/src/Makefile.am |
993 | -@@ -1241,6 +1241,11 @@ modlibexec_LTLIBRARIES += \ |
994 | +diff --git a/configure.ac b/configure.ac |
995 | +index 77b5ff5..34b58ef 100644 |
996 | +--- a/configure.ac |
997 | ++++ b/configure.ac |
998 | +@@ -1412,6 +1412,21 @@ AS_IF([test "x$os_is_win32" != "x1"], |
999 | + AS_IF([test "x$ax_pthread_ok" = "xyes"], |
1000 | + AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) |
1001 | + |
1002 | ++# Snappy support |
1003 | ++ |
1004 | ++AC_ARG_ENABLE([snap], |
1005 | ++ AS_HELP_STRING([--enable-snap], [Enable snap support])) |
1006 | ++ |
1007 | ++have_apparmor=0 |
1008 | ++have_snapd_glib=0 |
1009 | ++AS_IF([test "x$enable_snap" != "xno"], |
1010 | ++ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [have_apparmor=1]) |
1011 | ++ PKG_CHECK_MODULES(SNAPD_GLIB, [snapd-glib], [have_snapd_glib=1])]) |
1012 | ++ |
1013 | ++AS_IF([test "x$enable_snap" = "xyes" && test "x$have_apparmor" = "x0" -o "x$have-snapd_glib" = "x0"], |
1014 | ++ [AC_MSG_ERROR([*** Snap module dependencies missing])]) |
1015 | ++ |
1016 | ++AM_CONDITIONAL([BUILD_SNAP], [test "x$enable_snap" = "xyes"]) |
1017 | + |
1018 | + |
1019 | + ################################### |
1020 | +@@ -1588,6 +1603,7 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x$USE_PER_USER_ESOUND_SOCKET" = "x1"], EN |
1021 | + AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no) |
1022 | + AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no) |
1023 | + AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no) |
1024 | ++AS_IF([test "x$enable_snap" != "xno"], ENABLE_SNAP=yes, ENABLE_SNAP=no) |
1025 | + |
1026 | + echo " |
1027 | + ---{ $PACKAGE_NAME $VERSION }--- |
1028 | +@@ -1643,6 +1659,7 @@ echo " |
1029 | + Enable speex (resampler, AEC): ${ENABLE_SPEEX} |
1030 | + Enable soxr (resampler): ${ENABLE_SOXR} |
1031 | + Enable WebRTC echo canceller: ${ENABLE_WEBRTC} |
1032 | ++ Enable Snap support: ${ENABLE_SNAP} |
1033 | + Enable gcov coverage: ${ENABLE_GCOV} |
1034 | + Enable unit tests: ${ENABLE_TESTS} |
1035 | + Database |
1036 | +diff --git a/src/Makefile.am b/src/Makefile.am |
1037 | +index d49a6af..eb32f56 100644 |
1038 | +--- a/src/Makefile.am |
1039 | ++++ b/src/Makefile.am |
1040 | +@@ -1232,6 +1232,11 @@ modlibexec_LTLIBRARIES += \ |
1041 | module-esound-sink.la |
1042 | endif |
1043 | |
1044 | -+if HAVE_APPARMOR |
1045 | ++if BUILD_SNAP |
1046 | +modlibexec_LTLIBRARIES += \ |
1047 | -+ module-snappy-policy.la |
1048 | ++ module-snap-policy.la |
1049 | +endif |
1050 | + |
1051 | # See comment at librtp.la above |
1052 | if !OS_IS_WIN32 |
1053 | modlibexec_LTLIBRARIES += \ |
1054 | -@@ -1582,6 +1587,11 @@ SYMDEF_FILES += \ |
1055 | - module-esound-sink-symdef.h |
1056 | - endif |
1057 | - |
1058 | -+if HAVE_APPARMOR |
1059 | -+SYMDEF_FILES += \ |
1060 | -+ module-snappy-policy-symdef.h |
1061 | -+endif |
1062 | -+ |
1063 | - EXTRA_DIST += $(SYMDEF_FILES) |
1064 | - BUILT_SOURCES += $(SYMDEF_FILES) builddirs |
1065 | - |
1066 | -@@ -2101,6 +2111,14 @@ module_trust_store_la_LIBADD = $(MODULE_ |
1067 | - module_trust_store_la_CFLAGS = $(AM_CFLAGS) -DHAVE_TRUST_STORE=1 |
1068 | +@@ -2077,6 +2082,14 @@ module_echo_cancel_la_CFLAGS += -DHAVE_WEBRTC=1 |
1069 | + module_echo_cancel_la_LIBADD += libwebrtc-util.la |
1070 | endif |
1071 | |
1072 | +# Snappy |
1073 | -+if HAVE_APPARMOR |
1074 | -+module_snappy_policy_la_SOURCES = modules/module-snappy-policy.c |
1075 | -+module_snappy_policy_la_LDFLAGS = $(MODULE_LDFLAGS) -lapparmor |
1076 | -+module_snappy_policy_la_LIBADD = $(MODULE_LIBADD) |
1077 | -+module_snappy_policy_la_CFLAGS = $(AM_CFLAGS) -DHAVE_SNAPPY=1 |
1078 | ++if BUILD_SNAP |
1079 | ++module_snap_policy_la_SOURCES = modules/module-snap-policy.c |
1080 | ++module_snap_policy_la_LDFLAGS = $(MODULE_LDFLAGS) |
1081 | ++module_snap_policy_la_LIBADD = $(MODULE_LIBADD) $(APPARMOR_LIBS) $(SNAPD_GLIB_LIBS) |
1082 | ++module_snap_policy_la_CFLAGS = $(AM_CFLAGS) $(APPARMOR_CFLAGS) $(SNAPD_GLIB_CFLAGS) -DPA_MODULE_NAME=module_snap_policy |
1083 | +endif |
1084 | + |
1085 | # RTP modules |
1086 | module_rtp_send_la_SOURCES = modules/rtp/module-rtp-send.c |
1087 | module_rtp_send_la_LDFLAGS = $(MODULE_LDFLAGS) |
1088 | -Index: pulseaudio/src/modules/module-snappy-policy.c |
1089 | -=================================================================== |
1090 | +diff --git a/src/modules/module-snap-policy.c b/src/modules/module-snap-policy.c |
1091 | +new file mode 100644 |
1092 | +index 0000000..0a1f5f4 |
1093 | --- /dev/null |
1094 | -+++ pulseaudio/src/modules/module-snappy-policy.c |
1095 | -@@ -0,0 +1,98 @@ |
1096 | ++++ b/src/modules/module-snap-policy.c |
1097 | +@@ -0,0 +1,384 @@ |
1098 | +/*** |
1099 | + This file is part of PulseAudio. |
1100 | + |
1101 | -+ Copyright 2016 Canonical Ltd. |
1102 | -+ Written by Simon Fels <simon.fels@canonical.com> |
1103 | ++ Copyright 2018 Canonical Ltd. |
1104 | ++ Authors: |
1105 | ++ Simon Fels <simon.fels@canonical.com> |
1106 | ++ James Henstridge <james.henstridge@canonical.com> |
1107 | + |
1108 | + PulseAudio is free software; you can redistribute it and/or modify |
1109 | + it under the terms of the GNU Lesser General Public License as published |
1110 | @@ -83,118 +122,360 @@ Index: pulseaudio/src/modules/module-snappy-policy.c |
1111 | +#endif |
1112 | + |
1113 | +#include <sys/apparmor.h> |
1114 | -+#include <errno.h> |
1115 | ++#include <glib.h> |
1116 | ++#include <snapd-glib/snapd-glib.h> |
1117 | + |
1118 | -+#include <pulsecore/i18n.h> |
1119 | ++#include <pulsecore/asyncq.h> |
1120 | +#include <pulsecore/core.h> |
1121 | ++#include <pulsecore/idxset.h> |
1122 | ++#include <pulsecore/hashmap.h> |
1123 | +#include <pulsecore/module.h> |
1124 | -+#include <pulse/xmalloc.h> |
1125 | -+#include <pulsecore/fdsem.h> |
1126 | ++#include <pulsecore/mutex.h> |
1127 | +#include <pulsecore/thread.h> |
1128 | -+#include <pulsecore/core-util.h> |
1129 | +#include <pulse/mainloop-api.h> |
1130 | -+ |
1131 | -+#include "module-snappy-policy-symdef.h" |
1132 | ++#include <pulse/xmalloc.h> |
1133 | + |
1134 | +#define SNAP_LABEL_PREFIX "snap." |
1135 | +#define SNAP_LABEL_PREFIX_LENGTH 5 |
1136 | + |
1137 | -+PA_MODULE_AUTHOR("Simon Fels"); |
1138 | -+PA_MODULE_DESCRIPTION("Ubuntu Snappy policy management"); |
1139 | ++PA_MODULE_AUTHOR("Canonical Ltd"); |
1140 | ++PA_MODULE_DESCRIPTION("Ubuntu Snap policy management"); |
1141 | +PA_MODULE_VERSION(PACKAGE_VERSION); |
1142 | +PA_MODULE_LOAD_ONCE(true); |
1143 | + |
1144 | ++struct per_client { |
1145 | ++ struct userdata *userdata; |
1146 | ++ uint32_t index; |
1147 | ++ char *snap_name; |
1148 | ++ pa_dynarray *pending_requests; /* of pa_access_data */ |
1149 | ++ bool completed; |
1150 | ++ bool grant_access; |
1151 | ++}; |
1152 | ++ |
1153 | +struct userdata { |
1154 | -+ pa_core *core; |
1155 | ++ pa_io_event *io_event; |
1156 | + pa_hook_slot *connect_hook_slot; |
1157 | ++ |
1158 | ++ pa_thread *thread; |
1159 | ++ pa_mutex *mutex; |
1160 | ++ pa_cond *cond; |
1161 | ++ |
1162 | ++ pa_hashmap *clients; /* int => struct per_client */ |
1163 | ++ pa_asyncq *results; /* of struct per_client */ |
1164 | ++ |
1165 | ++ /* Data owned by glib thread */ |
1166 | ++ GMainContext *main_context; |
1167 | ++ GMainLoop *main_loop; |
1168 | ++ GCancellable *cancellable; |
1169 | ++ SnapdClient *snapd; |
1170 | +}; |
1171 | + |
1172 | -+static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, struct userdata *u) { |
1173 | -+ pa_client *client = pa_idxset_get_by_index(u->core->clients, d->client_index); |
1174 | -+ if (!client) |
1175 | -+ return PA_HOOK_OK; |
1176 | -+ |
1177 | -+ char *label = NULL; |
1178 | -+ char *mode = NULL; |
1179 | -+ if (aa_gettaskcon(client->creds.pid, &label, &mode) < 0) { |
1180 | -+ pa_log_warn("Failed to retrieve apparmor security label for pid %u: %s", |
1181 | -+ client->creds.pid, strerror(-errno)); |
1182 | -+ return PA_HOOK_OK; |
1183 | ++/* ---- Code running in glib thread ---- */ |
1184 | ++ |
1185 | ++static void complete_check_access(struct per_client *pc, bool grant_access) |
1186 | ++{ |
1187 | ++ struct userdata *u = pc->userdata; |
1188 | ++ |
1189 | ++ pa_mutex_lock(u->mutex); |
1190 | ++ pc->grant_access = grant_access; |
1191 | ++ pc->completed = true; |
1192 | ++ pa_asyncq_push(u->results, pc, true); |
1193 | ++ pa_mutex_unlock(u->mutex); |
1194 | ++} |
1195 | ++ |
1196 | ++static void get_interfaces_finished(GObject *source_object, |
1197 | ++ GAsyncResult *result, |
1198 | ++ gpointer user_data) |
1199 | ++{ |
1200 | ++ struct per_client *pc = user_data; |
1201 | ++ struct userdata *u = pc->userdata; |
1202 | ++ bool grant_access = false; |
1203 | ++ g_autoptr(GError) error = NULL; |
1204 | ++ g_autoptr(GPtrArray) plugs = NULL; |
1205 | ++ unsigned i; |
1206 | ++ |
1207 | ++ if (!snapd_client_get_interfaces_finish(u->snapd, result, &plugs, NULL, &error)) { |
1208 | ++ pa_log_warn("snapd_client_get_interfaces failed: %s", error->message); |
1209 | ++ goto end; |
1210 | ++ } |
1211 | ++ |
1212 | ++ /* determine pc->grant_access */ |
1213 | ++ for (i = 0; i < plugs->len; i++) { |
1214 | ++ SnapdPlug *plug = plugs->pdata[i]; |
1215 | ++ const char *snap_name = snapd_plug_get_snap(plug); |
1216 | ++ const char *iface = snapd_plug_get_interface(plug); |
1217 | ++ const bool connected = snapd_plug_get_connections(plug)->len != 0; |
1218 | ++ |
1219 | ++ /* We are only interested in connected plugs of our snap */ |
1220 | ++ if (!connected || strcmp(snap_name, pc->snap_name) != 0) { |
1221 | ++ continue; |
1222 | ++ } |
1223 | ++ if (!strcmp(iface, "pulseaudio") || !strcmp(iface, "audio-record")) { |
1224 | ++ grant_access = true; |
1225 | ++ break; |
1226 | ++ } |
1227 | ++ } |
1228 | ++ |
1229 | ++end: |
1230 | ++ complete_check_access(pc, grant_access); |
1231 | ++} |
1232 | ++ |
1233 | ++static void get_snap_finished(GObject *source_object, |
1234 | ++ GAsyncResult *result, |
1235 | ++ gpointer user_data) |
1236 | ++{ |
1237 | ++ struct per_client *pc = user_data; |
1238 | ++ struct userdata *u = pc->userdata; |
1239 | ++ g_autoptr(GError) error = NULL; |
1240 | ++ g_autoptr(SnapdSnap) snap = NULL; |
1241 | ++ |
1242 | ++ snap = snapd_client_get_snap_finish(u->snapd, result, &error); |
1243 | ++ if (!snap) { |
1244 | ++ pa_log_warn("snapd_client_get_snap failed: %s", error->message); |
1245 | ++ complete_check_access(pc, false); |
1246 | ++ return; |
1247 | ++ } |
1248 | ++ |
1249 | ++ /* Snaps using classic confinement are granted access */ |
1250 | ++ if (snapd_snap_get_confinement(snap) == SNAPD_CONFINEMENT_CLASSIC) { |
1251 | ++ complete_check_access(pc, true); |
1252 | ++ return; |
1253 | ++ } |
1254 | ++ |
1255 | ++ /* We have a non-classic snap, we need to check its connected |
1256 | ++ * interfaces */ |
1257 | ++ snapd_client_get_interfaces_async(u->snapd, u->cancellable, |
1258 | ++ get_interfaces_finished, pc); |
1259 | ++} |
1260 | ++ |
1261 | ++ |
1262 | ++static gboolean check_access(void *data) |
1263 | ++{ |
1264 | ++ struct per_client *pc = data; |
1265 | ++ struct userdata *u = pc->userdata; |
1266 | ++ |
1267 | ++ snapd_client_get_snap_async(u->snapd, pc->snap_name, u->cancellable, |
1268 | ++ get_snap_finished, pc); |
1269 | ++ return G_SOURCE_REMOVE; |
1270 | ++} |
1271 | ++ |
1272 | ++ |
1273 | ++static void thread_main(void *data) { |
1274 | ++ struct userdata *u = data; |
1275 | ++ |
1276 | ++ pa_mutex_lock(u->mutex); |
1277 | ++ |
1278 | ++ u->main_context = g_main_context_new(); |
1279 | ++ g_main_context_push_thread_default(u->main_context); |
1280 | ++ u->main_loop = g_main_loop_new(u->main_context, false); |
1281 | ++ u->cancellable = g_cancellable_new(); |
1282 | ++ u->snapd = snapd_client_new(); |
1283 | ++ |
1284 | ++ /* Signal main thread that we've finished initialisation */ |
1285 | ++ pa_cond_signal(u->cond, false); |
1286 | ++ pa_mutex_unlock(u->mutex); |
1287 | ++ |
1288 | ++ pa_log_info("Starting GLib main loop"); |
1289 | ++ g_main_loop_run(u->main_loop); |
1290 | ++ pa_log_info("GLib main loop exited"); |
1291 | ++ |
1292 | ++ g_cancellable_cancel(u->cancellable); |
1293 | ++ |
1294 | ++ g_clear_object(&u->snapd); |
1295 | ++ g_clear_object(&u->cancellable); |
1296 | ++ g_main_context_pop_thread_default(u->main_context); |
1297 | ++ g_clear_pointer(&u->main_loop, g_main_loop_unref); |
1298 | ++ g_clear_pointer(&u->main_context, g_main_context_unref); |
1299 | ++} |
1300 | ++ |
1301 | ++static gboolean thread_quit(void *data) |
1302 | ++{ |
1303 | ++ struct userdata *u = data; |
1304 | ++ |
1305 | ++ g_main_loop_quit(u->main_loop); |
1306 | ++ return G_SOURCE_REMOVE; |
1307 | ++} |
1308 | ++ |
1309 | ++/* ---- Code running in main thread ---- */ |
1310 | ++ |
1311 | ++static struct per_client *per_client_new(struct userdata *u, |
1312 | ++ uint32_t client_index, |
1313 | ++ char *snap_name) { |
1314 | ++ struct per_client *pc = pa_xnew0(struct per_client, 1); |
1315 | ++ pc->userdata = u; |
1316 | ++ pc->index = client_index; |
1317 | ++ pc->snap_name = snap_name; |
1318 | ++ pc->pending_requests = pa_dynarray_new(NULL); |
1319 | ++ pc->completed = false; |
1320 | ++ pc->grant_access = false; |
1321 | ++ return pc; |
1322 | ++} |
1323 | ++ |
1324 | ++static void per_client_free(struct per_client *pc) { |
1325 | ++ if (!pc) return; |
1326 | ++ pa_xfree(pc->snap_name); |
1327 | ++ pa_dynarray_free(pc->pending_requests); |
1328 | ++ pa_xfree(pc); |
1329 | ++} |
1330 | ++ |
1331 | ++static char *client_get_snap_name(pa_core *core, uint32_t client_index) { |
1332 | ++ pa_client *client; |
1333 | ++ char *context = NULL; |
1334 | ++ char *snap_name = NULL; |
1335 | ++ char *dot; |
1336 | ++ |
1337 | ++ client = pa_idxset_get_by_index(core->clients, client_index); |
1338 | ++ pa_assert(client != NULL); |
1339 | ++ if (!client->creds_valid) { |
1340 | ++ pa_log_warn("Client %u has no creds, cannot authenticate", client_index); |
1341 | ++ goto end; |
1342 | ++ } |
1343 | ++ |
1344 | ++ /* If AppArmor is not enabled, then we can't identify the client */ |
1345 | ++ if (!aa_is_enabled()) { |
1346 | ++ goto end; |
1347 | + } |
1348 | ++ if (aa_gettaskcon(client->creds.pid, &context, NULL) < 0) { |
1349 | ++ pa_log_error("AppArmor profile could not be retrieved."); |
1350 | ++ goto end; |
1351 | ++ } |
1352 | ++ |
1353 | ++ /* If the AppArmor context does not begin with "snap.", then this |
1354 | ++ * is not a snap */ |
1355 | ++ if (strncmp(context, SNAP_LABEL_PREFIX, SNAP_LABEL_PREFIX_LENGTH) != 0) { |
1356 | ++ goto end; |
1357 | ++ } |
1358 | ++ |
1359 | ++ dot = strchr(context+SNAP_LABEL_PREFIX_LENGTH, '.'); |
1360 | ++ if (dot == NULL) { |
1361 | ++ pa_log_warn("Malformed snapd AppArmor profile name: %s", context); |
1362 | ++ goto end; |
1363 | ++ } |
1364 | ++ snap_name = pa_xstrndup(context+5, dot-context-SNAP_LABEL_PREFIX_LENGTH); |
1365 | ++ |
1366 | ++end: |
1367 | ++ free(context); |
1368 | ++ return snap_name; |
1369 | ++} |
1370 | ++ |
1371 | ++static pa_hook_result_t connect_record_hook(pa_core *core, pa_access_data *d, |
1372 | ++ struct userdata *u) { |
1373 | ++ pa_hook_result_t result = PA_HOOK_STOP; |
1374 | ++ struct per_client *pc = NULL; |
1375 | ++ char *snap_name = NULL; |
1376 | + |
1377 | -+ pa_hook_result_t decision = PA_HOOK_OK; |
1378 | ++ pa_mutex_lock(u->mutex); |
1379 | ++ pc = pa_hashmap_get(u->clients, (void *)(size_t)d->client_index); |
1380 | ++ if (pc) { |
1381 | ++ if (pc->completed) { |
1382 | ++ result = pc->grant_access ? PA_HOOK_OK : PA_HOOK_STOP; |
1383 | ++ } else { |
1384 | ++ /* A permission check for this snap is currently in progress */ |
1385 | ++ pa_dynarray_append(pc->pending_requests, d); |
1386 | ++ result = PA_HOOK_CANCEL; |
1387 | ++ } |
1388 | ++ goto end; |
1389 | ++ } |
1390 | ++ |
1391 | ++ snap_name = client_get_snap_name(core, d->client_index); |
1392 | ++ if (!snap_name) { |
1393 | ++ /* Not a snap, so allow access */ |
1394 | ++ result = PA_HOOK_OK; |
1395 | ++ goto end; |
1396 | ++ } |
1397 | ++ |
1398 | ++ /* create new per client struct, and submit to glib thread */ |
1399 | ++ pc = per_client_new(u, d->client_index, snap_name); |
1400 | ++ pa_dynarray_append(pc->pending_requests, d); |
1401 | ++ pa_hashmap_put(u->clients, (void *) (size_t) d->client_index, pc); |
1402 | ++ pa_log_info("Checking access for client %d (%s)", pc->index, pc->snap_name); |
1403 | ++ g_main_context_invoke(u->main_context, check_access, pc); |
1404 | + |
1405 | -+ // We only cancel the attempt of the client to start audio recording |
1406 | -+ // when we could successfully determine that the request is coming |
1407 | -+ // from an app which is part of a snap. Otherwise we continue to |
1408 | -+ // work as normal. |
1409 | -+ if (label && strncmp(SNAP_LABEL_PREFIX, label, SNAP_LABEL_PREFIX_LENGTH) == 0) |
1410 | -+ decision = PA_HOOK_CANCEL; |
1411 | ++ result = PA_HOOK_CANCEL; |
1412 | + |
1413 | -+ free(label); |
1414 | ++end: |
1415 | ++ pa_mutex_unlock(u->mutex); |
1416 | ++ return result; |
1417 | ++} |
1418 | ++ |
1419 | ++static void deliver_result(struct userdata *u, struct per_client *pc) { |
1420 | ++ pa_access_data *ad; |
1421 | ++ unsigned i; |
1422 | + |
1423 | -+ return decision; |
1424 | ++ pa_log_info("Access check for client %u (%s): %d", |
1425 | ++ pc->index, pc->snap_name, pc->grant_access); |
1426 | ++ |
1427 | ++ /* Call the hooks without holding the mutex, since this will |
1428 | ++ * recurse into connect_record_hook. Access to pending_requests |
1429 | ++ * should be safe here, since connect_record_hook wont alter the |
1430 | ++ * array when the access check is complete. */ |
1431 | ++ PA_DYNARRAY_FOREACH(ad, pc->pending_requests, i) { |
1432 | ++ ad->async_finish_cb(ad, pc->grant_access); |
1433 | ++ } |
1434 | ++ pa_mutex_lock(u->mutex); |
1435 | ++ pa_hashmap_remove_and_free(u->clients, (void *) (size_t) pc->index); |
1436 | ++ pa_mutex_unlock(u->mutex); |
1437 | ++} |
1438 | ++ |
1439 | ++static void check_result(pa_mainloop_api *a, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) { |
1440 | ++ struct userdata *u = userdata; |
1441 | ++ struct per_client *pc; |
1442 | ++ |
1443 | ++ pa_asyncq_read_after_poll(u->results); |
1444 | ++ while ((pc = pa_asyncq_pop(u->results, false)) != NULL) { |
1445 | ++ deliver_result(u, pc); |
1446 | ++ } |
1447 | ++ pa_asyncq_read_before_poll(u->results); |
1448 | +} |
1449 | + |
1450 | +int pa__init(pa_module *m) { |
1451 | + struct userdata *u; |
1452 | -+ u = pa_xnew0(struct userdata, 1); |
1453 | -+ u->core = m->core; |
1454 | + |
1455 | ++ u = pa_xnew0(struct userdata, 1); |
1456 | + m->userdata = u; |
1457 | -+ u->connect_hook_slot = pa_hook_connect(&m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], |
1458 | -+ PA_HOOK_NORMAL, (pa_hook_cb_t) connect_record_hook, u); |
1459 | ++ u->mutex = pa_mutex_new(false, false); |
1460 | ++ u->cond = pa_cond_new(); |
1461 | ++ |
1462 | ++ u->clients = pa_hashmap_new_full(pa_idxset_trivial_hash_func, |
1463 | ++ pa_idxset_trivial_compare_func, |
1464 | ++ NULL, (pa_free_cb_t) per_client_free); |
1465 | ++ |
1466 | ++ u->results = pa_asyncq_new(0); |
1467 | ++ u->io_event = m->core->mainloop->io_new( |
1468 | ++ m->core->mainloop, pa_asyncq_read_fd(u->results), PA_IO_EVENT_INPUT, |
1469 | ++ check_result, u); |
1470 | ++ pa_asyncq_read_before_poll(u->results); |
1471 | ++ |
1472 | ++ u->connect_hook_slot = pa_hook_connect( |
1473 | ++ &m->core->access[PA_ACCESS_HOOK_CONNECT_RECORD], PA_HOOK_NORMAL, |
1474 | ++ (pa_hook_cb_t) connect_record_hook, u); |
1475 | ++ |
1476 | ++ /* Start glib thread and wait for it to finish initialising. */ |
1477 | ++ pa_mutex_lock(u->mutex); |
1478 | ++ u->thread = pa_thread_new("snapd-glib", thread_main, u); |
1479 | ++ pa_cond_wait(u->cond, u->mutex); |
1480 | ++ pa_mutex_unlock(u->mutex); |
1481 | + |
1482 | + return 0; |
1483 | +} |
1484 | + |
1485 | +void pa__done(pa_module *m) { |
1486 | + struct userdata *u = m->userdata; |
1487 | -+ if (u) { |
1488 | -+ if (u->connect_hook_slot) |
1489 | -+ pa_hook_slot_free(u->connect_hook_slot); |
1490 | -+ pa_xfree(u); |
1491 | -+ } |
1492 | -+} |
1493 | -Index: pulseaudio/configure.ac |
1494 | -=================================================================== |
1495 | ---- pulseaudio.orig/configure.ac |
1496 | -+++ pulseaudio/configure.ac |
1497 | -@@ -1412,6 +1412,19 @@ AS_IF([test "x$os_is_win32" != "x1"], |
1498 | - AS_IF([test "x$ax_pthread_ok" = "xyes"], |
1499 | - AC_DEFINE([_POSIX_PTHREAD_SEMANTICS], 1, [Needed on Solaris])) |
1500 | - |
1501 | -+# Snappy support |
1502 | ++ if (!u) return; |
1503 | + |
1504 | -+AC_ARG_ENABLE([snappy], |
1505 | -+ AS_HELP_STRING([--enable-snappy], [Enable snappy support])) |
1506 | ++ pa_hook_slot_free(u->connect_hook_slot); |
1507 | ++ m->core->mainloop->io_free(u->io_event); |
1508 | + |
1509 | -+AS_IF([test "x$enable_snappy" != "xno"], |
1510 | -+ [PKG_CHECK_MODULES(APPARMOR, [libapparmor], [HAVE_APPARMOR=1], [HAVE_APPARMOR=0])], |
1511 | -+ [HAVE_APPARMOR=0]) |
1512 | ++ /* Stop the glib thread and wait for it to exit */ |
1513 | ++ g_main_context_invoke(u->main_context, thread_quit, u); |
1514 | ++ pa_thread_join(u->thread); |
1515 | ++ pa_thread_free(u->thread); |
1516 | + |
1517 | -+AS_IF([test "x$enable_snappy" = "xyes" && test "x$HAVE_APPARMOR" = "x0"], |
1518 | -+ [AC_MSG_ERROR([*** Apparmor library not found])]) |
1519 | ++ pa_asyncq_free(u->results, NULL); /* items in queue owned by u->clients */ |
1520 | ++ g_clear_pointer(&u->clients, pa_hashmap_free); |
1521 | + |
1522 | -+AM_CONDITIONAL([HAVE_APPARMOR], [test "x$HAVE_APPARMOR" = "x1"]) |
1523 | - |
1524 | - # Ubuntu touch trust store |
1525 | - |
1526 | -@@ -1602,6 +1615,8 @@ AS_IF([test "x$HAVE_ESOUND" = "x1" -a "x |
1527 | - AS_IF([test "x$HAVE_GCOV" = "x1"], ENABLE_GCOV=yes, ENABLE_GCOV=no) |
1528 | - AS_IF([test "x$HAVE_LIBCHECK" = "x1"], ENABLE_TESTS=yes, ENABLE_TESTS=no) |
1529 | - AS_IF([test "x$enable_legacy_database_entry_format" != "xno"], ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=yes, ENABLE_LEGACY_DATABASE_ENTRY_FORMAT=no) |
1530 | -+AS_IF([test "x$enable_snappy" = "x1"], ENABLE_SNAPPY=yes, ENABLE_SNAPPY=no) |
1531 | -+AS_IF([test "x$HAVE_APPARMOR" = "x1"], ENABLE_APPARMOR=yes, ENABLE_APPARMOR=no) |
1532 | - |
1533 | - echo " |
1534 | - ---{ $PACKAGE_NAME $VERSION }--- |
1535 | -@@ -1658,6 +1673,8 @@ echo " |
1536 | - Enable soxr (resampler): ${ENABLE_SOXR} |
1537 | - Enable WebRTC echo canceller: ${ENABLE_WEBRTC} |
1538 | - Enable Ubuntu trust store: ${ENABLE_TRUST_STORE} |
1539 | -+ Enable Snappy support: ${ENABLE_SNAPPY} |
1540 | -+ Enable Apparmor: ${ENABLE_APPARMOR} |
1541 | - Enable gcov coverage: ${ENABLE_GCOV} |
1542 | - Enable unit tests: ${ENABLE_TESTS} |
1543 | - Database |
1544 | ++ g_clear_pointer(&u->cond, pa_cond_free); |
1545 | ++ g_clear_pointer(&u->mutex, pa_mutex_free); |
1546 | ++ |
1547 | ++ pa_xfree(u); |
1548 | ++} |
1549 | diff --git a/debian/patches/0701-enable-snap-policy-module.patch b/debian/patches/0701-enable-snap-policy-module.patch |
1550 | new file mode 100644 |
1551 | index 0000000..47a23ab |
1552 | --- /dev/null |
1553 | +++ b/debian/patches/0701-enable-snap-policy-module.patch |
1554 | @@ -0,0 +1,30 @@ |
1555 | +From: James Henstridge <james.henstridge@canonical.com> |
1556 | +Date: Tue, 7 Aug 2018 16:54:00 +0800 |
1557 | +Subject: daemon: enable module-snap-policy module |
1558 | + |
1559 | +Forwarded: not-needed |
1560 | + |
1561 | +This patch allows pulseaudio to limit audio recording to snaps with |
1562 | +the audio-recording interface connected. We will not pursue upstreaming |
1563 | +this patch as the longer term solution will probably use PipeWire. |
1564 | +--- |
1565 | + src/daemon/default.pa.in | 6 ++++++ |
1566 | + 1 file changed, 6 insertions(+) |
1567 | + |
1568 | +diff --git a/src/daemon/default.pa.in b/src/daemon/default.pa.in |
1569 | +index a9a97c0..6c5e7bc 100755 |
1570 | +--- a/src/daemon/default.pa.in |
1571 | ++++ b/src/daemon/default.pa.in |
1572 | +@@ -158,6 +158,12 @@ load-module module-position-event-sounds |
1573 | + ### Cork music/video streams when a phone stream is active |
1574 | + load-module module-role-cork |
1575 | + |
1576 | ++### Block audio recording for snap confined packages unless they have |
1577 | ++### the "pulseaudio" or "audio-record" interfaces plugged. |
1578 | ++.ifexists module-snap-policy@PA_SOEXT@ |
1579 | ++load-module module-snap-policy |
1580 | ++.endif |
1581 | ++ |
1582 | + ### Modules to allow autoloading of filters (such as echo cancellation) |
1583 | + ### on demand. module-filter-heuristics tries to determine what filters |
1584 | + ### make sense, and module-filter-apply does the heavy-lifting of |
1585 | diff --git a/debian/patches/series b/debian/patches/series |
1586 | index 6d07b3b..34b66de 100644 |
1587 | --- a/debian/patches/series |
1588 | +++ b/debian/patches/series |
1589 | @@ -5,16 +5,13 @@ |
1590 | 0022-inotify-wrapper-Quit-daemon-if-pid-file-is-removed.patch |
1591 | 0030-load-module-switch-on-connect.patch |
1592 | |
1593 | -# Ubuntu touch: support for trust-store |
1594 | +# Ubuntu Snap policy support |
1595 | 0406-tagstruct-add-copy-method.patch |
1596 | 0407-access-Add-access-control-hooks.patch |
1597 | 0408-protocol-native-add-access-checks.patch |
1598 | -0409-Trust-store-patch.patch |
1599 | -0410-Add-thread-to-activate-trust-store-interface.patch |
1600 | -0417-increase-timeout-check-apparmor.patch |
1601 | - |
1602 | -# Ubuntu Snappy |
1603 | +0409-pa-client-peer-credentials.patch |
1604 | 0700-modules-add-snappy-policy-module.patch |
1605 | +0701-enable-snap-policy-module.patch |
1606 | |
1607 | # Bug fixes backported from PulseAudio 12 |
1608 | 0800-fix-lp1720684.patch |
1609 | diff --git a/debian/pulseaudio.install b/debian/pulseaudio.install |
1610 | index 513b7ad..5586bfd 100755 |
1611 | --- a/debian/pulseaudio.install |
1612 | +++ b/debian/pulseaudio.install |
1613 | @@ -77,7 +77,7 @@ usr/lib/pulse-*/modules/module-virtual-sink.so |
1614 | usr/lib/pulse-*/modules/module-virtual-source.so |
1615 | usr/lib/pulse-*/modules/module-switch-on-port-available.so |
1616 | usr/lib/pulse-*/modules/module-virtual-surround-sink.so |
1617 | -usr/lib/pulse-*/modules/module-snappy-policy.so |
1618 | +usr/lib/pulse-*/modules/module-snap-policy.so |
1619 | usr/lib/pulse-*/modules/module-x11*.so |
1620 | usr/lib/pulse-*/modules/module-allow-passthrough.so |
1621 | [linux-any] usr/lib/pulse-*/modules/module-systemd-login.so |
1622 | diff --git a/debian/rules b/debian/rules |
1623 | index 3737dc5..32cc45f 100755 |
1624 | --- a/debian/rules |
1625 | +++ b/debian/rules |
1626 | @@ -22,7 +22,7 @@ DEB_CONFIGURE_EXTRA_FLAGS = --enable-x11 --disable-hal-compat \ |
1627 | --with-zsh-completion-dir=\$${datadir}/zsh/vendor-completions \ |
1628 | --with-bash-completion-dir=\$${datadir}/bash-completion/completions \ |
1629 | --with-systemduserunitdir=\$${prefix}/lib/systemd/user \ |
1630 | - --disable-bluez4 --enable-snappy |
1631 | + --disable-bluez4 --enable-snap |
1632 | |
1633 | PA_MAJORMINOR = $(shell echo $(DEB_VERSION_UPSTREAM) | sed -r -e 's/^([0-9]+\.[0-9]+).*/\1/') |
1634 |