PHP LDAP notify on expiring passwords

Install an configure tacacs+ on Debian 8 – part 1
November 17, 2016
Show all

PHP LDAP notify on expiring passwords

Hello,

Bellow is a script I put together to notify users via e-mail about their domain account password expiration.

The script is set to:

  • notify users when there are 7 or 3 or 1 days left until the password will expire;
  • create ticket via e-mail when there are 7, 3, 1 days left until the password will expire, but there’s no e-mail address set for that account;
  • create ticket via e-mail when password already expired  ( this can be easily changed to “spam” the user). Please note this will basically open a ticket/day until the problem is solved.
  • create ticket via e-mail when password already expired and also there’s no e-mail address set for that account. Please note this will basically open a ticket/day until the problem is solved.

So, basically, the script is composed of 5 files: the main script, handling the data and 4  “e-mail templates”, 1 for each situation described above.

Some problems I encountered while working on this script:

  • You will probably want to exclude from the accounts to be verified the following:
    • guest and administrator accounts
    • service accounts
    • other accounts, which,  for whatever reason, have the password set to never expire.
  • while retrieving the data, you will need to escape [count] from the array generated by ldap_get_entries function. I solved this by using a little function. Others may consider something else.

Prerequisites:

  • you will need a service account set in your AD;
  • a webserver to handle the PHP and PHP Ldap ( I used Debian 8 & Lamp ). For an easy tutorial, please check this link
  • access to the system crontab ( or, if using other OS, a way to schedule the script to run once every day ) . Check this link if you need help understanding the crontab mechanism.

Main script:

I tried to document all steps in the script, however, you can just remove the comments.

Also, make sure you are using a correct base dn, that includes all the user accounts.

