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 2 of the License, or
+ 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,
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.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "lib/cmdline/popt_common.h"
#include "libcli/raw/libcliraw.h"
-#include "libcli/raw/ioctl.h"
+#include "libcli/raw/raw_proto.h"
+#include "../libcli/smb/smb_constants.h"
#include "libcli/libcli.h"
#include "system/filesys.h"
#include "system/shmem.h"
#include "system/wait.h"
#include "system/time.h"
-#include "torture/ui.h"
#include "torture/torture.h"
-#include "util/dlinklist.h"
-
+#include "../lib/util/dlinklist.h"
+#include "libcli/resolve/resolve.h"
+#include "param/param.h"
+#include "libcli/security/security.h"
+#include "libcli/util/clilsa.h"
+#include "torture/util.h"
+#include "libcli/smb/smbXcli_base.h"
/**
setup a directory ready for a test
if (smbcli_deltree(cli->tree, dname) == -1 ||
NT_STATUS_IS_ERR(smbcli_mkdir(cli->tree, dname))) {
printf("Unable to setup %s - %s\n", dname, smbcli_errstr(cli->tree));
- return False;
+ return false;
}
- return True;
+ return true;
}
/*
mem_ctx = talloc_named_const(tree, 0, "create_directory_handle");
io.generic.level = RAW_OPEN_NTCREATEX;
- io.ntcreatex.in.root_fid = 0;
+ io.ntcreatex.in.root_fid.fnum = 0;
io.ntcreatex.in.flags = 0;
io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
io.ntcreatex.in.create_options = NTCREATEX_OPTIONS_DIRECTORY;
io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
- io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE;
+ io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE | NTCREATEX_SHARE_ACCESS_DELETE;
io.ntcreatex.in.alloc_size = 0;
io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
}
}
- /* make sure all the timestamps aren't the same, and are also
- in different DST zones*/
- setfile.generic.level = RAW_SFILEINFO_SETATTRE;
+ /* make sure all the timestamps aren't the same */
+ ZERO_STRUCT(setfile);
+ setfile.generic.level = RAW_SFILEINFO_BASIC_INFO;
setfile.generic.in.file.fnum = fnum;
- setfile.setattre.in.create_time = t + 9*30*24*60*60;
- setfile.setattre.in.access_time = t + 6*30*24*60*60;
- setfile.setattre.in.write_time = t + 3*30*24*60*60;
+ unix_to_nt_time(&setfile.basic_info.in.create_time,
+ t + 9*30*24*60*60);
+ unix_to_nt_time(&setfile.basic_info.in.access_time,
+ t + 6*30*24*60*60);
+ unix_to_nt_time(&setfile.basic_info.in.write_time,
+ t + 3*30*24*60*60);
status = smb_raw_setfileinfo(cli->tree, &setfile);
if (!NT_STATUS_IS_OK(status)) {
}
/* make sure all the timestamps aren't the same */
- fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
+ fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO;
fileinfo.generic.in.file.fnum = fnum;
status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
printf("Failed to query file times - %s\n", nt_errstr(status));
}
- if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
+ if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) {
printf("create_time not setup correctly\n");
}
- if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
+ if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) {
printf("access_time not setup correctly\n");
}
- if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
+ if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) {
printf("write_time not setup correctly\n");
}
}
}
- /* make sure all the timestamps aren't the same, and are also
- in different DST zones*/
- setfile.generic.level = RAW_SFILEINFO_SETATTRE;
+ /* make sure all the timestamps aren't the same */
+ ZERO_STRUCT(setfile);
+ setfile.generic.level = RAW_SFILEINFO_BASIC_INFO;
setfile.generic.in.file.fnum = fnum;
- setfile.setattre.in.create_time = t + 9*30*24*60*60;
- setfile.setattre.in.access_time = t + 6*30*24*60*60;
- setfile.setattre.in.write_time = t + 3*30*24*60*60;
+ unix_to_nt_time(&setfile.basic_info.in.create_time,
+ t + 9*30*24*60*60);
+ unix_to_nt_time(&setfile.basic_info.in.access_time,
+ t + 6*30*24*60*60);
+ unix_to_nt_time(&setfile.basic_info.in.write_time,
+ t + 3*30*24*60*60);
status = smb_raw_setfileinfo(cli->tree, &setfile);
if (!NT_STATUS_IS_OK(status)) {
}
/* make sure all the timestamps aren't the same */
- fileinfo.generic.level = RAW_FILEINFO_GETATTRE;
+ fileinfo.generic.level = RAW_FILEINFO_BASIC_INFO;
fileinfo.generic.in.file.fnum = fnum;
status = smb_raw_fileinfo(cli->tree, mem_ctx, &fileinfo);
printf("Failed to query file times - %s\n", nt_errstr(status));
}
- if (setfile.setattre.in.create_time != fileinfo.getattre.out.create_time) {
+ if (setfile.basic_info.in.create_time != fileinfo.basic_info.out.create_time) {
printf("create_time not setup correctly\n");
}
- if (setfile.setattre.in.access_time != fileinfo.getattre.out.access_time) {
+ if (setfile.basic_info.in.access_time != fileinfo.basic_info.out.access_time) {
printf("access_time not setup correctly\n");
}
- if (setfile.setattre.in.write_time != fileinfo.getattre.out.write_time) {
+ if (setfile.basic_info.in.write_time != fileinfo.basic_info.out.write_time) {
printf("write_time not setup correctly\n");
}
return fnum;
}
-
-
-/* return a pointer to a anonymous shared memory segment of size "size"
- which will persist across fork() but will disappear when all processes
- exit
-
- The memory is not zeroed
-
- This function uses system5 shared memory. It takes advantage of a property
- that the memory is not destroyed if it is attached when the id is removed
- */
-void *shm_setup(int size)
-{
- int shmid;
- void *ret;
-
- shmid = shmget(IPC_PRIVATE, size, SHM_R | SHM_W);
- if (shmid == -1) {
- printf("can't get shared memory\n");
- exit(1);
- }
- ret = (void *)shmat(shmid, 0, 0);
- if (!ret || ret == (void *)-1) {
- printf("can't attach to shared memory\n");
- return NULL;
- }
- /* the following releases the ipc, but note that this process
- and all its children will still have access to the memory, its
- just that the shmid is no longer valid for other shm calls. This
- means we don't leave behind lots of shm segments after we exit
-
- See Stevens "advanced programming in unix env" for details
- */
- shmctl(shmid, IPC_RMID, 0);
-
- return ret;
-}
-
-
-/*
+/**
check that a wire string matches the flags specified
not 100% accurate, but close enough for testing
*/
-bool wire_bad_flags(struct smb_wire_string *str, int flags, struct smbcli_state *cli)
+bool wire_bad_flags(struct smb_wire_string *str, int flags,
+ struct smbcli_transport *transport)
{
bool server_unicode;
int len;
- if (!str || !str->s) return True;
+ if (!str || !str->s) return true;
len = strlen(str->s);
if (flags & STR_TERMINATE) len++;
- server_unicode = (cli->transport->negotiate.capabilities&CAP_UNICODE)?True:False;
- if (getenv("CLI_FORCE_ASCII") || !lp_unicode()) {
- server_unicode = False;
+ server_unicode = (transport->negotiate.capabilities&CAP_UNICODE)?true:false;
+ if (getenv("CLI_FORCE_ASCII") || !transport->options.unicode) {
+ server_unicode = false;
}
if ((flags & STR_UNICODE) || server_unicode) {
if (str->private_length != len) {
printf("Expected wire_length %d but got %d for '%s'\n",
len, str->private_length, str->s);
- return True;
+ return true;
}
- return False;
+ return false;
}
/*
nt.ntioctl.level = RAW_IOCTL_NTIOCTL;
nt.ntioctl.in.function = FSCTL_SET_SPARSE;
nt.ntioctl.in.file.fnum = fnum;
- nt.ntioctl.in.fsctl = True;
+ nt.ntioctl.in.fsctl = true;
nt.ntioctl.in.filter = 0;
nt.ntioctl.in.max_data = 0;
nt.ntioctl.in.blob = data_blob(NULL, 0);
_PUBLIC_ bool torture_open_connection_share(TALLOC_CTX *mem_ctx,
struct smbcli_state **c,
+ struct torture_context *tctx,
const char *hostname,
const char *sharename,
- struct event_context *ev)
+ struct tevent_context *ev)
{
NTSTATUS status;
+ struct smbcli_options options;
+ struct smbcli_session_options session_options;
+
+ lpcfg_smbcli_options(tctx->lp_ctx, &options);
+ lpcfg_smbcli_session_options(tctx->lp_ctx, &session_options);
+
+ options.use_oplocks = torture_setting_bool(tctx, "use_oplocks", true);
+ options.use_level2_oplocks = torture_setting_bool(tctx, "use_level2_oplocks", true);
+
status = smbcli_full_connection(mem_ctx, c, hostname,
+ lpcfg_smb_ports(tctx->lp_ctx),
sharename, NULL,
- cmdline_credentials, ev);
+ lpcfg_socket_options(tctx->lp_ctx),
+ popt_get_cmdline_credentials(),
+ lpcfg_resolve_context(tctx->lp_ctx),
+ ev, &options, &session_options,
+ lpcfg_gensec_settings(tctx, tctx->lp_ctx));
if (!NT_STATUS_IS_OK(status)) {
printf("Failed to open connection - %s\n", nt_errstr(status));
- return False;
+ return false;
}
- (*c)->transport->options.use_oplocks = lp_parm_bool(-1, "torture",
- "use_oplocks", False);
- (*c)->transport->options.use_level2_oplocks = lp_parm_bool(-1, "torture",
- "use_level2_oplocks", False);
-
- return True;
+ return true;
}
_PUBLIC_ bool torture_get_conn_index(int conn_index,
TALLOC_CTX *mem_ctx,
+ struct torture_context *tctx,
char **host, char **share)
{
char **unc_list = NULL;
int num_unc_names = 0;
const char *p;
- (*host) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "host"));
- (*share) = talloc_strdup(mem_ctx, lp_parm_string(-1, "torture", "share"));
+ (*host) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "host", NULL));
+ (*share) = talloc_strdup(mem_ctx, torture_setting_string(tctx, "share", NULL));
- p = lp_parm_string(-1, "torture", "unclist");
+ p = torture_setting_string(tctx, "unclist", NULL);
if (!p) {
- return True;
+ return true;
}
- unc_list = file_lines_load(p, &num_unc_names, NULL);
+ unc_list = file_lines_load(p, &num_unc_names, 0, NULL);
if (!unc_list || num_unc_names <= 0) {
DEBUG(0,("Failed to load unc names list from '%s'\n", p));
- return False;
+ return false;
}
- if (!smbcli_parse_unc(unc_list[conn_index % num_unc_names],
- mem_ctx, host, share)) {
+ p = unc_list[conn_index % num_unc_names];
+ if (p[0] != '/' && p[0] != '\\') {
+ /* allow UNC lists of hosts */
+ (*host) = talloc_strdup(mem_ctx, p);
+ } else if (!smbcli_parse_unc(p, mem_ctx, host, share)) {
DEBUG(0, ("Failed to parse UNC name %s\n",
unc_list[conn_index % num_unc_names]));
- return False;
+ return false;
}
talloc_free(unc_list);
- return True;
+ return true;
}
_PUBLIC_ bool torture_open_connection_ev(struct smbcli_state **c,
int conn_index,
- struct event_context *ev)
+ struct torture_context *tctx,
+ struct tevent_context *ev)
{
char *host, *share;
bool ret;
- if (!torture_get_conn_index(conn_index, ev, &host, &share)) {
- return False;
+ if (!torture_get_conn_index(conn_index, ev, tctx, &host, &share)) {
+ return false;
}
- ret = torture_open_connection_share(NULL, c, host, share, ev);
+ ret = torture_open_connection_share(NULL, c, tctx, host, share, ev);
talloc_free(host);
talloc_free(share);
return ret;
}
-_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, int conn_index)
+_PUBLIC_ bool torture_open_connection(struct smbcli_state **c, struct torture_context *tctx, int conn_index)
{
- return torture_open_connection_ev(c, conn_index, NULL);
+ return torture_open_connection_ev(c, conn_index, tctx, tctx->ev);
}
_PUBLIC_ bool torture_close_connection(struct smbcli_state *c)
{
- bool ret = True;
- if (!c) return True;
+ bool ret = true;
+ if (!c) return true;
if (NT_STATUS_IS_ERR(smbcli_tdis(c))) {
printf("tdis failed (%s)\n", smbcli_errstr(c->tree));
- ret = False;
+ ret = false;
}
talloc_free(c);
return ret;
status = smbcli_nt_error(c->tree);
if (NT_STATUS_IS_DOS(status)) {
- int class, num;
- class = NT_STATUS_DOS_CLASS(status);
+ int classnum, num;
+ classnum = NT_STATUS_DOS_CLASS(status);
num = NT_STATUS_DOS_CODE(status);
- if (eclass != class || ecode != num) {
+ if (eclass != classnum || ecode != num) {
printf("unexpected error code %s\n", nt_errstr(status));
printf(" expected %s or %s (at %s)\n",
nt_errstr(NT_STATUS_DOS(eclass, ecode)),
nt_errstr(nterr), location);
- return False;
+ return false;
}
} else {
if (!NT_STATUS_EQUAL(nterr, status)) {
printf("unexpected error code %s\n", nt_errstr(status));
printf(" expected %s (at %s)\n", nt_errstr(nterr), location);
- return False;
+ return false;
}
}
- return True;
+ return true;
}
static struct smbcli_state *current_cli;
{
}
-double torture_create_procs(struct torture_context *tctx,
- bool (*fn)(struct torture_context *, struct smbcli_state *, int), bool *result)
+struct child_status {
+ pid_t pid;
+ bool start;
+ enum torture_result result;
+ char reason[1024];
+};
+
+double torture_create_procs(struct torture_context *tctx,
+ bool (*fn)(struct torture_context *, struct smbcli_state *, int),
+ bool *result)
{
int i, status;
- volatile pid_t *child_status;
- volatile bool *child_status_out;
+ struct child_status *child_status;
int synccount;
int tries = 8;
- int torture_nprocs = lp_parm_int(-1, "torture", "nprocs", 4);
+ int torture_nprocs = torture_setting_int(tctx, "nprocs", 4);
double start_time_limit = 10 + (torture_nprocs * 1.5);
struct timeval tv;
- *result = True;
+ *result = true;
synccount = 0;
signal(SIGCONT, sigcont);
- child_status = (volatile pid_t *)shm_setup(sizeof(pid_t)*torture_nprocs);
- if (!child_status) {
+ child_status = (struct child_status *)anonymous_shared_allocate(
+ sizeof(struct child_status)*torture_nprocs);
+ if (child_status == NULL) {
printf("Failed to setup shared memory\n");
return -1;
}
- child_status_out = (volatile bool *)shm_setup(sizeof(bool)*torture_nprocs);
- if (!child_status_out) {
- printf("Failed to setup result status shared memory\n");
- return -1;
- }
-
for (i = 0; i < torture_nprocs; i++) {
- child_status[i] = 0;
- child_status_out[i] = True;
+ ZERO_STRUCT(child_status[i]);
}
tv = timeval_current();
procnum = i;
if (fork() == 0) {
char *myname;
+ bool ok;
pid_t mypid = getpid();
srandom(((int)mypid) ^ ((int)time(NULL)));
- asprintf(&myname, "CLIENT%d", i);
- lp_set_cmdline("netbios name", myname);
+ if (asprintf(&myname, "CLIENT%d", i) == -1) {
+ printf("asprintf failed\n");
+ return -1;
+ }
+ lpcfg_set_cmdline(tctx->lp_ctx, "netbios name", myname);
free(myname);
while (1) {
- if (torture_open_connection(¤t_cli, i)) {
+ if (torture_open_connection(¤t_cli, tctx, i)) {
break;
}
if (tries-- == 0) {
printf("pid %d failed to start\n", (int)getpid());
_exit(1);
}
- msleep(100);
+ smb_msleep(100);
}
- child_status[i] = getpid();
+ child_status[i].pid = getpid();
pause();
- if (child_status[i]) {
+ if (!child_status[i].start) {
+ child_status[i].result = TORTURE_ERROR;
printf("Child %d failed to start!\n", i);
- child_status_out[i] = 1;
_exit(1);
}
- child_status_out[i] = fn(tctx, current_cli, i);
+ ok = fn(tctx, current_cli, i);
+ if (!ok) {
+ if (tctx->last_result == TORTURE_OK) {
+ torture_result(tctx, TORTURE_ERROR,
+ "unknown error: missing "
+ "torture_result call?\n");
+ }
+
+ child_status[i].result = tctx->last_result;
+
+ if (strlen(tctx->last_reason) > 1023) {
+ /* note: reason already contains \n */
+ torture_comment(tctx,
+ "child %d (pid %u) failed: %s",
+ i,
+ (unsigned)child_status[i].pid,
+ tctx->last_reason);
+ }
+
+ snprintf(child_status[i].reason,
+ 1024, "child %d (pid %u) failed: %s",
+ i, (unsigned)child_status[i].pid,
+ tctx->last_reason);
+ /* ensure proper "\n\0" termination: */
+ if (child_status[i].reason[1022] != '\0') {
+ child_status[i].reason[1022] = '\n';
+ child_status[i].reason[1023] = '\0';
+ }
+ }
_exit(0);
}
}
do {
synccount = 0;
for (i=0;i<torture_nprocs;i++) {
- if (child_status[i]) synccount++;
+ if (child_status[i].pid != 0) {
+ synccount++;
+ }
}
- if (synccount == torture_nprocs) break;
- msleep(100);
+ if (synccount == torture_nprocs) {
+ break;
+ }
+ smb_msleep(100);
} while (timeval_elapsed(&tv) < start_time_limit);
if (synccount != torture_nprocs) {
printf("FAILED TO START %d CLIENTS (started %d)\n", torture_nprocs, synccount);
- *result = False;
+
+ /* cleanup child processes */
+ for (i = 0; i < torture_nprocs; i++) {
+ if (child_status[i].pid != 0) {
+ kill(child_status[i].pid, SIGTERM);
+ }
+ }
+
+ *result = false;
return timeval_elapsed(&tv);
}
/* start the client load */
tv = timeval_current();
for (i=0;i<torture_nprocs;i++) {
- child_status[i] = 0;
+ child_status[i].start = true;
}
printf("%d clients started\n", torture_nprocs);
int ret;
while ((ret=waitpid(0, &status, 0)) == -1 && errno == EINTR) /* noop */ ;
if (ret == -1 || WEXITSTATUS(status) != 0) {
- *result = False;
+ *result = false;
}
}
printf("\n");
-
+
for (i=0;i<torture_nprocs;i++) {
- if (!child_status_out[i]) {
- *result = False;
+ if (child_status[i].result != TORTURE_OK) {
+ *result = false;
+ torture_result(tctx, child_status[i].result,
+ "%s", child_status[i].reason);
}
}
+
return timeval_elapsed(&tv);
}
test->fn = run;
test->dangerous = false;
- DLIST_ADD_END(tcase->tests, test, struct torture_test *);
+ DLIST_ADD_END(tcase->tests, test);
return test;
{
bool (*fn) (struct torture_context *, struct smbcli_state *,
struct smbcli_state *);
- bool ret;
+ bool ret = true;
- struct smbcli_state *cli1, *cli2;
+ struct smbcli_state *cli1 = NULL, *cli2 = NULL;
- if (!torture_open_connection(&cli1, 0) ||
- !torture_open_connection(&cli2, 1))
- return false;
+ torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection");
+ torture_assert_goto(torture_ctx, torture_open_connection(&cli2, torture_ctx, 1), ret, fail, "Failed to open connection");
fn = test->fn;
ret = fn(torture_ctx, cli1, cli2);
-
+fail:
talloc_free(cli1);
talloc_free(cli2);
test->fn = run;
test->dangerous = false;
- DLIST_ADD_END(tcase->tests, test, struct torture_test *);
+ DLIST_ADD_END(tcase->tests, test);
return test;
struct torture_test *test)
{
bool (*fn) (struct torture_context *, struct smbcli_state *);
- bool ret;
+ bool ret = true;
- struct smbcli_state *cli1;
+ struct smbcli_state *cli1 = NULL;
- if (!torture_open_connection(&cli1, 0))
- return false;
+ torture_assert_goto(torture_ctx, torture_open_connection(&cli1, torture_ctx, 0), ret, fail, "Failed to open connection");
fn = test->fn;
ret = fn(torture_ctx, cli1);
-
+fail:
talloc_free(cli1);
return ret;
}
_PUBLIC_ struct torture_test *torture_suite_add_1smb_test(
- struct torture_suite *suite,
- const char *name,
- bool (*run) (struct torture_context *,
- struct smbcli_state *))
+ struct torture_suite *suite,
+ const char *name,
+ bool (*run) (struct torture_context *, struct smbcli_state *))
{
struct torture_test *test;
struct torture_tcase *tcase;
test->fn = run;
test->dangerous = false;
- DLIST_ADD_END(tcase->tests, test, struct torture_test *);
+ DLIST_ADD_END(tcase->tests, test);
return test;
}
+NTSTATUS torture_second_tcon(TALLOC_CTX *mem_ctx,
+ struct smbcli_session *session,
+ const char *sharename,
+ struct smbcli_tree **res)
+{
+ union smb_tcon tcon;
+ struct smbcli_tree *result;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ if ((tmp_ctx = talloc_new(mem_ctx)) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ result = smbcli_tree_init(session, tmp_ctx, false);
+ if (result == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ tcon.generic.level = RAW_TCON_TCONX;
+ tcon.tconx.in.flags = TCONX_FLAG_EXTENDED_RESPONSE;
+ tcon.tconx.in.flags |= TCONX_FLAG_EXTENDED_SIGNATURES;
+
+ /* Ignore share mode security here */
+ tcon.tconx.in.password = data_blob(NULL, 0);
+ tcon.tconx.in.path = sharename;
+ tcon.tconx.in.device = "?????";
+
+ status = smb_raw_tcon(result, tmp_ctx, &tcon);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ result->tid = tcon.tconx.out.tid;
+
+ if (tcon.tconx.out.options & SMB_EXTENDED_SIGNATURES) {
+ smb1cli_session_protect_session_key(result->session->smbXcli);
+ }
+
+ *res = talloc_steal(mem_ctx, result);
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+/*
+ a wrapper around smblsa_sid_check_privilege, that tries to take
+ account of the fact that the lsa privileges calls don't expand
+ group memberships, using an explicit check for administrator. There
+ must be a better way ...
+ */
+NTSTATUS torture_check_privilege(struct smbcli_state *cli,
+ const char *sid_str,
+ const char *privilege)
+{
+ struct dom_sid *sid;
+ TALLOC_CTX *tmp_ctx = talloc_new(cli);
+ uint32_t rid;
+ NTSTATUS status;
+
+ sid = dom_sid_parse_talloc(tmp_ctx, sid_str);
+ if (sid == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_SID;
+ }
+
+ status = dom_sid_split_rid(tmp_ctx, sid, NULL, &rid);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ if (rid == DOMAIN_RID_ADMINISTRATOR) {
+ /* assume the administrator has them all */
+ return NT_STATUS_OK;
+ }
+
+ talloc_free(tmp_ctx);
+
+ return smblsa_sid_check_privilege(cli, sid_str, privilege);
+}