diff options
Diffstat (limited to 'source4/torture/rpc')
-rw-r--r-- | source4/torture/rpc/eventlog.c | 8 | ||||
-rw-r--r-- | source4/torture/rpc/netlogon.c | 535 | ||||
-rw-r--r-- | source4/torture/rpc/ntsvcs.c | 169 | ||||
-rw-r--r-- | source4/torture/rpc/rpc.c | 2 | ||||
-rw-r--r-- | source4/torture/rpc/rpc.h | 3 | ||||
-rw-r--r-- | source4/torture/rpc/samba3rpc.c | 61 | ||||
-rw-r--r-- | source4/torture/rpc/samr.c | 1214 | ||||
-rw-r--r-- | source4/torture/rpc/samr_accessmask.c | 29 | ||||
-rw-r--r-- | source4/torture/rpc/samsync.c | 149 | ||||
-rw-r--r-- | source4/torture/rpc/schannel.c | 2 | ||||
-rw-r--r-- | source4/torture/rpc/svcctl.c | 178 | ||||
-rw-r--r-- | source4/torture/rpc/testjoin.c | 19 |
12 files changed, 2065 insertions, 304 deletions
diff --git a/source4/torture/rpc/eventlog.c b/source4/torture/rpc/eventlog.c index d5665ad07d..e89cdd3ea9 100644 --- a/source4/torture/rpc/eventlog.c +++ b/source4/torture/rpc/eventlog.c @@ -46,12 +46,12 @@ static bool get_policy_handle(struct torture_context *tctx, unknown0.unknown1 = 0x0001; r.in.unknown0 = &unknown0; - init_lsa_String(r.in.logname, "dns server"); - init_lsa_String(r.in.servername, NULL); + init_lsa_String(&logname, "dns server"); + init_lsa_String(&servername, NULL); r.in.logname = &logname; r.in.servername = &servername; - r.in.unknown2 = 0x00000001; - r.in.unknown3 = 0x00000001; + r.in.major_version = 0x00000001; + r.in.minor_version = 0x00000001; r.out.handle = handle; torture_assert_ntstatus_ok(tctx, diff --git a/source4/torture/rpc/netlogon.c b/source4/torture/rpc/netlogon.c index 96cab0bf02..953f9d126d 100644 --- a/source4/torture/rpc/netlogon.c +++ b/source4/torture/rpc/netlogon.c @@ -32,8 +32,10 @@ #include "../lib/crypto/crypto.h" #include "libcli/auth/libcli_auth.h" #include "librpc/gen_ndr/ndr_netlogon_c.h" +#include "librpc/gen_ndr/ndr_netlogon.h" #include "librpc/gen_ndr/ndr_lsa_c.h" #include "param/param.h" +#include "libcli/security/security.h" #define TEST_MACHINE_NAME "torturetest" @@ -831,6 +833,538 @@ static bool test_DatabaseDeltas(struct torture_context *tctx, return true; } +static bool test_DatabaseRedo(struct torture_context *tctx, + struct dcerpc_pipe *p, + struct cli_credentials *machine_credentials) +{ + NTSTATUS status; + struct netr_DatabaseRedo r; + struct creds_CredentialState *creds; + struct netr_Authenticator credential; + struct netr_Authenticator return_authenticator; + struct netr_DELTA_ENUM_ARRAY *delta_enum_array = NULL; + struct netr_ChangeLogEntry e; + struct dom_sid null_sid, *sid; + int i,d; + + ZERO_STRUCT(null_sid); + + sid = dom_sid_parse_talloc(tctx, "S-1-5-21-1111111111-2222222222-333333333-500"); + + { + + struct { + uint32_t rid; + uint16_t flags; + uint8_t db_index; + uint8_t delta_type; + struct dom_sid sid; + const char *name; + NTSTATUS expected_error; + uint32_t expected_num_results; + uint8_t expected_delta_type_1; + uint8_t expected_delta_type_2; + const char *comment; + } changes[] = { + + /* SAM_DATABASE_DOMAIN */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_DOMAIN, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NETR_DELTA_DOMAIN" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_USER, + .comment = "NETR_DELTA_USER by rid 500" + }, + { + .rid = DOMAIN_RID_GUEST, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_USER, + .comment = "NETR_DELTA_USER by rid 501" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = *sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by null_sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = "administrator", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by name 'administrator'" + }, + { + .rid = DOMAIN_RID_ADMINS, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_GROUP, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_GROUP, + .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER, + .comment = "NETR_DELTA_GROUP by rid 512" + }, + { + .rid = DOMAIN_RID_ADMINS, + .flags = 0, + .db_index = SAM_DATABASE_DOMAIN, + .delta_type = NETR_DELTA_GROUP_MEMBER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_GROUP, + .expected_delta_type_2 = NETR_DELTA_GROUP_MEMBER, + .comment = "NETR_DELTA_GROUP_MEMBER by rid 512" + }, + + + /* SAM_DATABASE_BUILTIN */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_DOMAIN, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NETR_DELTA_DOMAIN" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER by rid 500" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_USER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_USER, + .comment = "NETR_DELTA_USER" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS by rid 544" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS_MEMBER, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS_MEMBER by rid 544" + }, + { + .rid = 544, + .flags = 0, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA by rid 544" + }, + { + .rid = 544, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = 0, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DOMAIN, + .comment = "NULL DELTA by rid 544 sid S-1-5-32-544 and flags" + }, + { + .rid = 544, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 2, + .expected_delta_type_1 = NETR_DELTA_ALIAS, + .expected_delta_type_2 = NETR_DELTA_ALIAS_MEMBER, + .comment = "NETR_DELTA_ALIAS by rid 544 and sid S-1-5-32-544 and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_BUILTIN, + .delta_type = NETR_DELTA_ALIAS, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32-544"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_ALIAS, + .comment = "NETR_DELTA_ALIAS by sid S-1-5-32-544 and flags" + }, + + /* SAM_DATABASE_PRIVS */ + + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = 0, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_ACCESS_DENIED, + .expected_num_results = 0, + .comment = "NULL DELTA" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_MODIFY_COUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, + .expected_num_results = 0, + .comment = "NETR_DELTA_MODIFY_COUNT" + }, + { + .rid = 0, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY by null sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_POLICY, + .sid = *dom_sid_parse_talloc(tctx, "S-1-5-32"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_POLICY, + .comment = "NETR_DELTA_POLICY by sid S-1-5-32 and flags" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = 0, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = null_sid, + .name = NULL, + .expected_error = NT_STATUS_SYNCHRONIZATION_REQUIRED, /* strange */ + .expected_num_results = 0, + .comment = "NETR_DELTA_ACCOUNT by rid 500" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED | + NETR_CHANGELOG_IMMEDIATE_REPL_REQUIRED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and 2 flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_SID_INCLUDED | + NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *dom_sid_parse_talloc(tctx, "S-1-1-0"), + .name = NULL, + .expected_error = NT_STATUS_INVALID_PARAMETER, + .expected_num_results = 0, + .comment = "NETR_DELTA_ACCOUNT by sid S-1-1-0 and invalid flags" + }, + { + .rid = DOMAIN_RID_ADMINISTRATOR, + .flags = NETR_CHANGELOG_SID_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_ACCOUNT, + .sid = *sid, + .name = NULL, + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_ACCOUNT, + .comment = "NETR_DELTA_ACCOUNT by rid 500, sid and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_SECRET, + .sid = null_sid, + .name = "IsurelydontexistIhope", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_DELETE_SECRET, + .comment = "NETR_DELTA_SECRET by name 'IsurelydontexistIhope' and flags" + }, + { + .rid = 0, + .flags = NETR_CHANGELOG_NAME_INCLUDED, + .db_index = SAM_DATABASE_PRIVS, + .delta_type = NETR_DELTA_SECRET, + .sid = null_sid, + .name = "G$BCKUPKEY_P", + .expected_error = NT_STATUS_OK, + .expected_num_results = 1, + .expected_delta_type_1 = NETR_DELTA_SECRET, + .comment = "NETR_DELTA_SECRET by name 'G$BCKUPKEY_P' and flags" + } + }; + + ZERO_STRUCT(return_authenticator); + + r.in.logon_server = talloc_asprintf(tctx, "\\\\%s", dcerpc_server_name(p)); + r.in.computername = TEST_MACHINE_NAME; + r.in.return_authenticator = &return_authenticator; + r.out.return_authenticator = &return_authenticator; + r.out.delta_enum_array = &delta_enum_array; + + for (d=0; d<3; d++) { + + const char *database; + + switch (d) { + case 0: + database = "SAM"; + break; + case 1: + database = "BUILTIN"; + break; + case 2: + database = "LSA"; + break; + default: + break; + } + + torture_comment(tctx, "Testing DatabaseRedo\n"); + + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + + for (i=0;i<ARRAY_SIZE(changes);i++) { + + if (d != changes[i].db_index) { + continue; + } + + creds_client_authenticator(creds, &credential); + + r.in.credential = &credential; + + e.serial_number1 = 0; + e.serial_number2 = 0; + e.object_rid = changes[i].rid; + e.flags = changes[i].flags; + e.db_index = changes[i].db_index; + e.delta_type = changes[i].delta_type; + + switch (changes[i].flags & (NETR_CHANGELOG_NAME_INCLUDED | NETR_CHANGELOG_SID_INCLUDED)) { + case NETR_CHANGELOG_SID_INCLUDED: + e.object.object_sid = changes[i].sid; + break; + case NETR_CHANGELOG_NAME_INCLUDED: + e.object.object_name = changes[i].name; + break; + default: + break; + } + + r.in.change_log_entry = e; + + torture_comment(tctx, "Testing DatabaseRedo with database %s and %s\n", + database, changes[i].comment); + + status = dcerpc_netr_DatabaseRedo(p, tctx, &r); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + return true; + } + + torture_assert_ntstatus_equal(tctx, status, changes[i].expected_error, changes[i].comment); + if (delta_enum_array) { + torture_assert_int_equal(tctx, + delta_enum_array->num_deltas, + changes[i].expected_num_results, + changes[i].comment); + if (delta_enum_array->num_deltas > 0) { + torture_assert_int_equal(tctx, + delta_enum_array->delta_enum[0].delta_type, + changes[i].expected_delta_type_1, + changes[i].comment); + } + if (delta_enum_array->num_deltas > 1) { + torture_assert_int_equal(tctx, + delta_enum_array->delta_enum[1].delta_type, + changes[i].expected_delta_type_2, + changes[i].comment); + } + } + + if (!creds_client_check(creds, &return_authenticator.cred)) { + torture_comment(tctx, "Credential chaining failed\n"); + if (!test_SetupCredentials(p, tctx, machine_credentials, &creds)) { + return false; + } + } + } + } + } + + return true; +} /* try a netlogon AccountDeltas @@ -1761,6 +2295,7 @@ struct torture_suite *torture_rpc_netlogon(TALLOC_CTX *mem_ctx) torture_rpc_tcase_add_test_creds(tcase, "GetDomainInfo", test_GetDomainInfo); torture_rpc_tcase_add_test_creds(tcase, "DatabaseSync", test_DatabaseSync); torture_rpc_tcase_add_test_creds(tcase, "DatabaseDeltas", test_DatabaseDeltas); + torture_rpc_tcase_add_test_creds(tcase, "DatabaseRedo", test_DatabaseRedo); torture_rpc_tcase_add_test_creds(tcase, "AccountDeltas", test_AccountDeltas); torture_rpc_tcase_add_test_creds(tcase, "AccountSync", test_AccountSync); torture_rpc_tcase_add_test(tcase, "GetDcName", test_GetDcName); diff --git a/source4/torture/rpc/ntsvcs.c b/source4/torture/rpc/ntsvcs.c new file mode 100644 index 0000000000..5453102039 --- /dev/null +++ b/source4/torture/rpc/ntsvcs.c @@ -0,0 +1,169 @@ +/* + Unix SMB/CIFS implementation. + test suite for rpc ntsvcs operations + + Copyright (C) Guenther Deschner 2008 + + 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "lib/torture/torture.h" +#include "torture/rpc/rpc.h" +#include "librpc/gen_ndr/ndr_ntsvcs_c.h" +#include "torture/util.h" +#include "param/param.h" + +static bool test_PNP_GetVersion(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct PNP_GetVersion r; + uint16_t version = 0; + + r.out.version = &version; + + status = dcerpc_PNP_GetVersion(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "PNP_GetVersion"); + torture_assert_werr_ok(tctx, r.out.result, "PNP_GetVersion"); + torture_assert_int_equal(tctx, version, 0x400, "invalid version"); + + return true; +} + +static bool test_PNP_GetDeviceListSize(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct PNP_GetDeviceListSize r; + uint32_t size = 0; + + r.in.devicename = NULL; + r.in.flags = 0; + r.out.size = &size; + + status = dcerpc_PNP_GetDeviceListSize(p, tctx, &r); + + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceListSize"); + torture_assert_werr_ok(tctx, r.out.result, "PNP_GetDeviceListSize"); + + return true; +} + +static bool test_PNP_GetDeviceList(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct PNP_GetDeviceList r; + uint16_t *buffer = NULL; + uint32_t length = 0; + + buffer = talloc_array(tctx, uint16_t, 0); + + r.in.filter = NULL; + r.in.flags = 0; + r.in.length = &length; + r.out.length = &length; + r.out.buffer = buffer; + + status = dcerpc_PNP_GetDeviceList(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceList"); + + if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) { + struct PNP_GetDeviceListSize s; + + s.in.devicename = NULL; + s.in.flags = 0; + s.out.size = &length; + + status = dcerpc_PNP_GetDeviceListSize(p, tctx, &s); + + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceListSize"); + torture_assert_werr_ok(tctx, s.out.result, "PNP_GetDeviceListSize"); + } + + buffer = talloc_array(tctx, uint16_t, length); + + r.in.length = &length; + r.out.length = &length; + r.out.buffer = buffer; + + status = dcerpc_PNP_GetDeviceList(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceList"); + torture_assert_werr_ok(tctx, r.out.result, "PNP_GetDeviceList"); + + return true; +} + +static bool test_PNP_GetDeviceRegProp(struct torture_context *tctx, + struct dcerpc_pipe *p) +{ + NTSTATUS status; + struct PNP_GetDeviceRegProp r; + + enum winreg_Type reg_data_type = REG_NONE; + uint32_t buffer_size = 0; + uint32_t needed = 0; + uint8_t *buffer; + + buffer = talloc(tctx, uint8_t); + + r.in.devicepath = "ACPI\\ACPI0003\\1"; + r.in.property = DEV_REGPROP_DESC; + r.in.flags = 0; + r.in.reg_data_type = ®_data_type; + r.in.buffer_size = &buffer_size; + r.in.needed = &needed; + r.out.buffer = buffer; + r.out.reg_data_type = ®_data_type; + r.out.buffer_size = &buffer_size; + r.out.needed = &needed; + + status = dcerpc_PNP_GetDeviceRegProp(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp"); + + if (W_ERROR_EQUAL(r.out.result, WERR_CM_BUFFER_SMALL)) { + + buffer = talloc_array(tctx, uint8_t, needed); + r.in.buffer_size = &needed; + + status = dcerpc_PNP_GetDeviceRegProp(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "PNP_GetDeviceRegProp"); + } + + return true; +} + +struct torture_suite *torture_rpc_ntsvcs(TALLOC_CTX *mem_ctx) +{ + struct torture_rpc_tcase *tcase; + struct torture_suite *suite = torture_suite_create(mem_ctx, "NTSVCS"); + struct torture_test *test; + + tcase = torture_suite_add_rpc_iface_tcase(suite, "ntsvcs", + &ndr_table_ntsvcs); + + test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceRegProp", + test_PNP_GetDeviceRegProp); + test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceList", + test_PNP_GetDeviceList); + test = torture_rpc_tcase_add_test(tcase, "PNP_GetDeviceListSize", + test_PNP_GetDeviceListSize); + test = torture_rpc_tcase_add_test(tcase, "PNP_GetVersion", + test_PNP_GetVersion); + + return suite; +} diff --git a/source4/torture/rpc/rpc.c b/source4/torture/rpc/rpc.c index 2fcf700c36..7f6b06d000 100644 --- a/source4/torture/rpc/rpc.c +++ b/source4/torture/rpc/rpc.c @@ -395,6 +395,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_simple_test(suite, "SAMR", torture_rpc_samr); torture_suite_add_simple_test(suite, "SAMR-USERS", torture_rpc_samr_users); torture_suite_add_simple_test(suite, "SAMR-PASSWORDS", torture_rpc_samr_passwords); + torture_suite_add_simple_test(suite, "SAMR-PASSWORDS-PWDLASTSET", torture_rpc_samr_passwords_pwdlastset); torture_suite_add_suite(suite, torture_rpc_netlogon(suite)); torture_suite_add_suite(suite, torture_rpc_remote_pac(suite)); torture_suite_add_simple_test(suite, "SAMLOGON", torture_rpc_samlogon); @@ -438,6 +439,7 @@ NTSTATUS torture_rpc_init(void) torture_suite_add_simple_test(suite, "DSSYNC", torture_rpc_dssync); torture_suite_add_simple_test(suite, "BENCH-RPC", torture_bench_rpc); torture_suite_add_simple_test(suite, "ASYNCBIND", torture_async_bind); + torture_suite_add_suite(suite, torture_rpc_ntsvcs(suite)); suite->description = talloc_strdup(suite, "DCE/RPC protocol and interface tests"); diff --git a/source4/torture/rpc/rpc.h b/source4/torture/rpc/rpc.h index 29b1ebee54..9fd64f18b5 100644 --- a/source4/torture/rpc/rpc.h +++ b/source4/torture/rpc/rpc.h @@ -21,14 +21,13 @@ #ifndef __TORTURE_RPC_H__ #define __TORTURE_RPC_H__ -#include "torture/torture.h" +#include "lib/torture/torture.h" #include "auth/credentials/credentials.h" #include "torture/rpc/drsuapi.h" #include "libnet/libnet_join.h" #include "librpc/rpc/dcerpc.h" #include "libcli/raw/libcliraw.h" #include "torture/rpc/proto.h" -#include "torture/torture.h" struct torture_rpc_tcase { struct torture_tcase tcase; diff --git a/source4/torture/rpc/samba3rpc.c b/source4/torture/rpc/samba3rpc.c index c2b849127f..f1e7e5a367 100644 --- a/source4/torture/rpc/samba3rpc.c +++ b/source4/torture/rpc/samba3rpc.c @@ -355,7 +355,7 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, char **domain, struct dcerpc_pipe **result_pipe, struct policy_handle **result_handle, - struct dom_sid **sid) + struct dom_sid **sid_p) { struct dcerpc_pipe *samr_pipe; NTSTATUS status; @@ -365,7 +365,10 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, struct samr_Connect2 conn; struct samr_EnumDomains enumdom; uint32_t resume_handle = 0; + uint32_t num_entries = 0; + struct samr_SamArray *sam = NULL; struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; int dom_idx; struct lsa_String domain_name; struct lsa_String user_name; @@ -423,6 +426,8 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, enumdom.in.resume_handle = &resume_handle; enumdom.in.buf_size = (uint32_t)-1; enumdom.out.resume_handle = &resume_handle; + enumdom.out.num_entries = &num_entries; + enumdom.out.sam = &sam; status = dcerpc_samr_EnumDomains(samr_pipe, mem_ctx, &enumdom); if (!NT_STATUS_IS_OK(status)) { @@ -430,20 +435,21 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, goto fail; } - if (enumdom.out.num_entries != 2) { + if (*enumdom.out.num_entries != 2) { d_printf("samr_EnumDomains returned %d entries, expected 2\n", - enumdom.out.num_entries); + *enumdom.out.num_entries); status = NT_STATUS_UNSUCCESSFUL; goto fail; } - dom_idx = strequal(enumdom.out.sam->entries[0].name.string, + dom_idx = strequal(sam->entries[0].name.string, "builtin") ? 1:0; l.in.connect_handle = &conn_handle; - domain_name.string = enumdom.out.sam->entries[dom_idx].name.string; + domain_name.string = sam->entries[dom_idx].name.string; *domain = talloc_strdup(mem_ctx, domain_name.string); l.in.domain_name = &domain_name; + l.out.sid = &sid; status = dcerpc_samr_LookupDomain(samr_pipe, mem_ctx, &l); if (!NT_STATUS_IS_OK(status)) { @@ -453,7 +459,7 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, o.in.connect_handle = &conn_handle; o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - o.in.sid = l.out.sid; + o.in.sid = *l.out.sid; o.out.domain_handle = &domain_handle; status = dcerpc_samr_OpenDomain(samr_pipe, mem_ctx, &o); @@ -477,10 +483,13 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) { struct samr_LookupNames ln; struct samr_OpenUser ou; + struct samr_Ids rids, types; ln.in.domain_handle = &domain_handle; ln.in.num_names = 1; ln.in.names = &user_name; + ln.out.rids = &rids; + ln.out.types = &types; status = dcerpc_samr_LookupNames(samr_pipe, mem_ctx, &ln); if (!NT_STATUS_IS_OK(status)) { @@ -491,7 +500,7 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, ou.in.domain_handle = &domain_handle; ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - user_rid = ou.in.rid = ln.out.rids.ids[0]; + user_rid = ou.in.rid = ln.out.rids->ids[0]; ou.out.user_handle = user_handle; status = dcerpc_samr_OpenUser(samr_pipe, mem_ctx, &ou); @@ -509,8 +518,8 @@ static NTSTATUS get_usr_handle(struct smbcli_state *cli, *result_pipe = samr_pipe; *result_handle = user_handle; - if (sid != NULL) { - *sid = dom_sid_add_rid(mem_ctx, l.out.sid, user_rid); + if (sid_p != NULL) { + *sid_p = dom_sid_add_rid(mem_ctx, *l.out.sid, user_rid); } return NT_STATUS_OK; @@ -555,6 +564,7 @@ static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, struct samr_SetUserInfo sui; struct samr_QueryUserInfo qui; union samr_UserInfo u_info; + union samr_UserInfo *info; DATA_BLOB session_key; @@ -597,6 +607,7 @@ static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, qui.in.user_handle = wks_handle; qui.in.level = 21; + qui.out.info = &info; status = dcerpc_samr_QueryUserInfo(samr_pipe, tmp_ctx, &qui); if (!NT_STATUS_IS_OK(status)) { @@ -604,14 +615,14 @@ static bool create_user(TALLOC_CTX *mem_ctx, struct smbcli_state *cli, goto done; } - qui.out.info->info21.allow_password_change = 0; - qui.out.info->info21.force_password_change = 0; - qui.out.info->info21.account_name.string = NULL; - qui.out.info->info21.rid = 0; - qui.out.info->info21.acct_expiry = 0; - qui.out.info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */ + info->info21.allow_password_change = 0; + info->info21.force_password_change = 0; + info->info21.account_name.string = NULL; + info->info21.rid = 0; + info->info21.acct_expiry = 0; + info->info21.fields_present = 0x81827fa; /* copy usrmgr.exe */ - u_info.info21 = qui.out.info->info21; + u_info.info21 = info->info21; sui.in.user_handle = wks_handle; sui.in.info = &u_info; sui.in.level = 21; @@ -721,9 +732,11 @@ static bool join3(struct smbcli_state *cli, { struct samr_QueryUserInfo q; + union samr_UserInfo *info; q.in.user_handle = wks_handle; q.in.level = 21; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -732,7 +745,7 @@ static bool join3(struct smbcli_state *cli, goto done; } - last_password_change = q.out.info->info21.last_password_change; + last_password_change = info->info21.last_password_change; } cli_credentials_set_domain(wks_creds, dom_name, CRED_SPECIFIED); @@ -755,6 +768,10 @@ static bool join3(struct smbcli_state *cli, i21->acct_flags = ACB_WSTRUST; i21->fields_present = SAMR_FIELD_FULL_NAME | SAMR_FIELD_ACCT_FLAGS | SAMR_FIELD_PASSWORD; + /* this would break the test result expectations + i21->fields_present |= SAMR_FIELD_EXPIRED_FLAG; + i21->password_expired = 1; + */ encode_pw_buffer(u_info.info25.password.data, cli_credentials_get_password(wks_creds), @@ -795,8 +812,8 @@ static bool join3(struct smbcli_state *cli, encode_pw_buffer(u_info.info24.password.data, cli_credentials_get_password(wks_creds), STR_UNICODE); - u_info.info24.pw_len = - strlen_m(cli_credentials_get_password(wks_creds))*2; + /* just to make this test pass */ + u_info.info24.password_expired = 1; status = dcerpc_fetch_session_key(samr_pipe, &session_key); if (!NT_STATUS_IS_OK(status)) { @@ -830,9 +847,11 @@ static bool join3(struct smbcli_state *cli, { struct samr_QueryUserInfo q; + union samr_UserInfo *info; q.in.user_handle = wks_handle; q.in.level = 21; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(samr_pipe, mem_ctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -843,7 +862,7 @@ static bool join3(struct smbcli_state *cli, if (use_level25) { if (last_password_change - == q.out.info->info21.last_password_change) { + == info->info21.last_password_change) { d_printf("(%s) last_password_change unchanged " "during join, level25 must change " "it\n", __location__); @@ -852,7 +871,7 @@ static bool join3(struct smbcli_state *cli, } else { if (last_password_change - != q.out.info->info21.last_password_change) { + != info->info21.last_password_change) { d_printf("(%s) last_password_change changed " "during join, level24 doesn't " "change it\n", __location__); diff --git a/source4/torture/rpc/samr.c b/source4/torture/rpc/samr.c index 23c288bfcc..01ff01674c 100644 --- a/source4/torture/rpc/samr.c +++ b/source4/torture/rpc/samr.c @@ -29,6 +29,8 @@ #include "libcli/security/security.h" #include "torture/rpc/rpc.h" +#include <unistd.h> + #define TEST_ACCOUNT_NAME "samrtorturetest" #define TEST_ALIASNAME "samrtorturetestalias" #define TEST_GROUPNAME "samrtorturetestgroup" @@ -37,6 +39,7 @@ enum torture_samr_choice { TORTURE_SAMR_PASSWORDS, + TORTURE_SAMR_PASSWORDS_PWDLASTSET, TORTURE_SAMR_USER_ATTRIBUTES, TORTURE_SAMR_OTHER }; @@ -59,6 +62,13 @@ static void init_lsa_String(struct lsa_String *string, const char *s) string->string = s; } +static void init_lsa_BinaryString(struct lsa_BinaryString *string, const char *s, uint32_t length) +{ + string->length = length; + string->size = length; + string->array = (uint16_t *)discard_const(s); +} + bool test_samr_handle_Close(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle) { @@ -131,18 +141,20 @@ static bool test_QuerySecurity(struct dcerpc_pipe *p, NTSTATUS status; struct samr_QuerySecurity r; struct samr_SetSecurity s; + struct sec_desc_buf *sdbuf = NULL; r.in.handle = handle; r.in.sec_info = 7; + r.out.sdbuf = &sdbuf; status = dcerpc_samr_QuerySecurity(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "QuerySecurity"); - torture_assert(tctx, r.out.sdbuf != NULL, "sdbuf is NULL"); + torture_assert(tctx, sdbuf != NULL, "sdbuf is NULL"); s.in.handle = handle; s.in.sec_info = 7; - s.in.sdbuf = r.out.sdbuf; + s.in.sdbuf = sdbuf; if (torture_setting_bool(tctx, "samba4", false)) { torture_skip(tctx, "skipping SetSecurity test against Samba4\n"); @@ -168,6 +180,7 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx struct samr_QueryUserInfo q; struct samr_QueryUserInfo q0; union samr_UserInfo u; + union samr_UserInfo *info; bool ret = true; const char *test_account_name; @@ -184,7 +197,7 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx s2.in.info = &u; q.in.user_handle = handle; - q.out.info = &u; + q.out.info = &info; q0 = q; #define TESTCALL(call, r) \ @@ -204,6 +217,14 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx break; \ } +#define MEM_EQUAL(s1, s2, length, field) \ + if ((s1 && !s2) || (s2 && !s1) || memcmp(s1, s2, length)) { \ + torture_comment(tctx, "Failed to set %s to '%s' (%s)\n", \ + #field, (const char *)s2, __location__); \ + ret = false; \ + break; \ + } + #define INT_EQUAL(i1, i2, field) \ if (i1 != i2) { \ torture_comment(tctx, "Failed to set %s to 0x%llx - got 0x%llx (%s)\n", \ @@ -218,7 +239,7 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx TESTCALL(QueryUserInfo, q) \ s.in.level = lvl1; \ s2.in.level = lvl1; \ - u = *q.out.info; \ + u = *info; \ if (lvl1 == 21) { \ ZERO_STRUCT(u.info21); \ u.info21.fields_present = fpval; \ @@ -228,21 +249,45 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx TESTCALL(SetUserInfo2, s2) \ init_lsa_String(&u.info ## lvl1.field1, ""); \ TESTCALL(QueryUserInfo, q); \ - u = *q.out.info; \ + u = *info; \ STRING_EQUAL(u.info ## lvl1.field1.string, value, field1); \ q.in.level = lvl2; \ TESTCALL(QueryUserInfo, q) \ - u = *q.out.info; \ + u = *info; \ STRING_EQUAL(u.info ## lvl2.field2.string, value, field2); \ } while (0) +#define TEST_USERINFO_BINARYSTRING(lvl1, field1, lvl2, field2, value, fpval) do { \ + torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ + q.in.level = lvl1; \ + TESTCALL(QueryUserInfo, q) \ + s.in.level = lvl1; \ + s2.in.level = lvl1; \ + u = *info; \ + if (lvl1 == 21) { \ + ZERO_STRUCT(u.info21); \ + u.info21.fields_present = fpval; \ + } \ + init_lsa_BinaryString(&u.info ## lvl1.field1, value, strlen(value)); \ + TESTCALL(SetUserInfo, s) \ + TESTCALL(SetUserInfo2, s2) \ + init_lsa_BinaryString(&u.info ## lvl1.field1, "", 1); \ + TESTCALL(QueryUserInfo, q); \ + u = *info; \ + MEM_EQUAL(u.info ## lvl1.field1.array, value, strlen(value), field1); \ + q.in.level = lvl2; \ + TESTCALL(QueryUserInfo, q) \ + u = *info; \ + MEM_EQUAL(u.info ## lvl2.field2.array, value, strlen(value), field2); \ + } while (0) + #define TEST_USERINFO_INT_EXP(lvl1, field1, lvl2, field2, value, exp_value, fpval) do { \ torture_comment(tctx, "field test %d/%s vs %d/%s\n", lvl1, #field1, lvl2, #field2); \ q.in.level = lvl1; \ TESTCALL(QueryUserInfo, q) \ s.in.level = lvl1; \ s2.in.level = lvl1; \ - u = *q.out.info; \ + u = *info; \ if (lvl1 == 21) { \ uint8_t *bits = u.info21.logon_hours.bits; \ ZERO_STRUCT(u.info21); \ @@ -257,11 +302,11 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx TESTCALL(SetUserInfo2, s2) \ u.info ## lvl1.field1 = 0; \ TESTCALL(QueryUserInfo, q); \ - u = *q.out.info; \ + u = *info; \ INT_EQUAL(u.info ## lvl1.field1, exp_value, field1); \ q.in.level = lvl2; \ TESTCALL(QueryUserInfo, q) \ - u = *q.out.info; \ + u = *info; \ INT_EQUAL(u.info ## lvl2.field2, exp_value, field1); \ } while (0) @@ -359,10 +404,10 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx TEST_USERINFO_STRING(21, workstations, 14, workstations, "21workstation14", SAMR_FIELD_WORKSTATIONS); - TEST_USERINFO_STRING(20, parameters, 21, parameters, "xx20-21 parameters", 0); - TEST_USERINFO_STRING(21, parameters, 21, parameters, "xx21-21 parameters", + TEST_USERINFO_BINARYSTRING(20, parameters, 21, parameters, "xx20-21 parameters", 0); + TEST_USERINFO_BINARYSTRING(21, parameters, 21, parameters, "xx21-21 parameters", SAMR_FIELD_PARAMETERS); - TEST_USERINFO_STRING(21, parameters, 20, parameters, "xx21-20 parameters", + TEST_USERINFO_BINARYSTRING(21, parameters, 20, parameters, "xx21-20 parameters", SAMR_FIELD_PARAMETERS); TEST_USERINFO_INT(2, country_code, 2, country_code, __LINE__, 0); @@ -466,12 +511,19 @@ static bool test_SetUserInfo(struct dcerpc_pipe *p, struct torture_context *tctx /* generate a random password for password change tests */ -static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) +static char *samr_rand_pass_silent(TALLOC_CTX *mem_ctx, int min_len) { size_t len = MAX(8, min_len) + (random() % 6); char *s = generate_random_str(mem_ctx, len); + return s; +} + +static char *samr_rand_pass(TALLOC_CTX *mem_ctx, int min_len) +{ + char *s = samr_rand_pass_silent(mem_ctx, min_len); printf("Generated password '%s'\n", s); return s; + } /* @@ -512,12 +564,14 @@ static bool test_SetUserPass(struct dcerpc_pipe *p, struct torture_context *tctx DATA_BLOB session_key; char *newpass; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -526,8 +580,7 @@ static bool test_SetUserPass(struct dcerpc_pipe *p, struct torture_context *tctx s.in.level = 24; encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE); - /* w2k3 ignores this length */ - u.info24.pw_len = strlen_m(newpass) * 2; + u.info24.password_expired = 0; status = dcerpc_fetch_session_key(p, &session_key); if (!NT_STATUS_IS_OK(status)) { @@ -564,12 +617,14 @@ static bool test_SetUserPass_23(struct dcerpc_pipe *p, struct torture_context *t DATA_BLOB session_key; char *newpass; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -643,12 +698,14 @@ static bool test_SetUserPassEx(struct dcerpc_pipe *p, struct torture_context *tc char *newpass; struct MD5Context ctx; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } if (makeshort && policy_min_pw_len) { newpass = samr_rand_pass_fixed_len(tctx, policy_min_pw_len - 1); @@ -661,7 +718,7 @@ static bool test_SetUserPassEx(struct dcerpc_pipe *p, struct torture_context *tc s.in.level = 26; encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE); - u.info26.pw_len = strlen(newpass); + u.info26.password_expired = 0; status = dcerpc_fetch_session_key(p, &session_key); if (!NT_STATUS_IS_OK(status)) { @@ -725,12 +782,14 @@ static bool test_SetUserPass_25(struct dcerpc_pipe *p, struct torture_context *t uint8_t confounder[16]; char *newpass; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; int policy_min_pw_len = 0; pwp.in.user_handle = handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -790,12 +849,161 @@ static bool test_SetUserPass_25(struct dcerpc_pipe *p, struct torture_context *t return ret; } +static bool test_SetUserPass_level_ex(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint16_t level, + uint32_t fields_present, + char **password, uint8_t password_expired, + bool use_setinfo2, NTSTATUS expected_error) +{ + NTSTATUS status; + struct samr_SetUserInfo s; + struct samr_SetUserInfo2 s2; + union samr_UserInfo u; + bool ret = true; + DATA_BLOB session_key; + DATA_BLOB confounded_session_key = data_blob_talloc(tctx, NULL, 16); + struct MD5Context ctx; + uint8_t confounder[16]; + char *newpass; + struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; + int policy_min_pw_len = 0; + const char *comment = NULL; + + pwp.in.user_handle = handle; + pwp.out.info = &info; + + status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); + if (NT_STATUS_IS_OK(status)) { + policy_min_pw_len = pwp.out.info->min_password_length; + } + newpass = samr_rand_pass_silent(tctx, policy_min_pw_len); + + if (use_setinfo2) { + s2.in.user_handle = handle; + s2.in.info = &u; + s2.in.level = level; + } else { + s.in.user_handle = handle; + s.in.info = &u; + s.in.level = level; + } + + if (fields_present & SAMR_FIELD_COMMENT) { + comment = talloc_asprintf(tctx, "comment: %d\n", time(NULL)); + } + + ZERO_STRUCT(u); + + switch (level) { + case 21: + u.info21.fields_present = fields_present; + u.info21.password_expired = password_expired; + u.info21.comment.string = comment; + + break; + case 23: + u.info23.info.fields_present = fields_present; + u.info23.info.password_expired = password_expired; + u.info23.info.comment.string = comment; + + encode_pw_buffer(u.info23.password.data, newpass, STR_UNICODE); + + break; + case 24: + u.info24.password_expired = password_expired; + + encode_pw_buffer(u.info24.password.data, newpass, STR_UNICODE); + + break; + case 25: + u.info25.info.fields_present = fields_present; + u.info25.info.password_expired = password_expired; + u.info25.info.comment.string = comment; + + encode_pw_buffer(u.info25.password.data, newpass, STR_UNICODE); + + break; + case 26: + u.info26.password_expired = password_expired; + + encode_pw_buffer(u.info26.password.data, newpass, STR_UNICODE); + + break; + } + + status = dcerpc_fetch_session_key(p, &session_key); + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo level %u - no session key - %s\n", + s.in.level, nt_errstr(status)); + return false; + } + + generate_random_buffer((uint8_t *)confounder, 16); + + MD5Init(&ctx); + MD5Update(&ctx, confounder, 16); + MD5Update(&ctx, session_key.data, session_key.length); + MD5Final(confounded_session_key.data, &ctx); + + switch (level) { + case 23: + arcfour_crypt_blob(u.info23.password.data, 516, &session_key); + break; + case 24: + arcfour_crypt_blob(u.info24.password.data, 516, &session_key); + break; + case 25: + arcfour_crypt_blob(u.info25.password.data, 516, &confounded_session_key); + memcpy(&u.info25.password.data[516], confounder, 16); + break; + case 26: + arcfour_crypt_blob(u.info26.password.data, 516, &confounded_session_key); + memcpy(&u.info26.password.data[516], confounder, 16); + break; + } + + if (use_setinfo2) { + status = dcerpc_samr_SetUserInfo2(p, tctx, &s2); + } else { + status = dcerpc_samr_SetUserInfo(p, tctx, &s); + } + + if (!NT_STATUS_IS_OK(expected_error)) { + if (use_setinfo2) { + torture_assert_ntstatus_equal(tctx, + s2.out.result, + expected_error, "SetUserInfo2 failed"); + } else { + torture_assert_ntstatus_equal(tctx, + s.out.result, + expected_error, "SetUserInfo failed"); + } + return true; + } + + if (!NT_STATUS_IS_OK(status)) { + printf("SetUserInfo%s level %u failed - %s\n", + use_setinfo2 ? "2":"", level, nt_errstr(status)); + ret = false; + } else { + if (level != 21) { + *password = newpass; + } + } + + return ret; +} + static bool test_SetAliasInfo(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *handle) { NTSTATUS status; struct samr_SetAliasInfo r; struct samr_QueryAliasInfo q; + union samr_AliasInfo *info; uint16_t levels[] = {2, 3}; int i; bool ret = true; @@ -826,6 +1034,7 @@ static bool test_SetAliasInfo(struct dcerpc_pipe *p, struct torture_context *tct q.in.alias_handle = handle; q.in.level = levels[i]; + q.out.info = &info; status = dcerpc_samr_QueryAliasInfo(p, tctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -842,11 +1051,13 @@ static bool test_GetGroupsForUser(struct dcerpc_pipe *p, struct torture_context struct policy_handle *user_handle) { struct samr_GetGroupsForUser r; + struct samr_RidWithAttributeArray *rids = NULL; NTSTATUS status; torture_comment(tctx, "testing GetGroupsForUser\n"); r.in.user_handle = user_handle; + r.out.rids = &rids; status = dcerpc_samr_GetGroupsForUser(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "GetGroupsForUser"); @@ -860,8 +1071,11 @@ static bool test_GetDomPwInfo(struct dcerpc_pipe *p, struct torture_context *tct { NTSTATUS status; struct samr_GetDomPwInfo r; + struct samr_PwInfo info; r.in.domain_name = domain_name; + r.out.info = &info; + torture_comment(tctx, "Testing GetDomPwInfo with name %s\n", r.in.domain_name->string); status = dcerpc_samr_GetDomPwInfo(p, tctx, &r); @@ -893,10 +1107,12 @@ static bool test_GetUserPwInfo(struct dcerpc_pipe *p, struct torture_context *tc { NTSTATUS status; struct samr_GetUserPwInfo r; + struct samr_PwInfo info; torture_comment(tctx, "Testing GetUserPwInfo\n"); r.in.user_handle = handle; + r.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "GetUserPwInfo"); @@ -911,15 +1127,18 @@ static NTSTATUS test_LookupName(struct dcerpc_pipe *p, struct torture_context *t NTSTATUS status; struct samr_LookupNames n; struct lsa_String sname[2]; + struct samr_Ids rids, types; init_lsa_String(&sname[0], name); n.in.domain_handle = domain_handle; n.in.num_names = 1; n.in.names = sname; + n.out.rids = &rids; + n.out.types = &types; status = dcerpc_samr_LookupNames(p, tctx, &n); if (NT_STATUS_IS_OK(status)) { - *rid = n.out.rids.ids[0]; + *rid = n.out.rids->ids[0]; } else { return status; } @@ -1071,6 +1290,7 @@ static bool test_ChangePasswordUser(struct dcerpc_pipe *p, struct torture_contex char *newpass; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; int policy_min_pw_len = 0; status = test_OpenUser_byname(p, tctx, handle, acct_name, &user_handle); @@ -1078,10 +1298,11 @@ static bool test_ChangePasswordUser(struct dcerpc_pipe *p, struct torture_contex return false; } pwp.in.user_handle = &user_handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(p, tctx, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -1346,12 +1567,14 @@ static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, struct torture_co uint8_t old_lm_hash[16], new_lm_hash[16]; struct samr_GetDomPwInfo dom_pw_info; + struct samr_PwInfo info; int policy_min_pw_len = 0; struct lsa_String domain_name; domain_name.string = ""; dom_pw_info.in.domain_name = &domain_name; + dom_pw_info.out.info = &info; torture_comment(tctx, "Testing OemChangePasswordUser2\n"); @@ -1362,7 +1585,7 @@ static bool test_OemChangePasswordUser2(struct dcerpc_pipe *p, struct torture_co status = dcerpc_samr_GetDomPwInfo(p, tctx, &dom_pw_info); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = dom_pw_info.out.info.min_password_length; + policy_min_pw_len = dom_pw_info.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -1515,11 +1738,13 @@ static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, struct torture_conte uint8_t old_lm_hash[16], new_lm_hash[16]; struct samr_GetDomPwInfo dom_pw_info; + struct samr_PwInfo info; struct lsa_String domain_name; domain_name.string = ""; dom_pw_info.in.domain_name = &domain_name; + dom_pw_info.out.info = &info; torture_comment(tctx, "Testing ChangePasswordUser2 on %s\n", acct_name); @@ -1531,7 +1756,7 @@ static bool test_ChangePasswordUser2(struct dcerpc_pipe *p, struct torture_conte int policy_min_pw_len = 0; status = dcerpc_samr_GetDomPwInfo(p, tctx, &dom_pw_info); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = dom_pw_info.out.info.min_password_length; + policy_min_pw_len = dom_pw_info.out.info->min_password_length; } newpass = samr_rand_pass(tctx, policy_min_pw_len); @@ -1594,6 +1819,8 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tct uint8_t old_nt_hash[16], new_nt_hash[16]; uint8_t old_lm_hash[16], new_lm_hash[16]; NTTIME t; + struct samr_DomInfo1 *dominfo = NULL; + struct samr_ChangeReject *reject = NULL; torture_comment(tctx, "Testing ChangePasswordUser3\n"); @@ -1641,6 +1868,8 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tct r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r); if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && @@ -1670,6 +1899,8 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tct r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r); if (!NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) && @@ -1712,21 +1943,23 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tct r.in.lm_password = &lm_pass; r.in.lm_verifier = &lm_verifier; r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r); if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION) - && r.out.dominfo - && r.out.reject + && dominfo + && reject && handle_reject_reason - && (!null_nttime(last_password_change) || !r.out.dominfo->min_password_age)) { - if (r.out.dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) { + && (!null_nttime(last_password_change) || !dominfo->min_password_age)) { + if (dominfo->password_properties & DOMAIN_REFUSE_PASSWORD_CHANGE ) { - if (r.out.reject && (r.out.reject->reason != SAMR_REJECT_OTHER)) { + if (reject && (reject->reason != SAMR_REJECT_OTHER)) { printf("expected SAMR_REJECT_OTHER (%d), got %d\n", - SAMR_REJECT_OTHER, r.out.reject->reason); + SAMR_REJECT_OTHER, reject->reason); return false; } } @@ -1740,54 +1973,54 @@ bool test_ChangePasswordUser3(struct dcerpc_pipe *p, struct torture_context *tct Guenther */ - if ((r.out.dominfo->min_password_age > 0) && !null_nttime(last_password_change) && - (last_password_change + r.out.dominfo->min_password_age > t)) { + if ((dominfo->min_password_age > 0) && !null_nttime(last_password_change) && + (last_password_change + dominfo->min_password_age > t)) { - if (r.out.reject->reason != SAMR_REJECT_OTHER) { + if (reject->reason != SAMR_REJECT_OTHER) { printf("expected SAMR_REJECT_OTHER (%d), got %d\n", - SAMR_REJECT_OTHER, r.out.reject->reason); + SAMR_REJECT_OTHER, reject->reason); return false; } - } else if ((r.out.dominfo->min_password_length > 0) && - (strlen(newpass) < r.out.dominfo->min_password_length)) { + } else if ((dominfo->min_password_length > 0) && + (strlen(newpass) < dominfo->min_password_length)) { - if (r.out.reject->reason != SAMR_REJECT_TOO_SHORT) { + if (reject->reason != SAMR_REJECT_TOO_SHORT) { printf("expected SAMR_REJECT_TOO_SHORT (%d), got %d\n", - SAMR_REJECT_TOO_SHORT, r.out.reject->reason); + SAMR_REJECT_TOO_SHORT, reject->reason); return false; } - } else if ((r.out.dominfo->password_history_length > 0) && + } else if ((dominfo->password_history_length > 0) && strequal(oldpass, newpass)) { - if (r.out.reject->reason != SAMR_REJECT_IN_HISTORY) { + if (reject->reason != SAMR_REJECT_IN_HISTORY) { printf("expected SAMR_REJECT_IN_HISTORY (%d), got %d\n", - SAMR_REJECT_IN_HISTORY, r.out.reject->reason); + SAMR_REJECT_IN_HISTORY, reject->reason); return false; } - } else if (r.out.dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) { + } else if (dominfo->password_properties & DOMAIN_PASSWORD_COMPLEX) { - if (r.out.reject->reason != SAMR_REJECT_COMPLEXITY) { + if (reject->reason != SAMR_REJECT_COMPLEXITY) { printf("expected SAMR_REJECT_COMPLEXITY (%d), got %d\n", - SAMR_REJECT_COMPLEXITY, r.out.reject->reason); + SAMR_REJECT_COMPLEXITY, reject->reason); return false; } } - if (r.out.reject->reason == SAMR_REJECT_TOO_SHORT) { + if (reject->reason == SAMR_REJECT_TOO_SHORT) { /* retry with adjusted size */ return test_ChangePasswordUser3(p, tctx, account_string, - r.out.dominfo->min_password_length, + dominfo->min_password_length, password, NULL, 0, false); } } else if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { - if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) { + if (reject && reject->reason != SAMR_REJECT_OTHER) { printf("expected SAMR_REJECT_OTHER (%d), got %d\n", - SAMR_REJECT_OTHER, r.out.reject->reason); + SAMR_REJECT_OTHER, reject->reason); return false; } /* Perhaps the server has a 'min password age' set? */ @@ -1823,6 +2056,8 @@ bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_contex char *oldpass; uint8_t old_nt_hash[16], new_nt_hash[16]; NTTIME t; + struct samr_DomInfo1 *dominfo = NULL; + struct samr_ChangeReject *reject = NULL; new_random_pass = samr_very_rand_pass(tctx, 128); @@ -1889,15 +2124,17 @@ bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_contex r.in.lm_password = NULL; r.in.lm_verifier = NULL; r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r); if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { - if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) { + if (reject && reject->reason != SAMR_REJECT_OTHER) { printf("expected SAMR_REJECT_OTHER (%d), got %d\n", - SAMR_REJECT_OTHER, r.out.reject->reason); + SAMR_REJECT_OTHER, reject->reason); return false; } /* Perhaps the server has a 'min password age' set? */ @@ -1925,15 +2162,17 @@ bool test_ChangePasswordRandomBytes(struct dcerpc_pipe *p, struct torture_contex r.in.lm_password = NULL; r.in.lm_verifier = NULL; r.in.password3 = NULL; + r.out.dominfo = &dominfo; + r.out.reject = &reject; unix_to_nt_time(&t, time(NULL)); status = dcerpc_samr_ChangePasswordUser3(p, tctx, &r); if (NT_STATUS_EQUAL(status, NT_STATUS_PASSWORD_RESTRICTION)) { - if (r.out.reject && r.out.reject->reason != SAMR_REJECT_OTHER) { + if (reject && reject->reason != SAMR_REJECT_OTHER) { printf("expected SAMR_REJECT_OTHER (%d), got %d\n", - SAMR_REJECT_OTHER, r.out.reject->reason); + SAMR_REJECT_OTHER, reject->reason); return false; } /* Perhaps the server has a 'min password age' set? */ @@ -2051,6 +2290,486 @@ static bool test_TestPrivateFunctionsUser(struct dcerpc_pipe *p, struct torture_ return true; } +static bool test_QueryUserInfo_pwdlastset(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + bool use_info2, + NTTIME *pwdlastset) +{ + NTSTATUS status; + uint16_t levels[] = { /* 3, */ 5, 21 }; + int i; + NTTIME pwdlastset3 = 0; + NTTIME pwdlastset5 = 0; + NTTIME pwdlastset21 = 0; + + torture_comment(tctx, "Testing QueryUserInfo%s level 5 and 21 call ", + use_info2 ? "2":""); + + for (i=0; i<ARRAY_SIZE(levels); i++) { + + struct samr_QueryUserInfo r; + struct samr_QueryUserInfo2 r2; + union samr_UserInfo *info; + + if (use_info2) { + r2.in.user_handle = handle; + r2.in.level = levels[i]; + r2.out.info = &info; + status = dcerpc_samr_QueryUserInfo2(p, tctx, &r2); + + } else { + r.in.user_handle = handle; + r.in.level = levels[i]; + r.out.info = &info; + status = dcerpc_samr_QueryUserInfo(p, tctx, &r); + } + + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_INVALID_INFO_CLASS)) { + printf("QueryUserInfo%s level %u failed - %s\n", + use_info2 ? "2":"", levels[i], nt_errstr(status)); + return false; + } + + switch (levels[i]) { + case 3: + pwdlastset3 = info->info3.last_password_change; + break; + case 5: + pwdlastset5 = info->info5.last_password_change; + break; + case 21: + pwdlastset21 = info->info21.last_password_change; + break; + default: + return false; + } + } + /* torture_assert_int_equal(tctx, pwdlastset3, pwdlastset5, + "pwdlastset mixup"); */ + torture_assert_int_equal(tctx, pwdlastset5, pwdlastset21, + "pwdlastset mixup"); + + *pwdlastset = pwdlastset21; + + torture_comment(tctx, "(pwdlastset: %lld)\n", *pwdlastset); + + return true; +} + +static bool test_SetPassword_level(struct dcerpc_pipe *p, + struct torture_context *tctx, + struct policy_handle *handle, + uint16_t level, + uint32_t fields_present, + uint8_t password_expired, + NTSTATUS expected_error, + bool use_setinfo2, + char **password, + bool use_queryinfo2, + NTTIME *pwdlastset) +{ + const char *fields = NULL; + bool ret = true; + + switch (level) { + case 21: + case 23: + case 25: + fields = talloc_asprintf(tctx, "(fields_present: 0x%08x)", + fields_present); + break; + default: + break; + } + + torture_comment(tctx, "Testing SetUserInfo%s level %d call " + "(password_expired: %d) %s\n", + use_setinfo2 ? "2":"", level, password_expired, + fields ? fields : ""); + + switch (level) { + case 21: + case 23: + case 24: + case 25: + case 26: + if (!test_SetUserPass_level_ex(p, tctx, handle, level, + fields_present, + password, + password_expired, + use_setinfo2, + expected_error)) { + ret = false; + } + break; + default: + return false; + } + + if (!test_QueryUserInfo_pwdlastset(p, tctx, handle, + use_queryinfo2, + pwdlastset)) { + ret = false; + } + + return ret; +} + +static bool test_SetPassword_pwdlastset(struct dcerpc_pipe *p, + struct torture_context *tctx, + uint32_t acct_flags, + struct policy_handle *handle, + char **password) +{ + int i, s = 0, q = 0; + bool ret = true; + int delay = 500000; + bool set_levels[] = { false, true }; + bool query_levels[] = { false, true }; + + struct { + uint16_t level; + uint8_t password_expired_nonzero; + uint32_t fields_present; + bool query_info2; + bool set_info2; + NTSTATUS set_error; + } pwd_tests[] = { + + /* level 21 */ + { + .level = 21, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_EXPIRED_FLAG + },{ + .level = 21, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_LAST_PWD_CHANGE, + .set_error = NT_STATUS_ACCESS_DENIED + },{ + .level = 21, + .password_expired_nonzero = 1, + .fields_present = 0, + .set_error = NT_STATUS_INVALID_PARAMETER + },{ + .level = 21, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_COMMENT, + +#if 0 + /* FIXME */ + },{ + .level = 21, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2 | + SAMR_FIELD_LAST_PWD_CHANGE, + .query_info2 = false, + .set_error = NT_STATUS_ACCESS_DENIED +#endif + + /* level 23 */ + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_EXPIRED_FLAG + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_LAST_PWD_CHANGE, + .set_error = NT_STATUS_ACCESS_DENIED + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_LAST_PWD_CHANGE | + SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2, + .set_error = NT_STATUS_ACCESS_DENIED + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_LAST_PWD_CHANGE | + SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2 | + SAMR_FIELD_EXPIRED_FLAG, + .set_error = NT_STATUS_ACCESS_DENIED + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2 | + SAMR_FIELD_EXPIRED_FLAG + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2, + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_COMMENT, + },{ + .level = 23, + .password_expired_nonzero = 1, + .fields_present = 0, + .set_error = NT_STATUS_INVALID_PARAMETER + },{ + + /* level 24 */ + + .level = 24, + .password_expired_nonzero = 1 + },{ + .level = 24, + .password_expired_nonzero = 24 + },{ + + /* level 25 */ + + .level = 25, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_LAST_PWD_CHANGE, + .set_error = NT_STATUS_ACCESS_DENIED + },{ + .level = 25, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_EXPIRED_FLAG, + },{ + .level = 25, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2 | + SAMR_FIELD_EXPIRED_FLAG + },{ + .level = 25, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_PASSWORD | + SAMR_FIELD_PASSWORD2, + },{ + .level = 25, + .password_expired_nonzero = 1, + .fields_present = SAMR_FIELD_COMMENT, + },{ + + /* level 26 */ + + .level = 26, + .password_expired_nonzero = 1 + },{ + .level = 26, + .password_expired_nonzero = 24 + } + }; + + if (torture_setting_bool(tctx, "samba3", false)) { + delay = 1000000; + printf("Samba3 has second granularity, setting delay to: %d\n", + delay); + } + + /* set to 1 to enable testing for all possible opcode + (SetUserInfo, SetUserInfo2, QueryUserInfo, QueryUserInfo2) + combinations */ +#if 0 +#define TEST_SET_LEVELS 1 +#define TEST_QUERY_LEVELS 1 +#endif + for (i=0; i<ARRAY_SIZE(pwd_tests); i++) { +#ifdef TEST_SET_LEVELS + for (s=0; s<ARRAY_SIZE(set_levels); s++) { +#endif +#ifdef TEST_QUERY_LEVELS + for (q=0; q<ARRAY_SIZE(query_levels); q++) { +#endif + NTTIME pwdlastset_old = 0; + NTTIME pwdlastset_new = 0; + + torture_comment(tctx, "------------------------------\n" + "Testing pwdLastSet attribute for flags: 0x%08x " + "(s: %d (l: %d), q: %d)\n", + acct_flags, s, pwd_tests[i].level, q); + + /* set #1 */ + + /* set a password and force password change (pwdlastset 0) by + * setting the password expired flag to a non-0 value */ + + if (!test_SetPassword_level(p, tctx, handle, + pwd_tests[i].level, + pwd_tests[i].fields_present, + pwd_tests[i].password_expired_nonzero, + pwd_tests[i].set_error, + set_levels[s], + password, + query_levels[q], + &pwdlastset_old)) { + ret = false; + } + + if (!NT_STATUS_IS_OK(pwd_tests[i].set_error)) { + /* skipping on expected failure */ + continue; + } + + /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25 + * set without the SAMR_FIELD_EXPIRED_FLAG */ + + switch (pwd_tests[i].level) { + case 21: + case 23: + case 25: + if ((pwdlastset_new != 0) && + !(pwd_tests[i].fields_present & SAMR_FIELD_EXPIRED_FLAG)) { + torture_comment(tctx, "not considering a non-0 " + "pwdLastSet as a an error as the " + "SAMR_FIELD_EXPIRED_FLAG has not " + "been set\n"); + break; + } + default: + if (pwdlastset_new != 0) { + torture_warning(tctx, "pwdLastSet test failed: " + "expected pwdLastSet 0 but got %lld\n", + pwdlastset_old); + ret = false; + } + break; + } + + usleep(delay); + + /* set #2 */ + + /* set a password, pwdlastset needs to get updated (increased + * value), password_expired value used here is 0 */ + + if (!test_SetPassword_level(p, tctx, handle, pwd_tests[i].level, + pwd_tests[i].fields_present, + 0, + pwd_tests[i].set_error, + set_levels[s], + password, + query_levels[q], + &pwdlastset_new)) { + + ret = false; + } + + /* when a password has been changed, pwdlastset must not be 0 afterwards + * and must be larger then the old value */ + + switch (pwd_tests[i].level) { + case 21: + case 23: + case 25: + + /* SAMR_FIELD_EXPIRED_FLAG has not been set and no + * password has been changed, old and new pwdlastset + * need to be the same value */ + + if (!(pwd_tests[i].fields_present & SAMR_FIELD_EXPIRED_FLAG) && + !((pwd_tests[i].fields_present & SAMR_FIELD_PASSWORD) || + (pwd_tests[i].fields_present & SAMR_FIELD_PASSWORD2))) + { + torture_assert_int_equal(tctx, pwdlastset_old, + pwdlastset_new, "pwdlastset must be equal"); + break; + } + default: + if (pwdlastset_old >= pwdlastset_new) { + torture_warning(tctx, "pwdLastSet test failed: " + "expected last pwdlastset (%lld) < new pwdlastset (%lld)\n", + pwdlastset_old, pwdlastset_new); + ret = false; + } + if (pwdlastset_new == 0) { + torture_warning(tctx, "pwdLastSet test failed: " + "expected non-0 pwdlastset, got: %lld\n", + pwdlastset_new); + ret = false; + } + } + + pwdlastset_old = pwdlastset_new; + + usleep(delay); + + /* set #3 */ + + /* set a password and force password change (pwdlastset 0) by + * setting the password expired flag to a non-0 value */ + + if (!test_SetPassword_level(p, tctx, handle, pwd_tests[i].level, + pwd_tests[i].fields_present, + pwd_tests[i].password_expired_nonzero, + pwd_tests[i].set_error, + set_levels[s], + password, + query_levels[q], + &pwdlastset_new)) { + ret = false; + } + + /* pwdlastset must be 0 afterwards, except for a level 21, 23 and 25 + * set without the SAMR_FIELD_EXPIRED_FLAG */ + + switch (pwd_tests[i].level) { + case 21: + case 23: + case 25: + if ((pwdlastset_new != 0) && + !(pwd_tests[i].fields_present & SAMR_FIELD_EXPIRED_FLAG)) { + torture_comment(tctx, "not considering a non-0 " + "pwdLastSet as a an error as the " + "SAMR_FIELD_EXPIRED_FLAG has not " + "been set\n"); + break; + } + + /* SAMR_FIELD_EXPIRED_FLAG has not been set and no + * password has been changed, old and new pwdlastset + * need to be the same value */ + + if (!(pwd_tests[i].fields_present & SAMR_FIELD_EXPIRED_FLAG) && + !((pwd_tests[i].fields_present & SAMR_FIELD_PASSWORD) || + (pwd_tests[i].fields_present & SAMR_FIELD_PASSWORD2))) + { + torture_assert_int_equal(tctx, pwdlastset_old, + pwdlastset_new, "pwdlastset must be equal"); + break; + } + default: + + if (pwdlastset_old == pwdlastset_new) { + torture_warning(tctx, "pwdLastSet test failed: " + "expected last pwdlastset (%lld) != new pwdlastset (%lld)\n", + pwdlastset_old, pwdlastset_new); + ret = false; + } + + if (pwdlastset_new != 0) { + torture_warning(tctx, "pwdLastSet test failed: " + "expected pwdLastSet 0, got %lld\n", + pwdlastset_old); + ret = false; + } + break; + } +#ifdef TEST_QUERY_LEVELS + } +#endif +#ifdef TEST_SET_LEVELS + } +#endif + } + +#undef TEST_SET_LEVELS +#undef TEST_QUERY_LEVELS + + return ret; +} static bool test_user_ops(struct dcerpc_pipe *p, struct torture_context *tctx, @@ -2061,6 +2780,7 @@ static bool test_user_ops(struct dcerpc_pipe *p, { char *password = NULL; struct samr_QueryUserInfo q; + union samr_UserInfo *info; NTSTATUS status; bool ret = true; @@ -2143,7 +2863,7 @@ static bool test_user_ops(struct dcerpc_pipe *p, ret = false; } } - + for (i = 0; password_fields[i]; i++) { if (!test_SetUserPass_23(p, tctx, user_handle, password_fields[i], &password)) { ret = false; @@ -2176,6 +2896,7 @@ static bool test_user_ops(struct dcerpc_pipe *p, q.in.user_handle = user_handle; q.in.level = 5; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, tctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -2184,20 +2905,37 @@ static bool test_user_ops(struct dcerpc_pipe *p, ret = false; } else { uint32_t expected_flags = (base_acct_flags | ACB_PWNOTREQ | ACB_DISABLED); - if ((q.out.info->info5.acct_flags) != expected_flags) { + if ((info->info5.acct_flags) != expected_flags) { printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", - q.out.info->info5.acct_flags, + info->info5.acct_flags, expected_flags); ret = false; } - if (q.out.info->info5.rid != rid) { + if (info->info5.rid != rid) { printf("QuerUserInfo level 5 failed, it returned %u when we expected rid of %u\n", - q.out.info->info5.rid, rid); + info->info5.rid, rid); } } break; + + case TORTURE_SAMR_PASSWORDS_PWDLASTSET: + + /* test last password change timestamp behaviour */ + if (!test_SetPassword_pwdlastset(p, tctx, base_acct_flags, + user_handle, &password)) { + ret = false; + } + + if (ret == true) { + torture_comment(tctx, "pwdLastSet test succeeded\n"); + } else { + torture_warning(tctx, "pwdLastSet test failed\n"); + } + + break; + case TORTURE_SAMR_OTHER: /* We just need the account to exist */ break; @@ -2481,6 +3219,7 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, /* set samr_SetDomainInfo level 1 with min_length 5 */ { struct samr_QueryDomainInfo r; + union samr_DomainInfo *info = NULL; struct samr_SetDomainInfo s; uint16_t len_old, len; uint32_t pwd_prop_old; @@ -2491,6 +3230,7 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.domain_handle = domain_handle; r.in.level = 1; + r.out.info = &info; printf("testing samr_QueryDomainInfo level 1\n"); status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &r); @@ -2500,7 +3240,7 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, s.in.domain_handle = domain_handle; s.in.level = 1; - s.in.info = r.out.info; + s.in.info = info; /* remember the old min length, so we can reset it */ len_old = s.in.info->info1.min_password_length; @@ -2540,13 +3280,17 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, NTSTATUS status; struct samr_OpenUser r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct samr_LookupNames n; struct policy_handle user_handle; + struct samr_Ids rids, types; n.in.domain_handle = domain_handle; n.in.num_names = 1; n.in.names = talloc_array(mem_ctx, struct lsa_String, 1); n.in.names[0].string = acct_name; + n.out.rids = &rids; + n.out.types = &types; status = dcerpc_samr_LookupNames(p, mem_ctx, &n); if (!NT_STATUS_IS_OK(status)) { @@ -2556,17 +3300,18 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.domain_handle = domain_handle; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - r.in.rid = n.out.rids.ids[0]; + r.in.rid = n.out.rids->ids[0]; r.out.user_handle = &user_handle; status = dcerpc_samr_OpenUser(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { - printf("OpenUser(%u) failed - %s\n", n.out.rids.ids[0], nt_errstr(status)); + printf("OpenUser(%u) failed - %s\n", n.out.rids->ids[0], nt_errstr(status)); return false; } q.in.user_handle = &user_handle; q.in.level = 5; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -2577,7 +3322,7 @@ static bool test_ChangePassword(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, printf("calling test_ChangePasswordUser3 with too early password change\n"); if (!test_ChangePasswordUser3(p, mem_ctx, acct_name, 0, password, NULL, - q.out.info->info5.last_password_change, true)) { + info->info5.last_password_change, true)) { ret = false; } } @@ -2607,6 +3352,7 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, NTSTATUS status; struct samr_CreateUser r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct samr_DeleteUser d; uint32_t rid; @@ -2654,6 +3400,7 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, } else { q.in.user_handle = &user_handle; q.in.level = 16; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -2661,9 +3408,9 @@ static bool test_CreateUser(struct dcerpc_pipe *p, struct torture_context *tctx, q.in.level, nt_errstr(status)); ret = false; } else { - if ((q.out.info->info16.acct_flags & acct_flags) != acct_flags) { + if ((info->info16.acct_flags & acct_flags) != acct_flags) { printf("QuerUserInfo level 16 failed, it returned 0x%08x when we expected flags of 0x%08x\n", - q.out.info->info16.acct_flags, + info->info16.acct_flags, acct_flags); ret = false; } @@ -2705,6 +3452,7 @@ static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx NTSTATUS status; struct samr_CreateUser2 r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct samr_DeleteUser d; struct policy_handle user_handle; uint32_t rid; @@ -2783,6 +3531,7 @@ static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx if (NT_STATUS_IS_OK(status)) { q.in.user_handle = &user_handle; q.in.level = 5; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, user_ctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -2794,31 +3543,31 @@ static bool test_CreateUser2(struct dcerpc_pipe *p, struct torture_context *tctx if (acct_flags == ACB_NORMAL) { expected_flags |= ACB_PW_EXPIRED; } - if ((q.out.info->info5.acct_flags) != expected_flags) { + if ((info->info5.acct_flags) != expected_flags) { printf("QuerUserInfo level 5 failed, it returned 0x%08x when we expected flags of 0x%08x\n", - q.out.info->info5.acct_flags, + info->info5.acct_flags, expected_flags); ret = false; } switch (acct_flags) { case ACB_SVRTRUST: - if (q.out.info->info5.primary_gid != DOMAIN_RID_DCS) { + if (info->info5.primary_gid != DOMAIN_RID_DCS) { printf("QuerUserInfo level 5: DC should have had Primary Group %d, got %d\n", - DOMAIN_RID_DCS, q.out.info->info5.primary_gid); + DOMAIN_RID_DCS, info->info5.primary_gid); ret = false; } break; case ACB_WSTRUST: - if (q.out.info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) { + if (info->info5.primary_gid != DOMAIN_RID_DOMAIN_MEMBERS) { printf("QuerUserInfo level 5: Domain Member should have had Primary Group %d, got %d\n", - DOMAIN_RID_DOMAIN_MEMBERS, q.out.info->info5.primary_gid); + DOMAIN_RID_DOMAIN_MEMBERS, info->info5.primary_gid); ret = false; } break; case ACB_NORMAL: - if (q.out.info->info5.primary_gid != DOMAIN_RID_USERS) { + if (info->info5.primary_gid != DOMAIN_RID_USERS) { printf("QuerUserInfo level 5: Users should have had Primary Group %d, got %d\n", - DOMAIN_RID_USERS, q.out.info->info5.primary_gid); + DOMAIN_RID_USERS, info->info5.primary_gid); ret = false; } break; @@ -2852,6 +3601,7 @@ static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryAliasInfo r; + union samr_AliasInfo *info; uint16_t levels[] = {1, 2, 3}; int i; bool ret = true; @@ -2861,6 +3611,7 @@ static bool test_QueryAliasInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.alias_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryAliasInfo(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -2878,6 +3629,7 @@ static bool test_QueryGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryGroupInfo r; + union samr_GroupInfo *info; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; bool ret = true; @@ -2887,6 +3639,7 @@ static bool test_QueryGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.group_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -2904,11 +3657,13 @@ static bool test_QueryGroupMember(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryGroupMember r; + struct samr_RidTypeArray *rids = NULL; bool ret = true; printf("Testing QueryGroupMember\n"); r.in.group_handle = handle; + r.out.rids = &rids; status = dcerpc_samr_QueryGroupMember(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -2925,6 +3680,7 @@ static bool test_SetGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryGroupInfo r; + union samr_GroupInfo *info; struct samr_SetGroupInfo s; uint16_t levels[] = {1, 2, 3, 4}; uint16_t set_ok[] = {0, 1, 1, 1}; @@ -2936,6 +3692,7 @@ static bool test_SetGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.group_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryGroupInfo(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -2948,7 +3705,7 @@ static bool test_SetGroupInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, s.in.group_handle = handle; s.in.level = levels[i]; - s.in.info = r.out.info; + s.in.info = *r.out.info; #if 0 /* disabled this, as it changes the name only from the point of view of samr, @@ -2990,6 +3747,7 @@ static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryUserInfo r; + union samr_UserInfo *info; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20, 21}; int i; @@ -3000,6 +3758,7 @@ static bool test_QueryUserInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.user_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3017,6 +3776,7 @@ static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, { NTSTATUS status; struct samr_QueryUserInfo2 r; + union samr_UserInfo *info; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 20, 21}; int i; @@ -3027,6 +3787,7 @@ static bool test_QueryUserInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.user_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryUserInfo2(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3174,6 +3935,7 @@ static bool check_mask(struct dcerpc_pipe *p, struct torture_context *tctx, NTSTATUS status; struct samr_OpenUser r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct policy_handle user_handle; bool ret = true; @@ -3192,6 +3954,7 @@ static bool check_mask(struct dcerpc_pipe *p, struct torture_context *tctx, q.in.user_handle = &user_handle; q.in.level = 16; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, tctx, &q); if (!NT_STATUS_IS_OK(status)) { @@ -3199,9 +3962,9 @@ static bool check_mask(struct dcerpc_pipe *p, struct torture_context *tctx, nt_errstr(status)); ret = false; } else { - if ((acct_flag_mask & q.out.info->info16.acct_flags) == 0) { + if ((acct_flag_mask & info->info16.acct_flags) == 0) { printf("Server failed to filter for 0x%x, allowed 0x%x (%d) on EnumDomainUsers\n", - acct_flag_mask, q.out.info->info16.acct_flags, rid); + acct_flag_mask, info->info16.acct_flags, rid); ret = false; } } @@ -3223,6 +3986,11 @@ static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context * bool ret = true; struct samr_LookupNames n; struct samr_LookupRids lr ; + struct lsa_Strings names; + struct samr_Ids rids, types; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; + uint32_t masks[] = {ACB_NORMAL, ACB_DOMTRUST, ACB_WSTRUST, ACB_DISABLED, ACB_NORMAL | ACB_DISABLED, ACB_SVRTRUST | ACB_DOMTRUST | ACB_WSTRUST, @@ -3236,6 +4004,8 @@ static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context * r.in.acct_flags = mask = masks[mask_idx]; r.in.max_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; + r.out.num_entries = &num_entries; + r.out.sam = &sam; status = dcerpc_samr_EnumDomainUsers(p, tctx, &r); if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && @@ -3244,18 +4014,18 @@ static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context * return false; } - torture_assert(tctx, r.out.sam, "EnumDomainUsers failed: r.out.sam unexpectedly NULL"); + torture_assert(tctx, sam, "EnumDomainUsers failed: r.out.sam unexpectedly NULL"); - if (r.out.sam->count == 0) { + if (sam->count == 0) { continue; } - for (i=0;i<r.out.sam->count;i++) { + for (i=0;i<sam->count;i++) { if (mask) { - if (!check_mask(p, tctx, handle, r.out.sam->entries[i].idx, mask)) { + if (!check_mask(p, tctx, handle, sam->entries[i].idx, mask)) { ret = false; } - } else if (!test_OpenUser(p, tctx, handle, r.out.sam->entries[i].idx)) { + } else if (!test_OpenUser(p, tctx, handle, sam->entries[i].idx)) { ret = false; } } @@ -3263,10 +4033,12 @@ static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context * printf("Testing LookupNames\n"); n.in.domain_handle = handle; - n.in.num_names = r.out.sam->count; - n.in.names = talloc_array(tctx, struct lsa_String, r.out.sam->count); - for (i=0;i<r.out.sam->count;i++) { - n.in.names[i].string = r.out.sam->entries[i].name.string; + n.in.num_names = sam->count; + n.in.names = talloc_array(tctx, struct lsa_String, sam->count); + n.out.rids = &rids; + n.out.types = &types; + for (i=0;i<sam->count;i++) { + n.in.names[i].string = sam->entries[i].name.string; } status = dcerpc_samr_LookupNames(p, tctx, &n); if (!NT_STATUS_IS_OK(status)) { @@ -3277,10 +4049,12 @@ static bool test_EnumDomainUsers(struct dcerpc_pipe *p, struct torture_context * printf("Testing LookupRids\n"); lr.in.domain_handle = handle; - lr.in.num_rids = r.out.sam->count; - lr.in.rids = talloc_array(tctx, uint32_t, r.out.sam->count); - for (i=0;i<r.out.sam->count;i++) { - lr.in.rids[i] = r.out.sam->entries[i].idx; + lr.in.num_rids = sam->count; + lr.in.rids = talloc_array(tctx, uint32_t, sam->count); + lr.out.names = &names; + lr.out.types = &types; + for (i=0;i<sam->count;i++) { + lr.in.rids[i] = sam->entries[i].idx; } status = dcerpc_samr_LookupRids(p, tctx, &lr); torture_assert_ntstatus_ok(tctx, status, "LookupRids"); @@ -3337,6 +4111,8 @@ static bool test_EnumDomainGroups(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, NTSTATUS status; struct samr_EnumDomainGroups r; uint32_t resume_handle=0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; int i; bool ret = true; @@ -3346,6 +4122,8 @@ static bool test_EnumDomainGroups(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.resume_handle = &resume_handle; r.in.max_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; + r.out.num_entries = &num_entries; + r.out.sam = &sam; status = dcerpc_samr_EnumDomainGroups(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3353,12 +4131,12 @@ static bool test_EnumDomainGroups(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, return false; } - if (!r.out.sam) { + if (!sam) { return false; } - for (i=0;i<r.out.sam->count;i++) { - if (!test_OpenGroup(p, mem_ctx, handle, r.out.sam->entries[i].idx)) { + for (i=0;i<sam->count;i++) { + if (!test_OpenGroup(p, mem_ctx, handle, sam->entries[i].idx)) { ret = false; } } @@ -3372,6 +4150,8 @@ static bool test_EnumDomainAliases(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, NTSTATUS status; struct samr_EnumDomainAliases r; uint32_t resume_handle=0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; int i; bool ret = true; @@ -3379,7 +4159,9 @@ static bool test_EnumDomainAliases(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.domain_handle = handle; r.in.resume_handle = &resume_handle; - r.in.acct_flags = (uint32_t)-1; + r.in.max_size = (uint32_t)-1; + r.out.sam = &sam; + r.out.num_entries = &num_entries; r.out.resume_handle = &resume_handle; status = dcerpc_samr_EnumDomainAliases(p, mem_ctx, &r); @@ -3388,12 +4170,12 @@ static bool test_EnumDomainAliases(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, return false; } - if (!r.out.sam) { + if (!sam) { return false; } - for (i=0;i<r.out.sam->count;i++) { - if (!test_OpenAlias(p, mem_ctx, handle, r.out.sam->entries[i].idx)) { + for (i=0;i<sam->count;i++) { + if (!test_OpenAlias(p, mem_ctx, handle, sam->entries[i].idx)) { ret = false; } } @@ -3409,14 +4191,19 @@ static bool test_GetDisplayEnumerationIndex(struct dcerpc_pipe *p, TALLOC_CTX *m bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; + struct lsa_String name; + uint32_t idx = 0; int i; for (i=0;i<ARRAY_SIZE(levels);i++) { printf("Testing GetDisplayEnumerationIndex level %u\n", levels[i]); + init_lsa_String(&name, TEST_ACCOUNT_NAME); + r.in.domain_handle = handle; r.in.level = levels[i]; - init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME); + r.in.name = &name; + r.out.idx = &idx; status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r); @@ -3428,7 +4215,7 @@ static bool test_GetDisplayEnumerationIndex(struct dcerpc_pipe *p, TALLOC_CTX *m ret = false; } - init_lsa_String(&r.in.name, "zzzzzzzz"); + init_lsa_String(&name, "zzzzzzzz"); status = dcerpc_samr_GetDisplayEnumerationIndex(p, mem_ctx, &r); @@ -3450,14 +4237,19 @@ static bool test_GetDisplayEnumerationIndex2(struct dcerpc_pipe *p, TALLOC_CTX * bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; uint16_t ok_lvl[] = {1, 1, 1, 0, 0}; + struct lsa_String name; + uint32_t idx = 0; int i; for (i=0;i<ARRAY_SIZE(levels);i++) { printf("Testing GetDisplayEnumerationIndex2 level %u\n", levels[i]); + init_lsa_String(&name, TEST_ACCOUNT_NAME); + r.in.domain_handle = handle; r.in.level = levels[i]; - init_lsa_String(&r.in.name, TEST_ACCOUNT_NAME); + r.in.name = &name; + r.out.idx = &idx; status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r); if (ok_lvl[i] && @@ -3468,7 +4260,7 @@ static bool test_GetDisplayEnumerationIndex2(struct dcerpc_pipe *p, TALLOC_CTX * ret = false; } - init_lsa_String(&r.in.name, "zzzzzzzz"); + init_lsa_String(&name, "zzzzzzzz"); status = dcerpc_samr_GetDisplayEnumerationIndex2(p, mem_ctx, &r); if (ok_lvl[i] && !NT_STATUS_EQUAL(NT_STATUS_NO_MORE_ENTRIES, status)) { @@ -3502,6 +4294,7 @@ static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ct { struct samr_OpenUser r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct policy_handle user_handle; int i, ret = true; NTSTATUS status; @@ -3510,16 +4303,16 @@ static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ct for (i = 0; ; i++) { switch (querydisplayinfo->in.level) { case 1: - if (i >= querydisplayinfo->out.info.info1.count) { + if (i >= querydisplayinfo->out.info->info1.count) { return ret; } - r.in.rid = querydisplayinfo->out.info.info1.entries[i].rid; + r.in.rid = querydisplayinfo->out.info->info1.entries[i].rid; break; case 2: - if (i >= querydisplayinfo->out.info.info2.count) { + if (i >= querydisplayinfo->out.info->info2.count) { return ret; } - r.in.rid = querydisplayinfo->out.info.info2.entries[i].rid; + r.in.rid = querydisplayinfo->out.info->info2.entries[i].rid; break; case 3: /* Groups */ @@ -3543,6 +4336,7 @@ static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ct q.in.user_handle = &user_handle; q.in.level = 21; + q.out.info = &info; status = dcerpc_samr_QueryUserInfo(p, mem_ctx, &q); if (!NT_STATUS_IS_OK(status)) { printf("QueryUserInfo(%u) failed - %s\n", r.in.rid, nt_errstr(status)); @@ -3551,41 +4345,41 @@ static bool test_each_DisplayInfo_user(struct dcerpc_pipe *p, TALLOC_CTX *mem_ct switch (querydisplayinfo->in.level) { case 1: - if (seen_testuser && strcmp(q.out.info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) { + if (seen_testuser && strcmp(info->info21.account_name.string, TEST_ACCOUNT_NAME) == 0) { *seen_testuser = true; } - STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].full_name, - q.out.info->info21.full_name, q.out.info->info21.account_name); - STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].account_name, - q.out.info->info21.account_name, q.out.info->info21.account_name); - STRING_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].description, - q.out.info->info21.description, q.out.info->info21.account_name); - INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].rid, - q.out.info->info21.rid, q.out.info->info21.account_name); - INT_EQUAL_QUERY(querydisplayinfo->out.info.info1.entries[i].acct_flags, - q.out.info->info21.acct_flags, q.out.info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].full_name, + info->info21.full_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].account_name, + info->info21.account_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].description, + info->info21.description, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].rid, + info->info21.rid, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info1.entries[i].acct_flags, + info->info21.acct_flags, info->info21.account_name); break; case 2: - STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].account_name, - q.out.info->info21.account_name, q.out.info->info21.account_name); - STRING_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].description, - q.out.info->info21.description, q.out.info->info21.account_name); - INT_EQUAL_QUERY(querydisplayinfo->out.info.info2.entries[i].rid, - q.out.info->info21.rid, q.out.info->info21.account_name); - INT_EQUAL_QUERY((querydisplayinfo->out.info.info2.entries[i].acct_flags & ~ACB_NORMAL), - q.out.info->info21.acct_flags, q.out.info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].account_name, + info->info21.account_name, info->info21.account_name); + STRING_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].description, + info->info21.description, info->info21.account_name); + INT_EQUAL_QUERY(querydisplayinfo->out.info->info2.entries[i].rid, + info->info21.rid, info->info21.account_name); + INT_EQUAL_QUERY((querydisplayinfo->out.info->info2.entries[i].acct_flags & ~ACB_NORMAL), + info->info21.acct_flags, info->info21.account_name); - if (!(querydisplayinfo->out.info.info2.entries[i].acct_flags & ACB_NORMAL)) { + if (!(querydisplayinfo->out.info->info2.entries[i].acct_flags & ACB_NORMAL)) { printf("Missing ACB_NORMAL in querydisplayinfo->out.info.info2.entries[i].acct_flags on %s\n", - q.out.info->info21.account_name.string); + info->info21.account_name.string); } - if (!(q.out.info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) { + if (!(info->info21.acct_flags & (ACB_WSTRUST | ACB_SVRTRUST))) { printf("Found non-trust account %s in trust account listing: 0x%x 0x%x\n", - q.out.info->info21.account_name.string, - querydisplayinfo->out.info.info2.entries[i].acct_flags, - q.out.info->info21.acct_flags); + info->info21.account_name.string, + querydisplayinfo->out.info->info2.entries[i].acct_flags, + info->info21.acct_flags); return false; } @@ -3605,10 +4399,15 @@ static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, NTSTATUS status; struct samr_QueryDisplayInfo r; struct samr_QueryDomainInfo dom_info; + union samr_DomainInfo *info = NULL; bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; bool seen_testuser = false; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo disp_info; + for (i=0;i<ARRAY_SIZE(levels);i++) { printf("Testing QueryDisplayInfo level %u\n", levels[i]); @@ -3620,6 +4419,9 @@ static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.level = levels[i]; r.in.max_entries = 2; r.in.buf_size = (uint32_t)-1; + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &disp_info; status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r); if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) && !NT_STATUS_IS_OK(status)) { @@ -3632,27 +4434,29 @@ static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, if (!test_each_DisplayInfo_user(p, mem_ctx, &r, &seen_testuser)) { ret = false; } - r.in.start_idx += r.out.info.info1.count; + r.in.start_idx += r.out.info->info1.count; break; case 2: if (!test_each_DisplayInfo_user(p, mem_ctx, &r, NULL)) { ret = false; } - r.in.start_idx += r.out.info.info2.count; + r.in.start_idx += r.out.info->info2.count; break; case 3: - r.in.start_idx += r.out.info.info3.count; + r.in.start_idx += r.out.info->info3.count; break; case 4: - r.in.start_idx += r.out.info.info4.count; + r.in.start_idx += r.out.info->info4.count; break; case 5: - r.in.start_idx += r.out.info.info5.count; + r.in.start_idx += r.out.info->info5.count; break; } } dom_info.in.domain_handle = handle; dom_info.in.level = 2; + dom_info.out.info = &info; + /* Check number of users returned is correct */ status = dcerpc_samr_QueryDomainInfo(p, mem_ctx, &dom_info); if (!NT_STATUS_IS_OK(status)) { @@ -3664,17 +4468,17 @@ static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, switch (r.in.level) { case 1: case 4: - if (dom_info.out.info->general.num_users < r.in.start_idx) { + if (info->general.num_users < r.in.start_idx) { printf("QueryDomainInfo indicates that QueryDisplayInfo returned more users (%d/%d) than the domain %s is said to contain!\n", - r.in.start_idx, dom_info.out.info->general.num_groups, - dom_info.out.info->general.domain_name.string); + r.in.start_idx, info->general.num_groups, + info->general.domain_name.string); ret = false; } if (!seen_testuser) { struct policy_handle user_handle; if (NT_STATUS_IS_OK(test_OpenUser_byname(p, mem_ctx, handle, TEST_ACCOUNT_NAME, &user_handle))) { printf("Didn't find test user " TEST_ACCOUNT_NAME " in enumeration of %s\n", - dom_info.out.info->general.domain_name.string); + info->general.domain_name.string); ret = false; test_samr_handle_Close(p, mem_ctx, &user_handle); } @@ -3682,10 +4486,10 @@ static bool test_QueryDisplayInfo(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, break; case 3: case 5: - if (dom_info.out.info->general.num_groups != r.in.start_idx) { + if (info->general.num_groups != r.in.start_idx) { printf("QueryDomainInfo indicates that QueryDisplayInfo didn't return all (%d/%d) the groups in %s\n", - r.in.start_idx, dom_info.out.info->general.num_groups, - dom_info.out.info->general.domain_name.string); + r.in.start_idx, info->general.num_groups, + info->general.domain_name.string); ret = false; } @@ -3705,6 +4509,9 @@ static bool test_QueryDisplayInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; for (i=0;i<ARRAY_SIZE(levels);i++) { printf("Testing QueryDisplayInfo2 level %u\n", levels[i]); @@ -3714,6 +4521,9 @@ static bool test_QueryDisplayInfo2(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, r.in.start_idx = 0; r.in.max_entries = 1000; r.in.buf_size = (uint32_t)-1; + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; status = dcerpc_samr_QueryDisplayInfo2(p, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3734,6 +4544,9 @@ static bool test_QueryDisplayInfo3(struct dcerpc_pipe *p, struct torture_context bool ret = true; uint16_t levels[] = {1, 2, 3, 4, 5}; int i; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; for (i=0;i<ARRAY_SIZE(levels);i++) { torture_comment(tctx, "Testing QueryDisplayInfo3 level %u\n", levels[i]); @@ -3743,6 +4556,9 @@ static bool test_QueryDisplayInfo3(struct dcerpc_pipe *p, struct torture_context r.in.start_idx = 0; r.in.max_entries = 1000; r.in.buf_size = (uint32_t)-1; + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; status = dcerpc_samr_QueryDisplayInfo3(p, tctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3762,6 +4578,9 @@ static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p, TALLOC_CTX *me NTSTATUS status; struct samr_QueryDisplayInfo r; bool ret = true; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; printf("Testing QueryDisplayInfo continuation\n"); @@ -3770,14 +4589,17 @@ static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p, TALLOC_CTX *me r.in.start_idx = 0; r.in.max_entries = 1; r.in.buf_size = (uint32_t)-1; + r.out.total_size = &total_size; + r.out.returned_size = &returned_size; + r.out.info = &info; do { status = dcerpc_samr_QueryDisplayInfo(p, mem_ctx, &r); - if (NT_STATUS_IS_OK(status) && r.out.returned_size != 0) { - if (r.out.info.info1.entries[0].idx != r.in.start_idx + 1) { + if (NT_STATUS_IS_OK(status) && *r.out.returned_size != 0) { + if (r.out.info->info1.entries[0].idx != r.in.start_idx + 1) { printf("expected idx %d but got %d\n", r.in.start_idx + 1, - r.out.info.info1.entries[0].idx); + r.out.info->info1.entries[0].idx); break; } } @@ -3791,7 +4613,7 @@ static bool test_QueryDisplayInfo_continue(struct dcerpc_pipe *p, TALLOC_CTX *me r.in.start_idx++; } while ((NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || NT_STATUS_IS_OK(status)) && - r.out.returned_size != 0); + *r.out.returned_size != 0); return ret; } @@ -3801,6 +4623,7 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context * { NTSTATUS status; struct samr_QueryDomainInfo r; + union samr_DomainInfo *info = NULL; struct samr_SetDomainInfo s; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; uint16_t set_ok[] = {1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0}; @@ -3827,6 +4650,7 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context * r.in.domain_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryDomainInfo(p, tctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3838,40 +4662,40 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context * switch (levels[i]) { case 2: - if (strcmp(r.out.info->general.oem_information.string, domain_comment) != 0) { + if (strcmp(info->general.oem_information.string, domain_comment) != 0) { printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", - levels[i], r.out.info->general.oem_information.string, domain_comment); + levels[i], info->general.oem_information.string, domain_comment); ret = false; } - if (!r.out.info->general.primary.string) { + if (!info->general.primary.string) { printf("QueryDomainInfo level %u returned no PDC name\n", levels[i]); ret = false; - } else if (r.out.info->general.role == SAMR_ROLE_DOMAIN_PDC) { - if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), r.out.info->general.primary.string) != 0) { + } else if (info->general.role == SAMR_ROLE_DOMAIN_PDC) { + if (dcerpc_server_name(p) && strcasecmp_m(dcerpc_server_name(p), info->general.primary.string) != 0) { printf("QueryDomainInfo level %u returned different PDC name (%s) compared to server name (%s), despite claiming to be the PDC\n", - levels[i], r.out.info->general.primary.string, dcerpc_server_name(p)); + levels[i], info->general.primary.string, dcerpc_server_name(p)); } } break; case 4: - if (strcmp(r.out.info->oem.oem_information.string, domain_comment) != 0) { + if (strcmp(info->oem.oem_information.string, domain_comment) != 0) { printf("QueryDomainInfo level %u returned different oem_information (comment) (%s, expected %s)\n", - levels[i], r.out.info->oem.oem_information.string, domain_comment); + levels[i], info->oem.oem_information.string, domain_comment); ret = false; } break; case 6: - if (!r.out.info->info6.primary.string) { + if (!info->info6.primary.string) { printf("QueryDomainInfo level %u returned no PDC name\n", levels[i]); ret = false; } break; case 11: - if (strcmp(r.out.info->general2.general.oem_information.string, domain_comment) != 0) { + if (strcmp(info->general2.general.oem_information.string, domain_comment) != 0) { printf("QueryDomainInfo level %u returned different comment (%s, expected %s)\n", - levels[i], r.out.info->general2.general.oem_information.string, domain_comment); + levels[i], info->general2.general.oem_information.string, domain_comment); ret = false; } break; @@ -3881,7 +4705,7 @@ static bool test_QueryDomainInfo(struct dcerpc_pipe *p, struct torture_context * s.in.domain_handle = handle; s.in.level = levels[i]; - s.in.info = r.out.info; + s.in.info = info; status = dcerpc_samr_SetDomainInfo(p, tctx, &s); if (set_ok[i]) { @@ -3918,6 +4742,7 @@ static bool test_QueryDomainInfo2(struct dcerpc_pipe *p, struct torture_context { NTSTATUS status; struct samr_QueryDomainInfo2 r; + union samr_DomainInfo *info = NULL; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; int i; bool ret = true; @@ -3927,6 +4752,7 @@ static bool test_QueryDomainInfo2(struct dcerpc_pipe *p, struct torture_context r.in.domain_handle = handle; r.in.level = levels[i]; + r.out.info = &info; status = dcerpc_samr_QueryDomainInfo2(p, tctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -3949,8 +4775,13 @@ static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx, struct samr_QueryDisplayInfo q2; NTSTATUS status; uint32_t resume_handle=0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; int i; bool ret = true; + uint32_t total_size; + uint32_t returned_size; + union samr_DispInfo info; int num_names = 0; const char **names = NULL; @@ -3961,6 +4792,8 @@ static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx, q1.in.resume_handle = &resume_handle; q1.in.max_size = 5; q1.out.resume_handle = &resume_handle; + q1.out.num_entries = &num_entries; + q1.out.sam = &sam; status = STATUS_MORE_ENTRIES; while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { @@ -3970,22 +4803,25 @@ static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx, !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; - for (i=0; i<q1.out.num_entries; i++) { + for (i=0; i<*q1.out.num_entries; i++) { add_string_to_array(tctx, - q1.out.sam->entries[i].name.string, + sam->entries[i].name.string, &names, &num_names); } } torture_assert_ntstatus_ok(tctx, status, "EnumDomainGroups"); - torture_assert(tctx, q1.out.sam, "EnumDomainGroups failed to return q1.out.sam"); + torture_assert(tctx, sam, "EnumDomainGroups failed to return sam"); q2.in.domain_handle = handle; q2.in.level = 5; q2.in.start_idx = 0; q2.in.max_entries = 5; q2.in.buf_size = (uint32_t)-1; + q2.out.total_size = &total_size; + q2.out.returned_size = &returned_size; + q2.out.info = &info; status = STATUS_MORE_ENTRIES; while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) { @@ -3995,9 +4831,9 @@ static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx, !NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) break; - for (i=0; i<q2.out.info.info5.count; i++) { + for (i=0; i<q2.out.info->info5.count; i++) { int j; - const char *name = q2.out.info.info5.entries[i].account_name.string; + const char *name = q2.out.info->info5.entries[i].account_name.string; bool found = false; for (j=0; j<num_names; j++) { if (names[j] == NULL) @@ -4015,7 +4851,7 @@ static bool test_GroupList(struct dcerpc_pipe *p, struct torture_context *tctx, ret = false; } } - q2.in.start_idx += q2.out.info.info5.count; + q2.in.start_idx += q2.out.info->info5.count; } if (!NT_STATUS_IS_OK(status)) { @@ -4076,7 +4912,7 @@ static bool test_RidToSid(struct dcerpc_pipe *p, struct torture_context *tctx, struct samr_RidToSid r; NTSTATUS status; bool ret = true; - struct dom_sid *calc_sid; + struct dom_sid *calc_sid, *out_sid; int rids[] = { 0, 42, 512, 10200 }; int i; @@ -4086,6 +4922,7 @@ static bool test_RidToSid(struct dcerpc_pipe *p, struct torture_context *tctx, calc_sid = dom_sid_dup(tctx, domain_sid); r.in.domain_handle = domain_handle; r.in.rid = rids[i]; + r.out.sid = &out_sid; status = dcerpc_samr_RidToSid(p, tctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -4094,9 +4931,9 @@ static bool test_RidToSid(struct dcerpc_pipe *p, struct torture_context *tctx, } else { calc_sid = dom_sid_add_rid(calc_sid, calc_sid, rids[i]); - if (!dom_sid_equal(calc_sid, r.out.sid)) { + if (!dom_sid_equal(calc_sid, out_sid)) { printf("RidToSid for %d failed - got %s, expected %s\n", rids[i], - dom_sid_string(tctx, r.out.sid), + dom_sid_string(tctx, out_sid), dom_sid_string(tctx, calc_sid)); ret = false; } @@ -4112,10 +4949,12 @@ static bool test_GetBootKeyInformation(struct dcerpc_pipe *p, struct torture_con struct samr_GetBootKeyInformation r; NTSTATUS status; bool ret = true; + uint32_t unknown = 0; torture_comment(tctx, "Testing GetBootKeyInformation\n"); r.in.domain_handle = domain_handle; + r.out.unknown = &unknown; status = dcerpc_samr_GetBootKeyInformation(p, tctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -4134,6 +4973,7 @@ static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *t struct samr_AddGroupMember r; struct samr_DeleteGroupMember d; struct samr_QueryGroupMember q; + struct samr_RidTypeArray *rids = NULL; struct samr_SetMemberAttributesOfGroup s; uint32_t rid; @@ -4173,6 +5013,7 @@ static bool test_AddGroupMember(struct dcerpc_pipe *p, struct torture_context *t } q.in.group_handle = group_handle; + q.out.rids = &rids; status = dcerpc_samr_QueryGroupMember(p, tctx, &q); torture_assert_ntstatus_ok(tctx, status, "QueryGroupMember"); @@ -4312,6 +5153,7 @@ static bool test_OpenDomain(struct dcerpc_pipe *p, struct torture_context *tctx, switch (which_ops) { case TORTURE_SAMR_USER_ATTRIBUTES: case TORTURE_SAMR_PASSWORDS: + case TORTURE_SAMR_PASSWORDS_PWDLASTSET: ret &= test_CreateUser2(p, tctx, &domain_handle, sid, which_ops); ret &= test_CreateUser(p, tctx, &domain_handle, &user_handle, sid, which_ops); /* This test needs 'complex' users to validate */ @@ -4388,6 +5230,7 @@ static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tct { NTSTATUS status; struct samr_LookupDomain r; + struct dom_sid2 *sid = NULL; struct lsa_String n1; struct lsa_String n2; bool ret = true; @@ -4397,6 +5240,7 @@ static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tct /* check for correct error codes */ r.in.connect_handle = handle; r.in.domain_name = &n2; + r.out.sid = &sid; n2.string = NULL; status = dcerpc_samr_LookupDomain(p, tctx, &r); @@ -4419,7 +5263,7 @@ static bool test_LookupDomain(struct dcerpc_pipe *p, struct torture_context *tct ret = false; } - if (!test_OpenDomain(p, tctx, handle, r.out.sid, which_ops)) { + if (!test_OpenDomain(p, tctx, handle, *r.out.sid, which_ops)) { ret = false; } @@ -4433,6 +5277,8 @@ static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx NTSTATUS status; struct samr_EnumDomains r; uint32_t resume_handle = 0; + uint32_t num_entries = 0; + struct samr_SamArray *sam = NULL; int i; bool ret = true; @@ -4440,17 +5286,19 @@ static bool test_EnumDomains(struct dcerpc_pipe *p, struct torture_context *tctx r.in.resume_handle = &resume_handle; r.in.buf_size = (uint32_t)-1; r.out.resume_handle = &resume_handle; + r.out.num_entries = &num_entries; + r.out.sam = &sam; status = dcerpc_samr_EnumDomains(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "EnumDomains"); - if (!r.out.sam) { + if (!*r.out.sam) { return false; } - for (i=0;i<r.out.sam->count;i++) { + for (i=0;i<sam->count;i++) { if (!test_LookupDomain(p, tctx, handle, - r.out.sam->entries[i].name.string, which_ops)) { + sam->entries[i].name.string, which_ops)) { ret = false; } } @@ -4473,6 +5321,7 @@ static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx, struct samr_Connect5 r5; union samr_ConnectInfo info; struct policy_handle h; + uint32_t level_out = 0; bool ret = true, got_handle = false; torture_comment(tctx, "testing samr_Connect\n"); @@ -4553,9 +5402,10 @@ static bool test_Connect(struct dcerpc_pipe *p, struct torture_context *tctx, r5.in.system_name = ""; r5.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - r5.in.level = 1; - r5.in.info = &info; - r5.out.info = &info; + r5.in.level_in = 1; + r5.out.level_out = &level_out; + r5.in.info_in = &info; + r5.out.info_out = &info; r5.out.connect_handle = &h; status = dcerpc_samr_Connect5(p, tctx, &r5); @@ -4651,3 +5501,25 @@ bool torture_rpc_samr_passwords(struct torture_context *torture) return ret; } +bool torture_rpc_samr_passwords_pwdlastset(struct torture_context *torture) +{ + NTSTATUS status; + struct dcerpc_pipe *p; + bool ret = true; + struct policy_handle handle; + + status = torture_rpc_connection(torture, &p, &ndr_table_samr); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + ret &= test_Connect(p, torture, &handle); + + ret &= test_EnumDomains(p, torture, &handle, + TORTURE_SAMR_PASSWORDS_PWDLASTSET); + + ret &= test_samr_handle_Close(p, torture, &handle); + + return ret; +} + diff --git a/source4/torture/rpc/samr_accessmask.c b/source4/torture/rpc/samr_accessmask.c index bfff8c9267..9a8e442019 100644 --- a/source4/torture/rpc/samr_accessmask.c +++ b/source4/torture/rpc/samr_accessmask.c @@ -52,13 +52,15 @@ static NTSTATUS torture_samr_Connect5(struct torture_context *tctx, NTSTATUS status; struct samr_Connect5 r5; union samr_ConnectInfo info; + uint32_t level_out = 0; info.info1.client_version = 0; info.info1.unknown2 = 0; r5.in.system_name = ""; - r5.in.level = 1; - r5.in.info = &info; - r5.out.info = &info; + r5.in.level_in = 1; + r5.in.info_in = &info; + r5.out.info_out = &info; + r5.out.level_out = &level_out; r5.out.connect_handle = h; r5.in.access_mask = mask; @@ -147,6 +149,8 @@ static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, int i; uint32_t mask; uint32_t resume_handle = 0; + struct samr_SamArray *sam = NULL; + uint32_t num_entries = 0; printf("testing which bits in Connect5 accessmask allows us to EnumDomains\n"); mask = 1; @@ -170,6 +174,8 @@ static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, ed.in.resume_handle = &resume_handle; ed.in.buf_size = (uint32_t)-1; ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; status = dcerpc_samr_EnumDomains(p, tctx, &ed); if (!NT_STATUS_IS_OK(status)) { @@ -195,6 +201,8 @@ static bool test_samr_accessmask_EnumDomains(struct torture_context *tctx, ed.in.resume_handle = &resume_handle; ed.in.buf_size = (uint32_t)-1; ed.out.resume_handle = &resume_handle; + ed.out.num_entries = &num_entries; + ed.out.sam = &sam; status = dcerpc_samr_EnumDomains(p, tctx, &ed); if(!NT_STATUS_EQUAL(NT_STATUS_ACCESS_DENIED, status)) { @@ -236,7 +244,7 @@ static bool test_samr_connect_user_acl(struct torture_context *tctx, struct samr_SetSecurity ss; struct security_ace ace; struct security_descriptor *sd; - struct sec_desc_buf sdb; + struct sec_desc_buf sdb, *sdbuf = NULL; bool ret = true; int sd_size; struct dcerpc_pipe *test_p; @@ -255,6 +263,7 @@ static bool test_samr_connect_user_acl(struct torture_context *tctx, /* get the current ACL for the SAMR policy handle */ qs.in.handle = &ch; qs.in.sec_info = SECINFO_DACL; + qs.out.sdbuf = &sdbuf; status = dcerpc_samr_QuerySecurity(p, tctx, &qs); if (!NT_STATUS_IS_OK(status)) { printf("QuerySecurity failed - %s\n", nt_errstr(status)); @@ -262,13 +271,13 @@ static bool test_samr_connect_user_acl(struct torture_context *tctx, } /* how big is the security descriptor? */ - sd_size = qs.out.sdbuf->sd_size; + sd_size = sdbuf->sd_size; /* add an ACE to the security descriptor to deny the user the * 'connect to server' right */ - sd = qs.out.sdbuf->sd; + sd = sdbuf->sd; ace.type = SEC_ACE_TYPE_ACCESS_DENIED; ace.flags = 0; ace.access_mask = SAMR_ACCESS_CONNECT_TO_SERVER; @@ -314,7 +323,7 @@ static bool test_samr_connect_user_acl(struct torture_context *tctx, printf("QuerySecurity failed - %s\n", nt_errstr(status)); ret = false; } - if (sd_size != qs.out.sdbuf->sd_size) { + if (sd_size != sdbuf->sd_size) { printf("security descriptor changed\n"); ret = false; } @@ -387,6 +396,7 @@ static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, { NTSTATUS status; struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; struct policy_handle ch; struct lsa_String dn; int i; @@ -412,6 +422,7 @@ static bool test_samr_accessmask_LookupDomain(struct torture_context *tctx, ld.in.connect_handle = &ch; ld.in.domain_name = &dn; + ld.out.sid = &sid; dn.string = lp_workgroup(tctx->lp_ctx); status = dcerpc_samr_LookupDomain(p, tctx, &ld); @@ -471,6 +482,7 @@ static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, { NTSTATUS status; struct samr_LookupDomain ld; + struct dom_sid2 *sid = NULL; struct samr_OpenDomain od; struct policy_handle ch; struct policy_handle dh; @@ -488,6 +500,7 @@ static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, ld.in.connect_handle = &ch; ld.in.domain_name = &dn; + ld.out.sid = &sid; dn.string = lp_workgroup(tctx->lp_ctx); status = dcerpc_samr_LookupDomain(p, tctx, &ld); if (!NT_STATUS_IS_OK(status)) { @@ -517,7 +530,7 @@ static bool test_samr_accessmask_OpenDomain(struct torture_context *tctx, od.in.connect_handle = &ch; od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - od.in.sid = ld.out.sid; + od.in.sid = *ld.out.sid; od.out.domain_handle = &dh; status = dcerpc_samr_OpenDomain(p, tctx, &od); diff --git a/source4/torture/rpc/samsync.c b/source4/torture/rpc/samsync.c index 12ddc934c9..a3fc6f740f 100644 --- a/source4/torture/rpc/samsync.c +++ b/source4/torture/rpc/samsync.c @@ -151,17 +151,19 @@ struct samsync_trusted_domain { static struct policy_handle *samsync_open_domain(TALLOC_CTX *mem_ctx, struct samsync_state *samsync_state, const char *domain, - struct dom_sid **sid) + struct dom_sid **sid_p) { struct lsa_String name; struct samr_OpenDomain o; struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; struct policy_handle *domain_handle = talloc(mem_ctx, struct policy_handle); NTSTATUS nt_status; name.string = domain; l.in.connect_handle = samsync_state->connect_handle; l.in.domain_name = &name; + l.out.sid = &sid; nt_status = dcerpc_samr_LookupDomain(samsync_state->p_samr, mem_ctx, &l); if (!NT_STATUS_IS_OK(nt_status)) { @@ -171,11 +173,11 @@ static struct policy_handle *samsync_open_domain(TALLOC_CTX *mem_ctx, o.in.connect_handle = samsync_state->connect_handle; o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - o.in.sid = l.out.sid; + o.in.sid = *l.out.sid; o.out.domain_handle = domain_handle; if (sid) { - *sid = l.out.sid; + *sid_p = *l.out.sid; } nt_status = dcerpc_samr_OpenDomain(samsync_state->p_samr, mem_ctx, &o); @@ -192,10 +194,12 @@ static struct sec_desc_buf *samsync_query_samr_sec_desc(TALLOC_CTX *mem_ctx, struct policy_handle *handle) { struct samr_QuerySecurity r; + struct sec_desc_buf *sdbuf = NULL; NTSTATUS status; r.in.handle = handle; r.in.sec_info = 0x7; + r.out.sdbuf = &sdbuf; status = dcerpc_samr_QuerySecurity(samsync_state->p_samr, mem_ctx, &r); if (!NT_STATUS_IS_OK(status)) { @@ -203,7 +207,7 @@ static struct sec_desc_buf *samsync_query_samr_sec_desc(TALLOC_CTX *mem_ctx, return NULL; } - return r.out.sdbuf; + return sdbuf; } static struct sec_desc_buf *samsync_query_lsa_sec_desc(TALLOC_CTX *mem_ctx, @@ -260,6 +264,15 @@ static struct sec_desc_buf *samsync_query_lsa_sec_desc(TALLOC_CTX *mem_ctx, } \ } while (0) +#define TEST_BINARY_STRING_EQUAL(s1, s2) do {\ + if (!((!s1.array || s1.array[0]=='\0') && (!s2.array || s2.array[0]=='\0')) \ + && memcmp(s1.array, s2.array, s1.length * 2) != 0) {\ + printf("%s: string mismatch: " #s1 ":%s != " #s2 ": %s\n", \ + __location__, (const char *)s1.array, (const char *)s2.array);\ + ret = false;\ + } \ +} while (0) + #define TEST_SID_EQUAL(s1, s2) do {\ if (!dom_sid_equal(s1, s2)) {\ printf("%s: dom_sid mismatch: " #s1 ":%s != " #s2 ": %s\n", \ @@ -294,6 +307,7 @@ static bool samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *sam struct netr_DELTA_DOMAIN *domain = delta->delta_union.domain; struct dom_sid *dom_sid; struct samr_QueryDomainInfo q[14]; /* q[0] will be unused simple for clarity */ + union samr_DomainInfo *info[14]; uint16_t levels[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13}; NTSTATUS nt_status; int i; @@ -341,8 +355,10 @@ static bool samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *sam (long long)samsync_state->seq_num[database_id]); for (i=0;i<ARRAY_SIZE(levels);i++) { + q[levels[i]].in.domain_handle = samsync_state->domain_handle[database_id]; q[levels[i]].in.level = levels[i]; + q[levels[i]].out.info = &info[levels[i]]; nt_status = dcerpc_samr_QueryDomainInfo(samsync_state->p_samr, mem_ctx, &q[levels[i]]); @@ -353,23 +369,23 @@ static bool samsync_handle_domain(TALLOC_CTX *mem_ctx, struct samsync_state *sam } } - TEST_STRING_EQUAL(q[5].out.info->info5.domain_name, domain->domain_name); + TEST_STRING_EQUAL(info[5]->info5.domain_name, domain->domain_name); - TEST_STRING_EQUAL(q[2].out.info->general.oem_information, domain->oem_information); - TEST_STRING_EQUAL(q[4].out.info->oem.oem_information, domain->oem_information); - TEST_TIME_EQUAL(q[2].out.info->general.force_logoff_time, domain->force_logoff_time); - TEST_TIME_EQUAL(q[3].out.info->info3.force_logoff_time, domain->force_logoff_time); + TEST_STRING_EQUAL(info[2]->general.oem_information, domain->oem_information); + TEST_STRING_EQUAL(info[4]->oem.oem_information, domain->oem_information); + TEST_TIME_EQUAL(info[2]->general.force_logoff_time, domain->force_logoff_time); + TEST_TIME_EQUAL(info[3]->info3.force_logoff_time, domain->force_logoff_time); - TEST_TIME_EQUAL(q[1].out.info->info1.min_password_length, domain->min_password_length); - TEST_TIME_EQUAL(q[1].out.info->info1.password_history_length, domain->password_history_length); - TEST_TIME_EQUAL(q[1].out.info->info1.max_password_age, domain->max_password_age); - TEST_TIME_EQUAL(q[1].out.info->info1.min_password_age, domain->min_password_age); + TEST_TIME_EQUAL(info[1]->info1.min_password_length, domain->min_password_length); + TEST_TIME_EQUAL(info[1]->info1.password_history_length, domain->password_history_length); + TEST_TIME_EQUAL(info[1]->info1.max_password_age, domain->max_password_age); + TEST_TIME_EQUAL(info[1]->info1.min_password_age, domain->min_password_age); - TEST_UINT64_EQUAL(q[8].out.info->info8.sequence_num, + TEST_UINT64_EQUAL(info[8]->info8.sequence_num, domain->sequence_num); - TEST_TIME_EQUAL(q[8].out.info->info8.domain_create_time, + TEST_TIME_EQUAL(info[8]->info8.domain_create_time, domain->domain_create_time); - TEST_TIME_EQUAL(q[13].out.info->info13.domain_create_time, + TEST_TIME_EQUAL(info[13]->info13.domain_create_time, domain->domain_create_time); TEST_SEC_DESC_EQUAL(domain->sdbuf, samr, samsync_state->domain_handle[database_id]); @@ -425,9 +441,12 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct struct samr_OpenUser r; struct samr_QueryUserInfo q; + union samr_UserInfo *info; struct policy_handle user_handle; struct samr_GetGroupsForUser getgroups; + struct samr_RidWithAttributeArray *rids; + if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { printf("SamSync needs domain information before the users\n"); return false; @@ -446,6 +465,7 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct q.in.user_handle = &user_handle; q.in.level = 21; + q.out.info = &info; TEST_SEC_DESC_EQUAL(user->sdbuf, samr, &user_handle); @@ -457,6 +477,7 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct } getgroups.in.user_handle = &user_handle; + getgroups.out.rids = &rids; nt_status = dcerpc_samr_GetGroupsForUser(samsync_state->p_samr, mem_ctx, &getgroups); if (!NT_STATUS_IS_OK(nt_status)) { @@ -480,67 +501,67 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct return false; } - TEST_STRING_EQUAL(q.out.info->info21.account_name, user->account_name); - TEST_STRING_EQUAL(q.out.info->info21.full_name, user->full_name); - TEST_INT_EQUAL(q.out.info->info21.rid, user->rid); - TEST_INT_EQUAL(q.out.info->info21.primary_gid, user->primary_gid); - TEST_STRING_EQUAL(q.out.info->info21.home_directory, user->home_directory); - TEST_STRING_EQUAL(q.out.info->info21.home_drive, user->home_drive); - TEST_STRING_EQUAL(q.out.info->info21.logon_script, user->logon_script); - TEST_STRING_EQUAL(q.out.info->info21.description, user->description); - TEST_STRING_EQUAL(q.out.info->info21.workstations, user->workstations); + TEST_STRING_EQUAL(info->info21.account_name, user->account_name); + TEST_STRING_EQUAL(info->info21.full_name, user->full_name); + TEST_INT_EQUAL(info->info21.rid, user->rid); + TEST_INT_EQUAL(info->info21.primary_gid, user->primary_gid); + TEST_STRING_EQUAL(info->info21.home_directory, user->home_directory); + TEST_STRING_EQUAL(info->info21.home_drive, user->home_drive); + TEST_STRING_EQUAL(info->info21.logon_script, user->logon_script); + TEST_STRING_EQUAL(info->info21.description, user->description); + TEST_STRING_EQUAL(info->info21.workstations, user->workstations); - TEST_TIME_EQUAL(q.out.info->info21.last_logon, user->last_logon); - TEST_TIME_EQUAL(q.out.info->info21.last_logoff, user->last_logoff); + TEST_TIME_EQUAL(info->info21.last_logon, user->last_logon); + TEST_TIME_EQUAL(info->info21.last_logoff, user->last_logoff); - TEST_INT_EQUAL(q.out.info->info21.logon_hours.units_per_week, + TEST_INT_EQUAL(info->info21.logon_hours.units_per_week, user->logon_hours.units_per_week); if (ret) { - if (memcmp(q.out.info->info21.logon_hours.bits, user->logon_hours.bits, - q.out.info->info21.logon_hours.units_per_week/8) != 0) { + if (memcmp(info->info21.logon_hours.bits, user->logon_hours.bits, + info->info21.logon_hours.units_per_week/8) != 0) { printf("Logon hours mismatch\n"); ret = false; } } - TEST_INT_EQUAL(q.out.info->info21.bad_password_count, + TEST_INT_EQUAL(info->info21.bad_password_count, user->bad_password_count); - TEST_INT_EQUAL(q.out.info->info21.logon_count, + TEST_INT_EQUAL(info->info21.logon_count, user->logon_count); - TEST_TIME_EQUAL(q.out.info->info21.last_password_change, + TEST_TIME_EQUAL(info->info21.last_password_change, user->last_password_change); - TEST_TIME_EQUAL(q.out.info->info21.acct_expiry, + TEST_TIME_EQUAL(info->info21.acct_expiry, user->acct_expiry); - TEST_INT_EQUAL((q.out.info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags); + TEST_INT_EQUAL((info->info21.acct_flags & ~ACB_PW_EXPIRED), user->acct_flags); if (user->acct_flags & ACB_PWNOEXP) { - if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) { + if (info->info21.acct_flags & ACB_PW_EXPIRED) { printf("ACB flags mismatch: both expired and no expiry!\n"); ret = false; } - if (q.out.info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) { + if (info->info21.force_password_change != (NTTIME)0x7FFFFFFFFFFFFFFFULL) { printf("ACB flags mismatch: no password expiry, but force password change 0x%016llx (%lld) != 0x%016llx (%lld)\n", - (unsigned long long)q.out.info->info21.force_password_change, - (unsigned long long)q.out.info->info21.force_password_change, + (unsigned long long)info->info21.force_password_change, + (unsigned long long)info->info21.force_password_change, (unsigned long long)0x7FFFFFFFFFFFFFFFULL, (unsigned long long)0x7FFFFFFFFFFFFFFFULL ); ret = false; } } - TEST_INT_EQUAL(q.out.info->info21.nt_password_set, user->nt_password_present); - TEST_INT_EQUAL(q.out.info->info21.lm_password_set, user->lm_password_present); - TEST_INT_EQUAL(q.out.info->info21.password_expired, user->password_expired); + TEST_INT_EQUAL(info->info21.nt_password_set, user->nt_password_present); + TEST_INT_EQUAL(info->info21.lm_password_set, user->lm_password_present); + TEST_INT_EQUAL(info->info21.password_expired, user->password_expired); - TEST_STRING_EQUAL(q.out.info->info21.comment, user->comment); - TEST_STRING_EQUAL(q.out.info->info21.parameters, user->parameters); + TEST_STRING_EQUAL(info->info21.comment, user->comment); + TEST_BINARY_STRING_EQUAL(info->info21.parameters, user->parameters); - TEST_INT_EQUAL(q.out.info->info21.country_code, user->country_code); - TEST_INT_EQUAL(q.out.info->info21.code_page, user->code_page); + TEST_INT_EQUAL(info->info21.country_code, user->country_code); + TEST_INT_EQUAL(info->info21.code_page, user->code_page); - TEST_STRING_EQUAL(q.out.info->info21.profile_path, user->profile_path); + TEST_STRING_EQUAL(info->info21.profile_path, user->profile_path); if (user->lm_password_present) { sam_rid_crypt(rid, user->lmpassword.hash, lm_hash.hash, 0); @@ -619,7 +640,7 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct return true; } } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_PASSWORD_EXPIRED)) { - if (q.out.info->info21.acct_flags & ACB_PW_EXPIRED) { + if (info->info21.acct_flags & ACB_PW_EXPIRED) { return true; } } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD)) { @@ -654,7 +675,7 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct TEST_TIME_EQUAL(user->last_logon, info3->base.last_logon); TEST_TIME_EQUAL(user->acct_expiry, info3->base.acct_expiry); TEST_TIME_EQUAL(user->last_password_change, info3->base.last_password_change); - TEST_TIME_EQUAL(q.out.info->info21.force_password_change, info3->base.force_password_change); + TEST_TIME_EQUAL(info->info21.force_password_change, info3->base.force_password_change); /* Does the concept of a logoff time ever really * exist? (not in any sensible way, according to the @@ -667,28 +688,28 @@ static bool samsync_handle_user(struct torture_context *tctx, TALLOC_CTX *mem_ct TEST_TIME_EQUAL(user->last_logoff, info3->base.last_logoff); } - TEST_INT_EQUAL(getgroups.out.rids->count, info3->base.groups.count); - if (getgroups.out.rids->count == info3->base.groups.count) { + TEST_INT_EQUAL(rids->count, info3->base.groups.count); + if (rids->count == info3->base.groups.count) { int i, j; - int count = getgroups.out.rids->count; - bool *matched = talloc_zero_array(mem_ctx, bool, getgroups.out.rids->count); + int count = rids->count; + bool *matched = talloc_zero_array(mem_ctx, bool, rids->count); for (i = 0; i < count; i++) { for (j = 0; j < count; j++) { - if ((getgroups.out.rids->rids[i].rid == + if ((rids->rids[i].rid == info3->base.groups.rids[j].rid) - && (getgroups.out.rids->rids[i].attributes == + && (rids->rids[i].attributes == info3->base.groups.rids[j].attributes)) { matched[i] = true; } } } - for (i = 0; i < getgroups.out.rids->count; i++) { + for (i = 0; i < rids->count; i++) { if (matched[i] == false) { ret = false; printf("Could not find group RID %u found in getgroups in NETLOGON reply\n", - getgroups.out.rids->rids[i].rid); + rids->rids[i].rid); } } } @@ -711,6 +732,7 @@ static bool samsync_handle_alias(TALLOC_CTX *mem_ctx, struct samsync_state *sams struct samr_OpenAlias r; struct samr_QueryAliasInfo q; + union samr_AliasInfo *info; struct policy_handle alias_handle; if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { @@ -731,6 +753,7 @@ static bool samsync_handle_alias(TALLOC_CTX *mem_ctx, struct samsync_state *sams q.in.alias_handle = &alias_handle; q.in.level = 1; + q.out.info = &info; TEST_SEC_DESC_EQUAL(alias->sdbuf, samr, &alias_handle); @@ -745,8 +768,8 @@ static bool samsync_handle_alias(TALLOC_CTX *mem_ctx, struct samsync_state *sams return false; } - TEST_STRING_EQUAL(q.out.info->all.name, alias->alias_name); - TEST_STRING_EQUAL(q.out.info->all.description, alias->description); + TEST_STRING_EQUAL(info->all.name, alias->alias_name); + TEST_STRING_EQUAL(info->all.description, alias->description); return ret; } @@ -760,6 +783,7 @@ static bool samsync_handle_group(TALLOC_CTX *mem_ctx, struct samsync_state *sams struct samr_OpenGroup r; struct samr_QueryGroupInfo q; + union samr_GroupInfo *info; struct policy_handle group_handle; if (!samsync_state->domain_name || !samsync_state->domain_handle[database_id]) { @@ -780,6 +804,7 @@ static bool samsync_handle_group(TALLOC_CTX *mem_ctx, struct samsync_state *sams q.in.group_handle = &group_handle; q.in.level = 1; + q.out.info = &info; TEST_SEC_DESC_EQUAL(group->sdbuf, samr, &group_handle); @@ -794,9 +819,9 @@ static bool samsync_handle_group(TALLOC_CTX *mem_ctx, struct samsync_state *sams return false; } - TEST_STRING_EQUAL(q.out.info->all.name, group->group_name); - TEST_INT_EQUAL(q.out.info->all.attributes, group->attributes); - TEST_STRING_EQUAL(q.out.info->all.description, group->description); + TEST_STRING_EQUAL(info->all.name, group->group_name); + TEST_INT_EQUAL(info->all.attributes, group->attributes); + TEST_STRING_EQUAL(info->all.description, group->description); return ret; } diff --git a/source4/torture/rpc/schannel.c b/source4/torture/rpc/schannel.c index ca1655729b..451990a71b 100644 --- a/source4/torture/rpc/schannel.c +++ b/source4/torture/rpc/schannel.c @@ -128,6 +128,7 @@ static bool test_samr_ops(struct torture_context *tctx, { NTSTATUS status; struct samr_GetDomPwInfo r; + struct samr_PwInfo info; struct samr_Connect connect; struct samr_OpenDomain opendom; int i; @@ -137,6 +138,7 @@ static bool test_samr_ops(struct torture_context *tctx, name.string = lp_workgroup(tctx->lp_ctx); r.in.domain_name = &name; + r.out.info = &info; connect.in.system_name = 0; connect.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; diff --git a/source4/torture/rpc/svcctl.c b/source4/torture/rpc/svcctl.c index c9006baaf5..8d8f43a0cf 100644 --- a/source4/torture/rpc/svcctl.c +++ b/source4/torture/rpc/svcctl.c @@ -1,19 +1,19 @@ -/* +/* Unix SMB/CIFS implementation. test suite for srvsvc rpc operations Copyright (C) Jelmer Vernooij 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/>. */ @@ -26,28 +26,141 @@ static bool test_OpenSCManager(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h) { struct svcctl_OpenSCManagerW r; - + r.in.MachineName = NULL; r.in.DatabaseName = NULL; r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; r.out.handle = h; - - torture_assert_ntstatus_ok(tctx, - dcerpc_svcctl_OpenSCManagerW(p, tctx, &r), - "OpenSCManager failed!"); - + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_OpenSCManagerW(p, tctx, &r), + "OpenSCManager failed!"); + return true; } static bool test_CloseServiceHandle(struct dcerpc_pipe *p, struct torture_context *tctx, struct policy_handle *h) { - struct svcctl_CloseServiceHandle r; + struct svcctl_CloseServiceHandle r; r.in.handle = h; r.out.handle = h; - torture_assert_ntstatus_ok(tctx, - dcerpc_svcctl_CloseServiceHandle(p, tctx, &r), - "CloseServiceHandle failed"); + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_CloseServiceHandle(p, tctx, &r), + "CloseServiceHandle failed"); + + return true; +} + +static bool test_OpenService(struct dcerpc_pipe *p, struct torture_context *tctx, + struct policy_handle *h, const char *name, struct policy_handle *s) +{ + struct svcctl_OpenServiceW r; + + r.in.scmanager_handle = h; + r.in.ServiceName = name; + r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; + r.out.handle = s; + + torture_assert_ntstatus_ok(tctx, + dcerpc_svcctl_OpenServiceW(p, tctx, &r), + "OpenServiceW failed!"); + torture_assert_werr_ok(tctx, r.out.result, "OpenServiceW failed!"); + + return true; + +} + +static bool test_QueryServiceStatusEx(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceStatusEx r; + struct policy_handle h, s; + NTSTATUS status; + + uint32_t info_level = 0; + uint8_t *buffer; + uint32_t buf_size = 0; + uint32_t bytes_needed = 0; + + if (!test_OpenSCManager(p, tctx, &h)) + return false; + + if (!test_OpenService(p, tctx, &h, "Netlogon", &s)) + return false; + + buffer = talloc(tctx, uint8_t); + + r.in.handle = &s; + r.in.info_level = info_level; + r.in.buf_size = buf_size; + r.out.buffer = buffer; + r.out.bytes_needed = &bytes_needed; + + status = dcerpc_svcctl_QueryServiceStatusEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.buf_size = bytes_needed; + buffer = talloc_array(tctx, uint8_t, bytes_needed); + r.out.buffer = buffer; + + status = dcerpc_svcctl_QueryServiceStatusEx(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceStatusEx failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceStatusEx failed!"); + } + + if (!test_CloseServiceHandle(p, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(p, tctx, &h)) + return false; + + return true; +} + +static bool test_QueryServiceConfig2W(struct torture_context *tctx, struct dcerpc_pipe *p) +{ + struct svcctl_QueryServiceConfig2W r; + struct policy_handle h, s; + NTSTATUS status; + + uint32_t info_level = 1; + uint8_t *buffer; + uint32_t buf_size = 0; + uint32_t bytes_needed = 0; + + if (!test_OpenSCManager(p, tctx, &h)) + return false; + + if (!test_OpenService(p, tctx, &h, "Netlogon", &s)) + return false; + + buffer = talloc(tctx, uint8_t); + + r.in.handle = &s; + r.in.info_level = info_level; + r.in.buf_size = buf_size; + r.out.buffer = buffer; + r.out.bytes_needed = &bytes_needed; + + status = dcerpc_svcctl_QueryServiceConfig2W(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + + if (W_ERROR_EQUAL(r.out.result, WERR_INSUFFICIENT_BUFFER)) { + r.in.buf_size = bytes_needed; + buffer = talloc_array(tctx, uint8_t, bytes_needed); + r.out.buffer = buffer; + + status = dcerpc_svcctl_QueryServiceConfig2W(p, tctx, &r); + torture_assert_ntstatus_ok(tctx, status, "QueryServiceConfig2W failed!"); + torture_assert_werr_ok(tctx, r.out.result, "QueryServiceConfig2W failed!"); + } + + if (!test_CloseServiceHandle(p, tctx, &s)) + return false; + + if (!test_CloseServiceHandle(p, tctx, &h)) + return false; return true; } @@ -59,7 +172,9 @@ static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_ int i; NTSTATUS status; uint32_t resume_handle = 0; - struct ENUM_SERVICE_STATUS *service = NULL; + struct ENUM_SERVICE_STATUS *service = NULL; + uint32_t bytes_needed = 0; + uint32_t services_returned = 0; if (!test_OpenSCManager(p, tctx, &h)) return false; @@ -71,17 +186,17 @@ static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_ r.in.resume_handle = &resume_handle; r.out.service = NULL; r.out.resume_handle = &resume_handle; - r.out.services_returned = 0; - r.out.bytes_needed = 0; + r.out.services_returned = &services_returned; + r.out.bytes_needed = &bytes_needed; status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); if (W_ERROR_EQUAL(r.out.result, WERR_MORE_DATA)) { - r.in.buf_size = *r.out.bytes_needed; - r.out.service = talloc_array(tctx, uint8_t, *r.out.bytes_needed); - + r.in.buf_size = bytes_needed; + r.out.service = talloc_array(tctx, uint8_t, bytes_needed); + status = dcerpc_svcctl_EnumServicesStatusW(p, tctx, &r); torture_assert_ntstatus_ok(tctx, status, "EnumServicesStatus failed!"); @@ -90,17 +205,17 @@ static bool test_EnumServicesStatus(struct torture_context *tctx, struct dcerpc_ service = (struct ENUM_SERVICE_STATUS *)r.out.service; } - for(i = 0; i < *r.out.services_returned; i++) { + for(i = 0; i < services_returned; i++) { printf("Type: %d, State: %d\n", service[i].status.type, service[i].status.state); } - + if (!test_CloseServiceHandle(p, tctx, &h)) return false; return true; } -static bool test_SCManager(struct torture_context *tctx, +static bool test_SCManager(struct torture_context *tctx, struct dcerpc_pipe *p) { struct policy_handle h; @@ -119,13 +234,16 @@ struct torture_suite *torture_rpc_svcctl(TALLOC_CTX *mem_ctx) struct torture_suite *suite = torture_suite_create(mem_ctx, "SVCCTL"); struct torture_rpc_tcase *tcase; - tcase = torture_suite_add_rpc_iface_tcase(suite, "svcctl", - &ndr_table_svcctl); - - torture_rpc_tcase_add_test(tcase, "SCManager", - test_SCManager); - torture_rpc_tcase_add_test(tcase, "EnumServicesStatus", - test_EnumServicesStatus); + tcase = torture_suite_add_rpc_iface_tcase(suite, "svcctl", &ndr_table_svcctl); + + torture_rpc_tcase_add_test(tcase, "SCManager", + test_SCManager); + torture_rpc_tcase_add_test(tcase, "EnumServicesStatus", + test_EnumServicesStatus); + torture_rpc_tcase_add_test(tcase, "QueryServiceStatusEx", + test_QueryServiceStatusEx); + torture_rpc_tcase_add_test(tcase, "QueryServiceConfig2W", + test_QueryServiceConfig2W); return suite; } diff --git a/source4/torture/rpc/testjoin.c b/source4/torture/rpc/testjoin.c index 3408a1924c..c93358015c 100644 --- a/source4/torture/rpc/testjoin.c +++ b/source4/torture/rpc/testjoin.c @@ -59,6 +59,7 @@ static NTSTATUS DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct policy_handle user_handle; uint32_t rid; struct samr_LookupNames n; + struct samr_Ids rids, types; struct lsa_String sname; struct samr_OpenUser r; @@ -67,10 +68,12 @@ static NTSTATUS DeleteUser_byname(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, n.in.domain_handle = handle; n.in.num_names = 1; n.in.names = &sname; + n.out.rids = &rids; + n.out.types = &types; status = dcerpc_samr_LookupNames(p, mem_ctx, &n); if (NT_STATUS_IS_OK(status)) { - rid = n.out.rids.ids[0]; + rid = n.out.rids->ids[0]; } else { return status; } @@ -113,7 +116,9 @@ struct test_join *torture_create_testuser(struct torture_context *torture, struct samr_CreateUser2 r; struct samr_OpenDomain o; struct samr_LookupDomain l; + struct dom_sid2 *sid = NULL; struct samr_GetUserPwInfo pwp; + struct samr_PwInfo info; struct samr_SetUserInfo s; union samr_UserInfo u; struct policy_handle handle; @@ -172,6 +177,7 @@ struct test_join *torture_create_testuser(struct torture_context *torture, name.string = domain; l.in.connect_handle = &handle; l.in.domain_name = &name; + l.out.sid = &sid; status = dcerpc_samr_LookupDomain(join->p, join, &l); if (!NT_STATUS_IS_OK(status)) { @@ -179,14 +185,14 @@ struct test_join *torture_create_testuser(struct torture_context *torture, goto failed; } - talloc_steal(join, l.out.sid); - join->dom_sid = l.out.sid; + talloc_steal(join, *l.out.sid); + join->dom_sid = *l.out.sid; join->dom_netbios_name = talloc_strdup(join, domain); if (!join->dom_netbios_name) goto failed; o.in.connect_handle = &handle; o.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED; - o.in.sid = l.out.sid; + o.in.sid = *l.out.sid; o.out.domain_handle = &domain_handle; status = dcerpc_samr_OpenDomain(join->p, join, &o); @@ -224,10 +230,11 @@ again: join->user_sid = dom_sid_add_rid(join, join->dom_sid, rid); pwp.in.user_handle = &join->user_handle; + pwp.out.info = &info; status = dcerpc_samr_GetUserPwInfo(join->p, join, &pwp); if (NT_STATUS_IS_OK(status)) { - policy_min_pw_len = pwp.out.info.min_password_length; + policy_min_pw_len = pwp.out.info->min_password_length; } random_pw = generate_random_str(join, MAX(8, policy_min_pw_len)); @@ -240,7 +247,7 @@ again: s.in.level = 24; encode_pw_buffer(u.info24.password.data, random_pw, STR_UNICODE); - u.info24.pw_len = strlen(random_pw); + u.info24.password_expired = 0; status = dcerpc_fetch_session_key(join->p, &session_key); if (!NT_STATUS_IS_OK(status)) { |