Skip to content

Commit

Permalink
Merge tag '6.4-rc-ksmbd-server-fixes-part2' of git://git.samba.org/ksmbd
Browse files Browse the repository at this point in the history
Pull ksmbd server fixes from Steve French:
 "Ten ksmbd server fixes, including some important security fixes:

   - Two use after free fixes

   - Fix RCU callback race

   - Deadlock fix

   - Three patches to prevent session setup attacks

   - Prevent guest users from establishing multichannel sessions

   - Fix null pointer dereference in query FS info

   - Memleak fix"

* tag '6.4-rc-ksmbd-server-fixes-part2' of git://git.samba.org/ksmbd:
  ksmbd: call rcu_barrier() in ksmbd_server_exit()
  ksmbd: fix racy issue under cocurrent smb2 tree disconnect
  ksmbd: fix racy issue from smb2 close and logoff with multichannel
  ksmbd: not allow guest user on multichannel
  ksmbd: fix deadlock in ksmbd_find_crypto_ctx()
  ksmbd: block asynchronous requests when making a delay on session setup
  ksmbd: destroy expired sessions
  ksmbd: fix racy issue from session setup and logoff
  ksmbd: fix NULL pointer dereference in smb2_get_info_filesystem()
  ksmbd: fix memleak in session setup
  • Loading branch information
torvalds committed May 6, 2023
2 parents ed23734 + eb307d0 commit 2e1e133
Show file tree
Hide file tree
Showing 11 changed files with 250 additions and 110 deletions.
19 changes: 11 additions & 8 deletions fs/ksmbd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -221,22 +221,22 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
{
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
char ntlmv2_rsp[CIFS_HMAC_MD5_HASH_SIZE];
struct ksmbd_crypto_ctx *ctx;
struct ksmbd_crypto_ctx *ctx = NULL;
char *construct = NULL;
int rc, len;

ctx = ksmbd_crypto_ctx_find_hmacmd5();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
return -ENOMEM;
}

rc = calc_ntlmv2_hash(conn, sess, ntlmv2_hash, domain_name);
if (rc) {
ksmbd_debug(AUTH, "could not get v2 hash rc %d\n", rc);
goto out;
}

ctx = ksmbd_crypto_ctx_find_hmacmd5();
if (!ctx) {
ksmbd_debug(AUTH, "could not crypto alloc hmacmd5\n");
return -ENOMEM;
}

rc = crypto_shash_setkey(CRYPTO_HMACMD5_TFM(ctx),
ntlmv2_hash,
CIFS_HMAC_MD5_HASH_SIZE);
Expand Down Expand Up @@ -272,6 +272,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
ksmbd_debug(AUTH, "Could not generate md5 hash\n");
goto out;
}
ksmbd_release_crypto_ctx(ctx);
ctx = NULL;

rc = ksmbd_gen_sess_key(sess, ntlmv2_hash, ntlmv2_rsp);
if (rc) {
Expand All @@ -282,7 +284,8 @@ int ksmbd_auth_ntlmv2(struct ksmbd_conn *conn, struct ksmbd_session *sess,
if (memcmp(ntlmv2->ntlmv2_hash, ntlmv2_rsp, CIFS_HMAC_MD5_HASH_SIZE) != 0)
rc = -EINVAL;
out:
ksmbd_release_crypto_ctx(ctx);
if (ctx)
ksmbd_release_crypto_ctx(ctx);
kfree(construct);
return rc;
}
Expand Down
68 changes: 48 additions & 20 deletions fs/ksmbd/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ static DEFINE_MUTEX(init_lock);
static struct ksmbd_conn_ops default_conn_ops;

LIST_HEAD(conn_list);
DEFINE_RWLOCK(conn_list_lock);
DECLARE_RWSEM(conn_list_lock);

/**
* ksmbd_conn_free() - free resources of the connection instance
Expand All @@ -32,9 +32,9 @@ DEFINE_RWLOCK(conn_list_lock);
*/
void ksmbd_conn_free(struct ksmbd_conn *conn)
{
write_lock(&conn_list_lock);
down_write(&conn_list_lock);
list_del(&conn->conns_list);
write_unlock(&conn_list_lock);
up_write(&conn_list_lock);

xa_destroy(&conn->sessions);
kvfree(conn->request_buf);
Expand All @@ -56,7 +56,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
return NULL;

conn->need_neg = true;
conn->status = KSMBD_SESS_NEW;
ksmbd_conn_set_new(conn);
conn->local_nls = load_nls("utf8");
if (!conn->local_nls)
conn->local_nls = load_nls_default();
Expand Down Expand Up @@ -84,9 +84,9 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
spin_lock_init(&conn->llist_lock);
INIT_LIST_HEAD(&conn->lock_list);

write_lock(&conn_list_lock);
down_write(&conn_list_lock);
list_add(&conn->conns_list, &conn_list);
write_unlock(&conn_list_lock);
up_write(&conn_list_lock);
return conn;
}

Expand All @@ -95,15 +95,15 @@ bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
struct ksmbd_conn *t;
bool ret = false;

read_lock(&conn_list_lock);
down_read(&conn_list_lock);
list_for_each_entry(t, &conn_list, conns_list) {
if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
continue;

ret = true;
break;
}
read_unlock(&conn_list_lock);
up_read(&conn_list_lock);
return ret;
}

