Différences entre versions de « CoSign »

De Cliss XXI
Sauter à la navigation Sauter à la recherche
imported>SylvainBeucler
imported>SylvainBeucler
m (article publié sur le wiki CoSign)
 
(125 versions intermédiaires par 2 utilisateurs non affichées)
Ligne 1 : Ligne 1 :
HOWTO: I found it difficult to install CoSign 2.0.2a because the documentation contains inaccuracies, and because it can be hard to debug configuration errors. Here's a working test configuration:
+
Article publié sur http://webapps.itcs.umich.edu/cosign/index.php/Cosign_Wiki:Test_install_HOWTO
 +
 
 +
-----
 +
 
 +
HOWTO: I found it difficult to install CoSign 2.0.2a because the documentation contains inaccuracies (TODO: submit a patch), and because it can be hard to debug configuration errors. Here's a working test configuration:
  
 
CoSign is composed of 3 main parts:
 
CoSign is composed of 3 main parts:
 
* filter: this is mod_cosign, allowing Apache to automatically check the user's CoSign cookie
 
* filter: this is mod_cosign, allowing Apache to automatically check the user's CoSign cookie
* cgi: cosign.cgi and logout.cgi, for initial login and full logout
+
* cgi: cosign.cgi and logout, for initial login and full logout
 
* cosignd: the daemon that centralises authentication sessions, can be called from different computers where filter and cgi are installed
 
* cosignd: the daemon that centralises authentication sessions, can be called from different computers where filter and cgi are installed
 +
  
 
== Compilation and Installation ==
 
== Compilation and Installation ==
  
 
I'm using Apache2 here.
 
I'm using Apache2 here.
 +
Let's also try to use [http://www.pathname.com/fhs/ FHS]-compliant paths.
 +
 +
Dependencies as Debian packages:
 +
aptitude install build-essential apache2-dev openssl-dev
 +
 +
Dependencies as Mandriva packages:
 +
urpmi apache-devel make
  
 
  ./configure --enable-apache2=/usr/bin/apxs2 \
 
  ./configure --enable-apache2=/usr/bin/apxs2 \
Ligne 16 : Ligne 28 :
 
      --with-filterdb=/var/lib/cosign/filter \
 
      --with-filterdb=/var/lib/cosign/filter \
 
      --with-cosigndb=/var/lib/cosign/daemon \
 
      --with-cosigndb=/var/lib/cosign/daemon \
      --with-cosignconf=/etc/cosign.conf \
+
      --with-cosignconf=/etc/cosign/cosign.conf \
 
      --with-cosigncadir=/etc/cosign/certs/CA \
 
      --with-cosigncadir=/etc/cosign/certs/CA \
 
      --with-cosigncert=/etc/cosign/certs/cert.pem \
 
      --with-cosigncert=/etc/cosign/certs/cert.pem \
Ligne 22 : Ligne 34 :
  
 
TODO: use /usr/lib and /usr/share appropriately, also allowing using the standard --prefix=/usr/local. Use FHS-compliant /usr/lib/cosign/cgi-ssl
 
TODO: use /usr/lib and /usr/share appropriately, also allowing using the standard --prefix=/usr/local. Use FHS-compliant /usr/lib/cosign/cgi-ssl
 +
 +
