Skip to content

Commit

Permalink
Merge tag 'v6.15rc-part1-ksmbd-server-fixes' of git://git.samba.org/k…
Browse files Browse the repository at this point in the history
…smbd

Pull smb server updates from Steve French:

 - Two fixes for bounds checks of open contexts

 - Two multichannel fixes, including one for important UAF

 - Oplock/lease break fix for potential ksmbd connection refcount leak

 - Security fix to free crypto data more securely

 - Fix to enable allowing Kerberos authentication by default

 - Two RDMA/smbdirect fixes

 - Minor cleanup

* tag 'v6.15rc-part1-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix r_count dec/increment mismatch
  ksmbd: fix multichannel connection failure
  ksmbd: fix use-after-free in ksmbd_sessions_deregister()
  ksmbd: use ib_device_get_netdev() instead of calling ops.get_netdev
  ksmbd: use aead_request_free to match aead_request_alloc
  Revert "ksmbd: fix missing RDMA-capable flag for IPoIB device in ksmbd_rdma_capable_netdev()"
  ksmbd: add bounds check for create lease context
  ksmbd: add bounds check for durable handle context
  ksmbd: make SMB_SERVER_KERBEROS5 enable by default
  ksmbd: Use str_read_write() and str_true_false() helpers
  • Loading branch information
Linus Torvalds committed Apr 1, 2025
2 parents 8b175e2 + ddb7ea3 commit f64a72b
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 43 deletions.
2 changes: 1 addition & 1 deletion fs/smb/server/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,4 @@ config SMB_SERVER_CHECK_CAP_NET_ADMIN
config SMB_SERVER_KERBEROS5
bool "Support for Kerberos 5"
depends on SMB_SERVER
default n
default y
2 changes: 1 addition & 1 deletion fs/smb/server/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,7 @@ int ksmbd_crypt_message(struct ksmbd_work *work, struct kvec *iov,
free_sg:
kfree(sg);
free_req:
kfree(req);
aead_request_free(req);
free_ctx:
ksmbd_release_crypto_ctx(ctx);
return rc;
Expand Down
19 changes: 19 additions & 0 deletions fs/smb/server/mgmt/user_session.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,9 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
if (!ksmbd_chann_del(conn, sess) &&
xa_empty(&sess->ksmbd_chann_list)) {
hash_del(&sess->hlist);
down_write(&conn->session_lock);
xa_erase(&conn->sessions, sess->id);
up_write(&conn->session_lock);
ksmbd_session_destroy(sess);
}
}
Expand All @@ -256,6 +259,22 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
up_write(&sessions_table_lock);
}

bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn,
unsigned long long id)
{
struct ksmbd_session *sess;

down_read(&conn->session_lock);
sess = xa_load(&conn->sessions, id);
if (sess) {
up_read(&conn->session_lock);
return true;
}
up_read(&conn->session_lock);

return false;
}

struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id)
{
Expand Down
2 changes: 2 additions & 0 deletions fs/smb/server/mgmt/user_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess);
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
unsigned long long id);
bool is_ksmbd_session_in_connection(struct ksmbd_conn *conn,
unsigned long long id);
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess);
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
Expand Down
12 changes: 10 additions & 2 deletions fs/smb/server/oplock.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,8 @@ static int smb2_oplock_break_noti(struct oplock_info *opinfo)
work->conn = conn;
work->sess = opinfo->sess;

ksmbd_conn_r_count_inc(conn);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
ksmbd_conn_r_count_inc(conn);
INIT_WORK(&work->work, __smb2_oplock_break_noti);
ksmbd_queue_work(work);

Expand Down Expand Up @@ -833,8 +833,8 @@ static int smb2_lease_break_noti(struct oplock_info *opinfo)
work->conn = conn;
work->sess = opinfo->sess;

ksmbd_conn_r_count_inc(conn);
if (opinfo->op_state == OPLOCK_ACK_WAIT) {
ksmbd_conn_r_count_inc(conn);
INIT_WORK(&work->work, __smb2_lease_break_noti);
ksmbd_queue_work(work);
wait_for_break_ack(opinfo);
Expand Down Expand Up @@ -1505,6 +1505,10 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
if (sizeof(struct lease_context_v2) == le32_to_cpu(cc->DataLength)) {
struct create_lease_v2 *lc = (struct create_lease_v2 *)cc;

if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) <
sizeof(struct create_lease_v2) - 4)
return NULL;

memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
Expand All @@ -1517,6 +1521,10 @@ struct lease_ctx_info *parse_lease_state(void *open_req)
} else {
struct create_lease *lc = (struct create_lease *)cc;

if (le16_to_cpu(cc->DataOffset) + le32_to_cpu(cc->DataLength) <
sizeof(struct create_lease))
return NULL;

memcpy(lreq->lease_key, lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
lreq->req_state = lc->lcontext.LeaseState;
lreq->flags = lc->lcontext.LeaseFlags;
Expand Down
33 changes: 25 additions & 8 deletions fs/smb/server/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1707,44 +1707,38 @@ int smb2_sess_setup(struct ksmbd_work *work)

if (conn->dialect != sess->dialect) {
rc = -EINVAL;
ksmbd_user_session_put(sess);
goto out_err;
}

if (!(req->hdr.Flags & SMB2_FLAGS_SIGNED)) {
rc = -EINVAL;
ksmbd_user_session_put(sess);
goto out_err;
}

if (strncmp(conn->ClientGUID, sess->ClientGUID,
SMB2_CLIENT_GUID_SIZE)) {
rc = -ENOENT;
ksmbd_user_session_put(sess);
goto out_err;
}

if (sess->state == SMB2_SESSION_IN_PROGRESS) {
rc = -EACCES;
ksmbd_user_session_put(sess);
goto out_err;
}

if (sess->state == SMB2_SESSION_EXPIRED) {
rc = -EFAULT;
ksmbd_user_session_put(sess);
goto out_err;
}
ksmbd_user_session_put(sess);

if (ksmbd_conn_need_reconnect(conn)) {
rc = -EFAULT;
ksmbd_user_session_put(sess);
sess = NULL;
goto out_err;
}

sess = ksmbd_session_lookup(conn, sess_id);
if (!sess) {
if (is_ksmbd_session_in_connection(conn, sess_id)) {
rc = -EACCES;
goto out_err;
}
Expand Down Expand Up @@ -1910,6 +1904,8 @@ int smb2_sess_setup(struct ksmbd_work *work)

sess->last_active = jiffies;
sess->state = SMB2_SESSION_EXPIRED;
ksmbd_user_session_put(sess);
work->sess = NULL;
if (try_delay) {
ksmbd_conn_set_need_reconnect(conn);
ssleep(5);
Expand Down Expand Up @@ -2708,6 +2704,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}

if (le16_to_cpu(context->DataOffset) +
le32_to_cpu(context->DataLength) <
sizeof(struct create_durable_reconn_v2_req)) {
err = -EINVAL;
goto out;
}

recon_v2 = (struct create_durable_reconn_v2_req *)context;
persistent_id = recon_v2->Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
Expand Down Expand Up @@ -2741,6 +2744,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}

if (le16_to_cpu(context->DataOffset) +
le32_to_cpu(context->DataLength) <
sizeof(struct create_durable_reconn_req)) {
err = -EINVAL;
goto out;
}

recon = (struct create_durable_reconn_req *)context;
persistent_id = recon->Data.Fid.PersistentFileId;
dh_info->fp = ksmbd_lookup_durable_fd(persistent_id);
Expand All @@ -2766,6 +2776,13 @@ static int parse_durable_handle_context(struct ksmbd_work *work,
goto out;
}

if (le16_to_cpu(context->DataOffset) +
le32_to_cpu(context->DataLength) <
sizeof(struct create_durable_req_v2)) {
err = -EINVAL;
goto out;
}

durable_v2_blob =
(struct create_durable_req_v2 *)context;
ksmbd_debug(SMB, "Request for durable v2 open\n");
Expand Down
41 changes: 10 additions & 31 deletions fs/smb/server/transport_rdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/mempool.h>
#include <linux/highmem.h>
#include <linux/scatterlist.h>
#include <linux/string_choices.h>
#include <rdma/ib_verbs.h>
#include <rdma/rdma_cm.h>
#include <rdma/rw.h>
Expand Down Expand Up @@ -1396,7 +1397,7 @@ static int smb_direct_rdma_xmit(struct smb_direct_transport *t,
}

ksmbd_debug(RDMA, "RDMA %s, len %#x, needed credits %#x\n",
is_read ? "read" : "write", buf_len, credits_needed);
str_read_write(is_read), buf_len, credits_needed);

ret = wait_for_rw_credits(t, credits_needed);
if (ret < 0)
Expand Down Expand Up @@ -2241,38 +2242,16 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
for (i = 0; i < smb_dev->ib_dev->phys_port_cnt; i++) {
struct net_device *ndev;

if (smb_dev->ib_dev->ops.get_netdev) {
ndev = smb_dev->ib_dev->ops.get_netdev(
smb_dev->ib_dev, i + 1);
if (!ndev)
continue;
ndev = ib_device_get_netdev(smb_dev->ib_dev, i + 1);
if (!ndev)
continue;

if (ndev == netdev) {
dev_put(ndev);
rdma_capable = true;
goto out;
}
if (ndev == netdev) {
dev_put(ndev);
/* if ib_dev does not implement ops.get_netdev
* check for matching infiniband GUID in hw_addr
*/
} else if (netdev->type == ARPHRD_INFINIBAND) {
struct netdev_hw_addr *ha;
union ib_gid gid;
u32 port_num;
int ret;

netdev_hw_addr_list_for_each(
ha, &netdev->dev_addrs) {
memcpy(&gid, ha->addr + 4, sizeof(gid));
ret = ib_find_gid(smb_dev->ib_dev, &gid,
&port_num, NULL);
if (!ret) {
rdma_capable = true;
goto out;
}
}
rdma_capable = true;
goto out;
}
dev_put(ndev);
}
}
out:
Expand All @@ -2289,7 +2268,7 @@ bool ksmbd_rdma_capable_netdev(struct net_device *netdev)
}

ksmbd_debug(RDMA, "netdev(%s) rdma capable : %s\n",
netdev->name, rdma_capable ? "true" : "false");
netdev->name, str_true_false(rdma_capable));

return rdma_capable;
}
Expand Down

0 comments on commit f64a72b

Please sign in to comment.