wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client notify calls
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a cancel request
|
||||
*/
|
||||
NTSTATUS smb2_cancel(struct smb2_request *r)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2_request *c;
|
||||
uint32_t old_timeout;
|
||||
uint64_t old_seqnum;
|
||||
|
||||
/*
|
||||
* if we don't get a pending id yet, we just
|
||||
* mark the request for pending, so that we directly
|
||||
* send the cancel after getting the pending id
|
||||
*/
|
||||
if (!r->cancel.can_cancel) {
|
||||
r->cancel.do_cancel++;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* we don't want a seqmun for a SMB2 Cancel */
|
||||
old_seqnum = r->transport->seqnum;
|
||||
c = smb2_request_init(r->transport, SMB2_OP_CANCEL, 0x04, False, 0);
|
||||
r->transport->seqnum = old_seqnum;
|
||||
NT_STATUS_HAVE_NO_MEMORY(c);
|
||||
c->seqnum = 0;
|
||||
|
||||
SIVAL(c->out.hdr, SMB2_HDR_FLAGS, 0x00000002);
|
||||
SSVAL(c->out.hdr, SMB2_HDR_UNKNOWN1, 0x0030);
|
||||
SIVAL(c->out.hdr, SMB2_HDR_PID, r->cancel.pending_id);
|
||||
SBVAL(c->out.hdr, SMB2_HDR_SEQNUM, c->seqnum);
|
||||
if (r->session) {
|
||||
SBVAL(c->out.hdr, SMB2_HDR_UID, r->session->uid);
|
||||
}
|
||||
|
||||
SSVAL(c->out.body, 0x02, 0);
|
||||
|
||||
old_timeout = c->transport->options.timeout;
|
||||
c->transport->options.timeout = 0;
|
||||
smb2_transport_send(c);
|
||||
c->transport->options.timeout = old_timeout;
|
||||
|
||||
if (c->state == SMB2_REQUEST_ERROR) {
|
||||
status = c->status;
|
||||
} else {
|
||||
status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client close handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a close request
|
||||
*/
|
||||
struct smb2_request *smb2_close_send(struct smb2_tree *tree, struct smb2_close *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_CLOSE, 0x18, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.flags);
|
||||
SIVAL(req->out.body, 0x04, 0); /* pad */
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a close reply
|
||||
*/
|
||||
NTSTATUS smb2_close_recv(struct smb2_request *req, struct smb2_close *io)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x3C, False);
|
||||
|
||||
io->out.flags = SVAL(req->in.body, 0x02);
|
||||
io->out._pad = IVAL(req->in.body, 0x04);
|
||||
io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
|
||||
io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
|
||||
io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
|
||||
io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
|
||||
io->out.alloc_size = BVAL(req->in.body, 0x28);
|
||||
io->out.size = BVAL(req->in.body, 0x30);
|
||||
io->out.file_attr = IVAL(req->in.body, 0x38);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync close request
|
||||
*/
|
||||
NTSTATUS smb2_close(struct smb2_tree *tree, struct smb2_close *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_close_send(tree, io);
|
||||
return smb2_close_recv(req, io);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
[SUBSYSTEM::LIBCLI_SMB2]
|
||||
PRIVATE_PROTO_HEADER = smb2_proto.h
|
||||
OBJ_FILES = \
|
||||
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
|
||||
PUBLIC_DEPENDENCIES = LIBCLI_RAW LIBPACKET gensec
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 composite connection setup
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "libcli/resolve/resolve.h"
|
||||
|
||||
struct smb2_connect_state {
|
||||
struct cli_credentials *credentials;
|
||||
const char *host;
|
||||
const char *share;
|
||||
struct smb2_negprot negprot;
|
||||
struct smb2_tree_connect tcon;
|
||||
struct smb2_session *session;
|
||||
struct smb2_tree *tree;
|
||||
};
|
||||
|
||||
/*
|
||||
continue after tcon reply
|
||||
*/
|
||||
static void continue_tcon(struct smb2_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
struct composite_context);
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
|
||||
c->status = smb2_tree_connect_recv(req, &state->tcon);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
state->tree->tid = state->tcon.out.tid;
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
/*
|
||||
continue after a session setup
|
||||
*/
|
||||
static void continue_session(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
struct smb2_request *req;
|
||||
|
||||
c->status = smb2_session_setup_spnego_recv(creq);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
state->tree = smb2_tree_init(state->session, state, True);
|
||||
if (composite_nomem(state->tree, c)) return;
|
||||
|
||||
state->tcon.in.unknown1 = 0x09;
|
||||
state->tcon.in.path = talloc_asprintf(state, "\\\\%s\\%s",
|
||||
state->host, state->share);
|
||||
if (composite_nomem(state->tcon.in.path, c)) return;
|
||||
|
||||
req = smb2_tree_connect_send(state->tree, &state->tcon);
|
||||
if (composite_nomem(req, c)) return;
|
||||
|
||||
req->async.fn = continue_tcon;
|
||||
req->async.private = c;
|
||||
}
|
||||
|
||||
/*
|
||||
continue after negprot reply
|
||||
*/
|
||||
static void continue_negprot(struct smb2_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
struct composite_context);
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
struct smb2_transport *transport = req->transport;
|
||||
struct composite_context *creq;
|
||||
|
||||
c->status = smb2_negprot_recv(req, c, &state->negprot);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
state->session = smb2_session_init(transport, state, True);
|
||||
if (composite_nomem(state->session, c)) return;
|
||||
|
||||
creq = smb2_session_setup_spnego_send(state->session, state->credentials);
|
||||
|
||||
composite_continue(c, creq, continue_session, c);
|
||||
}
|
||||
|
||||
/*
|
||||
continue after a socket connect completes
|
||||
*/
|
||||
static void continue_socket(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
struct smbcli_socket *sock;
|
||||
struct smb2_transport *transport;
|
||||
struct smb2_request *req;
|
||||
|
||||
c->status = smbcli_sock_connect_recv(creq, state, &sock);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
transport = smb2_transport_init(sock, state);
|
||||
if (composite_nomem(transport, c)) return;
|
||||
|
||||
ZERO_STRUCT(state->negprot);
|
||||
state->negprot.in.unknown1 = 0x0001;
|
||||
|
||||
req = smb2_negprot_send(transport, &state->negprot);
|
||||
if (composite_nomem(req, c)) return;
|
||||
|
||||
req->async.fn = continue_negprot;
|
||||
req->async.private = c;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
continue after a resolve finishes
|
||||
*/
|
||||
static void continue_resolve(struct composite_context *creq)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(creq->async.private_data,
|
||||
struct composite_context);
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
const char *addr;
|
||||
|
||||
c->status = resolve_name_recv(creq, state, &addr);
|
||||
if (!composite_is_ok(c)) return;
|
||||
|
||||
creq = smbcli_sock_connect_send(state, addr, 445, state->host, c->event_ctx);
|
||||
|
||||
composite_continue(c, creq, continue_socket, c);
|
||||
}
|
||||
|
||||
/*
|
||||
a composite function that does a full negprot/sesssetup/tcon, returning
|
||||
a connected smb2_tree
|
||||
*/
|
||||
struct composite_context *smb2_connect_send(TALLOC_CTX *mem_ctx,
|
||||
const char *host,
|
||||
const char *share,
|
||||
struct cli_credentials *credentials,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct smb2_connect_state *state;
|
||||
struct nbt_name name;
|
||||
struct composite_context *creq;
|
||||
|
||||
c = composite_create(mem_ctx, ev);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
state = talloc(c, struct smb2_connect_state);
|
||||
if (composite_nomem(state, c)) return c;
|
||||
c->private_data = state;
|
||||
|
||||
state->credentials = credentials;
|
||||
state->host = talloc_strdup(c, host);
|
||||
if (composite_nomem(state->host, c)) return c;
|
||||
state->share = talloc_strdup(c, share);
|
||||
if (composite_nomem(state->share, c)) return c;
|
||||
|
||||
ZERO_STRUCT(name);
|
||||
name.name = host;
|
||||
|
||||
creq = resolve_name_send(&name, c->event_ctx, lp_name_resolve_order());
|
||||
composite_continue(c, creq, continue_resolve, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
receive a connect reply
|
||||
*/
|
||||
NTSTATUS smb2_connect_recv(struct composite_context *c, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_tree **tree)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2_connect_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_connect_state);
|
||||
status = composite_wait(c);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
*tree = talloc_steal(mem_ctx, state->tree);
|
||||
}
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
sync version of smb2_connect
|
||||
*/
|
||||
NTSTATUS smb2_connect(TALLOC_CTX *mem_ctx,
|
||||
const char *host, const char *share,
|
||||
struct cli_credentials *credentials,
|
||||
struct smb2_tree **tree,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct composite_context *c = smb2_connect_send(mem_ctx, host, share,
|
||||
credentials, ev);
|
||||
return smb2_connect_recv(c, mem_ctx, tree);
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client tree handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
#define CREATE_TAG_EXTA 0x41747845 /* "ExtA" */
|
||||
#define CREATE_TAG_MXAC 0x6341784D /* "MxAc" */
|
||||
|
||||
/*
|
||||
add a blob to a smb2_create attribute blob
|
||||
*/
|
||||
static NTSTATUS smb2_create_blob_add(TALLOC_CTX *mem_ctx, DATA_BLOB *blob,
|
||||
uint32_t tag,
|
||||
DATA_BLOB add, BOOL last)
|
||||
{
|
||||
NTSTATUS status;
|
||||
uint32_t ofs = blob->length;
|
||||
uint8_t pad = smb2_padding_size(add.length, 8);
|
||||
status = data_blob_realloc(mem_ctx, blob, blob->length + 0x18 + add.length + pad);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (last) {
|
||||
SIVAL(blob->data, ofs+0x00, 0);
|
||||
} else {
|
||||
SIVAL(blob->data, ofs+0x00, 0x18 + add.length + pad);
|
||||
}
|
||||
SSVAL(blob->data, ofs+0x04, 0x10); /* offset of tag */
|
||||
SIVAL(blob->data, ofs+0x06, 0x04); /* tag length */
|
||||
SSVAL(blob->data, ofs+0x0A, 0x18); /* offset of data */
|
||||
SIVAL(blob->data, ofs+0x0C, add.length);
|
||||
SIVAL(blob->data, ofs+0x10, tag);
|
||||
SIVAL(blob->data, ofs+0x14, 0); /* pad? */
|
||||
memcpy(blob->data+ofs+0x18, add.data, add.length);
|
||||
memset(blob->data+ofs+0x18+add.length, 0, pad);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
send a create request
|
||||
*/
|
||||
struct smb2_request *smb2_create_send(struct smb2_tree *tree, struct smb2_create *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob = data_blob(NULL, 0);
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_CREATE, 0x38, True, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.oplock_flags);
|
||||
SIVAL(req->out.body, 0x04, io->in.impersonation);
|
||||
SIVAL(req->out.body, 0x08, io->in.unknown3[0]);
|
||||
SIVAL(req->out.body, 0x0C, io->in.unknown3[1]);
|
||||
SIVAL(req->out.body, 0x10, io->in.unknown3[2]);
|
||||
SIVAL(req->out.body, 0x14, io->in.unknown3[3]);
|
||||
SIVAL(req->out.body, 0x18, io->in.access_mask);
|
||||
SIVAL(req->out.body, 0x1C, io->in.file_attr);
|
||||
SIVAL(req->out.body, 0x20, io->in.share_access);
|
||||
SIVAL(req->out.body, 0x24, io->in.open_disposition);
|
||||
SIVAL(req->out.body, 0x28, io->in.create_options);
|
||||
|
||||
status = smb2_push_o16s16_string(&req->out, 0x2C, io->in.fname);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (io->in.eas.num_eas != 0) {
|
||||
DATA_BLOB b = data_blob_talloc(req, NULL,
|
||||
ea_list_size_chained(io->in.eas.num_eas, io->in.eas.eas));
|
||||
ea_put_list_chained(b.data, io->in.eas.num_eas, io->in.eas.eas);
|
||||
status = smb2_create_blob_add(req, &blob, CREATE_TAG_EXTA, b, False);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
data_blob_free(&b);
|
||||
}
|
||||
|
||||
/* 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, CREATE_TAG_MXAC, data_blob(NULL, 0), True);
|
||||
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a create reply
|
||||
*/
|
||||
NTSTATUS smb2_create_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx, struct smb2_create *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x58, True);
|
||||
|
||||
io->out.oplock_flags = SVAL(req->in.body, 0x02);
|
||||
io->out.create_action = IVAL(req->in.body, 0x04);
|
||||
io->out.create_time = smbcli_pull_nttime(req->in.body, 0x08);
|
||||
io->out.access_time = smbcli_pull_nttime(req->in.body, 0x10);
|
||||
io->out.write_time = smbcli_pull_nttime(req->in.body, 0x18);
|
||||
io->out.change_time = smbcli_pull_nttime(req->in.body, 0x20);
|
||||
io->out.alloc_size = BVAL(req->in.body, 0x28);
|
||||
io->out.size = BVAL(req->in.body, 0x30);
|
||||
io->out.file_attr = IVAL(req->in.body, 0x38);
|
||||
io->out._pad = IVAL(req->in.body, 0x3C);
|
||||
smb2_pull_handle(req->in.body+0x40, &io->out.file.handle);
|
||||
status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x50, &io->out.blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync create request
|
||||
*/
|
||||
NTSTATUS smb2_create(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_create *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_create_send(tree, io);
|
||||
return smb2_create_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client find calls
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a find request
|
||||
*/
|
||||
struct smb2_request *smb2_find_send(struct smb2_tree *tree, struct smb2_find *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
NTSTATUS status;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_FIND, 0x20, True, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SCVAL(req->out.body, 0x02, io->in.level);
|
||||
SCVAL(req->out.body, 0x03, io->in.continue_flags);
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
|
||||
status = smb2_push_o16s16_string(&req->out, 0x18, io->in.pattern);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x1C, io->in.max_response_size);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a find reply
|
||||
*/
|
||||
NTSTATUS smb2_find_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_find *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x08, True);
|
||||
|
||||
status = smb2_pull_o16s32_blob(&req->in, mem_ctx,
|
||||
req->in.body+0x02, &io->out.blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync find request
|
||||
*/
|
||||
NTSTATUS smb2_find(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_find *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_find_send(tree, io);
|
||||
return smb2_find_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a varient of smb2_find_recv that parses the resulting blob into
|
||||
smb_search_data structures
|
||||
*/
|
||||
NTSTATUS smb2_find_level_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
uint8_t level, uint_t *count,
|
||||
union smb_search_data **io)
|
||||
{
|
||||
struct smb2_find f;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB b;
|
||||
enum smb_search_data_level smb_level;
|
||||
uint_t next_ofs=0;
|
||||
|
||||
switch (level) {
|
||||
case SMB2_FIND_DIRECTORY_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_DIRECTORY_INFO;
|
||||
break;
|
||||
case SMB2_FIND_FULL_DIRECTORY_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_FULL_DIRECTORY_INFO;
|
||||
break;
|
||||
case SMB2_FIND_BOTH_DIRECTORY_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO;
|
||||
break;
|
||||
case SMB2_FIND_NAME_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_NAME_INFO;
|
||||
break;
|
||||
case SMB2_FIND_ID_FULL_DIRECTORY_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO;
|
||||
break;
|
||||
case SMB2_FIND_ID_BOTH_DIRECTORY_INFO:
|
||||
smb_level = RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_INFO_CLASS;
|
||||
}
|
||||
|
||||
status = smb2_find_recv(req, mem_ctx, &f);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
b = f.out.blob;
|
||||
*io = NULL;
|
||||
*count = 0;
|
||||
|
||||
do {
|
||||
union smb_search_data *io2;
|
||||
|
||||
io2 = talloc_realloc(mem_ctx, *io, union smb_search_data, (*count)+1);
|
||||
if (io2 == NULL) {
|
||||
data_blob_free(&f.out.blob);
|
||||
talloc_free(*io);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
*io = io2;
|
||||
|
||||
status = smb_raw_search_common(*io, smb_level, &b, (*io) + (*count),
|
||||
&next_ofs, STR_UNICODE);
|
||||
|
||||
if (NT_STATUS_IS_OK(status) &&
|
||||
next_ofs >= b.length) {
|
||||
data_blob_free(&f.out.blob);
|
||||
talloc_free(*io);
|
||||
return NT_STATUS_INFO_LENGTH_MISMATCH;
|
||||
}
|
||||
|
||||
(*count)++;
|
||||
|
||||
b = data_blob_const(b.data+next_ofs, b.length - next_ofs);
|
||||
} while (NT_STATUS_IS_OK(status) && next_ofs != 0);
|
||||
|
||||
data_blob_free(&f.out.blob);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
a varient of smb2_find that parses the resulting blob into
|
||||
smb_search_data structures
|
||||
*/
|
||||
NTSTATUS smb2_find_level(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_find *f,
|
||||
uint_t *count, union smb_search_data **io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_find_send(tree, f);
|
||||
return smb2_find_level_recv(req, mem_ctx, f->in.level, count, io);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client flush handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a flush request
|
||||
*/
|
||||
struct smb2_request *smb2_flush_send(struct smb2_tree *tree, struct smb2_flush *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_FLUSH, 0x18, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0); /* pad? */
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a flush reply
|
||||
*/
|
||||
NTSTATUS smb2_flush_recv(struct smb2_request *req, struct smb2_flush *io)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x04, False);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync flush request
|
||||
*/
|
||||
NTSTATUS smb2_flush(struct smb2_tree *tree, struct smb2_flush *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_flush_send(tree, io);
|
||||
return smb2_flush_recv(req, io);
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client getinfo calls
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a getinfo request
|
||||
*/
|
||||
struct smb2_request *smb2_getinfo_send(struct smb2_tree *tree, struct smb2_getinfo *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_GETINFO, 0x28, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
/* this seems to be a bug, they use 0x29 but only send 0x28 bytes */
|
||||
SSVAL(req->out.body, 0x00, 0x29);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.level);
|
||||
SIVAL(req->out.body, 0x04, io->in.max_response_size);
|
||||
SIVAL(req->out.body, 0x08, io->in.unknown1);
|
||||
SIVAL(req->out.body, 0x0C, io->in.unknown2);
|
||||
SIVAL(req->out.body, 0x10, io->in.flags);
|
||||
SIVAL(req->out.body, 0x14, io->in.flags2);
|
||||
smb2_push_handle(req->out.body+0x18, &io->in.file.handle);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a getinfo reply
|
||||
*/
|
||||
NTSTATUS smb2_getinfo_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_getinfo *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x08, True);
|
||||
|
||||
status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync getinfo request
|
||||
*/
|
||||
NTSTATUS smb2_getinfo(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_getinfo *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_getinfo_send(tree, io);
|
||||
return smb2_getinfo_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map a generic info level to a SMB2 info level
|
||||
*/
|
||||
uint16_t smb2_getinfo_map_level(uint16_t level, uint8_t class)
|
||||
{
|
||||
if (class == SMB2_GETINFO_FILE &&
|
||||
level == RAW_FILEINFO_SEC_DESC) {
|
||||
return SMB2_GETINFO_SECURITY;
|
||||
}
|
||||
if ((level & 0xFF) == class) {
|
||||
return level;
|
||||
} else if (level > 1000) {
|
||||
return ((level-1000)<<8) | class;
|
||||
}
|
||||
DEBUG(0,("Unable to map SMB2 info level 0x%04x of class %d\n", level, class));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
level specific getinfo call - async send
|
||||
*/
|
||||
struct smb2_request *smb2_getinfo_file_send(struct smb2_tree *tree, union smb_fileinfo *io)
|
||||
{
|
||||
struct smb2_getinfo b;
|
||||
uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
|
||||
|
||||
if (smb2_level == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(b);
|
||||
b.in.max_response_size = 0x10000;
|
||||
b.in.file.handle = io->generic.in.file.handle;
|
||||
b.in.level = smb2_level;
|
||||
|
||||
if (io->generic.level == RAW_FILEINFO_SEC_DESC) {
|
||||
b.in.flags = io->query_secdesc.in.secinfo_flags;
|
||||
}
|
||||
if (io->generic.level == RAW_FILEINFO_SMB2_ALL_EAS) {
|
||||
b.in.flags2 = io->all_eas.in.continue_flags;
|
||||
}
|
||||
|
||||
return smb2_getinfo_send(tree, &b);
|
||||
}
|
||||
|
||||
/*
|
||||
recv a getinfo reply and parse the level info
|
||||
*/
|
||||
NTSTATUS smb2_getinfo_file_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
union smb_fileinfo *io)
|
||||
{
|
||||
struct smb2_getinfo b;
|
||||
NTSTATUS status;
|
||||
|
||||
status = smb2_getinfo_recv(req, mem_ctx, &b);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = smb_raw_fileinfo_passthru_parse(&b.out.blob, mem_ctx, io->generic.level, io);
|
||||
data_blob_free(&b.out.blob);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
level specific getinfo call
|
||||
*/
|
||||
NTSTATUS smb2_getinfo_file(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
union smb_fileinfo *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_getinfo_file_send(tree, io);
|
||||
return smb2_getinfo_file_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
level specific getinfo call - async send
|
||||
*/
|
||||
struct smb2_request *smb2_getinfo_fs_send(struct smb2_tree *tree, union smb_fsinfo *io)
|
||||
{
|
||||
struct smb2_getinfo b;
|
||||
uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FS);
|
||||
|
||||
if (smb2_level == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(b);
|
||||
b.in.max_response_size = 0x10000;
|
||||
b.in.file.handle = io->generic.handle;
|
||||
b.in.level = smb2_level;
|
||||
|
||||
return smb2_getinfo_send(tree, &b);
|
||||
}
|
||||
|
||||
/*
|
||||
recv a getinfo reply and parse the level info
|
||||
*/
|
||||
NTSTATUS smb2_getinfo_fs_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
union smb_fsinfo *io)
|
||||
{
|
||||
struct smb2_getinfo b;
|
||||
NTSTATUS status;
|
||||
|
||||
status = smb2_getinfo_recv(req, mem_ctx, &b);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = smb_raw_fsinfo_passthru_parse(b.out.blob, mem_ctx, io->generic.level, io);
|
||||
data_blob_free(&b.out.blob);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
level specific getinfo call
|
||||
*/
|
||||
NTSTATUS smb2_getinfo_fs(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
union smb_fsinfo *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_getinfo_fs_send(tree, io);
|
||||
return smb2_getinfo_fs_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client ioctl call
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a ioctl request
|
||||
*/
|
||||
struct smb2_request *smb2_ioctl_send(struct smb2_tree *tree, struct smb2_ioctl *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_IOCTL, 0x38, True,
|
||||
io->in.in.length+io->in.out.length);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0); /* pad */
|
||||
SIVAL(req->out.body, 0x04, io->in.function);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
|
||||
status = smb2_push_o32s32_blob(&req->out, 0x18, io->in.out);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x20, io->in.unknown2);
|
||||
|
||||
status = smb2_push_o32s32_blob(&req->out, 0x24, io->in.in);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x2C, io->in.max_response_size);
|
||||
SBVAL(req->out.body, 0x30, io->in.flags);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a ioctl reply
|
||||
*/
|
||||
NTSTATUS smb2_ioctl_recv(struct smb2_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x30, True);
|
||||
|
||||
io->out._pad = SVAL(req->in.body, 0x02);
|
||||
io->out.function = IVAL(req->in.body, 0x04);
|
||||
smb2_pull_handle(req->in.body+0x08, &io->out.file.handle);
|
||||
|
||||
status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x18, &io->out.in);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = smb2_pull_o32s32_blob(&req->in, mem_ctx, req->in.body+0x20, &io->out.out);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
io->out.unknown2 = IVAL(req->in.body, 0x28);
|
||||
io->out.unknown3 = IVAL(req->in.body, 0x2C);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync ioctl request
|
||||
*/
|
||||
NTSTATUS smb2_ioctl(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_ioctl *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_ioctl_send(tree, io);
|
||||
return smb2_ioctl_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client keepalive handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a keepalive request
|
||||
*/
|
||||
struct smb2_request *smb2_keepalive_send(struct smb2_transport *transport)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init(transport, SMB2_OP_KEEPALIVE, 0x04, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a keepalive reply
|
||||
*/
|
||||
NTSTATUS smb2_keepalive_recv(struct smb2_request *req)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x04, False);
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync keepalive request
|
||||
*/
|
||||
NTSTATUS smb2_keepalive(struct smb2_transport *transport)
|
||||
{
|
||||
struct smb2_request *req = smb2_keepalive_send(transport);
|
||||
return smb2_keepalive_recv(req);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client lock handling
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a lock request
|
||||
*/
|
||||
struct smb2_request *smb2_lock_send(struct smb2_tree *tree, struct smb2_lock *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_LOCK, 0x30, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.unknown1);
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown2);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
SBVAL(req->out.body, 0x18, io->in.offset);
|
||||
SBVAL(req->out.body, 0x20, io->in.count);
|
||||
SIVAL(req->out.body, 0x28, io->in.unknown5);
|
||||
SIVAL(req->out.body, 0x28, io->in.flags);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a lock reply
|
||||
*/
|
||||
NTSTATUS smb2_lock_recv(struct smb2_request *req, struct smb2_lock *io)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x04, False);
|
||||
|
||||
io->out.unknown1 = SVAL(req->in.body, 0x02);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync lock request
|
||||
*/
|
||||
NTSTATUS smb2_lock(struct smb2_tree *tree, struct smb2_lock *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_lock_send(tree, io);
|
||||
return smb2_lock_recv(req, io);
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client logoff handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a logoff request
|
||||
*/
|
||||
struct smb2_request *smb2_logoff_send(struct smb2_session *session)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init(session->transport, SMB2_OP_LOGOFF, 0x04, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, session->uid);
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a logoff reply
|
||||
*/
|
||||
NTSTATUS smb2_logoff_recv(struct smb2_request *req)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x04, False);
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync logoff request
|
||||
*/
|
||||
NTSTATUS smb2_logoff(struct smb2_session *session)
|
||||
{
|
||||
struct smb2_request *req = smb2_logoff_send(session);
|
||||
return smb2_logoff_recv(req);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client negprot handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a negprot request
|
||||
*/
|
||||
struct smb2_request *smb2_negprot_send(struct smb2_transport *transport,
|
||||
struct smb2_negprot *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init(transport, SMB2_OP_NEGPROT, 0x26, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
/* this seems to be a bug, they use 0x24 but the length is 0x26 */
|
||||
SSVAL(req->out.body, 0x00, 0x24);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.unknown1);
|
||||
memcpy(req->out.body+0x04, io->in.unknown2, 32);
|
||||
SSVAL(req->out.body, 0x24, io->in.unknown3);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
recv a negprot reply
|
||||
*/
|
||||
NTSTATUS smb2_negprot_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_negprot *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x40, True);
|
||||
|
||||
io->out._pad = SVAL(req->in.body, 0x02);
|
||||
io->out.unknown2 = IVAL(req->in.body, 0x04);
|
||||
memcpy(io->out.sessid, req->in.body + 0x08, 16);
|
||||
io->out.unknown3 = IVAL(req->in.body, 0x18);
|
||||
io->out.unknown4 = SVAL(req->in.body, 0x1C);
|
||||
io->out.unknown5 = IVAL(req->in.body, 0x1E);
|
||||
io->out.unknown6 = IVAL(req->in.body, 0x22);
|
||||
io->out.unknown7 = SVAL(req->in.body, 0x26);
|
||||
io->out.current_time = smbcli_pull_nttime(req->in.body, 0x28);
|
||||
io->out.boot_time = smbcli_pull_nttime(req->in.body, 0x30);
|
||||
|
||||
status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x38, &io->out.secblob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
io->out.unknown9 = IVAL(req->in.body, 0x3C);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync negprot request
|
||||
*/
|
||||
NTSTATUS smb2_negprot(struct smb2_transport *transport,
|
||||
TALLOC_CTX *mem_ctx, struct smb2_negprot *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_negprot_send(transport, io);
|
||||
return smb2_negprot_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client notify calls
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a notify request
|
||||
*/
|
||||
struct smb2_request *smb2_notify_send(struct smb2_tree *tree, struct smb2_notify *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
uint32_t old_timeout;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_NOTIFY, 0x20, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1, 0x0030);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.recursive);
|
||||
SIVAL(req->out.body, 0x04, io->in.buffer_size);
|
||||
smb2_push_handle(req->out.body+0x08, &io->in.file.handle);
|
||||
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;
|
||||
smb2_transport_send(req);
|
||||
req->transport->options.timeout = old_timeout;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a notify reply
|
||||
*/
|
||||
NTSTATUS smb2_notify_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_notify *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
uint32_t ofs, i;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x08, True);
|
||||
|
||||
status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
io->out.changes = NULL;
|
||||
io->out.num_changes = 0;
|
||||
|
||||
/* count them */
|
||||
for (ofs=0; blob.length - ofs > 12; ) {
|
||||
uint32_t next = IVAL(blob.data, ofs);
|
||||
io->out.num_changes++;
|
||||
if (next == 0 || (ofs + next) >= blob.length) break;
|
||||
ofs += next;
|
||||
}
|
||||
|
||||
/* allocate array */
|
||||
io->out.changes = talloc_array(mem_ctx, struct notify_changes, io->out.num_changes);
|
||||
if (!io->out.changes) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i=ofs=0; i<io->out.num_changes; i++) {
|
||||
io->out.changes[i].action = IVAL(blob.data, ofs+4);
|
||||
smbcli_blob_pull_string(NULL, mem_ctx, &blob,
|
||||
&io->out.changes[i].name,
|
||||
ofs+8, ofs+12, STR_UNICODE);
|
||||
ofs += IVAL(blob.data, ofs);
|
||||
}
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync notify request
|
||||
*/
|
||||
NTSTATUS smb2_notify(struct smb2_tree *tree, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_notify *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_notify_send(tree, io);
|
||||
return smb2_notify_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client read call
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a read request
|
||||
*/
|
||||
struct smb2_request *smb2_read_send(struct smb2_tree *tree, struct smb2_read *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_READ, 0x30, True, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0); /* pad */
|
||||
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);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a read reply
|
||||
*/
|
||||
NTSTATUS smb2_read_recv(struct smb2_request *req,
|
||||
TALLOC_CTX *mem_ctx, struct smb2_read *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x10, True);
|
||||
|
||||
status = smb2_pull_o16s32_blob(&req->in, mem_ctx, req->in.body+0x02, &io->out.data);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
io->out.unknown1 = BVAL(req->in.body, 0x08);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync read request
|
||||
*/
|
||||
NTSTATUS smb2_read(struct smb2_tree *tree, TALLOC_CTX *mem_ctx, struct smb2_read *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_read_send(tree, io);
|
||||
return smb2_read_recv(req, mem_ctx, io);
|
||||
}
|
||||
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client request handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
Copyright (C) Stefan Metzmacher 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
/*
|
||||
initialise a smb2 request
|
||||
*/
|
||||
struct smb2_request *smb2_request_init(struct smb2_transport *transport, uint16_t opcode,
|
||||
uint16_t body_fixed_size, BOOL body_dynamic_present,
|
||||
uint32_t body_dynamic_size)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
uint64_t seqnum;
|
||||
|
||||
if (body_dynamic_present) {
|
||||
if (body_dynamic_size == 0) {
|
||||
body_dynamic_size = 1;
|
||||
}
|
||||
} else {
|
||||
body_dynamic_size = 0;
|
||||
}
|
||||
|
||||
req = talloc(transport, struct smb2_request);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
seqnum = transport->seqnum++;
|
||||
if (seqnum == UINT64_MAX) {
|
||||
seqnum = transport->seqnum++;
|
||||
}
|
||||
|
||||
req->state = SMB2_REQUEST_INIT;
|
||||
req->transport = transport;
|
||||
req->session = NULL;
|
||||
req->tree = NULL;
|
||||
req->seqnum = seqnum;
|
||||
req->status = NT_STATUS_OK;
|
||||
req->async.fn = NULL;
|
||||
req->next = req->prev = NULL;
|
||||
|
||||
ZERO_STRUCT(req->cancel);
|
||||
ZERO_STRUCT(req->in);
|
||||
|
||||
req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE+body_fixed_size;
|
||||
|
||||
req->out.allocated = req->out.size + body_dynamic_size;
|
||||
req->out.buffer = talloc_size(req, req->out.allocated);
|
||||
if (req->out.buffer == NULL) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
|
||||
req->out.body = req->out.hdr + SMB2_HDR_BODY;
|
||||
req->out.body_fixed= body_fixed_size;
|
||||
req->out.body_size = body_fixed_size;
|
||||
req->out.dynamic = (body_dynamic_size ? req->out.body + body_fixed_size : NULL);
|
||||
|
||||
SIVAL(req->out.hdr, 0, SMB2_MAGIC);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_LENGTH, SMB2_HDR_BODY);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_PAD1, 0);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_STATUS, 0);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_OPCODE, opcode);
|
||||
SSVAL(req->out.hdr, SMB2_HDR_UNKNOWN1,0);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN2,0);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_SEQNUM, req->seqnum);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_PID, 0);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, 0);
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, 0);
|
||||
memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
|
||||
|
||||
/* set the length of the fixed body part and +1 if there's a dynamic part also */
|
||||
SSVAL(req->out.body, 0, body_fixed_size + (body_dynamic_size?1:0));
|
||||
|
||||
/*
|
||||
* if we have a dynamic part, make sure the first byte
|
||||
* which is always be part of the packet is initialized
|
||||
*/
|
||||
if (body_dynamic_size) {
|
||||
req->out.size += 1;
|
||||
SCVAL(req->out.dynamic, 0, 0);
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
initialise a smb2 request for tree operations
|
||||
*/
|
||||
struct smb2_request *smb2_request_init_tree(struct smb2_tree *tree, uint16_t opcode,
|
||||
uint16_t body_fixed_size, BOOL body_dynamic_present,
|
||||
uint32_t body_dynamic_size)
|
||||
{
|
||||
struct smb2_request *req = smb2_request_init(tree->session->transport, opcode,
|
||||
body_fixed_size, body_dynamic_present,
|
||||
body_dynamic_size);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, tree->session->uid);
|
||||
SIVAL(req->out.hdr, SMB2_HDR_TID, tree->tid);
|
||||
req->session = tree->session;
|
||||
req->tree = tree;
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/* destroy a request structure and return final status */
|
||||
NTSTATUS smb2_request_destroy(struct smb2_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
/* this is the error code we give the application for when a
|
||||
_send() call fails completely */
|
||||
if (!req) return NT_STATUS_UNSUCCESSFUL;
|
||||
|
||||
if (req->transport) {
|
||||
/* remove it from the list of pending requests (a null op if
|
||||
its not in the list) */
|
||||
DLIST_REMOVE(req->transport->pending_recv, req);
|
||||
}
|
||||
|
||||
if (req->state == SMB2_REQUEST_ERROR &&
|
||||
NT_STATUS_IS_OK(req->status)) {
|
||||
req->status = NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
status = req->status;
|
||||
talloc_free(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
receive a response to a packet
|
||||
*/
|
||||
BOOL smb2_request_receive(struct smb2_request *req)
|
||||
{
|
||||
/* req can be NULL when a send has failed. This eliminates lots of NULL
|
||||
checks in each module */
|
||||
if (!req) return False;
|
||||
|
||||
/* keep receiving packets until this one is replied to */
|
||||
while (req->state <= SMB2_REQUEST_RECV) {
|
||||
if (event_loop_once(req->transport->socket->event.ctx) != 0) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
return req->state == SMB2_REQUEST_DONE;
|
||||
}
|
||||
|
||||
/* Return true if the last packet was in error */
|
||||
BOOL smb2_request_is_error(struct smb2_request *req)
|
||||
{
|
||||
return NT_STATUS_IS_ERR(req->status);
|
||||
}
|
||||
|
||||
/* Return true if the last packet was OK */
|
||||
BOOL smb2_request_is_ok(struct smb2_request *req)
|
||||
{
|
||||
return NT_STATUS_IS_OK(req->status);
|
||||
}
|
||||
|
||||
/*
|
||||
check if a range in the reply body is out of bounds
|
||||
*/
|
||||
BOOL smb2_oob(struct smb2_request_buffer *buf, const uint8_t *ptr, size_t size)
|
||||
{
|
||||
/* be careful with wraparound! */
|
||||
if (ptr < buf->body ||
|
||||
ptr >= buf->body + buf->body_size ||
|
||||
size > buf->body_size ||
|
||||
ptr + size > buf->body + buf->body_size) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
size_t smb2_padding_size(uint32_t offset, size_t n)
|
||||
{
|
||||
if ((offset & (n-1)) == 0) return 0;
|
||||
return n - (offset & (n-1));
|
||||
}
|
||||
|
||||
static size_t smb2_padding_fix(struct smb2_request_buffer *buf)
|
||||
{
|
||||
if (buf->dynamic == (buf->body + buf->body_fixed)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
grow a SMB2 buffer by the specified amount
|
||||
*/
|
||||
static NTSTATUS smb2_grow_buffer(struct smb2_request_buffer *buf, size_t increase)
|
||||
{
|
||||
size_t dynamic_ofs;
|
||||
uint8_t *buffer_ptr;
|
||||
uint32_t newsize = buf->size + increase;
|
||||
|
||||
/* a packet size should be limited a bit */
|
||||
if (newsize >= 0x00FFFFFF) return NT_STATUS_MARSHALL_OVERFLOW;
|
||||
|
||||
if (newsize <= buf->allocated) return NT_STATUS_OK;
|
||||
|
||||
dynamic_ofs = buf->dynamic - buf->buffer;
|
||||
|
||||
buffer_ptr = talloc_realloc_size(buf, buf->buffer, newsize);
|
||||
NT_STATUS_HAVE_NO_MEMORY(buffer_ptr);
|
||||
|
||||
buf->buffer = buffer_ptr;
|
||||
buf->hdr = buf->buffer + NBT_HDR_SIZE;
|
||||
buf->body = buf->hdr + SMB2_HDR_BODY;
|
||||
buf->dynamic = buf->buffer + dynamic_ofs;
|
||||
buf->allocated = newsize;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a uint16_t ofs/ uint16_t length/blob triple from a data blob
|
||||
the ptr points to the start of the offset/length pair
|
||||
*/
|
||||
NTSTATUS smb2_pull_o16s16_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
|
||||
{
|
||||
uint16_t ofs, size;
|
||||
if (smb2_oob(buf, ptr, 4)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
ofs = SVAL(ptr, 0);
|
||||
size = SVAL(ptr, 2);
|
||||
if (ofs == 0 || size == 0) {
|
||||
*blob = data_blob(NULL, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (smb2_oob(buf, buf->hdr + ofs, size)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob->data);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
push a uint16_t ofs/ uint16_t length/blob triple into a data blob
|
||||
the ofs points to the start of the offset/length pair, and is relative
|
||||
to the body start
|
||||
*/
|
||||
NTSTATUS smb2_push_o16s16_blob(struct smb2_request_buffer *buf,
|
||||
uint16_t ofs, DATA_BLOB blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t offset;
|
||||
size_t padding_length;
|
||||
size_t padding_fix;
|
||||
uint8_t *ptr = buf->body+ofs;
|
||||
|
||||
if (buf->dynamic == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* we have only 16 bit for the size */
|
||||
if (blob.length > 0xFFFF) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
/* check if there're enough room for ofs and size */
|
||||
if (smb2_oob(buf, ptr, 4)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (blob.length == 0) {
|
||||
SSVAL(ptr, 0, 0);
|
||||
SSVAL(ptr, 2, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
offset = buf->dynamic - buf->hdr;
|
||||
padding_length = smb2_padding_size(offset, 2);
|
||||
offset += padding_length;
|
||||
padding_fix = smb2_padding_fix(buf);
|
||||
|
||||
SSVAL(ptr, 0, offset);
|
||||
SSVAL(ptr, 2, blob.length);
|
||||
|
||||
status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memset(buf->dynamic, 0, padding_length);
|
||||
buf->dynamic += padding_length;
|
||||
|
||||
memcpy(buf->dynamic, blob.data, blob.length);
|
||||
buf->dynamic += blob.length;
|
||||
|
||||
buf->size += blob.length + padding_length - padding_fix;
|
||||
buf->body_size += blob.length + padding_length;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
push a uint16_t ofs/ uint32_t length/blob triple into a data blob
|
||||
the ofs points to the start of the offset/length pair, and is relative
|
||||
to the body start
|
||||
*/
|
||||
NTSTATUS smb2_push_o16s32_blob(struct smb2_request_buffer *buf,
|
||||
uint16_t ofs, DATA_BLOB blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t offset;
|
||||
size_t padding_length;
|
||||
size_t padding_fix;
|
||||
uint8_t *ptr = buf->body+ofs;
|
||||
|
||||
if (buf->dynamic == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* check if there're enough room for ofs and size */
|
||||
if (smb2_oob(buf, ptr, 6)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (blob.length == 0) {
|
||||
SSVAL(ptr, 0, 0);
|
||||
SIVAL(ptr, 2, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
offset = buf->dynamic - buf->hdr;
|
||||
padding_length = smb2_padding_size(offset, 2);
|
||||
offset += padding_length;
|
||||
padding_fix = smb2_padding_fix(buf);
|
||||
|
||||
SSVAL(ptr, 0, offset);
|
||||
SIVAL(ptr, 2, blob.length);
|
||||
|
||||
status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memset(buf->dynamic, 0, padding_length);
|
||||
buf->dynamic += padding_length;
|
||||
|
||||
memcpy(buf->dynamic, blob.data, blob.length);
|
||||
buf->dynamic += blob.length;
|
||||
|
||||
buf->size += blob.length + padding_length - padding_fix;
|
||||
buf->body_size += blob.length + padding_length;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
push a uint32_t ofs/ uint32_t length/blob triple into a data blob
|
||||
the ofs points to the start of the offset/length pair, and is relative
|
||||
to the body start
|
||||
*/
|
||||
NTSTATUS smb2_push_o32s32_blob(struct smb2_request_buffer *buf,
|
||||
uint32_t ofs, DATA_BLOB blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t offset;
|
||||
size_t padding_length;
|
||||
size_t padding_fix;
|
||||
uint8_t *ptr = buf->body+ofs;
|
||||
|
||||
if (buf->dynamic == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* check if there're enough room for ofs and size */
|
||||
if (smb2_oob(buf, ptr, 8)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (blob.length == 0) {
|
||||
SIVAL(ptr, 0, 0);
|
||||
SIVAL(ptr, 4, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
offset = buf->dynamic - buf->hdr;
|
||||
padding_length = smb2_padding_size(offset, 8);
|
||||
offset += padding_length;
|
||||
padding_fix = smb2_padding_fix(buf);
|
||||
|
||||
SIVAL(ptr, 0, offset);
|
||||
SIVAL(ptr, 4, blob.length);
|
||||
|
||||
status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memset(buf->dynamic, 0, padding_length);
|
||||
buf->dynamic += padding_length;
|
||||
|
||||
memcpy(buf->dynamic, blob.data, blob.length);
|
||||
buf->dynamic += blob.length;
|
||||
|
||||
buf->size += blob.length + padding_length - padding_fix;
|
||||
buf->body_size += blob.length + padding_length;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
push a uint32_t length/ uint32_t ofs/blob triple into a data blob
|
||||
the ofs points to the start of the length/offset pair, and is relative
|
||||
to the body start
|
||||
*/
|
||||
NTSTATUS smb2_push_s32o32_blob(struct smb2_request_buffer *buf,
|
||||
uint32_t ofs, DATA_BLOB blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
size_t offset;
|
||||
size_t padding_length;
|
||||
size_t padding_fix;
|
||||
uint8_t *ptr = buf->body+ofs;
|
||||
|
||||
if (buf->dynamic == NULL) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* check if there're enough room for ofs and size */
|
||||
if (smb2_oob(buf, ptr, 8)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
|
||||
if (blob.length == 0) {
|
||||
SIVAL(ptr, 0, 0);
|
||||
SIVAL(ptr, 4, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
offset = buf->dynamic - buf->hdr;
|
||||
padding_length = smb2_padding_size(offset, 8);
|
||||
offset += padding_length;
|
||||
padding_fix = smb2_padding_fix(buf);
|
||||
|
||||
SIVAL(ptr, 0, blob.length);
|
||||
SIVAL(ptr, 4, offset);
|
||||
|
||||
status = smb2_grow_buffer(buf, blob.length + padding_length - padding_fix);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
memset(buf->dynamic, 0, padding_length);
|
||||
buf->dynamic += padding_length;
|
||||
|
||||
memcpy(buf->dynamic, blob.data, blob.length);
|
||||
buf->dynamic += blob.length;
|
||||
|
||||
buf->size += blob.length + padding_length - padding_fix;
|
||||
buf->body_size += blob.length + padding_length;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a uint16_t ofs/ uint32_t length/blob triple from a data blob
|
||||
the ptr points to the start of the offset/length pair
|
||||
*/
|
||||
NTSTATUS smb2_pull_o16s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
|
||||
{
|
||||
uint16_t ofs;
|
||||
uint32_t size;
|
||||
|
||||
if (smb2_oob(buf, ptr, 6)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
ofs = SVAL(ptr, 0);
|
||||
size = IVAL(ptr, 2);
|
||||
if (ofs == 0 || size == 0) {
|
||||
*blob = data_blob(NULL, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (smb2_oob(buf, buf->hdr + ofs, size)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob->data);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a uint32_t ofs/ uint32_t length/blob triple from a data blob
|
||||
the ptr points to the start of the offset/length pair
|
||||
*/
|
||||
NTSTATUS smb2_pull_o32s32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
|
||||
{
|
||||
uint32_t ofs, size;
|
||||
if (smb2_oob(buf, ptr, 8)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
ofs = IVAL(ptr, 0);
|
||||
size = IVAL(ptr, 4);
|
||||
if (ofs == 0 || size == 0) {
|
||||
*blob = data_blob(NULL, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (smb2_oob(buf, buf->hdr + ofs, size)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob->data);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a uint32_t length/ uint32_t ofs/blob triple from a data blob
|
||||
the ptr points to the start of the offset/length pair
|
||||
*/
|
||||
NTSTATUS smb2_pull_s32o32_blob(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx, uint8_t *ptr, DATA_BLOB *blob)
|
||||
{
|
||||
uint32_t ofs, size;
|
||||
if (smb2_oob(buf, ptr, 8)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
size = IVAL(ptr, 0);
|
||||
ofs = IVAL(ptr, 4);
|
||||
if (ofs == 0 || size == 0) {
|
||||
*blob = data_blob(NULL, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (smb2_oob(buf, buf->hdr + ofs, size)) {
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
}
|
||||
*blob = data_blob_talloc(mem_ctx, buf->hdr + ofs, size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(blob->data);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a string in a uint16_t ofs/ uint16_t length/blob format
|
||||
UTF-16 without termination
|
||||
*/
|
||||
NTSTATUS smb2_pull_o16s16_string(struct smb2_request_buffer *buf, TALLOC_CTX *mem_ctx,
|
||||
uint8_t *ptr, const char **str)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
ssize_t size;
|
||||
void *vstr;
|
||||
|
||||
status = smb2_pull_o16s16_blob(buf, mem_ctx, ptr, &blob);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (blob.length == 0) {
|
||||
char *s;
|
||||
s = talloc_strdup(mem_ctx, "");
|
||||
NT_STATUS_HAVE_NO_MEMORY(s);
|
||||
*str = s;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
size = convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
|
||||
blob.data, blob.length, &vstr);
|
||||
data_blob_free(&blob);
|
||||
(*str) = vstr;
|
||||
if (size == -1) {
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
push a string in a uint16_t ofs/ uint16_t length/blob format
|
||||
UTF-16 without termination
|
||||
*/
|
||||
NTSTATUS smb2_push_o16s16_string(struct smb2_request_buffer *buf,
|
||||
uint16_t ofs, const char *str)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
ssize_t size;
|
||||
|
||||
if (strcmp("", str) == 0) {
|
||||
return smb2_push_o16s16_blob(buf, ofs, data_blob(NULL, 0));
|
||||
}
|
||||
|
||||
size = convert_string_talloc(buf->buffer, CH_UNIX, CH_UTF16,
|
||||
str, strlen(str), (void **)&blob.data);
|
||||
if (size == -1) {
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
blob.length = size;
|
||||
|
||||
status = smb2_push_o16s16_blob(buf, ofs, blob);
|
||||
data_blob_free(&blob);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
push a file handle into a buffer
|
||||
*/
|
||||
void smb2_push_handle(uint8_t *data, struct smb2_handle *h)
|
||||
{
|
||||
SBVAL(data, 0, h->data[0]);
|
||||
SBVAL(data, 8, h->data[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
pull a file handle from a buffer
|
||||
*/
|
||||
void smb2_pull_handle(uint8_t *ptr, struct smb2_handle *h)
|
||||
{
|
||||
h->data[0] = BVAL(ptr, 0);
|
||||
h->data[1] = BVAL(ptr, 8);
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client session handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "libcli/composite/composite.h"
|
||||
#include "auth/gensec/gensec.h"
|
||||
|
||||
/*
|
||||
initialise a smb2_session structure
|
||||
*/
|
||||
struct smb2_session *smb2_session_init(struct smb2_transport *transport,
|
||||
TALLOC_CTX *parent_ctx, BOOL primary)
|
||||
{
|
||||
struct smb2_session *session;
|
||||
NTSTATUS status;
|
||||
|
||||
session = talloc_zero(parent_ctx, struct smb2_session);
|
||||
if (!session) {
|
||||
return NULL;
|
||||
}
|
||||
if (primary) {
|
||||
session->transport = talloc_steal(session, transport);
|
||||
} else {
|
||||
session->transport = talloc_reference(session, transport);
|
||||
}
|
||||
|
||||
/* prepare a gensec context for later use */
|
||||
status = gensec_client_start(session, &session->gensec,
|
||||
session->transport->socket->event.ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gensec_want_feature(session->gensec, GENSEC_FEATURE_SESSION_KEY);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
/*
|
||||
send a session setup request
|
||||
*/
|
||||
struct smb2_request *smb2_session_setup_send(struct smb2_session *session,
|
||||
struct smb2_session_setup *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
NTSTATUS status;
|
||||
|
||||
req = smb2_request_init(session->transport, SMB2_OP_SESSSETUP,
|
||||
0x18, True, io->in.secblob.length);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, session->uid);
|
||||
SSVAL(req->out.body, 0x02, io->in._pad); /* pad */
|
||||
SIVAL(req->out.body, 0x04, io->in.unknown2);
|
||||
SIVAL(req->out.body, 0x08, io->in.unknown3);
|
||||
|
||||
req->session = session;
|
||||
|
||||
status = smb2_push_o16s16_blob(&req->out, 0x0C, io->in.secblob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
SBVAL(req->out.body, 0x10, io->in.unknown4);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a session setup reply
|
||||
*/
|
||||
NTSTATUS smb2_session_setup_recv(struct smb2_request *req, TALLOC_CTX *mem_ctx,
|
||||
struct smb2_session_setup *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (!smb2_request_receive(req) ||
|
||||
(smb2_request_is_error(req) &&
|
||||
!NT_STATUS_EQUAL(req->status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x08, True);
|
||||
|
||||
io->out._pad = SVAL(req->in.body, 0x02);
|
||||
io->out.uid = BVAL(req->in.hdr, SMB2_HDR_UID);
|
||||
|
||||
status = smb2_pull_o16s16_blob(&req->in, mem_ctx, req->in.body+0x04, &io->out.secblob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
smb2_request_destroy(req);
|
||||
return status;
|
||||
}
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync session setup request
|
||||
*/
|
||||
NTSTATUS smb2_session_setup(struct smb2_session *session,
|
||||
TALLOC_CTX *mem_ctx, struct smb2_session_setup *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_session_setup_send(session, io);
|
||||
return smb2_session_setup_recv(req, mem_ctx, io);
|
||||
}
|
||||
|
||||
|
||||
struct smb2_session_state {
|
||||
struct smb2_session_setup io;
|
||||
struct smb2_request *req;
|
||||
NTSTATUS gensec_status;
|
||||
};
|
||||
|
||||
/*
|
||||
handle continuations of the spnego session setup
|
||||
*/
|
||||
static void session_request_handler(struct smb2_request *req)
|
||||
{
|
||||
struct composite_context *c = talloc_get_type(req->async.private,
|
||||
struct composite_context);
|
||||
struct smb2_session_state *state = talloc_get_type(c->private_data,
|
||||
struct smb2_session_state);
|
||||
struct smb2_session *session = req->session;
|
||||
|
||||
c->status = smb2_session_setup_recv(req, c, &state->io);
|
||||
if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
|
||||
(NT_STATUS_IS_OK(c->status) &&
|
||||
NT_STATUS_EQUAL(state->gensec_status, NT_STATUS_MORE_PROCESSING_REQUIRED))) {
|
||||
NTSTATUS session_key_err;
|
||||
DATA_BLOB session_key;
|
||||
c->status = gensec_update(session->gensec, c,
|
||||
state->io.out.secblob,
|
||||
&state->io.in.secblob);
|
||||
state->gensec_status = c->status;
|
||||
|
||||
session_key_err = gensec_session_key(session->gensec, &session_key);
|
||||
if (NT_STATUS_IS_OK(session_key_err)) {
|
||||
session->session_key = session_key;
|
||||
}
|
||||
}
|
||||
|
||||
session->uid = state->io.out.uid;
|
||||
|
||||
if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
state->req = smb2_session_setup_send(session, &state->io);
|
||||
if (state->req == NULL) {
|
||||
composite_error(c, NT_STATUS_NO_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
state->req->async.fn = session_request_handler;
|
||||
state->req->async.private = c;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(c->status)) {
|
||||
composite_error(c, c->status);
|
||||
return;
|
||||
}
|
||||
|
||||
composite_done(c);
|
||||
}
|
||||
|
||||
/*
|
||||
a composite function that does a full SPNEGO session setup
|
||||
*/
|
||||
struct composite_context *smb2_session_setup_spnego_send(struct smb2_session *session,
|
||||
struct cli_credentials *credentials)
|
||||
{
|
||||
struct composite_context *c;
|
||||
struct smb2_session_state *state;
|
||||
|
||||
c = composite_create(session, session->transport->socket->event.ctx);
|
||||
if (c == NULL) return NULL;
|
||||
|
||||
state = talloc(c, struct smb2_session_state);
|
||||
if (composite_nomem(state, c)) return c;
|
||||
c->private_data = state;
|
||||
|
||||
ZERO_STRUCT(state->io);
|
||||
state->io.in._pad = 0x0000;
|
||||
state->io.in.unknown2 = 0x0000000F;
|
||||
state->io.in.unknown3 = 0x00000000;
|
||||
state->io.in.unknown4 = 0; /* uint64_t */
|
||||
|
||||
c->status = gensec_set_credentials(session->gensec, credentials);
|
||||
if (!composite_is_ok(c)) return c;
|
||||
|
||||
c->status = gensec_set_target_hostname(session->gensec,
|
||||
session->transport->socket->hostname);
|
||||
if (!composite_is_ok(c)) return c;
|
||||
|
||||
c->status = gensec_set_target_service(session->gensec, "cifs");
|
||||
if (!composite_is_ok(c)) return c;
|
||||
|
||||
c->status = gensec_start_mech_by_oid(session->gensec, GENSEC_OID_SPNEGO);
|
||||
if (!composite_is_ok(c)) return c;
|
||||
|
||||
c->status = gensec_update(session->gensec, c,
|
||||
session->transport->negotiate.secblob,
|
||||
&state->io.in.secblob);
|
||||
if (!NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
composite_error(c, c->status);
|
||||
return c;
|
||||
}
|
||||
state->gensec_status = c->status;
|
||||
|
||||
state->req = smb2_session_setup_send(session, &state->io);
|
||||
composite_continue_smb2(c, state->req, session_request_handler, c);
|
||||
return c;
|
||||
}
|
||||
|
||||
/*
|
||||
receive a composite session setup reply
|
||||
*/
|
||||
NTSTATUS smb2_session_setup_spnego_recv(struct composite_context *c)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = composite_wait(c);
|
||||
talloc_free(c);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
sync version of smb2_session_setup_spnego
|
||||
*/
|
||||
NTSTATUS smb2_session_setup_spnego(struct smb2_session *session,
|
||||
struct cli_credentials *credentials)
|
||||
{
|
||||
struct composite_context *c = smb2_session_setup_spnego_send(session, credentials);
|
||||
return smb2_session_setup_spnego_recv(c);
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client setinfo calls
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a setinfo request
|
||||
*/
|
||||
struct smb2_request *smb2_setinfo_send(struct smb2_tree *tree, struct smb2_setinfo *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_SETINFO, 0x20, True, io->in.blob.length);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.level);
|
||||
|
||||
status = smb2_push_s32o32_blob(&req->out, 0x04, io->in.blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SIVAL(req->out.body, 0x0C, io->in.flags);
|
||||
smb2_push_handle(req->out.body+0x10, &io->in.file.handle);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a setinfo reply
|
||||
*/
|
||||
NTSTATUS smb2_setinfo_recv(struct smb2_request *req)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x02, False);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync setinfo request
|
||||
*/
|
||||
NTSTATUS smb2_setinfo(struct smb2_tree *tree, struct smb2_setinfo *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_setinfo_send(tree, io);
|
||||
return smb2_setinfo_recv(req);
|
||||
}
|
||||
|
||||
/*
|
||||
level specific file setinfo call - async send
|
||||
*/
|
||||
struct smb2_request *smb2_setinfo_file_send(struct smb2_tree *tree, union smb_setfileinfo *io)
|
||||
{
|
||||
struct smb2_setinfo b;
|
||||
uint16_t smb2_level = smb2_getinfo_map_level(io->generic.level, SMB2_GETINFO_FILE);
|
||||
struct smb2_request *req;
|
||||
|
||||
if (smb2_level == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(b);
|
||||
b.in.level = smb2_level;
|
||||
b.in.file.handle = io->generic.in.file.handle;
|
||||
if (!smb_raw_setfileinfo_passthru(tree, io->generic.level, io, &b.in.blob)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (io->generic.level == RAW_SFILEINFO_SEC_DESC) {
|
||||
b.in.flags = io->set_secdesc.in.secinfo_flags;
|
||||
}
|
||||
|
||||
req = smb2_setinfo_send(tree, &b);
|
||||
data_blob_free(&b.in.blob);
|
||||
return req;
|
||||
}
|
||||
|
||||
/*
|
||||
level specific file setinfo call - sync
|
||||
*/
|
||||
NTSTATUS smb2_setinfo_file(struct smb2_tree *tree, union smb_setfileinfo *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_setinfo_file_send(tree, io);
|
||||
return smb2_setinfo_recv(req);
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client library header
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
struct smb2_options {
|
||||
uint32_t timeout;
|
||||
};
|
||||
|
||||
/*
|
||||
information returned from the negotiate response
|
||||
*/
|
||||
struct smb2_negotiate {
|
||||
DATA_BLOB secblob;
|
||||
};
|
||||
|
||||
/* this is the context for the smb2 transport layer */
|
||||
struct smb2_transport {
|
||||
/* socket level info */
|
||||
struct smbcli_socket *socket;
|
||||
|
||||
struct smb2_options options;
|
||||
struct smb2_negotiate negotiate;
|
||||
|
||||
/* next seqnum to allocate */
|
||||
uint64_t seqnum;
|
||||
|
||||
/* a list of requests that are pending for receive on this
|
||||
connection */
|
||||
struct smb2_request *pending_recv;
|
||||
|
||||
/* context of the stream -> packet parser */
|
||||
struct packet_context *packet;
|
||||
|
||||
/* an idle function - if this is defined then it will be
|
||||
called once every period microseconds while we are waiting
|
||||
for a packet */
|
||||
struct {
|
||||
void (*func)(struct smb2_transport *, void *);
|
||||
void *private;
|
||||
uint_t period;
|
||||
} idle;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
SMB2 tree context
|
||||
*/
|
||||
struct smb2_tree {
|
||||
struct smb2_session *session;
|
||||
uint32_t tid;
|
||||
};
|
||||
|
||||
/*
|
||||
SMB2 session context
|
||||
*/
|
||||
struct smb2_session {
|
||||
struct smb2_transport *transport;
|
||||
struct gensec_security *gensec;
|
||||
uint64_t uid;
|
||||
DATA_BLOB session_key;
|
||||
};
|
||||
|
||||
|
||||
struct smb2_request_buffer {
|
||||
/* the raw SMB2 buffer, including the 4 byte length header */
|
||||
uint8_t *buffer;
|
||||
|
||||
/* the size of the raw buffer, including 4 byte header */
|
||||
size_t size;
|
||||
|
||||
/* how much has been allocated - on reply the buffer is over-allocated to
|
||||
prevent too many realloc() calls
|
||||
*/
|
||||
size_t allocated;
|
||||
|
||||
/* the start of the SMB2 header - this is always buffer+4 */
|
||||
uint8_t *hdr;
|
||||
|
||||
/* the packet body */
|
||||
uint8_t *body;
|
||||
size_t body_fixed;
|
||||
size_t body_size;
|
||||
|
||||
/* this point to the next dynamic byte that can be used
|
||||
* this will be moved when some dynamic data is pushed
|
||||
*/
|
||||
uint8_t *dynamic;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
a client request moves between the following 4 states.
|
||||
*/
|
||||
enum smb2_request_state {SMB2_REQUEST_INIT, /* we are creating the request */
|
||||
SMB2_REQUEST_RECV, /* we are waiting for a matching reply */
|
||||
SMB2_REQUEST_DONE, /* the request is finished */
|
||||
SMB2_REQUEST_ERROR}; /* a packet or transport level error has occurred */
|
||||
|
||||
/* the context for a single SMB2 request */
|
||||
struct smb2_request {
|
||||
/* allow a request to be part of a list of requests */
|
||||
struct smb2_request *next, *prev;
|
||||
|
||||
/* each request is in one of 3 possible states */
|
||||
enum smb2_request_state state;
|
||||
|
||||
struct smb2_transport *transport;
|
||||
struct smb2_session *session;
|
||||
struct smb2_tree *tree;
|
||||
|
||||
uint64_t seqnum;
|
||||
|
||||
struct {
|
||||
BOOL do_cancel;
|
||||
BOOL can_cancel;
|
||||
uint32_t pending_id;
|
||||
} cancel;
|
||||
|
||||
/* the NT status for this request. Set by packet receive code
|
||||
or code detecting error. */
|
||||
NTSTATUS status;
|
||||
|
||||
struct smb2_request_buffer in;
|
||||
struct smb2_request_buffer out;
|
||||
|
||||
/* information on what to do with a reply when it is received
|
||||
asyncronously. If this is not setup when a reply is received then
|
||||
the reply is discarded
|
||||
|
||||
The private pointer is private to the caller of the client
|
||||
library (the application), not private to the library
|
||||
*/
|
||||
struct {
|
||||
void (*fn)(struct smb2_request *);
|
||||
void *private;
|
||||
} async;
|
||||
};
|
||||
|
||||
|
||||
#define SMB2_MIN_SIZE 0x42
|
||||
|
||||
/* offsets into header elements */
|
||||
#define SMB2_HDR_LENGTH 0x04
|
||||
#define SMB2_HDR_PAD1 0x06
|
||||
#define SMB2_HDR_STATUS 0x08
|
||||
#define SMB2_HDR_OPCODE 0x0c
|
||||
#define SMB2_HDR_UNKNOWN1 0x0e
|
||||
#define SMB2_HDR_FLAGS 0x10
|
||||
#define SMB2_HDR_UNKNOWN2 0x14
|
||||
#define SMB2_HDR_SEQNUM 0x18
|
||||
#define SMB2_HDR_PID 0x20
|
||||
#define SMB2_HDR_TID 0x24
|
||||
#define SMB2_HDR_UID 0x28 /* 64 bit */
|
||||
#define SMB2_HDR_SIG 0x30 /* guess ... */
|
||||
#define SMB2_HDR_BODY 0x40
|
||||
|
||||
/* SMB2 opcodes */
|
||||
#define SMB2_OP_NEGPROT 0x00
|
||||
#define SMB2_OP_SESSSETUP 0x01
|
||||
#define SMB2_OP_LOGOFF 0x02
|
||||
#define SMB2_OP_TCON 0x03
|
||||
#define SMB2_OP_TDIS 0x04
|
||||
#define SMB2_OP_CREATE 0x05
|
||||
#define SMB2_OP_CLOSE 0x06
|
||||
#define SMB2_OP_FLUSH 0x07
|
||||
#define SMB2_OP_READ 0x08
|
||||
#define SMB2_OP_WRITE 0x09
|
||||
#define SMB2_OP_LOCK 0x0a
|
||||
#define SMB2_OP_IOCTL 0x0b
|
||||
#define SMB2_OP_CANCEL 0x0c
|
||||
#define SMB2_OP_KEEPALIVE 0x0d
|
||||
#define SMB2_OP_FIND 0x0e
|
||||
#define SMB2_OP_NOTIFY 0x0f
|
||||
#define SMB2_OP_GETINFO 0x10
|
||||
#define SMB2_OP_SETINFO 0x11
|
||||
#define SMB2_OP_BREAK 0x12
|
||||
|
||||
#define SMB2_MAGIC 0x424D53FE /* 0xFE 'S' 'M' 'B' */
|
||||
|
||||
/*
|
||||
check that a body has the expected size
|
||||
*/
|
||||
#define SMB2_CHECK_PACKET_RECV(req, size, dynamic) do { \
|
||||
size_t is_size = req->in.body_size; \
|
||||
uint16_t field_size = SVAL(req->in.body, 0); \
|
||||
uint16_t want_size = ((dynamic)?(size)+1:(size)); \
|
||||
if (is_size < (size)) { \
|
||||
DEBUG(0,("%s: buffer too small 0x%x. Expected 0x%x\n", \
|
||||
__location__, (unsigned)is_size, (unsigned)want_size)); \
|
||||
return NT_STATUS_BUFFER_TOO_SMALL; \
|
||||
}\
|
||||
if (field_size != want_size) { \
|
||||
DEBUG(0,("%s: unexpected fixed body size 0x%x. Expected 0x%x\n", \
|
||||
__location__, (unsigned)field_size, (unsigned)want_size)); \
|
||||
return NT_STATUS_INVALID_PARAMETER; \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client calls
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "libcli/raw/interfaces.h"
|
||||
|
||||
struct smb2_negprot {
|
||||
struct {
|
||||
/* static body buffer 38 (0x26) bytes */
|
||||
/* uint16_t buffer_code; 0x24 (why?) */
|
||||
uint16_t unknown1; /* 0x0001 */
|
||||
uint8_t unknown2[32]; /* all zero */
|
||||
uint16_t unknown3; /* 0x00000 */
|
||||
} in;
|
||||
struct {
|
||||
/* static body buffer 64 (0x40) bytes */
|
||||
/* uint16_t buffer_code; 0x41 = 0x40 + 1 */
|
||||
uint16_t _pad;
|
||||
uint32_t unknown2; /* 0x06 */
|
||||
uint8_t sessid[16];
|
||||
uint32_t unknown3; /* 0x0d */
|
||||
uint16_t unknown4; /* 0x00 */
|
||||
uint32_t unknown5; /* 0x01 */
|
||||
uint32_t unknown6; /* 0x01 */
|
||||
uint16_t unknown7; /* 0x01 */
|
||||
NTTIME current_time;
|
||||
NTTIME boot_time;
|
||||
/* uint16_t secblob_ofs */
|
||||
/* uint16_t secblob_size */
|
||||
uint32_t unknown9; /* 0x204d4c20 */
|
||||
|
||||
/* dynamic body buffer */
|
||||
DATA_BLOB secblob;
|
||||
} out;
|
||||
};
|
||||
|
||||
/* getinfo classes */
|
||||
#define SMB2_GETINFO_FILE 0x01
|
||||
#define SMB2_GETINFO_FS 0x02
|
||||
#define SMB2_GETINFO_SECURITY 0x03
|
||||
|
||||
/* NOTE! the getinfo fs and file levels exactly match up with the
|
||||
'passthru' SMB levels, which are levels >= 1000. The SMB2 client
|
||||
lib uses the names from the libcli/raw/ library */
|
||||
|
||||
struct smb2_getinfo {
|
||||
struct {
|
||||
/* static body buffer 40 (0x28) bytes */
|
||||
/* uint16_t buffer_code; 0x29 = 0x28 + 1 (why???) */
|
||||
uint16_t level;
|
||||
uint32_t max_response_size;
|
||||
uint32_t unknown1;
|
||||
uint32_t unknown2;
|
||||
uint32_t flags; /* level specific */
|
||||
uint32_t flags2; /* used by all_eas level */
|
||||
union smb_handle file;
|
||||
} in;
|
||||
|
||||
struct {
|
||||
/* static body buffer 8 (0x08) bytes */
|
||||
/* uint16_t buffer_code; 0x09 = 0x08 + 1 */
|
||||
/* uint16_t blob_ofs; */
|
||||
/* uint16_t blob_size; */
|
||||
|
||||
/* dynamic body */
|
||||
DATA_BLOB blob;
|
||||
} out;
|
||||
};
|
||||
|
||||
struct smb2_setinfo {
|
||||
struct {
|
||||
uint16_t level;
|
||||
uint32_t flags;
|
||||
union smb_handle file;
|
||||
DATA_BLOB blob;
|
||||
} in;
|
||||
};
|
||||
|
||||
struct cli_credentials;
|
||||
struct event_context;
|
||||
#include "libcli/smb2/smb2_proto.h"
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client tree handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
initialise a smb2_session structure
|
||||
*/
|
||||
struct smb2_tree *smb2_tree_init(struct smb2_session *session,
|
||||
TALLOC_CTX *parent_ctx, BOOL primary)
|
||||
{
|
||||
struct smb2_tree *tree;
|
||||
|
||||
tree = talloc_zero(parent_ctx, struct smb2_tree);
|
||||
if (!session) {
|
||||
return NULL;
|
||||
}
|
||||
if (primary) {
|
||||
tree->session = talloc_steal(tree, session);
|
||||
} else {
|
||||
tree->session = talloc_reference(tree, session);
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/*
|
||||
send a tree connect
|
||||
*/
|
||||
struct smb2_request *smb2_tree_connect_send(struct smb2_tree *tree,
|
||||
struct smb2_tree_connect *io)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
NTSTATUS status;
|
||||
|
||||
req = smb2_request_init(tree->session->transport, SMB2_OP_TCON,
|
||||
0x08, True, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SBVAL(req->out.hdr, SMB2_HDR_UID, tree->session->uid);
|
||||
|
||||
SSVAL(req->out.body, 0x02, io->in.unknown1);
|
||||
status = smb2_push_o16s16_string(&req->out, 0x04, io->in.path);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a tree connect reply
|
||||
*/
|
||||
NTSTATUS smb2_tree_connect_recv(struct smb2_request *req, struct smb2_tree_connect *io)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x10, False);
|
||||
|
||||
io->out.tid = IVAL(req->in.hdr, SMB2_HDR_TID);
|
||||
|
||||
io->out.unknown1 = SVAL(req->in.body, 0x02);
|
||||
io->out.unknown2 = IVAL(req->in.body, 0x04);
|
||||
io->out.unknown3 = IVAL(req->in.body, 0x08);
|
||||
io->out.access_mask = IVAL(req->in.body, 0x0C);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync tree connect request
|
||||
*/
|
||||
NTSTATUS smb2_tree_connect(struct smb2_tree *tree, struct smb2_tree_connect *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_tree_connect_send(tree, io);
|
||||
return smb2_tree_connect_recv(req, io);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client tdis handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a tdis request
|
||||
*/
|
||||
struct smb2_request *smb2_tdis_send(struct smb2_tree *tree)
|
||||
{
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_TDIS, 0x04, False, 0);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
SSVAL(req->out.body, 0x02, 0);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a tdis reply
|
||||
*/
|
||||
NTSTATUS smb2_tdis_recv(struct smb2_request *req)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
!smb2_request_is_ok(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x04, False);
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync tdis request
|
||||
*/
|
||||
NTSTATUS smb2_tdis(struct smb2_tree *tree)
|
||||
{
|
||||
struct smb2_request *req = smb2_tdis_send(tree);
|
||||
return smb2_tdis_recv(req);
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client transport context management functions
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/libcliraw.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
#include "lib/socket/socket.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/stream/packet.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
|
||||
/*
|
||||
an event has happened on the socket
|
||||
*/
|
||||
static void smb2_transport_event_handler(struct event_context *ev,
|
||||
struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct smb2_transport *transport = talloc_get_type(private,
|
||||
struct smb2_transport);
|
||||
if (flags & EVENT_FD_READ) {
|
||||
packet_recv(transport->packet);
|
||||
return;
|
||||
}
|
||||
if (flags & EVENT_FD_WRITE) {
|
||||
packet_queue_run(transport->packet);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a transport
|
||||
*/
|
||||
static int transport_destructor(struct smb2_transport *transport)
|
||||
{
|
||||
smb2_transport_dead(transport, NT_STATUS_LOCAL_DISCONNECT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle receive errors
|
||||
*/
|
||||
static void smb2_transport_error(void *private, NTSTATUS status)
|
||||
{
|
||||
struct smb2_transport *transport = talloc_get_type(private,
|
||||
struct smb2_transport);
|
||||
smb2_transport_dead(transport, status);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct smb2_transport *transport;
|
||||
|
||||
transport = talloc_zero(parent_ctx, struct smb2_transport);
|
||||
if (!transport) return NULL;
|
||||
|
||||
transport->socket = talloc_steal(transport, sock);
|
||||
|
||||
/* setup the stream -> packet parser */
|
||||
transport->packet = packet_init(transport);
|
||||
if (transport->packet == NULL) {
|
||||
talloc_free(transport);
|
||||
return NULL;
|
||||
}
|
||||
packet_set_private(transport->packet, transport);
|
||||
packet_set_socket(transport->packet, transport->socket->sock);
|
||||
packet_set_callback(transport->packet, smb2_transport_finish_recv);
|
||||
packet_set_full_request(transport->packet, packet_full_request_nbt);
|
||||
packet_set_error_handler(transport->packet, smb2_transport_error);
|
||||
packet_set_event_context(transport->packet, transport->socket->event.ctx);
|
||||
packet_set_nofree(transport->packet);
|
||||
|
||||
/* take over event handling from the socket layer - it only
|
||||
handles events up until we are connected */
|
||||
talloc_free(transport->socket->event.fde);
|
||||
transport->socket->event.fde = event_add_fd(transport->socket->event.ctx,
|
||||
transport->socket,
|
||||
socket_get_fd(transport->socket->sock),
|
||||
EVENT_FD_READ,
|
||||
smb2_transport_event_handler,
|
||||
transport);
|
||||
|
||||
packet_set_fde(transport->packet, transport->socket->event.fde);
|
||||
packet_set_serialise(transport->packet);
|
||||
|
||||
talloc_set_destructor(transport, transport_destructor);
|
||||
|
||||
transport->options.timeout = 30;
|
||||
|
||||
return transport;
|
||||
}
|
||||
|
||||
/*
|
||||
mark the transport as dead
|
||||
*/
|
||||
void smb2_transport_dead(struct smb2_transport *transport, NTSTATUS status)
|
||||
{
|
||||
smbcli_sock_dead(transport->socket);
|
||||
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_UNSUCCESSFUL, status)) {
|
||||
status = NT_STATUS_UNEXPECTED_NETWORK_ERROR;
|
||||
}
|
||||
|
||||
/* kill all pending receives */
|
||||
while (transport->pending_recv) {
|
||||
struct smb2_request *req = transport->pending_recv;
|
||||
req->state = SMB2_REQUEST_ERROR;
|
||||
req->status = status;
|
||||
DLIST_REMOVE(transport->pending_recv, req);
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
we have a full request in our receive buffer - match it to a pending request
|
||||
and process
|
||||
*/
|
||||
static NTSTATUS smb2_transport_finish_recv(void *private, DATA_BLOB blob)
|
||||
{
|
||||
struct smb2_transport *transport = talloc_get_type(private,
|
||||
struct smb2_transport);
|
||||
uint8_t *buffer, *hdr;
|
||||
int len;
|
||||
struct smb2_request *req = NULL;
|
||||
uint64_t seqnum;
|
||||
uint32_t flags;
|
||||
uint16_t buffer_code;
|
||||
uint32_t dynamic_size;
|
||||
uint32_t i;
|
||||
|
||||
buffer = blob.data;
|
||||
len = blob.length;
|
||||
|
||||
hdr = buffer+NBT_HDR_SIZE;
|
||||
|
||||
if (len < SMB2_MIN_SIZE) {
|
||||
DEBUG(1,("Discarding smb2 reply of size %d\n", len));
|
||||
goto error;
|
||||
}
|
||||
|
||||
flags = IVAL(hdr, SMB2_HDR_FLAGS);
|
||||
seqnum = BVAL(hdr, SMB2_HDR_SEQNUM);
|
||||
|
||||
/* match the incoming request against the list of pending requests */
|
||||
for (req=transport->pending_recv; req; req=req->next) {
|
||||
if (req->seqnum == seqnum) break;
|
||||
}
|
||||
|
||||
if (!req) {
|
||||
DEBUG(1,("Discarding unmatched reply with seqnum 0x%llx op %d\n",
|
||||
(long long)seqnum, SVAL(hdr, SMB2_HDR_OPCODE)));
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* fill in the 'in' portion of the matching request */
|
||||
req->in.buffer = buffer;
|
||||
talloc_steal(req, buffer);
|
||||
req->in.size = len;
|
||||
req->in.allocated = req->in.size;
|
||||
|
||||
req->in.hdr = hdr;
|
||||
req->in.body = hdr+SMB2_HDR_BODY;
|
||||
req->in.body_size = req->in.size - (SMB2_HDR_BODY+NBT_HDR_SIZE);
|
||||
req->status = NT_STATUS(IVAL(hdr, SMB2_HDR_STATUS));
|
||||
|
||||
if (NT_STATUS_EQUAL(req->status, STATUS_PENDING)) {
|
||||
if (flags & 0x00000002) {
|
||||
req->cancel.can_cancel = True;
|
||||
req->cancel.pending_id = IVAL(hdr, SMB2_HDR_PID);
|
||||
for (i=0; i< req->cancel.do_cancel; i++) {
|
||||
smb2_cancel(req);
|
||||
}
|
||||
}
|
||||
talloc_free(buffer);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
buffer_code = SVAL(req->in.body, 0);
|
||||
req->in.body_fixed = (buffer_code & ~1);
|
||||
req->in.dynamic = NULL;
|
||||
dynamic_size = req->in.body_size - req->in.body_fixed;
|
||||
if (dynamic_size != 0 && (buffer_code & 1)) {
|
||||
req->in.dynamic = req->in.body + req->in.body_fixed;
|
||||
if (smb2_oob(&req->in, req->in.dynamic, dynamic_size)) {
|
||||
DEBUG(1,("SMB2 request invalid dynamic size 0x%x\n",
|
||||
dynamic_size));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG(2, ("SMB2 RECV seqnum=0x%llx\n", (long long)req->seqnum));
|
||||
dump_data(5, req->in.body, req->in.body_size);
|
||||
|
||||
/* if this request has an async handler then call that to
|
||||
notify that the reply has been received. This might destroy
|
||||
the request so it must happen last */
|
||||
DLIST_REMOVE(transport->pending_recv, req);
|
||||
req->state = SMB2_REQUEST_DONE;
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
error:
|
||||
dump_data(5, buffer, len);
|
||||
if (req) {
|
||||
DLIST_REMOVE(transport->pending_recv, req);
|
||||
req->state = SMB2_REQUEST_ERROR;
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
} else {
|
||||
talloc_free(buffer);
|
||||
}
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
handle timeouts of individual smb requests
|
||||
*/
|
||||
static void smb2_timeout_handler(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *private)
|
||||
{
|
||||
struct smb2_request *req = talloc_get_type(private, struct smb2_request);
|
||||
|
||||
if (req->state == SMB2_REQUEST_RECV) {
|
||||
DLIST_REMOVE(req->transport->pending_recv, req);
|
||||
}
|
||||
req->status = NT_STATUS_IO_TIMEOUT;
|
||||
req->state = SMB2_REQUEST_ERROR;
|
||||
if (req->async.fn) {
|
||||
req->async.fn(req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
destroy a request
|
||||
*/
|
||||
static int smb2_request_destructor(struct smb2_request *req)
|
||||
{
|
||||
if (req->state == SMB2_REQUEST_RECV) {
|
||||
DLIST_REMOVE(req->transport->pending_recv, req);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
put a request into the send queue
|
||||
*/
|
||||
void smb2_transport_send(struct smb2_request *req)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
_smb2_setlen(req->out.buffer, req->out.size - NBT_HDR_SIZE);
|
||||
|
||||
DEBUG(2, ("SMB2 send seqnum=0x%llx\n", (long long)req->seqnum));
|
||||
dump_data(5, req->out.body, req->out.body_size);
|
||||
|
||||
/* check if the transport is dead */
|
||||
if (req->transport->socket->sock == NULL) {
|
||||
req->state = SMB2_REQUEST_ERROR;
|
||||
req->status = NT_STATUS_NET_WRITE_FAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
blob = data_blob_const(req->out.buffer, req->out.size);
|
||||
status = packet_send(req->transport->packet, blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
req->state = SMB2_REQUEST_ERROR;
|
||||
req->status = status;
|
||||
return;
|
||||
}
|
||||
|
||||
req->state = SMB2_REQUEST_RECV;
|
||||
DLIST_ADD(req->transport->pending_recv, req);
|
||||
|
||||
/* add a timeout */
|
||||
if (req->transport->options.timeout) {
|
||||
event_add_timed(req->transport->socket->event.ctx, req,
|
||||
timeval_current_ofs(req->transport->options.timeout, 0),
|
||||
smb2_timeout_handler, req);
|
||||
}
|
||||
|
||||
talloc_set_destructor(req, smb2_request_destructor);
|
||||
}
|
||||
|
||||
static void idle_handler(struct event_context *ev,
|
||||
struct timed_event *te, struct timeval t, void *private)
|
||||
{
|
||||
struct smb2_transport *transport = talloc_get_type(private,
|
||||
struct smb2_transport);
|
||||
struct timeval next = timeval_add(&t, 0, transport->idle.period);
|
||||
transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
|
||||
transport,
|
||||
next,
|
||||
idle_handler, transport);
|
||||
transport->idle.func(transport, transport->idle.private);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the idle handler for a transport
|
||||
the period is in microseconds
|
||||
*/
|
||||
void smb2_transport_idle_handler(struct smb2_transport *transport,
|
||||
void (*idle_func)(struct smb2_transport *, void *),
|
||||
uint64_t period,
|
||||
void *private)
|
||||
{
|
||||
transport->idle.func = idle_func;
|
||||
transport->idle.private = private;
|
||||
transport->idle.period = period;
|
||||
|
||||
if (transport->socket->event.te != NULL) {
|
||||
talloc_free(transport->socket->event.te);
|
||||
}
|
||||
|
||||
transport->socket->event.te = event_add_timed(transport->socket->event.ctx,
|
||||
transport,
|
||||
timeval_current_ofs(0, period),
|
||||
idle_handler, transport);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
SMB2 client write call
|
||||
|
||||
Copyright (C) Andrew Tridgell 2005
|
||||
|
||||
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 2 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, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/smb2/smb2.h"
|
||||
#include "libcli/smb2/smb2_calls.h"
|
||||
|
||||
/*
|
||||
send a write request
|
||||
*/
|
||||
struct smb2_request *smb2_write_send(struct smb2_tree *tree, struct smb2_write *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct smb2_request *req;
|
||||
|
||||
req = smb2_request_init_tree(tree, SMB2_OP_WRITE, 0x30, True, io->in.data.length);
|
||||
if (req == NULL) return NULL;
|
||||
|
||||
status = smb2_push_o16s32_blob(&req->out, 0x02, io->in.data);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
smb2_transport_send(req);
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
recv a write reply
|
||||
*/
|
||||
NTSTATUS smb2_write_recv(struct smb2_request *req, struct smb2_write *io)
|
||||
{
|
||||
if (!smb2_request_receive(req) ||
|
||||
smb2_request_is_error(req)) {
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
SMB2_CHECK_PACKET_RECV(req, 0x10, True);
|
||||
|
||||
io->out._pad = SVAL(req->in.body, 0x02);
|
||||
io->out.nwritten = IVAL(req->in.body, 0x04);
|
||||
io->out.unknown1 = BVAL(req->in.body, 0x08);
|
||||
|
||||
return smb2_request_destroy(req);
|
||||
}
|
||||
|
||||
/*
|
||||
sync write request
|
||||
*/
|
||||
NTSTATUS smb2_write(struct smb2_tree *tree, struct smb2_write *io)
|
||||
{
|
||||
struct smb2_request *req = smb2_write_send(tree, io);
|
||||
return smb2_write_recv(req, io);
|
||||
}
|
||||
Reference in New Issue
Block a user