ten smb3 server fixes, including three for stable

-----BEGIN PGP SIGNATURE-----
 
 iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmP1kvMACgkQiiy9cAdy
 T1GwlQwAvFBHn8oUwDTj2bIlZRKsMMWGP61qdtgrsKPXcg2hZzw2jDmM6nk12MrS
 AqYCnT1YdjKzDnEgBhHnaPLe+q2M27feJ5Y+FTqv1Hb9VwNY9OJT4hH3II3cuHfm
 zLcnAAdVmtIh7FRYKrwGxqyESNik9N3g4evXcafKKq99sxaGnrnbTWQmOvTqM7kF
 2AeBh6AHlHkoJNfd3BjlB8158Y/7Sfkul1AA7xXm6GWhgnWt2rmWNNqAEpTPXWyj
 xkcb3Jy0wez1D8IXRqyrZdqdFXIfri6mvJoun3zAll6f29T/xxz0CYZUDA881q6E
 EBANy/mLeMMKC4g3c2LPdEUB9ijwA9N+w8E7BSefCIMtN46cOQBTxYrbDfbCEs/m
 ElGo2AcOM25FrkMdl9XSX0Phru3OAOrOivVLCBqbGbHPa0lgBhIZOdy38HCK3ZGm
 0eH6ttyE0p0Mpt1+kex1e6t8XfdyFkHk5gjPElWbsrhSQYK1AAWuBhETWZ79CSlx
 fCou/JZ1
 =HBuN
 -----END PGP SIGNATURE-----

Merge tag '6.3-rc-ksmbd-fixes' of git://git.samba.org/ksmbd

Pull ksmbd server updates from Steve French:

 - Fix for memory leak

 - Two important fixes for frame length checks (which are also now
   stricter)

 - four minor cleanup fixes

 - Fix to clarify ksmbd/Kconfig to indent properl

 - Conversion of the channel list and rpc handle list to xarrays

* tag '6.3-rc-ksmbd-fixes' of git://git.samba.org/ksmbd:
  ksmbd: fix possible memory leak in smb2_lock()
  ksmbd: do not allow the actual frame length to be smaller than the rfc1002 length
  ksmbd: fix wrong data area length for smb2 lock request
  ksmbd: Fix parameter name and comment mismatch
  ksmbd: Fix spelling mistake "excceed" -> "exceeded"
  ksmbd: update Kconfig to note Kerberos support and fix indentation
  ksmbd: Remove duplicated codes
  ksmbd: fix typo, syncronous->synchronous
  ksmbd: Implements sess->rpc_handle_list as xarray
  ksmbd: Implements sess->ksmbd_chann_list as xarray
This commit is contained in:
Linus Torvalds 2023-02-22 14:17:27 -08:00
commit 25ac8c12ff
10 changed files with 98 additions and 151 deletions

View File

@ -33,14 +33,16 @@ config SMB_SERVER
in ksmbd-tools, available from
https://github.com/cifsd-team/ksmbd-tools.
More detail about how to run the ksmbd kernel server is
available via README file
available via the README file
(https://github.com/cifsd-team/ksmbd-tools/blob/master/README).
ksmbd kernel server includes support for auto-negotiation,
Secure negotiate, Pre-authentication integrity, oplock/lease,
compound requests, multi-credit, packet signing, RDMA(smbdirect),
smb3 encryption, copy-offload, secure per-user session
establishment via NTLM or NTLMv2.
establishment via Kerberos or NTLMv2.
if SMB_SERVER
config SMB_SERVER_SMBDIRECT
bool "Support for SMB Direct protocol"
@ -54,6 +56,8 @@ config SMB_SERVER_SMBDIRECT
SMB Direct allows transferring SMB packets over RDMA. If unsure,
say N.
endif
config SMB_SERVER_CHECK_CAP_NET_ADMIN
bool "Enable check network administration capability"
depends on SMB_SERVER

View File

@ -208,9 +208,9 @@ int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen,
return 0;
}
int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
unsigned char tag, const void *value,
size_t vlen)
static int ksmbd_neg_token_alloc(void *context, size_t hdrlen,
unsigned char tag, const void *value,
size_t vlen)
{
struct ksmbd_conn *conn = context;
@ -223,17 +223,16 @@ int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
return 0;
}
int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
unsigned char tag, const void *value,
size_t vlen)
{
return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
}
int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen,
unsigned char tag, const void *value,
size_t vlen)
{
struct ksmbd_conn *conn = context;
conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
if (!conn->mechToken)
return -ENOMEM;
memcpy(conn->mechToken, value, vlen);
conn->mechToken[vlen] = '\0';
return 0;
return ksmbd_neg_token_alloc(context, hdrlen, tag, value, vlen);
}

