James wrote:
If your implementation is different than the "mod_authn_yubikey" Apache module discussed in
this thread and at
this website, I would be interested in how you did it. I think it's good to have more than one way to do something in the event one of of the techniques no longer works or is no longer maintained.
Thanks for the links, James. Well, I don't know about mod_authn_yubikey (yet), will check it out later.
My method uses mod_auth_external. That module can be used to call an external program
Caveat! Caveat!
** WARNING: this is just a proof of concept, you need to consider carefully if you want to use this code
** WARNING: it involves running a setuid binary!
** WARNING: and a very crude session mechanism, that really needs improvement
** WARNING: calling an external program can be a real resource hog
Having said that..
To set mod-auth-external up, I did something like this:
$ wget
http://mod-auth-external.googlecode.com ... .11.tar.gz$ tar xzf mod_auth_external-2.2.11.tar.gz
$ cd mod_auth_external-2.2.11
$ more INSTALL
$ apxs -c mod_auth_external.c
$ apxs -i -a mod_auth_external.la
So, now I had a method to call an external program. The external program is expected to read username and password from stdin, it
has to return true or false to indicate if the username / password combination was correct.
As the yubikey software already comes with 'ykvalidate', I decided to write a little wrapper around it to do just that. However, ykvalidate will not authenticate others than yourself, unless you are root. I decided to change the source code of ykvalidate a bit so it would allow processes that run with the uid of the webserver to access entries of other users: made the program run setuid and made it check for the effective uid instead of the real uid). Recompiled it, su-ed to 'apache', tried to ykvalidate root's yubikey - and it which worked fine. Then I wrote this wrapperscript:
Code:
#!/bin/bash
read username
read yubikey
# in minutes, maximum inactive time
MAXINACTIVE=2
# config
SESSIONS=/etc/yubikey.d/sessions
# sanity
[ -z "$username" ] && exit 1
[ -z "$yubikey" ] && exit 1
[ ${#yubikey} -ne 44 ] && exit 1
[ -d $SESSIONS ] || exit 1
status=1;
if [ -f $SESSIONS/$yubikey ]
then
# continuation of earlier session
atim=$(stat -c '%X' $SESSIONS/$yubikey)
wtim=$(date +%s)
asec=$[ ( $wtim - $atim ) / 60 ]
if [ $asec -gt $MAXINACTIVE ]
then
#echo "session $yubikey is $asec minutes old, expired" >>/tmp/hk.log
# remove expired sessions
rm $SESSIONS/$yubikey
find $SESSIONS -type f -amin +$MAXINACTIVE -exec rm {} \;
status=1
else
# refresh session
#echo "session $yubikey is $asec minutes old, refreshed" >>/tmp/hk.log
touch $SESSIONS/$yubikey
status=0
fi
else
# new session
#echo "session $yubikey is new" >>/tmp/hk.log
/usr/local/bin/ykvalidate --user $username $yubikey 2>/dev/null 1>&2
status=$?
#echo "session $yubikey status: $status" >>/tmp/h
[ $status -eq 0 ] && touch $SESSIONS/$yubikey
fi
exit $status
(Note that the session mechanisme is quite crude, this still is a proof of concept, any improvements are welcomed).
(also note that I had to create the various directories and make them owned by apache).
I changed the http configuration for the website to include these rules within it's virtualhost container:
Code:
AddExternalAuth yubikey /usr/local/bin/ykvalidate_wrapper
SetExternalAuthMethod yubikey pipe
<Location /test>
AuthType Basic
AuthName Requires_Yubikey
AuthExternal yubikey
require valid-user
</Location>
Next, I restarted my Apache, surfed to
http://www.example.com/test and the box popped up, I filled in username and pressed the yubikey button in the 'password' field. Works fine. If you don't do anything for 2 minutes and surf to that same page again, you'll need to redo the authentication.