* crypto: decryption support (WIP)

This commit is contained in:
djcb
2012-07-22 19:39:17 +03:00
parent a75664b955
commit 5a92c8b58a
6 changed files with 352 additions and 54 deletions

View File

@ -21,6 +21,8 @@
#include "config.h"
#endif /*HAVE_CONFIG_H*/
#include <string.h>
#include "mu-msg.h"
#include "mu-msg-priv.h"
#include "mu-msg-part.h"
@ -31,74 +33,138 @@
#include <gmime/gmime.h>
#include <gmime/gmime-multipart-signed.h>
#define CALLBACK_DATA "callback-data"
struct _CallbackData {
MuMsgPartPasswordFunc pw_func;
gpointer user_data;
};
typedef struct _CallbackData CallbackData;
static gboolean
dummy_password_requester (GMimeCryptoContext *ctx, const char *user_id,
password_requester (GMimeCryptoContext *ctx, const char *user_id,
const char* prompt_ctx, gboolean reprompt,
GMimeStream *response, GError **err)
{
g_print ("password requested\n");
return TRUE;
CallbackData *cbdata;
gchar *password;
ssize_t written;
cbdata = g_object_get_data (G_OBJECT(ctx), CALLBACK_DATA);
g_return_val_if_fail (cbdata, FALSE);
password = cbdata->pw_func (user_id, prompt_ctx, reprompt,
cbdata->user_data);
if (!password)
return FALSE;
written = g_mime_stream_write_string (response, password);
if (written != -1)
written = g_mime_stream_write_string (response, "\n");
if (written == -1)
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
"writing password to mime stream failed");
if (g_mime_stream_flush (response) != 0)
g_printerr ("error flushing stream!\n");
memset (password, 0, strlen(password));
g_free (password);
return written != -1 ? TRUE : FALSE;
}
GMimeCryptoContext*
static char*
dummy_password_func (const char *user_id, const char *prompt_ctx,
gboolean reprompt, gpointer user_data)
{
g_print ("password requested for %s (%s) %s\n",
user_id, prompt_ctx, reprompt ? "again" : "");
return NULL;
}
static GMimeCryptoContext*
get_gpg_crypto_context (MuMsgOptions opts, GError **err)
{
GMimeCryptoContext *ctx;
GMimeCryptoContext *cctx;
const char *prog;
ctx = NULL;
cctx = NULL;
prog = g_getenv ("MU_GPG_PATH");
if (prog)
ctx = g_mime_gpg_context_new (
(GMimePasswordRequestFunc)dummy_password_requester,
prog);
cctx = g_mime_gpg_context_new (
(GMimePasswordRequestFunc)password_requester, prog);
else {
char *path;
path = g_find_program_in_path ("gpg");
if (path)
ctx = g_mime_gpg_context_new (
(GMimePasswordRequestFunc)dummy_password_requester,
path);
cctx = g_mime_gpg_context_new (
password_requester, path);
g_free (path);
}
if (!ctx) {
if (!cctx) {
mu_util_g_set_error (err, MU_ERROR,
"failed to get GPG crypto context");
return NULL;
}
g_mime_gpg_context_set_use_agent
(GMIME_GPG_CONTEXT(ctx),
opts & MU_MSG_OPTION_USE_AGENT);
g_mime_gpg_context_set_always_trust
(GMIME_GPG_CONTEXT(ctx), FALSE);
(GMIME_GPG_CONTEXT(cctx),
opts & MU_MSG_OPTION_USE_AGENT ? TRUE:FALSE);
g_mime_gpg_context_set_auto_key_retrieve
(GMIME_GPG_CONTEXT(ctx),
opts & MU_MSG_OPTION_AUTO_RETRIEVE_KEY);
(GMIME_GPG_CONTEXT(cctx),
opts & MU_MSG_OPTION_AUTO_RETRIEVE_KEY ? TRUE:FALSE);
g_mime_gpg_context_set_always_trust
(GMIME_GPG_CONTEXT(cctx), FALSE);
return ctx;
return cctx;
}
GMimeCryptoContext*
static GMimeCryptoContext*
get_pkcs7_crypto_context (MuMsgOptions opts, GError **err)
{
GMimeCryptoContext *ctx;
GMimeCryptoContext *cctx;
ctx = g_mime_pkcs7_context_new
((GMimePasswordRequestFunc)dummy_password_requester);
if (!ctx) {
cctx = g_mime_pkcs7_context_new (password_requester);
if (!cctx) {
mu_util_g_set_error (err, MU_ERROR,
"failed to get PKCS7 crypto context");
return NULL;
}
g_mime_pkcs7_context_set_always_trust
(GMIME_PKCS7_CONTEXT(ctx), FALSE);
(GMIME_PKCS7_CONTEXT(cctx), FALSE);
return ctx;
return cctx;
}
static GMimeCryptoContext*
get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func,
gpointer user_data, GError **err)
{
CallbackData *cbdata;
GMimeCryptoContext *cctx;
if (opts & MU_MSG_OPTION_USE_PKCS7)
cctx = get_pkcs7_crypto_context (opts, err);
else
cctx = get_gpg_crypto_context (opts, err);
/* use gobject to pass data to the callback func */
cbdata = g_new0 (CallbackData, 1);
cbdata->pw_func = password_func;
cbdata->user_data = user_data;
g_object_set_data_full (G_OBJECT(cctx), CALLBACK_DATA,
cbdata, (GDestroyNotify)g_free);
return cctx;
}
@ -266,13 +332,11 @@ mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, MuMsgOptions opts,
return NULL; /* error */
}
if (opts & MU_MSG_OPTION_USE_PKCS7)
cctx = get_pkcs7_crypto_context (opts, err);
else
cctx = get_gpg_crypto_context (opts, err);
/* dummy is good, since we don't need a password when checking
* signatures */
cctx = get_crypto_context (opts, dummy_password_func, NULL, err);
/* return a fake siginfos with the error */
if (!cctx)
if (!cctx) /* return a fake siginfos with the error */
return error_sig_infos (); /* error */
sigs = g_mime_multipart_signed_verify (sigmpart, cctx, err);
@ -313,7 +377,7 @@ mu_msg_part_free_sig_infos (GSList *siginfos)
* - if not, the verdic is MU_MSG_PART_SIG_STATUS_UNKNOWN
*/
MuMsgPartSigStatus
mu_msg_mime_sig_infos_verdict (GSList *sig_infos)
mu_msg_part_sig_infos_verdict (GSList *sig_infos)
{
GSList *cur;
MuMsgPartSigStatus status;
@ -457,3 +521,120 @@ mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info)
return g_string_free (gstr, FALSE);
}
struct _MuMsgDecryptedPart {
GMimeObject *decrypted;
};
struct _PartData {
MuMsgPartDecryptForeachFunc func;
gpointer user_data;
GMimeCryptoContext *cctx;
GError *err;
};
typedef struct _PartData PartData;
static void
each_encpart (GMimeObject *parent, GMimeObject *part, PartData *pdata)
{
MuMsgDecryptedPart dpart;
if (pdata->err)
return;
if (!GMIME_IS_MULTIPART_ENCRYPTED (part))
return; /* nothing to do for this part */
dpart.decrypted =
g_mime_multipart_encrypted_decrypt (
GMIME_MULTIPART_ENCRYPTED(part),
pdata->cctx, NULL, &pdata->err);
if (!dpart.decrypted || pdata->err) {
if (!pdata->err)
mu_util_g_set_error
(&pdata->err,
MU_ERROR_CRYPTO,
"decryption failed");
return;
}
pdata->func (&dpart, pdata->user_data);
}
gboolean
mu_msg_part_decrypt_foreach (MuMsg *msg, MuMsgPartDecryptForeachFunc func,
MuMsgPartPasswordFunc password_func,
gpointer user_data, MuMsgOptions opts, GError **err)
{
PartData pdata;
g_return_val_if_fail (msg, FALSE);
g_return_val_if_fail (func, FALSE);
g_return_val_if_fail (password_func, FALSE);
if (!mu_msg_load_msg_file (msg, err))
return FALSE;
pdata.func = func;
pdata.user_data = user_data;
pdata.err = NULL;
pdata.cctx = get_crypto_context (opts, password_func,
user_data, err);
if (!pdata.cctx)
return FALSE;
g_mime_message_foreach (msg->_file->_mime_msg,
(GMimeObjectForeachFunc)each_encpart,
&pdata);
if (pdata.err) {
*err = pdata.err;
return FALSE;
}
g_object_unref (pdata.cctx);
return TRUE;
}
char*
mu_msg_decrypted_part_to_string (MuMsgDecryptedPart *dpart, GError **err)
{
GMimePart *part;
gboolean not_ok;
gchar *str;
g_return_val_if_fail (dpart, NULL);
if (!GMIME_IS_PART(dpart->decrypted)) {
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
"wrong mime-type");
return NULL; /* can only convert parts to string*/
}
part = GMIME_PART(dpart->decrypted);
str = mu_msg_mime_part_to_string (part, &not_ok);
if (not_ok) {
g_free (str);
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
"failed to convert part to string");
return NULL;
}
return str;
}
gboolean
mu_msg_decrypted_part_to_file (MuMsgDecryptedPart *dpart, const char *path,
GError **err)
{
g_return_val_if_fail (dpart, FALSE);
g_return_val_if_fail (path, FALSE);
return mu_msg_part_mime_save_object (dpart->decrypted, path, FALSE, FALSE,
err);
}

