CVE-2020-10704: libcli ldap_message: Add search size limits to ldap_decode
authorGary Lockyer <gary@catalyst.net.nz>
Tue, 7 Apr 2020 20:49:23 +0000 (08:49 +1200)
committerGary Lockyer <gary@samba.org>
Mon, 4 May 2020 02:59:32 +0000 (02:59 +0000)
Add search request size limits to ldap_decode calls.

The ldap server uses the smb.conf variable
"ldap max search request size" which defaults to 250Kb.
For cldap the limit is hard coded as 4096.

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>
docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml [new file with mode: 0644]
lib/fuzzing/fuzz_ldap_decode.c
lib/param/loadparm.c
libcli/cldap/cldap.c
libcli/ldap/ldap_message.c
libcli/ldap/ldap_message.h
libcli/ldap/tests/ldap_message_test.c
source3/param/loadparm.c
source4/ldap_server/ldap_server.c
source4/libcli/ldap/ldap_client.c

diff --git a/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml
new file mode 100644 (file)
index 0000000..ebeb081
--- /dev/null
@@ -0,0 +1,18 @@
+<samba:parameter name="ldap max search request size"
+                 context="G"
+                 type="integer"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+       <para>
+               This parameter specifies the maximum permitted size (in bytes)
+               for an LDAP search request. 
+       </para>
+
+       <para>
+               If the request size exceeds this limit the request will be
+               rejected.
+       </para>
+</description>
+<value type="default">256000</value>
+<value type="example">4194304</value>
+</samba:parameter>
index d89ba6370612745e6634adbcdcb33ce23e186da8..e3bcf7b9d0ab809959f8bb53f05a544af1108a4a 100644 (file)
@@ -32,6 +32,12 @@ int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len)
        TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__);
        struct asn1_data *asn1;
        struct ldap_message *ldap_msg;
+       struct ldap_request_limits limits = {
+               /*
+                * The default size is currently 256000 bytes
+                */
+               .max_search_size = 256000
+       };
        NTSTATUS status;
 
        /*
@@ -50,7 +56,8 @@ int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len)
                goto out;
        }
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
+       status = ldap_decode(
+               asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
 
 out:
        talloc_free(mem_ctx);
index 813766a6b609b56e4fe8e27640155fffe2526874..777999a3a76e65174e692a28c867f8d11280f825 100644 (file)
@@ -3060,6 +3060,8 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
                lp_ctx, "ldap max anonymous request size", "256000");
        lpcfg_do_global_parameter(
                lp_ctx, "ldap max authenticated request size", "16777216");
+       lpcfg_do_global_parameter(
+               lp_ctx, "ldap max search request size", "256000");
 
        for (i = 0; parm_table[i].label; i++) {
                if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
index 3f68772851722a5de3645812de8748178f0d6d91..7de72b5e8996d424023a52186afd5fa995f4262e 100644 (file)
@@ -111,6 +111,11 @@ struct cldap_search_state {
        struct tevent_req *req;
 };
 
+/*
+ * For CLDAP we limit the maximum search request size to 4kb
+ */
+#define MAX_SEARCH_REQUEST 4096
+
 static int cldap_socket_destructor(struct cldap_socket *c)
 {
        while (c->searches.list) {
@@ -228,6 +233,9 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
        void *p;
        struct cldap_search_state *search;
        NTSTATUS status;
+       struct ldap_request_limits limits = {
+               .max_search_size = MAX_SEARCH_REQUEST
+       };
 
        if (in->recv_errno != 0) {
                goto error;
@@ -246,7 +254,7 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
        }
 
        /* this initial decode is used to find the message id */
-       status = ldap_decode(asn1, NULL, in->ldap_msg);
+       status = ldap_decode(asn1, &limits, NULL, in->ldap_msg);
        if (!NT_STATUS_IS_OK(status)) {
                goto nterror;
        }
@@ -774,6 +782,9 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
                                           struct cldap_search_state);
        struct ldap_message *ldap_msg;
        NTSTATUS status;
+       struct ldap_request_limits limits = {
+               .max_search_size = MAX_SEARCH_REQUEST
+       };
 
        if (tevent_req_is_nterror(req, &status)) {
                goto failed;
@@ -784,7 +795,7 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
                goto nomem;
        }
 
-       status = ldap_decode(state->response.asn1, NULL, ldap_msg);
+       status = ldap_decode(state->response.asn1, &limits, NULL, ldap_msg);
        if (!NT_STATUS_IS_OK(status)) {
                goto failed;
        }
@@ -800,7 +811,8 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
                *io->out.response = ldap_msg->r.SearchResultEntry;
 
                /* decode the 2nd part */
-               status = ldap_decode(state->response.asn1, NULL, ldap_msg);
+               status = ldap_decode(
+                       state->response.asn1, &limits, NULL, ldap_msg);
                if (!NT_STATUS_IS_OK(status)) {
                        goto failed;
                }
index ba82bddeab10ac0302f36a332752ffb1563cb41e..d38fa0b3b615db7b2630135199ffedb7425cf299 100644 (file)
@@ -1162,6 +1162,7 @@ static bool ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data,
 /* This routine returns LDAP status codes */
 
 _PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data,
+                             const struct ldap_request_limits *limits,
                              const struct ldap_control_handler *control_handlers,
                              struct ldap_message *msg)
 {
index 2f64881c053e9f590502055de0309d8377d6d46c..19bfb99ac97c71c1693b73c38a5e9966d10f98fb 100644 (file)
@@ -213,10 +213,15 @@ struct ldap_control_handler {
        bool (*encode)(void *mem_ctx, void *in, DATA_BLOB *out);
 };
 
+struct ldap_request_limits {
+       unsigned max_search_size;
+};
+
 struct asn1_data;
 
 struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx);
 NTSTATUS ldap_decode(struct asn1_data *data,
+                    const struct ldap_request_limits *limits,
                     const struct ldap_control_handler *control_handlers,
                     struct ldap_message *msg);
 bool ldap_encode(struct ldap_message *msg,
index 9cc9cc5d8a092fec3c308e860f5cabced49ca38f..c5aacd4bc6b1296fc3f5fb4e116515e0d77663e3 100644 (file)
@@ -117,6 +117,9 @@ static void test_empty_input(void **state)
        NTSTATUS status;
        uint8_t buf[0];
        size_t len = 0;
+       struct ldap_request_limits limits = {
+               .max_search_size = 256000,
+       };
 
 
        asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH);
