Mail::SRS

TriggerTek Logo
abcdefghijklmnopqrstuvwxyz_
Mail::SRS(3)	     User Contributed Perl Documentation	 Mail::SRS(3)



NAME
       Mail::SRS - Interface to Sender Rewriting Scheme

SYNOPSIS
	       use Mail::SRS;
	       my $srs = new Mail::SRS(
		       Secret	  => [ .... ],	  # scalar or array
		       MaxAge	  => 49,	  # days
		       HashLength => 4,		  # base64 characters: 4 x 6bits
		       HashMin	  => 4,		  # base64 characters
			       );
	       my $srsaddress = $srs->forward($sender, $alias);
	       my $sender = $srs->reverse($srsaddress);

DESCRIPTION
       The Sender Rewriting Scheme preserves .forward functionality in an
       SPF-compliant world.

       SPF requires the SMTP client IP to match the envelope sender
       (return-path). When a message is forwarded through an intermediate
       server, that intermediate server may need to rewrite the return-path
       to remain SPF compliant. If the message bounces, that intermediate
       server needs to validate the bounce and forward the bounce to the
       original sender.

       SRS provides a convention for return-path rewriting which allows mul-
       tiple forwarding servers to compact the return-path. SRS also provides
       an authentication mechanism to ensure that purported bounces are not
       arbitrarily forwarded.

       SRS is documented at http://spf.pobox.com/srs.html and many points
       about the scheme are discussed at http://www.anarres.org/projects/srs/

       For a better understanding of this code and how it functions, please
       read this document and run the interactive walkthrough in eg/simple.pl
       in this distribution. To run this from the build directory, type "make
       teach".

