New source file implementing a very basic certificate database. This is
authorJeffrey Stedfast <fejj@ximian.com>
Tue, 30 Jul 2002 19:16:11 +0000 (19:16 +0000)
committerJeffrey Stedfast <fejj@src.gnome.org>
Tue, 30 Jul 2002 19:16:11 +0000 (19:16 +0000)
2002-07-30  Jeffrey Stedfast  <fejj@ximian.com>

* camel-certdb.c: New source file implementing a very basic
certificate database. This is mostly just here because the Mozilla
NSS certdb seems to not be working for everyone's Evolution
install (works fine for me and Ettore but not many other people).

* camel-tcp-stream-ssl.c (ssl_bad_cert): If we have this
certificate in our own CamelCertDB, then get the trust value from
that and only prompt the user if the trust is unknown.

* camel-tcp-stream-openssl.c (ssl_verify): Same.

* camel.c (camel_init): Create our default certdb.

camel/ChangeLog
camel/Makefile.am
camel/camel-folder.h
camel/camel-private.h
camel/camel-tcp-stream-openssl.c
camel/camel-tcp-stream-ssl.c
camel/camel.c

index b979b84..e17722a 100644 (file)
@@ -1,3 +1,18 @@
+2002-07-30  Jeffrey Stedfast  <fejj@ximian.com>
+
+       * camel-certdb.c: New source file implementing a very basic
+       certificate database. This is mostly just here because the Mozilla
+       NSS certdb seems to not be working for everyone's Evolution
+       install (works fine for me and Ettore but not many other people).
+
+       * camel-tcp-stream-ssl.c (ssl_bad_cert): If we have this
+       certificate in our own CamelCertDB, then get the trust value from
+       that and only prompt the user if the trust is unknown.
+
+       * camel-tcp-stream-openssl.c (ssl_verify): Same.
+
+       * camel.c (camel_init): Create our default certdb.
+
 2002-07-30  Peter Williams  <peterw@ximian.com>
 
        * providers/imap/camel-imap-folder.c (imap_transfer_offline): Use
index 71fbe00..642641e 100644 (file)
@@ -22,6 +22,8 @@ libcamel_la_SOURCES =                                 \
        camel-address.c                         \
        camel-arg.c                             \
        camel-block-file.c                      \
+       camel-certdb.c                          \
+       camel-charset-map.c                     \
        camel-cipher-context.c                  \
        camel-cms-context.c                     \
        camel-data-cache.c                      \
@@ -110,7 +112,6 @@ libcamel_la_SOURCES =                               \
        camel-vee-folder.c                      \
        camel-vee-store.c                       \
        camel-vtrash-folder.c                   \
-       camel-charset-map.c                     \
        camel.c                                 \
        gstring-util.c                          \
        hash-table-utils.c                      \
@@ -121,6 +122,7 @@ libcamelinclude_HEADERS =                   \
        camel-address.h                         \
        camel-arg.h                             \
        camel-block-file.h                      \
+       camel-certdb.h                          \
        camel-charset-map.h                     \
        camel-cipher-context.h                  \
        camel-cms-context.h                     \
