lib: modules: Change XXX_init interface from XXX_init(void) to XXX_init(TALLOC_CTX *)
[amitay/samba.git] / source4 / ldap_server / ldap_server.c
index 411be294401a67b3b4567783a9fb9faae45d857f..747e25dde22876ab9f1d48d28b9fb9f6fcdda5c6 100644 (file)
@@ -46,6 +46,7 @@
 #include "../lib/tsocket/tsocket.h"
 #include "../lib/util/tevent_ntstatus.h"
 #include "../libcli/util/tstream.h"
+#include "libds/common/roles.h"
 
 static void ldapsrv_terminate_connection_done(struct tevent_req *subreq);
 
@@ -61,9 +62,12 @@ static void ldapsrv_terminate_connection(struct ldapsrv_connection *conn,
                return;
        }
 
+       DLIST_REMOVE(conn->service->connections, conn);
+
        conn->limits.endtime = timeval_current_ofs(0, 500);
 
        tevent_queue_stop(conn->sockets.send_queue);
+       TALLOC_FREE(conn->sockets.read_req);
        if (conn->active_call) {
                tevent_req_cancel(conn->active_call);
                conn->active_call = NULL;
@@ -99,10 +103,9 @@ static void ldapsrv_terminate_connection_done(struct tevent_req *subreq)
        struct ldapsrv_connection *conn =
                tevent_req_callback_data(subreq,
                struct ldapsrv_connection);
-       int ret;
        int sys_errno;
 
-       ret = tstream_disconnect_recv(subreq, &sys_errno);
+       tstream_disconnect_recv(subreq, &sys_errno);
        TALLOC_FREE(subreq);
 
        if (conn->sockets.active == conn->sockets.raw) {
@@ -166,6 +169,7 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn)
        conn->limits.initial_timeout = 120;
        conn->limits.conn_idle_time = 900;
        conn->limits.max_page_size = 1000;
+       conn->limits.max_notifications = 5;
        conn->limits.search_timeout = 120;
 
 
@@ -218,9 +222,8 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn)
                int policy_value, s;
 
                s = sscanf((const char *)el->values[i].data, "%255[^=]=%d", policy_name, &policy_value);
-               if (ret != 2 || policy_value == 0)
+               if (s != 2 || policy_value == 0)
                        continue;
-
                if (strcasecmp("InitRecvTimeout", policy_name) == 0) {
                        conn->limits.initial_timeout = policy_value;
                        continue;
@@ -233,6 +236,10 @@ static int ldapsrv_load_limits(struct ldapsrv_connection *conn)
                        conn->limits.max_page_size = policy_value;
                        continue;
                }
