Music Hub ..
A session-wide music playback service
 
Loading...
Searching...
No Matches
lomiri.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2014 Canonical Ltd.
3 * Copyright © 2022 UBports Foundation.
4 *
5 * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License version 3,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Authored by: Thomas Voß <thomas.voss@canonical.com>
20 */
21
22#include "apparmor/lomiri.h"
23
24#include "logging.h"
25
26#include <QDBusMessage>
27#include <QDBusPendingCall>
28#include <QDBusPendingCallWatcher>
29#include <QDBusReply>
30#include <QDebug>
31#include <QString>
32#include <QUrl>
33#include <QVariantList>
34#include <QVariantMap>
35
36#include <glib.h>
37#include <click.h>
38
39#include <sys/apparmor.h>
40#include <unistd.h> // geteuid()
41
44namespace MediaHubService = apparmor::lomiri;
45
46namespace
47{
48
49static constexpr std::size_t index_package{0};
50static constexpr std::size_t index_app{1};
51
52// Returns true if the context name is a valid Lomiri app id.
53// If it is, out is populated with the package and app name.
54bool process_context_name(const QString &s, QStringList &out,
55 QString &pkg_name)
56{
57 // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
58
59 if (s == "lomiri-messaging-app")
60 {
61 pkg_name = s;
62 return true;
63 }
64
65
66 out = s.split('_');
67 if (out.count() == 2 || out.count() == 3)
68 {
69 pkg_name = out[index_package];
70 return true;
71 }
72
73 return false;
74}
75}
76
77apparmor::lomiri::Context::Context(const QString &name)
78 : apparmor::Context{name},
79 unconfined_{str() == lomiri::unconfined},
80 has_package_name_{process_context_name(str(), app_id_parts, pkg_name_)}
81{
82 MH_DEBUG("apparmor profile name: %s", qUtf8Printable(name));
83 MH_DEBUG("is_unconfined(): %s", (is_unconfined() ? "true" : "false"));
84 MH_DEBUG("has_package_name(): %s", (has_package_name() ? "true" : "false"));
85 if (not is_unconfined() and not has_package_name()) {
86 MH_FATAL("apparmor::lomiri::Context: Invalid profile name %s", qUtf8Printable(str()));
87 }
88}
89
91{
92 return unconfined_;
93}
94
96{
97 return has_package_name_;
98}
99
101{
102 return pkg_name_;
103}
104
106{
107 switch (app_id_parts.count()) {
108 case 3:
109 return app_id_parts[2];
110 case 2:
111 return app_id_parts[1];
112 default:
113 return str();
114 }
115}
116
118{
119 return app_id_parts.isEmpty() ?
120 str() : (app_id_parts[index_package] + "-" + app_id_parts[index_app]);
121}
122
124 m_connection(QDBusConnection::sessionBus())
125{
126}
127
129 const QString &name,
131{
132 const QString dbusServiceName =
133 qEnvironmentVariable("MEDIA_HUB_MOCKED_DBUS", "org.freedesktop.DBus");
134 QDBusMessage msg =
135 QDBusMessage::createMethodCall(dbusServiceName,
136 "/org/freedesktop/DBus",
137 "org.freedesktop.DBus",
138 "GetConnectionCredentials");
139 msg.setArguments({ name });
140 QDBusPendingCall call = QDBusConnection::sessionBus().asyncCall(msg);
141 QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call);
142 QObject::connect(callWatcher, &QDBusPendingCallWatcher::finished,
143 [cb](QDBusPendingCallWatcher *callWatcher) {
144 QDBusReply<QVariantMap> reply(*callWatcher);
145 QString appId;
146 if (reply.isValid()) {
147 QVariantMap map = reply.value();
148 QByteArray context = map.value("LinuxSecurityLabel").toByteArray();
149 if (!context.isEmpty()) {
150 aa_splitcon(context.data(), NULL);
151 appId = QString::fromUtf8(context);
152 }
153 } else {
154 QDBusError error = reply.error();
155 qWarning() << "Error getting app ID:" << error.name() <<
156 error.message();
157 }
158 cb(apparmor::lomiri::Context(appId));
159 callWatcher->deleteLater();
160 });
161}
162
163bool apparmor::lomiri::ExistingAuthenticator::is_click_package_path(const apparmor::lomiri::Context& context, const QString &path)
164{
165 g_autoptr(ClickDB) db = 0;
166 g_autoptr(GError) error = 0;
167 g_autofree gchar *click_path = 0;
168 QString package = context.package_name();
169 QString version = context.package_version();
170
171 db = click_db_new();
172 if (!db) {
173 qWarning() << "Failed to create ClickDB";
174 return false;
175 }
176 click_db_read(db, 0, &error);
177 if (error) {
178 qWarning() << "Error reading click DB:" << error->message;
179 return false;
180 }
181 click_path = click_db_get_path(db, package.toUtf8(), version.toUtf8(), &error);
182 if (error) {
183 MH_DEBUG("click package path could not be determined for %s: %s", qUtf8Printable(context.package_name()), error->message);
184 return false;
185 }
186
187 return path.startsWith(QString(click_path));
188}
189
191{
192 if (context.is_unconfined())
193 return Result{true, "Client allowed access since it's unconfined"};
194
195 QString path = uri.path();
196 MH_DEBUG("context.profile_name(): %s", qUtf8Printable(context.profile_name()));
197 MH_DEBUG("parsed_uri.path: %s", qUtf8Printable(path));
198
199 // All confined apps can access their own files
200 if (path.contains(".local/share/" + context.package_name() + "/") ||
201 path.contains(".cache/" + context.package_name() + "/") ||
202 path.contains("/run/user/" + QString::number(geteuid()) + "/confined/" + context.package_name()))
203 {
204 return Result
205 {
206 true,
207 "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
208 };
209 }
210 // Check for trust-store compatible path name using full messaging-app profile_name
211 else if (context.package_name() == "lomiri-messaging-app" &&
212 /* Since the full APP_ID is not available yet (see aa_query_file_path()), add an exception: */
213 (path.contains(".local/share/messaging-app.ubports/") ||
214 path.contains(".cache/messaging-app.ubports/")))
215 {
216 return Result
217 {
218 true,
219 "Client can access content in ~/.local/share/messaging-app.ubports or ~/.cache/messaging-app.ubports"
220 };
221 }
222 else if (is_click_package_path(context, path))
223 {
224 return Result{true, "Client can access content in own opt directory"};
225 }
226 else if ((path.contains("/system/media/audio/ui/") ||
227 path.contains("/android/system/media/audio/ui/")) &&
228 context.package_name() == "camera.ubports")
229 {
230 return Result{true, "Camera app can access ui sounds"};
231 }
232
233 // TODO: Check if the trust store previously allowed direct access to uri
234
235 // Check in ~/Music and ~/Videos
236 // TODO: when the trust store lands, check it to see if this app can access the dirs and
237 // then remove the explicit whitelist of the music-app, and gallery-app
238 else if ((context.package_name() == "music.ubports" || context.package_name() == "gallery.ubports") &&
239 (path.contains("Music/") ||
240 path.contains("Videos/") ||
241 path.contains("/media")))
242 {
243 return Result{true, "Client can access content in ~/Music or ~/Videos"};
244 }
245 else if (path.contains("/usr/share/sounds"))
246 {
247 return Result{true, "Client can access content in /usr/share/sounds"};
248 }
249 else if (uri.scheme() == "http" ||
250 uri.scheme() == "https" ||
251 uri.scheme() == "rtp" ||
252 uri.scheme() == "rtmp" ||
253 uri.scheme() == "rtmps" ||
254 uri.scheme() == "rtsp" ||
255 uri.scheme() == "udp")
256 {
257 return Result{true, "Client can access streaming content"};
258 }
259
260 return Result{false, "Client is not allowed to access: " + uri.toString()};
261}
void resolve_context_for_dbus_name_async(const QString &name, ResolveCallback) override
Definition lomiri.cpp:128
std::function< void(const Context &)> ResolveCallback
Definition lomiri.h:88
#define MH_FATAL(...)
Definition logging.h:42
#define MH_DEBUG(...)
Definition logging.h:38
Result authenticate_open_uri_request(const Context &, const QUrl &uri) override
Definition lomiri.cpp:190