NTLM/Negotiate Auth hangs browser
Categories
(Core :: Networking: HTTP, defect, P3)
Tracking
()
People
(Reporter: fb+mozdev, Assigned: valentin)
References
(Blocks 2 open bugs)
Details
(Whiteboard: [necko-triaged])
Attachments
(9 files, 1 obsolete file)
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review | |
|
48 bytes,
text/x-phabricator-request
|
Details | Review |
Comment 1•9 years ago
|
||
Comment 2•9 years ago
|
||
Comment 3•9 years ago
|
||
| Reporter | ||
Comment 4•9 years ago
|
||
Comment 5•9 years ago
|
||
Updated•9 years ago
|
Updated•9 years ago
|
Comment 6•8 years ago
|
||
| Reporter | ||
Comment 7•8 years ago
|
||
| Assignee | ||
Comment 9•6 years ago
|
||
This is blocking socket process, being the last consumer of DeprecatedSyncResolve.
| Assignee | ||
Comment 10•4 years ago
|
||
I need to look into how difficult it is to refactor this.
| Assignee | ||
Comment 11•4 years ago
|
||
It's not very clear why this bug was happening, but there are two possibilities:
- It was happening because of the call to DeprecatedSyncResolve on the main thread.
- It was happening because of the call to AcquireCredentialsHandleW was taking too long (also on the main thread)
For both of these, the solution would be to implement GenerateCredentialsAsync for nsHttpNTLMAuth.
The simple way to implement it would be to dispatch a task to the background thread that resolves the cname for the host, then calls nsAuthSSPI::Init on the same background thread.
The main problem with this is there is no code coverage for this.
Since this is using the windows SSPI functions, can't really hit this in automation. I've looked into creating a kerberos server in a VM, but it's not trivial. Another approach would be to have a dummy shim for the SecurityFunctionTableW structure for testing.
| Assignee | ||
Updated•4 years ago
|
| Assignee | ||
Comment 12•4 years ago
|
||
| Assignee | ||
Comment 13•4 years ago
|
||
Depends on D142161
| Reporter | ||
Comment 14•4 years ago
|
||
Do you still need logs from me?
Comment 15•4 years ago
|
||
(In reply to Florian Bender from comment #14)
Do you still need logs from me?
Yes, I think so. Could you send the log to necko at mozilla dot com?
Thanks.
| Reporter | ||
Comment 16•4 years ago
|
||
Copy I'll see what I can do, will take a few days.
Comment 17•3 years ago
|
||
Clear a needinfo that is pending on an inactive user.
Inactive users most likely will not respond; if the missing information is essential and cannot be collected another way, the bug maybe should be closed as INCOMPLETE.
For more information, please visit BugBot documentation.
Updated•3 months ago
|
Updated•1 month ago
|
| Assignee | ||
Comment 18•1 month ago
|
||
On windows the sys-ntlm module is used, instead of the generic impl.
That can make the tests fail if we have a different expectation.
Updated•1 month ago
|
| Assignee | ||
Comment 19•1 month ago
|
||
nsAuthSSPI::InitAsync resolves the canonical name of the host so that the
SPN is built from the CNAME target rather than the name in the URI. Nothing
covered that: every existing Negotiate test uses localhost, which
nsHostResolver satisfies without reaching a resolver thread, so the lookup
completes re-entrantly inside AsyncResolveNative and the asynchronous path
is never taken.
Resolve the host through a TRR server instead. That forces a real async
resolution and lets the name be a CNAME for a different host, so the test
fails if the canonical name is dropped or if the lookup result never makes
it back to the channel.
Windows only, since nsAuthSSPI is the only nsIAuthModule that implements
initAsync.
| Assignee | ||
Comment 20•1 month ago
|
||
Deferring nsIAuthModule::init to a background thread means a channel can be
cancelled while the init is still running. Cancel() runs on the main thread
and races the background thread that finishes the init and goes on to
generate the credentials, both of which touch GetNextTokenCompleteEvent.
test_bug1683176.js already cancels during Negotiate credential generation,
but it resolves localhost, so the init completes re-entrantly and the
cancel can only ever land after it. Delay the DoH response instead: the
transaction is suspended for authentication while the canonical name lookup
is still outstanding, so the cancel is guaranteed to land mid-init. The test
asserts the server never sees an authenticated retry, which is what
distinguishes a cancel that took effect from one that arrived too late.
| Assignee | ||
Comment 21•1 month ago
|
||
nsHttpNTLMAuth::GenerateCredentialsAsync runs GenerateCredentials on a
background thread, so Cancel() can run on the main thread concurrently with
the background thread filling in NTLMGetNextTokenCompleteEvent. Whichever
side wins, the callback has to run exactly once and the channel has to end
up aborted.
test_bug1683176.js covers this for Negotiate but there was no equivalent for
NTLM, which is the path that just gained a background thread.
Uses a content principal because the generic NTLM module reports the
identity as invalid and the channel prompts, which is blocked for
non-web-content triggered loads.
| Assignee | ||
Comment 22•1 month ago
|
||
There is no way to drive initAsync from JS with a mock module, because
nsIAuthModule::CreateInstance is a hardcoded factory rather than a component
manager lookup, so cover the contract directly.
-
Modules without an async init must report NS_ERROR_NOT_IMPLEMENTED.
NegotiateAuthState::InitAsync and GetNextTokenRunnable::Run both fall back
to the synchronous Init on exactly that result, so a module that grows a
partial implementation without returning it would silently skip
initialization. -
nsAuthSSPI must complete a Kerberos init off the main thread. localhost
is answered without reaching a resolver thread, so before InitAsync passed
an event target to AsyncResolveNative this ran re-entrantly on the caller's
thread; the test fails (on the MOZ_ASSERT in OnLookupComplete) if that
regresses. -
NTLM skips the canonical name lookup, so its init still reports completion
before returning. -
Empty and malformed service names are rejected without invoking the
callback.
| Assignee | ||
Comment 23•1 month ago
|
||
Every existing NTLM test either sets network.auth.force-generic-ntlm or runs
somewhere sys-ntlm is unavailable, so nsHttpNTLMAuth::mUseNative and the
native module itself had no coverage at all. That matters here because
nsAuthSSPI is now constructed on the main thread in ChallengeReceived and
then has Init and GetNextToken called on a background thread, which is why it
needed threadsafe refcounting.
Trusting localhost for automatic NTLM means the native module uses default
credentials and reports the identity as valid, so the channel never prompts.
The test fails if anything asks for a prompt, which is what keeps it honest:
the internal module always prompts, so a silent fallback to it is caught
rather than passing quietly.
The sequence stops after the type 1 message. Answering a type 2 challenge
makes SSPI derive a response from whoever is logged in, and on a machine with
no credentials for the domain named in the challenge it fails with
SEC_E_NO_CREDENTIALS -- which is why the other proxy tests force the generic
module. The type 3 step stays covered there.
| Assignee | ||
Comment 24•1 month ago
|
||
The other native NTLM tests accept any well-formed type 3 message, so a bug
that computed the NTLMv2 proof wrongly would still pass them. This one has
the server recompute the proof from a password it knows and compare:
NTHash = MD4(UTF16LE(password))
NTLMv2Hash = HMAC-MD5(NTHash, UTF16LE(uppercase(user) + domain))
NTProofStr = HMAC-MD5(NTLMv2Hash, serverChallenge + blob)
The server runs on the node harness, issues a real type 2 with a fresh random
challenge per connection, and returns 200 only when the proof matches. A
second task points it at a different password and expects 403, so a validator
that accepted everything cannot masquerade as a working one.
MD4 is implemented in JS because OpenSSL 3 moved it to the legacy provider
and node cannot supply it. It is checked against three known answers,
including the NT hash of the password from MS-NLMP, so a broken digest shows
up as a failed vector rather than as an unexplained rejection.
Uses explicit credentials for the same reason as the full exchange test:
default credentials give SEC_E_NO_CREDENTIALS on a machine that is not
domain joined.
| Assignee | ||
Comment 25•1 month ago
|
||
Nothing calls it. There is no JS caller anywhere in the tree, and the only two
implementations were nsDNSService::Resolve, which unwrapped the origin
attributes and forwarded straight to ResolveNative, and
ChildDNSService::Resolve, which returned NS_ERROR_NOT_AVAILABLE with the
comment that sync IPDL is evil. DNSServiceWrapper forwards via
NS_FORWARD_NSIDNSSERVICE and both implementors declare their methods with
NS_DECL_NSIDNSSERVICE, so removing the method from the interface is enough.
resolveNative stays. It has three callers, and they all pass RESOLVE_OFFLINE or
run off the main thread, so they consult literals and the cache rather than
blocking: nsCORSListenerProxy and two in DnsAndConnectSocket. Its documentation
was attached to the scriptable method, so it moves and is corrected to describe
the RESOLVE_OFFLINE exemption that ResolveNative actually implements.
The uuid is deliberately not bumped: the interface is builtinclass and internal,
and binary XPCOM consumers have not existed since Firefox 57.
Updated•1 month ago
|
Comment 26•5 days ago
|
||
Comment 27•5 days ago
|
||
| bugherder | ||
https://hg.mozilla.org/mozilla-central/rev/876b42ba609b
https://hg.mozilla.org/mozilla-central/rev/588ed54bb13e
https://hg.mozilla.org/mozilla-central/rev/f6ac49b649fb
https://hg.mozilla.org/mozilla-central/rev/28c149b17367
https://hg.mozilla.org/mozilla-central/rev/30ea1007227a
https://hg.mozilla.org/mozilla-central/rev/d4d395dae5d8
https://hg.mozilla.org/mozilla-central/rev/74584fca31f6
https://hg.mozilla.org/mozilla-central/rev/eded1691d088
https://hg.mozilla.org/mozilla-central/rev/e6cdd2a3ede9
Comment 28•4 days ago
|
||
Did you want to nominate this for the Fx160 relnotes? If so, set the relnote-firefox flag to "?"
https://wiki.mozilla.org/Release_Management/Release_Notes_Nomination
Possible wording:
Fixed Firefox freezing for several seconds when signing in to intranet sites that use NTLM or Kerberos (Negotiate) authentication.
| Assignee | ||
Comment 30•7 hours ago
|
||
Release Note Request (optional, but appreciated)
[Why is this notable]: Fixes a longstanding issue in Firefox where the thread is blocked waiting for DNS resolution.
[Affects Firefox for Android]: No
[Suggested wording]: Fixed Firefox freezing for several seconds when signing in to intranet sites that use NTLM or Kerberos (Negotiate) authentication.
[Links (documentation, blog post, etc)]:
Comment 31•3 hours ago
|
||
Thanks, added to the Fx160 nightly release notes, please allow 30 minutes for the site to update.
Description
•