/** * Handles connecting to LDAP and performing a search for the given SSOID * * @param string $strSSOID * @return mixed WP_Error object on failure, array of user details on success * @TODO this function shares a LOT with wpDirAuth_auth. see if you cant combine them some more */ function wpDirAuth_ConnectAndLookupUser($strSSOID) { $boolFound = false; $strBaseDn = get_site_option('dirAuthBaseDn'); $strPreBindUser = get_site_option('dirAuthPreBindUser', ''); $strPreBindPassword = get_site_option('dirAuthPreBindPassword', ''); $strAccountSuffix = get_site_option('dirAuthAccountSuffix'); $strFilter = get_site_option('dirAuthFilter'); $intEnableSSL = get_site_option('dirAuthEnableSsl'); if ($strAccountSuffix) { $strSSOID .= $strAccountSuffix; } if (!$strFilter || empty($strFilter)) { $strFilter = WPDIRAUTH_DEFAULT_FILTER; } $strFilterQuery = "({$strFilter}={$strSSOID})"; $aryControllers = wpDirAuth_shuffleControllers(explode(',', get_site_option('dirAuthControllers'))); if (is_wp_error($aryControllers)) { return $aryControllers; //there werent any controllers to connect to } /** * @todo if we get a successful connection, cant we break out of the loop before we go through binding and a search? Or is it possible that one DC in the * list might not allow anonymous searching and/or the pre-bind user/pass isnt valid on one of them and we need to try the next in the list? */ foreach ($aryControllers as $strDC) { $rscConnection = wpDirAuth_establishConnection($strDC, $intEnableSSL); if (is_wp_error($rscConnection)) { return $rscConnection; //tls failed to start on the DC } if (!wpDirAuth_bindTest($rscConnection, $strPreBindUser, $strPreBindPassword, $strBaseDn)) { return new WP_Error('login_failed', __('<strong>Error Connecting to LDAP Server</strong><p>' . 'There was an error connecting to your LDAP server (' . htmlentities($strDC, ENT_QUOTES, 'UTF-8') . '). Please see the LDAP error message below for troubleshooting:</p>' . ldap_error($rscConnection))); } //successfully bound, now we need to get the user details return wpDirAuth_retrieveUserDetails($rscConnection, $strBaseDn, $strFilterQuery); } }
/** * Custom LDAP authentication module. * The returned keys are in the same format used by WP for * the wp_insert_user and wp_update_user functions. * * @param string $username LDAP username * @param string $password LDAP password * @return WP_Error object OR array Directory email, last_name and first_name * * @uses WPDIRAUTH_DEFAULT_FILTER * @uses wpDirAuth_bindTest */ function wpDirAuth_auth($username, $password) { global $error, $pwd; $errorTitle = __('<strong>Directory Login Error</strong>: '); $controllers = explode(',', get_option('dirAuthControllers')); $baseDn = get_option('dirAuthBaseDn'); $preBindUser = get_option('dirAuthPreBindUser'); $preBindPassword = get_option('dirAuthPreBindPassword'); $accountSuffix = get_option('dirAuthAccountSuffix'); $filter = get_option('dirAuthFilter'); $enableSsl = get_option('dirAuthEnableSsl'); $returnKeys = array('sn', 'givenname', 'mail'); $isBound = $isPreBound = $isLoggedIn = false; if (count($controllers) > 1) { // shuffle the domain controllers for pseudo load balancing and fault tolerance. shuffle($controllers); } elseif (count($controllers) == 0) { return new WP_Error('no_controllers', $errorTitle . __(' wpDirAuth config error: no domain controllers specified.')); } if ($accountSuffix) { $username .= $accountSuffix; } /** * Only setup protocol value if ldaps is required to help with older AD * @see http://groups.google.com/group/wpdirauth-support/browse_thread/thread/7b744c7ad66a4829 */ $protocol = $enableSsl ? 'ldaps://' : ''; if (!$filter) { $filter = WPDIRAUTH_DEFAULT_FILTER; } $filterQuery = "({$filter}={$username})"; // Connection pool loop - Haha, PooL LooP foreach ($controllers as $dc) { /** * Scan for and use alternate server port, but only if ssl is disabled. * @see Parameters constraint at http://ca.php.net/ldap_connect */ if (strstr($dc, ':')) { list($dc, $port) = explode(':', $dc); } if (!$enableSsl && isset($port)) { $connection = @ldap_connect($protocol . $dc, $port); } else { $connection = @ldap_connect($protocol . $dc); } /** * Copes with W2K3/AD issue. * @see http://bugs.php.net/bug.php?id=30670 */ if (@ldap_set_option($connection, LDAP_OPT_PROTOCOL_VERSION, 3)) { @ldap_set_option($connection, LDAP_OPT_REFERRALS, 0); } if ($preBindUser && $preBindPassword) { /** * Use case 1: Servers requiring pre-binding with admin defined * credentials to search for the user's full DN before attempting * to login. * @see http://dev.wp-plugins.org/ticket/681 */ if ($isPreBound = wpDirAuth_bindTest($connection, $preBindUser, $preBindPassword) === true) { if (($results = @ldap_search($connection, $baseDn, $filterQuery, $returnKeys)) !== false) { if (($userDn = @ldap_get_dn($connection, ldap_first_entry($connection, $results))) !== false) { if (($isBound = wpDirAuth_bindTest($connection, $userDn, $password)) === true) { $isLoggedIn = true; // valid server, valid login, move on break; // valid server, valid login, move on } } } } } elseif (($isBound = wpDirAuth_bindTest($connection, $username, $password)) === true) { /** * Use case 2: Servers that will not let you bind anonymously * but will let the end user bind directly. * @see http://groups.google.com/group/wpdirauth-support/browse_thread/thread/8fd16c05266fc832 */ $isLoggedIn = true; break; // valid server, valid login, move on } elseif (($isBound = @ldap_bind($connection)) === true) { /** * Use case 3: Servers that might require a full user DN to * actually login and therefore let you bind anonymously first . * Try ldap_search + ldap_get_dn before attempting a login. * @see http://wordpress.org/support/topic/129814?replies=34#post-603644 */ if (($results = @ldap_search($connection, $baseDn, $filterQuery, $returnKeys)) !== false) { if (($userDn = @ldap_get_dn($connection, ldap_first_entry($connection, $results))) !== false) { $isInDirectory = true; // account exists in directory if (($isBound = wpDirAuth_bindTest($connection, $userDn, $password)) === true) { $isLoggedIn = true; // valid server, valid login, move on break; // valid server, valid login, move on } } } } } if ($preBindUser && $preBindPassword && !$isPreBound) { return new WP_Error('no_directory_or_prebinding', $errorTitle . __(' wpDirAuth config error: No directory server available for authentication, OR pre-binding credentials denied.')); } elseif ($isInDirectory && !$isBound) { return new WP_Error('could_not_bind_as_user', $errorTitle . __(' Incorrect password.')); } elseif (!$isBound && !$isPreBound) { return new WP_Error('no_directory_available', $errorTitle . __(' wpDirAuth config error: No directory server available for authentication.')); } elseif (!$isLoggedIn) { return new WP_Error('could_not_authenticate', $errorTitle . __(' Could not authenticate user. Please check your credentials.') . " [{$username}]"); } else { /** * Search for profile, if still needed. * @see $results in preceding loop: Use case 3 */ if (!$results) { $results = @ldap_search($connection, $baseDn, $filterQuery, $returnKeys); } if (!$results) { return new WP_Error('noprofile_search', $errorTitle . __('Directory authentication initially succeeded, but no valid profile was found (search procedure).') . " [{$filter}]"); } else { $userInfo = @ldap_get_entries($connection, $results); $count = intval($userInfo['count']); if ($count < 1) { return new WP_Error('noprofile_getentries', $errorTitle . __('Directory authentication initially succeeded, but no valid profile was found ("get entries" procedure).') . " [{$filter}]"); } elseif ($count > 1) { return new WP_Error('not_unique', $errorTitle . __('Directory authentication initially succeeded, but the username you sent is not a unique profile identifier.') . " [{$filter}]"); } else { $email = isset($userInfo[0]['mail'][0]) ? $userInfo[0]['mail'][0] : ''; $lastName = isset($userInfo[0]['sn'][0]) ? $userInfo[0]['sn'][0] : ''; $firstName = isset($userInfo[0]['givenname'][0]) ? $userInfo[0]['givenname'][0] : ''; return array('email' => $email, 'last_name' => $lastName, 'first_name' => $firstName); } } } }