ldapjs / node-ldapjs

LDAP Client and Server API for node.js
http://ldapjs.org
MIT License
1.6k stars 438 forks source link

problems changing passwords #980

Open byteAr opened 4 months ago

byteAr commented 4 months ago

I have had the problem that when trying to change the password of a user with valid credentials:

const ldap = require('ldapjs');

const ldapClient = ldap.createClient({ url: 'ldap://ipldap', reconnect: true });

const adminDN = 'cn=admin,cn=Users,dc=iugnad,dc=lan'; const adminPassword = 'passAdmin';

const searchFilter = '(sAMAccountName=mlopez)';

const searchOptions = { scope: 'sub', filter: searchFilter };

ldapClient.bind(adminDN, adminPassword, (err) => { if (err) { console.error('Error LDAP:', err); return; } console.log('Administrador autenticado correctamente.');

ldapClient.search('DC=example, DC=lan', searchOptions, (err, res) => { if (err) { console.error('Error User:', err); return; }

res.on('searchEntry', (entry) => {
  const userDN = entry.objectName;
  const newPassword= '********'
  console.log('DN del usuario encontrado:', userDN);

  const userCN = 'User';
 const userDN2 = `CN=${userCN},CN=Users,DC=iugnad,DC=lan`;

  const userPasswordAttribute = new ldap.Attribute({
    type: 'userPassword',
    vals: newPassword
  })

  ldapClient.modify(userDN2, 
    [
      new ldap.Change({
        operation: 'replace',
        modification: userPasswordAttribute
      })
    ],  
    (err) => {
    if (err) {
      console.log(err);
    } else {
      console.log('Password change successful');
    }
    ldapClient.unbind();
  });

I manage to log in and find the user to change the password. But when I change it I receive the following error:

ConnectionError: 1ldap://servereldap:port closed at C:\Users\mlopez\Desktop\prueba intranet\LDAP_Nodejs\node_modules\ldapjs\lib\client\client.js:1083:17 at C:\Users\mlopez\Desktop\prueba intranet\LDAP_Nodejs\node_modules\ldapjs\lib\client\message-tracker\index.js:113:7 at Map.forEach () at Object.purgeMessages [as purge] (C:\Users\mlopez\Desktop\prueba intranet\LDAP_Nodejs\node_modules\ldapjs\lib\client\message-tracker\index.js:110:14) at Client._onClose (C:\Users\mlopez\Desktop\prueba intranet\LDAP_Nodejs\node_modules\ldapjs\lib\client\client.js:1081:11) at Object.onceWrapper (node:events:633:26) at Socket.emit (node:events:518:28) at TCP. (node:net:337:12) { lde_message: '1ldap://10.98.40.22:389 closed', lde_dn: null }

WanpengQian commented 4 months ago

Since your code has an attribute sAMAccountName, assume your LDAP server is Windows AD Server.

AFAIK, For changing AD password, you should use delete operation follow a add operation, instead of replace operation.

replace operation is for password reset, that is the permission of administrator. while normal user has not such permission. for ref: https://learn.microsoft.com/en-us/troubleshoot/windows-server/active-directory/change-windows-active-directory-user-password

another thing, when you change password, you should use LDAPS instead of LDAP. for AD server, you have to enable LDAPS manually, (default configuration is not enabled.)

Connection sample code

const url = "ldaps://xxx.xxx.xxx.xxx:636";
const client = ldap.createClient({
  url: `${url}`,
  tlsOptions: {
    rejectUnauthorized: false
  }
});

PWD change sample code:

client.search("DC=xxx,DC=xxx,DC=xxx", searchOptions, (err, res) => {
  res.on('searchEntry', entry => {
    let dn = entry.pojo.objectName;
    client.modify(dn, [
      new ldap.Change({
        operation: 'delete',
        modification: new ldap.Attribute({
          type: 'unicodePwd',
          values: encodePassword(currentPassword)
        })
      }),
      new ldap.Change({
        operation: 'add',
        modification: new ldap.Attribute({
          type: 'unicodePwd',
          values: encodePassword(newPassword)
        })
      })
    ], function (e) {
      if (e) {
        resp.json({
          result: "failed",
          message: e
        });
        console.log(e);
      }
      else {
        resp.json({
          result: "success"
        });
        console.log('Password changed!');
      }
    });
  });
  res.on('error', e => {
    console.error('error: ' + e.message);
    resp.json({
      result: "failed",
      message: e
    });
  });
});

function encodePassword(password) {
  return new Buffer('"' + password + '"', 'utf16le').toString();
}
WanpengQian commented 4 months ago

And for TLS version, it depends on your Server Version. NodeJS is mark TLS 1.0/1.1 disabled by default. you have to make sure client and server can success have an TLS handshake. You can capture the TLS packet by wireshark for sure.