Creating a LdapContext from valid ldap service ticket using GSSAPI - java

Please Note:- I just want to validate whether the following can be achieved using the JAAS/GSSAPI. I am not able to find any pointers.
Let me first clear the constraints on my application:
We can't have a static krb.conf file. It is dynamic and constantly changing. Hence, caching LoginContext objects in memory is not possible as the Subject's credentials are invalidated once the krb.conf file is modified.
We want to manage large set of realms of which we don't have any prior information, hence static krb.conf file is not possible.
"sun.security.krb5.internal.tools.Kinit" is proprietary and always get a warning it might get removed in future releases. So we can't use this to generate the cache because of the risk of getting a runtime issues. Note: Cached TGTs don't expire even if the krb.conf file is changed natively. But the problem here is, we will have to make our code of generating and maintaining the krb.conf file much complex. We want to avoid this
We are keeping the option of running the kinit tool using Runtime#exec(...) as the last resort to generate the TGT cache files (which will reduce the dependency on sun.security.krb5.internal.tools.Kinit package) but will make our krb.conf generation and maintenance logic more complex.
I am in search of figuring out a more easy solution.
Our current implementation is very straightforward: We change our krb.conf file to suit the current realm and domain information and then delete the file. Everything being very dynamic, it is very efficient but the problem is, we are not able to cache the TGTs and service tickets which increases the load on KDC server and results in performance hit!
With the thorough introduction, let us move to the PoC that I am trying out. Please provide pointers or on:
Whether I am moving in the right direction or not?
Are the things that I am envisioning even possible or not?
If anyone of you have come across such a scenario, what strategy have you employed?
We are connecting to Active Directory DCs to form a LdapContext/ DirContext to fetch the AD object Data.
When there is not cached data available, proceed normally to create the LoginContext as usual:
System.setProperty("java.security.krb5.conf", "C:\\Windows\\krb5.ini");
System.setProperty("sun.security.jgss.debug","true");
LoginContext lc = null;
try {
lc = new LoginContext(LdapCtxGSSAPIEx.class.getName(),
new LoginCallbackHandler(username,password));
// Attempt authentication
// You might want to do this in a "for" loop to give
// user more than one chance to enter correct username/password
lc.login();
} catch (LoginException le) {
System.err.println("Authentication attempt failed" + le);
System.exit(-1);
}
Solution #1:
Once the valid LoginContext is created, use GSSAPI to get the service ticket for ldap. I know of the other way (which actually is our current implementation, and shown in Solution#2) to form the LdapContext in the PrivilegedAction#run(). But that is not helping in caching. Hence, trying a PoC on storing the Service Ticket instead of the TGT. Getting the ldap service ticket is as follows:
// servicePrincipalName = ldap/ad01.example.lab#EXAMPLE.LAB
GSSManager manager = GSSManager.getInstance();
GSSName serverName = manager.createName( servicePrincipalName,
GSSName.NT_HOSTBASED_SERVICE);
final GSSContext context = manager.createContext( serverName, krb5OID, null,
GSSContext.DEFAULT_LIFETIME);
// The GSS context initiation has to be performed as a privileged action.
byte[] serviceTicket = Subject.doAs( subject, new PrivilegedAction<byte[]>() {
public byte[] run() {
try {
byte[] token = new byte[0];
// This is a one pass context initialisation.
context.requestMutualAuth( false);
context.requestCredDeleg( false);
return context.initSecContext( token, 0, token.length);
}
catch ( GSSException e) {
e.printStackTrace();
return null;
}
}
});
Questions:
How can I use this service ticket to form the LdapContext ?
Can I store this ticket in a file (encoded) and then later use the ticket in the same way to form the LdapContext ?
Solution #2:
Creating the LdapContext as seen in most of the tutorials and also in our current implementation:
DirContext ctx = Subject.doAs(lc.getSubject(), new JndiAction<DirContext>(args,providerURL ));
class JndiAction<DirContext> implements java.security.PrivilegedAction<DirContext> {
.......
public DirContext run() {
return performJndiOperation(args, this.providerURL);
}
public DirContext performJndiOperation(String[] args, String providerURL){
......
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
// Must use fully qualified hostname
env.put(Context.PROVIDER_URL, providerURL);
// Request the use of the "GSSAPI" SASL mechanism
// Authenticate by using already established Kerberos credentials
env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
// Request mutual authentication
env.put("javax.security.sasl.server.authentication", "true");
DirContext ctx = new InitialDirContext(env);
return ctx;
......
}
.......
}
When this line: DirContext ctx = new InitialDirContext(env); in the above code is executed: the Subject is populated with a new PrivateCredential with the service principal : ldap/ad01.example.lab#EXAMPLE.LAB
Question: Can I store this service ticket for further reference to create the LdapContext instead of again performing all the steps of authentication again and again?
What are delegation credentials? Would they help in solving my issue?
UPDATE:
Why store the service ticket and not the TGT in cache? ---> To avoid kinit explicitly. Storing the service ticket will fit in with our current solution easily. Also, this is going to be a temporary solution as we are going to ask our customers the krb.conf file as per their needs and get off the responsibility of creating a krb.conf file. But for now, we have to do this!
Please Help!

Related

Modify LDAP attribute fails from Java, but succeeds with ldapmodify command

I manage to perform a STARTTLS ldapmodify ( -ZZ) of an attribute, but fail to perform the same modification using javax.naming.ldap coding
The server is OpenLDAP. It has SSigned SSL Certificate for modification, and unsecured reading is allowed. Classical combination.
I run the following command from the same server on which I will try to execute the Java code (later):
ldapmodify -H ldap://my.ldap.server:389 -D "myldapadmin" -W -ZZ
Enter LDAP Password:
dn: CN=John Westood,OU=L100,DC=l,DC=woods
changetype: modify
replace: uPwd
uPwd:: 234WF34TG2U
modifying entry "CN=John Westood,OU=L100,DC=l,DC=woods"
... as you can see, it asks me for myldapadmin's password, I enter it, and the modification happens.
Here is what I am doing from Java, I want to do the same modification, I am running it on the same server. I have imported the SSigned LDAP SSL certificate into java first.
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://my.ldap.server:389");
// Create initial context
LdapContext ctx = new InitialLdapContext(env, null);
ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, "myldapadmin");
ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, "myadminpass");
// Start TLS (STARTTLS is also used by the console command ldapmodify)
StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new
StartTlsRequest());
SSLSession sess = tls.negotiate();
//Modification of 'uPwd' attribute
ModificationItem[] mods = new ModificationItem[1];
Attribute mod0 = new BasicAttribute("uPwd", "4G45G435G436UJWG");
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
ctx.modifyAttributes("CN=John Westood,OU=L100,DC=l,DC=woods", mods);
// Stop TLS
tls.close();
// Context close
ctx.close();
I get exception in method ctx.modifyAttributes. The Exception is the following:
javax.naming.OperationNotSupportedException: [LDAP: error code 53 - 0000052D: SvcErr: DSID-031A12D2, problem 5003 (WILL_NOT_PERFORM), data 0
Anybody have idea why modify uPwd works with STARTLS from commandline, but not from Java ?
Appears you are trying to change perhaps a password against Microsoft Active Directory (Even though you show openLDAP)
(I have no idea what uPwd could be.)
Changing Microsoft Active Directory Passwords has several constraints.
Once you are confident you have an acceptable connection to Microsoft Active Directory, We have an example for Changing Microsoft Active Directory Password with JNDI
FYI: By default startTLS would use 389.

How do I modify Operational Attributes in OpenLDAP from Java/JNDI?

We are working on a custom password reset tool which is currently able to reset the passwords for users (using the admin DN), but I need to also remove/modify some Operational Attributes in order to completely handle the business use cases. I connect to the LDAP server using:
private void connect() throws NamingException {
Properties props = new Properties();
props.put(INITIAL_CONTEXT_FACTORY, LDAP_CTX_FACTORY);
props.put(PROVIDER_URL, format("ldap://%s:%d/", config.ldapHost(), config.ldapPort()));
props.put(SECURITY_CREDENTIALS, config.ldapBindPassword());
props.put(SECURITY_PRINCIPAL, config.ldapBindUser());
props.put(SECURITY_AUTHENTICATION, "simple");
props.put(REFERRAL, "follow");
props.put(BATCHSIZE, "1000");
connection = new InitialLdapContext(props, null);
connection.setRequestControls(LDAPControls.controls());
LOG.debug("Successfully completed bind to LDAP server '{}'", config.ldapHost());
connected = true;
}
And I need to modify some operational attributes to do things like unlock accounts/update modified time/etc...
List<BasicAttribute> attrs = new ArrayList<>();
List<ModificationItem> mods = new ArrayList<>();
// Set password hash
attrs.add(new BasicAttribute("userPassword", "{SSHA}" + hashPassword(salt, password)));
mods.add(new ModificationItem(REPLACE_ATTRIBUTE, attrs.get(0)));
// Set last modified timestamp
attrs.add(new BasicAttribute("modifyTimestamp", date.withZone(UTC).format(now())));
mods.add(new ModificationItem(REPLACE_ATTRIBUTE, attrs.get(1)));
// Set password changed time
attrs.add(new BasicAttribute("pwdChangeTime", date.withZone(UTC).format(now())));
mods.add(new ModificationItem(REPLACE_ATTRIBUTE, attrs.get(2)));
// Remove password lock
attrs.add(new BasicAttribute("pwdAccountLockedTime"));
mods.add(new ModificationItem(REMOVE_ATTRIBUTE, attrs.get(3)));
// Clear password failure time
attrs.add(new BasicAttribute("pwdFailureTime"));
mods.add(new ModificationItem(REMOVE_ATTRIBUTE, attrs.get(4)));
this.reconnect();
ModificationItem[] modItems = new ModificationItem[mods.size()];
mods.toArray(modItems);
connection.modifyAttributes(getDN(email), modItems);
LOG.debug("Completed update of user password for '{}'", email);
return true;
But when I run this, I get:
LDAP: error code 21 - modifyTimestamp: value #0 invalid per syntax
Could anyone help me to figure out why?
How do I modify Operational Attributes in OpenLDAP from Java/JNDI?
You don't. The server does. That's what 'operational attribute' means.
I need to also remove/modify some Operational Attributes in order to completely handle the business use cases
Bad luck.
You should be using the 'ppolicy' overlay and the associated extended password-modify operations, rather than rolling all this yourself. It does everything you need, and if it doesn't you need to adjust your needs ;-)
NB You should not hash the password yourself. OpenLDAP will do that for you when configured correctly.

MongoDB: how to create an authenticated db via java driver

I'm trying to create an authenticate database in MongoDB 2.6 using java driver v 2.12.
In particular I need to create a user accessing to admin collection.
Any suggestion?
Thanks.
Here my solution:
MongoClient mcAdmin = new MongoClient(
configuration.getServerAddresses(),
Arrays.asList(MongoCredential.createMongoCRCredential(
MONGODB_ADMIN_USERNAME, "admin",
MONGODB_ADMIN_PASSWORD.toCharArray())));
try {
mcAdmin.setWriteConcern(WriteConcern.JOURNALED);
DB db = mcAdmin.getDB(userDbName);
BasicDBObject commandArguments = new BasicDBObject();
commandArguments.put("user", userUsername);
commandArguments.put("pwd", userPassword);
String[] roles = { "readWrite" };
commandArguments.put("roles", roles);
BasicDBObject command = new BasicDBObject("createUser",
commandArguments);
db.command(command);
} finally {
mcAdmin.close();
}
Doing this in Java code is not the best way to do it, and except for very rare use cases (writing an admin application for MongoDB) even one I would strongly advice against.
Security risk
First of all, your application would need extremely high privileges, namely userAdminAnyDatabase or userAdmin on the admin database, which more or less grants the same rights: creating a superuser at will. To put it in other words: this code would be a high security risk.
Granting roles and rights on a database is an administrative task and for good reasons should be decoupled from an application accessible by arbitrary users.
Technical problems
Activating authentication from a client simply is impossible. The mongod instance in question has to be started with authentication enabled. Furthermore, you would have to save to create a user with the mentioned roles before you could have your app administer users. The problem: you would have to store the password for that user somewhere. Unless you encrypt it, you basically store the most powerful password for your MongoDB databases and cluster in cleartext. And if you encrypt it, you have to pass the key for decryption to your application at some point in a secure manner. And all this to break best practices ("Separation of concerns")?

XStorable storeToURL and WebDav

I have seen multiple threads regarding the use of XStorable.storeToURL and vnd.sun.star.webdav://domain:8080//path/to/document_library to save OO documents to a webdav library folder. However, I have not seen a posting where someone has successfully used this in Java. While the use of the UCB vnd.sun.star.webdav://domain:8080//path/to/document_library/doc.odt works when using the File, Save menu options within OO Writer, I am prompted for a username and password. Supplying user and password via vnd.sun.star.webdav://user:password#domain:8080/ has not worked for me. I need to use this method from within a Java class to save a OO document. Has anyone had success using the following or something similar?
xStorable.storeToURL("vnd.sun.star.webdav://domain:8080/path/to/document_library/doc.odt", storeProps)
In the OO Developer's Guide, there is a paragraph regarding WebDav authentication:
DAV resources that require authentication are accessed using the interaction handler mechanism of the UCB. The DAV content calls an interaction handler supplied by the client to let it handle an authentication request. The implementation of the interaction handler collects the user name or password from a location, for example, a login dialog, and supplies this data as an interaction response.
Maybe this is related to the issue? If so, how to use an interaction handler for the authentication when trying to storeToURL via webdav?
Adding InteractionHandler was the issue. With this added, documents can be saved via storeToURL and passing the handler in as an argument:
String oooExeFolder = "C:/OpenOffice/program";
XComponentContext xLocalContext = BootstrapSocketConnector.bootstrap(oooExeFolder);
Object serviceManager = xLocalServiceManager.createInstanceWithContext("com.sun.star.task.InteractionHandler", xLocalContext);
XInteractionHandler xHandler = (XInteractionHandler)UnoRuntime.queryInterface( XInteractionHandler.class, serviceManager);
PropertyValue[] storeProps = new PropertyValue[1];
storeProps[0] = new PropertyValue();
storeProps[0].Name = "InteractionHandler";
storeProps[0].Value = xHandler;
xStorable.storeToURL("vnd.sun.star.webdav://domain:8080/path/to/document_library/doc.odt", storeProps);

Obtaining WorklistContext and Querying Tasks in Weblogic Integration

In order to get the Weblogic initial context to query the task database i am doing the following:
Properties h = new Properties();
h.put(Context.SECURITY_PRINCIPAL, "weblogic");
h.put(Context.PROVIDER_URL, "t3://localhost:17101");
h.put(Context.SECURITY_CREDENTIALS, "weblogic");
h.put(Context.SECURITY_AUTHENTICATION, "simple");
WLInitialContextFactory test = new WLInitialContextFactory();
test.getInitialContext(h);
Context ctx = null;
ctx = getInitialContext();
WorklistContext wliContext = WorklistContextFactory.getRemoteWorklistContext(ctx, "MyTaskApplication");
I then get the TaskQuery interface with the following code:
WorklistTaskQuery taskQuery = wliContext.getInterfaceForTaskQuery();
and to get the tasks i do:
taskQuery.getTasks(query);
where query is com.bea.wli.worklist.api.TaskQuery object.
Please note that this code is running inside the domain running the tasks.
Unfortunally i am getting the following error when i call the getTasks methods:
java.lang.SecurityException: [WLI-Worklist:493103]Access denied to resource /taskplans
/Manual:1.0. Applicable policy: Query Caller: principals=[] Method: com.bea.wli.worklist.security.WorklistSecurityManager.assertTaskAccessAllowed
It seems Weblogic is ignoring the user set on the new initial context and trying to use the one coming from the browser. It so happens that i might need to do query searchs in background workers that don't have a browser session(obviously).
Can anyone help with this?
I've found a solution for this, though it's convoluted and ugly as hell.
Since i'm making these calls through an EJB i can authenticate the call by grabbing the EJB implementation from an authenticated context like so:
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.SECURITY_PRINCIPAL,"user");
env.put(Context.PROVIDER_URL,"t3://localhost:7001");
env.put(Context.SECURITY_CREDENTIALS,"password");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
getSessionInterface(interfaceClass, new InitialContext(env));
Your best bet for this is to avoid the above example and this API all together. Just use the regular MBean Implementation which allows authentication.
Update this solution doesn't seem to be viable, it will screw up the transaction management. Will report back, but it seems if you need to create tasks outside of an authenticated context you will need to go the MBean way

Categories

Resources