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