NTLM authentication is a challenge-response authentication type.
That it relies on implicit connection state only makes matter worse.
This has consequences at all levels, including the squid-ntlm helper protocol, which you'll notice is much more complex than the squid-basic helper protocol.
This document is the reference information for the squid-ntlm_helper protocol. Some parts are marked as planned, which means that are still not implemented but they will be planned very shortly.
This is an introductory chapter, where challenge-response-based protocols are introduced.
When a client wants to authenticate itself against some resource (in our
case, squid), it contacts it and sends a negotiate request.
The resource provide replies with a challenge, usually a random
string of data of some length.
The client then encodes this challenge with some algorithm that involves a secret key (the password), and sends the encoded challenge back to the server, along with enough information to allow the server to look up in some database for the secret key. Sane protocols could also send the challenge back (it's not a security risk, because it has been sent in the clear from the server to the client already, and so any interested party ALREADY has that info).
Microsoft's NTLM authentication of course doesn't; instead, the server should remember what challenge it sent. It is not a big deal if you have a strict "one client-one connection-one thread" mapping, but it's quite painful with a multiplexed, single-thread approach like Squid's architecture. Note that a "one client-one connection-one thread" mapping introduces it's own issues. Most notable is sharing state information (for example valid nonces in Digest Authentication across the threads.
When designing the squid authentication system, several performance-improving "tricks" have been employed, with beneficial effects to response times, authenticators load, and domain controller load. Unfortunately they have the effect of making the subsystem somewhat complex. In this chapter we'll just have a look at the basic communications among the interested parties, omitting those hacks.
There are at least three parties involved in the authentication procedure, but there could be as many as five. There are at least the client, Squid, and the authentication helper, but also one or more domain controllers might be involved, either in a load balancing/failover architecture, or in a chained architecture (Domain Trusts).
I've prepared some graphic (PNG format (11 Kb) or MS-PowerPoint (22 Kb)) detailing the protocol, along with a somewhat deep explanation of the NTLM authentication protocol as applied to WWW- and Proxy- authentication.
The Squid-helper protocol is text-based and line-oriented. All messages
are terminated with a single "\n".
Since second revision, we worked on moving as much logic as possible into squid, to better deal with some racy behaviors that are better handled in a centralized way. A side effect is that the V3 of the protocol is much simpler than V2 might have been.
V3 of the protocol only know of six messages, most parametrized somehow. To simplify parsers as much as possible, all of them are two letters long, followed by a space, a parameter if supported, and are terminated by a single newline ("\n"). They are:
|Squid instructs the helper to refresh the challenge. The helper MUST return TT||None|
|The helper has refreshed his challenge, and returns the new challenge to squid.||base64-encoded challenge packet|
|Squid is asking the helper to authenticate the client that has sent the supplied challenge. The helper MUST reply with one of AF, NA, BH.||base64-encoded authenticate packet|
|The supplied credentials checked out fine, and the user authenticated right. The user's credentials are in the attached parameter||User's credentials (form: domain\user)|
|The supplied credentials DID NOT match. There was no error in the protocol, but the credentials were just wrong.||A MANDATORY text message detailing the situation, which MAY be used for logging or sent back to the client.|
|There was a critical error of some kind while performing the authentication operation. The helper can't guarrantee continued operation, a challenge refresh should be asked as soon as possible to try and get the helper to work again.||A MANDATORY text message detailing the situation, which MAY be used for logging or sent back to the client.|
It's perfectly fine for an helper to block while performing operations. I's fine to intentionally block, and sometimes it's the sensible choice. For instance, in case of trouble communicating with the Domain Controller, an helper SHOULD block after receiving YR and before sending TT.
A reference to the second revision of the squid-to-helper protocol is available for historic reference.
Squid guarantees that all messages pertaining authentication from a client connection will be delivered to the same helper. Also, it won't send requests while waiting for an helper to do what it requires to perform the authentication operation: the protocol is strictly half-duplex.
Squid does heavy use of challenge reuse, to improve (dramatically) performance. In V2 of the protocol specification, logic to determine when to refresh the challenge was left to the helpers' discretion, while with V3 most of that logic was folded back into squid, and the helpers were "dumbified". This greatly simplified the squid-helper protocol, and reduced code redundancy.