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_server.c | 586 +++++++++++++++----------------------- 1 file changed, 234 insertions(+), 352 deletions(-) (limited to 'source4/ldap_server/ldap_server.c') 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; } -- cgit