Package: yaird Version: 0.0.12-18 Severity: wishlist Tags: patch Hello,
although I fear that the number of people on this planet who still strongly prefer loop-AES over dm-crypt, and would wish for full loop-AES integration into yaird, might effectively be reduced to one, I decided to finally publish this patch... Loopback and loop-AES device support for yaird in every possible way. Some highlights: - Both native and loop-AES patched losetup/loop module support. - As there is no need for a custom initrd script any longer, it is possible to boot off a loop-AES encrypted root filesystem with a standard distribution kernel, supplemented only with a loop-aes-modules-* package (and loop-aes-utils). - Loop-AES devices need not necessarily be mounted directly. Using a single loop-AES encrypted device as an LVM physical volume allows easy use of multiple filesystems (i.e. LVM logical volumes) with a single GPG key loaded at boot time. - GPG encryption keys are either copied directly onto the initramfs image, or may be loaded off an external (unencrypted) device which is mounted automatically upon boot time, for example an USB stick. Public-key encrypted GPG keys (with a GPG home directory on the image or on the external key device) are also supported. - Suspend-to-disk with (non-random-key) loop-AES encrypted swap. Instead of enumerating all supported options, here's a list of things not supported with this implementation: - File-backed loopback and loop-AES devices, - Random-key encrypted loop-AES devices (either via losetup option phash=random/* or a swap fstab entry with loop= and encryption= attributes). - (Not tested on other distributions besides Debian, yet.) I would like to shortly mention the only big concern I had while implementing this, and my proposed solution. Deriving a complete configuration from losetup output only is not possible for a loop-AES encrypted device, as losetup only shows the attributes offset=, sizelimit= encryption= and loinit=. For GPG key encryption (with options gpgkey= and gpghome=), it is necessary to have an additional configuration, e.g. an fstab entry. However, such an entry is not feasible for a loop device which is not mounted directly, such as a loop-AES physical LVM volume. Therefore, I introduced a new configuration file ('/etc/loopaestab') which specifically contains all options necessary for setting up loop-AES devices. Its use is only mandatory in case a loop-AES device does not appear within a loop= attribute in an fstab entry. My proposal and a possible config format have already been described in wishlist bug #393363 filed against the loop-aes-utils package. The supported options would include all losetup options (see man page description for option -F), and additionally the keyword option 'gpgmount', which instructs an initramfs creator to mount the device containing the GPG key (and optionally GPG home directory) upon boot. An example entry might look like this: # <target name> <source device> <options> /dev/loop0 /dev/md0 encryption=AES256,gpgkey=/boot/gnupg/maia.gpg,gpgmount Concerning the patch itself, I split it into multiple parts as included below, applied in ascending order to the patched yaird 0.0.12-18 package source. The first five patches fix minor bugs I stumbled upon in code related to loop-AES support, or add some small subroutines for general purpose use. => 1101_base_canon_root_path_fix.patch The Base::canon subroutine does not work when given the root path '/' as an argument. This is a result of perl's split function: split (/\/+/, '/') returns an empty array. => 1102_base_filedev_stat_device_of_file.patch New Base::filedev subroutine to determine the device (and subsequently the filesystem via an fstab entry) a file or directory lies on. This is used with the gpgmount support. => 1103_fstab_findbydevno.patch - Break FsTab::findByDevName into two separate subroutines to allow directly looking for an entry by a device number. - Do not issue fatal errors within findByMountPoint or findByDevName subroutines! This should be handled on a higher level, as in Plan::addBlockDevMount or Plan::addFsTabMount. As a matter of fact, it *is* already handled there, but ironically with erroneous calls to a non-existent subroutine 'fatal' (should be 'Base::fatal'). One may now take an educated guess as to why these bugs were never discovered... => 1104_templates_quiet_mksymdev_delay.patch When waiting for new /sysfs entries to appear (like with an USB stick used as a GPG key device upon booting), do not output error messages. => 1105_mount_readonly_and_unmount.patch This adds an additional argument $isRoot to the Plan::addBlockDevMount subroutine to explicitly specify whether a device to be mounted is a "root" or a "non-root" device. "root" devices are given the $ro /proc/cmdline option, while "non-root" devices (e.g. GPG key devices) are always mounted read-only. Finally, an unmount subroutine and template is added. => 1106_add_loopback_and_loopaes_with_loopaestab_support.patch This is it. In conclusion, I would be glad hearing of real-world tests with yaird and loop-AES support, or any suggestions for improvement. With the hope of possible discussion and future inclusion in Debian, and maybe later upstream... Regards, Peter -- System Information: Debian Release: testing/unstable APT prefers unstable APT policy: (500, 'unstable'), (400, 'testing'), (1, 'experimental') Architecture: amd64 (x86_64) Shell: /bin/sh linked to /bin/bash Kernel: Linux 2.6.18-maia Locale: LANG=C, [EMAIL PROTECTED] (charmap=ISO-8859-15) Versions of packages yaird depends on: ii cpio 2.6-17 GNU cpio -- a program to manage ar ii dash 0.5.3-3 The Debian Almquist Shell ii libc6 2.3.6.ds1-6 GNU C Library: Shared libraries ii libhtml-template-perl 2.8-1 HTML::Template : A module for usin ii libparse-recdescent-perl 1.94.free-3 Generates recursive-descent parser ii perl 5.8.8-6.1 Larry Wall's Practical Extraction yaird recommends no packages. -- no debconf information
diff -urN yaird-0.0.12.orig/perl/Base.pm yaird-0.0.12/perl/Base.pm --- yaird-0.0.12.orig/perl/Base.pm 2006-10-15 19:42:31.000000000 +0200 +++ yaird-0.0.12/perl/Base.pm 2006-10-15 19:43:03.000000000 +0200 @@ -339,6 +339,9 @@ # (2) ../.. is not dropped. # (3) ./.. => .. my @result = (); + if ($path eq '/') { + push @result, ''; + } for my $component (split (/\/+/, $path)) { if ($component eq '.') { next;
diff -urN yaird-0.0.12.orig/perl/Base.pm yaird-0.0.12/perl/Base.pm --- yaird-0.0.12.orig/perl/Base.pm 2006-10-15 19:43:03.000000000 +0200 +++ yaird-0.0.12/perl/Base.pm 2006-10-15 19:44:17.000000000 +0200 @@ -289,6 +289,26 @@ return "$major:$minor"; } +# +# filedev -- given pathname to a file or directory, return "maj:min" +# of the underlying (not necessarily block) device or undef. +# +sub filedev ($) { + my ($path) = @_; + if ( ! (-e $path)) { + return undef; + } + + my @fields = stat _; + if ($#fields != 12) { + Base::fatal ("stat failed on file $path"); + } + # from 2.6.10-rc2, kdev.h, backward compatible. + my $devno = $fields[0]; + my $major = ($devno & 0xfff00) >> 8; + my $minor = ($devno & 0xff) | (($devno >> 12) & 0xfff00); + return "$major:$minor"; +} # # expandLink -- given a path to a symlink file,
diff -urN yaird-0.0.12.orig/perl/FsTab.pm yaird-0.0.12/perl/FsTab.pm --- yaird-0.0.12.orig/perl/FsTab.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/FsTab.pm 2006-10-15 21:21:54.000000000 +0200 @@ -94,7 +94,7 @@ sub findByMountPoint ($) { my ($mnt) = @_; - my $result; + my $result = undef; $mnt = Base::canon ($mnt); for my $entry (@{FsTab::all()}) { @@ -105,9 +105,6 @@ $result = $entry; } } - if (! defined ($result)) { - Base::fatal ("mount point not in fstab: $mnt"); - } return $result; } @@ -120,11 +117,16 @@ # sub findByDevName ($) { my ($dev) = @_; - my $result; my $devno = Base::devno ($dev); if (! defined ($devno)) { Base::fatal ("cannot find device number for: $dev"); } + return findByDevno ($devno); +} + +sub findByDevno ($) { + my ($devno) = @_; + my $result = undef; for my $entry (@{FsTab::all()}) { my ($b2, $msg) = $entry->blockDevPath(); @@ -138,14 +140,11 @@ if ($n2 eq $devno) { if (defined ($result)) { - Base::fatal ("duplicate device name in fstab: $dev"); + Base::fatal ("duplicate device name in fstab: $b2"); } $result = $entry; } } - if (! defined ($result)) { - Base::fatal ("device name not in fstab: $dev"); - } return $result; } diff -urN yaird-0.0.12.orig/perl/Plan.pm yaird-0.0.12/perl/Plan.pm --- yaird-0.0.12.orig/perl/Plan.pm 2006-10-15 21:21:19.000000000 +0200 +++ yaird-0.0.12/perl/Plan.pm 2006-10-15 21:22:14.000000000 +0200 @@ -631,7 +631,7 @@ # my $root = FsTab::findByDevName($rootDevName); if (! defined ($root)) { - fatal ("requested root device ($rootDevName) not in fstab"); + Base::fatal ("requested root device ($rootDevName) not in fstab"); } # @@ -706,7 +706,7 @@ my ($actions, $fsTabEntry, $mountPoint) = @_; my $root = FsTab::findByMountPoint($fsTabEntry); if (! defined ($root)) { - fatal ("can't find $fsTabEntry in fstab"); + Base::fatal ("can't find $fsTabEntry in fstab"); } my ($blockDevName, $msg) = $root->blockDevPath();
diff -urN yaird-0.0.12.orig/templates/Debian-initrd.cfg yaird-0.0.12/templates/Debian-initrd.cfg --- yaird-0.0.12.orig/templates/Debian-initrd.cfg 2006-10-15 19:42:31.000000000 +0200 +++ yaird-0.0.12/templates/Debian-initrd.cfg 2006-10-15 19:46:03.000000000 +0200 @@ -120,7 +120,7 @@ ! devfile="$1" ! sysfile="$2" ! cb="$3" - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! for delay in 1 2 4 8 16 ! do ! if [ "$devpair" = "" ] @@ -128,7 +128,7 @@ ! echo "Waiting $delay seconds for $sysfile to show up" ! sleep $delay ! fi - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! done ! ! if [ "$devpair" = "" ] diff -urN yaird-0.0.12.orig/templates/Debian.cfg yaird-0.0.12/templates/Debian.cfg --- yaird-0.0.12.orig/templates/Debian.cfg 2006-10-15 19:42:32.000000000 +0200 +++ yaird-0.0.12/templates/Debian.cfg 2006-10-15 19:46:03.000000000 +0200 @@ -103,7 +103,7 @@ ! devfile="$1" ! sysfile="$2" ! cb="$3" - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! for delay in 1 2 4 8 16 ! do ! if [ "$devpair" = "" ] @@ -111,7 +111,7 @@ ! echo "Waiting $delay seconds for $sysfile to show up" ! sleep $delay ! fi - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! done ! ! if [ "$devpair" = "" ] diff -urN yaird-0.0.12.orig/templates/Fedora.cfg yaird-0.0.12/templates/Fedora.cfg --- yaird-0.0.12.orig/templates/Fedora.cfg 2006-10-15 19:42:32.000000000 +0200 +++ yaird-0.0.12/templates/Fedora.cfg 2006-10-15 19:46:03.000000000 +0200 @@ -103,7 +103,7 @@ ! devfile="$1" ! sysfile="$2" ! cb="$3" - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! for delay in 1 2 4 8 16 ! do ! if [ "$devpair" = "" ] @@ -111,7 +111,7 @@ ! echo "Waiting $delay seconds for $sysfile to show up" ! sleep $delay ! fi - ! devpair=$(/bin/cat "$sysfile") + ! devpair=$(/bin/cat "$sysfile" 2>/dev/null) ! done ! ! if [ "$devpair" = "" ]
diff -urN yaird-0.0.12.orig/perl/Plan.pm yaird-0.0.12/perl/Plan.pm --- yaird-0.0.12.orig/perl/Plan.pm 2006-10-15 21:22:14.000000000 +0200 +++ yaird-0.0.12/perl/Plan.pm 2006-10-15 21:25:09.000000000 +0200 @@ -623,8 +623,8 @@ # addBlockDevMount -- add list of actions to mount named device # at mountPoint: activate device, activate fstype, do mount. # -sub addBlockDevMount ($$$) { - my ($actions, $rootDevName, $mountPoint) = @_; +sub addBlockDevMount ($$$$) { + my ($actions, $rootDevName, $mountPoint, $isRoot) = @_; # # Device must be in fstab, to determine options @@ -687,11 +687,12 @@ my $yspecial = $abd->yspecial(); my $opts = $root->opts->cmdLineVersion(); - # XXX - isRoot should be readOnly, and configurable. + # mount devices explicitly specified as non-root read-only $actions->add ("mount", $mountPoint, options => $opts, fsType => $fsType, - isRoot => 1, + isRoot => $isRoot, + readOnly => (! $isRoot), device => $yspecial); } @@ -713,7 +714,15 @@ if (! defined ($blockDevName)) { Base::fatal ($msg); } - addBlockDevMount ($actions, $blockDevName, $mountPoint); + addBlockDevMount ($actions, $blockDevName, $mountPoint, 1); +} + +# +# addUnmount -- detach device mounted at mountPoint +# +sub addUnmount ($$) { + my ($actions, $mountPoint) = @_; + $actions->add ("umount", $mountPoint); } # @@ -792,7 +801,7 @@ elsif ($type eq 'mountdev') { my $mountPoint = $goal->{mountPoint}; Base::assert (defined ($mountPoint)); - addBlockDevMount ($actions, $value, $mountPoint); + addBlockDevMount ($actions, $value, $mountPoint, 1); } else { Base::fatal ("Unknown goal"); diff -urN yaird-0.0.12.orig/templates/Debian-initrd.cfg yaird-0.0.12/templates/Debian-initrd.cfg --- yaird-0.0.12.orig/templates/Debian-initrd.cfg 2006-10-15 21:21:19.000000000 +0200 +++ yaird-0.0.12/templates/Debian-initrd.cfg 2006-10-15 21:25:09.000000000 +0200 @@ -392,6 +392,7 @@ BEGIN !/bin/mount -n \ ! <TMPL_IF NAME=isRoot>$ro</TMPL_IF> \ + ! <TMPL_IF NAME=readOnly>-r</TMPL_IF> \ ! -t <TMPL_VAR NAME=fsType> \ ! <TMPL_VAR NAME=options> \ ! '<TMPL_VAR NAME=device>' \ @@ -399,6 +400,14 @@ END SCRIPT END TEMPLATE + TEMPLATE umount + BEGIN + SCRIPT "/sbin/init" + BEGIN + !/bin/umount -n <TMPL_VAR NAME=target> + END SCRIPT + END TEMPLATE + TEMPLATE nfsstart BEGIN FILE "<TMPL_VAR NAME=auxDir>/trynfs" diff -urN yaird-0.0.12.orig/templates/Debian.cfg yaird-0.0.12/templates/Debian.cfg --- yaird-0.0.12.orig/templates/Debian.cfg 2006-10-15 21:21:19.000000000 +0200 +++ yaird-0.0.12/templates/Debian.cfg 2006-10-15 21:25:09.000000000 +0200 @@ -457,6 +457,7 @@ BEGIN !/bin/mount -n \ ! <TMPL_IF NAME=isRoot>$ro</TMPL_IF> \ + ! <TMPL_IF NAME=readOnly>-r</TMPL_IF> \ ! -t <TMPL_VAR NAME=fsType> \ ! <TMPL_VAR NAME=options> \ ! '<TMPL_VAR NAME=device>' \ @@ -464,6 +465,14 @@ END SCRIPT END TEMPLATE + TEMPLATE umount + BEGIN + SCRIPT "/init" + BEGIN + !/bin/umount -n <TMPL_VAR NAME=target> + END SCRIPT + END TEMPLATE + TEMPLATE nfsstart BEGIN FILE "<TMPL_VAR NAME=auxDir>/trynfs" diff -urN yaird-0.0.12.orig/templates/Fedora.cfg yaird-0.0.12/templates/Fedora.cfg --- yaird-0.0.12.orig/templates/Fedora.cfg 2006-10-15 21:21:19.000000000 +0200 +++ yaird-0.0.12/templates/Fedora.cfg 2006-10-15 21:25:09.000000000 +0200 @@ -468,6 +468,7 @@ BEGIN !/bin/mount -n \ ! <TMPL_IF NAME=isRoot>$ro</TMPL_IF> \ + ! <TMPL_IF NAME=readOnly>-r</TMPL_IF> \ ! -t <TMPL_VAR NAME=fsType> \ ! <TMPL_VAR NAME=options> \ ! '<TMPL_VAR NAME=device>' \ @@ -476,6 +477,15 @@ END TEMPLATE + TEMPLATE umount + BEGIN + SCRIPT "/init" + BEGIN + !/bin/umount -n <TMPL_VAR NAME=target> + END SCRIPT + END TEMPLATE + + TEMPLATE nfsstart BEGIN FILE "<TMPL_VAR NAME=auxDir>/trynfs"
diff -x .pc -x patches -x debian -urN yaird-0.0.12.orig/perl/ActiveBlockDev.pm yaird-0.0.12/perl/ActiveBlockDev.pm --- yaird-0.0.12.orig/perl/ActiveBlockDev.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/ActiveBlockDev.pm 2006-10-20 16:53:15.000000000 +0200 @@ -173,6 +173,10 @@ Base::assert ($name =~ /^md\d+$/); $self->{yspecial} = "/dev/$name"; } + elsif ($creator eq "losetup") { + Base::assert ($name =~ /^loop\d+$/); + $self->{yspecial} = "/dev/$name"; + } elsif ($creator eq "devmapper") { Base::assert ($name =~ /^dm-\d+$/); my $paths = BlockSpecialFileTab::pathsByDevno ($devno); --- yaird-0.0.12.orig/perl/Conf.pm.in 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/Conf.pm.in 2006-10-20 16:53:15.000000000 +0200 @@ -61,6 +61,7 @@ procFs => sub { "/proc"; }, dev => sub { "/dev"; }, fstab => sub { "/etc/fstab"; }, + loopaestab => sub { "/etc/loopaestab"; }, crypttab => sub { "/etc/crypttab"; }, hotplug => sub { "/etc/hotplug"; }, appVersion => sub { "@VERSION@"; }, --- yaird-0.0.12.orig/perl/FsEntry.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/FsEntry.pm 2006-10-20 16:53:15.000000000 +0200 @@ -83,8 +83,8 @@ my $msg = undef; if ($dev =~ /^\//) { - if ($self->opts->exists('loop')) { - $msg = "loopback mount for '$dev' not supported ($origin)"; + if ($self->opts->exists('loop') && $self->type eq 'swap') { + $msg = "loop option for swap device '$dev' not supported ($origin)"; return (undef, $msg); } if (-f $dev && $self->type eq "swap") { --- yaird-0.0.12.orig/perl/FsOpts.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/FsOpts.pm 2006-10-20 16:53:15.000000000 +0200 @@ -43,6 +43,19 @@ next if $key eq 'user'; next if $key eq 'users'; next if $key eq 'defaults'; + + # loop and loop-AES attributes + next if $key eq 'loop'; + next if $key eq 'offset'; + next if $key eq 'sizelimit'; + next if $key eq 'encryption'; + next if $key eq 'pseed'; + next if $key eq 'phash'; + next if $key eq 'loinit'; + next if $key eq 'gpgkey'; + next if $key eq 'gpghome'; + next if $key eq 'itercountk'; + my $val = $opts->{$key}; if (defined ($val)) { push @cmdLine, "$key=$val"; --- yaird-0.0.12.orig/perl/KConfig.pm 2006-10-20 16:54:51.000000000 +0200 +++ yaird-0.0.12/perl/KConfig.pm 2006-10-20 16:53:15.000000000 +0200 @@ -208,6 +208,9 @@ # Compaq Smart Array controllers 'cpqarray' => [ 'BLK_CPQ_DA' ], 'cciss' => [ 'BLK_CPQ_CISS_DA' ], + + # loopback + 'loop' => ['BLK_DEV_LOOP' ], }; --- yaird-0.0.12.orig/perl/LoopAesEntry.pm 1970-01-01 01:00:00.000000000 +0100 +++ yaird-0.0.12/perl/LoopAesEntry.pm 2006-10-20 16:53:15.000000000 +0200 @@ -0,0 +1,47 @@ +#!perl -w +# +# LoopAesEntry -- encapsulate a single entry in /etc/loopaestab +# Copyright (C) 2005 Erik van Konijnenburg +# Copyright (C) 2006 Peter Colberg +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +use strict; +use warnings; +use LabeledPartitionTab; +package LoopAesEntry; +use base 'Obj'; + +sub fill { + my $self = shift; + $self->SUPER::fill(); + $self->takeArgs ('target', 'source', 'opts', 'origin'); +} + +sub target { return $_[0]->{target}; } +sub source { return $_[0]->{source}; } +sub opts { return $_[0]->{opts}; } +sub origin { return $_[0]->{origin}; } + +sub string { + my $self = shift; + my $target = $self->target(); + my $source = $self->source(); + my $opts = $self->opts()->string(); + return "$target in $source with $opts"; +} + + +1; --- yaird-0.0.12.orig/perl/LoopAesTab.pm 1970-01-01 01:00:00.000000000 +0100 +++ yaird-0.0.12/perl/LoopAesTab.pm 2006-10-20 16:53:15.000000000 +0200 @@ -0,0 +1,117 @@ +#!perl -w +# +# LoopAesTab -- encapsulate /etc/loopaestab. +# Copyright (C) 2005 Erik van Konijnenburg +# Copyright (C) 2006 Peter Colberg +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +use strict; +use warnings; +use Base; +use Conf; +use LoopAesEntry; +package LoopAesTab; + + +my $loopAesTab = undef; + + +sub init () { + if (defined ($loopAesTab)) { + return; + } + $loopAesTab = []; + my $name = Conf::get('loopaestab'); + if (! -e $name) { + # + # It's OK if there's no /etc/loopaestab, but if it + # exists, it had better be readable. + # + return; + } + if (! open (IN, "<", "$name")) { + Base::fatal ("can't read $name"); + } + my $lineNo = 0; + while (defined (my $line = <IN>)) { + $lineNo++; + chomp $line; + + $line =~ s/^\s*//; + next if $line =~ /^#/; # comment line + next if $line eq ""; + + my @fields = split (/\s+/, $line); + if (@fields < 2) { + Base::fatal ("no source device in $name:$lineNo"); + } + my $target = shift @fields; + my $source = shift @fields; + my $optString = (shift @fields or ''); + my $opts = Opts->new (string => $optString); + + my $descr = LoopAesEntry->new( + target => $target, + source => $source, + opts => $opts, + origin => "$name:$lineNo", + ); + push @{$loopAesTab}, $descr; + } + if (! close (IN)) { + Base::fatal ("could not read $name"); + } +} + +sub all () { + init; + return $loopAesTab; +} + +sub findByTarget ($) { + my ($target) = @_; + my $result; + my $devno = Base::devno ($target); + if (! defined ($devno)) { + Base::fatal ("cannot find device number for: $target"); + } + return findByDevno ($devno); +} + +sub findByDevno ($) { + my ($devno) = @_; + my $result = undef; + + for my $entry (@{LoopAesTab::all()}) { + my $b2 = $entry->target; + my $n2 = Base::devno ($b2); + if (! defined ($n2)) { + next; + } + + if ($n2 eq $devno) { + if (defined ($result)) { + my $o1 = $entry->origin; + my $o2 = $result->origin; + Base::fatal ("duplicate device '$b2' in $o1, $o2"); + } + $result = $entry; + } + } + return $result; +} + +1; --- yaird-0.0.12.orig/perl/LoopAesVersion.pm 1970-01-01 01:00:00.000000000 +0100 +++ yaird-0.0.12/perl/LoopAesVersion.pm 2006-10-20 16:53:15.000000000 +0200 @@ -0,0 +1,78 @@ +#!perl -w +# +# LoopAesVersion -- utility functions to probe for loop-AES support +# Copyright (C) 2006 Peter Colberg +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +use strict; +use warnings; +use Base; +package LoopAesVersion; + +my $hasLoopAesModule = undef; +my $hasLoopAesSetup = undef; + +# +# Probe loop-AES patched loop module +# +sub probeLoopAesModule ($) { + my ($path) = @_; + + for my $s ('loop_compute_sector_iv', 'loop_compute_md5_iv', 'loop_compute_md5_iv_v3') { + my ($rc, $lines) = Base::runCmd (failOk => 1, cmd => ['/bin/grep', $s, $path]); + if (! $rc) { + $hasLoopAesModule = 0; + return; + } + } + $hasLoopAesModule = 1; +} + +# +# Probe loop-AES patched losetup program +# +sub probeLoopAesSetup ($) { + my ($path) = @_; + + for my $s ('multi-key-v2', 'multi-key-v3') { + my ($rc, $lines) = Base::runCmd (failOk => 1, cmd => ['/bin/grep', $s, $path]); + if (! $rc) { + $hasLoopAesSetup = 0; + return; + } + } + $hasLoopAesSetup = 1; +} + +# +# Check if userspace and kernel loopback versions match +# +sub matchingVersions () { + if (KConfig::isBuiltIn ('loop')) { + # You are on your own here... + return 1; + } + if (! defined ($hasLoopAesModule)) { + Base::fatal ("unknown loop module version"); + } + if (! defined ($hasLoopAesSetup)) { + Base::fatal ("unknown losetup version"); + } + return ($hasLoopAesModule == $hasLoopAesSetup); +} + + +1; --- yaird-0.0.12.orig/perl/LoopDev.pm 1970-01-01 01:00:00.000000000 +0100 +++ yaird-0.0.12/perl/LoopDev.pm 2006-10-20 16:53:15.000000000 +0200 @@ -0,0 +1,47 @@ +#!perl -w +# +# LoopDev -- the probed values for a loopback device, as found by losetup. +# Copyright (C) 2005 Erik van Konijnenburg +# Copyright (C) 2006 Peter Colberg +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +use strict; +use warnings; +package LoopDev; +use base 'Obj'; + +sub fill { + my $self = shift; + $self->SUPER::fill(); + $self->takeArgs ('path', 'devno', 'opts', 'device'); +} + +sub path { return $_[0]->{path}; } +sub devno { return $_[0]->{devno}; } +sub opts { return $_[0]->{opts}; } +sub device { return $_[0]->{device}; } + +sub string { + my $self = shift; + my $path = $self->path; + my $devno = $self->devno; + my $opts = $self->opts->string; + my $device = $self->device; + return "$path($devno) on $device" . ($opts ? " with $opts" : ""); +} + + +1; --- yaird-0.0.12.orig/perl/LoopTab.pm 1970-01-01 01:00:00.000000000 +0100 +++ yaird-0.0.12/perl/LoopTab.pm 2006-10-20 16:53:15.000000000 +0200 @@ -0,0 +1,178 @@ +#!perl -w +# +# LoopTab -- encapsulate losetup output +# Copyright (C) 2005 Erik van Konijnenburg +# Copyright (C) 2006 Peter Colberg +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +# +# +use strict; +use warnings; +use ActiveBlockDevTab; +use Base; +use BlockSpecialFileTab; +use LoopAesVersion; +use LoopDev; +package LoopTab; + + +my $loopTab = undef; + + +# +# init -- initialise table of all known loopback devices. +# +sub init () { + if (defined ($loopTab)) { + return; + } + + $loopTab = []; + + for my $abd (@{ActiveBlockDevTab::all ()}) { + next if (! ($abd->name =~ /^loop\d+$/)); + my $paths = BlockSpecialFileTab::pathsByDevno ($abd->devno); + next if (! defined ($paths)); + my ($rc, $lines) = Base::runCmd (missingOk => 1, failOk => 1, cmd => ['/sbin/losetup', ${$paths}[0]]); + if ($rc && defined ($lines) && @{$lines} == 1) { + processLine (${$lines}[0]); + } + } + LoopAesVersion::probeLoopAesSetup ('/sbin/losetup'); +} + +# +# processLine -- parse losetup output for a single loopback device +# +# Both native and loop-AES patched losetup syntax is supported. +# +# Example native syntax: +# /dev/loop0: [0900]:57004 (/dev/md0), offset 1000000 +# +# Example loop-AES syntax: +# /dev/loop1: [000e]:3762 (/dev/vg/swap) offset=1024 sizelimit=20971520 encryption=AES256 multi-key-v3 loinit=123 +# +# Parsing expressions were derived from the function 'show_loop' +# in file 'mount/lomount.c' of the unpatched and loop-AES (v3.1d) +# patched util-linux package (v2.12r). +# +sub processLine ($) { + my ($line) = @_; + + my ($path, $device, @attributes, @opts); + + # common device description + if ($line =~ /^(\S+): \[[[:xdigit:]]+\]:\d+ \((\S+)\)(?:(,? )(.*))?$/) { + $path = $1; + $device = $2; + if (defined ($4)) { + @attributes = split (/$3/, $4); + } + } + else { + Base::fatal ("could not parse losetup output: '$line'"); + } + + for my $attr (@attributes) { + # common attribute + if ($attr =~ /^offset[ =](@?\d+)$/) { + push @opts, "offset=$1"; + } + # loop-AES attribute + elsif ($attr =~ /^sizelimit=(\d+)$/) { + push @opts, "sizelimit=$1"; + } + # non-loop-AES attribute + elsif ($attr =~ /^sizelimit (\d+)$/) { + # info-only attribute + } + # loop-AES attribute + elsif ($attr =~ /^encryption=(\S+)$/) { + push @opts, "encryption=$1"; + } + # non-loop-AES attribute + elsif ($attr =~ /^encryption /) { + Base::fatal ("cryptoloop ('$path') not supported"); + } + # loop-AES attribute + elsif ($attr =~ /^loinit=(\d+)$/) { + push @opts, "loinit=$1"; + } + # loop-AES attribute + elsif ($attr =~ /^read-only$/) { + Base::fatal ("read-only loopback device ('$path') not supported"); + } + # loop-AES attribute + elsif ($attr =~ /^multi-key-(v[23])$/) { + # + # Note: If a loopback device is in multi-key mode, + # a GPG encryption key is definitely required. + # + push @opts, "multikey=$1"; + } + else { + Base::fatal ("unknown attribute '$attr' in losetup output"); + } + } + my $optString = join (",", @opts); + my $opts = Opts->new (string => $optString); + + my $devno = Base::devno ($path); + if (! defined($devno)) { + Base::fatal ("Device '$path' in losetup output: can't find device major/minor number"); + } + + my $n = Base::devno ($device); + if (! defined ($n)) { + Base::fatal ("Device '$device' in losetup output: can't find device major/minor number"); + } + + my $descr = LoopDev->new ( + path => $path, + devno => $devno, + opts => $opts, + device => $device, + ); + push @{$loopTab}, $descr; +} + +sub all () { + init; + return $loopTab; +} + +sub findByPath ($) { + my ($path) = @_; + for my $ld (@{all()}) { + if ($ld->path() eq $path) { + return $ld; + } + } + return undef; +} + +sub findByDevno ($) { + my ($devno) = @_; + for my $ld (@{all()}) { + if ($ld->devno() eq $devno) { + return $ld; + } + } + return undef; +} + + +1; --- yaird-0.0.12.orig/perl/Makefile.am 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/Makefile.am 2006-10-20 16:53:15.000000000 +0200 @@ -71,6 +71,11 @@ LabeledPartition.pm \ LabeledPartitionTab.pm \ LogicalVolume.pm \ + LoopAesEntry.pm \ + LoopAesTab.pm \ + LoopAesVersion.pm \ + LoopDev.pm \ + LoopTab.pm \ LvmTab.pm \ ModProbe.pm \ NetDev.pm \ --- yaird-0.0.12.orig/perl/Makefile.in 2005-12-08 23:42:36.000000000 +0100 +++ yaird-0.0.12/perl/Makefile.in 2006-10-20 16:53:15.000000000 +0200 @@ -229,6 +229,11 @@ LabeledPartition.pm \ LabeledPartitionTab.pm \ LogicalVolume.pm \ + LoopAesEntry.pm \ + LoopAesTab.pm \ + LoopAesVersion.pm \ + LoopDev.pm \ + LoopTab.pm \ LvmTab.pm \ ModProbe.pm \ NetDev.pm \ --- yaird-0.0.12.orig/perl/ModProbe.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/ModProbe.pm 2006-10-20 16:53:15.000000000 +0200 @@ -90,6 +90,7 @@ use ActionList; use Blacklist; use KConfig; +use LoopAesVersion; package ModProbe; @@ -147,6 +148,15 @@ Base::fatal ("modprobe shows that module $m needs an external program; this is not supported. The offending line is: install $1"); } elsif ($line =~ /^insmod (\S+)$/) { + if ($m eq 'loop' ) { + # + # There exist two different loop modules both + # named 'loop': a native version and a loop-AES + # patched version. + # + LoopAesVersion::probeLoopAesModule ($1); + } + $actionList->add ("insmod", $1, optionList => ''); } --- yaird-0.0.12.orig/perl/Plan.pm 2006-10-20 16:54:51.000000000 +0200 +++ yaird-0.0.12/perl/Plan.pm 2006-10-20 16:54:13.000000000 +0200 @@ -33,6 +33,9 @@ use ActionList; use CryptTab; use NetDevTab; +use LoopAesTab; +use LoopTab; +use LoopAesVersion; package Plan; @@ -89,6 +92,7 @@ $ok || ($ok = tryDmCrypt ($actions,$device,[$device,@{$working}])); $ok || ($ok = tryLvm ($actions,$device,[$device,@{$working}])); $ok || ($ok = tryRaid ($actions,$device,[$device,@{$working}])); + $ok || ($ok = tryLoop ($actions,$device,[$device,@{$working}])); $ok || ($ok = tryHardware ($actions,$device,[$device,@{$working}])); if (! $ok) { Base::fatal ("unsupported device required: $name"); @@ -427,6 +431,414 @@ return 1; } + +# +# tryLoop -- To start a loopback device, start the underlying hardware, +# optionally make available a GPG encryption key by mounting (and +# later on unmounting) the respective filesystem at boot time or +# copying it to the image at build time, load the loop module and +# optional encryption modules, and setup the loopback device. +# +sub tryLoop ($$$) { + my ($actions, $device, $working) = @_; + + my $name = $device->name; + if ($name !~ /^loop\d+$/) { + return 0; + } + my $devno = $device->devno; + my ($major, $minor) = ($devno =~ /(\d+):(\d+)/); + + my $loopdev = LoopTab::findByDevno ($devno); + if (! defined ($loopdev)) { + Base::fatal ("can't find Loop info for $name"); + } + + # start the underlying block device + my $subdev = ActiveBlockDevTab::findByPath ($loopdev->device); + addDevicePlan ($actions, $subdev, $working); + + + # + # In most cases, the losetup status output only provides + # a partial set of the attributes required to setup a + # loop-AES device. For a complete set of attributes, we + # therefore need to parse fstab and loopaestab in addition, + # and all given attribute sets have to be compared and + # merged in a nit-picky fashion. + # + + # complete loopback device config + my %loopOpts; + + # + # Full set of possible attributes for each loop device + # config source, i.e. losetup info, fstab and loopaestab. + # + # For the purpose of comparison, an attribute is either + # marked as case sensitive (e.g. 'gpgkey' => 1) or + # case insensitive (e.g. 'encryption' => 0). + # + my $commonSupportedOpts = { + 'offset' => 0, + 'sizelimit' => 0, + 'encryption' => 0, + 'loinit' => 0, + }; + my $losetupSupportedOpts = { + %{$commonSupportedOpts}, + 'multikey' => 0, + }; + my $fsTabSupportedOpts = { + %{$commonSupportedOpts}, + 'gpgkey' => 1, + 'gpghome' => 1, + 'pseed' => 0, + 'phash' => 0, + 'itercountk' => 0, + }; + my $loopAesTabSupportedOpts = { + %{$fsTabSupportedOpts}, + 'gpgmount' => 0, + }; + + # available loop device config entries + my @conf; + + # + # All attributes shown by losetup describe the actual + # loopback device, so this set of attributes is made + # the highest-priority config source. + # + push @conf, { + supportedOpts => $losetupSupportedOpts, + opts => $loopdev->opts, + origin => 'losetup status', + }; + + # query fstab entry for the underlying block device + my $fsTabEntry = FsTab::findByDevno ($subdev->devno); + if (defined ($fsTabEntry)) { + # + # The forbidden case of a loopback swap device with a + # random encryption key is handled within FsTab, + # so this does not have to be reconsidered here. + # + + # check if loopback device is set correctly + if (! $fsTabEntry->opts->exists ("loop")) { + my $origin = $fsTabEntry->origin; + Base::fatal ("missing loop option in $origin"); + } + my $n = Base::devno ($fsTabEntry->opts->get ("loop")); + if ((! defined ($n)) || (! ($n eq $devno))) { + my $origin = $fsTabEntry->origin; + Base::fatal ("invalid loop option in $origin"); + } + push @conf, { + supportedOpts => $fsTabSupportedOpts, + opts => $fsTabEntry->opts, + origin => $fsTabEntry->origin, + }; + } + + # query loopaestab entry for the loopback device + my $loopAesTabEntry = LoopAesTab::findByDevno ($loopdev->devno); + if (defined ($loopAesTabEntry)) { + # check if underlying block device is set correctly + my $n = Base::devno ($loopAesTabEntry->source); + if ((! defined ($n)) || (! ($n eq $subdev->devno))) { + my $origin = $loopAesTabEntry->origin; + Base::fatal ("invalid source device in $origin"); + } + push @conf, { + supportedOpts => $loopAesTabSupportedOpts, + opts => $loopAesTabEntry->opts, + origin => $loopAesTabEntry->origin, + }; + } + + # require fstab or loopaestab entry for loop-AES devices + if (@conf < 2 && defined ($loopOpts{encryption})) { + Base::fatal ("can't find fstab or loopaestab entry for loop-AES device: $name"); + } + + # + # For every distinct pair of config entries, all attributes + # supported by both config sources strictly have to match. + # + for my $i (0 .. $#conf) { + for my $j (($i + 1) .. $#conf) { + my (%count, @intersect, @conflicts); + my @suppOpts1 = keys (%{${$conf[$i]}{supportedOpts}}); + my @suppOpts2 = keys (%{${$conf[$j]}{supportedOpts}}); + + # determine common set of supported attributes + for my $opt (@suppOpts1, @suppOpts2) { + if (++$count{$opt} == 2) { + push @intersect, $opt; + } + } + + # basically do a case (in)sensitive hash comparison + for my $opt (@intersect) { + my $exists1 = ${$conf[$i]}{opts}->exists ($opt); + my $exists2 = ${$conf[$j]}{opts}->exists ($opt); + my $val1 = ${$conf[$i]}{opts}->get ($opt); + my $val2 = ${$conf[$j]}{opts}->get ($opt); + + # case (in)sensitivity of an attribute value + my $case = ${${$conf[$i]}{supportedOpts}}{$opt}; + + if ($exists1 xor $exists2) { + push @conflicts, $opt; + } + elsif (defined ($val1) xor defined ($val2)) { + push @conflicts, $opt; + } + elsif (defined ($val1) && defined ($val2)) { + if ($case) { + if ($val1 ne $val2) { + push @conflicts, $opt; + } + } + else { + if (lc ($val1) ne lc ($val2)) { + push @conflicts, $opt; + } + } + } + } + if (@conflicts) { + my $opt = join (', ', @conflicts); + my $origin1 = ${$conf[$i]}{origin}; + my $origin2 = ${$conf[$j]}{origin}; + Base::fatal ("conflicting option(s) '$opt' in $origin1 and $origin2"); + } + } + } + # + # At this point, all config entries strictly match pertaining + # to their respective supported attributes, and can therefore + # be merged to provide a complete loopback device config. + # + for my $c (@conf) { + my @suppOpts = keys (%{${$c}{supportedOpts}}); + + for my $opt (@suppOpts) { + my $exists = ${$c}{opts}->exists ($opt); + my $val = ${$c}{opts}->get ($opt); + + if ($exists && (! exists ($loopOpts{$opt}))) { + $loopOpts{$opt} = $val; + } + } + } + + # + # Loopback device attributes specified manually in fstab + # or loopaestab must be checked for mutual compatibility + # and plausible attribute values. + # + if (! defined ($loopOpts{encryption})) { + my @opts = ('gpgkey', 'gpghome', 'gpgmount', 'pseed', 'phash', 'itercountk'); + if (@opts = grep { exists ($loopOpts{$_}) } @opts) { + my $opt = join (', ', @opts); + Base::fatal ("can't use option(s) '$opt' with unencrypted loopback device: $name"); + } + } + if (! defined ($loopOpts{gpgkey})) { + my @opts = ('gpghome', 'gpgmount'); + if (@opts = grep { exists ($loopOpts{$_}) } @opts) { + my $opt = join (', ', @opts); + Base::fatal ("can't use option(s) '$opt' with password encrypted loop-AES device: $name"); + } + if (exists ($loopOpts{multikey})) { + Base::fatal ("multi-key mode loop-AES device requires GPG key: $name"); + } + } + if (defined ($loopOpts{phash})) { + if (! ($loopOpts{phash} =~ /^(sha256|sha384|sha512|rmd160)$/i)) { + my $value = $loopOpts{phash}; + Base::fatal ("unsupported value 'phash=$value' for loop-AES device: $name"); + } + } + if (defined ($loopOpts{itercountk})) { + if (! ($loopOpts{itercountk} =~ /^\d+$/)) { + my $value = $loopOpts{itercountk}; + Base::fatal ("unsupported value 'itercountk=$value' for loop-AES device: $name"); + } + } + + # check existence of GPG key (and optionally GPG home directory) + my $gpgkey = $loopOpts{gpgkey}; + if (defined ($gpgkey)) { + if (! Base::isAbsolute ($gpgkey)) { + Base::fatal ("GPG key file ($gpgkey) not absolute: $name"); + } + if (! (-f $gpgkey)) { + Base::fatal ("GPG key file ($gpgkey) not a regular file: $name"); + } + $gpgkey = Base::canon ($gpgkey); + } + my $gpghome = $loopOpts{gpghome}; + if (defined ($gpghome)) { + if (! Base::isAbsolute ($gpghome)) { + Base::fatal ("GPG home directory ($gpghome) not absolute: $name"); + } + if (! (-d $gpghome)) { + Base::fatal ("GPG home directory ($gpghome) not a directory: $name"); + } + $gpghome = Base::canon ($gpghome); + } + + # + # In case of encryption with a GPG key, there exist + # two different modes of operation: + # + # * If the option 'gpgmount' has been specified, the + # filesystem containing the GPG key file (and optionally + # the GPG home directory) will be mounted at boot time. + # + # * Otherwise, the GPG key file (and optionally the GPG + # home directory) will be copied to the image. + # + + # choose a unique mount point for each loop device + my $gpgkey_mnt = (exists ($loopOpts{gpgmount}) ? "/.$name" : undef); + + if (defined ($gpgkey) && defined ($gpgkey_mnt)) { + # create GPG key mount point on the image + $actions->add ("gpgmount", $gpgkey_mnt); + + # + # Determine block device of GPG key file, and ensure + # that an optional GPG home directory lies on the same + # filesystem. + # + my $gpgdevno = Base::filedev ($gpgkey); + if (! defined ($gpgdevno)) { + Base::fatal ("can't determine device of GPG key file ($gpgkey): $name"); + } + if (defined ($gpghome)) { + my $n = Base::filedev ($gpghome); + if (! defined ($n)) { + Base::fatal ("can't determine device of GPG home directory ($gpghome): $name"); + } + if (! ($n eq $gpgdevno)) { + Base::fatal ("GPG key file ($gpgkey) and GPG home directory ($gpghome) not on the same filesystem: $name"); + } + } + + # + # Determine the fstab mount point of the block device + # and ensure that the GPG key file (and optionally the + # GPG home directory) really lies on this filesystem. + # + my $gpgfs = FsTab::findByDevno ($gpgdevno); + if (! defined ($gpgfs)) { + Base::fatal ("GPG key ($gpgkey) device not in fstab: $name"); + } + my $gpgkey_dev = $gpgfs->dev; + # + # If it's mounted via a loop device, use that as GPG key device + # + if ($gpgfs->opts->exists ('loop')) { + $gpgkey_dev = $gpgfs->opts->get ('loop'); + } + my $gk = $gpgkey; + my $gh = $gpghome; + my $fsmnt = $gpgfs->mnt; + $gk =~ s/^$fsmnt//; + if (defined ($gh)) { + $gh =~ s/^$fsmnt//; + } + if (! ($gpgkey eq Base::canon ($fsmnt . "/" . $gk))) { + Base::fatal ("GPG key file ($gpgkey) filesystem not found: $name"); + } + if (defined ($gpghome) && (! ($gpghome eq Base::canon ($fsmnt . "/" . $gh)))) { + Base::fatal ("GPG home directory ($gpghome) filesystem not found: $name"); + } + + # construct absolute paths as found at boot time. + $gpgkey = Base::canon ($gpgkey_mnt . "/" . $gk); + if (defined ($gpghome)) { + $gpghome = Base::canon ($gpgkey_mnt . "/" . $gh); + } + + # + # Start and (read-only) mount GPG key device + # + # (Manually adding the device plan before mounting ensures + # that potential loops are detected. A loop might occur + # if the GPG key file erroneously lies on the encrypted + # loopback device itself.) + # + my $gpgdev = ActiveBlockDevTab::findByPath ($gpgkey_dev); + if (! defined ($gpgdev)) { + Base::fatal ("can't find block device '$gpgkey_dev' in fstab"); + } + addDevicePlan ($actions, $gpgdev, $working); + addBlockDevMount ($actions, $gpgkey_dev, $gpgkey_mnt, 0); + } + if (defined ($gpgkey) && (! defined ($gpgkey_mnt))) { + # copy GPG key file (and optionally GPG home directory) to image + if (defined ($gpghome)) { + $actions->add ("gpgpublic", $gpgkey, gpghome => $gpghome); + } + else { + $actions->add ("gpgkey", $gpgkey); + } + } + + # + # Load required modules, ensuring matching kernel and + # userspace loopback support (i.e. native or loop-AES). + # + ModProbe::addModules ($actions, [ "loop" ]); + if (! LoopAesVersion::matchingVersions) { + Base::fatal ("loop kernel module and losetup program versions mismatch"); + } + my $encryption = $loopOpts{encryption}; + if (defined ($encryption)) { + if ($encryption =~ /^(twofish|blowfish|serpent)\d+$/i) { + ModProbe::addModules ($actions, [ "loop_" . lc ($1) ]); + } + elsif (! ($encryption =~ /^(aes\d*|xor)$/i)) { + Base::fatal ("unsupported encryption type ($encryption): '$name'"); + } + } + + # load keymap to allow proper password entry + if (defined ($encryption)) { + $actions->add ("loadkeys", "loadkeys"); + } + + # setup loopback device + $device->setCreator ("losetup"); + $actions->add ("losetup", $device->yspecial, + major => $major, + minor => $minor, + offset => $loopOpts{offset}, + sizelimit => $loopOpts{sizelimit}, + encryption => $loopOpts{encryption}, + loinit => $loopOpts{loinit}, + gpgkey => $gpgkey, + gpghome => $gpghome, + pseed => $loopOpts{pseed}, + phash => $loopOpts{phash}, + itercountk => $loopOpts{itercountk}, + device => $subdev->yspecial, + ); + + # unmount GPG key device if appropriate + if (defined ($gpgkey_mnt)) { + addUnmount ($actions, $gpgkey_mnt); + } + + return 1; +} + # # tryEvms Look if the device could be an evms one # @@ -635,6 +1047,13 @@ } # + # If it's mounted via a loop device, use that as root device + # + if ($root->opts->exists ('loop')) { + $rootDevName = $root->opts->get ('loop'); + } + + # # and device must be in /dev, to determine whether # it's raid, lvm, scsi or whatever. # --- yaird-0.0.12.orig/perl/TestSet.pm 2005-12-08 23:42:33.000000000 +0100 +++ yaird-0.0.12/perl/TestSet.pm 2006-10-20 16:53:15.000000000 +0200 @@ -29,6 +29,7 @@ use LvmTab; use Hardware; use RaidTab; +use LoopTab; use EvmsTab; use InputTab; use Image; @@ -132,6 +133,14 @@ } } +sub testLoopDevices () { + print "Loopback devices:\n"; + for my $ld (@{LoopTab::all()}) { + my $str = $ld->string; + print "\t$str\n"; + } +} + sub testEvms () { print "Evms devices:\n"; for my $ev (@{EvmsTab::all()}) { @@ -231,6 +240,7 @@ testLvm (); testHardware (); testRaidDevices(); + testLoopDevices(); testInterpretation (); testInput (); testKconfig (); --- yaird-0.0.12.orig/templates/Debian-initrd.cfg 2006-10-20 16:54:51.000000000 +0200 +++ yaird-0.0.12/templates/Debian-initrd.cfg 2006-10-20 16:53:15.000000000 +0200 @@ -300,6 +300,124 @@ END SCRIPT END TEMPLATE + + # + # Load keymap upon boot, allowing proper password entry + # for encrypted devices. + # + TEMPLATE loadkeys + BEGIN + FILE "/bin/loadkeys" + FILE "/bin/gunzip" + FILE "/etc/console/boottime.kmap.gz" + SCRIPT "/sbin/init" + BEGIN + !# loadkeys from the kbd package has problems + !# uncompressing the keymap on-the-fly when + !# run from initramfs, don't know why + !gunzip /etc/console/boottime.kmap.gz + !loadkeys /etc/console/boottime.kmap + END SCRIPT + END TEMPLATE + + + # + # Include GPG program and GPG key file. + # + TEMPLATE gpgkey + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Include GPG program, GPG key file and GPG home directory. + # + TEMPLATE gpgpublic + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + TREE "<TMPL_VAR NAME=gpghome>" + END TEMPLATE + + + # + # Include GPG program and create GPG key device mount point. + # + TEMPLATE gpgmount + BEGIN + FILE "/usr/bin/gpg" + DIRECTORY "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Setup plain loopback or loop-AES encrypted device. + # + # common losetup arguments: + # - offset + # - device (underlying block device) + # + # loop-AES specific losetup arguments: + # - encryption + # - loinit + # - sizelimit + # - gpgkey + # - gpghome + # - pseed + # - phash + # - itercountk + # + TEMPLATE losetup + BEGIN + FILE "/sbin/losetup" + SCRIPT "/sbin/init" + BEGIN + !mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor> + !DOCRYPT=1 + !while [ "$DOCRYPT" != "0" ]; do + ! <TMPL_IF NAME=encryption> + ! echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase" + ! </TMPL_IF> + ! losetup \ + ! <TMPL_IF NAME=encryption> \ + ! -e <TMPL_VAR NAME=encryption> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=loinit> \ + ! -I <TMPL_VAR NAME=loinit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=offset> \ + ! -o <TMPL_VAR NAME=offset> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=sizelimit> \ + ! -s <TMPL_VAR NAME=sizelimit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=gpgkey> \ + ! -K <TMPL_VAR NAME=gpgkey> \ + ! <TMPL_IF NAME=gpghome> \ + ! -G <TMPL_VAR NAME=gpghome> \ + ! <TMPL_ELSE> \ + ! -G /nonexistent \ + ! </TMPL_IF> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=pseed> \ + ! -S <TMPL_VAR NAME=pseed> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=phash> \ + ! -H <TMPL_VAR NAME=phash> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=itercountk> \ + ! -C <TMPL_VAR NAME=itercountk> \ + ! </TMPL_IF> \ + ! <TMPL_VAR NAME=target> \ + ! <TMPL_VAR NAME=device> + ! DOCRYPT=$? + !done + END SCRIPT + END TEMPLATE + + # # cryptsetup arguments: # - target --- yaird-0.0.12.orig/templates/Debian.cfg 2006-10-20 16:54:51.000000000 +0200 +++ yaird-0.0.12/templates/Debian.cfg 2006-10-20 16:54:13.000000000 +0200 @@ -329,6 +329,123 @@ # + # Load keymap upon boot, allowing proper password entry + # for encrypted devices. + # + TEMPLATE loadkeys + BEGIN + FILE "/bin/loadkeys" + FILE "/bin/gunzip" + FILE "/etc/console/boottime.kmap.gz" + SCRIPT "/init" + BEGIN + !# loadkeys from the kbd package has problems + !# uncompressing the keymap on-the-fly when + !# run from initramfs, don't know why + !gunzip /etc/console/boottime.kmap.gz + !loadkeys /etc/console/boottime.kmap + END SCRIPT + END TEMPLATE + + + # + # Include GPG program and GPG key file. + # + TEMPLATE gpgkey + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Include GPG program, GPG key file and GPG home directory. + # + TEMPLATE gpgpublic + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + TREE "<TMPL_VAR NAME=gpghome>" + END TEMPLATE + + + # + # Include GPG program and create GPG key device mount point. + # + TEMPLATE gpgmount + BEGIN + FILE "/usr/bin/gpg" + DIRECTORY "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Setup plain loopback or loop-AES encrypted device. + # + # common losetup arguments: + # - offset + # - device (underlying block device) + # + # loop-AES specific losetup arguments: + # - encryption + # - loinit + # - sizelimit + # - gpgkey + # - gpghome + # - pseed + # - phash + # - itercountk + # + TEMPLATE losetup + BEGIN + FILE "/sbin/losetup" + SCRIPT "/init" + BEGIN + !mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor> + !DOCRYPT=1 + !while [ "$DOCRYPT" != "0" ]; do + ! <TMPL_IF NAME=encryption> + ! echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase" + ! </TMPL_IF> + ! losetup \ + ! <TMPL_IF NAME=encryption> \ + ! -e <TMPL_VAR NAME=encryption> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=loinit> \ + ! -I <TMPL_VAR NAME=loinit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=offset> \ + ! -o <TMPL_VAR NAME=offset> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=sizelimit> \ + ! -s <TMPL_VAR NAME=sizelimit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=gpgkey> \ + ! -K <TMPL_VAR NAME=gpgkey> \ + ! <TMPL_IF NAME=gpghome> \ + ! -G <TMPL_VAR NAME=gpghome> \ + ! <TMPL_ELSE> \ + ! -G /nonexistent \ + ! </TMPL_IF> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=pseed> \ + ! -S <TMPL_VAR NAME=pseed> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=phash> \ + ! -H <TMPL_VAR NAME=phash> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=itercountk> \ + ! -C <TMPL_VAR NAME=itercountk> \ + ! </TMPL_IF> \ + ! <TMPL_VAR NAME=target> \ + ! <TMPL_VAR NAME=device> + ! DOCRYPT=$? + !done + END SCRIPT + END TEMPLATE + + + # # cryptsetup arguments: # - target # - cipher --- yaird-0.0.12.orig/templates/Fedora.cfg 2006-10-20 16:54:51.000000000 +0200 +++ yaird-0.0.12/templates/Fedora.cfg 2006-10-20 16:54:13.000000000 +0200 @@ -340,6 +340,123 @@ # + # Load keymap upon boot, allowing proper password entry + # for encrypted devices. + # + TEMPLATE loadkeys + BEGIN + FILE "/bin/loadkeys" + FILE "/bin/gunzip" + FILE "/etc/console/boottime.kmap.gz" + SCRIPT "/init" + BEGIN + !# loadkeys from the kbd package has problems + !# uncompressing the keymap on-the-fly when + !# run from initramfs, don't know why + !gunzip /etc/console/boottime.kmap.gz + !loadkeys /etc/console/boottime.kmap + END SCRIPT + END TEMPLATE + + + # + # Include GPG program and GPG key file. + # + TEMPLATE gpgkey + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Include GPG program, GPG key file and GPG home directory. + # + TEMPLATE gpgpublic + BEGIN + FILE "/usr/bin/gpg" + FILE "<TMPL_VAR NAME=target>" + TREE "<TMPL_VAR NAME=gpghome>" + END TEMPLATE + + + # + # Include GPG program and create GPG key device mount point. + # + TEMPLATE gpgmount + BEGIN + FILE "/usr/bin/gpg" + DIRECTORY "<TMPL_VAR NAME=target>" + END TEMPLATE + + + # + # Setup plain loopback or loop-AES encrypted device. + # + # common losetup arguments: + # - offset + # - device (underlying block device) + # + # loop-AES specific losetup arguments: + # - encryption + # - loinit + # - sizelimit + # - gpgkey + # - gpghome + # - pseed + # - phash + # - itercountk + # + TEMPLATE losetup + BEGIN + FILE "/sbin/losetup" + SCRIPT "/init" + BEGIN + !mknod <TMPL_VAR NAME=target> b <TMPL_VAR NAME=major> <TMPL_VAR NAME=minor> + !DOCRYPT=1 + !while [ "$DOCRYPT" != "0" ]; do + ! <TMPL_IF NAME=encryption> + ! echo "Encrypted device ('<TMPL_VAR NAME=device>'), please supply passphrase" + ! </TMPL_IF> + ! losetup \ + ! <TMPL_IF NAME=encryption> \ + ! -e <TMPL_VAR NAME=encryption> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=loinit> \ + ! -I <TMPL_VAR NAME=loinit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=offset> \ + ! -o <TMPL_VAR NAME=offset> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=sizelimit> \ + ! -s <TMPL_VAR NAME=sizelimit> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=gpgkey> \ + ! -K <TMPL_VAR NAME=gpgkey> \ + ! <TMPL_IF NAME=gpghome> \ + ! -G <TMPL_VAR NAME=gpghome> \ + ! <TMPL_ELSE> \ + ! -G /nonexistent \ + ! </TMPL_IF> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=pseed> \ + ! -S <TMPL_VAR NAME=pseed> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=phash> \ + ! -H <TMPL_VAR NAME=phash> \ + ! </TMPL_IF> \ + ! <TMPL_IF NAME=itercountk> \ + ! -C <TMPL_VAR NAME=itercountk> \ + ! </TMPL_IF> \ + ! <TMPL_VAR NAME=target> \ + ! <TMPL_VAR NAME=device> + ! DOCRYPT=$? + !done + END SCRIPT + END TEMPLATE + + + # # cryptsetup arguments: # - target # - cipher