Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 120 additions & 21 deletions ext/ldap/ldap.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,6 @@ static zend_object *ldap_link_create_object(zend_class_entry *class_type) {
return &intern->std;
}

static zend_function *ldap_link_get_constructor(zend_object *object) {
zend_throw_error(NULL, "Cannot directly construct LDAP\\Connection, use ldap_connect() instead");
return NULL;
}

static void ldap_link_free(ldap_linkdata *ld)
{
/* We use ldap_destroy rather than ldap_unbind here, because ldap_unbind
Expand Down Expand Up @@ -874,7 +869,6 @@ PHP_MINIT_FUNCTION(ldap)
memcpy(&ldap_link_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
ldap_link_object_handlers.offset = offsetof(ldap_linkdata, std);
ldap_link_object_handlers.free_obj = ldap_link_free_obj;
ldap_link_object_handlers.get_constructor = ldap_link_get_constructor;
ldap_link_object_handlers.clone_obj = NULL;
ldap_link_object_handlers.compare = zend_objects_not_comparable;

Expand Down Expand Up @@ -954,6 +948,60 @@ PHP_MINFO_FUNCTION(ldap)
}
/* }}} */

/* {{{ Creates new LDAP\Connection object */
PHP_METHOD(LDAP_Connection, __construct)
{
char *url = NULL;
size_t urllen = 0;
ldap_linkdata *ld;
LDAP *ldap = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &url, &urllen) != SUCCESS) {
RETURN_THROWS();
}

if (LDAPG(max_links) != -1 && LDAPG(num_links) >= LDAPG(max_links)) {
zend_throw_error(NULL, "Too many open links (" ZEND_LONG_FMT ")", LDAPG(num_links));
RETURN_THROWS();
}

{
int rc = LDAP_SUCCESS;
if (url && !ldap_is_ldap_url(url)) {
zend_argument_value_error(1, "is not a valid LDAP URI");
RETURN_THROWS();
}

#ifdef LDAP_OPT_X_TLS_NEWCTX
if (LDAPG(tls_newctx) && url && !strncmp(url, "ldaps:", 6)) {
int val = 0;

/* ensure all pending TLS options are applied in a new context */
if (ldap_set_option(NULL, LDAP_OPT_X_TLS_NEWCTX, &val) != LDAP_OPT_SUCCESS) {
zend_throw_error(NULL, "Could not create new security context");
RETURN_THROWS();
}
LDAPG(tls_newctx) = false;
}
#endif
ld = Z_LDAP_LINK_P(ZEND_THIS);
rc = ldap_initialize(&ldap, url);

if (rc != LDAP_SUCCESS) {
zend_throw_error(NULL, "Could not create session handle: %s", ldap_err2string(rc));
RETURN_THROWS();
}
}

if (ldap == NULL) {
RETURN_THROWS();
} else {
LDAPG(num_links)++;
ld->link = ldap;
}
}
/* }}} */

/* {{{ Connect to an LDAP server */
PHP_FUNCTION(ldap_connect)
{
Expand Down Expand Up @@ -1205,27 +1253,20 @@ PHP_FUNCTION(ldap_bind)
}
/* }}} */

/* {{{ Bind to LDAP directory */
PHP_FUNCTION(ldap_bind_ext)

/* {{{ php_ldap_do_bind_ext */
static void php_ldap_do_bind_ext(zval *link, char *ldap_bind_dn, size_t ldap_bind_dnlen, char* ldap_bind_pw, size_t ldap_bind_pwlen, HashTable *server_controls_ht, zval *return_value, char **exception_message)
{
zval *link;
char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
size_t ldap_bind_dnlen, ldap_bind_pwlen;
HashTable *server_controls_ht = NULL;
ldap_linkdata *ld;
LDAPControl **lserverctrls = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|p!p!h!", &link, ldap_link_ce, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) {
RETURN_THROWS();
}

ld = Z_LDAP_LINK_P(link);
VERIFY_LDAP_LINK_CONNECTED(ld);

if (server_controls_ht) {
lserverctrls = php_ldap_controls_from_array(ld->link, server_controls_ht, 4);
if (lserverctrls == NULL) {
RETVAL_FALSE;
spprintf(exception_message, 0, "Failed to parse controls");
goto cleanup;
}
}
Expand All @@ -1242,16 +1283,14 @@ PHP_FUNCTION(ldap_bind_ext)
rc = ldap_sasl_bind(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred,
lserverctrls, NULL, &msgid);
if (rc != LDAP_SUCCESS ) {
php_error_docref(NULL, E_WARNING, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc);
RETVAL_FALSE;
spprintf(exception_message, 0, "Unable to bind to server: %s (%d)", ldap_err2string(rc), rc);
goto cleanup;
}

LDAPMessage *ldap_res;
rc = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
if (rc == -1) {
php_error_docref(NULL, E_WARNING, "Bind operation failed");
RETVAL_FALSE;
spprintf(exception_message, 0, "Bind operation failed");
goto cleanup;
}

Expand All @@ -1270,6 +1309,50 @@ PHP_FUNCTION(ldap_bind_ext)
}
/* }}} */


/* {{{ Bind to LDAP directory */
PHP_METHOD(LDAP_Connection, bind)
{
char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
size_t ldap_bind_dnlen, ldap_bind_pwlen;
HashTable *server_controls_ht = NULL;
char *exception_message = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "|p!p!h!", &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) {
RETURN_THROWS();
}