Note: old versions of apxs (eg. Debian Sarge's) may not provide the libapr include path, resulting in lengthy compilation errors. In this case, provide it manually by prexifing the configure call with a CPPFLAGS variable:
 +
CPPFLAGS=-I/usr/include/apr-0 ./configure --enable-apache2=...
 +
 +
Note 2: Mandriva 2007.1's version of apxs also do not provide a lot of information, you need to help ./configure even more:
 +
CPPFLAGS=`apr-1-config --cppflags --includes` ./configure --enable-apache2=/usr/sbin/apxs ...
 +
 +
Note 3: the path to apxs may vary depending on distributions
 +
 +
Next:
  
 
  make everything \
 
  make everything \
Ligne 27 : Ligne 49 :
 
  && sudo invoke-rc.d apache2 stop && sleep 1 && sudo invoke-rc.d apache2 start
 
  && sudo invoke-rc.d apache2 stop && sleep 1 && sudo invoke-rc.d apache2 start
  
 +
APACHE_USER=www-data
 
  # mod_cosign dir
 
  # mod_cosign dir
  mkdir -p /var/lib/cosign/filter
+
  mkdir -p -m 770 /var/lib/cosign/filter
  chown www-data: /var/lib/cosign/filter
+
chgrp $APACHE_USER /var/lib/cosign/filter
 +
   
 +
# cosignd dir
 +
mkdir -p -m 2770 /var/lib/cosign/daemon
 +
groupadd cosign
 +
useradd cosign -g cosign
 +
chgrp cosign /var/lib/cosign/daemon
 +
mkdir -p /usr/lib/cosign/factor
  
  # cosignd dir
+
  # Copy customizable files somewhere else
  sudo mkdir -p /var/lib/cosign/daemon
+
# so they're not overwritten by an unfortunate 'make install'..
  sudo useradd cosign
+
  cp -a /var/lib/cosign/templates /var/lib/cosign/templates-local
  sudo chown cosign /var/lib/cosign/daemon
+
  cp -a /var/lib/cosign/html /var/lib/cosign/html-local
 +
  cp -a /var/lib/cosign/services /var/lib/cosign/services-local
 +
 
 +
== Hosts specification ==
 +
 
 +
To clearly distinguish the independent parts of CoSign, we'll add a couple aliases to <code>/etc/hosts</code>:
 +
127.0.0.1 cosignd.local weblogin.local portal.local groupware.local
 +
 
 +
Each entry could be a separate host in a real setup - but nothing prevents you from installing everything on the same machine, as we're doing now.
  
 
== Apache2 configuration ==
 
== Apache2 configuration ==
 +
 +
/etc/apache2/sites-available/cosign-config.inc:
 +
# General CoSign configuration:
 +
CosignProtected        Off
 +
CosignHostname          cosignd.local
 +
CosignRedirect          https://weblogin.local/
 +
CosignPostErrorRedirect https://weblogin.local/cosign/post_error.html
 +
CosignCrypto /etc/cosign/certs/mod_cosign.key /etc/cosign/certs/mod_cosign.crt /etc/cosign/certs/CA
  
 
/etc/apache2/sites-available/default:
 
/etc/apache2/sites-available/default:
  # Automatically added in /etc/apache2/httpd.conf by 'make install-all'
+
  # Automatically added in /etc/apache2/httpd.conf by 'make install-all' (via <code>apxs2</code>)
  LoadModule cosign_module /usr/lib/apache2/modules/mod_cosign.so
+
  #LoadModule cosign_module /usr/lib/apache2/modules/mod_cosign.so
 
   
 
   
 
  NameVirtualHost *:80
 
  NameVirtualHost *:80
 
  NameVirtualHost *:443
 
  NameVirtualHost *:443
  # TLS VirtualHost 'cause CoSign requires https login
+
 +
  # Host 1: the login/logout forms
 
  <VirtualHost *:443>
 
  <VirtualHost *:443>
 +
  # TLS VirtualHost 'cause CoSign requires https login
 +
  # If your test install doesn't work and you're not using https, you
 +
  # now know why :)
 
   SSLEngine On
 
   SSLEngine On
 
   SSLCertificateFile /etc/apache2/ssl/cert.pem
 
   SSLCertificateFile /etc/apache2/ssl/cert.pem
 
   SSLCertificateKeyFile /etc/apache2/ssl/key.pem
 
   SSLCertificateKeyFile /etc/apache2/ssl/key.pem
 +
  ServerName weblogin.local
 +
 +
 +
  Include sites-available/cosign-config.inc
 +
 
   
 
   
   Alias /cosign/ "/var/lib/cosign/html/"
+
   RedirectMatch ^/$ /cosign-bin/cosign.cgi
 
   ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/"
 
   ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/"
 +
  # CoSign images and CSS
 +
  Alias /cosign/ "/var/lib/cosign/html-local/"
 +
 +
  # Variant: publish cosign directly at the root (/) of the server
 +
  # (you need to modify the templates accordingly):
 +
  #DocumentRoot "/var/lib/cosign/cgi-ssl"
 +
  #<Directory "/var/lib/cosign/cgi-ssl/">
 +
  #  DirectoryIndex cosign.cgi
 +
  #  AddHandler cgi-script .cgi
 +
  #  Options ExecCGI
 +
  #  <Files "logout">
 +
  #    SetHandler cgi-script
 +
  #  </Files>
 +
  #</Directory>
 
   
 
   
   Include sites-available/common.inc
+
 +
   # /services is the hard-coded fall-back URL after login,
 +
  # if you didn't come from a service:
 +
  Redirect /services http://portal.local
 +
 +
  # (You can also host the pages directly here instead of redirecting
 +
  # somewhere else)
 
  </VirtualHost>
 
  </VirtualHost>
 
   
 
   
 +
# Redirect people to https if they type the URL directly:
 
  <VirtualHost *:80>
 
  <VirtualHost *:80>
   Include sites-available/common.inc
+
   ServerName weblogin.local
 +
  Redirect / https://weblogin.local/
 
  </VirtualHost>
 
  </VirtualHost>
 
/etc/apache2/sites-available/common.inc:
 
CosignProtected        Off
 
CosignHostname          localhost
 
# Don't redirect to https if we come from http
 
CosignHttpOnly          On
 
CosignRedirect          https://localhost/cosign-bin/cosign.cgi
 
CosignPostErrorRedirect https://localhost/cosign/post_error.html
 
CosignService          simpleservice
 
 
   
 
   
CosignCrypto /usr/local/cosign/certs/mod_cosign.key /usr/local/cosign/certs/mod_cosign.crt /usr/local/cosign/certs/CA
 
 
   
 
   
  Alias /cosign/ "/var/lib/cosign/html/"
+
  # Host 2: a list of available services
  ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/"
+
<VirtualHost *:80>
 +
  ServerName portal.local
 +
 +
  Include sites-available/cosign-config.inc
 +
 +
  # Don't redirect to https if we come from http
 +
  CosignHttpOnly On
 +
 +
  DocumentRoot "/var/lib/cosign/services-local"
 +
  <Directory "/var/lib/cosign/services-local/">
 +
    CosignProtected On
 +
    CosignService list
 +
  </Directory>
 +
  </VirtualHost>
 +
 +
# Host 3: other services
 +
<VirtualHost *:80>
 +
  ServerName groupware.local
 +
 +
  Include sites-available/cosign-config.inc
 +
 +
  # Don't redirect to https if we come from http
 +
  CosignHttpOnly On
 +
 +
  # Example 1: the root directory is protected
 +
  DocumentRoot /var/www/groupware/
 +
  <Directory "/var/www/groupware/">
 +
    CosignProtected On
 +
    CosignService groupware
 +
  </Directory>
 
   
 
   
Alias /services/ "/usr/local/cosign/services/"
+
  # Example 2: only a subdirectory is protected
<Directory "/usr/local/cosign/services">
+
  Alias /ldapadmin "/var/www/ldapadmin/"
  CosignProtected On
+
  <Directory "/var/www/ldapadmin/">
  </Directory>
+
    CosignProtected On
 +
    CosignService ldapadmin
 +
    CosignRequireFactor admins
 +
  </Directory>
 +
  </VirtualHost>
  
== CoSign configuration ==
 
  
Pitfalls:
+
If you're using Debian, you can easily enable SSL through:
* The <code>cgi</code> doesn't take more than 1 parameter - only service does.
+
a2enmod ssl
* <code>set cosignhost</code> is used by cgi, not cosignd. It specifies the host where cosignd runs, and is not related to replication.
+
echo "Listen 443" >> /etc/apache2/ports.conf
* Afaics 'factor' lines will always be ran and returns the name of the validated factor. This among others allows to filter which users can access to which service.
+
and restarting Apache.
* TODO: fix documentation
 
* TODO: explain what <code>service</code> is for
 
  
/etc/cosign.conf:
+
If you need a simple self-signed certificate:
  # Allow CGI access from this host
+
  cd /etc/apache2/ssl/
cgi localhost
+
  openssl req -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SABDFL/CN=*/emailAddress=me@home/" \
+
  -new -x509 -nodes -days 10000 -keyout key.pem -out cert.pem
set cosignhost localhost
+
  # check
set cosigncadir /etc/cosign/certs/CA/
+
  openssl x509 -noout -text -in cert.pem
  set cosigncert /etc/cosign/certs/cgi.crt
 
set cosignkey /etc/cosign/certs/cgi.key
 
 
  # Grant the given session type if credentials match the given factor
 
  cookie cosign-simpleservice reauth FACTOR-LDAP
 
# Argument 3 and later are name of <FORM> fields from the template
 
factor /usr/share/cosign/factor/ldap login password
 
 
set cosignlogouturl http://localhost/
 
set cosignloopurl https://localhost/cosign/looping.html
 
  
 
== Certificates generation ==
 
== Certificates generation ==
  
We'll generate our own Certificate Authority.
+
We'll generate our own Certificate Authority for the CoSign system.
  
TODO: during tests it's frequent to remove and rebuild everything. Typing password is really inconvenient in this case, and <code>-passin</code> doesn't work for 'ca'. Please find a way to make this unattented!
+
OpenSSL constraint: the country and organization must be the same for the CA and the certificates it signs.
  
 +
Note: the Apache2 certificate (the https one, not the mod_cosign one) was treated separately because it is not involved in the CoSign protocol (communication between cgi, filter and cosignd), so it need not be part of this CA tree.
 +
 +
PREVIOUS_UMASK=`umask`
 +
APACHE_USER=www-data
 +
 
  # Base OpenSSL install
 
  # Base OpenSSL install
  mkdir -p -m 755 /etc/cosign/certs/CA
+
  mkdir -p -m 755 /etc/cosign/certs
  cd mkdir -p /etc/cosign/certs
+
  cd /etc/cosign/certs
  umask 0027
+
  umask 0027 # no read access to private keys!
 
  mkdir -m 700 demoCA
 
  mkdir -m 700 demoCA
 
  pushd demoCA
 
  pushd demoCA
Ligne 122 : Ligne 214 :
 
  touch index.txt
 
  touch index.txt
 
  popd
 
  popd
 +
 +
# Generate a secure password
 +
pwgen -s1 > pass.txt
 
   
 
   
 
  # Root CA
 
  # Root CA
  # subj model from `openssl x509 -noout -text -in machin.cert`
+
  # subj model from `openssl x509 -noout -text -in stuff.crt`
 
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO/CN=Root CA/" \
 
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO/CN=Root CA/" \
   -x509 -days 365 -keyout demoCA/private/cakey.pem -out demoCA/cacert.pem
+
   -x509 -days 3650 -keyout demoCA/private/cakey.pem -out demoCA/cacert.pem \
 +
  -passout file:pass.txt
 
  chmod a+r demoCA/cacert.pem
 
  chmod a+r demoCA/cacert.pem
 
   
 
   
 
  # Certificate request and private key
 
  # Certificate request and private key
 
  # cosignd
 
  # cosignd
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cosignd/CN=localhost/" \
+
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cosignd/CN=cosignd.local/" \
 
   -nodes -keyout "cosignd.key" -out "cosignd.csr"
 
   -nodes -keyout "cosignd.key" -out "cosignd.csr"
 
  # Sign certificate
 
  # Sign certificate
  openssl ca -in "cosignd.csr" -out "cosignd.crt"
+
  openssl ca -in "cosignd.csr" -out "cosignd.crt" -days 3650 -batch -passin file:pass.txt
 +
chgrp cosign cosignd.key cosignd.crt
 
   
 
   
 
  # CGI
 
  # CGI
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cgi/CN=localhost/" \
+
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cgi/CN=cgi-1/" \
 
   -nodes -keyout "cgi.key" -out "cgi.csr"
 
   -nodes -keyout "cgi.key" -out "cgi.csr"
 
  # Sign certificate
 
  # Sign certificate
  openssl ca -in "cgi.csr" -out "cgi.crt"
+
  openssl ca -in "cgi.csr" -out "cgi.crt" -days 3650 -batch -passin file:pass.txt
  chgrp www-data cgi.key cgi.crt
+
  chgrp $APACHE_USER cgi.key cgi.crt
 
   
 
   
 
  # mod_cosign
 
  # mod_cosign
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO mod_cosign/CN=localhost/" \
+
  openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO mod_cosign/CN=mod_cosign-1/" \
 
   -nodes -keyout "mod_cosign.key" -out "mod_cosign.csr"
 
   -nodes -keyout "mod_cosign.key" -out "mod_cosign.csr"
 
  # Sign certificate
 
  # Sign certificate
  openssl ca -in "mod_cosign.csr" -out "mod_cosign.crt"
+
  openssl ca -in "mod_cosign.csr" -out "mod_cosign.crt" -days 3650 -batch -passin file:pass.txt
 +
 +
# Return to normal permissions
 +
umask $PREVIOUS_UMASK
 +
unset PREVIOUS_UMASK
 
   
 
   
 
  # Allowed certs path
 
  # Allowed certs path
Ligne 155 : Ligne 256 :
 
   
 
   
 
  # Tests - cf. http://www.umich.edu/~umweb/software/cosign/faq.html
 
  # Tests - cf. http://www.umich.edu/~umweb/software/cosign/faq.html
 +
openssl verify -CApath CA/ -purpose sslserver cosignd.crt
 
  openssl verify -CApath CA/ -purpose sslclient cgi.crt mod_cosign.crt
 
  openssl verify -CApath CA/ -purpose sslclient cgi.crt mod_cosign.crt
  openssl verify -CApath CA/ -purpose sslserver cosignd.crt
+
  # Once you've got your cosignd running, you can test the connection:
  openssl s_client -connect localhost:6663 -cert cgi.crt -key cgi.key -CApath CA/ \
+
  openssl s_client -connect cosignd.local:6663 -cert cgi.crt -key cgi.key -CApath CA/ \
 
   -showcerts -state -debug -crlf -starttls smtp
 
   -showcerts -state -debug -crlf -starttls smtp
 
  # DO CHECK THAT YOU GET "Verify return code: 0 (ok)"!!!
 
  # DO CHECK THAT YOU GET "Verify return code: 0 (ok)"!!!
 
  # (s_client won't stop on error)
 
  # (s_client won't stop on error)
 +
 +
# Remove password (remember it :))
 +
rm pass.txt
  
== cosignd server start ==
+
== CoSign configuration ==
  
TODO: use/adapt <code>scripts/startup/cosignd</code>
+
Pitfalls:
 +
* The <code>cgi</code> doesn't take more than 1 parameter - only service does.
 +
* <code>set cosignhost</code> is used by cgi, not cosignd. It specifies the host where cosignd runs, and is not related to replication.
 +
* Don't end lines with a comment, especially the <code>factor</code> ones - you'll keep getting "please enter your login and password" because CoSign will interpret the comment as additional parameters.
 +
* TODO: fix documentation
 +
 
 +
/etc/cosign/cosign.conf:
 +
## [Common to cgi and cosignd]
 +
# TLS parameters
 +
set cosigncadir /etc/cosign/certs/CA/
 +
set cosigncert /etc/cosign/certs/cgi.crt
 +
set cosignkey /etc/cosign/certs/cgi.key
 +
 +
 +
## [cosignd-specific]
 +
# Allow access to cosignd with cgi-level privileges (REGISTER new sessions)
 +
# from this CommonName (CN is from the TLS certificate)
 +
cgi cgi-1
 +
 +
# Allow cosignd access with service-level privileges (CHECK existing sessions)
 +
# from these CN (they need not match the source IP or domaine name)
 +
service mod_cosign-1 0
 +
service mod_cosign-2 0
 +
 +
 +
## [cgi-specific]
 +
# cosignd host (it must match the server's CN!)
 +
set cosignhost cosignd.local
 +
 +
# Grab this user's factor:
 +
# - argument 3 and later are name(s) of <FORM>/POST fields from the template
 +
# - at least one factor is required for authentication to succeed
 +
# - a factor executable only returns 1 factor name
 +
# - factor names can be used by mod_cosign (CosignRequireFactor)
 +
# - "-2" means this secondary script is started only if another one was successful
 +
#factor /usr/lib/cosign/factor/test login password
 +
factor /usr/lib/cosign/factor/ldap login password
 +
factor /usr/lib/cosign/factor/admin -2 login
 +
 +
# Override the default template directories,
 +
# so our changes won't be overwritten by an unfortunate 'make install'
 +
set cosigntmpldir /var/lib/cosign/templates-local
 +
 +
set cosignlogouturl https://weblogin.local/
 +
set cosignloopurl https://weblogin.local/cosign/looping.html
 +
 
 +
Also describe cosign as a known service:
 +
echo "cosign 6663/tcp" >> /etc/services
 +
 
 +
== Start the servers ==
 +
 
 +
You can use/adapt <code>scripts/startup/cosignd</code> for your needs.
 +
 
 +
Manually:
 +
# Launch server with different keys than cgi, just in case:
 +
su cosign -c \
 +
  "/usr/local/sbin/cosignd -y /etc/cosign/certs/cosignd.crt -z /etc/cosign/certs/cosignd.key"
 +
 +
# Start the monster daemon, so that old sessions are cleaned-up
 +
su cosign -c "/usr/local/sbin/monster"
 +
 
 +
Here's a <code>init.d/</code> script that uses <code>start-stop-daemon</code>:
 +
#! /bin/sh
 +
set -e
 +
 +
# start and stop the cosign daemons
 +
 +
COSIGN_DEFAULTS_FILE=/etc/default/cosign
 +
if [ -s $COSIGN_DEFAULTS_FILE ]; then
 +
    . $COSIGN_DEFAULTS_FILE
 +
fi
 +
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
 +
 +
case "$1" in
 +
  start)
 +
        echo -n "Starting cosign daemons:";
 +
        echo -n " cosignd"
 +
        if [ -s /var/run/cosignd.pid ] && kill -0 $(cat /var/run/cosignd.pid) >/dev/null 2>&1; then
 +
            echo " apparently already running."
 +
            exit 0
 +
        fi
 +
        start-stop-daemon --start --quiet --background\
 +
            --pidfile /var/run/cosignd.pid --make-pidfile \
 +
            --exec /usr/local/sbin/cosignd --chuid cosign \
 +
            -- -d $COSIGND_OPTS
 +
 +
        echo -n " monster"
 +
        if [ -s /var/run/monster.pid ] && kill -0 $(cat /var/run/monster.pid) >/dev/null 2>&1; then
 +
            echo " apparently already running."
 +
            exit 0
 +
        fi
 +
        start-stop-daemon --start --quiet --background \
 +
            --pidfile /var/run/monster.pid --make-pidfile \
 +
            --exec /usr/local/sbin/monster --chuid cosign \
 +
            -- -d $MONSTER_OPTS
 +
        echo "."
 +
        ;;
 +
  stop)
 +
        echo -n "Stopping cosign daemons:"
 +
        echo -n " cosignd"
 +
        start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/cosignd.pid
 +
        rm -f /var/run/cosignd.pid
 +
        echo -n " monster"
 +
        start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/monster.pid
 +
        rm -f /var/run/monster.pid
 +
        echo "."
 +
        ;;
 +
 +
  restart)
 +
        $0 stop
 +
        $0 start
 +
        ;;
 +
 +
  *)
 +
        echo "Usage: $0 {start|stop|restart}"
 +
        exit 1
 +
