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 ¶ms);
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