Introduce CamelSession::trust_prompt()
authorMilan Crha <mcrha@redhat.com>
Fri, 14 Dec 2012 14:25:55 +0000 (15:25 +0100)
committerMilan Crha <mcrha@redhat.com>
Fri, 14 Dec 2012 14:25:55 +0000 (15:25 +0100)
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
camel/camel-session.c
camel/camel-session.h
camel/camel-tcp-stream-ssl.c
camel/providers/imap/camel-imap-command.c
camel/providers/imap/camel-imap-journal.c
configure.ac

index 47202ef..2be544c 100644 (file)
@@ -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);
                }
index 94fce5b..302f14e 100644 (file)
@@ -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);
 }
 
 /**
index 93b5150..0c7a338 100644 (file)
@@ -36,6 +36,7 @@
 #include <camel/camel-msgport.h>
 #include <camel/camel-provider.h>
 #include <camel/camel-service.h>
+#include <camel/camel-certdb.h>
 
 /* 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,
index d1f1bb7..cd3cbc7 100644 (file)
@@ -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 *
index 4584413..bbe1e69 100644 (file)
@@ -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);
                        }
                }
index e743253..6491c80 100644 (file)
@@ -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);
                }
        }
index 31995d4..212736c 100644 (file)
@@ -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