Skip to content

Commit 766e9cf

Browse files
committed
Merge tag '6.7-rc-smb3-client-fixes-part1' of git://git.samba.org/sfrench/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
2 parents 4c975a4 + d9a6d78 commit 766e9cf

File tree

14 files changed

+138
-90
lines changed

14 files changed

+138
-90
lines changed

fs/smb/client/cached_dir.c

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
3232
* fully cached or it may be in the process of
3333
* being deleted due to a lease break.
3434
*/
35-
if (!cfid->has_lease) {
35+
if (!cfid->time || !cfid->has_lease) {
3636
spin_unlock(&cfids->cfid_list_lock);
3737
return NULL;
3838
}
@@ -193,10 +193,20 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
193193
npath = path_no_prefix(cifs_sb, path);
194194
if (IS_ERR(npath)) {
195195
rc = PTR_ERR(npath);
196-
kfree(utf16_path);
197-
return rc;
196+
goto out;
198197
}
199198

199+
if (!npath[0]) {
200+
dentry = dget(cifs_sb->root);
201+
} else {
202+
dentry = path_to_dentry(cifs_sb, npath);
203+
if (IS_ERR(dentry)) {
204+
rc = -ENOENT;
205+
goto out;
206+
}
207+
}
208+
cfid->dentry = dentry;
209+
200210
/*
201211
* We do not hold the lock for the open because in case
202212
* SMB2_open needs to reconnect.
@@ -249,6 +259,15 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
249259

250260
smb2_set_related(&rqst[1]);
251261

262+
/*
263+
* Set @cfid->has_lease to true before sending out compounded request so
264+
* its lease reference can be put in cached_dir_lease_break() due to a
265+
* potential lease break right after the request is sent or while @cfid
266+
* is still being cached. Concurrent processes won't be to use it yet
267+
* due to @cfid->time being zero.
268+
*/
269+
cfid->has_lease = true;
270+
252271
rc = compound_send_recv(xid, ses, server,
253272
flags, 2, rqst,
254273
resp_buftype, rsp_iov);
@@ -263,89 +282,84 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
263282
cfid->tcon = tcon;
264283
cfid->is_open = true;
265284

285+
spin_lock(&cfids->cfid_list_lock);
286+
266287
o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
267288
oparms.fid->persistent_fid = o_rsp->PersistentFileId;
268289
oparms.fid->volatile_fid = o_rsp->VolatileFileId;
269290
#ifdef CONFIG_CIFS_DEBUG2
270291
oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
271292
#endif /* CIFS_DEBUG2 */
272293

273-
if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE)
294+
rc = -EINVAL;
295+
if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) {
296+
spin_unlock(&cfids->cfid_list_lock);
274297
goto oshr_free;
298+
}
275299

276300
smb2_parse_contexts(server, o_rsp,
277301
&oparms.fid->epoch,
278302
oparms.fid->lease_key, &oplock,
279303
NULL, NULL);
280-
if (!(oplock & SMB2_LEASE_READ_CACHING_HE))
304+
if (!(oplock & SMB2_LEASE_READ_CACHING_HE)) {
305+
spin_unlock(&cfids->cfid_list_lock);
281306
goto oshr_free;
307+
}
282308
qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
283-
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
309+
if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info)) {
310+
spin_unlock(&cfids->cfid_list_lock);
284311
goto oshr_free;
312+
}
285313
if (!smb2_validate_and_copy_iov(
286314
le16_to_cpu(qi_rsp->OutputBufferOffset),
287315
sizeof(struct smb2_file_all_info),
288316
&rsp_iov[1], sizeof(struct smb2_file_all_info),
289317
(char *)&cfid->file_all_info))
290318
cfid->file_all_info_is_valid = true;
291319

