16 smb3 client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmjf7TUACgkQiiy9cAdy
 T1EeoQv/cCbhCv5S+wM9mtqtJt3ea+0E4+fXc012198jgphgd39s5cV07flVj4vA
 P5gC0ZGQmTsCOhcmYzxOefUpyq5N+ckIY8PoQKEXyHf3e/0GqczkdDokkhB8eOkh
 aC/arAcQvYXzcR+RwLdkRa0W/kwoXJ6iQ4+44KS7U6AUzWOR3H21UAJ2tg96xNLk
 Qp4dKOG3i1RcBMMX2iuFKbF693XQPhoefs+/kPzWe2R99IIfJrFT7rLPhuwV+iJ7
 TIHgXk+UEUiDizLsSi9SPs59X0oOrQ2BlVI6mR0sUso94yQanItla9TTVTVTy2Tz
 tB7FUbF5x7a0Yaj3pVai5MXLRaKDEP38KWoM5mUsDyxDLblE5cHLtfxZzPwK8VO0
 oTVRDQu9DigOmfyiwJ9tu3O5/ZnAr0A5pJEYHHfue84xuBRb1mUPyzX+ML2yym7u
 8K+CUOKXS3M8x4Ep+88MukSX3PNEhJ1ohleyQghJHQVqrcA4tp289X+GJRuG9Q1b
 NiII7Tk0
 =4Nt1
 -----END PGP SIGNATURE-----

Merge tag '6.18-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client updates from Steve French:

 - Fix oops in crypt message

 - Remove duplicate arc4 code

 - Fix potential io_uring reconnect

 - Two important directory leases fixes and three perf improvements

 - Three minor cleanups

 - Four debug improvements (e.g. for showing more information on leases,
   and one for adding more helpful information on reconnect)

* tag '6.18-rc-part1-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: client: force multichannel=off when max_channels=1
  smb client: fix bug with newly created file in cached dir
  smb: client: short-circuit negative lookups when parent dir is fully cached
  smb: client: short-circuit in open_cached_dir_by_dentry() if !dentry
  smb: client: remove pointless cfid->has_lease check
  smb: client: transport: minor indentation style fix
  smb: client: transport: avoid reconnects triggered by pending task work
  smb: client: remove unused fid_lock
  smb: client: update cfid->last_access_time in open_cached_dir_by_dentry()
  smb: client: ensure open_cached_dir_by_dentry() only returns valid cfid
  smb: client: account smb directory cache usage and per-tcon totals
  smb: client: add drop_dir_cache module parameter to invalidate cached dirents
  smb: client: show lease state as R/H/W (or NONE) in open_files
  smb: client: fix crypto buffers in non-linear memory
  smb: Use arc4 library instead of duplicate arc4 code
  smb: client: add tcon information to smb2_reconnect() debug messages
This commit is contained in:
Linus Torvalds 2025-10-03 14:13:23 -07:00
commit 63e62baaa7
18 changed files with 236 additions and 177 deletions

View File

@ -15,6 +15,7 @@ config CIFS
select CRYPTO_GCM
select CRYPTO_ECB
select CRYPTO_AES
select CRYPTO_LIB_ARC4
select KEYS
select DNS_RESOLVER
select ASN1

View File

