From adc23614a166b7203498eae7be8abf58fd2e7e5f Mon Sep 17 00:00:00 2001 From: djcb Date: Thu, 30 Aug 2012 12:53:52 +0300 Subject: [PATCH] * enable signature verification (WIP) --- lib/mu-msg-crypto.c | 396 +++++--------------------------------------- lib/mu-msg-crypto.h | 98 ----------- lib/mu-msg-part.c | 115 +++---------- lib/mu-msg-part.h | 17 +- lib/mu-msg-priv.h | 28 ++-- lib/mu-msg-sexp.c | 8 +- mu/mu-cmd.c | 78 ++++----- 7 files changed, 130 insertions(+), 610 deletions(-) diff --git a/lib/mu-msg-crypto.c b/lib/mu-msg-crypto.c index 0e589aa2..20b96ee6 100644 --- a/lib/mu-msg-crypto.c +++ b/lib/mu-msg-crypto.c @@ -125,25 +125,6 @@ get_gpg_crypto_context (MuMsgOptions opts, GError **err) return cctx; } -/* static GMimeCryptoContext* */ -/* get_pkcs7_crypto_context (MuMsgOptions opts, GError **err) */ -/* { */ -/* GMimeCryptoContext *cctx; */ - -/* 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(cctx), FALSE); */ - -/* return cctx; */ -/* } */ - - static GMimeCryptoContext* get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, @@ -152,14 +133,11 @@ get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, 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->pw_func = password_func ? password_func : dummy_password_func; cbdata->user_data = user_data; g_object_set_data_full (G_OBJECT(cctx), CALLBACK_DATA, @@ -168,239 +146,26 @@ get_crypto_context (MuMsgOptions opts, MuMsgPartPasswordFunc password_func, } - - -const char* -get_pubkey_algo_name (GMimePubKeyAlgo algo) -{ - 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 algorithm"; - } -} - -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 algorithm"; - } -} - - -static void -harvest_certificate_info (GMimeSignature *sig, MuMsgPartSigInfo *siginfo) -{ - GMimeCertificate *cert; - - cert = g_mime_signature_get_certificate (sig); - if (!cert) - return; /* nothing to harvest */ - - siginfo->_cert = cert; - - siginfo->issuer_serial = g_mime_certificate_get_issuer_serial (cert); - siginfo->issuer_name = g_mime_certificate_get_issuer_name (cert); - siginfo->fingerprint = g_mime_certificate_get_fingerprint (cert); - siginfo->key_id = g_mime_certificate_get_key_id (cert); - siginfo->email = g_mime_certificate_get_email (cert); - siginfo->name = g_mime_certificate_get_name (cert); - - siginfo->pubkey_algo = get_pubkey_algo_name - (g_mime_certificate_get_pubkey_algo (cert)); - siginfo->digest_algo = get_digestkey_algo_name - (g_mime_certificate_get_digest_algo (cert)); -} - - -static MuMsgPartSigInfo* -sig_info_new (GMimeSignature *sig) -{ - MuMsgPartSigInfo *siginfo; - MuMsgPartSigStatus status; - - switch (g_mime_signature_get_status (sig)) { - case GMIME_SIGNATURE_STATUS_GOOD: - status = MU_MSG_PART_SIG_STATUS_GOOD; break; - case GMIME_SIGNATURE_STATUS_BAD: - status = MU_MSG_PART_SIG_STATUS_BAD; break; - default: - status = MU_MSG_PART_SIG_STATUS_ERROR; break; - } - - if (status != MU_MSG_PART_SIG_STATUS_GOOD) { - GMimeSignatureError sigerr; - sigerr = g_mime_signature_get_errors (sig); - if (sigerr & GMIME_SIGNATURE_ERROR_EXPSIG) - status |= MU_MSG_PART_SIG_STATUS_EXPSIG; - if (sigerr & GMIME_SIGNATURE_ERROR_NO_PUBKEY) - status |= MU_MSG_PART_SIG_STATUS_NO_PUBKEY; - if (sigerr & GMIME_SIGNATURE_ERROR_EXPKEYSIG) - status |= MU_MSG_PART_SIG_STATUS_EXPKEYSIG; - if (sigerr & GMIME_SIGNATURE_ERROR_REVKEYSIG) - status |= MU_MSG_PART_SIG_STATUS_REVKEYSIG; - if (sigerr & GMIME_SIGNATURE_ERROR_UNSUPP_ALGO) - status |= MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO; - } - - siginfo = g_new0 (MuMsgPartSigInfo, 1); - siginfo->status = status; - siginfo->created = g_mime_signature_get_created (sig); - siginfo->expires = g_mime_signature_get_expires (sig); - - harvest_certificate_info (sig, siginfo); - - return siginfo; -} - -static void -sig_info_destroy (MuMsgPartSigInfo *siginfo) -{ - if (!siginfo) - return; - - if (G_IS_OBJECT(siginfo->_cert)) - g_object_unref (siginfo->_cert); - - g_free (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, MuMsgOptions opts, - GError **err) +static MuMsgPartSigStatus +get_verdict (GMimeSignatureList *sigs) { int i; - GMimeSignatureList *sigs; - GMimeCryptoContext *cctx; - GSList *siginfos; - - if (!GMIME_IS_MULTIPART_SIGNED (sigmpart)) { - mu_util_g_set_error (err, MU_ERROR_IN_PARAMETERS, - "not a multipart/signed part"); - return NULL; /* error */ - } - - /* dummy is good, since we don't need a password when checking - * signatures */ - cctx = get_crypto_context (opts, dummy_password_func, NULL, err); - - if (!cctx) /* return a fake siginfos with the error */ - 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)); - - siginfos = g_slist_prepend (siginfos, siginfo); - } - - return siginfos; -} - - - -void -mu_msg_part_free_sig_infos (GSList *siginfos) -{ - g_slist_foreach (siginfos, - (GFunc)sig_info_destroy, NULL); - g_slist_free (siginfos); -} - - -/* - * - if there's any signature with MU_MSG_PART_SIG_STATUS_(ERROR|FAIL), - * the verdict is MU_MSG_PART_SIG_STATUS_ERROR - * - if not, if there's any signature with MU_MSG_PART_SIG_STATUS_BAD - * the verdict is MU_MSG_PART_SIG_STATUS_BAD - * - if not, if there's any signature with MU_MSG_PART_SIG_STATUS_GOOD - * the verdict is MU_MSG_PART_SIG_STATUS_GOOD - * - if not, the verdic is MU_MSG_PART_SIG_STATUS_UNKNOWN - */ -MuMsgPartSigStatus -mu_msg_part_sig_infos_verdict (GSList *sig_infos) -{ - GSList *cur; MuMsgPartSigStatus status; - status = MU_MSG_PART_SIG_STATUS_UNKNOWN; + status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */ - for (cur = sig_infos; cur; cur = g_slist_next (cur)) { - MuMsgPartSigInfo *siginfo; - siginfo = (MuMsgPartSigInfo*)cur->data; + for (i = 0; i != g_mime_signature_list_length (sigs); ++i) { - /* if there's an error/failure, the verdict is error */ - if (siginfo->status & MU_MSG_PART_SIG_STATUS_ERROR || - siginfo->status & MU_MSG_PART_SIG_STATUS_FAIL) - return MU_MSG_PART_SIG_STATUS_ERROR; + GMimeSignature *msig; + GMimeSignatureStatus sigstat; + msig = g_mime_signature_list_get_signature (sigs, i); + sigstat = g_mime_signature_get_status (msig); - if (siginfo->status & MU_MSG_PART_SIG_STATUS_BAD) - status = MU_MSG_PART_SIG_STATUS_BAD; - - if ((siginfo->status & MU_MSG_PART_SIG_STATUS_GOOD) && - status == MU_MSG_PART_SIG_STATUS_UNKNOWN) - status = MU_MSG_PART_SIG_STATUS_GOOD; + 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; + } } return status; @@ -408,122 +173,41 @@ mu_msg_part_sig_infos_verdict (GSList *sig_infos) - -const char* -mu_msg_part_sig_status_to_string (MuMsgPartSigStatus status) +MuMsgPartSigStatus +mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, + GError **err) { - switch (status) { - case MU_MSG_PART_SIG_STATUS_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: - return "no public key found"; - case MU_MSG_PART_SIG_STATUS_EXPKEYSIG: - return "expired public key"; - case MU_MSG_PART_SIG_STATUS_REVKEYSIG: - return "revoked public key"; - case MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO: - return "unsupported algorithm"; - default: - g_warning ("%s: invalid status %d", - __FUNCTION__, status); - return "invalid status"; - } -} + MuMsgPartSigStatus sigstat; + GMimeCryptoContext *ctx; + GMimeSignatureList *sigs; + g_return_val_if_fail (GMIME_IS_MULTIPART_SIGNED(sig), + MU_MSG_PART_SIG_STATUS_FAIL); -char* -mu_msg_part_sig_statuses_to_string (MuMsgPartSigStatus status) -{ - unsigned u; - GString *gstr; - - MuMsgPartSigStatus statuses[] = { - MU_MSG_PART_SIG_STATUS_UNKNOWN, - 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, - MU_MSG_PART_SIG_STATUS_REVKEYSIG, - MU_MSG_PART_SIG_STATUS_UNSUPP_ALGO - }; - - 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 (!(status & statuses[u])) - continue; - - statstr = mu_msg_part_sig_status_to_string (statuses[u]); - if (gstr->len != 0) - gstr = g_string_append (gstr, ", "); - - gstr = g_string_append (gstr, statstr); + 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 g_string_free (gstr, FALSE); + sigs = g_mime_multipart_signed_verify (sig, ctx, err); + g_object_unref (ctx); + if (!sigs) { + if (err && !*err) + mu_util_g_set_error (err, MU_ERROR_CRYPTO, + "verification failed"); + return MU_MSG_PART_SIG_STATUS_FAIL; + } + + sigstat = get_verdict (sigs); + g_mime_signature_list_clear (sigs); + + return sigstat; } -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)", - info->pubkey_algo, info->digest_algo); - - g_string_append_printf (gstr, "; created: %s, expires: %s", - mu_date_str_s ("%c", info->created), - mu_date_str_s ("%c", info->expires)); - - if (info->name || info->email) - g_string_append_printf (gstr, "; who:%s %s", - info->name ? info-> name : "", - info->email ? info->email : ""); - - if (info->issuer_name && info->issuer_serial) - g_string_append_printf (gstr, "; issuer: %s (%s)", - info->issuer_name, - info->issuer_serial); - if (info->fingerprint) - g_string_append_printf (gstr, "; fingerprint: %s", - info->fingerprint); - - return g_string_free (gstr, FALSE); -} - GMimeObject* /* this is declared in mu-msg-priv.h */ mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, diff --git a/lib/mu-msg-crypto.h b/lib/mu-msg-crypto.h index 7d1b8e4a..2bb755ba 100644 --- a/lib/mu-msg-crypto.h +++ b/lib/mu-msg-crypto.h @@ -25,104 +25,6 @@ #include #include -/* the signature status */ -enum _MuMsgPartSigStatus { - MU_MSG_PART_SIG_STATUS_UNKNOWN = 0, - MU_MSG_PART_SIG_STATUS_GOOD = 1 << 0, - - MU_MSG_PART_SIG_STATUS_BAD = 1 << 1, - MU_MSG_PART_SIG_STATUS_ERROR = 1 << 2, - - /* 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; - - -struct _MuMsgPartSigInfo { - time_t created; /* creation time */ - time_t expires; /* expiration time */ - MuMsgPartSigStatus status; /* status of the signature */ - - const char *issuer_serial; /* issuer's serial #*/ - const char *issuer_name; /* issuer name */ - const char *fingerprint; /* fingerprint */ - const char *key_id; /* key id */ - const char *email; - const char *name; - - 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; -}; -typedef struct _MuMsgPartSigInfo MuMsgPartSigInfo; - - -/** - * get a human-readable string describing @param status; note, status - * must match a _single_ status. - * - * @param status - * - * @return a constant string describing status - */ -const char* mu_msg_part_sig_status_to_string (MuMsgPartSigStatus 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 - * - if not, if there's any signature with MU_MSG_PART_SIG_STATUS_BAD - * the verdict is MU_MSG_PART_SIG_STATUS_BAD - * - if not, if there's any signature with MU_MSG_PART_SIG_STATUS_GOOD - * the verdict is MU_MSG_PART_SIG_STATUS_GOOD - * - if not, the verdic is MU_MSG_PART_SIG_STATUS_UNKNOWN - * - * @param sig_infos - * - * @return the status - */ -MuMsgPartSigStatus mu_msg_part_sig_infos_verdict (GSList *sig_infos); - -/** - * 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 - * - * @param info a MuMsgPartSigInfo ptr - * - * @return a newly allocated string (g_free) - */ -char* mu_msg_part_sig_info_to_string (MuMsgPartSigInfo *info) - G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT; - -/** - * free the list of MuMsgPartSigInfo structures - * - * @param siginfo - */ -void mu_msg_part_free_sig_infos (GSList *siginfos); struct _MuMsgDecryptedPart; typedef struct _MuMsgDecryptedPart MuMsgDecryptedPart; diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index 7d77b638..f3deffed 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -220,87 +220,6 @@ get_part_size (GMimePart *part) } -/* #ifdef BUILD_CRYPTO */ -/* static void */ -/* check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi, */ -/* MuMsgOptions opts) */ -/* { */ -/* GMimeContentType *ctype; */ -/* GError *err; */ -/* gboolean pkcs7; */ - -/* if (!GMIME_IS_MULTIPART_SIGNED (parent)) */ -/* 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_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) { */ -/* g_warning ("error verifying signature: %s", err->message); */ -/* g_clear_error (&err); */ -/* } */ -/* } */ - - - -/* #endif /\*BUILD_CRYPTO*\/ */ - -/* static gboolean */ -/* init_msg_part_from_mime_part (MuMsgOptions opts, GMimeObject *parent, */ -/* GMimePart *part, MuMsgPart *pi) */ -/* { */ -/* const gchar *fname, *descr; */ -/* GMimeContentType *ct; */ - -/* ct = g_mime_object_get_content_type ((GMimeObject*)part); */ -/* if (GMIME_IS_CONTENT_TYPE(ct)) { */ -/* pi->type = (char*)g_mime_content_type_get_media_type (ct); */ -/* pi->subtype = (char*)g_mime_content_type_get_media_subtype (ct); */ -/* } */ - -/* pi->disposition = (char*)g_mime_object_get_disposition */ -/* ((GMimeObject*)part); */ - -/* fname = g_mime_part_get_filename (part); */ -/* pi->file_name = fname ? mu_str_utf8ify (fname) : NULL; */ - -/* descr = g_mime_part_get_content_description (part); */ -/* pi->description = descr ? mu_str_utf8ify (descr) : NULL; */ -/* pi->size = get_part_size (part); */ -/* pi->part_type = MU_MSG_PART_TYPE_LEAF; */ - -/* if (!pi->disposition || */ -/* g_ascii_strcasecmp (pi->disposition, */ -/* GMIME_DISPOSITION_INLINE) == 0) */ -/* pi->part_type |= MU_MSG_PART_TYPE_INLINE; */ - -/* if (GMIME_IS_MULTIPART_SIGNED (parent)) */ -/* pi->part_type |= MU_MSG_PART_TYPE_SIGNED; */ - -/* /\* if we have crypto support, check the signature if there is one *\/ */ -/* #ifdef BUILD_CRYPTO */ -/* if (opts & MU_MSG_OPTION_CHECK_SIGNATURES) */ -/* check_signature_maybe (parent, (GMimeObject*)part, */ -/* pi, opts); */ -/* #endif /\*BUILD_CRYPTO*\/ */ - -/* return TRUE; */ -/* } */ - - - static void cleanup_filename (char *fname) { @@ -378,13 +297,28 @@ get_disposition (GMimeObject *mobj) return MU_MSG_PART_TYPE_NONE; } +#define SIG_STATUS "sig-status" + /* call 'func' with information about this MIME-part */ static gboolean -handle_signed_part (MuMsg *msg, - GMimeMultipartSigned *part, GMimeObject *parent, - MuMsgOptions opts, unsigned index, - MuMsgPartForeachFunc func, gpointer user_data) +check_signature (MuMsg *msg, GMimeMultipartSigned *part, MuMsgOptions opts) { + /* the signature status */ + MuMsgPartSigStatus sigstat; + GError *err; + + err = NULL; + sigstat = 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)); + return TRUE; } @@ -414,6 +348,12 @@ 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); @@ -480,9 +420,8 @@ handle_mime_object (MuMsg *msg, (msg, GMIME_MESSAGE_PART(mobj), parent, opts, index, func, user_data); else if (GMIME_IS_MULTIPART_SIGNED (mobj)) - return handle_signed_part - (msg, GMIME_MULTIPART_SIGNED (mobj), - parent, opts, index, func, user_data); + return check_signature + (msg, GMIME_MULTIPART_SIGNED (mobj), opts); else if (GMIME_IS_MULTIPART_ENCRYPTED (mobj)) return handle_encrypted_part (msg, GMIME_MULTIPART_ENCRYPTED (mobj), diff --git a/lib/mu-msg-part.h b/lib/mu-msg-part.h index 3012644c..6287f2c7 100644 --- a/lib/mu-msg-part.h +++ b/lib/mu-msg-part.h @@ -52,6 +52,18 @@ enum _MuMsgPartType { typedef enum _MuMsgPartType MuMsgPartType; +/* the signature status */ +enum _MuMsgPartSigStatus { + MU_MSG_PART_SIG_STATUS_UNSIGNED = 0, + + MU_MSG_PART_SIG_STATUS_GOOD, + MU_MSG_PART_SIG_STATUS_BAD, + MU_MSG_PART_SIG_STATUS_ERROR, + MU_MSG_PART_SIG_STATUS_FAIL +}; +typedef enum _MuMsgPartSigStatus MuMsgPartSigStatus; + + struct _MuMsgPart { /* index of this message part */ @@ -69,10 +81,9 @@ struct _MuMsgPart { gpointer data; /* opaque data */ - MuMsgPartType part_type; + MuMsgPartType part_type; + MuMsgPartSigStatus sig_status; - /* crypto stuff */ - GSList *sig_infos; /* list of MuMsgPartSig */ }; typedef struct _MuMsgPart MuMsgPart; diff --git a/lib/mu-msg-priv.h b/lib/mu-msg-priv.h index 59b19e45..78adecb9 100644 --- a/lib/mu-msg-priv.h +++ b/lib/mu-msg-priv.h @@ -108,19 +108,6 @@ void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, gpointer user_data); #ifdef BUILD_CRYPTO -/** - * get signature information for the mime part - * - * @param part a multipart/sigde part - * @param opts options for the signature verification (we only use the - * crypto-related options in opts) - * @param err receives error info - * - * @return a list of MuMsgPartSig, or NULL - */ -GSList* mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, - MuMsgOptions opts, GError **err); - /** * callback function to retrieve a password from the user * @@ -134,6 +121,21 @@ GSList* mu_msg_mime_sig_infos (GMimeMultipartSigned *sigmpart, typedef char* (*MuMsgPartPasswordFunc) (const char *user_id, const char *prompt_ctx, gboolean reprompt, gpointer user_data); + +/** + * verify the signature of a signed message part + * + * @param sig a signed message part + * @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 + */ +MuMsgPartSigStatus mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts, + GError **err); + + /** * decrypt the given encrypted mime multipart * diff --git a/lib/mu-msg-sexp.c b/lib/mu-msg-sexp.c index bcdef24c..c903c989 100644 --- a/lib/mu-msg-sexp.c +++ b/lib/mu-msg-sexp.c @@ -263,10 +263,12 @@ elvis (const char *s1, const char *s2) } static const char* -sig_verdict (GSList *sig_infos) +sig_verdict (MuMsgPart *mpart) { #ifdef BUILD_CRYPTO - switch (mu_msg_part_sig_infos_verdict (sig_infos)) { + MuMsgPartSigStatus sigstat; + + switch (mpart->sig_status) { case MU_MSG_PART_SIG_STATUS_GOOD: return ":signature good"; case MU_MSG_PART_SIG_STATUS_BAD: @@ -338,7 +340,7 @@ each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) parttype, mu_msg_part_maybe_attachment (part) ? "t" : "nil", (int)part->size, - sig_verdict (part->sig_infos)); + sig_verdict (part)); g_free (pinfo->parts); pinfo->parts = tmp; diff --git a/mu/mu-cmd.c b/mu/mu-cmd.c index 389f7bb8..dc7ddf34 100644 --- a/mu/mu-cmd.c +++ b/mu/mu-cmd.c @@ -401,40 +401,14 @@ mu_cmd_remove (MuStore *store, MuConfig *opts, GError **err) #ifdef BUILD_CRYPTO -struct _VData { - MuMsgPartSigStatus status; - MuConfig*opts; - gchar *msg; -}; -typedef struct _VData VData; - - -static void -each_sig (MuMsg *msg, MuMsgPart *part, VData *vdata) + static void +each_sig (MuMsg *msg, MuMsgPart *part, MuMsgPartSigStatus *sigstat) { - GSList *cur; - - if (!part->sig_infos) + if (*sigstat == MU_MSG_PART_SIG_STATUS_BAD || + *sigstat == MU_MSG_PART_SIG_STATUS_ERROR) return; - 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)) { - - 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; - } + *sigstat = part->sig_status; } MuError @@ -442,7 +416,7 @@ mu_cmd_verify (MuConfig *opts, GError **err) { MuMsg *msg; MuMsgOptions msgopts; - VData vdata; + MuMsgPartSigStatus sigstat; g_return_val_if_fail (opts, MU_ERROR_INTERNAL); g_return_val_if_fail (opts->cmd == MU_CONFIG_CMD_VERIFY, @@ -454,34 +428,40 @@ mu_cmd_verify (MuConfig *opts, GError **err) msgopts = mu_config_get_msg_options (opts); - vdata.status = MU_MSG_PART_SIG_STATUS_UNKNOWN; - vdata.opts = opts; - vdata.msg = NULL; - - /* TODO: update for decryption */ - mu_msg_part_foreach (msg, msgopts, (MuMsgPartForeachFunc)each_sig, &vdata); - - /* 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; + sigstat = MU_MSG_PART_SIG_STATUS_UNSIGNED; + mu_msg_part_foreach (msg, msgopts, + (MuMsgPartForeachFunc)each_sig, &sigstat); if (!opts->quiet) { - gchar *str; - str = mu_msg_part_sig_statuses_to_string (vdata.status); - g_print ("verdict: %s\n", str); - g_free (str); + 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); } mu_msg_unref (msg); - return vdata.status == MU_MSG_PART_SIG_STATUS_GOOD ? MU_OK : MU_ERROR; + return sigstat == MU_MSG_PART_SIG_STATUS_GOOD ? MU_OK : MU_ERROR; } #else MuError mu_cmd_verify (MuConfig *opts, GError **err) { - g_warning ("your version of mu does not support the 'verify' command"); + g_warning ("this version of mu does not support the 'verify' command"); return MU_ERROR_IN_PARAMETERS; }