I apologise if there's something really straight forward I'm getting wrong with this - I've been finding it really hard to find much information about GSSAPI and JAAS.
I'm writing a program that makes a request to an IIS server and authenticates using Kerberos.
I have two VMs: the first is an Active Directory domain controller. The other other is running IIS server.
From my machine, I'm running a program that uses GSSAPI and JAAS to authenticate. It's pretty heavily based on the Oracle tutorials for GSSAPI, since, at this stage, I'm just sort of trying to get my head around it. It logs in using the KrbLoginModule to create a subject based on some given test user from the domain, which all appears to be working fine. In the network trace, there's an AS-REQ, AS-REP, TGS-REQ and TGS-REP (which, if I'm correct, is as it should be) between my machine and the domain controller VM.
My program then uses the subject generated above to run some GSSAPI code that's mostly based on the code from the GSSAPI/JAAS tutorial. It creates the names/credential/context without any errors, but when it tries to establish the context, things start to go wrong.
I was originally doing the context loop how it's done in the tutorial (linked above), but when it tried to start reading the token from the server (i.e. token = new byte[inStream.readInt()];
) the length
int it read was huge (1213486160, the few times I check it - it seemed like it was that every time). This caused an OutOfMemoryError.
I then started using the "Stream version" of the initSecContext which takes an input and output stream (I gave the in/out streams of the socket I had open to my server - I assume that's what it wants). This seems to work pretty well at first. It establishes over one iteration, but when you try to wrap/unwrap messages, things start to go wrong again. When you try to unwrap a message, it throws an error:
org.ietf.jgss.GSSException, major code: 10, minor code: 0
major string: Defective token
minor string: Bad token tag: 7284
The network trace for this shows a TCP message with the same length as the token generated (I'm assuming that it would be the wrapped token being sent). Then there's an ICMP Redirect. I'm not sure if this is nothing, but I thought I'd mention it (I've noticed it contains almost the entirety of the first TCP message).
Then there's an HTTP response. It's a 400 Bad Request. In the body of the response it says that it's an Invalid Verb (I'm assuming when it says verb it's meaning like "GET" or "POST etc. Is this the case?). The trace also notes that it's an acknowledgement to the first TCP message. With a bit of googling, I found a guy who had a similar problem. It turned out that it was about an antivirus messing with the headers of the request that was being sent (maybe it's related to the redirect?).
There's then a few more TCP messages (three in a row) from my machine (they contain the data I'm sending in the message with the wrap method) followed by a length=0 TCP acknowledgement from the server.
So I'm not sure what it could be (maybe it's the redirect, but that doesn't seem likely).
UPDATE:
I'm running it on the another VM (on the same Active Directory domain) but it's still getting a HTTP 400 Bad Request response. Unlike before, it's now definitely using Kerberos (above, it was sending NTLM requests).
The network trace shows the AS and TGS request and responses, then the ticket being sent. The IIS server then throws back the 400 Bad Request (again, invalid verb). There is a subtle difference: the Bad Request response is no longer in acknowledgement to the token being sent, though there is an empty ack TCP message which comes just before the 400.
The program still thinks that its context is established, but if you try to wrap a message, it breaks with:
General failure, unspecified at GSSAPI level
minor string: Error in method wrap, error: java.net.SocketException:
Unrecognized Windows Sockets error: 0: socket write error
UPDATE:
As mentioned earlier, when I read in the token with initSecContext, I'm expecting a length first (i.e. token = new byte[inStream.readInt()];
) based on the tutorial. What I'm getting is 1213486160 which caused an OutOfMemoryError. I realised, though, that 1213486160 is 0x48545450 which, in ASCII, is "HTTP". So I read the next bunch of characters and the "response token" is the HTTP 400 Bad Request.
This makes me question whether I'm supposed to read an integer for the length as shown in the tutorial. I feel like this question is somewhat different, so I've made a new question here. That might not be the issue though (like, not what's causing it to send "Bad Request" in the first place), so I'll keep this question open for now.
ANOTHER UPDATE:
So I looked at the token that's getting spit out by initSecContext
and encoded to Base64, it's YII... (i.e. a Negotiate token).
I've also had a closer look at the network trace when I log in through chrome and after the AS and TGS messages, it sends a HTTP GET request with a header line of Authorization: Negotiate YII....., as opposed to my program which sends a TCP message with the length of the token, then the token.
This suggest to me that I don't need to do the establish context step (at least, as it's shown in the tutorial), or that I need to grab the token and send it in a HTTP request rather than a straight TCP message. Is there anyone (or any examples of people - I can't seem to find any really) who has used GSSAPI/Kerberos to authenticate for IIS who can confirm this?
Ensure the client machine is joined to the same Active Directory domain as the IIS server in order for Kerberos authentication to work. "Defective token" generally means the client machine sent an NTLM authentication token while the server wanted a Kerberos one. There are reasons why this undesired behavior occurs.