Skip to Content.
Sympa Menu

devel - Re: [sympa-developpers] [sympa-commits] sympa[11356] branches/sympa-6.0-branch: [svn] Retrieving changes about DMARC protection from sympa-6.1-branch ( not yet tested).

Subject: Developers of Sympa

List archive

Chronological Thread  
  • From: David Verdin <address@concealed>
  • To: address@concealed
  • Subject: Re: [sympa-developpers] [sympa-commits] sympa[11356] branches/sympa-6.0-branch: [svn] Retrieving changes about DMARC protection from sympa-6.1-branch ( not yet tested).
  • Date: Tue, 09 Sep 2014 17:35:38 +0200

Great Soji.
I wanted to do so. You beat me here...

That will be appreciable for our users.

I'll test it as soon as possible.

Regards,

David

Le 09/09/14 17:32, address@concealed a écrit :
sympa[11356] branches/sympa-6.0-branch: [svn] Retrieving changes about DMARC protection from sympa-6.1-branch (not yet tested).
Revision
11356
Author
sikeda
Date
2014-09-09 17:32:42 +0200 (mar. 09 sept. 2014)

Log Message

[svn] Retrieving changes about DMARC protection from sympa-6.1-branch (not yet tested).

Modified Paths

  • branches/sympa-6.0-branch/src/lib/List.pm
  • branches/sympa-6.0-branch/src/lib/confdef.pm
  • branches/sympa-6.0-branch/src/lib/tools.pm
  • branches/sympa-6.0-branch/src/sympa_wizard.pl.in

Property Changed

Diff

Property changes: branches/sympa-6.0-branch


Modified: svn:mergeinfo

/branches/sympa-6.1-branch:7208 /branches/sympa-autotools-cleanup:5761-5815,5873-5980 /trunk:6003,6005,6015-6019,6022,6026,6050,6053,6161,6164,6192-6194,6269-6270,6326,6506-6507,6516,6518,6528-6529,6532,6537,6540,6599,6605,6611,6623,6627,6637,6676 + /branches/branche-julien:6001-6002 /branches/sympa-6.1-branch:7208,10541-10550 /branches/sympa-autotools-cleanup:5761-5815,5873-5980 /trunk:6003,6005,6015-6019,6022,6026,6050,6053,6161,6164,6192-6194,6269-6270,6326,6506-6507,6516,6518,6528-6529,6532,6537,6540,6599,6605,6611,6623,6627,6637,6676

Modified: branches/sympa-6.0-branch/src/lib/List.pm (11355 => 11356)


--- branches/sympa-6.0-branch/src/lib/List.pm	2014-09-09 13:05:58 UTC (rev 11355)
+++ branches/sympa-6.0-branch/src/lib/List.pm	2014-09-09 15:32:42 UTC (rev 11356)
@@ -590,7 +590,108 @@
 		      'gettext_id' => "Inclusions timeout for message distribution",
 		      'group' => 'data_source'
 		      },
