From f58f4faca264ae98a3ff1d97df09b14c828cfcf1 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Fri, 14 Dec 2012 15:25:55 +0100 Subject: [PATCH] Introduce CamelSession::trust_prompt() This way a trust-prompt dialog from EUserPrompter service can be used with all its features and a view consistency between components. --- camel/camel-disco-diary.c | 4 +- camel/camel-session.c | 45 +++++- camel/camel-session.h | 19 ++- camel/camel-tcp-stream-ssl.c | 256 ++++++------------------------ camel/providers/imap/camel-imap-command.c | 2 +- camel/providers/imap/camel-imap-journal.c | 2 +- configure.ac | 2 +- 7 files changed, 115 insertions(+), 215 deletions(-) diff --git a/camel/camel-disco-diary.c b/camel/camel-disco-diary.c index 47202ef..2be544c 100644 --- a/camel/camel-disco-diary.c +++ b/camel/camel-disco-diary.c @@ -210,7 +210,7 @@ camel_disco_diary_log (CamelDiscoDiary *diary, "reconnect to the network."), g_strerror (errno)); camel_session_alert_user ( - session, CAMEL_SESSION_ALERT_ERROR, msg, NULL); + session, CAMEL_SESSION_ALERT_ERROR, msg, NULL, NULL); g_free (msg); fclose (diary->file); @@ -276,7 +276,7 @@ diary_decode_folder (CamelDiscoDiary *diary, camel_session_alert_user ( camel_service_get_session (CAMEL_SERVICE (diary->store)), CAMEL_SESSION_ALERT_WARNING, - msg, NULL); + msg, NULL, cancellable); g_free (msg); g_free (name); } diff --git a/camel/camel-session.c b/camel/camel-session.c index 94fce5b..302f14e 100644 --- a/camel/camel-session.c +++ b/camel/camel-session.c @@ -1278,6 +1278,7 @@ camel_session_forget_password (CamelSession *session, * @type: the type of alert (info, warning, or error) * @prompt: the message for the user * @button_captions: List of button captions to use. If NULL, only "Dismiss" button is shown. + * @cancellable: (allow-non): optional #GCancellable object, or %NULL * * Presents the given @prompt to the user, in the style indicated by * @type. If @cancel is %TRUE, the user will be able to accept or @@ -1289,7 +1290,8 @@ gint camel_session_alert_user (CamelSession *session, CamelSessionAlertType type, const gchar *prompt, - GSList *button_captions) + GSList *button_captions, + GCancellable *cancellable) { CamelSessionClass *class; @@ -1299,7 +1301,46 @@ camel_session_alert_user (CamelSession *session, class = CAMEL_SESSION_GET_CLASS (session); g_return_val_if_fail (class->alert_user != NULL, -1); - return class->alert_user (session, type, prompt, button_captions); + return class->alert_user (session, type, prompt, button_captions, cancellable); +} + +/** + * camel_session_trust_prompt: + * @session: a #CamelSession + * @host: host name, to which the @certificate belongs + * @certificate: base64-encoded DER certificate on which to ask + * @certificate_errors: errors found with the certificate; a bit-OR of a #GTlsCertificateFlags + * @issuers: (allow-none): chain of issuers, or %NULL + * @cancellable: (allow-non): optional #GCancellable object, or %NULL + * + * Prompts user about trust of a certificate. The @certificate is not + * considered trusted, due to reasons set in @certificate_errors. + * There can be passed a list of @issuers, which has as items also base64-encoded + * DER certificates. The first item in the list is an issuer of the @certificate, + * the second item is an issuer of the first item, and so on. + * + * Returns: What trust level should be used for this certificate. It returns + * #CAMEL_CERT_TRUST_UNKNOWN on error or if user cancelled the dialog prompt. + * + * Since: 3.8 + **/ +CamelCertTrust +camel_session_trust_prompt (CamelSession *session, + const gchar *host, + const gchar *certificate, + guint32 certificate_errors, + const GSList *issuers, + GCancellable *cancellable) +{ + CamelSessionClass *class; + + g_return_val_if_fail (CAMEL_IS_SESSION (session), CAMEL_CERT_TRUST_UNKNOWN); + g_return_val_if_fail (certificate != NULL, CAMEL_CERT_TRUST_UNKNOWN); + + class = CAMEL_SESSION_GET_CLASS (session); + g_return_val_if_fail (class->trust_prompt != NULL, CAMEL_CERT_TRUST_UNKNOWN); + + return class->trust_prompt (session, host, certificate, certificate_errors, issuers, cancellable); } /** diff --git a/camel/camel-session.h b/camel/camel-session.h index 93b5150..0c7a338 100644 --- a/camel/camel-session.h +++ b/camel/camel-session.h @@ -36,6 +36,7 @@ #include #include #include +#include /* Standard GObject macros */ #define CAMEL_TYPE_SESSION \ @@ -115,7 +116,14 @@ struct _CamelSessionClass { gint (*alert_user) (CamelSession *session, CamelSessionAlertType type, const gchar *prompt, - GSList *button_captions); + GSList *button_captions, + GCancellable *cancellable); + CamelCertTrust (*trust_prompt) (CamelSession *session, + const gchar *host, + const gchar *certificate, + guint32 certificate_errors, + const GSList *issuers, + GCancellable *cancellable); CamelFilterDriver * (*get_filter_driver) (CamelSession *session, const gchar *type, @@ -208,7 +216,14 @@ gboolean camel_session_forget_password (CamelSession *session, gint camel_session_alert_user (CamelSession *session, CamelSessionAlertType type, const gchar *prompt, - GSList *button_captions); + GSList *button_captions, + GCancellable *cancellable); +CamelCertTrust camel_session_trust_prompt (CamelSession *session, + const gchar *host, + const gchar *certificate, + guint32 certificate_errors, + const GSList *issuers, + GCancellable *cancellable); gchar * camel_session_build_password_prompt (const gchar *type, const gchar *user, diff --git a/camel/camel-tcp-stream-ssl.c b/camel/camel-tcp-stream-ssl.c index d1f1bb7..cd3cbc7 100644 --- a/camel/camel-tcp-stream-ssl.c +++ b/camel/camel-tcp-stream-ssl.c @@ -425,33 +425,6 @@ camel_certdb_nss_cert_set (CamelCertDB *certdb, g_free (filename); } -#if 0 -/* used by the mozilla-like code below */ -static gchar * -get_nickname (CERTCertificate *cert) -{ - gchar *server, *nick = NULL; - gint 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 void tcp_stream_cancelled (GCancellable *cancellable, PRThread *thread) @@ -467,10 +440,8 @@ ssl_bad_cert (gpointer data, CamelCertDB *certdb = NULL; CamelCert *ccert = NULL; gboolean ccert_is_new = FALSE; - gchar *prompt, *cert_str, *fingerprint; CamelTcpStreamSSL *ssl; CERTCertificate *cert; - SECStatus status = SECFailure; g_return_val_if_fail (data != NULL, SECFailure); g_return_val_if_fail (CAMEL_IS_TCP_STREAM_SSL (data), SECFailure); @@ -493,59 +464,66 @@ ssl_bad_cert (gpointer data, } if (ccert->trust == CAMEL_CERT_TRUST_UNKNOWN) { - GSList *button_captions = NULL; - gint button_id; - - 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); + CERTCertificate *issuer; + CamelCertTrust trust_response; + gchar *base64; + guint32 certificate_errors = 0; + GSList *issuers = NULL; + + if (CERT_VerifyCertNow (cert->dbhandle, cert, TRUE, certUsageSSLClient, NULL) != SECSuccess) { + gint pr_error; + + pr_error = PR_GetError (); + + switch (pr_error) { + case SEC_ERROR_UNKNOWN_ISSUER: + certificate_errors |= G_TLS_CERTIFICATE_UNKNOWN_CA; + break; + case SSL_ERROR_BAD_CERT_DOMAIN: + certificate_errors |= G_TLS_CERTIFICATE_BAD_IDENTITY; + break; + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + certificate_errors |= G_TLS_CERTIFICATE_EXPIRED; + break; + } + } - /* construct our user prompt */ - prompt = g_strdup_printf ( - _("SSL Certificate for '%s' is not trusted. " - "Do you wish to accept it?\n\n" - "Detailed information about the certificate:\n%s"), - ssl->priv->expected_host, cert_str); - g_free (cert_str); + issuer = cert; + while (issuer) { + if (SECITEM_CompareItem (&issuer->derIssuer, &issuer->derSubject) == SECEqual) + break; - button_captions = g_slist_append (button_captions, _("_Reject")); - button_captions = g_slist_append (button_captions, _("Accept _Temporarily")); - button_captions = g_slist_append (button_captions, _("_Accept Permanently")); + issuer = CERT_FindCertIssuer (issuer, PR_Now (), certUsageSSLClient); + if (!issuer) + break; + + base64 = g_base64_encode (issuer->derIssuer.data, issuer->derIssuer.len); + if (!base64) + break; + + issuers = g_slist_append (issuers, base64); + } + + base64 = g_base64_encode (cert->derCert.data, cert->derCert.len); /* query the user to find out if we want to accept this certificate */ - button_id = camel_session_alert_user (ssl->priv->session, CAMEL_SESSION_ALERT_WARNING, prompt, button_captions); - g_slist_free (button_captions); - g_free (prompt); + trust_response = camel_session_trust_prompt (ssl->priv->session, ssl->priv->expected_host, base64, certificate_errors, issuers, NULL); + + g_free (base64); + g_slist_free_full (issuers, g_free); + + accept = trust_response != CAMEL_CERT_TRUST_UNKNOWN && + trust_response != CAMEL_CERT_TRUST_NEVER; - accept = button_id != 0; if (ccert_is_new) { camel_certdb_nss_cert_set (certdb, ccert, cert); camel_certdb_put (certdb, ccert); } - switch (button_id) { - case 0: /* Reject */ - camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_NEVER); - break; - case 1: /* Accept temporarily */ - camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_TEMPORARY); - break; - case 2: /* Accept permanently */ - camel_cert_set_trust (certdb, ccert, CAMEL_CERT_TRUST_FULLY); - break; - default: /* anything else means failure and will ask again */ - accept = FALSE; - break; - } + if (trust_response != CAMEL_CERT_TRUST_UNKNOWN) + camel_cert_set_trust (certdb, ccert, trust_response); + camel_certdb_touch (certdb); } else { accept = ccert->trust != CAMEL_CERT_TRUST_NEVER; @@ -556,140 +534,6 @@ ssl_bad_cert (gpointer data, g_object_unref (certdb); return accept ? SECSuccess : SECFailure; - -#if 0 - gint i, error; - CERTCertTrust trust; - SECItem *certs[1]; - gint go = 1; - gchar *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) { - gchar *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 (ssl->priv->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"); - } - - 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 ((gpointer) &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 - - 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 (ssl->priv->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 (ssl->priv->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 (ssl->priv->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); - } - - CERT_DestroyCertificate (cert); - - return status; -#endif } static PRFileDesc * diff --git a/camel/providers/imap/camel-imap-command.c b/camel/providers/imap/camel-imap-command.c index 4584413..bbe1e69 100644 --- a/camel/providers/imap/camel-imap-command.c +++ b/camel/providers/imap/camel-imap-command.c @@ -430,7 +430,7 @@ camel_imap_command_response (CamelImapStore *store, user, host, alert); camel_session_alert_user ( session, CAMEL_SESSION_ALERT_WARNING, - msg, NULL); + msg, NULL, cancellable); g_free (msg); } } diff --git a/camel/providers/imap/camel-imap-journal.c b/camel/providers/imap/camel-imap-journal.c index e743253..6491c80 100644 --- a/camel/providers/imap/camel-imap-journal.c +++ b/camel/providers/imap/camel-imap-journal.c @@ -318,7 +318,7 @@ journal_decode_folder (CamelIMAPJournal *journal, camel_service_get_session ( CAMEL_SERVICE (parent_store)), CAMEL_SESSION_ALERT_WARNING, - msg, NULL); + msg, NULL, cancellable); g_free (msg); } } diff --git a/configure.ac b/configure.ac index 31995d4..212736c 100644 --- a/configure.ac +++ b/configure.ac @@ -125,7 +125,7 @@ LIBEBOOK_CURRENT=17 LIBEBOOK_REVISION=1 LIBEBOOK_AGE=3 -LIBCAMEL_CURRENT=42 +LIBCAMEL_CURRENT=43 LIBCAMEL_REVISION=0 LIBCAMEL_AGE=0 -- 2.7.4