]> Git Repo - J-linux.git/blob - fs/smb/server/mgmt/user_session.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / 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
19 static DEFINE_IDA(session_ida);
20
21 #define SESSION_HASH_BITS               3
22 static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);
23 static DECLARE_RWSEM(sessions_table_lock);
24
25 struct ksmbd_session_rpc {
26         int                     id;
27         unsigned int            method;
28 };
29
30 static 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
43 static 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
57 static 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
70 static 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
91 int 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), KSMBD_DEFAULT_GFP);
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, KSMBD_DEFAULT_GFP);
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;
119 erase_xa:
120         xa_erase(&sess->rpc_handle_list, entry->id);
121 free_id:
122         ksmbd_rpc_id_free(entry->id);
123 free_entry:
124         kfree(entry);
125         return -EINVAL;
126 }
127
128 void 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
137 int 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
145 void 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
163 struct 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
176 static 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
198 int 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, KSMBD_DEFAULT_GFP));
205 }
206
207 static 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
219 void 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
259 struct 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                 ksmbd_user_session_get(sess);
269         }
270         up_read(&conn->session_lock);
271         return sess;
272 }
273
274 struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
275 {
276         struct ksmbd_session *sess;
277
278         down_read(&sessions_table_lock);
279         sess = __session_lookup(id);
280         if (sess)
281                 ksmbd_user_session_get(sess);
282         up_read(&sessions_table_lock);
283
284         return sess;
285 }
286
287 struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
288                                                unsigned long long id)
289 {
290         struct ksmbd_session *sess;
291
292         sess = ksmbd_session_lookup(conn, id);
293         if (!sess && conn->binding)
294                 sess = ksmbd_session_lookup_slowpath(id);
295         if (sess && sess->state != SMB2_SESSION_VALID)
296                 sess = NULL;
297         return sess;
298 }
299
300 void ksmbd_user_session_get(struct ksmbd_session *sess)
301 {
302         atomic_inc(&sess->refcnt);
303 }
304
305 void ksmbd_user_session_put(struct ksmbd_session *sess)
306 {
307         if (!sess)
308                 return;
309
310         if (atomic_read(&sess->refcnt) <= 0)
311                 WARN_ON(1);
312         else
313                 atomic_dec(&sess->refcnt);
314 }
315
316 struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
317                                                     u64 sess_id)
318 {
319         struct preauth_session *sess;
320
321         sess = kmalloc(sizeof(struct preauth_session), KSMBD_DEFAULT_GFP);
322         if (!sess)
323                 return NULL;
324
325         sess->id = sess_id;
326         memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue,
327                PREAUTH_HASHVALUE_SIZE);
328         list_add(&sess->preauth_entry, &conn->preauth_sess_table);
329
330         return sess;
331 }
332
333 void destroy_previous_session(struct ksmbd_conn *conn,
334                               struct ksmbd_user *user, u64 id)
335 {
336         struct ksmbd_session *prev_sess;
337         struct ksmbd_user *prev_user;
338         int err;
339
340         down_write(&sessions_table_lock);
341         down_write(&conn->session_lock);
342         prev_sess = __session_lookup(id);
343         if (!prev_sess || prev_sess->state == SMB2_SESSION_EXPIRED)
344                 goto out;
345
346         prev_user = prev_sess->user;
347         if (!prev_user ||
348             strcmp(user->name, prev_user->name) ||
349             user->passkey_sz != prev_user->passkey_sz ||
350             memcmp(user->passkey, prev_user->passkey, user->passkey_sz))
351                 goto out;
352
353         ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_RECONNECT);
354         err = ksmbd_conn_wait_idle_sess_id(conn, id);
355         if (err) {
356                 ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
357                 goto out;
358         }
359
360         ksmbd_destroy_file_table(&prev_sess->file_table);
361         prev_sess->state = SMB2_SESSION_EXPIRED;
362         ksmbd_all_conn_set_status(id, KSMBD_SESS_NEED_NEGOTIATE);
363         ksmbd_launch_ksmbd_durable_scavenger();
364 out:
365         up_write(&conn->session_lock);
366         up_write(&sessions_table_lock);
367 }
368
369 static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
370                                            unsigned long long id)
371 {
372         return sess->id == id;
373 }
374
375 struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
376                                                      unsigned long long id)
377 {
378         struct preauth_session *sess = NULL;
379
380         list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) {
381                 if (ksmbd_preauth_session_id_match(sess, id))
382                         return sess;
383         }
384         return NULL;
385 }
386
387 static int __init_smb2_session(struct ksmbd_session *sess)
388 {
389         int id = ksmbd_acquire_smb2_uid(&session_ida);
390
391         if (id < 0)
392                 return -EINVAL;
393         sess->id = id;
394         return 0;
395 }
396
397 static struct ksmbd_session *__session_create(int protocol)
398 {
399         struct ksmbd_session *sess;
400         int ret;
401
402         if (protocol != CIFDS_SESSION_FLAG_SMB2)
403                 return NULL;
404
405         sess = kzalloc(sizeof(struct ksmbd_session), KSMBD_DEFAULT_GFP);
406         if (!sess)
407                 return NULL;
408
409         if (ksmbd_init_file_table(&sess->file_table))
410                 goto error;
411
412         sess->last_active = jiffies;
413         sess->state = SMB2_SESSION_IN_PROGRESS;
414         set_session_flag(sess, protocol);
415         xa_init(&sess->tree_conns);
416         xa_init(&sess->ksmbd_chann_list);
417         xa_init(&sess->rpc_handle_list);
418         sess->sequence_number = 1;
419         rwlock_init(&sess->tree_conns_lock);
420         atomic_set(&sess->refcnt, 1);
421
422         ret = __init_smb2_session(sess);
423         if (ret)
424                 goto error;
425
426         ida_init(&sess->tree_conn_ida);
427
428         down_write(&sessions_table_lock);
429         hash_add(sessions_table, &sess->hlist, sess->id);
430         up_write(&sessions_table_lock);
431
432         return sess;
433
434 error:
435         ksmbd_session_destroy(sess);
436         return NULL;
437 }
438
439 struct ksmbd_session *ksmbd_smb2_session_create(void)
440 {
441         return __session_create(CIFDS_SESSION_FLAG_SMB2);
442 }
443
444 int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)
445 {
446         int id = -EINVAL;
447
448         if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
449                 id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida);
450
451         return id;
452 }
453
454 void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)
455 {
456         if (id >= 0)
457                 ksmbd_release_id(&sess->tree_conn_ida, id);
458 }
This page took 0.051808 seconds and 4 git commands to generate.