summaryrefslogtreecommitdiff
path: root/source4/torture/basic/denytest.c
diff options
context:
space:
mode:
Diffstat (limited to 'source4/torture/basic/denytest.c')
-rw-r--r--source4/torture/basic/denytest.c755
1 files changed, 752 insertions, 3 deletions
diff --git a/source4/torture/basic/denytest.c b/source4/torture/basic/denytest.c
index bc64d4b2ff..d13ea2c745 100644
--- a/source4/torture/basic/denytest.c
+++ b/source4/torture/basic/denytest.c
@@ -22,7 +22,11 @@
#include "system/filesys.h"
#include "libcli/raw/libcliraw.h"
#include "libcli/libcli.h"
+#include "libcli/security/security.h"
#include "torture/util.h"
+#include "torture/smbtorture.h"
+#include "libcli/util/clilsa.h"
+#include "cxd_known.h"
extern int torture_failures;
@@ -1765,7 +1769,7 @@ static bool torture_ntdenytest(struct torture_context *tctx,
GetTimeOfDay(&tv_start);
io1.ntcreatex.level = RAW_OPEN_NTCREATEX;
- io1.ntcreatex.in.root_fid = 0;
+ io1.ntcreatex.in.root_fid.fnum = 0;
io1.ntcreatex.in.flags = NTCREATEX_FLAGS_EXTENDED;
io1.ntcreatex.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
io1.ntcreatex.in.file_attr = 0;
@@ -1911,10 +1915,19 @@ bool torture_ntdenytest2(struct torture_context *torture,
return torture_ntdenytest(torture, cli1, cli2, 0);
}
+#define COMPARE_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) Incorrect status %s - should be %s\n", \
+ __location__, nt_errstr(status), nt_errstr(correct)); \
+ ret = false; \
+ failed = true; \
+ }} while (0)
#define CHECK_STATUS(status, correct) do { \
if (!NT_STATUS_EQUAL(status, correct)) { \
- torture_comment(tctx, "(%s) Incorrect status %s - should be %s\n", \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) Incorrect status %s - should be %s\n", \
__location__, nt_errstr(status), nt_errstr(correct)); \
ret = false; \
goto done; \
@@ -1922,7 +1935,8 @@ bool torture_ntdenytest2(struct torture_context *torture,
#define CHECK_VAL(v, correct) do { \
if ((v) != (correct)) { \
- torture_comment(tctx, "(%s) wrong value for %s 0x%x - should be 0x%x\n", \
+ torture_result(tctx, TORTURE_FAIL, \
+ "(%s) wrong value for %s 0x%x - should be 0x%x\n", \
__location__, #v, (int)(v), (int)correct); \
ret = false; \
}} while (0)
@@ -2030,4 +2044,739 @@ done:
return ret;
}
+#define CXD_MATCHES(_cxd, i) \
+ ((cxd_known[i].cxd_test == (_cxd)->cxd_test) && \
+ (cxd_known[i].cxd_flags == (_cxd)->cxd_flags) && \
+ (cxd_known[i].cxd_access1 == (_cxd)->cxd_access1) && \
+ (cxd_known[i].cxd_sharemode1 == (_cxd)->cxd_sharemode1) && \
+ (cxd_known[i].cxd_access2 == (_cxd)->cxd_access2) && \
+ (cxd_known[i].cxd_sharemode2 == (_cxd)->cxd_sharemode2))
+
+static int cxd_find_known(struct createx_data *cxd)
+{
+ static int i = -1;
+
+ /* Optimization for tests which we don't have results saved for. */
+ if ((cxd->cxd_test == CXD_TEST_CREATEX_ACCESS_EXHAUSTIVE) ||
+ (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE_EXTENDED))
+ return -1;
+
+ /* Optimization: If our cxd_known table is too large, it hurts test
+ * performance to search through the entire table each time. If the
+ * caller can pass in the previous result, we can try the next entry.
+ * This works if results are taken directly from the same code. */
+ i++;
+ if ((i >= 0) && (i < sizeof(cxd_known) / sizeof(cxd_known[0])) &&
+ CXD_MATCHES(cxd, i))
+ return i;
+
+ for (i = 0; i < (sizeof(cxd_known) / sizeof(cxd_known[0])); i++) {
+ if (CXD_MATCHES(cxd, i))
+ return i;
+ }
+
+ return -1;
+}
+
+#define FILL_NTCREATEX(_struct, _init...) \
+ do { \
+ (_struct)->generic.level = RAW_OPEN_NTCREATEX; \
+ (_struct)->ntcreatex.in \
+ = (typeof((_struct)->ntcreatex.in)) {_init};\
+ } while (0)
+
+#define CREATEX_NAME "\\createx_dir"
+
+static bool createx_make_dir(struct torture_context *tctx,
+ struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, const char *fname)
+{
+ bool ret = true;
+ NTSTATUS status;
+
+ status = smbcli_mkdir(tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ done:
+ return ret;
+}
+
+static bool createx_make_file(struct torture_context *tctx,
+ struct smbcli_tree *tree, TALLOC_CTX *mem_ctx, const char *fname)
+{
+ union smb_open open_parms;
+ bool ret = true;
+ NTSTATUS status;
+
+ FILL_NTCREATEX(&open_parms,
+ .flags = 0,
+ .access_mask = SEC_RIGHTS_FILE_ALL,
+ .file_attr = FILE_ATTRIBUTE_NORMAL,
+ .share_access = 0,
+ .open_disposition = NTCREATEX_DISP_CREATE,
+ .create_options = 0,
+ .fname = fname,
+ );
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ status = smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ done:
+ return ret;
+}
+
+static void createx_fill_dir(union smb_open *open_parms, int accessmode,
+ int sharemode, const char *fname)
+{
+ FILL_NTCREATEX(open_parms,
+ .flags = 0,
+ .access_mask = accessmode,
+ .file_attr = FILE_ATTRIBUTE_DIRECTORY,
+ .share_access = sharemode,
+ .open_disposition = NTCREATEX_DISP_OPEN_IF,
+ .create_options = NTCREATEX_OPTIONS_DIRECTORY,
+ .fname = fname,
+ );
+}
+
+static void createx_fill_file(union smb_open *open_parms, int accessmode,
+ int sharemode, const char *fname)
+{
+ FILL_NTCREATEX(open_parms,
+ .flags = 0,
+ .access_mask = accessmode,
+ .file_attr = FILE_ATTRIBUTE_NORMAL,
+ .share_access = sharemode,
+ .open_disposition = NTCREATEX_DISP_OPEN_IF,
+ .create_options = 0,
+ .fname = fname,
+ .root_fid = { .fnum = 0 },
+ );
+}
+
+static int data_file_fd = -1;
+
+#define KNOWN "known"
+#define CHILD "child"
+static bool createx_test_dir(struct torture_context *tctx,
+ struct smbcli_tree *tree, int fnum, TALLOC_CTX *mem_ctx, NTSTATUS *result)
+{
+ bool ret = true;
+ NTSTATUS status;
+ union smb_open open_parms;
+
+ /* bypass original handle to guarantee creation */
+ FILL_NTCREATEX(&open_parms,
+ .flags = 0,
+ .access_mask = SEC_RIGHTS_FILE_ALL,
+ .file_attr = FILE_ATTRIBUTE_NORMAL,
+ .share_access = 0,
+ .open_disposition = NTCREATEX_DISP_CREATE,
+ .create_options = 0,
+ .fname = CREATEX_NAME "\\" KNOWN,
+ );
+ status = smb_raw_open(tree, mem_ctx, &open_parms);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
+
+ result[CXD_DIR_ENUMERATE] = NT_STATUS_OK;
+
+ /* try to create a child */
+ FILL_NTCREATEX(&open_parms,
+ .flags = 0,
+ .access_mask = SEC_RIGHTS_FILE_ALL,
+ .file_attr = FILE_ATTRIBUTE_NORMAL,
+ .share_access = 0,
+ .open_disposition = NTCREATEX_DISP_CREATE,
+ .create_options = 0,
+ .fname = CHILD,
+ .root_fid = { .fnum = fnum },
+ );
+
+ result[CXD_DIR_CREATE_CHILD] =
+ smb_raw_open(tree, mem_ctx, &open_parms);
+ smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
+
+ /* try to traverse dir to known good file */
+ FILL_NTCREATEX(&open_parms,
+ .flags = 0,
+ .access_mask = SEC_RIGHTS_FILE_ALL,
+ .file_attr = FILE_ATTRIBUTE_NORMAL,
+ .share_access = 0,
+ .open_disposition = NTCREATEX_DISP_OPEN,
+ .create_options = 0,
+ .fname = KNOWN,
+ .root_fid = {.fnum = fnum},
+ );
+
+ result[CXD_DIR_TRAVERSE] =
+ smb_raw_open(tree, mem_ctx, &open_parms);
+
+
+ smbcli_close(tree, open_parms.ntcreatex.out.file.fnum);
+ smbcli_unlink(tree, CREATEX_NAME "\\" KNOWN);
+ smbcli_unlink(tree, CREATEX_NAME "\\" CHILD);
+
+ done:
+ return ret;
+}
+
+static bool createx_test_file(struct torture_context *tctx,
+ struct smbcli_tree *tree, int fnum, TALLOC_CTX *mem_ctx, NTSTATUS *result)
+{
+ union smb_read rd = {};
+ union smb_write wr = {};
+ char buf[256] = "";
+
+ rd.readx.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = sizeof(buf);
+ rd.readx.in.maxcnt = sizeof(buf);
+ rd.readx.out.data = (uint8_t *)buf;
+
+ result[CXD_FILE_READ] = smb_raw_read(tree, &rd);
+
+ wr.writex.level = RAW_WRITE_WRITEX;
+ wr.writex.in.file.fnum = fnum;
+ wr.writex.in.count = sizeof(buf);
+ wr.writex.in.data = (uint8_t *)buf;
+
+ result[CXD_FILE_WRITE] = smb_raw_write(tree, &wr);
+
+ memset(&rd, 0, sizeof(rd));
+ rd.readx.level = RAW_READ_READX;
+ rd.readx.in.file.fnum = fnum;
+ rd.readx.in.mincnt = sizeof(buf);
+ rd.readx.in.maxcnt = sizeof(buf);
+ rd.readx.in.read_for_execute = 1;
+ rd.readx.out.data = (uint8_t *)buf;
+
+ result[CXD_FILE_EXECUTE] = smb_raw_read(tree, &rd);
+
+ return true;
+}
+
+/* TODO When redirecting stdout to a file, the progress bar really screws up
+ * the output. Could use a switch "--noprogress", or direct the progress bar to
+ * stderr? No other solution? */
+static void createx_progress_bar(struct torture_context *tctx, uint_t i,
+ uint_t total, uint_t skipped)
+{
+ if (torture_setting_bool(tctx, "progress", true)) {
+ torture_comment(tctx, "%5d/%5d (%d skipped)\r", i, total,
+ skipped);
+ fflush(stdout);
+ }
+}
+
+static bool torture_createx_specific(struct torture_context *tctx, struct
+ smbcli_state *cli, struct smbcli_state *cli2, TALLOC_CTX *mem_ctx, struct
+ createx_data *cxd, int estimated_count)
+{
+ static int call_count = 1;
+ static int unskipped_call_count = 1;
+ const char *fname = CREATEX_NAME;
+ int fnum = -1, fnum2 = -1, res, i;
+ union smb_open open_parms1, open_parms2;
+ bool ret = true;
+ bool is_dir = cxd->cxd_flags & CXD_FLAGS_DIRECTORY;
+ NTSTATUS *result = &cxd->cxd_result[0];
+ NTSTATUS *result2 = &cxd->cxd_result2[0];
+ bool found = false, failed = false;
+
+ bool (*make_func)(struct torture_context *,
+ struct smbcli_tree *, TALLOC_CTX *, const char *);
+ void (*fill_func)(union smb_open *, int, int, const char *);
+ bool (*test_func)(struct torture_context *,
+ struct smbcli_tree *, int, TALLOC_CTX *, NTSTATUS *);
+ NTSTATUS (*destroy_func)(struct smbcli_tree *, const char *);
+
+ if (is_dir) {
+ make_func = createx_make_dir;
+ fill_func = createx_fill_dir;
+ test_func = createx_test_dir;
+ destroy_func = smbcli_rmdir;
+ } else {
+ make_func = createx_make_file;
+ fill_func = createx_fill_file;
+ test_func = createx_test_file;
+ destroy_func = smbcli_unlink;
+ }
+
+ /* Skip all SACL related tests. */
+ if ((!torture_setting_bool(tctx, "sacl_support", true)) &&
+ ((cxd->cxd_access1 & SEC_FLAG_SYSTEM_SECURITY) ||
+ (cxd->cxd_access2 & SEC_FLAG_SYSTEM_SECURITY)))
+ goto done;
+
+ if (cxd->cxd_flags & CXD_FLAGS_MAKE_BEFORE_CREATEX) {
+ ret = make_func(tctx, cli->tree, mem_ctx, fname);
+ if (!ret) {
+ torture_result(tctx, TORTURE_FAIL,
+ "Initial creation failed\n");
+ goto done;
+ }
+ }
+
+ /* Initialize. */
+ fill_func(&open_parms1, cxd->cxd_access1, cxd->cxd_sharemode1, fname);
+
+ if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) {
+ fill_func(&open_parms2, cxd->cxd_access2, cxd->cxd_sharemode2,
+ fname);
+ }
+
+ for (i = CXD_CREATEX + 1; i < CXD_MAX; i++) {
+ result[i] = NT_STATUS_UNSUCCESSFUL;
+ result2[i] = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Perform open(s). */
+ result[CXD_CREATEX] = smb_raw_open(cli->tree, mem_ctx, &open_parms1);
+ if (NT_STATUS_IS_OK(result[CXD_CREATEX])) {
+ fnum = open_parms1.ntcreatex.out.file.fnum;
+ ret = test_func(tctx, cli->tree, fnum, mem_ctx, result);
+ smbcli_close(cli->tree, fnum);
+ }
+
+ if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) {
+ result2[CXD_CREATEX] = smb_raw_open(cli2->tree, mem_ctx,
+ &open_parms2);
+ if (NT_STATUS_IS_OK(result2[CXD_CREATEX])) {
+ fnum2 = open_parms2.ntcreatex.out.file.fnum;
+ ret = test_func(tctx, cli2->tree, fnum2, mem_ctx,
+ result2);
+ smbcli_close(cli2->tree, fnum2);
+ }
+ }
+
+ if (data_file_fd >= 0) {
+ found = true;
+ res = write(data_file_fd, &cxd, sizeof(cxd));
+ if (res != sizeof(cxd)) {
+ torture_result(tctx, TORTURE_FAIL,
+ "(%s): write failed: %s!",
+ __location__, strerror(errno));
+ ret = false;
+ }
+ } else if ((res = cxd_find_known(cxd)) >= 0) {
+ found = true;
+ for (i = 0; i < CXD_MAX; i++) {
+ /* Note: COMPARE_STATUS will set the "failed" bool. */
+ COMPARE_STATUS(result[i], cxd_known[res].cxd_result[i]);
+ if (i == 0 && !NT_STATUS_IS_OK(result[i]))
+ break;
+
+ if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) {
+ COMPARE_STATUS(result2[i],
+ cxd_known[res].cxd_result2[i]);
+ if (i == 0 && !NT_STATUS_IS_OK(result2[i]))
+ break;
+ }
+ }
+ }
+
+ /* We print if its not in the "cxd_known" list or if we fail. */
+ if (!found || failed) {
+ torture_comment(tctx,
+ " { .cxd_test = %d, .cxd_flags = %#3x, "
+ ".cxd_access1 = %#10x, .cxd_sharemode1=%1x, "
+ ".cxd_access2=%#10x, .cxd_sharemode2=%1x, "
+ ".cxd_result = { ", cxd->cxd_test, cxd->cxd_flags,
+ cxd->cxd_access1, cxd->cxd_sharemode1, cxd->cxd_access2,
+ cxd->cxd_sharemode2);
+ for (i = 0; i < CXD_MAX; i++) {
+ torture_comment(tctx, "%s, ", nt_errstr(result[i]));
+ if (i == 0 && !NT_STATUS_IS_OK(result[i]))
+ break;
+ }
+ torture_comment(tctx, "}");
+ if (cxd->cxd_test == CXD_TEST_CREATEX_SHAREMODE) {
+ torture_comment(tctx, ", .cxd_result2 = { ");
+ for (i = 0; i < CXD_MAX; i++) {
+ torture_comment(tctx, "%s, ",
+ nt_errstr(result2[i]));
+ if (i == 0 && !NT_STATUS_IS_OK(result2[i]))
+ break;
+ }
+ torture_comment(tctx, "}");
+ }
+ torture_comment(tctx, "}, \n");
+ } else {
+ createx_progress_bar(tctx, call_count, estimated_count,
+ call_count - unskipped_call_count);
+ }
+ /* Count tests that we didn't skip. */
+ unskipped_call_count++;
+ done:
+ call_count++;
+
+ destroy_func(cli->tree, fname);
+ return ret;
+}
+
+uint32_t sec_access_bit_groups[] = {
+ SEC_RIGHTS_FILE_READ,
+ SEC_RIGHTS_FILE_WRITE,
+ SEC_RIGHTS_FILE_EXECUTE
+};
+#define NUM_ACCESS_GROUPS (sizeof(sec_access_bit_groups) / sizeof(uint32_t))
+#define ACCESS_GROUPS_COUNT ((1 << NUM_ACCESS_GROUPS))
+#define BITSINBYTE 8
+
+/* Note: See NTCREATEX_SHARE_ACCESS_{NONE,READ,WRITE,DELETE} for share mode
+ * declarations. */
+#define NUM_SHAREMODE_PERMUTATIONS 8
+
+/**
+ * NTCREATEX and SHARE MODE test.
+ *
+ * Open with combinations of (access_mode, share_mode).
+ * - Check status
+ * Open 2nd time with combination of (access_mode2, share_mode2).
+ * - Check status
+ * Perform operations to verify?
+ * - Read
+ * - Write
+ * - Delete
+ */
+bool torture_createx_sharemodes(struct torture_context *tctx,
+ struct smbcli_state *cli,
+ struct smbcli_state *cli2,
+ bool dir,
+ bool extended)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ int i, j, est;
+ int gp1, gp2; /* group permuters */
+ struct createx_data cxd = {0};
+ int num_access_bits1 = sizeof(cxd.cxd_access1) * BITSINBYTE;
+ int num_access_bits2 = sizeof(cxd.cxd_access2) * BITSINBYTE;
+
+ mem_ctx = talloc_init("createx_sharemodes");
+ if (!mem_ctx)
+ return false;
+
+ if (!torture_setting_bool(tctx, "sacl_support", true))
+ torture_warning(tctx, "Skipping SACL related tests!\n");
+
+ cxd.cxd_test = extended ? CXD_TEST_CREATEX_SHAREMODE_EXTENDED :
+ CXD_TEST_CREATEX_SHAREMODE;
+ cxd.cxd_flags = dir ? CXD_FLAGS_DIRECTORY: 0;
+
+ /* HACK for progress bar: figure out estimated count. */
+ est = (NUM_SHAREMODE_PERMUTATIONS * NUM_SHAREMODE_PERMUTATIONS) *
+ ((ACCESS_GROUPS_COUNT * ACCESS_GROUPS_COUNT) +
+ (extended ? num_access_bits1 * num_access_bits2 : 0));
+
+ /* Blank slate. */
+ smbcli_deltree(cli->tree, CREATEX_NAME);
+ smbcli_unlink(cli->tree, CREATEX_NAME);
+
+ /* Choose 2 random share modes. */
+ for (cxd.cxd_sharemode1 = 0;
+ cxd.cxd_sharemode1 < NUM_SHAREMODE_PERMUTATIONS;
+ cxd.cxd_sharemode1++) {
+ for (cxd.cxd_sharemode2 = 0;
+ cxd.cxd_sharemode2 < NUM_SHAREMODE_PERMUTATIONS;
+ cxd.cxd_sharemode2++) {
+
+ /* Permutate through our access_bit_groups. */
+ for (gp1 = 0; gp1 < ACCESS_GROUPS_COUNT; gp1++) {
+ for (gp2 = 0; gp2 < ACCESS_GROUPS_COUNT; gp2++)
+ {
+ cxd.cxd_access1 = cxd.cxd_access2 = 0;
+
+ for (i = 0; i < NUM_ACCESS_GROUPS; i++)
+ {
+ cxd.cxd_access1 |=
+ (gp1 & (1 << i)) ?
+ sec_access_bit_groups[i]:0;
+ cxd.cxd_access2 |=
+ (gp2 & (1 << i)) ?
+ sec_access_bit_groups[i]:0;
+ }
+
+ torture_createx_specific(tctx, cli,
+ cli2, mem_ctx, &cxd, est);
+ }
+ }
+
+ /* Only do the single access bits on an extended run. */
+ if (!extended)
+ continue;
+
+ for (i = 0; i < num_access_bits1; i++) {
+ for (j = 0; j < num_access_bits2; j++) {
+ cxd.cxd_access1 = 1ull << i;
+ cxd.cxd_access2 = 1ull << j;
+
+ torture_createx_specific(tctx, cli,
+ cli2, mem_ctx, &cxd, est);
+ }
+ }
+ }
+ }
+ torture_comment(tctx, "\n");
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+bool torture_createx_sharemodes_file(struct torture_context *tctx,
+ struct smbcli_state *cli, struct smbcli_state *cli2)
+{
+ return torture_createx_sharemodes(tctx, cli, cli2, false, false);
+}
+
+bool torture_createx_sharemodes_dir(struct torture_context *tctx,
+ struct smbcli_state *cli, struct smbcli_state *cli2)
+{
+ return torture_createx_sharemodes(tctx, cli, cli2, true, false);
+}
+
+bool torture_createx_access(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ TALLOC_CTX *mem_ctx;
+ bool ret = true;
+ uint32_t group_permuter;
+ uint32_t i;
+ struct createx_data cxd = {0};
+ int est;
+ int num_access_bits = sizeof(cxd.cxd_access1) * BITSINBYTE;
+
+ mem_ctx = talloc_init("createx_dir");
+ if (!mem_ctx)
+ return false;
+
+ if (!torture_setting_bool(tctx, "sacl_support", true))
+ torture_warning(tctx, "Skipping SACL related tests!\n");
+
+ cxd.cxd_test = CXD_TEST_CREATEX_ACCESS;
+
+ /* HACK for progress bar: figure out estimated count. */
+ est = CXD_FLAGS_COUNT * (ACCESS_GROUPS_COUNT + (num_access_bits * 3));
+
+ /* Blank slate. */
+ smbcli_deltree(cli->tree, CREATEX_NAME);
+ smbcli_unlink(cli->tree, CREATEX_NAME);
+
+ for (cxd.cxd_flags = 0; cxd.cxd_flags <= CXD_FLAGS_MASK;
+ cxd.cxd_flags++) {
+ /**
+ * This implements a basic permutation of all elements of
+ * 'bit_group'. group_permuter is a bit field representing
+ * which groups to turn on.
+ */
+ for (group_permuter = 0; group_permuter < (1 <<
+ NUM_ACCESS_GROUPS); group_permuter++) {
+ for (i = 0, cxd.cxd_access1 = 0;
+ i < NUM_ACCESS_GROUPS; i++) {
+ cxd.cxd_access1 |= (group_permuter & (1 << i))
+ ? sec_access_bit_groups[i] : 0;
+ }
+ torture_createx_specific(tctx, cli, NULL, mem_ctx,
+ &cxd, est);
+ }
+ for (i = 0; i < num_access_bits; i++) {
+ /* And now run through the single access bits. */
+ cxd.cxd_access1 = 1 << i;
+ torture_createx_specific(tctx, cli, NULL, mem_ctx,
+ &cxd, est);
+
+ /* Does SEC_FLAG_MAXIMUM_ALLOWED override? */
+ cxd.cxd_access1 |= SEC_FLAG_MAXIMUM_ALLOWED;
+ torture_createx_specific(tctx, cli, NULL, mem_ctx,
+ &cxd, est);
+
+ /* What about SEC_FLAG_SYSTEM_SECURITY? */
+ cxd.cxd_access1 |= SEC_FLAG_SYSTEM_SECURITY;
+ torture_createx_specific(tctx, cli, NULL, mem_ctx,
+ &cxd, est);
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+#define ACCESS_KNOWN_MASK 0xF31F01FFull
+
+bool torture_createx_access_exhaustive(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ char *data_file;
+ TALLOC_CTX *mem_ctx;
+ bool ret = true, first;
+ uint32_t i;
+ struct createx_data cxd = {0};
+
+ mem_ctx = talloc_init("createx_dir");
+ if (!mem_ctx)
+ return false;
+
+ if (!torture_setting_bool(tctx, "sacl_support", true))
+ torture_warning(tctx, "Skipping SACL related tests!\n");
+
+ data_file = getenv("CREATEX_DATA");
+ if (data_file) {
+ data_file_fd = open(data_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (data_file_fd < 0) {
+ torture_result(tctx, TORTURE_FAIL,
+ "(%s): data file open failedu: %s!",
+ __location__, strerror(errno));
+ ret = false;
+ goto done;
+ }
+ }
+
+ /* Blank slate. */
+ smbcli_deltree(cli->tree, CREATEX_NAME);
+ smbcli_unlink(cli->tree, CREATEX_NAME);
+
+ cxd.cxd_test = CXD_TEST_CREATEX_ACCESS_EXHAUSTIVE;
+
+ for (cxd.cxd_flags = 0; cxd.cxd_flags <= CXD_FLAGS_MASK;
+ cxd.cxd_flags++) {
+ for (i = 0, first = true; (i != 0) || first; first = false,
+ i = ((i | ~ACCESS_KNOWN_MASK) + 1) & ACCESS_KNOWN_MASK) {
+ cxd.cxd_access1 = i;
+ ret = torture_createx_specific(tctx, cli, NULL,
+ mem_ctx, &cxd, 0);
+ if (!ret)
+ break;
+ }
+ }
+
+ close(data_file_fd);
+ data_file_fd = -1;
+
+ done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+#define MAXIMUM_ALLOWED_FILE "torture_maximum_allowed"
+bool torture_maximum_allowed(struct torture_context *tctx,
+ struct smbcli_state *cli)
+{
+ struct security_descriptor *sd, *sd_orig;
+ union smb_open io = {};
+ static TALLOC_CTX *mem_ctx;
+ int fnum, i;
+ bool ret = true;
+ NTSTATUS status;
+ union smb_fileinfo q;
+ const char *owner_sid;
+ bool has_restore_privilege, has_backup_privilege;
+
+ mem_ctx = talloc_init("torture_maximum_allowed");
+
+ if (!torture_setting_bool(tctx, "sacl_support", true))
+ torture_warning(tctx, "Skipping SACL related tests!\n");
+
+ sd = security_descriptor_dacl_create(mem_ctx,
+ 0, NULL, NULL,
+ SID_NT_AUTHENTICATED_USERS,
+ SEC_ACE_TYPE_ACCESS_ALLOWED,
+ SEC_RIGHTS_FILE_READ,
+ 0, NULL);
+
+ /* Blank slate */
+ smbcli_unlink(cli->tree, MAXIMUM_ALLOWED_FILE);
+
+ /* create initial file with restrictive SD */
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io.ntcreatex.in.impersonation = NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.fname = MAXIMUM_ALLOWED_FILE;
+ io.ntcreatex.in.sec_desc = sd;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ fnum = io.ntcreatex.out.file.fnum;
+
+ /* the correct answers for this test depends on whether the
+ user has restore privileges. To find that out we first need
+ to know our SID - get it from the owner_sid of the file we
+ just created */
+ q.query_secdesc.level = RAW_FILEINFO_SEC_DESC;
+ q.query_secdesc.in.file.fnum = fnum;
+ q.query_secdesc.in.secinfo_flags = SECINFO_DACL | SECINFO_OWNER;
+ status = smb_raw_fileinfo(cli->tree, tctx, &q);
+ CHECK_STATUS(status, NT_STATUS_OK);
+ sd_orig = q.query_secdesc.out.sd;
+
+ owner_sid = dom_sid_string(tctx, sd_orig->owner_sid);
+
+ status = torture_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_RESTORE));
+ has_restore_privilege = NT_STATUS_IS_OK(status);
+ torture_comment(tctx, "Checked SEC_PRIV_RESTORE for %s - %s\n",
+ owner_sid,
+ has_restore_privilege?"Yes":"No");
+
+ status = torture_check_privilege(cli,
+ owner_sid,
+ sec_privilege_name(SEC_PRIV_BACKUP));
+ has_backup_privilege = NT_STATUS_IS_OK(status);
+ torture_comment(tctx, "Checked SEC_PRIV_BACKUP for %s - %s\n",
+ owner_sid,
+ has_backup_privilege?"Yes":"No");
+
+ smbcli_close(cli->tree, fnum);
+
+ for (i = 0; i < 32; i++) {
+ uint32_t mask = SEC_FLAG_MAXIMUM_ALLOWED | (1u << i);
+ uint32_t ok_mask = SEC_RIGHTS_FILE_READ | SEC_GENERIC_READ |
+ SEC_STD_DELETE | SEC_STD_WRITE_DAC;
+
+ if (has_restore_privilege) {
+ ok_mask |= SEC_RIGHTS_PRIV_RESTORE;
+ }
+ if (has_backup_privilege) {
+ ok_mask |= SEC_RIGHTS_PRIV_BACKUP;
+ }
+
+ /* Skip all SACL related tests. */
+ if ((!torture_setting_bool(tctx, "sacl_support", true)) &&
+ (mask & SEC_FLAG_SYSTEM_SECURITY))
+ continue;
+
+ memset(&io, 0, sizeof(io));
+ io.generic.level = RAW_OPEN_NTTRANS_CREATE;
+ io.ntcreatex.in.access_mask = mask;
+ io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
+ io.ntcreatex.in.open_disposition = NTCREATEX_DISP_OPEN;
+ io.ntcreatex.in.impersonation =
+ NTCREATEX_IMPERSONATION_ANONYMOUS;
+ io.ntcreatex.in.fname = MAXIMUM_ALLOWED_FILE;
+
+ status = smb_raw_open(cli->tree, mem_ctx, &io);
+ if (mask & ok_mask ||
+ mask == SEC_FLAG_MAXIMUM_ALLOWED) {
+ CHECK_STATUS(status, NT_STATUS_OK);
+ } else {
+ if (mask & SEC_FLAG_SYSTEM_SECURITY) {
+ CHECK_STATUS(status, NT_STATUS_PRIVILEGE_NOT_HELD);
+ } else {
+ CHECK_STATUS(status, NT_STATUS_ACCESS_DENIED);
+ }
+ }
+
+ fnum = io.ntcreatex.out.file.fnum;
+
+ smbcli_close(cli->tree, fnum);
+ }
+
+ done:
+ smbcli_unlink(cli->tree, MAXIMUM_ALLOWED_FILE);
+ return ret;
+}