Skip to content

Commit 2395690

Browse files
committed
Merge tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd
Pull smb server updates from Steve French: - add support for durable file handles (an important data integrity feature) - fixes for potential out of bounds issues - fix possible null dereference in close - getattr fixes - trivial typo fix and minor cleanup * tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd: ksmbd: remove module version ksmbd: fix potencial out-of-bounds when buffer offset is invalid ksmbd: fix slab-out-of-bounds in smb_strndup_from_utf16() ksmbd: Fix spelling mistake "connction" -> "connection" ksmbd: fix possible null-deref in smb_lazy_parent_lease_break_close ksmbd: add support for durable handles v1/v2 ksmbd: mark SMB2_SESSION_EXPIRED to session when destroying previous session ksmbd: retrieve number of blocks using vfs_getattr in set_file_allocation_info ksmbd: replace generic_fillattr with vfs_getattr
2 parents 42c2a75 + def30e7 commit 2395690

15 files changed

Lines changed: 716 additions & 147 deletions

fs/smb/server/glob.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
#include "unicode.h"
1313
#include "vfs_cache.h"
1414

15-
#define KSMBD_VERSION "3.4.2"
16-
1715
extern int ksmbd_debug_types;
1816

1917
#define KSMBD_DEBUG_SMB BIT(0)

fs/smb/server/ksmbd_netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
7575
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
7676
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
7777
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
78+
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)
7879

7980
/*
8081
* IPC request for ksmbd server startup

fs/smb/server/mgmt/user_session.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
156156
kfree(sess);
157157
}
158158

159-
static struct ksmbd_session *__session_lookup(unsigned long long id)
159+
struct ksmbd_session *__session_lookup(unsigned long long id)
160160
{
161161
struct ksmbd_session *sess;
162162

@@ -305,6 +305,32 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
305305
return sess;
306306
}
307307

308+
void destroy_previous_session(struct ksmbd_conn *conn,
309+
struct ksmbd_user *user, u64 id)
310+
{
311+
struct ksmbd_session *prev_sess;
312+
struct ksmbd_user *prev_user;
313+
314+
down_write(&sessions_table_lock);
315+
down_write(&conn->session_lock);
316+
prev_sess = __session_lookup(id);
317+
if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)
318+
goto out;
319+
320+
prev_user = prev_sess->user;
321+
if (!prev_user ||
322+
strcmp(user->name, prev_user->name) ||
323+
user->passkey_sz != prev_user->passkey_sz ||
324+
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
325+
goto out;
326+
327+
ksmbd_destroy_file_table(&prev_sess->file_table);
328+
prev_sess->state = SMB2_SESSION_EXPIRED;
329+
out:
330+
up_write(&conn->session_lock);
331+
up_write(&sessions_table_lock);
332+
}
333+
308334
static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
309335
unsigned long long id)
310336
{

fs/smb/server/mgmt/user_session.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
8888
int ksmbd_session_register(struct ksmbd_conn *conn,
8989
struct ksmbd_session *sess);
9090
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
91+
struct ksmbd_session *__session_lookup(unsigned long long id);
9192
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
9293
unsigned long long id);
94+
void destroy_previous_session(struct ksmbd_conn *conn,
95+
struct ksmbd_user *user, u64 id);
9396
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
9497
u64 sess_id);
9598
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,

fs/smb/server/oplock.c

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
159159
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
160160
op_entry);
161161
if (opinfo) {
162-
if (!atomic_inc_not_zero(&opinfo->refcount))
162+
if (opinfo->conn == NULL ||
163+
!atomic_inc_not_zero(&opinfo->refcount))
163164
opinfo = NULL;
164165
else {
165166
atomic_inc(&opinfo->conn->r_count);
@@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
527528
*/
528529
read_lock(&ci->m_lock);
529530
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
530-
if (!opinfo->is_lease)
531+
if (!opinfo->is_lease || !opinfo->conn)
531532
continue;
532533
read_unlock(&ci->m_lock);
533534
lease = opinfo->o_lease;
@@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
641642
struct smb2_hdr *rsp_hdr;
642643
struct ksmbd_file *fp;
643644

