]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cifs: SMB1 split: Move some SMB1 received PDU checking bits to smb1transport.c
authorDavid Howells <dhowells@redhat.com>
Thu, 11 Sep 2025 13:09:17 +0000 (14:09 +0100)
committerSteve French <stfrench@microsoft.com>
Sun, 8 Feb 2026 23:07:45 +0000 (17:07 -0600)
Move some SMB1 received checking bits to smb1transport.c from misc.c
so that they're with the rest of the receive handling code.

Signed-off-by: David Howells <dhowells@redhat.com>
cc: Steve French <sfrench@samba.org>
cc: Paulo Alcantara <pc@manguebit.org>
cc: Enzo Matsumiya <ematsumiya@suse.de>
cc: linux-cifs@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
cc: linux-kernel@vger.kernel.org
Acked-by: Enzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsproto.h
fs/smb/client/misc.c
fs/smb/client/smb1proto.h
fs/smb/client/smb1transport.c

index b151796b3ba5f7df169d6d33195b4f1a1560d8b7..53d23958b9daf35b9474adb2cf4550e4d5926458 100644 (file)
@@ -131,8 +131,6 @@ void cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
 void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
                                           bool mark_smb_session);
 int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session);
-int checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read,
-            struct TCP_Server_Info *server);
 bool is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv);
 bool backup_cred(struct cifs_sb_info *cifs_sb);
 bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file,
index 9529fa385938e05925fc443a91c0dca81464740f..f3ecdf20dbe0f1b52209247499de19ec894984e8 100644 (file)
@@ -314,132 +314,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command,
        return in_len;
 }
 