esac
 +
 +
exit 0
 +
 
 +
and in <code>/etc/default/cosign</code>:
 +
COSIGND_OPTS="-y /etc/cosign/certs/cosignd.crt -z /etc/cosign/certs/cosignd.key"
 +
MONSTER_OPTS=
 +
 
 +
Last, you'll need a cron job to clean-up old filter sessions, <code>/etc/cron.d/cosign</code>:
 +
# Delete mod_cosign files that are older than a day:# Delete mod_cosign files that are older than a day:
 +
0 0 * * *  root  find /var/lib/cosign/filter -type f -mtime +1 | xargs rm -f
 +
# Done by monster:
 +
#10 0 * * *  root  find /var/lib/cosign/daemon -type f -mtime +1 | xargs rm -f
 +
 
 +
And of course, you'll need to open port 6663/tcp of your firewall.
 +
 
 +
== Factors ==
 +
 
 +
* If you just want to test your setup, here's a simple factor that accepts admin/admin credentials (<code>/usr/lib/cosign/factor/test</code>):
 +
#!/bin/bash
 +
# Read login and password
 +
read login
 +
read pass
 +
# Check login and password
 +
if [ $login = 'admin' -a $pass = 'admin' ]; then
 +
  # Return factor name:
 +
  echo "everybody"
 +
