Skip to content

Commit

Permalink
Merge tag '6.7-rc-smb3-client-fixes-part1' of git://git.samba.org/sfr…
Browse files Browse the repository at this point in the history
…ench/cifs-2.6

Pull smb client updates from Steve French:

 - use after free fixes and deadlock fix

 - symlink timestamp fix

 - hashing perf improvement

 - multichannel fixes

 - minor debugging improvements

 - fix creating fifos when using "sfu" mounts

 - NTLMSSP authentication improvement

 - minor fixes to include some missing create flags and structures from
   recently updated protocol documentation

* tag '6.7-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: force interface update before a fresh session setup
  cifs: do not reset chan_max if multichannel is not supported at mount
  cifs: reconnect helper should set reconnect for the right channel
  smb: client: fix use-after-free in smb2_query_info_compound()
  smb: client: remove extra @chan_count check in __cifs_put_smb_ses()
  cifs: add xid to query server interface call
  cifs: print server capabilities in DebugData
  smb: use crypto_shash_digest() in symlink_hash()
  smb: client: fix use-after-free bug in cifs_debug_data_proc_show()
  smb: client: fix potential deadlock when releasing mids
  smb3: fix creating FIFOs when mounting with "sfu" mount option
  Add definition for new smb3.1.1 command type
  SMB3: clarify some of the unused CreateOption flags
  cifs: Add client version details to NTLM authenticate message
  smb3: fix touch -h of symlink
  • Loading branch information
torvalds committed Nov 4, 2023
2 parents 4c975a4 + d9a6d78 commit 766e9cf
Show file tree
Hide file tree
Showing 14 changed files with 138 additions and 90 deletions.
84 changes: 49 additions & 35 deletions fs/smb/client/cached_dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
* fully cached or it may be in the process of
* being deleted due to a lease break.
*/
if (!cfid->has_lease) {
if (!cfid->time || !cfid->has_lease) {
spin_unlock(&cfids->cfid_list_lock);
return NULL;
}
Expand Down Expand Up @@ -193,10 +193,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
npath = path_no_prefix(cifs_sb, path);
if (IS_ERR(npath)) {
rc = PTR_ERR(npath);
kfree(utf16_path);
return rc;
goto out;
}

if (!npath[0]) {
dentry = dget(cifs_sb->root);
} else {
dentry = path_to_dentry(cifs_sb, npath);
if (IS_ERR(dentry)) {
rc = -ENOENT;
goto out;
}
}
cfid->dentry = dentry;

/*
* We do not hold the lock for the open because in case
* SMB2_open needs to reconnect.
Expand Down Expand Up @@ -249,6 +259,15 @@ 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;

rc = compound_send_recv(xid, ses, server,
flags, 2, rqst,
resp_buftype, rsp_iov);
Expand All @@ -263,89 +282,84 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
cfid->tcon = tcon;
cfid->is_open = true;

spin_lock(&cfids->cfid_list_lock);

o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
oparms.fid->persistent_fid = o_rsp->PersistentFileId;
oparms.fid->volatile_fid = o_rsp->VolatileFileId;
#ifdef CONFIG_CIFS_DEBUG2
oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
#endif /* CIFS_DEBUG2 */

if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE)
rc = -EINVAL;
if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) {
spin_unlock(&cfids->cfid_list_lock);
goto oshr_free;
}

smb2_parse_contexts(server, o_rsp,
&oparms.fid->epoch,
oparms.fid->lease_key, &oplock,
NULL, NULL);
if (!(oplock & SMB2_LEASE_READ_CACHING_HE))
if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) {
spin_unlock(&cfids->cfid_list_lock);
goto oshr_free;
}
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) {
spin_unlock(&cfids->cfid_list_lock);
goto oshr_free;
}
if (!smb2_validate_and_copy_iov(
le16_to_cpu(qi_rsp->OutputBufferOffset),
sizeof(struct smb2_file_all_info),
&rsp_iov[1], sizeof(struct smb2_file_all_info),
(char *)&cfid->file_all_info))
cfid->file_all_info_is_valid = true;

