2009-05-14 17:32:02 +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"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2009-05-14 17:32:02 +04:00
# include "smbd/globals.h"
2009-08-12 19:52:55 +04:00
# include "../libcli/smb/smb_common.h"
2012-03-10 05:08:36 +04:00
# include "../lib/tsocket/tsocket.h"
2011-12-15 17:45:56 +04:00
# include "../librpc/ndr/libndr.h"
2009-05-14 17:32:02 +04:00
2012-12-13 13:44:07 +04:00
extern fstring remote_proto ;
2009-05-19 12:47:51 +04:00
/*
* this is the entry point if SMB2 is selected via
2011-09-05 15:14:40 +04:00
* the SMB negprot and the given dialect .
2009-05-19 12:47:51 +04:00
*/
2011-09-05 15:14:40 +04:00
static void reply_smb20xx ( struct smb_request * req , uint16_t dialect )
2009-05-19 12:47:51 +04:00
{
uint8_t * smb2_inbuf ;
uint8_t * smb2_hdr ;
uint8_t * smb2_body ;
uint8_t * smb2_dyn ;
size_t len = 4 + SMB2_HDR_BODY + 0x24 + 2 ;
smb2_inbuf = talloc_zero_array ( talloc_tos ( ) , uint8_t , len ) ;
if ( smb2_inbuf = = NULL ) {
DEBUG ( 0 , ( " Could not push spnego blob \n " ) ) ;
reply_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
smb2_hdr = smb2_inbuf + 4 ;
smb2_body = smb2_hdr + SMB2_HDR_BODY ;
smb2_dyn = smb2_body + 0x24 ;
SIVAL ( smb2_hdr , SMB2_HDR_PROTOCOL_ID , SMB2_MAGIC ) ;
SIVAL ( smb2_hdr , SMB2_HDR_LENGTH , SMB2_HDR_BODY ) ;
SSVAL ( smb2_body , 0x00 , 0x0024 ) ; /* struct size */
SSVAL ( smb2_body , 0x02 , 0x0001 ) ; /* dialect count */
2011-09-05 15:14:40 +04:00
SSVAL ( smb2_dyn , 0x00 , dialect ) ;
2009-05-19 12:47:51 +04:00
req - > outbuf = NULL ;
2010-06-12 13:26:51 +04:00
smbd_smb2_first_negprot ( req - > sconn , smb2_inbuf , len ) ;
2009-05-19 12:47:51 +04:00
return ;
}
2011-09-05 15:14:40 +04:00
/*
* this is the entry point if SMB2 is selected via
* the SMB negprot and the " SMB 2.002 " dialect .
*/
void reply_smb2002 ( struct smb_request * req , uint16_t choice )
{
reply_smb20xx ( req , SMB2_DIALECT_REVISION_202 ) ;
}
/*
* this is the entry point if SMB2 is selected via
* the SMB negprot and the " SMB 2.??? " dialect .
*/
void reply_smb20ff ( struct smb_request * req , uint16_t choice )
{
req - > sconn - > smb2 . negprot_2ff = true ;
reply_smb20xx ( req , SMB2_DIALECT_REVISION_2FF ) ;
}
2014-06-21 08:29:26 +04:00
enum protocol_types smbd_smb2_protocol_dialect_match ( const uint8_t * indyn ,
const int dialect_count ,
uint16_t * dialect )
2009-05-14 17:32:02 +04:00
{
2014-06-21 08:29:26 +04:00
size_t c = 0 ;
2011-09-05 14:23:51 +04:00
enum protocol_types protocol = PROTOCOL_NONE ;
2009-05-14 17:32:02 +04:00
2012-05-05 11:35:17 +04:00
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB3_00 ) {
2012-05-05 11:35:17 +04:00
break ;
}
2014-02-04 06:09:09 +04:00
if ( lp_server_min_protocol ( ) > PROTOCOL_SMB3_00 ) {
2012-05-05 11:35:17 +04:00
break ;
}
2014-06-21 08:29:26 +04:00
* dialect = SVAL ( indyn , c * 2 ) ;
if ( * dialect = = SMB3_DIALECT_REVISION_300 ) {
2012-05-05 11:35:17 +04:00
protocol = PROTOCOL_SMB3_00 ;
break ;
}
}
2011-12-22 16:11:57 +04:00
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB2_24 ) {
2011-12-22 16:11:57 +04:00
break ;
}
2014-02-04 06:09:09 +04:00
if ( lp_server_min_protocol ( ) > PROTOCOL_SMB2_24 ) {
2011-12-22 16:11:57 +04:00
break ;
}
2014-06-21 08:29:26 +04:00
* dialect = SVAL ( indyn , c * 2 ) ;
if ( * dialect = = SMB2_DIALECT_REVISION_224 ) {
2011-12-22 16:11:57 +04:00
protocol = PROTOCOL_SMB2_24 ;
break ;
}
}
2011-11-19 17:02:22 +04:00
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB2_22 ) {
2011-11-19 17:02:22 +04:00
break ;
}
2014-02-04 06:09:09 +04:00
if ( lp_server_min_protocol ( ) > PROTOCOL_SMB2_22 ) {
2011-11-19 17:02:22 +04:00
break ;
}
2014-06-21 08:29:26 +04:00
* dialect = SVAL ( indyn , c * 2 ) ;
if ( * dialect = = SMB2_DIALECT_REVISION_222 ) {
2011-11-19 17:02:22 +04:00
protocol = PROTOCOL_SMB2_22 ;
break ;
}
}
2011-09-05 14:23:51 +04:00
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB2_10 ) {
2011-09-05 15:14:40 +04:00
break ;
}
2014-02-04 06:09:09 +04:00
if ( lp_server_min_protocol ( ) > PROTOCOL_SMB2_10 ) {
2011-09-05 15:14:40 +04:00
break ;
}
2014-06-21 08:29:26 +04:00
* dialect = SVAL ( indyn , c * 2 ) ;
if ( * dialect = = SMB2_DIALECT_REVISION_210 ) {
2011-09-05 15:14:40 +04:00
protocol = PROTOCOL_SMB2_10 ;
break ;
}
}
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB2_02 ) {
2011-09-05 15:14:40 +04:00
break ;
}
2014-02-04 06:09:09 +04:00
if ( lp_server_min_protocol ( ) > PROTOCOL_SMB2_02 ) {
2011-09-05 15:14:40 +04:00
break ;
}
2014-06-21 08:29:26 +04:00
* dialect = SVAL ( indyn , c * 2 ) ;
if ( * dialect = = SMB2_DIALECT_REVISION_202 ) {
2011-09-05 14:23:51 +04:00
protocol = PROTOCOL_SMB2_02 ;
2009-05-14 17:32:02 +04:00
break ;
}
}
2014-06-21 08:29:26 +04:00
return protocol ;
}
NTSTATUS smbd_smb2_request_process_negprot ( struct smbd_smb2_request * req )
{
NTSTATUS status ;
const uint8_t * inbody ;
const uint8_t * indyn = NULL ;
DATA_BLOB outbody ;
DATA_BLOB outdyn ;
DATA_BLOB negprot_spnego_blob ;
uint16_t security_offset ;
DATA_BLOB security_buffer ;
size_t expected_dyn_size = 0 ;
size_t c ;
uint16_t security_mode ;
uint16_t dialect_count ;
uint16_t in_security_mode ;
uint32_t in_capabilities ;
DATA_BLOB in_guid_blob ;
struct GUID in_guid ;
uint16_t dialect = 0 ;
uint32_t capabilities ;
DATA_BLOB out_guid_blob ;
struct GUID out_guid ;
enum protocol_types protocol = PROTOCOL_NONE ;
uint32_t max_limit ;
uint32_t max_trans = lp_smb2_max_trans ( ) ;
uint32_t max_read = lp_smb2_max_read ( ) ;
uint32_t max_write = lp_smb2_max_write ( ) ;
NTTIME now = timeval_to_nttime ( & req - > request_time ) ;
status = smbd_smb2_request_verify_sizes ( req , 0x24 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
inbody = SMBD_SMB2_IN_BODY_PTR ( req ) ;
dialect_count = SVAL ( inbody , 0x02 ) ;
in_security_mode = SVAL ( inbody , 0x04 ) ;
in_capabilities = IVAL ( inbody , 0x08 ) ;
in_guid_blob = data_blob_const ( inbody + 0x0C , 16 ) ;
if ( dialect_count = = 0 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
status = GUID_from_ndr_blob ( & in_guid_blob , & in_guid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
expected_dyn_size = dialect_count * 2 ;
if ( SMBD_SMB2_IN_DYN_LEN ( req ) < expected_dyn_size ) {
return smbd_smb2_request_error ( req , NT_STATUS_INVALID_PARAMETER ) ;
}
indyn = SMBD_SMB2_IN_DYN_PTR ( req ) ;
protocol = smbd_smb2_protocol_dialect_match ( indyn ,
dialect_count ,
& dialect ) ;
2011-09-05 15:14:40 +04:00
for ( c = 0 ; protocol = = PROTOCOL_NONE & & c < dialect_count ; c + + ) {
2014-02-04 06:09:09 +04:00
if ( lp_server_max_protocol ( ) < PROTOCOL_SMB2_10 ) {
2011-09-05 15:14:40 +04:00
break ;
}
dialect = SVAL ( indyn , c * 2 ) ;
if ( dialect = = SMB2_DIALECT_REVISION_2FF ) {
if ( req - > sconn - > smb2 . negprot_2ff ) {
req - > sconn - > smb2 . negprot_2ff = false ;
protocol = PROTOCOL_SMB2_10 ;
break ;
}
}
}
2011-09-05 14:23:51 +04:00
if ( protocol = = PROTOCOL_NONE ) {
return smbd_smb2_request_error ( req , NT_STATUS_NOT_SUPPORTED ) ;
2009-05-14 17:32:02 +04:00
}
if ( get_remote_arch ( ) ! = RA_SAMBA ) {
set_remote_arch ( RA_VISTA ) ;
}
2012-12-13 13:44:07 +04:00
fstr_sprintf ( remote_proto , " SMB%X_%02X " ,
( dialect > > 8 ) & 0xFF , dialect & 0xFF ) ;
reload_services ( req - > sconn , conn_snum_used , true ) ;
DEBUG ( 3 , ( " Selected protocol %s \n " , remote_proto ) ) ;
2009-05-14 17:32:02 +04:00
/* negprot_spnego() returns a the server guid in the first 16 bytes */
2010-07-20 03:45:16 +04:00
negprot_spnego_blob = negprot_spnego ( req , req - > sconn ) ;
2009-05-14 17:32:02 +04:00
if ( negprot_spnego_blob . data = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
if ( negprot_spnego_blob . length < 16 ) {
return smbd_smb2_request_error ( req , NT_STATUS_INTERNAL_ERROR ) ;
}
2012-10-03 23:50:42 +04:00
security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED ;
if ( lp_server_signing ( ) = = SMB_SIGNING_REQUIRED ) {
security_mode | = SMB2_NEGOTIATE_SIGNING_REQUIRED ;
2009-05-22 23:26:03 +04:00
}
capabilities = 0 ;
if ( lp_host_msdfs ( ) ) {
capabilities | = SMB2_CAP_DFS ;
}
2012-08-08 09:07:53 +04:00
if ( ( protocol > = PROTOCOL_SMB2_24 ) & &
2012-09-12 18:43:36 +04:00
( lp_smb_encrypt ( - 1 ) ! = SMB_SIGNING_OFF ) & &
( in_capabilities & SMB2_CAP_ENCRYPTION ) ) {
capabilities | = SMB2_CAP_ENCRYPTION ;
2012-08-08 09:07:53 +04:00
}
2011-09-05 14:14:06 +04:00
/*
* 0x10000 ( 65536 ) is the maximum allowed message size
2012-02-27 17:57:47 +04:00
* for SMB 2.0
2011-09-05 14:14:06 +04:00
*/
max_limit = 0x10000 ;
2012-02-27 17:57:47 +04:00
if ( protocol > = PROTOCOL_SMB2_10 ) {
2012-06-25 23:40:00 +04:00
int p = 0 ;
2012-02-27 17:57:47 +04:00
2012-06-25 23:40:00 +04:00
if ( tsocket_address_is_inet ( req - > sconn - > local_address , " ip " ) ) {
p = tsocket_address_inet_port ( req - > sconn - > local_address ) ;
}
/* largeMTU is not supported over NBT (tcp port 139) */
if ( p ! = NBT_SMB_PORT ) {
2012-02-27 17:57:47 +04:00
capabilities | = SMB2_CAP_LARGE_MTU ;
req - > sconn - > smb2 . supports_multicredit = true ;
2014-06-13 03:55:21 +04:00
/* Windows 2012R2 allows up to 8 MB */
max_limit = 0x800000 ; /* 8MB */
2012-02-27 17:57:47 +04:00
}
}
/*
2014-06-13 03:55:21 +04:00
* the defaults are 8 MB , but we ' ll limit this to max_limit based on
* the dialect ( 64 kb for SMB 2.0 , 8 MB for SMB > = 2.1 with LargeMTU )
2012-02-27 17:57:47 +04:00
*
* user configured values exceeding the limits will be overwritten ,
* only smaller values will be accepted
*/
max_trans = MIN ( max_limit , lp_smb2_max_trans ( ) ) ;
max_read = MIN ( max_limit , lp_smb2_max_read ( ) ) ;
max_write = MIN ( max_limit , lp_smb2_max_write ( ) ) ;
2011-09-05 14:14:06 +04:00
2009-05-14 17:32:02 +04:00
security_offset = SMB2_HDR_BODY + 0x40 ;
2009-06-04 22:14:20 +04:00
# if 1
/* Try SPNEGO auth... */
2009-05-14 17:32:02 +04:00
security_buffer = data_blob_const ( negprot_spnego_blob . data + 16 ,
negprot_spnego_blob . length - 16 ) ;
2009-06-04 22:14:20 +04:00
# else
2009-05-20 21:45:28 +04:00
/* for now we want raw NTLMSSP */
security_buffer = data_blob_const ( NULL , 0 ) ;
2009-06-04 22:14:20 +04:00
# endif
2009-05-20 21:45:28 +04:00
2011-12-15 17:45:56 +04:00
out_guid_blob = data_blob_const ( negprot_spnego_blob . data , 16 ) ;
status = GUID_from_ndr_blob ( & out_guid_blob , & out_guid ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
2013-12-04 17:59:07 +04:00
outbody = smbd_smb2_generate_outbody ( req , 0x40 ) ;
2009-05-14 17:32:02 +04:00
if ( outbody . data = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
SSVAL ( outbody . data , 0x00 , 0x40 + 1 ) ; /* struct size */
2009-05-22 23:26:03 +04:00
SSVAL ( outbody . data , 0x02 ,
security_mode ) ; /* security mode */
2009-05-14 17:32:02 +04:00
SSVAL ( outbody . data , 0x04 , dialect ) ; /* dialect revision */
SSVAL ( outbody . data , 0x06 , 0 ) ; /* reserved */
memcpy ( outbody . data + 0x08 ,
2011-12-15 17:45:56 +04:00
out_guid_blob . data , 16 ) ; /* server guid */
2009-05-22 23:26:03 +04:00
SIVAL ( outbody . data , 0x18 ,
capabilities ) ; /* capabilities */
2011-09-05 14:14:06 +04:00
SIVAL ( outbody . data , 0x1C , max_trans ) ; /* max transact size */
2012-02-27 17:56:57 +04:00
SIVAL ( outbody . data , 0x20 , max_read ) ; /* max read size */
SIVAL ( outbody . data , 0x24 , max_write ) ; /* max write size */
2012-07-26 11:55:29 +04:00
SBVAL ( outbody . data , 0x28 , now ) ; /* system time */
2009-05-14 17:32:02 +04:00
SBVAL ( outbody . data , 0x30 , 0 ) ; /* server start time */
SSVAL ( outbody . data , 0x38 ,
security_offset ) ; /* security buffer offset */
SSVAL ( outbody . data , 0x3A ,
security_buffer . length ) ; /* security buffer length */
SIVAL ( outbody . data , 0x3C , 0 ) ; /* reserved */
outdyn = security_buffer ;
2011-05-30 18:30:54 +04:00
req - > sconn - > using_smb2 = true ;
2012-05-11 17:19:20 +04:00
if ( dialect ! = SMB2_DIALECT_REVISION_2FF ) {
2011-12-15 17:45:56 +04:00
struct smbXsrv_connection * conn = req - > sconn - > conn ;
status = smbXsrv_connection_init_tables ( conn , protocol ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return smbd_smb2_request_error ( req , status ) ;
}
conn - > smb2 . client . capabilities = in_capabilities ;
conn - > smb2 . client . security_mode = in_security_mode ;
conn - > smb2 . client . guid = in_guid ;
conn - > smb2 . client . num_dialects = dialect_count ;
conn - > smb2 . client . dialects = talloc_array ( conn ,
uint16_t ,
dialect_count ) ;
if ( conn - > smb2 . client . dialects = = NULL ) {
return smbd_smb2_request_error ( req , NT_STATUS_NO_MEMORY ) ;
}
for ( c = 0 ; c < dialect_count ; c + + ) {
conn - > smb2 . client . dialects [ c ] = SVAL ( indyn , c * 2 ) ;
}
conn - > smb2 . server . capabilities = capabilities ;
conn - > smb2 . server . security_mode = security_mode ;
conn - > smb2 . server . guid = out_guid ;
conn - > smb2 . server . dialect = dialect ;
conn - > smb2 . server . max_trans = max_trans ;
conn - > smb2 . server . max_read = max_read ;
conn - > smb2 . server . max_write = max_write ;
2012-05-11 17:19:20 +04:00
req - > sconn - > smb2 . max_trans = max_trans ;
req - > sconn - > smb2 . max_read = max_read ;
req - > sconn - > smb2 . max_write = max_write ;
}
2011-05-30 18:30:54 +04:00
2009-05-14 17:32:02 +04:00
return smbd_smb2_request_done ( req , outbody , & outdyn ) ;
}