else
 +
  # The login form will print this error message:
 +
  echo "Wrong username or password"
 +
  exit 1
 +
fi
  
# Launch server with different keys just in case:
+
* Here's a simple LDAP factor (<code>/usr/lib/cosign/factor/ldap</code>):
cd /var/lib/cosign/certs/
+
 
  /usr/local/sbin/cosignd -y cosignd.crt -z cosignd.key -x /usr/local/cosign/certs/CA/
+
  #!/usr/bin/perl
  # CA path must be a full path, because cosignd chdir to /var/lib/cosign/daemon
+
# Basic LDAP "factor" for CoSign
 +
# Copyright (C) 2007  Cliss XXI
 +
#
 +
# Permission is hereby granted, free of charge, to any person obtaining
 +
# a copy of this software and associated documentation files (the
 +
# "Software"), to deal in the Software without restriction, including
 +
# without limitation the rights to use, copy, modify, merge, publish,
 +
# distribute, sublicense, and/or sell copies of the Software, and to
 +
# permit persons to whom the Software is furnished to do so, subject to
 +
# the following conditions:
 +
#
 +
# The above copyright notice and this permission notice shall be included
 +
# in all copies or substantial portions of the Software.
 +
#
 +
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 +
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 +
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 +
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 +
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 +
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 +
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 +
#
 +
