diff options
author | Jelmer Vernooij <jelmer@samba.org> | 2008-05-30 12:18:07 +0200 |
---|---|---|
committer | Jelmer Vernooij <jelmer@samba.org> | 2008-05-30 12:18:07 +0200 |
commit | 5107f093c270f7a0dfc359de088b475fffc4e279 (patch) | |
tree | 99f616c6e352f87fbd41e60ad7fae3adf67e72fa | |
parent | 46f22add9ae03b95f4b22235c297737475427f41 (diff) | |
parent | be1927cd80838a6807827cef4431c03160d52582 (diff) | |
download | samba-5107f093c270f7a0dfc359de088b475fffc4e279.tar.gz samba-5107f093c270f7a0dfc359de088b475fffc4e279.tar.bz2 samba-5107f093c270f7a0dfc359de088b475fffc4e279.zip |
Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-defs
Conflicts:
source/samba4-skip
(This used to be commit 7b0e0eb346c2f6a240b20fbcf14029539c6512b9)
44 files changed, 2028 insertions, 2686 deletions
diff --git a/source4/build/smb_build/makefile.pm b/source4/build/smb_build/makefile.pm index 73801c25fd..0ea31062f7 100644 --- a/source4/build/smb_build/makefile.pm +++ b/source4/build/smb_build/makefile.pm @@ -208,9 +208,9 @@ sub Binary($$) $self->_prepare_list($ctx, "LINK_FLAGS"); if (defined($ctx->{USE_HOSTCC}) && $ctx->{USE_HOSTCC} eq "YES") { -$self->output("\$(call host_binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST), \$($ctx->{NAME}_LINK_FLAGS))\n"); +$self->output("\$(call host_binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_FULL_OBJ_LIST) \$($ctx->{NAME}_DEPEND_LIST), \$($ctx->{NAME}_LINK_FLAGS))\n"); } else { -$self->output("\$(call binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_DEPEND_LIST) \$($ctx->{NAME}_FULL_OBJ_LIST), \$($ctx->{NAME}_LINK_FLAGS))\n"); +$self->output("\$(call binary_link_template, $ctx->{RESULT_BINARY}, \$($ctx->{NAME}_FULL_OBJ_LIST) \$($ctx->{NAME}_DEPEND_LIST), \$($ctx->{NAME}_LINK_FLAGS))\n"); } } diff --git a/source4/lib/messaging/pymessaging.c b/source4/lib/messaging/pymessaging.c index 1c22fb431a..41c9c82b1f 100644 --- a/source4/lib/messaging/pymessaging.c +++ b/source4/lib/messaging/pymessaging.c @@ -127,12 +127,15 @@ static PyObject *py_messaging_send(PyObject *self, PyObject *args, PyObject *kwa NTSTATUS status; struct server_id server; const char *kwnames[] = { "target", "msg_type", "data", NULL }; + int length; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "Ois#|:send", - discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &data.length)) { + discard_const_p(char *, kwnames), &target, &msg_type, &data.data, &length)) { return NULL; } + data.length = length; + if (!server_id_from_py(target, &server)) return NULL; @@ -159,11 +162,11 @@ static void py_msg_callback_wrapper(struct messaging_context *msg, void *private static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject *kwargs) { messaging_Object *iface = (messaging_Object *)self; - uint32_t msg_type = -1; + int msg_type = -1; PyObject *callback; NTSTATUS status; const char *kwnames[] = { "callback", "msg_type", NULL }; - + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|i:send", discard_const_p(char *, kwnames), &callback, &msg_type)) { return NULL; @@ -172,8 +175,10 @@ static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject Py_INCREF(callback); if (msg_type == -1) { + uint32_t msg_type32 = msg_type; status = messaging_register_tmp(iface->msg_ctx, callback, - py_msg_callback_wrapper, &msg_type); + py_msg_callback_wrapper, &msg_type32); + msg_type = msg_type32; } else { status = messaging_register(iface->msg_ctx, callback, msg_type, py_msg_callback_wrapper); @@ -189,7 +194,7 @@ static PyObject *py_messaging_register(PyObject *self, PyObject *args, PyObject static PyObject *py_messaging_deregister(PyObject *self, PyObject *args, PyObject *kwargs) { messaging_Object *iface = (messaging_Object *)self; - uint32_t msg_type = -1; + int msg_type = -1; PyObject *callback; const char *kwnames[] = { "callback", "msg_type", NULL }; diff --git a/source4/libcli/raw/interfaces.h b/source4/libcli/raw/interfaces.h index d170006d3b..19d51893a6 100644 --- a/source4/libcli/raw/interfaces.h +++ b/source4/libcli/raw/interfaces.h @@ -1354,7 +1354,7 @@ union smb_open { break; \ } \ } while (0) - /* SMBNTCreateX interface */ + /* SMBNTCreateX, nttrans and generic interface */ struct { enum smb_open_level level; struct { @@ -1377,6 +1377,9 @@ union smb_open { NTTRANS varient of the call */ struct security_descriptor *sec_desc; struct smb_ea_list *ea_list; + + /* some optional parameters from the SMB2 varient */ + bool query_maximal_access; } in; struct { union smb_handle file; @@ -1392,6 +1395,10 @@ union smb_open { uint16_t file_type; uint16_t ipc_state; uint8_t is_directory; + + /* optional return values matching SMB2 tagged + values in the call */ + uint32_t maximal_access; } out; } ntcreatex, nttrans, generic; diff --git a/source4/libcli/smb2/cancel.c b/source4/libcli/smb2/cancel.c index 80127feea5..65f02187c1 100644 --- a/source4/libcli/smb2/cancel.c +++ b/source4/libcli/smb2/cancel.c @@ -61,10 +61,10 @@ NTSTATUS smb2_cancel(struct smb2_request *r) SSVAL(c->out.body, 0x02, 0); - old_timeout = c->transport->options.timeout; - c->transport->options.timeout = 0; + old_timeout = c->transport->options.request_timeout; + c->transport->options.request_timeout = 0; smb2_transport_send(c); - c->transport->options.timeout = old_timeout; + c->transport->options.request_timeout = old_timeout; if (c->state == SMB2_REQUEST_ERROR) { status = c->status; diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk index 00b6305def..322bca1416 100644 --- a/source4/libcli/smb2/config.mk +++ b/source4/libcli/smb2/config.mk @@ -5,6 +5,6 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix $(libclisrcdir)/smb2/, \ transport.o request.o negprot.o session.o tcon.o \ create.o close.o connect.o getinfo.o write.o read.o \ setinfo.o find.o ioctl.o logoff.o tdis.o flush.o \ - lock.o notify.o cancel.o keepalive.o break.o util.o) + lock.o notify.o cancel.o keepalive.o break.o util.o signing.o) $(eval $(call proto_header_template,$(libclisrcdir)/smb2/smb2_proto.h,$(LIBCLI_SMB2_OBJ_FILES:.o=.c))) diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c index eabfa410ad..cdb5e3b5d4 100644 --- a/source4/libcli/smb2/connect.c +++ b/source4/libcli/smb2/connect.c @@ -33,6 +33,7 @@ struct smb2_connect_state { struct resolve_context *resolve_ctx; const char *host; const char *share; + struct smbcli_options options; struct smb2_negprot negprot; struct smb2_tree_connect tcon; struct smb2_session *session; @@ -103,6 +104,34 @@ static void continue_negprot(struct smb2_request *req) transport->negotiate.system_time = state->negprot.out.system_time; transport->negotiate.server_start_time = state->negprot.out.server_start_time; + transport->negotiate.security_mode = state->negprot.out.security_mode; + + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + transport->signing.doing_signing = false; + break; + case SMB_SIGNING_SUPPORTED: + case SMB_SIGNING_AUTO: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) { + transport->signing.doing_signing = true; + } else { + transport->signing.doing_signing = false; + } + break; + case SMB_SIGNING_REQUIRED: + if (transport->negotiate.security_mode & SMB2_NEGOTIATE_SIGNING_ENABLED) { + transport->signing.doing_signing = true; + } else { + composite_error(c, NT_STATUS_ACCESS_DENIED); + return; + } + break; + } + state->session = smb2_session_init(transport, global_loadparm, state, true); if (composite_nomem(state->session, c)) return; @@ -129,12 +158,24 @@ static void continue_socket(struct composite_context *creq) c->status = smbcli_sock_connect_recv(creq, state, &sock); if (!composite_is_ok(c)) return; - transport = smb2_transport_init(sock, state); + transport = smb2_transport_init(sock, state, &state->options); if (composite_nomem(transport, c)) return; ZERO_STRUCT(state->negprot); state->negprot.in.dialect_count = 2; - state->negprot.in.security_mode = 0; + switch (transport->options.signing) { + case SMB_SIGNING_OFF: + state->negprot.in.security_mode = 0; + break; + case SMB_SIGNING_SUPPORTED: + case SMB_SIGNING_AUTO: + state->negprot.in.security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; + break; + case SMB_SIGNING_REQUIRED: + state->negprot.in.security_mode = + SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; + break; + } state->negprot.in.capabilities = 0; unix_to_nt_time(&state->negprot.in.start_time, time(NULL)); dialects[0] = 0; @@ -178,7 +219,8 @@ struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx, const char *share, struct resolve_context *resolve_ctx, struct cli_credentials *credentials, - struct event_context *ev) + struct event_context *ev, + struct smbcli_options *options) { struct composite_context *c; struct smb2_connect_state *state; @@ -193,6 +235,7 @@ struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx, c->private_data = state; state->credentials = credentials; + state->options = *options; state->host = talloc_strdup(c, host); if (composite_nomem(state->host, c)) return c; state->share = talloc_strdup(c, share); @@ -232,10 +275,11 @@ NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx, struct resolve_context *resolve_ctx, struct cli_credentials *credentials, struct smb2_tree **tree, - struct event_context *ev) + struct event_context *ev, + struct smbcli_options *options) { struct composite_context *c = smb2_connect_send(mem_ctx, host, share, resolve_ctx, - credentials, ev); + credentials, ev, options); return smb2_connect_recv(c, mem_ctx, tree); } diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c index bff0a1587d..8a40e56a00 100644 --- a/source4/libcli/smb2/create.c +++ b/source4/libcli/smb2/create.c @@ -387,12 +387,13 @@ NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct /* pull out the parsed blobs */ for (i=0;i<io->out.blobs.num_blobs;i++) { if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_MXAC) == 0) { - /* why 8 bytes not 4?? */ + /* TODO: this also contains a status field in + first 4 bytes */ 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); + io->out.maximal_access = IVAL(io->out.blobs.blobs[i].data.data, 4); } if (strcmp(io->out.blobs.blobs[i].tag, SMB2_CREATE_TAG_QFID) == 0) { if (io->out.blobs.blobs[i].data.length != 32) { diff --git a/source4/libcli/smb2/notify.c b/source4/libcli/smb2/notify.c index 096d790a31..ef7341cae8 100644 --- a/source4/libcli/smb2/notify.c +++ b/source4/libcli/smb2/notify.c @@ -44,10 +44,10 @@ struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify SIVAL(req->out.body, 0x18, io->in.completion_filter); SIVAL(req->out.body, 0x1C, io->in.unknown); - old_timeout = req->transport->options.timeout; - req->transport->options.timeout = 0; + old_timeout = req->transport->options.request_timeout; + req->transport->options.request_timeout = 0; smb2_transport_send(req); - req->transport->options.timeout = old_timeout; + req->transport->options.request_timeout = old_timeout; return req; } diff --git a/source4/libcli/smb2/session.c b/source4/libcli/smb2/session.c index 29af6652f2..54915d8535 100644 --- a/source4/libcli/smb2/session.c +++ b/source4/libcli/smb2/session.c @@ -164,8 +164,8 @@ static void session_request_handler(struct smb2_request *req) session_key_err = gensec_session_key(session->gensec, &session_key); if (NT_STATUS_IS_OK(session_key_err)) { - session->session_key = session_key; - } + session->transport->signing.session_key = session_key; + } } session->uid = state->io.out.uid; @@ -187,6 +187,14 @@ static void session_request_handler(struct smb2_request *req) return; } + if (session->transport->signing.doing_signing) { + c->status = smb2_start_signing(session->transport); + if (!NT_STATUS_IS_OK(c->status)) { + composite_error(c, c->status); + return; + } + } + composite_done(c); } @@ -208,7 +216,10 @@ struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *se ZERO_STRUCT(state->io); state->io.in.vc_number = 0; - state->io.in.security_mode = 0; + if (session->transport->signing.doing_signing) { + state->io.in.security_mode = + SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED; + } state->io.in.capabilities = 0; state->io.in.channel = 0; state->io.in.previous_sessionid = 0; diff --git a/source4/libcli/smb2/signing.c b/source4/libcli/smb2/signing.c new file mode 100644 index 0000000000..01f7576134 --- /dev/null +++ b/source4/libcli/smb2/signing.c @@ -0,0 +1,165 @@ +/* + Unix SMB/CIFS implementation. + + SMB2 Signing Code + + Copyright (C) Andrew Tridgell <tridge@samba.org> 2008 + + 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/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" +#include "heimdal/lib/hcrypto/sha.h" + +/* + NOTE: this code does not yet interoperate with the windows SMB2 + implementation. We are waiting on feedback on the docs to find out + why + */ + + +/* + setup signing on a transport + */ +NTSTATUS smb2_start_signing(struct smb2_transport *transport) +{ + if (transport->signing.session_key.length != 16) { + DEBUG(2,("Wrong session key length %u for SMB2 signing\n", + (unsigned)transport->signing.session_key.length)); + return NT_STATUS_ACCESS_DENIED; + } + + transport->signing.signing_started = true; + return NT_STATUS_OK; +} + +/* + sign an outgoing message + */ +NTSTATUS smb2_sign_message(struct smb2_request *req) +{ + struct smb2_request_buffer *buf = &req->out; + uint64_t session_id; + SHA256_CTX m; + uint8_t res[32]; + + if (!req->transport->signing.doing_signing || + !req->transport->signing.signing_started) { + return NT_STATUS_OK; + } + + if (buf->size < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) { + /* can't sign non-SMB2 messages */ + return NT_STATUS_OK; + } + + session_id = BVAL(buf->hdr, SMB2_HDR_SESSION_ID); + if (session_id == 0) { + /* we don't sign messages with a zero session_id. See + MS-SMB2 3.2.4.1.1 */ + return NT_STATUS_OK; + } + + if (req->transport->signing.session_key.length != 16) { + DEBUG(2,("Wrong session key length %u for SMB2 signing\n", + (unsigned)req->transport->signing.session_key.length)); + return NT_STATUS_ACCESS_DENIED; + } + + memset(buf->hdr + SMB2_HDR_SIGNATURE, 0, 16); + + SIVAL(buf->hdr, SMB2_HDR_FLAGS, IVAL(buf->hdr, SMB2_HDR_FLAGS) | SMB2_HDR_FLAG_SIGNED); + + ZERO_STRUCT(m); + SHA256_Init(&m); + SHA256_Update(&m, req->transport->signing.session_key.data, + req->transport->signing.session_key.length); + SHA256_Update(&m, buf->buffer+NBT_HDR_SIZE, buf->size-NBT_HDR_SIZE); + SHA256_Final(res, &m); + + DEBUG(5,("signed SMB2 message of size %u\n", (unsigned)buf->size - NBT_HDR_SIZE)); + + memcpy(buf->hdr + SMB2_HDR_SIGNATURE, res, 16); + + if (DEBUGLVL(5)) { + /* check our own signature */ + smb2_check_signature(req->transport, buf->buffer, buf->size); + } + + return NT_STATUS_OK; +} + +/* + check an incoming signature + */ +NTSTATUS smb2_check_signature(struct smb2_transport *transport, + uint8_t *buffer, uint_t length) +{ + uint64_t session_id; + SHA256_CTX m; + uint8_t res[SHA256_DIGEST_LENGTH]; + uint8_t sig[16]; + + if (!transport->signing.signing_started || + !transport->signing.doing_signing) { + return NT_STATUS_OK; + } + + if (length < NBT_HDR_SIZE + SMB2_HDR_SIGNATURE + 16) { + /* can't check non-SMB2 messages */ + return NT_STATUS_OK; + } + + session_id = BVAL(buffer+NBT_HDR_SIZE, SMB2_HDR_SESSION_ID); + if (session_id == 0) { + /* don't sign messages with a zero session_id. See + MS-SMB2 3.2.4.1.1 */ + return NT_STATUS_OK; + } + + if (transport->signing.session_key.length == 0) { + /* we don't have the session key yet */ + return NT_STATUS_OK; + } + + if (transport->signing.session_key.length != 16) { + DEBUG(2,("Wrong session key length %u for SMB2 signing\n", + (unsigned)transport->signing.session_key.length)); + return NT_STATUS_ACCESS_DENIED; + } + + memcpy(sig, buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, 16); + + memset(buffer + NBT_HDR_SIZE + SMB2_HDR_SIGNATURE, 0, 16); + + ZERO_STRUCT(m); + SHA256_Init(&m); + SHA256_Update(&m, transport->signing.session_key.data, 16); + SHA256_Update(&m, buffer+NBT_HDR_SIZE, length-NBT_HDR_SIZE); + SHA256_Final(res, &m); + + memcpy(buffer+NBT_HDR_SIZE+SMB2_HDR_SIGNATURE, sig, 16); + + if (memcmp(res, sig, 16) != 0) { + DEBUG(0,("Bad SMB2 signature for message of size %u\n", length)); + dump_data(0, sig, 16); + dump_data(0, res, 16); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h index b55da05e21..0903509528 100644 --- a/source4/libcli/smb2/smb2.h +++ b/source4/libcli/smb2/smb2.h @@ -23,20 +23,24 @@ #define __LIBCLI_SMB2_SMB2_H__ #include "libcli/raw/request.h" +#include "libcli/raw/libcliraw.h" struct smb2_handle; -struct smb2_options { - uint32_t timeout; +struct smb2_signing_context { + bool doing_signing; + bool signing_started; + DATA_BLOB session_key; }; /* - information returned from the negotiate response + information returned from the negotiate process */ struct smb2_negotiate { DATA_BLOB secblob; NTTIME system_time; NTTIME server_start_time; + uint16_t security_mode; }; /* this is the context for the smb2 transport layer */ @@ -44,7 +48,6 @@ struct smb2_transport { /* socket level info */ struct smbcli_socket *socket; - struct smb2_options options; struct smb2_negotiate negotiate; /* next seqnum to allocate */ @@ -74,6 +77,9 @@ struct smb2_transport { /* private data passed to the oplock handler */ void *private_data; } oplock; + + struct smbcli_options options; + struct smb2_signing_context signing; }; @@ -92,7 +98,6 @@ struct smb2_session { struct smb2_transport *transport; struct gensec_security *gensec; uint64_t uid; - DATA_BLOB session_key; }; @@ -193,6 +198,13 @@ struct smb2_request { #define SMB2_HDR_SIGNATURE 0x30 /* 16 bytes */ #define SMB2_HDR_BODY 0x40 +/* header flags */ +#define SMB2_HDR_FLAG_REDIRECT 0x01 +#define SMB2_HDR_FLAG_ASYNC 0x02 +#define SMB2_HDR_FLAG_CHAINED 0x04 +#define SMB2_HDR_FLAG_SIGNED 0x08 +#define SMB2_HDR_FLAG_DFS 0x10000000 + /* SMB2 opcodes */ #define SMB2_OP_NEGPROT 0x00 #define SMB2_OP_SESSSETUP 0x01 diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c index 8eb60a06f1..561b6e528e 100644 --- a/source4/libcli/smb2/transport.c +++ b/source4/libcli/smb2/transport.c @@ -74,7 +74,8 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob); create a transport structure based on an established socket */ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, - TALLOC_CTX *parent_ctx) + TALLOC_CTX *parent_ctx, + struct smbcli_options *options) { struct smb2_transport *transport; @@ -82,6 +83,7 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, if (!transport) return NULL; transport->socket = talloc_steal(transport, sock); + transport->options = *options; /* setup the stream -> packet parser */ transport->packet = packet_init(transport); @@ -112,8 +114,6 @@ struct smb2_transport *smb2_transport_init(struct smbcli_socket *sock, talloc_set_destructor(transport, transport_destructor); - transport->options.timeout = 30; - return transport; } @@ -140,27 +140,24 @@ void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status) } } -static bool smb2_handle_oplock_break(struct smb2_transport *transport, - const DATA_BLOB *blob) +static NTSTATUS smb2_handle_oplock_break(struct smb2_transport *transport, + const DATA_BLOB *blob) { uint8_t *hdr; uint16_t opcode; - uint64_t seqnum; hdr = blob->data+NBT_HDR_SIZE; if (blob->length < (SMB2_MIN_SIZE+0x18)) { DEBUG(1,("Discarding smb2 oplock reply of size %u\n", - blob->length)); - return false; + (unsigned)blob->length)); + return NT_STATUS_INVALID_NETWORK_RESPONSE; } opcode = SVAL(hdr, SMB2_HDR_OPCODE); - seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); - if ((opcode != SMB2_OP_BREAK) || - (seqnum != UINT64_MAX)) { - return false; + if (opcode != SMB2_OP_BREAK) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; } if (transport->oplock.handler) { @@ -173,9 +170,11 @@ static bool smb2_handle_oplock_break(struct smb2_transport *transport, transport->oplock.handler(transport, &h, level, transport->oplock.private_data); + } else { + DEBUG(5,("Got SMB2 oplock break with no handler\n")); } - return true; + return NT_STATUS_OK; } /* @@ -194,6 +193,7 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob) uint16_t buffer_code; uint32_t dynamic_size; uint32_t i; + NTSTATUS status; buffer = blob.data; len = blob.length; @@ -205,14 +205,20 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob) goto error; } - if (smb2_handle_oplock_break(transport, &blob)) { + status = smb2_check_signature(transport, buffer, len); + if (!NT_STATUS_IS_OK(status)) { talloc_free(buffer); - return NT_STATUS_OK; + return status; } - + flags = IVAL(hdr, SMB2_HDR_FLAGS); seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID); + /* see MS-SMB2 3.2.5.19 */ + if (seqnum == UINT64_MAX) { + return smb2_handle_oplock_break(transport, &blob); + } + /* match the incoming request against the list of pending requests */ for (req=transport->pending_recv; req; req=req->next) { if (req->seqnum == seqnum) break; @@ -340,6 +346,13 @@ void smb2_transport_send(struct smb2_request *req) return; } + status = smb2_sign_message(req); + if (!NT_STATUS_IS_OK(status)) { + req->state = SMB2_REQUEST_ERROR; + req->status = status; + return; + } + blob = data_blob_const(req->out.buffer, req->out.size); status = packet_send(req->transport->packet, blob); if (!NT_STATUS_IS_OK(status)) { @@ -352,9 +365,9 @@ void smb2_transport_send(struct smb2_request *req) DLIST_ADD(req->transport->pending_recv, req); /* add a timeout */ - if (req->transport->options.timeout) { + if (req->transport->options.request_timeout) { event_add_timed(req->transport->socket->event.ctx, req, - timeval_current_ofs(req->transport->options.timeout, 0), + timeval_current_ofs(req->transport->options.request_timeout, 0), smb2_timeout_handler, req); } diff --git a/source4/librpc/rpc/dcerpc_connect.c b/source4/librpc/rpc/dcerpc_connect.c index a22cad9a4a..318b8fe36d 100644 --- a/source4/librpc/rpc/dcerpc_connect.c +++ b/source4/librpc/rpc/dcerpc_connect.c @@ -218,6 +218,7 @@ static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb2_send( struct composite_context *c; struct pipe_np_smb2_state *s; struct composite_context *conn_req; + struct smbcli_options options; /* composite context allocation and setup */ c = composite_create(mem_ctx, io->pipe->conn->event_ctx); @@ -240,11 +241,14 @@ static struct composite_context *dcerpc_pipe_connect_ncacn_np_smb2_send( cli_credentials_guess(s->io.creds, lp_ctx); } + lp_smbcli_options(lp_ctx, &options); + /* send smb2 connect request */ conn_req = smb2_connect_send(mem_ctx, s->io.binding->host, "IPC$", s->io.resolve_ctx, s->io.creds, - c->event_ctx); + c->event_ctx, + &options); composite_continue(c, conn_req, continue_smb2_connect, c); return c; } diff --git a/source4/librpc/rpc/dcerpc_smb2.c b/source4/librpc/rpc/dcerpc_smb2.c index 4767165fba..211015a4cf 100644 --- a/source4/librpc/rpc/dcerpc_smb2.c +++ b/source4/librpc/rpc/dcerpc_smb2.c @@ -376,7 +376,7 @@ static NTSTATUS smb2_session_key(struct dcerpc_connection *c, DATA_BLOB *session { struct smb2_private *smb = talloc_get_type(c->transport.private_data, struct smb2_private); - *session_key = smb->tree->session->session_key; + *session_key = smb->tree->session->transport->signing.session_key; if (session_key->data == NULL) { return NT_STATUS_NO_USER_SESSION_KEY; } diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c index 9227295696..d705758475 100644 --- a/source4/ntvfs/ntvfs_generic.c +++ b/source4/ntvfs/ntvfs_generic.c @@ -233,6 +233,7 @@ 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.maximal_access = io2->generic.out.maximal_access; break; default: @@ -522,6 +523,7 @@ NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs, io2->generic.in.fname = io->smb2.in.fname; io2->generic.in.sec_desc = io->smb2.in.sec_desc; io2->generic.in.ea_list = &io->smb2.in.eas; + io2->generic.in.query_maximal_access = io->smb2.in.query_maximal_access; /* we don't support timewarp yet */ if (io->smb2.in.timewarp != 0) { diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c index 507c22f050..9a9200e4f0 100644 --- a/source4/ntvfs/posix/pvfs_acl.c +++ b/source4/ntvfs/posix/pvfs_acl.c @@ -464,7 +464,11 @@ NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, return NT_STATUS_ACCESS_DENIED; } - *access_mask |= SEC_FILE_READ_ATTRIBUTE; + if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) { + /* on SMB, this bit is always granted, even if not + asked for */ + *access_mask |= SEC_FILE_READ_ATTRIBUTE; + } return NT_STATUS_OK; } @@ -496,7 +500,9 @@ NTSTATUS pvfs_access_check(struct pvfs_state *pvfs, /* expand the generic access bits to file specific bits */ *access_mask = pvfs_translate_mask(*access_mask); - *access_mask &= ~SEC_FILE_READ_ATTRIBUTE; + if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) { + *access_mask &= ~SEC_FILE_READ_ATTRIBUTE; + } status = pvfs_acl_load(pvfs, name, -1, acl); if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { @@ -518,8 +524,11 @@ NTSTATUS pvfs_access_check(struct pvfs_state *pvfs, /* check the acl against the required access mask */ status = sec_access_check(sd, token, *access_mask, access_mask); - /* this bit is always granted, even if not asked for */ - *access_mask |= SEC_FILE_READ_ATTRIBUTE; + if (pvfs->ntvfs->ctx->protocol != PROTOCOL_SMB2) { + /* on SMB, this bit is always granted, even if not + asked for */ + *access_mask |= SEC_FILE_READ_ATTRIBUTE; + } talloc_free(acl); @@ -800,3 +809,15 @@ NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, return status; } + +/* + return the maximum allowed access mask +*/ +NTSTATUS pvfs_access_maximal_allowed(struct pvfs_state *pvfs, + struct ntvfs_request *req, + struct pvfs_filename *name, + uint32_t *maximal_access) +{ + *maximal_access = SEC_FLAG_MAXIMUM_ALLOWED; + return pvfs_access_check(pvfs, req, name, maximal_access); +} diff --git a/source4/ntvfs/posix/pvfs_ioctl.c b/source4/ntvfs/posix/pvfs_ioctl.c index d0360e67ed..92d3eae061 100644 --- a/source4/ntvfs/posix/pvfs_ioctl.c +++ b/source4/ntvfs/posix/pvfs_ioctl.c @@ -73,7 +73,8 @@ NTSTATUS pvfs_ioctl(struct ntvfs_module_context *ntvfs, case RAW_IOCTL_SMB2: case RAW_IOCTL_SMB2_NO_HANDLE: - return NT_STATUS_FS_DRIVER_REQUIRED; + /* see WSPP SMB2 test 46 */ + return NT_STATUS_INVALID_DEVICE_REQUEST; } return NT_STATUS_INVALID_LEVEL; diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index baa92880f1..822b28246a 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -68,13 +68,8 @@ static void pvfs_lock_async_failed(struct pvfs_state *pvfs, int i, NTSTATUS status) { - /* in SMB2 mode we also try to unlock failing lock */ - if (req->ctx->protocol != PROTOCOL_SMB2) { - i--; - } - /* undo the locks we just did */ - for (;i>=0;i--) { + for (i--;i>=0;i--) { brl_unlock(pvfs->brl_context, f->brl_handle, locks[i].pid, @@ -390,12 +385,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, DLIST_ADD(f->pending_list, pending); return NT_STATUS_OK; } - /* in SMB2 mode we also try to unlock failing lock */ - if (req->ctx->protocol != PROTOCOL_SMB2) { - i--; - } + /* undo the locks we just did */ - for (;i>=0;i--) { + for (i--;i>=0;i--) { brl_unlock(pvfs->brl_context, f->brl_handle, locks[i].pid, diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 0f08136a79..dada9f503f 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -252,8 +252,12 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, } else { status = pvfs_access_check_create(pvfs, req, name, &access_mask); } - if (!NT_STATUS_IS_OK(status)) { - return status; + NT_STATUS_NOT_OK_RETURN(status); + + if (io->generic.in.query_maximal_access) { + status = pvfs_access_maximal_allowed(pvfs, req, name, + &io->generic.out.maximal_access); + NT_STATUS_NOT_OK_RETURN(status); } f->ntvfs = h; @@ -578,6 +582,12 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, status = pvfs_access_check_create(pvfs, req, name, &access_mask); NT_STATUS_NOT_OK_RETURN(status); + if (io->generic.in.query_maximal_access) { + status = pvfs_access_maximal_allowed(pvfs, req, name, + &io->generic.out.maximal_access); + NT_STATUS_NOT_OK_RETURN(status); + } + /* check that the parent isn't opened with delete on close set */ status = pvfs_resolve_parent(pvfs, req, name, &parent); if (NT_STATUS_IS_OK(status)) { @@ -1122,6 +1132,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, uint32_t create_options; uint32_t share_access; uint32_t access_mask; + uint32_t create_action = NTCREATEX_ACTION_EXISTED; bool del_on_close; bool stream_existed, stream_truncate=false; uint32_t oplock_level = OPLOCK_NONE, oplock_granted; @@ -1134,6 +1145,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return ntvfs_map_open(ntvfs, req, io); } + ZERO_STRUCT(io->generic.out); + create_options = io->generic.in.create_options; share_access = io->generic.in.share_access; access_mask = io->generic.in.access_mask; @@ -1169,6 +1182,13 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_PARAMETER; } + /* we ignore some file_attr bits */ + io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED | + FILE_ATTRIBUTE_COMPRESSED | + FILE_ATTRIBUTE_REPARSE_POINT | + FILE_ATTRIBUTE_SPARSE | + FILE_ATTRIBUTE_NORMAL); + /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, PVFS_RESOLVE_STREAMS, &name); @@ -1210,6 +1230,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, } else { stream_truncate = true; } + create_action = NTCREATEX_ACTION_TRUNCATED; break; case NTCREATEX_DISP_OPEN: @@ -1228,6 +1249,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, } else { stream_truncate = true; } + create_action = NTCREATEX_ACTION_TRUNCATED; break; case NTCREATEX_DISP_CREATE: @@ -1272,8 +1294,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, /* check the security descriptor */ status = pvfs_access_check(pvfs, req, name, &access_mask); - if (!NT_STATUS_IS_OK(status)) { - return status; + NT_STATUS_NOT_OK_RETURN(status); + + if (io->generic.in.query_maximal_access) { + status = pvfs_access_maximal_allowed(pvfs, req, name, + &io->generic.out.maximal_access); + NT_STATUS_NOT_OK_RETURN(status); } status = ntvfs_handle_new(pvfs->ntvfs, req, &h); @@ -1487,7 +1513,8 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, io->generic.out.oplock_level = oplock_granted; io->generic.out.file.ntvfs = h; io->generic.out.create_action = stream_existed? - NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED; + create_action:NTCREATEX_ACTION_CREATED; + io->generic.out.create_time = name->dos.create_time; io->generic.out.access_time = name->dos.access_time; io->generic.out.write_time = name->dos.write_time; diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index c663466985..3196cf2f8d 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -41,6 +41,10 @@ static uint32_t pvfs_fileinfo_access(union smb_fileinfo *info) needed = 0; break; + case RAW_FILEINFO_ACCESS_INFORMATION: + needed = 0; + break; + case RAW_FILEINFO_SEC_DESC: needed = 0; if (info->query_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) { @@ -216,6 +220,10 @@ static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs, case RAW_FILEINFO_NAME_INFO: case RAW_FILEINFO_NAME_INFORMATION: + if (req->ctx->protocol == PROTOCOL_SMB2) { + /* strange that SMB2 doesn't have this */ + return NT_STATUS_NOT_SUPPORTED; + } info->name_info.out.fname.s = name->original_name; return NT_STATUS_OK; diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 0beca75ead..1dd2c075d9 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -457,7 +457,12 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, /* possibly change the attribute */ if (newstats.dos.attrib != h->name->dos.attrib) { - mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib); + mode_t mode; + if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) && + !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { + return NT_STATUS_INVALID_PARAMETER; + } + mode = pvfs_fileperms(pvfs, newstats.dos.attrib); if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) { if (fchmod(h->fd, mode) == -1) { return pvfs_map_errno(pvfs, errno); diff --git a/source4/ntvfs/smb2/vfs_smb2.c b/source4/ntvfs/smb2/vfs_smb2.c index cc09daf83f..68b475a084 100644 --- a/source4/ntvfs/smb2/vfs_smb2.c +++ b/source4/ntvfs/smb2/vfs_smb2.c @@ -162,9 +162,9 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, struct composite_context *creq; struct share_config *scfg = ntvfs->ctx->config; struct smb2_tree *tree; - struct cli_credentials *credentials; bool machine_account; + struct smbcli_options options; /* Here we need to determine which server to connect to. * For now we use parametric options, type cifs. @@ -224,10 +224,12 @@ static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs, return NT_STATUS_INVALID_PARAMETER; } + lp_smbcli_options(ntvfs->ctx->lp_ctx, &options); + creq = smb2_connect_send(private, host, remote_share, lp_resolve_context(ntvfs->ctx->lp_ctx), credentials, - ntvfs->ctx->event_ctx); + ntvfs->ctx->event_ctx, &options); status = smb2_connect_recv(creq, private, &tree); NT_STATUS_NOT_OK_RETURN(status); diff --git a/source4/samba4-skip b/source4/samba4-skip index dca5fc3a5d..ed547ba72d 100644 --- a/source4/samba4-skip +++ b/source4/samba4-skip @@ -61,3 +61,4 @@ nss.test # Fails samba4.samba3sam.python # Conversion from EJS not yet finished raw.offline # Samba 4 doesn't have much offline support yet winreg* #Does not authenticate against the target server +^samba4.samdb.python #Not working yet diff --git a/source4/scripting/python/samba/provision.py b/source4/scripting/python/samba/provision.py index 71c1ac3187..b7112e16c3 100644 --- a/source4/scripting/python/samba/provision.py +++ b/source4/scripting/python/samba/provision.py @@ -930,8 +930,9 @@ def provision(setup_dir, message, session_info, if aci is None: aci = "# no aci for local ldb" - if smbconf is None: - os.makedirs(os.path.join(targetdir, "etc")) + if targetdir is not None: + if (not os.path.exists(os.path.join(targetdir, "etc"))): + os.makedirs(os.path.join(targetdir, "etc")) smbconf = os.path.join(targetdir, "etc", "smb.conf") # only install a new smb.conf if there isn't one there already diff --git a/source4/scripting/python/samba/tests/dcerpc/bare.py b/source4/scripting/python/samba/tests/dcerpc/bare.py index d75ffc381e..fae699a249 100644 --- a/source4/scripting/python/samba/tests/dcerpc/bare.py +++ b/source4/scripting/python/samba/tests/dcerpc/bare.py @@ -20,27 +20,28 @@ from samba.dcerpc import ClientConnection from unittest import TestCase +from samba.tests import cmdline_loadparm class BareTestCase(TestCase): def test_bare(self): # Connect to the echo pipe x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("60a15ec5-4de8-11d7-a637-005056a20182", 1)) + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm) self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4)) def test_alter_context(self): x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("12345778-1234-abcd-ef00-0123456789ac", 1)) + ("12345778-1234-abcd-ef00-0123456789ac", 1), lp_ctx=cmdline_loadparm) y = ClientConnection("ncalrpc:localhost", ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - basis_connection=x) + basis_connection=x, lp_ctx=cmdline_loadparm) x.alter_context(("60a15ec5-4de8-11d7-a637-005056a20182", 1)) # FIXME: self.assertEquals("\x01\x00\x00\x00", x.request(0, chr(0) * 4)) def test_two_connections(self): x = ClientConnection("ncalrpc:localhost[DEFAULT]", - ("60a15ec5-4de8-11d7-a637-005056a20182", 1)) + ("60a15ec5-4de8-11d7-a637-005056a20182", 1), lp_ctx=cmdline_loadparm) y = ClientConnection("ncalrpc:localhost", ("60a15ec5-4de8-11d7-a637-005056a20182", 1), - basis_connection=x) + basis_connection=x, lp_ctx=cmdline_loadparm) self.assertEquals("\x01\x00\x00\x00", y.request(0, chr(0) * 4)) diff --git a/source4/scripting/python/samba/tests/dcerpc/rpcecho.py b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py index bbbd0d76ec..96bb3923a6 100644 --- a/source4/scripting/python/samba/tests/dcerpc/rpcecho.py +++ b/source4/scripting/python/samba/tests/dcerpc/rpcecho.py @@ -27,7 +27,7 @@ class RpcEchoTests(RpcInterfaceTestCase): self.conn = echo.rpcecho("ncalrpc:", self.get_loadparm()) def test_two_contexts(self): - self.conn2 = echo.rpcecho("ncalrpc:", basis_connection=self.conn) + self.conn2 = echo.rpcecho("ncalrpc:", self.get_loadparm(), basis_connection=self.conn) self.assertEquals(3, self.conn2.AddOne(2)) def test_abstract_syntax(self): diff --git a/source4/scripting/python/samba/tests/samdb.py b/source4/scripting/python/samba/tests/samdb.py index 7e8ba053d4..fcf93a3fc6 100644 --- a/source4/scripting/python/samba/tests/samdb.py +++ b/source4/scripting/python/samba/tests/samdb.py @@ -19,12 +19,13 @@ from samba.auth import system_session from samba.credentials import Credentials import os -from samba.provision import setup_samdb, guess_names, setup_templatesdb +from samba.provision import setup_samdb, guess_names, setup_templatesdb, make_smbconf from samba.samdb import SamDB from samba.tests import cmdline_loadparm, TestCaseInTempDir from samba import security from unittest import TestCase import uuid +import param class SamDBTestCase(TestCaseInTempDir): def setUp(self): @@ -43,9 +44,22 @@ class SamDBTestCase(TestCaseInTempDir): hostguid = str(uuid.uuid4()) path = os.path.join(self.tempdir, "samdb.ldb") session_info = system_session() - names = guess_names(lp=cmdline_loadparm, hostname="foo", - domain="EXAMPLE.COM", dnsdomain="example.com", - serverrole="domain controller", + + hostname="foo" + domain="EXAMPLE" + dnsdomain="example.com" + serverrole="domain controller" + + smbconf = os.path.join(self.tempdir, "smb.conf") + make_smbconf(smbconf, setup_path, hostname, domain, dnsdomain, serverrole, + self.tempdir) + + lp = param.LoadParm() + lp.load(smbconf) + + names = guess_names(lp=lp, hostname=hostname, + domain=domain, dnsdomain=dnsdomain, + serverrole=severrole, domaindn=self.domaindn, configdn=configdn, schemadn=schemadn) setup_templatesdb(os.path.join(self.tempdir, "templates.ldb"), @@ -58,9 +72,10 @@ class SamDBTestCase(TestCaseInTempDir): policyguid, False, "secret", "secret", "secret", invocationid, "secret", "domain controller") + def tearDown(self): for f in ['templates.ldb', 'schema.ldb', 'configuration.ldb', - 'users.ldb', 'samdb.ldb']: + 'users.ldb', 'samdb.ldb', 'smb.conf']: os.remove(os.path.join(self.tempdir, f)) super(SamDBTestCase, self).tearDown() diff --git a/source4/selftest/samba4_tests.sh b/source4/selftest/samba4_tests.sh index 860a54f80c..b73ba55586 100755 --- a/source4/selftest/samba4_tests.sh +++ b/source4/selftest/samba4_tests.sh @@ -352,6 +352,6 @@ rm -rf $PREFIX/upgrade plantest "blackbox.upgrade" none $PYTHON setup/upgrade $CONFIGURATION --targetdir=$PREFIX/upgrade ../testdata/samba3 ../testdata/samba3/smb.conf rm -rf $PREFIX/provision mkdir $PREFIX/provision -plantest "blackbox.provision.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision.sh "$PREFIX/provision" "$CONFIGURATION" -plantest "blackbox.setpassword.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_setpassword.sh "$PREFIX/provision" "$CONFIGURATION" -plantest "blackbox.newuser.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_newuser.sh "$PREFIX/provision" "$CONFIGURATION" +plantest "blackbox.provision.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_provision.sh "$PREFIX/provision" +plantest "blackbox.setpassword.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_setpassword.sh "$PREFIX/provision" +plantest "blackbox.newuser.py" none PYTHON="$PYTHON" $samba4srcdir/setup/tests/blackbox_newuser.sh "$PREFIX/provision" diff --git a/source4/selftest/target/Samba4.pm b/source4/selftest/target/Samba4.pm index a12939b0a1..9f771ab8a3 100644 --- a/source4/selftest/target/Samba4.pm +++ b/source4/selftest/target/Samba4.pm @@ -521,6 +521,7 @@ sub provision($$$$$$) my $ncalrpcdir = "$prefix_abs/ncalrpc"; my $lockdir = "$prefix_abs/lockdir"; my $winbindd_socket_dir = "$prefix_abs/winbind_socket"; + my $ntp_signd_socket_dir = "$prefix_abs/ntp_signd_socket"; my $winbindd_priv_pipe_dir = "$privatedir/smbd.tmp/winbind_pipe"; my $nsswrap_passwd = "$etcdir/passwd"; my $nsswrap_group = "$etcdir/group"; @@ -556,6 +557,7 @@ sub provision($$$$$$) modules dir = $self->{bindir}/modules js include = $srcdir/scripting/libjs winbindd socket directory = $winbindd_socket_dir + ntp signd socket directory = $ntp_signd_socket_dir winbind separator = / name resolve order = bcast interfaces = $interfaces diff --git a/source4/setup/tests/blackbox_newuser.sh b/source4/setup/tests/blackbox_newuser.sh index fed5f7d263..3e534f2b52 100755 --- a/source4/setup/tests/blackbox_newuser.sh +++ b/source4/setup/tests/blackbox_newuser.sh @@ -1,20 +1,19 @@ #!/bin/sh -if [ $# -lt 2 ]; then +if [ $# -lt 1 ]; then cat <<EOF -Usage: blackbox_newuser.sh PREFIX CONFIGURATION +Usage: blackbox_newuser.sh PREFIX EOF exit 1; fi PREFIX="$1" -CONFIGURATION="$2" -shift 2 +shift 1 . `dirname $0`/../../../testprogs/blackbox/subunit.sh -testit "simple-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc +testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc testit "newuser" $PYTHON ./setup/newuser --configfile=$PREFIX/simple-dc/etc/smb.conf testuser testpass diff --git a/source4/setup/tests/blackbox_provision.sh b/source4/setup/tests/blackbox_provision.sh index 2afa9dc952..1a915aff79 100755 --- a/source4/setup/tests/blackbox_provision.sh +++ b/source4/setup/tests/blackbox_provision.sh @@ -1,28 +1,27 @@ #!/bin/sh -if [ $# -lt 2 ]; then +if [ $# -lt 1 ]; then cat <<EOF -Usage: blackbox_provision.sh PREFIX CONFIGURATION +Usage: blackbox_provision.sh PREFIX EOF exit 1; fi PREFIX="$1" -CONFIGURATION="$2" -shift 2 +shift 1 . `dirname $0`/../../../testprogs/blackbox/subunit.sh -testit "simple-default" $PYTHON ./setup/provision $CONFIGURATION --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-default -testit "simple-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc -testit "simple-member" $PYTHON ./setup/provision $CONFIGURATION --server-role="member" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-member -testit "simple-standalone" $PYTHON ./setup/provision $CONFIGURATION --server-role="standalone" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-standalone -testit "blank-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/blank-dc --blank -testit "partitions-only-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/partitions-only-dc --partitions-only +testit "simple-default" $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-default +testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc +testit "simple-member" $PYTHON ./setup/provision --server-role="member" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-member +testit "simple-standalone" $PYTHON ./setup/provision --server-role="standalone" --domain=FOO --realm=foo.example.com --targetdir=$PREFIX/simple-standalone +testit "blank-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/blank-dc --blank +testit "partitions-only-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/partitions-only-dc --partitions-only reprovision() { - $PYTHON ./setup/provision $CONFIGURATION --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision" - $PYTHON ./setup/provision $CONFIGURATION --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision" + $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision" + $PYTHON ./setup/provision --domain=FOO --realm=foo.example.com --targetdir="$PREFIX/reprovision" } testit "reprovision" reprovision diff --git a/source4/setup/tests/blackbox_setpassword.sh b/source4/setup/tests/blackbox_setpassword.sh index 725466150c..89f1aa5858 100755 --- a/source4/setup/tests/blackbox_setpassword.sh +++ b/source4/setup/tests/blackbox_setpassword.sh @@ -1,19 +1,18 @@ #!/bin/sh -if [ $# -lt 2 ]; then +if [ $# -lt 1 ]; then cat <<EOF -Usage: blackbox_setpassword.sh PREFIX CONFIGURATION +Usage: blackbox_setpassword.sh PREFIX EOF exit 1; fi PREFIX="$1" -CONFIGURATION="$2" -shift 2 +shift 1 . `dirname $0`/../../../testprogs/blackbox/subunit.sh -testit "simple-dc" $PYTHON ./setup/provision $CONFIGURATION --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc +testit "simple-dc" $PYTHON ./setup/provision --server-role="dc" --domain=FOO --realm=foo.example.com --domain-sid=S-1-5-21-4177067393-1453636373-93818738 --targetdir=$PREFIX/simple-dc testit "newuser" $PYTHON ./setup/newuser --configfile=$PREFIX/simple-dc/etc/smb.conf testuser testpass diff --git a/source4/smb_server/smb2/fileinfo.c b/source4/smb_server/smb2/fileinfo.c index d6db61eaba..942000133c 100644 --- a/source4/smb_server/smb2/fileinfo.c +++ b/source4/smb_server/smb2/fileinfo.c @@ -55,8 +55,7 @@ static void smb2srv_getinfo_send(struct ntvfs_request *ntvfs) SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x08, true, op->info->out.blob.length)); - /* TODO: this is maybe a o16s32_blob */ - SMB2SRV_CHECK(smb2_push_o16s16_blob(&req->out, 0x02, op->info->out.blob)); + SMB2SRV_CHECK(smb2_push_o16s32_blob(&req->out, 0x02, op->info->out.blob)); SSVAL(req->out.body, 0x06, 0); smb2srv_send_reply(req); diff --git a/source4/smb_server/smb2/fileio.c b/source4/smb_server/smb2/fileio.c index 086ddc690e..2c322ea587 100644 --- a/source4/smb_server/smb2/fileio.c +++ b/source4/smb_server/smb2/fileio.c @@ -36,6 +36,18 @@ static void smb2srv_create_send(struct ntvfs_request *ntvfs) DATA_BLOB blob; SMB2SRV_CHECK_ASYNC_STATUS(io, union smb_open); + + /* setup the blobs we should give in the reply */ + if (io->smb2.out.maximal_access != 0) { + uint32_t data[2]; + SIVAL(data, 0, 0); + SIVAL(data, 4, io->smb2.out.maximal_access); + SMB2SRV_CHECK(smb2_create_blob_add(req, &io->smb2.out.blobs, + SMB2_CREATE_TAG_MXAC, + data_blob_const(data, 8))); + } + + SMB2SRV_CHECK(smb2_create_blob_push(req, &blob, io->smb2.out.blobs)); SMB2SRV_CHECK(smb2srv_setup_reply(req, 0x58, true, blob.length)); diff --git a/source4/smb_server/smb2/sesssetup.c b/source4/smb_server/smb2/sesssetup.c index 1aaacf897c..d386bfc72d 100644 --- a/source4/smb_server/smb2/sesssetup.c +++ b/source4/smb_server/smb2/sesssetup.c @@ -158,7 +158,8 @@ static void smb2srv_sesssetup_backend(struct smb2srv_request *req, union smb_ses } if (!smb_sess) { - status = NT_STATUS_USER_SESSION_DELETED; + /* see WSPP test suite - test 11 */ + status = NT_STATUS_REQUEST_NOT_ACCEPTED; goto failed; } diff --git a/source4/torture/config.mk b/source4/torture/config.mk index ab4dcbbc96..b11448a218 100644 --- a/source4/torture/config.mk +++ b/source4/torture/config.mk @@ -265,23 +265,6 @@ gentest_OBJ_FILES = $(torturesrcdir)/gentest.o MANPAGES += $(torturesrcdir)/man/gentest.1 ################################# -# Start BINARY gentest_smb2 -[BINARY::gentest_smb2] -INSTALLDIR = BINDIR -PRIVATE_DEPENDENCIES = \ - LIBSAMBA-HOSTCONFIG \ - LIBSAMBA-UTIL \ - LIBPOPT \ - POPT_SAMBA \ - POPT_CREDENTIALS \ - LIBCLI_SMB \ - LIBCLI_RAW -# End BINARY gentest_smb2 -################################# - -gentest_smb2_OBJ_FILES = $(torturesrcdir)/gentest_smb2.o - -################################# # Start BINARY masktest [BINARY::masktest] INSTALLDIR = BINDIR diff --git a/source4/torture/gentest.c b/source4/torture/gentest.c index 068b6bdc2f..07d394fad6 100644 --- a/source4/torture/gentest.c +++ b/source4/torture/gentest.c @@ -1,7 +1,9 @@ /* Unix SMB/CIFS implementation. - generic testing tool - Copyright (C) Andrew Tridgell 2003 + + generic testing tool - version with both SMB and SMB2 support + + Copyright (C) Andrew Tridgell 2003-2008 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 @@ -25,12 +27,17 @@ #include "libcli/raw/request.h" #include "libcli/libcli.h" #include "libcli/raw/libcliraw.h" +#include "libcli/smb2/smb2.h" +#include "libcli/smb2/smb2_calls.h" #include "librpc/gen_ndr/security.h" +#include "librpc/gen_ndr/ndr_security.h" #include "auth/credentials/credentials.h" #include "libcli/resolve/resolve.h" #include "auth/gensec/gensec.h" #include "param/param.h" #include "dynconfig/dynconfig.h" +#include "libcli/security/security.h" +#include "libcli/raw/raw_proto.h" #define NSERVERS 2 #define NINSTANCES 2 @@ -49,14 +56,20 @@ static struct gentest_options { const char *seeds_file; int use_preset_seeds; int fast_reconnect; + int mask_indexing; + int no_eas; + int no_acls; int skip_cleanup; + int valid; + int smb2; } options; /* mapping between open handles on the server and local handles */ static struct { bool active; uint_t instance; - uint_t server_fnum[NSERVERS]; + struct smb2_handle smb2_handle[NSERVERS]; /* SMB2 */ + uint16_t smb_handle[NSERVERS]; /* SMB */ const char *name; } *open_handles; static uint_t num_open_handles; @@ -64,7 +77,8 @@ static uint_t num_open_handles; /* state information for the servers. We open NINSTANCES connections to each server */ static struct { - struct smbcli_state *cli[NINSTANCES]; + struct smb2_tree *smb2_tree[NINSTANCES]; + struct smbcli_tree *smb_tree[NINSTANCES]; char *server_name; char *share_name; struct cli_credentials *credentials; @@ -80,7 +94,8 @@ static struct { /* oplock break info */ static struct { bool got_break; - uint16_t fnum; + struct smb2_handle smb2_handle; + uint16_t smb_handle; uint16_t handle; uint8_t level; bool do_close; @@ -100,14 +115,19 @@ static struct { NTSTATUS status; uint_t opnum; TALLOC_CTX *mem_ctx; + const char *mismatch; } current_op; +static struct smb2_handle bad_smb2_handle; #define BAD_HANDLE 0xFFFE -static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private); -static void idle_func(struct smbcli_transport *transport, void *private); +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data); +static void idle_func_smb2(struct smb2_transport *transport, void *private); +static bool oplock_handler_smb(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private); +static void idle_func_smb(struct smbcli_transport *transport, void *private); /* check if a string should be ignored. This is used as the basis @@ -139,8 +159,15 @@ static bool connect_servers_fast(void) for (h=0;h<options.max_open_handles;h++) { if (!open_handles[h].active) continue; for (i=0;i<NSERVERS;i++) { - if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree, - open_handles[h].server_fnum[i])))) { + NTSTATUS status; + if (options.smb2) { + status = smb2_util_close(servers[i].smb2_tree[open_handles[h].instance], + open_handles[h].smb2_handle[i]); + } else { + status = smbcli_close(servers[i].smb_tree[open_handles[h].instance], + open_handles[h].smb_handle[i]); + } + if (NT_STATUS_IS_ERR(status)) { return false; } open_handles[h].active = false; @@ -161,7 +188,7 @@ static bool connect_servers(struct event_context *ev, { int i, j; - if (options.fast_reconnect && servers[0].cli[0]) { + if (options.fast_reconnect && servers[0].smb2_tree[0]) { if (connect_servers_fast()) { return true; } @@ -170,18 +197,25 @@ static bool connect_servers(struct event_context *ev, /* close any existing connections */ for (i=0;i<NSERVERS;i++) { for (j=0;j<NINSTANCES;j++) { - if (servers[i].cli[j]) { - smbcli_tdis(servers[i].cli[j]); - talloc_free(servers[i].cli[j]); - servers[i].cli[j] = NULL; + if (servers[i].smb2_tree[j]) { + smb2_tdis(servers[i].smb2_tree[j]); + talloc_free(servers[i].smb2_tree[j]); + servers[i].smb2_tree[j] = NULL; + } + if (servers[i].smb_tree[j]) { + smb_tree_disconnect(servers[i].smb_tree[j]); + talloc_free(servers[i].smb_tree[j]); + servers[i].smb_tree[j] = NULL; } } } for (i=0;i<NSERVERS;i++) { for (j=0;j<NINSTANCES;j++) { - struct smbcli_options smb_options; NTSTATUS status; + struct smbcli_options smb_options; + lp_smbcli_options(lp_ctx, &smb_options); + printf("Connecting to \\\\%s\\%s as %s - instance %d\n", servers[i].server_name, servers[i].share_name, servers[i].credentials->username, j); @@ -189,14 +223,23 @@ static bool connect_servers(struct event_context *ev, cli_credentials_set_workstation(servers[i].credentials, "gentest", CRED_SPECIFIED); - lp_smbcli_options(lp_ctx, &smb_options); - status = smbcli_full_connection(NULL, &servers[i].cli[j], - servers[i].server_name, - lp_smb_ports(lp_ctx), - servers[i].share_name, NULL, - servers[i].credentials, - lp_resolve_context(lp_ctx), - ev, &smb_options); + if (options.smb2) { + status = smb2_connect(NULL, servers[i].server_name, + servers[i].share_name, + lp_resolve_context(lp_ctx), + servers[i].credentials, + &servers[i].smb2_tree[j], + ev, &smb_options); + } else { + status = smbcli_tree_full_connection(NULL, + &servers[i].smb_tree[j], + servers[i].server_name, + lp_smb_ports(lp_ctx), + servers[i].share_name, "A:", + servers[i].credentials, + lp_resolve_context(lp_ctx), ev, + &smb_options); + } if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect to \\\\%s\\%s - %s\n", servers[i].server_name, servers[i].share_name, @@ -204,8 +247,17 @@ static bool connect_servers(struct event_context *ev, return false; } - smbcli_oplock_handler(servers[i].cli[j]->transport, oplock_handler, NULL); - smbcli_transport_idle_handler(servers[i].cli[j]->transport, idle_func, 50000, NULL); + if (options.smb2) { + servers[i].smb2_tree[j]->session->transport->oplock.handler = oplock_handler_smb2; + servers[i].smb2_tree[j]->session->transport->oplock.private_data = (void *)(uintptr_t)((i<<8)|j); + smb2_transport_idle_handler(servers[i].smb2_tree[j]->session->transport, + idle_func_smb2, 50000, NULL); + } else { + smbcli_oplock_handler(servers[i].smb_tree[j]->session->transport, oplock_handler_smb, + (void *)(uintptr_t)((i<<8)|j)); + smbcli_transport_idle_handler(servers[i].smb_tree[j]->session->transport, idle_func_smb, + 50000, (void *)(uintptr_t)((i<<8)|j)); + } } } @@ -218,33 +270,62 @@ static bool connect_servers(struct event_context *ev, static uint_t time_skew(void) { uint_t ret; - ret = labs(servers[0].cli[0]->transport->negotiate.server_time - - servers[1].cli[0]->transport->negotiate.server_time); + if (options.smb2) { + ret = labs(servers[0].smb2_tree[0]->session->transport->negotiate.system_time - + servers[1].smb2_tree[0]->session->transport->negotiate.system_time); + } else { + ret = labs(servers[0].smb_tree[0]->session->transport->negotiate.server_time - + servers[1].smb_tree[0]->session->transport->negotiate.server_time); + } return ret + 300; } + +static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) +{ + return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; +} + /* - turn an fnum for an instance into a handle + turn a server handle into a local handle */ -static uint_t fnum_to_handle(int server, int instance, uint16_t fnum) +static uint_t fnum_to_handle_smb2(int server, int instance, struct smb2_handle server_handle) { uint_t i; for (i=0;i<options.max_open_handles;i++) { if (!open_handles[i].active || instance != open_handles[i].instance) continue; - if (open_handles[i].server_fnum[server] == fnum) { + if (smb2_handle_equal(&open_handles[i].smb2_handle[server], &server_handle)) { return i; } } - printf("Invalid fnum %d in fnum_to_handle on server %d instance %d\n", - fnum, server, instance); + printf("Invalid server handle in fnum_to_handle on server %d instance %d\n", + server, instance); + return BAD_HANDLE; +} + +/* + turn a server handle into a local handle +*/ +static uint_t fnum_to_handle_smb(int server, int instance, uint16_t server_handle) +{ + uint_t i; + for (i=0;i<options.max_open_handles;i++) { + if (!open_handles[i].active || + instance != open_handles[i].instance) continue; + if (open_handles[i].smb_handle[server] == server_handle) { + return i; + } + } + printf("Invalid server handle in fnum_to_handle on server %d instance %d\n", + server, instance); return BAD_HANDLE; } /* add some newly opened handles */ -static void gen_add_handle(int instance, const char *name, uint16_t fnums[NSERVERS]) +static void gen_add_handle_smb2(int instance, const char *name, struct smb2_handle handles[NSERVERS]) { int i, h; for (h=0;h<options.max_open_handles;h++) { @@ -254,43 +335,100 @@ static void gen_add_handle(int instance, const char *name, uint16_t fnums[NSERVE /* we have to force close a random handle */ h = random() % options.max_open_handles; for (i=0;i<NSERVERS;i++) { - if (NT_STATUS_IS_ERR((smbcli_close(servers[i].cli[open_handles[h].instance]->tree, - open_handles[h].server_fnum[i])))) { + NTSTATUS status; + status = smb2_util_close(servers[i].smb2_tree[open_handles[h].instance], + open_handles[h].smb2_handle[i]); + if (NT_STATUS_IS_ERR(status)) { printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n", - smbcli_errstr(servers[i].cli[open_handles[h].instance]->tree)); + nt_errstr(status)); } } printf("Recovered handle %d\n", h); num_open_handles--; } for (i=0;i<NSERVERS;i++) { - open_handles[h].server_fnum[i] = fnums[i]; + open_handles[h].smb2_handle[i] = handles[i]; open_handles[h].instance = instance; open_handles[h].active = true; open_handles[h].name = name; } num_open_handles++; - printf("OPEN num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", - num_open_handles, h, - open_handles[h].server_fnum[0], open_handles[h].server_fnum[1], - name); + printf("OPEN num_open_handles=%d h=%d (%s)\n", + num_open_handles, h, name); +} + +/* + add some newly opened handles +*/ +static void gen_add_handle_smb(int instance, const char *name, uint16_t handles[NSERVERS]) +{ + int i, h; + for (h=0;h<options.max_open_handles;h++) { + if (!open_handles[h].active) break; + } + if (h == options.max_open_handles) { + /* we have to force close a random handle */ + h = random() % options.max_open_handles; + for (i=0;i<NSERVERS;i++) { + NTSTATUS status; + status = smbcli_close(servers[i].smb_tree[open_handles[h].instance], + open_handles[h].smb_handle[i]); + if (NT_STATUS_IS_ERR(status)) { + printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n", + nt_errstr(status)); + } + } + printf("Recovered handle %d\n", h); + num_open_handles--; + } + for (i=0;i<NSERVERS;i++) { + open_handles[h].smb_handle[i] = handles[i]; + open_handles[h].instance = instance; + open_handles[h].active = true; + open_handles[h].name = name; + } + num_open_handles++; + + printf("OPEN num_open_handles=%d h=%d (%s)\n", + num_open_handles, h, name); +} + + +/* + remove a closed handle +*/ +static void gen_remove_handle_smb2(int instance, struct smb2_handle handles[NSERVERS]) +{ + int h; + for (h=0;h<options.max_open_handles;h++) { + if (instance == open_handles[h].instance && + smb2_handle_equal(&open_handles[h].smb2_handle[0], &handles[0])) { + open_handles[h].active = false; + num_open_handles--; + printf("CLOSE num_open_handles=%d h=%d (%s)\n", + num_open_handles, h, + open_handles[h].name); + return; + } + } + printf("Removing invalid handle!?\n"); + exit(1); } /* remove a closed handle */ -static void gen_remove_handle(int instance, uint16_t fnums[NSERVERS]) +static void gen_remove_handle_smb(int instance, uint16_t handles[NSERVERS]) { int h; for (h=0;h<options.max_open_handles;h++) { if (instance == open_handles[h].instance && - open_handles[h].server_fnum[0] == fnums[0]) { + open_handles[h].smb_handle[0] == handles[0]) { open_handles[h].active = false; num_open_handles--; - printf("CLOSE num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s)\n", + printf("CLOSE num_open_handles=%d h=%d (%s)\n", num_open_handles, h, - open_handles[h].server_fnum[0], open_handles[h].server_fnum[1], open_handles[h].name); return; } @@ -308,12 +446,21 @@ static bool gen_chance(uint_t chance) } /* - map an internal handle number to a server fnum + map an internal handle number to a server handle +*/ +static struct smb2_handle gen_lookup_handle_smb2(int server, uint16_t handle) +{ + if (handle == BAD_HANDLE) return bad_smb2_handle; + return open_handles[handle].smb2_handle[server]; +} + +/* + map an internal handle number to a server handle */ -static uint16_t gen_lookup_fnum(int server, uint16_t handle) +static uint16_t gen_lookup_handle_smb(int server, uint16_t handle) { - if (handle == BAD_HANDLE) return handle; - return open_handles[handle].server_fnum[server]; + if (handle == BAD_HANDLE) return BAD_HANDLE; + return open_handles[handle].smb_handle[server]; } /* @@ -342,8 +489,8 @@ static uint16_t gen_fnum(int instance) */ static uint16_t gen_fnum_close(int instance) { - if (num_open_handles < 3) { - if (gen_chance(80)) return BAD_HANDLE; + if (num_open_handles < 5) { + if (gen_chance(90)) return BAD_HANDLE; } return gen_fnum(instance); @@ -352,7 +499,7 @@ static uint16_t gen_fnum_close(int instance) /* generate an integer in a specified range */ -static int gen_int_range(uint_t min, uint_t max) +static int gen_int_range(uint64_t min, uint64_t max) { uint_t r = random(); return min + (r % (1+max-min)); @@ -374,6 +521,7 @@ static uint16_t gen_root_fid(int instance) static int gen_offset(void) { if (gen_chance(20)) return 0; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); return gen_int_range(0, 1024*1024); } @@ -383,6 +531,7 @@ static int gen_offset(void) static int gen_io_count(void) { if (gen_chance(20)) return 0; +// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); return gen_int_range(0, 4096); } @@ -391,32 +540,29 @@ static int gen_io_count(void) */ static const char *gen_fname(void) { - const char *names[] = {"\\gentest\\gentest.dat", - "\\gentest\\foo", - "\\gentest\\foo2.sym", - "\\gentest\\foo3.dll", - "\\gentest\\foo4", - "\\gentest\\foo4:teststream1", - "\\gentest\\foo4:teststream2", - "\\gentest\\foo5.exe", - "\\gentest\\foo5.exe:teststream3", - "\\gentest\\foo5.exe:teststream4", - "\\gentest\\foo6.com", - "\\gentest\\blah", - "\\gentest\\blah\\blergh.txt", - "\\gentest\\blah\\blergh2", - "\\gentest\\blah\\blergh3.txt", - "\\gentest\\blah\\blergh4", - "\\gentest\\blah\\blergh5.txt", - "\\gentest\\blah\\blergh5", - "\\gentest\\blah\\.", -#if 0 - /* this causes problem with w2k3 */ - "\\gentest\\blah\\..", -#endif - "\\gentest\\a_very_long_name.bin", - "\\gentest\\x.y", - "\\gentest\\blah"}; + const char *names[] = {"gentest\\gentest.dat", + "gentest\\foo", + "gentest\\foo2.sym", + "gentest\\foo3.dll", + "gentest\\foo4", + "gentest\\foo4:teststream1", + "gentest\\foo4:teststream2", + "gentest\\foo5.exe", + "gentest\\foo5.exe:teststream3", + "gentest\\foo5.exe:teststream4", + "gentest\\foo6.com", + "gentest\\blah", + "gentest\\blah\\blergh.txt", + "gentest\\blah\\blergh2", + "gentest\\blah\\blergh3.txt", + "gentest\\blah\\blergh4", + "gentest\\blah\\blergh5.txt", + "gentest\\blah\\blergh5", + "gentest\\blah\\.", + "gentest\\blah\\..", + "gentest\\a_very_long_name.bin", + "gentest\\x.y", + "gentest\\blah"}; int i; do { @@ -446,12 +592,12 @@ static const char *gen_fname_open(int instance) static const char *gen_pattern(void) { int i; - const char *names[] = {"\\gentest\\*.dat", - "\\gentest\\*", - "\\gentest\\*.*", - "\\gentest\\blah\\*.*", - "\\gentest\\blah\\*", - "\\gentest\\?"}; + const char *names[] = {"gentest\\*.dat", + "gentest\\*", + "gentest\\*.*", + "gentest\\blah\\*.*", + "gentest\\blah\\*", + "gentest\\?"}; if (gen_chance(50)) return gen_fname(); @@ -462,6 +608,25 @@ static const char *gen_pattern(void) return names[i]; } +static uint32_t gen_bits_levels(int nlevels, ...) +{ + va_list ap; + uint32_t pct; + uint32_t mask; + int i; + va_start(ap, nlevels); + for (i=0;i<nlevels;i++) { + pct = va_arg(ap, uint32_t); + mask = va_arg(ap, uint32_t); + if (pct == 100 || gen_chance(pct)) { + va_end(ap); + return mask & random(); + } + } + va_end(ap); + return 0; +} + /* generate a bitmask */ @@ -477,11 +642,40 @@ static uint32_t gen_bits_mask(uint_t mask) */ static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2) { - if (gen_chance(10)) return gen_bits_mask(mask2); + if (!options.valid && gen_chance(10)) return gen_bits_mask(mask2); return gen_bits_mask(mask1); } /* + generate reserved values + */ +static uint64_t gen_reserved8(void) +{ + if (options.valid) return 0; + return gen_bits_mask(0xFF); +} + +static uint64_t gen_reserved16(void) +{ + if (options.valid) return 0; + return gen_bits_mask(0xFFFF); +} + +static uint64_t gen_reserved32(void) +{ + if (options.valid) return 0; + return gen_bits_mask(0xFFFFFFFF); +} + +static uint64_t gen_reserved64(void) +{ + if (options.valid) return 0; + return gen_bits_mask(0xFFFFFFFF) | (((uint64_t)gen_bits_mask(0xFFFFFFFF))<<32); +} + + + +/* generate a boolean */ static bool gen_bool(void) @@ -500,24 +694,26 @@ static uint16_t gen_rename_flags(void) return gen_bits_mask(0xFFFF); } - /* - return a lockingx lock mode + generate a pid */ -static uint16_t gen_lock_mode(void) +static uint16_t gen_pid(void) { - if (gen_chance(5)) return gen_bits_mask(0xFFFF); - if (gen_chance(20)) return gen_bits_mask(0x1F); - return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES); + if (gen_chance(10)) return gen_bits_mask(0xFFFF); + return getpid(); } /* - generate a pid + return a set of lock flags */ -static uint16_t gen_pid(void) +static uint16_t gen_lock_flags_smb2(void) { - if (gen_chance(10)) return gen_bits_mask(0xFFFF); - return getpid(); + if (!options.valid && gen_chance(5)) return gen_bits_mask(0xFFFF); + if (gen_chance(20)) return gen_bits_mask(0x1F); + if (gen_chance(50)) return SMB2_LOCK_FLAG_UNLOCK; + return gen_bits_mask(SMB2_LOCK_FLAG_SHARED | + SMB2_LOCK_FLAG_EXCLUSIVE | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY); } /* @@ -529,22 +725,35 @@ static off_t gen_lock_count(void) } /* - generate a ntcreatex flags field + generate a NT access mask */ -static uint32_t gen_ntcreatex_flags(void) +static uint32_t gen_access_mask(void) { - if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED; - return gen_bits_mask2(0x1F, 0xFFFFFFFF); + uint32_t ret; + if (gen_chance(70)) return SEC_FLAG_MAXIMUM_ALLOWED; + if (gen_chance(70)) return SEC_FILE_ALL; + ret = gen_bits_mask(0xFFFFFFFF); + if (options.valid) ret &= ~SEC_MASK_INVALID; + return ret; } /* - generate a NT access mask + return a lockingx lock mode */ -static uint32_t gen_access_mask(void) +static uint16_t gen_lock_mode(void) { - if (gen_chance(50)) return SEC_FLAG_MAXIMUM_ALLOWED; - if (gen_chance(20)) return SEC_FILE_ALL; - return gen_bits_mask(0xFFFFFFFF); + if (!options.valid && gen_chance(5)) return gen_bits_mask(0xFFFF); + if (gen_chance(20)) return gen_bits_mask(0x1F); + return gen_bits_mask(LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES); +} + +/* + generate a ntcreatex flags field +*/ +static uint32_t gen_ntcreatex_flags(void) +{ + if (gen_chance(70)) return NTCREATEX_FLAGS_EXTENDED; + return gen_bits_mask2(0x1F, 0xFFFFFFFF); } /* @@ -552,7 +761,7 @@ static uint32_t gen_access_mask(void) */ static uint32_t gen_create_options(void) { - if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); + if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); if (gen_chance(50)) return 0; return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY); } @@ -562,7 +771,8 @@ static uint32_t gen_create_options(void) */ static uint32_t gen_open_disp(void) { - if (gen_chance(10)) return gen_bits_mask(0xFFFFFFFF); + if (gen_chance(50)) return NTCREATEX_DISP_OPEN_IF; + if (!options.valid && gen_chance(10)) return gen_bits_mask(0xFFFFFFFF); return gen_int_range(0, 5); } @@ -571,7 +781,7 @@ static uint32_t gen_open_disp(void) */ static uint16_t gen_openx_mode(void) { - if (gen_chance(20)) return gen_bits_mask(0xFFFF); + if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF); if (gen_chance(20)) return gen_bits_mask(0xFF); return OPENX_MODE_DENY_NONE | gen_bits_mask(0x3); } @@ -581,7 +791,7 @@ static uint16_t gen_openx_mode(void) */ static uint16_t gen_openx_flags(void) { - if (gen_chance(20)) return gen_bits_mask(0xFFFF); + if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF); return gen_bits_mask(0x7); } @@ -590,7 +800,7 @@ static uint16_t gen_openx_flags(void) */ static uint16_t gen_openx_func(void) { - if (gen_chance(20)) return gen_bits_mask(0xFFFF); + if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFF); return gen_bits_mask(0x13); } @@ -599,7 +809,12 @@ static uint16_t gen_openx_func(void) */ static uint32_t gen_attrib(void) { - if (gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); + uint32_t ret; + if (gen_chance(20)) { + ret = gen_bits_mask(0xFFFFFFFF); + if (options.valid) ret &= FILE_ATTRIBUTE_ALL_MASK; + return ret; + } return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); } @@ -613,7 +828,16 @@ static time_t gen_timet(void) } /* - generate a unix timestamp + generate a milliseconds protocol timeout +*/ +static uint32_t gen_timeout(void) +{ + if (gen_chance(98)) return 0; + return random() % 50; +} + +/* + generate a timestamp */ static NTTIME gen_nttime(void) { @@ -623,12 +847,13 @@ static NTTIME gen_nttime(void) } /* - generate a milliseconds protocol timeout + generate a timewarp value */ -static uint32_t gen_timeout(void) +static NTTIME gen_timewarp(void) { - if (gen_chance(98)) return 0; - return random() % 50; + NTTIME ret = gen_nttime(); + if (gen_chance(98)) ret = 0; + return ret; } /* @@ -688,43 +913,47 @@ static struct ea_struct gen_ea_struct(void) return ea; } - /* - this is called when a change notify reply comes in + generate an ea_struct */ -static void async_notify(struct smbcli_request *req) +static struct smb_ea_list gen_ea_list(void) { - union smb_notify notify; - NTSTATUS status; - int i, j; - uint16_t tid; - struct smbcli_transport *transport = req->transport; - - tid = SVAL(req->in.hdr, HDR_TID); - - notify.nttrans.level = RAW_NOTIFY_NTTRANS; - status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); - if (NT_STATUS_IS_OK(status)) { - printf("notify tid=%d num_changes=%d action=%d name=%s\n", - tid, - notify.nttrans.out.num_changes, - notify.nttrans.out.changes[0].action, - notify.nttrans.out.changes[0].name.s); + struct smb_ea_list eas; + int i; + if (options.no_eas) { + ZERO_STRUCT(eas); + return eas; } - - for (i=0;i<NSERVERS;i++) { - for (j=0;j<NINSTANCES;j++) { - if (transport == servers[i].cli[j]->transport && - tid == servers[i].cli[j]->tree->tid) { - notifies[i][j].notify_count++; - notifies[i][j].status = status; - notifies[i][j].notify = notify; - } - } + eas.num_eas = gen_int_range(0, 3); + eas.eas = talloc_array(current_op.mem_ctx, struct ea_struct, eas.num_eas); + for (i=0;i<eas.num_eas;i++) { + eas.eas[i] = gen_ea_struct(); } + return eas; +} + +/* generate a security descriptor */ +static struct security_descriptor *gen_sec_desc(void) +{ + struct security_descriptor *sd; + if (options.no_acls || gen_chance(90)) return NULL; + + sd = security_descriptor_dacl_create(current_op.mem_ctx, + 0, NULL, NULL, + NULL, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_WRITE_DATA | SEC_STD_WRITE_DAC, + SEC_ACE_FLAG_OBJECT_INHERIT, + SID_WORLD, + SEC_ACE_TYPE_ACCESS_ALLOWED, + SEC_FILE_ALL | SEC_STD_ALL, + 0, + NULL); + return sd; } -static void oplock_handler_close_recv(struct smbcli_request *req) + +static void oplock_handler_close_recv_smb(struct smbcli_request *req) { NTSTATUS status; status = smbcli_request_simple_recv(req); @@ -737,7 +966,7 @@ static void oplock_handler_close_recv(struct smbcli_request *req) /* the oplock handler will either ack the break or close the file */ -static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private) +static bool oplock_handler_smb(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *private) { union smb_close io; int i, j; @@ -750,14 +979,14 @@ static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin for (i=0;i<NSERVERS;i++) { for (j=0;j<NINSTANCES;j++) { - if (transport == servers[i].cli[j]->transport && - tid == servers[i].cli[j]->tree->tid) { + if (transport == servers[i].smb_tree[j]->session->transport && + tid == servers[i].smb_tree[j]->tid) { oplocks[i][j].got_break = true; - oplocks[i][j].fnum = fnum; - oplocks[i][j].handle = fnum_to_handle(i, j, fnum); + oplocks[i][j].smb_handle = fnum; + oplocks[i][j].handle = fnum_to_handle_smb(i, j, fnum); oplocks[i][j].level = level; oplocks[i][j].do_close = do_close; - tree = servers[i].cli[j]->tree; + tree = servers[i].smb_tree[j]; } } } @@ -784,7 +1013,7 @@ static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin return false; } - req->async.fn = oplock_handler_close_recv; + req->async.fn = oplock_handler_close_recv_smb; req->async.private = NULL; return true; @@ -796,14 +1025,134 @@ static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uin an operation on another connection blocking until that break is acked we check for operations on all transports in the idle function */ -static void idle_func(struct smbcli_transport *transport, void *private) +static void idle_func_smb(struct smbcli_transport *transport, void *private) +{ + int i, j; + for (i=0;i<NSERVERS;i++) { + for (j=0;j<NINSTANCES;j++) { + if (servers[i].smb_tree[j] && + transport != servers[i].smb_tree[j]->session->transport) { + smbcli_transport_process(servers[i].smb_tree[j]->session->transport); + } + } + } + +} + +static void oplock_handler_close_recv_smb2(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_close io; + status = smb2_close_recv(req, &io); + if (!NT_STATUS_IS_OK(status)) { + printf("close failed in oplock_handler\n"); + smb_panic("close failed in oplock_handler"); + } +} + +static void oplock_handler_ack_callback_smb2(struct smb2_request *req) +{ + NTSTATUS status; + struct smb2_break br; + + status = smb2_break_recv(req, &br); + if (!NT_STATUS_IS_OK(status)) { + printf("oplock break ack failed in oplock_handler\n"); + smb_panic("oplock break ack failed in oplock_handler"); + } +} + +static bool send_oplock_ack_smb2(struct smb2_tree *tree, struct smb2_handle handle, + uint8_t level) +{ + struct smb2_break br; + struct smb2_request *req; + + ZERO_STRUCT(br); + br.in.file.handle = handle; + br.in.oplock_level = level; + br.in.reserved = gen_reserved8(); + br.in.reserved2 = gen_reserved32(); + + req = smb2_break_send(tree, &br); + if (req == NULL) return false; + req->async.fn = oplock_handler_ack_callback_smb2; + req->async.private_data = NULL; + return true; +} + +/* + the oplock handler will either ack the break or close the file +*/ +static bool oplock_handler_smb2(struct smb2_transport *transport, const struct smb2_handle *handle, + uint8_t level, void *private_data) +{ + struct smb2_close io; + unsigned i, j; + bool do_close; + struct smb2_tree *tree = NULL; + struct smb2_request *req; + + srandom(current_op.seed); + do_close = gen_chance(50); + + i = ((uintptr_t)private_data) >> 8; + j = ((uintptr_t)private_data) & 0xFF; + + if (i >= NSERVERS || j >= NINSTANCES) { + printf("Bad private_data in oplock_handler\n"); + return false; + } + + oplocks[i][j].got_break = true; + oplocks[i][j].smb2_handle = *handle; + oplocks[i][j].handle = fnum_to_handle_smb2(i, j, *handle); + oplocks[i][j].level = level; + oplocks[i][j].do_close = do_close; + tree = talloc_get_type(servers[i].smb2_tree[j], struct smb2_tree); + + if (!tree) { + printf("Oplock break not for one of our trees!?\n"); + return false; + } + + if (!do_close) { + printf("oplock ack handle=%d\n", oplocks[i][j].handle); + return send_oplock_ack_smb2(tree, *handle, level); + } + + printf("oplock close fnum=%d\n", oplocks[i][j].handle); + + ZERO_STRUCT(io); + io.in.file.handle = *handle; + io.in.flags = 0; + req = smb2_close_send(tree, &io); + + if (req == NULL) { + printf("WARNING: close failed in oplock_handler_close\n"); + return false; + } + + req->async.fn = oplock_handler_close_recv_smb2; + req->async.private_data = NULL; + + return true; +} + + +/* + the idle function tries to cope with getting an oplock break on a connection, and + an operation on another connection blocking until that break is acked + we check for operations on all transports in the idle function +*/ +static void idle_func_smb2(struct smb2_transport *transport, void *private) { int i, j; for (i=0;i<NSERVERS;i++) { for (j=0;j<NINSTANCES;j++) { - if (servers[i].cli[j] && - transport != servers[i].cli[j]->transport) { - smbcli_transport_process(servers[i].cli[j]->transport); + if (servers[i].smb2_tree[j] && + transport != servers[i].smb2_tree[j]->session->transport) { + // smb2_transport_process(servers[i].smb2_tree[j]->session->transport); } } } @@ -819,17 +1168,20 @@ static bool compare_status(NTSTATUS status1, NTSTATUS status2) if (NT_STATUS_EQUAL(status1, status2)) return true; /* one code being an error and the other OK is always an error */ - if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return false; + if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) { + current_op.mismatch = nt_errstr(status1); + return false; + } /* if we are ignoring one of the status codes then consider this a match */ if (ignore_pattern(nt_errstr(status1)) || ignore_pattern(nt_errstr(status2))) { return true; } + current_op.mismatch = nt_errstr(status1); return false; } - /* check for pending packets on all connections */ @@ -841,7 +1193,7 @@ static void check_pending(void) for (j=0;j<NINSTANCES;j++) { for (i=0;i<NSERVERS;i++) { - smbcli_transport_process(servers[i].cli[j]->transport); + // smb2_transport_process(servers[i].smb2_tree[j]->session->transport); } } } @@ -854,6 +1206,11 @@ static bool check_oplocks(const char *call) int i, j; int tries = 0; + if (!options.use_oplocks || options.smb2) { + /* no smb2 oplocks in gentest yet */ + return true; + } + again: check_pending(); @@ -870,6 +1227,7 @@ again: oplocks[i][j].got_break, oplocks[i][j].handle, oplocks[i][j].level); + current_op.mismatch = "oplock break"; return false; } } @@ -881,9 +1239,9 @@ again: oplocks[0][j].do_close) { uint16_t fnums[NSERVERS]; for (i=0;i<NSERVERS;i++) { - fnums[i] = oplocks[i][j].fnum; + fnums[i] = oplocks[i][j].smb_handle; } - gen_remove_handle(j, fnums); + gen_remove_handle_smb(j, fnums); break; } } @@ -899,6 +1257,11 @@ static bool check_notifies(const char *call) int i, j; int tries = 0; + if (options.smb2) { + /* no smb2 notifies in gentest yet */ + return true; + } + again: check_pending(); @@ -912,6 +1275,7 @@ again: printf("Notify count inconsistent %d %d\n", notifies[0][j].notify_count, notifies[i][j].notify_count); + current_op.mismatch = "notify count"; return false; } @@ -922,6 +1286,7 @@ again: printf("Notify status mismatch - %s - %s\n", nt_errstr(notifies[0][j].status), nt_errstr(notifies[i][j].status)); + current_op.mismatch = "Notify status"; return false; } @@ -938,6 +1303,7 @@ again: printf("Notify action %d inconsistent %d %d\n", n, not1.nttrans.out.changes[n].action, not2.nttrans.out.changes[n].action); + current_op.mismatch = "notify action"; return false; } if (strcmp(not1.nttrans.out.changes[n].name.s, @@ -945,6 +1311,7 @@ again: printf("Notify name %d inconsistent %s %s\n", n, not1.nttrans.out.changes[n].name.s, not2.nttrans.out.changes[n].name.s); + current_op.mismatch = "notify name"; return false; } if (not1.nttrans.out.changes[n].name.private_length != @@ -952,6 +1319,7 @@ again: printf("Notify name length %d inconsistent %d %d\n", n, not1.nttrans.out.changes[n].name.private_length, not2.nttrans.out.changes[n].name.private_length); + current_op.mismatch = "notify name length"; return false; } } @@ -963,7 +1331,6 @@ again: return true; } - #define GEN_COPY_PARM do { \ int i; \ for (i=1;i<NSERVERS;i++) { \ @@ -971,12 +1338,12 @@ again: } \ } while (0) -#define GEN_CALL(call) do { \ +#define GEN_CALL(call, treetype, treefield) do { \ int i; \ ZERO_STRUCT(oplocks); \ ZERO_STRUCT(notifies); \ for (i=0;i<NSERVERS;i++) { \ - struct smbcli_tree *tree = servers[i].cli[instance]->tree; \ + struct treetype *tree = servers[i].treefield[instance]; \ status[i] = call; \ } \ current_op.status = status[0]; \ @@ -984,43 +1351,92 @@ again: if (!compare_status(status[i], status[0])) { \ printf("status different in %s - %s %s\n", #call, \ nt_errstr(status[0]), nt_errstr(status[i])); \ + current_op.mismatch = nt_errstr(status[0]); \ return false; \ } \ } \ - if (!check_oplocks(#call)) return false; \ - if (!check_notifies(#call)) return false; \ + if (!check_oplocks(#call)) return false; \ + if (!check_notifies(#call)) return false; \ if (!NT_STATUS_IS_OK(status[0])) { \ return true; \ } \ } while(0) -#define ADD_HANDLE(name, field) do { \ - uint16_t fnums[NSERVERS]; \ +#define GEN_CALL_SMB(call) GEN_CALL(call, smbcli_tree, smb_tree) +#define GEN_CALL_SMB2(call) GEN_CALL(call, smb2_tree, smb2_tree) + +#define ADD_HANDLE_SMB2(name, field) do { \ + struct smb2_handle handles[NSERVERS]; \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + handles[i] = parm[i].field; \ + } \ + gen_add_handle_smb2(instance, name, handles); \ +} while(0) + +#define REMOVE_HANDLE_SMB2(field) do { \ + struct smb2_handle handles[NSERVERS]; \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + handles[i] = parm[i].field; \ + } \ + gen_remove_handle_smb2(instance, handles); \ +} while(0) + +#define ADD_HANDLE_SMB(name, field) do { \ + uint16_t handles[NSERVERS]; \ int i; \ for (i=0;i<NSERVERS;i++) { \ - fnums[i] = parm[i].field; \ + handles[i] = parm[i].field; \ } \ - gen_add_handle(instance, name, fnums); \ + gen_add_handle_smb(instance, name, handles); \ } while(0) -#define REMOVE_HANDLE(field) do { \ - uint16_t fnums[NSERVERS]; \ +#define REMOVE_HANDLE_SMB(field) do { \ + uint16_t handles[NSERVERS]; \ int i; \ for (i=0;i<NSERVERS;i++) { \ - fnums[i] = parm[i].field; \ + handles[i] = parm[i].field; \ } \ - gen_remove_handle(instance, fnums); \ + gen_remove_handle_smb(instance, handles); \ } while(0) -#define GEN_SET_FNUM(field) do { \ +#define GEN_SET_FNUM_SMB2(field) do { \ int i; \ for (i=0;i<NSERVERS;i++) { \ - parm[i].field = gen_lookup_fnum(i, parm[i].field); \ + parm[i].field = gen_lookup_handle_smb2(i, parm[i].field.data[0]); \ + } \ +} while(0) + +#define GEN_SET_FNUM_SMB(field) do { \ + int i; \ + for (i=0;i<NSERVERS;i++) { \ + parm[i].field = gen_lookup_handle_smb(i, parm[i].field); \ } \ } while(0) #define CHECK_EQUAL(field) do { \ if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s - 0x%llx 0x%llx\n", #field, \ + (unsigned long long)parm[0].field, (unsigned long long)parm[1].field); \ + return false; \ + } \ +} while(0) + +#define CHECK_SECDESC(field) do { \ + if (!security_acl_equal(parm[0].field->dacl, parm[1].field->dacl) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ + printf("Mismatch in %s\n", #field); \ + return false; \ + } \ +} while(0) + +#define CHECK_ATTRIB(field) do { \ + if (!options.mask_indexing) { \ + CHECK_EQUAL(field); \ + } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)parm[0].field, (int)parm[1].field); \ return false; \ @@ -1029,10 +1445,12 @@ again: #define CHECK_WSTR_EQUAL(field) do { \ if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ + current_op.mismatch = #field; \ printf("%s is NULL!\n", #field); \ return false; \ } \ if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - %s %s\n", #field, \ parm[0].field.s, parm[1].field.s); \ return false; \ @@ -1042,6 +1460,7 @@ again: #define CHECK_BLOB_EQUAL(field) do { \ if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s\n", #field); \ return false; \ } \ @@ -1051,6 +1470,7 @@ again: #define CHECK_TIMES_EQUAL(field) do { \ if (labs(parm[0].field - parm[1].field) > time_skew() && \ !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)parm[0].field, (int)parm[1].field); \ return false; \ @@ -1061,6 +1481,7 @@ again: if (labs(nt_time_to_unix(parm[0].field) - \ nt_time_to_unix(parm[1].field)) > time_skew() && \ !ignore_pattern(#field)) { \ + current_op.mismatch = #field; \ printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ (int)nt_time_to_unix(parm[0].field), \ (int)nt_time_to_unix(parm[1].field)); \ @@ -1068,10 +1489,226 @@ again: } \ } while(0) + +/* + compare returned fileinfo structures +*/ +static bool cmp_fileinfo(int instance, + union smb_fileinfo parm[NSERVERS], + NTSTATUS status[NSERVERS]) +{ + int i; + enum smb_fileinfo_level level = parm[0].generic.level; + + if (level == RAW_FILEINFO_ALL_INFORMATION && + options.smb2) { + level = RAW_FILEINFO_SMB2_ALL_INFORMATION; + } + + switch (level) { + case RAW_FILEINFO_GENERIC: + return false; + + case RAW_FILEINFO_GETATTR: + CHECK_ATTRIB(getattr.out.attrib); + CHECK_EQUAL(getattr.out.size); + CHECK_TIMES_EQUAL(getattr.out.write_time); + break; + + case RAW_FILEINFO_GETATTRE: + CHECK_TIMES_EQUAL(getattre.out.create_time); + CHECK_TIMES_EQUAL(getattre.out.access_time); + CHECK_TIMES_EQUAL(getattre.out.write_time); + CHECK_EQUAL(getattre.out.size); + CHECK_EQUAL(getattre.out.alloc_size); + CHECK_ATTRIB(getattre.out.attrib); + break; + + case RAW_FILEINFO_STANDARD: + CHECK_TIMES_EQUAL(standard.out.create_time); + CHECK_TIMES_EQUAL(standard.out.access_time); + CHECK_TIMES_EQUAL(standard.out.write_time); + CHECK_EQUAL(standard.out.size); + CHECK_EQUAL(standard.out.alloc_size); + CHECK_ATTRIB(standard.out.attrib); + break; + + case RAW_FILEINFO_EA_SIZE: + CHECK_TIMES_EQUAL(ea_size.out.create_time); + CHECK_TIMES_EQUAL(ea_size.out.access_time); + CHECK_TIMES_EQUAL(ea_size.out.write_time); + CHECK_EQUAL(ea_size.out.size); + CHECK_EQUAL(ea_size.out.alloc_size); + CHECK_ATTRIB(ea_size.out.attrib); + CHECK_EQUAL(ea_size.out.ea_size); + break; + + case RAW_FILEINFO_ALL_EAS: + CHECK_EQUAL(all_eas.out.num_eas); + for (i=0;i<parm[0].all_eas.out.num_eas;i++) { + CHECK_EQUAL(all_eas.out.eas[i].flags); + CHECK_WSTR_EQUAL(all_eas.out.eas[i].name); + CHECK_BLOB_EQUAL(all_eas.out.eas[i].value); + } + break; + + case RAW_FILEINFO_IS_NAME_VALID: + break; + + case RAW_FILEINFO_BASIC_INFO: + case RAW_FILEINFO_BASIC_INFORMATION: + CHECK_NTTIMES_EQUAL(basic_info.out.create_time); + CHECK_NTTIMES_EQUAL(basic_info.out.access_time); + CHECK_NTTIMES_EQUAL(basic_info.out.write_time); + CHECK_NTTIMES_EQUAL(basic_info.out.change_time); + CHECK_ATTRIB(basic_info.out.attrib); + break; + + case RAW_FILEINFO_STANDARD_INFO: + case RAW_FILEINFO_STANDARD_INFORMATION: + CHECK_EQUAL(standard_info.out.alloc_size); + CHECK_EQUAL(standard_info.out.size); + CHECK_EQUAL(standard_info.out.nlink); + CHECK_EQUAL(standard_info.out.delete_pending); + CHECK_EQUAL(standard_info.out.directory); + break; + + case RAW_FILEINFO_EA_INFO: + case RAW_FILEINFO_EA_INFORMATION: + CHECK_EQUAL(ea_info.out.ea_size); + break; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + CHECK_WSTR_EQUAL(name_info.out.fname); + break; + + case RAW_FILEINFO_ALL_INFO: + case RAW_FILEINFO_ALL_INFORMATION: + CHECK_NTTIMES_EQUAL(all_info.out.create_time); + CHECK_NTTIMES_EQUAL(all_info.out.access_time); + CHECK_NTTIMES_EQUAL(all_info.out.write_time); + CHECK_NTTIMES_EQUAL(all_info.out.change_time); + CHECK_ATTRIB(all_info.out.attrib); + CHECK_EQUAL(all_info.out.alloc_size); + CHECK_EQUAL(all_info.out.size); + CHECK_EQUAL(all_info.out.nlink); + CHECK_EQUAL(all_info.out.delete_pending); + CHECK_EQUAL(all_info.out.directory); + CHECK_EQUAL(all_info.out.ea_size); + CHECK_WSTR_EQUAL(all_info.out.fname); + break; + + case RAW_FILEINFO_ALT_NAME_INFO: + case RAW_FILEINFO_ALT_NAME_INFORMATION: + CHECK_WSTR_EQUAL(alt_name_info.out.fname); + break; + + case RAW_FILEINFO_STREAM_INFO: + case RAW_FILEINFO_STREAM_INFORMATION: + CHECK_EQUAL(stream_info.out.num_streams); + for (i=0;i<parm[0].stream_info.out.num_streams;i++) { + CHECK_EQUAL(stream_info.out.streams[i].size); + CHECK_EQUAL(stream_info.out.streams[i].alloc_size); + CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name); + } + break; + + case RAW_FILEINFO_COMPRESSION_INFO: + case RAW_FILEINFO_COMPRESSION_INFORMATION: + CHECK_EQUAL(compression_info.out.compressed_size); + CHECK_EQUAL(compression_info.out.format); + CHECK_EQUAL(compression_info.out.unit_shift); + CHECK_EQUAL(compression_info.out.chunk_shift); + CHECK_EQUAL(compression_info.out.cluster_shift); + break; + + case RAW_FILEINFO_INTERNAL_INFORMATION: + CHECK_EQUAL(internal_information.out.file_id); + break; + + case RAW_FILEINFO_ACCESS_INFORMATION: + CHECK_EQUAL(access_information.out.access_flags); + break; + + case RAW_FILEINFO_POSITION_INFORMATION: + CHECK_EQUAL(position_information.out.position); + break; + + case RAW_FILEINFO_MODE_INFORMATION: + CHECK_EQUAL(mode_information.out.mode); + break; + + case RAW_FILEINFO_ALIGNMENT_INFORMATION: + CHECK_EQUAL(alignment_information.out.alignment_requirement); + break; + + case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: + CHECK_NTTIMES_EQUAL(network_open_information.out.create_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.access_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.write_time); + CHECK_NTTIMES_EQUAL(network_open_information.out.change_time); + CHECK_EQUAL(network_open_information.out.alloc_size); + CHECK_EQUAL(network_open_information.out.size); + CHECK_ATTRIB(network_open_information.out.attrib); + break; + + case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: + CHECK_ATTRIB(attribute_tag_information.out.attrib); + CHECK_EQUAL(attribute_tag_information.out.reparse_tag); + break; + + case RAW_FILEINFO_SMB2_ALL_INFORMATION: + CHECK_NTTIMES_EQUAL(all_info2.out.create_time); + CHECK_NTTIMES_EQUAL(all_info2.out.access_time); + CHECK_NTTIMES_EQUAL(all_info2.out.write_time); + CHECK_NTTIMES_EQUAL(all_info2.out.change_time); + CHECK_ATTRIB(all_info2.out.attrib); + CHECK_EQUAL(all_info2.out.unknown1); + CHECK_EQUAL(all_info2.out.alloc_size); + CHECK_EQUAL(all_info2.out.size); + CHECK_EQUAL(all_info2.out.nlink); + CHECK_EQUAL(all_info2.out.delete_pending); + CHECK_EQUAL(all_info2.out.directory); + CHECK_EQUAL(all_info2.out.file_id); + CHECK_EQUAL(all_info2.out.ea_size); + CHECK_EQUAL(all_info2.out.access_mask); + CHECK_EQUAL(all_info2.out.position); + CHECK_EQUAL(all_info2.out.mode); + CHECK_EQUAL(all_info2.out.alignment_requirement); + CHECK_WSTR_EQUAL(all_info2.out.fname); + break; + + case RAW_FILEINFO_SMB2_ALL_EAS: + CHECK_EQUAL(all_eas.out.num_eas); + for (i=0;i<parm[0].all_eas.out.num_eas;i++) { + CHECK_EQUAL(all_eas.out.eas[i].flags); + CHECK_WSTR_EQUAL(all_eas.out.eas[i].name); + CHECK_BLOB_EQUAL(all_eas.out.eas[i].value); + } + break; + + case RAW_FILEINFO_SEC_DESC: + CHECK_SECDESC(query_secdesc.out.sd); + break; + + /* Unhandled levels */ + case RAW_FILEINFO_EA_LIST: + case RAW_FILEINFO_UNIX_BASIC: + case RAW_FILEINFO_UNIX_LINK: + case RAW_FILEINFO_UNIX_INFO2: + break; + } + + return true; +} + + + /* generate openx operations */ -static bool handler_openx(int instance) +static bool handler_smb_openx(int instance) { union smb_open parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1094,9 +1731,9 @@ static bool handler_openx(int instance) } GEN_COPY_PARM; - GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); - CHECK_EQUAL(openx.out.attrib); + CHECK_ATTRIB(openx.out.attrib); CHECK_EQUAL(openx.out.size); CHECK_EQUAL(openx.out.access); CHECK_EQUAL(openx.out.ftype); @@ -1107,7 +1744,7 @@ static bool handler_openx(int instance) CHECK_TIMES_EQUAL(openx.out.write_time); /* open creates a new file handle */ - ADD_HANDLE(parm[0].openx.in.fname, openx.out.file.fnum); + ADD_HANDLE_SMB(parm[0].openx.in.fname, openx.out.file.fnum); return true; } @@ -1116,7 +1753,7 @@ static bool handler_openx(int instance) /* generate open operations */ -static bool handler_open(int instance) +static bool handler_smb_open(int instance) { union smb_open parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1133,15 +1770,15 @@ static bool handler_open(int instance) } GEN_COPY_PARM; - GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); - CHECK_EQUAL(openold.out.attrib); + CHECK_ATTRIB(openold.out.attrib); CHECK_TIMES_EQUAL(openold.out.write_time); CHECK_EQUAL(openold.out.size); CHECK_EQUAL(openold.out.rmode); /* open creates a new file handle */ - ADD_HANDLE(parm[0].openold.in.fname, openold.out.file.fnum); + ADD_HANDLE_SMB(parm[0].openold.in.fname, openold.out.file.fnum); return true; } @@ -1150,7 +1787,7 @@ static bool handler_open(int instance) /* generate ntcreatex operations */ -static bool handler_ntcreatex(int instance) +static bool handler_smb_ntcreatex(int instance) { union smb_open parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1176,9 +1813,9 @@ static bool handler_ntcreatex(int instance) GEN_COPY_PARM; if (parm[0].ntcreatex.in.root_fid != 0) { - GEN_SET_FNUM(ntcreatex.in.root_fid); + GEN_SET_FNUM_SMB(ntcreatex.in.root_fid); } - GEN_CALL(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); + GEN_CALL_SMB(smb_raw_open(tree, current_op.mem_ctx, &parm[i])); CHECK_EQUAL(ntcreatex.out.oplock_level); CHECK_EQUAL(ntcreatex.out.create_action); @@ -1186,7 +1823,7 @@ static bool handler_ntcreatex(int instance) CHECK_NTTIMES_EQUAL(ntcreatex.out.access_time); CHECK_NTTIMES_EQUAL(ntcreatex.out.write_time); CHECK_NTTIMES_EQUAL(ntcreatex.out.change_time); - CHECK_EQUAL(ntcreatex.out.attrib); + CHECK_ATTRIB(ntcreatex.out.attrib); CHECK_EQUAL(ntcreatex.out.alloc_size); CHECK_EQUAL(ntcreatex.out.size); CHECK_EQUAL(ntcreatex.out.file_type); @@ -1194,7 +1831,7 @@ static bool handler_ntcreatex(int instance) CHECK_EQUAL(ntcreatex.out.is_directory); /* ntcreatex creates a new file handle */ - ADD_HANDLE(parm[0].ntcreatex.in.fname, ntcreatex.out.file.fnum); + ADD_HANDLE_SMB(parm[0].ntcreatex.in.fname, ntcreatex.out.file.fnum); return true; } @@ -1202,7 +1839,7 @@ static bool handler_ntcreatex(int instance) /* generate close operations */ -static bool handler_close(int instance) +static bool handler_smb_close(int instance) { union smb_close parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1212,10 +1849,10 @@ static bool handler_close(int instance) parm[0].close.in.write_time = gen_timet(); GEN_COPY_PARM; - GEN_SET_FNUM(close.in.file.fnum); - GEN_CALL(smb_raw_close(tree, &parm[i])); + GEN_SET_FNUM_SMB(close.in.file.fnum); + GEN_CALL_SMB(smb_raw_close(tree, &parm[i])); - REMOVE_HANDLE(close.in.file.fnum); + REMOVE_HANDLE_SMB(close.in.file.fnum); return true; } @@ -1223,7 +1860,7 @@ static bool handler_close(int instance) /* generate unlink operations */ -static bool handler_unlink(int instance) +static bool handler_smb_unlink(int instance) { union smb_unlink parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1232,7 +1869,7 @@ static bool handler_unlink(int instance) parm[0].unlink.in.attrib = gen_attrib(); GEN_COPY_PARM; - GEN_CALL(smb_raw_unlink(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_unlink(tree, &parm[i])); return true; } @@ -1240,7 +1877,7 @@ static bool handler_unlink(int instance) /* generate chkpath operations */ -static bool handler_chkpath(int instance) +static bool handler_smb_chkpath(int instance) { union smb_chkpath parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1248,7 +1885,7 @@ static bool handler_chkpath(int instance) parm[0].chkpath.in.path = gen_fname_open(instance); GEN_COPY_PARM; - GEN_CALL(smb_raw_chkpath(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_chkpath(tree, &parm[i])); return true; } @@ -1256,7 +1893,7 @@ static bool handler_chkpath(int instance) /* generate mkdir operations */ -static bool handler_mkdir(int instance) +static bool handler_smb_mkdir(int instance) { union smb_mkdir parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1265,7 +1902,7 @@ static bool handler_mkdir(int instance) parm[0].mkdir.in.path = gen_fname_open(instance); GEN_COPY_PARM; - GEN_CALL(smb_raw_mkdir(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_mkdir(tree, &parm[i])); return true; } @@ -1273,7 +1910,7 @@ static bool handler_mkdir(int instance) /* generate rmdir operations */ -static bool handler_rmdir(int instance) +static bool handler_smb_rmdir(int instance) { struct smb_rmdir parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1281,7 +1918,7 @@ static bool handler_rmdir(int instance) parm[0].in.path = gen_fname_open(instance); GEN_COPY_PARM; - GEN_CALL(smb_raw_rmdir(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_rmdir(tree, &parm[i])); return true; } @@ -1289,7 +1926,7 @@ static bool handler_rmdir(int instance) /* generate rename operations */ -static bool handler_rename(int instance) +static bool handler_smb_rename(int instance) { union smb_rename parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1300,7 +1937,7 @@ static bool handler_rename(int instance) parm[0].rename.in.attrib = gen_attrib(); GEN_COPY_PARM; - GEN_CALL(smb_raw_rename(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_rename(tree, &parm[i])); return true; } @@ -1308,7 +1945,7 @@ static bool handler_rename(int instance) /* generate ntrename operations */ -static bool handler_ntrename(int instance) +static bool handler_smb_ntrename(int instance) { union smb_rename parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1321,7 +1958,7 @@ static bool handler_ntrename(int instance) parm[0].ntrename.in.flags = gen_rename_flags(); GEN_COPY_PARM; - GEN_CALL(smb_raw_rename(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_rename(tree, &parm[i])); return true; } @@ -1330,7 +1967,7 @@ static bool handler_ntrename(int instance) /* generate seek operations */ -static bool handler_seek(int instance) +static bool handler_smb_seek(int instance) { union smb_seek parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1340,8 +1977,8 @@ static bool handler_seek(int instance) parm[0].lseek.in.offset = gen_offset(); GEN_COPY_PARM; - GEN_SET_FNUM(lseek.in.file.fnum); - GEN_CALL(smb_raw_seek(tree, &parm[i])); + GEN_SET_FNUM_SMB(lseek.in.file.fnum); + GEN_CALL_SMB(smb_raw_seek(tree, &parm[i])); CHECK_EQUAL(lseek.out.offset); @@ -1352,7 +1989,7 @@ static bool handler_seek(int instance) /* generate readx operations */ -static bool handler_readx(int instance) +static bool handler_smb_readx(int instance) { union smb_read parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1368,8 +2005,8 @@ static bool handler_readx(int instance) MAX(parm[0].readx.in.mincnt, parm[0].readx.in.maxcnt)); GEN_COPY_PARM; - GEN_SET_FNUM(readx.in.file.fnum); - GEN_CALL(smb_raw_read(tree, &parm[i])); + GEN_SET_FNUM_SMB(readx.in.file.fnum); + GEN_CALL_SMB(smb_raw_read(tree, &parm[i])); CHECK_EQUAL(readx.out.remaining); CHECK_EQUAL(readx.out.compaction_mode); @@ -1381,7 +2018,7 @@ static bool handler_readx(int instance) /* generate writex operations */ -static bool handler_writex(int instance) +static bool handler_smb_writex(int instance) { union smb_write parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1395,8 +2032,8 @@ static bool handler_writex(int instance) parm[0].writex.in.data = talloc_zero_array(current_op.mem_ctx, uint8_t, parm[0].writex.in.count); GEN_COPY_PARM; - GEN_SET_FNUM(writex.in.file.fnum); - GEN_CALL(smb_raw_write(tree, &parm[i])); + GEN_SET_FNUM_SMB(writex.in.file.fnum); + GEN_CALL_SMB(smb_raw_write(tree, &parm[i])); CHECK_EQUAL(writex.out.nwritten); CHECK_EQUAL(writex.out.remaining); @@ -1407,7 +2044,7 @@ static bool handler_writex(int instance) /* generate lockingx operations */ -static bool handler_lockingx(int instance) +static bool handler_smb_lockingx(int instance) { union smb_lock parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1437,255 +2074,113 @@ static bool handler_lockingx(int instance) } GEN_COPY_PARM; - GEN_SET_FNUM(lockx.in.file.fnum); - GEN_CALL(smb_raw_lock(tree, &parm[i])); + GEN_SET_FNUM_SMB(lockx.in.file.fnum); + GEN_CALL_SMB(smb_raw_lock(tree, &parm[i])); return true; } +#if 0 /* generate a fileinfo query structure */ -static void gen_fileinfo(int instance, union smb_fileinfo *info) +static void gen_setfileinfo(int instance, union smb_setfileinfo *info) { int i; - #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + #undef LVL + #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} struct { - enum smb_fileinfo_level level; + enum smb_setfileinfo_level level; const char *name; } levels[] = { - LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD), - LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID), - LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO), - LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO), - LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION), - LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), - LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), - LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION), - LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), - LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION) +#if 0 + /* disabled until win2003 can handle them ... */ + LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), + LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), +#endif + LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) }; do { i = gen_int_range(0, ARRAY_SIZE(levels)-1); } while (ignore_pattern(levels[i].name)); info->generic.level = levels[i].level; -} -/* - compare returned fileinfo structures -*/ -static bool cmp_fileinfo(int instance, - union smb_fileinfo parm[NSERVERS], - NTSTATUS status[NSERVERS]) -{ - int i; - - switch (parm[0].generic.level) { - case RAW_FILEINFO_GENERIC: - return false; - - case RAW_FILEINFO_GETATTR: - CHECK_EQUAL(getattr.out.attrib); - CHECK_EQUAL(getattr.out.size); - CHECK_TIMES_EQUAL(getattr.out.write_time); - break; - - case RAW_FILEINFO_GETATTRE: - CHECK_TIMES_EQUAL(getattre.out.create_time); - CHECK_TIMES_EQUAL(getattre.out.access_time); - CHECK_TIMES_EQUAL(getattre.out.write_time); - CHECK_EQUAL(getattre.out.size); - CHECK_EQUAL(getattre.out.alloc_size); - CHECK_EQUAL(getattre.out.attrib); - break; - - case RAW_FILEINFO_STANDARD: - CHECK_TIMES_EQUAL(standard.out.create_time); - CHECK_TIMES_EQUAL(standard.out.access_time); - CHECK_TIMES_EQUAL(standard.out.write_time); - CHECK_EQUAL(standard.out.size); - CHECK_EQUAL(standard.out.alloc_size); - CHECK_EQUAL(standard.out.attrib); - break; - - case RAW_FILEINFO_EA_SIZE: - CHECK_TIMES_EQUAL(ea_size.out.create_time); - CHECK_TIMES_EQUAL(ea_size.out.access_time); - CHECK_TIMES_EQUAL(ea_size.out.write_time); - CHECK_EQUAL(ea_size.out.size); - CHECK_EQUAL(ea_size.out.alloc_size); - CHECK_EQUAL(ea_size.out.attrib); - CHECK_EQUAL(ea_size.out.ea_size); - break; - - case RAW_FILEINFO_ALL_EAS: - CHECK_EQUAL(all_eas.out.num_eas); - for (i=0;i<parm[0].all_eas.out.num_eas;i++) { - CHECK_EQUAL(all_eas.out.eas[i].flags); - CHECK_WSTR_EQUAL(all_eas.out.eas[i].name); - CHECK_BLOB_EQUAL(all_eas.out.eas[i].value); - } - break; - - case RAW_FILEINFO_IS_NAME_VALID: - break; - - case RAW_FILEINFO_BASIC_INFO: - case RAW_FILEINFO_BASIC_INFORMATION: - CHECK_NTTIMES_EQUAL(basic_info.out.create_time); - CHECK_NTTIMES_EQUAL(basic_info.out.access_time); - CHECK_NTTIMES_EQUAL(basic_info.out.write_time); - CHECK_NTTIMES_EQUAL(basic_info.out.change_time); - CHECK_EQUAL(basic_info.out.attrib); - break; - - case RAW_FILEINFO_STANDARD_INFO: - case RAW_FILEINFO_STANDARD_INFORMATION: - CHECK_EQUAL(standard_info.out.alloc_size); - CHECK_EQUAL(standard_info.out.size); - CHECK_EQUAL(standard_info.out.nlink); - CHECK_EQUAL(standard_info.out.delete_pending); - CHECK_EQUAL(standard_info.out.directory); - break; - - case RAW_FILEINFO_EA_INFO: - case RAW_FILEINFO_EA_INFORMATION: - CHECK_EQUAL(ea_info.out.ea_size); - break; - - case RAW_FILEINFO_NAME_INFO: - case RAW_FILEINFO_NAME_INFORMATION: - CHECK_WSTR_EQUAL(name_info.out.fname); - break; - - case RAW_FILEINFO_ALL_INFO: - case RAW_FILEINFO_ALL_INFORMATION: - CHECK_NTTIMES_EQUAL(all_info.out.create_time); - CHECK_NTTIMES_EQUAL(all_info.out.access_time); - CHECK_NTTIMES_EQUAL(all_info.out.write_time); - CHECK_NTTIMES_EQUAL(all_info.out.change_time); - CHECK_EQUAL(all_info.out.attrib); - CHECK_EQUAL(all_info.out.alloc_size); - CHECK_EQUAL(all_info.out.size); - CHECK_EQUAL(all_info.out.nlink); - CHECK_EQUAL(all_info.out.delete_pending); - CHECK_EQUAL(all_info.out.directory); - CHECK_EQUAL(all_info.out.ea_size); - CHECK_WSTR_EQUAL(all_info.out.fname); + switch (info->generic.level) { + case RAW_SFILEINFO_SETATTR: + info->setattr.in.attrib = gen_attrib(); + info->setattr.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_ALT_NAME_INFO: - case RAW_FILEINFO_ALT_NAME_INFORMATION: - CHECK_WSTR_EQUAL(alt_name_info.out.fname); + case RAW_SFILEINFO_SETATTRE: + info->setattre.in.create_time = gen_timet(); + info->setattre.in.access_time = gen_timet(); + info->setattre.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_STREAM_INFO: - case RAW_FILEINFO_STREAM_INFORMATION: - CHECK_EQUAL(stream_info.out.num_streams); - for (i=0;i<parm[0].stream_info.out.num_streams;i++) { - CHECK_EQUAL(stream_info.out.streams[i].size); - CHECK_EQUAL(stream_info.out.streams[i].alloc_size); - CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name); - } + case RAW_SFILEINFO_STANDARD: + info->standard.in.create_time = gen_timet(); + info->standard.in.access_time = gen_timet(); + info->standard.in.write_time = gen_timet(); break; - - case RAW_FILEINFO_COMPRESSION_INFO: - case RAW_FILEINFO_COMPRESSION_INFORMATION: - CHECK_EQUAL(compression_info.out.compressed_size); - CHECK_EQUAL(compression_info.out.format); - CHECK_EQUAL(compression_info.out.unit_shift); - CHECK_EQUAL(compression_info.out.chunk_shift); - CHECK_EQUAL(compression_info.out.cluster_shift); + case RAW_SFILEINFO_EA_SET: { + static struct ea_struct ea; + info->ea_set.in.num_eas = 1; + info->ea_set.in.eas = &ea; + info->ea_set.in.eas[0] = gen_ea_struct(); + } break; - - case RAW_FILEINFO_INTERNAL_INFORMATION: - CHECK_EQUAL(internal_information.out.file_id); + case RAW_SFILEINFO_BASIC_INFO: + case RAW_SFILEINFO_BASIC_INFORMATION: + info->basic_info.in.create_time = gen_nttime(); + info->basic_info.in.access_time = gen_nttime(); + info->basic_info.in.write_time = gen_nttime(); + info->basic_info.in.change_time = gen_nttime(); + info->basic_info.in.attrib = gen_attrib(); break; - - case RAW_FILEINFO_ACCESS_INFORMATION: - CHECK_EQUAL(access_information.out.access_flags); + case RAW_SFILEINFO_DISPOSITION_INFO: + case RAW_SFILEINFO_DISPOSITION_INFORMATION: + info->disposition_info.in.delete_on_close = gen_bool(); break; - - case RAW_FILEINFO_POSITION_INFORMATION: - CHECK_EQUAL(position_information.out.position); + case RAW_SFILEINFO_ALLOCATION_INFO: + case RAW_SFILEINFO_ALLOCATION_INFORMATION: + info->allocation_info.in.alloc_size = gen_alloc_size(); break; - - case RAW_FILEINFO_MODE_INFORMATION: - CHECK_EQUAL(mode_information.out.mode); + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + info->end_of_file_info.in.size = gen_offset(); break; - - case RAW_FILEINFO_ALIGNMENT_INFORMATION: - CHECK_EQUAL(alignment_information.out.alignment_requirement); + case RAW_SFILEINFO_RENAME_INFORMATION: + case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: + info->rename_information.in.overwrite = gen_bool(); + info->rename_information.in.root_fid = gen_root_fid(instance); + info->rename_information.in.new_name = gen_fname_open(instance); break; - - case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: - CHECK_NTTIMES_EQUAL(network_open_information.out.create_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.access_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.write_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.change_time); - CHECK_EQUAL(network_open_information.out.alloc_size); - CHECK_EQUAL(network_open_information.out.size); - CHECK_EQUAL(network_open_information.out.attrib); + case RAW_SFILEINFO_POSITION_INFORMATION: + info->position_information.in.position = gen_offset(); break; - - case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: - CHECK_EQUAL(attribute_tag_information.out.attrib); - CHECK_EQUAL(attribute_tag_information.out.reparse_tag); + case RAW_SFILEINFO_MODE_INFORMATION: + info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); break; - - /* Unhandled levels */ - - case RAW_FILEINFO_SEC_DESC: - case RAW_FILEINFO_EA_LIST: - case RAW_FILEINFO_UNIX_BASIC: - case RAW_FILEINFO_UNIX_LINK: - case RAW_FILEINFO_SMB2_ALL_EAS: - case RAW_FILEINFO_SMB2_ALL_INFORMATION: - case RAW_FILEINFO_UNIX_INFO2: + case RAW_SFILEINFO_GENERIC: + case RAW_SFILEINFO_SEC_DESC: + case RAW_SFILEINFO_UNIX_BASIC: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: + case RAW_SFILEINFO_1023: + case RAW_SFILEINFO_1025: + case RAW_SFILEINFO_1029: + case RAW_SFILEINFO_1032: + case RAW_SFILEINFO_1039: + case RAW_SFILEINFO_1040: + case RAW_SFILEINFO_UNIX_INFO2: + /* Untested */ break; } - - return true; -} - -/* - generate qpathinfo operations -*/ -static bool handler_qpathinfo(int instance) -{ - union smb_fileinfo parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].generic.in.file.path = gen_fname_open(instance); - - gen_fileinfo(instance, &parm[0]); - - GEN_COPY_PARM; - GEN_CALL(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i])); - - return cmp_fileinfo(instance, parm, status); -} - -/* - generate qfileinfo operations -*/ -static bool handler_qfileinfo(int instance) -{ - union smb_fileinfo parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].generic.in.file.fnum = gen_fnum(instance); - - gen_fileinfo(instance, &parm[0]); - - GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.fnum); - GEN_CALL(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i])); - - return cmp_fileinfo(instance, parm, status); } - +#endif /* generate a fileinfo query structure @@ -1695,25 +2190,33 @@ static void gen_setfileinfo(int instance, union smb_setfileinfo *info) int i; #undef LVL #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} - struct { + struct levels { enum smb_setfileinfo_level level; const char *name; - } levels[] = { -#if 0 - /* disabled until win2003 can handle them ... */ + }; + struct levels smb_levels[] = { LVL(EA_SET), LVL(BASIC_INFO), LVL(DISPOSITION_INFO), LVL(STANDARD), LVL(ALLOCATION_INFO), LVL(END_OF_FILE_INFO), -#endif LVL(SETATTR), LVL(SETATTRE), LVL(BASIC_INFORMATION), LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), + LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040), + }; + struct levels smb2_levels[] = { + LVL(BASIC_INFORMATION), + LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), + LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), + LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) }; + struct levels *levels = options.smb2?smb2_levels:smb_levels; + uint32_t num_levels = options.smb2?ARRAY_SIZE(smb2_levels):ARRAY_SIZE(smb_levels); + do { - i = gen_int_range(0, ARRAY_SIZE(levels)-1); + i = gen_int_range(0, num_levels-1); } while (ignore_pattern(levels[i].name)); - + info->generic.level = levels[i].level; switch (info->generic.level) { @@ -1736,8 +2239,8 @@ static void gen_setfileinfo(int instance, union smb_setfileinfo *info) info->ea_set.in.num_eas = 1; info->ea_set.in.eas = &ea; info->ea_set.in.eas[0] = gen_ea_struct(); - } break; + } case RAW_SFILEINFO_BASIC_INFO: case RAW_SFILEINFO_BASIC_INFORMATION: info->basic_info.in.create_time = gen_nttime(); @@ -1770,27 +2273,98 @@ static void gen_setfileinfo(int instance, union smb_setfileinfo *info) case RAW_SFILEINFO_MODE_INFORMATION: info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); break; + case RAW_SFILEINFO_GENERIC: case RAW_SFILEINFO_SEC_DESC: - case RAW_SFILEINFO_UNIX_BASIC: - case RAW_SFILEINFO_UNIX_LINK: - case RAW_SFILEINFO_UNIX_HLINK: case RAW_SFILEINFO_1023: case RAW_SFILEINFO_1025: case RAW_SFILEINFO_1029: case RAW_SFILEINFO_1032: case RAW_SFILEINFO_1039: case RAW_SFILEINFO_1040: + case RAW_SFILEINFO_UNIX_BASIC: case RAW_SFILEINFO_UNIX_INFO2: + case RAW_SFILEINFO_UNIX_LINK: + case RAW_SFILEINFO_UNIX_HLINK: /* Untested */ break; } } + + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo_smb(int instance, union smb_fileinfo *info) +{ + int i; + #undef LVL + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(GETATTR), LVL(GETATTRE), LVL(STANDARD), + LVL(EA_SIZE), LVL(ALL_EAS), LVL(IS_NAME_VALID), + LVL(BASIC_INFO), LVL(STANDARD_INFO), LVL(EA_INFO), + LVL(NAME_INFO), LVL(ALL_INFO), LVL(ALT_NAME_INFO), + LVL(STREAM_INFO), LVL(COMPRESSION_INFO), LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION) + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; +} + +/* + generate qpathinfo operations +*/ +static bool handler_smb_qpathinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.path = gen_fname_open(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_CALL_SMB(smb_raw_pathinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + +/* + generate qfileinfo operations +*/ +static bool handler_smb_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_fileinfo_smb(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_fileinfo(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + /* generate setpathinfo operations */ -static bool handler_spathinfo(int instance) +static bool handler_smb_spathinfo(int instance) { union smb_setfileinfo parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1804,10 +2378,10 @@ static bool handler_spathinfo(int instance) /* a special case for the fid in a RENAME */ if (parm[0].generic.level == RAW_SFILEINFO_RENAME_INFORMATION && parm[0].rename_information.in.root_fid != 0) { - GEN_SET_FNUM(rename_information.in.root_fid); + GEN_SET_FNUM_SMB(rename_information.in.root_fid); } - GEN_CALL(smb_raw_setpathinfo(tree, &parm[i])); + GEN_CALL_SMB(smb_raw_setpathinfo(tree, &parm[i])); return true; } @@ -1816,7 +2390,7 @@ static bool handler_spathinfo(int instance) /* generate setfileinfo operations */ -static bool handler_sfileinfo(int instance) +static bool handler_smb_sfileinfo(int instance) { union smb_setfileinfo parm[NSERVERS]; NTSTATUS status[NSERVERS]; @@ -1826,17 +2400,52 @@ static bool handler_sfileinfo(int instance) gen_setfileinfo(instance, &parm[0]); GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.fnum); - GEN_CALL(smb_raw_setfileinfo(tree, &parm[i])); + GEN_SET_FNUM_SMB(generic.in.file.fnum); + GEN_CALL_SMB(smb_raw_setfileinfo(tree, &parm[i])); return true; } /* + this is called when a change notify reply comes in +*/ +static void async_notify_smb(struct smbcli_request *req) +{ + union smb_notify notify; + NTSTATUS status; + int i, j; + uint16_t tid; + struct smbcli_transport *transport = req->transport; + + tid = SVAL(req->in.hdr, HDR_TID); + + notify.nttrans.level = RAW_NOTIFY_NTTRANS; + status = smb_raw_changenotify_recv(req, current_op.mem_ctx, ¬ify); + if (NT_STATUS_IS_OK(status) && notify.nttrans.out.num_changes > 0) { + printf("notify tid=%d num_changes=%d action=%d name=%s\n", + tid, + notify.nttrans.out.num_changes, + notify.nttrans.out.changes[0].action, + notify.nttrans.out.changes[0].name.s); + } + + for (i=0;i<NSERVERS;i++) { + for (j=0;j<NINSTANCES;j++) { + if (transport == servers[i].smb_tree[j]->session->transport && + tid == servers[i].smb_tree[j]->tid) { + notifies[i][j].notify_count++; + notifies[i][j].status = status; + notifies[i][j].notify = notify; + } + } + } +} + +/* generate change notify operations */ -static bool handler_notify(int instance) +static bool handler_smb_notify(int instance) { union smb_notify parm[NSERVERS]; int n; @@ -1849,14 +2458,296 @@ static bool handler_notify(int instance) parm[0].nttrans.in.recursive = gen_bool(); GEN_COPY_PARM; - GEN_SET_FNUM(nttrans.in.file.fnum); + GEN_SET_FNUM_SMB(nttrans.in.file.fnum); for (n=0;n<NSERVERS;n++) { struct smbcli_request *req; - req = smb_raw_changenotify_send(servers[n].cli[instance]->tree, &parm[n]); - req->async.fn = async_notify; + req = smb_raw_changenotify_send(servers[n].smb_tree[instance], &parm[n]); + req->async.fn = async_notify_smb; + } + + return true; +} + + +/* + generate ntcreatex operations +*/ +static bool handler_smb2_create(int instance) +{ + struct smb2_create parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.security_flags = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFF); + parm[0].in.oplock_level = gen_bits_levels(3, 90, 0x0, 70, 0x9, 100, 0xFF); + parm[0].in.impersonation_level = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFFFFFFFF); + parm[0].in.create_flags = gen_reserved64(); + parm[0].in.reserved = gen_reserved64(); + parm[0].in.desired_access = gen_access_mask(); + parm[0].in.file_attributes = gen_attrib(); + parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); + parm[0].in.create_disposition = gen_open_disp(); + 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(); + parm[0].in.sec_desc = gen_sec_desc(); + + if (!options.use_oplocks) { + /* mask out oplocks */ + parm[0].in.oplock_level = 0; + } + + if (options.valid) { + parm[0].in.security_flags &= 3; + parm[0].in.oplock_level &= 9; + parm[0].in.impersonation_level &= 3; + } + + GEN_COPY_PARM; + GEN_CALL_SMB2(smb2_create(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.oplock_level); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.create_action); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + 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_SMB2(parm[0].in.fname, out.file.handle); + + return true; +} + +/* + generate close operations +*/ +static bool handler_smb2_close(int instance) +{ + struct smb2_close parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.file.handle.data[0] = gen_fnum_close(instance); + parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_close(tree, &parm[i])); + + CHECK_EQUAL(out.flags); + CHECK_EQUAL(out._pad); + CHECK_NTTIMES_EQUAL(out.create_time); + CHECK_NTTIMES_EQUAL(out.access_time); + CHECK_NTTIMES_EQUAL(out.write_time); + CHECK_NTTIMES_EQUAL(out.change_time); + CHECK_EQUAL(out.alloc_size); + CHECK_EQUAL(out.size); + CHECK_ATTRIB(out.file_attr); + + REMOVE_HANDLE_SMB2(in.file.handle); + + return true; +} + +/* + generate read operations +*/ +static bool handler_smb2_read(int instance) +{ + struct smb2_read parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.reserved = gen_reserved8(); + parm[0].in.length = gen_io_count(); + parm[0].in.offset = gen_offset(); + parm[0].in.min_count = gen_io_count(); + parm[0].in.channel = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.remaining = gen_bits_mask2(0x0, 0xFFFFFFFF); + parm[0].in.channel_offset = gen_bits_mask2(0x0, 0xFFFF); + parm[0].in.channel_length = gen_bits_mask2(0x0, 0xFFFF); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_read(tree, current_op.mem_ctx, &parm[i])); + + CHECK_EQUAL(out.remaining); + CHECK_EQUAL(out.reserved); + CHECK_EQUAL(out.data.length); + + return true; +} + +/* + generate write operations +*/ +static bool handler_smb2_write(int instance) +{ + struct smb2_write parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.offset = gen_offset(); + parm[0].in.unknown1 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.unknown2 = gen_bits_mask2(0, 0xFFFFFFFF); + parm[0].in.data = data_blob_talloc(current_op.mem_ctx, NULL, + gen_io_count()); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_write(tree, &parm[i])); + + CHECK_EQUAL(out._pad); + CHECK_EQUAL(out.nwritten); + CHECK_EQUAL(out.unknown1); + + return true; +} + +/* + generate lockingx operations +*/ +static bool handler_smb2_lock(int instance) +{ + struct smb2_lock parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + int n; + + parm[0].level = RAW_LOCK_LOCKX; + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.lock_count = gen_lock_count(); + parm[0].in.reserved = gen_reserved32(); + + parm[0].in.locks = talloc_array(current_op.mem_ctx, + struct smb2_lock_element, + parm[0].in.lock_count); + for (n=0;n<parm[0].in.lock_count;n++) { + parm[0].in.locks[n].offset = gen_offset(); + parm[0].in.locks[n].length = gen_io_count(); + /* don't yet cope with async replies */ + parm[0].in.locks[n].flags = gen_lock_flags_smb2() | + SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + parm[0].in.locks[n].reserved = gen_bits_mask2(0x0, 0xFFFFFFFF); } + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_lock(tree, &parm[i])); + + return true; +} + +/* + generate flush operations +*/ +static bool handler_smb2_flush(int instance) +{ + struct smb2_flush parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + ZERO_STRUCT(parm[0]); + parm[0].in.file.handle.data[0] = gen_fnum(instance); + parm[0].in.reserved1 = gen_reserved16(); + parm[0].in.reserved2 = gen_reserved32(); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(in.file.handle); + GEN_CALL_SMB2(smb2_flush(tree, &parm[i])); + + CHECK_EQUAL(out.reserved); + + return true; +} + +/* + generate echo operations +*/ +static bool handler_smb2_echo(int instance) +{ + NTSTATUS status[NSERVERS]; + + GEN_CALL_SMB2(smb2_keepalive(tree->session->transport)); + + return true; +} + + + +/* + generate a fileinfo query structure +*/ +static void gen_fileinfo_smb2(int instance, union smb_fileinfo *info) +{ + int i; + #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} + struct { + enum smb_fileinfo_level level; + const char *name; + } levels[] = { + LVL(BASIC_INFORMATION), + LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), + LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), + LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), + LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), + LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), + LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), LVL(SEC_DESC), + }; + do { + i = gen_int_range(0, ARRAY_SIZE(levels)-1); + } while (ignore_pattern(levels[i].name)); + + info->generic.level = levels[i].level; +} + +/* + generate qfileinfo operations +*/ +static bool handler_smb2_qfileinfo(int instance) +{ + union smb_fileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.handle.data[0] = gen_fnum(instance); + + gen_fileinfo_smb2(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_getinfo_file(tree, current_op.mem_ctx, &parm[i])); + + return cmp_fileinfo(instance, parm, status); +} + + +/* + generate setfileinfo operations +*/ +static bool handler_smb2_sfileinfo(int instance) +{ + union smb_setfileinfo parm[NSERVERS]; + NTSTATUS status[NSERVERS]; + + parm[0].generic.in.file.fnum = gen_fnum(instance); + + gen_setfileinfo(instance, &parm[0]); + + GEN_COPY_PARM; + GEN_SET_FNUM_SMB2(generic.in.file.handle); + GEN_CALL_SMB2(smb2_setinfo_file(tree, &parm[i])); + return true; } @@ -1866,20 +2757,30 @@ static bool handler_notify(int instance) static void wipe_files(void) { int i; + NTSTATUS status; if (options.skip_cleanup) { return; } for (i=0;i<NSERVERS;i++) { - int n = smbcli_deltree(servers[i].cli[0]->tree, "\\gentest"); + int n; + if (options.smb2) { + n = smb2_deltree(servers[i].smb2_tree[0], "gentest"); + } else { + n = smbcli_deltree(servers[i].smb_tree[0], "gentest"); + } if (n == -1) { printf("Failed to wipe tree on server %d\n", i); exit(1); } - if (NT_STATUS_IS_ERR(smbcli_mkdir(servers[i].cli[0]->tree, "\\gentest"))) { - printf("Failed to create \\gentest - %s\n", - smbcli_errstr(servers[i].cli[0]->tree)); + if (options.smb2) { + status = smb2_util_mkdir(servers[i].smb2_tree[0], "gentest"); + } else { + status = smbcli_mkdir(servers[i].smb_tree[0], "gentest"); + } + if (NT_STATUS_IS_ERR(status)) { + printf("Failed to create gentest on server %d - %s\n", i, nt_errstr(status)); exit(1); } if (n > 0) { @@ -1917,27 +2818,39 @@ static void dump_seeds(void) static struct { const char *name; bool (*handler)(int instance); + bool smb2; int count, success_count; } gen_ops[] = { - {"OPEN", handler_open}, - {"OPENX", handler_openx}, - {"NTCREATEX", handler_ntcreatex}, - {"CLOSE", handler_close}, - {"UNLINK", handler_unlink}, - {"MKDIR", handler_mkdir}, - {"RMDIR", handler_rmdir}, - {"RENAME", handler_rename}, - {"NTRENAME", handler_ntrename}, - {"READX", handler_readx}, - {"WRITEX", handler_writex}, - {"CHKPATH", handler_chkpath}, - {"LOCKINGX", handler_lockingx}, - {"QPATHINFO", handler_qpathinfo}, - {"QFILEINFO", handler_qfileinfo}, - {"SPATHINFO", handler_spathinfo}, - {"SFILEINFO", handler_sfileinfo}, - {"NOTIFY", handler_notify}, - {"SEEK", handler_seek}, + {"CREATE", handler_smb2_create, true}, + {"CLOSE", handler_smb2_close, true}, + {"READ", handler_smb2_read, true}, + {"WRITE", handler_smb2_write, true}, + {"LOCK", handler_smb2_lock, true}, + {"FLUSH", handler_smb2_flush, true}, + {"ECHO", handler_smb2_echo, true}, + {"QFILEINFO", handler_smb2_qfileinfo, true}, + {"SFILEINFO", handler_smb2_sfileinfo, true}, + + {"OPEN", handler_smb_open, false}, + {"OPENX", handler_smb_openx, false}, + {"NTCREATEX", handler_smb_ntcreatex, false}, + {"CLOSE", handler_smb_close, false}, + {"UNLINK", handler_smb_unlink, false}, + {"MKDIR", handler_smb_mkdir, false}, + {"RMDIR", handler_smb_rmdir, false}, + {"RENAME", handler_smb_rename, false}, + {"NTRENAME", handler_smb_ntrename, false}, + {"READX", handler_smb_readx, false}, + {"WRITEX", handler_smb_writex, false}, + {"CHKPATH", handler_smb_chkpath, false}, + {"SEEK", handler_smb_seek, false}, + {"LOCKINGX", handler_smb_lockingx, false}, + {"QPATHINFO", handler_smb_qpathinfo, false}, + {"QFILEINFO", handler_smb_qfileinfo, false}, + {"SPATHINFO", handler_smb_spathinfo, false}, + {"SFILEINFO", handler_smb_sfileinfo, false}, + {"NOTIFY", handler_smb_notify, false}, + {"SEEK", handler_smb_seek, false}, }; @@ -1981,7 +2894,8 @@ static int run_test(struct event_context *ev, struct loadparm_context *lp_ctx) /* generate a non-ignored operation */ do { which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1); - } while (ignore_pattern(gen_ops[which_op].name)); + } while (ignore_pattern(gen_ops[which_op].name) || + gen_ops[which_op].smb2 != options.smb2); DEBUG(3,("Generating op %s on instance %d\n", gen_ops[which_op].name, instance)); @@ -2030,6 +2944,7 @@ static void backtrack_analyze(struct event_context *ev, struct loadparm_context *lp_ctx) { int chunk, ret; + const char *mismatch = current_op.mismatch; chunk = options.numops / 2; @@ -2056,6 +2971,10 @@ static void backtrack_analyze(struct event_context *ev, if (ret == options.numops) { /* this chunk is needed */ base += chunk; + } else if (mismatch != current_op.mismatch && + strcmp(mismatch, current_op.mismatch)) { + base += chunk; + printf("Different error in backtracking\n"); } else if (ret < base) { printf("damn - inconsistent errors! found early error\n"); options.numops = ret+1; @@ -2189,6 +3108,7 @@ static bool split_unc_name(const char *unc, char **server, char **share) enum {OPT_UNCLIST=1000}; struct poptOption long_options[] = { POPT_AUTOHELP + {"smb2", 0, POPT_ARG_NONE, &options.smb2, 0, "use SMB2 protocol", NULL}, {"seed", 0, POPT_ARG_INT, &options.seed, 0, "Seed to use for randomizer", NULL}, {"num-ops", 0, POPT_ARG_INT, &options.numops, 0, "num ops", NULL}, {"oplocks", 0, POPT_ARG_NONE, &options.use_oplocks,0, "use oplocks", NULL}, @@ -2202,7 +3122,11 @@ static bool split_unc_name(const char *unc, char **server, char **share) {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, {"seedsfile", 0, POPT_ARG_STRING, &options.seeds_file, 0, "seed file", NULL}, { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" }, + {"maskindexing", 0, POPT_ARG_NONE, &options.mask_indexing, 0, "mask out the indexed file attrib", NULL}, + {"noeas", 0, POPT_ARG_NONE, &options.no_eas, 0, "don't use extended attributes", NULL}, + {"noacls", 0, POPT_ARG_NONE, &options.no_acls, 0, "don't use ACLs", NULL}, {"skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, "don't delete files at start", NULL}, + {"valid", 0, POPT_ARG_NONE, &options.valid, 0, "generate only valid fields", NULL}, POPT_COMMON_SAMBA POPT_COMMON_CONNECTION POPT_COMMON_CREDENTIALS @@ -2210,6 +3134,8 @@ static bool split_unc_name(const char *unc, char **server, char **share) { NULL } }; + memset(&bad_smb2_handle, 0xFF, sizeof(bad_smb2_handle)); + setlinebuf(stdout); options.seed = time(NULL); options.numops = 1000; diff --git a/source4/torture/gentest_smb2.c b/source4/torture/gentest_smb2.c deleted file mode 100644 index a3a794d3ea..0000000000 --- a/source4/torture/gentest_smb2.c +++ /dev/null @@ -1,2064 +0,0 @@ -/* - Unix SMB/CIFS implementation. - - generic testing tool - version with SMB2 support - - Copyright (C) Andrew Tridgell 2003-2008 - - 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 "lib/cmdline/popt_common.h" -#include "lib/events/events.h" -#include "system/time.h" -#include "system/filesys.h" -#include "libcli/raw/request.h" -#include "libcli/libcli.h" -#include "libcli/raw/libcliraw.h" -#include "libcli/smb2/smb2.h" -#include "libcli/smb2/smb2_calls.h" -#include "librpc/gen_ndr/security.h" -#include "auth/credentials/credentials.h" -#include "libcli/resolve/resolve.h" -#include "auth/gensec/gensec.h" -#include "param/param.h" -#include "dynconfig/dynconfig.h" - -#define NSERVERS 2 -#define NINSTANCES 2 - -/* global options */ -static struct gentest_options { - int showall; - int analyze; - int analyze_always; - int analyze_continuous; - uint_t max_open_handles; - uint_t seed; - uint_t numops; - int use_oplocks; - char **ignore_patterns; - const char *seeds_file; - int use_preset_seeds; - int fast_reconnect; - int mask_indexing; - int no_eas; - int skip_cleanup; - int valid; -} options; - -/* mapping between open handles on the server and local handles */ -static struct { - bool active; - uint_t instance; - struct smb2_handle server_handle[NSERVERS]; - const char *name; -} *open_handles; -static uint_t num_open_handles; - -/* state information for the servers. We open NINSTANCES connections to - each server */ -static struct { - struct smb2_tree *tree[NINSTANCES]; - char *server_name; - char *share_name; - struct cli_credentials *credentials; -} servers[NSERVERS]; - -/* the seeds and flags for each operation */ -static struct { - uint_t seed; - bool disabled; -} *op_parms; - - -/* oplock break info */ -static struct { - bool got_break; - struct smb2_handle server_handle; - uint16_t handle; - uint8_t level; - bool do_close; -} oplocks[NSERVERS][NINSTANCES]; - -/* change notify reply info */ -static struct { - int notify_count; - NTSTATUS status; - union smb_notify notify; -} notifies[NSERVERS][NINSTANCES]; - -/* info relevant to the current operation */ -static struct { - const char *name; - uint_t seed; - NTSTATUS status; - uint_t opnum; - TALLOC_CTX *mem_ctx; -} current_op; - -static struct smb2_handle bad_smb2_handle; - - -#define BAD_HANDLE 0xFFFE - -static bool oplock_handler(struct smb2_transport *transport, const struct smb2_handle *handle, - uint8_t level, void *private_data); -static void idle_func(struct smb2_transport *transport, void *private); - -/* - check if a string should be ignored. This is used as the basis - for all error ignore settings -*/ -static bool ignore_pattern(const char *str) -{ - int i; - if (!options.ignore_patterns) return false; - - for (i=0;options.ignore_patterns[i];i++) { - if (strcmp(options.ignore_patterns[i], str) == 0 || - gen_fnmatch(options.ignore_patterns[i], str) == 0) { - DEBUG(2,("Ignoring '%s'\n", str)); - return true; - } - } - return false; -} - -/***************************************************** -connect to the servers -*******************************************************/ -static bool connect_servers_fast(void) -{ - int h, i; - - /* close all open files */ - for (h=0;h<options.max_open_handles;h++) { - if (!open_handles[h].active) continue; - for (i=0;i<NSERVERS;i++) { - NTSTATUS status = smb2_util_close(servers[i].tree[open_handles[h].instance], - open_handles[h].server_handle[i]); - if (NT_STATUS_IS_ERR(status)) { - return false; - } - open_handles[h].active = false; - } - } - - return true; -} - - - - -/***************************************************** -connect to the servers -*******************************************************/ -static bool connect_servers(struct event_context *ev, - struct loadparm_context *lp_ctx) -{ - int i, j; - - if (options.fast_reconnect && servers[0].tree[0]) { - if (connect_servers_fast()) { - return true; - } - } - - /* close any existing connections */ - for (i=0;i<NSERVERS;i++) { - for (j=0;j<NINSTANCES;j++) { - if (servers[i].tree[j]) { - smb2_tdis(servers[i].tree[j]); - talloc_free(servers[i].tree[j]); - servers[i].tree[j] = NULL; - } - } - } - - for (i=0;i<NSERVERS;i++) { - for (j=0;j<NINSTANCES;j++) { - NTSTATUS status; - printf("Connecting to \\\\%s\\%s as %s - instance %d\n", - servers[i].server_name, servers[i].share_name, - servers[i].credentials->username, j); - - cli_credentials_set_workstation(servers[i].credentials, - "gentest", CRED_SPECIFIED); - - status = smb2_connect(NULL, servers[i].server_name, - servers[i].share_name, - lp_resolve_context(lp_ctx), - servers[i].credentials, - &servers[i].tree[j], - ev); - if (!NT_STATUS_IS_OK(status)) { - printf("Failed to connect to \\\\%s\\%s - %s\n", - servers[i].server_name, servers[i].share_name, - nt_errstr(status)); - return false; - } - - servers[i].tree[j]->session->transport->oplock.handler = oplock_handler; - servers[i].tree[j]->session->transport->oplock.private_data = (void *)(uintptr_t)((i<<8)|j); - smb2_transport_idle_handler(servers[i].tree[j]->session->transport, idle_func, 50000, NULL); - } - } - - return true; -} - -/* - work out the time skew between the servers - be conservative -*/ -static uint_t time_skew(void) -{ - uint_t ret; - ret = labs(servers[0].tree[0]->session->transport->negotiate.system_time - - servers[1].tree[0]->session->transport->negotiate.system_time); - return ret + 300; -} - - -static bool smb2_handle_equal(const struct smb2_handle *h1, const struct smb2_handle *h2) -{ - return memcmp(h1, h2, sizeof(struct smb2_handle)) == 0; -} - -/* - turn a server handle into a local handle -*/ -static uint_t fnum_to_handle(int server, int instance, struct smb2_handle server_handle) -{ - uint_t i; - for (i=0;i<options.max_open_handles;i++) { - if (!open_handles[i].active || - instance != open_handles[i].instance) continue; - if (smb2_handle_equal(&open_handles[i].server_handle[server], &server_handle)) { - return i; - } - } - printf("Invalid server handle in fnum_to_handle on server %d instance %d\n", - server, instance); - return BAD_HANDLE; -} - -/* - add some newly opened handles -*/ -static void gen_add_handle(int instance, const char *name, struct smb2_handle handles[NSERVERS]) -{ - int i, h; - for (h=0;h<options.max_open_handles;h++) { - if (!open_handles[h].active) break; - } - if (h == options.max_open_handles) { - /* we have to force close a random handle */ - h = random() % options.max_open_handles; - for (i=0;i<NSERVERS;i++) { - NTSTATUS status; - status = smb2_util_close(servers[i].tree[open_handles[h].instance], - open_handles[h].server_handle[i]); - if (NT_STATUS_IS_ERR(status)) { - printf("INTERNAL ERROR: Close failed when recovering handle! - %s\n", - nt_errstr(status)); - } - } - printf("Recovered handle %d\n", h); - num_open_handles--; - } - for (i=0;i<NSERVERS;i++) { - open_handles[h].server_handle[i] = handles[i]; - open_handles[h].instance = instance; - open_handles[h].active = true; - open_handles[h].name = name; - } - num_open_handles++; - - printf("OPEN num_open_handles=%d h=%d (%s)\n", - num_open_handles, h, name); -} - -/* - remove a closed handle -*/ -static void gen_remove_handle(int instance, struct smb2_handle handles[NSERVERS]) -{ - int h; - for (h=0;h<options.max_open_handles;h++) { - if (instance == open_handles[h].instance && - smb2_handle_equal(&open_handles[h].server_handle[0], &handles[0])) { - open_handles[h].active = false; - num_open_handles--; - printf("CLOSE num_open_handles=%d h=%d (%s)\n", - num_open_handles, h, - open_handles[h].name); - return; - } - } - printf("Removing invalid handle!?\n"); - exit(1); -} - -/* - return true with 'chance' probability as a percentage -*/ -static bool gen_chance(uint_t chance) -{ - return ((random() % 100) <= chance); -} - -/* - map an internal handle number to a server handle -*/ -static struct smb2_handle gen_lookup_handle(int server, uint16_t handle) -{ - if (handle == BAD_HANDLE) return bad_smb2_handle; - return open_handles[handle].server_handle[server]; -} - -/* - return a file handle -*/ -static uint16_t gen_fnum(int instance) -{ - uint16_t h; - int count = 0; - - if (gen_chance(20)) return BAD_HANDLE; - - while (num_open_handles > 0 && count++ < 10*options.max_open_handles) { - h = random() % options.max_open_handles; - if (open_handles[h].active && - open_handles[h].instance == instance) { - return h; - } - } - return BAD_HANDLE; -} - -/* - return a file handle, but skewed so we don't close the last - couple of handles too readily -*/ -static uint16_t gen_fnum_close(int instance) -{ - if (num_open_handles < 5) { - if (gen_chance(90)) return BAD_HANDLE; - } - - return gen_fnum(instance); -} - -/* - generate an integer in a specified range -*/ -static int gen_int_range(uint64_t min, uint64_t max) -{ - uint_t r = random(); - return min + (r % (1+max-min)); -} - -/* - return a fnum for use as a root fid - be careful to call GEN_SET_FNUM() when you use this! -*/ -static uint16_t gen_root_fid(int instance) -{ - if (gen_chance(5)) return gen_fnum(instance); - return 0; -} - -/* - generate a file offset -*/ -static int gen_offset(void) -{ - if (gen_chance(20)) return 0; -// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); - return gen_int_range(0, 1024*1024); -} - -/* - generate a io count -*/ -static int gen_io_count(void) -{ - if (gen_chance(20)) return 0; -// if (gen_chance(5)) return gen_int_range(0, 0xFFFFFFFF); - return gen_int_range(0, 4096); -} - -/* - generate a filename -*/ -static const char *gen_fname(void) -{ - const char *names[] = {"gentest\\gentest.dat", - "gentest\\foo", - "gentest\\foo2.sym", - "gentest\\foo3.dll", - "gentest\\foo4", - "gentest\\foo4:teststream1", - "gentest\\foo4:teststream2", - "gentest\\foo5.exe", - "gentest\\foo5.exe:teststream3", - "gentest\\foo5.exe:teststream4", - "gentest\\foo6.com", - "gentest\\blah", - "gentest\\blah\\blergh.txt", - "gentest\\blah\\blergh2", - "gentest\\blah\\blergh3.txt", - "gentest\\blah\\blergh4", - "gentest\\blah\\blergh5.txt", - "gentest\\blah\\blergh5", - "gentest\\blah\\.", -#if 0 - /* this causes problem with w2k3 */ - "gentest\\blah\\..", -#endif - "gentest\\a_very_long_name.bin", - "gentest\\x.y", - "gentest\\blah"}; - int i; - - do { - i = gen_int_range(0, ARRAY_SIZE(names)-1); - } while (ignore_pattern(names[i])); - - return names[i]; -} - -/* - generate a filename with a higher chance of choosing an already - open file -*/ -static const char *gen_fname_open(int instance) -{ - uint16_t h; - h = gen_fnum(instance); - if (h == BAD_HANDLE) { - return gen_fname(); - } - return open_handles[h].name; -} - -/* - generate a wildcard pattern -*/ -static const char *gen_pattern(void) -{ - int i; - const char *names[] = {"gentest\\*.dat", - "gentest\\*", - "gentest\\*.*", - "gentest\\blah\\*.*", - "gentest\\blah\\*", - "gentest\\?"}; - - if (gen_chance(50)) return gen_fname(); - - do { - i = gen_int_range(0, ARRAY_SIZE(names)-1); - } while (ignore_pattern(names[i])); - - return names[i]; -} - -static uint32_t gen_bits_levels(int nlevels, ...) -{ - va_list ap; - uint32_t pct; - uint32_t mask; - int i; - va_start(ap, nlevels); - for (i=0;i<nlevels;i++) { - pct = va_arg(ap, uint32_t); - mask = va_arg(ap, uint32_t); - if (pct == 100 || gen_chance(pct)) { - va_end(ap); - return mask & random(); - } - } - va_end(ap); - return 0; -} - -/* - generate a bitmask -*/ -static uint32_t gen_bits_mask(uint_t mask) -{ - uint_t ret = random(); - return ret & mask; -} - -/* - generate a bitmask with high probability of the first mask - and low of the second -*/ -static uint32_t gen_bits_mask2(uint32_t mask1, uint32_t mask2) -{ - if (!options.valid && gen_chance(10)) return gen_bits_mask(mask2); - return gen_bits_mask(mask1); -} - -/* - generate reserved values - */ -static uint64_t gen_reserved8(void) -{ - if (options.valid) return 0; - return gen_bits_mask(0xFF); -} - -static uint64_t gen_reserved16(void) -{ - if (options.valid) return 0; - return gen_bits_mask(0xFFFF); -} - -static uint64_t gen_reserved32(void) -{ - if (options.valid) return 0; - return gen_bits_mask(0xFFFFFFFF); -} - -static uint64_t gen_reserved64(void) -{ - if (options.valid) return 0; - return gen_bits_mask(0xFFFFFFFF) | (((uint64_t)gen_bits_mask(0xFFFFFFFF))<<32); -} - - - -/* - generate a boolean -*/ -static bool gen_bool(void) -{ - return gen_bits_mask2(0x1, 0xFF); -} - -/* - return a set of lock flags -*/ -static uint16_t gen_lock_flags(void) -{ - if (!options.valid && gen_chance(5)) return gen_bits_mask(0xFFFF); - if (gen_chance(20)) return gen_bits_mask(0x1F); - if (gen_chance(50)) return SMB2_LOCK_FLAG_UNLOCK; - return gen_bits_mask(SMB2_LOCK_FLAG_SHARED | - SMB2_LOCK_FLAG_EXCLUSIVE | - SMB2_LOCK_FLAG_FAIL_IMMEDIATELY); -} - -/* - generate a lock count -*/ -static off_t gen_lock_count(void) -{ - return gen_int_range(0, 3); -} - -/* - generate a NT access mask -*/ -static uint32_t gen_access_mask(void) -{ - uint32_t ret; - if (gen_chance(70)) return SEC_FLAG_MAXIMUM_ALLOWED; - if (gen_chance(70)) return SEC_FILE_ALL; - ret = gen_bits_mask(0xFFFFFFFF); - if (options.valid) ret &= ~SEC_MASK_INVALID; - return ret; -} - -/* - generate a ntcreatex create options bitfield -*/ -static uint32_t gen_create_options(void) -{ - if (!options.valid && gen_chance(20)) return gen_bits_mask(0xFFFFFFFF); - if (gen_chance(50)) return 0; - return gen_bits_mask(NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY); -} - -/* - generate a ntcreatex open disposition -*/ -static uint32_t gen_open_disp(void) -{ - if (gen_chance(50)) return NTCREATEX_DISP_OPEN_IF; - if (!options.valid && gen_chance(10)) return gen_bits_mask(0xFFFFFFFF); - return gen_int_range(0, 5); -} - -/* - generate a file attrib combination -*/ -static uint32_t gen_attrib(void) -{ - uint32_t ret; - if (gen_chance(20)) { - ret = gen_bits_mask(0xFFFFFFFF); - if (options.valid) ret &= FILE_ATTRIBUTE_ALL_MASK; - return ret; - } - return gen_bits_mask(FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); -} - -/* - generate a unix timestamp -*/ -static time_t gen_timet(void) -{ - if (gen_chance(30)) return 0; - return (time_t)random(); -} - -/* - generate a timestamp -*/ -static NTTIME gen_nttime(void) -{ - NTTIME ret; - unix_to_nt_time(&ret, gen_timet()); - 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 -*/ -static uint_t gen_alloc_size(void) -{ - uint_t ret; - - if (gen_chance(30)) return 0; - - ret = random() % 4*1024*1024; - /* give a high chance of a round number */ - if (gen_chance(60)) { - ret &= ~(1024*1024 - 1); - } - return ret; -} - -/* - generate an ea_struct -*/ -static struct ea_struct gen_ea_struct(void) -{ - struct ea_struct ea; - const char *names[] = {"EAONE", - "", - "FOO!", - " WITH SPACES ", - ".", - "AVERYLONGATTRIBUTENAME"}; - const char *values[] = {"VALUE1", - "", - "NOT MUCH FOO", - " LEADING SPACES ", - ":", - "ASOMEWHATLONGERATTRIBUTEVALUE"}; - int i; - - ZERO_STRUCT(ea); - - do { - i = gen_int_range(0, ARRAY_SIZE(names)-1); - } while (ignore_pattern(names[i])); - - ea.name.s = names[i]; - - do { - i = gen_int_range(0, ARRAY_SIZE(values)-1); - } while (ignore_pattern(values[i])); - - ea.value = data_blob(values[i], strlen(values[i])); - - if (gen_chance(10)) ea.flags = gen_bits_mask(0xFF); - ea.flags = 0; - - return ea; -} - -/* - generate an ea_struct -*/ -static struct smb_ea_list gen_ea_list(void) -{ - struct smb_ea_list eas; - int i; - if (options.no_eas) { - ZERO_STRUCT(eas); - return eas; - } - eas.num_eas = gen_int_range(0, 3); - eas.eas = talloc_array(current_op.mem_ctx, struct ea_struct, eas.num_eas); - for (i=0;i<eas.num_eas;i++) { - eas.eas[i] = gen_ea_struct(); - } - return eas; -} - -static void oplock_handler_close_recv(struct smb2_request *req) -{ - NTSTATUS status; - struct smb2_close io; - status = smb2_close_recv(req, &io); - if (!NT_STATUS_IS_OK(status)) { - printf("close failed in oplock_handler\n"); - smb_panic("close failed in oplock_handler"); - } -} - -static void oplock_handler_ack_callback(struct smb2_request *req) -{ - NTSTATUS status; - struct smb2_break br; - - status = smb2_break_recv(req, &br); - if (!NT_STATUS_IS_OK(status)) { - printf("oplock break ack failed in oplock_handler\n"); - smb_panic("oplock break ack failed in oplock_handler"); - } -} - -static bool send_oplock_ack(struct smb2_tree *tree, struct smb2_handle handle, - uint8_t level) -{ - struct smb2_break br; - struct smb2_request *req; - - ZERO_STRUCT(br); - br.in.file.handle = handle; - br.in.oplock_level = level; - br.in.reserved = gen_reserved8(); - br.in.reserved2 = gen_reserved32(); - - req = smb2_break_send(tree, &br); - if (req == NULL) return false; - req->async.fn = oplock_handler_ack_callback; - req->async.private_data = NULL; - return true; -} - -/* - the oplock handler will either ack the break or close the file -*/ -static bool oplock_handler(struct smb2_transport *transport, const struct smb2_handle *handle, - uint8_t level, void *private_data) -{ - struct smb2_close io; - unsigned i, j; - bool do_close; - struct smb2_tree *tree = NULL; - struct smb2_request *req; - - srandom(current_op.seed); - do_close = gen_chance(50); - - i = ((uintptr_t)private_data) >> 8; - j = ((uintptr_t)private_data) & 0xFF; - - if (i >= NSERVERS || j >= NINSTANCES) { - printf("Bad private_data in oplock_handler\n"); - return false; - } - - oplocks[i][j].got_break = true; - oplocks[i][j].server_handle = *handle; - oplocks[i][j].handle = fnum_to_handle(i, j, *handle); - oplocks[i][j].level = level; - oplocks[i][j].do_close = do_close; - tree = talloc_get_type(servers[i].tree[j], struct smb2_tree); - - if (!tree) { - printf("Oplock break not for one of our trees!?\n"); - return false; - } - - if (!do_close) { - printf("oplock ack handle=%d\n", oplocks[i][j].handle); - return send_oplock_ack(tree, *handle, level); - } - - printf("oplock close fnum=%d\n", oplocks[i][j].handle); - - ZERO_STRUCT(io); - io.in.file.handle = *handle; - io.in.flags = 0; - req = smb2_close_send(tree, &io); - - if (req == NULL) { - printf("WARNING: close failed in oplock_handler_close\n"); - return false; - } - - req->async.fn = oplock_handler_close_recv; - req->async.private_data = NULL; - - return true; -} - - -/* - the idle function tries to cope with getting an oplock break on a connection, and - an operation on another connection blocking until that break is acked - we check for operations on all transports in the idle function -*/ -static void idle_func(struct smb2_transport *transport, void *private) -{ - int i, j; - for (i=0;i<NSERVERS;i++) { - for (j=0;j<NINSTANCES;j++) { - if (servers[i].tree[j] && - transport != servers[i].tree[j]->session->transport) { - // smb2_transport_process(servers[i].tree[j]->session->transport); - } - } - } - -} - - -/* - compare NTSTATUS, using checking ignored patterns -*/ -static bool compare_status(NTSTATUS status1, NTSTATUS status2) -{ - if (NT_STATUS_EQUAL(status1, status2)) return true; - - /* one code being an error and the other OK is always an error */ - if (NT_STATUS_IS_OK(status1) || NT_STATUS_IS_OK(status2)) return false; - - /* if we are ignoring one of the status codes then consider this a match */ - if (ignore_pattern(nt_errstr(status1)) || - ignore_pattern(nt_errstr(status2))) { - return true; - } - return false; -} - -#if 0 -/* - check for pending packets on all connections -*/ -static void check_pending(void) -{ - int i, j; - - msleep(20); - - for (j=0;j<NINSTANCES;j++) { - for (i=0;i<NSERVERS;i++) { - // smb2_transport_process(servers[i].tree[j]->session->transport); - } - } -} -#endif - -/* - check that the same oplock breaks have been received by all instances -*/ -static bool check_oplocks(const char *call) -{ -#if 0 - int i, j; - int tries = 0; - -again: - check_pending(); - - for (j=0;j<NINSTANCES;j++) { - for (i=1;i<NSERVERS;i++) { - if (oplocks[0][j].got_break != oplocks[i][j].got_break || - oplocks[0][j].handle != oplocks[i][j].handle || - oplocks[0][j].level != oplocks[i][j].level) { - if (tries++ < 10) goto again; - printf("oplock break inconsistent - %d/%d/%d vs %d/%d/%d\n", - oplocks[0][j].got_break, - oplocks[0][j].handle, - oplocks[0][j].level, - oplocks[i][j].got_break, - oplocks[i][j].handle, - oplocks[i][j].level); - return false; - } - } - } - - /* if we got a break and closed then remove the handle */ - for (j=0;j<NINSTANCES;j++) { - if (oplocks[0][j].got_break && - oplocks[0][j].do_close) { - uint16_t fnums[NSERVERS]; - for (i=0;i<NSERVERS;i++) { - fnums[i] = oplocks[i][j].fnum; - } - gen_remove_handle(j, fnums); - break; - } - } -#endif - return true; -} - - -/* - check that the same change notify info has been received by all instances -*/ -static bool check_notifies(const char *call) -{ -#if 0 - int i, j; - int tries = 0; - -again: - check_pending(); - - for (j=0;j<NINSTANCES;j++) { - for (i=1;i<NSERVERS;i++) { - int n; - union smb_notify not1, not2; - - if (notifies[0][j].notify_count != notifies[i][j].notify_count) { - if (tries++ < 10) goto again; - printf("Notify count inconsistent %d %d\n", - notifies[0][j].notify_count, - notifies[i][j].notify_count); - return false; - } - - if (notifies[0][j].notify_count == 0) continue; - - if (!NT_STATUS_EQUAL(notifies[0][j].status, - notifies[i][j].status)) { - printf("Notify status mismatch - %s - %s\n", - nt_errstr(notifies[0][j].status), - nt_errstr(notifies[i][j].status)); - return false; - } - - if (!NT_STATUS_IS_OK(notifies[0][j].status)) { - continue; - } - - not1 = notifies[0][j].notify; - not2 = notifies[i][j].notify; - - for (n=0;n<not1.nttrans.out.num_changes;n++) { - if (not1.nttrans.out.changes[n].action != - not2.nttrans.out.changes[n].action) { - printf("Notify action %d inconsistent %d %d\n", n, - not1.nttrans.out.changes[n].action, - not2.nttrans.out.changes[n].action); - return false; - } - if (strcmp(not1.nttrans.out.changes[n].name.s, - not2.nttrans.out.changes[n].name.s)) { - printf("Notify name %d inconsistent %s %s\n", n, - not1.nttrans.out.changes[n].name.s, - not2.nttrans.out.changes[n].name.s); - return false; - } - if (not1.nttrans.out.changes[n].name.private_length != - not2.nttrans.out.changes[n].name.private_length) { - printf("Notify name length %d inconsistent %d %d\n", n, - not1.nttrans.out.changes[n].name.private_length, - not2.nttrans.out.changes[n].name.private_length); - return false; - } - } - } - } - - ZERO_STRUCT(notifies); - -#endif - return true; -} - -#define GEN_COPY_PARM do { \ - int i; \ - for (i=1;i<NSERVERS;i++) { \ - parm[i] = parm[0]; \ - } \ -} while (0) - -#define GEN_CALL(call) do { \ - int i; \ - ZERO_STRUCT(oplocks); \ - ZERO_STRUCT(notifies); \ - for (i=0;i<NSERVERS;i++) { \ - struct smb2_tree *tree = servers[i].tree[instance]; \ - status[i] = call; \ - } \ - current_op.status = status[0]; \ - for (i=1;i<NSERVERS;i++) { \ - if (!compare_status(status[i], status[0])) { \ - printf("status different in %s - %s %s\n", #call, \ - nt_errstr(status[0]), nt_errstr(status[i])); \ - return false; \ - } \ - } \ - if (!check_oplocks(#call)) return false; \ - if (!check_notifies(#call)) return false; \ - if (!NT_STATUS_IS_OK(status[0])) { \ - return true; \ - } \ -} while(0) - -#define ADD_HANDLE(name, field) do { \ - struct smb2_handle handles[NSERVERS]; \ - int i; \ - for (i=0;i<NSERVERS;i++) { \ - handles[i] = parm[i].field; \ - } \ - gen_add_handle(instance, name, handles); \ -} while(0) - -#define REMOVE_HANDLE(field) do { \ - struct smb2_handle handles[NSERVERS]; \ - int i; \ - for (i=0;i<NSERVERS;i++) { \ - handles[i] = parm[i].field; \ - } \ - gen_remove_handle(instance, handles); \ -} while(0) - -#define GEN_SET_FNUM(field) do { \ - int i; \ - for (i=0;i<NSERVERS;i++) { \ - parm[i].field = gen_lookup_handle(i, parm[i].field.data[0]); \ - } \ -} while(0) - -#define CHECK_EQUAL(field) do { \ - if (parm[0].field != parm[1].field && !ignore_pattern(#field)) { \ - printf("Mismatch in %s - 0x%llx 0x%llx\n", #field, \ - (unsigned long long)parm[0].field, (unsigned long long)parm[1].field); \ - return false; \ - } \ -} while(0) - -#define CHECK_ATTRIB(field) do { \ - if (!options.mask_indexing) { \ - CHECK_EQUAL(field); \ - } else if ((~FILE_ATTRIBUTE_NONINDEXED & parm[0].field) != (~FILE_ATTRIBUTE_NONINDEXED & parm[1].field) && !ignore_pattern(#field)) { \ - printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ - (int)parm[0].field, (int)parm[1].field); \ - return false; \ - } \ -} while(0) - -#define CHECK_WSTR_EQUAL(field) do { \ - if ((!parm[0].field.s && parm[1].field.s) || (parm[0].field.s && !parm[1].field.s)) { \ - printf("%s is NULL!\n", #field); \ - return false; \ - } \ - if (parm[0].field.s && strcmp(parm[0].field.s, parm[1].field.s) != 0 && !ignore_pattern(#field)) { \ - printf("Mismatch in %s - %s %s\n", #field, \ - parm[0].field.s, parm[1].field.s); \ - return false; \ - } \ - CHECK_EQUAL(field.private_length); \ -} while(0) - -#define CHECK_BLOB_EQUAL(field) do { \ - if (memcmp(parm[0].field.data, parm[1].field.data, parm[0].field.length) != 0 && !ignore_pattern(#field)) { \ - printf("Mismatch in %s\n", #field); \ - return false; \ - } \ - CHECK_EQUAL(field.length); \ -} while(0) - -#define CHECK_NTTIMES_EQUAL(field) do { \ - if (labs(nt_time_to_unix(parm[0].field) - \ - nt_time_to_unix(parm[1].field)) > time_skew() && \ - !ignore_pattern(#field)) { \ - printf("Mismatch in %s - 0x%x 0x%x\n", #field, \ - (int)nt_time_to_unix(parm[0].field), \ - (int)nt_time_to_unix(parm[1].field)); \ - return false; \ - } \ -} while(0) - -/* - generate ntcreatex operations -*/ -static bool handler_create(int instance) -{ - struct smb2_create parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - ZERO_STRUCT(parm[0]); - parm[0].in.security_flags = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFF); - parm[0].in.oplock_level = gen_bits_levels(3, 90, 0x0, 70, 0x9, 100, 0xFF); - parm[0].in.impersonation_level = gen_bits_levels(3, 90, 0x0, 70, 0x3, 100, 0xFFFFFFFF); - parm[0].in.create_flags = gen_reserved64(); - parm[0].in.reserved = gen_reserved64(); - parm[0].in.desired_access = gen_access_mask(); - parm[0].in.file_attributes = gen_attrib(); - parm[0].in.share_access = gen_bits_mask2(0x7, 0xFFFFFFFF); - parm[0].in.create_disposition = gen_open_disp(); - 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 */ - parm[0].in.oplock_level = 0; - } - - if (options.valid) { - parm[0].in.security_flags &= 3; - parm[0].in.oplock_level &= 9; - parm[0].in.impersonation_level &= 3; - } - - GEN_COPY_PARM; - GEN_CALL(smb2_create(tree, current_op.mem_ctx, &parm[i])); - - CHECK_EQUAL(out.oplock_level); - CHECK_EQUAL(out.reserved); - CHECK_EQUAL(out.create_action); - CHECK_NTTIMES_EQUAL(out.create_time); - CHECK_NTTIMES_EQUAL(out.access_time); - CHECK_NTTIMES_EQUAL(out.write_time); - CHECK_NTTIMES_EQUAL(out.change_time); - CHECK_EQUAL(out.alloc_size); - 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); - - return true; -} - -/* - generate close operations -*/ -static bool handler_close(int instance) -{ - struct smb2_close parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - ZERO_STRUCT(parm[0]); - parm[0].in.file.handle.data[0] = gen_fnum_close(instance); - parm[0].in.flags = gen_bits_mask2(0x1, 0xFFFF); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_close(tree, &parm[i])); - - CHECK_EQUAL(out.flags); - CHECK_EQUAL(out._pad); - CHECK_NTTIMES_EQUAL(out.create_time); - CHECK_NTTIMES_EQUAL(out.access_time); - CHECK_NTTIMES_EQUAL(out.write_time); - CHECK_NTTIMES_EQUAL(out.change_time); - CHECK_EQUAL(out.alloc_size); - CHECK_EQUAL(out.size); - CHECK_ATTRIB(out.file_attr); - - REMOVE_HANDLE(in.file.handle); - - return true; -} - -/* - generate read operations -*/ -static bool handler_read(int instance) -{ - struct smb2_read parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.reserved = gen_reserved8(); - parm[0].in.length = gen_io_count(); - parm[0].in.offset = gen_offset(); - parm[0].in.min_count = gen_io_count(); - parm[0].in.channel = gen_bits_mask2(0x0, 0xFFFFFFFF); - parm[0].in.remaining = gen_bits_mask2(0x0, 0xFFFFFFFF); - parm[0].in.channel_offset = gen_bits_mask2(0x0, 0xFFFF); - parm[0].in.channel_length = gen_bits_mask2(0x0, 0xFFFF); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_read(tree, current_op.mem_ctx, &parm[i])); - - CHECK_EQUAL(out.remaining); - CHECK_EQUAL(out.reserved); - CHECK_EQUAL(out.data.length); - - return true; -} - -/* - generate write operations -*/ -static bool handler_write(int instance) -{ - struct smb2_write parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.offset = gen_offset(); - parm[0].in.unknown1 = gen_bits_mask2(0, 0xFFFFFFFF); - parm[0].in.unknown2 = gen_bits_mask2(0, 0xFFFFFFFF); - parm[0].in.data = data_blob_talloc(current_op.mem_ctx, NULL, - gen_io_count()); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_write(tree, &parm[i])); - - CHECK_EQUAL(out._pad); - CHECK_EQUAL(out.nwritten); - CHECK_EQUAL(out.unknown1); - - return true; -} - -/* - generate lockingx operations -*/ -static bool handler_lock(int instance) -{ - struct smb2_lock parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - int n; - - parm[0].level = RAW_LOCK_LOCKX; - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.lock_count = gen_lock_count(); - parm[0].in.reserved = gen_reserved32(); - - parm[0].in.locks = talloc_array(current_op.mem_ctx, - struct smb2_lock_element, - parm[0].in.lock_count); - for (n=0;n<parm[0].in.lock_count;n++) { - parm[0].in.locks[n].offset = gen_offset(); - parm[0].in.locks[n].length = gen_io_count(); - /* don't yet cope with async replies */ - parm[0].in.locks[n].flags = gen_lock_flags() | - SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; - parm[0].in.locks[n].reserved = gen_bits_mask2(0x0, 0xFFFFFFFF); - } - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_lock(tree, &parm[i])); - - return true; -} - -/* - generate flush operations -*/ -static bool handler_flush(int instance) -{ - struct smb2_flush parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - ZERO_STRUCT(parm[0]); - parm[0].in.file.handle.data[0] = gen_fnum(instance); - parm[0].in.reserved1 = gen_reserved16(); - parm[0].in.reserved2 = gen_reserved32(); - - GEN_COPY_PARM; - GEN_SET_FNUM(in.file.handle); - GEN_CALL(smb2_flush(tree, &parm[i])); - - CHECK_EQUAL(out.reserved); - - return true; -} - -/* - generate echo operations -*/ -static bool handler_echo(int instance) -{ - NTSTATUS status[NSERVERS]; - - GEN_CALL(smb2_keepalive(tree->session->transport)); - - return true; -} - - - -/* - generate a fileinfo query structure -*/ -static void gen_fileinfo(int instance, union smb_fileinfo *info) -{ - int i; - #define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v} - struct { - enum smb_fileinfo_level level; - const char *name; - } levels[] = { - LVL(BASIC_INFORMATION), - LVL(STANDARD_INFORMATION), LVL(INTERNAL_INFORMATION), LVL(EA_INFORMATION), - LVL(ACCESS_INFORMATION), LVL(NAME_INFORMATION), LVL(POSITION_INFORMATION), - LVL(MODE_INFORMATION), LVL(ALIGNMENT_INFORMATION), LVL(SMB2_ALL_INFORMATION), - LVL(ALT_NAME_INFORMATION), LVL(STREAM_INFORMATION), LVL(COMPRESSION_INFORMATION), - LVL(NETWORK_OPEN_INFORMATION), LVL(ATTRIBUTE_TAG_INFORMATION), - LVL(SMB2_ALL_EAS), LVL(SMB2_ALL_INFORMATION), - }; - do { - i = gen_int_range(0, ARRAY_SIZE(levels)-1); - } while (ignore_pattern(levels[i].name)); - - info->generic.level = levels[i].level; -} - -/* - compare returned fileinfo structures -*/ -static bool cmp_fileinfo(int instance, - union smb_fileinfo parm[NSERVERS], - NTSTATUS status[NSERVERS]) -{ - int i; - - switch (parm[0].generic.level) { - case RAW_FILEINFO_GENERIC: - return false; - - /* SMB1 specific values */ - case RAW_FILEINFO_GETATTR: - case RAW_FILEINFO_GETATTRE: - case RAW_FILEINFO_STANDARD: - case RAW_FILEINFO_EA_SIZE: - case RAW_FILEINFO_ALL_EAS: - case RAW_FILEINFO_IS_NAME_VALID: - case RAW_FILEINFO_BASIC_INFO: - case RAW_FILEINFO_STANDARD_INFO: - case RAW_FILEINFO_EA_INFO: - case RAW_FILEINFO_NAME_INFO: - case RAW_FILEINFO_ALL_INFO: - case RAW_FILEINFO_ALT_NAME_INFO: - case RAW_FILEINFO_STREAM_INFO: - case RAW_FILEINFO_COMPRESSION_INFO: - return false; - - case RAW_FILEINFO_BASIC_INFORMATION: - CHECK_NTTIMES_EQUAL(basic_info.out.create_time); - CHECK_NTTIMES_EQUAL(basic_info.out.access_time); - CHECK_NTTIMES_EQUAL(basic_info.out.write_time); - CHECK_NTTIMES_EQUAL(basic_info.out.change_time); - CHECK_ATTRIB(basic_info.out.attrib); - break; - - case RAW_FILEINFO_STANDARD_INFORMATION: - CHECK_EQUAL(standard_info.out.alloc_size); - CHECK_EQUAL(standard_info.out.size); - CHECK_EQUAL(standard_info.out.nlink); - CHECK_EQUAL(standard_info.out.delete_pending); - CHECK_EQUAL(standard_info.out.directory); - break; - - case RAW_FILEINFO_EA_INFORMATION: - CHECK_EQUAL(ea_info.out.ea_size); - break; - - case RAW_FILEINFO_NAME_INFORMATION: - CHECK_WSTR_EQUAL(name_info.out.fname); - break; - - case RAW_FILEINFO_ALT_NAME_INFORMATION: - CHECK_WSTR_EQUAL(alt_name_info.out.fname); - break; - - case RAW_FILEINFO_STREAM_INFORMATION: - CHECK_EQUAL(stream_info.out.num_streams); - for (i=0;i<parm[0].stream_info.out.num_streams;i++) { - CHECK_EQUAL(stream_info.out.streams[i].size); - CHECK_EQUAL(stream_info.out.streams[i].alloc_size); - CHECK_WSTR_EQUAL(stream_info.out.streams[i].stream_name); - } - break; - - case RAW_FILEINFO_COMPRESSION_INFORMATION: - CHECK_EQUAL(compression_info.out.compressed_size); - CHECK_EQUAL(compression_info.out.format); - CHECK_EQUAL(compression_info.out.unit_shift); - CHECK_EQUAL(compression_info.out.chunk_shift); - CHECK_EQUAL(compression_info.out.cluster_shift); - break; - - case RAW_FILEINFO_INTERNAL_INFORMATION: - CHECK_EQUAL(internal_information.out.file_id); - break; - - case RAW_FILEINFO_ACCESS_INFORMATION: - CHECK_EQUAL(access_information.out.access_flags); - break; - - case RAW_FILEINFO_POSITION_INFORMATION: - CHECK_EQUAL(position_information.out.position); - break; - - case RAW_FILEINFO_MODE_INFORMATION: - CHECK_EQUAL(mode_information.out.mode); - break; - - case RAW_FILEINFO_ALIGNMENT_INFORMATION: - CHECK_EQUAL(alignment_information.out.alignment_requirement); - break; - - case RAW_FILEINFO_NETWORK_OPEN_INFORMATION: - CHECK_NTTIMES_EQUAL(network_open_information.out.create_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.access_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.write_time); - CHECK_NTTIMES_EQUAL(network_open_information.out.change_time); - CHECK_EQUAL(network_open_information.out.alloc_size); - CHECK_EQUAL(network_open_information.out.size); - CHECK_ATTRIB(network_open_information.out.attrib); - break; - - case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION: - CHECK_ATTRIB(attribute_tag_information.out.attrib); - CHECK_EQUAL(attribute_tag_information.out.reparse_tag); - break; - - case RAW_FILEINFO_ALL_INFORMATION: - case RAW_FILEINFO_SMB2_ALL_INFORMATION: - CHECK_NTTIMES_EQUAL(all_info2.out.create_time); - CHECK_NTTIMES_EQUAL(all_info2.out.access_time); - CHECK_NTTIMES_EQUAL(all_info2.out.write_time); - CHECK_NTTIMES_EQUAL(all_info2.out.change_time); - CHECK_ATTRIB(all_info2.out.attrib); - CHECK_EQUAL(all_info2.out.unknown1); - CHECK_EQUAL(all_info2.out.alloc_size); - CHECK_EQUAL(all_info2.out.size); - CHECK_EQUAL(all_info2.out.nlink); - CHECK_EQUAL(all_info2.out.delete_pending); - CHECK_EQUAL(all_info2.out.directory); - CHECK_EQUAL(all_info2.out.file_id); - CHECK_EQUAL(all_info2.out.ea_size); - CHECK_EQUAL(all_info2.out.access_mask); - CHECK_EQUAL(all_info2.out.position); - CHECK_EQUAL(all_info2.out.mode); - CHECK_EQUAL(all_info2.out.alignment_requirement); - CHECK_WSTR_EQUAL(all_info2.out.fname); - break; - - case RAW_FILEINFO_SMB2_ALL_EAS: - CHECK_EQUAL(all_eas.out.num_eas); - for (i=0;i<parm[0].all_eas.out.num_eas;i++) { - CHECK_EQUAL(all_eas.out.eas[i].flags); - CHECK_WSTR_EQUAL(all_eas.out.eas[i].name); - CHECK_BLOB_EQUAL(all_eas.out.eas[i].value); - } - break; - - /* Unhandled levels */ - - case RAW_FILEINFO_SEC_DESC: - case RAW_FILEINFO_EA_LIST: - case RAW_FILEINFO_UNIX_BASIC: - case RAW_FILEINFO_UNIX_LINK: - case RAW_FILEINFO_UNIX_INFO2: - break; - } - - return true; -} - -/* - generate qfileinfo operations -*/ -static bool handler_qfileinfo(int instance) -{ - union smb_fileinfo parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].generic.in.file.handle.data[0] = gen_fnum(instance); - - gen_fileinfo(instance, &parm[0]); - - GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.handle); - GEN_CALL(smb2_getinfo_file(tree, current_op.mem_ctx, &parm[i])); - - return cmp_fileinfo(instance, parm, status); -} - - -/* - generate a fileinfo query structure -*/ -static void gen_setfileinfo(int instance, union smb_setfileinfo *info) -{ - int i; - #undef LVL - #define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v} - struct { - enum smb_setfileinfo_level level; - const char *name; - } levels[] = { - LVL(BASIC_INFORMATION), - LVL(RENAME_INFORMATION), LVL(DISPOSITION_INFORMATION), - LVL(POSITION_INFORMATION), LVL(MODE_INFORMATION), - LVL(ALLOCATION_INFORMATION), LVL(END_OF_FILE_INFORMATION), - LVL(1023), LVL(1025), LVL(1029), LVL(1032), LVL(1039), LVL(1040) - }; - do { - i = gen_int_range(0, ARRAY_SIZE(levels)-1); - } while (ignore_pattern(levels[i].name)); - - info->generic.level = levels[i].level; - - switch (info->generic.level) { - case RAW_SFILEINFO_SETATTR: - case RAW_SFILEINFO_SETATTRE: - case RAW_SFILEINFO_STANDARD: - case RAW_SFILEINFO_EA_SET: - case RAW_SFILEINFO_BASIC_INFO: - case RAW_SFILEINFO_DISPOSITION_INFO: - case RAW_SFILEINFO_END_OF_FILE_INFO: - case RAW_SFILEINFO_ALLOCATION_INFO: - break; - - case RAW_SFILEINFO_BASIC_INFORMATION: - info->basic_info.in.create_time = gen_nttime(); - info->basic_info.in.access_time = gen_nttime(); - info->basic_info.in.write_time = gen_nttime(); - info->basic_info.in.change_time = gen_nttime(); - info->basic_info.in.attrib = gen_attrib(); - break; - case RAW_SFILEINFO_DISPOSITION_INFORMATION: - info->disposition_info.in.delete_on_close = gen_bool(); - break; - case RAW_SFILEINFO_ALLOCATION_INFORMATION: - info->allocation_info.in.alloc_size = gen_alloc_size(); - break; - case RAW_SFILEINFO_END_OF_FILE_INFORMATION: - info->end_of_file_info.in.size = gen_offset(); - break; - case RAW_SFILEINFO_RENAME_INFORMATION: - case RAW_SFILEINFO_RENAME_INFORMATION_SMB2: - info->rename_information.in.overwrite = gen_bool(); - info->rename_information.in.root_fid = gen_root_fid(instance); - info->rename_information.in.new_name = gen_fname_open(instance); - break; - case RAW_SFILEINFO_POSITION_INFORMATION: - info->position_information.in.position = gen_offset(); - break; - case RAW_SFILEINFO_MODE_INFORMATION: - info->mode_information.in.mode = gen_bits_mask(0xFFFFFFFF); - break; - case RAW_SFILEINFO_GENERIC: - case RAW_SFILEINFO_SEC_DESC: - case RAW_SFILEINFO_1023: - case RAW_SFILEINFO_1025: - case RAW_SFILEINFO_1029: - case RAW_SFILEINFO_1032: - case RAW_SFILEINFO_1039: - case RAW_SFILEINFO_1040: - case RAW_SFILEINFO_UNIX_BASIC: - case RAW_SFILEINFO_UNIX_INFO2: - case RAW_SFILEINFO_UNIX_LINK: - case RAW_SFILEINFO_UNIX_HLINK: - /* Untested */ - break; - } -} - -/* - generate setfileinfo operations -*/ -static bool handler_sfileinfo(int instance) -{ - union smb_setfileinfo parm[NSERVERS]; - NTSTATUS status[NSERVERS]; - - parm[0].generic.in.file.fnum = gen_fnum(instance); - - gen_setfileinfo(instance, &parm[0]); - - GEN_COPY_PARM; - GEN_SET_FNUM(generic.in.file.handle); - GEN_CALL(smb2_setinfo_file(tree, &parm[i])); - - return true; -} - -/* - wipe any relevant files -*/ -static void wipe_files(void) -{ - int i; - NTSTATUS status; - - if (options.skip_cleanup) { - return; - } - - for (i=0;i<NSERVERS;i++) { - int n = smb2_deltree(servers[i].tree[0], "gentest"); - if (n == -1) { - printf("Failed to wipe tree on server %d\n", i); - exit(1); - } - status = smb2_util_mkdir(servers[i].tree[0], "gentest"); - if (NT_STATUS_IS_ERR(status)) { - printf("Failed to create gentest on server %d - %s\n", i, nt_errstr(status)); - exit(1); - } - if (n > 0) { - printf("Deleted %d files on server %d\n", n, i); - } - } -} - -/* - dump the current seeds - useful for continuing a backtrack -*/ -static void dump_seeds(void) -{ - int i; - FILE *f; - - if (!options.seeds_file) { - return; - } - f = fopen("seeds.tmp", "w"); - if (!f) return; - - for (i=0;i<options.numops;i++) { - fprintf(f, "%u\n", op_parms[i].seed); - } - fclose(f); - rename("seeds.tmp", options.seeds_file); -} - - - -/* - the list of top-level operations that we will generate -*/ -static struct { - const char *name; - bool (*handler)(int instance); - int count, success_count; -} gen_ops[] = { - {"CREATE", handler_create}, - {"CLOSE", handler_close}, - {"READ", handler_read}, - {"WRITE", handler_write}, - {"LOCK", handler_lock}, - {"FLUSH", handler_flush}, - {"ECHO", handler_echo}, - {"QFILEINFO", handler_qfileinfo}, - {"SFILEINFO", handler_sfileinfo}, -}; - - -/* - run the test with the current set of op_parms parameters - return the number of operations that completed successfully -*/ -static int run_test(struct event_context *ev, struct loadparm_context *lp_ctx) -{ - int op, i; - - if (!connect_servers(ev, lp_ctx)) { - printf("Failed to connect to servers\n"); - exit(1); - } - - dump_seeds(); - - /* wipe any leftover files from old runs */ - wipe_files(); - - /* reset the open handles array */ - memset(open_handles, 0, options.max_open_handles * sizeof(open_handles[0])); - num_open_handles = 0; - - for (i=0;i<ARRAY_SIZE(gen_ops);i++) { - gen_ops[i].count = 0; - gen_ops[i].success_count = 0; - } - - for (op=0; op<options.numops; op++) { - int instance, which_op; - bool ret; - - if (op_parms[op].disabled) continue; - - srandom(op_parms[op].seed); - - instance = gen_int_range(0, NINSTANCES-1); - - /* generate a non-ignored operation */ - do { - which_op = gen_int_range(0, ARRAY_SIZE(gen_ops)-1); - } while (ignore_pattern(gen_ops[which_op].name)); - - DEBUG(3,("Generating op %s on instance %d\n", - gen_ops[which_op].name, instance)); - - current_op.seed = op_parms[op].seed; - current_op.opnum = op; - current_op.name = gen_ops[which_op].name; - current_op.status = NT_STATUS_OK; - current_op.mem_ctx = talloc_named(NULL, 0, "%s", current_op.name); - - ret = gen_ops[which_op].handler(instance); - - talloc_free(current_op.mem_ctx); - - gen_ops[which_op].count++; - if (NT_STATUS_IS_OK(current_op.status)) { - gen_ops[which_op].success_count++; - } - - if (!ret) { - printf("Failed at operation %d - %s\n", - op, gen_ops[which_op].name); - return op; - } - - if (op % 100 == 0) { - printf("%d\n", op); - } - } - - for (i=0;i<ARRAY_SIZE(gen_ops);i++) { - printf("Op %-10s got %d/%d success\n", - gen_ops[i].name, - gen_ops[i].success_count, - gen_ops[i].count); - } - - return op; -} - -/* - perform a backtracking analysis of the minimal set of operations - to generate an error -*/ -static void backtrack_analyze(struct event_context *ev, - struct loadparm_context *lp_ctx) -{ - int chunk, ret; - - chunk = options.numops / 2; - - do { - int base; - for (base=0; - chunk > 0 && base+chunk < options.numops && options.numops > 1; ) { - int i, max; - - chunk = MIN(chunk, options.numops / 2); - - /* mark this range as disabled */ - max = MIN(options.numops, base+chunk); - for (i=base;i<max; i++) { - op_parms[i].disabled = true; - } - printf("Testing %d ops with %d-%d disabled\n", - options.numops, base, max-1); - ret = run_test(ev, lp_ctx); - printf("Completed %d of %d ops\n", ret, options.numops); - for (i=base;i<max; i++) { - op_parms[i].disabled = false; - } - if (ret == options.numops) { - /* this chunk is needed */ - base += chunk; - } else if (ret < base) { - printf("damn - inconsistent errors! found early error\n"); - options.numops = ret+1; - base = 0; - } else { - /* it failed - this chunk isn't needed for a failure */ - memmove(&op_parms[base], &op_parms[max], - sizeof(op_parms[0]) * (options.numops - max)); - options.numops = (ret+1) - (max - base); - } - } - - if (chunk == 2) { - chunk = 1; - } else { - chunk *= 0.4; - } - - if (options.analyze_continuous && chunk == 0 && options.numops != 1) { - chunk = 1; - } - } while (chunk > 0); - - printf("Reduced to %d ops\n", options.numops); - ret = run_test(ev, lp_ctx); - if (ret != options.numops - 1) { - printf("Inconsistent result? ret=%d numops=%d\n", ret, options.numops); - } -} - -/* - start the main gentest process -*/ -static bool start_gentest(struct event_context *ev, - struct loadparm_context *lp_ctx) -{ - int op; - int ret; - - /* allocate the open_handles array */ - open_handles = calloc(options.max_open_handles, sizeof(open_handles[0])); - - srandom(options.seed); - op_parms = calloc(options.numops, sizeof(op_parms[0])); - - /* generate the seeds - after this everything is deterministic */ - if (options.use_preset_seeds) { - int numops; - char **preset = file_lines_load(options.seeds_file, &numops, NULL); - if (!preset) { - printf("Failed to load %s - %s\n", options.seeds_file, strerror(errno)); - exit(1); - } - if (numops < options.numops) { - options.numops = numops; - } - for (op=0;op<options.numops;op++) { - if (!preset[op]) { - printf("Not enough seeds in %s\n", options.seeds_file); - exit(1); - } - op_parms[op].seed = atoi(preset[op]); - } - printf("Loaded %d seeds from %s\n", options.numops, options.seeds_file); - } else { - for (op=0; op<options.numops; op++) { - op_parms[op].seed = random(); - } - } - - ret = run_test(ev, lp_ctx); - - if (ret != options.numops && options.analyze) { - options.numops = ret+1; - backtrack_analyze(ev, lp_ctx); - } else if (options.analyze_always) { - backtrack_analyze(ev, lp_ctx); - } else if (options.analyze_continuous) { - while (run_test(ev, lp_ctx) == options.numops) ; - } - - return ret == options.numops; -} - - -static void usage(poptContext pc) -{ - printf( -"Usage:\n\ - gentest //server1/share1 //server2/share2 [options..]\n\ -"); - poptPrintUsage(pc, stdout, 0); -} - -/** - split a UNC name into server and share names -*/ -static bool split_unc_name(const char *unc, char **server, char **share) -{ - char *p = strdup(unc); - if (!p) return false; - all_string_sub(p, "\\", "/", 0); - if (strncmp(p, "//", 2) != 0) return false; - - (*server) = p+2; - p = strchr(*server, '/'); - if (!p) return false; - - *p = 0; - (*share) = p+1; - - return true; -} - - - -/**************************************************************************** - main program -****************************************************************************/ - int main(int argc, char *argv[]) -{ - int opt; - int i, username_count=0; - bool ret; - char *ignore_file=NULL; - struct event_context *ev; - struct loadparm_context *lp_ctx; - poptContext pc; - int argc_new; - char **argv_new; - enum {OPT_UNCLIST=1000}; - struct poptOption long_options[] = { - POPT_AUTOHELP - {"seed", 0, POPT_ARG_INT, &options.seed, 0, "Seed to use for randomizer", NULL}, - {"num-ops", 0, POPT_ARG_INT, &options.numops, 0, "num ops", NULL}, - {"oplocks", 0, POPT_ARG_NONE, &options.use_oplocks,0, "use oplocks", NULL}, - {"showall", 0, POPT_ARG_NONE, &options.showall, 0, "display all operations", NULL}, - {"analyse", 0, POPT_ARG_NONE, &options.analyze, 0, "do backtrack analysis", NULL}, - {"analysealways", 0, POPT_ARG_NONE, &options.analyze_always, 0, "analysis always", NULL}, - {"analysecontinuous", 0, POPT_ARG_NONE, &options.analyze_continuous, 0, "analysis continuous", NULL}, - {"ignore", 0, POPT_ARG_STRING, &ignore_file, 0, "ignore from file", NULL}, - {"preset", 0, POPT_ARG_NONE, &options.use_preset_seeds, 0, "use preset seeds", NULL}, - {"fast", 0, POPT_ARG_NONE, &options.fast_reconnect, 0, "use fast reconnect", NULL}, - {"unclist", 0, POPT_ARG_STRING, NULL, OPT_UNCLIST, "unclist", NULL}, - {"seedsfile", 0, POPT_ARG_STRING, &options.seeds_file, 0, "seed file", NULL}, - { "user", 'U', POPT_ARG_STRING, NULL, 'U', "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" }, - {"maskindexing", 0, POPT_ARG_NONE, &options.mask_indexing, 0, "mask out the indexed file attrib", NULL}, - {"noeas", 0, POPT_ARG_NONE, &options.no_eas, 0, "don't use extended attributes", NULL}, - {"skip-cleanup", 0, POPT_ARG_NONE, &options.skip_cleanup, 0, "don't delete files at start", NULL}, - {"valid", 0, POPT_ARG_NONE, &options.valid, 0, "generate only valid fields", NULL}, - POPT_COMMON_SAMBA - POPT_COMMON_CONNECTION - POPT_COMMON_CREDENTIALS - POPT_COMMON_VERSION - { NULL } - }; - - memset(&bad_smb2_handle, 0xFF, sizeof(bad_smb2_handle)); - - setlinebuf(stdout); - options.seed = time(NULL); - options.numops = 1000; - options.max_open_handles = 20; - options.seeds_file = "gentest_seeds.dat"; - - pc = poptGetContext("gentest", argc, (const char **) argv, long_options, - POPT_CONTEXT_KEEP_FIRST); - - poptSetOtherOptionHelp(pc, "<unc1> <unc2>"); - - lp_ctx = cmdline_lp_ctx; - servers[0].credentials = cli_credentials_init(talloc_autofree_context()); - servers[1].credentials = cli_credentials_init(talloc_autofree_context()); - cli_credentials_guess(servers[0].credentials, lp_ctx); - cli_credentials_guess(servers[1].credentials, lp_ctx); - - while((opt = poptGetNextOpt(pc)) != -1) { - switch (opt) { - case OPT_UNCLIST: - lp_set_cmdline(cmdline_lp_ctx, "torture:unclist", poptGetOptArg(pc)); - break; - case 'U': - if (username_count == 2) { - usage(pc); - exit(1); - } - cli_credentials_parse_string(servers[username_count].credentials, poptGetOptArg(pc), CRED_SPECIFIED); - username_count++; - break; - } - } - - if (ignore_file) { - options.ignore_patterns = file_lines_load(ignore_file, NULL, NULL); - } - - argv_new = discard_const_p(char *, poptGetArgs(pc)); - argc_new = argc; - for (i=0; i<argc; i++) { - if (argv_new[i] == NULL) { - argc_new = i; - break; - } - } - - if (!(argc_new >= 3)) { - usage(pc); - exit(1); - } - - setlinebuf(stdout); - - setup_logging("gentest", DEBUG_STDOUT); - - if (argc < 3 || argv[1][0] == '-') { - usage(pc); - exit(1); - } - - setup_logging(argv[0], DEBUG_STDOUT); - - for (i=0;i<NSERVERS;i++) { - const char *share = argv[1+i]; - if (!split_unc_name(share, &servers[i].server_name, &servers[i].share_name)) { - printf("Invalid share name '%s'\n", share); - return -1; - } - } - - if (username_count == 0) { - usage(pc); - return -1; - } - if (username_count == 1) { - servers[1].credentials = servers[0].credentials; - } - - printf("seed=%u\n", options.seed); - - ev = event_context_init(talloc_autofree_context()); - - gensec_init(lp_ctx); - - ret = start_gentest(ev, lp_ctx); - - if (ret) { - printf("gentest completed - no errors\n"); - } else { - printf("gentest failed\n"); - } - - return ret?0:-1; -} diff --git a/source4/torture/smb2/create.c b/source4/torture/smb2/create.c index 733b2c2e8a..c23ff8b8ce 100644 --- a/source4/torture/smb2/create.c +++ b/source4/torture/smb2/create.c @@ -41,7 +41,7 @@ if (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; \ + return false; \ }} while (0) /* @@ -52,7 +52,8 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre struct smb2_create io; NTSTATUS status; TALLOC_CTX *tmp_ctx = talloc_new(tree); - uint32_t access_mask, file_attributes, denied_mask; + uint32_t access_mask, file_attributes, file_attributes_set, denied_mask; + union smb_fileinfo q; ZERO_STRUCT(io); io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; @@ -115,7 +116,8 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre for (i=0;i<32;i++) { io.in.desired_access = 1<<i; status = smb2_create(tree, tmp_ctx, &io); - if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) || + NT_STATUS_EQUAL(status, NT_STATUS_PRIVILEGE_NOT_HELD)) { access_mask |= io.in.desired_access; } else { CHECK_STATUS(status, NT_STATUS_OK); @@ -131,6 +133,7 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre io.in.desired_access = SEC_FLAG_MAXIMUM_ALLOWED; io.in.file_attributes = 0; file_attributes = 0; + file_attributes_set = 0; denied_mask = 0; { int i; @@ -146,12 +149,14 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); + file_attributes_set |= io.out.file_attr; } } } CHECK_EQUAL(file_attributes, 0xffff8048); CHECK_EQUAL(denied_mask, 0x4000); + CHECK_EQUAL(file_attributes_set, 0x00001127); smb2_deltree(tree, FNAME); @@ -177,6 +182,20 @@ static bool test_create_gentest(struct torture_context *torture, struct smb2_tre status = smb2_create(tree, tmp_ctx, &io); CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + io.in.fname = FNAME; + io.in.file_attributes = 0; + io.in.desired_access = SEC_FILE_READ_DATA | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA; + io.in.query_maximal_access = true; + status = smb2_create(tree, tmp_ctx, &io); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); + + q.access_information.level = RAW_FILEINFO_ACCESS_INFORMATION; + q.access_information.in.file.handle = io.out.file.handle; + status = smb2_getinfo_file(tree, tmp_ctx, &q); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(q.access_information.out.access_flags, io.in.desired_access); + talloc_free(tmp_ctx); smb2_deltree(tree, FNAME); @@ -237,6 +256,7 @@ static bool test_create_blob(struct torture_context *torture, struct smb2_tree * io.in.query_maximal_access = true; status = smb2_create(tree, tmp_ctx, &io); CHECK_STATUS(status, NT_STATUS_OK); + CHECK_EQUAL(io.out.maximal_access, 0x001f01ff); status = smb2_util_close(tree, io.out.file.handle); CHECK_STATUS(status, NT_STATUS_OK); diff --git a/source4/torture/smb2/lock.c b/source4/torture/smb2/lock.c index 5a36ac3eae..35ad839610 100644 --- a/source4/torture/smb2/lock.c +++ b/source4/torture/smb2/lock.c @@ -106,7 +106,11 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree lck.in.reserved = 0x123ab3; status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); lck.in.reserved = 0x123ab4; @@ -115,7 +119,11 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree lck.in.reserved = 0x123ab5; status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); lck.in.lock_count = 0x0001; @@ -133,14 +141,22 @@ static bool test_valid_request(struct torture_context *torture, struct smb2_tree CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); status = smb2_lock(tree, &lck); CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); status = smb2_lock(tree, &lck); - CHECK_STATUS(status, NT_STATUS_OK); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } CHECK_VALUE(lck.out.reserved, 0); el[0].flags = 0x00000000; @@ -473,6 +489,51 @@ static bool test_lock_rw_exclusiv(struct torture_context *torture, struct smb2_t return test_lock_read_write(torture, tree, &s); } + +static bool test_lock_auto_unlock(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[200]; + struct smb2_lock lck; + struct smb2_lock_element el[2]; + + ZERO_STRUCT(buf); + + status = torture_smb2_testfile(tree, "autounlock.txt", &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(lck); + lck.in.locks = el; + lck.in.lock_count = 0x0001; + lck.in.file.handle = h; + el[0].offset = 0; + el[0].length = 1; + el[0].flags = SMB2_LOCK_FLAG_EXCLUSIVE | SMB2_LOCK_FLAG_FAIL_IMMEDIATELY; + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_lock(tree, &lck); + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + + status = smb2_lock(tree, &lck); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_LOCK_NOT_GRANTED); + } + + + +done: + return ret; +} + + /* basic testing of SMB2 locking */ struct torture_suite *torture_smb2_lock_init(void) @@ -483,6 +544,7 @@ struct torture_suite *torture_smb2_lock_init(void) torture_suite_add_1smb2_test(suite, "RW-NONE", test_lock_rw_none); torture_suite_add_1smb2_test(suite, "RW-SHARED", test_lock_rw_shared); torture_suite_add_1smb2_test(suite, "RW-EXCLUSIV", test_lock_rw_exclusiv); + torture_suite_add_1smb2_test(suite, "AUTO-UNLOCK", test_lock_auto_unlock); suite->description = talloc_strdup(suite, "SMB2-LOCK tests"); diff --git a/source4/torture/smb2/read.c b/source4/torture/smb2/read.c index b3c13ad667..548bd1ce61 100644 --- a/source4/torture/smb2/read.c +++ b/source4/torture/smb2/read.c @@ -44,6 +44,9 @@ goto done; \ }} while (0) +#define FNAME "smb2_readtest.dat" +#define DNAME "smb2_readtest.dir" + static bool test_read_eof(struct torture_context *torture, struct smb2_tree *tree) { bool ret = true; @@ -55,7 +58,7 @@ static bool test_read_eof(struct torture_context *torture, struct smb2_tree *tre ZERO_STRUCT(buf); - status = torture_smb2_testfile(tree, "lock1.txt", &h); + status = torture_smb2_testfile(tree, FNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); @@ -139,7 +142,7 @@ static bool test_read_position(struct torture_context *torture, struct smb2_tree ZERO_STRUCT(buf); - status = torture_smb2_testfile(tree, "lock1.txt", &h); + status = torture_smb2_testfile(tree, FNAME, &h); CHECK_STATUS(status, NT_STATUS_OK); status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); @@ -172,6 +175,58 @@ done: return ret; } +static bool test_read_dir(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + struct smb2_read rd; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + status = torture_smb2_testdir(tree, DNAME, &h); + if (!NT_STATUS_IS_OK(status)) { + printf(__location__ " Unable to create test directory '%s' - %s\n", DNAME, nt_errstr(status)); + return false; + } + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.min_count = 11; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + + rd.in.length = 0; + rd.in.min_count = 2592; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + + rd.in.length = 0; + rd.in.min_count = 0; + rd.in.channel = 0; + status = smb2_read(tree, tmp_ctx, &rd); + if (torture_setting_bool(torture, "windows", false)) { + CHECK_STATUS(status, NT_STATUS_OK); + } else { + CHECK_STATUS(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } + +done: + talloc_free(tmp_ctx); + return ret; +} + + /* basic testing of SMB2 read */ @@ -181,6 +236,7 @@ struct torture_suite *torture_smb2_read_init(void) torture_suite_add_1smb2_test(suite, "EOF", test_read_eof); torture_suite_add_1smb2_test(suite, "POSITION", test_read_position); + torture_suite_add_1smb2_test(suite, "DIR", test_read_dir); suite->description = talloc_strdup(suite, "SMB2-READ tests"); diff --git a/source4/torture/smb2/scan.c b/source4/torture/smb2/scan.c index 889d343a49..1ce796be4d 100644 --- a/source4/torture/smb2/scan.c +++ b/source4/torture/smb2/scan.c @@ -203,17 +203,20 @@ bool torture_smb2_scan(struct torture_context *torture) NTSTATUS status; int opcode; struct smb2_request *req; + struct smbcli_options options; + + lp_smbcli_options(torture->lp_ctx, &options); status = smb2_connect(mem_ctx, host, share, lp_resolve_context(torture->lp_ctx), credentials, &tree, - torture->ev); + torture->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Connection failed - %s\n", nt_errstr(status)); return false; } - tree->session->transport->options.timeout = 3; + tree->session->transport->options.request_timeout = 3; for (opcode=0;opcode<1000;opcode++) { req = smb2_request_init_tree(tree, opcode, 2, false, 0); @@ -224,12 +227,12 @@ bool torture_smb2_scan(struct torture_context *torture) status = smb2_connect(mem_ctx, host, share, lp_resolve_context(torture->lp_ctx), credentials, &tree, - torture->ev); + torture->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Connection failed - %s\n", nt_errstr(status)); return false; } - tree->session->transport->options.timeout = 3; + tree->session->transport->options.request_timeout = 3; } else { status = smb2_request_destroy(req); printf("active opcode %4d gave status %s\n", opcode, nt_errstr(status)); diff --git a/source4/torture/smb2/setinfo.c b/source4/torture/smb2/setinfo.c index 685bb5a47b..7a912b4989 100644 --- a/source4/torture/smb2/setinfo.c +++ b/source4/torture/smb2/setinfo.c @@ -175,6 +175,10 @@ bool torture_smb2_setinfo(struct torture_context *torture) CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); CHECK_VALUE(SMB2_ALL_INFORMATION, all_info2, attrib, FILE_ATTRIBUTE_HIDDEN); + printf("can't change a file to a directory\n"); + sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_DIRECTORY; + CHECK_CALL(BASIC_INFORMATION, NT_STATUS_INVALID_PARAMETER); + printf("restore attribute\n"); sfinfo.basic_info.in.attrib = FILE_ATTRIBUTE_NORMAL; CHECK_CALL(BASIC_INFORMATION, NT_STATUS_OK); diff --git a/source4/torture/smb2/util.c b/source4/torture/smb2/util.c index af4f345104..3a437acbab 100644 --- a/source4/torture/smb2/util.c +++ b/source4/torture/smb2/util.c @@ -270,11 +270,14 @@ bool torture_smb2_connection(struct torture_context *tctx, struct smb2_tree **tr const char *host = torture_setting_string(tctx, "host", NULL); const char *share = torture_setting_string(tctx, "share", NULL); struct cli_credentials *credentials = cmdline_credentials; + struct smbcli_options options; + + lp_smbcli_options(tctx->lp_ctx, &options); status = smb2_connect(tctx, host, share, lp_resolve_context(tctx->lp_ctx), credentials, tree, - tctx->ev); + tctx->ev, &options); if (!NT_STATUS_IS_OK(status)) { printf("Failed to connect to SMB2 share \\\\%s\\%s - %s\n", host, share, nt_errstr(status)); |