Discussion:
[squid-users] Uncomplete Negotiate negotiation with Kerberos
Emmanuel Coirier
2018-10-02 11:32:26 UTC
Permalink
Hi everybody !

Is it desirable that the Negotiate/Kerberos proxy authentication method be interrupted as soon as Squid knows the identity of the browser's user, without letting the browser authenticates Squid in response ?

When a browser wants to connect to some random HTTP website, it sends a GET request. A proxy wanting to authenticate the browser, via Kerberos, responds with 407, which lets the browser knows that it has to authenticate. Then the browser sends a Kerberos authenticator (in base64 in the Proxy-Authorization header). Then Squid *should* answer back with a Proxy-Authentication-Info letting the browser finish the Kerberos process and validating that it is communicating with the real proxy it wants to use. It is how gss-api is intended to be used.

It was the case with Squid 3.3.8, where Squid answers back, along with the response (200 OK), the Proxy-Authentication-Info header with the final token. But with Squid 4.2, the browser doesn't anymore add this headers, preventing the browser to authenticate back the proxy.

The problem is that it enables some potentially Man in the Middle attack (since any malicious proxy where the traffic is diverted could then answers back without the client knowing it talks to a malicious proxy)

Here is an example of this phenomenon with GET and CONNECT :

GET http://perdu.com/ HTTP/1.1
Host: perdu.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1

HTTP/1.1 407 Proxy Authentication Required
Server: squid/4.2
Mime-Version: 1.0
Date: Tue, 02 Oct 2018 08:35:27 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 1852
X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
Vary: Accept-Language
Content-Language: fr
Proxy-Authenticate: Negotiate
X-Cache: MISS from ceriseconfite
X-Cache-Lookup: NONE from ceriseconfite:3129
Via: 1.1 ceriseconfite (squid/4.2)
Connection: keep-alive

<some really long webpage for something that shouldn't be displayed>

GET http://perdu.com/ HTTP/1.1
Host: perdu.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: fr,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Proxy-Authorization: Negotiate YIIChwYGKw[...blablabla some random authenticator...]W6m

HTTP/1.1 200 OK (with Squid 4.2)
Date: Tue, 02 Oct 2018 08:35:27 GMT
Server: Apache
Last-Modified: Thu, 02 Jun 2016 06:01:08 GMT
ETag: "cc-5344555136fe9"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 163
Content-Type: text/html
X-Cache: MISS from ceriseconfite
X-Cache-Lookup: MISS from ceriseconfite:3129
Via: 1.1 ceriseconfite (squid/4.2)
Connection: keep-alive
<------------- Missing the Proxy-Authentication-Info


HTTP/1.1 200 OK (with older Squid 3.3.8)
Date: Fri, 28 Sep 2018 08:09:27 GMT
Server: Apache
Last-Modified: Thu, 02 Jun 2016 06:01:08 GMT
ETag: "cc-5344555136fe9"
Accept-Ranges: bytes
Vary: Accept-Encoding
Content-Encoding: gzip
Content-Length: 163&
Content-Type: text/html
Proxy-Authentication-Info: Negotiate oRQwEqADCgEAoQsGCSqGSIb3EgECAg== <--- here was the authentication response back to the client
X-Cache: MISS from sucre-SQUID-50118215
X-Cache-Lookup: MISS from sucre-SQUID-50118215:3127
Connection: keep-alive

..........5....0.Dw...H.....L.l..Cc.H%).S..'!....:...L..:B
:3.S.pU.0....4....#.O....\...-*KdmzE...m........."...v...R..0...$\. j.....ny
2...0.4.B...>....|....-....


And here is the same exchange but with TLS :

CONNECT perdu.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Proxy-Connection: keep-alive
Connection: keep-alive
Host: perdu.com:443

HTTP/1.1 407 Proxy Authentication Required
Server: squid/4.2
Mime-Version: 1.0
Date: Tue, 02 Oct 2018 08:30:51 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 1813
X-Squid-Error: ERR_CACHE_ACCESS_DENIED 0
Vary: Accept-Language
Content-Language: en
Proxy-Authenticate: Negotiate
X-Cache: MISS from ceriseconfite
X-Cache-Lookup: NONE from ceriseconfite:3129
Via: 1.1 ceriseconfite (squid/4.2)
Connection: keep-alive

<some really long webpage for something that shouldn't be displayed>

CONNECT perdu.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Proxy-Connection: keep-alive
Connection: keep-alive
Host: perdu.com:443
Proxy-Authorization: Negotiate YIIChwYGKwYBBQUCo[...blablabla some other random authenticator...]zCqD

