Merge lp:~jamesh/mediascanner2/dbus-apparmor into lp:mediascanner2
- dbus-apparmor
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 232 |
Proposed branch: | lp:~jamesh/mediascanner2/dbus-apparmor |
Merge into: | lp:mediascanner2 |
Prerequisite: | lp:~jamesh/mediascanner2/dbus-transport |
Diff against target: |
276 lines (+122/-10) 6 files modified
CMakeLists.txt (+1/-0) debian/control (+1/-0) src/ms-dbus/CMakeLists.txt (+5/-2) src/ms-dbus/dbus-interface.hh (+7/-0) src/ms-dbus/service-skeleton.cc (+105/-6) test/CMakeLists.txt (+3/-2) |
To merge this branch: | bzr merge lp:~jamesh/mediascanner2/dbus-apparmor |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tyler Hicks (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Jussi Pakkanen (community) | Approve | ||
Review via email: mp+221058@code.launchpad.net |
Commit message
Limit access to the MediaScanner D-Bus interface from confined processes.
The trust-store API does not exist yet, so it just hard codes support for the "com.ubuntu.music" application.
Description of the change
Limit access to the MediaScanner D-Bus interface from confined processes.
This is roughly based on the equivalent code from media-hub:
The trust-store API does not exist yet, so it just hard codes support for the "com.ubuntu.music" application.
PS Jenkins bot (ps-jenkins) wrote : | # |
Jussi Pakkanen (jpakkane) wrote : | # |
Not a security expert, but looks fine to me.
- 257. By James Henstridge
-
Include a timeout on the QML test.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:257
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Tyler Hicks (tyhicks) wrote : | # |
* AppArmor related review comments that need fixing
- media-hub limits access for the "com.ubuntu.music" application to
/home/
+ http://
+ MediaScanner should probably do the same thing
+ However, note that mediahub is currently not doing this quite right since
the path should *equal* /home/phablet/Music or /home/phablet/
rather than just containing the strings Music/ or Videos/
* AppArmor related review comments that should be fixed but are non-blockers
- You may want to make the handling of GetConnectionAp
more robust
+ When AppArmor is disabled in the kernel or in dbus-daemon,
GetConnec
DBUS_
+ In the current merge proposal, this will always result in the client
being denied access
+ We may want to link against libapparmor to call aa_is_enabled() and, if
AppArmor is disabled, always grant access before even calling
GetConnec
- does_client_
+ Some confined clients may not be Click Apps, so they may not contains '_'
chars in their AppArmor confinement context
+ This is fine for now, but in the future we will want to handle
applications that are applications that are confined by AppArmor but are
not Click Apps
* Minor things that caught my eye
- The bus variable in get_client_
- The MediaType parameter in does_client_
+ Because of this, the MediaType parameter of check_access is also unused
- 258. By James Henstridge
-
If apparmor is not enabled, treat d-bus peers as unconfined.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:258
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
James Henstridge (jamesh) wrote : | # |
> * AppArmor related review comments that need fixing
> - media-hub limits access for the "com.ubuntu.music" application to
> /home/phablet/
> + http://
> condensed/
> + MediaScanner should probably do the same thing
> + However, note that mediahub is currently not doing this quite right
> since
> the path should *equal* /home/phablet/Music or /home/phablet/
> rather than just containing the strings Music/ or Videos/
Currently the media scanner is only scanning ~/Music and ~/Videos and external media. Most of the APIs are centred around media types rather than locations though. I'm not sure what the best answer is here.
>
> * AppArmor related review comments that should be fixed but are non-blockers
> - You may want to make the handling of
> GetConnectionAp
> more robust
> + When AppArmor is disabled in the kernel or in dbus-daemon,
> GetConnectionAp
> DBUS_ERROR_
> + In the current merge proposal, this will always result in the client
> being denied access
> + We may want to link against libapparmor to call aa_is_enabled() and, if
> AppArmor is disabled, always grant access before even calling
> GetConnectionAp
> - does_client_
> + Some confined clients may not be Click Apps, so they may not contains
> '_'
> chars in their AppArmor confinement context
> + This is fine for now, but in the future we will want to handle
> applications that are applications that are confined by AppArmor but are
> not Click Apps
I've updated the code to call aa_is_enabled(), and treat peers as unconfined if it returns false. That seems to help get the code through Jenkins (and presumably also Launchpad's builder).
I agree that it would be good to externalise the security policy, but this seems to be the kind of thing media-hub is also doing for now. I'd be happy to change this once we've got an idea of how to do that.
>
> * Minor things that caught my eye
> - The bus variable in get_client_
> - The MediaType parameter in does_client_
> + Because of this, the MediaType parameter of check_access is also unused
I've switched the policy over to allowing com.ubuntu.music to access AudioMedia, so the MediaType parameter is now used (I guess I missed this when I got distracted by the Jenkins failure). I've removed the unused bus variable in get_client_
Tyler Hicks (tyhicks) wrote : | # |
Everything looks good to me. Thanks!
Preview Diff
1 | === modified file 'CMakeLists.txt' | |||
2 | --- CMakeLists.txt 2014-05-29 07:30:29 +0000 | |||
3 | +++ CMakeLists.txt 2014-05-29 07:30:30 +0000 | |||
4 | @@ -16,6 +16,7 @@ | |||
5 | 16 | ) | 16 | ) |
6 | 17 | pkg_check_modules(GST gstreamer-1.0 gstreamer-pbutils-1.0 REQUIRED) | 17 | pkg_check_modules(GST gstreamer-1.0 gstreamer-pbutils-1.0 REQUIRED) |
7 | 18 | pkg_check_modules(DBUSCPP dbus-cpp REQUIRED) | 18 | pkg_check_modules(DBUSCPP dbus-cpp REQUIRED) |
8 | 19 | pkg_check_modules(APPARMOR libapparmor REQUIRED) | ||
9 | 19 | find_package(Threads REQUIRED) | 20 | find_package(Threads REQUIRED) |
10 | 20 | find_package(Qt5Core REQUIRED) | 21 | find_package(Qt5Core REQUIRED) |
11 | 21 | 22 | ||
12 | 22 | 23 | ||
13 | === modified file 'debian/control' | |||
14 | --- debian/control 2014-05-29 07:30:29 +0000 | |||
15 | +++ debian/control 2014-05-29 07:30:30 +0000 | |||
16 | @@ -6,6 +6,7 @@ | |||
17 | 6 | Build-Depends: cmake, | 6 | Build-Depends: cmake, |
18 | 7 | dbus, | 7 | dbus, |
19 | 8 | debhelper (>= 9), | 8 | debhelper (>= 9), |
20 | 9 | libapparmor-dev, | ||
21 | 9 | libdbus-1-dev, | 10 | libdbus-1-dev, |
22 | 10 | libdbus-cpp-dev (>= 3.0.0), | 11 | libdbus-cpp-dev (>= 3.0.0), |
23 | 11 | libglib2.0-dev, | 12 | libglib2.0-dev, |
24 | 12 | 13 | ||
25 | === modified file 'src/ms-dbus/CMakeLists.txt' | |||
26 | --- src/ms-dbus/CMakeLists.txt 2014-05-29 07:30:29 +0000 | |||
27 | +++ src/ms-dbus/CMakeLists.txt 2014-05-29 07:30:30 +0000 | |||
28 | @@ -1,5 +1,5 @@ | |||
29 | 1 | include_directories(..) | 1 | include_directories(..) |
31 | 2 | add_definitions(${MEDIASCANNER_CFLAGS} ${DBUSCPP_CFLAGS}) | 2 | add_definitions(${MEDIASCANNER_CFLAGS} ${DBUSCPP_CFLAGS} ${APPARMOR_CFLAGS}) |
32 | 3 | 3 | ||
33 | 4 | add_library(ms-dbus STATIC | 4 | add_library(ms-dbus STATIC |
34 | 5 | dbus-codec.cc | 5 | dbus-codec.cc |
35 | @@ -7,7 +7,10 @@ | |||
36 | 7 | service-stub.cc | 7 | service-stub.cc |
37 | 8 | ) | 8 | ) |
38 | 9 | 9 | ||
40 | 10 | target_link_libraries(ms-dbus mediascanner ${DBUSCPP_LDFLAGS}) | 10 | target_link_libraries(ms-dbus |
41 | 11 | mediascanner | ||
42 | 12 | ${DBUSCPP_LDFLAGS} | ||
43 | 13 | ${APPARMOR_LDFLAGS}) | ||
44 | 11 | # Compile with -fPIC, since this code is linked into the QML plugin. | 14 | # Compile with -fPIC, since this code is linked into the QML plugin. |
45 | 12 | set_target_properties(ms-dbus PROPERTIES COMPILE_FLAGS "-fPIC") | 15 | set_target_properties(ms-dbus PROPERTIES COMPILE_FLAGS "-fPIC") |
46 | 13 | 16 | ||
47 | 14 | 17 | ||
48 | === modified file 'src/ms-dbus/dbus-interface.hh' | |||
49 | --- src/ms-dbus/dbus-interface.hh 2014-05-29 07:30:29 +0000 | |||
50 | +++ src/ms-dbus/dbus-interface.hh 2014-05-29 07:30:30 +0000 | |||
51 | @@ -41,6 +41,13 @@ | |||
52 | 41 | return s; | 41 | return s; |
53 | 42 | } | 42 | } |
54 | 43 | }; | 43 | }; |
55 | 44 | |||
56 | 45 | struct Unauthorized { | ||
57 | 46 | inline static const std::string& name() { | ||
58 | 47 | static std::string s = "com.canonical.MediaScanner2.Error.Unauthorized"; | ||
59 | 48 | return s; | ||
60 | 49 | } | ||
61 | 50 | }; | ||
62 | 44 | }; | 51 | }; |
63 | 45 | 52 | ||
64 | 46 | struct Lookup { | 53 | struct Lookup { |
65 | 47 | 54 | ||
66 | === modified file 'src/ms-dbus/service-skeleton.cc' | |||
67 | --- src/ms-dbus/service-skeleton.cc 2014-05-29 07:30:29 +0000 | |||
68 | +++ src/ms-dbus/service-skeleton.cc 2014-05-29 07:30:30 +0000 | |||
69 | @@ -3,6 +3,7 @@ | |||
70 | 3 | #include <core/dbus/message.h> | 3 | #include <core/dbus/message.h> |
71 | 4 | #include <core/dbus/object.h> | 4 | #include <core/dbus/object.h> |
72 | 5 | #include <core/dbus/types/object_path.h> | 5 | #include <core/dbus/types/object_path.h> |
73 | 6 | #include <sys/apparmor.h> | ||
74 | 6 | 7 | ||
75 | 7 | #include <mediascanner/Album.hh> | 8 | #include <mediascanner/Album.hh> |
76 | 8 | #include <mediascanner/MediaFile.hh> | 9 | #include <mediascanner/MediaFile.hh> |
77 | @@ -17,6 +18,26 @@ | |||
78 | 17 | namespace mediascanner { | 18 | namespace mediascanner { |
79 | 18 | namespace dbus { | 19 | namespace dbus { |
80 | 19 | 20 | ||
81 | 21 | struct Apparmor { | ||
82 | 22 | static const std::string &name() { | ||
83 | 23 | static std::string s = "org.freedesktop.DBus"; | ||
84 | 24 | return s; | ||
85 | 25 | } | ||
86 | 26 | |||
87 | 27 | struct GetConnectionAppArmorSecurityContext { | ||
88 | 28 | typedef Apparmor Interface; | ||
89 | 29 | |||
90 | 30 | static const std::string &name() { | ||
91 | 31 | static std::string s = "GetConnectionAppArmorSecurityContext"; | ||
92 | 32 | return s; | ||
93 | 33 | } | ||
94 | 34 | |||
95 | 35 | static const std::chrono::milliseconds default_timeout() { | ||
96 | 36 | return std::chrono::seconds{1}; | ||
97 | 37 | } | ||
98 | 38 | }; | ||
99 | 39 | }; | ||
100 | 40 | |||
101 | 20 | struct ServiceSkeleton::Private { | 41 | struct ServiceSkeleton::Private { |
102 | 21 | ServiceSkeleton *impl; | 42 | ServiceSkeleton *impl; |
103 | 22 | std::shared_ptr<MediaStore> store; | 43 | std::shared_ptr<MediaStore> store; |
104 | @@ -69,7 +90,69 @@ | |||
105 | 69 | std::placeholders::_1)); | 90 | std::placeholders::_1)); |
106 | 70 | } | 91 | } |
107 | 71 | 92 | ||
108 | 93 | std::string get_client_apparmor_context(const Message::Ptr &message) { | ||
109 | 94 | if (!aa_is_enabled()) { | ||
110 | 95 | return "unconfined"; | ||
111 | 96 | } | ||
112 | 97 | auto service = core::dbus::Service::use_service( | ||
113 | 98 | impl->access_bus(), "org.freedesktop.DBus"); | ||
114 | 99 | auto obj = service->object_for_path( | ||
115 | 100 | core::dbus::types::ObjectPath("/org/freedesktop/DBus")); | ||
116 | 101 | |||
117 | 102 | core::dbus::Result<std::string> result; | ||
118 | 103 | try { | ||
119 | 104 | result = obj->invoke_method_synchronously<Apparmor::GetConnectionAppArmorSecurityContext, std::string>(message->sender()); | ||
120 | 105 | } catch (const std::runtime_error &e) { | ||
121 | 106 | fprintf(stderr, "Error getting apparmor context: %s\n", e.what()); | ||
122 | 107 | return std::string(); | ||
123 | 108 | } | ||
124 | 109 | if (result.is_error()) { | ||
125 | 110 | fprintf(stderr, "Error getting apparmor context: %s\n", result.error().print().c_str()); | ||
126 | 111 | return std::string(); | ||
127 | 112 | } | ||
128 | 113 | return result.value(); | ||
129 | 114 | } | ||
130 | 115 | |||
131 | 116 | bool does_client_have_access(const std::string &context, MediaType type) { | ||
132 | 117 | if (context.empty()) { | ||
133 | 118 | // Deny access if we don't have a context | ||
134 | 119 | return false; | ||
135 | 120 | } | ||
136 | 121 | if (context == "unconfined") { | ||
137 | 122 | // Unconfined | ||
138 | 123 | return true; | ||
139 | 124 | } | ||
140 | 125 | |||
141 | 126 | auto pos = context.find_first_of('_'); | ||
142 | 127 | if (pos == std::string::npos) { | ||
143 | 128 | fprintf(stderr, "Badly formed AppArmor context: %s\n", context.c_str()); | ||
144 | 129 | return false; | ||
145 | 130 | } | ||
146 | 131 | const std::string pkgname = context.substr(0, pos); | ||
147 | 132 | |||
148 | 133 | // TODO: when the trust store lands, check it to see if this | ||
149 | 134 | // app can access the index. | ||
150 | 135 | if (type == AudioMedia && pkgname == "com.ubuntu.music") { | ||
151 | 136 | return true; | ||
152 | 137 | } | ||
153 | 138 | return false; | ||
154 | 139 | } | ||
155 | 140 | |||
156 | 141 | bool check_access(const Message::Ptr &message, MediaType type) { | ||
157 | 142 | const std::string context = get_client_apparmor_context(message); | ||
158 | 143 | bool have_access = does_client_have_access(context, type); | ||
159 | 144 | if (!have_access) { | ||
160 | 145 | auto reply = Message::make_error( | ||
161 | 146 | message, MediaStoreInterface::Errors::Unauthorized::name(), "Unauthorized"); | ||
162 | 147 | impl->access_bus()->send(reply); | ||
163 | 148 | } | ||
164 | 149 | return have_access; | ||
165 | 150 | } | ||
166 | 151 | |||
167 | 72 | void handle_lookup(const Message::Ptr &message) { | 152 | void handle_lookup(const Message::Ptr &message) { |
168 | 153 | if (!check_access(message, AllMedia)) | ||
169 | 154 | return; | ||
170 | 155 | |||
171 | 73 | std::string filename; | 156 | std::string filename; |
172 | 74 | message->reader() >> filename; | 157 | message->reader() >> filename; |
173 | 75 | Message::Ptr reply; | 158 | Message::Ptr reply; |
174 | @@ -89,8 +172,11 @@ | |||
175 | 89 | std::string query; | 172 | std::string query; |
176 | 90 | int32_t type; | 173 | int32_t type; |
177 | 91 | int32_t limit; | 174 | int32_t limit; |
178 | 92 | |||
179 | 93 | message->reader() >> query >> type >> limit; | 175 | message->reader() >> query >> type >> limit; |
180 | 176 | |||
181 | 177 | if (!check_access(message, (MediaType)type)) | ||
182 | 178 | return; | ||
183 | 179 | |||
184 | 94 | Message::Ptr reply; | 180 | Message::Ptr reply; |
185 | 95 | try { | 181 | try { |
186 | 96 | auto results = store->query(query, (MediaType)type, limit); | 182 | auto results = store->query(query, (MediaType)type, limit); |
187 | @@ -105,9 +191,11 @@ | |||
188 | 105 | } | 191 | } |
189 | 106 | 192 | ||
190 | 107 | void handle_query_albums(const Message::Ptr &message) { | 193 | void handle_query_albums(const Message::Ptr &message) { |
191 | 194 | if (!check_access(message, AudioMedia)) | ||
192 | 195 | return; | ||
193 | 196 | |||
194 | 108 | std::string query; | 197 | std::string query; |
195 | 109 | int32_t limit; | 198 | int32_t limit; |
196 | 110 | |||
197 | 111 | message->reader() >> query >> limit; | 199 | message->reader() >> query >> limit; |
198 | 112 | Message::Ptr reply; | 200 | Message::Ptr reply; |
199 | 113 | try { | 201 | try { |
200 | @@ -123,8 +211,10 @@ | |||
201 | 123 | } | 211 | } |
202 | 124 | 212 | ||
203 | 125 | void handle_get_album_songs(const Message::Ptr &message) { | 213 | void handle_get_album_songs(const Message::Ptr &message) { |
204 | 214 | if (!check_access(message, AudioMedia)) | ||
205 | 215 | return; | ||
206 | 216 | |||
207 | 126 | Album album("", ""); | 217 | Album album("", ""); |
208 | 127 | |||
209 | 128 | message->reader() >> album; | 218 | message->reader() >> album; |
210 | 129 | Message::Ptr reply; | 219 | Message::Ptr reply; |
211 | 130 | try { | 220 | try { |
212 | @@ -140,6 +230,9 @@ | |||
213 | 140 | } | 230 | } |
214 | 141 | 231 | ||
215 | 142 | void handle_get_etag(const Message::Ptr &message) { | 232 | void handle_get_etag(const Message::Ptr &message) { |
216 | 233 | if (!check_access(message, AllMedia)) | ||
217 | 234 | return; | ||
218 | 235 | |||
219 | 143 | std::string filename; | 236 | std::string filename; |
220 | 144 | message->reader() >> filename; | 237 | message->reader() >> filename; |
221 | 145 | 238 | ||
222 | @@ -157,9 +250,11 @@ | |||
223 | 157 | } | 250 | } |
224 | 158 | 251 | ||
225 | 159 | void handle_list_songs(const Message::Ptr &message) { | 252 | void handle_list_songs(const Message::Ptr &message) { |
226 | 253 | if (!check_access(message, AudioMedia)) | ||
227 | 254 | return; | ||
228 | 255 | |||
229 | 160 | std::string artist, album, album_artist; | 256 | std::string artist, album, album_artist; |
230 | 161 | int32_t limit; | 257 | int32_t limit; |
231 | 162 | |||
232 | 163 | message->reader() >> artist >> album >> album_artist >> limit; | 258 | message->reader() >> artist >> album >> album_artist >> limit; |
233 | 164 | Message::Ptr reply; | 259 | Message::Ptr reply; |
234 | 165 | try { | 260 | try { |
235 | @@ -175,9 +270,11 @@ | |||
236 | 175 | } | 270 | } |
237 | 176 | 271 | ||
238 | 177 | void handle_list_albums(const Message::Ptr &message) { | 272 | void handle_list_albums(const Message::Ptr &message) { |
239 | 273 | if (!check_access(message, AudioMedia)) | ||
240 | 274 | return; | ||
241 | 275 | |||
242 | 178 | std::string artist, album_artist; | 276 | std::string artist, album_artist; |
243 | 179 | int32_t limit; | 277 | int32_t limit; |
244 | 180 | |||
245 | 181 | message->reader() >> artist >> album_artist >> limit; | 278 | message->reader() >> artist >> album_artist >> limit; |
246 | 182 | Message::Ptr reply; | 279 | Message::Ptr reply; |
247 | 183 | try { | 280 | try { |
248 | @@ -193,9 +290,11 @@ | |||
249 | 193 | } | 290 | } |
250 | 194 | 291 | ||
251 | 195 | void handle_list_artists(const Message::Ptr &message) { | 292 | void handle_list_artists(const Message::Ptr &message) { |
252 | 293 | if (!check_access(message, AudioMedia)) | ||
253 | 294 | return; | ||
254 | 295 | |||
255 | 196 | bool album_artists; | 296 | bool album_artists; |
256 | 197 | int32_t limit; | 297 | int32_t limit; |
257 | 198 | |||
258 | 199 | message->reader() >> album_artists >> limit; | 298 | message->reader() >> album_artists >> limit; |
259 | 200 | Message::Ptr reply; | 299 | Message::Ptr reply; |
260 | 201 | try { | 300 | try { |
261 | 202 | 301 | ||
262 | === modified file 'test/CMakeLists.txt' | |||
263 | --- test/CMakeLists.txt 2014-05-29 07:30:29 +0000 | |||
264 | +++ test/CMakeLists.txt 2014-05-29 07:30:30 +0000 | |||
265 | @@ -51,8 +51,9 @@ | |||
266 | 51 | qt5_use_modules(test_qml QuickTest) | 51 | qt5_use_modules(test_qml QuickTest) |
267 | 52 | target_link_libraries(test_qml mediascanner ${DBUSCPP_LDFLAGS}) | 52 | target_link_libraries(test_qml mediascanner ${DBUSCPP_LDFLAGS}) |
268 | 53 | add_test(test_qml test_qml -input ${CMAKE_CURRENT_SOURCE_DIR}/qml -import ${CMAKE_BINARY_DIR}/src/qml) | 53 | add_test(test_qml test_qml -input ${CMAKE_CURRENT_SOURCE_DIR}/qml -import ${CMAKE_BINARY_DIR}/src/qml) |
271 | 54 | set_tests_properties(test_qml | 54 | set_tests_properties(test_qml PROPERTIES |
272 | 55 | PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=minimal") | 55 | ENVIRONMENT "QT_QPA_PLATFORM=minimal" |
273 | 56 | TIMEOUT 600) | ||
274 | 56 | 57 | ||
275 | 57 | add_executable(test_util test_util.cc ../src/daemon/util.cc) | 58 | add_executable(test_util test_util.cc ../src/daemon/util.cc) |
276 | 58 | target_link_libraries(test_util gtest) | 59 | target_link_libraries(test_util gtest) |
FAILED: Continuous integration, rev:256 jenkins. qa.ubuntu. com/job/ mediascanner2- ci/78/ jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- amd64-ci/ 19/console jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- armhf-ci/ 19/console jenkins. qa.ubuntu. com/job/ mediascanner2- utopic- i386-ci/ 19/console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mediascanne r2-ci/78/ rebuild
http://