From: Ken Hornstein Date: Wed, 21 Sep 2016 00:09:37 +0000 (-0400) Subject: Shuffle some stuff around, change some prototypes, and modify the X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/546b7396244452dba8f9cb917e904028ab01dda5?ds=sidebyside;hp=-c Shuffle some stuff around, change some prototypes, and modify the oauth code a bit to fit our new structure. --- 546b7396244452dba8f9cb917e904028ab01dda5 diff --git a/h/netsec.h b/h/netsec.h index ef0d521d..5372714c 100644 --- a/h/netsec.h +++ b/h/netsec.h @@ -210,16 +210,17 @@ enum sasl_message_type { * outdatasize - Size of output data * errstr - An error string to be returned (freed by caller). * + * As a general note, plugins should perform their own I/O. Buffers returned + * by NETSEC_SASL_READ should be allocated by the plugins and will be freed + * by the netsec package. Error messages returned should be created by + * netsec_err(). + * * Parameter interpretation based on mtype value: * * NETSEC_SASL_START - Create a protocol message that starts SASL * authentication. If an initial response is * supported, indata and indatasize will contain it. * Otherwise they will be set to NULL and 0. - * The complete protocol message should be - * stored in outdata/outdatasize, to be free()d - * by the caller. Alternatively, the plugin - * can choose to send the data on their own. * NETSEC_SASL_READ - Parse and decode a protocol message and extract * out the SASL payload data. indata will be set * to NULL; the callback must read in the necessary @@ -228,10 +229,7 @@ enum sasl_message_type { * SASL message (again, must be free()d by the caller). * NETSEC_SASL_WRITE - Generate a protocol message to send over the * network. indata/indatasize will contain the - * SASL payload data. outdata/outdatasize should - * contain the complete protocol message. Alternatively - * the plugin can write the data to the network - * directly. + * SASL payload data. * NETSEC_SASL_FINISH - Process the final SASL message exchange; at * this point SASL exchange should have completed * and we should get a message back from the server @@ -244,11 +242,6 @@ enum sasl_message_type { * The callback should return OK on success, NOTOK on failure. Depending * at the point of the authentication exchange, the callback may be asked * to generate a cancel message. - * - * Some higher-level notes in terms of protocol management: - * - * Any data returned in outdata should consist of allocated data that - * the sasl routines is expected to free. */ typedef int (*netsec_sasl_callback)(enum sasl_message_type mtype, diff --git a/h/oauth.h b/h/oauth.h index 274ca9de..0278cae7 100644 --- a/h/oauth.h +++ b/h/oauth.h @@ -105,14 +105,18 @@ struct mh_oauth_service_info { * Do the complete dance for XOAUTH2 as used by POP3 and SMTP. * * Load tokens for svc from disk, refresh if necessary, and return the - * base64-encoded client response. + * client response in client_response and client_response_len. * * If refreshing, writes freshened tokens to disk. * * Exits via adios on any error. + * + * Always returns OK for now, but in the future could return NOTOK on error. */ -char * -mh_oauth_do_xoauth(const char *user, const char *svc, FILE *log); + +int +mh_oauth_do_xoauth(const char *user, const char *svc, unsigned char **oauth_res, + size_t *oauth_res_len, FILE *log); /* * Allocate and initialize a new OAuth context. diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index 79c1e79b..dff7628a 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -1160,7 +1160,7 @@ sm_auth_xoauth2(const char *user, const char *oauth_svc, int snoop) const char *xoauth_client_res; int status; -#ifdef OAUTH_SUPPORT +#if 0 xoauth_client_res = mh_oauth_do_xoauth(user, oauth_svc, snoop ? stderr : NULL); diff --git a/sbr/netsec.c b/sbr/netsec.c index abdcaee8..c5dfef01 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -974,7 +974,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) int *outbufmax; #endif #ifdef OAUTH_SUPPORT - const char *xoauth_client_res; + unsigned char *xoauth_client_res; + size_t xoauth_client_res_len; #endif /* OAUTH_SUPPORT */ int rc; @@ -1012,7 +1013,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) /* * This should be relatively straightforward, but requires some * help from the plugin. Basically, if XOAUTH2 is a success, - * the plugin has to return success, but no output data. If + * the callback has to return success, but no output data. If * there is output data, it will be assumed that it is the JSON * error message. */ @@ -1024,19 +1025,45 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) nsc->sasl_chosen_mech = getcpy(nsc->sasl_mech); - xoauth_client_res = mh_oauth_do_xoauth(nsc->ns_userid, - nsc->oauth_service, - nsc->ns_snoop ? stderr : NULL); - - if (xoauth_client_res == NULL) { - netsec_err(errstr, "Internal error: mh_oauth_do_xoauth() " - "returned NULL"); + if (mh_oauth_do_xoauth(nsc->ns_userid, nsc->oauth_service, + &xoauth_client_res, &xoauth_client_res_len, + nsc->ns_snoop ? stderr : NULL) != OK) { + netsec_err(errstr, "Internal error: Unable to get OAuth2 " + "bearer token"); return NOTOK; } -#if 0 - rc = nsc->sasl_proto_cb(NETSEC_SASL_START, -#endif + rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res, + xoauth_client_res_len, NULL, 0, errstr); + free(xoauth_client_res); + + if (rc != OK) + return NOTOK; + + /* + * Okay, we need to do a NETSEC_SASL_FINISH now. If we return + * success, we indicate that with no output data. But if we + * fail, then send a blank message and get the resulting + * error. + */ + + rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, errstr); + + if (rc != OK) { + /* + * We're going to assume the error here is a JSON response; + * we ignore it and send a blank message in response. We should + * then get either an +OK or -ERR + */ + free(errstr); + nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, NULL); + rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, + errstr); + if (rc == 0) { + netsec_err(errstr, "Unexpected success after OAuth failure!"); + } + return NOTOK; + } } #endif /* OAUTH_SUPPORT */ @@ -1087,21 +1114,10 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) nsc->sasl_chosen_mech = getcpy(chosen_mech); - if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, &outbuf, - &outbuflen, errstr) != OK) + if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0, + errstr) != OK) return NOTOK; - if (outbuflen > 0) { - if (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) { - free(outbuf); - return NOTOK; - } - free(outbuf); - if (netsec_flush(nsc, errstr) != OK) - return NOTOK; - } - - /* * We've written out our first message; enter in the step loop */ @@ -1114,14 +1130,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) if (nsc->sasl_proto_cb(NETSEC_SASL_READ, NULL, 0, &outbuf, &outbuflen, errstr) != OK) { - if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf, - &outbuflen, NULL) == OK) { - if (outbuflen > 0) { - netsec_write(nsc, outbuf, outbuflen, NULL); - netsec_flush(nsc, NULL); - free(outbuf); - } - } + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); return NOTOK; } @@ -1134,39 +1143,15 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) if (rc != SASL_OK && rc != SASL_CONTINUE) { netsec_err(errstr, "SASL client negotiation failed: %s", sasl_errdetail(nsc->sasl_conn)); - if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf, - &outbuflen, NULL) == OK) { - if (outbuflen > 0) { - netsec_write(nsc, outbuf, outbuflen, NULL); - netsec_flush(nsc, NULL); - free(outbuf); - } - } + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); return NOTOK; } if (nsc->sasl_proto_cb(NETSEC_SASL_WRITE, saslbuf, saslbuflen, - &outbuf, &outbuflen, errstr) != OK) { - if (nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, &outbuf, - &outbuflen, NULL) == OK) { - if (outbuflen > 0) { - netsec_write(nsc, outbuf, outbuflen, NULL); - netsec_flush(nsc, NULL); - free(outbuf); - } - } + NULL, 0, errstr) != OK) { + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); return NOTOK; } - - if (outbuflen > 0) { - if (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) { - free(outbuf); - return NOTOK; - } - free(outbuf); - if (netsec_flush(nsc, errstr) != OK) - return NOTOK; - } } /* diff --git a/sbr/oauth.c b/sbr/oauth.c index ae745fcf..85fe7146 100644 --- a/sbr/oauth.c +++ b/sbr/oauth.c @@ -127,17 +127,16 @@ static boolean get_json_strings(const char *, size_t, FILE *, ...); static boolean make_query_url(char *, size_t, CURL *, const char *, ...); static boolean post(struct curl_ctx *, const char *, const char *); -char * -mh_oauth_do_xoauth(const char *user, const char *svc, FILE *log) +int +mh_oauth_do_xoauth(const char *user, const char *svc, unsigned char **oauth_res, + size_t *oauth_res_len, FILE *log) { mh_oauth_ctx *ctx; mh_oauth_cred *cred; char *fn; int failed_to_lock = 0; FILE *fp; - size_t client_res_len; char *client_res; - char *client_res_b64; if (!mh_oauth_new (&ctx, svc)) adios(NULL, mh_oauth_get_err_string(ctx)); @@ -183,18 +182,14 @@ mh_oauth_do_xoauth(const char *user, const char *svc, FILE *log) free(fn); /* XXX writeBase64raw modifies the source buffer! make a copy */ - client_res = getcpy(mh_oauth_sasl_client_response(&client_res_len, user, + client_res = getcpy(mh_oauth_sasl_client_response(oauth_res_len, user, cred)); mh_oauth_cred_free(cred); mh_oauth_free(ctx); - client_res_b64 = mh_xmalloc(((((client_res_len) + 2) / 3 ) * 4) + 1); - if (writeBase64raw((unsigned char *)client_res, client_res_len, - (unsigned char *)client_res_b64) != OK) { - adios(NULL, "base64 encoding of XOAUTH2 client response failed"); - } - free(client_res); - return client_res_b64; + *oauth_res = (unsigned char *) client_res; + + return OK; } static boolean diff --git a/uip/popsbr.c b/uip/popsbr.c index 884fea4f..56765916 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -682,7 +682,7 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, /* * We should get one line back, with our base64 data. Decode that - * and feed it back in. + * and feed it back into the SASL library. */ case NETSEC_SASL_READ: line = netsec_readline(nsc, &len, errstr); @@ -711,20 +711,21 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, case NETSEC_SASL_WRITE: if (indatalen == 0) { - *outdata = (unsigned char *) getcpy("\r\n"); - *outdatalen = strlen((char *) *outdata); + rc = netsec_printf(nsc, errstr, "\r\n"); } else { unsigned char *b64data; - b64data = mh_xmalloc(BASE64SIZE(indatalen) + 3); + b64data = mh_xmalloc(BASE64SIZE(indatalen)); writeBase64raw(indata, indatalen, b64data); - len = strlen((char *) b64data); - b64data[len++] = '\r'; - b64data[len++] = '\n'; - b64data[len++] = '\0'; - *outdata = b64data; - *outdatalen = len - 1; + rc = netsec_printf(nsc, errstr, "%s\r\n", b64data); + free(b64data); } + if (rc != OK) + return NOTOK; + + if (netsec_flush(nsc, errstr) != OK) + return NOTOK; + return OK; break; @@ -746,11 +747,18 @@ pop_sasl_callback(enum sasl_message_type mtype, unsigned const char *indata, /* * Cancel the SASL exchange in the middle of the commands; for * POP, that's a single "*". + * + * It's unclear to me if I should be returning errors up; I finally + * decided the answer should be "yes", and if the upper layer wants to + * ignore them that's their choice. */ case NETSEC_SASL_CANCEL: - *outdata = (unsigned char *) getcpy("*\r\n"); - *outdatalen = strlen((char *) *outdata); + rc = netsec_printf(nsc, errstr, "*\r\n"); + if (rc == OK) + rc = netsec_flush(nsc, errstr); + if (rc != OK) + return NOTOK; break; }