aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camel/ChangeLog31
-rw-r--r--camel/camel-certdb.c6
-rw-r--r--camel/camel-certdb.h1
-rw-r--r--camel/camel-file-utils.c2
-rw-r--r--camel/camel-tcp-stream-ssl.c434
5 files changed, 381 insertions, 93 deletions
diff --git a/camel/ChangeLog b/camel/ChangeLog
index 2805e19cb1..625f86ba05 100644
--- a/camel/ChangeLog
+++ b/camel/ChangeLog
@@ -1,3 +1,34 @@
+2002-10-15 Not Zed <NotZed@Ximian.com>
+
+ * camel-tcp-stream-ssl.c (cert_fingerprint): helper, build
+ fingerprint.
+ (camel_certdb_nss_cert_get): Helper for nss certs. Lookup cert.
+ As well as fingerprint, the whole raw cert is checked for
+ validity.
+ (camel_certdb_nss_cert_add): Add an nss cert to a certdb, also
+ saves the cert by fingerprint in ~/.camel_certs/.
+ (ssl_bad_cert): Changed to use above functions to simplify logic.
+ (ssl_bad_cert): Also added non-compiled code which mimics what
+ mozilla does, but it doesn't work right :-/
+ (camel_certdb_nss_cert_set): Save the raw cert associated with a
+ cert.
+
+ * camel-certdb.c (certdb_cert_free): Free the raw cert data if
+ set.
+
+2002-10-14 Not Zed <NotZed@Ximian.com>
+
+ * camel-file-utils.c (camel_file_util_encode_string): Encode a
+ length of 0 as 1, not 0, to match the decode code.
+
+2002-10-03 Not Zed <NotZed@Ximian.com>
+
+ * camel-folder.c
+ (filter_filter): Do the filtering ourselves. This is so a problem
+ like a missing uid (which can happen legitemitely) doesn't
+ prematurely abort filtering. Stop on all other errors. See
+ #31667.
+
2002-10-16 Jeffrey Stedfast <fejj@ximian.com>
* camel-gpg-context.c (camel_gpg_context_new): No longer takes a
diff --git a/camel/camel-certdb.c b/camel/camel-certdb.c
index 14020fec16..7f944f8fcd 100644
--- a/camel/camel-certdb.c
+++ b/camel/camel-certdb.c
@@ -234,7 +234,7 @@ certdb_cert_load (CamelCertDB *certdb, FILE *istream)
CamelCert *cert;
cert = camel_certdb_cert_new (certdb);
-
+
if (camel_file_util_decode_string (istream, &cert->issuer) == -1)
goto error;
if (camel_file_util_decode_string (istream, &cert->subject) == -1)
@@ -264,7 +264,7 @@ camel_certdb_load (CamelCertDB *certdb)
g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
g_return_val_if_fail (certdb->filename, -1);
-
+
in = fopen (certdb->filename, "r");
if (in == NULL)
return -1;
@@ -526,6 +526,8 @@ certdb_cert_free (CamelCertDB *certdb, CamelCert *cert)
g_free (cert->subject);
g_free (cert->hostname);
g_free (cert->fingerprint);
+ if (cert->rawcert)
+ g_byte_array_free(cert->rawcert, TRUE);
}
void
diff --git a/camel/camel-certdb.h b/camel/camel-certdb.h
index 3d592a23b4..60429edcd1 100644
--- a/camel/camel-certdb.h
+++ b/camel/camel-certdb.h
@@ -68,6 +68,7 @@ typedef struct {
char *fingerprint;
CamelCertTrust trust;
+ GByteArray *rawcert;
} CamelCert;
struct _CamelCertDB {
diff --git a/camel/camel-file-utils.c b/camel/camel-file-utils.c
index 0b909f3e82..33f599d463 100644
--- a/camel/camel-file-utils.c
+++ b/camel/camel-file-utils.c
@@ -248,7 +248,7 @@ camel_file_util_encode_string (FILE *out, const char *str)
register int len;
if (str == NULL)
- return camel_file_util_encode_uint32 (out, 0);
+ return camel_file_util_encode_uint32 (out, 1);
len = strlen (str);
if (camel_file_util_encode_uint32 (out, len+1) == -1)
diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c
index 09e5c94a02..c5e6ad9160 100644
--- a/camel/camel-tcp-stream-ssl.c
+++ b/camel/camel-tcp-stream-ssl.c
@@ -44,6 +44,8 @@
#include <prio.h>
#include <prerror.h>
#include <prerr.h>
+#include <secerr.h>
+#include <sslerr.h>
#include "nss.h" /* Don't use <> here or it will include the system nss.h instead */
#include <ssl.h>
#include <cert.h>
@@ -466,117 +468,369 @@ ssl_auth_cert (void *data, PRFileDesc *sockfd, PRBool checksig, PRBool is_server
}
#endif
+CamelCert *camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert);
+CamelCert *camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert);
+void camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert);
+
+static char *
+cert_fingerprint(CERTCertificate *cert)
+{
+ unsigned char md5sum[16], fingerprint[50], *f;
+ int i;
+ const char tohex[16] = "0123456789abcdef";
+
+ md5_get_digest (cert->derCert.data, cert->derCert.len, md5sum);
+ for (i=0,f = fingerprint; i<16; i++) {
+ unsigned int c = md5sum[i];
+
+ *f++ = tohex[(c >> 4) & 0xf];
+ *f++ = tohex[c & 0xf];
+ *f++ = ':';
+ }
+
+ fingerprint[47] = 0;
+
+ return g_strdup(fingerprint);
+}
+
+/* lookup a cert uses fingerprint to index an on-disk file */
+CamelCert *
+camel_certdb_nss_cert_get(CamelCertDB *certdb, CERTCertificate *cert)
+{
+ char *fingerprint, *path;
+ CamelCert *ccert;
+ int fd;
+ ssize_t len;
+ struct stat st;
+
+ fingerprint = cert_fingerprint(cert);
+ ccert = camel_certdb_get_cert(certdb, fingerprint);
+ if (ccert == NULL) {
+ g_free(fingerprint);
+ return ccert;
+ }
+
+ if (ccert->rawcert == NULL) {
+ path = g_strdup_printf("%s/.camel_certs/%s", getenv("HOME"), fingerprint);
+ if (stat(path, &st) == -1
+ || (fd = open(path, O_RDONLY)) == -1) {
+ g_warning("could not load cert %s: %s", path, strerror(errno));
+ g_free(fingerprint);
+ g_free(path);
+ camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
+ camel_certdb_touch(certdb);
+
+ return ccert;
+ }
+ g_free(path);
+
+ ccert->rawcert = g_byte_array_new();
+ g_byte_array_set_size(ccert->rawcert, st.st_size);
+ len = read(fd, ccert->rawcert->data, st.st_size);
+ close(fd);
+ if (len != st.st_size) {
+ g_warning("cert size read truncated %s: %d != %ld", path, len, st.st_size);
+ g_byte_array_free(ccert->rawcert, TRUE);
+ ccert->rawcert = NULL;
+ g_free(fingerprint);
+ camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
+ camel_certdb_touch(certdb);
+
+ return ccert;
+ }
+ }
+
+ g_free(fingerprint);
+ if (ccert->rawcert->len != cert->derCert.len
+ || memcmp(ccert->rawcert->data, cert->derCert.data, cert->derCert.len) != 0) {
+ g_warning("rawcert != derCer");
+ camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_UNKNOWN);
+ camel_certdb_touch(certdb);
+ }
+
+ return ccert;
+}
+
+/* add a cert to the certdb */
+CamelCert *
+camel_certdb_nss_cert_add(CamelCertDB *certdb, CERTCertificate *cert)
+{
+ CamelCert *ccert;
+ char *fingerprint;
+
+ fingerprint = cert_fingerprint(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));
+ /* hostname is set in caller */
+ /*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);
+ g_free(fingerprint);
+
+ camel_certdb_nss_cert_set(certdb, ccert, cert);
+
+ camel_certdb_add(certdb, ccert);
+
+ return ccert;
+}
+
+/* set the 'raw' cert (& save it) */
+void
+camel_certdb_nss_cert_set(CamelCertDB *certdb, CamelCert *ccert, CERTCertificate *cert)
+{
+ char *path, *fingerprint;
+ CamelStream *stream;
+
+ fingerprint = ccert->fingerprint;
+
+ if (ccert->rawcert == NULL)
+ ccert->rawcert = g_byte_array_new();
+ g_byte_array_set_size(ccert->rawcert, cert->derCert.len);
+ memcpy(ccert->rawcert->data, cert->derCert.data, cert->derCert.len);
+
+ path = g_strdup_printf("%s/.camel_certs/%s", getenv("HOME"), fingerprint);
+
+ stream = camel_stream_fs_new_with_name(path, O_WRONLY|O_CREAT|O_TRUNC, 0600);
+ if (stream != NULL) {
+ if (camel_stream_write(stream, ccert->rawcert->data, ccert->rawcert->len) != ccert->rawcert->len) {
+ g_warning("Could not save cert: %s: %s", path, strerror(errno));
+ unlink(path);
+ }
+ camel_stream_close(stream);
+ camel_object_unref(stream);
+ } else {
+ g_warning("Could not save cert: %s: %s", path, strerror(errno));
+ }
+
+ g_free(path);
+}
+
+
+#if 0
+/* used by the mozilla-like code below */
+static char *
+get_nickname(CERTCertificate *cert)
+{
+ char *server, *nick = NULL;
+ int i;
+ PRBool status = PR_TRUE;
+
+ server = CERT_GetCommonName(&cert->subject);
+ if (server == NULL)
+ return NULL;
+
+ for (i=1;status == PR_TRUE;i++) {
+ if (nick) {
+ g_free(nick);
+ nick = g_strdup_printf("%s #%d", server, i);
+ } else {
+ nick = g_strdup(server);
+ }
+ status = SEC_CertNicknameConflict(server, &cert->derSubject, cert->dbhandle);
+ }
+
+ return nick;
+}
+#endif
+
static SECStatus
ssl_bad_cert (void *data, PRFileDesc *sockfd)
{
- unsigned char md5sum[16], fingerprint[40], *f;
- gboolean accept, valid_cert;
+ gboolean accept;
CamelCertDB *certdb = NULL;
CamelCert *ccert = NULL;
- char *prompt, *cert_str;
+ char *prompt, *cert_str, *fingerprint;
CamelTcpStreamSSL *ssl;
CERTCertificate *cert;
CamelService *service;
- int i;
-
+ SECStatus status = SECFailure;
+
g_return_val_if_fail (data != NULL, SECFailure);
g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure);
-
+
ssl = CAMEL_TCP_STREAM_SSL (data);
service = ssl->priv->service;
cert = SSL_PeerCertificate (sockfd);
-
- /* calculate the MD5 hash of the raw certificate */
- md5_get_digest (cert->derCert.data, cert->derCert.len, md5sum);
- /*HASH_HashBuf (HASH_AlgMD5, md5sum, cert->derCert.data, cert->derCert.len);*/
- for (i = 0, f = fingerprint; i < 16; i++, f += 3)
- sprintf (f, "%.2x%c", md5sum[i], i != 15 ? ':' : '\0');
-
- valid_cert = CERT_VerifyCertNow (CERT_GetDefaultCertDB (), cert, TRUE, certUsageSSLClient, NULL);
- /*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);
+ if (cert == NULL)
+ return SECFailure;
+
+ certdb = camel_certdb_get_default();
+ ccert = camel_certdb_nss_cert_get(certdb, cert);
+ if (ccert == NULL) {
+ ccert = camel_certdb_nss_cert_add(certdb, cert);
+ camel_cert_set_hostname(certdb, ccert, ssl->priv->expected_host);
+ }
+
+ if (ccert->trust == CAMEL_CERT_TRUST_UNKNOWN) {
+ status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
+ fingerprint = cert_fingerprint(cert);
+ cert_str = g_strdup_printf (_("Issuer: %s\n"
+ "Subject: %s\n"
+ "Fingerprint: %s\n"
+ "Signature: %s"),
+ CERT_NameToAscii (&cert->issuer),
+ CERT_NameToAscii (&cert->subject),
+ fingerprint, status == SECSuccess?_("GOOD"):_("BAD"));
+ g_free(fingerprint);
+
+ /* construct our user prompt */
+ prompt = g_strdup_printf (_("SSL Certificate check for %s:\n\n%s\n\nDo you wish to accept?"),
+ service->url->host, cert_str);
+ g_free (cert_str);
+
+ /* query the user to find out if we want to accept this certificate */
+ accept = camel_session_alert_user (service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE);
+ g_free(prompt);
+ if (accept) {
+ camel_certdb_nss_cert_set(certdb, ccert, cert);
+ camel_cert_set_trust(certdb, ccert, CAMEL_CERT_TRUST_FULLY);
+ camel_certdb_touch(certdb);
}
+ } else {
+ accept = ccert->trust != CAMEL_CERT_TRUST_NEVER;
}
-
- cert_str = g_strdup_printf (_("Issuer: %s\n"
- "Subject: %s\n"
- "Fingerprint: %s\n"
- "Signature: %s"),
- CERT_NameToAscii (&cert->issuer),
- CERT_NameToAscii (&cert->subject),
- fingerprint, valid_cert ? _("GOOD") : _("BAD"));
-
- /* construct our user prompt */
- prompt = g_strdup_printf (_("SSL Certificate check for %s:\n\n%s\n\nDo you wish to accept?"),
- service->url->host, cert_str);
- g_free (cert_str);
-
- /* query the user to find out if we want to accept this certificate */
- accept = camel_session_alert_user (service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE);
- g_free (prompt);
-
- if (accept) {
+
+ camel_certdb_cert_unref(certdb, ccert);
+ camel_object_unref(certdb);
+
+ return accept ? SECSuccess : SECFailure;
+
#if 0
- /* this is how mutt does it but last time I tried to
- use CERT_AddTempCertToPerm() I got link errors and
- I have also been told by the nss devs that that
- function has been deprecated... */
- CERTCertTrust trust;
-
- CERT_DecodeTrustString (&trust, "P,,");
-
- CERT_AddTempCertToPerm (cert, NULL, &trust);
-#else
- SECItem *certs[1];
-
- if (!cert->trust)
- cert->trust = PORT_ZAlloc (sizeof (CERTCertTrust));
-
- cert->trust->sslFlags = CERTDB_VALID_PEER | CERTDB_TRUSTED;
-
- certs[0] = &cert->derCert;
+ int i, error;
+ CERTCertTrust trust;
+ SECItem *certs[1];
+ int go = 1;
+ char *host, *nick;
+
+ error = PR_GetError();
+
+ /* This code is basically what mozilla does - however it doesn't seem to work here
+ very reliably :-/ */
+ while (go && status != SECSuccess) {
+ char *prompt = NULL;
+
+ printf("looping, error '%d'\n", error);
+
+ switch(error) {
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_UNTRUSTED_ISSUER:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ /* add certificate */
+ printf("unknown issuer, adding ... \n");
+ prompt = g_strdup_printf(_("Certificate problem: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
+
+ if (camel_session_alert_user(service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
+
+ nick = get_nickname(cert);
+ if (NULL == nick) {
+ g_free(prompt);
+ status = SECFailure;
+ break;
+ }
+
+ printf("adding cert '%s'\n", nick);
+
+ if (!cert->trust) {
+ cert->trust = (CERTCertTrust*)PORT_ArenaZAlloc(cert->arena, sizeof(CERTCertTrust));
+ CERT_DecodeTrustString(cert->trust, "P");
+ }
- CERT_ImportCerts (CERT_GetDefaultCertDB (), certUsageSSLServer, 1, certs,
- NULL, TRUE, FALSE, cert->nickname);
+ certs[0] = &cert->derCert;
+ /*CERT_ImportCerts (cert->dbhandle, certUsageSSLServer, 1, certs, NULL, TRUE, FALSE, nick);*/
+ CERT_ImportCerts(cert->dbhandle, certUsageUserCertImport, 1, certs, NULL, TRUE, FALSE, nick);
+ g_free(nick);
+
+ printf(" cert type %08x\n", cert->nsCertType);
+
+ memset((void*)&trust, 0, sizeof(trust));
+ if (CERT_GetCertTrust(cert, &trust) != SECSuccess) {
+ CERT_DecodeTrustString(&trust, "P");
+ }
+ trust.sslFlags |= CERTDB_VALID_PEER | CERTDB_TRUSTED;
+ if (CERT_ChangeCertTrust(cert->dbhandle, cert, &trust) != SECSuccess) {
+ printf("couldn't change cert trust?\n");
+ }
+
+ /*status = SECSuccess;*/
+#if 1
+ /* re-verify? */
+ status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLServer, NULL);
+ error = PR_GetError();
+ printf("re-verify status %d, error %d\n", status, error);
#endif
-
- if (ccert) {
- camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_FULLY);
- camel_certdb_touch (certdb);
+
+ printf(" cert type %08x\n", cert->nsCertType);
+ } else {
+ printf("failed/cancelled\n");
+ go = 0;
+ }
+
+ break;
+ case SSL_ERROR_BAD_CERT_DOMAIN:
+ printf("bad domain\n");
+
+ prompt = g_strdup_printf(_("Bad certificate domain: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
+
+ if (camel_session_alert_user (service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
+ host = SSL_RevealURL(sockfd);
+ status = CERT_AddOKDomainName(cert, host);
+ printf("add ok domain name : %s\n", status == SECFailure?"fail":"ok");
+ error = PR_GetError();
+ if (status == SECFailure)
+ go = 0;
+ } else {
+ go = 0;
+ }
+
+ break;
+
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ printf("expired\n");
+
+ prompt = g_strdup_printf(_("Certificate expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
+
+ if (camel_session_alert_user(service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
+ cert->timeOK = PR_TRUE;
+ status = CERT_VerifyCertNow(cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL);
+ error = PR_GetError();
+ if (status == SECFailure)
+ go = 0;
+ } else {
+ go = 0;
+ }
+
+ break;
+
+ case SEC_ERROR_CRL_EXPIRED:
+ printf("crl expired\n");
+
+ prompt = g_strdup_printf(_("Certificate revocation list expired: %s\nIssuer: %s"), cert->subjectName, cert->issuerName);
+
+ if (camel_session_alert_user(service->session, CAMEL_SESSION_ALERT_WARNING, prompt, TRUE)) {
+ host = SSL_RevealURL(sockfd);
+ status = CERT_AddOKDomainName(cert, host);
+ }
+
+ go = 0;
+ break;
+
+ default:
+ printf("generic error\n");
+ go = 0;
+ break;
}
+
+ g_free(prompt);
}
-
- if (certdb) {
- camel_certdb_cert_unref (certdb, ccert);
- camel_object_unref (certdb);
- }
-
- return accept ? SECSuccess : SECFailure;
+
+ CERT_DestroyCertificate(cert);
+
+ return status;
+#endif
}
static PRFileDesc *