Index | Thread | Search

From:
Christian Schulte <cs@schulte.it>
Subject:
smtpd.conf(5): Add domain-from match option in addition to mail-from
To:
tech@openbsd.org
Date:
Tue, 24 Sep 2024 05:51:24 +0200

Download raw body.

Thread
Sorry for disturbing you again. This has been discussed on the OpenSMTPD
mailing list, where I got pointed to the "correct" direction.

<https://www.mail-archive.com/misc@opensmtpd.org/msg06370.html>

Since I sent a patch to @tech, I somehow feel I should at least send the
corrected version here as well. My bad. I should not have sent anything like
this here in the first place.

All the diff adds is a domain-from match option modeled after the mail-from
match option with the only difference, that the domain-from match option will
be backed by a table of service K_DOMAIN instead of K_MAILADDR.

The discussion on the misc@opensmtpd.org mailing list ended with

Hi

Can you stop spamming me.

That's the last response I got after having used telnet on port 25 to
demonstrate that it's exactly about this kind of spamming I woud like to
get rid of. I take that response as a proof of concept. It should not have been
possible to send those mails without beeing authenticated in the first place.
So. Here's the final diff I will be running locally although I hate running
something great with patches. I got pointed to various options including
writing a table backend myself and got told that this would be flexibility.
No. I am not going to write a whole new table backend, when I just can run
OpenSMTPD with the following diff applied.

Sorry for stealing your time. You have better things to do, of course.
I can live with running OpenSMTPD with patches, although I really hate doing
that.


