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]
Second, 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
files from the linux kernel's build directory.
Having done that, you have all the data you need in /tmp/modsig
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 built kernel using sha512 signatures. This should be of course much better done by using the openssl libraries directly, instead of kludging together slow and fragile openssl x509
, asn1parse
and rsautl
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 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;
Best Answer
It is possible to find all
ioctl
definitions by using the kernel source,vim
andctags
program. I just use the tools from my toolbox, you can use others - maybe IDE like Netbeans or Eclipse. The essence is the same.Demonstration: (you can download this
.gif
file and open it by GIMP for example. Then all frames will be available without time limit)Explanation:
1) I think, we are not have other ways, except digging into the kernel source. The different manuals exists, check
apropos ioctl
, but they are either incomplete or outdated. So, the most reliable way is looking at the source (if you don't have documentation from the module creator). The user process usesioctl
system call to send different commands to the kernel module. The module has definition and comments for each ioctl command. Our task is to find them.Note start
You should use the same kernel source version as the
glibc
uses, not current kernel on your system, becauseglibc
headers are used in the compilation of user programs. Thus, if you will use the last kernel source (or other, different from theglibc
's), the result could be wrong.To find the
glibc
headers version on Ubuntu do:Result:
Download the kernel source code with Ubuntu patches from repository into the current directory (on Ubuntu):
Note end
2) ioctl commands are defined in the module header file - some_name.h. They are just numbers, which can be calculated by special macro:
From documentation:
But some
ioctl
definitions are not following this prescription and are not different from usual macros, like in the case oftty
driver:so, we can't just
grep
header files.3) The defined ioctl commands are used in the module source code file - some_name.c. There are special function - handler, which is called on each ioctl request. It takes the ioctl number as argument and switches the program execution to the corresponding branch, like:
4) This ioctl handler is stored in the
.unlocked_ioctl
field of thefile_operations struct
:Therefore, we can find this assignment in the source code, jump to the handler function definition by
ctags
program and look at ioctl branches in theswitch
construction. Then jump further, to the specific command in the header file and look at the definition and comments.