Skip to content

Commit 9764c02

Browse files
committed
SMB3: Add support for multidialect negotiate (SMB2.1 and later)
With the need to discourage use of less secure dialect, SMB1 (CIFS), we temporarily upgraded the dialect to SMB3 in 4.13, but since there are various servers which only support SMB2.1 (2.1 is more secure than CIFS/SMB1) but not optimal for a default dialect - add support for multidialect negotiation. cifs.ko will now request SMB2.1 or later (ie SMB2.1 or SMB3.0, SMB3.02) and the server will pick the latest most secure one it can support. In addition since we are sending multidialect negotiate, add support for secure negotiate to validate that a man in the middle didn't downgrade us. Signed-off-by: Steve French <[email protected]> Reviewed-by: Pavel Shilovsky <[email protected]> CC: Stable <[email protected]> # 4.13+
1 parent ec11653 commit 9764c02

File tree

5 files changed

+139
-18
lines changed

5 files changed

+139
-18
lines changed

fs/cifs/cifsglob.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,8 @@ enum smb_version {
188188
#ifdef CONFIG_CIFS_SMB311
189189
Smb_311,
190190
#endif /* SMB311 */
191+
Smb_3any,
192+
Smb_default,
191193
Smb_version_err
192194
};
193195

@@ -1701,6 +1703,10 @@ extern struct smb_version_values smb20_values;
17011703
#define SMB21_VERSION_STRING "2.1"
17021704
extern struct smb_version_operations smb21_operations;
17031705
extern struct smb_version_values smb21_values;
1706+
#define SMBDEFAULT_VERSION_STRING "default"
1707+
extern struct smb_version_values smbdefault_values;
1708+
#define SMB3ANY_VERSION_STRING "3"
1709+
extern struct smb_version_values smb3any_values;
17041710
#define SMB30_VERSION_STRING "3.0"
17051711
extern struct smb_version_operations smb30_operations;
17061712
extern struct smb_version_values smb30_values;

fs/cifs/connect.c

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ static const match_table_t cifs_smb_version_tokens = {
301301
{ Smb_311, SMB311_VERSION_STRING },
302302
{ Smb_311, ALT_SMB311_VERSION_STRING },
303303
#endif /* SMB311 */
304+
{ Smb_3any, SMB3ANY_VERSION_STRING },
305+
{ Smb_default, SMBDEFAULT_VERSION_STRING },
304306
{ Smb_version_err, NULL }
305307
};
306308

@@ -1148,6 +1150,14 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
11481150
vol->vals = &smb311_values;
11491151
break;
11501152
#endif /* SMB311 */
1153+
case Smb_3any:
1154+
vol->ops = &smb30_operations; /* currently identical with 3.0 */
1155+
vol->vals = &smb3any_values;
1156+
break;
1157+
case Smb_default:
1158+
vol->ops = &smb30_operations; /* currently identical with 3.0 */
1159+
vol->vals = &smbdefault_values;
1160+
break;
11511161
default:
11521162
cifs_dbg(VFS, "Unknown vers= option specified: %s\n", value);
11531163
return 1;
@@ -1274,9 +1284,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
12741284

12751285
vol->actimeo = CIFS_DEF_ACTIMEO;
12761286

1277-
/* FIXME: add autonegotiation for SMB3 or later rather than just SMB3 */
1278-
vol->ops = &smb30_operations; /* both secure and accepted widely */
1279-
vol->vals = &smb30_values;
1287+
/* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */
1288+
vol->ops = &smb30_operations;
1289+
vol->vals = &smbdefault_values;
12801290

12811291
vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
12821292

@@ -1988,11 +1998,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
19881998

19891999
if (got_version == false)
19902000
pr_warn("No dialect specified on mount. Default has changed to "
1991-
"a more secure dialect, SMB3 (vers=3.0), from CIFS "
2001+
"a more secure dialect, SMB2.1 or later (e.g. SMB3), from CIFS "
19922002
"(SMB1). To use the less secure SMB1 dialect to access "
1993-
"old servers which do not support SMB3 specify vers=1.0"
1994-
" on mount. For somewhat newer servers such as Windows "
1995-
"7 try vers=2.1.\n");
2003+
"old servers which do not support SMB3 (or SMB2.1) specify vers=1.0"
2004+
" on mount.\n");
19962005