# Author: Sylvain Beucler <beuc@beuc.net>
 +
 +
use strict;
 +
 +
# Base configuration
 +
my $base = "ou=users,dc=cliss21,dc=com";
 +
my $host = "ldaps://192.168.1.149";
 +
my $user_filter = "(objectClass=posixAccount)";
 +
 +
# -----
 +
 +
# If authentication is successful, the external authenticator writes
 +
# the factor name on stdout (file descriptor 1) and exits with a value
 +
# of 0.  If an error occurs, the external authenticator writes an
 +
# error message on stdout and exits with a value of 1. All other exit
 +
# values are reserved for future use.
 +
# -- cosign.conf(5)
 +
 +
use Net::LDAP; # aptitude install libnet-ldap-perl libio-socket-ssl-perl
 +
 +
# Grab CoSign parameters from standard input
 +
my $login = <STDIN>; chomp $login;
 +
my $pass = <STDIN>;  chomp $pass;
 +
 +
# Get the login's DN
 +
my $binddn = "";
 +
my $bindpw = "";
 +
my $filter = "&$user_filter(uid=$login)";
 +
my $attrs = [];
 +
 +
my $ldap = Net::LDAP->new($host);
 +
if (!defined($ldap)) {
 +
    # Be sure to catch the error manually and not use die, otherwise
 +
    # the return code will be different than 1, making CoSign unhappy,
 +
    # and the error will be print on stderr, so will be missing in the
 +
    # web interface
 +
    print "Cannot connect to LDAP server $host\n";
 +
    exit 1;
 +
}
 +
$ldap->bind("$binddn", password => "$bindpw", version => 3);
 +
 +
my $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
 +
my $count = $mesg->count;
 +
if ($count == 0) {
 +
    print "Login failed: user '$login' does not exist.\n";
 +
    exit 1;
 +
} elsif ($count > 1) {
 +
    print "Login failed: several users '$login' exist!\n";
 +
    exit 1;
 +
}
 +
 +
my $entry = $mesg->entry();
 +
my $dn = $entry->dn();
 +
 +
# Try to login with the DN
 +
 +
$binddn = $dn;
 +
$bindpw = $pass;
 +
my $result = $ldap->bind("$binddn", password => "$bindpw", version => 3);
 +
if ($result->code()) {
 +
print "Login failed (LDAP error: " . $result->error . ")\n";
 +
exit 1;
 +
}
 +
 +
$ldap->unbind();
 +
 +
print "ldap\n"; # factor name
 +
exit 0; # success
 +
 
 +
* Here's a secondary factor, used restrict access (CosignRequireFactor) to admins:
 +
#!/usr/bin/perl
 +
# Is the user an admin?
 +
# Copyright (C) 2007  Cliss XXI
 +
#
 +
# Permission is hereby granted, free of charge, to any person obtaining
 +
  # a copy of this software and associated documentation files (the
 +
# "Software"), to deal in the Software without restriction, including
 +
# without limitation the rights to use, copy, modify, merge, publish,
 +
# distribute, sublicense, and/or sell copies of the Software, and to
 +
# permit persons to whom the Software is furnished to do so, subject to
 +
# the following conditions:
 +
#
 +
# The above copyright notice and this permission notice shall be included
 +
# in all copies or substantial portions of the Software.
 +
#
 +
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 +
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 +
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 +
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 +
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 +
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 +
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 +
#
 +
# Author: Sylvain Beucler <beuc@beuc.net>
 +
 +
use strict;
 +
 +
# Base configuration
 +
my $base = "ou=groupes,dc=cliss21,dc=com";
 +
my $host = "ldaps://192.168.1.149";
 +
 +
# -----
 +
 +
use Net::LDAP; # aptitude install libnet-ldap-perl
 +
 +
# Grab CoSign parameters from standard input
 +
my $login = <STDIN>; chomp $login;
 +
 +
# Check group membership
 +
my $binddn = "";
 +
my $bindpw = "";
 +
my $filter = "(&(cn=Admins)(memberuid=$login))";
 +
my $attrs = [ 'cn' ];
 +
 +
my $ldap = Net::LDAP->new($host);
 +
if (!defined($ldap)) {
 +
    print "Cannot connect to LDAP server $host\n";
 +
    exit 1;
 +
}
 +
$ldap->bind("$binddn", password => "$bindpw", version => 3);
 +
 +
my $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
 +
my $count = $mesg->count;
 +
$ldap->unbind();
 +
 +
if ($count > 0) {
 +
    print "admin\n";
 +
}
 +
exit 0;
  
 
== Test ==
 
== Test ==
  
Now hit https://localhost/cosign-bin/cosign.cgi
+
* Now hit http://portal.local/
 +
* It should redirect you to https://weblogin.local/ where you can login.
 +
* You now can also hit http://groupware.local/ without a new login
 +
* Create <code>/var/www/groupware/index.php</code> for testing:
 +
<?php
 +
echo "Hello " . $_SERVER['REMOTE_USER'] . "!";
 +
* You should see "Hello admin!" :)
 +
* If you get endless redirections, be sure you enabled cookies.
  
 
== Beyond ==
 
== Beyond ==
  
