diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source4/torture/ldap | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.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.c | 1004 | ||||
-rw-r--r-- | source4/torture/ldap/cldap.c | 179 | ||||
-rw-r--r-- | source4/torture/ldap/cldapbench.c | 233 | ||||
-rw-r--r-- | source4/torture/ldap/common.c | 135 | ||||
-rw-r--r-- | source4/torture/ldap/ldap_sort.c | 158 | ||||
-rw-r--r-- | source4/torture/ldap/nested_search.c | 206 | ||||
-rw-r--r-- | source4/torture/ldap/netlogon.c | 664 | ||||
-rw-r--r-- | source4/torture/ldap/schema.c | 408 | ||||
-rw-r--r-- | source4/torture/ldap/session_expiry.c | 122 | ||||
-rw-r--r-- | source4/torture/ldap/uptodatevector.c | 173 |
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; +} |