[Evolution-hackers] [PATCH 1/3] Add camel_sasl_try_empty_password_sync() method.



This indicates that a SASL method with the need_password flag can be tried
without providing a password, for single-sign-on using system credentials.

This will be used by NTLM.
---
 camel/camel-sasl.c                          |   25 ++++++++++
 camel/camel-sasl.h                          |    6 +++
 camel/providers/imap/camel-imap-store.c     |   51 +++++++++++++++------
 camel/providers/imapx/camel-imapx-server.c  |   31 +++++++++++--
 camel/providers/smtp/camel-smtp-transport.c |   65 ++++++++++++++-------------
 5 files changed, 128 insertions(+), 50 deletions(-)

diff --git a/camel/camel-sasl.c b/camel/camel-sasl.c
index 02006c6..58a4a2b 100644
--- a/camel/camel-sasl.c
+++ b/camel/camel-sasl.c
@@ -419,6 +419,31 @@ camel_sasl_get_authenticated (CamelSasl *sasl)
 }
 
 /**
+ * camel_sasl_try_empty_password_sync:
+ * @sasl: a #CamelSasl object
+ *
+ * Returns: whether or not @sasl can attempt to authenticate without a
+ * password being provided by the caller. This will be %TRUE for an
+ * authentication method which can attempt to use single-sign-on
+ * credentials, but which can fall back to using a provided password
+ * so it still has the @need_password flag set in its description.
+ **/
+gboolean
+camel_sasl_try_empty_password_sync (CamelSasl *sasl, GCancellable *cancellable)
+{
+	CamelSaslClass *class;
+
+	g_return_val_if_fail (CAMEL_IS_SASL (sasl), FALSE);
+
+	class = CAMEL_SASL_GET_CLASS (sasl);
+
+	if (class->try_empty_password_sync == NULL)
+		return FALSE;
+
+	return class->try_empty_password_sync (sasl, cancellable);
+}
+
+/**
  * camel_sasl_set_authenticated:
  * @sasl: a #CamelSasl
  * @authenticated: whether we have successfully authenticated
diff --git a/camel/camel-sasl.h b/camel/camel-sasl.h
index 3bb3a84..eede608 100644
--- a/camel/camel-sasl.h
+++ b/camel/camel-sasl.h
@@ -64,6 +64,9 @@ struct _CamelSaslClass {
 	CamelObjectClass parent_class;
 
 	/* Synchronous I/O Methods */