644-
fp = ksmbd_lookup_durable_fd(br_info->fid);
645+
fp = ksmbd_lookup_global_fd(br_info->fid);
645646
if (!fp)
646647
goto out;
647648

@@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
11061107

11071108
read_lock(&p_ci->m_lock);
11081109
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1109-
if (!opinfo->is_lease)
1110+
if (opinfo->conn == NULL || !opinfo->is_lease)
11101111
continue;
11111112

11121113
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE &&
@@ -1142,7 +1143,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
11421143
opinfo = rcu_dereference(fp->f_opinfo);
11431144
rcu_read_unlock();
11441145

1145-
if (!opinfo->is_lease || opinfo->o_lease->version != 2)
1146+
if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
11461147
return;
11471148

11481149
p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
@@ -1151,7 +1152,7 @@ void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
11511152

11521153
read_lock(&p_ci->m_lock);
11531154
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
1154-
if (!opinfo->is_lease)
1155+
if (opinfo->conn == NULL || !opinfo->is_lease)
11551156
continue;
11561157

11571158
if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
@@ -1361,6 +1362,9 @@ void smb_break_all_levII_oplock(struct ksmbd_work *work, struct ksmbd_file *fp,
13611362

13621363
rcu_read_lock();
13631364
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
1365+
if (brk_op->conn == NULL)
1366+
continue;
1367+
13641368
if (!atomic_inc_not_zero(&brk_op->refcount))
13651369
continue;
13661370

@@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
14961500
/**
14971501
* parse_lease_state() - parse lease context containted in file open request
14981502
* @open_req: buffer containing smb2 file open(create) request
1499-
* @is_dir: whether leasing file is directory
15001503
*
15011504
* Return: oplock state, -ENOENT if create lease context not found
15021505
*/
1503-
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
1506+
struct lease_ctx_info *parse_lease_state(void *open_req)
15041507
{
15051508
struct create_context *cc;
15061509
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
@@ -1518,12 +1521,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
15181521
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;
15191522

15201523
memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
1521-
if (is_dir) {
1522-
lreq->req_state = lc->lcontext.LeaseState &
1523-
~SMB2_LEASE_WRITE_CACHING_LE;
1524-
lreq->is_dir = true;
1525-
} else
1526-
lreq->req_state = lc->lcontext.LeaseState;
1524+
lreq->req_state = lc->lcontext.LeaseState;
15271525
lreq->flags = lc->lcontext.LeaseFlags;
15281526
lreq->epoch = lc->lcontext.Epoch;
15291527
lreq->duration = lc->lcontext.LeaseDuration;
@@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
16461644
buf->Name[3] = 'Q';
16471645

16481646
buf->Timeout = cpu_to_le32(fp->durable_timeout);
1647+
if (fp->is_persistent)
1648+
buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
16491649
}
16501650

16511651
/**
@@ -1813,3 +1813,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
18131813
read_unlock(&lease_list_lock);
18141814
return ret_op;
18151815
}
1816+
1817+
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
1818+
struct ksmbd_share_config *share,
1819+
struct ksmbd_file *fp,
1820+
struct lease_ctx_info *lctx,
1821+
char *name)
1822+
{
1823+
struct oplock_info *opinfo = opinfo_get(fp);
1824+
int ret = 0;
1825+
1826+
if (!opinfo)
1827+
return 0;
1828+
1829+
if (opinfo->is_lease == false) {
1830+
if (lctx) {
1831+
pr_err("create context include lease\n");
1832+
ret = -EBADF;
1833+
goto out;
1834+
}
1835+
1836+
if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) {
1837+
pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n");
1838+
ret = -EBADF;
1839+
}
1840+
1841+
goto out;
1842+
}
1843+
1844+
if (memcmp(conn->ClientGUID, fp->client_guid,
1845+
SMB2_CLIENT_GUID_SIZE)) {
1846+
ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n");
1847+
ret = -EBADF;
1848+
goto out;
1849+
}
1850+
1851+
if (!lctx) {
1852+
ksmbd_debug(SMB, "create context does not include lease\n");
1853+
ret = -EBADF;
1854+
goto out;
1855+
}
1856+
1857+
if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
1858+
SMB2_LEASE_KEY_SIZE)) {
1859+
ksmbd_debug(SMB,
1860+
"lease key of fp does not match lease key in create context\n");
1861+
ret = -EBADF;
1862+
goto out;
1863+
}
1864+
1865+
if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) {
1866+
ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n");
1867+
ret = -EBADF;
1868+
goto out;
1869+
}
1870+
1871+
if (opinfo->o_lease->version != lctx->version) {
1872+
ksmbd_debug(SMB,
1873+
"lease version of fp does not match the one in create context\n");
1874+
ret = -EBADF;
1875+
goto out;
1876+
}
1877+
1878+
if (!ksmbd_inode_pending_delete(fp))
1879+
ret = ksmbd_validate_name_reconnect(share, fp, name);
1880+
out:
1881+
opinfo_put(opinfo);
1882+
return ret;
1883+
}

fs/smb/server/oplock.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);
111111

112112
/* Lease related functions */
113113
void create_lease_buf(u8 *rbuf, struct lease *lease);
114-
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
114+
struct lease_ctx_info *parse_lease_state(void *open_req);
115115
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
116116
int lease_read_to_write(struct oplock_info *opinfo);
117117

@@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
130130
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
131131
struct lease_ctx_info *lctx);
132132
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
133+
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
134+
struct ksmbd_share_config *share,
135+
struct ksmbd_file *fp,
136+
struct lease_ctx_info *lctx,
137+
char *name);
133138
#endif /* __KSMBD_OPLOCK_H */

