X-Git-Url: http://git.samba.org/samba.git/?a=blobdiff_plain;f=source4%2Fldap_server%2Fldap_server.c;h=886c684ff335814fba4a8fd62930c95e99217839;hb=b8815dc23d36468cce9b615335ed62f119eb8f35;hp=11cb63e07bf2f3d753b11c9cfb576f345cb5f349;hpb=b17484a70f50d3cc2b1031e86dfe080b9f48181d;p=garming%2Fsamba-autobuild%2F.git diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index 11cb63e07bf..886c684ff33 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -6,28 +6,29 @@ Copyright (C) Andrew Tridgell 2005 Copyright (C) Volker Lendecke 2004 Copyright (C) Stefan Metzmacher 2004 - + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" +#include "system/network.h" #include "lib/events/events.h" #include "auth/auth.h" #include "auth/credentials/credentials.h" #include "librpc/gen_ndr/ndr_samr.h" -#include "lib/util/dlinklist.h" -#include "lib/util/asn1.h" +#include "../lib/util/dlinklist.h" +#include "../lib/util/asn1.h" #include "ldap_server/ldap_server.h" #include "smbd/service_task.h" #include "smbd/service_stream.h" @@ -35,146 +36,101 @@ #include "smbd/process_model.h" #include "lib/tls/tls.h" #include "lib/messaging/irpc.h" -#include "lib/ldb/include/ldb.h" -#include "lib/ldb/include/ldb_errors.h" -#include "libcli/ldap/ldap.h" +#include +#include #include "libcli/ldap/ldap_proto.h" #include "system/network.h" #include "lib/socket/netif.h" #include "dsdb/samdb/samdb.h" #include "param/param.h" +#include "../lib/tsocket/tsocket.h" +#include "../lib/util/tevent_ntstatus.h" +#include "../libcli/util/tstream.h" + +static void ldapsrv_terminate_connection_done(struct tevent_req *subreq); + /* close the socket and shutdown a server_context */ -void ldapsrv_terminate_connection(struct ldapsrv_connection *conn, +static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn, const char *reason) { - stream_terminate_connection(conn->connection, reason); -} + struct tevent_req *subreq; -/* - handle packet errors -*/ -static void ldapsrv_error_handler(void *private, NTSTATUS status) -{ - struct ldapsrv_connection *conn = talloc_get_type(private, - struct ldapsrv_connection); - ldapsrv_terminate_connection(conn, nt_errstr(status)); -} + if (conn->limits.reason) { + return; + } -/* - 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 blob; + conn->limits.endtime = timeval_current_ofs(0, 500); - 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; - call->send_callback = NULL; - call->send_private = NULL; - - /* make the call */ - status = ldapsrv_do_call(call); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(call); - return; + tevent_queue_stop(conn->sockets.send_queue); + if (conn->active_call) { + tevent_req_cancel(conn->active_call); + conn->active_call = NULL; } - - blob = data_blob(NULL, 0); - if (call->replies == NULL) { - talloc_free(call); + conn->limits.reason = talloc_strdup(conn, reason); + if (conn->limits.reason == NULL) { + TALLOC_FREE(conn->sockets.tls); + TALLOC_FREE(conn->sockets.sasl); + TALLOC_FREE(conn->sockets.raw); + stream_terminate_connection(conn->connection, reason); return; } - /* build all the replies into a single blob */ - while (call->replies) { - DATA_BLOB b; - bool ret; - - msg = call->replies->msg; - if (!ldap_encode(msg, &b, call)) { - DEBUG(0,("Failed to encode ldap reply of type %d\n", msg->type)); - talloc_free(call); - return; - } - - ret = data_blob_append(call, &blob, b.data, b.length); - data_blob_free(&b); - - talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet"); - - if (!ret) { - talloc_free(call); - return; - } - - DLIST_REMOVE(call->replies, call->replies); + subreq = tstream_disconnect_send(conn, + conn->connection->event.ctx, + conn->sockets.active); + if (subreq == NULL) { + TALLOC_FREE(conn->sockets.tls); + TALLOC_FREE(conn->sockets.sasl); + TALLOC_FREE(conn->sockets.raw); + stream_terminate_connection(conn->connection, reason); + return; } - - packet_send_callback(conn->packet, blob, - call->send_callback, call->send_private); - talloc_free(call); - return; + tevent_req_set_endtime(subreq, + conn->connection->event.ctx, + conn->limits.endtime); + tevent_req_set_callback(subreq, ldapsrv_terminate_connection_done, conn); } -/* - decode/process data -*/ -static NTSTATUS ldapsrv_decode(void *private, DATA_BLOB blob) +static void ldapsrv_terminate_connection_done(struct tevent_req *subreq) { - NTSTATUS status; - struct ldapsrv_connection *conn = talloc_get_type(private, - struct ldapsrv_connection); - struct asn1_data *asn1 = asn1_init(conn); - struct ldap_message *msg = talloc(conn, struct ldap_message); - - if (asn1 == NULL || msg == NULL) { - return NT_STATUS_NO_MEMORY; - } - - if (!asn1_load(asn1, blob)) { - talloc_free(msg); - talloc_free(asn1); - return NT_STATUS_NO_MEMORY; + struct ldapsrv_connection *conn = + tevent_req_callback_data(subreq, + struct ldapsrv_connection); + int ret; + int sys_errno; + + ret = tstream_disconnect_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + + if (conn->sockets.active == conn->sockets.raw) { + TALLOC_FREE(conn->sockets.tls); + TALLOC_FREE(conn->sockets.sasl); + TALLOC_FREE(conn->sockets.raw); + stream_terminate_connection(conn->connection, + conn->limits.reason); + return; } - status = ldap_decode(asn1, msg); - if (!NT_STATUS_IS_OK(status)) { - asn1_free(asn1); - return status; + TALLOC_FREE(conn->sockets.tls); + TALLOC_FREE(conn->sockets.sasl); + conn->sockets.active = conn->sockets.raw; + + subreq = tstream_disconnect_send(conn, + conn->connection->event.ctx, + conn->sockets.active); + if (subreq == NULL) { + TALLOC_FREE(conn->sockets.raw); + stream_terminate_connection(conn->connection, + conn->limits.reason); + return; } - - data_blob_free(&blob); - talloc_steal(conn, msg); - asn1_free(asn1); - - ldapsrv_process_message(conn, msg); - return NT_STATUS_OK; -} - -/* - Idle timeout handler -*/ -static void ldapsrv_conn_idle_timeout(struct event_context *ev, - struct timed_event *te, - struct timeval t, - void *private) -{ - struct ldapsrv_connection *conn = talloc_get_type(private, struct ldapsrv_connection); - - ldapsrv_terminate_connection(conn, "Timeout. No requests after bind"); + tevent_req_set_endtime(subreq, + conn->connection->event.ctx, + conn->limits.endtime); + tevent_req_set_callback(subreq, ldapsrv_terminate_connection_done, conn); } /* @@ -182,25 +138,7 @@ static void ldapsrv_conn_idle_timeout(struct event_context *ev, */ void ldapsrv_recv(struct stream_connection *c, uint16_t flags) { - struct ldapsrv_connection *conn = - talloc_get_type(c->private, struct ldapsrv_connection); - - if (conn->limits.ite) { /* clean initial timeout if any */ - talloc_free(conn->limits.ite); - conn->limits.ite = NULL; - } - - if (conn->limits.te) { /* clean idle timeout if any */ - talloc_free(conn->limits.te); - conn->limits.te = NULL; - } - - packet_recv(conn->packet); - - /* set idle timeout */ - conn->limits.te = event_add_timed(c->event.ctx, conn, - timeval_current_ofs(conn->limits.conn_idle_time, 0), - ldapsrv_conn_idle_timeout, conn); + smb_panic(__location__); } /* @@ -208,20 +146,7 @@ void ldapsrv_recv(struct stream_connection *c, uint16_t flags) */ static void ldapsrv_send(struct stream_connection *c, uint16_t flags) { - struct ldapsrv_connection *conn = - talloc_get_type(c->private, struct ldapsrv_connection); - - packet_queue_run(conn->packet); -} - -static void ldapsrv_conn_init_timeout(struct event_context *ev, - struct timed_event *te, - struct timeval t, - void *private) -{ - struct ldapsrv_connection *conn = talloc_get_type(private, struct ldapsrv_connection); - - ldapsrv_terminate_connection(conn, "Timeout. No requests after initial connection"); + smb_panic(__location__); } static int ldapsrv_load_limits(struct ldapsrv_connection *conn) @@ -234,7 +159,8 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn) struct ldb_dn *basedn; struct ldb_dn *conf_dn; struct ldb_dn *policy_dn; - int i,ret; + unsigned int i; + int ret; /* set defaults limits in case of failure */ conn->limits.initial_timeout = 120; @@ -249,17 +175,15 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn) } basedn = ldb_dn_new(tmp_ctx, conn->ldb, NULL); - if ( ! ldb_dn_validate(basedn)) { + if (basedn == NULL) { goto failed; } - ret = ldb_search(conn->ldb, basedn, LDB_SCOPE_BASE, NULL, attrs, &res); + ret = ldb_search(conn->ldb, tmp_ctx, &res, basedn, LDB_SCOPE_BASE, attrs, NULL); if (ret != LDB_SUCCESS) { goto failed; } - talloc_steal(tmp_ctx, res); - if (res->count != 1) { goto failed; } @@ -275,13 +199,11 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn) goto failed; } - ret = ldb_search(conn->ldb, policy_dn, LDB_SCOPE_BASE, NULL, attrs2, &res); + ret = ldb_search(conn->ldb, tmp_ctx, &res, policy_dn, LDB_SCOPE_BASE, attrs2, NULL); if (ret != LDB_SUCCESS) { goto failed; } - talloc_steal(tmp_ctx, res); - if (res->count != 1) { goto failed; } @@ -298,7 +220,7 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn) s = sscanf((const char *)el->values[i].data, "%255[^=]=%d", policy_name, &policy_value); if (ret != 2 || policy_value == 0) continue; - + if (strcasecmp("InitRecvTimeout", policy_name) == 0) { conn->limits.initial_timeout = policy_value; continue; @@ -325,33 +247,65 @@ failed: return -1; } +static struct tevent_req *ldapsrv_process_call_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_queue *call_queue, + struct ldapsrv_call *call); +static NTSTATUS ldapsrv_process_call_recv(struct tevent_req *req); + +static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn); +static void ldapsrv_accept_tls_done(struct tevent_req *subreq); + /* 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 *c) +static void ldapsrv_accept(struct stream_connection *c, + struct auth_session_info *session_info, + bool is_privileged) { struct ldapsrv_service *ldapsrv_service = - talloc_get_type(c->private, struct ldapsrv_service); + talloc_get_type(c->private_data, struct ldapsrv_service); struct ldapsrv_connection *conn; struct cli_credentials *server_credentials; struct socket_address *socket_address; NTSTATUS status; int port; + int ret; + struct tevent_req *subreq; + struct timeval endtime; conn = talloc_zero(c, struct ldapsrv_connection); if (!conn) { stream_terminate_connection(c, "ldapsrv_accept: out of memory"); return; } + conn->is_privileged = is_privileged; + + conn->sockets.send_queue = tevent_queue_create(conn, "ldapsev send queue"); + if (conn->sockets.send_queue == NULL) { + stream_terminate_connection(c, + "ldapsrv_accept: tevent_queue_create failed"); + return; + } + + TALLOC_FREE(c->event.fde); + + ret = tstream_bsd_existing_socket(conn, + socket_get_fd(c->socket), + &conn->sockets.raw); + if (ret == -1) { + stream_terminate_connection(c, + "ldapsrv_accept: out of memory"); + return; + } + socket_set_flags(c->socket, SOCKET_FLAG_NOCLOSE); - conn->packet = NULL; conn->connection = c; conn->service = ldapsrv_service; - conn->sockets.raw = c->socket; conn->lp_ctx = ldapsrv_service->task->lp_ctx; - c->private = conn; + c->private_data = conn; socket_address = socket_get_my_addr(c->socket, conn); if (!socket_address) { @@ -360,46 +314,16 @@ static void ldapsrv_accept(struct stream_connection *c) } port = socket_address->port; talloc_free(socket_address); - - if (port == 636) { - struct socket_context *tls_socket = tls_init_server(ldapsrv_service->tls_params, c->socket, - c->event.fde, NULL); - if (!tls_socket) { - ldapsrv_terminate_connection(conn, "ldapsrv_accept: tls_init_server() failed"); - return; - } - talloc_unlink(c, c->socket); - talloc_steal(c, tls_socket); - c->socket = tls_socket; - conn->sockets.tls = tls_socket; - - } else if (port == 3268) /* Global catalog */ { + if (port == 3268 || port == 3269) /* Global catalog */ { conn->global_catalog = true; } - conn->packet = packet_init(conn); - if (conn->packet == NULL) { - ldapsrv_terminate_connection(conn, "out of memory"); - return; - } - - packet_set_private(conn->packet, conn); - packet_set_socket(conn->packet, c->socket); - packet_set_callback(conn->packet, ldapsrv_decode); - packet_set_full_request(conn->packet, ldap_full_packet); - packet_set_error_handler(conn->packet, ldapsrv_error_handler); - packet_set_event_context(conn->packet, c->event.ctx); - packet_set_fde(conn->packet, c->event.fde); - packet_set_serialise(conn->packet); - - /* Ensure we don't get packets until the database is ready below */ - packet_recv_disable(conn->packet); server_credentials = cli_credentials_init(conn); if (!server_credentials) { stream_terminate_connection(c, "Failed to init server credentials\n"); return; } - + cli_credentials_set_conf(server_credentials, conn->lp_ctx); status = cli_credentials_set_machine_account(server_credentials, conn->lp_ctx); if (!NT_STATUS_IS_OK(status)) { @@ -408,11 +332,9 @@ static void ldapsrv_accept(struct stream_connection *c) } conn->server_credentials = server_credentials; - /* Connections start out anonymous */ - if (!NT_STATUS_IS_OK(auth_anonymous_session_info(conn, conn->lp_ctx, &conn->session_info))) { - ldapsrv_terminate_connection(conn, "failed to setup anonymous session info"); - return; - } + conn->session_info = session_info; + + conn->sockets.active = conn->sockets.raw; if (!NT_STATUS_IS_OK(ldapsrv_backend_Init(conn))) { ldapsrv_terminate_connection(conn, "backend Init failed"); @@ -425,27 +347,465 @@ static void ldapsrv_accept(struct stream_connection *c) /* register the server */ irpc_add_name(c->msg_ctx, "ldap_server"); - /* set connections limits */ - conn->limits.ite = event_add_timed(c->event.ctx, conn, - timeval_current_ofs(conn->limits.initial_timeout, 0), - ldapsrv_conn_init_timeout, conn); + if (port != 636 && port != 3269) { + ldapsrv_call_read_next(conn); + return; + } + + endtime = timeval_current_ofs(conn->limits.conn_idle_time, 0); + + subreq = tstream_tls_accept_send(conn, + conn->connection->event.ctx, + conn->sockets.raw, + conn->service->tls_params); + if (subreq == NULL) { + ldapsrv_terminate_connection(conn, "ldapsrv_accept: " + "no memory for tstream_tls_accept_send"); + return; + } + tevent_req_set_endtime(subreq, + conn->connection->event.ctx, + endtime); + tevent_req_set_callback(subreq, ldapsrv_accept_tls_done, conn); +} + +static void ldapsrv_accept_tls_done(struct tevent_req *subreq) +{ + struct ldapsrv_connection *conn = + tevent_req_callback_data(subreq, + struct ldapsrv_connection); + int ret; + int sys_errno; + + ret = tstream_tls_accept_recv(subreq, &sys_errno, + conn, &conn->sockets.tls); + TALLOC_FREE(subreq); + if (ret == -1) { + const char *reason; + + reason = talloc_asprintf(conn, "ldapsrv_accept_tls_loop: " + "tstream_tls_accept_recv() - %d:%s", + sys_errno, strerror(sys_errno)); + if (!reason) { + reason = "ldapsrv_accept_tls_loop: " + "tstream_tls_accept_recv() - failed"; + } + + ldapsrv_terminate_connection(conn, reason); + return; + } + + conn->sockets.active = conn->sockets.tls; + ldapsrv_call_read_next(conn); +} + +static void ldapsrv_call_read_done(struct tevent_req *subreq); - packet_recv_enable(conn->packet); +static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn) +{ + struct tevent_req *subreq; + if (timeval_is_zero(&conn->limits.endtime)) { + conn->limits.endtime = + timeval_current_ofs(conn->limits.initial_timeout, 0); + } else { + conn->limits.endtime = + timeval_current_ofs(conn->limits.conn_idle_time, 0); + } + + /* + * The minimun size of a LDAP pdu is 7 bytes + * + * dumpasn1 -hh ldap-unbind-min.dat + * + * <30 05 02 01 09 42 00> + * 0 5: SEQUENCE { + * <02 01 09> + * 2 1: INTEGER 9 + * <42 00> + * 5 0: [APPLICATION 2] + * : Error: Object has zero length. + * : } + * + * dumpasn1 -hh ldap-unbind-windows.dat + * + * <30 84 00 00 00 05 02 01 09 42 00> + * 0 5: SEQUENCE { + * <02 01 09> + * 6 1: INTEGER 9 + * <42 00> + * 9 0: [APPLICATION 2] + * : Error: Object has zero length. + * : } + * + * This means using an initial read size + * of 7 is ok. + */ + subreq = tstream_read_pdu_blob_send(conn, + conn->connection->event.ctx, + conn->sockets.active, + 7, /* initial_read_size */ + ldap_full_packet, + conn); + if (subreq == NULL) { + ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: " + "no memory for tstream_read_pdu_blob_send"); + return false; + } + tevent_req_set_endtime(subreq, + conn->connection->event.ctx, + conn->limits.endtime); + tevent_req_set_callback(subreq, ldapsrv_call_read_done, conn); + return true; } -static const struct stream_server_ops ldap_stream_ops = { +static void ldapsrv_call_process_done(struct tevent_req *subreq); + +static void ldapsrv_call_read_done(struct tevent_req *subreq) +{ + struct ldapsrv_connection *conn = + tevent_req_callback_data(subreq, + struct ldapsrv_connection); + NTSTATUS status; + struct ldapsrv_call *call; + struct asn1_data *asn1; + DATA_BLOB blob; + + call = talloc_zero(conn, struct ldapsrv_call); + if (!call) { + ldapsrv_terminate_connection(conn, "no memory"); + return; + } + + call->conn = conn; + + status = tstream_read_pdu_blob_recv(subreq, + call, + &blob); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + const char *reason; + + reason = talloc_asprintf(call, "ldapsrv_call_loop: " + "tstream_read_pdu_blob_recv() - %s", + nt_errstr(status)); + if (!reason) { + reason = nt_errstr(status); + } + + ldapsrv_terminate_connection(conn, reason); + return; + } + + asn1 = asn1_init(call); + if (asn1 == NULL) { + ldapsrv_terminate_connection(conn, "no memory"); + return; + } + + call->request = talloc(call, struct ldap_message); + if (call->request == NULL) { + ldapsrv_terminate_connection(conn, "no memory"); + return; + } + + if (!asn1_load(asn1, blob)) { + ldapsrv_terminate_connection(conn, "asn1_load failed"); + return; + } + + status = ldap_decode(asn1, samba_ldap_control_handlers(), + call->request); + if (!NT_STATUS_IS_OK(status)) { + ldapsrv_terminate_connection(conn, nt_errstr(status)); + return; + } + + data_blob_free(&blob); + + + /* queue the call in the global queue */ + subreq = ldapsrv_process_call_send(call, + conn->connection->event.ctx, + conn->service->call_queue, + call); + if (subreq == NULL) { + ldapsrv_terminate_connection(conn, "ldapsrv_process_call_send failed"); + return; + } + tevent_req_set_callback(subreq, ldapsrv_call_process_done, call); + conn->active_call = subreq; +} + +static void ldapsrv_call_writev_done(struct tevent_req *subreq); + +static void ldapsrv_call_process_done(struct tevent_req *subreq) +{ + struct ldapsrv_call *call = + tevent_req_callback_data(subreq, + struct ldapsrv_call); + struct ldapsrv_connection *conn = call->conn; + NTSTATUS status; + DATA_BLOB blob = data_blob_null; + + conn->active_call = NULL; + + status = ldapsrv_process_call_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + ldapsrv_terminate_connection(conn, nt_errstr(status)); + return; + } + + /* build all the replies into a single blob */ + while (call->replies) { + DATA_BLOB b; + bool ret; + + if (!ldap_encode(call->replies->msg, samba_ldap_control_handlers(), &b, call)) { + DEBUG(0,("Failed to encode ldap reply of type %d\n", + call->replies->msg->type)); + ldapsrv_terminate_connection(conn, "ldap_encode failed"); + return; + } + + ret = data_blob_append(call, &blob, b.data, b.length); + data_blob_free(&b); + + talloc_set_name_const(blob.data, "Outgoing, encoded LDAP packet"); + + if (!ret) { + ldapsrv_terminate_connection(conn, "data_blob_append failed"); + return; + } + + DLIST_REMOVE(call->replies, call->replies); + } + + if (blob.length == 0) { + TALLOC_FREE(call); + + ldapsrv_call_read_next(conn); + return; + } + + call->out_iov.iov_base = blob.data; + call->out_iov.iov_len = blob.length; + + subreq = tstream_writev_queue_send(call, + conn->connection->event.ctx, + conn->sockets.active, + conn->sockets.send_queue, + &call->out_iov, 1); + if (subreq == NULL) { + ldapsrv_terminate_connection(conn, "stream_writev_queue_send failed"); + return; + } + tevent_req_set_callback(subreq, ldapsrv_call_writev_done, call); +} + +static void ldapsrv_call_postprocess_done(struct tevent_req *subreq); + +static void ldapsrv_call_writev_done(struct tevent_req *subreq) +{ + struct ldapsrv_call *call = + tevent_req_callback_data(subreq, + struct ldapsrv_call); + struct ldapsrv_connection *conn = call->conn; + int sys_errno; + int rc; + + rc = tstream_writev_queue_recv(subreq, &sys_errno); + TALLOC_FREE(subreq); + if (rc == -1) { + const char *reason; + + reason = talloc_asprintf(call, "ldapsrv_call_writev_done: " + "tstream_writev_queue_recv() - %d:%s", + sys_errno, strerror(sys_errno)); + if (reason == NULL) { + reason = "ldapsrv_call_writev_done: " + "tstream_writev_queue_recv() failed"; + } + + ldapsrv_terminate_connection(conn, reason); + return; + } + + if (call->postprocess_send) { + subreq = call->postprocess_send(call, + conn->connection->event.ctx, + call->postprocess_private); + if (subreq == NULL) { + ldapsrv_terminate_connection(conn, "ldapsrv_call_writev_done: " + "call->postprocess_send - no memory"); + return; + } + tevent_req_set_callback(subreq, + ldapsrv_call_postprocess_done, + call); + return; + } + + TALLOC_FREE(call); + + ldapsrv_call_read_next(conn); +} + +static void ldapsrv_call_postprocess_done(struct tevent_req *subreq) +{ + struct ldapsrv_call *call = + tevent_req_callback_data(subreq, + struct ldapsrv_call); + struct ldapsrv_connection *conn = call->conn; + NTSTATUS status; + + status = call->postprocess_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + const char *reason; + + reason = talloc_asprintf(call, "ldapsrv_call_postprocess_done: " + "call->postprocess_recv() - %s", + nt_errstr(status)); + if (reason == NULL) { + reason = nt_errstr(status); + } + + ldapsrv_terminate_connection(conn, reason); + return; + } + + TALLOC_FREE(call); + + ldapsrv_call_read_next(conn); +} + +struct ldapsrv_process_call_state { + struct ldapsrv_call *call; +}; + +static void ldapsrv_process_call_trigger(struct tevent_req *req, + void *private_data); + +static struct tevent_req *ldapsrv_process_call_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tevent_queue *call_queue, + struct ldapsrv_call *call) +{ + struct tevent_req *req; + struct ldapsrv_process_call_state *state; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct ldapsrv_process_call_state); + if (req == NULL) { + return req; + } + + state->call = call; + + ok = tevent_queue_add(call_queue, ev, req, + ldapsrv_process_call_trigger, NULL); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + + return req; +} + +static void ldapsrv_process_call_trigger(struct tevent_req *req, + void *private_data) +{ + struct ldapsrv_process_call_state *state = + tevent_req_data(req, + struct ldapsrv_process_call_state); + NTSTATUS status; + + /* make the call */ + status = ldapsrv_do_call(state->call); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); +} + +static NTSTATUS ldapsrv_process_call_recv(struct tevent_req *req) +{ + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +static void ldapsrv_accept_nonpriv(struct stream_connection *c) +{ + struct ldapsrv_service *ldapsrv_service = talloc_get_type_abort( + c->private_data, struct ldapsrv_service); + struct auth_session_info *session_info; + NTSTATUS status; + + status = auth_anonymous_session_info( + c, ldapsrv_service->task->lp_ctx, &session_info); + if (!NT_STATUS_IS_OK(status)) { + stream_terminate_connection(c, "failed to setup anonymous " + "session info"); + return; + } + ldapsrv_accept(c, session_info, false); +} + +static const struct stream_server_ops ldap_stream_nonpriv_ops = { .name = "ldap", - .accept_connection = ldapsrv_accept, + .accept_connection = ldapsrv_accept_nonpriv, .recv_handler = ldapsrv_recv, .send_handler = ldapsrv_send, }; +/* The feature removed behind an #ifdef until we can do it properly + * with an EXTERNAL bind. */ + +#define WITH_LDAPI_PRIV_SOCKET + +#ifdef WITH_LDAPI_PRIV_SOCKET +static void ldapsrv_accept_priv(struct stream_connection *c) +{ + struct ldapsrv_service *ldapsrv_service = talloc_get_type_abort( + c->private_data, struct ldapsrv_service); + struct auth_session_info *session_info; + + session_info = system_session(ldapsrv_service->task->lp_ctx); + if (!session_info) { + stream_terminate_connection(c, "failed to setup system " + "session info"); + return; + } + ldapsrv_accept(c, session_info, true); +} + +static const struct stream_server_ops ldap_stream_priv_ops = { + .name = "ldap", + .accept_connection = ldapsrv_accept_priv, + .recv_handler = ldapsrv_recv, + .send_handler = ldapsrv_send, +}; + +#endif + + /* add a socket address to the list of events, one event per port */ -static NTSTATUS add_socket(struct event_context *event_context, - struct loadparm_context *lp_ctx, +static NTSTATUS add_socket(struct task_server *task, + struct loadparm_context *lp_ctx, const struct model_ops *model_ops, const char *address, struct ldapsrv_service *ldap_service) { @@ -453,50 +813,75 @@ static NTSTATUS add_socket(struct event_context *event_context, NTSTATUS status; struct ldb_context *ldb; - status = stream_setup_socket(event_context, lp_ctx, - model_ops, &ldap_stream_ops, - "ipv4", address, &port, - lp_socket_options(lp_ctx), + status = stream_setup_socket(task, task->event_ctx, lp_ctx, + model_ops, &ldap_stream_nonpriv_ops, + "ip", address, &port, + lpcfg_socket_options(lp_ctx), 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; } - if (tls_support(ldap_service->tls_params)) { + if (tstream_tls_params_enabled(ldap_service->tls_params)) { /* add ldaps server */ port = 636; - status = stream_setup_socket(event_context, lp_ctx, - model_ops, &ldap_stream_ops, - "ipv4", address, &port, - lp_socket_options(lp_ctx), + status = stream_setup_socket(task, task->event_ctx, lp_ctx, + model_ops, + &ldap_stream_nonpriv_ops, + "ip", address, &port, + lpcfg_socket_options(lp_ctx), 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; } } - /* Load LDAP database */ - ldb = samdb_connect(ldap_service, lp_ctx, system_session(ldap_service, lp_ctx)); + /* Load LDAP database, but only to read our settings */ + ldb = samdb_connect(ldap_service, ldap_service->task->event_ctx, + lp_ctx, system_session(lp_ctx), 0); if (!ldb) { return NT_STATUS_INTERNAL_DB_CORRUPTION; } - + if (samdb_is_gc(ldb)) { port = 3268; - status = stream_setup_socket(event_context, lp_ctx, - model_ops, &ldap_stream_ops, - "ipv4", address, &port, - lp_socket_options(lp_ctx), + status = stream_setup_socket(task, task->event_ctx, lp_ctx, + model_ops, + &ldap_stream_nonpriv_ops, + "ip", address, &port, + lpcfg_socket_options(lp_ctx), 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; + } + if (tstream_tls_params_enabled(ldap_service->tls_params)) { + /* add ldaps server for the global catalog */ + port = 3269; + status = stream_setup_socket(task, task->event_ctx, lp_ctx, + model_ops, + &ldap_stream_nonpriv_ops, + "ip", address, &port, + lpcfg_socket_options(lp_ctx), + 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; + } } } - return status; + /* And once we are bound, free the temporary ldb, it will + * connect again on each incoming LDAP connection */ + talloc_unlink(ldap_service, ldb); + + return NT_STATUS_OK; } /* @@ -505,18 +890,24 @@ static NTSTATUS add_socket(struct event_context *event_context, static void ldapsrv_task_init(struct task_server *task) { char *ldapi_path; +#ifdef WITH_LDAPI_PRIV_SOCKET + char *priv_dir; +#endif + const char *dns_host_name; struct ldapsrv_service *ldap_service; NTSTATUS status; const struct model_ops *model_ops; - switch (lp_server_role(task->lp_ctx)) { + switch (lpcfg_server_role(task->lp_ctx)) { case ROLE_STANDALONE: - task_server_terminate(task, "ldap_server: no LDAP server required in standalone configuration"); + task_server_terminate(task, "ldap_server: no LDAP server required in standalone configuration", + false); return; case ROLE_DOMAIN_MEMBER: - task_server_terminate(task, "ldap_server: no LDAP server required in member server configuration"); + task_server_terminate(task, "ldap_server: no LDAP server required in member server configuration", + false); return; - case ROLE_DOMAIN_CONTROLLER: + case ROLE_ACTIVE_DIRECTORY_DC: /* Yes, we want an LDAP server */ break; } @@ -524,7 +915,7 @@ static void ldapsrv_task_init(struct task_server *task) task_server_set_title(task, "task[ldapsrv]"); /* run the ldap server as a single process */ - model_ops = process_model_byname("single"); + model_ops = process_model_startup("single"); if (!model_ops) goto failed; ldap_service = talloc_zero(task, struct ldapsrv_service); @@ -532,41 +923,101 @@ static void ldapsrv_task_init(struct task_server *task) ldap_service->task = task; - ldap_service->tls_params = tls_initialise(ldap_service, task->lp_ctx); - if (ldap_service->tls_params == NULL) goto failed; + dns_host_name = talloc_asprintf(ldap_service, "%s.%s", + lpcfg_netbios_name(task->lp_ctx), + lpcfg_dnsdomain(task->lp_ctx)); + if (dns_host_name == NULL) goto failed; + + status = tstream_tls_params_server(ldap_service, + dns_host_name, + lpcfg_tls_enabled(task->lp_ctx), + lpcfg_tls_keyfile(ldap_service, task->lp_ctx), + lpcfg_tls_certfile(ldap_service, task->lp_ctx), + lpcfg_tls_cafile(ldap_service, task->lp_ctx), + lpcfg_tls_crlfile(ldap_service, task->lp_ctx), + lpcfg_tls_dhpfile(ldap_service, task->lp_ctx), + &ldap_service->tls_params); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("ldapsrv failed tstream_tls_params_server - %s\n", + nt_errstr(status))); + goto failed; + } + + ldap_service->call_queue = tevent_queue_create(ldap_service, "ldapsrv_call_queue"); + if (ldap_service->call_queue == NULL) goto failed; - if (lp_interfaces(task->lp_ctx) && lp_bind_interfaces_only(task->lp_ctx)) { + if (lpcfg_interfaces(task->lp_ctx) && lpcfg_bind_interfaces_only(task->lp_ctx)) { struct interface *ifaces; int num_interfaces; int i; - load_interfaces(task, lp_interfaces(task->lp_ctx), &ifaces); - num_interfaces = iface_count(ifaces); + load_interface_list(task, task->lp_ctx, &ifaces); + num_interfaces = iface_list_count(ifaces); /* We have been given an interfaces line, and been told to only bind to those interfaces. Create a socket per interface and bind to only these. */ for(i = 0; i < num_interfaces; i++) { - const char *address = iface_n_ip(ifaces, i); - status = add_socket(task->event_ctx, task->lp_ctx, model_ops, address, ldap_service); + const char *address = iface_list_n_ip(ifaces, i); + status = add_socket(task, task->lp_ctx, model_ops, address, ldap_service); if (!NT_STATUS_IS_OK(status)) goto failed; } } else { - status = add_socket(task->event_ctx, task->lp_ctx, model_ops, - lp_socket_address(task->lp_ctx), ldap_service); - if (!NT_STATUS_IS_OK(status)) goto failed; + const char **wcard; + int i; + wcard = iface_list_wildcard(task, task->lp_ctx); + if (wcard == NULL) { + DEBUG(0,("No wildcard addresses available\n")); + goto failed; + } + for (i=0; wcard[i]; i++) { + status = add_socket(task, task->lp_ctx, model_ops, wcard[i], ldap_service); + if (!NT_STATUS_IS_OK(status)) goto failed; + } + talloc_free(wcard); } - ldapi_path = private_path(ldap_service, task->lp_ctx, "ldapi"); + ldapi_path = lpcfg_private_path(ldap_service, task->lp_ctx, "ldapi"); if (!ldapi_path) { goto failed; } - status = stream_setup_socket(task->event_ctx, task->lp_ctx, - model_ops, &ldap_stream_ops, + status = stream_setup_socket(task, task->event_ctx, task->lp_ctx, + model_ops, &ldap_stream_nonpriv_ops, "unix", ldapi_path, NULL, - lp_socket_options(task->lp_ctx), + lpcfg_socket_options(task->lp_ctx), + ldap_service); + talloc_free(ldapi_path); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("ldapsrv failed to bind to %s - %s\n", + ldapi_path, nt_errstr(status))); + } + +#ifdef WITH_LDAPI_PRIV_SOCKET + priv_dir = lpcfg_private_path(ldap_service, task->lp_ctx, "ldap_priv"); + if (priv_dir == NULL) { + goto failed; + } + /* + * Make sure the directory for the privileged ldapi socket exists, and + * is of the correct permissions + */ + if (!directory_create_or_exist(priv_dir, geteuid(), 0750)) { + task_server_terminate(task, "Cannot create ldap " + "privileged ldapi directory", true); + return; + } + ldapi_path = talloc_asprintf(ldap_service, "%s/ldapi", priv_dir); + talloc_free(priv_dir); + if (ldapi_path == NULL) { + goto failed; + } + + status = stream_setup_socket(task, task->event_ctx, task->lp_ctx, + model_ops, &ldap_stream_priv_ops, + "unix", ldapi_path, NULL, + lpcfg_socket_options(task->lp_ctx), ldap_service); talloc_free(ldapi_path); if (!NT_STATUS_IS_OK(status)) { @@ -574,10 +1025,11 @@ static void ldapsrv_task_init(struct task_server *task) ldapi_path, nt_errstr(status))); } +#endif return; failed: - task_server_terminate(task, "Failed to startup ldap server task"); + task_server_terminate(task, "Failed to startup ldap server task", true); }