Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:19927 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 40720 invoked by uid 1010); 8 Nov 2005 22:30:35 -0000 Delivered-To: ezmlm-scan-internals@lists.php.net Delivered-To: ezmlm-internals@lists.php.net Received: (qmail 40705 invoked from network); 8 Nov 2005 22:30:35 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 8 Nov 2005 22:30:35 -0000 Received: from ([127.0.0.1:9501]) by pb1.pair.com (ecelerity 2.0 beta r(6323M)) with ECSTREAM id 97/C2-09491-B0721734 for ; Tue, 08 Nov 2005 17:30:35 -0500 X-Host-Fingerprint: 194.244.21.114 ns1.sys-net.it Linux 2.4 in cluster Received: from ([194.244.21.114:35871] helo=mail.sys-net.it) by pb1.pair.com (ecelerity 2.0 beta r(6323M)) with SMTP id DD/3B-09491-B6711734 for ; Tue, 08 Nov 2005 16:23:55 -0500 Received: from 192.168.1.199 (host40-89.pool8172.interbusiness.it [81.72.89.40]) (authenticated bits=0) by mail.sys-net.it (8.13.3/8.13.3) with ESMTP id jA8LN3tO032352 (version=TLSv1/SSLv3 cipher=RC4-MD5 bits=128 verify=NOT); Tue, 8 Nov 2005 22:23:03 +0100 To: venaas@php.net Cc: internals@lists.php.net X-Priority: 3 (Normal) Content-Type: multipart/mixed; boundary="=-Dk8tZxoP/ODrUWa1Eymz" Date: Tue, 08 Nov 2005 22:28:35 +0100 Message-ID: <1131485315.3327.15.camel@ando> Mime-Version: 1.0 X-Mailer: Evolution 2.0.2 (2.0.2-22) X-Greylist: Sender succeeded SMTP AUTH authentication, not delayed by milter-greylist-2.0.1 (mail.sys-net.it [194.244.21.114]); Tue, 08 Nov 2005 22:23:04 +0100 (CET) X-SysNet-MailScanner-Information: postmaster@sys-net.it X-SysNet-MailScanner: X-MailScanner-From: ando@sys-net.it Subject: LDAP controls in response From: ando@sys-net.it (Pierangelo Masarati) --=-Dk8tZxoP/ODrUWa1Eymz Content-Type: text/plain Content-Transfer-Encoding: 7bit Stig, I need to use LDAP controls in PHP, including control response from server to client, so I patched the 5.0.5/HEAD code to add an extra arg to ldap_parse_result() and ldap_parse_reference(). I'd need this patch in production at some point, that's why it would be great to see it merged into the mainstream; however, I'm little familiar with the internals of PHP, so please excuse me if I missed anything in coding and in the submission procedure. The very same patch applies to HEAD and to 5.0.5. If you don't mind, I'm posting it to you right now, pending further work. It also includes some extra work I did to eliminate some warnings from OpenLDAP 2.3, but it works fine with 2.2 as well. All changes specific to OpenLDAP are protected behind the LDAP_API_FEATURE_X_OPENLDAP macro. Unfortunately I have no chances to check it with other APIs right now. I'm also including a trivial script I tested with ./sapi/cli/php against the server resulting from test003 of OpenLDAP 2.3; to reproduce, just cd openldap/tests/ ../run -k test003 cd php ../sapi/cli/php pagedResults.php If the code looks fine, I plan to document the new API, which is completely backwards compatible, and add some facilities to encode/decode the control values; hopefully, I won't have to get to writing a complete wrapper around liblber! My idea is to provide dumb helpers that encode well-known controls through a trivial API; e.g., for pagedResults: ldap_control_paged_results($handler, $size, $iscritical[, $cookie]) ldap_control_paged_results_resp($result, &$cookie[, &$iscritical[, & $estimate]]) for passwdPolicy: ldap_control_passwd_policy($handler[, $iscritical]) ldap_control_passwd_policy_resp($result, &$warning, &$error) Please let me know if you need me to do anything else. Sincerely, p. -- Pierangelo Masarati mailto:pierangelo.masarati@sys-net.it Ing. Pierangelo Masarati Responsabile Open Solution SysNet s.n.c. Via Dossi, 8 - 27100 Pavia - ITALIA http://www.sys-net.it ------------------------------------------ Office: +39.02.23998309 Mobile: +39.333.4963172 Email: pierangelo.masarati@sys-net.it ------------------------------------------ --=-Dk8tZxoP/ODrUWa1Eymz Content-Disposition: attachment; filename=php-ldap-result-controls.txt Content-Type: text/x-patch; name=php-ldap-result-controls.txt; charset=utf-8 Content-Transfer-Encoding: 7bit Index: ext/ldap/ldap.c =================================================================== RCS file: /repository/php-src/ext/ldap/ldap.c,v retrieving revision 1.162 diff -u -r1.162 ldap.c --- ext/ldap/ldap.c 22 Aug 2005 12:22:08 -0000 1.162 +++ ext/ldap/ldap.c 8 Nov 2005 21:04:05 -0000 @@ -191,7 +191,9 @@ { ldap_linkdata *ld = (ldap_linkdata *)rsrc->ptr; - ldap_unbind_s(ld->link); + /* ldap_unbind_s() is deprecated; + * the distinction between ldap_unbind() and ldap_unbind_s() is moot */ + ldap_unbind_ext(ld->link, NULL, NULL); #if defined(LDAP_API_FEATURE_X_OPENLDAP) && defined(HAVE_3ARG_SETREBINDPROC) if (ld->rebindproc != NULL) { zval_dtor(ld->rebindproc); @@ -215,6 +217,79 @@ efree(entry); } +static int _parse_referrals_resp(char ***lreferralsp, zval **referrals) +{ + int num_referrals = 0; + + if (*lreferralsp != NULL) { + char **refp = *lreferralsp; + + while (*refp) { + add_next_index_string(*referrals, *refp, 1); + refp++; + num_referrals++; + } +#ifdef LDAP_API_FEATURE_X_OPENLDAP + ber_memvfree((void **)*lreferralsp); +#else + ldap_value_free(*lreferralsp); +#endif + *lreferralsp = NULL; + } + + add_assoc_long(*referrals, "count", num_referrals); + + return num_referrals; +} + +static int _parse_server_controls_resp(LDAPControl ***lserverctrlsp, zval **serverctrls) +{ + int num_serverctrls = 0; + + if (*lserverctrlsp != NULL) { + int error = 0; + LDAPControl **ctrlp = *lserverctrlsp; + + while (*ctrlp) { + zval *ctrlval = NULL; + + if ( (*ctrlp)->ldctl_oid == NULL ) { + error = 1; + break; + } + + MAKE_STD_ZVAL(ctrlval); + array_init(ctrlval); + + add_assoc_string(ctrlval, "oid", (*ctrlp)->ldctl_oid, 1); + if ( (*ctrlp)->ldctl_value.bv_len ) { + add_assoc_stringl(ctrlval, "value", (*ctrlp)->ldctl_value.bv_val, (*ctrlp)->ldctl_value.bv_len, 1); + } + if ((*ctrlp)->ldctl_iscritical) { + add_assoc_bool(ctrlval, "iscritical", (*ctrlp)->ldctl_iscritical); + } + + add_next_index_zval(*serverctrls, ctrlval); + + num_serverctrls++; + ctrlp++; + } + ldap_controls_free(*lserverctrlsp); + *lserverctrlsp = NULL; + + if (error) { + /* ... */ + return -1; + } + } + + if (num_serverctrls != -1) { + add_assoc_long(*serverctrls, "count", num_serverctrls); + } + + return num_serverctrls; +} + /* {{{ PHP_INI_BEGIN */ PHP_INI_BEGIN() @@ -370,7 +445,11 @@ { char *host = NULL; int hostlen; +#ifdef LDAP_API_FEATURE_X_OPENLDAP + long port = LDAP_PORT; +#else long port = 389; /* Default port */ +#endif #ifdef HAVE_ORALDAP char *wallet, *walletpasswd; int walletlen, walletpasswdlen; @@ -406,17 +485,33 @@ ld = ecalloc(1, sizeof(ldap_linkdata)); #ifdef LDAP_API_FEATURE_X_OPENLDAP - if (host != NULL && strchr(host, '/')) { - int rc; + /* OpenLDAP provides a specific call to detect valid LDAP URIs */ + { + int rc; + char *url = host; + + if (!ldap_is_ldap_url(url)) { + int urllen = hostlen + sizeof( "ldap://:65535" ); + + if (port < 0 || port > 65535) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid port number: %ld", port); + RETURN_FALSE; + } + + url = emalloc(urllen); + snprintf( url, urllen, "ldap://%s:%ld", host ? host : "", port ? port : LDAP_PORT ); + } - rc = ldap_initialize(&ldap, host); + rc = ldap_initialize(&ldap, url); if (rc != LDAP_SUCCESS) { efree(ld); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Could not create session handle: %s", ldap_err2string(rc)); RETURN_FALSE; } - } else { - ldap = ldap_init(host, port); + + if (url != host) { + efree(url); + } } #else ldap = ldap_open(host, port); @@ -479,7 +574,21 @@ ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link); - if ((rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE)) != LDAP_SUCCESS) { +#ifdef LDAP_API_FEATURE_X_OPENLDAP + { + struct berval cred; + + /* ldap_bind_s() is deprecated; use ldap_sasl_bind_s() instead */ + cred.bv_val = ldap_bind_pw; + cred.bv_len = ldap_bind_pw ? ldap_bind_pwlen : 0; + rc = ldap_sasl_bind_s(ld->link, ldap_bind_dn, LDAP_SASL_SIMPLE, &cred, + NULL, NULL, /* no controls right now */ + NULL); /* we don't care about the server's credentials */ + } +#else + rc = ldap_bind_s(ld->link, ldap_bind_dn, ldap_bind_pw, LDAP_AUTH_SIMPLE); +#endif + if ( rc != LDAP_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to bind to server: %s", ldap_err2string(rc)); RETURN_FALSE; } else { @@ -1030,7 +1139,6 @@ BerElement *ber; char *attribute; size_t attr_len; - char **ldap_value; char *dn; if (ZEND_NUM_ARGS() != 2 || zend_get_parameters_ex(2, &link, &result) == FAILURE) { @@ -1061,16 +1169,18 @@ attribute = ldap_first_attribute(ldap, ldap_result_entry, &ber); while (attribute != NULL) { - ldap_value = ldap_get_values(ldap, ldap_result_entry, attribute); - num_values = ldap_count_values(ldap_value); + struct berval **ldap_value; + + ldap_value = ldap_get_values_len(ldap, ldap_result_entry, attribute); + num_values = ldap_count_values_len(ldap_value); MAKE_STD_ZVAL(tmp2); array_init(tmp2); add_assoc_long(tmp2, "count", num_values); for (i = 0; i < num_values; i++) { - add_index_string(tmp2, i, ldap_value[i], 1); + add_index_stringl(tmp2, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1); } - ldap_value_free(ldap_value); + ldap_value_free_len(ldap_value); attr_len = strlen(attribute); zend_hash_update(Z_ARRVAL_P(tmp1), php_strtolower(attribute, attr_len), attr_len+1, (void *) &tmp2, sizeof(zval *), NULL); @@ -1177,7 +1287,6 @@ ldap_linkdata *ld; ldap_resultentry *resultentry; char *attribute; - char **ldap_value; int i, num_values, num_attrib; BerElement *ber; @@ -1193,16 +1302,18 @@ attribute = ldap_first_attribute(ld->link, resultentry->data, &ber); while (attribute != NULL) { - ldap_value = ldap_get_values(ld->link, resultentry->data, attribute); - num_values = ldap_count_values(ldap_value); + struct berval **ldap_value; + + ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute); + num_values = ldap_count_values_len(ldap_value); MAKE_STD_ZVAL(tmp); array_init(tmp); add_assoc_long(tmp, "count", num_values); for (i = 0; i < num_values; i++) { - add_index_string(tmp, i, ldap_value[i], 1); + add_index_stringl(tmp, i, ldap_value[i]->bv_val, ldap_value[i]->bv_len, 1); } - ldap_value_free(ldap_value); + ldap_value_free_len(ldap_value); zend_hash_update(Z_ARRVAL_P(return_value), attribute, strlen(attribute)+1, (void *) &tmp, sizeof(zval *), NULL); add_index_string(return_value, num_attrib, attribute, 1); @@ -1231,7 +1342,7 @@ ldap_linkdata *ld; ldap_resultentry *resultentry; char *attribute; - char **ldap_value; + struct berval **ldap_value; int i, num_values; if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &link, &result_entry, &attr) == FAILURE) { @@ -1244,21 +1355,21 @@ convert_to_string_ex(attr); attribute = Z_STRVAL_PP(attr); - if ((ldap_value = ldap_get_values(ld->link, resultentry->data, attribute)) == NULL) { + if ((ldap_value = ldap_get_values_len(ld->link, resultentry->data, attribute)) == NULL) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot get the value(s) of attribute %s", ldap_err2string(_get_lderrno(ld->link))); RETURN_FALSE; } - num_values = ldap_count_values(ldap_value); + num_values = ldap_count_values_len(ldap_value); array_init(return_value); for (i = 0; ibv_val, ldap_value[i]->bv_len, 1); } add_assoc_long(return_value, "count", num_values); - ldap_value_free(ldap_value); + ldap_value_free_len(ldap_value); } /* }}} */ @@ -1363,7 +1474,11 @@ add_index_string(return_value, i, ldap_value[i], 1); } +#ifdef LDAP_API_FEATURE_X_OPENLDAP + ber_memvfree((void **)ldap_value); +#else ldap_value_free(ldap_value); +#endif } /* }}} */ @@ -1933,14 +2048,15 @@ Extract information from result */ PHP_FUNCTION(ldap_parse_result) { - zval **link, **result, **errcode, **matcheddn, **errmsg, **referrals; + zval **link, **result, **errcode, **matcheddn, **errmsg, **referrals, **serverctrls; ldap_linkdata *ld; LDAPMessage *ldap_result; - char **lreferrals, **refp; + LDAPControl **lserverctrls; + char **lreferrals; char *lmatcheddn, *lerrmsg; int rc, lerrcode, myargcount = ZEND_NUM_ARGS(); - if (myargcount < 3 || myargcount > 6 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals) == FAILURE) { + if (myargcount < 3 || myargcount > 7 || zend_get_parameters_ex(myargcount, &link, &result, &errcode, &matcheddn, &errmsg, &referrals, &serverctrls) == FAILURE) { WRONG_PARAM_COUNT; } @@ -1951,7 +2067,7 @@ myargcount > 3 ? &lmatcheddn : NULL, myargcount > 4 ? &lerrmsg : NULL, myargcount > 5 ? &lreferrals : NULL, - NULL /* &serverctrls */, + myargcount > 6 ? &lserverctrls : NULL, 0); if (rc != LDAP_SUCCESS) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to parse result: %s", ldap_err2string(rc)); @@ -1963,17 +2079,15 @@ /* Reverse -> fall through */ switch (myargcount) { + case 7: + /* use arg #7 as the array of controls returned by the server */ + zval_dtor(*serverctrls); + array_init(*serverctrls); + _parse_server_controls_resp(&lserverctrls, serverctrls); case 6: zval_dtor(*referrals); array_init(*referrals); - if (lreferrals != NULL) { - refp = lreferrals; - while (*refp) { - add_next_index_string(*referrals, *refp, 1); - refp++; - } - ldap_value_free(lreferrals); - } + _parse_referrals_resp(&lreferrals, referrals); case 5: zval_dtor(*errmsg); if (lerrmsg == NULL) { @@ -2057,32 +2171,38 @@ Extract information from reference entry */ PHP_FUNCTION(ldap_parse_reference) { - zval **link, **result_entry, **referrals; + zval **link, **result_entry, **referrals, **serverctrls; ldap_linkdata *ld; ldap_resultentry *resultentry; - char **lreferrals, **refp; + char **lreferrals; + LDAPControl **lserverctrls; + int myargcount = ZEND_NUM_ARGS(); - if (ZEND_NUM_ARGS() != 3 || zend_get_parameters_ex(3, &link, &result_entry, &referrals) == FAILURE) { + if (myargcount < 3 || myargcount > 4 || zend_get_parameters_ex(4, &link, &result_entry, &referrals, &serverctrls) == FAILURE) { WRONG_PARAM_COUNT; } ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, link, -1, "ldap link", le_link); ZEND_FETCH_RESOURCE(resultentry, ldap_resultentry *, result_entry, -1, "ldap result entry", le_result_entry); - if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, NULL /* &serverctrls */, 0) != LDAP_SUCCESS) { + if (ldap_parse_reference(ld->link, resultentry->data, &lreferrals, &lserverctrls, 0) != LDAP_SUCCESS) { RETURN_FALSE; } - zval_dtor(*referrals); - array_init(*referrals); - if (lreferrals != NULL) { - refp = lreferrals; - while (*refp) { - add_next_index_string(*referrals, *refp, 1); - refp++; - } - ldap_value_free(lreferrals); + + /* Reverse -> fall through */ + switch (myargcount) { + case 4: + /* use arg #4 as the array of controls returned by the server */ + zval_dtor(*serverctrls); + array_init(*serverctrls); + _parse_server_controls_resp(&lserverctrls, serverctrls); + case 3: + zval_dtor(*referrals); + array_init(*referrals); + _parse_referrals_resp(&lreferrals, referrals); } + RETURN_TRUE; } /* }}} */ --=-Dk8tZxoP/ODrUWa1Eymz Content-Disposition: attachment; filename=pagedResults.php Content-Type: application/x-php; name=pagedResults.php Content-Transfer-Encoding: 7bit LDAP pagedResults query test"; echo "Connecting ..."; $ds=ldap_connect("ldap://:9011"); // must be a valid LDAP server! echo "connect result is [" . $ds . "]
"; if ($ds) { if (ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3)) { echo "Using LDAPv3
"; } else { echo "Failed to set protocol version to 3
"; } echo "Binding ..."; $r = ldap_bind($ds); // this is an "anonymous" bind, typically // read-only access echo "Bind result is [" . $r . "]
"; //if (ldap_set_option($ds, LDAP_OPT_SIZELIMIT, 4)) { // echo "setting sizelimit
"; //} else { // echo "Failed to set sizelimit
"; //} $pageSize = "\003"; $pagedResultsValue = "0\005\002\001\003\004\000"; //0 \r 002 001 \0 004 \b \n \0 \0 \0 \0 \0 \0 \0 while (1) { $pagedResultsValue{4} = $pageSize; //echo "Setting pagedResults control = " . $pagedResultsValue . "... "; echo "Setting pagedResults control... "; $ctrl1 = array("oid" => "1.2.840.113556.1.4.319", "value" => $pagedResultsValue, "iscritical" => 1); if (ldap_set_option($ds, LDAP_OPT_SERVER_CONTROLS, array($ctrl1))) { echo "success
"; } else { echo "ldap_set_option failed
"; } echo "Searching for (sn=S*) ..."; // Search surname entry $sr=ldap_search($ds, "dc=example,dc=com", "sn=*"); echo "Search result is [" . $sr . "]
"; echo "Number of entires returned is " . ldap_count_entries($ds, $sr) . "
"; echo "Getting entries ...

