From b8064a482bee86071817e7db1661f19cf4a83646 Mon Sep 17 00:00:00 2001 From: Brooke Hedrick Date: Wed, 15 Nov 2017 13:58:53 -0600 Subject: [PATCH] Added Active Directory LDAP hostname lookup If you are using Active Directory, you likely have multiple LDAP servers. Which servers your Windows clients use is controlled by a DNS entry for sites and services. This DNS entry looks like _ldap._tcp.corporate._sites.yourdomain.com . This code change allows for the LDAP host name to be looked up automatically based on the DNS entry. This way if your Active Directory architecture changes (different Domain Controllers / LDAP servers ), you won't need to update this plugin's configuration. This is only enabled if you set the new LDAPAUTH_DNS_SITES_AND_SERVICES configuration value. Please see documentation in the README.md. --- README.md | 5 +++++ plugin.php | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 16459fc..d1e20be 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ To define the type of user cache used: To automatically add LDAP users to config.php: * define( 'LDAPAUTH_ADD_NEW', true ); // (optional) Add LDAP users to config.php + +To use Active Directory Sites and Services DNS entry for LDAP server name lookup + * define( 'LDAPAUTH_DNS_SITES_AND_SERVICES', '_ldap._tcp.corporate._sites.yourdomain.com' ); // If using Active Directory, with multiple Domain Controllers, the safe way to use DNS to look up your active LDAP server names. If set, it will be used to override the hostname portion of LDAPAUTH_HOST. + * define( 'LDAPAUTH_HOST', 'ldap://'); // LDAP protocol without hostname. You can use 'ldaps://' for LDAP with TLS. + NOTE: This will require config.php to be writable by your webserver user. This function is now largely unneeded because the database based cache offers similar benefits without the need to make config.php writeable. It is retained for backwards compatability Troubleshooting diff --git a/plugin.php b/plugin.php index 48b2529..017dc20 100644 --- a/plugin.php +++ b/plugin.php @@ -60,6 +60,61 @@ function ldapauth_environment_check() { yourls_add_filter( 'is_valid_user', 'ldapauth_is_valid_user' ); +function ldapauth_shuffle_assoc($list) { + if (!is_array($list)) return $list; + + $keys = array_keys($list); + shuffle($keys); + $random = array(); + foreach ($keys as $key) { + $random[$key] = $list[$key]; + } + return $random; +} + +// return list of Active Directory Ldap servers that are associated with a site and service +// example for $site = = '_ldap._tcp.corporate._sites.company.com' +function ldapauth_get_ad_servers_for_site() { + $results = []; + $ad_servers = dns_get_record(LDAPAUTH_DNS_SITES_AND_SERVICES, DNS_SRV, $authns, $addtl); + foreach ($ad_servers as $ad_server) { + array_push($results, $ad_server['target']); + } + $results = ldapauth_shuffle_assoc($results); #randomize the order + return $results; +} + +// returns ldap connection +function ldapauth_get_ldap_connection() { + if (defined('LDAPAUTH_DNS_SITES_AND_SERVICES')) { + $connection = NULL; + $ldap_servers = ldapauth_get_ad_servers_for_site(); + foreach ($ldap_servers as $ldap_server) { + $ldap_address = LDAPAUTH_HOST . $ldap_server; + try { + $temp_conn = ldap_connect($ldap_address, LDAPAUTH_PORT); # ldap_connect doesn't actually connect it just checks for plausiable parameters. Only ldap_bind connects + if ($temp_conn) { + $connection = $temp_conn; + break; + } else { + error_log('Warning, unable to connect to: ' . $ldap_address . ' on port ' . LDAPAUTH_PORT . '. ' . ldap_error($temp_conn)); + } + } catch (Exception $e) { + error_log('Warning, unable to connect to: ' . $ldap_address . ' on port ' . LDAPAUTH_PORT . '. ' . __FILE__, __FUNCTION__,$e->getMessage()); + } + } + + if ($connection) { + return $connection; + } else { + die("Cannot connect to LDAP for site and service " . LDAPAUTH_DNS_SITES_AND_SERVICES); + } + + } else { + return ldap_connect(LDAPAUTH_HOST, LDAPAUTH_PORT); + } +} + // returns true/false function ldapauth_is_valid_user( $value ) { global $yourls_user_passwords; @@ -106,7 +161,7 @@ function ldapauth_is_valid_user( $value ) { && !empty( $_REQUEST['username'] ) && !empty( $_REQUEST['password'] ) ) { // try to authenticate - $ldapConnection = ldap_connect(LDAPAUTH_HOST, LDAPAUTH_PORT); + $ldapConnection = ldapauth_get_ldap_connection(); if (!$ldapConnection) die("Cannot connect to LDAP " . LDAPAUTH_HOST); ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, 3); //ldap_set_option($ldapConnection, LDAP_OPT_REFERRALS, 0);