View File

@ -80,7 +80,7 @@ const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status);
/**
* summarize the signatures to one status:
* summarize the signature checks to one status:
*
* - if there's any signature with MU_MSG_PART_SIG_STATUS_(ERROR|FAIL),
* the verdict is MU_MSG_PART_SIG_STATUS_ERROR
@ -94,7 +94,7 @@ const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status);
*
* @return the status
*/
MuMsgPartSigStatus mu_msg_mime_sig_infos_verdict (GSList *sig_infos);
MuMsgPartSigStatus mu_msg_part_sig_infos_verdict (GSList *sig_infos);
/**
@ -125,4 +125,81 @@ char* mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info)
*/
void mu_msg_part_free_sig_infos (GSList *siginfos);
/*
* below function only do anything useful if mu was built with crypto
* support
*/
struct _MuMsgDecryptedPart;
typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart;
/**
* callback function to provide decrypted message parts
*
* @param dpart a decrypted par
* @param user_data user pointer (as passed to mu_msg_part_decrypt_foreach)
*/
typedef void (*MuMsgPartDecryptForeachFunc) (MuMsgDecryptedPart *dpart,
gpointer user_data);
/**
* callback function to retrieve a password from the user
*
* @param user_id the user name / id to get the password for
* @param prompt_ctx a string containing some helpful context for the prompt
* @param reprompt whether this is a reprompt after an earlier, incorrect password
* @param user_data the user_data pointer passed to mu_msg_part_decrypt_foreach
*
* @return a newly allocated (g_free'able) string
*/
typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *prompt_ctx,
gboolean reprompt, gpointer user_data);
/**
* go through all MIME-parts for this message, and decrypted all parts
* that are encrypted. After decryption,
*
* If mu was built without crypto support, function does nothing.
*
* @param msg a valid MuMsg instance
* @param fun a callback function called for each decrypted part
* @param password_func a callback func called to retrieve a password from user
* @param user_data user data which passed to the callback function
* @param opts options
* @param err receives error information
*
* @return TRUE if function succeeded, FALSE otherwise
*/
gboolean mu_msg_part_decrypt_foreach (MuMsg *msg, MuMsgPartDecryptForeachFunc func,
MuMsgPartPasswordFunc password_func,
gpointer user_data, MuMsgOptions opts,
GError **err);
/**
* convert the decrypted part to a string.
*
* @param dpart decrypted part
* @param err receives error information
*
* @return decrypted part as a string (g_free after use), or NULL
*/
char* mu_msg_decrypted_part_to_string (MuMsgDecryptedPart *dpart, GError **err);
/**
* write the decrypted part to a file.
*
* @param dpart decrypted part
* @param path path to write it to
* @param err receives error information
*
* @return TRUE if it succeeded, FALSE otherwise
*/
gboolean mu_msg_decrypted_part_to_file (MuMsgDecryptedPart *dpart, const char *path,
GError **err);
#endif /*__MU_MSG_CRYPTO_H__*/