"; $info = ldap_get_entries($ds, $sr); echo "Data for " . $info["count"] . " items returned:

"; for ($i=0; $i<$info["count"]; $i++) { echo "dn is: " . $info[$i]["dn"] . "
"; echo "first cn entry is: " . $info[$i]["cn"][0] . "
"; echo "first email entry is: " . $info[$i]["mail"][0] . "


"; } $r=ldap_parse_result($ds, $sr, $errcode, $matcheddn, $errmsg, &$referrals, &$ctrls); //$r=ldap_parse_result($ds, $sr, $errcode, $matcheddn, $errmsg, $referrals); echo "error: " . $errcode . " (" . $errmsg . ")
"; if ($ctrls["count"] > 0) { $dostop = 0; for ($i = 0; $i < $ctrls["count"]; $i++) { //echo "control " . $i . ": oid=" . $ctrls[$i]["oid"] . " value=" . $ctrls[$i]["value"] . " iscritical=" . $ctrls[$i]["iscritical"] . "
"; echo "control " . $i . ": oid=" . $ctrls[$i]["oid"] . " iscritical=" . $ctrls[$i]["iscritical"] . "
"; if ($ctrls[$i]["oid"] == "1.2.840.113556.1.4.319") { $pagedResultsValue = $ctrls[$i]["value"]; if ($pagedResultsValue{6} == "\000") { $dostop = 1; } } } if ($dostop) { break; } } else { echo "Control missing?
"; break; } echo "


"; } echo "Closing connection...
"; ldap_close($ds); } else { echo "

Unable to connect to LDAP server

"; } ?> --=-Dk8tZxoP/ODrUWa1Eymz--