diff options
author | Jeffrey Stedfast <fejj@ximian.com> | 2002-07-31 03:16:11 +0800 |
---|---|---|
committer | Jeffrey Stedfast <fejj@src.gnome.org> | 2002-07-31 03:16:11 +0800 |
commit | b0633536f2b5e11a1a1ab032bad38218c31173db (patch) | |
tree | 69da48419125f8df2b6fe07166fd526b5353d20a | |
parent | edc715415f16324b248eca2067d2700042a27ed4 (diff) | |
download | gsoc2013-evolution-b0633536f2b5e11a1a1ab032bad38218c31173db.tar.gz gsoc2013-evolution-b0633536f2b5e11a1a1ab032bad38218c31173db.tar.zst gsoc2013-evolution-b0633536f2b5e11a1a1ab032bad38218c31173db.zip |
New source file implementing a very basic certificate database. This is
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.
svn path=/trunk/; revision=17642
-rw-r--r-- | camel/ChangeLog | 15 | ||||
-rw-r--r-- | camel/Makefile.am | 4 | ||||
-rw-r--r-- | camel/camel-folder.h | 2 | ||||
-rw-r--r-- | camel/camel-private.h | 20 | ||||
-rw-r--r-- | camel/camel-tcp-stream-openssl.c | 202 | ||||
-rw-r--r-- | camel/camel-tcp-stream-ssl.c | 44 | ||||
-rw-r--r-- | camel/camel.c | 27 |
7 files changed, 242 insertions, 72 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog index b979b8466e..e17722a7c5 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -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 diff --git a/camel/Makefile.am b/camel/Makefile.am index 71fbe008fa..642641e12f 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -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 \ diff --git a/camel/camel-folder.h b/camel/camel-folder.h index ebaf080457..a1bb7baf66 100644 --- a/camel/camel-folder.h +++ b/camel/camel-folder.h @@ -29,7 +29,7 @@ #ifdef __cplusplus extern "C" { #pragma } -#endif /* __cplusplus }*/ +#endif /* __cplusplus */ #include <glib.h> #include <camel/camel-object.h> diff --git a/camel/camel-private.h b/camel/camel-private.h index 5c978da9ac..0530c27fe7 100644 --- a/camel/camel-private.h +++ b/camel/camel-private.h @@ -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 */ 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; diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c index 31a69eb371..15b3dcd8fb 100644 --- a/camel/camel-tcp-stream-ssl.c +++ b/camel/camel-tcp-stream-ssl.c @@ -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 * diff --git a/camel/camel.c b/camel/camel.c index 25807b5aac..7b250a14b1 100644 --- a/camel/camel.c +++ b/camel/camel.c @@ -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; } |