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