Skip to content

Commit

Permalink
CIFS: Separate SMB2 header structure
Browse files Browse the repository at this point in the history
In order to support compounding and encryption we need to separate
RFC1001 length field and SMB2 header structure because the protocol
treats them differently. This change will allow to simplify parsing
of such complex SMB2 packets further.

Signed-off-by: Pavel Shilovsky <[email protected]>
  • Loading branch information
piastry authored and smfrench committed Feb 1, 2017
1 parent 9c25702 commit 31473fc
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 148 deletions.
5 changes: 5 additions & 0 deletions fs/cifs/smb2glob.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,9 @@
/* Maximum buffer size value we can send with 1 credit */
#define SMB2_MAX_BUFFER_SIZE 65536

static inline struct smb2_sync_hdr *get_sync_hdr(void *buf)
{
return &(((struct smb2_hdr *)buf)->sync_hdr);
}

#endif /* _SMB2_GLOB_H */
5 changes: 3 additions & 2 deletions fs/cifs/smb2maperror.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "smb2pdu.h"
#include "smb2proto.h"
#include "smb2status.h"
#include "smb2glob.h"

struct status_to_posix_error {
__le32 smb2_status;
Expand Down Expand Up @@ -2449,10 +2450,10 @@ smb2_print_status(__le32 status)
int
map_smb2_to_linux_error(char *buf, bool log_err)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
unsigned int i;
int rc = -EIO;
__le32 smb2err = hdr->Status;
__le32 smb2err = shdr->Status;

if (smb2err == 0)
return 0;
Expand Down
61 changes: 33 additions & 28 deletions fs/cifs/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,31 +28,32 @@
#include "cifs_debug.h"
#include "cifs_unicode.h"
#include "smb2status.h"
#include "smb2glob.h"

static int
check_smb2_hdr(struct smb2_hdr *hdr, __u64 mid)
check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
{
__u64 wire_mid = le64_to_cpu(hdr->MessageId);
__u64 wire_mid = le64_to_cpu(shdr->MessageId);

/*
* Make sure that this really is an SMB, that it is a response,
* and that the message ids match.
*/
if ((hdr->ProtocolId == SMB2_PROTO_NUMBER) &&
if ((shdr->ProtocolId == SMB2_PROTO_NUMBER) &&
(mid == wire_mid)) {
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
if (shdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
return 0;
else {
/* only one valid case where server sends us request */
if (hdr->Command == SMB2_OPLOCK_BREAK)
if (shdr->Command == SMB2_OPLOCK_BREAK)
return 0;
else
cifs_dbg(VFS, "Received Request not response\n");
}
} else { /* bad signature or mid */
if (hdr->ProtocolId != SMB2_PROTO_NUMBER)
if (shdr->ProtocolId != SMB2_PROTO_NUMBER)
cifs_dbg(VFS, "Bad protocol string signature header %x\n",
le32_to_cpu(hdr->ProtocolId));
le32_to_cpu(shdr->ProtocolId));
if (mid != wire_mid)
cifs_dbg(VFS, "Mids do not match: %llu and %llu\n",
mid, wire_mid);
Expand Down Expand Up @@ -95,8 +96,9 @@ static const __le16 smb2_rsp_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
int
smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
struct smb2_hdr *hdr = &pdu->hdr;
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
__u64 mid;
__u32 len = get_rfc1002_length(buf);
__u32 clc_len; /* calculated length */
Expand All @@ -111,7 +113,7 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
* ie Validate the wct via smb2_struct_sizes table above
*/

if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
struct smb2_transform_hdr *thdr =
(struct smb2_transform_hdr *)buf;
struct cifs_ses *ses = NULL;
Expand All @@ -133,10 +135,10 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
}
}


mid = le64_to_cpu(hdr->MessageId);
mid = le64_to_cpu(shdr->MessageId);
if (length < sizeof(struct smb2_pdu)) {
if ((length >= sizeof(struct smb2_hdr)) && (hdr->Status != 0)) {
if ((length >= sizeof(struct smb2_hdr))
&& (shdr->Status != 0)) {
pdu->StructureSize2 = 0;
/*
* As with SMB/CIFS, on some error cases servers may
Expand All @@ -154,29 +156,30 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
return 1;
}

if (check_smb2_hdr(hdr, mid))
if (check_smb2_hdr(shdr, mid))
return 1;

if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
if (shdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
cifs_dbg(VFS, "Illegal structure size %u\n",
le16_to_cpu(hdr->StructureSize));
le16_to_cpu(shdr->StructureSize));
return 1;
}

command = le16_to_cpu(hdr->Command);
command = le16_to_cpu(shdr->Command);
if (command >= NUMBER_OF_SMB2_COMMANDS) {
cifs_dbg(VFS, "Illegal SMB2 command %d\n", command);
return 1;
}

if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 ||
if (command != SMB2_OPLOCK_BREAK_HE && (shdr->Status == 0 ||
pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) {
/* error packets have 9 byte structure size */
cifs_dbg(VFS, "Illegal response size %u for command %d\n",
le16_to_cpu(pdu->StructureSize2), command);
return 1;
} else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0)
} else if (command == SMB2_OPLOCK_BREAK_HE
&& (shdr->Status == 0)
&& (le16_to_cpu(pdu->StructureSize2) != 44)
&& (le16_to_cpu(pdu->StructureSize2) != 36)) {
/* special case for SMB2.1 lease break message */
Expand All @@ -199,7 +202,7 @@ smb2_check_message(char *buf, unsigned int length, struct TCP_Server_Info *srvr)
clc_len, 4 + len, mid);
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
hdr->Status == STATUS_STOPPED_ON_SYMLINK)
shdr->Status == STATUS_STOPPED_ON_SYMLINK)
return 0;
/* Windows 7 server returns 24 bytes more */
if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
Expand Down Expand Up @@ -261,11 +264,12 @@ static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = {
char *
smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
{
struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
*off = 0;
*len = 0;

/* error responses do not have data area */
if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
if (shdr->Status && shdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
(((struct smb2_err_rsp *)hdr)->StructureSize) ==
SMB2_ERROR_STRUCTURE_SIZE2)
return NULL;
Expand All @@ -275,7 +279,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
* of the data buffer offset and data buffer length for the particular
* command.
*/
switch (hdr->Command) {
switch (shdr->Command) {
case SMB2_NEGOTIATE:
*off = le16_to_cpu(
((struct smb2_negotiate_rsp *)hdr)->SecurityBufferOffset);
Expand Down Expand Up @@ -346,7 +350,7 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)

/* return pointer to beginning of data area, ie offset from SMB start */
if ((*off != 0) && (*len != 0))
return (char *)(&hdr->ProtocolId) + *off;
return (char *)shdr + *off;
else
return NULL;
}
Expand All @@ -358,20 +362,21 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
unsigned int
smb2_calc_size(void *buf)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
struct smb2_hdr *hdr = &pdu->hdr;
struct smb2_sync_hdr *shdr = get_sync_hdr(hdr);
int offset; /* the offset from the beginning of SMB to data area */
int data_length; /* the length of the variable length data area */
/* Structure Size has already been checked to make sure it is 64 */
int len = 4 + le16_to_cpu(pdu->hdr.StructureSize);
int len = 4 + le16_to_cpu(shdr->StructureSize);

/*
* StructureSize2, ie length of fixed parameter area has already
* been checked to make sure it is the correct length.
*/
len += le16_to_cpu(pdu->StructureSize2);

if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false)
if (has_smb2_data_area[le16_to_cpu(shdr->Command)] == false)
goto calc_size_exit;

smb2_get_data_area_len(&offset, &data_length, hdr);
Expand Down Expand Up @@ -582,7 +587,7 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)

