From: Ken Hornstein Date: Tue, 10 Jun 2014 23:06:54 +0000 (-0400) Subject: Use variable-size (but in one case, fixed but larger) buffers for the SASL X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/af2971700a5d9fbc9f68c4afca5ba6567e800260?ds=inline;hp=-c Use variable-size (but in one case, fixed but larger) buffers for the SASL exchange to deal with larger SASL messages (specifically, when doing GSSAPI with certain ticket extensions). --- af2971700a5d9fbc9f68c4afca5ba6567e800260 diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index e6ac1661..65de0a7f 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -10,6 +10,7 @@ #include "smtp.h" #include #include +#include #ifdef CYRUS_SASL #include @@ -798,17 +799,26 @@ sm_end (int type) * completes successfully, then authentication is successful and we've * (optionally) negotiated a security layer. */ + +#define CHECKB64SIZE(insize, outbuf, outsize) \ + { size_t wantout = (((insize + 2) / 3) * 4) + 32; \ + if (wantout > outsize) { \ + outbuf = mh_xrealloc(outbuf, outsize = wantout); \ + } \ + } + static int sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost) { int result, status; unsigned int buflen, outlen; - char *buf, outbuf[BUFSIZ], host[NI_MAXHOST]; + char *buf, *outbuf = NULL, host[NI_MAXHOST]; const char *chosen_mech; sasl_security_properties_t secprops; sasl_ssf_t *ssf; int *outbufmax; struct nmh_creds creds = { 0, 0, 0 }; + size_t outbufsize = 0; /* * Initialize the callback contexts @@ -905,10 +915,13 @@ sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost) */ if (buflen) { - status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL); + CHECKB64SIZE(buflen, outbuf, outbufsize); + status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL); if (status != SASL_OK) { sm_ierror("SASL base64 encode failed: %s", sasl_errstring(status, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } @@ -935,8 +948,11 @@ sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost) if (status == 235) break; - else if (status < 300 || status > 399) + else if (status < 300 || status > 399) { + if (outbuf) + free(outbuf); return RP_BHST; + } /* * Special case; a zero-length response from the SMTP server @@ -947,12 +963,18 @@ sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost) if (strcmp("=", sm_reply.text) == 0) { outlen = 0; } else { + if (sm_reply.length > (int) outbufsize) { + outbuf = mh_xrealloc(outbuf, outbufsize = sm_reply.length); + } + result = sasl_decode64(sm_reply.text, sm_reply.length, - outbuf, sizeof(outbuf), &outlen); + outbuf, outbufsize, &outlen); if (result != SASL_OK) { smtalk(SM_AUTH, "*"); sm_ierror("SASL base64 decode failed: %s", sasl_errstring(result, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } } @@ -964,21 +986,29 @@ sm_auth_sasl(char *user, int saslssf, char *mechlist, char *inhost) smtalk(SM_AUTH, "*"); sm_ierror("SASL client negotiation failed: %s", sasl_errstring(result, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } - status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL); + CHECKB64SIZE(buflen, outbuf, outbufsize); + status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL); if (status != SASL_OK) { smtalk(SM_AUTH, "*"); sm_ierror("SASL base64 encode failed: %s", sasl_errstring(status, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } status = smtalk(SM_AUTH, outbuf); } + if (outbuf) + free(outbuf); + /* * Make sure that we got the correct response */ @@ -1122,12 +1152,22 @@ smtalk (int time, char *fmt, ...) { va_list ap; int result; - char buffer[BUFSIZ]; + char *buffer; + size_t bufsize = BUFSIZ; + + buffer = mh_xmalloc(bufsize); va_start(ap, fmt); - vsnprintf (buffer, sizeof(buffer), fmt, ap); + result = vsnprintf (buffer, bufsize, fmt, ap); va_end(ap); + if (result > (int) bufsize) { + buffer = mh_xrealloc(buffer, bufsize = result + 1); + va_start(ap, fmt); + vsnprintf (buffer, bufsize, fmt, ap); + va_end(ap); + } + if (sm_debug) { if (sasl_ssf) printf("(sasl-encrypted) "); @@ -1143,6 +1183,8 @@ smtalk (int time, char *fmt, ...) result = smhear (); alarm (0); + free(buffer); + return result; } diff --git a/uip/popsbr.c b/uip/popsbr.c index 2eb823cf..479fdb1f 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -89,12 +89,20 @@ static int putline (char *, FILE *); * layer. */ +#define CHECKB64SIZE(insize, outbuf, outsize) \ + { size_t wantout = (((insize + 2) / 3) * 4) + 32; \ + if (wantout > outsize) { \ + outbuf = mh_xrealloc(outbuf, outsize = wantout); \ + } \ + } + int pop_auth_sasl(char *user, char *host, char *mech) { int result, status, sasl_capability = 0; unsigned int buflen, outlen; - char server_mechs[256], *buf, outbuf[BUFSIZ]; + char server_mechs[256], *buf, *outbuf = NULL; + size_t outbufsize = 0; const char *chosen_mech; sasl_security_properties_t secprops; struct pass_context p_context; @@ -209,10 +217,13 @@ pop_auth_sasl(char *user, char *host, char *mech) } if (buflen) { - status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL); + CHECKB64SIZE(buflen, outbuf, outbufsize); + status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL); if (status != SASL_OK) { snprintf(response, sizeof(response), "SASL base64 encode " "failed: %s", sasl_errstring(status, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } @@ -221,8 +232,13 @@ pop_auth_sasl(char *user, char *host, char *mech) status = command("AUTH %s", chosen_mech); while (result == SASL_CONTINUE) { - if (status == NOTOK) + size_t inlen; + + if (status == NOTOK) { + if (outbuf) + free(outbuf); return NOTOK; + } /* * If we get a "+OK" prefix to our response, then we should @@ -241,16 +257,31 @@ pop_auth_sasl(char *user, char *host, char *mech) command("*"); snprintf(response, sizeof(response), "Malformed authentication message from server"); + if (outbuf) + free(outbuf); return NOTOK; } + /* + * For decode, it will always be shorter, so just make sure + * that outbuf is as at least as big as the encoded response. + */ + + inlen = strlen(response + 2); + + if (inlen > outbufsize) { + outbuf = mh_xrealloc(outbuf, outbufsize = inlen); + } + result = sasl_decode64(response + 2, strlen(response + 2), - outbuf, sizeof(outbuf), &outlen); + outbuf, outbufsize, &outlen); if (result != SASL_OK) { command("*"); snprintf(response, sizeof(response), "SASL base64 decode " "failed: %s", sasl_errstring(result, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } @@ -261,21 +292,30 @@ pop_auth_sasl(char *user, char *host, char *mech) command("*"); snprintf(response, sizeof(response), "SASL client negotiaton " "failed: %s", sasl_errdetail(conn)); + if (outbuf) + free(outbuf); return NOTOK; } - status = sasl_encode64(buf, buflen, outbuf, sizeof(outbuf), NULL); + CHECKB64SIZE(buflen, outbuf, outbufsize); + + status = sasl_encode64(buf, buflen, outbuf, outbufsize, NULL); if (status != SASL_OK) { command("*"); snprintf(response, sizeof(response), "SASL base64 encode " "failed: %s", sasl_errstring(status, NULL, NULL)); + if (outbuf) + free(outbuf); return NOTOK; } status = command(outbuf); } + if (outbuf) + free(outbuf); + /* * If we didn't get a positive final response, then error out * (that probably means we failed an authorization check). @@ -753,9 +793,10 @@ command(const char *fmt, ...) static int vcommand (const char *fmt, va_list ap) { - char *cp, buffer[BUFSIZ]; + char *cp, buffer[65536]; vsnprintf (buffer, sizeof(buffer), fmt, ap); + if (poprint) { #ifdef CYRUS_SASL if (sasl_ssf)