enum ImapAuthRes imap_auth_oauth(struct ImapData *idata, const char *method)
{
char *ibuf = NULL;
- char *oauth_buf = NULL;
- int len, ilen, oalen;
+ char *oauthbearer = NULL;
+ int ilen;
int rc;
/* For now, we only support SASL_IR also and over TLS */
mutt_message(_("Authenticating (OAUTHBEARER)..."));
- /* get auth info */
- if (mutt_account_getlogin(&idata->conn->account))
+ /* We get the access token from the imap_oauth_refresh_command */
+ oauthbearer = mutt_account_getoauthbearer(&idata->conn->account);
+ if (oauthbearer == NULL)
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 = mutt_str_strlen(idata->conn->account.user) +
- mutt_str_strlen(idata->conn->account.host) +
- mutt_str_strlen(idata->conn->account.pass) + 50;
- oauth_buf = mutt_mem_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 = mutt_str_strlen(oauth_buf) * 2 + 30;
+ ilen = mutt_str_strlen(oauthbearer) + 30;
ibuf = mutt_mem_malloc(ilen);
- ibuf[0] = '\0';
-
- mutt_str_strcat(ibuf, ilen, "AUTHENTICATE OAUTHBEARER ");
- len = mutt_str_strlen(ibuf);
-
- mutt_b64_encode(oauth_buf, mutt_str_strlen(oauth_buf), (ibuf + len), ilen - len);
+ snprintf(ibuf, ilen, "AUTHENTICATE OAUTHBEARER %s", oauthbearer);
/* 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(&oauthbearer);
FREE(&ibuf);
+ if (rc)
+ {
+ /* The error response was in SASL continuation, so continue the SASL
+ * to cause a failure and exit SASL input. See RFC 7628 3.2.3
+ */
+ mutt_socket_send(idata->conn, "\001");
+ rc = imap_exec(idata, ibuf, IMAP_CMD_FAIL_OK);
+ }
+
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_send(idata->conn, "an noop\r\n");
-
mutt_error(_("OAUTHBEARER authentication failed."));
mutt_sleep(2);
return IMAP_AUTH_FAILURE;
** .pp
** This variable defaults to the value of $$imap_user.
*/
+ { "imap_oauth_refresh_command", DT_STRING, R_NONE, &ImapOauthRefreshCmd, 0 },
+ /*
+ ** .pp
+ ** The command to run to generate an OAUTH refresh token for
+ ** authorizing your connection to your IMAP server. This command will be
+ ** run on every connection attempt that uses the OAUTHBEARER authentication
+ ** mechanism.
+ */
{ "imap_pass", DT_STRING, R_NONE|F_SENSITIVE, &ImapPass, 0 },
/*
** .pp
** for retrieving only unread messages from the POP server when using
** the \fC$<fetch-mail>\fP function.
*/
+ { "pop_oauth_refresh_command", DT_STRING, R_NONE, &PopOauthRefreshCmd, 0 },
+ /*
+ ** .pp
+ ** The command to run to generate an OAUTH refresh token for
+ ** authorizing your connection to your POP server. This command will be
+ ** run on every connection attempt that uses the OAUTHBEARER authentication
+ ** mechanism.
+ */
{ "pop_pass", DT_STRING, R_NONE|F_SENSITIVE, &PopPass, 0 },
/*
** .pp
** set smtp_authenticators="digest-md5:cram-md5"
** .te
*/
+ { "smtp_oauth_refresh_command", DT_STRING, R_NONE, &SmtpOauthRefreshCmd, 0 },
+ /*
+ ** .pp
+ ** The command to run to generate an OAUTH refresh token for
+ ** authorizing your connection to your SMTP server. This command will be
+ ** run on every connection attempt that uses the OAUTHBEARER authentication
+ ** mechanism.
+ */
{ "smtp_pass", DT_STRING, R_NONE|F_SENSITIVE, &SmtpPass, 0 },
/*
** .pp
#include "conn/conn.h"
#include "mutt_account.h"
#include "curs_lib.h"
+#include "filter.h"
#include "globals.h"
#include "options.h"
+#include "pop/pop.h"
/* These Config Variables are only used in mutt_account.c */
char *ImapLogin; ///< Config: (imap) Login name for the IMAP server (defaults to ImapUser)
+char *ImapOauthRefreshCmd;
char *ImapPass; ///< Config: (imap) Password for the IMAP server
char *NntpPass; ///< Config: (nntp) Password for the news server
char *NntpUser; ///< Config: (nntp) Username for the news server
char *PopPass; ///< Config: (pop) Password of the POP server
char *PopUser; ///< Config: (pop) Username of the POP server
char *SmtpPass; ///< Config: (smtp) Password for the SMTP server
+char *SmtpOauthRefreshCmd;
/**
* mutt_account_match - Compare account info (host/port/user)
{
account->flags &= ~MUTT_ACCT_PASS;
}
+
+/* mutt_account_getoauthbearer: call external command to generate the
+ * oauth refresh token for this ACCOUNT, then create and encode the
+ * OAUTHBEARER token based on RFC 7628. Returns NULL on failure.
+ * Resulting token is dynamically allocated and should be FREE'd by the
+ * caller.
+ */
+char *mutt_account_getoauthbearer(struct Account *account)
+{
+ FILE *fp;
+ char *cmd = NULL;
+ char *token = NULL;
+ size_t token_size = 0;
+ char *oauthbearer = NULL;
+ size_t oalen;
+ char *encoded_token = NULL;
+ size_t encoded_len;
+ pid_t pid;
+
+ /* The oauthbearer token includes the login */
+ if (mutt_account_getlogin(account))
+ return NULL;
+
+#ifdef USE_IMAP
+ if ((account->type == MUTT_ACCT_TYPE_IMAP) && ImapOauthRefreshCmd)
+ cmd = ImapOauthRefreshCmd;
+#endif
+#ifdef USE_POP
+ else if ((account->type == MUTT_ACCT_TYPE_POP) && PopOauthRefreshCmd)
+ cmd = PopOauthRefreshCmd;
+#endif
+#ifdef USE_SMTP
+ else if ((account->type == MUTT_ACCT_TYPE_SMTP) && SmtpOauthRefreshCmd)
+ cmd = SmtpOauthRefreshCmd;
+#endif
+
+ if (cmd == NULL)
+ {
+ mutt_error(
+ _("mutt_account_getoauthbearer: No OAUTH refresh command defined"));
+ return NULL;
+ }
+
+ if ((pid = mutt_create_filter(cmd, NULL, &fp, NULL)) < 0)
+ {
+ mutt_perror(
+ _("mutt_account_getoauthbearer: Unable to run refresh command"));
+ return NULL;
+ }
+
+ /* read line */
+ token = mutt_file_read_line(NULL, &token_size, fp, NULL, 0);
+ mutt_file_fclose(&fp);
+ mutt_wait_filter(pid);
+
+ if (token == NULL || *token == '\0')
+ {
+ mutt_error(_("mutt_account_getoauthbearer: Command returned empty string"));
+ FREE(&token);
+ return NULL;
+ }
+
+ /* Determine the length of the keyed message digest, add 50 for
+ * overhead.
+ */
+ oalen = strlen(account->login) + strlen(account->host) + strlen(token) + 50;
+ oauthbearer = mutt_mem_malloc(oalen);
+
+ snprintf(oauthbearer, oalen, "n,a=%s,\001host=%s\001port=%d\001auth=Bearer %s\001\001",
+ account->login, account->host, account->port, token);
+
+ FREE(&token);
+
+ encoded_len = strlen(oauthbearer) * 4 / 3 + 10;
+ encoded_token = mutt_mem_malloc(encoded_len);
+ mutt_b64_encode(oauthbearer, strlen(oauthbearer), encoded_token, encoded_len);
+ FREE(&oauthbearer);
+ return encoded_token;
+}
/* These Config Variables are only used in mutt_account.c */
extern char *ImapLogin;
+extern char *ImapOauthRefreshCmd;
extern char *ImapPass;
extern char *NntpPass;
extern char *NntpUser;
extern char *PopPass;
extern char *PopUser;
extern char *SmtpPass;
+extern char *SmtpOauthRefreshCmd;
/**
* enum AccountType - account types
int mutt_account_getlogin(struct Account *account);
int mutt_account_getpass(struct Account *account);
void mutt_account_unsetpass(struct Account *account);
+char *mutt_account_getoauthbearer(struct Account *account);
#endif /* _MUTT_ACCOUNT_H */
short PopCheckinterval; ///< Config: (pop) Interval between checks for new mail
unsigned char PopDelete; ///< Config: (pop) After downloading POP messages, delete them on the server
char *PopHost; ///< Config: (pop) Url of the POP server
-bool PopLast; ///< Config: (pop) Use the 'LAST' command to fetch new mail
+char *PopOauthRefreshCmd;
+bool PopLast; ///< Config: (pop) Use the 'LAST' command to fetch new mail
#ifdef USE_HCACHE
#define HC_FNAME "neomutt" /* filename for hcache as POP lacks paths */
extern short PopCheckinterval;
extern unsigned char PopDelete;
extern char * PopHost;
+extern char *PopOauthRefreshCmd;
extern bool PopLast;
/* These Config Variables are only used in pop/pop_auth.c */
const char *pc = NULL;
unsigned int len = 0, olen = 0, client_start;
+ if (mutt_account_getpass(&pop_data->conn->account) || !pop_data->conn->account.pass[0])
+ return POP_A_FAILURE;
+
if (mutt_sasl_client_new(pop_data->conn, &saslconn) < 0)
{
mutt_debug(1, "Error allocating SASL connection.\n");
char hash[33];
char buf[LONG_STRING];
+ if (mutt_account_getpass(&pop_data->conn->account) || !pop_data->conn->account.pass[0])
+ return POP_A_FAILURE;
+
if (!pop_data->timestamp)
return POP_A_UNAVAIL;
if (!pop_data->cmd_user)
return POP_A_UNAVAIL;
+ if (mutt_account_getpass(&pop_data->conn->account) || !pop_data->conn->account.pass[0])
+ return POP_A_FAILURE;
+
mutt_message(_("Logging in..."));
snprintf(buf, sizeof(buf), "USER %s\r\n", pop_data->conn->account.user);
return POP_A_FAILURE;
}
+/* OAUTHBEARER authenticator */
+static enum PopAuthRes pop_auth_oauth(struct PopData *pop_data, const char *method)
+{
+ char *oauthbearer = NULL;
+ char decoded_err[LONG_STRING];
+ char *err = NULL;
+ char *auth_cmd = NULL;
+ size_t auth_cmd_len;
+ int ret, len;
+
+ mutt_message(_("Authenticating (OAUTHBEARER)..."));
+
+ oauthbearer = mutt_account_getoauthbearer(&pop_data->conn->account);
+ if (oauthbearer == NULL)
+ return POP_A_FAILURE;
+
+ auth_cmd_len = strlen(oauthbearer) + 30;
+ auth_cmd = mutt_mem_malloc(auth_cmd_len);
+ snprintf(auth_cmd, auth_cmd_len, "AUTH OAUTHBEARER %s\r\n", oauthbearer);
+ FREE(&oauthbearer);
+
+ ret = pop_query_d(pop_data, auth_cmd, strlen(auth_cmd),
+#ifdef DEBUG
+ /* don't print the bearer token unless we're at the ungodly debugging level */
+ DebugLevel < MUTT_SOCK_LOG_FULL ? "AUTH OAUTHBEARER *\r\n" :
+#endif
+ NULL);
+ FREE(&auth_cmd);
+
+ switch (ret)
+ {
+ case 0:
+ return POP_A_SUCCESS;
+ case -1:
+ return POP_A_SOCKET;
+ }
+
+ /* The error response was a SASL continuation, so "continue" it.
+ * See RFC 7628 3.2.3
+ */
+ mutt_socket_send(pop_data->conn, "\001");
+
+ err = pop_data->err_msg;
+ len = mutt_b64_decode(pop_data->err_msg, decoded_err, sizeof(decoded_err) - 1);
+ if (len >= 0)
+ {
+ decoded_err[len] = '\0';
+ err = decoded_err;
+ }
+ mutt_error("%s %s", _("Authentication failed."), err);
+
+ return POP_A_FAILURE;
+}
+
static const struct PopAuth pop_authenticators[] = {
+ { pop_auth_oauth, "oauthbearer" },
#ifdef USE_SASL
{ pop_auth_sasl, NULL },
#endif
- { pop_auth_apop, "apop" },
- { pop_auth_user, "user" },
- { NULL, NULL },
+ { pop_auth_apop, "apop" }, { pop_auth_user, "user" }, { NULL, NULL },
};
/**
int attempts = 0;
int ret = POP_A_UNAVAIL;
- if ((mutt_account_getuser(acct) < 0) || (acct->user[0] == '\0') ||
- (mutt_account_getpass(acct) < 0) || (acct->pass[0] == '\0'))
+ if ((mutt_account_getuser(acct) < 0) || (acct->user[0] == '\0'))
{
return -3;
}
static int smtp_auth_oauth(struct Connection *conn)
{
char *ibuf = NULL;
- char *oauth_buf = NULL;
- int len, ilen, oalen;
+ char *oauthbearer = NULL;
+ int ilen;
int rc;
mutt_message(_("Authenticating (OAUTHBEARER)..."));
- /* get auth info */
- if (mutt_account_getlogin(&conn->account))
+ /* We get the access token from the smtp_oauth_refresh_command */
+ oauthbearer = mutt_account_getoauthbearer(&conn->account);
+ if (oauthbearer == NULL)
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 = mutt_mem_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;
+ ilen = strlen(oauthbearer) + 30;
ibuf = mutt_mem_malloc(ilen);
- ibuf[0] = '\0';
-
- mutt_str_strcat(ibuf, ilen, "AUTH OAUTHBEARER ");
- len = strlen(ibuf);
-
- mutt_b64_encode(oauth_buf, strlen(oauth_buf), (ibuf + len), ilen - len);
- mutt_str_strcat(ibuf, ilen, "\r\n");
+ snprintf(ibuf, ilen, "AUTH OAUTHBEARER %s\r\n", oauthbearer);
rc = mutt_socket_send(conn, ibuf);
- FREE(&oauth_buf);
+ FREE(&oauthbearer);
FREE(&ibuf);
if (rc == -1)