@ -36,9 +36,8 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
* fully cached or it may be in the process of
* being deleted due to a lease break.
*/
if (!cfid->time || !cfid->has_lease) {
if (!is_valid_cached_dir(cfid))
return NULL;
}
kref_get(&cfid->refcount);
return cfid;
}
@ -194,7 +193,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
* Otherwise, it is either a new entry or laundromat worker removed it
* from @cfids->entries. Caller will put last reference if the latter.
*/
if (cfid->has_lease && cfid->time) {
if (is_valid_cached_dir(cfid)) {
cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
*ret_cfid = cfid;
@ -233,7 +232,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
list_for_each_entry(parent_cfid, &cfids->entries, entry) {
if (parent_cfid->dentry == dentry->d_parent) {
cifs_dbg(FYI, "found a parent cached file handle\n");
if (parent_cfid->has_lease && parent_cfid->time) {
if (is_valid_cached_dir(parent_cfid)) {
lease_flags
|= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
memcpy(pfid->parent_lease_key,
@ -417,12 +416,18 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
if (cfids == NULL)
return -EOPNOTSUPP;
if (!dentry)
return -ENOENT;
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
if (dentry && cfid->dentry == dentry) {
if (cfid->dentry == dentry) {
if (!is_valid_cached_dir(cfid))
break;
cifs_dbg(FYI, "found a cached file handle by dentry\n");
kref_get(&cfid->refcount);
*ret_cfid = cfid;
cfid->last_access_time = jiffies;
spin_unlock(&cfids->cfid_list_lock);
return 0;
}
@ -522,10 +527,9 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
spin_unlock(&cifs_sb->tlink_tree_lock);
goto done;
}
spin_lock(&cfid->fid_lock);
tmp_list->dentry = cfid->dentry;
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
list_add_tail(&tmp_list->entry, &entry);
}
@ -608,14 +612,9 @@ static void cached_dir_put_work(struct work_struct *work)
{
struct cached_fid *cfid = container_of(work, struct cached_fid,
put_work);
struct dentry *dentry;
spin_lock(&cfid->fid_lock);
dentry = cfid->dentry;
dput(cfid->dentry);
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
dput(dentry);
queue_work(serverclose_wq, &cfid->close_work);
}
@ -673,7 +672,6 @@ static struct cached_fid *init_cached_dir(const char *path)
INIT_LIST_HEAD(&cfid->entry);
INIT_LIST_HEAD(&cfid->dirents.entries);
mutex_init(&cfid->dirents.de_mutex);
spin_lock_init(&cfid->fid_lock);
kref_init(&cfid->refcount);
return cfid;
}
@ -697,6 +695,21 @@ static void free_cached_dir(struct cached_fid *cfid)
kfree(dirent);
}
/* adjust tcon-level counters and reset per-dir accounting */
if (cfid->cfids) {
if (cfid->dirents.entries_count)
atomic_long_sub((long)cfid->dirents.entries_count,
&cfid->cfids->total_dirents_entries);
if (cfid->dirents.bytes_used) {
atomic64_sub((long long)cfid->dirents.bytes_used,
&cfid->cfids->total_dirents_bytes);
atomic64_sub((long long)cfid->dirents.bytes_used,
&cifs_dircache_bytes_used);
}
}
cfid->dirents.entries_count = 0;
cfid->dirents.bytes_used = 0;
kfree(cfid->path);
cfid->path = NULL;
kfree(cfid);
@ -725,7 +738,6 @@ static void cfids_laundromat_worker(struct work_struct *work)
{
struct cached_fids *cfids;
struct cached_fid *cfid, *q;
struct dentry *dentry;
LIST_HEAD(entry);
cfids = container_of(work, struct cached_fids, laundromat_work.work);
@ -752,12 +764,9 @@ static void cfids_laundromat_worker(struct work_struct *work)
list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
spin_lock(&cfid->fid_lock);
dentry = cfid->dentry;
dput(cfid->dentry);
cfid->dentry = NULL;
spin_unlock(&cfid->fid_lock);
dput(dentry);
if (cfid->is_open) {
spin_lock(&cifs_tcp_ses_lock);
++cfid->tcon->tc_count;
@ -792,6 +801,9 @@ struct cached_fids *init_cached_dirs(void)
queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
dir_cache_timeout * HZ);
atomic_long_set(&cfids->total_dirents_entries, 0);
atomic64_set(&cfids->total_dirents_bytes, 0);
return cfids;
}

View File

@ -27,6 +27,9 @@ struct cached_dirents {
struct mutex de_mutex;
loff_t pos; /* Expected ctx->pos */
struct list_head entries;
/* accounting for cached entries in this directory */
unsigned long entries_count;
unsigned long bytes_used;
};
struct cached_fid {
@ -41,7 +44,6 @@ struct cached_fid {
unsigned long last_access_time; /* jiffies of when last accessed */
struct kref refcount;
struct cifs_fid fid;
spinlock_t fid_lock;
struct cifs_tcon *tcon;
struct dentry *dentry;
struct work_struct put_work;
@ -62,8 +64,20 @@ struct cached_fids {
struct list_head dying;
struct work_struct invalidation_work;
struct delayed_work laundromat_work;
/* aggregate accounting for all cached dirents under this tcon */
atomic_long_t total_dirents_entries;
atomic64_t total_dirents_bytes;
};
/* Module-wide directory cache accounting (defined in cifsfs.c) */
extern atomic64_t cifs_dircache_bytes_used; /* bytes across all mounts */
static inline bool
is_valid_cached_dir(struct cached_fid *cfid)
{
return cfid->time && cfid->has_lease;
}
extern struct cached_fids *init_cached_dirs(void);
extern void free_cached_dirs(struct cached_fids *cfids);
extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,

View File

@ -240,14 +240,18 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct cifsFileInfo *cfile;
struct inode *inode;
struct cifsInodeInfo *cinode;
char lease[4];
int n;
seq_puts(m, "# Version:1\n");
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <ses id> <persistent fid> <flags> <count> <pid> <uid>");
#ifdef CONFIG_CIFS_DEBUG2
seq_printf(m, " <filename> <mid>\n");
seq_puts(m, " <filename> <lease> <mid>\n");
#else
seq_printf(m, " <filename>\n");
seq_puts(m, " <filename> <lease>\n");
#endif /* CIFS_DEBUG2 */
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
@ -267,11 +271,30 @@ static int cifs_debug_files_proc_show(struct seq_file *m, void *v)
cfile->pid,
from_kuid(&init_user_ns, cfile->uid),
cfile->dentry);
/* Append lease/oplock caching state as RHW letters */
inode = d_inode(cfile->dentry);
n = 0;
if (inode) {
cinode = CIFS_I(inode);
if (CIFS_CACHE_READ(cinode))
lease[n++] = 'R';
if (CIFS_CACHE_HANDLE(cinode))
lease[n++] = 'H';
if (CIFS_CACHE_WRITE(cinode))
lease[n++] = 'W';
}
lease[n] = '\0';
seq_puts(m, " ");
if (n)
seq_printf(m, "%s", lease);
else
seq_puts(m, "NONE");
#ifdef CONFIG_CIFS_DEBUG2
seq_printf(m, " %llu\n", cfile->fid.mid);
#else
seq_printf(m, " %llu", cfile->fid.mid);
#endif /* CONFIG_CIFS_DEBUG2 */
seq_printf(m, "\n");
#endif /* CIFS_DEBUG2 */
}
spin_unlock(&tcon->open_file_lock);
}
@ -308,7 +331,10 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
if (!cfids)
continue;
spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
seq_printf(m, "Num entries: %d\n", cfids->num_entries);
seq_printf(m, "Num entries: %d, cached_dirents: %lu entries, %llu bytes\n",
cfids->num_entries,
(unsigned long)atomic_long_read(&cfids->total_dirents_entries),
(unsigned long long)atomic64_read(&cfids->total_dirents_bytes));
list_for_each_entry(cfid, &cfids->entries, entry) {
seq_printf(m, "0x%x 0x%llx 0x%llx %s",
tcon->tid,
@ -319,6 +345,9 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\tvalid file info");
if (cfid->dirents.is_valid)
seq_printf(m, ", valid dirents");
if (!list_empty(&cfid->dirents.entries))
seq_printf(m, ", dirents: %lu entries, %lu bytes",
cfid->dirents.entries_count, cfid->dirents.bytes_used);
seq_printf(m, "\n");
}
spin_unlock(&cfids->cfid_list_lock);

View File

@ -22,8 +22,8 @@
#include <linux/highmem.h>
#include <linux/fips.h>
#include <linux/iov_iter.h>
#include "../common/arc4.h"
#include <crypto/aead.h>
#include <crypto/arc4.h>
static size_t cifs_shash_step(void *iter_base, size_t progress, size_t len,
void *priv, void *priv2)
@ -725,9 +725,9 @@ calc_seckey(struct cifs_ses *ses)
return -ENOMEM;
}
cifs_arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE);
cifs_arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key,
CIFS_CPHTXT_SIZE);
arc4_setkey(ctx_arc4, ses->auth_key.response, CIFS_SESS_KEY_SIZE);
arc4_crypt(ctx_arc4, ses->ntlmssp->ciphertext, sec_key,
CIFS_CPHTXT_SIZE);
/* make secondary_key/nonce as session key */
memcpy(ses->auth_key.response, sec_key, CIFS_SESS_KEY_SIZE);

View File

@ -121,6 +121,46 @@ unsigned int dir_cache_timeout = 30;
module_param(dir_cache_timeout, uint, 0644);
MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory contents for which we have a lease. Default: 30 "
"Range: 1 to 65000 seconds, 0 to disable caching dir contents");
/* Module-wide total cached dirents (in bytes) across all tcons */
atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
/*
* Write-only module parameter to drop all cached directory entries across
* all CIFS mounts. Echo a non-zero value to trigger.
*/
static void cifs_drop_all_dir_caches(void)
{
struct TCP_Server_Info *server;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
if (cifs_ses_exiting(ses))
continue;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
invalidate_all_cached_dirs(tcon);
}
}
spin_unlock(&cifs_tcp_ses_lock);
}
static int cifs_param_set_drop_dir_cache(const char *val, const struct kernel_param *kp)
{
bool bv;
int rc = kstrtobool(val, &bv);
if (rc)
return rc;
if (bv)
cifs_drop_all_dir_caches();
return 0;
}
module_param_call(drop_dir_cache, cifs_param_set_drop_dir_cache, NULL, NULL, 0200);
MODULE_PARM_DESC(drop_dir_cache, "Write 1 to drop all cached directory entries across all CIFS mounts");
#ifdef CONFIG_CIFS_STATS2
unsigned int slow_rsp_threshold = 1;
module_param(slow_rsp_threshold, uint, 0644);

