Change Password in Active Directory using LDAP/PHP/IIS/SSL - php

First of all, this may be less of a programming question and more of a how do I configure LDAPS question, but here goes...
Background Info:
I have two Windows 2008 R2 servers. One is a domain controller (DC) with Active Directory (AD) that I want to communicate with via LDAP. This one is named TestBox.TestDomain.local. The other server is running IIS, PHP (with ldap and openssl), and mySQL.
What is/isn't working:
I can successfully connect to the DC unsecured over port 389 and read/write data to AD. What I can't do is change or set user passwords since this requires a secure connection using LDAPS (LDAP w/ SSL) over port 636.
What I need help with:
I have tried installing Active Directory Certificate Services (AD CS) and configuring the DC to act as a Certificate Authority (CA) using information found here: http://technet.microsoft.com/en-us/library/cc770357(WS.10).aspx but no matter what I try I can't get a connection over LDAPS to work.
Sample Code:
Creating the LDAP Connection
function ldapConnect(){
$ip = "100.200.300.400"; // WAN IP goes here;
$ldap_url = "ldap://$ip";
$ldaps_url = "ldaps://$ip";
$ldap_domain = 'testdomain.local';
$ldap_dn = "dc=testdomain,dc=local";
// Unsecure - WORKS
$ldap_conn = ldap_connect( $ldap_url ) or die("Could not connect to LDAP server ($ldap_url)");
//alternate connection method
//$ldap_conn=ldap_connect( $ip, 389 ) or die("Could not connect to LDAP server (IP: $ip, PORT: 389)");
// Secure - DOESN'T WORK
//$ldap_conn = ldap_connect( $ldaps_url ) or die("Could not connect to LDAP server ($ldaps_url)");
//alternate connection method
//$ldap_conn=ldap_connect( $ip, 636 ) or die("Could not connect to LDAP server (IP: $ip, PORT: 636)");
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ds, LDAP_OPT_REFERRALS, 0);
$username = "AdminUser";
$password = "AdminPass";
// bind using admin username and password
// could also use dn... ie. CN=Administrator,CN=Users,DC=TestDomain,DC=local
$result = ldap_bind($ldap_conn, "$username#$ldap_domain", $password ) or die("<br>Error: Couldn't bind to server using supplied credentials!");
if($result){
return $ldap_conn;
}else{
die("<br>Error: Couldn't bind to server using supplied credentials!");
}
}
Adding a New User to Active Directory
function ldapAddUser($ldap_conn, $ou_dn, $firstName, $lastName, $username, $pwdtxt, $email){
$dn = "CN=$firstName $lastName,".$ou_dn;
## Create Unicode password
$newPassword = "\"" . $pwdtxt . "\"";
$len = strlen($newPassword);
$newPassw = "";
for($i=0;$i<$len;$i++) {
$newPassw .= "{$newPassword{$i}}\000";
}
$ldaprecord['cn'] = $firstName." ".$lastName;
$ldaprecord['displayName'] = $firstName." ".$lastName;
$ldaprecord['name'] = $firstName." ".$lastName;
$ldaprecord['givenName'] = $firstName;
$ldaprecord['sn'] = $lastName;
$ldaprecord['mail'] = $email;
$ldaprecord['objectclass'] = array("top","person","organizationalPerson","user");
$ldaprecord["sAMAccountName"] = $username;
//$ldaprecord["unicodepwd"] = $newPassw;
$ldaprecord["UserAccountControl"] = "544";
$r = ldap_add($ldap_conn, $dn, $ldaprecord);
// set password .. not sure if I need to base64 encode or not
$encodedPass = array('userpassword' => base64_encode($newPassw));
//$encodedPass = array('unicodepwd' => $newPassw);
echo "Change password ";
if(ldap_mod_replace ($ldap_conn, $dn, $encodedPass)){
echo "succeded";
}else{
echo "failed";
}
}

Just two pieces of advice:
During the AD CS setup, in the Specify Setup Type page, click Enterprise, and then click Next.
AD service is supposed to take himself his own certificate, but if it works like in Windows server 2003, you must reboot the server to make it work. Perhaps just stop and restart the service in W2K8 R2.
Afer that, you can just try to build a certificate and install it on the AD service account, like you can find it done with ADAM.