index ebaf080..a1bb7ba 100644 (file)
@@ -29,7 +29,7 @@
 #ifdef __cplusplus
 extern "C" {
 #pragma }
-#endif /* __cplusplus }*/
+#endif /* __cplusplus */
 
 #include <glib.h>
 #include <camel/camel-object.h>
index 5c978da..0530c27 100644 (file)
@@ -215,6 +215,26 @@ struct _CamelDataWrapperPrivate {
 #define CAMEL_DATA_WRAPPER_UNLOCK(dw, l)
 #endif
 
+/* most of this stuff really is private, but the lock can be used by subordinate classes */
+struct _CamelCertDBPrivate {
+#ifdef ENABLE_THREADS
+       GMutex *db_lock;        /* for the db hashtable/array */
+       GMutex *io_lock;        /* load/save lock, for access to saved_count, etc */
+       GMutex *alloc_lock;     /* for setting up and using allocators */
+       GMutex *ref_lock;       /* for reffing/unreffing certs */
+#else
+       gpointer dummy;
+#endif
+};
+
+#ifdef ENABLE_THREADS
+#define CAMEL_CERTDB_LOCK(db, l) (g_mutex_lock (((CamelCertDB *) db)->priv->l))
+#define CAMEL_CERTDB_UNLOCK(db, l) (g_mutex_unlock (((CamelCertDB *) db)->priv->l))
+#else
+#define CAMEL_CERTDB_LOCK(db, l)
+#define CAMEL_CERTDB_UNLOCK(db, l)
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
index 785f5ad..e9880f3 100644 (file)
@@ -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;
index 31a69eb..15b3dcd 100644 (file)
@@ -27,6 +27,7 @@
  * will be used instead.
  */
 
+
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
@@ -55,7 +56,7 @@
 
 #include "camel-tcp-stream-ssl.h"
 #include "camel-session.h"
-
+#include "camel-certdb.h"
 
 /* from md5-utils.h */
 void md5_get_digest (const char *buffer, int buffer_size, unsigned char digest[16]);
@@ -468,6 +469,8 @@ ssl_bad_cert (void *data, PRFileDesc *sockfd)
 {
        unsigned char md5sum[16], fingerprint[40], *f;
        gboolean accept, valid_cert;
+       CamelCertDB *certdb = NULL;
+       CamelCert *ccert = NULL;
        char *prompt, *cert_str;
        CamelTcpStreamSSL *ssl;
        CERTCertificate *cert;
@@ -492,6 +495,32 @@ ssl_bad_cert (void *data, PRFileDesc *sockfd)
        /*issuer = CERT_FindCertByName (CERT_GetDefaultCertDB (), &cert->derIssuer);
          valid_cert = issuer && CERT_VerifySignedData (&cert->signatureWrap, issuer, PR_Now (), NULL);*/
        
+       /* first check our own certificate database to see if we accepted the cert (nss's certdb seems to not work) */
+       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 ? SECSuccess : SECFailure;
+                       }
+               } else {
+                       /* create a new camel-cert */
+                       ccert = camel_certdb_cert_new (certdb);
+                       camel_cert_set_issuer (certdb, ccert, CERT_NameToAscii (&cert->issuer));
+                       camel_cert_set_subject (certdb, ccert, CERT_NameToAscii (&cert->subject));
+                       camel_cert_set_hostname (certdb, ccert, ssl->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"
@@ -533,10 +562,19 @@ ssl_bad_cert (void *data, PRFileDesc *sockfd)
                CERT_ImportCerts (CERT_GetDefaultCertDB (), certUsageSSLServer, 1, certs,
                                  NULL, TRUE, FALSE, cert->nickname);
 #endif
-               return SECSuccess;
+               
+               if (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 SECFailure;
+       return accept ? SECSuccess : SECFailure;
 }
 
 static PRFileDesc *
index 25807b5..7b250a1 100644 (file)
@@ -36,6 +36,7 @@
 #endif /* HAVE_NSS */
 
 #include "camel.h"
+#include "camel-certdb.h"
 #include "camel-mime-utils.h"
 
 gboolean camel_verbose_debug = FALSE;
@@ -44,15 +45,26 @@ gboolean camel_verbose_debug = FALSE;
 static void
 camel_shutdown (void)
 {
+       CamelCertDB *certdb;
+       
        NSS_Shutdown ();
        
        PR_Cleanup ();
+       
+       certdb = camel_certdb_get_default ();
+       if (certdb) {
+               camel_certdb_save (certdb);
+               camel_object_unref (certdb);
+       }
 }
 #endif /* HAVE_NSS */
 
 gint
 camel_init (const char *configdir, gboolean nss_init)
 {
+       CamelCertDB *certdb;
+       char *path;
+       
 #ifdef ENABLE_THREADS
 #ifdef G_THREADS_ENABLED       
        /*g_thread_init (NULL);*/
@@ -68,7 +80,7 @@ camel_init (const char *configdir, gboolean nss_init)
        camel_object_get_type();
 
        camel_mime_utils_init();
-
+       
 #ifdef HAVE_NSS
        if (nss_init) {
                PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 10);
@@ -92,5 +104,18 @@ camel_init (const char *configdir, gboolean nss_init)
        SSL_OptionSetDefault (SSL_V2_COMPATIBLE_HELLO, PR_TRUE /* maybe? */);
 #endif /* HAVE_NSS */
        
+       path = g_strdup_printf ("%s/camel-cert.db", configdir);
+       certdb = camel_certdb_new ();
+       camel_certdb_set_filename (certdb, path);
+       g_free (path);
+       
+       /* if we fail to load, who cares? it'll just be a volatile certdb */
+       camel_certdb_load (certdb);
+       
+       /* set this certdb as the default db */
+       camel_certdb_set_default (certdb);
+       
+       camel_object_unref (certdb);
+       
        return 0;
 }