-static int
-check_smb_hdr(struct smb_hdr *smb)
-{
-       /* does it have the right SMB "signature" ? */
-       if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) {
-               cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
-                        *(unsigned int *)smb->Protocol);
-               return 1;
-       }
-
-       /* if it's a response then accept */
-       if (smb->Flags & SMBFLG_RESPONSE)
-               return 0;
-
-       /* only one valid case where server sends us request */
-       if (smb->Command == SMB_COM_LOCKING_ANDX)
-               return 0;
-
-       /*
-        * Windows NT server returns error resposne (e.g. STATUS_DELETE_PENDING
-        * or STATUS_OBJECT_NAME_NOT_FOUND or ERRDOS/ERRbadfile or any other)
-        * for some TRANS2 requests without the RESPONSE flag set in header.
-        */
-       if (smb->Command == SMB_COM_TRANSACTION2 && smb->Status.CifsError != 0)
-               return 0;
-
-       cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
-                get_mid(smb));
-       return 1;
-}
-
-int
-checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read,
-        struct TCP_Server_Info *server)
-{
-       struct smb_hdr *smb = (struct smb_hdr *)buf;
-       __u32 rfclen = pdu_len;
-       __u32 clc_len;  /* calculated length */
-       cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
-                total_read, rfclen);
-
-       /* is this frame too small to even get to a BCC? */
-       if (total_read < 2 + sizeof(struct smb_hdr)) {
-               if ((total_read >= sizeof(struct smb_hdr) - 1)
-                           && (smb->Status.CifsError != 0)) {
-                       /* it's an error return */
-                       smb->WordCount = 0;
-                       /* some error cases do not return wct and bcc */
-                       return 0;
-               } else if ((total_read == sizeof(struct smb_hdr) + 1) &&
-                               (smb->WordCount == 0)) {
-                       char *tmp = (char *)smb;
-                       /* Need to work around a bug in two servers here */
-                       /* First, check if the part of bcc they sent was zero */
-                       if (tmp[sizeof(struct smb_hdr)] == 0) {
-                               /* some servers return only half of bcc
-                                * on simple responses (wct, bcc both zero)
-                                * in particular have seen this on
-                                * ulogoffX and FindClose. This leaves
-                                * one byte of bcc potentially uninitialized
-                                */
-                               /* zero rest of bcc */
-                               tmp[sizeof(struct smb_hdr)+1] = 0;
-                               return 0;
-                       }
-                       cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");
-                       return smb_EIO1(smb_eio_trace_rx_inv_bcc, tmp[sizeof(struct smb_hdr)]);
-               } else {
-                       cifs_dbg(VFS, "Length less than smb header size\n");
-                       return smb_EIO2(smb_eio_trace_rx_too_short,
-                                       total_read, smb->WordCount);
-               }
-       } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) {
-               cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n",
-                        __func__, smb->WordCount);
-               return smb_EIO2(smb_eio_trace_rx_check_rsp,
-                               total_read, 2 + sizeof(struct smb_hdr));
-       }
-
-       /* otherwise, there is enough to get to the BCC */
-       if (check_smb_hdr(smb))
-               return smb_EIO1(smb_eio_trace_rx_rfc1002_magic, *(u32 *)smb->Protocol);
-       clc_len = smbCalcSize(smb);
-
-       if (rfclen != total_read) {
-               cifs_dbg(VFS, "Length read does not match RFC1001 length %d/%d\n",
-                        rfclen, total_read);
-               return smb_EIO2(smb_eio_trace_rx_check_rsp,
-                               total_read, rfclen);
-       }
-
-       if (rfclen != clc_len) {
-               __u16 mid = get_mid(smb);
-               /* check if bcc wrapped around for large read responses */
-               if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
-                       /* check if lengths match mod 64K */
-                       if (((rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
-                               return 0; /* bcc wrapped */
-               }
-               cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
-                        clc_len, rfclen, mid);
-
-               if (rfclen < clc_len) {
-                       cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
-                                rfclen, mid);
-                       return smb_EIO2(smb_eio_trace_rx_calc_len_too_big,
-                                       rfclen, clc_len);
-               } else if (rfclen > clc_len + 512) {
-                       /*
-                        * Some servers (Windows XP in particular) send more
-                        * data than the lengths in the SMB packet would
-                        * indicate on certain calls (byte range locks and
-                        * trans2 find first calls in particular). While the
-                        * client can handle such a frame by ignoring the
-                        * trailing data, we choose limit the amount of extra
-                        * data to 512 bytes.
-                        */
-                       cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
-                                rfclen, mid);
-                       return smb_EIO2(smb_eio_trace_rx_overlong,
-                                       rfclen, clc_len + 512);
-               }
-       }
-       return 0;
-}
-
 bool
 is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
 {
index 445bb4b0582911774c89659a970c64308d71cafd..8de8b937294d7461f6efabf09b632e80b6ba8baa 100644 (file)
@@ -235,6 +235,8 @@ int SendReceive(const unsigned int xid, struct cifs_ses *ses,
                const int flags);
 bool cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
                       char *buf, int malformed);
+int checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read,
+            struct TCP_Server_Info *server);
 
 
 #endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
index dbe971374842cc9f9dce622bd7a9b7ebdb270697..c2836644a0d577de401ed561763960bd714690f9 100644 (file)
@@ -434,3 +434,129 @@ cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,
        }
        return true;
 }
