this can be combined with an external tool (PassCmd) to generate access tokens used for authentication
For more information, see: https://tools.ietf.org/html/rfc7628 --- src/drv_imap.c | 123 ++++++++++++++++++++++++++++++++++++++++--------- src/mbsync.1 | 2 +- 2 files changed, 102 insertions(+), 23 deletions(-) diff --git a/src/drv_imap.c b/src/drv_imap.c index 4ea86d6..b0a3815 100644 --- a/src/drv_imap.c +++ b/src/drv_imap.c @@ -42,6 +42,7 @@ #endif #ifdef HAVE_LIBSSL +# include <openssl/evp.h> enum { SSL_None, SSL_STARTTLS, SSL_IMAPS }; #endif @@ -2108,14 +2109,91 @@ done_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) #endif +static size_t +encoded_length( size_t in ) +{ + in += 2; + in /= 3; + in <<= 2; + in++; + + return in; +} + +static int +auth_builtin_oauthbearer( imap_store_t *ctx ) +{ + imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf; + imap_server_conf_t *srvc = cfg->server; + size_t buf_size = 38; /* format string, port, NULL */ + size_t input_size; + char *buf, *buf_encoded; + +#ifdef HAVE_LIBSSL + if (!ctx->conn.ssl) { +#endif + error( "Note: not using OAUTHBEARER because connection is not encrypted;\n" ); + return -1; +#ifdef HAVE_LIBSSL + } +#endif + + if (!ensure_user( srvc ) || !ensure_password( srvc )) + return -1; + + buf_size += strlen( srvc->user ); + buf_size += strlen( srvc->sconf.host ); + buf_size += strlen( srvc->pass ); + + buf = nfmalloc( buf_size ); + input_size = nfsnprintf( buf, buf_size, "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001", + srvc->user, srvc->sconf.host, srvc->sconf.port, srvc->pass ); + + buf_encoded = nfmalloc ( encoded_length( input_size ) ); + +#ifdef HAVE_LIBSSL + EVP_EncodeBlock( (unsigned char *)buf_encoded, (unsigned char *)buf, input_size ); +#endif + + imap_exec( ctx, 0, imap_open_store_authenticate2_p2, + "AUTHENTICATE OAUTHBEARER %s", buf_encoded ); + + free ( buf_encoded ); + free ( buf ); + + return 0; +} + +static int +auth_builtin_login( imap_store_t *ctx ) +{ + imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf; + imap_server_conf_t *srvc = cfg->server; + + if (!ensure_user( srvc ) || !ensure_password( srvc )) + return -1; + +#ifdef HAVE_LIBSSL + if (!ctx->conn.ssl) +#endif + warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); + imap_exec( ctx, 0, imap_open_store_authenticate2_p2, + "LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass ); + + return 0; +} + +#define AUTH_BUILTIN_LOGIN 0x1 +#define AUTH_BUILTIN_OAUTHBEARER 0x2 + static void imap_open_store_authenticate2( imap_store_t *ctx ) { imap_store_conf_t *cfg = (imap_store_conf_t *)ctx->gen.conf; imap_server_conf_t *srvc = cfg->server; string_list_t *mech, *cmech; - int auth_login = 0; - int skipped_login = 0; + int auth_builtin = 0; + int skipped_builtin = 0; #ifdef HAVE_LIBSASL const char *saslavail; char saslmechs[1024], *saslend = saslmechs; @@ -2126,15 +2204,19 @@ imap_open_store_authenticate2( imap_store_t *ctx ) int any = !strcmp( mech->string, "*" ); for (cmech = ctx->auth_mechs; cmech; cmech = cmech->next) { if (any || !strcasecmp( mech->string, cmech->string )) { - if (!strcasecmp( cmech->string, "LOGIN" )) { + if (!strcasecmp( cmech->string, "LOGIN" )) + auth_builtin |= AUTH_BUILTIN_LOGIN; + if (!strcasecmp( cmech->string, "OAUTHBEARER" )) + auth_builtin |= AUTH_BUILTIN_OAUTHBEARER; + if (auth_builtin & (AUTH_BUILTIN_LOGIN | AUTH_BUILTIN_OAUTHBEARER)) { #ifdef HAVE_LIBSSL - if (ctx->conn.ssl || !any) + if (!ctx->conn.ssl && any) { #else - if (!any) + if (any) { #endif - auth_login = 1; - else - skipped_login = 1; + auth_builtin = 0; + skipped_builtin = 1; + } #ifdef HAVE_LIBSASL } else { int len = strlen( cmech->string ); @@ -2202,7 +2284,7 @@ imap_open_store_authenticate2( imap_store_t *ctx ) notsasl: if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", " ", "", &saslavail, NULL, NULL ) != SASL_OK) saslavail = "(none)"; /* EXTERNAL is always there anyway. */ - if (!auth_login) { + if (!auth_builtin) { error( "IMAP error: selected SASL mechanism(s) not available;\n" " selected:%s\n available: %s\n", saslmechs, saslavail ); goto skipnote; @@ -2211,24 +2293,21 @@ imap_open_store_authenticate2( imap_store_t *ctx ) sasl_dispose( &ctx->sasl ); } #endif - if (auth_login) { - if (!ensure_user( srvc ) || !ensure_password( srvc )) - goto bail; -#ifdef HAVE_LIBSSL - if (!ctx->conn.ssl) -#endif - warn( "*** IMAP Warning *** Password is being sent in the clear\n" ); - imap_exec( ctx, 0, imap_open_store_authenticate2_p2, - "LOGIN \"%\\s\" \"%\\s\"", srvc->user, srvc->pass ); - return; + if (auth_builtin & AUTH_BUILTIN_LOGIN) { + if ( !auth_builtin_login( ctx )) + return; + } + if (auth_builtin & AUTH_BUILTIN_OAUTHBEARER) { + if ( !auth_builtin_oauthbearer( ctx )) + return; } error( "IMAP error: server supports no acceptable authentication mechanism\n" ); #ifdef HAVE_LIBSASL skipnote: #endif - if (skipped_login) - error( "Note: not using LOGIN because connection is not encrypted;\n" - " use 'AuthMechs LOGIN' explicitly to force it.\n" ); + if (skipped_builtin) + error( "Note: not using builtin authentication because connection is not encrypted;\n" + " use e.g. 'AuthMechs LOGIN' explicitly to force it.\n" ); bail: imap_open_store_bail( ctx, FAIL_FINAL ); diff --git a/src/mbsync.1 b/src/mbsync.1 index 6830508..3327443 100644 --- a/src/mbsync.1 +++ b/src/mbsync.1 @@ -341,7 +341,7 @@ example. \fBAuthMechs\fR \fItype\fR ... The list of acceptable authentication mechanisms. In addition to the mechanisms listed in the SASL registry (link below), -the legacy IMAP \fBLOGIN\fR mechanism is known. +two built-in IMAP mechanisms (\fBLOGIN\fR and \fBOAUTHBEARER\fR) are known. The wildcard \fB*\fR represents all mechanisms that are deemed secure enough for the current \fBSSLType\fR setting. The actually used mechanism is the most secure choice from the intersection -- 2.28.0 _______________________________________________ isync-devel mailing list isync-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/isync-devel