Open ghost opened 1 year ago
According to https://bugs.php.net/bug.php?id=73558 there seem to be issues with the underlying OpenLDAP library where one needs to set the CACERTDIR const needs to be set.
Also the putenv makes sure that the variables are set for the underlying OpenLDAP lib regardless of what you set in PHP.
As you are setting the options AFTER creating the connection via ldap_connect
the CA options can not be used any more for the connection.
Have you tried setting them before the ldap_connect using null
as connection parameter?
According to https://bugs.php.net/bug.php?id=73558 there seem to be issues with the underlying OpenLDAP library where one needs to set the CACERTDIR const needs to be set.
But set to what? I tried ''
, null
, /tmp
and /etc/pki/tls/certs
(Fedora), nothing works.
Also the putenv makes sure that the variables are set for the underlying OpenLDAP lib regardless of what you set in PHP.
Exactly, that is why putenv
works.
As you are setting the options AFTER creating the connection via ldap_connect the CA options can not be used any more for the connection.
How would one do that? Also here you can read this: Note: This function does not open a connection. Also, only the ldap_bind()
call errors as at that point an actually connection is established.
Have you tried setting them before the ldap_connect using null as connection parameter?
You mean using ldap_connect(null)
, then setting the options including LDAP_OPT_HOST_NAME
? How would one then specify that LDAPS should be used, and how to configure the port? The LDAP_OPT_URI
(0x5006
) option is not (yet) exposed in PHP which would take URIs, but it is documented in ldap_set_option(3)
, but also this does not work:
$ldapResource = ldap_connect();
$ldapOptions = [
0x5006 => $ldapUri,
LDAP_OPT_PROTOCOL_VERSION => 3,
LDAP_OPT_REFERRALS => 0,
LDAP_OPT_X_TLS_CACERTDIR => '/etc/pki/tls/certs',
LDAP_OPT_X_TLS_CACERTFILE => $caFile,
LDAP_OPT_X_TLS_CERTFILE => $certFile,
LDAP_OPT_X_TLS_KEYFILE => $keyFile,
];
But set to what? I tried '', null, /tmp and /etc/pki/tls/certs (Fedora), nothing works.
It needs to be set to the folder where your custom certificates are located. In the script above that would be __DIR__
How would one do that? Also here you can read this: Note: This function does not open a connection. Also, only the ldap_bind() call errors as at that point an actually connection is established.
The connection itself is not opened. But the main information to create the connection is initialized at that point and associated with the returned resource/object handle. So whenever you use the resource/object returned by ldap_connect
those informations are used. And at that point there are no informations associated regarding the TLS cert.
You mean using ldap_connect(null), then setting the options including LDAP_OPT_HOST_NAME?
I was more thinking along these lines:
<?php
// Test case for PHP bug: https://bugs.php.net/bug.php?id=73558
//
// The two commands below work totally fine:
//
// OpenSSL:
// $ openssl s_client -verifyCAfile ca.crt -cert ldap-client.crt -key ldap-client.key -connect ldap.home.arpa:636
//
// ldapsearch:
// $ LDAPTLS_CACERT=ca.crt LDAPTLS_CERT=ldap-client.crt LDAPTLS_KEY=ldap-client.key ldapwhoami -H ldaps://ldap.home.arpa -x
// anonymous
$ldapUri = 'ldaps://ldap.home.arpa';
$caFile = __DIR__.'/ca.crt';
$certFile = __DIR__.'/ldap-client.crt';
$keyFile = __DIR__.'/ldap-client.key';
//putenv(sprintf('LDAPTLS_CACERT=%s', $caFile));
//putenv(sprintf('LDAPTLS_CERT=%s', $certFile));
//putenv(sprintf('LDAPTLS_KEY=%s', $keyFile));
$ldapTlsOptions = [
LDAP_OPT_X_TLS_CACERTDIR => __DIR__,
LDAP_OPT_X_TLS_CACERTFILE => $caFile,
LDAP_OPT_X_TLS_CERTFILE => $certFile,
LDAP_OPT_X_TLS_KEYFILE => $keyFile,
];
foreach($ldapTlsOptions as $k => $v) {
ldap_set_option(null, $k, $v);
}
$ldapResource = ldap_connect($ldapUri);
$ldapOptions = [
LDAP_OPT_PROTOCOL_VERSION => 3,
LDAP_OPT_REFERRALS => 0,
];
foreach($ldapOptions as $k => $v) {
ldap_set_option($ldapResource, $k, $v);
}
if(false === ldap_bind($ldapResource)) {
ldap_get_option($ldapResource, LDAP_OPT_DIAGNOSTIC_MESSAGE, $errMsg);
echo sprintf(
"%s (%d): %s\n",
ldap_error($ldapResource),
ldap_errno($ldapResource),
$errMsg
);
}
var_dump(
ldap_exop_whoami($ldapResource)
);
See the explanation unter ldap
of ldap_set_option for more info.
This assumes that you actually have the TLS certificates and keys at the appropriate locations.
I managed to get it working!
It needs to be set to the folder where your custom certificates are located. In the script above that would be DIR
This turns out not to be necessary (for me).
See the explanation unter ldap of ldap_set_option for more info.
I missed that the connection in ldap_set_option
can be null
, although not sure it would have occurred to me to use that, perhaps eventually!
Full working version:
<?php
$ldapUri = 'ldaps://ldap.home.arpa:636';
$caFile = __DIR__.'/ca.crt';
$certFile = __DIR__.'/ldap-client.crt';
$keyFile = __DIR__.'/ldap-client.key';
$ldapOptions = [
LDAP_OPT_X_TLS_CACERTFILE => $caFile,
LDAP_OPT_X_TLS_CERTFILE => $certFile,
LDAP_OPT_X_TLS_KEYFILE => $keyFile,
LDAP_OPT_PROTOCOL_VERSION => 3,
LDAP_OPT_REFERRALS => 0,
];
foreach($ldapOptions as $k => $v) {
ldap_set_option(null, $k, $v);
}
$ldapResource = ldap_connect($ldapUri);
if(false === ldap_bind($ldapResource)) {
ldap_get_option($ldapResource, LDAP_OPT_DIAGNOSTIC_MESSAGE, $errMsg);
echo sprintf(
"%s (%d): %s\n",
ldap_error($ldapResource),
ldap_errno($ldapResource),
$errMsg
);
}
var_dump(
ldap_exop_whoami($ldapResource)
);
Should this be documented somewhere that it works like this? It is not obvious to have 'global' options that are needed for some, but not others.
Thanks for the help! :1st_place_medal:
Edit: I notice this comment now that writes something similar, not 100% identical but close enough: https://www.php.net/manual/en/function.ldap-set-option.php#124602
Description
The following code:
Resulted in this output:
But I expected this output instead (anonymous bind):
Workaround
When you enable the three
putenv
lines, things start working, but this probably not how it should be :)Additional details
It seems the options
LDAP_OPT_X_TLS_CACERTFILE
,LDAP_OPT_X_TLS_CERTFILE
andLDAP_OPT_X_TLS_KEYFILE
are somehow ignored.We tested this on Fedora 38 (
PHP 8.2.9 (cli) (built: Aug 3 2023 11:39:08) (NTS gcc x86_64)
):And on Debian 12 (
PHP 8.2.7 (cli) (built: Jun 9 2023 19:37:27) (NTS)
):See also: https://bugs.php.net/bug.php?id=73558
Test Setup without LDAP server
If you do not have an LDAP server, you can also use
openssl
to create a test server that results in the exact same error message in the PHP script as the problem is in the TLS setup, not the actual LDAP connection:All you need is the client cert/key, server cert/key and CA and connect to port 4433 (the default of
s_server
).PHP Version
PHP 8.2.9 / 8.2.7
Operating System
Fedora 38 / Debian 12