summaryrefslogtreecommitdiffstats
path: root/imap
diff options
context:
space:
mode:
authorKevin McCarthy <kevin@8t8.us>2020-06-11 16:11:36 -0700
committerKevin McCarthy <kevin@8t8.us>2020-06-13 14:44:17 -0700
commitc7a872d1eeea39df148396869c1cbbc0fa26552f (patch)
tree0fa80c6f4e6e0a7f8693ee087f18b3870a16b718 /imap
parent5b844328bb7d7fb0357328bed002e7672f9b9e2a (diff)
Add basic XOAUTH2 support.
This still relies on an external script to obtain the resource access token. Since XOAUTH2 should be slowly going away, use the same refresh_commands as with OAUTHBEARER. Unlike OAUTHBEARER, XOAUTH2 must be explicitly added to the $imap/smtp/pop_authenticators list. To keep the shared functions simpler, convert them to use buffers. RFC 7628 indicates that upon authentication failure the clients should be sending an BASE 64 encoded '^a' ("AQ=="), to terminate the SASL session, so change all the handlers to do that and read the following response. The RFC doesn't comment about a line terminator being required, but I assume it is, so add that too.
Diffstat (limited to 'imap')
-rw-r--r--imap/auth.c3
-rw-r--r--imap/auth.h3
-rw-r--r--imap/auth_oauth.c102
-rw-r--r--imap/command.c1
-rw-r--r--imap/imap_private.h1
5 files changed, 72 insertions, 38 deletions
diff --git a/imap/auth.c b/imap/auth.c
index 48909cd8..83c87f01 100644
--- a/imap/auth.c
+++ b/imap/auth.c
@@ -29,7 +29,8 @@
#include "auth.h"
static const imap_auth_t imap_authenticators[] = {
- { imap_auth_oauth, "oauthbearer" },
+ { imap_auth_oauthbearer, "oauthbearer" },
+ { imap_auth_xoauth2, "xoauth2" },
#ifdef USE_SASL
{ imap_auth_sasl, NULL },
#else
diff --git a/imap/auth.h b/imap/auth.h
index 6ae33fc6..f022beae 100644
--- a/imap/auth.h
+++ b/imap/auth.h
@@ -51,6 +51,7 @@ imap_auth_res_t imap_auth_gss (IMAP_DATA* idata, const char* method);
#ifdef USE_SASL
imap_auth_res_t imap_auth_sasl (IMAP_DATA* idata, const char* method);
#endif
-imap_auth_res_t imap_auth_oauth (IMAP_DATA* idata, const char* method);
+imap_auth_res_t imap_auth_oauthbearer (IMAP_DATA* idata, const char* method);
+imap_auth_res_t imap_auth_xoauth2 (IMAP_DATA* idata, const char* method);
#endif /* _IMAP_AUTH_H */
diff --git a/imap/auth_oauth.c b/imap/auth_oauth.c
index a301e166..b7f01e4f 100644
--- a/imap/auth_oauth.c
+++ b/imap/auth_oauth.c
@@ -28,58 +28,88 @@
#include "auth.h"
/* imap_auth_oauth: AUTH=OAUTHBEARER support. See RFC 7628 */
-imap_auth_res_t imap_auth_oauth (IMAP_DATA* idata, const char* method)
+static imap_auth_res_t imap_auth_oauth (IMAP_DATA* idata, int xoauth2)
{
- char* ibuf = NULL;
- char* oauthbearer = NULL;
- int ilen;
- int rc;
+ int rc = IMAP_AUTH_FAILURE, steprc;
+ BUFFER *bearertoken = NULL, *authline = NULL;
+ const char *authtype;
- /* For now, we only support SASL_IR also and over TLS */
- if (!mutt_bit_isset (idata->capabilities, AUTH_OAUTHBEARER) ||
- !mutt_bit_isset (idata->capabilities, SASL_IR) ||
- !idata->conn->ssf)
- return IMAP_AUTH_UNAVAIL;
+ authtype = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
- /* If they did not explicitly request or configure oauth then fail quietly */
- if (!(method || ImapOauthRefreshCmd))
- return IMAP_AUTH_UNAVAIL;
+ mutt_message (_("Authenticating (%s)..."), authtype);
- mutt_message _("Authenticating (OAUTHBEARER)...");
+ bearertoken = mutt_buffer_pool_get ();
+ authline = mutt_buffer_pool_get ();
/* We get the access token from the imap_oauth_refresh_command */
- oauthbearer = mutt_account_getoauthbearer (&idata->conn->account);
- if (oauthbearer == NULL)
- return IMAP_AUTH_FAILURE;
+ if (mutt_account_getoauthbearer (&idata->conn->account, bearertoken, xoauth2))
+ goto cleanup;
- ilen = strlen (oauthbearer) + 30;
- ibuf = safe_malloc (ilen);
- snprintf (ibuf, ilen, "AUTHENTICATE OAUTHBEARER %s", oauthbearer);
+ mutt_buffer_printf (authline, "AUTHENTICATE %s %s",
+ authtype, mutt_b2s (bearertoken));
/* This doesn't really contain a password, but the token is good for
* an hour, so suppress it anyways.
*/
- rc = imap_exec (idata, ibuf, IMAP_CMD_FAIL_OK | IMAP_CMD_PASS);
-
- FREE (&oauthbearer);
- FREE (&ibuf);
-
- if (rc)
+ if (imap_exec (idata, mutt_b2s (authline), IMAP_CMD_FAIL_OK | IMAP_CMD_PASS))
{
/* The error response was in SASL continuation, so continue the SASL
* to cause a failure and exit SASL input. See RFC 7628 3.2.3
+ * "AQ==" is Base64 encoded ^A (0x01) .
*/
- mutt_socket_write (idata->conn, "\001");
- rc = imap_exec (idata, ibuf, IMAP_CMD_FAIL_OK);
- }
+ mutt_socket_write (idata->conn, "AQ==\r\n");
+ do
+ steprc = imap_cmd_step (idata);
+ while (steprc == IMAP_CMD_CONTINUE);
- if (!rc)
- {
- mutt_clear_error();
- return IMAP_AUTH_SUCCESS;
+ /* L10N:
+ %s is the authentication type, for example OAUTHBEARER
+ */
+ mutt_error (_("%s authentication failed."), authtype);
+ mutt_sleep (2);
+ goto cleanup;
}
- mutt_error _("OAUTHBEARER authentication failed.");
- mutt_sleep (2);
- return IMAP_AUTH_FAILURE;
+ mutt_clear_error();
+ rc = IMAP_AUTH_SUCCESS;
+
+cleanup:
+ mutt_buffer_pool_release (&bearertoken);
+ mutt_buffer_pool_release (&authline);
+ return rc;
+}
+
+/* AUTH=OAUTHBEARER support. See RFC 7628 */
+imap_auth_res_t imap_auth_oauthbearer (IMAP_DATA* idata, const char* method)
+{
+ /* For now, we only support SASL_IR also and over TLS */
+ if (!mutt_bit_isset (idata->capabilities, SASL_IR) ||
+ !idata->conn->ssf)
+ return IMAP_AUTH_UNAVAIL;
+
+ if (!mutt_bit_isset (idata->capabilities, AUTH_OAUTHBEARER))
+ return IMAP_AUTH_UNAVAIL;
+
+ if (!ImapOauthRefreshCmd)
+ return IMAP_AUTH_UNAVAIL;
+
+ return imap_auth_oauth (idata, 0);
+}
+
+/* AUTH=XOAUTH2 support. */
+imap_auth_res_t imap_auth_xoauth2 (IMAP_DATA* idata, const char* method)
+{
+ /* For now, we only support SASL_IR also and over TLS */
+ if (!mutt_bit_isset (idata->capabilities, SASL_IR) ||
+ !idata->conn->ssf)
+ return IMAP_AUTH_UNAVAIL;
+
+ if (!mutt_bit_isset (idata->capabilities, AUTH_XOAUTH2))
+ return IMAP_AUTH_UNAVAIL;
+
+ /* If they did not explicitly request XOAUTH2 then fail quietly */
+ if (!(method && ImapOauthRefreshCmd))
+ return IMAP_AUTH_UNAVAIL;
+
+ return imap_auth_oauth (idata, 1);
}
diff --git a/imap/command.c b/imap/command.c
index 239eb1fc..1cf02257 100644
--- a/imap/command.c
+++ b/imap/command.c
@@ -66,6 +66,7 @@ static const char * const Capabilities[] = {
"AUTH=GSSAPI",
"AUTH=ANONYMOUS",
"AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
"STARTTLS",
"LOGINDISABLED",
"IDLE",
diff --git a/imap/imap_private.h b/imap/imap_private.h
index eb073efc..42078349 100644
--- a/imap/imap_private.h
+++ b/imap/imap_private.h
@@ -113,6 +113,7 @@ enum
AGSSAPI, /* RFC 1731: GSSAPI authentication */
AUTH_ANON, /* AUTH=ANONYMOUS */
AUTH_OAUTHBEARER, /* RFC 7628: AUTH=OAUTHBEARER */
+ AUTH_XOAUTH2, /* Deprecated precursor to OAUTHBEARER */
STARTTLS, /* RFC 2595: STARTTLS */
LOGINDISABLED, /* LOGINDISABLED */
IDLE, /* RFC 2177: IDLE */