if (!npath[0])
dentry = dget(cifs_sb->root);
else {
dentry = path_to_dentry(cifs_sb, npath);
if (IS_ERR(dentry)) {
rc = -ENOENT;
goto oshr_free;
}
}
spin_lock(&cfids->cfid_list_lock);
cfid->dentry = dentry;
cfid->time = jiffies;
cfid->has_lease = true;
spin_unlock(&cfids->cfid_list_lock);
/* At this point the directory handle is fully cached */
rc = 0;

oshr_free:
kfree(utf16_path);
SMB2_open_free(&rqst[0]);
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);
spin_lock(&cfids->cfid_list_lock);
if (!cfid->has_lease) {
if (rc) {
if (cfid->on_list) {
list_del(&cfid->entry);
cfid->on_list = false;
cfids->num_entries--;
}
rc = -ENOENT;
} else {
if (rc) {
spin_lock(&cfids->cfid_list_lock);
if (cfid->on_list) {
list_del(&cfid->entry);
cfid->on_list = false;
cfids->num_entries--;
}
if (cfid->has_lease) {
/*
* 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.
*/
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);
}
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);
cfid = NULL;
}
out:
if (rc == 0) {
} else {
*ret_cfid = cfid;
atomic_inc(&tcon->num_remote_opens);
}

kfree(utf16_path);
return rc;
}

Expand Down
8 changes: 8 additions & 0 deletions fs/smb/client/cifs_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
if (server->nosharesock)
seq_printf(m, " nosharesock");

seq_printf(m, "\nServer capabilities: 0x%x", server->capabilities);

if (server->rdma)
seq_printf(m, "\nRDMA ");
seq_printf(m, "\nTCP status: %d Instance: %d"
Expand All @@ -452,6 +454,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "\n\n\tSessions: ");
i = 0;
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_EXITING) {
spin_unlock(&ses->ses_lock);
continue;
}
i++;
if ((ses->serverDomain == NULL) ||
(ses->serverOS == NULL) ||
Expand All @@ -472,6 +479,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
ses->ses_count, ses->serverOS, ses->serverNOS,
ses->capabilities, ses->ses_status);
}
spin_unlock(&ses->ses_lock);

seq_printf(m, "\n\tSecurity type: %s ",
get_security_type_str(server->ops->select_sectype(server, ses->sectype)));
Expand Down
1 change: 1 addition & 0 deletions fs/smb/client/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,7 @@ const char *cifs_get_link(struct dentry *dentry, struct inode *inode,

const struct inode_operations cifs_symlink_inode_ops = {
.get_link = cifs_get_link,
.setattr = cifs_setattr,
.permission = cifs_permission,
.listxattr = cifs_listxattr,
};
Expand Down
2 changes: 1 addition & 1 deletion fs/smb/client/cifspdu.h
Original file line number Diff line number Diff line change
Expand Up @@ -2570,7 +2570,7 @@ typedef struct {


struct win_dev {
unsigned char type[8]; /* IntxCHR or IntxBLK */
unsigned char type[8]; /* IntxCHR or IntxBLK or LnxFIFO*/
__le64 major;
__le64 minor;
} __attribute__((packed));
Expand Down
7 changes: 6 additions & 1 deletion fs/smb/client/cifsproto.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
char *cifs_build_devname(char *nodename, const char *prepath);
extern void delete_mid(struct mid_q_entry *mid);
extern void release_mid(struct mid_q_entry *mid);
void __release_mid(struct kref *refcount);
extern void cifs_wake_up_task(struct mid_q_entry *mid);
extern int cifs_handle_standard(struct TCP_Server_Info *server,
struct mid_q_entry *mid);
Expand Down Expand Up @@ -740,4 +740,9 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
return true;
}

static inline void release_mid(struct mid_q_entry *mid)
{
kref_put(&mid->refcount, __release_mid);
}

#endif /* _CIFSPROTO_H */
44 changes: 24 additions & 20 deletions fs/smb/client/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,18 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
static void smb2_query_server_interfaces(struct work_struct *work)
{
int rc;
int xid;
struct cifs_tcon *tcon = container_of(work,
struct cifs_tcon,
query_interfaces.work);

/*
* query server network interfaces, in case they change
*/
rc = SMB3_request_interfaces(0, tcon, false);
xid = get_xid();
rc = SMB3_request_interfaces(xid, tcon, false);
free_xid(xid);

if (rc) {
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
__func__, rc);
Expand Down Expand Up @@ -156,13 +160,14 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;

spin_lock(&pserver->srv_lock);
/* if we need to signal just this channel */
if (!all_channels) {
pserver->tcpStatus = CifsNeedReconnect;
spin_unlock(&pserver->srv_lock);
spin_lock(&server->srv_lock);
if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&server->srv_lock);
return;
}
spin_unlock(&pserver->srv_lock);

spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
Expand Down Expand Up @@ -1969,9 +1974,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)

