X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/4ea2f92f36fcf39727438cd55580c99a5a6e6ad8..9a6d835cfe7761f6a85f84233d9d93722efe6ecc:/sbr/netsec.c diff --git a/sbr/netsec.c b/sbr/netsec.c index 372732c3..3fd31854 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -56,6 +56,8 @@ static SSL_CTX *sslctx = NULL; /* SSL Context */ struct _netsec_context { int ns_fd; /* Descriptor for network connection */ int ns_snoop; /* If true, display network data */ + int ns_snoop_noend; /* If true, didn't get a CR/LF on last line */ + netsec_snoop_callback ns_snoop_cb; /* Snoop output callback */ int ns_timeout; /* Network read timeout, in seconds */ char *ns_userid; /* Userid for authentication */ unsigned char *ns_inbuffer; /* Our read input buffer */ @@ -89,12 +91,6 @@ struct _netsec_context { #endif /* TLS_SUPPORT */ }; -/* - * Function to allocate error message strings - */ - -static void netsec_err(char **errstr, const char *format, ...); - /* * Function to read data from the actual network socket */ @@ -128,6 +124,8 @@ netsec_init(void) nsc->ns_fd = -1; nsc->ns_snoop = 0; + nsc->ns_snoop_noend = 0; + nsc->ns_snoop_cb = NULL; nsc->ns_userid = NULL; nsc->ns_timeout = 60; /* Our default */ nsc->ns_inbufsize = NETSEC_BUFSIZE; @@ -327,7 +325,7 @@ netsec_read(netsec_context *nsc, void *buffer, size_t size, char **errstr) */ char * -netsec_readline(netsec_context *nsc, char **errstr) +netsec_readline(netsec_context *nsc, size_t *len, char **errstr) { unsigned char *ptr = nsc->ns_inptr; size_t count = 0, offset; @@ -344,8 +342,25 @@ retry: if (count > 1 && *(ptr - 2) == '\r') ptr--; *--ptr = '\0'; + if (len) + *len = ptr - nsc->ns_inptr; nsc->ns_inptr += count; nsc->ns_inbuflen -= count; + if (nsc->ns_snoop) { +#ifdef CYRUS_SASL + if (nsc->sasl_seclayer) + fprintf(stderr, "(sasl-encrypted) "); +#endif /* CYRUS_SASL */ +#ifdef TLS_SUPPORT + if (nsc->tls_active) + fprintf(stderr, "(tls-encrypted) "); +#endif /* TLS_SUPPORT */ + fprintf(stderr, "<= "); + if (nsc->ns_snoop_cb) + nsc->ns_snoop_cb(nsc, sptr, strlen(sptr)); + else + fprintf(stderr, "%s\n", sptr); + } return sptr; } } @@ -567,6 +582,11 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size, const unsigned char *bufptr = buffer; int rc, remaining; + /* Just in case */ + + if (size == 0) + return OK; + /* * If TLS is active, then bypass all of our buffering logic; just * write it directly to our BIO. We have a buffering BIO first in @@ -624,6 +644,7 @@ netsec_write(netsec_context *nsc, const void *buffer, size_t size, /* * Our network printf() routine, which really just calls netsec_vprintf(). + */ int netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) @@ -631,7 +652,7 @@ netsec_printf(netsec_context *nsc, char **errstr, const char *format, ...) va_list ap; int rc; - va_start(format, ap); + va_start(ap, format); rc = netsec_vprintf(nsc, errstr, format, ap); va_end(ap); @@ -709,6 +730,34 @@ retry: } } + if (nsc->ns_snoop) { + int outlen = rc; + if (outlen > 0 && nsc->ns_outptr[outlen - 1] == '\n') { + outlen--; + if (outlen > 0 && nsc->ns_outptr[outlen - 1] == '\r') + outlen--; + } else { + nsc->ns_snoop_noend = 1; + } + if (outlen > 0 || nsc->ns_snoop_noend == 0) { +#ifdef CYRUS_SASL + if (nsc->sasl_seclayer) + fprintf(stderr, "(sasl-encrypted) "); +#endif /* CYRUS_SASL */ +#ifdef TLS_SUPPORT + if (nsc->tls_active) + fprintf(stderr, "(tls-encrypted) "); +#endif /* TLS_SUPPORT */ + fprintf(stderr, "=> "); + if (nsc->ns_snoop_cb) + nsc->ns_snoop_cb(nsc, nsc->ns_outptr, outlen); + else + fprintf(stderr, "%.*s\n", outlen, nsc->ns_outptr); + } else { + nsc->ns_snoop_noend = 0; + } + } + nsc->ns_outptr += rc; nsc->ns_outbuflen += rc; @@ -745,6 +794,13 @@ netsec_flush(netsec_context *nsc, char **errstr) } #endif /* TLS_SUPPORT */ + /* + * Small optimization + */ + + if (netoutlen == 0) + return OK; + /* * If SASL security layers are in effect, run the data through * sasl_encode() first and then write it. @@ -965,7 +1021,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; @@ -999,11 +1056,11 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) } #ifdef OAUTH_SUPPORT - if (strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) { + if (nsc->sasl_mech && strcasecmp(nsc->sasl_mech, "XOAUTH2") == 0) { /* * 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. */ @@ -1015,19 +1072,46 @@ 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; + } + return OK; } #endif /* OAUTH_SUPPORT */ @@ -1078,18 +1162,8 @@ 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) - return NOTOK; - - if (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) { - free(outbuf); - return NOTOK; - } - - free(outbuf); - - if (netsec_flush(nsc, errstr) != OK) + if (nsc->sasl_proto_cb(NETSEC_SASL_START, saslbuf, saslbuflen, NULL, 0, + errstr) != OK) return NOTOK; /* @@ -1104,52 +1178,28 @@ 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) { - 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; } rc = sasl_client_step(nsc->sasl_conn, (char *) outbuf, outbuflen, NULL, (const char **) &saslbuf, &saslbuflen); - free(outbuf); + if (outbuf) + free(outbuf); 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) { - 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) { - 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 (netsec_write(nsc, outbuf, outbuflen, errstr) != OK) { - free(outbuf); - return NOTOK; - } - - free(outbuf); - - if (netsec_flush(nsc, errstr) != OK) - return NOTOK; } /* @@ -1455,7 +1505,7 @@ netsec_negotiate_tls(netsec_context *nsc, char **errstr) * Generate an (allocated) error string */ -static void +void netsec_err(char **errstr, const char *fmt, ...) { va_list ap;