* 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
* 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
* 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,
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;
/*
* 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.
*/
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 */
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
*/
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;
}
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;
- }
}
/*
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));
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
/*
* 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);
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;
/*
* 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;
}