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

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to