ldap_server: Add explict repsonse size limit of 256MB
authorAndrew Bartlett <abartlet@samba.org>
Wed, 8 May 2019 02:03:50 +0000 (14:03 +1200)
committerAndrew Bartlett <abartlet@samba.org>
Mon, 20 May 2019 04:01:11 +0000 (04:01 +0000)
This allows us to replace the implicit limit via data_blob_append()
removed in the previous commit.

Signed-off-by: Andrew Bartlett <abartlet@samba.org>
Reviewed-by: Gary Lockyer <gary@catalyst.net.nz>
selftest/knownfail.d/large-dc [deleted file]
source4/ldap_server/ldap_backend.c
source4/ldap_server/ldap_server.h

diff --git a/selftest/knownfail.d/large-dc b/selftest/knownfail.d/large-dc
deleted file mode 100644 (file)
index 9a9e53a..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# Current Samba drops the LDAP socket after 256MB of replies, rather
-# than giving an nice error message
-^samba4.ldap.large_ldap.gssapi.python\(vampire_dc\).__main__.LargeLDAPTest.test_unindexed_iterator_search
-^samba4.ldap.large_ldap.ntlmssp.python\(ad_dc_default\).__main__.LargeLDAPTest.test_unindexed_iterator_search
-^samba4.ldap.large_ldap.ldaps.python\(ad_dc_ntvfs\).__main__.LargeLDAPTest.test_unindexed_iterator_search
-^samba4.ldap.large_ldap.straight_ldap.python\(fl2008r2dc\).__main__.LargeLDAPTest.test_unindexed_iterator_search
index 4be07d7c7fbe30bda5f82b889b5fea59ba88e69c..7e66672834e2316e260f534e4344abf5685de5cf 100644 (file)
@@ -258,26 +258,75 @@ struct ldapsrv_reply *ldapsrv_init_reply(struct ldapsrv_call *call, uint8_t type
        return reply;
 }
 
-NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
+/*
+ * Encode a reply to an LDAP client as ASN.1, free the original memory
+ */
+static NTSTATUS ldapsrv_encode(TALLOC_CTX *mem_ctx,
+                              struct ldapsrv_reply *reply)
 {
        bool bret = ldap_encode(reply->msg,
                                samba_ldap_control_handlers(),
                                &reply->blob,
-                               call);
+                               mem_ctx);
        TALLOC_FREE(reply->msg);
        if (!bret) {
-               DEBUG(0,("Failed to encode ldap reply of type %d: ldap_encode() failed\n",
-                        call->replies->msg->type));
+               DEBUG(0,("Failed to encode ldap reply of type %d: "
+                        "ldap_encode() failed\n",
+                        reply->msg->type));
                return NT_STATUS_NO_MEMORY;
        }
 
        talloc_set_name_const(reply->blob.data,
                              "Outgoing, encoded single LDAP reply");
 
-       DLIST_ADD_END(call->replies, reply);
        return NT_STATUS_OK;
 }
 
+/*
+ * Queue a reply (encoding it also), even if it would exceed the
+ * limit.  This allows the error packet with LDAP_SIZE_LIMIT_EXCEEDED
+ * to be sent
+ */
+static NTSTATUS ldapsrv_queue_reply_forced(struct ldapsrv_call *call,
+                                          struct ldapsrv_reply *reply)
+{
+       NTSTATUS status = ldapsrv_encode(call, reply);
+
+       if (NT_STATUS_IS_OK(status)) {
+               DLIST_ADD_END(call->replies, reply);
+       }
+       return status;
+}
+
+/*
+ * Queue a reply (encoding it also) but check we do not send more than
+ * LDAP_SERVER_MAX_REPLY_SIZE of responses as a way to limit the
+ * amount of data a client can make us allocate.
+ */
+NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply)
+{
+       NTSTATUS status = ldapsrv_encode(call, reply);
+
+       if (!NT_STATUS_IS_OK(status)) {
+               return status;
+       }
+
+       if (call->reply_size > call->reply_size + reply->blob.length
+           || call->reply_size + reply->blob.length > LDAP_SERVER_MAX_REPLY_SIZE) {
+               DBG_WARNING("Refusing to queue LDAP search response size "
+                           "of more than %zu bytes\n",
+                           LDAP_SERVER_MAX_REPLY_SIZE);
+               TALLOC_FREE(reply->blob.data);
+               return NT_STATUS_FILE_TOO_LARGE;
+       }
+
+       call->reply_size += reply->blob.length;
+
+       DLIST_ADD_END(call->replies, reply);
+
+       return status;
+}
+
 static NTSTATUS ldapsrv_unwilling(struct ldapsrv_call *call, int error)
 {
        struct ldapsrv_reply *reply;
@@ -708,7 +757,14 @@ static NTSTATUS ldapsrv_SearchRequest(struct ldapsrv_call *call)
                        }
 queue_reply:
                        status = ldapsrv_queue_reply(call, ent_r);
-                       if (!NT_STATUS_IS_OK(status)) {
+                       if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_TOO_LARGE)) {
+                               result = LDB_ERR_SIZE_LIMIT_EXCEEDED;
+                               ldb_asprintf_errstring(samdb,
+                                                      "LDAP search response size "
+                                                      "limited to %zu bytes\n",
+                                                      LDAP_SERVER_MAX_REPLY_SIZE);
+                               goto reply;
+                       } else if (!NT_STATUS_IS_OK(status)) {
                                result = ldb_operr(samdb);
                                goto reply;
                        }
@@ -782,7 +838,7 @@ reply:
 
        talloc_free(local_ctx);
 
-       return ldapsrv_queue_reply(call, done_r);
+       return ldapsrv_queue_reply_forced(call, done_r);
 }
 
 static NTSTATUS ldapsrv_ModifyRequest(struct ldapsrv_call *call)
index 75c8adcb5efd056c0eb92a9cb318897e911b0383..48634e7610c2a791032c6b47bdc15934b9a9a9c2 100644 (file)
@@ -74,6 +74,7 @@ struct ldapsrv_call {
        } *replies;
        struct iovec *out_iov;
        size_t iov_count;
+       size_t reply_size;
 
        struct tevent_req *(*wait_send)(TALLOC_CTX *mem_ctx,
                                        struct tevent_context *ev,
@@ -93,6 +94,12 @@ struct ldapsrv_call {
        } notification;
 };
 
+/*
+ * This matches the previous implicit size limit via talloc's maximum
+ * allocation size
+ */
+#define LDAP_SERVER_MAX_REPLY_SIZE ((size_t)(256 * 1024 * 1024))
+
 struct ldapsrv_service {
        struct tstream_tls_params *tls_params;
        struct task_server *task;