diff --git a/lib/mu-msg-crypto.c b/lib/mu-msg-crypto.c index 20b96ee6..826f0fc7 100644 --- a/lib/mu-msg-crypto.c +++ b/lib/mu-msg-crypto.c @@ -145,50 +145,207 @@ get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, return cctx; } - -static MuMsgPartSigStatus -get_verdict (GMimeSignatureList *sigs) +static const char* +get_pubkey_algo_name (GMimePubKeyAlgo algo) { - int i; - MuMsgPartSigStatus status; + switch (algo) { + case GMIME_PUBKEY_ALGO_DEFAULT: + return "default"; + case GMIME_PUBKEY_ALGO_RSA: + return "RSA"; + case GMIME_PUBKEY_ALGO_RSA_E: + return "RSA (encryption only)"; + case GMIME_PUBKEY_ALGO_RSA_S: + return "RSA (signing only)"; + case GMIME_PUBKEY_ALGO_ELG_E: + return "ElGamal (encryption only)"; + case GMIME_PUBKEY_ALGO_DSA: + return "DSA"; + case GMIME_PUBKEY_ALGO_ELG: + return "ElGamal"; + default: + return "unknown pubkey algorithm"; + } +} - status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */ +static const gchar* +get_digestkey_algo_name (GMimeDigestAlgo algo) +{ + switch (algo) { + case GMIME_DIGEST_ALGO_DEFAULT: + return "default"; + case GMIME_DIGEST_ALGO_MD5: + return "MD5"; + case GMIME_DIGEST_ALGO_SHA1: + return "SHA-1"; + case GMIME_DIGEST_ALGO_RIPEMD160: + return "RIPEMD160"; + case GMIME_DIGEST_ALGO_MD2: + return "MD2"; + case GMIME_DIGEST_ALGO_TIGER192: + return "TIGER-192"; + case GMIME_DIGEST_ALGO_HAVAL5160: + return "HAVAL-5-160"; + case GMIME_DIGEST_ALGO_SHA256: + return "SHA-256"; + case GMIME_DIGEST_ALGO_SHA384: + return "SHA-384"; + case GMIME_DIGEST_ALGO_SHA512: + return "SHA-512"; + case GMIME_DIGEST_ALGO_SHA224: + return "SHA-224"; + case GMIME_DIGEST_ALGO_MD4: + return "MD4"; + default: + return "unknown digest algorithm"; + } +} - for (i = 0; i != g_mime_signature_list_length (sigs); ++i) { - GMimeSignature *msig; - GMimeSignatureStatus sigstat; - msig = g_mime_signature_list_get_signature (sigs, i); - sigstat = g_mime_signature_get_status (msig); +/* get data from the 'certificate' */ +static char* +get_cert_data (GMimeCertificate *cert) +{ + const char /*issuer_name, *issuer_serial, ,*fprint*/ + *email, *name, + *digest_algo, *pubkey_algo, + *keyid, *trust; - switch (sigstat) { - case GMIME_SIGNATURE_STATUS_GOOD: continue; - case GMIME_SIGNATURE_STATUS_ERROR: return MU_MSG_PART_SIG_STATUS_ERROR; - case GMIME_SIGNATURE_STATUS_BAD: return MU_MSG_PART_SIG_STATUS_BAD; - } + /* issuer_name = g_mime_certificate_get_issuer_name (cert); */ + /* issuer_serial = g_mime_certificate_get_issuer_serial (cert); */ + email = g_mime_certificate_get_email (cert); + name = g_mime_certificate_get_name (cert); + /* fprint = g_mime_certificate_get_fingerprint (cert); */ + keyid = g_mime_certificate_get_key_id (cert); + + digest_algo = get_digestkey_algo_name + (g_mime_certificate_get_digest_algo (cert)); + pubkey_algo = get_pubkey_algo_name + (g_mime_certificate_get_pubkey_algo (cert)); + + switch (g_mime_certificate_get_trust (cert)) { + case GMIME_CERTIFICATE_TRUST_NONE: trust = "none"; break; + case GMIME_CERTIFICATE_TRUST_NEVER: trust = "never"; break; + case GMIME_CERTIFICATE_TRUST_UNDEFINED: trust = "undefined"; break; + case GMIME_CERTIFICATE_TRUST_MARGINAL: trust= "marginal"; break; + case GMIME_CERTIFICATE_TRUST_FULLY: trust = "full"; break; + case GMIME_CERTIFICATE_TRUST_ULTIMATE: trust = "ultimate"; break; + default: + g_return_val_if_reached (NULL); } - return status; + return g_strdup_printf ( + "signed by: %s <%s>; " /*; issued by %s (%s); */ + "algos: <%s,%s>; key-id: %s; trust: %s", + name ? name : "?", email ? email : "?", + /* issuer_name, issuer_serial */ + pubkey_algo, digest_algo, keyid, trust); } -MuMsgPartSigStatus +/* get a human-readable report about the signature */ +static char* +get_verdict_report (GMimeSignature *msig) +{ + time_t t; + const char *status, *created, *expires; + gchar *certdata, *report; + + switch (g_mime_signature_get_status (msig)) { + case GMIME_SIGNATURE_STATUS_GOOD: status = "good"; break; + case GMIME_SIGNATURE_STATUS_ERROR: status = "error"; break; + case GMIME_SIGNATURE_STATUS_BAD: status = "bad"; break; + default: g_return_val_if_reached (NULL); + } + + t = g_mime_signature_get_created (msig); + created = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); + + t = g_mime_signature_get_expires (msig); + expires = (t == 0 || t == (time_t)-1) ? "?" : mu_date_str_s ("%x", t); + + certdata = get_cert_data (g_mime_signature_get_certificate (msig)); + report = g_strdup_printf ("status: %s; created: %s, expires: %s (%s)", + status, created, expires, + certdata ? certdata : "?"); + g_free (certdata); + return report; +} + + +static MuMsgPartSigStatusReport* +get_status_report (GMimeSignatureList *sigs) +{ + int i; + MuMsgPartSigStatus status; + MuMsgPartSigStatusReport *status_report; + char *report; + + status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */ + + for (i = 0, report = NULL; i != g_mime_signature_list_length (sigs); ++i) { + + GMimeSignature *msig; + GMimeSignatureStatus sigstat; + gchar *rep; + + msig = g_mime_signature_list_get_signature (sigs, i); + sigstat = g_mime_signature_get_status (msig); + + switch (sigstat) { + case GMIME_SIGNATURE_STATUS_GOOD: + break; + case GMIME_SIGNATURE_STATUS_ERROR: + status = MU_MSG_PART_SIG_STATUS_ERROR; + break; + case GMIME_SIGNATURE_STATUS_BAD: + status = MU_MSG_PART_SIG_STATUS_BAD; + break; + default: g_return_val_if_reached (NULL); + } + + rep = get_verdict_report (msig); + report = g_strdup_printf ("%s%s[%d] %s", + report ? report : "", + report ? "; " : "", + i, rep); + g_free (rep); + } + + status_report = g_slice_new (MuMsgPartSigStatusReport); + status_report->verdict = status; + status_report->report = report; + + return status_report; +} + +void +mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report) +{ + if (!report) + return; + + g_free ((char*)report->report); + g_slice_free (MuMsgPartSigStatusReport, report); +} + + +MuMsgPartSigStatusReport* mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, GError **err) { - MuMsgPartSigStatus sigstat; + MuMsgPartSigStatusReport *report; GMimeCryptoContext *ctx; GMimeSignatureList *sigs; - g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED(sig), - MU_MSG_PART_SIG_STATUS_FAIL); + g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED(sig), NULL); ctx = get_crypto_context (opts, NULL, NULL, err); if (!ctx) { mu_util_g_set_error (err, MU_ERROR_CRYPTO, "failed to get crypto context"); - return MU_MSG_PART_SIG_STATUS_FAIL; + return NULL; } sigs = g_mime_multipart_signed_verify (sig, ctx, err); @@ -197,13 +354,13 @@ mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, if (err && !*err) mu_util_g_set_error (err, MU_ERROR_CRYPTO, "verification failed"); - return MU_MSG_PART_SIG_STATUS_FAIL; + return NULL; } - sigstat = get_verdict (sigs); + report = get_status_report (sigs); g_mime_signature_list_clear (sigs); - return sigstat; + return report; } diff --git a/lib/mu-msg-crypto.h b/lib/mu-msg-crypto.h index 2bb755ba..3988decb 100644 --- a/lib/mu-msg-crypto.h +++ b/lib/mu-msg-crypto.h @@ -25,9 +25,7 @@ #include #include - struct _MuMsgDecryptedPart; typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart; - #endif /*__MU_MSG_CRYPTO_H__*/ diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index f3deffed..33039e55 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -297,27 +297,28 @@ get_disposition (GMimeObject *mobj) return MU_MSG_PART_TYPE_NONE; } -#define SIG_STATUS "sig-status" +#define SIG_STATUS_REPORT "sig-status-report" /* call 'func' with information about this MIME-part */ static gboolean check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts) { /* the signature status */ - MuMsgPartSigStatus sigstat; + MuMsgPartSigStatusReport *sigrep; GError *err; err = NULL; - sigstat = mu_msg_crypto_verify_part (part, opts, &err); - + sigrep = mu_msg_crypto_verify_part (part, opts, &err); if (err) { g_warning ("error verifying signature: %s", err->message); g_clear_error (&err); } /* tag this part with the signature status check */ - g_object_set_data (G_OBJECT(part), SIG_STATUS, - GSIZE_TO_POINTER(sigstat)); + g_object_set_data_full + (G_OBJECT(part), SIG_STATUS_REPORT, + sigrep, + (GDestroyNotify)mu_msg_part_sig_status_report_destroy); return TRUE; } @@ -348,12 +349,6 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, msgpart.part_type = MU_MSG_PART_TYPE_LEAF; msgpart.part_type |= get_disposition ((GMimeObject*)part); - /* get the sig status from the parent */ - msgpart.sig_status = - (MuMsgPartSigStatus) - GPOINTER_TO_SIZE(g_object_get_data (G_OBJECT(parent), - SIG_STATUS)); - ct = g_mime_object_get_content_type ((GMimeObject*)part); if (GMIME_IS_CONTENT_TYPE(ct)) { msgpart.type = g_mime_content_type_get_media_type (ct); @@ -366,6 +361,14 @@ handle_part (MuMsg *msg, GMimePart *part, GMimeObject *parent, msgpart.part_type |= MU_MSG_PART_TYPE_TEXT_HTML; } + /* get the sig status from the parent, but don't set if for + * the signature part itself */ + msgpart.sig_status_report = NULL; + if (g_ascii_strcasecmp (msgpart.subtype, "pgp-signature") != 0) + msgpart.sig_status_report = + (MuMsgPartSigStatusReport*) + g_object_get_data (G_OBJECT(parent), SIG_STATUS_REPORT); + msgpart.data = (gpointer)part; msgpart.index = index; @@ -419,7 +422,8 @@ handle_mime_object (MuMsg *msg, return handle_message_part (msg, GMIME_MESSAGE_PART(mobj), parent, opts, index, func, user_data); - else if (GMIME_IS_MULTIPART_SIGNED (mobj)) + else if ((opts & MU_MSG_OPTION_VERIFY) && + GMIME_IS_MULTIPART_SIGNED (mobj)) return check_signature (msg, GMIME_MULTIPART_SIGNED (mobj), opts); else if (GMIME_IS_MULTIPART_ENCRYPTED (mobj)) diff --git a/lib/mu-msg-part.h b/lib/mu-msg-part.h index 6287f2c7..2584a3f7 100644 --- a/lib/mu-msg-part.h +++ b/lib/mu-msg-part.h @@ -63,6 +63,19 @@ enum _MuMsgPartSigStatus { }; typedef enum _MuMsgPartSigStatus MuMsgPartSigStatus; +struct _MuMsgPartSigStatusReport { + MuMsgPartSigStatus verdict; + const char *report; +}; +typedef struct _MuMsgPartSigStatusReport MuMsgPartSigStatusReport; + +/** + * destroy a MuMsgPartSignatureStatusReport object + * + * @param report a MuMsgPartSignatureStatusReport object + */ +void mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report); + struct _MuMsgPart { @@ -81,9 +94,8 @@ struct _MuMsgPart { gpointer data; /* opaque data */ - MuMsgPartType part_type; - MuMsgPartSigStatus sig_status; - + MuMsgPartType part_type; + MuMsgPartSigStatusReport *sig_status_report; }; typedef struct _MuMsgPart MuMsgPart; diff --git a/lib/mu-msg-priv.h b/lib/mu-msg-priv.h index 78adecb9..efa053cd 100644 --- a/lib/mu-msg-priv.h +++ b/lib/mu-msg-priv.h @@ -129,12 +129,11 @@ typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *promp * @param opts message options * @param err receive error information * - * @return the verification status, or MU_MSG_PART_SIG_STATUS_FAIL in - * case of some internal error + * @return a status report object, free with mu_msg_part_sig_status_report_destroy */ -MuMsgPartSigStatus mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, - GError **err); - +MuMsgPartSigStatusReport* mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, + MuMsgOptions opts, + GError **err); /** * decrypt the given encrypted mime multipart diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index dc7ddf34..30a551e2 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -285,7 +285,6 @@ mu_cmd_mkdir (MuConfig *opts, GError **err) FALSE, err)) return err && *err ? (*err)->code : MU_ERROR_FILE_CANNOT_MKDIR; - return MU_OK; } @@ -401,22 +400,74 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err) #ifdef BUILD_CRYPTO - static void -each_sig (MuMsg *msg, MuMsgPart *part, MuMsgPartSigStatus *sigstat) + +struct _VData { + MuMsgPartSigStatus combined_status; + char *report; +}; +typedef struct _VData VData; + +static void +each_sig (MuMsg *msg, MuMsgPart *part, VData *vdata) { - if (*sigstat == MU_MSG_PART_SIG_STATUS_BAD || - *sigstat == MU_MSG_PART_SIG_STATUS_ERROR) + MuMsgPartSigStatusReport *report; + report = part->sig_status_report; + if (!report) return; - *sigstat = part->sig_status; + vdata->report = g_strdup_printf + ("%s%s%s", + vdata->report ? vdata->report : "", + vdata->report ? "; " : "", + report->report); + + if (vdata->combined_status == MU_MSG_PART_SIG_STATUS_BAD || + vdata->combined_status == MU_MSG_PART_SIG_STATUS_ERROR) + return; + + vdata->combined_status = report->verdict; } + +static void +print_verdict (VData *vdata, gboolean color) +{ + char *str; + + switch (vdata->combined_status) { + + case MU_MSG_PART_SIG_STATUS_UNSIGNED: + str = g_strdup ("no signature found"); + break; + case MU_MSG_PART_SIG_STATUS_GOOD: + str = g_strdup_printf ("signature verified; %s", + vdata->report); + break; + case MU_MSG_PART_SIG_STATUS_BAD: + str = g_strdup_printf ("bad signature; %s", + vdata->report); + break; + case MU_MSG_PART_SIG_STATUS_ERROR: + str = g_strdup_printf ("verification failed; %s", + vdata->report); + break; + case MU_MSG_PART_SIG_STATUS_FAIL: + str = g_strdup ("error in verification process"); + break; + default: g_return_if_reached (); + } + + print_field ("verdict", str, color); + g_free (str); +} + + MuError mu_cmd_verify (MuConfig *opts, GError **err) { MuMsg *msg; MuMsgOptions msgopts; - MuMsgPartSigStatus sigstat; + VData vdata; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY, @@ -426,36 +477,20 @@ mu_cmd_verify (MuConfig *opts, GError **err) if (!msg) return MU_ERROR; - msgopts = mu_config_get_msg_options (opts); - - sigstat = MU_MSG_PART_SIG_STATUS_UNSIGNED; + msgopts = mu_config_get_msg_options (opts) | MU_MSG_OPTION_VERIFY; + vdata.report = NULL; + vdata.combined_status = MU_MSG_PART_SIG_STATUS_UNSIGNED; mu_msg_part_foreach (msg, msgopts, - (MuMsgPartForeachFunc)each_sig, &sigstat); + (MuMsgPartForeachFunc)each_sig, &vdata); - if (!opts->quiet) { - const char *verdict; - - switch (sigstat) { - case MU_MSG_PART_SIG_STATUS_UNSIGNED: - verdict = "no signature found"; break; - case MU_MSG_PART_SIG_STATUS_GOOD: - verdict = "signature verified"; break; - case MU_MSG_PART_SIG_STATUS_BAD: - verdict = "signature not verified"; break; - case MU_MSG_PART_SIG_STATUS_ERROR: - verdict = "failed to verify signature"; break; - case MU_MSG_PART_SIG_STATUS_FAIL: - verdict = "error in verification process"; break; - default: - g_return_val_if_reached (MU_ERROR); - } - - g_print ("verdict: %s\n", verdict); - } + if (!opts->quiet) + print_verdict (&vdata, !opts->nocolor); mu_msg_unref (msg); + g_free (vdata.report); - return sigstat == MU_MSG_PART_SIG_STATUS_GOOD ? MU_OK : MU_ERROR; + return vdata.combined_status == MU_MSG_PART_SIG_STATUS_GOOD ? + MU_OK : MU_ERROR; } #else MuError