19972006
kfree(mountdata_copy);
19982007
return 0;
@@ -2133,6 +2142,7 @@ static int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
21332142
if (vol->nosharesock)
21342143
return 0;
21352144

2145+
/* BB update this for smb3any and default case */
21362146
if ((server->vals != vol->vals) || (server->ops != vol->ops))
21372147
return 0;
21382148

fs/cifs/smb2ops.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3110,6 +3110,46 @@ struct smb_version_values smb21_values = {
31103110
.create_lease_size = sizeof(struct create_lease),
31113111
};
31123112

3113+
struct smb_version_values smb3any_values = {
3114+
.version_string = SMB3ANY_VERSION_STRING,
3115+
.protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */
3116+
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
3117+
.large_lock_type = 0,
3118+
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
3119+
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
3120+
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
3121+
.header_size = sizeof(struct smb2_hdr),
3122+
.max_header_size = MAX_SMB2_HDR_SIZE,
3123+
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
3124+
.lock_cmd = SMB2_LOCK,
3125+
.cap_unix = 0,
3126+
.cap_nt_find = SMB2_NT_FIND,
3127+
.cap_large_files = SMB2_LARGE_FILES,
3128+
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
3129+
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
3130+
.create_lease_size = sizeof(struct create_lease_v2),
3131+
};
3132+
3133+
struct smb_version_values smbdefault_values = {
3134+
.version_string = SMBDEFAULT_VERSION_STRING,
3135+
.protocol_id = SMB302_PROT_ID, /* doesn't matter, send protocol array */
3136+
.req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU | SMB2_GLOBAL_CAP_PERSISTENT_HANDLES | SMB2_GLOBAL_CAP_ENCRYPTION,
3137+
.large_lock_type = 0,
3138+
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
3139+
.shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
3140+
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
3141+
.header_size = sizeof(struct smb2_hdr),
3142+
.max_header_size = MAX_SMB2_HDR_SIZE,
3143+
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
3144+
.lock_cmd = SMB2_LOCK,
3145+
.cap_unix = 0,
3146+
.cap_nt_find = SMB2_NT_FIND,
3147+
.cap_large_files = SMB2_LARGE_FILES,
3148+
.signing_enabled = SMB2_NEGOTIATE_SIGNING_ENABLED | SMB2_NEGOTIATE_SIGNING_REQUIRED,
3149+
.signing_required = SMB2_NEGOTIATE_SIGNING_REQUIRED,
3150+
.create_lease_size = sizeof(struct create_lease_v2),
3151+
};
3152+
31133153
struct smb_version_values smb30_values = {
31143154
.version_string = SMB30_VERSION_STRING,
31153155
.protocol_id = SMB30_PROT_ID,

fs/cifs/smb2pdu.c

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -491,10 +491,25 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
491491

492492
req->hdr.sync_hdr.SessionId = 0;
493493

494-
req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
495-
496-
req->DialectCount = cpu_to_le16(1); /* One vers= at a time for now */
497-
inc_rfc1001_len(req, 2);
494+
if (strcmp(ses->server->vals->version_string,
495+
SMB3ANY_VERSION_STRING) == 0) {
496+
req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
497+
req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
498+
req->DialectCount = cpu_to_le16(2);
499+
inc_rfc1001_len(req, 4);
500+
} else if (strcmp(ses->server->vals->version_string,
501+
SMBDEFAULT_VERSION_STRING) == 0) {
502+
req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
503+
req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
504+
req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
505+
req->DialectCount = cpu_to_le16(3);
506+
inc_rfc1001_len(req, 6);
507+
} else {
508+
/* otherwise send specific dialect */
509+
req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
510+
req->DialectCount = cpu_to_le16(1);
511+
inc_rfc1001_len(req, 2);
512+
}
498513

499514
/* only one of SMB2 signing flags may be set in SMB2 request */
500515
if (ses->sign)
@@ -528,16 +543,42 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
528543
*/
529544
if (rc == -EOPNOTSUPP) {
530545
cifs_dbg(VFS, "Dialect not supported by server. Consider "
531-
"specifying vers=1.0 or vers=2.1 on mount for accessing"
546+
"specifying vers=1.0 or vers=2.0 on mount for accessing"
532547
" older servers\n");
533548
goto neg_exit;
534549
} else if (rc != 0)
535550
goto neg_exit;
536551

552+
if (strcmp(ses->server->vals->version_string,
553+
SMB3ANY_VERSION_STRING) == 0) {
554+
if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
555+
cifs_dbg(VFS,
556+
"SMB2 dialect returned but not requested\n");
557+
return -EIO;
558+
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
559+
cifs_dbg(VFS,
560+
"SMB2.1 dialect returned but not requested\n");
561+
return -EIO;
562+
}
563+
} else if (strcmp(ses->server->vals->version_string,
564+
SMBDEFAULT_VERSION_STRING) == 0) {
565+
if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
566+
cifs_dbg(VFS,
567+
"SMB2 dialect returned but not requested\n");
568+
return -EIO;
569+
} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
570+
/* ops set to 3.0 by default for default so update */
571+
ses->server->ops = &smb21_operations;
572+
}
573+
} else if (rsp->DialectRevision != ses->server->vals->protocol_id) {
574+
/* if requested single dialect ensure returned dialect matched */
575+
cifs_dbg(VFS, "Illegal 0x%x dialect returned: not requested\n",
576+
cpu_to_le16(rsp->DialectRevision));
577+
return -EIO;
578+
}
579+
537580
cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
538581

