The impact of isInitiator on JAAS login configuration and the role of SPN

Disclaimer: This post serves a learning note to myself. It’s very possible you will find it boring :) .

During the process to run JGSS tutorial (see my previous topic), I came to realize that when keytab is used, isInitiator=false has to be present in the JAAS login configuration file. Since the tutorial never mentioned to use it, I was curious about whether there were ways to achieve the same results for the server without using the parameter (which would mean isInitiator=true automatically). Then I was drawn to devise several testing cases to see how things morphed cross each stage. The whole procecss led me to understand the Kerberos protocol and JAAS programming better.

Case One

Starting with the configuration file from my previous topic where the JGSS samples eventually worked successfully, the line of isInitiator was simply commented out (and added semicolon to the previous line to confirm with the configuration format for the Login Utility), then I got the following:

server {
	com.sun.security.auth.module.Krb5LoginModule required
	useKeyTab=true
	storeKey=true
	keyTab=my.keytab
	principal="HTTP/mylaptop.corpnet.mycorp.com";
//	isInitiator=false;
};

The testing of running ‘Jaas server’ generated these exceptions:

principal's key obtained from the keytab
Acquire TGT using AS Exchange
                [Krb5LoginModule] authentication failed
Client not found in Kerberos database (6)
Login failed
javax.security.auth.login.LoginException: Client not found in Kerberos database (6)
        at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Unknown Source)
        at com.sun.security.auth.module.Krb5LoginModule.login(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at javax.security.auth.login.LoginContext.invoke(Unknown Source)
        at javax.security.auth.login.LoginContext.access$000(Unknown Source)
        at javax.security.auth.login.LoginContext$4.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.login.LoginContext.invokePriv(Unknown Source)
        at javax.security.auth.login.LoginContext.login(Unknown Source)
        at Jaas.loginAndAction(Jaas.java:97)
        at Jaas.main(Jaas.java:76)
Caused by: KrbException: Client not found in Kerberos database (6)
        at sun.security.krb5.KrbAsRep.(Unknown Source)
        at sun.security.krb5.KrbAsReq.getReply(Unknown Source)
        at sun.security.krb5.Credentials.sendASRequest(Unknown Source)
        at sun.security.krb5.Credentials.acquireTGT(Unknown Source)
        ... 14 more
Caused by: KrbException: Identifier doesn't match expected value (906)
        at sun.security.krb5.internal.KDCRep.init(Unknown Source)
        at sun.security.krb5.internal.ASRep.init(Unknown Source)
        at sun.security.krb5.internal.ASRep.(Unknown Source)
        ... 18 more

Even the testing did not get through, it still revealed something:
1. Acquiring TGT is mandatory when isInitiator = true.
2. The key for the principal was found from the keytab file (we knew it is there). However, Kerberos database did not recognize the principal itself.

Case Two

In order to provide a principal that KDC knows, I had to change the principal setting as following:

server {
	com.sun.security.auth.module.Krb5LoginModule required
	useKeyTab=true
	storeKey=true
	keyTab=my.keytab
	principal="myserviceaccount";
//	isInitiator=false;
};

The partial results of testing both Jaas Server and GssServer are shown below:

Key for the principal myserviceaccount@CORPNET.MYCORP.COM not available in my.keytab
Kerberos password for myserviceaccount:
                [Krb5LoginModule] user entered username: myserviceaccount

Acquire TGT using AS Exchange
principal is myserviceaccount@CORPNET.MYCORP.COM
EncryptionKey: keyType=23 keyBytes (hex dump)=0000: 32 EC A0 27 50 47 27 C9 \
  F7 9A A4 E6 EE BF 17 2E  2..'PG'.........

Added server's keyKerberos Principal myserviceaccount@CORPNET.MYCORP.COMKey \
Version 0key EncryptionKey: keyType=23 keyBytes (hex dump)=

0000: 32 EC A0 27 50 47 27 C9   F7 9A A4 E6 EE BF 17 2E  2..'PG'.........

                [Krb5LoginModule] added Krb5Principal  myserviceaccount@CORPNET.MYCORP.COM to Subject
Commit Succeeded

Subject:
        Principal: myserviceaccount@CORPNET.MYCORP.COM
        Private Credential: Ticket (hex) = ... ...

In the testing the key for the given principal (myserviceaccount) could not be found from the keytab file, then I was asked to provide the password for it. Apparently, Kerberos database recognized it in this case (at least we passed the issue faced last time). Once the correct password was provided, TGT was successfully obtained from AS service. From the testing, it also confirmed what we have known from case one:
- When isInitiator = false is set, acquring TGT for the principal (service principal in this case) is mandatory.
- The Krb5LoginModule would locate the principal from the configuration file and then try to obtain its key from the keytab file when keytab is used. It just happened in this case the key was not available from the provided keytab file.

Since I changed the principal in this case, it would be interesting to see how the client and server interacted with the new principal. The following testing commands from my previous topic were used here and all the later testings:

// Commands for GssServer testing
java -Djava.security.auth.login.config=jaas-krb5.conf GssServer
java -Djava.security.auth.login.config=jaas-krb5.conf GssClient HTTP MYLAPTOP

The output from the server end (of the GssServer example) regarding the principal used was:

Waiting for incoming connection...
Got connection from client /10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is myserviceaccount@CORPNET.MYCORP.COM
Mutual authentication took place!
Received data "Hello There!" of length 12

From the client end (of the GssClient example), we have

Connected to address MYLAPYOP/10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is HTTP@MYLAPTOP
Mutual authentication took place!
Sending message: Hello There!

It’s interesting to note the difference for the server principal identified from the server and client end, while the client principal matched from both.

In this case, I had to provide password for myserviceaccount. What if a key for the principal is available within the keytab file? Would I be able to avoid being asked for password? These questions lead to the next case.

Case Three

A keytab file was created for the new principal (myserviceaccount) that the KDC aknowledges, then the following configurator was used:

server {
	com.sun.security.auth.module.Krb5LoginModule required
	useKeyTab=true
	storeKey=true
	keyTab=myserviceaccount.keytab
	principal="myserviceaccount";
//	isInitiator=false;
};

The keytab was created using the following line for myserviceaccount:

C:\jdk1.6.0_18\jre\bin\ktab.exe -a myserviceaccont \
<password for myserviceaccount>  -k myserviceaccount.keytab
Done!
Service key for myserviceaccount is saved in myserviceaccount.keytab

The keytab file was verified with the following result:

C:\jdk1.6.0_18\jre\bin\ktab.exe -l -k myserviceaccount.keytab
Keytab name: C:\downloads\Java\JGSS\src\bin\myserviceaccount.keytab
KVNO    Principal
-----------------------------------------
  1     myserviceaccount@CORPNET.MYCORP.COM

With this setup and the new keytab file, guess what? Everything worked perfectly! I was not asked for password this time.

Interestingly it also shows that it is not always true that in order to use keytab file isInitiator = false has to be present, which is totally contradictory to my belief at the beginning when devising these testing cases.

From the server end, the output about the principal used was:

Waiting for incoming connection...
Got connection from client /10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is myserviceaccount@CORPNET.MYCORP.COM
Mutual authentication took place!
Received data "Hello There!" of length 12

From the client end, we have:

Connected to address MYLAPTOP/10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is HTTP@MYLAPTOP
Mutual authentication took place!
Sending message: Hello There!

The output from this case is identical to what we got from case two. It’s understandable, since the only improvement here is to use keytab for myserviceaccount from the server end.

Case Foure

This case is actually the same from the final successful testing in my previous topic. In order to compare the usage of principals, the configurator was copied here:

server {
	com.sun.security.auth.module.Krb5LoginModule required
	useKeyTab=true
	storeKey=true
	keyTab=my.keytab
	principal="HTTP/mylaptop.corpnet.mycorp.com"
	isInitiator=false;
};

Comparing the configuration file here with the one from last case, the only difference is that myserviceaccount was there, whileas the SPN was used here. As shown in case three, the usage of myserviceaccount made the parameter isInitiator unnecessary anymore.

The output related to the display of principal on the server end is:

Waiting for incoming connection...
Got connection from client /10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is HTTP/mylaptop.corpnet.mycorp.com@CORPNET.MYCORP.COM
Mutual authentication took place!
Received data "Hello There!" of length 12

The client end counterpart is:

Connected to address MYLAPTOP/10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is HTTP@MYLAPTOP
Mutual authentication took place!
Sending message: Hello There!

In this case the only significant difference from other cases is that the server principal from the server end is the SPN presented from the configuration file. However from the client end, the server pricipal has always been HTTP@MYLAPTOP.

Overall, from these testing, it concludes that the isInitiator has the following impact on the JAAS configurator:

When isInitiator = false is NOT presented, acquring TGT from KDC will be done for the given principal.
As we know for the service principal, acquring TGT is not actually necessary. Since we would not expect the service principal to ask KDC to grant other service ticket.

keytab file can also be created for user account and acting as a way of SSO (kind of)
As we showed in case three where keytab file is used for a service account, actually a keytab file can also be used for a client. The following configuration section can be used for client to suppress the use of password.

client {
	com.sun.security.auth.module.Krb5LoginModule required
	debug=true
	useKeyTab=true
	keyTab=myserviceaccount.keytab
	principal="myserviceaccount";
};

I have seen many literaturs stated that keytab files should be treated as the same as password. After you have done a testing with above congfiguration, you would understand the statement first-handed.

The usage of keytab file for SPN and isInitiator = false would provide additional security
As case one showed, Kerberos database dose not know any SPN. A keytab created for a SPN would not warrent the possibility to granting TGT for the SPN. With the usage of isInitiator = false, the service principal (SPN in this case) could get away without having the ability to acquire TGT but still be able to decrypte any messages from clients when these mesages were encrypted by the service session key. Suppose somehow the keytab file was stolen, the person who possessed it could not pretend to be the SPN within the keytab to get authenticated from KDC to any services.

So far, it seemed I have got most of my questions anwsered. But something is still lingering in my mind. Given the fact the service account worked well in case three, is there any special purpose to use SPN, or even why bother to introduce SPN from the very beginning?

Why the existence of SPN?

The following was taken from Kerberos Technical Supplements for Windows:

Service Principal Names

An SPN is a unique identifier that applications can use to request a service ticket instead of using the service account name. The Kerberos protocol implementation in Windows uses the SPN to retrieve a valid service account from Active Directory. In other words, an SPN is another type of identifier that can be assigned to an account in Active Directory.

Without the use of an SPN, client applications that request service tickets must know the name of the Windows identity that is used as the service account to request a service ticket.

By using SPNs you do not need to expose account names and you have the ability to implement mutual authentication. In other words, a valid service response provides authentication that the service account associated with the SPN was used to process the request. As a result, when you request a service ticket, the use of SPNs is strongly recommended over the use of service account names.

From the description, the introduction of SPN is a convenient way to avoid using the service account. It sure should not suppress the possibility that a service account can be used by a client to identify a service.

We have seen many examples for a client to use SPN to identify the target service. So far that’s the only way used for the client. The following code exception from JssClient showed the procedure for a client to get to the service:

public static void main(String[] args) throws Exception {
... ...
    String serverPrinc = args[0] + "@" + args[1];
    PrivilegedExceptionAction action = new GssClientAction(serverPrinc, args[1], PORT);
    Jaas.loginAndAction("client", action);
}

static class GssClientAction implements PrivilegedExceptionAction {
... ...
    public Object run() throws Exception {
    ... ...
       GSSManager manager = GSSManager.getInstance();

       /*
        * Create a GSSName out of the server's name.
        */
       GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE);
       ... ...
    }
...
}

