linux/fs/ksmbd/mgmt/user_session.c
<<
>>
Prefs
   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}
 370