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