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 	struct list_head	list;
29 };
30 
free_channel_list(struct ksmbd_session * sess)31 static void free_channel_list(struct ksmbd_session *sess)
32 {
33 	struct channel *chann, *tmp;
34 
35 	write_lock(&sess->chann_lock);
36 	list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
37 				 chann_list) {
38 		list_del(&chann->chann_list);
39 		kfree(chann);
40 	}
41 	write_unlock(&sess->chann_lock);
42 }
43 
__session_rpc_close(struct ksmbd_session * sess,struct ksmbd_session_rpc * entry)44 static void __session_rpc_close(struct ksmbd_session *sess,
45 				struct ksmbd_session_rpc *entry)
46 {
47 	struct ksmbd_rpc_command *resp;
48 
49 	resp = ksmbd_rpc_close(sess, entry->id);
50 	if (!resp)
51 		pr_err("Unable to close RPC pipe %d\n", entry->id);
52 
53 	kvfree(resp);
54 	ksmbd_rpc_id_free(entry->id);
55 	kfree(entry);
56 }
57 
ksmbd_session_rpc_clear_list(struct ksmbd_session * sess)58 static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
59 {
60 	struct ksmbd_session_rpc *entry;
61 
62 	while (!list_empty(&sess->rpc_handle_list)) {
63 		entry = list_entry(sess->rpc_handle_list.next,
64 				   struct ksmbd_session_rpc,
65 				   list);
66 
67 		list_del(&entry->list);
68 		__session_rpc_close(sess, entry);
69 	}
70 }
71 
__rpc_method(char * rpc_name)72 static int __rpc_method(char *rpc_name)
73 {
74 	if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc"))
75 		return KSMBD_RPC_SRVSVC_METHOD_INVOKE;
76 
77 	if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc"))
78 		return KSMBD_RPC_WKSSVC_METHOD_INVOKE;
79 
80 	if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman"))
81 		return KSMBD_RPC_RAP_METHOD;
82 
83 	if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr"))
84 		return KSMBD_RPC_SAMR_METHOD_INVOKE;
85 
86 	if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc"))
87 		return KSMBD_RPC_LSARPC_METHOD_INVOKE;
88 
89 	pr_err("Unsupported RPC: %s\n", rpc_name);
90 	return 0;
91 }
92 
ksmbd_session_rpc_open(struct ksmbd_session * sess,char * rpc_name)93 int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
94 {
95 	struct ksmbd_session_rpc *entry;
96 	struct ksmbd_rpc_command *resp;
97 	int method;
98 
99 	method = __rpc_method(rpc_name);
100 	if (!method)
101 		return -EINVAL;
102 
103 	entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
104 	if (!entry)
105 		return -EINVAL;
106 
107 	list_add(&entry->list, &sess->rpc_handle_list);
108 	entry->method = method;
109 	entry->id = ksmbd_ipc_id_alloc();
110 	if (entry->id < 0)
111 		goto free_entry;
112 
113 	resp = ksmbd_rpc_open(sess, entry->id);
114 	if (!resp)
115 		goto free_id;
116 
117 	kvfree(resp);
118 	return entry->id;
119 free_id:
120 	ksmbd_rpc_id_free(entry->id);
121 free_entry:
122 	list_del(&entry->list);
123 	kfree(entry);
124 	return -EINVAL;
125 }
126 
ksmbd_session_rpc_close(struct ksmbd_session * sess,int id)127 void 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 
ksmbd_session_rpc_method(struct ksmbd_session * sess,int id)140 int 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 
ksmbd_session_destroy(struct ksmbd_session * sess)151 void ksmbd_session_destroy(struct ksmbd_session *sess)
152 {
153 	if (!sess)
154 		return;
155 
156 	down_write(&sessions_table_lock);
157 	hash_del(&sess->hlist);
158 	up_write(&sessions_table_lock);
159 
160 	if (sess->user)
161 		ksmbd_free_user(sess->user);
162 
163 	ksmbd_tree_conn_session_logoff(sess);
164 	ksmbd_destroy_file_table(&sess->file_table);
165 	ksmbd_session_rpc_clear_list(sess);
166 	free_channel_list(sess);
167 	kfree(sess->Preauth_HashValue);
168 	ksmbd_release_id(&session_ida, sess->id);
169 	kfree(sess);
170 }
171 
__session_lookup(unsigned long long id)172 static struct ksmbd_session *__session_lookup(unsigned long long id)
173 {
174 	struct ksmbd_session *sess;
175 
176 	hash_for_each_possible(sessions_table, sess, hlist, id) {
177 		if (id == sess->id)
178 			return sess;
179 	}
180 	return NULL;
181 }
182 
ksmbd_session_register(struct ksmbd_conn * conn,struct ksmbd_session * sess)183 int ksmbd_session_register(struct ksmbd_conn *conn,
184 			   struct ksmbd_session *sess)
185 {
186 	sess->dialect = conn->dialect;
187 	memcpy(sess->ClientGUID, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE);
188 	return xa_err(xa_store(&conn->sessions, sess->id, sess, GFP_KERNEL));
189 }
190 
ksmbd_chann_del(struct ksmbd_conn * conn,struct ksmbd_session * sess)191 static int ksmbd_chann_del(struct ksmbd_conn *conn, struct ksmbd_session *sess)
192 {
193 	struct channel *chann, *tmp;
194 
195 	write_lock(&sess->chann_lock);
196 	list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
197 				 chann_list) {
198 		if (chann->conn == conn) {
199 			list_del(&chann->chann_list);
200 			kfree(chann);
201 			write_unlock(&sess->chann_lock);
202 			return 0;
203 		}
204 	}
205 	write_unlock(&sess->chann_lock);
206 
207 	return -ENOENT;
208 }
209 
ksmbd_sessions_deregister(struct ksmbd_conn * conn)210 void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
211 {
212 	struct ksmbd_session *sess;
213 
214 	if (conn->binding) {
215 		int bkt;
216 
217 		down_write(&sessions_table_lock);
218 		hash_for_each(sessions_table, bkt, sess, hlist) {
219 			if (!ksmbd_chann_del(conn, sess)) {
220 				up_write(&sessions_table_lock);
221 				goto sess_destroy;
222 			}
223 		}
224 		up_write(&sessions_table_lock);
225 	} else {
226 		unsigned long id;
227 
228 		xa_for_each(&conn->sessions, id, sess) {
229 			if (!ksmbd_chann_del(conn, sess))
230 				goto sess_destroy;
231 		}
232 	}
233 
234 	return;
235 
236 sess_destroy:
237 	if (list_empty(&sess->ksmbd_chann_list)) {
238 		xa_erase(&conn->sessions, sess->id);
239 		ksmbd_session_destroy(sess);
240 	}
241 }
242 
ksmbd_session_lookup(struct ksmbd_conn * conn,unsigned long long id)243 struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
244 					   unsigned long long id)
245 {
246 	return xa_load(&conn->sessions, id);
247 }
248 
ksmbd_session_lookup_slowpath(unsigned long long id)249 struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
250 {
251 	struct ksmbd_session *sess;
252 
253 	down_read(&sessions_table_lock);
254 	sess = __session_lookup(id);
255 	up_read(&sessions_table_lock);
256 
257 	return sess;
258 }
259 
ksmbd_session_lookup_all(struct ksmbd_conn * conn,unsigned long long id)260 struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
261 					       unsigned long long id)
262 {
263 	struct ksmbd_session *sess;
264 
265 	sess = ksmbd_session_lookup(conn, id);
266 	if (!sess && conn->binding)
267 		sess = ksmbd_session_lookup_slowpath(id);
268 	if (sess && sess->state != SMB2_SESSION_VALID)
269 		sess = NULL;
270 	return sess;
271 }
272 
ksmbd_preauth_session_alloc(struct ksmbd_conn * conn,u64 sess_id)273 struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
274 						    u64 sess_id)
275 {
276 	struct preauth_session *sess;
277 
278 	sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL);
279 	if (!sess)
280 		return NULL;
281 
282 	sess->id = sess_id;
283 	memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue,
284 	       PREAUTH_HASHVALUE_SIZE);
285 	list_add(&sess->preauth_entry, &conn->preauth_sess_table);
286 
287 	return sess;
288 }
289 
ksmbd_preauth_session_id_match(struct preauth_session * sess,unsigned long long id)290 static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
291 					   unsigned long long id)
292 {
293 	return sess->id == id;
294 }
295 
ksmbd_preauth_session_lookup(struct ksmbd_conn * conn,unsigned long long id)296 struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
297 						     unsigned long long id)
298 {
299 	struct preauth_session *sess = NULL;
300 
301 	list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) {
302 		if (ksmbd_preauth_session_id_match(sess, id))
303 			return sess;
304 	}
305 	return NULL;
306 }
307 
__init_smb2_session(struct ksmbd_session * sess)308 static int __init_smb2_session(struct ksmbd_session *sess)
309 {
310 	int id = ksmbd_acquire_smb2_uid(&session_ida);
311 
312 	if (id < 0)
313 		return -EINVAL;
314 	sess->id = id;
315 	return 0;
316 }
317 
__session_create(int protocol)318 static struct ksmbd_session *__session_create(int protocol)
319 {
320 	struct ksmbd_session *sess;
321 	int ret;
322 
323 	sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL);
324 	if (!sess)
325 		return NULL;
326 
327 	if (ksmbd_init_file_table(&sess->file_table))
328 		goto error;
329 
330 	set_session_flag(sess, protocol);
331 	xa_init(&sess->tree_conns);
332 	INIT_LIST_HEAD(&sess->ksmbd_chann_list);
333 	INIT_LIST_HEAD(&sess->rpc_handle_list);
334 	sess->sequence_number = 1;
335 	rwlock_init(&sess->chann_lock);
336 
337 	switch (protocol) {
338 	case CIFDS_SESSION_FLAG_SMB2:
339 		ret = __init_smb2_session(sess);
340 		break;
341 	default:
342 		ret = -EINVAL;
343 		break;
344 	}
345 
346 	if (ret)
347 		goto error;
348 
349 	ida_init(&sess->tree_conn_ida);
350 
351 	if (protocol == CIFDS_SESSION_FLAG_SMB2) {
352 		down_write(&sessions_table_lock);
353 		hash_add(sessions_table, &sess->hlist, sess->id);
354 		up_write(&sessions_table_lock);
355 	}
356 	return sess;
357 
358 error:
359 	ksmbd_session_destroy(sess);
360 	return NULL;
361 }
362 
ksmbd_smb2_session_create(void)363 struct ksmbd_session *ksmbd_smb2_session_create(void)
364 {
365 	return __session_create(CIFDS_SESSION_FLAG_SMB2);
366 }
367 
ksmbd_acquire_tree_conn_id(struct ksmbd_session * sess)368 int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)
369 {
370 	int id = -EINVAL;
371 
372 	if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
373 		id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida);
374 
375 	return id;
376 }
377 
ksmbd_release_tree_conn_id(struct ksmbd_session * sess,int id)378 void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)
379 {
380 	if (id >= 0)
381 		ksmbd_release_id(&sess->tree_conn_ida, id);
382 }
383