[evolution-mapi] Bug #592779 - Authenticate using Kerberos



commit 6e731ab4ab1da39837ef42e0e32630e587cd370f
Author: Sean Finney <seanius seanius net>
Date:   Wed Aug 10 17:18:48 2011 +0200

    Bug #592779 - Authenticate using Kerberos

 .../exchange-mapi-account-listener.c               |   71 ++++++--
 .../exchange-mapi-account-setup.c                  |  180 ++++++++++++++++---
 src/addressbook/e-book-backend-mapi.c              |  146 ++++++++++-------
 src/calendar/Makefile.am                           |    6 +-
 src/calendar/e-cal-backend-mapi.c                  |  111 ++++++++-----
 src/camel/camel-mapi-store.c                       |   85 ++++++----
 src/libexchangemapi/exchange-mapi-connection.c     |   65 ++++---
 src/libexchangemapi/exchange-mapi-connection.h     |   25 ++-
 src/libexchangemapi/exchange-mapi-utils.c          |  101 ++++++++++-
 src/libexchangemapi/exchange-mapi-utils.h          |    5 +-
 10 files changed, 567 insertions(+), 228 deletions(-)
---
diff --git a/src/account-setup-eplugin/exchange-mapi-account-listener.c b/src/account-setup-eplugin/exchange-mapi-account-listener.c
index 1369a58..d5a9174 100644
--- a/src/account-setup-eplugin/exchange-mapi-account-listener.c
+++ b/src/account-setup-eplugin/exchange-mapi-account-listener.c
@@ -45,6 +45,17 @@
 
 #define d(x)
 
+#define SET_KRB_SSO(_esource, _krbval) \
+	G_STMT_START { \
+		ESource *tmp_src = (_esource); \
+		const gchar *tmp_val = (_krbval); \
+		e_source_set_property (tmp_src, "kerberos", tmp_val); \
+		if (tmp_val && g_str_equal (tmp_val, "required")) { \
+			e_source_set_property (tmp_src, "auth", NULL); \
+			e_source_set_property (tmp_src, "auth-type", NULL); \
+		} \
+	} G_STMT_END
+
 G_DEFINE_TYPE (ExchangeMAPIAccountListener, exchange_mapi_account_listener, G_TYPE_OBJECT)
 
 static gboolean create_profile_entry (CamelURL *url, EAccount *account);
