#!/usr/bin/perl # # Calculate the digest of the kernel module # It will strip kernel modules signature before calculation. # # Based on modsign-verify, written by Michal Marek # Authors: # Gary Lin # Joey Lee # my $USAGE = "Usage: modhash [-v] [-q] [-d ] \n"; use strict; use warnings; use IPC::Open2; use Getopt::Long; use File::Temp qw(tempfile); my $verbose = 1; my $dgst = "sha256"; GetOptions( "d=s" => \$dgst, "q|quiet" => sub { $verbose-- if $verbose; }, "v|verbose" => sub { $verbose++; }, "h|help" => sub { print $USAGE; 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); } my $module_name = shift(@ARGV); if ($dgst ne "sha" and $dgst ne "sha1" and $dgst ne "sha256" and $dgst ne "sha384" and $dgst ne "sha512") { die("unsupported algorithm: $dgst"); } # # 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; } 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; if ($module_len < $magic_len) { die "Module size too short\n"; } 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 (substr($module, -$magic_len) eq $magic_number) { $module = substr($module, 0, $module_len - $magic_len); $module_len -= $magic_len; 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 ($id_type == 1) { if (unpack("n", $signature) == $sig_len - 2) { verbose ("signed module (X.509)\n"); } else { die "Invalid signature format\n"; } if ($algo != 1) { die "Unsupported signature algorithm\n"; } $signature = substr($signature, 2); my $key_id = eat($key_len); my $name = eat($name_len); } elsif ($id_type == 2) { verbose ("signed module (PKCS#7)\n"); } } else { verbose ("unsigned module\n"); } verbose("Hash algorithm: $dgst\n"); my $digest = openssl_pipe($module, "openssl dgst -$dgst"); $digest =~ s/\(stdin\)= //; print "$module_name: $digest"