fs/smb/server/server.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,6 @@ static void __exit ksmbd_server_exit(void)
625625
}
626626

627627
MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");
628-
MODULE_VERSION(KSMBD_VERSION);
629628
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
630629
MODULE_LICENSE("GPL");
631630
MODULE_SOFTDEP("pre: ecb");

fs/smb/server/smb2misc.c

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,17 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
101101
*len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
102102
break;
103103
case SMB2_TREE_CONNECT:
104-
*off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset);
104+
*off = max_t(unsigned short int,
105+
le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset),
106+
offsetof(struct smb2_tree_connect_req, Buffer));
105107
*len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
106108
break;
107109
case SMB2_CREATE:
108110
{
109111
unsigned short int name_off =
110-
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
112+
max_t(unsigned short int,
113+
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
114+
offsetof(struct smb2_create_req, Buffer));
111115
unsigned short int name_len =
112116
le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
113117

@@ -128,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
128132
break;
129133
}
130134
case SMB2_QUERY_INFO:
131-
*off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset);
135+
*off = max_t(unsigned int,
136+
le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset),
137+
offsetof(struct smb2_query_info_req, Buffer));
132138
*len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
133139
break;
134140
case SMB2_SET_INFO:
135-
*off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset);
141+
*off = max_t(unsigned int,
142+
le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset),
143+
offsetof(struct smb2_set_info_req, Buffer));
136144
*len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
137145
break;
138146
case SMB2_READ:
@@ -142,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
142150
case SMB2_WRITE:
143151
if (((struct smb2_write_req *)hdr)->DataOffset ||
144152
((struct smb2_write_req *)hdr)->Length) {
145-
*off = max_t(unsigned int,
153+
*off = max_t(unsigned short int,
146154
le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
147155
offsetof(struct smb2_write_req, Buffer));
148156
*len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
@@ -153,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
153161
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
154162
break;
155163
case SMB2_QUERY_DIRECTORY:
156-
*off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset);
164+
*off = max_t(unsigned short int,
165+
le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset),
166+
offsetof(struct smb2_query_directory_req, Buffer));
157167
*len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
158168
break;
159169
case SMB2_LOCK:
@@ -168,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
168178
break;
169179
}
170180
case SMB2_IOCTL:
171-
*off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset);
181+
*off = max_t(unsigned int,
182+
le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset),
183+
offsetof(struct smb2_ioctl_req, Buffer));
172184
*len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
173185
break;
174186
default:

fs/smb/server/smb2ops.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
256256

257257
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
258258
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
259+
260+
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
261+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
259262
}
260263

261264
/**
@@ -283,6 +286,9 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
283286
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
284287
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
285288

289+
if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
290+
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
291+
286292
INIT_LIST_HEAD(&conn->preauth_sess_table);
287293
return 0;
288294
}

0 commit comments

Comments
 (0)