summaryrefslogtreecommitdiffstats
path: root/source4/torture/ldap
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 17:47:29 +0000
commit4f5791ebd03eaec1c7da0865a383175b05102712 (patch)
tree8ce7b00f7a76baa386372422adebbe64510812d4 /source4/torture/ldap
parentInitial commit. (diff)
downloadsamba-upstream.tar.xz
samba-upstream.zip
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/torture/ldap')
-rw-r--r--source4/torture/ldap/basic.c1004
-rw-r--r--source4/torture/ldap/cldap.c179
-rw-r--r--source4/torture/ldap/cldapbench.c233
-rw-r--r--source4/torture/ldap/common.c135
-rw-r--r--source4/torture/ldap/ldap_sort.c158
-rw-r--r--source4/torture/ldap/nested_search.c206
-rw-r--r--source4/torture/ldap/netlogon.c664
-rw-r--r--source4/torture/ldap/schema.c408
-rw-r--r--source4/torture/ldap/session_expiry.c122
-rw-r--r--source4/torture/ldap/uptodatevector.c173
10 files changed, 3282 insertions, 0 deletions
diff --git a/source4/torture/ldap/basic.c b/source4/torture/ldap/basic.c
new file mode 100644
index 0000000..ff9207e
--- /dev/null
+++ b/source4/torture/ldap/basic.c
@@ -0,0 +1,1004 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2004
+ Copyright (C) Matthias Dieter Wallnöfer 2009-2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "ldb_wrap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/cmdline.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#undef strcasecmp
+
+static bool test_bind_sasl(struct torture_context *tctx,
+ struct ldap_connection *conn, struct cli_credentials *creds)
+{
+ NTSTATUS status;
+ bool ret = true;
+
+ printf("Testing sasl bind as user\n");
+
+ status = torture_ldap_bind_sasl(conn, creds, tctx->lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ ret = false;
+ }
+
+ return ret;
+}
+
+static bool test_multibind(struct ldap_connection *conn, const char *userdn, const char *password)
+{
+ NTSTATUS status, expected;
+ bool ok;
+
+ printf("Testing multiple binds on a single connection as anonymous and user\n");
+
+ status = torture_ldap_bind(conn, NULL, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("1st bind as anonymous failed with %s\n",
+ nt_errstr(status));
+ return false;
+ }
+
+ expected = NT_STATUS_LDAP(LDAP_STRONG_AUTH_REQUIRED);
+ status = torture_ldap_bind(conn, userdn, password);
+
+ ok = NT_STATUS_EQUAL(status, expected);
+ if (!ok) {
+ printf("2nd bind as authenticated user should have "
+ "failed with: %s, got %s\n",
+ nt_errstr(expected),
+ nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_search_rootDSE(struct ldap_connection *conn, const char **basedn,
+ const char ***partitions)
+{
+ bool ret = true;
+ struct ldap_message *msg, *result;
+ struct ldap_request *req;
+ int i;
+ struct ldap_SearchResEntry *r;
+ NTSTATUS status;
+
+ printf("Testing RootDSE Search\n");
+
+ *basedn = NULL;
+
+ if (partitions != NULL) {
+ *partitions = const_str_list(str_list_make_empty(conn));
+ }
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = "";
+ msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = false;
+ msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)");
+ msg->r.SearchRequest.num_attributes = 0;
+ msg->r.SearchRequest.attributes = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ printf("Could not setup ldap search\n");
+ return false;
+ }
+
+ status = ldap_result_one(req, &result, LDAP_TAG_SearchResultEntry);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("search failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("received %d replies\n", req->num_replies);
+
+ r = &result->r.SearchResultEntry;
+
+ DEBUG(1,("\tdn: %s\n", r->dn));
+ for (i=0; i<r->num_attributes; i++) {
+ unsigned int j;
+ for (j=0; j<r->attributes[i].num_values; j++) {
+ DEBUG(1,("\t%s: %d %.*s\n", r->attributes[i].name,
+ (int)r->attributes[i].values[j].length,
+ (int)r->attributes[i].values[j].length,
+ (char *)r->attributes[i].values[j].data));
+ if (!(*basedn) &&
+ strcasecmp("defaultNamingContext",r->attributes[i].name)==0) {
+ *basedn = talloc_asprintf(conn, "%.*s",
+ (int)r->attributes[i].values[j].length,
+ (char *)r->attributes[i].values[j].data);
+ }
+ if ((partitions != NULL) &&
+ (strcasecmp("namingContexts", r->attributes[i].name) == 0)) {
+ char *entry = talloc_asprintf(conn, "%.*s",
+ (int)r->attributes[i].values[j].length,
+ (char *)r->attributes[i].values[j].data);
+ *partitions = str_list_add(*partitions, entry);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static bool test_search_rootDSE_empty_substring(struct ldap_connection *conn)
+{
+ bool ret = true;
+ struct ldap_message *msg, *result;
+ struct ldap_request *req;
+ NTSTATUS status;
+
+ printf("Testing RootDSE Search with objectclass= substring filter\n");
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = "";
+ msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = false;
+ msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)");
+ msg->r.SearchRequest.tree->operation = LDB_OP_SUBSTRING;
+ msg->r.SearchRequest.tree->u.substring.attr = "objectclass";
+ msg->r.SearchRequest.tree->u.substring.start_with_wildcard = 1;
+ msg->r.SearchRequest.tree->u.substring.end_with_wildcard = 1;
+ msg->r.SearchRequest.tree->u.substring.chunks = NULL;
+ msg->r.SearchRequest.num_attributes = 0;
+ msg->r.SearchRequest.attributes = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ printf("Could not setup ldap search\n");
+ return false;
+ }
+
+ status = ldap_result_one(req, &result, LDAP_TAG_SearchResultEntry);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("looking for search result reply failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("received %d replies\n", req->num_replies);
+
+ return ret;
+}
+
+static bool test_search_auth_empty_substring(struct ldap_connection *conn, const char *basedn)
+{
+ bool ret = true;
+ struct ldap_message *msg, *result;
+ struct ldap_request *req;
+ NTSTATUS status;
+ struct ldap_Result *r;
+
+ printf("Testing authenticated base Search with objectclass= substring filter\n");
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = basedn;
+ msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = false;
+ msg->r.SearchRequest.tree = ldb_parse_tree(msg, "(objectclass=*)");
+ msg->r.SearchRequest.tree->operation = LDB_OP_SUBSTRING;
+ msg->r.SearchRequest.tree->u.substring.attr = "objectclass";
+ msg->r.SearchRequest.tree->u.substring.start_with_wildcard = 1;
+ msg->r.SearchRequest.tree->u.substring.end_with_wildcard = 1;
+ msg->r.SearchRequest.tree->u.substring.chunks = NULL;
+ msg->r.SearchRequest.num_attributes = 0;
+ msg->r.SearchRequest.attributes = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ printf("Could not setup ldap search\n");
+ return false;
+ }
+
+ status = ldap_result_one(req, &result, LDAP_TAG_SearchResultDone);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("looking for search result done failed - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ printf("received %d replies\n", req->num_replies);
+
+ r = &result->r.SearchResultDone;
+
+ if (r->resultcode != LDAP_SUCCESS) {
+ printf("search result done gave error - %s\n", ldb_strerror(r->resultcode));
+ return false;
+ }
+
+ return ret;
+}
+
+static bool test_compare_sasl(struct ldap_connection *conn, const char *basedn)
+{
+ struct ldap_message *msg, *rep;
+ struct ldap_request *req;
+ const char *val;
+ NTSTATUS status;
+
+ printf("Testing SASL Compare: %s\n", basedn);
+
+ if (!basedn) {
+ return false;
+ }
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ msg->type = LDAP_TAG_CompareRequest;
+ msg->r.CompareRequest.dn = basedn;
+ msg->r.CompareRequest.attribute = talloc_strdup(msg, "objectClass");
+ val = "domain";
+ msg->r.CompareRequest.value = data_blob_talloc(msg, val, strlen(val));
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_CompareResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap compare request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ DEBUG(5,("Code: %d DN: [%s] ERROR:[%s] REFERRAL:[%s]\n",
+ rep->r.CompareResponse.resultcode,
+ rep->r.CompareResponse.dn,
+ rep->r.CompareResponse.errormessage,
+ rep->r.CompareResponse.referral));
+
+ return true;
+}
+
+/*
+ * This takes an AD error message and splits it into the WERROR code
+ * (WERR_DS_GENERIC if none found) and the reason (remaining string).
+ */
+static WERROR ad_error(const char *err_msg, char **reason)
+{
+ WERROR err = W_ERROR(strtol(err_msg, reason, 16));
+
+ if ((reason != NULL) && (*reason[0] != ':')) {
+ return WERR_DS_GENERIC_ERROR; /* not an AD std error message */
+ }
+
+ if (reason != NULL) {
+ *reason += 2; /* skip ": " */
+ }
+ return err;
+}
+
+/* This has to be done using the LDAP API since the LDB API does only transmit
+ * the error code and not the error message. */
+static bool test_error_codes(struct torture_context *tctx,
+ struct ldap_connection *conn, const char *basedn)
+{
+ struct ldap_message *msg, *rep;
+ struct ldap_request *req;
+ const char *err_code_str;
+ char *endptr;
+ WERROR err;
+ NTSTATUS status;
+
+ printf("Testing the most important error code -> error message conversions!\n");
+
+ if (!basedn) {
+ return false;
+ }
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ printf(" Try a wrong addition\n");
+
+ msg->type = LDAP_TAG_AddRequest;
+ msg->r.AddRequest.dn = basedn;
+ msg->r.AddRequest.num_attributes = 0;
+ msg->r.AddRequest.attributes = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_AddResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap add request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.AddResponse.resultcode == 0)
+ || (rep->r.AddResponse.errormessage == NULL)
+ || (strtol(rep->r.AddResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.AddResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_REFERRAL))
+ || (rep->r.AddResponse.resultcode != LDAP_REFERRAL)) {
+ return false;
+ }
+ if ((rep->r.AddResponse.referral == NULL)
+ || (strstr(rep->r.AddResponse.referral, basedn) == NULL)) {
+ return false;
+ }
+
+ printf(" Try another wrong addition\n");
+
+ msg->type = LDAP_TAG_AddRequest;
+ msg->r.AddRequest.dn = "";
+ msg->r.AddRequest.num_attributes = 0;
+ msg->r.AddRequest.attributes = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_AddResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap add request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.AddResponse.resultcode == 0)
+ || (rep->r.AddResponse.errormessage == NULL)
+ || (strtol(rep->r.AddResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.AddResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_ROOT_MUST_BE_NC) &&
+ !W_ERROR_EQUAL(err, WERR_DS_NAMING_VIOLATION))
+ || (rep->r.AddResponse.resultcode != LDAP_NAMING_VIOLATION)) {
+ return false;
+ }
+
+ printf(" Try a wrong modification\n");
+
+ msg->type = LDAP_TAG_ModifyRequest;
+ msg->r.ModifyRequest.dn = basedn;
+ msg->r.ModifyRequest.num_mods = 0;
+ msg->r.ModifyRequest.mods = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap modifification request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyResponse.resultcode == 0)
+ || (rep->r.ModifyResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) &&
+ !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM))
+ || (rep->r.ModifyResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) {
+ return false;
+ }
+
+ printf(" Try another wrong modification\n");
+
+ msg->type = LDAP_TAG_ModifyRequest;
+ msg->r.ModifyRequest.dn = "";
+ msg->r.ModifyRequest.num_mods = 0;
+ msg->r.ModifyRequest.mods = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap modifification request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyResponse.resultcode == 0)
+ || (rep->r.ModifyResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) &&
+ !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM))
+ || (rep->r.ModifyResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) {
+ return false;
+ }
+
+ printf(" Try a wrong removal\n");
+
+ msg->type = LDAP_TAG_DelRequest;
+ msg->r.DelRequest.dn = basedn;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_DelResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap removal request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.DelResponse.resultcode == 0)
+ || (rep->r.DelResponse.errormessage == NULL)
+ || (strtol(rep->r.DelResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.DelResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_CANT_DELETE) &&
+ !W_ERROR_EQUAL(err, WERR_DS_UNWILLING_TO_PERFORM))
+ || (rep->r.DelResponse.resultcode != LDAP_UNWILLING_TO_PERFORM)) {
+ return false;
+ }
+
+ printf(" Try another wrong removal\n");
+
+ msg->type = LDAP_TAG_DelRequest;
+ msg->r.DelRequest.dn = "";
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_DelResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap removal request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.DelResponse.resultcode == 0)
+ || (rep->r.DelResponse.errormessage == NULL)
+ || (strtol(rep->r.DelResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.DelResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_OBJ_NOT_FOUND) &&
+ !W_ERROR_EQUAL(err, WERR_DS_NO_SUCH_OBJECT))
+ || (rep->r.DelResponse.resultcode != LDAP_NO_SUCH_OBJECT)) {
+ return false;
+ }
+
+ printf(" Try a wrong rename\n");
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = basedn;
+ msg->r.ModifyDNRequest.newrdn = "dc=test";
+ msg->r.ModifyDNRequest.deleteolddn = true;
+ msg->r.ModifyDNRequest.newsuperior = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap rename request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyDNResponse.resultcode == 0)
+ || (rep->r.ModifyDNResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_NO_PARENT_OBJECT) &&
+ !W_ERROR_EQUAL(err, WERR_DS_GENERIC_ERROR))
+ || (rep->r.ModifyDNResponse.resultcode != LDAP_OTHER)) {
+ return false;
+ }
+
+ printf(" Try another wrong rename\n");
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = basedn;
+ msg->r.ModifyDNRequest.newrdn = basedn;
+ msg->r.ModifyDNRequest.deleteolddn = true;
+ msg->r.ModifyDNRequest.newsuperior = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap rename request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyDNResponse.resultcode == 0)
+ || (rep->r.ModifyDNResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) &&
+ !W_ERROR_EQUAL(err, WERR_DS_NAMING_VIOLATION))
+ || (rep->r.ModifyDNResponse.resultcode != LDAP_NAMING_VIOLATION)) {
+ return false;
+ }
+
+ printf(" Try another wrong rename\n");
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = basedn;
+ msg->r.ModifyDNRequest.newrdn = "";
+ msg->r.ModifyDNRequest.deleteolddn = true;
+ msg->r.ModifyDNRequest.newsuperior = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap rename request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyDNResponse.resultcode == 0)
+ || (rep->r.ModifyDNResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_INVALID_PARAMETER) &&
+ !W_ERROR_EQUAL(err, WERR_DS_PROTOCOL_ERROR))
+ || (rep->r.ModifyDNResponse.resultcode != LDAP_PROTOCOL_ERROR)) {
+ return false;
+ }
+
+ printf(" Try another wrong rename\n");
+
+ msg->type = LDAP_TAG_ModifyDNRequest;
+ msg->r.ModifyDNRequest.dn = "";
+ msg->r.ModifyDNRequest.newrdn = "cn=temp";
+ msg->r.ModifyDNRequest.deleteolddn = true;
+ msg->r.ModifyDNRequest.newsuperior = NULL;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_result_one(req, &rep, LDAP_TAG_ModifyDNResponse);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap rename request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ if ((rep->r.ModifyDNResponse.resultcode == 0)
+ || (rep->r.ModifyDNResponse.errormessage == NULL)
+ || (strtol(rep->r.ModifyDNResponse.errormessage, &endptr,16) <= 0)
+ || (*endptr != ':')) {
+ printf("Invalid error message!\n");
+ return false;
+ }
+
+ err = ad_error(rep->r.ModifyDNResponse.errormessage, &endptr);
+ err_code_str = win_errstr(err);
+ printf(" - Errorcode: %s; Reason: %s\n", err_code_str, endptr);
+ if ((!W_ERROR_EQUAL(err, WERR_DS_OBJ_NOT_FOUND) &&
+ !W_ERROR_EQUAL(err, WERR_DS_NO_SUCH_OBJECT))
+ || (rep->r.ModifyDNResponse.resultcode != LDAP_NO_SUCH_OBJECT)) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool test_referrals(struct torture_context *tctx, TALLOC_CTX *mem_ctx,
+ const char *url, const char *basedn, const char **partitions)
+{
+ struct ldb_context *ldb;
+ struct ldb_result *res;
+ const char * const *attrs = { NULL };
+ struct ldb_dn *dn1, *dn2;
+ int ret;
+ int i, j, k;
+ char *tempstr;
+ bool found, l_found;
+
+ printf("Testing referrals\n");
+
+ if (partitions[0] == NULL) {
+ printf("Partitions list empty!\n");
+ return false;
+ }
+
+ if (strcmp(partitions[0], basedn) != 0) {
+ printf("The first (root) partition DN should be the base DN!\n");
+ return false;
+ }
+
+ ldb = ldb_wrap_connect(mem_ctx, tctx->ev, tctx->lp_ctx, url,
+ NULL, samba_cmdline_get_creds(), 0);
+
+ /* "partitions[i]" are the partitions for which we search the parents */
+ for (i = 1; partitions[i] != NULL; i++) {
+ dn1 = ldb_dn_new(mem_ctx, ldb, partitions[i]);
+ if (dn1 == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ /* search using base scope */
+ /* "partitions[j]" are the parent candidates */
+ for (j = str_list_length(partitions) - 1; j >= 0; --j) {
+ dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
+ if (dn2 == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res, dn2,
+ LDB_SCOPE_BASE, attrs,
+ "(foo=bar)");
+ if (ret != LDB_SUCCESS) {
+ printf("%s", ldb_errstring(ldb));
+ talloc_free(ldb);
+ return false;
+ }
+
+ if (res->refs != NULL) {
+ printf("There shouldn't be generated any referrals in the base scope!\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ talloc_free(res);
+ talloc_free(dn2);
+ }
+
+ /* search using onelevel scope */
+ found = false;
+ /* "partitions[j]" are the parent candidates */
+ for (j = str_list_length(partitions) - 1; j >= 0; --j) {
+ dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
+ if (dn2 == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res, dn2,
+ LDB_SCOPE_ONELEVEL, attrs,
+ "(foo=bar)");
+ if (ret != LDB_SUCCESS) {
+ printf("%s", ldb_errstring(ldb));
+ talloc_free(ldb);
+ return false;
+ }
+
+ tempstr = talloc_asprintf(mem_ctx, "/%s??base",
+ partitions[i]);
+ if (tempstr == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ /* Try to find or find not a matching referral */
+ l_found = false;
+ for (k = 0; (!l_found) && (res->refs != NULL)
+ && (res->refs[k] != NULL); k++) {
+ if (strstr(res->refs[k], tempstr) != NULL) {
+ l_found = true;
+ }
+ }
+
+ talloc_free(tempstr);
+
+ if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0)
+ && (ldb_dn_compare(dn2, dn1) != 0)) {
+ /* This is a referral candidate */
+ if (!l_found) {
+ printf("A required referral hasn't been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]);
+ talloc_free(ldb);
+ return false;
+ }
+ found = true;
+ } else {
+ /* This isn't a referral candidate */
+ if (l_found) {
+ printf("A unrequired referral has been found on onelevel scope (%s -> %s)!\n", partitions[j], partitions[i]);
+ talloc_free(ldb);
+ return false;
+ }
+ }
+
+ talloc_free(res);
+ talloc_free(dn2);
+ }
+
+ /* search using subtree scope */
+ found = false;
+ /* "partitions[j]" are the parent candidates */
+ for (j = str_list_length(partitions) - 1; j >= 0; --j) {
+ dn2 = ldb_dn_new(mem_ctx, ldb, partitions[j]);
+ if (dn2 == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ ret = ldb_search(ldb, mem_ctx, &res, dn2,
+ LDB_SCOPE_SUBTREE, attrs,
+ "(foo=bar)");
+ if (ret != LDB_SUCCESS) {
+ printf("%s", ldb_errstring(ldb));
+ talloc_free(ldb);
+ return false;
+ }
+
+ tempstr = talloc_asprintf(mem_ctx, "/%s",
+ partitions[i]);
+ if (tempstr == NULL) {
+ printf("Out of memory\n");
+ talloc_free(ldb);
+ return false;
+ }
+
+ /* Try to find or find not a matching referral */
+ l_found = false;
+ for (k = 0; (!l_found) && (res->refs != NULL)
+ && (res->refs[k] != NULL); k++) {
+ if (strstr(res->refs[k], tempstr) != NULL) {
+ l_found = true;
+ }
+ }
+
+ talloc_free(tempstr);
+
+ if ((!found) && (ldb_dn_compare_base(dn2, dn1) == 0)
+ && (ldb_dn_compare(dn2, dn1) != 0)) {
+ /* This is a referral candidate */
+ if (!l_found) {
+ printf("A required referral hasn't been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]);
+ talloc_free(ldb);
+ return false;
+ }
+ found = true;
+ } else {
+ /* This isn't a referral candidate */
+ if (l_found) {
+ printf("A unrequired referral has been found on subtree scope (%s -> %s)!\n", partitions[j], partitions[i]);
+ talloc_free(ldb);
+ return false;
+ }
+ }
+
+ talloc_free(res);
+ talloc_free(dn2);
+ }
+
+ talloc_free(dn1);
+ }
+
+ talloc_free(ldb);
+
+ return true;
+}
+
+static bool test_abandon_request(struct torture_context *tctx,
+ struct ldap_connection *conn, const char *basedn)
+{
+ struct ldap_message *msg;
+ struct ldap_request *req;
+ NTSTATUS status;
+
+ printf("Testing the AbandonRequest with an old message id!\n");
+
+ if (!basedn) {
+ return false;
+ }
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ return false;
+ }
+
+ printf(" Try a AbandonRequest for an old message id\n");
+
+ msg->type = LDAP_TAG_AbandonRequest;
+ msg->r.AbandonRequest.messageid = 1;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ return false;
+ }
+
+ status = ldap_request_wait(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap abandon request - %s\n", nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+
+bool torture_ldap_basic(struct torture_context *torture)
+{
+ NTSTATUS status;
+ struct ldap_connection *conn;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ const char *userdn = torture_setting_string(torture, "ldap_userdn", NULL);
+ const char *secret = torture_setting_string(torture, "ldap_secret", NULL);
+ const char *url;
+ const char *basedn;
+ const char **partitions;
+
+ mem_ctx = talloc_init("torture_ldap_basic");
+
+ url = talloc_asprintf(mem_ctx, "ldap://%s/", host);
+
+ status = torture_ldap_connection(torture, &conn, url);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (!test_search_rootDSE(conn, &basedn, &partitions)) {
+ ret = false;
+ }
+
+ if (!test_search_rootDSE_empty_substring(conn)) {
+ ret = false;
+ }
+
+ /* other bind tests here */
+
+ if (!test_multibind(conn, userdn, secret)) {
+ ret = false;
+ }
+
+ if (!test_bind_sasl(torture, conn, samba_cmdline_get_creds())) {
+ ret = false;
+ }
+
+ if (!test_search_auth_empty_substring(conn, basedn)) {
+ ret = false;
+ }
+
+ if (!test_compare_sasl(conn, basedn)) {
+ ret = false;
+ }
+
+ /* error codes test here */
+
+ if (!test_error_codes(torture, conn, basedn)) {
+ ret = false;
+ }
+
+ /* referrals test here */
+
+ if (!test_referrals(torture, mem_ctx, url, basedn, partitions)) {
+ ret = false;
+ }
+
+ if (!test_abandon_request(torture, conn, basedn)) {
+ ret = false;
+ }
+
+ /* if there are no more tests we are closing */
+ torture_ldap_close(conn);
+ talloc_free(mem_ctx);
+
+ torture_assert(torture, ret, "torture_ldap_basic failed");
+
+ return ret;
+}
+
diff --git a/source4/torture/ldap/cldap.c b/source4/torture/ldap/cldap.c
new file mode 100644
index 0000000..a021f4c
--- /dev/null
+++ b/source4/torture/ldap/cldap.c
@@ -0,0 +1,179 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ test CLDAP operations
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Matthias Dieter Wallnöfer 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status")
+
+#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
+
+#define CHECK_STRING(v, correct) torture_assert_str_equal(tctx, v, correct, "incorrect value");
+
+/*
+ convert a ldap result message to a ldb message. This allows us to
+ use the convenient ldif dump routines in ldb to print out cldap
+ search results
+*/
+static struct ldb_message *ldap_msg_to_ldb(TALLOC_CTX *mem_ctx, struct ldb_context *ldb, struct ldap_SearchResEntry *res)
+{
+ struct ldb_message *msg;
+
+ msg = ldb_msg_new(mem_ctx);
+ msg->dn = ldb_dn_new(msg, ldb, res->dn);
+ msg->num_elements = res->num_attributes;
+ msg->elements = talloc_steal(msg, res->attributes);
+ return msg;
+}
+
+/*
+ dump a set of cldap results
+*/
+static void cldap_dump_results(struct cldap_search *search)
+{
+ struct ldb_ldif ldif;
+ struct ldb_context *ldb;
+
+ if (!search || !(search->out.response)) {
+ return;
+ }
+
+ /* we need a ldb context to use ldb_ldif_write_file() */
+ ldb = ldb_init(NULL, NULL);
+
+ ZERO_STRUCT(ldif);
+ ldif.msg = ldap_msg_to_ldb(ldb, ldb, search->out.response);
+
+ ldb_ldif_write_file(ldb, stdout, &ldif);
+
+ talloc_free(ldb);
+}
+
+/*
+ test generic cldap operations
+*/
+static bool test_cldap_generic(struct torture_context *tctx, const char *dest)
+{
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+ struct cldap_search search;
+ const char *attrs1[] = { "currentTime", "highestCommittedUSN", NULL };
+ const char *attrs2[] = { "currentTime", "highestCommittedUSN", "netlogon", NULL };
+ const char *attrs3[] = { "netlogon", NULL };
+ struct tsocket_address *dest_addr;
+ const char *ip;
+ struct nbt_name nbt_name;
+ int ret;
+
+ make_nbt_name_server(&nbt_name, dest);
+
+ status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0, &nbt_name, tctx, &ip, tctx->ev);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx,"Failed to resolve %s: %s",
+ nbt_name.name, nt_errstr(status)));
+
+ ret = tsocket_address_inet_from_strings(tctx, "ip",
+ ip,
+ lpcfg_cldap_port(tctx->lp_ctx),
+ &dest_addr);
+ CHECK_VAL(ret, 0);
+
+ /* cldap_socket_init should now know about the dest. address */
+ status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.timeout = 10;
+ search.in.retries = 3;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("fetching whole rootDSE\n");
+ search.in.filter = "(objectclass=*)";
+ search.in.attributes = NULL;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (DEBUGLVL(3)) cldap_dump_results(&search);
+
+ printf("fetching currentTime and USN\n");
+ search.in.filter = "(objectclass=*)";
+ search.in.attributes = attrs1;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (DEBUGLVL(3)) cldap_dump_results(&search);
+
+ printf("Testing currentTime, USN and netlogon\n");
+ search.in.filter = "(objectclass=*)";
+ search.in.attributes = attrs2;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (DEBUGLVL(3)) cldap_dump_results(&search);
+
+ printf("Testing objectClass=* and netlogon\n");
+ search.in.filter = "(objectclass=*)";
+ search.in.attributes = attrs3;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (DEBUGLVL(3)) cldap_dump_results(&search);
+
+ printf("Testing a false expression\n");
+ search.in.filter = "(&(objectclass=*)(highestCommittedUSN=2))";
+ search.in.attributes = attrs1;
+
+ status = cldap_search(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ if (DEBUGLVL(3)) cldap_dump_results(&search);
+
+ return true;
+}
+
+bool torture_cldap(struct torture_context *torture)
+{
+ bool ret = true;
+ const char *host = torture_setting_string(torture, "host", NULL);
+
+ ret &= test_cldap_generic(torture, host);
+
+ return ret;
+}
+
diff --git a/source4/torture/ldap/cldapbench.c b/source4/torture/ldap/cldapbench.c
new file mode 100644
index 0000000..9b6f7f2
--- /dev/null
+++ b/source4/torture/ldap/cldapbench.c
@@ -0,0 +1,233 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CLDAP benchmark test
+
+ Copyright (C) Andrew Tridgell 2005
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/resolve/resolve.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+
+#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
+
+struct bench_state {
+ struct torture_context *tctx;
+ int pass_count, fail_count;
+};
+
+static void request_netlogon_handler(struct tevent_req *req)
+{
+ struct cldap_netlogon io;
+ struct bench_state *state = tevent_req_callback_data(req, struct bench_state);
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ io.in.version = 6;
+ status = cldap_netlogon_recv(req, tmp_ctx, &io);
+ talloc_free(req);
+ if (NT_STATUS_IS_OK(status)) {
+ state->pass_count++;
+ } else {
+ state->fail_count++;
+ }
+ talloc_free(tmp_ctx);
+}
+
+/*
+ benchmark cldap netlogon calls
+*/
+static bool bench_cldap_netlogon(struct torture_context *tctx, const char *address)
+{
+ struct cldap_socket *cldap;
+ int num_sent=0;
+ struct timeval tv = timeval_current();
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ struct cldap_netlogon search;
+ struct bench_state *state;
+ NTSTATUS status;
+ struct tsocket_address *dest_addr;
+ int ret;
+
+ ret = tsocket_address_inet_from_strings(tctx, "ip",
+ address,
+ lpcfg_cldap_port(tctx->lp_ctx),
+ &dest_addr);
+ CHECK_VAL(ret, 0);
+
+ status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
+ torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init");
+
+ state = talloc_zero(tctx, struct bench_state);
+ state->tctx = tctx;
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.acct_control = -1;
+ search.in.version = 6;
+
+ printf("Running CLDAP/netlogon for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ while (num_sent - (state->pass_count+state->fail_count) < 10) {
+ struct tevent_req *req;
+ req = cldap_netlogon_send(state, tctx->ev,
+ cldap, &search);
+
+ tevent_req_set_callback(req, request_netlogon_handler, state);
+
+ num_sent++;
+ if (num_sent % 50 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("%.1f queries per second (%d failures) \r",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+ fflush(stdout);
+ }
+ }
+ }
+
+ tevent_loop_once(tctx->ev);
+ }
+
+ while (num_sent != (state->pass_count + state->fail_count)) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ printf("%.1f queries per second (%d failures) \n",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+
+ talloc_free(cldap);
+ return true;
+}
+
+static void request_rootdse_handler(struct tevent_req *req)
+{
+ struct cldap_search io;
+ struct bench_state *state = tevent_req_callback_data(req, struct bench_state);
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ status = cldap_search_recv(req, tmp_ctx, &io);
+ talloc_free(req);
+ if (NT_STATUS_IS_OK(status)) {
+ state->pass_count++;
+ } else {
+ state->fail_count++;
+ }
+ talloc_free(tmp_ctx);
+}
+
+/*
+ benchmark cldap netlogon calls
+*/
+static bool bench_cldap_rootdse(struct torture_context *tctx, const char *address)
+{
+ struct cldap_socket *cldap;
+ int num_sent=0;
+ struct timeval tv = timeval_current();
+ int timelimit = torture_setting_int(tctx, "timelimit", 10);
+ struct cldap_search search;
+ struct bench_state *state;
+ NTSTATUS status;
+ struct tsocket_address *dest_addr;
+ int ret;
+
+ ret = tsocket_address_inet_from_strings(tctx, "ip",
+ address,
+ lpcfg_cldap_port(tctx->lp_ctx),
+ &dest_addr);
+ CHECK_VAL(ret, 0);
+
+ /* cldap_socket_init should now know about the dest. address */
+ status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
+ torture_assert_ntstatus_ok(tctx, status, "cldap_socket_init");
+
+ state = talloc_zero(tctx, struct bench_state);
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.filter = "(objectClass=*)";
+ search.in.timeout = 2;
+ search.in.retries = 1;
+
+ printf("Running CLDAP/rootdse for %d seconds\n", timelimit);
+ while (timeval_elapsed(&tv) < timelimit) {
+ while (num_sent - (state->pass_count+state->fail_count) < 10) {
+ struct tevent_req *req;
+ req = cldap_search_send(state, tctx->ev, cldap, &search);
+
+ tevent_req_set_callback(req, request_rootdse_handler, state);
+
+ num_sent++;
+ if (num_sent % 50 == 0) {
+ if (torture_setting_bool(tctx, "progress", true)) {
+ printf("%.1f queries per second (%d failures) \r",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+ fflush(stdout);
+ }
+ }
+ }
+
+ tevent_loop_once(tctx->ev);
+ }
+
+ while (num_sent != (state->pass_count + state->fail_count)) {
+ tevent_loop_once(tctx->ev);
+ }
+
+ printf("%.1f queries per second (%d failures) \n",
+ state->pass_count / timeval_elapsed(&tv),
+ state->fail_count);
+
+ talloc_free(cldap);
+ return true;
+}
+
+/*
+ benchmark how fast a CLDAP server can respond to a series of parallel
+ requests
+*/
+bool torture_bench_cldap(struct torture_context *torture)
+{
+ const char *address;
+ struct nbt_name name;
+ NTSTATUS status;
+ bool ret = true;
+
+ make_nbt_name_server(&name, torture_setting_string(torture, "host", NULL));
+
+ /* do an initial name resolution to find its IP */
+ status = resolve_name_ex(lpcfg_resolve_context(torture->lp_ctx),
+ 0, 0, &name, torture, &address, torture->ev);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to resolve %s - %s\n",
+ name.name, nt_errstr(status));
+ return false;
+ }
+
+ ret &= bench_cldap_netlogon(torture, address);
+ ret &= bench_cldap_rootdse(torture, address);
+
+ return ret;
+}
diff --git a/source4/torture/ldap/common.c b/source4/torture/ldap/common.c
new file mode 100644
index 0000000..c33fda7
--- /dev/null
+++ b/source4/torture/ldap/common.c
@@ -0,0 +1,135 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP protocol helper functions for SAMBA
+
+ Copyright (C) Stefan Metzmacher 2004
+ Copyright (C) Simo Sorce 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/smbtorture.h"
+#include "torture/ldap/proto.h"
+
+NTSTATUS torture_ldap_bind(struct ldap_connection *conn, const char *userdn, const char *password)
+{
+ NTSTATUS status;
+
+ status = ldap_bind_simple(conn, userdn, password);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to bind with provided credentials - %s\n",
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+NTSTATUS torture_ldap_bind_sasl(struct ldap_connection *conn,
+ struct cli_credentials *creds,
+ struct loadparm_context *lp_ctx)
+{
+ NTSTATUS status;
+
+ status = ldap_bind_sasl(conn, creds, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed sasl bind with provided credentials - %s\n",
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/* open a ldap connection to a server */
+NTSTATUS torture_ldap_connection(struct torture_context *tctx,
+ struct ldap_connection **conn,
+ const char *url)
+{
+ NTSTATUS status;
+
+ if (!url) {
+ printf("You must specify a url string\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ *conn = ldap4_new_connection(tctx, tctx->lp_ctx, tctx->ev);
+
+ status = ldap_connect(*conn, url);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect to ldap server '%s' - %s\n",
+ url, nt_errstr(status));
+ }
+
+ return status;
+}
+
+/* close an ldap connection to a server */
+NTSTATUS torture_ldap_close(struct ldap_connection *conn)
+{
+ struct ldap_message *msg;
+ struct ldap_request *req;
+ NTSTATUS status;
+
+ printf("Closing the connection...\n");
+
+ msg = new_ldap_message(conn);
+ if (!msg) {
+ talloc_free(conn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ printf(" Try a UnbindRequest\n");
+
+ msg->type = LDAP_TAG_UnbindRequest;
+
+ req = ldap_request_send(conn, msg);
+ if (!req) {
+ talloc_free(conn);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ldap_request_wait(req);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("error in ldap unbind request - %s\n", nt_errstr(status));
+ talloc_free(conn);
+ return status;
+ }
+
+ talloc_free(conn);
+ return NT_STATUS_OK;
+}
+
+NTSTATUS torture_ldap_init(TALLOC_CTX *ctx)
+{
+ struct torture_suite *suite = torture_suite_create(ctx, "ldap");
+ torture_suite_add_simple_test(suite, "bench-cldap", torture_bench_cldap);
+ torture_suite_add_simple_test(suite, "basic", torture_ldap_basic);
+ torture_suite_add_simple_test(suite, "sort", torture_ldap_sort);
+ torture_suite_add_simple_test(suite, "cldap", torture_cldap);
+ torture_suite_add_simple_test(suite, "netlogon-udp", torture_netlogon_udp);
+ torture_suite_add_simple_test(suite, "netlogon-tcp", torture_netlogon_tcp);
+ torture_suite_add_simple_test(suite, "schema", torture_ldap_schema);
+ torture_suite_add_simple_test(suite, "uptodatevector", torture_ldap_uptodatevector);
+ torture_suite_add_simple_test(suite, "nested-search", test_ldap_nested_search);
+ torture_suite_add_simple_test(
+ suite, "session-expiry", torture_ldap_session_expiry);
+
+ suite->description = talloc_strdup(suite, "LDAP and CLDAP tests");
+
+ torture_register_suite(ctx, suite);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/torture/ldap/ldap_sort.c b/source4/torture/ldap/ldap_sort.c
new file mode 100644
index 0000000..e90bd54
--- /dev/null
+++ b/source4/torture/ldap/ldap_sort.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Test LDB attribute functions
+
+ Copyright (C) Andrew Bartlet <abartlet@samba.org> 2008-2009
+ Copyright (C) Matthieu Patou <mat@matws.net> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "param/param.h"
+#include "lib/cmdline/cmdline.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/smbtorture.h"
+#include "torture/ldap/proto.h"
+#include <ctype.h>
+
+bool torture_ldap_sort(struct torture_context *torture)
+{
+ struct ldb_context *ldb;
+
+ bool ret = false;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ char *url;
+ codepoint_t j;
+ struct ldb_message_element *elem;
+ struct ldb_message *msg;
+
+ struct ldb_server_sort_control **control;
+ struct ldb_request *req;
+ struct ldb_result *ctx;
+ struct ldb_val *prev = NULL;
+ const char *prev_txt = NULL;
+ int prev_len = 0;
+ struct ldb_val *cur = NULL;
+ const char *cur_txt = NULL;
+ int cur_len = 0;
+ struct ldb_dn *dn;
+
+
+ /* TALLOC_CTX* ctx;*/
+
+ url = talloc_asprintf(torture, "ldap://%s/", host);
+
+ ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+ torture_assert(torture, ldb, "Failed to make LDB connection to target");
+
+ ctx = talloc_zero(ldb, struct ldb_result);
+
+ control = talloc_array(ctx, struct ldb_server_sort_control *, 2);
+ control[0] = talloc(control, struct ldb_server_sort_control);
+ control[0]->attributeName = talloc_strdup(control, "cn");
+ control[0]->orderingRule = NULL;
+ control[0]->reverse = 0;
+ control[1] = NULL;
+
+ dn = ldb_get_default_basedn(ldb);
+ ldb_dn_add_child_fmt(dn, "cn=users");
+ ret = ldb_build_search_req(&req, ldb, ctx,
+ dn,
+ LDB_SCOPE_SUBTREE,
+ "(objectClass=*)", NULL,
+ NULL,
+ ctx, ldb_search_default_callback, NULL);
+ torture_assert(torture, ret == LDB_SUCCESS, "Failed to build search request");
+
+ ret = ldb_request_add_control(req, LDB_CONTROL_SERVER_SORT_OID, true, control);
+ torture_assert(torture, ret == LDB_SUCCESS, "Failed to add control to search request");
+
+ ret = ldb_request(ldb, req);
+ torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb));
+
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ torture_assert(torture, ret == LDB_SUCCESS, ldb_errstring(ldb));
+
+ ret = true;
+ if (ctx->count > 1) {
+ unsigned int i;
+ for (i=0;i<ctx->count;i++) {
+ msg = ctx->msgs[i];
+ elem = ldb_msg_find_element(msg,"cn");
+ torture_assert_not_null(torture, elem, "msg lacks CN");
+ cur = elem->values;
+ torture_comment(torture, "cn: %s\n",cur->data);
+ if (prev != NULL)
+ {
+ /* Do only the ascii case right now ... */
+ cur_txt = (const char *) cur->data;
+ cur_len = cur->length;
+ prev_txt = (const char *) prev->data;
+ prev_len = prev->length;
+ /* Remove leading whitespace as the sort function do so ... */
+ while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;}
+ while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;}
+ while( *(cur_txt) && *(prev_txt) && cur_len && prev_len ) {
+ j = toupper_m(*(prev_txt))-toupper_m(*(cur_txt));
+ if ( j > 0 ) {
+ /* Just check that is not due to trailling white space in prev_txt
+ * That is to say *cur_txt = 0 and prev_txt = 20 */
+ /* Remove trailling whitespace */
+ while ( *prev_txt == ' ' ) { prev_txt++; prev_len--;}
+ while ( *cur_txt == ' ' ) { cur_txt++; cur_len--;}
+ /* Now that potential whitespace are removed if we are at the end
+ * of the cur_txt then it means that in fact strings were identical
+ */
+ torture_assert(torture, *cur_txt && *prev_txt, "Data wrongly sorted");
+ break;
+ }
+ else
+ {
+ if ( j == 0 )
+ {
+ if ( *(cur_txt) == ' ') {
+ while ( cur_txt[0] == cur_txt[1] ) { cur_txt++; cur_len--;}
+ while ( prev_txt[0] == prev_txt[1] ) { prev_txt++; prev_len--;}
+ }
+ cur_txt++;
+ prev_txt++;
+ prev_len--;
+ cur_len--;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ if ( ret != 1 ) {
+ break;
+ }
+ }
+ prev = cur;
+ }
+
+ }
+
+ return ret;
+}
diff --git a/source4/torture/ldap/nested_search.c b/source4/torture/ldap/nested_search.c
new file mode 100644
index 0000000..8f4d71b
--- /dev/null
+++ b/source4/torture/ldap/nested_search.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ BRIEF FILE DESCRIPTION
+
+ Copyright (C) Kamen Mazdrashki <kamen.mazdrashki@postpath.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "ldb.h"
+#include "ldb_wrap.h"
+#include "lib/cmdline/cmdline.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#define torture_assert_res(torture_ctx,expr,cmt,_res) \
+ if (!(expr)) { \
+ torture_result(torture_ctx, TORTURE_FAIL, __location__": Expression `%s' failed: %s", __STRING(expr), cmt); \
+ return _res; \
+ }
+
+
+struct nested_search_context {
+ struct torture_context *tctx;
+ struct ldb_dn *root_dn;
+ struct ldb_context *ldb;
+ struct ldb_result *ldb_res;
+};
+
+/*
+ * ldb_search handler - used to executed a nested
+ * ldap search request during LDB_REPLY_ENTRY handling
+ */
+static int nested_search_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ unsigned int i;
+ int res;
+ struct nested_search_context *sctx;
+ struct ldb_result *ldb_res;
+ struct ldb_message *ldb_msg;
+ static const char *attrs[] = {
+ "rootDomainNamingContext",
+ "configurationNamingContext",
+ "schemaNamingContext",
+ "defaultNamingContext",
+ NULL
+ };
+ enum ldb_reply_type type;
+
+ sctx = talloc_get_type(req->context, struct nested_search_context);
+
+ type = ares->type;
+ /* sanity check */
+ switch (type) {
+ case LDB_REPLY_ENTRY:
+ torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_ENTRY\n");
+ ldb_msg = ares->message;
+ torture_assert_res(sctx->tctx, ldb_msg, "ares->message is NULL!", LDB_ERR_OPERATIONS_ERROR);
+ torture_assert_res(sctx->tctx, ldb_msg->num_elements, "No elements returned!", LDB_ERR_OPERATIONS_ERROR);
+ torture_assert_res(sctx->tctx, ldb_msg->elements, "elements member is NULL!", LDB_ERR_OPERATIONS_ERROR);
+ break;
+ case LDB_REPLY_DONE:
+ torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_DONE\n");
+ break;
+ case LDB_REPLY_REFERRAL:
+ torture_comment(sctx->tctx, "nested_search_callback: LDB_REPLY_REFERRAL\n");
+ break;
+ }
+
+ /* switch context and let default handler do its job */
+ req->context = sctx->ldb_res;
+ res = ldb_search_default_callback(req, ares);
+ req->context = sctx;
+ if (res != LDB_SUCCESS) {
+ return res;
+ }
+
+ /* not a search reply, then get out */
+ if (type != LDB_REPLY_ENTRY) {
+ return res;
+ }
+
+
+ res = ldb_search(sctx->ldb, sctx, &ldb_res, sctx->root_dn, LDB_SCOPE_BASE, attrs, "(objectClass=*)");
+ if (res != LDB_SUCCESS) {
+ torture_warning(sctx->tctx,
+ "Search on RootDSE failed in search_entry handler: %s",
+ ldb_errstring(sctx->ldb));
+ return LDB_SUCCESS;
+ }
+
+ torture_assert_res(sctx->tctx, ldb_res->count == 1, "One message expected here", LDB_ERR_OPERATIONS_ERROR);
+
+ ldb_msg = ldb_res->msgs[0];
+ torture_assert_res(sctx->tctx, ldb_msg->num_elements == (ARRAY_SIZE(attrs)-1),
+ "Search returned different number of elts than requested", LDB_ERR_OPERATIONS_ERROR);
+ for (i = 0; i < ldb_msg->num_elements; i++) {
+ const char *msg;
+ struct ldb_message_element *elt1;
+ struct ldb_message_element *elt2;
+
+ elt2 = &ldb_msg->elements[i];
+ msg = talloc_asprintf(sctx, "Processing element: %s", elt2->name);
+ elt1 = ldb_msg_find_element(sctx->ldb_res->msgs[0], elt2->name);
+ torture_assert_res(sctx->tctx, elt1, msg, LDB_ERR_OPERATIONS_ERROR);
+
+ /* compare elements */
+ torture_assert_res(sctx->tctx, elt2->flags == elt1->flags, "", LDB_ERR_OPERATIONS_ERROR);
+ torture_assert_res(sctx->tctx, elt2->num_values == elt1->num_values, "", LDB_ERR_OPERATIONS_ERROR);
+ }
+ /* TODO: check returned result */
+
+ return LDB_SUCCESS;
+}
+
+/**
+ * Test nested search execution against RootDSE
+ * on remote LDAP server.
+ */
+bool test_ldap_nested_search(struct torture_context *tctx)
+{
+ int ret;
+ char *url;
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ struct ldb_request *req;
+ struct nested_search_context *sctx;
+ static const char *attrs[] = {
+/*
+ "rootDomainNamingContext",
+ "configurationNamingContext",
+ "schemaNamingContext",
+ "defaultNamingContext",
+*/
+ "*",
+ NULL
+ };
+
+ sctx = talloc_zero(tctx, struct nested_search_context);
+ torture_assert(tctx, sctx, "Not enough memory");
+ sctx->tctx = tctx;
+
+ url = talloc_asprintf(sctx, "ldap://%s/", host);
+ if (!url) {
+ torture_assert(tctx, url, "Not enough memory");
+ }
+
+ torture_comment(tctx, "Connecting to: %s\n", url);
+ sctx->ldb = ldb_wrap_connect(sctx, tctx->ev, tctx->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+ torture_assert(tctx, sctx->ldb, "Failed to create ldb connection");
+
+ /* prepare context for searching */
+ sctx->root_dn = ldb_dn_new(sctx, sctx->ldb, NULL);
+ sctx->ldb_res = talloc_zero(sctx, struct ldb_result);
+
+ /* build search request */
+ ret = ldb_build_search_req(&req,
+ sctx->ldb,
+ sctx,
+ sctx->root_dn, LDB_SCOPE_BASE,
+ "(objectClass=*)", attrs, NULL,
+ sctx, nested_search_callback,
+ NULL);
+ if (ret != LDB_SUCCESS) {
+ torture_result(tctx, TORTURE_FAIL,
+ __location__ ": Allocating request failed: %s", ldb_errstring(sctx->ldb));
+ return false;
+ }
+
+ ret = ldb_request(sctx->ldb, req);
+ if (ret != LDB_SUCCESS) {
+ torture_result(tctx, TORTURE_FAIL,
+ __location__ ": Search failed: %s", ldb_errstring(sctx->ldb));
+ return false;
+ }
+
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ torture_result(tctx, TORTURE_FAIL,
+ __location__ ": Search error: %s", ldb_errstring(sctx->ldb));
+ return false;
+ }
+
+ /* TODO: check returned result */
+
+ talloc_free(sctx);
+ return true;
+}
+
diff --git a/source4/torture/ldap/netlogon.c b/source4/torture/ldap/netlogon.c
new file mode 100644
index 0000000..c2dd834
--- /dev/null
+++ b/source4/torture/ldap/netlogon.c
@@ -0,0 +1,664 @@
+/*
+ Unix SMB/CIFS Implementation.
+
+ test CLDAP/LDAP netlogon operations
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Matthias Dieter Wallnöfer 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/cldap/cldap.h"
+#include "libcli/ldap/ldap_client.h"
+#include "libcli/ldap/ldap_ndr.h"
+#include "libcli/resolve/resolve.h"
+#include "librpc/gen_ndr/netlogon.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#undef strcasecmp
+
+#define CHECK_STATUS(status, correct) torture_assert_ntstatus_equal(tctx, status, correct, "incorrect status")
+
+#define CHECK_VAL(v, correct) torture_assert_int_equal(tctx, (v), (correct), "incorrect value");
+
+#define CHECK_STRING(v, correct) torture_assert_str_equal(tctx, v, correct, "incorrect value");
+
+typedef NTSTATUS (*request_netlogon_t)(void *con,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io);
+
+typedef NTSTATUS (*request_rootdse_t)(void *con,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io);
+
+/*
+ test netlogon operations
+*/
+static bool test_ldap_netlogon(struct torture_context *tctx,
+ request_netlogon_t request_netlogon,
+ void *cldap,
+ const char *dest)
+{
+ NTSTATUS status;
+ struct cldap_netlogon search, empty_search;
+ struct netlogon_samlogon_response n1;
+ struct GUID guid;
+ int i;
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ empty_search = search;
+
+ printf("Trying without any attributes\n");
+ search = empty_search;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ n1 = search.out.netlogon;
+
+ search.in.user = "Administrator";
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ search.in.host = "__cldap_torture__";
+
+ printf("Scanning for netlogon levels\n");
+ for (i=0;i<256;i++) {
+ search.in.version = i;
+ printf("Trying netlogon level %d\n", i);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ printf("Scanning for netlogon level bits\n");
+ for (i=0;i<31;i++) {
+ search.in.version = (1<<i);
+ printf("Trying netlogon level 0x%x\n", i);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ }
+
+ search.in.version = NETLOGON_NT_VERSION_5|NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_IP;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying with User=NULL\n");
+ search.in.user = NULL;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ torture_assert(tctx,
+ strstr(search.out.netlogon.data.nt5_ex.pdc_name, "\\\\") == NULL,
+ "PDC name should not be in UNC form");
+
+ printf("Trying with User=Administrator\n");
+ search.in.user = "Administrator";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ torture_assert(tctx,
+ strstr(search.out.netlogon.data.nt5_ex.pdc_name, "\\\\") == NULL,
+ "PDC name should not be in UNC form");
+
+ search.in.version = NETLOGON_NT_VERSION_5;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ printf("Trying with User=NULL\n");
+ search.in.user = NULL;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ torture_assert(tctx,
+ strstr(search.out.netlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ printf("Trying with User=Administrator\n");
+ search.in.user = "Administrator";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ torture_assert(tctx,
+ strstr(search.out.netlogon.data.nt5_ex.pdc_name, "\\\\") != NULL,
+ "PDC name should be in UNC form");
+
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+
+ printf("Trying with a GUID\n");
+ search.in.realm = NULL;
+ search.in.domain_guid = GUID_string(tctx, &n1.data.nt5_ex.domain_uuid);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(GUID_string(tctx, &search.out.netlogon.data.nt5_ex.domain_uuid), search.in.domain_guid);
+ torture_assert(tctx,
+ strstr(search.out.netlogon.data.nt5_ex.pdc_name, "\\\\") == NULL,
+ "PDC name should not be in UNC form");
+
+ printf("Trying with a incorrect GUID\n");
+ guid = GUID_random();
+ search.in.user = NULL;
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+
+ printf("Trying with a AAC\n");
+ search.in.acct_control = ACB_WSTRUST|ACB_SVRTRUST;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a zero AAC\n");
+ search.in.acct_control = 0x0;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a zero AAC and user=Administrator\n");
+ search.in.acct_control = 0x0;
+ search.in.user = "Administrator";
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "Administrator");
+
+ printf("Trying with a bad AAC\n");
+ search.in.user = NULL;
+ search.in.acct_control = 0xFF00FF00;
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+
+ printf("Trying with a user only\n");
+ search = empty_search;
+ search.in.user = "Administrator";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ printf("Trying with just a bad username\n");
+ search.in.user = "___no_such_user___";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ printf("Trying with just a bad domain\n");
+ search = empty_search;
+ search.in.realm = "___no_such_domain___";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+
+ printf("Trying with a incorrect domain and correct guid\n");
+ search.in.domain_guid = GUID_string(tctx, &n1.data.nt5_ex.domain_uuid);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ printf("Trying with a incorrect domain and incorrect guid\n");
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_NOT_FOUND);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ printf("Trying with a incorrect GUID and correct domain\n");
+ search.in.domain_guid = GUID_string(tctx, &guid);
+ search.in.realm = n1.data.nt5_ex.dns_domain;
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(search.out.netlogon.data.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, "");
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ printf("Proof other results\n");
+ search.in.user = "Administrator";
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.forest, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.dns_domain, n1.data.nt5_ex.dns_domain);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.domain_name, n1.data.nt5_ex.domain_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.pdc_name, n1.data.nt5_ex.pdc_name);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.user_name, search.in.user);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.server_site, n1.data.nt5_ex.server_site);
+ CHECK_STRING(search.out.netlogon.data.nt5_ex.client_site, n1.data.nt5_ex.client_site);
+
+ return true;
+}
+
+/*
+ test cldap netlogon server type flags
+*/
+static bool test_ldap_netlogon_flags(struct torture_context *tctx,
+ request_netlogon_t request_netlogon,
+ void *cldap,
+ const char *dest)
+{
+ NTSTATUS status;
+ struct cldap_netlogon search;
+ struct netlogon_samlogon_response n1;
+ uint32_t server_type = 0;
+
+ printf("Printing out netlogon server type flags: %s\n", dest);
+
+ ZERO_STRUCT(search);
+ search.in.dest_address = NULL;
+ search.in.dest_port = 0;
+ search.in.acct_control = -1;
+ search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
+ search.in.map_response = true;
+
+ status = request_netlogon(cldap, tctx, &search);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ n1 = search.out.netlogon;
+ if (n1.ntver == NETLOGON_NT_VERSION_5)
+ server_type = n1.data.nt5.server_type;
+ else if (n1.ntver == NETLOGON_NT_VERSION_5EX)
+ server_type = n1.data.nt5_ex.server_type;
+
+ printf("The word is: %i\n", server_type);
+ if (server_type & NBT_SERVER_PDC)
+ printf("NBT_SERVER_PDC ");
+ if (server_type & NBT_SERVER_GC)
+ printf("NBT_SERVER_GC ");
+ if (server_type & NBT_SERVER_LDAP)
+ printf("NBT_SERVER_LDAP ");
+ if (server_type & NBT_SERVER_DS)
+ printf("NBT_SERVER_DS ");
+ if (server_type & NBT_SERVER_KDC)
+ printf("NBT_SERVER_KDC ");
+ if (server_type & NBT_SERVER_TIMESERV)
+ printf("NBT_SERVER_TIMESERV ");
+ if (server_type & NBT_SERVER_CLOSEST)
+ printf("NBT_SERVER_CLOSEST ");
+ if (server_type & NBT_SERVER_WRITABLE)
+ printf("NBT_SERVER_WRITABLE ");
+ if (server_type & NBT_SERVER_GOOD_TIMESERV)
+ printf("NBT_SERVER_GOOD_TIMESERV ");
+ if (server_type & NBT_SERVER_NDNC)
+ printf("NBT_SERVER_NDNC ");
+ if (server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6)
+ printf("NBT_SERVER_SELECT_SECRET_DOMAIN_6");
+ if (server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6)
+ printf("NBT_SERVER_FULL_SECRET_DOMAIN_6");
+ if (server_type & NBT_SERVER_ADS_WEB_SERVICE)
+ printf("NBT_SERVER_ADS_WEB_SERVICE ");
+ if (server_type & NBT_SERVER_DS_8)
+ printf("NBT_SERVER_DS_8 ");
+ if (server_type & DS_DNS_CONTROLLER)
+ printf("DS_DNS_CONTROLLER ");
+ if (server_type & DS_DNS_DOMAIN)
+ printf("DS_DNS_DOMAIN ");
+ if (server_type & DS_DNS_FOREST_ROOT)
+ printf("DS_DNS_FOREST_ROOT ");
+
+ printf("\n");
+
+ return true;
+}
+
+static NTSTATUS tcp_ldap_rootdse(void *data,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct ldap_connection *conn = talloc_get_type(data,
+ struct ldap_connection);
+ struct ldap_message *msg, *result;
+ struct ldap_request *req;
+ int i;
+ NTSTATUS status;
+
+ msg = new_ldap_message(mem_ctx);
+ if (!msg) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ msg->type = LDAP_TAG_SearchRequest;
+ msg->r.SearchRequest.basedn = "";
+ msg->r.SearchRequest.scope = LDAP_SEARCH_SCOPE_BASE;
+ msg->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
+ msg->r.SearchRequest.timelimit = 0;
+ msg->r.SearchRequest.sizelimit = 0;
+ msg->r.SearchRequest.attributesonly = false;
+ msg->r.SearchRequest.tree = ldb_parse_tree(msg, io->in.filter);
+ msg->r.SearchRequest.num_attributes = str_list_length(io->in.attributes);
+ msg->r.SearchRequest.attributes = io->in.attributes;
+
+ req = ldap_request_send(conn, msg);
+ if (req == NULL) {
+ printf("Could not setup ldap search\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ ZERO_STRUCT(io->out);
+ for (i = 0; i < 2; ++i) {
+ status = ldap_result_n(req, i, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ switch (result->type) {
+ case LDAP_TAG_SearchResultEntry:
+ if (i != 0) {
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+ io->out.response = &result->r.SearchResultEntry;
+ break;
+ case LDAP_TAG_SearchResultDone:
+ io->out.result = &result->r.SearchResultDone;
+ if (io->out.result->resultcode != LDAP_SUCCESS) {
+ return NT_STATUS_LDAP(io->out.result->resultcode);
+ }
+
+ return NT_STATUS_OK;
+ default:
+ return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS tcp_ldap_netlogon(void *conn,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ struct cldap_search search;
+ struct ldap_SearchResEntry *res;
+ NTSTATUS status;
+ DATA_BLOB *blob;
+
+ ZERO_STRUCT(search);
+ search.in.attributes = (const char *[]) { "netlogon", NULL };
+ search.in.filter = cldap_netlogon_create_filter(mem_ctx, io);
+ if (search.in.filter == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = tcp_ldap_rootdse(conn, mem_ctx, &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ res = search.out.response;
+ if (res == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ if (res->num_attributes != 1 ||
+ strcasecmp(res->attributes[0].name, "netlogon") != 0 ||
+ res->attributes[0].num_values != 1 ||
+ res->attributes[0].values->length < 2) {
+ return NT_STATUS_UNEXPECTED_NETWORK_ERROR;
+ }
+
+ blob = res->attributes[0].values;
+ status = pull_netlogon_samlogon_response(blob, mem_ctx,
+ &io->out.netlogon);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (io->in.map_response) {
+ map_netlogon_samlogon_response(&io->out.netlogon);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS udp_ldap_rootdse(void *data, TALLOC_CTX *mem_ctx,
+ struct cldap_search *io)
+{
+ struct cldap_socket *cldap = talloc_get_type(data,
+ struct cldap_socket);
+
+ return cldap_search(cldap, mem_ctx, io);
+}
+
+static bool test_netlogon_extra_attrs(struct torture_context *tctx,
+ request_rootdse_t request_rootdse,
+ void *conn)
+{
+ struct cldap_search io;
+ NTSTATUS status;
+ const char *attrs[] = {
+ "netlogon",
+ "supportedCapabilities",
+ NULL
+ };
+ const char *attrs2[] = { "netlogon", "*", NULL };
+ struct ldb_message ldbmsg = { NULL, 0, NULL };
+
+ ZERO_STRUCT(io);
+ io.in.dest_address = NULL;
+ io.in.dest_port = 0;
+ io.in.timeout = 2;
+ io.in.retries = 2;
+ /* Additional attributes may be requested next to netlogon */
+ torture_comment(tctx, "Requesting netlogon with additional attribute\n");
+ io.in.filter =
+ talloc_asprintf(tctx, "(&"
+ "(NtVer=%s)(AAC=%s)"
+ /* Query for LDAP_CAP_ACTIVE_DIRECTORY_OID */
+ "(supportedCapabilities=1.2.840.113556.1.4.800)"
+ ")",
+ ldap_encode_ndr_uint32(tctx,
+ NETLOGON_NT_VERSION_5EX),
+ ldap_encode_ndr_uint32(tctx, 0));
+ torture_assert(tctx, io.in.filter != NULL, "OOM");
+ io.in.attributes = attrs;
+ status = request_rootdse(conn, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ torture_assert(tctx, io.out.response != NULL, "No Entries found.");
+ CHECK_VAL(io.out.response->num_attributes, 2);
+
+ /* netlogon + '*' attr return zero results */
+ torture_comment(tctx, "Requesting netlogon and '*' attributes\n");
+ io.in.attributes = attrs2;
+ status = request_rootdse(conn, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ torture_assert(tctx, io.out.response != NULL, "No Entries found.");
+ ldbmsg.num_elements = io.out.response->num_attributes;
+ ldbmsg.elements = io.out.response->attributes;
+ torture_assert(tctx, ldb_msg_find_element(&ldbmsg, "netlogon") != NULL,
+ "Attribute netlogon not found in Result Entry\n");
+
+ /* Wildcards are not allowed in filters when netlogon is requested. */
+ torture_comment(tctx, "Requesting netlogon with invalid attr filter\n");
+ io.in.filter =
+ talloc_asprintf(tctx,
+ "(&(NtVer=%s)(AAC=%s)(supportedCapabilities=*))",
+ ldap_encode_ndr_uint32(tctx,
+ NETLOGON_NT_VERSION_5EX),
+ ldap_encode_ndr_uint32(tctx, 0));
+ torture_assert(tctx, io.in.filter != NULL, "OOM");
+ io.in.attributes = attrs;
+ status = request_rootdse(conn, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ torture_assert(tctx, io.out.response == NULL,
+ "A wildcard filter should return no entries.");
+
+ return true;
+}
+
+/*
+ Bug #11392: Huawei Unified Storage System S5500 V3 sends no NtVer
+ [MS-ADTS] Section 7.3.3.2 "Domain Controller Response to an LDAP Ping"
+*/
+static bool test_netlogon_huawei(struct torture_context *tctx,
+ request_rootdse_t request_rootdse,
+ void *conn)
+{
+ struct cldap_search io;
+ struct netlogon_samlogon_response n1;
+ NTSTATUS status;
+ const char *attrs[] = {
+ "netlogon",
+ NULL
+ };
+ struct ldb_message ldbmsg = { NULL, 0, NULL };
+
+ ZERO_STRUCT(io);
+ io.in.dest_address = NULL;
+ io.in.dest_port = 0;
+ io.in.timeout = 2;
+ io.in.retries = 2;
+
+ torture_comment(tctx, "Requesting netlogon without NtVer filter\n");
+ io.in.filter = talloc_asprintf(tctx, "(&(DnsDomain=%s))",
+ lpcfg_dnsdomain(tctx->lp_ctx));
+ torture_assert(tctx, io.in.filter != NULL, "OOM");
+ io.in.attributes = attrs;
+ status = request_rootdse(conn, tctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ torture_assert(tctx, io.out.response != NULL, "No Entries found.");
+ CHECK_VAL(io.out.response->num_attributes, 1);
+
+ ldbmsg.num_elements = io.out.response->num_attributes;
+ ldbmsg.elements = io.out.response->attributes;
+ torture_assert(tctx, ldb_msg_find_element(&ldbmsg, "netlogon") != NULL,
+ "Attribute netlogon not found in Result Entry\n");
+
+ status = pull_netlogon_samlogon_response(
+ io.out.response->attributes[0].values,
+ tctx,
+ &n1);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ CHECK_VAL(n1.ntver, NETLOGON_NT_VERSION_5);
+
+ return true;
+}
+
+bool torture_netlogon_tcp(struct torture_context *tctx)
+{
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ bool ret = true;
+ NTSTATUS status;
+ struct ldap_connection *conn;
+ TALLOC_CTX *mem_ctx;
+ const char *url;
+
+ mem_ctx = talloc_init("torture_ldap_netlogon");
+
+ url = talloc_asprintf(mem_ctx, "ldap://%s/", host);
+
+ status = torture_ldap_connection(tctx, &conn, url);
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ ret &= test_ldap_netlogon(tctx, tcp_ldap_netlogon, conn, host);
+ ret &= test_ldap_netlogon_flags(tctx, tcp_ldap_netlogon, conn, host);
+ ret &= test_netlogon_extra_attrs(tctx, tcp_ldap_rootdse, conn);
+
+ return ret;
+}
+
+static NTSTATUS udp_ldap_netlogon(void *data,
+ TALLOC_CTX *mem_ctx,
+ struct cldap_netlogon *io)
+{
+ struct cldap_socket *cldap = talloc_get_type(data,
+ struct cldap_socket);
+
+ return cldap_netlogon(cldap, mem_ctx, io);
+}
+
+bool torture_netlogon_udp(struct torture_context *tctx)
+{
+ const char *host = torture_setting_string(tctx, "host", NULL);
+ const char *ip;
+ struct nbt_name nbt_name;
+ bool ret = true;
+ int r;
+ struct cldap_socket *cldap;
+ NTSTATUS status;
+ struct tsocket_address *dest_addr;
+
+ make_nbt_name_server(&nbt_name, host);
+
+ status = resolve_name_ex(lpcfg_resolve_context(tctx->lp_ctx),
+ 0, 0, &nbt_name, tctx, &ip, tctx->ev);
+ torture_assert_ntstatus_ok(tctx, status,
+ talloc_asprintf(tctx,"Failed to resolve %s: %s",
+ nbt_name.name, nt_errstr(status)));
+
+ r = tsocket_address_inet_from_strings(tctx, "ip",
+ ip,
+ lpcfg_cldap_port(tctx->lp_ctx),
+ &dest_addr);
+ CHECK_VAL(r, 0);
+
+ /* cldap_socket_init should now know about the dest. address */
+ status = cldap_socket_init(tctx, NULL, dest_addr, &cldap);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ ret &= test_ldap_netlogon(tctx, udp_ldap_netlogon, cldap, host);
+ ret &= test_ldap_netlogon_flags(tctx, udp_ldap_netlogon, cldap, host);
+ ret &= test_netlogon_extra_attrs(tctx, udp_ldap_rootdse, cldap);
+ ret &= test_netlogon_huawei(tctx, udp_ldap_rootdse, cldap);
+
+ return ret;
+}
diff --git a/source4/torture/ldap/schema.c b/source4/torture/ldap/schema.c
new file mode 100644
index 0000000..06313bc
--- /dev/null
+++ b/source4/torture/ldap/schema.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP schema tests
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/cmdline.h"
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+#include "../lib/util/dlinklist.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+
+struct test_rootDSE {
+ const char *defaultdn;
+ const char *rootdn;
+ const char *configdn;
+ const char *schemadn;
+};
+
+struct test_schema_ctx {
+ struct ldb_context *ldb;
+
+ struct ldb_paged_control *ctrl;
+ uint32_t count;
+ bool pending;
+
+ int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *);
+ void *private_data;
+};
+
+static bool test_search_rootDSE(struct ldb_context *ldb, struct test_rootDSE *root)
+{
+ int ret;
+ struct ldb_message *msg;
+ struct ldb_result *r;
+
+ d_printf("Testing RootDSE Search\n");
+
+ ret = ldb_search(ldb, ldb, &r, ldb_dn_new(ldb, ldb, NULL),
+ LDB_SCOPE_BASE, NULL, NULL);
+ if (ret != LDB_SUCCESS) {
+ return false;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return false;
+ }
+
+ msg = r->msgs[0];
+
+ root->defaultdn = ldb_msg_find_attr_as_string(msg, "defaultNamingContext", NULL);
+ talloc_steal(ldb, root->defaultdn);
+ root->rootdn = ldb_msg_find_attr_as_string(msg, "rootDomainNamingContext", NULL);
+ talloc_steal(ldb, root->rootdn);
+ root->configdn = ldb_msg_find_attr_as_string(msg, "configurationNamingContext", NULL);
+ talloc_steal(ldb, root->configdn);
+ root->schemadn = ldb_msg_find_attr_as_string(msg, "schemaNamingContext", NULL);
+ talloc_steal(ldb, root->schemadn);
+
+ talloc_free(r);
+
+ return true;
+}
+
+static int test_schema_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct test_schema_ctx *actx;
+ int ret = LDB_SUCCESS;
+
+ actx = talloc_get_type(req->context, struct test_schema_ctx);
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ actx->count++;
+ ret = actx->callback(actx->private_data, actx->ldb, ares->message);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ break;
+
+ case LDB_REPLY_DONE:
+ if (ares->controls) {
+ struct ldb_paged_control *ctrl = NULL;
+ int i;
+
+ for (i=0; ares->controls[i]; i++) {
+ if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[i]->oid) == 0) {
+ ctrl = talloc_get_type(ares->controls[i]->data, struct ldb_paged_control);
+ break;
+ }
+ }
+
+ if (!ctrl) break;
+
+ talloc_free(actx->ctrl->cookie);
+ actx->ctrl->cookie = talloc_steal(actx->ctrl->cookie, ctrl->cookie);
+ actx->ctrl->cookie_len = ctrl->cookie_len;
+
+ if (actx->ctrl->cookie_len > 0) {
+ actx->pending = true;
+ }
+ }
+ talloc_free(ares);
+ return ldb_request_done(req, LDB_SUCCESS);
+
+ default:
+ d_printf("%s: unknown Reply Type %u\n", __location__, ares->type);
+ return ldb_request_done(req, LDB_ERR_OTHER);
+ }
+
+ if (talloc_free(ares) == -1) {
+ d_printf("talloc_free failed\n");
+ actx->pending = 0;
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ret) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static bool test_create_schema_type(struct ldb_context *ldb, struct test_rootDSE *root,
+ const char *filter,
+ int (*callback)(void *, struct ldb_context *ldb, struct ldb_message *),
+ void *private_data)
+{
+ struct ldb_control **ctrl;
+ struct ldb_paged_control *control;
+ struct ldb_request *req;
+ int ret;
+ struct test_schema_ctx *actx;
+
+ actx = talloc(ldb, struct test_schema_ctx);
+ actx->ldb = ldb;
+ actx->private_data = private_data;
+ actx->callback= callback;
+
+ ctrl = talloc_array(actx, struct ldb_control *, 2);
+ ctrl[0] = talloc(ctrl, struct ldb_control);
+ ctrl[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ ctrl[0]->critical = true;
+ control = talloc(ctrl[0], struct ldb_paged_control);
+ control->size = 1000;
+ control->cookie = NULL;
+ control->cookie_len = 0;
+ ctrl[0]->data = control;
+ ctrl[1] = NULL;
+
+ ret = ldb_build_search_req(&req, ldb, actx,
+ ldb_dn_new(actx, ldb, root->schemadn),
+ LDB_SCOPE_SUBTREE,
+ filter, NULL,
+ ctrl,
+ actx, test_schema_search_callback,
+ NULL);
+
+ actx->ctrl = control;
+ actx->count = 0;
+again:
+ actx->pending = false;
+
+ ret = ldb_request(ldb, req);
+ if (ret != LDB_SUCCESS) {
+ d_printf("search failed - %s\n", ldb_errstring(ldb));
+ talloc_free(actx);
+ return false;
+ }
+
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ if (ret != LDB_SUCCESS) {
+ d_printf("search error - %s\n", ldb_errstring(ldb));
+ talloc_free(actx);
+ return false;
+ }
+
+ if (actx->pending)
+ goto again;
+
+ d_printf("filter[%s] count[%u]\n", filter, actx->count);
+ talloc_free(actx);
+ return true;
+}
+
+static int test_add_attribute(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
+{
+ struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
+ WERROR status;
+
+ status = dsdb_set_attribute_from_ldb(ldb, schema, msg);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ return LDB_SUCCESS;
+failed:
+ return LDB_ERR_OTHER;
+}
+
+static int test_add_class(void *ptr, struct ldb_context *ldb, struct ldb_message *msg)
+{
+ struct dsdb_schema *schema = talloc_get_type(ptr, struct dsdb_schema);
+ WERROR status;
+
+ status = dsdb_set_class_from_ldb(schema, msg);
+ if (!W_ERROR_IS_OK(status)) {
+ goto failed;
+ }
+
+ return LDB_SUCCESS;
+failed:
+ return LDB_ERR_OTHER;
+}
+
+static bool test_create_schema(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema **_schema)
+{
+ bool ret = true;
+ struct dsdb_schema *schema;
+
+ schema = talloc_zero(ldb, struct dsdb_schema);
+
+ d_printf("Fetching attributes...\n");
+ ret &= test_create_schema_type(ldb, root, "(objectClass=attributeSchema)",
+ test_add_attribute, schema);
+ d_printf("Fetching objectClasses...\n");
+ ret &= test_create_schema_type(ldb, root, "(objectClass=classSchema)",
+ test_add_class, schema);
+
+ if (ret == true) {
+ *_schema = schema;
+ }
+ return ret;
+}
+
+static bool test_dump_not_replicated(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *a;
+ uint32_t a_i = 1;
+
+ d_printf("Dumping not replicated attributes\n");
+
+ for (a=schema->attributes; a; a = a->next) {
+ if (!(a->systemFlags & 0x00000001)) continue;
+ d_printf("attr[%4u]: '%s'\n", a_i++,
+ a->lDAPDisplayName);
+ }
+
+ return true;
+}
+
+static bool test_dump_partial(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *a;
+ uint32_t a_i = 1;
+
+ d_printf("Dumping attributes which are provided by the global catalog\n");
+
+ for (a=schema->attributes; a; a = a->next) {
+ if (!(a->systemFlags & 0x00000002) && !a->isMemberOfPartialAttributeSet) continue;
+ d_printf("attr[%4u]: %u %u '%s'\n", a_i++,
+ a->systemFlags & 0x00000002, a->isMemberOfPartialAttributeSet,
+ a->lDAPDisplayName);
+ }
+
+ return true;
+}
+
+static bool test_dump_contructed(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *a;
+ uint32_t a_i = 1;
+
+ d_printf("Dumping constructed attributes\n");
+
+ for (a=schema->attributes; a; a = a->next) {
+ if (!(a->systemFlags & 0x00000004)) continue;
+ d_printf("attr[%4u]: '%s'\n", a_i++,
+ a->lDAPDisplayName);
+ }
+
+ return true;
+}
+
+static bool test_dump_sorted_syntax(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *a;
+ uint32_t a_i = 1;
+ uint32_t i;
+ const char *syntaxes[] = {
+ "2.5.5.0",
+ "2.5.5.1",
+ "2.5.5.2",
+ "2.5.5.3",
+ "2.5.5.4",
+ "2.5.5.5",
+ "2.5.5.6",
+ "2.5.5.7",
+ "2.5.5.8",
+ "2.5.5.9",
+ "2.5.5.10",
+ "2.5.5.11",
+ "2.5.5.12",
+ "2.5.5.13",
+ "2.5.5.14",
+ "2.5.5.15",
+ "2.5.5.16",
+ "2.5.5.17"
+ };
+
+ d_printf("Dumping attribute syntaxes\n");
+
+ for (i=0; i < ARRAY_SIZE(syntaxes); i++) {
+ for (a=schema->attributes; a; a = a->next) {
+ char *om_hex;
+
+ if (strcmp(syntaxes[i], a->attributeSyntax_oid) != 0) continue;
+
+ om_hex = data_blob_hex_string_upper(ldb, &a->oMObjectClass);
+ if (!om_hex) {
+ return false;
+ }
+
+ d_printf("attr[%4u]: %s %u '%s' '%s'\n", a_i++,
+ a->attributeSyntax_oid, a->oMSyntax,
+ om_hex, a->lDAPDisplayName);
+ talloc_free(om_hex);
+ }
+ }
+
+ return true;
+}
+
+static bool test_dump_not_in_filtered_replica(struct ldb_context *ldb, struct test_rootDSE *root, struct dsdb_schema *schema)
+{
+ struct dsdb_attribute *a;
+ uint32_t a_i = 1;
+
+ d_printf("Dumping attributes not in filtered replica\n");
+
+ for (a=schema->attributes; a; a = a->next) {
+ if (!dsdb_attribute_is_attr_in_filtered_replica(a)) {
+ d_printf("attr[%4u]: '%s'\n", a_i++,
+ a->lDAPDisplayName);
+ }
+ }
+ return true;
+}
+
+bool torture_ldap_schema(struct torture_context *torture)
+{
+ struct ldb_context *ldb;
+ bool ret = true;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ char *url;
+ struct test_rootDSE rootDSE;
+ struct dsdb_schema *schema = NULL;
+
+ ZERO_STRUCT(rootDSE);
+
+ url = talloc_asprintf(torture, "ldap://%s/", host);
+
+ ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+ if (!ldb) goto failed;
+
+ ret &= test_search_rootDSE(ldb, &rootDSE);
+ if (!ret) goto failed;
+ ret &= test_create_schema(ldb, &rootDSE, &schema);
+ if (!ret) goto failed;
+
+ ret &= test_dump_not_replicated(ldb, &rootDSE, schema);
+ ret &= test_dump_partial(ldb, &rootDSE, schema);
+ ret &= test_dump_contructed(ldb, &rootDSE, schema);
+ ret &= test_dump_sorted_syntax(ldb, &rootDSE, schema);
+ ret &= test_dump_not_in_filtered_replica(ldb, &rootDSE, schema);
+
+failed:
+ return ret;
+}
diff --git a/source4/torture/ldap/session_expiry.c b/source4/torture/ldap/session_expiry.c
new file mode 100644
index 0000000..e910662
--- /dev/null
+++ b/source4/torture/ldap/session_expiry.c
@@ -0,0 +1,122 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Test LDB attribute functions
+ *
+ * Copyright (C) Andrew Bartlet <abartlet@samba.org> 2008-2009
+ * Copyright (C) Matthieu Patou <mat@matws.net> 2009
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include <ldb.h>
+#include <ldb_errors.h>
+#include "ldb_wrap.h"
+#include "param/param.h"
+#include "lib/cmdline/cmdline.h"
+#include "auth/credentials/credentials.h"
+#include "libcli/ldap/ldap_client.h"
+#include "torture/smbtorture.h"
+#include "torture/ldap/proto.h"
+
+bool torture_ldap_session_expiry(struct torture_context *torture)
+{
+ const char *host = torture_setting_string(torture, "host", NULL);
+ struct cli_credentials *credentials = samba_cmdline_get_creds();
+ struct ldb_context *ldb = NULL;
+ const char *url = NULL;
+ bool ret = false;
+ bool ok;
+ struct ldb_dn *rootdn = NULL;
+ struct ldb_result *result = NULL;
+ int rc = LDB_SUCCESS;
+
+ /*
+ * Further down we request a ticket lifetime of 4
+ * seconds. Give the server 10 seconds for this to kick in
+ */
+ const struct timeval endtime = timeval_current_ofs(10, 0);
+
+ url = talloc_asprintf(torture, "ldap://%s/", host);
+ torture_assert_goto(
+ torture, url!=NULL, ret, fail, "talloc_asprintf failed");
+
+ cli_credentials_set_kerberos_state(credentials,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+
+ ok = lpcfg_set_option(
+ torture->lp_ctx, "gensec_gssapi:requested_life_time=4");
+ torture_assert_goto(
+ torture, ok, ret, fail, "lpcfg_set_option failed");
+
+ ldb = ldb_wrap_connect(
+ torture,
+ torture->ev,
+ torture->lp_ctx,
+ url,
+ NULL,
+ credentials,
+ 0);
+ torture_assert_goto(
+ torture, ldb!=NULL, ret, fail, "ldb_wrap_connect failed");
+
+ rootdn = ldb_dn_new(ldb, ldb, NULL);
+ torture_assert_goto(
+ torture, rootdn!=NULL, ret, fail, "ldb_dn_new failed");
+
+ rc = ldb_search(
+ ldb, /* ldb */
+ ldb, /* mem_ctx */
+ &result, /* result */
+ rootdn, /* base */
+ LDB_SCOPE_BASE, /* scope */
+ NULL, /* attrs */
+ "(objectclass=*)"); /* exp_fmt */
+ torture_assert_goto(
+ torture, rc==LDB_SUCCESS, ret, fail, "1st ldb_search failed");
+
+ do {
+ smb_msleep(1000);
+
+ rc = ldb_search(
+ ldb, /* ldb */
+ ldb, /* mem_ctx */
+ &result, /* result */
+ rootdn, /* base */
+ LDB_SCOPE_BASE, /* scope */
+ NULL, /* attrs */
+ "(objectclass=*)"); /* exp_fmt */
+ printf("ldb_search returned %s\n", ldb_strerror(rc));
+ TALLOC_FREE(result);
+
+ if (rc != LDB_SUCCESS) {
+ break;
+ }
+ } while (!timeval_expired(&endtime));
+
+ torture_assert_goto(
+ torture,
+ rc==LDB_ERR_PROTOCOL_ERROR,
+ ret,
+ fail,
+ "expected LDB_ERR_PROTOCOL_ERROR after 4 seconds");
+
+ ret = true;
+fail:
+ TALLOC_FREE(ldb);
+ return ret;
+}
diff --git a/source4/torture/ldap/uptodatevector.c b/source4/torture/ldap/uptodatevector.c
new file mode 100644
index 0000000..01c85ab
--- /dev/null
+++ b/source4/torture/ldap/uptodatevector.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS Implementation.
+ LDAP replUpToDateVector tests
+
+ Copyright (C) Stefan Metzmacher 2007
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "libcli/ldap/ldap_client.h"
+#include "lib/cmdline/cmdline.h"
+#include "ldb_wrap.h"
+#include "dsdb/samdb/samdb.h"
+
+#include "torture/torture.h"
+#include "torture/ldap/proto.h"
+
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+
+#include "param/param.h"
+
+static bool test_check_uptodatevector(struct torture_context *torture,
+ struct ldb_context *ldb,
+ struct ldb_dn *partition_dn)
+{
+ bool ok = true;
+ uint32_t i;
+ int ret;
+ enum ndr_err_code ndr_err;
+ struct ldb_result *r;
+ const struct ldb_val *utdv_val1;
+ struct replUpToDateVectorBlob utdv1;
+ static const char *attrs[] = {
+ "uSNChanged",
+ "replUpToDateVector",
+ "description",
+ NULL
+ };
+
+ torture_comment(torture, "Check replUpToDateVector on partition[%s]\n",
+ ldb_dn_get_linearized(partition_dn));
+
+ ret = ldb_search(ldb, torture, &r, partition_dn, LDB_SCOPE_BASE, attrs,
+ "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return false;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return false;
+ }
+
+ ZERO_STRUCT(utdv1);
+ utdv_val1 = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector");
+ if (utdv_val1) {
+ ndr_err = ndr_pull_struct_blob_all(utdv_val1, torture,
+ &utdv1,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ }
+
+ for (i=0; i < 2; i++) {
+ const struct ldb_val *utdv_val;
+ struct replUpToDateVectorBlob utdv;
+ struct ldb_message *msg;
+ char *description;
+ uint32_t j;
+ bool no_match = false;
+
+ /* make a 'modify' msg, and only for serverReference */
+ msg = ldb_msg_new(torture);
+ if (!msg) return false;
+ msg->dn = partition_dn;
+
+ description = talloc_asprintf(msg, "torture replUpToDateVector[%u]", i);
+ if (!description) return false;
+
+ ret = ldb_msg_add_string(msg, "description", description);
+ if (ret != 0) return false;
+
+ for (j=0;j<msg->num_elements;j++) {
+ msg->elements[j].flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ ret = ldb_modify(ldb, msg);
+ if (ret != LDB_SUCCESS) return false;
+
+ ret = ldb_search(ldb, msg, &r, partition_dn, LDB_SCOPE_BASE,
+ attrs, "(objectClass=*)");
+ if (ret != LDB_SUCCESS) {
+ return false;
+ } else if (r->count != 1) {
+ talloc_free(r);
+ return false;
+ }
+
+ ZERO_STRUCT(utdv);
+ utdv_val = ldb_msg_find_ldb_val(r->msgs[0], "replUpToDateVector");
+ if (utdv_val) {
+ ndr_err = ndr_pull_struct_blob_all(utdv_val, torture,
+ &utdv,
+ (ndr_pull_flags_fn_t)ndr_pull_replUpToDateVectorBlob);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+ }
+
+ if (!utdv_val1 && utdv_val) {
+ no_match = true;
+ } else if (utdv_val1 && !utdv_val) {
+ no_match = true;
+ } else if (!utdv_val1 && !utdv_val) {
+ } else if (utdv_val1->length != utdv_val->length) {
+ no_match = true;
+ } else if (utdv_val1->length && memcmp(utdv_val1->data, utdv_val->data, utdv_val->length) != 0) {
+ no_match = true;
+ }
+
+ torture_comment(torture, "[%u]: uSNChanged[%llu] description[%s] replUpToDateVector[%s]\n", i,
+ (unsigned long long)ldb_msg_find_attr_as_uint64(r->msgs[0], "uSNChanged", 0),
+ ldb_msg_find_attr_as_string(r->msgs[0], "description", NULL),
+ (no_match ? "changed!: not ok" : "not changed: ok"));
+
+ if (no_match) {
+ NDR_PRINT_DEBUG(replUpToDateVectorBlob, &utdv1);
+ NDR_PRINT_DEBUG(replUpToDateVectorBlob, &utdv);
+ ok = false;
+ }
+
+ talloc_free(msg);
+ }
+
+ return ok;
+}
+
+bool torture_ldap_uptodatevector(struct torture_context *torture)
+{
+ struct ldb_context *ldb;
+ bool ret = true;
+ const char *host = torture_setting_string(torture, "host", NULL);
+ char *url;
+
+ url = talloc_asprintf(torture, "ldap://%s/", host);
+ if (!url) goto failed;
+
+ ldb = ldb_wrap_connect(torture, torture->ev, torture->lp_ctx, url,
+ NULL,
+ samba_cmdline_get_creds(),
+ 0);
+ if (!ldb) goto failed;
+
+ ret &= test_check_uptodatevector(torture, ldb, ldb_get_default_basedn(ldb));
+ ret &= test_check_uptodatevector(torture, ldb, ldb_get_config_basedn(ldb));
+ ret &= test_check_uptodatevector(torture, ldb, ldb_get_schema_basedn(ldb));
+
+ return ret;
+failed:
+ return false;
+}