Wie überprüfe ich eine Kernelmodulsignatur?

7

Wenn Sie eine Kernelquelle kompilieren, können Sie mithilfe der CONFIG_MODULE_SIG*Optionen Kernelmodule signieren . Das modinfoTool sollte die Aufgabe übernehmen, die Modulsignatur zu überprüfen, aber es gibt seit Jahren einen Fehler, und das Tool kann den Job einfach nicht mehr ausführen. Ich bekomme nur folgendes:

sig_id:         PKCS#7
signer:
sig_key:
sig_hashalgo:   md4
signature:      30:82:02:F4:06:09:2A:86:48:86:F7:0D:01:07:02:A0:82:02:E5:30:
                ...

Es gibt also keinen Schlüssel und der Hash-Algorithmus ist md4, der nicht einmal im Kernel kompiliert ist.

Wie kann man die Modulsignatur manuell überprüfen und verifizieren? Ist das überhaupt möglich?

Mikhail Morfikov
quelle
Welche Kernel-Version ist das? Es muss ziemlich alt sein, einen solchen veralteten Hash-Algorithmus zu verwenden.
Time4Tea
1
Laut dieser Seite werden öffentliche Schlüssel unter gespeichert /proc/keys. Hast du dort gesucht? Siehe den Abschnitt 'Öffentliche Schlüssel im Kernel'.
Time4Tea
Der Kernel ist nicht alt, es ist 4.20. Der md4Algorithmus wird wegen eines kmodFehlers gezeigt und ist seit mindestens 2-3 Jahren dort: bugzilla.redhat.com/show_bug.cgi?id=1320921
Mikhail Morfikov
1
Lesen Sie dies: wiki.gentoo.org/wiki/Signed_kernel_module_support
Mikhail Morfikov
1
@MikhailMorfikov für die seltsame Ausgabe von modinfo, ich denke, dies beantwortet es: "Wenn die PKC # 7-Signaturmethode verwendet wird, enthält die alte Struktur keine nützlichen Daten"; Wenn Sie eine neue Version von kmod aus dem Quellcode erstellen, wird modinfo unknownanstelle von md4for gedruckt sig_hashalgo.
Mosvy

Antworten:

5

Ja, das ist möglich, aber es ist ziemlich kompliziert.

Zuerst müssen Sie die Modulsignatur extrahieren - dafür können Sie das extract-module.sig.plSkript aus der Kernelquelle verwenden:

$ scripts/extract-module-sig.pl -s MODULE.ko >/tmp/modsig.
Read 789006 bytes from module file
Found magic number at 789006
Found PKCS#7/CMS encapsulation
Found 670 bytes of signature [3082029a06092a864886f70d010702a0]

Zweitens müssen Sie das Zertifikat und den öffentlichen Schlüssel aus dem Kernel extrahieren. Sie können das extract-sys-certs.plSkript dafür verwenden:

$ scripts/extract-sys-certs.pl /PATH/TO/vmlinux /tmp/cert.x509
Have 32 sections
Have 28167 symbols
Have 1346 bytes of certs at VMA 0xffffffff81be6db8
Certificate list in section .init.data
Certificate list at file offset 0xde6db8
$ openssl x509 -pubkey -noout -inform der -in /tmp/cert.x509 -out /tmp/pubkey

Sie können den öffentlichen Schlüssel auch aus dem certs/signing_key.x509oder den certs/signing_key.pemDateien aus dem Build-Verzeichnis des Linux-Kernels extrahieren .

Nachdem das getan, Sie alle Daten haben müssen Sie in /tmp/modsigund /tmp/cert.x509und kann mit dem Dutzend fortsetzen oder so Schritte notwendig , um eine PKCS # 7 Signatur zu verifizieren.

Sie können in diesem Blog suchen Beitrag für das gesamte Rezept.


Ich habe versucht, den gesamten Prozess (mit Ausnahme des extract-certs.plSchritts) in ein Perl-Skript zu schreiben.

Sie können es so verwenden:

perl checkmodsig.pl /path/to/cert.x509 mod1.ko mod2.ko ...

YMMV . Ich habe dies nur mit einem benutzerdefinierten Kernel versucht, der sha512-Signaturen verwendet. Dies sollte durch die Verwendung der OpenSSL - Bibliotheken direkt natürlich viel besser gemacht von, statt kludging zusammen langsam und fragil openssl x509, asn1parseund rsautlAnrufungen.

checkmodsig.pl

use strict;

sub through {
    my ($cmd, $data, $cb) = @_;
    use IPC::Open2;
    my $pid = open2 my $from, my $to, ref $cmd ? @$cmd : $cmd;
    print $to $data; close $to; my $out;
    if($cb){ while(<$from>){ last if $out = $cb->($_) } }
    else { local $/; $out = <$from>; }
    waitpid ($pid, 0);
    die "status $?" if $? != 0;
    $out;
}
sub gethash {
    my ($d) = @_; my ($alg, $hash);
    through [qw(openssl asn1parse -inform der)], $d, sub {
        if(/(\d+):d=\d+ +hl= *(\d+) +l= *(\d+) +prim: +OCTET STRING/){
            $hash = substr $d, $1 + $2, $3
        }elsif(/prim: +OBJECT +:(sha\w+)/){
            $alg = $1;
        }
        undef
    };
    $alg, $hash
}

use File::Temp;
my $tf = new File::Temp;
my $pub_key;
my @type = qw(PGP X509 PKCS7);
my $r = 0;
if((my $cert = shift) =~ /(\.x509)$|\.pem$/i){
    $pub_key = $tf->filename;
    system qw(openssl x509 -pubkey -noout),
        '-inform', $1 ? 'der' : 'pem',
        '-in', $cert, '-out', $pub_key;
    die "status $?" if $? != 0;
}
die "no certificate/key file" unless $pub_key;
for my $kof (@ARGV){
    open my $ko, '<', $kof or die "open $kof: $!\n";
    seek $ko, -4096, 2 or die "seek: $!";
    read $ko, my $d, 4096 or die "read: $!";
    my ($algo, $hash, $type, $signer_len, $key_id_len, $sig_len, $magic) =
        unpack 'C5x3Na*', substr $d, -40;
    die "no signature in $kof"
        unless $magic eq "~Module signature appended~\n";
    die "this script only knows about PKCS#7 signatures"
        unless $type[$type] eq 'PKCS7';

    my $hash = gethash substr $d, - 40 - $sig_len, $sig_len;
    die "hash not found" unless $hash;

    my ($alg, $vhash) = gethash
        through [qw(openssl rsautl -verify -pubin -inkey), $pub_key],
            $hash;

    seek $ko, 0, 0 or die "seek: $!";
    read $ko, my $d, (-s $ko) - $sig_len - 40 or die "read: $!";
    use Digest::SHA;
    my $fhash = new Digest::SHA($alg)->add($d)->digest;

    if($fhash eq $vhash){
        print "OK $kof\n";
    }else{
        print "**FAIL** $kof\n";
        $r = 1;
        warn 'orig=', unpack('H*', $vhash), "\n";
        warn 'file=', unpack('H*', $fhash), "\n";
    }
}
exit $r;
Mosvy
quelle