Music Hub ..
A session-wide music playback service
ubuntu.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2014 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License version 3,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Thomas Voß <thomas.voss@canonical.com>
17 */
18
20
22
24
25#include <regex>
26#include <unistd.h> // geteuid()
27
29namespace media = core::ubuntu::media;
30namespace ubuntu = apparmor::ubuntu;
31
32namespace
33{
34struct Uri
35{
36 std::string scheme;
37 std::string authority;
38 std::string path;
39 std::string query;
40 std::string fragment;
41};
42
43// Poor mans version of a uri parser.
44// See https://tools.ietf.org/html/rfc3986#appendix-B
45Uri parse_uri(const std::string& s)
46{
47 // Indices into the regex match go here.
48 struct Index
49 {
50 const std::size_t scheme{2};
51 const std::size_t authority{4};
52 const std::size_t path{5};
53 const std::size_t query{7};
54 const std::size_t fragment{9};
55 } static index;
56
57 static const std::regex regex{R"delim(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)delim"};
58 std::smatch match;
59
60 if (not std::regex_match(s, match, regex)) throw std::runtime_error
61 {
62 "Not a valid URI: " + s
63 };
64
65 return Uri
66 {
67 match.str(index.scheme),
68 match.str(index.authority),
69 match.str(index.path),
70 match.str(index.query),
71 match.str(index.fragment)
72 };
73}
74
75static constexpr std::size_t index_package{1};
76static constexpr std::size_t index_app{2};
77static const std::string unity_name{"unity8-dash"};
78static const std::string unity8_snap_name{"snap.unity8-session.unity8-session"};
79
80// ad-hoc for mediaplayer-app/music-app until it settles down with proper handling
81// Bug #1642611
82static const std::string mediaplayer_snap_name{"snap.mediaplayer-app.mediaplayer-app"};
83static const std::string music_snap_name{"snap.music-app.music-app"};
84// Returns true if the context name is a valid Ubuntu app id.
85// If it is, out is populated with the package and app name.
86bool process_context_name(const std::string& s, std::smatch& out,
87 std::string& pkg_name)
88{
89 // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
90 static const std::regex short_re{"(.*)_(.*)"};
91 static const std::regex full_re{"(.*)_(.*)_(.*)"};
92 static const std::regex trust_store_re{"(.*)-(.*)"};
93
94 if ((s == "messaging-app" or s == unity_name or s == unity8_snap_name or
95 s == mediaplayer_snap_name or s == music_snap_name)
96 and std::regex_match(s, out, trust_store_re))
97 {
98 pkg_name = s;
99 return true;
100 }
101
102 if (std::regex_match(s, out, full_re) or std::regex_match(s, out, short_re))
103 {
104 pkg_name = out[index_package];
105 return true;
106 }
107
108 return false;
109}
110}
111
112apparmor::ubuntu::Context::Context(const std::string& name)
113 : apparmor::Context{name},
114 unconfined_{str() == ubuntu::unconfined},
115 unity_{name == unity_name || name == unity8_snap_name},
116 has_package_name_{process_context_name(str(), match_, pkg_name_)}
117{
118 MH_DEBUG("apparmor profile name: %s", name);
119 MH_DEBUG("is_unconfined(): %s", (is_unconfined() ? "true" : "false"));
120 MH_DEBUG("has_package_name(): %s", (has_package_name() ? "true" : "false"));
121 if (not is_unconfined() and not is_unity() and not has_package_name())
122 throw std::logic_error
123 {
124 "apparmor::ubuntu::Context: Invalid profile name " + str()
125 };
126}
127
129{
130 return unconfined_;
131}
132
134{
135 return unity_;
136}
137
139{
140 return has_package_name_;
141}
142
144{
145 return pkg_name_;
146}
147
149{
150 return std::string{match_[index_package]} + "-" + std::string{match_[index_app]};
151}
152
154{
155}
156
158 const std::string& name,
160{
161 dbus_daemon.get_connection_app_armor_security_async(name, [cb](const std::string& context_name)
162 {
163 cb(apparmor::ubuntu::Context{context_name});
164 });
165}
166
168{
169 if (context.is_unconfined())
170 return Result{true, "Client allowed access since it's unconfined"};
171
172 Uri parsed_uri = parse_uri(uri);
173
174 MH_DEBUG("context.profile_name(): %s", context.profile_name());
175 MH_DEBUG("parsed_uri.path: %s", parsed_uri.path);
176
177 // All confined apps can access their own files
178 if (parsed_uri.path.find(std::string(".local/share/" + context.package_name() + "/")) != std::string::npos ||
179 parsed_uri.path.find(std::string(".cache/" + context.package_name() + "/")) != std::string::npos ||
180 parsed_uri.path.find(std::string("/run/user/" + std::to_string(geteuid()) + "/confined/" + context.package_name())) != std::string::npos)
181 {
182 return Result
183 {
184 true,
185 "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
186 };
187 }
188 // Check for trust-store compatible path name using full messaging-app profile_name
189 else if (context.profile_name() == "messaging-app" &&
190 /* Since the full APP_ID is not available yet (see aa_query_file_path()), add an exception: */
191 (parsed_uri.path.find(std::string(".local/share/com.ubuntu." + context.profile_name() + "/")) != std::string::npos ||
192 parsed_uri.path.find(std::string(".cache/com.ubuntu." + context.profile_name() + "/")) != std::string::npos))
193 {
194 return Result
195 {
196 true,
197 "Client can access content in ~/.local/share/" + context.profile_name() + " or ~/.cache/" + context.profile_name()
198 };
199 }
200 else if (parsed_uri.path.find(std::string("opt/click.ubuntu.com/")) != std::string::npos &&
201 parsed_uri.path.find(context.package_name()) != std::string::npos)
202 {
203 return Result{true, "Client can access content in own opt directory"};
204 }
205 else if ((parsed_uri.path.find(std::string("/system/media/audio/ui/")) != std::string::npos ||
206 parsed_uri.path.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) &&
207 context.package_name() == "com.ubuntu.camera")
208 {
209 return Result{true, "Camera app can access ui sounds"};
210 }
211
212 // TODO: Check if the trust store previously allowed direct access to uri
213
214 // Check in ~/Music and ~/Videos
215 // TODO: when the trust store lands, check it to see if this app can access the dirs and
216 // then remove the explicit whitelist of the music-app, and gallery-app
217 else if ((context.package_name() == "com.ubuntu.music" || context.package_name() == "com.ubuntu.gallery" ||
218 context.profile_name() == unity_name || context.profile_name() == unity8_snap_name ||
219 context.profile_name() == mediaplayer_snap_name || context.profile_name() == music_snap_name) &&
220 (parsed_uri.path.find(std::string("Music/")) != std::string::npos ||
221 parsed_uri.path.find(std::string("Videos/")) != std::string::npos ||
222 parsed_uri.path.find(std::string("/media")) != std::string::npos))
223 {
224 return Result{true, "Client can access content in ~/Music or ~/Videos"};
225 }
226 else if (parsed_uri.path.find(std::string("/usr/share/sounds")) != std::string::npos)
227 {
228 return Result{true, "Client can access content in /usr/share/sounds"};
229 }
230 else if (parsed_uri.scheme == "http" ||
231 parsed_uri.scheme == "https" ||
232 parsed_uri.scheme == "rtsp")
233 {
234 return Result{true, "Client can access streaming content"};
235 }
236
237 return Result{false, "Client is not allowed to access: " + uri};
238}
239
240// Returns the platform-default implementation of RequestContextResolver.
242{
243 return std::make_shared<apparmor::ubuntu::DBusDaemonRequestContextResolver>(es.session);
244}
245
246// Returns the platform-default implementation of RequestAuthenticator.
248{
249 return std::make_shared<apparmor::ubuntu::ExistingAuthenticator>();
250}
const std::string & str() const
Definition: context.cpp:32
virtual std::string package_name() const
Definition: ubuntu.cpp:143
virtual std::string profile_name() const
Definition: ubuntu.cpp:148
void resolve_context_for_dbus_name_async(const std::string &name, ResolveCallback) override
Definition: ubuntu.cpp:157
std::shared_ptr< RequestAuthenticator > Ptr
Definition: ubuntu.h:134
std::shared_ptr< RequestContextResolver > Ptr
Definition: ubuntu.h:94
std::function< void(const Context &)> ResolveCallback
Definition: ubuntu.h:97
#define MH_DEBUG(...)
Definition: logger.h:123
RequestAuthenticator::Ptr make_platform_default_request_authenticator()
RequestContextResolver::Ptr make_platform_default_request_context_resolver(helper::ExternalServices &es)
Result authenticate_open_uri_request(const Context &, const std::string &uri) override
Definition: ubuntu.cpp:167