Commit 06a22366 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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

Pull smb server fixes from Steve French:
 "Four ksmbd SMB3 server fixes, all also for stable"

* tag 'v6.15rc-part2-ksmbd-server-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix null pointer dereference in alloc_preauth_hash()
  ksmbd: validate zero num_subauth before sub_auth is accessed
  ksmbd: fix overflow in dacloffset bounds check
  ksmbd: fix session use-after-free in multichannel connection
parents 6cb0bd94 c8b5b7c5
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1016,9 +1016,9 @@ static int ksmbd_get_encryption_key(struct ksmbd_work *work, __u64 ses_id,

	ses_enc_key = enc ? sess->smb3encryptionkey :
		sess->smb3decryptionkey;
	if (enc)
		ksmbd_user_session_get(sess);
	memcpy(key, ses_enc_key, SMB3_ENC_DEC_KEY_SIZE);
	if (!enc)
		ksmbd_user_session_put(sess);

	return 0;
}
+11 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ enum {
	KSMBD_SESS_EXITING,
	KSMBD_SESS_NEED_RECONNECT,
	KSMBD_SESS_NEED_NEGOTIATE,
	KSMBD_SESS_NEED_SETUP,
	KSMBD_SESS_RELEASING
};

@@ -187,6 +188,11 @@ static inline bool ksmbd_conn_need_negotiate(struct ksmbd_conn *conn)
	return READ_ONCE(conn->status) == KSMBD_SESS_NEED_NEGOTIATE;
}

static inline bool ksmbd_conn_need_setup(struct ksmbd_conn *conn)
{
	return READ_ONCE(conn->status) == KSMBD_SESS_NEED_SETUP;
}

static inline bool ksmbd_conn_need_reconnect(struct ksmbd_conn *conn)
{
	return READ_ONCE(conn->status) == KSMBD_SESS_NEED_RECONNECT;
@@ -217,6 +223,11 @@ static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_conn *conn)
	WRITE_ONCE(conn->status, KSMBD_SESS_NEED_NEGOTIATE);
}

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

static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_conn *conn)
{
	WRITE_ONCE(conn->status, KSMBD_SESS_NEED_RECONNECT);
+10 −8
Original line number Diff line number Diff line
@@ -181,7 +181,7 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
	down_write(&sessions_table_lock);
	down_write(&conn->session_lock);
	xa_for_each(&conn->sessions, id, sess) {
		if (atomic_read(&sess->refcnt) == 0 &&
		if (atomic_read(&sess->refcnt) <= 1 &&
		    (sess->state != SMB2_SESSION_VALID ||
		     time_after(jiffies,
			       sess->last_active + SMB2_SESSION_TIMEOUT))) {
@@ -233,6 +233,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
				down_write(&conn->session_lock);
				xa_erase(&conn->sessions, sess->id);
				up_write(&conn->session_lock);
				if (atomic_dec_and_test(&sess->refcnt))
					ksmbd_session_destroy(sess);
			}
		}
@@ -252,6 +253,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
		if (xa_empty(&sess->ksmbd_chann_list)) {
			xa_erase(&conn->sessions, sess->id);
			hash_del(&sess->hlist);
			if (atomic_dec_and_test(&sess->refcnt))
				ksmbd_session_destroy(sess);
		}
	}
@@ -328,8 +330,8 @@ void ksmbd_user_session_put(struct ksmbd_session *sess)

	if (atomic_read(&sess->refcnt) <= 0)
		WARN_ON(1);
	else
		atomic_dec(&sess->refcnt);
	else if (atomic_dec_and_test(&sess->refcnt))
		ksmbd_session_destroy(sess);
}

struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
@@ -372,13 +374,13 @@ void destroy_previous_session(struct ksmbd_conn *conn,
	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
	err = ksmbd_conn_wait_idle_sess_id(conn, id);
	if (err) {
		ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
		ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
		goto out;
	}

	ksmbd_destroy_file_table(&prev_sess->file_table);
	prev_sess->state = SMB2_SESSION_EXPIRED;
	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_SETUP);
	ksmbd_launch_ksmbd_durable_scavenger();
out:
	up_write(&conn->session_lock);
@@ -436,7 +438,7 @@ static struct ksmbd_session *__session_create(int protocol)
	xa_init(&sess->rpc_handle_list);
	sess->sequence_number = 1;
	rwlock_init(&sess->tree_conns_lock);
	atomic_set(&sess->refcnt, 1);
	atomic_set(&sess->refcnt, 2);

	ret = __init_smb2_session(sess);
	if (ret)
