FILE
user_session.c
File preview with syntax highlighting.
linux-6.19.14/fs/smb/server/mgmt/user_session.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.