2021-03-16 04:49:09 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright ( C ) 2016 Namjae Jeon < linkinjeon @ kernel . org >
* Copyright ( C ) 2018 Samsung Electronics Co . , Ltd .
*/
# include "glob.h"
# include "nterr.h"
# include "smb_common.h"
# include "smbstatus.h"
# include "mgmt/user_session.h"
# include "connection.h"
static int check_smb2_hdr ( struct smb2_hdr * hdr )
{
/*
* Make sure that this really is an SMB , that it is a response .
*/
if ( hdr - > Flags & SMB2_FLAGS_SERVER_TO_REDIR )
return 1 ;
return 0 ;
}
/*
* The following table defines the expected " StructureSize " of SMB2 requests
* in order by SMB2 command . This is similar to " wct " in SMB / CIFS requests .
*
* Note that commands are defined in smb2pdu . h in le16 but the array below is
* indexed by command in host byte order
*/
static const __le16 smb2_req_struct_sizes [ NUMBER_OF_SMB2_COMMANDS ] = {
/* SMB2_NEGOTIATE */ cpu_to_le16 ( 36 ) ,
/* SMB2_SESSION_SETUP */ cpu_to_le16 ( 25 ) ,
/* SMB2_LOGOFF */ cpu_to_le16 ( 4 ) ,
/* SMB2_TREE_CONNECT */ cpu_to_le16 ( 9 ) ,
/* SMB2_TREE_DISCONNECT */ cpu_to_le16 ( 4 ) ,
/* SMB2_CREATE */ cpu_to_le16 ( 57 ) ,
/* SMB2_CLOSE */ cpu_to_le16 ( 24 ) ,
/* SMB2_FLUSH */ cpu_to_le16 ( 24 ) ,
/* SMB2_READ */ cpu_to_le16 ( 49 ) ,
/* SMB2_WRITE */ cpu_to_le16 ( 49 ) ,
/* SMB2_LOCK */ cpu_to_le16 ( 48 ) ,
/* SMB2_IOCTL */ cpu_to_le16 ( 57 ) ,
/* SMB2_CANCEL */ cpu_to_le16 ( 4 ) ,
/* SMB2_ECHO */ cpu_to_le16 ( 4 ) ,
/* SMB2_QUERY_DIRECTORY */ cpu_to_le16 ( 33 ) ,
/* SMB2_CHANGE_NOTIFY */ cpu_to_le16 ( 32 ) ,
/* SMB2_QUERY_INFO */ cpu_to_le16 ( 41 ) ,
/* SMB2_SET_INFO */ cpu_to_le16 ( 33 ) ,
/* use 44 for lease break */
/* SMB2_OPLOCK_BREAK */ cpu_to_le16 ( 36 )
} ;
/*
* The size of the variable area depends on the offset and length fields
* located in different fields for various SMB2 requests . SMB2 requests
* with no variable length info , show an offset of zero for the offset field .
*/
static const bool has_smb2_data_area [ NUMBER_OF_SMB2_COMMANDS ] = {
/* SMB2_NEGOTIATE */ true ,
/* SMB2_SESSION_SETUP */ true ,
/* SMB2_LOGOFF */ false ,
/* SMB2_TREE_CONNECT */ true ,
/* SMB2_TREE_DISCONNECT */ false ,
/* SMB2_CREATE */ true ,
/* SMB2_CLOSE */ false ,
/* SMB2_FLUSH */ false ,
/* SMB2_READ */ true ,
/* SMB2_WRITE */ true ,
/* SMB2_LOCK */ true ,
/* SMB2_IOCTL */ true ,
/* SMB2_CANCEL */ false , /* BB CHECK this not listed in documentation */
/* SMB2_ECHO */ false ,
/* SMB2_QUERY_DIRECTORY */ true ,
/* SMB2_CHANGE_NOTIFY */ false ,
/* SMB2_QUERY_INFO */ true ,
/* SMB2_SET_INFO */ true ,
/* SMB2_OPLOCK_BREAK */ false
} ;
/*
2021-09-26 15:55:02 +03:00
* Set length of the data area and the offset to arguments .
* if they are invalid , return error .
2021-03-16 04:49:09 +03:00
*/
2021-09-26 15:55:02 +03:00
static int smb2_get_data_area_len ( unsigned int * off , unsigned int * len ,
struct smb2_hdr * hdr )
2021-03-16 04:49:09 +03:00
{
2021-09-26 15:55:02 +03:00
int ret = 0 ;
2021-03-16 04:49:09 +03:00
* off = 0 ;
* len = 0 ;
/*
* Following commands have data areas so we have to get the location
* of the data buffer offset and data buffer length for the particular
* command .
*/
switch ( hdr - > Command ) {
case SMB2_SESSION_SETUP :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_sess_setup_req * ) hdr ) - > SecurityBufferOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_sess_setup_req * ) hdr ) - > SecurityBufferLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_TREE_CONNECT :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_tree_connect_req * ) hdr ) - > PathOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_tree_connect_req * ) hdr ) - > PathLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_CREATE :
{
if ( ( ( struct smb2_create_req * ) hdr ) - > CreateContextsLength ) {
* off = le32_to_cpu ( ( ( struct smb2_create_req * )
hdr ) - > CreateContextsOffset ) ;
* len = le32_to_cpu ( ( ( struct smb2_create_req * )
hdr ) - > CreateContextsLength ) ;
break ;
}
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_create_req * ) hdr ) - > NameOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_create_req * ) hdr ) - > NameLength ) ;
2021-03-16 04:49:09 +03:00
break ;
}
case SMB2_QUERY_INFO :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_query_info_req * ) hdr ) - > InputBufferOffset ) ;
* len = le32_to_cpu ( ( ( struct smb2_query_info_req * ) hdr ) - > InputBufferLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_SET_INFO :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_set_info_req * ) hdr ) - > BufferOffset ) ;
* len = le32_to_cpu ( ( ( struct smb2_set_info_req * ) hdr ) - > BufferLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_READ :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_read_req * ) hdr ) - > ReadChannelInfoOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_read_req * ) hdr ) - > ReadChannelInfoLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_WRITE :
2022-07-28 17:41:51 +03:00
if ( ( ( struct smb2_write_req * ) hdr ) - > DataOffset | |
( ( struct smb2_write_req * ) hdr ) - > Length ) {
* off = max_t ( unsigned int ,
le16_to_cpu ( ( ( struct smb2_write_req * ) hdr ) - > DataOffset ) ,
offsetof ( struct smb2_write_req , Buffer ) ) ;
2021-03-30 08:25:35 +03:00
* len = le32_to_cpu ( ( ( struct smb2_write_req * ) hdr ) - > Length ) ;
2021-03-16 04:49:09 +03:00
break ;
}
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_write_req * ) hdr ) - > WriteChannelInfoOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_write_req * ) hdr ) - > WriteChannelInfoLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_QUERY_DIRECTORY :
2021-03-30 08:25:35 +03:00
* off = le16_to_cpu ( ( ( struct smb2_query_directory_req * ) hdr ) - > FileNameOffset ) ;
* len = le16_to_cpu ( ( ( struct smb2_query_directory_req * ) hdr ) - > FileNameLength ) ;
2021-03-16 04:49:09 +03:00
break ;
case SMB2_LOCK :
{
int lock_count ;
/*
* smb2_lock request size is 48 included single
* smb2_lock_element structure size .
*/
2021-03-30 08:25:35 +03:00
lock_count = le16_to_cpu ( ( ( struct smb2_lock_req * ) hdr ) - > LockCount ) - 1 ;
2021-03-16 04:49:09 +03:00
if ( lock_count > 0 ) {
* off = __SMB2_HEADER_STRUCTURE_SIZE + 48 ;
* len = sizeof ( struct smb2_lock_element ) * lock_count ;
}
break ;
}
case SMB2_IOCTL :
2021-03-30 08:25:35 +03:00
* off = le32_to_cpu ( ( ( struct smb2_ioctl_req * ) hdr ) - > InputOffset ) ;
2021-03-16 04:49:09 +03:00
* len = le32_to_cpu ( ( ( struct smb2_ioctl_req * ) hdr ) - > InputCount ) ;
break ;
default :
ksmbd_debug ( SMB , " no length check for command \n " ) ;
break ;
}
if ( * off > 4096 ) {
2021-09-26 15:55:02 +03:00
ksmbd_debug ( SMB , " offset %d too large \n " , * off ) ;
ret = - EINVAL ;
} else if ( ( u64 ) * off + * len > MAX_STREAM_PROT_LEN ) {
ksmbd_debug ( SMB , " Request is larger than maximum stream protocol length(%u): %llu \n " ,
MAX_STREAM_PROT_LEN , ( u64 ) * off + * len ) ;
ret = - EINVAL ;
2021-03-16 04:49:09 +03:00
}
2021-09-26 15:55:02 +03:00
return ret ;
2021-03-16 04:49:09 +03:00
}
/*
* Calculate the size of the SMB message based on the fixed header
* portion , the number of word parameters and the data portion of the message .
*/
2021-09-26 15:55:02 +03:00
static int smb2_calc_size ( void * buf , unsigned int * len )
2021-03-16 04:49:09 +03:00
{
struct smb2_pdu * pdu = ( struct smb2_pdu * ) buf ;
struct smb2_hdr * hdr = & pdu - > hdr ;
2021-09-26 15:55:02 +03:00
unsigned int offset ; /* the offset from the beginning of SMB to data area */
unsigned int data_length ; /* the length of the variable length data area */
int ret ;
2021-03-16 04:49:09 +03:00
/* Structure Size has already been checked to make sure it is 64 */
2021-09-26 15:55:02 +03:00
* len = le16_to_cpu ( hdr - > StructureSize ) ;
2021-03-16 04:49:09 +03:00
/*
* StructureSize2 , ie length of fixed parameter area has already
* been checked to make sure it is the correct length .
*/
2021-09-26 15:55:02 +03:00
* len + = le16_to_cpu ( pdu - > StructureSize2 ) ;
/*
* StructureSize2 of smb2_lock pdu is set to 48 , indicating
* the size of smb2 lock request with single smb2_lock_element
* regardless of number of locks . Subtract single
* smb2_lock_element for correct buffer size check .
*/
if ( hdr - > Command = = SMB2_LOCK )
* len - = sizeof ( struct smb2_lock_element ) ;
2021-03-16 04:49:09 +03:00
if ( has_smb2_data_area [ le16_to_cpu ( hdr - > Command ) ] = = false )
goto calc_size_exit ;
2021-09-26 15:55:02 +03:00
ret = smb2_get_data_area_len ( & offset , & data_length , hdr ) ;
if ( ret )
return ret ;
ksmbd_debug ( SMB , " SMB2 data length %u offset %u \n " , data_length ,
2021-05-26 11:57:12 +03:00
offset ) ;
2021-03-16 04:49:09 +03:00
if ( data_length > 0 ) {
/*
* Check to make sure that data area begins after fixed area ,
* Note that last byte of the fixed area is part of data area
* for some commands , typically those with odd StructureSize ,
* so we must add one to the calculation .
*/
2021-09-26 15:55:02 +03:00
if ( offset + 1 < * len ) {
2021-03-16 04:49:09 +03:00
ksmbd_debug ( SMB ,
2021-09-26 15:55:02 +03:00
" data area offset %d overlaps SMB2 header %u \n " ,
offset + 1 , * len ) ;
return - EINVAL ;
}
* len = offset + data_length ;
2021-03-16 04:49:09 +03:00
}
2021-09-26 15:55:02 +03:00
2021-03-16 04:49:09 +03:00
calc_size_exit :
2021-09-26 15:55:02 +03:00
ksmbd_debug ( SMB , " SMB2 len %u \n " , * len ) ;
return 0 ;
2021-03-16 04:49:09 +03:00
}
static inline int smb2_query_info_req_len ( struct smb2_query_info_req * h )
{
return le32_to_cpu ( h - > InputBufferLength ) +
le32_to_cpu ( h - > OutputBufferLength ) ;
}
static inline int smb2_set_info_req_len ( struct smb2_set_info_req * h )
{
return le32_to_cpu ( h - > BufferLength ) ;
}
static inline int smb2_read_req_len ( struct smb2_read_req * h )
{
return le32_to_cpu ( h - > Length ) ;
}
static inline int smb2_write_req_len ( struct smb2_write_req * h )
{
return le32_to_cpu ( h - > Length ) ;
}
static inline int smb2_query_dir_req_len ( struct smb2_query_directory_req * h )
{
return le32_to_cpu ( h - > OutputBufferLength ) ;
}
static inline int smb2_ioctl_req_len ( struct smb2_ioctl_req * h )
{
return le32_to_cpu ( h - > InputCount ) +
le32_to_cpu ( h - > OutputCount ) ;
}
static inline int smb2_ioctl_resp_len ( struct smb2_ioctl_req * h )
{
return le32_to_cpu ( h - > MaxInputResponse ) +
le32_to_cpu ( h - > MaxOutputResponse ) ;
}
2021-10-07 10:26:58 +03:00
static int smb2_validate_credit_charge ( struct ksmbd_conn * conn ,
struct smb2_hdr * hdr )
2021-03-16 04:49:09 +03:00
{
2021-10-07 10:26:58 +03:00
unsigned int req_len = 0 , expect_resp_len = 0 , calc_credit_num , max_len ;
unsigned short credit_charge = le16_to_cpu ( hdr - > CreditCharge ) ;
2021-03-16 04:49:09 +03:00
void * __hdr = hdr ;
2021-12-29 17:10:03 +03:00
int ret = 0 ;
2021-03-16 04:49:09 +03:00
switch ( hdr - > Command ) {
case SMB2_QUERY_INFO :
req_len = smb2_query_info_req_len ( __hdr ) ;
break ;
case SMB2_SET_INFO :
req_len = smb2_set_info_req_len ( __hdr ) ;
break ;
case SMB2_READ :
req_len = smb2_read_req_len ( __hdr ) ;
break ;
case SMB2_WRITE :
req_len = smb2_write_req_len ( __hdr ) ;
break ;
case SMB2_QUERY_DIRECTORY :
req_len = smb2_query_dir_req_len ( __hdr ) ;
break ;
case SMB2_IOCTL :
req_len = smb2_ioctl_req_len ( __hdr ) ;
expect_resp_len = smb2_ioctl_resp_len ( __hdr ) ;
break ;
2021-10-07 10:26:58 +03:00
case SMB2_CANCEL :
2021-03-16 04:49:09 +03:00
return 0 ;
2021-10-07 10:26:58 +03:00
default :
req_len = 1 ;
break ;
2021-03-16 04:49:09 +03:00
}
2021-10-07 10:26:58 +03:00
credit_charge = max_t ( unsigned short , credit_charge , 1 ) ;
max_len = max_t ( unsigned int , req_len , expect_resp_len ) ;
2021-03-16 04:49:09 +03:00
calc_credit_num = DIV_ROUND_UP ( max_len , SMB2_MAX_BUFFER_SIZE ) ;
2021-06-26 16:56:48 +03:00
if ( credit_charge < calc_credit_num ) {
2021-10-07 10:26:58 +03:00
ksmbd_debug ( SMB , " Insufficient credit charge, given: %d, needed: %d \n " ,
credit_charge , calc_credit_num ) ;
return 1 ;
2021-12-29 17:08:46 +03:00
} else if ( credit_charge > conn - > vals - > max_credits ) {
2021-10-07 10:26:58 +03:00
ksmbd_debug ( SMB , " Too large credit charge: %d \n " , credit_charge ) ;
2021-03-16 04:49:09 +03:00
return 1 ;
}
2021-10-07 10:26:58 +03:00
spin_lock ( & conn - > credits_lock ) ;
2021-12-29 17:10:03 +03:00
if ( credit_charge > conn - > total_credits ) {
2021-10-07 10:26:58 +03:00
ksmbd_debug ( SMB , " Insufficient credits granted, given: %u, granted: %u \n " ,
credit_charge , conn - > total_credits ) ;
ret = 1 ;
}
2021-12-31 03:26:25 +03:00
2022-05-20 08:35:47 +03:00
if ( ( u64 ) conn - > outstanding_credits + credit_charge > conn - > total_credits ) {
2021-12-31 03:26:25 +03:00
ksmbd_debug ( SMB , " Limits exceeding the maximum allowable outstanding requests, given : %u, pending : %u \n " ,
credit_charge , conn - > outstanding_credits ) ;
ret = 1 ;
} else
conn - > outstanding_credits + = credit_charge ;
2021-10-07 10:26:58 +03:00
spin_unlock ( & conn - > credits_lock ) ;
2021-12-31 03:26:25 +03:00
2021-10-07 10:26:58 +03:00
return ret ;
2021-03-16 04:49:09 +03:00
}
int ksmbd_smb2_check_message ( struct ksmbd_work * work )
{
2021-10-05 08:03:36 +03:00
struct smb2_pdu * pdu = ksmbd_req_buf_next ( work ) ;
2021-03-16 04:49:09 +03:00
struct smb2_hdr * hdr = & pdu - > hdr ;
int command ;
__u32 clc_len ; /* calculated length */
2021-11-03 02:08:44 +03:00
__u32 len = get_rfc1002_len ( work - > request_buf ) ;
2021-03-16 04:49:09 +03:00
2021-10-29 02:09:50 +03:00
if ( le32_to_cpu ( hdr - > NextCommand ) > 0 )
2021-03-16 04:49:09 +03:00
len = le32_to_cpu ( hdr - > NextCommand ) ;
2021-10-29 02:09:50 +03:00
else if ( work - > next_smb2_rcv_hdr_off )
2021-03-16 04:49:09 +03:00
len - = work - > next_smb2_rcv_hdr_off ;
if ( check_smb2_hdr ( hdr ) )
return 1 ;
if ( hdr - > StructureSize ! = SMB2_HEADER_STRUCTURE_SIZE ) {
ksmbd_debug ( SMB , " Illegal structure size %u \n " ,
2021-05-26 11:57:12 +03:00
le16_to_cpu ( hdr - > StructureSize ) ) ;
2021-03-16 04:49:09 +03:00
return 1 ;
}
command = le16_to_cpu ( hdr - > Command ) ;
if ( command > = NUMBER_OF_SMB2_COMMANDS ) {
ksmbd_debug ( SMB , " Illegal SMB2 command %d \n " , command ) ;
return 1 ;
}
if ( smb2_req_struct_sizes [ command ] ! = pdu - > StructureSize2 ) {
2021-03-30 08:25:35 +03:00
if ( command ! = SMB2_OPLOCK_BREAK_HE & &
( hdr - > Status = = 0 | | pdu - > StructureSize2 ! = SMB2_ERROR_STRUCTURE_SIZE2_LE ) ) {
2021-03-16 04:49:09 +03:00
/* error packets have 9 byte structure size */
ksmbd_debug ( SMB ,
2021-05-26 11:57:12 +03:00
" Illegal request size %u for command %d \n " ,
le16_to_cpu ( pdu - > StructureSize2 ) , command ) ;
2021-03-16 04:49:09 +03:00
return 1 ;
2021-03-30 08:25:35 +03:00
} else if ( command = = SMB2_OPLOCK_BREAK_HE & &
hdr - > Status = = 0 & &
le16_to_cpu ( pdu - > StructureSize2 ) ! = OP_BREAK_STRUCT_SIZE_20 & &
le16_to_cpu ( pdu - > StructureSize2 ) ! = OP_BREAK_STRUCT_SIZE_21 ) {
2021-03-16 04:49:09 +03:00
/* special case for SMB2.1 lease break message */
ksmbd_debug ( SMB ,
2021-05-26 11:57:12 +03:00
" Illegal request size %d for oplock break \n " ,
le16_to_cpu ( pdu - > StructureSize2 ) ) ;
2021-03-16 04:49:09 +03:00
return 1 ;
}
}
2021-09-26 15:55:02 +03:00
if ( smb2_calc_size ( hdr , & clc_len ) )
return 1 ;
2021-03-16 04:49:09 +03:00
if ( len ! = clc_len ) {
2021-09-26 15:55:02 +03:00
/* client can return one byte more due to implied bcc[0] */
2021-03-16 04:49:09 +03:00
if ( clc_len = = len + 1 )
2021-10-15 06:52:58 +03:00
goto validate_credit ;
2021-03-16 04:49:09 +03:00
/*
* Some windows servers ( win2016 ) will pad also the final
* PDU in a compound to 8 bytes .
*/
if ( ALIGN ( clc_len , 8 ) = = len )
2021-10-15 06:52:58 +03:00
goto validate_credit ;
2021-03-16 04:49:09 +03:00
/*
* windows client also pad up to 8 bytes when compounding .
* If pad is longer than eight bytes , log the server behavior
* ( once ) , since may indicate a problem but allow it and
* continue since the frame is parseable .
*/
if ( clc_len < len ) {
ksmbd_debug ( SMB ,
2021-05-26 11:57:12 +03:00
" cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu \n " ,
len , clc_len , command ,
le64_to_cpu ( hdr - > MessageId ) ) ;
2021-10-15 06:52:58 +03:00
goto validate_credit ;
2021-03-16 04:49:09 +03:00
}
ksmbd_debug ( SMB ,
2021-05-26 11:57:12 +03:00
" cli req too short, len %d not %d. cmd:%d mid:%llu \n " ,
len , clc_len , command ,
le64_to_cpu ( hdr - > MessageId ) ) ;
2021-03-16 04:49:09 +03:00
return 1 ;
}
2021-10-15 06:52:58 +03:00
validate_credit :
if ( ( work - > conn - > vals - > capabilities & SMB2_GLOBAL_CAP_LARGE_MTU ) & &
smb2_validate_credit_charge ( work - > conn , hdr ) ) {
work - > conn - > ops - > set_rsp_status ( work , STATUS_INVALID_PARAMETER ) ;
return 1 ;
}
2021-07-16 08:52:46 +03:00
return 0 ;
2021-03-16 04:49:09 +03:00
}
int smb2_negotiate_request ( struct ksmbd_work * work )
{
return ksmbd_smb_negotiate_common ( work , SMB2_NEGOTIATE_HE ) ;
}