View File

@ -322,13 +322,14 @@ static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned
list_for_each_entry(parent_cfid, &tcon->cfids->entries, entry) {
if (parent_cfid->dentry == direntry->d_parent) {
cifs_dbg(FYI, "found a parent cached file handle\n");
if (parent_cfid->has_lease && parent_cfid->time) {
if (is_valid_cached_dir(parent_cfid)) {
lease_flags
|= SMB2_LEASE_FLAG_PARENT_LEASE_KEY_SET_LE;
memcpy(fid->parent_lease_key,
parent_cfid->fid.lease_key,
SMB2_LEASE_KEY_SIZE);
parent_cfid->dirents.is_valid = false;
parent_cfid->dirents.is_failed = true;
}
break;
}
@ -677,6 +678,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
const char *full_path;
void *page;
int retry_count = 0;
struct cached_fid *cfid = NULL;
xid = get_xid();
@ -716,6 +718,28 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
cifs_dbg(FYI, "non-NULL inode in lookup\n");
} else {
cifs_dbg(FYI, "NULL inode in lookup\n");
/*
* We can only rely on negative dentries having the same
* spelling as the cached dirent if case insensitivity is
* forced on mount.
*
* XXX: if servers correctly announce Case Sensitivity Search
* on GetInfo of FileFSAttributeInformation, then we can take
* correct action even if case insensitive is not forced on
* mount.
*/
if (pTcon->nocase && !open_cached_dir_by_dentry(pTcon, direntry->d_parent, &cfid)) {
/*
* dentry is negative and parent is fully cached:
* we can assume file does not exist
*/
if (cfid->dirents.is_valid) {
close_cached_dir(cfid);
goto out;
}
close_cached_dir(cfid);
}
}
cifs_dbg(FYI, "Full path: %s inode = 0x%p\n",
full_path, d_inode(direntry));
@ -749,6 +773,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
}
newInode = ERR_PTR(rc);
}
out:
free_dentry_path(page);
cifs_put_tlink(tlink);
free_xid(xid);
@ -759,7 +785,8 @@ static int
cifs_d_revalidate(struct inode *dir, const struct qstr *name,
struct dentry *direntry, unsigned int flags)
{
struct inode *inode;
struct inode *inode = NULL;
struct cached_fid *cfid;
int rc;
if (flags & LOOKUP_RCU)
@ -806,6 +833,21 @@ cifs_d_revalidate(struct inode *dir, const struct qstr *name,
return 1;
}
} else {
struct cifs_sb_info *cifs_sb = CIFS_SB(dir->i_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
if (!open_cached_dir_by_dentry(tcon, direntry->d_parent, &cfid)) {
/*
* dentry is negative and parent is fully cached:
* we can assume file does not exist
*/
if (cfid->dirents.is_valid) {
close_cached_dir(cfid);
return 1;
}
close_cached_dir(cfid);
}
}
/*

View File

@ -1818,6 +1818,13 @@ static int smb3_fs_context_parse_param(struct fs_context *fc,
goto cifs_parse_mount_err;
}
/*
* Multichannel is not meaningful if max_channels is 1.
* Force multichannel to false to ensure consistent configuration.
*/
if (ctx->multichannel && ctx->max_channels == 1)
ctx->multichannel = false;
return 0;
cifs_parse_mount_err:

View File

@ -2704,7 +2704,7 @@ cifs_dentry_needs_reval(struct dentry *dentry)
return true;
if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
if (cfid->time && cifs_i->time > cfid->time) {
if (cifs_i->time > cfid->time) {
close_cached_dir(cfid);
return false;
}

View File

@ -874,39 +874,42 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
cde->is_valid = 1;
}
static void add_cached_dirent(struct cached_dirents *cde,
struct dir_context *ctx,
const char *name, int namelen,
struct cifs_fattr *fattr,
struct file *file)
static bool add_cached_dirent(struct cached_dirents *cde,
struct dir_context *ctx, const char *name,
int namelen, struct cifs_fattr *fattr,
struct file *file)
{
struct cached_dirent *de;
if (cde->file != file)
return;
return false;
if (cde->is_valid || cde->is_failed)
return;
return false;
if (ctx->pos != cde->pos) {
cde->is_failed = 1;
return;
return false;
}
de = kzalloc(sizeof(*de), GFP_ATOMIC);
if (de == NULL) {
cde->is_failed = 1;
return;
return false;
}
de->namelen = namelen;
de->name = kstrndup(name, namelen, GFP_ATOMIC);
if (de->name == NULL) {
kfree(de);
cde->is_failed = 1;
return;
return false;
}
de->pos = ctx->pos;
memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
list_add_tail(&de->entry, &cde->entries);
/* update accounting */
cde->entries_count++;
cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
return true;
}
static bool cifs_dir_emit(struct dir_context *ctx,
@ -915,7 +918,8 @@ static bool cifs_dir_emit(struct dir_context *ctx,
struct cached_fid *cfid,
struct file *file)
{
bool rc;
size_t delta_bytes = 0;
bool rc, added = false;
ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
@ -923,10 +927,20 @@ static bool cifs_dir_emit(struct dir_context *ctx,
return rc;
if (cfid) {
/* Cost of this entry */
delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
mutex_lock(&cfid->dirents.de_mutex);
add_cached_dirent(&cfid->dirents, ctx, name, namelen,
fattr, file);
added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
fattr, file);
mutex_unlock(&cfid->dirents.de_mutex);
if (added) {
/* per-tcon then global for consistency with free path */
atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
atomic_long_inc(&cfid->cfids->total_dirents_entries);
atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
}
}
return rc;

View File

@ -954,11 +954,8 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
if (!rc) {
if (cfid->has_lease) {
close_cached_dir(cfid);
return 0;
}
close_cached_dir(cfid);
return 0;
}
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
@ -4219,7 +4216,7 @@ fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst *rqst,
int num_rqst, const u8 *sig, u8 **iv,
struct aead_request **req, struct sg_table *sgt,
unsigned int *num_sgs, size_t *sensitive_size)
unsigned int *num_sgs)
{
unsigned int req_size = sizeof(**req) + crypto_aead_reqsize(tfm);
unsigned int iv_size = crypto_aead_ivsize(tfm);
@ -4236,9 +4233,8 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
len += req_size;
len = ALIGN(len, __alignof__(struct scatterlist));
len += array_size(*num_sgs, sizeof(struct scatterlist));
*sensitive_size = len;
p = kvzalloc(len, GFP_NOFS);
p = kzalloc(len, GFP_NOFS);
if (!p)
return ERR_PTR(-ENOMEM);
@ -4252,16 +4248,14 @@ static void *smb2_aead_req_alloc(struct crypto_aead *tfm, const struct smb_rqst
static void *smb2_get_aead_req(struct crypto_aead *tfm, struct smb_rqst *rqst,
int num_rqst, const u8 *sig, u8 **iv,
struct aead_request **req, struct scatterlist **sgl,
size_t *sensitive_size)
struct aead_request **req, struct scatterlist **sgl)
{
struct sg_table sgtable = {};
unsigned int skip, num_sgs, i, j;
ssize_t rc;
void *p;
p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, &sgtable,
&num_sgs, sensitive_size);
p = smb2_aead_req_alloc(tfm, rqst, num_rqst, sig, iv, req, &sgtable, &num_sgs);
if (IS_ERR(p))
return ERR_CAST(p);
@ -4350,7 +4344,6 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
DECLARE_CRYPTO_WAIT(wait);
unsigned int crypt_len = le32_to_cpu(tr_hdr->OriginalMessageSize);
void *creq;
size_t sensitive_size;
rc = smb2_get_enc_key(server, le64_to_cpu(tr_hdr->SessionId), enc, key);
if (rc) {
@ -4376,8 +4369,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
return rc;
}
creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg,
&sensitive_size);
creq = smb2_get_aead_req(tfm, rqst, num_rqst, sign, &iv, &req, &sg);
if (IS_ERR(creq))
return PTR_ERR(creq);
@ -4407,7 +4399,7 @@ crypt_message(struct TCP_Server_Info *server, int num_rqst,
if (!rc && enc)
memcpy(&tr_hdr->Signature, sign, SMB2_SIGNATURE_SIZE);
kvfree_sensitive(creq, sensitive_size);
kfree_sensitive(creq);
return rc;
}

View File

@ -240,8 +240,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
*/
if (smb2_command != SMB2_TREE_DISCONNECT) {
spin_unlock(&tcon->tc_lock);
cifs_dbg(FYI, "can not send cmd %d while umounting\n",
smb2_command);
cifs_tcon_dbg(FYI, "can not send cmd %d while umounting\n",
smb2_command);
return -ENODEV;
}
}
@ -296,9 +296,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
return 0;
}
spin_unlock(&ses->chan_lock);
cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
tcon->ses->chans_need_reconnect,
tcon->need_reconnect);
cifs_tcon_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d\n",
tcon->ses->chans_need_reconnect,
tcon->need_reconnect);
mutex_lock(&ses->session_mutex);
/*
@ -392,11 +392,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
rc = cifs_tree_connect(0, tcon);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
cifs_tcon_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) {
/* If sess reconnected but tcon didn't, something strange ... */
mutex_unlock(&ses->session_mutex);
cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
cifs_tcon_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
goto out;
}
@ -442,8 +442,8 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
from_reconnect);
goto skip_add_channels;
} else if (rc)
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
cifs_tcon_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
if (ses->chan_max > ses->chan_count &&
ses->iface_count &&

View File

@ -22,6 +22,7 @@
#include <linux/mempool.h>
#include <linux/sched/signal.h>
#include <linux/task_io_accounting_ops.h>
#include <linux/task_work.h>
#include "cifspdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
@ -173,9 +174,16 @@ smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg,
* send a packet. In most cases if we fail to send
* after the retries we will kill the socket and
* reconnect which may clear the network problem.
*
* Even if regular signals are masked, EINTR might be
* propagated from sk_stream_wait_memory() to here when
* TIF_NOTIFY_SIGNAL is used for task work. For example,
* certain io_uring completions will use that. Treat
* having EINTR with pending task work the same as EAGAIN
* to avoid unnecessary reconnects.
*/
rc = sock_sendmsg(ssocket, smb_msg);
if (rc == -EAGAIN) {
if (rc == -EAGAIN || unlikely(rc == -EINTR && task_work_pending(current))) {
retries++;
if (retries >= 14 ||
(!server->noblocksnd && (retries > 2))) {
@ -323,8 +331,7 @@ int __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
break;
total_len += sent;
}
}
}
unmask:
sigprocmask(SIG_SETMASK, &oldmask, NULL);

View File

@ -3,5 +3,4 @@
# Makefile for Linux filesystem routines that are shared by client and server.
#
obj-$(CONFIG_SMBFS) += cifs_arc4.o
obj-$(CONFIG_SMBFS) += cifs_md4.o

View File

@ -1,23 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Common values for ARC4 Cipher Algorithm
*/
#ifndef _CRYPTO_ARC4_H
#define _CRYPTO_ARC4_H
#include <linux/types.h>
#define ARC4_MIN_KEY_SIZE 1
#define ARC4_MAX_KEY_SIZE 256
#define ARC4_BLOCK_SIZE 1
struct arc4_ctx {
u32 S[256];
u32 x, y;
};
int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len);
void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len);
#endif /* _CRYPTO_ARC4_H */

