1// Copyright (c) 2012 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 "chrome/browser/extensions/permissions_updater.h"
6
7#include "base/json/json_writer.h"
8#include "base/memory/ref_counted.h"
9#include "base/values.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/extensions/api/permissions/permissions_api_helpers.h"
12#include "chrome/browser/extensions/extension_util.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/common/extensions/api/permissions.h"
15#include "content/public/browser/notification_observer.h"
16#include "content/public/browser/notification_registrar.h"
17#include "content/public/browser/notification_service.h"
18#include "content/public/browser/render_process_host.h"
19#include "extensions/browser/event_router.h"
20#include "extensions/browser/extension_prefs.h"
21#include "extensions/common/extension.h"
22#include "extensions/common/extension_messages.h"
23#include "extensions/common/manifest_handlers/permissions_parser.h"
24#include "extensions/common/permissions/permission_set.h"
25#include "extensions/common/permissions/permissions_data.h"
26#include "extensions/common/url_pattern.h"
27#include "extensions/common/url_pattern_set.h"
28
29using content::RenderProcessHost;
30using extensions::permissions_api_helpers::PackPermissionSet;
31
32namespace extensions {
33
34namespace permissions = api::permissions;
35
36namespace {
37
38// Returns a set of single origin permissions from |permissions| that match
39// |bounds|. This is necessary for two reasons:
40//   a) single origin active permissions can get filtered out in
41//      GetBoundedActivePermissions because they are not recognized as a subset
42//      of all-host permissions
43//   b) active permissions that do not match any manifest permissions can
44//      exist if a manifest permission is dropped
45URLPatternSet FilterSingleOriginPermissions(const URLPatternSet& permissions,
46                                            const URLPatternSet& bounds) {
47  URLPatternSet single_origin_permissions;
48  for (URLPatternSet::const_iterator iter = permissions.begin();
49       iter != permissions.end();
50       ++iter) {
51    if (iter->MatchesSingleOrigin() &&
52        bounds.MatchesURL(GURL(iter->GetAsString()))) {
53      single_origin_permissions.AddPattern(*iter);
54    }
55  }
56  return single_origin_permissions;
57}
58
59// Returns a PermissionSet that has the active permissions of the extension,
60// bounded to its current manifest.
61scoped_refptr<const PermissionSet> GetBoundedActivePermissions(
62    const Extension* extension,
63    const scoped_refptr<const PermissionSet>& active_permissions) {
64  // If the extension has used the optional permissions API, it will have a
65  // custom set of active permissions defined in the extension prefs. Here,
66  // we update the extension's active permissions based on the prefs.
67  if (!active_permissions.get())
68    return extension->permissions_data()->active_permissions();
69
70  scoped_refptr<const PermissionSet> required_permissions =
71      PermissionsParser::GetRequiredPermissions(extension);
72
73  // We restrict the active permissions to be within the bounds defined in the
74  // extension's manifest.
75  //  a) active permissions must be a subset of optional + default permissions
76  //  b) active permissions must contains all default permissions
77  scoped_refptr<PermissionSet> total_permissions = PermissionSet::CreateUnion(
78      required_permissions.get(),
79      PermissionsParser::GetOptionalPermissions(extension).get());
80
81  // Make sure the active permissions contain no more than optional + default.
82  scoped_refptr<PermissionSet> adjusted_active =
83      PermissionSet::CreateIntersection(total_permissions.get(),
84                                        active_permissions.get());
85
86  // Make sure the active permissions contain the default permissions.
87  adjusted_active = PermissionSet::CreateUnion(required_permissions.get(),
88                                               adjusted_active.get());
89
90  return adjusted_active;
91}
92
93// Divvy up the |url patterns| between those we grant and those we do not. If
94// |withhold_permissions| is false (because the requisite feature is not
95// enabled), no permissions are withheld.
96void SegregateUrlPermissions(const URLPatternSet& url_patterns,
97                             bool withhold_permissions,
98                             URLPatternSet* granted,
99                             URLPatternSet* withheld) {
100  for (URLPatternSet::const_iterator iter = url_patterns.begin();
101       iter != url_patterns.end();
102       ++iter) {
103    if (withhold_permissions && iter->ImpliesAllHosts())
104      withheld->AddPattern(*iter);
105    else
106      granted->AddPattern(*iter);
107  }
108}
109
110}  // namespace
111
112PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context)
113    : browser_context_(browser_context), init_flag_(INIT_FLAG_NONE) {
114}
115
116PermissionsUpdater::PermissionsUpdater(content::BrowserContext* browser_context,
117                                       InitFlag init_flag)
118    : browser_context_(browser_context), init_flag_(init_flag) {
119}
120
121PermissionsUpdater::~PermissionsUpdater() {}
122
123void PermissionsUpdater::AddPermissions(
124    const Extension* extension, const PermissionSet* permissions) {
125  scoped_refptr<const PermissionSet> existing(
126      extension->permissions_data()->active_permissions());
127  scoped_refptr<PermissionSet> total(
128      PermissionSet::CreateUnion(existing.get(), permissions));
129  scoped_refptr<PermissionSet> added(
130      PermissionSet::CreateDifference(total.get(), existing.get()));
131
132  SetPermissions(extension, total, NULL);
133
134  // Update the granted permissions so we don't auto-disable the extension.
135  GrantActivePermissions(extension);
136
137  NotifyPermissionsUpdated(ADDED, extension, added.get());
138}
139
140void PermissionsUpdater::RemovePermissions(
141    const Extension* extension, const PermissionSet* permissions) {
142  scoped_refptr<const PermissionSet> existing(
143      extension->permissions_data()->active_permissions());
144  scoped_refptr<PermissionSet> total(
145      PermissionSet::CreateDifference(existing.get(), permissions));
146  scoped_refptr<PermissionSet> removed(
147      PermissionSet::CreateDifference(existing.get(), total.get()));
148
149  // We update the active permissions, and not the granted permissions, because
150  // the extension, not the user, removed the permissions. This allows the
151  // extension to add them again without prompting the user.
152  SetPermissions(extension, total, NULL);
153
154  NotifyPermissionsUpdated(REMOVED, extension, removed.get());
155}
156
157void PermissionsUpdater::GrantActivePermissions(const Extension* extension) {
158  CHECK(extension);
159
160  // We only maintain the granted permissions prefs for INTERNAL and LOAD
161  // extensions.
162  if (!Manifest::IsUnpackedLocation(extension->location()) &&
163      extension->location() != Manifest::INTERNAL)
164    return;
165
166  ExtensionPrefs::Get(browser_context_)->AddGrantedPermissions(
167      extension->id(),
168      extension->permissions_data()->active_permissions().get());
169}
170
171void PermissionsUpdater::InitializePermissions(const Extension* extension) {
172  scoped_refptr<const PermissionSet> active_permissions(NULL);
173  scoped_refptr<const PermissionSet> bounded_active(NULL);
174  // If |extension| is a transient dummy extension, we do not want to look for
175  // it in preferences.
176  if (init_flag_ & INIT_FLAG_TRANSIENT) {
177    bounded_active = active_permissions =
178        extension->permissions_data()->active_permissions();
179  } else {
180    active_permissions = ExtensionPrefs::Get(browser_context_)
181                             ->GetActivePermissions(extension->id());
182    bounded_active = GetBoundedActivePermissions(extension, active_permissions);
183  }
184
185  // Withhold permissions if the switch applies to this extension.
186  // Non-transient extensions also must not have the preference to allow
187  // scripting on all urls.
188  bool should_withhold_permissions =
189      util::ScriptsMayRequireActionForExtension(extension);
190  if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
191    should_withhold_permissions &=
192        !util::AllowedScriptingOnAllUrls(extension->id(), browser_context_);
193  }
194
195  URLPatternSet granted_explicit_hosts;
196  URLPatternSet withheld_explicit_hosts;
197  SegregateUrlPermissions(bounded_active->explicit_hosts(),
198                          should_withhold_permissions,
199                          &granted_explicit_hosts,
200                          &withheld_explicit_hosts);
201
202  URLPatternSet granted_scriptable_hosts;
203  URLPatternSet withheld_scriptable_hosts;
204  SegregateUrlPermissions(bounded_active->scriptable_hosts(),
205                          should_withhold_permissions,
206                          &granted_scriptable_hosts,
207                          &withheld_scriptable_hosts);
208
209  // After withholding permissions, add back any origins to the active set that
210  // may have been lost during the set operations that would have dropped them.
211  // For example, the union of <all_urls> and "example.com" is <all_urls>, so
212  // we may lose "example.com". However, "example.com" is important once
213  // <all_urls> is stripped during withholding.
214  if (active_permissions.get()) {
215    granted_explicit_hosts.AddPatterns(
216        FilterSingleOriginPermissions(active_permissions->explicit_hosts(),
217                                      bounded_active->explicit_hosts()));
218    granted_scriptable_hosts.AddPatterns(
219        FilterSingleOriginPermissions(active_permissions->scriptable_hosts(),
220                                      bounded_active->scriptable_hosts()));
221  }
222
223  bounded_active = new PermissionSet(bounded_active->apis(),
224                                     bounded_active->manifest_permissions(),
225                                     granted_explicit_hosts,
226                                     granted_scriptable_hosts);
227
228  scoped_refptr<const PermissionSet> withheld =
229      new PermissionSet(APIPermissionSet(),
230                        ManifestPermissionSet(),
231                        withheld_explicit_hosts,
232                        withheld_scriptable_hosts);
233  SetPermissions(extension, bounded_active, withheld);
234}
235
236void PermissionsUpdater::WithholdImpliedAllHosts(const Extension* extension) {
237  scoped_refptr<const PermissionSet> active =
238      extension->permissions_data()->active_permissions();
239  scoped_refptr<const PermissionSet> withheld =
240      extension->permissions_data()->withheld_permissions();
241
242  URLPatternSet withheld_scriptable = withheld->scriptable_hosts();
243  URLPatternSet active_scriptable;
244  SegregateUrlPermissions(active->scriptable_hosts(),
245                          true,  // withhold permissions
246                          &active_scriptable,
247                          &withheld_scriptable);
248
249  URLPatternSet withheld_explicit = withheld->explicit_hosts();
250  URLPatternSet active_explicit;
251  SegregateUrlPermissions(active->explicit_hosts(),
252                          true,  // withhold permissions
253                          &active_explicit,
254                          &withheld_explicit);
255
256  SetPermissions(extension,
257                 new PermissionSet(active->apis(),
258                                   active->manifest_permissions(),
259                                   active_explicit,
260                                   active_scriptable),
261                  new PermissionSet(withheld->apis(),
262                                    withheld->manifest_permissions(),
263                                    withheld_explicit,
264                                    withheld_scriptable));
265  // TODO(rdevlin.cronin) We should notify the observers/renderer.
266}
267
268void PermissionsUpdater::GrantWithheldImpliedAllHosts(
269    const Extension* extension) {
270  scoped_refptr<const PermissionSet> active =
271      extension->permissions_data()->active_permissions();
272  scoped_refptr<const PermissionSet> withheld =
273      extension->permissions_data()->withheld_permissions();
274
275  // Move the all-hosts permission from withheld to active.
276  // We can cheat a bit here since we know that the only host permission we
277  // withhold is allhosts (or something similar enough to it), so we can just
278  // grant all withheld host permissions.
279  URLPatternSet explicit_hosts;
280  URLPatternSet::CreateUnion(
281      active->explicit_hosts(), withheld->explicit_hosts(), &explicit_hosts);
282  URLPatternSet scriptable_hosts;
283  URLPatternSet::CreateUnion(active->scriptable_hosts(),
284                             withheld->scriptable_hosts(),
285                             &scriptable_hosts);
286
287  // Since we only withhold host permissions (so far), we know that withheld
288  // permissions will be empty.
289  SetPermissions(extension,
290                 new PermissionSet(active->apis(),
291                                   active->manifest_permissions(),
292                                   explicit_hosts,
293                                   scriptable_hosts),
294                 new PermissionSet());
295  // TODO(rdevlin.cronin) We should notify the observers/renderer.
296}
297
298void PermissionsUpdater::SetPermissions(
299    const Extension* extension,
300    const scoped_refptr<const PermissionSet>& active,
301    scoped_refptr<const PermissionSet> withheld) {
302  withheld = withheld.get() ? withheld
303                 : extension->permissions_data()->withheld_permissions();
304  extension->permissions_data()->SetPermissions(active, withheld);
305  if ((init_flag_ & INIT_FLAG_TRANSIENT) == 0) {
306    ExtensionPrefs::Get(browser_context_)
307        ->SetActivePermissions(extension->id(), active.get());
308  }
309}
310
311void PermissionsUpdater::DispatchEvent(
312    const std::string& extension_id,
313    const char* event_name,
314    const PermissionSet* changed_permissions) {
315  EventRouter* event_router = EventRouter::Get(browser_context_);
316  if (!event_router)
317    return;
318
319  scoped_ptr<base::ListValue> value(new base::ListValue());
320  scoped_ptr<api::permissions::Permissions> permissions =
321      PackPermissionSet(changed_permissions);
322  value->Append(permissions->ToValue().release());
323  scoped_ptr<Event> event(new Event(event_name, value.Pass()));
324  event->restrict_to_browser_context = browser_context_;
325  event_router->DispatchEventToExtension(extension_id, event.Pass());
326}
327
328void PermissionsUpdater::NotifyPermissionsUpdated(
329    EventType event_type,
330    const Extension* extension,
331    const PermissionSet* changed) {
332  DCHECK((init_flag_ & INIT_FLAG_TRANSIENT) == 0);
333  if (!changed || changed->IsEmpty())
334    return;
335
336  UpdatedExtensionPermissionsInfo::Reason reason;
337  const char* event_name = NULL;
338
339  if (event_type == REMOVED) {
340    reason = UpdatedExtensionPermissionsInfo::REMOVED;
341    event_name = permissions::OnRemoved::kEventName;
342  } else {
343    CHECK_EQ(ADDED, event_type);
344    reason = UpdatedExtensionPermissionsInfo::ADDED;
345    event_name = permissions::OnAdded::kEventName;
346  }
347
348  // Notify other APIs or interested parties.
349  UpdatedExtensionPermissionsInfo info = UpdatedExtensionPermissionsInfo(
350      extension, changed, reason);
351  Profile* profile = Profile::FromBrowserContext(browser_context_);
352  content::NotificationService::current()->Notify(
353      extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED,
354      content::Source<Profile>(profile),
355      content::Details<UpdatedExtensionPermissionsInfo>(&info));
356
357  ExtensionMsg_UpdatePermissions_Params params;
358  params.extension_id = extension->id();
359  params.active_permissions = ExtensionMsg_PermissionSetStruct(
360      *extension->permissions_data()->active_permissions());
361  params.withheld_permissions = ExtensionMsg_PermissionSetStruct(
362      *extension->permissions_data()->withheld_permissions());
363
364  // Send the new permissions to the renderers.
365  for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
366       !i.IsAtEnd(); i.Advance()) {
367    RenderProcessHost* host = i.GetCurrentValue();
368    if (profile->IsSameProfile(
369            Profile::FromBrowserContext(host->GetBrowserContext()))) {
370      host->Send(new ExtensionMsg_UpdatePermissions(params));
371    }
372  }
373
374  // Trigger the onAdded and onRemoved events in the extension.
375  DispatchEvent(extension->id(), event_name, changed);
376}
377
378}  // namespace extensions
379