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