/* Unix SMB/CIFS implementation. Copyright (C) Ralph Boehme 2020 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "includes.h" #include "torture/proto.h" #include "libcli/security/security.h" #include "libsmb/libsmb.h" #include "libsmb/clirap.h" #include "libsmb/proto.h" #include "../libcli/smb/smbXcli_base.h" #include "util_sd.h" #include "trans2.h" extern struct cli_credentials *torture_creds; extern fstring host, workgroup, share, password, username, myname; struct posix_test_entry { const char *name; const char *target; const char *expected; uint32_t attr_win; uint32_t attr_lin; uint64_t returned_size; bool ok; }; enum client_flavour { WINDOWS, POSIX }; struct posix_test_state { enum client_flavour flavour; struct posix_test_entry *entries; }; static NTSTATUS posix_ls_fn(struct file_info *finfo, const char *name, void *_state) { struct posix_test_state *state = (struct posix_test_state *)_state; struct posix_test_entry *e = state->entries; for (; e->name != NULL; e++) { uint32_t attr; if (!strequal(finfo->name, e->expected)) { continue; } if (state->flavour == WINDOWS) { attr = e->attr_win; } else { attr = e->attr_lin; } if (attr != finfo->attr) { break; } e->ok = true; e->returned_size = finfo->size; break; } return NT_STATUS_OK; } static void posix_test_entries_reset(struct posix_test_state *state) { struct posix_test_entry *e = state->entries; for (; e->name != NULL; e++) { e->ok = false; e->returned_size = 0; } } static bool posix_test_entry_check(struct posix_test_state *state, const char *name, bool expected, uint64_t expected_size) { struct posix_test_entry *e = state->entries; bool result = false; for (; e->name != NULL; e++) { if (strequal(name, e->name)) { result = e->ok; break; } } if (e->name == NULL) { printf("test failed, unknown name: %s\n", name); return false; } if (expected == result) { return true; } printf("test failed, %s: %s\n", expected ? "missing" : "unexpected", name); return false; } /* Test non-POSIX vs POSIX ls * of symlinks */ bool run_posix_ls_wildcard_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; struct cli_state *cli_win = NULL; uint16_t fnum = (uint16_t)-1; NTSTATUS status; const char *file = "file"; const char *symlnk_dangling = "dangling"; const char *symlnk_dst_dangling = "xxxxxxx"; const char *symlnk_in_share = "symlnk_in_share"; const char *symlnk_dst_in_share = file; const char *symlnk_outside_share = "symlnk_outside_share"; const char *symlnk_dst_outside_share = "/etc/passwd"; struct posix_test_entry entries[] = { { .name = file, .target = NULL, .expected = file, .attr_win = FILE_ATTRIBUTE_ARCHIVE, .attr_lin = FILE_ATTRIBUTE_ARCHIVE, }, { .name = symlnk_dangling, .target = symlnk_dst_dangling, .expected = symlnk_dangling, .attr_win = FILE_ATTRIBUTE_INVALID, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = symlnk_in_share, .target = symlnk_dst_in_share, .expected = symlnk_in_share, .attr_win = FILE_ATTRIBUTE_ARCHIVE, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = symlnk_outside_share, .target = symlnk_dst_outside_share, .expected = symlnk_outside_share, .attr_win = FILE_ATTRIBUTE_INVALID, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = NULL, } }; struct posix_test_state _state = { .entries = entries, }; struct posix_test_state *state = &_state; int i; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-LS-WILDCARD test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } if (!torture_open_connection(&cli_win, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); torture_conn_set_sockopt(cli_win); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); status = cli_posix_open(cli_unix, file, O_RDWR|O_CREAT, 0666, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", file, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; for (i = 0; entries[i].name != NULL; i++) { if (entries[i].target == NULL) { continue; } status = cli_posix_symlink(cli_unix, entries[i].target, entries[i].name); if (!NT_STATUS_IS_OK(status)) { printf("POSIX symlink of %s failed (%s)\n", symlnk_dangling, nt_errstr(status)); goto out; } } printf("Doing Windows ls *\n"); state->flavour = WINDOWS; status = cli_list(cli_win, "*", 0, posix_ls_fn, state); if (!NT_STATUS_IS_OK(status)) { printf("cli_list failed %s\n", nt_errstr(status)); goto out; } if (!posix_test_entry_check(state, file, true, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) { goto out; } posix_test_entries_reset(state); printf("Doing POSIX ls *\n"); state->flavour = POSIX; status = cli_list(cli_unix, "*", 0, posix_ls_fn, state); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } if (!posix_test_entry_check(state, file, true, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_dangling, true, strlen(symlnk_dst_dangling))) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, true, strlen(symlnk_dst_outside_share))) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, strlen(symlnk_dst_in_share))) { goto out; } printf("POSIX-LS-WILDCARD test passed\n"); correct = true; out: cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); if (!torture_close_connection(cli_unix)) { correct = false; } if (!torture_close_connection(cli_win)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Test non-POSIX vs POSIX ls single of symlinks */ bool run_posix_ls_single_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; struct cli_state *cli_win = NULL; uint16_t fnum = (uint16_t)-1; NTSTATUS status; const char *file = "file"; const char *symlnk_dangling = "dangling"; const char *symlnk_dst_dangling = "xxxxxxx"; const char *symlnk_in_share = "symlnk_in_share"; const char *symlnk_dst_in_share = file; const char *symlnk_outside_share = "symlnk_outside_share"; const char *symlnk_dst_outside_share = "/etc/passwd"; struct posix_test_entry entries[] = { { .name = file, .target = NULL, .expected = file, .attr_win = FILE_ATTRIBUTE_ARCHIVE, .attr_lin = FILE_ATTRIBUTE_ARCHIVE, }, { .name = symlnk_dangling, .target = symlnk_dst_dangling, .expected = symlnk_dangling, .attr_win = FILE_ATTRIBUTE_INVALID, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = symlnk_in_share, .target = symlnk_dst_in_share, .expected = symlnk_in_share, .attr_win = FILE_ATTRIBUTE_ARCHIVE, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = symlnk_outside_share, .target = symlnk_dst_outside_share, .expected = symlnk_outside_share, .attr_win = FILE_ATTRIBUTE_INVALID, .attr_lin = FILE_ATTRIBUTE_NORMAL, }, { .name = NULL, } }; struct posix_test_state _state = { .entries = &entries[0], }; struct posix_test_state *state = &_state; int i; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-LS-SINGLE test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } if (!torture_init_connection(&cli_win)) { TALLOC_FREE(frame); return false; } status = smbXcli_negprot(cli_win->conn, cli_win->timeout, lp_client_min_protocol(), lp_client_max_protocol(), NULL, NULL, NULL); if (!NT_STATUS_IS_OK(status)) { printf("smbXcli_negprot returned %s\n", nt_errstr(status)); TALLOC_FREE(frame); return false; } status = cli_session_setup_creds(cli_win, torture_creds); if (!NT_STATUS_IS_OK(status)) { printf("smb2cli_sesssetup returned %s\n", nt_errstr(status)); TALLOC_FREE(frame); return false; } status = cli_tree_connect(cli_win, share, "?????", NULL); if (!NT_STATUS_IS_OK(status)) { printf("cli_tree_connect returned %s\n", nt_errstr(status)); TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); torture_conn_set_sockopt(cli_win); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); status = cli_posix_open(cli_unix, file, O_RDWR|O_CREAT, 0666, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", file, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; for (i = 0; entries[i].name != NULL; i++) { if (entries[i].target == NULL) { continue; } status = cli_posix_symlink(cli_unix, entries[i].target, entries[i].name); if (!NT_STATUS_IS_OK(status)) { printf("POSIX symlink of %s failed (%s)\n", symlnk_dangling, nt_errstr(status)); goto out; } } printf("Doing Windows ls single\n"); state->flavour = WINDOWS; cli_list(cli_win, file, 0, posix_ls_fn, state); cli_list(cli_win, symlnk_dangling, 0, posix_ls_fn, state); cli_list(cli_win, symlnk_outside_share, 0, posix_ls_fn, state); cli_list(cli_win, symlnk_in_share, 0, posix_ls_fn, state); if (!posix_test_entry_check(state, file, true, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_dangling, false, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, false, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, 0)) { goto out; } posix_test_entries_reset(state); printf("Doing POSIX ls single\n"); state->flavour = POSIX; cli_list(cli_unix, file, 0, posix_ls_fn, state); cli_list(cli_unix, symlnk_dangling, 0, posix_ls_fn, state); cli_list(cli_unix, symlnk_outside_share, 0, posix_ls_fn, state); cli_list(cli_unix, symlnk_in_share, 0, posix_ls_fn, state); if (!posix_test_entry_check(state, file, true, 0)) { goto out; } if (!posix_test_entry_check(state, symlnk_dangling, true, strlen(symlnk_dst_dangling))) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, true, strlen(symlnk_dst_outside_share))) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, strlen(symlnk_dst_in_share))) { goto out; } printf("POSIX-LS-SINGLE test passed\n"); correct = true; out: cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); if (!torture_close_connection(cli_unix)) { correct = false; } if (!torture_close_connection(cli_win)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Test POSIX readlink of symlinks */ bool run_posix_readlink_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; uint16_t fnum = (uint16_t)-1; NTSTATUS status; const char *file = "file"; const char *symlnk_dangling = "dangling"; const char *symlnk_dst_dangling = "xxxxxxx"; const char *symlnk_in_share = "symlnk_in_share"; const char *symlnk_dst_in_share = file; const char *symlnk_outside_share = "symlnk_outside_share"; const char *symlnk_dst_outside_share = "/etc/passwd"; struct posix_test_entry entries[] = { { .name = symlnk_dangling, .target = symlnk_dst_dangling, .expected = symlnk_dangling, }, { .name = symlnk_in_share, .target = symlnk_dst_in_share, .expected = symlnk_in_share, }, { .name = symlnk_outside_share, .target = symlnk_dst_outside_share, .expected = symlnk_outside_share, }, { .name = NULL, } }; struct posix_test_state _state = { .entries = &entries[0], }; struct posix_test_state *state = &_state; int i; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-READLINK test\n"); state->flavour = POSIX; if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); status = cli_posix_open(cli_unix, file, O_RDWR|O_CREAT, 0666, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", file, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; for (i = 0; entries[i].name != NULL; i++) { status = cli_posix_symlink(cli_unix, entries[i].target, entries[i].name); if (!NT_STATUS_IS_OK(status)) { printf("POSIX symlink of %s failed (%s)\n", symlnk_dangling, nt_errstr(status)); goto out; } } for (i = 0; entries[i].name != NULL; i++) { char *target = NULL; status = cli_readlink( cli_unix, entries[i].name, talloc_tos(), &target, NULL, NULL); if (!NT_STATUS_IS_OK(status)) { printf("POSIX readlink on %s failed (%s)\n", entries[i].name, nt_errstr(status)); goto out; } if (strequal(target, entries[i].target)) { entries[i].ok = true; entries[i].returned_size = strlen(target); } } if (!posix_test_entry_check(state, symlnk_dangling, true, strlen(symlnk_dst_dangling))) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, true, strlen(symlnk_dst_outside_share))) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, strlen(symlnk_dst_in_share))) { goto out; } printf("POSIX-READLINK test passed\n"); correct = true; out: cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Test POSIX stat of symlinks */ bool run_posix_stat_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; uint16_t fnum = (uint16_t)-1; NTSTATUS status; const char *file = "file"; const char *symlnk_dangling = "dangling"; const char *symlnk_dst_dangling = "xxxxxxx"; const char *symlnk_in_share = "symlnk_in_share"; const char *symlnk_dst_in_share = file; const char *symlnk_outside_share = "symlnk_outside_share"; const char *symlnk_dst_outside_share = "/etc/passwd"; struct posix_test_entry entries[] = { { .name = symlnk_dangling, .target = symlnk_dst_dangling, .expected = symlnk_dangling, }, { .name = symlnk_in_share, .target = symlnk_dst_in_share, .expected = symlnk_in_share, }, { .name = symlnk_outside_share, .target = symlnk_dst_outside_share, .expected = symlnk_outside_share, }, { .name = NULL, } }; struct posix_test_state _state = { .entries = &entries[0], }; struct posix_test_state *state = &_state; int i; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-STAT test\n"); state->flavour = POSIX; if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); status = cli_posix_open(cli_unix, file, O_RDWR|O_CREAT, 0666, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", file, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; for (i = 0; entries[i].name != NULL; i++) { status = cli_posix_symlink(cli_unix, entries[i].target, entries[i].name); if (!NT_STATUS_IS_OK(status)) { printf("POSIX symlink of %s failed (%s)\n", symlnk_dangling, nt_errstr(status)); goto out; } } for (i = 0; entries[i].name != NULL; i++) { SMB_STRUCT_STAT sbuf; status = cli_posix_stat(cli_unix, entries[i].name, &sbuf); if (!NT_STATUS_IS_OK(status)) { printf("POSIX stat on %s failed (%s)\n", entries[i].name, nt_errstr(status)); continue; } entries[i].ok = true; entries[i].returned_size = sbuf.st_ex_size; } if (!posix_test_entry_check(state, symlnk_dangling, true, strlen(symlnk_dst_dangling))) { goto out; } if (!posix_test_entry_check(state, symlnk_outside_share, true, strlen(symlnk_dst_outside_share))) { goto out; } if (!posix_test_entry_check(state, symlnk_in_share, true, strlen(symlnk_dst_in_share))) { goto out; } printf("POSIX-STAT test passed\n"); correct = true; out: cli_posix_unlink(cli_unix, file); cli_posix_unlink(cli_unix, symlnk_dangling); cli_posix_unlink(cli_unix, symlnk_in_share); cli_posix_unlink(cli_unix, symlnk_outside_share); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Test Creating files and directories directly under a symlink. */ bool run_posix_symlink_parent_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; uint16_t fnum = (uint16_t)-1; NTSTATUS status; const char *parent_dir = "target_dir"; const char *parent_symlink = "symlink_to_target_dir"; const char *fname_real = "target_dir/file"; const char *dname_real = "target_dir/dir"; const char *fname_link = "symlink_to_target_dir/file"; const char *dname_link = "symlink_to_target_dir/dir"; const char *sname_link = "symlink_to_target_dir/symlink"; const char *hname_link = "symlink_to_target_dir/hardlink"; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-SYMLINK-PARENT test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, fname_real); cli_posix_rmdir(cli_unix, dname_real); cli_posix_unlink(cli_unix, fname_link); cli_posix_rmdir(cli_unix, dname_link); cli_posix_unlink(cli_unix, sname_link); cli_posix_unlink(cli_unix, hname_link); cli_posix_unlink(cli_unix, parent_symlink); cli_posix_rmdir(cli_unix, parent_dir); /* Create parent_dir. */ status = cli_posix_mkdir(cli_unix, parent_dir, 0777); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_mkdir of %s failed error %s\n", parent_dir, nt_errstr(status)); goto out; } /* Create symlink to parent_dir. */ status = cli_posix_symlink(cli_unix, parent_dir, parent_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", parent_symlink, parent_dir, nt_errstr(status)); goto out; } /* Try and create a directory under the symlink. */ status = cli_posix_mkdir(cli_unix, dname_link, 0777); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_mkdir of %s failed error %s\n", dname_link, nt_errstr(status)); goto out; } /* Try and create a file under the symlink. */ status = cli_posix_open(cli_unix, fname_link, O_RDWR|O_CREAT, 0666, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", fname_link, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; /* Try and create a symlink to the file under the symlink. */ status = cli_posix_symlink(cli_unix, fname_link, sname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", sname_link, fname_link, nt_errstr(status)); goto out; } /* Try and create a hardlink to the file under the symlink. */ status = cli_posix_hardlink(cli_unix, fname_link, hname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_hardlink of %s -> %s failed error %s\n", hname_link, fname_link, nt_errstr(status)); goto out; } /* Ensure we can delete the symlink via the parent symlink */ status = cli_posix_unlink(cli_unix, sname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_unlink of %s failed error %s\n", sname_link, nt_errstr(status)); goto out; } /* Ensure we can delete the hardlink via the parent symlink */ status = cli_posix_unlink(cli_unix, hname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_unlink of %s failed error %s\n", hname_link, nt_errstr(status)); goto out; } /* Ensure we can delete the directory via the parent symlink */ status = cli_posix_rmdir(cli_unix, dname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_rmdir of %s failed error %s\n", dname_link, nt_errstr(status)); goto out; } /* Ensure we can delete the file via the parent symlink */ status = cli_posix_unlink(cli_unix, fname_link); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_unlink of %s failed error %s\n", fname_link, nt_errstr(status)); goto out; } printf("POSIX-SYMLINK-PARENT test passed\n"); correct = true; out: if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, fname_real); cli_posix_rmdir(cli_unix, dname_real); cli_posix_unlink(cli_unix, fname_link); cli_posix_rmdir(cli_unix, dname_link); cli_posix_unlink(cli_unix, sname_link); cli_posix_unlink(cli_unix, hname_link); cli_posix_unlink(cli_unix, parent_symlink); cli_posix_rmdir(cli_unix, parent_dir); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Ensure we get an error when doing chmod on a symlink, whether it is pointing to a real object or dangling. */ bool run_posix_symlink_chmod_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; NTSTATUS status; uint16_t fnum = (uint16_t)-1; const char *fname_real = "file_real"; const char *fname_real_symlink = "file_real_symlink"; const char *nonexist = "nonexist"; const char *nonexist_symlink = "dangling_symlink"; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-SYMLINK-CHMOD test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); /* Create a real file. */ status = cli_posix_open(cli_unix, fname_real, O_RDWR|O_CREAT, 0644, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", fname_real, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; /* Create symlink to real target. */ status = cli_posix_symlink(cli_unix, fname_real, fname_real_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", fname_real_symlink, fname_real, nt_errstr(status)); goto out; } /* We should not be able to chmod symlinks that point to something. */ status = cli_posix_chmod(cli_unix, fname_real_symlink, 0777); /* This should fail with something other than server crashed. */ if (NT_STATUS_IS_OK(status)) { printf("cli_posix_chmod of %s succeeded (should have failed)\n", fname_real_symlink); goto out; } if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { /* Oops. Server crashed. */ printf("cli_posix_chmod of %s failed error %s\n", fname_real_symlink, nt_errstr(status)); goto out; } /* Any other failure is ok. */ /* Now create symlink to non-existing target. */ status = cli_posix_symlink(cli_unix, nonexist, nonexist_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", nonexist_symlink, nonexist, nt_errstr(status)); goto out; } /* We should not be able to chmod symlinks that point to nothing. */ status = cli_posix_chmod(cli_unix, nonexist_symlink, 0777); /* This should fail with something other than server crashed. */ if (NT_STATUS_IS_OK(status)) { printf("cli_posix_chmod of %s succeeded (should have failed)\n", nonexist_symlink); goto out; } if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { /* Oops. Server crashed. */ printf("cli_posix_chmod of %s failed error %s\n", nonexist_symlink, nt_errstr(status)); goto out; } /* Any other failure is ok. */ printf("POSIX-SYMLINK-CHMOD test passed (expected failure was %s)\n", nt_errstr(status)); correct = true; out: if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Ensure we get an ACL containing OI|IO ACE entries after we add a default POSIX ACL to a directory. This will only ever be an SMB1 test as it depends on POSIX ACL semantics. */ bool run_posix_dir_default_acl_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; NTSTATUS status; uint16_t fnum = (uint16_t)-1; const char *dname = "dir_with_default_acl"; bool correct = false; SMB_STRUCT_STAT sbuf; size_t acl_size = 0; char *aclbuf = NULL; size_t num_file_acls = 0; size_t num_dir_acls = 0; size_t expected_buflen; uint8_t def_acl[SMB_POSIX_ACL_HEADER_SIZE + 5*SMB_POSIX_ACL_ENTRY_SIZE] = {0}; uint8_t *p = NULL; uint32_t i = 0; struct security_descriptor *sd = NULL; bool got_inherit = false; frame = talloc_stackframe(); printf("Starting POSIX-DIR-DEFAULT-ACL test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, dname); cli_posix_rmdir(cli_unix, dname); status = cli_posix_mkdir(cli_unix, dname, 0777); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_mkdir of %s failed error %s\n", dname, nt_errstr(status)); goto out; } /* Do a posix stat to get the owner. */ status = cli_posix_stat(cli_unix, dname, &sbuf); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_stat of %s failed %s\n", dname, nt_errstr(status)); goto out; } /* Get the ACL on the directory. */ status = cli_posix_getacl(cli_unix, dname, frame, &acl_size, &aclbuf); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_getacl on %s failed %s\n", dname, nt_errstr(status)); goto out; } if (acl_size < 6 || SVAL(aclbuf,0) != SMB_POSIX_ACL_VERSION) { printf("%s, unknown POSIX acl version %u.\n", dname, (unsigned int)CVAL(aclbuf,0) ); goto out; } num_file_acls = SVAL(aclbuf,2); num_dir_acls = SVAL(aclbuf,4); /* * No overflow check, num_*_acls comes from a 16-bit value, * and we expect expected_buflen (size_t) to be of at least 32 * bit. */ expected_buflen = SMB_POSIX_ACL_HEADER_SIZE + SMB_POSIX_ACL_ENTRY_SIZE*(num_file_acls+num_dir_acls); if (acl_size != expected_buflen) { printf("%s, incorrect POSIX acl buffer size " "(should be %zu, was %zu).\n", dname, expected_buflen, acl_size); goto out; } if (num_dir_acls != 0) { printf("%s, POSIX default acl already exists" "(should be 0, was %zu).\n", dname, num_dir_acls); goto out; } /* * Get the Windows ACL on the directory. * Make sure there are no inheritable entries. */ status = cli_ntcreate(cli_unix, dname, 0, SEC_STD_READ_CONTROL, 0, FILE_SHARE_READ| FILE_SHARE_WRITE| FILE_SHARE_DELETE, FILE_OPEN, FILE_DIRECTORY_FILE, 0x0, &fnum, NULL); if (!NT_STATUS_IS_OK(status)) { printf("Failed to open directory %s: %s\n", dname, nt_errstr(status)); goto out; } status = cli_query_security_descriptor(cli_unix, fnum, SECINFO_DACL, frame, &sd); if (!NT_STATUS_IS_OK(status)) { printf("Failed to get security descriptor on directory %s: %s\n", dname, nt_errstr(status)); goto out; } for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { struct security_ace *ace = &sd->dacl->aces[i]; if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT| SEC_ACE_FLAG_CONTAINER_INHERIT)) { printf("security descriptor on directory %s already " "contains inheritance flags\n", dname); sec_desc_print(NULL, stdout, sd, true); goto out; } } TALLOC_FREE(sd); /* Construct a new default ACL. */ SSVAL(def_acl,0,SMB_POSIX_ACL_VERSION); SSVAL(def_acl,2,SMB_POSIX_IGNORE_ACE_ENTRIES); SSVAL(def_acl,4,5); /* num_dir_acls. */ p = def_acl + SMB_POSIX_ACL_HEADER_SIZE; /* USER_OBJ. */ SCVAL(p,0,SMB_POSIX_ACL_USER_OBJ); /* tagtype. */ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE); p += SMB_POSIX_ACL_ENTRY_SIZE; /* GROUP_OBJ. */ SCVAL(p,0,SMB_POSIX_ACL_GROUP_OBJ); /* tagtype. */ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE); p += SMB_POSIX_ACL_ENTRY_SIZE; /* OTHER. */ SCVAL(p,0,SMB_POSIX_ACL_OTHER); /* tagtype. */ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE); p += SMB_POSIX_ACL_ENTRY_SIZE; /* Explicit user. */ SCVAL(p,0,SMB_POSIX_ACL_USER); /* tagtype. */ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE); SIVAL(p,2,sbuf.st_ex_uid); p += SMB_POSIX_ACL_ENTRY_SIZE; /* MASK. */ SCVAL(p,0,SMB_POSIX_ACL_MASK); /* tagtype. */ SCVAL(p,1,SMB_POSIX_ACL_READ|SMB_POSIX_ACL_WRITE|SMB_POSIX_ACL_EXECUTE); p += SMB_POSIX_ACL_ENTRY_SIZE; /* Set the POSIX default ACL. */ status = cli_posix_setacl(cli_unix, dname, def_acl, sizeof(def_acl)); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_setacl on %s failed %s\n", dname, nt_errstr(status)); goto out; } /* * Get the Windows ACL on the directory again. * Now there should be inheritable entries. */ status = cli_query_security_descriptor(cli_unix, fnum, SECINFO_DACL, frame, &sd); if (!NT_STATUS_IS_OK(status)) { printf("Failed (2) to get security descriptor " "on directory %s: %s\n", dname, nt_errstr(status)); goto out; } for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { struct security_ace *ace = &sd->dacl->aces[i]; if (ace->flags & (SEC_ACE_FLAG_OBJECT_INHERIT| SEC_ACE_FLAG_CONTAINER_INHERIT)) { got_inherit = true; break; } } if (!got_inherit) { printf("security descriptor on directory %s does not " "contain inheritance flags\n", dname); sec_desc_print(NULL, stdout, sd, true); goto out; } cli_close(cli_unix, fnum); fnum = (uint16_t)-1; printf("POSIX-DIR-DEFAULT-ACL test passed\n"); correct = true; out: TALLOC_FREE(sd); if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, dname); cli_posix_rmdir(cli_unix, dname); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* Ensure we can rename a symlink whether it is pointing to a real object or dangling. */ bool run_posix_symlink_rename_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; NTSTATUS status; uint16_t fnum = (uint16_t)-1; const char *fname_real = "file_real"; const char *fname_real_symlink = "file_real_symlink"; const char *fname_real_symlink_newname = "rename_file_real_symlink"; const char *nonexist = "nonexist"; const char *nonexist_symlink = "dangling_symlink"; const char *nonexist_symlink_newname = "dangling_symlink_rename"; bool correct = false; frame = talloc_stackframe(); printf("Starting POSIX-SYMLINK-RENAME test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, fname_real_symlink_newname); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); cli_posix_unlink(cli_unix, nonexist_symlink_newname); /* Create a real file. */ status = cli_posix_open(cli_unix, fname_real, O_RDWR|O_CREAT, 0644, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", fname_real, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; /* Create symlink to real target. */ status = cli_posix_symlink(cli_unix, fname_real, fname_real_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", fname_real_symlink, fname_real, nt_errstr(status)); goto out; } /* Ensure we can rename the symlink to the real file. */ status = cli_rename(cli_unix, fname_real_symlink, fname_real_symlink_newname, false); if (!NT_STATUS_IS_OK(status)) { printf("cli_rename of %s -> %s failed %s\n", fname_real_symlink, fname_real_symlink_newname, nt_errstr(status)); goto out; } /* Now create symlink to non-existing target. */ status = cli_posix_symlink(cli_unix, nonexist, nonexist_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", nonexist_symlink, nonexist, nt_errstr(status)); goto out; } /* Ensure we can rename the dangling symlink. */ status = cli_rename(cli_unix, nonexist_symlink, nonexist_symlink_newname, false); if (!NT_STATUS_IS_OK(status)) { printf("cli_rename of %s -> %s failed %s\n", nonexist_symlink, nonexist_symlink_newname, nt_errstr(status)); goto out; } printf("POSIX-SYMLINK-RENAME test passed\n"); correct = true; out: if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, fname_real_symlink_newname); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); cli_posix_unlink(cli_unix, nonexist_symlink_newname); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* List of info levels to try with a POSIX symlink path. */ static struct { uint32_t level; const char *name; } posix_smb1_qpath_array[] = { { SMB_INFO_STANDARD, "SMB_INFO_STANDARD"}, { SMB_INFO_QUERY_EA_SIZE, "SMB_INFO_QUERY_EA_SIZE"}, { SMB_INFO_IS_NAME_VALID, "SMB_INFO_IS_NAME_VALID"}, { SMB_INFO_QUERY_EAS_FROM_LIST, "SMB_INFO_QUERY_EAS_FROM_LIST"}, { SMB_INFO_QUERY_ALL_EAS, "SMB_INFO_QUERY_ALL_EAS"}, { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION"}, { SMB_FILE_STANDARD_INFORMATION, "SMB_FILE_STANDARD_INFORMATION"}, { SMB_FILE_EA_INFORMATION, "SMB_FILE_EA_INFORMATION"}, { SMB_FILE_ALTERNATE_NAME_INFORMATION,"SMB_FILE_ALTERNATE_NAME_INFORMATION"}, { SMB_QUERY_FILE_NAME_INFO, "SMB_QUERY_FILE_NAME_INFO"}, { SMB_FILE_NORMALIZED_NAME_INFORMATION,"SMB_FILE_NORMALIZED_NAME_INFORMATION"}, { SMB_FILE_ALLOCATION_INFORMATION, "SMB_FILE_ALLOCATION_INFORMATION"}, { SMB_FILE_END_OF_FILE_INFORMATION, "SMB_FILE_END_OF_FILE_INFORMATION"}, { SMB_FILE_ALL_INFORMATION, "SMB_FILE_ALL_INFORMATION"}, { SMB_FILE_INTERNAL_INFORMATION, "SMB_FILE_INTERNAL_INFORMATION"}, { SMB_FILE_ACCESS_INFORMATION, "SMB_FILE_ACCESS_INFORMATION"}, { SMB_FILE_NAME_INFORMATION, "SMB_FILE_NAME_INFORMATION"}, { SMB_FILE_DISPOSITION_INFORMATION, "SMB_FILE_DISPOSITION_INFORMATION"}, { SMB_FILE_POSITION_INFORMATION, "SMB_FILE_POSITION_INFORMATION"}, { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION"}, { SMB_FILE_ALIGNMENT_INFORMATION, "SMB_FILE_ALIGNMENT_INFORMATION"}, { SMB_FILE_STREAM_INFORMATION, "SMB_FILE_STREAM_INFORMATION"}, { SMB_FILE_COMPRESSION_INFORMATION, "SMB_FILE_COMPRESSION_INFORMATION"}, { SMB_FILE_NETWORK_OPEN_INFORMATION, "SMB_FILE_NETWORK_OPEN_INFORMATION"}, { SMB_FILE_ATTRIBUTE_TAG_INFORMATION, "SMB_FILE_ATTRIBUTE_TAG_INFORMATION"}, { SMB_QUERY_FILE_UNIX_BASIC, "SMB_QUERY_FILE_UNIX_BASIC"}, { SMB_QUERY_FILE_UNIX_INFO2, "SMB_QUERY_FILE_UNIX_INFO2"}, { SMB_QUERY_FILE_UNIX_LINK, "SMB_QUERY_FILE_UNIX_LINK"}, { SMB_QUERY_POSIX_ACL, "SMB_QUERY_POSIX_ACL"}, { SMB_QUERY_POSIX_LOCK, "SMB_QUERY_POSIX_LOCK"}, }; static NTSTATUS do_qpath(TALLOC_CTX *ctx, struct cli_state *cli_unix, const char *fname, size_t i) { NTSTATUS status; if (posix_smb1_qpath_array[i].level == SMB_INFO_QUERY_EAS_FROM_LIST) { uint16_t setup; uint8_t *param; uint8_t data[8]; uint8_t *rparam = NULL; uint8_t *rdata = NULL; uint32_t rbytes = 0; /* Set up an EA list with 'a' as the single name. */ SIVAL(data,0, 8); SCVAL(data,4, 2); /* namelen. */ SCVAL(data,5, 'a'); SCVAL(data,6, '\0'); /* name. */ SCVAL(data,7, '\0'); /* padding. */ SSVAL(&setup, 0, TRANSACT2_QPATHINFO); param = talloc_zero_array(ctx, uint8_t, 6); if (param == NULL) { return NT_STATUS_NO_MEMORY; } SSVAL(param, 0, SMB_INFO_QUERY_EAS_FROM_LIST); param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli_unix->conn), fname, strlen(fname)+1, NULL); if (param == NULL) { return NT_STATUS_NO_MEMORY; } status = cli_trans(ctx, cli_unix, SMBtrans2, NULL, -1, 0, 0, &setup, 1, 0, param, talloc_get_size(param), talloc_get_size(param), data, 8, 0, NULL, NULL, 0, NULL, &rparam, 0, &rbytes, &rdata, 0, &rbytes); TALLOC_FREE(rparam); TALLOC_FREE(rdata); } else { uint8_t *rdata = NULL; uint32_t num_rdata = 0; status = cli_qpathinfo(ctx, cli_unix, fname, posix_smb1_qpath_array[i].level, 0, /* min_rdata */ 65534, /* max_rdata */ &rdata, &num_rdata); TALLOC_FREE(rdata); } /* * We don't care what came back, so long as the * server didn't crash. */ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { printf("cli_qpathinfo of %s failed error " "NT_STATUS_CONNECTION_DISCONNECTED\n", fname); return status; } printf("cli_qpathinfo info %x (%s) of %s got %s " "(this is not an error)\n", (unsigned int)posix_smb1_qpath_array[i].level, posix_smb1_qpath_array[i].name, fname, nt_errstr(status)); return NT_STATUS_OK; } /* Ensure we can call SMB1 getpathinfo in a symlink, pointing to a real object or dangling. We mostly expect errors, but the server must not crash. */ bool run_posix_symlink_getpathinfo_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; NTSTATUS status; uint16_t fnum = (uint16_t)-1; const char *fname_real = "file_getpath_real"; const char *fname_real_symlink = "file_real_getpath_symlink"; const char *nonexist = "nonexist_getpath"; const char *nonexist_symlink = "dangling_getpath_symlink"; bool correct = false; size_t i; frame = talloc_stackframe(); printf("Starting POSIX-SYMLINK-GETPATHINFO test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); /* Create a real file. */ status = cli_posix_open(cli_unix, fname_real, O_RDWR|O_CREAT, 0644, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", fname_real, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; /* Create symlink to real target. */ status = cli_posix_symlink(cli_unix, fname_real, fname_real_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", fname_real_symlink, fname_real, nt_errstr(status)); goto out; } /* Now create symlink to non-existing target. */ status = cli_posix_symlink(cli_unix, nonexist, nonexist_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", nonexist_symlink, nonexist, nt_errstr(status)); goto out; } for (i = 0; i < ARRAY_SIZE(posix_smb1_qpath_array); i++) { status = do_qpath(frame, cli_unix, fname_real_symlink, i); if (!NT_STATUS_IS_OK(status)) { goto out; } status = do_qpath(frame, cli_unix, nonexist_symlink, i); if (!NT_STATUS_IS_OK(status)) { goto out; } } printf("POSIX-SYMLINK-GETPATHINFO test passed\n"); correct = true; out: if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; } /* List of info levels to try with a POSIX symlink path. */ static struct { uint32_t level; const char *name; uint32_t data_len; } posix_smb1_setpath_array[] = { { SMB_SET_FILE_UNIX_BASIC, "SMB_SET_FILE_UNIX_BASIC", 100}, { SMB_SET_FILE_UNIX_INFO2, "SMB_SET_FILE_UNIX_INFO2", 116}, { SMB_SET_FILE_UNIX_LINK, "SMB_SET_FILE_UNIX_LINK", 8}, { SMB_SET_FILE_UNIX_HLINK, "SMB_SET_FILE_UNIX_HLINK", 8}, { SMB_SET_POSIX_ACL, "SMB_SET_POSIX_ACL", 6}, { SMB_SET_POSIX_LOCK, "SMB_SET_POSIX_LOCK", 24}, { SMB_INFO_STANDARD, "SMB_INFO_STANDARD", 12}, { SMB_INFO_SET_EA, "SMB_INFO_SET_EA", 10}, { SMB_FILE_BASIC_INFORMATION, "SMB_FILE_BASIC_INFORMATION", 36}, { SMB_SET_FILE_ALLOCATION_INFO, "SMB_SET_FILE_ALLOCATION_INFO", 8}, { SMB_SET_FILE_END_OF_FILE_INFO,"SMB_SET_FILE_END_OF_FILE_INFO",8}, { SMB_SET_FILE_DISPOSITION_INFO,"SMB_SET_FILE_DISPOSITION_INFO",1}, { SMB_FILE_POSITION_INFORMATION,"SMB_FILE_POSITION_INFORMATION",8}, { SMB_FILE_FULL_EA_INFORMATION, "SMB_FILE_FULL_EA_INFORMATION",10}, { SMB_FILE_MODE_INFORMATION, "SMB_FILE_MODE_INFORMATION", 4}, { SMB_FILE_SHORT_NAME_INFORMATION,"SMB_FILE_SHORT_NAME_INFORMATION",12}, { SMB_FILE_RENAME_INFORMATION,"SMB_FILE_RENAME_INFORMATION", 20}, { SMB_FILE_LINK_INFORMATION, "SMB_FILE_LINK_INFORMATION", 20}, }; static NTSTATUS do_setpath(TALLOC_CTX *ctx, struct cli_state *cli_unix, const char *fname, size_t i) { NTSTATUS status; uint8_t *data = NULL; data = talloc_zero_array(ctx, uint8_t, posix_smb1_setpath_array[i].data_len); if (data == NULL) { return NT_STATUS_NO_MEMORY; } status = cli_setpathinfo(cli_unix, posix_smb1_setpath_array[i].level, fname, data, posix_smb1_setpath_array[i].data_len); TALLOC_FREE(data); /* * We don't care what came back, so long as the * server didn't crash. */ if (NT_STATUS_EQUAL(status, NT_STATUS_CONNECTION_DISCONNECTED)) { printf("cli_setpathinfo info %x (%s) of %s failed" "error NT_STATUS_CONNECTION_DISCONNECTED\n", (unsigned int)posix_smb1_setpath_array[i].level, posix_smb1_setpath_array[i].name, fname); return status; } printf("cli_setpathinfo info %x (%s) of %s got %s " "(this is not an error)\n", (unsigned int)posix_smb1_setpath_array[i].level, posix_smb1_setpath_array[i].name, fname, nt_errstr(status)); return NT_STATUS_OK; } /* Ensure we can call SMB1 setpathinfo in a symlink, pointing to a real object or dangling. We mostly expect errors, but the server must not crash. */ bool run_posix_symlink_setpathinfo_test(int dummy) { TALLOC_CTX *frame = NULL; struct cli_state *cli_unix = NULL; NTSTATUS status; uint16_t fnum = (uint16_t)-1; const char *fname_real = "file_setpath_real"; const char *fname_real_symlink = "file_real_setpath_symlink"; const char *nonexist = "nonexist_setpath"; const char *nonexist_symlink = "dangling_setpath_symlink"; bool correct = false; size_t i; frame = talloc_stackframe(); printf("Starting POSIX-SYMLINK-SETPATHINFO test\n"); if (!torture_open_connection(&cli_unix, 0)) { TALLOC_FREE(frame); return false; } torture_conn_set_sockopt(cli_unix); status = torture_setup_unix_extensions(cli_unix); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(frame); return false; } /* Start with a clean slate. */ cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); /* Create a real file. */ status = cli_posix_open(cli_unix, fname_real, O_RDWR|O_CREAT, 0644, &fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_open of %s failed error %s\n", fname_real, nt_errstr(status)); goto out; } status = cli_close(cli_unix, fnum); if (!NT_STATUS_IS_OK(status)) { printf("cli_close failed %s\n", nt_errstr(status)); goto out; } fnum = (uint16_t)-1; /* Create symlink to real target. */ status = cli_posix_symlink(cli_unix, fname_real, fname_real_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", fname_real_symlink, fname_real, nt_errstr(status)); goto out; } /* Now create symlink to non-existing target. */ status = cli_posix_symlink(cli_unix, nonexist, nonexist_symlink); if (!NT_STATUS_IS_OK(status)) { printf("cli_posix_symlink of %s -> %s failed error %s\n", nonexist_symlink, nonexist, nt_errstr(status)); goto out; } for (i = 0; i < ARRAY_SIZE(posix_smb1_setpath_array); i++) { status = do_setpath(frame, cli_unix, fname_real_symlink, i); if (!NT_STATUS_IS_OK(status)) { goto out; } status = do_setpath(frame, cli_unix, nonexist_symlink, i); if (!NT_STATUS_IS_OK(status)) { goto out; } } printf("POSIX-SYMLINK-SETPATHINFO test passed\n"); correct = true; out: if (fnum != (uint16_t)-1) { cli_close(cli_unix, fnum); } cli_posix_unlink(cli_unix, fname_real); cli_posix_unlink(cli_unix, fname_real_symlink); cli_posix_unlink(cli_unix, nonexist); cli_posix_unlink(cli_unix, nonexist_symlink); if (!torture_close_connection(cli_unix)) { correct = false; } TALLOC_FREE(frame); return correct; }