all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pmg-devel] [RFC PATCH] fix-2971: DKIM: Add setting to use From header when signing
@ 2024-01-03 14:53 Maximiliano Sandoval
  2024-01-10 21:39 ` Stoiko Ivanov
  0 siblings, 1 reply; 2+ messages in thread
From: Maximiliano Sandoval @ 2024-01-03 14:53 UTC (permalink / raw)
  To: pmg-devel

We add an option to use the address from the `From:` header instead of
the Envelope From address. Following RFC 5322 [2], we use the `Sender:`
header when there are multiple addresses in the `From:` header.

From RFC 6376 [1]:

   To illustrate, in relaxed mode, if a validated DKIM signature
   successfully verifies with a "d=" domain of "example.com", and the
   RFC5322.From address is "alerts@news.example.com", the DKIM "d="
   domain and the RFC5322.From domain are considered to be "in
   alignment".  In strict mode, this test would fail, since the "d="
   domain does not exactly match the FQDN of the address.

Current Problems:

 - `dkim_sign_rfc5322` is probably not a descriptive name for the setting
 - The setting description might be improved upon

Tested with the following command:

    swaks --from foo@bar1 --to EMAIL -s PMG_ADDR:26 --data "Date: %DATE%\nTo: %TO_ADDRESS%\nFrom: foo@bar2\nSubject: test %DATE%\nMessage-Id: <%MESSAGEID%>\nX-Mailer: swaks v%SWAKS_VERSION% jetmore.org/john/code/swaks/\n%NEW_HEADERS%\n%BODY%\n"

[1] https://datatracker.ietf.org/doc/html/rfc6376
[2] https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---
 src/PMG/Config.pm        |  7 +++++++
 src/PMG/DKIMSign.pm      | 43 +++++++++++++++++++++++++++++++++++++++-
 src/PMG/RuleDB/Accept.pm |  2 +-
 src/PMG/RuleDB/BCC.pm    |  2 +-
 src/bin/pmg-smtp-filter  |  1 +
 5 files changed, 52 insertions(+), 3 deletions(-)

diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
index 7339e0d..b6c61e1 100644
--- a/src/PMG/Config.pm
+++ b/src/PMG/Config.pm
@@ -134,6 +134,11 @@ EODESC
 	    description => "Default DKIM selector",
 	    type => 'string', format => 'dns-name', #see RFC6376 3.1
 	},
+	dkim_sign_rfc5322 => {
+	    description => "Whether to use the address from the From header when DKIM signing outbound mails.",
+	    type => 'boolean',
+	    default => 0,
+	},
     };
 }
 
@@ -152,6 +157,7 @@ sub options {
 	dkim_sign => { optional => 1 },
 	dkim_sign_all_mail => { optional => 1 },
 	dkim_selector => { optional => 1 },
+	dkim_sign_rfc5322 => { optional => 1 },
     };
 }
 
@@ -1788,6 +1794,7 @@ my $pmg_service_params = {
 	dkim_selector => 1,
 	dkim_sign => 1,
 	dkim_sign_all_mail => 1,
+	dkim_sign_rfc5322 => 1,
     },
 };
 
diff --git a/src/PMG/DKIMSign.pm b/src/PMG/DKIMSign.pm
index 08197f8..009313d 100644
--- a/src/PMG/DKIMSign.pm
+++ b/src/PMG/DKIMSign.pm
@@ -84,8 +84,49 @@ sub signing_domain {
 }
 
 
+sub parse_signing_sender {
+    # If there is exactly one single address in the 'From' header we use that
+    # address for signing, otherwise we use the 'Sender' header.
+    my ($entity) = @_;
+
+    my $from_count = 0;
+    my @from_headers = $entity->head->get('from');
+    foreach my $from_header (@from_headers) {
+	my @senders = split(',', $from_header);
+	$from_count += scalar(@senders);
+    }
+
+    if ($from_count > 1) {
+	my $send_count = 0;
+	my @send_headers = $entity->head->get('sender');
+	foreach my $send_header (@send_headers) {
+	    my @senders = split(',', $send_header);
+	    $send_count += scalar(@senders);
+	}
+	if ($send_count > 1) {
+	    syslog('warning', "Email has more than one address in its 'Sender' " .
+                   "it won't be signed. See RFC 5322 Section 3.6.2");
+	} elsif ($send_count == 1) {
+	    return $entity->head->get('sender', 0);
+	} else {
+	    syslog('warning', "Email has multiple addresses in its 'From' header " .
+		   "and no 'Sender' header, it won't be signed. See RFC 5322 Section 3.6.2");
+	}
+    } elsif ($from_count == 1) {
+	return $entity->head->get('from', 0);
+    }
+}
+
+
 sub sign_entity {
-    my ($entity, $selector, $sender, $sign_all) = @_;
+    my ($entity, $dkim, $sender) = @_;
+
+    my $sign_all = $dkim->{sign_all};
+    my $selector = $dkim->{selector};
+
+    if ($dkim->{sign_rfc5322}) {
+	$sender = parse_signing_sender($entity);
+    }
 
     die "no selector provided\n" if ! $selector;
 
diff --git a/src/PMG/RuleDB/Accept.pm b/src/PMG/RuleDB/Accept.pm
index 4ebd6da..2e1bfa7 100644
--- a/src/PMG/RuleDB/Accept.pm
+++ b/src/PMG/RuleDB/Accept.pm
@@ -103,7 +103,7 @@ sub execute {
 	if ($dkim->{sign}) {
 	    eval {
 		$entity = PMG::DKIMSign::sign_entity($entity,
-		    $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
+		    $dkim, $msginfo->{sender});
 	    };
 	    syslog('warning',
 		"Could not create DKIM-Signature - disabling Signing: $@") if $@;
diff --git a/src/PMG/RuleDB/BCC.pm b/src/PMG/RuleDB/BCC.pm
index 0f016f8..6db48af 100644
--- a/src/PMG/RuleDB/BCC.pm
+++ b/src/PMG/RuleDB/BCC.pm
@@ -143,7 +143,7 @@ sub execute {
 	if ($dkim->{sign}) {
 	    eval {
 		$entity = PMG::DKIMSign::sign_entity($entity,
-		    $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
+		    $dkim, $msginfo->{sender});
 	    };
 	    syslog('warning',
 		"Could not create DKIM-Signature - disabling Signing: $@") if $@;
diff --git a/src/bin/pmg-smtp-filter b/src/bin/pmg-smtp-filter
index 7da3de8..549dbd4 100755
--- a/src/bin/pmg-smtp-filter
+++ b/src/bin/pmg-smtp-filter
@@ -627,6 +627,7 @@ sub handle_smtp {
 	my $dkim_sign = $msginfo->{trusted} && $pmg_cfg->get('admin', 'dkim_sign');
 	if ($dkim_sign) {
 	    $msginfo->{dkim}->{sign} = $dkim_sign;
+	    $msginfo->{dkim}->{sign_rfc5322} = $pmg_cfg->get('admin', 'dkim_sign_rfc5322');
 	    $msginfo->{dkim}->{sign_all} = $pmg_cfg->get('admin', 'dkim_sign_all_mail');
 	    $msginfo->{dkim}->{selector} = $pmg_cfg->get('admin', 'dkim_selector');
 	}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [pmg-devel] [RFC PATCH] fix-2971: DKIM: Add setting to use From header when signing
  2024-01-03 14:53 [pmg-devel] [RFC PATCH] fix-2971: DKIM: Add setting to use From header when signing Maximiliano Sandoval
@ 2024-01-10 21:39 ` Stoiko Ivanov
  0 siblings, 0 replies; 2+ messages in thread