Expand Down Expand Up @@ -147,19 +147,47 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
return ret;
}

static void ksmbd_conn_lock(struct ksmbd_conn *conn)
void ksmbd_conn_lock(struct ksmbd_conn *conn)
{
mutex_lock(&conn->srv_mutex);
}

static void ksmbd_conn_unlock(struct ksmbd_conn *conn)
void ksmbd_conn_unlock(struct ksmbd_conn *conn)
{
mutex_unlock(&conn->srv_mutex);
}

void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
{
struct ksmbd_conn *conn;

down_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
if (conn->binding || xa_load(&conn->sessions, sess_id))
WRITE_ONCE(conn->status, status);
}
up_read(&conn_list_lock);
}

void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id)
{
struct ksmbd_conn *bind_conn;

wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);

down_read(&conn_list_lock);
list_for_each_entry(bind_conn, &conn_list, conns_list) {
if (bind_conn == conn)
continue;

if ((bind_conn->binding || xa_load(&bind_conn->sessions, sess_id)) &&
!ksmbd_conn_releasing(bind_conn) &&
atomic_read(&bind_conn->req_running)) {
wait_event(bind_conn->req_running_q,
atomic_read(&bind_conn->req_running) == 0);
}
}
up_read(&conn_list_lock);
}

int ksmbd_conn_write(struct ksmbd_work *work)
Expand Down Expand Up @@ -243,7 +271,7 @@ bool ksmbd_conn_alive(struct ksmbd_conn *conn)
if (!ksmbd_server_running())
return false;

if (conn->status == KSMBD_SESS_EXITING)
if (ksmbd_conn_exiting(conn))
return false;

if (kthread_should_stop())
Expand Down Expand Up @@ -303,7 +331,7 @@ int ksmbd_conn_handler_loop(void *p)
pdu_size = get_rfc1002_len(hdr_buf);
ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);

if (conn->status == KSMBD_SESS_GOOD)
if (ksmbd_conn_good(conn))
max_allowed_pdu_size =
SMB3_MAX_MSGSIZE + conn->vals->max_write_size;
else
Expand All @@ -312,7 +340,7 @@ int ksmbd_conn_handler_loop(void *p)
if (pdu_size > max_allowed_pdu_size) {
pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
pdu_size, max_allowed_pdu_size,
conn->status);
READ_ONCE(conn->status));
break;
}

Expand Down Expand Up @@ -360,10 +388,10 @@ int ksmbd_conn_handler_loop(void *p)
}

out:
ksmbd_conn_set_releasing(conn);
/* Wait till all reference dropped to the Server object*/
wait_event(conn->r_count_q, atomic_read(&conn->r_count) == 0);


if (IS_ENABLED(CONFIG_UNICODE))
utf8_unload(conn->um);
unload_nls(conn->local_nls);
Expand Down Expand Up @@ -407,7 +435,7 @@ static void stop_sessions(void)
struct ksmbd_transport *t;

again:
read_lock(&conn_list_lock);
down_read(&conn_list_lock);
list_for_each_entry(conn, &conn_list, conns_list) {
struct task_struct *task;

Expand All @@ -416,14 +444,14 @@ static void stop_sessions(void)
if (task)
ksmbd_debug(CONN, "Stop session handler %s/%d\n",
task->comm, task_pid_nr(task));
conn->status = KSMBD_SESS_EXITING;
ksmbd_conn_set_exiting(conn);
if (t->ops->shutdown) {
read_unlock(&conn_list_lock);
up_read(&conn_list_lock);
t->ops->shutdown(t);
read_lock(&conn_list_lock);
down_read(&conn_list_lock);
}
}
read_unlock(&conn_list_lock);
up_read(&conn_list_lock);