@@ -127,7 +130,8 @@ static void test_empty_input(void **state)
        ldap_msg = talloc(test_ctx, struct ldap_message);
        assert_non_null(ldap_msg);
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
+       status = ldap_decode(
+               asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
        assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status);
 }
 
@@ -149,6 +153,9 @@ static void test_recursion_depth_large(void **state)
        uint8_t *buffer = NULL;
        const size_t BUFF_SIZE = 1048576;
        size_t len;
+       struct ldap_request_limits limits = {
+               .max_search_size = 256000,
+       };
 
 
        /*
@@ -169,7 +176,8 @@ static void test_recursion_depth_large(void **state)
        ldap_msg = talloc(test_ctx, struct ldap_message);
        assert_non_null(ldap_msg);
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
+       status = ldap_decode(
+               asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
        assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status);
 }
 
@@ -189,6 +197,9 @@ static void test_recursion_depth_equals_max(void **state)
        uint8_t *buffer = NULL;
        const size_t BUFF_SIZE = 1048576;
        size_t len;
+       struct ldap_request_limits limits = {
+               .max_search_size = 256000,
+       };
 
 
        buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE);
@@ -205,7 +216,8 @@ static void test_recursion_depth_equals_max(void **state)
        ldap_msg = talloc(test_ctx, struct ldap_message);
        assert_non_null(ldap_msg);
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
+       status = ldap_decode(
+               asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
        assert_true(NT_STATUS_IS_OK(status));
 }
 
@@ -225,6 +237,9 @@ static void test_recursion_depth_greater_than_max(void **state)
        uint8_t *buffer = NULL;
        const size_t BUFF_SIZE = 1048576;
        size_t len;
+       struct ldap_request_limits limits = {
+               .max_search_size = 256000,
+       };
 
 
        buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE);
@@ -241,7 +256,8 @@ static void test_recursion_depth_greater_than_max(void **state)
        ldap_msg = talloc(test_ctx, struct ldap_message);
        assert_non_null(ldap_msg);
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
+       status = ldap_decode(
+               asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
        assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status);
 }
 
index cbdc132e2f5fd644ed7fda3fa296a2abf4795a5e..ce2aca2c5a450a3ecc229773713d46db4c3b4e2f 100644 (file)
@@ -958,6 +958,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 
        Globals.ldap_max_anonymous_request_size = 256000;
        Globals.ldap_max_authenticated_request_size = 16777216;
+       Globals.ldap_max_search_request_size = 256000;
 
        /* Now put back the settings that were set with lp_set_cmdline() */
        apply_lp_set_cmdline();
index a730667abb91eb51b719d2ceec8714be6dc508f8..a9b162b284e60fbe63d20cec5f47e25bbfe03d9f 100644 (file)
@@ -538,6 +538,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
        struct asn1_data *asn1;
        DATA_BLOB blob;
        int ret = LDAP_SUCCESS;
+       struct ldap_request_limits limits = {0};
 
        conn->sockets.read_req = NULL;
 
@@ -593,8 +594,13 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
                return;
        }
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(),
-                            call->request);
+       limits.max_search_size =
+               lpcfg_ldap_max_search_request_size(conn->lp_ctx);
+       status = ldap_decode(
+               asn1,
+               &limits,
+               samba_ldap_control_handlers(),
+               call->request);
        if (!NT_STATUS_IS_OK(status)) {
                ldapsrv_terminate_connection(conn, nt_errstr(status));
                return;
index 319ef3a60a7c1ef7743d8bed5a3dc74fc6ab9cb8..abe4e523585e00f2124336967045f304fa8e42e8 100644 (file)
@@ -277,6 +277,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq)
        struct ldap_message *msg;
        struct asn1_data *asn1;
        DATA_BLOB blob;
+       struct ldap_request_limits limits = {0};
 
        msg = talloc_zero(conn, struct ldap_message);
        if (msg == NULL) {
@@ -306,7 +307,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq)
 
        asn1_load_nocopy(asn1, blob.data, blob.length);
 
-       status = ldap_decode(asn1, samba_ldap_control_handlers(), msg);
+       status = ldap_decode(asn1, &limits, samba_ldap_control_handlers(), msg);
        asn1_free(asn1);
        if (!NT_STATUS_IS_OK(status)) {
                TALLOC_FREE(msg);