Skip to content
Snippets Groups Projects
Commit 0c535147 authored by Hendrik Grahl's avatar Hendrik Grahl Committed by Mr Snow
Browse files

Issue #2728821 by grahl: Update ldap_sso

parent 04979d6b
No related branches found
No related tags found
No related merge requests found
Microsoft Forefront Threat Management Gateway
http://technet.microsoft.com/en-us/library/cc441438.aspx
Overview of authentication in Forefront TMG
http://technet.microsoft.com/en-us/library/cc441695.aspx
About authentication in Web publishing
http://technet.microsoft.com/en-us/library/cc441671.aspx
-- third component of TMG authentication for web publishing is: "Delegation of authentication ot web servers behind FTMG". This is where LDAP modules need to be integrated. This aspect is configured in the "publishing rule" such that a single listener can have multiple types of delegation.
1) The Web server must be configured to use the authentication scheme that matches the delegation method used by Forefront TMG. Delegation of client credentials is configured on the publishing rule. In the Publishing Rule wizard, configure this on the Authentication Delegation page. In the publishing rule properties, the authentication settings are on the Authentication Delegation tab.
2) Delegation options are (sorted in order of what I think are desireable)
- NTLM/Kerberos (Negotiate). Tries for Kerberos ticket and goes for credentials via NTLM.
- NTLM. "In NTLM delegation, Forefront TMG delegates the credentials by using the NTLM challenge/response authentication protocol. If authentication fails, Forefront TMG replaces the delegation with the authentication type used by the Web listener. If the server requires a different type of credentials, Forefront TMG triggers an alert."
- No delegation, and client cannot authenticate directly. Not useful. Just for avoiding false passing of credentials when not needed.
- No delegation, but client may authenticate directly. User credentials passed to drupal. Not desireable.
- Basic. cleartext passing of credentials to drupal. Not desireable.
- SecurID
- Kerberos constrained delegation
=======================================
LDAP Single Sign-On
=======================================
To use the single sign-on feature, your web server must provide an authentication
mechanism for LDAP. The only authentication mechanism used in development
was mod_auth_sspi for Apache/Windows, but so long as the web server's LDAP
authentication mechanism is configured to provide the $_SERVER variable
$_SERVER['REMOTE_USER'] or $_SERVER['REDIRECT_REMOTE_USER'] corresponding
directly to a user's LDAP user name, this should work all the same. This
will require some sort of LDAP authentication mechanism; mod_auth_sspi is
available here: http://mod-auth-sspi.sourceforge.net/, while mod_ntlm is
available here: http://modntlm.sourceforge.net/, and mod_auth_ntlm_winbind is
available here: http://samba.org/ftp/unpacked/lorikeet/mod_auth_ntlm_winbind/.
If a Linux distribution is being used, Apache authentication modules are likely
available within the distro's package manager.
Unless an administrator wishes to require that all visitors be authenticated,
NTLM and/or basic authentication should be set up only on the path
user/login/sso, which will authentify the visitor but not deny access to view
the site if the visitor is not authenticated. An administrator may wish to
require LDAP authentication to view any portion of the site; this can be
achieved by changing the location directive below to "/". An administrator may
also wish to automatically log in visitors to Drupal; this can be achieved by
checking "Turn on automated single sign-on" in the modules' configuration page.
An example of an Apache configuration for a named virtualhost configuration
using mod_auth_sspi on Windows is as follows:
httpd.conf:
_______________________________________________________________________________
_______________________________________________________________________________
# Virtual hosts
Include conf/extra/httpd-vhosts.conf
# Pass NTLM authentication to Apache
LoadModule sspi_auth_module modules/mod_auth_sspi.so
<IfModule !mod_auth_sspi.c>
LoadModule sspi_auth_module modules/mod_auth_sspi.so
</IfModule>
_______________________________________________________________________________
_______________________________________________________________________________
httpd-vhosts.conf:
_______________________________________________________________________________
_______________________________________________________________________________
NameVirtualHost example.com
<VirtualHost example.com>
DocumentRoot "D:/www/example.com/htdocs"
ServerName example.com
<directory "D:/www/example.com/htdocs">
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order Allow,Deny
Allow from all
</directory>
<Location /user/login/sso>
AuthType SSPI
AuthName "Example.com - Login using your LDAP user name and password"
SSPIAuth On
SSPIAuthoritative On
### The domain used to authenticate with LDAP; this should match the domain
### configured in the LDAP integration configuration within Drupal
SSPIDomain ad.example.com
SSPIOmitDomain On
SSPIOfferBasic On
Require valid-user
#SSPIBasicPreferred On
#SSPIofferSSPI off
</Location>
</VirtualHost>
_______________________________________________________________________________
_______________________________________________________________________________
After enabling and configuring an LDAP authentication module within Apache,
visit user/login/sso in the Drupal installation on example.com. With or without
the ldap sso feature enabled, the browser should prompt for a user name and
password if using Internet Explorer 8 or a non-Microsoft browser. Internet
Explorer 7 by default will pass NTLM authentication credentials to local
websites, and IE8 and Firefox can be configured to do this as well.
If prompted for credentials on that path, enter a valid LDAP user name,
omitting the domain if "SSPIOmitDomain On" is configured, as well as a password.
If the credentials are correct, or if NTLM credentials are passed automatically
by the browser and successfully authenticated, a Drupal 404 "Page not found"
message will be displayed if the module is not enabled; an "access is denied"
message will be displayed if the module is enabled and the browser is already
logged in; and if the ldap_sso module is fully configured and there is no
existing session, the browser will display the message "You have been
successfully authenticated" after redirecting to the sites' home page.
name: 'LDAP SSO'
description: 'Implements Single Sign On (SSO) LDAP Authentication'
type: module
core: 8.x
package: 'Lightweight Directory Access Protocol'
dependencies:
- ldap_servers
- ldap_authentication
#configure = admin/config/people/ldap/authentication
<?php
/**
* @file
* This module injects itself into Drupal's Authentication stack.
*/
/**
* Implements hook_menu().
*/
function ldap_sso_menu() {
$items = array();
$items['user/login/sso'] = array(
'title' => 'Log In',
'page callback' => 'ldap_sso_user_login_sso',
'access callback' => '_ldap_authentication_user_access',
'type' => MENU_NORMAL_ITEM,
);
return $items;
}
/**
* Implements hook_user_logout().
*
* The user just logged out.
*/
function ldap_sso_user_logout($account) {
$auth_conf = ldap_authentication_get_valid_conf();
if ($auth_conf->seamlessLogin == 1) {
$cookie_string = 'do not auto login';
$cookie_timeout = (int) $auth_conf->cookieExpire;
setcookie("seamless_login", $cookie_string, (($cookie_timeout == -1) ? 0 : $cookie_timeout + time()), base_path(), "");
$_SESSION['seamless_login'] = $cookie_string;
}
}
/**
* Implements hook_boot().
* Perform setup tasks. This entry point is used because hook_user_load no
* longer runs on anonymous users, and hook_boot is guaranteed to run,
* regardless of cache.
*/
function ldap_sso_boot() {
if (!drupal_is_cli() && $GLOBALS['user']->uid == 0) {
if (ldap_sso_path_excluded_from_sso()) {
return;
}
if (!(isset($_COOKIE['seamless_login'])) || $_COOKIE['seamless_login'] == 'auto login') {
if ((arg(0) == 'user' && !(is_numeric(arg(1)))) || arg(0) == 'logout') {
return;
}
else {
if (isset($_COOKIE['seamless_login_attempted'])) {
$login_attempted = $_COOKIE['seamless_login_attempted'];
}
else {
$login_attempted = FALSE;
}
require_once DRUPAL_ROOT . '/includes/common.inc';
require_once DRUPAL_ROOT . '/includes/path.inc';
$ldap_authentication_conf = variable_get('ldap_authentication_conf', array());
if (isset($ldap_authentication_conf['seamlessLogin']) && $ldap_authentication_conf['seamlessLogin'] == 1 && ($login_attempted != 'true')) {
setcookie("seamless_login_attempted", 'true', time() + (int) $ldap_authentication_conf['cookieExpire'], base_path(), "");
$_SESSION['seamless_login_attempted'] = $login_attempted;
// Removed with http://drupal.org/node/1485118 patch
// $ldap_sso_q = (!isset($_GET['q']) || $_GET['q'] == '') ? 'user' : $_GET['q'];
// drupal_goto('user/login/sso', array('query' => array('destination' => rawurlencode($ldap_sso_q))));
drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
// Seems redundant, but need to check this again after additional bootstrap.
if (ldap_sso_path_excluded_from_sso()) {
return;
}
drupal_goto('user/login/sso', array('query' => drupal_get_destination()));
}
else {
return;
}
}
}
}
}
/**
*
*/
function ldap_sso_default_excluded_paths() {
return array(
'admin/config/search/clean-urls/check',
);
}
/**
*
*/
function ldap_sso_path_excluded_from_sso($path = FALSE) {
$result = FALSE;
if ($path) {
// don't derive.
}
elseif ($_SERVER['PHP_SELF'] == '/index.php') {
$path = $_GET['q'];
}
else {
// cron.php, etc.
$path = ltrim($_SERVER['PHP_SELF'], '/');
}
if (in_array($path, ldap_sso_default_excluded_paths())) {
return TRUE;
}
$ldap_authentication_conf = variable_get('ldap_authentication_conf', array());
if (isset($ldap_authentication_conf['ssoExcludedHosts']) && is_array($ldap_authentication_conf['ssoExcludedHosts'])) {
$host = $_SERVER['SERVER_NAME'];
foreach ($ldap_authentication_conf['ssoExcludedHosts'] as $host_to_check) {
if ($host_to_check == $host) {
return TRUE;
}
}
}
if (isset($ldap_authentication_conf['ssoExcludedPaths'])) {
$patterns = join("\r\n", $ldap_authentication_conf['ssoExcludedPaths']);
if ($patterns) {
if (function_exists('drupal_get_path_alias')) {
$path = drupal_get_path_alias($path);
}
$path = (function_exists('drupal_strtolower')) ? drupal_strtolower($path) : strtolower($path);
$to_replace = array(
// Newlines.
'/(\r\n?|\n)/',
// Asterisks.
'/\\\\\*/',
// <front>.
'/(^|\|)\\\\<front\\\\>($|\|)/',
);
$replacements = array(
'|',
'.*',
'\1' . preg_quote(config('system.site')->get('frontpage'), '/') . '\2',
);
$patterns_quoted = preg_quote($patterns, '/');
$regex = '/^(' . preg_replace($to_replace, $replacements, $patterns_quoted) . ')$/';
$result = (bool) preg_match($regex, $path);
}
}
return $result;
}
/**
* A proxy function for the actual authentication routine. This is in place
* so various implementations of grabbing NTLM credentials can be used and
* selected from an administration page. This is the real gatekeeper since
* this assumes that any NTLM authentication from the underlying web server
* is good enough, and only checks that there are values in place for the
* user name, and anything else that is set for a particular implementation. In
* the case that there are no credentials set by the underlying web server, the
* user is redirected to the normal user login form.
*
* @return false
*/
function ldap_sso_user_login_sso() {
$detailed_watchdog_log = config('ldap_help.settings')->get('watchdog_detail');
$auth_conf = ldap_authentication_get_valid_conf();
$implementation = $auth_conf->ldapImplementation;
// $enabled = $auth_conf->ssoEnabled; // this is redundant since hook won't be called if ldap_sso isn't enabled.
if ($detailed_watchdog_log) {
$watchdog_tokens = array(
'!implementation' => $auth_conf->ldapImplementation,
'!enabled' => $auth_conf->ssoEnabled,
'!server_remote_user' => @$_SERVER['REMOTE_USER'],
'!server_redirect_remote_user' => @$_SERVER['REDIRECT_REMOTE_USER'],
'!ssoRemoteUserStripDomainName' => $auth_conf->ssoRemoteUserStripDomainName,
'!seamlessLogin' => $auth_conf->seamlessLogin,
);
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.step1: implementation: !implementation, enabled: !enabled, server_remote_user: !server_remote_user, server_redirect_remote_user: !server_redirect_remote_user, ssoRemoteUserStripDomainName: !ssoRemoteUserStripDomainName,seamlessLogin: !seamlessLogin',
$watchdog_tokens
);
}
// If ($enabled != TRUE) { // this is redundant as well as problematic. if ldap_sso is disabled, no need to redirect to user/login
// drupal_goto('user/login');
// }.
$remote_user = NULL;
$realm = NULL;
$domain = NULL;
switch ($implementation) {
case 'mod_auth_sspi':
$remote_user = FALSE;
if (isset($_SERVER['REMOTE_USER'])) {
$remote_user = $_SERVER['REMOTE_USER'];
}
elseif (isset($_SERVER['REDIRECT_REMOTE_USER'])) {
$remote_user = $_SERVER['REDIRECT_REMOTE_USER'];
}
break;
case 'mod_auth_kerb':
$remote_user = FALSE;
if (isset($_SERVER['REMOTE_USER'])) {
$remote_user = $_SERVER['REMOTE_USER'];
}
elseif (isset($_SERVER['REDIRECT_REMOTE_USER'])) {
$remote_user = $_SERVER['REDIRECT_REMOTE_USER'];
}
if ($remote_user && preg_match('/^([A-Za-z0-9_\-\.]+)@([A-Za-z0-9_\-.]+)$/',
$remote_user,
$matches)) {
$remote_user = $matches[1];
// This can be used later if realms is ever supported properly.
$realm = $matches[2];
}
break;
}
if ($detailed_watchdog_log) {
$watchdog_tokens['!remote_user'] = $remote_user;
$watchdog_tokens['!realm'] = $realm;
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.implementation: username=!remote_user, (realm=!realm) found',
$watchdog_tokens);
}
if ($remote_user) {
if ($auth_conf->ssoRemoteUserStripDomainName) {
// Might be in form <remote_user>@<domain> or <domain>\<remote_user>.
$domain = NULL;
$exploded = preg_split('/[\@\\\\]/', $remote_user);
if (count($exploded) == 2) {
if (strpos($remote_user, '@') !== FALSE) {
$remote_user = $exploded[0];
$domain = $exploded[1];
}
else {
$domain = $exploded[0];
$remote_user = $exploded[1];
}
if ($detailed_watchdog_log) {
$watchdog_tokens['!remote_user'] = $remote_user;
$watchdog_tokens['!domain'] = $domain;
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.stripdomain: remote_user=!remote_user, domain=!domain',
$watchdog_tokens
);
}
}
}
if ($detailed_watchdog_log) {
$watchdog_tokens['!remote_user'] = $remote_user;
$watchdog_tokens['!realm'] = $realm;
$watchdog_tokens['!domain'] = $domain;
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user: username=!remote_user, (realm=!realm, domain=!domain) found',
$watchdog_tokens
);
}
$fake_form_state = array(
'values' => array(
'name' => check_plain($remote_user),
'pass' => user_password(20),
),
'sso_login' => TRUE,
);
$user = ldap_authentication_user_login_authenticate_validate(array(), $fake_form_state, TRUE);
if ($detailed_watchdog_log) {
$watchdog_tokens['!uid'] = is_object($user) ? $user->uid : NULL;
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user: uid of user=!uid',
$watchdog_tokens
);
}
if ($user && $user->uid > 0) {
if ($auth_conf->seamlessLogin == 1) {
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user.user_success.seemlessLogin',
$watchdog_tokens
);
}
setcookie("seamless_login", 'auto login', time() + $auth_conf->cookieExpire, base_path(), "");
$_SESSION['seamless_login'] = 'auto login';
setcookie("seamless_login_attempted", '');
unset($_SESSION['seamless_login_attempted']);
}
drupal_set_message(theme('ldap_authentication_login_message',
array('message' => t('You have been successfully authenticated'))));
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user.user_success.drupal_goto front',
$watchdog_tokens
);
}
drupal_goto('<front>');
}
else {
if ($auth_conf->seamlessLogin == 1) {
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user.user_fail.seamlessLogin',
$watchdog_tokens
);
}
setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), "");
$_SESSION['seamless_login'] = 'do not auto login';
}
drupal_set_message(theme('ldap_authentication_message_not_found',
array(
'message' => t('Sorry, your LDAP credentials were not found, ' .
'or the LDAP server is not available. You may log in ' .
'with other credentials on the !user_login_form.',
array('!user_login_form' => l(t('user login form'), 'user/login'))),
)
), 'error');
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.remote_user.user_fail.drupal_goto user/logint',
$watchdog_tokens
);
}
drupal_goto('user/login');
}
}
else {
\Drupal::logger('ldap_authentication')->debug(
'$_SERVER[\'REMOTE_USER\'] not found', array()
);
if ($auth_conf->seamlessLogin == 1) {
setcookie("seamless_login", 'do not auto login', time() + $auth_conf->cookieExpire, base_path(), "");
$_SESSION['seamless_login'] = 'do not auto login';
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.no_remote_user.seamlessLogin', $watchdog_tokens
);
}
}
drupal_set_message(theme('ldap_authentication_message_not_authenticated',
array(
'message' =>
t('You were not authenticated by the server.
You may log in with your credentials below.'),
)
), 'error');
if ($detailed_watchdog_log) {
\Drupal::logger('ldap_authentication')->debug(
'ldap_sso_user_login_sso.no_remote_user.drupal_goto user/login',
$watchdog_tokens
);
}
drupal_goto('user/login');
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment