/src/mozilla-central/extensions/auth/nsHttpNegotiateAuth.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* vim:set ts=4 sw=4 sts=4 et cindent: */ |
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 | | // |
7 | | // HTTP Negotiate Authentication Support Module |
8 | | // |
9 | | // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt |
10 | | // (formerly draft-brezak-spnego-http-04.txt) |
11 | | // |
12 | | // Also described here: |
13 | | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp |
14 | | // |
15 | | |
16 | | #include <string.h> |
17 | | #include <stdlib.h> |
18 | | |
19 | | #include "nsAuth.h" |
20 | | #include "nsHttpNegotiateAuth.h" |
21 | | |
22 | | #include "nsIHttpAuthenticableChannel.h" |
23 | | #include "nsIProxiedChannel.h" |
24 | | #include "nsIAuthModule.h" |
25 | | #include "nsIServiceManager.h" |
26 | | #include "nsIPrefService.h" |
27 | | #include "nsIPrefBranch.h" |
28 | | #include "nsIProxyInfo.h" |
29 | | #include "nsIURI.h" |
30 | | #include "nsCOMPtr.h" |
31 | | #include "nsString.h" |
32 | | #include "nsNetCID.h" |
33 | | #include "plbase64.h" |
34 | | #include "plstr.h" |
35 | | #include "mozilla/Base64.h" |
36 | | #include "mozilla/Logging.h" |
37 | | #include "mozilla/Tokenizer.h" |
38 | | #include "mozilla/UniquePtr.h" |
39 | | #include "mozilla/Unused.h" |
40 | | #include "prmem.h" |
41 | | #include "prnetdb.h" |
42 | | #include "mozilla/Likely.h" |
43 | | #include "mozilla/Sprintf.h" |
44 | | #include "nsIChannel.h" |
45 | | #include "nsNetUtil.h" |
46 | | #include "nsThreadUtils.h" |
47 | | #include "nsIHttpAuthenticatorCallback.h" |
48 | | #include "mozilla/Mutex.h" |
49 | | #include "nsICancelable.h" |
50 | | #include "nsUnicharUtils.h" |
51 | | #include "mozilla/net/HttpAuthUtils.h" |
52 | | |
53 | | using mozilla::Base64Decode; |
54 | | |
55 | | //----------------------------------------------------------------------------- |
56 | | |
57 | | static const char kNegotiate[] = "Negotiate"; |
58 | | static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris"; |
59 | | static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris"; |
60 | | static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies"; |
61 | | static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn"; |
62 | | static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi"; |
63 | | static const char kSSOinPBmode[] = "network.auth.private-browsing-sso"; |
64 | | |
65 | 0 | #define kNegotiateLen (sizeof(kNegotiate)-1) |
66 | 0 | #define DEFAULT_THREAD_TIMEOUT_MS 30000 |
67 | | |
68 | | //----------------------------------------------------------------------------- |
69 | | |
70 | | // Return false when the channel comes from a Private browsing window. |
71 | | static bool |
72 | | TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel, bool proxyAuth) |
73 | 0 | { |
74 | 0 | // Proxy should go all the time, it's not considered a privacy leak |
75 | 0 | // to send default credentials to a proxy. |
76 | 0 | if (proxyAuth) { |
77 | 0 | return true; |
78 | 0 | } |
79 | 0 | |
80 | 0 | nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel); |
81 | 0 | MOZ_ASSERT(bareChannel); |
82 | 0 |
|
83 | 0 | if (!NS_UsePrivateBrowsing(bareChannel)) { |
84 | 0 | return true; |
85 | 0 | } |
86 | 0 | |
87 | 0 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
88 | 0 | if (prefs) { |
89 | 0 | bool ssoInPb; |
90 | 0 | if (NS_SUCCEEDED(prefs->GetBoolPref(kSSOinPBmode, &ssoInPb)) && ssoInPb) { |
91 | 0 | return true; |
92 | 0 | } |
93 | 0 | |
94 | 0 | // When the "Never remember history" option is set, all channels are |
95 | 0 | // set PB mode flag, but here we want to make an exception, users |
96 | 0 | // want their credentials go out. |
97 | 0 | bool dontRememberHistory; |
98 | 0 | if (NS_SUCCEEDED(prefs->GetBoolPref("browser.privatebrowsing.autostart", |
99 | 0 | &dontRememberHistory)) && |
100 | 0 | dontRememberHistory) { |
101 | 0 | return true; |
102 | 0 | } |
103 | 0 | } |
104 | 0 | |
105 | 0 | return false; |
106 | 0 | } |
107 | | |
108 | | NS_IMETHODIMP |
109 | | nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags) |
110 | 0 | { |
111 | 0 | // |
112 | 0 | // Negotiate Auth creds should not be reused across multiple requests. |
113 | 0 | // Only perform the negotiation when it is explicitly requested by the |
114 | 0 | // server. Thus, do *NOT* use the "REUSABLE_CREDENTIALS" flag here. |
115 | 0 | // |
116 | 0 | // CONNECTION_BASED is specified instead of REQUEST_BASED since we need |
117 | 0 | // to complete a sequence of transactions with the server over the same |
118 | 0 | // connection. |
119 | 0 | // |
120 | 0 | *flags = CONNECTION_BASED | IDENTITY_IGNORED; |
121 | 0 | return NS_OK; |
122 | 0 | } |
123 | | |
124 | | // |
125 | | // Always set *identityInvalid == FALSE here. This |
126 | | // will prevent the browser from popping up the authentication |
127 | | // prompt window. Because GSSAPI does not have an API |
128 | | // for fetching initial credentials (ex: A Kerberos TGT), |
129 | | // there is no correct way to get the users credentials. |
130 | | // |
131 | | NS_IMETHODIMP |
132 | | nsHttpNegotiateAuth::ChallengeReceived(nsIHttpAuthenticableChannel *authChannel, |
133 | | const char *challenge, |
134 | | bool isProxyAuth, |
135 | | nsISupports **sessionState, |
136 | | nsISupports **continuationState, |
137 | | bool *identityInvalid) |
138 | 0 | { |
139 | 0 | nsIAuthModule *module = (nsIAuthModule *) *continuationState; |
140 | 0 |
|
141 | 0 | *identityInvalid = false; |
142 | 0 | if (module) |
143 | 0 | return NS_OK; |
144 | 0 | |
145 | 0 | nsresult rv; |
146 | 0 |
|
147 | 0 | nsCOMPtr<nsIURI> uri; |
148 | 0 | rv = authChannel->GetURI(getter_AddRefs(uri)); |
149 | 0 | if (NS_FAILED(rv)) |
150 | 0 | return rv; |
151 | 0 | |
152 | 0 | uint32_t req_flags = nsIAuthModule::REQ_DEFAULT; |
153 | 0 | nsAutoCString service; |
154 | 0 |
|
155 | 0 | if (isProxyAuth) { |
156 | 0 | if (!TestBoolPref(kNegotiateAuthAllowProxies)) { |
157 | 0 | LOG(("nsHttpNegotiateAuth::ChallengeReceived proxy auth blocked\n")); |
158 | 0 | return NS_ERROR_ABORT; |
159 | 0 | } |
160 | 0 |
|
161 | 0 | req_flags |= nsIAuthModule::REQ_PROXY_AUTH; |
162 | 0 | nsCOMPtr<nsIProxyInfo> proxyInfo; |
163 | 0 | authChannel->GetProxyInfo(getter_AddRefs(proxyInfo)); |
164 | 0 | NS_ENSURE_STATE(proxyInfo); |
165 | 0 |
|
166 | 0 | proxyInfo->GetHost(service); |
167 | 0 | } |
168 | 0 | else { |
169 | 0 | bool allowed = TestNotInPBMode(authChannel, isProxyAuth) && |
170 | 0 | (TestNonFqdn(uri) || |
171 | 0 | mozilla::net::auth::URIMatchesPrefPattern(uri, kNegotiateAuthTrustedURIs)); |
172 | 0 | if (!allowed) { |
173 | 0 | LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n")); |
174 | 0 | return NS_ERROR_ABORT; |
175 | 0 | } |
176 | 0 |
|
177 | 0 | bool delegation = mozilla::net::auth::URIMatchesPrefPattern(uri, kNegotiateAuthDelegationURIs); |
178 | 0 | if (delegation) { |
179 | 0 | LOG((" using REQ_DELEGATE\n")); |
180 | 0 | req_flags |= nsIAuthModule::REQ_DELEGATE; |
181 | 0 | } |
182 | 0 |
|
183 | 0 | rv = uri->GetAsciiHost(service); |
184 | 0 | if (NS_FAILED(rv)) |
185 | 0 | return rv; |
186 | 0 | } |
187 | 0 | |
188 | 0 | LOG((" service = %s\n", service.get())); |
189 | 0 |
|
190 | 0 | // |
191 | 0 | // The correct service name for IIS servers is "HTTP/f.q.d.n", so |
192 | 0 | // construct the proper service name for passing to "gss_import_name". |
193 | 0 | // |
194 | 0 | // TODO: Possibly make this a configurable service name for use |
195 | 0 | // with non-standard servers that use stuff like "khttp/f.q.d.n" |
196 | 0 | // instead. |
197 | 0 | // |
198 | 0 | service.InsertLiteral("HTTP@", 0); |
199 | 0 |
|
200 | 0 | const char *contractID; |
201 | 0 | if (TestBoolPref(kNegotiateAuthSSPI)) { |
202 | 0 | LOG((" using negotiate-sspi\n")); |
203 | 0 | contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-sspi"; |
204 | 0 | } |
205 | 0 | else { |
206 | 0 | LOG((" using negotiate-gss\n")); |
207 | 0 | contractID = NS_AUTH_MODULE_CONTRACTID_PREFIX "negotiate-gss"; |
208 | 0 | } |
209 | 0 |
|
210 | 0 | rv = CallCreateInstance(contractID, &module); |
211 | 0 |
|
212 | 0 | if (NS_FAILED(rv)) { |
213 | 0 | LOG((" Failed to load Negotiate Module \n")); |
214 | 0 | return rv; |
215 | 0 | } |
216 | 0 |
|
217 | 0 | rv = module->Init(service.get(), req_flags, nullptr, nullptr, nullptr); |
218 | 0 |
|
219 | 0 | if (NS_FAILED(rv)) { |
220 | 0 | NS_RELEASE(module); |
221 | 0 | return rv; |
222 | 0 | } |
223 | 0 |
|
224 | 0 | *continuationState = module; |
225 | 0 | return NS_OK; |
226 | 0 | } |
227 | | |
228 | | NS_IMPL_ISUPPORTS(nsHttpNegotiateAuth, nsIHttpAuthenticator) |
229 | | |
230 | | namespace { |
231 | | |
232 | | // |
233 | | // GetNextTokenCompleteEvent |
234 | | // |
235 | | // This event is fired on main thread when async call of |
236 | | // nsHttpNegotiateAuth::GenerateCredentials is finished. During the Run() |
237 | | // method the nsIHttpAuthenticatorCallback::OnCredsAvailable is called with |
238 | | // obtained credentials, flags and NS_OK when successful, otherwise |
239 | | // NS_ERROR_FAILURE is returned as a result of failed operation. |
240 | | // |
241 | | class GetNextTokenCompleteEvent final : public nsIRunnable, |
242 | | public nsICancelable |
243 | | { |
244 | | virtual ~GetNextTokenCompleteEvent() |
245 | 0 | { |
246 | 0 | if (mCreds) { |
247 | 0 | free(mCreds); |
248 | 0 | } |
249 | 0 | }; |
250 | | |
251 | | public: |
252 | | NS_DECL_THREADSAFE_ISUPPORTS |
253 | | |
254 | | explicit GetNextTokenCompleteEvent(nsIHttpAuthenticatorCallback* aCallback) |
255 | | : mCallback(aCallback) |
256 | | , mCreds(nullptr) |
257 | | , mCancelled(false) |
258 | 0 | { |
259 | 0 | } |
260 | | |
261 | | NS_IMETHODIMP DispatchSuccess(char *aCreds, |
262 | | uint32_t aFlags, |
263 | | already_AddRefed<nsISupports> aSessionState, |
264 | | already_AddRefed<nsISupports> aContinuationState) |
265 | 0 | { |
266 | 0 | // Called from worker thread |
267 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
268 | 0 |
|
269 | 0 | mCreds = aCreds; |
270 | 0 | mFlags = aFlags; |
271 | 0 | mResult = NS_OK; |
272 | 0 | mSessionState = aSessionState; |
273 | 0 | mContinuationState = aContinuationState; |
274 | 0 | return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); |
275 | 0 | } |
276 | | |
277 | | NS_IMETHODIMP DispatchError(already_AddRefed<nsISupports> aSessionState, |
278 | | already_AddRefed<nsISupports> aContinuationState) |
279 | 0 | { |
280 | 0 | // Called from worker thread |
281 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
282 | 0 |
|
283 | 0 | mResult = NS_ERROR_FAILURE; |
284 | 0 | mSessionState = aSessionState; |
285 | 0 | mContinuationState = aContinuationState; |
286 | 0 | return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL); |
287 | 0 | } |
288 | | |
289 | | NS_IMETHODIMP Run() override |
290 | 0 | { |
291 | 0 | // Runs on main thread |
292 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
293 | 0 |
|
294 | 0 | if (!mCancelled) { |
295 | 0 | nsCOMPtr<nsIHttpAuthenticatorCallback> callback; |
296 | 0 | callback.swap(mCallback); |
297 | 0 | callback->OnCredsGenerated(mCreds, mFlags, mResult, mSessionState, mContinuationState); |
298 | 0 | } |
299 | 0 | return NS_OK; |
300 | 0 | } |
301 | | |
302 | | NS_IMETHODIMP Cancel(nsresult aReason) override |
303 | 0 | { |
304 | 0 | // Supposed to be called from main thread |
305 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
306 | 0 |
|
307 | 0 | mCancelled = true; |
308 | 0 | return NS_OK; |
309 | 0 | } |
310 | | |
311 | | private: |
312 | | nsCOMPtr<nsIHttpAuthenticatorCallback> mCallback; |
313 | | char *mCreds; // This class owns it, freed in destructor |
314 | | uint32_t mFlags; |
315 | | nsresult mResult; |
316 | | bool mCancelled; |
317 | | nsCOMPtr<nsISupports> mSessionState; |
318 | | nsCOMPtr<nsISupports> mContinuationState; |
319 | | }; |
320 | | |
321 | | NS_IMPL_ISUPPORTS(GetNextTokenCompleteEvent, nsIRunnable, nsICancelable) |
322 | | |
323 | | // |
324 | | // GetNextTokenRunnable |
325 | | // |
326 | | // This runnable is created by GenerateCredentialsAsync and it runs |
327 | | // in nsHttpNegotiateAuth::mNegotiateThread and calling GenerateCredentials. |
328 | | // |
329 | | class GetNextTokenRunnable final : public mozilla::Runnable |
330 | | { |
331 | 0 | ~GetNextTokenRunnable() override = default; |
332 | | public: |
333 | | GetNextTokenRunnable(nsIHttpAuthenticableChannel* authChannel, |
334 | | const char* challenge, |
335 | | bool isProxyAuth, |
336 | | const char16_t* domain, |
337 | | const char16_t* username, |
338 | | const char16_t* password, |
339 | | nsISupports* sessionState, |
340 | | nsISupports* continuationState, |
341 | | GetNextTokenCompleteEvent* aCompleteEvent) |
342 | | : mozilla::Runnable("GetNextTokenRunnable") |
343 | | , mAuthChannel(authChannel) |
344 | | , mChallenge(challenge) |
345 | | , mIsProxyAuth(isProxyAuth) |
346 | | , mDomain(domain) |
347 | | , mUsername(username) |
348 | | , mPassword(password) |
349 | | , mSessionState(sessionState) |
350 | | , mContinuationState(continuationState) |
351 | | , mCompleteEvent(aCompleteEvent) |
352 | 0 | { |
353 | 0 | } |
354 | | |
355 | | NS_IMETHODIMP Run() override |
356 | 0 | { |
357 | 0 | // Runs on worker thread |
358 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
359 | 0 |
|
360 | 0 | char *creds; |
361 | 0 | uint32_t flags; |
362 | 0 | nsresult rv = ObtainCredentialsAndFlags(&creds, &flags); |
363 | 0 |
|
364 | 0 | // Passing session and continuation state this way to not touch |
365 | 0 | // referencing of the object that may not be thread safe. |
366 | 0 | // Not having a thread safe referencing doesn't mean the object |
367 | 0 | // cannot be used on multiple threads (one example is nsAuthSSPI.) |
368 | 0 | // This ensures state objects will be destroyed on the main thread |
369 | 0 | // when not changed by GenerateCredentials. |
370 | 0 | if (NS_FAILED(rv)) { |
371 | 0 | return mCompleteEvent->DispatchError(mSessionState.forget(), |
372 | 0 | mContinuationState.forget()); |
373 | 0 | } |
374 | 0 | |
375 | 0 | return mCompleteEvent->DispatchSuccess(creds, flags, |
376 | 0 | mSessionState.forget(), |
377 | 0 | mContinuationState.forget()); |
378 | 0 | } |
379 | | |
380 | | NS_IMETHODIMP ObtainCredentialsAndFlags(char **aCreds, uint32_t *aFlags) |
381 | 0 | { |
382 | 0 | nsresult rv; |
383 | 0 |
|
384 | 0 | // Use negotiate service to call GenerateCredentials outside of main thread |
385 | 0 | nsAutoCString contractId; |
386 | 0 | contractId.AssignLiteral(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX); |
387 | 0 | contractId.AppendLiteral("negotiate"); |
388 | 0 | nsCOMPtr<nsIHttpAuthenticator> authenticator = |
389 | 0 | do_GetService(contractId.get(), &rv); |
390 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
391 | 0 |
|
392 | 0 | nsISupports *sessionState = mSessionState; |
393 | 0 | nsISupports *continuationState = mContinuationState; |
394 | 0 | // The continuationState is for the sake of completeness propagated |
395 | 0 | // to the caller (despite it is not changed in any GenerateCredentials |
396 | 0 | // implementation). |
397 | 0 | // |
398 | 0 | // The only implementation that use sessionState is the |
399 | 0 | // nsHttpDigestAuth::GenerateCredentials. Since there's no reason |
400 | 0 | // to implement nsHttpDigestAuth::GenerateCredentialsAsync |
401 | 0 | // because digest auth does not block the main thread, we won't |
402 | 0 | // propagate changes to sessionState to the caller because of |
403 | 0 | // the change is too complicated on the caller side. |
404 | 0 | // |
405 | 0 | // Should any of the session or continuation states change inside |
406 | 0 | // this method, they must be threadsafe. |
407 | 0 | rv = authenticator->GenerateCredentials(mAuthChannel, |
408 | 0 | mChallenge.get(), |
409 | 0 | mIsProxyAuth, |
410 | 0 | mDomain.get(), |
411 | 0 | mUsername.get(), |
412 | 0 | mPassword.get(), |
413 | 0 | &sessionState, |
414 | 0 | &continuationState, |
415 | 0 | aFlags, |
416 | 0 | aCreds); |
417 | 0 | if (mSessionState != sessionState) { |
418 | 0 | mSessionState = sessionState; |
419 | 0 | } |
420 | 0 | if (mContinuationState != continuationState) { |
421 | 0 | mContinuationState = continuationState; |
422 | 0 | } |
423 | 0 | return rv; |
424 | 0 | } |
425 | | private: |
426 | | nsCOMPtr<nsIHttpAuthenticableChannel> mAuthChannel; |
427 | | nsCString mChallenge; |
428 | | bool mIsProxyAuth; |
429 | | nsString mDomain; |
430 | | nsString mUsername; |
431 | | nsString mPassword; |
432 | | nsCOMPtr<nsISupports> mSessionState; |
433 | | nsCOMPtr<nsISupports> mContinuationState; |
434 | | RefPtr<GetNextTokenCompleteEvent> mCompleteEvent; |
435 | | }; |
436 | | |
437 | | } // anonymous namespace |
438 | | |
439 | | NS_IMETHODIMP |
440 | | nsHttpNegotiateAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel, |
441 | | nsIHttpAuthenticatorCallback* aCallback, |
442 | | const char *challenge, |
443 | | bool isProxyAuth, |
444 | | const char16_t *domain, |
445 | | const char16_t *username, |
446 | | const char16_t *password, |
447 | | nsISupports *sessionState, |
448 | | nsISupports *continuationState, |
449 | | nsICancelable **aCancelable) |
450 | 0 | { |
451 | 0 | NS_ENSURE_ARG(aCallback); |
452 | 0 | NS_ENSURE_ARG_POINTER(aCancelable); |
453 | 0 |
|
454 | 0 | RefPtr<GetNextTokenCompleteEvent> cancelEvent = |
455 | 0 | new GetNextTokenCompleteEvent(aCallback); |
456 | 0 |
|
457 | 0 |
|
458 | 0 | nsCOMPtr<nsIRunnable> getNextTokenRunnable = |
459 | 0 | new GetNextTokenRunnable(authChannel, |
460 | 0 | challenge, |
461 | 0 | isProxyAuth, |
462 | 0 | domain, |
463 | 0 | username, |
464 | 0 | password, |
465 | 0 | sessionState, |
466 | 0 | continuationState, |
467 | 0 | cancelEvent); |
468 | 0 | cancelEvent.forget(aCancelable); |
469 | 0 |
|
470 | 0 | nsresult rv; |
471 | 0 | if (!mNegotiateThread) { |
472 | 0 | mNegotiateThread = |
473 | 0 | new mozilla::LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, |
474 | 0 | NS_LITERAL_CSTRING("NegotiateAuth")); |
475 | 0 | NS_ENSURE_TRUE(mNegotiateThread, NS_ERROR_OUT_OF_MEMORY); |
476 | 0 | } |
477 | 0 | rv = mNegotiateThread->Dispatch(getNextTokenRunnable, NS_DISPATCH_NORMAL); |
478 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
479 | 0 |
|
480 | 0 | return NS_OK; |
481 | 0 | } |
482 | | |
483 | | // |
484 | | // GenerateCredentials |
485 | | // |
486 | | // This routine is responsible for creating the correct authentication |
487 | | // blob to pass to the server that requested "Negotiate" authentication. |
488 | | // |
489 | | NS_IMETHODIMP |
490 | | nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel, |
491 | | const char *challenge, |
492 | | bool isProxyAuth, |
493 | | const char16_t *domain, |
494 | | const char16_t *username, |
495 | | const char16_t *password, |
496 | | nsISupports **sessionState, |
497 | | nsISupports **continuationState, |
498 | | uint32_t *flags, |
499 | | char **creds) |
500 | 0 | { |
501 | 0 | // ChallengeReceived must have been called previously. |
502 | 0 | nsIAuthModule *module = (nsIAuthModule *) *continuationState; |
503 | 0 | NS_ENSURE_TRUE(module, NS_ERROR_NOT_INITIALIZED); |
504 | 0 |
|
505 | 0 | *flags = USING_INTERNAL_IDENTITY; |
506 | 0 |
|
507 | 0 | LOG(("nsHttpNegotiateAuth::GenerateCredentials() [challenge=%s]\n", challenge)); |
508 | 0 |
|
509 | 0 | NS_ASSERTION(creds, "null param"); |
510 | 0 |
|
511 | | #ifdef DEBUG |
512 | | bool isGssapiAuth = |
513 | | !PL_strncasecmp(challenge, kNegotiate, kNegotiateLen); |
514 | | NS_ASSERTION(isGssapiAuth, "Unexpected challenge"); |
515 | | #endif |
516 | |
|
517 | 0 | // |
518 | 0 | // If the "Negotiate:" header had some data associated with it, |
519 | 0 | // that data should be used as the input to this call. This may |
520 | 0 | // be a continuation of an earlier call because GSSAPI authentication |
521 | 0 | // often takes multiple round-trips to complete depending on the |
522 | 0 | // context flags given. We want to use MUTUAL_AUTHENTICATION which |
523 | 0 | // generally *does* require multiple round-trips. Don't assume |
524 | 0 | // auth can be completed in just 1 call. |
525 | 0 | // |
526 | 0 | unsigned int len = strlen(challenge); |
527 | 0 |
|
528 | 0 | void *inToken = nullptr, *outToken; |
529 | 0 | uint32_t inTokenLen, outTokenLen; |
530 | 0 |
|
531 | 0 | if (len > kNegotiateLen) { |
532 | 0 | challenge += kNegotiateLen; |
533 | 0 | while (*challenge == ' ') |
534 | 0 | challenge++; |
535 | 0 | len = strlen(challenge); |
536 | 0 |
|
537 | 0 | // strip off any padding (see bug 230351) |
538 | 0 | while (challenge[len - 1] == '=') |
539 | 0 | len--; |
540 | 0 |
|
541 | 0 | // |
542 | 0 | // Decode the response that followed the "Negotiate" token |
543 | 0 | // |
544 | 0 | nsresult rv = |
545 | 0 | Base64Decode(challenge, len, (char**)&inToken, &inTokenLen); |
546 | 0 |
|
547 | 0 | if (NS_FAILED(rv)) { |
548 | 0 | free(inToken); |
549 | 0 | return rv; |
550 | 0 | } |
551 | 0 | } |
552 | 0 | else { |
553 | 0 | // |
554 | 0 | // Initializing, don't use an input token. |
555 | 0 | // |
556 | 0 | inTokenLen = 0; |
557 | 0 | } |
558 | 0 |
|
559 | 0 | nsresult rv = module->GetNextToken(inToken, inTokenLen, &outToken, &outTokenLen); |
560 | 0 |
|
561 | 0 | free(inToken); |
562 | 0 |
|
563 | 0 | if (NS_FAILED(rv)) |
564 | 0 | return rv; |
565 | 0 | |
566 | 0 | if (outTokenLen == 0) { |
567 | 0 | LOG((" No output token to send, exiting")); |
568 | 0 | return NS_ERROR_FAILURE; |
569 | 0 | } |
570 | 0 |
|
571 | 0 | // |
572 | 0 | // base64 encode the output token. |
573 | 0 | // |
574 | 0 | char *encoded_token = PL_Base64Encode((char *)outToken, outTokenLen, nullptr); |
575 | 0 |
|
576 | 0 | free(outToken); |
577 | 0 |
|
578 | 0 | if (!encoded_token) |
579 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
580 | 0 | |
581 | 0 | LOG((" Sending a token of length %d\n", outTokenLen)); |
582 | 0 |
|
583 | 0 | // allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0") |
584 | 0 | const int bufsize = kNegotiateLen + 1 + strlen(encoded_token) + 1; |
585 | 0 | *creds = (char *) moz_xmalloc(bufsize); |
586 | 0 | snprintf(*creds, bufsize, "%s %s", kNegotiate, encoded_token); |
587 | 0 |
|
588 | 0 | PR_Free(encoded_token); // PL_Base64Encode() uses PR_Malloc(). |
589 | 0 | return rv; |
590 | 0 | } |
591 | | |
592 | | bool |
593 | | nsHttpNegotiateAuth::TestBoolPref(const char *pref) |
594 | 0 | { |
595 | 0 | nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); |
596 | 0 | if (!prefs) |
597 | 0 | return false; |
598 | 0 | |
599 | 0 | bool val; |
600 | 0 | nsresult rv = prefs->GetBoolPref(pref, &val); |
601 | 0 | if (NS_FAILED(rv)) |
602 | 0 | return false; |
603 | 0 | |
604 | 0 | return val; |
605 | 0 | } |
606 | | |
607 | | bool |
608 | | nsHttpNegotiateAuth::TestNonFqdn(nsIURI *uri) |
609 | 0 | { |
610 | 0 | nsAutoCString host; |
611 | 0 | PRNetAddr addr; |
612 | 0 |
|
613 | 0 | if (!TestBoolPref(kNegotiateAuthAllowNonFqdn)) |
614 | 0 | return false; |
615 | 0 | |
616 | 0 | if (NS_FAILED(uri->GetAsciiHost(host))) |
617 | 0 | return false; |
618 | 0 | |
619 | 0 | // return true if host does not contain a dot and is not an ip address |
620 | 0 | return !host.IsEmpty() && !host.Contains('.') && |
621 | 0 | PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS; |
622 | 0 | } |