1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-11 05:18:09 +03:00
samba-mirror/source4/libcli/smb2/request.c

207 lines
5.7 KiB
C
Raw Normal View History

/*
Unix SMB/CIFS implementation.
SMB2 client request 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 "include/dlinklist.h"
#include "lib/events/events.h"
/*
initialise a smb2 request
*/
struct smb2_request *smb2_request_init(struct smb2_transport *transport,
uint16_t opcode, uint32_t body_size)
{
struct smb2_request *req;
req = talloc(transport, struct smb2_request);
if (req == NULL) return NULL;
req->state = SMB2_REQUEST_INIT;
req->transport = transport;
req->seqnum = transport->seqnum++;
req->status = NT_STATUS_OK;
req->async.fn = NULL;
req->next = req->prev = NULL;
ZERO_STRUCT(req->in);
req->out.allocated = SMB2_HDR_BODY+NBT_HDR_SIZE+body_size;
req->out.buffer = talloc_size(req, req->out.allocated);
if (req->out.buffer == NULL) {
talloc_free(req);
return NULL;
}
req->out.size = SMB2_HDR_BODY+NBT_HDR_SIZE + body_size;
req->out.hdr = req->out.buffer + NBT_HDR_SIZE;
req->out.body = req->out.hdr + SMB2_HDR_BODY;
req->out.body_size = body_size;
req->out.ptr = req->out.body;
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_PAD2, 0);
SIVAL(req->out.hdr, SMB2_HDR_FLAGS, 0);
SIVAL(req->out.hdr, SMB2_HDR_UNKNOWN, 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, (uint64_t)0);
memset(req->out.hdr+SMB2_HDR_SIG, 0, 16);
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 == SMBCLI_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);
}
/*
check if a range in the reply body is out of bounds
*/
BOOL smb2_oob_in(struct smb2_request *req, const uint8_t *ptr, uint_t size)
{
/* be careful with wraparound! */
if (ptr < req->in.body ||
ptr >= req->in.body + req->in.body_size ||
size > req->in.body_size ||
ptr + size > req->in.body + req->in.body_size) {
return True;
}
return False;
}
/*
check if a range in the outgoing body is out of bounds
*/
BOOL smb2_oob_out(struct smb2_request *req, const uint8_t *ptr, uint_t size)
{
/* be careful with wraparound! */
if (ptr < req->out.body ||
ptr >= req->out.body + req->out.body_size ||
size > req->out.body_size ||
ptr + size > req->out.body + req->out.body_size) {
return True;
}
return False;
}
/*
pull a data blob from the body of a reply
*/
DATA_BLOB smb2_pull_blob(struct smb2_request *req, uint8_t *ptr, uint_t size)
{
if (smb2_oob_in(req, ptr, size)) {
return data_blob(NULL, 0);
}
return data_blob_talloc(req, ptr, size);
}
/*
pull a ofs/length/blob triple into a data blob
the ptr points to the start of the offset/length pair
*/
NTSTATUS smb2_pull_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB *blob)
{
uint16_t ofs, size;
if (smb2_oob_in(req, ptr, 4)) {
return NT_STATUS_BUFFER_TOO_SMALL;
}
ofs = SVAL(ptr, 0);
size = SVAL(ptr, 2);
if (smb2_oob_in(req, req->in.hdr + ofs, size)) {
return NT_STATUS_BUFFER_TOO_SMALL;
}
*blob = data_blob_talloc(req, req->in.hdr+ofs, size);
NT_STATUS_HAVE_NO_MEMORY(blob->data);
return NT_STATUS_OK;
}
/*
push a ofs/length/blob triple into a data blob
the ptr points to the start of the offset/length pair
NOTE: assumes blob goes immediately after the offset/length pair. Needs
to be generalised
*/
NTSTATUS smb2_push_ofs_blob(struct smb2_request *req, uint8_t *ptr, DATA_BLOB blob)
{
if (smb2_oob_out(req, ptr, 4+blob.length)) {
return NT_STATUS_BUFFER_TOO_SMALL;
}
SSVAL(ptr, 0, 4 + (ptr - req->out.hdr));
SSVAL(ptr, 2, blob.length);
memcpy(ptr+4, blob.data, blob.length);
return NT_STATUS_OK;
}