Configuration
To use the module, first make sure that it is accessible as /lib/security/pam_oath.so:
# ln -s /usr/local/lib/security/pam_oath.so /lib/security/pam_oath.so
# ls -la /lib/security/pam_oath.so
lrwxrwxrwx 1 root root 35 Dec 4 10:49 /lib/security/pam_oath.so -> /usr/local/lib/security/pam_oath.so
#
The next step is to configure PAM to use the module. One simple way for testing is to configure it for "su". Make sure you have a root window open before making any changes!
# head -1 /etc/pam.d/su
auth [user_unknown=ignore success=ok] pam_oath.so debug usersfile=/etc/users.oath window=20
#
The next step is to create a user credentials file. As you can see, the PAM configuration uses /etc/users.oath for this. So we create the file:
# cat>/etc/users.oath
HOTP root - 00
# chmod go-rw /etc/users.oath
#
The file format handles any whitespace as field separator. You may also add lines starting with # for comments. The file format is documented here: http://code.google.com/p/mod-authn-otp/wiki/UsersFile
WARNING! The above added an OATH secret of all-zeros, which leads to no security. In production, replace "00" with a randomly generate hex encoded data of say, 20 bytes in size.
To test the setup, we need to generate some one-time passwords. The "oathtool" is handy for this purpose. Replace 00 with the key you generated.
# oathtool/oathtool -w 5 00
328482
812658
073348
887919
320986
435986
#
So let’s test this by running su. At the prompt, you type the password (in this example, "pw") concatenated with the OTP (in this example, "328482").
jas@mocca:~$ su
[pam_oath.c:parse_cfg(122)] called.
[pam_oath.c:parse_cfg(123)] flags 0 argc 4
[pam_oath.c:parse_cfg(125)] argv[0]=alwaysok
[pam_oath.c:parse_cfg(125)] argv[1]=debug
[pam_oath.c:parse_cfg(125)] argv[2]=usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(125)] argv[3]=window=20
[pam_oath.c:parse_cfg(126)] debug=1
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=0
[pam_oath.c:parse_cfg(132)] window=20
[pam_oath.c:pam_sm_authenticate(162)] get user returned: root
One-time password (OATH) for `root':
[pam_oath.c:pam_sm_authenticate(237)] conv returned: 328482
[pam_oath.c:pam_sm_authenticate(297)] OTP: 328482
[pam_oath.c:pam_sm_authenticate(308)] authenticate rc 0 last otp Wed Jun 3 19:22:50 1931
[pam_oath.c:pam_sm_authenticate(329)] done. [Success]
[pam_oath.c:pam_sm_setcred(341)] called.
[pam_oath.c:pam_sm_setcred(347)] retval: 0
[pam_oath.c:pam_sm_setcred(367)] done. [Success]
mocca:/home/jas#
Success!
The code should have updated the /etc/users.oath file to record the incremented counter, last OTP and timestamp. Inspect the new value as follows:
# cat /etc/users.oath
HOTP root - 00 0 328482 2009-12-07T23:23:49L
#
In production use you will want to remove the debug parameter in the PAM configuration, and make sure you use random keys instead of 00.
Two-factor authentication
To also verify passwords, you need to configure the one-time password length for the PAM module:
# head -1 /etc/pam.d/su
auth requisite pam_oath.so usersfile=/etc/users.oath window=20 digits=6
#
Next specify a password in the users.oath file:
# cat>/etc/users.oath
HOTP root pw 00
# chmod go-rw /etc/users.oath
#
Then test it:
jas@mocca:~$ su
[pam_oath.c:parse_cfg(122)] called.
[pam_oath.c:parse_cfg(123)] flags 0 argc 5
[pam_oath.c:parse_cfg(125)] argv[0]=alwaysok
[pam_oath.c:parse_cfg(125)] argv[1]=debug
[pam_oath.c:parse_cfg(125)] argv[2]=usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(125)] argv[3]=window=20
[pam_oath.c:parse_cfg(125)] argv[4]=digits=6
[pam_oath.c:parse_cfg(126)] debug=1
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=6
[pam_oath.c:parse_cfg(132)] window=20
[pam_oath.c:pam_sm_authenticate(163)] get user returned: root
One-time password (OATH) for `root':
[pam_oath.c:pam_sm_authenticate(238)] conv returned: pw820368
[pam_oath.c:pam_sm_authenticate(279)] Password: pw
[pam_oath.c:pam_sm_authenticate(297)] OTP: 820368
[pam_oath.c:pam_sm_authenticate(308)] authenticate rc 0 last otp Mon Jun 1 20:43:54 1931
[pam_oath.c:pam_sm_authenticate(330)] done. [Success]
[pam_oath.c:pam_sm_setcred(342)] called.
[pam_oath.c:pam_sm_setcred(348)] retval: 0
[pam_oath.c:pam_sm_setcred(368)] done. [Success]
mocca:/home/jas#
If you supply an incorrect password, you’ll get:
jas@mocca:~$ su
[pam_oath.c:parse_cfg(122)] called.
[pam_oath.c:parse_cfg(123)] flags 0 argc 5
[pam_oath.c:parse_cfg(125)] argv[0]=alwaysok
[pam_oath.c:parse_cfg(125)] argv[1]=debug
[pam_oath.c:parse_cfg(125)] argv[2]=usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(125)] argv[3]=window=20
[pam_oath.c:parse_cfg(125)] argv[4]=digits=6
[pam_oath.c:parse_cfg(126)] debug=1
[pam_oath.c:parse_cfg(127)] alwaysok=1
[pam_oath.c:parse_cfg(128)] try_first_pass=0
[pam_oath.c:parse_cfg(129)] use_first_pass=0
[pam_oath.c:parse_cfg(130)] usersfile=/etc/users.oath
[pam_oath.c:parse_cfg(131)] digits=6
[pam_oath.c:parse_cfg(132)] window=20
[pam_oath.c:pam_sm_authenticate(163)] get user returned: root
One-time password (OATH) for `root':
[pam_oath.c:pam_sm_authenticate(238)] conv returned: grr525990
[pam_oath.c:pam_sm_authenticate(279)] Password: grr
[pam_oath.c:pam_sm_authenticate(297)] OTP: 525990
[pam_oath.c:pam_sm_authenticate(308)] authenticate rc -8 last otp Tue Jun 2 19:29:14 1931
[pam_oath.c:pam_sm_authenticate(314)] One-time password not authorized to login as user 'root'
[pam_oath.c:pam_sm_authenticate(327)] alwaysok needed (otherwise return with 9)
[pam_oath.c:pam_sm_authenticate(330)] done. [Success]
[pam_oath.c:pam_sm_setcred(342)] called.
[pam_oath.c:pam_sm_setcred(348)] retval: 0
[pam_oath.c:pam_sm_setcred(368)] done. [Success]
mocca:/home/jas#
List of all parameters
"debug": Enable debug output to stdout.
"alwaysok": Enable that all authentication attempts should succeed
(aka presentation mode).
"try_first_pass": Before prompting the user for their password, the
module first tries the previous stacked module´s
password in case that satisfies this module as
well.
"use_first_pass": The argument use_first_pass forces the module to
use a previous stacked modules password and will
never prompt the user - if no password is
available or the password is not appropriate, the
user will be denied access.
"usersfile": Specify filename where credentials are stored, for
example "/etc/users.oath". The placeholder values
"${USER}" and "${HOME}" may be used to specify the
filename on a per-user basis. The values "${USER}" and
"${HOME}" expand to the user's username and home
directory, respectively.
"digits": Specify number of digits in the one-time password,
required when using passwords in usersfile. Supported
values are 6, 7, and 8.
"window": Specify search depth, an integer typically from 5 to 50
but other values can be useful too.
SSH Configuration
Configuring pam_oath for use with SSH is straight forward, however you should make sure the /etc/ssh/sshd_config file contains the following:
UsePAM yes
ChallengeResponseAuthentication yes
Another reported cause of problems has been SELinux, and you could experiment with disabling SELinux by doing "/usr/sbin/setenforce 0".
Alternatively, instead of "ChallengeResponseAuthentication" it should work to enable "PasswordAuthentication" instead.
Debug hint
By using a /lib/security symlink, you may point the module to a file in your build directory (for testing purposes):
# ln -s /home/jas/src/oath-toolkit/pam_oath/.libs/pam_oath.so /lib/security/pam_oath.so
# ls -la /lib/security/pam_oath.so
lrwxrwxrwx 1 root root 53 Dec 4 10:49 /lib/security/pam_oath.so -> /home/jas/src/oath-toolkit/pam_oath/.libs/pam_oath.so
#
This makes it easy to test newly built code.