Check what monsterd is :)
+
* [http://www.umich.edu/~umweb/software/cosign/cosign-discuss/msg00469.html Understanding logout]
 +
* [http://webapps.itcs.umich.edu/cosign/ CoSign Wiki]
 +
* Multi-servers configuration
 +
* Replication
 +
* Proxy cookies
 +
* Kerberos gateway
 +
 
 +
== Links ==
 +
 
 +
* [https://weblion.psu.edu/trac/weblion/wiki/CompileModCosignOnWindows mod_cosign on Windows] doesn't look easy

Version actuelle datée du 13 juin 2007 à 10:27

Article publié sur http://webapps.itcs.umich.edu/cosign/index.php/Cosign_Wiki:Test_install_HOWTO


HOWTO: I found it difficult to install CoSign 2.0.2a because the documentation contains inaccuracies (TODO: submit a patch), and because it can be hard to debug configuration errors. Here's a working test configuration:

CoSign is composed of 3 main parts:

  • filter: this is mod_cosign, allowing Apache to automatically check the user's CoSign cookie
  • cgi: cosign.cgi and logout, for initial login and full logout
  • cosignd: the daemon that centralises authentication sessions, can be called from different computers where filter and cgi are installed


Compilation and Installation

I'm using Apache2 here. Let's also try to use FHS-compliant paths.

Dependencies as Debian packages:

aptitude install build-essential apache2-dev openssl-dev

Dependencies as Mandriva packages:

urpmi apache-devel make
./configure --enable-apache2=/usr/bin/apxs2 \
	    --prefix=/var/lib/cosign \
	    --sbindir=/usr/local/sbin \
	    --mandir=/usr/local/share/man \
	    --with-filterdb=/var/lib/cosign/filter \
	    --with-cosigndb=/var/lib/cosign/daemon \
	    --with-cosignconf=/etc/cosign/cosign.conf \
	    --with-cosigncadir=/etc/cosign/certs/CA \
	    --with-cosigncert=/etc/cosign/certs/cert.pem \
	    --with-cosignkey=/etc/cosign/certs/key.pem

TODO: use /usr/lib and /usr/share appropriately, also allowing using the standard --prefix=/usr/local. Use FHS-compliant /usr/lib/cosign/cgi-ssl

Note: old versions of apxs (eg. Debian Sarge's) may not provide the libapr include path, resulting in lengthy compilation errors. In this case, provide it manually by prexifing the configure call with a CPPFLAGS variable:

CPPFLAGS=-I/usr/include/apr-0 ./configure --enable-apache2=...

Note 2: Mandriva 2007.1's version of apxs also do not provide a lot of information, you need to help ./configure even more:

CPPFLAGS=`apr-1-config --cppflags --includes` ./configure --enable-apache2=/usr/sbin/apxs ...

Note 3: the path to apxs may vary depending on distributions

Next:

make everything \
&& sudo make install-all \
&& sudo invoke-rc.d apache2 stop && sleep 1 && sudo invoke-rc.d apache2 start
APACHE_USER=www-data
# mod_cosign dir
mkdir -p -m 770 /var/lib/cosign/filter
chgrp $APACHE_USER /var/lib/cosign/filter

# cosignd dir
mkdir -p -m 2770 /var/lib/cosign/daemon
groupadd cosign
useradd cosign -g cosign
chgrp cosign /var/lib/cosign/daemon
mkdir -p /usr/lib/cosign/factor
# Copy customizable files somewhere else
# so they're not overwritten by an unfortunate 'make install'..
cp -a /var/lib/cosign/templates /var/lib/cosign/templates-local
cp -a /var/lib/cosign/html /var/lib/cosign/html-local
cp -a /var/lib/cosign/services /var/lib/cosign/services-local

Hosts specification

To clearly distinguish the independent parts of CoSign, we'll add a couple aliases to /etc/hosts:

127.0.0.1 cosignd.local weblogin.local portal.local groupware.local

Each entry could be a separate host in a real setup - but nothing prevents you from installing everything on the same machine, as we're doing now.

Apache2 configuration

/etc/apache2/sites-available/cosign-config.inc:

# General CoSign configuration:
CosignProtected         Off
CosignHostname          cosignd.local
CosignRedirect          https://weblogin.local/
CosignPostErrorRedirect https://weblogin.local/cosign/post_error.html
CosignCrypto /etc/cosign/certs/mod_cosign.key /etc/cosign/certs/mod_cosign.crt /etc/cosign/certs/CA

/etc/apache2/sites-available/default:

# Automatically added in /etc/apache2/httpd.conf by 'make install-all' (via apxs2)
#LoadModule cosign_module /usr/lib/apache2/modules/mod_cosign.so

NameVirtualHost *:80
NameVirtualHost *:443

# Host 1: the login/logout forms
<VirtualHost *:443>
  # TLS VirtualHost 'cause CoSign requires https login
  # If your test install doesn't work and you're not using https, you
  # now know why :)
  SSLEngine		On
  SSLCertificateFile	/etc/apache2/ssl/cert.pem
  SSLCertificateKeyFile	/etc/apache2/ssl/key.pem
  ServerName weblogin.local


  Include sites-available/cosign-config.inc


  RedirectMatch ^/$ /cosign-bin/cosign.cgi
  ScriptAlias /cosign-bin/ "/var/lib/cosign/cgi-ssl/"
  # CoSign images and CSS
  Alias /cosign/ "/var/lib/cosign/html-local/"

  # Variant: publish cosign directly at the root (/) of the server
  # (you need to modify the templates accordingly):
  #DocumentRoot "/var/lib/cosign/cgi-ssl"
  #<Directory "/var/lib/cosign/cgi-ssl/">
  #  DirectoryIndex cosign.cgi
  #  AddHandler cgi-script .cgi
  #  Options ExecCGI
  #  <Files "logout">
  #    SetHandler cgi-script
  #  </Files>
  #</Directory>


  # /services is the hard-coded fall-back URL after login,
  # if you didn't come from a service:
  Redirect /services http://portal.local

  # (You can also host the pages directly here instead of redirecting
  # somewhere else)
</VirtualHost>

# Redirect people to https if they type the URL directly:
<VirtualHost *:80>
  ServerName weblogin.local
  Redirect / https://weblogin.local/
</VirtualHost>


# Host 2: a list of available services
<VirtualHost *:80>
  ServerName portal.local

  Include sites-available/cosign-config.inc

  # Don't redirect to https if we come from http
  CosignHttpOnly On

  DocumentRoot "/var/lib/cosign/services-local"
  <Directory "/var/lib/cosign/services-local/">
    CosignProtected On
    CosignService list
  </Directory>
</VirtualHost>

# Host 3: other services
<VirtualHost *:80>
  ServerName groupware.local

  Include sites-available/cosign-config.inc

  # Don't redirect to https if we come from http
  CosignHttpOnly On

  # Example 1: the root directory is protected
  DocumentRoot /var/www/groupware/
  <Directory "/var/www/groupware/">
    CosignProtected On
    CosignService groupware
  </Directory>

  # Example 2: only a subdirectory is protected
  Alias /ldapadmin "/var/www/ldapadmin/"
  <Directory "/var/www/ldapadmin/">
    CosignProtected On
    CosignService ldapadmin
    CosignRequireFactor admins
  </Directory>
</VirtualHost>


If you're using Debian, you can easily enable SSL through:

a2enmod ssl
echo "Listen 443" >> /etc/apache2/ports.conf

and restarting Apache.

If you need a simple self-signed certificate:

cd /etc/apache2/ssl/
openssl req -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SABDFL/CN=*/emailAddress=me@home/" \
  -new -x509 -nodes -days 10000 -keyout key.pem -out cert.pem
# check
openssl x509 -noout -text -in cert.pem

Certificates generation

We'll generate our own Certificate Authority for the CoSign system.

OpenSSL constraint: the country and organization must be the same for the CA and the certificates it signs.

Note: the Apache2 certificate (the https one, not the mod_cosign one) was treated separately because it is not involved in the CoSign protocol (communication between cgi, filter and cosignd), so it need not be part of this CA tree.

PREVIOUS_UMASK=`umask`
APACHE_USER=www-data

# Base OpenSSL install
mkdir -p -m 755 /etc/cosign/certs
cd /etc/cosign/certs
umask 0027 # no read access to private keys!
mkdir -m 700 demoCA
pushd demoCA
mkdir -m 755 newcerts
mkdir -m 700 private
echo "01" > serial
touch index.txt
popd

# Generate a secure password
pwgen -s1 > pass.txt

# Root CA
# subj model from `openssl x509 -noout -text -in stuff.crt`
openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO/CN=Root CA/" \
  -x509 -days 3650 -keyout demoCA/private/cakey.pem -out demoCA/cacert.pem \
  -passout file:pass.txt
chmod a+r demoCA/cacert.pem

# Certificate request and private key
# cosignd
openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cosignd/CN=cosignd.local/" \
  -nodes -keyout "cosignd.key" -out "cosignd.csr"
# Sign certificate
openssl ca -in "cosignd.csr" -out "cosignd.crt" -days 3650 -batch -passin file:pass.txt
chgrp cosign cosignd.key cosignd.crt

# CGI
openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO cgi/CN=cgi-1/" \
  -nodes -keyout "cgi.key" -out "cgi.csr"
# Sign certificate
openssl ca -in "cgi.csr" -out "cgi.crt" -days 3650 -batch -passin file:pass.txt
chgrp $APACHE_USER cgi.key cgi.crt

# mod_cosign
openssl req -new -subj "/C=FR/ST=NPDC/L=Lievin/O=Cliss XXI/OU=SSO mod_cosign/CN=mod_cosign-1/" \
  -nodes -keyout "mod_cosign.key" -out "mod_cosign.csr"
# Sign certificate
openssl ca -in "mod_cosign.csr" -out "mod_cosign.crt" -days 3650 -batch -passin file:pass.txt

# Return to normal permissions
umask $PREVIOUS_UMASK
unset PREVIOUS_UMASK

# Allowed certs path
mkdir -m 755 CA
ln demoCA/cacert.pem CA/
c_rehash CA/

# Tests - cf. http://www.umich.edu/~umweb/software/cosign/faq.html
openssl verify -CApath CA/ -purpose sslserver cosignd.crt
openssl verify -CApath CA/ -purpose sslclient cgi.crt mod_cosign.crt
# Once you've got your cosignd running, you can test the connection:
openssl s_client -connect cosignd.local:6663 -cert cgi.crt -key cgi.key -CApath CA/ \
  -showcerts -state -debug -crlf -starttls smtp
# DO CHECK THAT YOU GET "Verify return code: 0 (ok)"!!!
# (s_client won't stop on error)

# Remove password (remember it :))
rm pass.txt

CoSign configuration

Pitfalls:

  • The cgi doesn't take more than 1 parameter - only service does.
  • set cosignhost is used by cgi, not cosignd. It specifies the host where cosignd runs, and is not related to replication.
  • Don't end lines with a comment, especially the factor ones - you'll keep getting "please enter your login and password" because CoSign will interpret the comment as additional parameters.
  • TODO: fix documentation

/etc/cosign/cosign.conf:

## [Common to cgi and cosignd]
# TLS parameters
set cosigncadir /etc/cosign/certs/CA/
set cosigncert /etc/cosign/certs/cgi.crt
set cosignkey /etc/cosign/certs/cgi.key


## [cosignd-specific]
# Allow access to cosignd with cgi-level privileges (REGISTER new sessions)
# from this CommonName (CN is from the TLS certificate)
cgi cgi-1

# Allow cosignd access with service-level privileges (CHECK existing sessions)
# from these CN (they need not match the source IP or domaine name)
service mod_cosign-1 0
service mod_cosign-2 0


## [cgi-specific]
# cosignd host (it must match the server's CN!)
set cosignhost cosignd.local

# Grab this user's factor:
# - argument 3 and later are name(s) of <FORM>/POST fields from the template
# - at least one factor is required for authentication to succeed
# - a factor executable only returns 1 factor name
# - factor names can be used by mod_cosign (CosignRequireFactor)
# - "-2" means this secondary script is started only if another one was successful
#factor /usr/lib/cosign/factor/test login password
factor /usr/lib/cosign/factor/ldap login password
factor /usr/lib/cosign/factor/admin -2 login

# Override the default template directories,
# so our changes won't be overwritten by an unfortunate 'make install'
set cosigntmpldir /var/lib/cosign/templates-local

set cosignlogouturl https://weblogin.local/
set cosignloopurl https://weblogin.local/cosign/looping.html

Also describe cosign as a known service:

echo "cosign 6663/tcp" >> /etc/services

Start the servers

You can use/adapt scripts/startup/cosignd for your needs.

Manually:

# Launch server with different keys than cgi, just in case:
su cosign -c \
  "/usr/local/sbin/cosignd -y /etc/cosign/certs/cosignd.crt -z /etc/cosign/certs/cosignd.key"

# Start the monster daemon, so that old sessions are cleaned-up
su cosign -c "/usr/local/sbin/monster"

Here's a init.d/ script that uses start-stop-daemon:

#! /bin/sh
set -e

# start and stop the cosign daemons

COSIGN_DEFAULTS_FILE=/etc/default/cosign
if [ -s $COSIGN_DEFAULTS_FILE ]; then
    . $COSIGN_DEFAULTS_FILE
fi
export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"

case "$1" in
  start)
        echo -n "Starting cosign daemons:";
        echo -n " cosignd"
        if [ -s /var/run/cosignd.pid ] && kill -0 $(cat /var/run/cosignd.pid) >/dev/null 2>&1; then
            echo " apparently already running."
            exit 0
        fi
        start-stop-daemon --start --quiet --background\
            --pidfile /var/run/cosignd.pid --make-pidfile \
            --exec /usr/local/sbin/cosignd --chuid cosign \
            -- -d $COSIGND_OPTS

        echo -n " monster"
        if [ -s /var/run/monster.pid ] && kill -0 $(cat /var/run/monster.pid) >/dev/null 2>&1; then
            echo " apparently already running."
            exit 0
        fi
        start-stop-daemon --start --quiet --background \
            --pidfile /var/run/monster.pid --make-pidfile \
            --exec /usr/local/sbin/monster --chuid cosign \
            -- -d $MONSTER_OPTS
        echo "."
        ;;
  stop)
        echo -n "Stopping cosign daemons:"
        echo -n " cosignd"
        start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/cosignd.pid
        rm -f /var/run/cosignd.pid
        echo -n " monster"
        start-stop-daemon --stop --quiet --oknodo --pidfile /var/run/monster.pid
        rm -f /var/run/monster.pid
        echo "."
        ;;

  restart)
        $0 stop
        $0 start
        ;;

  *)
        echo "Usage: $0 {start|stop|restart}"
        exit 1
