1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Mozilla Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2009
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Brandon Sterne <bsterne@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "prlog.h"
40 : #include "nsString.h"
41 : #include "nsCOMPtr.h"
42 : #include "nsIURI.h"
43 : #include "nsIPrincipal.h"
44 : #include "nsIObserver.h"
45 : #include "nsIDocument.h"
46 : #include "nsIContent.h"
47 : #include "nsCSPService.h"
48 : #include "nsIContentSecurityPolicy.h"
49 : #include "nsIChannelPolicy.h"
50 : #include "nsIChannelEventSink.h"
51 : #include "nsIPropertyBag2.h"
52 : #include "nsIWritablePropertyBag2.h"
53 : #include "nsNetError.h"
54 : #include "nsChannelProperties.h"
55 : #include "nsIAsyncVerifyRedirectCallback.h"
56 : #include "nsAsyncRedirectVerifyHelper.h"
57 : #include "mozilla/Preferences.h"
58 : #include "nsIScriptError.h"
59 : #include "nsContentUtils.h"
60 :
61 : using namespace mozilla;
62 :
63 : /* Keeps track of whether or not CSP is enabled */
64 : bool CSPService::sCSPEnabled = true;
65 :
66 : #ifdef PR_LOGGING
67 : static PRLogModuleInfo* gCspPRLog;
68 : #endif
69 :
70 40 : CSPService::CSPService()
71 : {
72 40 : Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
73 :
74 : #ifdef PR_LOGGING
75 40 : if (!gCspPRLog)
76 40 : gCspPRLog = PR_NewLogModule("CSP");
77 : #endif
78 40 : }
79 :
80 80 : CSPService::~CSPService()
81 : {
82 160 : }
83 :
84 440 : NS_IMPL_ISUPPORTS2(CSPService, nsIContentPolicy, nsIChannelEventSink)
85 :
86 : /* nsIContentPolicy implementation */
87 : NS_IMETHODIMP
88 10 : CSPService::ShouldLoad(PRUint32 aContentType,
89 : nsIURI *aContentLocation,
90 : nsIURI *aRequestOrigin,
91 : nsISupports *aRequestContext,
92 : const nsACString &aMimeTypeGuess,
93 : nsISupports *aExtra,
94 : PRInt16 *aDecision)
95 : {
96 10 : if (!aContentLocation)
97 0 : return NS_ERROR_FAILURE;
98 :
99 : #ifdef PR_LOGGING
100 : {
101 20 : nsCAutoString location;
102 10 : aContentLocation->GetSpec(location);
103 10 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
104 : ("CSPService::ShouldLoad called for %s", location.get()));
105 : }
106 : #endif
107 : // default decision, CSP can revise it if there's a policy to enforce
108 10 : *aDecision = nsIContentPolicy::ACCEPT;
109 :
110 : // No need to continue processing if CSP is disabled
111 10 : if (!sCSPEnabled)
112 0 : return NS_OK;
113 :
114 : // find the principal of the document that initiated this request and see
115 : // if it has a CSP policy object
116 20 : nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
117 20 : nsCOMPtr<nsIPrincipal> principal;
118 20 : nsCOMPtr<nsIContentSecurityPolicy> csp;
119 10 : if (node) {
120 8 : principal = node->NodePrincipal();
121 8 : principal->GetCsp(getter_AddRefs(csp));
122 :
123 8 : if (csp) {
124 : #ifdef PR_LOGGING
125 0 : nsAutoString policy;
126 0 : csp->GetPolicy(policy);
127 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
128 : ("Document has CSP: %s",
129 : NS_ConvertUTF16toUTF8(policy).get()));
130 : #endif
131 : // obtain the enforcement decision
132 0 : csp->ShouldLoad(aContentType,
133 : aContentLocation,
134 : aRequestOrigin,
135 : aRequestContext,
136 : aMimeTypeGuess,
137 : aExtra,
138 0 : aDecision);
139 : }
140 : }
141 : #ifdef PR_LOGGING
142 : else {
143 4 : nsCAutoString uriSpec;
144 2 : aContentLocation->GetSpec(uriSpec);
145 2 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
146 : ("COULD NOT get nsINode for location: %s", uriSpec.get()));
147 : }
148 : #endif
149 :
150 10 : return NS_OK;
151 : }
152 :
153 : NS_IMETHODIMP
154 0 : CSPService::ShouldProcess(PRUint32 aContentType,
155 : nsIURI *aContentLocation,
156 : nsIURI *aRequestOrigin,
157 : nsISupports *aRequestContext,
158 : const nsACString &aMimeTypeGuess,
159 : nsISupports *aExtra,
160 : PRInt16 *aDecision)
161 : {
162 0 : if (!aContentLocation)
163 0 : return NS_ERROR_FAILURE;
164 :
165 : // default decision is to accept the item
166 0 : *aDecision = nsIContentPolicy::ACCEPT;
167 :
168 : // No need to continue processing if CSP is disabled
169 0 : if (!sCSPEnabled)
170 0 : return NS_OK;
171 :
172 : // find the nsDocument that initiated this request and see if it has a
173 : // CSP policy object
174 0 : nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
175 0 : nsCOMPtr<nsIPrincipal> principal;
176 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
177 0 : if (node) {
178 0 : principal = node->NodePrincipal();
179 0 : principal->GetCsp(getter_AddRefs(csp));
180 :
181 0 : if (csp) {
182 : #ifdef PR_LOGGING
183 0 : nsAutoString policy;
184 0 : csp->GetPolicy(policy);
185 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
186 : ("shouldProcess - document has policy: %s",
187 : NS_ConvertUTF16toUTF8(policy).get()));
188 : #endif
189 : // obtain the enforcement decision
190 0 : csp->ShouldProcess(aContentType,
191 : aContentLocation,
192 : aRequestOrigin,
193 : aRequestContext,
194 : aMimeTypeGuess,
195 : aExtra,
196 0 : aDecision);
197 : }
198 : }
199 : #ifdef PR_LOGGING
200 : else {
201 0 : nsCAutoString uriSpec;
202 0 : aContentLocation->GetSpec(uriSpec);
203 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
204 : ("COULD NOT get nsINode for location: %s", uriSpec.get()));
205 : }
206 : #endif
207 0 : return NS_OK;
208 : }
209 :
210 : /* nsIChannelEventSink implementation */
211 : NS_IMETHODIMP
212 153 : CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
213 : nsIChannel *newChannel,
214 : PRUint32 flags,
215 : nsIAsyncVerifyRedirectCallback *callback)
216 : {
217 306 : nsAsyncRedirectAutoCallback autoCallback(callback);
218 :
219 : // get the Content Security Policy and load type from the property bag
220 306 : nsCOMPtr<nsISupports> policyContainer;
221 306 : nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
222 153 : if (!props)
223 0 : return NS_OK;
224 :
225 153 : props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
226 : NS_GET_IID(nsISupports),
227 153 : getter_AddRefs(policyContainer));
228 :
229 : // see if we have a valid nsIChannelPolicy containing CSP and load type
230 306 : nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
231 153 : if (!channelPolicy)
232 153 : return NS_OK;
233 :
234 0 : nsCOMPtr<nsIContentSecurityPolicy> csp;
235 0 : channelPolicy->GetContentSecurityPolicy(getter_AddRefs(csp));
236 : PRUint32 loadType;
237 0 : channelPolicy->GetLoadType(&loadType);
238 :
239 : // if no CSP in the channelPolicy, nothing for us to add to the channel
240 0 : if (!csp)
241 0 : return NS_OK;
242 :
243 : /* Since redirecting channels don't call into nsIContentPolicy, we call our
244 : * Content Policy implementation directly when redirects occur. When channels
245 : * are created using NS_NewChannel(), callers can optionally pass in a
246 : * nsIChannelPolicy containing a CSP object and load type, which is placed in
247 : * the new channel's property bag. This container is propagated forward when
248 : * channels redirect.
249 : */
250 :
251 : // Does the CSP permit this host for this type of load?
252 : // If not, cancel the load now.
253 0 : nsCOMPtr<nsIURI> newUri;
254 0 : newChannel->GetURI(getter_AddRefs(newUri));
255 0 : PRInt16 aDecision = nsIContentPolicy::ACCEPT;
256 0 : csp->ShouldLoad(loadType, // load type per nsIContentPolicy (PRUint32)
257 : newUri, // nsIURI
258 : nsnull, // nsIURI
259 : nsnull, // nsISupports
260 0 : EmptyCString(), // ACString - MIME guess
261 : nsnull, // nsISupports - extra
262 0 : &aDecision);
263 :
264 : #ifdef PR_LOGGING
265 0 : if (newUri) {
266 0 : nsCAutoString newUriSpec("None");
267 0 : newUri->GetSpec(newUriSpec);
268 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
269 : ("CSPService::AsyncOnChannelRedirect called for %s",
270 : newUriSpec.get()));
271 : }
272 0 : if (aDecision == 1)
273 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
274 : ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
275 : else
276 0 : PR_LOG(gCspPRLog, PR_LOG_DEBUG,
277 : ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
278 : #endif
279 :
280 : // if ShouldLoad doesn't accept the load, cancel the request
281 0 : if (aDecision != 1) {
282 0 : autoCallback.DontCallback();
283 0 : return NS_BINDING_FAILED;
284 : }
285 :
286 : // the redirect is permitted, so propagate the Content Security Policy
287 : // and load type to the redirecting channel
288 : nsresult rv;
289 0 : nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
290 0 : if (props2) {
291 0 : rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
292 0 : channelPolicy);
293 0 : if (NS_SUCCEEDED(rv)) {
294 0 : return NS_OK;
295 : }
296 : }
297 :
298 : // The redirecting channel isn't a writable property bag, we won't be able
299 : // to enforce the load policy if it redirects again, so we stop it now.
300 0 : nsCAutoString newUriSpec;
301 0 : newUri->GetSpec(newUriSpec);
302 0 : const PRUnichar *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
303 0 : if (NS_SUCCEEDED(rv)) {
304 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
305 : "Redirect Error", nsnull,
306 : nsContentUtils::eDOM_PROPERTIES,
307 : "InvalidRedirectChannelWarning",
308 0 : formatParams, 1);
309 : }
310 :
311 0 : return NS_BINDING_FAILED;
312 : }
|