CVE-2020-10704: S4 ldap server: Limit request sizes
authorGary Lockyer <gary@catalyst.net.nz>
Wed, 8 Apr 2020 03:32:22 +0000 (15:32 +1200)
committerKarolin Seeger <kseeger@samba.org>
Wed, 22 Apr 2020 10:50:42 +0000 (12:50 +0200)
Check the size of authenticated and anonymous ldap requests and reject
them if they exceed the limits in smb.conf

Credit to OSS-Fuzz

REF: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=20454
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14334

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

diff --git a/selftest/knownfail.d/ldap_raw b/selftest/knownfail.d/ldap_raw
deleted file mode 100644 (file)
index 8bd2ee5..0000000
+++ /dev/null
@@ -1 +0,0 @@
-^samba.tests.ldap_raw.samba.tests.ldap_raw.RawLdapTest.test_search_exceeds_maximum_permitted_size\(ad_dc\)
index 6d32932990988ebc938284d5446a3ef778b88f1c..a730667abb91eb51b719d2ceec8714be6dc508f8 100644 (file)
@@ -441,6 +441,10 @@ static void ldapsrv_accept_tls_done(struct tevent_req *subreq)
 }
 
 static void ldapsrv_call_read_done(struct tevent_req *subreq);
+static NTSTATUS ldapsrv_packet_check(
+       void *private_data,
+       DATA_BLOB blob,
+       size_t *packet_size);
 
 static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
 {
@@ -494,7 +498,7 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
                                            conn->connection->event.ctx,
                                            conn->sockets.active,
                                            7, /* initial_read_size */
-                                           ldap_full_packet,
+                                           ldapsrv_packet_check,
                                            conn);
        if (subreq == NULL) {
                ldapsrv_terminate_connection(conn, "ldapsrv_call_read_next: "
@@ -520,6 +524,9 @@ static bool ldapsrv_call_read_next(struct ldapsrv_connection *conn)
 }
 
 static void ldapsrv_call_process_done(struct tevent_req *subreq);
+static int ldapsrv_check_packet_size(
+       struct ldapsrv_connection *conn,
+       size_t size);
 
 static void ldapsrv_call_read_done(struct tevent_req *subreq)
 {
@@ -530,6 +537,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
        struct ldapsrv_call *call;
        struct asn1_data *asn1;
        DATA_BLOB blob;
+       int ret = LDAP_SUCCESS;
 
        conn->sockets.read_req = NULL;
 
@@ -560,6 +568,14 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
                return;
        }
 
+       ret = ldapsrv_check_packet_size(conn, blob.length);
+       if (ret != LDAP_SUCCESS) {
+               ldapsrv_terminate_connection(
+                       conn,
+                       "Request packet too large");
+               return;
+       }
+
        asn1 = asn1_init(call, ASN1_MAX_TREE_DEPTH);
        if (asn1 == NULL) {
                ldapsrv_terminate_connection(conn, "no memory");
@@ -1362,6 +1378,84 @@ static void ldapsrv_post_fork(struct task_server *task, struct process_details *
        }
 }
 
+/*
+ * Check the size of an ldap request packet.
+ *
+ * For authenticated connections the maximum packet size is controlled by
+ * the smb.conf parameter "ldap max authenticated request size"
+ *
+ * For anonymous connections the maximum packet size is controlled by
+ * the smb.conf parameter "ldap max anonymous request size"
+ */
+static int ldapsrv_check_packet_size(
+       struct ldapsrv_connection *conn,
+       size_t size)
+{
+       bool is_anonymous = false;
+       size_t max_size = 0;
+
+       max_size = lpcfg_ldap_max_anonymous_request_size(conn->lp_ctx);
+       if (size <= max_size) {
+               return LDAP_SUCCESS;
+       }
+
+       /*
+        * Request is larger than the maximum unauthenticated request size.
+        * As this code is called frequently we avoid calling
+        * security_token_is_anonymous if possible
+        */
+       if (conn->session_info != NULL &&
+               conn->session_info->security_token != NULL) {
+               is_anonymous = security_token_is_anonymous(
+                       conn->session_info->security_token);
+       }
+
+       if (is_anonymous) {
+               DBG_WARNING(
+                       "LDAP request size (%zu) exceeds (%zu)\n",
+                       size,
+                       max_size);
+               return LDAP_UNWILLING_TO_PERFORM;
+       }
+
+       max_size = lpcfg_ldap_max_authenticated_request_size(conn->lp_ctx);
+       if (size > max_size) {
+               DBG_WARNING(
+                       "LDAP request size (%zu) exceeds (%zu)\n",
+                       size,
+                       max_size);
+               return LDAP_UNWILLING_TO_PERFORM;
+       }
+       return LDAP_SUCCESS;
+
+}
+
+/*
+ * Check that the blob contains enough data to be a valid packet
+ * If there is a packet header check the size to ensure that it does not
+ * exceed the maximum sizes.
+ *
+ */
+static NTSTATUS ldapsrv_packet_check(
+       void *private_data,
+       DATA_BLOB blob,
+       size_t *packet_size)
+{
+       NTSTATUS ret;
+       struct ldapsrv_connection *conn = private_data;
+       int result = LDB_SUCCESS;
+
+       ret = ldap_full_packet(private_data, blob, packet_size);
+       if (!NT_STATUS_IS_OK(ret)) {
+               return ret;
+       }
+       result = ldapsrv_check_packet_size(conn, *packet_size);
+       if (result != LDAP_SUCCESS) {
+               return NT_STATUS_LDAP(result);
+       }
+       return NT_STATUS_OK;
+}
+
 NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx)
 {
        static const struct service_details details = {