Angel Yankov
9f1d1d27ed
- Upgrade to 6.5.1 * Drop two wolfSSL compile-time checks that were for older 6.4 or for future 7.0 releases and broke compilation with wolfSSL 5.7.4. Fixes https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=282413#c4 * Use %p instead of non-portable %#p for one wolfSSL-related diagnostic message (FreeBSD defines %#p to be %p, on many other platforms it's undefined behavior). * Add regex_helper.c to list of files that contain translatable strings, which contains two strings we missed to translate. * Simplify EVP_MD_fetch API detection ("like OpenSSL 3" vs. "like OpenSSL 1") for version switch and base it on the claimed OpenSSL version of the crypto SSL, which works for LibreSSL (claims OpenSSL 2) and wolfSSL alike. * Several translations added - Rebased fetchmail-6.3.8-smtp_errors.patch - Rebased fetchmail-FAQ-list-gmail-options-including-oauthbearer-and-app.patch - Rebased fetchmail-add-contrib-fetchnmail-oauth2.py-token-acquisition-u.patch - Rebased fetchmail-add-imap-oauthbearer-support.patch - Rebased fetchmail-add-passwordfile-and-passwordfd-options.patch - Rebased fetchmail-add-query_to64_outsize-utility-function.patch - Rebased fetchmail-bump-max-passwordlen-to-1bytes.patch - Rebased fetchmail-give-each-ctl-it-s-own-copy-of-password.patch - Rebased fetchmail-increase-max-password-length-to-handle-oauth-tokens.patch - Rebased fetchmail-re-read-passwordfile-on-every-poll.patch - Rebased fetchmail-support-oauthbearer-xoauth2-with-pop3.patch - Rebased fetchmailconf-no-more-future.patch OBS-URL: https://build.opensuse.org/request/show/1227336 OBS-URL: https://build.opensuse.org/package/show/server:mail/fetchmail?expand=0&rev=159
424 lines
13 KiB
Diff
424 lines
13 KiB
Diff
From: Matthew Ogilvie <mmogilvi+fml@zoho.com>
|
|
Date: Fri, 30 Jun 2017 02:35:12 -0600
|
|
Subject: support oauthbearer/xoauth2 with pop3
|
|
Git-repo: https://gitlab.com/fetchmail/fetchmail.git
|
|
Git-commit: 7b5c56f0fa3acb4c5589a4747c1921a311d8a464
|
|
|
|
(Also factor out some common imap/pop3 oauth2 code.)
|
|
---
|
|
Makefile.am | 2
|
|
fetchmail.man | 5 --
|
|
imap.c | 53 +++--------------------
|
|
oauth2.c | 61 +++++++++++++++++++++++++++
|
|
oauth2.h | 6 ++
|
|
pop3.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
|
|
6 files changed, 202 insertions(+), 55 deletions(-)
|
|
create mode 100644 oauth2.c
|
|
create mode 100644 oauth2.h
|
|
|
|
Index: fetchmail-6.5.1/Makefile.am
|
|
===================================================================
|
|
--- fetchmail-6.5.1.orig/Makefile.am
|
|
+++ fetchmail-6.5.1/Makefile.am
|
|
@@ -63,7 +63,7 @@ fetchmail_SOURCES= fetchmail.h getopt.h
|
|
fetchmail.c env.c idle.c options.c daemon.c \
|
|
driver.c transact.c sink.c smtp.c \
|
|
idlist.c uid.c mxget.c md5ify.c cram.c gssapi.c \
|
|
- opie.c interface.c netrc.c \
|
|
+ oauth2.c opie.c interface.c netrc.c \
|
|
unmime.c conf.c checkalias.c uid_db.h uid_db.c\
|
|
lock.h lock.c \
|
|
rcfile_l.l rcfile_y.y \
|
|
Index: fetchmail-6.5.1/fetchmail.man
|
|
===================================================================
|
|
--- fetchmail-6.5.1.orig/fetchmail.man
|
|
+++ fetchmail-6.5.1/fetchmail.man
|
|
@@ -1114,7 +1114,7 @@ AUTHENTICATION below for details). The
|
|
excruciating exactness, \fBkerberos_v4\fP), \fBgssapi\fP,
|
|
\fBcram\-md5\fP, \fBotp\fP, \fBntlm\fP, \fBmsn\fP (only for POP3),
|
|
\fBexternal\fP (only IMAP), \fBimplicit\fP (\fBssh\fP is understood
|
|
-as alias for \fBimplicit\fP) and \fBoauthbearer\fP (only IMAP).
|
|
+as alias for \fBimplicit\fP) and \fBoauthbearer\fP (requires token).
|
|
When \fBany\fP (the default) is specified, fetchmail tries
|
|
first methods that do not require a password (EXTERNAL, GSSAPI, KERBEROS\ IV,
|
|
KERBEROS\ 5); then it looks for methods that mask your password
|
|
@@ -2492,8 +2492,7 @@ Legal protocol identifiers for use with
|
|
Legal authentication types are 'any', 'password', 'kerberos',
|
|
\&'kerberos_v4', 'kerberos_v5' and 'gssapi', 'cram\-md5', 'otp', 'msn'
|
|
(only for POP3), 'ntlm', 'implicit', 'external' (only IMAP),
|
|
-'oauthbearer' (only for IMAP; requires authentication token in
|
|
-place of password).
|
|
+'oauthbearer' (requires authentication token in place of password).
|
|
The 'password' type specifies
|
|
authentication by normal transmission of a password (the password may be
|
|
plain text or subject to protocol-specific encryption as in CRAM-MD5);
|
|
Index: fetchmail-6.5.1/imap.c
|
|
===================================================================
|
|
--- fetchmail-6.5.1.orig/imap.c
|
|
+++ fetchmail-6.5.1/imap.c
|
|
@@ -15,6 +15,7 @@
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
#include <errno.h>
|
|
+#include "oauth2.h"
|
|
#include "socket.h"
|
|
|
|
#include "i18n.h"
|
|
@@ -417,63 +418,23 @@ static int do_imap_ntlm(int sock, struct
|
|
|
|
static int do_imap_oauthbearer(int sock, struct query *ctl,flag xoauth2)
|
|
{
|
|
- /* Implements relevant parts of RFC-7628, RFC-6750, and
|
|
- * https://developers.google.com/gmail/imap/xoauth2-protocol
|
|
- *
|
|
- * This assumes something external manages obtaining an up-to-date
|
|
- * authentication/bearer token and arranging for it to be in
|
|
- * ctl->password. This may involve renewing it ahead of time if
|
|
- * necessary using a renewal token that fetchmail knows nothing about.
|
|
- * See:
|
|
- * https://github.com/google/gmail-oauth2-tools/wiki/OAuth2DotPyRunThrough
|
|
- */
|
|
- const char *name;
|
|
- char *oauth2str;
|
|
- int oauth2len;
|
|
- int saved_suppress_tags = suppress_tags;
|
|
-
|
|
- char *oauth2b64;
|
|
-
|
|
+ char *oauth2str = get_oauth2_string(ctl, xoauth2);
|
|
+ const char *name = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
|
|
int ok;
|
|
|
|
- oauth2len = strlen(ctl->remotename) + strlen(ctl->password) + 32;
|
|
- oauth2str = (char *)xmalloc(oauth2len);
|
|
- if (xoauth2)
|
|
- {
|
|
- snprintf(oauth2str, oauth2len,
|
|
- "user=%s\1auth=Bearer %s\1\1",
|
|
- ctl->remotename,
|
|
- ctl->password);
|
|
- name = "XOAUTH2";
|
|
- }
|
|
- else
|
|
- {
|
|
- snprintf(oauth2str, oauth2len,
|
|
- "n,a=%s,\1auth=Bearer %s\1\1",
|
|
- ctl->remotename,
|
|
- ctl->password);
|
|
- name = "OAUTHBEARER";
|
|
- }
|
|
-
|
|
- oauth2b64 = (char *)xmalloc(2*strlen(oauth2str)+8);
|
|
- to64frombits(oauth2b64, oauth2str, strlen(oauth2str));
|
|
-
|
|
- memset(oauth2str, 0x55, strlen(oauth2str));
|
|
- free(oauth2str);
|
|
-
|
|
/* Protect the access token like a password in logs, despite the
|
|
* usually-short expiration time and base64 encoding:
|
|
*/
|
|
- strlcpy(shroud, oauth2b64, sizeof(shroud));
|
|
+ strlcpy(shroud, oauth2str, sizeof(shroud));
|
|
|
|
plus_cont_context = IPLUS_OAUTHBEARER;
|
|
- ok = gen_transact(sock, "AUTHENTICATE %s %s", name, oauth2b64);
|
|
+ ok = gen_transact(sock, "AUTHENTICATE %s %s", name, oauth2str);
|
|
plus_cont_context = IPLUS_NONE;
|
|
|
|
memset(shroud, 0x55, sizeof(shroud));
|
|
shroud[0] = '\0';
|
|
- memset(oauth2b64, 0x55, strlen(oauth2b64));
|
|
- free(oauth2b64);
|
|
+ memset(oauth2str, 0x55, strlen(oauth2str));
|
|
+ free(oauth2str);
|
|
|
|
return ok;
|
|
}
|
|
Index: fetchmail-6.5.1/oauth2.c
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ fetchmail-6.5.1/oauth2.c
|
|
@@ -0,0 +1,61 @@
|
|
+/*
|
|
+ * oauth2.c -- oauthbearer and xoauth2 support
|
|
+ *
|
|
+ * Copyright 2017 by Matthew Ogilvie
|
|
+ * For license terms, see the file COPYING in this directory.
|
|
+ */
|
|
+
|
|
+#include "config.h"
|
|
+#include "fetchmail.h"
|
|
+#include "oauth2.h"
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+
|
|
+char *get_oauth2_string(struct query *ctl,flag xoauth2)
|
|
+{
|
|
+ /* Implements the bearer token string based for a
|
|
+ * combination of RFC-7628 (ouath sasl, with
|
|
+ * examples for imap only), RFC-6750 (oauth2), and
|
|
+ * RFC-5034 (pop sasl), as implemented by gmail and others.
|
|
+ *
|
|
+ * Also supports xoauth2, which is just a couple of minor variariations.
|
|
+ * https://developers.google.com/gmail/imap/xoauth2-protocol
|
|
+ *
|
|
+ * This assumes something external manages obtaining an up-to-date
|
|
+ * authentication/bearer token and arranging for it to be in
|
|
+ * ctl->password. This may involve renewing it ahead of time if
|
|
+ * necessary using a renewal token that fetchmail knows nothing about.
|
|
+ * See:
|
|
+ * https://github.com/google/gmail-oauth2-tools/wiki/OAuth2DotPyRunThrough
|
|
+ */
|
|
+ char *oauth2str;
|
|
+ int oauth2len;
|
|
+
|
|
+ char *oauth2b64;
|
|
+
|
|
+ oauth2len = strlen(ctl->remotename) + strlen(ctl->password) + 32;
|
|
+ oauth2str = (char *)xmalloc(oauth2len);
|
|
+ if (xoauth2)
|
|
+ {
|
|
+ snprintf(oauth2str, oauth2len,
|
|
+ "user=%s\1auth=Bearer %s\1\1",
|
|
+ ctl->remotename,
|
|
+ ctl->password);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ snprintf(oauth2str, oauth2len,
|
|
+ "n,a=%s,\1auth=Bearer %s\1\1",
|
|
+ ctl->remotename,
|
|
+ ctl->password);
|
|
+ }
|
|
+
|
|
+ oauth2b64 = (char *)xmalloc(2*strlen(oauth2str)+8);
|
|
+ to64frombits(oauth2b64, oauth2str, strlen(oauth2str));
|
|
+
|
|
+ memset(oauth2str, 0x55, strlen(oauth2str));
|
|
+ free(oauth2str);
|
|
+
|
|
+ return oauth2b64;
|
|
+}
|
|
Index: fetchmail-6.5.1/oauth2.h
|
|
===================================================================
|
|
--- /dev/null
|
|
+++ fetchmail-6.5.1/oauth2.h
|
|
@@ -0,0 +1,6 @@
|
|
+#ifndef OAUTH2_H
|
|
+#define OAUTH2_H
|
|
+
|
|
+char *get_oauth2_string(struct query *ctl,flag xoauth2);
|
|
+
|
|
+#endif /*OAUTH2_H*/
|
|
Index: fetchmail-6.5.1/pop3.c
|
|
===================================================================
|
|
--- fetchmail-6.5.1.orig/pop3.c
|
|
+++ fetchmail-6.5.1/pop3.c
|
|
@@ -17,6 +17,7 @@
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
+#include "oauth2.h"
|
|
#include "socket.h"
|
|
#include "i18n.h"
|
|
#include "uid_db.h"
|
|
@@ -49,6 +50,10 @@ static flag has_cram = FALSE;
|
|
static flag has_otp = FALSE;
|
|
static flag has_ntlm = FALSE;
|
|
static flag has_stls = FALSE;
|
|
+static flag has_oauthbearer = FALSE;
|
|
+static flag has_xoauth2 = FALSE;
|
|
+
|
|
+static const char *next_sasl_resp = NULL;
|
|
|
|
static void clear_sessiondata(void) {
|
|
/* must match defaults above */
|
|
@@ -132,12 +137,65 @@ static int pop3_ok (int sock, char *argb
|
|
char buf [POPBUFSIZE+1];
|
|
char *bufp;
|
|
|
|
- if ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
|
|
+ while ((ok = gen_recv(sock, buf, sizeof(buf))) == 0)
|
|
{ bufp = buf;
|
|
- if (*bufp == '+' || *bufp == '-')
|
|
- bufp++;
|
|
- else
|
|
+ if (*bufp == '+')
|
|
+ {
|
|
+ bufp++;
|
|
+ if (*bufp == ' ' && next_sasl_resp != NULL)
|
|
+ {
|
|
+ /* Currently only used for OAUTHBEARER/XOAUTH2, and only
|
|
+ * rarely even then.
|
|
+ *
|
|
+ * This is the only case where the top while() actually
|
|
+ * loops.
|
|
+ *
|
|
+ * For OAUTHBEARER, data aftetr '+ ' is probably
|
|
+ * base64-encoded JSON with some HTTP-related error details.
|
|
+ */
|
|
+ if (*next_sasl_resp != '\0')
|
|
+ SockWrite(sock, next_sasl_resp, strlen(next_sasl_resp));
|
|
+ SockWrite(sock, "\r\n", 2);
|
|
+ if (outlevel >= O_MONITOR)
|
|
+ {
|
|
+ const char *found;
|
|
+ if (shroud[0] && (found = strstr(next_sasl_resp, shroud)))
|
|
+ {
|
|
+ /* enshroud() without copies, and avoid
|
|
+ * confusing with a genuine "*" (cancel).
|
|
+ */
|
|
+ report(stdout, "POP3> %.*s[SHROUDED]%s\n",
|
|
+ (int)(found-next_sasl_resp), next_sasl_resp,
|
|
+ found+strlen(shroud));
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ report(stdout, "POP3> %s\n", next_sasl_resp);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (*next_sasl_resp == '\0' || *next_sasl_resp == '*')
|
|
+ {
|
|
+ /* No more responses expected, cancel AUTH command if
|
|
+ * more responses requested.
|
|
+ */
|
|
+ next_sasl_resp = "*";
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ next_sasl_resp = "";
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ else if (*bufp == '-')
|
|
+ {
|
|
+ bufp++;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
return(PS_PROTOCOL);
|
|
+ }
|
|
|
|
while (isalpha((unsigned char)*bufp))
|
|
bufp++;
|
|
@@ -206,6 +264,8 @@ static int pop3_ok (int sock, char *argb
|
|
#endif
|
|
if (argbuf != NULL)
|
|
strcpy(argbuf,bufp);
|
|
+
|
|
+ break;
|
|
}
|
|
|
|
return(ok);
|
|
@@ -234,11 +294,13 @@ static int capa_probe(int sock)
|
|
#ifdef NTLM_ENABLE
|
|
has_ntlm = FALSE;
|
|
#endif /* NTLM_ENABLE */
|
|
+ has_oauthbearer = FALSE;
|
|
+ has_xoauth2 = FALSE;
|
|
|
|
ok = gen_transact(sock, "CAPA");
|
|
if (ok == PS_SUCCESS)
|
|
{
|
|
- char buffer[64];
|
|
+ char buffer[128];
|
|
char *cp;
|
|
|
|
/* determine what authentication methods we have available */
|
|
@@ -253,6 +315,10 @@ static int capa_probe(int sock)
|
|
if (strstr(buffer, "STLS"))
|
|
has_stls = TRUE;
|
|
#endif /* SSL_ENABLE */
|
|
+static flag has_oauthbearer = FALSE;
|
|
+static flag has_xoauth2 = FALSE;
|
|
+
|
|
+static const char *next_sasl_resp = NULL;
|
|
|
|
#if defined(GSSAPI)
|
|
if (strstr(buffer, "GSSAPI"))
|
|
@@ -276,6 +342,12 @@ static int capa_probe(int sock)
|
|
|
|
if (strstr(buffer, "CRAM-MD5"))
|
|
has_cram = TRUE;
|
|
+
|
|
+ if (strstr(buffer, "OAUTHBEARER"))
|
|
+ has_oauthbearer = TRUE;
|
|
+
|
|
+ if (strstr(buffer, "XOAUTH2"))
|
|
+ has_xoauth2 = TRUE;
|
|
}
|
|
}
|
|
done_capa = TRUE;
|
|
@@ -292,6 +364,40 @@ static void set_peek_capable(struct quer
|
|
peek_capable = !ctl->fetchall && (!ctl->keep || ctl->server.uidl);
|
|
}
|
|
|
|
+static int do_oauthbearer(int sock, struct query *ctl, flag xoauth2)
|
|
+{
|
|
+ char *oauth2str = get_oauth2_string(ctl, xoauth2);
|
|
+ const char *name = xoauth2 ? "XOAUTH2" : "OAUTHBEARER";
|
|
+ int ok;
|
|
+
|
|
+ /* Protect the access token like a password in logs, despite the
|
|
+ * usually-short expiration time and base64 encoding:
|
|
+ */
|
|
+ strlcpy(shroud, oauth2str, sizeof(shroud));
|
|
+
|
|
+ if (4+1+1+2+strlen(name)+strlen(oauth2str) <= 255)
|
|
+ {
|
|
+ next_sasl_resp = "";
|
|
+ ok = gen_transact(sock, "AUTH %s %s", name, oauth2str);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ /* Too long to use "initial client response" (RFC-5034 section 4,
|
|
+ * referencing RFC-4422 section 4).
|
|
+ */
|
|
+ next_sasl_resp = oauth2str;
|
|
+ ok = gen_transact(sock, "AUTH %s", name);
|
|
+ }
|
|
+ next_sasl_resp = NULL;
|
|
+
|
|
+ memset(shroud, 0x55, sizeof(shroud));
|
|
+ shroud[0] = '\0';
|
|
+ memset(oauth2str, 0x55, strlen(oauth2str));
|
|
+ free(oauth2str);
|
|
+
|
|
+ return ok;
|
|
+}
|
|
+
|
|
static int pop3_getauth(int sock, struct query *ctl, char *greeting)
|
|
/* apply for connection authorization */
|
|
{
|
|
@@ -371,6 +477,7 @@ static int pop3_getauth(int sock, struct
|
|
(ctl->server.authenticate == A_KERBEROS_V5) ||
|
|
(ctl->server.authenticate == A_OTP) ||
|
|
(ctl->server.authenticate == A_CRAM_MD5) ||
|
|
+ (ctl->server.authenticate == A_OAUTHBEARER) ||
|
|
maybe_starttls(ctl))
|
|
{
|
|
if ((ok = capa_probe(sock)) != PS_SUCCESS)
|
|
@@ -520,6 +627,19 @@ static int pop3_getauth(int sock, struct
|
|
/*
|
|
* OK, we have an authentication type now.
|
|
*/
|
|
+ if (ctl->server.authenticate == A_OAUTHBEARER)
|
|
+ {
|
|
+ if (has_oauthbearer || !has_xoauth2)
|
|
+ {
|
|
+ ok = do_oauthbearer(sock, ctl, FALSE); /* OAUTHBEARER */
|
|
+ }
|
|
+ if (ok != PS_SUCCESS && has_xoauth2)
|
|
+ {
|
|
+ ok = do_oauthbearer(sock, ctl, TRUE); /* XOAUTH2 */
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+
|
|
#if defined(KERBEROS_V4)
|
|
/*
|
|
* Servers doing KPOP have to go through a dummy login sequence
|