Skip to content

Commit b26df4f

Browse files
paliSteve French
authored andcommitted
cifs: Improve establishing SMB connection with NetBIOS session
Function ip_rfc1001_connect() send NetBIOS session request but currently does not read response. It even does not wait for the response. Instead it just calls usleep_range(1000, 2000) and explain in comment that some servers require short break before sending SMB negotiate packet. Response is later handled in generic is_smb_response() function called from cifs_demultiplex_thread(). That comment probably refers to the old DOS SMB server which cannot process incoming SMB negotiate packet if it has not sent NetBIOS session response packet. Note that current sleep timeout is too small when trying to establish connection to DOS SMB server running in qemu virtual machine connected over qemu user networking with guestfwd netcat options. So that usleep_range() call is not useful at all. NetBIOS session response packet contains useful error information, like the server name specified NetBIOS session request packet is incorrect. Old Windows SMB servers and even the latest SMB server on the latest Windows Server 2022 version requires that the name is the correct server name, otherwise they return error RFC1002_NOT_PRESENT. This applies for all SMB dialects (old SMB1, and also modern SMB2 and SMB3). Therefore read the reply of NetBIOS session request and implement parsing of the reply. Log received error to dmesg to help debugging reason why connection was refused. Also convert NetBIOS error to useful errno. Note that ip_rfc1001_connect() function is used only when doing connection over port 139. So the common SMB scenario over port 445 is not affected by this change at all. Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 781802a commit b26df4f

File tree

1 file changed

+134
-3
lines changed

1 file changed

+134
-3
lines changed

fs/smb/client/connect.c

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,6 +3025,44 @@ bind_socket(struct TCP_Server_Info *server)
30253025
return rc;
30263026
}
30273027

