Most of the code I see everywhere is meant for Linux, sometimes OpenBSD but I hardly ever see any FreeBSD examples.
Pretty much everything I see fails, for various reasons, not just autoconf 1.12, so I had to come up with an alternative.
I've seen a couple of ForceCommand variants, but they all depend on a working connection to a remote API and again software that doesn't compile.
Now thanks to Phil Massyn (out here on these fora) there is a useful Perl module: Auth::Yubikey_Decrypter
Then in the FreeBSD ports I found p5-Auth-YubikeyDecrypter, which I've used in a perl script, that also checks for replay attacks.
My solutions is relative simple/compact, and involves:
- /home/john/.ssh/yubikey
- /home/john/.ssh/yubikey_count
- /etc/ssh/sshd_config
- /etc/ssh/yubikey.sh
- /etc/ssh/yubikey.pl/home/john/.ssh/yubikey contains the private id, a delimiter, and the aes
Code:
1d1d1d1d1d1d:ae5ae5ae5ae5ae5ae5ae5ae5ae5aeetc
/home/john/.ssh/yubikey_count contains the counter against replay attacks
Code:
0
/etc/ssh/sshd_config with the ForceCommand
Code:
AllowUsers root@192.168.0.37 john
PermitRootLogin yes
MaxAuthTries 3
UseDNS no
Match User john # ...or Match Group ykusers
X11Forwarding no
AllowTcpForwarding no
ForceCommand /etc/ssh/yubikey.sh
the yubikey.sh script that above config refers to:
Code:
#!/bin/sh
# MIND: further on this script uses OTP's of 44 chars;
# but this could be different in your customized OTP's
# Some OS's `read` command have delimiter options - for example 44 chars :)
trap disconnect INT
disconnect() {
kill -9 $PPID
exit 1
}
# stty -echo # uncomment this (and below) if you prefer to hide the public ID
read -p "OTP: " -t 15 OTP_INPUT
# stty echo # sometimes `read` has -s (silent)
echo; echo # cosmetics
OTP=$(echo "$OTP_INPUT" | tr -c -d a-z)
if [ $? == 0 ] && [ ${#OTP} == 44 ]; then
CNT=`cat .ssh/yubikey_count`
NEW=`perl -T -- /etc/ssh/yubikey.pl $OTP $CNT`
if [ $? == 0 ]; then
echo $NEW > .ssh/yubikey_count
# clear
login -f $USER
disconnect
fi
fi
echo "invalid OTP" > /dev/stderr
disconnect
the yubikey.pl code that the perl command in above script executes
Code:
use strict;
use Auth::Yubikey_Decrypter;
# get values
open (FILE, "<", ".ssh/yubikey") or die "Could not open yubikey file.\n";
my @line = <FILE>;
chomp $line[0];
my @ykdata = split ":" , $line[0];
close FILE or die $!;
# decrypt:
my ($publicID,$secretid_hex,$counter_dec,$timestamp_dec,$session_use_dec,$random_dec,$crc_dec,$crc_ok) =
Auth::Yubikey_Decrypter::yubikey_decrypt($ARGV[0],$ykdata[1]);
# prepare to check replay attacks
my $ctr32 = (($counter_dec & 0x7fff) << 8) + $session_use_dec;
# validate:
if ( $ykdata[0] eq $secretid_hex && $crc_ok == 1 && $ctr32 > $ARGV[1] ) {
print $ctr32;
exit 0;
}
exit 1;
Now...
chmod 744 /etc/ssh/yubikey.*
/etc/rc.d/sshd reload
...and do NOT logout, but see if all of the above works well by initiating a new session as user "john".
This hopefully work for most *nix flavors.
Yes, it's also another of the same, still; let me know if you think it can be improved, or have a good argument you think this method is useless, or maybe is better than PAM.
One improvement would be having the yubikey data/count files not in the user directories.
Another to have it working without perl, but all in sh or csh or maybe even a C binary that does the same.