summaryrefslogtreecommitdiffabout
path: root/libetpan/src/engine/mailprivacy_smime.c
Side-by-side diff
Diffstat (limited to 'libetpan/src/engine/mailprivacy_smime.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/engine/mailprivacy_smime.c1755
1 files changed, 1755 insertions, 0 deletions
diff --git a/libetpan/src/engine/mailprivacy_smime.c b/libetpan/src/engine/mailprivacy_smime.c
new file mode 100644
index 0000000..43eb69f
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_smime.c
@@ -0,0 +1,1755 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailprivacy_smime.h"
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include "mailprivacy_tools.h"
+#include "mailprivacy.h"
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <libetpan/libetpan-config.h>
+
+/*
+ global variable
+
+ TODO : instance of privacy drivers
+*/
+
+static char cert_dir[PATH_MAX] = "";
+static chash * certificates = NULL;
+static chash * private_keys = NULL;
+static char CAcert_dir[PATH_MAX] = "";
+static char * CAfile = NULL;
+static int CA_check = 1;
+static int store_cert = 0;
+static char private_keys_dir[PATH_MAX] = "";
+
+static char * get_cert_file(char * email);
+
+static char * get_private_key_file(char * email);
+
+
+static int smime_is_signed(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ clistiter * cur;
+
+ for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailmime_parameter * param;
+
+ param = cur->data;
+
+ if ((strcasecmp(param->pa_name, "protocol") == 0) &&
+ ((strcasecmp(param->pa_value, "application/x-pkcs7-signature") == 0) ||
+ (strcasecmp(param->pa_value, "application/pkcs7-signature") == 0)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int smime_is_encrypted(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ if ((strcasecmp(mime->mm_content_type->ct_subtype, "x-pkcs7-mime") == 0) ||
+ (strcasecmp(mime->mm_content_type->ct_subtype, "pkcs7-mime") == 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+#define BUF_SIZE 1024
+
+enum {
+ NO_ERROR_SMIME = 0,
+ ERROR_SMIME_CHECK,
+ ERROR_SMIME_COMMAND,
+ ERROR_SMIME_FILE,
+};
+
+/* write output to a file */
+
+static int get_smime_output(FILE * dest_f, char * command)
+{
+ FILE * p;
+ char buf[BUF_SIZE];
+ size_t size;
+ int res;
+ int status;
+ char command_redirected[PATH_MAX];
+
+ snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command);
+
+ /*
+ flush buffer so that it is not flushed more than once when forking
+ */
+ fflush(dest_f);
+
+ p = popen(command_redirected, "r");
+ if (p == NULL) {
+ res = ERROR_SMIME_COMMAND;
+ goto err;
+ }
+
+ while ((size = fread(buf, 1, sizeof(buf), p)) != 0) {
+ size_t written;
+
+ written = fwrite(buf, 1, size, dest_f);
+ if (written != size) {
+ res = ERROR_SMIME_FILE;
+ goto close;
+ }
+ }
+ status = pclose(p);
+
+ if (WEXITSTATUS(status) != 0)
+ return ERROR_SMIME_CHECK;
+ else
+ return NO_ERROR_SMIME;
+
+ close:
+ pclose(p);
+ err:
+ return res;
+}
+
+static char * get_first_from_addr(struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailimf_single_fields single_fields;
+ struct mailimf_fields * fields;
+ struct mailimf_mailbox * mb;
+
+ while (mime->mm_parent != NULL)
+ mime = mime->mm_parent;
+
+ if (mime->mm_type != MAILMIME_MESSAGE)
+ return NULL;
+
+ fields = mime->mm_data.mm_message.mm_fields;
+ if (fields == NULL)
+ return NULL;
+
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_from == NULL)
+ return NULL;
+
+ cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list);
+ if (cur == NULL)
+ return NULL;
+
+ mb = clist_content(cur);
+
+ return mb->mb_addr_spec;
+}
+
+#define SMIME_DECRYPT_DESCRIPTION "S/MIME encrypted part\r\n"
+#define SMIME_DECRYPT_FAILED "S/MIME decryption FAILED\r\n"
+#define SMIME_DECRYPT_SUCCESS "S/MIME decryption success\r\n"
+
+static int smime_decrypt(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char smime_filename[PATH_MAX];
+ char quoted_smime_filename[PATH_MAX];
+ char description_filename[PATH_MAX];
+ FILE * description_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ struct mailmime * description_mime;
+ struct mailmime * decrypted_mime;
+ int r;
+ int res;
+ int sign_ok;
+ char quoted_decrypted_filename[PATH_MAX];
+ struct mailmime * multipart;
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+ chashiter * iter;
+
+ /* fetch the whole multipart and write it to a file */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ smime_filename, sizeof(smime_filename),
+ msg, mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* we are in a safe directory */
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_smime;
+ }
+ fclose(decrypted_f);
+
+ sign_ok = 0;
+ for(iter = chash_begin(private_keys) ; iter != NULL ;
+ iter = chash_next(private_keys, iter)) {
+ chashdatum key;
+ char email_buf[BUF_SIZE];
+
+ chash_key(iter, &key);
+
+ if (key.len >= sizeof(email_buf))
+ continue;
+
+ strncpy(email_buf, key.data, key.len);
+ email_buf[key.len] = '\0';
+ email = email_buf;
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
+
+ /* get encryption key */
+
+#if 0
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ fclose(description_f);
+ res = MAIL_ERROR_INVAL;
+ goto unlink_description;
+ }
+#endif
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ fclose(description_f);
+ res = MAIL_ERROR_INVAL;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert, sizeof(quoted_smime_cert),
+ smime_cert);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_smime_key, sizeof(quoted_smime_key),
+ smime_key);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_smime_filename,
+ sizeof(quoted_smime_filename), smime_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, PATH_MAX,
+ "openssl smime -decrypt -in %s -out %s -inkey %s -recip %s",
+ quoted_smime_filename, quoted_decrypted_filename,
+ quoted_smime_key, quoted_smime_cert);
+
+ r = get_smime_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_SMIME:
+ sign_ok = 1;
+ break;
+ case ERROR_SMIME_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_SMIME_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_SMIME_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+
+ if (sign_ok) {
+ fprintf(description_f, SMIME_DECRYPT_SUCCESS);
+ fclose(description_f);
+ break;
+ }
+ else {
+ fclose(description_f);
+ unlink(description_filename);
+ }
+ }
+
+ if (!sign_ok) {
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
+ fprintf(description_f, SMIME_DECRYPT_FAILED);
+ fclose(description_f);
+ }
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the decrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ decrypted_filename, &decrypted_mime);
+ if (r == MAIL_NO_ERROR) {
+ /* adds the decrypted part */
+
+ r = mailmime_smart_add_part(multipart, decrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(decrypted_mime);
+ mailmime_free(decrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+ }
+
+ unlink(description_filename);
+ unlink(decrypted_filename);
+ unlink(smime_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ unlink_smime:
+ unlink(smime_filename);
+ err:
+ return res;
+}
+
+
+static int get_cert_from_sig(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime);
+
+#define SMIME_VERIFY_DESCRIPTION "S/MIME verify signed message\r\n"
+#define SMIME_VERIFY_FAILED "S/MIME verification FAILED\r\n"
+#define SMIME_VERIFY_SUCCESS "S/MIME verification success\r\n"
+
+static int
+smime_verify(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char smime_filename[PATH_MAX];
+ char quoted_smime_filename[PATH_MAX];
+ int res;
+ int r;
+ char command[PATH_MAX];
+ int sign_ok;
+ struct mailmime * description_mime;
+ FILE * description_f;
+ char description_filename[PATH_MAX];
+ struct mailmime * multipart;
+ char stripped_filename[PATH_MAX];
+ struct mailmime * stripped_mime;
+ char quoted_stripped_filename[PATH_MAX];
+ FILE * stripped_f;
+ char check_CA[PATH_MAX];
+ char quoted_CAfile[PATH_MAX];
+ char noverify[PATH_MAX];
+
+ if (store_cert)
+ get_cert_from_sig(privacy, msg, mime);
+
+ * check_CA = '\0';
+ if (CAfile != NULL) {
+ r = mail_quote_filename(quoted_CAfile, sizeof(quoted_CAfile), CAfile);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto err;
+ }
+
+ snprintf(check_CA, sizeof(check_CA), "-CAfile %s", quoted_CAfile);
+ }
+
+ * noverify = '\0';
+ if (!CA_check) {
+ snprintf(noverify, sizeof(noverify), "-noverify");
+ }
+
+ /* fetch the whole multipart and write it to a file */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ smime_filename, sizeof(smime_filename),
+ msg, mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ stripped_f = mailprivacy_get_tmp_file(privacy,
+ stripped_filename,
+ sizeof(stripped_filename));
+ if (stripped_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_smime;
+ }
+ fclose(stripped_f);
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_stripped;
+ }
+
+ fprintf(description_f, SMIME_VERIFY_DESCRIPTION);
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_smime_filename,
+ sizeof(quoted_smime_filename), smime_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_stripped_filename,
+ sizeof(quoted_stripped_filename), stripped_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, PATH_MAX, "openssl smime -verify -in %s -out %s %s %s",
+ quoted_smime_filename, quoted_stripped_filename, check_CA, noverify);
+
+ r = get_smime_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_SMIME:
+ sign_ok = 1;
+ break;
+ case ERROR_SMIME_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_SMIME_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_SMIME_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+ if (sign_ok)
+ fprintf(description_f, SMIME_VERIFY_SUCCESS);
+ else
+ fprintf(description_f, SMIME_VERIFY_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ stripped_filename, &stripped_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_description;
+ }
+
+ r = mailmime_smart_add_part(multipart, stripped_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(stripped_mime);
+ mailmime_free(stripped_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ unlink(description_filename);
+ unlink(stripped_filename);
+ unlink(smime_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_stripped:
+ unlink(stripped_filename);
+ unlink_smime:
+ unlink(smime_filename);
+ err:
+ return res;
+}
+
+static int smime_test_encrypted(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ return smime_is_signed(mime);
+
+ case MAILMIME_SINGLE:
+ return smime_is_encrypted(mime);
+ }
+
+ return 0;
+}
+
+static int smime_handler(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ int r;
+ struct mailmime * alternative_mime;
+
+ alternative_mime = NULL;
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ r = MAIL_ERROR_INVAL;
+ if (smime_is_signed(mime))
+ r = smime_verify(privacy, msg, mime, &alternative_mime);
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+
+ case MAILMIME_SINGLE:
+ r = MAIL_ERROR_INVAL;
+ if (smime_is_encrypted(mime))
+ r = smime_decrypt(privacy, msg, mime, &alternative_mime);
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+ }
+
+ return MAIL_ERROR_INVAL;
+}
+
+
+
+
+static void strip_mime_headers(struct mailmime * mime)
+{
+ struct mailmime_fields * fields;
+ clistiter * cur;
+
+ fields = mime->mm_mime_fields;
+
+ if (fields == NULL)
+ return;
+
+ for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+
+ if (field->fld_type == MAILMIME_FIELD_VERSION) {
+ mailmime_field_free(field);
+ clist_delete(fields->fld_list, cur);
+ break;
+ }
+ }
+}
+
+static int smime_sign(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char signed_filename[PATH_MAX];
+ FILE * signed_f;
+ int res;
+ int r;
+ int col;
+ char signature_filename[PATH_MAX];
+ FILE * signature_f;
+ char command[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ char quoted_signed_filename[PATH_MAX];
+ struct mailmime * signed_mime;
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+
+ /* get signing key */
+
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* part to sign */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ signed_f = mailprivacy_get_tmp_file(privacy,
+ signed_filename, sizeof(signed_filename));
+ if (signed_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(signed_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(signed_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+
+ fclose(signed_f);
+
+ /* prepare destination file for signature */
+
+ signature_f = mailprivacy_get_tmp_file(privacy,
+ signature_filename,
+ sizeof(signature_filename));
+ if (signature_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+ fclose(signature_f);
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_key,
+ sizeof(quoted_smime_key), smime_key);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert,
+ sizeof(quoted_smime_cert), smime_cert);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
+ quoted_signed_filename, quoted_signature_filename,
+ quoted_smime_cert, quoted_smime_key);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+ /* signature part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ signature_filename, &signed_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_signature;
+ }
+ strip_mime_headers(signed_mime);
+
+ unlink(signature_filename);
+ unlink(signed_filename);
+
+ * result = signed_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_signed:
+ unlink(signed_filename);
+ err:
+ return res;
+}
+
+
+/* ********************************************************************* */
+/* find S/MIME recipient */
+
+static int recipient_add_mb(char * recipient, size_t * len,
+ struct mailimf_mailbox * mb)
+{
+ char * filename;
+ char quoted_filename[PATH_MAX];
+ size_t buflen;
+ int r;
+
+ if (mb->mb_addr_spec == NULL)
+ return MAIL_NO_ERROR;
+
+ filename = get_cert_file(mb->mb_addr_spec);
+ if (filename == NULL)
+ return MAIL_ERROR_INVAL;
+
+ r = mail_quote_filename(quoted_filename, sizeof(quoted_filename),
+ filename);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ buflen = strlen(quoted_filename + 1);
+ if (buflen >= * len)
+ return MAIL_ERROR_MEMORY;
+
+ strncat(recipient, quoted_filename, * len);
+ (* len) -= buflen;
+ strncat(recipient, " ", * len);
+ (* len) --;
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_mb_list(char * recipient, size_t * len,
+ struct mailimf_mailbox_list * mb_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_mailbox * mb;
+
+ mb = clist_content(cur);
+
+ r = recipient_add_mb(recipient, len, mb);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_group(char * recipient, size_t * len,
+ struct mailimf_group * group)
+{
+ return recipient_add_mb_list(recipient, len, group->grp_mb_list);
+}
+
+static int recipient_add_addr(char * recipient, size_t * len,
+ struct mailimf_address * addr)
+{
+ int r;
+
+ switch (addr->ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox);
+ break;
+ case MAILIMF_ADDRESS_GROUP:
+ r = recipient_add_group(recipient, len, addr->ad_data.ad_group);
+ break;
+ default:
+ r = MAIL_ERROR_INVAL;
+ }
+
+ return r;
+}
+
+static int recipient_add_addr_list(char * recipient, size_t * len,
+ struct mailimf_address_list * addr_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_address * addr;
+
+ addr = clist_content(cur);
+
+ r = recipient_add_addr(recipient, len, addr);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int collect_smime_cert(char * recipient, size_t size,
+ struct mailimf_fields * fields)
+{
+ struct mailimf_single_fields single_fields;
+ int r;
+ size_t remaining;
+ int res;
+
+ * recipient = '\0';
+ remaining = size;
+
+ if (fields != NULL)
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_to != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_to->to_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_cc != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_cc->cc_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_bcc != NULL) {
+ if (single_fields.fld_bcc->bcc_addr_list != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_bcc->bcc_addr_list);
+ if (r < 0) {
+ res = r;
+ goto err;
+ }
+ }
+ }
+
+ return MAIL_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+
+static int smime_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ int res;
+ int r;
+ int col;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ struct mailmime * encrypted_mime;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+ r = collect_smime_cert(recipient, sizeof(recipient), fields);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(decrypted_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(decrypted_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fclose(decrypted_f);
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
+ quoted_decrypted_filename, quoted_encrypted_filename, recipient);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* encrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ encrypted_filename, &encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_encrypted;
+ }
+ strip_mime_headers(encrypted_mime);
+
+ unlink(encrypted_filename);
+ unlink(decrypted_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ err:
+ return res;
+}
+
+
+static int smime_sign_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ int res;
+ int r;
+ int col;
+ char signature_filename[PATH_MAX];
+ FILE * signature_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ struct mailmime * encrypted_mime;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+ r = collect_smime_cert(recipient, sizeof(recipient), fields);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* get signing key */
+
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename, sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(decrypted_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(decrypted_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fclose(decrypted_f);
+
+ /* prepare destination file for signature */
+
+ signature_f = mailprivacy_get_tmp_file(privacy,
+ signature_filename,
+ sizeof(signature_filename));
+ if (signature_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+ fclose(signature_f);
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_key,
+ sizeof(quoted_smime_key), smime_key);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert,
+ sizeof(quoted_smime_cert), smime_cert);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
+ quoted_decrypted_filename, quoted_signature_filename,
+ quoted_smime_cert, quoted_smime_key);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signature;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
+ quoted_signature_filename, quoted_encrypted_filename, recipient);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* encrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ encrypted_filename, &encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_encrypted;
+ }
+ strip_mime_headers(encrypted_mime);
+
+ unlink(encrypted_filename);
+ unlink(signature_filename);
+ unlink(decrypted_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ err:
+ return res;
+}
+
+
+
+static struct mailprivacy_encryption smime_encryption_tab[] = {
+ /* S/MIME signed part */
+ {
+ .name = "signed",
+ .description = "S/MIME signed part",
+ .encrypt = smime_sign,
+ },
+
+ /* S/MIME encrypted part */
+
+ {
+ .name = "encrypted",
+ .description = "S/MIME encrypted part",
+ .encrypt = smime_encrypt,
+ },
+
+ /* S/MIME signed & encrypted part */
+
+ {
+ .name = "signed-encrypted",
+ .description = "S/MIME signed & encrypted part",
+ .encrypt = smime_sign_encrypt,
+ },
+};
+
+static struct mailprivacy_protocol smime_protocol = {
+ .name = "smime",
+ .description = "S/MIME",
+
+ .is_encrypted = smime_test_encrypted,
+ .decrypt = smime_handler,
+
+ .encryption_count =
+ (sizeof(smime_encryption_tab) / sizeof(smime_encryption_tab[0])),
+
+ .encryption_tab = smime_encryption_tab,
+};
+
+int mailprivacy_smime_init(struct mailprivacy * privacy)
+{
+ certificates = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
+ if (certificates == NULL)
+ goto err;
+
+ private_keys = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
+ if (private_keys == NULL)
+ goto free_cert;
+
+ CAcert_dir[0] = '\0';
+
+ return mailprivacy_register(privacy, &smime_protocol);
+
+ free_cert:
+ chash_free(certificates);
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+void mailprivacy_smime_done(struct mailprivacy * privacy)
+{
+ mailprivacy_unregister(privacy, &smime_protocol);
+ chash_free(private_keys);
+ private_keys = NULL;
+ chash_free(certificates);
+ certificates = NULL;
+ if (CAfile != NULL) {
+ unlink(CAfile);
+ free(CAfile);
+ }
+ CAfile = NULL;
+ CAcert_dir[0] = '\0';
+}
+
+
+static void strip_string(char * str)
+{
+ char * p;
+ size_t len;
+
+ p = strchr(str, '\r');
+ if (p != NULL)
+ * p = 0;
+
+ p = strchr(str, '\n');
+ if (p != NULL)
+ * p = 0;
+
+ p = str;
+ while ((* p == ' ') || (* p == '\t')) {
+ p ++;
+ }
+
+ len = strlen(p);
+ memmove(str, p, len);
+ str[len] = 0;
+
+ if (len == 0)
+ return;
+
+ p = str;
+ len = len - 1;
+ while ((p[len] == ' ') || (p[len] == '\t')) {
+ p[len] = '\0';
+
+ if (len == 0)
+ break;
+
+ len --;
+ }
+}
+
+
+
+#define MAX_EMAIL_SIZE 1024
+
+static void set_file(chash * hash, char * email, char * filename)
+{
+ char * n;
+ char buf[MAX_EMAIL_SIZE];
+ chashdatum key;
+ chashdatum data;
+
+ strncpy(buf, email, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ for(n = buf ; * n != '\0' ; n ++)
+ * n = toupper((unsigned char) * n);
+ strip_string(buf);
+
+ key.data = buf;
+ key.len = strlen(buf);
+ data.data = filename;
+ data.len = strlen(filename) + 1;
+
+ chash_set(hash, &key, &data, NULL);
+}
+
+static char * get_file(chash * hash, char * email)
+{
+ chashdatum key;
+ chashdatum data;
+ char buf[MAX_EMAIL_SIZE];
+ char * n;
+ int r;
+
+ strncpy(buf, email, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ for(n = buf ; * n != '\0' ; n ++)
+ * n = toupper((unsigned char) * n);
+
+ strip_string(buf);
+ key.data = buf;
+ key.len = strlen(buf);
+ r = chash_get(hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ return data.data;
+}
+
+void mailprivacy_smime_set_cert_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+
+ chash_clear(certificates);
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ strncpy(cert_dir, directory, sizeof(cert_dir));
+ cert_dir[sizeof(cert_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL)
+ return;
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char command[PATH_MAX];
+ char buf[MAX_EMAIL_SIZE];
+ FILE * p;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ snprintf(command, sizeof(command),
+ "openssl x509 -email -noout -in %s 2>/dev/null", filename);
+
+ p = popen(command, "r");
+ if (p == NULL)
+ continue;
+
+ while (fgets(buf, sizeof(buf), p) != NULL)
+ set_file(certificates, buf, filename);
+
+ pclose(p);
+ }
+ closedir(dir);
+}
+
+static char * get_cert_file(char * email)
+{
+ return get_file(certificates, email);
+}
+
+static char * get_private_key_file(char * email)
+{
+ return get_file(private_keys, email);
+}
+
+void mail_private_smime_clear_private_keys(struct mailprivacy * privacy)
+{
+ chash_clear(private_keys);
+}
+
+#define MAX_BUF 1024
+
+void mailprivacy_smime_set_CA_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+ FILE * f_CA;
+ char CA_filename[PATH_MAX];
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ /* make a temporary file that contains all the CAs */
+
+ if (CAfile != NULL) {
+ unlink(CAfile);
+ free(CAfile);
+ CAfile = NULL;
+ }
+
+ f_CA = mailprivacy_get_tmp_file(privacy, CA_filename, sizeof(CA_filename));
+ if (f_CA == NULL)
+ return;
+
+ strncpy(CAcert_dir, directory, sizeof(CAcert_dir));
+ CAcert_dir[sizeof(CAcert_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL) {
+ fclose(f_CA);
+ goto unlink_CA;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char command[PATH_MAX];
+ char buf[MAX_BUF];
+ FILE * f;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ continue;
+
+ while (fgets(buf, sizeof(buf), f) != NULL)
+ fputs(buf, f_CA);
+
+ fclose(f);
+ }
+
+ closedir(dir);
+
+ fclose(f_CA);
+
+ CAfile = strdup(CA_filename);
+ if (CAfile == NULL)
+ goto unlink_CA;
+
+ return;
+
+ unlink_CA:
+ unlink(CA_filename);
+}
+
+void mailprivacy_smime_set_CA_check(struct mailprivacy * privacy,
+ int enabled)
+{
+ CA_check = enabled;
+}
+
+void mailprivacy_smime_set_store_cert(struct mailprivacy * privacy,
+ int enabled)
+{
+ store_cert = enabled;
+}
+
+static int get_cert_from_sig(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailmime * signed_mime;
+ struct mailmime * signature_mime;
+ int res;
+ char signature_filename[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ char * email;
+ char * cert_file;
+ char store_cert_filename[PATH_MAX];
+ char quoted_store_cert_filename[PATH_MAX];
+ int r;
+ char command[PATH_MAX];
+
+ if (* cert_dir == '\0')
+ return MAIL_ERROR_INVAL;
+
+ if (mime->mm_type != MAILMIME_MULTIPLE)
+ return MAIL_ERROR_INVAL;
+
+ email = get_first_from_addr(mime);
+ if (email == NULL)
+ return MAIL_ERROR_INVAL;
+
+ cert_file = get_cert_file(email);
+ if (cert_file != NULL)
+ return MAIL_NO_ERROR;
+
+ /* get the two parts of the S/MIME message */
+
+ cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signed_mime = cur->data;
+ cur = clist_next(cur);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signature_mime = cur->data;
+
+ r = mailprivacy_fetch_decoded_to_file(privacy,
+ signature_filename, sizeof(signature_filename),
+ msg, signature_mime);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(store_cert_filename, sizeof(store_cert_filename),
+ "%s/%s-cert.pem", cert_dir, email);
+
+ r = mail_quote_filename(quoted_store_cert_filename,
+ sizeof(quoted_store_cert_filename), store_cert_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl pkcs7 -inform DER -in %s -out %s -print_certs 2>/dev/null",
+ quoted_signature_filename, quoted_store_cert_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+ unlink(signature_filename);
+
+ set_file(certificates, email, store_cert_filename);
+
+ return MAIL_NO_ERROR;
+
+ unlink_signature:
+ unlink(signature_filename);
+ err:
+ return res;
+}
+
+
+static void set_private_key(struct mailprivacy * privacy,
+ char * email, char * file)
+{
+ set_file(private_keys, email, file);
+}
+
+#define PRIVATE_KEY_SUFFIX "-private-key.pem"
+
+void mailprivacy_smime_set_private_keys_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+
+ chash_clear(private_keys);
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ strncpy(private_keys_dir, directory, sizeof(private_keys_dir));
+ private_keys_dir[sizeof(private_keys_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL)
+ return;
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char email[PATH_MAX];
+ char * p;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ strncpy(email, ent->d_name, sizeof(email));
+ email[sizeof(email) - 1] = '\0';
+
+ p = strstr(email, PRIVATE_KEY_SUFFIX);
+ if (p == NULL)
+ continue;
+
+ if (strlen(p) != sizeof(PRIVATE_KEY_SUFFIX) - 1)
+ continue;
+
+ * p = 0;
+
+ if (* email == '\0')
+ continue;
+
+ set_private_key(privacy, email, filename);
+ }
+ closedir(dir);
+}