2005-11-11 07:45:38 +03:00
/*
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 0213 9 , 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 ) ;
2005-11-11 11:00:05 +03:00
SBVAL ( req - > out . hdr , SMB2_HDR_UID , ( uint64_t ) 0 ) ;
2005-11-11 07:45:38 +03:00
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
*/
2005-11-11 10:23:45 +03:00
BOOL smb2_oob_in ( struct smb2_request * req , const uint8_t * ptr , uint_t size )
2005-11-11 07:45:38 +03:00
{
/* 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 ;
}
2005-11-11 10:23:45 +03:00
/*
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 ;
}
2005-11-11 07:45:38 +03:00
/*
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 )
{
2005-11-11 10:23:45 +03:00
if ( smb2_oob_in ( req , ptr , size ) ) {
2005-11-11 07:45:38 +03:00
return data_blob ( NULL , 0 ) ;
}
return data_blob_talloc ( req , ptr , size ) ;
}
2005-11-11 10:23:45 +03:00
/*
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 ;
}