[evolution-ews] Bug #704869 - Support Kerberos authentication



commit 56c1b7b904789453c07baefa21636f617fca0886
Author: Milan Crha <mcrha redhat com>
Date:   Mon Sep 30 13:28:13 2013 +0200

    Bug #704869 - Support Kerberos authentication

 src/camel/camel-ews-provider.c |   17 +++-
 src/server/e-ews-connection.c  |  176 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 187 insertions(+), 6 deletions(-)
---
diff --git a/src/camel/camel-ews-provider.c b/src/camel/camel-ews-provider.c
index 719b618..47085a8 100644
--- a/src/camel/camel-ews-provider.c
+++ b/src/camel/camel-ews-provider.c
@@ -108,14 +108,25 @@ CamelServiceAuthType camel_ews_basic_authtype = {
        TRUE
 };
 
+CamelServiceAuthType camel_ews_gssapi_authtype = {
+       N_("Kerberos"),
+
+       N_("This option will connect to the Exchange server using a "
+          "Kerberos/GSSAPI authentication."),
+
+       "GSSAPI",
+       FALSE
+};
+
 void
 camel_provider_module_init (void)
 {
        ews_provider.url_hash = ews_url_hash;
        ews_provider.url_equal = ews_url_equal;
-       ews_provider.authtypes = g_list_prepend (
-               g_list_prepend (NULL, &camel_ews_basic_authtype),
-               &camel_ews_ntlm_authtype);
+       ews_provider.authtypes = g_list_append (g_list_append (g_list_append (NULL,
+               &camel_ews_ntlm_authtype),
+               &camel_ews_basic_authtype),
+               &camel_ews_gssapi_authtype);
        ews_provider.translation_domain = GETTEXT_PACKAGE;
 
        ews_provider.object_types[CAMEL_PROVIDER_STORE] =  CAMEL_TYPE_EWS_STORE;
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 4e2e71a..b1cd4a9 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -174,9 +174,10 @@ ews_auth_mech_to_use_ntlm (GBinding *binding,
        const gchar *auth_mechanism;
        gboolean use_ntlm;
 
-       /* Use NTLM unless the auth mechanism is "PLAIN". */
+       /* Use NTLM unless the auth mechanism is "PLAIN" or "GSSAPI". */
        auth_mechanism = g_value_get_string (source_value);
-       use_ntlm = (g_strcmp0 (auth_mechanism, "PLAIN") != 0);
+       use_ntlm = g_strcmp0 (auth_mechanism, "PLAIN") != 0 &&
+                  g_strcmp0 (auth_mechanism, "GSSAPI") != 0;
        g_value_set_boolean (target_value, use_ntlm);
 
        return TRUE;
@@ -271,6 +272,164 @@ comp_func (gconstpointer a,
                return 0;
 }
 
+static gchar *
+ews_connection_gssapi_challenge (CamelSasl *sasl,
+                                const gchar *what,
+                                gboolean is_base64,
+                                GError **error)
+{
+       GByteArray *ain, *aout = NULL;
+       gchar *response = NULL;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (sasl != NULL, NULL);
+
+       ain = g_byte_array_new ();
+
+       if (what && *what) {
+               if (is_base64) {
+                       guchar *bytes;
+                       gsize len = 0;
+
+                       bytes = g_base64_decode (what, &len);
+                       if (bytes) {
+                               g_byte_array_append (ain, bytes, len);
+                               g_free (bytes);
+                       }
+               } else {
+                       g_byte_array_append (ain, (const guchar *) what, strlen (what));
+               }
+       }
+
+       aout = camel_sasl_challenge_sync (sasl, ain, NULL, &local_error);
+
+       if (local_error) {
+               g_propagate_error (error, local_error);
+       } else if (aout && aout->len) {
+               response = g_base64_encode (aout->data, aout->len);
+       } else {
+               response = g_strdup ("");
+       }
+
+       g_byte_array_unref (ain);
+
+       if (aout)
+               g_byte_array_unref (aout);
+
+       return response;
+}
+
+#define EWS_GSSAPI_SOUP_SESSION "ews-gssapi-soup-session"
+#define EWS_GSSAPI_SASL                "ews-gssapi-sasl"
+#define EWS_GSSAPI_CONNECTION  "ews-gssapi-connection"
+
+static void
+ews_connection_authenticate_gssapi_cb (SoupMessage *message,
+                                      gpointer user_data)
+{
+       EEwsConnection *connection = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_CONNECTION);
+       SoupSession *session = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION);
+       CamelSasl *sasl = g_object_get_data (G_OBJECT (message), EWS_GSSAPI_SASL);
+       const gchar *auths_lst;
+       gchar **auths;
+       gint ii;
+
+       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
+       g_return_if_fail (SOUP_IS_SESSION (session));
+       g_return_if_fail (CAMEL_IS_SASL (sasl));
+
+       if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code))
+               return;
+
+       auths_lst = soup_message_headers_get_list (message->response_headers, "WWW-Authenticate");
+       if (!auths_lst)
+               return;
+
+       auths = g_strsplit (auths_lst, ",", -1);
+       for (ii = 0; auths && auths[ii]; ii++) {
+               if (g_ascii_strncasecmp (auths[ii], "Negotiate", 9) == 0) {
+                       GError *error = NULL;
+                       const gchar *chlg = auths[ii] + 9;
+                       gchar *response;
+
+                       if (*chlg)
+                               chlg++;
+                       if (!*chlg)
+                               chlg = NULL;
+
+                       response = ews_connection_gssapi_challenge (sasl, chlg ? chlg : "\r\n", chlg != NULL, 
&error);
+
+                       if (response && *response) {
+                               gchar *sasl_response = g_strconcat ("Negotiate ", response, NULL);
+
+                               soup_message_headers_remove (message->request_headers, "Authorization");
+                               soup_message_headers_append (message->request_headers, "Authorization", 
sasl_response);
+                               soup_session_requeue_message (session, message);
+
+                               g_free (sasl_response);
+                       } else if (error) {
+                               /* cannot use SOUP_STATUS_UNAUTHORIZED, because it may hide an error message,
+                                  which is a local error of Kerberos/GSSAPI call */
+                               soup_message_set_status_full (message, SOUP_STATUS_BAD_REQUEST, 
error->message);
+                       }
+
+                       g_free (response);
+                       break;
+               }
+       }
+
+       g_strfreev (auths);
+}
+
+static void
+ews_connection_setup_msg_gssapi_auth (EEwsConnection *connection,
+                                     SoupSession *session,
+                                     SoupMessage *message)
+{
+       CamelSasl *gssapi_sasl;
+       CamelEwsSettings *ews_settings;
+       CamelNetworkSettings *network_settings;
+       SoupURI *soup_uri;
+       const gchar *host, *user;
+
+       if (!camel_sasl_gssapi_is_available ())
+               return;
+
+       g_return_if_fail (E_IS_EWS_CONNECTION (connection));
+       g_return_if_fail (SOUP_IS_MESSAGE (message));
+
+       ews_settings = e_ews_connection_ref_settings (connection);
+       network_settings = CAMEL_NETWORK_SETTINGS (ews_settings);
+       gssapi_sasl = g_object_new (camel_sasl_gssapi_get_type (),
+                       "mechanism", "GSSAPI",
+                       "service-name", "HTTP",
+                       NULL);
+
+       soup_uri = soup_message_get_uri (message);
+       host = soup_uri_get_host (soup_uri);
+       user = soup_uri_get_user (soup_uri);
+       if (!host || !*host)
+               host = camel_network_settings_get_host (network_settings);
+       if (!user || !*user)
+               user = camel_network_settings_get_user (network_settings);
+
+       camel_sasl_gssapi_override_host_and_user (CAMEL_SASL_GSSAPI (gssapi_sasl), host, user);
+
+       /* this might not be a cyclic ref dependency, as long as the message
+          is properly served through the session and freed */
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SOUP_SESSION,
+               g_object_ref (session), g_object_unref);
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_CONNECTION,
+               g_object_ref (connection), e_ews_connection_utils_unref_in_thread);
+       g_object_set_data_full (G_OBJECT (message), EWS_GSSAPI_SASL,
+               gssapi_sasl, g_object_unref);
+
+       soup_message_add_header_handler (message, "got_body", "WWW-Authenticate",
+               G_CALLBACK (ews_connection_authenticate_gssapi_cb), NULL);
+
+       g_object_unref (ews_settings);
+}
+
 typedef enum _EwsScheduleOp {
        EWS_SCHEDULE_OP_QUEUE_MESSAGE,
        EWS_SCHEDULE_OP_CANCEL,
@@ -455,7 +614,18 @@ ews_next_request (gpointer _cnc)
        cnc->priv->active_job_queue = g_slist_append (cnc->priv->active_job_queue, node);
 
        if (cnc->priv->soup_session) {
-               soup_session_queue_message (cnc->priv->soup_session, SOUP_MESSAGE (node->msg), 
ews_response_cb, node);
+               SoupMessage *msg = SOUP_MESSAGE (node->msg);
+               CamelEwsSettings *ews_settings = e_ews_connection_ref_settings (cnc);
+               gchar *auth_mech = NULL;
+
+               g_object_get (G_OBJECT (ews_settings), "auth-mechanism", &auth_mech, NULL);
+               if (g_strcmp0 (auth_mech, "GSSAPI") == 0)
+                       ews_connection_setup_msg_gssapi_auth (cnc, cnc->priv->soup_session, msg);
+
+               g_object_unref (ews_settings);
+               g_free (auth_mech);
+
+               soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node);
                QUEUE_UNLOCK (cnc);
        } else {
                QUEUE_UNLOCK (cnc);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]