Add a per-account preference to allow automatically accepting unknown
authorColin Leroy <colin@colino.net>
Wed, 28 May 2014 19:18:34 +0000 (21:18 +0200)
committerColin Leroy <colin@colino.net>
Wed, 28 May 2014 19:18:34 +0000 (21:18 +0200)
and changed SSL certificates, if they're valid (that is, if the root CA
is trusted by the distro).

15 files changed:
src/common/session.c
src/common/session.h
src/common/socket.h
src/common/ssl.c
src/common/ssl_certificate.c
src/common/ssl_certificate.h
src/compose.c
src/etpan/etpan-ssl.c
src/etpan/etpan-ssl.h
src/etpan/imap-thread.c
src/etpan/nntp-thread.c
src/pop.c
src/prefs_account.c
src/prefs_account.h
src/send_message.c

index 25038c9..f1af55b 100644 (file)
@@ -165,6 +165,8 @@ static gint session_connect_cb(SockInfo *sock, gpointer data)
        session->sock = sock;
        sock->account = session->account;
        sock->is_smtp = session->is_smtp;
+       sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
+
 #ifdef USE_GNUTLS
        sock->gnutls_priority = session->gnutls_priority;
 
@@ -373,6 +375,8 @@ gint session_start_tls(Session *session)
 
        nb_mode = sock_is_nonblocking_mode(session->sock);
 
+       session->sock->ssl_cert_auto_accept = session->ssl_cert_auto_accept;
+
        if (nb_mode)
                sock_set_nonblocking_mode(session->sock, FALSE);
 
index 00675c4..98ae50e 100644 (file)
@@ -150,10 +150,10 @@ struct _Session
        gpointer recv_data_notify_data;
        gpointer send_data_progressive_notify_data;
        gpointer send_data_notify_data;
-       
+
        const void *account;
        gboolean is_smtp;
-
+       gboolean ssl_cert_auto_accept;
        gint ping_tag;
 
 #ifdef USE_GNUTLS
index 39c6e2e..59cb230 100644 (file)
@@ -76,9 +76,10 @@ struct _SockInfo
        SockFunc callback;
        GIOCondition condition;
        gchar *canonical_name;
-       
+
        const void *account;
        gboolean is_smtp;
+       gboolean ssl_cert_auto_accept;
 };
 
 void refresh_resolvers                 (void);
index b1812f9..1c7c335 100644 (file)
@@ -371,7 +371,8 @@ gboolean ssl_init_socket_with_method(SockInfo *sockinfo, SSLMethod method)
                return FALSE;
        }
 
