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