3028+
static int
3029+
smb_recv_kvec(struct TCP_Server_Info *server, struct msghdr *msg, size_t *recv)
3030+
{
3031+
int rc = 0;
3032+
int retries = 0;
3033+
int msg_flags = server->noblocksnd ? MSG_DONTWAIT : 0;
3034+
3035+
*recv = 0;
3036+
3037+
while (msg_data_left(msg)) {
3038+
rc = sock_recvmsg(server->ssocket, msg, msg_flags);
3039+
if (rc == -EAGAIN) {
3040+
retries++;
3041+
if (retries >= 14 ||
3042+
(!server->noblocksnd && (retries > 2))) {
3043+
cifs_server_dbg(VFS, "sends on sock %p stuck for 15 seconds\n",
3044+
server->ssocket);
3045+
return -EAGAIN;
3046+
}
3047+
msleep(1 << retries);
3048+
continue;
3049+
}
3050+
3051+
if (rc < 0)
3052+
return rc;
3053+
3054+
if (rc == 0) {
3055+
cifs_dbg(FYI, "Received no data (TCP RST)\n");
3056+
return -ECONNABORTED;
3057+
}
3058+
3059+
/* recv was at least partially successful */
3060+
*recv += rc;
3061+
retries = 0; /* in case we get ENOSPC on the next send */
3062+
}
3063+
return 0;
3064+
}
3065+
30283066
static int
30293067
ip_rfc1001_connect(struct TCP_Server_Info *server)
30303068
{
@@ -3035,10 +3073,12 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
30353073
* sessinit is sent but no second negprot
30363074
*/
30373075
struct rfc1002_session_packet req = {};
3076+
struct rfc1002_session_packet resp = {};
30383077
struct msghdr msg = {};
30393078
struct kvec iov = {};
30403079
unsigned int len;
30413080
size_t sent;
3081+
size_t recv;
30423082

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

@@ -3082,10 +3122,101 @@ ip_rfc1001_connect(struct TCP_Server_Info *server)
30823122
/*
30833123
* RFC1001 layer in at least one server requires very short break before
30843124
* negprot presumably because not expecting negprot to follow so fast.
3085-
* This is a simple solution that works without complicating the code
3086-
* and causes no significant slowing down on mount for everyone else
3125+
* For example DOS SMB servers cannot process negprot if it was received
3126+
* before the server sent response for SESSION_REQUEST packet. So, wait
3127+
* for the response, read it and parse it as it can contain useful error
3128+
* information (e.g. specified server name was incorrect). For example
3129+
* even the latest Windows Server 2022 SMB1 server over port 139 send
3130+
* error if its server name was in SESSION_REQUEST packet incorrect.
3131+
* Nowadays usage of port 139 is not common, so waiting for reply here
3132+
* does not slowing down mounting of common case (over port 445).
30873133
*/
3088-
usleep_range(1000, 2000);
3134+
len = offsetof(typeof(resp), trailer);
3135+
iov.iov_base = &resp;
3136+
iov.iov_len = len;
3137+
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
3138+
rc = smb_recv_kvec(server, &msg, &recv);
3139+
if (rc < 0 || recv != len)
3140+
return (rc == -EINTR || rc == -EAGAIN) ? rc : -ECONNABORTED;
3141+
3142+
switch (resp.type) {
3143+
case RFC1002_POSITIVE_SESSION_RESPONSE:
3144+
if (be16_to_cpu(resp.length) != 0) {
3145+
cifs_dbg(VFS, "RFC 1002 positive session response but with invalid non-zero length %u\n",
3146+
be16_to_cpu(resp.length));
3147+
return -EIO;
3148+
}
3149+
cifs_dbg(FYI, "RFC 1002 positive session response");
3150+
break;
3151+
case RFC1002_NEGATIVE_SESSION_RESPONSE:
3152+
/* Read RFC1002 response error code and convert it to errno in rc */
3153+
len = sizeof(resp.trailer.neg_ses_resp_error_code);
3154+
iov.iov_base = &resp.trailer.neg_ses_resp_error_code;
3155+
iov.iov_len = len;
3156+
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
3157+
if (be16_to_cpu(resp.length) == len &&
3158+
smb_recv_kvec(server, &msg, &recv) == 0 &&
3159+
recv == len) {
3160+
cifs_dbg(VFS, "RFC 1002 negative session response with error 0x%x\n",
3161+
resp.trailer.neg_ses_resp_error_code);
3162+
switch (resp.trailer.neg_ses_resp_error_code) {
3163+
case RFC1002_NOT_LISTENING_CALLED:
3164+
/* server does not listen for specified server name */
3165+
fallthrough;
3166+
case RFC1002_NOT_PRESENT:
3167+
/* server name is incorrect */
3168+
rc = -ENOENT;
3169+
cifs_dbg(VFS, "Server rejected NetBIOS servername %.15s\n",
3170+
server->server_RFC1001_name[0] ?
3171+
server->server_RFC1001_name :
3172+
DEFAULT_CIFS_CALLED_NAME);
3173+
cifs_dbg(VFS, "Specify correct NetBIOS servername in source path or with -o servern= option\n");
3174+
break;
3175+
case RFC1002_NOT_LISTENING_CALLING:
3176+
/* client name was not accepted by server */
3177+
rc = -EACCES;
3178+
cifs_dbg(VFS, "Server rejected NetBIOS clientname %.15s\n",
3179+
server->workstation_RFC1001_name[0] ?
3180+
server->workstation_RFC1001_name :
3181+
"LINUX_CIFS_CLNT");
3182+
cifs_dbg(VFS, "Specify correct NetBIOS clientname with -o netbiosname= option\n");
3183+
break;
3184+
case RFC1002_INSUFFICIENT_RESOURCE:
3185+
/* remote server resource error */
3186+
rc = -EREMOTEIO;
3187+
break;
3188+
case RFC1002_UNSPECIFIED_ERROR:
3189+
default:
3190+
/* other/unknown error */
3191+
rc = -EIO;
3192+
break;
3193+
}
3194+
} else {
3195+
cifs_dbg(VFS, "RFC 1002 negative session response\n");
3196+
rc = -EIO;
3197+
}
3198+
return rc;
3199+
case RFC1002_RETARGET_SESSION_RESPONSE:
3200+
cifs_dbg(VFS, "RFC 1002 retarget session response\n");
3201+
if (be16_to_cpu(resp.length) == sizeof(resp.trailer.retarget_resp)) {
3202+
len = sizeof(resp.trailer.retarget_resp);
3203+
iov.iov_base = &resp.trailer.retarget_resp;
3204+
iov.iov_len = len;
3205+
iov_iter_kvec(&msg.msg_iter, ITER_DEST, &iov, 1, len);
3206+
if (smb_recv_kvec(server, &msg, &recv) == 0 && recv == len) {
3207+
cifs_dbg(VFS, "Server wants to redirect connection\n");
3208+
cifs_dbg(VFS, "Remount with options -o ip=%pI4,port=%u\n",
3209+
&resp.trailer.retarget_resp.retarget_ip_addr,
3210+
be16_to_cpu(resp.trailer.retarget_resp.port));
3211+
}
3212+
}
3213+
cifs_dbg(VFS, "Closing connection\n");
3214+
/* FIXME: Should we automatically redirect to new retarget_resp server? */
3215+
return -EMULTIHOP;
3216+
default:
3217+
cifs_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", resp.type);
3218+
return -EIO;
3219+
}
30893220

30903221
return 0;
30913222
}

0 commit comments

Comments
 (0)