summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source4/libcli/ldap/ldap.c8
-rw-r--r--source4/libcli/ldap/ldap_client.c286
-rw-r--r--source4/libcli/ldap/ldap_client.h8
-rw-r--r--source4/libcli/util/asn1.c27
4 files changed, 156 insertions, 173 deletions
diff --git a/source4/libcli/ldap/ldap.c b/source4/libcli/ldap/ldap.c
index 7d1758a8fa..3cfbe3a1e1 100644
--- a/source4/libcli/ldap/ldap.c
+++ b/source4/libcli/ldap/ldap.c
@@ -1289,3 +1289,11 @@ BOOL ldap_decode(struct asn1_data *data, struct ldap_message *msg)
}
+/*
+ return NT_STATUS_OK if a blob has enough bytes in it to be a full
+ ldap packet. Set packet_size if true.
+*/
+NTSTATUS ldap_full_packet(void *private, DATA_BLOB blob, size_t *packet_size)
+{
+ return asn1_full_tag(blob, ASN1_SEQUENCE(0), packet_size);
+}
diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c
index 3b801ca225..503016e896 100644
--- a/source4/libcli/ldap/ldap_client.c
+++ b/source4/libcli/ldap/ldap_client.c
@@ -32,6 +32,7 @@
#include "libcli/ldap/ldap.h"
#include "libcli/ldap/ldap_client.h"
#include "libcli/composite/composite.h"
+#include "lib/stream/packet.h"
/*
@@ -82,20 +83,20 @@ static void ldap_connection_dead(struct ldap_connection *conn)
}
}
- while (conn->send_queue) {
- req = conn->send_queue;
- DLIST_REMOVE(req->conn->send_queue, req);
- req->state = LDAP_REQUEST_DONE;
- req->status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
- if (req->async.fn) {
- req->async.fn(req);
- }
- }
-
talloc_free(conn->tls);
conn->tls = NULL;
}
+/*
+ handle packet errors
+*/
+static void ldap_error_handler(void *private, NTSTATUS status)
+{
+ struct ldap_connection *conn = talloc_get_type(private,
+ struct ldap_connection);
+ ldap_connection_dead(conn);
+}
+
/*
match up with a pending message, adding to the replies list
@@ -149,184 +150,99 @@ static void ldap_match_message(struct ldap_connection *conn, struct ldap_message
}
}
+
/*
- try and decode/process plain data
+ check if a blob is a complete ldap packet
+ handle wrapper or unwrapped connections
*/
-static void ldap_try_decode_plain(struct ldap_connection *conn)
+NTSTATUS ldap_complete_packet(void *private, DATA_BLOB blob, size_t *size)
{
- struct asn1_data asn1;
-
- if (!asn1_load(&asn1, conn->partial)) {
- ldap_connection_dead(conn);
- return;
- }
-
- /* try and decode - this will fail if we don't have a full packet yet */
- while (asn1.ofs < asn1.length) {
- struct ldap_message *msg = talloc(conn, struct ldap_message);
- off_t saved_ofs = asn1.ofs;
-
- if (msg == NULL) {
- ldap_connection_dead(conn);
- return;
- }
-
- if (ldap_decode(&asn1, msg)) {
- ldap_match_message(conn, msg);
- } else {
- asn1.ofs = saved_ofs;
- talloc_free(msg);
- break;
- }
- }
-
- /* keep any remaining data in conn->partial */
- data_blob_free(&conn->partial);
- if (asn1.ofs != asn1.length) {
- conn->partial = data_blob_talloc(conn,
- asn1.data + asn1.ofs,
- asn1.length - asn1.ofs);
+ struct ldap_connection *conn = talloc_get_type(private,
+ struct ldap_connection);
+ if (conn->enable_wrap) {
+ return packet_full_request_u32(private, blob, size);
}
- asn1_free(&asn1);
+ return ldap_full_packet(private, blob, size);
}
/*
- try and decode/process wrapped data
+ decode/process plain data
*/
-static void ldap_try_decode_wrapped(struct ldap_connection *conn)
+static NTSTATUS ldap_decode_plain(struct ldap_connection *conn, DATA_BLOB blob)
{
- uint32_t len;
-
- /* keep decoding while we have a full wrapped packet */
- while (conn->partial.length >= 4 &&
- (len=RIVAL(conn->partial.data, 0)) <= conn->partial.length-4) {
- DATA_BLOB wrapped, unwrapped;
- struct asn1_data asn1;
- struct ldap_message *msg = talloc(conn, struct ldap_message);
- NTSTATUS status;
-
- if (msg == NULL) {
- ldap_connection_dead(conn);
- return;
- }
+ struct asn1_data asn1;
+ struct ldap_message *msg = talloc(conn, struct ldap_message);
- wrapped.data = conn->partial.data+4;
- wrapped.length = len;
+ if (msg == NULL) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
- status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
- if (!NT_STATUS_IS_OK(status)) {
- ldap_connection_dead(conn);
- return;
- }
+ if (!asn1_load(&asn1, blob)) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+
+ if (!ldap_decode(&asn1, msg)) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
- if (!asn1_load(&asn1, unwrapped)) {
- ldap_connection_dead(conn);
- return;
- }
+ ldap_match_message(conn, msg);
- while (ldap_decode(&asn1, msg)) {
- ldap_match_message(conn, msg);
- msg = talloc(conn, struct ldap_message);
- }
-
- talloc_free(msg);
- asn1_free(&asn1);
-
- if (conn->partial.length == len + 4) {
- data_blob_free(&conn->partial);
- } else {
- memmove(conn->partial.data, conn->partial.data+len+4,
- conn->partial.length - (len+4));
- conn->partial.length -= len + 4;
- }
- }
+ data_blob_free(&blob);
+ asn1_free(&asn1);
+ return NT_STATUS_OK;
}
-
/*
- handle ldap recv events
+ decode/process wrapped data
*/
-static void ldap_recv_handler(struct ldap_connection *conn)
+static NTSTATUS ldap_decode_wrapped(struct ldap_connection *conn, DATA_BLOB blob)
{
+ DATA_BLOB wrapped, unwrapped;
+ struct asn1_data asn1;
+ struct ldap_message *msg = talloc(conn, struct ldap_message);
NTSTATUS status;
- size_t npending=0, nread;
- /* work out how much data is pending */
- status = tls_socket_pending(conn->tls, &npending);
- if (!NT_STATUS_IS_OK(status) || npending == 0) {
- ldap_connection_dead(conn);
- return;
+ if (msg == NULL) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
}
- conn->partial.data = talloc_realloc_size(conn, conn->partial.data,
- conn->partial.length + npending);
- if (conn->partial.data == NULL) {
- ldap_connection_dead(conn);
- return;
- }
+ wrapped = data_blob_const(blob.data+4, blob.length-4);
- /* receive the pending data */
- status = tls_socket_recv(conn->tls, conn->partial.data + conn->partial.length,
- npending, &nread);
- if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
- return;
- }
+ status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped);
if (!NT_STATUS_IS_OK(status)) {
- ldap_connection_dead(conn);
- return;
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
}
- conn->partial.length += nread;
- /* see if we can decode what we have */
- if (conn->enable_wrap) {
- ldap_try_decode_wrapped(conn);
- } else {
- ldap_try_decode_plain(conn);
+ data_blob_free(&blob);
+
+ if (!asn1_load(&asn1, unwrapped)) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
}
+
+ while (ldap_decode(&asn1, msg)) {
+ ldap_match_message(conn, msg);
+ msg = talloc(conn, struct ldap_message);
+ }
+
+ talloc_free(msg);
+ asn1_free(&asn1);
+
+ return NT_STATUS_OK;
}
/*
- handle ldap send events
+ handle ldap recv events
*/
-static void ldap_send_handler(struct ldap_connection *conn)
+static NTSTATUS ldap_recv_handler(void *private, DATA_BLOB blob)
{
- while (conn->send_queue) {
- struct ldap_request *req = conn->send_queue;
- size_t nsent;
- NTSTATUS status;
-
- status = tls_socket_send(conn->tls, &req->data, &nsent);
- if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
- break;
- }
- if (!NT_STATUS_IS_OK(status)) {
- ldap_connection_dead(conn);
- return;
- }
-
- req->data.data += nsent;
- req->data.length -= nsent;
- if (req->data.length == 0) {
- req->state = LDAP_REQUEST_PENDING;
- DLIST_REMOVE(conn->send_queue, req);
-
- /* some types of requests don't expect a reply */
- if (req->type == LDAP_TAG_AbandonRequest ||
- req->type == LDAP_TAG_UnbindRequest) {
- req->status = NT_STATUS_OK;
- req->state = LDAP_REQUEST_DONE;
- if (req->async.fn) {
- req->async.fn(req);
- }
- } else {
- DLIST_ADD(conn->pending, req);
- }
- }
- }
- if (conn->send_queue == NULL) {
- EVENT_FD_NOT_WRITEABLE(conn->event.fde);
+ struct ldap_connection *conn = talloc_get_type(private,
+ struct ldap_connection);
+ if (conn->enable_wrap) {
+ return ldap_decode_wrapped(conn, blob);
}
+
+ return ldap_decode_plain(conn, blob);
}
@@ -336,13 +252,14 @@ static void ldap_send_handler(struct ldap_connection *conn)
static void ldap_io_handler(struct event_context *ev, struct fd_event *fde,
uint16_t flags, void *private)
{
- struct ldap_connection *conn = talloc_get_type(private, struct ldap_connection);
+ struct ldap_connection *conn = talloc_get_type(private,
+ struct ldap_connection);
if (flags & EVENT_FD_WRITE) {
- ldap_send_handler(conn);
+ packet_queue_run(conn->packet);
if (conn->tls == NULL) return;
}
if (flags & EVENT_FD_READ) {
- ldap_recv_handler(conn);
+ packet_recv(conn->packet);
}
}
@@ -470,6 +387,19 @@ static void ldap_connect_recv_conn(struct composite_context *ctx)
talloc_steal(conn, conn->tls);
talloc_steal(conn->tls, conn->sock);
+ conn->packet = packet_init(conn);
+ if (conn->packet == NULL) {
+ talloc_free(conn->sock);
+ return;
+ }
+ packet_set_private(conn->packet, conn);
+ packet_set_tls(conn->packet, conn->tls);
+ packet_set_callback(conn->packet, ldap_recv_handler);
+ packet_set_full_request(conn->packet, ldap_complete_packet);
+ packet_set_error_handler(conn->packet, ldap_error_handler);
+ packet_set_event_context(conn->packet, conn->event.event_ctx);
+ packet_set_serialise(conn->packet, conn->event.fde);
+
composite_done(state->ctx);
return;
@@ -492,9 +422,6 @@ NTSTATUS ldap_connect(struct ldap_connection *conn, const char *url)
static int ldap_request_destructor(void *ptr)
{
struct ldap_request *req = talloc_get_type(ptr, struct ldap_request);
- if (req->state == LDAP_REQUEST_SEND) {
- DLIST_REMOVE(req->conn->send_queue, req);
- }
if (req->state == LDAP_REQUEST_PENDING) {
DLIST_REMOVE(req->conn->pending, req);
}
@@ -509,9 +436,6 @@ static void ldap_request_timeout(struct event_context *ev, struct timed_event *t
{
struct ldap_request *req = talloc_get_type(private, struct ldap_request);
req->status = NT_STATUS_IO_TIMEOUT;
- if (req->state == LDAP_REQUEST_SEND) {
- DLIST_REMOVE(req->conn->send_queue, req);
- }
if (req->state == LDAP_REQUEST_PENDING) {
DLIST_REMOVE(req->conn->pending, req);
}
@@ -521,6 +445,19 @@ static void ldap_request_timeout(struct event_context *ev, struct timed_event *t
}
}
+
+/*
+ called on completion of a one-way ldap request
+*/
+static void ldap_request_complete(struct event_context *ev, struct timed_event *te,
+ struct timeval t, void *private)
+{
+ struct ldap_request *req = talloc_get_type(private, struct ldap_request);
+ if (req->async.fn) {
+ req->async.fn(req);
+ }
+}
+
/*
send a ldap message - async interface
*/
@@ -528,6 +465,7 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
struct ldap_message *msg)
{
struct ldap_request *req;
+ NTSTATUS status;
if (conn->tls == NULL) {
return NULL;
@@ -558,7 +496,6 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
/* possibly encrypt/sign the request */
if (conn->enable_wrap) {
DATA_BLOB wrapped;
- NTSTATUS status;
status = gensec_wrap(conn->gensec, req, &req->data, &wrapped);
if (!NT_STATUS_IS_OK(status)) {
@@ -574,11 +511,26 @@ struct ldap_request *ldap_request_send(struct ldap_connection *conn,
data_blob_free(&wrapped);
}
+ status = packet_send(conn->packet, req->data);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
- if (conn->send_queue == NULL) {
- EVENT_FD_WRITEABLE(conn->event.fde);
+ /* some requests don't expect a reply, so don't add those to the
+ pending queue */
+ if (req->type == LDAP_TAG_AbandonRequest ||
+ req->type == LDAP_TAG_UnbindRequest) {
+ req->status = NT_STATUS_OK;
+ req->state = LDAP_REQUEST_DONE;
+ /* we can't call the async callback now, as it isn't setup, so
+ call it as next event */
+ event_add_timed(conn->event.event_ctx, req, timeval_zero(),
+ ldap_request_complete, req);
+ return req;
}
- DLIST_ADD_END(conn->send_queue, req, struct ldap_request *);
+
+ req->state = LDAP_REQUEST_PENDING;
+ DLIST_ADD(conn->pending, req);
/* put a timeout on the request */
event_add_timed(conn->event.event_ctx, req,
diff --git a/source4/libcli/ldap/ldap_client.h b/source4/libcli/ldap/ldap_client.h
index 38e043da1f..ee458dc5b0 100644
--- a/source4/libcli/ldap/ldap_client.h
+++ b/source4/libcli/ldap/ldap_client.h
@@ -61,9 +61,6 @@ struct ldap_connection {
/* next message id to assign */
unsigned next_messageid;
- /* outgoing send queue */
- struct ldap_request *send_queue;
-
/* Outstanding LDAP requests that have not yet been replied to */
struct ldap_request *pending;
@@ -73,9 +70,6 @@ struct ldap_connection {
/* set if we are wrapping requests */
BOOL enable_wrap;
- /* partially received packet */
- DATA_BLOB partial;
-
/* the default timeout for messages */
int timeout;
@@ -86,4 +80,6 @@ struct ldap_connection {
struct event_context *event_ctx;
struct fd_event *fde;
} event;
+
+ struct packet_context *packet;
};
diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c
index 6ca2221b1a..0dceb1bba6 100644
--- a/source4/libcli/util/asn1.c
+++ b/source4/libcli/util/asn1.c
@@ -607,3 +607,30 @@ BOOL asn1_write_enumerated(struct asn1_data *data, uint8_t v)
asn1_pop_tag(data);
return !data->has_error;
}
+
+/*
+ check if a ASN.1 blob is a full tag
+*/
+NTSTATUS asn1_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size)
+{
+ struct asn1_data asn1;
+ int size;
+
+ ZERO_STRUCT(asn1);
+ asn1.data = blob.data;
+ asn1.length = blob.length;
+ asn1_start_tag(&asn1, tag);
+ if (asn1.has_error) {
+ talloc_free(asn1.nesting);
+ return STATUS_MORE_ENTRIES;
+ }
+ size = asn1_tag_remaining(&asn1) + asn1.ofs;
+ talloc_free(asn1.nesting);
+
+ if (size > blob.length) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ *packet_size = size;
+ return NT_STATUS_OK;
+}