From 9f01e7af6b5f0ab8022baabf097af6be82f13877 Mon Sep 17 00:00:00 2001 From: Matthieu Patou Date: Mon, 10 May 2010 01:39:27 +0400 Subject: s4 torture: Add tests for dfs referrals handling in SMB/trans2 requests Signed-off-by: Stefan Metzmacher --- source4/torture/dfs/common.c | 71 ++++++ source4/torture/dfs/domaindfs.c | 488 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 559 insertions(+) create mode 100644 source4/torture/dfs/common.c create mode 100644 source4/torture/dfs/domaindfs.c (limited to 'source4/torture/dfs') diff --git a/source4/torture/dfs/common.c b/source4/torture/dfs/common.c new file mode 100644 index 0000000000..f1cc10663b --- /dev/null +++ b/source4/torture/dfs/common.c @@ -0,0 +1,71 @@ +/* + Unix SMB/CIFS implementation. + test suite for various Domain DFS + Copyright (C) Matthieu Patou 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 . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "../librpc/gen_ndr/ndr_dfsblobs.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" +#include "torture/torture.h" +#include "torture/dfs/proto.h" +#include "libcli/raw/raw_proto.h" + +NTSTATUS dfs_cli_do_call(struct smbcli_tree *tree, + struct dfs_GetDFSReferral *ref) +{ + NTSTATUS result; + enum ndr_err_code ndr_err; + uint16_t setup = TRANSACT2_GET_DFS_REFERRAL; + struct smb_trans2 trans; + + trans.in.max_param = 0; + trans.in.max_data = 4096; + trans.in.max_setup = 0; + trans.in.flags = 0; + trans.in.timeout = 0; + trans.in.setup_count = 1; + trans.in.setup = &setup; + trans.in.trans_name = NULL; + ZERO_STRUCT(trans.out); + + ndr_err = ndr_push_struct_blob(&trans.in.params, tree, + &ref->in.req, + (ndr_push_flags_fn_t)ndr_push_dfs_GetDFSReferral_in); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INTERNAL_ERROR; + } + trans.in.data = data_blob(NULL, 0); + + result = smb_raw_trans2(tree, tree, &trans); + + if (!NT_STATUS_IS_OK(result)) + return result; + + ndr_err = ndr_pull_struct_blob(&trans.out.data, tree, + ref->out.resp, + (ndr_pull_flags_fn_t)ndr_pull_dfs_referral_resp); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + return NT_STATUS_OK; +} + diff --git a/source4/torture/dfs/domaindfs.c b/source4/torture/dfs/domaindfs.c new file mode 100644 index 0000000000..18c84396d3 --- /dev/null +++ b/source4/torture/dfs/domaindfs.c @@ -0,0 +1,488 @@ +/* + Unix SMB/CIFS implementation. + test suite for various Domain DFS + Copyright (C) Matthieu Patou 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 . +*/ + +#include "includes.h" +#include "libcli/libcli.h" +#include "torture/smbtorture.h" +#include "torture/util.h" +#include "../librpc/gen_ndr/ndr_dfsblobs.h" +#include "librpc/ndr/libndr.h" +#include "param/param.h" +#include "torture/torture.h" +#include "torture/dfs/proto.h" + +static bool test_getdomainreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r; + struct dfs_referral_resp resp; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + torture_assert_int_equal(tctx, resp.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp.nb_referrals != 0, 1, + "0 domains referrals returned"); + torture_assert_int_equal(tctx, resp.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp.referral_entries[0].referral.v3.data.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + return true; +} + +static bool test_getdcreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + const char* str; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + str = resp.referral_entries[0].referral.v3.data.referrals.r2.special_name; + if( strchr(str, '.') == NULL ) { + str = resp.referral_entries[1].referral.v3.data.referrals.r2.special_name; + } + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + + torture_assert_int_equal(tctx, resp2.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp2.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp2.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp2.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp2.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp2.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.data.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp2.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + + r3.in.req.max_referral_level = 3; + /* + * Windows 7 and at least windows 2008 server sends domain.fqdn instead of \domain.fqdn + * (as it is specified in the spec) + * Let's check that we are able to support it too + */ + r3.in.req.servername = str; + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp3.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp3.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + return true; +} + +static bool test_getdcreferral_netbios(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + const char* str; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + + str = resp.referral_entries[0].referral.v3.data.referrals.r2.special_name; + if( strchr(str, '.') != NULL ) { + str = resp.referral_entries[1].referral.v3.data.referrals.r2.special_name; + } + + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp2.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp2.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp2.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp2.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp2.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp2.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp2.referral_entries[0].referral.v3.data.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp2.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + torture_assert(tctx, strchr( + resp2.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0],'.') == NULL, + "referral contains dots it's not a netbios name"); + + r3.in.req.max_referral_level = 3; + /* + * Windows 7 and at least windows 2008 server sends domain.fqdn instead of \domain.fqdn + * (as it is specified in the spec) + * Let's check that we are able to support it too + */ + r3.in.req.servername = str + 1; + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get DC Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 0, + "Path consumed not equal to 0"); + torture_assert_int_equal(tctx, resp3.nb_referrals , 1, + "We do not received only 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, 0, + "Header flag different it's not a referral server"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.entry_flags, + DFS_FLAG_REFERRAL_DOMAIN_RESP, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a domain response and got %d", + resp3.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first dc is less than 0"); + torture_assert(tctx, strchr( + resp3.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0],'.') == NULL, + "referral contains dots it's not a netbios name"); + return true; +} + +static bool test_getsysvolreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char* str; + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + str = resp.referral_entries[0].referral.v3.data.referrals.r2.special_name; + if( strchr(str, '.') == NULL ) { + str = resp.referral_entries[1].referral.v3.data.referrals.r2.special_name; + } + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = str; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + r3.in.req.max_referral_level = 3; + r3.in.req.servername = talloc_asprintf(tctx, "%s\\sysvol", str); + r3.out.resp = &resp3; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get sysvol Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.path_consumed, 2*strlen(r3.in.req.servername), + "Path consumed not equal to length of the request"); + torture_assert_int_equal(tctx, resp3.nb_referrals != 0, 1, + "We do not receive at least 1 referral"); + torture_assert_int_equal(tctx, resp3.header_flags, DFS_HEADER_FLAG_STORAGE_SVR, + "Header flag different it's not a referral for a storage"); + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 3, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 3", + resp3.referral_entries[0].version)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.server_type, + DFS_SERVER_NON_ROOT, + talloc_asprintf(tctx, + "Wrong server type, expected non root server and got %d", + resp3.referral_entries[0].referral.v3.data.server_type)); + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v3.data.entry_flags, + 0, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a non domain response and got %d", + resp3.referral_entries[0].referral.v3.data.entry_flags)); + torture_assert_int_equal(tctx, strlen( + resp3.referral_entries[0].referral.v3.data.referrals.r2.special_name) > 0, + 1, + "Length of domain is 0 or less"); + torture_assert_int_equal(tctx, strlen( + resp2.referral_entries[0].referral.v3.data.referrals.r2.expanded_names[0]) > 0, + 1, + "Length of first referral is less than 0"); + + r3.in.req.max_referral_level = 4; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r3), + "Get sysvol Domain referral failed"); + + torture_assert_int_equal(tctx, resp3.referral_entries[0].version, 4, + talloc_asprintf(tctx, + "Not expected version for referral entry 0 got %d expected 4", + resp3.referral_entries[0].version)); +#if 0 + /* + * We do not support fallback indication for the moment + */ + torture_assert_int_equal(tctx, resp3.header_flags, + DFS_HEADER_FLAG_STORAGE_SVR | DFS_HEADER_FLAG_TARGET_BCK, + "Header flag different it's not a referral for a storage with fallback"); +#endif + torture_assert_int_equal(tctx, resp3.referral_entries[0].referral.v4.entry_flags, + DFS_FLAG_REFERRAL_FIRST_TARGET_SET, + talloc_asprintf(tctx, + "Wrong entry flag expected to have a non domain response and got %d", + resp3.referral_entries[0].referral.v4.entry_flags)); + return true; +} + +static bool test_unknowndomain(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r, r2; + struct dfs_referral_resp resp, resp2; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = "foobar.none.net"; + r2.out.resp = &resp2; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r2), + NT_STATUS_INVALID_PARAMETER, + "Get DC Domain didn't return exptected error code"); + + return true; +} + +static bool test_getsysvolplusreferral(struct torture_context *tctx, + struct smbcli_state *cli) +{ + const char* str; + struct dfs_GetDFSReferral r, r2, r3; + struct dfs_referral_resp resp, resp2, resp3; + + r.in.req.max_referral_level = 3; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r), + "Get Domain referral failed"); + + r2.in.req.max_referral_level = 3; + r2.in.req.servername = resp.referral_entries[0].referral.v3.data.referrals.r2.special_name; + r2.out.resp = &resp2; + + torture_assert_ntstatus_ok(tctx, + dfs_cli_do_call(cli->tree, &r2), + "Get DC Domain referral failed"); + + str = resp2.referral_entries[0].referral.v3.data.referrals.r2.special_name; + r3.in.req.max_referral_level = 3; + r3.in.req.servername = talloc_asprintf(tctx, "%s\\sysvol\\foo", str); + r3.out.resp = &resp3; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r3), + NT_STATUS_NOT_FOUND, + "Bad behavior with subtree sysvol referral"); + + return true; +} + +static bool test_low_referral_level(struct torture_context *tctx, + struct smbcli_state *cli) +{ + struct dfs_GetDFSReferral r; + struct dfs_referral_resp resp; + + r.in.req.max_referral_level = 2; + r.in.req.servername = ""; + r.out.resp = &resp; + + torture_assert_ntstatus_equal(tctx, + dfs_cli_do_call(cli->tree, &r), + NT_STATUS_UNSUCCESSFUL, + "Unexpected STATUS for invalid deferral retquest"); + + return true; +} + +NTSTATUS torture_dfs_init(void) +{ + struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "DFS"); + struct torture_suite *suite_basic = torture_suite_create(suite, "DOMAIN"); + + torture_suite_add_suite(suite, suite_basic); + + torture_suite_add_1smb_test(suite_basic, "domain referral", + test_getdomainreferral); + torture_suite_add_1smb_test(suite_basic, "dc referral", + test_getdcreferral); + torture_suite_add_1smb_test(suite_basic, "dc referral netbios", + test_getdcreferral_netbios); + + torture_suite_add_1smb_test(suite_basic, "sysvol referral", + test_getsysvolreferral); + + /* Non standard case */ + + torture_suite_add_1smb_test(suite_basic, "dc referral on unknown domain", + test_unknowndomain); + torture_suite_add_1smb_test(suite_basic, "sysvol with subtree referral", + test_getsysvolplusreferral); + torture_suite_add_1smb_test(suite_basic, "referral with a level 2", + test_low_referral_level); + + /* + * test with invalid level + * test with netbios + */ + + suite->description = talloc_strdup(suite, "DFS referrals calls"); + + torture_register_suite(suite); + + return NT_STATUS_OK; +} -- cgit