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

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.

Related

Creating a LdapContext from valid ldap service ticket using GSSAPI

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!

ldap search query with java will not work when running multiple times

I'm using InitialLdapContext in order to search a Ldap directory .
Though it behaves strange - if i run the search query 1-5 times in a row, its working ok, returns an answer immediately , though if i run it more than that it suddenly doesn't return , and hangs until timeout .
But if i try to do the same thing through an Ldap Client for exmaple JXplorer , it never give me a timeout .. which means there is something wrong with my code.
In addition, I'm not sure it even regards to the timeout I set to. I tried both 5sec timeout and 15sec timeout and occasionally it will wait for much more than that.
Here is the code sample:
Hashtable<String,Object> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "my url");
env.put(Context.SECURITY_AUTHENTICATION, "none");
env.put("com.sun.jndi.ldap.connect.timeout", "5000");
InitialLdapContext context = new InitialLdapContext(env,null);
NamingEnumeration<SearchResult> me = context.search("cn=*,cn=nfs,ou=policy,ou=Service,dc=doo,dc=myorg,dc=com",
FILTER_WITH_DEPTH.replace("replaceMe", String.valueOf(4)), getSimpleSearchControls());
me.hasMore();
me.close();
What am I doing wrong ?

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.

Call to GSSContext.initSecContext fails intermittently: Receive timed out

I'm writing client-side code for Windows Kerberos authentication with a service (logging code omitted):
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
// System.setProperty("sun.security.krb5.debug", "true");
Package thisPkg = AuthHelper.class.getPackage();
String configPath = Util.getConfigPath(thisPkg, "jaas.conf");
System.setProperty("java.security.auth.login.config", "=" + configPath);
GSSManager manager = GSSManager.getInstance();
GSSName peerName = manager.createName(spn, GSSName.NT_HOSTBASED_SERVICE);
GSSContext context = manager.createContext(peerName, null, null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(true); // required
context.requestCredDeleg(true); // required for publish
byte[] serverTokenBytes = new byte[0];
while (!context.isEstablished()) {
byte[] clientTokenBytes = context.initSecContext(serverTokenBytes, 0,
serverTokenBytes.length);
if (clientTokenBytes != null)
socket.send(createClientMessage(clientTokenBytes));
if (context.isEstablished()) break;
Message message = socket.receive();
String serverToken = message.getFirst("SERVERTOKEN").toString();
serverTokenBytes = Base64.decodeBase64(serverToken);
}
Where jaas.conf simply contains:
sp {
com.sun.security.auth.module.Krb5LoginModule required debug=true;
};
I have also set the allowtgtsessionkey registry key as required, and installed JCE Unlimited Strength Jurisdiction Policy Files 7.
The code sometimes works (i.e. mutual authentication is established); however, sometimes it gets stuck for a while at the first call to GSSContext.initSecContext, throwing an exception after about a minute:
Exception in thread "main" GSSException: No valid credentials provided (Mechanism level: Receive timed out)
...
Caused by: java.net.SocketTimeoutException: Receive timed out
...
When I enable Kerberos debugging output (by uncommenting the second line above), I can see that the protocol sometimes gets stuck at line:
getKDCFromDNS using UDP
A Java Kerberos troubleshooting website suggests that this is an issue with the Kerberos authentication server, but I know that the server is up and running, since we have similar code written in C# (using .NET libraries) that never gets stuck.
It seems like the DNS resolution for the Kerberos authentication server is going through some indirection, which is unreliable. If you specify the server explicitly (somewhere at the beginning of your code), it will bypass that redirection:
System.setProperty("java.security.krb5.realm", "<YOUR_KRB_REALM>");
System.setProperty("java.security.krb5.kdc", "<YOUR_KRB_SERVER_ADDR_OR_IP>");
EDIT: It turns out that communication with Kerberos servers was inherently unreliable due to the protocol using UDP, so it had a high chance of failing for servers that are relatively far away. Windows 8 uses TCP by default; to force TCP on previous versions:
XP/2000: In HKLM\System\CurrentControlSet\Control\Lsa\Kerberos, set DWORD MaxPacketSize to 1.
2003/Vista/7: In HKLM\System\CurrentControlSet\Control\Lsa\Kerberos\Parameters, set DWORD MaxPacketSize to 1.
(Note that the same registry directory also needs DWORD AllowTGTSessionKey set to 1 for Kerberos to work at all.)

Get Active Directory connection through glassfish jndi resource

I'm trying to get an LDAP-Connection with the attributes provided by a glassfish custom-resource.
My jndi settings:
Resourcetype: javax.naming.directory.Directory
Factory-Class: com.sun.jndi.ldap.LdapCtxFactory
Parameters:
java.naming.security.credentials = myPassword
java.naming.security.principal = cn=ldapUser,ou=myOrganization,dc=myDomain,dc=net
URL = ldap://ldapserver/ou=myOrganization,dc=myDomain,dc=net
This is how I get the connection in Java:
Context initCtx = new InitialContext();
DirContext ctx = (DirContext) initCtx.lookup("CMDB2LDAP");
This works perfectly with OpenLDAP but when I try to connect to an AD 2003 I get the following Exception:
javax.naming.NamingException: [LDAP: error code 1 - 00000000: LdapErr: DSID-0C090627, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, vece]; remaining name ''
When I create my own DirContext and put exactly the same properties in it, the connection works flawlessly.
I would prefer to get the connection settings from glassfish for the sake of easy administration.
Try adding java.naming.referral = follow as another attribute and see if that works.

Categories

Resources