METHODS
       $srs = new Mail::SRS(...)

       Construct a new Mail::SRS object and return it. Available parameters
       are:

       Secret => $string
	   A key for the cryptographic algorithms. This may be an array or a
	   single string. A string is promoted into an array of one element.

       MaxAge
	   The maximum number of days for which a timestamp is considered
	   valid. After this time, the timestamp is invalid.

       HashLength => $integer
	   The number of bytes of base64 encoded data to use for the crypto-
	   graphic hash. More is better, but makes for longer addresses which
	   might exceed the 64 character length suggested by RFC2821. This
	   defaults to 4, which gives 4 x 6 = 24 bits of cryptographic infor-
	   mation, which means that a spammer will have to make 2^24 attempts
	   to guarantee forging an SRS address.

       HashMin => $integer
	   The shortest hash which we will allow to pass authentication.
	   Since we allow any valid prefix of the full SHA1 HMAC to pass
	   authentication, a spammer might just suggest a hash of length 0.
	   We require at least HashMin characters, which must all be correct.
	   Naturally, this must be no greater than HashLength and will
	   default to HashLength unless otherwise specified.

       Separator => $character
	   Specify the initial separator to use immediately after the SRS
	   tag. SRS uses the = separator throughout EXCEPT for the initial
	   separator, which may be any of + - or =.

	   Some MTAs already have a feature by which text after a + or - is
	   ignored for the purpose of identifying a local recipient. If the
	   initial separator is set to + or -, then an administrator may pro-
	   cess all SRS mails by creating users SRS0 and SRS1, and using
	   Mail::SRS in the default delivery rule for these users.

	   Some notes on the use and preservation of these separators are
	   found in the perldoc for Mail::SRS::Guarded.

       AlwaysRewrite => $boolean
	   SRS rewriting is not performed by default if the alias host
	   matches the sender host, since it would be unnecessary to do so,
	   and it interacts badly with ezmlm if we do. Set this to true if
	   you want always to rewrite when requested to do so.

       IgnoreTimestamp => $boolean
	   Consider all timestamps to be valid. Defaults to false. It is
	   STRONGLY recommended that this remain false. This parameter is
	   provided so that timestamps may be ignored temporarily after a
	   change in the timestamp format or encoding, until all timestamps
	   in the old encoding would have become invalid. Note that times-
	   tamps still form a part of the cryptographic data when this is
	   enabled.

       AllowUnsafeSrs
	   This is a backwards compatibility option for an older version of
	   the protocol where SRS1 was not hash-protected. The ’reverse’
	   method will detect such addresses, and handle them properly.
	   Deployments upgrading from version <=0.27 to any version >=0.28
	   should enable this for MaxAge+1 days.

	   When this option is enabled, all new addresses will be generated
	   with cryptographic protection.

       Some subclasses require other parameters. See their documentation for
       details.

       $srsaddress = $srs->forward($sender, $alias)

       Map a sender address into a new sender and a cryptographic cookie.
       Returns an SRS address to use as the new sender.

       There are alternative subclasses, some of which will return SRS com-
       pliant addresses, some will simply return non-SRS but valid RFC821
       addresses. See the interactive walkthrough for more information on
       this ("make teach").

       $sender = $srs->reverse($srsaddress)

       Reverse the mapping to get back the original address. Validates all
       cryptographic and timestamp information. Returns the original sender
       address. This method will die if the address cannot be reversed.

       $srs->compile($sendhost, $senduser)

       This method, designed to be overridden by subclasses, takes as parame-
       ters the original host and user and must compile a new username for
       the SRS transformed address. It is expected that this new username
       will be joined on $SRSSEP, and will contain a hash generated from
       $self->hash_create(...), and possibly a timestamp generated by
       $self->timestamp_create().

       $srs->parse($srsuser)

       This method, designed to be overridden by subclasses, takes an SRS-
       transformed username as an argument, and must reverse the transforma-
       tion produced by compile(). It is required to verify any hash and
       timestamp in the parsed data, using $self->hash_verify($hash, ...) and
       $self->timestamp_check($timestamp).

       $srs->timestamp_create([$time])

       Return a two character timestamp representing ’today’, or $time if
       given. $time is a Unix timestamp (seconds since the aeon).

       This Perl function has been designed to be agnostic as to base, and in
       practice, base32 is used since it can be reversed even if a remote MTA
       smashes case (in violation of RFC2821 section 2.4). The agnosticism
       means that the Perl uses division instead of rightshift, but in Perl
       that doesn’t matter. C implementors should implement this operation as
       a right shift by 5.

       $srs->timestamp_check($timestamp)

       Return 1 if a timestamp is valid, undef otherwise. There are 4096 pos-
       sible timestamps, used in a cycle. At any time, $srs->{MaxAge} times-
       tamps in this cycle are valid, the last one being today. A timestamp
       from the future is not valid, neither is a timestamp from too far into
       the past. Of course if you go far enough into the future, the cycle
       wraps around, and there are valid timestamps again, but the likelihood
       of a random timestamp being valid is 4096/$srs->{MaxAge}, which is
       usually quite small: 1 in 132 by default.

       $srs->time_check($time)

       Similar to $srs->timestamp_check($timestamp), but takes a Unix time,
       and checks that an alias created at that Unix time is still valid.
       This is designed for use by subclasses with storage backends.

       $srs->hash_create(@data)

       Returns a cryptographic hash of all data in @data. Any piece of data
       encoded into an address which must remain inviolate should be hashed,
       so that when the address is reversed, we can check that this data has
       not been tampered with. You must provide at least one piece of data to
       this method (otherwise this system is both cryptographically weak and
       there may be collision problems with sender addresses).

       $srs->hash_verify($hash, @data)

       Verify that @data has not been tampered with, given the cryptographic
       hash previously output by $srs->hash_create(); Returns 1 or undef.
       All known secrets are tried in order to see if the hash was created
       with an old secret.

       $srs->set_secret($new, @old)

       Add a new secret to the rewriter. When an address is returned, all
       secrets are tried to see if the hash can be validated. Don’t use
       "foo", "secret", "password", "10downing", "god" or "wednesday" as your
       secret.

       $srs->get_secret()

       Return the list of secrets. These are secret. Don’t publish them.

       $srs->separator()

       Return the initial separator, which follows the SRS tag. This is only
       used as the initial separator, for the convenience of administrators
       who wish to make srs0 and srs1 users on their mail servers and require
       to use + or - as the user delimiter. All other separators in the SRS
       address must be "=".

