Merge branch 'yubikey' of git://github.com/Calrama/nixpkgs

This commit is contained in:
Petr Rockai 2014-01-29 18:54:07 +01:00
commit 2062abfd4f
5 changed files with 346 additions and 9 deletions

View File

@ -18,6 +18,7 @@
bjornfor = "Bjørn Forsman <bjorn.forsman@gmail.com>";
bluescreen303 = "Mathijs Kwik <mathijs@bluescreen303.nl>";
bodil = "Bodil Stokke <nix@bodil.org>";
calrama = "Moritz Maxeiner <moritz@ucworks.org>";
chaoflow = "Florian Friesdorf <flo@chaoflow.net>";
coconnor = "Corey O'Connor <coreyoconnor@gmail.com>";
coroa = "Jonas Hörsch <jonas@chaoflow.net>";

View File

@ -5,7 +5,7 @@ with pkgs.lib;
let
luks = config.boot.initrd.luks;
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
# Wait for luksRoot to appear, e.g. if on a usb drive.
# XXX: copied and adapted from stage-1-init.sh - should be
# available as a function.
@ -31,9 +31,191 @@ let
fi
''}
open_normally() {
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
}
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
rbtohex() {
od -An -vtx1 | tr -d ' \n'
}
hextorb() {
tr '[:lower:]' '[:upper:]' | sed -e 's|\([0-9A-F]\{2\}\)|\\\\\\x\1|gI' | xargs printf
}
take() {
local c="$1"
shift
head -c $c "$@"
}
drop() {
local c="$1"
shift
if [ -e "$1" ]; then
cat "$1" | ( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
else
( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
fi
}
open_yubikey() {
mkdir -p ${yubikey.storage.mountPoint}
mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
local uuid_r
local k_user
local challenge
local k_blob
local aes_blob_decrypted
local checksum_correct
local checksum
local uuid_luks
local user_record
uuid_luks="$(cryptsetup luksUUID ${device} | take 36 | tr -d '-')"
${optionalString (!yubikey.multiUser) ''
user_record="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path})"
uuid_r="$(echo -n $user_record | take 32)"
''}
for try in $(seq 3); do
${optionalString yubikey.multiUser ''
local user_id
echo -n "Enter user id: "
read -s user_id
echo
''}
${optionalString yubikey.twoFactor ''
echo -n "Enter two-factor passphrase: "
read -s k_user
echo
''}
${optionalString yubikey.multiUser ''
local user_id_hash
user_id_hash="$(echo -n $user_id | openssl-wrap dgst -binary -sha512 | rbtohex)"
user_record="$(sed -n -e /^$user_id_hash[^$]*$/p ${yubikey.storage.mountPoint}${yubikey.storage.path} | tr -d '\n')"
if [ ! -z "$user_record" ]; then
user_record="$(echo -n $user_record | drop 128)"
uuid_r="$(echo -n $user_record | take 32)"
''}
challenge="$(echo -n $k_user$uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
k_blob="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
aes_blob_decrypted="$(echo -n $user_record | drop 32 | hextorb | openssl-wrap enc -d -aes-256-ctr -K $k_blob -iv $uuid_r | rbtohex)"
checksum="$(echo -n $aes_blob_decrypted | drop 168)"
if [ "$(echo -n $aes_blob_decrypted | hextorb | take 84 | openssl-wrap dgst -binary -sha512 | rbtohex)" == "$checksum" ]; then
checksum_correct=1
break
else
checksum_correct=0
echo "Authentication failed!"
fi
${optionalString yubikey.multiUser ''
else
checksum_correct=0
echo "Authentication failed!"
fi
''}
done
if [ "$checksum_correct" != "1" ]; then
umount ${yubikey.storage.mountPoint}
echo "Maximum authentication errors reached"
exit 1
fi
local k_yubi
k_yubi="$(echo -n $aes_blob_decrypted | take 40)"
local k_luks
k_luks="$(echo -n $aes_blob_decrypted | drop 40 | take 128)"
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
update_failed=false
local new_uuid_r
new_uuid_r="$(uuidgen)"
if [ $? != "0" ]; then
for try in $(seq 10); do
sleep 1
new_uuid_r="$(uuidgen)"
if [ $? == "0" ]; then break; fi
if [ $try -eq 10 ]; then update_failed=true; fi
done
fi
if [ "$update_failed" == false ]; then
new_uuid_r="$(echo -n $new_uuid_r | take 36 | tr -d '-')"
local new_challenge
new_challenge="$(echo -n $k_user$new_uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
local new_k_blob
new_k_blob="$(echo -n $new_challenge | hextorb | openssl-wrap dgst -binary -sha1 -mac HMAC -macopt hexkey:$k_yubi | rbtohex)"
local new_aes_blob
new_aes_blob=$(echo -n "$k_yubi$k_luks$checksum" | hextorb | openssl-wrap enc -e -aes-256-ctr -K "$new_k_blob" -iv "$new_uuid_r" | rbtohex)
${optionalString yubikey.multiUser ''
sed -i -e "s|^$user_id_hash$user_record|$user_id_hash$new_uuid_r$new_aes_blob|1"
''}
${optionalString (!yubikey.multiUser) ''
echo -n "$new_uuid_r$new_aes_blob" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
''}
else
echo "Warning: Could not obtain new UUID, current challenge persists!"
fi
umount ${yubikey.storage.mountPoint}
}
yubikey_missing=true
ykinfo -v 1>/dev/null 2>&1
if [ $? != "0" ]; then
echo -n "waiting 10 seconds for yubikey to appear..."
for try in $(seq 10); do
sleep 1
ykinfo -v 1>/dev/null 2>&1
if [ $? == "0" ]; then
yubikey_missing=false
break
fi
echo -n .
done
echo "ok"
else
yubikey_missing=false
fi
if [ "$yubikey_missing" == true ]; then
echo "no yubikey found, falling back to non-yubikey open procedure"
open_normally
else
open_yubikey
fi
''}
# open luksRoot and scan for logical volumes
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
open_normally
''}
'';
isPreLVM = f: f.preLVM;
@ -139,10 +321,84 @@ in
'';
};
};
yubikey = mkOption {
default = null;
type = types.nullOr types.optionSet;
description = ''
The options to use for this LUKS device in Yubikey-PBA.
If null (the default), Yubikey-PBA will be disabled for this device.
'';
options = {
twoFactor = mkOption {
default = true;
type = types.bool;
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
};
multiUser = mkOption {
default = false;
type = types.bool;
description = "Whether to allow multiple users to authenticate with a Yubikey";
};
slot = mkOption {
default = 2;
type = types.int;
description = "Which slot on the Yubikey to challenge";
};
storage = mkOption {
type = types.optionSet;
description = "Options related to the authentication record";
options = {
device = mkOption {
default = /dev/sda1;
type = types.path;
description = ''
An unencrypted device that will temporarily be mounted in stage-1.
Must contain the authentication record for this LUKS device.
'';
};
fsType = mkOption {
default = "vfat";
type = types.string;
description = "The filesystem of the unencrypted device";
};
mountPoint = mkOption {
default = "/crypt-storage";
type = types.string;
description = "Path where the unencrypted device will be mounted in stage-1";
};
path = mkOption {
default = "/crypt-storage/default";
type = types.string;
description = ''
Absolute path of the authentication record on the unencrypted device with
that device's root directory as "/".
'';
};
};
};
};
};
};
};
boot.initrd.luks.yubikeySupport = mkOption {
default = false;
type = types.bool;
description = ''
Enables support for authenticating with a Yubikey on LUKS devices.
See the NixOS wiki for information on how to properly setup a LUKS device
and a Yubikey to work with this feature.
'';
};
};
config = mkIf (luks.devices != []) {
@ -157,15 +413,47 @@ in
# copy the cryptsetup binary and it's dependencies
boot.initrd.extraUtilsCommands = ''
cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
# XXX: do we have a function that does this?
for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
cp -pdvn $lib $out/lib
cp -pvn $(readlink -f $lib) $out/lib
done
cp -pdv ${pkgs.libgcrypt}/lib/libgcrypt*.so.* $out/lib
cp -pdv ${pkgs.libgpgerror}/lib/libgpg-error*.so.* $out/lib
cp -pdv ${pkgs.cryptsetup}/lib/libcryptsetup*.so.* $out/lib
cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
${optionalString luks.yubikeySupport ''
cp -pdv ${pkgs.utillinux}/bin/uuidgen $out/bin
cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
cp -pdv ${pkgs.openssl}/lib/libssl*.so.* $out/lib
cp -pdv ${pkgs.openssl}/lib/libcrypto*.so.* $out/lib
mkdir -p $out/etc/ssl
cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
cat > $out/bin/openssl-wrap <<EOF
#!$out/bin/sh
EOF
chmod +x $out/bin/openssl-wrap
''}
'';
boot.initrd.extraUtilsCommandsTest = ''
$out/bin/cryptsetup --version
${optionalString luks.yubikeySupport ''
$out/bin/uuidgen --version
$out/bin/ykchalresp -V
$out/bin/ykinfo -V
cat > $out/bin/openssl-wrap <<EOF
#!$out/bin/sh
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
$out/bin/openssl "\$@"
EOF
$out/bin/openssl-wrap version
''}
'';
boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;

View File

@ -0,0 +1,23 @@
{stdenv, fetchurl, pkgconfig, libusb1, libyubikey}:
stdenv.mkDerivation rec
{
version = "1.15.0";
name = "ykpers-${version}";
src = fetchurl
{
url = "http://opensource.yubico.com/yubikey-personalization/releases/${name}.tar.gz";
sha256 = "1n4s8kk31q5zh2rm7sj9qmv86yl8ibimdnpvk9ny391a88qlypyd";
};
buildInputs = [pkgconfig libusb1 libyubikey];
meta =
{
homepage = "http://opensource.yubico.com/yubikey-personalization/";
description = "YubiKey Personalization cross-platform library and tool";
license = "bsd";
maintainers = [ stdenv.lib.maintainers.calrama ];
};
}

View File

@ -0,0 +1,21 @@
{stdenv, fetchurl}:
stdenv.mkDerivation rec
{
version = "1.11";
name = "libyubikey-${version}";
src = fetchurl
{
url = "http://opensource.yubico.com/yubico-c/releases/${name}.tar.gz";
sha256 = "19pm4rqsnm9r0n5j26bqkxa1jpimdavzcvg5g7p416vkjhxc6lw9";
};
meta =
{
homepage = "http://opensource.yubico.com/yubico-c/";
description = "C library for manipulating Yubico YubiKey One-Time Passwords (OTPs)";
license = "bsd";
maintainers = [ stdenv.lib.maintainers.calrama ];
};
}

View File

@ -5063,6 +5063,8 @@ let
libyamlcpp = callPackage ../development/libraries/libyaml-cpp { };
libyamlcpp03 = callPackage ../development/libraries/libyaml-cpp/0.3.x.nix { };
libyubikey = callPackage ../development/libraries/libyubikey {};
libzip = callPackage ../development/libraries/libzip { };
libzrtpcpp = callPackage ../development/libraries/libzrtpcpp { };
@ -9153,6 +9155,8 @@ let
qgis = callPackage ../applications/misc/qgis {};
ykpers = callPackage ../applications/misc/ykpers {};
yoshimi = callPackage ../applications/audio/yoshimi {
fltk = fltk13;
};