diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/torture/smb2/session.c | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/torture/smb2/session.c')
-rw-r--r-- | source4/torture/smb2/session.c | 5670 |
1 files changed, 5670 insertions, 0 deletions
diff --git a/source4/torture/smb2/session.c b/source4/torture/smb2/session.c new file mode 100644 index 0000000..823304f --- /dev/null +++ b/source4/torture/smb2/session.c @@ -0,0 +1,5670 @@ +/* + Unix SMB/CIFS implementation. + + test suite for SMB2 session setups + + Copyright (C) Michael Adam 2012 + + 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/>. +*/ + +#include "includes.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "torture/torture.h" +#include "torture/util.h" +#include "torture/smb2/proto.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/cmdline/cmdline.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/credentials_krb5.h" +#include "libcli/security/security.h" +#include "libcli/resolve/resolve.h" +#include "lib/param/param.h" +#include "lib/util/tevent_ntstatus.h" + +/* Ticket lifetime we want to request in seconds */ +#define KRB5_TICKET_LIFETIME 5 +/* Allowed clock skew in seconds */ +#define KRB5_CLOCKSKEW 5 +/* Time till ticket fully expired in seconds */ +#define KRB5_TICKET_EXPIRETIME KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + +#define texpand(x) #x +#define GENSEC_GSSAPI_REQUESTED_LIFETIME(x) \ + "gensec_gssapi:requested_life_time=" texpand(x) + +#define CHECK_CREATED(tctx, __io, __created, __attribute) \ + do { \ + torture_assert_int_equal(tctx, (__io)->out.create_action, \ + NTCREATEX_ACTION_ ## __created, \ + "out.create_action incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.size, 0, \ + "out.size incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.file_attr, \ + (__attribute), \ + "out.file_attr incorrect"); \ + torture_assert_int_equal(tctx, (__io)->out.reserved2, 0, \ + "out.reserverd2 incorrect"); \ + } while(0) + +#define WAIT_FOR_ASYNC_RESPONSE(req) \ + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ + if (tevent_loop_once(tctx->ev) != 0) { \ + break; \ + } \ + } + +static void sleep_remaining(struct torture_context *tctx, + const struct timeval *endtime) +{ + struct timeval current = tevent_timeval_current(); + double remaining_secs = timeval_elapsed2(¤t, endtime); + + remaining_secs = remaining_secs < 1.0 ? 1.0 : remaining_secs; + torture_comment( + tctx, + "sleep for %2.f second(s) that the krb5 ticket expires", + remaining_secs); + smb_msleep((int)(remaining_secs * 1000)); +} + +/** + * basic test for doing a session reconnect + */ +bool test_session_reconnect1(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_handle _h2; + struct smb2_handle *h2 = NULL; + struct smb2_create io1, io2; + uint64_t previous_session_id; + bool ret = true; + struct smb2_tree *tree2 = NULL; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reconnect_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* disconnect, reconnect and then do durable reopen */ + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + torture_assert_goto(tctx, torture_smb2_connection_ext(tctx, previous_session_id, + &tree->session->transport->options, &tree2), + ret, done, + "session reconnect failed\n"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_USER_SESSION_DELETED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + h1 = NULL; + + smb2_oplock_create_share(&io2, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree2, mem_ctx, &io2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + + CHECK_CREATED(tctx, &io2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + _h2 = io2.out.file.handle; + h2 = &_h2; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + if (h2 != NULL) { + smb2_util_close(tree2, *h2); + } + + if (tree2 != NULL) { + smb2_util_unlink(tree2, fname); + } + smb2_util_unlink(tree, fname); + + talloc_free(tree); + talloc_free(tree2); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * basic test for doing a session reconnect on one connection + */ +bool test_session_reconnect2(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + uint64_t previous_session_id; + bool ret = true; + struct smb2_session *session2 = NULL; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reconnect_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* disconnect, reconnect and then do durable reopen */ + previous_session_id = smb2cli_session_current_id(tree->session->smbXcli); + + torture_assert(tctx, torture_smb2_session_setup(tctx, tree->session->transport, + previous_session_id, tctx, &session2), + "session reconnect (on the same connection) failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_USER_SESSION_DELETED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + h1 = NULL; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + talloc_free(session2); + + talloc_free(mem_ctx); + + return ret; +} + +bool test_session_reauth1(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +bool test_session_reauth2(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + struct cli_credentials *anon_creds = NULL; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test getting security descriptor after reauth + */ +bool test_session_reauth3(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth3_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test setting security descriptor after reauth. + */ +bool test_session_reauth4(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + struct security_descriptor *sd1; + struct security_ace ace; + struct dom_sid *extra_sid; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth4_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sd1 = qfinfo.query_secdesc.out.sd; + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* give full access on the file to anonymous */ + + extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL | SEC_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _h1; + sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; + sfinfo.set_secdesc.in.sd = sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* re-get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * test renaming after reauth. + * compare security descriptors before and after rename/reauth + */ +bool test_session_reauth5(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char dname[128]; + char fname[256]; + char fname2[256]; + struct smb2_handle _dh1; + struct smb2_handle *dh1 = NULL; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + bool ok; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct cli_credentials *anon_creds = NULL; + uint32_t secinfo_flags = SECINFO_OWNER + | SECINFO_GROUP + | SECINFO_DACL + | SECINFO_PROTECTED_DACL + | SECINFO_UNPROTECTED_DACL; + struct security_descriptor *f_sd1; + struct security_descriptor *d_sd1 = NULL; + struct security_ace ace; + struct dom_sid *extra_sid; + + /* Add some random component to the file name. */ + snprintf(dname, sizeof(dname), "session_reauth5_%s.d", + generate_random_str(tctx, 8)); + snprintf(fname, sizeof(fname), "%s\\file.dat", dname); + + ok = smb2_util_setup_dir(tctx, tree, dname); + torture_assert(tctx, ok, "smb2_util_setup_dir not ok"); + + status = torture_smb2_testdir(tree, dname, &_dh1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "torture_smb2_testdir failed"); + dh1 = &_dh1; + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + f_sd1 = qfinfo.query_secdesc.out.sd; + + /* re-authenticate as anonymous */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to rename the file: fails */ + + snprintf(fname2, sizeof(fname2), "%s\\file2.dat", dname); + + status = smb2_util_unlink(tree, fname2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* re-authenticate as original user again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* give full access on the file to anonymous */ + + extra_sid = dom_sid_parse_talloc(tctx, SID_NT_ANONYMOUS); + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_RIGHTS_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(f_sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _h1; + sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; + sfinfo.set_secdesc.in.sd = f_sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* re-get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* re-authenticate as anonymous - again */ + + anon_creds = cli_credentials_init_anon(mem_ctx); + torture_assert(tctx, (anon_creds != NULL), "talloc error"); + + status = smb2_session_setup_spnego(tree->session, + anon_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* try to rename the file: fails */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* give full access on the parent dir to anonymous */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _dh1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + d_sd1 = qfinfo.query_secdesc.out.sd; + + ZERO_STRUCT(ace); + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_RIGHTS_FILE_ALL; + ace.trustee = *extra_sid; + + status = security_descriptor_dacl_add(d_sd1, &ace); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "security_descriptor_dacl_add failed"); + + ZERO_STRUCT(sfinfo); + sfinfo.set_secdesc.level = RAW_SFILEINFO_SEC_DESC; + sfinfo.set_secdesc.in.file.handle = _dh1; + sfinfo.set_secdesc.in.secinfo_flags = secinfo_flags; + sfinfo.set_secdesc.in.secinfo_flags = SECINFO_DACL; + sfinfo.set_secdesc.in.sd = d_sd1; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _dh1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + status = smb2_util_close(tree, _dh1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + dh1 = NULL; + + /* try to rename the file: still fails */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_ACCESS_DENIED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + /* re-authenticate as original user - again */ + + status = smb2_session_setup_spnego(tree->session, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* rename the file - for verification that it works */ + + ZERO_STRUCT(sfinfo); + sfinfo.rename_information.level = RAW_SFILEINFO_RENAME_INFORMATION; + sfinfo.rename_information.in.file.handle = _h1; + sfinfo.rename_information.in.overwrite = true; + sfinfo.rename_information.in.new_name = fname2; + + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_setinfo_file failed"); + + /* closs the file, check it is gone and reopen under the new name */ + + status = smb2_util_close(tree, _h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + ZERO_STRUCT(io1); + + smb2_generic_create_share(&io1, + NULL /* lease */, false /* dir */, + fname, + NTCREATEX_DISP_OPEN, + smb2_util_share_access(""), + smb2_util_oplock_level("b"), + 0 /* leasekey */, 0 /* leasestate */); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_OBJECT_NAME_NOT_FOUND, + ret, done, "smb2_create " + "returned unexpected status"); + + ZERO_STRUCT(io1); + + smb2_generic_create_share(&io1, + NULL /* lease */, false /* dir */, + fname2, + NTCREATEX_DISP_OPEN, + smb2_util_share_access(""), + smb2_util_oplock_level("b"), + 0 /* leasekey */, 0 /* leasestate */); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* try to access the file via the old handle */ + + ZERO_STRUCT(qfinfo); + + qfinfo.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + qfinfo.query_secdesc.in.file.handle = _h1; + qfinfo.query_secdesc.in.secinfo_flags = secinfo_flags; + + status = smb2_getinfo_file(tree, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + +done: + if (dh1 != NULL) { + smb2_util_close(tree, *dh1); + } + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_deltree(tree, dname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + +/** + * do reauth with wrong credentials, + * hence triggering the error path in reauth. + * The invalid reauth deletes the session. + */ +bool test_session_reauth6(struct torture_context *tctx, struct smb2_tree *tree) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + bool ret = true; + char *corrupted_password; + struct cli_credentials *broken_creds; + bool ok; + bool encrypted; + NTSTATUS expected; + enum credentials_use_kerberos krb_state; + + krb_state = cli_credentials_get_kerberos_state( + samba_cmdline_get_creds()); + if (krb_state == CRED_USE_KERBEROS_REQUIRED) { + torture_skip(tctx, + "Can't test failing session setup with kerberos."); + } + + encrypted = smb2cli_tcon_is_encryption_on(tree->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_reauth1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* + * reauthentication with invalid credentials: + */ + + broken_creds = cli_credentials_shallow_copy(mem_ctx, + samba_cmdline_get_creds()); + torture_assert(tctx, (broken_creds != NULL), "talloc error"); + + corrupted_password = talloc_asprintf(mem_ctx, "%s%s", + cli_credentials_get_password(broken_creds), + "corrupt"); + torture_assert(tctx, (corrupted_password != NULL), "talloc error"); + + ok = cli_credentials_set_password(broken_creds, corrupted_password, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_password not ok"); + + status = smb2_session_setup_spnego(tree->session, + broken_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego " + "returned unexpected status"); + + torture_comment(tctx, "did failed reauth\n"); + /* + * now verify that the invalid session reauth has closed our session + */ + + if (encrypted) { + expected = NT_STATUS_CONNECTION_DISCONNECTED; + } else { + expected = NT_STATUS_USER_SESSION_DELETED; + } + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree, mem_ctx, &io1); + torture_assert_ntstatus_equal_goto(tctx, status, expected, + ret, done, "smb2_create " + "returned unexpected status"); + +done: + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + smb2_util_unlink(tree, fname); + + talloc_free(tree); + + talloc_free(mem_ctx); + + return ret; +} + + +static bool test_session_expire1i(struct torture_context *tctx, + bool force_signing, + bool force_encryption) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + enum credentials_use_kerberos use_kerberos; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + size_t i; + struct timeval endtime; + bool ticket_expired = false; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + torture_assert_int_equal(tctx, + use_kerberos, + CRED_USE_KERBEROS_REQUIRED, + "please use --use-kerberos=required"); + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + if (force_signing) { + options.signing = SMB_SIGNING_REQUIRED; + } + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + if (force_encryption) { + status = smb2cli_session_encryption_on(tree->session->smbXcli); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2cli_session_encryption_on failed"); + } + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + for (i=0; i < 2; i++) { + torture_comment(tctx, "%s: query info => OK\n", + current_timestring(tctx, true)); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "%s: query info => EXPIRED\n", + current_timestring(tctx, true)); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + /* + * the krb5 library may not handle expired creds + * well, lets start with an empty ccache. + */ + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (!force_encryption) { + smb2cli_session_require_signed_response( + tree->session->smbXcli, true); + } + + torture_comment(tctx, "%s: reauth => OK\n", + current_timestring(tctx, true)); + status = smb2_session_setup_spnego(tree->session, + credentials, + 0 /* previous_session_id */); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + + * KRB5_CLOCKSKEW + a few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, + 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + smb2cli_session_require_signed_response( + tree->session->smbXcli, false); + } + + ticket_expired = timeval_expired(&endtime); + if (ticket_expired) { + struct timeval current = timeval_current(); + double remaining_secs = timeval_elapsed2(¤t, &endtime); + remaining_secs = remaining_secs < 0.0 ? remaining_secs * -1.0 + : remaining_secs; + torture_warning( + tctx, + "The ticket already expired %.2f seconds ago. " + "You might want to increase KRB5_TICKET_LIFETIME.", + remaining_secs); + } + torture_assert(tctx, + ticket_expired == false, + "The kerberos ticket already expired"); + ZERO_STRUCT(qfinfo.access_information.out); + torture_comment(tctx, "%s: %s:%s: before smb2_getinfo_file()\n", + current_timestring(tctx, true), + __location__, __func__); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_comment(tctx, "%s: %s:%s: after smb2_getinfo_file() => %s\n", + current_timestring(tctx, true), + __location__, __func__, + nt_errstr(status)); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +static bool test_session_expire1n(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + false, /* force_signing */ + false); /* force_encryption */ +} + +static bool test_session_expire1s(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + true, /* force_signing */ + false); /* force_encryption */ +} + +static bool test_session_expire1e(struct torture_context *tctx) +{ + return test_session_expire1i(tctx, + true, /* force_signing */ + true); /* force_encryption */ +} + +static bool test_session_expire2i(struct torture_context *tctx, + bool force_encryption) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + const char *unc = NULL; + struct smb2_tree *tree2 = NULL; + struct tevent_req *subreq = NULL; + uint32_t timeout_msec; + enum credentials_use_kerberos use_kerberos; + uint32_t caps; + char fname[256]; + struct smb2_handle dh; + struct smb2_handle dh2; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + union smb_setfileinfo sfinfo; + struct smb2_flush flsh; + struct smb2_read rd; + const uint8_t wd = 0; + struct smb2_lock lck; + struct smb2_lock_element el; + struct smb2_ioctl ctl; + struct smb2_break oack; + struct smb2_lease_break_ack lack; + struct smb2_find fnd; + union smb_search_data *d = NULL; + unsigned int count; + struct smb2_request *req = NULL; + struct smb2_notify ntf1; + struct smb2_notify ntf2; + struct timeval endtime; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + torture_assert_int_equal(tctx, + use_kerberos, + CRED_USE_KERBEROS_REQUIRED, + "please use --use-kerberos=required"); + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + + lpcfg_smbcli_options(tctx->lp_ctx, &options); + options.signing = SMB_SIGNING_REQUIRED; + + unc = talloc_asprintf(tctx, "\\\\%s\\%s", host, share); + torture_assert(tctx, unc != NULL, "talloc_asprintf"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + if (force_encryption) { + status = smb2cli_session_encryption_on(tree->session->smbXcli); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2cli_session_encryption_on failed"); + } + + caps = smb2cli_conn_server_capabilities(tree->session->transport->conn); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + status = smb2_util_roothandle(tree, &dh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_roothandle failed"); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + torture_comment(tctx, "query info => OK\n"); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + torture_comment(tctx, "lock => OK\n"); + ZERO_STRUCT(lck); + lck.in.locks = ⪙ + lck.in.lock_count = 0x0001; + lck.in.lock_sequence = 0x00000000; + lck.in.file.handle = *h1; + ZERO_STRUCT(el); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + el.offset = 0x0000000000000000; + el.length = 0x0000000000000001; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_lock lock failed"); + + torture_comment(tctx, "1st notify => PENDING\n"); + ZERO_STRUCT(ntf1); + ntf1.in.file.handle = dh; + ntf1.in.recursive = 0x0000; + ntf1.in.buffer_size = 128; + ntf1.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES; + ntf1.in.unknown = 0x00000000; + req = smb2_notify_send(tree, &ntf1); + + while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { + if (tevent_loop_once(tctx->ev) != 0) { + break; + } + } + + torture_assert_goto(tctx, req->state <= SMB2_REQUEST_RECV, ret, done, + "smb2_notify finished"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "query info => EXPIRED\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + + torture_comment(tctx, "set info => EXPIRED\n"); + ZERO_STRUCT(sfinfo); + sfinfo.end_of_file_info.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION; + sfinfo.end_of_file_info.in.file.handle = *h1; + sfinfo.end_of_file_info.in.size = 1; + status = smb2_setinfo_file(tree, &sfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_setinfo_file " + "returned unexpected status"); + + torture_comment(tctx, "flush => EXPIRED\n"); + ZERO_STRUCT(flsh); + flsh.in.file.handle = *h1; + status = smb2_flush(tree, &flsh); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_flush " + "returned unexpected status"); + + torture_comment(tctx, "read => EXPIRED\n"); + ZERO_STRUCT(rd); + rd.in.file.handle = *h1; + rd.in.length = 5; + rd.in.offset = 0; + status = smb2_read(tree, tctx, &rd); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_read " + "returned unexpected status"); + + torture_comment(tctx, "write => EXPIRED\n"); + status = smb2_util_write(tree, *h1, &wd, 0, 1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_write " + "returned unexpected status"); + + torture_comment(tctx, "ioctl => EXPIRED\n"); + ZERO_STRUCT(ctl); + ctl.in.file.handle = *h1; + ctl.in.function = FSCTL_SRV_ENUM_SNAPS; + ctl.in.max_output_response = 16; + ctl.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL; + status = smb2_ioctl(tree, tctx, &ctl); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_ioctl " + "returned unexpected status"); + + torture_comment(tctx, "oplock ack => EXPIRED\n"); + ZERO_STRUCT(oack); + oack.in.file.handle = *h1; + status = smb2_break(tree, &oack); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_break " + "returned unexpected status"); + + if (caps & SMB2_CAP_LEASING) { + torture_comment(tctx, "lease ack => EXPIRED\n"); + ZERO_STRUCT(lack); + lack.in.lease.lease_version = 1; + lack.in.lease.lease_key.data[0] = 1; + lack.in.lease.lease_key.data[1] = 2; + status = smb2_lease_break_ack(tree, &lack); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_break " + "returned unexpected status"); + } + + torture_comment(tctx, "query directory => EXPIRED\n"); + ZERO_STRUCT(fnd); + fnd.in.file.handle = dh; + fnd.in.pattern = "*"; + fnd.in.continue_flags = SMB2_CONTINUE_FLAG_SINGLE; + fnd.in.max_response_size= 0x100; + fnd.in.level = SMB2_FIND_BOTH_DIRECTORY_INFO; + status = smb2_find_level(tree, tree, &fnd, &count, &d); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_find_level " + "returned unexpected status"); + + torture_comment(tctx, "1st notify => CANCEL\n"); + smb2_cancel(req); + + torture_comment(tctx, "2nd notify => EXPIRED\n"); + ZERO_STRUCT(ntf2); + ntf2.in.file.handle = dh; + ntf2.in.recursive = 0x0000; + ntf2.in.buffer_size = 128; + ntf2.in.completion_filter= FILE_NOTIFY_CHANGE_ATTRIBUTES; + ntf2.in.unknown = 0x00000000; + status = smb2_notify(tree, tctx, &ntf2); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_notify " + "returned unexpected status"); + + torture_assert_goto(tctx, req->state > SMB2_REQUEST_RECV, ret, done, + "smb2_notify (1st) not finished"); + + status = smb2_notify_recv(req, tctx, &ntf1); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_CANCELLED, + ret, done, "smb2_notify cancelled" + "returned unexpected status"); + + torture_comment(tctx, "tcon => EXPIRED\n"); + tree2 = smb2_tree_init(tree->session, tctx, false); + torture_assert(tctx, tree2 != NULL, "smb2_tree_init"); + timeout_msec = tree->session->transport->options.request_timeout * 1000; + subreq = smb2cli_tcon_send(tree2, tctx->ev, + tree2->session->transport->conn, + timeout_msec, + tree2->session->smbXcli, + tree2->smbXcli, + 0, /* flags */ + unc); + torture_assert(tctx, subreq != NULL, "smb2cli_tcon_send"); + torture_assert(tctx, + tevent_req_poll_ntstatus(subreq, tctx->ev, &status), + "tevent_req_poll_ntstatus"); + status = smb2cli_tcon_recv(subreq); + TALLOC_FREE(subreq); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2cli_tcon" + "returned unexpected status"); + + torture_comment(tctx, "create => EXPIRED\n"); + status = smb2_util_roothandle(tree, &dh2); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_roothandle" + "returned unexpected status"); + + torture_comment(tctx, "tdis => EXPIRED\n"); + status = smb2_tdis(tree); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2cli_tdis" + "returned unexpected status"); + + /* + * (Un)Lock, Close and Logoff are still possible + */ + + torture_comment(tctx, "1st unlock => OK\n"); + el.flags = SMB2_LOCK_FLAG_UNLOCK; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_lock unlock failed"); + + torture_comment(tctx, "2nd unlock => RANGE_NOT_LOCKED\n"); + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_RANGE_NOT_LOCKED, + ret, done, "smb2_lock 2nd unlock" + "returned unexpected status"); + + torture_comment(tctx, "lock => EXPIRED\n"); + el.flags = SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_util_roothandle" + "returned unexpected status"); + + torture_comment(tctx, "close => OK\n"); + status = smb2_util_close(tree, *h1); + h1 = NULL; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_close failed"); + + torture_comment(tctx, "echo without session => OK\n"); + status = smb2_keepalive(tree->session->transport); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_keepalive without session failed"); + + torture_comment(tctx, "echo with session => OK\n"); + req = smb2_keepalive_send(tree->session->transport, tree->session); + status = smb2_keepalive_recv(req); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_keepalive with session failed"); + + torture_comment(tctx, "logoff => OK\n"); + status = smb2_logoff(tree->session); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_logoff failed"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +static bool test_session_expire2s(struct torture_context *tctx) +{ + return test_session_expire2i(tctx, + false); /* force_encryption */ +} + +static bool test_session_expire2e(struct torture_context *tctx) +{ + return test_session_expire2i(tctx, + true); /* force_encryption */ +} + +static bool test_session_expire_disconnect(struct torture_context *tctx) +{ + NTSTATUS status; + bool ret = false; + struct smbcli_options options; + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct smb2_tree *tree = NULL; + enum credentials_use_kerberos use_kerberos; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool connected; + struct timeval endtime; + + use_kerberos = cli_credentials_get_kerberos_state(credentials); + if (use_kerberos != CRED_USE_KERBEROS_REQUIRED) { + torture_warning(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + torture_skip(tctx, + "smb2.session.expire1 requires " + "--use-kerberos=required!"); + } + + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + lpcfg_set_option( + tctx->lp_ctx, + GENSEC_GSSAPI_REQUESTED_LIFETIME(KRB5_TICKET_LIFETIME)); + lpcfg_smbcli_options(tctx->lp_ctx, &options); + options.signing = SMB_SIGNING_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + /* + * We request a ticket lifetime of KRB5_TICKET_LIFETIME seconds. + * Give the server at least KRB5_TICKET_LIFETIME + KRB5_CLOCKSKEW + a + * few more milliseconds for this to kick in. + */ + endtime = timeval_current_ofs(KRB5_TICKET_EXPIRETIME, 500 * 1000); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + + smbXcli_session_set_disconnect_expired(tree->session->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_expire1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + + status = smb2_create(tree, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* get the security descriptor */ + + ZERO_STRUCT(qfinfo); + + qfinfo.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + qfinfo.access_information.in.file.handle = _h1; + + torture_comment(tctx, "query info => OK\n"); + + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + sleep_remaining(tctx, &endtime); + + torture_comment(tctx, "query info => EXPIRED\n"); + ZERO_STRUCT(qfinfo.access_information.out); + status = smb2_getinfo_file(tree, tctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, + NT_STATUS_NETWORK_SESSION_EXPIRED, + ret, done, "smb2_getinfo_file " + "returned unexpected status"); + + connected = smbXcli_conn_is_connected(tree->session->transport->conn); + torture_assert_goto(tctx, !connected, ret, done, "connected\n"); + + ret = true; +done: + cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED); + + if (h1 != NULL) { + smb2_util_close(tree, *h1); + } + + talloc_free(tree); + lpcfg_set_option(tctx->lp_ctx, GENSEC_GSSAPI_REQUESTED_LIFETIME(0)); + return ret; +} + +bool test_session_bind1(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "session_bind1_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree1, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* use the 1st connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* use the 2nd connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1 = NULL; + + /* + * Now bind the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_1, + samba_cmdline_get_creds(), + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + ret = true; +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_util_unlink(tree1, fname); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind2(struct torture_context *tctx, struct smb2_tree *tree1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *credentials = samba_cmdline_get_creds(); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname1[256]; + char fname2[256]; + struct smb2_handle _h1f1; + struct smb2_handle *h1f1 = NULL; + struct smb2_handle _h1f2; + struct smb2_handle *h1f2 = NULL; + struct smb2_handle _h2f2; + struct smb2_handle *h2f2 = NULL; + struct smb2_create io1f1; + struct smb2_create io1f2; + struct smb2_create io2f1; + struct smb2_create io2f2; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport2 = NULL; + struct smbcli_options options3; + struct smb2_tree *tree3 = NULL; + struct smb2_transport *transport3 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session1_3 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session2_3 = NULL; + uint32_t caps; + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname1, sizeof(fname1), "session_bind2_1_%s.dat", + generate_random_str(tctx, 8)); + snprintf(fname2, sizeof(fname2), "session_bind2_2_%s.dat", + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + smb2_oplock_create_share(&io1f1, fname1, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + smb2_oplock_create_share(&io1f2, fname2, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + + status = smb2_create(tree1, mem_ctx, &io1f1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1f1 = io1f1.out.file.handle; + h1f1 = &_h1f1; + CHECK_CREATED(tctx, &io1f1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1f1.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + smb2cli_tcon_should_sign(tree2->smbXcli, true); + + smb2_oplock_create_share(&io2f1, fname1, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + smb2_oplock_create_share(&io2f2, fname2, + smb2_util_share_access(""), + smb2_util_oplock_level("")); + + status = smb2_create(tree2, mem_ctx, &io2f2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h2f2 = io2f2.out.file.handle; + h2f2 = &_h2f2; + CHECK_CREATED(tctx, &io2f2, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io2f2.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + options3 = transport1->options; + options3.signing = SMB_SIGNING_REQUIRED; + options3.only_negprot = true; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree3, + tctx->ev, + &options3, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + transport3 = tree3->session->transport; + + /* + * Create a fake session for the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + /* + * Now bind the 3rd transport connection to the 1st session + */ + session1_3 = smb2_session_channel(transport3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session1_1); + torture_assert(tctx, session1_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* + * Create a fake session for the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + /* + * Now bind the 3rd transport connection to the 2nd session + */ + session2_3 = smb2_session_channel(transport3, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session2_2); + torture_assert(tctx, session2_3 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_3, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1f1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_getinfo_file failed"); + tree1->session = session1_3; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h2f2; + tree2->session = session2_1; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_getinfo_file failed"); + tree2->session = session2_2; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + tree2->session = session2_3; + status = smb2_getinfo_file(tree2, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree1->session = session1_2; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree1->session = session1_3; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + tree2->session = session2_1; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree2->session = session2_2; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree2->session = session2_3; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + smbXcli_conn_disconnect(transport3->conn, NT_STATUS_LOCAL_DISCONNECT); + smb_msleep(500); + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + tree1->session = session1_2; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + + tree2->session = session2_1; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_create failed"); + tree2->session = session2_2; + status = smb2_create(tree2, mem_ctx, &io2f1); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_SHARING_VIOLATION, ret, done, + "smb2_create failed"); + + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + smb_msleep(500); + h2f2 = NULL; + + tree1->session = session1_1; + status = smb2_create(tree1, mem_ctx, &io1f2); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1f2 = io1f2.out.file.handle; + h1f2 = &_h1f2; + CHECK_CREATED(tctx, &io1f2, EXISTED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1f2.out.oplock_level, + smb2_util_oplock_level(""), + "oplock_level incorrect"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1f1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1f1 = NULL; + + ret = true; +done: + + smbXcli_conn_disconnect(transport3->conn, NT_STATUS_LOCAL_DISCONNECT); + smbXcli_conn_disconnect(transport2->conn, NT_STATUS_LOCAL_DISCONNECT); + + tree1->session = session1_1; + tree2->session = session2_2; + + if (h1f1 != NULL) { + smb2_util_close(tree1, *h1f1); + } + if (h1f2 != NULL) { + smb2_util_close(tree1, *h1f2); + } + if (h2f2 != NULL) { + smb2_util_close(tree2, *h2f2); + } + + smb2_util_unlink(tree1, fname1); + smb2_util_unlink(tree1, fname2); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind_auth_mismatch(struct torture_context *tctx, + struct smb2_tree *tree1, + const char *testname, + struct cli_credentials *creds1, + struct cli_credentials *creds2, + bool creds2_require_ok) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_new(tctx); + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo; + bool ret = false; + struct smb2_tree *tree2 = NULL; + struct smb2_transport *transport1 = tree1->session->transport; + struct smbcli_options options2; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_1 = tree1->session; + struct smb2_session *session1_2 = NULL; + struct smb2_session *session2_1 = NULL; + struct smb2_session *session2_2 = NULL; + struct smb2_session *session3_1 = NULL; + uint32_t caps; + bool encrypted; + bool creds2_got_ok = false; + + encrypted = smb2cli_tcon_is_encryption_on(tree1->smbXcli); + + caps = smb2cli_conn_server_capabilities(transport1->conn); + if (!(caps & SMB2_CAP_MULTI_CHANNEL)) { + torture_skip(tctx, "server doesn't support SMB2_CAP_MULTI_CHANNEL\n"); + } + + /* + * We always want signing for this test! + */ + smb2cli_tcon_should_sign(tree1->smbXcli, true); + options2 = transport1->options; + options2.signing = SMB_SIGNING_REQUIRED; + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", testname, + generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + status = smb2_create(tree1, mem_ctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + creds1, + &tree2, + tctx->ev, + &options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect failed"); + session2_2 = tree2->session; + transport2 = tree2->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + creds1, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + + /* use the 1st connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_1; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* use the 2nd connection, 1st session */ + ZERO_STRUCT(qfinfo); + qfinfo.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, mem_ctx, &qfinfo); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + tree1->session = session1_1; + status = smb2_util_close(tree1, *h1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_close failed"); + h1 = NULL; + + /* + * Create a 3rd session in order to check if the invalid (creds2) + * are mapped to guest. + */ + session3_1 = smb2_session_init(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tctx); + torture_assert(tctx, session3_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session3_1, + creds2, + 0 /* previous_session_id */); + if (creds2_require_ok) { + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego worked"); + creds2_got_ok = true; + } else if (NT_STATUS_IS_OK(status)) { + bool authentiated = smbXcli_session_is_authenticated(session3_1->smbXcli); + torture_assert(tctx, !authentiated, "Invalid credentials allowed!"); + creds2_got_ok = true; + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + } + + /* + * Now bind the 1st transport connection to the 2nd session + */ + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked on invalid channel"); + + status = smb2_session_setup_spnego(session2_1, + creds2, + 0 /* previous_session_id */); + if (creds2_got_ok) { + /* + * attaching with a different user (guest or anonymous) results + * in ACCESS_DENIED. + */ + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_ACCESS_DENIED, ret, done, + "smb2_session_setup_spnego worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + } + + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked on invalid channel"); + + tree2->session = session2_2; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_unlink failed"); + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + if (creds2_got_ok) { + /* + * We got ACCESS_DENIED on the session bind + * with a different user, now check that + * the correct user can actually bind on + * the same connection. + */ + TALLOC_FREE(session2_1); + session2_1 = smb2_session_channel(transport1, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree1, + session2_2); + torture_assert(tctx, session2_1 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session2_1, + creds1, + 0 /* previous_session_id */); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_session_setup_spnego failed"); + tree2->session = session2_1; + status = smb2_util_unlink(tree2, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + tree2->session = session2_2; + } + + tree1->session = session1_1; + status = smb2_util_unlink(tree1, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + + tree1->session = session1_2; + status = smb2_util_unlink(tree1, fname); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + + if (creds2_got_ok) { + /* + * With valid credentials, there's no point to test a failing + * reauth. + */ + ret = true; + goto done; + } + + /* + * Do a failing reauth the 2nd channel + */ + status = smb2_session_setup_spnego(session1_2, + creds2, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_LOGON_FAILURE, ret, done, + "smb2_session_setup_spnego worked"); + + tree1->session = session1_1; + status = smb2_util_unlink(tree1, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport1->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked"); + } + + tree1->session = session1_2; + status = smb2_util_unlink(tree1, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport2->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_USER_SESSION_DELETED, ret, done, + "smb2_util_unlink worked"); + } + + status = smb2_util_unlink(tree2, fname); + if (encrypted) { + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport1->conn), ret, done, + "smb2_util_unlink worked"); + torture_assert_goto(tctx, !smbXcli_conn_is_connected(transport2->conn), ret, done, + "smb2_util_unlink worked"); + } else { + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND, ret, done, + "smb2_util_unlink worked"); + } + + ret = true; +done: + talloc_free(tree2); + tree1->session = session1_1; + + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + + smb2_util_unlink(tree1, fname); + + talloc_free(tree1); + + talloc_free(mem_ctx); + + return ret; +} + +static bool test_session_bind_invalid_auth(struct torture_context *tctx, struct smb2_tree *tree1) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + struct cli_credentials *invalid_credentials = NULL; + bool ret = false; + + invalid_credentials = cli_credentials_init(tctx); + torture_assert(tctx, (invalid_credentials != NULL), "talloc error"); + cli_credentials_set_username(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_domain(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_password(invalid_credentials, "__none__invalid__none__", CRED_SPECIFIED); + cli_credentials_set_realm(invalid_credentials, NULL, CRED_SPECIFIED); + cli_credentials_set_workstation(invalid_credentials, "", CRED_UNINITIALISED); + + ret = test_session_bind_auth_mismatch(tctx, tree1, __func__, + credentials, + invalid_credentials, + false); + return ret; +} + +static bool test_session_bind_different_user(struct torture_context *tctx, struct smb2_tree *tree1) +{ + struct cli_credentials *credentials1 = samba_cmdline_get_creds(); + struct cli_credentials *credentials2 = torture_user2_credentials(tctx, tctx); + char *u1 = cli_credentials_get_unparsed_name(credentials1, tctx); + char *u2 = cli_credentials_get_unparsed_name(credentials2, tctx); + bool ret = false; + bool bval; + + torture_assert(tctx, (credentials2 != NULL), "talloc error"); + bval = cli_credentials_is_anonymous(credentials2); + if (bval) { + torture_skip(tctx, "valid user2 credentials are required"); + } + bval = strequal(u1, u2); + if (bval) { + torture_skip(tctx, "different user2 credentials are required"); + } + + ret = test_session_bind_auth_mismatch(tctx, tree1, __func__, + credentials1, + credentials2, + true); + return ret; +} + +static bool test_session_bind_negative_smbXtoX(struct torture_context *tctx, + const char *testname, + struct cli_credentials *credentials, + const struct smbcli_options *options1, + const struct smbcli_options *options2, + NTSTATUS bind_reject_status) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + bool ret = false; + struct smb2_tree *tree1 = NULL; + struct smb2_session *session1_1 = NULL; + char fname[256]; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo1; + struct smb2_tree *tree2_0 = NULL; + struct smb2_transport *transport2 = NULL; + struct smb2_session *session1_2 = NULL; + uint64_t session1_id = 0; + uint16_t session1_flags = 0; + NTSTATUS deleted_status = NT_STATUS_USER_SESSION_DELETED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree1, + tctx->ev, + options1, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options1 failed"); + session1_1 = tree1->session; + session1_id = smb2cli_session_current_id(session1_1->smbXcli); + session1_flags = smb2cli_session_get_flags(session1_1->smbXcli); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", + testname, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + status = smb2_create(tree1, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials, + &tree2_0, + tctx->ev, + options2, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options2 failed"); + transport2 = tree2_0->session->transport; + + /* + * Now bind the 2nd transport connection to the 1st session + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, bind_reject_status, ret, done, + "smb2_session_setup_spnego failed"); + if (NT_STATUS_IS_OK(bind_reject_status)) { + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + tree1->session = session1_2; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + tree1->session = session1_1; + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + } + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + if (NT_STATUS_IS_OK(bind_reject_status)) { + deleted_status = NT_STATUS_ACCESS_DENIED; + bind_reject_status = NT_STATUS_ACCESS_DENIED; + } + + /* + * I guess this is not part of MultipleChannel_Negative_SMB2002, + * but we should also check the status without + * SMB2_SESSION_FLAG_BINDING. + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + session1_2->needs_bind = false; + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, deleted_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* + * ... and we should also check the status without any existing + * session keys. + */ + session1_2 = smb2_session_init(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + talloc_steal(tree2_0->session, transport2); + smb2cli_session_set_id_and_flags(session1_2->smbXcli, + session1_id, session1_flags); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, deleted_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* + * Now bind the 2nd transport connection to the 1st session (again) + */ + session1_2 = smb2_session_channel(transport2, + lpcfg_gensec_settings(tctx, tctx->lp_ctx), + tree2_0, + session1_1); + torture_assert(tctx, session1_2 != NULL, "smb2_session_channel failed"); + + status = smb2_session_setup_spnego(session1_2, + credentials, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal_goto(tctx, status, bind_reject_status, ret, done, + "smb2_session_setup_spnego failed"); + TALLOC_FREE(session1_2); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + talloc_free(tree2_0); + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + talloc_free(tree1); + + return ret; +} + +/* + * This is similar to the MultipleChannel_Negative_SMB2002 test + * from the Windows Protocol Test Suite. + * + * It demonstrates that the server needs to do lookup + * in the global session table in order to get the signing + * and error code of invalid session setups correct. + * + * See: https://bugzilla.samba.org/show_bug.cgi?id=14512 + * + * Note you can ignore tree0... + */ +static bool test_session_bind_negative_smb202(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.02 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_zero(); + options1.max_protocol = PROTOCOL_SMB2_02; + + options2 = options1; + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb210s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.max_protocol = PROTOCOL_SMB2_10; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb210d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.max_protocol = PROTOCOL_SMB2_10; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb2to3s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb2to3d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to2s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to2d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_00) { + torture_skip(tctx, + "Can't test without SMB3 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_NOT_ACCEPTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to3s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_02; + options1.max_protocol = PROTOCOL_SMB3_02; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3to3d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_02; + options1.max_protocol = PROTOCOL_SMB3_02; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3encGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3encGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_INVALID_PARAMETER); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_OK); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signHtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signCtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoCs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoCd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoHs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneGtoHd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneCtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneCtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneHtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3sneHtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + options2.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signC30toGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_02; + options1.signing = SMB_SIGNING_REQUIRED; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signC30toGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_00; + options1.max_protocol = PROTOCOL_SMB3_02; + options1.signing = SMB_SIGNING_REQUIRED; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signH2XtoGs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + options1.signing = SMB_SIGNING_REQUIRED; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signH2XtoGd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB2_02; + options1.max_protocol = PROTOCOL_SMB2_10; + options1.signing = SMB_SIGNING_REQUIRED; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_11; + options2.max_protocol = PROTOCOL_SMB3_11; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_NOT_SUPPORTED); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoC30s(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_02; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoC30d(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB3_00; + options2.max_protocol = PROTOCOL_SMB3_02; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoH2Xs(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* same client guid */ + options2 = options1; + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_bind_negative_smb3signGtoH2Xd(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + struct smbcli_options options2; + bool ok; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test SMB 2.10 if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + /* different client guid */ + options2 = options1; + options2.client_guid = GUID_random(); + options2.only_negprot = true; + options2.min_protocol = PROTOCOL_SMB2_02; + options2.max_protocol = PROTOCOL_SMB2_10; + options2.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_bind_negative_smbXtoX(tctx, __func__, + credentials, + &options1, &options2, + NT_STATUS_REQUEST_OUT_OF_SEQUENCE); + talloc_free(tree0); + return ret; +} + +static bool test_session_two_logoff(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + NTSTATUS status; + bool ret = true; + struct smbcli_options transport2_options; + struct smb2_tree *tree2 = NULL; + struct smb2_session *session2 = NULL; + struct smb2_session *session1 = tree1->session; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2; + bool ok; + + /* Connect 2nd connection */ + torture_comment(tctx, "connect tree2 with the same client_guid\n"); + transport2_options = transport1->options; + ok = torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2); + torture_assert(tctx, ok, "couldn't connect tree2\n"); + transport2 = tree2->session->transport; + session2 = tree2->session; + + torture_comment(tctx, "session2: logoff\n"); + status = smb2_logoff(session2); + torture_assert_ntstatus_ok(tctx, status, "session2: logoff"); + torture_comment(tctx, "transport2: keepalive\n"); + status = smb2_keepalive(transport2); + torture_assert_ntstatus_ok(tctx, status, "transport2: keepalive"); + torture_comment(tctx, "transport2: disconnect\n"); + TALLOC_FREE(tree2); + + torture_comment(tctx, "session1: logoff\n"); + status = smb2_logoff(session1); + torture_assert_ntstatus_ok(tctx, status, "session1: logoff"); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + torture_assert_ntstatus_ok(tctx, status, "transport1: keepalive"); + torture_comment(tctx, "transport1: disconnect\n"); + TALLOC_FREE(tree1); + + return ret; +} + +static bool test_session_sign_enc(struct torture_context *tctx, + const char *testname, + struct cli_credentials *credentials1, + const struct smbcli_options *options1) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + NTSTATUS status; + bool ret = false; + struct smb2_tree *tree1 = NULL; + char fname[256]; + struct smb2_handle rh = {{0}}; + struct smb2_handle _h1; + struct smb2_handle *h1 = NULL; + struct smb2_create io1; + union smb_fileinfo qfinfo1; + union smb_notify notify; + struct smb2_request *req = NULL; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + credentials1, + &tree1, + tctx->ev, + options1, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx) + ); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_connect options1 failed"); + + status = smb2_util_roothandle(tree1, &rh); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_util_roothandle failed"); + + /* Add some random component to the file name. */ + snprintf(fname, sizeof(fname), "%s_%s.dat", + testname, generate_random_str(tctx, 8)); + + smb2_util_unlink(tree1, fname); + + smb2_oplock_create_share(&io1, fname, + smb2_util_share_access(""), + smb2_util_oplock_level("b")); + + io1.in.create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE; + status = smb2_create(tree1, tctx, &io1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_create failed"); + _h1 = io1.out.file.handle; + h1 = &_h1; + CHECK_CREATED(tctx, &io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + torture_assert_int_equal(tctx, io1.out.oplock_level, + smb2_util_oplock_level("b"), + "oplock_level incorrect"); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + /* ask for a change notify, + on file or directory name changes */ + ZERO_STRUCT(notify); + notify.smb2.level = RAW_NOTIFY_SMB2; + notify.smb2.in.buffer_size = 1000; + notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; + notify.smb2.in.file.handle = rh; + notify.smb2.in.recursive = true; + + req = smb2_notify_send(tree1, &(notify.smb2)); + WAIT_FOR_ASYNC_RESPONSE(req); + + status = smb2_cancel(req); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_cancel failed"); + + status = smb2_notify_recv(req, tctx, &(notify.smb2)); + torture_assert_ntstatus_equal_goto(tctx, status, NT_STATUS_CANCELLED, + ret, done, + "smb2_notify_recv failed"); + + /* Check the initial session is still alive */ + ZERO_STRUCT(qfinfo1); + qfinfo1.generic.level = RAW_FILEINFO_POSITION_INFORMATION; + qfinfo1.generic.in.file.handle = _h1; + status = smb2_getinfo_file(tree1, tctx, &qfinfo1); + torture_assert_ntstatus_ok_goto(tctx, status, ret, done, + "smb2_getinfo_file failed"); + + ret = true; +done: + if (h1 != NULL) { + smb2_util_close(tree1, *h1); + } + TALLOC_FREE(tree1); + + return ret; +} + +static bool test_session_signing_hmac_sha_256(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_HMAC_SHA256, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_signing_aes_128_cmac(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_CMAC, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_signing_aes_128_gmac(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials = samba_cmdline_get_creds(); + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool encrypted; + + encrypted = smb2cli_tcon_is_encryption_on(tree0->smbXcli); + if (encrypted) { + torture_skip(tctx, + "Can't test signing only if encryption is required"); + } + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.signing = (struct smb3_signing_capabilities) { + .num_algos = 1, + .algos = { + SMB2_SIGNING_AES128_GMAC, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_128_ccm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_CCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_128_gcm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES128_GCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_256_ccm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES256_CCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_encryption_aes_256_gcm(struct torture_context *tctx, struct smb2_tree *tree0) +{ + struct cli_credentials *credentials0 = samba_cmdline_get_creds(); + struct cli_credentials *credentials = NULL; + bool ret = false; + struct smb2_transport *transport0 = tree0->session->transport; + struct smbcli_options options1; + bool ok; + + if (smbXcli_conn_protocol(transport0->conn) < PROTOCOL_SMB3_11) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 support"); + } + + if (smb2cli_conn_server_signing_algo(transport0->conn) < SMB2_SIGNING_AES128_GMAC) { + torture_skip(tctx, + "Can't test without SMB 3.1.1 signing negotiation support"); + } + + credentials = cli_credentials_shallow_copy(tctx, credentials0); + torture_assert(tctx, credentials != NULL, "cli_credentials_shallow_copy"); + ok = cli_credentials_set_smb_encryption(credentials, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + torture_assert(tctx, ok, "cli_credentials_set_smb_encryption"); + + options1 = transport0->options; + options1.client_guid = GUID_random(); + options1.min_protocol = PROTOCOL_SMB3_11; + options1.max_protocol = PROTOCOL_SMB3_11; + options1.signing = SMB_SIGNING_REQUIRED; + options1.smb3_capabilities.encryption = (struct smb3_encryption_capabilities) { + .num_algos = 1, + .algos = { + SMB2_ENCRYPTION_AES256_GCM, + }, + }; + + ret = test_session_sign_enc(tctx, + __func__, + credentials, + &options1); + TALLOC_FREE(tree0); + return ret; +} + +static bool test_session_ntlmssp_bug14932(struct torture_context *tctx, struct smb2_tree *tree) +{ + struct cli_credentials *ntlm_creds = + cli_credentials_shallow_copy(tctx, samba_cmdline_get_creds()); + NTSTATUS status; + bool ret = true; + /* + * This is a NTLMv2_RESPONSE with the strange + * NTLMv2_CLIENT_CHALLENGE used by the net diag + * tool. + * + * As we expect an error anyway we fill the + * Response part with 0xab... + */ + static const char *netapp_magic = + "\xab\xab\xab\xab\xab\xab\xab\xab" + "\xab\xab\xab\xab\xab\xab\xab\xab" + "\x01\x01\x00\x00\x00\x00\x00\x00" + "\x3f\x3f\x3f\x3f\x3f\x3f\x3f\x3f" + "\xb8\x82\x3a\xf1\xb3\xdd\x08\x15" + "\x00\x00\x00\x00\x11\xa2\x08\x81" + "\x50\x38\x22\x78\x2b\x94\x47\xfe" + "\x54\x94\x7b\xff\x17\x27\x5a\xb4" + "\xf4\x18\xba\xdc\x2c\x38\xfd\x5b" + "\xfb\x0e\xc1\x85\x1e\xcc\x92\xbb" + "\x9b\xb1\xc4\xd5\x53\x14\xff\x8c" + "\x76\x49\xf5\x45\x90\x19\xa2"; + DATA_BLOB lm_response = data_blob_talloc_zero(tctx, 24); + DATA_BLOB lm_session_key = data_blob_talloc_zero(tctx, 16); + DATA_BLOB nt_response = data_blob_const(netapp_magic, 95); + DATA_BLOB nt_session_key = data_blob_talloc_zero(tctx, 16); + + cli_credentials_set_kerberos_state(ntlm_creds, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + cli_credentials_set_ntlm_response(ntlm_creds, + &lm_response, + &lm_session_key, + &nt_response, + &nt_session_key, + CRED_SPECIFIED); + status = smb2_session_setup_spnego(tree->session, + ntlm_creds, + 0 /* previous_session_id */); + torture_assert_ntstatus_equal(tctx, status, NT_STATUS_INVALID_PARAMETER, + "smb2_session_setup_spnego failed"); + + return ret; +} + +struct torture_suite *torture_smb2_session_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "session"); + + torture_suite_add_1smb2_test(suite, "reconnect1", test_session_reconnect1); + torture_suite_add_1smb2_test(suite, "reconnect2", test_session_reconnect2); + torture_suite_add_1smb2_test(suite, "reauth1", test_session_reauth1); + torture_suite_add_1smb2_test(suite, "reauth2", test_session_reauth2); + torture_suite_add_1smb2_test(suite, "reauth3", test_session_reauth3); + torture_suite_add_1smb2_test(suite, "reauth4", test_session_reauth4); + torture_suite_add_1smb2_test(suite, "reauth5", test_session_reauth5); + torture_suite_add_1smb2_test(suite, "reauth6", test_session_reauth6); + torture_suite_add_simple_test(suite, "expire1n", test_session_expire1n); + torture_suite_add_simple_test(suite, "expire1s", test_session_expire1s); + torture_suite_add_simple_test(suite, "expire1e", test_session_expire1e); + torture_suite_add_simple_test(suite, "expire2s", test_session_expire2s); + torture_suite_add_simple_test(suite, "expire2e", test_session_expire2e); + torture_suite_add_simple_test(suite, "expire_disconnect", + test_session_expire_disconnect); + torture_suite_add_1smb2_test(suite, "bind1", test_session_bind1); + torture_suite_add_1smb2_test(suite, "bind2", test_session_bind2); + torture_suite_add_1smb2_test(suite, "bind_invalid_auth", test_session_bind_invalid_auth); + torture_suite_add_1smb2_test(suite, "bind_different_user", test_session_bind_different_user); + torture_suite_add_1smb2_test(suite, "bind_negative_smb202", test_session_bind_negative_smb202); + torture_suite_add_1smb2_test(suite, "bind_negative_smb210s", test_session_bind_negative_smb210s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb210d", test_session_bind_negative_smb210d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb2to3s", test_session_bind_negative_smb2to3s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb2to3d", test_session_bind_negative_smb2to3d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to2s", test_session_bind_negative_smb3to2s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to2d", test_session_bind_negative_smb3to2d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to3s", test_session_bind_negative_smb3to3s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3to3d", test_session_bind_negative_smb3to3d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3encGtoCs", test_session_bind_negative_smb3encGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3encGtoCd", test_session_bind_negative_smb3encGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoHs", test_session_bind_negative_smb3signCtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoHd", test_session_bind_negative_smb3signCtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoGs", test_session_bind_negative_smb3signCtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signCtoGd", test_session_bind_negative_smb3signCtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoCs", test_session_bind_negative_smb3signHtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoCd", test_session_bind_negative_smb3signHtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoGs", test_session_bind_negative_smb3signHtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signHtoGd", test_session_bind_negative_smb3signHtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoCs", test_session_bind_negative_smb3signGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoCd", test_session_bind_negative_smb3signGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoHs", test_session_bind_negative_smb3signGtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoHd", test_session_bind_negative_smb3signGtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoCs", test_session_bind_negative_smb3sneGtoCs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoCd", test_session_bind_negative_smb3sneGtoCd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoHs", test_session_bind_negative_smb3sneGtoHs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneGtoHd", test_session_bind_negative_smb3sneGtoHd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneCtoGs", test_session_bind_negative_smb3sneCtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneCtoGd", test_session_bind_negative_smb3sneCtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneHtoGs", test_session_bind_negative_smb3sneHtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3sneHtoGd", test_session_bind_negative_smb3sneHtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signC30toGs", test_session_bind_negative_smb3signC30toGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signC30toGd", test_session_bind_negative_smb3signC30toGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signH2XtoGs", test_session_bind_negative_smb3signH2XtoGs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signH2XtoGd", test_session_bind_negative_smb3signH2XtoGd); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoC30s", test_session_bind_negative_smb3signGtoC30s); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoC30d", test_session_bind_negative_smb3signGtoC30d); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoH2Xs", test_session_bind_negative_smb3signGtoH2Xs); + torture_suite_add_1smb2_test(suite, "bind_negative_smb3signGtoH2Xd", test_session_bind_negative_smb3signGtoH2Xd); + torture_suite_add_1smb2_test(suite, "two_logoff", test_session_two_logoff); + torture_suite_add_1smb2_test(suite, "signing-hmac-sha-256", test_session_signing_hmac_sha_256); + torture_suite_add_1smb2_test(suite, "signing-aes-128-cmac", test_session_signing_aes_128_cmac); + torture_suite_add_1smb2_test(suite, "signing-aes-128-gmac", test_session_signing_aes_128_gmac); + torture_suite_add_1smb2_test(suite, "encryption-aes-128-ccm", test_session_encryption_aes_128_ccm); + torture_suite_add_1smb2_test(suite, "encryption-aes-128-gcm", test_session_encryption_aes_128_gcm); + torture_suite_add_1smb2_test(suite, "encryption-aes-256-ccm", test_session_encryption_aes_256_ccm); + torture_suite_add_1smb2_test(suite, "encryption-aes-256-gcm", test_session_encryption_aes_256_gcm); + torture_suite_add_1smb2_test(suite, "ntlmssp_bug14932", test_session_ntlmssp_bug14932); + + suite->description = talloc_strdup(suite, "SMB2-SESSION tests"); + + return suite; +} + +static bool test_session_require_sign_bug15397(struct torture_context *tctx, + struct smb2_tree *_tree) +{ + const char *host = torture_setting_string(tctx, "host", NULL); + const char *share = torture_setting_string(tctx, "share", NULL); + struct cli_credentials *_creds = samba_cmdline_get_creds(); + struct cli_credentials *creds = NULL; + struct smbcli_options options; + struct smb2_tree *tree = NULL; + uint8_t security_mode; + NTSTATUS status; + bool ok = true; + + /* + * Setup our own connection so we can control the signing flags + */ + + creds = cli_credentials_shallow_copy(tctx, _creds); + torture_assert(tctx, creds != NULL, "cli_credentials_shallow_copy"); + + options = _tree->session->transport->options; + options.client_guid = GUID_random(); + options.signing = SMB_SIGNING_IF_REQUIRED; + + status = smb2_connect(tctx, + host, + lpcfg_smb_ports(tctx->lp_ctx), + share, + lpcfg_resolve_context(tctx->lp_ctx), + creds, + &tree, + tctx->ev, + &options, + lpcfg_socket_options(tctx->lp_ctx), + lpcfg_gensec_settings(tctx, tctx->lp_ctx)); + torture_assert_ntstatus_ok_goto(tctx, status, ok, done, + "smb2_connect failed"); + + security_mode = smb2cli_session_security_mode(tree->session->smbXcli); + + torture_assert_int_equal_goto( + tctx, + security_mode, + SMB2_NEGOTIATE_SIGNING_REQUIRED | SMB2_NEGOTIATE_SIGNING_ENABLED, + ok, + done, + "Signing not required"); + +done: + return ok; +} + +struct torture_suite *torture_smb2_session_req_sign_init(TALLOC_CTX *ctx) +{ + struct torture_suite *suite = + torture_suite_create(ctx, "session-require-signing"); + + torture_suite_add_1smb2_test(suite, "bug15397", + test_session_require_sign_bug15397); + + suite->description = talloc_strdup(suite, "SMB2-SESSION require signing tests"); + return suite; +} |