diff --git a/selftest/knownfail.d/large-dc b/selftest/knownfail.d/large-dc deleted file mode 100644 index 9a9e53a193f..00000000000 --- a/selftest/knownfail.d/large-dc +++ /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 diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c index 4be07d7c7fb..7e66672834e 100644 --- a/source4/ldap_server/ldap_backend.c +++ b/source4/ldap_server/ldap_backend.c @@ -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) diff --git a/source4/ldap_server/ldap_server.h b/source4/ldap_server/ldap_server.h index 75c8adcb5ef..48634e7610c 100644 --- a/source4/ldap_server/ldap_server.h +++ b/source4/ldap_server/ldap_server.h @@ -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;