From 354674406ed866dffbcab71455f796275a63df36 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Fri, 26 Jun 2020 12:19:56 -0600 Subject: * Expansion of the configuration screen for XOAUTH2 to include username, and tenant. * If a user has more than one client-id for a service, Alpine tries to asks the user which client-id to use and associates that client-id to the credentials in the XOAUTH2 configuration screen. --- imap/src/c-client/auth_bea.c | 21 ++----- imap/src/c-client/auth_oa2.c | 14 ++--- imap/src/c-client/http.h | 1 + imap/src/c-client/mail.h | 14 ++++- imap/src/c-client/oauth2_aux.c | 132 ++++++++++++++++++++++++++++++++++------- imap/src/osdep/nt/makefile.nt | 2 +- 6 files changed, 137 insertions(+), 47 deletions(-) (limited to 'imap') diff --git a/imap/src/c-client/auth_bea.c b/imap/src/c-client/auth_bea.c index bf5c9c1e..66604a35 100644 --- a/imap/src/c-client/auth_bea.c +++ b/imap/src/c-client/auth_bea.c @@ -121,21 +121,9 @@ long auth_oauthbearer_client (authchallenge_t challenger,authrespond_t responder + strlen(BEARER_HOST) + strlen(mb->orighost) + 1 + strlen(BEARER_PORT) + strlen(ports) + 1 + strlen(OAUTH2_BEARER) + strlen(oauth2.access_token) + 2; - t = response = (char *) fs_get (rlen); - for (u = BEARER_ACCOUNT; *u; *t++ = *u++); - for (u = user; *u; *t++ = *u++); - *t++ = ','; - *t++ = '\001'; /* delimiting ^A */ - for (u = BEARER_HOST; *u; *t++ = *u++); - for (u = mb->orighost; *u; *t++ = *u++); - *t++ = '\001'; /* delimiting ^A */ - for (u = BEARER_PORT; *u; *t++ = *u++); - for (u = ports; *u; *t++ = *u++); - *t++ = '\001'; /* delimiting ^A */ - for (u = OAUTH2_BEARER; *u; *t++ = *u++); - for (u = oauth2.access_token; *u; *t++ = *u++); - *t++ = '\001'; /* delimiting ^A */ - *t++ = '\001'; /* delimiting ^A */ + response = (char *) fs_get (rlen+1); + sprintf(response, "%s%s,\001%s%s\001%s%s\001%s%s\001\001", BEARER_ACCOUNT, user, + BEARER_HOST, mb->orighost, BEARER_PORT, ports, OAUTH2_BEARER, oauth2.access_token); if ((*responder) (stream,base,response,rlen)) { if ((challenge = (*challenger) (stream,&clen)) != NULL) fs_give ((void **) &challenge); @@ -159,6 +147,9 @@ long auth_oauthbearer_client (authchallenge_t challenger,authrespond_t responder fs_give ((void **) &response); } } + 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 (!ret || !oauth2.name) *trial = 65535; /* don't retry if bad protocol */ return ret; diff --git a/imap/src/c-client/auth_oa2.c b/imap/src/c-client/auth_oa2.c index 69987559..f5345bfa 100644 --- a/imap/src/c-client/auth_oa2.c +++ b/imap/src/c-client/auth_oa2.c @@ -140,15 +140,8 @@ long auth_oauth2_client (authchallenge_t challenger,authrespond_t responder, cha else { unsigned long rlen = strlen(OAUTH2_USER) + strlen(user) + strlen(OAUTH2_BEARER) + strlen(oauth2.access_token) + 1 + 2; - char *response = (char *) fs_get (rlen); - char *t = response; /* copy authorization id */ - for (u = OAUTH2_USER; *u; *t++ = *u++); - for (u = user; *u; *t++ = *u++); - *t++ = '\001'; /* delimiting ^A */ - for (u = OAUTH2_BEARER; *u; *t++ = *u++); - for (u = oauth2.access_token; *u; *t++ = *u++); - *t++ = '\001'; /* delimiting ^A */ - *t++ = '\001'; /* delimiting ^A */ + char *response = (char *) fs_get (rlen + 1); + sprintf(response, "%s%s\001%s%s\001\001", OAUTH2_USER, user, OAUTH2_BEARER, oauth2.access_token); if ((*responder) (stream,base,response,rlen)) { if ((challenge = (*challenger) (stream,&clen)) != NULL) fs_give ((void **) &challenge); @@ -172,6 +165,9 @@ long auth_oauth2_client (authchallenge_t challenger,authrespond_t responder, cha fs_give ((void **) &response); } } + 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 (!ret || !oauth2.name) *trial = 65535; /* don't retry if bad protocol */ return ret; diff --git a/imap/src/c-client/http.h b/imap/src/c-client/http.h index 621e7c48..41b16842 100644 --- a/imap/src/c-client/http.h +++ b/imap/src/c-client/http.h @@ -73,6 +73,7 @@ typedef struct http_header_data_s { #define HTTP_1_1_VERSION "HTTP/1.1" #define HTTP_OK 200 #define HTTP_BAD 400 +#define HTTP_UNAUTHORIZED 401 #define GET_HTTPPORT (long) 490 #define SET_HTTPPORT (long) 491 diff --git a/imap/src/c-client/mail.h b/imap/src/c-client/mail.h index f74d889d..eb8f0139 100644 --- a/imap/src/c-client/mail.h +++ b/imap/src/c-client/mail.h @@ -1929,6 +1929,7 @@ int PFLUSH (void); typedef enum {OA2_Id = 0, OA2_Secret, + OA2_Tenant, OA2_Code, OA2_RefreshToken, OA2_Scope, @@ -1986,10 +1987,21 @@ typedef struct deviceproc_s { char code_wait; /* code to say keep waiting */ } OAUTH2_DEVICEPROC_S; +typedef struct xoauth_default_s { + unsigned char *name; + char *client_id; + char *client_secret; + char *tenant; + char *users; +} XOAUTH2_INFO_S; + /* Supporting external functions for XOAUTH2 and OAUTHBEARER */ typedef char *(*oauth2getaccesscode_t) (unsigned char *, char *, OAUTH2_S *, int *); -typedef void (*oauth2clientinfo_t)(unsigned char *name, char **id, char **secret); +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 **); +XOAUTH2_INFO_S *copy_xoauth2_info(XOAUTH2_INFO_S *); diff --git a/imap/src/c-client/oauth2_aux.c b/imap/src/c-client/oauth2_aux.c index a23f7c28..88ee52a6 100644 --- a/imap/src/c-client/oauth2_aux.c +++ b/imap/src/c-client/oauth2_aux.c @@ -53,6 +53,7 @@ char *oauth2_generate_state(void) 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; \ @@ -64,20 +65,60 @@ JSON_S *oauth2_json_reply(OAUTH2_SERVER_METHOD_S, OAUTH2_S *, int *); (Y)[i].name = (Y)[i].value = NULL; \ } +char *xoauth2_server(char *server, char *tenant) +{ + char *rv = NULL; + char *s; + + if (server == NULL) return NULL; + + s = cpystr(server); + if(tenant){ + char *t = s, *u; + int i; + for(i = 0; t != NULL; i++){ + t = strchr(t, '\001'); + if(t != NULL) t++; + } + rv = fs_get((strlen(s) + i*(strlen(tenant)-1) + 1)*sizeof(char)); + *rv = '\0'; + for(u = t = s; t != NULL; i++){ + t = strchr(t, '\001'); + if (t != NULL) *t = '\0'; + strcat(rv, u); + if(t != NULL){ + strcat(rv, tenant); + *t++ = '\001'; + } + u = t; + } + + } + else + rv = cpystr(server); + + return rv; +} + JSON_S *oauth2_json_reply(OAUTH2_SERVER_METHOD_S RefreshMethod, OAUTH2_S *oauth2, int *status) { JSON_S *json = NULL; HTTP_PARAM_S params[OAUTH2_PARAM_NUMBER]; unsigned char *s; + char *server = NULL; LOAD_HTTP_PARAMS(RefreshMethod, params); *status = 0; + server = xoauth2_server(RefreshMethod.urlserver, oauth2->param[OA2_Tenant].value); if(strcmp(RefreshMethod.name, "POST") == 0 - && ((s = http_post_param(RefreshMethod.urlserver, params, status)) != NULL)){ + && ((s = http_post_param(server, params, status)) != NULL)){ unsigned char *u = s; json = json_parse(&u); fs_give((void **) &s); } + if(server) + fs_give((void **) &server); + return json; } @@ -92,11 +133,16 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, if(oauth2->param[OA2_Id].value == NULL || (oauth2->require_secret && oauth2->param[OA2_Secret].value == NULL)){ + XOAUTH2_INFO_S *x; oauth2clientinfo_t ogci = (oauth2clientinfo_t) mail_parameters (NIL, GET_OA2CLIENTINFO, NIL); - if(ogci) (*ogci)(oauth2->name, &oauth2->param[OA2_Id].value, - &oauth2->param[OA2_Secret].value); + if(ogci && (x = (*ogci)(oauth2->name, user)) != NULL){ + oauth2->param[OA2_Id].value = cpystr(x->client_id); + oauth2->param[OA2_Secret].value = x->client_secret ? cpystr(x->client_secret) : NULL; + oauth2->param[OA2_Tenant].value = x->tenant ? cpystr(x->tenant) : NULL; + free_xoauth2_info(&x); + } } if(oauth2->param[OA2_Id].value == NULL @@ -157,24 +203,34 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, /* else check if we have a refresh token, and in that case use it */ if(oauth2->param[OA2_RefreshToken].value){ - json = oauth2_json_reply(oauth2->server_mthd[OA2_GetAccessTokenFromRefreshToken], oauth2, &status); if(json != NULL){ JSON_X *jx; - jx = json_body_value(json, "access_token"); - if(jx && jx->jtype == JString) - oauth2->access_token = cpystr((char *) jx->value); + switch(status){ + case HTTP_UNAUTHORIZED: + mm_log("Client not authorized (wrong client-id?)", ERROR); + break; + case HTTP_OK: jx = json_body_value(json, "access_token"); + if(jx && jx->jtype == JString) + oauth2->access_token = cpystr((char *) jx->value); - if((jx = json_body_value(json, "expires_in")) != NULL) - switch(jx->jtype){ - case JString: oauth2->expiration = time(0) + atol((char *) jx->value); - break; - case JLong : oauth2->expiration = time(0) + *(long *) jx->value; - break; - } + if((jx = json_body_value(json, "expires_in")) != NULL) + switch(jx->jtype){ + case JString: oauth2->expiration = time(0) + atol((char *) jx->value); + break; + case JLong : oauth2->expiration = time(0) + *(long *) jx->value; + break; + } + break; + default : { char tmp[100]; + sprintf(tmp, "Oauth2 client Received Code %d", status); + mm_log (tmp, ERROR); + } + break; + } json_free(&json); } return; @@ -190,12 +246,15 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, LOAD_HTTP_PARAMS(RefreshMethod, params); if(strcmp(RefreshMethod.name, "GET") == 0){ - char *url = http_get_param_url(RefreshMethod.urlserver, params); + char *server = xoauth2_server(RefreshMethod.urlserver, oauth2->param[OA2_Tenant].value); + char *url = http_get_param_url(server, params); oauth2getaccesscode_t ogac = (oauth2getaccesscode_t) mail_parameters (NIL, GET_OA2CLIENTGETACCESSCODE, NIL); if(ogac) oauth2->param[OA2_Code].value = (*ogac)(url, method, oauth2, tryanother); + + if(server) fs_give((void **) &server); } if(oauth2->param[OA2_Code].value){ @@ -230,7 +289,7 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, case HTTP_BAD : break; default : { char tmp[100]; - sprintf(tmp, "Oauth Client Received Code %d", status); + sprintf(tmp, "Oauth2 Client Received Code %d", status); fatal (tmp); } } @@ -240,8 +299,6 @@ mm_login_oauth2_c_client_method (NETMBX *mb, char *user, char *method, } return; } - - /* Else, does this server use the /devicecode method? */ } void oauth2deviceinfo_get_accesscode(void *inp, void *outp) @@ -304,9 +361,9 @@ void oauth2deviceinfo_get_accesscode(void *inp, void *outp) break; default : { char tmp[100]; - sprintf(tmp, "Oauth device Received Code %d", status); - fatal (tmp); - } + sprintf(tmp, "Oauth device Received Code %d", status); + mm_log (tmp, ERROR); + } } json_free(&json); @@ -314,3 +371,36 @@ void oauth2deviceinfo_get_accesscode(void *inp, void *outp) *(int *)outp = rv; } + +XOAUTH2_INFO_S *new_xoauth2_info(void) +{ + XOAUTH2_INFO_S *rv = fs_get(sizeof(XOAUTH2_INFO_S)); + memset((void *) rv, 0, sizeof(XOAUTH2_INFO_S)); + return rv; +} + +void free_xoauth2_info(XOAUTH2_INFO_S **xp) +{ + if(xp == NULL || *xp == NULL) return; + + if((*xp)->name) fs_give((void **) &(*xp)->name); + if((*xp)->client_id) fs_give((void **) &(*xp)->client_id); + if((*xp)->client_secret) fs_give((void **) &(*xp)->client_secret); + if((*xp)->tenant) fs_give((void **) &(*xp)->tenant); + if((*xp)->users) fs_give((void **) &(*xp)->users); + fs_give((void **) xp); +} + +XOAUTH2_INFO_S *copy_xoauth2_info(XOAUTH2_INFO_S *x) +{ + XOAUTH2_INFO_S *y; + + if(x == NULL) return NULL; + y = new_xoauth2_info(); + if(x->name) y->name = cpystr(x->name); + if(x->client_id) y->client_id = cpystr(x->client_id); + if(x->client_secret) y->client_secret = cpystr(x->client_secret); + if(x->tenant) y->tenant = cpystr(x->tenant); + if(x->users) y->users = cpystr(x->users); + return y; +} diff --git a/imap/src/osdep/nt/makefile.nt b/imap/src/osdep/nt/makefile.nt index 83e74826..283d86a7 100644 --- a/imap/src/osdep/nt/makefile.nt +++ b/imap/src/osdep/nt/makefile.nt @@ -29,7 +29,7 @@ EXTRAAUTHENTICATORS = EXTRADRIVERS = EXTRACFLAGS = -AUTHENTICATORS = ext md5 pla bea oa2 log +AUTHENTICATORS = ext md5 pla log bea oa2 DRIVERS = imap nntp pop3 mbx mtx tenex unix CREATEDRIVER = mbx APPENDDRIVER = unix -- cgit v1.2.3-70-g09d2