# above directory
set smime_hash_cert_command="openssl x509 -in %f -noout -hash"
+# This is used to get a md5-fingerprint of a certificate for purpose
+# of comparism
+set smime_fingerprint_cert_command="openssl x509 -in %f -noout -fingerprint"
+
# This is used to get the email address the certificate was issued to.
set smime_get_cert_email_command="openssl x509 -in %f -noout -email"
/*
** .pp
** Since there is no pubring/secring as with PGP, mutt has to handle
- ** storage ad retrieval of keys by itself. This is very basic right now,
+ ** storage ad retrieval of keys/certs by itself. This is very basic right now,
** and stores keys and certificates in two different directories, both
- ** named as the hash-value retrieved from OpenSSl. There is an index file
- ** which contains mailbox-address keyid pai, and which can be manually
- ** edited.
+ ** named as the hash-value retrieved from OpenSSL. There is an index file
+ ** which contains mailbox-address keyid pair, and which can be manually
+ ** edited. This one points to the location of the private keys.
*/
{ "smime_ca_location", DT_PATH, R_NONE, UL &SmimeCALocation, 0 },
/*
** and stores keys and certificates in two different directories, both
** named as the hash-value retrieved from OpenSSl. There is an index file
** which contains mailbox-address keyid pai, and which can be manually
- ** edited.
+ ** edited. This one points to the location of the certificates.
*/
{ "smime_decrypt_command", DT_STR, R_NONE, UL &SmimeDecryptCommand, 0},
/*
** .pp
- ** This format strings specifies a command which is used to decrypt
+ ** This format string specifies a command which is used to decrypt
** application/x-pkcs7-mime attachments.
** .pp
** The OpenSSL command formats have their own set of printf-like sequences
** . "-CApath $$smime_ca_location" or "-CAfile $$smime_ca_location".
** .de
** .pp
- ** For examples on how to configure these formats, see the smime.rc
+ ** For examples on how to configure these formats, see the smime.rc in
** the samples/ subdirectory which has been installed on your system
** alongside the documentation.
*/
/*
** .pp
** This command is used to extract PKCS7 structures of S/MIME signatures,
- ** in Order to extract the public X509 certificate(s).
+ ** in order to extract the public X509 certificate(s).
*/
{ "smime_get_cert_command", DT_STR, R_NONE, UL &SmimeGetCertCommand, 0},
/*
{ "smime_get_signer_cert_command", DT_STR, R_NONE, UL &SmimeGetSignerCertCommand, 0},
/*
** .pp
- ** This command is used to extract only the signers X509 certificate from a S/MIME signature,
- ** so that the certificate's owner may get compared to the email's from field.
+ ** This command is used to extract only the signers X509 certificate from a S/MIME
+ ** signature, so that the certificate's owner may get compared to the email's
+ ** 'From'-field.
*/
{ "smime_hash_cert_command", DT_STR, R_NONE, UL &SmimeHashCertCommand, 0},
/*
** .pp
** This command is used to calculate a hash value used for storing
- ** X509 certificates.
+ ** X509 certificates. (The value is derived from the cert's subject field)
+ */
+ { "smime_fingerprint_cert_command", DT_STR, R_NONE, UL &SmimeFingerprintCertCommand, 0},
+ /*
+ ** .pp
+ ** This command returns a md5-fingerprint of the certificate.
+ ** That way, certificates with an identical subject field can get compared.
*/
{ "smime_get_cert_email_command", DT_STR, R_NONE, UL &SmimeGetCertEmailCommand, 0},
/*
** .pp
- ** This command is used to extract the mail address used for storing
- ** X509 certificates, abd for verification purposes (to see if the
- ** certifacate was issued for the sender's mailbox.
+ ** This command is used to extract the mail address(es) used for storing
+ ** X509 certificates, and for verification purposes (to check, wether the
+ ** certifacate was issued for the sender's mailbox).
*/
{ "smime_sign_as", DT_STR, R_NONE, UL &SmimeSignAs, 0 },
/*
** .pp
** This is the default key-pair to use vor signing. This must be set to the
- ** keyid (the hash-value, OpenSSL generates) to work properly (key handling
- ** is very limited right now.)
+ ** keyid (the hash-value that OpenSSL generates) to work properly
*/
#endif /* HAVE_SMIME */
/*
- * Copyright (C) 2001 Oliver Ehli <elmy@acm.org>
+ * Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
* Copyright (C) 2002 Mike Schiraldi <raldi@research.netsol.com>
*
* This program is free software; you can redistribute it and/or modify
-static int smime_check_cert_email (char *certificate, char *mailbox)
+static int smime_handle_cert_email (char *certificate, char *mailbox,
+ int copy, char ***buffer, int *num)
{
FILE *fpout = NULL, *fperr = NULL;
char tmpfname[_POSIX_PATH_MAX];
char email[STRING];
- int ret = -1;
+ int ret = -1, count = 0;
pid_t thepid;
mutt_mktemp (tmpfname);
while ((fgets (email, sizeof (email), fpout)))
{
- *(email+mutt_strlen(email)-1) = '\0';
- if(mutt_strncasecmp (email, mailbox, mutt_strlen (mailbox))==0)
- {
- ret = 0;
- break;
- }
- ret = 1;
+ *(email + mutt_strlen (email)-1) = '\0';
+ if(mutt_strncasecmp (email, mailbox, mutt_strlen (mailbox)) == 0)
+ ret=1;
+
+ ret = ret < 0 ? 0 : ret;
+ count++;
}
+
if (ret == -1)
{
mutt_copy_stream (fperr, stdout);
mutt_error (_("Alert: No mailbox specified in certificate.\n"));
ret = 1;
}
- else if (ret == 1)
+ else if (!ret)
{
mutt_endwin(NULL);
- mutt_error (_("Alert: Certificate belongs to \"%s\".\n"
- " But sender was \"%s\".\n"), email, mailbox);
+ mutt_error (_("Alert: Certificate does *NOT* belong to \"%s\".\n"), mailbox);
ret = 1;
}
+ else ret = 0;
+
+ if(copy && buffer && num)
+ {
+ (*num) = count;
+ *buffer = safe_calloc(sizeof(char*), count);
+ count = 0;
+
+ rewind (fpout);
+ while ((fgets (email, sizeof (email), fpout)))
+ {
+ *(email + mutt_strlen (email) - 1) = '\0';
+ (*buffer)[count] = safe_calloc(1, mutt_strlen (email) + 1);
+ strncpy((*buffer)[count], email, mutt_strlen (email));
+ count++;
+ }
+ }
+ else if(copy) ret = 2;
fclose (fpout);
fclose (fperr);
}
+
+static int smime_compare_fingerprint (char *certificate, char *hashval, char *dest)
+{
+ FILE *fpout = NULL, *fperr = NULL;
+ char tmpfname[_POSIX_PATH_MAX];
+ char md5New[STRING], md5Old[STRING];
+ pid_t thepid;
+
+ mutt_mktemp (tmpfname);
+ if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
+ {
+ mutt_perror (tmpfname);
+ return -1;
+ }
+ mutt_unlink (tmpfname);
+
+ mutt_mktemp (tmpfname);
+ if ((fpout = safe_fopen (tmpfname, "w+")) == NULL)
+ {
+ fclose (fperr);
+ mutt_perror (tmpfname);
+ return -1;
+ }
+ mutt_unlink (tmpfname);
+
+ /* fingerprint the certificate to add */
+ if ((thepid = smime_invoke (NULL, NULL, NULL,
+ -1, fileno (fpout), fileno (fperr),
+ certificate, NULL, NULL, NULL, NULL, NULL,
+ SmimeFingerprintCertCommand))== -1)
+ {
+ mutt_message (_("Error: unable to create OpenSSL subprocess!"));
+ fclose (fperr);
+ fclose (fpout);
+ return -1;
+ }
+
+ mutt_wait_filter (thepid);
+
+ fflush (fpout);
+ rewind (fpout);
+ fflush (fperr);
+ rewind (fperr);
+
+ if (!(fgets (md5New, sizeof (md5New), fpout)))
+ {
+ mutt_copy_stream (fperr, stdout);
+ fclose (fpout);
+ fclose (fperr);
+ return -1;
+ }
+
+ /* fingerprint the certificate already installed */
+ if ((thepid = smime_invoke (NULL, NULL, NULL,
+ -1, fileno (fpout), fileno (fperr),
+ dest, NULL, NULL, NULL, NULL, NULL,
+ SmimeFingerprintCertCommand))== -1)
+ {
+ mutt_message (_("Error: unable to create OpenSSL subprocess!"));
+ fclose (fperr);
+ fclose (fpout);
+ return -1;
+ }
+
+ mutt_wait_filter (thepid);
+
+ fflush (fpout);
+ rewind (fpout);
+ fflush (fperr);
+ rewind (fperr);
+
+ if (!(fgets (md5Old, sizeof (md5Old), fpout)))
+ {
+ mutt_copy_stream (fperr, stdout);
+ fclose (fpout);
+ fclose (fperr);
+ return -1;
+ }
+
+ fclose (fpout);
+ fclose (fperr);
+
+ return ((mutt_strcasecmp (md5Old, md5New) == 0));
+}
+
+
/* Add a certificate and update index file. */
-static void smime_add_certificate (char *certificate, char *mailbox, short public)
+static int smime_add_certificate (char *certificate, char *mailbox)
{
FILE *fpin = NULL, *fpout = NULL, *fperr = NULL;
char tmpfname[_POSIX_PATH_MAX], dest[_POSIX_PATH_MAX];
char buf[LONG_STRING], hashval[STRING], *tmpKey;
struct stat info;
- int i = 0;
+ int i = 0, certExists=0;
pid_t thepid;
mutt_mktemp (tmpfname);
if ((fperr = safe_fopen (tmpfname, "w+")) == NULL)
{
mutt_perror (tmpfname);
- return;
+ return 1;
}
mutt_unlink (tmpfname);
{
fclose (fperr);
mutt_perror (tmpfname);
- return;
+ return 1;
}
mutt_unlink (tmpfname);
mutt_message (_("Error: unable to create OpenSSL subprocess!"));
fclose (fperr);
fclose (fpout);
- return;
+ return 1;
}
mutt_wait_filter (thepid);
fflush (fpout);
rewind (fpout);
- rewind (fperr);
fflush (fperr);
+ rewind (fperr);
if (!(fgets (hashval, sizeof (hashval), fpout)))
{
mutt_copy_stream (fperr, stdout);
fclose (fpout);
fclose (fperr);
- return;
+ return 1;
}
fclose (fpout);
fclose (fperr);
if (stat (dest, &info))
break;
- else
- i++;
+ else { /* check wether this certificate already exists. */
+ if((certExists = smime_compare_fingerprint (certificate, hashval, dest)) == 1)
+ {
+ break;
+ }
+ else
+ {
+ if(!certExists)
+ i++;
+ else /* some error: abort. */
+ return 1;
+ }
+ }
}
-
- if ((fpout = safe_fopen (dest, "w+")) == NULL)
+
+ if(!certExists)
{
- mutt_perror (dest);
- return;
- }
+ if ((fpout = safe_fopen (dest, "w+")) == NULL)
+ {
+ mutt_perror (dest);
+ return 1;
+ }
- if ((fpin = safe_fopen (certificate, "r")) == NULL)
- {
- mutt_perror (certificate);
+ if ((fpin = safe_fopen (certificate, "r")) == NULL)
+ {
+ mutt_perror (certificate);
+ fclose (fpout);
+ mutt_unlink (dest);
+ return 1;
+ }
+
+ mutt_copy_stream (fpin, fpout);
fclose (fpout);
- mutt_unlink (dest);
- return;
+ fclose (fpin);
}
-
- mutt_copy_stream (fpin, fpout);
- fclose (fpout);
- fclose (fpin);
/*
Now check if the mailbox is already found with the certificate's
- hash value.
-
- openssl uses md5 fingerprints to check wether two keys are identical.
- I have to add that.
-
+ hash value and/or md5 fingerprint.
*/
- tmpKey = smime_get_field_from_db (mailbox, NULL, public, 0);
+ tmpKey = smime_get_field_from_db (mailbox, NULL, 1, 0); /* _always_ public! */
- /* check if hash values are identical => same certificate ? */
+ /* check if hash is identical => same certificate! */
/* perhaps we should ask for permission to overwrite ? */
/* what about revoked certificates anyway ? */
- /* reminder: openssl checks md5 - fingerprint for equality. add this. */
-
if (tmpKey && !mutt_strncmp (tmpKey, hashval, mutt_strlen (hashval)))
{
mutt_message (_("Certificate \"%s\" exists for \"%s\"."), hashval, mailbox);
- mutt_unlink (dest);
- return;
+ return 0;
}
- /* append to index. */
+ /* doesn't exist or is a new one, so append to index. */
snprintf (tmpfname, sizeof (tmpfname), "%s/.index",
- (public ? NONULL(SmimeCertificates) : NONULL(SmimeKeys)));
+ NONULL(SmimeCertificates)); /* _always_ public: we don't add keys here */
if (!stat (tmpfname, &info))
{
{
mutt_perror (tmpfname);
mutt_unlink (dest);
- return;
+ return 1;
}
/*
? = unknown issuer, - = unassigned label,
- u = undefined trust settings.
+ v = undefined trust settings (else we wouldn't have got that far).
*/
snprintf (buf, sizeof (buf), "%s %s.%d - ? u\n", mailbox, hashval, i);
fputs (buf, fpout);
mutt_message (_("Successfully added certificate \"%s\" for \"%s\". "), hashval, mailbox);
}
- return;
+ return 0;
}
void smime_invoke_import (char *infile, char *mailbox)
{
- char *certfile = NULL;
- int i;
+ char *certfile = NULL, **addrList=0;
+ int i, addrCount=0, ret = 0;
certfile = smime_extract_signer_certificate(infile);
- if (certfile == NULL)
+ if (certfile)
{
- mutt_message _("Certificate *NOT* added.");
- return;
- }
-
- i = smime_check_cert_email (certfile, mailbox);
+ i = smime_handle_cert_email (certfile, mailbox, 1, &addrList, &addrCount);
- mutt_unlink(certfile);
+ mutt_unlink(certfile);
- if (i)
- {
- mutt_message _("Certificate *NOT* added.");
- return;
- }
+ if (i)
+ {
+ mutt_message _("Certificate *NOT* added.");
+ return;
+ }
+ if ((certfile = smime_extract_certificate(infile)))
+ {
+ for (i = 0; i < addrCount; i++)
+ {
+ /* perhaps we shouldn't abort completley ? */
+
+ if(!ret)
+ ret = smime_add_certificate (certfile, addrList[i]);
- if ((certfile = smime_extract_certificate(infile)))
+ safe_free((void **)&(addrList[i]));
+ }
+ mutt_unlink (certfile);
+ safe_free((void **)&certfile);
+ safe_free((void **)&addrList);
+ }
+ if(!ret)
+ return;
+ }
+
+ if(isendwin())
{
- smime_add_certificate (certfile, mailbox, 1);
- mutt_unlink (certfile);
- safe_free((void **)&certfile);
+ mutt_any_key_to_continue(NULL);
+ mutt_message _("Certificate *NOT* added.");
}
-
return;
}
if ((certfile = smime_extract_signer_certificate(tempfname)))
{
mutt_unlink(tempfname);
- if (smime_check_cert_email (certfile, mbox))
+ if (smime_handle_cert_email (certfile, mbox, 0, NULL, NULL))
mutt_any_key_to_continue(NULL);
else
retval = 0;
WHERE char *SmimePk7outCommand;
WHERE char *SmimeGetCertCommand;
WHERE char *SmimeHashCertCommand;
+WHERE char *SmimeFingerprintCertCommand;
WHERE char *SmimeGetCertEmailCommand;
#! /usr/bin/perl -w
-# Copyright (C) 2001 Oliver Ehli <elmy@acm.org>
+# Copyright (C) 2001,2002 Oliver Ehli <elmy@acm.org>
# Copyright (C) 2001 Mike Schiraldi <raldi@research.netsol.com>
#
# This program is free software; you can redistribute it and/or modify
handle_pem(@pem);
}
elsif(@ARGV == 4 and $ARGV[0] eq "add_chain") {
+ my $mailbox;
my $cmd = "openssl x509 -noout -hash -in $ARGV[2]";
my $cert_hash = `$cmd`;
+
$? and die "'$cmd' returned $?";
$cmd = "openssl x509 -noout -hash -in $ARGV[3]";
my $label = query_label;
add_certificate($ARGV[3], \$issuer_hash, 0, $label);
- my $mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash);
+ my @mailbox = &add_certificate($ARGV[2], \$cert_hash, 1, $label, $issuer_hash);
- add_key($ARGV[1], $cert_hash, $mailbox, $label);
+ foreach $mailbox (@mailbox) {
+ chomp($mailbox);
+ add_key($ARGV[1], $cert_hash, $mailbox, $label);
+ }
}
elsif((@ARGV == 2 or @ARGV == 3) and $ARGV[0] eq "verify") {
verify_cert($ARGV[1], $ARGV[2]);
print "$fields[1]: Issued for: $fields[0] \"$fields[2]\" $keyflags{$fields[4]}\n";
}
- (my $subject_in, my $email_in, my $issuer_in, my $date1_in, my $date2_in) =
- `openssl x509 -subject -email -issuer -dates -noout -in $certificates_path/$fields[1]`;
+ (my $subject_in, my $issuer_in, my $date1_in, my $date2_in) =
+ `openssl x509 -subject -issuer -dates -noout -in $certificates_path/$fields[1]`;
my @subject = split(/\//, $subject_in);
while(@subject) {
my $issuer_hash = shift;
my $iter = 0;
+ my @mailbox;
my $mailbox;
while(-e "$certificates_path/$$hashvalue.$iter") {
if ($add_to_index) {
my $cmd = "openssl x509 -in $filename -email -noout";
- $mailbox = `$cmd`;
+ @mailbox = `$cmd`;
$? and die "'$cmd' returned $?";
- chomp($mailbox);
- add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
+ foreach $mailbox (@mailbox) {
+ chomp($mailbox);
+ add_entry($mailbox, $$hashvalue, 1, $label, $issuer_hash);
- print "added certificate: $certificates_path/$$hashvalue for $mailbox.\n";
+ print "added certificate: $certificates_path/$$hashvalue for $mailbox.\n";
+ }
}
else {
print "added certificate: $certificates_path/$$hashvalue.\n";
}
}
- return $mailbox;
+ return @mailbox;
}
unless (-e "$private_keys_path/$hashvalue") {
my $cmd = "cp $file $private_keys_path/$hashvalue";
system $cmd and die "$cmd returned $!";
- print "added private key: " .
- "$private_keys_path/$hashvalue for $mailbox\n";
- add_entry($mailbox, $hashvalue, 0, $label, "");
}
+
+ add_entry($mailbox, $hashvalue, 0, $label, "");
+ print "added private key: " .
+ "$private_keys_path/$hashvalue for $mailbox\n";
}
my $root_cert;
my $key;
my $certificate;
+ my $intermediate;
+ my @mailbox;
my $mailbox;
@pem_contents = &parse_pem(@_);
}
$iter++;
}
- ($key > $#pem_contents>>2) and die("Couldn't find private key!");
+ ($iter > $#pem_contents>>2) and die("Couldn't find private key!");
$pem_contents[($key<<2)+1] or die("Attribute 'localKeyID' wasn't set.");
}
$iter++;
}
- ($certificate > $#pem_contents>>2) and die("Couldn't find matching certificate!");
+ ($iter > $#pem_contents>>2) and die("Couldn't find matching certificate!");
my $cmd = "cp cert_tmp.$key tmp_key";
system $cmd and die "'$cmd' returned $?";
}
$iter++;
}
- ($root_cert > $#pem_contents>>2) and die("Couldn't identify root certificate!");
+ ($iter > $#pem_contents>>2) and die("Couldn't identify root certificate!");
# what's left are intermediate certificates.
$iter = 0;
unlink "tmp_issuer_cert";
+ # needs to be set, so we can check it later
+ $intermediate = $root_cert;
while($iter <= $#pem_contents>>2) {
if ($iter == $key or $iter == $certificate or $iter == $root_cert) {
$iter++;
my $cmd = "cat cert_tmp.$iter >> tmp_issuer_cert";
system $cmd and die "'$cmd' returned $?";
+ # although there may be many, just need to know if there was any
+ $intermediate = $iter;
+
$iter++;
}
+ # no intermediate certificates ? use root-cert instead
+ if($intermediate == $root_cert) {
+ $cmd = "cp cert_tmp.$root_cert tmp_issuer_cert";
+ }
+
my $label = query_label;
$cmd = "openssl x509 -noout -hash -in tmp_certificate";
# Note: $cert_hash will be changed to reflect the correct filename
# within add_cert() ONLY, so these _have_ to get called first..
add_certificate("tmp_issuer_cert", \$issuer_hash, 0, $label);
- $mailbox = &add_certificate("tmp_certificate", \$cert_hash, 1, $label, $issuer_hash);
- add_key("tmp_key", $cert_hash, $mailbox, $label);
+ @mailbox = &add_certificate("tmp_certificate", \$cert_hash, 1, $label, $issuer_hash);
+ foreach $mailbox (@mailbox) {
+ chomp($mailbox);
+ add_key("tmp_key", $cert_hash, $mailbox, $label);
+ }
unlink <cert_tmp.*>;
unlink <tmp_*>;