diff options
author | Gilles Chehade <gilles@cvs.openbsd.org> | 2009-11-05 23:30:02 +0000 |
---|---|---|
committer | Gilles Chehade <gilles@cvs.openbsd.org> | 2009-11-05 23:30:02 +0000 |
commit | bc1a437cc2bfd454a2a92a61e839d7ef366335b1 (patch) | |
tree | 8719aa1755ae95f2e179f4e85279380cff0dee81 /usr.sbin | |
parent | 0929a0632ee1a8c9fba681cdfc711fcdeb4e5acf (diff) |
evbuffer_readline() assumes end of line to be \r, \n, \r\n, \n\r or \r\r.
smtp protocol expects lines to end with \r\n.
if a client sends a very long line which is unfortunately read up to \r,
evbuffer_readline() will return the line, then will detect another line
when the buffer is filled again and starts with \n, returning again with
an empty line. this is a bug which trigger very rarely and usually shows
up as an empty line in the middle of headers, causing all subsequent
headers to appear as part of the body to most mail user agents.
upstream fixes this with evbuffer_readln() in version 2.0 of libevent, a
mail will be sent to see if they can backport it, meanwhile we fix it by
introducing evbuffer_readln_crlf().
discussed with and ok jacekm@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/smtpd/smtp_session.c | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index 928041e2650..ae32db2413d 100644 --- a/usr.sbin/smtpd/smtp_session.c +++ b/usr.sbin/smtpd/smtp_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp_session.c,v 1.122 2009/10/19 20:48:13 gilles Exp $ */ +/* $OpenBSD: smtp_session.c,v 1.123 2009/11/05 23:30:01 gilles Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -74,6 +74,7 @@ void session_respond_delayed(int, short, void *); int session_set_path(struct path *, char *); void session_imsg(struct session *, enum smtp_proc_type, enum imsg_type, u_int32_t, pid_t, int, void *, u_int16_t); +char *evbuffer_readln_crlf(struct evbuffer *); struct session_cmd { char *name; @@ -996,7 +997,7 @@ session_readline(struct session *s) size_t nr; nr = EVBUFFER_LENGTH(s->s_bev->input); - line = evbuffer_readline(s->s_bev->input); + line = evbuffer_readln_crlf(s->s_bev->input); if (line == NULL) { if (EVBUFFER_LENGTH(s->s_bev->input) > SMTP_LINE_MAX) { session_respond(s, "500 Line too long"); @@ -1018,7 +1019,7 @@ session_readline(struct session *s) } if ((s->s_state != S_DATACONTENT || strcmp(line, ".") == 0) && - (line2 = evbuffer_readline(s->s_bev->input)) != NULL) { + (line2 = evbuffer_readln_crlf(s->s_bev->input)) != NULL) { session_respond(s, "500 Pipelining unsupported"); s->s_env->stats->smtp.toofast++; s->s_flags |= F_QUIT; @@ -1165,4 +1166,37 @@ session_imsg(struct session *s, enum smtp_proc_type proc, enum imsg_type type, datalen); } +char * +evbuffer_readln_crlf(struct evbuffer *buffer) +{ + u_char *data = EVBUFFER_DATA(buffer); + size_t len = EVBUFFER_LENGTH(buffer); + char *line; + unsigned int i, j; + + for (i = 0; i < len; ++i) { + if (data[i] == '\n') + break; + } + + if (i == len) + return NULL; + + j = i; + if (i != 0 && data[i - 1] == '\r') + --j; + + line = calloc(j + 1, 1); + if (line == NULL) + fatal("calloc"); + + if (j != 0) + memcpy(line, data, j); + + evbuffer_drain(buffer, i + 1); + + return (line); +} + + SPLAY_GENERATE(sessiontree, session, s_nodes, session_cmp); |