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