From 7d652142f4960b679cb5399fc0485470af2cc878 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 21 Nov 2021 02:19:32 -0700 Subject: * Support for code_verifier and code_challenge when generating a refresh token and access token in Gmail using the S256 method and plain method. --- alpine/xoauth2info.c | 84 ++++++++++++++++--------- imap/src/c-client/auth_bea.c | 10 ++- imap/src/c-client/auth_oa2.c | 10 ++- imap/src/c-client/hash.h | 2 +- imap/src/c-client/http.c | 4 +- imap/src/c-client/mail.h | 12 +++- imap/src/c-client/oauth2_aux.c | 140 +++++++++++++++++++++++++++++++++++------ imap/src/c-client/sha.c | 6 +- pith/pine.hlp | 6 +- 9 files changed, 204 insertions(+), 70 deletions(-) diff --git a/alpine/xoauth2info.c b/alpine/xoauth2info.c index 8a518f47..79d43e6e 100644 --- a/alpine/xoauth2info.c +++ b/alpine/xoauth2info.c @@ -40,15 +40,19 @@ OAUTH2_S alpine_oauth2_list[] = { {"grant_type", "refresh_token"}, {"response_type", "code"}, {"state", NULL}, - {"device_code", NULL} /* not used */ + {"device_code", NULL}, /* not used */ + {"code_verifier", NULL}, /* OA2_Extra1: code_verifier, generated by c-client */ + {"code_challenge", NULL}, /* OA2_Extra2: code_challenge, genrated by c-client */ + {"code_challenge_method", "S256"}, /* OA2_Extra3: code_challenge_method, given by Alpine */ + {"login_hint", NULL} /* OA2_Extra4: username, provided by Alpine, filled up by c-client */ }, {{"GET", (unsigned char *) "https://accounts.google.com/o/oauth2/auth", /* authorization address, get access code */ - {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End}}, - {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* Device Info information, not used */ + {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_Extra2, OA2_Extra3, OA2_Extra4, OA2_End}}, + {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* Device Info information, not used */ {"POST", (unsigned char *) "https://accounts.google.com/o/oauth2/token", /* Address to get refresh token from access code */ - {OA2_Id, OA2_Secret, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Code, OA2_End, OA2_End}}, + {OA2_Id, OA2_Secret, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Code, OA2_Extra1, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://accounts.google.com/o/oauth2/token", /* access token from refresh token */ - {OA2_Id, OA2_Secret, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End}} + {OA2_Id, OA2_Secret, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End, OA2_End}} }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information */ NULL, /* access token */ @@ -73,15 +77,19 @@ OAUTH2_S alpine_oauth2_list[] = { {"grant_type", "refresh_token"}, {"response_type", "code"}, /* not used */ {"state", NULL}, /* not used */ - {"device_code", NULL} /* only used for frst time set up */ + {"device_code", NULL}, /* only used for frst time set up */ + {"response_mode", "query"}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL} /* Extra parameter for this server */ }, - {{NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* Get Access Code, Not used */ + {{NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* Get Access Code, Not used */ {"POST", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/devicecode", /* first time use and get device code information */ - {OA2_Id, OA2_Scope, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, + {OA2_Id, OA2_Scope, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get first Refresh Token and Access token */ - {OA2_Id, OA2_Redirect, OA2_DeviceCode, OA2_End, OA2_End, OA2_End, OA2_End}}, + {OA2_Id, OA2_Redirect, OA2_DeviceCode, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get access token from refresh token */ - {OA2_Id, OA2_RefreshToken, OA2_Scope, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End}} + {OA2_Id, OA2_RefreshToken, OA2_Scope, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End, OA2_End}} }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information */ NULL, /* access token */ @@ -106,15 +114,19 @@ OAUTH2_S alpine_oauth2_list[] = { {"grant_type", "refresh_token"}, {"response_type", "code"}, {"state", NULL}, /* not used */ - {"device_code", NULL} /* not used */ + {"device_code", NULL}, /* not used */ + {"response_mode", "query"}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL} /* Extra parameter for this server */ }, {{"GET", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/authorize", /* Get Access Code */ - {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End}}, - {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ + {OA2_Id, OA2_Scope, OA2_Redirect, OA2_Response, OA2_Extra1, OA2_End, OA2_End, OA2_End}}, + {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ {"POST", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get first Refresh Token and Access token */ - {OA2_Id, OA2_Redirect, OA2_Scope, OA2_GrantTypeforAccessToken, OA2_Secret, OA2_Code, OA2_End}}, + {OA2_Id, OA2_Redirect, OA2_Scope, OA2_GrantTypeforAccessToken, OA2_Secret, OA2_Code, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://login.microsoftonline.com/\001/oauth2/v2.0/token", /* Get access token from refresh token */ - {OA2_Id, OA2_RefreshToken, OA2_Scope, OA2_GrantTypefromRefreshToken, OA2_Secret, OA2_End, OA2_End}} + {OA2_Id, OA2_RefreshToken, OA2_Scope, OA2_GrantTypefromRefreshToken, OA2_Secret, OA2_End, OA2_End, OA2_End}} }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information, not used */ NULL, /* access token */ @@ -139,15 +151,19 @@ OAUTH2_S alpine_oauth2_list[] = { {"grant_type", "refresh_token"}, {"response_type", "code"}, {"state", NULL}, /* used */ - {"device_code", NULL} /* not used */ + {"device_code", NULL}, /* not used */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL} /* Extra parameter for this server */ }, {{"GET", (unsigned char *) "https://api.login.yahoo.com/oauth2/request_auth", /* Get Access Code */ - {OA2_Id, OA2_Redirect, OA2_Response, OA2_State, OA2_End, OA2_End, OA2_End}}, - {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ + {OA2_Id, OA2_Redirect, OA2_Response, OA2_State, OA2_End, OA2_End, OA2_End, OA2_End}}, + {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ {"POST", (unsigned char *) "https://api.login.yahoo.com/oauth2/get_token", /* Get first Refresh Token and Access token */ - {OA2_Id, OA2_Secret, OA2_Redirect, OA2_Code, OA2_GrantTypeforAccessToken, OA2_End, OA2_End}}, + {OA2_Id, OA2_Secret, OA2_Redirect, OA2_Code, OA2_GrantTypeforAccessToken, OA2_End, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://api.login.yahoo.com/oauth2/get_token", /* Get access token from refresh token */ - {OA2_Id, OA2_Secret, OA2_Redirect, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End}} + {OA2_Id, OA2_Secret, OA2_Redirect, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_End, OA2_End, OA2_End}} }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information, not used */ NULL, /* access token */ @@ -172,15 +188,19 @@ OAUTH2_S alpine_oauth2_list[] = { {"grant_type", "refresh_token"}, {"response_type", "code"}, {"state", NULL}, /* not used */ - {"device_code", NULL} /* not used */ + {"device_code", NULL}, /* not used */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL} /* Extra parameter for this server */ }, {{"GET", (unsigned char *) "https://oauth.yandex.com/authorize", /* Get Access Code */ - {OA2_Id, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End, OA2_End}}, - {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ + {OA2_Id, OA2_Redirect, OA2_Response, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, + {NULL, NULL, {OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End, OA2_End}}, /* device code, not used */ {"POST", (unsigned char *) "https://oauth.yandex.com/token", /* Get first Refresh Token and Access token */ - {OA2_Id, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Secret, OA2_Code, OA2_End, OA2_End}}, + {OA2_Id, OA2_Redirect, OA2_GrantTypeforAccessToken, OA2_Secret, OA2_Code, OA2_End, OA2_End, OA2_End}}, {"POST", (unsigned char *) "https://oauth.yandex.com/token", /* Get access token from refresh token */ - {OA2_Id, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_Secret, OA2_End, OA2_End, OA2_End}} + {OA2_Id, OA2_RefreshToken, OA2_GrantTypefromRefreshToken, OA2_Secret, OA2_End, OA2_End, OA2_End, OA2_End}} }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information, not used */ NULL, /* access token */ @@ -205,12 +225,16 @@ OAUTH2_S alpine_oauth2_list[] = { {NULL, NULL}, /* grant type from refresh token */ {NULL, NULL}, /* response */ {NULL, NULL}, /* state */ - {NULL, NULL} /* device code */ + {NULL, NULL}, /* device code */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL}, /* Extra parameter for this server */ + {NULL, NULL} /* Extra parameter for this server */ }, - {{NULL, NULL, {0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for authorize */ - {NULL, NULL, {0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for device login */ - {NULL, NULL, {0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for getting refresh token */ - {NULL, NULL, {0, 0, 0, 0, 0, 0, 0}} /* method, server, parameters for refreshing access token */ + {{NULL, NULL, {0, 0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for authorize */ + {NULL, NULL, {0, 0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for device login */ + {NULL, NULL, {0, 0, 0, 0, 0, 0, 0, 0}}, /* method, server, parameters for getting refresh token */ + {NULL, NULL, {0, 0, 0, 0, 0, 0, 0, 0}} /* method, server, parameters for refreshing access token */ }, {NULL, NULL, NULL, 0, 0, NULL}, /* device_code information */ NULL, /* access token */ diff --git a/imap/src/c-client/auth_bea.c b/imap/src/c-client/auth_bea.c index f9e8e190..7848a8fc 100644 --- a/imap/src/c-client/auth_bea.c +++ b/imap/src/c-client/auth_bea.c @@ -87,6 +87,9 @@ long auth_oauthbearer_client (authchallenge_t challenger,authrespond_t responder oauth2.param[OA2_State].value = oauth2_generate_state(); + oauth2_code_challenge(&oauth2); + + oauth2_login_hint(&oauth2, user); /* * If we did not get an access token, try to get one through * our internal functions @@ -157,11 +160,6 @@ long auth_oauthbearer_client (authchallenge_t challenger,authrespond_t responder *trial = 65535; /* don't retry if bad protocol */ } } - if(oauth2.param[OA2_Id].value) fs_give((void **) &oauth2.param[OA2_Id].value); - if(oauth2.param[OA2_Secret].value) fs_give((void **) &oauth2.param[OA2_Secret].value); - if(oauth2.param[OA2_Tenant].value) fs_give((void **) &oauth2.param[OA2_Tenant].value); - if(oauth2.param[OA2_State].value) fs_give((void **) &oauth2.param[OA2_State].value); - if(oauth2.param[OA2_RefreshToken].value) fs_give((void **) &oauth2.param[OA2_RefreshToken].value); - if(oauth2.access_token) fs_give((void **) &oauth2.access_token); + oauth2_free_extra_values(oauth2); return ret; } diff --git a/imap/src/c-client/auth_oa2.c b/imap/src/c-client/auth_oa2.c index 9081a1e3..45156e95 100644 --- a/imap/src/c-client/auth_oa2.c +++ b/imap/src/c-client/auth_oa2.c @@ -107,6 +107,9 @@ long auth_oauth2_client (authchallenge_t challenger,authrespond_t responder, cha oauth2.param[OA2_State].value = oauth2_generate_state(); + oauth2_code_challenge(&oauth2); + + oauth2_login_hint(&oauth2, user); /* * If we did not get an access token, try to get one through * our internal functions @@ -168,11 +171,6 @@ long auth_oauth2_client (authchallenge_t challenger,authrespond_t responder, cha *trial = 65535; /* don't retry if bad protocol */ } } - if(oauth2.param[OA2_Id].value) fs_give((void **) &oauth2.param[OA2_Id].value); - if(oauth2.param[OA2_Secret].value) fs_give((void **) &oauth2.param[OA2_Secret].value); - if(oauth2.param[OA2_Tenant].value) fs_give((void **) &oauth2.param[OA2_Tenant].value); - if(oauth2.param[OA2_State].value) fs_give((void **) &oauth2.param[OA2_State].value); - if(oauth2.param[OA2_RefreshToken].value) fs_give((void **) &oauth2.param[OA2_RefreshToken].value); - if(oauth2.access_token) fs_give((void **) &oauth2.access_token); + oauth2_free_extra_values(oauth2); return ret; } diff --git a/imap/src/c-client/hash.h b/imap/src/c-client/hash.h index 4e796429..f7b6761e 100644 --- a/imap/src/c-client/hash.h +++ b/imap/src/c-client/hash.h @@ -1,6 +1,6 @@ #ifndef HASH_H_INCLUDED #define HASH_H_INCLUDED -char *hash_from_sizedtext(char *, char *, size_t); +char *hash_from_sizedtext(char *, char *, size_t, unsigned char **); #endif /* HASH_H_INCLUDED */ diff --git a/imap/src/c-client/http.c b/imap/src/c-client/http.c index 617b457a..b02f027e 100644 --- a/imap/src/c-client/http.c +++ b/imap/src/c-client/http.c @@ -832,7 +832,7 @@ hex_escape_url_part(unsigned char *text, unsigned char *addsafe) unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t; *s = '\0'; - for(t = text; *t != '\0'; t++) + for(t = text; t != NULL && *t != '\0'; t++) if(strchr(safechars, *t) != NULL || (addsafe != NULL && strchr(addsafe, *t) != NULL)) sprintf(s + strlen(s), "%c", *t); @@ -850,7 +850,7 @@ encode_url_body_part(unsigned char *text, unsigned char *addsafe) unsigned char *s = fs_get((3*strlen(text) + 1)*sizeof(char)), *t; *s = '\0'; - for(t = text; *t != '\0'; t++) + for(t = text; t != NULL && *t != '\0'; t++) if(*t == ' ') /* ASCII 32 is never safe, must always be encoded */ sprintf(s + strlen(s), "%c", '+'); else if(strchr(safechars, *t) != NULL diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h index 94ac64fa..6e429b00 100644 --- a/imap/src/c-client/mail.h +++ b/imap/src/c-client/mail.h @@ -1946,7 +1946,7 @@ int PFLUSH (void); #define OAUTH2_MAX_EQUIV (2) #define OAUTH2_TOT_EQUIV (OAUTH2_MAX_EQUIV + 2) -#define OAUTH2_PARAM_NUMBER (7) +#define OAUTH2_PARAM_NUMBER (8) #define OA2_UNKNOWN 0x00 /* We do not know what method this client-id uses */ #define OA2_DEVICE 0x01 /* Client ID obtained by client uses Device Method */ @@ -1963,7 +1963,11 @@ typedef enum {OA2_Id = 0, OA2_GrantTypefromRefreshToken, OA2_Response, OA2_State, - OA2_DeviceCode, + OA2_DeviceCode, /* code assumes that the list below is last in this list */ + OA2_Extra1, /* server unique parameter for authentication */ + OA2_Extra2, /* server unique parameter for authentication */ + OA2_Extra3, /* server unique parameter for authentication */ + OA2_Extra4, /* server unique parameter for authentication */ OA2_End} OA2_type; typedef enum {OA2_GetAccessCode = 0, /* define this to get access code */ @@ -2030,7 +2034,6 @@ typedef char *(*oauth2getaccesscode_t) (unsigned char *, char *, OAUTH2_S *, int typedef XOAUTH2_INFO_S *(*oauth2clientinfo_t)(unsigned char *name, char *user); typedef void (*oauth2deviceinfo_t)(OAUTH2_S *, char *method); void mm_login_oauth2_c_client_method (NETMBX *, char *, char *, OAUTH2_S *, unsigned long, int *); -char *oauth2_generate_state(void); void oauth2deviceinfo_get_accesscode(void *, void *); XOAUTH2_INFO_S *new_xoauth2_info(void); void free_xoauth2_info(XOAUTH2_INFO_S **); @@ -2038,5 +2041,8 @@ void free_xoauth2_info_list(XOAUTH2_INFO_S ***); XOAUTH2_INFO_S *copy_xoauth2_info(XOAUTH2_INFO_S *); int same_xoauth2_info(XOAUTH2_INFO_S, XOAUTH2_INFO_S); char *oauth2_generate_state(void); +void oauth2_code_challenge(OAUTH2_S *); +void oauth2_free_extra_values(OAUTH2_S); +void oauth2_login_hint(OAUTH2_S *, char *); void renew_accesstoken(MAILSTREAM *); XOAUTH2_INFO_S **parse_xoauth2_info_list(char **, int *); diff --git a/imap/src/c-client/oauth2_aux.c b/imap/src/c-client/oauth2_aux.c index 363f41d1..eac92a10 100644 --- a/imap/src/c-client/oauth2_aux.c +++ b/imap/src/c-client/oauth2_aux.c @@ -24,6 +24,125 @@ #include "json.h" #include "oauth2_aux.h" +OA2_type oauth2_find_extra_parameter(OAUTH2_S *, char *); +JSON_S *oauth2_json_reply(OAUTH2_SERVER_METHOD_S, OAUTH2_S *, int *); +char *xoauth2_server(char *, char *); + +#define LOAD_HTTP_PARAMS(X, Y) { \ + int i; \ + for(i = 0; (X).params[i] != OA2_End; i++){ \ + OA2_type j = (X).params[i]; \ + (Y)[i].name = oauth2->param[j].name; \ + (Y)[i].value = oauth2->param[j].value; \ + } \ + (Y)[i].name = (Y)[i].value = NULL; \ +} + +#define OAUTH2_CLEAR_EXTRA(X, Y) do { \ + OA2_type i = oauth2_find_extra_parameter(&(X), (Y)); \ + if(i < OA2_End && (X).param[i].value) \ + fs_give((void **) &(X).param[i].value); \ +} while(0) + +void oauth2_free_extra_values(OAUTH2_S oauth2) +{ + OA2_type i; + + if(oauth2.param[OA2_Id].value) fs_give((void **) &oauth2.param[OA2_Id].value); + if(oauth2.param[OA2_Secret].value) fs_give((void **) &oauth2.param[OA2_Secret].value); + if(oauth2.param[OA2_Tenant].value) fs_give((void **) &oauth2.param[OA2_Tenant].value); + if(oauth2.param[OA2_State].value) fs_give((void **) &oauth2.param[OA2_State].value); + if(oauth2.param[OA2_RefreshToken].value) fs_give((void **) &oauth2.param[OA2_RefreshToken].value); + if(oauth2.access_token) fs_give((void **) &oauth2.access_token); + + /* free extra parameters generated by us */ + OAUTH2_CLEAR_EXTRA(oauth2, "code_verifier"); + OAUTH2_CLEAR_EXTRA(oauth2, "code_challenge"); + OAUTH2_CLEAR_EXTRA(oauth2, "login_hint"); +} + +OA2_type oauth2_find_extra_parameter(OAUTH2_S *oauth2, char *name) +{ + OA2_type i; + + if(!name) return OA2_End; + + for(i = OA2_Extra1; i < OA2_End; i++){ + if(oauth2->param[i].name + && !compare_cstring(oauth2->param[i].name, name)) + break; + } + return i; +} + +/* code_challenge generator */ +void oauth2_code_challenge(OAUTH2_S *oauth2) +{ + OA2_type i, j, k; + char *cv, *cv1, *cv2; + + i = oauth2_find_extra_parameter(oauth2, "code_verifier"); + + if(i == OA2_End) return; + + j = oauth2_find_extra_parameter(oauth2, "code_challenge"); + + if(j == OA2_End) return; + + k = oauth2_find_extra_parameter(oauth2, "code_challenge_method"); + + cv1 = oauth2_generate_state(); + cv2 = oauth2_generate_state(); + + if(!cv1 || !cv2) return; + + if(oauth2->param[i].value) fs_give((void **) &oauth2->param[i].value); + if(oauth2->param[j].value) fs_give((void **) &oauth2->param[j].value); + + cv = fs_get(strlen(cv1) + strlen(cv2) + 1 + 1); + if(cv){ + sprintf(cv, "%s-%s", cv1, cv2); + fs_give((void **) &cv1); + fs_give((void **) &cv2); + oauth2->param[i].value = cv; + if(k == OA2_End + || !compare_cstring(oauth2->param[k].value, "plain")) + oauth2->param[j].value = cpystr(cv); + else if(!compare_cstring(oauth2->param[k].value, "S256")){ + unsigned char *t, *u, *v; + char *s = hash_from_sizedtext("SHA256", cv, strlen(cv), &t); + if(s){ + u = v = t; + if(t){ + for(; *v != '\0'; v++){ + if(*v < 0x20) continue; + *u++ = *v; + } + *u = '\0'; + oauth2->param[j].value = t; + } + fs_give((void **) &s); + } + } + } +} + +/* code_challenge generator */ +void oauth2_login_hint(OAUTH2_S *oauth2, char *user) +{ + OA2_type i, j, k; + char *cv, *cv1, *cv2; + + if(!user || !*user) return; + + i = oauth2_find_extra_parameter(oauth2, "login_hint"); + + if(i == OA2_End) return; + + if(oauth2->param[i].value) fs_give((void **) &oauth2->param[i].value); + oauth2->param[i].value = cpystr(user); +} + /* we generate something like a guid, but not care about * anything, but that it is really random. */ @@ -51,20 +170,6 @@ char *oauth2_generate_state(void) return cpystr(rv); } - -JSON_S *oauth2_json_reply(OAUTH2_SERVER_METHOD_S, OAUTH2_S *, int *); -char *xoauth2_server(char *, char *); - -#define LOAD_HTTP_PARAMS(X, Y) { \ - int i; \ - for(i = 0; (X).params[i] != OA2_End; i++){ \ - OA2_type j = (X).params[i]; \ - (Y)[i].name = oauth2->param[j].name; \ - (Y)[i].value = oauth2->param[j].value; \ - } \ - (Y)[i].name = (Y)[i].value = NULL; \ -} - char * xoauth2_server(char *server, char *tenant) { @@ -483,10 +588,5 @@ void renew_accesstoken(MAILSTREAM *stream) mm_login_method (&mb, user, (void *) &oauth2, trial, stream->auth.name); stream->auth.expiration = oauth2.expiration; - if(oauth2.param[OA2_Id].value) fs_give((void **) &oauth2.param[OA2_Id].value); - if(oauth2.param[OA2_Secret].value) fs_give((void **) &oauth2.param[OA2_Secret].value); - if(oauth2.param[OA2_Tenant].value) fs_give((void **) &oauth2.param[OA2_Tenant].value); - if(oauth2.param[OA2_State].value) fs_give((void **) &oauth2.param[OA2_State].value); - if(oauth2.param[OA2_RefreshToken].value) fs_give((void **) &oauth2.param[OA2_RefreshToken].value); - if(oauth2.access_token) fs_give((void **) &oauth2.access_token); + oauth2_free_extra_values(oauth2); } diff --git a/imap/src/c-client/sha.c b/imap/src/c-client/sha.c index 3c4b43c3..8942299e 100644 --- a/imap/src/c-client/sha.c +++ b/imap/src/c-client/sha.c @@ -32,13 +32,16 @@ struct hash { }; static const char hexdigits[] = "0123456789abcdef"; -char *hash_from_sizedtext(char *hash, char *text, size_t len) +char *hash_from_sizedtext(char *hash, char *text, size_t len, unsigned char **digest) { char *rv = NIL; USHAContext sha; HMACContext hmac; uint8_t Message_Digest[USHAMaxHashSize]; int i, hashno; + unsigned long len2; + + if(digest) *digest = NIL; if(!hash || !text) return NIL; @@ -52,6 +55,7 @@ char *hash_from_sizedtext(char *hash, char *text, size_t len) if(USHAReset(&sha, hashes[hashno].whichSha) == shaSuccess && USHAInput(&sha, (const uint8_t *) text, len) == shaSuccess){ if(USHAResult(&sha, (uint8_t *) Message_Digest) == shaSuccess){ + if(digest) *digest = rfc822_urlbinary((void *) Message_Digest, hashes[hashno].hashsize, &len2); rv = fs_get(2*hashes[hashno].hashsize + 1); for (i = 0; i < hashes[hashno].hashsize ; ++i) { rv[2*i] = hexdigits[(Message_Digest[i] >> 4) & 0xF]; diff --git a/pith/pine.hlp b/pith/pine.hlp index c212e5e0..2141748e 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 616 2021-11-20 12:54:44 +Alpine Commit 617 2021-11-21 02:19:27 ============= h_news ================= @@ -193,6 +193,10 @@ new additions to Alpine, please check it periodically. external browser for display using the external command, and now it will. +
  • Support for code_verifier and code_challenge when generating a + refresh token and access token in Gmail using the S256 method + and plain method. +
  • Some servers do not allow the Drafts folder to be removed, even when it is empty. Alpine, however, assumes that if the folder exists, it must contain a draft message. This joint collaboration with Thomas -- cgit v1.2.3-54-g00ecf