Merge lp:~ken-vandine/content-hub/15.04-lp1456628 into lp:content-hub/15.04

Proposed by Ken VanDine
Status: Merged
Approved by: Michael Sheldon
Approved revision: 209
Merged at revision: 209
Proposed branch: lp:~ken-vandine/content-hub/15.04-lp1456628
Merge into: lp:content-hub/15.04
Diff against target: 489 lines (+192/-27)
16 files modified
CMakeLists.txt (+1/-0)
debian/apparmor/content-hub-testability (+15/-0)
debian/content-hub-testability.install (+1/-0)
debian/control (+4/-0)
debian/rules (+4/-0)
debian/tests/aa-check (+41/-0)
debian/tests/control (+3/-0)
src/com/ubuntu/content/CMakeLists.txt (+2/-0)
src/com/ubuntu/content/detail/service.h (+1/-1)
src/com/ubuntu/content/detail/transfer.cpp (+19/-2)
src/com/ubuntu/content/detail/transfer.h (+4/-2)
src/com/ubuntu/content/utils.cpp (+51/-7)
tests/peers/exporter/CMakeLists.txt (+2/-0)
tests/peers/exporter/autoexporter.cpp (+21/-12)
tests/peers/exporter/autoexporter.h (+4/-0)
tests/peers/exporter/exporter.cpp (+19/-3)
To merge this branch: bzr merge lp:~ken-vandine/content-hub/15.04-lp1456628
Reviewer Review Type Date Requested Status
Michael Sheldon (community) Approve
Review via email: mp+260831@code.launchpad.net

Commit message

