Archive

Archive for the ‘Rants’ Category

PHP and Sendmail – Custom Submit.cf Configurations

August 29th, 2014 No comments

aka: How did sendmail annoy me today?

I run multiple sendmails for different virtual hosts and ip addresses. When I send email from php on my web server I expect the emails to be perfect. In any default configuration they would all send via the same sendmail instance/ip and that sucks.

Why it sucks:

1. If the envelope “from” address is overridden, you get a stupid “X-Authentication-Warning” header in the email.

2. The submission program inserts this lame header “Received: (from httpd@localhost)”  which only serves to increase the odds of being flagged as spam.

3.  You get other Received: headers that refer to whatever the default MTA host is, not likely the host you really want to send the mail from. That makes your email look spammy.

Here’s what I did to do this up right.

1. Hack sendmail! Ultimately I’d prefer to have multiple configuration files for my mail submission program. The hidden super evil gotcha is that if you specify an alternate config on the command line, sendmail drops it’s setgid smmsp group privileges. That results in all sorts of horrible problems.

Fixing the -C option to not drop privileges would make a security hole since anyone could run sendmail with any config file.

Eventually I compromised and modified -C to allow ‘trusted’ .cf files. It essentially prefixes your .cf with /etc/mail/ to ensure it’s in a trusted location. It was too much hassle to enforce MSP mode so just don’t be stupid with it I guess. Yes I suppose someone could attempt to run one of your daemon config files.

         case 'C':
            if (optarg[0] == 't')
            {
                /* Select TRUSTED configuration file (must reside in _DIR_SENDMAILCF), FORCE SUBMIT TYPE */
                cftype = SM_GET_SUBMIT_CF; /* this won't ensure we enforce submit mode but whatever */
                static char cfs[MAXPATHLEN];
                (void) sm_strlcpyn(cfs, sizeof cfs, 2, _DIR_SENDMAILCF, &optarg[1]);
                conffile = newstr(cfs);
            }
            else
            {
                /* select configuration file (already done) */                        
                if (RealUid != 0)
                    warn_C_flag = true;
                conffile = newstr(optarg);
                dp = drop_privileges(true);
                setstat(dp);
                safecf = false;
            }
            break;

You could look at libsmutil/cf.c to see how the name for submit.cf is usually produced.

I specify my new sendmail binary in php.ini

sendmail_path = /usr/sbin/sendmail-php -L sm-msp-site1 -Ctsubmit-php-site1.cf -t -i

If you have multiple web hosts and each one needs to use it’s own sendmail instance, you can set this variable in your virtual host’s “directory” definition .

<VirtualHost *:80>
Standard stuff goes here

<Directory /dir/to/your/web/root>
php_admin_value sendmail_path "/usr/sbin/sendmail -L sm-msp-site1 -Ctsubmit-php-site1.cf -t -i"
</Directory>

</VirtualHost>

I stole that tip from the web. Very nice.

Don’t forget to add your httpd or apache user to the trusted_users file in /etc/mail. The default submit.cf doesn’t enable the trusted user file so be warned. You must both define it and enable it. Or just add the trusted user to the .cf file itself… that might be good when you have multiple sendmails and multiple websites with your web servers also running as multiple separate users. Disabling the trusted-users file (or having a unique file name for each unique submit.cf) would prevent one httpd user from using a sendmail config intended for another httpd user and potentially sending out faked emails.

in m4:
FEATURE(use_ct_file)dnl
define(`confCT_FILE’, `/etc/mail/trusted-users’)

in .cf:
Ft/etc/mail/trusted-users
or
Thttpd

I also commented out the entire HReceived header in the .cf, I don’t need the MSP inserting it’s own stupid “Received: (from httpd@localhost)” header. (problem #2)

Set this ip to the server you want to deliver the mail to.
O ClientPortOptions=Family=inet, Address=x.x.x.x

I’m not saying there isn’t a better way. Odds are if you’re reading this far you don’t know either.