diff options
author | Gary Lockyer <gary@catalyst.net.nz> | 2020-04-08 08:49:23 +1200 |
---|---|---|
committer | Karolin Seeger <kseeger@samba.org> | 2020-04-22 12:50:42 +0200 |
commit | db78f2667eb51c106c66edebcf66914ea580bfc6 (patch) | |
tree | 7a3fd1e016a79e22e0aca35c9a4ff9d316987860 | |
parent | 8729c05b1cd6a63d9f8e163b2e438007db3eb4f8 (diff) | |
download | samba-db78f2667eb51c106c66edebcf66914ea580bfc6.tar.gz |
CVE-2020-10704: libcli ldap_message: Add search size limits to ldap_decode
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>
-rw-r--r-- | docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml | 18 | ||||
-rw-r--r-- | lib/fuzzing/fuzz_ldap_decode.c | 9 | ||||
-rw-r--r-- | lib/param/loadparm.c | 2 | ||||
-rw-r--r-- | libcli/cldap/cldap.c | 18 | ||||
-rw-r--r-- | libcli/ldap/ldap_message.c | 1 | ||||
-rw-r--r-- | libcli/ldap/ldap_message.h | 5 | ||||
-rw-r--r-- | libcli/ldap/tests/ldap_message_test.c | 24 | ||||
-rw-r--r-- | source3/param/loadparm.c | 1 | ||||
-rw-r--r-- | source4/ldap_server/ldap_server.c | 10 | ||||
-rw-r--r-- | source4/libcli/ldap/ldap_client.c | 3 |
10 files changed, 80 insertions, 11 deletions
diff --git a/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml new file mode 100644 index 00000000000..ebeb0816c01 --- /dev/null +++ b/docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml @@ -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> diff --git a/lib/fuzzing/fuzz_ldap_decode.c b/lib/fuzzing/fuzz_ldap_decode.c index d89ba637061..e3bcf7b9d0a 100644 --- a/lib/fuzzing/fuzz_ldap_decode.c +++ b/lib/fuzzing/fuzz_ldap_decode.c @@ -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); diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c index 2eed5aa082d..63291283905 100644 --- a/lib/param/loadparm.c +++ b/lib/param/loadparm.c @@ -3045,6 +3045,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)) { diff --git a/libcli/cldap/cldap.c b/libcli/cldap/cldap.c index 3f687728517..7de72b5e899 100644 --- a/libcli/cldap/cldap.c +++ b/libcli/cldap/cldap.c @@ -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; } diff --git a/libcli/ldap/ldap_message.c b/libcli/ldap/ldap_message.c index ba82bddeab1..d38fa0b3b61 100644 --- a/libcli/ldap/ldap_message.c +++ b/libcli/ldap/ldap_message.c @@ -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) { diff --git a/libcli/ldap/ldap_message.h b/libcli/ldap/ldap_message.h index 2f64881c053..19bfb99ac97 100644 --- a/libcli/ldap/ldap_message.h +++ b/libcli/ldap/ldap_message.h @@ -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, diff --git a/libcli/ldap/tests/ldap_message_test.c b/libcli/ldap/tests/ldap_message_test.c index 9cc9cc5d8a0..c5aacd4bc6b 100644 --- a/libcli/ldap/tests/ldap_message_test.c +++ b/libcli/ldap/tests/ldap_message_test.c @@ -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); } diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c index dd51042c125..d3d81f6ece5 100644 --- a/source3/param/loadparm.c +++ b/source3/param/loadparm.c @@ -957,6 +957,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(); diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c index a730667abb9..a9b162b284e 100644 --- a/source4/ldap_server/ldap_server.c +++ b/source4/ldap_server/ldap_server.c @@ -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; diff --git a/source4/libcli/ldap/ldap_client.c b/source4/libcli/ldap/ldap_client.c index 319ef3a60a7..abe4e523585 100644 --- a/source4/libcli/ldap/ldap_client.c +++ b/source4/libcli/ldap/ldap_client.c @@ -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); |