View File

@ -1,75 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Cryptographic API
*
* ARC4 Cipher Algorithm
*
* Jon Oberheide <jon@oberheide.org>
*/
#include <linux/module.h>
#include "arc4.h"
MODULE_DESCRIPTION("ARC4 Cipher Algorithm");
MODULE_LICENSE("GPL");
int cifs_arc4_setkey(struct arc4_ctx *ctx, const u8 *in_key, unsigned int key_len)
{
int i, j = 0, k = 0;
ctx->x = 1;
ctx->y = 0;
for (i = 0; i < 256; i++)
ctx->S[i] = i;
for (i = 0; i < 256; i++) {
u32 a = ctx->S[i];
j = (j + in_key[k] + a) & 0xff;
ctx->S[i] = ctx->S[j];
ctx->S[j] = a;
if (++k >= key_len)
k = 0;
}
return 0;
}
EXPORT_SYMBOL_GPL(cifs_arc4_setkey);
void cifs_arc4_crypt(struct arc4_ctx *ctx, u8 *out, const u8 *in, unsigned int len)
{
u32 *const S = ctx->S;
u32 x, y, a, b;
u32 ty, ta, tb;
if (len == 0)
return;
x = ctx->x;
y = ctx->y;
a = S[x];
y = (y + a) & 0xff;
b = S[y];
do {
S[y] = a;
a = (a + b) & 0xff;
S[x] = b;
x = (x + 1) & 0xff;
ta = S[x];
ty = (y + ta) & 0xff;
tb = S[ty];
*out++ = *in++ ^ S[a];
if (--len == 0)
break;
y = ty;
a = ta;
b = tb;
} while (true);
ctx->x = x;
ctx->y = y;
}
EXPORT_SYMBOL_GPL(cifs_arc4_crypt);