292-
if (!npath[0])
293-
dentry = dget(cifs_sb->root);
294-
else {
295-
dentry = path_to_dentry(cifs_sb, npath);
296-
if (IS_ERR(dentry)) {
297-
rc = -ENOENT;
298-
goto oshr_free;
299-
}
300-
}
301-
spin_lock(&cfids->cfid_list_lock);
302-
cfid->dentry = dentry;
303320
cfid->time = jiffies;
304-
cfid->has_lease = true;
305321
spin_unlock(&cfids->cfid_list_lock);
322+
/* At this point the directory handle is fully cached */
323+
rc = 0;
306324

307325
oshr_free:
308-
kfree(utf16_path);
309326
SMB2_open_free(&rqst[0]);
310327
SMB2_query_info_free(&rqst[1]);
311328
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
312329
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
313-
spin_lock(&cfids->cfid_list_lock);
314-
if (!cfid->has_lease) {
315-
if (rc) {
316-
if (cfid->on_list) {
317-
list_del(&cfid->entry);
318-
cfid->on_list = false;
319-
cfids->num_entries--;
320-
}
321-
rc = -ENOENT;
322-
} else {
330+
if (rc) {
331+
spin_lock(&cfids->cfid_list_lock);
332+
if (cfid->on_list) {
333+
list_del(&cfid->entry);
334+
cfid->on_list = false;
335+
cfids->num_entries--;
336+
}
337+
if (cfid->has_lease) {
323338
/*
324339
* We are guaranteed to have two references at this
325340
* point. One for the caller and one for a potential
326341
* lease. Release the Lease-ref so that the directory
327342
* will be closed when the caller closes the cached
328343
* handle.
329344
*/
345+
cfid->has_lease = false;
330346
spin_unlock(&cfids->cfid_list_lock);
331347
kref_put(&cfid->refcount, smb2_close_cached_fid);
332348
goto out;
333349
}
350+
spin_unlock(&cfids->cfid_list_lock);
334351
}
335-
spin_unlock(&cfids->cfid_list_lock);
352+
out:
336353
if (rc) {
337354
if (cfid->is_open)
338355
SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
339356
cfid->fid.volatile_fid);
340357
free_cached_dir(cfid);
341-
cfid = NULL;
342-
}
343-
out:
344-
if (rc == 0) {
358+
} else {
345359
*ret_cfid = cfid;
346360
atomic_inc(&tcon->num_remote_opens);
347361
}
348-
362+
kfree(utf16_path);
349363
return rc;
350364
}
351365

fs/smb/client/cifs_debug.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
427427
if (server->nosharesock)
428428
seq_printf(m, " nosharesock");
429429

430+
seq_printf(m, "\nServer capabilities: 0x%x", server->capabilities);
431+
430432
if (server->rdma)
431433
seq_printf(m, "\nRDMA ");
432434
seq_printf(m, "\nTCP status: %d Instance: %d"
@@ -452,6 +454,11 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
452454
seq_printf(m, "\n\n\tSessions: ");
453455
i = 0;
454456
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
457+
spin_lock(&ses->ses_lock);
458+
if (ses->ses_status == SES_EXITING) {
459+
spin_unlock(&ses->ses_lock);
460+
continue;
461+
}
455462
i++;
456463
if ((ses->serverDomain == NULL) ||
457464
(ses->serverOS == NULL) ||
@@ -472,6 +479,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
472479
ses->ses_count, ses->serverOS, ses->serverNOS,
473480
ses->capabilities, ses->ses_status);
474481
}
482+
spin_unlock(&ses->ses_lock);
475483

476484
seq_printf(m, "\n\tSecurity type: %s ",
477485
get_security_type_str(server->ops->select_sectype(server, ses->sectype)));