View File

@ -242,13 +242,11 @@ msg_part_free (MuMsgPart *pi)
#endif /*BUILD_CRYPTO*/
}
#ifdef BUILD_CRYPTO
static void
check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
MuMsgOptions opts)
{
#ifdef BUILD_CRYPTO
GMimeContentType *ctype;
GError *err;
gboolean pkcs7;
@ -275,10 +273,10 @@ check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
g_warning ("error verifying signature: %s", err->message);
g_clear_error (&err);
}
}
#endif /*BUILD_CRYPTO*/
return;
}
static void
@ -307,7 +305,9 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata)
return;
rv = init_msg_part_from_mime_message_part (mmsg, &pi);
if (rv && (pdata->_opts && MU_MSG_OPTION_RECURSE_RFC822))
/* NOTE: this screws up the counting (pdata->_idx) */
/* NOTE: this screws up the counting
* (pdata->_idx); happily, we only use it
* where we don't care about that */
g_mime_message_foreach /* recurse */
(mmsg, (GMimeObjectForeachFunc)part_foreach_cb,
pdata);
@ -315,8 +315,10 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata)
rv = FALSE; /* ignore */
/* if we have crypto support, check the signature if there is one */
#ifdef BUILD_CRYPTO
if (pdata->_opts & MU_MSG_OPTION_CHECK_SIGNATURES)
check_signature_maybe (parent, mobj, &pi, pdata->_opts);
#endif /*BUILD_CRYPTO*/
if (rv)
pdata->_func(pdata->_msg, &pi, pdata->_user_data);
@ -352,7 +354,7 @@ mu_msg_part_foreach (MuMsg *msg, MuMsgPartForeachFunc func, gpointer user_data,
}
static gboolean
gboolean
write_part_to_fd (GMimePart *part, int fd, GError **err)
{
GMimeStream *stream;
@ -416,8 +418,8 @@ write_object_to_fd (GMimeObject *obj, int fd, GError **err)
static gboolean
save_mime_object (GMimeObject *obj, const char *fullpath,
gboolean
mu_msg_part_mime_save_object (GMimeObject *obj, const char *fullpath,
gboolean overwrite, gboolean use_existing, GError **err)
{
int fd;
@ -498,7 +500,6 @@ mu_msg_part_filepath (MuMsg *msg, const char* targetdir, guint partidx,
gchar*
mu_msg_part_filepath_cache (MuMsg *msg, guint partid)
{
@ -556,7 +557,7 @@ mu_msg_part_save (MuMsg *msg, const char *fullpath, guint partidx,
partidx);
return FALSE;
} else
return save_mime_object (part, fullpath, overwrite,
return mu_msg_part_mime_save_object (part, fullpath, overwrite,
use_cached, err);
}

View File

@ -76,6 +76,23 @@ struct _MuMsg {
gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err);
/**
* write a GMimeObject to a file
*
* @param obj a GMimeObject
* @param fullpath full file path
* @param overwrite allow overwriting existing file
* @param if file already exist, don't bother to write
* @param err receives error information
*
* @return TRUE if writing succeeded, FALSE otherwise.
*/
gboolean mu_msg_part_mime_save_object (GMimeObject *obj, const char *fullpath,
gboolean overwrite, gboolean use_existing,
GError **err);
/**
* get the MIME part that's probably the body of the message (heuristic)
*
@ -87,6 +104,9 @@ gchar* mu_msg_mime_part_to_string (GMimePart *part, gboolean *err);
GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html);
#ifdef BUILD_CRYPTO
/**
* get signature information for the mime part
@ -100,6 +120,22 @@ GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html);
*/
GSList* mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart,
MuMsgOptions opts, GError **err);
/**
* decrypt the given mime part
*
* @param encpart
* @param opts
* @param err
*
* @return
*/
char* mu_msg_mime_decrypt (GMimeMultipartEncrypted *encpart,
MuMsgOptions opts, GError **err);
#endif /*BUILD_CRYPTO*/
G_END_DECLS

View File

@ -242,7 +242,7 @@ elvis (const char *s1, const char *s2)
static const char*
sig_verdict (GSList *sig_infos)
{
switch (mu_msg_mime_sig_infos_verdict (sig_infos)) {
switch (mu_msg_part_sig_infos_verdict (sig_infos)) {
case MU_MSG_PART_SIG_STATUS_GOOD:
return ":signature good";
case MU_MSG_PART_SIG_STATUS_BAD:

View File

@ -414,6 +414,9 @@ enum _MuError {
MU_ERROR_CONTACTS = 50,
MU_ERROR_CONTACTS_CANNOT_RETRIEVE = 51,
/* crypto related errors */
MU_ERROR_CRYPTO = 60,
/* File errors */
/* generic file-related error */