diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm index dd1400633e..70e577243d 100755 --- a/selftest/target/Samba4.pm +++ b/selftest/target/Samba4.pm @@ -1173,6 +1173,26 @@ userPrincipalName: jane.doe\@$ctx->{realm} return undef; } + # Change the MaxPageSize attribute value + my $policy_dn = "CN=Default Query Policy,CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$base_dn"; + + open($ldif, "|$ldbmodify -H $ctx->{privatedir}/sam.ldb") + or die "Failed to run $ldbmodify: $!"; + print $ldif "dn: $policy_dn +changetype: modify +delete: lDAPAdminLimits +lDAPAdminLimits: MaxPageSize=1000 +- +add: lDAPAdminLimits +lDAPAdminLimits: MaxPageSize=5000 +"; + close($ldif); + unless ($? == 0) { + warn("$ldbmodify failed: $?"); + return undef; + } + + return $ret; } diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c index 4e94417c21..715fd8ab31 100644 --- a/source4/ldap_server/ldap_backend.c +++ b/source4/ldap_server/ldap_backend.c @@ -325,6 +325,7 @@ static NTSTATUS ldapsrv_queue_reply_forced(struct ldapsrv_call *call, */ NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *reply) { + enum ldap_request_tag reply_type = reply->msg->type; NTSTATUS status = ldapsrv_encode(call, reply); if (!NT_STATUS_IS_OK(status)) { @@ -340,8 +341,20 @@ NTSTATUS ldapsrv_queue_reply(struct ldapsrv_call *call, struct ldapsrv_reply *re return NT_STATUS_FILE_TOO_LARGE; } + if (call->conn->limits.max_page_size > 0 && + call->reply_cnt > call->conn->limits.max_page_size) { + DBG_INFO("LDAP Server Page Size Limit Exceeded (%zu > %d).\n", + call->reply_cnt, call->conn->limits.max_page_size); + TALLOC_FREE(reply->blob.data); + return NT_STATUS_LDAP(LDAP_SIZE_LIMIT_EXCEEDED); + } + call->reply_size += reply->blob.length; + /* do not count "result references" and "referrals" */ + if (reply_type != LDAP_TAG_SearchResultReference) + call->reply_cnt++; + DLIST_ADD_END(call->replies, reply); return status; @@ -606,6 +619,7 @@ static int ldap_server_search_callback(struct ldb_request *req, struct ldb_reply struct ldapsrv_reply *ent_r = NULL; struct ldap_SearchResEntry *ent; int ret; + const char *error_str = NULL; NTSTATUS status; if (!ares) { @@ -665,9 +679,13 @@ queue_reply: "LDAP search response size " "limited to %zu bytes\n", LDAP_SERVER_MAX_REPLY_SIZE); - } else if (!NT_STATUS_IS_OK(status)) { + } else if (!NT_STATUS_IS_OK(status) && !NT_STATUS_IS_LDAP(status)) { ret = ldb_request_done(req, ldb_operr(ldb)); + } else if (NT_STATUS_IS_LDAP(status)) { + ret = ldb_request_done(req, NT_STATUS_LDAP_CODE(status)); + error_str = ldb_strerror(ret); + ldb_asprintf_errstring(ldb, "%s", error_str); } else { ret = LDB_SUCCESS; } @@ -701,8 +719,12 @@ queue_reply: ent_ref->referral = ares->referral; status = ldapsrv_queue_reply(call, ent_r); - if (!NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_IS_OK(status) && !NT_STATUS_IS_LDAP(status)) { ret = LDB_ERR_OPERATIONS_ERROR; + } else if (NT_STATUS_IS_LDAP(status)) { + ret = ldb_request_done(req, NT_STATUS_LDAP_CODE(status)); + error_str = ldb_strerror(ret); + ldb_asprintf_errstring(ldb, "%s", error_str); } else { ret = LDB_SUCCESS; } @@ -1635,6 +1657,9 @@ NTSTATUS ldapsrv_do_call(struct ldapsrv_call *call) if (NT_STATUS_IS_OK(status)) { ldapsrv_notification_retry_setup(call->conn->service, true); + } else if (NT_STATUS_IS_LDAP(status)) { + const char *error_str = ldb_strerror(NT_STATUS_LDAP_CODE(status)); + ldb_asprintf_errstring(call->conn->ldb, "%s", error_str); } return status; diff --git a/source4/ldap_server/ldap_server.h b/source4/ldap_server/ldap_server.h index a56aa8f8c4..f7cfd526c6 100644 --- a/source4/ldap_server/ldap_server.h +++ b/source4/ldap_server/ldap_server.h @@ -83,6 +83,7 @@ struct ldapsrv_call { struct iovec *out_iov; size_t iov_count; size_t reply_size; + size_t reply_cnt; struct tevent_req *(*wait_send)(TALLOC_CTX *mem_ctx, struct tevent_context *ev,