diff -urN ../2.6.4+0.orig/src/config.php ./src/config.php --- ../2.6.4+0.orig/src/config.php 2021-06-25 11:55:30.000000000 +0300 +++ ./src/config.php 2023-06-18 21:46:21.619073000 +0300 @@ -59,6 +59,42 @@ */ define('USE_FULLEMAIL_FOR_LOGIN', true); + /* + * Whether to overwrite the login based on the full email address. + * This is required for Z-Push to work properly after autodiscover + * in the case when the login differs from the local part of the email address. + * Possible values: + * false - do nothing. + * 'ldap' - the username will be the result of a ldap query. REMEMBER TO INSTALL PHP-LDAP!! + */ + define('OVERWRITE_LOGIN_BASED_ON_FULLEMAIL', false); +// define('OVERWRITE_LOGIN_BASED_ON_FULLEMAIL', 'ldap'); + + // LDAP server uri + //define("LOGIN_FROM_LDAP_SERVER_URI", "ldap://127.0.0.1:389/"); + //define("LOGIN_FROM_LDAP_START_TLS", false); + + //define("LOGIN_FROM_LDAP_SERVER_URI", "ldap://127.0.0.1:636/"); + define("LOGIN_FROM_LDAP_DISABLE_REFERRALS", true); + + //define("LOGIN_FROM_LDAP_START_TLS", true); + //define("LOGIN_FROM_LDAP_BIND_TIMEOUT", 10); + + // Set USER and PASSWORD if not using anonymous bind + define("LOGIN_FROM_LDAP_ANONYMOUS_BIND", false); + //define("LOGIN_FROM_LDAP_USER", "CN=searchuser,DC=company,DC=local"); + //define("LOGIN_FROM_LDAP_PASSWORD", ""); + + // Search base & filter + // the SEARCHVALUE string is substituded by the value inserted into the search field + //define("LOGIN_FROM_LDAP_BASE", "DC=company,DC=local"); + define("LOGIN_FROM_LDAP_QUERY", "(mail=#mail_address)"); + + // LDAP field in which the login is stored + define("LOGIN_FROM_LDAP_FIELD", "userPrincipalName"); + + define("LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR", false); + /********************************************************************************** * StateMachine setting * diff -urN ../2.6.4+0.orig/src/lib/request/login_by_fullemail.php ./src/lib/request/login_by_fullemail.php --- ../2.6.4+0.orig/src/lib/request/login_by_fullemail.php 1970-01-01 03:00:00.000000000 +0300 +++ ./src/lib/request/login_by_fullemail.php 2023-06-18 20:27:29.859606000 +0300 @@ -0,0 +1,216 @@ +. +* +* Consult LICENSE file for details +************************************************/ + +/** + * Returns the login based on full email address. + * + * @params string $email full email + * @return string + */ +function getLoginByEmailFromLDAP($email) { + $ret_value = $email; + + $ldap_conn = null; + try { + if (defined('LOGIN_FROM_LDAP_SERVER_URI')) { + ZLog::Write(LOGLEVEL_INFO, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Try to connect to %s", LOGIN_FROM_LDAP_SERVER_URI)); + $ldap_conn = @ldap_connect(LOGIN_FROM_LDAP_SERVER_URI); + + if ($ldap_conn === false) { + $message = sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Unable to create LDAP object. May be incorrect LOGIN_FROM_LDAP_SERVER_URI %s", LOGIN_FROM_LDAP_SERVER_URI); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } else { + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Connected to LDAP")); + + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Set LDAP_OPT_PROTOCOL_VERSION to 3")); + @ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3); + + if (defined('LOGIN_FROM_LDAP_DISABLE_REFERRALS') and (LOGIN_FROM_LDAP_DISABLE_REFERRALS === true)) { + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Set LDAP_OPT_REFERRALS to 0")); + @ldap_set_option($ldap_conn, LDAP_OPT_REFERRALS, 0); + } + + if (defined('LOGIN_FROM_LDAP_BIND_TIMEOUT') and (!empty(LOGIN_FROM_LDAP_BIND_TIMEOUT))) { + ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendContactsLDAP(): Set LDAP_OPT_NETWORK_TIMEOUT to %d", LOGIN_FROM_LDAP_BIND_TIMEOUT)); + @ldap_set_option($ldap_conn, LDAP_OPT_NETWORK_TIMEOUT, LOGIN_FROM_LDAP_BIND_TIMEOUT); + } + + if (defined('LOGIN_FROM_LDAP_START_TLS') and (LOGIN_FROM_LDAP_START_TLS === true)) { + ZLog::Write(LOGLEVEL_DEBUG, "Request::Initialize()->getLoginByEmailFromLDAP(): Try to start TLS"); + if (! @ldap_start_tls($ldap_conn)) { + $ldap_errno = ldap_errno($ldap_conn); + $ldap_error = ldap_error($ldap_conn); + $ldap_conn = false; + $message = sprintf( + "Request::Initialize()->getLoginByEmailFromLDAP(): Could not start TLS session with LDAP server %s (error %d: %s)", + LOGIN_FROM_LDAP_SERVER_URI, $ldap_errno, $ldap_error + ); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } + } + + // Authenticate + if (defined(LOGIN_FROM_LDAP_ANONYMOUS_BIND) and (LOGIN_FROM_LDAP_ANONYMOUS_BIND === true)) { + ZLog::Write(LOGLEVEL_DEBUG, "BackendContactsLDAP(): Try to bind anonymously"); + if ($ldap_bind = @ldap_bind($ldap_conn)) { + ZLog::Write(LOGLEVEL_DEBUG, "BackendContactsLDAP(): Successfully bind as anonymous"); + } else { + $ldap_errno = ldap_errno($ldap_conn); + $ldap_error = ldap_error($ldap_conn); + $ldap_conn = false; + $message = sprintf( + "Request::Initialize()->getLoginByEmailFromLDAP(): Could not bind anonymously to LDAP server %s (error %d: %s)", + LOGIN_FROM_LDAP_SERVER_URI, $ldap_errno, $ldap_error + ); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } + } elseif ((defined('LOGIN_FROM_LDAP_USER')) and (!empty(LOGIN_FROM_LDAP_USER)) and (defined('LOGIN_FROM_LDAP_PASSWORD')) and (!empty(LOGIN_FROM_LDAP_PASSWORD))) { + ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendContactsLDAP(): Try to bind to LDAP server %s as %s", LOGIN_FROM_LDAP_SERVER_URI, LOGIN_FROM_LDAP_USER)); + if ($ldap_bind = @ldap_bind($ldap_conn, LOGIN_FROM_LDAP_USER, LOGIN_FROM_LDAP_PASSWORD)) { + ZLog::Write(LOGLEVEL_DEBUG, sprintf("BackendContactsLDAP(): Succesfully authenticated as %s", LOGIN_FROM_LDAP_USER)); + } else { + $ldap_errno = ldap_errno($ldap_conn); + $ldap_error = ldap_error($ldap_conn); + $ldap_conn = false; + $message = sprintf( + "Request::Initialize()->getLoginByEmailFromLDAP(): Could not bind to LDAP server %s with user '%s' and specified password (error %d: %s)", + LOGIN_FROM_LDAP_SERVER_URI, LOGIN_FROM_LDAP_USER, $ldap_errno, $ldap_error + ); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } + } else { + // it would be possible to use the users login and password to authenticate on the LDAP server + // the main $backend has to keep these values so they could be used here + $ldap_conn = false; + $message = "Request::Initialize()->getLoginByEmailFromLDAP(): neither anonymous nor default bind enabled. Other options not implemented."; + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } + + if ($ldap_bind) { + $filter = str_replace('#mail_address', $email, LOGIN_FROM_LDAP_QUERY); + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Searching login with filter: '%s'", $filter)); + + if (!defined('LOGIN_FROM_LDAP_FIELD')) { + $message = "Request::Initialize()->getLoginByEmailFromLDAP(): LOGIN_FROM_LDAP_FIELD not defined."; + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } else { + $search_result = @ldap_search($ldap_conn, LOGIN_FROM_LDAP_BASE, $filter, [LOGIN_FROM_LDAP_FIELD]); + if (!$search_result) { + $message = sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Error in LDAP search query '%s'. Search aborted", $filter); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } else { + $items = ldap_get_entries($ldap_conn, $search_result); + if ($items['count'] > 0) { + ZLog::Write(LOGLEVEL_DEBUG, "Request::Initialize()->getLoginByEmailFromLDAP(): Found entry in LDAP. Generating login"); + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Found entry in LDAP %s", print_r($items, true))); + + // We get the first object. It's your responsibility to make the query unique. + if (!isset($items[0][strtolower(LOGIN_FROM_LDAP_FIELD)][0])) { + $message = sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Field %s not found in search result", LOGIN_FROM_LDAP_FIELD); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } else { + $ret_value = $items[0][strtolower(LOGIN_FROM_LDAP_FIELD)][0]; + $ret_value = trim($ret_value); + + if(defined('USE_FULLEMAIL_FOR_LOGIN') and (! USE_FULLEMAIL_FOR_LOGIN)) { + $ret_value = Utils::GetLocalPartFromEmail($ret_value); + } + + ZLog::Write(LOGLEVEL_DEBUG, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Got login %s", $ret_value)); + } + } else { + $message = sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): No entry found in LDAP for %s", $email); + ZLog::Write(LOGLEVEL_WARN, $message); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + $ret_value = ''; + } + } + } + } + } + } + } else { + ZLog::Write(LOGLEVEL_WARN, sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): No LDAP server URI defined")); + } + } catch(Exception $ex) { + $message = sprintf("Request::Initialize()->getLoginByEmailFromLDAP(): Error getting login value from LDAP server %s: %s", LOGIN_FROM_LDAP_SERVER_URI, $ex); + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true)) { + ZLog::Write(LOGLEVEL_ERROR, $message); + $ret_value = ''; + } else { + ZLog::Write(LOGLEVEL_WARN, $message); + } + } + + if ($ldap_conn != null) { + ldap_close($ldap_conn); + } + + if (defined('LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR') and (LOGIN_FROM_LDAP_SERVICE_UNAVAILABLE_ON_ERROR === true) and ($ret_value == '')) { + throw new ServiceUnavailableException(sprintf("Error getting login from LDAP server %s for %s", LOGIN_FROM_LDAP_SERVER_URI, $email)); + } + + return $ret_value; +} diff -urN ../2.6.4+0.orig/src/lib/request/request.php ./src/lib/request/request.php --- ../2.6.4+0.orig/src/lib/request/request.php 2021-02-04 03:38:38.000000000 +0200 +++ ./src/lib/request/request.php 2023-06-18 18:43:24.402782000 +0300 @@ -179,6 +179,13 @@ } } + if (isset($_SERVER['PHP_AUTH_USER'])) { + if ((preg_match('/@/', $_SERVER['PHP_AUTH_USER'])) and (defined('OVERWRITE_LOGIN_BASED_ON_FULLEMAIL')) and (OVERWRITE_LOGIN_BASED_ON_FULLEMAIL == 'ldap')) { + include('lib/request/login_by_fullemail.php'); + $_SERVER['PHP_AUTH_USER'] = getLoginByEmailFromLDAP($_SERVER['PHP_AUTH_USER']); + } + } + // authUser & authPassword are unfiltered! // split username & domain if received as one if (isset($_SERVER['PHP_AUTH_USER'])) {