I'm working a web appliaction with SSO based on the Windows domain login, for this purpose I have chosen to validate Kerberos tickets. But now I'm facing a problem for which I can't find a solution. I manage to validate a ticket without exceptions, but when I'm trying to get the userName, NullPointerException is thrown, because the username is null and I don't know where is problem.
Why is user name null if I don't get any exception during the validation?
How I get userName:
String clientName = gssContext.getSrcName().toString();
I create my client based on this:
Using GSSManager to validate a Kerberos ticket
How to obtain a kerberos service ticket via GSS-API?
http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/single-signon.html
Update 1:
How I setup content, just copy-paste form here https://stackoverflow.com/a/25450862/1646082:
final Oid spnegoOid = new Oid("1.3.6.1.5.5.2");
GSSManager gssmgr = GSSManager.getInstance();
// tell the GSSManager the Kerberos name of the service
GSSName serviceName = gssmgr.createName(this.servicePrincipal, GSSName.NT_USER_NAME);
// get the service's credentials. note that this run() method was called by Subject.doAs(),
// so the service's credentials (Service Principal Name and password) are already
// available in the Subject
GSSCredential serviceCredentials = gssmgr.createCredential(serviceName,
GSSCredential.INDEFINITE_LIFETIME, spnegoOid, GSSCredential.ACCEPT_ONLY);
// create a security context for decrypting the service ticket
GSSContext gssContext = gssmgr.createContext(serviceCredentials);
// decrypt the service ticket
System.out.println("Entering accpetSecContext...");
System.out.println( new String (Base64.encodeBase64( gssContext.acceptSecContext(this.kerberosTicket, 0,
this.kerberosTicket.length) ) ));
// get the client name from the decrypted service ticket
// note that Active Directory created the service ticket, so we can trust it
String clientName = gssContext.getSrcName().toString();
Update 2:
If I setup spring security based on this https://spring.io/blog/2009/09/28/spring-security-kerberos-spnego-extension i also got the same error:
java.lang.NullPointerException at
org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:136)
at
org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:125)
at java.security.AccessController.doPrivileged(Native Method) at
javax.security.auth.Subject.doAs(Subject.java:422)
private static class KerberosValidateAction implements PrivilegedExceptionAction<String> {
byte[] kerberosTicket;
public KerberosValidateAction(byte[] kerberosTicket) {
this.kerberosTicket = kerberosTicket;
}
#Override
public String run() throws Exception {
GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
String user = context.getSrcName().toString(); // ERROR!
context.dispose();
return user;
}
}
Update 3:
Also tried change Java version from 1.8 to 1.7 as suggested here Domain authentication with Kerberos fails. No result.
Update 4:
First of all. Don't user Java 1.8 b40 and b45, both of them are broken. And don't test it on local PC, it doesn't work(I don't know why).
After changing on newest(b65) Java version, I got exception about encription(Cannot find key of appropriate type to decrypt AP REP - AES256 ...). This I have fixed by Java Cryptography Extension (JCE) for Java 1.8 and re-create keytab with /crypto AES256-SHA1 after all this i got exception:
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Checksum failed) at
sun.security.jgss.krb5.Krb5Context.acceptSecContext(Unknown Source) at
sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) at
sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) at
GssServer$GssServerAction.run(GssServer.java:159)
... 4 more
Caused by: KrbException: Checksum failed at
sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown
Source) at
sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown
Source) at sun.security.krb5.EncryptedData.decrypt(Unknown Source) at
sun.security.krb5.KrbApReq.authenticate(Unknown Source) at
sun.security.krb5.KrbApReq.(Unknown Source) at
sun.security.jgss.krb5.InitSecContextToken.(Unknown Source)
... 8 more
Caused by: java.security.GeneralSecurityException: Checksum
failed at
sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(Unknown
Source) at
sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(Unknown Source)
... 14 more
I tried this tutorial and other way to create keytabfile, but i still don't have solution.
I faced the same Checksum failed error when implementing my GSSAPI socket demo that is a modification of the Oracle GSSAPI tutorial code.
I executed my code on a Linux machine enrolled into a FreeIPA kerberos realm.
I have used the vanilla krb5.conf file of my Linux system. No constraints on ticket etype are there:
...
[libdefaults]
default_realm = AUTHDEMO.IT
dns_lookup_realm = true
dns_lookup_kdc = true
rdns = false
ticket_lifetime = 24h
forwardable = true
udp_preference_limit = 0
...
The FreeIPA realm default is to use type 18 tickets (AES-256).
About my application, it has this policy file configured:
grant CodeBase "file:./app.jar" {
permission java.security.AllPermission;
};
grant CodeBase "file:./app.jar"
Principal javax.security.auth.kerberos.KerberosPrincipal
"servicename#AUTHDEMO.IT" {
permission java.net.SocketPermission "*", "accept";
permission javax.security.auth.kerberos.ServicePermission
"servicename#AUTHDEMO.IT", "accept";
};
When executing the application I got this error on the acceptor side:
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Encryption type AES256CTS mode with HMAC SHA1-96 is not
supported/enabled)
In my case the error arises in the GSS acceptor side. In my application I generate the Jaas configuration programmatically (I'll refer at this as DConfig) and I don't use a config file.
The first solution, I found, is to use config files instead of DConfig and the problem disappeared, it worked fine.
The temporary solution, Jaas Config file:
DemoServer {
com.sun.security.auth.module.Krb5LoginModule required
principal="servicename#AUTHDEMO.IT"
storeKey=true
debug=true; #not mandatory
};
With this configuration, no problem arises on the acceptor side and the application were able to check the service ticket validity and accept the connection.
I asked myself.. WHY?
I checked differences in the Subject(s) acquired with the two configurations. In the working case, with the config file, the subject contains, into the private credentials, both the password hashes credentials and the principal TGT ticket.
With DConfig, I obtain a Subject with only password hashes but there is no principal TGT ticket in private credentials.
My fix
DConfig contains the same settings of the configuration file and the other options are the replica of Krb5LoginModule defaults, at first I cannot see a reason for the misbehaviour.
Setting isInitiator = true, into the acceptor side DConfig, solved the issue. `isInitiator = true has forced the persistence of TGT ticket into the subject.
With this workaround the error has disappeared with no change into the system krb5.conf.
My cent is... after Jaas login, let's check your subject private credentials for lacking creds (You need the service principal TGT into your acceptor side subject!) and in case try to set isInitiator = true to the acceptor side too.
Regards
It seems that the context is not fully established when you are trying to get SrcName. It seems to be the reason for ScrName to be null. According to https://www-01.ibm.com/support/knowledgecenter/SSYKE2_7.0.0/com.ibm.java.security.api.doc/jgss/org/ietf/jgss/GSSContext.html, acceptSecContext() generates a token and if it's not null, this token should be sent to the peer.
After the call acceptSecContext() you should check if isEstablished() returns false. If it's so,
If this method returns false it indicates that a token is needed from its peer in order to continue the context establishment phase. A return value of true signals that the local end of the context is established. This may still require that a token be sent to the peer, if one is produced by GSS-API. During the context establishment phase, the isProtReady() method may be called to determine if the context can be used for the per-message operations. This allows applications to use per-message operations on contexts which aren't fully established.
The same is explained in more details in the tutorial http://www.cs.mun.ca/java-api-1.5/guide/security/jgss/tutorials/BasicClientServer.html:
The acceptSecContext method may in turn return a token. If it does, the acceptor should send that token to the initiator, which should then call initSecContext again and pass it this token. Each time initSecContext or acceptSecContext returns a token, the application that called the method should send the token to its peer and that peer should pass the token to its appropriate method (acceptSecContext or initSecContext). This continues until the context is fully established (which is the case when the context's isEstablished method returns true).
Related
I have a long-running worker process running on EC2 that consumes items from an SQS queue. After a time (8-12 hours, I reckon) I begin getting expired security token errors. I would expect the aws lib to handle the refresh of credentials automatically but this seems not to be the case. Is it in anyway handled within the client?
This happens only when I use the DefaultCredentialsProviderChain to generate the access. This error does not occur when used with key and secret.
The stacktrace is as below:
com.amazonaws.AmazonServiceException: The security token included in the request is expired (Service: AmazonSQS; Status Code: 403; Error Code: ExpiredToken; Request ID: 6ff6e1a0-d668-5ac5-bcd7-ae30058f25c0)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1182)
at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:770)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:489)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:310)
at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2419)
at com.amazonaws.services.sqs.AmazonSQSClient.receiveMessage(AmazonSQSClient.java:1130)
at com.amazonaws.services.sqs.AmazonSQSAsyncClient$24.call(AmazonSQSAsyncClient.java:1783)
at com.amazonaws.services.sqs.AmazonSQSAsyncClient$24.call(AmazonSQSAsyncClient.java:1779)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
The workaround I found was to renew the awsCredentials everytime I encountered the expired token error and reset the sqs client.
awsCredentials = (new DefaultAWSCredentialsProviderChain).getCredentials
sqs = SimpleSQSClient(awsCredentials, Regions.US_EAST_1)
queueSQS = sqs.simple(QueueName(queueName), true)
Note: I am using the wrapper kifi/franz
The AWS SDKs are indeed capable of cycling temporary credentials inherited from the instance profile, but by passing an explicit AWSCredentials object in the constructor of SimpleSQSClient I believe you are denying it the opportunity to do so.
You didn't explicitly state that your application is inheriting the instance role, but there is enough evidence in your post to infer that this is the case:
Your application is running on EC2.
DefaultAWSCredentialsProviderChain's behavior is to look for "Instance profile credentials delivered through the Amazon EC2 metadata service" if it can find no other credentials.
You're only seeing this behavior when not explicitly passing your own known access/secret keys.
The specific behavior for automatic credentials refresh is described in the documentation:
The automatic credentials refresh happens only when you use the
default client constructor, which creates its own
InstanceProfileCredentialsProvider as part of the default provider
chain, or when you pass an InstanceProfileCredentialsProvider instance
directly to the client constructor. If you use another method to
obtain or pass instance profile credentials, you are responsible for
checking for and refreshing expired credentials.
By passing AWSCredentials directly instead of an AWSCredentialsProvider, you become responsible for checking and refreshing expired credentials. On the plus side, your workaround is just fine if you want to keep passing credentials explicitly.
SimpleSQSClient has a constructor that will work better for your use case:
new SimpleSQSClient(
credentialProvider: com.amazonaws.auth.AWSCredentialsProvider,
region: com.amazonaws.regions.Regions,
buffered: Boolean
)
Example:
SimpleSQSClient sqs = SimpleSQSClient(new DefaultAWSCredentialsProviderChain(), Regions.US_EAST_1, false)
Example, explicitly using InstanceProfileCredentialsProvider:
SimpleSQSClient sqs = SimpleSQSClient(new InstanceProfileCredentialsProvider(), Regions.US_EAST_1, false)
Further reading:
AWS SDK for Java > DefaultAWSCredentialsProviderChain - Describes default provider chain in more detail
kini/franz - Initialization - SimpleSQSClient Constructor References
AWS SDK for Java >
Developer Guide > Using IAM Roles to Grant Access to AWS Resources on Amazon EC2 - A great resource for correct usage of temporary credentials
Update 2017/11/06: DefaultAWSCredentialsProviderChain was renamed to DefaultCredentialsProvider during the AWS SDK for Java 2.0 preview. Preview documentation here.
I am using the Java class org.apache.hadoop.security.
authentication.server.AuthenticationFilter from Apache
Hadoop 2.5.0 as a filter in front of a Tomcat 6 Servlet we
wish to add Kerberos authentication to.
I am attempting to write some test cases against this filter
so that we have a better understanding of how it
works and what it does.
In order for the filter to authenticate a user, it is reading the
'Authorization' header of the HTTP request,
expecting the value to contain 'Negotiate '
My understanding of how Kerberos works leads me to believe that I
should be able to write code while creating my
HTTP request that looks something like this:
// normally the server principal keytab is not available from the client side,
// but for the purpose of making test cases I see no problem with sharing the keytab
// between the client side and the server side
javax.security.auth.kerberos.Keytab kt = KeyTab.getInstance("keytab");
KerberosKey keys[] = kt.getKeys("HTTP/voltage-pp-0000.albert.int#ALBERTS.INT");
SomeTokenType token = new SomeTokenType();
<code to set token parameters>
// my understanding of Kerberos is that the only cyphertext key
// needed on this token
// is one of the server principal's keys from the Keytab file
// (which does contain ~5
// keys of different sizes and types, I've checked)
EncryptedTokenType etoken = <encrypt token with a key from keys>
byte[] array = etoken.getBytes();
httprequest.addHeader("Authorization","Negotiate " + new Base64(0).encode(array));
So, questions here:
What is the Java Class that embodies the Kerberos Auth Token sent
in "Authorization Negotiate"?
What fields of that auth token have to be set to what values?
What is the encryption algorithm used to encrypt the auth token
against the keytab key?
What is the best keytab key to use?
What is the mechanism for byte-serializing the auth token, once
encrypted?
You are correct in that it is possible to "forge" a ticket in this manner. However, I know of no standard kerberos API that would do this.
You'll essentially need to reverse engineer the entire kerberos protocol to
create a service ticket based on the keytab. The service ticket format is
documented here
https://www.ietf.org/rfc/rfc4120.txt
You can use any of the keys in the keytab to encyrpt the service ticket. Once
you have the service ticket, you'll need to implement this RFC to create the
negotiation header.
https://www.rfc-editor.org/rfc/rfc4559
Generally, it's a lot simpler to just get a keytab for a client principal and use that and kinit to get a service ticket for testing. Your approach could work
and there is probably hacker code out there somewhere to implement it, but it's
an extremely non-standard way to do testing in a kerberos environment.
I am using JAAS to get Kerberos credentials. My config file look like this:
SignedOnUserLoginContext
{
com.sun.security.auth.module.Krb5LoginModule required debug=true useTicketCache=true doNotPrompt=true;
};
The code to get Kerberos credentials
try {
LoginContext lc = new LoginContext("SignedOnUserLoginContext");
lc.login();
Subject signedOnUserSubject = lc.getSubject();
Set<Object> privateCred = signedOnUserSubject.getPrivateCredentials();
for (Object privates : privateCred) {
if (privates instanceof KerberosTicket) {
KerberosTicket ticket = (KerberosTicket)privates;
return ticket.getEncoded();
}
}
}
When i transfer the ticket to other machines and using JAAS to login using Kerberos, it doesn't get authenticated. my config file at receiving:
KrbLogin{
com.sun.security.auth.module.Krb5LoginModule required
principal=principal#realm
useTicketCache="FILE:///where i store the ticket"
};
I am suspecting I cannot just get the ticket like that, but need to get the whole private credentials returned by getPrivateCredentials(). Also, using doNotPrompt=true and useTicketCache=true I am trying to get from Windows cache.
I read in some Java security book that private credentials can include other data such as private keys, encryption keys, password etc...
Hence, i would need to get the return value of getPrivateCredentials(). How can get what is returned by getPrivateCredentials() into an actual Kerberos credential file. I read in order to access these data, I would need to use PrivateCredentialPermission module. Is there example to show how to do this?
Credentials of Kerberos is not portable, technically, you cannot do kinit on machine A and then use that TGT (ticket granting ticket) or ST (service ticket) on machine B (except for delegation), because both TGT and ST contains encrypted IP address of client.
ST is encrypted by service server's key, which means only SS can verify/read content of the ticket.
TGT is encrypted by key of a TGS (ticket granting server).
By the way, maybe what you want is what called Kerberos keytab --- which contains principal's user name & password.
But, transferring keytab through network is dangerous and deprecated.
Am bit similar situation as explained in this question. I also have a WSDL at a particular link. When I open that link I get the There is a problem with this website's security certificate... error in IE. When I click continue it opens up WSDL file.
Now I am writing a client for this webservice in Java. And it throws following exception:
Exception in thread "main" com.sun.xml.internal.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names matching IP address 172.17.245.196 found while opening stream from https://172.17.245.196/ews/Services.wsdl
java.io.IOException: Got java.security.cert.CertificateException: No subject alternative names matching IP address 172.17.245.196 found while opening stream from https://172.17.245.196/ews/Services.wsdl?wsdl
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(Unknown Source)
at javax.xml.ws.Service.<init>(Unknown Source)
at com.microsoft.schemas.exchange.services._2006.messages.ExchangeWebService.<init>(ExchangeWebService.java:58)
at com.xyz.cms.EWSJavaAPI.ExchangeAuthenticator.getExchangeServicePort(ExchangeAuthenticator.java:32)
at com.xyz.cms.test.ExchangeDevelopmentTest.main(ExchangeDevelopmentTest.java:31)
So I guess it is related to resolving certificates and since the guy on the said thread got similar exception, I am trying out the solution suggested there - downloading and adding the certificate to the private using keytool.exe, though I really dont think I have completely understood this certificate stuff and also keytool.
So I
Downloaded the certificate by visiting the link in browser and then copy pasted it in app directory in eclipse.
Also I copy pasted $JAVA_HOME/lib/security/cacerts to my app directory. So by now my app hierarchy looks something like this in eclipse:
Then opened command prompt and navigated to the app directory.
Finally executed the command (as suggested in that thread). It gave me following output. It gave me following output
However it is giving me exactly the same exception. What should I do?
Edit
Well this is my effort to write java client for Exchange Web Services. Their is ExchangeAuthenticator which manages web services authentication requests to the Exchange and ExchangeDevelopmentTest which contains main method to test functionality of above class. a Here is the code:
ExchangeAuthenticator
public class ExchangeAuthenticator {
/**
* Obtains an authenticated ExchangeServicePortType with given credentials.
*
*/
public ExchangeServicePortType getExchangeServicePort(String username, String password, String domain, URL wsdlURL) throws MalformedURLException {
// Concatinate our domain and username for the UID needed in authentication.
String uid = "domain" + "\\" + "uname";
// Create an ExchangeWebService object that uses the supplied WSDL file, wsdlURL.
ExchangeWebService exchangeWebService = new ExchangeWebService(wsdlURL, new QName("http://schemas.microsoft.com/exchange/services/2006/messages", "ExchangeWebService"));
ExchangeServicePortType port = exchangeWebService.getExchangeWebPort();
// Supply your username and password when the ExchangeServicePortType is used for binding in the SOAP request.
((BindingProvider)port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY, uid);
((BindingProvider)port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, password);
return port;
}
}
ExchangeDevelopmentTest
public class ExchangeDevelopmentTest {
public static void main (String[] args) {
ExchangeAuthenticator exchangeAuthenticator = new ExchangeAuthenticator();
// Print statement so we can easily see where our statements start in the Java console.
System.out.println("Let's get started!");
try {
// Create a URL object which points at the .wsdl we deployed in the previous step.
URL wsdlURL = new URL("https://172.17.245.196/ews/Services.wsdl");
//URL wsdlURL = new URL("https://172.17.245.196/ews/Services.wsdl");
// Call to the class we just created to return an ExchangeServicePortType with authentication credentials.
ExchangeServicePortType port = exchangeAuthenticator.getExchangeServicePort("uname", "password#123", "domain", wsdlURL);
// Prints out the default toString() for the ExchangeServicePortType.
System.out.println(port.toString());
} catch (MalformedURLException ex) {
// Catch any errors that may occur.
Logger.getLogger(ExchangeDevelopmentTest.class.getName()).log(Level.SEVERE, null, ex);
System.out.println(ex.getMessage()+"\n"+ex.getStackTrace());
}
}
}
The problem is that your certificate is not issued for 172.17.245.196 IP address, so the client used to parse WSDL does not trust it. That IP address shall be in subject field of the certificate.
Is your certificate trusted by official certification authority or is it self signed? Probably you will need Java to trust it. Add it to keystore and then set system properties:
System.setProperty("javax.net.ssl.keyStore", "lfkeystore2");
System.setProperty("javax.net.ssl.keyStorePassword", "wshr.ut");
We have a kerberos domain at my company and I'm running a few of the Java/Kerberos examples. The question I have is around the login mechanism from the server's perspective. When running the server example GssServer.java, I need to get a javax.security.auth.Subject; in the supplied code this is via a LoginContext thus:
// Create a LoginContext with a callback handler and login
LoginContext context = new LoginContext(name, new TextCallbackHandler());
context.login();
Subject subject = context.getSubject();
This is all OK and when I run the example I see a lovely login prompt. However my issue is that this is not really how my server will run and nor how I've been led to understand how I should be presenting up services from within a kerberos domain. In the GssServer example, the problem is my server (read: service) should not need to authenticate itself to the KDC in order to present its service to clients. Access to the server-side keytab file should be sufficient to do this. So for the example config:
//jaas-krb5.conf
server {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/home/myusr/my-server.keytab"
principal="myserv/mymachine.some.domain";
};
And in the Java code:
GSSManager manager = GSSManager.getInstance();
Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
GSSName gssName = manager.createName("myserv/mymachine.some.domain#THE.REALM.COM",
GSSName.NT_HOSTBASED_SERVICE);
GSSCredential serverCreds = manager.createCredential(gssName,
GSSCredential.DEFAULT_LIFETIME,
krb5Mechanism,
GSSCredential.ACCEPT_ONLY);
The trouble with this is that the server information in the jaas-krb5.conf file is not available unless I authenticate myself via the line:
Jaas.loginAndAction("server", action);
I shouldn't have to go thru this authentication! But if I don't authenticate myself, I end up with:
Exception in thread "main" GSSException: No valid credentials provided (Mechanism level: Attempt to obtain new ACCEPT credentials failed!)
at sun.security.jgss.krb5.Krb5AcceptCredential.getKeysFromSubject(Krb5AcceptCredential.java:188)
at sun.security.jgss.krb5.Krb5AcceptCredential.getInstance(Krb5AcceptCredential.java:73)
at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:77)
at sun.security.jgss.GSSManagerImpl.getCredentialElement(GSSManagerImpl.java:149)
at sun.security.jgss.GSSCredentialImpl.add(GSSCredentialImpl.java:389)
at sun.security.jgss.GSSCredentialImpl.<init>(GSSCredentialImpl.java:45)
at sun.security.jgss.GSSManagerImpl.createCredential(GSSManagerImpl.java:102)
at gsa.hk.GssServer$GssServerAction.run(GssServer.java:79)
at gsa.hk.GssServer.main(GssServer.java:57)
Caused by: javax.security.auth.login.LoginException: No LoginModules configured for com.sun.security.jgss.accept
at javax.security.auth.login.LoginContext.init(LoginContext.java:256)
at javax.security.auth.login.LoginContext.<init>(LoginContext.java:403)
at sun.security.jgss.LoginUtility.login(LoginUtility.java:72)
at sun.security.jgss.krb5.Krb5Util.getKeysFromSubject(Krb5Util.java:205)
at sun.security.jgss.krb5.Krb5AcceptCredential$1.run(Krb5AcceptCredential.java:184)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.jgss.krb5.Krb5AcceptCredential.getKeysFromSubject(Krb5AcceptCredential.java:181)
... 8 more
It's not surprising that there is a problem. After all, unless I have a handle on the server subject, how can I know where my keytab is, or what service I am providing?
So my question is: how can I tell the GSS API about the keytab/service without authenticating myself in code?
OK. So this turns out to be extremely easy assuming you know that the keytab file can be used instead of authentication. This is outlined a bit in the JavaDoc for Krb5LoginModule
Basically, if I
loginAndAction("anything", action)
Then my config should look like:
//jaas-krb5.conf
anything {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
doNotPrompt=true
keyTab="/home/myusr/my-server.keytab"
principal="myserv/mymachine.some.domain";
};
The important bit is the addition of doNotPrompt=true. When this property is set, the server code uses the information in the keytab