lib: Update for new querying machinery
Port to c++ and use new APIs.
This commit is contained in:
381
lib/mu-msg-crypto.cc
Normal file
381
lib/mu-msg-crypto.cc
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
** Copyright (C) 2012-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify it
|
||||
** under the terms of the GNU General Public License as published by the
|
||||
** Free Software Foundation; either version 3, or (at your option) any
|
||||
** later version.
|
||||
**
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
**
|
||||
** You should have received a copy of the GNU General Public License
|
||||
** along with this program; if not, write to the Free Software Foundation,
|
||||
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "mu-msg.hh"
|
||||
#include "mu-msg-priv.hh"
|
||||
#include "mu-msg-part.hh"
|
||||
#include "utils/mu-date.h"
|
||||
|
||||
#include <gmime/gmime.h>
|
||||
#include <gmime/gmime-multipart-signed.h>
|
||||
|
||||
using namespace Mu;
|
||||
|
||||
static const char*
|
||||
get_pubkey_algo_name (GMimePubKeyAlgo algo)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
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";
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
get_digestkey_algo_name (GMimeDigestAlgo algo)
|
||||
{
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
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";
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
}
|
||||
|
||||
|
||||
/* get data from the 'certificate' */
|
||||
static char*
|
||||
get_cert_data (GMimeCertificate *cert)
|
||||
{
|
||||
const char /**email,*/ *name, *digest_algo, *pubkey_algo,
|
||||
*keyid, *trust;
|
||||
|
||||
/* email = g_mime_certificate_get_email (cert); */
|
||||
name = g_mime_certificate_get_name (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_TRUST_UNKNOWN: trust = "unknown"; break;
|
||||
case GMIME_TRUST_UNDEFINED: trust = "undefined"; break;
|
||||
case GMIME_TRUST_NEVER: trust = "never"; break;
|
||||
case GMIME_TRUST_MARGINAL: trust = "marginal"; break;
|
||||
case GMIME_TRUST_FULL: trust = "full"; break;
|
||||
case GMIME_TRUST_ULTIMATE: trust = "ultimate"; break;
|
||||
default:
|
||||
g_return_val_if_reached (NULL);
|
||||
}
|
||||
|
||||
return g_strdup_printf (
|
||||
"signer:%s, key:%s (%s,%s), trust:%s",
|
||||
name ? name : "?",
|
||||
/* email ? email : "?", */
|
||||
keyid, pubkey_algo, digest_algo,
|
||||
trust);
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
get_signature_status (GMimeSignatureStatus status)
|
||||
{
|
||||
size_t n;
|
||||
GString *descr;
|
||||
|
||||
struct {
|
||||
GMimeSignatureStatus status;
|
||||
const char *name;
|
||||
} status_info[] = {
|
||||
{ GMIME_SIGNATURE_STATUS_VALID, "valid" },
|
||||
{ GMIME_SIGNATURE_STATUS_GREEN, "green" },
|
||||
{ GMIME_SIGNATURE_STATUS_RED, "red" },
|
||||
{ GMIME_SIGNATURE_STATUS_KEY_REVOKED, "key revoked" },
|
||||
{ GMIME_SIGNATURE_STATUS_KEY_EXPIRED, "key expired" },
|
||||
{ GMIME_SIGNATURE_STATUS_SIG_EXPIRED, "signature expired" },
|
||||
{ GMIME_SIGNATURE_STATUS_KEY_MISSING, "key missing" },
|
||||
{ GMIME_SIGNATURE_STATUS_CRL_MISSING, "crl missing" },
|
||||
{ GMIME_SIGNATURE_STATUS_CRL_TOO_OLD, "crl too old" },
|
||||
{ GMIME_SIGNATURE_STATUS_BAD_POLICY, "bad policy" },
|
||||
{ GMIME_SIGNATURE_STATUS_SYS_ERROR, "system error" },
|
||||
{ GMIME_SIGNATURE_STATUS_TOFU_CONFLICT, "tofu conflict " },
|
||||
};
|
||||
|
||||
descr = g_string_new("");
|
||||
for (n = 0; n != G_N_ELEMENTS(status_info); ++n) {
|
||||
|
||||
if (!(status & status_info[n].status))
|
||||
continue;
|
||||
|
||||
g_string_append_printf (descr, "%s%s",
|
||||
descr->len > 0 ? ", " : "",
|
||||
status_info[n].name);
|
||||
}
|
||||
|
||||
return g_string_free (descr, FALSE);
|
||||
}
|
||||
|
||||
|
||||
/* get a human-readable report about the signature */
|
||||
static char*
|
||||
get_verdict_report (GMimeSignature *msig)
|
||||
{
|
||||
time_t t;
|
||||
const char *created, *expires;
|
||||
gchar *certdata, *report, *status;
|
||||
GMimeSignatureStatus sigstat;
|
||||
|
||||
sigstat = g_mime_signature_get_status (msig);
|
||||
status = get_signature_status(sigstat);
|
||||
|
||||
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 ("%s; created:%s, expires:%s, %s",
|
||||
status, created, expires,
|
||||
certdata ? certdata : "?");
|
||||
g_free (certdata);
|
||||
g_free (status);
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
|
||||
static char*
|
||||
get_signers (GHashTable *signerhash)
|
||||
{
|
||||
GString *gstr;
|
||||
GHashTableIter iter;
|
||||
char *name;
|
||||
|
||||
if (!signerhash || g_hash_table_size(signerhash) == 0)
|
||||
return NULL;
|
||||
|
||||
gstr = g_string_new (NULL);
|
||||
g_hash_table_iter_init (&iter, signerhash);
|
||||
|
||||
while (g_hash_table_iter_next (&iter, reinterpret_cast<void**>(&name), NULL)) {
|
||||
if (gstr->len != 0)
|
||||
g_string_append_c (gstr, ',');
|
||||
gstr = g_string_append (gstr, name);
|
||||
}
|
||||
|
||||
return g_string_free (gstr, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static MuMsgPartSigStatusReport*
|
||||
get_status_report (GMimeSignatureList *sigs)
|
||||
{
|
||||
int i;
|
||||
MuMsgPartSigStatus status;
|
||||
MuMsgPartSigStatusReport *status_report;
|
||||
char *report;
|
||||
GHashTable *signerhash;
|
||||
|
||||
status = MU_MSG_PART_SIG_STATUS_GOOD; /* let's start positive! */
|
||||
signerhash = g_hash_table_new (g_str_hash, g_str_equal);
|
||||
|
||||
for (i = 0, report = NULL; i != g_mime_signature_list_length (sigs);
|
||||
++i) {
|
||||
|
||||
GMimeSignature *msig;
|
||||
GMimeCertificate *cert;
|
||||
GMimeSignatureStatus sigstat;
|
||||
gchar *rep;
|
||||
|
||||
msig = g_mime_signature_list_get_signature (sigs, i);
|
||||
sigstat = g_mime_signature_get_status (msig);
|
||||
|
||||
/* downgrade our expectations */
|
||||
if ((sigstat & GMIME_SIGNATURE_STATUS_ERROR_MASK) &&
|
||||
status != MU_MSG_PART_SIG_STATUS_ERROR)
|
||||
status = MU_MSG_PART_SIG_STATUS_ERROR;
|
||||
else if ((sigstat & GMIME_SIGNATURE_STATUS_RED) &&
|
||||
status == MU_MSG_PART_SIG_STATUS_GOOD)
|
||||
status = MU_MSG_PART_SIG_STATUS_BAD;
|
||||
|
||||
rep = get_verdict_report (msig);
|
||||
report = g_strdup_printf ("%s%s%d: %s",
|
||||
report ? report : "",
|
||||
report ? "; " : "", i + 1,
|
||||
rep);
|
||||
g_free (rep);
|
||||
|
||||
cert = g_mime_signature_get_certificate (msig);
|
||||
if (cert && g_mime_certificate_get_name (cert))
|
||||
g_hash_table_add (
|
||||
signerhash,
|
||||
(gpointer)g_mime_certificate_get_name (cert));
|
||||
}
|
||||
|
||||
status_report = g_slice_new0 (MuMsgPartSigStatusReport);
|
||||
|
||||
status_report->verdict = status;
|
||||
status_report->report = report;
|
||||
status_report->signers = get_signers(signerhash);
|
||||
|
||||
g_hash_table_unref (signerhash);
|
||||
|
||||
return status_report;
|
||||
}
|
||||
|
||||
void
|
||||
Mu::mu_msg_part_sig_status_report_destroy (MuMsgPartSigStatusReport *report)
|
||||
{
|
||||
if (!report)
|
||||
return;
|
||||
|
||||
g_free ((char*)report->report);
|
||||
g_free ((char*)report->signers);
|
||||
|
||||
g_slice_free (MuMsgPartSigStatusReport, report);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
tag_with_sig_status(GObject *part,
|
||||
MuMsgPartSigStatusReport *report)
|
||||
{
|
||||
g_object_set_data_full
|
||||
(part, SIG_STATUS_REPORT, report,
|
||||
(GDestroyNotify)mu_msg_part_sig_status_report_destroy);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Mu::mu_msg_crypto_verify_part (GMimeMultipartSigned *sig, MuMsgOptions opts,
|
||||
GError **err)
|
||||
{
|
||||
/* the signature status */
|
||||
MuMsgPartSigStatusReport *report;
|
||||
GMimeSignatureList *sigs;
|
||||
|
||||
g_return_if_fail (GMIME_IS_MULTIPART_SIGNED(sig));
|
||||
|
||||
sigs = g_mime_multipart_signed_verify (sig, GMIME_VERIFY_NONE, err);
|
||||
if (!sigs) {
|
||||
if (err && !*err)
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"verification failed");
|
||||
return;
|
||||
}
|
||||
|
||||
report = get_status_report (sigs);
|
||||
g_clear_object (&sigs);
|
||||
|
||||
/* tag this part with the signature status check */
|
||||
tag_with_sig_status(G_OBJECT(sig), report);
|
||||
}
|
||||
|
||||
|
||||
static inline void
|
||||
check_decrypt_result(GMimeMultipartEncrypted *part, GMimeDecryptResult *res,
|
||||
GError **err)
|
||||
{
|
||||
GMimeSignatureList *sigs;
|
||||
MuMsgPartSigStatusReport *report;
|
||||
|
||||
if (res) {
|
||||
/* Check if the decrypted part had any embed signatures */
|
||||
sigs = res->signatures;
|
||||
if (sigs) {
|
||||
report = get_status_report (sigs);
|
||||
g_mime_signature_list_clear (sigs);
|
||||
|
||||
/* tag this part with the signature status check */
|
||||
tag_with_sig_status(G_OBJECT(part), report);
|
||||
}
|
||||
else {
|
||||
if (err && !*err)
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"verification failed");
|
||||
}
|
||||
g_object_unref (res);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
GMimeObject* /* this is declared in mu-msg-priv.h */
|
||||
Mu::mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts,
|
||||
MuMsgPartPasswordFunc func, gpointer user_data,
|
||||
GError **err)
|
||||
{
|
||||
GMimeObject *dec;
|
||||
GMimeDecryptResult *res;
|
||||
|
||||
g_return_val_if_fail (GMIME_IS_MULTIPART_ENCRYPTED(enc), NULL);
|
||||
|
||||
res = NULL;
|
||||
dec = g_mime_multipart_encrypted_decrypt (enc, GMIME_DECRYPT_NONE, NULL,
|
||||
&res, err);
|
||||
check_decrypt_result(enc, res, err);
|
||||
|
||||
if (!dec) {
|
||||
if (err && !*err)
|
||||
mu_util_g_set_error (err, MU_ERROR_CRYPTO,
|
||||
"decryption failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dec;
|
||||
}
|
||||
Reference in New Issue
Block a user