aboutsummaryrefslogtreecommitdiffstats
path: root/camel/camel-tcp-stream-openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'camel/camel-tcp-stream-openssl.c')
-rw-r--r--camel/camel-tcp-stream-openssl.c202
1 files changed, 136 insertions, 66 deletions
diff --git a/camel/camel-tcp-stream-openssl.c b/camel/camel-tcp-stream-openssl.c
index 785f5ad74b..e9880f3507 100644
--- a/camel/camel-tcp-stream-openssl.c
+++ b/camel/camel-tcp-stream-openssl.c
@@ -20,6 +20,7 @@
*
*/
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -539,92 +540,161 @@ socket_connect (struct hostent *h, int port)
return fd;
}
-static void
-save_ssl_cert (const char *certid)
-{
- char *path, *filename;
- struct stat st;
- int fd;
-
- path = g_strdup_printf ("%s/.camel_certs", getenv ("HOME"));
- if (mkdir (path, 0700) == -1) {
- if (errno != EEXIST)
- return;
-
- if (stat (path, &st) == -1)
- return;
-
- if (!S_ISDIR (st.st_mode))
- return;
- }
-
- filename = g_strdup_printf ("%s/%s", path, certid);
- g_free (path);
-
- fd = open (filename, O_WRONLY | O_CREAT, 0600);
- if (fd != -1)
- close (fd);
-
- g_free (filename);
-}
-
-static gboolean
-ssl_cert_is_saved (const char *certid)
+static const char *
+x509_strerror (int err)
{
- char *filename;
- struct stat st;
-
- filename = g_strdup_printf ("%s/.camel_certs/%s", getenv ("HOME"), certid);
-
- if (stat (filename, &st) == -1) {
- g_free (filename);
- return FALSE;
+ switch (err) {
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ return _("Unable to get issuer's certificate");
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ return _("Unable to get Certificate Revocation List");
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ return _("Unable to decrypt certificate signature");
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ return _("Unable to decrypt Certificate Revocation List signature");
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ return _("Unable to decode issuer's public key");
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ return _("Certificate signature failure");
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ return _("Certificate Revocation List signature failure");
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ return _("Certificate not yet valid");
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ return _("Certificate has expired");
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ return _("CRL not yet valid");
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ return _("CRL has expired");
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ return _("Error in CRL");
+ case X509_V_ERR_OUT_OF_MEM:
+ return _("Out of memory");
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ return _("Zero-depth self-signed certificate");
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ return _("Self-signed certificate in chain");
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ return _("Unable to get issuer's certificate locally");
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ return _("Unable to verify leaf signature");
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ return _("Certificate chain too long");
+ case X509_V_ERR_CERT_REVOKED:
+ return _("Certificate Revoked");
+ case X509_V_ERR_INVALID_CA:
+ return _("Invalid Certificate Authority (CA)");
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ return _("Path length exceeded");
+ case X509_V_ERR_INVALID_PURPOSE:
+ return _("Invalid purpose");
+ case X509_V_ERR_CERT_UNTRUSTED:
+ return _("Certificate untrusted");
+ case X509_V_ERR_CERT_REJECTED:
+ return _("Certificate rejected");
+ /* These are 'informational' when looking for issuer cert */
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ return _("Subject/Issuer mismatch");
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ return _("AKID/SKID mismatch");
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ return _("AKID/Issuer serial mismatch");
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ return _("Key usage does not support certificate signing");
+ /* The application is not happy */
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ return _("Error in application verification");
+ default:
+ return _("Unknown");
}
-
- g_free (filename);
-
- return st.st_uid == getuid ();
}
static int
ssl_verify (int ok, X509_STORE_CTX *ctx)
{
+ unsigned char md5sum[16], fingerprint[40], *f;
CamelTcpStreamSSL *stream;
+ CamelService *service;
+ CamelCertDB *certdb = NULL;
+ CamelCert *ccert = NULL;
+ char *prompt, *cert_str;
+ char buf[257];
X509 *cert;
SSL *ssl;
- int err;
+ int i, err;
+
+ if (ok)
+ return TRUE;
ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ());
stream = SSL_CTX_get_app_data (ssl->ctx);
+ if (!stream)
+ return FALSE;
+
+ service = stream->priv->service;
cert = X509_STORE_CTX_get_current_cert (ctx);
err = X509_STORE_CTX_get_error (ctx);
- if (stream)
- ok = ssl_cert_is_saved (stream->priv->expected_host);
+ /* calculate the MD5 hash of the raw certificate */
+ X509_digest (cert, EVP_md5 (), md5sum, sizeof (md5sum));
+ for (i = 0, f = fingerprint; i < 16; i++, f += 3)
+ sprintf (f, "%.2x%c", md5sum[i], i != 15 ? ':' : '\0');
- if (!ok && stream) {
- CamelService *service = stream->priv->service;
- char *prompt, *cert_str;
- char buf[257];
-
#define GET_STRING(name) X509_NAME_oneline (name, buf, 256)
-
- cert_str = g_strdup_printf (_("Issuer: %s\n"
- "Subject: %s"),
- GET_STRING (X509_get_issuer_name (cert)),
- GET_STRING (X509_get_subject_name (cert)));
-
- prompt = g_strdup_printf (_("Bad certificate from %s:\n\n%s\n\n"
- "Do you wish to accept anyway?"),
- service->url->host, cert_str);
-
- ok = camel_session_alert_user (service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE);
- g_free (prompt);
-
- if (ok)
- save_ssl_cert (stream->priv->expected_host);
+
+ certdb = camel_certdb_get_default ();
+ if (certdb) {
+ ccert = camel_certdb_get_cert (certdb, fingerprint);
+ if (ccert) {
+ if (ccert->trust != CAMEL_CERT_TRUST_UNKNOWN) {
+ accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
+ camel_certdb_cert_unref (certdb, ccert);
+ camel_object_unref (certdb);
+
+ return accept;
+ }
+ } else {
+ /* create a new camel-cert */
+ ccert = camel_certdb_cert_new (certdb);
+ camel_cert_set_issuer (certdb, ccert, GET_STRING (X509_get_issuer_name (cert)));
+ camel_cert_set_subject (certdb, ccert, GET_STRING (X509_get_subject_name (cert)));
+ camel_cert_set_hostname (certdb, ccert, stream->priv->expected_host);
+ camel_cert_set_fingerprint (certdb, ccert, fingerprint);
+ camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
+
+ /* Add the certificate to our db */
+ camel_certdb_add (certdb, ccert);
+ }
+ }
+
+ cert_str = g_strdup_printf (_("Issuer: %s\n"
+ "Subject: %s\n"
+ "Fingerprint: %s\n"
+ "Signature: %s"),
+ GET_STRING (X509_get_issuer_name (cert)),
+ GET_STRING (X509_get_subject_name (cert)),
+ fingerprint, cert->valid ? _("GOOD") : _("BAD"));
+
+ prompt = g_strdup_printf (_("Bad certificate from %s:\n\n%s\n\n%s\n\n"
+ "Do you wish to accept anyway?"),
+ service->url->host, cert_str, x509_strerror (err));
+
+ ok = camel_session_alert_user (service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE);
+ g_free (prompt);
+
+ if (ok && ccert) {
+ camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_FULLY);
+ camel_certdb_touch (certdb);
+ }
+
+ if (certdb) {
+ camel_certdb_cert_unref (certdb, ccert);
+ camel_object_unref (certdb);
}
return ok;