@@ -219,7 +230,7 @@ add_cal_esource (EAccount *account, GSList *folders, ExchangeMAPIFolderType fold
 {
 	ESourceList *source_list = NULL;
 	ESourceGroup *group = NULL;
-	const gchar *conf_key = NULL, *source_selection_key = NULL;
+	const gchar *conf_key = NULL, *source_selection_key = NULL, *kerberos;
 	GConfClient* client;
 	GSList *ids, *temp_list, *old_sources = NULL;
 	gchar *base_uri = NULL;
@@ -262,6 +273,9 @@ add_cal_esource (EAccount *account, GSList *folders, ExchangeMAPIFolderType fold
 	e_source_group_set_property (group, "host", url->host);
 	e_source_group_set_property (group, "profile", camel_url_get_param (url, "profile"));
 	e_source_group_set_property (group, "domain", camel_url_get_param (url, "domain"));
+	e_source_group_set_property (group, "realm", camel_url_get_param (url, "realm"));
+	kerberos = camel_url_get_param (url, "kerberos");
+	e_source_group_set_property (group, "kerberos", kerberos);
 
 	/* We set these because on new folder creation - these are required. */
 	e_source_group_set_property (group, "acl-user-name", account->id->name);
@@ -297,8 +311,10 @@ add_cal_esource (EAccount *account, GSList *folders, ExchangeMAPIFolderType fold
 		e_source_set_property (source, "host", url->host);
 		e_source_set_property (source, "profile", camel_url_get_param (url, "profile"));
 		e_source_set_property (source, "domain", camel_url_get_param (url, "domain"));
+		e_source_set_property (source, "realm", camel_url_get_param (url, "realm"));
 		e_source_set_property (source, "folder-id", fid);
 		e_source_set_property (source, "public", "no");
+		SET_KRB_SSO(source, kerberos);
 
 		if (is_new_source)
 			e_source_set_property (source, "offline_sync",
@@ -369,7 +385,7 @@ void exchange_mapi_add_esource (CamelURL *url, const gchar *folder_name, const g
 {
 	ESourceList *source_list = NULL;
 	ESourceGroup *group = NULL;
-	const gchar *conf_key = NULL;
+	const gchar *conf_key = NULL, *kerberos = NULL;
 	GConfClient* client;
 	GSList *sources;
 	ESource *source = NULL;
@@ -413,6 +429,7 @@ void exchange_mapi_add_esource (CamelURL *url, const gchar *folder_name, const g
 	}
 
 	relative_uri = g_strconcat (";", fid, NULL);
+	kerberos = camel_url_get_param (url, "kerberos");
 	source = e_source_new (folder_name, relative_uri);
 	e_source_set_property (source, "auth", "1");
 	e_source_set_property (source, "auth-type", "plain/password");
@@ -420,11 +437,13 @@ void exchange_mapi_add_esource (CamelURL *url, const gchar *folder_name, const g
 	e_source_set_property (source, "host", url->host);
 	e_source_set_property (source, "profile", camel_url_get_param (url, "profile"));
 	e_source_set_property (source, "domain", camel_url_get_param (url, "domain"));
+	e_source_set_property (source, "realm", camel_url_get_param (url, "realm"));
 	e_source_set_property (source, "folder-id", fid);
 	e_source_set_property (source, "offline_sync",
 				camel_url_get_param (url, "offline_sync") ? "1" : "0");
 	e_source_set_property (source, "public", "yes");
 	e_source_set_property (source, "delete", "yes");
+	SET_KRB_SSO(source, kerberos);
 
 	e_source_group_add_source (group, source, -1);
 
@@ -624,6 +643,7 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 	GSList *temp_list, *old_sources = NULL;
 	GConfClient* client;
 	gboolean is_new_group = FALSE;
+	const gchar *kerberos = NULL;
 
 	url = camel_url_new (account->source->url, NULL);
 	if (url == NULL) {
@@ -647,11 +667,14 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 		is_new_group = TRUE;
 		old_sources = NULL;
 	}
+	kerberos = camel_url_get_param (url, "kerberos");
 	e_source_group_set_property (group, "user", NULL);
 	e_source_group_set_property (group, "username", url->user);
 	e_source_group_set_property (group, "host", url->host);
 	e_source_group_set_property (group, "profile", camel_url_get_param (url, "profile"));
 	e_source_group_set_property (group, "domain", camel_url_get_param (url, "domain"));
+	e_source_group_set_property (group, "realm", camel_url_get_param (url, "realm"));
+	e_source_group_set_property (group, "kerberos", kerberos);
 
 	for (temp_list = folders; temp_list != NULL; temp_list = g_slist_next (temp_list)) {
 		ExchangeMAPIFolder *folder = temp_list->data;
@@ -680,8 +703,10 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 		e_source_set_property(source, "host", url->host);
 		e_source_set_property(source, "profile", camel_url_get_param (url, "profile"));
 		e_source_set_property(source, "domain", camel_url_get_param (url, "domain"));
+		e_source_set_property(source, "realm", camel_url_get_param (url, "realm"));
 		e_source_set_property(source, "folder-id", fid);
 		e_source_set_property (source, "public", "no");
+		SET_KRB_SSO(source, kerberos);
 
 		if (is_new_source) {
 			e_source_set_property (source, "offline_sync",
@@ -744,6 +769,8 @@ add_addressbook_sources (EAccount *account, GSList *folders, mapi_id_t trash_fid
 		e_source_set_property(source, "view-limit", camel_url_get_param (url, "ad_limit"));
 		e_source_set_property(source, "profile", camel_url_get_param (url, "profile"));
 		e_source_set_property(source, "domain", camel_url_get_param (url, "domain"));
+		e_source_set_property(source, "realm", camel_url_get_param (url, "realm"));
+		SET_KRB_SSO(source, kerberos);
 
 		if (is_new_source) {
 			e_source_set_property(source, "offline_sync", "1");
@@ -1101,42 +1128,45 @@ create_profile_entry (CamelURL *url, EAccount *account)
 {
 	gboolean status = FALSE;
 	guint8 attempts = 0;
+	ExchangeMapiProfileData empd = { 0 };
 
 	if (!e_shell_get_online (e_shell_get_default ()))
 		return FALSE;
 
+	exchange_mapi_util_profiledata_from_camelurl (&empd, url);
+
 	while (!status && attempts <= 3) {
-		gchar *password = NULL, *key = NULL;
+		gchar *key = NULL;
 
 		key = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
-		if (!attempts)
-			password = e_passwords_get_password (NULL, key);
-		if (!password) {
+		if (!attempts && !empd.krb_sso)
+			empd.password = e_passwords_get_password (NULL, key);
+		if (!empd.password && !empd.krb_sso) {
 			gboolean remember = account && e_account_get_bool (account, E_ACCOUNT_SOURCE_SAVE_PASSWD);
 			gchar *title;
 
-			title = g_strdup_printf (_("Enter Password for %s %s"), url->user, url->host);
-			password = e_passwords_ask_password (title, NULL, key, title,
-					E_PASSWORDS_REMEMBER_FOREVER | E_PASSWORDS_SECRET | (attempts ? E_PASSWORDS_REPROMPT : 0),
-					&remember, NULL);
+			title = g_strdup_printf (_("Enter Password for %s %s"),
+						 url->user, url->host);
+			empd.password = e_passwords_ask_password (title, NULL, key, title, E_PASSWORDS_REMEMBER_FOREVER | E_PASSWORDS_SECRET | (attempts ? E_PASSWORDS_REPROMPT : 0), &remember, NULL);
 			g_free (title);
 		}
 		g_free (key);
 
-		if (password) {
+
+		if (empd.password || empd.krb_sso) {
 			GError *error = NULL;
-			guint32 cp_flags = (camel_url_get_param (url, "ssl") && g_str_equal (camel_url_get_param (url, "ssl"), "1")) ? CREATE_PROFILE_FLAG_USE_SSL : CREATE_PROFILE_FLAG_NONE;
 
-			status = exchange_mapi_create_profile (url->user, password, camel_url_get_param (url, "domain"), url->host, cp_flags, NULL, NULL, &error);
+			status = exchange_mapi_create_profile (&empd, NULL,
+							       NULL, &error);
 			if (status) {
 				/* profile was created, try to connect to the server */
 				ExchangeMapiConnection *conn;
 				gchar *profname;
 
 				status = FALSE;
-				profname = exchange_mapi_util_profile_name (url->user, camel_url_get_param (url, "domain"), url->host, FALSE);
+				profname = exchange_mapi_util_profile_name (&empd, FALSE);
 
-				conn = exchange_mapi_connection_new (profname, password, &error);
+				conn = exchange_mapi_connection_new (profname, empd.password, &error);
 				if (conn) {
 					status = exchange_mapi_connection_connected (conn);
 					g_object_unref (conn);
@@ -1169,7 +1199,7 @@ check_equal (const gchar *a, const gchar *b)
 static gboolean
 mapi_camel_url_equal (CamelURL *a, CamelURL *b)
 {
-	const gchar *params[] = { "profile", "domain", "ad_limit", "ad_server" };
+	const gchar *params[] = { "profile", "domain", "realm", "kerberos", "ad_limit", "ad_server" };
 	guint n_params = G_N_ELEMENTS (params), i;
 	gboolean retval = TRUE;
 
@@ -1191,6 +1221,7 @@ mapi_account_changed_async (gpointer worker_data, gboolean cancelled, gpointer u
 	ExchangeMAPIAccountInfo *existing_account_info = NULL;
 	EAccountList *account_listener = worker_data;
 	EAccount *account = user_data;
+	ExchangeMapiProfileData empd = { 0 };
 
 	g_return_if_fail (account_listener != NULL);
 	g_return_if_fail (account != NULL);
@@ -1212,7 +1243,10 @@ mapi_account_changed_async (gpointer worker_data, gboolean cancelled, gpointer u
 			gchar *profname = NULL, *uri = NULL;
 			ExchangeMAPIAccountListener *config_listener = exchange_mapi_accounts_peek_config_listener();
 
-			profname = exchange_mapi_util_profile_name (new_url->user, camel_url_get_param (new_url, "domain"), new_url->host, FALSE);
+			exchange_mapi_util_profiledata_from_camelurl (&empd,
+								      new_url);
+			profname = exchange_mapi_util_profile_name (&empd,
+								    FALSE);
 			camel_url_set_param(new_url, "profile", profname);
 			g_free (profname);
 
@@ -1245,7 +1279,8 @@ mapi_account_changed_async (gpointer worker_data, gboolean cancelled, gpointer u
 				gchar *profname = NULL, *uri = NULL;
 				ExchangeMAPIAccountListener *config_listener = exchange_mapi_accounts_peek_config_listener();
 
-				profname = exchange_mapi_util_profile_name (new_url->user, camel_url_get_param (new_url, "domain"), new_url->host, FALSE);
+				exchange_mapi_util_profiledata_from_camelurl (&empd, new_url);
+				profname = exchange_mapi_util_profile_name (&empd, FALSE);
 				camel_url_set_param(new_url, "profile", profname);
 				g_free (profname);
 
diff --git a/src/account-setup-eplugin/exchange-mapi-account-setup.c b/src/account-setup-eplugin/exchange-mapi-account-setup.c
index 5d1e030..04f540d 100644
--- a/src/account-setup-eplugin/exchange-mapi-account-setup.c
+++ b/src/account-setup-eplugin/exchange-mapi-account-setup.c
@@ -108,7 +108,7 @@ tree_selection_changed (GtkTreeSelection *selection, GtkDialog *dialog)
 /* Callback for ProcessNetworkProfile. If we have more than one username,
  we need to let the user select. */
 static uint32_t
-create_profile_callback (struct SRowSet *rowset, gpointer data)
+create_profile_callback (struct SRowSet *rowset, gconstpointer data)
 {
 	struct SPropValue *lpProp_fullname, *lpProp_account;
 	gint response;
@@ -208,13 +208,32 @@ create_profile_callback (struct SRowSet *rowset, gpointer data)
 	return index;
 }
 
+static char*
+prompt_password(const gchar *user, const gchar *host, const gchar *key,
+		EMConfigTargetAccount *account)
+{
+	int pw_flags = E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET;
+	gchar *password, *title;
+	gboolean save;
+
+	password = e_passwords_get_password (NULL, key);
+	save = e_account_get_bool (account->modified_account,
+				   E_ACCOUNT_SOURCE_SAVE_PASSWD);
+	title = g_strdup_printf (_("Enter Password for %s %s"), user, host);
+	password = e_passwords_ask_password (title, NULL, key, title, pw_flags,
+					     &save, NULL);
+	g_free (title);
+	return password;
+}
+
 static void
 validate_credentials (GtkWidget *widget, EConfig *config)
 {
 	EMConfigTargetAccount *target_account = (EMConfigTargetAccount *)(config->target);
 	CamelURL *url = NULL;
-	gchar *key = NULL, *password = NULL;
-	const gchar *domain_name = NULL;
+	gchar *key = NULL;
+	ExchangeMapiProfileData empd = { 0 };
+	GError *error = NULL;
 
 	if (!e_shell_get_online (e_shell_get_default ())) {
 		e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Cannot create MAPI folders in offline mode."));
@@ -222,7 +241,6 @@ validate_credentials (GtkWidget *widget, EConfig *config)
 	}
 
 	url = camel_url_new (e_account_get_string (target_account->modified_account, E_ACCOUNT_SOURCE_URL), NULL);
-	domain_name = camel_url_get_param (url, "domain");
 
 	/* Silently remove domain part from a username when user enters it as such.
 	   This change will be visible in the UI on new edit open. */
@@ -235,32 +253,32 @@ validate_credentials (GtkWidget *widget, EConfig *config)
 		g_free (tmp);
 	}
 
-	if (!url->user || !*url->user || !url->host || !*url->host || !domain_name || !*domain_name) {
+	exchange_mapi_util_profiledata_from_camelurl (&empd, url);
+	if (!empd.username || !*(empd.username)
+	    || !empd.server || !*(empd.server)
+	    || ((!empd.domain || !*(empd.domain))
+		&& !empd.krb_sso)) {
 		e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Server, username and domain name cannot be empty. Please fill them with correct values."));
 		camel_url_free (url);
 		return;
+	} else if (empd.krb_sso && (!empd.krb_realm || !*(empd.krb_realm))) {
+		e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Realm name cannot be empty when kerberos is selected. Please fill them with correct values."));
+		camel_url_free (url);
+		return;
 	}
 
 	key = camel_url_to_string (url, CAMEL_URL_HIDE_PASSWORD | CAMEL_URL_HIDE_PARAMS);
-	password = e_passwords_get_password (NULL, key);
-	if (!password || !*password) {
-		gboolean remember = e_account_get_bool (target_account->modified_account, E_ACCOUNT_SOURCE_SAVE_PASSWD);
-		gchar *title;
-
-		g_free (password);
-		title = g_strdup_printf (_("Enter Password for %s %s"), url->user, url->host);
-		password = e_passwords_ask_password (title, NULL, key, title,
-						     E_PASSWORDS_REMEMBER_FOREVER|E_PASSWORDS_SECRET,
-						     &remember, NULL);
-		g_free (title);
+	if (empd.krb_sso) {
+		exchange_mapi_util_trigger_krb_auth (&empd, &error);
+	} else {
+		empd.password = prompt_password(empd.username, empd.server,
+						key, target_account);
 	}
 
-	/*Can there be a account without password ?*/
-	if (password && *password && domain_name && *domain_name && *url->user && *url->host) {
-		guint32 cp_flags = (camel_url_get_param (url, "ssl") && g_str_equal (camel_url_get_param (url, "ssl"), "1")) ? CREATE_PROFILE_FLAG_USE_SSL : CREATE_PROFILE_FLAG_NONE;
-		GError *error = NULL;
-		gboolean status = exchange_mapi_create_profile (url->user, password, domain_name, url->host, cp_flags,
-								(mapi_profile_callback_t) create_profile_callback, url->user,
+	if (COMPLETE_PROFILEDATA(&empd)) {
+		gboolean status = exchange_mapi_create_profile (&empd,
+								(mapi_profile_callback_t) create_profile_callback,
+								empd.username,
 								&error);
 		if (status) {
 			/* profile was created, try to connect to the server */
@@ -268,9 +286,10 @@ validate_credentials (GtkWidget *widget, EConfig *config)
 			gchar *profname;
 
 			status = FALSE;
-			profname = exchange_mapi_util_profile_name (url->user, domain_name, url->host, FALSE);
+			profname = exchange_mapi_util_profile_name (&empd,
+								    FALSE);
 
-			conn = exchange_mapi_connection_new (profname, password, &error);
+			conn = exchange_mapi_connection_new (profname, empd.password, &error);
 			if (conn) {
 				status = exchange_mapi_connection_connected (conn);
 				g_object_unref (conn);
@@ -283,7 +302,8 @@ validate_credentials (GtkWidget *widget, EConfig *config)
 			/* Things are successful */
 			gchar *profname = NULL, *uri = NULL;
 
-			profname = exchange_mapi_util_profile_name (url->user, domain_name, url->host, FALSE);
+			profname = exchange_mapi_util_profile_name (&empd,
+								    FALSE);
 			camel_url_set_param (url, "profile", profname);
 			g_free (profname);
 
@@ -305,14 +325,15 @@ validate_credentials (GtkWidget *widget, EConfig *config)
 			g_free (e);
 		}
 
-		if (error)
-			g_error_free (error);
 	} else {
 		e_passwords_forget_password (NULL, key);
 		e_notice (NULL, GTK_MESSAGE_ERROR, "%s", _("Authentication failed."));
 	}
 
-	g_free (password);
+	if (error)
+		g_error_free (error);
+
+	g_free (empd.password);
 	g_free (key);
 	camel_url_free (url);
 }
@@ -342,6 +363,49 @@ domain_entry_changed(GtkWidget *entry, EConfig *config)
 }
 
 static void
+realm_entry_changed (GtkWidget *entry, EConfig *config)
+{
+	EMConfigTargetAccount *target = (EMConfigTargetAccount *)(config->target);
+	CamelURL *url = NULL;
+	const gchar *realm = NULL;
+	gchar *url_string = NULL;
+
+	url = camel_url_new (e_account_get_string(target->modified_account, E_ACCOUNT_SOURCE_URL), NULL);
+	realm = gtk_entry_get_text (GTK_ENTRY(entry));
+
+	if (realm && realm[0])
+		camel_url_set_param (url, "realm", realm);
+	else
+		camel_url_set_param (url, "realm", NULL);
+
+	url_string = camel_url_to_string (url, 0);
+	e_account_set_string (target->modified_account, E_ACCOUNT_SOURCE_URL, url_string);
+	e_account_set_string (target->modified_account, E_ACCOUNT_TRANSPORT_URL, url_string);
+	g_free (url_string);
+
+	camel_url_free (url);
+}
+
+static void
+krb_sso_check_toggled (GtkWidget *check, EConfig *config)
+{
+	EMConfigTargetAccount *target = (EMConfigTargetAccount *)(config->target);
+	CamelURL *url = NULL;
+	gchar *url_string = NULL;
+
+	url = camel_url_new (e_account_get_string (target->modified_account, E_ACCOUNT_SOURCE_URL), NULL);
+
+	camel_url_set_param (url, "kerberos", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check)) ? "required" : NULL);
+
+	url_string = camel_url_to_string (url, 0);
+	e_account_set_string (target->modified_account, E_ACCOUNT_SOURCE_URL, url_string);
+	e_account_set_string (target->modified_account, E_ACCOUNT_TRANSPORT_URL, url_string);
+	g_free (url_string);
+
+	camel_url_free (url);
+}
+
+static void
 secure_check_toggled (GtkWidget *check, EConfig *config)
 {
 	EMConfigTargetAccount *target = (EMConfigTargetAccount *)(config->target);
@@ -360,6 +424,15 @@ secure_check_toggled (GtkWidget *check, EConfig *config)
 	camel_url_free (url);
 }
 
+static void
+widget_sanitizer_cb (GtkToggleButton *button, GtkWidget *widget)
+{
+	g_return_if_fail (GTK_IS_TOGGLE_BUTTON (button));
+	g_return_if_fail (GTK_IS_WIDGET (button));
+
+	gtk_widget_set_sensitive (widget, gtk_toggle_button_get_active (button));
+}
+
 GtkWidget *
 org_gnome_exchange_mapi_account_setup (EPlugin *epl, EConfigHookItemFactoryData *data)
 {
@@ -378,10 +451,14 @@ org_gnome_exchange_mapi_account_setup (EPlugin *epl, EConfigHookItemFactoryData
 	if (!g_ascii_strcasecmp (url->protocol, "mapi")) {
 		GtkWidget *label;
 		GtkWidget *domain_name;
+		GtkWidget *realm_name;
 		GtkWidget *auth_button;
 		GtkWidget *secure_conn;
+		GtkWidget *krb_sso;
 		const gchar *domain_value = camel_url_get_param (url, "domain");
+		const gchar *realm_value = camel_url_get_param (url, "realm");
 		const gchar *use_ssl = camel_url_get_param (url, "ssl");
+		const gchar *kerberos = camel_url_get_param (url, "kerberos");
 
 		g_object_get (data->parent, "n-rows", &row, NULL);
 
@@ -406,12 +483,37 @@ org_gnome_exchange_mapi_account_setup (EPlugin *epl, EConfigHookItemFactoryData
 		gtk_table_attach (GTK_TABLE (data->parent), GTK_WIDGET (hgrid), 1, 2, row, row+1, GTK_FILL|GTK_EXPAND, GTK_FILL, 0, 0);
 
 		row++;
-
 		secure_conn = gtk_check_button_new_with_mnemonic (_("_Use secure connection"));
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (secure_conn), use_ssl && g_str_equal (use_ssl, "1"));
 		g_signal_connect (secure_conn, "toggled", G_CALLBACK (secure_check_toggled), data->config);
 		gtk_widget_show (secure_conn);
 		gtk_table_attach (GTK_TABLE (data->parent), GTK_WIDGET (secure_conn), 1, 2, row, row + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+		row++;
+		krb_sso = gtk_check_button_new_with_mnemonic (_("_Kerberos authentication"));
+		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (krb_sso), kerberos && g_str_equal (kerberos, "required"));
+		g_signal_connect (krb_sso, "toggled", G_CALLBACK (krb_sso_check_toggled), data->config);
+		gtk_widget_show (krb_sso);
+		gtk_table_attach (GTK_TABLE (data->parent), GTK_WIDGET (krb_sso), 1, 2, row, row + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+		row++;
+		label = gtk_label_new_with_mnemonic (_("_Realm name:"));
+		gtk_widget_show (label);
+
+		realm_name = gtk_entry_new ();
+		gtk_label_set_mnemonic_widget (GTK_LABEL (label), realm_name);
+		if (realm_value && *realm_value)
+			gtk_entry_set_text (GTK_ENTRY (realm_name), realm_value);
+		gtk_widget_show (realm_name);
+		g_signal_connect (realm_name, "changed", G_CALLBACK(realm_entry_changed), data->config);
+		gtk_table_attach (GTK_TABLE (data->parent), label, 0, 1, row, row + 1, 0, 0, 0, 0);
+		gtk_table_attach (GTK_TABLE (data->parent), GTK_WIDGET (realm_name), 1, 2, row, row + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0);
+
+		g_signal_connect (krb_sso, "toggled", G_CALLBACK (widget_sanitizer_cb), label);
+		g_signal_connect (krb_sso, "toggled", G_CALLBACK (widget_sanitizer_cb), realm_name);
+
+		widget_sanitizer_cb (GTK_TOGGLE_BUTTON (krb_sso), label);
+		widget_sanitizer_cb (GTK_TOGGLE_BUTTON (krb_sso), realm_name);
 	}
 
 	camel_url_free (url);
@@ -774,6 +876,7 @@ exchange_mapi_book_commit (EPlugin *epl, EConfigTarget *target)
 	EABConfigTargetSource *t = (EABConfigTargetSource *) target;
 	ESource *source = t->source;
 	gchar *uri_text, *r_uri, *sfid;
+	const gchar *kerberos = NULL;
 	ESourceGroup *grp;
 	ExchangeMapiConnection *conn;
 	mapi_id_t fid, pfid;
@@ -815,6 +918,13 @@ exchange_mapi_book_commit (EPlugin *epl, EConfigTarget *target)
 	e_source_set_property(source, "host", e_source_group_get_property (grp, "host"));
 	e_source_set_property(source, "profile", e_source_group_get_property (grp, "profile"));
 	e_source_set_property(source, "domain", e_source_group_get_property (grp, "domain"));
+	e_source_set_property(source, "realm", e_source_group_get_property (grp, "realm"));
+	kerberos = e_source_group_get_property (grp, "kerberos");
+	e_source_set_property(source, "kerberos", kerberos);
+	if (kerberos && g_str_equal (kerberos, "required")) {
+		e_source_set_property (source, "auth", NULL);
+		e_source_set_property (source, "auth-type", NULL);
+	}
 	e_source_set_relative_uri (source, g_strconcat (";",e_source_peek_name (source), NULL));
 
 	e_source_set_property (source, "completion", "true");
@@ -940,6 +1050,18 @@ exchange_mapi_cal_commit (EPlugin *epl, EConfigTarget *target)
 	e_source_set_property (source, "domain", tmp);
 	g_free (tmp);
 
+	tmp = e_source_group_get_property (group, "realm");
+	e_source_set_property (source, "realm", tmp);
+	g_free (tmp);
+
+	tmp = e_source_group_get_property (group, "kerberos");
+	e_source_set_property (source, "kerberos", tmp);
+	if (tmp && g_str_equal (tmp, "required")) {
+		e_source_set_property (source, "auth", NULL);
+		e_source_set_property (source, "auth-type", NULL);
+	}
+	g_free (tmp);
+
 	tmp = exchange_mapi_util_mapi_id_to_string (fid);
 	e_source_set_property (source, "folder-id", tmp);
 	g_free (tmp);
diff --git a/src/addressbook/e-book-backend-mapi.c b/src/addressbook/e-book-backend-mapi.c
index 4493f07..9b0c1a5 100644
--- a/src/addressbook/e-book-backend-mapi.c
+++ b/src/addressbook/e-book-backend-mapi.c
@@ -385,12 +385,70 @@ ebbm_update_cache_cb (gpointer data)
 }
 
 static void
+ebbm_connect_user (EBookBackendMAPI *ebma, GCancellable *cancellable, const gchar *password, GError **error)
+{
+	EBookBackendMAPIPrivate *priv = ebma->priv;
+	GError *mapi_error = NULL;
+	ExchangeMapiConnection *old_conn;
+
+	if (!e_book_backend_is_online (E_BOOK_BACKEND (ebma))) {
+		ebbm_notify_connection_status (ebma, FALSE);
+	} else {
+		if (priv->update_cache_thread) {
+			g_cancellable_cancel (priv->update_cache);
+			g_thread_join (priv->update_cache_thread);
+			priv->update_cache_thread = NULL;
+		}
+
+		e_book_backend_mapi_lock_connection (ebma);
+
+		old_conn = priv->conn;
+		priv->conn = NULL;
+
+		priv->conn = exchange_mapi_connection_new (priv->profile,
+							   password,
+							   &mapi_error);
+		if (!priv->conn) {
+			priv->conn = exchange_mapi_connection_find (priv->profile);
+			if (priv->conn && !exchange_mapi_connection_connected (priv->conn))
+				exchange_mapi_connection_reconnect (priv->conn, password, &mapi_error);
+		}
+
+		if (old_conn)
+			g_object_unref (old_conn);
+
+		if (!priv->conn || mapi_error) {
+			if (priv->conn) {
+				g_object_unref (priv->conn);
+				priv->conn = NULL;
+			}
+
+			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Cannot connect"));
+			e_book_backend_mapi_unlock_connection (ebma);
+
+			if (mapi_error)
+				g_error_free (mapi_error);
+
+			ebbm_notify_connection_status (ebma, FALSE);
+			return;
+		}
+
+		e_book_backend_mapi_unlock_connection (ebma);
+
+		ebbm_notify_connection_status (ebma, TRUE);
+
+		/* if (priv->marked_for_offline) */
+		priv->update_cache_thread = g_thread_create (ebbm_update_cache_cb, ebma, TRUE, NULL);
+	}
+}
+
+static void
 ebbm_open (EBookBackendMAPI *ebma, GCancellable *cancellable, gboolean only_if_exists, GError **perror)
 {
 	EBookBackendMAPIPrivate *priv = ebma->priv;
 	ESource *source = e_book_backend_get_source (E_BOOK_BACKEND (ebma));
 	const gchar *offline;
-	const gchar *cache_dir;
+	const gchar *cache_dir, *krb_sso;
 	GError *error = NULL;
 
 	if (e_book_backend_is_opened (E_BOOK_BACKEND (ebma))) {
@@ -443,7 +501,14 @@ ebbm_open (EBookBackendMAPI *ebma, GCancellable *cancellable, gboolean only_if_e
 	}
 
 	e_book_backend_notify_online (E_BOOK_BACKEND (ebma), TRUE);
-	e_book_backend_notify_auth_required (E_BOOK_BACKEND (ebma), TRUE, NULL);
+	krb_sso = e_source_get_property (source, "kerberos");
+	if (!krb_sso || !g_str_equal (krb_sso, "required")) {
+		e_book_backend_notify_auth_required (E_BOOK_BACKEND (ebma),
+						     TRUE, NULL);
+	} else {
+		ebbm_connect_user (ebma, cancellable, NULL, perror);
+		e_book_backend_notify_opened (E_BOOK_BACKEND (ebma), NULL);
+	}
 }
 
 static void
@@ -520,66 +585,13 @@ ebbm_get_backend_property (EBookBackendMAPI *ebma, const gchar *prop_name, gchar
 static void
 ebbm_authenticate_user (EBookBackendMAPI *ebma, GCancellable *cancellable, ECredentials *credentials, GError **error)
 {
-	EBookBackendMAPIPrivate *priv = ebma->priv;
-	GError *mapi_error = NULL;
-	ExchangeMapiConnection *old_conn;
+	const gchar *password;
 
 	if (!e_book_backend_is_online (E_BOOK_BACKEND (ebma))) {
 		ebbm_notify_connection_status (ebma, FALSE);
 	} else {
-		if (priv->update_cache_thread) {
-			g_cancellable_cancel (priv->update_cache);
-			g_thread_join (priv->update_cache_thread);
-			priv->update_cache_thread = NULL;
-		}
-
-		e_book_backend_mapi_lock_connection (ebma);
-
-		if (g_cancellable_set_error_if_cancelled (cancellable, error)) {
-			e_book_backend_mapi_unlock_connection (ebma);
-			return;
-		}
-
-		old_conn = priv->conn;
-		priv->conn = NULL;
-
-		priv->conn = exchange_mapi_connection_new (priv->profile, e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD), &mapi_error);
-		if (!priv->conn) {
-			priv->conn = exchange_mapi_connection_find (priv->profile);
-			if (priv->conn && !exchange_mapi_connection_connected (priv->conn))
-				exchange_mapi_connection_reconnect (priv->conn, e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD), &mapi_error);
-		}
-
-		if (old_conn)
-			g_object_unref (old_conn);
-
-		if (!priv->conn || mapi_error) {
-			if (priv->conn) {
-				g_object_unref (priv->conn);
-				priv->conn = NULL;
-			}
-
-			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Cannot connect"));
-			e_book_backend_mapi_unlock_connection (ebma);
-
-			if (mapi_error)
-				g_error_free (mapi_error);
-
-			ebbm_notify_connection_status (ebma, FALSE);
-			return;
-		}
-
-		e_book_backend_mapi_unlock_connection (ebma);
-
-		ebbm_notify_connection_status (ebma, TRUE);
-
-		if (!g_cancellable_is_cancelled (cancellable) /* && priv->marked_for_offline */) {
-			g_object_ref (ebma);
-
-			priv->update_cache_thread = g_thread_create (ebbm_update_cache_cb, ebma, TRUE, NULL);
-			if (!priv->update_cache_thread)
-				g_object_unref (ebma);
-		}
+		password = e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD);
+		ebbm_connect_user (ebma, cancellable, password, error);
 	}
 }
 
@@ -588,11 +600,14 @@ ebbm_set_online (EBookBackend *backend, gboolean is_online)
 {
 	EBookBackendMAPI *ebma = E_BOOK_BACKEND_MAPI (backend);
 	EBookBackendMAPIPrivate *priv = ebma->priv;
+	ESource *esource;
+	const gchar *krb_sso = NULL;
 
 	e_book_backend_notify_online (backend, is_online);
 	if (e_book_backend_is_opened (backend)) {
 		e_book_backend_mapi_lock_connection (ebma);
 
+		esource = e_book_backend_get_source (E_BOOK_BACKEND (ebma));
 		if (!is_online) {
 			e_book_backend_notify_readonly (backend, TRUE);
 			ebbm_notify_connection_status (ebma, FALSE);
@@ -603,8 +618,19 @@ ebbm_set_online (EBookBackend *backend, gboolean is_online)
 			}
 		} else {
 			ebbm_notify_connection_status (ebma, TRUE);
-			if (!priv->conn)
-				e_book_backend_notify_auth_required (backend, TRUE, NULL);
+			if (!priv->conn) {
+				krb_sso = e_source_get_property (esource,
+								 "kerberos");
+				if (!krb_sso
+				    || !g_str_equal (krb_sso, "required")) {
+					e_book_backend_notify_auth_required (backend, TRUE, NULL);
+				} else {
+					ebbm_connect_user (ebma, NULL, NULL,
+							   NULL);
+					e_book_backend_notify_opened (backend,
+								      NULL);
+				}
+			}
 		}
 
 		e_book_backend_mapi_unlock_connection (ebma);
diff --git a/src/calendar/Makefile.am b/src/calendar/Makefile.am
index 8a457b6..f29f072 100644
--- a/src/calendar/Makefile.am
+++ b/src/calendar/Makefile.am
@@ -5,7 +5,8 @@ AM_CPPFLAGS =					\
 	$(LIBEBACKEND_CFLAGS)			\
 	$(LIBECAL_CFLAGS)			\
 	$(LIBEDATACAL_CFLAGS)			\
-	$(LIBMAPI_CFLAGS)
+	$(LIBMAPI_CFLAGS) \
+	$(CAMEL_CFLAGS)
 
 ecal_backend_LTLIBRARIES = libecalbackendmapi.la
 
@@ -20,7 +21,8 @@ libecalbackendmapi_la_LIBADD =			\
 	$(LIBEBACKEND_LIBS)			\
 	$(LIBECAL_LIBS)				\
 	$(LIBEDATACAL_LIBS)			\
-	$(LIBMAPI_LIBS)
+	$(LIBMAPI_LIBS)				\
+	$(CAMEL_LIBS)
 
 libecalbackendmapi_la_LDFLAGS =			\
 	-module -avoid-version $(NO_UNDEFINED)
diff --git a/src/calendar/e-cal-backend-mapi.c b/src/calendar/e-cal-backend-mapi.c
index a91ab4b..790b096 100644
--- a/src/calendar/e-cal-backend-mapi.c
+++ b/src/calendar/e-cal-backend-mapi.c
@@ -1221,13 +1221,63 @@ ecbm_connect (ECalBackendMAPI *cbmapi, GError **perror)
 }
 
 static void
+ecbm_connect_user (ECalBackend *backend, GCancellable *cancellable, const gchar *password, GError **perror)
+{
+	ECalBackendMAPI *cbmapi;
+	ECalBackendMAPIPrivate *priv;
+	ExchangeMapiConnection *old_conn;
+	GError *mapi_error = NULL;
+
+	g_static_mutex_lock (&auth_mutex);
+
+	cbmapi = E_CAL_BACKEND_MAPI (backend);
+	priv = cbmapi->priv;
+
+	old_conn = priv->conn;
+
+	priv->conn = exchange_mapi_connection_new (priv->profile, password, &mapi_error);
+	if (!priv->conn) {
+		priv->conn = exchange_mapi_connection_find (priv->profile);
+		if (priv->conn
+		    && !exchange_mapi_connection_connected (priv->conn)) {
+			exchange_mapi_connection_reconnect (priv->conn, password, &mapi_error);
+		}
+	}
+
+	if (old_conn)
+		g_object_unref (old_conn);
+
+	if (priv->conn && exchange_mapi_connection_connected (priv->conn)) {
+		/* Success */;
+	} else {
+		mapi_error_to_edc_error (perror, mapi_error, AuthenticationFailed, NULL);
+		if (mapi_error)
+			g_error_free (mapi_error);
+		g_static_mutex_unlock (&auth_mutex);
+		return;
+	}
+
+	if (mapi_error) {
+		mapi_error_to_edc_error (perror, mapi_error, AuthenticationFailed, NULL);
+		g_error_free (mapi_error);
+		g_static_mutex_unlock (&auth_mutex);
+		return;
+	}
+
+	g_static_mutex_unlock (&auth_mutex);
+
+	ecbm_connect (cbmapi, perror);
+}
+
+
+static void
 ecbm_open (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable, gboolean only_if_exists, GError **perror)
 {
 	ECalBackendMAPI *cbmapi;
 	ECalBackendMAPIPrivate *priv;
 	ESource *esource;
 	const gchar *fid = NULL;
-	const gchar *cache_dir;
+	const gchar *cache_dir, *krb_sso = NULL;
 	uint32_t olFolder = 0;
 
 	if (e_cal_backend_is_opened (E_CAL_BACKEND (backend))) {
@@ -1312,58 +1362,29 @@ ecbm_open (ECalBackend *backend, EDataCal *cal, GCancellable *cancellable, gbool
 	exchange_mapi_util_mapi_id_from_string (fid, &priv->fid);
 	priv->olFolder = olFolder;
 
+	krb_sso = e_source_get_property (esource, "kerberos");
 	g_mutex_unlock (priv->mutex);
 
 	e_cal_backend_notify_online (backend, TRUE);
 	e_cal_backend_notify_readonly (backend, priv->read_only);
-	e_cal_backend_notify_auth_required (backend, TRUE, NULL);
+
+	if (!krb_sso || !g_str_equal (krb_sso, "required")) {
+		e_cal_backend_notify_auth_required (backend, TRUE, NULL);
+	} else {
+		ecbm_connect_user (backend, cancellable, NULL, perror);
+		e_cal_backend_notify_opened (backend, NULL);
+	}
 }
 
 static void
 ecbm_authenticate_user (ECalBackend *backend, GCancellable *cancellable, ECredentials *credentials, GError **perror)
 {
-	ECalBackendMAPI *cbmapi;
-	ECalBackendMAPIPrivate *priv;
-	ExchangeMapiConnection *old_conn;
-	GError *mapi_error = NULL;
+	const gchar *password;
 
 	g_static_mutex_lock (&auth_mutex);
-
-	cbmapi = E_CAL_BACKEND_MAPI (backend);
-	priv = cbmapi->priv;
-
-	old_conn = priv->conn;
-
-	priv->conn = exchange_mapi_connection_new (priv->profile, e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD), &mapi_error);
-	if (!priv->conn) {
-		priv->conn = exchange_mapi_connection_find (priv->profile);
-		if (priv->conn && !exchange_mapi_connection_connected (priv->conn))
-			exchange_mapi_connection_reconnect (priv->conn, e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD), &mapi_error);
-	}
-
-	if (old_conn)
-		g_object_unref (old_conn);
-
-	if (priv->conn && exchange_mapi_connection_connected (priv->conn)) {
-		/* Success */;
-	} else {
-		mapi_error_to_edc_error (perror, mapi_error, AuthenticationFailed, NULL);
-		if (mapi_error)
-			g_error_free (mapi_error);
-		g_static_mutex_unlock (&auth_mutex);
-		return;
-	}
-
-	if (mapi_error) {
-		mapi_error_to_edc_error (perror, mapi_error, AuthenticationFailed, NULL);
-		g_error_free (mapi_error);
-		g_static_mutex_unlock (&auth_mutex);
-		return;
-	}
-
+	password = e_credentials_peek (credentials, E_CREDENTIALS_KEY_PASSWORD);
 	g_static_mutex_unlock (&auth_mutex);
-
-	ecbm_connect (cbmapi, perror);
+	ecbm_connect_user (backend, cancellable, password, perror);
 }
 
 static gboolean
@@ -2459,7 +2480,9 @@ ecbm_set_online (ECalBackend *backend, gboolean is_online)
 {
 	ECalBackendMAPI *cbmapi;
 	ECalBackendMAPIPrivate *priv;
+	ESource *esource = NULL;
 	gboolean re_open = FALSE;
+	const gchar *krb_sso = NULL;
 
 	cbmapi = E_CAL_BACKEND_MAPI (backend);
 	priv = cbmapi->priv;
@@ -2470,14 +2493,18 @@ ecbm_set_online (ECalBackend *backend, gboolean is_online)
 
 	g_mutex_lock (priv->mutex);
 
+	esource = e_cal_backend_get_source (E_CAL_BACKEND (cbmapi));
+	krb_sso = e_source_get_property (esource, "kerberos");
 	re_open = (e_cal_backend_is_online (backend) ? 1 : 0) < (is_online ? 1 : 0);
 	e_cal_backend_notify_online (backend, is_online);
 
 	priv->mode_changed = TRUE;
 	if (is_online) {
 		priv->read_only = FALSE;
-		if (e_cal_backend_is_opened (backend) && re_open)
+		if (e_cal_backend_is_opened (backend) && re_open
+		    && ! (krb_sso && g_str_equal (krb_sso, "required"))) {
 			e_cal_backend_notify_auth_required (backend, TRUE, NULL);
+		}
 	} else {
 		priv->read_only = TRUE;
 	}
diff --git a/src/camel/camel-mapi-store.c b/src/camel/camel-mapi-store.c
index f0d4f16..b14ecb8 100644
--- a/src/camel/camel-mapi-store.c
+++ b/src/camel/camel-mapi-store.c
@@ -1634,56 +1634,75 @@ mapi_get_name(CamelService *service, gboolean brief)
 }
 
 static gboolean
+mapi_prompt_pass_creds (CamelService *service, CamelURL *url, const gchar *reason, GError **error) {
+	gchar *prompt;
+	guint32 prompt_flags = CAMEL_SESSION_PASSWORD_SECRET;
+	CamelSession *session = camel_service_get_session (service);
+
+	if (reason && *reason) {
+		/* We need to un-cache the password before prompting again */
+		prompt_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
+		g_free (url->passwd);
+		url->passwd = NULL;
+	}
+
+	/*To translators : First %s : is the error text or the reason
+	  for prompting the user if it is available.
+	 Second %s is : Username.
+	 Third %s is : Server host name.*/
+	prompt = g_strdup_printf (_("%s Please enter the MAPI password for %s %s"),
+				  reason, url->user, url->host);
+	url->passwd =
+		camel_session_get_password (session, service,
+					    prompt, "password", prompt_flags, NULL);
+	g_free (prompt);
+
+	if (!url->passwd) {
+		g_set_error (
+			error, G_IO_ERROR,
+			G_IO_ERROR_CANCELLED,
+			_("You did not enter a password."));
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static gboolean
 mapi_auth_loop (CamelService *service, GError **error)
 {
 	CamelMapiStore *store = CAMEL_MAPI_STORE (service);
-	CamelSession *session;
 	CamelURL *url;
+	ExchangeMapiProfileData empd = { 0 };
 
 	gchar *errbuf = NULL;
-	gboolean authenticated = FALSE;
-	guint32 prompt_flags = CAMEL_SESSION_PASSWORD_SECRET;
+	gboolean authenticated = FALSE, ret, krb_requested = FALSE;
 
 	url = camel_service_get_camel_url (service);
-	session = camel_service_get_session (service);
+	exchange_mapi_util_profiledata_from_camelurl (&empd, url);
 
 	url->passwd = NULL;
 
 	while (!authenticated) {
 		GError *mapi_error = NULL;
 
-		if (errbuf) {
-			/* We need to un-cache the password before prompting again */
-			prompt_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
-			g_free (url->passwd);
-			url->passwd = NULL;
-		}
+		if (!url->passwd) {
+			const gchar *why = errbuf ? errbuf : "";
+
+			if (empd.krb_sso) {
+				if (!krb_requested) {
+					ret = exchange_mapi_util_trigger_krb_auth (&empd, error);
+					krb_requested = TRUE;
+				}
+			} else {
+				ret = mapi_prompt_pass_creds (service, url,
+							      why, error);
+			}
 
-		if (!url->passwd ) {
-			gchar *prompt;
-
-			/*To translators : First %s : is the error text or the reason
-			  for prompting the user if it is available.
-			 Second %s is : Username.
-			 Third %s is : Server host name.*/
-			prompt = g_strdup_printf (_("%s Please enter the MAPI password for %s %s"),
-						  errbuf ? errbuf : "",
-						  url->user,
-						  url->host);
-			url->passwd =
-				camel_session_get_password (session, service,
-							    prompt, "password", prompt_flags, NULL);
-			g_free (prompt);
 			g_free (errbuf);
 			errbuf = NULL;
-
-			if (!url->passwd) {
-				g_set_error (
-					error, G_IO_ERROR,
-					G_IO_ERROR_CANCELLED,
-					_("You did not enter a password."));
-				return FALSE;
-			}
+		}
+		if (!ret || (error && *error)) {
+			return FALSE;
 		}
 
 		store->priv->conn = exchange_mapi_connection_new (store->priv->profile, url->passwd, &mapi_error);
diff --git a/src/libexchangemapi/exchange-mapi-connection.c b/src/libexchangemapi/exchange-mapi-connection.c
index 5dc46a7..54515d0 100644
--- a/src/libexchangemapi/exchange-mapi-connection.c
+++ b/src/libexchangemapi/exchange-mapi-connection.c
@@ -43,7 +43,7 @@
 
 static void register_connection (ExchangeMapiConnection *conn);
 static void unregister_connection (ExchangeMapiConnection *conn);
-static gboolean mapi_profile_create (const gchar *username, const gchar *password, const gchar *domain, const gchar *server, guint32 flags, mapi_profile_callback_t callback, gpointer data, GError **perror, gboolean use_locking);
+static gboolean mapi_profile_create (const ExchangeMapiProfileData *empd, mapi_profile_callback_t callback, gconstpointer data, GError **perror, gboolean use_locking);
 static struct mapi_session *mapi_profile_load (const gchar *profname, const gchar *password, GError **perror);
 static void ema_global_lock (void);
 static void ema_global_unlock (void);
@@ -3623,6 +3623,7 @@ static gboolean
 try_create_profile_main_thread_cb (struct tcp_data *data)
 {
 	EAccountList *accounts;
+	ExchangeMapiProfileData empd = { 0 };
 	EIterator *iter;
 	GConfClient *gconf;
 
@@ -3634,17 +3635,17 @@ try_create_profile_main_thread_cb (struct tcp_data *data)
 		EAccount *account = E_ACCOUNT (e_iterator_get (iter));
 		if (account && account->source && account->source->url && g_ascii_strncasecmp (account->source->url, "mapi://", 7) == 0) {
 			CamelURL *url = camel_url_new (e_account_get_string (account, E_ACCOUNT_SOURCE_URL), NULL);
-			const gchar *domain_name;
+			exchange_mapi_util_profiledata_from_camelurl (&empd,
+								      url);
+			/* cast away the const, but promise not to touch it */
+			empd.password = (gchar*)data->password;
 
-			domain_name = camel_url_get_param (url, "domain");
-			if (data->password && *data->password && domain_name && *domain_name && url->user && *url->user && url->host && *url->host) {
-				gchar *profname = exchange_mapi_util_profile_name (url->user, domain_name, url->host, FALSE);
+			if (COMPLETE_PROFILEDATA(&empd)) {
+				gchar *profname = exchange_mapi_util_profile_name (&empd, FALSE);
 
 				if (profname && g_str_equal (profname, data->profname)) {
-					guint32 cp_flags = (camel_url_get_param (url, "ssl") && g_str_equal (camel_url_get_param (url, "ssl"), "1")) ? CREATE_PROFILE_FLAG_USE_SSL : CREATE_PROFILE_FLAG_NONE;
-
 					/* do not use locking here, because when this is called then other thread is holding the lock */
-					data->has_profile = mapi_profile_create (url->user, data->password, domain_name, url->host, cp_flags, NULL, NULL, NULL, FALSE);
+					data->has_profile = mapi_profile_create (&empd, NULL, NULL, NULL, FALSE);
 
 					g_free (profname);
 					camel_url_free (url);
@@ -3673,8 +3674,6 @@ try_create_profile (const gchar *profname, const gchar *password)
 
 	g_return_val_if_fail (profname != NULL, FALSE);
 	g_return_val_if_fail (*profname != 0, FALSE);
-	g_return_val_if_fail (password != NULL, FALSE);
-	g_return_val_if_fail (*password != 0, FALSE);
 
 	data.profname = profname;
 	data.password = password;
@@ -3841,9 +3840,8 @@ create_profile_fallback_callback (struct SRowSet *rowset, gconstpointer data)
 }
 
 static gboolean
-mapi_profile_create (const gchar *username, const gchar *password, const gchar *domain,
-		     const gchar *server, guint32 flags,
-		     mapi_profile_callback_t callback, gpointer data,
+mapi_profile_create (const ExchangeMapiProfileData *empd,
+		     mapi_profile_callback_t callback, gconstpointer data,
 		     GError **perror,
 		     gboolean use_locking)
 {
@@ -3855,17 +3853,18 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 
 	if (!callback) {
 		callback = create_profile_fallback_callback;
-		data = (gpointer) username;
+		data = (gpointer) empd->username;
 	}
 
 	/*We need all the params before proceeding.*/
-	e_return_val_mapi_error_if_fail (username && *username && password && *password &&
-			      domain && *domain && server && *server, MAPI_E_INVALID_PARAMETER, FALSE);
+	e_return_val_mapi_error_if_fail (COMPLETE_PROFILEDATA(empd),
+					 MAPI_E_INVALID_PARAMETER, FALSE);
 
 	if (use_locking)
 		g_static_rec_mutex_lock (&profile_mutex);
 
-	g_debug ("Create profile with %s %s %s\n", username, domain, server);
+	g_debug ("Create profile with %s %s %s\n", empd->username,
+		 empd->domain, empd->server);
 
 	if (!ensure_mapi_init_called (perror)) {
 		if (use_locking)
@@ -3873,14 +3872,14 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 		return FALSE;
 	}
 
-	profname = exchange_mapi_util_profile_name (username, domain, server, TRUE);
+	profname = exchange_mapi_util_profile_name (empd, TRUE);
 
 	/* Delete any existing profiles with the same profilename */
 	ms = DeleteProfile (mapi_ctx, profname);
 	/* don't bother to check error - it would be valid if we got an error */
 
-	ms = CreateProfile (mapi_ctx, profname, username, password,
-			    OC_PROFILE_NOPASSWORD);
+	ms = CreateProfile (mapi_ctx, profname, empd->username,
+			    empd->password, OC_PROFILE_NOPASSWORD);
 	if (ms != MAPI_E_SUCCESS) {
 		make_mapi_error (perror, "CreateProfile", ms);
 		goto cleanup;
@@ -3889,11 +3888,20 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 	#define add_string_attr(_prof,_aname,_val)				\
 		mapi_profile_add_string_attr (mapi_ctx, _prof, _aname, _val)
 
-	add_string_attr (profname, "binding", server);
+	add_string_attr (profname, "binding", empd->server);
 	add_string_attr (profname, "workstation", workstation);
-	add_string_attr (profname, "domain", domain);
 
-	if ((flags & CREATE_PROFILE_FLAG_USE_SSL) != 0)
+	if (empd->krb_sso) {
+		/* note: domain and realm are intentially not added to
+		 *       the libmapi profile in the case of SSO enabled,
+		 *       as it changes the behavior, and breaks SSO support. */
+		add_string_attr (profname, "kerberos", "yes");
+	} else {
+		/* only add domain if !kerberos SSO */
+		add_string_attr (profname, "domain", empd->domain);
+	}
+
+	if (empd->use_ssl)
 		add_string_attr (profname, "seal", "true");
 
 	/* This is only convenient here and should be replaced at some point */
@@ -3905,7 +3913,7 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 
 	/* Login now */
 	g_debug("Logging into the server... ");
-	ms = MapiLogonProvider (mapi_ctx, &session, profname, password,
+	ms = MapiLogonProvider (mapi_ctx, &session, profname, empd->password,
 				PROVIDER_ID_NSPI);
 	if (ms != MAPI_E_SUCCESS) {
 		make_mapi_error (perror, "MapiLogonProvider", ms);
@@ -3915,7 +3923,7 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 	}
 	g_debug("MapiLogonProvider : succeeded \n");
 
-	ms = ProcessNetworkProfile (session, username, callback, data);
+	ms = ProcessNetworkProfile (session, empd->username, callback, data);
 	if (ms != MAPI_E_SUCCESS) {
 		make_mapi_error (perror, "ProcessNetworkProfile", ms);
 		g_debug ("Deleting profile %s ", profname);
@@ -3953,12 +3961,11 @@ mapi_profile_create (const gchar *username, const gchar *password, const gchar *
 }
 
 gboolean
-exchange_mapi_create_profile (const gchar *username, const gchar *password, const gchar *domain,
-			      const gchar *server, guint32 flags,
-			      mapi_profile_callback_t callback, gpointer data,
+exchange_mapi_create_profile (ExchangeMapiProfileData *empd,
+			      mapi_profile_callback_t callback, gconstpointer data,
 			      GError **perror)
 {
-	return mapi_profile_create (username, password, domain, server, flags, callback, data, perror, TRUE);
+	return mapi_profile_create (empd, callback, data, perror, TRUE);
 }
 
 gboolean
diff --git a/src/libexchangemapi/exchange-mapi-connection.h b/src/libexchangemapi/exchange-mapi-connection.h
index d5eee38..9ee9d8a 100644
--- a/src/libexchangemapi/exchange-mapi-connection.h
+++ b/src/libexchangemapi/exchange-mapi-connection.h
@@ -237,14 +237,23 @@ gboolean		exchange_mapi_connection_events_unsubscribe (ExchangeMapiConnection *c
 
 /* profile functions */
 
-enum {
-	CREATE_PROFILE_FLAG_NONE = 0,
-	CREATE_PROFILE_FLAG_USE_SSL = (1 << 0)
-};
-
-gboolean		exchange_mapi_create_profile (const gchar *username, const gchar *password,
-				       const gchar *domain, const gchar *server, guint32 flags,
-				       mapi_profile_callback_t cb, gpointer data, GError **perror);
+typedef struct {
+	const gchar *username;
+	gchar *password;
+	const gchar *domain;
+	const gchar *server;
+	gboolean use_ssl;
+	gboolean krb_sso;
+	const gchar *krb_realm;
+} ExchangeMapiProfileData;
+
+#define COMPLETE_PROFILEDATA(x) \
+	((x)->username && *(x)->username && (x)->server && *(x)->server \
+	 && (((x)->domain && *(x)->domain && (x)->password && *(x)->password) \
+	     || ((x)->krb_sso && (x)->krb_realm && *(x)->krb_realm)))
+
+gboolean		exchange_mapi_create_profile (ExchangeMapiProfileData *profile,
+				       mapi_profile_callback_t cb, gconstpointer data, GError **perror);
 
 gboolean		exchange_mapi_delete_profile (const gchar *profile, GError **perror);
 void			exchange_mapi_rename_profile (const gchar *old_name, const gchar *new_name);
diff --git a/src/libexchangemapi/exchange-mapi-utils.c b/src/libexchangemapi/exchange-mapi-utils.c
index e712c5f..bb9ea69 100644
--- a/src/libexchangemapi/exchange-mapi-utils.c
+++ b/src/libexchangemapi/exchange-mapi-utils.c
@@ -41,6 +41,10 @@
 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
 #endif
 
+/* Used for callout to krb5-auth-dialog */
+#define KRB_DBUS_PATH               "/org/gnome/KrbAuthDialog"
+#define KRB_DBUS_INTERFACE          "org.gnome.KrbAuthDialog"
+
 inline gchar *
 exchange_mapi_util_mapi_id_to_string (mapi_id_t id)
 {
@@ -1020,10 +1024,93 @@ exchange_crlf_to_lf (const gchar *in)
 }
 
 /**
+ * exchange_mapi_util_profiledata_from_camelurl:
+ * @empd: destination for profile settings
+ * @url: CamelURL with account settings
+ *
+ * Sets the members of an ExchangeMapiProfileData instance to
+ * reflect the account settings listed in the corresponding
+ * CamelURL pointer.
+ *
+ * @note: no allocation is done, so do not free the CamelUrl pointer and
+ *        the respective underlying pointers until you no longer need the
+ *        profile data.
+ **/
+void
+exchange_mapi_util_profiledata_from_camelurl (ExchangeMapiProfileData *empd, const CamelURL *url)
+{
+	const gchar *use_ssl = NULL, *use_krb = NULL;
+	CamelURL *promise_its_const = (CamelURL*)url; /* :) */
+	empd->username = promise_its_const->user;
+	empd->server = promise_its_const->host;
+
+	use_ssl = camel_url_get_param (promise_its_const, "ssl");
+	empd->use_ssl = (use_ssl && g_str_equal (use_ssl, "1"));
+	empd->domain = camel_url_get_param (promise_its_const, "domain");
+	use_krb = camel_url_get_param (promise_its_const, "kerberos");
+	empd->krb_sso = (use_krb && g_str_equal (use_krb, "required"));
+	empd->krb_realm = camel_url_get_param (promise_its_const, "realm");
+}
+
+gboolean
+exchange_mapi_util_trigger_krb_auth (const ExchangeMapiProfileData *empd, GError **error) {
+	gint success = FALSE;
+	GError *local_error = NULL;
+	GDBusConnection *connection;
+	GDBusMessage *message, *reply;
+	gchar *name;
+
+	connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &local_error);
+	if (local_error) {
+		g_warning ("could not get system bus: %s\n",
+			   local_error->message);
+		g_propagate_error (error, local_error);
+		return FALSE;
+	}
+
+	g_dbus_connection_set_exit_on_close (connection, FALSE);
+	/* Create a new message on the KRB_DBUS_INTERFACE */
+	message = g_dbus_message_new_method_call (KRB_DBUS_INTERFACE,
+						  KRB_DBUS_PATH,
+						  KRB_DBUS_INTERFACE,
+						  "acquireTgt");
+	if (!message) {
+		g_object_unref (connection);
+		return FALSE;
+	}
+
+	/* Appends the data as an argument to the message */
+	name = g_strdup_printf ("%s %s", empd->username, empd->krb_realm);
+	g_dbus_message_set_body (message, g_variant_new ("(s)", name));
+
+	/* Sends the message: Have a 300 sec wait timeout  */
+	reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 300 * 1000, NULL, NULL, &local_error);
+	g_free (name);
+
+	if (local_error) {
+		g_warning ("%s: %s\n", G_STRFUNC, local_error->message);
+		g_propagate_error (error, local_error);
+	}
+
+	if (reply) {
+		GVariant *body = g_dbus_message_get_body (reply);
+		if (body) {
+			g_variant_get (body, "(b)", &success);
+		}
+		g_object_unref (reply);
+	}
+
+	/* Free the message */
+	g_object_unref (message);
+	g_object_unref (connection);
+
+	return success && !local_error;
+}
+
+
+/**
  * exchange_mapi_util_profile_name:
- * @username: User name of the profile
- * @domain: Domain name of the profile
- * @hostname: Server host name
+ * @empd: profile information used to construct the name
  * @migrate: whether migrate old profile name to a new one
  *
  * Constructs profile name from given parameters and
@@ -1031,18 +1118,20 @@ exchange_crlf_to_lf (const gchar *in)
  * rename old profile name string to a new name, if requested.
  **/
 gchar *
-exchange_mapi_util_profile_name (const gchar *username, const gchar *domain, const gchar *hostname, gboolean migrate)
+exchange_mapi_util_profile_name (const ExchangeMapiProfileData *empd, gboolean migrate)
 {
 	gchar *res;
 
-	res = g_strdup_printf ("%s %s@%s", username, domain, hostname);
+	res = g_strdup_printf ("%s %s@%s", empd->username, empd->domain,
+			       empd->server);
 	res = g_strcanon (res, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789  -", '_');
 
 	if (migrate) {
 		/* expects MAPIInitialize already called! */
 		gchar *old_name;
 
-		old_name = g_strdup_printf ("%s %s", username, domain);
+		old_name = g_strdup_printf ("%s %s", empd->username,
+					    empd->domain);
 		old_name = g_strcanon (old_name, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@", '_');
 
 		exchange_mapi_rename_profile (old_name, res);
diff --git a/src/libexchangemapi/exchange-mapi-utils.h b/src/libexchangemapi/exchange-mapi-utils.h
index 51c32fc..b0ec1c6 100644
--- a/src/libexchangemapi/exchange-mapi-utils.h
+++ b/src/libexchangemapi/exchange-mapi-utils.h
@@ -25,6 +25,7 @@
 #define EXCHANGE_MAPI_UTILS_H 
 
 #include "exchange-mapi-connection.h"
+#include <camel/camel.h>
 
 gchar *  exchange_mapi_util_mapi_id_to_string (mapi_id_t id);
 gboolean exchange_mapi_util_mapi_id_from_string (const gchar *str, mapi_id_t *id);
@@ -58,7 +59,9 @@ gboolean exchange_mapi_util_recip_entryid_decode (ExchangeMapiConnection *conn,
 gchar *exchange_lf_to_crlf (const gchar *in);
 gchar *exchange_crlf_to_lf (const gchar *in);
 
-gchar *exchange_mapi_util_profile_name (const gchar *username, const gchar *domain, const gchar *hostname, gboolean migrate);
+void exchange_mapi_util_profiledata_from_camelurl (ExchangeMapiProfileData *empd, const CamelURL *url);
+gchar *exchange_mapi_util_profile_name (const ExchangeMapiProfileData *empd, gboolean migrate);
+gboolean exchange_mapi_util_trigger_krb_auth (const ExchangeMapiProfileData *empd, GError **error);
 
 gboolean exchange_mapi_utils_add_props_to_props_array (TALLOC_CTX *mem_ctx, struct SPropTagArray *props, const uint32_t *prop_ids, guint prop_ids_n_elems);
 gboolean exchange_mapi_utils_add_named_ids_to_props_array (ExchangeMapiConnection *conn, mapi_id_t fid, TALLOC_CTX *mem_ctx, struct SPropTagArray *props, ResolveNamedIDsData *named_ids_list, guint named_ids_n_elems);



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