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

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.

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!

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.

Account gets locked every time while trying to access via the V1APIConnector

I am trying to access the VersionOne data using the V1APIConnector. I can verify that I am using the correct data and meta URLs. I also have the correct domain/username and password.
But everytime I execute the below code, I get an Authentication error saying username/password is invalid and my account gets locked.
Once I unlocked my account, I tried again and the account was locked again. I am the V1 Administrator so I have the permissions.
Our VersionOne instance uses Windows Integrated Auththentication. Also my username is in the format -mydomain/myusername
Is there any different way to pass the credentials? Since my account is getting locked, it must mean at least the domain and the username are being passed correctly. Any Ideas?
V1APIConnector dataConnector = new V1APIConnector( _dataUrl, _username, _password);
V1APIConnector metaConnector = new V1APIConnector( _metaUrl );
IMetaModel metaModel = new MetaModel(metaConnector);
IServices services = new Services(metaModel, dataConnector);
System.out.println("Creating query");
IAssetType defectType = metaModel.getAssetType("Defect");
Query query = new Query(defectType);
IAttributeDefinition nameAttribute = defectType.getAttributeDefinition("Name");
query.getSelection().add(nameAttribute);
query.getPaging().setPageSize(3);
query.getPaging().setStart(0);
System.out.println("Retrieve from query");
QueryResult result = services.retrieve(query);
The Java.SDK ignores the username and password parameters of the V1APIConnector constructor when attempting to connect to a Windows Integrated instance, and instead uses the domain credentials that it is running under. If you are logged into your machine as "MyDomain\MyUsername" then that is the credentials that it will use. It does not support supplying the credentials of another account.
Note that there must also exist a VersionOne member account with the username set to "MyDomain\MyUsername" to successfully authenticate.
VersionOne locks accounts only when your license has expired, and if that happens, only the system administrator (Member:20) will remain active. In addition, administrators can deactivate accounts manually.

if pwdLastSet set to 0, user can't login

I am creating users in Active Directory using Java code.
I am using this AD in my authentication provider in WebLogic server.
I've googled a bit but didn't find satisfactory solution to this issue - my requirement is typical: user must change password after first logon.
I know there is an attribute called pwdLastSet for this purpose, however the issue is, if I set it to 0 at the time of user creation, this user is not able to login.
That's how I am setting it to 0:
mods[2] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("pwdLastSet", Integer.toString(0)));
Just in case if anyone would like to know what's value of userAccountControl:
For userAccountControl, I am doing following:
attrs.put("userAccountControl", Integer.toString(LDAPConstants.UF_NORMAL_ACCOUNT + LDAPConstants.UF_ACCOUNTDISABLE));
After disabled user is created, I set password and enable it:
mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl",Integer.toString(LDAPConstants.UF_NORMAL_ACCOUNT)));
Help?
Thanks.

Unlock a user object in HPUX LDAP Directory Server

I am accessing the HP UX directory server through my java code, for reset & unlock a locked out user account in the Directory server.
Here is my code for user account password reset.
openConnection(details);
loadUserInformation((String)details.get("END_USER_NAME"));
ModificationItem[] mods = new ModificationItem[1];
Attribute mod0 = new BasicAttribute("userpassword", (String)details.get("NEW_PASSWORD"));
mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
connection.modifyAttributes(user, mods);
closeConnection();
But I can't do the account unlock for the given user because I can't find the LDAP attribute for account lockout in my LDAP browser.
Looks like HPUX Directory server is a clone of Red hat Directory server.
First, which unlock are you trying to perform?
An account could be locked by different aspects depending on how you have setup your password policy.
If the account is intruder detected lockout, then you need to perform the following operation:
dn: uid=scarter,ou=people,dc=example,dc=com
changetype: modify
delete: passwordRetryCount
-
delete: accountUnlockTime
-jim
The correct answer is to configure the password policies first then configure subtree level or user based password policies and account lockout policies then make a user account get locked and try the following code will unlocks a locked out account.
ModificationItem[] mods = new ModificationItem[2];
mods[0] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("passwordRetryCount"));
mods[1] = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, new BasicAttribute("accountUnlockTime"));
connection.modifyAttributes(user, mods);
The entry's object class(es) define which attributes are allowed. You should lookup the entry's object class and try to find the correct attribute from there.

Categories

Resources