Index: usr.sbin/smtpd/parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/parse.y,v
retrieving revision 1.299
diff -u -p -u -r1.299 parse.y
--- usr.sbin/smtpd/parse.y	19 Feb 2024 21:00:19 -0000	1.299
+++ usr.sbin/smtpd/parse.y	24 Sep 2024 03:13:33 -0000
@@ -167,7 +167,7 @@ typedef struct {
 %token	ACTION ADMD ALIAS ANY ARROW AUTH AUTH_OPTIONAL
 %token	BACKUP BOUNCE BYPASS
 %token	CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
-%token	DATA DATA_LINE DHE DISCONNECT DOMAIN
+%token	DATA DATA_LINE DHE DISCONNECT DOMAIN DOMAIN_FROM
 %token	EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY 
 %token	FCRDNS FILTER FOR FORWARD_ONLY FROM
 %token	GROUP
@@ -1129,6 +1129,41 @@ negation TAG REGEX tables {
 	rule->flag_smtp_auth_regex = 1;
 	rule->table_smtp_auth = strdup(t->t_name);
 }
+| negation DOMAIN_FROM tables {
+	struct table   *t = $3;
+
+	if (rule->flag_smtp_domain_from) {
+		yyerror("domain-from already specified for this rule");
+		YYERROR;
+	}
+
+	if (!table_check_use(t, T_DYNAMIC|T_LIST|T_HASH, K_DOMAIN)) {
+		yyerror("table \"%s\" may not be used for domain-from lookups",
+		    t->t_name);
+		YYERROR;
+	}
+
+	rule->flag_smtp_domain_from = $1 ? -1 : 1;
+	rule->table_smtp_domain_from = strdup(t->t_name);
+}
+| negation DOMAIN_FROM REGEX tables {
+	struct table   *t = $4;
+
+	if (rule->flag_smtp_domain_from) {
+		yyerror("domain-from already specified for this rule");
+		YYERROR;
+	}
+
+	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+		yyerror("table \"%s\" may not be used for domain-from lookups",
+		    t->t_name);
+		YYERROR;
+	}
+
+	rule->flag_smtp_domain_from = $1 ? -1 : 1;
+	rule->flag_smtp_domain_from_regex = 1;
+	rule->table_smtp_domain_from = strdup(t->t_name);
+}
 | negation MAIL_FROM tables {
 	struct table   *t = $3;
 
@@ -1363,6 +1398,48 @@ negation TAG REGEX tables {
 	rule->table_smtp_auth = strdup(t->t_name);
 }
 
+| negation FROM DOMAIN_FROM tables {
+	struct table	*anyhost = table_find(conf, "<anyhost>");
+	struct table	*t = $4;
+
+	if (rule->flag_from) {
+		yyerror("from already specified for this rule");
+		YYERROR;
+	}
+
+	if (!table_check_use(t, T_DYNAMIC|T_LIST|T_HASH, K_DOMAIN)) {
+		yyerror("table \"%s\" may not be used for from lookups",
+		    t->t_name);
+		YYERROR;
+	}
+
+	rule->flag_from = 1;
+	rule->table_from = strdup(anyhost->t_name);
+	rule->flag_smtp_domain_from = $1 ? -1 : 1;
+	rule->table_smtp_domain_from = strdup(t->t_name);
+}
+| negation FROM DOMAIN_FROM REGEX tables {
+	struct table	*anyhost = table_find(conf, "<anyhost>");
+	struct table	*t = $5;
+
+	if (rule->flag_from) {
+		yyerror("from already specified for this rule");
+		YYERROR;
+	}
+
+	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+		yyerror("table \"%s\" may not be used for from lookups",
+		    t->t_name);
+		YYERROR;
+	}
+
+	rule->flag_from = 1;
+	rule->table_from = strdup(anyhost->t_name);
+	rule->flag_smtp_domain_from = $1 ? -1 : 1;
+	rule->flag_smtp_domain_from_regex = 1;
+	rule->table_smtp_domain_from = strdup(t->t_name);
+}
+
 | negation FROM MAIL_FROM tables {
 	struct table	*anyhost = table_find(conf, "<anyhost>");
 	struct table	*t = $4;
@@ -2667,6 +2744,7 @@ lookup(char *s)
 		{ "dhe",		DHE },
 		{ "disconnect",		DISCONNECT },
 		{ "domain",		DOMAIN },
+		{ "domain-from",	DOMAIN_FROM },
 		{ "ehlo",		EHLO },
 		{ "encryption",		ENCRYPTION },
 		{ "expand-only",      	EXPAND_ONLY },
Index: usr.sbin/smtpd/ruleset.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/ruleset.c,v
retrieving revision 1.48
diff -u -p -u -r1.48 ruleset.c
--- usr.sbin/smtpd/ruleset.c	14 Jun 2021 17:58:16 -0000	1.48
+++ usr.sbin/smtpd/ruleset.c	24 Sep 2024 03:13:33 -0000
@@ -165,6 +165,27 @@ ruleset_match_smtp_auth(struct rule *r, 
 }
 
 static int
+ruleset_match_smtp_domain_from(struct rule *r, const struct envelope *evp)
+{
+	int		ret;
+	const char	*key;
+	struct table	*table;
+	enum table_service service = K_DOMAIN;
+
+	if (!r->flag_smtp_domain_from)
+		return 1;
+
+	if (r->flag_smtp_domain_from_regex)
+		service = K_REGEX;
+
+	key = evp->sender.domain;
+	table = table_find(env, r->table_smtp_domain_from);
+	ret = table_match(table, service, key);
+
+	return MATCH_RESULT(ret, r->flag_smtp_domain_from);
+}
+
+static int
 ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
 {
 	int		ret;
@@ -230,6 +251,7 @@ ruleset_match(const struct envelope *evp
 		MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
 		MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
 		MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
+		MATCH_EVAL(ruleset_match_smtp_domain_from(r, evp));
 		MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
 		MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
 		goto matched;
Index: usr.sbin/smtpd/smtpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/smtpd.conf.5,v
retrieving revision 1.272
diff -u -p -u -r1.272 smtpd.conf.5
--- usr.sbin/smtpd/smtpd.conf.5	26 Jul 2024 06:24:52 -0000	1.272
+++ usr.sbin/smtpd/smtpd.conf.5	24 Sep 2024 03:13:33 -0000
@@ -675,6 +675,22 @@ or from the local enqueuer.
 This is the default, and may be omitted.
 .It Xo
 .Op Ic \&!
+.Cm from domain-from
+.Ar domain | Pf < Ar domain Ns >
+.Xc
+Specify that session may originate from sender domain or sender domain list
+.Ar domain ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from domain-from regex
+.Ar domain | Pf < Ar domain Ns >
+.Xc
+Specify that session may originate from regex or regex list
+.Ar domain ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
 .Cm from mail-from
 .Ar sender | Pf < Ar sender Ns >
 .Xc
Index: usr.sbin/smtpd/smtpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/smtpd.h,v
retrieving revision 1.688
diff -u -p -u -r1.688 smtpd.h
--- usr.sbin/smtpd/smtpd.h	3 Sep 2024 12:07:40 -0000	1.688
+++ usr.sbin/smtpd/smtpd.h	24 Sep 2024 03:13:33 -0000
@@ -1236,12 +1236,14 @@ struct rule {
 	int8_t	flag_smtp_helo;
 	int8_t	flag_smtp_starttls;
 	int8_t	flag_smtp_auth;
+	int8_t	flag_smtp_domain_from;
 	int8_t	flag_smtp_mail_from;
 	int8_t	flag_smtp_rcpt_to;
 
 	int8_t	flag_smtp_helo_regex;
 	int8_t	flag_smtp_starttls_regex;
 	int8_t	flag_smtp_auth_regex;
+	int8_t	flag_smtp_domain_from_regex;
 	int8_t	flag_smtp_mail_from_regex;
 	int8_t	flag_smtp_rcpt_to_regex;
 
@@ -1252,6 +1254,7 @@ struct rule {
 
 	char	*table_smtp_helo;
 	char	*table_smtp_auth;
+	char	*table_smtp_domain_from;
 	char	*table_smtp_mail_from;
 	char	*table_smtp_rcpt_to;
 
Index: usr.sbin/smtpd/to.c
===================================================================
RCS file: /cvs/src/usr.sbin/smtpd/to.c,v
retrieving revision 1.50
diff -u -p -u -r1.50 to.c
--- usr.sbin/smtpd/to.c	31 May 2023 16:51:46 -0000	1.50
+++ usr.sbin/smtpd/to.c	24 Sep 2024 03:13:33 -0000
@@ -480,6 +480,13 @@ rule_to_text(struct rule *r)
 		(void)strlcat(buf, " tls", sizeof buf);
 	}
 
+	if (r->flag_smtp_domain_from) {
+		if (r->flag_smtp_domain_from < 0)
+			(void)strlcat(buf, " !", sizeof buf);
+		(void)strlcat(buf, " domain-from ", sizeof buf);
+		(void)strlcat(buf, r->table_smtp_domain_from, sizeof buf);
+	}
+
 	if (r->flag_smtp_mail_from) {
 		if (r->flag_smtp_mail_from < 0)
 			(void)strlcat(buf, " !", sizeof buf);