user_session.c (8045B)
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 3 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 struct list_head list; 29}; 30 31static void free_channel_list(struct ksmbd_session *sess) 32{ 33 struct channel *chann, *tmp; 34 35 list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list, 36 chann_list) { 37 list_del(&chann->chann_list); 38 kfree(chann); 39 } 40} 41 42static void __session_rpc_close(struct ksmbd_session *sess, 43 struct ksmbd_session_rpc *entry) 44{ 45 struct ksmbd_rpc_command *resp; 46 47 resp = ksmbd_rpc_close(sess, entry->id); 48 if (!resp) 49 pr_err("Unable to close RPC pipe %d\n", entry->id); 50 51 kvfree(resp); 52 ksmbd_rpc_id_free(entry->id); 53 kfree(entry); 54} 55 56static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess) 57{ 58 struct ksmbd_session_rpc *entry; 59 60 while (!list_empty(&sess->rpc_handle_list)) { 61 entry = list_entry(sess->rpc_handle_list.next, 62 struct ksmbd_session_rpc, 63 list); 64 65 list_del(&entry->list); 66 __session_rpc_close(sess, entry); 67 } 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; 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 -EINVAL; 104 105 list_add(&entry->list, &sess->rpc_handle_list); 106 entry->method = method; 107 entry->id = ksmbd_ipc_id_alloc(); 108 if (entry->id < 0) 109 goto error; 110 111 resp = ksmbd_rpc_open(sess, entry->id); 112 if (!resp) 113 goto error; 114 115 kvfree(resp); 116 return entry->id; 117error: 118 list_del(&entry->list); 119 kfree(entry); 120 return -EINVAL; 121} 122 123void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id) 124{ 125 struct ksmbd_session_rpc *entry; 126 127 list_for_each_entry(entry, &sess->rpc_handle_list, list) { 128 if (entry->id == id) { 129 list_del(&entry->list); 130 __session_rpc_close(sess, entry); 131 break; 132 } 133 } 134} 135 136int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id) 137{ 138 struct ksmbd_session_rpc *entry; 139 140 list_for_each_entry(entry, &sess->rpc_handle_list, list) { 141 if (entry->id == id) 142 return entry->method; 143 } 144 return 0; 145} 146 147void ksmbd_session_destroy(struct ksmbd_session *sess) 148{ 149 if (!sess) 150 return; 151 152 if (!atomic_dec_and_test(&sess->refcnt)) 153 return; 154 155 list_del(&sess->sessions_entry); 156 157 down_write(&sessions_table_lock); 158 hash_del(&sess->hlist); 159 up_write(&sessions_table_lock); 160 161 if (sess->user) 162 ksmbd_free_user(sess->user); 163 164 ksmbd_tree_conn_session_logoff(sess); 165 ksmbd_destroy_file_table(&sess->file_table); 166 ksmbd_session_rpc_clear_list(sess); 167 free_channel_list(sess); 168 kfree(sess->Preauth_HashValue); 169 ksmbd_release_id(&session_ida, sess->id); 170 kfree(sess); 171} 172 173static struct ksmbd_session *__session_lookup(unsigned long long id) 174{ 175 struct ksmbd_session *sess; 176 177 hash_for_each_possible(sessions_table, sess, hlist, id) { 178 if (id == sess->id) 179 return sess; 180 } 181 return NULL; 182} 183 184void ksmbd_session_register(struct ksmbd_conn *conn, 185 struct ksmbd_session *sess) 186{ 187 sess->conn = conn; 188 list_add(&sess->sessions_entry, &conn->sessions); 189} 190 191void ksmbd_sessions_deregister(struct ksmbd_conn *conn) 192{ 193 struct ksmbd_session *sess; 194 195 while (!list_empty(&conn->sessions)) { 196 sess = list_entry(conn->sessions.next, 197 struct ksmbd_session, 198 sessions_entry); 199 200 ksmbd_session_destroy(sess); 201 } 202} 203 204static bool ksmbd_session_id_match(struct ksmbd_session *sess, 205 unsigned long long id) 206{ 207 return sess->id == id; 208} 209 210struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn, 211 unsigned long long id) 212{ 213 struct ksmbd_session *sess = NULL; 214 215 list_for_each_entry(sess, &conn->sessions, sessions_entry) { 216 if (ksmbd_session_id_match(sess, id)) 217 return sess; 218 } 219 return NULL; 220} 221 222int get_session(struct ksmbd_session *sess) 223{ 224 return atomic_inc_not_zero(&sess->refcnt); 225} 226 227void put_session(struct ksmbd_session *sess) 228{ 229 if (atomic_dec_and_test(&sess->refcnt)) 230 pr_err("get/%s seems to be mismatched.", __func__); 231} 232 233struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id) 234{ 235 struct ksmbd_session *sess; 236 237 down_read(&sessions_table_lock); 238 sess = __session_lookup(id); 239 if (sess) { 240 if (!get_session(sess)) 241 sess = NULL; 242 } 243 up_read(&sessions_table_lock); 244 245 return sess; 246} 247 248struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn, 249 unsigned long long id) 250{ 251 struct ksmbd_session *sess; 252 253 sess = ksmbd_session_lookup(conn, id); 254 if (!sess && conn->binding) 255 sess = ksmbd_session_lookup_slowpath(id); 256 return sess; 257} 258 259struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn, 260 u64 sess_id) 261{ 262 struct preauth_session *sess; 263 264 sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL); 265 if (!sess) 266 return NULL; 267 268 sess->id = sess_id; 269 memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue, 270 PREAUTH_HASHVALUE_SIZE); 271 list_add(&sess->preauth_entry, &conn->preauth_sess_table); 272 273 return sess; 274} 275 276static bool ksmbd_preauth_session_id_match(struct preauth_session *sess, 277 unsigned long long id) 278{ 279 return sess->id == id; 280} 281 282struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn, 283 unsigned long long id) 284{ 285 struct preauth_session *sess = NULL; 286 287 list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) { 288 if (ksmbd_preauth_session_id_match(sess, id)) 289 return sess; 290 } 291 return NULL; 292} 293 294static int __init_smb2_session(struct ksmbd_session *sess) 295{ 296 int id = ksmbd_acquire_smb2_uid(&session_ida); 297 298 if (id < 0) 299 return -EINVAL; 300 sess->id = id; 301 return 0; 302} 303 304static struct ksmbd_session *__session_create(int protocol) 305{ 306 struct ksmbd_session *sess; 307 int ret; 308 309 sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL); 310 if (!sess) 311 return NULL; 312 313 if (ksmbd_init_file_table(&sess->file_table)) 314 goto error; 315 316 set_session_flag(sess, protocol); 317 INIT_LIST_HEAD(&sess->sessions_entry); 318 xa_init(&sess->tree_conns); 319 INIT_LIST_HEAD(&sess->ksmbd_chann_list); 320 INIT_LIST_HEAD(&sess->rpc_handle_list); 321 sess->sequence_number = 1; 322 atomic_set(&sess->refcnt, 1); 323 324 switch (protocol) { 325 case CIFDS_SESSION_FLAG_SMB2: 326 ret = __init_smb2_session(sess); 327 break; 328 default: 329 ret = -EINVAL; 330 break; 331 } 332 333 if (ret) 334 goto error; 335 336 ida_init(&sess->tree_conn_ida); 337 338 if (protocol == CIFDS_SESSION_FLAG_SMB2) { 339 down_write(&sessions_table_lock); 340 hash_add(sessions_table, &sess->hlist, sess->id); 341 up_write(&sessions_table_lock); 342 } 343 return sess; 344 345error: 346 ksmbd_session_destroy(sess); 347 return NULL; 348} 349 350struct ksmbd_session *ksmbd_smb2_session_create(void) 351{ 352 return __session_create(CIFDS_SESSION_FLAG_SMB2); 353} 354 355int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess) 356{ 357 int id = -EINVAL; 358 359 if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2)) 360 id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida); 361 362 return id; 363} 364 365void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id) 366{ 367 if (id >= 0) 368 ksmbd_release_id(&sess->tree_conn_ida, id); 369}