summaryrefslogtreecommitdiffstats
path: root/source4/libcli/ldap
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/ldap')
-rw-r--r--source4/libcli/ldap/ldap_bind.c545
-rw-r--r--source4/libcli/ldap/ldap_client.c1069
-rw-r--r--source4/libcli/ldap/ldap_client.h149
-rw-r--r--source4/libcli/ldap/ldap_controls.c1288
-rw-r--r--source4/libcli/ldap/ldap_ildap.c133
-rw-r--r--source4/libcli/ldap/libcli_ldap.h31
-rw-r--r--source4/libcli/ldap/wscript_build11
7 files changed, 3226 insertions, 0 deletions
diff --git a/source4/libcli/ldap/ldap_bind.c b/source4/libcli/ldap/ldap_bind.c
new file mode 100644
index 0000000..1f73082
--- /dev/null
+++ b/source4/libcli/ldap/ldap_bind.c
@@ -0,0 +1,545 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ LDAP bind calls
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h" /* TODO: remove this */
+#include "source4/auth/gensec/gensec_tstream.h"
+#include "auth/credentials/credentials.h"
+#include "lib/stream/packet.h"
+#include "param/param.h"
+#include "param/loadparm.h"
+#include "librpc/gen_ndr/ads.h"
+
+struct ldap_simple_creds {
+ const char *dn;
+ const char *pw;
+};
+
+_PUBLIC_ NTSTATUS ldap_rebind(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ struct ldap_simple_creds *creds;
+
+ switch (conn->bind.type) {
+ case LDAP_BIND_SASL:
+ status = ldap_bind_sasl(conn, (struct cli_credentials *)conn->bind.creds,
+ conn->lp_ctx);
+ break;
+
+ case LDAP_BIND_SIMPLE:
+ creds = (struct ldap_simple_creds *)conn->bind.creds;
+
+ if (creds == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = ldap_bind_simple(conn, creds->dn, creds->pw);
+ break;
+
+ default:
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_simple_bind_msg(struct ldap_connection *conn,
+ const char *dn, const char *pw)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = talloc_strdup(res, dn);
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
+ res->r.BindRequest.creds.password = talloc_strdup(res, pw);
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a simple username/password bind
+*/
+_PUBLIC_ NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password)
+{
+ struct ldap_request *req;
+ struct ldap_message *msg;
+ const char *dn, *pw;
+ NTSTATUS status;
+
+ if (conn == NULL) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+
+ if (userdn) {
+ dn = userdn;
+ } else {
+ if (conn->auth_dn) {
+ dn = conn->auth_dn;
+ } else {
+ dn = "";
+ }
+ }
+
+ if (password) {
+ pw = password;
+ } else {
+ if (conn->simple_pw) {
+ pw = conn->simple_pw;
+ } else {
+ pw = "";
+ }
+ }
+
+ msg = new_ldap_simple_bind_msg(conn, dn, pw);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ /* send the request */
+ req = ldap_request_send(conn, msg);
+ talloc_free(msg);
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ /* wait for replies */
+ status = ldap_request_wait(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return status;
+ }
+
+ /* check its a valid reply */
+ msg = req->replies[0];
+ if (msg->type != LDAP_TAG_BindResponse) {
+ talloc_free(req);
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ status = ldap_check_response(conn, &msg->r.BindResponse.response);
+
+ talloc_free(req);
+
+ if (NT_STATUS_IS_OK(status)) {
+ struct ldap_simple_creds *creds = talloc(conn, struct ldap_simple_creds);
+ if (creds == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ creds->dn = talloc_strdup(creds, dn);
+ creds->pw = talloc_strdup(creds, pw);
+ if (creds->dn == NULL || creds->pw == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ conn->bind.type = LDAP_BIND_SIMPLE;
+ conn->bind.creds = creds;
+ }
+
+ return status;
+}
+
+
+static struct ldap_message *new_ldap_sasl_bind_msg(struct ldap_connection *conn,
+ const char *sasl_mechanism,
+ DATA_BLOB *secblob)
+{
+ struct ldap_message *res;
+
+ res = new_ldap_message(conn);
+ if (!res) {
+ return NULL;
+ }
+
+ res->type = LDAP_TAG_BindRequest;
+ res->r.BindRequest.version = 3;
+ res->r.BindRequest.dn = "";
+ res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
+ res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res, sasl_mechanism);
+ if (secblob) {
+ res->r.BindRequest.creds.SASL.secblob = talloc(res, DATA_BLOB);
+ if (!res->r.BindRequest.creds.SASL.secblob) {
+ talloc_free(res);
+ return NULL;
+ }
+ *res->r.BindRequest.creds.SASL.secblob = *secblob;
+ } else {
+ res->r.BindRequest.creds.SASL.secblob = NULL;
+ }
+ res->controls = NULL;
+
+ return res;
+}
+
+
+/*
+ perform a sasl bind using the given credentials
+*/
+_PUBLIC_ NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ DATA_BLOB input = data_blob(NULL, 0);
+ DATA_BLOB output = data_blob(NULL, 0);
+
+ struct ldap_message **sasl_mechs_msgs;
+ struct ldap_SearchResEntry *search;
+ int count, i;
+ bool first = true;
+ int wrap_flags = 0;
+ const char **sasl_names;
+ uint32_t old_gensec_features;
+ static const char *supported_sasl_mech_attrs[] = {
+ "supportedSASLMechanisms",
+ NULL
+ };
+ unsigned int logon_retries = 0;
+ size_t queue_length;
+
+ if (conn->sockets.active == NULL) {
+ status = NT_STATUS_CONNECTION_DISCONNECTED;
+ goto failed;
+ }
+
+ queue_length = tevent_queue_length(conn->sockets.send_queue);
+ if (queue_length != 0) {
+ status = NT_STATUS_INVALID_PARAMETER_MIX;
+ DEBUG(1, ("SASL bind triggered with non empty send_queue[%zu]: %s\n",
+ queue_length, nt_errstr(status)));
+ goto failed;
+ }
+
+ if (conn->pending != NULL) {
+ status = NT_STATUS_INVALID_PARAMETER_MIX;
+ DEBUG(1, ("SASL bind triggered with pending requests: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ status = ildap_search(conn, "", LDAP_SEARCH_SCOPE_BASE, "", supported_sasl_mech_attrs,
+ false, NULL, NULL, &sasl_mechs_msgs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ count = ildap_count_entries(conn, sasl_mechs_msgs);
+ if (count != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of replies: %d\n",
+ count));
+ goto failed;
+ }
+
+ tmp_ctx = talloc_new(conn);
+ if (tmp_ctx == NULL) goto failed;
+
+ search = &sasl_mechs_msgs[0]->r.SearchResultEntry;
+ if (search->num_attributes != 1) {
+ DEBUG(1, ("Failed to inquire of target's available sasl mechs in rootdse search: wrong number of attributes: %d != 1\n",
+ search->num_attributes));
+ goto failed;
+ }
+
+ sasl_names = talloc_array(tmp_ctx, const char *, search->attributes[0].num_values + 1);
+ if (!sasl_names) {
+ DEBUG(1, ("talloc_arry(char *, %d) failed\n",
+ count));
+ goto failed;
+ }
+
+ for (i=0; i<search->attributes[0].num_values; i++) {
+ sasl_names[i] = (const char *)search->attributes[0].values[i].data;
+ }
+ sasl_names[i] = NULL;
+
+ gensec_init();
+
+ if (conn->sockets.active == conn->sockets.tls) {
+ /*
+ * require Kerberos SIGN/SEAL only if we don't use SSL
+ * Windows seem not to like double encryption
+ */
+ wrap_flags = 0;
+ } else if (cli_credentials_is_anonymous(creds)) {
+ /*
+ * anonymous isn't protected
+ */
+ wrap_flags = 0;
+ } else {
+ wrap_flags = lpcfg_client_ldap_sasl_wrapping(lp_ctx);
+ }
+
+try_logon_again:
+ /*
+ we loop back here on a logon failure, and re-create the
+ gensec session. The logon_retries counter ensures we don't
+ loop forever.
+ */
+ data_blob_free(&input);
+ TALLOC_FREE(conn->gensec);
+
+ status = gensec_client_start(conn, &conn->gensec,
+ lpcfg_gensec_settings(conn, lp_ctx));
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
+ goto failed;
+ }
+
+ old_gensec_features = cli_credentials_get_gensec_features(creds);
+ if (wrap_flags == 0) {
+ cli_credentials_set_gensec_features(creds,
+ old_gensec_features & ~(GENSEC_FEATURE_SIGN|GENSEC_FEATURE_SEAL),
+ CRED_SPECIFIED);
+ }
+
+ /* this call also sets the gensec_want_features */
+ status = gensec_set_credentials(conn->gensec, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC creds: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ /* reset the original gensec_features (on the credentials
+ * context, so we don't tattoo it ) */
+ cli_credentials_set_gensec_features(creds,
+ old_gensec_features,
+ CRED_SPECIFIED);
+
+ if (wrap_flags & ADS_AUTH_SASL_SEAL) {
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SEAL);
+ }
+ if (wrap_flags & ADS_AUTH_SASL_SIGN) {
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_SIGN);
+ }
+
+ /*
+ * This is an indication for the NTLMSSP backend to
+ * also encrypt when only GENSEC_FEATURE_SIGN is requested
+ * in gensec_[un]wrap().
+ */
+ gensec_want_feature(conn->gensec, GENSEC_FEATURE_LDAP_STYLE);
+
+ if (conn->host) {
+ status = gensec_set_target_hostname(conn->gensec, conn->host);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target hostname: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+ }
+
+ status = gensec_set_target_service(conn->gensec, "ldap");
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Failed to set GENSEC target service: %s\n",
+ nt_errstr(status)));
+ goto failed;
+ }
+
+ status = gensec_start_mech_by_sasl_list(conn->gensec, sasl_names);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("None of the %d proposed SASL mechs were acceptable: %s\n",
+ count, nt_errstr(status)));
+ goto failed;
+ }
+
+ while (1) {
+ NTSTATUS gensec_status;
+ struct ldap_message *response;
+ struct ldap_message *msg;
+ struct ldap_request *req;
+ int result = LDAP_OTHER;
+
+ status = gensec_update(conn->gensec, tmp_ctx,
+ input,
+ &output);
+ /* The status value here, from GENSEC is vital to the security
+ * of the system. Even if the other end accepts, if GENSEC
+ * claims 'MORE_PROCESSING_REQUIRED' then you must keep
+ * feeding it blobs, or else the remote host/attacker might
+ * avoid mutual authentication requirements.
+ *
+ * Likewise, you must not feed GENSEC too much (after the OK),
+ * it doesn't like that either.
+ *
+ * For SASL/EXTERNAL, there is no data to send, but we still
+ * must send the actual Bind request the first time around.
+ * Otherwise, a result of NT_STATUS_OK with 0 output means the
+ * end of a multi-step authentication, and no message must be
+ * sent.
+ */
+
+ gensec_status = status;
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) &&
+ !NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ if (NT_STATUS_IS_OK(status) && output.length == 0) {
+ if (!first)
+ break;
+ }
+ first = false;
+
+ /* Perhaps we should make gensec_start_mech_by_sasl_list() return the name we got? */
+ msg = new_ldap_sasl_bind_msg(tmp_ctx, conn->gensec->ops->sasl_name, (output.data?&output:NULL));
+ if (msg == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ talloc_reparent(conn, tmp_ctx, req);
+
+ status = ldap_result_n(req, 0, &response);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ if (response->type != LDAP_TAG_BindResponse) {
+ status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ goto failed;
+ }
+
+ result = response->r.BindResponse.response.resultcode;
+
+ if (result == LDAP_STRONG_AUTH_REQUIRED) {
+ if (wrap_flags == 0) {
+ wrap_flags = ADS_AUTH_SASL_SIGN;
+ goto try_logon_again;
+ }
+ }
+
+ if (result == LDAP_INVALID_CREDENTIALS) {
+ /*
+ try a second time on invalid credentials, to
+ give the user a chance to re-enter the
+ password and to handle the case where our
+ kerberos ticket is invalid as the server
+ password has changed
+ */
+ const char *principal;
+
+ principal = gensec_get_target_principal(conn->gensec);
+ if (principal == NULL) {
+ const char *hostname = gensec_get_target_hostname(conn->gensec);
+ const char *service = gensec_get_target_service(conn->gensec);
+ if (hostname != NULL && service != NULL) {
+ principal = talloc_asprintf(tmp_ctx, "%s/%s", service, hostname);
+ }
+ }
+
+ if (cli_credentials_failed_kerberos_login(creds, principal, &logon_retries) ||
+ cli_credentials_wrong_password(creds)) {
+ /*
+ destroy our gensec session and loop
+ back up to the top to retry,
+ offering the user a chance to enter
+ new credentials, or get a new ticket
+ if using kerberos
+ */
+ goto try_logon_again;
+ }
+ }
+
+ if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
+ status = ldap_check_response(conn,
+ &response->r.BindResponse.response);
+ break;
+ }
+
+ /* This is where we check if GENSEC wanted to be fed more data */
+ if (!NT_STATUS_EQUAL(gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ break;
+ }
+ if (response->r.BindResponse.SASL.secblob) {
+ input = *response->r.BindResponse.SASL.secblob;
+ } else {
+ input = data_blob(NULL, 0);
+ }
+ }
+
+ TALLOC_FREE(tmp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ conn->bind.type = LDAP_BIND_SASL;
+ conn->bind.creds = creds;
+
+ if (wrap_flags & ADS_AUTH_SASL_SEAL) {
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ } else if (wrap_flags & ADS_AUTH_SASL_SIGN) {
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN)) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ }
+
+ if (!gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) &&
+ !gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL)) {
+ return NT_STATUS_OK;
+ }
+
+ status = gensec_create_tstream(conn->sockets.raw,
+ conn->gensec,
+ conn->sockets.raw,
+ &conn->sockets.sasl);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ conn->sockets.active = conn->sockets.sasl;
+
+ return NT_STATUS_OK;
+
+failed:
+ talloc_free(tmp_ctx);
+ talloc_free(conn->gensec);
+ conn->gensec = NULL;
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c
new file mode 100644
index 0000000..db13ad3
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.c
@@ -0,0 +1,1069 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Andrew Tridgell 2004
+ Copyright (C) Volker Lendecke 2004
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "lib/socket/socket.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "../lib/util/asn1.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/composite/composite.h"
+#include "lib/tls/tls.h"
+#include "auth/gensec/gensec.h"
+#include "system/time.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+
+static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status);
+
+static int ldap_connection_destructor(struct ldap_connection *conn)
+{
+ /*
+ * NT_STATUS_OK means that callbacks of pending requests are not
+ * triggered
+ */
+ ldap_connection_dead(conn, NT_STATUS_OK);
+ return 0;
+}
+
+/**
+ create a new ldap_connection structure. The event context is optional
+*/
+
+_PUBLIC_ struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev)
+{
+ struct ldap_connection *conn;
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ conn = talloc_zero(mem_ctx, struct ldap_connection);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->next_messageid = 1;
+ conn->event.event_ctx = ev;
+
+ conn->sockets.send_queue = tevent_queue_create(conn,
+ "ldap_connection send_queue");
+ if (conn->sockets.send_queue == NULL) {
+ TALLOC_FREE(conn);
+ return NULL;
+ }
+
+ conn->lp_ctx = lp_ctx;
+
+ /* set a reasonable request timeout */
+ conn->timeout = 60;
+
+ /* explicitly avoid reconnections by default */
+ conn->reconnect.max_retries = 0;
+
+ talloc_set_destructor(conn, ldap_connection_destructor);
+ return conn;
+}
+
+/*
+ the connection is dead
+*/
+static void ldap_connection_dead(struct ldap_connection *conn, NTSTATUS status)
+{
+ struct ldap_request *req;
+
+ tevent_queue_stop(conn->sockets.send_queue);
+ TALLOC_FREE(conn->sockets.recv_subreq);
+ conn->sockets.active = NULL;
+ TALLOC_FREE(conn->sockets.sasl);
+ TALLOC_FREE(conn->sockets.tls);
+ TALLOC_FREE(conn->sockets.raw);
+
+ /* return an error for any pending request ... */
+ while (conn->pending) {
+ req = conn->pending;
+ DLIST_REMOVE(req->conn->pending, req);
+ req->conn = NULL;
+ req->state = LDAP_REQUEST_DONE;
+ if (NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ req->status = status;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ }
+}
+
+static void ldap_reconnect(struct ldap_connection *conn);
+
+/*
+ handle packet errors
+*/
+static void ldap_error_handler(struct ldap_connection *conn, NTSTATUS status)
+{
+ ldap_connection_dead(conn, status);
+
+ /* but try to reconnect so that the ldb client can go on */
+ ldap_reconnect(conn);
+}
+
+
+/*
+ match up with a pending message, adding to the replies list
+*/
+static void ldap_match_message(struct ldap_connection *conn, struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ int i;
+
+ for (req=conn->pending; req; req=req->next) {
+ if (req->messageid == msg->messageid) break;
+ }
+ /* match a zero message id to the last request sent.
+ It seems that servers send 0 if unable to parse */
+ if (req == NULL && msg->messageid == 0) {
+ req = conn->pending;
+ }
+ if (req == NULL) {
+ DEBUG(0,("ldap: no matching message id for %u\n",
+ msg->messageid));
+ TALLOC_FREE(msg);
+ return;
+ }
+
+ /* Check for undecoded critical extensions */
+ for (i=0; msg->controls && msg->controls[i]; i++) {
+ if (!msg->controls_decoded[i] &&
+ msg->controls[i]->critical) {
+ TALLOC_FREE(msg);
+ req->status = NT_STATUS_LDAP(LDAP_UNAVAILABLE_CRITICAL_EXTENSION);
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+ }
+
+ /* add to the list of replies received */
+ req->replies = talloc_realloc(req, req->replies,
+ struct ldap_message *, req->num_replies+1);
+ if (req->replies == NULL) {
+ TALLOC_FREE(msg);
+ req->status = NT_STATUS_NO_MEMORY;
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ req->replies[req->num_replies] = talloc_steal(req->replies, msg);
+ req->num_replies++;
+
+ if (msg->type != LDAP_TAG_SearchResultEntry &&
+ msg->type != LDAP_TAG_SearchResultReference) {
+ /* currently only search results expect multiple
+ replies */
+ req->state = LDAP_REQUEST_DONE;
+ DLIST_REMOVE(conn->pending, req);
+ }
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void ldap_connection_recv_done(struct tevent_req *subreq);
+
+static void ldap_connection_recv_next(struct ldap_connection *conn)
+{
+ struct tevent_req *subreq = NULL;
+
+ if (conn->sockets.recv_subreq != NULL) {
+ return;
+ }
+
+ if (conn->sockets.active == NULL) {
+ return;
+ }
+
+ if (conn->pending == NULL) {
+ return;
+ }
+
+ /*
+ * The minimum size of a LDAP pdu is 7 bytes
+ *
+ * dumpasn1 -hh ldap-unbind-min.dat
+ *
+ * <30 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 2 1: INTEGER 9
+ * <42 00>
+ * 5 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * dumpasn1 -hh ldap-unbind-windows.dat
+ *
+ * <30 84 00 00 00 05 02 01 09 42 00>
+ * 0 5: SEQUENCE {
+ * <02 01 09>
+ * 6 1: INTEGER 9
+ * <42 00>
+ * 9 0: [APPLICATION 2]
+ * : Error: Object has zero length.
+ * : }
+ *
+ * This means using an initial read size
+ * of 7 is ok.
+ */
+ subreq = tstream_read_pdu_blob_send(conn,
+ conn->event.event_ctx,
+ conn->sockets.active,
+ 7, /* initial_read_size */
+ ldap_full_packet,
+ conn);
+ if (subreq == NULL) {
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ tevent_req_set_callback(subreq, ldap_connection_recv_done, conn);
+ conn->sockets.recv_subreq = subreq;
+ return;
+}
+
+/*
+ decode/process LDAP data
+*/
+static void ldap_connection_recv_done(struct tevent_req *subreq)
+{
+ NTSTATUS status;
+ struct ldap_connection *conn =
+ tevent_req_callback_data(subreq,
+ struct ldap_connection);
+ struct ldap_message *msg;
+ struct asn1_data *asn1;
+ DATA_BLOB blob;
+ struct ldap_request_limits limits = {0};
+
+ msg = talloc_zero(conn, struct ldap_message);
+ if (msg == NULL) {
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ asn1 = asn1_init(conn, ASN1_MAX_TREE_DEPTH);
+ if (asn1 == NULL) {
+ TALLOC_FREE(msg);
+ ldap_error_handler(conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+
+ conn->sockets.recv_subreq = NULL;
+
+ status = tstream_read_pdu_blob_recv(subreq,
+ asn1,
+ &blob);
+ TALLOC_FREE(subreq);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(msg);
+ asn1_free(asn1);
+ ldap_error_handler(conn, status);
+ return;
+ }
+
+ asn1_load_nocopy(asn1, blob.data, blob.length);
+
+ status = ldap_decode(asn1, &limits, samba_ldap_control_handlers(), msg);
+ asn1_free(asn1);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(msg);
+ ldap_error_handler(conn, status);
+ return;
+ }
+
+ ldap_match_message(conn, msg);
+ ldap_connection_recv_next(conn);
+
+ return;
+}
+
+enum ldap_proto {
+ LDAP_PROTO_NONE,
+ LDAP_PROTO_LDAP,
+ LDAP_PROTO_LDAPS,
+ LDAP_PROTO_LDAPI
+};
+
+static int ldap_parse_basic_url(
+ const char *url,
+ enum ldap_proto *pproto,
+ TALLOC_CTX *mem_ctx,
+ char **pdest, /* path for ldapi, host for ldap[s] */
+ uint16_t *pport) /* Not set for ldapi */
+{
+ enum ldap_proto proto = LDAP_PROTO_NONE;
+ char *host = NULL;
+ int ret, port;
+
+ if (url == NULL) {
+ return EINVAL;
+ }
+
+ if (strncasecmp_m(url, "ldapi://", strlen("ldapi://")) == 0) {
+ char *path = NULL, *end = NULL;
+
+ path = talloc_strdup(mem_ctx, url+8);
+ if (path == NULL) {
+ return ENOMEM;
+ }
+ end = rfc1738_unescape(path);
+ if (end == NULL) {
+ TALLOC_FREE(path);
+ return EINVAL;
+ }
+
+ *pproto = LDAP_PROTO_LDAPI;
+ *pdest = path;
+ return 0;
+ }
+
+ if (strncasecmp_m(url, "ldap://", strlen("ldap://")) == 0) {
+ url += 7;
+ proto = LDAP_PROTO_LDAP;
+ port = 389;
+ }
+ if (strncasecmp_m(url, "ldaps://", strlen("ldaps://")) == 0) {
+ url += 8;
+ port = 636;
+ proto = LDAP_PROTO_LDAPS;
+ }
+
+ if (proto == LDAP_PROTO_NONE) {
+ return EPROTONOSUPPORT;
+ }
+
+ if (url[0] == '[') {
+ /*
+ * IPv6 with [aa:bb:cc..]:port
+ */
+ const char *end = NULL;
+
+ url +=1;
+
+ end = strchr(url, ']');
+ if (end == NULL) {
+ return EINVAL;
+ }
+
+ ret = sscanf(end+1, ":%d", &port);
+ if (ret < 0) {
+ return EINVAL;
+ }
+
+ *pdest = talloc_strndup(mem_ctx, url, end-url);
+ if (*pdest == NULL) {
+ return ENOMEM;
+ }
+ *pproto = proto;
+ *pport = port;
+ return 0;
+ }
+
+ ret = sscanf(url, "%m[^:/]:%d", &host, &port);
+ if (ret < 1) {
+ return EINVAL;
+ }
+
+ *pdest = talloc_strdup(mem_ctx, host);
+ SAFE_FREE(host);
+ if (*pdest == NULL) {
+ return ENOMEM;
+ }
+ *pproto = proto;
+ *pport = port;
+
+ return 0;
+}
+
+/*
+ connect to a ldap server
+*/
+
+struct ldap_connect_state {
+ struct composite_context *ctx;
+ struct ldap_connection *conn;
+ struct socket_context *sock;
+ struct tstream_context *raw;
+ struct tstream_tls_params *tls_params;
+ struct tstream_context *tls;
+};
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx);
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx);
+
+_PUBLIC_ struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url)
+{
+ struct composite_context *result, *ctx;
+ struct ldap_connect_state *state;
+ enum ldap_proto proto;
+ char *dest = NULL;
+ uint16_t port;
+ int ret;
+
+ result = talloc_zero(conn, struct composite_context);
+ if (result == NULL) goto failed;
+ result->state = COMPOSITE_STATE_IN_PROGRESS;
+ result->async.fn = NULL;
+ result->event_ctx = conn->event.event_ctx;
+
+ state = talloc(result, struct ldap_connect_state);
+ if (state == NULL) goto failed;
+ state->ctx = result;
+ result->private_data = state;
+
+ state->conn = conn;
+
+ if (conn->reconnect.url == NULL) {
+ conn->reconnect.url = talloc_strdup(conn, url);
+ if (conn->reconnect.url == NULL) goto failed;
+ }
+
+ ret = ldap_parse_basic_url(url, &proto, conn, &dest, &port);
+ if (ret != 0) {
+ composite_error(result, map_nt_error_from_unix_common(ret));
+ return result;
+ }
+
+ if (proto == LDAP_PROTO_LDAPI) {
+ struct socket_address *unix_addr;
+ NTSTATUS status = socket_create(state, "unix",
+ SOCKET_TYPE_STREAM,
+ &state->sock, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+
+ conn->host = talloc_asprintf(conn, "%s.%s",
+ lpcfg_netbios_name(conn->lp_ctx),
+ lpcfg_dnsdomain(conn->lp_ctx));
+ if (composite_nomem(conn->host, state->ctx)) {
+ return result;
+ }
+
+ unix_addr = socket_address_from_strings(state, state->sock->backend_name,
+ dest, 0);
+ if (composite_nomem(unix_addr, result)) {
+ return result;
+ }
+
+ ctx = socket_connect_send(state->sock, NULL, unix_addr,
+ 0, result->event_ctx);
+ ctx->async.fn = ldap_connect_recv_unix_conn;
+ ctx->async.private_data = state;
+ return result;
+ }
+
+ if ((proto == LDAP_PROTO_LDAP) || (proto == LDAP_PROTO_LDAPS)) {
+
+ conn->ldaps = (proto == LDAP_PROTO_LDAPS);
+
+ conn->host = talloc_move(conn, &dest);
+ conn->port = port;
+
+ if (conn->ldaps) {
+ char *ca_file = lpcfg_tls_cafile(state, conn->lp_ctx);
+ char *crl_file = lpcfg_tls_crlfile(state, conn->lp_ctx);
+ const char *tls_priority = lpcfg_tls_priority(conn->lp_ctx);
+ enum tls_verify_peer_state verify_peer =
+ lpcfg_tls_verify_peer(conn->lp_ctx);
+ NTSTATUS status;
+
+ status = tstream_tls_params_client(state,
+ ca_file,
+ crl_file,
+ tls_priority,
+ verify_peer,
+ conn->host,
+ &state->tls_params);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(result, status);
+ return result;
+ }
+ }
+
+ ctx = socket_connect_multi_send(state, conn->host, 1, &conn->port,
+ lpcfg_resolve_context(conn->lp_ctx),
+ result->event_ctx);
+ if (composite_nomem(ctx, result)) {
+ return result;
+ }
+
+ ctx->async.fn = ldap_connect_recv_tcp_conn;
+ ctx->async.private_data = state;
+ return result;
+ }
+ failed:
+ talloc_free(result);
+ return NULL;
+}
+
+static void ldap_connect_got_tls(struct tevent_req *subreq);
+
+static void ldap_connect_got_sock(struct composite_context *ctx,
+ struct ldap_connection *conn)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type_abort(ctx->private_data,
+ struct ldap_connect_state);
+ struct tevent_req *subreq = NULL;
+ int fd;
+ int ret;
+
+ socket_set_flags(state->sock, SOCKET_FLAG_NOCLOSE);
+ fd = socket_get_fd(state->sock);
+ TALLOC_FREE(state->sock);
+
+ smb_set_close_on_exec(fd);
+
+ ret = set_blocking(fd, false);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ret = tstream_bsd_existing_socket(state, fd, &state->raw);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(errno);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ if (!conn->ldaps) {
+ conn->sockets.raw = talloc_move(conn, &state->raw);
+ conn->sockets.active = conn->sockets.raw;
+ composite_done(state->ctx);
+ return;
+ }
+
+ subreq = tstream_tls_connect_send(state, state->ctx->event_ctx,
+ state->raw, state->tls_params);
+ if (composite_nomem(subreq, state->ctx)) {
+ return;
+ }
+ tevent_req_set_callback(subreq, ldap_connect_got_tls, state);
+}
+
+static void ldap_connect_got_tls(struct tevent_req *subreq)
+{
+ struct ldap_connect_state *state =
+ tevent_req_callback_data(subreq,
+ struct ldap_connect_state);
+ int err;
+ int ret;
+
+ ret = tstream_tls_connect_recv(subreq, &err, state, &state->tls);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS status = map_nt_error_from_unix_common(err);
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ talloc_steal(state->tls, state->tls_params);
+
+ state->conn->sockets.raw = talloc_move(state->conn, &state->raw);
+ state->conn->sockets.tls = talloc_move(state->conn->sockets.raw,
+ &state->tls);
+ state->conn->sockets.active = state->conn->sockets.tls;
+ composite_done(state->ctx);
+}
+
+static void ldap_connect_recv_tcp_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type_abort(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+ uint16_t port;
+ NTSTATUS status = socket_connect_multi_recv(ctx, state, &state->sock,
+ &port);
+ if (!NT_STATUS_IS_OK(status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+static void ldap_connect_recv_unix_conn(struct composite_context *ctx)
+{
+ struct ldap_connect_state *state =
+ talloc_get_type_abort(ctx->async.private_data,
+ struct ldap_connect_state);
+ struct ldap_connection *conn = state->conn;
+
+ NTSTATUS status = socket_connect_recv(ctx);
+
+ if (!NT_STATUS_IS_OK(state->ctx->status)) {
+ composite_error(state->ctx, status);
+ return;
+ }
+
+ ldap_connect_got_sock(state->ctx, conn);
+}
+
+_PUBLIC_ NTSTATUS ldap_connect_recv(struct composite_context *ctx)
+{
+ NTSTATUS status = composite_wait(ctx);
+ talloc_free(ctx);
+ return status;
+}
+
+_PUBLIC_ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
+{
+ struct composite_context *ctx = ldap_connect_send(conn, url);
+ return ldap_connect_recv(ctx);
+}
+
+/* set reconnect parameters */
+
+_PUBLIC_ void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries)
+{
+ if (conn) {
+ conn->reconnect.max_retries = max_retries;
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = time_mono(NULL);
+ }
+}
+
+/* Actually this function is NOT ASYNC safe, FIXME? */
+static void ldap_reconnect(struct ldap_connection *conn)
+{
+ NTSTATUS status;
+ time_t now = time_mono(NULL);
+
+ /* do we have set up reconnect ? */
+ if (conn->reconnect.max_retries == 0) return;
+
+ /* is the retry time expired ? */
+ if (now > conn->reconnect.previous + 30) {
+ conn->reconnect.retries = 0;
+ conn->reconnect.previous = now;
+ }
+
+ /* are we reconnectind too often and too fast? */
+ if (conn->reconnect.retries > conn->reconnect.max_retries) return;
+
+ /* keep track of the number of reconnections */
+ conn->reconnect.retries++;
+
+ /* reconnect */
+ status = ldap_connect(conn, conn->reconnect.url);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ /* rebind */
+ status = ldap_rebind(conn);
+ if ( ! NT_STATUS_IS_OK(status)) {
+ ldap_connection_dead(conn, status);
+ }
+}
+
+static void ldap_request_destructor_abandon(struct ldap_request *abandon)
+{
+ TALLOC_FREE(abandon);
+}
+
+/* destroy an open ldap request */
+static int ldap_request_destructor(struct ldap_request *req)
+{
+ if (req->state == LDAP_REQUEST_PENDING) {
+ struct ldap_message msg = {
+ .type = LDAP_TAG_AbandonRequest,
+ .r.AbandonRequest.messageid = req->messageid,
+ };
+ struct ldap_request *abandon = NULL;
+
+ DLIST_REMOVE(req->conn->pending, req);
+
+ abandon = ldap_request_send(req->conn, &msg);
+ if (abandon == NULL) {
+ ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
+ return 0;
+ }
+ abandon->async.fn = ldap_request_destructor_abandon;
+ abandon->async.private_data = NULL;
+ }
+
+ return 0;
+}
+
+static void ldap_request_timeout_abandon(struct ldap_request *abandon)
+{
+ struct ldap_request *req =
+ talloc_get_type_abort(abandon->async.private_data,
+ struct ldap_request);
+
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+/*
+ called on timeout of a ldap request
+*/
+static void ldap_request_timeout(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req =
+ talloc_get_type_abort(private_data,
+ struct ldap_request);
+
+ req->status = NT_STATUS_IO_TIMEOUT;
+ if (req->state == LDAP_REQUEST_PENDING) {
+ struct ldap_message msg = {
+ .type = LDAP_TAG_AbandonRequest,
+ .r.AbandonRequest.messageid = req->messageid,
+ };
+ struct ldap_request *abandon = NULL;
+
+ abandon = ldap_request_send(req->conn, &msg);
+ if (abandon == NULL) {
+ ldap_error_handler(req->conn, NT_STATUS_NO_MEMORY);
+ return;
+ }
+ talloc_reparent(req->conn, req, abandon);
+ abandon->async.fn = ldap_request_timeout_abandon;
+ abandon->async.private_data = req;
+ DLIST_REMOVE(req->conn->pending, req);
+ return;
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+
+/*
+ called on completion of a failed ldap request
+*/
+static void ldap_request_failed_complete(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct ldap_request *req =
+ talloc_get_type_abort(private_data,
+ struct ldap_request);
+
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
+static void ldap_request_written(struct tevent_req *subreq);
+
+/*
+ send a ldap message - async interface
+*/
+_PUBLIC_ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg)
+{
+ struct ldap_request *req;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct tevent_req *subreq = NULL;
+
+ req = talloc_zero(conn, struct ldap_request);
+ if (req == NULL) return NULL;
+
+ if (conn->sockets.active == NULL) {
+ status = NT_STATUS_INVALID_CONNECTION;
+ goto failed;
+ }
+
+ req->state = LDAP_REQUEST_SEND;
+ req->conn = conn;
+ req->messageid = conn->next_messageid++;
+ if (conn->next_messageid == 0) {
+ conn->next_messageid = 1;
+ }
+ req->type = msg->type;
+ if (req->messageid == -1) {
+ goto failed;
+ }
+
+ talloc_set_destructor(req, ldap_request_destructor);
+
+ msg->messageid = req->messageid;
+
+ if (!ldap_encode(msg, samba_ldap_control_handlers(), &req->data, req)) {
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto failed;
+ }
+
+ /* put a timeout on the request */
+ req->time_event = tevent_add_timer(conn->event.event_ctx, req,
+ timeval_current_ofs(conn->timeout, 0),
+ ldap_request_timeout, req);
+ if (req->time_event == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+
+ req->write_iov.iov_base = req->data.data;
+ req->write_iov.iov_len = req->data.length;
+
+ subreq = tstream_writev_queue_send(req, conn->event.event_ctx,
+ conn->sockets.active,
+ conn->sockets.send_queue,
+ &req->write_iov, 1);
+ if (subreq == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto failed;
+ }
+ tevent_req_set_callback(subreq, ldap_request_written, req);
+
+ req->state = LDAP_REQUEST_PENDING;
+ DLIST_ADD(conn->pending, req);
+
+ return req;
+
+failed:
+ req->status = status;
+ req->state = LDAP_REQUEST_ERROR;
+ tevent_add_timer(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_failed_complete, req);
+
+ return req;
+}
+
+static void ldap_request_written(struct tevent_req *subreq)
+{
+ struct ldap_request *req =
+ tevent_req_callback_data(subreq,
+ struct ldap_request);
+ int err;
+ ssize_t ret;
+
+ ret = tstream_writev_queue_recv(subreq, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ NTSTATUS error = map_nt_error_from_unix_common(err);
+ ldap_error_handler(req->conn, error);
+ return;
+ }
+
+ if (req->type == LDAP_TAG_AbandonRequest ||
+ req->type == LDAP_TAG_UnbindRequest)
+ {
+ if (req->state == LDAP_REQUEST_PENDING) {
+ DLIST_REMOVE(req->conn->pending, req);
+ }
+ req->state = LDAP_REQUEST_DONE;
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+ return;
+ }
+
+ ldap_connection_recv_next(req->conn);
+}
+
+
+/*
+ wait for a request to complete
+ note that this does not destroy the request
+*/
+_PUBLIC_ NTSTATUS ldap_request_wait(struct ldap_request *req)
+{
+ while (req->state < LDAP_REQUEST_DONE) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ req->state = LDAP_REQUEST_ERROR;
+ req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ break;
+ }
+ }
+ return req->status;
+}
+
+
+/*
+ a mapping of ldap response code to strings
+*/
+static const struct {
+ enum ldap_result_code code;
+ const char *str;
+} ldap_code_map[] = {
+#define _LDAP_MAP_CODE(c) { c, #c }
+ _LDAP_MAP_CODE(LDAP_SUCCESS),
+ _LDAP_MAP_CODE(LDAP_OPERATIONS_ERROR),
+ _LDAP_MAP_CODE(LDAP_PROTOCOL_ERROR),
+ _LDAP_MAP_CODE(LDAP_TIME_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_SIZE_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_COMPARE_FALSE),
+ _LDAP_MAP_CODE(LDAP_COMPARE_TRUE),
+ _LDAP_MAP_CODE(LDAP_AUTH_METHOD_NOT_SUPPORTED),
+ _LDAP_MAP_CODE(LDAP_STRONG_AUTH_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_REFERRAL),
+ _LDAP_MAP_CODE(LDAP_ADMIN_LIMIT_EXCEEDED),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE_CRITICAL_EXTENSION),
+ _LDAP_MAP_CODE(LDAP_CONFIDENTIALITY_REQUIRED),
+ _LDAP_MAP_CODE(LDAP_SASL_BIND_IN_PROGRESS),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_ATTRIBUTE),
+ _LDAP_MAP_CODE(LDAP_UNDEFINED_ATTRIBUTE_TYPE),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_MATCHING),
+ _LDAP_MAP_CODE(LDAP_CONSTRAINT_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_ATTRIBUTE_OR_VALUE_EXISTS),
+ _LDAP_MAP_CODE(LDAP_INVALID_ATTRIBUTE_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_NO_SUCH_OBJECT),
+ _LDAP_MAP_CODE(LDAP_ALIAS_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INVALID_DN_SYNTAX),
+ _LDAP_MAP_CODE(LDAP_ALIAS_DEREFERENCING_PROBLEM),
+ _LDAP_MAP_CODE(LDAP_INAPPROPRIATE_AUTHENTICATION),
+ _LDAP_MAP_CODE(LDAP_INVALID_CREDENTIALS),
+ _LDAP_MAP_CODE(LDAP_INSUFFICIENT_ACCESS_RIGHTS),
+ _LDAP_MAP_CODE(LDAP_BUSY),
+ _LDAP_MAP_CODE(LDAP_UNAVAILABLE),
+ _LDAP_MAP_CODE(LDAP_UNWILLING_TO_PERFORM),
+ _LDAP_MAP_CODE(LDAP_LOOP_DETECT),
+ _LDAP_MAP_CODE(LDAP_NAMING_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_VIOLATION),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_NON_LEAF),
+ _LDAP_MAP_CODE(LDAP_NOT_ALLOWED_ON_RDN),
+ _LDAP_MAP_CODE(LDAP_ENTRY_ALREADY_EXISTS),
+ _LDAP_MAP_CODE(LDAP_OBJECT_CLASS_MODS_PROHIBITED),
+ _LDAP_MAP_CODE(LDAP_AFFECTS_MULTIPLE_DSAS),
+ _LDAP_MAP_CODE(LDAP_OTHER)
+};
+
+/*
+ used to setup the status code from a ldap response
+*/
+_PUBLIC_ NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r)
+{
+ size_t i;
+ const char *codename = "unknown";
+
+ if (r->resultcode == LDAP_SUCCESS) {
+ return NT_STATUS_OK;
+ }
+
+ if (conn->last_error) {
+ talloc_free(conn->last_error);
+ }
+
+ for (i=0;i<ARRAY_SIZE(ldap_code_map);i++) {
+ if ((enum ldap_result_code)r->resultcode == ldap_code_map[i].code) {
+ codename = ldap_code_map[i].str;
+ break;
+ }
+ }
+
+ conn->last_error = talloc_asprintf(conn, "LDAP error %u %s - %s <%s> <%s>",
+ r->resultcode,
+ codename,
+ r->dn?r->dn:"(NULL)",
+ r->errormessage?r->errormessage:"",
+ r->referral?r->referral:"");
+
+ return NT_STATUS_LDAP(r->resultcode);
+}
+
+/*
+ return error string representing the last error
+*/
+_PUBLIC_ const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status)
+{
+ if (NT_STATUS_IS_LDAP(status) && conn->last_error != NULL) {
+ return talloc_strdup(mem_ctx, conn->last_error);
+ }
+ return talloc_asprintf(mem_ctx, "LDAP client internal error: %s", nt_errstr(status));
+}
+
+
+/*
+ return the Nth result message, waiting if necessary
+*/
+_PUBLIC_ NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg)
+{
+ *msg = NULL;
+
+ NT_STATUS_HAVE_NO_MEMORY(req);
+
+ while (req->state < LDAP_REQUEST_DONE && n >= req->num_replies) {
+ if (tevent_loop_once(req->conn->event.event_ctx) != 0) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ }
+
+ if (n < req->num_replies) {
+ *msg = req->replies[n];
+ return NT_STATUS_OK;
+ }
+
+ if (!NT_STATUS_IS_OK(req->status)) {
+ return req->status;
+ }
+
+ return NT_STATUS_NO_MORE_ENTRIES;
+}
+
+
+/*
+ return a single result message, checking if it is of the expected LDAP type
+*/
+_PUBLIC_ NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type)
+{
+ NTSTATUS status;
+ status = ldap_result_n(req, 0, msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if ((*msg) != NULL && (*msg)->type != (enum ldap_request_tag)type) {
+ *msg = NULL;
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+ return status;
+}
diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h
new file mode 100644
index 0000000..e2b1b30
--- /dev/null
+++ b/source4/libcli/ldap/ldap_client.h
@@ -0,0 +1,149 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ ldap client side header
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "system/network.h" /* for struct iovec */
+#include "libcli/ldap/libcli_ldap.h"
+
+enum ldap_request_state { LDAP_REQUEST_SEND=1, LDAP_REQUEST_PENDING=2, LDAP_REQUEST_DONE=3, LDAP_REQUEST_ERROR=4 };
+
+/* this is the handle that the caller gets when an async ldap message
+ is sent */
+struct ldap_request {
+ struct ldap_request *next, *prev;
+ struct ldap_connection *conn;
+
+ enum ldap_request_tag type;
+ int messageid;
+ enum ldap_request_state state;
+
+ int num_replies;
+ struct ldap_message **replies;
+
+ NTSTATUS status;
+ DATA_BLOB data;
+ struct iovec write_iov;
+
+ struct {
+ void (*fn)(struct ldap_request *);
+ void *private_data;
+ } async;
+
+ struct tevent_timer *time_event;
+};
+
+
+/* main context for a ldap client connection */
+struct ldap_connection {
+ struct {
+ struct tstream_context *raw;
+ struct tstream_context *tls;
+ struct tstream_context *sasl;
+ struct tstream_context *active;
+
+ struct tevent_queue *send_queue;
+ struct tevent_req *recv_subreq;
+ } sockets;
+
+ struct loadparm_context *lp_ctx;
+
+ char *host;
+ uint16_t port;
+ bool ldaps;
+
+ const char *auth_dn;
+ const char *simple_pw;
+
+ struct {
+ char *url;
+ int max_retries;
+ int retries;
+ time_t previous;
+ } reconnect;
+
+ struct {
+ enum { LDAP_BIND_SIMPLE, LDAP_BIND_SASL } type;
+ void *creds;
+ } bind;
+
+ /* next message id to assign */
+ unsigned next_messageid;
+
+ /* Outstanding LDAP requests that have not yet been replied to */
+ struct ldap_request *pending;
+
+ /* Let's support SASL */
+ struct gensec_security *gensec;
+
+ /* the default timeout for messages */
+ int timeout;
+
+ /* last error message */
+ char *last_error;
+
+ struct {
+ struct tevent_context *event_ctx;
+ } event;
+};
+
+struct ldap_connection *ldap4_new_connection(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev);
+
+NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url);
+struct composite_context *ldap_connect_send(struct ldap_connection *conn,
+ const char *url);
+
+NTSTATUS ldap_rebind(struct ldap_connection *conn);
+NTSTATUS ldap_bind_simple(struct ldap_connection *conn,
+ const char *userdn, const char *password);
+NTSTATUS ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx);
+struct ldap_request *ldap_request_send(struct ldap_connection *conn,
+ struct ldap_message *msg);
+NTSTATUS ldap_request_wait(struct ldap_request *req);
+struct composite_context;
+NTSTATUS ldap_connect_recv(struct composite_context *ctx);
+NTSTATUS ldap_result_n(struct ldap_request *req, int n, struct ldap_message **msg);
+NTSTATUS ldap_result_one(struct ldap_request *req, struct ldap_message **msg, int type);
+NTSTATUS ldap_transaction(struct ldap_connection *conn, struct ldap_message *msg);
+const char *ldap_errstr(struct ldap_connection *conn,
+ TALLOC_CTX *mem_ctx,
+ NTSTATUS status);
+NTSTATUS ldap_check_response(struct ldap_connection *conn, struct ldap_Result *r);
+void ldap_set_reconn_params(struct ldap_connection *conn, int max_retries);
+int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res);
+NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results);
+
+
+
diff --git a/source4/libcli/ldap/ldap_controls.c b/source4/libcli/ldap/ldap_controls.c
new file mode 100644
index 0000000..a1d421b
--- /dev/null
+++ b/source4/libcli/ldap/ldap_controls.c
@@ -0,0 +1,1288 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Simo Sorce 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+
+#include <ldb.h>
+
+#include "../lib/util/asn1.h"
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_proto.h"
+#include "dsdb/samdb/samdb.h"
+
+static bool decode_server_sort_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB attr;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_sort_resp_control *lsrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsrc = talloc(mem_ctx, struct ldb_sort_resp_control);
+ if (!lsrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lsrc->result))) {
+ return false;
+ }
+
+ lsrc->attr_desc = NULL;
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+ lsrc->attr_desc = talloc_strndup(lsrc, (const char *)attr.data, attr.length);
+ if (!lsrc->attr_desc) {
+ return false;
+ }
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsrc;
+
+ return true;
+}
+
+static bool decode_server_sort_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB attr;
+ DATA_BLOB rule;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_server_sort_control **lssc;
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ lssc = NULL;
+
+ for (num = 0; asn1_peek_tag(data, ASN1_SEQUENCE(0)); num++) {
+ lssc = talloc_realloc(mem_ctx, lssc, struct ldb_server_sort_control *, num + 2);
+ if (!lssc) {
+ return false;
+ }
+ lssc[num] = talloc_zero(lssc, struct ldb_server_sort_control);
+ if (!lssc[num]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+ return false;
+ }
+
+ lssc[num]->attributeName = talloc_strndup(lssc[num], (const char *)attr.data, attr.length);
+ if (!lssc [num]->attributeName) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(0))) {
+ if (!asn1_read_ContextSimple(data, mem_ctx, 0, &rule)) {
+ return false;
+ }
+ lssc[num]->orderingRule = talloc_strndup(lssc[num], (const char *)rule.data, rule.length);
+ if (!lssc[num]->orderingRule) {
+ return false;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) {
+ bool reverse;
+ if (!asn1_read_BOOLEAN_context(data, &reverse, 1)) {
+ return false;
+ }
+ lssc[num]->reverse = reverse;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+
+ if (lssc != NULL) {
+ lssc[num] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lssc;
+
+ return true;
+}
+
+static bool decode_extended_dn_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ struct asn1_data *data;
+ struct ldb_extended_dn_control *ledc;
+
+ /* The content of this control is optional */
+ if (in.length == 0) {
+ *out = NULL;
+ return true;
+ }
+
+ data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ledc = talloc(mem_ctx, struct ldb_extended_dn_control);
+ if (!ledc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ledc->type))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ledc;
+
+ return true;
+}
+
+static bool decode_sd_flags_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_sd_flags_control *lsdfc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsdfc = talloc(mem_ctx, struct ldb_sd_flags_control);
+ if (!lsdfc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, (int *) &(lsdfc->secinfo_flags))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsdfc;
+
+ return true;
+}
+
+static bool decode_search_options_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_search_options_control *lsoc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lsoc = talloc(mem_ctx, struct ldb_search_options_control);
+ if (!lsoc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, (int *) &(lsoc->search_options))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lsoc;
+
+ return true;
+}
+
+static bool decode_paged_results_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_paged_control *lprc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lprc = talloc(mem_ctx, struct ldb_paged_control);
+ if (!lprc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lprc->size))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ lprc->cookie_len = cookie.length;
+ if (lprc->cookie_len) {
+ lprc->cookie = talloc_memdup(lprc, cookie.data, cookie.length);
+
+ if (!(lprc->cookie)) {
+ return false;
+ }
+ } else {
+ lprc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lprc;
+
+ return true;
+}
+
+static bool decode_dirsync_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB cookie;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_dirsync_control *ldc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ ldc = talloc(mem_ctx, struct ldb_dirsync_control);
+ if (!ldc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->flags))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(ldc->max_attributes))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &cookie)) {
+ return false;
+ }
+ ldc->cookie_len = cookie.length;
+ if (ldc->cookie_len) {
+ ldc->cookie = talloc_memdup(ldc, cookie.data, cookie.length);
+
+ if (!(ldc->cookie)) {
+ return false;
+ }
+ } else {
+ ldc->cookie = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = ldc;
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool decode_asq_control(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB source_attribute;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_asq_control *lac;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lac = talloc(mem_ctx, struct ldb_asq_control);
+ if (!lac) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+
+ if (!asn1_read_OctetString(data, mem_ctx, &source_attribute)) {
+ return false;
+ }
+ lac->src_attr_len = source_attribute.length;
+ if (lac->src_attr_len) {
+ lac->source_attribute = talloc_strndup(lac, (const char *)source_attribute.data, source_attribute.length);
+
+ if (!(lac->source_attribute)) {
+ return false;
+ }
+ } else {
+ lac->source_attribute = NULL;
+ }
+
+ lac->request = 1;
+
+ } else if (asn1_peek_tag(data, ASN1_ENUMERATED)) {
+
+ if (!asn1_read_enumerated(data, &(lac->result))) {
+ return false;
+ }
+
+ lac->request = 0;
+
+ } else {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lac;
+
+ return true;
+}
+
+static bool decode_verify_name_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB name;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_verify_name_control *lvnc;
+ int len;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvnc = talloc(mem_ctx, struct ldb_verify_name_control);
+ if (!lvnc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvnc->flags))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString(data, mem_ctx, &name)) {
+ return false;
+ }
+
+ if (name.length) {
+ len = utf16_null_terminated_len_n(name.data, name.length);
+ convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
+ name.data, len,
+ &lvnc->gc, &lvnc->gc_len);
+
+ if (!(lvnc->gc)) {
+ return false;
+ }
+ } else {
+ lvnc->gc_len = 0;
+ lvnc->gc = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvnc;
+ return true;
+}
+
+static bool encode_verify_name_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_verify_name_control *lvnc = talloc_get_type(in, struct ldb_verify_name_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ DATA_BLOB gc_utf16;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvnc->flags)) {
+ return false;
+ }
+
+ if (lvnc->gc_len) {
+ bool ok;
+
+ ok = convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16,
+ lvnc->gc, lvnc->gc_len,
+ &gc_utf16.data, &gc_utf16.length);
+ if (!ok) {
+ return false;
+ }
+ if (!asn1_write_OctetString(data, gc_utf16.data, gc_utf16.length)) {
+ return false;
+ }
+ } else {
+ if (!asn1_write_OctetString(data, NULL, 0)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool decode_vlv_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB assertion_value, context_id;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_vlv_req_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_req_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->beforeCount))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->afterCount))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+
+ lvrc->type = 0;
+
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.offset))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->match.byOffset.contentCount))) {
+ return false;
+ }
+
+ if (!asn1_end_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+
+ } else {
+
+ lvrc->type = 1;
+
+ if (!asn1_read_ContextSimple(data, mem_ctx, 1, &assertion_value)){
+ return false;
+ }
+
+ lvrc->match.gtOrEq.value_len = assertion_value.length;
+ if (lvrc->match.gtOrEq.value_len) {
+ lvrc->match.gtOrEq.value = talloc_memdup(lvrc, assertion_value.data, assertion_value.length);
+
+ if (!(lvrc->match.gtOrEq.value)) {
+ return false;
+ }
+ } else {
+ lvrc->match.gtOrEq.value = NULL;
+ }
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ if (lvrc->ctxid_len) {
+ lvrc->contextId = talloc_memdup(lvrc, context_id.data, context_id.length);
+
+ if (!(lvrc->contextId)) {
+ return false;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ }
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool decode_vlv_response(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ DATA_BLOB context_id;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct ldb_vlv_resp_control *lvrc;
+
+ if (!data) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ lvrc = talloc(mem_ctx, struct ldb_vlv_resp_control);
+ if (!lvrc) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->targetPosition))) {
+ return false;
+ }
+
+ if (!asn1_read_Integer(data, &(lvrc->contentCount))) {
+ return false;
+ }
+
+ if (!asn1_read_enumerated(data, &(lvrc->vlv_result))) {
+ return false;
+ }
+
+ if (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+ if (!asn1_read_OctetString(data, mem_ctx, &context_id)) {
+ return false;
+ }
+ lvrc->contextId = talloc_memdup(lvrc, (const char *)context_id.data, context_id.length);
+ if (!lvrc->contextId) {
+ return false;
+ }
+ lvrc->ctxid_len = context_id.length;
+ } else {
+ lvrc->contextId = NULL;
+ lvrc->ctxid_len = 0;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ *out = lvrc;
+
+ return true;
+}
+
+static bool encode_server_sort_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sort_resp_control *lsrc = talloc_get_type(in, struct ldb_sort_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lsrc->result)) {
+ return false;
+ }
+
+ if (lsrc->attr_desc) {
+ if (!asn1_write_OctetString(data, lsrc->attr_desc, strlen(lsrc->attr_desc))) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_server_sort_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_server_sort_control **lssc = talloc_get_type(in, struct ldb_server_sort_control *);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ int num;
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ /*
+ RFC2891 section 1.1:
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeDescription,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+ */
+ for (num = 0; lssc[num]; num++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lssc[num]->attributeName, strlen(lssc[num]->attributeName))) {
+ return false;
+ }
+
+ if (lssc[num]->orderingRule) {
+ DATA_BLOB order = data_blob_string_const(lssc[num]->orderingRule);
+ if (!asn1_write_ContextSimple(data, 0, &order)) {
+ return false;
+ }
+ }
+
+ if (lssc[num]->reverse) {
+ if (!asn1_write_BOOLEAN_context(data, lssc[num]->reverse, 1)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_extended_dn_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_extended_dn_control *ledc = talloc_get_type(in, struct ldb_extended_dn_control);
+ struct asn1_data *data;
+
+ if (!in) {
+ *out = data_blob(NULL, 0);
+ return true;
+ }
+
+ data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ledc->type)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_sd_flags_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_sd_flags_control *lsdfc = talloc_get_type(in, struct ldb_sd_flags_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsdfc->secinfo_flags)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_search_options_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_search_options_control *lsoc = talloc_get_type(in, struct ldb_search_options_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lsoc->search_options)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_paged_results_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_paged_control *lprc = talloc_get_type(in, struct ldb_paged_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lprc->size)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, lprc->cookie, lprc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+/* seem that this controls has 2 forms one in case it is used with
+ * a Search Request and another when used ina Search Response
+ */
+static bool encode_asq_control(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_asq_control *lac = talloc_get_type(in, struct ldb_asq_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (lac->request) {
+
+ if (!asn1_write_OctetString(data, lac->source_attribute, lac->src_attr_len)) {
+ return false;
+ }
+ } else {
+ if (!asn1_write_enumerated(data, lac->result)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_dirsync_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_dirsync_control *ldc = talloc_get_type(in, struct ldb_dirsync_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->flags)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, ldc->max_attributes)) {
+ return false;
+ }
+
+ if (!asn1_write_OctetString(data, ldc->cookie, ldc->cookie_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_vlv_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_req_control *lvrc = talloc_get_type(in, struct ldb_vlv_req_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->beforeCount)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->afterCount)) {
+ return false;
+ }
+
+ if (lvrc->type == 0) {
+ if (!asn1_push_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.offset)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->match.byOffset.contentCount)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ } else {
+ if (!asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1))) {
+ return false;
+ }
+
+ if (!asn1_write(data, lvrc->match.gtOrEq.value, lvrc->match.gtOrEq.value_len)) {
+ return false;
+ }
+
+ if (!asn1_pop_tag(data)) { /*CONTEXT*/
+ return false;
+ }
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_vlv_response(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct ldb_vlv_resp_control *lvrc = talloc_get_type(in, struct ldb_vlv_resp_control);
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->targetPosition)) {
+ return false;
+ }
+
+ if (!asn1_write_Integer(data, lvrc->contentCount)) {
+ return false;
+ }
+
+ if (!asn1_write_enumerated(data, lvrc->vlv_result)) {
+ return false;
+ }
+
+ if (lvrc->ctxid_len) {
+ if (!asn1_write_OctetString(data, lvrc->contextId, lvrc->ctxid_len)) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+
+ return true;
+}
+
+static bool encode_openldap_dereference(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ struct dsdb_openldap_dereference_control *control = talloc_get_type(in, struct dsdb_openldap_dereference_control);
+ int i,j;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+
+ if (!data) return false;
+
+ if (!control) return false;
+
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ for (i=0; control->dereference && control->dereference[i]; i++) {
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ if (!asn1_write_OctetString(data, control->dereference[i]->source_attribute, strlen(control->dereference[i]->source_attribute))) {
+ return false;
+ }
+ if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+ for (j=0; control->dereference && control->dereference[i]->dereference_attribute[j]; j++) {
+ if (!asn1_write_OctetString(data, control->dereference[i]->dereference_attribute[j],
+ strlen(control->dereference[i]->dereference_attribute[j]))) {
+ return false;
+ }
+ }
+
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+ }
+ if (!asn1_pop_tag(data)) {
+ return false;
+ }
+
+ if (!asn1_extract_blob(data, mem_ctx, out)) {
+ return false;
+ }
+
+ talloc_free(data);
+ return true;
+}
+
+static bool decode_openldap_dereference(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ void **out = _out;
+ struct asn1_data *data = asn1_init(mem_ctx, ASN1_MAX_TREE_DEPTH);
+ struct dsdb_openldap_dereference_result_control *control;
+ struct dsdb_openldap_dereference_result **r = NULL;
+ int i = 0;
+ if (!data) return false;
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) return false;
+
+ if (!asn1_load(data, in)) {
+ return false;
+ }
+
+ control = talloc(mem_ctx, struct dsdb_openldap_dereference_result_control);
+ if (!control) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ while (asn1_tag_remaining(data) > 0) {
+ r = talloc_realloc(control, r, struct dsdb_openldap_dereference_result *, i + 2);
+ if (!r) {
+ return false;
+ }
+ r[i] = talloc_zero(r, struct dsdb_openldap_dereference_result);
+ if (!r[i]) {
+ return false;
+ }
+
+ if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+ return false;
+ }
+
+ if (!asn1_read_OctetString_talloc(r[i], data, &r[i]->source_attribute)) {
+ return false;
+ }
+ if (!asn1_read_OctetString_talloc(r[i], data, &r[i]->dereferenced_dn)) {
+ return false;
+ }
+ if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+ if (!asn1_start_tag(data, ASN1_CONTEXT(0))) {
+ return false;
+ }
+ if (!ldap_decode_attribs_bare(r, data, &r[i]->attributes,
+ &r[i]->num_attributes)) {
+ return false;
+ }
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ }
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+ i++;
+ r[i] = NULL;
+ }
+
+ if (!asn1_end_tag(data)) {
+ return false;
+ }
+
+ control->attributes = r;
+ *out = control;
+
+ return true;
+}
+
+static bool encode_flag_request(void *mem_ctx, void *in, DATA_BLOB *out)
+{
+ if (in) {
+ return false;
+ }
+
+ *out = data_blob(NULL, 0);
+ return true;
+}
+
+static bool decode_flag_request(void *mem_ctx, DATA_BLOB in, void *_out)
+{
+ if (in.length != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static const struct ldap_control_handler ldap_known_controls[] = {
+ { LDB_CONTROL_PAGED_RESULTS_OID, decode_paged_results_request, encode_paged_results_request },
+ { LDB_CONTROL_SD_FLAGS_OID, decode_sd_flags_request, encode_sd_flags_request },
+ { LDB_CONTROL_DOMAIN_SCOPE_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SEARCH_OPTIONS_OID, decode_search_options_request, encode_search_options_request },
+ { LDB_CONTROL_NOTIFICATION_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_TREE_DELETE_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_DELETED_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_RECYCLED_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SHOW_DEACTIVATED_LINK_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_EXTENDED_DN_OID, decode_extended_dn_request, encode_extended_dn_request },
+ { LDB_CONTROL_SERVER_SORT_OID, decode_server_sort_request, encode_server_sort_request },
+ { LDB_CONTROL_SORT_RESP_OID, decode_server_sort_response, encode_server_sort_response },
+ { LDB_CONTROL_ASQ_OID, decode_asq_control, encode_asq_control },
+ { LDB_CONTROL_DIRSYNC_OID, decode_dirsync_request, encode_dirsync_request },
+ { LDB_CONTROL_DIRSYNC_EX_OID, decode_dirsync_request, encode_dirsync_request },
+ { LDB_CONTROL_VLV_REQ_OID, decode_vlv_request, encode_vlv_request },
+ { LDB_CONTROL_VLV_RESP_OID, decode_vlv_response, encode_vlv_response },
+ { LDB_CONTROL_PERMISSIVE_MODIFY_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_SERVER_LAZY_COMMIT, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_RODC_DCPROMO_OID, decode_flag_request, encode_flag_request },
+ { LDB_CONTROL_RELAX_OID, decode_flag_request, encode_flag_request },
+ { DSDB_OPENLDAP_DEREFERENCE_CONTROL, decode_openldap_dereference, encode_openldap_dereference },
+ { LDB_CONTROL_VERIFY_NAME_OID, decode_verify_name_request, encode_verify_name_request },
+
+ /* the following are internal only, with a network
+ representation */
+ { DSDB_CONTROL_BYPASS_PASSWORD_HASH_OID, decode_flag_request, encode_flag_request },
+
+ /* all the ones below are internal only, and have no network
+ * representation */
+ { DSDB_CONTROL_CURRENT_PARTITION_OID, NULL, NULL },
+ { DSDB_CONTROL_REPLICATED_UPDATE_OID, NULL, NULL },
+ { DSDB_CONTROL_DN_STORAGE_FORMAT_OID, NULL, NULL },
+ { LDB_CONTROL_RECALCULATE_SD_OID, NULL, NULL },
+ { LDB_CONTROL_REVEAL_INTERNALS, NULL, NULL },
+ { LDB_CONTROL_AS_SYSTEM_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_CHANGE_STATUS_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_HASH_VALUES_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_CHANGE_OLD_PW_CHECKED_OID, NULL, NULL },
+ { DSDB_CONTROL_PASSWORD_ACL_VALIDATION_OID, NULL, NULL },
+ { DSDB_CONTROL_APPLY_LINKS, NULL, NULL },
+ { LDB_CONTROL_BYPASS_OPERATIONAL_OID, NULL, NULL },
+ { DSDB_CONTROL_CHANGEREPLMETADATA_OID, NULL, NULL },
+ { LDB_CONTROL_PROVISION_OID, NULL, NULL },
+ { DSDB_EXTENDED_REPLICATED_OBJECTS_OID, NULL, NULL },
+ { DSDB_EXTENDED_SCHEMA_UPDATE_NOW_OID, NULL, NULL },
+ { DSDB_EXTENDED_ALLOCATE_RID_POOL, NULL, NULL },
+ { DSDB_CONTROL_NO_GLOBAL_CATALOG, NULL, NULL },
+ { DSDB_EXTENDED_SCHEMA_UPGRADE_IN_PROGRESS_OID, NULL, NULL },
+ { DSDB_CONTROL_TRANSACTION_IDENTIFIER_OID, NULL, NULL},
+ { DSDB_CONTROL_CALCULATED_DEFAULT_SD_OID, NULL, NULL },
+ { NULL, NULL, NULL }
+};
+
+const struct ldap_control_handler *samba_ldap_control_handlers(void)
+{
+ return ldap_known_controls;
+}
diff --git a/source4/libcli/ldap/ldap_ildap.c b/source4/libcli/ldap/ldap_ildap.c
new file mode 100644
index 0000000..a06e3b6
--- /dev/null
+++ b/source4/libcli/ldap/ldap_ildap.c
@@ -0,0 +1,133 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ ildap api - an api similar to the traditional ldap api
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/libcli_ldap.h"
+#include "libcli/ldap/ldap_client.h"
+
+
+/*
+ count the returned search entries
+*/
+_PUBLIC_ int ildap_count_entries(struct ldap_connection *conn, struct ldap_message **res)
+{
+ int i;
+ for (i=0;res && res[i];i++) /* noop */ ;
+ return i;
+}
+
+
+/*
+ perform a synchronous ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search_bytree(struct ldap_connection *conn, const char *basedn,
+ int scope, struct ldb_parse_tree *tree,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ struct ldap_message *msg;
+ int n, i;
+ NTSTATUS status;
+ struct ldap_request *req;
+
+ if (control_res)
+ *control_res = NULL;
+ *results = NULL;
+
+ msg = new_ldap_message(conn);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ for (n=0;attrs && attrs[n];n++) /* noop */ ;
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = basedn;
+ msg->r.SearchRequest.scope = scope;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = attributesonly;
+ msg->r.SearchRequest.tree = tree;
+ msg->r.SearchRequest.num_attributes = n;
+ msg->r.SearchRequest.attributes = attrs;
+ msg->controls = control_req;
+
+ req = ldap_request_send(conn, msg);
+ talloc_reparent(conn, msg, req);
+
+ for (i=n=0;true;i++) {
+ struct ldap_message *res;
+ status = ldap_result_n(req, i, &res);
+ if (!NT_STATUS_IS_OK(status)) break;
+
+ if (res->type == LDAP_TAG_SearchResultDone) {
+ status = ldap_check_response(conn, &res->r.GeneralResult);
+ if (control_res) {
+ *control_res = talloc_steal(conn, res->controls);
+ }
+ break;
+ }
+
+ if (res->type != LDAP_TAG_SearchResultEntry &&
+ res->type != LDAP_TAG_SearchResultReference)
+ continue;
+
+ (*results) = talloc_realloc(conn, *results, struct ldap_message *, n+2);
+ if (*results == NULL) {
+ talloc_free(msg);
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*results)[n] = talloc_steal(*results, res);
+ (*results)[n+1] = NULL;
+ n++;
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+/*
+ perform a ldap search
+*/
+_PUBLIC_ NTSTATUS ildap_search(struct ldap_connection *conn, const char *basedn,
+ int scope, const char *expression,
+ const char * const *attrs, bool attributesonly,
+ struct ldb_control **control_req,
+ struct ldb_control ***control_res,
+ struct ldap_message ***results)
+{
+ NTSTATUS status;
+ struct ldb_parse_tree *tree = ldb_parse_tree(conn, expression);
+
+ if (tree == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ status = ildap_search_bytree(conn, basedn, scope, tree, attrs,
+ attributesonly, control_req,
+ control_res, results);
+ talloc_free(tree);
+ return status;
+}
diff --git a/source4/libcli/ldap/libcli_ldap.h b/source4/libcli/ldap/libcli_ldap.h
new file mode 100644
index 0000000..79cfef2
--- /dev/null
+++ b/source4/libcli/ldap/libcli_ldap.h
@@ -0,0 +1,31 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef _SMB_LDAP_H_
+#define _SMB_LDAP_H_
+
+#include "../libcli/ldap/ldap_message.h"
+#include "librpc/gen_ndr/misc.h"
+
+struct tevent_context;
+struct cli_credentials;
+struct dom_sid;
+
+#endif
diff --git a/source4/libcli/ldap/wscript_build b/source4/libcli/ldap/wscript_build
new file mode 100644
index 0000000..4588233
--- /dev/null
+++ b/source4/libcli/ldap/wscript_build
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('cli-ldap',
+ source='ldap_client.c ldap_bind.c ldap_ildap.c ldap_controls.c',
+ autoproto='ldap_proto.h',
+ public_deps='samba-errors tevent',
+ private_headers='libcli_ldap.h:ldap-util.h',
+ deps='cli_composite ldb LIBSAMBA_TSOCKET samba_socket NDR_SAMR LIBTLS ndr LP_RESOLVE gensec cli-ldap-common',
+ private_library=True
+ )
+