diff options
author | Jacek Masiulaniec <jacekm@cvs.openbsd.org> | 2009-05-27 13:09:08 +0000 |
---|---|---|
committer | Jacek Masiulaniec <jacekm@cvs.openbsd.org> | 2009-05-27 13:09:08 +0000 |
commit | 864dce2bc80e7c0ba01cabe9f8f53e7bbfdb85e3 (patch) | |
tree | 208c92dbab2a633fcf317a17dc286cc3c1649649 /usr.sbin | |
parent | bc74c06e354abc8aa1fda72cd517b1cb48c394e9 (diff) |
request flood mitigation:
1) each state may have 2 responses sent quickly;
2) more responses are delayed exponentially, up to a defined limit.
Delay count is user visible (smtp.errors.delays).
ok gilles@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/smtpd/smtp_session.c | 72 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpctl.c | 3 | ||||
-rw-r--r-- | usr.sbin/smtpd/smtpd.h | 13 |
3 files changed, 73 insertions, 15 deletions
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index 3b3913a01df..68f41878b41 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.97 2009/05/25 13:29:47 jacekm Exp $ */ +/* $OpenBSD: smtp_session.c,v 1.98 2009/05/27 13:09:07 jacekm Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -69,6 +69,7 @@ void session_write(struct bufferevent *, void *); void session_error(struct bufferevent *, short event, void *); void session_command(struct session *, char *, size_t); char *session_readline(struct session *, size_t *); +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); @@ -126,9 +127,8 @@ session_rfc3207_stls_handler(struct session *s, char *args) return 1; } - session_respond(s, "220 Ready to start TLS"); - s->s_state = S_TLS; + session_respond(s, "220 Ready to start TLS"); return 1; } @@ -179,10 +179,11 @@ session_rfc4954_auth_plain(struct session *s, char *arg) switch (s->s_state) { case S_HELO: if (arg == NULL) { - session_respond(s, "334 "); s->s_state = S_AUTH_INIT; + session_respond(s, "334 "); return; } + s->s_state = S_AUTH_INIT; /* FALLTHROUGH */ case S_AUTH_INIT: @@ -234,9 +235,8 @@ session_rfc4954_auth_login(struct session *s, char *arg) switch (s->s_state) { case S_HELO: - /* "Username:" base64 encoded is "VXNlcm5hbWU6" */ - session_respond(s, "334 VXNlcm5hbWU6"); s->s_state = S_AUTH_USERNAME; + session_respond(s, "334 VXNlcm5hbWU6"); return; case S_AUTH_USERNAME: @@ -244,9 +244,8 @@ session_rfc4954_auth_login(struct session *s, char *arg) if (kn_decode_base64(arg, a->user, sizeof(a->user) - 1) == -1) goto abort; - /* "Password:" base64 encoded is "UGFzc3dvcmQ6" */ - session_respond(s, "334 UGFzc3dvcmQ6"); s->s_state = S_AUTH_PASSWORD; + session_respond(s, "334 UGFzc3dvcmQ6"); return; case S_AUTH_PASSWORD: @@ -658,7 +657,6 @@ session_pickup(struct session *s, struct submit_status *ss) break; case S_DONE: - s->s_state = S_HELO; session_respond(s, "250 %s Message accepted for delivery", s->s_msg.message_id); log_info("%s: from=<%s@%s>, nrcpts=%zd, proto=%s, relay=%s [%s]", @@ -670,8 +668,10 @@ session_pickup(struct session *s, struct submit_status *ss) s->s_hostname, ss_to_text(&s->s_ss)); + s->s_state = S_HELO; s->s_msg.message_id[0] = '\0'; s->s_msg.message_uid[0] = '\0'; + bzero(&s->s_nresp, sizeof(s->s_nresp)); break; default: @@ -1016,7 +1016,10 @@ session_set_path(struct path *path, char *line) void session_respond(struct session *s, char *fmt, ...) { - va_list ap; + va_list ap; + int n, delay; + + n = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(s->s_bev)); va_start(ap, fmt); if (evbuffer_add_vprintf(EVBUFFER_OUTPUT(s->s_bev), fmt, ap) == -1 || @@ -1024,8 +1027,53 @@ session_respond(struct session *s, char *fmt, ...) fatal("session_respond: evbuffer_add_vprintf failed"); va_end(ap); - if (smtpd_process == PROC_SMTP) - bufferevent_disable(s->s_bev, EV_READ); + if (smtpd_process == PROC_MTA) { + bufferevent_enable(s->s_bev, EV_WRITE); + return; + } + + bufferevent_disable(s->s_bev, EV_READ); + + /* Detect multi-line response. */ + if (EVBUFFER_LENGTH(EVBUFFER_OUTPUT(s->s_bev)) - n < 4) + fatalx("session_respond: invalid response length"); + switch (EVBUFFER_DATA(EVBUFFER_OUTPUT(s->s_bev))[n + 3]) { + case '-': + return; + case ' ': + break; + default: + fatalx("session_respond: invalid response"); + } + + /* + * Deal with request flooding; avoid letting response rate keep up + * with incoming request rate. + */ + s->s_nresp[s->s_state]++; + + if (s->s_state == S_RCPT) + delay = 0; + else if ((n = s->s_nresp[s->s_state] - FAST_RESPONSES) > 0) + delay = MIN(1 << (n - 1), MAX_RESPONSE_DELAY); + else + delay = 0; + + if (delay > 0) { + struct timeval tv = { delay, 0 }; + + s->s_env->stats->smtp.delays++; + evtimer_set(&s->s_ev, session_respond_delayed, s); + evtimer_add(&s->s_ev, &tv); + } else + bufferevent_enable(s->s_bev, EV_WRITE); +} + +void +session_respond_delayed(int fd, short event, void *p) +{ + struct session *s = p; + bufferevent_enable(s->s_bev, EV_WRITE); } diff --git a/usr.sbin/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index 597c9b32e54..65dd4302bb0 100644 --- a/usr.sbin/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.27 2009/05/24 14:22:24 jacekm Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.28 2009/05/27 13:09:07 jacekm Exp $ */ /* * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -277,6 +277,7 @@ show_stats_output(struct imsg *imsg) printf("runner.active=%zd\n", stats->runner.active); + printf("smtp.errors.delays=%zd\n", stats->smtp.delays); printf("smtp.errors.linetoolong=%zd\n", stats->smtp.linetoolong); printf("smtp.errors.read_eof=%zd\n", stats->smtp.read_eof); printf("smtp.errors.read_system=%zd\n", stats->smtp.read_error); diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index 5b20d08ba09..70f539f8eca 100644 --- a/usr.sbin/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.116 2009/05/25 14:00:36 jacekm Exp $ */ +/* $OpenBSD: smtpd.h,v 1.117 2009/05/27 13:09:07 jacekm Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> @@ -67,7 +67,13 @@ #define PATH_OFFLINE "/offline" /* number of MX records to lookup */ -#define MAX_MX_COUNT 10 +#define MAX_MX_COUNT 10 + +/* max response delay under flood conditions */ +#define MAX_RESPONSE_DELAY 60 + +/* how many responses per state are undelayed */ +#define FAST_RESPONSES 2 /* rfc5321 limits */ #define SMTP_TEXTLINE_MAX 1000 @@ -563,6 +569,7 @@ enum session_state { S_DONE, S_QUIT }; +#define STATE_COUNT 18 struct ssl { SPLAY_ENTRY(ssl) ssl_nodes; @@ -623,6 +630,7 @@ struct session { int s_buflen; struct timeval s_tv; struct message s_msg; + short s_nresp[STATE_COUNT]; size_t rcptcount; struct auth s_auth; @@ -706,6 +714,7 @@ struct s_session { size_t toofast; size_t tempfail; size_t linetoolong; + size_t delays; }; struct stats { |