From f12bfecb345d42bc3b57e76b8f2e71548ceefc16 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sat, 29 Aug 2020 18:58:30 -0600 Subject: * When a server expires a refresh token, Alpine needs to cancel it internally. * Fixes to some prototyping for code related to authentication. * Fixes to documentation contributed by Dennis Davis. --- alpine/imap.c | 42 ++++++++++++++++++++++++++++-------------- alpine/osdep/termout.wnt.c | 8 ++++---- alpine/osdep/termout.wnt.h | 2 +- imap/src/c-client/auth_bea.c | 3 ++- imap/src/c-client/auth_oa2.c | 5 +++-- imap/src/c-client/mail.h | 1 + imap/src/c-client/oauth2_aux.c | 15 +++++++++++---- pith/pine.hlp | 11 +++++++---- 8 files changed, 57 insertions(+), 30 deletions(-) diff --git a/alpine/imap.c b/alpine/imap.c index 7c518cf6..f1654dec 100644 --- a/alpine/imap.c +++ b/alpine/imap.c @@ -172,7 +172,8 @@ OAUTH2_S alpine_oauth2_list[] = NULL, /* access token */ 0, /* expiration time */ 0, /* first time indicator */ - 1 /* client secret required */ + 1, /* client secret required */ + 0 /* Cancel refresh token */ }, {OUTLOOK_NAME, {"outlook.office365.com", "smtp.office365.com", NULL, NULL}, @@ -201,7 +202,8 @@ OAUTH2_S alpine_oauth2_list[] = NULL, /* access token */ 0, /* expiration time */ 0, /* first time indicator */ - 0 /* client secret required */ + 0, /* client secret required */ + 0 /* Cancel refresh token */ }, {OUTLOOK_NAME, {"outlook.office365.com", "smtp.office365.com", NULL, NULL}, @@ -230,7 +232,8 @@ OAUTH2_S alpine_oauth2_list[] = NULL, /* access token */ 0, /* expiration time */ 0, /* first time indicator */ - 1 /* client secret required */ + 1, /* client secret required */ + 0 /* Cancel refresh token */ }, {YANDEX_NAME, {"imap.yandex.com", "smtp.yandex.com", NULL, NULL}, @@ -259,9 +262,10 @@ OAUTH2_S alpine_oauth2_list[] = NULL, /* access token */ 0, /* expiration time */ 0, /* first time indicator */ - 1 /* client secret required */ + 1, /* client secret required */ + 0 /* Cancel refresh token */ }, - { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0}, + { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0}, }; int @@ -292,7 +296,7 @@ OAUTH2_S * oauth2_select_flow(char *host) { OAUTH2_S *oa2list, *oa2; - int i, n, rv; + int i, rv; char *method; if(ps_global->ttyo){ @@ -350,7 +354,7 @@ oauth2_select_flow(char *host) char *s; char prompt[1024]; char reply[1024]; - int sel, j; + int sel, n = 0, j; for(oa2list = alpine_oauth2_list; oa2list && oa2list->name ;oa2list++) n += strlen(oa2list->name); + 5; /* number, parenthesis, space */ @@ -1011,7 +1015,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, user[NETMAXUSER-1] = '\0'; /* The Old* variables is what c_client knows */ - OldRefreshToken = login->param[OA2_RefreshToken].value; + OldRefreshToken = login->cancel_refresh_token ? NULL : login->param[OA2_RefreshToken].value; OldAccessToken = login->access_token; OldExpirationTime = login->expiration; @@ -1020,7 +1024,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, NewExpirationTime = 0L; ChangeAccessToken = ChangeRefreshToken = ChangeExpirationTime = 0; - if(token && *token){ + if(token && *token && !login->cancel_refresh_token){ char *s, *t; s = token; @@ -1049,7 +1053,9 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, if(NewAccessToken && (NewExpirationTime == 0L || !*NewAccessToken)) fs_give((void **) &NewAccessToken); } - else login->first_time++; + + if(NewRefreshToken == NULL) + login->first_time++; if(login->first_time){ /* count how many authorization methods we support */ int nmethods, i, j; @@ -1071,7 +1077,7 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, /* Default to saving what we already had saved */ - SaveRefreshToken = NewRefreshToken; + SaveRefreshToken = login->cancel_refresh_token ? NULL : NewRefreshToken; SaveAccessToken = NewAccessToken; SaveExpirationTime = NewExpirationTime; @@ -1131,10 +1137,11 @@ mm_login_oauth2(NETMBX *mb, char *user, char *method, oa2list->access_token = SaveAccessToken; oa2list->expiration = SaveExpirationTime; oa2list->first_time = login->first_time; - *login = *oa2list; /* load login pointer */ + oa2list->cancel_refresh_token = login->cancel_refresh_token; + *login = *oa2list; /* load login pointer */ } - if(!ChangeAccessToken && !ChangeRefreshToken) + if(!ChangeAccessToken && !ChangeRefreshToken && !login->cancel_refresh_token) return; /* get ready to save this information. The format will be @@ -2027,8 +2034,15 @@ mm_login_work(NETMBX *mb, char *user, char **pwd, long int trial, flags = F_ON(F_QUELL_ASTERISKS, ps_global) ? OE_PASSWD_NOAST : OE_PASSWD; flags |= OE_KEEP_TRAILING_SPACE; #ifdef _WINDOWS - rc = os_login_dialog(mb, user, NETMAXUSER, tmp, NETMAXPASSWD, 0, 1, + { + char *tmpp; + tmpp = fs_get(NETMAXPASSWD*sizeof(char)); + rc = os_login_dialog(mb, user, NETMAXUSER, &tmpp, NETMAXPASSWD, 0, 1, &preserve_password); + strncpy(tmp, tmpp, sizeof(tmp)); + tmp[sizeof(tmp)-1] = '\0'; + if(tmpp) fs_give((void **)&tmpp); + } #else /* !_WINDOWS */ rc = optionally_enter(tmp, q_line, 0, NETMAXPASSWD, prompt, NULL, help, &flags); diff --git a/alpine/osdep/termout.wnt.c b/alpine/osdep/termout.wnt.c index 778890b7..95dec41b 100644 --- a/alpine/osdep/termout.wnt.c +++ b/alpine/osdep/termout.wnt.c @@ -737,7 +737,7 @@ os_argsdialog (char **arg_text) */ int os_login_dialog (NETMBX *mb, char *user_utf8, int userlen, - char *pwd_utf8, int pwdlen, int pwc, int fixuser, int *prespass) + char **pwd_utf8, int pwdlen, int pwc, int fixuser, int *prespass) { DLGPROC dlgprc; HINSTANCE hInst; @@ -760,7 +760,7 @@ os_login_dialog (NETMBX *mb, char *user_utf8, int userlen, dlgpw.userlen = userlen; dlgpw.pwd = (LPTSTR)fs_get(pwdlen*sizeof(TCHAR)); - pwd_lptstr = utf8_to_lptstr(pwd_utf8); + pwd_lptstr = utf8_to_lptstr(*pwd_utf8); _tcsncpy(dlgpw.pwd, pwd_lptstr, pwdlen - 1); dlgpw.pwd[pwdlen - 1] = '\0'; fs_give((void **) &pwd_lptstr); @@ -786,8 +786,8 @@ os_login_dialog (NETMBX *mb, char *user_utf8, int userlen, tpwd_utf8 = lptstr_to_utf8(dlgpw.pwd); if(tpwd_utf8){ - strncpy(pwd_utf8, tpwd_utf8, pwdlen - 1); - pwd_utf8[pwdlen - 1] = '\0'; + strncpy(*pwd_utf8, tpwd_utf8, pwdlen - 1); + (*pwd_utf8)[pwdlen - 1] = '\0'; fs_give((void **) &tpwd_utf8); } if(prespass) diff --git a/alpine/osdep/termout.wnt.h b/alpine/osdep/termout.wnt.h index 7344de6f..1993a6b5 100644 --- a/alpine/osdep/termout.wnt.h +++ b/alpine/osdep/termout.wnt.h @@ -39,7 +39,7 @@ void scroll_setrange(long, long); /* dialog stuff */ int init_install_get_vars(void); int os_argsdialog(char **); -int os_login_dialog(NETMBX *, char *, int, char *, int, int, int, int *); +int os_login_dialog(NETMBX *, char *, int, char **, int, int, int, int *); int os_flagmsgdialog(struct flag_table *); int os_sortdialog(DLG_SORTPARAM *); int os_config_dialog(char *, int, int *, int); diff --git a/imap/src/c-client/auth_bea.c b/imap/src/c-client/auth_bea.c index e5800427..64c3aa6c 100644 --- a/imap/src/c-client/auth_bea.c +++ b/imap/src/c-client/auth_bea.c @@ -99,7 +99,8 @@ long auth_oauthbearer_client (authchallenge_t challenger,authrespond_t responder && (oauth2.access_token || (!RefreshToken && oauth2.param[OA2_RefreshToken].value) || (RefreshToken && oauth2.param[OA2_RefreshToken].value - && strcmp(RefreshToken, oauth2.param[OA2_RefreshToken].value)))) + && strcmp(RefreshToken, oauth2.param[OA2_RefreshToken].value) + || oauth2.cancel_refresh_token))) mm_login_method (mb, user, (void *) &oauth2, *trial, BEARERNAME); } diff --git a/imap/src/c-client/auth_oa2.c b/imap/src/c-client/auth_oa2.c index f5345bfa..dac87960 100644 --- a/imap/src/c-client/auth_oa2.c +++ b/imap/src/c-client/auth_oa2.c @@ -124,10 +124,11 @@ long auth_oauth2_client (authchallenge_t challenger,authrespond_t responder, cha */ if(!tryanother - && (oauth2.access_token + && (oauth2.access_token || (!RefreshToken && oauth2.param[OA2_RefreshToken].value) || (RefreshToken && oauth2.param[OA2_RefreshToken].value - && strcmp(RefreshToken, oauth2.param[OA2_RefreshToken].value)))) + && strcmp(RefreshToken, oauth2.param[OA2_RefreshToken].value) + || oauth2.cancel_refresh_token))) mm_login_method (mb, user, (void *) &oauth2, *trial, OA2NAME); } diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h index 9a02c5f9..13bbc84f 100644 --- a/imap/src/c-client/mail.h +++ b/imap/src/c-client/mail.h @@ -1989,6 +1989,7 @@ typedef struct oauth2_s { unsigned long expiration; unsigned int first_time:1; /* this is the first time we get credentials for this account */ unsigned int require_secret:1; /* this server requires a client-secret */ + int cancel_refresh_token; /* ask client to cancel refresh token */ } OAUTH2_S; typedef struct deviceproc_s { diff --git a/imap/src/c-client/oauth2_aux.c b/imap/src/c-client/oauth2_aux.c index 6fab2917..6b7c401b 100644 --- a/imap/src/c-client/oauth2_aux.c +++ b/imap/src/c-client/oauth2_aux.c @@ -223,11 +223,13 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, case JLong : oauth2->expiration = time(0) + *(long *) jx->value; break; } + oauth2->cancel_refresh_token = 0; /* do not cancel this token. It is good */ break; default : { char tmp[100]; - sprintf(tmp, "Oauth2 client Received Code %d", status); - mm_log (tmp, ERROR); + sprintf(tmp, "Oauth2 client Received Code %d", status); + mm_log (tmp, ERROR); + oauth2->cancel_refresh_token++; } break; } @@ -284,13 +286,16 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, if(jx && jx->jtype == JString) oauth2->expiration = time(0) + atol((char *) jx->value); + oauth2->cancel_refresh_token = 0; /* do not cancel this token. It is good */ + break; case HTTP_BAD : break; default : { char tmp[100]; sprintf(tmp, "Oauth2 Client Received Code %d", status); - fatal (tmp); + mm_log (tmp, ERROR); + oauth2->cancel_refresh_token++; } } @@ -356,13 +361,15 @@ void oauth2deviceinfo_get_accesscode(void *inp, void *outp) } rv = OA2_CODE_SUCCESS; + oauth2->cancel_refresh_token = 0; /* do not cancel this token. It is good */ break; default : { char tmp[100]; sprintf(tmp, "Oauth device Received Code %d", status); mm_log (tmp, ERROR); - } + oauth2->cancel_refresh_token++; + } } json_free(&json); diff --git a/pith/pine.hlp b/pith/pine.hlp index d4810a6d..8412fcaa 100644 --- a/pith/pine.hlp +++ b/pith/pine.hlp @@ -140,7 +140,7 @@ with help text for the config screen and the composer that didn't have any reasonable place to be called from. Dummy change to get revision in pine.hlp ============= h_revision ================= -Alpine Commit 504 2020-08-27 20:15:43 +Alpine Commit 505 2020-08-29 18:58:26 ============= h_news ================= @@ -250,8 +250,12 @@ problems you find with this release. which allow a user to specify locations for certificates that the user trusts. -
  • Ignore non-empty initial challenge in the GSSAPI authenticaor. Based +
  • Ignore non-empty initial challenge in the GSSAPI authenticator. Based on a patch written by Jarek Polok, but submitted by Ignacio Reguero. + +
  • When a server expires a refresh token, Alpine needs to cancel it + internally. Alpine will attempt to get a new one when it reopens the + folder after it cancels it.

    @@ -293,8 +297,7 @@ Bugs addressed:

  • Crash in PC-Alpine when using the eXternal command.
  • Fix in Macs that made Alpine abort a ssh connection to an imap server. - Reported and asisted by Wang Kang. - + Reported and assisted by Wang Kang.

    -- cgit v1.2.3-54-g00ecf