+15 −6
Original line number Diff line number Diff line
@@ -1249,7 +1249,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
	}

	conn->srv_sec_mode = le16_to_cpu(rsp->SecurityMode);
	ksmbd_conn_set_need_negotiate(conn);
	ksmbd_conn_set_need_setup(conn);

err_out:
	ksmbd_conn_unlock(conn);
@@ -1271,6 +1271,9 @@ static int alloc_preauth_hash(struct ksmbd_session *sess,
	if (sess->Preauth_HashValue)
		return 0;

	if (!conn->preauth_info)
		return -ENOMEM;

	sess->Preauth_HashValue = kmemdup(conn->preauth_info->Preauth_HashValue,
					  PREAUTH_HASHVALUE_SIZE, KSMBD_DEFAULT_GFP);
	if (!sess->Preauth_HashValue)
@@ -1674,6 +1677,11 @@ int smb2_sess_setup(struct ksmbd_work *work)

	ksmbd_debug(SMB, "Received smb2 session setup request\n");

	if (!ksmbd_conn_need_setup(conn) && !ksmbd_conn_good(conn)) {
		work->send_no_response = 1;
		return rc;
	}

	WORK_BUFFERS(work, req, rsp);

	rsp->StructureSize = cpu_to_le16(9);
@@ -1909,7 +1917,7 @@ int smb2_sess_setup(struct ksmbd_work *work)
			if (try_delay) {
				ksmbd_conn_set_need_reconnect(conn);
				ssleep(5);
				ksmbd_conn_set_need_negotiate(conn);
				ksmbd_conn_set_need_setup(conn);
			}
		}
		smb2_set_err_rsp(work);
@@ -2235,14 +2243,15 @@ int smb2_session_logoff(struct ksmbd_work *work)
		return -ENOENT;
	}

	ksmbd_destroy_file_table(&sess->file_table);
	down_write(&conn->session_lock);
	sess->state = SMB2_SESSION_EXPIRED;
	up_write(&conn->session_lock);

	if (sess->user) {
		ksmbd_free_user(sess->user);
		sess->user = NULL;
	ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_NEGOTIATE);
	}
	ksmbd_all_conn_set_status(sess_id, KSMBD_SESS_NEED_SETUP);

	rsp->StructureSize = cpu_to_le16(4);
	err = ksmbd_iov_pin_rsp(work, rsp, sizeof(struct smb2_logoff_rsp));
+17 −4
Original line number Diff line number Diff line
@@ -270,6 +270,11 @@ static int sid_to_id(struct mnt_idmap *idmap,
		return -EIO;
	}

	if (psid->num_subauth == 0) {
		pr_err("%s: zero subauthorities!\n", __func__);
		return -EIO;
	}

	if (sidtype == SIDOWNER) {
		kuid_t uid;
		uid_t id;
@@ -1026,7 +1031,9 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
	struct dentry *parent = path->dentry->d_parent;
	struct mnt_idmap *idmap = mnt_idmap(path->mnt);
	int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
	int rc = 0, dacloffset, pntsd_type, pntsd_size, acl_len, aces_size;
	int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
	unsigned int dacloffset;
	size_t dacl_struct_end;
	u16 num_aces, ace_cnt = 0;
	char *aces_base;
	bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
@@ -1035,8 +1042,11 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
					    parent, &parent_pntsd);
	if (pntsd_size <= 0)
		return -ENOENT;

	dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
	if (!dacloffset || (dacloffset + sizeof(struct smb_acl) > pntsd_size)) {
	if (!dacloffset ||
	    check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
	    dacl_struct_end > (size_t)pntsd_size) {
		rc = -EINVAL;
		goto free_parent_pntsd;
	}
@@ -1240,7 +1250,9 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
	struct smb_ntsd *pntsd = NULL;
	struct smb_acl *pdacl;
	struct posix_acl *posix_acls;
	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size, dacl_offset;
	int rc = 0, pntsd_size, acl_size, aces_size, pdacl_size;
	unsigned int dacl_offset;
	size_t dacl_struct_end;
	struct smb_sid sid;
	int granted = le32_to_cpu(*pdaccess & ~FILE_MAXIMAL_ACCESS_LE);
	struct smb_ace *ace;
@@ -1259,7 +1271,8 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,

	dacl_offset = le32_to_cpu(pntsd->dacloffset);
	if (!dacl_offset ||
	    (dacl_offset + sizeof(struct smb_acl) > pntsd_size))
	    check_add_overflow(dacl_offset, sizeof(struct smb_acl), &dacl_struct_end) ||
	    dacl_struct_end > (size_t)pntsd_size)
		goto err_out;

	pdacl = (struct smb_acl *)((char *)pntsd + le32_to_cpu(pntsd->dacloffset));