<?php
/*
 SCRIPT  : ldap_notify_expire_pass.php
 AUTHOR  : Marin Nedea										
 WEBSITE : http://sysadmins.tech								
 BLOG    : http://sysadmins.tech								
 CREATED : 09-11-2016										
 COMMENT : Script to notify AD users via e-mail when 
		   their password is about to expire or to 
		   notify the admins team via e-mail/ticket.
 LICENSE : Copyright (C) 2016 - Marin Nedea @ http://sysadmins.tech

		   This program is free software: you can redistribute it and/or modify
		   it under the terms of the GNU General Public License as published by
		   the Free Software Foundation, either version 3 of the License, or
		   at your option) any later version.

  		   This program is distributed in the hope that it will be useful,
		   but WITHOUT ANY WARRANTY; without even the implied warranty of
		   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		   GNU General Public License for more details.

		   You should have received a copy of the GNU General Public License
		   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

set_time_limit(30);
ini_set('error_reporting', E_ALL);
ini_set('display_errors',0);

// LDAP server config
$ldapserver = 'example.com';
$ldapuser   = 'serviceuser'; 
$ldappass   = 'password_here';
$base_dn    = 'CN=Users,DC=example,DC=com';


// Connect to AD
$ldapconn = ldap_connect($ldapserver) or die('Could not connect to LDAP server.');
// Using LDAP version 3. Change the version to 2 if needed
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
// If you try to perform the searches on Windows 2003 Server Active Directory or above, it seems that you have to set the LDAP_OPT_REFERRALS option to 0.
// Without this, you will get "Operations error" if you try to search the whole AD (using root of the domain as a $base_dn).
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);

if($ldapconn) {
    // Binding to AD
    $ldapbind = ldap_bind($ldapconn, $ldapuser, $ldappass) or die ('Error trying to bind: '.ldap_error($ldapconn));
	// If bind was successful
	if ($ldapbind) {
        // echo 'Connected to LDAP server<br /><br />';
		
		/*	
		I know that (objectCategory=person) and (objectClass=user) are kind of redundant, but the result will be correct;
		Remove from the search the default Guest account, the Adminstrator Account, the group containing the Service
		Accounts  and the accounts marked for deletion.
		*/ 
		
		//Set a search filter
		$filter='(&(objectCategory=person)(objectClass=user)(!(sAMAccountName=Guest))(!(sAMAccountName=Administrator)))';
		
		// Set an array of attributes you wish to retrieve for each entry
		$attrs = array('cn', 'displayname', 'sn', 'givenname', 'mail', 'samaccountname', 'pwdlastset', 'useraccountcontrol', 'memberof');
		// Set the search
        $result = ldap_search($ldapconn, $base_dn, $filter, $attrs) or die ('Error in search query: '.ldap_error($ldapconn));  
		
		// Get an array containg the results
		$data = ldap_get_entries($ldapconn, $result);
	  	  
		// Function to remove [count] from the ldap_get_entries result
		// See http://stackoverflow.com/questions/5997815/php-strip-count-value-from-array for details.
		
		function rCountRemover($arr) {
		  foreach($arr as $key=>$val) {
			// (int)0 == "count", so we need to use ===
			if($key === "count")
			  unset($arr[$key]);
			elseif(is_array($val))
			  $arr[$key] = rCountRemover($arr[$key]);
		  }
		  return $arr;
		}

		// Renew $data array using the function above
		$data = rCountRemover($data);
		
		// Print number of entries found
		// echo 'Results: ' . ldap_count_entries($ldapconn, $result) . '<br />;
		 
    
		// Get the data parsed for each entry
		foreach($data as $key => $value) {
		
			$user_display_name = ucfirst($userinfo['displayname'][0];
			$username = $value['samaccountname'][0]; 
			$user_email = $value['mail'][0];
			  
			// Get the time since last password change 
			$fileTime 		= $value['pwdlastset'][0];
			$winSecs      	= (int)($fileTime / 10000000); // divide by 10 000 000 to get seconds
			$unixTimestamp 	= ($winSecs - 11644473600); // 1.1.1600 -> 1.1.1970 difference in seconds	
			$timestamp = date(DateTime::RFC822, $unixTimestamp);  // time passed since Last Password Change
			
			$date2 = date_create($timestamp);
			$datetime2 = date_format($date2, 'Y-m-d');
			
			$since = strtotime($datetime2);
			$today = time();
			$difference = $today - $since;
			
			// Get the number of days till the password will expire
			$expire_days =  floor($difference / (60 * 60 * 24));  
						
			// Get the remaining days till the password expire
			$remaining_days = 90 - $expire_days;
			
			/*
			In order to avoid spam ( you don't wanna send an e-mail every day, do you?), I decided 
			to send e-mails when the remainig days until the password will expire are 1, 3 or 7. 
			So .. I'm making a simple array containg the numbers 1,3 7 
			*/
			
			$a = array(1,3,7);
		
			// checking if the value of $expire_days will match values in our array $a
			if (in_array(''.$expire_days.'', $a)){			
				
				// Preparing to send e-mail. We need to check if we have an e-amil address to send to.
				// Also, please check https://css-tricks.com/sending-nice-html-email-with-php/ for information
				// on sending HTML formated e-mail.
			
				if(empty($user_email)){					
					
					/*
					If there's no e-mail address set for the user, we need to notify someone (an admin member, or 
					a ticketing system) by e-mail, to update the account for that user with an e-mail address.
					We are using OTRS as ticketing system here, so I set an e-mail account on our e-mail server 
					just for the OTRS, and I'm fetching in OTRS the e-mails every 5 minutes.
					The advantage of this is that, in case the e-mail address of your recipient is set wrong in 
					the Active Directory, when sending the e-mail you will also get an error reply mail on your 
					inbox. Since OTRS is fetching the e-mails in that inbox, it will creat a ticket and let you know
					that a specific e-mail address is incorrect/doesn't exists.
					If you are using a different method, you could use, with similar result, a service e-mail account that
					everyone in your team is reading frequently.
					*/
					
					// Setting the $user_email to a hardcoded value, corresponding to our ticketing system/service e-maila ccount
					$user_email = 'ticketing@example.com';
					
					// Including the formated e-mail to create a ticket.
					include('ticketing_mail.php');
					/*
					echo '<pre>';
					echo $username.'<br />';
					echo $user_email.'<br />';
					echo $remaining_days.'<br />';
					echo 'creating ticket - email empty<br />';
					echo '</pre>';
					*/ 
				} else {						
			
					// Including the script to send e-mail to the user 
					include('reset_pass_mail.php');	
					/*
					echo '<pre>';
					echo $username.'<br />';
					echo $user_email.'<br />';
					echo $remaining_days.'<br />';
					echo 'notify user - will expire pass<br />';
					echo '</pre>';
					*/
				}
				
				
							
			} else if($expire_days > 90){  // If password already expired
				
				if(empty($user_email)){				

					// Setting the $user_email to a hardcoded value, corresponding to our ticketing system/service e-maila ccount
					$user_email = 'ticketing@example.com';
					
					// Including the formated e-mail to create a ticket.
					include('ticketing_expired_no_email_mail.php');
					/*
					echo '<pre>';
					echo $username.'<br />';
					echo $user_email.'<br />';
					echo $remaining_days.'<br />';
					echo 'creating ticket - email empty and password expired<br />';
					echo '</pre>';
					*/
							
				} else {
					
					// Create a ticket in ticketing system, letting know the admins the user
					// didn't renew his password.
					include('expire_password_ticketing_mail.php');					
					
					/*
					echo '<pre>';
					echo $username.'<br />';
					echo $user_email.'<br />';
					echo $remaining_days.'<br />';
					echo 'creating ticket - password expired<br />';
					echo '</pre>';
					*/
				} 
			
			} /*  // Uncomment this if you wish to list the accounts without password problems.
			
				else { // Close if password already expired  
				
				echo '<pre>';
				echo $username.'<br />';
				echo $user_email.'<br />';
				echo $remaining_days.'<br />';
				echo 'All OK<br />';	
				echo '</pre>';
							
			}
			*/
		
			// NOTE:  
			// You need to replace "-fserver_email@example.com" with your actual e-mail account used to send 
			// e-mails. 
			// Please make sure you keep the "-f" (envelope FROM: ) in front of the e-mail address! If your e-mail
			// is, let's say, no-reply@example.com, in the script you should have "-fno-reply@example.com". Please check
			// http://stackoverflow.com/questions/179014/how-to-change-envelope-from-address-using-php-mail for more.
		
			// Sending the actual mail
			mail($to, $subject, $message, $headers, '-fserver_email@example.com');

			
		} // close foreach

	} else { // Close if bind 
			
        echo 'LDAP bind failed...';
    
	}

} // close LDAP connection

