2009-05-14 16:17:28 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
2010-04-29 01:56:12 +04:00
Copyright ( C ) Jeremy Allison 2010
2009-05-14 16:17:28 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2009-05-14 16:17:28 +04:00
# include "smbd/globals.h"
2009-08-12 19:52:55 +04:00
# include "../libcli/smb/smb_common.h"
2009-05-14 16:17:28 +04:00
# include "../lib/tsocket/tsocket.h"
2011-04-28 19:38:09 +04:00
# include "../lib/util/tevent_ntstatus.h"
2011-04-14 02:36:23 +04:00
# include "smbprofile.h"
2011-07-07 15:04:31 +04:00
# include "../lib/util/bitmap.h"
2011-07-08 21:44:29 +04:00
# include "../librpc/gen_ndr/krb5pac.h"
# include "auth.h"
2009-05-14 16:17:28 +04:00
2010-04-20 00:43:42 +04:00
# define OUTVEC_ALLOC_SIZE (SMB2_HDR_BODY + 9)
2010-04-18 08:20:17 +04:00
static const char * smb2_names [ ] = {
" SMB2_NEGPROT " ,
" SMB2_SESSSETUP " ,
" SMB2_LOGOFF " ,
" SMB2_TCON " ,
" SMB2_TDIS " ,
" SMB2_CREATE " ,
" SMB2_CLOSE " ,
" SMB2_FLUSH " ,
" SMB2_READ " ,
" SMB2_WRITE " ,
" SMB2_LOCK " ,
" SMB2_IOCTL " ,
" SMB2_CANCEL " ,
" SMB2_KEEPALIVE " ,
" SMB2_FIND " ,
" SMB2_NOTIFY " ,
" SMB2_GETINFO " ,
" SMB2_SETINFO " ,
" SMB2_BREAK "
} ;
const char * smb2_opcode_name ( uint16_t opcode )
{
2010-04-24 11:29:41 +04:00
if ( opcode > 0x12 ) {
2010-04-18 08:20:17 +04:00
return " Bad SMB2 opcode " ;
}
return smb2_names [ opcode ] ;
}
static void print_req_vectors ( struct smbd_smb2_request * req )
{
int i ;
for ( i = 0 ; i < req - > in . vector_count ; i + + ) {
dbgtext ( " \t req->in.vector[%u].iov_len = %u \n " ,
( unsigned int ) i ,
( unsigned int ) req - > in . vector [ i ] . iov_len ) ;
}
for ( i = 0 ; i < req - > out . vector_count ; i + + ) {
dbgtext ( " \t req->out.vector[%u].iov_len = %u \n " ,
( unsigned int ) i ,
( unsigned int ) req - > out . vector [ i ] . iov_len ) ;
}
}
2009-05-14 16:17:28 +04:00
bool smbd_is_smb2_header ( const uint8_t * inbuf , size_t size )
{
if ( size < ( 4 + SMB2_HDR_BODY ) ) {
return false ;
}
if ( IVAL ( inbuf , 4 ) ! = SMB2_MAGIC ) {
return false ;
}
return true ;
}
2009-08-07 17:21:07 +04:00
static NTSTATUS smbd_initialize_smb2 ( struct smbd_server_connection * sconn )
2009-05-14 16:17:28 +04:00
{
NTSTATUS status ;
int ret ;
2009-08-07 17:21:07 +04:00
TALLOC_FREE ( sconn - > smb1 . fde ) ;
2009-05-14 16:17:28 +04:00
2011-05-25 12:51:56 +04:00
sconn - > smb2 . event_ctx = server_event_context ( ) ;
2009-05-14 16:17:28 +04:00
2009-08-07 17:21:07 +04:00
sconn - > smb2 . recv_queue = tevent_queue_create ( sconn , " smb2 recv queue " ) ;
if ( sconn - > smb2 . recv_queue = = NULL ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
sconn - > smb2 . send_queue = tevent_queue_create ( sconn , " smb2 send queue " ) ;
if ( sconn - > smb2 . send_queue = = NULL ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
sconn - > smb2 . sessions . idtree = idr_init ( sconn ) ;
if ( sconn - > smb2 . sessions . idtree = = NULL ) {
2009-05-15 13:20:34 +04:00
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
sconn - > smb2 . sessions . limit = 0x0000FFFE ;
sconn - > smb2 . sessions . list = NULL ;
2010-12-11 02:46:41 +03:00
sconn - > smb2 . seqnum_low = 0 ;
2010-12-14 00:34:50 +03:00
sconn - > smb2 . credits_granted = 0 ;
2010-12-11 02:46:41 +03:00
sconn - > smb2 . max_credits = lp_smb2_max_credits ( ) ;
2010-12-22 05:07:52 +03:00
sconn - > smb2 . credits_bitmap = bitmap_talloc ( sconn ,
DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR * sconn - > smb2 . max_credits ) ;
2010-12-11 02:46:41 +03:00
if ( sconn - > smb2 . credits_bitmap = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-05-15 13:20:34 +04:00
2010-08-15 01:13:11 +04:00
ret = tstream_bsd_existing_socket ( sconn , sconn - > sock ,
2009-08-07 17:21:07 +04:00
& sconn - > smb2 . stream ) ;
2009-05-14 16:17:28 +04:00
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
return status ;
}
/* Ensure child is set to non-blocking mode */
2010-08-15 01:13:11 +04:00
set_blocking ( sconn - > sock , false ) ;
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
# define smb2_len(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
# define _smb2_setlen(_buf,len) do { \
uint8_t * buf = ( uint8_t * ) _buf ; \
buf [ 0 ] = 0 ; \
buf [ 1 ] = ( ( len ) & 0xFF0000 ) > > 16 ; \
buf [ 2 ] = ( ( len ) & 0xFF00 ) > > 8 ; \
buf [ 3 ] = ( len ) & 0xFF ; \
} while ( 0 )
static void smb2_setup_nbt_length ( struct iovec * vector , int count )
{
size_t len = 0 ;
int i ;
for ( i = 1 ; i < count ; i + + ) {
len + = vector [ i ] . iov_len ;
}
_smb2_setlen ( vector [ 0 ] . iov_base , len ) ;
}
2009-06-09 23:29:40 +04:00
static int smbd_smb2_request_parent_destructor ( struct smbd_smb2_request * * req )
{
if ( * req ) {
( * req ) - > parent = NULL ;
( * req ) - > mem_pool = NULL ;
}
return 0 ;
}
static int smbd_smb2_request_destructor ( struct smbd_smb2_request * req )
{
if ( req - > parent ) {
* req - > parent = NULL ;
talloc_free ( req - > mem_pool ) ;
}
return 0 ;
}
static struct smbd_smb2_request * smbd_smb2_request_allocate ( TALLOC_CTX * mem_ctx )
{
TALLOC_CTX * mem_pool ;
struct smbd_smb2_request * * parent ;
struct smbd_smb2_request * req ;
2010-04-23 10:52:19 +04:00
#if 0
/* Enable this to find subtle valgrind errors. */
mem_pool = talloc_init ( " smbd_smb2_request_allocate " ) ;
# else
2009-06-09 23:29:40 +04:00
mem_pool = talloc_pool ( mem_ctx , 8192 ) ;
2010-04-23 10:52:19 +04:00
# endif
2009-06-09 23:29:40 +04:00
if ( mem_pool = = NULL ) {
return NULL ;
}
parent = talloc ( mem_pool , struct smbd_smb2_request * ) ;
if ( parent = = NULL ) {
talloc_free ( mem_pool ) ;
return NULL ;
}
req = talloc_zero ( parent , struct smbd_smb2_request ) ;
if ( req = = NULL ) {
talloc_free ( mem_pool ) ;
return NULL ;
}
* parent = req ;
req - > mem_pool = mem_pool ;
req - > parent = parent ;
talloc_set_destructor ( parent , smbd_smb2_request_parent_destructor ) ;
talloc_set_destructor ( req , smbd_smb2_request_destructor ) ;
return req ;
}
2009-08-07 17:21:07 +04:00
static NTSTATUS smbd_smb2_request_create ( struct smbd_server_connection * sconn ,
2009-05-14 16:17:28 +04:00
const uint8_t * inbuf , size_t size ,
struct smbd_smb2_request * * _req )
{
struct smbd_smb2_request * req ;
uint32_t protocol_version ;
const uint8_t * inhdr = NULL ;
off_t ofs = 0 ;
uint16_t cmd ;
uint32_t next_command_ofs ;
if ( size < ( 4 + SMB2_HDR_BODY + 2 ) ) {
DEBUG ( 0 , ( " Invalid SMB2 packet length count %ld \n " , ( long ) size ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
inhdr = inbuf + 4 ;
protocol_version = IVAL ( inhdr , SMB2_HDR_PROTOCOL_ID ) ;
if ( protocol_version ! = SMB2_MAGIC ) {
DEBUG ( 0 , ( " Invalid SMB packet: protocol prefix: 0x%08X \n " ,
protocol_version ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
cmd = SVAL ( inhdr , SMB2_HDR_OPCODE ) ;
if ( cmd ! = SMB2_OP_NEGPROT ) {
DEBUG ( 0 , ( " Invalid SMB packet: first request: 0x%04X \n " ,
cmd ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
next_command_ofs = IVAL ( inhdr , SMB2_HDR_NEXT_COMMAND ) ;
if ( next_command_ofs ! = 0 ) {
DEBUG ( 0 , ( " Invalid SMB packet: next_command: 0x%08X \n " ,
next_command_ofs ) ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
2009-08-07 17:21:07 +04:00
req = smbd_smb2_request_allocate ( sconn ) ;
2009-05-14 16:17:28 +04:00
if ( req = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2009-08-07 17:21:07 +04:00
req - > sconn = sconn ;
2009-05-14 16:17:28 +04:00
talloc_steal ( req , inbuf ) ;
req - > in . vector = talloc_array ( req , struct iovec , 4 ) ;
if ( req - > in . vector = = NULL ) {
2009-06-09 23:29:40 +04:00
TALLOC_FREE ( req ) ;
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
req - > in . vector_count = 4 ;
memcpy ( req - > in . nbt_hdr , inbuf , 4 ) ;
ofs = 0 ;
2011-05-06 01:22:11 +04:00
req - > in . vector [ 0 ] . iov_base = discard_const_p ( void , req - > in . nbt_hdr ) ;
2009-05-14 16:17:28 +04:00
req - > in . vector [ 0 ] . iov_len = 4 ;
ofs + = req - > in . vector [ 0 ] . iov_len ;
2011-05-06 01:22:11 +04:00
req - > in . vector [ 1 ] . iov_base = discard_const_p ( void , ( inbuf + ofs ) ) ;
2009-05-14 16:17:28 +04:00
req - > in . vector [ 1 ] . iov_len = SMB2_HDR_BODY ;
ofs + = req - > in . vector [ 1 ] . iov_len ;
2011-05-06 01:22:11 +04:00
req - > in . vector [ 2 ] . iov_base = discard_const_p ( void , ( inbuf + ofs ) ) ;
2009-05-14 16:17:28 +04:00
req - > in . vector [ 2 ] . iov_len = SVAL ( inbuf , ofs ) & 0xFFFE ;
ofs + = req - > in . vector [ 2 ] . iov_len ;
if ( ofs > size ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2011-05-06 01:22:11 +04:00
req - > in . vector [ 3 ] . iov_base = discard_const_p ( void , ( inbuf + ofs ) ) ;
2009-05-14 16:17:28 +04:00
req - > in . vector [ 3 ] . iov_len = size - ofs ;
ofs + = req - > in . vector [ 3 ] . iov_len ;
req - > current_idx = 1 ;
* _req = req ;
return NT_STATUS_OK ;
}
2010-12-11 02:46:41 +03:00
static bool smb2_validate_message_id ( struct smbd_server_connection * sconn ,
const uint8_t * inhdr )
{
uint64_t message_id = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
struct bitmap * credits_bm = sconn - > smb2 . credits_bitmap ;
uint16_t opcode = IVAL ( inhdr , SMB2_HDR_OPCODE ) ;
unsigned int bitmap_offset ;
if ( opcode = = SMB2_OP_CANCEL ) {
/* SMB2_CANCEL requests by definition resend messageids. */
return true ;
}
if ( message_id < sconn - > smb2 . seqnum_low | |
message_id > ( sconn - > smb2 . seqnum_low +
2010-12-22 05:07:52 +03:00
( sconn - > smb2 . max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR ) ) ) {
2010-12-11 02:46:41 +03:00
DEBUG ( 0 , ( " smb2_validate_message_id: bad message_id "
2010-12-22 05:07:52 +03:00
" %llu (low = %llu, max = %lu) \n " ,
2010-12-11 02:46:41 +03:00
( unsigned long long ) message_id ,
( unsigned long long ) sconn - > smb2 . seqnum_low ,
2010-12-22 05:07:52 +03:00
( unsigned long ) sconn - > smb2 . max_credits ) ) ;
2010-12-11 02:46:41 +03:00
return false ;
}
/* client just used a credit. */
SMB_ASSERT ( sconn - > smb2 . credits_granted > 0 ) ;
sconn - > smb2 . credits_granted - = 1 ;
/* Mark the message_id as seen in the bitmap. */
bitmap_offset = ( unsigned int ) ( message_id %
2010-12-22 05:07:52 +03:00
( uint64_t ) ( sconn - > smb2 . max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR ) ) ;
2010-12-11 02:46:41 +03:00
if ( bitmap_query ( credits_bm , bitmap_offset ) ) {
DEBUG ( 0 , ( " smb2_validate_message_id: duplicate message_id "
" %llu (bm offset %u) \n " ,
( unsigned long long ) message_id ,
bitmap_offset ) ) ;
return false ;
}
bitmap_set ( credits_bm , bitmap_offset ) ;
if ( message_id = = sconn - > smb2 . seqnum_low + 1 ) {
/* Move the window forward by all the message_id's
already seen . */
while ( bitmap_query ( credits_bm , bitmap_offset ) ) {
DEBUG ( 10 , ( " smb2_validate_message_id: clearing "
" id %llu (position %u) from bitmap \n " ,
( unsigned long long ) ( sconn - > smb2 . seqnum_low + 1 ) ,
bitmap_offset ) ) ;
bitmap_clear ( credits_bm , bitmap_offset ) ;
sconn - > smb2 . seqnum_low + = 1 ;
bitmap_offset = ( bitmap_offset + 1 ) %
2010-12-22 05:07:52 +03:00
( sconn - > smb2 . max_credits * DEFAULT_SMB2_MAX_CREDIT_BITMAP_FACTOR ) ;
2010-12-11 02:46:41 +03:00
}
}
return true ;
}
static NTSTATUS smbd_smb2_request_validate ( struct smbd_smb2_request * req )
2009-06-08 15:30:32 +04:00
{
int count ;
int idx ;
bool compound_related = false ;
count = req - > in . vector_count ;
if ( count < 4 ) {
/* It's not a SMB2 request */
return NT_STATUS_INVALID_PARAMETER ;
}
for ( idx = 1 ; idx < count ; idx + = 3 ) {
const uint8_t * inhdr = NULL ;
uint32_t flags ;
if ( req - > in . vector [ idx ] . iov_len ! = SMB2_HDR_BODY ) {
return NT_STATUS_INVALID_PARAMETER ;
}
if ( req - > in . vector [ idx + 1 ] . iov_len < 2 ) {
return NT_STATUS_INVALID_PARAMETER ;
}
inhdr = ( const uint8_t * ) req - > in . vector [ idx ] . iov_base ;
2010-12-11 02:46:41 +03:00
/* Check the SMB2 header */
2009-06-08 15:30:32 +04:00
if ( IVAL ( inhdr , SMB2_HDR_PROTOCOL_ID ) ! = SMB2_MAGIC ) {
return NT_STATUS_INVALID_PARAMETER ;
}
2010-12-11 02:46:41 +03:00
if ( ! smb2_validate_message_id ( req - > sconn , inhdr ) ) {
return NT_STATUS_INVALID_PARAMETER ;
2010-04-07 03:31:26 +04:00
}
2010-02-25 03:16:30 +03:00
2009-06-08 15:30:32 +04:00
flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
if ( idx = = 1 ) {
/*
* the 1 st request should never have the
* SMB2_HDR_FLAG_CHAINED flag set
*/
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
req - > next_status = NT_STATUS_INVALID_PARAMETER ;
return NT_STATUS_OK ;
}
} else if ( idx = = 4 ) {
/*
* the 2 nd request triggers related vs . unrelated
* compounded requests
*/
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
compound_related = true ;
}
} else if ( idx > 4 ) {
2009-06-09 22:02:48 +04:00
#if 0
/*
* It seems the this tests are wrong
* see the SMB2 - COMPOUND test
*/
2009-06-08 15:30:32 +04:00
/*
* all other requests should match the 2 nd one
*/
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
if ( ! compound_related ) {
req - > next_status =
NT_STATUS_INVALID_PARAMETER ;
return NT_STATUS_OK ;
}
} else {
if ( compound_related ) {
req - > next_status =
NT_STATUS_INVALID_PARAMETER ;
return NT_STATUS_OK ;
}
}
2009-06-09 22:02:48 +04:00
# endif
2009-06-08 15:30:32 +04:00
}
}
return NT_STATUS_OK ;
}
2010-12-11 02:46:41 +03:00
static void smb2_set_operation_credit ( struct smbd_server_connection * sconn ,
const struct iovec * in_vector ,
struct iovec * out_vector )
{
2011-01-09 21:54:33 +03:00
uint8_t * outhdr = ( uint8_t * ) out_vector - > iov_base ;
2010-12-11 02:46:41 +03:00
uint16_t credits_requested = 0 ;
uint16_t credits_granted = 0 ;
if ( in_vector ! = NULL ) {
const uint8_t * inhdr = ( const uint8_t * ) in_vector - > iov_base ;
credits_requested = SVAL ( inhdr , SMB2_HDR_CREDIT ) ;
}
SMB_ASSERT ( sconn - > smb2 . max_credits > = sconn - > smb2 . credits_granted ) ;
2011-07-29 07:23:30 +04:00
if ( credits_requested ) {
uint16_t modified_credits_requested ;
uint32_t multiplier ;
/*
* Split up max_credits into 1 / 16 ths , and then scale
* the requested credits by how many 16 ths have been
* currently granted . Less than 1 / 16 th = = grant all
* requested ( 100 % ) , scale down as more have been
* granted . Never ask for less than 1 as the client
* asked for at least 1. JRA .
*/
multiplier = 16 - ( ( ( sconn - > smb2 . credits_granted * 16 ) / sconn - > smb2 . max_credits ) % 16 ) ;
modified_credits_requested = ( multiplier * credits_requested ) / 16 ;
if ( modified_credits_requested = = 0 ) {
modified_credits_requested = 1 ;
}
/* Remember what we gave out. */
credits_granted = MIN ( modified_credits_requested ,
( sconn - > smb2 . max_credits - sconn - > smb2 . credits_granted ) ) ;
}
2010-12-11 02:46:41 +03:00
if ( credits_granted = = 0 & & sconn - > smb2 . credits_granted = = 0 ) {
2010-12-14 00:34:50 +03:00
/* First negprot packet, or ensure the client credits can
never drop to zero . */
2010-12-11 02:46:41 +03:00
credits_granted = 1 ;
}
SSVAL ( outhdr , SMB2_HDR_CREDIT , credits_granted ) ;
sconn - > smb2 . credits_granted + = credits_granted ;
DEBUG ( 10 , ( " smb2_set_operation_credit: requested %u, "
" granted %u, total granted %u \n " ,
( unsigned int ) credits_requested ,
( unsigned int ) credits_granted ,
( unsigned int ) sconn - > smb2 . credits_granted ) ) ;
}
static void smb2_calculate_credits ( const struct smbd_smb2_request * inreq ,
struct smbd_smb2_request * outreq )
{
int count , idx ;
count = outreq - > out . vector_count ;
for ( idx = 1 ; idx < count ; idx + = 3 ) {
smb2_set_operation_credit ( outreq - > sconn ,
& inreq - > in . vector [ idx ] ,
& outreq - > out . vector [ idx ] ) ;
}
}
2010-12-14 00:17:49 +03:00
static NTSTATUS smbd_smb2_request_setup_out ( struct smbd_smb2_request * req )
2009-05-14 16:17:28 +04:00
{
struct iovec * vector ;
int count ;
int idx ;
count = req - > in . vector_count ;
2010-04-20 00:43:42 +04:00
vector = talloc_zero_array ( req , struct iovec , count ) ;
2009-05-14 16:17:28 +04:00
if ( vector = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
vector [ 0 ] . iov_base = req - > out . nbt_hdr ;
vector [ 0 ] . iov_len = 4 ;
SIVAL ( req - > out . nbt_hdr , 0 , 0 ) ;
for ( idx = 1 ; idx < count ; idx + = 3 ) {
const uint8_t * inhdr = NULL ;
2009-06-09 21:46:29 +04:00
uint32_t in_flags ;
2009-05-14 16:17:28 +04:00
uint8_t * outhdr = NULL ;
uint8_t * outbody = NULL ;
uint32_t next_command_ofs = 0 ;
struct iovec * current = & vector [ idx ] ;
if ( ( idx + 3 ) < count ) {
2010-04-20 01:32:08 +04:00
/* we have a next command -
* setup for the error case . */
next_command_ofs = SMB2_HDR_BODY + 9 ;
2009-05-14 16:17:28 +04:00
}
inhdr = ( const uint8_t * ) req - > in . vector [ idx ] . iov_base ;
2009-06-09 21:46:29 +04:00
in_flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
2009-05-14 16:17:28 +04:00
2010-04-20 00:43:42 +04:00
outhdr = talloc_zero_array ( vector , uint8_t ,
OUTVEC_ALLOC_SIZE ) ;
2009-05-14 16:17:28 +04:00
if ( outhdr = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
outbody = outhdr + SMB2_HDR_BODY ;
current [ 0 ] . iov_base = ( void * ) outhdr ;
current [ 0 ] . iov_len = SMB2_HDR_BODY ;
current [ 1 ] . iov_base = ( void * ) outbody ;
current [ 1 ] . iov_len = 8 ;
2009-06-09 19:06:40 +04:00
current [ 2 ] . iov_base = NULL ;
current [ 2 ] . iov_len = 0 ;
2009-05-14 16:17:28 +04:00
/* setup the SMB2 header */
SIVAL ( outhdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SSVAL ( outhdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( outhdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( outhdr , SMB2_HDR_STATUS ,
NT_STATUS_V ( NT_STATUS_INTERNAL_ERROR ) ) ;
SSVAL ( outhdr , SMB2_HDR_OPCODE ,
SVAL ( inhdr , SMB2_HDR_OPCODE ) ) ;
2009-06-09 21:46:29 +04:00
SIVAL ( outhdr , SMB2_HDR_FLAGS ,
IVAL ( inhdr , SMB2_HDR_FLAGS ) | SMB2_HDR_FLAG_REDIRECT ) ;
2009-05-14 16:17:28 +04:00
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , next_command_ofs ) ;
2009-05-20 21:32:55 +04:00
SBVAL ( outhdr , SMB2_HDR_MESSAGE_ID ,
BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ) ;
2009-05-14 16:17:28 +04:00
SIVAL ( outhdr , SMB2_HDR_PID ,
IVAL ( inhdr , SMB2_HDR_PID ) ) ;
SIVAL ( outhdr , SMB2_HDR_TID ,
IVAL ( inhdr , SMB2_HDR_TID ) ) ;
2009-05-20 21:32:55 +04:00
SBVAL ( outhdr , SMB2_HDR_SESSION_ID ,
BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ) ;
2009-05-14 16:17:28 +04:00
memset ( outhdr + SMB2_HDR_SIGNATURE , 0 , 16 ) ;
/* setup error body header */
2009-06-09 19:06:40 +04:00
SSVAL ( outbody , 0x00 , 0x08 + 1 ) ;
2009-05-14 16:17:28 +04:00
SSVAL ( outbody , 0x02 , 0 ) ;
SIVAL ( outbody , 0x04 , 0 ) ;
}
req - > out . vector = vector ;
req - > out . vector_count = count ;
/* setup the length of the NBT packet */
smb2_setup_nbt_length ( req - > out . vector , req - > out . vector_count ) ;
2009-08-07 17:21:07 +04:00
DLIST_ADD_END ( req - > sconn - > smb2 . requests , req , struct smbd_smb2_request * ) ;
2009-06-09 23:29:40 +04:00
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
2009-06-02 18:07:08 +04:00
void smbd_server_connection_terminate_ex ( struct smbd_server_connection * sconn ,
const char * reason ,
const char * location )
2009-05-14 16:17:28 +04:00
{
2009-06-02 18:07:08 +04:00
DEBUG ( 10 , ( " smbd_server_connection_terminate_ex: reason[%s] at %s \n " ,
reason , location ) ) ;
2009-05-14 16:17:28 +04:00
exit_server_cleanly ( reason ) ;
}
2010-06-09 04:44:05 +04:00
static bool dup_smb2_vec3 ( TALLOC_CTX * ctx ,
struct iovec * outvec ,
const struct iovec * srcvec )
2010-04-18 08:20:17 +04:00
{
2010-06-09 04:44:05 +04:00
/* vec[0] is always boilerplate and must
* be allocated with size OUTVEC_ALLOC_SIZE . */
2009-06-10 00:33:32 +04:00
2010-06-09 04:44:05 +04:00
outvec [ 0 ] . iov_base = talloc_memdup ( ctx ,
srcvec [ 0 ] . iov_base ,
OUTVEC_ALLOC_SIZE ) ;
if ( ! outvec [ 0 ] . iov_base ) {
return false ;
}
outvec [ 0 ] . iov_len = SMB2_HDR_BODY ;
/*
* If this is a " standard " vec [ 1 ] of length 8 ,
* pointing to srcvec [ 0 ] . iov_base + SMB2_HDR_BODY ,
* then duplicate this . Else use talloc_memdup ( ) .
*/
if ( srcvec [ 1 ] . iov_len = = 8 & &
srcvec [ 1 ] . iov_base = =
( ( uint8_t * ) srcvec [ 0 ] . iov_base ) +
SMB2_HDR_BODY ) {
2011-05-05 14:17:41 +04:00
outvec [ 1 ] . iov_base = ( ( uint8_t * ) outvec [ 0 ] . iov_base ) +
2010-06-09 04:44:05 +04:00
SMB2_HDR_BODY ;
outvec [ 1 ] . iov_len = 8 ;
} else {
outvec [ 1 ] . iov_base = talloc_memdup ( ctx ,
srcvec [ 1 ] . iov_base ,
srcvec [ 1 ] . iov_len ) ;
if ( ! outvec [ 1 ] . iov_base ) {
2010-04-18 08:20:17 +04:00
return false ;
}
2010-06-09 04:44:05 +04:00
outvec [ 1 ] . iov_len = srcvec [ 1 ] . iov_len ;
}
/*
* If this is a " standard " vec [ 2 ] of length 1 ,
* pointing to srcvec [ 0 ] . iov_base + ( OUTVEC_ALLOC_SIZE - 1 )
* then duplicate this . Else use talloc_memdup ( ) .
*/
if ( srcvec [ 2 ] . iov_base & &
srcvec [ 2 ] . iov_len ) {
if ( srcvec [ 2 ] . iov_base = =
( ( uint8_t * ) srcvec [ 0 ] . iov_base ) +
( OUTVEC_ALLOC_SIZE - 1 ) & &
srcvec [ 2 ] . iov_len = = 1 ) {
/* Common SMB2 error packet case. */
outvec [ 2 ] . iov_base = ( ( uint8_t * ) outvec [ 0 ] . iov_base ) +
( OUTVEC_ALLOC_SIZE - 1 ) ;
} else {
outvec [ 2 ] . iov_base = talloc_memdup ( ctx ,
srcvec [ 2 ] . iov_base ,
srcvec [ 2 ] . iov_len ) ;
if ( ! outvec [ 2 ] . iov_base ) {
return false ;
}
}
outvec [ 2 ] . iov_len = srcvec [ 2 ] . iov_len ;
2010-04-18 08:20:17 +04:00
} else {
2010-06-09 04:44:05 +04:00
outvec [ 2 ] . iov_base = NULL ;
outvec [ 2 ] . iov_len = 0 ;
2010-04-18 08:20:17 +04:00
}
return true ;
}
2010-04-18 09:42:23 +04:00
static struct smbd_smb2_request * dup_smb2_req ( const struct smbd_smb2_request * req )
2010-04-18 08:20:17 +04:00
{
struct smbd_smb2_request * newreq = NULL ;
struct iovec * outvec = NULL ;
int count = req - > out . vector_count ;
int i ;
newreq = smbd_smb2_request_allocate ( req - > sconn ) ;
if ( ! newreq ) {
return NULL ;
}
newreq - > sconn = req - > sconn ;
2011-06-29 04:45:49 +04:00
newreq - > session = req - > session ;
2010-04-18 08:20:17 +04:00
newreq - > do_signing = req - > do_signing ;
newreq - > current_idx = req - > current_idx ;
newreq - > async = false ;
newreq - > cancelled = false ;
2011-06-29 04:45:49 +04:00
/* Note we are leaving:
- > tcon
- > smb1req
- > compat_chain_fsp
uninitialized as NULL here as
they ' re not used in the interim
response code . JRA . */
2010-04-18 08:20:17 +04:00
2010-04-20 00:43:42 +04:00
outvec = talloc_zero_array ( newreq , struct iovec , count ) ;
2010-04-18 08:20:17 +04:00
if ( ! outvec ) {
TALLOC_FREE ( newreq ) ;
return NULL ;
}
newreq - > out . vector = outvec ;
newreq - > out . vector_count = count ;
/* Setup the outvec's identically to req. */
outvec [ 0 ] . iov_base = newreq - > out . nbt_hdr ;
outvec [ 0 ] . iov_len = 4 ;
memcpy ( newreq - > out . nbt_hdr , req - > out . nbt_hdr , 4 ) ;
2010-04-20 00:43:42 +04:00
2010-06-09 04:44:05 +04:00
/* Setup the vectors identically to the ones in req. */
2010-04-20 00:43:42 +04:00
for ( i = 1 ; i < count ; i + = 3 ) {
2010-06-09 04:44:05 +04:00
if ( ! dup_smb2_vec3 ( outvec , & outvec [ i ] , & req - > out . vector [ i ] ) ) {
2010-04-20 00:43:42 +04:00
break ;
2010-04-18 08:20:17 +04:00
}
}
2010-04-20 00:43:42 +04:00
if ( i < count ) {
/* Alloc failed. */
TALLOC_FREE ( newreq ) ;
return NULL ;
}
2010-04-18 08:20:17 +04:00
smb2_setup_nbt_length ( newreq - > out . vector ,
newreq - > out . vector_count ) ;
return newreq ;
}
static void smbd_smb2_request_writev_done ( struct tevent_req * subreq ) ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
static NTSTATUS smb2_send_async_interim_response ( const struct smbd_smb2_request * req )
{
int i = 0 ;
uint8_t * outhdr = NULL ;
struct smbd_smb2_request * nreq = NULL ;
/* Create a new smb2 request we'll use
for the interim return . */
nreq = dup_smb2_req ( req ) ;
if ( ! nreq ) {
return NT_STATUS_NO_MEMORY ;
}
/* Lose the last 3 out vectors. They're the
ones we ' ll be using for the async reply . */
nreq - > out . vector_count - = 3 ;
smb2_setup_nbt_length ( nreq - > out . vector ,
nreq - > out . vector_count ) ;
/* Step back to the previous reply. */
i = nreq - > current_idx - 3 ;
2010-04-19 13:53:11 +04:00
outhdr = ( uint8_t * ) nreq - > out . vector [ i ] . iov_base ;
2010-04-18 09:42:23 +04:00
/* And end the chain. */
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
2010-12-11 02:46:41 +03:00
/* Calculate outgoing credits */
smb2_calculate_credits ( req , nreq ) ;
2010-04-18 09:42:23 +04:00
/* Re-sign if needed. */
if ( nreq - > do_signing ) {
NTSTATUS status ;
status = smb2_signing_sign_pdu ( nreq - > session - > session_key ,
& nreq - > out . vector [ i ] , 3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
if ( DEBUGLEVEL > = 10 ) {
dbgtext ( " smb2_send_async_interim_response: nreq->current_idx = %u \n " ,
( unsigned int ) nreq - > current_idx ) ;
dbgtext ( " smb2_send_async_interim_response: returning %u vectors \n " ,
( unsigned int ) nreq - > out . vector_count ) ;
print_req_vectors ( nreq ) ;
}
nreq - > subreq = tstream_writev_queue_send ( nreq ,
nreq - > sconn - > smb2 . event_ctx ,
nreq - > sconn - > smb2 . stream ,
nreq - > sconn - > smb2 . send_queue ,
nreq - > out . vector ,
nreq - > out . vector_count ) ;
if ( nreq - > subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( nreq - > subreq ,
smbd_smb2_request_writev_done ,
nreq ) ;
return NT_STATUS_OK ;
}
struct smbd_smb2_request_pending_state {
struct smbd_server_connection * sconn ;
uint8_t buf [ 4 + SMB2_HDR_BODY + 0x08 + 1 ] ;
struct iovec vector [ 3 ] ;
} ;
static void smbd_smb2_request_pending_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_request_pending_state * state =
tevent_req_callback_data ( subreq ,
struct smbd_smb2_request_pending_state ) ;
struct smbd_server_connection * sconn = state - > sconn ;
int ret ;
int sys_errno ;
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( sys_errno ) ;
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
TALLOC_FREE ( state ) ;
}
2009-08-15 12:01:38 +04:00
NTSTATUS smbd_smb2_request_pending_queue ( struct smbd_smb2_request * req ,
struct tevent_req * subreq )
2009-06-10 00:33:32 +04:00
{
2010-04-18 09:42:23 +04:00
NTSTATUS status ;
struct smbd_smb2_request_pending_state * state = NULL ;
2009-06-10 00:33:32 +04:00
int i = req - > current_idx ;
2010-04-18 09:42:23 +04:00
uint8_t * reqhdr = NULL ;
uint8_t * hdr = NULL ;
uint8_t * body = NULL ;
2010-04-18 08:20:17 +04:00
uint32_t flags = 0 ;
uint64_t message_id = 0 ;
uint64_t async_id = 0 ;
struct iovec * outvec = NULL ;
2009-06-10 00:33:32 +04:00
2009-08-15 12:01:38 +04:00
if ( ! tevent_req_is_in_progress ( subreq ) ) {
return NT_STATUS_OK ;
}
2010-06-11 00:19:25 +04:00
req - > subreq = subreq ;
subreq = NULL ;
2010-04-18 08:20:17 +04:00
if ( req - > async ) {
/* We're already async. */
return NT_STATUS_OK ;
}
if ( req - > in . vector_count > i + 3 ) {
/*
* We ' re trying to go async in a compound
* request chain . This is not allowed .
* Cancel the outstanding request .
*/
2010-06-11 00:19:25 +04:00
tevent_req_cancel ( req - > subreq ) ;
2010-04-18 08:20:17 +04:00
return smbd_smb2_request_error ( req ,
NT_STATUS_INSUFFICIENT_RESOURCES ) ;
}
if ( DEBUGLEVEL > = 10 ) {
dbgtext ( " smbd_smb2_request_pending_queue: req->current_idx = %u \n " ,
( unsigned int ) req - > current_idx ) ;
print_req_vectors ( req ) ;
}
2010-04-18 09:42:23 +04:00
if ( req - > out . vector_count > 4 ) {
/* This is a compound reply. We
* must do an interim response
* followed by the async response
* to match W2K8R2 .
*/
status = smb2_send_async_interim_response ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2010-04-18 08:20:17 +04:00
}
2010-04-28 03:07:12 +04:00
/* Don't return an intermediate packet on a pipe read/write. */
if ( req - > tcon & & req - > tcon - > compat_conn & & IS_IPC ( req - > tcon - > compat_conn ) ) {
return NT_STATUS_OK ;
}
2010-04-18 09:42:23 +04:00
reqhdr = ( uint8_t * ) req - > out . vector [ i ] . iov_base ;
2010-04-24 02:35:34 +04:00
flags = ( IVAL ( reqhdr , SMB2_HDR_FLAGS ) & ~ SMB2_HDR_FLAG_CHAINED ) ;
2010-04-18 09:42:23 +04:00
message_id = BVAL ( reqhdr , SMB2_HDR_MESSAGE_ID ) ;
2009-06-10 00:33:32 +04:00
async_id = message_id ; /* keep it simple for now... */
2010-04-18 09:42:23 +04:00
2010-04-19 07:23:54 +04:00
/*
* What we send is identical to a smbd_smb2_request_error
* packet with an error status of STATUS_PENDING . Make use
* of this fact sometime when refactoring . JRA .
*/
2010-04-18 09:42:23 +04:00
state = talloc_zero ( req - > sconn , struct smbd_smb2_request_pending_state ) ;
if ( state = = NULL ) {
2009-06-10 00:33:32 +04:00
return NT_STATUS_NO_MEMORY ;
}
2010-04-18 09:42:23 +04:00
state - > sconn = req - > sconn ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
state - > vector [ 0 ] . iov_base = ( void * ) state - > buf ;
state - > vector [ 0 ] . iov_len = 4 ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
state - > vector [ 1 ] . iov_base = state - > buf + 4 ;
state - > vector [ 1 ] . iov_len = SMB2_HDR_BODY ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
state - > vector [ 2 ] . iov_base = state - > buf + 4 + SMB2_HDR_BODY ;
state - > vector [ 2 ] . iov_len = 9 ;
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
smb2_setup_nbt_length ( state - > vector , 3 ) ;
2010-04-19 13:53:11 +04:00
hdr = ( uint8_t * ) state - > vector [ 1 ] . iov_base ;
body = ( uint8_t * ) state - > vector [ 2 ] . iov_base ;
2010-04-18 09:42:23 +04:00
SIVAL ( hdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SSVAL ( hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( hdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( hdr , SMB2_HDR_STATUS , NT_STATUS_V ( STATUS_PENDING ) ) ;
SSVAL ( hdr , SMB2_HDR_OPCODE , SVAL ( reqhdr , SMB2_HDR_OPCODE ) ) ;
2010-05-19 04:11:54 +04:00
2010-04-18 09:42:23 +04:00
SIVAL ( hdr , SMB2_HDR_FLAGS , flags | SMB2_HDR_FLAG_ASYNC ) ;
SIVAL ( hdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
SBVAL ( hdr , SMB2_HDR_MESSAGE_ID , message_id ) ;
SBVAL ( hdr , SMB2_HDR_PID , async_id ) ;
SBVAL ( hdr , SMB2_HDR_SESSION_ID ,
BVAL ( reqhdr , SMB2_HDR_SESSION_ID ) ) ;
memset ( hdr + SMB2_HDR_SIGNATURE , 0 , 16 ) ;
SSVAL ( body , 0x00 , 0x08 + 1 ) ;
SCVAL ( body , 0x02 , 0 ) ;
SCVAL ( body , 0x03 , 0 ) ;
SIVAL ( body , 0x04 , 0 ) ;
/* Match W2K8R2... */
SCVAL ( body , 0x08 , 0x21 ) ;
2010-12-14 02:22:47 +03:00
/* Ensure we correctly go through crediting. Grant
the credits now , and zero credits on the final
response . */
2010-12-11 02:46:41 +03:00
smb2_set_operation_credit ( req - > sconn ,
2010-12-14 02:22:47 +03:00
& req - > in . vector [ i ] ,
2010-12-11 02:46:41 +03:00
& state - > vector [ 1 ] ) ;
2010-04-18 09:42:23 +04:00
if ( req - > do_signing ) {
status = smb2_signing_sign_pdu ( req - > session - > session_key ,
2011-06-29 20:56:47 +04:00
& state - > vector [ 1 ] , 2 ) ;
2010-04-18 08:20:17 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2009-06-10 00:33:32 +04:00
2010-04-18 09:42:23 +04:00
subreq = tstream_writev_queue_send ( state ,
req - > sconn - > smb2 . event_ctx ,
req - > sconn - > smb2 . stream ,
req - > sconn - > smb2 . send_queue ,
state - > vector ,
3 ) ;
2010-04-18 08:20:17 +04:00
2010-04-18 09:42:23 +04:00
if ( subreq = = NULL ) {
2009-06-10 00:33:32 +04:00
return NT_STATUS_NO_MEMORY ;
}
2010-04-18 09:42:23 +04:00
tevent_req_set_callback ( subreq ,
smbd_smb2_request_pending_writev_done ,
state ) ;
2009-06-10 00:33:32 +04:00
2010-04-18 08:20:17 +04:00
/* Note we're going async with this request. */
req - > async = true ;
2009-06-10 00:33:32 +04:00
2010-04-18 08:20:17 +04:00
/*
* Now manipulate req so that the outstanding async request
* is the only one left in the struct smbd_smb2_request .
*/
if ( req - > current_idx = = 1 ) {
/* There was only one. */
goto out ;
2009-06-10 00:33:32 +04:00
}
2010-04-18 08:20:17 +04:00
/* Re-arrange the in.vectors. */
req - > in . vector [ 1 ] = req - > in . vector [ i ] ;
req - > in . vector [ 2 ] = req - > in . vector [ i + 1 ] ;
req - > in . vector [ 3 ] = req - > in . vector [ i + 2 ] ;
req - > in . vector_count = 4 ;
/* Reset the new in size. */
smb2_setup_nbt_length ( req - > in . vector , 4 ) ;
/* Now recreate the out.vectors. */
2010-04-20 00:43:42 +04:00
outvec = talloc_zero_array ( req , struct iovec , 4 ) ;
2010-04-18 08:20:17 +04:00
if ( ! outvec ) {
return NT_STATUS_NO_MEMORY ;
}
2010-06-09 04:44:05 +04:00
/* 0 is always boilerplate and must
* be of size 4 for the length field . */
2010-04-18 08:20:17 +04:00
outvec [ 0 ] . iov_base = req - > out . nbt_hdr ;
outvec [ 0 ] . iov_len = 4 ;
SIVAL ( req - > out . nbt_hdr , 0 , 0 ) ;
2010-06-09 04:44:05 +04:00
if ( ! dup_smb2_vec3 ( outvec , & outvec [ 1 ] , & req - > out . vector [ i ] ) ) {
2010-04-18 08:20:17 +04:00
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( req - > out . vector ) ;
req - > out . vector = outvec ;
req - > current_idx = 1 ;
req - > out . vector_count = 4 ;
out :
smb2_setup_nbt_length ( req - > out . vector ,
req - > out . vector_count ) ;
/* Ensure our final reply matches the interim one. */
2010-04-18 09:42:23 +04:00
reqhdr = ( uint8_t * ) req - > out . vector [ 1 ] . iov_base ;
SIVAL ( reqhdr , SMB2_HDR_FLAGS , flags | SMB2_HDR_FLAG_ASYNC ) ;
SBVAL ( reqhdr , SMB2_HDR_PID , async_id ) ;
2010-04-18 08:20:17 +04:00
{
const uint8_t * inhdr =
( const uint8_t * ) req - > in . vector [ 1 ] . iov_base ;
DEBUG ( 10 , ( " smbd_smb2_request_pending_queue: opcode[%s] mid %llu "
" going async \n " ,
smb2_opcode_name ( ( uint16_t ) IVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
( unsigned long long ) async_id ) ) ;
}
return NT_STATUS_OK ;
2009-06-10 00:33:32 +04:00
}
static NTSTATUS smbd_smb2_request_process_cancel ( struct smbd_smb2_request * req )
{
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-06-10 00:33:32 +04:00
struct smbd_smb2_request * cur ;
const uint8_t * inhdr ;
int i = req - > current_idx ;
uint32_t flags ;
uint64_t search_message_id ;
uint64_t search_async_id ;
2010-04-18 08:20:17 +04:00
uint64_t found_id ;
2009-06-10 00:33:32 +04:00
inhdr = ( const uint8_t * ) req - > in . vector [ i ] . iov_base ;
flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
search_message_id = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
search_async_id = BVAL ( inhdr , SMB2_HDR_PID ) ;
/*
* we don ' t need the request anymore
* cancel requests never have a response
*/
2010-06-03 03:57:08 +04:00
DLIST_REMOVE ( req - > sconn - > smb2 . requests , req ) ;
2009-06-10 00:33:32 +04:00
TALLOC_FREE ( req ) ;
for ( cur = sconn - > smb2 . requests ; cur ; cur = cur - > next ) {
const uint8_t * outhdr ;
uint64_t message_id ;
uint64_t async_id ;
i = cur - > current_idx ;
outhdr = ( const uint8_t * ) cur - > out . vector [ i ] . iov_base ;
message_id = BVAL ( outhdr , SMB2_HDR_MESSAGE_ID ) ;
async_id = BVAL ( outhdr , SMB2_HDR_PID ) ;
if ( flags & SMB2_HDR_FLAG_ASYNC ) {
if ( search_async_id = = async_id ) {
2010-04-18 08:20:17 +04:00
found_id = async_id ;
2009-06-10 00:33:32 +04:00
break ;
}
} else {
if ( search_message_id = = message_id ) {
2010-04-18 08:20:17 +04:00
found_id = message_id ;
2009-06-10 00:33:32 +04:00
break ;
}
}
}
2009-08-15 12:01:38 +04:00
if ( cur & & cur - > subreq ) {
2010-04-18 08:20:17 +04:00
inhdr = ( const uint8_t * ) cur - > in . vector [ i ] . iov_base ;
DEBUG ( 10 , ( " smbd_smb2_request_process_cancel: attempting to "
" cancel opcode[%s] mid %llu \n " ,
smb2_opcode_name ( ( uint16_t ) IVAL ( inhdr , SMB2_HDR_OPCODE ) ) ,
( unsigned long long ) found_id ) ) ;
2009-08-15 12:01:38 +04:00
tevent_req_cancel ( cur - > subreq ) ;
2009-06-10 00:33:32 +04:00
}
return NT_STATUS_OK ;
}
2011-07-08 21:44:29 +04:00
/*************************************************************
Ensure an incoming tid is a valid one for us to access .
Change to the associated uid credentials and chdir to the
valid tid directory .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS smbd_smb2_request_check_tcon ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
const uint8_t * outhdr ;
int i = req - > current_idx ;
uint32_t in_tid ;
void * p ;
struct smbd_smb2_tcon * tcon ;
bool chained_fixup = false ;
inhdr = ( const uint8_t * ) req - > in . vector [ i + 0 ] . iov_base ;
in_tid = IVAL ( inhdr , SMB2_HDR_TID ) ;
if ( in_tid = = ( 0xFFFFFFFF ) ) {
if ( req - > async ) {
/*
* async request - fill in tid from
* already setup out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
in_tid = IVAL ( outhdr , SMB2_HDR_TID ) ;
} else if ( i > 2 ) {
/*
* Chained request - fill in tid from
* the previous request out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i - 3 ] . iov_base ;
in_tid = IVAL ( outhdr , SMB2_HDR_TID ) ;
chained_fixup = true ;
}
}
/* lookup an existing session */
p = idr_find ( req - > session - > tcons . idtree , in_tid ) ;
if ( p = = NULL ) {
return NT_STATUS_NETWORK_NAME_DELETED ;
}
tcon = talloc_get_type_abort ( p , struct smbd_smb2_tcon ) ;
if ( ! change_to_user ( tcon - > compat_conn , req - > session - > vuid ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
/* should we pass FLAG_CASELESS_PATHNAMES here? */
if ( ! set_current_service ( tcon - > compat_conn , 0 , true ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
req - > tcon = tcon ;
if ( chained_fixup ) {
/* Fix up our own outhdr. */
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
SIVAL ( discard_const_p ( uint8_t , outhdr ) , SMB2_HDR_TID , in_tid ) ;
}
return NT_STATUS_OK ;
}
/*************************************************************
Ensure an incoming session_id is a valid one for us to access .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS smbd_smb2_request_check_session ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
const uint8_t * outhdr ;
int i = req - > current_idx ;
uint64_t in_session_id ;
void * p ;
struct smbd_smb2_session * session ;
bool chained_fixup = false ;
inhdr = ( const uint8_t * ) req - > in . vector [ i + 0 ] . iov_base ;
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
if ( in_session_id = = ( 0xFFFFFFFFFFFFFFFFLL ) ) {
if ( req - > async ) {
/*
* async request - fill in session_id from
* already setup request out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
in_session_id = BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ;
} else if ( i > 2 ) {
/*
* Chained request - fill in session_id from
* the previous request out . vector [ ] . iov_base .
*/
outhdr = ( const uint8_t * ) req - > out . vector [ i - 3 ] . iov_base ;
in_session_id = BVAL ( outhdr , SMB2_HDR_SESSION_ID ) ;
chained_fixup = true ;
}
}
/* lookup an existing session */
p = idr_find ( req - > sconn - > smb2 . sessions . idtree , in_session_id ) ;
if ( p = = NULL ) {
return NT_STATUS_USER_SESSION_DELETED ;
}
session = talloc_get_type_abort ( p , struct smbd_smb2_session ) ;
if ( ! NT_STATUS_IS_OK ( session - > status ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
2011-07-15 09:55:31 +04:00
set_current_user_info ( session - > session_info - > unix_info - > sanitized_username ,
session - > session_info - > unix_info - > unix_name ,
2011-07-18 06:58:25 +04:00
session - > session_info - > info - > domain_name ) ;
2011-07-08 21:44:29 +04:00
req - > session = session ;
if ( chained_fixup ) {
/* Fix up our own outhdr. */
outhdr = ( const uint8_t * ) req - > out . vector [ i ] . iov_base ;
SBVAL ( discard_const_p ( uint8_t , outhdr ) , SMB2_HDR_SESSION_ID , in_session_id ) ;
}
return NT_STATUS_OK ;
}
2010-04-23 10:52:19 +04:00
NTSTATUS smbd_smb2_request_dispatch ( struct smbd_smb2_request * req )
2009-05-14 16:17:28 +04:00
{
const uint8_t * inhdr ;
int i = req - > current_idx ;
uint16_t opcode ;
2009-05-23 00:58:39 +04:00
uint32_t flags ;
2010-04-18 08:20:17 +04:00
uint64_t mid ;
2009-05-20 22:51:10 +04:00
NTSTATUS status ;
2009-05-23 00:58:39 +04:00
NTSTATUS session_status ;
2009-08-14 13:24:30 +04:00
uint32_t allowed_flags ;
2010-07-09 00:30:12 +04:00
NTSTATUS return_value ;
2009-05-14 16:17:28 +04:00
inhdr = ( const uint8_t * ) req - > in . vector [ i ] . iov_base ;
/* TODO: verify more things */
2009-05-23 00:58:39 +04:00
flags = IVAL ( inhdr , SMB2_HDR_FLAGS ) ;
2009-05-14 16:17:28 +04:00
opcode = IVAL ( inhdr , SMB2_HDR_OPCODE ) ;
2010-04-18 08:20:17 +04:00
mid = BVAL ( inhdr , SMB2_HDR_MESSAGE_ID ) ;
DEBUG ( 10 , ( " smbd_smb2_request_dispatch: opcode[%s] mid = %llu \n " ,
smb2_opcode_name ( opcode ) ,
( unsigned long long ) mid ) ) ;
2009-05-23 00:58:39 +04:00
2009-08-14 13:24:30 +04:00
allowed_flags = SMB2_HDR_FLAG_CHAINED |
SMB2_HDR_FLAG_SIGNED |
SMB2_HDR_FLAG_DFS ;
if ( opcode = = SMB2_OP_CANCEL ) {
allowed_flags | = SMB2_HDR_FLAG_ASYNC ;
}
if ( ( flags & ~ allowed_flags ) ! = 0 ) {
2009-05-23 00:58:39 +04:00
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
2011-07-08 11:08:39 +04:00
/*
* Check if the client provided a valid session id ,
* if so smbd_smb2_request_check_session ( ) calls
* set_current_user_info ( ) .
*
* As some command don ' t require a valid session id
* we defer the check of the session_status
*/
2009-05-23 00:58:39 +04:00
session_status = smbd_smb2_request_check_session ( req ) ;
req - > do_signing = false ;
if ( flags & SMB2_HDR_FLAG_SIGNED ) {
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
return smbd_smb2_request_error ( req , session_status ) ;
}
req - > do_signing = true ;
status = smb2_signing_check_pdu ( req - > session - > session_key ,
& req - > in . vector [ i ] , 3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
} else if ( req - > session & & req - > session - > do_signing ) {
return smbd_smb2_request_error ( req , NT_STATUS_ACCESS_DENIED ) ;
}
2009-06-09 21:48:08 +04:00
if ( flags & SMB2_HDR_FLAG_CHAINED ) {
/*
* This check is mostly for giving the correct error code
* for compounded requests .
*
* TODO : we may need to move this after the session
* and tcon checks .
*/
if ( ! NT_STATUS_IS_OK ( req - > next_status ) ) {
return smbd_smb2_request_error ( req , req - > next_status ) ;
}
} else {
2009-06-09 21:31:15 +04:00
req - > compat_chain_fsp = NULL ;
}
2009-05-14 16:17:28 +04:00
switch ( opcode ) {
case SMB2_OP_NEGPROT :
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_negprot ) ;
return_value = smbd_smb2_request_process_negprot ( req ) ;
END_PROFILE ( smb2_negprot ) ;
}
break ;
2009-05-14 17:32:02 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_SESSSETUP :
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_sesssetup ) ;
return_value = smbd_smb2_request_process_sesssetup ( req ) ;
END_PROFILE ( smb2_sesssetup ) ;
}
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_LOGOFF :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
}
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_logoff ) ;
return_value = smbd_smb2_request_process_logoff ( req ) ;
END_PROFILE ( smb2_logoff ) ;
2009-05-20 22:51:10 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_TCON :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-23 00:58:39 +04:00
}
2010-07-09 00:30:12 +04:00
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as root .
*
* smbd_smb2_request_process_tcon ( )
* calls make_connection_snum ( ) , which will call
* change_to_user ( ) , when needed .
*/
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_tcon ) ;
return_value = smbd_smb2_request_process_tcon ( req ) ;
END_PROFILE ( smb2_tcon ) ;
2009-05-20 22:51:10 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_TDIS :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_tdis ) ;
return_value = smbd_smb2_request_process_tdis ( req ) ;
END_PROFILE ( smb2_tdis ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CREATE :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_create ) ;
return_value = smbd_smb2_request_process_create ( req ) ;
END_PROFILE ( smb2_create ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CLOSE :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_close ) ;
return_value = smbd_smb2_request_process_close ( req ) ;
END_PROFILE ( smb2_close ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_FLUSH :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_flush ) ;
return_value = smbd_smb2_request_process_flush ( req ) ;
END_PROFILE ( smb2_flush ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_READ :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_read ) ;
return_value = smbd_smb2_request_process_read ( req ) ;
END_PROFILE ( smb2_read ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_WRITE :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_write ) ;
return_value = smbd_smb2_request_process_write ( req ) ;
END_PROFILE ( smb2_write ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_LOCK :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-05-07 12:20:26 +04:00
/* Too ugly to live ? JRA. */
if ( NT_STATUS_EQUAL ( session_status , NT_STATUS_USER_SESSION_DELETED ) ) {
session_status = NT_STATUS_FILE_CLOSED ;
}
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-05-07 12:20:26 +04:00
/* Too ugly to live ? JRA. */
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_NAME_DELETED ) ) {
status = NT_STATUS_FILE_CLOSED ;
}
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_lock ) ;
return_value = smbd_smb2_request_process_lock ( req ) ;
END_PROFILE ( smb2_lock ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_IOCTL :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_ioctl ) ;
return_value = smbd_smb2_request_process_ioctl ( req ) ;
END_PROFILE ( smb2_ioctl ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_CANCEL :
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as root
*
* That is what we also do in the SMB1 case .
*/
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
change_to_root_user ( ) ;
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_cancel ) ;
return_value = smbd_smb2_request_process_cancel ( req ) ;
END_PROFILE ( smb2_cancel ) ;
}
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_KEEPALIVE :
s3:smb2_server: call change_to_root_user() or smbd_smb2_request_check_tcon()
For all requests which don't operate on a tcon, we should call
change_to_root_user(), to match the SMB1 behavior.
For SMB1 we do the following operations without AS_USER:
/* 0x70 */ { "SMBtcon",reply_tcon,0},
/* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR},
/* 0x72 */ { "SMBnegprot",reply_negprot,0},
/* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0},
/* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */
/* 0x75 */ { "SMBtconX",reply_tcon_and_X,0},
...
/* 0x2b */ { "SMBecho",reply_echo,0},
...
/* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 },
For SMB2tdis we still call smbd_smb2_request_check_tcon()
as close_cnum() calls change_to_root_user() when needed.
metze
Signed-off-by: Jeremy Allison <jra@samba.org>
2011-07-07 18:38:33 +04:00
/* This call needs to be run as root */
change_to_root_user ( ) ;
{
START_PROFILE ( smb2_keepalive ) ;
return_value = smbd_smb2_request_process_keepalive ( req ) ;
END_PROFILE ( smb2_keepalive ) ;
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_FIND :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_find ) ;
return_value = smbd_smb2_request_process_find ( req ) ;
END_PROFILE ( smb2_find ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_NOTIFY :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_notify ) ;
return_value = smbd_smb2_request_process_notify ( req ) ;
END_PROFILE ( smb2_notify ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_GETINFO :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_getinfo ) ;
return_value = smbd_smb2_request_process_getinfo ( req ) ;
END_PROFILE ( smb2_getinfo ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_SETINFO :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
}
{
START_PROFILE ( smb2_setinfo ) ;
return_value = smbd_smb2_request_process_setinfo ( req ) ;
END_PROFILE ( smb2_setinfo ) ;
2009-05-15 13:50:20 +04:00
}
2010-07-09 00:30:12 +04:00
break ;
2009-05-15 14:07:28 +04:00
2009-05-14 16:17:28 +04:00
case SMB2_OP_BREAK :
2009-05-23 00:58:39 +04:00
if ( ! NT_STATUS_IS_OK ( session_status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , session_status ) ;
break ;
2009-05-20 22:51:10 +04:00
}
2011-07-08 11:08:39 +04:00
/*
* This call needs to be run as user .
*
* smbd_smb2_request_check_tcon ( )
* calls change_to_user ( ) on success .
*/
2009-05-15 13:50:20 +04:00
status = smbd_smb2_request_check_tcon ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-07-09 00:30:12 +04:00
return_value = smbd_smb2_request_error ( req , status ) ;
break ;
2009-05-15 13:50:20 +04:00
}
2009-05-14 16:17:28 +04:00
2010-07-09 00:30:12 +04:00
{
START_PROFILE ( smb2_break ) ;
return_value = smbd_smb2_request_process_break ( req ) ;
END_PROFILE ( smb2_break ) ;
}
break ;
default :
return_value = smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
break ;
}
return return_value ;
2009-05-14 16:17:28 +04:00
}
static NTSTATUS smbd_smb2_request_reply ( struct smbd_smb2_request * req )
{
struct tevent_req * subreq ;
2010-12-11 02:46:41 +03:00
int i = req - > current_idx ;
2009-05-14 16:17:28 +04:00
2009-08-15 12:01:38 +04:00
req - > subreq = NULL ;
2009-05-14 16:17:28 +04:00
req - > current_idx + = 3 ;
2009-06-05 21:49:40 +04:00
if ( req - > current_idx < req - > out . vector_count ) {
2010-04-18 08:20:17 +04:00
/*
* We must process the remaining compound
* SMB2 requests before any new incoming SMB2
* requests . This is because incoming SMB2
* requests may include a cancel for a
* compound request we haven ' t processed
* yet .
*/
struct tevent_immediate * im = tevent_create_immediate ( req ) ;
if ( ! im ) {
2009-05-14 16:17:28 +04:00
return NT_STATUS_NO_MEMORY ;
}
2010-04-18 08:20:17 +04:00
tevent_schedule_immediate ( im ,
req - > sconn - > smb2 . event_ctx ,
2010-04-23 10:52:19 +04:00
smbd_smb2_request_dispatch_immediate ,
2009-05-14 16:17:28 +04:00
req ) ;
return NT_STATUS_OK ;
}
2010-12-20 21:44:48 +03:00
smb2_setup_nbt_length ( req - > out . vector , req - > out . vector_count ) ;
/* Set credit for this operation (zero credits if this
is a final reply for an async operation ) . */
smb2_set_operation_credit ( req - > sconn ,
req - > async ? NULL : & req - > in . vector [ i ] ,
& req - > out . vector [ i ] ) ;
if ( req - > do_signing ) {
NTSTATUS status ;
status = smb2_signing_sign_pdu ( req - > session - > session_key ,
& req - > out . vector [ i ] , 3 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
2010-04-18 08:20:17 +04:00
if ( DEBUGLEVEL > = 10 ) {
dbgtext ( " smbd_smb2_request_reply: sending... \n " ) ;
print_req_vectors ( req ) ;
}
2010-12-15 00:36:08 +03:00
/* I am a sick, sick man... :-). Sendfile hack ... JRA. */
if ( req - > out . vector_count = = 4 & &
req - > out . vector [ 3 ] . iov_base = = NULL & &
req - > out . vector [ 3 ] . iov_len ! = 0 ) {
/* Dynamic part is NULL. Chop it off,
We ' re going to send it via sendfile . */
req - > out . vector_count - = 1 ;
}
2009-05-14 16:17:28 +04:00
subreq = tstream_writev_queue_send ( req ,
2009-08-07 17:21:07 +04:00
req - > sconn - > smb2 . event_ctx ,
req - > sconn - > smb2 . stream ,
req - > sconn - > smb2 . send_queue ,
2009-05-14 16:17:28 +04:00
req - > out . vector ,
req - > out . vector_count ) ;
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_writev_done , req ) ;
2010-06-03 03:43:31 +04:00
/*
* We ' re done with this request -
* move it off the " being processed " queue .
*/
DLIST_REMOVE ( req - > sconn - > smb2 . requests , req ) ;
2009-05-14 16:17:28 +04:00
return NT_STATUS_OK ;
}
2010-04-23 10:52:19 +04:00
void smbd_smb2_request_dispatch_immediate ( struct tevent_context * ctx ,
2010-04-18 08:20:17 +04:00
struct tevent_immediate * im ,
void * private_data )
2009-05-20 21:48:47 +04:00
{
2010-04-18 08:20:17 +04:00
struct smbd_smb2_request * req = talloc_get_type_abort ( private_data ,
2009-05-20 21:48:47 +04:00
struct smbd_smb2_request ) ;
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-05-20 21:48:47 +04:00
NTSTATUS status ;
2010-04-18 08:20:17 +04:00
TALLOC_FREE ( im ) ;
2009-05-20 21:48:47 +04:00
2010-04-18 08:20:17 +04:00
if ( DEBUGLEVEL > = 10 ) {
2010-04-23 10:52:19 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_dispatch_immediate: idx[%d] of %d vectors \n " ,
2010-04-18 08:20:17 +04:00
req - > current_idx , req - > in . vector_count ) ) ;
print_req_vectors ( req ) ;
}
2009-05-20 21:48:47 +04:00
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-20 21:48:47 +04:00
return ;
}
}
static void smbd_smb2_request_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_request * req = tevent_req_callback_data ( subreq ,
struct smbd_smb2_request ) ;
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = req - > sconn ;
2009-05-20 21:48:47 +04:00
int ret ;
int sys_errno ;
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
2009-06-09 23:29:40 +04:00
TALLOC_FREE ( req ) ;
2009-05-20 21:48:47 +04:00
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( sys_errno ) ;
2010-04-18 08:20:17 +04:00
DEBUG ( 2 , ( " smbd_smb2_request_writev_done: client write error %s \n " ,
nt_errstr ( status ) ) ) ;
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-20 21:48:47 +04:00
return ;
}
}
2009-05-20 21:35:39 +04:00
NTSTATUS smbd_smb2_request_done_ex ( struct smbd_smb2_request * req ,
NTSTATUS status ,
2009-06-05 13:05:03 +04:00
DATA_BLOB body , DATA_BLOB * dyn ,
const char * location )
2009-05-14 16:17:28 +04:00
{
uint8_t * outhdr ;
int i = req - > current_idx ;
uint32_t next_command_ofs ;
2009-05-20 21:35:39 +04:00
DEBUG ( 10 , ( " smbd_smb2_request_done_ex: "
2009-06-05 13:05:03 +04:00
" idx[%d] status[%s] body[%u] dyn[%s:%u] at %s \n " ,
2009-05-20 21:35:39 +04:00
i , nt_errstr ( status ) , ( unsigned int ) body . length ,
2009-05-14 16:17:28 +04:00
dyn ? " yes " : " no " ,
2009-06-05 13:05:03 +04:00
( unsigned int ) ( dyn ? dyn - > length : 0 ) ,
location ) ) ;
2009-05-14 16:17:28 +04:00
if ( body . length < 2 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
if ( ( body . length % 2 ) ! = 0 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
outhdr = ( uint8_t * ) req - > out . vector [ i ] . iov_base ;
2009-05-21 18:17:53 +04:00
next_command_ofs = IVAL ( outhdr , SMB2_HDR_NEXT_COMMAND ) ;
2009-05-20 21:35:39 +04:00
SIVAL ( outhdr , SMB2_HDR_STATUS , NT_STATUS_V ( status ) ) ;
2009-05-14 16:17:28 +04:00
req - > out . vector [ i + 1 ] . iov_base = ( void * ) body . data ;
req - > out . vector [ i + 1 ] . iov_len = body . length ;
if ( dyn ) {
2009-06-09 19:06:40 +04:00
req - > out . vector [ i + 2 ] . iov_base = ( void * ) dyn - > data ;
req - > out . vector [ i + 2 ] . iov_len = dyn - > length ;
2009-05-14 16:17:28 +04:00
} else {
req - > out . vector [ i + 2 ] . iov_base = NULL ;
req - > out . vector [ i + 2 ] . iov_len = 0 ;
}
/* see if we need to recalculate the offset to the next response */
if ( next_command_ofs > 0 ) {
next_command_ofs = SMB2_HDR_BODY ;
next_command_ofs + = req - > out . vector [ i + 1 ] . iov_len ;
next_command_ofs + = req - > out . vector [ i + 2 ] . iov_len ;
}
if ( ( next_command_ofs % 8 ) ! = 0 ) {
2009-06-09 21:21:26 +04:00
size_t pad_size = 8 - ( next_command_ofs % 8 ) ;
if ( req - > out . vector [ i + 2 ] . iov_len = = 0 ) {
/*
* if the dyn buffer is empty
* we can use it to add padding
*/
uint8_t * pad ;
pad = talloc_zero_array ( req - > out . vector ,
uint8_t , pad_size ) ;
if ( pad = = NULL ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_NO_MEMORY ) ;
}
req - > out . vector [ i + 2 ] . iov_base = ( void * ) pad ;
req - > out . vector [ i + 2 ] . iov_len = pad_size ;
} else {
/*
* For now we copy the dynamic buffer
* and add the padding to the new buffer
*/
size_t old_size ;
uint8_t * old_dyn ;
size_t new_size ;
uint8_t * new_dyn ;
old_size = req - > out . vector [ i + 2 ] . iov_len ;
old_dyn = ( uint8_t * ) req - > out . vector [ i + 2 ] . iov_base ;
new_size = old_size + pad_size ;
2010-04-20 00:43:42 +04:00
new_dyn = talloc_zero_array ( req - > out . vector ,
2009-06-09 21:21:26 +04:00
uint8_t , new_size ) ;
if ( new_dyn = = NULL ) {
return smbd_smb2_request_error ( req ,
NT_STATUS_NO_MEMORY ) ;
}
memcpy ( new_dyn , old_dyn , old_size ) ;
memset ( new_dyn + old_size , 0 , pad_size ) ;
req - > out . vector [ i + 2 ] . iov_base = ( void * ) new_dyn ;
req - > out . vector [ i + 2 ] . iov_len = new_size ;
}
next_command_ofs + = pad_size ;
2009-05-14 16:17:28 +04:00
}
SIVAL ( outhdr , SMB2_HDR_NEXT_COMMAND , next_command_ofs ) ;
return smbd_smb2_request_reply ( req ) ;
}
2010-04-20 01:32:08 +04:00
NTSTATUS smbd_smb2_request_error_ex ( struct smbd_smb2_request * req ,
NTSTATUS status ,
DATA_BLOB * info ,
const char * location )
{
DATA_BLOB body ;
int i = req - > current_idx ;
uint8_t * outhdr = ( uint8_t * ) req - > out . vector [ i ] . iov_base ;
DEBUG ( 10 , ( " smbd_smb2_request_error_ex: idx[%d] status[%s] |%s| at %s \n " ,
i , nt_errstr ( status ) , info ? " +info " : " " ,
location ) ) ;
body . data = outhdr + SMB2_HDR_BODY ;
body . length = 8 ;
SSVAL ( body . data , 0 , 9 ) ;
if ( info ) {
SIVAL ( body . data , 0x04 , info - > length ) ;
} else {
/* Allocated size of req->out.vector[i].iov_base
* * MUST BE * OUTVEC_ALLOC_SIZE . So we have room for
* 1 byte without having to do an alloc .
*/
info = talloc_zero_array ( req - > out . vector ,
DATA_BLOB ,
1 ) ;
if ( ! info ) {
return NT_STATUS_NO_MEMORY ;
}
info - > data = ( ( uint8_t * ) outhdr ) +
OUTVEC_ALLOC_SIZE - 1 ;
info - > length = 1 ;
SCVAL ( info - > data , 0 , 0 ) ;
}
/*
* if a request fails , all other remaining
* compounded requests should fail too
*/
req - > next_status = NT_STATUS_INVALID_PARAMETER ;
return smbd_smb2_request_done_ex ( req , status , body , info , __location__ ) ;
}
2009-06-09 22:44:13 +04:00
struct smbd_smb2_send_oplock_break_state {
struct smbd_server_connection * sconn ;
uint8_t buf [ 4 + SMB2_HDR_BODY + 0x18 ] ;
struct iovec vector ;
} ;
static void smbd_smb2_oplock_break_writev_done ( struct tevent_req * subreq ) ;
NTSTATUS smbd_smb2_send_oplock_break ( struct smbd_server_connection * sconn ,
2010-05-20 06:28:26 +04:00
uint64_t file_id_persistent ,
2009-06-09 22:44:13 +04:00
uint64_t file_id_volatile ,
uint8_t oplock_level )
{
struct smbd_smb2_send_oplock_break_state * state ;
struct tevent_req * subreq ;
uint8_t * hdr ;
uint8_t * body ;
state = talloc ( sconn , struct smbd_smb2_send_oplock_break_state ) ;
if ( state = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state - > sconn = sconn ;
state - > vector . iov_base = ( void * ) state - > buf ;
state - > vector . iov_len = sizeof ( state - > buf ) ;
_smb2_setlen ( state - > buf , sizeof ( state - > buf ) - 4 ) ;
hdr = state - > buf + 4 ;
body = hdr + SMB2_HDR_BODY ;
SIVAL ( hdr , 0 , SMB2_MAGIC ) ;
SSVAL ( hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( hdr , SMB2_HDR_EPOCH , 0 ) ;
SIVAL ( hdr , SMB2_HDR_STATUS , 0 ) ;
SSVAL ( hdr , SMB2_HDR_OPCODE , SMB2_OP_BREAK ) ;
SSVAL ( hdr , SMB2_HDR_CREDIT , 0 ) ;
SIVAL ( hdr , SMB2_HDR_FLAGS , SMB2_HDR_FLAG_REDIRECT ) ;
SIVAL ( hdr , SMB2_HDR_NEXT_COMMAND , 0 ) ;
SBVAL ( hdr , SMB2_HDR_MESSAGE_ID , UINT64_MAX ) ;
SIVAL ( hdr , SMB2_HDR_PID , 0 ) ;
SIVAL ( hdr , SMB2_HDR_TID , 0 ) ;
SBVAL ( hdr , SMB2_HDR_SESSION_ID , 0 ) ;
memset ( hdr + SMB2_HDR_SIGNATURE , 0 , 16 ) ;
SSVAL ( body , 0x00 , 0x18 ) ;
SCVAL ( body , 0x02 , oplock_level ) ;
SCVAL ( body , 0x03 , 0 ) ; /* reserved */
SIVAL ( body , 0x04 , 0 ) ; /* reserved */
2010-05-20 06:28:26 +04:00
SBVAL ( body , 0x08 , file_id_persistent ) ;
2009-06-09 22:44:13 +04:00
SBVAL ( body , 0x10 , file_id_volatile ) ;
subreq = tstream_writev_queue_send ( state ,
sconn - > smb2 . event_ctx ,
sconn - > smb2 . stream ,
sconn - > smb2 . send_queue ,
& state - > vector , 1 ) ;
if ( subreq = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
tevent_req_set_callback ( subreq ,
smbd_smb2_oplock_break_writev_done ,
state ) ;
return NT_STATUS_OK ;
}
static void smbd_smb2_oplock_break_writev_done ( struct tevent_req * subreq )
{
struct smbd_smb2_send_oplock_break_state * state =
tevent_req_callback_data ( subreq ,
struct smbd_smb2_send_oplock_break_state ) ;
struct smbd_server_connection * sconn = state - > sconn ;
int ret ;
int sys_errno ;
ret = tstream_writev_queue_recv ( subreq , & sys_errno ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
NTSTATUS status = map_nt_error_from_unix ( sys_errno ) ;
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
return ;
}
TALLOC_FREE ( state ) ;
}
2009-05-14 16:17:28 +04:00
struct smbd_smb2_request_read_state {
size_t missing ;
bool asked_for_header ;
struct smbd_smb2_request * smb2_req ;
} ;
static int smbd_smb2_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void smbd_smb2_request_read_done ( struct tevent_req * subreq ) ;
static struct tevent_req * smbd_smb2_request_read_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn )
2009-05-14 16:17:28 +04:00
{
struct tevent_req * req ;
struct smbd_smb2_request_read_state * state ;
struct tevent_req * subreq ;
req = tevent_req_create ( mem_ctx , & state ,
struct smbd_smb2_request_read_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > missing = 0 ;
state - > asked_for_header = false ;
2009-06-09 23:29:40 +04:00
state - > smb2_req = smbd_smb2_request_allocate ( state ) ;
2009-05-14 16:17:28 +04:00
if ( tevent_req_nomem ( state - > smb2_req , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2009-08-07 17:21:07 +04:00
state - > smb2_req - > sconn = sconn ;
2009-05-14 16:17:28 +04:00
2009-08-07 17:21:07 +04:00
subreq = tstream_readv_pdu_queue_send ( state , ev , sconn - > smb2 . stream ,
sconn - > smb2 . recv_queue ,
2009-05-14 16:17:28 +04:00
smbd_smb2_request_next_vector ,
state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , smbd_smb2_request_read_done , req ) ;
return req ;
}
static int smbd_smb2_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct smbd_smb2_request_read_state * state =
talloc_get_type_abort ( private_data ,
struct smbd_smb2_request_read_state ) ;
struct smbd_smb2_request * req = state - > smb2_req ;
struct iovec * vector ;
int idx = req - > in . vector_count ;
size_t len = 0 ;
uint8_t * buf = NULL ;
if ( req - > in . vector_count = = 0 ) {
/*
* first we need to get the NBT header
*/
req - > in . vector = talloc_array ( req , struct iovec ,
req - > in . vector_count + 1 ) ;
if ( req - > in . vector = = NULL ) {
return - 1 ;
}
req - > in . vector_count + = 1 ;
req - > in . vector [ idx ] . iov_base = ( void * ) req - > in . nbt_hdr ;
req - > in . vector [ idx ] . iov_len = 4 ;
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
vector [ 0 ] = req - > in . vector [ idx ] ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
if ( req - > in . vector_count = = 1 ) {
/*
* Now we analyze the NBT header
*/
state - > missing = smb2_len ( req - > in . vector [ 0 ] . iov_base ) ;
if ( state - > missing = = 0 ) {
/* if there're no remaining bytes, we're done */
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
req - > in . vector = talloc_realloc ( req , req - > in . vector ,
struct iovec ,
req - > in . vector_count + 1 ) ;
if ( req - > in . vector = = NULL ) {
return - 1 ;
}
req - > in . vector_count + = 1 ;
if ( CVAL ( req - > in . vector [ 0 ] . iov_base , 0 ) ! = 0 ) {
/*
* it ' s a special NBT message ,
* so get all remaining bytes
*/
len = state - > missing ;
} else if ( state - > missing < ( SMB2_HDR_BODY + 2 ) ) {
/*
* it ' s an invalid message , just read what we can get
* and let the caller handle the error
*/
len = state - > missing ;
} else {
/*
* We assume it ' s a SMB2 request ,
* and we first get the header and the
* first 2 bytes ( the struct size ) of the body
*/
len = SMB2_HDR_BODY + 2 ;
state - > asked_for_header = true ;
}
state - > missing - = len ;
buf = talloc_array ( req - > in . vector , uint8_t , len ) ;
if ( buf = = NULL ) {
return - 1 ;
}
req - > in . vector [ idx ] . iov_base = ( void * ) buf ;
req - > in . vector [ idx ] . iov_len = len ;
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
vector [ 0 ] = req - > in . vector [ idx ] ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
if ( state - > missing = = 0 ) {
/* if there're no remaining bytes, we're done */
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
if ( state - > asked_for_header ) {
const uint8_t * hdr ;
size_t full_size ;
size_t next_command_ofs ;
size_t body_size ;
uint8_t * body ;
size_t dyn_size ;
uint8_t * dyn ;
bool invalid = false ;
state - > asked_for_header = false ;
/*
* We got the SMB2 header and the first 2 bytes
* of the body . We fix the size to just the header
* and manually copy the 2 first bytes to the body section
*/
req - > in . vector [ idx - 1 ] . iov_len = SMB2_HDR_BODY ;
hdr = ( const uint8_t * ) req - > in . vector [ idx - 1 ] . iov_base ;
/* allocate vectors for body and dynamic areas */
req - > in . vector = talloc_realloc ( req , req - > in . vector ,
struct iovec ,
req - > in . vector_count + 2 ) ;
if ( req - > in . vector = = NULL ) {
return - 1 ;
}
req - > in . vector_count + = 2 ;
full_size = state - > missing + SMB2_HDR_BODY + 2 ;
next_command_ofs = IVAL ( hdr , SMB2_HDR_NEXT_COMMAND ) ;
body_size = SVAL ( hdr , SMB2_HDR_BODY ) ;
if ( next_command_ofs ! = 0 ) {
if ( next_command_ofs < ( SMB2_HDR_BODY + 2 ) ) {
/*
* this is invalid , just return a zero
* body and let the caller deal with the error
*/
invalid = true ;
} else if ( next_command_ofs > full_size ) {
/*
* this is invalid , just return a zero
* body and let the caller deal with the error
*/
invalid = true ;
} else {
full_size = next_command_ofs ;
}
}
if ( ! invalid ) {
if ( body_size < 2 ) {
/*
* this is invalid , just return a zero
* body and let the caller deal with the error
*/
invalid = true ;
2009-08-14 13:14:42 +04:00
}
2011-05-06 13:26:56 +04:00
/*
* Mask out the lowest bit , the " dynamic " part
* of body_size .
*/
body_size & = ~ 1 ;
2009-08-14 13:14:42 +04:00
if ( body_size > ( full_size - SMB2_HDR_BODY ) ) {
2009-05-14 16:17:28 +04:00
/*
* this is invalid , just return a zero
* body and let the caller deal with the error
*/
invalid = true ;
}
}
if ( invalid ) {
/* the caller should check this */
2009-07-08 19:02:00 +04:00
body_size = 2 ;
2009-05-14 16:17:28 +04:00
}
dyn_size = full_size - ( SMB2_HDR_BODY + body_size ) ;
state - > missing - = ( body_size - 2 ) + dyn_size ;
body = talloc_array ( req - > in . vector , uint8_t , body_size ) ;
if ( body = = NULL ) {
return - 1 ;
}
dyn = talloc_array ( req - > in . vector , uint8_t , dyn_size ) ;
if ( dyn = = NULL ) {
return - 1 ;
}
req - > in . vector [ idx ] . iov_base = ( void * ) body ;
req - > in . vector [ idx ] . iov_len = body_size ;
req - > in . vector [ idx + 1 ] . iov_base = ( void * ) dyn ;
req - > in . vector [ idx + 1 ] . iov_len = dyn_size ;
vector = talloc_array ( mem_ctx , struct iovec , 2 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
/*
* the first 2 bytes of the body were already fetched
* together with the header
*/
memcpy ( body , hdr + SMB2_HDR_BODY , 2 ) ;
vector [ 0 ] . iov_base = body + 2 ;
2009-07-08 19:02:00 +04:00
vector [ 0 ] . iov_len = body_size - 2 ;
2009-05-14 16:17:28 +04:00
vector [ 1 ] = req - > in . vector [ idx + 1 ] ;
* _vector = vector ;
* _count = 2 ;
return 0 ;
}
/*
* when we endup here , we ' re looking for a new SMB2 request
* next . And we ask for its header and the first 2 bytes of
* the body ( like we did for the first SMB2 request ) .
*/
req - > in . vector = talloc_realloc ( req , req - > in . vector ,
struct iovec ,
req - > in . vector_count + 1 ) ;
if ( req - > in . vector = = NULL ) {
return - 1 ;
}
req - > in . vector_count + = 1 ;
/*
* We assume it ' s a SMB2 request ,
* and we first get the header and the
* first 2 bytes ( the struct size ) of the body
*/
len = SMB2_HDR_BODY + 2 ;
if ( len > state - > missing ) {
/* let the caller handle the error */
len = state - > missing ;
}
state - > missing - = len ;
state - > asked_for_header = true ;
buf = talloc_array ( req - > in . vector , uint8_t , len ) ;
if ( buf = = NULL ) {
return - 1 ;
}
req - > in . vector [ idx ] . iov_base = ( void * ) buf ;
req - > in . vector [ idx ] . iov_len = len ;
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
vector [ 0 ] = req - > in . vector [ idx ] ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
static void smbd_smb2_request_read_done ( struct tevent_req * subreq )
{
struct tevent_req * req =
tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
int ret ;
int sys_errno ;
NTSTATUS status ;
ret = tstream_readv_pdu_queue_recv ( subreq , & sys_errno ) ;
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( sys_errno ) ;
tevent_req_nterror ( req , status ) ;
return ;
}
tevent_req_done ( req ) ;
}
static NTSTATUS smbd_smb2_request_read_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct smbd_smb2_request * * _smb2_req )
{
struct smbd_smb2_request_read_state * state =
tevent_req_data ( req ,
struct smbd_smb2_request_read_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
talloc_steal ( mem_ctx , state - > smb2_req - > mem_pool ) ;
* _smb2_req = state - > smb2_req ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
static void smbd_smb2_request_incoming ( struct tevent_req * subreq ) ;
2009-08-07 17:21:07 +04:00
void smbd_smb2_first_negprot ( struct smbd_server_connection * sconn ,
2009-05-14 16:17:28 +04:00
const uint8_t * inbuf , size_t size )
{
NTSTATUS status ;
2010-08-30 17:56:16 +04:00
struct smbd_smb2_request * req = NULL ;
2009-05-14 16:17:28 +04:00
struct tevent_req * subreq ;
DEBUG ( 10 , ( " smbd_smb2_first_negprot: packet length %u \n " ,
( unsigned int ) size ) ) ;
2009-08-07 17:21:07 +04:00
status = smbd_initialize_smb2 ( sconn ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-08-07 17:21:07 +04:00
status = smbd_smb2_request_create ( sconn , inbuf , size , & req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2010-12-14 00:17:49 +03:00
status = smbd_smb2_request_setup_out ( req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
/* ask for the next request */
2009-08-07 17:21:07 +04:00
subreq = smbd_smb2_request_read_send ( sconn , sconn - > smb2 . event_ctx , sconn ) ;
2009-05-14 16:17:28 +04:00
if ( subreq = = NULL ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , " no memory for reading " ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-08-07 17:21:07 +04:00
tevent_req_set_callback ( subreq , smbd_smb2_request_incoming , sconn ) ;
2011-07-08 01:59:41 +04:00
sconn - > num_requests + + ;
2009-05-14 16:17:28 +04:00
}
static void smbd_smb2_request_incoming ( struct tevent_req * subreq )
{
2009-08-07 17:21:07 +04:00
struct smbd_server_connection * sconn = tevent_req_callback_data ( subreq ,
struct smbd_server_connection ) ;
2009-05-14 16:17:28 +04:00
NTSTATUS status ;
2009-07-24 18:21:07 +04:00
struct smbd_smb2_request * req = NULL ;
2009-05-14 16:17:28 +04:00
2009-08-07 17:21:07 +04:00
status = smbd_smb2_request_read_recv ( subreq , sconn , & req ) ;
2009-05-14 16:17:28 +04:00
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2010-04-18 08:20:17 +04:00
DEBUG ( 2 , ( " smbd_smb2_request_incoming: client read error %s \n " ,
nt_errstr ( status ) ) ) ;
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-06-08 15:15:00 +04:00
if ( req - > in . nbt_hdr [ 0 ] ! = 0x00 ) {
DEBUG ( 1 , ( " smbd_smb2_request_incoming: ignore NBT[0x%02X] msg \n " ,
req - > in . nbt_hdr [ 0 ] ) ) ;
2009-06-09 23:29:40 +04:00
TALLOC_FREE ( req ) ;
2009-06-08 15:15:00 +04:00
goto next ;
}
2009-05-14 16:17:28 +04:00
req - > current_idx = 1 ;
DEBUG ( 10 , ( " smbd_smb2_request_incoming: idx[%d] of %d vectors \n " ,
req - > current_idx , req - > in . vector_count ) ) ;
2010-12-11 02:46:41 +03:00
status = smbd_smb2_request_validate ( req ) ;
2009-06-08 15:30:32 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-06-08 15:30:32 +04:00
return ;
}
2010-12-14 00:17:49 +03:00
status = smbd_smb2_request_setup_out ( req ) ;
2009-05-14 16:17:28 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
status = smbd_smb2_request_dispatch ( req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , nt_errstr ( status ) ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-06-08 15:15:00 +04:00
next :
2009-05-14 16:17:28 +04:00
/* ask for the next request (this constructs the main loop) */
2009-08-07 17:21:07 +04:00
subreq = smbd_smb2_request_read_send ( sconn , sconn - > smb2 . event_ctx , sconn ) ;
2009-05-14 16:17:28 +04:00
if ( subreq = = NULL ) {
2009-08-07 17:21:07 +04:00
smbd_server_connection_terminate ( sconn , " no memory for reading " ) ;
2009-05-14 16:17:28 +04:00
return ;
}
2009-08-07 17:21:07 +04:00
tevent_req_set_callback ( subreq , smbd_smb2_request_incoming , sconn ) ;
2011-07-08 01:59:41 +04:00
sconn - > num_requests + + ;
/* The timeout_processing function isn't run nearly
often enough to implement ' max log size ' without
overrunning the size of the file by many megabytes .
This is especially true if we are running at debug
level 10. Checking every 50 SMB2s is a nice
tradeoff of performance vs log file size overrun . */
if ( ( sconn - > num_requests % 50 ) = = 0 & &
need_to_check_log_size ( ) ) {
change_to_root_user ( ) ;
check_log_size ( ) ;
}
2009-05-14 16:17:28 +04:00
}