* restore mu-verify (WIP)

This commit is contained in:
djcb 2012-08-30 20:26:35 +03:00
parent 90290a132c
commit 14661a6d7b
6 changed files with 285 additions and 80 deletions

View File

@ -145,50 +145,207 @@ get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func,
return cctx; return cctx;
} }
static const char*
static MuMsgPartSigStatus get_pubkey_algo_name (GMimePubKeyAlgo algo)
get_verdict (GMimeSignatureList *sigs)
{ {
int i; switch (algo) {
MuMsgPartSigStatus status; 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; /* get data from the 'certificate' */
GMimeSignatureStatus sigstat; static char*
msig = g_mime_signature_list_get_signature (sigs, i); get_cert_data (GMimeCertificate *cert)
sigstat = g_mime_signature_get_status (msig); {
const char /*issuer_name, *issuer_serial, ,*fprint*/
*email, *name,
*digest_algo, *pubkey_algo,
*keyid, *trust;
switch (sigstat) { /* issuer_name = g_mime_certificate_get_issuer_name (cert); */
case GMIME_SIGNATURE_STATUS_GOOD: continue; /* issuer_serial = g_mime_certificate_get_issuer_serial (cert); */
case GMIME_SIGNATURE_STATUS_ERROR: return MU_MSG_PART_SIG_STATUS_ERROR; email = g_mime_certificate_get_email (cert);
case GMIME_SIGNATURE_STATUS_BAD: return MU_MSG_PART_SIG_STATUS_BAD; 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, mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts,
GError **err) GError **err)
{ {
MuMsgPartSigStatus sigstat; MuMsgPartSigStatusReport *report;
GMimeCryptoContext *ctx; GMimeCryptoContext *ctx;
GMimeSignatureList *sigs; GMimeSignatureList *sigs;
g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED(sig), g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED(sig), NULL);
MU_MSG_PART_SIG_STATUS_FAIL);
ctx = get_crypto_context (opts, NULL, NULL, err); ctx = get_crypto_context (opts, NULL, NULL, err);
if (!ctx) { if (!ctx) {
mu_util_g_set_error (err, MU_ERROR_CRYPTO, mu_util_g_set_error (err, MU_ERROR_CRYPTO,
"failed to get crypto context"); "failed to get crypto context");
return MU_MSG_PART_SIG_STATUS_FAIL; return NULL;
} }
sigs = g_mime_multipart_signed_verify (sig, ctx, err); 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) if (err && !*err)
mu_util_g_set_error (err, MU_ERROR_CRYPTO, mu_util_g_set_error (err, MU_ERROR_CRYPTO,
"verification failed"); "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); g_mime_signature_list_clear (sigs);
return sigstat; return report;
} }

View File

@ -25,9 +25,7 @@
#include <glib.h> #include <glib.h>
#include <mu-msg.h> #include <mu-msg.h>
struct _MuMsgDecryptedPart; struct _MuMsgDecryptedPart;
typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart; typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart;
#endif /*__MU_MSG_CRYPTO_H__*/ #endif /*__MU_MSG_CRYPTO_H__*/

View File

@ -297,27 +297,28 @@ get_disposition (GMimeObject *mobj)
return MU_MSG_PART_TYPE_NONE; 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 */ /* call 'func' with information about this MIME-part */
static gboolean static gboolean
check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts) check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts)
{ {
/* the signature status */ /* the signature status */
MuMsgPartSigStatus sigstat; MuMsgPartSigStatusReport *sigrep;
GError *err; GError *err;
err = NULL; err = NULL;
sigstat = mu_msg_crypto_verify_part (part, opts, &err); sigrep = mu_msg_crypto_verify_part (part, opts, &err);
if (err) { if (err) {
g_warning ("error verifying signature: %s", err->message); g_warning ("error verifying signature: %s", err->message);
g_clear_error (&err); g_clear_error (&err);
} }
/* tag this part with the signature status check */ /* tag this part with the signature status check */
g_object_set_data (G_OBJECT(part), SIG_STATUS, g_object_set_data_full
GSIZE_TO_POINTER(sigstat)); (G_OBJECT(part), SIG_STATUS_REPORT,
sigrep,
(GDestroyNotify)mu_msg_part_sig_status_report_destroy);
return TRUE; 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 = MU_MSG_PART_TYPE_LEAF;
msgpart.part_type |= get_disposition ((GMimeObject*)part); 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); ct = g_mime_object_get_content_type ((GMimeObject*)part);
if (GMIME_IS_CONTENT_TYPE(ct)) { if (GMIME_IS_CONTENT_TYPE(ct)) {
msgpart.type = g_mime_content_type_get_media_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; 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.data = (gpointer)part;
msgpart.index = index; msgpart.index = index;
@ -419,7 +422,8 @@ handle_mime_object (MuMsg *msg,
return handle_message_part return handle_message_part
(msg, GMIME_MESSAGE_PART(mobj), (msg, GMIME_MESSAGE_PART(mobj),
parent, opts, index, func, user_data); 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 return check_signature
(msg, GMIME_MULTIPART_SIGNED (mobj), opts); (msg, GMIME_MULTIPART_SIGNED (mobj), opts);
else if (GMIME_IS_MULTIPART_ENCRYPTED (mobj)) else if (GMIME_IS_MULTIPART_ENCRYPTED (mobj))

View File

@ -63,6 +63,19 @@ enum _MuMsgPartSigStatus {
}; };
typedef enum _MuMsgPartSigStatus 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 { struct _MuMsgPart {
@ -81,9 +94,8 @@ struct _MuMsgPart {
gpointer data; /* opaque data */ gpointer data; /* opaque data */
MuMsgPartType part_type; MuMsgPartType part_type;
MuMsgPartSigStatus sig_status; MuMsgPartSigStatusReport *sig_status_report;
}; };
typedef struct _MuMsgPart MuMsgPart; typedef struct _MuMsgPart MuMsgPart;

View File

@ -129,12 +129,11 @@ typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *promp
* @param opts message options * @param opts message options
* @param err receive error information * @param err receive error information
* *
* @return the verification status, or MU_MSG_PART_SIG_STATUS_FAIL in * @return a status report object, free with mu_msg_part_sig_status_report_destroy
* case of some internal error
*/ */
MuMsgPartSigStatus mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, MuMsgPartSigStatusReport* mu_msg_crypto_verify_part (GMimeMultipartSigned *sig,
GError **err); MuMsgOptions opts,
GError **err);
/** /**
* decrypt the given encrypted mime multipart * decrypt the given encrypted mime multipart

View File

@ -285,7 +285,6 @@ mu_cmd_mkdir (MuConfig *opts, GError **err)
FALSE, err)) FALSE, err))
return err && *err ? (*err)->code : return err && *err ? (*err)->code :
MU_ERROR_FILE_CANNOT_MKDIR; MU_ERROR_FILE_CANNOT_MKDIR;
return MU_OK; return MU_OK;
} }
@ -401,22 +400,74 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err)
#ifdef BUILD_CRYPTO #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 || MuMsgPartSigStatusReport *report;
*sigstat == MU_MSG_PART_SIG_STATUS_ERROR) report = part->sig_status_report;
if (!report)
return; 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 MuError
mu_cmd_verify (MuConfig *opts, GError **err) mu_cmd_verify (MuConfig *opts, GError **err)
{ {
MuMsg *msg; MuMsg *msg;
MuMsgOptions msgopts; MuMsgOptions msgopts;
MuMsgPartSigStatus sigstat; VData vdata;
g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts, MU_ERROR_INTERNAL);
g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY, g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY,
@ -426,36 +477,20 @@ mu_cmd_verify (MuConfig *opts, GError **err)
if (!msg) if (!msg)
return MU_ERROR; return MU_ERROR;
msgopts = mu_config_get_msg_options (opts); msgopts = mu_config_get_msg_options (opts) | MU_MSG_OPTION_VERIFY;
vdata.report = NULL;
sigstat = MU_MSG_PART_SIG_STATUS_UNSIGNED; vdata.combined_status = MU_MSG_PART_SIG_STATUS_UNSIGNED;
mu_msg_part_foreach (msg, msgopts, mu_msg_part_foreach (msg, msgopts,
(MuMsgPartForeachFunc)each_sig, &sigstat); (MuMsgPartForeachFunc)each_sig, &vdata);
if (!opts->quiet) { if (!opts->quiet)
const char *verdict; print_verdict (&vdata, !opts->nocolor);
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);
}
mu_msg_unref (msg); 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 #else
MuError MuError