| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0-or-later
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org> | 
					
						
							|  |  |  |  *   Copyright (C) 2018 Samsung Electronics Co., Ltd. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "glob.h"
 | 
					
						
							|  |  |  | #include "oplock.h"
 | 
					
						
							|  |  |  | #include "misc.h"
 | 
					
						
							|  |  |  | #include <linux/sched/signal.h>
 | 
					
						
							|  |  |  | #include <linux/workqueue.h>
 | 
					
						
							|  |  |  | #include <linux/sysfs.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/moduleparam.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "server.h"
 | 
					
						
							|  |  |  | #include "smb_common.h"
 | 
					
						
							|  |  |  | #include "smbstatus.h"
 | 
					
						
							|  |  |  | #include "connection.h"
 | 
					
						
							|  |  |  | #include "transport_ipc.h"
 | 
					
						
							|  |  |  | #include "mgmt/user_session.h"
 | 
					
						
							|  |  |  | #include "crypto_ctx.h"
 | 
					
						
							|  |  |  | #include "auth.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ksmbd_debug_types; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct ksmbd_server_config server_conf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum SERVER_CTRL_TYPE { | 
					
						
							|  |  |  | 	SERVER_CTRL_TYPE_INIT, | 
					
						
							|  |  |  | 	SERVER_CTRL_TYPE_RESET, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct server_ctrl_struct { | 
					
						
							|  |  |  | 	int			type; | 
					
						
							|  |  |  | 	struct work_struct	ctrl_work; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static DEFINE_MUTEX(ctrl_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ___server_conf_set(int idx, char *val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (idx >= ARRAY_SIZE(server_conf.conf)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!val || val[0] == 0x00) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kfree(server_conf.conf[idx]); | 
					
						
							|  |  |  | 	server_conf.conf[idx] = kstrdup(val, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!server_conf.conf[idx]) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ksmbd_set_netbios_name(char *v) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ksmbd_set_server_string(char *v) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ___server_conf_set(SERVER_CONF_SERVER_STRING, v); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int ksmbd_set_work_group(char *v) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return ___server_conf_set(SERVER_CONF_WORK_GROUP, v); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *ksmbd_netbios_name(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return server_conf.conf[SERVER_CONF_NETBIOS_NAME]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *ksmbd_server_string(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return server_conf.conf[SERVER_CONF_SERVER_STRING]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *ksmbd_work_group(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return server_conf.conf[SERVER_CONF_WORK_GROUP]; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * check_conn_state() - check state of server thread connection | 
					
						
							| 
									
										
										
										
											2021-03-21 17:05:56 +09:00
										 |  |  |  * @work:     smb work containing server thread information | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Return:	0 on valid connection, otherwise 1 to reconnect | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static inline int check_conn_state(struct ksmbd_work *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct smb_hdr *rsp_hdr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ksmbd_conn_exiting(work) || ksmbd_conn_need_reconnect(work)) { | 
					
						
							| 
									
										
										
										
											2021-03-30 12:35:23 +09:00
										 |  |  | 		rsp_hdr = work->response_buf; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED; | 
					
						
							|  |  |  | 		return 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | #define SERVER_HANDLER_CONTINUE		0
 | 
					
						
							|  |  |  | #define SERVER_HANDLER_ABORT		1
 | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn, | 
					
						
							| 
									
										
										
										
											2021-05-26 18:01:08 +09:00
										 |  |  | 			     u16 *cmd) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct smb_version_cmds *cmds; | 
					
						
							| 
									
										
										
										
											2021-05-26 18:01:08 +09:00
										 |  |  | 	u16 command; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (check_conn_state(work)) | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		return SERVER_HANDLER_CONTINUE; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ksmbd_verify_smb_message(work)) | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		return SERVER_HANDLER_ABORT; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	command = conn->ops->get_cmd_val(work); | 
					
						
							|  |  |  | 	*cmd = command; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | andx_again: | 
					
						
							|  |  |  | 	if (command >= conn->max_cmds) { | 
					
						
							|  |  |  | 		conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER); | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		return SERVER_HANDLER_CONTINUE; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmds = &conn->cmds[command]; | 
					
						
							|  |  |  | 	if (!cmds->proc) { | 
					
						
							|  |  |  | 		ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command); | 
					
						
							|  |  |  | 		conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED); | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		return SERVER_HANDLER_CONTINUE; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (work->sess && conn->ops->is_sign_req(work, command)) { | 
					
						
							|  |  |  | 		ret = conn->ops->check_sign_req(work); | 
					
						
							|  |  |  | 		if (!ret) { | 
					
						
							|  |  |  | 			conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED); | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 			return SERVER_HANDLER_CONTINUE; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = cmds->proc(work); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret); | 
					
						
							|  |  |  | 	/* AndX commands - chained request can return positive values */ | 
					
						
							|  |  |  | 	else if (ret > 0) { | 
					
						
							|  |  |  | 		command = ret; | 
					
						
							|  |  |  | 		*cmd = command; | 
					
						
							|  |  |  | 		goto andx_again; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (work->send_no_response) | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		return SERVER_HANDLER_ABORT; | 
					
						
							|  |  |  | 	return SERVER_HANDLER_CONTINUE; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void __handle_ksmbd_work(struct ksmbd_work *work, | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 				struct ksmbd_conn *conn) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | 	u16 command = 0; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	int rc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (conn->ops->allocate_rsp_buf(work)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (conn->ops->is_transform_hdr && | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | 	    conn->ops->is_transform_hdr(work->request_buf)) { | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		rc = conn->ops->decrypt_req(work); | 
					
						
							|  |  |  | 		if (rc < 0) { | 
					
						
							|  |  |  | 			conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); | 
					
						
							|  |  |  | 			goto send; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		work->encrypted = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rc = conn->ops->init_rsp_hdr(work); | 
					
						
							|  |  |  | 	if (rc) { | 
					
						
							|  |  |  | 		/* either uid or tid is not correct */ | 
					
						
							|  |  |  | 		conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE); | 
					
						
							|  |  |  | 		goto send; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (conn->ops->check_user_session) { | 
					
						
							|  |  |  | 		rc = conn->ops->check_user_session(work); | 
					
						
							|  |  |  | 		if (rc < 0) { | 
					
						
							|  |  |  | 			command = conn->ops->get_cmd_val(work); | 
					
						
							|  |  |  | 			conn->ops->set_rsp_status(work, | 
					
						
							|  |  |  | 					STATUS_USER_SESSION_DELETED); | 
					
						
							|  |  |  | 			goto send; | 
					
						
							|  |  |  | 		} else if (rc > 0) { | 
					
						
							|  |  |  | 			rc = conn->ops->get_ksmbd_tcon(work); | 
					
						
							|  |  |  | 			if (rc < 0) { | 
					
						
							|  |  |  | 				conn->ops->set_rsp_status(work, | 
					
						
							|  |  |  | 					STATUS_NETWORK_NAME_DELETED); | 
					
						
							|  |  |  | 				goto send; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		rc = __process_request(work, conn, &command); | 
					
						
							| 
									
										
										
										
											2021-07-16 14:52:46 +09:00
										 |  |  | 		if (rc == SERVER_HANDLER_ABORT) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/*
 | 
					
						
							|  |  |  | 		 * Call smb2_set_rsp_credits() function to set number of credits | 
					
						
							|  |  |  | 		 * granted in hdr of smb2 response. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (conn->ops->set_rsp_credits) { | 
					
						
							|  |  |  | 			spin_lock(&conn->credits_lock); | 
					
						
							|  |  |  | 			rc = conn->ops->set_rsp_credits(work); | 
					
						
							|  |  |  | 			spin_unlock(&conn->credits_lock); | 
					
						
							|  |  |  | 			if (rc < 0) { | 
					
						
							|  |  |  | 				conn->ops->set_rsp_status(work, | 
					
						
							|  |  |  | 					STATUS_INVALID_PARAMETER); | 
					
						
							|  |  |  | 				goto send; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 		if (work->sess && | 
					
						
							|  |  |  | 		    (work->sess->sign || smb3_11_final_sess_setup_resp(work) || | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		     conn->ops->is_sign_req(work, command))) | 
					
						
							|  |  |  | 			conn->ops->set_sign_rsp(work); | 
					
						
							|  |  |  | 	} while (is_chained_smb2_message(work)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (work->send_no_response) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | send: | 
					
						
							|  |  |  | 	smb3_preauth_hash_rsp(work); | 
					
						
							|  |  |  | 	if (work->sess && work->sess->enc && work->encrypted && | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | 	    conn->ops->encrypt_resp) { | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		rc = conn->ops->encrypt_resp(work); | 
					
						
							|  |  |  | 		if (rc < 0) { | 
					
						
							|  |  |  | 			conn->ops->set_rsp_status(work, STATUS_DATA_ERROR); | 
					
						
							|  |  |  | 			goto send; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ksmbd_conn_write(work); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * handle_ksmbd_work() - process pending smb work requests | 
					
						
							| 
									
										
										
										
											2021-03-21 17:05:56 +09:00
										 |  |  |  * @wk:	smb work containing request command buffer | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  |  * | 
					
						
							|  |  |  |  * called by kworker threads to processing remaining smb work requests | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void handle_ksmbd_work(struct work_struct *wk) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work); | 
					
						
							|  |  |  | 	struct ksmbd_conn *conn = work->conn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	atomic64_inc(&conn->stats.request_served); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__handle_ksmbd_work(work, conn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ksmbd_conn_try_dequeue_request(work); | 
					
						
							|  |  |  | 	ksmbd_free_work_struct(work); | 
					
						
							|  |  |  | 	atomic_dec(&conn->r_count); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * queue_ksmbd_work() - queue a smb request to worker thread queue | 
					
						
							|  |  |  |  *		for proccessing smb command and sending response | 
					
						
							|  |  |  |  * @conn:	connection instance | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * read remaining data from socket create and submit work. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int queue_ksmbd_work(struct ksmbd_conn *conn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ksmbd_work *work; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	work = ksmbd_alloc_work_struct(); | 
					
						
							|  |  |  | 	if (!work) { | 
					
						
							| 
									
										
										
										
											2021-06-28 15:23:19 +09:00
										 |  |  | 		pr_err("allocation for work failed\n"); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	work->conn = conn; | 
					
						
							|  |  |  | 	work->request_buf = conn->request_buf; | 
					
						
							|  |  |  | 	conn->request_buf = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ksmbd_init_smb_server(work)) { | 
					
						
							|  |  |  | 		ksmbd_free_work_struct(work); | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ksmbd_conn_enqueue_request(work); | 
					
						
							|  |  |  | 	atomic_inc(&conn->r_count); | 
					
						
							|  |  |  | 	/* update activity on connection */ | 
					
						
							|  |  |  | 	conn->last_active = jiffies; | 
					
						
							|  |  |  | 	INIT_WORK(&work->work, handle_ksmbd_work); | 
					
						
							|  |  |  | 	ksmbd_queue_work(work); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ksmbd_server_process_request(struct ksmbd_conn *conn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return queue_ksmbd_work(conn); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ksmbd_sessions_deregister(conn); | 
					
						
							|  |  |  | 	destroy_lease_table(conn); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void ksmbd_server_tcp_callbacks_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct ksmbd_conn_ops ops; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ops.process_fn = ksmbd_server_process_request; | 
					
						
							|  |  |  | 	ops.terminate_fn = ksmbd_server_terminate_conn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ksmbd_conn_init_server_callbacks(&ops); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void server_conf_free(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) { | 
					
						
							|  |  |  | 		kfree(server_conf.conf[i]); | 
					
						
							|  |  |  | 		server_conf.conf[i] = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int server_conf_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); | 
					
						
							|  |  |  | 	server_conf.enforced_signing = 0; | 
					
						
							|  |  |  | 	server_conf.min_protocol = ksmbd_min_protocol(); | 
					
						
							|  |  |  | 	server_conf.max_protocol = ksmbd_max_protocol(); | 
					
						
							|  |  |  | 	server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP; | 
					
						
							|  |  |  | #ifdef CONFIG_SMB_SERVER_KERBEROS5
 | 
					
						
							|  |  |  | 	server_conf.auth_mechs |= KSMBD_AUTH_KRB5 | | 
					
						
							|  |  |  | 				KSMBD_AUTH_MSKRB5; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = ksmbd_conn_transport_init(); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		server_queue_ctrl_reset_work(); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ksmbd_ipc_soft_reset(); | 
					
						
							|  |  |  | 	ksmbd_conn_transport_destroy(); | 
					
						
							|  |  |  | 	server_conf_free(); | 
					
						
							|  |  |  | 	server_conf_init(); | 
					
						
							|  |  |  | 	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void server_ctrl_handle_work(struct work_struct *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct server_ctrl_struct *ctrl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctrl = container_of(work, struct server_ctrl_struct, ctrl_work); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mutex_lock(&ctrl_lock); | 
					
						
							|  |  |  | 	switch (ctrl->type) { | 
					
						
							|  |  |  | 	case SERVER_CTRL_TYPE_INIT: | 
					
						
							|  |  |  | 		server_ctrl_handle_init(ctrl); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SERVER_CTRL_TYPE_RESET: | 
					
						
							|  |  |  | 		server_ctrl_handle_reset(ctrl); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		pr_err("Unknown server work type: %d\n", ctrl->type); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	mutex_unlock(&ctrl_lock); | 
					
						
							|  |  |  | 	kfree(ctrl); | 
					
						
							|  |  |  | 	module_put(THIS_MODULE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __queue_ctrl_work(int type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct server_ctrl_struct *ctrl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!ctrl) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	__module_get(THIS_MODULE); | 
					
						
							|  |  |  | 	ctrl->type = type; | 
					
						
							|  |  |  | 	INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work); | 
					
						
							|  |  |  | 	queue_work(system_long_wq, &ctrl->ctrl_work); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int server_queue_ctrl_init_work(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int server_queue_ctrl_reset_work(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | static ssize_t stats_show(struct class *class, struct class_attribute *attr, | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 			  char *buf) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Inc this each time you change stats output format, | 
					
						
							|  |  |  | 	 * so user space will know what to do. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	static int stats_version = 2; | 
					
						
							|  |  |  | 	static const char * const state[] = { | 
					
						
							|  |  |  | 		"startup", | 
					
						
							|  |  |  | 		"running", | 
					
						
							|  |  |  | 		"reset", | 
					
						
							|  |  |  | 		"shutdown" | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 	ssize_t sz = scnprintf(buf, PAGE_SIZE, "%d %s %d %lu\n", stats_version, | 
					
						
							|  |  |  | 			       state[server_conf.state], server_conf.tcp_port, | 
					
						
							|  |  |  | 			       server_conf.ipc_last_active / HZ); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	return sz; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t kill_server_store(struct class *class, | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 				 struct class_attribute *attr, const char *buf, | 
					
						
							|  |  |  | 				 size_t len) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (!sysfs_streq(buf, "hard")) | 
					
						
							|  |  |  | 		return len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-28 15:23:19 +09:00
										 |  |  | 	pr_info("kill command received\n"); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	mutex_lock(&ctrl_lock); | 
					
						
							|  |  |  | 	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING); | 
					
						
							|  |  |  | 	__module_get(THIS_MODULE); | 
					
						
							|  |  |  | 	server_ctrl_handle_reset(NULL); | 
					
						
							|  |  |  | 	module_put(THIS_MODULE); | 
					
						
							|  |  |  | 	mutex_unlock(&ctrl_lock); | 
					
						
							|  |  |  | 	return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const char * const debug_type_strings[] = {"smb", "auth", "vfs", | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 						  "oplock", "ipc", "conn", | 
					
						
							|  |  |  | 						  "rdma"}; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | static ssize_t debug_show(struct class *class, struct class_attribute *attr, | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 			  char *buf) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	ssize_t sz = 0; | 
					
						
							|  |  |  | 	int i, pos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { | 
					
						
							|  |  |  | 		if ((ksmbd_debug_types >> i) & 1) { | 
					
						
							|  |  |  | 			pos = scnprintf(buf + sz, | 
					
						
							|  |  |  | 					PAGE_SIZE - sz, | 
					
						
							|  |  |  | 					"[%s] ", | 
					
						
							|  |  |  | 					debug_type_strings[i]); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			pos = scnprintf(buf + sz, | 
					
						
							|  |  |  | 					PAGE_SIZE - sz, | 
					
						
							|  |  |  | 					"%s ", | 
					
						
							|  |  |  | 					debug_type_strings[i]); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sz += pos; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); | 
					
						
							|  |  |  | 	return sz; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-30 14:25:35 +09:00
										 |  |  | static ssize_t debug_store(struct class *class, struct class_attribute *attr, | 
					
						
							| 
									
										
										
										
											2021-05-26 17:57:12 +09:00
										 |  |  | 			   const char *buf, size_t len) | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) { | 
					
						
							|  |  |  | 		if (sysfs_streq(buf, "all")) { | 
					
						
							|  |  |  | 			if (ksmbd_debug_types == KSMBD_DEBUG_ALL) | 
					
						
							|  |  |  | 				ksmbd_debug_types = 0; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				ksmbd_debug_types = KSMBD_DEBUG_ALL; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (sysfs_streq(buf, debug_type_strings[i])) { | 
					
						
							|  |  |  | 			if (ksmbd_debug_types & (1 << i)) | 
					
						
							|  |  |  | 				ksmbd_debug_types &= ~(1 << i); | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				ksmbd_debug_types |= (1 << i); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return len; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static CLASS_ATTR_RO(stats); | 
					
						
							|  |  |  | static CLASS_ATTR_WO(kill_server); | 
					
						
							|  |  |  | static CLASS_ATTR_RW(debug); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct attribute *ksmbd_control_class_attrs[] = { | 
					
						
							|  |  |  | 	&class_attr_stats.attr, | 
					
						
							|  |  |  | 	&class_attr_kill_server.attr, | 
					
						
							|  |  |  | 	&class_attr_debug.attr, | 
					
						
							|  |  |  | 	NULL, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | ATTRIBUTE_GROUPS(ksmbd_control_class); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct class ksmbd_control_class = { | 
					
						
							|  |  |  | 	.name		= "ksmbd-control", | 
					
						
							|  |  |  | 	.owner		= THIS_MODULE, | 
					
						
							|  |  |  | 	.class_groups	= ksmbd_control_class_groups, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int ksmbd_server_shutdown(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	class_unregister(&ksmbd_control_class); | 
					
						
							|  |  |  | 	ksmbd_workqueue_destroy(); | 
					
						
							|  |  |  | 	ksmbd_ipc_release(); | 
					
						
							|  |  |  | 	ksmbd_conn_transport_destroy(); | 
					
						
							|  |  |  | 	ksmbd_crypto_destroy(); | 
					
						
							|  |  |  | 	ksmbd_free_global_file_table(); | 
					
						
							|  |  |  | 	destroy_lease_table(NULL); | 
					
						
							| 
									
										
										
										
											2021-06-18 10:17:37 +09:00
										 |  |  | 	ksmbd_work_pool_destroy(); | 
					
						
							|  |  |  | 	ksmbd_exit_file_cache(); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	server_conf_free(); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init ksmbd_server_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = class_register(&ksmbd_control_class); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							| 
									
										
										
										
											2021-06-28 15:23:19 +09:00
										 |  |  | 		pr_err("Unable to register ksmbd-control class\n"); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ksmbd_server_tcp_callbacks_init(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = server_conf_init(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_unregister; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 10:17:37 +09:00
										 |  |  | 	ret = ksmbd_work_pool_init(); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_unregister; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-18 10:17:37 +09:00
										 |  |  | 	ret = ksmbd_init_file_cache(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto err_destroy_work_pools; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	ret = ksmbd_ipc_init(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-06-18 10:17:37 +09:00
										 |  |  | 		goto err_exit_file_cache; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = ksmbd_init_global_file_table(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_ipc_release; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = ksmbd_inode_hash_init(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_destroy_file_table; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = ksmbd_crypto_create(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_release_inode_hash; | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret = ksmbd_workqueue_init(); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | 		goto err_crypto_destroy; | 
					
						
							| 
									
										
										
										
											2021-09-20 19:01:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-14 22:50:49 -05:00
										 |  |  | 	pr_warn_once("The ksmbd server is experimental\n"); | 
					
						
							| 
									
										
										
										
											2021-09-20 19:01:42 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | err_crypto_destroy: | 
					
						
							|  |  |  | 	ksmbd_crypto_destroy(); | 
					
						
							|  |  |  | err_release_inode_hash: | 
					
						
							|  |  |  | 	ksmbd_release_inode_hash(); | 
					
						
							|  |  |  | err_destroy_file_table: | 
					
						
							|  |  |  | 	ksmbd_free_global_file_table(); | 
					
						
							|  |  |  | err_ipc_release: | 
					
						
							|  |  |  | 	ksmbd_ipc_release(); | 
					
						
							| 
									
										
										
										
											2021-06-18 10:17:37 +09:00
										 |  |  | err_exit_file_cache: | 
					
						
							|  |  |  | 	ksmbd_exit_file_cache(); | 
					
						
							|  |  |  | err_destroy_work_pools: | 
					
						
							|  |  |  | 	ksmbd_work_pool_destroy(); | 
					
						
							| 
									
										
										
										
											2021-03-23 16:27:04 +03:00
										 |  |  | err_unregister: | 
					
						
							|  |  |  | 	class_unregister(&ksmbd_control_class); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2021-03-21 17:05:56 +09:00
										 |  |  |  * ksmbd_server_exit() - shutdown forker thread and free memory at module exit | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  |  */ | 
					
						
							|  |  |  | static void __exit ksmbd_server_exit(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	ksmbd_server_shutdown(); | 
					
						
							|  |  |  | 	ksmbd_release_inode_hash(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>"); | 
					
						
							|  |  |  | MODULE_VERSION(KSMBD_VERSION); | 
					
						
							|  |  |  | MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER"); | 
					
						
							|  |  |  | MODULE_LICENSE("GPL"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: ecb"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: hmac"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: md5"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: nls"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: aes"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: cmac"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: sha256"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: sha512"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: aead2"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: ccm"); | 
					
						
							|  |  |  | MODULE_SOFTDEP("pre: gcm"); | 
					
						
							| 
									
										
										
										
											2021-10-31 09:53:50 +09:00
										 |  |  | MODULE_SOFTDEP("pre: crc32"); | 
					
						
							| 
									
										
										
										
											2021-03-16 13:07:11 +09:00
										 |  |  | module_init(ksmbd_server_init) | 
					
						
							|  |  |  | module_exit(ksmbd_server_exit) |