netwerk/base/src/nsChannelClassifier.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:dcbca83143f8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include "nsChannelClassifier.h"
6
7 #include "nsNetUtil.h"
8 #include "nsIChannel.h"
9 #include "nsIProtocolHandler.h"
10 #include "nsICachingChannel.h"
11 #include "nsICacheEntryDescriptor.h"
12 #include "prlog.h"
13 #include "nsIScriptSecurityManager.h"
14
15 #if defined(PR_LOGGING)
16 //
17 // NSPR_LOG_MODULES=nsChannelClassifier:5
18 //
19 static PRLogModuleInfo *gChannelClassifierLog;
20 #endif
21 #undef LOG
22 #define LOG(args) PR_LOG(gChannelClassifierLog, PR_LOG_DEBUG, args)
23
24 NS_IMPL_ISUPPORTS(nsChannelClassifier,
25 nsIURIClassifierCallback)
26
27 nsChannelClassifier::nsChannelClassifier()
28 {
29 #if defined(PR_LOGGING)
30 if (!gChannelClassifierLog)
31 gChannelClassifierLog = PR_NewLogModule("nsChannelClassifier");
32 #endif
33 }
34
35 nsresult
36 nsChannelClassifier::Start(nsIChannel *aChannel)
37 {
38 // Don't bother to run the classifier on a load that has already failed.
39 // (this might happen after a redirect)
40 nsresult status;
41 aChannel->GetStatus(&status);
42 if (NS_FAILED(status))
43 return NS_OK;
44
45 // Don't bother to run the classifier on a cached load that was
46 // previously classified.
47 if (HasBeenClassified(aChannel)) {
48 return NS_OK;
49 }
50
51 nsCOMPtr<nsIURI> uri;
52 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
53 NS_ENSURE_SUCCESS(rv, rv);
54
55 // Don't bother checking certain types of URIs.
56 bool hasFlags;
57 rv = NS_URIChainHasFlags(uri,
58 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
59 &hasFlags);
60 NS_ENSURE_SUCCESS(rv, rv);
61 if (hasFlags) return NS_OK;
62
63 rv = NS_URIChainHasFlags(uri,
64 nsIProtocolHandler::URI_IS_LOCAL_FILE,
65 &hasFlags);
66 NS_ENSURE_SUCCESS(rv, rv);
67 if (hasFlags) return NS_OK;
68
69 rv = NS_URIChainHasFlags(uri,
70 nsIProtocolHandler::URI_IS_UI_RESOURCE,
71 &hasFlags);
72 NS_ENSURE_SUCCESS(rv, rv);
73 if (hasFlags) return NS_OK;
74
75 rv = NS_URIChainHasFlags(uri,
76 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
77 &hasFlags);
78 NS_ENSURE_SUCCESS(rv, rv);
79 if (hasFlags) return NS_OK;
80
81 nsCOMPtr<nsIURIClassifier> uriClassifier =
82 do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
83 if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
84 rv == NS_ERROR_NOT_AVAILABLE) {
85 // no URI classifier, ignore this failure.
86 return NS_OK;
87 }
88 NS_ENSURE_SUCCESS(rv, rv);
89
90 nsCOMPtr<nsIScriptSecurityManager> securityManager =
91 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
92 NS_ENSURE_SUCCESS(rv, rv);
93
94 nsCOMPtr<nsIPrincipal> principal;
95 rv = securityManager->GetChannelPrincipal(aChannel,
96 getter_AddRefs(principal));
97 NS_ENSURE_SUCCESS(rv, rv);
98
99 bool expectCallback;
100 rv = uriClassifier->Classify(principal, this, &expectCallback);
101 if (NS_FAILED(rv)) return rv;
102
103 if (expectCallback) {
104 // Suspend the channel, it will be resumed when we get the classifier
105 // callback.
106 rv = aChannel->Suspend();
107 if (NS_FAILED(rv)) {
108 // Some channels (including nsJSChannel) fail on Suspend. This
109 // shouldn't be fatal, but will prevent malware from being
110 // blocked on these channels.
111 return NS_OK;
112 }
113
114 mSuspendedChannel = aChannel;
115 #ifdef DEBUG
116 LOG(("nsChannelClassifier[%p]: suspended channel %p",
117 this, mSuspendedChannel.get()));
118 #endif
119 }
120
121 return NS_OK;
122 }
123
124 // Note in the cache entry that this URL was classified, so that future
125 // cached loads don't need to be checked.
126 void
127 nsChannelClassifier::MarkEntryClassified(nsresult status)
128 {
129 nsCOMPtr<nsICachingChannel> cachingChannel =
130 do_QueryInterface(mSuspendedChannel);
131 if (!cachingChannel) {
132 return;
133 }
134
135 nsCOMPtr<nsISupports> cacheToken;
136 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
137 if (!cacheToken) {
138 return;
139 }
140
141 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
142 do_QueryInterface(cacheToken);
143 if (!cacheEntry) {
144 return;
145 }
146
147 cacheEntry->SetMetaDataElement("necko:classified",
148 NS_SUCCEEDED(status) ? "1" : nullptr);
149 }
150
151 bool
152 nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel)
153 {
154 nsCOMPtr<nsICachingChannel> cachingChannel =
155 do_QueryInterface(aChannel);
156 if (!cachingChannel) {
157 return false;
158 }
159
160 // Only check the tag if we are loading from the cache without
161 // validation.
162 bool fromCache;
163 if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) {
164 return false;
165 }
166
167 nsCOMPtr<nsISupports> cacheToken;
168 cachingChannel->GetCacheToken(getter_AddRefs(cacheToken));
169 if (!cacheToken) {
170 return false;
171 }
172
173 nsCOMPtr<nsICacheEntryDescriptor> cacheEntry =
174 do_QueryInterface(cacheToken);
175 if (!cacheEntry) {
176 return false;
177 }
178
179 nsXPIDLCString tag;
180 cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
181 return tag.EqualsLiteral("1");
182 }
183
184 NS_IMETHODIMP
185 nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
186 {
187 if (mSuspendedChannel) {
188 MarkEntryClassified(aErrorCode);
189
190 if (NS_FAILED(aErrorCode)) {
191 #ifdef DEBUG
192 LOG(("nsChannelClassifier[%p]: cancelling channel %p with error "
193 "code: %x", this, mSuspendedChannel.get(), aErrorCode));
194 #endif
195 mSuspendedChannel->Cancel(aErrorCode);
196 }
197 #ifdef DEBUG
198 LOG(("nsChannelClassifier[%p]: resuming channel %p from "
199 "OnClassifyComplete", this, mSuspendedChannel.get()));
200 #endif
201 mSuspendedChannel->Resume();
202 mSuspendedChannel = nullptr;
203 }
204
205 return NS_OK;
206 }

mercurial