aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2009-02-25 14:03:42 +0300
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2009-02-25 14:03:42 +0300
commitb9a9496cc4cd6619fc1a7c6a59d39e7147a9f20e (patch)
tree43b692bdccc08ae8672ae0b576fbf1e1baaa6a79
parent1cd34f5283bcf1d4973a351dc5235094608e3d2d (diff)
downloadrspamd-b9a9496cc4cd6619fc1a7c6a59d39e7147a9f20e.tar.gz
rspamd-b9a9496cc4cd6619fc1a7c6a59d39e7147a9f20e.zip
* Fix memory corruption and lmtp handling
* Add e-mail address validation in lmtp
-rw-r--r--CMakeLists.txt7
-rw-r--r--config.h.in6
-rw-r--r--src/controller.c16
-rw-r--r--src/fstring.c15
-rw-r--r--src/lmtp.c6
-rw-r--r--src/lmtp_proto.c140
-rw-r--r--src/mem_pool.c21
-rw-r--r--src/mem_pool.h27
8 files changed, 168 insertions, 70 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e11320de1..7a1031b93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -147,9 +147,16 @@ CHECK_INCLUDE_FILES(netdb.h HAVE_NETDB_H)
CHECK_INCLUDE_FILES(syslog.h HAVE_SYSLOG_H)
CHECK_INCLUDE_FILES(libgen.h HAVE_LIBGEN_H)
+IF(HAVE_SYS_WAIT_H)
+ LIST(APPEND CMAKE_REQUIRED_INCLUDES sys/wait.h)
+ENDIF(HAVE_SYS_WAIT_H)
+
CHECK_FUNCTION_EXISTS(setproctitle HAVE_SETPROCTITLE)
CHECK_FUNCTION_EXISTS(getpagesize HAVE_GETPAGESIZE)
CHECK_FUNCTION_EXISTS(nanosleep HAVE_NANOSLEEP)
+CHECK_FUNCTION_EXISTS(vfork HAVE_VFORK)
+CHECK_FUNCTION_EXISTS(wait4 HAVE_WAIT4)
+CHECK_FUNCTION_EXISTS(waitpid HAVE_WAITPID)
CHECK_SYMBOL_EXISTS(PATH_MAX limits.h HAVE_PATH_MAX)
CHECK_SYMBOL_EXISTS(MAXPATHLEN sys/param.h HAVE_MAXPATHLEN)
diff --git a/config.h.in b/config.h.in
index b77d31454..23d08ee36 100644
--- a/config.h.in
+++ b/config.h.in
@@ -72,6 +72,12 @@
#cmakedefine HAVE_SC_NPROCESSORS_ONLN 1
+#cmakedefine HAVE_VFORK 1
+
+#cmakedefine HAVE_WAIT4 1
+
+#cmakedefine HAVE_WAITPID 1
+
#cmakedefine DEBUG_MODE 1
#define RVERSION "${RSPAMD_VERSION}"
diff --git a/src/controller.c b/src/controller.c
index 2b1b5099e..1b2e72634 100644
--- a/src/controller.c
+++ b/src/controller.c
@@ -194,14 +194,14 @@ process_command (struct controller_command *cmd, char **cmd_args, struct control
session->worker->srv->stat->connections_count);
r += snprintf (out_buf + r, sizeof (out_buf) - r, "Control connections count: %u" CRLF,
session->worker->srv->stat->control_connections_count);
- r += snprintf (out_buf + r, sizeof (out_buf) - r, "Bytes allocated: %zd" CRLF,
- mem_st.bytes_allocated);
- r += snprintf (out_buf + r, sizeof (out_buf) - r, "Memory chunks allocated: %zd" CRLF,
- mem_st.chunks_allocated);
- r += snprintf (out_buf + r, sizeof (out_buf) - r, "Shared chunks allocated: %zd" CRLF,
- mem_st.shared_chunks_allocated);
- r += snprintf (out_buf + r, sizeof (out_buf) - r, "Chunks freed: %zd" CRLF,
- mem_st.chunks_freed);
+ r += snprintf (out_buf + r, sizeof (out_buf) - r, "Bytes allocated: %ld" CRLF,
+ (long int)mem_st.bytes_allocated);
+ r += snprintf (out_buf + r, sizeof (out_buf) - r, "Memory chunks allocated: %ld" CRLF,
+ (long int)mem_st.chunks_allocated);
+ r += snprintf (out_buf + r, sizeof (out_buf) - r, "Shared chunks allocated: %ld" CRLF,
+ (long int)mem_st.shared_chunks_allocated);
+ r += snprintf (out_buf + r, sizeof (out_buf) - r, "Chunks freed: %ld" CRLF,
+ (long int)mem_st.chunks_freed);
rspamd_dispatcher_write (session->dispatcher, out_buf, r, FALSE);
}
break;
diff --git a/src/fstring.c b/src/fstring.c
index 82d0b095e..3098e8726 100644
--- a/src/fstring.c
+++ b/src/fstring.c
@@ -190,14 +190,16 @@ fstrcpy (f_str_t *dest, f_str_t *src)
size_t
fstrcat (f_str_t *dest, f_str_t *src)
{
- register size_t cur = src->len;
+ register size_t cur = 0;
+ char *p = dest->begin + dest->len;
if (dest->size < src->len + dest->len) {
return 0;
}
- while (cur < src->len && cur < dest->size) {
- *(dest->begin + cur) = *(src->begin + cur);
+ while (cur < src->len) {
+ *p = *(src->begin + cur);
+ p ++;
cur ++;
}
@@ -246,14 +248,7 @@ fstralloc (memory_pool_t *pool, size_t len)
{
f_str_t *res = memory_pool_alloc (pool, sizeof (f_str_t));
- if (res == NULL) {
- return NULL;
- }
res->begin = memory_pool_alloc (pool, len);
- if (res->begin == NULL) {
- free (res);
- return NULL;
- }
res->size = len;
res->len = 0;
diff --git a/src/lmtp.c b/src/lmtp.c
index ba03cd93d..f7aec0bdf 100644
--- a/src/lmtp.c
+++ b/src/lmtp.c
@@ -260,6 +260,12 @@ start_lmtp_worker (struct rspamd_worker *worker)
init_signals (&signals, sig_handler);
sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL);
+ /* Ignore SIGPIPE for further use in LDA delivery */
+ sigemptyset (&signals.sa_mask);
+ sigaddset (&signals.sa_mask, SIGPIPE);
+ signals.sa_handler = SIG_IGN;
+ sigaction (SIGPIPE, &signals, NULL);
+ sigprocmask (SIG_UNBLOCK, &signals.sa_mask, NULL);
/* SIGUSR2 handler */
signal_set (&worker->sig_ev, SIGUSR2, sigusr_handler, (void *) worker);
diff --git a/src/lmtp_proto.c b/src/lmtp_proto.c
index df53f69e3..9237dfc28 100644
--- a/src/lmtp_proto.c
+++ b/src/lmtp_proto.c
@@ -53,6 +53,36 @@ static f_str_t data_dot = {
.len = sizeof (".\r\n") - 1
};
+static const char *mail_regexp = "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
+static GRegex *mail_re = NULL;
+
+/*
+ * Extract e-mail from read line
+ * return <> if no valid address detected
+ */
+static char *
+extract_mail (memory_pool_t *pool, f_str_t *line)
+{
+ GError *err = NULL;
+ char *match;
+ GMatchInfo *info;
+
+ if (mail_re == NULL) {
+ /* Compile regexp */
+ mail_re = g_regex_new (mail_regexp, G_REGEX_RAW, 0, &err);
+ }
+
+ if (g_regex_match_full (mail_re, line->begin, line->len, 0, 0, &info, NULL) == TRUE) {
+ match = memory_pool_strdup (pool, g_match_info_fetch (info, 0));
+ g_match_info_free (info);
+ }
+ else {
+ match = "<>";
+ }
+
+ return match;
+}
+
static void
out_lmtp_reply (struct rspamd_lmtp_proto *lmtp, int code, char *rcode, char *msg)
{
@@ -72,6 +102,7 @@ int
read_lmtp_input_line (struct rspamd_lmtp_proto *lmtp, f_str_t *line)
{
char *c, *rcpt;
+ f_str_t fstr;
unsigned int i = 0, l = 0, size;
switch (lmtp->state) {
@@ -108,19 +139,9 @@ read_lmtp_input_line (struct rspamd_lmtp_proto *lmtp, f_str_t *line)
else {
i += mail_command.len;
c = line->begin + i;
- /* Get data from brackets (<>)*/
- while (*c++ != '<' && i < line->len) {
- i ++;
- }
- while (*c != '>' && i < line->len) {
- l ++;
- c ++;
- i ++;
- }
-
- lmtp->task->from = memory_pool_alloc (lmtp->task->task_pool, l + 1);
- /* Strlcpy makes string null terminated by design */
- g_strlcpy (lmtp->task->from, c - l, l + 1);
+ fstr.begin = line->begin + i;
+ fstr.len = line->len - i;
+ lmtp->task->from = extract_mail (lmtp->task->task_pool, &fstr);
lmtp->state = LMTP_READ_RCPT;
out_lmtp_reply (lmtp, LMTP_OK, "2.1.0", "Sender ok");
return 0;
@@ -130,24 +151,22 @@ read_lmtp_input_line (struct rspamd_lmtp_proto *lmtp, f_str_t *line)
/* Search RCPT_TO: line */
if ((i = fstrstri (line, &rcpt_command)) == -1) {
msg_info ("read_lmtp_input_line: RCPT expected but not found");
- out_lmtp_reply (lmtp, LMTP_BAD_CMD, "5.0.0", "Need RCPT here");
+ out_lmtp_reply (lmtp, LMTP_NO_RCPT, "5.5.4", "Need RCPT here");
return -1;
}
else {
i += rcpt_command.len;
c = line->begin + i;
- /* Get data from brackets (<>)*/
- while (*c++ != '<' && i < line->len) {
- i ++;
+ fstr.begin = line->begin + i;
+ fstr.len = line->len - i;
+ rcpt = extract_mail (lmtp->task->task_pool, &fstr);
+ if (*rcpt == '<' && *(rcpt + 1) == '>') {
+ /* Invalid or empty rcpt not allowed */
+ msg_info ("read_lmtp_input_line: bad recipient");
+ out_lmtp_reply (lmtp, LMTP_NO_RCPT, "5.5.4", "Bad recipient");
+ return -1;
}
- while (*c != '>' && i < line->len) {
- l ++;
- c ++;
- i ++;
- }
- rcpt = memory_pool_alloc (lmtp->task->task_pool, l + 1);
/* Strlcpy makes string null terminated by design */
- g_strlcpy (rcpt, c - l, l + 1);
lmtp->task->rcpt = g_list_prepend (lmtp->task->rcpt, rcpt);
lmtp->state = LMTP_READ_DATA;
out_lmtp_reply (lmtp, LMTP_OK, "2.1.0", "Recipient ok");
@@ -162,7 +181,7 @@ read_lmtp_input_line (struct rspamd_lmtp_proto *lmtp, f_str_t *line)
return -1;
}
else {
- i += rcpt_command.len;
+ i += data_command.len;
c = line->begin + i;
/* Skip spaces */
while (isspace (*c++)) {
@@ -299,43 +318,98 @@ format_lda_args (struct worker_task *task)
static int
lmtp_deliver_lda (struct worker_task *task)
{
- char *args;
- FILE *lda;
+ char *args, **argv;
GMimeStream *stream;
- int rc, ecode;
+ int rc, ecode, p[2], argc;
+ pid_t cpid, pid;
if ((args = format_lda_args (task)) == NULL) {
return -1;
}
+
+ /* Format arguments in shell style */
+ if (!g_shell_parse_argv (args, &argc, &argv, NULL)) {
+ msg_info ("lmtp_deliver_lda: cannot parse arguments");
+ return -1;
+ }
- lda = popen (args, "w");
- if (lda == NULL) {
- msg_info ("lmtp_deliver_lda: cannot deliver to lda, %m");
+ if (pipe (p) == -1) {
+ g_strfreev (argv);
+ msg_info ("lmtp_deliver_lda: cannot open pipe: %m");
return -1;
}
+
+ /* Fork to exec LDA */
+#ifdef HAVE_VFORK
+ if ((cpid = vfork ()) == -1) {
+ g_strfreev (argv);
+ msg_info ("lmtp_deliver_lda: cannot fork: %m");
+ return -1;
+ }
+#else
+ if ((cpid = fork ()) == -1) {
+ g_strfreev (argv);
+ msg_info ("lmtp_deliver_lda: cannot fork: %m");
+ return -1;
+ }
+#endif
- stream = g_mime_stream_file_new (lda);
+ if (cpid == 0) {
+ /* Child process, close write pipe and keep only read one */
+ close (p[1]);
+ /* Set standart IO descriptors */
+ if (p[0] != STDIN_FILENO) {
+ (void)dup2(p[0], STDIN_FILENO);
+ (void)close(p[0]);
+ }
+
+ execv (argv[0], argv);
+ _exit (127);
+ }
+
+ close (p[0]);
+ stream = g_mime_stream_fs_new (p[1]);
if (g_mime_object_write_to_stream ((GMimeObject *)task->message, stream) == -1) {
+ g_strfreev (argv);
msg_info ("lmtp_deliver_lda: cannot write stream to lda");
return -1;
}
- rc = pclose (lda);
+ g_object_unref (stream);
+ close (p[1]);
+
+#if defined(HAVE_WAIT4)
+ do {
+ pid = wait4(cpid, &rc, 0, NULL);
+ } while (pid == -1 && errno == EINTR);
+#elif defined(HAVE_WAITPID)
+ do {
+ pid = waitpid(cpid, &rc, 0);
+ } while (pid == -1 && errno == EINTR);
+#else
+#error wait mechanisms are undefined
+#endif
if (rc == -1) {
+ g_strfreev (argv);
msg_info ("lmtp_deliver_lda: lda returned error code");
return -1;
}
else if (WIFEXITED (rc)) {
ecode = WEXITSTATUS (rc);
if (ecode == 0) {
+ g_strfreev (argv);
return 0;
}
else {
+ g_strfreev (argv);
msg_info ("lmtp_deliver_lda: lda returned error code %d", ecode);
return -1;
}
}
+
+ g_strfreev (argv);
+ return -1;
}
int
diff --git a/src/mem_pool.c b/src/mem_pool.c
index ecdfbb2b5..eb722fb92 100644
--- a/src/mem_pool.c
+++ b/src/mem_pool.c
@@ -52,9 +52,12 @@ pthread_mutex_t stat_mtx = PTHREAD_MUTEX_INITIALIZER;
static memory_pool_stat_t *mem_pool_stat = NULL;
static struct _pool_chain *
-pool_chain_new (size_t size)
+pool_chain_new (memory_pool_ssize_t size)
{
struct _pool_chain *chain;
+
+ g_assert (size > 0);
+
chain = g_malloc (sizeof (struct _pool_chain));
chain->begin = g_malloc (size);
chain->len = size;
@@ -68,7 +71,7 @@ pool_chain_new (size_t size)
}
static struct _pool_chain_shared *
-pool_chain_new_shared (size_t size)
+pool_chain_new_shared (memory_pool_ssize_t size)
{
struct _pool_chain_shared *chain;
@@ -111,10 +114,11 @@ pool_chain_new_shared (size_t size)
* @return new memory pool object
*/
memory_pool_t*
-memory_pool_new (size_t size)
+memory_pool_new (memory_pool_ssize_t size)
{
memory_pool_t *new;
+ g_assert (size > 0);
/* Allocate statistic structure if it is not allocated before */
if (mem_pool_stat == NULL) {
#if defined(HAVE_MMAP_ANON)
@@ -142,7 +146,7 @@ memory_pool_new (size_t size)
}
void *
-memory_pool_alloc (memory_pool_t *pool, size_t size)
+memory_pool_alloc (memory_pool_t *pool, memory_pool_ssize_t size)
{
u_char *tmp;
struct _pool_chain *new, *cur;
@@ -185,7 +189,7 @@ memory_pool_alloc (memory_pool_t *pool, size_t size)
}
void *
-memory_pool_alloc0 (memory_pool_t *pool, size_t size)
+memory_pool_alloc0 (memory_pool_t *pool, memory_pool_ssize_t size)
{
void *pointer = memory_pool_alloc (pool, size);
if (pointer) {
@@ -197,7 +201,7 @@ memory_pool_alloc0 (memory_pool_t *pool, size_t size)
char *
memory_pool_strdup (memory_pool_t *pool, const char *src)
{
- size_t len;
+ memory_pool_ssize_t len;
char *newstr;
if (src == NULL) {
@@ -211,12 +215,13 @@ memory_pool_strdup (memory_pool_t *pool, const char *src)
}
void *
-memory_pool_alloc_shared (memory_pool_t *pool, size_t size)
+memory_pool_alloc_shared (memory_pool_t *pool, memory_pool_ssize_t size)
{
u_char *tmp;
struct _pool_chain_shared *new, *cur;
if (pool) {
+ g_assert (size > 0);
cur = pool->shared_pool;
if (!cur) {
cur = pool_chain_new_shared (pool->first_pool->len);
@@ -384,7 +389,7 @@ memory_pool_stat (memory_pool_stat_t *st)
}
#define FIXED_POOL_SIZE 4095
-size_t
+memory_pool_ssize_t
memory_pool_get_size ()
{
#ifdef HAVE_GETPAGESIZE
diff --git a/src/mem_pool.h b/src/mem_pool.h
index af4f3d063..d332a2c11 100644
--- a/src/mem_pool.h
+++ b/src/mem_pool.h
@@ -20,12 +20,17 @@
typedef void (*pool_destruct_func)(void *ptr);
/**
+ * Type that represents allocating size
+ */
+typedef long int memory_pool_ssize_t;
+
+/**
* Pool page structure
*/
struct _pool_chain {
u_char *begin; /**< begin of pool chain block */
u_char *pos; /**< current start of free space in block */
- size_t len; /**< length of block */
+ memory_pool_ssize_t len; /**< length of block */
struct _pool_chain *next; /**< chain link */
};
@@ -35,7 +40,7 @@ struct _pool_chain {
struct _pool_chain_shared {
u_char *begin;
u_char *pos;
- size_t len;
+ memory_pool_ssize_t len;
gint lock;
struct _pool_chain_shared *next;
};
@@ -63,10 +68,10 @@ typedef struct memory_pool_s {
* Statistics structure
*/
typedef struct memory_pool_stat_s {
- size_t bytes_allocated; /**< bytes that are allocated with pool allocator */
- size_t chunks_allocated; /**< number of chunks that are allocated */
- size_t shared_chunks_allocated; /**< shared chunks allocated */
- size_t chunks_freed; /**< chunks freed */
+ memory_pool_ssize_t bytes_allocated; /**< bytes that are allocated with pool allocator */
+ memory_pool_ssize_t chunks_allocated; /**< number of chunks that are allocated */
+ memory_pool_ssize_t shared_chunks_allocated; /**< shared chunks allocated */
+ memory_pool_ssize_t chunks_freed; /**< chunks freed */
} memory_pool_stat_t;
/**
@@ -82,7 +87,7 @@ typedef struct memory_pool_rwlock_s {
* @param size size of pool's page
* @return new memory pool object
*/
-memory_pool_t* memory_pool_new (size_t size);
+memory_pool_t* memory_pool_new (memory_pool_ssize_t size);
/**
* Get memory from pool
@@ -90,7 +95,7 @@ memory_pool_t* memory_pool_new (size_t size);
* @param size bytes to allocate
* @return pointer to allocated object
*/
-void* memory_pool_alloc (memory_pool_t* pool, size_t size);
+void* memory_pool_alloc (memory_pool_t* pool, memory_pool_ssize_t size);
/**
* Get memory and set it to zero
@@ -98,7 +103,7 @@ void* memory_pool_alloc (memory_pool_t* pool, size_t size);
* @param size bytes to allocate
* @return pointer to allocated object
*/
-void* memory_pool_alloc0 (memory_pool_t* pool, size_t size);
+void* memory_pool_alloc0 (memory_pool_t* pool, memory_pool_ssize_t size);
/**
* Make a copy of string in pool
@@ -113,7 +118,7 @@ char* memory_pool_strdup (memory_pool_t* pool, const char *src);
* @param pool memory pool object
* @param size bytes to allocate
*/
-void* memory_pool_alloc_shared (memory_pool_t *pool, size_t size);
+void* memory_pool_alloc_shared (memory_pool_t *pool, memory_pool_ssize_t size);
/**
* Lock chunk of shared memory in which pointer is placed
@@ -203,7 +208,7 @@ void memory_pool_stat (memory_pool_stat_t *st);
* Get optimal pool size based on page size for this system
* @return size of memory page in system
*/
-size_t memory_pool_get_size ();
+memory_pool_ssize_t memory_pool_get_size ();
/**
* Macro that return free space in pool page