summaryrefslogtreecommitdiff
path: root/source4/ldap_server/ldap_server.c
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2004-09-13 10:36:59 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:58:43 -0500
commita560d554bdfade75b81780e427e51cc436d9488a (patch)
tree1826e5cea2eca411d746433199a913a21ed13e76 /source4/ldap_server/ldap_server.c
parent17bdcc9056c77bcecd8078863ca7a7bd7f7e478e (diff)
downloadsamba-a560d554bdfade75b81780e427e51cc436d9488a.tar.gz
samba-a560d554bdfade75b81780e427e51cc436d9488a.tar.bz2
samba-a560d554bdfade75b81780e427e51cc436d9488a.zip
r2321: add complately untested LDAP server start
based on volker's patch this is compiled by default but not started by default metze (This used to be commit 5387bc423d4dc669cbac6626f8dd3a5498a6519d)
Diffstat (limited to 'source4/ldap_server/ldap_server.c')
-rw-r--r--source4/ldap_server/ldap_server.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c
new file mode 100644
index 0000000000..d9c2957027
--- /dev/null
+++ b/source4/ldap_server/ldap_server.c
@@ -0,0 +1,455 @@
+/*
+ Unix SMB/CIFS implementation.
+ LDAP server
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+
+/*
+ close the socket and shutdown a server_context
+*/
+static void ldapsrv_terminate_connection(struct ldapsrv_connection *ldap_conn, const char *reason)
+{
+ server_terminate_connection(ldap_conn->connection, reason);
+}
+
+/*
+ add a socket address to the list of events, one event per port
+*/
+static void add_socket(struct server_service *service,
+ const struct model_ops *model_ops,
+ struct socket_context *socket_ctx,
+ struct in_addr *ifip)
+{
+ uint16_t port = 389;
+
+ service_setup_socket(service, model_ops, socket_ctx, ifip, &port);
+}
+
+/****************************************************************************
+ Open the socket communication.
+****************************************************************************/
+static void ldapsrv_init(struct server_service *service,
+ const struct model_ops *model_ops)
+{
+ DEBUG(1,("ldapsrv_init\n"));
+
+ if (lp_interfaces() && lp_bind_interfaces_only()) {
+ int num_interfaces = iface_count();
+ int i;
+
+ /* 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++) {
+ struct in_addr *ifip = iface_n_ip(i);
+
+ if (ifip == NULL) {
+ DEBUG(0,("ldapsrv_init: interface %d has NULL "
+ "IP address !\n", i));
+ continue;
+ }
+
+ add_socket(service, model_ops, NULL, ifip);
+ }
+ } else {
+ struct in_addr *ifip;
+ TALLOC_CTX *mem_ctx = talloc_init("ldapsrv_init");
+
+ if (!mem_ctx) {
+ smb_panic("No memory");
+ }
+
+ /* Just bind to lp_socket_address() (usually 0.0.0.0) */
+ ifip = interpret_addr2(mem_ctx, lp_socket_address());
+ add_socket(service, model_ops, NULL, ifip);
+
+ talloc_destroy(mem_ctx);
+ }
+}
+
+/* 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. */
+
+static BOOL 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(int fd, struct rw_buffer *buf)
+{
+ char tmp_buf[1024];
+ int len;
+
+ len = read(fd, tmp_buf, sizeof(tmp_buf));
+ if (len == 0)
+ return False;
+
+ return append_to_buf(buf, tmp_buf, len);
+}
+
+static BOOL write_from_buf(int fd, struct rw_buffer *buf)
+{
+ int len;
+
+ len = write(fd, buf->data, buf->length);
+ if (len != buf->length)
+ return False;
+
+ return True;
+}
+
+static void peek_into_read_buf(struct rw_buffer *buf, uint8_t **out,
+ size_t *out_length)
+{
+ *out = buf->data;
+ *out_length = buf->length;
+}
+
+static void consumed_from_read_buf(struct rw_buffer *buf,
+ size_t length)
+{
+ memcpy(buf->data, buf->data+length, buf->length-length);
+ buf->length -= length;
+}
+
+static BOOL ldap_append_to_buf(struct ldap_message *msg, struct rw_buffer *buf)
+{
+ DATA_BLOB blob;
+ BOOL res;
+
+ if (!ldap_encode(msg, &blob))
+ return False;
+
+ res = append_to_buf(buf, blob.data, blob.length);
+
+ data_blob_free(&blob);
+ return res;
+}
+
+static void reply_unwilling(struct ldapsrv_connection *ldap_conn, int error)
+{
+ struct ldap_message *msg;
+ struct ldap_ExtendedResponse *r;
+
+ msg = new_ldap_message();
+
+ if (msg == NULL) {
+ ldapsrv_terminate_connection(ldap_conn, "new_ldap_message() failed");
+ return;
+ }
+
+ msg->messageid = 0;
+ r = &msg->r.ExtendedResponse;
+
+ /* When completely freaking out, OpenLDAP responds with an ExtResp */
+ msg->type = LDAP_TAG_ExtendedResponse;
+ r->response.resultcode = error;
+ r->response.dn = NULL;
+ r->response.errormessage = NULL;
+ r->response.referral = NULL;
+ r->name = NULL;
+ r->value.data = NULL;
+ r->value.length = 0;
+
+ ldap_append_to_buf(msg, &ldap_conn->out_buffer);
+
+ talloc_destroy(msg->mem_ctx);
+}
+
+static void ldap_reply_BindRequest(struct ldapsrv_connection *conn,
+ struct ldap_message *request)
+{
+ struct ldap_BindRequest *req = &request->r.BindRequest;
+
+ struct ldap_message *msg;
+ struct ldap_BindResponse *resp;
+
+ DEBUG(5, ("Binding as %s with pw %s\n",
+ req->dn, req->creds.password));
+
+ msg = new_ldap_message();
+
+ if (msg == NULL) {
+ ldapsrv_terminate_connection(conn, "new_ldap_message() failed");
+ return;
+ }
+
+ resp = &msg->r.BindResponse;
+
+ msg->messageid = request->messageid;
+ msg->type = LDAP_TAG_BindResponse;
+ resp->response.resultcode = 0;
+ resp->response.dn = NULL;
+ resp->response.errormessage = NULL;
+ resp->response.referral = NULL;
+ resp->SASL.secblob = data_blob(NULL, 0);
+
+ ldap_append_to_buf(msg, &conn->out_buffer);
+ talloc_destroy(msg->mem_ctx);
+}
+
+static void ldap_reply_SearchRequest(struct ldapsrv_connection *conn,
+ struct ldap_message *request)
+{
+ struct ldap_SearchRequest *req = &request->r.SearchRequest;
+
+ struct ldap_message *msg;
+ struct ldap_Result *resp;
+
+ DEBUG(10, ("Search filter: %s\n", req->filter));
+
+ msg = new_ldap_message();
+
+ if (msg == NULL) {
+ ldapsrv_terminate_connection(conn, "new_ldap_message() failed");
+ return;
+ }
+
+ msg->messageid = request->messageid;
+ resp = &msg->r.SearchResultDone;
+
+ /* Is this a rootdse request? */
+ if ((strlen(req->basedn) == 0) &&
+ (req->scope == LDAP_SEARCH_SCOPE_BASE) &&
+ strequal(req->filter, "(objectclass=*)")) {
+ msg->type = LDAP_TAG_SearchResultEntry;
+ msg->r.SearchResultEntry.dn = "";
+ msg->r.SearchResultEntry.num_attributes = 0;
+ msg->r.SearchResultEntry.attributes = NULL;
+ return;
+ }
+
+ msg->type = LDAP_TAG_SearchResultDone;
+ resp->resultcode = 0;
+ resp->dn = NULL;
+ resp->errormessage = NULL;
+ resp->referral = NULL;
+
+ ldap_append_to_buf(msg, &conn->out_buffer);
+ talloc_destroy(msg->mem_ctx);
+}
+
+static void switch_ldap_message(struct ldapsrv_connection *conn,
+ struct ldap_message *msg)
+{
+ switch(msg->type) {
+ case LDAP_TAG_BindRequest:
+ ldap_reply_BindRequest(conn, msg);
+ break;
+ case LDAP_TAG_SearchRequest:
+ ldap_reply_SearchRequest(conn, msg);
+ break;
+ default:
+ reply_unwilling(conn, 2);
+ break;
+ }
+}
+
+static void ldap_queue_run(struct server_connection *conn)
+{
+ struct ldapsrv_connection *ldap_conn = conn->private_data;
+
+ while (ldap_conn->in_queue) {
+ struct ldap_message_queue *req = ldap_conn->in_queue;
+ DLIST_REMOVE(ldap_conn->in_queue, req);
+
+ switch_ldap_message(ldap_conn, req->msg);
+ talloc_destroy(req->msg->mem_ctx);
+ }
+}
+
+/*
+ called when a LDAP socket becomes readable
+*/
+static void ldapsrv_recv(struct server_connection *conn, time_t t,
+ uint16_t flags)
+{
+ struct ldapsrv_connection *ldap_conn = conn->private_data;
+ uint8_t *buf;
+ int buf_length, msg_length;
+ DATA_BLOB blob;
+ ASN1_DATA data;
+ struct ldap_message *msg;
+ struct ldap_message_queue *queue_entry;
+
+ DEBUG(10,("ldapsrv_recv\n"));
+
+ if (!read_into_buf(conn->event.fde->fd, &ldap_conn->in_buffer)) {
+ ldapsrv_terminate_connection(ldap_conn, "read_into_buf() failed");
+ return;
+ }
+
+ 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. */
+
+ blob.data = buf;
+ blob.length = msg_length;
+
+ if (!asn1_load(&data, blob)) {
+ ldapsrv_terminate_connection(ldap_conn, "asn1_load() failed");
+ return;
+ }
+
+ msg = new_ldap_message();
+
+ if ((msg == NULL) || !ldap_decode(&data, msg)) {
+ ldapsrv_terminate_connection(ldap_conn, "ldap_decode() failed");
+ return;
+ }
+
+ queue_entry = talloc_p(msg->mem_ctx, struct ldap_message_queue);
+
+ if (queue_entry == NULL) {
+ ldapsrv_terminate_connection(ldap_conn, "alloc_p(msg->mem_ctx, struct ldap_message_queue) failed");
+ return;
+ }
+
+ queue_entry->msg = msg;
+
+ DLIST_ADD_END(ldap_conn->in_queue, queue_entry,
+ struct ldap_message_queue *);
+
+ consumed_from_read_buf(&ldap_conn->in_buffer, msg_length);
+
+ peek_into_read_buf(&ldap_conn->in_buffer, &buf, &buf_length);
+ }
+
+ ldap_queue_run(conn);
+
+ return;
+}
+
+/*
+ called when a LDAP socket becomes writable
+*/
+static void ldapsrv_send(struct server_connection *conn, time_t t,
+ uint16_t flags)
+{
+ struct ldapsrv_connection *ldap_conn = conn->private_data;
+
+ DEBUG(10,("ldapsrv_send\n"));
+
+ if (!write_from_buf(conn->event.fde->fd, &ldap_conn->out_buffer)) {
+ ldapsrv_terminate_connection(ldap_conn, "write_from_buf() failed");
+ return;
+ }
+
+ return;
+}
+
+/*
+ called when connection is idle
+*/
+static void ldapsrv_idle(struct server_connection *conn, time_t t)
+{
+ DEBUG(10,("ldapsrv_idle: not implemented!\n"));
+ return;
+}
+
+static void ldapsrv_close(struct server_connection *conn, const char *reason)
+{
+ struct ldapsrv_connection *ldap_conn = conn->private_data;
+
+ talloc_free(ldap_conn);
+
+ return;
+}
+
+/*
+ initialise a server_context from a open socket and register a event handler
+ for reading from that socket
+*/
+static void ldapsrv_accept(struct server_connection *conn)
+{
+ struct ldapsrv_connection *ldap_conn;
+
+ DEBUG(5, ("ldapsrv_accept\n"));
+
+ ldap_conn = talloc_p(NULL, struct ldapsrv_connection);
+
+ if (ldap_conn == NULL)
+ return;
+
+ ZERO_STRUCTP(ldap_conn);
+ ldap_conn->connection = conn;
+
+ conn->private_data = ldap_conn;
+
+ return;
+}
+
+/*
+ called on a fatal error that should cause this server to terminate
+*/
+static void ldapsrv_exit(struct server_service *service, const char *reason)
+{
+ DEBUG(1,("ldapsrv_exit\n"));
+ return;
+}
+
+static const struct server_service_ops ldap_server_ops = {
+ .name = "ldap",
+ .service_init = ldapsrv_init,
+ .accept_connection = ldapsrv_accept,
+ .recv_handler = ldapsrv_recv,
+ .send_handler = ldapsrv_send,
+ .idle_handler = ldapsrv_idle,
+ .close_connection = ldapsrv_close,
+ .service_exit = ldapsrv_exit,
+};
+
+const struct server_service_ops *ldapsrv_get_ops(void)
+{
+ return &ldap_server_ops;
+}
+
+NTSTATUS server_service_ldap_init(void)
+{
+ return NT_STATUS_OK;
+}