Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

MODSIGN: perlify sign-file and merge in x509keyid

Turn sign-file into perl and merge in x509keyid. The latter doesn't
need to be a separate script as it doesn't actually need to work out the
SHA1 sum of the X.509 certificate itself, since it can get that from the
X.509 certificate.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

David Howells and committed by
Linus Torvalds
b37d1bfb 9e781440

+399 -346
+1 -1
Makefile
··· 723 723 MODSECKEY = ./signing_key.priv 724 724 MODPUBKEY = ./signing_key.x509 725 725 export MODPUBKEY 726 - mod_sign_cmd = sh $(srctree)/scripts/sign-file $(MODSECKEY) $(MODPUBKEY) $(srctree)/scripts/x509keyid 726 + mod_sign_cmd = perl $(srctree)/scripts/sign-file $(MODSECKEY) $(MODPUBKEY) 727 727 else 728 728 mod_sign_cmd = true 729 729 endif
+398 -77
scripts/sign-file
··· 1 - #!/bin/bash 1 + #!/usr/bin/perl -w 2 2 # 3 3 # Sign a module file using the given key. 4 4 # 5 - # Format: sign-file <key> <x509> <keyid-script> <module> 5 + # Format: 6 6 # 7 + # ./scripts/sign-file [-v] <key> <x509> <module> [<dest>] 8 + # 9 + # 10 + use strict; 11 + use FileHandle; 12 + use IPC::Open2; 7 13 8 - scripts=`dirname $0` 14 + my $verbose = 0; 15 + if ($#ARGV >= 0 && $ARGV[0] eq "-v") { 16 + $verbose = 1; 17 + shift; 18 + } 9 19 10 - CONFIG_MODULE_SIG_SHA512=y 11 - if [ -r .config ] 12 - then 13 - . ./.config 14 - fi 20 + die "Format: ./scripts/sign-file [-v] <key> <x509> <module> [<dest>]\n" 21 + if ($#ARGV != 2 && $#ARGV != 3); 15 22 16 - key="$1" 17 - x509="$2" 18 - keyid_script="$3" 19 - mod="$4" 23 + my $private_key = $ARGV[0]; 24 + my $x509 = $ARGV[1]; 25 + my $module = $ARGV[2]; 26 + my $dest = ($#ARGV == 3) ? $ARGV[3] : $ARGV[2] . "~"; 20 27 21 - if [ ! -r "$key" ] 22 - then 23 - echo "Can't read private key" >&2 24 - exit 2 25 - fi 28 + die "Can't read private key\n" unless (-r $private_key); 29 + die "Can't read X.509 certificate\n" unless (-r $x509); 30 + die "Can't read module\n" unless (-r $module); 26 31 27 - if [ ! -r "$x509" ] 28 - then 29 - echo "Can't read X.509 certificate" >&2 30 - exit 2 31 - fi 32 + # 33 + # Read the kernel configuration 34 + # 35 + my %config = ( 36 + CONFIG_MODULE_SIG_SHA512 => 1 37 + ); 38 + 39 + if (-r ".config") { 40 + open(FD, "<.config") || die ".config"; 41 + while (<FD>) { 42 + if ($_ =~ /^(CONFIG_.*)=[ym]/) { 43 + $config{$1} = 1; 44 + } 45 + } 46 + close(FD); 47 + } 48 + 49 + # 50 + # Function to read the contents of a file into a variable. 51 + # 52 + sub read_file($) 53 + { 54 + my ($file) = @_; 55 + my $contents; 56 + my $len; 57 + 58 + open(FD, "<$file") || die $file; 59 + binmode FD; 60 + my @st = stat(FD); 61 + die $file if (!@st); 62 + $len = read(FD, $contents, $st[7]) || die $file; 63 + close(FD) || die $file; 64 + die "$file: Wanted length ", $st[7], ", got ", $len, "\n" 65 + if ($len != $st[7]); 66 + return $contents; 67 + } 68 + 69 + ############################################################################### 70 + # 71 + # First of all, we have to parse the X.509 certificate to find certain details 72 + # about it. 73 + # 74 + # We read the DER-encoded X509 certificate and parse it to extract the Subject 75 + # name and Subject Key Identifier. Theis provides the data we need to build 76 + # the certificate identifier. 77 + # 78 + # The signer's name part of the identifier is fabricated from the commonName, 79 + # the organizationName or the emailAddress components of the X.509 subject 80 + # name. 81 + # 82 + # The subject key ID is used to select which of that signer's certificates 83 + # we're intending to use to sign the module. 84 + # 85 + ############################################################################### 86 + my $x509_certificate = read_file($x509); 87 + 88 + my $UNIV = 0 << 6; 89 + my $APPL = 1 << 6; 90 + my $CONT = 2 << 6; 91 + my $PRIV = 3 << 6; 92 + 93 + my $CONS = 0x20; 94 + 95 + my $BOOLEAN = 0x01; 96 + my $INTEGER = 0x02; 97 + my $BIT_STRING = 0x03; 98 + my $OCTET_STRING = 0x04; 99 + my $NULL = 0x05; 100 + my $OBJ_ID = 0x06; 101 + my $UTF8String = 0x0c; 102 + my $SEQUENCE = 0x10; 103 + my $SET = 0x11; 104 + my $UTCTime = 0x17; 105 + my $GeneralizedTime = 0x18; 106 + 107 + my %OIDs = ( 108 + pack("CCC", 85, 4, 3) => "commonName", 109 + pack("CCC", 85, 4, 6) => "countryName", 110 + pack("CCC", 85, 4, 10) => "organizationName", 111 + pack("CCC", 85, 4, 11) => "organizationUnitName", 112 + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption", 113 + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption", 114 + pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress", 115 + pack("CCC", 85, 29, 35) => "authorityKeyIdentifier", 116 + pack("CCC", 85, 29, 14) => "subjectKeyIdentifier", 117 + pack("CCC", 85, 29, 19) => "basicConstraints" 118 + ); 119 + 120 + ############################################################################### 121 + # 122 + # Extract an ASN.1 element from a string and return information about it. 123 + # 124 + ############################################################################### 125 + sub asn1_extract($$@) 126 + { 127 + my ($cursor, $expected_tag, $optional) = @_; 128 + 129 + return [ -1 ] 130 + if ($cursor->[1] == 0 && $optional); 131 + 132 + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n" 133 + if ($cursor->[1] < 2); 134 + 135 + my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2)); 136 + 137 + if ($expected_tag != -1 && $tag != $expected_tag) { 138 + return [ -1 ] 139 + if ($optional); 140 + die $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag, 141 + " not ", $expected_tag, ")\n"; 142 + } 143 + 144 + $cursor->[0] += 2; 145 + $cursor->[1] -= 2; 146 + 147 + die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n" 148 + if (($tag & 0x1f) == 0x1f); 149 + die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n" 150 + if ($len == 0x80); 151 + 152 + if ($len > 0x80) { 153 + my $l = $len - 0x80; 154 + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n" 155 + if ($cursor->[1] < $l); 156 + 157 + if ($l == 0x1) { 158 + $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)); 159 + } elsif ($l = 0x2) { 160 + $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2)); 161 + } elsif ($l = 0x3) { 162 + $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16; 163 + $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2)); 164 + } elsif ($l = 0x4) { 165 + $len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4)); 166 + } else { 167 + die $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n"; 168 + } 169 + 170 + $cursor->[0] += $l; 171 + $cursor->[1] -= $l; 172 + } 173 + 174 + die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n" 175 + if ($cursor->[1] < $len); 176 + 177 + my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ]; 178 + $cursor->[0] += $len; 179 + $cursor->[1] -= $len; 180 + 181 + return $ret; 182 + } 183 + 184 + ############################################################################### 185 + # 186 + # Retrieve the data referred to by a cursor 187 + # 188 + ############################################################################### 189 + sub asn1_retrieve($) 190 + { 191 + my ($cursor) = @_; 192 + my ($offset, $len, $data) = @$cursor; 193 + return substr($$data, $offset, $len); 194 + } 195 + 196 + ############################################################################### 197 + # 198 + # Roughly parse the X.509 certificate 199 + # 200 + ############################################################################### 201 + my $cursor = [ 0, length($x509_certificate), \$x509_certificate ]; 202 + 203 + my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE); 204 + my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE); 205 + my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1); 206 + my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER); 207 + my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 208 + my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 209 + my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 210 + my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 211 + my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 212 + my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1); 213 + my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1); 214 + my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1); 215 + 216 + my $subject_key_id = (); 217 + my $authority_key_id = (); 218 + 219 + # 220 + # Parse the extension list 221 + # 222 + if ($extension_list->[0] != -1) { 223 + my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE); 224 + 225 + while ($extensions->[1]->[1] > 0) { 226 + my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE); 227 + my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID); 228 + my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1); 229 + my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING); 230 + 231 + my $raw_oid = asn1_retrieve($x_oid->[1]); 232 + next if (!exists($OIDs{$raw_oid})); 233 + my $x_type = $OIDs{$raw_oid}; 234 + 235 + my $raw_value = asn1_retrieve($x_val->[1]); 236 + 237 + if ($x_type eq "subjectKeyIdentifier") { 238 + my $vcursor = [ 0, length($raw_value), \$raw_value ]; 239 + 240 + $subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING); 241 + } 242 + } 243 + } 244 + 245 + ############################################################################### 246 + # 247 + # Determine what we're going to use as the signer's name. In order of 248 + # preference, take one of: commonName, organizationName or emailAddress. 249 + # 250 + ############################################################################### 251 + my $org = ""; 252 + my $cn = ""; 253 + my $email = ""; 254 + 255 + while ($subject->[1]->[1] > 0) { 256 + my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET); 257 + my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE); 258 + my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID); 259 + my $n_val = asn1_extract($attr->[1], -1); 260 + 261 + my $raw_oid = asn1_retrieve($n_oid->[1]); 262 + next if (!exists($OIDs{$raw_oid})); 263 + my $n_type = $OIDs{$raw_oid}; 264 + 265 + my $raw_value = asn1_retrieve($n_val->[1]); 266 + 267 + if ($n_type eq "organizationName") { 268 + $org = $raw_value; 269 + } elsif ($n_type eq "commonName") { 270 + $cn = $raw_value; 271 + } elsif ($n_type eq "emailAddress") { 272 + $email = $raw_value; 273 + } 274 + } 275 + 276 + my $signers_name = $email; 277 + 278 + if ($org && $cn) { 279 + # Don't use the organizationName if the commonName repeats it 280 + if (length($org) <= length($cn) && 281 + substr($cn, 0, length($org)) eq $org) { 282 + $signers_name = $cn; 283 + goto got_id_name; 284 + } 285 + 286 + # Or a signifcant chunk of it 287 + if (length($org) >= 7 && 288 + length($cn) >= 7 && 289 + substr($cn, 0, 7) eq substr($org, 0, 7)) { 290 + $signers_name = $cn; 291 + goto got_id_name; 292 + } 293 + 294 + $signers_name = $org . ": " . $cn; 295 + } elsif ($org) { 296 + $signers_name = $org; 297 + } elsif ($cn) { 298 + $signers_name = $cn; 299 + } 300 + 301 + got_id_name: 302 + 303 + die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n" 304 + if (!$subject_key_id); 305 + 306 + my $key_identifier = asn1_retrieve($subject_key_id->[1]); 307 + 308 + ############################################################################### 309 + # 310 + # Create and attach the module signature 311 + # 312 + ############################################################################### 32 313 33 314 # 34 315 # Signature parameters 35 316 # 36 - algo=1 # Public-key crypto algorithm: RSA 37 - hash= # Digest algorithm 38 - id_type=1 # Identifier type: X.509 317 + my $algo = 1; # Public-key crypto algorithm: RSA 318 + my $hash = 0; # Digest algorithm 319 + my $id_type = 1; # Identifier type: X.509 39 320 40 321 # 41 322 # Digest the data 42 323 # 43 - dgst= 44 - if [ "$CONFIG_MODULE_SIG_SHA1" = "y" ] 45 - then 46 - prologue="0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14" 47 - dgst=-sha1 48 - hash=2 49 - elif [ "$CONFIG_MODULE_SIG_SHA224" = "y" ] 50 - then 51 - prologue="0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1C" 52 - dgst=-sha224 53 - hash=7 54 - elif [ "$CONFIG_MODULE_SIG_SHA256" = "y" ] 55 - then 56 - prologue="0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20" 57 - dgst=-sha256 58 - hash=4 59 - elif [ "$CONFIG_MODULE_SIG_SHA384" = "y" ] 60 - then 61 - prologue="0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30" 62 - dgst=-sha384 63 - hash=5 64 - elif [ "$CONFIG_MODULE_SIG_SHA512" = "y" ] 65 - then 66 - prologue="0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40" 67 - dgst=-sha512 68 - hash=6 69 - else 70 - echo "$0: Can't determine hash algorithm" >&2 71 - exit 2 72 - fi 324 + my ($dgst, $prologue) = (); 325 + if (exists $config{"CONFIG_MODULE_SIG_SHA1"}) { 326 + $prologue = pack("C*", 327 + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 328 + 0x2B, 0x0E, 0x03, 0x02, 0x1A, 329 + 0x05, 0x00, 0x04, 0x14); 330 + $dgst = "-sha1"; 331 + $hash = 2; 332 + } elsif (exists $config{"CONFIG_MODULE_SIG_SHA224"}) { 333 + $prologue = pack("C*", 334 + 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 335 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 336 + 0x05, 0x00, 0x04, 0x1C); 337 + $dgst = "-sha224"; 338 + $hash = 7; 339 + } elsif (exists $config{"CONFIG_MODULE_SIG_SHA256"}) { 340 + $prologue = pack("C*", 341 + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 342 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 343 + 0x05, 0x00, 0x04, 0x20); 344 + $dgst = "-sha256"; 345 + $hash = 4; 346 + } elsif (exists $config{"CONFIG_MODULE_SIG_SHA384"}) { 347 + $prologue = pack("C*", 348 + 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 349 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 350 + 0x05, 0x00, 0x04, 0x30); 351 + $dgst = "-sha384"; 352 + $hash = 5; 353 + } elsif (exists $config{"CONFIG_MODULE_SIG_SHA512"}) { 354 + $prologue = pack("C*", 355 + 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 356 + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 357 + 0x05, 0x00, 0x04, 0x40); 358 + $dgst = "-sha512"; 359 + $hash = 6; 360 + } else { 361 + die "Can't determine hash algorithm"; 362 + } 73 363 74 - ( 75 - perl -e "binmode STDOUT; print pack(\"C*\", $prologue)" || exit $? 76 - openssl dgst $dgst -binary $mod || exit $? 77 - ) >$mod.dig || exit $? 364 + # 365 + # Generate the digest and read from openssl's stdout 366 + # 367 + my $digest; 368 + $digest = readpipe("openssl dgst $dgst -binary $module") || die "openssl dgst"; 78 369 79 370 # 80 371 # Generate the binary signature, which will be just the integer that comprises 81 372 # the signature with no metadata attached. 82 373 # 83 - openssl rsautl -sign -inkey $key -keyform PEM -in $mod.dig -out $mod.sig || exit $? 84 - siglen=`stat -c %s $mod.sig` 374 + my $pid; 375 + $pid = open2(*read_from, *write_to, 376 + "openssl rsautl -sign -inkey $private_key -keyform PEM") || 377 + die "openssl rsautl"; 378 + binmode write_to; 379 + print write_to $prologue . $digest || die "pipe to openssl rsautl"; 380 + close(write_to) || die "pipe to openssl rsautl"; 85 381 86 - SIGNER="`perl $keyid_script $x509 signer-name`" 87 - KEYID="`perl $keyid_script $x509 keyid`" 88 - keyidlen=$(echo -n "$KEYID" | wc -c) 89 - signerlen=$(echo -n "$SIGNER" | wc -c) 382 + binmode read_from; 383 + my $signature; 384 + read(read_from, $signature, 4096) || die "pipe from openssl rsautl"; 385 + close(read_from) || die "pipe from openssl rsautl"; 386 + $signature = pack("n", length($signature)) . $signature, 387 + 388 + waitpid($pid, 0) || die; 389 + die "openssl rsautl died: $?" if ($? >> 8); 90 390 91 391 # 92 392 # Build the signed binary 93 393 # 94 - ( 95 - cat $mod || exit $? 96 - echo '~Module signature appended~' || exit $? 97 - echo -n "$SIGNER" || exit $? 98 - echo -n "$KEYID" || exit $? 394 + my $unsigned_module = read_file($module); 99 395 100 - # Preface each signature integer with a 2-byte BE length 101 - perl -e "binmode STDOUT; print pack(\"n\", $siglen)" || exit $? 102 - cat $mod.sig || exit $? 396 + my $magic_number = "~Module signature appended~\n"; 103 397 104 - # Generate the information block 105 - perl -e "binmode STDOUT; print pack(\"CCCCCxxxN\", $algo, $hash, $id_type, $signerlen, $keyidlen, $siglen + 2)" || exit $? 106 - ) >$mod~ || exit $? 398 + my $info = pack("CCCCCxxxN", 399 + $algo, $hash, $id_type, 400 + length($signers_name), 401 + length($key_identifier), 402 + length($signature)); 107 403 108 - mv $mod~ $mod || exit $? 404 + if ($verbose) { 405 + print "Size of unsigned module: ", length($unsigned_module), "\n"; 406 + print "Size of magic number : ", length($magic_number), "\n"; 407 + print "Size of signer's name : ", length($signers_name), "\n"; 408 + print "Size of key identifier : ", length($key_identifier), "\n"; 409 + print "Size of signature : ", length($signature), "\n"; 410 + print "Size of informaton : ", length($info), "\n"; 411 + print "Signer's name : '", $signers_name, "'\n"; 412 + print "Digest : $dgst\n"; 413 + } 414 + 415 + open(FD, ">$dest") || die $dest; 416 + binmode FD; 417 + print FD 418 + $unsigned_module, 419 + $magic_number, 420 + $signers_name, 421 + $key_identifier, 422 + $signature, 423 + $info 424 + ; 425 + close FD || die $dest; 426 + 427 + if ($#ARGV != 3) { 428 + rename($dest, $module) || die $module; 429 + }
-268
scripts/x509keyid
··· 1 - #!/usr/bin/perl -w 2 - # 3 - # Generate an identifier from an X.509 certificate that can be placed in a 4 - # module signature to indentify the key to use. 5 - # 6 - # Format: 7 - # 8 - # ./scripts/x509keyid <x509-cert> <signer's-name> <key-id> 9 - # 10 - # We read the DER-encoded X509 certificate and parse it to extract the Subject 11 - # name and Subject Key Identifier. The provide the data we need to build the 12 - # certificate identifier. 13 - # 14 - # The signer's name part of the identifier is fabricated from the commonName, 15 - # the organizationName or the emailAddress components of the X.509 subject 16 - # name and written to the second named file. 17 - # 18 - # The subject key ID to select which of that signer's certificates we're 19 - # intending to use to sign the module is written to the third named file. 20 - # 21 - use strict; 22 - 23 - my $raw_data; 24 - 25 - die "Need a filename [keyid|signer-name]\n" if ($#ARGV != 1); 26 - 27 - my $src = $ARGV[0]; 28 - 29 - open(FD, "<$src") || die $src; 30 - binmode FD; 31 - my @st = stat(FD); 32 - die $src if (!@st); 33 - read(FD, $raw_data, $st[7]) || die $src; 34 - close(FD); 35 - 36 - my $UNIV = 0 << 6; 37 - my $APPL = 1 << 6; 38 - my $CONT = 2 << 6; 39 - my $PRIV = 3 << 6; 40 - 41 - my $CONS = 0x20; 42 - 43 - my $BOOLEAN = 0x01; 44 - my $INTEGER = 0x02; 45 - my $BIT_STRING = 0x03; 46 - my $OCTET_STRING = 0x04; 47 - my $NULL = 0x05; 48 - my $OBJ_ID = 0x06; 49 - my $UTF8String = 0x0c; 50 - my $SEQUENCE = 0x10; 51 - my $SET = 0x11; 52 - my $UTCTime = 0x17; 53 - my $GeneralizedTime = 0x18; 54 - 55 - my %OIDs = ( 56 - pack("CCC", 85, 4, 3) => "commonName", 57 - pack("CCC", 85, 4, 6) => "countryName", 58 - pack("CCC", 85, 4, 10) => "organizationName", 59 - pack("CCC", 85, 4, 11) => "organizationUnitName", 60 - pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption", 61 - pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption", 62 - pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress", 63 - pack("CCC", 85, 29, 35) => "authorityKeyIdentifier", 64 - pack("CCC", 85, 29, 14) => "subjectKeyIdentifier", 65 - pack("CCC", 85, 29, 19) => "basicConstraints" 66 - ); 67 - 68 - ############################################################################### 69 - # 70 - # Extract an ASN.1 element from a string and return information about it. 71 - # 72 - ############################################################################### 73 - sub asn1_extract($$@) 74 - { 75 - my ($cursor, $expected_tag, $optional) = @_; 76 - 77 - return [ -1 ] 78 - if ($cursor->[1] == 0 && $optional); 79 - 80 - die $src, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n" 81 - if ($cursor->[1] < 2); 82 - 83 - my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2)); 84 - 85 - if ($expected_tag != -1 && $tag != $expected_tag) { 86 - return [ -1 ] 87 - if ($optional); 88 - die $src, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag, 89 - " not ", $expected_tag, ")\n"; 90 - } 91 - 92 - $cursor->[0] += 2; 93 - $cursor->[1] -= 2; 94 - 95 - die $src, ": ", $cursor->[0], ": ASN.1 long tag\n" 96 - if (($tag & 0x1f) == 0x1f); 97 - die $src, ": ", $cursor->[0], ": ASN.1 indefinite length\n" 98 - if ($len == 0x80); 99 - 100 - if ($len > 0x80) { 101 - my $l = $len - 0x80; 102 - die $src, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n" 103 - if ($cursor->[1] < $l); 104 - 105 - if ($l == 0x1) { 106 - $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)); 107 - } elsif ($l = 0x2) { 108 - $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2)); 109 - } elsif ($l = 0x3) { 110 - $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16; 111 - $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2)); 112 - } elsif ($l = 0x4) { 113 - $len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4)); 114 - } else { 115 - die $src, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n"; 116 - } 117 - 118 - $cursor->[0] += $l; 119 - $cursor->[1] -= $l; 120 - } 121 - 122 - die $src, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n" 123 - if ($cursor->[1] < $len); 124 - 125 - my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ]; 126 - $cursor->[0] += $len; 127 - $cursor->[1] -= $len; 128 - 129 - return $ret; 130 - } 131 - 132 - ############################################################################### 133 - # 134 - # Retrieve the data referred to by a cursor 135 - # 136 - ############################################################################### 137 - sub asn1_retrieve($) 138 - { 139 - my ($cursor) = @_; 140 - my ($offset, $len, $data) = @$cursor; 141 - return substr($$data, $offset, $len); 142 - } 143 - 144 - ############################################################################### 145 - # 146 - # Roughly parse the X.509 certificate 147 - # 148 - ############################################################################### 149 - my $cursor = [ 0, length($raw_data), \$raw_data ]; 150 - 151 - my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE); 152 - my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE); 153 - my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1); 154 - my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER); 155 - my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 156 - my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 157 - my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 158 - my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 159 - my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE); 160 - my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1); 161 - my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1); 162 - my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1); 163 - 164 - my $subject_key_id = (); 165 - my $authority_key_id = (); 166 - 167 - # 168 - # Parse the extension list 169 - # 170 - if ($extension_list->[0] != -1) { 171 - my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE); 172 - 173 - while ($extensions->[1]->[1] > 0) { 174 - my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE); 175 - my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID); 176 - my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1); 177 - my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING); 178 - 179 - my $raw_oid = asn1_retrieve($x_oid->[1]); 180 - next if (!exists($OIDs{$raw_oid})); 181 - my $x_type = $OIDs{$raw_oid}; 182 - 183 - my $raw_value = asn1_retrieve($x_val->[1]); 184 - 185 - if ($x_type eq "subjectKeyIdentifier") { 186 - my $vcursor = [ 0, length($raw_value), \$raw_value ]; 187 - 188 - $subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING); 189 - } 190 - } 191 - } 192 - 193 - ############################################################################### 194 - # 195 - # Determine what we're going to use as the signer's name. In order of 196 - # preference, take one of: commonName, organizationName or emailAddress. 197 - # 198 - ############################################################################### 199 - my $org = ""; 200 - my $cn = ""; 201 - my $email = ""; 202 - 203 - while ($subject->[1]->[1] > 0) { 204 - my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET); 205 - my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE); 206 - my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID); 207 - my $n_val = asn1_extract($attr->[1], -1); 208 - 209 - my $raw_oid = asn1_retrieve($n_oid->[1]); 210 - next if (!exists($OIDs{$raw_oid})); 211 - my $n_type = $OIDs{$raw_oid}; 212 - 213 - my $raw_value = asn1_retrieve($n_val->[1]); 214 - 215 - if ($n_type eq "organizationName") { 216 - $org = $raw_value; 217 - } elsif ($n_type eq "commonName") { 218 - $cn = $raw_value; 219 - } elsif ($n_type eq "emailAddress") { 220 - $email = $raw_value; 221 - } 222 - } 223 - 224 - my $id_name = $email; 225 - 226 - if ($org && $cn) { 227 - # Don't use the organizationName if the commonName repeats it 228 - if (length($org) <= length($cn) && 229 - substr($cn, 0, length($org)) eq $org) { 230 - $id_name = $cn; 231 - goto got_id_name; 232 - } 233 - 234 - # Or a signifcant chunk of it 235 - if (length($org) >= 7 && 236 - length($cn) >= 7 && 237 - substr($cn, 0, 7) eq substr($org, 0, 7)) { 238 - $id_name = $cn; 239 - goto got_id_name; 240 - } 241 - 242 - $id_name = $org . ": " . $cn; 243 - } elsif ($org) { 244 - $id_name = $org; 245 - } elsif ($cn) { 246 - $id_name = $cn; 247 - } 248 - 249 - got_id_name: 250 - 251 - ############################################################################### 252 - # 253 - # Output the signer's name and the key identifier that we're going to include 254 - # in module signatures. 255 - # 256 - ############################################################################### 257 - die $src, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n" 258 - if (!$subject_key_id); 259 - 260 - my $id_key_id = asn1_retrieve($subject_key_id->[1]); 261 - 262 - if ($ARGV[1] eq "signer-name") { 263 - print $id_name; 264 - } elsif ($ARGV[1] eq "keyid") { 265 - print $id_key_id; 266 - } else { 267 - die "Unknown arg"; 268 - }