diff options
-rw-r--r-- | source4/ldap_server/ldap_server.c | 194 | ||||
-rw-r--r-- | source4/ldap_server/ldap_server.h | 3 | ||||
-rw-r--r-- | source4/libcli/util/asn1.c | 20 |
3 files changed, 115 insertions, 102 deletions
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 9c5f5fccc8..01a48b78b2 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -1,6 +1,9 @@ /* Unix SMB/CIFS implementation. + LDAP server + + Copyright (C) Andrew Tridgell 2005 Copyright (C) Volker Lendecke 2004 Copyright (C) Stefan Metzmacher 2004 @@ -25,14 +28,18 @@ #include "dlinklist.h" #include "asn_1.h" #include "ldap_server/ldap_server.h" +#include "smbd/service_task.h" #include "smbd/service_stream.h" #include "lib/socket/socket.h" +#include "lib/tls/tls.h" /* close the socket and shutdown a server_context */ static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, const char *reason) { + talloc_free(ldap_conn->tls); + ldap_conn->tls = NULL; stream_terminate_connection(ldap_conn->connection, reason); } @@ -65,27 +72,36 @@ BOOL ldapsrv_append_to_buf(struct rw_buffer *buf, uint8_t *data, size_t length) memcpy(buf->data+buf->length, data, length); buf->length += length; + return True; } -static BOOL read_into_buf(struct socket_context *sock, struct rw_buffer *buf) +static BOOL read_into_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf) { NTSTATUS status; DATA_BLOB tmp_blob; BOOL ret; size_t nread; - tmp_blob = data_blob_talloc(sock, NULL, 1024); + tmp_blob = data_blob_talloc(conn, NULL, 1024); if (tmp_blob.data == NULL) { return False; } - status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0); + 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; } + 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); @@ -104,19 +120,13 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn) BOOL ret; uint8_t *buf; size_t buf_length, sasl_length; - struct socket_context *sock = conn->connection->socket; TALLOC_CTX *mem_ctx; size_t nread; - if (!conn->gensec) { - return read_into_buf(sock, &conn->in_buffer); - } - if (!conn->session_info) { - return read_into_buf(sock, &conn->in_buffer); - } - if (!(gensec_have_feature(conn->gensec, GENSEC_FEATURE_SIGN) || + 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(sock, &conn->in_buffer); + return read_into_buf(conn, &conn->in_buffer); } mem_ctx = talloc_new(conn); @@ -131,12 +141,20 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn) return False; } - status = socket_recv(sock, tmp_blob.data, tmp_blob.length, &nread, 0); + 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; ret = ldapsrv_append_to_buf(&conn->sasl_in_buffer, tmp_blob.data, tmp_blob.length); @@ -185,7 +203,7 @@ static BOOL ldapsrv_read_buf(struct ldapsrv_connection *conn) return ret; } -static BOOL write_from_buf(struct socket_context *sock, struct rw_buffer *buf) +static BOOL write_from_buf(struct ldapsrv_connection *conn, struct rw_buffer *buf) { NTSTATUS status; DATA_BLOB tmp_blob; @@ -194,7 +212,7 @@ static BOOL write_from_buf(struct socket_context *sock, struct rw_buffer *buf) tmp_blob.data = buf->data; tmp_blob.length = buf->length; - status = socket_send(sock, &tmp_blob, &sendlen, 0); + 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; @@ -213,20 +231,19 @@ static BOOL ldapsrv_write_buf(struct ldapsrv_connection *conn) DATA_BLOB sasl; size_t sendlen; BOOL ret; - struct socket_context *sock = conn->connection->socket; TALLOC_CTX *mem_ctx; if (!conn->gensec) { - return write_from_buf(sock, &conn->out_buffer); + return write_from_buf(conn, &conn->out_buffer); } if (!conn->session_info) { - return write_from_buf(sock, &conn->out_buffer); + 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(sock, &conn->out_buffer); + return write_from_buf(conn, &conn->out_buffer); } mem_ctx = talloc_new(conn); @@ -270,7 +287,7 @@ nodata: tmp_blob.data = conn->sasl_out_buffer.data; tmp_blob.length = conn->sasl_out_buffer.length; - status = socket_send(sock, &tmp_blob, &sendlen, 0); + 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); @@ -334,14 +351,10 @@ static void ldapsrv_recv(struct stream_connection *conn, uint16_t flags) { struct ldapsrv_connection *ldap_conn = talloc_get_type(conn->private, struct ldapsrv_connection); uint8_t *buf; - size_t buf_length, msg_length; - DATA_BLOB blob; - struct asn1_data data; + size_t buf_length; struct ldapsrv_call *call; NTSTATUS status; - DEBUG(10,("ldapsrv_recv\n")); - if (!ldapsrv_read_buf(ldap_conn)) { ldapsrv_terminate_connection(ldap_conn, "ldapsrv_read_buf() failed"); return; @@ -350,59 +363,45 @@ static void ldapsrv_recv(struct stream_connection *conn, uint16_t flags) peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length); while (buf_length > 0) { - /* LDAP Messages are always SEQUENCES */ - - if (!asn1_object_length(buf, buf_length, ASN1_SEQUENCE(0), - &msg_length)) { - ldapsrv_terminate_connection(ldap_conn, "asn1_object_length() failed"); - return; - } - - if (buf_length < msg_length) { - /* Not enough yet */ - break; - } - - /* We've got a complete LDAP request in the in-buffer, convert - * that to a ldap_message and put it into the incoming - * queue. */ + DATA_BLOB blob; + struct asn1_data data; + struct ldap_message *msg = talloc(conn, struct ldap_message); blob.data = buf; - blob.length = msg_length; + blob.length = buf_length; if (!asn1_load(&data, blob)) { ldapsrv_terminate_connection(ldap_conn, "asn1_load() failed"); return; } - call = talloc_zero(ldap_conn, struct ldapsrv_call); - if (!call) { - ldapsrv_terminate_connection(ldap_conn, "no memory"); - 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; } - call->request = talloc_zero(call, struct ldap_message); - if (call->request == NULL) { + 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; - if (!ldap_decode(&data, call->request)) { - dump_data(0,buf, msg_length); - asn1_free(&data); - ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed"); - return; - } - - asn1_free(&data); - - DLIST_ADD_END(ldap_conn->calls, call, - struct ldapsrv_call *); - - ldapsrv_consumed_from_buf(&ldap_conn->in_buffer, msg_length); + DLIST_ADD_END(ldap_conn->calls, call, struct ldapsrv_call *); status = ldapsrv_do_call(call); if (!NT_STATUS_IS_OK(status)) { @@ -453,18 +452,27 @@ static void ldapsrv_send(struct stream_connection *conn, uint16_t flags) */ static void ldapsrv_accept(struct stream_connection *conn) { + struct ldapsrv_service *ldapsrv_service = + talloc_get_type(conn->private, struct ldapsrv_service); struct ldapsrv_connection *ldap_conn; - DEBUG(10, ("ldapsrv_accept\n")); - ldap_conn = talloc_zero(conn, struct ldapsrv_connection); - - if (ldap_conn == NULL) - return; + if (ldap_conn == NULL) goto failed; ldap_conn->connection = conn; ldap_conn->service = talloc_get_type(conn->private, struct ldapsrv_service); conn->private = ldap_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; + + return; + +failed: + talloc_free(conn); } static const struct stream_server_ops ldap_stream_ops = { @@ -485,31 +493,40 @@ static NTSTATUS add_socket(struct event_context *event_context, const struct mod status = stream_setup_socket(event_context, model_ops, &ldap_stream_ops, "ipv4", address, &port, ldap_service); - NT_STATUS_NOT_OK_RETURN(status); - - port = 3268; + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n", + address, port, nt_errstr(status))); + } - return stream_setup_socket(event_context, model_ops, &ldap_stream_ops, - "ipv4", address, &port, ldap_service); + /* 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; } /* open the ldap server sockets */ -static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct model_ops *model_ops) +static void ldapsrv_task_init(struct task_server *task) { struct ldapsrv_service *ldap_service; struct ldapsrv_partition *rootDSE_part; struct ldapsrv_partition *part; NTSTATUS status; - DEBUG(10,("ldapsrv_init\n")); + ldap_service = talloc_zero(task, struct ldapsrv_service); + if (ldap_service == NULL) goto failed; - ldap_service = talloc_zero(event_context, struct ldapsrv_service); - NT_STATUS_HAVE_NO_MEMORY(ldap_service); + ldap_service->tls_params = tls_initialise(ldap_service); + if (ldap_service->tls_params == NULL) goto failed; rootDSE_part = talloc(ldap_service, struct ldapsrv_partition); - NT_STATUS_HAVE_NO_MEMORY(rootDSE_part); + if (rootDSE_part == NULL) goto failed; rootDSE_part->base_dn = ""; /* RootDSE */ rootDSE_part->ops = ldapsrv_get_rootdse_partition_ops(); @@ -518,7 +535,7 @@ static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct m DLIST_ADD_END(ldap_service->partitions, rootDSE_part, struct ldapsrv_partition *); part = talloc(ldap_service, struct ldapsrv_partition); - NT_STATUS_HAVE_NO_MEMORY(part); + if (part == NULL) goto failed; part->base_dn = "*"; /* default partition */ if (lp_parm_bool(-1, "ldapsrv", "hacked", False)) { @@ -540,15 +557,28 @@ static NTSTATUS ldapsrv_init(struct event_context *event_context, const struct m */ for(i = 0; i < num_interfaces; i++) { const char *address = iface_n_ip(i); - status = add_socket(event_context, model_ops, address, ldap_service); - NT_STATUS_NOT_OK_RETURN(status); + status = add_socket(task->event_ctx, task->model_ops, address, ldap_service); + if (!NT_STATUS_IS_OK(status)) goto failed; } } else { - status = add_socket(event_context, model_ops, lp_socket_address(), ldap_service); - NT_STATUS_NOT_OK_RETURN(status); + status = add_socket(task->event_ctx, task->model_ops, lp_socket_address(), ldap_service); + if (!NT_STATUS_IS_OK(status)) goto failed; } - return NT_STATUS_OK; + return; + +failed: + task_terminate(task, "Failed to startup ldap server task"); +} + +/* + called on startup of the web server service It's job is to start + listening on all configured sockets +*/ +static NTSTATUS ldapsrv_init(struct event_context *event_context, + const struct model_ops *model_ops) +{ + return task_server_startup(event_context, model_ops, ldapsrv_task_init); } diff --git a/source4/ldap_server/ldap_server.h b/source4/ldap_server/ldap_server.h index 890e2f3003..2427c2b698 100644 --- a/source4/ldap_server/ldap_server.h +++ b/source4/ldap_server/ldap_server.h @@ -73,6 +73,8 @@ struct ldapsrv_connection { struct ldapsrv_call *calls; struct ldapsrv_service *service; + + struct tls_context *tls; }; struct ldapsrv_partition; @@ -103,4 +105,5 @@ struct ldapsrv_service { struct ldapsrv_partition *rootDSE; struct ldapsrv_partition *default_partition; struct ldapsrv_partition *partitions; + struct tls_params *tls_params; }; diff --git a/source4/libcli/util/asn1.c b/source4/libcli/util/asn1.c index 92f9a8c389..10afd74273 100644 --- a/source4/libcli/util/asn1.c +++ b/source4/libcli/util/asn1.c @@ -387,26 +387,6 @@ BOOL asn1_start_tag(struct asn1_data *data, uint8_t tag) } -/* Get the length to be expected in buf */ -BOOL asn1_object_length(uint8_t *buf, size_t buf_length, - uint8_t tag, size_t *result) -{ - struct asn1_data data; - - /* Fake the asn1_load to avoid the memdup, this is just to be able to - * re-use the length-reading in asn1_start_tag */ - ZERO_STRUCT(data); - data.data = buf; - data.length = buf_length; - if (!asn1_start_tag(&data, tag)) - return False; - *result = asn1_tag_remaining(&data)+data.ofs; - /* We can't use asn1_end_tag here, as we did not consume the complete - * tag, so asn1_end_tag would flag an error and not free nesting */ - talloc_free(data.nesting); - return True; -} - /* stop reading a tag */ BOOL asn1_end_tag(struct asn1_data *data) { |