[cvs] [Wiki] changed: CASAuthHowTo
Wiki Guest
wikiguest at horde.org
Fri Mar 19 12:23:25 UTC 2010
guest [90.43.155.155] Fri, 19 Mar 2010 08:23:25 -0400
Modified page: http://wiki.horde.org/CASAuthHowTo
New Revision: 2.21
Change log: Revert
@@ -1,449 +1,891 @@
+ CAS Authentication !HowTo
+
+
Jan Van der Velpen aka Velpi (who did all the work)
+
Peter Arien aka Kaos99 (who just likes playing around with Horde)
+
Thanks go to the [http://www.ja-sig.org/products/cas/ Ja-Sig] and
the
[http://esup-portail.org/consortium/espace/SSO_1B/tech/cas/cas_install.html
ESUP] people!!
+
+
[http://www.kuleuven.be/ Our university] is working towards a
complete AAI (Authentication and Authorization Infrastructure)
implementation. For web applications we are using the
[http://shibboleth.internet2.edu/ Shibboleth architecture]. But as you
can read in [ShibbolethAuthHowTo the Shibboleth Authentication HowTo],
a big problem with AAI and webapplications is authentication on the
backend (with Horde/IMP that would be the mailservers). What we needed
was a way to prevent the password passing the webmail servers AND the
mailservers.
+
+
Meet CAS: "Central Authentication System". It was originally
developed by Yale and then adopted by the JA-SIG group. The ESUP
consortium is also actively developing in the CAS area.
+
+
We chose to use CAS (http://www.ja-sig.org/products/cas/index.html)
as an authentication mechanism on top of Shibboleth. Because both
Shibboleth and CAS do the initial authentication at the CAS server,
users will see it as one integrated SSO system. Specific information
about our implementation of CAS and Horde can be found at
http://shib.kuleuven.be/docs/horde3-cas/
+
+
First we used the ESUP pam module (referenced
[http://www.ja-sig.org/wiki/display/CAS/PAM+Module here]) to let our
mailservers use the CAS server as a possible authentication service.
Here's how the cas lines in our mailserver pam-config looks like:
+
{{/etc/pam.conf:}}
+
+
<code>
+
imap auth sufficient /usr/lib/security/pam_cas.so
-simap://127.0.0.1 -f/etc/pam_cas.conf
+
imap auth sufficient /usr/lib/security/pam_ldap.so try_first_pass
+
</code>
+
+
For a Debian+Dovecot-and-ldap machine the entire file could look
like (/etc/pam.d/dovecot):
+
[20081009 Added by Velpi]
+
<code>
+
auth sufficient /lib/security/pam_cas.so -simap://127.0.0.1
-f/etc/pam_cas.conf
+
auth sufficient pam_ldap.so config=/etc/pam_ldap.conf
+
account required pam_ldap.so config=/etc/pam_ldap.conf
+
session required pam_ldap.so config=/etc/pam_ldap.conf
+
</code>
+
+
+
+
{{/etc/pam_cas.conf:}}
+
+
<code>
+
host cas.example.com
+
port 80
+
uriValidate /cas/proxyValidate
+
ssl off
+
debug off
+
proxy https://webmail.example.com/hordecas/casProxy.php
+
trusted_ca /etc/pki/example.com.chain
+
</code>
+
+
note that this configuration means we're validating the PT to our
CAS server at port 80 (regular http), which isn't the best thing to do
considering security, but it saves quite some CPU cycles.
+
If you're not sure about the network between your IMAP and CAS
server then certainly use SSL, port 443 and trusted_ca!
+
+
+
+
Next step was to make the ESUP Horde CAS authentication driver work
on our webmail servers using Horde 3.1.1 and IMP 4.1.2.
+
+
For now I'll just copy/paste Velpi's *notes*:
+
+
HOWTO CASify HORDE3 AND IMP4 [Velpi;20051201, Kaos99; 20060620, ...]
+
############################
+
+
Tested succesfully using standard Debian packages [20051206]
+
* Horde 3.0.4-4
+
* IMP 4.0.2-2
+
Connection problems when using horde from CVS [framework_3 20051220]
+
Tested succesfully using standard horde release packages [20060620]
+
* Horde 3.1.1
+
* IMP 4.1.2
+
* phpCAS 0.4.22-RC with patches (see below)
+
Tested succesfully using standard horde release packages [20081009]
+
* Horde 3.1.3
+
* IMP 4.1.3
+
* phpCAS 0.6.0
+
+
+
First, install a basic horde system
+
Configure it to use IMAP auth for horde-auth
+
Set imp/conf/servers.php correctly for your backend and set
'hordeauth' => true
+
You will need an IMAPPROXY to cache the connections when using CAS.
It is a good habit to install it too when not using CAS.
+
We use up-imapproxy from http://www.imapproxy.org/ .
+
+
Check your current system so everything works at this point (DO IT!)
+
Now we can start patching it to use CAS
+
(if you didn't check your "normal" system at this point you will
most likely curse if you need to debug, you have been warned...)
+
+
+
+
1) configure Apache
+
Apache HAS to be configured to use SSL for horde when using CAS. CAS
relies on SSL to make sure it's talking to right server, that and
encryption of course.
+
PHP (curl) should trust the certificateS that will be offered by
your CAS-server. This means you need to feed the certificate of the
(root CA of the) CAS server to Apache in its trust directive.
+
-----------httpd.conf------------
+
SSLCertificateFile /etc/pki/myHORDEserver.pem
+
SSLCertificateChainFile /etc/pki/ca_cert.pem
+
#added for the trust mechanism----
+
SSLCACertificateFile /etc/pki/ca_cert.pem
+
#----added
+
---------------------------------
+
If you see an error in CAS logs about a missing PGTIou then you did
this step wrong.
+
+
+
[You may also consider downloading the Horde-CAS package from the
ESUP consortium that does every one of the next steps automatically.
It is located at
http://www.esup-portail.org/consortium/espace/download/horde/]
+
+
+
2) install phpCAS library in horde
+
phpCAS uses domxml for php4.3, php5 means phpCAS will use a
conversion class automatically. The Auth driver for Horde checks
whether all necessary components are installed.
+
K.U.Leuven's Horde-CAS authentication driver is patched to use PHP5.
This means the check for domxml is commented out.
+
+
phpCAS has become a JA-SIG project, see:
+
http://www.ja-sig.org/wiki/display/CASC/phpCAS
+
(extract the package and)
+
[change the path to your horde/php lib dir accordingly]
+
phpCAS 0.6 and lower:
+
<code>
+
mkdir $HORDE_DIR/lib/CAS/
+
cp -r $PHPCAS_SOURCE_DIR/CAS/* $HORDE_DIR/lib/CAS/
+
</code>
+
phpCAS 1.0 and higher (DOES NOT WORK: K.U.Leuven driver needs
adjustments!! To be continued...; 20081009):
+
<code>
+
mkdir $HORDE_DIR/lib/CAS/
+
cp -r $PHPCAS_SOURCE_DIR/CAS.php $HORDE_DIR/lib/CAS/
+
mkdir $HORDE_DIR/lib/CAS/CAS/
+
cp -r $PHPCAS_SOURCE_DIR/CAS/* $HORDE_DIR/lib/CAS/CAS/
+
</code>
+
+
3) install horde driver and proxyticketReceptor script for phpCAS
+
K.U.Leuven made these two files public available with some modifications.
+
You can download them from
http://shib.kuleuven.be/docs/horde3-cas/horde_cas_auth_driver/
+
[the CAS auth driver for horde]
+
cp $CAS_DIR/cas.php $HORDE_DIR/lib/Horde/Auth/
+
[the callback url for the PGT=proxyticketReceptor]
+
cp $CAS_DIR/casProxy.php $HORDE_DIR/
+
//--
+
IMPORTANT NOTE:
+
for CAS3: the regex matches to "PT" AND to "ST"
+
CAS2: ST, PGT, PT
+
CAS3: ST, TGT (PT's are now regular STs and PGTs are now TGTs)
+
//--
+
+
+
4) set IMP to use horde credentials
+
/imp/config/server.php
+
hordeauth => true
+
+
+
+
5) patch IMP: IMP has to request a new PT if necessary (PT are only
valid for ONE login at the IMAP)
+
notes:
+
* hordeauth=true => horde "pass" will be a PT that phpCAS has
already requested
+
* imapproxy HAS to be used, else IMP will need to detect that this
PT is invalid for login and request a new one FOR EACH REQUEST!
[imapproxy solves this problem smoothly, don't hesitate to use it;
SASLauthd should solve this too if you are using a cyrus IMAP backend
or so]
+
+
<code>
+
diff -ru1b /usr/src/imp-h3-4.1.2/lib/Auth/imp.php imp/lib/Auth/imp.php
+
--- /usr/src/imp-h3-4.1.2/lib/Auth/imp.php 2006-04-10
07:03:44.000000000 +0200
+
+++ imp/lib/Auth/imp.php 2006-05-05 11:41:27.000000000 +0200
+
@@ -268,2 +268,11 @@
+
+
+ //VELPI--
+
+ $entry = sprintf('LOGIN OK %s to %s:%s[%s] as %s',
+
+ $_SERVER['REMOTE_ADDR'],
+
+ $_SESSION['imp']['server'],
+
+ $_SESSION['imp']['port'],
+
+ $_SESSION['imp']['protocol'],
+
+ $_SESSION['imp']['user']);
+
+ Horde::logMessage($entry, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ //--VELPI
+
return true;
+
+
diff -ru1b /usr/src/imp-h3-4.1.2/lib/IMAP.php imp/lib/IMAP.php
+
--- /usr/src/imp-h3-4.1.2/lib/IMAP.php 2006-04-10 07:03:44.000000000 +0200
+
+++ imp/lib/IMAP.php 2006-05-10 09:27:09.000000000 +0200
+
@@ -103,11 +103,58 @@
+
}
+
-
+
+ /* CAS: [VELPI]
+
+ GrEaT: the new IMP version automaticaly retries => so we
can just hop the wagon and get a new ticket on failure!
+
+ Login failure might mean bad password: always retry when
using CAS because we'll request a new password if needed.
+
+ Do 4 attempts, assume current pasword should work most of
the time (cache/proxy timeout should be large enough!):
+
+ 1) current pwd
+
+ 2) new ticket
+
+ 3) retry with the same, new ticket (after short sleep)
+
+ 4) another new ticket
+
+ */
+
+ //keep retry-ing connect: max 4 attemps; stop when "login
failure" and not using CAS (CAS=request new pwd on fail)
+
while (($ret === false) &&
+
- !strstr(strtolower(imap_last_error()), 'login failure') &&
+
- (++$i < 3)) {
+
- if ($i != 0) {
+
+ ! ( (strstr( strtolower(imap_last_error()), 'login
failure' ) )
+
+ && ($GLOBALS['conf']['auth']['driver'] !=
"cas") ) &&
+
+ (++$i < 4)) {
+
+ if ($i > 0) {
+
+ //every pass except the first
+
+ Horde::logMessage("short sleep hoping for delay fix
on IMAP connect", __FILE__, __LINE__, LOG_INFO);
+
sleep(1);
+
- }
+
+
+
+ //-----CAS: get a new ticket if connection
is lost-----
+
+ Horde::logMessage("login fail on pass [$i]",
__FILE__, __LINE__, LOG_INFO);
+
+ //request new ticket on each second or fourth pass
+
+ if ( ($i==1 || $i==3) &&
$GLOBALS['conf']['auth']['driver'] == "cas") {
+
+ Horde::logMessage("login fail
user=".$this->_user."; serverString=".$this->_serverString.";
requesting new ticket [pass $i]"
+
+ , __FILE__, __LINE__, LOG_WARN);
+
+ $auth =
&Auth::singleton($GLOBALS['conf']['auth']['driver']);
+
+ if(is_a($auth,"Auth_composite")) {
+
+ if (($login_driver =
Auth::_getDriverByParam('loginscreen_switch', $auth->_params)) &&
+
+ $auth->_loadDriver($login_driver)) {
+
+ $this->_pass =
$auth->_drivers[$login_driver]->getNewPT();
+
+ }
+
+ }
+
+ elseif(is_a($auth,"Auth_cas")) {
+
+ $this->_pass = $auth->getNewPT();
+
+ }
+
+ Horde::logMessage('new proxy
ticket='.$this->_pass.' for user='.$this->_user, __FILE__, __LINE__,
LOG_DEBUG);
+
+ } //END if cas
+
+ //this isn't needed since cas.php solves that
itself, so let's not do this (keep it here as a reference)
+
+ //$_SESSION['imp']['pass'] =
Secret::write(Secret::getKey('imp'), $this->_pass);
+
+ //-----CAS: END get a new ticket if connection lost-----
+
+
+
+ } //END if $i>0
+
+
+
+ Horde::logMessage("IMAP connect
[$i]:".$this->_serverString.' || '. $mbox .' || '. $this->_user
+
+ .' || '. $this->_pass.' || '.
$flags, __FILE__, __LINE__, LOG_DEBUG);
+
+ //the actual login attempt:
+
$ret = @imap_open($this->_serverString . $mbox,
$this->_user, $this->_pass, $flags);
+
}
+
+ if ($ret===false) {
+
+ //still failed, and we're not going to try again...
so let's send out some info to the admin (=> read the logs plz)
+
+ $local_severity=LOG_INFO;
+
+ //if we're using CAS this is a severe error
+
+ if ($GLOBALS['conf']['auth']['driver'] == "cas")
$local_severity=LOG_ERR;
+
+ Horde::logMessage('LOGIN FAILED to
serverString='.$this->_serverString, __FILE__, __LINE__,
$local_severity);
+
+ }
+
+ Horde::logMessage('openIMAPStream return value is: '.$ret,
__FILE__, __LINE__, LOG_DEBUG);
+
return $ret;
+
@@ -129,3 +176,5 @@
+
if (empty($_SESSION['imp']['stream'])) {
+
+ Horde::logMessage('no stream in session; requesting new
IMAP stream', __FILE__, __LINE__, LOG_DEBUG);
+
if (($_SESSION['imp']['stream'] =
$this->openIMAPStream($mbox, $flags))) {
+
+ Horde::logMessage('new stream opened for
mbox='.$mbox, __FILE__, __LINE__, LOG_DEBUG);
+
$this->_openMbox = $mbox;
+
@@ -135,3 +184,2 @@
+
}
+
-
+
if (!empty($_SESSION['imp']['imap_server']['timeout'])) {
+
@@ -154,2 +202,3 @@
+
if (($this->_openMbox != $mbox) || ($this->_mboxFlags != $flags)) {
+
+ Horde::logMessage('imap_reopen: changing to
mbox='.$mbox, __FILE__, __LINE__, LOG_DEBUG);
+
$result = @imap_reopen($_SESSION['imp']['stream'],
$this->_serverString . $mbox, $flags);
+
+
diff -ru1b /usr/src/imp-h3-4.1.2/lib/Session.php imp/lib/Session.php
+
--- /usr/src/imp-h3-4.1.2/lib/Session.php 2006-05-10
00:05:40.000000000 +0200
+
+++ imp/lib/Session.php 2006-06-20 10:25:54.000000000 +0200
+
@@ -205,3 +205,2 @@
+
$_SESSION['imp']['mailbox'] = $_SESSION['imp']['thismailbox'] = '';
+
-
+
/* Try to authenticate with the given information. */
+
@@ -261,2 +260,3 @@
+
* (per RFC 3501 [6.3.8]). */
+
+
+
$box =
@imap_getmailboxes($_SESSION['imp']['stream'], IMP::serverString(),
$val);
+
@@ -274,2 +274,3 @@
+
/* Auto-detect namespace parameters from IMAP server. */
+
+/* VELPI: auto-detect fails with CAS&imapproxy => don't use
+
$res =
$imapclient->login($_SESSION['imp']['user'], $password);
+
@@ -287,7 +288,10 @@
+
$_SESSION['imp']['imap_server']['children'] =
$imapclient->queryCapability('CHILDREN');
+
-
+
+*/
+
/* Determine if the search command supports the current
+
* browser's charset. */
+
+/*
+
$charset = NLS::getCharset();
+
$_SESSION['imp']['imap_server']['search_charset'] = array($charset =>
$imapclient->searchCharset($charset));
+
+ $imapclient->logout();
+
+*/
+
</code>
+
+
6) configure horde to use CAS
+
note: don't forget to tell IMP to try hordeauth (imp/config/servers.php)
+
you might want to use the built-in administration tools, but real
men do it with vi ;)
+
enabling CAS is easy now, just tell horde to use it:
+
--------horde/config/conf.php---------- [part of! replace the auth
thingies with something like this]
+
<code>
+
//make sure horde won't put the CAS login screen in a frame, this
will seriously mess up the browser window :(
+
$conf['menu']['always'] = false
+
+
//please make me admin
+
$conf['auth']['admins'] = array('u0049919');
+
...
+
//checkip is nice, but not when you're using NAT so turn it off :s
+
$conf['auth']['checkip'] = false;
+
...
+
//host name of your CAS server
+
$conf['auth']['params']['hostspec'] = 'myCASserver';
+
//most likely 443
+
$conf['auth']['params']['hostport'] = 443;
+
//the part that comes after the hostname eg 'cas' in https://myCASserver/cas
+
$conf['auth']['params']['hostpath'] = 'cas';
+
//the script that will receive PT's (part of phpCAS)
+
$conf['auth']['params']['proxyback'] =
'https://thisHORDEserver/horde/casProxy.php';
+
//PT's can be saved in a database too if you like; but a writable dir is fine
+
//note: should be writable by user that runs PHP/horde
+
$conf['auth']['params']['tmpdir'] = '/tmp';
+
+
//hooks into horde's as an ACL check (eg to LDAP); see hooks.php
+
$conf['auth']['params']['authorisation'] = false;
+
+
//you will need to see some logs at first to check everything,
fairly verbose though
+
$conf['auth']['params']['debug'] = true;
+
//note: should be writable by user that runs PHP/horde
+
$conf['auth']['params']['debug_file'] = '/tmp/hordeaai-cas.log';
+
+
//yup, we're using cas now
+
$conf['auth']['driver'] = 'cas';
+
+
...
+
$conf['log']['name'] = '/tmp/hordeaai.log';
+
</code>
+
---------------------------------------
+
Please note that CAS will request a PT for the service that it is
trying to connect to.
+
This means that the IMAP server that checks the PT needs to do that
with the same service name as the ticket was requested for!
+
(when using an IMAPPROXY -which you should- the service name will be
"imap://127.0.0.1" or "imap://localhost")
+
+
+
+
7) patch horde configuration interface
+
notes:
+
* horde uid (login name) will be the CAS netId when authenticated
+
* CAS does no authorisation, everybody that can login to CAS, can
enter horde (if no extra measures are taken, see next topic)
+
+
enable configuration settings for horde auth:
+
---------horde/config/conf.xml---------
+
<code>
+
@@ -132,6 +132,19 @@
+
</configdescription>
+
</case>
+
+ <case name="cas" desc="CAS authentication">
+
+ <configsection name="params">
+
+ <configstring name="hostspec" desc="The hostname of the CAS
server">cas.kuleuven.be</configstring>
+
+ <configinteger name="hostport" desc="The HTTPS Port of the
CAS server">443</configinteger>
+
+ <configstring name="hostpath" desc="The root web path of the
CAS server" required="false">cas</configstring>
+
+ <configstring name="proxyback" desc="The proxy URL of
horde">https://webmail.kuleuven.be/horde3/casProxy.php</configstring>
+
+ <configstring name="tmpdir" desc="Temporary">/tmp</configstring>
+
+ <configboolean name="authorisation" desc="Use hook for
authorisation (function _cas_hook_authorisation)">false</configboolean>
+
+ <configboolean name="debug" desc="Debugging">false</configboolean>
+
+ <configstring name="debug_file" desc="Debugging
file">/tmp/phpCAS.log</configstring>
+
+ </configsection>
+
+ </case>
+
+
+
<case name="ftp" desc="FTP authentication">
+
<configsection name="params">
+
<configstring name="hostspec" desc="The hostname or IP
address of the FTP
+
</code>
+
---------------------------------------
+
+
+
+
8) patch horde's hooks if you want authorisation (=check user with
another backend)
+
note: this has nothing to do with AUTHENTICATION! Meaning you don't
need this to get CAS working.
+
note: this is a configurable option (horde config.php:
$conf['auth']['params']['authorisation'])
+
don't forget to configure this correctly if you want to use it (eg
LDAP settings)
+
---------horde/config/hooks.php---------
+
<code>
+
if (!function_exists('_cas_hook_authorisation')) {
+
function _cas_hook_authorisation($username = null)
+
{
+
if(empty($username)) {
+
return(false);
+
}
+
+
$ldapServer = '__LDAP_HOST__';
+
$ldapPort = '__LDAP_PORT__';
+
$searchBase = '__LDAP_BASEDN__';
+
$filter = "(&(uid=%s)(objectclass=eduPerson)(mail=*))";
+
+
if(! $ds = @ldap_connect($ldapServer, $ldapPort)){
+
return(false);
+
}
+
+
$filter = sprintf($filter,$username );
+
+
$searchResult = ldap_search($ds, $searchBase, $filter,array('uid'));
+
+
$information = @ldap_get_entries($ds, $searchResult);
+
@ldap_free_result($searchResult);
+
@ldap_close($ds);
+
+
if(!is_array($information) || $information['count']!=1) return(false);
+
return(true);
+
}
+
}
+
</code>
+
---------------------------------------
+
+
+
[optional steps]
+
+
*) redirect on logout (highly recommended)
+
Logging out is a little less easy when using a WebISO since it will
automatically re-login when there is still a session with the central
server.
+
A simple workaround is to make the redirect on logout link to a
location that doesn't need authentication.
+
----horde/config/conf.php----
+
<code>
+
...
+
$conf['auth']['redirect_on_logout'] = 'http://cas.example.be/cas/logout';
+
// or $conf['auth']['redirect_on_logout'] =
'https://idp.example.be/shibboleth-idp/logout.jsp?return=http://webmail.example.be';
+
...
+
</code>
+
---------------------------------------
+
+
*) adjust the standard login page (recommended)
+
You might want to adjust this page so it doesn't show a login box
when using CAS.
+
----horde/config/conf.php----
+
<code>
+
//redirect back to IMP to make sure there's no frame-in-frame when
sth goes wrong
+
$conf['auth']['alternate_login'] =
'https://cas.example.be/cas/login?service=https://'.$_SERVER['SERVER_NAME'].'/horde/imp';
+
</code>
+
---------------------------------------
+
+
--INSTALL COMPLETED--
+
+
+
*) contributed by Maja Gorecka-Wolniewicz, Uczelniane Centrum Informatyczne:
+
When a IMAP server is using non-standard port the CAS auth driver keeps
+
asking for ticket for service imap://name while the ticket for
+
imap://name:port is needed.
+
I've added in function __getIMPVars() after
+
+
<code>
+
$this->_imapService = $p."://".$servers[$server]['server'];
+
</code>
+
+
the code
+
+
<code>
+
if ( $servers[$server]['port'] != 143 ) $this->_imapService
.=":".$servers[$server]['port'];
+
</code>
+
+
+
Now it's time for debugging fun!
+
try checking your email and keep an eye on these files:
+
* at horde server: logfile of CAS that you specified (needs to be
writable by user that runs PHP/horde), possibly apache on SSL errors
+
* you might want to check imapproxy logs (also see "pimpstat")
+
* at IMAP: /var/log/auth.log and /var/log/syslog
+
+
DEBUG HINTS:
+
* horde uid (login name) will be the CAS netId when authenticated
+
* HORDE: see /tmp/hordeaai-cas.log (when debug=true and configured
like in this document; needs to be writable by user that runs
PHP/horde; possibly see apache on SSL errors)
+
=> if contains "domxml_open_mem failed": the response from CAS
server is not XML: use your browser to go to the URL that phpCAS shows
in the logs right above the error
+
* IMAP proxy: see /var/log/mail.log (also see "pimpstat")
+
* IMAP server: see /var/log/auth (and /var/log/syslog)
- => if contains "<time> mail imapd[22677]: Command stream end of
file, while reading line user=... host=...
- <time> mail PAM_cas[22732]: authentication failure
for user '...' : bad CAS ticket."
- then check /etc/pam.d/imapd whether the "old" PAM login module
(system-auth? shadow?) is set to "sufficient" (NOT to "required"!)
+
* CAS server: see $TOMCAT/logs/cas3-server.log
+
More information about the cvs
mailing list