diff options
-rw-r--r-- | source4/libcli/ldap/ldap.c | 8 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_client.c | 286 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_client.h | 8 | ||||
-rw-r--r-- | source4/libcli/util/asn1.c | 27 |
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; +} |