EXPORTS
       Given :all, this module exports the following variables.

       $SRSSEP
	   The SRS separator. The choice of "=" as internal separator was
	   fairly arbitrary. It cannot be any of the following:

	   / + Used in Base64.

	   -   Used in domains.

	   ! % Used in bang paths and source routing.

	   :   Cannot be used in a Windows NT or Apple filename.

	   ; │ *
	       Shell or regular expression metacharacters are probably to be
	       avoided.

       $SRS0TAG
	   The SRS0 tag.

       $SRS1TAG
	   The SRS1 tag.

       $SRSTAG
	   Deprecated, equal to $SRS0TAG.

       $SRSWRAP
	   Deprecated, equal to $SRS1TAG.

       $SRSHASHLENGTH
	   The default hash length for the SRS HMAC.

       $SRSMAXAGE
	   The default expiry time for timestamps.

EXAMPLES OF USAGE
       For people wanting boilerplate and those less familiar with using Perl
       modules in larger applications.

       Forward Rewriting

	       my $srs = new Mail::SRS(...);
	       my $address = ...
	       my $domain = ...
	       my $srsaddress = eval { $srs->forward($srsaddress, $domain); };
	       if ($@) {
		       # The rewrite failed
	       }
	       else {
		       # The rewrite succeeded
	       }

       Reverse Rewriting

	       my $srs = new Mail::SRS(...);
	       my $srsaddress = ...
	       my $address = eval { $srs->reverse($srsaddress); };
	       if ($@) {
		       # The rewrite failed
	       }
	       else {
		       # The rewrite succeeded
	       }

NOTES ON SRS
       Case Sensitivity

       RFC2821 states in section 2.4: "The local-part of a mailbox MUST BE
       treated as case sensitive. Therefore, SMTP implementations MUST take
       care to preserve the case of mailbox local-parts. [...]	In particu-
       lar, for some hosts the user "smith" is different from the user
       "Smith".	 However, exploiting the case sensitivity of mailbox local-
       parts impedes interoperability and is discouraged."

       SRS does not rely on case sensitivity in the local part. It uses
       base64 for encoding the hash, but allows a case insensitive match,
       making this approximately equivalent to base36 at worst. It will issue
       a warning if it detects that a remote MTA has smashed case. The times-
       tamp is encoded in base32.

       The 64 Billion Character Question

       RFC2821 section 4.5.3.1: Size limits and minimums:

	       There are several objects that have required minimum/maximum
	       sizes.  Every implementation MUST be able to receive objects
	       of at least these sizes. Objects larger than these sizes
	       SHOULD be avoided when possible. However, some Internet
	       mail constructs such as encoded X.400 addresses [16] will
	       often require larger objects: clients MAY attempt to transmit
	       these, but MUST be prepared for a server to reject them if
	       they cannot be handled by it. To the maximum extent possible,
	       implementation techniques which impose no limits on the length
	       of these objects should be used.

	       local-part
		       The maximum total length of a user name or other
		       local-part is 64 characters.

       Clearly, by including 2 domain names and a local-part in the rewritten
       address, there is no way in which SRS can guarantee to stay under this
       limit. However, very few systems are known to actively enforce this
       limit, and those which become known to the developers will be listed
       here.

       Cisco: PIX MailGuard (firewall gimmick)
       WebShield [something] (firewall gimmick)

       Invalid SRS Addresses

       DO NOT MALFORMAT ADDRESSES. This is designed to be an interoperable
       format. Certain things are allowed, such as changing the semantics of
       the hash or the timestamp. However, both of these fields must be
       present and separated by the SRS separator character "=". The purpose
       of this section is to illustrate that if a malicious party were to
       malformat an address, he would gain nothing by doing so, nor would the
       network suffer.

       The SRS protocol is predicated on the fact that the first forwarder
       provides a cryptographic wrapper on the forward chain for sending mail
       to the original sender. So what happens if an SRS address is invalid,
       or faked by a spammer?

       The minimum parsing of existing SRS addresses is done at each hop. If
       an SRS0 address is not valid or badly formatted, it will not affect
       the operation of the system: the mail will go out along the forwarder
       chain, and return to the invalid or badly formatted address.

       If the spammer is not pretending to be the first hop, then he must
       somehow construct an SRS0 address to embed within his SRS1 address.
       The cryptographic checks on this SRS0 address will fail at the first
       forwarder and the mail will be dropped.

       If the spammer is pretending to be the first hop, then SPF should
       require that any bounces coming back return to his mail server, thus
       he wins nothing.

       Cryptographic Systems

       The hash in the address is designed to prevent the forging of reverse
       addresses by a spammer, who might then use the SRS host as a for-
       warder.	It may only be constructed or validated by a party who knows
       the secret key.

       The cryptographic system in the default implementation is not man-
       dated.  Since nobody else ever needs to interpret the hash, it is rea-
       sonable to put any binary data into this field (subject to the possi-
       ble constraint of case insensitive encoding).

       The SRS maintainers have attempted to provide a good system. It satis-
       fies a simple set of basic requirements: to provide unforgeability of
       SRS addresses given that every MTA for a domain shares a secret key.
       We prefer SHA1 over MD5 for political, rather than practical reasons.
       (Anyone disputing this statement must include an example of a practi-
       cal weakness in their mail. We would love to see it.)

       If you find a weakness in our system, or you think you know of a bet-
       ter system, please tell us. If your requirements are different, you
       may override hash_create() and hash_verify() to implement a different
       system without adversely impacting the network, as long as your
       addresses still behave as SRS addresses.

       Extending Mail::SRS

       Write a subclass. You will probably want to override compile() and
       parse(). If you are more familiar with the internals of SRS, you might
       want to override hash_create(), hash_verify(), timestamp_create() or
       timestamp_check().

