diff options
Diffstat (limited to 'imap')
-rw-r--r-- | imap/Makefile.am | 7 | ||||
-rw-r--r-- | imap/auth.c | 1 | ||||
-rw-r--r-- | imap/auth.h | 1 | ||||
-rw-r--r-- | imap/auth_oauth.c | 104 | ||||
-rw-r--r-- | imap/command.c | 1 | ||||
-rw-r--r-- | imap/imap_private.h | 1 |
6 files changed, 112 insertions, 3 deletions
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 */ |