0

I'm trying to use PHPMailer to send emails via SMTP over TLS.

I've found that when PHPMailer attempts to connect to the SMTP server and STARTTLS is invoked, the connection immediately fails - unless I set the PHP SSL context variable verify_peer_name => false when opening the SMTP connection.

The SMTP server I'm connecting to is prefix.myredactedcompany.mailguard.com.au:2525. Looking at the SMTP logs, I see that when I ask to connect to prefix.myredactedcompany.mailguard.com.au:2525, I actually get connected to someotherhost.mailguard.com.au. I assume this is the result of a load balancer or other clustered setup.

When I follow the PHPMailer troubleshooting guide to test the OpenSSL connection outside of PHP by running the command echo QUIT | .\openssl.exe s_client -starttls smtp -crlf -connect prefix.myredactedcompany.mailguard.com.au:2525, I get the following results:

CONNECTED(000001E0)
---
Certificate chain
 0 s:/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIF0jCCBLqgAwIBAgIQI1raes2jQvcAbHxOBIAtqTANBgkqhkiG9w0BAQsFADCB
... snip ...
pYwh4eDZtcm4tZQfc71R1KhA9ci5A0G9ewPLmZUYoDlguNdlNlVf07aus54EV6XI
1wHfJ/xs
-----END CERTIFICATE-----
subject=/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 5395 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: redacted
    Session-ID-ctx: 
    Master-Key: redacted
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - 18 ad e7 c2 c9 46 ab 96-5f 58 03 81 fc 48 3c 18   .....F.._X...H<.
    ... snip ...
    0090 - 41 47 9b f2 60 c4 41 5f-0d fc ea 2b 40 0c 25 3b   AG..`.A_...+@.%;

    Start Time: 1549441609
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---

The PHPMailer troubleshooting guide says that in this context

The verify error:num=20:unable to get local issuer certificate is not a problem.

This looks like a valid wildcard certificate for all subdomains of mailguard.com.au to me. Why is PHPMailer/OpenSSL rejecting it, unless I turn off peer name verification? Is it because the actual SMTP host name doesn't match the DNS name I used when establishing the outbound connection?

If I leave peer name verification turned off, what security risks does that expose me to?

Versions:

  • PHP 7.1.7
  • PHPMailer 6.0.6
  • OpenSSL 1.0.2l
  • Running on Windows via XAMPP 7.1.7, if it matters

A commenter asked what the openssl connection results were like when connecting directly to one of the hosts in the pool. It's extremely similar:

CONNECTED(000001F8)
---
Certificate chain
 0 s:/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIF0jCCBLqgAwIBAgIQI1raes2jQvcAbHxOBIAtqTANBgkqhkiG9w0BAQsFADCB
...snip...
pYwh4eDZtcm4tZQfc71R1KhA9ci5A0G9ewPLmZUYoDlguNdlNlVf07aus54EV6XI
1wHfJ/xs
-----END CERTIFICATE-----
subject=/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 5399 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: redacted
    Session-ID-ctx: 
    Master-Key: redacted
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - 6d bd 68 e7 44 29 72 ea-99 00 c8 84 a6 cc 45 76   m.h.D)r.......Ev
    ... snip ...
    0090 - 28 73 65 a4 a1 24 fd c6-18 ad fb 13 26 ec 6f b9   (se..$......&.o.

    Start Time: 1549519771
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---

Ok, so the comments in php.ini say that an empty openssl.cafile setting will cause PHP to use the OS-managed certificate store. This appears to be untrue on Windows, since OpenSSL doesn't have built-in support for the Windows certificate store. So, I followed the instructions in PHP - SSL certificate error: unable to get local issuer certificate to get a local root CA bundle set up. That seems to be working now. When I run the OpenSSL test command given above and append -CAfile C:\xampp\extras\cacert.pem, the final result is now Verify return code: 0 (ok) instead of Verify return code: 20 (unable to get local issuer certificate).

I've restarted Apache and checked the value of openssl.cafile in phpinfo() is good. But PHPMailer still won't successfully STARTTLS. Symptoms are the same as before - if I set the SSL context variable verify_peer_name to false, PHPMailer successfully connects. If I leave verify_peer_name turned on, the connection fails during STARTTLS.

I'm still interested in knowing:

  1. What are the security implications of disabling verify_peer_name? I'm assuming it would make it easy to MITM the connection.
  2. How I can further diagnose the exact cause of the STARTTLS failures, especially now that the certificate passes OpenSSL's validation?
Hydrargyrum
  • 3,378
  • 4
  • 27
  • 41
  • 1
    _“I actually get connected to `someotherhost.mailguard.com.au`”_ - what result do you get if you run that openssl check command using that hostname directly? – 04FS Feb 06 '19 at 09:08
  • 1
    It may be because wildcards only match at a single level, i.e. `prefix.myredactedcompany.mailguard.com.au` does not match `*.mailguard.com.au`. Alternatively it could be down to the CA certificate bundle that PHP is using. – Synchro Feb 06 '19 at 10:14
  • You may find the output from [testssl.sh](https://testssl.sh) is more comprehensible, if you can get it to run on Windows. – Synchro Feb 06 '19 at 10:22
  • @O4FS I opened a telnet session to the main host name, made a note of the host name that I was connected to, and then ran the OpenSSL diagnostic command against that host. I've appended the results to the bottom of the question. It looks substantially identical to me. – Hydrargyrum Feb 07 '19 at 06:14
  • According to phpinfo(), the openssl.cafile and openssl.capath configuration settings are both empty. The php.ini comments say that "PHP will attempt to use the OS-managed cert stores in [openssl.cafile's] absence", but maybe that's not true on Windows? Various web pages indicate that OpenSSL can't read the Windows certificate store. – Hydrargyrum Feb 07 '19 at 06:29
  • I've addressed the missing configuration of `openssl.cafile` but PHPMailer still fails on STARTTLS – Hydrargyrum Feb 07 '19 at 07:36
  • @Synchro the tip about the wildcard not matching sub-sub-domains looks promising, in conjunction with addressing the root CA cert misconfiguration. Maybe post those two things as a proposed answer? If it checks out I'll accept it. – Hydrargyrum Feb 07 '19 at 14:48

1 Answers1

1

It may be because wildcards only match at a single level, i.e. prefix.myredactedcompany.mailguard.com.au does not match *.mailguard.com.au. Using the reported name (seen when you connect) should help resolve that.

Alternatively it could be down to the CA certificate bundle that PHP is using, meaning that the server is ok, but you are unable to verify its certs correctly - this is often hard to diagnose because it can be fiddly to tell where in your chain the verification is failing. For example if your chain is:

host cert -> intermediate cert -> root (CA) cert

A verification failure in any of those will result in a verification failure, but it may not be clear which one is wrong.

You may need to fetch a copy of the latest CA certificate bundle and tell PHP to use it (as described in the PHPMailer troubleshooting guide), or use a package like Certainty to manage it from your app.

It could also be that your intermediate certificates are in the wrong order.

Synchro
  • 35,538
  • 15
  • 81
  • 104
  • 1
    Thanks. The two issues we had were: 1) the hostname we were told to connect to had more levels of subdomain than the wildcard certificate configured on the SMTP servers; and 2) stock XAMPP 7.1.7 doesn't actually configure OpenSSL to use the CA bundle that is included in XAMPP. Now I don't need to disable `verify_peer_name`. – Hydrargyrum Feb 08 '19 at 05:07