2021-03-16 07:07:11 +03: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 11:05:56 +03:00
* @ work : smb work containing server thread information
2021-03-16 07:07:11 +03: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 06:35:23 +03:00
rsp_hdr = work - > response_buf ;
2021-03-16 07:07:11 +03:00
rsp_hdr - > Status . CifsError = STATUS_CONNECTION_DISCONNECTED ;
return 1 ;
}
return 0 ;
}
2021-07-16 08:52:46 +03:00
# define SERVER_HANDLER_CONTINUE 0
# define SERVER_HANDLER_ABORT 1
2021-03-16 07:07:11 +03:00
2021-03-30 08:25:35 +03:00
static int __process_request ( struct ksmbd_work * work , struct ksmbd_conn * conn ,
2021-05-26 12:01:08 +03:00
u16 * cmd )
2021-03-16 07:07:11 +03:00
{
struct smb_version_cmds * cmds ;
2021-05-26 12:01:08 +03:00
u16 command ;
2021-03-16 07:07:11 +03:00
int ret ;
if ( check_conn_state ( work ) )
2021-07-16 08:52:46 +03:00
return SERVER_HANDLER_CONTINUE ;
2021-03-16 07:07:11 +03:00
if ( ksmbd_verify_smb_message ( work ) )
2021-07-16 08:52:46 +03:00
return SERVER_HANDLER_ABORT ;
2021-03-16 07:07:11 +03: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 08:52:46 +03:00
return SERVER_HANDLER_CONTINUE ;
2021-03-16 07:07:11 +03: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 08:52:46 +03:00
return SERVER_HANDLER_CONTINUE ;
2021-03-16 07:07:11 +03: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 08:52:46 +03:00
return SERVER_HANDLER_CONTINUE ;
2021-03-16 07:07:11 +03: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 08:52:46 +03:00
return SERVER_HANDLER_ABORT ;
return SERVER_HANDLER_CONTINUE ;
2021-03-16 07:07:11 +03:00
}
static void __handle_ksmbd_work ( struct ksmbd_work * work ,
2021-05-26 11:57:12 +03:00
struct ksmbd_conn * conn )
2021-03-16 07:07:11 +03:00
{
2021-03-30 08:25:35 +03:00
u16 command = 0 ;
2021-03-16 07:07:11 +03:00
int rc ;
if ( conn - > ops - > allocate_rsp_buf ( work ) )
return ;
if ( conn - > ops - > is_transform_hdr & &
2021-03-30 08:25:35 +03:00
conn - > ops - > is_transform_hdr ( work - > request_buf ) ) {
2021-03-16 07:07:11 +03: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 08:52:46 +03:00
if ( rc = = SERVER_HANDLER_ABORT )
2021-03-16 07:07:11 +03: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 11:57:12 +03:00
if ( work - > sess & &
( work - > sess - > sign | | smb3_11_final_sess_setup_resp ( work ) | |
2021-03-16 07:07:11 +03: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 08:25:35 +03:00
conn - > ops - > encrypt_resp ) {
2021-03-16 07:07:11 +03: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 11:05:56 +03:00
* @ wk : smb work containing request command buffer
2021-03-16 07:07:11 +03: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 09:23:19 +03:00
pr_err ( " allocation for work failed \n " ) ;
2021-03-16 07:07:11 +03: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 08:25:35 +03:00
static ssize_t stats_show ( struct class * class , struct class_attribute * attr ,
2021-05-26 11:57:12 +03:00
char * buf )
2021-03-16 07:07:11 +03: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 11:57:12 +03: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 07:07:11 +03:00
return sz ;
}
static ssize_t kill_server_store ( struct class * class ,
2021-05-26 11:57:12 +03:00
struct class_attribute * attr , const char * buf ,
size_t len )
2021-03-16 07:07:11 +03:00
{
if ( ! sysfs_streq ( buf , " hard " ) )
return len ;
2021-06-28 09:23:19 +03:00
pr_info ( " kill command received \n " ) ;
2021-03-16 07:07:11 +03: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 11:57:12 +03:00
" oplock " , " ipc " , " conn " ,
" rdma " } ;
2021-03-16 07:07:11 +03:00
2021-03-30 08:25:35 +03:00
static ssize_t debug_show ( struct class * class , struct class_attribute * attr ,
2021-05-26 11:57:12 +03:00
char * buf )
2021-03-16 07:07:11 +03: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 08:25:35 +03:00
static ssize_t debug_store ( struct class * class , struct class_attribute * attr ,
2021-05-26 11:57:12 +03:00
const char * buf , size_t len )
2021-03-16 07:07:11 +03: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 04:17:37 +03:00
ksmbd_work_pool_destroy ( ) ;
ksmbd_exit_file_cache ( ) ;
2021-03-16 07:07:11 +03: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 09:23:19 +03:00
pr_err ( " Unable to register ksmbd-control class \n " ) ;
2021-03-16 07:07:11 +03: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 07:07:11 +03:00
2021-06-18 04:17:37 +03:00
ret = ksmbd_work_pool_init ( ) ;
2021-03-16 07:07:11 +03:00
if ( ret )
2021-03-23 16:27:04 +03:00
goto err_unregister ;
2021-03-16 07:07:11 +03:00
2021-06-18 04:17:37 +03:00
ret = ksmbd_init_file_cache ( ) ;
if ( ret )
goto err_destroy_work_pools ;
2021-03-16 07:07:11 +03:00
ret = ksmbd_ipc_init ( ) ;
if ( ret )
2021-06-18 04:17:37 +03:00
goto err_exit_file_cache ;
2021-03-16 07:07:11 +03:00
ret = ksmbd_init_global_file_table ( ) ;
if ( ret )
2021-03-23 16:27:04 +03:00
goto err_ipc_release ;
2021-03-16 07:07:11 +03:00
ret = ksmbd_inode_hash_init ( ) ;
if ( ret )
2021-03-23 16:27:04 +03:00
goto err_destroy_file_table ;
2021-03-16 07:07:11 +03:00
ret = ksmbd_crypto_create ( ) ;
if ( ret )
2021-03-23 16:27:04 +03:00
goto err_release_inode_hash ;
2021-03-16 07:07:11 +03:00
ret = ksmbd_workqueue_init ( ) ;
if ( ret )
2021-03-23 16:27:04 +03:00
goto err_crypto_destroy ;
2021-09-21 03:01:42 +03:00
2022-03-15 06:50:49 +03:00
pr_warn_once ( " The ksmbd server is experimental \n " ) ;
2021-09-21 03:01:42 +03:00
2021-03-16 07:07:11 +03: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 04:17:37 +03: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 07:07:11 +03:00
return ret ;
}
/**
2021-03-21 11:05:56 +03:00
* ksmbd_server_exit ( ) - shutdown forker thread and free memory at module exit
2021-03-16 07:07:11 +03: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 03:53:50 +03:00
MODULE_SOFTDEP ( " pre: crc32 " ) ;
2021-03-16 07:07:11 +03:00
module_init ( ksmbd_server_init )
module_exit ( ksmbd_server_exit )