+	gboolean	(*try_empty_password_sync)
+						(CamelSasl *sasl,
+						 GCancellable *cancellable);
 	GByteArray *	(*challenge_sync)	(CamelSasl *sasl,
 						 GByteArray *token,
 						 GCancellable *cancellable,
@@ -85,6 +88,9 @@ GType		camel_sasl_get_type		(void);
 CamelSasl *	camel_sasl_new			(const gchar *service_name,
 						 const gchar *mechanism,
 						 CamelService *service);
+gboolean	camel_sasl_try_empty_password_sync
+						(CamelSasl *sasl,
+						 GCancellable *cancellable);
 gboolean	camel_sasl_get_authenticated	(CamelSasl *sasl);
 void		camel_sasl_set_authenticated	(CamelSasl *sasl,
 						 gboolean authenticated);
diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c
index 521a185..9fa53ee 100644
--- a/camel/providers/imap/camel-imap-store.c
+++ b/camel/providers/imap/camel-imap-store.c
@@ -653,20 +653,23 @@ connect_to_server_wrapper (CamelService *service,
 
 static gboolean
 try_auth (CamelImapStore *store,
-          const gchar *mech,
+          CamelSasl *sasl,
           GCancellable *cancellable,
           GError **error)
 {
-	CamelSasl *sasl;
+	CamelService *service = CAMEL_SERVICE (store);
 	CamelImapResponse *response;
 	gchar *resp;
 	gchar *sasl_resp;
 
-	response = camel_imap_command (store, NULL, cancellable, error, "AUTHENTICATE %s", mech);
-	if (!response)
+	response = camel_imap_command (store, NULL, cancellable, error,
+				       "AUTHENTICATE %s",
+				       service->url->authmech);
+	if (!response) {
+		g_object_unref (sasl);
 		return FALSE;
+	}
 
-	sasl = camel_sasl_new ("imap", mech, CAMEL_SERVICE (store));
 	while (!camel_sasl_get_authenticated (sasl)) {
 		resp = camel_imap_response_extract_continuation (store, response, error);
 		if (!resp)
@@ -718,6 +721,7 @@ imap_auth_loop (CamelService *service,
 	CamelSession *session = camel_service_get_session (service);
 	CamelServiceAuthType *authtype = NULL;
 	CamelImapResponse *response;
+	CamelSasl *sasl = NULL;
 	gchar *errbuf = NULL;
 	gboolean authenticated = FALSE;
 	const gchar *auth_domain;
@@ -754,12 +758,22 @@ imap_auth_loop (CamelService *service,
 			return FALSE;
 		}
 
-		if (!authtype->need_password) {
-			authenticated = try_auth (
-				store, authtype->authproto,
-				cancellable, error);
-			if (!authenticated)
+		sasl = camel_sasl_new ("imap", service->url->authmech,
+				       CAMEL_SERVICE (store));
+		if (!sasl) {
+		nosasl:
+			g_set_error (
+				     error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+				     _("Error creating SASL authentication object."));
+			return FALSE;
+		}
+
+		if (!authtype->need_password ||
+		    camel_sasl_try_empty_password_sync (sasl, cancellable)) {
+			authenticated = try_auth (store, sasl, cancellable, error);
+			if (!authenticated && !authtype->need_password)
 				return FALSE;
+			sasl = NULL;
 		}
 	}
 
@@ -799,6 +813,8 @@ imap_auth_loop (CamelService *service,
 					error, G_IO_ERROR,
 					G_IO_ERROR_CANCELLED,
 					_("You did not enter a password."));
+				if (sasl)
+					g_object_unref (sasl);
 				return FALSE;
 			}
 		}
@@ -811,11 +827,16 @@ imap_auth_loop (CamelService *service,
 				return FALSE;
 		}
 
-		if (authtype)
-			authenticated = try_auth (
-				store, authtype->authproto,
-				cancellable, &local_error);
-		else {
+		if (authtype) {
+			if (!sasl)
+				sasl = camel_sasl_new ("imap", service->url->authmech,
+						       CAMEL_SERVICE (store));
+			if (!sasl)
+				goto nosasl;
+			authenticated = try_auth (store, sasl, cancellable,
+						  &local_error);
+			sasl = NULL;
+		} else {
 			response = camel_imap_command (store, NULL, cancellable, &local_error,
 						       "LOGIN %S %S",
 						       service->url->user,
diff --git a/camel/providers/imapx/camel-imapx-server.c b/camel/providers/imapx/camel-imapx-server.c
index 2c73fb9..08a6e4d 100644
--- a/camel/providers/imapx/camel-imapx-server.c
+++ b/camel/providers/imapx/camel-imapx-server.c
@@ -3010,7 +3010,6 @@ imapx_reconnect (CamelIMAPXServer *is,
                  GCancellable *cancellable,
                  GError **error)
 {
-	CamelSasl *sasl;
 	CamelIMAPXCommand *ic;
 	gchar *errbuf = NULL;
 	CamelService *service = (CamelService *) is->store;
@@ -3018,9 +3017,17 @@ imapx_reconnect (CamelIMAPXServer *is,
 	gboolean authenticated = FALSE;
 	CamelServiceAuthType *authtype = NULL;
 	guint32 prompt_flags = CAMEL_SESSION_PASSWORD_SECRET;
+	gboolean need_password = FALSE;
 
 	while (!authenticated) {
-		if (errbuf) {
+		CamelSasl *sasl = NULL;
+
+		if (authtype && authtype->need_password && !need_password) {
+			/* We tried an empty password, but it didn't work */
+			need_password = TRUE;
+			g_free (errbuf);
+			errbuf = NULL;
+		} else if (errbuf) {
 			/* We need to un-cache the password before prompting again */
 			prompt_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
 			g_free (service->url->passwd);
@@ -3047,6 +3054,7 @@ imapx_reconnect (CamelIMAPXServer *is,
 
 			authtype = camel_sasl_authtype (service->url->authmech);
 			if (!authtype) {
+			noauth:
 				g_set_error (
 					error, CAMEL_SERVICE_ERROR,
 					CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
@@ -3056,7 +3064,20 @@ imapx_reconnect (CamelIMAPXServer *is,
 			}
 		}
 
-		if (service->url->passwd == NULL && (!authtype || authtype->need_password)) {
+		if (authtype) {
+			sasl = camel_sasl_new ("imap", authtype->authproto, service);
+			if (!sasl)
+				goto noauth;
+
+			/* If this is the *first* attempt, set 'need_password'
+			   as appropriate. */
+			if (!need_password)
+				need_password = authtype->need_password &&
+					!camel_sasl_try_empty_password_sync (sasl, cancellable);
+		} else
+			need_password = TRUE;
+
+		if (need_password && service->url->passwd == NULL) {
 			gchar *base_prompt;
 			gchar *full_prompt;
 
@@ -3083,10 +3104,12 @@ imapx_reconnect (CamelIMAPXServer *is,
 					error, G_IO_ERROR,
 					G_IO_ERROR_CANCELLED,
 					_("You did not enter a password."));
+				if (sasl)
+					g_object_unref (sasl);
 				goto exception;
 			}
 		}
-		if (authtype && (sasl = camel_sasl_new ("imap", authtype->authproto, service))) {
+		if (sasl) {
 			ic = camel_imapx_command_new (
 				is, "AUTHENTICATE", NULL, cancellable,
 				"AUTHENTICATE %A", sasl);
diff --git a/camel/providers/smtp/camel-smtp-transport.c b/camel/providers/smtp/camel-smtp-transport.c
index 27a04e2..fe05241 100644
--- a/camel/providers/smtp/camel-smtp-transport.c
+++ b/camel/providers/smtp/camel-smtp-transport.c
@@ -60,7 +60,7 @@ static gboolean		smtp_helo		(CamelSmtpTransport *transport,
 						 GCancellable *cancellable,
 						 GError **error);
 static gboolean		smtp_auth		(CamelSmtpTransport *transport,
-						 const gchar *mech,
+						 CamelSasl *sasl,
 						 GCancellable *cancellable,
 						 GError **error);
 static gboolean		smtp_mail		(CamelSmtpTransport *transport,
@@ -368,6 +368,7 @@ smtp_connect_sync (CamelService *service,
                    GError **error)
 {
 	CamelSmtpTransport *transport = CAMEL_SMTP_TRANSPORT (service);
+	CamelSasl *sasl = NULL;
 	gboolean has_authtypes;
 
 	/* We (probably) need to check popb4smtp before we connect ... */
@@ -423,13 +424,23 @@ smtp_connect_sync (CamelService *service,
 			return FALSE;
 		}
 
-		if (!authtype->need_password) {
-			/* authentication mechanism doesn't need a password,
-			   so if it fails there's nothing we can do */
-			authenticated = smtp_auth (
-				transport, authtype->authproto,
-				cancellable, error);
-			if (!authenticated) {
+		sasl = camel_sasl_new ("smtp", service->url->authmech,
+				       CAMEL_SERVICE (transport));
+
+		if (!sasl) {
+		nosasl:
+			g_set_error (
+				     error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
+				     _("Error creating SASL authentication object."));
+			camel_service_disconnect_sync (service, TRUE, NULL);
+			return FALSE;
+		}
+		if (!authtype->need_password ||
+		    camel_sasl_try_empty_password_sync (sasl, cancellable)) {
+			authenticated = smtp_auth (transport, sasl, cancellable, error);
+			if (!authenticated && !authtype->need_password) {
+				/* authentication mechanism doesn't need a password,
+				   so if it fails there's nothing we can do */
 				camel_service_disconnect_sync (
 					service, TRUE, NULL);
 				return FALSE;
@@ -444,6 +455,7 @@ smtp_connect_sync (CamelService *service,
 
 			if (errbuf) {
 				/* We need to un-cache the password before prompting again */
+				password_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
 				g_free (service->url->passwd);
 				service->url->passwd = NULL;
 			}
@@ -475,10 +487,14 @@ smtp_connect_sync (CamelService *service,
 					return FALSE;
 				}
 			}
-
-			authenticated = smtp_auth (
-				transport, authtype->authproto,
-				cancellable, &local_error);
+			if (!sasl)
+				sasl = camel_sasl_new ("smtp", service->url->authmech,
+						       CAMEL_SERVICE (transport));
+			if (!sasl)
+				goto nosasl;
+
+			authenticated = smtp_auth (transport, sasl, cancellable, &local_error);
+			sasl = NULL;
 			if (!authenticated) {
 				if (g_cancellable_is_cancelled (cancellable) ||
 				    g_error_matches (local_error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE)) {
@@ -501,11 +517,6 @@ smtp_connect_sync (CamelService *service,
 				service->url->passwd = NULL;
 			}
 
-			/* Force a password prompt on the next pass, in
-			 * case we have an invalid password cached.  This
-			 * avoids repeated authentication attempts using
-			 * the same invalid password. */
-			password_flags |= CAMEL_SESSION_PASSWORD_REPROMPT;
 		}
 	}
 
@@ -1136,36 +1147,28 @@ smtp_helo (CamelSmtpTransport *transport,
 
 static gboolean
 smtp_auth (CamelSmtpTransport *transport,
-           const gchar *mech,
+           CamelSasl *sasl,
            GCancellable *cancellable,
            GError **error)
 {
 	CamelService *service;
 	gchar *cmdbuf, *respbuf = NULL, *challenge;
 	gboolean auth_challenge = FALSE;
-	CamelSasl *sasl = NULL;
 
 	service = CAMEL_SERVICE (transport);
 
 	camel_operation_push_message (cancellable, _("SMTP Authentication"));
 
-	sasl = camel_sasl_new ("smtp", mech, service);
-	if (!sasl) {
-		camel_operation_pop_message (cancellable);
-		g_set_error (
-			error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
-			_("Error creating SASL authentication object."));
-		return FALSE;
-	}
-
 	challenge = camel_sasl_challenge_base64_sync (
 		sasl, NULL, cancellable, error);
 	if (challenge) {
 		auth_challenge = TRUE;
-		cmdbuf = g_strdup_printf ("AUTH %s %s\r\n", mech, challenge);
+		cmdbuf = g_strdup_printf ("AUTH %s %s\r\n",
+					  service->url->authmech, challenge);
 		g_free (challenge);
 	} else {
-		cmdbuf = g_strdup_printf ("AUTH %s\r\n", mech);
+		cmdbuf = g_strdup_printf ("AUTH %s\r\n",
+					  service->url->authmech);
 	}
 
 	d(fprintf (stderr, "sending : %s", cmdbuf));
@@ -1203,7 +1206,7 @@ smtp_auth (CamelSmtpTransport *transport,
 				   "authentication mechanism is broken. Please report this to the\n"
 				   "appropriate vendor and suggest that they re-read rfc2554 again\n"
 				   "for the first time (specifically Section 4).\n",
-				   mech));
+				   service->url->authmech));
 		}
 
 		/* eat whtspc */
-- 
1.7.4





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