Skip to content

Commit

Permalink
Merge tag 'v6.9-rc-smb3-server-fixes' of git://git.samba.org/ksmbd
Browse files Browse the repository at this point in the history
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
  • Loading branch information
torvalds committed Mar 20, 2024
2 parents 42c2a75 + def30e7 commit 2395690
Show file tree
Hide file tree
Showing 15 changed files with 716 additions and 147 deletions.
2 changes: 0 additions & 2 deletions fs/smb/server/glob.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
#include "unicode.h"
#include "vfs_cache.h"

#define KSMBD_VERSION "3.4.2"

extern int ksmbd_debug_types;

#define KSMBD_DEBUG_SMB BIT(0)
Expand Down
1 change: 1 addition & 0 deletions fs/smb/server/ksmbd_netlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ struct ksmbd_heartbeat {
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION_OFF BIT(3)
#define KSMBD_GLOBAL_FLAG_DURABLE_HANDLE BIT(4)

/*
* IPC request for ksmbd server startup
Expand Down
28 changes: 27 additions & 1 deletion fs/smb/server/mgmt/user_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
kfree(sess);
}

static struct ksmbd_session *__session_lookup(unsigned long long id)
struct ksmbd_session *__session_lookup(unsigned long long id)
{
struct ksmbd_session *sess;

Expand Down Expand Up @@ -305,6 +305,32 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
return sess;
}

void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id)
{
struct ksmbd_session *prev_sess;
struct ksmbd_user *prev_user;

down_write(&sessions_table_lock);
down_write(&conn->session_lock);
prev_sess = __session_lookup(id);
if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)
goto out;

prev_user = prev_sess->user;
if (!prev_user ||
strcmp(user->name, prev_user->name) ||
user->passkey_sz != prev_user->passkey_sz ||
memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
goto out;

ksmbd_destroy_file_table(&prev_sess->file_table);
prev_sess->state = SMB2_SESSION_EXPIRED;
out:
up_write(&conn->session_lock);
up_write(&sessions_table_lock);
}

static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
unsigned long long id)
{
Expand Down
3 changes: 3 additions & 0 deletions fs/smb/server/mgmt/user_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,11 @@ struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
struct ksmbd_session *__session_lookup(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
unsigned long long id);
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id);
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id);
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
Expand Down
96 changes: 82 additions & 14 deletions fs/smb/server/oplock.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ static struct oplock_info *opinfo_get_list(struct ksmbd_inode *ci)
opinfo = list_first_or_null_rcu(&ci->m_op_list, struct oplock_info,
op_entry);
if (opinfo) {
if (!atomic_inc_not_zero(&opinfo->refcount))
if (opinfo->conn == NULL ||
!atomic_inc_not_zero(&opinfo->refcount))
opinfo = NULL;
else {
atomic_inc(&opinfo->conn->r_count);
Expand Down Expand Up @@ -527,7 +528,7 @@ static struct oplock_info *same_client_has_lease(struct ksmbd_inode *ci,
*/
read_lock(&ci->m_lock);
list_for_each_entry(opinfo, &ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (!opinfo->is_lease || !opinfo->conn)
continue;
read_unlock(&ci->m_lock);
lease = opinfo->o_lease;
Expand Down Expand Up @@ -641,7 +642,7 @@ static void __smb2_oplock_break_noti(struct work_struct *wk)
struct smb2_hdr *rsp_hdr;
struct ksmbd_file *fp;

fp = ksmbd_lookup_durable_fd(br_info->fid);
fp = ksmbd_lookup_global_fd(br_info->fid);
if (!fp)
goto out;

Expand Down Expand Up @@ -1106,7 +1107,7 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,

read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (opinfo->conn == NULL || !opinfo->is_lease)
continue;

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

if (!opinfo->is_lease || opinfo->o_lease->version != 2)
if (!opinfo || !opinfo->is_lease || opinfo->o_lease->version != 2)
return;

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

read_lock(&p_ci->m_lock);
list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
if (!opinfo->is_lease)
if (opinfo->conn == NULL || !opinfo->is_lease)
continue;

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

rcu_read_lock();
list_for_each_entry_rcu(brk_op, &ci->m_op_list, op_entry) {
if (brk_op->conn == NULL)
continue;

if (!atomic_inc_not_zero(&brk_op->refcount))
continue;

Expand Down Expand Up @@ -1496,11 +1500,10 @@ void create_lease_buf(u8 *rbuf, struct lease *lease)
/**
* parse_lease_state() - parse lease context containted in file open request
* @open_req: buffer containing smb2 file open(create) request
* @is_dir: whether leasing file is directory
*
* Return: oplock state, -ENOENT if create lease context not found
*/
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
struct lease_ctx_info *parse_lease_state(void *open_req)
{
struct create_context *cc;
struct smb2_create_req *req = (struct smb2_create_req *)open_req;
Expand All @@ -1518,12 +1521,7 @@ struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir)
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;

memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
if (is_dir) {
lreq->req_state = lc->lcontext.LeaseState &
~SMB2_LEASE_WRITE_CACHING_LE;
lreq->is_dir = true;
} else
lreq->req_state = lc->lcontext.LeaseState;
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
lreq->epoch = lc->lcontext.Epoch;
lreq->duration = lc->lcontext.LeaseDuration;
Expand Down Expand Up @@ -1646,6 +1644,8 @@ void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp)
buf->Name[3] = 'Q';

buf->Timeout = cpu_to_le32(fp->durable_timeout);
if (fp->is_persistent)
buf->Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
}

/**
Expand Down Expand Up @@ -1813,3 +1813,71 @@ struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
read_unlock(&lease_list_lock);
return ret_op;
}

int smb2_check_durable_oplock(struct ksmbd_conn *conn,
struct ksmbd_share_config *share,
struct ksmbd_file *fp,
struct lease_ctx_info *lctx,
char *name)
{
struct oplock_info *opinfo = opinfo_get(fp);
int ret = 0;

if (!opinfo)
return 0;

if (opinfo->is_lease == false) {
if (lctx) {
pr_err("create context include lease\n");
ret = -EBADF;
goto out;
}

if (opinfo->level != SMB2_OPLOCK_LEVEL_BATCH) {
pr_err("oplock level is not equal to SMB2_OPLOCK_LEVEL_BATCH\n");
ret = -EBADF;
}

goto out;
}

if (memcmp(conn->ClientGUID, fp->client_guid,
SMB2_CLIENT_GUID_SIZE)) {
ksmbd_debug(SMB, "Client guid of fp is not equal to the one of connection\n");
ret = -EBADF;
goto out;
}

if (!lctx) {
ksmbd_debug(SMB, "create context does not include lease\n");
ret = -EBADF;
goto out;
}

if (memcmp(opinfo->o_lease->lease_key, lctx->lease_key,
SMB2_LEASE_KEY_SIZE)) {
ksmbd_debug(SMB,
"lease key of fp does not match lease key in create context\n");
ret = -EBADF;
goto out;
}

if (!(opinfo->o_lease->state & SMB2_LEASE_HANDLE_CACHING_LE)) {
ksmbd_debug(SMB, "lease state does not contain SMB2_LEASE_HANDLE_CACHING\n");
ret = -EBADF;
goto out;
}

if (opinfo->o_lease->version != lctx->version) {
ksmbd_debug(SMB,
"lease version of fp does not match the one in create context\n");
ret = -EBADF;
goto out;
}

if (!ksmbd_inode_pending_delete(fp))
ret = ksmbd_validate_name_reconnect(share, fp, name);
out:
opinfo_put(opinfo);
return ret;
}
7 changes: 6 additions & 1 deletion fs/smb/server/oplock.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void opinfo_put(struct oplock_info *opinfo);

/* Lease related functions */
void create_lease_buf(u8 *rbuf, struct lease *lease);
struct lease_ctx_info *parse_lease_state(void *open_req, bool is_dir);
struct lease_ctx_info *parse_lease_state(void *open_req);
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
int lease_read_to_write(struct oplock_info *opinfo);

Expand All @@ -130,4 +130,9 @@ void destroy_lease_table(struct ksmbd_conn *conn);
void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
struct lease_ctx_info *lctx);
void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
int smb2_check_durable_oplock(struct ksmbd_conn *conn,
struct ksmbd_share_config *share,
struct ksmbd_file *fp,
struct lease_ctx_info *lctx,
char *name);
#endif /* __KSMBD_OPLOCK_H */
1 change: 0 additions & 1 deletion fs/smb/server/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,6 @@ static void __exit ksmbd_server_exit(void)
}

