mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
- Validate the offsets and lengths a server puts in its responses before
using them, so that a malicious or broken server can no longer make the
client read past the received data: POSIX lock data (which leaked
kernel bytes through F_GETLK), NT transaction responses and SMB1 EA
lists (both wrapping their pointer checks on 32-bit), short
SESSION_SETUP responses (which sent stale buffer content back to the
server in the NTLMv2 AUTHENTICATE message), SMB2 READ data areas and
empty truncated fiemap responses
- Fix a heap overflow in SMB2_tcon() with UNC paths longer than
MAX_SHARENAME_LENGTH
- Fix a slab-out-of-bounds write when chmod() rewrites a security
descriptor that needs more room than was reserved
- Fix a use-after-free of the server info between the cifsd thread and
the release of the last session reference (reported by syzbot)
- Fix a use-after-free in the demultiplex thread when a receive error
reconnects and frees the read request still being processed
- Fix a double teardown of a tcon that the cached directory laundromat
took a reference on while it was being freed
- Fix a NULL pointer dereference when /proc/fs/cifs/DebugData is read
while the module is still initializing
-----BEGIN PGP SIGNATURE-----
iHUEABYKAB0WIQTcqRusfSdYROJQwGkpVtNKoQNdYwUCauUmHQAKCRApVtNKoQNd
YwbQAPsHdFjf5JOXEs1QNEf9GWYT47vHAnXt4ADbFwSokfLB3QEAqI1W7P6KJ3HO
6ckrTF9VyZ75ZaStkESSlqsuYRwpiQs=
=GLt9
-----END PGP SIGNATURE-----
Merge tag 'cifs-fixes-7.4-rc1' of https://git.manguebit.org/linux
Pull smb client fixes from Paulo Alcantara:
- Validate the offsets and lengths a server puts in its responses
before using them, so that a malicious or broken server can no longer
make the client read past the received data:
- POSIX lock data (which leaked kernel bytes through F_GETLK)
- NT transaction responses and SMB1 EA lists (both wrapping their
pointer checks on 32-bit)
- short SESSION_SETUP responses (which sent stale buffer content
back to the server in the NTLMv2 AUTHENTICATE message)
- SMB2 READ data areas
- empty truncated fiemap responses
- Fix a heap overflow in SMB2_tcon() with UNC paths longer than
MAX_SHARENAME_LENGTH
- Fix a slab-out-of-bounds write when chmod() rewrites a security
descriptor that needs more room than was reserved
- Fix a use-after-free of the server info between the cifsd thread and
the release of the last session reference (reported by syzbot)
- Fix a use-after-free in the demultiplex thread when a receive error
reconnects and frees the read request still being processed
- Fix a double teardown of a tcon that the cached directory laundromat
took a reference on while it was being freed
- Fix a NULL pointer dereference when /proc/fs/cifs/DebugData is read
while the module is still initializing
* tag 'cifs-fixes-7.4-rc1' of https://git.manguebit.org/linux:
smb: client: validate SMB1 EA list bounds before parsing
smb: client: stop processing a read response after a receive error
smb: client: don't resurrect an exiting tcon from cached dir workers
smb: client: fix early procfs exposure race
smb: client: fix slab-out-of-bounds write in id_mode_to_cifs_acl()
smb: client: hold server info until cifsd exits
smb: client: fix fiemap wild read on empty truncated response
smb: client: validate the data area in SMB2_read()
smb: client: reject short SESSION_SETUP responses
smb: client: fix buffer overflow in SMB2_tcon()
smb: client: validate NT transaction response layout and offsets
smb: client: bound POSIX lock data area by response length
This commit is contained in:
commit
51b4bf5fe7
|
|
@ -649,6 +649,17 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync)
|
|||
flush_delayed_work(&cfids->laundromat_work);
|
||||
}
|
||||
|
||||
static bool cached_dir_get_tcon(struct cifs_tcon *tcon,
|
||||
enum smb3_tcon_ref_trace trace)
|
||||
{
|
||||
guard(spinlock)(&tcon->tc_lock);
|
||||
if (tcon->status == TID_EXITING || tcon->tc_count <= 0)
|
||||
return false;
|
||||
++tcon->tc_count;
|
||||
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, trace);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
cached_dir_offload_close(struct work_struct *work)
|
||||
{
|
||||
|
|
@ -693,6 +704,9 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
|
|||
!memcmp(lease_key,
|
||||
cfid->fid.lease_key,
|
||||
SMB2_LEASE_KEY_SIZE)) {
|
||||
if (!cached_dir_get_tcon(tcon,
|
||||
netfs_trace_tcon_ref_get_cached_lease_break))
|
||||
break;
|
||||
cfid->has_lease = false;
|
||||
cfid->time = 0;
|
||||
/*
|
||||
|
|
@ -703,9 +717,6 @@ bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
|
|||
cfid->on_list = false;
|
||||
cfids->num_entries--;
|
||||
|
||||
++tcon->tc_count;
|
||||
trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count,
|
||||
netfs_trace_tcon_ref_get_cached_lease_break);
|
||||
queue_work(cfid_put_wq, &cfid->put_work);
|
||||
spin_unlock(&cfids->cfid_list_lock);
|
||||
return true;
|
||||
|
|
@ -813,12 +824,13 @@ static void cfids_laundromat_worker(struct work_struct *work)
|
|||
cfid->dentry = NULL;
|
||||
|
||||
if (cfid->is_open) {
|
||||
spin_lock(&cfid->tcon->tc_lock);
|
||||
++cfid->tcon->tc_count;
|
||||
trace_smb3_tcon_ref(cfid->tcon->debug_id, cfid->tcon->tc_count,
|
||||
netfs_trace_tcon_ref_get_cached_laundromat);
|
||||
spin_unlock(&cfid->tcon->tc_lock);
|
||||
queue_work(serverclose_wq, &cfid->close_work);
|
||||
if (cached_dir_get_tcon(cfid->tcon,
|
||||
netfs_trace_tcon_ref_get_cached_laundromat)) {
|
||||
queue_work(serverclose_wq, &cfid->close_work);
|
||||
continue;
|
||||
}
|
||||
scoped_guard(spinlock, &cfids->cfid_list_lock)
|
||||
list_add_tail(&cfid->entry, &cfids->dying);
|
||||
} else
|
||||
/*
|
||||
* Drop the ref-count from above, either the lease-ref (if there
|
||||
|
|
|
|||
|
|
@ -1826,12 +1826,15 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
|||
|
||||
/* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
|
||||
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
|
||||
nsecdesclen = sizeof(struct smb_acl) +
|
||||
2 * sizeof(struct smb_sid);
|
||||
if (posix)
|
||||
nsecdesclen = 1 * sizeof(struct smb_ace);
|
||||
nsecdesclen += sizeof(struct smb_ntsd) +
|
||||
sizeof(struct smb_ace);
|
||||
else if (mode_from_sid)
|
||||
nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace));
|
||||
nsecdesclen += secdesclen + 2 * sizeof(struct smb_ace);
|
||||
else /* cifsacl */
|
||||
nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace));
|
||||
nsecdesclen += secdesclen + 5 * sizeof(struct smb_ace);
|
||||
} else { /* chown */
|
||||
/* When ownership changes, changes new owner sid length could be different */
|
||||
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
|
||||
|
|
|
|||
|
|
@ -2019,7 +2019,6 @@ init_cifs(void)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
cifs_proc_init();
|
||||
INIT_LIST_HEAD(&cifs_tcp_ses_list);
|
||||
/*
|
||||
* Initialize Global counters
|
||||
|
|
@ -2068,10 +2067,8 @@ init_cifs(void)
|
|||
cifsiod_wq = alloc_workqueue("cifsiod",
|
||||
WQ_FREEZABLE | WQ_MEM_RECLAIM | WQ_PERCPU,
|
||||
0);
|
||||
if (!cifsiod_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto out_clean_proc;
|
||||
}
|
||||
if (!cifsiod_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Consider in future setting limit!=0 maybe to min(num_of_cores - 1, 3)
|
||||
|
|
@ -2162,19 +2159,22 @@ init_cifs(void)
|
|||
if (rc)
|
||||
goto out_cifs_swn_init;
|
||||
|
||||
cifs_proc_init();
|
||||
|
||||
rc = register_filesystem(&cifs_fs_type);
|
||||
if (rc)
|
||||
goto out_init_cifs_idmap;
|
||||
goto out_clean_proc;
|
||||
|
||||
rc = register_filesystem(&smb3_fs_type);
|
||||
if (rc) {
|
||||
unregister_filesystem(&cifs_fs_type);
|
||||
goto out_init_cifs_idmap;
|
||||
goto out_clean_proc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_init_cifs_idmap:
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
exit_cifs_idmap();
|
||||
out_cifs_swn_init:
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
|
|
@ -2210,8 +2210,6 @@ init_cifs(void)
|
|||
destroy_workqueue(decrypt_wq);
|
||||
out_destroy_cifsiod_wq:
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -2221,6 +2219,7 @@ exit_cifs(void)
|
|||
cifs_dbg(NOISY, "exit_smb3\n");
|
||||
unregister_filesystem(&cifs_fs_type);
|
||||
unregister_filesystem(&smb3_fs_type);
|
||||
cifs_proc_clean();
|
||||
cifs_release_automount_timer();
|
||||
exit_cifs_idmap();
|
||||
#ifdef CONFIG_CIFS_SWN_UPCALL
|
||||
|
|
@ -2243,7 +2242,6 @@ exit_cifs(void)
|
|||
destroy_workqueue(serverclose_wq);
|
||||
destroy_workqueue(cfid_put_wq);
|
||||
destroy_workqueue(cifsiod_wq);
|
||||
cifs_proc_clean();
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Steve French");
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/scatterlist.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/mempool.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/utsname.h>
|
||||
#include <linux/sched/mm.h>
|
||||
|
|
@ -681,6 +682,7 @@ struct TCP_Server_Info {
|
|||
spinlock_t srv_lock; /* protect anything here that is not protected */
|
||||
__u64 conn_id; /* connection identifier (useful for debugging) */
|
||||
int srv_count; /* reference counter */
|
||||
refcount_t obj_refs; /* struct lifetime: srv_count users + cifsd */
|
||||
int rfc1001_sessinit; /* whether to estasblish netbios session */
|
||||
bool with_rfc1001; /* if netbios session is used */
|
||||
/* 15 character server name + 0x20 16th byte indicating type = srv */
|
||||
|
|
|
|||
|
|
@ -2369,6 +2369,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
cifs_dbg(FYI, "Send error in Posix Lock = %d\n", rc);
|
||||
} else if (pLockData) {
|
||||
/* lock structure can be returned on get */
|
||||
unsigned int rsp_len;
|
||||
__u16 data_offset;
|
||||
__u16 data_count;
|
||||
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
|
||||
|
|
@ -2380,11 +2381,18 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
}
|
||||
data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
|
||||
data_count = le16_to_cpu(pSMBr->t2.DataCount);
|
||||
rsp_len = smbCalcSize(&pSMBr->hdr);
|
||||
if (data_count < sizeof(struct cifs_posix_lock)) {
|
||||
rc = smb_EIO2(smb_eio_trace_lock_data_too_small,
|
||||
data_count, sizeof(struct cifs_posix_lock));
|
||||
goto plk_err_exit;
|
||||
}
|
||||
if (data_offset > rsp_len ||
|
||||
data_count > rsp_len - data_offset) {
|
||||
rc = smb_EIO2(smb_eio_trace_lock_data_area,
|
||||
data_offset, data_count);
|
||||
goto plk_err_exit;
|
||||
}
|
||||
parm_data = (struct cifs_posix_lock *)
|
||||
((char *)&pSMBr->hdr.Protocol + data_offset);
|
||||
if (parm_data->lock_type == cpu_to_le16(CIFS_UNLCK))
|
||||
|
|
@ -3779,9 +3787,10 @@ static int
|
|||
validate_ntransact(char *buf, char **ppparm, char **ppdata,
|
||||
__u32 *pparmlen, __u32 *pdatalen)
|
||||
{
|
||||
char *end_of_smb;
|
||||
__u32 data_count, data_offset, parm_count, parm_offset;
|
||||
struct smb_com_ntransact_rsp *pSMBr;
|
||||
size_t buflen;
|
||||
char *end_buf;
|
||||
u16 bcc;
|
||||
|
||||
*pdatalen = 0;
|
||||
|
|
@ -3792,37 +3801,42 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
|
|||
|
||||
pSMBr = (struct smb_com_ntransact_rsp *)buf;
|
||||
|
||||
if (pSMBr->hdr.WordCount != 18) {
|
||||
cifs_dbg(FYI, "invalid NT transaction WordCount %u\n",
|
||||
pSMBr->hdr.WordCount);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bcc = get_bcc(&pSMBr->hdr);
|
||||
end_of_smb = 2 /* sizeof byte count */ + bcc +
|
||||
(char *)&pSMBr->ByteCount;
|
||||
end_buf = 2 /* sizeof byte count */ + bcc + (char *)&pSMBr->ByteCount;
|
||||
buflen = end_buf - buf;
|
||||
|
||||
data_offset = le32_to_cpu(pSMBr->DataOffset);
|
||||
data_count = le32_to_cpu(pSMBr->DataCount);
|
||||
parm_offset = le32_to_cpu(pSMBr->ParameterOffset);
|
||||
parm_count = le32_to_cpu(pSMBr->ParameterCount);
|
||||
|
||||
*ppparm = (char *)&pSMBr->hdr.Protocol + parm_offset;
|
||||
*ppdata = (char *)&pSMBr->hdr.Protocol + data_offset;
|
||||
|
||||
/* should we also check that parm and data areas do not overlap? */
|
||||
if (*ppparm > end_of_smb) {
|
||||
if (parm_offset > buflen) {
|
||||
cifs_dbg(FYI, "parms start after end of smb\n");
|
||||
return -EINVAL;
|
||||
} else if (parm_count + *ppparm > end_of_smb) {
|
||||
} else if (parm_count > buflen - parm_offset) {
|
||||
cifs_dbg(FYI, "parm end after end of smb\n");
|
||||
return -EINVAL;
|
||||
} else if (*ppdata > end_of_smb) {
|
||||
} else if (data_offset > buflen) {
|
||||
cifs_dbg(FYI, "data starts after end of smb\n");
|
||||
return -EINVAL;
|
||||
} else if (data_count + *ppdata > end_of_smb) {
|
||||
cifs_dbg(FYI, "data %p + count %d (%p) past smb end %p start %p\n",
|
||||
*ppdata, data_count, (data_count + *ppdata),
|
||||
end_of_smb, pSMBr);
|
||||
} else if (data_count > buflen - data_offset) {
|
||||
cifs_dbg(FYI, "data offset %u + count %u past smb length %zu\n",
|
||||
data_offset, data_count, buflen);
|
||||
return -EINVAL;
|
||||
} else if (parm_count + data_count > bcc) {
|
||||
cifs_dbg(FYI, "parm count and data count larger than SMB\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*ppparm = buf + parm_offset;
|
||||
*ppdata = buf + data_offset;
|
||||
*pdatalen = data_count;
|
||||
*pparmlen = parm_count;
|
||||
return 0;
|
||||
|
|
@ -6201,9 +6215,11 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
int rc = 0;
|
||||
int bytes_returned;
|
||||
int list_len;
|
||||
u32 ea_list_len;
|
||||
struct fealist *ea_response_data;
|
||||
struct fea *temp_fea;
|
||||
char *temp_ptr;
|
||||
char *byte_area;
|
||||
char *end_of_smb;
|
||||
__u16 params, byte_count, data_offset;
|
||||
unsigned int ea_name_len = ea_name ? strlen(ea_name) : 0;
|
||||
|
|
@ -6282,9 +6298,19 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
ea_response_data = (struct fealist *)
|
||||
(((char *) &pSMBr->hdr.Protocol) + data_offset);
|
||||
|
||||
list_len = le32_to_cpu(ea_response_data->list_len);
|
||||
cifs_dbg(FYI, "ea length %d\n", list_len);
|
||||
if (list_len <= 8) {
|
||||
byte_area = pByteArea(&pSMBr->hdr);
|
||||
end_of_smb = byte_area + get_bcc(&pSMBr->hdr);
|
||||
if ((char *)ea_response_data < byte_area ||
|
||||
(char *)ea_response_data >
|
||||
end_of_smb - sizeof(ea_response_data->list_len)) {
|
||||
rc = smb_EIO2(smb_eio_trace_qalleas_overlong,
|
||||
data_offset, get_bcc(&pSMBr->hdr));
|
||||
goto QAllEAsOut;
|
||||
}
|
||||
|
||||
ea_list_len = le32_to_cpu(ea_response_data->list_len);
|
||||
cifs_dbg(FYI, "%s: ea length: %u\n", __func__, ea_list_len);
|
||||
if (ea_list_len <= 8) {
|
||||
cifs_dbg(FYI, "empty EA list returned from server\n");
|
||||
/* didn't find the named attribute */
|
||||
if (ea_name)
|
||||
|
|
@ -6293,14 +6319,14 @@ CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
|
|||
}
|
||||
|
||||
/* make sure list_len doesn't go past end of SMB */
|
||||
end_of_smb = (char *)pByteArea(&pSMBr->hdr) + get_bcc(&pSMBr->hdr);
|
||||
if ((char *)ea_response_data + list_len > end_of_smb) {
|
||||
if (ea_list_len > end_of_smb - (char *)ea_response_data) {
|
||||
cifs_dbg(FYI, "EA list appears to go beyond SMB\n");
|
||||
rc = smb_EIO2(smb_eio_trace_qalleas_overlong,
|
||||
(unsigned long)ea_response_data + list_len - (unsigned long)pSMBr,
|
||||
(unsigned long)end_of_smb - (unsigned long)pSMBr);
|
||||
ea_list_len,
|
||||
end_of_smb - (char *)ea_response_data);
|
||||
goto QAllEAsOut;
|
||||
}
|
||||
list_len = ea_list_len;
|
||||
|
||||
/* account for ea list len */
|
||||
list_len -= 4;
|
||||
|
|
|
|||
|
|
@ -1083,6 +1083,19 @@ cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void cifs_tcp_server_put(struct TCP_Server_Info *server)
|
||||
{
|
||||
if (!refcount_dec_and_test(&server->obj_refs))
|
||||
return;
|
||||
|
||||
cifs_crypto_secmech_release(server);
|
||||
kfree_sensitive(server->session_key.response);
|
||||
put_net(cifs_net_ns(server));
|
||||
kfree(server->leaf_fullpath);
|
||||
kfree(server->hostname);
|
||||
kfree_sensitive(server);
|
||||
}
|
||||
|
||||
static noinline_for_stack void
|
||||
clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
{
|
||||
|
|
@ -1168,14 +1181,11 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
|
|||
*/
|
||||
}
|
||||
|
||||
put_net(cifs_net_ns(server));
|
||||
kfree(server->leaf_fullpath);
|
||||
kfree(server->hostname);
|
||||
kfree_sensitive(server);
|
||||
|
||||
length = atomic_dec_return(&tcpSesAllocCount);
|
||||
if (length > 0)
|
||||
mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
|
||||
|
||||
cifs_tcp_server_put(server);
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -1751,7 +1761,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
|||
else
|
||||
cancel_delayed_work_sync(&server->reconnect);
|
||||
|
||||
/* For secondary channels, we pick up ref-count on the primary server */
|
||||
/* For secondary channels, drop the reference on the primary server */
|
||||
if (SERVER_IS_CHAN(server))
|
||||
cifs_put_tcp_session(server->primary_server, from_reconnect);
|
||||
|
||||
|
|
@ -1759,15 +1769,11 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
|||
server->tcpStatus = CifsExiting;
|
||||
spin_unlock(&server->srv_lock);
|
||||
|
||||
cifs_crypto_secmech_release(server);
|
||||
|
||||
kfree_sensitive(server->session_key.response);
|
||||
server->session_key.response = NULL;
|
||||
server->session_key.len = 0;
|
||||
|
||||
task = xchg(&server->tsk, NULL);
|
||||
if (task)
|
||||
send_sig(SIGKILL, task, 1);
|
||||
|
||||
cifs_tcp_server_put(server);
|
||||
}
|
||||
|
||||
struct TCP_Server_Info *
|
||||
|
|
@ -1789,6 +1795,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
|
|||
rc = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
refcount_set(&tcp_ses->obj_refs, 1);
|
||||
|
||||
tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL);
|
||||
if (!tcp_ses->hostname) {
|
||||
|
|
@ -1927,6 +1934,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
|
|||
module_put(THIS_MODULE);
|
||||
goto out_err_crypto_release;
|
||||
}
|
||||
/* Keep the server alive until the demultiplex thread has exited */
|
||||
refcount_inc(&tcp_ses->obj_refs);
|
||||
/* thread created, put it on the list */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
|
||||
|
|
|
|||
|
|
@ -225,6 +225,22 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
|
|||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* A SESSION_SETUP response that asks for more processing carries a
|
||||
* security blob and is parsed as a normal reply. Do not let the
|
||||
* short-error path below accept a frame that cannot hold the fixed
|
||||
* response fields; the minimum-PDU check later is unreachable once
|
||||
* the early return has accepted the frame.
|
||||
*/
|
||||
if (command == SMB2_SESSION_SETUP_HE &&
|
||||
shdr->Status == STATUS_MORE_PROCESSING_REQUIRED &&
|
||||
len < smb2_min_pdu_len[command]) {
|
||||
cifs_server_dbg(VFS,
|
||||
"%s: session setup response too short: %u < %zu\n",
|
||||
__func__, len, smb2_min_pdu_len[command]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (len < pdu_size) {
|
||||
if ((len >= hdr_size)
|
||||
&& (shdr->Status != 0)) {
|
||||
|
|
|
|||
|
|
@ -4360,6 +4360,15 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* A truncated range blob must contain at least one range, otherwise
|
||||
* there is no offset to resume from and out_data is NULL.
|
||||
*/
|
||||
if (!last_blob && out_data_len == 0) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
num = out_data_len / sizeof(struct file_allocated_range_buffer);
|
||||
for (i = 0; i < num; i++) {
|
||||
flags = 0;
|
||||
|
|
@ -4380,10 +4389,25 @@ static int smb3_fiemap(struct cifs_tcon *tcon,
|
|||
}
|
||||
|
||||
if (!last_blob) {
|
||||
next = le64_to_cpu(out_data[num - 1].file_offset) +
|
||||
le64_to_cpu(out_data[num - 1].length);
|
||||
len = len - (next - start);
|
||||
u64 last_start, last_len;
|
||||
|
||||
last_start = le64_to_cpu(out_data[num - 1].file_offset);
|
||||
last_len = le64_to_cpu(out_data[num - 1].length);
|
||||
|
||||
if (check_add_overflow(last_start, last_len, &next) ||
|
||||
next <= start) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (next - start >= len) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len -= next - start;
|
||||
start = next;
|
||||
kfree(out_data);
|
||||
out_data = NULL;
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1857,14 +1857,20 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
|
|||
goto out;
|
||||
|
||||
u16 boff = le16_to_cpu(rsp->SecurityBufferOffset);
|
||||
u16 blen = le16_to_cpu(rsp->SecurityBufferLength);
|
||||
|
||||
if (offsetof(struct smb2_sess_setup_rsp, Buffer) != boff) {
|
||||
cifs_dbg(VFS, "Invalid security buffer offset %d\n", boff);
|
||||
rc = smb_EIO1(smb_eio_trace_sess_buf_off, boff);
|
||||
goto out;
|
||||
}
|
||||
rc = decode_ntlmssp_challenge(rsp->Buffer,
|
||||
le16_to_cpu(rsp->SecurityBufferLength), ses);
|
||||
if ((u32)boff + blen > sess_data->iov[0].iov_len) {
|
||||
cifs_dbg(VFS, "%s: invalid security buffer length: %u\n",
|
||||
__func__, blen);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
rc = decode_ntlmssp_challenge(rsp->Buffer, blen, ses);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
|
@ -2104,8 +2110,6 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */)
|
||||
|
||||
/* These are similar values to what Windows uses */
|
||||
static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon)
|
||||
{
|
||||
|
|
@ -2135,7 +2139,15 @@ int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
|||
if (!server || !tree)
|
||||
return smb_EIO(smb_eio_trace_null_pointers);
|
||||
|
||||
unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
|
||||
total_len = strlen(tree);
|
||||
if (total_len > MAX_TREE_SIZE) {
|
||||
cifs_server_dbg(VFS | ONCE,
|
||||
"%s: tree name exceeded %u characters\n",
|
||||
__func__, MAX_TREE_SIZE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
unc_path = kmalloc((total_len + 1) * sizeof(*unc_path), GFP_KERNEL);
|
||||
if (unc_path == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -4936,6 +4948,7 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
|
|||
struct kvec iov[1];
|
||||
struct kvec rsp_iov;
|
||||
unsigned int total_len;
|
||||
u16 data_offset;
|
||||
int flags = CIFS_LOG_ERROR;
|
||||
struct cifs_ses *ses = io_parms->tcon->ses;
|
||||
|
||||
|
|
@ -4995,8 +5008,29 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
|
|||
*nbytes = 0;
|
||||
}
|
||||
|
||||
data_offset = rsp->DataOffset;
|
||||
if (data_offset < io_parms->server->vals->read_rsp_size) {
|
||||
/*
|
||||
* win2k8 sometimes sends an offset of 0 when the read is beyond
|
||||
* the EOF. Treat it as if the data starts just after the header,
|
||||
* as handle_read_data() does.
|
||||
*/
|
||||
cifs_dbg(FYI, "data offset %u inside read response header\n",
|
||||
data_offset);
|
||||
data_offset = io_parms->server->vals->read_rsp_size;
|
||||
}
|
||||
|
||||
/* The data area must lie within the response actually received */
|
||||
if (*nbytes && (size_t)data_offset + *nbytes > rsp_iov.iov_len) {
|
||||
cifs_dbg(FYI, "bad data offset %u length %u for response of %zu\n",
|
||||
data_offset, *nbytes, rsp_iov.iov_len);
|
||||
rc = smb_EIO2(smb_eio_trace_read_bad_offset,
|
||||
data_offset, *nbytes);
|
||||
*nbytes = 0;
|
||||
}
|
||||
|
||||
if (*buf) {
|
||||
memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes);
|
||||
memcpy(*buf, (char *)rsp + data_offset, *nbytes);
|
||||
free_rsp_buf(resp_buftype, rsp_iov.iov_base);
|
||||
} else if (resp_buftype != CIFS_NO_BUFFER) {
|
||||
*buf = rsp_iov.iov_base;
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
EM(smb_eio_trace_ioctl_no_rsp, "ioctl_no_rsp") \
|
||||
EM(smb_eio_trace_ioctl_out_off, "ioctl_out_off") \
|
||||
EM(smb_eio_trace_lock_bcc_too_small, "lock_bcc_too_small") \
|
||||
EM(smb_eio_trace_lock_data_area, "lock_data_area") \
|
||||
EM(smb_eio_trace_lock_data_too_small, "lock_data_too_small") \
|
||||
EM(smb_eio_trace_malformed_ksid_key, "malformed_ksid_key") \
|
||||
EM(smb_eio_trace_malformed_sid_key, "malformed_sid_key") \
|
||||
|
|
|
|||
|
|
@ -1345,6 +1345,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
|
|||
#endif
|
||||
length = cifs_read_iter_from_socket(server, &rdata->subreq.io_iter,
|
||||
data_len);
|
||||
if (length < 0)
|
||||
return length;
|
||||
if (length > 0)
|
||||
rdata->got_bytes += length;
|
||||
server->total_read += length;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user