Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:15-SP1
fetchmail
fetchmail-support-oauthbearer-xoauth2-with-pop3...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File fetchmail-support-oauthbearer-xoauth2-with-pop3.patch of Package fetchmail
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 | 122 ++++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 196 insertions(+), 53 deletions(-) create mode 100644 oauth2.c create mode 100644 oauth2.h Index: fetchmail-6.4.22/Makefile.am =================================================================== --- fetchmail-6.4.22.orig/Makefile.am +++ fetchmail-6.4.22/Makefile.am @@ -68,7 +68,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.4.22/fetchmail.man =================================================================== --- fetchmail-6.4.22.orig/fetchmail.man +++ fetchmail-6.4.22/fetchmail.man @@ -1007,7 +1007,7 @@ AUTHENTICATION below for details). The \&\fBpassword\fP, \fBkerberos_v5\fP, \fBkerberos\fP (or, for excruciating exactness, \fBkerberos_v4\fP), \fBgssapi\fP, \fBcram\-md5\fP, \fBotp\fP, \fBntlm\fP, \fBmsn\fP (only for POP3), -\fBexternal\fP (only IMAP), \fBssh\fP and \fBoauthbearer\fP (only IMAP). +\fBexternal\fP (only IMAP), \fBssh\fP and \fBoauthbearer\fP (requires token). When \fBany\fP (the default) is specified, fetchmail tries first methods that don't require a password (EXTERNAL, GSSAPI, KERBEROS\ IV, KERBEROS\ 5); then it looks for methods that mask your password @@ -2351,8 +2351,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', 'ssh', '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.4.22/imap.c =================================================================== --- fetchmail-6.4.22.orig/imap.c +++ fetchmail-6.4.22/imap.c @@ -17,6 +17,7 @@ #include <limits.h> #include <errno.h> #endif +#include "oauth2.h" #include "socket.h" #include "i18n.h" @@ -419,63 +420,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.4.22/oauth2.c =================================================================== --- /dev/null +++ fetchmail-6.4.22/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.4.22/oauth2.h =================================================================== --- /dev/null +++ fetchmail-6.4.22/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.4.22/pop3.c =================================================================== --- fetchmail-6.4.22.orig/pop3.c +++ fetchmail-6.4.22/pop3.c @@ -20,6 +20,7 @@ #include <errno.h> #include "fetchmail.h" +#include "oauth2.h" #include "socket.h" #include "i18n.h" #include "uid_db.h" @@ -52,6 +53,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 */ @@ -135,12 +140,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++; @@ -209,6 +267,8 @@ static int pop3_ok (int sock, char *argb #endif if (argbuf != NULL) strcpy(argbuf,bufp); + + break; } return(ok); @@ -237,11 +297,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 */ @@ -256,6 +318,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")) @@ -279,6 +345,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; @@ -295,6 +367,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 */ { @@ -374,6 +480,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) @@ -523,6 +630,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
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor