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://urbanlinux.com								
 BLOG    : http://urbanlinux.com								
 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://urbanlinux.com

		   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://urbanlinux.com							
 BLOG    : http://urbanlinux.com							
 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://urbanlinux.com

		   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://urbanlinux.com								
 BLOG    : http://urbanlinux.com							
 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://urbanlinux.com

		   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://urbanlinux.com							
 BLOG    : http://urbanlinux.com							
 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://urbanlinux.com

		   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://urbanlinux.com							
 BLOG    : http://urbanlinux.com							
 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://urbanlinux.com

		   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