-       if (!ssl_certificate_check_chain(certs, cert_list_length, sockinfo->hostname, sockinfo->port)) {
+       if (!ssl_certificate_check_chain(certs, cert_list_length, sockinfo->hostname, sockinfo->port,
+                                        sockinfo->ssl_cert_auto_accept)) {
                for (i = 0; i < cert_list_length; i++)
                        gnutls_x509_crt_deinit(certs[i]);
                g_free(certs);
index b48d4d4..870ceb0 100644 (file)
@@ -603,6 +603,17 @@ static guint check_cert(SSLCertificate *cert)
 
 }
 
+static gboolean ssl_certificate_is_valid(SSLCertificate *cert, guint status)
+{
+       gchar *str_status = ssl_certificate_check_signer(cert, status);
+
+       if (str_status != NULL) {
+               g_free(str_status);
+               return FALSE;
+       }
+       return ssl_certificate_check_subject_cn(cert);
+}
+
 char *ssl_certificate_check_signer (SSLCertificate *cert, guint status) 
 {
        gnutls_x509_crt_t x509_cert = cert ? cert->x509_cert : NULL;
@@ -667,17 +678,20 @@ static void ssl_certificate_save_chain(gnutls_x509_crt_t *certs, gint len, const
                fclose(fp);
 }
 
-gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const gchar *host, gushort port)
+gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, 
+                               const gchar *host, gushort port,
+                               gboolean accept_if_valid)
 {
        SSLCertificate *current_cert = NULL;
        SSLCertificate *known_cert;
        SSLCertHookData cert_hook_data;
        gchar *fingerprint;
        size_t n;
-       unsigned char md[128];  
+       unsigned char md[128];
+       gboolean valid = FALSE;
 
        current_cert = ssl_certificate_new(x509_cert, host, port);
-       
+
        if (current_cert == NULL) {
                debug_print("Buggy certificate !\n");
                return FALSE;
@@ -693,14 +707,25 @@ gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const
 
        g_free(fingerprint);
 
+       if (accept_if_valid)
+               valid = ssl_certificate_is_valid(current_cert, status);
+       else
+               valid = FALSE; /* Force check */
+
        if (known_cert == NULL) {
+               if (valid) {
+                       ssl_certificate_save(current_cert);
+                       ssl_certificate_destroy(current_cert);
+                       return TRUE;
+               }
+
                cert_hook_data.cert = current_cert;
                cert_hook_data.old_cert = NULL;
                cert_hook_data.expired = FALSE;
                cert_hook_data.accept = FALSE;
-               
+
                hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
-               
+
                if (!cert_hook_data.accept) {
                        ssl_certificate_destroy(current_cert);
                        return FALSE;
@@ -710,11 +735,18 @@ gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const
                        return TRUE;
                }
        } else if (!ssl_certificate_compare (current_cert, known_cert)) {
+               if (valid) {
+                       ssl_certificate_save(current_cert);
+                       ssl_certificate_destroy(current_cert);
+                       ssl_certificate_destroy(known_cert);
+                       return TRUE;
+               }
+
                cert_hook_data.cert = current_cert;
                cert_hook_data.old_cert = known_cert;
                cert_hook_data.expired = FALSE;
                cert_hook_data.accept = FALSE;
-               
+
                hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
 
                if (!cert_hook_data.accept) {
@@ -729,22 +761,22 @@ gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const
                }
        } else if (gnutls_x509_crt_get_expiration_time(current_cert->x509_cert) < time(NULL)) {
                gchar *tmp = g_strdup_printf("%s:%d", current_cert->host, current_cert->port);
-               
+
                if (warned_expired == NULL)
                        warned_expired = g_hash_table_new(g_str_hash, g_str_equal);
-               
+
                if (g_hash_table_lookup(warned_expired, tmp)) {
                        g_free(tmp);
                        ssl_certificate_destroy(current_cert);
                        ssl_certificate_destroy(known_cert);
                        return TRUE;
                }
-                       
+
                cert_hook_data.cert = current_cert;
                cert_hook_data.old_cert = NULL;
                cert_hook_data.expired = TRUE;
                cert_hook_data.accept = FALSE;
-               
+
                hooks_invoke(SSLCERT_ASK_HOOKLIST, &cert_hook_data);
 
                if (!cert_hook_data.accept) {
@@ -765,7 +797,9 @@ gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const
        return TRUE;
 }
 
-gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len, const gchar *host, gushort port)
+gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len,
+                                    const gchar *host, gushort port,
+                                    gboolean accept_if_valid)
 {
        int ncas = 0;
        gnutls_x509_crt_t *cas = NULL;
@@ -798,7 +832,8 @@ gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len, c
                              GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
                              &status);
 
-       result = ssl_certificate_check(certs[0], status, host, port);
+       result = ssl_certificate_check(certs[0], status, host, port,
+                                       accept_if_valid);
 
        if (result == TRUE) {
                ssl_certificate_save_chain(certs, chain_len, host, port);
index 9f4ecf0..7089472 100644 (file)
@@ -57,8 +57,8 @@ struct _SSLCertHookData
 };
 
 SSLCertificate *ssl_certificate_find (const gchar *host, gushort port, const gchar *fingerprint);
-gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const gchar *host, gushort port);
-gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len, const gchar *host, gushort port);
+gboolean ssl_certificate_check (gnutls_x509_crt_t x509_cert, guint status, const gchar *host, gushort port, gboolean accept_if_valid);
+gboolean ssl_certificate_check_chain(gnutls_x509_crt_t *certs, gint chain_len, const gchar *host, gushort port, gboolean accept_if_valid);
 void ssl_certificate_destroy(SSLCertificate *cert);
 void ssl_certificate_delete_from_disk(SSLCertificate *cert);
 char * readable_fingerprint(unsigned char *src, int len);
