brintos

brintos / linux-shallow public Read only

0
0
Text · 14.5 KiB · e6cfedb Raw
648 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *   Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>4 *   Copyright (C) 2018 Samsung Electronics Co., Ltd.5 */6 7#include "glob.h"8#include "oplock.h"9#include "misc.h"10#include <linux/sched/signal.h>11#include <linux/workqueue.h>12#include <linux/sysfs.h>13#include <linux/module.h>14#include <linux/moduleparam.h>15 16#include "server.h"17#include "smb_common.h"18#include "../common/smb2status.h"19#include "connection.h"20#include "transport_ipc.h"21#include "mgmt/user_session.h"22#include "crypto_ctx.h"23#include "auth.h"24 25int ksmbd_debug_types;26 27struct ksmbd_server_config server_conf;28 29enum SERVER_CTRL_TYPE {30	SERVER_CTRL_TYPE_INIT,31	SERVER_CTRL_TYPE_RESET,32};33 34struct server_ctrl_struct {35	int			type;36	struct work_struct	ctrl_work;37};38 39static DEFINE_MUTEX(ctrl_lock);40 41static int ___server_conf_set(int idx, char *val)42{43	if (idx >= ARRAY_SIZE(server_conf.conf))44		return -EINVAL;45 46	if (!val || val[0] == 0x00)47		return -EINVAL;48 49	kfree(server_conf.conf[idx]);50	server_conf.conf[idx] = kstrdup(val, GFP_KERNEL);51	if (!server_conf.conf[idx])52		return -ENOMEM;53	return 0;54}55 56int ksmbd_set_netbios_name(char *v)57{58	return ___server_conf_set(SERVER_CONF_NETBIOS_NAME, v);59}60 61int ksmbd_set_server_string(char *v)62{63	return ___server_conf_set(SERVER_CONF_SERVER_STRING, v);64}65 66int ksmbd_set_work_group(char *v)67{68	return ___server_conf_set(SERVER_CONF_WORK_GROUP, v);69}70 71char *ksmbd_netbios_name(void)72{73	return server_conf.conf[SERVER_CONF_NETBIOS_NAME];74}75 76char *ksmbd_server_string(void)77{78	return server_conf.conf[SERVER_CONF_SERVER_STRING];79}80 81char *ksmbd_work_group(void)82{83	return server_conf.conf[SERVER_CONF_WORK_GROUP];84}85 86/**87 * check_conn_state() - check state of server thread connection88 * @work:     smb work containing server thread information89 *90 * Return:	0 on valid connection, otherwise 1 to reconnect91 */92static inline int check_conn_state(struct ksmbd_work *work)93{94	struct smb_hdr *rsp_hdr;95 96	if (ksmbd_conn_exiting(work->conn) ||97	    ksmbd_conn_need_reconnect(work->conn)) {98		rsp_hdr = work->response_buf;99		rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED;100		return 1;101	}102	return 0;103}104 105#define SERVER_HANDLER_CONTINUE		0106#define SERVER_HANDLER_ABORT		1107 108static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,109			     u16 *cmd)110{111	struct smb_version_cmds *cmds;112	u16 command;113	int ret;114 115	if (check_conn_state(work))116		return SERVER_HANDLER_CONTINUE;117 118	if (ksmbd_verify_smb_message(work)) {119		conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);120		return SERVER_HANDLER_ABORT;121	}122 123	command = conn->ops->get_cmd_val(work);124	*cmd = command;125 126andx_again:127	if (command >= conn->max_cmds) {128		conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);129		return SERVER_HANDLER_CONTINUE;130	}131 132	cmds = &conn->cmds[command];133	if (!cmds->proc) {134		ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command);135		conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED);136		return SERVER_HANDLER_CONTINUE;137	}138 139	if (work->sess && conn->ops->is_sign_req(work, command)) {140		ret = conn->ops->check_sign_req(work);141		if (!ret) {142			conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);143			return SERVER_HANDLER_CONTINUE;144		}145	}146 147	ret = cmds->proc(work);148 149	if (ret < 0)150		ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret);151	/* AndX commands - chained request can return positive values */152	else if (ret > 0) {153		command = ret;154		*cmd = command;155		goto andx_again;156	}157 158	if (work->send_no_response)159		return SERVER_HANDLER_ABORT;160	return SERVER_HANDLER_CONTINUE;161}162 163static void __handle_ksmbd_work(struct ksmbd_work *work,164				struct ksmbd_conn *conn)165{166	u16 command = 0;167	int rc;168	bool is_chained = false;169 170	if (conn->ops->is_transform_hdr &&171	    conn->ops->is_transform_hdr(work->request_buf)) {172		rc = conn->ops->decrypt_req(work);173		if (rc < 0)174			return;175		work->encrypted = true;176	}177 178	if (conn->ops->allocate_rsp_buf(work))179		return;180 181	rc = conn->ops->init_rsp_hdr(work);182	if (rc) {183		/* either uid or tid is not correct */184		conn->ops->set_rsp_status(work, STATUS_INVALID_HANDLE);185		goto send;186	}187 188	do {189		if (conn->ops->check_user_session) {190			rc = conn->ops->check_user_session(work);191			if (rc < 0) {192				if (rc == -EINVAL)193					conn->ops->set_rsp_status(work,194						STATUS_INVALID_PARAMETER);195				else196					conn->ops->set_rsp_status(work,197						STATUS_USER_SESSION_DELETED);198				goto send;199			} else if (rc > 0) {200				rc = conn->ops->get_ksmbd_tcon(work);201				if (rc < 0) {202					if (rc == -EINVAL)203						conn->ops->set_rsp_status(work,204							STATUS_INVALID_PARAMETER);205					else206						conn->ops->set_rsp_status(work,207							STATUS_NETWORK_NAME_DELETED);208					goto send;209				}210			}211		}212 213		rc = __process_request(work, conn, &command);214		if (rc == SERVER_HANDLER_ABORT)215			break;216 217		/*218		 * Call smb2_set_rsp_credits() function to set number of credits219		 * granted in hdr of smb2 response.220		 */221		if (conn->ops->set_rsp_credits) {222			spin_lock(&conn->credits_lock);223			rc = conn->ops->set_rsp_credits(work);224			spin_unlock(&conn->credits_lock);225			if (rc < 0) {226				conn->ops->set_rsp_status(work,227					STATUS_INVALID_PARAMETER);228				goto send;229			}230		}231 232		is_chained = is_chained_smb2_message(work);233 234		if (work->sess &&235		    (work->sess->sign || smb3_11_final_sess_setup_resp(work) ||236		     conn->ops->is_sign_req(work, command)))237			conn->ops->set_sign_rsp(work);238	} while (is_chained == true);239 240send:241	if (work->tcon)242		ksmbd_tree_connect_put(work->tcon);243	smb3_preauth_hash_rsp(work);244	if (work->sess)245		ksmbd_user_session_put(work->sess);246	if (work->sess && work->sess->enc && work->encrypted &&247	    conn->ops->encrypt_resp) {248		rc = conn->ops->encrypt_resp(work);249		if (rc < 0)250			conn->ops->set_rsp_status(work, STATUS_DATA_ERROR);251	}252 253	ksmbd_conn_write(work);254}255 256/**257 * handle_ksmbd_work() - process pending smb work requests258 * @wk:	smb work containing request command buffer259 *260 * called by kworker threads to processing remaining smb work requests261 */262static void handle_ksmbd_work(struct work_struct *wk)263{264	struct ksmbd_work *work = container_of(wk, struct ksmbd_work, work);265	struct ksmbd_conn *conn = work->conn;266 267	atomic64_inc(&conn->stats.request_served);268 269	__handle_ksmbd_work(work, conn);270 271	ksmbd_conn_try_dequeue_request(work);272	ksmbd_free_work_struct(work);273	atomic_dec(&conn->mux_smb_requests);274	/*275	 * Checking waitqueue to dropping pending requests on276	 * disconnection. waitqueue_active is safe because it277	 * uses atomic operation for condition.278	 */279	if (!atomic_dec_return(&conn->r_count) && waitqueue_active(&conn->r_count_q))280		wake_up(&conn->r_count_q);281}282 283/**284 * queue_ksmbd_work() - queue a smb request to worker thread queue285 *		for processing smb command and sending response286 * @conn:	connection instance287 *288 * read remaining data from socket create and submit work.289 */290static int queue_ksmbd_work(struct ksmbd_conn *conn)291{292	struct ksmbd_work *work;293	int err;294 295	err = ksmbd_init_smb_server(conn);296	if (err)297		return 0;298 299	if (atomic_inc_return(&conn->mux_smb_requests) >= conn->vals->max_credits) {300		atomic_dec_return(&conn->mux_smb_requests);301		return -ENOSPC;302	}303 304	work = ksmbd_alloc_work_struct();305	if (!work) {306		pr_err("allocation for work failed\n");307		return -ENOMEM;308	}309 310	work->conn = conn;311	work->request_buf = conn->request_buf;312	conn->request_buf = NULL;313 314	ksmbd_conn_enqueue_request(work);315	atomic_inc(&conn->r_count);316	/* update activity on connection */317	conn->last_active = jiffies;318	INIT_WORK(&work->work, handle_ksmbd_work);319	ksmbd_queue_work(work);320	return 0;321}322 323static int ksmbd_server_process_request(struct ksmbd_conn *conn)324{325	return queue_ksmbd_work(conn);326}327 328static int ksmbd_server_terminate_conn(struct ksmbd_conn *conn)329{330	ksmbd_sessions_deregister(conn);331	destroy_lease_table(conn);332	return 0;333}334 335static void ksmbd_server_tcp_callbacks_init(void)336{337	struct ksmbd_conn_ops ops;338 339	ops.process_fn = ksmbd_server_process_request;340	ops.terminate_fn = ksmbd_server_terminate_conn;341 342	ksmbd_conn_init_server_callbacks(&ops);343}344 345static void server_conf_free(void)346{347	int i;348 349	for (i = 0; i < ARRAY_SIZE(server_conf.conf); i++) {350		kfree(server_conf.conf[i]);351		server_conf.conf[i] = NULL;352	}353}354 355static int server_conf_init(void)356{357	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP);358	server_conf.enforced_signing = 0;359	server_conf.min_protocol = ksmbd_min_protocol();360	server_conf.max_protocol = ksmbd_max_protocol();361	server_conf.auth_mechs = KSMBD_AUTH_NTLMSSP;362#ifdef CONFIG_SMB_SERVER_KERBEROS5363	server_conf.auth_mechs |= KSMBD_AUTH_KRB5 |364				KSMBD_AUTH_MSKRB5;365#endif366	return 0;367}368 369static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl)370{371	int ret;372 373	ret = ksmbd_conn_transport_init();374	if (ret) {375		server_queue_ctrl_reset_work();376		return;377	}378 379	WRITE_ONCE(server_conf.state, SERVER_STATE_RUNNING);380}381 382static void server_ctrl_handle_reset(struct server_ctrl_struct *ctrl)383{384	ksmbd_ipc_soft_reset();385	ksmbd_conn_transport_destroy();386	ksmbd_stop_durable_scavenger();387	server_conf_free();388	server_conf_init();389	WRITE_ONCE(server_conf.state, SERVER_STATE_STARTING_UP);390}391 392static void server_ctrl_handle_work(struct work_struct *work)393{394	struct server_ctrl_struct *ctrl;395 396	ctrl = container_of(work, struct server_ctrl_struct, ctrl_work);397 398	mutex_lock(&ctrl_lock);399	switch (ctrl->type) {400	case SERVER_CTRL_TYPE_INIT:401		server_ctrl_handle_init(ctrl);402		break;403	case SERVER_CTRL_TYPE_RESET:404		server_ctrl_handle_reset(ctrl);405		break;406	default:407		pr_err("Unknown server work type: %d\n", ctrl->type);408	}409	mutex_unlock(&ctrl_lock);410	kfree(ctrl);411	module_put(THIS_MODULE);412}413 414static int __queue_ctrl_work(int type)415{416	struct server_ctrl_struct *ctrl;417 418	ctrl = kmalloc(sizeof(struct server_ctrl_struct), GFP_KERNEL);419	if (!ctrl)420		return -ENOMEM;421 422	__module_get(THIS_MODULE);423	ctrl->type = type;424	INIT_WORK(&ctrl->ctrl_work, server_ctrl_handle_work);425	queue_work(system_long_wq, &ctrl->ctrl_work);426	return 0;427}428 429int server_queue_ctrl_init_work(void)430{431	return __queue_ctrl_work(SERVER_CTRL_TYPE_INIT);432}433 434int server_queue_ctrl_reset_work(void)435{436	return __queue_ctrl_work(SERVER_CTRL_TYPE_RESET);437}438 439static ssize_t stats_show(const struct class *class, const struct class_attribute *attr,440			  char *buf)441{442	/*443	 * Inc this each time you change stats output format,444	 * so user space will know what to do.445	 */446	static int stats_version = 2;447	static const char * const state[] = {448		"startup",449		"running",450		"reset",451		"shutdown"452	};453	return sysfs_emit(buf, "%d %s %d %lu\n", stats_version,454			  state[server_conf.state], server_conf.tcp_port,455			  server_conf.ipc_last_active / HZ);456}457 458static ssize_t kill_server_store(const struct class *class,459				 const struct class_attribute *attr, const char *buf,460				 size_t len)461{462	if (!sysfs_streq(buf, "hard"))463		return len;464 465	pr_info("kill command received\n");466	mutex_lock(&ctrl_lock);467	WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);468	__module_get(THIS_MODULE);469	server_ctrl_handle_reset(NULL);470	module_put(THIS_MODULE);471	mutex_unlock(&ctrl_lock);472	return len;473}474 475static const char * const debug_type_strings[] = {"smb", "auth", "vfs",476						  "oplock", "ipc", "conn",477						  "rdma"};478 479static ssize_t debug_show(const struct class *class, const struct class_attribute *attr,480			  char *buf)481{482	ssize_t sz = 0;483	int i, pos = 0;484 485	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {486		if ((ksmbd_debug_types >> i) & 1) {487			pos = sysfs_emit_at(buf, sz, "[%s] ", debug_type_strings[i]);488		} else {489			pos = sysfs_emit_at(buf, sz, "%s ", debug_type_strings[i]);490		}491		sz += pos;492	}493	sz += sysfs_emit_at(buf, sz, "\n");494	return sz;495}496 497static ssize_t debug_store(const struct class *class, const struct class_attribute *attr,498			   const char *buf, size_t len)499{500	int i;501 502	for (i = 0; i < ARRAY_SIZE(debug_type_strings); i++) {503		if (sysfs_streq(buf, "all")) {504			if (ksmbd_debug_types == KSMBD_DEBUG_ALL)505				ksmbd_debug_types = 0;506			else507				ksmbd_debug_types = KSMBD_DEBUG_ALL;508			break;509		}510 511		if (sysfs_streq(buf, debug_type_strings[i])) {512			if (ksmbd_debug_types & (1 << i))513				ksmbd_debug_types &= ~(1 << i);514			else515				ksmbd_debug_types |= (1 << i);516			break;517		}518	}519 520	return len;521}522 523static CLASS_ATTR_RO(stats);524static CLASS_ATTR_WO(kill_server);525static CLASS_ATTR_RW(debug);526 527static struct attribute *ksmbd_control_class_attrs[] = {528	&class_attr_stats.attr,529	&class_attr_kill_server.attr,530	&class_attr_debug.attr,531	NULL,532};533ATTRIBUTE_GROUPS(ksmbd_control_class);534 535static struct class ksmbd_control_class = {536	.name		= "ksmbd-control",537	.class_groups	= ksmbd_control_class_groups,538};539 540static int ksmbd_server_shutdown(void)541{542	WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN);543 544	class_unregister(&ksmbd_control_class);545	ksmbd_workqueue_destroy();546	ksmbd_ipc_release();547	ksmbd_conn_transport_destroy();548	ksmbd_crypto_destroy();549	ksmbd_free_global_file_table();550	destroy_lease_table(NULL);551	ksmbd_work_pool_destroy();552	ksmbd_exit_file_cache();553	server_conf_free();554	return 0;555}556 557static int __init ksmbd_server_init(void)558{559	int ret;560 561	ret = class_register(&ksmbd_control_class);562	if (ret) {563		pr_err("Unable to register ksmbd-control class\n");564		return ret;565	}566 567	ksmbd_server_tcp_callbacks_init();568 569	ret = server_conf_init();570	if (ret)571		goto err_unregister;572 573	ret = ksmbd_work_pool_init();574	if (ret)575		goto err_unregister;576 577	ret = ksmbd_init_file_cache();578	if (ret)579		goto err_destroy_work_pools;580 581	ret = ksmbd_ipc_init();582	if (ret)583		goto err_exit_file_cache;584 585	ret = ksmbd_init_global_file_table();586	if (ret)587		goto err_ipc_release;588 589	ret = ksmbd_inode_hash_init();590	if (ret)591		goto err_destroy_file_table;592 593	ret = ksmbd_crypto_create();594	if (ret)595		goto err_release_inode_hash;596 597	ret = ksmbd_workqueue_init();598	if (ret)599		goto err_crypto_destroy;600 601	return 0;602 603err_crypto_destroy:604	ksmbd_crypto_destroy();605err_release_inode_hash:606	ksmbd_release_inode_hash();607err_destroy_file_table:608	ksmbd_free_global_file_table();609err_ipc_release:610	ksmbd_ipc_release();611err_exit_file_cache:612	ksmbd_exit_file_cache();613err_destroy_work_pools:614	ksmbd_work_pool_destroy();615err_unregister:616	class_unregister(&ksmbd_control_class);617 618	return ret;619}620 621/**622 * ksmbd_server_exit() - shutdown forker thread and free memory at module exit623 */624static void __exit ksmbd_server_exit(void)625{626	ksmbd_server_shutdown();627	rcu_barrier();628	ksmbd_release_inode_hash();629}630 631MODULE_AUTHOR("Namjae Jeon <linkinjeon@kernel.org>");632MODULE_DESCRIPTION("Linux kernel CIFS/SMB SERVER");633MODULE_LICENSE("GPL");634MODULE_SOFTDEP("pre: ecb");635MODULE_SOFTDEP("pre: hmac");636MODULE_SOFTDEP("pre: md5");637MODULE_SOFTDEP("pre: nls");638MODULE_SOFTDEP("pre: aes");639MODULE_SOFTDEP("pre: cmac");640MODULE_SOFTDEP("pre: sha256");641MODULE_SOFTDEP("pre: sha512");642MODULE_SOFTDEP("pre: aead2");643MODULE_SOFTDEP("pre: ccm");644MODULE_SOFTDEP("pre: gcm");645MODULE_SOFTDEP("pre: crc32");646module_init(ksmbd_server_init)647module_exit(ksmbd_server_exit)648