Просмотр исходного кода

Updates to address control characters inside From: header

Vijay Sarvepalli 3 месяцев назад
Родитель
Сommit
335a603b1b
2 измененных файлов с 56 добавлено и 18 удалено
  1. 4 0
      CHANGELOG.md
  2. 52 18
      src/milterfrom.c

+ 4 - 0
CHANGELOG.md

@@ -11,3 +11,7 @@ Version 1.0.2  2023-09-18
 * Fixed bug on null sender to SMFIS_ACCEPT and no more filtering
 * Added syslog on null sender event with connection details (IP,name)
 * Changed syslog to be from log_event routine
+
+Version 1.0.3 2025-09-24
+* Added verification of no control characters inside carrots.
+[See references https://blog.slonser.info/posts/email-attacks/ ]

+ 52 - 18
src/milterfrom.c

@@ -44,6 +44,9 @@
 #include <stdint.h>
 #include <syslog.h>
 
+#include <ctype.h>
+#include <stddef.h>
+
 #include "libmilter/mfapi.h"
 #include "libmilter/mfdef.h"
 
@@ -55,7 +58,7 @@ struct mlfiPriv {
 };
 
 #define MLFIPRIV ((struct mlfiPriv*)smfi_getpriv(ctx))
-#define VERSION "1.0.2"
+#define VERSION "1.0.3"
 
 extern const char *__progname;
 
@@ -65,23 +68,54 @@ static unsigned long mta_caps = 0;
 // contains a < with a subsequent >, the inner part is used. If not, the whole
 // header field is used. This allows matching "Max Mustermann
 // <max.mustermann@example.invalid>".
-const char *parse_address(const char *address, size_t *len)
-{
-	size_t inlen = strlen(address);
-	size_t pos_open = SIZE_MAX, pos_close = SIZE_MAX;
-	size_t i;
-	for (i = 0; i < inlen; ++i) {
-		if (address[i] == '<') pos_open = i;
-		else if (address[i] == '>') pos_close = i;
-	}
-
-	if (pos_open != SIZE_MAX && pos_close != SIZE_MAX && pos_open < pos_close) {
-		*len = pos_close - pos_open - 1;
-		return address + pos_open + 1;
-	} else {
-		*len = inlen;
-		return address;
-	}
+const char *parse_address(const char *header, size_t *len) {
+    if (!header || !len) return NULL;
+
+    const char *start = NULL;
+    const char *end = NULL;
+    size_t i;
+
+    /* Find the last '<' and the corresponding '>' */
+    for (i = 0; header[i]; i++) {
+        if (header[i] == '<') start = header + i + 1;
+        else if (header[i] == '>') end = header + i;
+    }
+
+    if (start && end && start < end) {
+        /* Trim whitespace from start */
+        while (start < end && isspace((unsigned char)*start)) start++;
+        /* Trim whitespace from end */
+        while (end > start && isspace((unsigned char)*(end-1))) end--;
+
+        /* Reject if empty */
+        if (end <= start) return NULL;
+
+        /* Reject if any control chars (\r, \n, 0x00-0x1F, 0x7F) */
+        for (const char *p = start; p < end; p++) {
+            if ((unsigned char)*p < 32 || *p == 127) return NULL;
+        }
+
+        *len = (size_t)(end - start);
+        return start;
+    }
+
+    /* No angle brackets: treat the entire header as address */
+    start = header;
+    end = header + strlen(header);
+
+    /* Trim whitespace */
+    while (start < end && isspace((unsigned char)*start)) start++;
+    while (end > start && isspace((unsigned char)*(end-1))) end--;
+
+    if (end <= start) return NULL;
+
+    /* Reject control characters */
+    for (const char *p = start; p < end; p++) {
+        if ((unsigned char)*p < 32 || *p == 127) return NULL;
+    }
+
+    *len = (size_t)(end - start);
+    return start;
 }
 
 void log_event(SMFICTX *ctx, char *msg) {