index a13e4ea..7833804 100644 (file)
@@ -11565,7 +11565,7 @@ gboolean compose_close(Compose *compose)
                }
                return TRUE;
        }
-       
+
        if (compose->draft_timeout_tag >= 0) {
                g_source_remove(compose->draft_timeout_tag);
                compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;
index f99955b..529bc30 100644 (file)
@@ -40,7 +40,8 @@
 #include "log.h"
 #include "prefs_account.h"
 
-gboolean etpan_certificate_check(mailstream *stream, const char *host, gint port)
+gboolean etpan_certificate_check(mailstream *stream, const char *host, gint port,
+                                gboolean accept_if_valid)
 {
 #if (!defined LIBETPAN_API_CURRENT || LIBETPAN_API_CURRENT < 18)
        unsigned char *cert_der = NULL;
@@ -69,7 +70,7 @@ gboolean etpan_certificate_check(mailstream *stream, const char *host, gint port
                free(tmp.data);
                g_warning("IMAP: can't get cert\n");
                return FALSE;
-       } else if (ssl_certificate_check(cert, (guint)-1, host, port) == TRUE) {
+       } else if (ssl_certificate_check(cert, (guint)-1, host, port, accept_if_valid) == TRUE) {
                free(tmp.data);
                gnutls_x509_crt_deinit(cert);
                return TRUE;
@@ -121,7 +122,8 @@ gboolean etpan_certificate_check(mailstream *stream, const char *host, gint port
        carray_free(certs_der);
 
        if (result == TRUE)
-               result = ssl_certificate_check_chain(certs, chain_len, host, port);
+               result = ssl_certificate_check_chain(certs, chain_len, host, port,
+                                                    accept_if_valid);
 
        for (i = 0; i < chain_len; i++)
                gnutls_x509_crt_deinit(certs[i]);
index 5607d1a..adb8f9d 100644 (file)
@@ -31,7 +31,7 @@
 
 #include <libetpan/libetpan.h>
 
-gboolean etpan_certificate_check(mailstream *imap_stream, const char *host, gint port);
+gboolean etpan_certificate_check(mailstream *imap_stream, const char *host, gint port, gboolean accept_if_valid);
 void etpan_connect_ssl_context_cb(struct mailstream_ssl_context * ssl_context, void * data);
 
 #endif /* USE_GNUTLS */
index 179f352..be7dca5 100644 (file)
@@ -544,11 +544,12 @@ int imap_threaded_connect_ssl(Folder * folder, const char * server, int port)
        chashdatum key;
        chashdatum value;
        mailimap * imap, * oldimap;
-       
+       gboolean accept_if_valid = FALSE;
+
        oldimap = get_imap(folder);
 
        imap = mailimap_new(0, NULL);
-       
+
        if (oldimap) {
                debug_print("deleting old imap %p\n", oldimap);
                delete_imap(folder, oldimap);
@@ -559,22 +560,26 @@ int imap_threaded_connect_ssl(Folder * folder, const char * server, int port)
        value.data = imap;
        value.len = 0;
        chash_set(session_hash, &key, &value, NULL);
-       
+
        param.imap = imap;
        param.server = server;
        param.port = port;
        param.account = folder->account;
 
+       if (folder->account)
+               accept_if_valid = folder->account->ssl_certs_auto_accept;
+
        refresh_resolvers();
        threaded_run(folder, &param, &result, connect_ssl_run);
 
        if ((result.error == MAILIMAP_NO_ERROR_AUTHENTICATED ||
             result.error == MAILIMAP_NO_ERROR_NON_AUTHENTICATED) && !etpan_skip_ssl_cert_check) {
-               if (etpan_certificate_check(imap->imap_stream, server, port) != TRUE)
+               if (etpan_certificate_check(imap->imap_stream, server, port,
+                                           accept_if_valid) != TRUE)
                        result.error = MAILIMAP_ERROR_SSL;
        }
        debug_print("connect %d with imap %p\n", result.error, imap);
-       
+
        return result.error;
 }
 
@@ -1094,20 +1099,25 @@ int imap_threaded_starttls(Folder * folder, const gchar *host, int port)
 {
        struct connect_param param;
        struct starttls_result result;
-       
+       gboolean accept_if_valid = FALSE;
+
        debug_print("imap starttls - begin\n");
-       
+
        param.imap = get_imap(folder);
        param.server = host;
        param.port = port;
        param.account = folder->account;
 
+       if (folder->account)
+               accept_if_valid = folder->account->ssl_certs_auto_accept;
+
        threaded_run(folder, &param, &result, starttls_run);
-       
+
        debug_print("imap starttls - end\n");
 
        if (result.error == 0 && param.imap && !etpan_skip_ssl_cert_check) {
-               if (etpan_certificate_check(param.imap->imap_stream, host, port) != TRUE)
+               if (etpan_certificate_check(param.imap->imap_stream, host, port,
+                                           accept_if_valid) != TRUE)
                        return MAILIMAP_ERROR_SSL;
        }       
        return result.error;
index 7708d31..b721e61 100644 (file)
@@ -398,11 +398,12 @@ int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port)
        chashdatum key;
        chashdatum value;
        newsnntp * nntp, * oldnntp;
-       
+       gboolean accept_if_valid = FALSE;
+
        oldnntp = get_nntp(folder);
 
        nntp = newsnntp_new(0, NULL);
-       
+
        if (oldnntp) {
                debug_print("deleting old nntp %p\n", oldnntp);
                delete_nntp(folder, oldnntp);
@@ -413,17 +414,21 @@ int nntp_threaded_connect_ssl(Folder * folder, const char * server, int port)
        value.data = nntp;
        value.len = 0;
        chash_set(session_hash, &key, &value, NULL);
-       
+
        param.nntp = nntp;
        param.server = server;
        param.port = port;
        param.account = folder->account;
 
+       if (folder->account)
+               accept_if_valid = folder->account->ssl_certs_auto_accept;
+
        refresh_resolvers();
        threaded_run(folder, &param, &result, connect_ssl_run);
 
        if (result.error == NEWSNNTP_NO_ERROR && !etpan_skip_ssl_cert_check) {
-               if (etpan_certificate_check(nntp->nntp_stream, server, port) != TRUE)
+               if (etpan_certificate_check(nntp->nntp_stream, server, port,
+                                           accept_if_valid) != TRUE)
                        return -1;
        }
        debug_print("connect %d with nntp %p\n", result.error, nntp);
index a9a0472..2f7eed7 100644 (file)
--- a/src/pop.c
+++ b/src/pop.c
@@ -530,7 +530,7 @@ Session *pop3_session_new(PrefsAccount *account)
        SESSION(session)->recv_msg = pop3_session_recv_msg;
        SESSION(session)->recv_data_finished = pop3_session_recv_data_finished;
        SESSION(session)->send_data_finished = NULL;
-
+       SESSION(session)->ssl_cert_auto_accept = account->ssl_certs_auto_accept;
        SESSION(session)->destroy = pop3_session_destroy;
 
        session->state = POP3_READY;
index 9163800..a03f500 100644 (file)
@@ -269,6 +269,7 @@ typedef struct SSLPage
        GtkWidget *entry_out_cert_file;
        GtkWidget *entry_out_cert_pass;
 
+       GtkWidget *ssl_certs_auto_accept_checkbtn;
        GtkWidget *use_nonblocking_ssl_checkbtn;
 } SSLPage;
 
@@ -729,6 +730,10 @@ static PrefParam ssl_param[] = {
         prefs_account_enum_set_data_from_radiobtn,
         prefs_account_enum_set_radiobtn},
 
+       {"ssl_certs_auto_accept", "0", &tmp_ac_prefs.ssl_certs_auto_accept, P_BOOL,
+        &ssl_page.ssl_certs_auto_accept_checkbtn,
+        prefs_set_data_from_toggle, prefs_set_toggle},
+
        {"use_nonblocking_ssl", "1", &tmp_ac_prefs.use_nonblocking_ssl, P_BOOL,
         &ssl_page.use_nonblocking_ssl_checkbtn,
         prefs_set_data_from_toggle, prefs_set_toggle},
@@ -769,6 +774,9 @@ static PrefParam ssl_param[] = {
        {"out_ssl_client_cert_pass", "", &tmp_ac_prefs.out_ssl_client_cert_pass, P_PASSWORD,
         NULL, NULL, NULL},
 
+       {"ssl_certs_auto_accept", "0", &tmp_ac_prefs.ssl_certs_auto_accept, P_BOOL,
+        NULL, NULL, NULL},
+
        {"use_nonblocking_ssl", "1", &tmp_ac_prefs.use_nonblocking_ssl, P_BOOL,
         NULL, NULL, NULL},
 #endif /* USE_GNUTLS */
@@ -2413,6 +2421,7 @@ static void ssl_create_widget_func(PrefsPage * _page,
        GtkWidget *entry_out_cert_pass;
 
        GtkWidget *vbox7;
+       GtkWidget *ssl_certs_auto_accept_checkbtn;
        GtkWidget *use_nonblocking_ssl_checkbtn;
        GtkWidget *hbox;
        GtkWidget *hbox_spc;
@@ -2545,6 +2554,9 @@ static void ssl_create_widget_func(PrefsPage * _page,
        gtk_widget_show (vbox7);
        gtk_box_pack_start (GTK_BOX (vbox1), vbox7, FALSE, FALSE, 0);
 
+       PACK_CHECK_BUTTON(vbox7, ssl_certs_auto_accept_checkbtn,
+                         _("Automatically accept unknown valid SSL certificates"));
+
        PACK_CHECK_BUTTON(vbox7, use_nonblocking_ssl_checkbtn,
                          _("Use non-blocking SSL"));
 
@@ -2587,6 +2599,7 @@ static void ssl_create_widget_func(PrefsPage * _page,
        page->entry_out_cert_file     = entry_out_cert_file;
        page->entry_out_cert_pass     = entry_out_cert_pass;
 
+       page->ssl_certs_auto_accept_checkbtn = ssl_certs_auto_accept_checkbtn;
        page->use_nonblocking_ssl_checkbtn = use_nonblocking_ssl_checkbtn;
 
        tmp_ac_prefs = *ac_prefs;
index 40b9301..dd118c4 100644 (file)
@@ -86,6 +86,7 @@ struct _PrefsAccount
        gchar *in_ssl_client_cert_file;
        gchar *in_ssl_client_cert_pass;
 
+       gboolean ssl_certs_auto_accept;
        gboolean use_nonblocking_ssl;
 
        /* Receive */
index d51994d..80c0fb7 100644 (file)
@@ -250,6 +250,8 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g
        if (!ac_prefs->session) {
                /* we can't reuse a previously initialised session */
                session = smtp_session_new(ac_prefs);
+               session->ssl_cert_auto_accept = ac_prefs->ssl_certs_auto_accept;
+
                smtp_session = SMTP_SESSION(session);
 
                if (ac_prefs->set_domain && ac_prefs->domain && strlen(ac_prefs->domain)) {