MODULE_AUTHOR("Namjae Jeon <[email protected]>");
MODULE_VERSION(KSMBD_VERSION);
MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");
MODULE_LICENSE("GPL");
MODULE_SOFTDEP("pre: ecb");
Expand Down
26 changes: 19 additions & 7 deletions fs/smb/server/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,17 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
*len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
break;
case SMB2_TREE_CONNECT:
*off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset);
*off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset),
offsetof(struct smb2_tree_connect_req, Buffer));
*len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
break;
case SMB2_CREATE:
{
unsigned short int name_off =
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
max_t(unsigned short int,
le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset),
offsetof(struct smb2_create_req, Buffer));
unsigned short int name_len =
le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);

Expand All @@ -128,11 +132,15 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break;
}
case SMB2_QUERY_INFO:
*off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset);
*off = max_t(unsigned int,
le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset),
offsetof(struct smb2_query_info_req, Buffer));
*len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
break;
case SMB2_SET_INFO:
*off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset);
*off = max_t(unsigned int,
le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset),
offsetof(struct smb2_set_info_req, Buffer));
*len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
break;
case SMB2_READ:
Expand All @@ -142,7 +150,7 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
case SMB2_WRITE:
if (((struct smb2_write_req *)hdr)->DataOffset ||
((struct smb2_write_req *)hdr)->Length) {
*off = max_t(unsigned int,
*off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset),
offsetof(struct smb2_write_req, Buffer));
*len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
Expand All @@ -153,7 +161,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
break;
case SMB2_QUERY_DIRECTORY:
*off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset);
*off = max_t(unsigned short int,
le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset),
offsetof(struct smb2_query_directory_req, Buffer));
*len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
break;
case SMB2_LOCK:
Expand All @@ -168,7 +178,9 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break;
}
case SMB2_IOCTL:
*off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset);
*off = max_t(unsigned int,
le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset),
offsetof(struct smb2_ioctl_req, Buffer));
*len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
break;
default:
Expand Down
6 changes: 6 additions & 0 deletions fs/smb/server/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,9 @@ void init_smb3_02_server(struct ksmbd_conn *conn)

if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;

if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
}

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

if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
conn->vals->capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;

INIT_LIST_HEAD(&conn->preauth_sess_table);
return 0;
}
Expand Down
Loading

0 comments on commit 2395690

Please sign in to comment.