View File

@ -10,6 +10,7 @@ config SMB_SERVER
select CRYPTO_MD5
select CRYPTO_HMAC
select CRYPTO_ECB
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_DES
select CRYPTO_LIB_SHA256
select CRYPTO_SHA256

View File

@ -20,6 +20,7 @@
#include "glob.h"
#include <linux/fips.h>
#include <crypto/arc4.h>
#include <crypto/des.h>
#include "server.h"
@ -29,7 +30,6 @@
#include "mgmt/user_config.h"
#include "crypto_ctx.h"
#include "transport_ipc.h"
#include "../common/arc4.h"
/*
* Fixed format data defining GSS header and fixed string
@ -365,10 +365,9 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
if (!ctx_arc4)
return -ENOMEM;
cifs_arc4_setkey(ctx_arc4, sess->sess_key,
SMB2_NTLMV2_SESSKEY_SIZE);
cifs_arc4_crypt(ctx_arc4, sess->sess_key,
(char *)authblob + sess_key_off, sess_key_len);
arc4_setkey(ctx_arc4, sess->sess_key, SMB2_NTLMV2_SESSKEY_SIZE);
arc4_crypt(ctx_arc4, sess->sess_key,
(char *)authblob + sess_key_off, sess_key_len);
kfree_sensitive(ctx_arc4);
}