Wrong certificate when fetching email

Dannik

Verified User
Joined
Jan 7, 2009
Messages
83
Location
Netherlands
Hi,

Most of my servers are setup with many default options, but this week a certificate problem was brought to my attention. 1 of my customers wants to pull mails using imap_open(). This works, but only with unencrypted connections:

PHP:
$hostname = '{mail.usersdomain.nl:143/notls}'; // functioning ok, but with security errors
$hostname = '{mail.usersdomain.nl:993/imap/ssl}'; // failing on the certificate most times; the server's certificate is returned

I found this very strange, so I tried myself at work, using a Windows server with IIS. All worked fine, I could connect and fetch mail without any problems and the right certificate was presented.
However, when running from some other servers (running FreeBSD and AlmaLinux) I experienced something else: the returned certificate was the one from the DA server, not from the originally requested domain. Tests show this also occurs with other domains on both my DA servers.
All tested domains use Let's Encrypt certificates and different SSL-checkers show everything is fine.

Can anyone help me with this problem?

I tested thing using the script below.

Regards,
Danny

PHP:
<?php

// $host  = 'mail.usersdomain.nl'; This doesn't work, so we have to use a workaround

// Workaround
$domain = 'mail.usersdomain.nl';
$ip = gethostbyname($domain);
$host = gethostbyaddr($ip); $port = 993;
 
 
// Next step
$mailbox = "{{$host}:{$port}/imap/ssl}INBOX";
$user = "[email protected]";
$pass = "TheRightPassword";
 
 
echo "<pre>";
 
// PHP / IMAP info
echo "PHP version: " . phpversion() . "\n";
if (function_exists('imap_open')) {
    echo "IMAP extension: enabled\n";
} else {
    echo "IMAP extension: NOT enabled\n";
    exit;
}
 
// Test connection
echo "Trying imap_open() on $mailbox ...\n";
$mbox = @imap_open($mailbox, $user, $pass);
 
if ($mbox) {
    echo "✅ Connection successful!\n";
    imap_close($mbox);
} else {
    echo "❌ Connection failed.\n";
    echo "Errors:\n";
    print_r(imap_errors());
    echo "Alerts:\n";
    print_r(imap_alerts());
}
 
// Extra openssl check
echo "\nOpenSSL version: " . OPENSSL_VERSION_TEXT . "\n";
echo "Stream context test:\n";
 
$context = stream_context_create([
    'ssl' => [
        'capture_peer_cert' => true,
        'capture_peer_cert_chain' => true,
        'verify_peer' => true,
        'verify_peer_name' => true,
        'allow_self_signed' => false,
    ]
]);
 
$client = @stream_socket_client("ssl://{$host}:{$port}", $errno, $errstr, 10, STREAM_CLIENT_CONNECT, $context);
if ($client) {
    $params = stream_context_get_params($client);
    $cert = $params['options']['ssl']['peer_certificate'];
    $certInfo = openssl_x509_parse($cert);
    echo "Server CN: " . $certInfo['subject']['CN'] . "\n";
    echo "Valid until: " . date('Y-m-d H:i:s', $certInfo['validTo_time_t']) . "\n";
} else {
    echo "Failed to fetch cert: $errstr ($errno)\n";
}
 
echo "</pre>";
[CODE]
 
I see one thing that is probably your issue:

$domain='mail.usersdomain.nl;
$ip=gethostbyname$domain); -> This gets the IP address of the mail server
$host=gethostbyaddr($ip); -> This gets the default domain associated with that IP address


When SNI is enabled on a shared server like a DA is, it uses the domain name used to make the connection to locate the correct certificate.

So, for example if mail.usersdomain.nl was one of the domains at the IP address 9.9.9.9, but the main domain name for the 9.9.9.9 server is abc.hostingco.com, then:
1) your script will connect as the IP mail.usersdomain.nl and receive the ip 9.9.9.9
2) it will resolve the IP address and it reverse DNS for that IP will return abc.hostingco.com
3) the connection to the mail server will use the hostname abc.hostingco.com so it will return the certificate for abc.hostingco.com

Don't use $host in your script, just use the $domain when you make the call to imap_open();
 
Thank you for looking into it and I understand why you mention this, but the issue you describe is just the fix to make it work. When I connect to $domain (mail.usersdomain.nl), then the problems begin :confused:

The results will be like this:

Trying imap_open() on {mail.usersdomain.nl:993/imap/ssl}INBOX ...
❌ Connection failed.
Errors:
Array
(
[0] => Certificate failure for mail.usersdomain.nl: Hostname mismatch: /CN=server05.theserver.eu
)
 
do you have sni enabled on your server? That is what controls selecting the correct certificate.
grep sni /usr/local/directadmin/conf/directadmin.conf
enable_ssl_sni=1
mail_sni=1
 
Also, that assumes that your usesrsdomain.nl has a certificate generated as well. If you don't have sni enabled already, you can update your directadmin.conf and regenerate your config files.
da build rewrite_confs
 
With imap there is another issue, it's not related to SNI because that should be enabled already if all is well.
You shouldn't need that script normally, it can be done automatically. I've struggled a bit with the same issue some time ago.

Just add imap to this line in your directadmin.conf like this:
letsencrypt_list=www:mail:ftp:pop:smtp:imap

Don't forget to restart DA and probably need to do the da rewrite_confs command too.

Additionally if you could make a custom dns_a.conf and dns_aaaa.conf and add IMAP in there too so imap will be added automatically every time yuo create a new domain:
Code:
|*if IS_IPV6!="yes"|
|DOMAIN|.=|IP|
mail=|IP|
pop=|IP|
imap=|IP|
smtp=|IP|
www=|IP|
ftp=|IP|
|*endif|
I presume you know how to copy these templates to the correct custom directory and fix it, if not let me know I will post the complete procedure.

Unless you have that already ofcourse.
 
Also, that assumes that your usesrsdomain.nl has a certificate generated as well. If you don't have sni enabled already, you can update your directadmin.conf and regenerate your config files.
da build rewrite_confs

Yes, it is enabled and for regular mail it seems to work fine.
To be certain I checked directadmin.conf AND I did a rewrite. No luck there.


With imap there is another issue, it's not related to SNI because that should be enabled already if all is well.
You shouldn't need that script normally, it can be done automatically. I've struggled a bit with the same issue some time ago.

Just add imap to this line in your directadmin.conf like this:
letsencrypt_list=www:mail:ftp:pop:smtp:imap

Don't forget to restart DA and probably need to do the da rewrite_confs command too.

Additionally if you could make a custom dns_a.conf and dns_aaaa.conf and add IMAP in there too so imap will be added automatically every time yuo create a new domain:
Code:
|*if IS_IPV6!="yes"|
|DOMAIN|.=|IP|
mail=|IP|
pop=|IP|
imap=|IP|
smtp=|IP|
www=|IP|
ftp=|IP|
|*endif|
I presume you know how to copy these templates to the correct custom directory and fix it, if not let me know I will post the complete procedure.

Unless you have that already ofcourse.

I tried all that too. Unfortunately still no luck :rolleyes:
Although I doubt the last part has something to do with it; the IP is resolved correctly because I use mail.userdomain.nl and not imap.userdomain.nl
;)
 
Back
Top