summaryrefslogtreecommitdiff
path: root/fs/smb
diff options
context:
space:
mode:
Diffstat (limited to 'fs/smb')
-rw-r--r--fs/smb/client/cifs_debug.c2
-rw-r--r--fs/smb/client/inode.c14
-rw-r--r--fs/smb/client/smb2pdu.c16
-rw-r--r--fs/smb/server/Kconfig1
-rw-r--r--fs/smb/server/auth.c2
-rw-r--r--fs/smb/server/connection.c19
-rw-r--r--fs/smb/server/connection.h1
-rw-r--r--fs/smb/server/mgmt/share_config.c7
-rw-r--r--fs/smb/server/mgmt/user_config.c1
-rw-r--r--fs/smb/server/mgmt/user_session.c24
-rw-r--r--fs/smb/server/mgmt/user_session.h1
-rw-r--r--fs/smb/server/server.c4
-rw-r--r--fs/smb/server/smb2ops.c11
-rw-r--r--fs/smb/server/smb2pdu.c15
-rw-r--r--fs/smb/server/smbacl.c4
-rw-r--r--fs/smb/server/transport_ipc.c12
-rw-r--r--fs/smb/server/transport_tcp.c6
-rw-r--r--fs/smb/server/vfs.c3
-rw-r--r--fs/smb/server/vfs_cache.c5
19 files changed, 104 insertions, 44 deletions
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 08538ad0f8dc..3045db555c3c 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -776,7 +776,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&server->smb2slowcmd[i], 0);
server->time_per_cmd[i] = 0;
server->slowest_cmd[i] = 0;
- server->fastest_cmd[0] = 0;
+ server->fastest_cmd[i] = 0;
}
#endif /* CONFIG_CIFS_STATS2 */
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index 72d2052a22d4..f23c36d6ae6b 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -3278,13 +3278,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto out;
- if ((attrs->ia_valid & ATTR_SIZE) &&
- attrs->ia_size != i_size_read(inode)) {
- truncate_setsize(inode, attrs->ia_size);
- netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
- fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
- }
-
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);
@@ -3496,13 +3489,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
if (rc)
goto cifs_setattr_exit;
- if ((attrs->ia_valid & ATTR_SIZE) &&
- attrs->ia_size != i_size_read(inode)) {
- truncate_setsize(inode, attrs->ia_size);
- netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true);
- fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
- }
-
setattr_copy(&nop_mnt_idmap, inode, attrs);
mark_inode_dirty(inode);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 5aca5b4a9868..46580d6eab62 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4454,8 +4454,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
if (rc)
return rc;
- if (server == NULL)
- return -ECONNABORTED;
+ if (!server) {
+ rc = -ECONNABORTED;
+ goto free_req;
+ }
shdr = &req->hdr;
shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
@@ -4486,8 +4488,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->subreq.io_iter,
true, need_invalidate);
- if (!rdata->mr)
- return -EAGAIN;
+ if (!rdata->mr) {
+ rc = -EAGAIN;
+ goto free_req;
+ }
req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
if (need_invalidate)
@@ -4530,6 +4534,10 @@ smb2_new_read_req(void **buf, unsigned int *total_len,
*buf = req;
return rc;
+
+free_req:
+ cifs_small_buf_release(req);
+ return rc;
}
static void
diff --git a/fs/smb/server/Kconfig b/fs/smb/server/Kconfig
index 12594879cb64..8b91a6323d93 100644
--- a/fs/smb/server/Kconfig
+++ b/fs/smb/server/Kconfig
@@ -9,7 +9,6 @@ config SMB_SERVER
select CRYPTO
select CRYPTO_ECB
select CRYPTO_LIB_ARC4
- select CRYPTO_LIB_DES
select CRYPTO_LIB_MD5
select CRYPTO_LIB_SHA256
select CRYPTO_LIB_SHA512
diff --git a/fs/smb/server/auth.c b/fs/smb/server/auth.c
index 3f8258954901..9844aaad7ab3 100644
--- a/fs/smb/server/auth.c
+++ b/fs/smb/server/auth.c
@@ -24,7 +24,6 @@
#include <linux/fips.h>
#include <crypto/arc4.h>
-#include <crypto/des.h>
#include "server.h"
#include "smb_common.h"
@@ -436,6 +435,7 @@ int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
resp_ext = ksmbd_ipc_login_request_ext(resp->login_response.account);
user = ksmbd_alloc_user(&resp->login_response, resp_ext);
+ kvfree(resp_ext);
if (!user) {
ksmbd_debug(AUTH, "login failure\n");
retval = -ENOMEM;
diff --git a/fs/smb/server/connection.c b/fs/smb/server/connection.c
index c193b87cfa75..ff6cdd720dd4 100644
--- a/fs/smb/server/connection.c
+++ b/fs/smb/server/connection.c
@@ -11,6 +11,7 @@
#include "server.h"
#include "smb_common.h"
#include "mgmt/ksmbd_ida.h"
+#include "mgmt/user_session.h"
#include "connection.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
@@ -123,6 +124,7 @@ void ksmbd_conn_free(struct ksmbd_conn *conn)
kvfree(conn->request_buf);
kfree(conn->preauth_info);
kfree(conn->mechToken);
+ ksmbd_preauth_session_destroy(conn);
ksmbd_conn_put(conn);
}
@@ -161,6 +163,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)
init_waitqueue_head(&conn->r_count_q);
INIT_LIST_HEAD(&conn->requests);
INIT_LIST_HEAD(&conn->async_requests);
+ INIT_LIST_HEAD(&conn->preauth_sess_table);
spin_lock_init(&conn->request_lock);
spin_lock_init(&conn->credits_lock);
ida_init(&conn->async_ida);
@@ -252,6 +255,22 @@ void ksmbd_all_conn_set_status(u64 sess_id, u32 status)
up_read(&conn_list_lock);
}
+void ksmbd_conn_abort(struct ksmbd_conn *conn)
+{
+ bool shutdown = false;
+
+ spin_lock(&conn->request_lock);
+ if (!ksmbd_conn_exiting(conn) && !ksmbd_conn_releasing(conn)) {
+ ksmbd_conn_set_exiting(conn);
+ shutdown = true;
+ }
+ spin_unlock(&conn->request_lock);
+ wake_up_all(&conn->req_running_q);
+
+ if (shutdown && conn->transport->ops->shutdown)
+ conn->transport->ops->shutdown(conn->transport);
+}
+
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
{
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h
index f8f88651166b..ceb1c1aefbf6 100644
--- a/fs/smb/server/connection.h
+++ b/fs/smb/server/connection.h
@@ -168,6 +168,7 @@ struct ksmbd_conn *ksmbd_conn_alloc(void);
void ksmbd_conn_free(struct ksmbd_conn *conn);
struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn);
void ksmbd_conn_put(struct ksmbd_conn *conn);
+void ksmbd_conn_abort(struct ksmbd_conn *conn);
int ksmbd_conn_wq_init(void);
void ksmbd_conn_wq_destroy(void);
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
diff --git a/fs/smb/server/mgmt/share_config.c b/fs/smb/server/mgmt/share_config.c
index c9b1108d6e96..1e90fd30bd77 100644
--- a/fs/smb/server/mgmt/share_config.c
+++ b/fs/smb/server/mgmt/share_config.c
@@ -158,6 +158,11 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
atomic_set(&share->refcount, 1);
INIT_LIST_HEAD(&share->veto_list);
share->name = kstrdup(name, KSMBD_DEFAULT_GFP);
+ if (!share->name) {
+ kill_share(share);
+ share = NULL;
+ goto out;
+ }
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
int path_len = PATH_MAX;
@@ -199,7 +204,7 @@ static struct ksmbd_share_config *share_config_request(struct ksmbd_work *work,
share->path = NULL;
}
}
- if (ret || !share->name) {
+ if (ret) {
kill_share(share);
share = NULL;
goto out;
diff --git a/fs/smb/server/mgmt/user_config.c b/fs/smb/server/mgmt/user_config.c
index d051ac3bc831..53db14e91bfd 100644
--- a/fs/smb/server/mgmt/user_config.c
+++ b/fs/smb/server/mgmt/user_config.c
@@ -26,6 +26,7 @@ struct ksmbd_user *ksmbd_login_user(const char *account)
resp_ext = ksmbd_ipc_login_request_ext(account);
user = ksmbd_alloc_user(resp, resp_ext);
+ kvfree(resp_ext);
out:
kvfree(resp);
return user;
diff --git a/fs/smb/server/mgmt/user_session.c b/fs/smb/server/mgmt/user_session.c
index 5bc2f18d68bb..e81591566521 100644
--- a/fs/smb/server/mgmt/user_session.c
+++ b/fs/smb/server/mgmt/user_session.c
@@ -212,10 +212,21 @@ static void ksmbd_expire_session(struct ksmbd_conn *conn)
int ksmbd_session_register(struct ksmbd_conn *conn,
struct ksmbd_session *sess)
{
+ int ret;
+
sess->dialect = conn->dialect;
memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
ksmbd_expire_session(conn);
- return xa_err(xa_store(&conn->sessions, sess->id, sess, KSMBD_DEFAULT_GFP));
+ ret = xa_err(xa_store(&conn->sessions, sess->id, sess,
+ KSMBD_DEFAULT_GFP));
+ if (ret) {
+ down_write(&sessions_table_lock);
+ hash_del(&sess->hlist);
+ up_write(&sessions_table_lock);
+ ksmbd_user_session_put(sess);
+ }
+
+ return ret;
}
static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
@@ -374,6 +385,17 @@ struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
return sess;
}
+void ksmbd_preauth_session_destroy(struct ksmbd_conn *conn)
+{
+ struct preauth_session *sess, *tmp;
+
+ list_for_each_entry_safe(sess, tmp, &conn->preauth_sess_table,
+ preauth_entry) {
+ list_del(&sess->preauth_entry);
+ kfree(sess);
+ }
+}
+
void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id)
{
diff --git a/fs/smb/server/mgmt/user_session.h b/fs/smb/server/mgmt/user_session.h
index cba7f688f6b5..525650d6876c 100644
--- a/fs/smb/server/mgmt/user_session.h
+++ b/fs/smb/server/mgmt/user_session.h
@@ -101,6 +101,7 @@ void destroy_previous_session(struct ksmbd_conn *conn,
struct ksmbd_user *user, u64 id);
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
u64 sess_id);
+void ksmbd_preauth_session_destroy(struct ksmbd_conn *conn);
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
unsigned long long id);
diff --git a/fs/smb/server/server.c b/fs/smb/server/server.c
index 9da4525010cc..76515d9777e3 100644
--- a/fs/smb/server/server.c
+++ b/fs/smb/server/server.c
@@ -170,8 +170,10 @@ static void __handle_ksmbd_work(struct ksmbd_work *work,
if (conn->ops->is_transform_hdr &&
conn->ops->is_transform_hdr(work->request_buf)) {
rc = conn->ops->decrypt_req(work);
- if (rc < 0)
+ if (rc < 0) {
+ ksmbd_conn_abort(conn);
return;
+ }
work->encrypted = true;
}
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
index bcf05caa2304..4c50b7f57f76 100644
--- a/fs/smb/server/smb2ops.c
+++ b/fs/smb/server/smb2ops.c
@@ -262,8 +262,10 @@ void init_smb3_02_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
+ /*
+ * Durable handles are in-memory only. Do not advertise persistent
+ * handles until CA recovery and fencing are implemented.
+ */
}
/**
@@ -286,10 +288,7 @@ int init_smb3_11_server(struct ksmbd_conn *conn)
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE)
- conn->vals->req_capabilities |= SMB2_GLOBAL_CAP_PERSISTENT_HANDLES;
-
- INIT_LIST_HEAD(&conn->preauth_sess_table);
+ /* See init_smb3_02_server(): persistent handles require CA recovery. */
return 0;
}
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
index e532ee89e327..89febfb8fc90 100644
--- a/fs/smb/server/smb2pdu.c
+++ b/fs/smb/server/smb2pdu.c
@@ -1170,7 +1170,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
KSMBD_DEFAULT_GFP);
if (!conn->preauth_info) {
rc = -ENOMEM;
- rsp->hdr.Status = STATUS_INVALID_PARAMETER;
+ rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
goto err_out;
}
@@ -1267,7 +1267,7 @@ int smb2_handle_negotiate(struct ksmbd_work *work)
ksmbd_conn_set_need_setup(conn);
err_out:
- if (rc)
+ if (rc && rsp->hdr.Status == STATUS_SUCCESS)
rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES;
if (!rc)
@@ -2049,12 +2049,11 @@ int smb2_tree_connect(struct ksmbd_work *work)
write_unlock(&sess->tree_conns_lock);
rsp->StructureSize = cpu_to_le16(16);
out_err1:
- if (server_conf.flags & KSMBD_GLOBAL_FLAG_DURABLE_HANDLE && share &&
- test_share_config_flag(share,
- KSMBD_SHARE_FLAG_CONTINUOUS_AVAILABILITY))
- rsp->Capabilities = SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY;
- else
- rsp->Capabilities = 0;
+ /*
+ * A configured CA share is not continuously available until persistent
+ * open recovery, ownership fencing, and failover are implemented.
+ */
+ rsp->Capabilities = 0;
rsp->Reserved = 0;
/* default manual caching */
rsp->ShareFlags = SMB2_SHAREFLAG_MANUAL_CACHING;
diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c
index d3d0a22620f9..0879012aa63c 100644
--- a/fs/smb/server/smbacl.c
+++ b/fs/smb/server/smbacl.c
@@ -1471,7 +1471,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
DELETE;
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
- aces_size = acl_size - sizeof(struct smb_acl);
+ aces_size = pdacl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
@@ -1495,7 +1495,7 @@ int smb_check_perm_dacl(struct ksmbd_conn *conn, const struct path *path,
id_to_sid(uid, sid_type, &sid);
ace = (struct smb_ace *)((char *)pdacl + sizeof(struct smb_acl));
- aces_size = acl_size - sizeof(struct smb_acl);
+ aces_size = pdacl_size - sizeof(struct smb_acl);
for (i = 0; i < le16_to_cpu(pdacl->num_aces); i++) {
if (aces_size < offsetof(struct smb_ace, sid) +
CIFS_SID_BASE_SIZE)
diff --git a/fs/smb/server/transport_ipc.c b/fs/smb/server/transport_ipc.c
index 1c5645238bd3..225f6459f378 100644
--- a/fs/smb/server/transport_ipc.c
+++ b/fs/smb/server/transport_ipc.c
@@ -498,6 +498,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_rpc_command *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_rpc_command))
+ return -EINVAL;
+
if (check_add_overflow(sizeof(struct ksmbd_rpc_command),
resp->payload_sz, &msg_sz))
return -EINVAL;
@@ -507,6 +510,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_spnego_authen_response *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_spnego_authen_response))
+ return -EINVAL;
+
msg_sz = sizeof(struct ksmbd_spnego_authen_response) +
resp->session_key_len + resp->spnego_blob_len;
break;
@@ -515,6 +521,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_share_config_response *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_share_config_response))
+ return -EINVAL;
+
if (resp->payload_sz) {
if (resp->payload_sz < resp->veto_list_sz)
return -EINVAL;
@@ -529,6 +538,9 @@ static int ipc_validate_msg(struct ipc_msg_table_entry *entry)
{
struct ksmbd_login_response_ext *resp = entry->response;
+ if (entry->msg_sz < sizeof(struct ksmbd_login_response_ext))
+ return -EINVAL;
+
if (resp->ngroups) {
if (resp->ngroups < 0 ||
resp->ngroups > NGROUPS_MAX) {
diff --git a/fs/smb/server/transport_tcp.c b/fs/smb/server/transport_tcp.c
index a09b07b3b108..78c909d66814 100644
--- a/fs/smb/server/transport_tcp.c
+++ b/fs/smb/server/transport_tcp.c
@@ -454,6 +454,11 @@ static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
atomic_dec(&active_num_conn);
}
+static void ksmbd_tcp_shutdown(struct ksmbd_transport *t)
+{
+ kernel_sock_shutdown(TCP_TRANS(t)->sock, SHUT_RDWR);
+}
+
static void tcp_destroy_socket(struct socket *ksmbd_socket)
{
int ret;
@@ -709,5 +714,6 @@ static const struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
.read = ksmbd_tcp_read,
.writev = ksmbd_tcp_writev,
.disconnect = ksmbd_tcp_disconnect,
+ .shutdown = ksmbd_tcp_shutdown,
.free_transport = ksmbd_tcp_free_transport,
};
diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c
index 764eca75291c..216b6e427aa7 100644
--- a/fs/smb/server/vfs.c
+++ b/fs/smb/server/vfs.c
@@ -327,9 +327,6 @@ static int check_lock_range(struct file *filp, loff_t start, loff_t end,
struct file_lock_context *ctx = locks_inode_context(file_inode(filp));
int error = 0;
- if (start == end)
- return 0;
-
if (!ctx || list_empty_careful(&ctx->flc_posix))
return 0;
diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c
index 8b5f50ac61e2..0b654021bec0 100644
--- a/fs/smb/server/vfs_cache.c
+++ b/fs/smb/server/vfs_cache.c
@@ -1103,9 +1103,12 @@ void ksmbd_launch_ksmbd_durable_scavenger(void)
server_conf.dh_task = kthread_run(ksmbd_durable_scavenger,
(void *)NULL, "ksmbd-durable-scavenger");
- if (IS_ERR(server_conf.dh_task))
+ if (IS_ERR(server_conf.dh_task)) {
pr_err("cannot start conn thread, err : %ld\n",
PTR_ERR(server_conf.dh_task));
+ server_conf.dh_task = NULL;
+ durable_scavenger_running = false;
+ }
mutex_unlock(&durable_scavenger_lock);
}