Commit d198d015 authored by Antonio Radici's avatar Antonio Radici

+ added upstream patches for OAUTHBEARER support by Brandon Long (Closes: #905551).

parent 83336850
mutt (1.10.1-2) unstable; urgency=low
[ Jonathan Nieder ]
* debian/patches:
+ added upstream patches for OAUTHBEARER support by Brandon Long
(Closes: #905551).
+ upstream/905551-oauthbearer-imap.patch
+ upstream/905551-oauthbearer-smtp.patch
+ upstream/905551-oauthbearer-refresh.patch
[ Antonio Radici ]
* New release to include the patch above.
-- Antonio Radici <antonio@debian.org> Tue, 07 Aug 2018 09:31:52 +0100
mutt (1.10.1-1) unstable; urgency=medium
* New upstream release.
......
......@@ -9,3 +9,6 @@ debian-specific/828751-pinentry-gpg2-support.patch
misc/gpg.rc-paths.patch
misc/smime.rc.patch
upstream/528233-readonly-open.patch
upstream/905551-oauthbearer-imap.patch
upstream/905551-oauthbearer-smtp.patch
upstream/905551-oauthbearer-refresh.patch
From: Brandon Long <blong@fiction.net>
Date: Mon, 11 Jun 2018 10:39:49 -0700
Subject: Initial support for OAUTHBEARER for IMAP.
commit 798f749eeeb98ed04028521a2eb3e505c1a83574 upstream.
Gmail supports RFC 7628 for using OAUTH with IMAP, and they really don't
like you using password based auth. You can still enable "less secure
apps" and then generate an application specific password, but I figured it
was time to support it.
Being mutt, I punted on some of the "hard" work to an external script, ie
getting/refreshing the OAUTH tokens. This avoids the issue of how do you
have a client-id and client-secret for an open source project, and the fact
that OAUTH discovery is still nascent, so you'd likely need separate things
for each of the providers.
At least for Gmail, you can use the oauth2.py script from Google's
gmail-oauth2-tools:
https://github.com/google/gmail-oauth2-tools/blob/master/python/oauth2.py
You'd need to get your own oauth client credentials for Gmail here:
https://console.developers.google.com/apis/credentials
Then, you'd use oauth2.py with --generate_oauth2_token to get a refresh
token, and configure mutt with:
set imap_authenticators="oauthbearer"
set imap_user="<email_address>"
set imap_pass=`/path/to/oauth2.py --quiet --user=<email_address>
--client_id=<client_id> --client_secret=<client_secret>
--refresh_token=<refresh_token>`
For this patch, I didn't add any new configuration, but I'm open to
suggestions on that.
The patch also only support SASL-IR to reduce round-trips to the server,
but it's certainly possible to change that if we think there are
OAUTHBEARER IMAP servers that don't support SASL-IR. It also requires the
connection to be encrypted as the access token is re-usable for an hour or
so. Again, Gmail only allows encrypted IMAP connections, not sure if any
OAUTHBEARER services allow non-encrypted.
Turns out that auth failure leaves you in SASL mode, so I have a hack to
issue a noop command on error. Not sure if that's just OAUTHBEARER
oddness, or whether I should be using lower level mutt imap functions.
---
imap/Makefile.am | 7 +--
imap/auth.c | 1 +
imap/auth.h | 1 +
imap/auth_oauth.c | 104 ++++++++++++++++++++++++++++++++++++++++++++
imap/command.c | 1 +
imap/imap_private.h | 1 +
6 files changed, 112 insertions(+), 3 deletions(-)
create mode 100644 imap/auth_oauth.c
diff --git a/imap/Makefile.am b/imap/Makefile.am
index 527b044f..199f6d6b 100644
--- a/imap/Makefile.am
+++ b/imap/Makefile.am
@@ -13,12 +13,13 @@ else
AUTHENTICATORS = auth_anon.c auth_cram.c
endif
-EXTRA_DIST = README TODO auth_anon.c auth_cram.c auth_gss.c auth_sasl.c
+EXTRA_DIST = README TODO auth_anon.c auth_cram.c auth_gss.c auth_oauth.c \
+ auth_sasl.c
AM_CPPFLAGS = -I$(top_srcdir) -I../intl
noinst_LIBRARIES = libimap.a
noinst_HEADERS = auth.h imap_private.h message.h
-libimap_a_SOURCES = auth.c auth_login.c browse.c command.c imap.c imap.h \
- message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES)
+libimap_a_SOURCES = auth.c auth_login.c auth_oauth.c browse.c command.c \
+ imap.c imap.h message.c utf7.c util.c $(AUTHENTICATORS) $(GSSSOURCES)
diff --git a/imap/auth.c b/imap/auth.c
index 047531a5..1b26077a 100644
--- a/imap/auth.c
+++ b/imap/auth.c
@@ -42,6 +42,7 @@ static const imap_auth_t imap_authenticators[] = {
{ imap_auth_cram_md5, "cram-md5" },
#endif
{ imap_auth_login, "login" },
+ { imap_auth_oauth, "oauthbearer" },
{ NULL, NULL }
};
diff --git a/imap/auth.h b/imap/auth.h
index 63107947..82ef2f4c 100644
--- a/imap/auth.h
+++ b/imap/auth.h
@@ -51,5 +51,6 @@ 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);
#endif /* _IMAP_AUTH_H */
diff --git a/imap/auth_oauth.c b/imap/auth_oauth.c
new file mode 100644
index 00000000..0bb5d2c2
--- /dev/null
+++ b/imap/auth_oauth.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 1999-2001,2005 Brendan Cully <brendan@kublai.com>
+ * Copyright (C) 2018 Brandon Long <blong@fiction.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/* IMAP login/authentication code */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "mutt.h"
+#include "imap_private.h"
+#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)
+{
+ char* ibuf = NULL;
+ char* oauth_buf = NULL;
+ int len, ilen, oalen;
+ int rc;
+
+ /* 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;
+
+ mutt_message _("Authenticating (OAUTHBEARER)...");
+
+ /* get auth info */
+ if (mutt_account_getlogin (&idata->conn->account))
+ return IMAP_AUTH_FAILURE;
+
+ /* We get the access token from the "imap_pass" field */
+ if (mutt_account_getpass (&idata->conn->account))
+ return IMAP_AUTH_FAILURE;
+
+ /* Determine the length of the keyed message digest, add 50 for
+ * overhead.
+ */
+ oalen = strlen (idata->conn->account.user) +
+ strlen (idata->conn->account.host) +
+ strlen (idata->conn->account.pass) + 50;
+ oauth_buf = safe_malloc (oalen);
+
+ snprintf (oauth_buf, oalen,
+ "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001",
+ idata->conn->account.user, idata->conn->account.host,
+ idata->conn->account.port, idata->conn->account.pass);
+
+ /* ibuf must be long enough to store the base64 encoding of
+ * oauth_buf, plus the additional debris.
+ */
+
+ ilen = strlen (oauth_buf) * 2 + 30;
+ ibuf = safe_malloc (ilen);
+ ibuf[0] = '\0';
+
+ safe_strcat (ibuf, ilen, "AUTHENTICATE OAUTHBEARER ");
+ len = strlen(ibuf);
+
+ mutt_to_base64 ((unsigned char*) (ibuf + len),
+ (unsigned char*) oauth_buf, strlen (oauth_buf),
+ ilen - len);
+
+ /* 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 (&oauth_buf);
+ FREE (&ibuf);
+
+ if (!rc)
+ {
+ mutt_clear_error();
+ return IMAP_AUTH_SUCCESS;
+ }
+
+ /* The error response was in SASL continuation, so "continue" the SASL
+ * to cause a failure and exit SASL input.
+ */
+ mutt_socket_write (idata->conn, "an noop\r\n");
+
+ mutt_error _("OAUTHBEARER authentication failed.");
+ mutt_sleep (2);
+ return IMAP_AUTH_FAILURE;
+}
diff --git a/imap/command.c b/imap/command.c
index c8825981..0d8fcc8b 100644
--- a/imap/command.c
+++ b/imap/command.c
@@ -64,6 +64,7 @@ static const char * const Capabilities[] = {
"AUTH=CRAM-MD5",
"AUTH=GSSAPI",
"AUTH=ANONYMOUS",
+ "AUTH=OAUTHBEARER",
"STARTTLS",
"LOGINDISABLED",
"IDLE",
diff --git a/imap/imap_private.h b/imap/imap_private.h
index 312fbfe4..d4337cbf 100644
--- a/imap/imap_private.h
+++ b/imap/imap_private.h
@@ -112,6 +112,7 @@ enum
ACRAM_MD5, /* RFC 2195: CRAM-MD5 authentication */
AGSSAPI, /* RFC 1731: GSSAPI authentication */
AUTH_ANON, /* AUTH=ANONYMOUS */
+ AUTH_OAUTHBEARER, /* RFC 7628: AUTH=OAUTHBEARER */
STARTTLS, /* RFC 2595: STARTTLS */
LOGINDISABLED, /* LOGINDISABLED */
IDLE, /* RFC 2177: IDLE */
--
2.18.0.597.ga71716f1ad
This diff is collapsed.
From: Brandon Long <blong@fiction.net>
Date: Tue, 12 Jun 2018 14:11:47 -0700
Subject: Support for using OAUTHBEARER for smtp.
commit fcd333986c0d15dec67870b7b74fef0e00e8c28b upstream.
This also means a bunch of smtp auth stuff is now compiled in by
default (with --enable-smtp) without having sasl
---
init.h | 2 --
smtp.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 88 insertions(+), 15 deletions(-)
diff --git a/init.h b/init.h
index 44d7fabd..983fde4b 100644
--- a/init.h
+++ b/init.h
@@ -3333,7 +3333,6 @@ struct option_t MuttVars[] = {
** (S/MIME only)
*/
#ifdef USE_SMTP
-# ifdef USE_SASL
{ "smtp_authenticators", DT_STR, R_NONE, UL &SmtpAuthenticators, UL 0 },
/*
** .pp
@@ -3350,7 +3349,6 @@ struct option_t MuttVars[] = {
** set smtp_authenticators="digest-md5:cram-md5"
** .te
*/
-# endif /* USE_SASL */
{ "smtp_pass", DT_STR, R_NONE, UL &SmtpPass, UL 0 },
/*
** .pp
diff --git a/smtp.c b/smtp.c
index 1d147a5a..0948af0a 100644
--- a/smtp.c
+++ b/smtp.c
@@ -66,8 +66,9 @@ enum {
CAPMAX
};
-#ifdef USE_SASL
static int smtp_auth (CONNECTION* conn);
+static int smtp_auth_oauth (CONNECTION* conn);
+#ifdef USE_SASL
static int smtp_auth_sasl (CONNECTION* conn, const char* mechanisms);
#endif
@@ -495,19 +496,12 @@ static int smtp_open (CONNECTION* conn)
return -1;
}
-#ifdef USE_SASL
return smtp_auth (conn);
-#else
- mutt_error (_("SMTP authentication requires SASL"));
- mutt_sleep (1);
- return -1;
-#endif /* USE_SASL */
}
return 0;
}
-#ifdef USE_SASL
static int smtp_auth (CONNECTION* conn)
{
int r = SMTP_AUTH_UNAVAIL;
@@ -528,21 +522,41 @@ static int smtp_auth (CONNECTION* conn)
dprint (2, (debugfile, "smtp_authenticate: Trying method %s\n", method));
- r = smtp_auth_sasl (conn, method);
-
+ if (!strcmp (method, "oauthbearer"))
+ {
+ r = smtp_auth_oauth (conn);
+ }
+ else
+ {
+#ifdef USE_SASL
+ r = smtp_auth_sasl (conn, method);
+#else
+ mutt_error (_("SMTP authentication method %s requires SASL"), method);
+ mutt_sleep (1);
+ continue;
+#endif
+ }
if (r == SMTP_AUTH_FAIL && delim)
{
- mutt_error (_("%s authentication failed, trying next method"), method);
- mutt_sleep (1);
+ mutt_error (_("%s authentication failed, trying next method"), method);
+ mutt_sleep (1);
}
else if (r != SMTP_AUTH_UNAVAIL)
- break;
+ break;
}
FREE (&methods);
}
else
+ {
+#ifdef USE_SASL
r = smtp_auth_sasl (conn, AuthMechs);
+#else
+ mutt_error (_("SMTP authentication requires SASL"));
+ mutt_sleep (1);
+ r = SMTP_AUTH_UNAVAIL;
+#endif
+ }
if (r != SMTP_AUTH_SUCCESS)
mutt_account_unsetpass (&conn->account);
@@ -561,6 +575,7 @@ static int smtp_auth (CONNECTION* conn)
return r == SMTP_AUTH_SUCCESS ? 0 : -1;
}
+#ifdef USE_SASL
static int smtp_auth_sasl (CONNECTION* conn, const char* mechlist)
{
sasl_conn_t* saslconn;
@@ -663,3 +678,63 @@ fail:
return SMTP_AUTH_FAIL;
}
#endif /* USE_SASL */
+
+
+/* smtp_auth_oauth: AUTH=OAUTHBEARER support. See RFC 7628 */
+static int smtp_auth_oauth (CONNECTION* conn)
+{
+ char* ibuf = NULL;
+ char* oauth_buf = NULL;
+ int len, ilen, oalen;
+ int rc;
+
+ mutt_message _("Authenticating (OAUTHBEARER)...");
+
+ /* get auth info */
+ if (mutt_account_getlogin (&conn->account))
+ return SMTP_AUTH_FAIL;
+
+ /* We get the access token from the "smtp_pass" field */
+ if (mutt_account_getpass (&conn->account))
+ return SMTP_AUTH_FAIL;
+
+ /* Determine the length of the keyed message digest, add 50 for
+ * overhead.
+ */
+ oalen = strlen (conn->account.user) +
+ strlen (conn->account.host) +
+ strlen (conn->account.pass) + 50;
+ oauth_buf = safe_malloc (oalen);
+
+ snprintf (oauth_buf, oalen,
+ "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001",
+ conn->account.user, conn->account.host, conn->account.port,
+ conn->account.pass);
+
+ /* ibuf must be long enough to store the base64 encoding of
+ * oauth_buf, plus the additional debris.
+ */
+
+ ilen = strlen (oauth_buf) * 2 + 30;
+ ibuf = safe_malloc (ilen);
+ ibuf[0] = '\0';
+
+ safe_strcat (ibuf, ilen, "AUTH OAUTHBEARER ");
+ len = strlen(ibuf);
+
+ mutt_to_base64 ((unsigned char*) (ibuf + len),
+ (unsigned char*) oauth_buf, strlen (oauth_buf),
+ ilen - len);
+ safe_strcat (ibuf, ilen, "\r\n");
+
+ rc = mutt_socket_write (conn, ibuf);
+ FREE (&oauth_buf);
+ FREE (&ibuf);
+
+ if (rc == -1)
+ return SMTP_AUTH_FAIL;
+ if (smtp_get_resp (conn) != 0)
+ return SMTP_AUTH_FAIL;
+
+ return SMTP_AUTH_SUCCESS;
+}
--
2.18.0.597.ga71716f1ad
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment