I'm developing a Java application that performs authentication with JAAS, should work as follows: (i) when the ticket for user uclient
is already in local cache it should authenticate the user without asking credentials, (ii) when no ticket for 'uclient' is in cache it should ask for username/password and save the acquired ticket into the local cache.
My application is able to perform 'i' but is not able to perform 'ii', it authenticates correctly the user (creates the Subject/Principal) but it doesn't persist the Krb ticket into the cache.
Questions
- How do I achieve/implement this?
- And.. is this going to create the Kerberos cache file when empty/nonexistent? - How do I realize the cache file creation/initialization programmatically from Java?
- And.. just for curiosity, is the Java JaaS able to manage the linux KEYRINGs ? (At the moment Jaas was not able to automatically manage them)
- Is Java JaaS only able to manage/persist tickets for the Default principal in the cache? - Or how do I manage with JaaS a situation where I have tickets for a lot of principals in a single cache file?
Please note that my application has to work in Windows AD and Linux Realms both.
More datails on my enviroment and my current code
I'm testing the client app in a Linux Kerberos Realm configured with FreeIPA client and server. I have a Linux VM that provides the KDC for a realm AUTHDEMO.IT , and a linux VM that is endorsed into the AUTHDEMO.IT realm. krb5.conf
configuration:
includedir /var/lib/sss/pubconf/krb5.include.d/
[libdefaults]
default_realm = AUTHDEMO.IT
dns_lookup_realm = true
dns_lookup_kdc = true
rdns = false
ticket_lifetime = 24h
forwardable = true
udp_preference_limit = 0
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
AUTHDEMO.IT = {
pkinit_anchors = FILE:/etc/ipa/ca.crt
}
[domain_realm]
.authdemo.it = AUTHDEMO.IT
authdemo.it = AUTHDEMO.IT
This is jaas.conf
for the application:
JaasDemo {
com.sun.security.auth.module.Krb5LoginModule required
useTicketCache=true
principal=uclient
debug=true;
};
I have not specified the default cache file name, I have verified in debug that it defaults to: /tmp/krb5cc_1000
where 1000 is the uid of running user.
In the JaasDemo class instance I perform authentication with this login
method:
public LoginContext login(){
LoginContext lc = null;
try {
System.out.println("Initialize logincontext");
lc = new LoginContext("JaasLogin",
new TextCallbackHandler());
} catch (LoginException | SecurityException le) {
System.err.println("Cannot create LoginContext."
+ le.getMessage());
return lc;
}
try {
// attempt authentication
System.out.println("Attempt login");
lc.login();
} catch (LoginException le) {
System.err.println("Authentication failed:");
System.err.println(" " + le.getMessage());
return lc;
}
System.out.println("Authentication succeeded!");
return lc;
}
I have executed my application with this command (note the options for verbose kerberos logging):
java -Dsun.security.krb5.debug=true -Dsun.security.jgss.debug=true -Djava.security.auth.login.config=jaas.conf -jar myapp.jar
Here follows the output of the application in different cases, please note that when asked, the user interactively provides the right credentials.
First case nonexistent /tmp/krb5cc_1000
file:
Initialize logincontext
Attempt login
Debug is true storeKey false useTicketCache true useKeyTab false doNotPrompt false ticketCache is null isInitiator true KeyTab is null refreshKrb5Config is false principal is uclient tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Acquire TGT from Cache
>>>KinitOptions cache name is /tmp/krb5cc_1000
Principal is [email protected]
null credentials from Ticket Cache
**Login Handler invoked, providing username and password to login manager..**
[Krb5LoginModule] user entered username: uclient
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23.
>>> KrbAsReq creating message
getKDCFromDNS using UDP
>>> KrbKdcReq send: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000, number of retries =3, #bytes=143
>>> KDCCommunication: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000,Attempt =1, #bytes=143
>>> KrbKdcReq send: #bytes read=283
>>>Pre-Authentication Data:
PA-DATA type = 136
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 18, salt = REMOVED 3@, s2kparams = null
PA-ETYPE-INFO2 etype = 17, salt = REMOVED, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 133
>>> KdcAccessibility: remove authdemo2.authdemo.it.:88
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
cTime is Wed Jun 29 17:12:49 CEST 1988 583600369000
sTime is Wed Aug 02 15:53:28 CEST 2017 1501682008000
suSec is 981130
error code is 25
error Message is Additional pre-authentication required
cname is [email protected]
sname is krbtgt/[email protected]
eData provided.
msgType is 30
>>>Pre-Authentication Data:
PA-DATA type = 136
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 18, salt = REMOVED 3@, s2kparams = null
PA-ETYPE-INFO2 etype = 17, salt = REMOVED, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 133
KRBError received: NEEDED_PREAUTH
KrbAsReqBuilder: PREAUTH FAILED/REQ, re-send AS-REQ
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23.
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23.
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsReq creating message
getKDCFromDNS using UDP
>>> KrbKdcReq send: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000, number of retries =3, #bytes=225
>>> KDCCommunication: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000,Attempt =1, #bytes=225
>>> KrbKdcReq send: #bytes read=674
>>> KdcAccessibility: remove authdemo2.authdemo.it.:88
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
>>> KrbAsRep cons in KrbAsReq.getReply uclient
principal is [email protected]
Commit Succeeded
Authentication succeeded!
Subject.toString:
Principal: [email protected]
Private Credential: Ticket (hex) =
REMOVED TICKET DETAILS K.
Client Principal = [email protected]
Server Principal = krbtgt/[email protected]
Session Key = EncryptionKey: keyType=18 keyBytes (hex dump)=
REMOVED
Forwardable Ticket true
Forwarded Ticket false
Proxiable Ticket false
Proxy Ticket false
Postdated Ticket false
Renewable Ticket false
Initial Ticket false
Auth Time = Wed Aug 02 15:53:28 CEST 2017
Start Time = Wed Aug 02 15:53:28 CEST 2017
End Time = Thu Aug 03 15:53:28 CEST 2017
Renew Till = null
Client Addresses Null
Second case /tmp/krb5cc_1000
file exists that contains ticket for another user (created with kinit -c); the application correctly authenticates, but the acquired ticket is not persisted to the cache file.
klist status first than application execution:
klist -c /tmp/krb5cc_1000
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: [email protected]
Valid starting Expires Service principal
08/02/2017 16:05:19 08/03/2017 16:05:13 krbtgt/[email protected]
Output of application:
Initialize logincontext
Attempt login
Debug is true storeKey false useTicketCache true useKeyTab false doNotPrompt false ticketCache is null isInitiator true KeyTab is null refreshKrb5Config is false principal is uclient tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Acquire TGT from Cache
>>>KinitOptions cache name is /tmp/krb5cc_1000
java.io.IOException: Primary principals don't match.
at sun.security.krb5.internal.ccache.FileCredentialsCache.load(FileCredentialsCache.java:179)
at sun.security.krb5.internal.ccache.FileCredentialsCache.acquireInstance(FileCredentialsCache.java:82)
at sun.security.krb5.internal.ccache.CredentialsCache.getInstance(CredentialsCache.java:83)
at sun.security.krb5.Credentials.acquireTGTFromCache(Credentials.java:333)
at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:665)
at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:617)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:755)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:195)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:682)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:680)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokePriv(LoginContext.java:680)
at javax.security.auth.login.LoginContext.login(LoginContext.java:587)
at it.kerberosdemo.login.JaasDemo.login(JaasDemo.java:45)
at it.kerberosdemo.login.JaasDemo.login(JaasDemo.java:27)
at it.male.kerberosdemo.client.ClientMain.main(ClientMain.java:29)
Principal is [email protected]
null credentials from Ticket Cache
Login Handler invokerd, providing username and password to login manager..
[Krb5LoginModule] user entered username: uclient
Using builtin default etypes for default_tkt_enctypes
default etypes for default_tkt_enctypes: 18 17 16 23.
>>> KrbAsReq creating message
getKDCFromDNS using UDP
>>> KrbKdcReq send: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000, number of retries =3, #bytes=143
>>> KDCCommunication: kdc=authdemo2.authdemo.it. UDP:88, timeout=30000,Attempt =1, #bytes=143
>>> KrbKdcReq send: #bytes read=283
>>>Pre-Authentication Data:
PA-DATA type = 136
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 18, salt = REMOVED, s2kparams = null
PA-ETYPE-INFO2 etype = 17, salt = REMOVED, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 133
>>> KdcAccessibility: remove authdemo2.authdemo.it.:88
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
cTime is Mon Sep 22 16:38:56 CEST 2031 1947854336000
sTime is Wed Aug 02 16:07:05 CEST 2017 1501682825000
suSec is 803283
error code is 25
error Message is Additional pre-authentication required
cname is [email protected]
sname is krbtgt/[email protected]
eData provided.
msgType is 30
>>>Pre-Authentication Data:
PA-DATA type = 136
...OMITTED IDENTICAL
klist confirms that no ticket is added for 'uclient' into the cache file.
Regards