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 
28 namespace media = core::ubuntu::media;
29 namespace ubuntu = apparmor::ubuntu;
30 
31 namespace
32 {
33 struct Uri
34 {
35  std::string scheme;
36  std::string authority;
37  std::string path;
38  std::string query;
39  std::string fragment;
40 };
41 
42 // Poor mans version of a uri parser.
43 // See https://tools.ietf.org/html/rfc3986#appendix-B
44 Uri parse_uri(const std::string& s)
45 {
46  // Indices into the regex match go here.
47  struct Index
48  {
49  const std::size_t scheme{2};
50  const std::size_t authority{4};
51  const std::size_t path{5};
52  const std::size_t query{7};
53  const std::size_t fragment{9};
54  } static index;
55 
56  static const std::regex regex{R"delim(^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)delim"};
57  std::smatch match;
58 
59  if (not std::regex_match(s, match, regex)) throw std::runtime_error
60  {
61  "Not a valid URI: " + s
62  };
63 
64  return Uri
65  {
66  match.str(index.scheme),
67  match.str(index.authority),
68  match.str(index.path),
69  match.str(index.query),
70  match.str(index.fragment)
71  };
72 }
73 
74 static constexpr std::size_t index_package{1};
75 static constexpr std::size_t index_app{2};
76 static const std::string unity_name{"unity8-dash"};
77 
78 // Returns true if the context name is a valid Ubuntu app id.
79 // If it is, out is populated with the package and app name.
80 bool process_context_name(const std::string& s, std::smatch& out,
81  std::string& pkg_name)
82 {
83  // See https://wiki.ubuntu.com/AppStore/Interfaces/ApplicationId.
84  static const std::regex short_re{"(.*)_(.*)"};
85  static const std::regex full_re{"(.*)_(.*)_(.*)"};
86  static const std::regex trust_store_re{"(.*)-(.*)"};
87 
88  if ((s == "messaging-app" or s == unity_name)
89  and std::regex_match(s, out, trust_store_re))
90  {
91  pkg_name = s;
92  return true;
93  }
94 
95  if (std::regex_match(s, out, full_re) or std::regex_match(s, out, short_re))
96  {
97  pkg_name = out[index_package];
98  return true;
99  }
100 
101  return false;
102 }
103 }
104 
105 apparmor::ubuntu::Context::Context(const std::string& name)
106  : apparmor::Context{name},
107  unconfined_{str() == ubuntu::unconfined},
108  unity_{name == unity_name},
109  has_package_name_{process_context_name(str(), match_, pkg_name_)}
110 {
111  MH_DEBUG("apparmor profile name: %s", name);
112  MH_DEBUG("is_unconfined(): %s", (is_unconfined() ? "true" : "false"));
113  MH_DEBUG("has_package_name(): %s", (has_package_name() ? "true" : "false"));
114  if (not is_unconfined() and not is_unity() and not has_package_name())
115  throw std::logic_error
116  {
117  "apparmor::ubuntu::Context: Invalid profile name " + str()
118  };
119 }
120 
122 {
123  return unconfined_;
124 }
125 
127 {
128  return unity_;
129 }
130 
132 {
133  return has_package_name_;
134 }
135 
137 {
138  return pkg_name_;
139 }
140 
142 {
143  return std::string{match_[index_package]} + "-" + std::string{match_[index_app]};
144 }
145 
147 {
148 }
149 
151  const std::string& name,
153 {
154  dbus_daemon.get_connection_app_armor_security_async(name, [cb](const std::string& context_name)
155  {
156  cb(apparmor::ubuntu::Context{context_name});
157  });
158 }
159 
161 {
162  if (context.is_unconfined())
163  return Result{true, "Client allowed access since it's unconfined"};
164 
165  Uri parsed_uri = parse_uri(uri);
166 
167  MH_DEBUG("context.profile_name(): %s", context.profile_name());
168  MH_DEBUG("parsed_uri.path: %s", parsed_uri.path);
169 
170  // All confined apps can access their own files
171  if (parsed_uri.path.find(std::string(".local/share/" + context.package_name() + "/")) != std::string::npos ||
172  parsed_uri.path.find(std::string(".cache/" + context.package_name() + "/")) != std::string::npos)
173  {
174  return Result
175  {
176  true,
177  "Client can access content in ~/.local/share/" + context.package_name() + " or ~/.cache/" + context.package_name()
178  };
179  }
180  // Check for trust-store compatible path name using full messaging-app profile_name
181  else if (context.profile_name() == "messaging-app" &&
182  /* Since the full APP_ID is not available yet (see aa_query_file_path()), add an exception: */
183  (parsed_uri.path.find(std::string(".local/share/com.ubuntu." + context.profile_name() + "/")) != std::string::npos ||
184  parsed_uri.path.find(std::string(".cache/com.ubuntu." + context.profile_name() + "/")) != std::string::npos))
185  {
186  return Result
187  {
188  true,
189  "Client can access content in ~/.local/share/" + context.profile_name() + " or ~/.cache/" + context.profile_name()
190  };
191  }
192  else if (parsed_uri.path.find(std::string("opt/click.ubuntu.com/")) != std::string::npos &&
193  parsed_uri.path.find(context.package_name()) != std::string::npos)
194  {
195  return Result{true, "Client can access content in own opt directory"};
196  }
197  else if ((parsed_uri.path.find(std::string("/system/media/audio/ui/")) != std::string::npos ||
198  parsed_uri.path.find(std::string("/android/system/media/audio/ui/")) != std::string::npos) &&
199  context.package_name() == "com.ubuntu.camera")
200  {
201  return Result{true, "Camera app can access ui sounds"};
202  }
203 
204  // TODO: Check if the trust store previously allowed direct access to uri
205 
206  // Check in ~/Music and ~/Videos
207  // TODO: when the trust store lands, check it to see if this app can access the dirs and
208  // then remove the explicit whitelist of the music-app, and gallery-app
209  else if ((context.package_name() == "com.ubuntu.music" || context.package_name() == "com.ubuntu.gallery" ||
210  context.profile_name() == unity_name) &&
211  (parsed_uri.path.find(std::string("Music/")) != std::string::npos ||
212  parsed_uri.path.find(std::string("Videos/")) != std::string::npos ||
213  parsed_uri.path.find(std::string("/media")) != std::string::npos))
214  {
215  return Result{true, "Client can access content in ~/Music or ~/Videos"};
216  }
217  else if (parsed_uri.path.find(std::string("/usr/share/sounds")) != std::string::npos)
218  {
219  return Result{true, "Client can access content in /usr/share/sounds"};
220  }
221  else if (parsed_uri.scheme == "http" ||
222  parsed_uri.scheme == "https" ||
223  parsed_uri.scheme == "rtsp")
224  {
225  return Result{true, "Client can access streaming content"};
226  }
227 
228  return Result{false, "Client is not allowed to access: " + uri};
229 }
230 
231 // Returns the platform-default implementation of RequestContextResolver.
233 {
234  return std::make_shared<apparmor::ubuntu::DBusDaemonRequestContextResolver>(es.session);
235 }
236 
237 // Returns the platform-default implementation of RequestAuthenticator.
239 {
240  return std::make_shared<apparmor::ubuntu::ExistingAuthenticator>();
241 }
RequestAuthenticator::Ptr make_platform_default_request_authenticator()
std::shared_ptr< RequestContextResolver > Ptr
Definition: ubuntu.h:94
virtual std::string profile_name() const
Definition: ubuntu.cpp:141
#define MH_DEBUG(...)
Definition: logger.h:123
RequestContextResolver::Ptr make_platform_default_request_context_resolver(helper::ExternalServices &es)
void resolve_context_for_dbus_name_async(const std::string &name, ResolveCallback) override
Definition: ubuntu.cpp:150
void get_connection_app_armor_security_async(const std::string &name, std::function< void(const std::string &)> handler)
Definition: dbus.h:76
std::function< void(const Context &)> ResolveCallback
Definition: ubuntu.h:97
Result authenticate_open_uri_request(const Context &, const std::string &uri) override
Definition: ubuntu.cpp:160
virtual std::string package_name() const
Definition: ubuntu.cpp:136
const std::string & str() const
Definition: context.cpp:31
std::shared_ptr< RequestAuthenticator > Ptr
Definition: ubuntu.h:134