esac

exit 0

and in /etc/default/cosign:

COSIGND_OPTS="-y /etc/cosign/certs/cosignd.crt -z /etc/cosign/certs/cosignd.key"
MONSTER_OPTS=

Last, you'll need a cron job to clean-up old filter sessions, /etc/cron.d/cosign:

# Delete mod_cosign files that are older than a day:# Delete mod_cosign files that are older than a day:
0 0 * * *  root  find /var/lib/cosign/filter -type f -mtime +1 | xargs rm -f
# Done by monster:
#10 0 * * *  root  find /var/lib/cosign/daemon -type f -mtime +1 | xargs rm -f

And of course, you'll need to open port 6663/tcp of your firewall.

Factors

  • If you just want to test your setup, here's a simple factor that accepts admin/admin credentials (/usr/lib/cosign/factor/test):
#!/bin/bash
# Read login and password
read login
read pass
# Check login and password
if [ $login = 'admin' -a $pass = 'admin' ]; then
  # Return factor name:
  echo "everybody"
else
  # The login form will print this error message:
  echo "Wrong username or password"
  exit 1
fi
  • Here's a simple LDAP factor (/usr/lib/cosign/factor/ldap):
#!/usr/bin/perl
# Basic LDAP "factor" for CoSign
# Copyright (C) 2007  Cliss XXI
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
# 
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Author: Sylvain Beucler <beuc@beuc.net>