From: Stoiko Ivanov @ 2024-01-10 21:39 UTC (permalink / raw)
  To: Maximiliano Sandoval; +Cc: pmg-devel

Thanks for tackling this!

Finally got around to look a bit into the RFCs...
(and currently don't think the premises of DMARC work too well with many
e-mail setups (not only lists))

the bugreport has gained quite a few comments and further suggestions that
we should consider:
* fixed signing domain for all mails (something the DKIM RFC orginally
  envisioned)
* signing bounces to not have them generate DMARC reports (maybe even a
  general fallback from envelope-from (RFC5321.From) to header-from
  (RFC5322.From) if the former would not be signed, but the latter would?
* out-of-office mails

at least the first two might make sense to consider (out-of-office mails
usually are sent as bounce (empty 5321.from))

maybe the setting could be "dkim_prefer_rfc5322_domain" (better names more
than welcome), and the choice on, which domain to actually use for the
'd=' tag in the signature could be done in PMG::DKIMSign::signing_domain.

to sign bounces originating from PMG (e.g. the daily spam and admin
reports) - you'd need to add the DKIM-signing to
PMG::Utils::reinject_local_mail (and to test what happens with mails sent
via '/usr/sbin/sendmail' on the PMG (e.g. smartd-messages and
cronjob-outputs).

The changes do neither to happen with this patchset nor do you need to do
them, but maybe some of it offers itself for implementation, but please
mention 'partially fixes #2971' somewhere.

Some documentation for this would be needed as well.

further comments inline:

On Wed,  3 Jan 2024 15:53:29 +0100
Maximiliano Sandoval <m.sandoval@proxmox.com> wrote:

> We add an option to use the address from the `From:` header instead of
> the Envelope From address. Following RFC 5322 [2], we use the `Sender:`
> header when there are multiple addresses in the `From:` header.
sorry, I know I pointed you in that direction - but the DMARC rfc (where
this enhancement request initially originated) says - most of those cases
don't happen in DMARC protected domains (reality might have a different
view on that):
https://datatracker.ietf.org/doc/html/rfc7489#section-6.6.1
so we can probably spare ourselves the going through multiple headers,
and can take the part after the first '@' until either EOL or '>'
as domain (but please verify this with the acceptable syntax in rfc 5322)

> 
> From RFC 6376 [1]:
wrong rfc - this is from the dmarc-one:
https://datatracker.ietf.org/doc/html/rfc7489#section-3.1.1
(DKIM itself doesn't require the signing domain to occur anywhere in the
SMTP-dialogue, or the mail-headers or body: 
https://datatracker.ietf.org/doc/html/rfc6376#section-3.11 (INFORMATIVE DISCUSSION))

> 
>    To illustrate, in relaxed mode, if a validated DKIM signature
>    successfully verifies with a "d=" domain of "example.com", and the
>    RFC5322.From address is "alerts@news.example.com", the DKIM "d="
>    domain and the RFC5322.From domain are considered to be "in
>    alignment".  In strict mode, this test would fail, since the "d="
>    domain does not exactly match the FQDN of the address.
> 
> Current Problems:
> 
>  - `dkim_sign_rfc5322` is probably not a descriptive name for the setting
>  - The setting description might be improved upon
> 
> Tested with the following command:
> 
>     swaks --from foo@bar1 --to EMAIL -s PMG_ADDR:26 --data "Date: %DATE%\nTo: %TO_ADDRESS%\nFrom: foo@bar2\nSubject: test %DATE%\nMessage-Id: <%MESSAGEID%>\nX-Mailer: swaks v%SWAKS_VERSION% jetmore.org/john/code/swaks/\n%NEW_HEADERS%\n%BODY%\n"
> 
> [1] https://datatracker.ietf.org/doc/html/rfc6376
> [2] https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2
> 
> Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
> ---
>  src/PMG/Config.pm        |  7 +++++++
>  src/PMG/DKIMSign.pm      | 43 +++++++++++++++++++++++++++++++++++++++-
>  src/PMG/RuleDB/Accept.pm |  2 +-
>  src/PMG/RuleDB/BCC.pm    |  2 +-
>  src/bin/pmg-smtp-filter  |  1 +
>  5 files changed, 52 insertions(+), 3 deletions(-)
> 
> diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
> index 7339e0d..b6c61e1 100644
> --- a/src/PMG/Config.pm
> +++ b/src/PMG/Config.pm
> @@ -134,6 +134,11 @@ EODESC
>  	    description => "Default DKIM selector",
>  	    type => 'string', format => 'dns-name', #see RFC6376 3.1
>  	},
> +	dkim_sign_rfc5322 => {
> +	    description => "Whether to use the address from the From header when DKIM signing outbound mails.",
> +	    type => 'boolean',
> +	    default => 0,
> +	},
>      };
>  }
>  
> @@ -152,6 +157,7 @@ sub options {
>  	dkim_sign => { optional => 1 },
>  	dkim_sign_all_mail => { optional => 1 },
>  	dkim_selector => { optional => 1 },
> +	dkim_sign_rfc5322 => { optional => 1 },
>      };
>  }
>  
> @@ -1788,6 +1794,7 @@ my $pmg_service_params = {
>  	dkim_selector => 1,
>  	dkim_sign => 1,
>  	dkim_sign_all_mail => 1,
> +	dkim_sign_rfc5322 => 1,
>      },
>  };
>  
> diff --git a/src/PMG/DKIMSign.pm b/src/PMG/DKIMSign.pm
> index 08197f8..009313d 100644
> --- a/src/PMG/DKIMSign.pm
> +++ b/src/PMG/DKIMSign.pm
> @@ -84,8 +84,49 @@ sub signing_domain {
>  }
>  
>  
> +sub parse_signing_sender {
> +    # If there is exactly one single address in the 'From' header we use that
> +    # address for signing, otherwise we use the 'Sender' header.
> +    my ($entity) = @_;
> +
> +    my $from_count = 0;
> +    my @from_headers = $entity->head->get('from');
> +    foreach my $from_header (@from_headers) {
> +	my @senders = split(',', $from_header);
this won't work in a robust manner - consider the following (valid) from
header:
```
From: "Ivanov, Stoiko" <s.ivanov@proxmox.com>
```

> +	$from_count += scalar(@senders);
> +    }
> +
> +    if ($from_count > 1) {
> +	my $send_count = 0;
> +	my @send_headers = $entity->head->get('sender');
> +	foreach my $send_header (@send_headers) {
> +	    my @senders = split(',', $send_header);
> +	    $send_count += scalar(@senders);
> +	}
> +	if ($send_count > 1) {
> +	    syslog('warning', "Email has more than one address in its 'Sender' " .
> +                   "it won't be signed. See RFC 5322 Section 3.6.2");
> +	} elsif ($send_count == 1) {
> +	    return $entity->head->get('sender', 0);
> +	} else {
> +	    syslog('warning', "Email has multiple addresses in its 'From' header " .
> +		   "and no 'Sender' header, it won't be signed. See RFC 5322 Section 3.6.2");
> +	}
> +    } elsif ($from_count == 1) {
> +	return $entity->head->get('from', 0);
> +    }
> +}
> +
> +
>  sub sign_entity {
> -    my ($entity, $selector, $sender, $sign_all) = @_;
> +    my ($entity, $dkim, $sender) = @_;
> +
> +    my $sign_all = $dkim->{sign_all};
> +    my $selector = $dkim->{selector};
> +
> +    if ($dkim->{sign_rfc5322}) {
> +	$sender = parse_signing_sender($entity);
> +    }
>  
>      die "no selector provided\n" if ! $selector;
>  
> diff --git a/src/PMG/RuleDB/Accept.pm b/src/PMG/RuleDB/Accept.pm
> index 4ebd6da..2e1bfa7 100644
> --- a/src/PMG/RuleDB/Accept.pm
> +++ b/src/PMG/RuleDB/Accept.pm
> @@ -103,7 +103,7 @@ sub execute {
>  	if ($dkim->{sign}) {
>  	    eval {
>  		$entity = PMG::DKIMSign::sign_entity($entity,
> -		    $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
> +		    $dkim, $msginfo->{sender});
>  	    };
>  	    syslog('warning',
>  		"Could not create DKIM-Signature - disabling Signing: $@") if $@;
> diff --git a/src/PMG/RuleDB/BCC.pm b/src/PMG/RuleDB/BCC.pm
> index 0f016f8..6db48af 100644
> --- a/src/PMG/RuleDB/BCC.pm
> +++ b/src/PMG/RuleDB/BCC.pm
> @@ -143,7 +143,7 @@ sub execute {
>  	if ($dkim->{sign}) {
>  	    eval {
>  		$entity = PMG::DKIMSign::sign_entity($entity,
> -		    $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
> +		    $dkim, $msginfo->{sender});
>  	    };
>  	    syslog('warning',
>  		"Could not create DKIM-Signature - disabling Signing: $@") if $@;
> diff --git a/src/bin/pmg-smtp-filter b/src/bin/pmg-smtp-filter
> index 7da3de8..549dbd4 100755
> --- a/src/bin/pmg-smtp-filter
> +++ b/src/bin/pmg-smtp-filter
> @@ -627,6 +627,7 @@ sub handle_smtp {
>  	my $dkim_sign = $msginfo->{trusted} && $pmg_cfg->get('admin', 'dkim_sign');
>  	if ($dkim_sign) {
>  	    $msginfo->{dkim}->{sign} = $dkim_sign;
> +	    $msginfo->{dkim}->{sign_rfc5322} = $pmg_cfg->get('admin', 'dkim_sign_rfc5322');
>  	    $msginfo->{dkim}->{sign_all} = $pmg_cfg->get('admin', 'dkim_sign_all_mail');
>  	    $msginfo->{dkim}->{selector} = $pmg_cfg->get('admin', 'dkim_selector');
>  	}





^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2024-01-10 21:39 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-03 14:53 [pmg-devel] [RFC PATCH] fix-2971: DKIM: Add setting to use From header when signing Maximiliano Sandoval
2024-01-10 21:39 ` Stoiko Ivanov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal