Check SMB3 dialects against downgrade attacks
When we are running SMB3 or SMB3.02 connections which are signed we need to validate the protocol negotiation information, to ensure that the negotiate protocol response was not tampered with. Add the missing FSCTL which is sent at mount time (immediately after the SMB3 Tree Connect) to validate that the capabilities match what we think the server sent. "Secure dialect negotiation is introduced in SMB3 to protect against man-in-the-middle attempt to downgrade dialect negotiation. The idea is to prevent an eavesdropper from downgrading the initially negotiated dialect and capabilities between the client and the server." For more explanation see 2.2.31.4 of MS-SMB2 or http://blogs.msdn.com/b/openspecification/archive/2012/06/28/smb3-secure-dialect-negotiation.aspx Reviewed-by: Pavel Shilovsky <piastry@etersoft.ru> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:
Родитель
7d3fb24bce
Коммит
ff1c038add
|
@ -384,6 +384,7 @@ struct smb_version_operations {
|
|||
int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file,
|
||||
struct cifsFileInfo *target_file, u64 src_off, u64 len,
|
||||
u64 dest_off);
|
||||
int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
|
||||
};
|
||||
|
||||
struct smb_version_values {
|
||||
|
|
|
@ -1319,6 +1319,7 @@ struct smb_version_operations smb30_operations = {
|
|||
.create_lease_buf = smb3_create_lease_buf,
|
||||
.parse_lease_buf = smb3_parse_lease_buf,
|
||||
.clone_range = smb2_clone_range,
|
||||
.validate_negotiate = smb3_validate_negotiate,
|
||||
};
|
||||
|
||||
struct smb_version_values smb20_values = {
|
||||
|
|
|
@ -454,6 +454,81 @@ neg_exit:
|
|||
return rc;
|
||||
}
|
||||
|
||||
int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
|
||||
{
|
||||
int rc = 0;
|
||||
struct validate_negotiate_info_req vneg_inbuf;
|
||||
struct validate_negotiate_info_rsp *pneg_rsp;
|
||||
u32 rsplen;
|
||||
|
||||
cifs_dbg(FYI, "validate negotiate\n");
|
||||
|
||||
/*
|
||||
* validation ioctl must be signed, so no point sending this if we
|
||||
* can not sign it. We could eventually change this to selectively
|
||||
* sign just this, the first and only signed request on a connection.
|
||||
* This is good enough for now since a user who wants better security
|
||||
* would also enable signing on the mount. Having validation of
|
||||
* negotiate info for signed connections helps reduce attack vectors
|
||||
*/
|
||||
if (tcon->ses->server->sign == false)
|
||||
return 0; /* validation requires signing */
|
||||
|
||||
vneg_inbuf.Capabilities =
|
||||
cpu_to_le32(tcon->ses->server->vals->req_capabilities);
|
||||
memcpy(vneg_inbuf.Guid, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
|
||||
|
||||
if (tcon->ses->sign)
|
||||
vneg_inbuf.SecurityMode =
|
||||
cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
|
||||
else if (global_secflags & CIFSSEC_MAY_SIGN)
|
||||
vneg_inbuf.SecurityMode =
|
||||
cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
|
||||
else
|
||||
vneg_inbuf.SecurityMode = 0;
|
||||
|
||||
vneg_inbuf.DialectCount = cpu_to_le16(1);
|
||||
vneg_inbuf.Dialects[0] =
|
||||
cpu_to_le16(tcon->ses->server->vals->protocol_id);
|
||||
|
||||
rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
|
||||
FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */,
|
||||
(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req),
|
||||
(char **)&pneg_rsp, &rsplen);
|
||||
|
||||
if (rc != 0) {
|
||||
cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (rsplen != sizeof(struct validate_negotiate_info_rsp)) {
|
||||
cifs_dbg(VFS, "invalid size of protocol negotiate response\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* check validate negotiate info response matches what we got earlier */
|
||||
if (pneg_rsp->Dialect !=
|
||||
cpu_to_le16(tcon->ses->server->vals->protocol_id))
|
||||
goto vneg_out;
|
||||
|
||||
if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode))
|
||||
goto vneg_out;
|
||||
|
||||
/* do not validate server guid because not saved at negprot time yet */
|
||||
|
||||
if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
|
||||
SMB2_LARGE_FILES) != tcon->ses->server->capabilities)
|
||||
goto vneg_out;
|
||||
|
||||
/* validate negotiate successful */
|
||||
cifs_dbg(FYI, "validate negotiate info successful\n");
|
||||
return 0;
|
||||
|
||||
vneg_out:
|
||||
cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int
|
||||
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
|
||||
const struct nls_table *nls_cp)
|
||||
|
@ -829,6 +904,8 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
|||
((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
|
||||
cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
|
||||
init_copy_chunk_defaults(tcon);
|
||||
if (tcon->ses->server->ops->validate_negotiate)
|
||||
rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);
|
||||
tcon_exit:
|
||||
free_rsp_buf(resp_buftype, rsp);
|
||||
kfree(unc_path);
|
||||
|
|
|
@ -577,13 +577,19 @@ struct copychunk_ioctl_rsp {
|
|||
__le32 TotalBytesWritten;
|
||||
} __packed;
|
||||
|
||||
/* Response and Request are the same format */
|
||||
struct validate_negotiate_info {
|
||||
struct validate_negotiate_info_req {
|
||||
__le32 Capabilities;
|
||||
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
|
||||
__le16 SecurityMode;
|
||||
__le16 DialectCount;
|
||||
__le16 Dialect[1];
|
||||
__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */
|
||||
} __packed;
|
||||
|
||||
struct validate_negotiate_info_rsp {
|
||||
__le32 Capabilities;
|
||||
__u8 Guid[SMB2_CLIENT_GUID_SIZE];
|
||||
__le16 SecurityMode;
|
||||
__le16 Dialect; /* Dialect in use for the connection */
|
||||
} __packed;
|
||||
|
||||
#define RSS_CAPABLE 0x00000001
|
||||
|
|
|
@ -162,5 +162,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
struct smb2_lock_element *buf);
|
||||
extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u8 *lease_key, const __le32 lease_state);
|
||||
extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);
|
||||
|
||||
#endif /* _SMB2PROTO_H */
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
#define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */
|
||||
#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
|
||||
#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
|
||||
#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */
|
||||
#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
|
||||
/* Perform server-side data movement */
|
||||
#define FSCTL_SRV_COPYCHUNK 0x001440F2
|
||||
#define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2
|
||||
|
|
Загрузка…
Ссылка в новой задаче