File Index
Files and directories
Sign in
FILE

user_session.c

File preview with syntax highlighting.

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