seven client fixes

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmn+DfMACgkQiiy9cAdy
 T1FlwQv/bOScs7kYk5M5cCUf8kvA3kHBBmXcewSXYVzEaspJFd49IOrbejh07UXR
 KmfJ4zgX3usbFNzXkmm8AKrax9ZJd8vmdey7/+ELxuBoYiyyDTATZ/VG+yDae0Cu
 zU7pZNv99LppFkkxQM+7hpBtbazRUTZu3VYprFZ+UCWPupKZs/fQm9huBzJPf2bn
 dMkojp/AAOGmhuRok3DWA1fu/BvFgslXPk4QohIfWxd0zRGVXQLRkOXvVI34bhR2
 IOLH1PohkFsajqWClEyikCaFjhW8ZpmmHVl2t+NZer/wYoq2Mp2Ad9NkILmfrWR1
 w4NSxh73emsllZpDkXYULlM9voxnjIXpvg/wPP+DA4yhuThwluJyCgsEkoInMw6X
 mLM8JiD4EMQhxKiZwtrO4gd/TshSBhm01ly0a6VwvV2p1mvW2cJH2VAZyoC+xN8d
 CabEmVnJuiwh4SPwKwsJN3bePwvjp30j1oVRspQthTQRrunyY4hkXr3z2Hpo6TNb
 tMudF/Qh
 =A8aI
 -----END PGP SIGNATURE-----

Merge tag 'v7.1-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - Fix for two ACL issues (security fix to validate dacloffset better
   and chmod fix)

 - Fix out of bounds reads (in check_wsl_eas and smb2_check_msg for
   symlinks)

 - Two Kerberos fixes including an important one when AES-256 encryption
   chosen

 - Fix open_cached_dir problem when directory leases disabled

* tag 'v7.1-rc3-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: validate dacloffset before building DACL pointers
  smb/client: fix out-of-bounds read in smb2_compound_op()
  smb/client: fix out-of-bounds read in symlink_data()
  smb: client: Zero-pad short GSS session keys per MS-SMB2
  smb: client: Use FullSessionKey for AES-256 encryption key derivation
  smb: client: use kzalloc to zero-initialize security descriptor buffer
  cifs: abort open_cached_dir if we don't request leases
This commit is contained in:
Linus Torvalds 2026-05-08 10:24:35 -07:00
commit 81d6f78075
7 changed files with 96 additions and 24 deletions

View File

@ -286,6 +286,14 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
&rqst[0], &oplock, &oparms, utf16_path);
if (rc)
goto oshr_free;
if (oplock != SMB2_OPLOCK_LEVEL_II) {
rc = -EINVAL;
cifs_dbg(FYI, "%s: Oplock level %d not suitable for cached directory\n",
__func__, oplock);
goto oshr_free;
}
smb2_set_next_command(tcon, &rqst[0]);
memset(&qi_iov, 0, sizeof(qi_iov));

View File

