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_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); }
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_conn_init_timeout(struct tevent_context *ev, struct tevent_timer *te, struct timeval t, void *private_data) { struct ldapsrv_connection *conn = talloc_get_type(private_data, struct ldapsrv_connection); ldapsrv_terminate_connection(conn, "Timeout. No requests after initial connection"); }
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; }
/* 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; 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; } blob = data_blob(NULL, 0); if (call->replies == NULL) { talloc_free(call); 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, samba_ldap_control_handlers(), &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); } packet_send_callback(conn->packet, blob, call->send_callback, call->send_private); talloc_free(call); return; }
/* handle packet errors */ static void ldapsrv_error_handler(void *private_data, NTSTATUS status) { struct ldapsrv_connection *conn = talloc_get_type(private_data, struct ldapsrv_connection); ldapsrv_terminate_connection(conn, nt_errstr(status)); }
/* 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, struct auth_session_info *session_info) { struct ldapsrv_service *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; conn = talloc_zero(c, struct ldapsrv_connection); if (!conn) { stream_terminate_connection(c, "ldapsrv_accept: out of memory"); return; } 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_data = conn; socket_address = socket_get_my_addr(c->socket, conn); if (!socket_address) { ldapsrv_terminate_connection(conn, "ldapsrv_accept: failed to obtain local socket address!"); return; } 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_steal(c, tls_socket); c->socket = tls_socket; conn->sockets.tls = tls_socket; } else if (port == 3268) /* 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); if (conn->sockets.tls) { packet_set_unreliable_select(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)) { stream_terminate_connection(c, talloc_asprintf(conn, "Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status))); return; } conn->server_credentials = server_credentials; conn->session_info = talloc_move(conn, &session_info); if (!NT_STATUS_IS_OK(ldapsrv_backend_Init(conn))) { ldapsrv_terminate_connection(conn, "backend Init failed"); return; } /* load limits from the conf partition */ ldapsrv_load_limits(conn); /* should we fail on error ? */ /* 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); packet_recv_enable(conn->packet); }
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_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; }
/* 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, struct auth_session_info *session_info, bool is_privileged) { struct ldapsrv_service *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->connection = c; conn->service = ldapsrv_service; conn->lp_ctx = ldapsrv_service->task->lp_ctx; c->private_data = conn; socket_address = socket_get_my_addr(c->socket, conn); if (!socket_address) { ldapsrv_terminate_connection(conn, "ldapsrv_accept: failed to obtain local socket address!"); return; } port = socket_address->port; talloc_free(socket_address); if (port == 3268 || port == 3269) /* Global catalog */ { conn->global_catalog = true; } 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)) { stream_terminate_connection(c, talloc_asprintf(conn, "Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status))); return; } conn->server_credentials = server_credentials; conn->session_info = session_info; if (!NT_STATUS_IS_OK(ldapsrv_backend_Init(conn))) { ldapsrv_terminate_connection(conn, "backend Init failed"); return; } /* load limits from the conf partition */ ldapsrv_load_limits(conn); /* should we fail on error ? */ /* register the server */ irpc_add_name(c->msg_ctx, "ldap_server"); conn->sockets.active = conn->sockets.raw; 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); }