зеркало из https://github.com/mozilla/gecko-dev.git
Preliminary checkin at a state where smime can parse messages generated
by itself.
This commit is contained in:
Родитель
9ed907c5a2
Коммит
d705ca0332
|
@ -34,7 +34,7 @@
|
|||
/*
|
||||
* cmsutil -- A command to work with CMS data
|
||||
*
|
||||
* $Id: cmsutil.c,v 1.4 2000/06/20 16:28:55 chrisk%netscape.com Exp $
|
||||
* $Id: cmsutil.c,v 1.5 2000/06/23 16:40:30 chrisk%netscape.com Exp $
|
||||
*/
|
||||
|
||||
#include "nspr.h"
|
||||
|
@ -325,9 +325,18 @@ decode(FILE *out, FILE *infile, char *progName, struct optionsStr options, struc
|
|||
}
|
||||
|
||||
if (!decodeOptions.suppressContent) {
|
||||
/* XXX only if we do not have detached content... */
|
||||
if ((item = NSS_CMSMessage_GetContent(cmsg)) != NULL) {
|
||||
fwrite(item->data, item->len, 1, out);
|
||||
if (decodeOptions.contentFile) {
|
||||
char buffer[4096];
|
||||
size_t nbytes;
|
||||
/* detached content: print content file */
|
||||
fseek(decodeOptions.contentFile, 0, SEEK_SET);
|
||||
while ((nbytes = fread(buffer, 1, sizeof(buffer), decodeOptions.contentFile)) != 0) {
|
||||
fwrite(buffer, nbytes, 1, out);
|
||||
}
|
||||
} else {
|
||||
if ((item = NSS_CMSMessage_GetContent(cmsg)) != NULL) {
|
||||
fwrite(item->data, item->len, 1, out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@
|
|||
# GPL.
|
||||
|
||||
#
|
||||
# smime.pl - frontend for S/MIME message generation
|
||||
# smime.pl - frontend for S/MIME message generation and parsing
|
||||
#
|
||||
# $Id: smime,v 1.3 2000/06/14 23:15:06 chrisk%netscape.com Exp $
|
||||
# $Id: smime,v 1.4 2000/06/23 16:40:31 chrisk%netscape.com Exp $
|
||||
#
|
||||
|
||||
use Getopt::Std;
|
||||
|
@ -45,7 +45,7 @@ use Getopt::Std;
|
|||
$cmsutilpath = "cmsutil";
|
||||
|
||||
#
|
||||
# Thanks to Gisle Aas <gisle@aas.no> for the encode_base64 function
|
||||
# Thanks to Gisle Aas <gisle@aas.no> for the base64 functions
|
||||
# originally taken from MIME-Base64-2.11 at www.cpan.org
|
||||
#
|
||||
sub encode_base64($)
|
||||
|
@ -65,6 +65,78 @@ sub encode_base64($)
|
|||
$res;
|
||||
}
|
||||
|
||||
sub decode_base64($)
|
||||
{
|
||||
local($^W) = 0; # unpack("u",...) gives bogus warning in 5.00[123]
|
||||
|
||||
my $str = shift;
|
||||
my $res = "";
|
||||
|
||||
$str =~ tr|A-Za-z0-9+=/||cd; # remove non-base64 chars
|
||||
if (length($str) % 4) {
|
||||
require Carp;
|
||||
Carp::carp("Length of base64 data not a multiple of 4")
|
||||
}
|
||||
$str =~ s/=+$//; # remove padding
|
||||
$str =~ tr|A-Za-z0-9+/| -_|; # convert to uuencoded format
|
||||
while ($str =~ /(.{1,60})/gs) {
|
||||
my $len = chr(32 + length($1)*3/4); # compute length byte
|
||||
$res .= unpack("u", $len . $1 ); # uudecode
|
||||
}
|
||||
$res;
|
||||
}
|
||||
|
||||
#
|
||||
# parse headers into a hash
|
||||
#
|
||||
# %headers = parseheaders($headertext);
|
||||
#
|
||||
sub parseheaders($)
|
||||
{
|
||||
my ($headerdata) = @_;
|
||||
my $hdr;
|
||||
my %hdrhash;
|
||||
my $hdrname;
|
||||
my $hdrvalue;
|
||||
my @hdrvalues;
|
||||
my $subhdrname;
|
||||
my $subhdrvalue;
|
||||
|
||||
# the expression in split() correctly handles continuation lines
|
||||
foreach $hdr (split(/\n(?=\S)/, $headerdata)) {
|
||||
$hdr =~ s/\r*\n\s+/ /g; # collapse continuation lines
|
||||
($hdrname, $hdrvalue) = $hdr =~ m/^(\S+):\s+(.*)$/;
|
||||
|
||||
# ignore non-headers (or should we die horribly?)
|
||||
next unless (defined($hdrname));
|
||||
$hdrname =~ tr/A-Z/a-z/;
|
||||
@hdrvalues = split(/\s*;\s*/, $hdrvalue);
|
||||
|
||||
# there is guaranteed to be at least one value
|
||||
$hdrvalue = shift @hdrvalues;
|
||||
if ($hdrvalue =~ /^\"(.*)\"$/) {
|
||||
$hdrvalue = $1;
|
||||
}
|
||||
|
||||
$hdrhash{$hdrname}{MAIN} = $hdrvalue;
|
||||
# print "XXX $hdrname = $hdrvalue\n";
|
||||
|
||||
# deal with additional name-value pairs
|
||||
foreach $hdrvalue (@hdrvalues) {
|
||||
($subhdrname, $subhdrvalue) = $hdrvalue =~ m/^(\S+)\s*=\s*(.*)$/;
|
||||
# ignore non-subheaders (or should we die?)
|
||||
next unless (defined($subhdrname));
|
||||
$subhdrname =~ tr/A-Z/a-z/;
|
||||
if ($subhdrvalue =~ /^\"(.*)\"$/) {
|
||||
$subhdrvalue = $1;
|
||||
}
|
||||
$hdrhash{$hdrname}{$subhdrname} = $subhdrvalue;
|
||||
}
|
||||
|
||||
}
|
||||
return %hdrhash;
|
||||
}
|
||||
|
||||
#
|
||||
# encryptentity($entity, $options) - encrypt an S/MIME entity
|
||||
#
|
||||
|
@ -193,10 +265,13 @@ sub usage {
|
|||
print STDERR " -S nick generate signed message, use certificate named \"nick\"\n";
|
||||
print STDERR " -p passwd use \"passwd\" as security module password\n";
|
||||
print STDERR " -E rec1[,rec2...] generate encrypted message for recipients\n";
|
||||
print STDERR " -D decode a S/MIME message\n";
|
||||
print STDERR " -C pathname set pathname of \"cmsutil\"\n";
|
||||
print STDERR "\nWith -S or -E, smime will take a regular RFC822 message or MIME entity\n";
|
||||
print STDERR "and generate a signed or encrypted S/MIME message with the same headers\n";
|
||||
print STDERR "and content from it. The output can be used as input to a MTA.\n";
|
||||
print STDERR "on stdin and generate a signed or encrypted S/MIME message with the same\n";
|
||||
print STDERR "headers and content from it. The output can be used as input to a MTA.\n";
|
||||
print STDERR "-D causes smime to strip off all S/MIME layers if possible and output\n";
|
||||
print STDERR "the \"inner\" message.\n";
|
||||
}
|
||||
|
||||
#
|
||||
|
@ -211,8 +286,8 @@ unless (getopts('S:E:p:C:D')) {
|
|||
exit 1;
|
||||
}
|
||||
|
||||
unless (defined($opt_S) or defined($opt_E)) {
|
||||
print STDERR "ERROR: -S and/or -E must be specified.\n";
|
||||
unless (defined($opt_S) or defined($opt_E) or defined($opt_D)) {
|
||||
print STDERR "ERROR: -S and/or -E, or -D must be specified.\n";
|
||||
usage();
|
||||
exit 1;
|
||||
}
|
||||
|
@ -243,51 +318,217 @@ if (defined($opt_C)) {
|
|||
# The RFC822 headers are preserved and stay on the outer layer of the message
|
||||
#
|
||||
$rfc822headers = "";
|
||||
$mimeentity = "";
|
||||
$mimeheaders = "";
|
||||
$mimebody = "";
|
||||
$skippedheaders = "";
|
||||
while (<STDIN>) {
|
||||
last if (/^$/);
|
||||
if (/^content-\S+: /i) {
|
||||
$mimeentity .= $_;
|
||||
$lastref = \$mimeheaders;
|
||||
} elsif (/^mime-version: /i) {
|
||||
; # skip it
|
||||
$lastref = \$skippedheaders; # skip it
|
||||
} elsif (/^\s/) {
|
||||
;
|
||||
} else {
|
||||
$rfc822headers .= $_;
|
||||
$lastref = \$rfc822headers;
|
||||
}
|
||||
$$lastref .= $_;
|
||||
}
|
||||
|
||||
#
|
||||
# if there are no MIME entity headers, generate some default ones
|
||||
#
|
||||
if ($mimeentity eq "") {
|
||||
$mimeentity .= "Content-Type: text/plain; charset=us-ascii\n";
|
||||
$mimeentity .= "Content-Transfer-Encoding: 7bit\n";
|
||||
if ($mimeheaders eq "") {
|
||||
$mimeheaders .= "Content-Type: text/plain; charset=us-ascii\n";
|
||||
$mimeheaders .= "Content-Transfer-Encoding: 7bit\n";
|
||||
}
|
||||
|
||||
#
|
||||
# generate end of header-LF/LF pair
|
||||
#
|
||||
$mimeentity .= "\n";
|
||||
|
||||
#
|
||||
# slurp in the entity body
|
||||
#
|
||||
$saveRS = $/;
|
||||
$/ = undef;
|
||||
$mimeentity .= <STDIN>;
|
||||
$mimebody = <STDIN>;
|
||||
$/ = $saveRS;
|
||||
|
||||
if (defined $opt_D) {
|
||||
#
|
||||
# decode
|
||||
#
|
||||
# possible options would be:
|
||||
# - strip off only one layer
|
||||
# - strip off outer signature (if present)
|
||||
# - just print information about the structure of the message
|
||||
# - strip n layers, then dump DER of CMS message
|
||||
|
||||
|
||||
while (1) {
|
||||
%hdrhash = parseheaders($mimeheaders);
|
||||
unless (exists($hdrhash{"content-type"}{MAIN})) {
|
||||
print STDERR "ERROR: no content type header found in MIME entity\n";
|
||||
last; # no content-type - we're done
|
||||
}
|
||||
|
||||
$contenttype = $hdrhash{"content-type"}{MAIN};
|
||||
if ($contenttype eq "application/pkcs7-mime") {
|
||||
#
|
||||
# opaque-signed or enveloped message
|
||||
#
|
||||
unless (exists($hdrhash{"content-type"}{"smime-type"})) {
|
||||
print STDERR "ERROR: no smime-type attribute in application/pkcs7-smime entity.\n";
|
||||
last;
|
||||
}
|
||||
$smimetype = $hdrhash{"content-type"}{"smime-type"};
|
||||
if ($smimetype eq "signed-data" or $smimetype eq "enveloped-data") {
|
||||
# it's verification or decryption time!
|
||||
# XXX
|
||||
if ($hdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
|
||||
$mimebody = decode_base64($mimebody);
|
||||
}
|
||||
|
||||
# we would dump the DER at this point
|
||||
|
||||
$tmpsigfile = "/tmp/sig.$$";
|
||||
open(TMP, ">$tmpsigfile") or die "ERROR: cannot write signature data to temporary file";
|
||||
print TMP $mimebody;
|
||||
unless (close(TMP)) {
|
||||
print STDERR "ERROR: writing signature data to temporary file.\n";
|
||||
unlink($tmpsigfile);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$mimeheaders = "";
|
||||
open(TMP, "$cmsutilpath -D -h 1 -i $tmpsigfile |") or die "ERROR: cannot open pipe to cmsutil";
|
||||
while (<TMP>) {
|
||||
last if (/^\r?$/);
|
||||
if (/^SMIME: /) {
|
||||
$lastref = \$rfc822headers;
|
||||
} elsif (/^\s/) {
|
||||
;
|
||||
} else {
|
||||
$lastref = \$mimeheaders;
|
||||
}
|
||||
$$lastref .= $_;
|
||||
}
|
||||
$olddelim = $/;
|
||||
$/ = undef;
|
||||
$mimebody = <TMP>;
|
||||
$/ = $olddelim;
|
||||
close(TMP);
|
||||
unlink($tmpsigfile);
|
||||
} else {
|
||||
print STDERR "ERROR: unknown smime-type \"$smimetype\" in application/pkcs7-smime entity.\n";
|
||||
last;
|
||||
}
|
||||
} elsif ($contenttype eq "multipart/signed") {
|
||||
# print STDERR "XXX multipart/signed\n";
|
||||
#
|
||||
# clear signed message
|
||||
#
|
||||
unless (exists($hdrhash{"content-type"}{"protocol"})) {
|
||||
print STDERR "ERROR: content type has no protocol attribute in multipart/signed entity.\n";
|
||||
last;
|
||||
}
|
||||
if ($hdrhash{"content-type"}{"protocol"} ne "application/pkcs7-signature") {
|
||||
# we cannot handle this guy
|
||||
print STDERR "ERROR: unknown protocol \"",
|
||||
$hdrhash{"content-type"}{"protocol"},
|
||||
"\" in multipart/signed entity.\n";
|
||||
last;
|
||||
}
|
||||
unless (exists($hdrhash{"content-type"}{"boundary"})) {
|
||||
print STDERR "ERROR: no boundary attribute in multipart/signed entity.\n";
|
||||
last;
|
||||
}
|
||||
$boundary = $hdrhash{"content-type"}{"boundary"};
|
||||
|
||||
# split $mimebody along \n--$boundary\n - gets you four parts
|
||||
# first (0), any comments the sending agent might have put in
|
||||
# second (1), the message itself
|
||||
# third (2), the signature as a mime entity
|
||||
# fourth (3), trailing data (there shouldn't be any)
|
||||
|
||||
@multiparts = split(/\n--$boundary(?:--)?\n/, $mimebody);
|
||||
|
||||
#
|
||||
# parse the signature headers
|
||||
($submimeheaders, $submimebody) = split(/^$/m, $multiparts[2]);
|
||||
%sighdrhash = parseheaders($submimeheaders);
|
||||
unless (exists($sighdrhash{"content-type"}{MAIN})) {
|
||||
print STDERR "ERROR: signature entity has no content type.\n";
|
||||
last;
|
||||
}
|
||||
if ($sighdrhash{"content-type"}{MAIN} ne "application/pkcs7-signature") {
|
||||
# we cannot handle this guy
|
||||
print STDERR "ERROR: unknown content type \"",
|
||||
$sighdrhash{"content-type"}{MAIN},
|
||||
"\" in signature entity.\n";
|
||||
last;
|
||||
}
|
||||
if ($sighdrhash{"content-transfer-encoding"}{MAIN} eq "base64") {
|
||||
$submimebody = decode_base64($submimebody);
|
||||
}
|
||||
|
||||
# we would dump the DER at this point
|
||||
|
||||
$tmpsigfile = "/tmp/sig.$$";
|
||||
open(TMP, ">$tmpsigfile") or die "ERROR: cannot write signature data to temporary file";
|
||||
print TMP $submimebody;
|
||||
unless (close(TMP)) {
|
||||
print STDERR "ERROR: writing signature data to temporary file.\n";
|
||||
unlink($tmpsigfile);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$tmpmsgfile = "/tmp/msg.$$";
|
||||
open(TMP, ">$tmpmsgfile") or die "ERROR: cannot write message data to temporary file";
|
||||
print TMP $multiparts[1];
|
||||
unless (close(TMP)) {
|
||||
print STDERR "ERROR: writing message data to temporary file.\n";
|
||||
unlink($tmpsigfile);
|
||||
unlink($tmpmsgfile);
|
||||
exit 1;
|
||||
}
|
||||
|
||||
$mimeheaders = "";
|
||||
open(TMP, "$cmsutilpath -D -h 1 -c $tmpmsgfile -i $tmpsigfile |") or die "ERROR: cannot open pipe to cmsutil";
|
||||
while (<TMP>) {
|
||||
last if (/^\r?$/);
|
||||
if (/^SMIME: /) {
|
||||
$lastref = \$rfc822headers;
|
||||
} elsif (/^\s/) {
|
||||
;
|
||||
} else {
|
||||
$lastref = \$mimeheaders;
|
||||
}
|
||||
$$lastref .= $_;
|
||||
}
|
||||
$olddelim = $/;
|
||||
$/ = undef;
|
||||
$mimebody = <TMP>;
|
||||
$/ = $olddelim;
|
||||
close(TMP);
|
||||
unlink($tmpsigfile);
|
||||
unlink($tmpmsgfile);
|
||||
} else {
|
||||
# not a content type we know - we're done
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# so now we have the S/MIME parsing information in rfc822headers
|
||||
# and the first mime entity we could not handle in mimeheaders and mimebody.
|
||||
# so dump em out and we're done.
|
||||
print $rfc822headers;
|
||||
print $mimeheaders . "\n" . $mimebody;
|
||||
|
||||
} else {
|
||||
|
||||
#
|
||||
# encode
|
||||
# encode (much easier than decode)
|
||||
#
|
||||
|
||||
$mimeentity = $mimeheaders . "\n" . $mimebody;
|
||||
|
||||
#
|
||||
# canonicalize inner entity (rudimentary yet)
|
||||
# convert single LFs to CRLF
|
||||
|
|
Загрузка…
Ссылка в новой задаче