diff options
Diffstat (limited to 'source4/torture/ldap')
-rw-r--r-- | source4/torture/ldap/basic.c | 239 | ||||
-rw-r--r-- | source4/torture/ldap/cldap.c | 496 | ||||
-rw-r--r-- | source4/torture/ldap/cldapbench.c | 131 | ||||
-rw-r--r-- | source4/torture/ldap/common.c | 117 | ||||
-rw-r--r-- | source4/torture/ldap/schema.c | 397 | ||||
-rw-r--r-- | source4/torture/ldap/uptodatevector.c | 178 |
6 files changed, 1558 insertions, 0 deletions
diff --git a/source4/torture/ldap/basic.c b/source4/torture/ldap/basic.c new file mode 100644 index 0000000000..358bf53590 --- /dev/null +++ b/source4/torture/ldap/basic.c @@ -0,0 +1,239 @@ +/* + Unix SMB/CIFS mplementation. + 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 "lib/cmdline/popt_common.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#include "param/param.h" + +static bool test_bind_simple(struct ldap_connection *conn, const char *userdn, const char *password) +{ + NTSTATUS status; + bool ret = true; + + status = torture_ldap_bind(conn, userdn, password); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + } + + return ret; +} + +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) +{ + bool ret = true; + + printf("Testing multiple binds on a single connnection as anonymous and user\n"); + + ret = test_bind_simple(conn, NULL, NULL); + if (!ret) { + printf("1st bind as anonymous failed\n"); + return ret; + } + + ret = test_bind_simple(conn, userdn, password); + if (!ret) { + printf("2nd bind as authenticated user failed\n"); + } + + return ret; +} + +static bool test_search_rootDSE(struct ldap_connection *conn, char **basedn) +{ + 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; + + 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++) { + 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); + } + } + } + + talloc_free(req); + + 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; +} + + +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); + char *url; + char *basedn; + + 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)) { + ret = false; + } + + /* other basic tests here */ + + if (!test_multibind(conn, userdn, secret)) { + ret = false; + } + + if (!test_bind_sasl(torture, conn, cmdline_credentials)) { + ret = false; + } + + if (!test_compare_sasl(conn, basedn)) { + ret = false; + } + + /* no more test we are closing */ + torture_ldap_close(conn); + talloc_free(mem_ctx); + + + return ret; +} + diff --git a/source4/torture/ldap/cldap.c b/source4/torture/ldap/cldap.c new file mode 100644 index 0000000000..3730193c86 --- /dev/null +++ b/source4/torture/ldap/cldap.c @@ -0,0 +1,496 @@ +/* + Unix SMB/CIFS mplementation. + + test CLDAP operations + + 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/ldap/ldap.h" +#include "librpc/gen_ndr/ndr_nbt.h" +#include "torture/torture.h" +#include "lib/ldb/include/ldb.h" +#include "param/param.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"); +/* + test netlogon operations +*/ +static bool test_cldap_netlogon(struct torture_context *tctx, const char *dest) +{ + struct cldap_socket *cldap; + NTSTATUS status; + struct cldap_netlogon search, empty_search; + struct netlogon_samlogon_response n1; + struct GUID guid; + int i; + + cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + ZERO_STRUCT(search); + search.in.dest_address = dest; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + 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 = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + n1 = search.out.netlogon; + + search.in.user = "Administrator"; + search.in.realm = n1.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 = cldap_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 = cldap_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 = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying with User=NULL\n"); + + search.in.user = NULL; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + + printf("Trying with User=Administrator\n"); + + search.in.user = "Administrator"; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, search.in.user); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX); + + search.in.version = NETLOGON_NT_VERSION_5; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("Trying with User=NULL\n"); + + search.in.user = NULL; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE); + + printf("Trying with User=Administrator\n"); + + search.in.user = "Administrator"; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, search.in.user); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN); + + 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.nt5_ex.domain_uuid); + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX); + CHECK_STRING(GUID_string(tctx, &search.out.netlogon.nt5_ex.domain_uuid), search.in.domain_guid); + + printf("Trying with a incorrect GUID\n"); + guid = GUID_random(); + search.in.user = NULL; + search.in.domain_guid = GUID_string(tctx, &guid); + status = cldap_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.nt5_ex.dns_domain; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + + printf("Trying with a zero AAC\n"); + search.in.acct_control = 0x0; + search.in.realm = n1.nt5_ex.dns_domain; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + CHECK_STRING(search.out.netlogon.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.nt5_ex.dns_domain; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX); + CHECK_STRING(search.out.netlogon.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.nt5_ex.dns_domain; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + + printf("Trying with a user only\n"); + search = empty_search; + search.in.user = "Administrator"; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.dns_domain, n1.nt5_ex.dns_domain); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, search.in.user); + + printf("Trying with just a bad username\n"); + search.in.user = "___no_such_user___"; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, search.in.user); + CHECK_STRING(search.out.netlogon.nt5_ex.dns_domain, n1.nt5_ex.dns_domain); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_USER_UNKNOWN_EX); + + printf("Trying with just a bad domain\n"); + search = empty_search; + search.in.realm = "___no_such_domain___"; + status = cldap_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.nt5_ex.domain_uuid); + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.dns_domain, n1.nt5_ex.dns_domain); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + + printf("Trying with a incorrect domain and incorrect guid\n"); + search.in.domain_guid = GUID_string(tctx, &guid); + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_NOT_FOUND); + CHECK_STRING(search.out.netlogon.nt5_ex.dns_domain, n1.nt5_ex.dns_domain); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + + printf("Trying with a incorrect GUID and correct domain\n"); + search.in.domain_guid = GUID_string(tctx, &guid); + search.in.realm = n1.nt5_ex.dns_domain; + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_STRING(search.out.netlogon.nt5_ex.dns_domain, n1.nt5_ex.dns_domain); + CHECK_STRING(search.out.netlogon.nt5_ex.user_name, ""); + CHECK_VAL(search.out.netlogon.nt5_ex.command, LOGON_SAM_LOGON_RESPONSE_EX); + + return true; +} + +/* + test cldap netlogon server type flags +*/ +static bool test_cldap_netlogon_flags(struct torture_context *tctx, + const char *dest) +{ + struct cldap_socket *cldap; + NTSTATUS status; + struct cldap_netlogon search; + struct netlogon_samlogon_response n1; + uint32_t server_type; + + cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + printf("Printing out netlogon server type flags:\n"); + + ZERO_STRUCT(search); + search.in.dest_address = dest; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + search.in.acct_control = -1; + search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; + search.in.map_response = true; + + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + n1 = search.out.netlogon; + if (n1.ntver == NETLOGON_NT_VERSION_5) + server_type = n1.nt5.server_type; + else if (n1.ntver == NETLOGON_NT_VERSION_5EX) + server_type = n1.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_SEL_SEC_DOM_6) + printf("NBT_SERVER_SEL_SEC_DOM_6 "); + if (server_type & NBT_SERVER_FUL_SEC_DOM_6) + printf("NBT_SERVER_FUL_SEC_DOM_6 "); + if (server_type & NBT_SERVER_DS_DNS_CONTR) + printf("NBT_SERVER_DS_DNS_CONTR "); + if (server_type & NBT_SERVER_DS_DNS_DOMAIN) + printf("NBT_SERVER_DS_DNS_DOMAIN "); + if (server_type & NBT_SERVER_DS_DNS_FOREST) + printf("NBT_SERVER_DS_DNS_FOREST "); + + printf("\n"); + + return true; +} + +/* + 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 cldap netlogon server type flag "NBT_SERVER_DS_DNS_FOREST" +*/ +static bool test_cldap_netlogon_flag_ds_dns_forest(struct torture_context *tctx, + const char *dest) +{ + struct cldap_socket *cldap; + NTSTATUS status; + struct cldap_netlogon search; + uint32_t server_type; + struct netlogon_samlogon_response n1; + + bool result = true; + + cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + printf("Testing netlogon server type flag NBT_SERVER_DS_DNS_FOREST: "); + + ZERO_STRUCT(search); + search.in.dest_address = dest; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + search.in.acct_control = -1; + search.in.version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX; + search.in.map_response = true; + + status = cldap_netlogon(cldap, tctx, &search); + CHECK_STATUS(status, NT_STATUS_OK); + + n1 = search.out.netlogon; + if (n1.ntver == NETLOGON_NT_VERSION_5) + server_type = n1.nt5.server_type; + else if (n1.ntver == NETLOGON_NT_VERSION_5EX) + server_type = n1.nt5_ex.server_type; + + if (server_type & NBT_SERVER_DS_DNS_FOREST) { + struct cldap_search search2; + const char *attrs[] = { "defaultNamingContext", "rootDomainNamingContext", + NULL }; + struct ldb_context *ldb; + struct ldb_message *msg; + + /* Trying to fetch the attributes "defaultNamingContext" and + "rootDomainNamingContext" */ + ZERO_STRUCT(search2); + search2.in.dest_address = dest; + search2.in.dest_port = lp_cldap_port(tctx->lp_ctx); + search2.in.timeout = 10; + search2.in.retries = 3; + search2.in.filter = "(objectclass=*)"; + search2.in.attributes = attrs; + + status = cldap_search(cldap, tctx, &search2); + CHECK_STATUS(status, NT_STATUS_OK); + + ldb = ldb_init(NULL, NULL); + + msg = ldap_msg_to_ldb(ldb, ldb, search2.out.response); + + /* Try to compare the two attributes */ + if (ldb_msg_element_compare(ldb_msg_find_element(msg, attrs[0]), + ldb_msg_find_element(msg, attrs[1]))) + result = false; + + talloc_free(ldb); + } + + if (result) + printf("passed\n"); + else + printf("failed\n"); + + return result; +} + +/* + 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 }; + + cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + ZERO_STRUCT(search); + search.in.dest_address = dest; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + 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 = "(objectclass2=*)"; + 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_netlogon(torture, host); + ret &= test_cldap_netlogon_flags(torture, host); + ret &= test_cldap_netlogon_flag_ds_dns_forest(torture, host); + 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 0000000000..df2a5b0551 --- /dev/null +++ b/source4/torture/ldap/cldapbench.c @@ -0,0 +1,131 @@ +/* + 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 "lib/events/events.h" +#include "libcli/cldap/cldap.h" +#include "libcli/resolve/resolve.h" +#include "torture/torture.h" +#include "param/param.h" + +struct bench_state { + int pass_count, fail_count; +}; + +static void request_handler(struct cldap_request *req) +{ + struct cldap_netlogon io; + struct bench_state *state = talloc_get_type(req->async.private, struct bench_state); + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(NULL); + io.in.version = 6; + status = cldap_netlogon_recv(req, tmp_ctx, &io); + if (NT_STATUS_IS_OK(status)) { + state->pass_count++; + } else { + state->fail_count++; + } + talloc_free(tmp_ctx); +} + +/* + benchmark cldap calls +*/ +static bool bench_cldap(struct torture_context *tctx, const char *address) +{ + struct cldap_socket *cldap; + int num_sent=0; + struct timeval tv = timeval_current(); + bool ret = true; + int timelimit = torture_setting_int(tctx, "timelimit", 10); + struct cldap_netlogon search; + struct bench_state *state; + + cldap = cldap_socket_init(tctx, tctx->ev, lp_iconv_convenience(tctx->lp_ctx)); + + state = talloc_zero(tctx, struct bench_state); + + ZERO_STRUCT(search); + search.in.dest_address = address; + search.in.dest_port = lp_cldap_port(tctx->lp_ctx); + search.in.acct_control = -1; + search.in.version = 6; + + printf("Running for %d seconds\n", timelimit); + while (timeval_elapsed(&tv) < timelimit) { + while (num_sent - (state->pass_count+state->fail_count) < 10) { + struct cldap_request *req; + req = cldap_netlogon_send(cldap, &search); + + req->async.private = state; + req->async.fn = request_handler; + 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); + } + } + } + + event_loop_once(cldap->event_ctx); + } + + while (num_sent != (state->pass_count + state->fail_count)) { + event_loop_once(cldap->event_ctx); + } + + printf("%.1f queries per second (%d failures) \n", + state->pass_count / timeval_elapsed(&tv), + state->fail_count); + + talloc_free(cldap); + return ret; +} + + +/* + 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(lp_resolve_context(torture->lp_ctx), &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(torture, address); + + return ret; +} diff --git a/source4/torture/ldap/common.c b/source4/torture/ldap/common.c new file mode 100644 index 0000000000..2c11de729c --- /dev/null +++ b/source4/torture/ldap/common.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS mplementation. + 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; +} + +/* open a ldap connection to a server */ +NTSTATUS torture_ldap_connection2(struct torture_context *tctx, struct ldap_connection **conn, + const char *url, const char *userdn, const char *password) +{ + NTSTATUS status; + + status = torture_ldap_connection(tctx, conn, url); + NT_STATUS_NOT_OK_RETURN(status); + + status = ldap_bind_simple(*conn, userdn, password); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed a simple ldap bind - %s\n", ldap_errstr(*conn, tctx, status)); + } + + return status; +} + +/* close an ldap connection to a server */ +NTSTATUS torture_ldap_close(struct ldap_connection *conn) +{ + talloc_free(conn); + return NT_STATUS_OK; +} + +NTSTATUS torture_ldap_init(void) +{ + struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "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, "CLDAP", torture_cldap); + torture_suite_add_simple_test(suite, "SCHEMA", torture_ldap_schema); + torture_suite_add_simple_test(suite, "UPTODATEVECTOR", torture_ldap_uptodatevector); + + suite->description = talloc_strdup(suite, "LDAP and CLDAP tests"); + + torture_register_suite(suite); + + return NT_STATUS_OK; +} diff --git a/source4/torture/ldap/schema.c b/source4/torture/ldap/schema.c new file mode 100644 index 0000000000..8437e7f79d --- /dev/null +++ b/source4/torture/ldap/schema.c @@ -0,0 +1,397 @@ +/* + Unix SMB/CIFS mplementation. + 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/popt_common.h" +#include "ldb_wrap.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "dsdb/samdb/samdb.h" +#include "lib/util/dlinklist.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#include "param/param.h" + +struct test_rootDSE { + const char *defaultdn; + const char *rootdn; + const char *configdn; + const char *schemadn; +}; + +struct test_schema_ctx { + 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_dn_new(ldb, ldb, NULL), LDB_SCOPE_BASE, + NULL, NULL, &r); + 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_context *ldb, void *context, struct ldb_reply *ares) +{ + struct test_schema_ctx *actx = talloc_get_type(context, struct test_schema_ctx); + int ret = LDB_SUCCESS; + + switch (ares->type) { + case LDB_REPLY_ENTRY: + actx->count++; + ret = actx->callback(actx->private_data, 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; + } + } + break; + + default: + d_printf("%s: unknown Reply Type %u\n", __location__, ares->type); + return LDB_ERR_OTHER; + } + + if (talloc_free(ares) == -1) { + d_printf("talloc_free failed\n"); + actx->pending = 0; + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ret) { + return 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; + + req = talloc(ldb, struct ldb_request); + actx = talloc(req, struct test_schema_ctx); + + ctrl = talloc_array(req, 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; + + req->operation = LDB_SEARCH; + req->op.search.base = ldb_dn_new(req, ldb, root->schemadn); + req->op.search.scope = LDB_SCOPE_SUBTREE; + req->op.search.tree = ldb_parse_tree(req, filter); + if (req->op.search.tree == NULL) return -1; + req->op.search.attrs = NULL; + req->controls = ctrl; + req->context = actx; + req->callback = test_schema_search_callback; + ldb_set_timeout(ldb, req, 0); + + actx->count = 0; + actx->ctrl = control; + actx->callback = callback; + actx->private_data = private_data; +again: + actx->pending = false; + + ret = ldb_request(ldb, req); + if (ret != LDB_SUCCESS) { + d_printf("search failed - %s\n", ldb_errstring(ldb)); + return false; + } + + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + if (ret != LDB_SUCCESS) { + d_printf("search error - %s\n", ldb_errstring(ldb)); + return false; + } + + if (actx->pending) + goto again; + + d_printf("filter[%s] count[%u]\n", filter, actx->count); + 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); + struct dsdb_attribute *attr = NULL; + WERROR status; + + attr = talloc_zero(schema, struct dsdb_attribute); + if (!attr) { + goto failed; + } + + status = dsdb_attribute_from_ldb(schema, msg, attr, attr); + if (!W_ERROR_IS_OK(status)) { + goto failed; + } + + DLIST_ADD_END(schema->attributes, attr, struct dsdb_attribute *); + return LDB_SUCCESS; +failed: + talloc_free(attr); + 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); + struct dsdb_class *obj; + WERROR status; + + obj = talloc_zero(schema, struct dsdb_class); + if (!obj) { + goto failed; + } + + status = dsdb_class_from_ldb(schema, msg, obj, obj); + if (!W_ERROR_IS_OK(status)) { + goto failed; + } + + DLIST_ADD_END(schema->classes, obj, struct dsdb_class *); + 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(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; +} + +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, + cmdline_credentials, + 0, NULL); + 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); + +failed: + return ret; +} diff --git a/source4/torture/ldap/uptodatevector.c b/source4/torture/ldap/uptodatevector.c new file mode 100644 index 0000000000..87b7e09e13 --- /dev/null +++ b/source4/torture/ldap/uptodatevector.c @@ -0,0 +1,178 @@ +/* + Unix SMB/CIFS mplementation. + 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/popt_common.h" +#include "ldb_wrap.h" +#include "lib/ldb/include/ldb.h" +#include "lib/ldb/include/ldb_errors.h" +#include "dsdb/samdb/samdb.h" +#include "lib/util/dlinklist.h" + +#include "torture/torture.h" +#include "torture/ldap/proto.h" + +#include "librpc/ndr/libndr.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, partition_dn, LDB_SCOPE_BASE, + "(objectClass=*)", attrs, &r); + if (ret != LDB_SUCCESS) { + return false; + } else if (r->count != 1) { + talloc_free(r); + return false; + } + talloc_steal(torture, r); + + 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, + lp_iconv_convenience(torture->lp_ctx), &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, partition_dn, LDB_SCOPE_BASE, + "(objectClass=*)", attrs, &r); + if (ret != LDB_SUCCESS) { + return false; + } else if (r->count != 1) { + talloc_free(r); + return false; + } + talloc_steal(msg, r); + + 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, + lp_iconv_convenience(torture->lp_ctx), &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->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)samdb_result_uint64(r->msgs[0], "uSNChanged", 0), + samdb_result_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, + cmdline_credentials, + 0, NULL); + if (!ldb) goto failed; + + ret &= test_check_uptodatevector(torture, ldb, samdb_base_dn(ldb)); + ret &= test_check_uptodatevector(torture, ldb, samdb_config_dn(ldb)); + ret &= test_check_uptodatevector(torture, ldb, samdb_schema_dn(ldb)); + + return ret; +failed: + return false; +} |