Skip to content

Commit

Permalink
Merge tag '6.13-rc-part1-SMB3-client-fixes' of git://git.samba.org/sf…
Browse files Browse the repository at this point in the history
…rench/cifs-2.6

Pull smb client updates from Steve French:

 - Fix two SMB3.1.1 POSIX Extensions problems

 - Fixes for special file handling (symlinks and FIFOs)

 - Improve compounding

 - Four cleanup patches

 - Fix use after free in signing

 - Add support for handling namespaces for reconnect related upcalls
   (e.g. for DNS names resolution and auth)

 - Fix various directory lease problems (directory entry caching),
   including some important potential use after frees

* tag '6.13-rc-part1-SMB3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: prevent use-after-free due to open_cached_dir error paths
  smb: Don't leak cfid when reconnect races with open_cached_dir
  smb: client: handle max length for SMB symlinks
  smb: client: get rid of bounds check in SMB2_ioctl_init()
  smb: client: improve compound padding in encryption
  smb3: request handle caching when caching directories
  cifs: Recognize SFU char/block devices created by Windows NFS server on Windows Server <<2012
  CIFS: New mount option for cifs.upcall namespace resolution
  smb/client: Prevent error pointer dereference
  fs/smb/client: implement chmod() for SMB3 POSIX Extensions
  smb: cached directories can be more than root file handle
  smb: client: fix use-after-free of signing key
  smb: client: Use str_yes_no() helper function
  smb: client: memcpy() with surrounding object base address
  cifs: Remove pre-historic unused CIFSSMBCopy
  • Loading branch information
Linus Torvalds committed Nov 23, 2024
2 parents e767523 + a9685b4 commit 228a115
Show file tree
Hide file tree
Showing 19 changed files with 293 additions and 246 deletions.
99 changes: 44 additions & 55 deletions fs/smb/client/cached_dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
list_add(&cfid->entry, &cfids->entries);
cfid->on_list = true;
kref_get(&cfid->refcount);
/*
* Set @cfid->has_lease to true during construction so that the lease
* reference can be put in cached_dir_lease_break() due to a potential
* lease break right after the request is sent or while @cfid is still
* being cached, or if a reconnection is triggered during construction.
* Concurrent processes won't be to use it yet due to @cfid->time being
* zero.
*/
cfid->has_lease = true;