HTTP/1.1 200 Connection established (same with Squid 4.2 and Squid 3.3.8)
<---- is the Proxy-Authentication-Info missing ?

(TLS tunnel exchange)

It this behavior endorsed, or is it a bug ?

Actually, I know that browsers seem not to care, but they won't be able to care if they don't receive all the needed information.

Thanks for reading and your response :-)

--
Emmanuel Coirier
Amos Jeffries
2018-10-02 12:55:18 UTC
Permalink
Post by Emmanuel Coirier
Hi everybody !
Is it desirable that the Negotiate/Kerberos proxy authentication method be interrupted as soon as Squid knows the identity of the browser's user, without letting the browser authenticates Squid in response ?
That is not possible to happen. The process of authenticating finishes
by telling Squid what the username is.
Post by Emmanuel Coirier
When a browser wants to connect to some random HTTP website, it sends a GET request. A proxy wanting to authenticate the browser, via Kerberos, responds with 407, which lets the browser knows that it has to authenticate. Then the browser sends a Kerberos authenticator (in base64 in the Proxy-Authorization header). Then Squid *should* answer back with a Proxy-Authentication-Info letting the browser finish the Kerberos process and validating that it is communicating with the real proxy it wants to use. It is how gss-api is intended to be used.
That is not true. RFC 4559 section 4
(<https://tools.ietf.org/html/rfc4559#section-4>) defines how Negotiate
scheme operates in HTTP.

Notice two things;

* firstly that there is no mention of *-Authentication-Info headers
anywhere in that specification.

* secondly that the auth tokens are exclusively flowing from the browser
to the server or proxy, not the other way around.
Post by Emmanuel Coirier
It was the case with Squid 3.3.8, where Squid answers back, along with the response (200 OK), the Proxy-Authentication-Info header with the final token. But with Squid 4.2, the browser doesn't anymore add this headers, preventing the browser to authenticate back the proxy.
Squid-4 has been updated to conform to RFC 7615 requirements of these
*-Authentication-Info headers usage which was in fact only previously
defined for use with Digest authentication scheme - not Negotiate.
Post by Emmanuel Coirier
The problem is that it enables some potentially Man in the Middle attack (since any malicious proxy where the traffic is diverted could then answers back without the client knowing it talks to a malicious proxy)
Quite the opposite. The MITM issue you point out is part of the
fundamental design of the Negotiate scheme and exists for both Squid-3
and Squid-4 behaviours.


Having the client use a token provided in-channel from the proxy enables
an MITM observing that channel to inject changes giving itself control
over what the client does in future authentication. This extra control
point is prohibited by the Squid-4 behaviour, and has never been a
formal part of Negotiate scheme as you can see from the RFC 4559 texts.

The design of Negotiate/Kerberos has both client and proxy independently
contact the DC to respectively generate and verify the tokens. All token
operations are performed by the DC itself and contain secrets only the
DC knows. The flow of tokens is exclusively from client to proxy as
proof that the client is already authenticated with the DC. The proxy /
server response is intentionally lacking to starve any MITM of
information it might use to reliably affect changes to the client tokens.



...
Post by Emmanuel Coirier
It this behavior endorsed, or is it a bug ?
The Squid-4 behaviour is "endorsed". The Squid-3 behaviour was arguably
a bug or more correctly an experimental feature which has now been
removed due to both the increased MITM risk I describe above and how its
content differs in syntax from the RFC 7615 specification.

Amos
Emmanuel Coirier
2018-10-02 14:29:10 UTC
Permalink
Hi Amos and others,

Thanks for your response, but I'm afraid I'm not sure to have understood everything...
de Amos Jeffries
Post by Emmanuel Coirier
When a browser wants to connect to some random HTTP website, it sends a GET
...
to be used.
That is not true. RFC 4559 section 4
(<https://tools.ietf.org/html/rfc4559#section-4>) defines how Negotiate scheme
operates in HTTP.
Ok
Post by Emmanuel Coirier
The problem is that it enables some potentially Man in the Middle
attack (since any malicious proxy where the traffic is diverted could
then answers back without the client knowing it talks to a malicious
proxy)
Quite the opposite. The MITM issue you point out is part of the fundamental
design of the Negotiate scheme and exists for both Squid-3 and Squid-4
behaviours.
Are you telling me that the Negotiate scheme is fundamentally flawed ?
Having the client use a token provided in-channel from the proxy enables an MITM
observing that channel to inject changes giving itself control over what the
client does in future authentication. This extra control point is prohibited by
I don't understand this point. If we hypothesize that the proxy sends the tokens (including the last one) generated by the service gssapi back to the client (whatever the means), we can think that the gssapi tokens going from service to client are authenticated and encrypted.

With Kerberos, it is possible since the client previously sends a service ticket which contains a random session key encrypted with the service key. The service can use this session key to authentify its response to the client.

So it is harder for a MITM to fake this last gssapi token, especially if the client wait for it. So I don't see how a MITM could exploit this last token.

Clearly, the http body response could be altered, but it can also be altered in the current situation.

Or, is it right that a client cannot trust any form of service authentication based on Negotiate since it is fundamentally flawed ? And thus this last token has no use ?
the Squid-4 behaviour, and has never been a formal part of Negotiate scheme as
you can see from the RFC 4559 texts.
The design of Negotiate/Kerberos has both client and proxy independently contact
the DC to respectively generate and verify the tokens. All token operations are
In my undestanding and experiments of Kerberos, the service (here Squid and more precisely its negotiate_kerberos_auth) doesn't contact anything, but only trusts the admin provided keytab (which is just the service key). This service key decrypts a service ticket provided by the client, and if this ticket is fine, the client is authenticated. But it doesn't imply any communication with some KDC (Heimdal for my case) or DC (Or is Microsft Kerberos implementation working differently ?). This has been tested with a KDC shut off when surfing once the service ticket was retrieved.

The client contacts the KDC from time to time to get a service ticket, but it's far less than for each TCP connection.
performed by the DC itself and contain secrets only the DC knows. The flow of
tokens is exclusively from client to proxy as proof that the client is already
authenticated with the DC. The proxy / server response is intentionally lacking
to starve any MITM of information it might use to reliably affect changes to the
client tokens.
This is the point I don't understand. Could you tell me more ?

Thanks

--
Emmanuel Coirier
Amos Jeffries
2018-10-02 22:11:12 UTC
Permalink
Post by Emmanuel Coirier
Hi Amos and others,
Thanks for your response, but I'm afraid I'm not sure to have understood everything...
de Amos Jeffries
Post by Emmanuel Coirier
When a browser wants to connect to some random HTTP website, it sends a GET
...
to be used.
That is not true. RFC 4559 section 4
(<https://tools.ietf.org/html/rfc4559#section-4>) defines how Negotiate scheme
operates in HTTP.
Ok
Post by Emmanuel Coirier
The problem is that it enables some potentially Man in the Middle
attack (since any malicious proxy where the traffic is diverted could
then answers back without the client knowing it talks to a malicious
proxy)
Quite the opposite. The MITM issue you point out is part of the fundamental
design of the Negotiate scheme and exists for both Squid-3 and Squid-4
behaviours.
Are you telling me that the Negotiate scheme is fundamentally flawed ?
No more so than any other authentication scheme. If we assume there is
an MITM on the channel, it can as easily relay the tokens as see them.
The security in Negotiate comes from making an MITM not be able to know
anything about what secrets they hold, nor use those opaque secrets on
other connections.
Post by Emmanuel Coirier
Having the client use a token provided in-channel from the proxy enables an MITM
observing that channel to inject changes giving itself control over what the
client does in future authentication. This extra control point is prohibited by
I don't understand this point. If we hypothesize that the proxy sends the tokens (including the last one) generated by the service gssapi back to the client (whatever the means), we can think that the gssapi tokens going from service to client are authenticated and encrypted.
Sorry, I was a bit tired when I wrote that and thinking only of the
Negotiate/Kerberos exchange myself.

There *MAY* be tokens going from proxy to client (eg in Negotiate/NTLM),
but they do so by extending the handshake with extra challenge messages
containing *-Authenticate headers. Not via *-Authentication-Info.
Post by Emmanuel Coirier
With Kerberos, it is possible since the client previously sends a service ticket which contains a random session key encrypted with the service key.
The client sends such ticket to the proxy, the proxy sends it to the DC.
The DC tells the proxy ("OK allow" or "no deny"). The HTTP part of
Kerberos is over with the client delivering its token to the proxy.

This lack of to-and-fro with tokens in messages is what makes
Negotiate/Kerberos significantly faster than either NTLM or Negotiate/NTLM.
Post by Emmanuel Coirier
The service can use this session key to authentify its response to the client.
Responses are messages. Negotiate does not authenticate any message.

The *TCP connection* is what is being authenticated by Negotiate.

A hypothetical MITM is free to alter any message contents within that
connection, so long as it can replay the Negotiate ticket which gives it
permission to open a TCP connection to the proxy.
Post by Emmanuel Coirier
So it is harder for a MITM to fake this last gssapi token, especially if the client wait for it. So I don't see how a MITM could exploit this last token.
What I have been trying to say is that there is no such "last GSSAPI
token" in Kerberos. You are thinking of Negotiate/NTLM contexts here.

And *-Info is informational extra data, not part of the authentication
exchange process.
Post by Emmanuel Coirier
Clearly, the http body response could be altered, but it can also be altered in the current situation.
As can tokens in the HTTP headers. With the same 'but'.
Post by Emmanuel Coirier
Or, is it right that a client cannot trust any form of service authentication based on Negotiate since it is fundamentally flawed ? And thus this last token has no use ?
All the tokens in Negotiate and context exchange is done with
*-Authenticate headers. Not *-Authentication-Info.

There *may* be context exchange that uses reply headers depending on the
specific context. Or there may not be. That has not changed in Squid AFAIK.
Post by Emmanuel Coirier
the Squid-4 behaviour, and has never been a formal part of Negotiate scheme as
you can see from the RFC 4559 texts.
The design of Negotiate/Kerberos has both client and proxy independently contact
the DC to respectively generate and verify the tokens. All token operations are
In my undestanding and experiments of Kerberos, the service (here Squid and more precisely its negotiate_kerberos_auth) doesn't contact anything, but only trusts the admin provided keytab (which is just the service key). This service key decrypts a service ticket provided by the client, and if this ticket is fine, the client is authenticated. But it doesn't imply any communication with some KDC (Heimdal for my case) or DC (Or is Microsft Kerberos implementation working differently ?). This has been tested with a KDC shut off when surfing once the service ticket was retrieved.
If that were the case there would be no need for auth_param helpers to
be configured. Squid passes tokens to the helper which handles all
contact and validation with the DC.

Once a token is validated it becomes *the* token for that TCP
connection. Any change to the token terminates that connection.
Post by Emmanuel Coirier
The client contacts the KDC from time to time to get a service ticket, but it's far less than for each TCP connection.
That is up to the client and DC settings. So far as Squid is concerned
every new TCP connection needs to be separately authenticated.
The helper informs the proxy what user label to log for that token/ticket.
Post by Emmanuel Coirier
performed by the DC itself and contain secrets only the DC knows. The flow of
tokens is exclusively from client to proxy as proof that the client is already
authenticated with the DC. The proxy / server response is intentionally lacking
to starve any MITM of information it might use to reliably affect changes to the
client tokens.
This is the point I don't understand. Could you tell me more ?
That is about the best description I can come up with right now. It is
basic PKI key exchange, using separate side channels to the DC instead
of in-channel (HTTP) key/ticket negotiation.


Amos
Emmanuel Coirier
2018-10-03 14:18:51 UTC
Permalink
Hi Amos !

First thank you for your patience and your answers.

I agree on the Negotiate/Kerberos workflow, and its security implication. But I have some
last extra questioning about Kerberos.
Post by Amos Jeffries
[Client sends a Kerberos Service Ticket to Squid]
The client sends such ticket to the proxy, the proxy sends it to the DC.
What do you consider as a DC ?

For me and until now, DC meant "Domain Controller", or "Distribution Center"
(shorthand for KDC, Kerberos' Key Distribution Center): the trusted third party
process that can be either MIT/Heimdal Kerberos or MS Active Directory.
Am I right ?
Post by Amos Jeffries
The DC tells the proxy ("OK allow" or "no deny").
I didn't observed any communication between the negotiate_kerberos_auth helper
and the DC. The only form of communication is reading requests on the standard input
(from squid), reading files like a keytab (the service key) and the krb5.cfg
Kerberos Realm configuration file, and writing responses on the standard output (to Squid).
No connect() system call, no socket, no IPC to some DC. (And users where correctly
authenticated, of course)

And it should be fine since the Kerberos protocol doesn't define any communication
between a service and a DC.

Yesterday I tried a Debian Firefox with a Heimdal Kerberos server. This morning,
I've tried to use a Firefox on Windows 10, with an account provided from a
MS Windows Terminal Server 2008 running an Active Directory.

In either case Firefox was configured to pass trough a Squid instance.

This Squid instance was configured like this in the two scenarii :

auth_param negotiate program /usr/lib/squid/negotiate_kerberos_auth -s GSS_C_NO_NAME
auth_param negotiate children 1
auth_param negotiate keep_alive on

acl foobar proxy_auth REQUIRED
http_access allow foobar

The keytab was configured via the KRB5_KTNAME environment variable like this :

(in the /etc/default/squid file)
export KRB5_KTNAME=FILE:/etc/squid/HTTP.keytab

This keytab was generated using the Samba "net ads keytab" command for Active Directory,
and with "kadmin ext" for Heimdal.
Post by Amos Jeffries
If that were the case there would be no need for auth_param helpers to be
configured. Squid passes tokens to the helper which handles all contact and
validation with the DC.
My point is that the Kerberos helper doesn't need any contact with the DC. All it
has to know is the service key stored in the keytab file. So each new TCP connection
does not create a contact to the DC. It is what I have observed, but perhaps I'm
missing something.

So I need to be sure of this detail because we need to reduce the authentication
load of some Active Directories (which previously used NTLM), and Kerberos seems
to be the solution. By telling me that the helper contacts the DC on each TCP
connection with Kerberos, you puzzles me...


--
Emmanuel Coirier
Amos Jeffries
2018-10-03 17:37:26 UTC
Permalink
Post by Emmanuel Coirier
Hi Amos !
First thank you for your patience and your answers.
I agree on the Negotiate/Kerberos workflow, and its security implication. But I have some
last extra questioning about Kerberos.
Post by Amos Jeffries
[Client sends a Kerberos Service Ticket to Squid]
The client sends such ticket to the proxy, the proxy sends it to the DC.
What do you consider as a DC ?
For me and until now, DC meant "Domain Controller", or "Distribution Center"
(shorthand for KDC, Kerberos' Key Distribution Center): the trusted third party
process that can be either MIT/Heimdal Kerberos or MS Active Directory.
Am I right ?
That matches my understanding.
Post by Emmanuel Coirier
Post by Amos Jeffries
The DC tells the proxy ("OK allow" or "no deny").
I didn't observed any communication between the negotiate_kerberos_auth helper
and the DC. The only form of communication is reading requests on the standard input
(from squid), reading files like a keytab (the service key) and the krb5.cfg
Kerberos Realm configuration file, and writing responses on the standard output (to Squid).
No connect() system call, no socket, no IPC to some DC. (And users where correctly
authenticated, of course)
And it should be fine since the Kerberos protocol doesn't define any communication
between a service and a DC.
I can only hazard a guess that you were not looking at the right place
or time for the server<->DC communication.

Maybe something to do with your "-s GSS_C_NO_NAME" setting.
Post by Emmanuel Coirier
Yesterday I tried a Debian Firefox with a Heimdal Kerberos server. This morning,
I've tried to use a Firefox on Windows 10, with an account provided from a
MS Windows Terminal Server 2008 running an Active Directory.
In either case Firefox was configured to pass trough a Squid instance.
auth_param negotiate program /usr/lib/squid/negotiate_kerberos_auth -s GSS_C_NO_NAME
auth_param negotiate children 1
auth_param negotiate keep_alive on
acl foobar proxy_auth REQUIRED
http_access allow foobar
(in the /etc/default/squid file)
export KRB5_KTNAME=FILE:/etc/squid/HTTP.keytab
This keytab was generated using the Samba "net ads keytab" command for Active Directory,
and with "kadmin ext" for Heimdal.
Post by Amos Jeffries
If that were the case there would be no need for auth_param helpers to be
configured. Squid passes tokens to the helper which handles all contact and
validation with the DC.
My point is that the Kerberos helper doesn't need any contact with the DC. All it
has to know is the service key stored in the keytab file. So each new TCP connection
does not create a contact to the DC. It is what I have observed, but perhaps I'm
missing something.
AFAIK the keytab file tells Squid helper what credentials *it* is to use
to login to the DC.


Consider, how is the Squid helper to know that a secret token delivered
by a random unknown client was actually generated by the DC and
validating that clients identity?
Post by Emmanuel Coirier
So I need to be sure of this detail because we need to reduce the authentication
load of some Active Directories (which previously used NTLM), and Kerberos seems
to be the solution. By telling me that the helper contacts the DC on each TCP
connection with Kerberos, you puzzles me...
I have not said that exactly. I have said that every connection needs to
be *authenticated*. There are optimizations built into kerberos that
*may* be used to minimize and/or speedup DC contact unless it is
specifically needed.


I also see another source of confusion there.


NTLM requires 2 HTTP messages to complete the handshake. Kerberos only
uses 1. This is achieved by removing the initial type-1 and type-2 token
exchange out of HTTP and into side-channels defined by the keytab's and
their related info (eg DNS).

By not causing HTTP requests to be repeated as much and waited for this
change alone produces a massive speed increase (seconds vs milliseconds
of latency) and load reduction (helpers not stuck waiting for next HTTP
message to arrive with more tokens for their incomplete auth process).

Notice that these are details at the HTTP level of things. Optimizations
at the level of things like contact with the DC are minuscule compared
to the above benefits.


Amos

Loading...