|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "base/basictypes.h" |
|
7 |
|
8 #include "nsAboutProtocolHandler.h" |
|
9 #include "nsIURI.h" |
|
10 #include "nsIAboutModule.h" |
|
11 #include "nsString.h" |
|
12 #include "nsNetCID.h" |
|
13 #include "nsAboutProtocolUtils.h" |
|
14 #include "nsError.h" |
|
15 #include "nsNetUtil.h" |
|
16 #include "nsIObjectInputStream.h" |
|
17 #include "nsIObjectOutputStream.h" |
|
18 #include "nsAutoPtr.h" |
|
19 #include "nsIWritablePropertyBag2.h" |
|
20 |
|
21 static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID); |
|
22 static NS_DEFINE_CID(kNestedAboutURICID, NS_NESTEDABOUTURI_CID); |
|
23 |
|
24 static bool IsSafeForUntrustedContent(nsIAboutModule *aModule, nsIURI *aURI) { |
|
25 uint32_t flags; |
|
26 nsresult rv = aModule->GetURIFlags(aURI, &flags); |
|
27 NS_ENSURE_SUCCESS(rv, false); |
|
28 |
|
29 return (flags & nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT) != 0; |
|
30 } |
|
31 //////////////////////////////////////////////////////////////////////////////// |
|
32 |
|
33 NS_IMPL_ISUPPORTS(nsAboutProtocolHandler, nsIProtocolHandler) |
|
34 |
|
35 //////////////////////////////////////////////////////////////////////////////// |
|
36 // nsIProtocolHandler methods: |
|
37 |
|
38 NS_IMETHODIMP |
|
39 nsAboutProtocolHandler::GetScheme(nsACString &result) |
|
40 { |
|
41 result.AssignLiteral("about"); |
|
42 return NS_OK; |
|
43 } |
|
44 |
|
45 NS_IMETHODIMP |
|
46 nsAboutProtocolHandler::GetDefaultPort(int32_t *result) |
|
47 { |
|
48 *result = -1; // no port for about: URLs |
|
49 return NS_OK; |
|
50 } |
|
51 |
|
52 NS_IMETHODIMP |
|
53 nsAboutProtocolHandler::GetProtocolFlags(uint32_t *result) |
|
54 { |
|
55 *result = URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD; |
|
56 return NS_OK; |
|
57 } |
|
58 |
|
59 NS_IMETHODIMP |
|
60 nsAboutProtocolHandler::NewURI(const nsACString &aSpec, |
|
61 const char *aCharset, // ignore charset info |
|
62 nsIURI *aBaseURI, |
|
63 nsIURI **result) |
|
64 { |
|
65 *result = nullptr; |
|
66 nsresult rv; |
|
67 |
|
68 // Use a simple URI to parse out some stuff first |
|
69 nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv); |
|
70 if (NS_FAILED(rv)) return rv; |
|
71 |
|
72 rv = url->SetSpec(aSpec); |
|
73 if (NS_FAILED(rv)) { |
|
74 return rv; |
|
75 } |
|
76 |
|
77 // Unfortunately, people create random about: URIs that don't correspond to |
|
78 // about: modules... Since those URIs will never open a channel, might as |
|
79 // well consider them unsafe for better perf, and just in case. |
|
80 bool isSafe = false; |
|
81 |
|
82 nsCOMPtr<nsIAboutModule> aboutMod; |
|
83 rv = NS_GetAboutModule(url, getter_AddRefs(aboutMod)); |
|
84 if (NS_SUCCEEDED(rv)) { |
|
85 isSafe = IsSafeForUntrustedContent(aboutMod, url); |
|
86 } |
|
87 |
|
88 if (isSafe) { |
|
89 // We need to indicate that this baby is safe. Use an inner URI that |
|
90 // no one but the security manager will see. Make sure to preserve our |
|
91 // path, in case someone decides to hardcode checks for particular |
|
92 // about: URIs somewhere. |
|
93 nsAutoCString spec; |
|
94 rv = url->GetPath(spec); |
|
95 NS_ENSURE_SUCCESS(rv, rv); |
|
96 |
|
97 spec.Insert("moz-safe-about:", 0); |
|
98 |
|
99 nsCOMPtr<nsIURI> inner; |
|
100 rv = NS_NewURI(getter_AddRefs(inner), spec); |
|
101 NS_ENSURE_SUCCESS(rv, rv); |
|
102 |
|
103 nsSimpleNestedURI* outer = new nsNestedAboutURI(inner, aBaseURI); |
|
104 NS_ENSURE_TRUE(outer, NS_ERROR_OUT_OF_MEMORY); |
|
105 |
|
106 // Take a ref to it in the COMPtr we plan to return |
|
107 url = outer; |
|
108 |
|
109 rv = outer->SetSpec(aSpec); |
|
110 NS_ENSURE_SUCCESS(rv, rv); |
|
111 } |
|
112 |
|
113 // We don't want to allow mutation, since it would allow safe and |
|
114 // unsafe URIs to change into each other... |
|
115 NS_TryToSetImmutable(url); |
|
116 url.swap(*result); |
|
117 return NS_OK; |
|
118 } |
|
119 |
|
120 NS_IMETHODIMP |
|
121 nsAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) |
|
122 { |
|
123 NS_ENSURE_ARG_POINTER(uri); |
|
124 |
|
125 // about:what you ask? |
|
126 nsCOMPtr<nsIAboutModule> aboutMod; |
|
127 nsresult rv = NS_GetAboutModule(uri, getter_AddRefs(aboutMod)); |
|
128 |
|
129 nsAutoCString path; |
|
130 nsresult rv2 = NS_GetAboutModuleName(uri, path); |
|
131 if (NS_SUCCEEDED(rv2) && path.EqualsLiteral("srcdoc")) { |
|
132 // about:srcdoc is meant to be unresolvable, yet is included in the |
|
133 // about lookup tables so that it can pass security checks when used in |
|
134 // a srcdoc iframe. To ensure that it stays unresolvable, we pretend |
|
135 // that it doesn't exist. |
|
136 rv = NS_ERROR_FACTORY_NOT_REGISTERED; |
|
137 } |
|
138 |
|
139 if (NS_SUCCEEDED(rv)) { |
|
140 // The standard return case: |
|
141 rv = aboutMod->NewChannel(uri, result); |
|
142 if (NS_SUCCEEDED(rv)) { |
|
143 // If this URI is safe for untrusted content, enforce that its |
|
144 // principal be based on the channel's originalURI by setting the |
|
145 // owner to null. |
|
146 // Note: this relies on aboutMod's newChannel implementation |
|
147 // having set the proper originalURI, which probably isn't ideal. |
|
148 if (IsSafeForUntrustedContent(aboutMod, uri)) { |
|
149 (*result)->SetOwner(nullptr); |
|
150 } |
|
151 |
|
152 nsRefPtr<nsNestedAboutURI> aboutURI; |
|
153 nsresult rv2 = uri->QueryInterface(kNestedAboutURICID, |
|
154 getter_AddRefs(aboutURI)); |
|
155 if (NS_SUCCEEDED(rv2) && aboutURI->GetBaseURI()) { |
|
156 nsCOMPtr<nsIWritablePropertyBag2> writableBag = |
|
157 do_QueryInterface(*result); |
|
158 if (writableBag) { |
|
159 writableBag-> |
|
160 SetPropertyAsInterface(NS_LITERAL_STRING("baseURI"), |
|
161 aboutURI->GetBaseURI()); |
|
162 } |
|
163 } |
|
164 } |
|
165 return rv; |
|
166 } |
|
167 |
|
168 // mumble... |
|
169 |
|
170 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) { |
|
171 // This looks like an about: we don't know about. Convert |
|
172 // this to an invalid URI error. |
|
173 rv = NS_ERROR_MALFORMED_URI; |
|
174 } |
|
175 |
|
176 return rv; |
|
177 } |
|
178 |
|
179 NS_IMETHODIMP |
|
180 nsAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) |
|
181 { |
|
182 // don't override anything. |
|
183 *_retval = false; |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 //////////////////////////////////////////////////////////////////////////////// |
|
188 // Safe about protocol handler impl |
|
189 |
|
190 NS_IMPL_ISUPPORTS(nsSafeAboutProtocolHandler, nsIProtocolHandler) |
|
191 |
|
192 // nsIProtocolHandler methods: |
|
193 |
|
194 NS_IMETHODIMP |
|
195 nsSafeAboutProtocolHandler::GetScheme(nsACString &result) |
|
196 { |
|
197 result.AssignLiteral("moz-safe-about"); |
|
198 return NS_OK; |
|
199 } |
|
200 |
|
201 NS_IMETHODIMP |
|
202 nsSafeAboutProtocolHandler::GetDefaultPort(int32_t *result) |
|
203 { |
|
204 *result = -1; // no port for moz-safe-about: URLs |
|
205 return NS_OK; |
|
206 } |
|
207 |
|
208 NS_IMETHODIMP |
|
209 nsSafeAboutProtocolHandler::GetProtocolFlags(uint32_t *result) |
|
210 { |
|
211 *result = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT; |
|
212 return NS_OK; |
|
213 } |
|
214 |
|
215 NS_IMETHODIMP |
|
216 nsSafeAboutProtocolHandler::NewURI(const nsACString &aSpec, |
|
217 const char *aCharset, // ignore charset info |
|
218 nsIURI *aBaseURI, |
|
219 nsIURI **result) |
|
220 { |
|
221 nsresult rv; |
|
222 |
|
223 nsCOMPtr<nsIURI> url = do_CreateInstance(kSimpleURICID, &rv); |
|
224 if (NS_FAILED(rv)) return rv; |
|
225 |
|
226 rv = url->SetSpec(aSpec); |
|
227 if (NS_FAILED(rv)) { |
|
228 return rv; |
|
229 } |
|
230 |
|
231 NS_TryToSetImmutable(url); |
|
232 |
|
233 *result = nullptr; |
|
234 url.swap(*result); |
|
235 return rv; |
|
236 } |
|
237 |
|
238 NS_IMETHODIMP |
|
239 nsSafeAboutProtocolHandler::NewChannel(nsIURI* uri, nsIChannel* *result) |
|
240 { |
|
241 *result = nullptr; |
|
242 return NS_ERROR_NOT_AVAILABLE; |
|
243 } |
|
244 |
|
245 NS_IMETHODIMP |
|
246 nsSafeAboutProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) |
|
247 { |
|
248 // don't override anything. |
|
249 *_retval = false; |
|
250 return NS_OK; |
|
251 } |
|
252 |
|
253 //////////////////////////////////////////////////////////// |
|
254 // nsNestedAboutURI implementation |
|
255 NS_INTERFACE_MAP_BEGIN(nsNestedAboutURI) |
|
256 if (aIID.Equals(kNestedAboutURICID)) |
|
257 foundInterface = static_cast<nsIURI*>(this); |
|
258 else |
|
259 NS_INTERFACE_MAP_END_INHERITING(nsSimpleNestedURI) |
|
260 |
|
261 // nsISerializable |
|
262 NS_IMETHODIMP |
|
263 nsNestedAboutURI::Read(nsIObjectInputStream* aStream) |
|
264 { |
|
265 nsresult rv = nsSimpleNestedURI::Read(aStream); |
|
266 if (NS_FAILED(rv)) return rv; |
|
267 |
|
268 bool haveBase; |
|
269 rv = aStream->ReadBoolean(&haveBase); |
|
270 if (NS_FAILED(rv)) return rv; |
|
271 |
|
272 if (haveBase) { |
|
273 nsCOMPtr<nsISupports> supports; |
|
274 rv = aStream->ReadObject(true, getter_AddRefs(supports)); |
|
275 if (NS_FAILED(rv)) return rv; |
|
276 |
|
277 mBaseURI = do_QueryInterface(supports, &rv); |
|
278 if (NS_FAILED(rv)) return rv; |
|
279 } |
|
280 |
|
281 return NS_OK; |
|
282 } |
|
283 |
|
284 NS_IMETHODIMP |
|
285 nsNestedAboutURI::Write(nsIObjectOutputStream* aStream) |
|
286 { |
|
287 nsresult rv = nsSimpleNestedURI::Write(aStream); |
|
288 if (NS_FAILED(rv)) return rv; |
|
289 |
|
290 rv = aStream->WriteBoolean(mBaseURI != nullptr); |
|
291 if (NS_FAILED(rv)) return rv; |
|
292 |
|
293 if (mBaseURI) { |
|
294 // A previous iteration of this code wrote out mBaseURI as nsISupports |
|
295 // and then read it in as nsIURI, which is non-kosher when mBaseURI |
|
296 // implements more than just a single line of interfaces and the |
|
297 // canonical nsISupports* isn't the one a static_cast<> of mBaseURI |
|
298 // would produce. For backwards compatibility with existing |
|
299 // serializations we continue to write mBaseURI as nsISupports but |
|
300 // switch to reading it as nsISupports, with a post-read QI to get to |
|
301 // nsIURI. |
|
302 rv = aStream->WriteCompoundObject(mBaseURI, NS_GET_IID(nsISupports), |
|
303 true); |
|
304 if (NS_FAILED(rv)) return rv; |
|
305 } |
|
306 |
|
307 return NS_OK; |
|
308 } |
|
309 |
|
310 // nsSimpleURI |
|
311 /* virtual */ nsSimpleURI* |
|
312 nsNestedAboutURI::StartClone(nsSimpleURI::RefHandlingEnum aRefHandlingMode) |
|
313 { |
|
314 // Sadly, we can't make use of nsSimpleNestedURI::StartClone here. |
|
315 // However, this function is expected to exactly match that function, |
|
316 // aside from the "new ns***URI()" call. |
|
317 NS_ENSURE_TRUE(mInnerURI, nullptr); |
|
318 |
|
319 nsCOMPtr<nsIURI> innerClone; |
|
320 nsresult rv = aRefHandlingMode == eHonorRef ? |
|
321 mInnerURI->Clone(getter_AddRefs(innerClone)) : |
|
322 mInnerURI->CloneIgnoringRef(getter_AddRefs(innerClone)); |
|
323 |
|
324 if (NS_FAILED(rv)) { |
|
325 return nullptr; |
|
326 } |
|
327 |
|
328 nsNestedAboutURI* url = new nsNestedAboutURI(innerClone, mBaseURI); |
|
329 url->SetMutable(false); |
|
330 |
|
331 return url; |
|
332 } |
|
333 |
|
334 // nsIClassInfo |
|
335 NS_IMETHODIMP |
|
336 nsNestedAboutURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc) |
|
337 { |
|
338 *aClassIDNoAlloc = kNestedAboutURICID; |
|
339 return NS_OK; |
|
340 } |