From 2173169e191754887acddb669a937b872b7ce017 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 15:27:50 +1000 Subject: added support for all of the known SMB2 create tags in our client library (This used to be commit 597b38e97b01d2137e6ac96ca07cd56fadb2c09e) --- source4/libcli/raw/interfaces.h | 20 ++++- source4/libcli/smb2/create.c | 186 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 195 insertions(+), 11 deletions(-) diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index 17c85138ac..d170006d3b 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -1586,9 +1586,17 @@ union smb_open { /* dynamic body */ const char *fname; - /* optional list of extended attributes */ + /* now some optional parameters - encoded as tagged blobs */ struct smb_ea_list eas; - + uint64_t alloc_size; + struct security_descriptor *sec_desc; + bool durable_open; + struct smb2_handle *durable_handle; + bool query_maximal_access; + NTTIME timewarp; + bool query_on_disk_id; + + /* and any additional blobs the caller wants */ struct smb2_create_blobs { uint32_t num_blobs; struct smb2_create_blob { @@ -1617,8 +1625,12 @@ union smb_open { /* uint32_t blob_ofs; */ /* uint32_t blob_size; */ - /* dynamic body */ - DATA_BLOB blob; + /* optional return values matching tagged values in the call */ + uint32_t maximal_access; + uint8_t on_disk_id[32]; + + /* tagged blobs in the reply */ + struct smb2_create_blobs blobs; } out; } smb2; }; diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index 1901cb4977..4a97f8aafb 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -24,6 +24,74 @@ #include "libcli/raw/raw_proto.h" #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" +#include "librpc/gen_ndr/ndr_security.h" + + +/* + parse a set of SMB2 create blobs +*/ +NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer, + struct smb2_create_blobs *blobs) +{ + const uint8_t *data = buffer.data; + uint32_t remaining = buffer.length; + + while (remaining > 0) { + uint32_t next; + uint32_t name_offset, name_length; + uint32_t reserved, data_offset; + uint32_t data_length; + char *tag; + DATA_BLOB b; + NTSTATUS status; + + if (remaining < 16) { + return NT_STATUS_INVALID_PARAMETER; + } + next = IVAL(data, 0); + name_offset = SVAL(data, 4); + name_length = SVAL(data, 6); + reserved = SVAL(data, 8); + data_offset = SVAL(data, 10); + data_length = IVAL(data, 12); + + if ((next & 0x7) != 0 || + next > remaining || + name_offset < 16 || + name_offset > remaining || + name_offset + name_length > remaining || + data_offset < name_offset + name_length || + data_offset > remaining || + data_offset + (uint64_t)data_length > remaining) { + return NT_STATUS_INVALID_PARAMETER; + } + + tag = talloc_strndup(mem_ctx, (const char *)data + name_offset, name_length); + if (tag == NULL) { + return NT_STATUS_NO_MEMORY; + } + + b = data_blob_const(data+data_offset, data_length); + status = smb2_create_blob_add(mem_ctx, blobs, tag, b); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + talloc_free(tag); + + if (next == 0) break; + + remaining -= next; + data += next; + + if (remaining < 16) { + return NT_STATUS_INVALID_PARAMETER; + } + } + + return NT_STATUS_OK; +} + /* add a blob to a smb2_create attribute blob @@ -116,6 +184,7 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create return NULL; } + /* now add all the optional blobs */ if (io->in.eas.num_eas != 0) { DATA_BLOB b = data_blob_talloc(req, NULL, ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas, 4)); @@ -131,13 +200,87 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create /* an empty MxAc tag seems to be used to ask the server to return the maximum access mask allowed on the file */ - status = smb2_create_blob_add(req, &blobs, - SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0)); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(req); - return NULL; + if (io->in.query_maximal_access) { + /* TODO: MS-SMB2 2.2.13.2.5 says this can contain a timestamp? What to do + with that if it doesn't match? */ + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.alloc_size != 0) { + uint8_t data[8]; + SBVAL(data, 0, io->in.alloc_size); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_ALSI, data_blob_const(data, 8)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.durable_open) { + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_DHNQ, data_blob_talloc_zero(req, 16)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.durable_handle) { + uint8_t data[16]; + smb2_push_handle(data, io->in.durable_handle); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_DHNC, data_blob_const(data, 16)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.timewarp) { + uint8_t data[8]; + SBVAL(data, 0, io->in.timewarp); + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_TWRP, data_blob_const(data, 8)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } } + if (io->in.sec_desc) { + enum ndr_err_code ndr_err; + DATA_BLOB sd_blob; + ndr_err = ndr_push_struct_blob(&sd_blob, req, NULL, + io->in.sec_desc, + (ndr_push_flags_fn_t)ndr_push_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + talloc_free(req); + return NULL; + } + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_SECD, sd_blob); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + if (io->in.query_on_disk_id) { + status = smb2_create_blob_add(req, &blobs, + SMB2_CREATE_TAG_QFID, data_blob(NULL, 0)); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + /* and any custom blobs */ for (i=0; i < blobs.num_blobs; i++) { bool last = false; const struct smb2_create_blob *c; @@ -173,6 +316,8 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io) { NTSTATUS status; + DATA_BLOB blob; + int i; if (!smb2_request_receive(req) || !smb2_request_is_ok(req)) { @@ -180,7 +325,7 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct } SMB2_CHECK_PACKET_RECV(req, 0x58, true); - + ZERO_STRUCT(io->out); io->out.oplock_level = CVAL(req->in.body, 0x02); io->out.reserved = CVAL(req->in.body, 0x03); io->out.create_action = IVAL(req->in.body, 0x04); @@ -193,12 +338,39 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct io->out.file_attr = IVAL(req->in.body, 0x38); io->out.reserved2 = IVAL(req->in.body, 0x3C); smb2_pull_handle(req->in.body+0x40, &io->out.file.handle); - status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &io->out.blob); + status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &blob); if (!NT_STATUS_IS_OK(status)) { smb2_request_destroy(req); return status; } + status = smb2_create_blob_parse(mem_ctx, blob, &io->out.blobs); + if (!NT_STATUS_IS_OK(status)) { + smb2_request_destroy(req); + return status; + } + + /* pull out the parsed blobs */ + for (i=0;iout.blobs.num_blobs;i++) { + if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) { + /* why 8 bytes not 4?? */ + if (io->out.blobs.blobs[i].data.length != 8) { + smb2_request_destroy(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 0); + } + if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) { + if (io->out.blobs.blobs[i].data.length != 32) { + smb2_request_destroy(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + memcpy(io->out.on_disk_id, io->out.blobs.blobs[i].data.data, 32); + } + } + + data_blob_free(&blob); + return smb2_request_destroy(req); } -- cgit From 773f5cce80322d8674ac0b99b0e0ea641991af1b Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:27:38 +1000 Subject: expose a function for pushing all SMB2 create blobs (This used to be commit f5985a0490e4105a9b0208f6b7b19e635db324f9) --- source4/libcli/smb2/create.c | 53 ++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index 4a97f8aafb..b976b528f1 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -125,6 +125,35 @@ static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer return NT_STATUS_OK; } + +/* + create a buffer of a set of create blobs +*/ +NTSTATUS smb2_create_blob_push(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer, + const struct smb2_create_blobs blobs) +{ + int i; + NTSTATUS status; + + *buffer = data_blob(NULL, 0); + for (i=0; i < blobs.num_blobs; i++) { + bool last = false; + const struct smb2_create_blob *c; + + if ((i + 1) == blobs.num_blobs) { + last = true; + } + + c = &blobs.blobs[i]; + status = smb2_create_blob_push_one(mem_ctx, buffer, c, last); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + return NT_STATUS_OK; +} + + NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b, const char *tag, DATA_BLOB data) { @@ -160,8 +189,7 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create { struct smb2_request *req; NTSTATUS status; - DATA_BLOB blob = data_blob(NULL, 0); - uint32_t i; + DATA_BLOB blob; struct smb2_create_blobs blobs = io->in.blobs; req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0); @@ -281,21 +309,10 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create } /* and any custom blobs */ - for (i=0; i < blobs.num_blobs; i++) { - bool last = false; - const struct smb2_create_blob *c; - - if ((i + 1) == blobs.num_blobs) { - last = true; - } - - c = &blobs.blobs[i]; - status = smb2_create_blob_push_one(req, &blob, - c, last); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(req); - return NULL; - } + status = smb2_create_blob_push(req, &blob, blobs); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; } status = smb2_push_o32s32_blob(&req->out, 0x30, blob); @@ -304,6 +321,8 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create return NULL; } + data_blob_free(&blob); + smb2_transport_send(req); return req; -- cgit From 6b707263058ec69f12a6235890005a226c8671de Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:28:37 +1000 Subject: implement the documented SMB2 create blobs in the server Not all of them are honoured yet, but they are all parsed and the ones that have SMB equivalents are honoured (This used to be commit 9fc70e2ed6a54f6d9a0530f4d37c0f8acadb6778) --- source4/ntvfs/ipc/vfs_ipc.c | 2 +- source4/ntvfs/ntvfs_generic.c | 14 +++++--- source4/ntvfs/posix/pvfs_open.c | 3 ++ source4/smb_server/smb2/fileio.c | 76 ++++++++++++++++++++++++++++++++++++---- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c index ea7b54ae6a..8ac7ac7d03 100644 --- a/source4/ntvfs/ipc/vfs_ipc.c +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -321,6 +321,7 @@ static NTSTATUS ipc_open_smb2(struct ntvfs_module_context *ntvfs, status = ipc_open_generic(ntvfs, req, oi->smb2.in.fname, &p); NT_STATUS_NOT_OK_RETURN(status); + ZERO_STRUCT(oi->smb2.out); oi->smb2.out.file.ntvfs = p->handle; oi->smb2.out.oplock_level = oi->smb2.in.oplock_level; oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED; @@ -332,7 +333,6 @@ static NTSTATUS ipc_open_smb2(struct ntvfs_module_context *ntvfs, oi->smb2.out.size = 0; oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL; oi->smb2.out.reserved2 = 0; - oi->smb2.out.blob = data_blob(NULL, 0); return status; } diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c index 06d89a717b..9227295696 100644 --- a/source4/ntvfs/ntvfs_generic.c +++ b/source4/ntvfs/ntvfs_generic.c @@ -207,6 +207,7 @@ static NTSTATUS ntvfs_map_open_finish(struct ntvfs_module_context *ntvfs, break; case RAW_OPEN_SMB2: + ZERO_STRUCT(io->smb2.out); io->smb2.out.file.ntvfs = io2->generic.out.file.ntvfs; switch (io2->generic.out.oplock_level) { case BATCH_OPLOCK_RETURN: @@ -232,7 +233,6 @@ static NTSTATUS ntvfs_map_open_finish(struct ntvfs_module_context *ntvfs, io->smb2.out.size = io2->generic.out.size; io->smb2.out.file_attr = io2->generic.out.attrib; io->smb2.out.reserved2 = 0; - io->smb2.out.blob = data_blob(NULL, 0); break; default: @@ -512,7 +512,7 @@ NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs, } io2->generic.in.root_fid = 0; io2->generic.in.access_mask = io->smb2.in.desired_access; - io2->generic.in.alloc_size = 0; + io2->generic.in.alloc_size = io->smb2.in.alloc_size; io2->generic.in.file_attr = io->smb2.in.file_attributes; io2->generic.in.share_access = io->smb2.in.share_access; io2->generic.in.open_disposition= io->smb2.in.create_disposition; @@ -520,8 +520,14 @@ NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs, io2->generic.in.impersonation = io->smb2.in.impersonation_level; io2->generic.in.security_flags = 0; io2->generic.in.fname = io->smb2.in.fname; - io2->generic.in.sec_desc = NULL; - io2->generic.in.ea_list = NULL; + io2->generic.in.sec_desc = io->smb2.in.sec_desc; + io2->generic.in.ea_list = &io->smb2.in.eas; + + /* we don't support timewarp yet */ + if (io->smb2.in.timewarp != 0) { + status = NT_STATUS_OBJECT_NAME_NOT_FOUND; + break; + } /* we need to check these bits before we check the private mask */ if (io2->generic.in.create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) { diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 49710806c7..0f08136a79 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -634,6 +634,8 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return status; } + /* support initial alloc sizes */ + name->dos.alloc_size = io->ntcreatex.in.alloc_size; name->dos.attrib = attrib; status = pvfs_dosattrib_save(pvfs, name, fd); if (!NT_STATUS_IS_OK(status)) { @@ -1464,6 +1466,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, talloc_free(lck); return pvfs_map_errno(pvfs, errno); } + name->dos.alloc_size = io->ntcreatex.in.alloc_size; name->dos.attrib = attrib; status = pvfs_dosattrib_save(pvfs, name, fd); if (!NT_STATUS_IS_OK(status)) { diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c index 5ab217bbfd..086ddc690e 100644 --- a/source4/smb_server/smb2/fileio.c +++ b/source4/smb_server/smb2/fileio.c @@ -25,14 +25,19 @@ #include "smb_server/smb2/smb2_server.h" #include "ntvfs/ntvfs.h" #include "param/param.h" +#include "libcli/raw/libcliraw.h" +#include "libcli/raw/raw_proto.h" +#include "librpc/gen_ndr/ndr_security.h" static void smb2srv_create_send(struct ntvfs_request *ntvfs) { struct smb2srv_request *req; union smb_open *io; + DATA_BLOB blob; SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open); - SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, io->smb2.out.blob.length)); + SMB2SRV_CHECK(smb2_create_blob_push(req, &blob, io->smb2.out.blobs)); + SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, blob.length)); SCVAL(req->out.body, 0x02, io->smb2.out.oplock_level); SCVAL(req->out.body, 0x03, io->smb2.out.reserved); @@ -46,7 +51,7 @@ static void smb2srv_create_send(struct ntvfs_request *ntvfs) SIVAL(req->out.body, 0x38, io->smb2.out.file_attr); SIVAL(req->out.body, 0x3C, io->smb2.out.reserved2); smb2srv_push_handle(req->out.body, 0x40, io->smb2.out.file.ntvfs); - SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, io->smb2.out.blob)); + SMB2SRV_CHECK(smb2_push_o32s32_blob(&req->out, 0x50, blob)); /* also setup the chained file handle */ req->chained_file_handle = req->_chained_file_handle; @@ -59,11 +64,13 @@ void smb2srv_create_recv(struct smb2srv_request *req) { union smb_open *io; DATA_BLOB blob; + int i; SMB2SRV_CHECK_BODY_SIZE(req, 0x38, true); SMB2SRV_TALLOC_IO_PTR(io, union smb_open); SMB2SRV_SETUP_NTVFS_REQUEST(smb2srv_create_send, NTVFS_ASYNC_STATE_MAY_ASYNC); + ZERO_STRUCT(io->smb2.in); io->smb2.level = RAW_OPEN_SMB2; io->smb2.in.security_flags = CVAL(req->in.body, 0x02); io->smb2.in.oplock_level = CVAL(req->in.body, 0x03); @@ -77,10 +84,67 @@ void smb2srv_create_recv(struct smb2srv_request *req) io->smb2.in.create_options = IVAL(req->in.body, 0x28); SMB2SRV_CHECK(smb2_pull_o16s16_string(&req->in, io, req->in.body+0x2C, &io->smb2.in.fname)); SMB2SRV_CHECK(smb2_pull_o32s32_blob(&req->in, io, req->in.body+0x30, &blob)); - /* TODO: parse the blob */ - ZERO_STRUCT(io->smb2.in.eas); - ZERO_STRUCT(io->smb2.in.blobs); - + SMB2SRV_CHECK(smb2_create_blob_parse(io, blob, &io->smb2.in.blobs)); + + /* interpret the parsed tags that a server needs to respond to */ + for (i=0;ismb2.in.blobs.num_blobs;i++) { + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_EXTA) == 0) { + SMB2SRV_CHECK(ea_pull_list_chained(&io->smb2.in.blobs.blobs[i].data, io, + &io->smb2.in.eas.num_eas, + &io->smb2.in.eas.eas)); + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_SECD) == 0) { + enum ndr_err_code ndr_err; + io->smb2.in.sec_desc = talloc(io, struct security_descriptor); + if (io->smb2.in.sec_desc == NULL) { + smb2srv_send_error(req, NT_STATUS_NO_MEMORY); + return; + } + ndr_err = ndr_pull_struct_blob(&io->smb2.in.blobs.blobs[i].data, io, NULL, + io->smb2.in.sec_desc, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + smb2srv_send_error(req, ndr_map_error2ntstatus(ndr_err)); + return; + } + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNQ) == 0) { + io->smb2.in.durable_open = true; + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_DHNC) == 0) { + if (io->smb2.in.blobs.blobs[i].data.length != 16) { + smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + io->smb2.in.durable_handle = talloc(io, struct smb2_handle); + if (io->smb2.in.durable_handle == NULL) { + smb2srv_send_error(req, NT_STATUS_NO_MEMORY); + return; + } + smb2_pull_handle(io->smb2.in.blobs.blobs[i].data.data, io->smb2.in.durable_handle); + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_ALSI) == 0) { + if (io->smb2.in.blobs.blobs[i].data.length != 8) { + smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + io->smb2.in.alloc_size = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0); + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) { + io->smb2.in.query_maximal_access = true; + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_TWRP) == 0) { + if (io->smb2.in.blobs.blobs[i].data.length != 8) { + smb2srv_send_error(req, NT_STATUS_INVALID_PARAMETER); + return; + } + io->smb2.in.timewarp = BVAL(io->smb2.in.blobs.blobs[i].data.data, 0); + } + if (strcmp(io->smb2.in.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) { + io->smb2.in.query_on_disk_id = true; + } + } + /* the VFS backend does not yet handle NULL filenames */ if (io->smb2.in.fname == NULL) { io->smb2.in.fname = ""; -- cgit From 082272e49d3cb8022a43fff5bfbcdf79adb5a44f Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:28:50 +1000 Subject: added testing of SMB2 create blobs (This used to be commit a48cbec6b90c6ba9db870fc33eed06b36612d8ff) --- source4/torture/smb2/create.c | 102 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 9c9e7e2997..070cb65c5c 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -38,8 +38,8 @@ #define CHECK_EQUAL(v, correct) do { \ if (v != correct) { \ - printf("(%s) Incorrect value for %s 0x%08x - should be 0x%08x\n", \ - __location__, #v, v, correct); \ + printf("(%s) Incorrect value for %s 0x%08llx - should be 0x%08llx\n", \ + __location__, #v, (unsigned long long)v, (unsigned long long)correct); \ return false; \ }} while (0) @@ -166,6 +166,9 @@ bool torture_smb2_create_gentest(struct torture_context *torture, struct smb2_tr status = smb2_create(tree, tmp_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + io.in.fname = FNAME; io.in.file_attributes = 0x8040; io.in.share_access = @@ -174,6 +177,100 @@ bool torture_smb2_create_gentest(struct torture_context *torture, struct smb2_tr CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); talloc_free(tmp_ctx); + + smb2_deltree(tree, FNAME); + + return true; +} + + +/* + try the various request blobs + */ +bool torture_smb2_create_blob(struct torture_context *torture, struct smb2_tree *tree) +{ + struct smb2_create io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + smb2_deltree(tree, FNAME); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io.in.security_flags = 0x00; + io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; + io.in.create_flags = 0x00000000; + io.in.reserved = 0x00000000; + io.in.desired_access = SEC_RIGHTS_FILE_ALL; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | + NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_DELETE; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io.in.fname = FNAME; + + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing alloc size\n"); + io.in.alloc_size = 4096; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(io.out.alloc_size, io.in.alloc_size); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing durable open\n"); + io.in.durable_open = true; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing query maximal access\n"); + io.in.query_maximal_access = true; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing timewarp\n"); + io.in.timewarp = 10000; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND); + io.in.timewarp = 0; + + printf("testing query_on_disk\n"); + io.in.query_on_disk_id = true; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + talloc_free(tmp_ctx); + + smb2_deltree(tree, FNAME); return true; } @@ -192,6 +289,7 @@ bool torture_smb2_create(struct torture_context *torture) } ret &= torture_smb2_create_gentest(torture, tree); + ret &= torture_smb2_create_blob(torture, tree); smb2_deltree(tree, FNAME); -- cgit From 0be9746e1fc47aff93c1d77d256f4fb7942529d6 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:58:34 +1000 Subject: ensure we don't change the incoming blobs in a SMB2 create (This used to be commit a6cc89fffe8c149b540f2125cea57f31331d5460) --- source4/libcli/smb2/create.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index b976b528f1..bff0a1587d 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -59,6 +59,7 @@ NTSTATUS smb2_create_blob_parse(TALLOC_CTX *mem_ctx, const DATA_BLOB buffer, next > remaining || name_offset < 16 || name_offset > remaining || + name_length != 4 || /* windows enforces this */ name_offset + name_length > remaining || data_offset < name_offset + name_length || data_offset > remaining || @@ -190,7 +191,10 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create struct smb2_request *req; NTSTATUS status; DATA_BLOB blob; - struct smb2_create_blobs blobs = io->in.blobs; + struct smb2_create_blobs blobs; + int i; + + ZERO_STRUCT(blobs); req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0); if (req == NULL) return NULL; @@ -309,6 +313,17 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create } /* and any custom blobs */ + for (i=0;iin.blobs.num_blobs;i++) { + status = smb2_create_blob_add(req, &blobs, + io->in.blobs.blobs[i].tag, + io->in.blobs.blobs[i].data); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(req); + return NULL; + } + } + + status = smb2_create_blob_push(req, &blob, blobs); if (!NT_STATUS_IS_OK(status)) { talloc_free(req); -- cgit From 669c528611a032626e5e594d04d8360c2aaa32c2 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:58:57 +1000 Subject: added some of the new SMB2 create tags to gentest_smb2 (This used to be commit b3f638581689946084148b241f9fd7c0b938ade2) --- source4/torture/gentest_smb2.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c index 60bf6264c2..a3a794d3ea 100644 --- a/source4/torture/gentest_smb2.c +++ b/source4/torture/gentest_smb2.c @@ -630,7 +630,7 @@ static time_t gen_timet(void) } /* - generate a unix timestamp + generate a timestamp */ static NTTIME gen_nttime(void) { @@ -639,6 +639,16 @@ static NTTIME gen_nttime(void) return ret; } +/* + generate a timewarp value +*/ +static NTTIME gen_timewarp(void) +{ + NTTIME ret = gen_nttime(); + if (gen_chance(98)) ret = 0; + return ret; +} + /* generate a file allocation size */ @@ -1119,6 +1129,11 @@ static bool handler_create(int instance) parm[0].in.create_options = gen_create_options(); parm[0].in.fname = gen_fname_open(instance); parm[0].in.eas = gen_ea_list(); + parm[0].in.alloc_size = gen_alloc_size(); + parm[0].in.durable_open = gen_bool(); + parm[0].in.query_maximal_access = gen_bool(); + parm[0].in.timewarp = gen_timewarp(); + parm[0].in.query_on_disk_id = gen_bool(); if (!options.use_oplocks) { /* mask out oplocks */ @@ -1145,6 +1160,7 @@ static bool handler_create(int instance) CHECK_EQUAL(out.size); CHECK_ATTRIB(out.file_attr); CHECK_EQUAL(out.reserved2); + CHECK_EQUAL(out.maximal_access); /* ntcreatex creates a new file handle */ ADD_HANDLE(parm[0].in.fname, out.file.handle); -- cgit From 0eb4ecc81a2fa1f37e09b60e750854d75ac425a0 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 16:59:12 +1000 Subject: test unknown tags and bad tag lengths (This used to be commit 72902c1d0f85048adf3088b4f90bbc34858b8658) --- source4/torture/smb2/create.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 070cb65c5c..211b418a12 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -268,6 +268,25 @@ bool torture_smb2_create_blob(struct torture_context *torture, struct smb2_tree status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); + printf("testing unknown tag\n"); + status = smb2_create_blob_add(tmp_ctx, &io.in.blobs, + "FooO", data_blob(NULL, 0)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("testing bad tag length\n"); + status = smb2_create_blob_add(tmp_ctx, &io.in.blobs, + "xxx", data_blob(NULL, 0)); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + talloc_free(tmp_ctx); smb2_deltree(tree, FNAME); -- cgit From 07b4cafb0914545c3839fdba7ddab179656b3074 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 18:47:39 +1000 Subject: added a newline at the end of the IDL (This used to be commit 6386155e99628377046e23342bc640710567afd0) --- source4/librpc/idl/ntp_signd.idl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/librpc/idl/ntp_signd.idl b/source4/librpc/idl/ntp_signd.idl index af3f4337e2..2863401389 100644 --- a/source4/librpc/idl/ntp_signd.idl +++ b/source4/librpc/idl/ntp_signd.idl @@ -33,4 +33,4 @@ interface ntp_signd ); -} \ No newline at end of file +} -- cgit From 91069754f8f60e2ade0f907c4420e01125405919 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 28 May 2008 18:48:23 +1000 Subject: add testing of creating a file with an initial ACL on SMB2 (This used to be commit e957e86a03baa0c0daf3bfe1aaeceb7a08e3c24e) --- source4/torture/smb2/create.c | 121 ++++++++++++++++++++++++++++++++---------- source4/torture/smb2/read.c | 3 +- source4/torture/smb2/smb2.c | 2 +- 3 files changed, 95 insertions(+), 31 deletions(-) diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 211b418a12..733b2c2e8a 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -26,6 +26,7 @@ #include "torture/smb2/proto.h" #include "param/param.h" #include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" #define FNAME "test_create.dat" @@ -46,7 +47,7 @@ /* test some interesting combinations found by gentest */ -bool torture_smb2_create_gentest(struct torture_context *torture, struct smb2_tree *tree) +static bool test_create_gentest(struct torture_context *torture, struct smb2_tree *tree) { struct smb2_create io; NTSTATUS status; @@ -187,7 +188,7 @@ bool torture_smb2_create_gentest(struct torture_context *torture, struct smb2_tr /* try the various request blobs */ -bool torture_smb2_create_blob(struct torture_context *torture, struct smb2_tree *tree) +static bool test_create_blob(struct torture_context *torture, struct smb2_tree *tree) { struct smb2_create io; NTSTATUS status; @@ -203,20 +204,6 @@ bool torture_smb2_create_blob(struct torture_context *torture, struct smb2_tree NTCREATEX_SHARE_ACCESS_DELETE| NTCREATEX_SHARE_ACCESS_READ| NTCREATEX_SHARE_ACCESS_WRITE; - io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | - NTCREATEX_OPTIONS_ASYNC_ALERT | - NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | - 0x00200000; - io.in.security_flags = 0x00; - io.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION; - io.in.create_flags = 0x00000000; - io.in.reserved = 0x00000000; - io.in.desired_access = SEC_RIGHTS_FILE_ALL; - io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; - io.in.share_access = NTCREATEX_SHARE_ACCESS_READ | - NTCREATEX_SHARE_ACCESS_WRITE | - NTCREATEX_SHARE_ACCESS_DELETE; - io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | NTCREATEX_OPTIONS_ASYNC_ALERT | NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | @@ -294,25 +281,101 @@ bool torture_smb2_create_blob(struct torture_context *torture, struct smb2_tree return true; } -/* - basic testing of SMB2 create calls -*/ -bool torture_smb2_create(struct torture_context *torture) +/* + try creating with acls + */ +static bool test_create_acl(struct torture_context *torture, struct smb2_tree *tree) { - TALLOC_CTX *mem_ctx = talloc_new(NULL); - struct smb2_tree *tree; - bool ret = true; + struct smb2_create io; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + struct security_ace ace; + struct security_descriptor *sd, *sd2; + struct dom_sid *test_sid; + union smb_fileinfo q; + + smb2_deltree(tree, FNAME); + + ZERO_STRUCT(io); + io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; + io.in.file_attributes = FILE_ATTRIBUTE_NORMAL; + io.in.create_disposition = NTCREATEX_DISP_OVERWRITE_IF; + io.in.share_access = + NTCREATEX_SHARE_ACCESS_DELETE| + NTCREATEX_SHARE_ACCESS_READ| + NTCREATEX_SHARE_ACCESS_WRITE; + io.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY | + NTCREATEX_OPTIONS_ASYNC_ALERT | + NTCREATEX_OPTIONS_NON_DIRECTORY_FILE | + 0x00200000; + io.in.fname = FNAME; - if (!torture_smb2_connection(torture, &tree)) { + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + q.query_secdesc.level = RAW_FILEINFO_SEC_DESC; + q.query_secdesc.in.file.handle = io.out.file.handle; + q.query_secdesc.in.secinfo_flags = + SECINFO_OWNER | + SECINFO_GROUP | + SECINFO_DACL; + status = smb2_getinfo_file(tree, tmp_ctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd = q.query_secdesc.out.sd; + + status = smb2_util_close(tree, io.out.file.handle); + CHECK_STATUS(status, NT_STATUS_OK); + + smb2_util_unlink(tree, FNAME); + + printf("adding a new ACE\n"); + test_sid = dom_sid_parse_talloc(tmp_ctx, "S-1-5-32-1234-54321"); + + ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED; + ace.flags = 0; + ace.access_mask = SEC_STD_ALL; + ace.trustee = *test_sid; + + status = security_descriptor_dacl_add(sd, &ace); + CHECK_STATUS(status, NT_STATUS_OK); + + printf("creating a file with an initial ACL\n"); + + io.in.sec_desc = sd; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + + q.query_secdesc.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tmp_ctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + sd2 = q.query_secdesc.out.sd; + + if (!security_acl_equal(sd->dacl, sd2->dacl)) { + printf("%s: security descriptors don't match!\n", __location__); + printf("got:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd2); + printf("expected:\n"); + NDR_PRINT_DEBUG(security_descriptor, sd); return false; } - ret &= torture_smb2_create_gentest(torture, tree); - ret &= torture_smb2_create_blob(torture, tree); + talloc_free(tmp_ctx); + + return true; +} - smb2_deltree(tree, FNAME); +/* + basic testing of SMB2 read +*/ +struct torture_suite *torture_smb2_create_init(void) +{ + struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "CREATE"); + + torture_suite_add_1smb2_test(suite, "GENTEST", test_create_gentest); + torture_suite_add_1smb2_test(suite, "BLOB", test_create_blob); + torture_suite_add_1smb2_test(suite, "ACL", test_create_acl); - talloc_free(mem_ctx); + suite->description = talloc_strdup(suite, "SMB2-CREATE tests"); - return ret; + return suite; } diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c index 62240f35ba..b3c13ad667 100644 --- a/source4/torture/smb2/read.c +++ b/source4/torture/smb2/read.c @@ -172,7 +172,8 @@ done: return ret; } -/* basic testing of SMB2 read +/* + basic testing of SMB2 read */ struct torture_suite *torture_smb2_read_init(void) { diff --git a/source4/torture/smb2/smb2.c b/source4/torture/smb2/smb2.c index f813148e1d..120a71cc95 100644 --- a/source4/torture/smb2/smb2.c +++ b/source4/torture/smb2/smb2.c @@ -135,9 +135,9 @@ NTSTATUS torture_smb2_init(void) torture_suite_add_simple_test(suite, "GETINFO", torture_smb2_getinfo); torture_suite_add_simple_test(suite, "SETINFO", torture_smb2_setinfo); torture_suite_add_simple_test(suite, "FIND", torture_smb2_find); - torture_suite_add_simple_test(suite, "CREATE", torture_smb2_create); torture_suite_add_suite(suite, torture_smb2_lock_init()); torture_suite_add_suite(suite, torture_smb2_read_init()); + torture_suite_add_suite(suite, torture_smb2_create_init()); torture_suite_add_simple_test(suite, "NOTIFY", torture_smb2_notify); torture_suite_add_2smb2_test(suite, "PERSISTENT-HANDLES1", torture_smb2_persistent_handles1); torture_suite_add_1smb2_test(suite, "OPLOCK-BATCH1", torture_smb2_oplock_batch1); -- cgit