Bash – OpenBSD 6.0 chrooted httpd with php 7.0 mail() works, but no mail goes out

bashchrootopenbsdphp-fpmsendmail

I started with a fresh install of OpenBSD 6.0 which has a chroot (/var/www) on their httpd server (not Apache). I installed PHP 7.0 and set up the php-fpm using the binary installs. In the web root there exists both sendmail and femail objects. I moved a website into place and php works very well and php queries the postgresql database (also installed from binary) and everything works well – except mail().

I set up a log file in /var/www/logs/php.mail.log and it sees that mail is being recognized by php with log entries such as this:

[09-Dec-2016 15:04:34 UTC] mail() on [/do_quick_mail_test.php:23]: To: myemail@domain.com -- Headers: From: support@domain.com (domain.com Robot)

No errors occur in /var/www/logs/error.log nor in the system messages.

No indication of an email in the system maillog.

when I run the command from the command line like this it works and mail is delivered normally with no problem:

echo 'Subject: test' | chroot /var/www /usr/sbin/sendmail -v myemail@domain.com

The php program I am hitting with my browser is a very simple one:

<?php
session_start();

header( "Content-Type: text/plain" );

echo( 'Configuration Tests:'."\n" );

echo('Testing DNS:'."\n" );
print_r( dns_get_record("trialtoaster.com") );
echo( 'localhost lookup: '.gethostbyname( "localhost" )."\n" );

echo('Testing DateTime:'."\n" );
print_r( getdate() );

echo('Sending test email:'."\n" );
if ( mail("myemail@domain.com", "PHP Test mail", "PHP email - test message.", "From: support@domain.com (domain.com Robot)") ) {
        echo '- PHP thinks the email went normally.';
} else {
        echo '- PHP thinks the email failed.';
}
?>

The program produces no failures except mail() which dies. The DNS test returns all of the records including the MX records and the date is accurate. In spite of logging correctly in the php mail log.

When displayed, phpinfo() reflects the configuration correctly:

sendmail_path:  /usr/sbin/sendmail -t
SMTP:  localhost
smtp_port: 25

When I check the packet filter it is allowing anything in on lo0 to go anywhere and when I run the command I can see it on the pftop but nothing shows when I run the mail() from the browser.

I have tied installing a sendmail.ini in the same directory as the chrooted sendmail and that makes no difference whatsoever.

It is starting to look like the OpenBSD chrooted httpd install is incomplete in that in order to use the php mail() command something is just plain completely missing and I fear it might be a bash shell and libraries since the mail sends fine from the command line. This seems out of whack to me since the point of the chroot is to jail hacks and giving a bash shell and libraries to a jailed system just seems like a LOT of surface area for an attack.

I just feel like that CAN'T be the problem, because otherwise, you might as well just drop the chroot and just run it without a jail (it would seem).

Does anyone see what I am missing – and if not and I copy in a shell and libraries, what is the safest way to do that with the minimum exposure and without writing custom wrappers that can just be rewrapped by an attacker anyway?

Best Answer

I believe this to be solved in one of two ways:

(1) You CAN solve this by installing an executable shell in the chroot so that the sendmail binary can run. If you do, even if you install it in a wrapper, you have increased the surface area for an attack and you may as well just drop the chroot. Wrappers can be re-wrapped and all you need to do is perform a restart and your system is cracked. My vote is not to do that.

(2) The best option is to abandon mail and use SMTP directly through a socket - pretty much the same way that PHP and the webserver itself are already operating. There is no shell on the chroot and all you are doing is installing more php code and letting that code open a socket on the localhost to port 25 where your MTA is already listening and passing through to the outside world but not executing any arbitrary code.

Here is how that works:

Install Pear if it is not already installed and then install the mail scripts. You can do this easily like this:

pkg_add install Pear
pear install Mail_smtp
pear install Net_SMTP

Depending upon your system - the first pear install may do the second one for you as a dependency.

From there I added a php function in its own php program:

<?php
/**
 * Sends an email using SMTP directly rather than using the sendmail binary (which requires
 * a shell environment to run).  This allows the chrooted server to run with less exposure.
*/
require_once "Mail.php";

function SMTP_mail($recipients, $subjectHeader, $message, $fromHeader)
{
    $headers['From']    = $fromHeader;
    $headers['Subject'] = $subjectHeader;

    $smtpinfo["host"] = "localhost";
    $smtpinfo["port"] = "25";
    $smtpinfo["auth"] = false;

    // Create the mail object using the Mail::factory method
    $mail_object = Mail::factory("smtp", $smtpinfo);

    $mail_object->send($recipients, $headers, $message);
}

All that was left to do was to go through the code I was migrating and convert the mail instructions to use this function instead:

SMTP_mail($sendToEmailAddr, $subjectLine, $messageBody, 'From: Support@domain.com (Domain.com Robot)');

If you have not already done so, the ability to send mail outside the chroot should already be working. The important but for us here is in the /etc/mail/smtp.conf file:

listen on lo0

# Since we are only listening on the lo0 (local) we can safely use
# commands that are "accept from any" or bare "accept" commands.

# accept from any for domain "example.org" alias <aliases> deliver to mbox
accept for local alias <aliases> deliver to mbox

# accept from the lo0 (local) interface anything and relay it out
accept for any relay

# This was the original command - use it if you ever open up
# the external interface by doing a "listen on any" rather than
# the above command - that will keep us from being an open relay:
#accept from local for any relay

Give that a nice reboot and it should just work - given that you have the same setup I did in my question. PHP will open an SMTP connection over the localhost in the chroot to the localhost outside the chroot, send the email that you programmed it to send, and close the connection. OpenBSD's mailer.conf will make sure that the "real" sendmail (smtpctl) gets it and routes it to the outside world based upon the MX entry in the DNS of the mailhost for that email address. You will want to be sure that SMTP is running by setting smtpd_flags in your /etc/rc.conf.local system file.

All run by demons and as safe as your program code. I hope this helps!