Yubico-pam With LDAP Binddn and Bindpw Support

yubicoYubico offers a PAM module for unix/linux systems so you are able to log in with their yubikeys. The PAM module supports LDAP for yubikey/user mapping, but unfortunately it allows only to bind anonymously (you can’t give binddn or bindpw as an argument).

Since I don’t allow anonymous binds on my LDAP server I started looking for a solution. Except for falling back to the ‘authfile’ parameter in /etc/pam.d/common-session (or whatever file you’re using), I couldn’t find anything. So I dived into the C code of pam-yubico.c (which is part of yubico-pam), implemented support for binddn and bindpw and re-built it. In order to easily reprocess this I wrote a patch.

First of all, get the source code from https://github.com/Yubico/yubico-pam.git:

git clone https://github.com/Yubico/yubico-pam.git
cd yubico-pam

Now, in order to make the modification easily to pam_yubico.c, you could use the patch I wrote:

*** pam_yubico.c Mon Jan 7 10:52:52 2013
--- pam_yubico.c Mon Jan 7 10:55:31 2013
***************
*** 111,116 ****
--- 111,118 ----
char *user_attr;
char *yubi_attr;
char *yubi_attr_prefix;
+ char *binddn;
+ char *bindpw;
int token_id_length;
enum key_mode mode;
char *chalresp_path;
***************
*** 330,337 ****
protocol = LDAP_VERSION3;
ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);

! /* Bind anonymously to the LDAP server. */
! rc = ldap_simple_bind_s (ld, NULL, NULL);
if (rc != LDAP_SUCCESS)
{
DBG (("ldap_simple_bind_s: %s", ldap_err2string (rc)));
--- 332,345 ----
protocol = LDAP_VERSION3;
ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);

! if (cfg->binddn && cfg->bindpw)
! {
! rc = ldap_simple_bind_s (ld, cfg->binddn, cfg->bindpw);
! }
! else{ /* Bind anonymously to the LDAP server. */
! rc = ldap_simple_bind_s (ld, NULL, NULL);
! }
!
if (rc != LDAP_SUCCESS)
{
DBG (("ldap_simple_bind_s: %s", ldap_err2string (rc)));
***************
*** 721,726 ****
--- 729,738 ----
cfg->yubi_attr = (char *) argv[i] + 10;
if (strncmp (argv[i], "yubi_attr_prefix=", 17) == 0)
cfg->yubi_attr_prefix = (char *) argv[i] + 17;
+ if (strncmp (argv[i], "binddn=", 7) == 0)
+ cfg->binddn = (char *) argv[i] + 7;
+ if (strncmp (argv[i], "bindpw=", 7) ==0)
+ cfg->bindpw = (char *) argv[i] + 7;
if (strncmp (argv[i], "token_id_length=", 16) == 0)
sscanf (argv[i], "token_id_length=%d", &cfg->token_id_length);
if (strcmp (argv[i], "mode=challenge-response") == 0)
***************
*** 751,756 ****
--- 763,770 ----
D (("user_attr=%s", cfg->user_attr ? cfg->user_attr : "(null)"));
D (("yubi_attr=%s", cfg->yubi_attr ? cfg->yubi_attr : "(null)"));
D (("yubi_attr_prefix=%s", cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "(null)"));
+ D (("binddn=%s", cfg->binddn ? cfg->binddn : "(null)"));
+ D (("bindpw=%s", cfg->bindpw ? cfg->bindpw : "(null)"));
D (("url=%s", cfg->url ? cfg->url : "(null)"));
D (("capath=%s", cfg->capath ? cfg->capath : "(null)"));
D (("token_id_length=%d", cfg->token_id_length));

To patch the C code, copy-paste the file into pam_yubico.patch and apply it by invoking the following command (while remaining in the yubico-pam directory):

patch -i <path_to_patch>/pam_yubico.patch

Compile and install it – short version (if you already have all the dependencies)

Compile and install with the usual commands:

autoreconf --install
./configure
make clean && sudo make install

Copy the PAM module to the right directory. On Ubuntu 12.04:

cp /usr/local/lib/security/pam_yubico.so /lib/security/

 

Compile and install it – long version (if you don’t have the dependencies yet)

First, allow installing from the following repository  so you have the most up-to-date packages:

sudo add-apt-repository ppa:yubico/stable
sudo apt-get update

Secondly, install the following dependencies:

apt-get install autoconf libtool libcurl4-gnutls-dev libpam-dev libusb-1.0 libykpers-1-dev python-software-properties

For the last few dependencies, we’ll have to install from source. First install ykclient:

mkdir ~/downloads
cd ~/downloads
wget http://yubico-c-client.googlecode.com/files/ykclient-2.9.tar.gz
tar zxvf ykclient-2.9.tar.gz
cd ykclient-2.9
./configure
make
make install

Next, install yubico-c:

cd..
git clone https://github.com/Yubico/yubico-c.git
cd yubico-c
autoreconf --install
./configure
make check && sudo make install

pkg-config is also needed. It is available in the Ubuntu repo, but if it doesn’t work, try to install from source, it worked for me:

cd ..
git clone git://anongit.freedesktop.org/pkg-config
cd pkg-config/
autoreconf --install
./configure --with-internal-glib
make check && sudo make install

Download, build and install the yubico-c-client:

cd ..
git clone https://github.com/Yubico/yubico-c-client.git
cd yubico-c-client
autoreconf --install
./configure
make clean && sudo make install

And finally, build and install the yubico PAM module:

cd yubico-pam
autoreconf --install
./configure
make clean && sudo make install

Do not forget to copy the PAM module into the right directory. For Ubuntu 12.04:

cp /usr/local/lib/security/pam_yubico.so /lib/security/

Use the new parameters!

In your /etc/pam.d/common-session you can now put:

auth    required       pam_yubico.so   id=1 debug  url=https://api.example.com/wsapi/2.0/verify?id=%d&otp=%s ldap_uri=ldap://ldap.example.com ldapdn=ou=group,dc=example,dc=com user_attr=uid yubi_attr=yubiKeyId binddn=uid=bind_user,ou=agroup,dc=example,dc=com bindpw=password123