Bearing the above specification in mind, we could now write a Cosign ‘factor’/authenticator which targets Yubikeys: we’d just need to feed it with the username, as well as the one-time-password produced by the Yubikey; have it perform the authentication – by slaving that off to a back-end authentication server; and then have it produce outputs as specified by the protocol.
However: we already have a Yubico PAM module which handles the details of the back-end authentication of the Yubikey one-time-password, and it turns out that a ‘universal Cosign PAM factor adaptor’ is available – see the relevant postings on the cosign-discuss
mailing list. So we can use that instead.
The relevant C code file is now part of the Cosign download tarball, but it’s not autobuilt as part of the ./configure; make
process. However:
- find it (as file
pam_factor.c
in thecgi/factors
directory of the distribution); - build it as follows:
gcc -lpam -o pam_factor pam_factor.c
- copy the resulting binary factor to some suitable location, e.g.,
/usr/lib/cosign/factors
; and - edit
/etc/cosign.conf
to add the factor.
In the /etc/cosign.conf
configuration file, all this finally takes the form:
factor /usr/lib/cosign/factors/otp login passcode
where otp
is a symlink to the universal Cosign PAM factor adaptor pam_factor
, and a new ‘otp
‘ PAM service using the Yubico PAM module has also been declared.
Note: the name of the factor is significant, and is more than just, for example, self-documentation! Recall that the factor must, on successful authentication:
- write the factor name to stdout; and
- exit with 0.
The universal Cosign PAM ‘factor adaptor’ is coded to emit a factor name which corresponds to its basename – i.e. in our case, it will emit the factor name of ‘otp
‘ on successful authentication. So the literal factor name ‘otp
‘ is what the rest of the corresponding Cosign configuration client-side must be set up to expect.
In addition: the name of the corresponding local PAM service must match that of the factor: the factor is coded to call out to the local PAM service which corresponds to its basename.
Finally, however: why call this factor ‘otp
‘ rather than, say, the more mnemonic name ‘yubikey
‘? After all, both of the above-mentioned pieces of configuration could be fixed up accordingly. Well, as well as ‘leaking’ less internal configuration information, it turns out that the factor ‘otp
‘ is already explicitly supported by the supporting HTML and Javascript distributed in the Cosign tarball. So ‘otp
‘ is just better and easier all round.