diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/smb/client/Kconfig | 2 | ||||
| -rw-r--r-- | fs/smb/client/cached_dir.c | 5 | ||||
| -rw-r--r-- | fs/smb/client/cached_dir.h | 2 | ||||
| -rw-r--r-- | fs/smb/client/cifs_debug.c | 56 | ||||
| -rw-r--r-- | fs/smb/client/cifs_unicode.c | 1 | ||||
| -rw-r--r-- | fs/smb/client/cifsacl.c | 115 | ||||
| -rw-r--r-- | fs/smb/client/cifsencrypt.c | 62 | ||||
| -rw-r--r-- | fs/smb/client/cifsfs.c | 38 | ||||
| -rw-r--r-- | fs/smb/client/cifsfs.h | 4 | ||||
| -rw-r--r-- | fs/smb/client/cifsglob.h | 7 | ||||
| -rw-r--r-- | fs/smb/client/cifsproto.h | 3 | ||||
| -rw-r--r-- | fs/smb/client/compress.c | 6 | ||||
| -rw-r--r-- | fs/smb/client/compress/lz77.c | 274 | ||||
| -rw-r--r-- | fs/smb/client/compress/lz77.h | 32 | ||||
| -rw-r--r-- | fs/smb/client/connect.c | 10 | ||||
| -rw-r--r-- | fs/smb/client/file.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/misc.c | 57 | ||||
| -rw-r--r-- | fs/smb/client/sess.c | 11 | ||||
| -rw-r--r-- | fs/smb/client/smb1ops.c | 19 | ||||
| -rw-r--r-- | fs/smb/client/smb2ops.c | 6 | ||||
| -rw-r--r-- | fs/smb/client/smb2pdu.c | 2 | ||||
| -rw-r--r-- | fs/smb/client/smb2proto.h | 1 | ||||
| -rw-r--r-- | fs/smb/client/smb2transport.c | 113 | ||||
| -rw-r--r-- | fs/smb/common/smb2pdu.h | 2 |
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) |