php_ldap_do_bind_ext(ZEND_THIS, ldap_bind_dn, ldap_bind_dnlen, ldap_bind_pw, ldap_bind_pwlen, server_controls_ht, return_value, &exception_message);
if (exception_message) {
zend_throw_error(NULL, "%s", exception_message);
efree(exception_message);
RETURN_THROWS();
}
}
/* }}} */

/* {{{ Bind to LDAP directory */
PHP_FUNCTION(ldap_bind_ext)
{
zval *link;
char *ldap_bind_dn = NULL, *ldap_bind_pw = NULL;
size_t ldap_bind_dnlen, ldap_bind_pwlen;
HashTable *server_controls_ht = NULL;
char *exception_message = NULL;

if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|p!p!h!", &link, ldap_link_ce, &ldap_bind_dn, &ldap_bind_dnlen, &ldap_bind_pw, &ldap_bind_pwlen, &server_controls_ht) != SUCCESS) {
RETURN_THROWS();
}

php_ldap_do_bind_ext(link, ldap_bind_dn, ldap_bind_dnlen, ldap_bind_pw, ldap_bind_pwlen, server_controls_ht, return_value, &exception_message);
if (exception_message) {
php_error_docref(NULL, E_WARNING, "%s", exception_message);
efree(exception_message);
RETVAL_FALSE;
}
}
/* }}} */

#ifdef HAVE_LDAP_SASL
typedef struct {
char *mech;
Expand Down Expand Up @@ -1404,6 +1487,22 @@ PHP_FUNCTION(ldap_sasl_bind)
/* }}} */
#endif /* HAVE_LDAP_SASL */

/* {{{ Unbind from LDAP directory */
PHP_METHOD(LDAP_Connection, unbind)
{
ldap_linkdata *ld;

ZEND_PARSE_PARAMETERS_NONE();

ld = Z_LDAP_LINK_P(ZEND_THIS);
VERIFY_LDAP_LINK_CONNECTED(ld);

ldap_link_free(ld);

RETURN_TRUE;
}
/* }}} */

/* {{{ Unbind from LDAP directory */
PHP_FUNCTION(ldap_unbind)
{
Expand Down
7 changes: 7 additions & 0 deletions ext/ldap/ldap.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,13 @@ function ldap_parse_exop(LDAP\Connection $ldap, LDAP\Result $result, &$response_
*/
final class Connection
{
public function __construct(?string $uri = null);
public function bind(
?string $dn = null,
#[\SensitiveParameter] ?string $password = null,
?array $controls = null
): Result;
public function unbind(): bool;
}

/**
Expand Down
30 changes: 28 additions & 2 deletions ext/ldap/ldap_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions ext/ldap/tests/ldap_bind_ext.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,20 @@ var_dump(
$ctrls
);

// Same thing using Connection::bind
var_dump(
$result = $link->bind($user, $passwd),
ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls),
$errcode,
$errmsg,
$ctrls,
$result = $link->bind($user, $passwd, [['oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST]]),
ldap_parse_result($link, $result, $errcode, $matcheddn, $errmsg, $referrals, $ctrls),
$errcode,
$errmsg,
$ctrls
);

/* Failures */
var_dump(
$result = ldap_bind_ext($link, $user, "wrongPassword", [['oid' => LDAP_CONTROL_PASSWORDPOLICYREQUEST]]),
Expand Down Expand Up @@ -62,6 +76,20 @@ array(0) {
object(LDAP\Result)#%d (0) {
}
bool(true)
int(0)
string(0) ""
array(0) {
}
object(LDAP\Result)#%d (0) {
}
bool(true)
int(0)
string(0) ""
array(0) {
}
object(LDAP\Result)#%d (0) {
}
bool(true)
int(49)
string(0) ""
array(0) {
Expand Down
36 changes: 29 additions & 7 deletions ext/ldap/tests/ldap_constructor.phpt
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
--TEST--
Attempt to instantiate an LDAP\Connection directly
new \LDAP\Connection() - Variation
--EXTENSIONS--
ldap
--FILE--
Comment thread
MCMic marked this conversation as resolved.
<?php
$constructorCalls = [
// no hostname, no port
[],
// URI
["ldap://hostname:389"],
// ldaps URI
["ldaps://hostname:689"],
// URI no port
["ldap://hostname"],
];

try {
new LDAP\Connection();
} catch (Error $ex) {
echo $ex::class, ': ', $ex->getMessage(), "\n";
foreach ($constructorCalls as $parameters) {
$link = new \LDAP\Connection(...$parameters);
var_dump($link);
ldap_get_option($link, LDAP_OPT_HOST_NAME, $hostname);
var_dump($hostname);
}
?>
--EXPECT--
Error: Cannot directly construct LDAP\Connection, use ldap_connect() instead
--EXPECTF--
object(LDAP\Connection)#%d (0) {
}
string(%d) "%s:%d"
object(LDAP\Connection)#%d (0) {
}
string(12) "hostname:389"
object(LDAP\Connection)#%d (0) {
}
string(12) "hostname:689"
object(LDAP\Connection)#%d (0) {
}
string(12) "hostname:389"
5 changes: 5 additions & 0 deletions ext/ldap/tests/ldap_unbind_basic.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ require "connect.inc";
$link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version);

var_dump(ldap_unbind($link));

$link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version);

var_dump($link->unbind());
?>
--EXPECT--
bool(true)
bool(true)
Loading