cifs_dbg(FYI, "Checking for oplock break\n");

if (rsp->hdr.Command != SMB2_OPLOCK_BREAK)
if (rsp->hdr.sync_hdr.Command != SMB2_OPLOCK_BREAK)
return false;

if (rsp->StructureSize !=
Expand Down
26 changes: 14 additions & 12 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ smb2_get_credits_field(struct TCP_Server_Info *server, const int optype)
static unsigned int
smb2_get_credits(struct mid_q_entry *mid)
{
return le16_to_cpu(((struct smb2_hdr *)mid->resp_buf)->CreditRequest);
struct smb2_sync_hdr *shdr = get_sync_hdr(mid->resp_buf);

return le16_to_cpu(shdr->CreditRequest);
}

static int
Expand Down Expand Up @@ -184,10 +186,10 @@ static struct mid_q_entry *
smb2_find_mid(struct TCP_Server_Info *server, char *buf)
{
struct mid_q_entry *mid;
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
__u64 wire_mid = le64_to_cpu(hdr->MessageId);
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);
__u64 wire_mid = le64_to_cpu(shdr->MessageId);

if (hdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
if (shdr->ProtocolId == SMB2_TRANSFORM_PROTO_NUM) {
cifs_dbg(VFS, "encrypted frame parsing not supported yet");
return NULL;
}
Expand All @@ -196,7 +198,7 @@ smb2_find_mid(struct TCP_Server_Info *server, char *buf)
list_for_each_entry(mid, &server->pending_mid_q, qhead) {
if ((mid->mid == wire_mid) &&
(mid->mid_state == MID_REQUEST_SUBMITTED) &&
(mid->command == hdr->Command)) {
(mid->command == shdr->Command)) {
spin_unlock(&GlobalMid_Lock);
return mid;
}
Expand All @@ -209,12 +211,12 @@ static void
smb2_dump_detail(void *buf)
{
#ifdef CONFIG_CIFS_DEBUG2
struct smb2_hdr *smb = (struct smb2_hdr *)buf;
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);

cifs_dbg(VFS, "Cmd: %d Err: 0x%x Flags: 0x%x Mid: %llu Pid: %d\n",
smb->Command, smb->Status, smb->Flags, smb->MessageId,
smb->ProcessId);
cifs_dbg(VFS, "smb buf %p len %u\n", smb, smb2_calc_size(smb));
shdr->Command, shdr->Status, shdr->Flags, shdr->MessageId,
shdr->ProcessId);
cifs_dbg(VFS, "smb buf %p len %u\n", buf, smb2_calc_size(buf));
#endif
}

Expand Down Expand Up @@ -1002,14 +1004,14 @@ smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
static bool
smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
{
struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_sync_hdr *shdr = get_sync_hdr(buf);

if (hdr->Status != STATUS_PENDING)
if (shdr->Status != STATUS_PENDING)
return false;

if (!length) {
spin_lock(&server->req_lock);
server->credits += le16_to_cpu(hdr->CreditRequest);
server->credits += le16_to_cpu(shdr->CreditRequest);
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
}
Expand Down
Loading

0 comments on commit 31473fc

Please sign in to comment.