3052 lines
74 KiB
C
3052 lines
74 KiB
C
/*
|
|
* 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: mailprivacy_gnupg.c,v 1.14 2011/05/03 16:30:22 hoa Exp $
|
|
*/
|
|
|
|
/* passphrase is needed when private key is needed
|
|
private key is needed :
|
|
- to sign a message
|
|
- and to decrypt a message
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include "mailprivacy_gnupg.h"
|
|
|
|
#include "mailprivacy.h"
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#ifdef WIN32
|
|
# include "win_etpan.h"
|
|
#else
|
|
# include <sys/mman.h>
|
|
# include <sys/wait.h>
|
|
#endif
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "mailprivacy_tools.h"
|
|
#include "mailprivacy_tools_private.h"
|
|
#include <libetpan/mailmime.h>
|
|
#include <libetpan/libetpan-config.h>
|
|
#ifdef LIBETPAN_REENTRANT
|
|
#if defined(HAVE_PTHREAD_H) && !defined(IGNORE_PTHREAD_H)
|
|
#include <pthread.h>
|
|
#elif (defined WIN32)
|
|
#include <windows.h>
|
|
#endif
|
|
#endif
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
|
|
#include "../data-types/syscall_wrappers.h"
|
|
|
|
enum {
|
|
NO_ERROR_PGP = 0,
|
|
ERROR_PGP_CHECK,
|
|
ERROR_PGP_COMMAND,
|
|
ERROR_PGP_FILE,
|
|
ERROR_PGP_NOPASSPHRASE
|
|
};
|
|
|
|
static int mailprivacy_gnupg_add_encryption_id(struct mailprivacy * privacy,
|
|
mailmessage * msg, char * encryption_id);
|
|
static char * get_passphrase(struct mailprivacy * privacy,
|
|
char * user_id);
|
|
static int get_userid(char * filename, char * username, size_t length);
|
|
|
|
|
|
static int gpg_command_passphrase(struct mailprivacy * privacy,
|
|
struct mailmessage * msg,
|
|
char * command, char * userid,
|
|
char * stdoutfile, char * stderrfile)
|
|
{
|
|
char * passphrase;
|
|
int bad_passphrase;
|
|
int res;
|
|
int r;
|
|
|
|
bad_passphrase = 0;
|
|
|
|
passphrase = NULL;
|
|
if (userid != NULL)
|
|
passphrase = get_passphrase(privacy, userid);
|
|
|
|
res = mailprivacy_spawn_and_wait(command, passphrase, stdoutfile, stderrfile,
|
|
&bad_passphrase);
|
|
if (res != NO_ERROR_PASSPHRASE) {
|
|
switch (res) {
|
|
case ERROR_PASSPHRASE_COMMAND:
|
|
return ERROR_PGP_COMMAND;
|
|
case ERROR_PASSPHRASE_FILE:
|
|
return ERROR_PGP_FILE;
|
|
default:
|
|
return ERROR_PGP_COMMAND;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
if (bad_passphrase && (userid == NULL)) {
|
|
char encryption_id[4096];
|
|
|
|
encryption_id[0] = '\0';
|
|
r = get_userid(stderrfile, encryption_id, sizeof(encryption_id));
|
|
if (r == 0) {
|
|
passphrase = get_passphrase(privacy, encryption_id);
|
|
if (passphrase == NULL) {
|
|
mailprivacy_gnupg_add_encryption_id(privacy, msg, encryption_id);
|
|
return ERROR_PGP_NOPASSPHRASE;
|
|
}
|
|
else {
|
|
free(passphrase);
|
|
return gpg_command_passphrase(privacy, msg, command, encryption_id,
|
|
stdoutfile, stderrfile);
|
|
}
|
|
}
|
|
else {
|
|
return ERROR_PGP_CHECK;
|
|
}
|
|
}
|
|
|
|
if (bad_passphrase && (passphrase != NULL)) {
|
|
return ERROR_PGP_CHECK;
|
|
}
|
|
|
|
if (bad_passphrase) {
|
|
mailprivacy_gnupg_add_encryption_id(privacy, msg, userid);
|
|
return ERROR_PGP_NOPASSPHRASE;
|
|
}
|
|
|
|
return NO_ERROR_PGP;
|
|
}
|
|
|
|
static int pgp_is_encrypted(struct mailmime * mime)
|
|
{
|
|
if (mime->mm_content_type != NULL) {
|
|
clistiter * cur;
|
|
|
|
if (strcasecmp(mime->mm_content_type->ct_subtype, "encrypted") != 0)
|
|
return 0;
|
|
|
|
for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
|
|
cur = clist_next(cur)) {
|
|
struct mailmime_parameter * param;
|
|
|
|
param = clist_content(cur);
|
|
|
|
if ((strcasecmp(param->pa_name, "protocol") == 0) &&
|
|
(strcasecmp(param->pa_value, "application/pgp-encrypted") == 0))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pgp_is_signed(struct mailmime * mime)
|
|
{
|
|
if (mime->mm_content_type != NULL) {
|
|
clistiter * cur;
|
|
|
|
if (strcasecmp(mime->mm_content_type->ct_subtype, "signed") != 0)
|
|
return 0;
|
|
|
|
for(cur = clist_begin(mime->mm_content_type->ct_parameters) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_parameter * param;
|
|
|
|
param = clist_content(cur);
|
|
|
|
if ((strcasecmp(param->pa_name, "protocol") == 0) &&
|
|
(strcasecmp(param->pa_value, "application/pgp-signature") == 0))
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define PGP_SIGNED "-----BEGIN PGP SIGNED MESSAGE-----"
|
|
|
|
int pgp_is_clearsigned(char * data, size_t len)
|
|
{
|
|
if (len >= strlen(PGP_SIGNED))
|
|
if (strncmp(data, PGP_SIGNED, sizeof(PGP_SIGNED) - 1) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define PGP_CRYPTED "-----BEGIN PGP MESSAGE-----"
|
|
|
|
int pgp_is_crypted_armor(char * data, size_t len)
|
|
{
|
|
if (len >= strlen(PGP_CRYPTED))
|
|
if (strncmp(data, PGP_CRYPTED, sizeof(PGP_CRYPTED) - 1) == 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#if 0
|
|
#define BUF_SIZE 1024
|
|
|
|
/* write output to a file */
|
|
|
|
static int get_pgp_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_PGP_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_PGP_FILE;
|
|
goto Close;
|
|
}
|
|
}
|
|
status = pclose(p);
|
|
|
|
if (WEXITSTATUS(status) != 0)
|
|
return ERROR_PGP_CHECK;
|
|
else
|
|
return NO_ERROR_PGP;
|
|
|
|
Close:
|
|
pclose(p);
|
|
err:
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
/* parse output */
|
|
|
|
enum {
|
|
STATE_USERID,
|
|
STATE_NORMAL
|
|
};
|
|
|
|
static int get_userid(char * filename, char * username, size_t length)
|
|
{
|
|
FILE * f;
|
|
int state;
|
|
char buffer[4096];
|
|
int exit_code;
|
|
int r;
|
|
|
|
exit_code = -1;
|
|
|
|
f = Fopen(filename, "r");
|
|
if (f == NULL)
|
|
goto exit;
|
|
|
|
state = STATE_NORMAL;
|
|
while (Fgets(buffer, sizeof(buffer), f) != NULL) {
|
|
|
|
switch (state) {
|
|
case STATE_NORMAL:
|
|
if (strncmp(buffer, "gpg: encrypted", 14) == 0)
|
|
state = STATE_USERID;
|
|
break;
|
|
|
|
case STATE_USERID:
|
|
{
|
|
struct mailimf_mailbox * mb;
|
|
size_t current_index;
|
|
int r;
|
|
size_t buflen;
|
|
size_t i;
|
|
char * beginning;
|
|
|
|
/* find double-quotes and remove beginning and ending */
|
|
|
|
buflen = strlen(buffer);
|
|
for(i = buflen - 1 ; 1 ; i --) {
|
|
if (buffer[i] == '\"') {
|
|
buffer[i] = '\0';
|
|
break;
|
|
}
|
|
|
|
if (i == 0)
|
|
break;
|
|
}
|
|
|
|
beginning = buffer;
|
|
for(i = 0 ; i < buflen ; i ++) {
|
|
if (buffer[i] == '\"') {
|
|
beginning = buffer + i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
r = mailimf_mailbox_parse(beginning, strlen(beginning),
|
|
¤t_index, &mb);
|
|
if (r == MAILIMF_NO_ERROR) {
|
|
strncpy(username, mb->mb_addr_spec, length);
|
|
username[length - 1] = '\0';
|
|
mailimf_mailbox_free(mb);
|
|
exit_code = 0;
|
|
}
|
|
|
|
state = STATE_NORMAL;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
Fclose(f);
|
|
|
|
exit:
|
|
return exit_code;
|
|
}
|
|
|
|
#define PGP_DECRYPT_DESCRIPTION "PGP encrypted part\r\n"
|
|
#define PGP_DECRYPT_FAILED "PGP decryption FAILED\r\n"
|
|
#define PGP_DECRYPT_SUCCESS "PGP decryption success\r\n"
|
|
|
|
/* extracted from mailprivacy_smime.c -- begin */
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
/* extracted from mailprivacy_smime.c -- end */
|
|
|
|
static int pgp_decrypt(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
struct mailmime * version_mime;
|
|
struct mailmime * encrypted_mime;
|
|
clistiter * cur;
|
|
char encrypted_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char decrypted_filename[PATH_MAX];
|
|
char command[PATH_MAX];
|
|
struct mailmime * description_mime;
|
|
struct mailmime * decrypted_mime;
|
|
int r;
|
|
int res;
|
|
int decrypt_ok;
|
|
char quoted_encrypted_filename[PATH_MAX];
|
|
struct mailmime * multipart;
|
|
|
|
/* get the two parts of the PGP message */
|
|
|
|
cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
|
|
if (cur == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
version_mime = clist_content(cur);
|
|
cur = clist_next(cur);
|
|
if (cur == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
encrypted_mime = clist_content(cur);
|
|
|
|
/* fetch the second section, that's the useful one */
|
|
|
|
r = mailprivacy_fetch_decoded_to_file(privacy,
|
|
encrypted_filename, sizeof(encrypted_filename),
|
|
msg, encrypted_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto err;
|
|
}
|
|
|
|
/* we are in a safe directory */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy,
|
|
decrypted_filename, sizeof(decrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
/* description */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
/* run the command */
|
|
|
|
r = mail_quote_filename(quoted_encrypted_filename,
|
|
sizeof(quoted_encrypted_filename), encrypted_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 --batch --yes --decrypt '%s'",
|
|
quoted_encrypted_filename);
|
|
|
|
decrypt_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
decrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
decrypt_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
decrypt_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!decrypt_ok) {
|
|
char encryption_id[4096];
|
|
|
|
encryption_id[0] = '\0';
|
|
r = get_userid(description_filename, encryption_id, sizeof(encryption_id));
|
|
if (r == 0) {
|
|
mailprivacy_gnupg_add_encryption_id(privacy, msg, encryption_id);
|
|
}
|
|
}
|
|
|
|
/* 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, 0,
|
|
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(encrypted_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_decrypted:
|
|
unlink(decrypted_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
#define PGP_VERIFY_DESCRIPTION "PGP verify signed message\r\n"
|
|
#define PGP_VERIFY_FAILED "PGP verification FAILED\r\n"
|
|
#define PGP_VERIFY_SUCCESS "PGP verification success\r\n"
|
|
|
|
static int
|
|
pgp_verify(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
struct mailmime * signed_mime;
|
|
struct mailmime * signature_mime;
|
|
char signed_filename[PATH_MAX];
|
|
char signature_filename[PATH_MAX];
|
|
int res;
|
|
int r;
|
|
clistiter * cur;
|
|
char command[PATH_MAX];
|
|
int sign_ok;
|
|
struct mailmime * description_mime;
|
|
char decrypted_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char quoted_signed_filename[PATH_MAX];
|
|
char quoted_signature_filename[PATH_MAX];
|
|
struct mailmime * multipart;
|
|
struct mailmime * signed_msg_mime;
|
|
|
|
/* get the two parts of the PGP message */
|
|
|
|
cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
|
|
if (cur == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
signed_mime = clist_content(cur);
|
|
cur = clist_next(cur);
|
|
if (cur == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
signature_mime = clist_content(cur);
|
|
|
|
/* fetch signed part and write it to a file */
|
|
|
|
r = mailprivacy_fetch_mime_body_to_file(privacy,
|
|
signed_filename, sizeof(signed_filename),
|
|
msg, signed_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto err;
|
|
}
|
|
|
|
/* fetch signed part and write it to a file */
|
|
|
|
r = mailprivacy_fetch_decoded_to_file(privacy,
|
|
signature_filename, sizeof(signature_filename),
|
|
msg, signature_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_signed;
|
|
}
|
|
|
|
/* description */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_signature;
|
|
}
|
|
|
|
/* decrypted (dummy) */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, decrypted_filename,
|
|
sizeof(decrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* run the command */
|
|
|
|
r = mail_quote_filename(quoted_signature_filename,
|
|
sizeof(quoted_signature_filename), signature_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_signed_filename,
|
|
sizeof(quoted_signed_filename), signed_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
snprintf(command, sizeof(command), "gpg --batch --yes --verify '%s' '%s'",
|
|
quoted_signature_filename, quoted_signed_filename);
|
|
|
|
sign_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
decrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
sign_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
sign_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_decrypted;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
/* building multipart */
|
|
|
|
r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
/* 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_decrypted;
|
|
}
|
|
|
|
/* 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_decrypted;
|
|
}
|
|
|
|
r = mailprivacy_get_part_from_file(privacy, 1, 0,
|
|
signed_filename, &signed_msg_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, signed_msg_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(signed_msg_mime);
|
|
mailmime_free(signed_msg_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
unlink(decrypted_filename);
|
|
unlink(description_filename);
|
|
unlink(signature_filename);
|
|
unlink(signed_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_decrypted:
|
|
unlink(decrypted_filename);
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_signature:
|
|
unlink(signature_filename);
|
|
unlink_signed:
|
|
unlink(signed_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
#define PGP_CLEAR_VERIFY_DESCRIPTION "PGP verify clear signed message\r\n"
|
|
#define PGP_CLEAR_VERIFY_FAILED "PGP verification FAILED\r\n"
|
|
#define PGP_CLEAR_VERIFY_SUCCESS "PGP verification success\r\n"
|
|
|
|
static int pgp_verify_clearsigned(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime,
|
|
char * content, size_t content_len, struct mailmime ** result)
|
|
{
|
|
int r;
|
|
char command[PATH_MAX];
|
|
int res;
|
|
int sign_ok;
|
|
size_t written;
|
|
char signed_filename[PATH_MAX];
|
|
FILE * signed_f;
|
|
char stripped_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char quoted_signed_filename[PATH_MAX];
|
|
struct mailmime * stripped_mime;
|
|
struct mailmime * description_mime;
|
|
struct mailmime * multipart;
|
|
struct mailmime_content * content_type;
|
|
|
|
if (mime->mm_parent == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
signed_f = mailprivacy_get_tmp_file(privacy,
|
|
signed_filename, sizeof(signed_filename));
|
|
if (signed_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
// BUGS: many other errors are not covered
|
|
written = Fwrite(content, 1, content_len, signed_f);
|
|
if (written != content_len) {
|
|
Fclose(signed_f);
|
|
unlink(signed_filename);
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
Fclose(signed_f);
|
|
|
|
/* XXX - prepare file for PGP, remove trailing WS */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, stripped_filename,
|
|
sizeof(stripped_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_signed;
|
|
}
|
|
|
|
/* description */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_stripped;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_signed_filename,
|
|
sizeof(quoted_signed_filename), signed_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --batch --yes --decrypt '%s'", quoted_signed_filename);
|
|
|
|
sign_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
stripped_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
sign_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
sign_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* building the signature stripped part */
|
|
|
|
stripped_mime = mailprivacy_new_file_part(privacy,
|
|
stripped_filename,
|
|
"application/octet-stream",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (stripped_mime == NULL) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* place original content type */
|
|
|
|
content_type = mailmime_content_dup(mime->mm_content_type);
|
|
if (content_type == NULL) {
|
|
mailprivacy_mime_clear(stripped_mime);
|
|
mailmime_free(stripped_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
mailmime_content_free(stripped_mime->mm_content_type);
|
|
stripped_mime->mm_content_type = content_type;
|
|
|
|
/* place original MIME fields */
|
|
|
|
if (mime->mm_mime_fields != NULL) {
|
|
struct mailmime_fields * mime_fields;
|
|
clistiter * cur;
|
|
|
|
mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
|
|
if (mime_fields == NULL) {
|
|
mailprivacy_mime_clear(stripped_mime);
|
|
mailmime_free(stripped_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
for(cur = clist_begin(mime_fields->fld_list) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = clist_content(cur);
|
|
if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
|
|
mailmime_field_free(field);
|
|
clist_delete(mime_fields->fld_list, cur);
|
|
break;
|
|
}
|
|
}
|
|
clist_concat(stripped_mime->mm_mime_fields->fld_list,
|
|
mime_fields->fld_list);
|
|
mailmime_fields_free(mime_fields);
|
|
}
|
|
|
|
/* adds the stripped part */
|
|
|
|
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(signed_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_stripped:
|
|
unlink(stripped_filename);
|
|
unlink_signed:
|
|
unlink(signed_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
#define PGP_DECRYPT_ARMOR_DESCRIPTION "PGP ASCII armor encrypted part\r\n"
|
|
#define PGP_DECRYPT_ARMOR_FAILED "PGP ASCII armor decryption FAILED\r\n"
|
|
#define PGP_DECRYPT_ARMOR_SUCCESS "PGP ASCII armor decryption success\r\n"
|
|
|
|
static int pgp_decrypt_armor(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime,
|
|
char * content, size_t content_len, struct mailmime ** result)
|
|
{
|
|
FILE * encrypted_f;
|
|
char encrypted_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char decrypted_filename[PATH_MAX];
|
|
size_t written;
|
|
char command[PATH_MAX];
|
|
struct mailmime * description_mime;
|
|
struct mailmime * decrypted_mime;
|
|
struct mailmime * multipart;
|
|
int r;
|
|
int res;
|
|
int sign_ok;
|
|
char quoted_encrypted_filename[PATH_MAX];
|
|
|
|
if (mime->mm_parent == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
encrypted_f = mailprivacy_get_tmp_file(privacy,
|
|
encrypted_filename,
|
|
sizeof(encrypted_filename));
|
|
if (encrypted_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
written = Fwrite(content, 1, content_len, encrypted_f);
|
|
if (written != content_len) {
|
|
Fclose(encrypted_f);
|
|
unlink(encrypted_filename);
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
Fclose(encrypted_f);
|
|
|
|
/* we are in a safe directory */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, decrypted_filename,
|
|
sizeof(decrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
/* description */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_decrypted;
|
|
}
|
|
|
|
/* run the command */
|
|
|
|
r = mail_quote_filename(quoted_encrypted_filename,
|
|
sizeof(quoted_encrypted_filename), encrypted_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 --batch --yes --decrypt '%s'",
|
|
quoted_encrypted_filename);
|
|
|
|
sign_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
decrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
sign_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
sign_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* 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, 0,
|
|
decrypted_filename, &decrypted_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* 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(encrypted_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_decrypted:
|
|
unlink(decrypted_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
static int mime_is_text(struct mailmime * build_info)
|
|
{
|
|
if (build_info->mm_type == MAILMIME_SINGLE) {
|
|
if (build_info->mm_content_type != NULL) {
|
|
if (build_info->mm_content_type->ct_type->tp_type ==
|
|
MAILMIME_TYPE_DISCRETE_TYPE) {
|
|
if (build_info->mm_content_type->ct_type->tp_data.tp_discrete_type->dt_type ==
|
|
MAILMIME_DISCRETE_TYPE_TEXT)
|
|
return 1;
|
|
}
|
|
}
|
|
else
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int pgp_test_encrypted(struct mailprivacy * privacy,
|
|
mailmessage * msg, struct mailmime * mime)
|
|
{
|
|
int r;
|
|
int res;
|
|
|
|
switch (mime->mm_type) {
|
|
case MAILMIME_MULTIPLE:
|
|
return (pgp_is_encrypted(mime) || pgp_is_signed(mime));
|
|
|
|
case MAILMIME_SINGLE:
|
|
/* clear sign or ASCII armor encryption */
|
|
if (mime_is_text(mime)) {
|
|
char * content;
|
|
size_t content_len;
|
|
char * parsed_content;
|
|
size_t parsed_content_len;
|
|
size_t cur_token;
|
|
int encoding;
|
|
struct mailmime_single_fields single_fields;
|
|
|
|
r = mailprivacy_msg_fetch_section(privacy, msg, mime,
|
|
&content, &content_len);
|
|
if (r != MAIL_NO_ERROR)
|
|
return 0;
|
|
|
|
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
|
|
mime->mm_content_type);
|
|
if (single_fields.fld_encoding != NULL)
|
|
encoding = single_fields.fld_encoding->enc_type;
|
|
else
|
|
encoding = MAILMIME_MECHANISM_8BIT;
|
|
|
|
cur_token = 0;
|
|
r = mailmime_part_parse(content, content_len, &cur_token,
|
|
encoding, &parsed_content, &parsed_content_len);
|
|
mailprivacy_msg_fetch_result_free(privacy, msg, content);
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return 0;
|
|
|
|
res = 0;
|
|
|
|
if (pgp_is_clearsigned(parsed_content, parsed_content_len))
|
|
res = 1;
|
|
else if (pgp_is_crypted_armor(parsed_content, parsed_content_len))
|
|
res = 1;
|
|
|
|
mmap_string_unref(parsed_content);
|
|
|
|
return res;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pgp_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 (pgp_is_encrypted(mime)) {
|
|
r = pgp_decrypt(privacy, msg, mime, &alternative_mime);
|
|
}
|
|
else if (pgp_is_signed(mime)) {
|
|
r = pgp_verify(privacy, msg, mime, &alternative_mime);
|
|
}
|
|
|
|
if (r != MAIL_NO_ERROR)
|
|
return r;
|
|
|
|
* result = alternative_mime;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
case MAILMIME_SINGLE:
|
|
/* clear sign or ASCII armor encryption */
|
|
if (mime_is_text(mime)) {
|
|
char * content;
|
|
size_t content_len;
|
|
char * parsed_content;
|
|
size_t parsed_content_len;
|
|
size_t cur_token;
|
|
int encoding;
|
|
struct mailmime_single_fields single_fields;
|
|
|
|
r = mailprivacy_msg_fetch_section(privacy, msg, mime,
|
|
&content, &content_len);
|
|
if (r != MAIL_NO_ERROR)
|
|
return MAIL_ERROR_FETCH;
|
|
|
|
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
|
|
mime->mm_content_type);
|
|
if (single_fields.fld_encoding != NULL)
|
|
encoding = single_fields.fld_encoding->enc_type;
|
|
else
|
|
encoding = MAILMIME_MECHANISM_8BIT;
|
|
|
|
cur_token = 0;
|
|
r = mailmime_part_parse(content, content_len, &cur_token,
|
|
encoding, &parsed_content, &parsed_content_len);
|
|
mailprivacy_msg_fetch_result_free(privacy, msg, content);
|
|
|
|
if (r != MAILIMF_NO_ERROR)
|
|
return MAIL_ERROR_PARSE;
|
|
|
|
r = MAIL_ERROR_INVAL;
|
|
if (pgp_is_clearsigned(parsed_content,
|
|
parsed_content_len)) {
|
|
r = pgp_verify_clearsigned(privacy,
|
|
msg, mime, parsed_content, parsed_content_len, &alternative_mime);
|
|
}
|
|
else if (pgp_is_crypted_armor(parsed_content,
|
|
parsed_content_len)) {
|
|
r = pgp_decrypt_armor(privacy,
|
|
msg, mime, parsed_content, parsed_content_len, &alternative_mime);
|
|
}
|
|
|
|
mmap_string_unref(parsed_content);
|
|
|
|
if (r != MAIL_NO_ERROR)
|
|
return r;
|
|
|
|
* result = alternative_mime;
|
|
|
|
return MAIL_NO_ERROR;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return MAIL_ERROR_INVAL;
|
|
}
|
|
|
|
|
|
#if 0
|
|
static void prepare_mime_single(struct mailmime * mime)
|
|
{
|
|
struct mailmime_single_fields single_fields;
|
|
int encoding;
|
|
int r;
|
|
|
|
if (mime->mime_fields != NULL) {
|
|
mailmime_single_fields_init(&single_fields, mime->mime_fields,
|
|
mime->content_type);
|
|
if (single_fields.encoding != NULL) {
|
|
encoding = single_fields.encoding->type;
|
|
switch (encoding) {
|
|
case MAILMIME_MECHANISM_8BIT:
|
|
case MAILMIME_MECHANISM_7BIT:
|
|
case MAILMIME_MECHANISM_BINARY:
|
|
single_fields.encoding->type = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
struct mailmime_mechanism * mechanism;
|
|
struct mailmime_field * field;
|
|
|
|
mechanism =
|
|
mailmime_mechanism_new(MAILMIME_MECHANISM_QUOTED_PRINTABLE, NULL);
|
|
if (mechanism == NULL)
|
|
return;
|
|
|
|
field = mailmime_field_new(MAILMIME_FIELD_TRANSFER_ENCODING,
|
|
NULL, mechanism, NULL, NULL, 0, NULL, NULL);
|
|
if (field == NULL) {
|
|
mailmime_mechanism_free(mechanism);
|
|
return;
|
|
}
|
|
|
|
r = clist_append(mime->mime_fields->list, field);
|
|
if (r < 0) {
|
|
mailmime_field_free(field);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (mime->body->encoding) {
|
|
case MAILMIME_MECHANISM_8BIT:
|
|
case MAILMIME_MECHANISM_7BIT:
|
|
case MAILMIME_MECHANISM_BINARY:
|
|
mime->body->encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
|
|
mime->body->encoded = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
prepare_mime()
|
|
|
|
we assume we built ourself the message.
|
|
*/
|
|
|
|
static void prepare_mime(struct mailmime * mime)
|
|
{
|
|
clistiter * cur;
|
|
|
|
switch (mime->type) {
|
|
case MAILMIME_SINGLE:
|
|
if (mime->body != NULL) {
|
|
prepare_mime_single(mime);
|
|
}
|
|
break;
|
|
|
|
case MAILMIME_MULTIPLE:
|
|
for(cur = clist_begin(mime->list) ; cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime * child;
|
|
|
|
child = cur->data;
|
|
|
|
prepare_mime(child);
|
|
}
|
|
break;
|
|
|
|
case MAILMIME_MESSAGE:
|
|
if (mime->msg_mime) {
|
|
prepare_mime(mime->msg_mime);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int pgp_sign_mime(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char to_sign_filename[PATH_MAX];
|
|
char quoted_to_sign_filename[PATH_MAX];
|
|
FILE * to_sign_f;
|
|
int res;
|
|
int r;
|
|
int col;
|
|
char description_filename[PATH_MAX];
|
|
char signature_filename[PATH_MAX];
|
|
char command[PATH_MAX];
|
|
char default_key[PATH_MAX];
|
|
struct mailmime * signature_mime;
|
|
struct mailmime * multipart;
|
|
struct mailmime_content * content;
|
|
struct mailmime_parameter * param;
|
|
struct mailmime * to_sign_msg_mime;
|
|
char * dup_signature_filename;
|
|
char * email;
|
|
int sign_ok;
|
|
|
|
/* get signing key */
|
|
|
|
* default_key = '\0';
|
|
email = get_first_from_addr(mime);
|
|
if (email != NULL)
|
|
snprintf(default_key, sizeof(default_key),
|
|
"--default-key %s", email);
|
|
|
|
/* part to sign */
|
|
|
|
/* encode quoted printable all text parts */
|
|
|
|
mailprivacy_prepare_mime(mime);
|
|
|
|
to_sign_f = mailprivacy_get_tmp_file(privacy,
|
|
to_sign_filename, sizeof(to_sign_filename));
|
|
if (to_sign_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_write(to_sign_f, &col, mime);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(to_sign_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_to_sign;
|
|
}
|
|
|
|
Fclose(to_sign_f);
|
|
|
|
/* prepare destination file for signature */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, signature_filename,
|
|
sizeof(signature_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_to_sign;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_signature;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_to_sign_filename,
|
|
sizeof(quoted_to_sign_filename), to_sign_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 -a --batch --yes --digest-algo sha1 %s -b '%s'",
|
|
default_key, quoted_to_sign_filename);
|
|
|
|
sign_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
signature_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
sign_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
sign_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!sign_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* multipart */
|
|
|
|
multipart = mailprivacy_new_file_part(privacy, NULL,
|
|
"multipart/signed", -1);
|
|
|
|
content = multipart->mm_content_type;
|
|
|
|
param = mailmime_param_new_with_data("micalg", "pgp-sha1");
|
|
if (param == NULL) {
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
r = clist_append(content->ct_parameters, param);
|
|
if (r < 0) {
|
|
mailmime_parameter_free(param);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
param = mailmime_param_new_with_data("protocol",
|
|
"application/pgp-signature");
|
|
if (param == NULL) {
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
r = clist_append(content->ct_parameters, param);
|
|
if (r < 0) {
|
|
mailmime_parameter_free(param);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* signed part */
|
|
|
|
r = mailprivacy_get_part_from_file(privacy, 1, 0,
|
|
to_sign_filename, &to_sign_msg_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_description;
|
|
}
|
|
|
|
mailprivacy_prepare_mime(to_sign_msg_mime);
|
|
|
|
r = mailmime_smart_add_part(multipart, to_sign_msg_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(to_sign_msg_mime);
|
|
mailmime_free(to_sign_msg_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* signature part */
|
|
|
|
/* reencode the signature file with CRLF */
|
|
dup_signature_filename = mailprivacy_dup_imf_file(privacy,
|
|
signature_filename);
|
|
if (dup_signature_filename == NULL) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* replace the signature file */
|
|
unlink(signature_filename);
|
|
strncpy(signature_filename,
|
|
dup_signature_filename, sizeof(signature_filename));
|
|
signature_filename[sizeof(signature_filename) - 1] = '\0';
|
|
|
|
signature_mime = mailprivacy_new_file_part(privacy,
|
|
signature_filename,
|
|
"application/pgp-signature",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (signature_mime == NULL) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, signature_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(signature_mime);
|
|
mailmime_free(signature_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
unlink(description_filename);
|
|
unlink(signature_filename);
|
|
unlink(to_sign_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_signature:
|
|
unlink(signature_filename);
|
|
unlink_to_sign:
|
|
unlink(to_sign_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
/* ********************************************************************* */
|
|
/* find GPG recipient */
|
|
|
|
static int recipient_add_mb(char * recipient, size_t * len,
|
|
struct mailimf_mailbox * mb)
|
|
{
|
|
char buffer[PATH_MAX];
|
|
size_t buflen;
|
|
|
|
if (mb->mb_addr_spec != NULL) {
|
|
snprintf(buffer, sizeof(buffer), "-r %s ", mb->mb_addr_spec);
|
|
buflen = strlen(buffer);
|
|
if (buflen >= * len)
|
|
return MAIL_ERROR_MEMORY;
|
|
|
|
strncat(recipient, buffer, * len);
|
|
(* len) -= buflen;
|
|
}
|
|
|
|
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_recipient(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 - 1;
|
|
|
|
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 != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
#define PGP_VERSION "Version: 1\r\n"
|
|
|
|
static int pgp_sign_encrypt_mime(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char original_filename[PATH_MAX];
|
|
FILE * original_f;
|
|
int res;
|
|
int r;
|
|
int col;
|
|
char encrypted_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char version_filename[PATH_MAX];
|
|
FILE * version_f;
|
|
char command[PATH_MAX];
|
|
char quoted_original_filename[PATH_MAX];
|
|
struct mailmime * version_mime;
|
|
struct mailmime * multipart;
|
|
struct mailmime_content * content;
|
|
struct mailmime_parameter * param;
|
|
struct mailmime * encrypted_mime;
|
|
char recipient[PATH_MAX];
|
|
struct mailimf_fields * fields;
|
|
struct mailmime * root;
|
|
size_t written;
|
|
char * email;
|
|
int encrypt_ok;
|
|
char default_key[PATH_MAX];
|
|
|
|
/* get signing key */
|
|
|
|
* default_key = '\0';
|
|
email = get_first_from_addr(mime);
|
|
if (email != NULL)
|
|
snprintf(default_key, sizeof(default_key),
|
|
"--default-key %s", 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 */
|
|
|
|
collect_recipient(recipient, sizeof(recipient), fields);
|
|
|
|
/* part to encrypt */
|
|
|
|
/* encode quoted printable all text parts */
|
|
|
|
mailprivacy_prepare_mime(mime);
|
|
|
|
original_f = mailprivacy_get_tmp_file(privacy, original_filename,
|
|
sizeof(original_filename));
|
|
if (original_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_write(original_f, &col, mime);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(original_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
|
|
Fclose(original_f);
|
|
|
|
/* prepare destination file for encryption */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, encrypted_filename,
|
|
sizeof(encrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_original;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_original_filename,
|
|
sizeof(quoted_original_filename), original_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 %s -a --batch --yes --digest-algo sha1 -s %s -e '%s'",
|
|
recipient, default_key, quoted_original_filename);
|
|
|
|
encrypt_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
encrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
encrypt_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
encrypt_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!encrypt_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* multipart */
|
|
|
|
multipart = mailprivacy_new_file_part(privacy, NULL,
|
|
"multipart/encrypted", -1);
|
|
|
|
content = multipart->mm_content_type;
|
|
|
|
param = mailmime_param_new_with_data("protocol",
|
|
"application/pgp-encrypted");
|
|
if (param == NULL) {
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
r = clist_append(content->ct_parameters, param);
|
|
if (r < 0) {
|
|
mailmime_parameter_free(param);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* version part */
|
|
|
|
version_f = mailprivacy_get_tmp_file(privacy,
|
|
version_filename,
|
|
sizeof(version_filename));
|
|
if (version_f == NULL) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
written = Fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
|
|
if (written != sizeof(PGP_VERSION) - 1) {
|
|
Fclose(version_f);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
Fclose(version_f);
|
|
|
|
version_mime = mailprivacy_new_file_part(privacy,
|
|
version_filename,
|
|
"application/pgp-encrypted",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_version;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, version_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(version_mime);
|
|
mailmime_free(version_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_version;
|
|
}
|
|
|
|
/* encrypted part */
|
|
|
|
encrypted_mime = mailprivacy_new_file_part(privacy,
|
|
encrypted_filename,
|
|
"application/octet-stream",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_version;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, encrypted_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_version;
|
|
}
|
|
|
|
unlink(version_filename);
|
|
unlink(description_filename);
|
|
unlink(encrypted_filename);
|
|
unlink(original_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_version:
|
|
unlink(version_filename);
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
unlink_original:
|
|
unlink(original_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
static int pgp_encrypt_mime(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char original_filename[PATH_MAX];
|
|
FILE * original_f;
|
|
int res;
|
|
int r;
|
|
int col;
|
|
char description_filename[PATH_MAX];
|
|
char encrypted_filename[PATH_MAX];
|
|
char version_filename[PATH_MAX];
|
|
FILE * version_f;
|
|
char command[PATH_MAX];
|
|
char quoted_original_filename[PATH_MAX];
|
|
struct mailmime * version_mime;
|
|
struct mailmime * multipart;
|
|
struct mailmime_content * content;
|
|
struct mailmime_parameter * param;
|
|
struct mailmime * encrypted_mime;
|
|
char recipient[PATH_MAX];
|
|
struct mailimf_fields * fields;
|
|
struct mailmime * root;
|
|
size_t written;
|
|
int encrypt_ok;
|
|
|
|
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 */
|
|
|
|
collect_recipient(recipient, sizeof(recipient), fields);
|
|
|
|
/* part to encrypt */
|
|
|
|
/* encode quoted printable all text parts */
|
|
|
|
mailprivacy_prepare_mime(mime);
|
|
|
|
original_f = mailprivacy_get_tmp_file(privacy,
|
|
original_filename, sizeof(original_filename));
|
|
if (original_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_write(original_f, &col, mime);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(original_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
|
|
Fclose(original_f);
|
|
|
|
/* prepare destination file for encryption */
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, encrypted_filename,
|
|
sizeof(encrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_original;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_original_filename,
|
|
sizeof(quoted_original_filename), original_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
snprintf(command, sizeof(command), "gpg %s -a --batch --yes -e '%s'",
|
|
recipient, quoted_original_filename);
|
|
|
|
encrypt_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
encrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
encrypt_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
encrypt_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!encrypt_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* multipart */
|
|
|
|
multipart = mailprivacy_new_file_part(privacy, NULL,
|
|
"multipart/encrypted", -1);
|
|
|
|
content = multipart->mm_content_type;
|
|
|
|
param = mailmime_param_new_with_data("protocol",
|
|
"application/pgp-encrypted");
|
|
if (param == NULL) {
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
r = clist_append(content->ct_parameters, param);
|
|
if (r < 0) {
|
|
mailmime_parameter_free(param);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* version part */
|
|
|
|
version_f = mailprivacy_get_tmp_file(privacy,
|
|
version_filename, sizeof(version_filename));
|
|
if (version_f == NULL) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
written = Fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
|
|
if (written != sizeof(PGP_VERSION) - 1) {
|
|
Fclose(version_f);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
Fclose(version_f);
|
|
|
|
version_mime = mailprivacy_new_file_part(privacy,
|
|
version_filename,
|
|
"application/pgp-encrypted",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_version;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, version_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(version_mime);
|
|
mailmime_free(version_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_version;
|
|
}
|
|
|
|
/* encrypted part */
|
|
|
|
encrypted_mime = mailprivacy_new_file_part(privacy,
|
|
encrypted_filename,
|
|
"application/octet-stream",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = r;
|
|
goto unlink_version;
|
|
}
|
|
|
|
r = mailmime_smart_add_part(multipart, encrypted_mime);
|
|
if (r != MAIL_NO_ERROR) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
mailprivacy_mime_clear(multipart);
|
|
mailmime_free(multipart);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_version;
|
|
}
|
|
|
|
unlink(version_filename);
|
|
unlink(description_filename);
|
|
unlink(encrypted_filename);
|
|
unlink(original_filename);
|
|
|
|
* result = multipart;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_version:
|
|
unlink(version_filename);
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
unlink_original:
|
|
unlink(original_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
static int pgp_clear_sign(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char default_key[PATH_MAX];
|
|
char original_filename[PATH_MAX];
|
|
FILE * original_f;
|
|
char signed_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char quoted_original_filename[PATH_MAX];
|
|
int col;
|
|
struct mailmime * signed_mime;
|
|
int res;
|
|
int r;
|
|
char command[PATH_MAX];
|
|
struct mailmime_content * content_type;
|
|
char * email;
|
|
int sign_ok;
|
|
|
|
if (mime->mm_type != MAILMIME_SINGLE) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (mime->mm_data.mm_single == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* get signing key */
|
|
|
|
* default_key = '\0';
|
|
email = get_first_from_addr(mime);
|
|
if (email != NULL)
|
|
snprintf(default_key, sizeof(default_key),
|
|
"--default-key %s", email);
|
|
|
|
/* get part to sign */
|
|
|
|
original_f = mailprivacy_get_tmp_file(privacy,
|
|
original_filename,
|
|
sizeof(original_filename));
|
|
if (original_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(original_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
Fclose(original_f);
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, signed_filename,
|
|
sizeof(signed_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_original;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_signed;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_original_filename,
|
|
sizeof(quoted_original_filename), original_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 --batch --yes --digest-algo sha1 %s --clearsign '%s'",
|
|
default_key, quoted_original_filename);
|
|
|
|
sign_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
signed_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
sign_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
sign_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!sign_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* building the signed part */
|
|
|
|
signed_mime = mailprivacy_new_file_part(privacy, signed_filename,
|
|
NULL, MAILMIME_MECHANISM_8BIT);
|
|
if (signed_mime == NULL) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* place original content type */
|
|
|
|
content_type = mailmime_content_dup(mime->mm_content_type);
|
|
if (content_type == NULL) {
|
|
mailprivacy_mime_clear(signed_mime);
|
|
mailmime_free(signed_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
mailmime_content_free(signed_mime->mm_content_type);
|
|
signed_mime->mm_content_type = content_type;
|
|
|
|
/* place original MIME fields */
|
|
|
|
if (mime->mm_mime_fields != NULL) {
|
|
struct mailmime_fields * mime_fields;
|
|
clistiter * cur;
|
|
|
|
mime_fields = mailprivacy_mime_fields_dup(privacy,
|
|
mime->mm_mime_fields);
|
|
if (mime_fields == NULL) {
|
|
mailprivacy_mime_clear(signed_mime);
|
|
mailmime_free(signed_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
for(cur = clist_begin(mime_fields->fld_list) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = clist_content(cur);
|
|
if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
|
|
mailmime_field_free(field);
|
|
clist_delete(mime_fields->fld_list, cur);
|
|
break;
|
|
}
|
|
}
|
|
clist_concat(signed_mime->mm_mime_fields->fld_list,
|
|
mime_fields->fld_list);
|
|
mailmime_fields_free(mime_fields);
|
|
}
|
|
|
|
unlink(description_filename);
|
|
unlink(signed_filename);
|
|
unlink(original_filename);
|
|
|
|
* result = signed_mime;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_signed:
|
|
unlink(signed_filename);
|
|
unlink_original:
|
|
unlink(original_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
static int pgp_armor_encrypt(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char original_filename[PATH_MAX];
|
|
FILE * original_f;
|
|
char encrypted_filename[PATH_MAX];
|
|
char quoted_original_filename[PATH_MAX];
|
|
int col;
|
|
struct mailmime * encrypted_mime;
|
|
int res;
|
|
int r;
|
|
char command[PATH_MAX];
|
|
struct mailmime_content * content_type;
|
|
struct mailmime * root;
|
|
struct mailimf_fields * fields;
|
|
char recipient[PATH_MAX];
|
|
int encrypt_ok;
|
|
char description_filename[PATH_MAX];
|
|
|
|
if (mime->mm_type != MAILMIME_SINGLE) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (mime->mm_data.mm_single == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
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 */
|
|
|
|
collect_recipient(recipient, sizeof(recipient), fields);
|
|
|
|
/* get part to encrypt */
|
|
|
|
original_f = mailprivacy_get_tmp_file(privacy, original_filename,
|
|
sizeof(original_filename));
|
|
if (original_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(original_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
Fclose(original_f);
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, encrypted_filename,
|
|
sizeof(encrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_original;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_original_filename,
|
|
sizeof(quoted_original_filename), original_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command), "gpg %s --batch --yes -e --armor '%s'",
|
|
recipient, quoted_original_filename);
|
|
|
|
encrypt_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
encrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
encrypt_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
encrypt_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!encrypt_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* building the encrypted part */
|
|
|
|
encrypted_mime = mailprivacy_new_file_part(privacy,
|
|
encrypted_filename,
|
|
"application/octet-stream",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (encrypted_mime == NULL) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* place original content type */
|
|
|
|
content_type = mailmime_content_dup(mime->mm_content_type);
|
|
if (content_type == NULL) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
mailmime_content_free(encrypted_mime->mm_content_type);
|
|
encrypted_mime->mm_content_type = content_type;
|
|
|
|
/* place original MIME fields */
|
|
|
|
if (mime->mm_mime_fields != NULL) {
|
|
struct mailmime_fields * mime_fields;
|
|
clistiter * cur;
|
|
|
|
mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
|
|
if (mime_fields == NULL) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
for(cur = clist_begin(mime_fields->fld_list) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = clist_content(cur);
|
|
if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
|
|
mailmime_field_free(field);
|
|
clist_delete(mime_fields->fld_list, cur);
|
|
break;
|
|
}
|
|
}
|
|
clist_concat(encrypted_mime->mm_mime_fields->fld_list,
|
|
mime_fields->fld_list);
|
|
mailmime_fields_free(mime_fields);
|
|
}
|
|
|
|
unlink(description_filename);
|
|
unlink(encrypted_filename);
|
|
unlink(original_filename);
|
|
|
|
* result = encrypted_mime;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
unlink_original:
|
|
unlink(original_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
|
|
static int pgp_armor_sign_encrypt(struct mailprivacy * privacy,
|
|
mailmessage * msg,
|
|
struct mailmime * mime, struct mailmime ** result)
|
|
{
|
|
char default_key[PATH_MAX];
|
|
char original_filename[PATH_MAX];
|
|
FILE * original_f;
|
|
char encrypted_filename[PATH_MAX];
|
|
char description_filename[PATH_MAX];
|
|
char quoted_original_filename[PATH_MAX];
|
|
int col;
|
|
struct mailmime * encrypted_mime;
|
|
int res;
|
|
int r;
|
|
char command[PATH_MAX];
|
|
struct mailmime_content * content_type;
|
|
struct mailmime * root;
|
|
struct mailimf_fields * fields;
|
|
char recipient[PATH_MAX];
|
|
char * email;
|
|
int encrypt_ok;
|
|
|
|
if (mime->mm_type != MAILMIME_SINGLE) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (mime->mm_data.mm_single == NULL) {
|
|
res = MAIL_ERROR_INVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* get signing key */
|
|
|
|
* default_key = '\0';
|
|
email = get_first_from_addr(mime);
|
|
if (email != NULL)
|
|
snprintf(default_key, sizeof(default_key),
|
|
"--default-key %s", 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 */
|
|
|
|
collect_recipient(recipient, sizeof(recipient), fields);
|
|
|
|
/* get part to encrypt */
|
|
|
|
original_f = mailprivacy_get_tmp_file(privacy,
|
|
original_filename,
|
|
sizeof(original_filename));
|
|
if (original_f == NULL) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto err;
|
|
}
|
|
|
|
col = 0;
|
|
r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
|
|
if (r != MAILIMF_NO_ERROR) {
|
|
Fclose(original_f);
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
Fclose(original_f);
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, encrypted_filename,
|
|
sizeof(encrypted_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_original;
|
|
}
|
|
|
|
r = mailprivacy_get_tmp_filename(privacy, description_filename,
|
|
sizeof(description_filename));
|
|
if (r != MAIL_NO_ERROR) {
|
|
res = r;
|
|
goto unlink_encrypted;
|
|
}
|
|
|
|
r = mail_quote_filename(quoted_original_filename,
|
|
sizeof(quoted_original_filename), original_filename);
|
|
if (r < 0) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
snprintf(command, sizeof(command),
|
|
"gpg --passphrase-fd=0 %s --batch --yes --digest-algo sha1 "
|
|
"%s -e -s -a '%s'",
|
|
recipient, default_key, quoted_original_filename);
|
|
|
|
encrypt_ok = 0;
|
|
r = gpg_command_passphrase(privacy, msg, command, NULL,
|
|
encrypted_filename, description_filename);
|
|
switch (r) {
|
|
case NO_ERROR_PGP:
|
|
encrypt_ok = 1;
|
|
break;
|
|
case ERROR_PGP_NOPASSPHRASE:
|
|
case ERROR_PGP_CHECK:
|
|
encrypt_ok = 0;
|
|
break;
|
|
case ERROR_PGP_COMMAND:
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
case ERROR_PGP_FILE:
|
|
res = MAIL_ERROR_FILE;
|
|
goto unlink_description;
|
|
}
|
|
|
|
if (!encrypt_ok) {
|
|
res = MAIL_ERROR_COMMAND;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* building the encrypted part */
|
|
|
|
encrypted_mime = mailprivacy_new_file_part(privacy,
|
|
encrypted_filename,
|
|
"application/octet-stream",
|
|
MAILMIME_MECHANISM_8BIT);
|
|
if (encrypted_mime == NULL) {
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
/* place original content type */
|
|
|
|
content_type = mailmime_content_dup(mime->mm_content_type);
|
|
if (content_type == NULL) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
|
|
mailmime_content_free(encrypted_mime->mm_content_type);
|
|
encrypted_mime->mm_content_type = content_type;
|
|
|
|
/* place original MIME fields */
|
|
|
|
if (mime->mm_mime_fields != NULL) {
|
|
struct mailmime_fields * mime_fields;
|
|
clistiter * cur;
|
|
|
|
mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
|
|
if (mime_fields == NULL) {
|
|
mailprivacy_mime_clear(encrypted_mime);
|
|
mailmime_free(encrypted_mime);
|
|
res = MAIL_ERROR_MEMORY;
|
|
goto unlink_description;
|
|
}
|
|
for(cur = clist_begin(mime_fields->fld_list) ;
|
|
cur != NULL ; cur = clist_next(cur)) {
|
|
struct mailmime_field * field;
|
|
|
|
field = clist_content(cur);
|
|
if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
|
|
mailmime_field_free(field);
|
|
clist_delete(mime_fields->fld_list, cur);
|
|
break;
|
|
}
|
|
}
|
|
clist_concat(encrypted_mime->mm_mime_fields->fld_list,
|
|
mime_fields->fld_list);
|
|
mailmime_fields_free(mime_fields);
|
|
}
|
|
|
|
unlink(description_filename);
|
|
unlink(encrypted_filename);
|
|
unlink(original_filename);
|
|
|
|
* result = encrypted_mime;
|
|
|
|
return MAIL_NO_ERROR;
|
|
|
|
unlink_description:
|
|
unlink(description_filename);
|
|
unlink_encrypted:
|
|
unlink(encrypted_filename);
|
|
unlink_original:
|
|
unlink(original_filename);
|
|
err:
|
|
return res;
|
|
}
|
|
|
|
static struct mailprivacy_encryption pgp_encryption_tab[] = {
|
|
/* PGP signed part */
|
|
{
|
|
/* name */ "signed",
|
|
/* description */ "PGP signed part",
|
|
/* encrypt */ pgp_sign_mime
|
|
},
|
|
|
|
/* pgp encrypted part */
|
|
|
|
{
|
|
/* name */ "encrypted",
|
|
/* description */ "PGP encrypted part",
|
|
/* encrypt */ pgp_encrypt_mime
|
|
},
|
|
|
|
/* PGP signed & encrypted part */
|
|
|
|
{
|
|
/* name */ "signed-encrypted",
|
|
/* description */ "PGP signed & encrypted part",
|
|
/* encrypt */ pgp_sign_encrypt_mime
|
|
},
|
|
|
|
/* PGP clear signed part */
|
|
|
|
{
|
|
/* name */ "clear-signed",
|
|
/* description */ "PGP clear signed part",
|
|
/* encrypt */ pgp_clear_sign
|
|
},
|
|
|
|
/* PGP armor encrypted part */
|
|
|
|
{
|
|
/* name */ "encrypted-armor",
|
|
/* description */ "PGP ASCII armor encrypted part",
|
|
/* encrypt */ pgp_armor_encrypt
|
|
},
|
|
|
|
/* PGP armor signed & encrypted part */
|
|
|
|
{
|
|
/* name */ "signed-encrypted-armor",
|
|
/* description */ "PGP ASCII armor signed & encrypted part",
|
|
/* encrypt */ pgp_armor_sign_encrypt
|
|
}
|
|
};
|
|
|
|
static struct mailprivacy_protocol pgp_protocol = {
|
|
/* name */ "pgp",
|
|
/* description */ "OpenPGP",
|
|
|
|
/* is_encrypted */ pgp_test_encrypted,
|
|
/* decrypt */ pgp_handler,
|
|
|
|
/* encryption_count */
|
|
(sizeof(pgp_encryption_tab) / sizeof(pgp_encryption_tab[0])),
|
|
|
|
/* encryption_tab */ pgp_encryption_tab
|
|
};
|
|
|
|
#ifdef LIBETPAN_REENTRANT
|
|
#if defined(HAVE_PTHREAD_H) && !defined(IGNORE_PTHREAD_H)
|
|
static pthread_mutex_t encryption_id_hash_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
#define LOCK() pthread_mutex_lock(&encryption_id_hash_lock)
|
|
#define UNLOCK() pthread_mutex_unlock(&encryption_id_hash_lock)
|
|
#elif (defined WIN32)
|
|
static CRITICAL_SECTION encryption_id_hash_lock = {0};
|
|
static int mailprivacy_gnupg_init_lock_done = 0;
|
|
#define LOCK() EnterCriticalSection(&encryption_id_hash_lock)
|
|
#define UNLOCK() LeaveCriticalSection(&encryption_id_hash_lock)
|
|
#endif
|
|
#else
|
|
#define LOCK() do while (0)
|
|
#define UNLOCK() do while (0)
|
|
#endif
|
|
static chash * encryption_id_hash = NULL;
|
|
|
|
static void mailprivacy_gnupg_init_lock(void)
|
|
{
|
|
#ifdef LIBETPAN_REENTRANT
|
|
#if defined(HAVE_PTHREAD_H) && !defined(IGNORE_PTHREAD_H)
|
|
#elif (defined WIN32)
|
|
if (InterlockedExchange(&mailprivacy_gnupg_init_lock_done, 1) == 0){
|
|
InitializeCriticalSection(&encryption_id_hash_lock);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int mailprivacy_gnupg_init(struct mailprivacy * privacy)
|
|
{
|
|
return mailprivacy_register(privacy, &pgp_protocol);
|
|
}
|
|
|
|
void mailprivacy_gnupg_done(struct mailprivacy * privacy)
|
|
{
|
|
mailprivacy_unregister(privacy, &pgp_protocol);
|
|
}
|
|
|
|
static clist * get_list(struct mailprivacy * privacy, mailmessage * msg)
|
|
{
|
|
clist * encryption_id_list;
|
|
|
|
encryption_id_list = NULL;
|
|
if (encryption_id_hash != NULL) {
|
|
chashdatum key;
|
|
chashdatum value;
|
|
int r;
|
|
|
|
key.data = &msg;
|
|
key.len = sizeof(msg);
|
|
r = chash_get(encryption_id_hash, &key, &value);
|
|
if (r == 0) {
|
|
encryption_id_list = value.data;
|
|
}
|
|
}
|
|
|
|
return encryption_id_list;
|
|
}
|
|
|
|
void mailprivacy_gnupg_encryption_id_list_clear(struct mailprivacy * privacy,
|
|
mailmessage * msg)
|
|
{
|
|
clist * encryption_id_list;
|
|
clistiter * iter;
|
|
|
|
LOCK();
|
|
encryption_id_list = get_list(privacy, msg);
|
|
if (encryption_id_list != NULL) {
|
|
chashdatum key;
|
|
|
|
for(iter = clist_begin(encryption_id_list) ;
|
|
iter != NULL ; iter = clist_next(iter)) {
|
|
char * str;
|
|
|
|
str = clist_content(iter);
|
|
free(str);
|
|
}
|
|
clist_free(encryption_id_list);
|
|
|
|
key.data = &msg;
|
|
key.len = sizeof(msg);
|
|
chash_delete(encryption_id_hash, &key, NULL);
|
|
|
|
if (chash_count(encryption_id_hash) == 0) {
|
|
chash_free(encryption_id_hash);
|
|
encryption_id_hash = NULL;
|
|
}
|
|
}
|
|
UNLOCK();
|
|
}
|
|
|
|
clist * mailprivacy_gnupg_encryption_id_list(struct mailprivacy * privacy,
|
|
mailmessage * msg)
|
|
{
|
|
clist * encryption_id_list;
|
|
|
|
LOCK();
|
|
encryption_id_list = get_list(privacy, msg);
|
|
UNLOCK();
|
|
|
|
return encryption_id_list;
|
|
}
|
|
|
|
static int mailprivacy_gnupg_add_encryption_id(struct mailprivacy * privacy,
|
|
mailmessage * msg, char * encryption_id)
|
|
{
|
|
clist * encryption_id_list;
|
|
int r;
|
|
int res;
|
|
|
|
LOCK();
|
|
|
|
res = -1;
|
|
|
|
encryption_id_list = get_list(privacy, msg);
|
|
if (encryption_id_list == NULL) {
|
|
if (encryption_id_hash == NULL)
|
|
encryption_id_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
|
|
|
|
if (encryption_id_hash != NULL) {
|
|
encryption_id_list = clist_new();
|
|
if (encryption_id_list != NULL) {
|
|
chashdatum key;
|
|
chashdatum value;
|
|
|
|
key.data = &msg;
|
|
key.len = sizeof(msg);
|
|
value.data = encryption_id_list;
|
|
value.len = 0;
|
|
r = chash_set(encryption_id_hash, &key, &value, NULL);
|
|
if (r < 0)
|
|
clist_free(encryption_id_list);
|
|
}
|
|
}
|
|
}
|
|
|
|
encryption_id_list = get_list(privacy, msg);
|
|
if (encryption_id_list != NULL) {
|
|
char * str;
|
|
|
|
str = strdup(encryption_id);
|
|
if (str != NULL) {
|
|
r = clist_append(encryption_id_list, str);
|
|
if (r < 0) {
|
|
free(str);
|
|
}
|
|
else {
|
|
res = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK();
|
|
|
|
return res;
|
|
}
|
|
|
|
#define MAX_EMAIL_SIZE 1024
|
|
|
|
static chash * passphrase_hash = NULL;
|
|
|
|
int mailprivacy_gnupg_set_encryption_id(struct mailprivacy * privacy,
|
|
char * user_id, char * passphrase)
|
|
{
|
|
chashdatum key;
|
|
chashdatum value;
|
|
int r;
|
|
char buf[MAX_EMAIL_SIZE];
|
|
char * n;
|
|
|
|
strncpy(buf, user_id, sizeof(buf));
|
|
buf[sizeof(buf) - 1] = '\0';
|
|
for(n = buf ; * n != '\0' ; n ++)
|
|
* n = toupper((unsigned char) * n);
|
|
|
|
if (passphrase_hash == NULL) {
|
|
passphrase_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
|
|
if (passphrase_hash == NULL)
|
|
return MAIL_ERROR_MEMORY;
|
|
}
|
|
|
|
key.data = buf;
|
|
|