void __cifs_put_smb_ses(struct cifs_ses *ses)
{
unsigned int rc, xid;
unsigned int chan_count;
struct TCP_Server_Info *server = ses->server;
unsigned int xid;
size_t i;
int rc;

spin_lock(&ses->ses_lock);
if (ses->ses_status == SES_EXITING) {
Expand Down Expand Up @@ -2017,20 +2023,14 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
list_del_init(&ses->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock);

chan_count = ses->chan_count;

/* close any extra channels */
if (chan_count > 1) {
int i;

for (i = 1; i < chan_count; i++) {
if (ses->chans[i].iface) {
kref_put(&ses->chans[i].iface->refcount, release_iface);
ses->chans[i].iface = NULL;
}
cifs_put_tcp_session(ses->chans[i].server, 0);
ses->chans[i].server = NULL;
for (i = 1; i < ses->chan_count; i++) {
if (ses->chans[i].iface) {
kref_put(&ses->chans[i].iface->refcount, release_iface);
ses->chans[i].iface = NULL;
}
cifs_put_tcp_session(ses->chans[i].server, 0);
ses->chans[i].server = NULL;
}

sesInfoFree(ses);
Expand Down Expand Up @@ -3849,8 +3849,12 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);

if (!is_binding)
if (!is_binding) {
ses->ses_status = SES_IN_SETUP;

/* force iface_list refresh */
ses->iface_last_update = 0;
}
spin_unlock(&ses->ses_lock);

/* update ses ip_addr only for primary chan */
Expand Down
4 changes: 4 additions & 0 deletions fs/smb/client/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,10 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
cifs_dbg(FYI, "Symlink\n");
fattr->cf_mode |= S_IFLNK;
fattr->cf_dtype = DT_LNK;
} else if (memcmp("LnxFIFO", pbuf, 8) == 0) {
cifs_dbg(FYI, "FIFO\n");
fattr->cf_mode |= S_IFIFO;
fattr->cf_dtype = DT_FIFO;
} else {
fattr->cf_mode |= S_IFREG; /* file? */
fattr->cf_dtype = DT_REG;
Expand Down
16 changes: 2 additions & 14 deletions fs/smb/client/link.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,11 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)

rc = cifs_alloc_hash("md5", &md5);
if (rc)
goto symlink_hash_err;
return rc;

rc = crypto_shash_init(md5);
if (rc) {
cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
goto symlink_hash_err;
}
rc = crypto_shash_update(md5, link_str, link_len);
if (rc) {
cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
goto symlink_hash_err;
}
rc = crypto_shash_final(md5, md5_hash);
rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);
if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);

symlink_hash_err:
cifs_free_hash(&md5);
return rc;
}
Expand Down
4 changes: 2 additions & 2 deletions fs/smb/client/ntlmssp.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ typedef struct _AUTHENTICATE_MESSAGE {
SECURITY_BUFFER WorkstationName;
SECURITY_BUFFER SessionKey;
__le32 NegotiateFlags;
/* SECURITY_BUFFER for version info not present since we
do not set the version is present flag */
struct ntlmssp_version Version;
/* SECURITY_BUFFER */
char UserString[];
} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;

Expand Down
13 changes: 9 additions & 4 deletions fs/smb/client/sess.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,6 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
}

if (!(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
ses->chan_max = 1;
spin_unlock(&ses->chan_lock);
cifs_server_dbg(VFS, "no multichannel support\n");
return 0;
Expand Down Expand Up @@ -1060,10 +1059,16 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer,
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmAuthenticate;

/* send version information in ntlmssp authenticate also */
flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
/* we only send version information in ntlmssp negotiate, so do not set this flag */
flags = flags & ~NTLMSSP_NEGOTIATE_VERSION;
NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_VERSION |
NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;

sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;

tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags);

Expand Down
Loading

0 comments on commit 766e9cf

Please sign in to comment.