security/nss/cmd/smimetools/smime

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rwxr-xr-x

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 #!/usr/local/bin/perl
michael@0 2
michael@0 3 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 6
michael@0 7 #
michael@0 8 # smime.pl - frontend for S/MIME message generation and parsing
michael@0 9 #
michael@0 10
michael@0 11 use Getopt::Std;
michael@0 12
michael@0 13 @boundarychars = ( "0" .. "9", "A" .. "F" );
michael@0 14
michael@0 15 # path to cmsutil
michael@0 16 $cmsutilpath = "cmsutil";
michael@0 17
michael@0 18 #
michael@0 19 # Thanks to Gisle Aas <gisle@aas.no> for the base64 functions
michael@0 20 # originally taken from MIME-Base64-2.11 at www.cpan.org
michael@0 21 #
michael@0 22 sub encode_base64($)
michael@0 23 {
michael@0 24 my $res = "";
michael@0 25 pos($_[0]) = 0; # ensure start at the beginning
michael@0 26 while ($_[0] =~ /(.{1,45})/gs) {
michael@0 27 $res .= substr(pack('u', $1), 1); # get rid of length byte after packing
michael@0 28 chop($res);
michael@0 29 }
michael@0 30 $res =~ tr|` -_|AA-Za-z0-9+/|;
michael@0 31 # fix padding at the end
michael@0 32 my $padding = (3 - length($_[0]) % 3) % 3;
michael@0 33 $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
michael@0 34 # break encoded string into lines of no more than 76 characters each
michael@0 35 $res =~ s/(.{1,76})/$1\n/g;
michael@0 36 $res;
michael@0 37 }
michael@0 38
michael@0 39 sub decode_base64($)
michael@0 40 {
michael@0 41 local($^W) = 0; # unpack("u",...) gives bogus warning in 5.00[123]
michael@0 42
michael@0 43 my $str = shift;
michael@0 44 my $res = "";
michael@0 45
michael@0 46 $str =~ tr|A-Za-z0-9+=/||cd; # remove non-base64 chars
michael@0 47 if (length($str) % 4) {
michael@0 48 require Carp;
michael@0 49 Carp::carp("Length of base64 data not a multiple of 4")
michael@0 50 }
michael@0 51 $str =~ s/=+$//; # remove padding
michael@0 52 $str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format
michael@0 53 while ($str =~ /(.{1,60})/gs) {
michael@0 54 my $len = chr(32 + length($1)*3/4); # compute length byte
michael@0 55 $res .= unpack("u", $len . $1 ); # uudecode
michael@0 56 }
michael@0 57 $res;
michael@0 58 }
michael@0 59
michael@0 60 #
michael@0 61 # parse headers into a hash
michael@0 62 #
michael@0 63 # %headers = parseheaders($headertext);
michael@0 64 #
michael@0 65 sub parseheaders($)
michael@0 66 {
michael@0 67 my ($headerdata) = @_;
michael@0 68 my $hdr;
michael@0 69 my %hdrhash;
michael@0 70 my $hdrname;
michael@0 71 my $hdrvalue;
michael@0 72 my @hdrvalues;
michael@0 73 my $subhdrname;
michael@0 74 my $subhdrvalue;
michael@0 75
michael@0 76 # the expression in split() correctly handles continuation lines
michael@0 77 foreach $hdr (split(/\n(?=\S)/, $headerdata)) {
michael@0 78 $hdr =~ s/\r*\n\s+/ /g; # collapse continuation lines
michael@0 79 ($hdrname, $hdrvalue) = $hdr =~ m/^(\S+):\s+(.*)$/;
michael@0 80
michael@0 81 # ignore non-headers (or should we die horribly?)
michael@0 82 next unless (defined($hdrname));
michael@0 83 $hdrname =~ tr/A-Z/a-z/; # lowercase the header name
michael@0 84 @hdrvalues = split(/\s*;\s*/, $hdrvalue); # split header values (XXXX quoting)
michael@0 85
michael@0 86 # there is guaranteed to be at least one value
michael@0 87 $hdrvalue = shift @hdrvalues;
michael@0 88 if ($hdrvalue =~ /^\s*\"(.*)\"\s*$/) { # strip quotes if there
michael@0 89 $hdrvalue = $1;
michael@0 90 }
michael@0 91
michael@0 92 $hdrhash{$hdrname}{MAIN} = $hdrvalue;
michael@0 93 # print "XXX $hdrname = $hdrvalue\n";
michael@0 94
michael@0 95 # deal with additional name-value pairs
michael@0 96 foreach $hdrvalue (@hdrvalues) {
michael@0 97 ($subhdrname, $subhdrvalue) = $hdrvalue =~ m/^(\S+)\s*=\s*(.*)$/;
michael@0 98 # ignore non-name-value pairs (or should we die?)
michael@0 99 next unless (defined($subhdrname));
michael@0 100 $subhdrname =~ tr/A-Z/a-z/;
michael@0 101 if ($subhdrvalue =~ /^\s*\"(.*)\"\s*$/) { # strip quotes if there
michael@0 102 $subhdrvalue = $1;
michael@0 103 }
michael@0 104 $hdrhash{$hdrname}{$subhdrname} = $subhdrvalue;
michael@0 105 }
michael@0 106
michael@0 107 }
michael@0 108 return %hdrhash;
michael@0 109 }
michael@0 110
michael@0 111 #
michael@0 112 # encryptentity($entity, $options) - encrypt an S/MIME entity,
michael@0 113 # creating a new application/pkcs7-smime entity
michael@0 114 #
michael@0 115 # entity - string containing entire S/MIME entity to encrypt
michael@0 116 # options - options for cmsutil
michael@0 117 #
michael@0 118 # this will generate and return a new application/pkcs7-smime entity containing
michael@0 119 # the enveloped input entity.
michael@0 120 #
michael@0 121 sub encryptentity($$)
michael@0 122 {
michael@0 123 my ($entity, $cmsutiloptions) = @_;
michael@0 124 my $out = "";
michael@0 125 my $boundary;
michael@0 126
michael@0 127 $tmpencfile = "/tmp/encryptentity.$$";
michael@0 128
michael@0 129 #
michael@0 130 # generate a random boundary string
michael@0 131 #
michael@0 132 $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
michael@0 133
michael@0 134 #
michael@0 135 # tell cmsutil to generate a enveloped CMS message using our data
michael@0 136 #
michael@0 137 open(CMS, "|$cmsutilpath -E $cmsutiloptions -o $tmpencfile") or die "ERROR: cannot pipe to cmsutil";
michael@0 138 print CMS $entity;
michael@0 139 unless (close(CMS)) {
michael@0 140 print STDERR "ERROR: encryption failed.\n";
michael@0 141 unlink($tmpsigfile);
michael@0 142 exit 1;
michael@0 143 }
michael@0 144
michael@0 145 $out = "Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m\n";
michael@0 146 $out .= "Content-Transfer-Encoding: base64\n";
michael@0 147 $out .= "Content-Disposition: attachment; filename=smime.p7m\n";
michael@0 148 $out .= "\n"; # end of entity header
michael@0 149
michael@0 150 open (ENC, $tmpencfile) or die "ERROR: cannot find newly generated encrypted content";
michael@0 151 local($/) = undef; # slurp whole file
michael@0 152 $out .= encode_base64(<ENC>), "\n"; # entity body is base64-encoded CMS message
michael@0 153 close(ENC);
michael@0 154
michael@0 155 unlink($tmpencfile);
michael@0 156
michael@0 157 $out;
michael@0 158 }
michael@0 159
michael@0 160 #
michael@0 161 # signentity($entity, $options) - sign an S/MIME entity
michael@0 162 #
michael@0 163 # entity - string containing entire S/MIME entity to sign
michael@0 164 # options - options for cmsutil
michael@0 165 #
michael@0 166 # this will generate and return a new multipart/signed entity consisting
michael@0 167 # of the canonicalized original content, plus a signature block.
michael@0 168 #
michael@0 169 sub signentity($$)
michael@0 170 {
michael@0 171 my ($entity, $cmsutiloptions) = @_;
michael@0 172 my $out = "";
michael@0 173 my $boundary;
michael@0 174
michael@0 175 $tmpsigfile = "/tmp/signentity.$$";
michael@0 176
michael@0 177 #
michael@0 178 # generate a random boundary string
michael@0 179 #
michael@0 180 $boundary = "------------ms" . join("", @boundarychars[map{rand @boundarychars }( 1 .. 24 )]);
michael@0 181
michael@0 182 #
michael@0 183 # tell cmsutil to generate a signed CMS message using the canonicalized data
michael@0 184 # The signedData has detached content (-T) and includes a signing time attribute (-G)
michael@0 185 #
michael@0 186 # if we do not provide a password on the command line, here's where we would be asked for it
michael@0 187 #
michael@0 188 open(CMS, "|$cmsutilpath -S -T -G $cmsutiloptions -o $tmpsigfile") or die "ERROR: cannot pipe to cmsutil";
michael@0 189 print CMS $entity;
michael@0 190 unless (close(CMS)) {
michael@0 191 print STDERR "ERROR: signature generation failed.\n";
michael@0 192 unlink($tmpsigfile);
michael@0 193 exit 1;
michael@0 194 }
michael@0 195
michael@0 196 open (SIG, $tmpsigfile) or die "ERROR: cannot find newly generated signature";
michael@0 197
michael@0 198 #
michael@0 199 # construct a new multipart/signed MIME entity consisting of the original content and
michael@0 200 # the signature
michael@0 201 #
michael@0 202 # (we assume that cmsutil generates a SHA1 digest)
michael@0 203 $out .= "Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\"; micalg=sha1; boundary=\"${boundary}\"\n";
michael@0 204 $out .= "\n"; # end of entity header
michael@0 205 $out .= "This is a cryptographically signed message in MIME format.\n"; # explanatory comment
michael@0 206 $out .= "\n--${boundary}\n";
michael@0 207 $out .= $entity;
michael@0 208 $out .= "\n--${boundary}\n";
michael@0 209 $out .= "Content-Type: application/pkcs7-signature; name=smime.p7s\n";
michael@0 210 $out .= "Content-Transfer-Encoding: base64\n";
michael@0 211 $out .= "Content-Disposition: attachment; filename=smime.p7s\n";
michael@0 212 $out .= "Content-Description: S/MIME Cryptographic Signature\n";
michael@0 213 $out .= "\n"; # end of signature subentity header
michael@0 214
michael@0 215 local($/) = undef; # slurp whole file
michael@0 216 $out .= encode_base64(<SIG>); # append base64-encoded signature
michael@0 217 $out .= "\n--${boundary}--\n";
michael@0 218
michael@0 219 close(SIG);
michael@0 220 unlink($tmpsigfile);
michael@0 221
michael@0 222 $out;
michael@0 223 }
michael@0 224
michael@0 225 sub usage {
michael@0 226 print STDERR "usage: smime [options]\n";
michael@0 227 print STDERR " options:\n";
michael@0 228 print STDERR " -S nick generate signed message, use certificate named \"nick\"\n";
michael@0 229 print STDERR " -p passwd use \"passwd\" as security module password\n";
michael@0 230 print STDERR " -E rec1[,rec2...] generate encrypted message for recipients\n";
michael@0 231 print STDERR " -D decode a S/MIME message\n";
michael@0 232 print STDERR " -p passwd use \"passwd\" as security module password\n";
michael@0 233 print STDERR " (required for decrypting only)\n";
michael@0 234 print STDERR " -C pathname set pathname of \"cmsutil\"\n";
michael@0 235 print STDERR " -d directory set directory containing certificate db\n";
michael@0 236 print STDERR " (default: ~/.netscape)\n";
michael@0 237 print STDERR "\nWith -S or -E, smime will take a regular RFC822 message or MIME entity\n";
michael@0 238 print STDERR "on stdin and generate a signed or encrypted S/MIME message with the same\n";
michael@0 239 print STDERR "headers and content from it. The output can be used as input to a MTA.\n";
michael@0 240 print STDERR "-D causes smime to strip off all S/MIME layers if possible and output\n";
michael@0 241 print STDERR "the \"inner\" message.\n";
michael@0 242 }
michael@0 243
michael@0 244 #
michael@0 245 # start of main procedures
michael@0 246 #
michael@0 247
michael@0 248 #
michael@0 249 # process command line options
michael@0 250 #
michael@0 251 unless (getopts('S:E:p:d:C:D')) {
michael@0 252 usage();
michael@0 253 exit 1;
michael@0 254 }
michael@0 255
michael@0 256 unless (defined($opt_S) or defined($opt_E) or defined($opt_D)) {
michael@0 257 print STDERR "ERROR: -S and/or -E, or -D must be specified.\n";
michael@0 258 usage();
michael@0 259 exit 1;
michael@0 260 }
michael@0 261
michael@0 262 $signopts = "";
michael@0 263 $encryptopts = "";
michael@0 264 $decodeopts = "";
michael@0 265
michael@0 266 # pass -d option along
michael@0 267 if (defined($opt_d)) {
michael@0 268 $signopts .= "-d \"$opt_d\" ";
michael@0 269 $encryptopts .= "-d \"$opt_d\" ";
michael@0 270 $decodeopts .= "-d \"$opt_d\" ";
michael@0 271 }
michael@0 272
michael@0 273 if (defined($opt_S)) {
michael@0 274 $signopts .= "-N \"$opt_S\" ";
michael@0 275 }
michael@0 276
michael@0 277 if (defined($opt_p)) {
michael@0 278 $signopts .= "-p \"$opt_p\" ";
michael@0 279 $decodeopts .= "-p \"$opt_p\" ";
michael@0 280 }
michael@0 281
michael@0 282 if (defined($opt_E)) {
michael@0 283 @recipients = split(",", $opt_E);
michael@0 284 $encryptopts .= "-r ";
michael@0 285 $encryptopts .= join (" -r ", @recipients);
michael@0 286 }
michael@0 287
michael@0 288 if (defined($opt_C)) {
michael@0 289 $cmsutilpath = $opt_C;
michael@0 290 }
michael@0 291
michael@0 292 #
michael@0 293 # split headers into mime entity headers and RFC822 headers
michael@0 294 # The RFC822 headers are preserved and stay on the outer layer of the message
michael@0 295 #
michael@0 296 $rfc822headers = "";
michael@0 297 $mimeheaders = "";
michael@0 298 $mimebody = "";
michael@0 299 $skippedheaders = "";
michael@0 300 while (<STDIN>) {
michael@0 301 last if (/^$/);
michael@0 302 if (/^content-\S+: /i) {
michael@0 303 $lastref = \$mimeheaders;
michael@0 304 } elsif (/^mime-version: /i) {
michael@0 305 $lastref = \$skippedheaders; # skip it
michael@0 306 } elsif (/^\s/) {
michael@0 307 ;
michael@0 308 } else {
michael@0 309 $lastref = \$rfc822headers;
michael@0 310 }
michael@0 311 $$lastref .= $_;
michael@0 312 }
michael@0 313
michael@0 314 #
michael@0 315 # if there are no MIME entity headers, generate some default ones
michael@0 316 #
michael@0 317 if ($mimeheaders eq "") {
michael@0 318 $mimeheaders .= "Content-Type: text/plain; charset=us-ascii\n";
michael@0 319 $mimeheaders .= "Content-Transfer-Encoding: 7bit\n";
michael@0 320 }
michael@0 321
michael@0 322 #
michael@0 323 # slurp in the entity body
michael@0 324 #
michael@0 325 $saveRS = $/;
michael@0 326 $/ = undef;
michael@0 327 $mimebody = <STDIN>;
michael@0 328 $/ = $saveRS;
michael@0 329 chomp($mimebody);
michael@0 330
michael@0 331 if (defined $opt_D) {
michael@0 332 #
michael@0 333 # decode
michael@0 334 #
michael@0 335 # possible options would be:
michael@0 336 # - strip off only one layer
michael@0 337 # - strip off outer signature (if present)
michael@0 338 # - just print information about the structure of the message
michael@0 339 # - strip n layers, then dump DER of CMS message
michael@0 340
michael@0 341 $layercounter = 1;
michael@0 342
michael@0 343 while (1) {
michael@0 344 %hdrhash = parseheaders($mimeheaders);
michael@0 345 unless (exists($hdrhash{"content-type"}{MAIN})) {
michael@0 346 print STDERR "ERROR: no content type header found in MIME entity\n";
michael@0 347 last; # no content-type - we're done
michael@0 348 }
michael@0 349
michael@0 350 $contenttype = $hdrhash{"content-type"}{MAIN};
michael@0 351 if ($contenttype eq "application/pkcs7-mime") {
michael@0 352 #
michael@0 353 # opaque-signed or enveloped message
michael@0 354 #
michael@0 355 unless (exists($hdrhash{"content-type"}{"smime-type"})) {
michael@0 356 print STDERR "ERROR: no smime-type attribute in application/pkcs7-smime entity.\n";
michael@0 357 last;
michael@0 358 }
michael@0 359 $smimetype = $hdrhash{"content-type"}{"smime-type"};
michael@0 360 if ($smimetype eq "signed-data" or $smimetype eq "enveloped-data") {
michael@0 361 # it's verification or decryption time!
michael@0 362
michael@0 363 # can handle only base64 encoding for now
michael@0 364 # all other encodings are treated as binary (8bit)
michael@0 365 if ($hdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
michael@0 366 $mimebody = decode_base64($mimebody);
michael@0 367 }
michael@0 368
michael@0 369 # if we need to dump the DER, we would do it right here
michael@0 370
michael@0 371 # now write the DER
michael@0 372 $tmpderfile = "/tmp/der.$$";
michael@0 373 open(TMP, ">$tmpderfile") or die "ERROR: cannot write signature data to temporary file";
michael@0 374 print TMP $mimebody;
michael@0 375 unless (close(TMP)) {
michael@0 376 print STDERR "ERROR: writing signature data to temporary file.\n";
michael@0 377 unlink($tmpderfile);
michael@0 378 exit 1;
michael@0 379 }
michael@0 380
michael@0 381 $mimeheaders = "";
michael@0 382 open(TMP, "$cmsutilpath -D $decodeopts -h $layercounter -i $tmpderfile |") or die "ERROR: cannot open pipe to cmsutil";
michael@0 383 $layercounter++;
michael@0 384 while (<TMP>) {
michael@0 385 last if (/^\r?$/); # empty lines mark end of header
michael@0 386 if (/^SMIME: /) { # add all SMIME info to the rfc822 hdrs
michael@0 387 $lastref = \$rfc822headers;
michael@0 388 } elsif (/^\s/) {
michael@0 389 ; # continuation lines go to the last dest
michael@0 390 } else {
michael@0 391 $lastref = \$mimeheaders; # all other headers are mime headers
michael@0 392 }
michael@0 393 $$lastref .= $_;
michael@0 394 }
michael@0 395 # slurp in rest of the data to $mimebody
michael@0 396 $saveRS = $/; $/ = undef; $mimebody = <TMP>; $/ = $saveRS;
michael@0 397 close(TMP);
michael@0 398
michael@0 399 unlink($tmpderfile);
michael@0 400
michael@0 401 } else {
michael@0 402 print STDERR "ERROR: unknown smime-type \"$smimetype\" in application/pkcs7-smime entity.\n";
michael@0 403 last;
michael@0 404 }
michael@0 405 } elsif ($contenttype eq "multipart/signed") {
michael@0 406 #
michael@0 407 # clear signed message
michael@0 408 #
michael@0 409 unless (exists($hdrhash{"content-type"}{"protocol"})) {
michael@0 410 print STDERR "ERROR: content type has no protocol attribute in multipart/signed entity.\n";
michael@0 411 last;
michael@0 412 }
michael@0 413 if ($hdrhash{"content-type"}{"protocol"} ne "application/pkcs7-signature") {
michael@0 414 # we cannot handle this guy
michael@0 415 print STDERR "ERROR: unknown protocol \"", $hdrhash{"content-type"}{"protocol"},
michael@0 416 "\" in multipart/signed entity.\n";
michael@0 417 last;
michael@0 418 }
michael@0 419 unless (exists($hdrhash{"content-type"}{"boundary"})) {
michael@0 420 print STDERR "ERROR: no boundary attribute in multipart/signed entity.\n";
michael@0 421 last;
michael@0 422 }
michael@0 423 $boundary = $hdrhash{"content-type"}{"boundary"};
michael@0 424
michael@0 425 # split $mimebody along \n--$boundary\n - gets you four parts
michael@0 426 # first (0), any comments the sending agent might have put in
michael@0 427 # second (1), the message itself
michael@0 428 # third (2), the signature as a mime entity
michael@0 429 # fourth (3), trailing data (there shouldn't be any)
michael@0 430
michael@0 431 @multiparts = split(/\r?\n--$boundary(?:--)?\r?\n/, $mimebody);
michael@0 432
michael@0 433 #
michael@0 434 # parse the signature headers
michael@0 435 ($submimeheaders, $submimebody) = split(/^$/m, $multiparts[2]);
michael@0 436 %sighdrhash = parseheaders($submimeheaders);
michael@0 437 unless (exists($sighdrhash{"content-type"}{MAIN})) {
michael@0 438 print STDERR "ERROR: signature entity has no content type.\n";
michael@0 439 last;
michael@0 440 }
michael@0 441 if ($sighdrhash{"content-type"}{MAIN} ne "application/pkcs7-signature") {
michael@0 442 # we cannot handle this guy
michael@0 443 print STDERR "ERROR: unknown content type \"", $sighdrhash{"content-type"}{MAIN},
michael@0 444 "\" in signature entity.\n";
michael@0 445 last;
michael@0 446 }
michael@0 447 if ($sighdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
michael@0 448 $submimebody = decode_base64($submimebody);
michael@0 449 }
michael@0 450
michael@0 451 # we would dump the DER at this point
michael@0 452
michael@0 453 $tmpsigfile = "/tmp/sig.$$";
michael@0 454 open(TMP, ">$tmpsigfile") or die "ERROR: cannot write signature data to temporary file";
michael@0 455 print TMP $submimebody;
michael@0 456 unless (close(TMP)) {
michael@0 457 print STDERR "ERROR: writing signature data to temporary file.\n";
michael@0 458 unlink($tmpsigfile);
michael@0 459 exit 1;
michael@0 460 }
michael@0 461
michael@0 462 $tmpmsgfile = "/tmp/msg.$$";
michael@0 463 open(TMP, ">$tmpmsgfile") or die "ERROR: cannot write message data to temporary file";
michael@0 464 print TMP $multiparts[1];
michael@0 465 unless (close(TMP)) {
michael@0 466 print STDERR "ERROR: writing message data to temporary file.\n";
michael@0 467 unlink($tmpsigfile);
michael@0 468 unlink($tmpmsgfile);
michael@0 469 exit 1;
michael@0 470 }
michael@0 471
michael@0 472 $mimeheaders = "";
michael@0 473 open(TMP, "$cmsutilpath -D $decodeopts -h $layercounter -c $tmpmsgfile -i $tmpsigfile |") or die "ERROR: cannot open pipe to cmsutil";
michael@0 474 $layercounter++;
michael@0 475 while (<TMP>) {
michael@0 476 last if (/^\r?$/);
michael@0 477 if (/^SMIME: /) {
michael@0 478 $lastref = \$rfc822headers;
michael@0 479 } elsif (/^\s/) {
michael@0 480 ;
michael@0 481 } else {
michael@0 482 $lastref = \$mimeheaders;
michael@0 483 }
michael@0 484 $$lastref .= $_;
michael@0 485 }
michael@0 486 $saveRS = $/; $/ = undef; $mimebody = <TMP>; $/ = $saveRS;
michael@0 487 close(TMP);
michael@0 488 unlink($tmpsigfile);
michael@0 489 unlink($tmpmsgfile);
michael@0 490
michael@0 491 } else {
michael@0 492
michael@0 493 # not a content type we know - we're done
michael@0 494 last;
michael@0 495
michael@0 496 }
michael@0 497 }
michael@0 498
michael@0 499 # so now we have the S/MIME parsing information in rfc822headers
michael@0 500 # and the first mime entity we could not handle in mimeheaders and mimebody.
michael@0 501 # dump 'em out and we're done.
michael@0 502 print $rfc822headers;
michael@0 503 print $mimeheaders . "\n" . $mimebody;
michael@0 504
michael@0 505 } else {
michael@0 506
michael@0 507 #
michael@0 508 # encode (which is much easier than decode)
michael@0 509 #
michael@0 510
michael@0 511 $mimeentity = $mimeheaders . "\n" . $mimebody;
michael@0 512
michael@0 513 #
michael@0 514 # canonicalize inner entity (rudimentary yet)
michael@0 515 # convert single LFs to CRLF
michael@0 516 # if no Content-Transfer-Encoding header present:
michael@0 517 # if 8 bit chars present, use Content-Transfer-Encoding: quoted-printable
michael@0 518 # otherwise, use Content-Transfer-Encoding: 7bit
michael@0 519 #
michael@0 520 $mimeentity =~ s/\r*\n/\r\n/mg;
michael@0 521
michael@0 522 #
michael@0 523 # now do the wrapping
michael@0 524 # we sign first, then encrypt because that's what Communicator needs
michael@0 525 #
michael@0 526 if (defined($opt_S)) {
michael@0 527 $mimeentity = signentity($mimeentity, $signopts);
michael@0 528 }
michael@0 529
michael@0 530 if (defined($opt_E)) {
michael@0 531 $mimeentity = encryptentity($mimeentity, $encryptopts);
michael@0 532 }
michael@0 533
michael@0 534 #
michael@0 535 # XXX sign again to do triple wrapping (RFC2634)
michael@0 536 #
michael@0 537
michael@0 538 #
michael@0 539 # now write out the RFC822 headers
michael@0 540 # followed by the final $mimeentity
michael@0 541 #
michael@0 542 print $rfc822headers;
michael@0 543 print "MIME-Version: 1.0 (NSS SMIME - http://www.mozilla.org/projects/security)\n"; # set up the flag
michael@0 544 print $mimeentity;
michael@0 545 }
michael@0 546
michael@0 547 exit 0;

mercurial