spin_unlock(&cfids->cfid_list_lock);
return cfid;
}
Expand Down Expand Up @@ -176,12 +186,12 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
return -ENOENT;
}
/*
* Return cached fid if it has a lease. Otherwise, it is either a new
* entry or laundromat worker removed it from @cfids->entries. Caller
* will put last reference if the latter.
* Return cached fid if it is valid (has a lease and has a time).
* Otherwise, it is either a new entry or laundromat worker removed it
* from @cfids->entries. Caller will put last reference if the latter.
*/
spin_lock(&cfids->cfid_list_lock);
if (cfid->has_lease) {
if (cfid->has_lease && cfid->time) {
spin_unlock(&cfids->cfid_list_lock);
*ret_cfid = cfid;
kfree(utf16_path);
Expand Down Expand Up @@ -267,15 +277,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,

smb2_set_related(&rqst[1]);

/*
* Set @cfid->has_lease to true before sending out compounded request so
* its lease reference can be put in cached_dir_lease_break() due to a
* potential lease break right after the request is sent or while @cfid
* is still being cached. Concurrent processes won't be to use it yet
* due to @cfid->time being zero.
*/
cfid->has_lease = true;

if (retries) {
smb2_set_replay(server, &rqst[0]);
smb2_set_replay(server, &rqst[1]);
Expand Down Expand Up @@ -347,6 +348,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
SMB2_query_info_free(&rqst[1]);
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
out:
if (rc) {
spin_lock(&cfids->cfid_list_lock);
if (cfid->on_list) {
Expand All @@ -358,23 +360,14 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
/*
* We are guaranteed to have two references at this
* point. One for the caller and one for a potential
* lease. Release the Lease-ref so that the directory
* will be closed when the caller closes the cached
* handle.
* lease. Release one here, and the second below.
*/
cfid->has_lease = false;
spin_unlock(&cfids->cfid_list_lock);
kref_put(&cfid->refcount, smb2_close_cached_fid);
goto out;
}
spin_unlock(&cfids->cfid_list_lock);
}
out:
if (rc) {
if (cfid->is_open)
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
cfid->fid.volatile_fid);
free_cached_dir(cfid);

kref_put(&cfid->refcount, smb2_close_cached_fid);
} else {
*ret_cfid = cfid;
atomic_inc(&tcon->num_remote_opens);
Expand All @@ -401,7 +394,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
spin_lock(&cfids->cfid_list_lock);
list_for_each_entry(cfid, &cfids->entries, entry) {
if (dentry && cfid->dentry == dentry) {
cifs_dbg(FYI, "found a cached root file handle by dentry\n");
cifs_dbg(FYI, "found a cached file handle by dentry\n");
kref_get(&cfid->refcount);
*ret_cfid = cfid;
spin_unlock(&cfids->cfid_list_lock);
Expand Down Expand Up @@ -512,25 +505,24 @@ void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
cfids->num_entries--;
cfid->is_open = false;
cfid->on_list = false;
/* To prevent race with smb2_cached_lease_break() */
kref_get(&cfid->refcount);
if (cfid->has_lease) {
/*
* The lease was never cancelled from the server,
* so steal that reference.
*/
cfid->has_lease = false;
} else
kref_get(&cfid->refcount);
}
spin_unlock(&cfids->cfid_list_lock);

list_for_each_entry_safe(cfid, q, &entry, entry) {
list_del(&cfid->entry);
cancel_work_sync(&cfid->lease_break);
if (cfid->has_lease) {
/*
* We lease was never cancelled from the server so we
* need to drop the reference.
*/
spin_lock(&cfids->cfid_list_lock);
cfid->has_lease = false;
spin_unlock(&cfids->cfid_list_lock);
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
/* Drop the extra reference opened above*/
/*
* Drop the ref-count from above, either the lease-ref (if there
* was one) or the extra one acquired.
*/
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
}
Expand All @@ -541,9 +533,6 @@ smb2_cached_lease_break(struct work_struct *work)
struct cached_fid *cfid = container_of(work,
struct cached_fid, lease_break);

spin_lock(&cfid->cfids->cfid_list_lock);
cfid->has_lease = false;
spin_unlock(&cfid->cfids->cfid_list_lock);
kref_put(&cfid->refcount, smb2_close_cached_fid);
}

Expand All @@ -561,6 +550,7 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
!memcmp(lease_key,
cfid->fid.lease_key,
SMB2_LEASE_KEY_SIZE)) {
cfid->has_lease = false;
cfid->time = 0;
/*
* We found a lease remove it from the list
Expand Down Expand Up @@ -638,8 +628,14 @@ static void cfids_laundromat_worker(struct work_struct *work)
cfid->on_list = false;
list_move(&cfid->entry, &entry);
cfids->num_entries--;
/* To prevent race with smb2_cached_lease_break() */
kref_get(&cfid->refcount);
if (cfid->has_lease) {
/*
* Our lease has not yet been cancelled from the
* server. Steal that reference.
*/
cfid->has_lease = false;
} else
kref_get(&cfid->refcount);
}
}
spin_unlock(&cfids->cfid_list_lock);
Expand All @@ -651,17 +647,10 @@ static void cfids_laundromat_worker(struct work_struct *work)
* with it.
*/
cancel_work_sync(&cfid->lease_break);
if (cfid->has_lease) {
/*
* Our lease has not yet been cancelled from the server
* so we need to drop the reference.
*/
spin_lock(&cfids->cfid_list_lock);
cfid->has_lease = false;
spin_unlock(&cfids->cfid_list_lock);
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
/* Drop the extra reference opened above */
/*
* Drop the ref-count from above, either the lease-ref (if there
* was one) or the extra one acquired.
*/
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
queue_delayed_work(cifsiod_wq, &cfids->laundromat_work,
Expand Down
16 changes: 16 additions & 0 deletions fs/smb/client/cifs_spnego.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ struct key_type cifs_spnego_key_type = {
/* strlen of ";pid=0x" */
#define PID_KEY_LEN 7

/* strlen of ";upcall_target=" */
#define UPCALL_TARGET_KEY_LEN 15

/* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key *
cifs_get_spnego_key(struct cifs_ses *sesInfo,
Expand All @@ -108,6 +111,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
if (sesInfo->user_name)
desc_len += USER_KEY_LEN + strlen(sesInfo->user_name);

if (sesInfo->upcall_target == UPTARGET_MOUNT)
desc_len += UPCALL_TARGET_KEY_LEN + 5; // strlen("mount")
else
desc_len += UPCALL_TARGET_KEY_LEN + 3; // strlen("app")

spnego_key = ERR_PTR(-ENOMEM);
description = kzalloc(desc_len, GFP_KERNEL);
if (description == NULL)
Expand Down Expand Up @@ -156,6 +164,14 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo,
dp = description + strlen(description);
sprintf(dp, ";pid=0x%x", current->pid);

if (sesInfo->upcall_target == UPTARGET_MOUNT) {
dp = description + strlen(description);
sprintf(dp, ";upcall_target=mount");
} else {
dp = description + strlen(description);
sprintf(dp, ";upcall_target=app");
}

cifs_dbg(FYI, "key description = %s\n", description);
saved_cred = override_creds(spnego_cred);
spnego_key = request_key(&cifs_spnego_key_type, description, "");
Expand Down
54 changes: 33 additions & 21 deletions fs/smb/client/cifsacl.c
Original file line number Diff line number Diff line change
Expand Up @@ -885,12 +885,17 @@ unsigned int setup_authusers_ACE(struct smb_ace *pntace)
* Fill in the special SID based on the mode. See
* https://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx
*/
unsigned int setup_special_mode_ACE(struct smb_ace *pntace, __u64 nmode)
unsigned int setup_special_mode_ACE(struct smb_ace *pntace,
bool posix,
__u64 nmode)
{
int i;
unsigned int ace_size = 28;

pntace->type = ACCESS_DENIED_ACE_TYPE;
if (posix)
pntace->type = ACCESS_ALLOWED_ACE_TYPE;
else
pntace->type = ACCESS_DENIED_ACE_TYPE;
pntace->flags = 0x0;
pntace->access_req = 0;
pntace->sid.num_subauth = 3;
Expand Down Expand Up @@ -933,7 +938,8 @@ static void populate_new_aces(char *nacl_base,
struct smb_sid *pownersid,
struct smb_sid *pgrpsid,
__u64 *pnmode, u32 *pnum_aces, u16 *pnsize,
bool modefromsid)
bool modefromsid,
bool posix)
{
__u64 nmode;
u32 num_aces = 0;
Expand All @@ -950,13 +956,15 @@ static void populate_new_aces(char *nacl_base,
num_aces = *pnum_aces;
nsize = *pnsize;

if (modefromsid) {
pnntace = (struct smb_ace *) (nacl_base + nsize);
nsize += setup_special_mode_ACE(pnntace, nmode);
num_aces++;
if (modefromsid || posix) {
pnntace = (struct smb_ace *) (nacl_base + nsize);
nsize += setup_authusers_ACE(pnntace);
nsize += setup_special_mode_ACE(pnntace, posix, nmode);
num_aces++;
if (modefromsid) {
pnntace = (struct smb_ace *) (nacl_base + nsize);
nsize += setup_authusers_ACE(pnntace);
num_aces++;
}
goto set_size;
}

Expand Down Expand Up @@ -1076,7 +1084,7 @@ static __u16 replace_sids_and_copy_aces(struct smb_acl *pdacl, struct smb_acl *p

static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
__u64 *pnmode, bool mode_from_sid)
__u64 *pnmode, bool mode_from_sid, bool posix)
{
int i;
u16 size = 0;
Expand All @@ -1094,11 +1102,11 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
nsize = sizeof(struct smb_acl);

/* If pdacl is NULL, we don't have a src. Simply populate new ACL. */
if (!pdacl) {
if (!pdacl || posix) {
populate_new_aces(nacl_base,
pownersid, pgrpsid,
pnmode, &num_aces, &nsize,
mode_from_sid);
mode_from_sid, posix);
goto finalize_dacl;
}

Expand All @@ -1115,7 +1123,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
populate_new_aces(nacl_base,
pownersid, pgrpsid,
pnmode, &num_aces, &nsize,
mode_from_sid);
mode_from_sid, posix);

new_aces_set = true;
}
Expand Down Expand Up @@ -1144,7 +1152,7 @@ static int set_chmod_dacl(struct smb_acl *pdacl, struct smb_acl *pndacl,
populate_new_aces(nacl_base,
pownersid, pgrpsid,
pnmode, &num_aces, &nsize,
mode_from_sid);
mode_from_sid, posix);

new_aces_set = true;
}
Expand Down Expand Up @@ -1251,7 +1259,7 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
/* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
__u32 secdesclen, __u32 *pnsecdesclen, __u64 *pnmode, kuid_t uid, kgid_t gid,
bool mode_from_sid, bool id_from_sid, int *aclflag)
bool mode_from_sid, bool id_from_sid, bool posix, int *aclflag)
{
int rc = 0;
__u32 dacloffset;
Expand Down Expand Up @@ -1288,7 +1296,7 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
ndacl_ptr->num_aces = cpu_to_le32(0);

rc = set_chmod_dacl(dacl_ptr, ndacl_ptr, owner_sid_ptr, group_sid_ptr,
pnmode, mode_from_sid);
pnmode, mode_from_sid, posix);

sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
/* copy the non-dacl portion of secdesc */
Expand Down Expand Up @@ -1584,13 +1592,16 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
struct smb_ntsd *pntsd = NULL; /* acl obtained from server */
struct smb_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
struct tcon_link *tlink;
struct smb_version_operations *ops;
bool mode_from_sid, id_from_sid;
const u32 info = 0;
bool posix;

tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
posix = tlink_tcon(tlink)->posix_extensions;

ops = tlink_tcon(tlink)->ses->server->ops;

Expand Down Expand Up @@ -1622,12 +1633,13 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
id_from_sid = false;

/* Potentially, five new ACEs can be added to the ACL for U,G,O mapping */
nsecdesclen = secdesclen;
if (pnmode && *pnmode != NO_CHANGE_64) { /* chmod */
if (mode_from_sid)
nsecdesclen += 2 * sizeof(struct smb_ace);
if (posix)
nsecdesclen = 1 * sizeof(struct smb_ace);
else if (mode_from_sid)
nsecdesclen = secdesclen + (2 * sizeof(struct smb_ace));
else /* cifsacl */
nsecdesclen += 5 * sizeof(struct smb_ace);
nsecdesclen = secdesclen + (5 * sizeof(struct smb_ace));
} else { /* chown */
/* When ownership changes, changes new owner sid length could be different */
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
Expand Down Expand Up @@ -1657,7 +1669,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
}

rc = build_sec_desc(pntsd, pnntsd, secdesclen, &nsecdesclen, pnmode, uid, gid,
mode_from_sid, id_from_sid, &aclflag);
mode_from_sid, id_from_sid, posix, &aclflag);

cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc);

Expand Down
Loading

0 comments on commit 228a115

Please sign in to comment.