aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2012-05-28 17:26:38 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2012-05-28 17:26:38 +0400
commit00c7c629771af48e4bc0756ac9b2eb2bba26d583 (patch)
tree5b0ea80dc745c868673663d53f668edb7b9f923c /src
parent5a0ad4684967e746b71f635f397ea4c0ae373f69 (diff)
downloadrspamd-00c7c629771af48e4bc0756ac9b2eb2bba26d583.tar.gz
rspamd-00c7c629771af48e4bc0756ac9b2eb2bba26d583.zip
* Avoid DoS while sending a message with a lot of recipients in 'To' header.
Fix possible deadlock in lua_redis. Version is now 0.4.8 as there are enough changes.
Diffstat (limited to 'src')
-rw-r--r--src/lua/lua_redis.c19
-rw-r--r--src/lua/lua_task.c17
-rw-r--r--src/plugins/lua/ratelimit.lua13
3 files changed, 44 insertions, 5 deletions
diff --git a/src/lua/lua_redis.c b/src/lua/lua_redis.c
index 0358a6960..0c1934345 100644
--- a/src/lua/lua_redis.c
+++ b/src/lua/lua_redis.c
@@ -98,8 +98,12 @@ static void
lua_redis_push_error (const gchar *err, struct lua_redis_userdata *ud, gboolean connected)
{
struct worker_task **ptask;
+ gboolean need_unlock = FALSE;
- g_mutex_lock (lua_mtx);
+ /* Avoid LOR here as mutex can be acquired before in lua_call */
+ if (g_mutex_trylock (lua_mtx)) {
+ need_unlock = TRUE;
+ }
/* Push error */
lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref);
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *));
@@ -113,7 +117,9 @@ lua_redis_push_error (const gchar *err, struct lua_redis_userdata *ud, gboolean
if (lua_pcall (ud->L, 3, 0, 0) != 0) {
msg_info ("call to callback failed: %s", lua_tostring (ud->L, -1));
}
- g_mutex_unlock (lua_mtx);
+ if (need_unlock) {
+ g_mutex_unlock (lua_mtx);
+ }
if (connected) {
remove_normal_event (ud->task->s, lua_redis_fin, ud);
@@ -130,8 +136,11 @@ static void
lua_redis_push_data (const redisReply *r, struct lua_redis_userdata *ud)
{
struct worker_task **ptask;
+ gboolean need_unlock = FALSE;
- g_mutex_lock (lua_mtx);
+ if (g_mutex_trylock (lua_mtx)) {
+ need_unlock = TRUE;
+ }
/* Push error */
lua_rawgeti (ud->L, LUA_REGISTRYINDEX, ud->cbref);
ptask = lua_newuserdata (ud->L, sizeof (struct worker_task *));
@@ -161,7 +170,9 @@ lua_redis_push_data (const redisReply *r, struct lua_redis_userdata *ud)
if (lua_pcall (ud->L, 3, 0, 0) != 0) {
msg_info ("call to callback failed: %s", lua_tostring (ud->L, -1));
}
- g_mutex_unlock (lua_mtx);
+ if (need_unlock) {
+ g_mutex_unlock (lua_mtx);
+ }
remove_normal_event (ud->task->s, lua_redis_fin, ud);
}
diff --git a/src/lua/lua_task.c b/src/lua/lua_task.c
index 5695b6f42..e44b873c0 100644
--- a/src/lua/lua_task.c
+++ b/src/lua/lua_task.c
@@ -69,6 +69,7 @@ LUA_FUNCTION_DEF (task, get_helo);
LUA_FUNCTION_DEF (task, get_images);
LUA_FUNCTION_DEF (task, get_symbol);
LUA_FUNCTION_DEF (task, get_date);
+LUA_FUNCTION_DEF (task, get_message_id);
LUA_FUNCTION_DEF (task, get_timeval);
LUA_FUNCTION_DEF (task, get_metric_score);
LUA_FUNCTION_DEF (task, get_metric_action);
@@ -101,6 +102,7 @@ static const struct luaL_reg tasklib_m[] = {
LUA_INTERFACE_DEF (task, get_images),
LUA_INTERFACE_DEF (task, get_symbol),
LUA_INTERFACE_DEF (task, get_date),
+ LUA_INTERFACE_DEF (task, get_message_id),
LUA_INTERFACE_DEF (task, get_timeval),
LUA_INTERFACE_DEF (task, get_metric_score),
LUA_INTERFACE_DEF (task, get_metric_action),
@@ -1194,6 +1196,21 @@ lua_task_get_date (lua_State *L)
}
static gint
+lua_task_get_message_id (lua_State *L)
+{
+ struct worker_task *task = lua_check_task (L);
+
+ if (task != NULL && task->message_id != NULL) {
+ lua_pushstring (L, task->message_id);
+ }
+ else {
+ lua_pushnil (L);
+ }
+
+ return 1;
+}
+
+static gint
lua_task_get_timeval (lua_State *L)
{
struct worker_task *task = lua_check_task (L);
diff --git a/src/plugins/lua/ratelimit.lua b/src/plugins/lua/ratelimit.lua
index 7b1ea3eff..39bac5534 100644
--- a/src/plugins/lua/ratelimit.lua
+++ b/src/plugins/lua/ratelimit.lua
@@ -21,6 +21,7 @@ local bounce_senders = {'postmaster', 'mailer-daemon', '', 'null', 'fetchmail-da
-- Do not check ratelimits for these senders
local whitelisted_rcpts = {'postmaster', 'mailer-daemon'}
local whitelisted_ip = nil
+local max_rcpt = 5
local upstreams = nil
--- Parse atime and bucket of limit
@@ -167,6 +168,11 @@ local function rate_test_set(task, func)
rcpts = task:get_recipients_headers()
end
if rcpts then
+ if table.maxn(rcpts) > max_rcpt then
+ rspamd_logger.info(string.format('message <%s> contains %d recipients, maximum is %d',
+ task:get_message_id(), table.maxn(rcpts), max_rcpt))
+ return
+ end
for i,r in ipairs(rcpts) do
rcpts_user[i] = get_local_part(r['addr'])
end
@@ -276,6 +282,7 @@ if rspamd_config:get_api_version() >= 9 then
rspamd_config:register_module_option('ratelimit', 'whitelisted_rcpts', 'string')
rspamd_config:register_module_option('ratelimit', 'whitelisted_ip', 'map')
rspamd_config:register_module_option('ratelimit', 'limit', 'string')
+ rspamd_config:register_module_option('ratelimit', 'max_rcpt', 'uint')
end
local function parse_whitelisted_rcpts(str)
@@ -301,6 +308,10 @@ if opts then
whitelisted_ip = rspamd_config:add_hash_map (opts['whitelisted_ip'])
end
+ if opts['max_rcpt'] then
+ max_rcpt = tonumber (opts['max_rcpt'])
+ end
+
if not opts['servers'] then
rspamd_logger.err('no servers are specified')
else
@@ -312,4 +323,4 @@ if opts then
rspamd_config:register_post_filter(rate_set)
end
end
-end
+end \ No newline at end of file