1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/common/permissions/permissions_data.h"
6
7#include "base/command_line.h"
8#include "content/public/common/url_constants.h"
9#include "extensions/common/constants.h"
10#include "extensions/common/error_utils.h"
11#include "extensions/common/extensions_client.h"
12#include "extensions/common/manifest.h"
13#include "extensions/common/manifest_constants.h"
14#include "extensions/common/manifest_handlers/permissions_parser.h"
15#include "extensions/common/permissions/permission_message_provider.h"
16#include "extensions/common/switches.h"
17#include "extensions/common/url_pattern_set.h"
18#include "extensions/common/user_script.h"
19#include "url/gurl.h"
20#include "url/url_constants.h"
21
22namespace extensions {
23
24namespace {
25
26PermissionsData::PolicyDelegate* g_policy_delegate = NULL;
27
28}  // namespace
29
30PermissionsData::PermissionsData(const Extension* extension)
31    : extension_id_(extension->id()), manifest_type_(extension->GetType()) {
32  base::AutoLock auto_lock(runtime_lock_);
33  scoped_refptr<const PermissionSet> required_permissions =
34      PermissionsParser::GetRequiredPermissions(extension);
35  active_permissions_unsafe_ =
36      new PermissionSet(required_permissions->apis(),
37                        required_permissions->manifest_permissions(),
38                        required_permissions->explicit_hosts(),
39                        required_permissions->scriptable_hosts());
40  withheld_permissions_unsafe_ = new PermissionSet();
41}
42
43PermissionsData::~PermissionsData() {
44}
45
46// static
47void PermissionsData::SetPolicyDelegate(PolicyDelegate* delegate) {
48  g_policy_delegate = delegate;
49}
50
51// static
52bool PermissionsData::CanSilentlyIncreasePermissions(
53    const Extension* extension) {
54  return extension->location() != Manifest::INTERNAL;
55}
56
57// static
58bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) {
59  if (extension->location() == Manifest::COMPONENT)
60    return true;
61
62  const ExtensionsClient::ScriptingWhitelist& whitelist =
63      ExtensionsClient::Get()->GetScriptingWhitelist();
64
65  return std::find(whitelist.begin(), whitelist.end(), extension->id()) !=
66         whitelist.end();
67}
68
69bool PermissionsData::ShouldSkipPermissionWarnings(
70    const std::string& extension_id) {
71  // See http://b/4946060 for more details.
72  return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd");
73}
74
75// static
76bool PermissionsData::IsRestrictedUrl(const GURL& document_url,
77                                      const GURL& top_frame_url,
78                                      const Extension* extension,
79                                      std::string* error) {
80  if (extension && CanExecuteScriptEverywhere(extension))
81    return false;
82
83  // Check if the scheme is valid for extensions. If not, return.
84  if (!URLPattern::IsValidSchemeForExtensions(document_url.scheme()) &&
85      document_url.spec() != url::kAboutBlankURL) {
86    if (error) {
87      *error = ErrorUtils::FormatErrorMessage(
88                   manifest_errors::kCannotAccessPage,
89                   document_url.spec());
90    }
91    return true;
92  }
93
94  if (!ExtensionsClient::Get()->IsScriptableURL(document_url, error))
95    return true;
96
97  bool allow_on_chrome_urls = base::CommandLine::ForCurrentProcess()->HasSwitch(
98                                  switches::kExtensionsOnChromeURLs);
99  if (document_url.SchemeIs(content::kChromeUIScheme) &&
100      !allow_on_chrome_urls) {
101    if (error)
102      *error = manifest_errors::kCannotAccessChromeUrl;
103    return true;
104  }
105
106  if (extension && top_frame_url.SchemeIs(kExtensionScheme) &&
107      top_frame_url.host() != extension->id() && !allow_on_chrome_urls) {
108    if (error)
109      *error = manifest_errors::kCannotAccessExtensionUrl;
110    return true;
111  }
112
113  return false;
114}
115
116void PermissionsData::SetPermissions(
117    const scoped_refptr<const PermissionSet>& active,
118    const scoped_refptr<const PermissionSet>& withheld) const {
119  base::AutoLock auto_lock(runtime_lock_);
120  active_permissions_unsafe_ = active;
121  withheld_permissions_unsafe_ = withheld;
122}
123
124void PermissionsData::UpdateTabSpecificPermissions(
125    int tab_id,
126    scoped_refptr<const PermissionSet> permissions) const {
127  base::AutoLock auto_lock(runtime_lock_);
128  CHECK_GE(tab_id, 0);
129  TabPermissionsMap::iterator iter = tab_specific_permissions_.find(tab_id);
130  if (iter == tab_specific_permissions_.end())
131    tab_specific_permissions_[tab_id] = permissions;
132  else
133    iter->second =
134        PermissionSet::CreateUnion(iter->second.get(), permissions.get());
135}
136
137void PermissionsData::ClearTabSpecificPermissions(int tab_id) const {
138  base::AutoLock auto_lock(runtime_lock_);
139  CHECK_GE(tab_id, 0);
140  tab_specific_permissions_.erase(tab_id);
141}
142
143bool PermissionsData::HasAPIPermission(APIPermission::ID permission) const {
144  return active_permissions()->HasAPIPermission(permission);
145}
146
147bool PermissionsData::HasAPIPermission(
148    const std::string& permission_name) const {
149  return active_permissions()->HasAPIPermission(permission_name);
150}
151
152bool PermissionsData::HasAPIPermissionForTab(
153    int tab_id,
154    APIPermission::ID permission) const {
155  if (HasAPIPermission(permission))
156    return true;
157
158  scoped_refptr<const PermissionSet> tab_permissions =
159      GetTabSpecificPermissions(tab_id);
160
161  // Place autolock below the HasAPIPermission() and
162  // GetTabSpecificPermissions(), since each already acquires the lock.
163  base::AutoLock auto_lock(runtime_lock_);
164  return tab_permissions.get() && tab_permissions->HasAPIPermission(permission);
165}
166
167bool PermissionsData::CheckAPIPermissionWithParam(
168    APIPermission::ID permission,
169    const APIPermission::CheckParam* param) const {
170  return active_permissions()->CheckAPIPermissionWithParam(permission, param);
171}
172
173const URLPatternSet& PermissionsData::GetEffectiveHostPermissions() const {
174  return active_permissions()->effective_hosts();
175}
176
177bool PermissionsData::HasHostPermission(const GURL& url) const {
178  return active_permissions()->HasExplicitAccessToOrigin(url);
179}
180
181bool PermissionsData::HasEffectiveAccessToAllHosts() const {
182  return active_permissions()->HasEffectiveAccessToAllHosts();
183}
184
185PermissionMessages PermissionsData::GetPermissionMessages() const {
186  if (ShouldSkipPermissionWarnings(extension_id_)) {
187    return PermissionMessages();
188  } else {
189    return PermissionMessageProvider::Get()->GetPermissionMessages(
190        active_permissions().get(), manifest_type_);
191  }
192}
193
194std::vector<base::string16> PermissionsData::GetPermissionMessageStrings()
195    const {
196  if (ShouldSkipPermissionWarnings(extension_id_))
197    return std::vector<base::string16>();
198  return PermissionMessageProvider::Get()->GetWarningMessages(
199      active_permissions().get(), manifest_type_);
200}
201
202std::vector<base::string16>
203PermissionsData::GetPermissionMessageDetailsStrings() const {
204  if (ShouldSkipPermissionWarnings(extension_id_))
205    return std::vector<base::string16>();
206  return PermissionMessageProvider::Get()->GetWarningMessagesDetails(
207      active_permissions().get(), manifest_type_);
208}
209
210bool PermissionsData::HasWithheldImpliedAllHosts() const {
211  // Since we currently only withhold all_hosts, it's sufficient to check
212  // that either set is not empty.
213  return !withheld_permissions()->explicit_hosts().is_empty() ||
214         !withheld_permissions()->scriptable_hosts().is_empty();
215}
216
217bool PermissionsData::CanAccessPage(const Extension* extension,
218                                    const GURL& document_url,
219                                    const GURL& top_frame_url,
220                                    int tab_id,
221                                    int process_id,
222                                    std::string* error) const {
223  AccessType result = CanRunOnPage(extension,
224                                   document_url,
225                                   top_frame_url,
226                                   tab_id,
227                                   process_id,
228                                   active_permissions()->explicit_hosts(),
229                                   withheld_permissions()->explicit_hosts(),
230                                   error);
231  // TODO(rdevlin.cronin) Update callers so that they only need ACCESS_ALLOWED.
232  return result == ACCESS_ALLOWED || result == ACCESS_WITHHELD;
233}
234
235PermissionsData::AccessType PermissionsData::GetPageAccess(
236    const Extension* extension,
237    const GURL& document_url,
238    const GURL& top_frame_url,
239    int tab_id,
240    int process_id,
241    std::string* error) const {
242  return CanRunOnPage(extension,
243                      document_url,
244                      top_frame_url,
245                      tab_id,
246                      process_id,
247                      active_permissions()->explicit_hosts(),
248                      withheld_permissions()->explicit_hosts(),
249                      error);
250}
251
252bool PermissionsData::CanRunContentScriptOnPage(const Extension* extension,
253                                                const GURL& document_url,
254                                                const GURL& top_frame_url,
255                                                int tab_id,
256                                                int process_id,
257                                                std::string* error) const {
258  AccessType result = CanRunOnPage(extension,
259                                   document_url,
260                                   top_frame_url,
261                                   tab_id,
262                                   process_id,
263                                   active_permissions()->scriptable_hosts(),
264                                   withheld_permissions()->scriptable_hosts(),
265                                   error);
266  // TODO(rdevlin.cronin) Update callers so that they only need ACCESS_ALLOWED.
267  return result == ACCESS_ALLOWED || result == ACCESS_WITHHELD;
268}
269
270PermissionsData::AccessType PermissionsData::GetContentScriptAccess(
271    const Extension* extension,
272    const GURL& document_url,
273    const GURL& top_frame_url,
274    int tab_id,
275    int process_id,
276    std::string* error) const {
277  return CanRunOnPage(extension,
278                      document_url,
279                      top_frame_url,
280                      tab_id,
281                      process_id,
282                      active_permissions()->scriptable_hosts(),
283                      withheld_permissions()->scriptable_hosts(),
284                      error);
285}
286
287bool PermissionsData::CanCaptureVisiblePage(int tab_id,
288                                            std::string* error) const {
289  const URLPattern all_urls(URLPattern::SCHEME_ALL,
290                            URLPattern::kAllUrlsPattern);
291
292  if (active_permissions()->explicit_hosts().ContainsPattern(all_urls))
293    return true;
294
295  if (tab_id >= 0) {
296    scoped_refptr<const PermissionSet> tab_permissions =
297        GetTabSpecificPermissions(tab_id);
298    if (tab_permissions.get() &&
299        tab_permissions->HasAPIPermission(APIPermission::kTab)) {
300      return true;
301    }
302    if (error)
303      *error = manifest_errors::kActiveTabPermissionNotGranted;
304    return false;
305  }
306
307  if (error)
308    *error = manifest_errors::kAllURLOrActiveTabNeeded;
309  return false;
310}
311
312scoped_refptr<const PermissionSet> PermissionsData::GetTabSpecificPermissions(
313    int tab_id) const {
314  base::AutoLock auto_lock(runtime_lock_);
315  CHECK_GE(tab_id, 0);
316  TabPermissionsMap::const_iterator iter =
317      tab_specific_permissions_.find(tab_id);
318  return (iter != tab_specific_permissions_.end()) ? iter->second : NULL;
319}
320
321bool PermissionsData::HasTabSpecificPermissionToExecuteScript(
322    int tab_id,
323    const GURL& url) const {
324  if (tab_id >= 0) {
325    scoped_refptr<const PermissionSet> tab_permissions =
326        GetTabSpecificPermissions(tab_id);
327    if (tab_permissions.get() &&
328        tab_permissions->explicit_hosts().MatchesSecurityOrigin(url)) {
329      return true;
330    }
331  }
332  return false;
333}
334
335PermissionsData::AccessType PermissionsData::CanRunOnPage(
336    const Extension* extension,
337    const GURL& document_url,
338    const GURL& top_frame_url,
339    int tab_id,
340    int process_id,
341    const URLPatternSet& permitted_url_patterns,
342    const URLPatternSet& withheld_url_patterns,
343    std::string* error) const {
344  if (g_policy_delegate &&
345      !g_policy_delegate->CanExecuteScriptOnPage(
346          extension, document_url, top_frame_url, tab_id, process_id, error)) {
347    return ACCESS_DENIED;
348  }
349
350  if (IsRestrictedUrl(document_url, top_frame_url, extension, error))
351    return ACCESS_DENIED;
352
353  if (HasTabSpecificPermissionToExecuteScript(tab_id, top_frame_url))
354    return ACCESS_ALLOWED;
355
356  if (permitted_url_patterns.MatchesURL(document_url))
357    return ACCESS_ALLOWED;
358
359  if (withheld_url_patterns.MatchesURL(document_url))
360    return ACCESS_WITHHELD;
361
362  if (error) {
363    *error = ErrorUtils::FormatErrorMessage(manifest_errors::kCannotAccessPage,
364                                            document_url.spec());
365  }
366  return ACCESS_DENIED;
367}
368
369}  // namespace extensions
370