From 807c9de625cc953dc9e9eab7c4fa93248576f778 Mon Sep 17 00:00:00 2001 From: djcb Date: Wed, 18 Jul 2012 10:30:23 +0300 Subject: [PATCH] * crypto: better handling of crypto errors --- lib/mu-msg-crypto.c | 99 ++++++++++++++++++++++++++++++++++++--------- lib/mu-msg-crypto.h | 29 ++++++++++--- lib/mu-msg-part.c | 22 ++++++---- lib/mu-msg-part.h | 4 +- mu/mu-cmd.c | 54 ++++++++++++++++++++----- 5 files changed, 165 insertions(+), 43 deletions(-) diff --git a/lib/mu-msg-crypto.c b/lib/mu-msg-crypto.c index 64e53a5d..9c9174a9 100644 --- a/lib/mu-msg-crypto.c +++ b/lib/mu-msg-crypto.c @@ -43,7 +43,7 @@ dummy_password_requester (GMimeCryptoContext *ctx, const char *user_id, GMimeCryptoContext* -get_crypto_context (MuMsgPartOptions opts, GError **err) +get_gpg_crypto_context (MuMsgPartOptions opts, GError **err) { GMimeCryptoContext *ctx; const char *prog; @@ -64,9 +64,9 @@ get_crypto_context (MuMsgPartOptions opts, GError **err) path); g_free (path); } - if (!ctx) { - mu_util_g_set_error (err, MU_ERROR, "failed to get crypto context"); + mu_util_g_set_error (err, MU_ERROR, + "failed to get GPG crypto context"); return NULL; } @@ -74,15 +74,38 @@ get_crypto_context (MuMsgPartOptions opts, GError **err) (GMIME_GPG_CONTEXT(ctx), opts & MU_MSG_PART_OPTION_USE_AGENT); g_mime_gpg_context_set_always_trust - (GMIME_GPG_CONTEXT(ctx), FALSE); + (GMIME_GPG_CONTEXT(ctx), + opts & MU_MSG_PART_OPTION_TRUST_ALWAYS); g_mime_gpg_context_set_auto_key_retrieve (GMIME_GPG_CONTEXT(ctx), opts & MU_MSG_PART_OPTION_AUTO_RETRIEVE_KEY); return ctx; - } +GMimeCryptoContext* +get_pkcs7_crypto_context (MuMsgPartOptions opts, GError **err) +{ + GMimeCryptoContext *ctx; + + ctx = g_mime_pkcs7_context_new + ((GMimePasswordRequestFunc)dummy_password_requester); + if (!ctx) { + 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), + opts & MU_MSG_PART_OPTION_TRUST_ALWAYS); + + return ctx; +} + + + + const char* get_pubkey_algo_name (GMimePubKeyAlgo algo) { @@ -216,6 +239,19 @@ sig_info_destroy (MuMsgPartSigInfo *siginfo) } +/* we create a fake siginfo when things go wrong */ +static GSList* +error_sig_infos (void) +{ + MuMsgPartSigInfo *sig_info; + + sig_info = g_new0 (MuMsgPartSigInfo, 1); + sig_info->status = MU_MSG_PART_SIG_STATUS_FAIL; + + return g_slist_prepend (NULL, sig_info); +} + + GSList* mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, MuMsgPartOptions opts, @@ -223,7 +259,7 @@ mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, MuMsgPartOptions opts, { int i; GMimeSignatureList *sigs; - GMimeCryptoContext *gpgctx; + GMimeCryptoContext *cctx; GSList *siginfos; if (!GMIME_IS_MULTIPART_SIGNED (sigmpart)) { @@ -232,20 +268,23 @@ mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, MuMsgPartOptions opts, return NULL; /* error */ } - gpgctx = get_crypto_context (opts, err); - if (!gpgctx) - return NULL; /* error */ + if (opts & MU_MSG_PART_OPTION_USE_PKCS7) + cctx = get_pkcs7_crypto_context (opts, err); + else + cctx = get_gpg_crypto_context (opts, err); - sigs = g_mime_multipart_signed_verify (sigmpart, gpgctx, err); - g_object_unref (gpgctx); + /* return a fake siginfos with the error */ + if (!cctx) + return error_sig_infos (); /* error */ + sigs = g_mime_multipart_signed_verify (sigmpart, cctx, err); + g_object_unref (cctx); if (!sigs) return NULL; /* error */ for (i = 0, siginfos = NULL; i != g_mime_signature_list_length (sigs); ++i) { MuMsgPartSigInfo *siginfo; - siginfo = sig_info_new (g_mime_signature_list_get_signature (sigs, i)); @@ -270,13 +309,15 @@ mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status) { switch (status) { case MU_MSG_PART_SIG_STATUS_UNKNOWN: - return "unknown"; + return "no signed part found"; case MU_MSG_PART_SIG_STATUS_GOOD: return "good"; case MU_MSG_PART_SIG_STATUS_BAD: return "bad signature"; case MU_MSG_PART_SIG_STATUS_ERROR: return "error verifying signature"; + case MU_MSG_PART_SIG_STATUS_FAIL: + return "crypto failed"; case MU_MSG_PART_SIG_STATUS_EXPSIG: return "signature is expired"; case MU_MSG_PART_SIG_STATUS_NO_PUBKEY: @@ -296,7 +337,7 @@ mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status) char* -mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) +mu_msg_part_sig_statuses_to_string (MuMsgPartSigStatus status) { unsigned u; GString *gstr; @@ -306,6 +347,7 @@ mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) MU_MSG_PART_SIG_STATUS_GOOD, MU_MSG_PART_SIG_STATUS_BAD, MU_MSG_PART_SIG_STATUS_ERROR, + MU_MSG_PART_SIG_STATUS_FAIL, MU_MSG_PART_SIG_STATUS_EXPSIG, MU_MSG_PART_SIG_STATUS_NO_PUBKEY, MU_MSG_PART_SIG_STATUS_EXPKEYSIG, @@ -313,14 +355,15 @@ mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO }; - g_return_val_if_fail (info, NULL); + if (status == MU_MSG_PART_SIG_STATUS_UNKNOWN) + return g_strdup + (mu_msg_part_sig_status_to_string (status)); gstr = g_string_sized_new (128); for (u = 0; u != G_N_ELEMENTS(statuses); ++u) { const gchar *statstr; - - if (!(info->status & statuses[u])) + if (!(status & statuses[u])) continue; statstr = mu_msg_part_sig_status_to_string (statuses[u]); @@ -330,9 +373,27 @@ mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) gstr = g_string_append (gstr, statstr); } - gstr = g_string_prepend (gstr, "status: "); + return g_string_free (gstr, FALSE); +} - if (info->status & MU_MSG_PART_SIG_STATUS_ERROR) + + +char* +mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) +{ + GString *gstr; + gchar *statuses; + + g_return_val_if_fail (info, NULL); + + gstr = g_string_sized_new (128); + + statuses = mu_msg_part_sig_statuses_to_string (info->status); + g_string_append_printf (gstr, "status: %s", statuses); + g_free (statuses); + + if (info->status & MU_MSG_PART_SIG_STATUS_ERROR || + info->status & MU_MSG_PART_SIG_STATUS_FAIL) return g_string_free (gstr, FALSE); g_string_append_printf (gstr, "; algorithms (P/D) (%s, %s)", diff --git a/lib/mu-msg-crypto.h b/lib/mu-msg-crypto.h index 1d86bc75..f1e73273 100644 --- a/lib/mu-msg-crypto.h +++ b/lib/mu-msg-crypto.h @@ -33,11 +33,14 @@ enum _MuMsgPartSigStatus { MU_MSG_PART_SIG_STATUS_BAD = 1 << 1, MU_MSG_PART_SIG_STATUS_ERROR = 1 << 2, - MU_MSG_PART_SIG_STATUS_EXPSIG = 1 << 3, /* expired sig */ - MU_MSG_PART_SIG_STATUS_NO_PUBKEY = 1 << 4, /* no public key */ - MU_MSG_PART_SIG_STATUS_EXPKEYSIG = 1 << 5, /* key expired */ - MU_MSG_PART_SIG_STATUS_REVKEYSIG = 1 << 6, /* revoked key */ - MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO = 1 << 7 /* unsupp'd algo */ + /* status when crypto does not work */ + MU_MSG_PART_SIG_STATUS_FAIL = 1 << 3, + + MU_MSG_PART_SIG_STATUS_EXPSIG = 1 << 4, /* expired sig */ + MU_MSG_PART_SIG_STATUS_NO_PUBKEY = 1 << 5, /* no public key */ + MU_MSG_PART_SIG_STATUS_EXPKEYSIG = 1 << 6, /* key expired */ + MU_MSG_PART_SIG_STATUS_REVKEYSIG = 1 << 7, /* revoked key */ + MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO = 1 << 8 /* unsupp'd algo */ }; typedef enum _MuMsgPartSigStatus MuMsgPartSigStatus; @@ -57,6 +60,8 @@ struct _MuMsgPartSigInfo { const char *pubkey_algo; /* public key algorithm */ const char *digest_algo; /* digest algorithm */ + const char *errmsg; /* errmsg when status == + * MU_MSG_PART_SIG_STATUS_FAIL */ /* don't touch */ gpointer _cert; }; @@ -74,6 +79,17 @@ typedef struct _MuMsgPartSigInfo MuMsgPartSigInfo; const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status); +/** + * convert the bitwise-OR'ed statuses to a string + * + * @param statuses bitwise-OR'ed statuses + * + * @return newly allocated string (g_free) + */ +char* mu_msg_part_sig_statuses_to_string (MuMsgPartSigStatus statuses) + G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; + + /** * get a human readable-description of siginfo * @@ -81,7 +97,8 @@ const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status); * * @return a newly allocated string (g_free) */ -char* mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info); +char* mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) + G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; /** * free the list of MuMsgPartSigInfo structures diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index b52f70d3..c26c51fe 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -248,19 +248,27 @@ check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi, MuMsgPartOptions opts) { #ifdef BUILD_CRYPTO + + GMimeContentType *ctype; GError *err; - err = NULL; + gboolean pkcs7; if (!GMIME_IS_MULTIPART_SIGNED (parent)) return; - /* we're interested in the signed thing here, not the - * signature itself */ - if (g_mime_content_type_is_type ( - g_mime_object_get_content_type (mobj), - "application", "pgp-signature")) - return; + ctype = g_mime_object_get_content_type (mobj); + if (g_mime_content_type_is_type + (ctype, "application", "pgp-signature")) + pkcs7 = FALSE; + else if (g_mime_content_type_is_type + (ctype, "application", "x-pkcs7-signature")) + pkcs7 = TRUE; + else return; /* don't know how to handle other kinds */ + if (pkcs7) + opts |= MU_MSG_PART_OPTION_USE_PKCS7; /* gpg is the default */ + + err = NULL; pi->sig_infos = mu_msg_mime_sig_infos (GMIME_MULTIPART_SIGNED (parent), opts, &err); if (err) { diff --git a/lib/mu-msg-part.h b/lib/mu-msg-part.h index 5870199a..69181776 100644 --- a/lib/mu-msg-part.h +++ b/lib/mu-msg-part.h @@ -219,7 +219,9 @@ enum _MuMsgPartOptions { * if mu was built with crypto support */ MU_MSG_PART_OPTION_CHECK_SIGNATURES = 1 << 1, MU_MSG_PART_OPTION_AUTO_RETRIEVE_KEY = 1 << 2, - MU_MSG_PART_OPTION_USE_AGENT = 1 << 3 + MU_MSG_PART_OPTION_USE_AGENT = 1 << 3, + MU_MSG_PART_OPTION_TRUST_ALWAYS = 1 << 4, + MU_MSG_PART_OPTION_USE_PKCS7 = 1 << 5 /* gpg is the default */ }; typedef enum _MuMsgPartOptions MuMsgPartOptions; diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index 7e17956d..44126329 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -397,22 +397,39 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err) #ifdef BUILD_CRYPTO -static void print_signatures (MuMsg *msg, MuMsgPart *part, MuConfig *opts) +struct _VData { + MuMsgPartSigStatus status; + MuConfig*opts; + gchar *msg; +}; +typedef struct _VData VData; + + +static void +each_sig (MuMsg *msg, MuMsgPart *part, VData *vdata) { GSList *cur; if (!part->sig_infos) return; - g_print ("MIME-part %u has %u signature(s): ", - part->index, g_slist_length (part->sig_infos)); + if (vdata->opts->verbose) + g_print ("MIME-part %u has %u signature(s): ", + part->index, g_slist_length (part->sig_infos)); for (cur = part->sig_infos; cur; cur = g_slist_next (cur)) { - char *descr; - descr = mu_msg_part_sig_info_to_string - ((MuMsgPartSigInfo*)cur->data); - g_print ("%s\n", descr); - g_free (descr); + + MuMsgPartSigInfo *sig_info; + sig_info = (MuMsgPartSigInfo*)cur->data; + + if (vdata->opts->verbose) { + char *descr; + descr = mu_msg_part_sig_info_to_string (sig_info); + g_print ("%s\n", descr); + g_free (descr); + } + + vdata->status |= sig_info->status; } } @@ -421,6 +438,7 @@ mu_cmd_verify (MuConfig *opts, GError **err) { MuMsg *msg; MuMsgPartOptions partopts; + VData vdata; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY, @@ -436,12 +454,28 @@ mu_cmd_verify (MuConfig *opts, GError **err) if (opts->use_agent) partopts |= MU_MSG_PART_OPTION_USE_AGENT; - mu_msg_part_foreach (msg,(MuMsgPartForeachFunc)print_signatures, opts, + vdata.status = MU_MSG_PART_SIG_STATUS_UNKNOWN; + vdata.opts = opts; + vdata.msg = NULL; + + mu_msg_part_foreach (msg,(MuMsgPartForeachFunc)each_sig, &vdata, partopts); + /* if there's anything bad, all goodness goes away */ + if (vdata.status & MU_MSG_PART_SIG_STATUS_BAD || + vdata.status & MU_MSG_PART_SIG_STATUS_ERROR) + vdata.status &= ~MU_MSG_PART_SIG_STATUS_GOOD; + + if (!opts->quiet) { + gchar *str; + str = mu_msg_part_sig_statuses_to_string (vdata.status); + g_print ("verdict: %s\n", str); + g_free (str); + } + mu_msg_unref (msg); - return MU_OK; + return vdata.status == MU_MSG_PART_SIG_STATUS_GOOD ? MU_OK : MU_ERROR; } #else MuError