Skip to main content
1 of 4
user avatar
user avatar

Yes, that's possible, but it's quite involved.

First you have to extract the module signature -- you can use the extract-module.sig.pl script from the kernel source for that:

$ 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] 

First, you have to extract the certificate and public key from the kernel; you can use the extract-sys-certs.pl script for that:

$ 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 

You can also extract the public key from the certs/signing_key.x509 or certs/signing_key.pem from the linux kernel's build directory.

Having done that, you have all the data you need in /tmp/pubkey and /tmp/cert.x509 and can continue with the dozen or so steps necessary to verify a PKCS#7 signature.

You can look at this blog post for the whole recipe.


I've tried to put the whole process (except for the extract-certs.pl step) in a perl script.

You can use it like this:

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

YMMV. I've only tried this with a custom build kernel using sha512 signatures. This should of course much better done by using the openssl libraries directly, instead of kludging together slow and fragile openssl x509 and openssl asn1parse invocations.

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 PKCS7 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){ printf "OK %s\n", $kof; }else{ printf "FAIL %s\n", $kof; flush STDOUT; $r = 1; warn 'orig=', unpack('H*', $vhash), "\n"; warn 'file=', unpack('H*', $fhash), "\n"; } } exit $r; 
user313992