summaryrefslogtreecommitdiff
path: root/source4/libcli/smb2
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/smb2')
-rw-r--r--source4/libcli/smb2/break.c74
-rw-r--r--source4/libcli/smb2/config.mk2
-rw-r--r--source4/libcli/smb2/connect.c7
-rw-r--r--source4/libcli/smb2/create.c94
-rw-r--r--source4/libcli/smb2/read.c13
-rw-r--r--source4/libcli/smb2/request.c8
-rw-r--r--source4/libcli/smb2/smb2.h11
-rw-r--r--source4/libcli/smb2/transport.c43
8 files changed, 217 insertions, 35 deletions
diff --git a/source4/libcli/smb2/break.c b/source4/libcli/smb2/break.c
new file mode 100644
index 0000000000..fe0cceb829
--- /dev/null
+++ b/source4/libcli/smb2/break.c
@@ -0,0 +1,74 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SMB2 client oplock break handling
+
+ Copyright (C) Stefan Metzmacher 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/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+/*
+ send a break request
+*/
+struct smb2_request *smb2_break_send(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req;
+
+ req = smb2_request_init_tree(tree, SMB2_OP_BREAK, 0x18, false, 0);
+ if (req == NULL) return NULL;
+
+ SCVAL(req->out.body, 0x02, io->in.oplock_level);
+ SCVAL(req->out.body, 0x03, io->in.reserved);
+ SIVAL(req->out.body, 0x04, io->in.reserved2);
+ smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
+
+ smb2_transport_send(req);
+
+ return req;
+}
+
+
+/*
+ recv a break reply
+*/
+NTSTATUS smb2_break_recv(struct smb2_request *req, struct smb2_break *io)
+{
+ if (!smb2_request_receive(req) ||
+ !smb2_request_is_ok(req)) {
+ return smb2_request_destroy(req);
+ }
+
+ SMB2_CHECK_PACKET_RECV(req, 0x18, false);
+
+ io->out.oplock_level = CVAL(req->in.body, 0x02);
+ io->out.reserved = CVAL(req->in.body, 0x03);
+ io->out.reserved2 = IVAL(req->in.body, 0x04);
+ smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
+
+ return smb2_request_destroy(req);
+}
+
+/*
+ sync flush request
+*/
+NTSTATUS smb2_break(struct smb2_tree *tree, struct smb2_break *io)
+{
+ struct smb2_request *req = smb2_break_send(tree, io);
+ return smb2_break_recv(req, io);
+}
diff --git a/source4/libcli/smb2/config.mk b/source4/libcli/smb2/config.mk
index e95997db54..18f6245a3e 100644
--- a/source4/libcli/smb2/config.mk
+++ b/source4/libcli/smb2/config.mk
@@ -6,5 +6,5 @@ LIBCLI_SMB2_OBJ_FILES = $(addprefix libcli/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)
+ lock.o notify.o cancel.o keepalive.o break.o)
diff --git a/source4/libcli/smb2/connect.c b/source4/libcli/smb2/connect.c
index d68b85ad54..59d4e6ea2d 100644
--- a/source4/libcli/smb2/connect.c
+++ b/source4/libcli/smb2/connect.c
@@ -121,7 +121,7 @@ static void continue_socket(struct composite_context *creq)
struct smbcli_socket *sock;
struct smb2_transport *transport;
struct smb2_request *req;
- uint16_t dialects[1];
+ uint16_t dialects[2];
c->status = smbcli_sock_connect_recv(creq, state, &sock);
if (!composite_is_ok(c)) return;
@@ -130,11 +130,12 @@ static void continue_socket(struct composite_context *creq)
if (composite_nomem(transport, c)) return;
ZERO_STRUCT(state->negprot);
- state->negprot.in.dialect_count = 1;
+ state->negprot.in.dialect_count = 2;
state->negprot.in.security_mode = 0;
state->negprot.in.capabilities = 0;
unix_to_nt_time(&state->negprot.in.start_time, time(NULL));
- dialects[0] = SMB2_DIALECT_REVISION;
+ dialects[0] = 0;
+ dialects[1] = SMB2_DIALECT_REVISION;
state->negprot.in.dialects = dialects;
req = smb2_negprot_send(transport, &state->negprot);
diff --git a/source4/libcli/smb2/create.c b/source4/libcli/smb2/create.c
index 999c10ab08..6ac32a494f 100644
--- a/source4/libcli/smb2/create.c
+++ b/source4/libcli/smb2/create.c
@@ -28,30 +28,59 @@
/*
add a blob to a smb2_create attribute blob
*/
-NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
- const char *tag,
- DATA_BLOB add, bool last)
+static NTSTATUS smb2_create_blob_push_one(TALLOC_CTX *mem_ctx, DATA_BLOB *buffer,
+ const struct smb2_create_blob *blob,
+ bool last)
{
- uint32_t ofs = blob->length;
- size_t tag_length = strlen(tag);
- uint8_t pad = smb2_padding_size(add.length+tag_length, 8);
- if (!data_blob_realloc(mem_ctx, blob,
- blob->length + 0x14 + tag_length + add.length + pad))
+ uint32_t ofs = buffer->length;
+ size_t tag_length = strlen(blob->tag);
+ uint8_t pad = smb2_padding_size(blob->data.length+tag_length, 4);
+
+ if (!data_blob_realloc(mem_ctx, buffer,
+ buffer->length + 0x14 + tag_length + blob->data.length + pad))
return NT_STATUS_NO_MEMORY;
-
+
if (last) {
- SIVAL(blob->data, ofs+0x00, 0);
+ SIVAL(buffer->data, ofs+0x00, 0);
} else {
- SIVAL(blob->data, ofs+0x00, 0x14 + tag_length + add.length + pad);
+ SIVAL(buffer->data, ofs+0x00, 0x14 + tag_length + blob->data.length + pad);
}
- SSVAL(blob->data, ofs+0x04, 0x10); /* offset of tag */
- SIVAL(blob->data, ofs+0x06, tag_length); /* tag length */
- SSVAL(blob->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
- SIVAL(blob->data, ofs+0x0C, add.length);
- memcpy(blob->data+ofs+0x10, tag, tag_length);
- SIVAL(blob->data, ofs+0x10+tag_length, 0); /* pad? */
- memcpy(blob->data+ofs+0x14+tag_length, add.data, add.length);
- memset(blob->data+ofs+0x14+tag_length+add.length, 0, pad);
+ SSVAL(buffer->data, ofs+0x04, 0x10); /* offset of tag */
+ SIVAL(buffer->data, ofs+0x06, tag_length); /* tag length */
+ SSVAL(buffer->data, ofs+0x0A, 0x14 + tag_length); /* offset of data */
+ SIVAL(buffer->data, ofs+0x0C, blob->data.length);
+ memcpy(buffer->data+ofs+0x10, blob->tag, tag_length);
+ SIVAL(buffer->data, ofs+0x10+tag_length, 0); /* pad? */
+ memcpy(buffer->data+ofs+0x14+tag_length, blob->data.data, blob->data.length);
+ memset(buffer->data+ofs+0x14+tag_length+blob->data.length, 0, pad);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, struct smb2_create_blobs *b,
+ const char *tag, DATA_BLOB data)
+{
+ struct smb2_create_blob *array;
+
+ array = talloc_realloc(mem_ctx, b->blobs,
+ struct smb2_create_blob,
+ b->num_blobs + 1);
+ NT_STATUS_HAVE_NO_MEMORY(array);
+ b->blobs = array;
+
+ b->blobs[b->num_blobs].tag = talloc_strdup(b->blobs, tag);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].tag);
+
+ if (data.data) {
+ b->blobs[b->num_blobs].data = data_blob_talloc(b->blobs,
+ data.data,
+ data.length);
+ NT_STATUS_HAVE_NO_MEMORY(b->blobs[b->num_blobs].data.data);
+ } else {
+ b->blobs[b->num_blobs].data = data_blob(NULL, 0);
+ }
+
+ b->num_blobs += 1;
return NT_STATUS_OK;
}
@@ -64,6 +93,7 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
struct smb2_request *req;
NTSTATUS status;
DATA_BLOB blob = data_blob(NULL, 0);
+ uint32_t i;
req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, true, 0);
if (req == NULL) return NULL;
@@ -89,7 +119,8 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
DATA_BLOB b = data_blob_talloc(req, NULL,
ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas));
ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas);
- status = smb2_create_blob_add(req, &blob, SMB2_CREATE_TAG_EXTA, b, false);
+ status = smb2_create_blob_add(req, &io->in.blobs,
+ SMB2_CREATE_TAG_EXTA, b);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(req);
return NULL;
@@ -99,13 +130,30 @@ struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create
/* an empty MxAc tag seems to be used to ask the server to
return the maximum access mask allowed on the file */
- status = smb2_create_blob_add(req, &blob, SMB2_CREATE_TAG_MXAC,
- data_blob(NULL, 0), true);
-
+ status = smb2_create_blob_add(req, &io->in.blobs,
+ SMB2_CREATE_TAG_MXAC, data_blob(NULL, 0));
if (!NT_STATUS_IS_OK(status)) {
talloc_free(req);
return NULL;
}
+
+ for (i=0; i < io->in.blobs.num_blobs; i++) {
+ bool last = false;
+ const struct smb2_create_blob *c;
+
+ if ((i + 1) == io->in.blobs.num_blobs) {
+ last = true;
+ }
+
+ c = &io->in.blobs.blobs[i];
+ status = smb2_create_blob_push_one(req, &blob,
+ c, last);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
status = smb2_push_o32s32_blob(&req->out, 0x30, blob);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(req);
diff --git a/source4/libcli/smb2/read.c b/source4/libcli/smb2/read.c
index b61f918481..9d40e32a4d 100644
--- a/source4/libcli/smb2/read.c
+++ b/source4/libcli/smb2/read.c
@@ -33,12 +33,16 @@ struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io
req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, true, 0);
if (req == NULL) return NULL;
- SSVAL(req->out.body, 0x02, 0); /* pad */
+ SCVAL(req->out.body, 0x02, 0); /* pad */
+ SCVAL(req->out.body, 0x03, 0); /* reserved */
SIVAL(req->out.body, 0x04, io->in.length);
SBVAL(req->out.body, 0x08, io->in.offset);
smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
- SBVAL(req->out.body, 0x20, io->in.unknown1);
- SBVAL(req->out.body, 0x28, io->in.unknown2);
+ SIVAL(req->out.body, 0x20, io->in.min_count);
+ SIVAL(req->out.body, 0x24, io->in.channel);
+ SIVAL(req->out.body, 0x28, io->in.remaining);
+ SSVAL(req->out.body, 0x2C, io->in.channel_offset);
+ SSVAL(req->out.body, 0x2E, io->in.channel_length);
smb2_transport_send(req);
@@ -67,7 +71,8 @@ NTSTATUS smb2_read_recv(struct smb2_request *req,
return status;
}
- io->out.unknown1 = BVAL(req->in.body, 0x08);
+ io->out.remaining = IVAL(req->in.body, 0x08);
+ io->out.reserved = IVAL(req->in.body, 0x0C);
return smb2_request_destroy(req);
}
diff --git a/source4/libcli/smb2/request.c b/source4/libcli/smb2/request.c
index 2471fcaa4d..f52b0ceef2 100644
--- a/source4/libcli/smb2/request.c
+++ b/source4/libcli/smb2/request.c
@@ -211,10 +211,10 @@ bool smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
return false;
}
/* be careful with wraparound! */
- if (ptr < buf->body ||
- ptr >= buf->body + buf->body_size ||
+ if ((uintptr_t)ptr < (uintptr_t)buf->body ||
+ (uintptr_t)ptr >= (uintptr_t)buf->body + buf->body_size ||
size > buf->body_size ||
- ptr + size > buf->body + buf->body_size) {
+ (uintptr_t)ptr + size > (uintptr_t)buf->body + buf->body_size) {
return true;
}
return false;
@@ -669,7 +669,7 @@ NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
}
if (*str == 0) {
- blob.data = str;
+ blob.data = discard_const(str);
blob.length = 0;
return smb2_push_o16s16_blob(buf, ofs, blob);
}
diff --git a/source4/libcli/smb2/smb2.h b/source4/libcli/smb2/smb2.h
index 726df64090..ae66a6e0d3 100644
--- a/source4/libcli/smb2/smb2.h
+++ b/source4/libcli/smb2/smb2.h
@@ -21,6 +21,8 @@
#include "libcli/raw/request.h"
+struct smb2_handle;
+
struct smb2_options {
uint32_t timeout;
};
@@ -58,6 +60,15 @@ struct smb2_transport {
void *private;
uint_t period;
} idle;
+
+ struct {
+ /* a oplock break request handler */
+ bool (*handler)(struct smb2_transport *transport,
+ const struct smb2_handle *handle,
+ uint8_t level, void *private_data);
+ /* private data passed to the oplock handler */
+ void *private_data;
+ } oplock;
};
diff --git a/source4/libcli/smb2/transport.c b/source4/libcli/smb2/transport.c
index af19fcb0a9..8eb60a06f1 100644
--- a/source4/libcli/smb2/transport.c
+++ b/source4/libcli/smb2/transport.c
@@ -140,6 +140,44 @@ void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
}
}
+static bool 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;
+ }
+
+ opcode = SVAL(hdr, SMB2_HDR_OPCODE);
+ seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);
+
+ if ((opcode != SMB2_OP_BREAK) ||
+ (seqnum != UINT64_MAX)) {
+ return false;
+ }
+
+ if (transport->oplock.handler) {
+ uint8_t *body = hdr+SMB2_HDR_BODY;
+ struct smb2_handle h;
+ uint8_t level;
+
+ level = CVAL(body, 0x02);
+ smb2_pull_handle(body+0x08, &h);
+
+ transport->oplock.handler(transport, &h, level,
+ transport->oplock.private_data);
+ }
+
+ return true;
+}
+
/*
we have a full request in our receive buffer - match it to a pending request
and process
@@ -167,6 +205,11 @@ static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
goto error;
}
+ if (smb2_handle_oplock_break(transport, &blob)) {
+ talloc_free(buffer);
+ return NT_STATUS_OK;
+ }
+
flags = IVAL(hdr, SMB2_HDR_FLAGS);
seqnum = BVAL(hdr, SMB2_HDR_MESSAGE_ID);