Merge tag 'v6.15rc-part1-ksmbd-server-fixes' of git://git.samba.org/ksmbd

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
This commit is contained in:
Linus Torvalds
2025-03-31 17:42:26 -07:00
7 changed files with 68 additions and 43 deletions

View File

@@ -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

View File

@@ -1218,7 +1218,7 @@ free_iv:
free_sg:
kfree(sg);
free_req:
kfree(req);
aead_request_free(req);
free_ctx:
ksmbd_release_crypto_ctx(ctx);
return rc;

View File

@@ -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);
}
}
@@ -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)
{

View File

@@ -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);

View File

@@ -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);
@@ -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);
@@ -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;
@@ -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;

View File

@@ -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;
}
@@ -1910,6 +1904,8 @@ out_err:
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);
@@ -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);
@@ -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);
@@ -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");

View File

@@ -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>
@@ -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)
@@ -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:
@@ -2289,7 +2268,7 @@ out:
}
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;
}