169783Smsmith// SPDX-License-Identifier: GPL-2.0-or-later
269783Smsmith/*
369783Smsmith *   Copyright (C) 2018 Samsung Electronics Co., Ltd.
469783Smsmith */
569783Smsmith
669783Smsmith#include <linux/jhash.h>
769783Smsmith#include <linux/slab.h>
869783Smsmith#include <linux/rwsem.h>
969783Smsmith#include <linux/mutex.h>
1069783Smsmith#include <linux/wait.h>
1169783Smsmith#include <linux/hashtable.h>
1269783Smsmith#include <net/net_namespace.h>
1369783Smsmith#include <net/genetlink.h>
1469783Smsmith#include <linux/socket.h>
1569783Smsmith#include <linux/workqueue.h>
1669783Smsmith
1769783Smsmith#include "vfs_cache.h"
1869783Smsmith#include "transport_ipc.h"
1969783Smsmith#include "server.h"
2069783Smsmith#include "smb_common.h"
2169783Smsmith
2269783Smsmith#include "mgmt/user_config.h"
2369783Smsmith#include "mgmt/share_config.h"
2469783Smsmith#include "mgmt/user_session.h"
2569783Smsmith#include "mgmt/tree_connect.h"
2669783Smsmith#include "mgmt/ksmbd_ida.h"
2769783Smsmith#include "connection.h"
2869783Smsmith#include "transport_tcp.h"
2969783Smsmith#include "transport_rdma.h"
3069783Smsmith
31119418Sobrien#define IPC_WAIT_TIMEOUT	(2 * HZ)
32119418Sobrien
33119418Sobrien#define IPC_MSG_HASH_BITS	3
3469783Smsmithstatic DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS);
3569783Smsmithstatic DECLARE_RWSEM(ipc_msg_table_lock);
3669783Smsmithstatic DEFINE_MUTEX(startup_lock);
3769783Smsmith
3869783Smsmithstatic DEFINE_IDA(ipc_ida);
3969783Smsmith
4069783Smsmithstatic unsigned int ksmbd_tools_pid;
41129876Sphk
4269783Smsmithstatic bool ksmbd_ipc_validate_version(struct genl_info *m)
43107546Simp{
44107546Simp	if (m->genlhdr->version != KSMBD_GENL_VERSION) {
45106844Smdodd		pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n",
4669783Smsmith		       "Daemon and kernel module version mismatch",
4769783Smsmith		       m->genlhdr->version,
4869783Smsmith		       KSMBD_GENL_VERSION,
49119285Simp		       "User-space ksmbd should terminate");
50119285Simp		return false;
51119285Simp	}
5269783Smsmith	return true;
5369783Smsmith}
5469783Smsmith
55200341Sjkimstruct ksmbd_ipc_msg {
56200341Sjkim	unsigned int		type;
57200341Sjkim	unsigned int		sz;
58200341Sjkim	unsigned char		payload[];
59200341Sjkim};
60200341Sjkim
61200341Sjkimstruct ipc_msg_table_entry {
62200341Sjkim	unsigned int		handle;
63200341Sjkim	unsigned int		type;
6469783Smsmith	wait_queue_head_t	wait;
65200341Sjkim	struct hlist_node	ipc_table_hlist;
66200341Sjkim
6769783Smsmith	void			*response;
6869783Smsmith	unsigned int		msg_sz;
6969783Smsmith};
7069783Smsmith
7169783Smsmithstatic struct delayed_work ipc_timer_work;
72145661Simp
7369783Smsmithstatic int handle_startup_event(struct sk_buff *skb, struct genl_info *info);
74200341Sjkimstatic int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info);
75200341Sjkimstatic int handle_generic_event(struct sk_buff *skb, struct genl_info *info);
7669783Smsmithstatic int ksmbd_ipc_heartbeat_request(void);
7769783Smsmith
7869783Smsmithstatic const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = {
7969783Smsmith	[KSMBD_EVENT_UNSPEC] = {
8069783Smsmith		.len = 0,
8169783Smsmith	},
8269783Smsmith	[KSMBD_EVENT_HEARTBEAT_REQUEST] = {
8369783Smsmith		.len = sizeof(struct ksmbd_heartbeat),
8469783Smsmith	},
8569783Smsmith	[KSMBD_EVENT_STARTING_UP] = {
8669783Smsmith		.len = sizeof(struct ksmbd_startup_request),
8769783Smsmith	},
8869783Smsmith	[KSMBD_EVENT_SHUTTING_DOWN] = {
8969783Smsmith		.len = sizeof(struct ksmbd_shutdown_request),
9069783Smsmith	},
9169783Smsmith	[KSMBD_EVENT_LOGIN_REQUEST] = {
9269783Smsmith		.len = sizeof(struct ksmbd_login_request),
93164264Sjhb	},
94164264Sjhb	[KSMBD_EVENT_LOGIN_RESPONSE] = {
95164264Sjhb		.len = sizeof(struct ksmbd_login_response),
96164264Sjhb	},
97169221Sjhb	[KSMBD_EVENT_SHARE_CONFIG_REQUEST] = {
9869783Smsmith		.len = sizeof(struct ksmbd_share_config_request),
9969783Smsmith	},
10069783Smsmith	[KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = {
10169783Smsmith		.len = sizeof(struct ksmbd_share_config_response),
102154079Sjhb	},
10369783Smsmith	[KSMBD_EVENT_TREE_CONNECT_REQUEST] = {
104154079Sjhb		.len = sizeof(struct ksmbd_tree_connect_request),
10569783Smsmith	},
10669783Smsmith	[KSMBD_EVENT_TREE_CONNECT_RESPONSE] = {
10769783Smsmith		.len = sizeof(struct ksmbd_tree_connect_response),
108163805Simp	},
109163805Simp	[KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = {
110163805Simp		.len = sizeof(struct ksmbd_tree_disconnect_request),
111163805Simp	},
112163805Simp	[KSMBD_EVENT_LOGOUT_REQUEST] = {
113163805Simp		.len = sizeof(struct ksmbd_logout_request),
114163805Simp	},
115163805Simp	[KSMBD_EVENT_RPC_REQUEST] = {
116163805Simp	},
117163805Simp	[KSMBD_EVENT_RPC_RESPONSE] = {
118163805Simp	},
119163805Simp	[KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = {
120163805Simp	},
121163805Simp	[KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = {
122163805Simp	},
123163805Simp};
124163805Simp
125163805Simpstatic struct genl_ops ksmbd_genl_ops[] = {
126163805Simp	{
127163805Simp		.cmd	= KSMBD_EVENT_UNSPEC,
128163805Simp		.doit	= handle_unsupported_event,
129163805Simp	},
130163805Simp	{
131163805Simp		.cmd	= KSMBD_EVENT_HEARTBEAT_REQUEST,
132163805Simp		.doit	= handle_unsupported_event,
133163805Simp	},
134163805Simp	{
135200341Sjkim		.cmd	= KSMBD_EVENT_STARTING_UP,
136200341Sjkim		.doit	= handle_startup_event,
137200341Sjkim	},
138200341Sjkim	{
139200341Sjkim		.cmd	= KSMBD_EVENT_SHUTTING_DOWN,
140200341Sjkim		.doit	= handle_unsupported_event,
141200341Sjkim	},
142200341Sjkim	{
143200341Sjkim		.cmd	= KSMBD_EVENT_LOGIN_REQUEST,
144200341Sjkim		.doit	= handle_unsupported_event,
145200341Sjkim	},
146200341Sjkim	{
147200341Sjkim		.cmd	= KSMBD_EVENT_LOGIN_RESPONSE,
148200341Sjkim		.doit	= handle_generic_event,
149200341Sjkim	},
150200341Sjkim	{
151200341Sjkim		.cmd	= KSMBD_EVENT_SHARE_CONFIG_REQUEST,
152200341Sjkim		.doit	= handle_unsupported_event,
153200341Sjkim	},
154200341Sjkim	{
155200341Sjkim		.cmd	= KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
156200341Sjkim		.doit	= handle_generic_event,
157200341Sjkim	},
158200341Sjkim	{
159200341Sjkim		.cmd	= KSMBD_EVENT_TREE_CONNECT_REQUEST,
160200341Sjkim		.doit	= handle_unsupported_event,
161200341Sjkim	},
162200341Sjkim	{
163200341Sjkim		.cmd	= KSMBD_EVENT_TREE_CONNECT_RESPONSE,
164200341Sjkim		.doit	= handle_generic_event,
165200341Sjkim	},
166200341Sjkim	{
167200341Sjkim		.cmd	= KSMBD_EVENT_TREE_DISCONNECT_REQUEST,
168200341Sjkim		.doit	= handle_unsupported_event,
169200341Sjkim	},
170200341Sjkim	{
171200341Sjkim		.cmd	= KSMBD_EVENT_LOGOUT_REQUEST,
172200341Sjkim		.doit	= handle_unsupported_event,
173200341Sjkim	},
174200341Sjkim	{
175200341Sjkim		.cmd	= KSMBD_EVENT_RPC_REQUEST,
176200341Sjkim		.doit	= handle_unsupported_event,
177200341Sjkim	},
178200341Sjkim	{
179200341Sjkim		.cmd	= KSMBD_EVENT_RPC_RESPONSE,
180200341Sjkim		.doit	= handle_generic_event,
181200341Sjkim	},
182200341Sjkim	{
183200341Sjkim		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
184200341Sjkim		.doit	= handle_unsupported_event,
185200341Sjkim	},
186200341Sjkim	{
187200341Sjkim		.cmd	= KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE,
188200341Sjkim		.doit	= handle_generic_event,
189200341Sjkim	},
190200341Sjkim};
191200341Sjkim
192200341Sjkimstatic struct genl_family ksmbd_genl_family = {
193200341Sjkim	.name		= KSMBD_GENL_NAME,
194200341Sjkim	.version	= KSMBD_GENL_VERSION,
195200341Sjkim	.hdrsize	= 0,
196200341Sjkim	.maxattr	= KSMBD_EVENT_MAX,
197200341Sjkim	.netnsok	= true,
198200341Sjkim	.module		= THIS_MODULE,
199200341Sjkim	.ops		= ksmbd_genl_ops,
200200341Sjkim	.n_ops		= ARRAY_SIZE(ksmbd_genl_ops),
201200341Sjkim	.resv_start_op	= KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE + 1,
202200341Sjkim};
203200341Sjkim
204200341Sjkimstatic void ksmbd_nl_init_fixup(void)
205200341Sjkim{
206200341Sjkim	int i;
207200341Sjkim
208200341Sjkim	for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++)
209200341Sjkim		ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT |
210200341Sjkim						GENL_DONT_VALIDATE_DUMP;
211200341Sjkim
212200341Sjkim	ksmbd_genl_family.policy = ksmbd_nl_policy;
213200341Sjkim}
214200341Sjkim
215200341Sjkimstatic int rpc_context_flags(struct ksmbd_session *sess)
216200341Sjkim{
217200341Sjkim	if (user_guest(sess->user))
218200341Sjkim		return KSMBD_RPC_RESTRICTED_CONTEXT;
219200341Sjkim	return 0;
220200341Sjkim}
221200341Sjkim
222200341Sjkimstatic void ipc_update_last_active(void)
223200341Sjkim{
224200341Sjkim	if (server_conf.ipc_timeout)
225200341Sjkim		server_conf.ipc_last_active = jiffies;
226200341Sjkim}
227200341Sjkim
228200341Sjkimstatic struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
229200341Sjkim{
230200341Sjkim	struct ksmbd_ipc_msg *msg;
231200341Sjkim	size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
232200341Sjkim
233200341Sjkim	msg = kvzalloc(msg_sz, GFP_KERNEL);
234200341Sjkim	if (msg)
235200341Sjkim		msg->sz = sz;
236200341Sjkim	return msg;
237200341Sjkim}
238200341Sjkim
239200341Sjkimstatic void ipc_msg_free(struct ksmbd_ipc_msg *msg)
240200341Sjkim{
241200341Sjkim	kvfree(msg);
242200341Sjkim}
243200341Sjkim
244200341Sjkimstatic void ipc_msg_handle_free(int handle)
245200341Sjkim{
246200341Sjkim	if (handle >= 0)
247200341Sjkim		ksmbd_release_id(&ipc_ida, handle);
248200341Sjkim}
249200341Sjkim
250200341Sjkimstatic int handle_response(int type, void *payload, size_t sz)
251200341Sjkim{
252200341Sjkim	unsigned int handle = *(unsigned int *)payload;
253200341Sjkim	struct ipc_msg_table_entry *entry;
254200341Sjkim	int ret = 0;
255200341Sjkim
256200341Sjkim	ipc_update_last_active();
257200341Sjkim	down_read(&ipc_msg_table_lock);
258200341Sjkim	hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) {
259200341Sjkim		if (handle != entry->handle)
260200341Sjkim			continue;
261200341Sjkim
262200341Sjkim		entry->response = NULL;
263200341Sjkim		/*
264200341Sjkim		 * Response message type value should be equal to
265200341Sjkim		 * request message type + 1.
266200341Sjkim		 */
267200341Sjkim		if (entry->type + 1 != type) {
268200341Sjkim			pr_err("Waiting for IPC type %d, got %d. Ignore.\n",
269200341Sjkim			       entry->type + 1, type);
270200341Sjkim		}
271200341Sjkim
272200341Sjkim		entry->response = kvzalloc(sz, GFP_KERNEL);
273200341Sjkim		if (!entry->response) {
274200341Sjkim			ret = -ENOMEM;
275200341Sjkim			break;
276200341Sjkim		}
277200341Sjkim
278200341Sjkim		memcpy(entry->response, payload, sz);
279200341Sjkim		entry->msg_sz = sz;
280200341Sjkim		wake_up_interruptible(&entry->wait);
281200341Sjkim		ret = 0;
282200341Sjkim		break;
28369783Smsmith	}
28469783Smsmith	up_read(&ipc_msg_table_lock);
28569783Smsmith
28669783Smsmith	return ret;
28769783Smsmith}
28869783Smsmith
28969783Smsmithstatic int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
29069783Smsmith{
29169783Smsmith	int ret;
29269783Smsmith
29369783Smsmith	ksmbd_set_fd_limit(req->file_max);
29469783Smsmith	server_conf.flags = req->flags;
29569783Smsmith	server_conf.signing = req->signing;
296102441Sjhb	server_conf.tcp_port = req->tcp_port;
297102441Sjhb	server_conf.ipc_timeout = req->ipc_timeout * HZ;
29869783Smsmith	server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL;
29969783Smsmith	server_conf.share_fake_fscaps = req->share_fake_fscaps;
300181789Simp	ksmbd_init_domain(req->sub_auth);
301181789Simp
30269783Smsmith	if (req->smb2_max_read)
30369783Smsmith		init_smb2_max_read_size(req->smb2_max_read);
30469783Smsmith	if (req->smb2_max_write)
30569783Smsmith		init_smb2_max_write_size(req->smb2_max_write);
30669908Smsmith	if (req->smb2_max_trans)
30769908Smsmith		init_smb2_max_trans_size(req->smb2_max_trans);
30869908Smsmith	if (req->smb2_max_credits)
309200341Sjkim		init_smb2_max_credits(req->smb2_max_credits);
310200341Sjkim	if (req->smbd_max_io_size)
311200341Sjkim		init_smbd_max_io_size(req->smbd_max_io_size);
31269783Smsmith
31369908Smsmith	if (req->max_connections)
314181789Simp		server_conf.max_connections = req->max_connections;
315181789Simp
316181789Simp	ret = ksmbd_set_netbios_name(req->netbios_name);
317181789Simp	ret |= ksmbd_set_server_string(req->server_string);
318181789Simp	ret |= ksmbd_set_work_group(req->work_group);
319182706Simp	ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
320181789Simp					req->ifc_list_sz);
321182706Simp	if (ret) {
322181789Simp		pr_err("Server configuration error: %s %s %s\n",
323182706Simp		       req->netbios_name, req->server_string,
324181789Simp		       req->work_group);
325182706Simp		return ret;
326181789Simp	}
327181789Simp
32869908Smsmith	if (req->min_prot[0]) {
32969908Smsmith		ret = ksmbd_lookup_protocol_idx(req->min_prot);
33069908Smsmith		if (ret >= 0)
331124365Simp			server_conf.min_protocol = ret;
33269908Smsmith	}
333119266Simp	if (req->max_prot[0]) {
33469908Smsmith		ret = ksmbd_lookup_protocol_idx(req->max_prot);
33569908Smsmith		if (ret >= 0)
33669908Smsmith			server_conf.max_protocol = ret;
33769908Smsmith	}
33869908Smsmith
33969908Smsmith	if (server_conf.ipc_timeout)
340124365Simp		schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout);
34169908Smsmith	return 0;
342124365Simp}
343124365Simp
344124365Simpstatic int handle_startup_event(struct sk_buff *skb, struct genl_info *info)
345124365Simp{
346124365Simp	int ret = 0;
347124365Simp
348124365Simp#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
349124365Simp	if (!netlink_capable(skb, CAP_NET_ADMIN))
350124365Simp		return -EPERM;
351124365Simp#endif
352124365Simp
35369908Smsmith	if (!ksmbd_ipc_validate_version(info))
354149521Sjkim		return -EINVAL;
355149521Sjkim
356149521Sjkim	if (!info->attrs[KSMBD_EVENT_STARTING_UP])
357149521Sjkim		return -EINVAL;
358149521Sjkim
359149521Sjkim	mutex_lock(&startup_lock);
360157949Sjkim	if (!ksmbd_server_configurable()) {
361149521Sjkim		mutex_unlock(&startup_lock);
362157949Sjkim		pr_err("Server reset is in progress, can't start daemon\n");
363157949Sjkim		return -EINVAL;
364149521Sjkim	}
365157949Sjkim
366157949Sjkim	if (ksmbd_tools_pid) {
367157949Sjkim		if (ksmbd_ipc_heartbeat_request() == 0) {
368157949Sjkim			ret = -EINVAL;
369157949Sjkim			goto out;
370157949Sjkim		}
371157949Sjkim
372157949Sjkim		pr_err("Reconnect to a new user space daemon\n");
373157949Sjkim	} else {
374149521Sjkim		struct ksmbd_startup_request *req;
375149521Sjkim
376149521Sjkim		req = nla_data(info->attrs[info->genlhdr->cmd]);
377149521Sjkim		ret = ipc_server_config_on_startup(req);
378149521Sjkim		if (ret)
379149521Sjkim			goto out;
38069908Smsmith		server_queue_ctrl_init_work();
38169908Smsmith	}
382165995Sjhb
383165995Sjhb	ksmbd_tools_pid = info->snd_portid;
384165995Sjhb	ipc_update_last_active();
385124365Simp
386124365Simpout:
387124365Simp	mutex_unlock(&startup_lock);
388124365Simp	return ret;
389124365Simp}
390124365Simp
391124365Simpstatic int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info)
392124365Simp{
393124365Simp	pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd);
394168157Sjhb	return -EINVAL;
395124365Simp}
396124365Simp
39769783Smsmithstatic int handle_generic_event(struct sk_buff *skb, struct genl_info *info)
398172394Smarius{
39969783Smsmith	void *payload;
40069783Smsmith	int sz;
40169783Smsmith	int type = info->genlhdr->cmd;
402163805Simp
403163805Simp#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
404163805Simp	if (!netlink_capable(skb, CAP_NET_ADMIN))
405163805Simp		return -EPERM;
406163805Simp#endif
407163805Simp
408163805Simp	if (type > KSMBD_EVENT_MAX) {
409163805Simp		WARN_ON(1);
410124365Simp		return -EINVAL;
411124365Simp	}
41269783Smsmith
41369783Smsmith	if (!ksmbd_ipc_validate_version(info))
41469783Smsmith		return -EINVAL;
41569783Smsmith
416181798Simp	if (!info->attrs[type])
417181798Simp		return -EINVAL;
418181798Simp
419181798Simp	payload = nla_data(info->attrs[info->genlhdr->cmd]);
420181798Simp	sz = nla_len(info->attrs[info->genlhdr->cmd]);
421181798Simp	return handle_response(type, payload, sz);
42269783Smsmith}
42369783Smsmith
424181798Simpstatic int ipc_msg_send(struct ksmbd_ipc_msg *msg)
425181798Simp{
42669783Smsmith	struct genlmsghdr *nlh;
427102441Sjhb	struct sk_buff *skb;
42869783Smsmith	int ret = -EINVAL;
429103042Sjhb
430102441Sjhb	if (!ksmbd_tools_pid)
431102441Sjhb		return ret;
432102441Sjhb
433102441Sjhb	skb = genlmsg_new(msg->sz, GFP_KERNEL);
434102441Sjhb	if (!skb)
435102441Sjhb		return -ENOMEM;
436102441Sjhb
43769783Smsmith	nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type);
438103016Sjhb	if (!nlh)
43969783Smsmith		goto out;
44069783Smsmith
441181798Simp	ret = nla_put(skb, msg->type, msg->sz, msg->payload);
44269783Smsmith	if (ret) {
44369783Smsmith		genlmsg_cancel(skb, nlh);
44469783Smsmith		goto out;
44569783Smsmith	}
44669783Smsmith
447102441Sjhb	genlmsg_end(skb, nlh);
448200341Sjkim	ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid);
449200341Sjkim	if (!ret)
450200341Sjkim		ipc_update_last_active();
451200341Sjkim	return ret;
452200341Sjkim
453200341Sjkimout:
454200341Sjkim	nlmsg_free(skb);
455200341Sjkim	return ret;
456200341Sjkim}
457200341Sjkim
458200341Sjkimstatic int ipc_validate_msg(struct ipc_msg_table_entry *entry)
459200341Sjkim{
460200341Sjkim	unsigned int msg_sz = entry->msg_sz;
461200341Sjkim
462200341Sjkim	if (entry->type == KSMBD_EVENT_RPC_REQUEST) {
463200341Sjkim		struct ksmbd_rpc_command *resp = entry->response;
464200341Sjkim
465200341Sjkim		msg_sz = sizeof(struct ksmbd_rpc_command) + resp->payload_sz;
466200341Sjkim	} else if (entry->type == KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST) {
467200341Sjkim		struct ksmbd_spnego_authen_response *resp = entry->response;
468200341Sjkim
469200341Sjkim		msg_sz = sizeof(struct ksmbd_spnego_authen_response) +
470200341Sjkim				resp->session_key_len + resp->spnego_blob_len;
471200341Sjkim	} else if (entry->type == KSMBD_EVENT_SHARE_CONFIG_REQUEST) {
472200341Sjkim		struct ksmbd_share_config_response *resp = entry->response;
473200341Sjkim
474200341Sjkim		if (resp->payload_sz) {
475200341Sjkim			if (resp->payload_sz < resp->veto_list_sz)
476200341Sjkim				return -EINVAL;
477200341Sjkim
478200341Sjkim			msg_sz = sizeof(struct ksmbd_share_config_response) +
479200341Sjkim					resp->payload_sz;
480200341Sjkim		}
481200341Sjkim	}
482200341Sjkim
48369783Smsmith	return entry->msg_sz != msg_sz ? -EINVAL : 0;
48469783Smsmith}
48569783Smsmith
48669783Smsmithstatic void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle)
48769783Smsmith{
488172394Smarius	struct ipc_msg_table_entry entry;
489172394Smarius	int ret;
490172394Smarius
49169783Smsmith	if ((int)handle < 0)
49269783Smsmith		return NULL;
49369783Smsmith
49469783Smsmith	entry.type = msg->type;
49569783Smsmith	entry.response = NULL;
49669783Smsmith	init_waitqueue_head(&entry.wait);
49769783Smsmith
498102441Sjhb	down_write(&ipc_msg_table_lock);
49969783Smsmith	entry.handle = handle;
50069783Smsmith	hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle);
50169783Smsmith	up_write(&ipc_msg_table_lock);
50269783Smsmith
50369783Smsmith	ret = ipc_msg_send(msg);
504172394Smarius	if (ret)
505172394Smarius		goto out;
50669783Smsmith
50769783Smsmith	ret = wait_event_interruptible_timeout(entry.wait,
508172394Smarius					       entry.response != NULL,
50969783Smsmith					       IPC_WAIT_TIMEOUT);
51069783Smsmith	if (entry.response) {
51169783Smsmith		ret = ipc_validate_msg(&entry);
51269783Smsmith		if (ret) {
51369783Smsmith			kvfree(entry.response);
51469783Smsmith			entry.response = NULL;
51569783Smsmith		}
51669783Smsmith	}
517102441Sjhbout:
51869783Smsmith	down_write(&ipc_msg_table_lock);
519142051Simp	hash_del(&entry.ipc_table_hlist);
52069783Smsmith	up_write(&ipc_msg_table_lock);
521124365Simp	return entry.response;
522164130Sjhb}
523124365Simp
52469783Smsmithstatic int ksmbd_ipc_heartbeat_request(void)
52569783Smsmith{
52669783Smsmith	struct ksmbd_ipc_msg *msg;
52769783Smsmith	int ret;
528164130Sjhb
529164130Sjhb	msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat));
530164130Sjhb	if (!msg)
531164130Sjhb		return -EINVAL;
532164130Sjhb
533164130Sjhb	msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST;
53469783Smsmith	ret = ipc_msg_send(msg);
53569783Smsmith	ipc_msg_free(msg);
536107546Simp	return ret;
537124365Simp}
538124365Simp
539124365Simpstruct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
540145652Smarcel{
541145652Smarcel	struct ksmbd_ipc_msg *msg;
542145652Smarcel	struct ksmbd_login_request *req;
543145652Smarcel	struct ksmbd_login_response *resp;
544145652Smarcel
545145652Smarcel	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
546145652Smarcel		return NULL;
547145652Smarcel
548124365Simp	msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request));
549124365Simp	if (!msg)
550124365Simp		return NULL;
551124365Simp
552124365Simp	msg->type = KSMBD_EVENT_LOGIN_REQUEST;
553124365Simp	req = (struct ksmbd_login_request *)msg->payload;
554142051Simp	req->handle = ksmbd_acquire_id(&ipc_ida);
555142051Simp	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
556124365Simp
557106844Smdodd	resp = ipc_msg_send_request(msg, req->handle);
558124365Simp	ipc_msg_handle_free(req->handle);
559189844Simp	ipc_msg_free(msg);
560189792Simp	return resp;
561189792Simp}
562189792Simp
563189792Simpstruct ksmbd_spnego_authen_response *
564189792Simpksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
565189792Simp{
566189844Simp	struct ksmbd_ipc_msg *msg;
567106844Smdodd	struct ksmbd_spnego_authen_request *req;
568124365Simp	struct ksmbd_spnego_authen_response *resp;
569142051Simp
570142051Simp	msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) +
571124365Simp			blob_len + 1);
572124365Simp	if (!msg)
573124365Simp		return NULL;
574124365Simp
575124365Simp	msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST;
576164130Sjhb	req = (struct ksmbd_spnego_authen_request *)msg->payload;
577124365Simp	req->handle = ksmbd_acquire_id(&ipc_ida);
578164130Sjhb	req->spnego_blob_len = blob_len;
579124365Simp	memcpy(req->spnego_blob, spnego_blob, blob_len);
580124365Simp
581124365Simp	resp = ipc_msg_send_request(msg, req->handle);
582142051Simp	ipc_msg_handle_free(req->handle);
583164130Sjhb	ipc_msg_free(msg);
584164130Sjhb	return resp;
585124365Simp}
58669783Smsmith
58769783Smsmithstruct ksmbd_tree_connect_response *
588107546Simpksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
589107546Simp			       struct ksmbd_share_config *share,
590124365Simp			       struct ksmbd_tree_connect *tree_conn,
591107546Simp			       struct sockaddr *peer_addr)
592124365Simp{
593145652Smarcel	struct ksmbd_ipc_msg *msg;
594145652Smarcel	struct ksmbd_tree_connect_request *req;
595145652Smarcel	struct ksmbd_tree_connect_response *resp;
596145652Smarcel
597145652Smarcel	if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
598145652Smarcel		return NULL;
599145652Smarcel
600145652Smarcel	if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME)
601124365Simp		return NULL;
602124365Simp
603124365Simp	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request));
604124365Simp	if (!msg)
605124365Simp		return NULL;
606124365Simp
607124365Simp	msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST;
608124365Simp	req = (struct ksmbd_tree_connect_request *)msg->payload;
609124365Simp
610124365Simp	req->handle = ksmbd_acquire_id(&ipc_ida);
611124365Simp	req->account_flags = sess->user->flags;
612124365Simp	req->session_id = sess->id;
613124365Simp	req->connect_id = tree_conn->id;
614124365Simp	strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
615124365Simp	strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME);
616124365Simp	snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr);
617124365Simp
618124365Simp	if (peer_addr->sa_family == AF_INET6)
619124365Simp		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6;
620124365Simp	if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
621124365Simp		req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2;
622124365Simp
623107546Simp	resp = ipc_msg_send_request(msg, req->handle);
624107546Simp	ipc_msg_handle_free(req->handle);
625124365Simp	ipc_msg_free(msg);
626189844Simp	return resp;
627124365Simp}
628189792Simp
629189792Simpint ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
630124365Simp				      unsigned long long connect_id)
631124365Simp{
632189792Simp	struct ksmbd_ipc_msg *msg;
633189792Simp	struct ksmbd_tree_disconnect_request *req;
634124365Simp	int ret;
635189844Simp
636106844Smdodd	msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request));
637124365Simp	if (!msg)
638142051Simp		return -ENOMEM;
639142051Simp
640124365Simp	msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST;
641124365Simp	req = (struct ksmbd_tree_disconnect_request *)msg->payload;
642124365Simp	req->session_id = session_id;
643124365Simp	req->connect_id = connect_id;
644124365Simp
645124365Simp	ret = ipc_msg_send(msg);
646164130Sjhb	ipc_msg_free(msg);
647163805Simp	return ret;
648164130Sjhb}
649163805Simp
650163805Simpint ksmbd_ipc_logout_request(const char *account, int flags)
651124365Simp{
652124365Simp	struct ksmbd_ipc_msg *msg;
653124365Simp	struct ksmbd_logout_request *req;
654164130Sjhb	int ret;
655142051Simp
656164130Sjhb	if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
657124365Simp		return -EINVAL;
65869908Smsmith
65969783Smsmith	msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request));
660124365Simp	if (!msg)
66169783Smsmith		return -ENOMEM;
662124365Simp
663124365Simp	msg->type = KSMBD_EVENT_LOGOUT_REQUEST;
664124365Simp	req = (struct ksmbd_logout_request *)msg->payload;
665142051Simp	req->account_flags = flags;
666142051Simp	strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
66769783Smsmith
66869783Smsmith	ret = ipc_msg_send(msg);
66969783Smsmith	ipc_msg_free(msg);
67069783Smsmith	return ret;
67169783Smsmith}
672102441Sjhb
67369783Smsmithstruct ksmbd_share_config_response *
67469783Smsmithksmbd_ipc_share_config_request(const char *name)
67569908Smsmith{
67669783Smsmith	struct ksmbd_ipc_msg *msg;
67769783Smsmith	struct ksmbd_share_config_request *req;
67869783Smsmith	struct ksmbd_share_config_response *resp;
67969783Smsmith
68069783Smsmith	if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME)
681119266Simp		return NULL;
682189792Simp
68369783Smsmith	msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request));
68469783Smsmith	if (!msg)
68569783Smsmith		return NULL;
68669783Smsmith
687102441Sjhb	msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST;
688189792Simp	req = (struct ksmbd_share_config_request *)msg->payload;
68969783Smsmith	req->handle = ksmbd_acquire_id(&ipc_ida);
69069783Smsmith	strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME);
69169783Smsmith
69269783Smsmith	resp = ipc_msg_send_request(msg, req->handle);
69369783Smsmith	ipc_msg_handle_free(req->handle);
69469783Smsmith	ipc_msg_free(msg);
69569783Smsmith	return resp;
696109229Sbenno}
69769783Smsmith
69869783Smsmithstruct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle)
69969783Smsmith{
70069783Smsmith	struct ksmbd_ipc_msg *msg;
70169783Smsmith	struct ksmbd_rpc_command *req;
70269783Smsmith	struct ksmbd_rpc_command *resp;
70369783Smsmith
70469783Smsmith	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
70569783Smsmith	if (!msg)
70669783Smsmith		return NULL;
70769783Smsmith
70869783Smsmith	msg->type = KSMBD_EVENT_RPC_REQUEST;
70969783Smsmith	req = (struct ksmbd_rpc_command *)msg->payload;
71069783Smsmith	req->handle = handle;
71169783Smsmith	req->flags = ksmbd_session_rpc_method(sess, handle);
71269783Smsmith	req->flags |= KSMBD_RPC_OPEN_METHOD;
71369783Smsmith	req->payload_sz = 0;
714115234Sticso
71569783Smsmith	resp = ipc_msg_send_request(msg, req->handle);
71669783Smsmith	ipc_msg_free(msg);
71769783Smsmith	return resp;
71869783Smsmith}
71969783Smsmith
72069783Smsmithstruct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle)
72169783Smsmith{
722131398Sjhb	struct ksmbd_ipc_msg *msg;
723102977Sjhb	struct ksmbd_rpc_command *req;
724102977Sjhb	struct ksmbd_rpc_command *resp;
72590554Smsmith
72669783Smsmith	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
72769783Smsmith	if (!msg)
728107172Sjhb		return NULL;
729169221Sjhb
730164264Sjhb	msg->type = KSMBD_EVENT_RPC_REQUEST;
731164264Sjhb	req = (struct ksmbd_rpc_command *)msg->payload;
732164264Sjhb	req->handle = handle;
733169902Sgallatin	req->flags = ksmbd_session_rpc_method(sess, handle);
734164264Sjhb	req->flags |= KSMBD_RPC_CLOSE_METHOD;
735164264Sjhb	req->payload_sz = 0;
736165995Sjhb
737165995Sjhb	resp = ipc_msg_send_request(msg, req->handle);
738164264Sjhb	ipc_msg_free(msg);
739164264Sjhb	return resp;
740164264Sjhb}
741164264Sjhb
742164264Sjhbstruct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle,
743169221Sjhb					  void *payload, size_t payload_sz)
744164264Sjhb{
745164264Sjhb	struct ksmbd_ipc_msg *msg;
746164264Sjhb	struct ksmbd_rpc_command *req;
747164264Sjhb	struct ksmbd_rpc_command *resp;
748164264Sjhb
749164264Sjhb	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
750164264Sjhb	if (!msg)
751164264Sjhb		return NULL;
752164264Sjhb
753164264Sjhb	msg->type = KSMBD_EVENT_RPC_REQUEST;
754164264Sjhb	req = (struct ksmbd_rpc_command *)msg->payload;
755169221Sjhb	req->handle = handle;
756164264Sjhb	req->flags = ksmbd_session_rpc_method(sess, handle);
757169902Sgallatin	req->flags |= rpc_context_flags(sess);
758164264Sjhb	req->flags |= KSMBD_RPC_WRITE_METHOD;
759164264Sjhb	req->payload_sz = payload_sz;
760165995Sjhb	memcpy(req->payload, payload, payload_sz);
761165995Sjhb
762164264Sjhb	resp = ipc_msg_send_request(msg, req->handle);
763169221Sjhb	ipc_msg_free(msg);
764164264Sjhb	return resp;
765164264Sjhb}
766169221Sjhb
767166176Sjhbstruct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle)
768169221Sjhb{
769166176Sjhb	struct ksmbd_ipc_msg *msg;
770166176Sjhb	struct ksmbd_rpc_command *req;
771166176Sjhb	struct ksmbd_rpc_command *resp;
772166176Sjhb
773169221Sjhb	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
774166176Sjhb	if (!msg)
775166176Sjhb		return NULL;
776169221Sjhb
777164264Sjhb	msg->type = KSMBD_EVENT_RPC_REQUEST;
778169221Sjhb	req = (struct ksmbd_rpc_command *)msg->payload;
779169221Sjhb	req->handle = handle;
780164264Sjhb	req->flags = ksmbd_session_rpc_method(sess, handle);
781164264Sjhb	req->flags |= rpc_context_flags(sess);
782180753Sluoqi	req->flags |= KSMBD_RPC_READ_METHOD;
783164264Sjhb	req->payload_sz = 0;
784164264Sjhb
785180753Sluoqi	resp = ipc_msg_send_request(msg, req->handle);
786180753Sluoqi	ipc_msg_free(msg);
787180753Sluoqi	return resp;
788180753Sluoqi}
789180753Sluoqi
790180753Sluoqistruct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle,
791164264Sjhb					  void *payload, size_t payload_sz)
792164264Sjhb{
793107172Sjhb	struct ksmbd_ipc_msg *msg;
794107172Sjhb	struct ksmbd_rpc_command *req;
795107172Sjhb	struct ksmbd_rpc_command *resp;
796107172Sjhb
797107172Sjhb	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
798107172Sjhb	if (!msg)
799119266Simp		return NULL;
800107172Sjhb
801119266Simp	msg->type = KSMBD_EVENT_RPC_REQUEST;
802107172Sjhb	req = (struct ksmbd_rpc_command *)msg->payload;
803107172Sjhb	req->handle = handle;
804107248Sjhb	req->flags = ksmbd_session_rpc_method(sess, handle);
805107172Sjhb	req->flags |= rpc_context_flags(sess);
806107172Sjhb	req->flags |= KSMBD_RPC_IOCTL_METHOD;
807107172Sjhb	req->payload_sz = payload_sz;
808107172Sjhb	memcpy(req->payload, payload, payload_sz);
809107172Sjhb
810107172Sjhb	resp = ipc_msg_send_request(msg, req->handle);
811107172Sjhb	ipc_msg_free(msg);
812107172Sjhb	return resp;
813107172Sjhb}
814107172Sjhb
815107172Sjhbstruct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
816107172Sjhb					size_t payload_sz)
817107172Sjhb{
818107172Sjhb	struct ksmbd_ipc_msg *msg;
819107172Sjhb	struct ksmbd_rpc_command *req;
820107172Sjhb	struct ksmbd_rpc_command *resp;
821107172Sjhb
822107172Sjhb	msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
823107172Sjhb	if (!msg)
824107172Sjhb		return NULL;
825107172Sjhb
826107172Sjhb	msg->type = KSMBD_EVENT_RPC_REQUEST;
827107172Sjhb	req = (struct ksmbd_rpc_command *)msg->payload;
828107172Sjhb	req->handle = ksmbd_acquire_id(&ipc_ida);
829107172Sjhb	req->flags = rpc_context_flags(sess);
830107172Sjhb	req->flags |= KSMBD_RPC_RAP_METHOD;
831107172Sjhb	req->payload_sz = payload_sz;
832107172Sjhb	memcpy(req->payload, payload, payload_sz);
833107172Sjhb
834107172Sjhb	resp = ipc_msg_send_request(msg, req->handle);
835107248Sjhb	ipc_msg_handle_free(req->handle);
836107172Sjhb	ipc_msg_free(msg);
837107172Sjhb	return resp;
838107172Sjhb}
839107248Sjhb
840107172Sjhbstatic int __ipc_heartbeat(void)
841107172Sjhb{
842107172Sjhb	unsigned long delta;
843107248Sjhb
844107172Sjhb	if (!ksmbd_server_running())
845107172Sjhb		return 0;
846107172Sjhb
847107248Sjhb	if (time_after(jiffies, server_conf.ipc_last_active)) {
848107172Sjhb		delta = (jiffies - server_conf.ipc_last_active);
849107172Sjhb	} else {
850107172Sjhb		ipc_update_last_active();
851107172Sjhb		schedule_delayed_work(&ipc_timer_work,
852107172Sjhb				      server_conf.ipc_timeout);
853107172Sjhb		return 0;
854107172Sjhb	}
855107172Sjhb
856107172Sjhb	if (delta < server_conf.ipc_timeout) {
857107172Sjhb		schedule_delayed_work(&ipc_timer_work,
858107172Sjhb				      server_conf.ipc_timeout - delta);
859107172Sjhb		return 0;
860107172Sjhb	}
861107172Sjhb
862107172Sjhb	if (ksmbd_ipc_heartbeat_request() == 0) {
863107172Sjhb		schedule_delayed_work(&ipc_timer_work,
864107172Sjhb				      server_conf.ipc_timeout);
865107172Sjhb		return 0;
866144110Sjhb	}
867144110Sjhb
868144110Sjhb	mutex_lock(&startup_lock);
869144110Sjhb	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);
870144110Sjhb	server_conf.ipc_last_active = 0;
871107172Sjhb	ksmbd_tools_pid = 0;
872107172Sjhb	pr_err("No IPC daemon response for %lus\n", delta / HZ);
873107172Sjhb	mutex_unlock(&startup_lock);
874107172Sjhb	return -EINVAL;
875107172Sjhb}
876107172Sjhb
877107172Sjhbstatic void ipc_timer_heartbeat(struct work_struct *w)
878{
879	if (__ipc_heartbeat())
880		server_queue_ctrl_reset_work();
881}
882
883int ksmbd_ipc_id_alloc(void)
884{
885	return ksmbd_acquire_id(&ipc_ida);
886}
887
888void ksmbd_rpc_id_free(int handle)
889{
890	ksmbd_release_id(&ipc_ida, handle);
891}
892
893void ksmbd_ipc_release(void)
894{
895	cancel_delayed_work_sync(&ipc_timer_work);
896	genl_unregister_family(&ksmbd_genl_family);
897}
898
899void ksmbd_ipc_soft_reset(void)
900{
901	mutex_lock(&startup_lock);
902	ksmbd_tools_pid = 0;
903	cancel_delayed_work_sync(&ipc_timer_work);
904	mutex_unlock(&startup_lock);
905}
906
907int ksmbd_ipc_init(void)
908{
909	int ret = 0;
910
911	ksmbd_nl_init_fixup();
912	INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat);
913
914	ret = genl_register_family(&ksmbd_genl_family);
915	if (ret) {
916		pr_err("Failed to register KSMBD netlink interface %d\n", ret);
917		cancel_delayed_work_sync(&ipc_timer_work);
918	}
919
920	return ret;
921}
922