summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-05-27 13:09:08 +0000
committerJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-05-27 13:09:08 +0000
commit864dce2bc80e7c0ba01cabe9f8f53e7bbfdb85e3 (patch)
tree208c92dbab2a633fcf317a17dc286cc3c1649649 /usr.sbin
parentbc74c06e354abc8aa1fda72cd517b1cb48c394e9 (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.c72
-rw-r--r--usr.sbin/smtpd/smtpctl.c3
-rw-r--r--usr.sbin/smtpd/smtpd.h13
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 {