php / php/php-src

LDAPS client certificate authentication does not work

Open
#12,081 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Extension: ldap Status: Needs Triage
Dominant language
C
Stars
40.4k
Forks
8.1k
Avg merge
2d 13h
Merged PRs (30d)
96

Description

Description

The following code:

<?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));

$ldapResource = ldap_connect($ldapUri);
$ldapOptions = [
    LDAP_OPT_PROTOCOL_VERSION => 3,
    LDAP_OPT_REFERRALS => 0,
    LDAP_OPT_X_TLS_CACERTFILE => $caFile,
    LDAP_OPT_X_TLS_CERTFILE => $certFile,
    LDAP_OPT_X_TLS_KEYFILE => $keyFile,
];

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)
);

Resulted in this output:

PHP Warning:  ldap_bind(): Unable to bind to server: Can't contact LDAP server in /home/fkooman/ldap.home.arpa/ldap_test.php on line 36
Can't contact LDAP server (-1): error:0A000086:SSL routines::certificate verify failed (self-signed certificate in certificate chain)
PHP Warning:  ldap_exop_whoami(): Whoami extended operation failed: Can't contact LDAP server (-1) in /home/fkooman/ldap.home.arpa/ldap_test.php on line 47
bool(false)

But I expected this output instead (anonymous bind):

string(0) ""

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 and LDAP_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)):

LDAP Support => enabled
Total Links => 0/unlimited
API Version => 3001
Vendor Name => OpenLDAP
Vendor Version => 20604
SASL Support => Enabled

Directive => Local Value => Master Value
ldap.max_links => Unlimited => Unlimited

And on Debian 12 (PHP 8.2.7 (cli) (built: Jun 9 2023 19:37:27) (NTS)):

LDAP Support => enabled
Total Links => 0/unlimited
API Version => 3001
Vendor Name => OpenLDAP
Vendor Version => 20513
SASL Support => Enabled

Directive => Local Value => Master Value
ldap.max_links => Unlimited => Unlimited

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:

$ openssl s_server -CAfile ca.crt -cert ldap.home.arpa.crt -key ldap.home.arpa.key

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided PHP script, especially ldap_set_option(), ldap_bind(), and the LDAP_OPT_X_TLS_* options, then reproduce the TLS setup with the shown openssl s_server command. Compare this path with the working environment-variable configuration. Done means the certificate options are honored and the bind plus ldap_exop_whoami() succeeds without the certificate verification error.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
authentication, networking, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.