+               if (strcasecmp("MaxNotificationPerConn", policy_name) == 0) {
+                       conn->limits.max_notifications = policy_value;
+                       continue;
+               }
                if (strcasecmp("MaxQueryDuration", policy_name) == 0) {
                        conn->limits.search_timeout = policy_value;
                        continue;
@@ -334,6 +341,14 @@ static void ldapsrv_accept(struct stream_connection *c,
 
        conn->session_info = session_info;
 
+       conn->sockets.active = conn->sockets.raw;
+
+       if (conn->is_privileged) {
+               conn->require_strong_auth = LDAP_SERVER_REQUIRE_STRONG_AUTH_NO;
+       } else {
+               conn->require_strong_auth = lpcfg_ldap_server_require_strong_auth(conn->lp_ctx);
+       }
+
        if (!NT_STATUS_IS_OK(ldapsrv_backend_Init(conn))) {
                ldapsrv_terminate_connection(conn, "backend Init failed");
                return;
@@ -345,7 +360,7 @@ static void ldapsrv_accept(struct stream_connection *c,
        /* register the server */       
        irpc_add_name(c->msg_ctx, "ldap_server");
 
-       conn->sockets.active = conn->sockets.raw;
+       DLIST_ADD_END(ldapsrv_service->connections, conn);
 
        if (port != 636 && port != 3269) {
                ldapsrv_call_read_next(conn);
@@ -405,7 +420,11 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
 {
        struct tevent_req *subreq;
 
-       if (timeval_is_zero(&conn->limits.endtime)) {
+       if (conn->pending_calls != NULL) {
+               conn->limits.endtime = timeval_zero();
+
+               ldapsrv_notification_retry_setup(conn->service, false);
+       } else if (timeval_is_zero(&conn->limits.endtime)) {
                conn->limits.endtime =
                        timeval_current_ofs(conn->limits.initial_timeout, 0);
        } else {
@@ -413,8 +432,12 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
                        timeval_current_ofs(conn->limits.conn_idle_time, 0);
        }
 
+       if (conn->sockets.read_req != NULL) {
+               return true;
+       }
+
        /*
-        * The minimun size of a LDAP pdu is 7 bytes
+        * The minimum size of a LDAP pdu is 7 bytes
         *
         * dumpasn1 -hh ldap-unbind-min.dat
         *
@@ -452,10 +475,13 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
                                "no memory for tstream_read_pdu_blob_send");
                return false;
        }
-       tevent_req_set_endtime(subreq,
-                              conn->connection->event.ctx,
-                              conn->limits.endtime);
+       if (!timeval_is_zero(&conn->limits.endtime)) {
+               tevent_req_set_endtime(subreq,
+                                      conn->connection->event.ctx,
+                                      conn->limits.endtime);
+       }
        tevent_req_set_callback(subreq, ldapsrv_call_read_done, conn);
+       conn->sockets.read_req = subreq;
        return true;
 }
 
@@ -471,6 +497,8 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
        struct asn1_data *asn1;
        DATA_BLOB blob;
 
+       conn->sockets.read_req = NULL;
+
        call = talloc_zero(conn, struct ldapsrv_call);
        if (!call) {
                ldapsrv_terminate_connection(conn, "no memory");
@@ -537,6 +565,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
        conn->active_call = subreq;
 }
 
+
 static void ldapsrv_call_writev_done(struct tevent_req *subreq);
 
 static void ldapsrv_call_process_done(struct tevent_req *subreq)
@@ -583,7 +612,9 @@ static void ldapsrv_call_process_done(struct tevent_req *subreq)
        }
 
        if (blob.length == 0) {
-               TALLOC_FREE(call);
+               if (!call->notification.busy) {
+                       TALLOC_FREE(call);
+               }
 
                ldapsrv_call_read_next(conn);
                return;
@@ -647,7 +678,9 @@ static void ldapsrv_call_writev_done(struct tevent_req *subreq)
                return;
        }
 
-       TALLOC_FREE(call);
+       if (!call->notification.busy) {
+               TALLOC_FREE(call);
+       }
 
        ldapsrv_call_read_next(conn);
 }
@@ -681,6 +714,112 @@ static void ldapsrv_call_postprocess_done(struct tevent_req *subreq)
        ldapsrv_call_read_next(conn);
 }
 
+static void ldapsrv_notification_retry_done(struct tevent_req *subreq);
+
+void ldapsrv_notification_retry_setup(struct ldapsrv_service *service, bool force)
+{
+       struct ldapsrv_connection *conn = NULL;
+       struct timeval retry;
+       size_t num_pending = 0;
+       size_t num_active = 0;
+
+       if (force) {
+               TALLOC_FREE(service->notification.retry);
+               service->notification.generation += 1;
+       }
+
+       if (service->notification.retry != NULL) {
+               return;
+       }
+
+       for (conn = service->connections; conn != NULL; conn = conn->next) {
+               if (conn->pending_calls == NULL) {
+                       continue;
+               }
+
+               num_pending += 1;
+
+               if (conn->pending_calls->notification.generation !=
+                   service->notification.generation)
+               {
+                       num_active += 1;
+               }
+       }
+
+       if (num_pending == 0) {
+               return;
+       }
+
+       if (num_active != 0) {
+               retry = timeval_current_ofs(0, 100);
+       } else {
+               retry = timeval_current_ofs(5, 0);
+       }
+
+       service->notification.retry = tevent_wakeup_send(service,
+                                                        service->task->event_ctx,
+                                                        retry);
+       if (service->notification.retry == NULL) {
+               /* retry later */
+               return;
+       }
+
+       tevent_req_set_callback(service->notification.retry,
+                               ldapsrv_notification_retry_done,
+                               service);
+}
+
+static void ldapsrv_notification_retry_done(struct tevent_req *subreq)
+{
+       struct ldapsrv_service *service =
+               tevent_req_callback_data(subreq,
+               struct ldapsrv_service);
+       struct ldapsrv_connection *conn = NULL;
+       struct ldapsrv_connection *conn_next = NULL;
+       bool ok;
+
+       service->notification.retry = NULL;
+
+       ok = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ok) {
+               /* ignore */
+       }
+
+       for (conn = service->connections; conn != NULL; conn = conn_next) {
+               struct ldapsrv_call *call = conn->pending_calls;
+
+               conn_next = conn->next;
+
+               if (conn->pending_calls == NULL) {
+                       continue;
+               }
+
+               if (conn->active_call != NULL) {
+                       continue;
+               }
+
+               DLIST_DEMOTE(conn->pending_calls, call);
+               call->notification.generation =
+                               service->notification.generation;
+
+               /* 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");
+                       continue;
+               }
+               tevent_req_set_callback(subreq, ldapsrv_call_process_done, call);
+               conn->active_call = subreq;
+       }
+
+       ldapsrv_notification_retry_setup(service, false);
+}
+
 struct ldapsrv_process_call_state {
        struct ldapsrv_call *call;
 };
@@ -907,7 +1046,7 @@ static void ldapsrv_task_init(struct task_server *task)
                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;
        }
@@ -936,9 +1075,10 @@ static void ldapsrv_task_init(struct task_server *task)
                                           lpcfg_tls_cafile(ldap_service, task->lp_ctx),
                                           lpcfg_tls_crlfile(ldap_service, task->lp_ctx),
                                           lpcfg_tls_dhpfile(ldap_service, task->lp_ctx),
+                                          lpcfg_tls_priority(task->lp_ctx),
                                           &ldap_service->tls_params);
        if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0,("ldapsrv failed tstream_tls_patams_server - %s\n",
+               DEBUG(0,("ldapsrv failed tstream_tls_params_server - %s\n",
                         nt_errstr(status)));
                goto failed;
        }
@@ -964,18 +1104,24 @@ static void ldapsrv_task_init(struct task_server *task)
                        if (!NT_STATUS_IS_OK(status)) goto failed;
                }
        } else {
-               const char **wcard;
+               char **wcard;
                int i;
-               wcard = iface_list_wildcard(task, task->lp_ctx);
+               int num_binds = 0;
+               wcard = iface_list_wildcard(task);
                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;
+                       if (NT_STATUS_IS_OK(status)) {
+                               num_binds++;
+                       }
                }
                talloc_free(wcard);
+               if (num_binds == 0) {
+                       goto failed;
+               }
        }
 
        ldapi_path = lpcfg_private_path(ldap_service, task->lp_ctx, "ldapi");
@@ -1003,7 +1149,7 @@ static void ldapsrv_task_init(struct task_server *task)
         * 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)) {
+       if (!directory_create_or_exist(priv_dir, 0750)) {
                task_server_terminate(task, "Cannot create ldap "
                                      "privileged ldapi directory", true);
                return;
@@ -1026,6 +1172,9 @@ static void ldapsrv_task_init(struct task_server *task)
        }
 
 #endif
+
+       /* register the server */
+       irpc_add_name(task->msg_ctx, "ldap_server");
        return;
 
 failed:
@@ -1033,7 +1182,7 @@ failed:
 }
 
 
-NTSTATUS server_service_ldap_init(void)
+NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx)
 {
        return register_server_service("ldap", ldapsrv_task_init);
 }