#include "smtp.h"
#include <h/mts.h>
#include <h/signals.h>
+#include <h/utils.h>
#ifdef CYRUS_SASL
#include <sasl/sasl.h>
* 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
*/
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;
}
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
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;
}
}
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
*/
{
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) ");
result = smhear ();
alarm (0);
+ free(buffer);
+
return result;
}
* 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;
}
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;
}
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
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;
}
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).
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)