From c7496c6cdb7bdcdd483868c21457350f567ec054 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Sun, 19 Jun 2005 09:31:34 +0000 Subject: r7747: - simplified the ldap server buffer handling - got rid of the special cases for sasl buffers - added a tls_socket_pending() call to determine how much data is waiting on a tls connection - removed the attempt at async handling of ldap calls. The buffers/sockets are all async, but the calls themselves are sync. (This used to be commit 73cb4aad229d08e17e22d5792580bd43a61b142a) --- source4/ldap_server/ldap_backend.c | 14 +- source4/ldap_server/ldap_bind.c | 32 +- source4/ldap_server/ldap_hacked_ldb.c | 45 ++- source4/ldap_server/ldap_rootdse.c | 8 +- source4/ldap_server/ldap_server.c | 586 ++++++++++++++-------------------- source4/ldap_server/ldap_server.h | 60 +--- source4/ldap_server/ldap_simple_ldb.c | 24 +- source4/lib/tls/tls.c | 21 +- source4/lib/tls/tls.h | 5 + source4/libcli/ldap/ldap_client.c | 2 +- 10 files changed, 327 insertions(+), 470 deletions(-) diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c index 6ac9839e29..3da7277cc1 100644 --- a/source4/ldap_server/ldap_backend.c +++ b/source4/ldap_server/ldap_backend.c @@ -38,18 +38,15 @@ struct ldapsrv_reply *ldapsrv_init_reply(struct ldapsrv_call *call, uint8_t type return NULL; } - reply->prev = reply->next = NULL; - reply->state = LDAPSRV_REPLY_STATE_NEW; reply->msg->messageid = call->request->messageid; reply->msg->type = type; return reply; } -NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply) +void ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply) { DLIST_ADD_END(call->replies, reply, struct ldapsrv_reply *); - return NT_STATUS_OK; } struct ldapsrv_partition *ldapsrv_get_partition(struct ldapsrv_connection *conn, const char *dn, uint8_t scope) @@ -83,7 +80,8 @@ NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error) r->value.data = NULL; r->value.length = 0; - return ldapsrv_queue_reply(call, reply); + ldapsrv_queue_reply(call, reply); + return NT_STATUS_OK; } static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call) @@ -112,7 +110,8 @@ static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call) done->errormessage = NULL; done->referral = NULL; - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; } return part->ops->Search(part, call, req); @@ -225,7 +224,8 @@ static NTSTATUS ldapsrv_ExtendedRequest(struct ldapsrv_call *call) ZERO_STRUCT(reply->msg->r); - return ldapsrv_queue_reply(call, reply); + ldapsrv_queue_reply(call, reply); + return NT_STATUS_OK; } NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call) diff --git a/source4/ldap_server/ldap_bind.c b/source4/ldap_server/ldap_bind.c index 7b416c9726..aba35e0b56 100644 --- a/source4/ldap_server/ldap_bind.c +++ b/source4/ldap_server/ldap_bind.c @@ -44,7 +44,8 @@ static NTSTATUS ldapsrv_BindSimple(struct ldapsrv_call *call) resp->response.referral = NULL; resp->SASL.secblob = data_blob(NULL, 0); - return ldapsrv_queue_reply(call, reply); + ldapsrv_queue_reply(call, reply); + return NT_STATUS_OK; } static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call) @@ -56,8 +57,6 @@ static NTSTATUS ldapsrv_BindSASL(struct ldapsrv_call *call) int result; const char *errstr; NTSTATUS status = NT_STATUS_OK; - NTSTATUS sasl_status; - BOOL ret; DEBUG(10, ("BindSASL dn: %s\n",req->dn)); @@ -105,6 +104,7 @@ reply: } else if (NT_STATUS_IS_OK(status)) { result = LDAP_SUCCESS; errstr = NULL; + call->conn->enable_wrap = True; } else { result = 49; errstr = talloc_asprintf(reply, "SASL:[%s]: %s", req->creds.SASL.mechanism, nt_errstr(status)); @@ -115,27 +115,8 @@ reply: resp->response.errormessage = errstr; resp->response.referral = NULL; - sasl_status = status; - status = ldapsrv_queue_reply(call, reply); - if (!NT_STATUS_IS_OK(sasl_status) || !NT_STATUS_IS_OK(status)) { - return status; - } - - status = ldapsrv_do_responses(call->conn); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - ret = ldapsrv_append_to_buf(&conn->sasl_out_buffer, conn->out_buffer.data, conn->out_buffer.length); - if (!ret) { - return NT_STATUS_NO_MEMORY; - } - ldapsrv_consumed_from_buf(&conn->out_buffer, conn->out_buffer.length); - if (NT_STATUS_IS_OK(status)) { - status = gensec_session_info(conn->gensec, &conn->session_info); - } - - return status; + ldapsrv_queue_reply(call, reply); + return NT_STATUS_OK; } NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call) @@ -163,7 +144,8 @@ NTSTATUS ldapsrv_BindRequest(struct ldapsrv_call *call) resp->response.referral = NULL; resp->SASL.secblob = data_blob(NULL, 0); - return ldapsrv_queue_reply(call, reply); + ldapsrv_queue_reply(call, reply); + return NT_STATUS_OK; } NTSTATUS ldapsrv_UnbindRequest(struct ldapsrv_call *call) diff --git a/source4/ldap_server/ldap_hacked_ldb.c b/source4/ldap_server/ldap_hacked_ldb.c index be252c738f..e0056775b9 100644 --- a/source4/ldap_server/ldap_hacked_ldb.c +++ b/source4/ldap_server/ldap_hacked_ldb.c @@ -197,7 +197,6 @@ DEBUG(0, (__location__": convert_values(ncname): end ok\n")); static NTSTATUS hacked_wellknown_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, struct ldap_SearchRequest *r) { - NTSTATUS status; void *local_ctx; struct ldap_SearchResEntry *ent; struct ldap_Result *done; @@ -259,10 +258,7 @@ static NTSTATUS hacked_wellknown_Search(struct ldapsrv_partition *partition, str ent->num_attributes = 0; ent->attributes = NULL; - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone); NT_STATUS_HAVE_NO_MEMORY(done_r); @@ -277,7 +273,8 @@ static NTSTATUS hacked_wellknown_Search(struct ldapsrv_partition *partition, str talloc_free(local_ctx); - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; } static NTSTATUS hacked_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -385,10 +382,7 @@ DEBUGADD(0,("hacked filter: %s\n", ldb_filter_from_tree(r, r->tree))); } } queue_reply: - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); } else { for (i=0; i < count; i++) { ent_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultEntry); @@ -433,10 +427,7 @@ queue_reply: } } queue_reply2: - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); } } @@ -465,7 +456,8 @@ queue_reply2: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; } static NTSTATUS hldb_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -573,10 +565,7 @@ static NTSTATUS hldb_Search(struct ldapsrv_partition *partition, struct ldapsrv_ } } queue_reply: - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); } reply: @@ -607,7 +596,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; #endif } @@ -708,7 +698,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, add_reply); + ldapsrv_queue_reply(call, add_reply); + return NT_STATUS_OK; } static NTSTATUS hldb_Del(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -760,7 +751,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, del_reply); + ldapsrv_queue_reply(call, del_reply); + return NT_STATUS_OK; } static NTSTATUS hldb_Modify(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -875,7 +867,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, modify_reply); + ldapsrv_queue_reply(call, modify_reply); + return NT_STATUS_OK; } static NTSTATUS hldb_Compare(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -944,7 +937,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, compare_r); + ldapsrv_queue_reply(call, compare_r); + return NT_STATUS_OK; } static NTSTATUS hldb_ModifyDN(struct ldapsrv_partition *partition, struct ldapsrv_call *call, struct ldap_ModifyDNRequest *r) @@ -1041,7 +1035,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, modifydn_r); + ldapsrv_queue_reply(call, modifydn_r); + return NT_STATUS_OK; } static const struct ldapsrv_partition_ops hldb_ops = { diff --git a/source4/ldap_server/ldap_rootdse.c b/source4/ldap_server/ldap_rootdse.c index e5c74be78d..9d19f71fcb 100644 --- a/source4/ldap_server/ldap_rootdse.c +++ b/source4/ldap_server/ldap_rootdse.c @@ -322,10 +322,7 @@ static NTSTATUS rootdse_Search(struct ldapsrv_partition *partition, struct ldaps } } queue_reply: - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); } done_r = ldapsrv_init_reply(call, LDAP_TAG_SearchResultDone); @@ -357,7 +354,8 @@ queue_reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; } static const struct ldapsrv_partition_ops rootdse_ops = { diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 01a48b78b2..bf64735b0b 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -36,443 +36,321 @@ /* close the socket and shutdown a server_context */ -static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, const char *reason) +static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn, + const char *reason) { - talloc_free(ldap_conn->tls); - ldap_conn->tls = NULL; - stream_terminate_connection(ldap_conn->connection, reason); + talloc_free(conn->tls); + conn->tls = NULL; + stream_terminate_connection(conn->connection, reason); } -/* This rw-buf api is made to avoid memcpy. For now do that like mad... The - idea is to write into a circular list of buffers where the ideal case is - that a read(2) holds a complete request that is then thrown away - completely. */ - -void ldapsrv_consumed_from_buf(struct rw_buffer *buf, - size_t length) -{ - memmove(buf->data, buf->data+length, buf->length-length); - buf->length -= length; -} - -static void peek_into_read_buf(struct rw_buffer *buf, uint8_t **out, - size_t *out_length) -{ - *out = buf->data; - *out_length = buf->length; -} - -BOOL ldapsrv_append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length) -{ - buf->data = realloc(buf->data, buf->length+length); - - if (buf->data == NULL) - return False; - - memcpy(buf->data+buf->length, data, length); - - buf->length += length; - - return True; -} - -static BOOL read_into_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf) +/* + process a decoded ldap message +*/ +static void ldapsrv_process_message(struct ldapsrv_connection *conn, + struct ldap_message *msg) { + struct ldapsrv_call *call; NTSTATUS status; - DATA_BLOB tmp_blob; - BOOL ret; - size_t nread; + DATA_BLOB blob; + struct ldapsrv_send *q; + BOOL enable_wrap = conn->enable_wrap; - tmp_blob = data_blob_talloc(conn, NULL, 1024); - if (tmp_blob.data == NULL) { - return False; + call = talloc(conn, struct ldapsrv_call); + if (!call) { + ldapsrv_terminate_connection(conn, "no memory"); + return; } + + call->request = talloc_steal(call, msg); + call->conn = conn; + call->replies = NULL; - status = tls_socket_recv(conn->tls, tmp_blob.data, tmp_blob.length, &nread); - if (NT_STATUS_IS_OK(status) && nread == 0) { - return False; - } - if (NT_STATUS_IS_ERR(status)) { - DEBUG(10,("socket_recv: %s\n",nt_errstr(status))); - talloc_free(tmp_blob.data); - return False; - } + /* make the call */ + status = ldapsrv_do_call(call); if (!NT_STATUS_IS_OK(status)) { - talloc_free(tmp_blob.data); - return True; - } - - tmp_blob.length = nread; - - ret = ldapsrv_append_to_buf(buf, tmp_blob.data, tmp_blob.length); - - talloc_free(tmp_blob.data); - - return ret; -} - -static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn) -{ - NTSTATUS status; - DATA_BLOB tmp_blob; - DATA_BLOB wrapped; - DATA_BLOB unwrapped; - BOOL ret; - uint8_t *buf; - size_t buf_length, sasl_length; - TALLOC_CTX *mem_ctx; - size_t nread; - - if (!conn->gensec || !conn->session_info || - !(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) || - gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) { - return read_into_buf(conn, &conn->in_buffer); - } - - mem_ctx = talloc_new(conn); - if (!mem_ctx) { - DEBUG(0,("no memory\n")); - return False; + goto failed; } + + blob = data_blob(NULL, 0); - tmp_blob = data_blob_talloc(mem_ctx, NULL, 1024); - if (tmp_blob.data == NULL) { - talloc_free(mem_ctx); - return False; + if (call->replies == NULL) { + talloc_free(call); + return; } - status = tls_socket_recv(conn->tls, tmp_blob.data, tmp_blob.length, &nread); - if (NT_STATUS_IS_OK(status) && nread == 0) { - talloc_free(conn->tls); - return False; - } - if (NT_STATUS_IS_ERR(status)) { - DEBUG(10,("socket_recv: %s\n",nt_errstr(status))); - talloc_free(mem_ctx); - return False; - } - if (!NT_STATUS_IS_OK(status)) { - talloc_free(mem_ctx); - return True; - } - tmp_blob.length = nread; + /* build all the replies into a single blob */ + while (call->replies) { + DATA_BLOB b; - ret = ldapsrv_append_to_buf(&conn->sasl_in_buffer, tmp_blob.data, tmp_blob.length); - if (!ret) { - talloc_free(mem_ctx); - return False; - } + msg = call->replies->msg; + if (!ldap_encode(msg, &b)) { + DEBUG(0,("Failed to encode ldap reply of type %d\n", msg->type)); + goto failed; + } - peek_into_read_buf(&conn->sasl_in_buffer, &buf, &buf_length); + status = data_blob_append(call, &blob, b.data, b.length); + if (!NT_STATUS_IS_OK(status)) goto failed; - if (buf_length < 4) { - /* not enough yet */ - talloc_free(mem_ctx); - return True; + DLIST_REMOVE(call->replies, call->replies); } - sasl_length = RIVAL(buf, 0); + /* possibly encrypt/sign the reply */ + if (enable_wrap) { + DATA_BLOB wrapped; - if ((buf_length - 4) < sasl_length) { - /* not enough yet */ - talloc_free(mem_ctx); - return True; + status = gensec_wrap(conn->gensec, call, &blob, &wrapped); + if (!NT_STATUS_IS_OK(status)) { + goto failed; + } + data_blob_free(&blob); + blob = data_blob_talloc(call, NULL, wrapped.length + 4); + if (q->data.data == NULL) { + goto failed; + } + RSIVAL(blob.data, 0, wrapped.length); + memcpy(blob.data+4, wrapped.data, wrapped.length); + data_blob_free(&wrapped); } - wrapped.data = buf + 4; - wrapped.length = sasl_length; + q = talloc(conn, struct ldapsrv_send); + if (q == NULL) goto failed; - status = gensec_unwrap(conn->gensec, mem_ctx, - &wrapped, - &unwrapped); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("gensec_unwrap: %s\n",nt_errstr(status))); - talloc_free(mem_ctx); - return False; - } - - ret = ldapsrv_append_to_buf(&conn->in_buffer, unwrapped.data, unwrapped.length); - if (!ret) { - talloc_free(mem_ctx); - return False; + q->data = blob; + talloc_steal(q, blob.data); + + if (conn->send_queue == NULL) { + EVENT_FD_WRITEABLE(conn->connection->event.fde); } + DLIST_ADD_END(conn->send_queue, q, struct ldapsrv_send *); + talloc_free(call); + return; - ldapsrv_consumed_from_buf(&conn->sasl_in_buffer, 4 + sasl_length); - - talloc_free(mem_ctx); - return ret; +failed: + talloc_free(call); } -static BOOL write_from_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf) -{ - NTSTATUS status; - DATA_BLOB tmp_blob; - size_t sendlen; - - tmp_blob.data = buf->data; - tmp_blob.length = buf->length; - status = tls_socket_send(conn->tls, &tmp_blob, &sendlen); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("socket_send() %s\n",nt_errstr(status))); - return False; - } - - ldapsrv_consumed_from_buf(buf, sendlen); - - return True; -} - -static BOOL ldapsrv_write_buf(struct ldapsrv_connection *conn) +/* + try and decode the partial input buffer +*/ +static void ldapsrv_try_decode_plain(struct ldapsrv_connection *conn) { - NTSTATUS status; - DATA_BLOB wrapped; - DATA_BLOB tmp_blob; - DATA_BLOB sasl; - size_t sendlen; - BOOL ret; - TALLOC_CTX *mem_ctx; - - - if (!conn->gensec) { - return write_from_buf(conn, &conn->out_buffer); - } - if (!conn->session_info) { - return write_from_buf(conn, &conn->out_buffer); - } - if (conn->sasl_out_buffer.length == 0 && - !(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) || - gensec_have_feature(conn->gensec, GENSEC_FEATURE_SEAL))) { - return write_from_buf(conn, &conn->out_buffer); - } - - mem_ctx = talloc_new(conn); - if (!mem_ctx) { - DEBUG(0,("no memory\n")); - return False; - } - - if (conn->out_buffer.length == 0) { - goto nodata; - } + struct asn1_data asn1; - tmp_blob.data = conn->out_buffer.data; - tmp_blob.length = conn->out_buffer.length; - status = gensec_wrap(conn->gensec, mem_ctx, - &tmp_blob, - &wrapped); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("gensec_wrap: %s\n",nt_errstr(status))); - talloc_free(mem_ctx); - return False; - } + file_save("asn1.dat", conn->partial.data, conn->partial.length); - sasl = data_blob_talloc(mem_ctx, NULL, 4 + wrapped.length); - if (!sasl.data) { - DEBUG(0,("no memory\n")); - talloc_free(mem_ctx); - return False; + if (!asn1_load(&asn1, conn->partial)) { + ldapsrv_terminate_connection(conn, "out of memory"); + return; } - RSIVAL(sasl.data, 0, wrapped.length); - memcpy(sasl.data + 4, wrapped.data, wrapped.length); + /* 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) { + ldapsrv_terminate_connection(conn, "out of memory"); + return; + } - ret = ldapsrv_append_to_buf(&conn->sasl_out_buffer, sasl.data, sasl.length); - if (!ret) { - talloc_free(mem_ctx); - return False; + if (ldap_decode(&asn1, msg)) { + ldapsrv_process_message(conn, msg); + } else { + if (asn1.ofs < asn1.length) { + ldapsrv_terminate_connection(conn, "ldap_decode failed"); + return; + } + asn1.ofs = saved_ofs; + talloc_free(msg); + break; + } } - ldapsrv_consumed_from_buf(&conn->out_buffer, conn->out_buffer.length); -nodata: - tmp_blob.data = conn->sasl_out_buffer.data; - tmp_blob.length = conn->sasl_out_buffer.length; - status = tls_socket_send(conn->tls, &tmp_blob, &sendlen); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(10,("socket_send() %s\n",nt_errstr(status))); - talloc_free(mem_ctx); - return False; + /* 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); } + asn1_free(&asn1); +} - ldapsrv_consumed_from_buf(&conn->sasl_out_buffer, sendlen); - talloc_free(mem_ctx); +/* + try and decode/process wrapped data +*/ +static void ldapsrv_try_decode_wrapped(struct ldapsrv_connection *conn) +{ + uint32_t len; - return True; -} + /* 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; -static BOOL ldap_encode_to_buf(struct ldap_message *msg, struct rw_buffer *buf) -{ - DATA_BLOB blob; - BOOL res; + if (msg == NULL) { + ldapsrv_terminate_connection(conn, "out of memory"); + return; + } - if (!ldap_encode(msg, &blob)) - return False; + wrapped.data = conn->partial.data+4; + wrapped.length = len; - res = ldapsrv_append_to_buf(buf, blob.data, blob.length); + status = gensec_unwrap(conn->gensec, msg, &wrapped, &unwrapped); + if (!NT_STATUS_IS_OK(status)) { + ldapsrv_terminate_connection(conn, "gensec unwrap failed"); + return; + } - data_blob_free(&blob); - return res; -} + if (!asn1_load(&asn1, unwrapped)) { + ldapsrv_terminate_connection(conn, "out of memory"); + return; + } -NTSTATUS ldapsrv_do_responses(struct ldapsrv_connection *conn) -{ - struct ldapsrv_call *call, *next_call = NULL; - struct ldapsrv_reply *reply, *next_reply = NULL; + while (ldap_decode(&asn1, msg)) { + ldapsrv_process_message(conn, msg); + msg = talloc(conn, struct ldap_message); + } - for (call=conn->calls; call; call=next_call) { - for (reply=call->replies; reply; reply=next_reply) { - if (!ldap_encode_to_buf(reply->msg, &conn->out_buffer)) { - return NT_STATUS_FOOBAR; - } - next_reply = reply->next; - DLIST_REMOVE(call->replies, reply); - reply->state = LDAPSRV_REPLY_STATE_SEND; - talloc_free(reply); + if (asn1.ofs < asn1.length) { + ldapsrv_terminate_connection(conn, "ldap_decode failed"); + return; + } + + 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; } - next_call = call->next; - DLIST_REMOVE(conn->calls, call); - call->state = LDAPSRV_CALL_STATE_COMPLETE; - talloc_free(call); } - - return NT_STATUS_OK; } -NTSTATUS ldapsrv_flush_responses(struct ldapsrv_connection *conn) -{ - return NT_STATUS_OK; -} /* called when a LDAP socket becomes readable */ -static void ldapsrv_recv(struct stream_connection *conn, uint16_t flags) +static void ldapsrv_recv(struct stream_connection *c, uint16_t flags) { - struct ldapsrv_connection *ldap_conn = talloc_get_type(conn->private, struct ldapsrv_connection); - uint8_t *buf; - size_t buf_length; - struct ldapsrv_call *call; + struct ldapsrv_connection *conn = + talloc_get_type(c->private, struct ldapsrv_connection); NTSTATUS status; + size_t npending, nread; - if (!ldapsrv_read_buf(ldap_conn)) { - ldapsrv_terminate_connection(ldap_conn, "ldapsrv_read_buf() failed"); + /* work out how much data is pending */ + status = tls_socket_pending(conn->tls, &npending); + if (!NT_STATUS_IS_OK(status)) { + ldapsrv_terminate_connection(conn, "socket_pening() failed"); + return; + } + if (npending == 0) { return; } - peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length); - - while (buf_length > 0) { - DATA_BLOB blob; - struct asn1_data data; - struct ldap_message *msg = talloc(conn, struct ldap_message); - - blob.data = buf; - blob.length = buf_length; - - if (!asn1_load(&data, blob)) { - ldapsrv_terminate_connection(ldap_conn, "asn1_load() failed"); - return; - } - - if (!ldap_decode(&data, msg)) { - if (data.ofs == data.length) { - /* we don't have a complete msg yet */ - talloc_free(msg); - asn1_free(&data); - return; - } - asn1_free(&data); - talloc_free(msg); - ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed"); - return; - } - - ldapsrv_consumed_from_buf(&ldap_conn->in_buffer, data.ofs); - asn1_free(&data); - - call = talloc_zero(ldap_conn, struct ldapsrv_call); - if (!call) { - ldapsrv_terminate_connection(ldap_conn, "no memory"); - return; - } - - call->request = talloc_steal(call, msg); - call->state = LDAPSRV_CALL_STATE_NEW; - call->conn = ldap_conn; - - DLIST_ADD_END(ldap_conn->calls, call, struct ldapsrv_call *); - - status = ldapsrv_do_call(call); - if (!NT_STATUS_IS_OK(status)) { - ldapsrv_terminate_connection(ldap_conn, "ldapsrv_do_call() failed"); - return; - } - peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length); + conn->partial.data = talloc_realloc_size(conn, conn->partial.data, + conn->partial.length + npending); + if (conn->partial.data == NULL) { + ldapsrv_terminate_connection(conn, "out of memory"); + return; } - status = ldapsrv_do_responses(ldap_conn); + /* receive the data */ + status = tls_socket_recv(conn->tls, conn->partial.data + conn->partial.length, + npending, &nread); + if (NT_STATUS_IS_ERR(status)) { + ldapsrv_terminate_connection(conn, "socket_recv() failed"); + return; + } if (!NT_STATUS_IS_OK(status)) { - ldapsrv_terminate_connection(ldap_conn, "ldapsrv_do_responses() failed"); return; } - - if ((ldap_conn->out_buffer.length > 0)||(ldap_conn->sasl_out_buffer.length > 0)) { - EVENT_FD_WRITEABLE(conn->event.fde); + if (nread == 0) { + ldapsrv_terminate_connection(conn, "EOF from client"); + return; } + conn->partial.length += nread; - return; + /* see if we can decode what we have */ + if (conn->enable_wrap) { + ldapsrv_try_decode_wrapped(conn); + } else { + ldapsrv_try_decode_plain(conn); + } } /* called when a LDAP socket becomes writable */ -static void ldapsrv_send(struct stream_connection *conn, uint16_t flags) +static void ldapsrv_send(struct stream_connection *c, uint16_t flags) { - struct ldapsrv_connection *ldap_conn = talloc_get_type(conn->private, struct ldapsrv_connection); - - DEBUG(10,("ldapsrv_send\n")); + struct ldapsrv_connection *conn = + talloc_get_type(c->private, struct ldapsrv_connection); + while (conn->send_queue) { + struct ldapsrv_send *q = conn->send_queue; + size_t nsent; + NTSTATUS status; + + status = tls_socket_send(conn->tls, &q->data, &nsent); + if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { + break; + } + if (!NT_STATUS_IS_OK(status)) { + ldapsrv_terminate_connection(conn, "socket_send error"); + return; + } - if (!ldapsrv_write_buf(ldap_conn)) { - ldapsrv_terminate_connection(ldap_conn, "ldapsrv_write_buf() failed"); - return; + q->data.data += nsent; + q->data.length -= nsent; + if (q->data.length == 0) { + DLIST_REMOVE(conn->send_queue, q); + } } - - if (ldap_conn->out_buffer.length == 0 && ldap_conn->sasl_out_buffer.length == 0) { - EVENT_FD_NOT_WRITEABLE(conn->event.fde); + if (conn->send_queue == NULL) { + EVENT_FD_NOT_WRITEABLE(c->event.fde); } - - return; } /* initialise a server_context from a open socket and register a event handler for reading from that socket */ -static void ldapsrv_accept(struct stream_connection *conn) +static void ldapsrv_accept(struct stream_connection *c) { struct ldapsrv_service *ldapsrv_service = - talloc_get_type(conn->private, struct ldapsrv_service); - struct ldapsrv_connection *ldap_conn; + talloc_get_type(c->private, struct ldapsrv_service); + struct ldapsrv_connection *conn; - ldap_conn = talloc_zero(conn, struct ldapsrv_connection); - if (ldap_conn == NULL) goto failed; + conn = talloc_zero(c, struct ldapsrv_connection); + if (conn == NULL) goto failed; - ldap_conn->connection = conn; - ldap_conn->service = talloc_get_type(conn->private, struct ldapsrv_service); - conn->private = ldap_conn; + conn->enable_wrap = False; + conn->partial = data_blob(NULL, 0); + conn->send_queue = NULL; + conn->connection = c; + conn->service = talloc_get_type(c->private, struct ldapsrv_service); + c->private = conn; /* note that '0' is a ASN1_SEQUENCE(0), which is the first byte on any ldap connection */ - ldap_conn->tls = tls_init_server(ldapsrv_service->tls_params, conn->socket, - conn->event.fde, "0"); - if (ldap_conn->tls == NULL) goto failed; + conn->tls = tls_init_server(ldapsrv_service->tls_params, c->socket, + c->event.fde, "0"); + if (conn->tls == NULL) goto failed; return; failed: - talloc_free(conn); + talloc_free(c); } static const struct stream_server_ops ldap_stream_ops = { @@ -485,7 +363,8 @@ static const struct stream_server_ops ldap_stream_ops = { /* add a socket address to the list of events, one event per port */ -static NTSTATUS add_socket(struct event_context *event_context, const struct model_ops *model_ops, +static NTSTATUS add_socket(struct event_context *event_context, + const struct model_ops *model_ops, const char *address, struct ldapsrv_service *ldap_service) { uint16_t port = 389; @@ -498,14 +377,17 @@ static NTSTATUS add_socket(struct event_context *event_context, const struct mod address, port, nt_errstr(status))); } - /* add ldaps server */ - port = 636; - status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops, - "ipv4", address, &port, ldap_service); - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n", - address, port, nt_errstr(status))); + if (tls_support(ldap_service->tls_params)) { + /* add ldaps server */ + port = 636; + status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops, + "ipv4", address, &port, ldap_service); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n", + address, port, nt_errstr(status))); + } } + return status; } diff --git a/source4/ldap_server/ldap_server.h b/source4/ldap_server/ldap_server.h index 2427c2b698..32b2cffe99 100644 --- a/source4/ldap_server/ldap_server.h +++ b/source4/ldap_server/ldap_server.h @@ -21,62 +21,36 @@ #include "libcli/ldap/ldap.h" -struct rw_buffer { - uint8_t *data; - size_t ofs, length; -}; +struct ldapsrv_connection { + struct stream_connection *connection; + struct gensec_security *gensec; + struct auth_session_info *session_info; + struct ldapsrv_service *service; + struct tls_context *tls; -enum ldapsrv_call_state { - LDAPSRV_CALL_STATE_NEW = 0, - LDAPSRV_CALL_STATE_BUSY, - LDAPSRV_CALL_STATE_ASYNC, - LDAPSRV_CALL_STATE_ABORT, - LDAPSRV_CALL_STATE_COMPLETE -}; + /* partially received request */ + DATA_BLOB partial; -enum ldapsrv_reply_state { - LDAPSRV_REPLY_STATE_NEW = 0, - LDAPSRV_REPLY_STATE_SEND -}; + /* are we using gensec wrapping? */ + BOOL enable_wrap; -struct ldapsrv_connection; + /* reply send queue */ + struct ldapsrv_send { + struct ldapsrv_send *next, *prev; + DATA_BLOB data; + } *send_queue; +}; struct ldapsrv_call { - struct ldapsrv_call *prev,*next; - enum ldapsrv_call_state state; - struct ldapsrv_connection *conn; - struct ldap_message *request; - struct ldapsrv_reply { - struct ldapsrv_reply *prev,*next; - enum ldapsrv_reply_state state; + struct ldapsrv_reply *prev, *next; struct ldap_message *msg; } *replies; }; struct ldapsrv_service; - -struct ldapsrv_connection { - struct stream_connection *connection; - - struct gensec_security *gensec; - struct auth_session_info *session_info; - - struct rw_buffer sasl_in_buffer; - struct rw_buffer sasl_out_buffer; - - struct rw_buffer in_buffer; - struct rw_buffer out_buffer; - - struct ldapsrv_call *calls; - - struct ldapsrv_service *service; - - struct tls_context *tls; -}; - struct ldapsrv_partition; struct ldapsrv_partition_ops { diff --git a/source4/ldap_server/ldap_simple_ldb.c b/source4/ldap_server/ldap_simple_ldb.c index 898b5fd82a..4b73026d42 100644 --- a/source4/ldap_server/ldap_simple_ldb.c +++ b/source4/ldap_server/ldap_simple_ldb.c @@ -38,7 +38,6 @@ static NTSTATUS sldb_Search(struct ldapsrv_partition *partition, struct ldapsrv_call *call, struct ldap_SearchRequest *r) { - NTSTATUS status; void *local_ctx; struct ldap_dn *basedn; struct ldap_Result *done; @@ -128,10 +127,7 @@ static NTSTATUS sldb_Search(struct ldapsrv_partition *partition, struct ldapsrv_ } } queue_reply: - status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + ldapsrv_queue_reply(call, ent_r); } reply: @@ -162,7 +158,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, done_r); + ldapsrv_queue_reply(call, done_r); + return NT_STATUS_OK; } static NTSTATUS sldb_Add(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -261,7 +258,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, add_reply); + ldapsrv_queue_reply(call, add_reply); + return NT_STATUS_OK; } static NTSTATUS sldb_Del(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -313,7 +311,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, del_reply); + ldapsrv_queue_reply(call, del_reply); + return NT_STATUS_OK; } static NTSTATUS sldb_Modify(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -423,7 +422,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, modify_reply); + ldapsrv_queue_reply(call, modify_reply); + return NT_STATUS_OK; } static NTSTATUS sldb_Compare(struct ldapsrv_partition *partition, struct ldapsrv_call *call, @@ -492,7 +492,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, compare_r); + ldapsrv_queue_reply(call, compare_r); + return NT_STATUS_OK; } static NTSTATUS sldb_ModifyDN(struct ldapsrv_partition *partition, struct ldapsrv_call *call, struct ldap_ModifyDNRequest *r) @@ -589,7 +590,8 @@ reply: talloc_free(local_ctx); - return ldapsrv_queue_reply(call, modifydn_r); + ldapsrv_queue_reply(call, modifydn_r); + return NT_STATUS_OK; } static const struct ldapsrv_partition_ops sldb_ops = { diff --git a/source4/lib/tls/tls.c b/source4/lib/tls/tls.c index 8f443c67d7..53b689f135 100644 --- a/source4/lib/tls/tls.c +++ b/source4/lib/tls/tls.c @@ -159,6 +159,20 @@ static NTSTATUS tls_handshake(struct tls_context *tls) return NT_STATUS_OK; } +/* + see how many bytes are pending on the connection +*/ +NTSTATUS tls_socket_pending(struct tls_context *tls, size_t *npending) +{ + if (!tls->tls_enabled || tls->tls_detect) { + return socket_pending(tls->socket, npending); + } + *npending = gnutls_record_check_pending(tls->session); + if (*npending == 0) { + return socket_pending(tls->socket, npending); + } + return NT_STATUS_OK; +} /* receive data either by tls or normal socket_recv @@ -222,7 +236,7 @@ NTSTATUS tls_socket_send(struct tls_context *tls, const DATA_BLOB *blob, size_t return STATUS_MORE_ENTRIES; } if (ret < 0) { - DEBUG(0,("gnutls_record_send failed - %s\n", gnutls_strerror(ret))); + DEBUG(0,("gnutls_record_send of %d failed - %s\n", blob->length, gnutls_strerror(ret))); return NT_STATUS_UNEXPECTED_NETWORK_ERROR; } *sendlen = ret; @@ -426,4 +440,9 @@ BOOL tls_support(struct tls_params *params) return False; } +NTSTATUS tls_socket_pending(struct tls_context *tls, size_t *npending) +{ + return socket_pending((struct socket_context *)tls, npending); +} + #endif diff --git a/source4/lib/tls/tls.h b/source4/lib/tls/tls.h index f87d49d9eb..fe993a3804 100644 --- a/source4/lib/tls/tls.h +++ b/source4/lib/tls/tls.h @@ -59,3 +59,8 @@ BOOL tls_enabled(struct tls_context *tls); BOOL tls_support(struct tls_params *parms); +/* + ask for the number of bytes in a pending incoming packet +*/ +NTSTATUS tls_socket_pending(struct tls_context *tls, size_t *npending); + diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index 41764b9a37..c9915ae140 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -174,7 +174,7 @@ static void ldap_try_decode_plain(struct ldap_connection *conn) /* keep any remaining data in conn->partial */ data_blob_free(&conn->partial); - if (asn1.ofs != conn->partial.length) { + if (asn1.ofs != asn1.length) { conn->partial = data_blob_talloc(conn, asn1.data + asn1.ofs, asn1.length - asn1.ofs); -- cgit