Did you create a certificate request for the secure Ldap with the correct OIDs?
here's my inf file:
[Version]
Signature="$Windows NT$
[NewRequest]
Subject = "CN=my-server.blahblah.com" ; must be the FQDN of host
Exportable = TRUE ; TRUE = Private key is exportable
KeyLength = 4096 ; Common key sizes: 512, 1024, 2048,
; 4096, 8192, 16384
KeySpec = 1 ; Key Exchange
KeyUsage = 0xF8 ; Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment, Key Agreement
MachineKeySet = True
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = CMC
; Omit entire section if CA is an enterprise CA
[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.8.2.2
OID=1.3.6.1.5.5.7.3.1
OID=1.3.6.1.5.5.7.3.2
OID=1.3.6.1.5.5.7.3.3
OID=1.3.6.1.5.5.7.3.4
OID=1.3.6.1.5.5.7.3.5
OID=1.3.6.1.5.5.7.3.6
OID=1.3.6.1.5.5.7.3.7
OID=1.3.6.1.5.5.7.3.8
OID=1.3.6.1.5.5.7.3.9
OID=1.3.6.1.4.1.311.10.3.4
OID=1.3.6.1.4.1.311.54.1.2
[RequestAttributes]
CertificateTemplate = MySpecialTemplate ;Omit line if CA is a stand-alone CA
SAN="my-server.blahblah.com"
YOU SHOULD MAKE A TEMPLATE ON THE CA USING THE 2003 (NOT ALL MICROSOFT PRODUCTS CAN UTILIZE 2008 TEMPLATES -- I KNOW STUPID HUH) COPY IT FROM DOMAIN CONTROLLER AND THROW THE KITCHEN SINK AT THE OIDS

Just make your connection as trust all. Then it will no longer need certificates. Check out javax.net.sslTrustManager.

Related

Adding user password to an AD with PHP

My API (wrote in PHP) needs to register a new user in an AD. It's like the userPassword attribute does not set the password of the user (so he cannot log in).
Things I've tried :
Send the password in plain text : nOK
Send the password with Base64 encoding : nOK
I've read an article (see here) about unicodePwd and the use of LDAPS, but I'm not really sure how to implement this.
I'm actually working with a non-TLS LDAP connection (it's on a local network so I don't need it) on Win2k16 (latest version).
Domain is secureconnect.online (in my code it's .local but don't mind about it).
Here's my code :
public function addUser()
{
// LDAP variables
$ldap_username = $this->ldap_creds['username'];
$ldap_password = $this->ldap_creds['password'];
$ldapuri = $this->ldap_creds['uri'];
// LDAP connection
$link_id = ldap_connect($ldapuri);
if ($link_id) {
ldap_set_option($link_id, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_bind($link_id, $ldap_username, $ldap_password);
$lastname = strtolower($this->validFormData[0]);
$firstname = strtolower($this->validFormData[1]);
$username = $firstname . $lastname;
$display_name = ucwords($firstname) . " " . ucwords($lastname);
$unhashed_pass = $this->validFormData[8];
$encoded_newPassword = "{SHA}" . base64_encode(pack("H*", sha1($unhashed_pass)));
$adduserAD["cn"] = $username;
$adduserAD["givenname"] = ucwords($firstname);
$adduserAD["sn"] = ucwords($lastname);
$adduserAD["sAMAccountName"] = $username;
$adduserAD['userPrincipalName'] = $this->validFormData[2];
$adduserAD["objectClass"] = "user";
$adduserAD["displayname"] = $display_name;
$adduserAD["userPassword"] = $encoded_newPassword;
$adduserAD["userAccountControl"] = "544";
$adduserAD['postalCode'] = $this->validFormData[5];
// Add city
$adduserAD['l'] = $this->validFormData[6];
// Add street address
$adduserAD['streetAddress'] = $this->validFormData[4];
$dn = 'OU=Users-VPN,DC=secureconnect,DC=local';
$base_dn = 'cn=' . $adduserAD['cn'] . ',' . $dn;
$req = ldap_add($link_id, $base_dn, $adduserAD);
if ($req) {
$this->result = $username;
ldap_close($link_id);
} else {
$this->result = '{"error":"Contact Administrator"}';
}
} else {
$this->result = '{"error":"Cannot Connect To Ldap Server"}';
}
return $this->result;
}
Thank's in advance !
EDIT :
So, I've installed an AD LDS with a trusted root certificate. Now when I'm trying to connect with TLS to the server through my API, I'm stuck at this error :
Warning: ldap_start_tls(): Unable to start TLS: Can't contact LDAP server
Here's the code :
/**
* Method used to add and user to an LDAP annuary.
* #return bool|string
*/
public function addUser()
{
// LDAP variables
$ldap_username = $this->ldap_creds['username'];
$ldap_password = $this->ldap_creds['password'];
$ldapuri = $this->ldap_creds['uri'];
// Connexion LDAP
$link_id = ldap_connect($ldapuri);
if ($link_id) {
ldap_set_option($link_id, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_start_tls($link_id);
echo "yeet";
ldap_bind($link_id, $ldap_username, $ldap_password);
Everything beyond this snippet is the same as above.
What should I do ? Do I need to import the certificate to the Web server ?
We have some documentation that explains the process and requirements for using LDAP for Setting and Changing Microsoft Active Directory Passwords
Yes you need encrypted connection
By Default you need to use
UnicodePwd (unless you Enable UserPassword in Microsoft Active
Directory)
And there is a sample in JAVA that shows how we have performed the operation. Usually it works out best to create the user and then set the UnicodePwd value.
We are not real sure why this is the case but issues have been encountered when attempting to both in one operation.
Additionally, often depending on your Microsoft Active Directory settings users are created as disabled and may need to be enabled to be effective.

Ubuntu server able to connect via LDAP but not LDAPS though LDP shows SSL is enabled

I've installed Ubuntu Desktop 18.04 and LAMP, enabled LDAP, etc. I am able to connect via LDAP and port 389. When I attempt to connect with LDAPS on 636, I get blocked out. I've used LDP in Windows to connect to the server to verify SSL is enabled and I can connect to the server using SSL within the LDP program.
This is a fresh virtual machine with a bridged network connection running Ubuntu 18.04 desktop. I've tried multiple sets of code and different AD user accounts from basic user accounts to domain admin accounts.
<?php
$ldaphost = "ldaps://my.domain.controller"; //edited for security purposes
$ldapport = 636;
$lconn = ldap_connect($ldaphost) // also tried $lconn = ldap_connect($ldaphost,$ldapport); to no avail
or die("Could not connect to host!");
As mentioned, it works if I change it to standard LDAP so I know that's enabled correctly. I've been searching online for hours and nothing else has helped.
The LDP program provides the following information:
0x0 = ldap_unbind(ld);
ld = ldap_sslinit("my.domain.controller", 636, 1);
Error 0 = ldap_set_option(hLdap, LDAP_OPT_PROTOCOL_VERSION, 3);
Error 0 = ldap_connect(hLdap, NULL);
Error 0 = ldap_get_option(hLdap,LDAP_OPT_SSL,(void*)&lv);
Host supports SSL, SSL cipher strength = 256 bits
Established connection to my.domain.controller.
Retrieving base DSA information...
Getting 1 entries:
EDIT: Ended up being a cert error. They weren't set up properly by the previous administration. Working on fixing them now.
You are going to need to verify a few things:
CA cert from the DC is installed on the Ubuntu Server.
SSL cert signed from the CA with the FQDN (or IP- depending on how your LDAPS uri is written) in the CN of the cert is installed on the Ubuntu Server.
If using openldap for any part of connecting, modify your ldap.conf like so:
BASE dc=domain,dc=com
URI ldaps://dc.domain.com:636
TLS_CACERT /path/to/ca-cert.cer
TLS_REQCERT DEMAND
and then ldap search should return results with a query similar to:
ldapsearch -x -H ldaps://dc.domain.com -D 'CN=LDAP-bind,OU=Service Accounts,OU=Accounts,DC=domain,DC=com' -W -b 'OU=Accounts,DC=domain,DC=com'
These are the instructions I followed when I setup my web apps to authenticate over LDAPS → Enable LDAP over SSL for Microsoft Active Directory servers
This is the code I use to test my LDAPS connection from different client servers.
class LDAP {
public function connect($host, $user, $pass){
$ds = ldap_connect($host);
if(!ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)){
print "Could not set LDAPv3";
} else {
$ldap = ldap_bind($ds, $user, $pass);
}
if(strpos($host, 'ldaps://') !== false){
$ssl = ' over SSL';
$host = str_replace('ldaps://', '', $host);
} else {
$ssl = null;
$host = str_replace('ldap://', '', $host);
}
if($ldap) {
//$host = str_replace('ldap://', replace, subject)
echo '<b>LDAP</b> : <u>Microsoft AD</u> <br /><br />
Connection to <u>' . $host . '</u>' . $ssl . ' was successful! <br /><br />
[WebServer] ←→ [LDAP Server] <br /><br />
<b>Status:</b> <u>Up</u> ✔ <br />';
} else {
echo 'Connection to <u>' . $host . '</u>' . $ssl . ' was NOT successful. Please try again. <br /><br />
[WebServer] ←x→ [LDAP Server] <br /><br />
<b>Status:</b> <u>Down</u> ✖ <br />';
}
}
public function disconnect(){
$ldap = null;
}
} # class ldap
$LDAP = new LDAP();
$host = "ldaps://dc.domain.com";
$user = "svc.ldap#domain.com";
$pass = "password1";
$LDAP->connect($host, $user, $pass);
$LDAP->disconnect();
// echo 'HOST['.$host.'] USER['.$user.']'; // toggle to troubleshoot db connection

Set password when creating a new Active Directory user account in PHP

This PHP script creates enabled user accounts in Active Directory without a password. How do I set the password?
<?php
$examplePassword = "34mlrfm$sxkf";
$WinTimestamp = "131196672000000000" //30-09-16 00:00:00
//Create unicode password
function encodePassword($password) {
$password="\"".$password."\"";
$encoded="";
for ($i=0; $i <strlen($password); $i++){ $encoded.="{$password{$i}}\000";}
return $encoded;
}
//Build Active Directory record
$ldaprecord["accountExpires"] = $winTimestamp;
$ldaprecord["UserAccountControl"] = "544"; //544 - Account enabled, require password change
$ldaprecord['userPassword'] = encodePassword($examplePassword);
$ldaprecoed['otherAttributes'] = "Truncated from question";
$ds = ldap_connect($AD_server); // Connect to Active Directory
if ($ds) {
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
$r = ldap_bind($ds, $AD_Auth_User, $AD_Auth_PWD); //Bind
$r = ldap_add($ds,$dn,$ldaprecord); //Create account
ldap_close($ds); //Close connection
}
?>
I've tried different password encoding methoods.
I've also tried inserting the password into $ldaprecord["unicodepwd"]. Which results in "Server is unwilling to perform" error.
I've got it working. You can only set passwords over an SSL connection, thanks #stuartbrand
Either encrypt traffic on 389 using ldap_start_tls() or connect on 636 using $ds = ldap_connect('ldaps://'.$AD_server);
Password should be inserted into the $ldaprecord["unicodepwd"] attribute.

PHP LDAP binding AD with the server's user account

I have some code that uses PHP and LDAP to connect to AD:
$host = 'ldap://stack.overflow.com';
$port = 389;
$username = 'stackOverflow';
$password = 'IaMP4ssWord';
$dn = 'CN=Users, DC=STACK, DC=OVERFLOW, DC=COM';
$cond = '(&(objectcategory=user)(displayname=*))';//All users that have a displayname
if($ldap = ldap_connect($host, $port))
{
if(ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3))
{
if(ldap_bind($ldap, $username, $password))
{
$attrs = array('displayname', 'mail');
if($rs = ldap_search($ldap, $dn, $cond, $attrs))
{
$results = ldap_get_entries($ldap, $rs);
echo "<pre>";print_r($result);echo "</pre>";//Print the results
}
}
else
{ echo 'Binding failed';}
}
else
{ echo 'Setting options failed';}
}
else
{ echo 'Connection failed'; }
Now this code works just fine. It print out every user that has a displayname in AD.
Problem is for the username/password binding i am using my own user credential to bind to the server.
I would like to know if there is a way to bind using the servers credentials.
I am setup using PHP 5.3 + IIS on windows server 2008 R2 for both the server with IIS and the one that has AD.(two different VM).
I also know that IIS has a AD account named IISStackOverflow but I don't know the password or even if it has a password...
Thanks!
Oh! I tried changing $username to IISStackOverflow and $password to ''
But it gave invalid credential error.
--EDIT--
Do I have to do the binding part at all? (If I am only reading data)
As you run it from server itself, and you just want to read I would try to use :
...
if(ldap_bind($ldap))
...
According to PHP documentation if bind_rdn and bind_password are not specified, an anonymous bind is attempted.
Then if your anonymous logon is refused (this should not be, because running under IIS on the server your code is at least executed as a domain user) you will find there how to enable anonymous LDAP binds to Windows Server. This used to work forme on W2K8, Inever test it on W2K12.

Do I need ldap.conf when I connect to Active Directory using PHP?

I'm trying to authenticate to Active Directory using PHP (5.3.2)/Apache (2.0.59)/Windows (2003).
However, I'm getting the following error:
ldap_start_tls() [function.ldap-start-tls]: Unable to start TLS: Connect error in E:\my_page.php on line 15
Here is my current script:
putenv('LDAPTLS_REQCERT=never');
$resource = ldap_connect("xxx")
or die("Failed to connect to LDAP server.");
echo "Connected to LDAP server.<br />";
//these options may not be necessary in all environments
ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($resource, LDAP_OPT_REFERRALS, 0);
$result = ldap_start_tls($resource) or die("Failed to start TLS.<br />");
echo "Started TLS.<br />";
$result = ldap_sasl_bind($resource, NULL, '', 'GSSAPI', 'xxx', '', '')
or die("Failed to GSSAPI bind.<br />");
echo "GSSAPI bound.";
I've looked at this question for help, however, I keep seeing references to ldap.conf.
Is this for OpenLDAP as in, the LDAP server your connecting to? If it is, I could ignore it due to using an existing enteprise Active Directory ?
Or is this for the PHP libraries connecting to an LDAP server (ie. ldap_connect())?
Edit #1
Screenshot of Wireshark...
I see in there, unknown CA... how would I go solving this (looking online ATM).
Edit #2
Update, I'm now getting a different error. I created ldap.conf on c:\ and c:\openldap\sysconf
Content of ldap.conf:
#
# LDAP Defaults
#
TLS_REQCERT never
Now, it's stuck at the ldap_sasl_bind method which is normal - it's not installed.
Edit #3
Final product:
function isAuthenticated($user, $pass){
//init
$ldap_server = "";
$ldap_user = "";
$ldap_pass = "";
$ldap_dn = "";
$ldap_filter_fields = array("dn","cn","samaccountname");
//establish connection
$ldap_conn = ldap_connect($ldap_server)
or die("Failed to connect to LDAP server.");
//these options may not be necessary in all environments
ldap_set_option($resource, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($resource, LDAP_OPT_REFERRALS, 0);
//Magic happens here, encrypted tunnel starts!
$result = ldap_start_tls($ldap_conn) or die("Failed to start TLS.<br />");
$out = 0;
//connect using our known user
if($bind = ldap_bind($ldap_conn, $ldap_user, $ldap_pass)){
//search for the user
$ldap_search_results = ldap_search($ldap_conn, $ldap_dn, "samaccountname=".$user, $ldap_filter_fields) or die ("Failed to search LDAP");
//get entry
$ldap_record = ldap_get_entries($ldap_conn, $ldap_search_results);
debug($ldap_record);
if($ldap_record["count"] > 0){
//try to authenticate user here
if($bind2 = #ldap_bind($ldap_conn, $ldap_record[0]["dn"], $pass))
$out = 1;
else
//wrong password
$out = 0;
}
else
//user wasn't found
$out = 3;
}
else
//something happened when connecting with our ldap_user
$out = 2;
return $out;
}
You're on the right track with your unknown CA. I have had a similar issue with PHP on CentOS connecting to AD. I had to export the CA certificate from the AD server and configure it to be trusted on the CentOS system, which involved copying the certificate to /etc/openldap/cacerts and running OpenSSL's c_rehash. Unfortunately, I'm not sure how to tell you to get that same setup working under Windows.
Yes you are required to change the ldap.conf file and change the value of TLS_REQCERT to demand if you are trying to connect with the AD for which the trust has not been established on your host machine running Apache. If you have certificate from a trusted CA i.e. already installed on the machine OS keystore then it may run with the TLS_REQCERT set to never otherwise you will have to explicitly give the certificates and change the variable TLS_REQCERT.

Categories