smtp: distinguish bad paths from unknown users
Debian Trixie package bundle / packages (push) Successful in 15m54s

This commit is contained in:
Mario Fetka
2026-07-28 07:45:31 +02:00
parent 3209fc32dd
commit 0d0f97ea59
3 changed files with 55 additions and 2 deletions
+43 -1
View File
@@ -233,7 +233,49 @@ SMTPPathIsSafe(const char *path)
int
SMTPRecipientPathIsValid(const char *path)
{
return path != NULL && path[0] != '\0' && SMTPPathIsSafe(path);
const unsigned char *cursor = (const unsigned char *)path;
const unsigned char *separator = NULL;
const unsigned char *local_end;
int previous_dot = 0;
if (cursor == NULL || *cursor == '\0' || !SMTPPathIsSafe(path)) return 0;
/*
* RewriteAddress() understands obsolete percent routes, but accepting a
* second raw '@' here would turn malformed SMTP input into a routing
* lookup and make it indistinguishable from an unknown mailbox. Its
* resolver protocol cannot preserve quoted local-parts, so accept the
* RFC 5321 dot-string form here rather than silently changing one.
*/
local_end = cursor;
while (*local_end != '\0' && *local_end != '@') local_end++;
if (local_end == cursor) return 0;
while (cursor < local_end) {
unsigned char current = *cursor++;
if (current == '.') {
if (previous_dot ||
cursor - 1 == (const unsigned char *)path) return 0;
previous_dot = 1;
continue;
}
previous_dot = 0;
if (current >= 0x80U || isalnum(current) ||
strchr("!#$%&'*+-/=?^_`{|}~", current) != NULL)
continue;
return 0;
}
if (previous_dot) return 0;
if (*local_end == '@') {
separator = local_end;
cursor = local_end + 1;
} else {
cursor = local_end;
}
if (separator == NULL) return *cursor == '\0';
if (*cursor == '\0' || strchr((const char *)cursor, '@') != NULL) return 0;
return 1;
}
static int
+2 -1
View File
@@ -1582,7 +1582,8 @@ HandleConnection (void *param)
if ((ReplyInt = RewriteAddress (Client->nmap.conn,
RoutingName, To,
sizeof (To))) == MAIL_BOGUS) {
ConnWrite (Client->client.conn, MSG501RECIPNO, MSG501RECIPNO_LEN);
ConnWrite(Client->client.conn, MSG550NOTFOUND,
MSG550NOTFOUND_LEN);
}
else {
if (ReplyInt == MAIL_REMOTE &&
+10
View File
@@ -143,10 +143,20 @@ TestPaths(void)
assert(!SMTPPathIsSafe("user> SIZE=0"));
assert(!SMTPPathIsSafe("bad\177@example.test"));
assert(SMTPRecipientPathIsValid("user@example.test"));
assert(SMTPRecipientPathIsValid("smtp18-missing-600189@example.test"));
assert(SMTPRecipientPathIsValid("m\303\274ller@example.test"));
assert(!SMTPRecipientPathIsValid(NULL));
assert(!SMTPRecipientPathIsValid(""));
assert(!SMTPRecipientPathIsValid("user name@example.test"));
assert(!SMTPRecipientPathIsValid("@example.test"));
assert(!SMTPRecipientPathIsValid("user@"));
assert(!SMTPRecipientPathIsValid("bad@@example.test"));
assert(!SMTPRecipientPathIsValid(".user@example.test"));
assert(!SMTPRecipientPathIsValid("user.@example.test"));
assert(!SMTPRecipientPathIsValid("user..tag@example.test"));
assert(!SMTPRecipientPathIsValid("\"quoted local\"@example.test"));
assert(!SMTPRecipientPathIsValid("\"quoted@local\"@example.test"));
assert(!SMTPRecipientPathIsValid("\"unterminated@example.test"));
}
static void