if (!list_empty(&conn_list)) {
schedule_timeout_interruptible(HZ / 10); /* 100ms */
Expand Down
58 changes: 39 additions & 19 deletions fs/ksmbd/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ enum {
KSMBD_SESS_GOOD,
KSMBD_SESS_EXITING,
KSMBD_SESS_NEED_RECONNECT,
KSMBD_SESS_NEED_NEGOTIATE
KSMBD_SESS_NEED_NEGOTIATE,
KSMBD_SESS_RELEASING
};

struct ksmbd_stats {
Expand Down Expand Up @@ -140,10 +141,10 @@ struct ksmbd_transport {
#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))

extern struct list_head conn_list;
extern rwlock_t conn_list_lock;
extern struct rw_semaphore conn_list_lock;

bool ksmbd_conn_alive(struct ksmbd_conn *conn);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn, u64 sess_id);
struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
Expand All @@ -162,50 +163,69 @@ void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
int ksmbd_conn_handler_loop(void *p);
int ksmbd_conn_transport_init(void);
void ksmbd_conn_transport_destroy(void);
void ksmbd_conn_lock(struct ksmbd_conn *conn);
void ksmbd_conn_unlock(struct ksmbd_conn *conn);

/*
* WARNING
*
* This is a hack. We will move status to a proper place once we land
* a multi-sessions support.
*/
static inline bool ksmbd_conn_good(struct ksmbd_work *work)
static inline bool ksmbd_conn_good(struct ksmbd_conn *conn)
{
return work->conn->status == KSMBD_SESS_GOOD;
return READ_ONCE(conn->status) == KSMBD_SESS_GOOD;
}

static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work)
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
{
return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE;
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;
}

static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work)
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
{
return work->conn->status == KSMBD_SESS_NEED_RECONNECT;
return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
}

static inline bool ksmbd_conn_exiting(struct ksmbd_work *work)
static inline bool ksmbd_conn_exiting(struct ksmbd_conn *conn)
{
return work->conn->status == KSMBD_SESS_EXITING;
return READ_ONCE(conn->status) == KSMBD_SESS_EXITING;
}

static inline void ksmbd_conn_set_good(struct ksmbd_work *work)
static inline bool ksmbd_conn_releasing(struct ksmbd_conn *conn)
{
work->conn->status = KSMBD_SESS_GOOD;
return READ_ONCE(conn->status) == KSMBD_SESS_RELEASING;
}

static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work)
static inline void ksmbd_conn_set_new(struct ksmbd_conn *conn)
{
work->conn->status = KSMBD_SESS_NEED_NEGOTIATE;
WRITE_ONCE(conn->status, KSMBD_SESS_NEW);
}

static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work)
static inline void ksmbd_conn_set_good(struct ksmbd_conn *conn)
{
work->conn->status = KSMBD_SESS_NEED_RECONNECT;
WRITE_ONCE(conn->status, KSMBD_SESS_GOOD);
}

static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work)
static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn)
{
work->conn->status = KSMBD_SESS_EXITING;
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE);
}

static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
}

static inline void ksmbd_conn_set_exiting(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_EXITING);
}

static inline void ksmbd_conn_set_releasing(struct ksmbd_conn *conn)
{
WRITE_ONCE(conn->status, KSMBD_SESS_RELEASING);
}

void ksmbd_all_conn_set_status(u64 sess_id, u32 status);
#endif /* __CONNECTION_H__ */
13 changes: 12 additions & 1 deletion fs/ksmbd/mgmt/tree_connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,15 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
unsigned int id)
{
return xa_load(&sess->tree_conns, id);
struct ksmbd_tree_connect *tcon;

tcon = xa_load(&sess->tree_conns, id);
if (tcon) {
if (test_bit(TREE_CONN_EXPIRE, &tcon->status))
tcon = NULL;
}

return tcon;
}

struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
Expand All @@ -129,6 +137,9 @@ int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
struct ksmbd_tree_connect *tc;
unsigned long id;

if (!sess)
return -EINVAL;

xa_for_each(&sess->tree_conns, id, tc)
ret |= ksmbd_tree_conn_disconnect(sess, tc);
xa_destroy(&sess->tree_conns);
Expand Down
3 changes: 3 additions & 0 deletions fs/ksmbd/mgmt/tree_connect.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ struct ksmbd_share_config;
struct ksmbd_user;
struct ksmbd_conn;

#define TREE_CONN_EXPIRE 1

struct ksmbd_tree_connect {
int id;

Expand All @@ -25,6 +27,7 @@ struct ksmbd_tree_connect {

int maximal_access;
bool posix_extensions;
unsigned long status;
};

struct ksmbd_tree_conn_status {
Expand Down
Loading

0 comments on commit 2e1e133

Please sign in to comment.