From: Stoiko Ivanov <s.ivanov@proxmox.com>
To: Maximiliano Sandoval <m.sandoval@proxmox.com>
Cc: pmg-devel@lists.proxmox.com
Subject: Re: [pmg-devel] [PATCH pmg-api v5] fix-2971: DKIM: Add setting to use From header when signing
Date: Fri, 23 Feb 2024 12:31:46 +0100 [thread overview]
Message-ID: <20240223123146.73e9d45a@rosa.proxmox.com> (raw)
In-Reply-To: <20240216124135.237846-1-m.sandoval@proxmox.com>
Thanks for the update - the enum indeed feels nicer!
as said off-list - please also add this to the GUI
a few comments inline:
On Fri, 16 Feb 2024 13:41:35 +0100
Maximiliano Sandoval <m.sandoval@proxmox.com> wrote:
> Following RFC 5322 [2], we add an option to use the address from the
> `From:` header instead of the Envelope From address.
>
> From RFC 7489 [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.
>
> Tested with the following command:
>
> swaks --from foo@envelope.domain --to EMAIL -s PMG_ADDR:26 --data "Date: %DATE%\nTo: %TO_ADDRESS%\nFrom: bar@header.domain\nSubject: test %DATE%\nMessage-Id: <%MESSAGEID%>\nX-Mailer: swaks v%SWAKS_VERSION% jetmore.org/john/code/swaks/\n%NEW_HEADERS%\n%BODY%\n"
>
> Where EMAIL and PMG_ADDR are given adequate values, and envelope.domain
> and header.domain are in the 'Sign Domains' list.
>
> [1] https://datatracker.ietf.org/doc/html/rfc7489#section-3.1.1
> [2] https://datatracker.ietf.org/doc/html/rfc5322#section-3.6.2
>
> Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
> ---
> Do note that for testing one has to restart pmg-smtp-filter after changing the setting value.
>
> Differences from v4:
> - Rename setting to dkim_use_domain and make it an enum
> - Improved commit message
> - Do not use 'Sender' field as a fallback
>
> Differences from v3:
> - Take into account setting
> - rename parsing function to parse_headers_for_signing
>
> src/PMG/Config.pm | 8 +++++++
> src/PMG/DKIMSign.pm | 48 ++++++++++++++++++++++++++++++++++------
> src/PMG/RuleDB/Accept.pm | 3 +--
> src/PMG/RuleDB/BCC.pm | 3 +--
> src/bin/pmg-smtp-filter | 1 +
> 5 files changed, 52 insertions(+), 11 deletions(-)
>
> diff --git a/src/PMG/Config.pm b/src/PMG/Config.pm
> index 7339e0d..e9e740d 100644
> --- a/src/PMG/Config.pm
> +++ b/src/PMG/Config.pm
> @@ -134,6 +134,12 @@ EODESC
> description => "Default DKIM selector",
> type => 'string', format => 'dns-name', #see RFC6376 3.1
> },
> + dkim_use_domain => {
please use 'kebab-case' for new configs instead of 'snake_case'
> + description => "Whether to sign using the address from the header or the envelope.",
> + type => 'string',
> + enum => [qw(header envelope)],
> + default => 'envelope',
> + },
> };
> }
>
> @@ -152,6 +158,7 @@ sub options {
> dkim_sign => { optional => 1 },
> dkim_sign_all_mail => { optional => 1 },
> dkim_selector => { optional => 1 },
> + dkim_use_domain => { optional => 1 },
> };
> }
>
> @@ -1788,6 +1795,7 @@ my $pmg_service_params = {
> dkim_selector => 1,
> dkim_sign => 1,
> dkim_sign_all_mail => 1,
> + dkim_use_domain => 1,
> },
> };
>
> diff --git a/src/PMG/DKIMSign.pm b/src/PMG/DKIMSign.pm
> index 08197f8..5df9aa8 100644
> --- a/src/PMG/DKIMSign.pm
> +++ b/src/PMG/DKIMSign.pm
> @@ -2,6 +2,7 @@ package PMG::DKIMSign;
>
> use strict;
> use warnings;
> +use Email::Address::XS;
> use Mail::DKIM::Signer;
> use Mail::DKIM::TextWrap;
> use Crypt::OpenSSL::RSA;
> @@ -53,11 +54,16 @@ sub create_signature {
>
> #determines which domain should be used for signing based on the e-mailaddress
> sub signing_domain {
> - my ($self, $sender_email) = @_;
> -
> - my @parts = split('@', $sender_email);
> - die "no domain in sender e-mail\n" if scalar(@parts) < 2;
> - my $input_domain = $parts[-1];
> + my ($self, $sender_email, $entity, $use_domain) = @_;
> +
> + my $input_domain;
> + if ($use_domain eq 'header') {
> + $input_domain = parse_headers_for_signing($entity);
> + } else {
> + my @parts = split('@', $sender_email);
> + die "no domain in sender e-mail\n" if scalar(@parts) < 2;
> + $input_domain = $parts[-1];
> + }
>
> if ($self->{sign_all}) {
> $self->domain($input_domain) if $self->{sign_all};
> @@ -84,8 +90,36 @@ sub signing_domain {
> }
>
>
> +sub parse_headers_for_signing {
> + # Following RFC 7489 [1], we only sign emails with exactly one sender in the
> + # From header.
> + #
> + # [1] https://datatracker.ietf.org/doc/html/rfc7489#section-6.6.1
> + my ($entity) = @_;
> +
> + my @from_list;
> +
> + my @from_headers = $entity->head->get('from');
> + foreach my $from_header (@from_headers) {
> + my @senders = Email::Address::XS->parse($from_header);
> + foreach my $sender (@senders) {
> + push(@from_list, @senders);
why do you push the list of senders X times to the from_list (X being the
number in that particular from-header line)?
I think the for-loop is simply not needed
In general - if you want to see how some data-structure looks like during
testing - I can recommend adding Data::Dumper statements - with our
services in PMG you can usually log them with syslog e.g.:
```
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
syslog('err', "DEBUG: ". Dumper($data_for_debug));
```
it's very crude (and the syslog is not the right place for this) but gives
a good overview
> + }
> + }
> + my $from_count = scalar(@from_list);
if you only use the list for counting - why not use an integer and
increment it or return early above if you have more than one element?
> +
> + if ($from_count == 1) {
> + return $from_list[0]->host();
I'd consider a `die` here in case the count !=1 - that way it should be
logged by the actions (but please verify the call-sites) - we do log this
for the current code in sign_entity as well
(was confused why I did not get a warning for a mail without any
from-header)
> + }
> +}
> +
> +
> sub sign_entity {
> - my ($entity, $selector, $sender, $sign_all) = @_;
> + my ($entity, $dkim, $sender) = @_;
> +
> + my $sign_all = $dkim->{sign_all};
> + my $use_domain = $dkim->{use_domain};
> + my $selector = $dkim->{selector};
>
> die "no selector provided\n" if ! $selector;
>
> @@ -110,7 +144,7 @@ sub sign_entity {
>
> $signer->extended_headers($extended_headers);
>
> - if ($signer->signing_domain($sender)) {
> + if ($signer->signing_domain($sender, $entity, $use_domain)) {
> $entity->print($signer);
> my $signature = $signer->create_signature();
> $entity->head->add('DKIM-Signature', $signature, 0);
> diff --git a/src/PMG/RuleDB/Accept.pm b/src/PMG/RuleDB/Accept.pm
> index 4ebd6da..d14c2fb 100644
> --- a/src/PMG/RuleDB/Accept.pm
> +++ b/src/PMG/RuleDB/Accept.pm
> @@ -102,8 +102,7 @@ sub execute {
>
> if ($dkim->{sign}) {
> eval {
> - $entity = PMG::DKIMSign::sign_entity($entity,
> - $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
> + $entity = PMG::DKIMSign::sign_entity($entity, $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..65b6fb5 100644
> --- a/src/PMG/RuleDB/BCC.pm
> +++ b/src/PMG/RuleDB/BCC.pm
> @@ -142,8 +142,7 @@ sub execute {
> my $dkim = $msginfo->{dkim} // {};
> if ($dkim->{sign}) {
> eval {
> - $entity = PMG::DKIMSign::sign_entity($entity,
> - $dkim->{selector}, $msginfo->{sender}, $dkim->{sign_all});
> + $entity = PMG::DKIMSign::sign_entity($entity, $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..aedae0c 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}->{use_domain} = $pmg_cfg->get('admin', 'dkim_use_domain');
> $msginfo->{dkim}->{sign_all} = $pmg_cfg->get('admin', 'dkim_sign_all_mail');
> $msginfo->{dkim}->{selector} = $pmg_cfg->get('admin', 'dkim_selector');
> }
prev parent reply other threads:[~2024-02-23 11:31 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-02-16 12:41 Maximiliano Sandoval
2024-02-23 11:31 ` Stoiko Ivanov [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240223123146.73e9d45a@rosa.proxmox.com \
--to=s.ivanov@proxmox.com \
--cc=m.sandoval@proxmox.com \
--cc=pmg-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.