Home | History | Annotate | Download | only in permissions
      1 // Copyright 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/permission_set.h"
      6 
      7 #include <algorithm>
      8 #include <iterator>
      9 #include <string>
     10 
     11 #include "base/strings/stringprintf.h"
     12 #include "extensions/common/permissions/permissions_info.h"
     13 #include "extensions/common/url_pattern.h"
     14 #include "extensions/common/url_pattern_set.h"
     15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     16 #include "url/gurl.h"
     17 
     18 namespace extensions {
     19 
     20 namespace {
     21 
     22 void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) {
     23   DCHECK(out);
     24   for (URLPatternSet::const_iterator i = set.begin(); i != set.end(); ++i) {
     25     URLPattern p = *i;
     26     p.SetPath("/*");
     27     out->AddPattern(p);
     28   }
     29 }
     30 
     31 }  // namespace
     32 
     33 //
     34 // PermissionSet
     35 //
     36 
     37 PermissionSet::PermissionSet() : should_warn_all_hosts_(UNINITIALIZED) {}
     38 
     39 PermissionSet::PermissionSet(
     40     const APIPermissionSet& apis,
     41     const ManifestPermissionSet& manifest_permissions,
     42     const URLPatternSet& explicit_hosts,
     43     const URLPatternSet& scriptable_hosts)
     44     : apis_(apis),
     45       manifest_permissions_(manifest_permissions),
     46       scriptable_hosts_(scriptable_hosts),
     47       should_warn_all_hosts_(UNINITIALIZED) {
     48   AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
     49   InitImplicitPermissions();
     50   InitEffectiveHosts();
     51 }
     52 
     53 // static
     54 PermissionSet* PermissionSet::CreateDifference(
     55     const PermissionSet* set1,
     56     const PermissionSet* set2) {
     57   scoped_refptr<PermissionSet> empty = new PermissionSet();
     58   const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
     59   const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
     60 
     61   APIPermissionSet apis;
     62   APIPermissionSet::Difference(set1_safe->apis(), set2_safe->apis(), &apis);
     63 
     64   ManifestPermissionSet manifest_permissions;
     65   ManifestPermissionSet::Difference(set1_safe->manifest_permissions(),
     66                                     set2_safe->manifest_permissions(),
     67                                     &manifest_permissions);
     68 
     69   URLPatternSet explicit_hosts;
     70   URLPatternSet::CreateDifference(set1_safe->explicit_hosts(),
     71                                   set2_safe->explicit_hosts(),
     72                                   &explicit_hosts);
     73 
     74   URLPatternSet scriptable_hosts;
     75   URLPatternSet::CreateDifference(set1_safe->scriptable_hosts(),
     76                                   set2_safe->scriptable_hosts(),
     77                                   &scriptable_hosts);
     78 
     79   return new PermissionSet(apis, manifest_permissions,
     80                            explicit_hosts, scriptable_hosts);
     81 }
     82 
     83 // static
     84 PermissionSet* PermissionSet::CreateIntersection(
     85     const PermissionSet* set1,
     86     const PermissionSet* set2) {
     87   scoped_refptr<PermissionSet> empty = new PermissionSet();
     88   const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
     89   const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
     90 
     91   APIPermissionSet apis;
     92   APIPermissionSet::Intersection(set1_safe->apis(), set2_safe->apis(), &apis);
     93 
     94   ManifestPermissionSet manifest_permissions;
     95   ManifestPermissionSet::Intersection(set1_safe->manifest_permissions(),
     96                                       set2_safe->manifest_permissions(),
     97                                       &manifest_permissions);
     98 
     99   URLPatternSet explicit_hosts;
    100   URLPatternSet::CreateIntersection(set1_safe->explicit_hosts(),
    101                                     set2_safe->explicit_hosts(),
    102                                     &explicit_hosts);
    103 
    104   URLPatternSet scriptable_hosts;
    105   URLPatternSet::CreateIntersection(set1_safe->scriptable_hosts(),
    106                                     set2_safe->scriptable_hosts(),
    107                                     &scriptable_hosts);
    108 
    109   return new PermissionSet(apis, manifest_permissions,
    110                            explicit_hosts, scriptable_hosts);
    111 }
    112 
    113 // static
    114 PermissionSet* PermissionSet::CreateUnion(
    115     const PermissionSet* set1,
    116     const PermissionSet* set2) {
    117   scoped_refptr<PermissionSet> empty = new PermissionSet();
    118   const PermissionSet* set1_safe = (set1 == NULL) ? empty.get() : set1;
    119   const PermissionSet* set2_safe = (set2 == NULL) ? empty.get() : set2;
    120 
    121   APIPermissionSet apis;
    122   APIPermissionSet::Union(set1_safe->apis(), set2_safe->apis(), &apis);
    123 
    124   ManifestPermissionSet manifest_permissions;
    125   ManifestPermissionSet::Union(set1_safe->manifest_permissions(),
    126                                set2_safe->manifest_permissions(),
    127                                &manifest_permissions);
    128 
    129   URLPatternSet explicit_hosts;
    130   URLPatternSet::CreateUnion(set1_safe->explicit_hosts(),
    131                              set2_safe->explicit_hosts(),
    132                              &explicit_hosts);
    133 
    134   URLPatternSet scriptable_hosts;
    135   URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(),
    136                              set2_safe->scriptable_hosts(),
    137                              &scriptable_hosts);
    138 
    139   return new PermissionSet(apis, manifest_permissions,
    140                            explicit_hosts, scriptable_hosts);
    141 }
    142 
    143 bool PermissionSet::operator==(
    144     const PermissionSet& rhs) const {
    145   return apis_ == rhs.apis_ &&
    146       manifest_permissions_ == rhs.manifest_permissions_ &&
    147       scriptable_hosts_ == rhs.scriptable_hosts_ &&
    148       explicit_hosts_ == rhs.explicit_hosts_;
    149 }
    150 
    151 bool PermissionSet::Contains(const PermissionSet& set) const {
    152   return apis_.Contains(set.apis()) &&
    153          manifest_permissions_.Contains(set.manifest_permissions()) &&
    154          explicit_hosts().Contains(set.explicit_hosts()) &&
    155          scriptable_hosts().Contains(set.scriptable_hosts());
    156 }
    157 
    158 std::set<std::string> PermissionSet::GetAPIsAsStrings() const {
    159   std::set<std::string> apis_str;
    160   for (APIPermissionSet::const_iterator i = apis_.begin();
    161        i != apis_.end(); ++i) {
    162     apis_str.insert(i->name());
    163   }
    164   return apis_str;
    165 }
    166 
    167 bool PermissionSet::IsEmpty() const {
    168   // Not default if any host permissions are present.
    169   if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty()))
    170     return false;
    171 
    172   // Or if it has no api permissions.
    173   return apis().empty() && manifest_permissions().empty();
    174 }
    175 
    176 bool PermissionSet::HasAPIPermission(
    177     APIPermission::ID id) const {
    178   return apis().find(id) != apis().end();
    179 }
    180 
    181 bool PermissionSet::HasAPIPermission(const std::string& permission_name) const {
    182   const APIPermissionInfo* permission =
    183       PermissionsInfo::GetInstance()->GetByName(permission_name);
    184   // Ensure our PermissionsProvider is aware of this permission.
    185   CHECK(permission) << permission_name;
    186   return (permission && apis_.count(permission->id()));
    187 }
    188 
    189 bool PermissionSet::CheckAPIPermission(APIPermission::ID permission) const {
    190   return CheckAPIPermissionWithParam(permission, NULL);
    191 }
    192 
    193 bool PermissionSet::CheckAPIPermissionWithParam(
    194     APIPermission::ID permission,
    195     const APIPermission::CheckParam* param) const {
    196   APIPermissionSet::const_iterator iter = apis().find(permission);
    197   if (iter == apis().end())
    198     return false;
    199   return iter->Check(param);
    200 }
    201 
    202 bool PermissionSet::HasExplicitAccessToOrigin(
    203     const GURL& origin) const {
    204   return explicit_hosts().MatchesURL(origin);
    205 }
    206 
    207 bool PermissionSet::HasScriptableAccessToURL(
    208     const GURL& origin) const {
    209   // We only need to check our host list to verify access. The host list should
    210   // already reflect any special rules (such as chrome://favicon, all hosts
    211   // access, etc.).
    212   return scriptable_hosts().MatchesURL(origin);
    213 }
    214 
    215 bool PermissionSet::HasEffectiveAccessToAllHosts() const {
    216   // There are two ways this set can have effective access to all hosts:
    217   //  1) it has an <all_urls> URL pattern.
    218   //  2) it has a named permission with implied full URL access.
    219   for (URLPatternSet::const_iterator host = effective_hosts().begin();
    220        host != effective_hosts().end(); ++host) {
    221     if (host->match_all_urls() ||
    222         (host->match_subdomains() && host->host().empty()))
    223       return true;
    224   }
    225 
    226   for (APIPermissionSet::const_iterator i = apis().begin();
    227        i != apis().end(); ++i) {
    228     if (i->info()->implies_full_url_access())
    229       return true;
    230   }
    231   return false;
    232 }
    233 
    234 bool PermissionSet::ShouldWarnAllHosts() const {
    235   if (should_warn_all_hosts_ == UNINITIALIZED)
    236     InitShouldWarnAllHosts();
    237   return should_warn_all_hosts_ == WARN_ALL_HOSTS;
    238 }
    239 
    240 bool PermissionSet::HasEffectiveAccessToURL(const GURL& url) const {
    241   return effective_hosts().MatchesURL(url);
    242 }
    243 
    244 bool PermissionSet::HasEffectiveFullAccess() const {
    245   for (APIPermissionSet::const_iterator i = apis().begin();
    246        i != apis().end(); ++i) {
    247     if (i->info()->implies_full_access())
    248       return true;
    249   }
    250   return false;
    251 }
    252 
    253 PermissionSet::~PermissionSet() {}
    254 
    255 void PermissionSet::InitImplicitPermissions() {
    256   // The downloads permission implies the internal version as well.
    257   if (apis_.find(APIPermission::kDownloads) != apis_.end())
    258     apis_.insert(APIPermission::kDownloadsInternal);
    259 
    260   // The fileBrowserHandler permission implies the internal version as well.
    261   if (apis_.find(APIPermission::kFileBrowserHandler) != apis_.end())
    262     apis_.insert(APIPermission::kFileBrowserHandlerInternal);
    263 }
    264 
    265 void PermissionSet::InitEffectiveHosts() {
    266   effective_hosts_.ClearPatterns();
    267 
    268   URLPatternSet::CreateUnion(
    269       explicit_hosts(), scriptable_hosts(), &effective_hosts_);
    270 }
    271 
    272 void PermissionSet::InitShouldWarnAllHosts() const {
    273   if (HasEffectiveAccessToAllHosts()) {
    274     should_warn_all_hosts_ = WARN_ALL_HOSTS;
    275     return;
    276   }
    277 
    278   for (URLPatternSet::const_iterator iter = effective_hosts_.begin();
    279        iter != effective_hosts_.end();
    280        ++iter) {
    281     // If this doesn't even match subdomains, it can't possibly imply all hosts.
    282     if (!iter->match_subdomains())
    283       continue;
    284 
    285     // If iter->host() is a recognized TLD, this will be 0. We don't include
    286     // private TLDs, so that, e.g., *.appspot.com does not imply all hosts.
    287     size_t registry_length =
    288         net::registry_controlled_domains::GetRegistryLength(
    289             iter->host(),
    290             net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
    291             net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    292     // If there was more than just a TLD in the host (e.g., *.foobar.com), it
    293     // doesn't imply all hosts.
    294     if (registry_length > 0)
    295       continue;
    296 
    297     // At this point the host could either be just a TLD ("com") or some unknown
    298     // TLD-like string ("notatld"). To disambiguate between them construct a
    299     // fake URL, and check the registry. This returns 0 if the TLD is
    300     // unrecognized, or the length of the recognized TLD.
    301     registry_length = net::registry_controlled_domains::GetRegistryLength(
    302         base::StringPrintf("foo.%s", iter->host().c_str()),
    303         net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
    304         net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
    305     // If we recognized this TLD, then this is a pattern like *.com, and it
    306     // should imply all hosts.
    307     if (registry_length > 0) {
    308       should_warn_all_hosts_ = WARN_ALL_HOSTS;
    309       return;
    310     }
    311   }
    312 
    313   should_warn_all_hosts_ = DONT_WARN_ALL_HOSTS;
    314 }
    315 
    316 }  // namespace extensions
    317