[evolution-ews] Bug #704869 - Support Kerberos authentication
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews] Bug #704869 - Support Kerberos authentication
- Date: Mon, 30 Sep 2013 11:28:51 +0000 (UTC)
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]