summaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-04-23 16:59:55 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-04-23 16:59:55 -0700
commitd0ed69f3e380276e7ab36f1a3f19702c5e993915 (patch)
tree1fa742f35805c25ea97abda254f54db1f1fac182 /fs
parente728258debd553c95d2e70f9cd97c9fde27c7130 (diff)
parenta83307f34e0bd9b0e595b1074dc8fbcc1b7f3172 (diff)
Merge tag 'v7.1-rc1-part3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull smb client fixes from Steve French: - Four bug fixes: OOB read in ioctl query info, 3 ACL fixes - SMB1 Unix extensions mount fix - Four crypto improvements: move to AES-CMAC library, simpler and faster - Remove drop_dir_cache to avoid potential crash, and move to /procfs - Seven SMB3.1.1 compression fixes * tag 'v7.1-rc1-part3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: smb: client: Drop 'allocate_crypto' arg from smb*_calc_signature() smb: client: Make generate_key() return void smb: client: Remove obsolete cmac(aes) allocation smb: client: Use AES-CMAC library for SMB3 signature calculation smb: common: add SMB3_COMPRESS_MAX_ALGS smb: client: compress: add code docs to lz77.c smb: client: compress: LZ77 optimizations smb: client: compress: increase LZ77_MATCH_MAX_DIST smb: client: compress: fix counting in LZ77 match finding smb: client: compress: fix buffer overrun in lz77_compress() smb: client: scope end_of_dacl to CIFS_DEBUG2 use in parse_dacl smb: client: fix (remove) drop_dir_cache module parameter smb: client: require a full NFS mode SID before reading mode bits smb: client: validate the whole DACL before rewriting it in cifsacl smb: client: fix OOB read in smb2_ioctl_query_info QUERY_INFO path cifs: update internal module version number smb: client: compress: fix bad encoding on last LZ77 flag smb: client: fix dir separator in SMB1 UNIX mounts
Diffstat (limited to 'fs')
-rw-r--r--fs/smb/client/Kconfig2
-rw-r--r--fs/smb/client/cached_dir.c5
-rw-r--r--fs/smb/client/cached_dir.h2
-rw-r--r--fs/smb/client/cifs_debug.c56
-rw-r--r--fs/smb/client/cifs_unicode.c1
-rw-r--r--fs/smb/client/cifsacl.c115
-rw-r--r--fs/smb/client/cifsencrypt.c62
-rw-r--r--fs/smb/client/cifsfs.c38
-rw-r--r--fs/smb/client/cifsfs.h4
-rw-r--r--fs/smb/client/cifsglob.h7
-rw-r--r--fs/smb/client/cifsproto.h3
-rw-r--r--fs/smb/client/compress.c6
-rw-r--r--fs/smb/client/compress/lz77.c274
-rw-r--r--fs/smb/client/compress/lz77.h32
-rw-r--r--fs/smb/client/connect.c10
-rw-r--r--fs/smb/client/file.c2
-rw-r--r--fs/smb/client/misc.c57
-rw-r--r--fs/smb/client/sess.c11
-rw-r--r--fs/smb/client/smb1ops.c19
-rw-r--r--fs/smb/client/smb2ops.c6
-rw-r--r--fs/smb/client/smb2pdu.c2
-rw-r--r--fs/smb/client/smb2proto.h1
-rw-r--r--fs/smb/client/smb2transport.c113
-rw-r--r--fs/smb/common/smb2pdu.h2
24 files changed, 438 insertions, 392 deletions
diff --git a/fs/smb/client/Kconfig b/fs/smb/client/Kconfig
index 63831242fddf..a1c6ad4d574a 100644
--- a/fs/smb/client/Kconfig
+++ b/fs/smb/client/Kconfig
@@ -5,11 +5,11 @@ config CIFS
select NLS
select NLS_UCS2_UTILS
select CRYPTO
- select CRYPTO_CMAC
select CRYPTO_AEAD2
select CRYPTO_CCM
select CRYPTO_GCM
select CRYPTO_AES
+ select CRYPTO_LIB_AES_CBC_MACS
select CRYPTO_LIB_ARC4
select CRYPTO_LIB_MD5
select CRYPTO_LIB_SHA256
diff --git a/fs/smb/client/cached_dir.c b/fs/smb/client/cached_dir.c
index 04bb95091f49..02791ec3c5a1 100644
--- a/fs/smb/client/cached_dir.c
+++ b/fs/smb/client/cached_dir.c
@@ -593,7 +593,7 @@ done:
* Invalidate all cached dirs when a TCON has been reset
* due to a session loss.
*/
-void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync)
{
struct cached_fids *cfids = tcon->cfids;
struct cached_fid *cfid, *q;
@@ -625,7 +625,8 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
/* run laundromat unconditionally now as there might have been previously queued work */
mod_delayed_work(cfid_put_wq, &cfids->laundromat_work, 0);
- flush_delayed_work(&cfids->laundromat_work);
+ if (sync)
+ flush_delayed_work(&cfids->laundromat_work);
}
static void
diff --git a/fs/smb/client/cached_dir.h b/fs/smb/client/cached_dir.h
index 19d5592512e4..fc756836da95 100644
--- a/fs/smb/client/cached_dir.h
+++ b/fs/smb/client/cached_dir.h
@@ -90,7 +90,7 @@ void close_cached_dir(struct cached_fid *cfid);
void drop_cached_dir_by_name(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
-void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon, bool sync);
bool cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
#endif /* _CACHED_DIR_H */
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 0691d2a3e04b..4ed4f55a0bb7 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -306,6 +306,9 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
LIST_HEAD(entry);
seq_puts(m, "# Version:1\n");
+#ifdef CONFIG_CIFS_DEBUG
+ seq_puts(m, "# Write 0 to this file to drop all cached directory entries\n");
+#endif /* CONFIG_CIFS_DEBUG */
seq_puts(m, "# Format:\n");
seq_puts(m, "# <tree id> <sess id> <persistent fid> <lease-key> <path>\n");
@@ -353,6 +356,51 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
return 0;
}
+#ifdef CONFIG_CIFS_DEBUG
+static int cifs_debug_dirs_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cifs_debug_dirs_proc_show, NULL);
+}
+
+/* Drop all cached directory entries across all CIFS mounts. */
+static ssize_t cifs_debug_dirs_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int rc, v;
+
+ rc = kstrtoint_from_user(buffer, count, 10, &v);
+ if (rc)
+ return rc;
+
+ if (v == 0) {
+ 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, false);
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ }
+
+ return count;
+}
+
+static const struct proc_ops cifs_debug_dirs_proc_ops = {
+ .proc_open = cifs_debug_dirs_proc_open,
+ .proc_read = seq_read,
+ .proc_lseek = seq_lseek,
+ .proc_release = single_release,
+ .proc_write = cifs_debug_dirs_proc_write,
+};
+#endif /* CONFIG_CIFS_DEBUG */
+
static __always_inline const char *compression_alg_str(__le16 alg)
{
switch (alg) {
@@ -885,9 +933,11 @@ cifs_proc_init(void)
proc_create_single("open_files", 0400, proc_fs_cifs,
cifs_debug_files_proc_show);
- proc_create_single("open_dirs", 0400, proc_fs_cifs,
- cifs_debug_dirs_proc_show);
-
+#ifdef CONFIG_CIFS_DEBUG
+ proc_create("open_dirs", 0600, proc_fs_cifs, &cifs_debug_dirs_proc_ops);
+#else /* CONFIG_CIFS_DEBUG */
+ proc_create_single("open_dirs", 0400, proc_fs_cifs, cifs_debug_dirs_proc_show);
+#endif /* !CONFIG_CIFS_DEBUG */
proc_create("Stats", 0644, proc_fs_cifs, &cifs_stats_proc_ops);
proc_create("cifsFYI", 0644, proc_fs_cifs, &cifsFYI_proc_ops);
proc_create("traceSMB", 0644, proc_fs_cifs, &traceSMB_proc_ops);
diff --git a/fs/smb/client/cifs_unicode.c b/fs/smb/client/cifs_unicode.c
index e2edc207cef2..4a8a591f4bca 100644
--- a/fs/smb/client/cifs_unicode.c
+++ b/fs/smb/client/cifs_unicode.c
@@ -6,6 +6,7 @@
*/
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include "cifs_fs_sb.h"
#include "cifs_unicode.h"
#include "cifsglob.h"
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index c920039d733c..ec5d47779304 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -758,6 +758,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl)
}
#endif
+static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl)
+{
+ int i, ace_hdr_size, ace_size, min_ace_size;
+ u16 dacl_size, num_aces;
+ char *acl_base, *end_of_dacl;
+ struct smb_ace *pace;
+
+ if (!pdacl)
+ return 0;
+
+ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ dacl_size = le16_to_cpu(pdacl->size);
+ if (dacl_size < sizeof(struct smb_acl) ||
+ end_of_acl < (char *)pdacl + dacl_size) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ num_aces = le16_to_cpu(pdacl->num_aces);
+ if (!num_aces)
+ return 0;
+
+ ace_hdr_size = offsetof(struct smb_ace, sid) +
+ offsetof(struct smb_sid, sub_auth);
+ min_ace_size = ace_hdr_size + sizeof(__le32);
+ if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ return -EINVAL;
+ }
+
+ end_of_dacl = (char *)pdacl + dacl_size;
+ acl_base = (char *)pdacl;
+ ace_size = sizeof(struct smb_acl);
+
+ for (i = 0; i < num_aces; ++i) {
+ if (end_of_dacl - acl_base < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ pace = (struct smb_ace *)(acl_base + ace_size);
+ acl_base = (char *)pace;
+
+ if (end_of_dacl - acl_base < ace_hdr_size ||
+ pace->sid.num_subauth == 0 ||
+ pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
+ if (end_of_dacl - acl_base < ace_size ||
+ le16_to_cpu(pace->size) < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+
+ ace_size = le16_to_cpu(pace->size);
+ if (end_of_dacl - acl_base < ace_size) {
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
struct cifs_fattr *fattr, bool mode_from_special_sid)
@@ -777,12 +848,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
return;
}
- /* validate that we do not go past end of acl */
- if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
- end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
- cifs_dbg(VFS, "ACL too small to parse DACL\n");
+ if (validate_dacl(pdacl, end_of_acl))
return;
- }
cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
@@ -800,37 +867,19 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
if (num_aces > 0) {
umode_t denied_mode = 0;
- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
- (offsetof(struct smb_ace, sid) +
- offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
- return;
-
ppace = kmalloc_objs(struct smb_ace *, num_aces);
if (!ppace)
return;
for (i = 0; i < num_aces; ++i) {
- if (end_of_acl - acl_base < acl_size)
- break;
-
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
- acl_base = (char *)ppace[i];
- acl_size = offsetof(struct smb_ace, sid) +
- offsetof(struct smb_sid, sub_auth);
-
- if (end_of_acl - acl_base < acl_size ||
- ppace[i]->sid.num_subauth == 0 ||
- ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
- (end_of_acl - acl_base <
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
- (le16_to_cpu(ppace[i]->size) <
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
- break;
#ifdef CONFIG_CIFS_DEBUG2
- dump_ace(ppace[i], end_of_acl);
+ dump_ace(ppace[i],
+ (char *)pdacl + le16_to_cpu(pdacl->size));
#endif
if (mode_from_special_sid &&
+ ppace[i]->sid.num_subauth >= 3 &&
(compare_sids(&(ppace[i]->sid),
&sid_unix_NFS_mode) == 0)) {
/*
@@ -870,6 +919,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
(void *)ppace[i],
sizeof(struct smb_ace)); */
+ acl_base = (char *)ppace[i];
acl_size = le16_to_cpu(ppace[i]->size);
}
@@ -1293,10 +1343,9 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
- if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
- cifs_dbg(VFS, "Server returned illegal ACL size\n");
- return -EINVAL;
- }
+ rc = validate_dacl(dacl_ptr, end_of_acl);
+ if (rc)
+ return rc;
}
owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
@@ -1662,6 +1711,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
+ rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
+ if (rc) {
+ kfree(pntsd);
+ cifs_put_tlink(tlink);
+ return rc;
+ }
if (mode_from_sid)
nsecdesclen +=
le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
diff --git a/fs/smb/client/cifsencrypt.c b/fs/smb/client/cifsencrypt.c
index 3d731f3af235..34804e9842a8 100644
--- a/fs/smb/client/cifsencrypt.c
+++ b/fs/smb/client/cifsencrypt.c
@@ -22,49 +22,33 @@
#include <linux/fips.h>
#include <linux/iov_iter.h>
#include <crypto/aead.h>
+#include <crypto/aes-cbc-macs.h>
#include <crypto/arc4.h>
#include <crypto/md5.h>
#include <crypto/sha2.h>
-static int cifs_sig_update(struct cifs_calc_sig_ctx *ctx,
- const u8 *data, size_t len)
+static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
+ void *priv, void *priv2)
{
- if (ctx->md5) {
- md5_update(ctx->md5, data, len);
- return 0;
- }
- if (ctx->hmac) {
- hmac_sha256_update(ctx->hmac, data, len);
- return 0;
- }
- return crypto_shash_update(ctx->shash, data, len);
+ struct cifs_calc_sig_ctx *ctx = priv;
+
+ if (ctx->md5)
+ md5_update(ctx->md5, iter_base, len);
+ else if (ctx->hmac)
+ hmac_sha256_update(ctx->hmac, iter_base, len);
+ else
+ aes_cmac_update(ctx->cmac, iter_base, len);
+ return 0; /* Return value is length *not* processed, i.e. 0. */
}
-static int cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
+static void cifs_sig_final(struct cifs_calc_sig_ctx *ctx, u8 *out)
{
- if (ctx->md5) {
+ if (ctx->md5)
md5_final(ctx->md5, out);
- return 0;
- }
- if (ctx->hmac) {
+ else if (ctx->hmac)
hmac_sha256_final(ctx->hmac, out);
- return 0;
- }
- return crypto_shash_final(ctx->shash, out);
-}
-
-static size_t cifs_sig_step(void *iter_base, size_t progress, size_t len,
- void *priv, void *priv2)
-{
- struct cifs_calc_sig_ctx *ctx = priv;
- int ret, *pret = priv2;
-
- ret = cifs_sig_update(ctx, iter_base, len);
- if (ret < 0) {
- *pret = ret;
- return len;
- }
- return 0;
+ else
+ aes_cmac_final(ctx->cmac, out);
}
/*
@@ -75,9 +59,8 @@ static int cifs_sig_iter(const struct iov_iter *iter, size_t maxsize,
{
struct iov_iter tmp_iter = *iter;
size_t did;
- int err;
- did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, &err,
+ did = iterate_and_advance_kernel(&tmp_iter, maxsize, ctx, NULL,
cifs_sig_step);
if (did != maxsize)
return smb_EIO2(smb_eio_trace_sig_iter, did, maxsize);
@@ -108,11 +91,8 @@ int __cifs_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
if (rc < 0)
return rc;
- rc = cifs_sig_final(ctx, signature);
- if (rc)
- cifs_dbg(VFS, "%s: Could not generate hash\n", __func__);
-
- return rc;
+ cifs_sig_final(ctx, signature);
+ return 0;
}
/* Build a proper attribute value/target info pairs blob.
@@ -523,8 +503,6 @@ calc_seckey(struct cifs_ses *ses)
void
cifs_crypto_secmech_release(struct TCP_Server_Info *server)
{
- cifs_free_hash(&server->secmech.aes_cmac);
-
if (server->secmech.enc) {
crypto_free_aead(server->secmech.enc);
server->secmech.enc = NULL;
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 2025739f070a..9f76b0347fa9 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -127,43 +127,6 @@ atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
atomic_t cifs_sillycounter;
atomic_t cifs_tmpcounter;
-/*
- * 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);
@@ -2160,7 +2123,6 @@ MODULE_DESCRIPTION
MODULE_VERSION(CIFS_VERSION);
MODULE_SOFTDEP("nls");
MODULE_SOFTDEP("aes");
-MODULE_SOFTDEP("cmac");
MODULE_SOFTDEP("aead2");
MODULE_SOFTDEP("ccm");
MODULE_SOFTDEP("gcm");
diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h
index 7370b38da938..c455b15f2778 100644
--- a/fs/smb/client/cifsfs.h
+++ b/fs/smb/client/cifsfs.h
@@ -161,6 +161,6 @@ extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
/* when changing internal version - update following two lines at same time */
-#define SMB3_PRODUCT_BUILD 59
-#define CIFS_VERSION "2.59"
+#define SMB3_PRODUCT_BUILD 60
+#define CIFS_VERSION "2.60"
#endif /* _CIFSFS_H */
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index ccfde157d3be..82e0adc1dabd 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -23,7 +23,6 @@
#include <linux/fcntl.h>
#include "cifs_fs_sb.h"
#include "cifsacl.h"
-#include <crypto/internal/hash.h>
#include <uapi/linux/cifs/cifs_mount.h>
#include "../common/smbglob.h"
#include "../common/smb2pdu.h"
@@ -221,10 +220,8 @@ struct session_key {
char *response;
};
-/* crypto hashing related structure/fields, not specific to a sec mech */
+/* encryption related structure/fields, not specific to a sec mech */
struct cifs_secmech {
- struct shash_desc *aes_cmac; /* block-cipher based MAC function, for SMB3 signatures */
-
struct crypto_aead *enc; /* smb3 encryption AEAD TFM (AES-CCM and AES-GCM) */
struct crypto_aead *dec; /* smb3 decryption AEAD TFM (AES-CCM and AES-GCM) */
};
@@ -2324,7 +2321,7 @@ static inline void mid_execute_callback(struct TCP_Server_Info *server,
struct cifs_calc_sig_ctx {
struct md5_ctx *md5;
struct hmac_sha256_ctx *hmac;
- struct shash_desc *shash;
+ struct aes_cmac_ctx *cmac;
};
#define CIFS_RECONN_DELAY_SECS 30
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index c24c50d732e6..4a25afda9448 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -351,9 +351,6 @@ int __cifs_calc_signature(struct smb_rqst *rqst,
enum securityEnum cifs_select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested);
-int cifs_alloc_hash(const char *name, struct shash_desc **sdesc);
-void cifs_free_hash(struct shash_desc **sdesc);
-
int cifs_try_adding_channels(struct cifs_ses *ses);
int smb3_update_ses_channels(struct cifs_ses *ses,
struct TCP_Server_Info *server,
diff --git a/fs/smb/client/compress.c b/fs/smb/client/compress.c
index 3d1e73f5d9af..be9023f841e6 100644
--- a/fs/smb/client/compress.c
+++ b/fs/smb/client/compress.c
@@ -329,11 +329,7 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s
goto err_free;
}
- /*
- * This is just overprovisioning, as the algorithm will error out if @dst reaches 7/8
- * of @slen.
- */
- dlen = slen;
+ dlen = lz77_compressed_alloc_size(slen);
dst = kvzalloc(dlen, GFP_KERNEL);
if (!dst) {
ret = -ENOMEM;
diff --git a/fs/smb/client/compress/lz77.c b/fs/smb/client/compress/lz77.c
index 96e8a8057a77..7365d0f97396 100644
--- a/fs/smb/client/compress/lz77.c
+++ b/fs/smb/client/compress/lz77.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2024, SUSE LLC
+ * Copyright (C) 2024-2026, SUSE LLC
*
* Authors: Enzo Matsumiya <ematsumiya@suse.de>
*
@@ -15,19 +15,41 @@
/*
* Compression parameters.
+ *
+ * LZ77_MATCH_MAX_DIST: Farthest back a match can be from current position (can be 1 - 8K).
+ * LZ77_HASH_LOG:
+ * LZ77_HASH_SIZE: ilog2 hash size (recommended to be 13 - 18, default 15 (hash size
+ * 32k)).
+ * LZ77_RSTEP_SIZE: Number of bytes to read from input buffer for hashing and initial
+ * match check (default 4 bytes, this effectivelly makes this the min
+ * match len).
+ * LZ77_MSTEP_SIZE: Number of bytes to extend-compare a found match (default 8 bytes).
+ * LZ77_SKIP_TRIGGER: ilog2 value for adaptive skipping, i.e. to progressively skip input
+ * bytes when we can't find matches. Default is 4.
+ * Higher values (>0) will decrease compression time, but will result
+ * in worse compression ratio. Lower values will give better
+ * compression ratio (more matches found), but will increase time.
*/
-#define LZ77_MATCH_MIN_LEN 4
-#define LZ77_MATCH_MIN_DIST 1
-#define LZ77_MATCH_MAX_DIST SZ_1K
+#define LZ77_MATCH_MAX_DIST SZ_8K
#define LZ77_HASH_LOG 15
#define LZ77_HASH_SIZE (1 << LZ77_HASH_LOG)
-#define LZ77_STEP_SIZE sizeof(u64)
+#define LZ77_RSTEP_SIZE sizeof(u32)
+#define LZ77_MSTEP_SIZE sizeof(u64)
+#define LZ77_SKIP_TRIGGER 4
+
+#define LZ77_PREFETCH(ptr) __builtin_prefetch((ptr), 0, 3)
+#define LZ77_FLAG_MAX 32
static __always_inline u8 lz77_read8(const u8 *ptr)
{
return get_unaligned(ptr);
}
+static __always_inline u32 lz77_read32(const u32 *ptr)
+{
+ return get_unaligned(ptr);
+}
+
static __always_inline u64 lz77_read64(const u64 *ptr)
{
return get_unaligned(ptr);
@@ -48,17 +70,17 @@ static __always_inline void lz77_write32(u32 *ptr, u32 v)
put_unaligned_le32(v, ptr);
}
-static __always_inline u32 lz77_match_len(const void *wnd, const void *cur, const void *end)
+static __always_inline u32 lz77_match_len(const void *match, const void *cur, const void *end)
{
const void *start = cur;
- u64 diff;
/* Safe for a do/while because otherwise we wouldn't reach here from the main loop. */
do {
- diff = lz77_read64(cur) ^ lz77_read64(wnd);
+ const u64 diff = lz77_read64(cur) ^ lz77_read64(match);
+
if (!diff) {
- cur += LZ77_STEP_SIZE;
- wnd += LZ77_STEP_SIZE;
+ cur += LZ77_MSTEP_SIZE;
+ match += LZ77_MSTEP_SIZE;
continue;
}
@@ -67,15 +89,31 @@ static __always_inline u32 lz77_match_len(const void *wnd, const void *cur, cons
cur += count_trailing_zeros(diff) >> 3;
return (cur - start);
- } while (likely(cur + LZ77_STEP_SIZE < end));
+ } while (likely(cur + LZ77_MSTEP_SIZE <= end));
- while (cur < end && lz77_read8(cur++) == lz77_read8(wnd++))
- ;
+ /* Fallback to byte-by-byte comparison for last <8 bytes. */
+ while (cur < end && lz77_read8(cur) == lz77_read8(match)) {
+ cur++;
+ match++;
+ }
return (cur - start);
}
-static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u32 len)
+/**
+ * lz77_encode_match() - Match encoding.
+ * @dst: compressed buffer
+ * @nib: pointer to an address in @dst
+ * @dist: match distance
+ * @len: match length
+ *
+ * Assumes all args were previously checked.
+ *
+ * Return: @dst advanced to new position
+ *
+ * Ref: MS-XCA 2.3.4 "Plain LZ77 Compression Algorithm Details" - "Processing"
+ */
+static __always_inline void *lz77_encode_match(void *dst, void **nib, u16 dist, u32 len)
{
len -= 3;
dist--;
@@ -84,12 +122,12 @@ static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u
if (len < 7) {
lz77_write16(dst, dist + len);
- return dst + 2;
+ return dst + sizeof(u16);
}
dist |= 7;
lz77_write16(dst, dist);
- dst += 2;
+ dst += sizeof(u16);
len -= 7;
if (!*nib) {
@@ -119,113 +157,175 @@ static __always_inline void *lz77_write_match(void *dst, void **nib, u32 dist, u
if (len <= 0xffff) {
lz77_write16(dst, len);
- return dst + 2;
+ return dst + sizeof(u16);
}
lz77_write16(dst, 0);
- dst += 2;
+ dst += sizeof(u16);
lz77_write32(dst, len);
- return dst + 4;
+ return dst + sizeof(u32);
+}
+
+/**
+ * lz77_encode_literals() - Literals encoding.
+ * @start: where to start copying literals (uncompressed buffer)
+ * @end: when to stop copying (uncompressed buffer)
+ * @dst: compressed buffer
+ * @f: pointer to current flag value
+ * @fc: pointer to current flag count
+ * @fp: pointer to current flag address
+ *
+ * Batch copy literals from @start to @dst, updating flag values accordingly.
+ * Assumes all args were previously checked.
+ *
+ * Return: @dst advanced to new position
+ *
+ * MS-XCA 2.3.4 "Plain LZ77 Compression Algorithm Details" - "Processing"
+ */
+static __always_inline void *lz77_encode_literals(const void *start, const void *end, void *dst,
+ long *f, u32 *fc, void **fp)
+{
+ if (start >= end)
+ return dst;
+
+ do {
+ const u32 len = umin(end - start, LZ77_FLAG_MAX - *fc);
+
+ memcpy(dst, start, len);
+
+ dst += len;
+ start += len;
+
+ *f <<= len;
+ *fc += len;
+ if (*fc == LZ77_FLAG_MAX) {
+ lz77_write32(*fp, *f);
+ *fc = 0;
+ *fp = dst;
+ dst += sizeof(u32);
+ }
+ } while (start < end);
+
+ return dst;
}
-noinline int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen)
+static __always_inline u32 lz77_hash(const u32 v)
{
- const void *srcp, *end;
+ return ((v ^ 0x9E3779B9) * 0x85EBCA6B) >> (32 - LZ77_HASH_LOG);
+}
+
+noinline int lz77_compress(const void *src, const u32 slen, void *dst, u32 *dlen)
+{
+ const void *srcp, *rlim, *end, *anchor;
+ u32 *htable, hash, flag_count = 0;
void *dstp, *nib, *flag_pos;
- u32 flag_count = 0;
long flag = 0;
- u64 *htable;
- srcp = src;
- end = src + slen;
+ /* This is probably a bug, so throw a warning. */
+ if (WARN_ON_ONCE(*dlen < lz77_compressed_alloc_size(slen)))
+ return -EINVAL;
+
+ srcp = anchor = src;
+ end = srcp + slen; /* absolute end */
+ rlim = end - LZ77_MSTEP_SIZE; /* read limit (for lz77_match_len()) */
dstp = dst;
- nib = NULL;
flag_pos = dstp;
- dstp += 4;
+ dstp += sizeof(u32);
+ nib = NULL;
htable = kvcalloc(LZ77_HASH_SIZE, sizeof(*htable), GFP_KERNEL);
if (!htable)
return -ENOMEM;
- /* Main loop. */
+ LZ77_PREFETCH(srcp + LZ77_RSTEP_SIZE);
+
+ /*
+ * Adjust @srcp so we don't get a false positive match on first iteration.
+ * Then prepare hash for first loop iteration (don't advance @srcp again).
+ */
+ hash = lz77_hash(lz77_read32(srcp++));
+ htable[hash] = 0;
+ hash = lz77_hash(lz77_read32(srcp));
+
+ /*
+ * Main loop.
+ *
+ * @dlen is >= lz77_compressed_alloc_size(), so run without bound-checking @dstp.
+ *
+ * This code was crafted in a way to best utilise fetch-decode-execute CPU flow.
+ * Any attempt to optimize it, or even organize it, can lead to huge performance loss.
+ */
do {
- u32 dist, len = 0;
- const void *wnd;
- u64 hash;
-
- hash = ((lz77_read64(srcp) << 24) * 889523592379ULL) >> (64 - LZ77_HASH_LOG);
- wnd = src + htable[hash];
- htable[hash] = srcp - src;
- dist = srcp - wnd;
-
- if (dist && dist < LZ77_MATCH_MAX_DIST)
- len = lz77_match_len(wnd, srcp, end);
-
- if (len < LZ77_MATCH_MIN_LEN) {
- lz77_write8(dstp, lz77_read8(srcp));
-
- dstp++;
- srcp++;
-
- flag <<= 1;
- flag_count++;
- if (flag_count == 32) {
- lz77_write32(flag_pos, flag);
- flag_count = 0;
- flag_pos = dstp;
- dstp += 4;
- }
-
- continue;
- }
+ const void *match, *next = srcp;
+ u32 len, step = 1, skip = 1U << LZ77_SKIP_TRIGGER;
+
+ /* Match finding (hot path -- don't change the read/check/write order). */
+ do {
+ const u32 cur_hash = hash;
+
+ srcp = next;
+ next += step;
+
+ /*
+ * Adaptive skipping.
+ *
+ * Increment @step every (1 << LZ77_SKIP_TRIGGER, 16 in our case) bytes
+ * without a match.
+ * Reset to 1 when a match is found.
+ */
+ step = (skip++ >> LZ77_SKIP_TRIGGER);
+ if (unlikely(next > rlim))
+ goto out;
+
+ hash = lz77_hash(lz77_read32(next));
+ match = src + htable[cur_hash];
+ htable[cur_hash] = srcp - src;
+ } while (likely(match + LZ77_MATCH_MAX_DIST < srcp) ||
+ lz77_read32(match) != lz77_read32(srcp));
/*
- * Bail out if @dstp reached >= 7/8 of @slen -- already compressed badly, not worth
- * going further.
+ * Match found. Warm/cold path; begin parsing @srcp and writing to @dstp:
+ * - flush literals
+ * - compute match length (*)
+ * - encode match
+ *
+ * (*) Current minimum match length is defined by the memory read size above, so
+ * here we already know that we have 4 matching bytes, but it's just faster to
+ * redundantly compute it again in lz77_match_len() than to adjust pointers/len.
*/
- if (unlikely(dstp - dst >= slen - (slen >> 3))) {
- *dlen = slen;
- goto out;
- }
-
- dstp = lz77_write_match(dstp, &nib, dist, len);
+ dstp = lz77_encode_literals(anchor, srcp, dstp, &flag, &flag_count, &flag_pos);
+ len = lz77_match_len(match, srcp, end);
+ dstp = lz77_encode_match(dstp, &nib, srcp - match, len);
srcp += len;
+ anchor = srcp;
+
+ LZ77_PREFETCH(srcp);
flag = (flag << 1) | 1;
flag_count++;
- if (flag_count == 32) {
+ if (flag_count == LZ77_FLAG_MAX) {
lz77_write32(flag_pos, flag);
flag_count = 0;
flag_pos = dstp;
- dstp += 4;
+ dstp += sizeof(u32);
}
- } while (likely(srcp + LZ77_STEP_SIZE < end));
- while (srcp < end) {
- u32 c = umin(end - srcp, 32 - flag_count);
+ if (unlikely(srcp > rlim))
+ break;
- memcpy(dstp, srcp, c);
-
- dstp += c;
- srcp += c;
-
- flag <<= c;
- flag_count += c;
- if (flag_count == 32) {
- lz77_write32(flag_pos, flag);
- flag_count = 0;
- flag_pos = dstp;
- dstp += 4;
- }
- }
+ /* Prepare for next loop. */
+ hash = lz77_hash(lz77_read32(srcp));
+ } while (srcp < end);
+out:
+ dstp = lz77_encode_literals(anchor, end, dstp, &flag, &flag_count, &flag_pos);
- flag <<= (32 - flag_count);
- flag |= (1 << (32 - flag_count)) - 1;
+ flag_count = LZ77_FLAG_MAX - flag_count;
+ flag <<= flag_count;
+ flag |= (1UL << flag_count) - 1;
lz77_write32(flag_pos, flag);
*dlen = dstp - dst;
-out:
kvfree(htable);
if (*dlen < slen)
diff --git a/fs/smb/client/compress/lz77.h b/fs/smb/client/compress/lz77.h
index cdcb191b48a2..4e570846aefa 100644
--- a/fs/smb/client/compress/lz77.h
+++ b/fs/smb/client/compress/lz77.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
- * Copyright (C) 2024, SUSE LLC
+ * Copyright (C) 2024-2026, SUSE LLC
*
* Authors: Enzo Matsumiya <ematsumiya@suse.de>
*
@@ -11,5 +11,33 @@
#include <linux/kernel.h>
-int lz77_compress(const void *src, u32 slen, void *dst, u32 *dlen);
+/**
+ * lz77_compressed_alloc_size() - Compute compressed buffer size.
+ * @size: uncompressed (src) size
+ *
+ * Compute allocation size for the compressed buffer based on uncompressed size.
+ * Accounts for metadata and overprovision for the worst case scenario.
+ *
+ * LZ77 metadata is a 4-byte flag that is written:
+ * - on dst begin (pos 0)
+ * - every 32 literals or matches
+ * - on end-of-stream (possibly, if last write was another flag)
+ *
+ * Worst case scenario is an all-literal compression, which means:
+ * metadata bytes = 4 + ((@size / 32) * 4) + 4, or, simplified, (@size >> 3) + 8
+ *
+ * The worst case scenario rarely happens, but such overprovisioning also allows lz77_compress()
+ * main loop to run without ever bound checking dst, which is a huge perf improvement, while also
+ * being safe when compression goes bad.
+ *
+ * Return: required (*) allocation size for compressed buffer.
+ *
+ * (*) checked once in the beginning of lz77_compress()
+ */
+static __always_inline u32 lz77_compressed_alloc_size(const u32 size)
+{
+ return size + (size >> 3) + 8;
+}
+
+int lz77_compress(const void *src, const u32 slen, void *dst, u32 *dlen);
#endif /* _SMB_COMPRESS_LZ77_H */
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index ca1bc67eb23b..dcde25da468d 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -3609,7 +3609,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
server = mnt_ctx->server;
ctx = mnt_ctx->fs_ctx;
cifs_sb = mnt_ctx->cifs_sb;
- sbflags = cifs_sb_flags(cifs_sb);
/* search for existing tcon to this server share */
tcon = cifs_get_tcon(mnt_ctx->ses, ctx);
@@ -3624,9 +3623,10 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
* path (i.e., do not remap / and \ and do not map any special characters)
*/
if (tcon->posix_extensions) {
- sbflags |= CIFS_MOUNT_POSIX_PATHS;
- sbflags &= ~(CIFS_MOUNT_MAP_SFM_CHR |
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ atomic_or(CIFS_MOUNT_POSIX_PATHS, &cifs_sb->mnt_cifs_flags);
+ atomic_andnot(CIFS_MOUNT_MAP_SFM_CHR |
+ CIFS_MOUNT_MAP_SPECIAL_CHR,
+ &cifs_sb->mnt_cifs_flags);
}
#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
@@ -3650,6 +3650,7 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
tcon->unix_ext = 0; /* server does not support them */
+ sbflags = cifs_sb_flags(cifs_sb);
/* do not care if a following call succeed - informational */
if (!tcon->pipe && server->ops->qfs_tcon) {
server->ops->qfs_tcon(mnt_ctx->xid, tcon, cifs_sb);
@@ -3674,7 +3675,6 @@ int cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
out:
mnt_ctx->tcon = tcon;
- atomic_set(&cifs_sb->mnt_cifs_flags, sbflags);
return rc;
}
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index f743f058667f..664a2c223089 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -393,7 +393,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
}
spin_unlock(&tcon->open_file_lock);
- invalidate_all_cached_dirs(tcon);
+ invalidate_all_cached_dirs(tcon, true);
spin_lock(&tcon->tc_lock);
if (tcon->status == TID_IN_FILES_INVALIDATE)
tcon->status = TID_NEED_TCON;
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index 2aff1cab6c31..0c54b9b79a2c 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -785,63 +785,6 @@ parse_DFS_referrals_exit:
return rc;
}
-/**
- * cifs_alloc_hash - allocate hash and hash context together
- * @name: The name of the crypto hash algo
- * @sdesc: SHASH descriptor where to put the pointer to the hash TFM
- *
- * The caller has to make sure @sdesc is initialized to either NULL or
- * a valid context. It can be freed via cifs_free_hash().
- */
-int
-cifs_alloc_hash(const char *name, struct shash_desc **sdesc)
-{
- int rc = 0;
- struct crypto_shash *alg = NULL;
-
- if (*sdesc)
- return 0;
-
- alg = crypto_alloc_shash(name, 0, 0);
- if (IS_ERR(alg)) {
- cifs_dbg(VFS, "Could not allocate shash TFM '%s'\n", name);
- rc = PTR_ERR(alg);
- *sdesc = NULL;
- return rc;
- }
-
- *sdesc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(alg), GFP_KERNEL);
- if (*sdesc == NULL) {
- cifs_dbg(VFS, "no memory left to allocate shash TFM '%s'\n", name);
- crypto_free_shash(alg);
- return -ENOMEM;
- }
-
- (*sdesc)->tfm = alg;
- return 0;
-}
-
-/**
- * cifs_free_hash - free hash and hash context together
- * @sdesc: Where to find the pointer to the hash TFM
- *
- * Freeing a NULL descriptor is safe.
- */
-void
-cifs_free_hash(struct shash_desc **sdesc)
-{
- if (unlikely(!sdesc) || !*sdesc)
- return;
-
- if ((*sdesc)->tfm) {
- crypto_free_shash((*sdesc)->tfm);
- (*sdesc)->tfm = NULL;
- }
-
- kfree_sensitive(*sdesc);
- *sdesc = NULL;
-}
-
void extract_unc_hostname(const char *unc, const char **h, size_t *len)
{
const char *end;
diff --git a/fs/smb/client/sess.c b/fs/smb/client/sess.c
index 698bd27119ae..de2012cc9cf3 100644
--- a/fs/smb/client/sess.c
+++ b/fs/smb/client/sess.c
@@ -595,17 +595,6 @@ cifs_ses_add_channel(struct cifs_ses *ses,
spin_unlock(&ses->chan_lock);
mutex_lock(&ses->session_mutex);
- /*
- * We need to allocate the server crypto now as we will need
- * to sign packets before we generate the channel signing key
- * (we sign with the session key)
- */
- rc = smb3_crypto_shash_allocate(chan->server);
- if (rc) {
- cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- mutex_unlock(&ses->session_mutex);
- goto out;
- }
rc = cifs_negotiate_protocol(xid, ses, chan->server);
if (!rc)
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 9694117050a6..e198e3dda917 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -49,7 +49,6 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
- unsigned int sbflags;
cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
/*
@@ -76,29 +75,27 @@ void reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
cifs_dbg(VFS, "per-share encryption not supported yet\n");
- if (cifs_sb)
- sbflags = cifs_sb_flags(cifs_sb);
-
cap &= CIFS_UNIX_CAP_MASK;
if (ctx && ctx->no_psx_acl)
cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
cifs_dbg(FYI, "negotiated posix acl support\n");
- if (cifs_sb)
- sbflags |= CIFS_MOUNT_POSIXACL;
+ if (cifs_sb) {
+ atomic_or(CIFS_MOUNT_POSIXACL,
+ &cifs_sb->mnt_cifs_flags);
+ }
}
if (ctx && ctx->posix_paths == 0)
cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
cifs_dbg(FYI, "negotiate posix pathnames\n");
- if (cifs_sb)
- sbflags |= CIFS_MOUNT_POSIX_PATHS;
+ if (cifs_sb) {
+ atomic_or(CIFS_MOUNT_POSIX_PATHS,
+ &cifs_sb->mnt_cifs_flags);
+ }
}
- if (cifs_sb)
- atomic_set(&cifs_sb->mnt_cifs_flags, sbflags);
-
cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);
#ifdef CONFIG_CIFS_DEBUG2
if (cap & CIFS_UNIX_FCNTL_CAP)
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index a2105f4b54db..7f346ee50289 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1783,6 +1783,12 @@ replay_again:
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
if (le32_to_cpu(qi_rsp->OutputBufferLength) < qi.input_buffer_length)
qi.input_buffer_length = le32_to_cpu(qi_rsp->OutputBufferLength);
+ if (qi.input_buffer_length > 0 &&
+ struct_size(qi_rsp, Buffer, qi.input_buffer_length) >
+ rsp_iov[1].iov_len) {
+ rc = -EFAULT;
+ goto out;
+ }
if (copy_to_user(&pqi->input_buffer_length,
&qi.input_buffer_length,
sizeof(qi.input_buffer_length))) {
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index cd8b49722149..cb61051f9af3 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -2257,7 +2257,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
}
spin_unlock(&ses->chan_lock);
- invalidate_all_cached_dirs(tcon);
+ invalidate_all_cached_dirs(tcon, true);
rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, server,
(void **) &req,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 5f74475ba9d1..1ceb95b907e6 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -257,7 +257,6 @@ int smb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
char *data);
void smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
struct kstatfs *kst);
-int smb3_crypto_shash_allocate(struct TCP_Server_Info *server);
void smb311_update_preauth_hash(struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct kvec *iov, int nvec);
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 81be2b226e26..41009039b4cb 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -19,6 +19,7 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <crypto/aead.h>
+#include <crypto/aes-cbc-macs.h>
#include <crypto/sha2.h>
#include <crypto/utils.h>
#include "cifsglob.h"
@@ -28,14 +29,6 @@
#include "../common/smb2status.h"
#include "smb2glob.h"
-int
-smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
-{
- struct cifs_secmech *p = &server->secmech;
-
- return cifs_alloc_hash("cmac(aes)", &p->aes_cmac);
-}
-
static
int smb3_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
{
@@ -211,8 +204,7 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid)
}
static int
-smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int rc;
unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
@@ -258,14 +250,13 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
return rc;
}
-static int generate_key(struct cifs_ses *ses, struct kvec label,
- struct kvec context, __u8 *key, unsigned int key_size)
+static void generate_key(struct cifs_ses *ses, struct kvec label,
+ struct kvec context, __u8 *key, unsigned int key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
__u8 L128[4] = {0, 0, 0, 128};
__u8 L256[4] = {0, 0, 1, 0};
- int rc = 0;
unsigned char prfhash[SMB2_HMACSHA256_SIZE];
struct TCP_Server_Info *server = ses->server;
struct hmac_sha256_ctx hmac_ctx;
@@ -273,12 +264,6 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(key, 0x0, key_size);
- rc = smb3_crypto_shash_allocate(server);
- if (rc) {
- cifs_server_dbg(VFS, "%s: crypto alloc failed\n", __func__);
- return rc;
- }
-
hmac_sha256_init_usingrawkey(&hmac_ctx, ses->auth_key.response,
SMB2_NTLMV2_SESSKEY_SIZE);
hmac_sha256_update(&hmac_ctx, i, 4);
@@ -295,7 +280,6 @@ static int generate_key(struct cifs_ses *ses, struct kvec label,
hmac_sha256_final(&hmac_ctx, prfhash);
memcpy(key, prfhash, key_size);
- return 0;
}
struct derivation {
@@ -314,7 +298,6 @@ generate_smb3signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct derivation_triplet *ptriplet)
{
- int rc;
bool is_binding = false;
int chan_index = 0;
@@ -345,19 +328,14 @@ generate_smb3signingkey(struct cifs_ses *ses,
*/
if (is_binding) {
- rc = generate_key(ses, ptriplet->signing.label,
- ptriplet->signing.context,
- ses->chans[chan_index].signkey,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->signing.label,
+ ptriplet->signing.context,
+ ses->chans[chan_index].signkey,
+ SMB3_SIGN_KEY_SIZE);
} else {
- rc = generate_key(ses, ptriplet->signing.label,
- ptriplet->signing.context,
- ses->smb3signingkey,
- SMB3_SIGN_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->signing.label,
+ ptriplet->signing.context,
+ ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
/* safe to access primary channel, since it will never go away */
spin_lock(&ses->chan_lock);
@@ -365,18 +343,12 @@ generate_smb3signingkey(struct cifs_ses *ses,
SMB3_SIGN_KEY_SIZE);
spin_unlock(&ses->chan_lock);
- rc = generate_key(ses, ptriplet->encryption.label,
- ptriplet->encryption.context,
- ses->smb3encryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
- rc = generate_key(ses, ptriplet->decryption.label,
- ptriplet->decryption.context,
- ses->smb3decryptionkey,
- SMB3_ENC_DEC_KEY_SIZE);
- if (rc)
- return rc;
+ generate_key(ses, ptriplet->encryption.label,
+ ptriplet->encryption.context,
+ ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE);
+ generate_key(ses, ptriplet->decryption.label,
+ ptriplet->decryption.context,
+ ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
}
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
@@ -405,7 +377,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
SMB3_GCM128_CRYPTKEY_SIZE, ses->smb3decryptionkey);
}
#endif
- return rc;
+ return 0;
}
int
@@ -467,19 +439,19 @@ generate_smb311signingkey(struct cifs_ses *ses,
}
static int
-smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
- bool allocate_crypto)
+smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{
int rc;
unsigned char smb3_signature[SMB2_CMACAES_SIZE];
struct kvec *iov = rqst->rq_iov;
struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
- struct shash_desc *shash = NULL;
+ struct aes_cmac_key cmac_key;
+ struct aes_cmac_ctx cmac_ctx;
struct smb_rqst drqst;
u8 key[SMB3_SIGN_KEY_SIZE];
if (server->vals->protocol_id <= SMB21_PROT_ID)
- return smb2_calc_signature(rqst, server, allocate_crypto);
+ return smb2_calc_signature(rqst, server);
rc = smb3_get_sign_key(le64_to_cpu(shdr->SessionId), server, key);
if (unlikely(rc)) {
@@ -487,33 +459,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
return rc;
}
- if (allocate_crypto) {
- rc = cifs_alloc_hash("cmac(aes)", &shash);
- if (rc)
- return rc;
- } else {
- shash = server->secmech.aes_cmac;
- }
-
memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
- rc = crypto_shash_setkey(shash->tfm, key, SMB2_CMACAES_SIZE);
+ rc = aes_cmac_preparekey(&cmac_key, key, SMB2_CMACAES_SIZE);
if (rc) {
cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
- goto out;
+ return rc;
}
- /*
- * we already allocate aes_cmac when we init smb3 signing key,
- * so unlike smb2 case we do not have to check here if secmech are
- * initialized
- */
- rc = crypto_shash_init(shash);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
- goto out;
- }
+ aes_cmac_init(&cmac_ctx, &cmac_key);
/*
* For SMB2+, __cifs_calc_signature() expects to sign only the actual
@@ -524,26 +479,16 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
*/
drqst = *rqst;
if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
- rc = crypto_shash_update(shash, iov[0].iov_base,
- iov[0].iov_len);
- if (rc) {
- cifs_server_dbg(VFS, "%s: Could not update with payload\n",
- __func__);
- goto out;
- }
+ aes_cmac_update(&cmac_ctx, iov[0].iov_base, iov[0].iov_len);
drqst.rq_iov++;
drqst.rq_nvec--;
}
rc = __cifs_calc_signature(
&drqst, server, smb3_signature,
- &(struct cifs_calc_sig_ctx){ .shash = shash });
+ &(struct cifs_calc_sig_ctx){ .cmac = &cmac_ctx });
if (!rc)
memcpy(shdr->Signature, smb3_signature, SMB2_SIGNATURE_SIZE);
-
-out:
- if (allocate_crypto)
- cifs_free_hash(&shash);
return rc;
}
@@ -577,7 +522,7 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return 0;
}
- return smb3_calc_signature(rqst, server, false);
+ return smb3_calc_signature(rqst, server);
}
int
@@ -613,7 +558,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
- rc = smb3_calc_signature(rqst, server, true);
+ rc = smb3_calc_signature(rqst, server);
if (rc)
return rc;
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
index 85a4248d4f29..a4b12eb8df81 100644
--- a/fs/smb/common/smb2pdu.h
+++ b/fs/smb/common/smb2pdu.h
@@ -510,6 +510,8 @@ struct smb2_encryption_neg_context {
/* Pattern scanning algorithm See MS-SMB2 3.1.4.4.1 */
#define SMB3_COMPRESS_PATTERN cpu_to_le16(0x0004) /* Pattern_V1 */
#define SMB3_COMPRESS_LZ4 cpu_to_le16(0x0005)
+/* Account for NONE for easier array indexing */
+#define SMB3_COMPRESS_MAX_ALGS 6
/* Compression Flags */
#define SMB2_COMPRESSION_CAPABILITIES_FLAG_NONE cpu_to_le32(0x00000000)