@ -1264,6 +1264,17 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)
return 0;
}
static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
{
if (acl_len < sizeof(struct smb_acl))
return false;
if (dacloffset < sizeof(struct smb_ntsd))
return false;
return dacloffset <= acl_len - sizeof(struct smb_acl);
}
/* Convert CIFS ACL to POSIX form */
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
@ -1284,7 +1295,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
group_sid_ptr = (struct smb_sid *)((char *)pntsd +
le32_to_cpu(pntsd->gsidoffset));
dacloffset = le32_to_cpu(pntsd->dacloffset);
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
le32_to_cpu(pntsd->gsidoffset),
@ -1315,11 +1325,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
return rc;
}
if (dacloffset)
if (dacloffset) {
if (!dacl_offset_valid(acl_len, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
group_sid_ptr, fattr, get_mode_from_special_sid);
else
} else {
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
}
return rc;
}
@ -1342,6 +1359,11 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
return -EINVAL;
}
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, end_of_acl);
if (rc)
@ -1710,6 +1732,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
dacloffset = le32_to_cpu(pntsd->dacloffset);
if (dacloffset) {
if (!dacl_offset_valid(secdesclen, dacloffset)) {
cifs_dbg(VFS, "Server returned illegal DACL offset\n");
rc = -EINVAL;
goto id_mode_to_cifs_acl_exit;
}
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
if (rc) {
@ -1732,7 +1760,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
* descriptor parameters, and security descriptor itself
*/
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
if (!pnntsd) {
kfree(pntsd);
cifs_put_tlink(tlink);
@ -1752,6 +1780,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
}
id_mode_to_cifs_acl_exit:
cifs_put_tlink(tlink);
kfree(pnntsd);

View File

@ -296,7 +296,7 @@ static int cifs_dump_full_key(struct cifs_tcon *tcon, struct smb3_full_key_debug
break;
case SMB2_ENCRYPTION_AES256_CCM:
case SMB2_ENCRYPTION_AES256_GCM:
out.session_key_length = CIFS_SESS_KEY_SIZE;
out.session_key_length = ses->auth_key.len;
out.server_in_key_length = out.server_out_key_length = SMB3_GCM256_CRYPTKEY_SIZE;
break;
default:

View File

@ -111,7 +111,7 @@ static int check_wsl_eas(struct kvec *rsp_iov)
u32 outlen, next;
u16 vlen;
u8 nlen;
u8 *end;
u8 *ea_end, *iov_end;
outlen = le32_to_cpu(rsp->OutputBufferLength);
if (outlen < SMB2_WSL_MIN_QUERY_EA_RESP_SIZE ||
@ -120,15 +120,19 @@ static int check_wsl_eas(struct kvec *rsp_iov)
ea = (void *)((u8 *)rsp_iov->iov_base +
le16_to_cpu(rsp->OutputBufferOffset));
end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
ea_end = (u8 *)ea + outlen;
iov_end = (u8 *)rsp_iov->iov_base + rsp_iov->iov_len;
if (ea_end > iov_end)
return -EINVAL;
for (;;) {
if ((u8 *)ea > end - sizeof(*ea))
if ((u8 *)ea > ea_end - sizeof(*ea))
return -EINVAL;
nlen = ea->ea_name_length;
vlen = le16_to_cpu(ea->ea_value_length);
if (nlen != SMB2_WSL_XATTR_NAME_LEN ||
(u8 *)ea->ea_data + nlen + 1 + vlen > end)
(u8 *)ea->ea_data + nlen + 1 + vlen > ea_end)
return -EINVAL;
switch (vlen) {

View File

@ -241,7 +241,8 @@ smb2_check_message(char *buf, unsigned int pdu_len, unsigned int len,
if (len != calc_len) {
/* create failed on symlink */
if (command == SMB2_CREATE_HE &&
shdr->Status == STATUS_STOPPED_ON_SYMLINK)
shdr->Status == STATUS_STOPPED_ON_SYMLINK &&
len > calc_len)
return 0;
/* Windows 7 server returns 24 bytes more */
if (calc_len + 24 == len && command == SMB2_OPLOCK_BREAK_HE)

View File

@ -1713,17 +1713,30 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
is_binding = (ses->ses_status == SES_GOOD);
spin_unlock(&ses->ses_lock);
/*
* Per MS-SMB2 3.2.5.3, Session.SessionKey is the first 16 bytes of the
* GSS cryptographic key, right-padded with zero bytes if shorter.
* Allocate at least SMB2_NTLMV2_SESSKEY_SIZE bytes (zeroed) so the KDF
* input buffer is always valid for HMAC-SHA256 even with deprecated
* Kerberos enctypes that return a short session key.
*/
if (unlikely(msg->sesskey_len < SMB2_NTLMV2_SESSKEY_SIZE))
cifs_dbg(VFS,
"short GSS session key (%u bytes); zero-padding per MS-SMB2 3.2.5.3\n",
msg->sesskey_len);
kfree_sensitive(ses->auth_key.response);
ses->auth_key.response = kmemdup(msg->data,
msg->sesskey_len,
GFP_KERNEL);
ses->auth_key.len = max_t(unsigned int, msg->sesskey_len,
SMB2_NTLMV2_SESSKEY_SIZE);
ses->auth_key.response = kzalloc(ses->auth_key.len, GFP_KERNEL);
if (!ses->auth_key.response) {
cifs_dbg(VFS, "%s: can't allocate (%u bytes) memory\n",
__func__, msg->sesskey_len);
__func__, ses->auth_key.len);
ses->auth_key.len = 0;
rc = -ENOMEM;
goto out_put_spnego_key;
}
ses->auth_key.len = msg->sesskey_len;
memcpy(ses->auth_key.response, msg->data, msg->sesskey_len);
sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
sess_data->iov[1].iov_len = msg->secblob_len;

View File

@ -251,7 +251,8 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
}
static void generate_key(struct cifs_ses *ses, struct kvec label,
struct kvec context, __u8 *key, unsigned int key_size)
struct kvec context, __u8 *key, unsigned int key_size,
unsigned int full_key_size)
{
unsigned char zero = 0x0;
__u8 i[4] = {0, 0, 0, 1};
@ -265,7 +266,7 @@ static void generate_key(struct cifs_ses *ses, struct kvec label,
memset(key, 0x0, key_size);
hmac_sha256_init_usingrawkey(&hmac_ctx, ses->auth_key.response,
SMB2_NTLMV2_SESSKEY_SIZE);
full_key_size);
hmac_sha256_update(&hmac_ctx, i, 4);
hmac_sha256_update(&hmac_ctx, label.iov_base, label.iov_len);
hmac_sha256_update(&hmac_ctx, &zero, 1);
@ -298,6 +299,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct derivation_triplet *ptriplet)
{
unsigned int full_key_size = SMB2_NTLMV2_SESSKEY_SIZE;
bool is_binding = false;
int chan_index = 0;
@ -330,12 +332,24 @@ generate_smb3signingkey(struct cifs_ses *ses,
if (is_binding) {
generate_key(ses, ptriplet->signing.label,
ptriplet->signing.context,
ses->chans[chan_index].signkey,
SMB3_SIGN_KEY_SIZE);
ses->chans[chan_index].signkey, SMB3_SIGN_KEY_SIZE,
SMB2_NTLMV2_SESSKEY_SIZE);
} else {
generate_key(ses, ptriplet->signing.label,
ptriplet->signing.context,
ses->smb3signingkey, SMB3_SIGN_KEY_SIZE);
ptriplet->signing.context, ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE, SMB2_NTLMV2_SESSKEY_SIZE);
/*
* Per MS-SMB2 3.2.5.3.1, signing key always uses Session.SessionKey
* (first 16 bytes). Encryption/decryption keys use
* Session.FullSessionKey when dialect is 3.1.1 and cipher is
* AES-256-CCM or AES-256-GCM, otherwise Session.SessionKey.
*/
if (server->dialect == SMB311_PROT_ID &&
(server->cipher_type == SMB2_ENCRYPTION_AES256_CCM ||
server->cipher_type == SMB2_ENCRYPTION_AES256_GCM))
full_key_size = ses->auth_key.len;
/* safe to access primary channel, since it will never go away */
spin_lock(&ses->chan_lock);
@ -345,10 +359,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
generate_key(ses, ptriplet->encryption.label,
ptriplet->encryption.context,
ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE);
ses->smb3encryptionkey, SMB3_ENC_DEC_KEY_SIZE,
full_key_size);
generate_key(ses, ptriplet->decryption.label,
ptriplet->decryption.context,
ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE);
ses->smb3decryptionkey, SMB3_ENC_DEC_KEY_SIZE,
full_key_size);
}
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
@ -361,7 +378,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
&ses->Suid);
cifs_dbg(VFS, "Cipher type %d\n", server->cipher_type);
cifs_dbg(VFS, "Session Key %*ph\n",
SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
(int)ses->auth_key.len, ses->auth_key.response);
cifs_dbg(VFS, "Signing Key %*ph\n",
SMB3_SIGN_KEY_SIZE, ses->smb3signingkey);
if ((server->cipher_type == SMB2_ENCRYPTION_AES256_CCM) ||