brintos

brintos / linux-shallow public Read only

0
0
Text · 10.2 KiB · ad02fe5 Raw
455 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.4 */5 6#include <linux/list.h>7#include <linux/slab.h>8#include <linux/rwsem.h>9#include <linux/xarray.h>10 11#include "ksmbd_ida.h"12#include "user_session.h"13#include "user_config.h"14#include "tree_connect.h"15#include "../transport_ipc.h"16#include "../connection.h"17#include "../vfs_cache.h"18 19static DEFINE_IDA(session_ida);20 21#define SESSION_HASH_BITS		322static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);23static DECLARE_RWSEM(sessions_table_lock);24 25struct ksmbd_session_rpc {26	int			id;27	unsigned int		method;28};29 30static void free_channel_list(struct ksmbd_session *sess)31{32	struct channel *chann;33	unsigned long index;34 35	xa_for_each(&sess->ksmbd_chann_list, index, chann) {36		xa_erase(&sess->ksmbd_chann_list, index);37		kfree(chann);38	}39 40	xa_destroy(&sess->ksmbd_chann_list);41}42 43static void __session_rpc_close(struct ksmbd_session *sess,44				struct ksmbd_session_rpc *entry)45{46	struct ksmbd_rpc_command *resp;47 48	resp = ksmbd_rpc_close(sess, entry->id);49	if (!resp)50		pr_err("Unable to close RPC pipe %d\n", entry->id);51 52	kvfree(resp);53	ksmbd_rpc_id_free(entry->id);54	kfree(entry);55}56 57static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)58{59	struct ksmbd_session_rpc *entry;60	long index;61 62	xa_for_each(&sess->rpc_handle_list, index, entry) {63		xa_erase(&sess->rpc_handle_list, index);64		__session_rpc_close(sess, entry);65	}66 67	xa_destroy(&sess->rpc_handle_list);68}69 70static int __rpc_method(char *rpc_name)71{72	if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc"))73		return KSMBD_RPC_SRVSVC_METHOD_INVOKE;74 75	if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc"))76		return KSMBD_RPC_WKSSVC_METHOD_INVOKE;77 78	if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman"))79		return KSMBD_RPC_RAP_METHOD;80 81	if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr"))82		return KSMBD_RPC_SAMR_METHOD_INVOKE;83 84	if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc"))85		return KSMBD_RPC_LSARPC_METHOD_INVOKE;86 87	pr_err("Unsupported RPC: %s\n", rpc_name);88	return 0;89}90 91int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)92{93	struct ksmbd_session_rpc *entry, *old;94	struct ksmbd_rpc_command *resp;95	int method;96 97	method = __rpc_method(rpc_name);98	if (!method)99		return -EINVAL;100 101	entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);102	if (!entry)103		return -ENOMEM;104 105	entry->method = method;106	entry->id = ksmbd_ipc_id_alloc();107	if (entry->id < 0)108		goto free_entry;109	old = xa_store(&sess->rpc_handle_list, entry->id, entry, GFP_KERNEL);110	if (xa_is_err(old))111		goto free_id;112 113	resp = ksmbd_rpc_open(sess, entry->id);114	if (!resp)115		goto erase_xa;116 117	kvfree(resp);118	return entry->id;119erase_xa:120	xa_erase(&sess->rpc_handle_list, entry->id);121free_id:122	ksmbd_rpc_id_free(entry->id);123free_entry:124	kfree(entry);125	return -EINVAL;126}127 128void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)129{130	struct ksmbd_session_rpc *entry;131 132	entry = xa_erase(&sess->rpc_handle_list, id);133	if (entry)134		__session_rpc_close(sess, entry);135}136 137int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)138{139	struct ksmbd_session_rpc *entry;140 141	entry = xa_load(&sess->rpc_handle_list, id);142	return entry ? entry->method : 0;143}144 145void ksmbd_session_destroy(struct ksmbd_session *sess)146{147	if (!sess)148		return;149 150	if (sess->user)151		ksmbd_free_user(sess->user);152 153	ksmbd_tree_conn_session_logoff(sess);154	ksmbd_destroy_file_table(&sess->file_table);155	ksmbd_launch_ksmbd_durable_scavenger();156	ksmbd_session_rpc_clear_list(sess);157	free_channel_list(sess);158	kfree(sess->Preauth_HashValue);159	ksmbd_release_id(&session_ida, sess->id);160	kfree(sess);161}162 163struct ksmbd_session *__session_lookup(unsigned long long id)164{165	struct ksmbd_session *sess;166 167	hash_for_each_possible(sessions_table, sess, hlist, id) {168		if (id == sess->id) {169			sess->last_active = jiffies;170			return sess;171		}172	}173	return NULL;174}175 176static void ksmbd_expire_session(struct ksmbd_conn *conn)177{178	unsigned long id;179	struct ksmbd_session *sess;180 181	down_write(&sessions_table_lock);182	down_write(&conn->session_lock);183	xa_for_each(&conn->sessions, id, sess) {184		if (atomic_read(&sess->refcnt) == 0 &&185		    (sess->state != SMB2_SESSION_VALID ||186		     time_after(jiffies,187			       sess->last_active + SMB2_SESSION_TIMEOUT))) {188			xa_erase(&conn->sessions, sess->id);189			hash_del(&sess->hlist);190			ksmbd_session_destroy(sess);191			continue;192		}193	}194	up_write(&conn->session_lock);195	up_write(&sessions_table_lock);196}197 198int ksmbd_session_register(struct ksmbd_conn *conn,199			   struct ksmbd_session *sess)200{201	sess->dialect = conn->dialect;202	memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);203	ksmbd_expire_session(conn);204	return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));205}206 207static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)208{209	struct channel *chann;210 211	chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);212	if (!chann)213		return -ENOENT;214 215	kfree(chann);216	return 0;217}218 219void ksmbd_sessions_deregister(struct ksmbd_conn *conn)220{221	struct ksmbd_session *sess;222	unsigned long id;223 224	down_write(&sessions_table_lock);225	if (conn->binding) {226		int bkt;227		struct hlist_node *tmp;228 229		hash_for_each_safe(sessions_table, bkt, tmp, sess, hlist) {230			if (!ksmbd_chann_del(conn, sess) &&231			    xa_empty(&sess->ksmbd_chann_list)) {232				hash_del(&sess->hlist);233				ksmbd_session_destroy(sess);234			}235		}236	}237 238	down_write(&conn->session_lock);239	xa_for_each(&conn->sessions, id, sess) {240		unsigned long chann_id;241		struct channel *chann;242 243		xa_for_each(&sess->ksmbd_chann_list, chann_id, chann) {244			if (chann->conn != conn)245				ksmbd_conn_set_exiting(chann->conn);246		}247 248		ksmbd_chann_del(conn, sess);249		if (xa_empty(&sess->ksmbd_chann_list)) {250			xa_erase(&conn->sessions, sess->id);251			hash_del(&sess->hlist);252			ksmbd_session_destroy(sess);253		}254	}255	up_write(&conn->session_lock);256	up_write(&sessions_table_lock);257}258 259struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,260					   unsigned long long id)261{262	struct ksmbd_session *sess;263 264	down_read(&conn->session_lock);265	sess = xa_load(&conn->sessions, id);266	if (sess)267		sess->last_active = jiffies;268	up_read(&conn->session_lock);269	return sess;270}271 272struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)273{274	struct ksmbd_session *sess;275 276	down_read(&sessions_table_lock);277	sess = __session_lookup(id);278	up_read(&sessions_table_lock);279 280	return sess;281}282 283struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,284					       unsigned long long id)285{286	struct ksmbd_session *sess;287 288	sess = ksmbd_session_lookup(conn, id);289	if (!sess && conn->binding)290		sess = ksmbd_session_lookup_slowpath(id);291	if (sess && sess->state != SMB2_SESSION_VALID)292		sess = NULL;293	return sess;294}295 296void ksmbd_user_session_get(struct ksmbd_session *sess)297{298	atomic_inc(&sess->refcnt);299}300 301void ksmbd_user_session_put(struct ksmbd_session *sess)302{303	if (!sess)304		return;305 306	if (atomic_read(&sess->refcnt) <= 0)307		WARN_ON(1);308	else309		atomic_dec(&sess->refcnt);310}311 312struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,313						    u64 sess_id)314{315	struct preauth_session *sess;316 317	sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL);318	if (!sess)319		return NULL;320 321	sess->id = sess_id;322	memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue,323	       PREAUTH_HASHVALUE_SIZE);324	list_add(&sess->preauth_entry, &conn->preauth_sess_table);325 326	return sess;327}328 329void destroy_previous_session(struct ksmbd_conn *conn,330			      struct ksmbd_user *user, u64 id)331{332	struct ksmbd_session *prev_sess;333	struct ksmbd_user *prev_user;334	int err;335 336	down_write(&sessions_table_lock);337	down_write(&conn->session_lock);338	prev_sess = __session_lookup(id);339	if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)340		goto out;341 342	prev_user = prev_sess->user;343	if (!prev_user ||344	    strcmp(user->name, prev_user->name) ||345	    user->passkey_sz != prev_user->passkey_sz ||346	    memcmp(user->passkey, prev_user->passkey, user->passkey_sz))347		goto out;348 349	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);350	err = ksmbd_conn_wait_idle_sess_id(conn, id);351	if (err) {352		ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);353		goto out;354	}355 356	ksmbd_destroy_file_table(&prev_sess->file_table);357	prev_sess->state = SMB2_SESSION_EXPIRED;358	ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);359	ksmbd_launch_ksmbd_durable_scavenger();360out:361	up_write(&conn->session_lock);362	up_write(&sessions_table_lock);363}364 365static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,366					   unsigned long long id)367{368	return sess->id == id;369}370 371struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,372						     unsigned long long id)373{374	struct preauth_session *sess = NULL;375 376	list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) {377		if (ksmbd_preauth_session_id_match(sess, id))378			return sess;379	}380	return NULL;381}382 383static int __init_smb2_session(struct ksmbd_session *sess)384{385	int id = ksmbd_acquire_smb2_uid(&session_ida);386 387	if (id < 0)388		return -EINVAL;389	sess->id = id;390	return 0;391}392 393static struct ksmbd_session *__session_create(int protocol)394{395	struct ksmbd_session *sess;396	int ret;397 398	if (protocol != CIFDS_SESSION_FLAG_SMB2)399		return NULL;400 401	sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL);402	if (!sess)403		return NULL;404 405	if (ksmbd_init_file_table(&sess->file_table))406		goto error;407 408	sess->last_active = jiffies;409	sess->state = SMB2_SESSION_IN_PROGRESS;410	set_session_flag(sess, protocol);411	xa_init(&sess->tree_conns);412	xa_init(&sess->ksmbd_chann_list);413	xa_init(&sess->rpc_handle_list);414	sess->sequence_number = 1;415	rwlock_init(&sess->tree_conns_lock);416	atomic_set(&sess->refcnt, 1);417 418	ret = __init_smb2_session(sess);419	if (ret)420		goto error;421 422	ida_init(&sess->tree_conn_ida);423 424	down_write(&sessions_table_lock);425	hash_add(sessions_table, &sess->hlist, sess->id);426	up_write(&sessions_table_lock);427 428	return sess;429 430error:431	ksmbd_session_destroy(sess);432	return NULL;433}434 435struct ksmbd_session *ksmbd_smb2_session_create(void)436{437	return __session_create(CIFDS_SESSION_FLAG_SMB2);438}439 440int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)441{442	int id = -EINVAL;443 444	if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))445		id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida);446 447	return id;448}449 450void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)451{452	if (id >= 0)453		ksmbd_release_id(&sess->tree_conn_ida, id);454}455