Issue
*Wiki notifications are rich HTML - filled with links. In today's SPAM-filled, hostile world, they're subject to being mis-identified.
Further, email in this format could be used for various phishing attacks.
Solution
Allow *Wiki to provide s/mime signed email. This makes it easy to ensure the integrity of
WebNotify messages, and to SPAM-classify them appropriately.
It turns out that the code to send s/mime signed email isn't as scary as one might think.
In fact, here's a complete patch. The only restriction is that I didn't take the time to figure out how to persuade Net::SMTP to accept the additional headers - sendmail is good enough for me.
cd www/twiki/lib
lib$ for f in TWiki.spec TWiki/Configure/Checkers/SmimeCertificateFile.pm TWiki/Configure/Checkers/MailProgram.pm TWiki/Configure/Checkers/SmimeKeyFile.pm TWiki/Net.pm ; do if [ -r $f~ ]; then diff -U6 $f~ $f ; else diff -U6 /dev/null $f ; fi ; done
--- TWiki.spec~ 2008-09-11 23:41:58.000000000 -0400
+++ TWiki.spec 2008-12-15 10:26:07.000000000 -0500
@@ -996,12 +996,30 @@
# **STRING 30**
# TWiki administrator's name address, for use in mails (first name and
# last name, e.g. =Fred Smith=) (used in %WIKIWEBMASTERNAME%)
$TWiki::cfg{WebMasterName} = 'TWiki Administrator';
+# **PATH**
+# Secure email certificate. If you want e-mail sent by TWiki to be signed,
+# specify the filename of the administrator's X.509 certificate here. It
+# must be in PEM format. You must also use a mail program (not Net::SMTP)
+# in the following settings. <em>If you do not want signed e-mail,
+# leave this field blank. </em>
+$TWiki::cfg{SmimeCertificateFile} = '$TWiki::cfg{DataDir}/cert.pem';
+
+# **PATH**
+# Secure email certificate. If you want e-mail sent by TWiki to be signed,
+# specify the filename of the administrator's X.509 private key here. It
+# must be in PEM format. <em>Be sure that this file is only readable by the
+# TWiki software; it must NOT be readable by users!</em>
+# You must also use a mail program (not Net::SMTP)
+# in the following settings. <em>If you do not want signed e-mail,
+# leave this field blank. </em>
+$TWiki::cfg{SmimeKeyFile} = '$TWiki::cfg{DataDir}/key.pem';
+
# **COMMAND**
# Mail program. If Net::SMTP is installed, it will be used in preference.
# To force TWiki to use the {MailProgram}, unset both {SMTP}{MAILHOST}
# below and all SMTPMAILHOST settings in your TWiki's Preferences topics.
# This needs to be a command-line program that accepts
# MIME format mail messages on standard input, and mails them.
@@ -1046,13 +1064,13 @@
# <b>If you change this setting you will have to
# use TWiki to manually rename the topic in all existing webs</b>
$TWiki::cfg{NotifyTopicName} = 'WebNotify';
# **BOOLEAN EXPERT**
# Set this option on to enable debug
-# mode in SMTP. Output will go to the webserver error log.
+# mode in SMTP. Output will go to the webserver error log
$TWiki::cfg{SMTP}{Debug} = 0;
# **STRING 30 EXPERT**
# Some environments require outbound HTTP traffic to go through a proxy
# server. (e.g. proxy.your.company).
# <b>CAUTION</b> This setting can be overridden by a PROXYHOST setting
--- /dev/null 2008-04-26 06:27:25.709194980 -0400
+++ TWiki/Configure/Checkers/SmimeCertificateFile.pm 2008-12-15 10:15:12.000000000 -0500
@@ -0,0 +1,37 @@
+#
+# TWiki Enterprise Collaboration Platform, http://TWiki.org/
+#
+# Copyright (C) 2000-2006 TWiki Contributors.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version. For
+# more details read LICENSE in the root of this distribution.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# As per the GPL, removal of this notice is prohibited.
+package TWiki::Configure::Checkers::SmimeCertificateFile;
+
+use strict;
+
+use base 'TWiki::Configure::Checker';
+
+use TWiki::Configure::Checker;
+use TWiki::Configure::Load;
+
+sub check {
+ my $this = shift;
+
+ my $certFile = $TWiki::cfg{SmimeCertificateFile} || "";
+ $certFile =~ s/%DATE%/DATE/;
+ TWiki::Configure::Load::expandValue($certFile);
+ my $e = !-r ( $certFile ) && "Can\'t read $certFile";
+ $e = $this->ERROR($e) if $e;
+ return $e;
+}
+
+1;
--- TWiki/Configure/Checkers/MailProgram.pm~ 2008-09-11 23:41:58.000000000 -0400
+++ TWiki/Configure/Checkers/MailProgram.pm 2008-12-15 11:01:38.000000000 -0500
@@ -22,27 +22,37 @@
use base 'TWiki::Configure::Checker';
sub check {
my $this = shift;
- return '' if( !$Twiki::cfg{EnableEmail} );
+ return '' if( !$TWiki::cfg{EnableEmail} );
eval "use Net::SMTP";
- my $n;
+ my $n = '';
my $useprog = 0;
+
+ my $signmail = 0;
+ if( $TWiki::cfg{SmimeCertificateFile} || $TWiki::cfg{SmimeKeyFile} ) {
+ $signmail = 1;
+ unless( $TWiki::cfg{SmimeCertificateFile} && $TWiki::cfg{SmimeKeyFile} ) {
+ $n = $this->WARN( "Signed e-mail requires both a certificate and a key file.");
+ }
+ }
if ($@) {
- $n = "Net::SMTP is <b>not</b> installed in this environment. ";
+ $n .= "Net::SMTP is <b>not</b> installed in this environment. ";
$useprog = 1;
} elsif( !$TWiki::cfg{SMTP}{MAILHOST} ) {
- $n = $this->WARN('Net::SMTP is installed in this environment, but {SMTP}{MAILHOST} is not defined, so the {MailProgram} <b>will</b> be used..');
+ $n .= $this->WARN('Net::SMTP is installed in this environment, but {SMTP}{MAILHOST} is not defined, so the {MailProgram} <b>will</b> be used..') unless( $signmail );
$useprog = 1;
} else {
- $n = $this->NOTE('<em>Net::SMTP is installed in this environment, so this setting will <b>not</b> be used.</em>');
+ $n .= $this->NOTE('<em>Net::SMTP is installed in this environment, so this setting will <b>not</b> be used.</em>');
+ $n .= $this->WARN('<em>Signed e-mail is not supported by Net::SMTP. If you want signed e-mail, please leave {SMTP}{MAILHOST} blank. If not, please leave {SmimeCertificateFile} and {SmimeKeyFile} blank.') if( $signmail );
$useprog = 0;
}
+
if ($useprog) {
my $val = $TWiki::cfg{MailProgram} || '';
$val =~ s/\s.*$//g;
if( ! ( -x $val ) ) {
$n .= $this->WARN("<tt>$val</tt> was not found. Check the path.");
}
--- /dev/null 2008-04-26 06:27:25.709194980 -0400
+++ TWiki/Configure/Checkers/SmimeKeyFile.pm 2008-12-15 10:14:56.000000000 -0500
@@ -0,0 +1,37 @@
+#
+# TWiki Enterprise Collaboration Platform, http://TWiki.org/
+#
+# Copyright (C) 2000-2006 TWiki Contributors.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version. For
+# more details read LICENSE in the root of this distribution.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# As per the GPL, removal of this notice is prohibited.
+package TWiki::Configure::Checkers::SmimeKeyFile;
+
+use strict;
+
+use base 'TWiki::Configure::Checker';
+
+use TWiki::Configure::Checker;
+use TWiki::Configure::Load;
+
+sub check {
+ my $this = shift;
+
+ my $certFile = $TWiki::cfg{SmimeKeyFile} || "";
+ $certFile =~ s/%DATE%/DATE/;
+ TWiki::Configure::Load::expandValue($certFile);
+ my $e = !-r ( $certFile ) && "Can\'t read $certFile";
+ $e = $this->ERROR($e) if $e;
+ return $e;
+}
+
+1;
--- TWiki/Net.pm~ 2008-10-24 07:09:22.000000000 -0400
+++ TWiki/Net.pm 2008-12-15 11:07:27.000000000 -0500
@@ -341,20 +341,45 @@
# split up header lines that are too long
$addrs =~ s/(.{60}[^,]*,\s*)/$1\n /go;
$addrs =~ s/\n\s*$//gos;
return $addrs;
}
+sub _slurpFile( $ ) {
+ my $file = shift;
+
+ unless( open( IN, '<', $file ) ) {
+ ( $<,$>) = ( $>,$<);
+ die( "Failed to open $file: $!\n" );
+ }
+ my $text = do { local( $/ ); <IN> };
+
+ unless( close IN ) {
+ ( $<,$>) = ( $>,$<);
+ die( "Failed to close $file: $!\n" );
+ }
+
+ return $text;
+}
+
sub _sendEmailBySendmail {
my( $this, $text ) = @_;
# send with sendmail
my ( $header, $body ) = split( "\n\n", $text, 2 );
$header =~ s/([\n\r])(From|To|CC|BCC)(\:\s*)([^\n\r]*)/$1.$2.$3._fixLineLength($4)/geois;
$text = "$header\n\n$body"; # rebuild message
+ if( $TWiki::cfg{SmimeCertificateFile} && $TWiki::cfg{SmimeKeyFile} ) {
+ use Crypt::SMIME;
+
+ my $smime = Crypt::SMIME->new();
+
+ $smime->setPrivateKey( _slurpFile( $TWiki::cfg{SmimeKeyFile} ), _slurpFile( $TWiki::cfg{SmimeCertificateFile} ) );
+ $text = $smime->sign( $text );
+ }
open( MAIL, '|'.$TWiki::cfg{MailProgram} ) ||
die "ERROR: Can't send mail using TWiki::cfg{MailProgram}";
print MAIL $text;
close( MAIL );
die "ERROR: Exit code $? from TWiki::cfg{MailProgram}" if $?;
}
Is there a problem feeding Net::SMTP? Because this seems like a rational and useful feature, but it really has to work with Net::SMTP.
--
CrawfordCurrie - 17 Dec 2008
Sorry for the delayed response - I didn't get notification of your comment.
I don't know that there's a problem feeding Net::SMTP - it wasn't immediately obvious how to do it, since the Net::SMTP API isn't very well documented & it wants the message to be supplied piecemeal. Given that it was easy to make it work with an external mailer I stopped there. Why do you think that it has to work with Net::SMTP? I don't really see any huge benefit - it does avoid a process activiation, but that shouldn't be significant in the scheme of things. Either we're sending a single e-mail (e.g. registration), or we're sending bulk e-mail (e.g. mailercontrib). In the latter case, it's one activation for the whole subscriber list. (Or if it isn't that should be fixed.) And that makes either sender more efficient...
FWIW, (what's left of) the TWiki folks decided to take this patch into their next release...The install documentation can be found on
http://twiki.org/cgi-bin/view/TWiki/TWikiInstallationGuide430
--
TimotheLitt - 07 Feb 2009--
TimotheLitt
We need Net::SMTP support for Windows users - and other platforms where sendmail might not be available. A lot of extensions are not Windows compatible, but for Core, it's required.