mirror of
https://github.com/samba-team/samba.git
synced 2025-01-10 01:18:15 +03:00
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>
This commit is contained in:
parent
28ee4acc83
commit
3149ea0a8a
18
docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml
Normal file
18
docs-xml/smbdotconf/ldap/ldapmaxsearchrequest.xml
Normal file
@ -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>
|
@ -32,6 +32,12 @@ int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len)
|
|||||||
TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__);
|
TALLOC_CTX *mem_ctx = talloc_init(__FUNCTION__);
|
||||||
struct asn1_data *asn1;
|
struct asn1_data *asn1;
|
||||||
struct ldap_message *ldap_msg;
|
struct ldap_message *ldap_msg;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
/*
|
||||||
|
* The default size is currently 256000 bytes
|
||||||
|
*/
|
||||||
|
.max_search_size = 256000
|
||||||
|
};
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -50,7 +56,8 @@ int LLVMFuzzerTestOneInput(uint8_t *buf, size_t len)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = ldap_decode(asn1, samba_ldap_control_handlers(), ldap_msg);
|
status = ldap_decode(
|
||||||
|
asn1, &limits, samba_ldap_control_handlers(), ldap_msg);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
talloc_free(mem_ctx);
|
talloc_free(mem_ctx);
|
||||||
|
@ -3060,6 +3060,8 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
|
|||||||
lp_ctx, "ldap max anonymous request size", "256000");
|
lp_ctx, "ldap max anonymous request size", "256000");
|
||||||
lpcfg_do_global_parameter(
|
lpcfg_do_global_parameter(
|
||||||
lp_ctx, "ldap max authenticated request size", "16777216");
|
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++) {
|
for (i = 0; parm_table[i].label; i++) {
|
||||||
if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
|
if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
|
||||||
|
@ -111,6 +111,11 @@ struct cldap_search_state {
|
|||||||
struct tevent_req *req;
|
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)
|
static int cldap_socket_destructor(struct cldap_socket *c)
|
||||||
{
|
{
|
||||||
while (c->searches.list) {
|
while (c->searches.list) {
|
||||||
@ -228,6 +233,9 @@ static bool cldap_socket_recv_dgram(struct cldap_socket *c,
|
|||||||
void *p;
|
void *p;
|
||||||
struct cldap_search_state *search;
|
struct cldap_search_state *search;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
.max_search_size = MAX_SEARCH_REQUEST
|
||||||
|
};
|
||||||
|
|
||||||
if (in->recv_errno != 0) {
|
if (in->recv_errno != 0) {
|
||||||
goto error;
|
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 */
|
/* 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)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
goto nterror;
|
goto nterror;
|
||||||
}
|
}
|
||||||
@ -774,6 +782,9 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
|
|||||||
struct cldap_search_state);
|
struct cldap_search_state);
|
||||||
struct ldap_message *ldap_msg;
|
struct ldap_message *ldap_msg;
|
||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
.max_search_size = MAX_SEARCH_REQUEST
|
||||||
|
};
|
||||||
|
|
||||||
if (tevent_req_is_nterror(req, &status)) {
|
if (tevent_req_is_nterror(req, &status)) {
|
||||||
goto failed;
|
goto failed;
|
||||||
@ -784,7 +795,7 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
|
|||||||
goto nomem;
|
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)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
@ -800,7 +811,8 @@ NTSTATUS cldap_search_recv(struct tevent_req *req,
|
|||||||
*io->out.response = ldap_msg->r.SearchResultEntry;
|
*io->out.response = ldap_msg->r.SearchResultEntry;
|
||||||
|
|
||||||
/* decode the 2nd part */
|
/* 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)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
goto failed;
|
goto failed;
|
||||||
}
|
}
|
||||||
|
@ -1162,6 +1162,7 @@ static bool ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data,
|
|||||||
/* This routine returns LDAP status codes */
|
/* This routine returns LDAP status codes */
|
||||||
|
|
||||||
_PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data,
|
_PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data,
|
||||||
|
const struct ldap_request_limits *limits,
|
||||||
const struct ldap_control_handler *control_handlers,
|
const struct ldap_control_handler *control_handlers,
|
||||||
struct ldap_message *msg)
|
struct ldap_message *msg)
|
||||||
{
|
{
|
||||||
|
@ -213,10 +213,15 @@ struct ldap_control_handler {
|
|||||||
bool (*encode)(void *mem_ctx, void *in, DATA_BLOB *out);
|
bool (*encode)(void *mem_ctx, void *in, DATA_BLOB *out);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ldap_request_limits {
|
||||||
|
unsigned max_search_size;
|
||||||
|
};
|
||||||
|
|
||||||
struct asn1_data;
|
struct asn1_data;
|
||||||
|
|
||||||
struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx);
|
struct ldap_message *new_ldap_message(TALLOC_CTX *mem_ctx);
|
||||||
NTSTATUS ldap_decode(struct asn1_data *data,
|
NTSTATUS ldap_decode(struct asn1_data *data,
|
||||||
|
const struct ldap_request_limits *limits,
|
||||||
const struct ldap_control_handler *control_handlers,
|
const struct ldap_control_handler *control_handlers,
|
||||||
struct ldap_message *msg);
|
struct ldap_message *msg);
|
||||||
bool ldap_encode(struct ldap_message *msg,
|
bool ldap_encode(struct ldap_message *msg,
|
||||||
|
@ -117,6 +117,9 @@ static void test_empty_input(void **state)
|
|||||||
NTSTATUS status;
|
NTSTATUS status;
|
||||||
uint8_t buf[0];
|
uint8_t buf[0];
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
.max_search_size = 256000,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
asn1 = asn1_init(test_ctx, ASN1_MAX_TREE_DEPTH);
|
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);
|
ldap_msg = talloc(test_ctx, struct ldap_message);
|
||||||
assert_non_null(ldap_msg);
|
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);
|
assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +153,9 @@ static void test_recursion_depth_large(void **state)
|
|||||||
uint8_t *buffer = NULL;
|
uint8_t *buffer = NULL;
|
||||||
const size_t BUFF_SIZE = 1048576;
|
const size_t BUFF_SIZE = 1048576;
|
||||||
size_t len;
|
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);
|
ldap_msg = talloc(test_ctx, struct ldap_message);
|
||||||
assert_non_null(ldap_msg);
|
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);
|
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;
|
uint8_t *buffer = NULL;
|
||||||
const size_t BUFF_SIZE = 1048576;
|
const size_t BUFF_SIZE = 1048576;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
.max_search_size = 256000,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE);
|
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);
|
ldap_msg = talloc(test_ctx, struct ldap_message);
|
||||||
assert_non_null(ldap_msg);
|
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));
|
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;
|
uint8_t *buffer = NULL;
|
||||||
const size_t BUFF_SIZE = 1048576;
|
const size_t BUFF_SIZE = 1048576;
|
||||||
size_t len;
|
size_t len;
|
||||||
|
struct ldap_request_limits limits = {
|
||||||
|
.max_search_size = 256000,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
buffer = talloc_zero_array(test_ctx, uint8_t, BUFF_SIZE);
|
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);
|
ldap_msg = talloc(test_ctx, struct ldap_message);
|
||||||
assert_non_null(ldap_msg);
|
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);
|
assert_ldap_status_equal(LDAP_PROTOCOL_ERROR, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_anonymous_request_size = 256000;
|
||||||
Globals.ldap_max_authenticated_request_size = 16777216;
|
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() */
|
/* Now put back the settings that were set with lp_set_cmdline() */
|
||||||
apply_lp_set_cmdline();
|
apply_lp_set_cmdline();
|
||||||
|
@ -538,6 +538,7 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
|
|||||||
struct asn1_data *asn1;
|
struct asn1_data *asn1;
|
||||||
DATA_BLOB blob;
|
DATA_BLOB blob;
|
||||||
int ret = LDAP_SUCCESS;
|
int ret = LDAP_SUCCESS;
|
||||||
|
struct ldap_request_limits limits = {0};
|
||||||
|
|
||||||
conn->sockets.read_req = NULL;
|
conn->sockets.read_req = NULL;
|
||||||
|
|
||||||
@ -593,7 +594,12 @@ static void ldapsrv_call_read_done(struct tevent_req *subreq)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = ldap_decode(asn1, samba_ldap_control_handlers(),
|
limits.max_search_size =
|
||||||
|
lpcfg_ldap_max_search_request_size(conn->lp_ctx);
|
||||||
|
status = ldap_decode(
|
||||||
|
asn1,
|
||||||
|
&limits,
|
||||||
|
samba_ldap_control_handlers(),
|
||||||
call->request);
|
call->request);
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
ldapsrv_terminate_connection(conn, nt_errstr(status));
|
ldapsrv_terminate_connection(conn, nt_errstr(status));
|
||||||
|
@ -277,6 +277,7 @@ static void ldap_connection_recv_done(struct tevent_req *subreq)
|
|||||||
struct ldap_message *msg;
|
struct ldap_message *msg;
|
||||||
struct asn1_data *asn1;
|
struct asn1_data *asn1;
|
||||||
DATA_BLOB blob;
|
DATA_BLOB blob;
|
||||||
|
struct ldap_request_limits limits = {0};
|
||||||
|
|
||||||
msg = talloc_zero(conn, struct ldap_message);
|
msg = talloc_zero(conn, struct ldap_message);
|
||||||
if (msg == NULL) {
|
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);
|
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);
|
asn1_free(asn1);
|
||||||
if (!NT_STATUS_IS_OK(status)) {
|
if (!NT_STATUS_IS_OK(status)) {
|
||||||
TALLOC_FREE(msg);
|
TALLOC_FREE(msg);
|
||||||
|
Loading…
Reference in New Issue
Block a user