ldap_close($ldapconn);
?>

E-mail templates:

Case 1: password about to expire, the account has an e-mail address set.

<?php
/*
 SCRIPT  : will_expire_with_email.php
 AUTHOR  : Marin Nedea										
 WEBSITE : http://sysadmins.tech							
 BLOG    : http://sysadmins.tech							
 CREATED : 09-11-2016											
 COMMENT : This is a mail template used to notify users via	
           e-mail when their AD password is about to expire.
 LICENSE : Copyright (C) 2016 - Marin Nedea @ http://sysadmins.tech

		   This program is free software: you can redistribute it and/or modify
		   it under the terms of the GNU General Public License as published by
		   the Free Software Foundation, either version 3 of the License, or
		   at your option) any later version.

  		   This program is distributed in the hope that it will be useful,
		   but WITHOUT ANY WARRANTY; without even the implied warranty of
		   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		   GNU General Public License for more details.

		   You should have received a copy of the GNU General Public License
		   along with this program.  If not, see <http://www.gnu.org/licenses/>.		   
*/

$subject = 'Your Domain Account password is about to expire in '. $expire_days .' days';

// Prepare the e-mail to be sent 
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";	
$headers .= 'To:<'.$usermailaddress.'>'."\r\n";
$headers .= 'From: Support Team <no-reply@example.com>' . "\r\n";
$headers .= 'Subject:'.$subject.''."\r\n";

$message = '<html>
				<body>				
					<p>Hello ' .$user_display_name.', <br />
					<br />
					The password for your Domain account ' .$username. ' will expire in ' .$expire_days.' days.
					<br /><br />					
					<p> 
					To change your domain Account password, please <a href="#">click here</a> and follow instructions in the page.<br />
					If you are unable to change the password, for whatever reason, please open a ticket to us via the ticketing system.<br /><br />			
					
					<strong>Please note this is an automated e-mail and <i>shall</i> not be replied!</strong><br /><br />
					<br />
					Best Regards,<br />
					The Support Team
					</p>
				</body>
			</html>';
?>

Case 2: Password about to expire, the account has no e-mail address set, opening ticket.

