Hi all...
I've been experimenting with PAM authentication and the Yubikey, in particular, I've been tinkering with using the HMAC-SHA1 mode of the key. Ultimately I'd like to integrate this into the advanced-yubico-pam module.
As a first step, I've managed to get the YubiKey authenticating PAM using HMAC-SHA1, below is the code that I have written for this:
Code:
#!/usr/bin/python
import binascii
import hashlib
import hmac
import logging
import os
import random
import yubico.yubikey
import yubico.yubico_util
import yubico.yubico_exception
# Setup logging
logging.basicConfig( filename = '/tmp/nistcr-pam.log', \
filemode = 'a', \
level = logging.CRITICAL, \
format = '%(asctime)s %(levelname)-8s %(message)s', \
datefmt = '%d.%m.%Y %H:%M:%S')
log = logging.getLogger('nistcr-pam')
def pam_sm_authenticate(pamh, flags, argv):
try:
user = pamh.get_user(None)
except pamh.exception, e:
return e.pam_result
if user == None:
log.info('No user')
return pamh.PAM_AUTH_ERR
# Look for and initialize the YubiKey
try:
YK = yubico.yubikey.find_key(debug=False)
log.debug("Version : %s " % YK.version())
log.debug("Serial : %i" % YK.serial())
except yubico.yubico_exception.YubicoError as inst:
log.error("Yubikey Error: %s" % inst.reason)
return pamh.PAM_AUTH_ERR
# Attempt to pick up the user's key
fn = os.path.join(os.path.expanduser('~'+user),'.yubikey')
log.debug('Yubikey Configuration in ' + fn)
try:
fs = os.stat(fn)
except OSError:
log.info('Configuration file not accessible')
return pamh.PAM_AUTH_ERR
# Check for sane permissions
if (fs.st_mode & 07177):
log.error('File permissions not safe: {0:04o}'.format(fs.st_mode))
return pamh.PAM_AUTH_ERR
# Open the file
try:
fp = open(fn, 'r')
except OSError:
log.error('Failed to open configuration file')
return pamh.PAM_AUTH_ERR
# Read the key
key_hex = fp.readline()[:40]
key = binascii.a2b_hex(key_hex)
# Generate challenge
challenge = binascii.a2b_hex(hex(random.getrandbits(64*8))[2:-1])[0:63]
challenge_pad = challenge.ljust(64,chr(0))
log.debug('Challenge: ' + repr(challenge) + ' len:' + str(len(challenge)))
# Create HMAC and generate expected response
h = hmac.HMAC(key, challenge, hashlib.sha1)
expected = h.digest()
log.debug('Expecting: ' + repr(expected) + ' len:' + str(len(expected)))
# Ask the YubiKey
try:
response = YK.challenge_response(challenge_pad, slot=2)
except yubico.yubico_exception.YubicoError as inst:
log.error("Yubikey Error: %s" % inst.reason)
return pamh.PAM_AUTH_ERR
log.debug('Received: ' + repr(response) + ' len:' + str(len(response)))
if (response == expected):
return pamh.PAM_SUCCESS
else:
return pamh.PAM_AUTH_ERR
def pam_sm_setcred(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_acct_mgmt(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_open_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_close_session(pamh, flags, argv):
return pamh.PAM_SUCCESS
def pam_sm_chauthtok(pamh, flags, argv):
return pamh.PAM_SUCCESS
Very crude at this point. My next step was to try and get the advanced-yubico-pam-module installed and configured in its present state. So I clone the repository and install it in the usual manner. Some details of my machine and the revisions being used:
Code:
sh-4.1$ python --version
Python 2.6.6
sh-4.1$ uname -a
Linux zhouman 2.6.35.7-lm2f-nb #2 Wed Oct 13 00:42:58 EST 2010 mips64 ICT Loongson-2 V0.3 FPU V0.1 lemote-yeeloong-2f-8.9inches GNU/Linux
sh-4.1$ git describe
fatal: No annotated tags can describe 'b38e555356315616880a92f90c11e99b6deab85c'.
However, there were unannotated tags: try --tags.
sh-4.1$ git remote show origin
* remote origin
Fetch URL: git://github.com/Kami/python-yubico-client.git
Push URL: git://github.com/Kami/python-yubico-client.git
HEAD branch: master
Remote branch:
master tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)
Distribution is Gentoo Linux/MIPS 10.0 based on the latest O32 userland stage3.
Installation of a Python module appeared to go fine…
Code:
sh-4.1$ python setup.py build
running build
running build_py
creating build
creating build/lib
creating build/lib/yubico
copying yubico/yubico.py -> build/lib/yubico
copying yubico/yubico_exceptions.py -> build/lib/yubico
copying yubico/modhex.py -> build/lib/yubico
copying yubico/httplib_ssl.py -> build/lib/yubico
copying yubico/__init__.py -> build/lib/yubico
copying yubico/otp.py -> build/lib/yubico
sh-4.1$ sudo python setup.py install
running install
running build
running build_py
running install_lib
copying build/lib/yubico/yubico.py -> /usr/lib/python2.6/site-packages/yubico
copying build/lib/yubico/yubico_exceptions.py -> /usr/lib/python2.6/site-packages/yubico
copying build/lib/yubico/modhex.py -> /usr/lib/python2.6/site-packages/yubico
copying build/lib/yubico/httplib_ssl.py -> /usr/lib/python2.6/site-packages/yubico
copying build/lib/yubico/__init__.py -> /usr/lib/python2.6/site-packages/yubico
copying build/lib/yubico/otp.py -> /usr/lib/python2.6/site-packages/yubico
byte-compiling /usr/lib/python2.6/site-packages/yubico/yubico.py to yubico.pyc
byte-compiling /usr/lib/python2.6/site-packages/yubico/yubico_exceptions.py to yubico_exceptions.pyc
byte-compiling /usr/lib/python2.6/site-packages/yubico/modhex.py to modhex.pyc
byte-compiling /usr/lib/python2.6/site-packages/yubico/httplib_ssl.py to httplib_ssl.pyc
byte-compiling /usr/lib/python2.6/site-packages/yubico/__init__.py to __init__.pyc
byte-compiling /usr/lib/python2.6/site-packages/yubico/otp.py to otp.pyc
running install_egg_info
Removing /usr/lib/python2.6/site-packages/yubico-1.5.dev-py2.6.egg-info
Writing /usr/lib/python2.6/site-packages/yubico-1.5.dev-py2.6.egg-info
Great, now let's test it.
Code:
sh-4.1$ cd demo/
sh-4.1$ python example.py
Traceback (most recent call last):
File "example.py", line 2, in <module>
from yubico import yubico
ImportError: cannot import name yubico
Errm… didn't I just install that? Any ideas?