use strict;

# Base configuration
my $base = "ou=users,dc=cliss21,dc=com";
my $host = "ldaps://192.168.1.149";
my $user_filter = "(objectClass=posixAccount)";

# -----

# If authentication is successful, the external authenticator writes
# the factor name on stdout (file descriptor 1) and exits with a value
# of 0.  If an error occurs, the external authenticator writes an
# error message on stdout and exits with a value of 1. All other exit
# values are reserved for future use.
# -- cosign.conf(5)

use Net::LDAP; # aptitude install libnet-ldap-perl libio-socket-ssl-perl

# Grab CoSign parameters from standard input
my $login = <STDIN>; chomp $login;
my $pass = <STDIN>;  chomp $pass;

# Get the login's DN
my $binddn = "";
my $bindpw = "";
my $filter = "&$user_filter(uid=$login)";
my $attrs = [];

my $ldap = Net::LDAP->new($host);
if (!defined($ldap)) {
    # Be sure to catch the error manually and not use die, otherwise
    # the return code will be different than 1, making CoSign unhappy,
    # and the error will be print on stderr, so will be missing in the
    # web interface
    print "Cannot connect to LDAP server $host\n";
    exit 1;
}
$ldap->bind("$binddn", password => "$bindpw", version => 3);

my $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
my $count = $mesg->count;
if ($count == 0) {
    print "Login failed: user '$login' does not exist.\n";
    exit 1;
} elsif ($count > 1) {
    print "Login failed: several users '$login' exist!\n";
    exit 1;
}

my $entry = $mesg->entry();
my $dn = $entry->dn();

# Try to login with the DN

$binddn = $dn;
$bindpw = $pass;
my $result = $ldap->bind("$binddn", password => "$bindpw", version => 3);
if ($result->code()) {
	print "Login failed (LDAP error: " . $result->error . ")\n";
	exit 1;
}

$ldap->unbind();

print "ldap\n"; # factor name
exit 0; # success
  • Here's a secondary factor, used restrict access (CosignRequireFactor) to admins:
#!/usr/bin/perl
# Is the user an admin?
# Copyright (C) 2007  Cliss XXI
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Author: Sylvain Beucler <beuc@beuc.net>

use strict;

# Base configuration
my $base = "ou=groupes,dc=cliss21,dc=com";
my $host = "ldaps://192.168.1.149";

# -----

use Net::LDAP; # aptitude install libnet-ldap-perl

# Grab CoSign parameters from standard input
my $login = <STDIN>; chomp $login;

# Check group membership
my $binddn = "";
my $bindpw = "";
my $filter = "(&(cn=Admins)(memberuid=$login))";
my $attrs = [ 'cn' ];

my $ldap = Net::LDAP->new($host);
if (!defined($ldap)) {
    print "Cannot connect to LDAP server $host\n";
    exit 1;
}
$ldap->bind("$binddn", password => "$bindpw", version => 3);

my $mesg = $ldap->search(base => $base, filter => $filter, attrs => $attrs);
my $count = $mesg->count;
$ldap->unbind();

if ($count > 0) {
    print "admin\n";
}
exit 0;

Test

<?php
echo "Hello " . $_SERVER['REMOTE_USER'] . "!";
  • You should see "Hello admin!" :)
  • If you get endless redirections, be sure you enabled cookies.

Beyond

Links