X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/b625c88a67183f545ef2d80375a07b98d637d76b..94187a80bd60baab4b9c4b949ad820d730578123:/sbr/netsec.c diff --git a/sbr/netsec.c b/sbr/netsec.c index 3fcb2cf5..ab490467 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -12,6 +12,7 @@ #include #include #include +#include "base64.h" #ifdef CYRUS_SASL #include @@ -56,9 +57,9 @@ struct _netsec_context { int ns_writefd; /* Write descriptor for network connection */ int ns_noclose; /* Do not close file descriptors if set */ 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 */ void *ns_snoop_context; /* Context data for snoop function */ + char *ns_snoop_savebuf; /* Save buffer for snoop data */ int ns_timeout; /* Network read timeout, in seconds */ char *ns_userid; /* Userid for authentication */ char *ns_hostname; /* Hostname we've connected to */ @@ -73,6 +74,7 @@ struct _netsec_context { char *sasl_mech; /* User-requested mechanism */ char *sasl_chosen_mech; /* Mechanism chosen by SASL */ netsec_sasl_callback sasl_proto_cb; /* SASL callback we use */ + void *sasl_proto_context; /* Context to be used by SASL callback */ #ifdef OAUTH_SUPPORT char *oauth_service; /* OAuth2 service name */ #endif /* OAUTH_SUPPORT */ @@ -143,9 +145,9 @@ netsec_init(void) nsc->ns_writefd = -1; nsc->ns_noclose = 0; nsc->ns_snoop = 0; - nsc->ns_snoop_noend = 0; nsc->ns_snoop_cb = NULL; nsc->ns_snoop_context = NULL; + nsc->ns_snoop_savebuf = NULL; nsc->ns_userid = NULL; nsc->ns_hostname = NULL; nsc->ns_timeout = 60; /* Our default */ @@ -160,6 +162,7 @@ netsec_init(void) nsc->sasl_mech = NULL; nsc->sasl_chosen_mech = NULL; nsc->sasl_proto_cb = NULL; + nsc->sasl_proto_context = NULL; #ifdef OAUTH_SUPPORT nsc->oauth_service = NULL; #endif /* OAUTH_SUPPORT */ @@ -187,19 +190,20 @@ netsec_init(void) void netsec_shutdown(netsec_context *nsc) { - mh_xfree(nsc->ns_userid); - mh_xfree(nsc->ns_hostname); - mh_xfree(nsc->ns_inbuffer); - mh_xfree(nsc->ns_outbuffer); - mh_xfree(nsc->sasl_mech); - mh_xfree(nsc->sasl_chosen_mech); + free(nsc->ns_userid); + free(nsc->ns_hostname); + free(nsc->ns_inbuffer); + free(nsc->ns_outbuffer); + free(nsc->sasl_mech); + free(nsc->sasl_chosen_mech); + free(nsc->ns_snoop_savebuf); #ifdef OAUTH_SERVICE - mh_xfree(nsc->oauth_service); + free(nsc->oauth_service); #endif /* OAUTH_SERVICE */ #ifdef CYRUS_SASL if (nsc->sasl_conn) sasl_dispose(&nsc->sasl_conn); - mh_xfree(nsc->sasl_cbs); + free(nsc->sasl_cbs); if (nsc->sasl_creds) nmh_credentials_free(nsc->sasl_creds); if (nsc->sasl_secret) { @@ -208,7 +212,7 @@ netsec_shutdown(netsec_context *nsc) } free(nsc->sasl_secret); } - mh_xfree(nsc->sasl_tmpbuf); + free(nsc->sasl_tmpbuf); #endif /* CYRUS_SASL */ #ifdef TLS_SUPPORT if (nsc->ssl_io) @@ -460,8 +464,10 @@ retry: if (nsc->ns_snoop_cb) nsc->ns_snoop_cb(nsc, sptr, strlen(sptr), nsc->ns_snoop_context); - else - fprintf(stderr, "%s\n", sptr); + else { + fputs(sptr, stderr); + putc('\n', stderr); + } } return sptr; } @@ -473,7 +479,7 @@ retry: */ if (count >= nsc->ns_inbufsize / 2) { - netsec_err(errstr, "Unable to find a line terminator after %d bytes", + netsec_err(errstr, "Unable to find a line terminator after %zu bytes", count); return NULL; } @@ -841,35 +847,6 @@ retry: goto 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, (char *) nsc->ns_outptr, outlen, - nsc->ns_snoop_context); - else - fprintf(stderr, "%.*s\n", outlen, nsc->ns_outptr); - } else { - nsc->ns_snoop_noend = 0; - } - } - nsc->ns_outptr += rc; nsc->ns_outbuflen += rc; @@ -895,6 +872,95 @@ netsec_flush(netsec_context *nsc, char **errstr) if (netoutlen == 0) return OK; + /* + * If we have snoop turned on, output the data. + * + * Note here; if we don't have a CR or LF at the end, save the data + * in ns_snoop_savebuf for later and print it next time. + */ + + if (nsc->ns_snoop) { + unsigned int snoopoutlen = netoutlen; + const char *snoopoutbuf = (const char *) nsc->ns_outbuffer; + + while (snoopoutlen > 0) { + const char *end = strpbrk(snoopoutbuf, "\r\n"); + unsigned int outlen; + + if (! end) { + if (nsc->ns_snoop_savebuf) { + nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf, + strlen(nsc->ns_snoop_savebuf) + + snoopoutlen + 1); + strncat(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen); + } else { + nsc->ns_snoop_savebuf = mh_xmalloc(snoopoutlen + 1); + strncpy(nsc->ns_snoop_savebuf, snoopoutbuf, snoopoutlen); + nsc->ns_snoop_savebuf[snoopoutlen] = '\0'; + } + break; + } + + outlen = end - snoopoutbuf; + +#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) { + const char *ptr; + unsigned int cb_len = outlen; + + if (nsc->ns_snoop_savebuf) { + cb_len += strlen(nsc->ns_snoop_savebuf); + nsc->ns_snoop_savebuf = mh_xrealloc(nsc->ns_snoop_savebuf, + outlen); + ptr = nsc->ns_snoop_savebuf; + } else { + ptr = snoopoutbuf; + } + + nsc->ns_snoop_cb(nsc, ptr, cb_len, nsc->ns_snoop_context); + + if (nsc->ns_snoop_savebuf) { + free(nsc->ns_snoop_savebuf); + nsc->ns_snoop_savebuf = NULL; + } + } else { + if (nsc->ns_snoop_savebuf) { + fprintf(stderr, "%s", nsc->ns_snoop_savebuf); + free(nsc->ns_snoop_savebuf); + nsc->ns_snoop_savebuf = NULL; + } + fprintf(stderr, "%.*s\n", outlen, snoopoutbuf); + } + + /* + * Alright, hopefully any previous leftover data is done, + * and we have the current line output. Move things past the + * next CR/LF. + */ + + snoopoutlen -= outlen; + snoopoutbuf += outlen; + + if (snoopoutlen > 0 && *snoopoutbuf == '\r') { + snoopoutlen--; + snoopoutbuf++; + } + + if (snoopoutlen > 0 && *snoopoutbuf == '\n') { + snoopoutlen--; + snoopoutbuf++; + } + } + } + /* * If SASL security layers are in effect, run the data through * sasl_encode() first. @@ -948,7 +1014,7 @@ netsec_flush(netsec_context *nsc, char **errstr) int netsec_set_sasl_params(netsec_context *nsc, const char *service, const char *mechanism, netsec_sasl_callback callback, - char **errstr) + void *context, char **errstr) { #ifdef CYRUS_SASL sasl_callback_t *sasl_cbs; @@ -1021,15 +1087,12 @@ netsec_set_sasl_params(netsec_context *nsc, const char *service, */ if (mechanism) { - char *p; nsc->sasl_mech = mh_xstrdup(mechanism); - - for (p = nsc->sasl_mech; *p; p++) - if (isascii((unsigned char) *p)) /* Leave non-ASCII lower alone. */ - *p = toupper((unsigned char) *p); + to_upper(nsc->sasl_mech); } nsc->sasl_proto_cb = callback; + nsc->sasl_proto_context = context; return OK; } @@ -1082,7 +1145,7 @@ netsec_get_password(sasl_conn_t *conn, void *context, int id, * us room for a terminating NUL */ - *psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len); + *psecret = malloc(sizeof(sasl_secret_t) + len); if (! *psecret) return SASL_NOMEM; @@ -1174,7 +1237,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) } rc = nsc->sasl_proto_cb(NETSEC_SASL_START, xoauth_client_res, - xoauth_client_res_len, NULL, 0, errstr); + xoauth_client_res_len, NULL, 0, + nsc->sasl_proto_context, errstr); free(xoauth_client_res); if (rc != OK) @@ -1187,7 +1251,8 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) * error. */ - rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, errstr); + rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, + nsc->sasl_proto_context, errstr); if (rc != OK) { /* @@ -1197,9 +1262,10 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) * NOT get a success message at this point. */ free(*errstr); - nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, NULL); + nsc->sasl_proto_cb(NETSEC_SASL_WRITE, NULL, 0, NULL, 0, + nsc->sasl_proto_context, NULL); rc = nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, - errstr); + nsc->sasl_proto_context, errstr); if (rc == 0) { netsec_err(errstr, "Unexpected success after OAuth failure!"); } @@ -1217,7 +1283,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) * messages. */ - memset(&secprops, 0, sizeof(secprops)); + ZERO(&secprops); secprops.maxbufsize = SASL_MAXRECVBUF; /* @@ -1257,7 +1323,7 @@ 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, NULL, 0, - errstr) != OK) + nsc->sasl_proto_context, errstr) != OK) return NOTOK; /* @@ -1271,26 +1337,30 @@ 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) { - nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); + nsc->sasl_proto_context, errstr) != OK) { + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, + nsc->sasl_proto_context, NULL); return NOTOK; } rc = sasl_client_step(nsc->sasl_conn, (char *) outbuf, outbuflen, NULL, (const char **) &saslbuf, &saslbuflen); - mh_xfree(outbuf); + free(outbuf); if (rc != SASL_OK && rc != SASL_CONTINUE) { netsec_err(errstr, "SASL client negotiation failed: %s", sasl_errdetail(nsc->sasl_conn)); - nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, + nsc->sasl_proto_context, NULL); return NOTOK; } if (nsc->sasl_proto_cb(NETSEC_SASL_WRITE, saslbuf, saslbuflen, - NULL, 0, errstr) != OK) { - nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, NULL); + NULL, 0, nsc->sasl_proto_context, + errstr) != OK) { + nsc->sasl_proto_cb(NETSEC_SASL_CANCEL, NULL, 0, NULL, 0, + nsc->sasl_proto_context, NULL); return NOTOK; } } @@ -1301,7 +1371,7 @@ netsec_negotiate_sasl(netsec_context *nsc, const char *mechlist, char **errstr) */ if (nsc->sasl_proto_cb(NETSEC_SASL_FINISH, NULL, 0, NULL, 0, - errstr) != OK) { + nsc->sasl_proto_context, errstr) != OK) { /* * At this point we can't really send an abort since the SASL dialog * has completed, so just bubble back up the error message. @@ -1409,6 +1479,20 @@ netsec_get_sasl_mechanism(netsec_context *nsc) return nsc->sasl_chosen_mech; } +/* + * Return the negotiated SASL strength security factor (SSF) + */ + +int +netsec_get_sasl_ssf(netsec_context *nsc) +{ +#ifdef CYRUS_SASL + return nsc->sasl_ssf; +#else /* CYRUS_SASL */ + return 0; +#endif /* CYRUS_SASL */ +} + /* * Set an OAuth2 service name, if we support it. */