Is it possible for a client to identify a service using the service account? Anyway, I have the knowledge about the service account to which the service associates. So here is my next case.

Case Five

Unlike other cases where the variation was done to the JAAS login configuration file, this case is to the client program.

public static void main(String[] args) throws Exception {
... ...
    // String serverPrinc = args[0] + "@" + args[1];
    String serverPrinc = args[0];
    PrivilegedExceptionAction action = new GssClientAction(serverPrinc, args[1], PORT);
    Jaas.loginAndAction("client", action);
}

static class GssClientAction implements PrivilegedExceptionAction {
... ...
    public Object run() throws Exception {
    ... ...
       GSSManager manager = GSSManager.getInstance();

       /*
        * Create a GSSName out of the server's name.
        */
       // GSSName serverName = manager.createName(serverPrinc, GSSName.NT_HOSTBASED_SERVICE);
       GSSName serverName = manager.createName(serverPrinc, GSSName.NT_USER_NAME);
       ... ...
    }
...
}

The first line was changed because I wanted to pass the service account as args[0]. The second change was to replace the name type directive from GSSName.NT_HOSTBASED_SERVICE to GSSName.NT_USER_NAME. It directed GSSManager to create the principal as a user name (i.e. service account), instead of service name and server hostname. With the new changes and the following command from client end:

java -Djava.security.auth.login.config=jaas-krb5.conf GssClient myserviceaccount MYLAPTOP

The client was successfully located the service and authenticated to it by using the service account! The output from client end became:

Connected to address MYLAPTOP/10.xxx.x.xxx
Context Established!
Client principal is mypersonal@CORPNET.MYCORP.COM
Server principal is myserviceaccount
Mutual authentication took place!
Sending message: Hello There!

Check what the client found out for the server principal, it is myserviceaccount!

From this case, it showed the client indeed can locate a service by using the service account. The surprise to me comes because I rarely see a case like this. Microsoft has been quite successful to promote (maybe force) the use of SPN. The interchange of SPN and service account is possible for both client and service in Kerberos authentication.

Tags: , ,

One Response to “The impact of isInitiator on JAAS login configuration and the role of SPN”

  1. Kerberos setup and JAAS configuration for running Sun JGSS tutorial against AD « Documentum DAA Says:

    [...] another WordPress.com weblog « SPNEGO based Single sign-on (SSO) setup for Webtop The impact of isInitiator on JAAS login configuration and the role of SPN [...]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: