summaryrefslogtreecommitdiff
path: root/imap
diff options
context:
space:
mode:
authorEduardo Chappa <chappa@washington.edu>2021-11-21 02:19:32 -0700
committerEduardo Chappa <chappa@washington.edu>2021-11-21 02:19:32 -0700
commit7d652142f4960b679cb5399fc0485470af2cc878 (patch)
tree7afa6235429b5fdb835586bf671fa532b37281c3 /imap
parent626eccdd8d0a325842d736596348e9d1d81ca105 (diff)
downloadalpine-7d652142f4960b679cb5399fc0485470af2cc878.tar.xz
* Support for code_verifier and code_challenge when generating a
refresh token and access token in Gmail using the S256 method and plain method.
Diffstat (limited to 'imap')
-rw-r--r--imap/src/c-client/auth_bea.c10
-rw-r--r--imap/src/c-client/auth_oa2.c10
-rw-r--r--imap/src/c-client/hash.h2
-rw-r--r--imap/src/c-client/http.c4
-rw-r--r--imap/src/c-client/mail.h12
-rw-r--r--imap/src/c-client/oauth2_aux.c140
-rw-r--r--imap/src/c-client/sha.c6
7 files changed, 145 insertions, 39 deletions
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];