Mail-SpamAssassin
view release on metacpan or search on metacpan
Mail-SpamAssassin-rules-xxx.tgz
Mail-SpamAssassin-rules-xxx.tgz.asc
Mail-SpamAssassin-rules-xxx.tgz.sha512
(where xxx may look something like '4.0.0.r1900144')
Save them all to the current directory.
Obtain a rules-signing public key:
curl -O https://spamassassin.apache.org/updates/GPG.KEY
Import the signing key to the SpamAssassin gpg keyring, so that the rules
files can be verified safely:
sa-update --import GPG.KEY
Install rules from a compressed tar archive:
sa-update --install Mail-SpamAssassin-rules-xxx.tgz
Note that the ".tgz", ".tgz.asc" and ".tgz.sha512" files all need to
be in the same directory, otherwise sa-update will fail.
lib/Mail/SpamAssassin/Util/DependencyInfo.pm view on Meta::CPAN
Because perl module LWP does not support IPv6, sa-update as of
3.4.0 will use these standard programs to download rule updates
leaving LWP as a fallback if none of the programs are found.
*IMPORTANT NOTE*: You only need one of these programs
It's only a concern if you are warned about all 3
i.e. (curl, wget & fetch) missing";
our @OPTIONAL_BINARIES = (
{
binary => 'gpg',
version => '0',
recommended_min_version => '1.0.6',
version_check_params => '--version',
version_check_regex => 'gpg \(GnuPG\) ([\d\.]*)',
desc => 'The "sa-update" program requires this executable to verify
encryption signatures. It is not recommended, but you can use
"sa-update" with the --no-gpg to skip the verification. ',
},
{
binary => 'wget',
version => '0',
recommended_min_version => '1.8.2',
version_check_params => '--version',
version_check_regex => 'Gnu Wget ([\d\.]*)',
desc => $lwp_note,
},
{
sa-update.raw view on Meta::CPAN
##############################################################################
# Default list of GPG keys allowed to sign update releases
#
# pub 4096R/5244EC45 2005-12-20
# Key fingerprint = 5E54 1DC9 59CB 8BAC 7C78 DFDC 4056 A61A 5244 EC45
# uid updates.spamassassin.org Signing Key <release@spamassassin.org>
# sub 4096R/24F434CE 2005-12-20
#
# note for gpg newbs: these are "long" gpg keyids. It's common to also
# use the last 8 hex digits as a shorter keyid string.
#
my %valid_GPG = (
'0C2B1D7175B852C64B3CDC716C55397824F434CE' => 1,
'5E541DC959CB8BAC7C78DFDC4056A61A5244EC45' => 1,
);
# Default list of channels to update against
#
my @channels = ( 'updates.spamassassin.org' );
my $IGNORE_MIRBY_OLDER_THAN = (24 * 60 * 60 * 7); # 1 week
##############################################################################
my %opt;
@{$opt{'gpgkey'}} = ();
@{$opt{'channel'}} = ();
my $GPG_ENABLED = 1;
$opt{'gpghomedir'} = File::Spec->catfile($LOCAL_RULES_DIR, 'sa-update-keys');
Getopt::Long::Configure(
qw(bundling no_getopt_compat no_auto_abbrev no_ignore_case));
GetOptions(
'debug|D:s' => \$opt{'debug'},
'version|V' => \$opt{'version'},
'help|h|?' => \$opt{'help'},
'verbose|v+' => \$opt{'verbose'},
'checkonly' => \$opt{'checkonly'},
'allowplugins' => \$opt{'allowplugins'},
'reallyallowplugins' => \$opt{'reallyallowplugins'},
'refreshmirrors' => \$opt{'refreshmirrors'},
'forcemirror=s' => \$opt{'forcemirror'},
'httputil=s' => \$opt{'httputil'},
'score-multiplier=s' => \$opt{'score-multiplier'},
'score-limit=s' => \$opt{'score-limit'},
# allow multiple of these on the commandline
'gpgkey=s' => $opt{'gpgkey'},
'gpghomedir=s' => \$opt{'gpghomedir'},
'channel=s' => $opt{'channel'},
'install=s' => \$opt{'install'},
'import=s' => \$opt{'import'},
'gpgkeyfile=s' => \$opt{'gpgkeyfile'},
'channelfile=s' => \$opt{'channelfile'},
'updatedir=s' => \$opt{'updatedir'},
'gpg!' => \$GPG_ENABLED,
'4' => sub { $opt{'force_pf'} = 'inet' },
'6' => sub { $opt{'force_pf'} = 'inet6' },
# backward compatibility
'usegpg' => \$GPG_ENABLED,
) or print_usage_and_exit();
if ( defined $opt{'help'} ) {
print_usage_and_exit("For more information read the sa-update man page.\n", 0);
}
if ( defined $opt{'version'} ) {
print_version();
exit(0);
}
sa-update.raw view on Meta::CPAN
});
if (defined $opt{'updatedir'}) {
$opt{'updatedir'} = untaint_file_path($opt{'updatedir'});
}
else {
$opt{'updatedir'} = $SA->sed_path('__local_state_dir__/__version__');
}
# check only disabled gpg
# https://bz.apache.org/SpamAssassin/show_bug.cgi?id=5854
if ( defined $opt{'checkonly'}) {
$GPG_ENABLED=0;
dbg("gpg: Disabling gpg requirement due to checkonly flag.");
}
dbg("generic: sa-update version $VERSION");
dbg("generic: using update directory: $opt{'updatedir'}");
# doesn't really display useful things for this script, but we do want
# a module/version listing, etc. sa-update may be used for older versions
# of SA that don't include this function, so eval around it.
eval { $SA->debug_diagnostics(); 1; };
sa-update.raw view on Meta::CPAN
# untaint the command-line args; since the root user supplied these, and
# we're not a setuid script, we trust them
foreach my $optkey (keys %opt) {
next if ref $opt{$optkey};
untaint_var(\$opt{$optkey});
}
##############################################################################
# Deal with gpg-related options
if (@{$opt{'gpgkey'}}) {
$GPG_ENABLED = 1;
foreach my $key (@{$opt{'gpgkey'}}) {
unless (is_valid_gpg_key_id($key)) {
dbg("gpg: invalid gpgkey parameter $key");
next;
}
$key = uc $key;
dbg("gpg: adding key id $key");
$valid_GPG{$key} = 1;
}
}
if (defined $opt{'gpgkeyfile'}) {
$GPG_ENABLED = 1;
open(GPG, $opt{'gpgkeyfile'})
or die "cannot open $opt{'gpgkeyfile'} for reading: $!\n";
dbg("gpg: reading in gpgfile ".$opt{'gpgkeyfile'});
while(my $key = <GPG>) {
chomp $key;
$key =~ s/#.*$//; # remove comments
$key =~ s/^\s+//; # remove leading whitespace
$key =~ s/\s+$//; # remove tailing whitespace
next if $key eq ''; # skip empty lines
unless (is_valid_gpg_key_id($key)) {
dbg("gpg: invalid key id $key");
next;
}
$key = uc $key;
dbg("gpg: adding key id $key");
$valid_GPG{$key} = 1;
}
close(GPG) or die "cannot close $opt{'gpgkeyfile'}: $!";
}
# At this point, we need to know where GPG is ...
my $GPGPath;
if ($GPG_ENABLED || $opt{'import'}) {
# find GPG in the PATH
# bug 4958: for *NIX it's "gpg", in Windows it's "gpg.exe"
$GPGPath = 'gpg' . $Config{_exe};
dbg("gpg: Searching for '$GPGPath'");
if ($GPGPath = Mail::SpamAssassin::Util::find_executable_in_env_path($GPGPath)) {
dbg("gpg: found $GPGPath");
# bug 5030: if GPGPath has a space, put it in quotes
if ($GPGPath =~ / /) {
$GPGPath =~ s/"/\\"/g;
$GPGPath = qq/"$GPGPath"/;
dbg("gpg: path changed to $GPGPath");
}
}
else {
die "error: gpg required but not found! It is not recommended, but you can use \"sa-update\" with the --no-gpg to skip the verification. \n";
}
# GPG was found, and we've been asked to import a key only
if ( $opt{'import'} ) {
my $ex = import_gpg_key($opt{'import'});
exit $ex;
}
# does the sa-update keyring exist? if not, import it
if(!-f File::Spec->catfile($opt{'gpghomedir'}, "trustdb.gpg")) {
import_default_keyring();
# attempt to continue even if this fails, anyway
}
# specify which keys are trusted
dbg("gpg: release trusted key id list: ".join(" ", keys %valid_GPG));
# convert fingerprint gpg ids to keyids
foreach (keys %valid_GPG) {
my $id = substr $_, -8;
$valid_GPG{$id} = 1;
}
}
##############################################################################
# Deal with channel-related options
if (defined $opt{'channel'} && scalar @{$opt{'channel'}} > 0) {
sa-update.raw view on Meta::CPAN
dbg("channel: populating temp content file %s", $content_file);
open(TMP, ">$content_file")
or die "fatal: cannot create content temp file $content_file: $!\n";
binmode TMP
or die "fatal: cannot set binmode on content temp file $content_file: $!\n";
print TMP $content
or die "fatal: cannot write to content temp file $content_file: $!\n";
close TMP
or die "fatal: cannot close content temp file $content_file: $!\n";
# to sign : gpg -bas file
# to verify: gpg --verify --batch --no-tty --status-fd=1 -q --logger-fd=1 file.asc file
# look for : [GNUPG:] GOODSIG 6C55397824F434CE updates.spamassassin.org [...]
# [GNUPG:] VALIDSIG 0C2B1D7175B852C64B3CDC716C55397824F434CE [...]
# [GNUPG:] NO_PUBKEY 6C55397824F434CE
if ($GPG) {
dbg("gpg: populating temp signature file");
my $sig_file;
($sig_file, $tfh) = secure_tmpfile();
$tfh
or die "fatal: couldn't create temp file for GPG signature: $!\n";
binmode $tfh
or die "fatal: cannot set binmode on temp file for GPG signature: $!\n";
print $tfh $GPG
or die "fatal: cannot write temp file for GPG signature: $!\n";
close $tfh
or die "fatal: cannot close temp file for GPG signature: $!\n";
undef $tfh;
dbg("gpg: calling gpg");
my $gpghome = interpolate_gpghomedir();
# TODO: we could also use "--keyserver pgp.mit.edu" or similar,
# to autodownload missing keys...
my $CMD = "$GPGPath $gpghome --verify --batch ".
"--no-tty --status-fd=1 -q --logger-fd=1";
unless (open(CMD, "$CMD $sig_file $content_file|")) {
unlink $sig_file or warn "error: cannot unlink $sig_file: $!\n";
die "fatal: couldn't execute $GPGPath: $!\n";
}
# Determine the fate of the signature
my $signer = '';
my $missingkeys = '';
while(my $GNUPG = <CMD>) {
chop $GNUPG;
dbg ("gpg: $GNUPG");
if ($GNUPG =~ /^gpg: fatal:/) {
warn $GNUPG."\n"; # report bad news
}
local($1);
if ($GNUPG =~ /^\Q[GNUPG:]\E NO_PUBKEY \S+(\S{8})$/) {
$missingkeys .= $1." ";
}
next unless ($GNUPG =~ /^\Q[GNUPG:]\E (?:VALID|GOOD)SIG (\S{8,40})/);
my $key = $1;
sa-update.raw view on Meta::CPAN
if (length $key > 8 && length $key < 40) {
substr($key, 8) = '';
}
# use the longest match we can find
$signer = $key if length $key > length $signer;
}
my $errno = 0; close CMD or $errno = $!;
proc_status_ok($?,$errno)
or warn("gpg: process '$GPGPath' finished: ".
exit_status_str($?,$errno)."\n");
unlink $sig_file or warn "cannot unlink $sig_file: $!\n";
if ($signer) {
my $keyid = substr $signer, -8;
dbg("gpg: found signature made by key $signer");
if (exists $valid_GPG{$signer}) {
dbg("gpg: key id $signer is release trusted");
}
elsif (exists $valid_GPG{$keyid}) {
dbg("gpg: key id $keyid is release trusted");
}
else {
dbg("gpg: key id $keyid is not release trusted");
$signer = undef;
}
}
unless ($signer) {
warn "error: GPG validation failed!\n";
if ($missingkeys) {
warn <<ENDOFVALIDATIONERR;
sa-update.raw view on Meta::CPAN
##############################################################################
sub usage {
my ( $verbose, $message ) = @_;
print "sa-update version $VERSION\n";
pod2usage( -verbose => $verbose, -message => $message, -exitval => 64 );
}
##############################################################################
sub interpolate_gpghomedir {
my $gpghome = '';
if ($opt{'gpghomedir'}) {
$gpghome = $opt{'gpghomedir'};
if (am_running_on_windows()) {
# windows is single-quote-phobic; bug 4958 cmt 7
$gpghome =~ s/\"/\\\"/gs;
$gpghome = "--homedir=\"$gpghome\"";
} else {
$gpghome =~ s/\'/\\\'/gs;
$gpghome = "--homedir='$gpghome'";
}
}
return $gpghome;
}
##############################################################################
sub check_gpghomedir {
unless (-d $opt{gpghomedir}) {
dbg("gpg: creating gpg home dir ".$opt{gpghomedir});
# use 0700 to avoid "unsafe permissions" warning
mkpath([$opt{gpghomedir}], 0, 0700)
or die "cannot mkpath $opt{gpghomedir}: $!";
}
}
##############################################################################
sub import_gpg_key {
my $keyfile = shift;
my $gpghome = interpolate_gpghomedir();
check_gpghomedir();
my $CMD = "$GPGPath $gpghome --batch ".
"--no-tty --status-fd=1 -q --logger-fd=1 --import";
unless (open(CMD, "$CMD $keyfile|")) {
die "fatal: couldn't execute $GPGPath: $!\n";
}
# Determine the fate of the signature
while(my $GNUPG = <CMD>) {
chop $GNUPG;
dbg ("gpg: $GNUPG");
if ($GNUPG =~ /^gpg: /) {
warn $GNUPG."\n"; # report bad news
}
if ($GNUPG =~ /^IMPORTED /) {
dbg("gpg: gpg key imported successfully");
}
}
my $errno = 0; close CMD or $errno = $!;
proc_status_ok($?,$errno)
or warn("gpg: process '$CMD' finished: ".exit_status_str($?,$errno)."\n");
return ($? >> 8);
}
##############################################################################
sub import_default_keyring {
my $defkey = File::Spec->catfile ($DEF_RULES_DIR, "sa-update-pubkey.txt");
unless (-f $defkey) {
dbg("gpg: import of default keyring failed, couldn't find sa-update-pubkey.txt");
return;
}
dbg("gpg: importing default keyring to ".$opt{gpghomedir});
check_gpghomedir();
import_gpg_key($defkey);
}
##############################################################################
sub is_valid_gpg_key_id {
# either a keyid (8 bytes) or a fingerprint (40 bytes)
return ($_[0] =~ /^[a-fA-F0-9]+$/ && (length $_[0] == 8 || length $_[0] == 40));
}
##############################################################################
sub clean_update_dir {
my($dir, $preserve_files_ref) = @_;
dbg("generic: cleaning directory %s", $dir);
sa-update.raw view on Meta::CPAN
Options:
--channel channel Retrieve updates from this channel
Use multiple times for multiple channels
--channelfile file Retrieve updates from the channels in the file
--checkonly Check for update availability, do not install
--install file Install updates directly from this file. Signature
verification will use "file.asc", or "file.sha512"
or "file.sha256".
--allowplugins Allow updates to load plugin code (DANGEROUS)
--gpgkey key Trust the key id to sign releases
Use multiple times for multiple keys
--gpgkeyfile file Trust the key ids in the file to sign releases
--gpghomedir path Store the GPG keyring in this directory
--gpg and --nogpg Use (or do not use) GPG to verify updates
(--gpg is assumed by use of the above
--gpgkey and --gpgkeyfile options)
--import file Import GPG key(s) from file into sa-update's
keyring. Use multiple times for multiple files
--updatedir path Directory to place updates, defaults to the
SpamAssassin site rules directory
(default: @@LOCAL_STATE_DIR@@/@@VERSION@@)
--refreshmirrors Force the MIRRORED.BY file to be updated
--forcemirror url Use a specific mirror instead of downloading from
official mirrors
--httputil util Force used download tool. By default first found
from these is used: curl, wget, fetch, lwp
sa-update.raw view on Meta::CPAN
activate plugins; any C<loadplugin> or C<tryplugin> lines will be commented
in the downloaded update rules files.
You should never enable this for 3rd party update channels, since plugins
can execute unrestricted code on your system, even possibly as root! This
includes spamassassin official updates, which have no need to include
running code.
Use --reallyallowplugins option to bypass warnings and make it work.
=item B<--gpg>, B<--nogpg>
sa-update by default will verify update archives by use of GPG signature.
If you wish to skip GPG verification (very unsafe), you can use the
B<--nogpg> option to disable its use. Use of the following gpgkey-related
options will override B<--nogpg> and keep GPG verification enabled.
If GPG is disabled, only SHA512 or SHA256 checksums are used to verify
whether or not the downloaded archive has been corrupted, but it does not
offer any form of security regarding whether or not the downloaded archive
is legitimate (aka: non-modifed by evildoers).
Note: Only GnuPG is supported (ie: not any other PGP software).
=item B<--gpgkey>
sa-update has the concept of "release trusted" GPG keys. When an archive is
downloaded and the signature verified, sa-update requires that the signature
be from one of these "release trusted" keys or else verification fails. This
prevents third parties from manipulating the files on a mirror, for instance,
and signing with their own key.
By default, sa-update trusts key ids C<24F434CE> and C<5244EC45>, which are
the standard SpamAssassin release key and its sub-key. Use this option to
trust additional keys. See the B<--import> option for how to add keys to
sa-update's keyring. For sa-update to use a key it must be in sa-update's
keyring and trusted.
For multiple keys, use the option multiple times. i.e.:
sa-update --gpgkey E580B363 --gpgkey 298BC7D0
Note: use of this option automatically enables GPG verification.
=item B<--gpgkeyfile>
Similar to the B<--gpgkey> option, except specify the additional keys in a file
instead of on the commandline. This is extremely useful when there are a lot
of additional keys that you wish to trust.
=item B<--gpghomedir>
Specify a directory path to use as a storage area for the C<sa-update> GPG
keyring. By default, this is
@@LOCAL_RULES_DIR@@/sa-update-keys
=item B<--import>
Use to import GPG key(s) from a file into the sa-update keyring which is
located in the directory specified by B<--gpghomedir>. Before using channels
from third party sources, you should use this option to import the GPG key(s)
used by those channels. You must still use the B<--gpgkey> or B<--gpgkeyfile>
options above to get sa-update to trust imported keys.
To import multiple keys, use the option multiple times. i.e.:
sa-update --import channel1-GPG.KEY --import channel2-GPG.KEY
Note: use of this option automatically enables GPG verification.
=item B<--refreshmirrors>
sa-update.raw view on Meta::CPAN
Note that use of this option is not recommended; if you're just using sa-update
to download updated rulesets for a scanner, and sa-update is placing updates in
the wrong directory, you probably need to rebuild SpamAssassin with different
C<Makefile.PL> arguments, instead of overriding sa-update's runtime behaviour.
=item B<-D> [I<area,...>], B<--debug> [I<area,...>]
Produce debugging output. If no areas are listed, all debugging information is
printed. Diagnostic output can also be enabled for each area individually;
I<area> is the area of the code to instrument. For example, to produce
diagnostic output on channel, gpg, and http, use:
sa-update -D channel,gpg,http
For more information about which areas (also known as channels) are
available, please see the documentation at
L<https://wiki.apache.org/spamassassin/DebugChannels>.
=item B<-h>, B<--help>
Print help message and exit.
=item B<-V>, B<--version>
( run in 1.018 second using v1.01-cache-2.11-cpan-df04353d9ac )