1) fromaddrs table format user:username primary address email:localpart@domainpart usernames1,username2,username3 email:localpart@ usernames1,username2,username3 email:@domainpart usernames1,username2,username3 email:localpart usernames1,username2,username3 email:domainpart usernames1,username2,username3 name:username Users Full Name and other details user:username is expected to occur only once, and is used to verify address used and is treated as the primary, when the supplied address needs to be replaced. for multiple email addresses allowed to a user, pick a primary and supply the rest as email:localpart@domainpart. If you do that, you will have to specify EVERY user that uses that email address. if there is no user:username entry, this serves to not restrict the user solong as they dont match any of the email: tags to interrupt the search order of email address, insert the key with a blank value. name:username is used for replacing the from address, this is optional but should be there, so that the From: does not appear merely as an email address. EXAMPLE: Joe has a primary and multiple allowed. Yackov has a primary and multiple allowed, some of which are shared with joe user:joe jmaimon@example.com email:support@example.com joe,yackov email:techgeek@example.com joe user:yackov yrosenbe@example.com email:mis@example.com yackov 2) requires two map definitions Kfromaddrs btree -A -o -t -z, /etc/mail/fromaddrs Kcompare program /usr/local/bin/smcompare 2a) fromaddrs map is built like so: makemap btree -de /etc/mail/fromaddrs < /etc/mail/fromaddrs 3) smcompare looks like #!/bin/bash arg1=`echo $1 | cut -f1 -d':'` arg2=`echo $1 | cut -f2 -d':'` if [[ "$arg1" == "$arg2" ]]; then echo MATCH fi 4) The order of lookups -check if the user is authenticated by retrieving the authenticated credentials --if not authenticated return the input -Parse for email address --if no email address, lookup user:credentials in the addr table ---if match, return the value along with a lookup of name:credential ---if no match, return the input -Lookup Email:email@address for a list of users --if no match lookup Email:email@ for a list of users --if no match lookup Email:@address for a list of users --if no match lookup Email:email for a list of users --if no match lookup Email:address for a list of users --if no match, lookup user:credentials in the addr table ---if not match return input ---if match, compare input email address with return value ----if they are equal return input ----if they are not equal return returned value along with a lookup of name:credential -check each user returned if it matches the credentials --if matches, return input -lookup user:credentials in the addr table --if match, return the value along with a lookup of name:credential --if no match, return blank (meaning email address is restricted, but we have no replacement) (downside to this: no way to say everybody can use EmailAddress X except for Y, except by saying Y can only use ABC email addresses) In general cases the email addr table will need only one primary address entry for each user and optionaly emailaddress entries for user(s) (Text of ruleset, seperate lside from rside with TAB) Srewrite_sender Srewrite_sender R$* $: $1 $| $&{auth_authen} $| R$* $| $| $@ $1 R$* $| $+ $| $: $1 $| $| $>ParseRecipient $1 R$* $| $| $+ < $* > $: $1 $| $2 < $3 > R$* $| $* $: $1 $| $| $>ParseRecipient $(fromaddrs User:$&{auth_authen} $: $) R$* $| $| $+ < $* > $@ $(fromaddrs Name:$&{auth_authen} $: $) < $2 $3 > R$* $| $* $@ $1 R$* $| $+ < $* > $: $1 $| $2 < $3 > $| $(fromaddrs Email:$2$3 $: $) $| R$* $| $+ < $* > $| $| $: $1 $| $2 < $3 > $| $(fromaddrs Email:$2@ $: $) $| R$* $| $+ < $* > $| $| $: $1 $| $2 < $3 > $| $(fromaddrs Email:$3 $: $) $| R$* $| $+ < $* > $| $| $: $1 $| $2 < $3 > $| $(fromaddrs Email:$2 $: $) $| R$* $| $+ < @$* > $| $| $: $1 $| $2 < @$3 > $| $(fromaddrs Email:$3 $: $) $| R$* $| $+ < $* > $| $| $: $1 $| $| $2 < $3 > $| $>ParseRecipient $(fromaddrs User:$&{auth_authen} $: $) R$* $| $| $+ < $* > $| < @ > $@ $1 R$* $| $| $+ < $* > $| $+ < $* > $: $1 $| $| $2 < $3 > $| $(storage {COMPARE} $@ $4 < $5 > $) $4 < $5 > R$* $| $| $&{COMPARE} $| $+ < $* > $@ $1 R$* $| $| $* $| $+ < $* > $@ $(fromaddrs Name:$&{auth_authen} $: $) < $3 $4 > R$* $| $* $@ $1 #Uhoh, should never happen R$* $| $+ < $* > $| $* $| $: $1 $| $4 $| R$* $| $* $&{auth_authen} $* $@ $1 R$* $| $* $: $1 $| $>ParseRecipient $(fromaddrs User:$&{auth_authen} $: $) R$* $| $+ < $* > $@ $(fromaddrs Name:$&{auth_authen} $: $) < $2 $3 > R$* $| $* $@ 5) The check ruleset Calls the lookup ruleset If return is the same as input, good, if not reject with error message (Text of ruleset, seperate lside from rside with TAB) Scheck_rewrite_sender R$* $: $1 $| $>rewrite_sender $1 R$* $| $* $: $1 $| $2 $| $(compare $1:$2 $) R$* $| $* $| MATCH $#OK R$* $#error $@ 5.7.3 $: "Your from address is not permitted to your identity" 6) Sendmail will need authenticate turned on and REQUIRED for port 587, 465 7) STARTTLS and a certificate should be enabled 8) the cert pub key, and signing cert should be available at an URL 9) The firewall should restrict users who are to be controlled by this design from the emailserver port 25 10) The emailserver can optionally require auth on 25, this is not recommended 11) clients will be configured to use 7,8 if available and 6 with AUTH 12) to rewrite headers with a milter requires milter-rrres currently at v12 http://www.jmaimon.com/sendmail 13) I have written a milter which will do the job 14) It needs an init.d rc script 15) It uses a wrapper ruleset in sendmail Smilter_rewrite_sender R$* $: $1 $| $>rewrite_sender $1 R$* $| $* $: $1 $| $2 $| $(compare $1:$2 $) R$* $| $* $| MATCH $#OK R$* $| $* $| $* $#error $: $2 16) sendmail.mc must have a few more config entries for the milter 17) Take care to not have : as a valid portion of anyone full name or email address