View File

@ -114,7 +114,7 @@ void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
requests_queue = &conn->requests;
work->syncronous = true;
work->synchronous = true;
}
if (requests_queue) {
@ -139,7 +139,7 @@ int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
spin_lock(&conn->request_lock);
if (!work->multiRsp) {
list_del_init(&work->request_entry);
if (work->syncronous == false)
if (!work->synchronous)
list_del_init(&work->async_request_entry);
ret = 0;
}
@ -312,7 +312,7 @@ int ksmbd_conn_handler_loop(void *p)
max_allowed_pdu_size = SMB3_MAX_MSGSIZE;
if (pdu_size > max_allowed_pdu_size) {
pr_err_ratelimited("PDU length(%u) excceed maximum allowed pdu size(%u) on connection(%d)\n",
pr_err_ratelimited("PDU length(%u) exceeded maximum allowed pdu size(%u) on connection(%d)\n",
pdu_size, max_allowed_pdu_size,
conn->status);
break;

View File

@ -68,7 +68,7 @@ struct ksmbd_work {
/* Request is encrypted */
bool encrypted:1;
/* Is this SYNC or ASYNC ksmbd_work */
bool syncronous:1;
bool synchronous:1;
bool need_invalidate_rkey:1;
unsigned int remote_key;

View File

@ -25,20 +25,19 @@ static DECLARE_RWSEM(sessions_table_lock);
struct ksmbd_session_rpc {
int id;
unsigned int method;
struct list_head list;
};
static void free_channel_list(struct ksmbd_session *sess)
{
struct channel *chann, *tmp;
struct channel *chann;
unsigned long index;
write_lock(&sess->chann_lock);
list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
chann_list) {
list_del(&chann->chann_list);
xa_for_each(&sess->ksmbd_chann_list, index, chann) {
xa_erase(&sess->ksmbd_chann_list, index);
kfree(chann);
}
write_unlock(&sess->chann_lock);
xa_destroy(&sess->ksmbd_chann_list);
}
static void __session_rpc_close(struct ksmbd_session *sess,
@ -58,15 +57,14 @@ static void __session_rpc_close(struct ksmbd_session *sess,
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
{
struct ksmbd_session_rpc *entry;
long index;
while (!list_empty(&sess->rpc_handle_list)) {
entry = list_entry(sess->rpc_handle_list.next,
struct ksmbd_session_rpc,
list);
list_del(&entry->list);
xa_for_each(&sess->rpc_handle_list, index, entry) {
xa_erase(&sess->rpc_handle_list, index);
__session_rpc_close(sess, entry);
}
xa_destroy(&sess->rpc_handle_list);
}
static int __rpc_method(char *rpc_name)
@ -102,13 +100,13 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
if (!entry)
return -EINVAL;
return -ENOMEM;
list_add(&entry->list, &sess->rpc_handle_list);
entry->method = method;
entry->id = ksmbd_ipc_id_alloc();
if (entry->id < 0)
goto free_entry;
xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);
resp = ksmbd_rpc_open(sess, entry->id);
if (!resp)
@ -117,9 +115,9 @@ int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
kvfree(resp);
return entry->id;
free_id:
xa_erase(&sess->rpc_handle_list, entry->id);
ksmbd_rpc_id_free(entry->id);
free_entry:
list_del(&entry->list);
kfree(entry);
return -EINVAL;
}
@ -128,24 +126,17 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
if (entry->id == id) {
list_del(&entry->list);
__session_rpc_close(sess, entry);
break;
}
}
entry = xa_erase(&sess->rpc_handle_list, id);
if (entry)
__session_rpc_close(sess, entry);
}
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
{
struct ksmbd_session_rpc *entry;
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
if (entry->id == id)
return entry->method;
}
return 0;
entry = xa_load(&sess->rpc_handle_list, id);
return entry ? entry->method : 0;
}
void ksmbd_session_destroy(struct ksmbd_session *sess)
@ -190,21 +181,15 @@ int ksmbd_session_register(struct ksmbd_conn *conn,
static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
{
struct channel *chann, *tmp;
struct channel *chann;
write_lock(&sess->chann_lock);
list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
chann_list) {
if (chann->conn == conn) {
list_del(&chann->chann_list);
kfree(chann);
write_unlock(&sess->chann_lock);
return 0;
}
}
write_unlock(&sess->chann_lock);
chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
if (!chann)
return -ENOENT;
return -ENOENT;
kfree(chann);
return 0;
}
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
@ -234,7 +219,7 @@ void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
return;
sess_destroy:
if (list_empty(&sess->ksmbd_chann_list)) {
if (xa_empty(&sess->ksmbd_chann_list)) {
xa_erase(&conn->sessions, sess->id);
ksmbd_session_destroy(sess);
}
@ -320,6 +305,9 @@ static struct ksmbd_session *__session_create(int protocol)
struct ksmbd_session *sess;
int ret;
if (protocol != CIFDS_SESSION_FLAG_SMB2)
return NULL;
sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL);
if (!sess)
return NULL;
@ -329,30 +317,20 @@ static struct ksmbd_session *__session_create(int protocol)
set_session_flag(sess, protocol);
xa_init(&sess->tree_conns);
INIT_LIST_HEAD(&sess->ksmbd_chann_list);
INIT_LIST_HEAD(&sess->rpc_handle_list);
xa_init(&sess->ksmbd_chann_list);
xa_init(&sess->rpc_handle_list);
sess->sequence_number = 1;
rwlock_init(&sess->chann_lock);
switch (protocol) {
case CIFDS_SESSION_FLAG_SMB2:
ret = __init_smb2_session(sess);
break;
default:
ret = -EINVAL;
break;
}
ret = __init_smb2_session(sess);
if (ret)
goto error;
ida_init(&sess->tree_conn_ida);
if (protocol == CIFDS_SESSION_FLAG_SMB2) {
down_write(&sessions_table_lock);
hash_add(sessions_table, &sess->hlist, sess->id);
up_write(&sessions_table_lock);
}
down_write(&sessions_table_lock);
hash_add(sessions_table, &sess->hlist, sess->id);
up_write(&sessions_table_lock);
return sess;
error:

View File

@ -21,7 +21,6 @@ struct ksmbd_file_table;
struct channel {
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
struct ksmbd_conn *conn;
struct list_head chann_list;
};
struct preauth_session {
@ -50,11 +49,10 @@ struct ksmbd_session {
char sess_key[CIFS_KEY_SIZE];
struct hlist_node hlist;
rwlock_t chann_lock;
struct list_head ksmbd_chann_list;
struct xarray ksmbd_chann_list;
struct xarray tree_conns;
struct ida tree_conn_ida;
struct list_head rpc_handle_list;
struct xarray rpc_handle_list;
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];

View File

@ -149,15 +149,11 @@ static int smb2_get_data_area_len(unsigned int *off, unsigned int *len,
break;
case SMB2_LOCK:
{
int lock_count;
unsigned short lock_count;
/*
* smb2_lock request size is 48 included single
* smb2_lock_element structure size.
*/
lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount) - 1;
lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount);
if (lock_count > 0) {
*off = __SMB2_HEADER_STRUCTURE_SIZE + 48;
*off = offsetof(struct smb2_lock_req, locks);
*len = sizeof(struct smb2_lock_element) * lock_count;
}
break;
@ -412,20 +408,19 @@ int ksmbd_smb2_check_message(struct ksmbd_work *work)
goto validate_credit;
/*
* windows client also pad up to 8 bytes when compounding.
* If pad is longer than eight bytes, log the server behavior
* (once), since may indicate a problem but allow it and
* continue since the frame is parseable.
* SMB2 NEGOTIATE request will be validated when message
* handling proceeds.
*/
if (clc_len < len) {
ksmbd_debug(SMB,
"cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
len, clc_len, command,
le64_to_cpu(hdr->MessageId));
if (command == SMB2_NEGOTIATE_HE)
goto validate_credit;
}
ksmbd_debug(SMB,
/*
* Allow a message that padded to 8byte boundary.
*/
if (clc_len < len && (len - clc_len) < 8)
goto validate_credit;
pr_err_ratelimited(
"cli req too short, len %d not %d. cmd:%d mid:%llu\n",
len, clc_len, command,
le64_to_cpu(hdr->MessageId));

View File

@ -75,14 +75,7 @@ static inline bool check_session_id(struct ksmbd_conn *conn, u64 id)
struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn)
{
struct channel *chann;
list_for_each_entry(chann, &sess->ksmbd_chann_list, chann_list) {
if (chann->conn == conn)
return chann;
}
return NULL;
return xa_load(&sess->ksmbd_chann_list, (long)conn);
}
/**
@ -506,7 +499,7 @@ int init_smb2_rsp_hdr(struct ksmbd_work *work)
rsp_hdr->SessionId = rcv_hdr->SessionId;
memcpy(rsp_hdr->Signature, rcv_hdr->Signature, 16);
work->syncronous = true;
work->synchronous = true;
if (work->async_id) {
ksmbd_release_id(&conn->async_ida, work->async_id);
work->async_id = 0;
@ -596,6 +589,7 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_session *prev_sess = ksmbd_session_lookup_slowpath(id);
struct ksmbd_user *prev_user;
struct channel *chann;
long index;
if (!prev_sess)
return;
@ -609,10 +603,8 @@ static void destroy_previous_session(struct ksmbd_conn *conn,
return;
prev_sess->state = SMB2_SESSION_EXPIRED;
write_lock(&prev_sess->chann_lock);
list_for_each_entry(chann, &prev_sess->ksmbd_chann_list, chann_list)
xa_for_each(&prev_sess->ksmbd_chann_list, index, chann)
chann->conn->status = KSMBD_SESS_EXITING;
write_unlock(&prev_sess->chann_lock);
}
/**
@ -653,7 +645,7 @@ int setup_async_work(struct ksmbd_work *work, void (*fn)(void **), void **arg)
pr_err("Failed to alloc async message id\n");
return id;
}
work->syncronous = false;
work->synchronous = false;
work->async_id = id;
rsp_hdr->Id.AsyncId = cpu_to_le64(id);
@ -1520,19 +1512,14 @@ static int ntlm_authenticate(struct ksmbd_work *work)
binding_session:
if (conn->dialect >= SMB30_PROT_ID) {
read_lock(&sess->chann_lock);
chann = lookup_chann_list(sess, conn);
read_unlock(&sess->chann_lock);
if (!chann) {
chann = kmalloc(sizeof(struct channel), GFP_KERNEL);
if (!chann)
return -ENOMEM;
chann->conn = conn;
INIT_LIST_HEAD(&chann->chann_list);
write_lock(&sess->chann_lock);
list_add(&chann->chann_list, &sess->ksmbd_chann_list);
write_unlock(&sess->chann_lock);
xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL);
}
}
@ -1607,19 +1594,14 @@ static int krb5_authenticate(struct ksmbd_work *work)
}
if (conn->dialect >= SMB30_PROT_ID) {
read_lock(&sess->chann_lock);
chann = lookup_chann_list(sess, conn);
read_unlock(&sess->chann_lock);
if (!chann) {
chann = kmalloc(sizeof(struct channel), GFP_KERNEL);
if (!chann)
return -ENOMEM;
chann->conn = conn;
INIT_LIST_HEAD(&chann->chann_list);
write_lock(&sess->chann_lock);
list_add(&chann->chann_list, &sess->ksmbd_chann_list);
write_unlock(&sess->chann_lock);
xa_store(&sess->ksmbd_chann_list, (long)conn, chann, GFP_KERNEL);
}
}
@ -6645,7 +6627,7 @@ int smb2_cancel(struct ksmbd_work *work)
struct ksmbd_conn *conn = work->conn;
struct smb2_hdr *hdr = smb2_get_msg(work->request_buf);
struct smb2_hdr *chdr;
struct ksmbd_work *cancel_work = NULL, *iter;
struct ksmbd_work *iter;
struct list_head *command_list;
ksmbd_debug(SMB, "smb2 cancel called on mid %llu, async flags 0x%x\n",
@ -6667,7 +6649,9 @@ int smb2_cancel(struct ksmbd_work *work)
"smb2 with AsyncId %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->Id.AsyncId),
le16_to_cpu(chdr->Command));
cancel_work = iter;
iter->state = KSMBD_WORK_CANCELLED;
if (iter->cancel_fn)
iter->cancel_fn(iter->cancel_argv);
break;
}
spin_unlock(&conn->request_lock);
@ -6686,18 +6670,12 @@ int smb2_cancel(struct ksmbd_work *work)
"smb2 with mid %llu cancelled command = 0x%x\n",
le64_to_cpu(hdr->MessageId),
le16_to_cpu(chdr->Command));
cancel_work = iter;
iter->state = KSMBD_WORK_CANCELLED;
break;
}
spin_unlock(&conn->request_lock);
}
if (cancel_work) {
cancel_work->state = KSMBD_WORK_CANCELLED;
if (cancel_work->cancel_fn)
cancel_work->cancel_fn(cancel_work->cancel_argv);
}
/* For SMB2_CANCEL command itself send no response*/
work->send_no_response = 1;
return 0;
@ -7062,6 +7040,14 @@ skip:
ksmbd_vfs_posix_lock_wait(flock);
spin_lock(&work->conn->request_lock);
spin_lock(&fp->f_lock);
list_del(&work->fp_entry);
work->cancel_fn = NULL;
kfree(argv);
spin_unlock(&fp->f_lock);
spin_unlock(&work->conn->request_lock);
if (work->state != KSMBD_WORK_ACTIVE) {
list_del(&smb_lock->llist);
spin_lock(&work->conn->llist_lock);
@ -7070,9 +7056,6 @@ skip:
locks_free_lock(flock);
if (work->state == KSMBD_WORK_CANCELLED) {
spin_lock(&fp->f_lock);
list_del(&work->fp_entry);
spin_unlock(&fp->f_lock);
rsp->hdr.Status =
STATUS_CANCELLED;
kfree(smb_lock);
@ -7094,9 +7077,6 @@ skip:
list_del(&smb_lock->clist);
spin_unlock(&work->conn->llist_lock);
spin_lock(&fp->f_lock);
list_del(&work->fp_entry);
spin_unlock(&fp->f_lock);
goto retry;
} else if (!rc) {
spin_lock(&work->conn->llist_lock);
@ -8410,14 +8390,11 @@ int smb3_check_sign_req(struct ksmbd_work *work)
if (le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
signing_key = work->sess->smb3signingkey;
} else {
read_lock(&work->sess->chann_lock);
chann = lookup_chann_list(work->sess, conn);
if (!chann) {
read_unlock(&work->sess->chann_lock);
return 0;
}
signing_key = chann->smb3signingkey;
read_unlock(&work->sess->chann_lock);
}
if (!signing_key) {
@ -8477,14 +8454,11 @@ void smb3_set_sign_rsp(struct ksmbd_work *work)
le16_to_cpu(hdr->Command) == SMB2_SESSION_SETUP_HE) {
signing_key = work->sess->smb3signingkey;
} else {
read_lock(&work->sess->chann_lock);
chann = lookup_chann_list(work->sess, work->conn);
if (!chann) {
read_unlock(&work->sess->chann_lock);
return;
}
signing_key = chann->smb3signingkey;
read_unlock(&work->sess->chann_lock);
}
if (!signing_key)

View File

@ -952,9 +952,9 @@ ssize_t ksmbd_vfs_getxattr(struct mnt_idmap *idmap,
* ksmbd_vfs_setxattr() - vfs helper for smb set extended attributes value
* @idmap: idmap of the relevant mount
* @dentry: dentry to set XATTR at
* @name: xattr name for setxattr
* @value: xattr value to set
* @size: size of xattr value
* @attr_name: xattr name for setxattr
* @attr_value: xattr value to set
* @attr_size: size of xattr value
* @flags: destination buffer length
*
* Return: 0 on success, otherwise error

View File

@ -365,12 +365,11 @@ static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp)
static void set_close_state_blocked_works(struct ksmbd_file *fp)
{
struct ksmbd_work *cancel_work, *ctmp;
struct ksmbd_work *cancel_work;
spin_lock(&fp->f_lock);
list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works,
list_for_each_entry(cancel_work, &fp->blocked_works,
fp_entry) {
list_del(&cancel_work->fp_entry);
cancel_work->state = KSMBD_WORK_CLOSED;
cancel_work->cancel_fn(cancel_work->cancel_argv);
}