+
+static int
+check_smb_hdr(struct smb_hdr *smb)
+{
+       /* does it have the right SMB "signature" ? */
+       if (*(__le32 *) smb->Protocol != SMB1_PROTO_NUMBER) {
+               cifs_dbg(VFS, "Bad protocol string signature header 0x%x\n",
+                        *(unsigned int *)smb->Protocol);
+               return 1;
+       }
+
+       /* if it's a response then accept */
+       if (smb->Flags & SMBFLG_RESPONSE)
+               return 0;
+
+       /* only one valid case where server sends us request */
+       if (smb->Command == SMB_COM_LOCKING_ANDX)
+               return 0;
+
+       /*
+        * Windows NT server returns error resposne (e.g. STATUS_DELETE_PENDING
+        * or STATUS_OBJECT_NAME_NOT_FOUND or ERRDOS/ERRbadfile or any other)
+        * for some TRANS2 requests without the RESPONSE flag set in header.
+        */
+       if (smb->Command == SMB_COM_TRANSACTION2 && smb->Status.CifsError != 0)
+               return 0;
+
+       cifs_dbg(VFS, "Server sent request, not response. mid=%u\n",
+                get_mid(smb));
+       return 1;
+}
+
+int
+checkSMB(char *buf, unsigned int pdu_len, unsigned int total_read,
+        struct TCP_Server_Info *server)
+{
+       struct smb_hdr *smb = (struct smb_hdr *)buf;
+       __u32 rfclen = pdu_len;
+       __u32 clc_len;  /* calculated length */
+       cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n",
+                total_read, rfclen);
+
+       /* is this frame too small to even get to a BCC? */
+       if (total_read < 2 + sizeof(struct smb_hdr)) {
+               if ((total_read >= sizeof(struct smb_hdr) - 1)
+                           && (smb->Status.CifsError != 0)) {
+                       /* it's an error return */
+                       smb->WordCount = 0;
+                       /* some error cases do not return wct and bcc */
+                       return 0;
+               } else if ((total_read == sizeof(struct smb_hdr) + 1) &&
+                               (smb->WordCount == 0)) {
+                       char *tmp = (char *)smb;
+                       /* Need to work around a bug in two servers here */
+                       /* First, check if the part of bcc they sent was zero */
+                       if (tmp[sizeof(struct smb_hdr)] == 0) {
+                               /* some servers return only half of bcc
+                                * on simple responses (wct, bcc both zero)
+                                * in particular have seen this on
+                                * ulogoffX and FindClose. This leaves
+                                * one byte of bcc potentially uninitialized
+                                */
+                               /* zero rest of bcc */
+                               tmp[sizeof(struct smb_hdr)+1] = 0;
+                               return 0;
+                       }
+                       cifs_dbg(VFS, "rcvd invalid byte count (bcc)\n");
+                       return smb_EIO1(smb_eio_trace_rx_inv_bcc, tmp[sizeof(struct smb_hdr)]);
+               } else {
+                       cifs_dbg(VFS, "Length less than smb header size\n");
+                       return smb_EIO2(smb_eio_trace_rx_too_short,
+                                       total_read, smb->WordCount);
+               }
+       } else if (total_read < sizeof(*smb) + 2 * smb->WordCount) {
+               cifs_dbg(VFS, "%s: can't read BCC due to invalid WordCount(%u)\n",
+                        __func__, smb->WordCount);
+               return smb_EIO2(smb_eio_trace_rx_check_rsp,
+                               total_read, 2 + sizeof(struct smb_hdr));
+       }
+
+       /* otherwise, there is enough to get to the BCC */
+       if (check_smb_hdr(smb))
+               return smb_EIO1(smb_eio_trace_rx_rfc1002_magic, *(u32 *)smb->Protocol);
+       clc_len = smbCalcSize(smb);
+
+       if (rfclen != total_read) {
+               cifs_dbg(VFS, "Length read does not match RFC1001 length %d/%d\n",
+                        rfclen, total_read);
+               return smb_EIO2(smb_eio_trace_rx_check_rsp,
+                               total_read, rfclen);
+       }
+
+       if (rfclen != clc_len) {
+               __u16 mid = get_mid(smb);
+               /* check if bcc wrapped around for large read responses */
+               if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {
+                       /* check if lengths match mod 64K */
+                       if (((rfclen) & 0xFFFF) == (clc_len & 0xFFFF))
+                               return 0; /* bcc wrapped */
+               }
+               cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n",
+                        clc_len, rfclen, mid);
+
+               if (rfclen < clc_len) {
+                       cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n",
+                                rfclen, mid);
+                       return smb_EIO2(smb_eio_trace_rx_calc_len_too_big,
+                                       rfclen, clc_len);
+               } else if (rfclen > clc_len + 512) {
+                       /*
+                        * Some servers (Windows XP in particular) send more
+                        * data than the lengths in the SMB packet would
+                        * indicate on certain calls (byte range locks and
+                        * trans2 find first calls in particular). While the
+                        * client can handle such a frame by ignoring the
+                        * trailing data, we choose limit the amount of extra
+                        * data to 512 bytes.
+                        */
+                       cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n",
+                                rfclen, mid);
+                       return smb_EIO2(smb_eio_trace_rx_overlong,
+                                       rfclen, clc_len + 512);
+               }
+       }
+       return 0;
+}