hc-httpclient-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Sebastiaan van Erk <sebs...@sebster.com>
Subject Re: Kerberos proxy authentication issue
Date Wed, 30 Dec 2009 13:00:34 GMT
Oleg Kalnichevski wrote:
> Sebastiaan van Erk wrote:
>> Hi Oleg,
>>
>> Oleg Kalnichevski wrote:
>>> Sebastiaan van Erk wrote:
>>>
>>>> Hi Oleg,
>>>>
>>>> Thanks for adding this. I'm currently a bit stuck on the caching of 
>>>> the tickets, which really needs to be fixed, but it looks like I'll 
>>>> have to dive into protocols/APIs deeply (JAAS, Java GSSAPI, SPNEGO) 
>>>> to figure it out.
>>>>
>>>> There is however one thing about the SPENGO authentication protocol 
>>>> that does not yet fit nicely into the httpclient API, namely, if you 
>>>> look at the diagram:
>>>>
>>>> http://issues.apache.org/jira/secure/attachment/12419383/SPNEGO_cropped.png

>>>>
>>>>
>>>> What you see is a series of requests back and forth, with the last 
>>>> response containing the final negotiation token NOT having a 
>>>> response code of 401. In the diagram it's a 200, but in my case 
>>>> (with the redirect), it was a 3xx. In any case, the token should go 
>>>> back into the authentication scheme.
>>>>
>>>
>>> This is somewhat similar to NTLM authentication, where the final 
>>> response code may also be 3xx, so HttpClient should be able to 
>>> handler such situations, worst case with some API extensions.
>>>
>>>
>>>> BTW, I quickly hacked a response inteceptor which would do this for 
>>>> me (it was a hack, because it just called authenticate() again with 
>>>> the token and a null HttpRequest (since you don't have a request in 
>>>> the response interceptor) and at least GSSAPI was able to complete 
>>>> the negotiation. I did this because I was hoping this would solve 
>>>> the ticket caching problem, i.e, hoping that the tickets would be 
>>>> "committed" if the negotiation completed, but unfortunately this was 
>>>> not the case.
>>>>
>>>> I don't really know the authentication API should look to support 
>>>> this protocol... and it might in the end not really be necessary, 
>>>> except perhaps for mutual authentication.
>>>>
>>>> I'm going to look into the SPNEGO/GSSAPI stuff now to fix the 
>>>> caching, so I'm still on this. I just wanted to keep you posted.
>>>>
>>>> Regards,
>>>> Sebastiaan
>>>
>>> Keep us posted on the progress.
>>
>> I working code including caching, but there is still a bunch of 
>> smaller issues which are still up in the air. I can post the code I've 
>> got so far, should I make a new JIRA issue for this, or add it to 523?
>>
>> The current status is:
>>
>> - Both http auth and proxy auth work.
>>
>> - The mutual auth works now, with a response interceptor specific for 
>> the Negotiate scheme. Perhaps this could somehow be integrated into an 
>> API change (in NegotiateScheme I added a 
>> "completeAuthentication(HttpContext) method which is called from the 
>> response interceptor after it calls processToken with the final token).
>>
>> - I can get the caching to work now, turns out this is weirdness in 
>> the JDK kerberos implementation. You can do 1 of 2 things:
>>     a) set useSubjectCredentialsOnly to false. In this case the ticket 
>> cache of the OS can be used to get the TGT, but service tickets are 
>> not read from here and NOTHING is cached. Terrible performance, but no 
>> double login required (if a TGT is already present, you don't have to 
>> type your password).
>>     b) set useSubjectCredentialsOnly to true. In this case you must 
>> sign on yourself with JAAS and a LoginContext and explicitly execute 
>> the http client methods in a Subject.doAsPriveleged block, which is 
>> annoying. You have to provide credentials since Java won't look in the 
>> ticket cache, so you need to enter the credentials to get a TGT. 
>> Fortunately it DOES cache credentials with the Subject. Since it's 
>> single sign on (once in the application combined with 
>> Subject.doAsPrivileged calls), the credentials used by the AuthScheme 
>> are dummy credentials (by that time, you're already logged in).
>> I can't do anything about any of this, it's the way the Java Kerberos 
>> implementation works, so the only thing I can think of is adding an 
>> example using JAAS and Subject.doAsPrivileged and explain the 
>> benefits/problems with each approach in the example comments.
>>
>> - I haven't looked into the preemptive stuff yet.
>>
>> - The negotatiate scheme is connection based according to the docs I 
>> can find online, but my mod_auth_krb in apache doesn't seem to agree, 
>> so with each request I get another 401 and another negotiate cycle. 
>> HttpClient correctly does not try to reauthenticate, so I think this 
>> is the module's fault. Anyway, http client does reauthenticate after 
>> the 401, so it works.
>>
>> Regards,
>> Sebastiaan
>>
> 
> Hi Sebastiaan
> 
> I am glad you have been making progress with fixing SPNEGO issues. I 
> would like to suggest the following way forward. Submit your changes in 
> a series of relatively small, incremental patches. Start with more 
> important issues first. I'll be reviewing those patches and trying to 
> find ways to adapt the existing API to the peculiarities of Kerberos 
> authentication. Please also consider investing some time into 
> contributing additional content for the Kerberos related sections of the 
> HttpClient tutorial, especially to help deal with JDK implementation 
> weirdness and explain various trade-offs. Feel free to re-use the same 
> JIRA ticket or open new ones as you see fit.
> 
> Cheers
> 
> Oleg

Hi Oleg,

I'm still working on this, just been on a bit of a break during 
Christmas, just so you know.

I have a question on the effects of the following method:

     /**
      * Tests if the authentication scheme is provides authorization on 
a per
      * connection basis instead of usual per request basis
      *
      * @return <tt>true</tt> if the scheme is connection based, 
<tt>false</tt>
      * if the scheme is request based.
      */
     boolean isConnectionBased();

According to the currently submitted implementation, the negotiate 
scheme is connection based and returns true here. However, in my tests 
with apache + mod_auth_krb, it seems that mod_auth_krb does request 
based authorization. My Squid proxy seems to do connection based auth.

The problem I have is that if I want to do preemptive auth then I don't 
know how to do it on a request basis if isConnectionBased() returns 
true, because http client doesn't try to authenticate the second request 
on a connection in this case (understandably).

Theoretically returning false only hurts performance, and will allow 
preemptive auth for mod_auth_krb to work.

Returning true breaks non-restartable-requests (streamed post entities 
for example), because even if you authenticate the connection with a 
HEAD request or something like that, no preemptive auth is done on the 
streaming request.

Maybe I'm just implementing preemptive auth wrong... What I do is add 
the following interceptors to the http client instance:

	private static class PreemptiveAuth implements HttpRequestInterceptor {

		public void process(final HttpRequest request, final HttpContext 
context) throws org.apache.http.HttpException, IOException {
			final AuthState httpAuthState = (AuthState) 
context.getAttribute(ClientContext.TARGET_AUTH_STATE);
			if (httpAuthState.getAuthScheme() == null) {
				final AuthScheme authScheme = (AuthScheme) 
context.getAttribute("http-preemptive-auth");
				final CredentialsProvider credsProvider = (CredentialsProvider) 
context.getAttribute(ClientContext.CREDS_PROVIDER);
				final HttpHost targetHost = (HttpHost) 
context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
				if (authScheme != null) {
					final Credentials creds = credsProvider.getCredentials(new 
AuthScope(targetHost.getHostName(), targetHost.getPort()));
					if (creds != null) {
						httpAuthState.setAuthScheme(authScheme);
						httpAuthState.setCredentials(creds);
					}
				}
			}
		}
	}


	private static class PersistentAuth implements HttpResponseInterceptor {

		public void process(final HttpResponse response, final HttpContext 
context) throws org.apache.http.HttpException, IOException {
			final AuthState httpAuthState = (AuthState) 
context.getAttribute(ClientContext.TARGET_AUTH_STATE);
			if (httpAuthState != null) {
				final AuthScheme authScheme = httpAuthState.getAuthScheme();
				context.setAttribute("http-preemptive-auth", authScheme);
			}
		}
	}

So my questions basically are:

1) What to do about the fact that not all implementations seem to be 
connection based?
2) Is there a way to send preemptive auth per request even if the scheme 
says it's connection based?

Regards,
Sebastiaan

Mime
View raw message