539-
/* BB we may eventually want to match the negotiated vs. requested
540-
dialect, even though we are only requesting one at a time */
541582
if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
542583
cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
543584
else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
@@ -558,6 +599,8 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
558599
}
559600
server->dialect = le16_to_cpu(rsp->DialectRevision);
560601

602+
/* BB: add check that dialect was valid given dialect(s) we asked for */
603+
561604
/* SMB2 only has an extended negflavor */
562605
server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
563606
/* set it to the maximum buffer size value we can send with 1 credit */
@@ -606,6 +649,7 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
606649
struct validate_negotiate_info_req vneg_inbuf;
607650
struct validate_negotiate_info_rsp *pneg_rsp;
608651
u32 rsplen;
652+
u32 inbuflen; /* max of 4 dialects */
609653

610654
cifs_dbg(FYI, "validate negotiate\n");
611655

@@ -634,9 +678,30 @@ int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
634678
else
635679
vneg_inbuf.SecurityMode = 0;
636680

637-
vneg_inbuf.DialectCount = cpu_to_le16(1);
638-
vneg_inbuf.Dialects[0] =
639-
cpu_to_le16(tcon->ses->server->vals->protocol_id);
681+
682+
if (strcmp(tcon->ses->server->vals->version_string,
683+
SMB3ANY_VERSION_STRING) == 0) {
684+
vneg_inbuf.Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
685+
vneg_inbuf.Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
686+
vneg_inbuf.DialectCount = cpu_to_le16(2);
687+
/* structure is big enough for 3 dialects, sending only 2 */
688+
inbuflen = sizeof(struct validate_negotiate_info_req) - 2;
689+
} else if (strcmp(tcon->ses->server->vals->version_string,
690+
SMBDEFAULT_VERSION_STRING) == 0) {
691+
vneg_inbuf.Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
692+
vneg_inbuf.Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
693+
vneg_inbuf.Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
694+
vneg_inbuf.DialectCount = cpu_to_le16(3);
695+
/* structure is big enough for 3 dialects */
696+
inbuflen = sizeof(struct validate_negotiate_info_req);
697+
} else {
698+
/* otherwise specific dialect was requested */
699+
vneg_inbuf.Dialects[0] =
700+
cpu_to_le16(tcon->ses->server->vals->protocol_id);
701+
vneg_inbuf.DialectCount = cpu_to_le16(1);
702+
/* structure is big enough for 3 dialects, sending only 1 */
703+
inbuflen = sizeof(struct validate_negotiate_info_req) - 4;
704+
}
640705

641706
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
642707
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,

fs/cifs/smb2pdu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ struct validate_negotiate_info_req {
716716
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
717717
__le16 SecurityMode;
718718
__le16 DialectCount;
719-
__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
719+
__le16 Dialects[3]; /* BB expand this if autonegotiate > 3 dialects */
720720
} __packed;
721721

722722
struct validate_negotiate_info_rsp {

0 commit comments

Comments
 (0)