Skip to content

Commit

Permalink
cifs: Fix establishing NetBIOS session for SMB2+ connection
Browse files Browse the repository at this point in the history
Function ip_rfc1001_connect() which establish NetBIOS session for SMB
connections, currently uses smb_send() function for sending NetBIOS Session
Request packet. This function expects that the passed buffer is SMB packet
and for SMB2+ connections it mangles packet header, which breaks prepared
NetBIOS Session Request packet. Result is that this function send garbage
packet for SMB2+ connection, which SMB2+ server cannot parse. That function
is not mangling packets for SMB1 connections, so it somehow works for SMB1.

Fix this problem and instead of smb_send(), use smb_send_kvec() function
which does not mangle prepared packet, this function send them as is. Just
API of this function takes struct msghdr (kvec) instead of packet buffer.

[MS-SMB2] specification allows SMB2 protocol to use NetBIOS as a transport
protocol. NetBIOS can be used over TCP via port 139. So this is a valid
configuration, just not so common. And even recent Windows versions (e.g.
Windows Server 2022) still supports this configuration: SMB over TCP port
139, including for modern SMB2 and SMB3 dialects.

This change fixes SMB2 and SMB3 connections over TCP port 139 which
requires establishing of NetBIOS session. Tested that this change fixes
establishing of SMB2 and SMB3 connections with Windows Server 2022.

Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Pali Rohár authored and Steve French committed Mar 26, 2025
1 parent ad9364a commit 781802a
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 6 deletions.
3 changes: 3 additions & 0 deletions fs/smb/client/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ extern void cifs_small_buf_release(void *);
extern void free_rsp_buf(int, void *);
extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
unsigned int /* length */);
extern int smb_send_kvec(struct TCP_Server_Info *server,
struct msghdr *msg,
size_t *sent);
extern unsigned int _get_xid(void);
extern void _free_xid(unsigned int);
#define get_xid() \
Expand Down
20 changes: 15 additions & 5 deletions fs/smb/client/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -3035,8 +3035,10 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
* sessinit is sent but no second negprot
*/
struct rfc1002_session_packet req = {};
struct smb_hdr *smb_buf = (struct smb_hdr *)&req;
struct msghdr msg = {};
struct kvec iov = {};
unsigned int len;
size_t sent;

req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name);

Expand Down Expand Up @@ -3065,10 +3067,18 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
* As per rfc1002, @len must be the number of bytes that follows the
* length field of a rfc1002 session request payload.
*/
len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req);
len = sizeof(req.trailer.session_req);
req.type = RFC1002_SESSION_REQUEST;
req.flags = 0;
req.length = cpu_to_be16(len);
len += offsetof(typeof(req), trailer.session_req);
iov.iov_base = &req;
iov.iov_len = len;
iov_iter_kvec(&msg.msg_iter, ITER_SOURCE, &iov, 1, len);
rc = smb_send_kvec(server, &msg, &sent);
if (rc < 0 || len != sent)
return (rc == -EINTR || rc == -EAGAIN) ? rc : -ECONNABORTED;

smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len);
rc = smb_send(server, smb_buf, len);
/*
* RFC1001 layer in at least one server requires very short break before
* negprot presumably because not expecting negprot to follow so fast.
Expand All @@ -3077,7 +3087,7 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
*/
usleep_range(1000, 2000);

return rc;
return 0;
}

static int
Expand Down
2 changes: 1 addition & 1 deletion fs/smb/client/transport.c
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ delete_mid(struct mid_q_entry *mid)
* Our basic "send data to server" function. Should be called with srv_mutex
* held. The caller is responsible for handling the results.
*/
static int
int
smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
size_t *sent)
{
Expand Down

0 comments on commit 781802a

Please sign in to comment.