Merge tag '4.17-rc2-smb3' of git://git.samba.org/sfrench/cifs-2.6
Pull cifs fixes from Steve French: "A few security related fixes for SMB3, most importantly for SMB3.11 encryption" * tag '4.17-rc2-smb3' of git://git.samba.org/sfrench/cifs-2.6: cifs: smbd: Avoid allocating iov on the stack cifs: smbd: Don't use RDMA read/write when signing is used SMB311: Fix reconnect SMB3: Fix 3.11 encryption to Windows and handle encrypted smb3 tcon CIFS: set *resp_buf_type to NO_BUFFER on error
This commit is contained in:
@@ -455,6 +455,9 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
|
|||||||
server->sign = true;
|
server->sign = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cifs_rdma_enabled(server) && server->sign)
|
||||||
|
cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2959,6 +2959,22 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (volume_info->seal) {
|
||||||
|
if (ses->server->vals->protocol_id == 0) {
|
||||||
|
cifs_dbg(VFS,
|
||||||
|
"SMB3 or later required for encryption\n");
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
|
goto out_fail;
|
||||||
|
} else if (tcon->ses->server->capabilities &
|
||||||
|
SMB2_GLOBAL_CAP_ENCRYPTION)
|
||||||
|
tcon->seal = true;
|
||||||
|
else {
|
||||||
|
cifs_dbg(VFS, "Encryption is not supported on share\n");
|
||||||
|
rc = -EOPNOTSUPP;
|
||||||
|
goto out_fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BB Do we need to wrap session_mutex around this TCon call and Unix
|
* BB Do we need to wrap session_mutex around this TCon call and Unix
|
||||||
* SetFS as we do on SessSetup and reconnect?
|
* SetFS as we do on SessSetup and reconnect?
|
||||||
@@ -3007,22 +3023,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
|
|||||||
tcon->use_resilient = true;
|
tcon->use_resilient = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volume_info->seal) {
|
|
||||||
if (ses->server->vals->protocol_id == 0) {
|
|
||||||
cifs_dbg(VFS,
|
|
||||||
"SMB3 or later required for encryption\n");
|
|
||||||
rc = -EOPNOTSUPP;
|
|
||||||
goto out_fail;
|
|
||||||
} else if (tcon->ses->server->capabilities &
|
|
||||||
SMB2_GLOBAL_CAP_ENCRYPTION)
|
|
||||||
tcon->seal = true;
|
|
||||||
else {
|
|
||||||
cifs_dbg(VFS, "Encryption is not supported on share\n");
|
|
||||||
rc = -EOPNOTSUPP;
|
|
||||||
goto out_fail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We can have only one retry value for a connection to a share so for
|
* We can have only one retry value for a connection to a share so for
|
||||||
* resources mounted more than once to the same server share the last
|
* resources mounted more than once to the same server share the last
|
||||||
|
@@ -252,9 +252,14 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
|||||||
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
|
wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
|
||||||
wsize = min_t(unsigned int, wsize, server->max_write);
|
wsize = min_t(unsigned int, wsize, server->max_write);
|
||||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||||
if (server->rdma)
|
if (server->rdma) {
|
||||||
wsize = min_t(unsigned int,
|
if (server->sign)
|
||||||
|
wsize = min_t(unsigned int,
|
||||||
|
wsize, server->smbd_conn->max_fragmented_send_size);
|
||||||
|
else
|
||||||
|
wsize = min_t(unsigned int,
|
||||||
wsize, server->smbd_conn->max_readwrite_size);
|
wsize, server->smbd_conn->max_readwrite_size);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
||||||
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
|
wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);
|
||||||
@@ -272,9 +277,14 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
|
|||||||
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
|
rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
|
||||||
rsize = min_t(unsigned int, rsize, server->max_read);
|
rsize = min_t(unsigned int, rsize, server->max_read);
|
||||||
#ifdef CONFIG_CIFS_SMB_DIRECT
|
#ifdef CONFIG_CIFS_SMB_DIRECT
|
||||||
if (server->rdma)
|
if (server->rdma) {
|
||||||
rsize = min_t(unsigned int,
|
if (server->sign)
|
||||||
|
rsize = min_t(unsigned int,
|
||||||
|
rsize, server->smbd_conn->max_fragmented_recv_size);
|
||||||
|
else
|
||||||
|
rsize = min_t(unsigned int,
|
||||||
rsize, server->smbd_conn->max_readwrite_size);
|
rsize, server->smbd_conn->max_readwrite_size);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
if (!(server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
|
||||||
|
@@ -383,10 +383,10 @@ static void
|
|||||||
build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
|
build_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
|
||||||
{
|
{
|
||||||
pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
|
pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
|
||||||
pneg_ctxt->DataLength = cpu_to_le16(6);
|
pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + le16 cipher */
|
||||||
pneg_ctxt->CipherCount = cpu_to_le16(2);
|
pneg_ctxt->CipherCount = cpu_to_le16(1);
|
||||||
pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;
|
/* pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;*/ /* not supported yet */
|
||||||
pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM;
|
pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_CCM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -444,6 +444,7 @@ static int decode_encrypt_ctx(struct TCP_Server_Info *server,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
server->cipher_type = ctxt->Ciphers[0];
|
server->cipher_type = ctxt->Ciphers[0];
|
||||||
|
server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2590,7 +2591,7 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
|
|||||||
* If we want to do a RDMA write, fill in and append
|
* If we want to do a RDMA write, fill in and append
|
||||||
* smbd_buffer_descriptor_v1 to the end of read request
|
* smbd_buffer_descriptor_v1 to the end of read request
|
||||||
*/
|
*/
|
||||||
if (server->rdma && rdata &&
|
if (server->rdma && rdata && !server->sign &&
|
||||||
rdata->bytes >= server->smbd_conn->rdma_readwrite_threshold) {
|
rdata->bytes >= server->smbd_conn->rdma_readwrite_threshold) {
|
||||||
|
|
||||||
struct smbd_buffer_descriptor_v1 *v1;
|
struct smbd_buffer_descriptor_v1 *v1;
|
||||||
@@ -2968,7 +2969,7 @@ smb2_async_writev(struct cifs_writedata *wdata,
|
|||||||
* If we want to do a server RDMA read, fill in and append
|
* If we want to do a server RDMA read, fill in and append
|
||||||
* smbd_buffer_descriptor_v1 to the end of write request
|
* smbd_buffer_descriptor_v1 to the end of write request
|
||||||
*/
|
*/
|
||||||
if (server->rdma && wdata->bytes >=
|
if (server->rdma && !server->sign && wdata->bytes >=
|
||||||
server->smbd_conn->rdma_readwrite_threshold) {
|
server->smbd_conn->rdma_readwrite_threshold) {
|
||||||
|
|
||||||
struct smbd_buffer_descriptor_v1 *v1;
|
struct smbd_buffer_descriptor_v1 *v1;
|
||||||
|
@@ -297,7 +297,7 @@ struct smb2_encryption_neg_context {
|
|||||||
__le16 DataLength;
|
__le16 DataLength;
|
||||||
__le32 Reserved;
|
__le32 Reserved;
|
||||||
__le16 CipherCount; /* AES-128-GCM and AES-128-CCM */
|
__le16 CipherCount; /* AES-128-GCM and AES-128-CCM */
|
||||||
__le16 Ciphers[2]; /* Ciphers[0] since only one used now */
|
__le16 Ciphers[1]; /* Ciphers[0] since only one used now */
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
struct smb2_negotiate_rsp {
|
struct smb2_negotiate_rsp {
|
||||||
|
@@ -2086,7 +2086,7 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
|
|||||||
int start, i, j;
|
int start, i, j;
|
||||||
int max_iov_size =
|
int max_iov_size =
|
||||||
info->max_send_size - sizeof(struct smbd_data_transfer);
|
info->max_send_size - sizeof(struct smbd_data_transfer);
|
||||||
struct kvec iov[SMBDIRECT_MAX_SGE];
|
struct kvec *iov;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
info->smbd_send_pending++;
|
info->smbd_send_pending++;
|
||||||
@@ -2096,32 +2096,20 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This usually means a configuration error
|
* Skip the RFC1002 length defined in MS-SMB2 section 2.1
|
||||||
* We use RDMA read/write for packet size > rdma_readwrite_threshold
|
* It is used only for TCP transport in the iov[0]
|
||||||
* as long as it's properly configured we should never get into this
|
|
||||||
* situation
|
|
||||||
*/
|
|
||||||
if (rqst->rq_nvec + rqst->rq_npages > SMBDIRECT_MAX_SGE) {
|
|
||||||
log_write(ERR, "maximum send segment %x exceeding %x\n",
|
|
||||||
rqst->rq_nvec + rqst->rq_npages, SMBDIRECT_MAX_SGE);
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Remove the RFC1002 length defined in MS-SMB2 section 2.1
|
|
||||||
* It is used only for TCP transport
|
|
||||||
* In future we may want to add a transport layer under protocol
|
* In future we may want to add a transport layer under protocol
|
||||||
* layer so this will only be issued to TCP transport
|
* layer so this will only be issued to TCP transport
|
||||||
*/
|
*/
|
||||||
iov[0].iov_base = (char *)rqst->rq_iov[0].iov_base + 4;
|
|
||||||
iov[0].iov_len = rqst->rq_iov[0].iov_len - 4;
|
if (rqst->rq_iov[0].iov_len != 4) {
|
||||||
buflen += iov[0].iov_len;
|
log_write(ERR, "expected the pdu length in 1st iov, but got %zu\n", rqst->rq_iov[0].iov_len);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
iov = &rqst->rq_iov[1];
|
||||||
|
|
||||||
/* total up iov array first */
|
/* total up iov array first */
|
||||||
for (i = 1; i < rqst->rq_nvec; i++) {
|
for (i = 0; i < rqst->rq_nvec-1; i++) {
|
||||||
iov[i].iov_base = rqst->rq_iov[i].iov_base;
|
|
||||||
iov[i].iov_len = rqst->rq_iov[i].iov_len;
|
|
||||||
buflen += iov[i].iov_len;
|
buflen += iov[i].iov_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2198,14 +2186,14 @@ int smbd_send(struct smbd_connection *info, struct smb_rqst *rqst)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
if (i == rqst->rq_nvec)
|
if (i == rqst->rq_nvec-1)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
start = i;
|
start = i;
|
||||||
buflen = 0;
|
buflen = 0;
|
||||||
} else {
|
} else {
|
||||||
i++;
|
i++;
|
||||||
if (i == rqst->rq_nvec) {
|
if (i == rqst->rq_nvec-1) {
|
||||||
/* send out all remaining vecs */
|
/* send out all remaining vecs */
|
||||||
remaining_data_length -= buflen;
|
remaining_data_length -= buflen;
|
||||||
log_write(INFO,
|
log_write(INFO,
|
||||||
|
@@ -753,7 +753,7 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_SMB311
|
#ifdef CONFIG_CIFS_SMB311
|
||||||
if (ses->status == CifsNew)
|
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP))
|
||||||
smb311_update_preauth_hash(ses, rqst->rq_iov+1,
|
smb311_update_preauth_hash(ses, rqst->rq_iov+1,
|
||||||
rqst->rq_nvec-1);
|
rqst->rq_nvec-1);
|
||||||
#endif
|
#endif
|
||||||
@@ -798,7 +798,7 @@ cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
*resp_buf_type = CIFS_SMALL_BUFFER;
|
*resp_buf_type = CIFS_SMALL_BUFFER;
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_SMB311
|
#ifdef CONFIG_CIFS_SMB311
|
||||||
if (ses->status == CifsNew) {
|
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP)) {
|
||||||
struct kvec iov = {
|
struct kvec iov = {
|
||||||
.iov_base = buf + 4,
|
.iov_base = buf + 4,
|
||||||
.iov_len = get_rfc1002_length(buf)
|
.iov_len = get_rfc1002_length(buf)
|
||||||
@@ -834,8 +834,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
|
|||||||
if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
|
if (n_vec + 1 > CIFS_MAX_IOV_SIZE) {
|
||||||
new_iov = kmalloc(sizeof(struct kvec) * (n_vec + 1),
|
new_iov = kmalloc(sizeof(struct kvec) * (n_vec + 1),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!new_iov)
|
if (!new_iov) {
|
||||||
|
/* otherwise cifs_send_recv below sets resp_buf_type */
|
||||||
|
*resp_buf_type = CIFS_NO_BUFFER;
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
new_iov = s_iov;
|
new_iov = s_iov;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user