forked from pool/suse-module-tools
Accepting request 225595 from home:michal-m:branches:Base:System
- Add modsign-verify tool to verify signatures of modules (fate#314507). - weak-modules2: Support XZ compressed initrds (bnc#778119, bnc#867312) OBS-URL: https://build.opensuse.org/request/show/225595 OBS-URL: https://build.opensuse.org/package/show/Base:System/suse-module-tools?expand=0&rev=8
This commit is contained in:
parent
7f90a5aa10
commit
afc042a2b6
252
modsign-verify
Normal file
252
modsign-verify
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
#
|
||||||
|
# Verify kernel module signature generated by /usr/src/linux/scripts/sign-file
|
||||||
|
#
|
||||||
|
# Parts of this script were copied from sign-file, written by David Howels
|
||||||
|
#
|
||||||
|
|
||||||
|
my $USAGE = "Usage: modsign-verify [-v] [-q] [--certificate <x509> | --cert-dir <dir>] <module>\n";
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use IPC::Open2;
|
||||||
|
use Getopt::Long;
|
||||||
|
use File::Temp qw(tempfile);
|
||||||
|
|
||||||
|
my $cert;
|
||||||
|
my $cert_dir;
|
||||||
|
my $verbose = 1;
|
||||||
|
GetOptions(
|
||||||
|
"certificate=s" => \$cert,
|
||||||
|
"cert-dir=s" => \$cert_dir,
|
||||||
|
"q|quiet" => sub { $verbose-- if $verbose; },
|
||||||
|
"v|verbose" => sub { $verbose++; },
|
||||||
|
"h|help" => sub {
|
||||||
|
print $USAGE;
|
||||||
|
print "Return codes: 0 good signature\n";
|
||||||
|
print " 1 bad signature\n";
|
||||||
|
print " 2 certificate not found\n";
|
||||||
|
print " 3 module not signed\n";
|
||||||
|
print " >3 other error\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
) or die($USAGE);
|
||||||
|
|
||||||
|
sub _verbose {
|
||||||
|
my $level = shift;
|
||||||
|
|
||||||
|
return if $verbose < $level;
|
||||||
|
print STDERR @_;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub info { _verbose(1, @_); }
|
||||||
|
sub verbose { _verbose(2, @_); }
|
||||||
|
sub debug { _verbose(3, @_); }
|
||||||
|
|
||||||
|
if (@ARGV > 1) {
|
||||||
|
print STDERR "Excess arguments\n";
|
||||||
|
die($USAGE);
|
||||||
|
} elsif (@ARGV < 1) {
|
||||||
|
print STDERR "No module supplied\n";
|
||||||
|
die($USAGE);
|
||||||
|
} elsif ($cert && $cert_dir) {
|
||||||
|
print STDERR "Please specify either --certificate or --cert-dir, not both.\n";
|
||||||
|
die($USAGE);
|
||||||
|
}
|
||||||
|
my $module_name = shift(@ARGV);
|
||||||
|
if (!$cert && !$cert_dir) {
|
||||||
|
$cert_dir = "/etc/uefi/certs";
|
||||||
|
verbose("Using default certificate directory $cert_dir\n");
|
||||||
|
}
|
||||||
|
my @certs;
|
||||||
|
if ($cert) {
|
||||||
|
push(@certs, $cert);
|
||||||
|
} else {
|
||||||
|
my $dh;
|
||||||
|
if (!opendir($dh, $cert_dir)) {
|
||||||
|
print STDERR "$cert_dir: $!\n";
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
while (my $entry = readdir($dh)) {
|
||||||
|
next if $entry =~ /^\./;
|
||||||
|
next if !-f "$cert_dir/$entry";
|
||||||
|
push(@certs, "$cert_dir/$entry");
|
||||||
|
}
|
||||||
|
closedir($dh);
|
||||||
|
if (!@certs) {
|
||||||
|
print STDERR "No certificates found in $cert_dir\n";
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Function to read the contents of a file into a variable.
|
||||||
|
#
|
||||||
|
sub read_file($)
|
||||||
|
{
|
||||||
|
my ($file) = @_;
|
||||||
|
my $contents;
|
||||||
|
my $len;
|
||||||
|
|
||||||
|
open(FD, "<$file") || die $file;
|
||||||
|
binmode FD;
|
||||||
|
my @st = stat(FD);
|
||||||
|
die $file if (!@st);
|
||||||
|
$len = read(FD, $contents, $st[7]) || die $file;
|
||||||
|
close(FD) || die $file;
|
||||||
|
die "$file: Wanted length ", $st[7], ", got ", $len, "\n"
|
||||||
|
if ($len != $st[7]);
|
||||||
|
return $contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub openssl_pipe($$) {
|
||||||
|
my ($input, $cmd) = @_;
|
||||||
|
my ($pid, $res);
|
||||||
|
|
||||||
|
$pid = open2(*read_from, *write_to, $cmd) || die $cmd;
|
||||||
|
binmode write_to;
|
||||||
|
if (defined($input) && $input ne "") {
|
||||||
|
print write_to $input || die "$cmd: $!";
|
||||||
|
}
|
||||||
|
close(write_to) || die "$cmd: $!";
|
||||||
|
|
||||||
|
binmode read_from;
|
||||||
|
read(read_from, $res, 4096) || die "$cmd: $!";
|
||||||
|
close(read_from) || die "$cmd: $!";
|
||||||
|
waitpid($pid, 0) || die;
|
||||||
|
die "$cmd died: $?" if ($? >> 8);
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub cert_matches {
|
||||||
|
my ($cert, $subject_key_id, $subject_name) = @_;
|
||||||
|
|
||||||
|
open(my $pipe, '-|', "openssl", "x509", "-noout", "-text",
|
||||||
|
"-inform", "DER", "-in", $cert) or die "openssl x509: $!\n";
|
||||||
|
my $found = 0;
|
||||||
|
my $found_key_id;
|
||||||
|
while (<$pipe>) {
|
||||||
|
chomp;
|
||||||
|
if (/^\s*X509v3 Subject Key Identifier:/) {
|
||||||
|
$found = 1;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
if ($found) {
|
||||||
|
s/[\s:]*//g;
|
||||||
|
$found_key_id = pack("H*", $_);
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found_key_id) {
|
||||||
|
print STDERR "Warning: no subject key identifier in $cert\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
debug("$cert has hey id " . unpack("H*", $found_key_id));
|
||||||
|
# FIXME: Also check subject_name
|
||||||
|
return ($found_key_id eq $subject_key_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $module = read_file($module_name);
|
||||||
|
my $module_len = length($module);
|
||||||
|
my $magic_number = "~Module signature appended~\n";
|
||||||
|
my $magic_len = length($magic_number);
|
||||||
|
my $info_len = 12;
|
||||||
|
|
||||||
|
sub eat
|
||||||
|
{
|
||||||
|
my $length = shift;
|
||||||
|
if ($module_len < $length) {
|
||||||
|
die "Module size too short\n";
|
||||||
|
}
|
||||||
|
my $res = substr($module, -$length);
|
||||||
|
$module = substr($module, 0, $module_len - $length);
|
||||||
|
$module_len -= $length;
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eat($magic_len) ne $magic_number) {
|
||||||
|
print "$module_name: module not signed\n";
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
my $info = eat($info_len);
|
||||||
|
my ($algo, $hash, $id_type, $name_len, $key_len, $sig_len) =
|
||||||
|
unpack("CCCCCxxxN", $info);
|
||||||
|
my $signature = eat($sig_len);
|
||||||
|
if (unpack("n", $signature) != $sig_len - 2) {
|
||||||
|
die "Invalid signature format\n";
|
||||||
|
}
|
||||||
|
$signature = substr($signature, 2);
|
||||||
|
my $key_id = eat($key_len);
|
||||||
|
my $name = eat($name_len);
|
||||||
|
|
||||||
|
if ($algo != 1) {
|
||||||
|
die "Unsupported signature algorithm\n";
|
||||||
|
}
|
||||||
|
#
|
||||||
|
# Digest the data
|
||||||
|
#
|
||||||
|
my ($prologue, $dgst);
|
||||||
|
if ($hash == 2) {
|
||||||
|
$prologue = pack("C*",
|
||||||
|
0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
|
||||||
|
0x2B, 0x0E, 0x03, 0x02, 0x1A,
|
||||||
|
0x05, 0x00, 0x04, 0x14);
|
||||||
|
$dgst = "sha1";
|
||||||
|
} elsif ($hash == 7) {
|
||||||
|
$prologue = pack("C*",
|
||||||
|
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
|
||||||
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
|
||||||
|
0x05, 0x00, 0x04, 0x1C);
|
||||||
|
$dgst = "sha224";
|
||||||
|
} elsif ($hash == 4) {
|
||||||
|
$prologue = pack("C*",
|
||||||
|
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
|
||||||
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
||||||
|
0x05, 0x00, 0x04, 0x20);
|
||||||
|
$dgst = "sha256";
|
||||||
|
} elsif ($hash == 5) {
|
||||||
|
$prologue = pack("C*",
|
||||||
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
|
||||||
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
|
||||||
|
0x05, 0x00, 0x04, 0x30);
|
||||||
|
$dgst = "sha384";
|
||||||
|
} elsif ($hash == 6) {
|
||||||
|
$prologue = pack("C*",
|
||||||
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
|
||||||
|
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
|
||||||
|
0x05, 0x00, 0x04, 0x40);
|
||||||
|
$dgst = "sha512";
|
||||||
|
} else {
|
||||||
|
die "Unsupported hash algorithm\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
verbose("Signed by: $name\n");
|
||||||
|
verbose("Key id: " . unpack("H*", $key_id) . "\n");
|
||||||
|
verbose("Hash algorithm: $dgst\n");
|
||||||
|
|
||||||
|
my $digest = openssl_pipe($module, "openssl dgst -$dgst -binary");
|
||||||
|
my $original_message = $prologue . $digest;
|
||||||
|
|
||||||
|
for my $cert (sort @certs) {
|
||||||
|
debug("Trying $cert\n");
|
||||||
|
next unless cert_matches($cert, $key_id, $name);
|
||||||
|
verbose("Found certificate $cert\n");
|
||||||
|
|
||||||
|
my ($fh, $filename) = tempfile() or die "Cannot create temporary file: $!\n";
|
||||||
|
my $pubkey = openssl_pipe("",
|
||||||
|
"openssl x509 -noout -in $cert -inform DER -pubkey");
|
||||||
|
print $fh $pubkey;
|
||||||
|
close($fh);
|
||||||
|
my $verified_message = openssl_pipe($signature,
|
||||||
|
"openssl rsautl -verify -inkey $filename -keyform PEM -pubin");
|
||||||
|
unlink($filename);
|
||||||
|
if ($original_message ne $verified_message) {
|
||||||
|
print "$module_name: bad signature\n";
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
print "$module_name: good signature\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
print "certificate not found\n";
|
||||||
|
exit(2);
|
@ -1,3 +1,15 @@
|
|||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Mar 11 12:57:36 UTC 2014 - mmarek@suse.cz
|
||||||
|
|
||||||
|
- Add modsign-verify tool to verify signatures of modules
|
||||||
|
(fate#314507).
|
||||||
|
|
||||||
|
-------------------------------------------------------------------
|
||||||
|
Tue Mar 11 12:38:39 UTC 2014 - mmarek@suse.cz
|
||||||
|
|
||||||
|
- weak-modules2: Support XZ compressed initrds (bnc#778119,
|
||||||
|
bnc#867312)
|
||||||
|
|
||||||
-------------------------------------------------------------------
|
-------------------------------------------------------------------
|
||||||
Mon Mar 10 15:08:15 UTC 2014 - mmarek@suse.cz
|
Mon Mar 10 15:08:15 UTC 2014 - mmarek@suse.cz
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ Source5: weak-modules
|
|||||||
Source6: weak-modules2
|
Source6: weak-modules2
|
||||||
Source7: driver-check.sh
|
Source7: driver-check.sh
|
||||||
Source8: suse-module-tools.rpmlintrc
|
Source8: suse-module-tools.rpmlintrc
|
||||||
|
Source9: modsign-verify
|
||||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@ -76,6 +77,10 @@ install -d -m 755 "$b/usr/lib/module-init-tools"
|
|||||||
install -pm 755 %_sourcedir/weak-modules{,2} "$b/usr/lib/module-init-tools/"
|
install -pm 755 %_sourcedir/weak-modules{,2} "$b/usr/lib/module-init-tools/"
|
||||||
install -pm 755 %_sourcedir/driver-check.sh "$b/usr/lib/module-init-tools/"
|
install -pm 755 %_sourcedir/driver-check.sh "$b/usr/lib/module-init-tools/"
|
||||||
|
|
||||||
|
# modsign-verify for verifying module signatures
|
||||||
|
install -d -m 755 "$b/usr/bin"
|
||||||
|
install -pm 755 %_sourcedir/modsign-verify "$b/usr/bin/"
|
||||||
|
|
||||||
%post
|
%post
|
||||||
test_allow_on_install()
|
test_allow_on_install()
|
||||||
{
|
{
|
||||||
@ -139,6 +144,7 @@ fi
|
|||||||
%dir /etc/depmod.d
|
%dir /etc/depmod.d
|
||||||
%config /etc/depmod.d/00-system.conf
|
%config /etc/depmod.d/00-system.conf
|
||||||
%_docdir/module-init-tools
|
%_docdir/module-init-tools
|
||||||
|
/usr/bin/modsign-verify
|
||||||
/usr/lib/module-init-tools
|
/usr/lib/module-init-tools
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
@ -287,6 +287,16 @@ previous_version_of_kmp() {
|
|||||||
echo "$old_kmp"
|
echo "$old_kmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# write GZIP / XZ uncompressed file to stdout
|
||||||
|
uncomp() {
|
||||||
|
local file=$1
|
||||||
|
|
||||||
|
if gzip -cd "$file" 2>/dev/null; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
xz -cd "$file"
|
||||||
|
}
|
||||||
|
|
||||||
# test if mkinitrd is needed for $krel. This should be decided by initrd itself
|
# test if mkinitrd is needed for $krel. This should be decided by initrd itself
|
||||||
# actually
|
# actually
|
||||||
# stdin - list of changed modules ("_kernel_" for the whole kernel)
|
# stdin - list of changed modules ("_kernel_" for the whole kernel)
|
||||||
@ -317,7 +327,7 @@ needs_mkinitrd() {
|
|||||||
if [ ! -e /boot/initrd-$krel ]; then
|
if [ ! -e /boot/initrd-$krel ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
local initrd_basenames=($( (gzip -cd /boot/initrd-$krel | cpio -t --quiet | filter_basenames; INITRD_MODULES=; . /etc/sysconfig/kernel &>/dev/null; printf '%s.ko\n' $INITRD_MODULES) | sort -u))
|
local initrd_basenames=($( (uncomp /boot/initrd-$krel | cpio -t --quiet | filter_basenames; INITRD_MODULES=; . /etc/sysconfig/kernel &>/dev/null; printf '%s.ko\n' $INITRD_MODULES) | sort -u))
|
||||||
local i=($(join <(printf '%s\n' "${changed_basenames[@]}") \
|
local i=($(join <(printf '%s\n' "${changed_basenames[@]}") \
|
||||||
<(printf '%s\n' "${initrd_basenames[@]}") ))
|
<(printf '%s\n' "${initrd_basenames[@]}") ))
|
||||||
log "changed initrd modules for kernel $krel: ${i[@]-none}"
|
log "changed initrd modules for kernel $krel: ${i[@]-none}"
|
||||||
|
Loading…
Reference in New Issue
Block a user