fs/smb/client/cifsfs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ const char *cifs_get_link(struct dentry *dentry, struct inode *inode,
11911191

11921192
const struct inode_operations cifs_symlink_inode_ops = {
11931193
.get_link = cifs_get_link,
1194+
.setattr = cifs_setattr,
11941195
.permission = cifs_permission,
11951196
.listxattr = cifs_listxattr,
11961197
};

fs/smb/client/cifspdu.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2570,7 +2570,7 @@ typedef struct {
25702570

25712571

25722572
struct win_dev {
2573-
unsigned char type[8]; /* IntxCHR or IntxBLK */
2573+
unsigned char type[8]; /* IntxCHR or IntxBLK or LnxFIFO*/
25742574
__le64 major;
25752575
__le64 minor;
25762576
} __attribute__((packed));

fs/smb/client/cifsproto.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
8181
extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
8282
char *cifs_build_devname(char *nodename, const char *prepath);
8383
extern void delete_mid(struct mid_q_entry *mid);
84-
extern void release_mid(struct mid_q_entry *mid);
84+
void __release_mid(struct kref *refcount);
8585
extern void cifs_wake_up_task(struct mid_q_entry *mid);
8686
extern int cifs_handle_standard(struct TCP_Server_Info *server,
8787
struct mid_q_entry *mid);
@@ -740,4 +740,9 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
740740
return true;
741741
}
742742

743+
static inline void release_mid(struct mid_q_entry *mid)
744+
{
745+
kref_put(&mid->refcount, __release_mid);
746+
}
747+
743748
#endif /* _CIFSPROTO_H */

fs/smb/client/connect.c

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,18 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
119119
static void smb2_query_server_interfaces(struct work_struct *work)
120120
{
121121
int rc;
122+
int xid;
122123
struct cifs_tcon *tcon = container_of(work,
123124
struct cifs_tcon,
124125
query_interfaces.work);
125126

126127
/*
127128
* query server network interfaces, in case they change
128129
*/
129-
rc = SMB3_request_interfaces(0, tcon, false);
130+
xid = get_xid();
131+
rc = SMB3_request_interfaces(xid, tcon, false);
132+
free_xid(xid);
133+
130134
if (rc) {
131135
cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
132136
__func__, rc);
@@ -156,13 +160,14 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
156160
/* If server is a channel, select the primary channel */
157161
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
158162

159-
spin_lock(&pserver->srv_lock);
163+
/* if we need to signal just this channel */
160164
if (!all_channels) {
161-
pserver->tcpStatus = CifsNeedReconnect;
162-
spin_unlock(&pserver->srv_lock);
165+
spin_lock(&server->srv_lock);
166+
if (server->tcpStatus != CifsExiting)
167+
server->tcpStatus = CifsNeedReconnect;
168+
spin_unlock(&server->srv_lock);
163169
return;
164170
}
165-
spin_unlock(&pserver->srv_lock);
166171

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

19701975
void __cifs_put_smb_ses(struct cifs_ses *ses)
19711976
{
1972-
unsigned int rc, xid;
1973-
unsigned int chan_count;
19741977
struct TCP_Server_Info *server = ses->server;
1978+
unsigned int xid;
1979+
size_t i;
1980+
int rc;
19751981

19761982
spin_lock(&ses->ses_lock);
19771983
if (ses->ses_status == SES_EXITING) {
@@ -2017,20 +2023,14 @@ void __cifs_put_smb_ses(struct cifs_ses *ses)
20172023
list_del_init(&ses->smb_ses_list);
20182024
spin_unlock(&cifs_tcp_ses_lock);
20192025

2020-
chan_count = ses->chan_count;
2021-
20222026
/* close any extra channels */
2023-
if (chan_count > 1) {
2024-
int i;
2025-
2026-
for (i = 1; i < chan_count; i++) {
2027-
if (ses->chans[i].iface) {
2028-
kref_put(&ses->chans[i].iface->refcount, release_iface);
2029-
ses->chans[i].iface = NULL;
2030-
}
2031-
cifs_put_tcp_session(ses->chans[i].server, 0);
2032-
ses->chans[i].server = NULL;
2027+
for (i = 1; i < ses->chan_count; i++) {
2028+
if (ses->chans[i].iface) {
2029+
kref_put(&ses->chans[i].iface->refcount, release_iface);
2030+
ses->chans[i].iface = NULL;
20332031
}
2032+
cifs_put_tcp_session(ses->chans[i].server, 0);
2033+
ses->chans[i].server = NULL;
20342034
}
20352035

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

3852-
if (!is_binding)
3852+
if (!is_binding) {
38533853
ses->ses_status = SES_IN_SETUP;
3854+
3855+
/* force iface_list refresh */
3856+
ses->iface_last_update = 0;
3857+
}
38543858
spin_unlock(&ses->ses_lock);
38553859

38563860
/* update ses ip_addr only for primary chan */

fs/smb/client/inode.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,10 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
594594
cifs_dbg(FYI, "Symlink\n");
595595
fattr->cf_mode |= S_IFLNK;
596596
fattr->cf_dtype = DT_LNK;
597+
} else if (memcmp("LnxFIFO", pbuf, 8) == 0) {
598+
cifs_dbg(FYI, "FIFO\n");
599+
fattr->cf_mode |= S_IFIFO;
600+
fattr->cf_dtype = DT_FIFO;
597601
} else {
598602
fattr->cf_mode |= S_IFREG; /* file? */
599603
fattr->cf_dtype = DT_REG;

fs/smb/client/link.c

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -42,23 +42,11 @@ symlink_hash(unsigned int link_len, const char *link_str, u8 *md5_hash)
4242

4343
rc = cifs_alloc_hash("md5", &md5);
4444
if (rc)
45-
goto symlink_hash_err;
45+
return rc;
4646

47-
rc = crypto_shash_init(md5);
48-
if (rc) {
49-
cifs_dbg(VFS, "%s: Could not init md5 shash\n", __func__);
50-
goto symlink_hash_err;
51-
}
52-
rc = crypto_shash_update(md5, link_str, link_len);
53-
if (rc) {
54-
cifs_dbg(VFS, "%s: Could not update with link_str\n", __func__);
55-
goto symlink_hash_err;
56-
}
57-
rc = crypto_shash_final(md5, md5_hash);
47+
rc = crypto_shash_digest(md5, link_str, link_len, md5_hash);
5848
if (rc)
5949
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__);
60-
61-
symlink_hash_err:
6250
cifs_free_hash(&md5);
6351
return rc;
6452
}

fs/smb/client/ntlmssp.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ typedef struct _AUTHENTICATE_MESSAGE {
133133
SECURITY_BUFFER WorkstationName;
134134
SECURITY_BUFFER SessionKey;
135135
__le32 NegotiateFlags;
136-
/* SECURITY_BUFFER for version info not present since we
137-
do not set the version is present flag */
136+
struct ntlmssp_version Version;
137+
/* SECURITY_BUFFER */
138138
char UserString[];
139139
} __attribute__((packed)) AUTHENTICATE_MESSAGE, *PAUTHENTICATE_MESSAGE;
140140

fs/smb/client/sess.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
186186
}
187187

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

1062+
/* send version information in ntlmssp authenticate also */
10631063
flags = ses->ntlmssp->server_flags | NTLMSSP_REQUEST_TARGET |
1064-
NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
1065-
/* we only send version information in ntlmssp negotiate, so do not set this flag */
1066-
flags = flags & ~NTLMSSP_NEGOTIATE_VERSION;
1064+
NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_NEGOTIATE_VERSION |
1065+
NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
1066+
1067+
sec_blob->Version.ProductMajorVersion = LINUX_VERSION_MAJOR;
1068+
sec_blob->Version.ProductMinorVersion = LINUX_VERSION_PATCHLEVEL;
1069+
sec_blob->Version.ProductBuild = cpu_to_le16(SMB3_PRODUCT_BUILD);
1070+
sec_blob->Version.NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
1071+
10671072
tmp = *pbuffer + sizeof(AUTHENTICATE_MESSAGE);
10681073
sec_blob->NegotiateFlags = cpu_to_le32(flags);
10691074

0 commit comments

Comments
 (0)