* SECURITY UPDATE: file disclosure via unchecked AppArmor profile
    (LP: #1456628)
  - Don't allow exporting of files that aren't allowed by the source apparmor profile
  - CVE-2015-1327

Description of the change

Verify the source app has read access to local files being transferred

Debs can be found at http://people.canonical.com/~kenvandine/15.04-lp1456628.tar

This can be tested by installing the content-hub-testability package and running these commands checking their exit value.

"content-hub-test-exporter content-hub-test-importer file:///etc/issue content-hub-test-ok"

Should exit 0

"content-hub-test-exporter content-hub-test-importer file:///etc/issue content-hub-test-bad"

Should exit 1

To post a comment you must log in.
Revision history for this message
Ken VanDine (ken-vandine) wrote :

Are there any related MPs required for this MP to build/function as expected? Please list.

 * No

Is your branch in sync with latest trunk (e.g. bzr pull lp:trunk -> no changes)

 * Yes

Did you perform an exploratory manual test run of your code change and any related functionality on device or emulator?

 * Yes

Did you successfully run all tests found in your component's Test Plan (https://wiki.ubuntu.com/Process/Merges/TestPlan/content-hub) on device or emulator?

 * Yes

If you changed the UI, was the change specified/approved by design?

 * No change

If you changed UI labels, did you update the pot file?

 * No change

If you changed the packaging (debian), did you add a core-dev as a reviewer to this MP?

 * I'm a core-dev, added autopkgtests, apparmor profile for testing and build depends for apparmor

Revision history for this message
Michael Sheldon (michael-sheldon) wrote :

Did you perform an exploratory manual test run of the code change and any related functionality on device or emulator?

 * Yes

Did CI run pass? If not, please explain why.

 * No CI due to private MR

Have you checked that submitter has accurately filled out the submitter checklist and has taken no shortcut?

 * Yes

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-08-14 11:49:28 +0000
3+++ CMakeLists.txt 2015-06-02 13:16:53 +0000
4@@ -68,6 +68,7 @@
5 pkg_check_modules(DBUS REQUIRED dbus-1)
6 pkg_check_modules(UBUNTU_DOWNLOAD_MANAGER REQUIRED ubuntu-download-manager-client)
7 pkg_check_modules(NOTIFY REQUIRED libnotify)
8+pkg_check_modules(APPARMOR REQUIRED libapparmor)
9
10 add_definitions(-DDEBUG_ENABLED)
11
12
13=== added directory 'debian/apparmor'
14=== added file 'debian/apparmor/content-hub-testability'
15--- debian/apparmor/content-hub-testability 1970-01-01 00:00:00 +0000
16+++ debian/apparmor/content-hub-testability 2015-06-02 13:16:53 +0000
17@@ -0,0 +1,15 @@
18+# vim:syntax=apparmor
19+# Author: Ken VanDine <ken.vandine@canonical.com>
20+#
21+#include <tunables/global>
22+
23+profile "content-hub-test-ok" {
24+ #include <abstractions/base>
25+ #include <abstractions/dbus-session>
26+ /etc/issue r,
27+}
28+profile "content-hub-test-bad" {
29+ #include <abstractions/base>
30+ #include <abstractions/dbus-session>
31+ audit deny /etc/issue r,
32+}
33
34=== modified file 'debian/content-hub-testability.install'
35--- debian/content-hub-testability.install 2014-08-07 19:35:30 +0000
36+++ debian/content-hub-testability.install 2015-06-02 13:16:53 +0000
37@@ -3,3 +3,4 @@
38 usr/share/applications/content-hub-test*
39 usr/share/content-hub/testability/data
40 usr/share/icons/hicolor/512x512/apps/content-hub-test*
41+debian/apparmor/content-hub-testability etc/apparmor.d
42
43=== modified file 'debian/control'
44--- debian/control 2014-11-18 14:59:03 +0000
45+++ debian/control 2015-06-02 13:16:53 +0000
46@@ -5,10 +5,12 @@
47 click-dev,
48 dbus-test-runner,
49 debhelper (>= 9),
50+ dh-apparmor,
51 dh-translations,
52 doxygen,
53 google-mock,
54 lcov,
55+ libapparmor-dev,
56 libglib2.0-dev,
57 libgsettings-qt-dev,
58 libnih-dbus-dev,
59@@ -28,6 +30,7 @@
60 Vcs-Bzr: https://code.launchpad.net/~phablet-team/content-hub/trunk
61 Vcs-Browser: https://bazaar.launchpad.net/~phablet-team/content-hub/trunk/files
62 X-Ubuntu-Use-Langpack: yes
63+XS-Testsuite: autopkgtest
64
65 Package: content-hub
66 Architecture: any
67@@ -88,5 +91,6 @@
68 Architecture: any
69 Depends: ${misc:Depends},
70 ${shlibs:Depends},
71+ content-hub,
72 Description: content sharing testability
73 Files and utilities needed for automated testing of content-hub
74
75=== modified file 'debian/rules'
76--- debian/rules 2014-10-10 16:13:03 +0000
77+++ debian/rules 2015-06-02 13:16:53 +0000
78@@ -6,6 +6,10 @@
79 %:
80 dh $@ --with click,translations --fail-missing -- -B build
81
82+override_dh_auto_install:
83+ dh_auto_install
84+ dh_apparmor -pcontent-hub-testability --profile-name=content-hub-testability
85+
86 override_dh_auto_test:
87 dbus-test-runner -t make -p "-C" -p "build/tests/acceptance-tests" -p test
88
89
90=== added directory 'debian/tests'
91=== added file 'debian/tests/aa-check'
92--- debian/tests/aa-check 1970-01-01 00:00:00 +0000
93+++ debian/tests/aa-check 2015-06-02 13:16:53 +0000
94@@ -0,0 +1,41 @@
95+#!/bin/sh
96+
97+# start X
98+(Xvfb :5 >/dev/null 2>&1 &)
99+XVFB_PID=$!
100+export DISPLAY=:5
101+
102+# start local session D-BUS
103+eval `dbus-launch`
104+trap "kill $DBUS_SESSION_BUS_PID $XVFB_PID" 0 TERM QUIT INT
105+export DBUS_SESSION_BUS_ADDRESS
106+export XAUTHORITY=/dev/null
107+
108+oktests="content-hub-test-ok"
109+badtests="content-hub-test-bad"
110+FAILED=""
111+
112+for a in $oktests; do
113+ content-hub-test-importer &2>/dev/null
114+ content-hub-test-exporter content-hub-test-importer file:///etc/issue $a 2>/dev/null
115+ if [ $? -ne 0 ]; then
116+ FAILED="$FAILED $a"
117+ fi
118+done
119+
120+for b in $badtests; do
121+ content-hub-test-importer &2>/dev/null
122+ content-hub-test-exporter content-hub-test-importer file:///etc/issue $b 2>/dev/null
123+ if [ $? -eq 0 ]; then
124+ FAILED="$FAILED $b"
125+ fi
126+done
127+
128+if [ -z "$FAILED" ]; then
129+ echo "All tests passed"
130+ exit 0
131+else
132+ echo "$FAILED failed"
133+ exit 1
134+fi
135+
136
137=== added file 'debian/tests/control'
138--- debian/tests/control 1970-01-01 00:00:00 +0000
139+++ debian/tests/control 2015-06-02 13:16:53 +0000
140@@ -0,0 +1,3 @@
141+Tests: aa-check
142+Depends: content-hub-testability, dbus-x11, xvfb
143+Restrictions: allow-stderr
144
145=== modified file 'src/com/ubuntu/content/CMakeLists.txt'
146--- src/com/ubuntu/content/CMakeLists.txt 2014-05-26 06:45:57 +0000
147+++ src/com/ubuntu/content/CMakeLists.txt 2015-06-02 13:16:53 +0000
148@@ -28,6 +28,7 @@
149 ${UBUNTU_LAUNCH_INCLUDE_DIRS}
150 ${UBUNTU_DOWNLOAD_MANAGER_INCLUDE_DIRS}
151 ${NOTIFY_INCLUDE_DIRS}
152+ ${APPARMOR_INCLUDE_DIRS}
153 )
154
155 qt5_add_dbus_interface(
156@@ -105,6 +106,7 @@
157 ${GIO_LIBRARIES}
158 ${UBUNTU_DOWNLOAD_MANAGER_LIBRARIES}
159 ${NOTIFY_LIBRARIES}
160+ ${APPARMOR_LDFLAGS}
161 )
162
163 install(
164
165=== modified file 'src/com/ubuntu/content/detail/service.h'
166--- src/com/ubuntu/content/detail/service.h 2014-11-19 18:37:26 +0000
167+++ src/com/ubuntu/content/detail/service.h 2015-06-02 13:16:53 +0000
168@@ -79,7 +79,7 @@
169 void handle_exports(int);
170 void handler_unregistered(const QString&);
171 QDBusObjectPath CreateTransfer(const QString&, const QString&, int, const QString&);
172- void download_notify (com::ubuntu::content::detail::Transfer*);
173+ void download_notify(com::ubuntu::content::detail::Transfer*);
174
175 };
176 }
177
178=== modified file 'src/com/ubuntu/content/detail/transfer.cpp'
179--- src/com/ubuntu/content/detail/transfer.cpp 2015-04-21 14:46:25 +0000
180+++ src/com/ubuntu/content/detail/transfer.cpp 2015-06-02 13:16:53 +0000
181@@ -36,7 +36,7 @@
182 const QString& source,
183 const QString& destination,
184 const int direction,
185- const QString& content_type) :
186+ const QString& content_type):
187 state(cuc::Transfer::created),
188 id(id),
189 source(source),
190@@ -166,12 +166,28 @@
191 return;
192 }
193
194+ QString profile = aa_profile(message().service());
195+ TRACE() << Q_FUNC_INFO << "PROFILE:" << profile;
196+
197 QVariantList ret;
198 Q_FOREACH(QVariant iv, items) {
199 cuc::Item item = qdbus_cast<Item>(iv);
200 if (item.url().isEmpty()) {
201 ret.append(QVariant::fromValue(item));
202 } else {
203+ TRACE() << Q_FUNC_INFO;
204+ if (profile.toStdString() != QString("unconfined").toStdString() &&
205+ item.url().isLocalFile()) {
206+ TRACE() << Q_FUNC_INFO << "IS LOCAL FILE";
207+ QString file(item.url().toLocalFile());
208+ TRACE() << Q_FUNC_INFO << "FILE:" << file;
209+ // Verify app has read access to local file before transfer
210+ if (not check_profile_read(profile, file)) {
211+ // If failed to access file, abort
212+ ret.clear();
213+ goto abort;
214+ }
215+ }
216 QString newUrl = copy_to_store(item.url().toString(), d->store);
217 if (!newUrl.isEmpty()) {
218 item.setUrl(QUrl(newUrl));
219@@ -179,11 +195,12 @@
220 ret.append(QVariant::fromValue(item));
221 } else {
222 ret.clear();
223- break;
224+ goto abort;
225 }
226 }
227 }
228
229+abort:
230 if (ret.count() <= 0)
231 {
232 qWarning() << "Failed to charge items, aborting";
233
234=== modified file 'src/com/ubuntu/content/detail/transfer.h'
235--- src/com/ubuntu/content/detail/transfer.h 2014-09-26 10:34:39 +0000
236+++ src/com/ubuntu/content/detail/transfer.h 2015-06-02 13:16:53 +0000
237@@ -18,9 +18,11 @@
238 #ifndef TRANSFER_H_
239 #define TRANSFER_H_
240
241+#include <QDir>
242 #include <QObject>
243 #include <QStringList>
244-#include <QDir>
245+#include <QtDBus/QDBusMessage>
246+#include <QtDBus/QDBusContext>
247 #include <ubuntu/download_manager/error.h>
248
249 namespace com
250@@ -31,7 +33,7 @@
251 {
252 namespace detail
253 {
254-class Transfer : public QObject
255+class Transfer : public QObject, protected QDBusContext
256 {
257 Q_OBJECT
258 Q_PROPERTY(int State READ State NOTIFY StateChanged)
259
260=== modified file 'src/com/ubuntu/content/utils.cpp'
261--- src/com/ubuntu/content/utils.cpp 2015-03-16 16:57:15 +0000
262+++ src/com/ubuntu/content/utils.cpp 2015-06-02 13:16:53 +0000
263@@ -16,12 +16,14 @@
264 * Authored by: Ken VanDine <ken.vandine@canonical.com>
265 */
266
267+#include <QCoreApplication>
268+#include <QDir>
269+#include <QFile>
270+#include <QFileInfo>
271+#include <QProcess>
272 #include <QtCore>
273 #include <QtDBus/QDBusMessage>
274 #include <QtDBus/QDBusConnection>
275-#include <QFile>
276-#include <QDir>
277-#include <QFileInfo>
278 #include <QUrl>
279 #include <nih/alloc.h>
280 #include <nih-dbus/dbus_util.h>
281@@ -31,6 +33,11 @@
282 #include "com/ubuntu/content/type.h"
283 #include <unistd.h>
284
285+#include <sys/apparmor.h>
286+/* need to be exposed in libapparmor but for now ... */
287+#define AA_CLASS_FILE 2
288+#define AA_MAY_READ (1 << 2)
289+
290 namespace cuc = com::ubuntu::content;
291
292 namespace {
293@@ -102,10 +109,6 @@
294 reply.errorMessage();
295 }
296
297- if (aaProfile.toStdString() == QString("unconfined").toStdString())
298- {
299- return QString("");
300- }
301 return aaProfile;
302 }
303
304@@ -175,4 +178,45 @@
305 return false;
306 }
307
308+int query_file(const char *label, const char *path, int *allowed)
309+{
310+ int rc, audited;
311+ char *query;
312+
313+ /* + 1 for null separator and then + 1 AA_CLASS_FILE */
314+ int label_size = strlen(label);
315+ int size = label_size + 1 + strlen(path) + AA_QUERY_CMD_LABEL_SIZE + 1;
316+ /* +1 for null terminator used by strcpy, yes we could drop this
317+ * using memcpy */
318+ query = (char*)malloc(size + 1);
319+ if (!query)
320+ return -1;
321+ /* we want the null terminator here */
322+ strcpy(query + AA_QUERY_CMD_LABEL_SIZE, label);
323+ query[AA_QUERY_CMD_LABEL_SIZE + label_size + 1] = AA_CLASS_FILE;
324+ strcpy(query + AA_QUERY_CMD_LABEL_SIZE + label_size + 2, path);
325+ rc = aa_query_label(AA_MAY_READ, query, size , allowed, &audited);
326+ free(query);
327+ return rc;
328+}
329+
330+bool check_profile_read(QString profile, QString path)
331+{
332+ TRACE() << Q_FUNC_INFO << "PROFILE:" << profile;
333+
334+ int allowed;
335+ if (query_file(profile.toStdString().c_str(), path.toStdString().c_str(), &allowed) == -1) {
336+ qWarning() << "error:" << strerror(errno) << path;
337+ return false;
338+ }
339+
340+ if (allowed) {
341+ TRACE() << "ALLOWED:" << QString::number(allowed);
342+ return true;
343+ }
344+ TRACE() << "NOT ALLOWED:" << QString::number(allowed);
345+ return false;
346+
347+}
348+
349 }
350
351=== modified file 'tests/peers/exporter/CMakeLists.txt'
352--- tests/peers/exporter/CMakeLists.txt 2014-08-07 19:35:30 +0000
353+++ tests/peers/exporter/CMakeLists.txt 2015-06-02 13:16:53 +0000
354@@ -24,6 +24,7 @@
355 )
356
357 qt5_use_modules(content-hub-test-exporter Core Gui DBus)
358+pkg_check_modules(APPARMOR REQUIRED libapparmor)
359
360 set_target_properties(
361 content-hub-test-exporter
362@@ -35,6 +36,7 @@
363 content-hub-test-exporter
364
365 content-hub
366+ ${APPARMOR_LDFLAGS}
367 )
368
369 install(
370
371=== modified file 'tests/peers/exporter/autoexporter.cpp'
372--- tests/peers/exporter/autoexporter.cpp 2015-04-14 15:28:56 +0000
373+++ tests/peers/exporter/autoexporter.cpp 2015-06-02 13:16:53 +0000
374@@ -24,6 +24,12 @@
375 hub->register_import_export_handler(this);
376 }
377
378+void AutoExporter::setUrl(QString url)
379+{
380+ qDebug() << Q_FUNC_INFO << url;
381+ m_url = url;
382+}
383+
384 void AutoExporter::handle_import(cuc::Transfer *transfer)
385 {
386 qDebug() << Q_FUNC_INFO << "not implemented";
387@@ -42,19 +48,20 @@
388
389 QVector<cuc::Item> items;
390
391- if (transfer->contentType() == cuc::Type::Known::contacts().id()) {
392- items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/Joker.vcf"));
393-
394- if (transfer->selectionType() == cuc::Transfer::SelectionType::multiple) {
395- items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/Stark,_Tony.vcf"));
396+ if (m_url.isEmpty()) {
397+ if (transfer->contentType() == cuc::Type::Known::contacts().id()) {
398+ items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/Joker.vcf"));
399+ if (transfer->selectionType() == cuc::Transfer::SelectionType::multiple) {
400+ items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/Stark,_Tony.vcf"));
401+ }
402+ } else {
403+ items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/webbrowser-app.png"));
404+ if (transfer->selectionType() == cuc::Transfer::SelectionType::multiple) {
405+ items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/clock.png"));
406+ }
407 }
408-
409 } else {
410- items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/webbrowser-app.png"));
411-
412- if (transfer->selectionType() == cuc::Transfer::SelectionType::multiple) {
413- items << cuc::Item(QUrl("file:///usr/share/content-hub/testability/data/clock.png"));
414- }
415+ items << cuc::Item(QUrl(m_url));
416 }
417
418 transfer->charge(items);
419@@ -83,9 +90,11 @@
420
421 qDebug() << Q_FUNC_INFO << "STATE:" << transfer->state();
422
423+ if (transfer->state() == cuc::Transfer::aborted)
424+ QCoreApplication::instance()->exit(1);
425
426 if (transfer->state() == cuc::Transfer::collected)
427- QCoreApplication::instance()->quit();
428+ QCoreApplication::instance()->exit(0);
429 }
430
431
432
433=== modified file 'tests/peers/exporter/autoexporter.h'
434--- tests/peers/exporter/autoexporter.h 2014-08-07 18:48:10 +0000
435+++ tests/peers/exporter/autoexporter.h 2015-06-02 13:16:53 +0000
436@@ -39,6 +39,10 @@
437 Q_INVOKABLE void handle_export(cuc::Transfer*);
438 Q_INVOKABLE void handle_share(cuc::Transfer*);
439 Q_INVOKABLE void stateChanged();
440+ void setUrl(QString);
441+
442+private:
443+ QString m_url;
444 };
445
446 #endif // AUTOEXPORTER_H
447
448=== modified file 'tests/peers/exporter/exporter.cpp'
449--- tests/peers/exporter/exporter.cpp 2014-08-07 18:48:10 +0000
450+++ tests/peers/exporter/exporter.cpp 2015-06-02 13:16:53 +0000
451@@ -18,6 +18,8 @@
452
453 #include <QCoreApplication>
454 #include <QStringList>
455+#include <QUrl>
456+#include <sys/apparmor.h>
457
458 #include "autoexporter.h"
459
460@@ -30,12 +32,26 @@
461 qputenv("APP_ID", "content-hub-test-exporter");
462 }
463
464- AutoExporter exporter;
465-
466- QString peerName;
467+ QString peerName, url, profile;
468
469 if (a.arguments().size() > 1)
470 peerName = a.arguments().at(1);
471+ if (a.arguments().size() > 2)
472+ url = a.arguments().at(2);
473+ if (a.arguments().size() > 3)
474+ profile = a.arguments().at(3);
475+
476+ if (not profile.isEmpty()) {
477+ int ret = 2;
478+ ret = aa_change_profile(profile.toStdString().c_str());
479+ if (ret != 0)
480+ return 1;
481+ }
482+
483+ AutoExporter exporter;
484+
485+ if (not url.isEmpty())
486+ exporter.setUrl(url);
487
488 if (!peerName.isEmpty())
489 {

Subscribers

People subscribed via source and target branches