• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4 
5 #include "libcef/browser/origin_whitelist_impl.h"
6 
7 #include <string>
8 #include <vector>
9 
10 #include "include/cef_origin_whitelist.h"
11 #include "libcef/browser/context.h"
12 #include "libcef/browser/thread_util.h"
13 #include "libcef/common/cef_messages.h"
14 
15 #include "base/bind.h"
16 #include "base/lazy_instance.h"
17 #include "base/synchronization/lock.h"
18 #include "chrome/common/webui_url_constants.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/common/url_constants.h"
21 #include "extensions/common/constants.h"
22 #include "url/gurl.h"
23 #include "url/origin.h"
24 
25 namespace {
26 
27 // Class that manages cross-origin whitelist registrations.
28 class CefOriginWhitelistManager {
29  public:
CefOriginWhitelistManager()30   CefOriginWhitelistManager() {}
31 
32   // Retrieve the singleton instance.
33   static CefOriginWhitelistManager* GetInstance();
34 
AddOriginEntry(const std::string & source_origin,const std::string & target_protocol,const std::string & target_domain,bool allow_target_subdomains)35   bool AddOriginEntry(const std::string& source_origin,
36                       const std::string& target_protocol,
37                       const std::string& target_domain,
38                       bool allow_target_subdomains) {
39     Cef_CrossOriginWhiteListEntry_Params info;
40     info.source_origin = source_origin;
41     info.target_protocol = target_protocol;
42     info.target_domain = target_domain;
43     info.allow_target_subdomains = allow_target_subdomains;
44 
45     {
46       base::AutoLock lock_scope(lock_);
47 
48       // Verify that the origin entry doesn't already exist.
49       OriginList::const_iterator it = origin_list_.begin();
50       for (; it != origin_list_.end(); ++it) {
51         if (IsEqual(*it, info))
52           return false;
53       }
54 
55       origin_list_.push_back(info);
56     }
57 
58     SendModifyCrossOriginWhitelistEntry(true, info);
59     return true;
60   }
61 
RemoveOriginEntry(const std::string & source_origin,const std::string & target_protocol,const std::string & target_domain,bool allow_target_subdomains)62   bool RemoveOriginEntry(const std::string& source_origin,
63                          const std::string& target_protocol,
64                          const std::string& target_domain,
65                          bool allow_target_subdomains) {
66     Cef_CrossOriginWhiteListEntry_Params info;
67     info.source_origin = source_origin;
68     info.target_protocol = target_protocol;
69     info.target_domain = target_domain;
70     info.allow_target_subdomains = allow_target_subdomains;
71 
72     bool found = false;
73 
74     {
75       base::AutoLock lock_scope(lock_);
76 
77       OriginList::iterator it = origin_list_.begin();
78       for (; it != origin_list_.end(); ++it) {
79         if (IsEqual(*it, info)) {
80           origin_list_.erase(it);
81           found = true;
82           break;
83         }
84       }
85     }
86 
87     if (!found)
88       return false;
89 
90     SendModifyCrossOriginWhitelistEntry(false, info);
91     return true;
92   }
93 
ClearOrigins()94   void ClearOrigins() {
95     {
96       base::AutoLock lock_scope(lock_);
97       origin_list_.clear();
98     }
99 
100     SendClearCrossOriginWhitelist();
101   }
102 
GetCrossOriginWhitelistEntries(std::vector<Cef_CrossOriginWhiteListEntry_Params> * entries)103   void GetCrossOriginWhitelistEntries(
104       std::vector<Cef_CrossOriginWhiteListEntry_Params>* entries) {
105     base::AutoLock lock_scope(lock_);
106 
107     if (origin_list_.empty())
108       return;
109     entries->insert(entries->end(), origin_list_.begin(), origin_list_.end());
110   }
111 
112  private:
113   // Send the modify cross-origin whitelist entry message to all currently
114   // existing hosts.
SendModifyCrossOriginWhitelistEntry(bool add,Cef_CrossOriginWhiteListEntry_Params & params)115   static void SendModifyCrossOriginWhitelistEntry(
116       bool add,
117       Cef_CrossOriginWhiteListEntry_Params& params) {
118     CEF_REQUIRE_UIT();
119 
120     content::RenderProcessHost::iterator i(
121         content::RenderProcessHost::AllHostsIterator());
122     for (; !i.IsAtEnd(); i.Advance()) {
123       i.GetCurrentValue()->Send(
124           new CefProcessMsg_ModifyCrossOriginWhitelistEntry(add, params));
125     }
126   }
127 
128   // Send the clear cross-origin whitelists message to all currently existing
129   // hosts.
SendClearCrossOriginWhitelist()130   static void SendClearCrossOriginWhitelist() {
131     CEF_REQUIRE_UIT();
132 
133     content::RenderProcessHost::iterator i(
134         content::RenderProcessHost::AllHostsIterator());
135     for (; !i.IsAtEnd(); i.Advance()) {
136       i.GetCurrentValue()->Send(new CefProcessMsg_ClearCrossOriginWhitelist);
137     }
138   }
139 
IsEqual(const Cef_CrossOriginWhiteListEntry_Params & param1,const Cef_CrossOriginWhiteListEntry_Params & param2)140   static bool IsEqual(const Cef_CrossOriginWhiteListEntry_Params& param1,
141                       const Cef_CrossOriginWhiteListEntry_Params& param2) {
142     return (param1.source_origin == param2.source_origin &&
143             param1.target_protocol == param2.target_protocol &&
144             param1.target_domain == param2.target_domain &&
145             param1.allow_target_subdomains == param2.allow_target_subdomains);
146   }
147 
148   base::Lock lock_;
149 
150   // List of registered origins. Access must be protected by |lock_|.
151   typedef std::vector<Cef_CrossOriginWhiteListEntry_Params> OriginList;
152   OriginList origin_list_;
153 
154   DISALLOW_COPY_AND_ASSIGN(CefOriginWhitelistManager);
155 };
156 
157 base::LazyInstance<CefOriginWhitelistManager>::Leaky g_manager =
158     LAZY_INSTANCE_INITIALIZER;
159 
GetInstance()160 CefOriginWhitelistManager* CefOriginWhitelistManager::GetInstance() {
161   return g_manager.Pointer();
162 }
163 
IsMatch(const url::Origin & source_origin,const url::Origin & target_origin,const Cef_CrossOriginWhiteListEntry_Params & param)164 bool IsMatch(const url::Origin& source_origin,
165              const url::Origin& target_origin,
166              const Cef_CrossOriginWhiteListEntry_Params& param) {
167   if (!source_origin.IsSameOriginWith(
168           url::Origin::Create(GURL(param.source_origin)))) {
169     // Source origin does not match.
170     return false;
171   }
172 
173   if (target_origin.scheme() != param.target_protocol) {
174     // Target scheme does not match.
175     return false;
176   }
177 
178   if (param.allow_target_subdomains) {
179     if (param.target_domain.empty()) {
180       // Any domain will match.
181       return true;
182     } else {
183       // Match sub-domains.
184       return target_origin.DomainIs(param.target_domain.c_str());
185     }
186   } else {
187     // Match full domain.
188     return (target_origin.host() == param.target_domain);
189   }
190 }
191 
192 }  // namespace
193 
CefAddCrossOriginWhitelistEntry(const CefString & source_origin,const CefString & target_protocol,const CefString & target_domain,bool allow_target_subdomains)194 bool CefAddCrossOriginWhitelistEntry(const CefString& source_origin,
195                                      const CefString& target_protocol,
196                                      const CefString& target_domain,
197                                      bool allow_target_subdomains) {
198   // Verify that the context is in a valid state.
199   if (!CONTEXT_STATE_VALID()) {
200     NOTREACHED();
201     return false;
202   }
203 
204   std::string source_url = source_origin;
205   GURL gurl = GURL(source_url);
206   if (gurl.is_empty() || !gurl.is_valid()) {
207     NOTREACHED() << "Invalid source_origin URL: " << source_url;
208     return false;
209   }
210 
211   if (CEF_CURRENTLY_ON_UIT()) {
212     return CefOriginWhitelistManager::GetInstance()->AddOriginEntry(
213         source_origin, target_protocol, target_domain, allow_target_subdomains);
214   } else {
215     CEF_POST_TASK(
216         CEF_UIT,
217         base::Bind(base::IgnoreResult(&CefAddCrossOriginWhitelistEntry),
218                    source_origin, target_protocol, target_domain,
219                    allow_target_subdomains));
220   }
221 
222   return true;
223 }
224 
CefRemoveCrossOriginWhitelistEntry(const CefString & source_origin,const CefString & target_protocol,const CefString & target_domain,bool allow_target_subdomains)225 bool CefRemoveCrossOriginWhitelistEntry(const CefString& source_origin,
226                                         const CefString& target_protocol,
227                                         const CefString& target_domain,
228                                         bool allow_target_subdomains) {
229   // Verify that the context is in a valid state.
230   if (!CONTEXT_STATE_VALID()) {
231     NOTREACHED();
232     return false;
233   }
234 
235   std::string source_url = source_origin;
236   GURL gurl = GURL(source_url);
237   if (gurl.is_empty() || !gurl.is_valid()) {
238     NOTREACHED() << "Invalid source_origin URL: " << source_url;
239     return false;
240   }
241 
242   if (CEF_CURRENTLY_ON_UIT()) {
243     return CefOriginWhitelistManager::GetInstance()->RemoveOriginEntry(
244         source_origin, target_protocol, target_domain, allow_target_subdomains);
245   } else {
246     CEF_POST_TASK(
247         CEF_UIT,
248         base::Bind(base::IgnoreResult(&CefRemoveCrossOriginWhitelistEntry),
249                    source_origin, target_protocol, target_domain,
250                    allow_target_subdomains));
251   }
252 
253   return true;
254 }
255 
CefClearCrossOriginWhitelist()256 bool CefClearCrossOriginWhitelist() {
257   // Verify that the context is in a valid state.
258   if (!CONTEXT_STATE_VALID()) {
259     NOTREACHED();
260     return false;
261   }
262 
263   if (CEF_CURRENTLY_ON_UIT()) {
264     CefOriginWhitelistManager::GetInstance()->ClearOrigins();
265   } else {
266     CEF_POST_TASK(
267         CEF_UIT, base::Bind(base::IgnoreResult(&CefClearCrossOriginWhitelist)));
268   }
269 
270   return true;
271 }
272 
GetCrossOriginWhitelistEntries(std::vector<Cef_CrossOriginWhiteListEntry_Params> * entries)273 void GetCrossOriginWhitelistEntries(
274     std::vector<Cef_CrossOriginWhiteListEntry_Params>* entries) {
275   CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries(
276       entries);
277 }
278 
HasCrossOriginWhitelistEntry(const url::Origin & source,const url::Origin & target)279 bool HasCrossOriginWhitelistEntry(const url::Origin& source,
280                                   const url::Origin& target) {
281   // Components of chrome that are implemented as extensions or platform apps
282   // are allowed to use chrome://resources/ and chrome://theme/ URLs.
283   // See also RegisterNonNetworkSubresourceURLLoaderFactories.
284   if (source.scheme() == extensions::kExtensionScheme &&
285       target.scheme() == content::kChromeUIScheme &&
286       (target.host() == chrome::kChromeUIThemeHost ||
287        target.host() == content::kChromeUIResourcesHost)) {
288     return true;
289   }
290 
291   std::vector<Cef_CrossOriginWhiteListEntry_Params> params;
292   CefOriginWhitelistManager::GetInstance()->GetCrossOriginWhitelistEntries(
293       &params);
294 
295   if (params.empty())
296     return false;
297 
298   std::vector<Cef_CrossOriginWhiteListEntry_Params>::const_iterator it =
299       params.begin();
300   for (; it != params.end(); ++it) {
301     if (IsMatch(source, target, *it))
302       return true;
303   }
304 
305   return false;
306 }
307