<?php
/*
 SCRIPT  : will_expire_no_email.php
 AUTHOR  : Marin Nedea										
 WEBSITE : http://sysadmins.tech								
 BLOG    : http://sysadmins.tech							
 CREATED : 09-11-2016										
 COMMENT : This is a mail template used to notify admins via	
           e-mail/ticket when someone's AD account password 
		   is about to expire, but there's no e-mail address
		   set for that account.
 LICENSE : Copyright (C) 2016 - Marin Nedea @ http://sysadmins.tech

		   This program is free software: you can redistribute it and/or modify
		   it under the terms of the GNU General Public License as published by
		   the Free Software Foundation, either version 3 of the License, or
		   at your option) any later version.

  		   This program is distributed in the hope that it will be useful,
		   but WITHOUT ANY WARRANTY; without even the implied warranty of
		   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		   GNU General Public License for more details.

		   You should have received a copy of the GNU General Public License
		   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

$subject = 'The password for the Domain Account ' .$username. ' will expire in '. $expire_days .' days';

// Prepare the e-mail to be sent 
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";	
$headers .= 'To:<tickets@example.com>'."\r\n";
$headers .= 'From: Support Team <support@example.com>' . "\r\n";
$headers .= 'Subject:'.$subject.''."\r\n";

$message = '<html>
				<body>				
					<p>Hello ' .$user_display_name.', <br />
					<br />
					The password for the Domain account ' .$username. ' will expire in ' . $expire_days .' days but there\'s no e-mail address set for this account.
					<br /><br />					
					<p> 
					Please verify if the account and update the e-mail address.<br /><br />			
					
					<strong>Please note this is an automated e-mail and <i>shall</i> not be replied!</strong><br /><br />
					<br />
					Best Regards,<br />
					The Support Team
					</p>
				</body>
			</html>';
?>

Case 3: Password expired – opening ticket.

<?php 
/*
 SCRIPT  : expired_with_mail.php
 AUTHOR  : Marin Nedea										
 WEBSITE : http://sysadmins.tech							
 BLOG    : http://sysadmins.tech							
 CREATED : 09-11-2016										
 COMMENT : This is a mail template used to notify admins via	
           e-mail/ticket when someone's AD account password 
		   is already expired.
 LICENSE : Copyright (C) 2016 - Marin Nedea @ http://sysadmins.tech

		   This program is free software: you can redistribute it and/or modify
		   it under the terms of the GNU General Public License as published by
		   the Free Software Foundation, either version 3 of the License, or
		   at your option) any later version.

  		   This program is distributed in the hope that it will be useful,
		   but WITHOUT ANY WARRANTY; without even the implied warranty of
		   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		   GNU General Public License for more details.

		   You should have received a copy of the GNU General Public License
		   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

$subject = 'The password for the Domain Account ' .$username. ' expired';

// Prepare the e-mail to be sent 
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";	
$headers .= 'To:<tickets@example.com>'."\r\n";
$headers .= 'From: Support Team <support@example.com>' . "\r\n";
$headers .= 'Subject:'.$subject.''."\r\n";

$message = '<html>
				<body>				
					<p>Hello ' .$user_display_name.', <br />
					<br />
					The password for the Domain account ' .$username. ' expired ' . $remaining_days .' days ago.
					<br /><br />					
					<p> 
					Please verify if the account owner still needs this account.<br /><br />			
					
					<strong>Please note this is an automated e-mail and <i>shall</i> not be replied!</strong><br /><br />
					<br />
					Best Regards,<br />
					The Support Team
					</p>
				</body>
			</html>';
?>

Case 4: Password expired and also account has no e-mail address set – opening ticket.

<?php 
/*
 SCRIPT  : expired_no_email.php
 AUTHOR  : Marin Nedea										
 WEBSITE : http://sysadmins.tech							
 BLOG    : http://sysadmins.tech							
 CREATED : 09-11-2016										
 COMMENT : This is a mail template used to notify admins via	
           e-mail/ticket when someone's AD account password 
		   is already expired, but there's no e-mail address
		   set for that account.
 LICENSE : Copyright (C) 2016 - Marin Nedea @ http://sysadmins.tech

		   This program is free software: you can redistribute it and/or modify
		   it under the terms of the GNU General Public License as published by
		   the Free Software Foundation, either version 3 of the License, or
		   at your option) any later version.

  		   This program is distributed in the hope that it will be useful,
		   but WITHOUT ANY WARRANTY; without even the implied warranty of
		   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		   GNU General Public License for more details.

		   You should have received a copy of the GNU General Public License
		   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

$subject = 'The password for the Domain Account ' .$username. ' expired';

// Prepare the e-mail to be sent 
$headers  = 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=utf-8' . "\r\n";	
$headers .= 'To:<tickets@example.com>'."\r\n";
$headers .= 'From: Support Team <support@example.com>' . "\r\n";
$headers .= 'Subject:'.$subject.''."\r\n";

$message = '<html>
				<body>				
					<p>Hello ' .$user_display_name.', <br />
					<br />
					The password for the Domain account ' .$username. ' expired ' . $remaining_days .' days ago.<br />
					Also, please note there is no e-mail address set for this account.
					<br /><br />					
					<p> 
					Please verify if the account owner still needs this account.<br />
					<br />			
					<strong>Please note this is an automated e-mail and <i>shall</i> not be replied!</strong><br /><br />
					<br />
					Best Regards,<br />
					The Support Team
					</p>
				</body>
			</html>';
?>

Now, once you have the whole script, upload it to your web server to the directory you usually keep your custom apps/scrips ( e.g /usr/local ).

Now, to have your script run every day at a specific time, make a new entry in crontab. To do that, open a shell to your server with:

$ crontab -e

and add the following line at the end of the crontab file

10 1 * * * /usr/bin/php5 /usr/local/ldap_notify/ldap_notify_expire_pass.php

The above line will have the script  “/usr/local/ldap_notify/ldap_notify_expire_pass.php” run every day, at 01:10 AM .


Download Files

 

 

Marin Nedea
Marin Nedea
I'm passionate about open source software and technologies. In my spare time I build simple and functional websites from scratch, using PHP+HTML5+CSS3+MySQL and when I'm bored, I write simple PHP_CLI or bash scripts to play around on my Linux machine.

2 Comments

  1. Amie Ritter says:

    Hi there would you mind letting me know which hosting company you’re using?
    I’ve loaded your blog in 3 different browsers and I must say this blog loads a lot quicker then most.

    Can you recommend a good hosting provider at a honest price?

    Cheers, I appreciate it!

Leave a Reply

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close