1. NTLM Single Sign ON

NTLM SSO removes Windows users on a PC, which is a member of a Windows
domain and who are logged into that domain, from the need to explicitly log
into eGW.  They simply point IE to the eGW URL (eg. http://domain.com/egroupware/)
and start working. They can of cause explicitly log out and log in as an
other user.
For more information look at the README at
http://www.egroupware.org/viewvc/trunk/phpgwapi/ntml/README

2. different authentication for SyncML and/or GroupDAV
You can now use eg. an external auth provider for the login via the
WebGUI (eg. ADS) and the passwords stored in SQL for SyncML.
This commit is contained in:
Ralf Becker 2008-07-16 09:29:13 +00:00
parent 5477c71045
commit b5c28fba48
10 changed files with 263 additions and 54 deletions

View File

@ -17,15 +17,6 @@
exit; exit;
} }
$GLOBALS['sessionid'] = isset($_GET['sessionid']) ? $_GET['sessionid'] : @$_COOKIE['sessionid'];
if(!$GLOBALS['sessionid'])
{
Header('Location: login.php'.
(isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ?
'?phpgw_forward='.urlencode('/index.php?'.$_SERVER['QUERY_STRING']):''));
exit;
}
if(isset($_GET['hasupdates']) && $_GET['hasupdates'] == 'yes') if(isset($_GET['hasupdates']) && $_GET['hasupdates'] == 'yes')
{ {
$hasupdates = True; $hasupdates = True;

View File

@ -11,10 +11,17 @@
* @version $Id$ * @version $Id$
*/ */
// allow to set an application depending authentication type (eg. for syncml, groupdav, ...)
if (isset($GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']]) &&
$GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']])
{
$GLOBALS['egw_info']['server']['auth_type'] = $GLOBALS['egw_info']['server']['auth_type_'.$GLOBALS['egw_info']['flags']['currentapp']];
}
if(empty($GLOBALS['egw_info']['server']['auth_type'])) if(empty($GLOBALS['egw_info']['server']['auth_type']))
{ {
$GLOBALS['egw_info']['server']['auth_type'] = 'sql'; $GLOBALS['egw_info']['server']['auth_type'] = 'sql';
} }
//error_log('using auth_type='.$GLOBALS['egw_info']['server']['auth_type'].', currentapp='.$GLOBALS['egw_info']['flags']['currentapp']);
include(EGW_API_INC.'/class.auth_'.$GLOBALS['egw_info']['server']['auth_type'].'.inc.php'); include(EGW_API_INC.'/class.auth_'.$GLOBALS['egw_info']['server']['auth_type'].'.inc.php');
/** /**

View File

@ -131,7 +131,6 @@ class egw extends egw_minimal
// setup the other subclasses // setup the other subclasses
$this->translation = new translation(); $this->translation = new translation();
$this->common = new common(); $this->common = new common();
$this->auth = new auth();
$this->accounts = accounts::getInstance(); $this->accounts = accounts::getInstance();
$this->acl = new acl(); $this->acl = new acl();
/* Do not create the session object if called by the sessions class. This way /* Do not create the session object if called by the sessions class. This way
@ -277,7 +276,9 @@ class egw extends egw_minimal
} }
// this removes the sessiondata if its saved in the URL // this removes the sessiondata if its saved in the URL
$query = preg_replace('/[&]?sessionid(=|%3D)[^&]+&kp3(=|%3D)[^&]+&domain=.*$/','',$_SERVER['QUERY_STRING']); $query = preg_replace('/[&]?sessionid(=|%3D)[^&]+&kp3(=|%3D)[^&]+&domain=.*$/','',$_SERVER['QUERY_STRING']);
Header('Location: '.$GLOBALS['egw_info']['server']['webserver_url'].'/login.php?cd=10&phpgw_forward='.urlencode($relpath.(!empty($query) ? '?'.$query : ''))); $redirect = '/login.php?cd=10&';
if ($GLOBALS['egw_info']['server']['http_auth_types']) $redirect = '/phpgwapi/ntlm/index.php?';
Header('Location: '.$GLOBALS['egw_info']['server']['webserver_url'].$redirect.'phpgw_forward='.urlencode($relpath.(!empty($query) ? '?'.$query : '')));
exit; exit;
} }
} }

View File

@ -560,9 +560,10 @@
* @param string $passwd user password * @param string $passwd user password
* @param string $passwd_type type of password being used, ie plaintext, md5, sha1 * @param string $passwd_type type of password being used, ie plaintext, md5, sha1
* @param boolean $no_session_needed=false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support * @param boolean $no_session_needed=false dont create a real session, eg. for GroupDAV clients using only basic auth, no cookie support
* @param boolean $auth_check=true if false, the user is loged in without checking his password (eg. for single sign on), default = true
* @return string session id * @return string session id
*/ */
function create($login,$passwd = '',$passwd_type = '',$no_session=false) function create($login,$passwd = '',$passwd_type = '',$no_session=false,$auth_check=true)
{ {
if (is_array($login)) if (is_array($login))
{ {
@ -577,6 +578,7 @@
$this->passwd = $passwd; $this->passwd = $passwd;
$this->passwd_type = $passwd_type; $this->passwd_type = $passwd_type;
} }
//error_log(__METHOD__."($this->login,$this->passwd,$this->passwd_type,$no_session,$auth_check)");
$this->clean_sessions(); $this->clean_sessions();
$this->split_login_domain($login,$this->account_lid,$this->account_domain); $this->split_login_domain($login,$this->account_lid,$this->account_domain);
@ -618,7 +620,7 @@
if (($blocked = $this->login_blocked($login,$user_ip)) || // too many unsuccessful attempts if (($blocked = $this->login_blocked($login,$user_ip)) || // too many unsuccessful attempts
$GLOBALS['egw_info']['server']['global_denied_users'][$this->account_lid] || $GLOBALS['egw_info']['server']['global_denied_users'][$this->account_lid] ||
!$GLOBALS['egw']->auth->authenticate($this->account_lid, $this->passwd, $this->passwd_type) || $auth_check && !$GLOBALS['egw']->auth->authenticate($this->account_lid, $this->passwd, $this->passwd_type) ||
$this->account_id && $GLOBALS['egw']->accounts->get_type($this->account_id) == 'g') $this->account_id && $GLOBALS['egw']->accounts->get_type($this->account_id) == 'g')
{ {
$this->reason = $blocked ? 'blocked, too many attempts' : 'bad login or password'; $this->reason = $blocked ? 'blocked, too many attempts' : 'bad login or password';

View File

@ -244,51 +244,57 @@ class EGW_SyncML_State extends Horde_SyncML_State
} }
function isAuthorized() function isAuthorized()
{ {
if (!$this->_isAuthorized) { if (!$this->_isAuthorized)
if(!isset($this->_locName) && !isset($this->_password))
{ {
Horde::logMessage('SyncML: Authentication not yet possible currently. Username and password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG); if(!isset($this->_locName) && !isset($this->_password))
return FALSE; {
} Horde::logMessage('SyncML: Authentication not yet possible currently. Username and password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE;
if(!isset($this->_password)) }
{
Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG); if(!isset($this->_password))
return FALSE; {
} Horde::logMessage('SyncML: Authentication not yet possible currently. Password not available' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
return FALSE;
if(strpos($this->_locName,'@') === False) }
{
$this->_locName .= '@'.$GLOBALS['egw_info']['server']['default_domain']; if(strpos($this->_locName,'@') === False)
} {
$this->_locName .= '@'.$GLOBALS['egw_info']['server']['default_domain'];
#Horde::logMessage('SyncML: authenticate with username: ' . $this->_locName . ' and password: ' . $this->_password, __FILE__, __LINE__, PEAR_LOG_DEBUG); }
if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text')) #Horde::logMessage('SyncML: authenticate with username: ' . $this->_locName . ' and password: ' . $this->_password, __FILE__, __LINE__, PEAR_LOG_DEBUG);
{
$this->_isAuthorized = true; if($GLOBALS['sessionid'] = $GLOBALS['egw']->session->create($this->_locName,$this->_password,'text'))
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG); {
$this->_isAuthorized = true;
Horde::logMessage('SyncML_EGW: Authentication of ' . $this->_locName . '/' . $GLOBALS['sessionid'] . ' succeded' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
else
{
$this->_isAuthorized = false;
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_INFO);
}
} }
/*
* RalfBecker 2008-07-16: commented out, as return value is NOT used anyway
* It is not a security problem, as without a valid SyncML session
* one is created anyway. The horde SyncML codes handles that on it's own.
* Leaving it in gives problems with NTLM auth, as verify redirects there.
*
else else
{ {
$this->_isAuthorized = false; // store sessionID in a variable, because ->verify maybe resets that value
Horde::logMessage('SyncML: Authentication of ' . $this->_locName . ' failed' , __FILE__, __LINE__, PEAR_LOG_INFO); $sessionID = session_id();
} if(!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3')) {
} Horde::logMessage('SyncML_EGW: egw session(' .$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
else }
{ }*/
// store sessionID in a variable, because ->verify maybe resets that value
$sessionID = session_id();
if(!$GLOBALS['egw']->session->verify($sessionID, 'staticsyncmlkp3')) {
Horde::logMessage('SyncML_EGW: egw session(' .$sessionID. ') not verified ' , __FILE__, __LINE__, PEAR_LOG_DEBUG);
}
}
return $this->_isAuthorized; return $this->_isAuthorized;
} }
/** /**
* Removes all locid<->guid mappings for the given type. * Removes all locid<->guid mappings for the given type.

40
phpgwapi/ntlm/README Normal file
View File

@ -0,0 +1,40 @@
Steps to set up NTLM Single Sign On for eGroupWare 1.6
======================================================
(Version: $Id$)
NTLM SSO removes Windows users on a PC, which is a member of a Windows domain
and who are logged into that domain, from the need to explicitly log into eGW.
They simply point IE to the eGW URL (eg. http://domain.com/egroupware/) and
start working. They can of cause explicitly log out and log in as an other user.
As far as I tested, Firefox 3 only allows to enter user (including domain(!), eg. DOMAIN\user)
and password in a popup, which then get's checked from apache via winbind.
It does NOT automatically log you in, if you're logged into the domain on your PC!
Here's in short what you need:
-----------------------------
1. eGW 1.6 running on Apache
2. a fully working and configured winbind configuration (not described here)
3. mod_ntlm_winbind (eg. for openSUSE from their package apache2-mod_auth_ntml_winbind)
4. an Apache configuration with the egroupware.conf in this directory (expecting eGW
to be installed in it's default location /usr/share/egroupware) or port the necessary
settings to your Apache configuration.
--> You NEED to change the domain from "TEST" to your used domain name!
5. Make the following changes in eGW's setup >> configuraition:
- HTTP auth types (comma-separated) to use without login-page, eg. "NTLM": NTLM
- Select which type of authentication you are using: ADS
This is not needed for NTLM authentication, but allows the users to use their windows
user and password to log into eGW, if they log in using an other browser or location.
- Host/IP Domain controler: ... <-- NEED to be filled out
- Domain name: ... <-- NEED to be filled out, same domain name as above
6. If you use EMail, you have to explicitly specify user/pw to use for contacting the IMAP
(and SMTP) server, it's no longer available to eGW!
Please note the DC has to be started before you start winbind!
The eGW code should work with every Apache authentication, which sets REMOTE_USER and AUTH_TYPE.
With slight modifications (different var names) it should work eg. with SSL client certificates.
This feature was sponsored by Sponsored by Carl Knauber Holding GmbH und Co. KG.
Ralf Becker

View File

@ -0,0 +1,58 @@
#
# Apache and PHP configuration for eGroupWare using NTLM authentication
#
# Version: $Id$
#
Alias /egroupware /usr/share/egroupware
<Directory /usr/share/egroupware/phpgwapi/ntlm/>
AuthName "NTLM eGroupWare Authentication"
NTLMAuth on
NegotiateAuth off
NTLMBasicRealm TEST
NTLMBasicAuth on
NTLMAuthHelper "/usr/bin/ntlm_auth --helper-protocol=squid-2.5-ntlmssp"
NegotiateAuthHelper "/usr/bin/ntlm_auth --helper-protocol=gss-spnego"
PlaintextAuthHelper "/usr/bin/ntlm_auth --domain=TEST.LOCAL --helper-protocol=squid-2.5-basic"
NTLMBasicAuthoritative on
AuthType NTLM
require valid-user
</Directory>
<Directory /usr/share/egroupware/>
Options FollowSymLinks ExecCGI
AllowOverride None
Order allow,deny
Allow from all
DirectoryIndex index.html index.php
AddHandler cgi-script .cgi
AddDefaultCharset Off
php_flag file_uploads on
php_flag log_errors on
php_flag magic_quotes_gpc off
php_flag magic_quotes_runtime off
php_flag register_globals off
php_flag track_vars on
php_value error_reporting E_ALL
php_value max_execution_time 90
php_value mbstring.func_overload 7
php_value memory_limit 48M
php_value session.gc_maxlifetime 14400
php_value open_basedir /usr/share/egroupware:/var/lib/egroupware:/tmp:/var/lib/php5
php_value upload_max_filesize 16M
<Files ~ "\.inc\.php$">
Order allow,deny
Deny from all
</Files>
</Directory>
<Directory /usr/share/egroupware/phpsysinfo/>
php_value open_basedir /
</Directory>
<Location /egroupware/rpc.php>
php_value mbstring.func_overload 0
Order allow,deny
Allow from all
</Location>

61
phpgwapi/ntlm/index.php Normal file
View File

@ -0,0 +1,61 @@
<?php
/**
* eGroupWare - NTLM or other http auth access without login page
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package api
* @subpackage authentication
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @version $Id$
*/
/**
* check if the given user has access
*
* Create a session or if the user has no account return authenticate header and 401 Unauthorized
*
* @param array &$account
* @return int session-id
*/
function check_access(&$account)
{
//error_log("AUTH_TYPE={$_SERVER['AUTH_TYPE']}, REMOTE_USER={$_SERVER['REMOTE_USER']}, HTTP_USER_AGENT={$_SERVER['HTTP_USER_AGENT']}, http_auth_types={$GLOBALS['egw_info']['server']['http_auth_types']}");
if (isset($_SERVER['REMOTE_USER']) && $_SERVER['REMOTE_USER'] && isset($_SERVER['AUTH_TYPE']) &&
isset($GLOBALS['egw_info']['server']['http_auth_types']) && $GLOBALS['egw_info']['server']['http_auth_types'] &&
in_array(strtoupper($_SERVER['AUTH_TYPE']),explode(',',strtoupper($GLOBALS['egw_info']['server']['http_auth_types']))))
{
if (strpos($account=$_SERVER['REMOTE_USER'],'\\') !== false)
{
list(,$account) = explode('\\',$account,2);
}
$sessionid = $GLOBALS['egw']->session->create($account,null,'ntlm',false,false); // false=no auth check
//error_log("create('$account',null,'ntlm',false,false)=$sessionid ({$GLOBALS['egw']->session->reason})");
}
if (!$sessionid)
{
header('Location: ../../login.php'.(isset($_REQUEST['phpgw_forward']) ? '?'.$_REQUEST['phpgw_forward'] : ''));
exit;
}
return $sessionid;
}
$GLOBALS['egw_info']['flags'] = array(
'noheader' => True,
'currentapp' => 'home',
'autocreate_session_callback' => 'check_access',
);
// if you move this file somewhere else, you need to adapt the path to the header!
include(dirname(__FILE__).'/../../header.inc.php');
if ($_REQUEST['phpgw_forward'])
{
$forward = '../../'.(isset($_GET['phpgw_forward']) ? urldecode($_GET['phpgw_forward']) : @$_POST['phpgw_forward']);
}
else
{
$forward = '../../index.php';
}
header('Location: '.$forward);

View File

@ -24,6 +24,9 @@ $GLOBALS['egw_info'] = array(
); );
include('./header.inc.php'); include('./header.inc.php');
// allow to use an authentication specific for SyncML
$GLOBALS['egw_info']['flags']['currentapp'] = 'syncml';
$errors = array(); $errors = array();
// SyncML works currently only with PHP sessions // SyncML works currently only with PHP sessions

View File

@ -175,7 +175,7 @@
<td colspan="2"><b>{lang_Authentication_/_Accounts}</b></td> <td colspan="2"><b>{lang_Authentication_/_Accounts}</b></td>
</tr> </tr>
<tr class="row_on"> <tr class="row_off">
<td>{lang_Select_which_type_of_authentication_you_are_using}:</td> <td>{lang_Select_which_type_of_authentication_you_are_using}:</td>
<td> <td>
<select name="newsettings[auth_type]"> <select name="newsettings[auth_type]">
@ -191,6 +191,46 @@
</td> </td>
</tr> </tr>
<tr class="row_on">
<td>{lang_Authentication_type_for_application}: <b>SyncML</b></td>
<td>
<select name="newsettings[auth_type_syncml]">
<option value="">{lang_Standard,_as_defined_above}</option>
<option value="sql"{selected_auth_type_syncml_sql}>SQL</option>
<option value="sqlssl"{selected_auth_type_syncml_sqlssl}>SQL / SSL</option>
<option value="ldap"{selected_auth_type_syncml_ldap}>LDAP</option>
<option value="ads"{selected_auth_type_syncml_ads}>ADS</option>
<option value="mail"{selected_auth_type_syncml_mail}>Mail</option>
<option value="http"{selected_auth_type_syncml_http}>HTTP</option>
<option value="nis"{selected_auth_type_syncml_nis}>NIS</option>
<option value="pam"{selected_auth_type_syncml_pam}>PAM</option>
</select>
</td>
</tr>
<tr class="row_off">
<td>{lang_Authentication_type_for_application}: <b>GroupDAV/CalDAV/CardDAV</b></td>
<td>
<select name="newsettings[auth_type_groupdav]">
<option value="">{lang_Standard,_as_defined_above}</option>
<option value="sql"{selected_auth_type_groupdav_sql}>SQL</option>
<option value="sqlssl"{selected_auth_type_groupdav_sqlssl}>SQL / SSL</option>
<option value="ldap"{selected_auth_type_groupdav_ldap}>LDAP</option>
<option value="ads"{selected_auth_type_groupdav_ads}>ADS</option>
<option value="mail"{selected_auth_type_groupdav_mail}>Mail</option>
<option value="http"{selected_auth_type_groupdav_http}>HTTP</option>
<option value="nis"{selected_auth_type_groupdav_nis}>NIS</option>
<option value="pam"{selected_auth_type_groupdav_pam}>PAM</option>
</select>
</td>
</tr>
<tr class="row_on">
<td>{lang_HTTP_auth_types_(comma-separated)_to_use_without_login-page, eg. "NTLM"}:</td>
<td>
<input name="newsettings[http_auth_types]" value="{value_http_auth_types}" size="20" />
</td>
</tr>
<tr class="row_off"> <tr class="row_off">
<td>{lang_Select_where_you_want_to_store/retrieve_user_accounts}:</td> <td>{lang_Select_where_you_want_to_store/retrieve_user_accounts}:</td>