-
+	    'dkim_feature' => {'format' => ['on','off'],
+			      'occurence' => '0-1',
+			      'default' => {'conf' => 'dkim_feature'},
+			      'gettext_id' => "Insert DKIM signature to messages sent to the list",
+			      'comment' =>  "Enable/Disable DKIM. This feature require Mail::DKIM to installed and may be some custom scenario to be updated",
+			      'group' => 'dkim',
+			  },
+	    'dkim_signature_apply_on'=> {'format' => ['md5_authenticated_messages','smime_authenticated_messages','dkim_authenticated_messages','editor_validated_messages','none','any'],
+					 'occurrence' => '0-n',
+					 'split_char' => ',',
+					 'default' => {'conf' => 'dkim_signature_apply_on'},
+					 'gettext_id' => "The categories of messages sent to the list that will be signed using DKIM.",
+					 'comment' => "This parameter controls in which case messages must be signed using DKIM, you may sign every message choosing 'any' or a subset. The parameter value is a comma separated list of keywords",
+					 'group' => 'dkim',
+					 },
+	    'dkim_parameters'=> {'format' => {'private_key_path'=> {'format' => '\S+',
+		                         			  'occurence' => '0-1',
+			                                          'default' => {'conf' => 'dkim_private_key_path'},
+			                                          'gettext_id' => "File path for list DKIM private key",
+								  'comment' => "The file must contain a RSA pem encoded private key", 
+								  'order' => 1
+					                         },
+					     'selector' => { 'format' => '\S+',
+		                         			  'occurence' => '0-1',
+			                                          'default' => {'conf' => 'dkim_selector'},
+							          'comment' => "The selector is used in order to build the DNS query for public key. It is up to you to choose the value you want but verify that you can query the public DKIM key for <selector>._domainkey.your_domain",
+			                                          'gettext_id' => "Selector for DNS lookup of DKIM public key",
+								  'order' => 2
+                                                                  },
+							          
+					     'header_list'=>      { 'format' => '\S+',
+		                         			  'occurence' => '0-1',
+			                                          'default' => {'conf' => 'dkim_header_list'},
+			                                          'gettext_id' => 'List of headers to be included ito the message for signature',
+								  'comment' => 'You should probably use teh default value which is the value recommended by RFC4871',
+								  'order' => 4,
+								  'obsolete' => 1,
+                                                                  },
+					     'signer_domain' =>   {'format' => '\S+',
+		                         			  'occurence' => '0-1',
+			                                          'default' => {'conf' => 'dkim_signer_domain'},
+			                                          'gettext_id' => 'DKIM "d=" tag, you should probably use the default value',
+								   'omment' => ' The DKIM "d=" tag, is the domain of the signing entity. the list domain MUST must be included in the "d=" domain',
+								  'order' => 5
+								 },
+                                             'signer_identity'=>  {'format' => '\S+',
+		                         			  'occurence' => '0-1',
+								  'comment' => 'DKIM "i=" tag, you should probably not use this parameter, as recommended by RFC 4871, default for list brodcasted messages is i=<listname>-request@<domain>',
+			                                          'gettext_id' => 'DKIM "i=" tag, you should probably leave this parameter empty',
+								  'order' => 6
+								 },
+					     },
+			      'group' => 'dkim',
+			      'comment' => 'A set of parameters in order to define outgoing DKIM signature', 
+			      'occurrence' => '0-1',
+			      'gettext_id' => "DKIM configuration",
+			  },
+            'dmarc_protection' => { 
+			  'format' => {
+                                   'mode' => { 'format' => [ 'none', 'all', 'dkim_signature', 'dmarc_reject', 'dmarc_any', 'dmarc_quarantine', 'domain_regex' ],
+				     'synonym' => {
+					'dkim' => 'dkim_signature',
+					'dkim_exists' => 'dkim_signature',
+					'dmarc_exists' => 'dmarc_any',
+					'domain' => 'domain_regex',
+					'domain_match' => 'domain_regex',
+				     },
+                                     'gettext_id' => "Protection modes",
+				     'split_char' => ',',
+		                     'occurrence' => '0-n',
+                                     'default' => { 'conf'=>'dmarc_protection_mode' },
+                                     'comment' => 'Select one or more operation modes.  "Domain matching regular _expression_" (domain_regex) matches the specified Domain regexp; "DKIM signature exists" (dkim_signature) matches any message with a DKIM signature header; "DMARC policy ..." (dmarc_*) matches messages from sender domains with a DMARC policy as given; "all" (all) matches all messages.',
+                                     'order' => 1
+                                   },
+                                   'domain_regex' => { 'format' => '.+',
+                                     'gettext_id' => "Match domain regexp",
+		                     'occurrence' => '0-1',
+				     'comment' => 'Regexp match pattern for From domain',
+                                     'order' => 2,
+                                     'default' => { 'conf'=>'dmarc_protection_domain_regex' },
+                                   },
+				   'other_email' => { 'format' => '.+',
+                                     'gettext_id' => "New From address",
+		                     'occurrence' => '0-1',
+                                     'comment' => 'This is the email address to use when modifying the From header.  It defaults to the list address.  This is similar to Anonymisation but preserves the original sender details in the From address phrase.',
+                                     'order' => 3,
+                                     'default' => { 'conf'=>'dmarc_protection_other_email' },
+                                   },
+				   'phrase' => { 'format' => [ 'display_name', 'name_and_email', 'name_via_list', 'name_email_via_list' ],
+				     'synonym' => {'name' => 'display_name'},
+                                     'default' => { 'conf'=>'dmarc_protection_phrase' },
+                                     'gettext_id' => "New From name format",
+		                     'occurrence' => '0-1',
+                                     'comment' => 'This is the format to be used for the sender name part of the new From header.',
+                                     'order' => 4,
+                                   },
+                          },
+                          'gettext_id' => "DMARC Protection",
+                          'group' => 'dkim',
+			  'comment' => 'Parameters to define how to manage From address processing to avoid some domains\' excessive DMARC protection', 
+			  'occurrence' => '0-1',
+                      },
 	    'editor' => {'format' => {'email' => {'format' => &tools::get_regexp('email'),
 						  'length' => 30,
 						  'occurrence' => '1',
@@ -2487,6 +2588,127 @@
 	}
     }
 
+    ## Munge the From header if we are using DMARC Protection mode
+    if ( $self->{'admin'}{'dmarc_protection'}{'mode'} ) {
+        my $dkimdomain = $self->{'admin'}{'dmarc_protection'}{'domain_regex'};
+        my $originalFromHeader = $hdr->get('From');
+        my $anonaddr;
+        my @addresses = Mail::Address->parse($originalFromHeader);
+        my @anonFrom;
+        my $dkimSignature = $hdr->get('DKIM-Signature');
+        my $origFrom = '';
+        my $mungeFrom = 0;
+
+        if ( @addresses ) { $origFrom = $addresses[0]->address; }
+
+        # Will this message be processed?
+        $mungeFrom = 1 if( &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'all') );
+        if( !$mungeFrom and $dkimSignature 
+            and &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dkim_signature')) {
+            $mungeFrom = 1;
+        }
+        if( !$mungeFrom and $origFrom and $dkimdomain 
+            and &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'domain_regex') ) {
+            $mungeFrom = 1 if( $origFrom =~ /$dkimdomain$/ );
+        }
+        if( !$mungeFrom and $origFrom 
+            and (
+                &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_reject')
+                or &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_any')
+                or &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_quarantine')
+            )) {
+            # Strict auto policy - is the sender domain policy to reject
+            my $dom = $origFrom; $dom =~ s/^.*\@//;
+            eval { # In case Net::DNS is not installed
+                require Net::DNS;
+                my $res = Net::DNS::Resolver->new;
+                my $packet = $res->query("_dmarc.$dom","TXT");
+                if ($packet) {
+                    foreach my $rr (grep { $_->type eq 'TXT' } $packet->answer) {
+                        next if($rr->string !~ /v=DMARC/);
+                        if(!$mungeFrom and &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_reject')) {
+                            $mungeFrom = 1 if($rr->string =~ /p=reject/);
+                        }
+                        if(!$mungeFrom and &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_quarantine')) {
+                            $mungeFrom = 1 if($rr->string =~ /p=quarantine/);
+                        }
+                        if(!$mungeFrom and &tools::is_in_array($self->{'admin'}{'dmarc_protection'}{'mode'},'dmarc_any')) {
+                            $mungeFrom = 1;
+                        }
+                        $hdr->add('X-Original-DMARC-Record',"domain=$dom; ".$rr->string);
+                        last;
+                    }
+                }
+            };
+            $hdr->add('X-DMARC-Error',$@) if($@);
+        }
+
+        if( $mungeFrom ) {
+            # Remove any DKIM signatures we find
+            if( $dkimSignature ) {
+                $hdr->add('X-Original-DKIM-Signature',$dkimSignature);
+                $hdr->delete('DKIM-Signature');
+                $hdr->delete('DomainKey-Signature');
+            }
+
+            # Identify default new From address
+            my $phraseMode = $self->{'admin'}{'dmarc_protection'}{'phrase'} ||
+                'name_via_list';
+            my $newAddr;
+            my $displayName;
+            my $newComment;
+            $anonaddr = $self->{'admin'}{'dmarc_protection'}{'other_email'};
+            $anonaddr = $self->get_list_address()
+                unless $anonaddr and $anonaddr =~ /\@/;
+            @anonFrom = Mail::Address->parse($anonaddr);
+
+            if (@addresses) {
+                # We should always have a From address in reality, unless the
+                # message is from a badly-behaved automate
+                if ($addresses[0]->phrase) {
+                    $displayName = MIME::EncWords::decode_mimewords(
+			$addresses[0]->phrase, Charset => 'UTF-8');
+                    $newComment = $addresses[0]->address
+			if $phraseMode =~ /email/;
+                } else {
+                    # If we dont have a Phrase, should we search the Sympa database
+                    # for the sender to obtain their name that way? Might be difficult.
+                    $displayName = $addresses[0]->address;
+                    $displayName =~ s/\@.*// unless $phraseMode =~ /email/;
+                }
+                if($phraseMode =~ /list/) {
+                    if ($newComment and $newComment =~ /\S/) {
+                        $newComment =
+                            sprintf gettext('%s via %s Mailing List'),
+                                $newComment, $name;
+                    } else {
+                        $newComment =
+			    sprintf gettext('via %s Mailing List'), $name;
+                    }
+                }
+                $hdr->add('Reply-To',$addresses[0]->address) unless($hdr->get('Reply-To'));
+            }
+            # If the new From email address has a Phrase component, then append it
+            if (@anonFrom and $anonFrom[0]->phrase) {
+                if ($displayName and $displayName =~ /\S/) {
+                    $displayName .= ' ' . $anonFrom[0]->phrase;
+                } else {
+                    $displayName = $anonFrom[0]->phrase;
+                }
+            }
+            $displayName = gettext('Anonymous')
+                unless $displayName and $displayName =~ /\S/;
+
+            $newAddr = tools::addrencode(
+		(@anonFrom ? $anonFrom[0]->address : $anonaddr),
+		$displayName, Language::GetCharset(), $newComment);
+
+            $hdr->add('X-Original-From',"$originalFromHeader");
+            $hdr->replace('From', $newAddr);
+        }
+
+    }
+
     ## Hide the sender if the list is anonymoused
     if ( $self->{'admin'}{'anonymous_sender'} ) {
 

Modified: branches/sympa-6.0-branch/src/lib/confdef.pm (11355 => 11356)


--- branches/sympa-6.0-branch/src/lib/confdef.pm	2014-09-09 13:05:58 UTC (rev 11355)
+++ branches/sympa-6.0-branch/src/lib/confdef.pm	2014-09-09 15:32:42 UTC (rev 11356)
@@ -1199,6 +1199,38 @@
 	vhost   => '1',
     },
     {
+        'name'    => 'dmarc_protection_mode',
+        'query'   => 'Test mode(s) for DMARC Protection',
+        'sample'  => 'dmarc_reject,dkim_signature',
+        'vhost'   => '1',
+        'edit'    => '1',
+        'optional'=> '1',
+        'advice'  => 'Do not set unless you want to use DMARC protection.  This is a comma separated list of test modes; if multiple are selected then protection is activated if ANY match.  Do not use dmarc_* modes unless you have a local DNS cache as they do a DNS lookup for each received message.',
+    },
+    {
+        'name'    => 'dmarc_protection_domain_regex',
+        'query'   => 'Regexp for domain name match',
+        'vhost'   => '1',
+        'edit'    => '1',
+        'optional'=> '1',
+        'advice'  => 'This is used for the "domain_regex" protection mode.',
+    },
+    {
+        'name'    => 'dmarc_protection_phrase',
+        'query'   => 'Pattern used to create new From header phrase',
+        'vhost'   => '1',
+        'edit'    => '1',
+        'optional'=> '1',
+        'default' => 'name_via_list',
+    },
+    {
+        'name'    => 'dmarc_protection_other_email',
+        'query'   => 'Email to use for replacement From header',
+        'vhost'   => '1',
+        'edit'    => '1',
+        'optional'=> '1',
+    },
+    {
         name    => 'css_path',
         default => '',
 	vhost   => '1',

Modified: branches/sympa-6.0-branch/src/lib/tools.pm (11355 => 11356)


--- branches/sympa-6.0-branch/src/lib/tools.pm	2014-09-09 13:05:58 UTC (rev 11355)
+++ branches/sympa-6.0-branch/src/lib/tools.pm	2014-09-09 15:32:42 UTC (rev 11356)
@@ -3459,35 +3459,42 @@
 #*******************************************
 # Function : addrencode
 # Description : return formatted (and encoded) name-addr as RFC5322 3.4.
-## IN : addr, [phrase, [charset]]
+## IN : addr, [phrase, [charset, [comment]]]
 #*******************************************
 sub addrencode {
     my $addr = shift;
     my $phrase = (shift || '');
     my $charset = (shift || 'utf8');
+    my $comment = (shift || '');
 
     return undef unless $addr =~ /\S/;
 
     if ($phrase =~ /[^\s\x21-\x7E]/) {
-	# Minimal encoding leaves special characters unencoded.
-	# In this case do maximal encoding for workaround.
-	my $minimal =
-	    ($phrase =~ /(\A|\s)[\x21-\x7E]*[\"(),:;<>\@\\][\x21-\x7E]*(\s|\z)/)?
-	    'NO': 'YES';
 	$phrase = MIME::EncWords::encode_mimewords(
 	    Encode::decode('utf8', $phrase),
 	    'Encoding' => 'A', 'Charset' => $charset,
 	    'Replacement' => 'FALLBACK',
 	    'Field' => 'Resent-Sender', # almost longest
-	    'Minimal' => $minimal
+	    'Minimal' => 'DISPNAME',	# needs MIME::EncWords >= 1.012.
             );
-	return "$phrase <$addr>";
     } elsif ($phrase =~ /\S/) {
 	$phrase =~ s/([\\\"])/\\$1/g;
-	return "\"$phrase\" <$addr>";
-    } else {
-	return "<$addr>";
+	$phrase = '"' . $phrase . '"';
     }
+    if ($comment =~ /[^\s\x21-\x27\x2A-\x5B\x5D-\x7E]/) {
+	$comment = MIME::EncWords::encode_mimewords(
+	    Encode::decode('utf8', $comment),
+	    'Encoding' => 'A', 'Charset' => $charset,
+	    'Replacement' => 'FALLBACK',
+	    'Minimal' => 'DISPNAME',
+	    );
+    } elsif ($comment =~ /\S/) {
+	$comment =~ s/([\\\"])/\\$1/g;
+    }
+
+    return ($phrase =~ /\S/ ? "$phrase " : '')
+	. ($comment =~ /\S/ ? "($comment) " : '')
+	. "<$addr>";
 }
 
 1;

Modified: branches/sympa-6.0-branch/src/sympa_wizard.pl.in (11355 => 11356)


--- branches/sympa-6.0-branch/src/sympa_wizard.pl.in	2014-09-09 13:05:58 UTC (rev 11355)
+++ branches/sympa-6.0-branch/src/sympa_wizard.pl.in	2014-09-09 15:32:42 UTC (rev 11356)
@@ -426,13 +426,13 @@
 					   usage => 'required to compute digest for password and emails',
 					  },
 			'MIME::Charset' => {
-					    required_version =>'0.04.1',
+					    required_version =>'1.010',
 					    package_name => 'MIME-Charset',
 					    mandatory => 1,
 					    usage => 'used to encode mail body using a different charset',
 					   },
 			'MIME::EncWords' => {
-					     required_version =>'0.040',
+					     required_version =>'1.014',
 					     package_name => 'MIME-EncWords',
 					     mandatory => 1,
 					     usage => 'required to decode/encode SMTP header fields without breaking character encoding', 

--
A bug in Sympa? Quick! To the bug tracker!

 
David Verdin
Études et projets applicatifs
 
Tél : +33 2 23 23 69 71
Fax : +33 2 23 23 71 21
 
www.renater.fr
RENATER
263 Avenue du Gal Leclerc
35042 Rennes Cedex



PNG image

Attachment: smime.p7s
Description: Signature cryptographique S/MIME




Archive powered by MHonArc 2.6.19+.

Top of Page