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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
171 changes: 93 additions & 78 deletions ext/ldap/ldap.c
Original file line number Diff line number Diff line change
Expand Up @@ -2194,31 +2194,33 @@ PHP_FUNCTION(ldap_dn2ufn)
static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
{
zval *serverctrls = NULL;
zval *link, *entry, *value, *ivalue;
zval *link;
ldap_linkdata *ld;
char *dn;
HashTable *attributes_ht;
LDAPMod **ldap_mods;
LDAPControl **lserverctrls = NULL;
ldap_resultdata *result;
LDAPMessage *ldap_res;
int i, j, num_attribs, num_values, msgid;
size_t dn_len;
int *num_berval;
zend_string *attribute;
zend_ulong index;
int is_full_add=0; /* flag for full add operation so ldap_mod_add can be put back into oper, gerrit THomson */

if (zend_parse_parameters(ZEND_NUM_ARGS(), "Opa/|a!", &link, ldap_link_ce, &dn, &dn_len, &entry, &serverctrls) != SUCCESS) {
if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oph/|a!", &link, ldap_link_ce, &dn, &dn_len, &attributes_ht, &serverctrls) != SUCCESS) {
RETURN_THROWS();
}

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

num_attribs = zend_hash_num_elements(Z_ARRVAL_P(entry));
uint32_t num_attribs = zend_hash_num_elements(attributes_ht);
if (num_attribs == 0) {
zend_argument_must_not_be_empty_error(3);
RETURN_THROWS();
}

ldap_mods = safe_emalloc((num_attribs+1), sizeof(LDAPMod *), 0);
num_berval = safe_emalloc(num_attribs, sizeof(int), 0);
zend_hash_internal_pointer_reset(Z_ARRVAL_P(entry));
/* Zero out the list */
memset(ldap_mods, 0, sizeof(LDAPMod *) * (num_attribs+1));

/* added by gerrit thomson to fix ldap_add using ldap_mod_add */
if (oper == PHP_LD_FULL_ADD) {
Expand All @@ -2227,71 +2229,80 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
}
/* end additional , gerrit thomson */

for (i = 0; i < num_attribs; i++) {
ldap_mods[i] = emalloc(sizeof(LDAPMod));
ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
ldap_mods[i]->mod_type = NULL;

if (zend_hash_get_current_key(Z_ARRVAL_P(entry), &attribute, &index) == HASH_KEY_IS_STRING) {
ldap_mods[i]->mod_type = estrndup(ZSTR_VAL(attribute), ZSTR_LEN(attribute));
} else {
php_error_docref(NULL, E_WARNING, "Unknown attribute in the data");
const zend_string *attribute = NULL;
zval *attribute_values = NULL;
unsigned int attribute_index = 0;
ZEND_HASH_FOREACH_STR_KEY_VAL(attributes_ht, attribute, attribute_values) {
if (attribute == NULL) {
zend_argument_value_error(3, "must be an associative array of attribute => values");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plural use is a bit strange here. I would write attribute => value or attributes => values but not mix them. WDYT?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is for a single attribute there might be multiple values attached to it

RETVAL_FALSE;
num_berval[i] = 0;
num_attribs = i + 1;
ldap_mods[i]->mod_bvalues = NULL;
goto cleanup;
}

value = zend_hash_get_current_data(Z_ARRVAL_P(entry));

ZVAL_DEREF(value);
if (Z_TYPE_P(value) != IS_ARRAY) {
num_values = 1;
} else {
SEPARATE_ARRAY(value);
num_values = zend_hash_num_elements(Z_ARRVAL_P(value));
if (ZSTR_LEN(attribute) == 0) {
zend_argument_value_error(3, "key must not be empty");
RETVAL_FALSE;
goto cleanup;
}
if (zend_str_has_nul_byte(attribute)) {
zend_argument_value_error(3, "key must not contain any null bytes");
RETVAL_FALSE;
goto cleanup;
}

num_berval[i] = num_values;
ldap_mods[i]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
ldap_mods[attribute_index] = emalloc(sizeof(LDAPMod));
ldap_mods[attribute_index]->mod_op = oper | LDAP_MOD_BVALUES;
ldap_mods[attribute_index]->mod_type = estrndup(ZSTR_VAL(attribute), ZSTR_LEN(attribute));
ldap_mods[attribute_index]->mod_bvalues = NULL;

/* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
if ((num_values == 1) && (Z_TYPE_P(value) != IS_ARRAY)) {
convert_to_string(value);
ZVAL_DEREF(attribute_values);
/* If the attribute takes a single value it can be passed directly instead of as a list with one element */
/* allow for arrays with one element, no allowance for arrays with none but probably not required, gerrit thomson. */
if (Z_TYPE_P(attribute_values) != IS_ARRAY) {
convert_to_string(attribute_values);
if (EG(exception)) {
RETVAL_FALSE;
num_berval[i] = 0;
num_attribs = i + 1;
goto cleanup;
}
ldap_mods[i]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
ldap_mods[i]->mod_bvalues[0]->bv_val = Z_STRVAL_P(value);
ldap_mods[i]->mod_bvalues[0]->bv_len = Z_STRLEN_P(value);
ldap_mods[attribute_index]->mod_bvalues = safe_emalloc(2, sizeof(struct berval *), 0);
ldap_mods[attribute_index]->mod_bvalues[0] = (struct berval *) emalloc (sizeof(struct berval));
ldap_mods[attribute_index]->mod_bvalues[0]->bv_val = Z_STRVAL_P(attribute_values);
ldap_mods[attribute_index]->mod_bvalues[0]->bv_len = Z_STRLEN_P(attribute_values);
ldap_mods[attribute_index]->mod_bvalues[1] = NULL;
} else {
for (j = 0; j < num_values; j++) {
if ((ivalue = zend_hash_index_find(Z_ARRVAL_P(value), j)) == NULL) {
zend_argument_value_error(3, "must contain arrays with consecutive integer indices starting from 0");
num_berval[i] = j;
num_attribs = i + 1;
RETVAL_FALSE;
goto cleanup;
}
convert_to_string(ivalue);
SEPARATE_ARRAY(attribute_values);
uint32_t num_values = zend_hash_num_elements(Z_ARRVAL_P(attribute_values));
if (num_values == 0) {
zend_argument_value_error(3, "list of attribute values must not be empty");
RETVAL_FALSE;
goto cleanup;
}
if (!zend_array_is_list(Z_ARRVAL_P(attribute_values))) {
zend_argument_value_error(3, "must be a list of attribute values");
RETVAL_FALSE;
goto cleanup;
}

ldap_mods[attribute_index]->mod_bvalues = safe_emalloc((num_values + 1), sizeof(struct berval *), 0);
/* Zero out the list */
memset(ldap_mods[attribute_index]->mod_bvalues, 0, sizeof(struct berval *) * (num_values+1));

zend_ulong attribute_value_index = 0;
zval *attribute_value = NULL;
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(attribute_values), attribute_value_index, attribute_value) {
convert_to_string(attribute_value);
if (EG(exception)) {
num_berval[i] = j;
num_attribs = i + 1;
RETVAL_FALSE;
goto cleanup;
}
ldap_mods[i]->mod_bvalues[j] = (struct berval *) emalloc (sizeof(struct berval));
ldap_mods[i]->mod_bvalues[j]->bv_val = Z_STRVAL_P(ivalue);
ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(ivalue);
}
ldap_mods[attribute_index]->mod_bvalues[attribute_value_index] = (struct berval *) emalloc (sizeof(struct berval));
ldap_mods[attribute_index]->mod_bvalues[attribute_value_index]->bv_val = Z_STRVAL_P(attribute_value);
ldap_mods[attribute_index]->mod_bvalues[attribute_value_index]->bv_len = Z_STRLEN_P(attribute_value);
} ZEND_HASH_FOREACH_END();
ldap_mods[attribute_index]->mod_bvalues[num_values] = NULL;
}
ldap_mods[i]->mod_bvalues[num_values] = NULL;
zend_hash_move_forward(Z_ARRVAL_P(entry));
}

attribute_index++;
} ZEND_HASH_FOREACH_END();
ldap_mods[num_attribs] = NULL;

if (serverctrls) {
Expand All @@ -2302,19 +2313,21 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
}
}

/* check flag to see if do_mod was called to perform full add , gerrit thomson */
/* check flag to see if do_mod was called to perform full add , gerrit thomson */
int ldap_status_code = LDAP_SUCCESS;
int msgid;
if (is_full_add == 1) {
if (ext) {
i = ldap_add_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
ldap_status_code = ldap_add_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
} else {
i = ldap_add_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
ldap_status_code = ldap_add_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
}
if (i != LDAP_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(i));
if (ldap_status_code != LDAP_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Add: %s", ldap_err2string(ldap_status_code));
RETVAL_FALSE;
} else if (ext) {
i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
if (i == -1) {
ldap_status_code = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
if (ldap_status_code == -1) {
php_error_docref(NULL, E_WARNING, "Add operation failed");
RETVAL_FALSE;
goto cleanup;
Expand All @@ -2327,16 +2340,16 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
} else RETVAL_TRUE;
} else {
if (ext) {
i = ldap_modify_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
ldap_status_code = ldap_modify_ext(ld->link, dn, ldap_mods, lserverctrls, NULL, &msgid);
} else {
i = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
ldap_status_code = ldap_modify_ext_s(ld->link, dn, ldap_mods, lserverctrls, NULL);
}
if (i != LDAP_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(i));
if (ldap_status_code != LDAP_SUCCESS) {
php_error_docref(NULL, E_WARNING, "Modify: %s", ldap_err2string(ldap_status_code));
RETVAL_FALSE;
} else if (ext) {
i = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
if (i == -1) {
ldap_status_code = ldap_result(ld->link, msgid, 1 /* LDAP_MSG_ALL */, NULL, &ldap_res);
if (ldap_status_code == -1) {
php_error_docref(NULL, E_WARNING, "Modify operation failed");
RETVAL_FALSE;
goto cleanup;
Expand All @@ -2350,15 +2363,17 @@ static void php_ldap_do_modify(INTERNAL_FUNCTION_PARAMETERS, int oper, int ext)
}

cleanup:
for (i = 0; i < num_attribs; i++) {
efree(ldap_mods[i]->mod_type);
for (j = 0; j < num_berval[i]; j++) {
efree(ldap_mods[i]->mod_bvalues[j]);
for (LDAPMod **ptr = ldap_mods; *ptr != NULL; ptr++) {
LDAPMod *mod = *ptr;
efree(mod->mod_type);
if (mod->mod_bvalues != NULL) {
for (struct berval **bval_ptr = mod->mod_bvalues; *bval_ptr != NULL; bval_ptr++) {
efree(*bval_ptr);
}
efree(mod->mod_bvalues);
}
efree(ldap_mods[i]->mod_bvalues);
efree(ldap_mods[i]);
efree(mod);
}
efree(num_berval);
efree(ldap_mods);

if (lserverctrls) {
Expand Down
5 changes: 2 additions & 3 deletions ext/ldap/tests/gh16136.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,5 @@ try {
}

?>
--EXPECTF--
Warning: ldap_add(): Unknown attribute in the data in %s on line %d
bool(false)
--EXPECT--
ValueError: ldap_add(): Argument #3 ($entry) must be an associative array of attribute => values
95 changes: 35 additions & 60 deletions ext/ldap/tests/ldap_add_error.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,73 +13,55 @@ require "connect.inc";

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

var_dump(ldap_add($link, "$base", array()));

// Invalid DN
var_dump(
ldap_add($link, "weirdAttribute=val", array(
"weirdAttribute" => "val",
)),
ldap_add(
$link,
"weirdAttribute=val",
["weirdAttribute" => "val"],
),
ldap_error($link),
ldap_errno($link)
);

// Duplicate entry
for ($i = 0; $i < 2; $i++)
for ($i = 0; $i < 2; $i++) {
var_dump(
ldap_add($link, "dc=my-domain,$base", array(
"objectClass" => array(
"top",
"dcObject",
"organization"),
"dc" => "my-domain",
"o" => "my-domain",
))
ldap_add(
$link,
"dc=my-domain,$base",
[
"objectClass" => [
"top",
"dcObject",
"organization",
],
"dc" => "my-domain",
"o" => "my-domain",
],
)
);
var_dump(ldap_error($link), ldap_errno($link));

// Wrong array indexes
try {
ldap_add($link, "dc=my-domain2,dc=com", array(
"objectClass" => array(
0 => "top",
2 => "dcObject",
5 => "organization"),
"dc" => "my-domain",
"o" => "my-domain",
));
/* Is this correct behaviour to still have "Already exists" as error/errno?
,
ldap_error($link),
ldap_errno($link)
*/
} catch (ValueError $exception) {
echo $exception->getMessage() . "\n";
}
var_dump(ldap_error($link), ldap_errno($link));

// Invalid attribute
var_dump(
ldap_add($link, "$base", array(
"objectClass" => array(
"top",
"dcObject",
"organization"),
"dc" => "my-domain",
"o" => "my-domain",
"weirdAttr" => "weirdVal",
)),
ldap_error($link),
ldap_errno($link)
);

var_dump(
ldap_add($link, "$base", array(array( "Oops"
)))
/* Is this correct behaviour to still have "Undefined attribute type" as error/errno?
,
ldap_add(
$link,
"dc=my-domain,$base",
[
"objectClass" => [
"top",
"dcObject",
"organization",
],
"dc" => "my-domain",
"o" => "my-domain",
"weirdAttr" => "weirdVal",
],
),
ldap_error($link),
ldap_errno($link)
*/
ldap_errno($link),
);
?>
--CLEAN--
Expand All @@ -91,9 +73,6 @@ $link = ldap_connect_and_bind($uri, $user, $passwd, $protocol_version);
ldap_delete($link, "dc=my-domain,$base");
?>
--EXPECTF--
Warning: ldap_add(): Add: Protocol error in %s on line %d
bool(false)

Warning: ldap_add(): Add: Invalid DN syntax in %s on line %d
bool(false)
string(17) "Invalid DN syntax"
Expand All @@ -104,12 +83,8 @@ Warning: ldap_add(): Add: Already exists in %s on line %d
bool(false)
string(14) "Already exists"
int(68)
ldap_add(): Argument #3 ($entry) must contain arrays with consecutive integer indices starting from 0

Warning: ldap_add(): Add: Undefined attribute type in %s on line %d
bool(false)
string(24) "Undefined attribute type"
int(17)

Warning: ldap_add(): Unknown attribute in the data in %s on line %d
bool(false)
Loading