2009-05-15 13:20:34 +04:00
/*
Unix SMB / CIFS implementation .
Core SMB2 server
Copyright ( C ) Stefan Metzmacher 2009
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "smbd/globals.h"
# include "../source4/libcli/smb2/smb2_constants.h"
static NTSTATUS smbd_smb2_session_setup ( struct smbd_smb2_request * req ,
uint64_t in_session_id ,
DATA_BLOB in_security_buffer ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id ) ;
NTSTATUS smbd_smb2_request_process_sesssetup ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
const uint8_t * inbody ;
int i = req - > current_idx ;
uint8_t * outhdr ;
DATA_BLOB outbody ;
DATA_BLOB outdyn ;
size_t expected_body_size = 0x19 ;
size_t body_size ;
uint64_t in_session_id ;
uint16_t in_security_offset ;
uint16_t in_security_length ;
DATA_BLOB in_security_buffer ;
uint64_t out_session_id ;
uint16_t out_security_offset ;
DATA_BLOB out_security_buffer ;
NTSTATUS status ;
inhdr = ( const uint8_t * ) req - > in . vector [ i + 0 ] . iov_base ;
if ( req - > in . vector [ i + 1 ] . iov_len ! = ( expected_body_size & 0xFFFFFFFE ) ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
inbody = ( const uint8_t * ) req - > in . vector [ i + 1 ] . iov_base ;
body_size = SVAL ( inbody , 0x00 ) ;
if ( body_size ! = expected_body_size ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
in_security_offset = SVAL ( inbody , 0x0C ) ;
in_security_length = SVAL ( inbody , 0x0E ) ;
if ( in_security_offset ! = ( SMB2_HDR_BODY + ( body_size & 0xFFFFFFFE ) ) ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
if ( in_security_length > req - > in . vector [ i + 2 ] . iov_len ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
2009-05-22 13:06:54 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2009-05-15 13:20:34 +04:00
in_security_buffer . data = ( uint8_t * ) req - > in . vector [ i + 2 ] . iov_base ;
in_security_buffer . length = in_security_length ;
status = smbd_smb2_session_setup ( req ,
in_session_id ,
in_security_buffer ,
& out_security_buffer ,
& out_session_id ) ;
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
status = nt_status_squash ( status ) ;
return smbd_smb2_request_error ( req , status ) ;
}
out_security_offset = SMB2_HDR_BODY + 0x08 ;
outhdr = ( uint8_t * ) req - > out . vector [ i ] . iov_base ;
outbody = data_blob_talloc ( req - > out . vector , NULL , 0x08 ) ;
if ( outbody . data = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
SBVAL ( outhdr , SMB2_HDR_SESSION_ID , out_session_id ) ;
SSVAL ( outbody . data , 0x00 , 0x08 + 1 ) ; /* struct size */
SSVAL ( outbody . data , 0x02 , 0 ) ; /* session flags */
SSVAL ( outbody . data , 0x04 ,
out_security_offset ) ; /* security buffer offset */
SSVAL ( outbody . data , 0x06 ,
out_security_buffer . length ) ; /* security buffer length */
outdyn = out_security_buffer ;
return smbd_smb2_request_done_ex ( req , status , outbody , & outdyn ) ;
}
static int smbd_smb2_session_destructor ( struct smbd_smb2_session * session )
{
if ( session - > conn = = NULL ) {
return 0 ;
}
2009-05-15 13:50:20 +04:00
/* first free all tcons */
while ( session - > tcons . list ) {
talloc_free ( session - > tcons . list ) ;
}
2009-05-15 13:20:34 +04:00
idr_remove ( session - > conn - > smb2 . sessions . idtree , session - > vuid ) ;
DLIST_REMOVE ( session - > conn - > smb2 . sessions . list , session ) ;
2009-05-15 13:40:19 +04:00
session - > vuid = 0 ;
session - > status = NT_STATUS_USER_SESSION_DELETED ;
session - > conn = NULL ;
2009-05-15 13:20:34 +04:00
return 0 ;
}
static NTSTATUS smbd_smb2_session_setup ( struct smbd_smb2_request * req ,
uint64_t in_session_id ,
DATA_BLOB in_security_buffer ,
DATA_BLOB * out_security_buffer ,
uint64_t * out_session_id )
{
struct smbd_smb2_session * session ;
NTSTATUS status ;
if ( in_session_id = = 0 ) {
int id ;
/* create a new session */
session = talloc_zero ( req - > conn , struct smbd_smb2_session ) ;
if ( session = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
session - > status = NT_STATUS_MORE_PROCESSING_REQUIRED ;
id = idr_get_new_random ( req - > conn - > smb2 . sessions . idtree ,
session ,
req - > conn - > smb2 . sessions . limit ) ;
if ( id = = - 1 ) {
return NT_STATUS_INSUFFICIENT_RESOURCES ;
}
session - > vuid = id ;
2009-05-15 13:50:20 +04:00
session - > tcons . idtree = idr_init ( session ) ;
if ( session - > tcons . idtree = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
session - > tcons . limit = 0x00FFFFFF ;
session - > tcons . list = NULL ;
2009-05-15 13:20:34 +04:00
DLIST_ADD_END ( req - > conn - > smb2 . sessions . list , session ,
struct smbd_smb2_session * ) ;
session - > conn = req - > conn ;
talloc_set_destructor ( session , smbd_smb2_session_destructor ) ;
} else {
void * p ;
/* lookup an existing session */
p = idr_find ( req - > conn - > 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_REQUEST_NOT_ACCEPTED ;
}
if ( session - > auth_ntlmssp_state = = NULL ) {
status = auth_ntlmssp_start ( & session - > auth_ntlmssp_state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
}
status = auth_ntlmssp_update ( session - > auth_ntlmssp_state ,
in_security_buffer ,
out_security_buffer ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_MORE_PROCESSING_REQUIRED ) ) {
/* nothing to do */
} else if ( NT_STATUS_IS_OK ( status ) ) {
/* TODO: setup session key for signing */
session - > status = NT_STATUS_OK ;
2009-05-20 22:51:10 +04:00
/*
* we attach the session to the request
* so that the response can be signed
*/
req - > session = session ;
2009-05-15 13:20:34 +04:00
} else {
return status ;
}
* out_session_id = session - > vuid ;
return status ;
}
2009-05-20 22:51:10 +04:00
NTSTATUS smbd_smb2_request_check_session ( struct smbd_smb2_request * req )
{
const uint8_t * inhdr ;
int i = req - > current_idx ;
uint64_t in_session_id ;
void * p ;
struct smbd_smb2_session * session ;
inhdr = ( const uint8_t * ) req - > in . vector [ i + 0 ] . iov_base ;
2009-05-22 13:06:54 +04:00
in_session_id = BVAL ( inhdr , SMB2_HDR_SESSION_ID ) ;
2009-05-20 22:51:10 +04:00
/* lookup an existing session */
p = idr_find ( req - > conn - > 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 ;
}
req - > session = session ;
return NT_STATUS_OK ;
}
2009-05-15 13:40:19 +04:00
NTSTATUS smbd_smb2_request_process_logoff ( struct smbd_smb2_request * req )
{
const uint8_t * inbody ;
int i = req - > current_idx ;
DATA_BLOB outbody ;
size_t expected_body_size = 0x04 ;
size_t body_size ;
if ( req - > in . vector [ i + 1 ] . iov_len ! = ( expected_body_size & 0xFFFFFFFE ) ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
inbody = ( const uint8_t * ) req - > in . vector [ i + 1 ] . iov_base ;
body_size = SVAL ( inbody , 0x00 ) ;
if ( body_size ! = expected_body_size ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
/*
* TODO : cancel all outstanding requests on the session
* and delete all tree connections .
*/
smbd_smb2_session_destructor ( req - > session ) ;
/*
* we may need to sign the response , so we need to keep
* the session until the response is sent to the wire .
*/
talloc_steal ( req , req - > session ) ;
outbody = data_blob_talloc ( req - > out . vector , NULL , 0x04 ) ;
if ( outbody . data = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
SSVAL ( outbody . data , 0x00 , 0x04 ) ; /* struct size */
SSVAL ( outbody . data , 0x02 , 0 ) ; /* reserved */
return smbd_smb2_request_done ( req , outbody , NULL ) ;
}