CHANGELOG
       MINOR CHANGES since v0.29


       timestamp_check now explicitly smashes case when verifying. This means
       that the base used must be base32, NOT base64.
       hash_create and hash_verify now explicitly smash case when creating
       and verifying hashes. This does not have a significant cryptographic
       impact.

       MAJOR CHANGES since v0.27


       The SRS1 address format has changed to include cryptographic informa-
       tion. Existing deployments should consider setting AllowUnsafeSrs for
       MaxAge+1 days.

       MINOR CHANGES since v0.26

       parse() and compile() are explicitly specified to die() on error.

       MINOR CHANGES since v0.23


       Update BASE32 according to RFC3548.

       MINOR CHANGES since v0.21

       Dates are now encoded in base32.
       Case insensitive MAC validation is now allowed, but will issue a warn-
       ing.

       MINOR CHANGES since v0.18


       $SRSTAG and $SRSWRAP are deprecated.
       Mail::SRS::Reversable is now Mail::SRS::Reversible
	   This should not be a problem since people should not be using it!

       You must use $SRS0RE and $SRS1RE to detect SRS addresses.

       MAJOR CHANGES since v0.15


       The separator character is now "=".
       The cryptographic scheme is now HMAC with SHA1.
       Only a prefix of the MAC is used.

       This API is still a release candidate and should remain relatively
       stable.

BUGS
       Email address parsing for quoted addresses is not yet done properly.

       Case insensitive MAC validation should become an option.

TODO
       Write a testsuite for testing user-defined SRS implementations.

SEE ALSO
       Mail::SRS::Guarded, Mail::SRS::DB, Mail::SRS::Reversable, "make
       teach", eg/*, http://www.anarres.org/projects/srs/

AUTHOR
	       Shevek
	       CPAN ID: SHEVEK
	       cpan@anarres.org
	       http://www.anarres.org/projects/

COPYRIGHT
       Copyright (c) 2004 Shevek. All rights reserved.

       This program is free software; you can redistribute it and/or modify
       it under the same terms as Perl itself.



perl v5.8.8			  2004-10-19			 Mail::SRS(3)