Initial merge of kernel smb3 file server, ksmbd
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmEr2okACgkQiiy9cAdy T1GucQwAoZykSKyy2AMbyYSap1Bm+CXX7YHoMlnqt0IF8ka9XKFj8nMBPgB8GKFE JLyikUxxeqgwbuOAkeyJu2knzX7QH18yTqz/YpytCx5waBgWdhLybn01xx/2n8fX ZA6E3Sb4aJKD7DO1Ia5t0u1lI4c2iZTIOwpoYgTX7VQi8VQYU/TN6aOIZR0Xgznw ffoxC4d9nlDjLzlTqY4oLnuIEQ0zHUBhndXxSy5Zh20cBUOLeWkOLUt1w1IkaR36 f4k2UL3Y8njc9mKiOsQfFV/KE6YnPA0hZ48+h3xmWopjIJp5Wl7ublA/2c9rjxCf K5PwwgZ2bS5OpDvzSMPmquxGdQm9aPOlWwTbSL0NHe1ws/bD3Gx3/5/pjGJjyaYb i0YagAI8VaJPnPqBGdCfcwgV4NtshzSV2l9qfnkBG2hwkD4DldFt2qP4s/nQXPNt /bmEoTjuzrNhYbw3N2bbBQUK9CKhGywCbb2yAtSx5Me4FfxNANQ7lK1VLdfd6h4k o9esOBdF =yh1J -----END PGP SIGNATURE----- Merge tag '5.15-rc-first-ksmbd-merge' of git://git.samba.org/ksmbd Pull initial ksmbd implementation from Steve French: "Initial merge of kernel smb3 file server, ksmbd. The SMB family of protocols is the most widely deployed network filesystem protocol, the default on Windows and Macs (and even on many phones and tablets), with clients and servers on all major operating systems, but lacked a kernel server for Linux. For many cases the current userspace server choices were suboptimal either due to memory footprint, performance or difficulty integrating well with advanced Linux features. ksmbd is a new kernel module which implements the server-side of the SMB3 protocol. The target is to provide optimized performance, GPLv2 SMB server, and better lease handling (distributed caching). The bigger goal is to add new features more rapidly (e.g. RDMA aka "smbdirect", and recent encryption and signing improvements to the protocol) which are easier to develop on a smaller, more tightly optimized kernel server than for example in Samba. The Samba project is much broader in scope (tools, security services, LDAP, Active Directory Domain Controller, and a cross platform file server for a wider variety of purposes) but the user space file server portion of Samba has proved hard to optimize for some Linux workloads, including for smaller devices. This is not meant to replace Samba, but rather be an extension to allow better optimizing for Linux, and will continue to integrate well with Samba user space tools and libraries where appropriate. Working with the Samba team we have already made sure that the configuration files and xattrs are in a compatible format between the kernel and user space server. Various types of functional and regression tests are regularly run against it. One example is the automated 'buildbot' regression tests which use the Linux client to test against ksmbd, e.g. http://smb3-test-rhel-75.southcentralus.cloudapp.azure.com/#/builders/8/builds/56 but other test suites, including Samba's smbtorture functional test suite are also used regularly" * tag '5.15-rc-first-ksmbd-merge' of git://git.samba.org/ksmbd: (219 commits) ksmbd: fix __write_overflow warning in ndr_read_string MAINTAINERS: ksmbd: add cifs_common directory to ksmbd entry MAINTAINERS: ksmbd: update my email address ksmbd: fix permission check issue on chown and chmod ksmbd: don't set FILE DELETE and FILE_DELETE_CHILD in access mask by default MAINTAINERS: add git adddress of ksmbd ksmbd: update SMB3 multi-channel support in ksmbd.rst ksmbd: smbd: fix kernel oops during server shutdown ksmbd: remove select FS_POSIX_ACL in Kconfig ksmbd: use proper errno instead of -1 in smb2_get_ksmbd_tcon() ksmbd: update the comment for smb2_get_ksmbd_tcon() ksmbd: change int data type to boolean ksmbd: Fix multi-protocol negotiation ksmbd: fix an oops in error handling in smb2_open() ksmbd: add ipv6_addr_v4mapped check to know if connection from client is ipv4 ksmbd: fix missing error code in smb2_lock ksmbd: use channel signingkey for binding SMB2 session setup ksmbd: don't set RSS capable in FSCTL_QUERY_NETWORK_INTERFACE_INFO ksmbd: Return STATUS_OBJECT_PATH_NOT_FOUND if smb2_creat() returns ENOENT ksmbd: fix -Wstringop-truncation warnings ...
This commit is contained in:
commit
e24c567b7e
|
@ -0,0 +1,10 @@
|
|||
===============================
|
||||
CIFS
|
||||
===============================
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ksmbd
|
||||
cifsroot
|
|
@ -0,0 +1,165 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==========================
|
||||
KSMBD - SMB3 Kernel Server
|
||||
==========================
|
||||
|
||||
KSMBD is a linux kernel server which implements SMB3 protocol in kernel space
|
||||
for sharing files over network.
|
||||
|
||||
KSMBD architecture
|
||||
==================
|
||||
|
||||
The subset of performance related operations belong in kernelspace and
|
||||
the other subset which belong to operations which are not really related with
|
||||
performance in userspace. So, DCE/RPC management that has historically resulted
|
||||
into number of buffer overflow issues and dangerous security bugs and user
|
||||
account management are implemented in user space as ksmbd.mountd.
|
||||
File operations that are related with performance (open/read/write/close etc.)
|
||||
in kernel space (ksmbd). This also allows for easier integration with VFS
|
||||
interface for all file operations.
|
||||
|
||||
ksmbd (kernel daemon)
|
||||
---------------------
|
||||
|
||||
When the server daemon is started, It starts up a forker thread
|
||||
(ksmbd/interface name) at initialization time and open a dedicated port 445
|
||||
for listening to SMB requests. Whenever new clients make request, Forker
|
||||
thread will accept the client connection and fork a new thread for dedicated
|
||||
communication channel between the client and the server. It allows for parallel
|
||||
processing of SMB requests(commands) from clients as well as allowing for new
|
||||
clients to make new connections. Each instance is named ksmbd/1~n(port number)
|
||||
to indicate connected clients. Depending on the SMB request types, each new
|
||||
thread can decide to pass through the commands to the user space (ksmbd.mountd),
|
||||
currently DCE/RPC commands are identified to be handled through the user space.
|
||||
To further utilize the linux kernel, it has been chosen to process the commands
|
||||
as workitems and to be executed in the handlers of the ksmbd-io kworker threads.
|
||||
It allows for multiplexing of the handlers as the kernel take care of initiating
|
||||
extra worker threads if the load is increased and vice versa, if the load is
|
||||
decreased it destroys the extra worker threads. So, after connection is
|
||||
established with client. Dedicated ksmbd/1..n(port number) takes complete
|
||||
ownership of receiving/parsing of SMB commands. Each received command is worked
|
||||
in parallel i.e., There can be multiple clients commands which are worked in
|
||||
parallel. After receiving each command a separated kernel workitem is prepared
|
||||
for each command which is further queued to be handled by ksmbd-io kworkers.
|
||||
So, each SMB workitem is queued to the kworkers. This allows the benefit of load
|
||||
sharing to be managed optimally by the default kernel and optimizing client
|
||||
performance by handling client commands in parallel.
|
||||
|
||||
ksmbd.mountd (user space daemon)
|
||||
--------------------------------
|
||||
|
||||
ksmbd.mountd is userspace process to, transfer user account and password that
|
||||
are registered using ksmbd.adduser(part of utils for user space). Further it
|
||||
allows sharing information parameters that parsed from smb.conf to ksmbd in
|
||||
kernel. For the execution part it has a daemon which is continuously running
|
||||
and connected to the kernel interface using netlink socket, it waits for the
|
||||
requests(dcerpc and share/user info). It handles RPC calls (at a minimum few
|
||||
dozen) that are most important for file server from NetShareEnum and
|
||||
NetServerGetInfo. Complete DCE/RPC response is prepared from the user space
|
||||
and passed over to the associated kernel thread for the client.
|
||||
|
||||
|
||||
KSMBD Feature Status
|
||||
====================
|
||||
|
||||
============================== =================================================
|
||||
Feature name Status
|
||||
============================== =================================================
|
||||
Dialects Supported. SMB2.1 SMB3.0, SMB3.1.1 dialects
|
||||
(intentionally excludes security vulnerable SMB1
|
||||
dialect).
|
||||
Auto Negotiation Supported.
|
||||
Compound Request Supported.
|
||||
Oplock Cache Mechanism Supported.
|
||||
SMB2 leases(v1 lease) Supported.
|
||||
Directory leases(v2 lease) Planned for future.
|
||||
Multi-credits Supported.
|
||||
NTLM/NTLMv2 Supported.
|
||||
HMAC-SHA256 Signing Supported.
|
||||
Secure negotiate Supported.
|
||||
Signing Update Supported.
|
||||
Pre-authentication integrity Supported.
|
||||
SMB3 encryption(CCM, GCM) Supported. (CCM and GCM128 supported, GCM256 in
|
||||
progress)
|
||||
SMB direct(RDMA) Partially Supported. SMB3 Multi-channel is
|
||||
required to connect to Windows client.
|
||||
SMB3 Multi-channel Partially Supported. Planned to implement
|
||||
replay/retry mechanisms for future.
|
||||
SMB3.1.1 POSIX extension Supported.
|
||||
ACLs Partially Supported. only DACLs available, SACLs
|
||||
(auditing) is planned for the future. For
|
||||
ownership (SIDs) ksmbd generates random subauth
|
||||
values(then store it to disk) and use uid/gid
|
||||
get from inode as RID for local domain SID.
|
||||
The current acl implementation is limited to
|
||||
standalone server, not a domain member.
|
||||
Integration with Samba tools is being worked on
|
||||
to allow future support for running as a domain
|
||||
member.
|
||||
Kerberos Supported.
|
||||
Durable handle v1,v2 Planned for future.
|
||||
Persistent handle Planned for future.
|
||||
SMB2 notify Planned for future.
|
||||
Sparse file support Supported.
|
||||
DCE/RPC support Partially Supported. a few calls(NetShareEnumAll,
|
||||
NetServerGetInfo, SAMR, LSARPC) that are needed
|
||||
for file server handled via netlink interface
|
||||
from ksmbd.mountd. Additional integration with
|
||||
Samba tools and libraries via upcall is being
|
||||
investigated to allow support for additional
|
||||
DCE/RPC management calls (and future support
|
||||
for Witness protocol e.g.)
|
||||
ksmbd/nfsd interoperability Planned for future. The features that ksmbd
|
||||
support are Leases, Notify, ACLs and Share modes.
|
||||
============================== =================================================
|
||||
|
||||
|
||||
How to run
|
||||
==========
|
||||
|
||||
1. Download ksmbd-tools and compile them.
|
||||
- https://github.com/cifsd-team/ksmbd-tools
|
||||
|
||||
2. Create user/password for SMB share.
|
||||
|
||||
# mkdir /etc/ksmbd/
|
||||
# ksmbd.adduser -a <Enter USERNAME for SMB share access>
|
||||
|
||||
3. Create /etc/ksmbd/smb.conf file, add SMB share in smb.conf file
|
||||
- Refer smb.conf.example and
|
||||
https://github.com/cifsd-team/ksmbd-tools/blob/master/Documentation/configuration.txt
|
||||
|
||||
4. Insert ksmbd.ko module
|
||||
|
||||
# insmod ksmbd.ko
|
||||
|
||||
5. Start ksmbd user space daemon
|
||||
# ksmbd.mountd
|
||||
|
||||
6. Access share from Windows or Linux using CIFS
|
||||
|
||||
Shutdown KSMBD
|
||||
==============
|
||||
|
||||
1. kill user and kernel space daemon
|
||||
# sudo ksmbd.control -s
|
||||
|
||||
How to turn debug print on
|
||||
==========================
|
||||
|
||||
Each layer
|
||||
/sys/class/ksmbd-control/debug
|
||||
|
||||
1. Enable all component prints
|
||||
# sudo ksmbd.control -d "all"
|
||||
|
||||
2. Enable one of components(smb, auth, vfs, oplock, ipc, conn, rdma)
|
||||
# sudo ksmbd.control -d "smb"
|
||||
|
||||
3. Show what prints are enable.
|
||||
# cat/sys/class/ksmbd-control/debug
|
||||
[smb] auth vfs oplock ipc conn [rdma]
|
||||
|
||||
4. Disable prints:
|
||||
If you try the selected component once more, It is disabled without brackets.
|
|
@ -72,7 +72,7 @@ Documentation for filesystem implementations.
|
|||
befs
|
||||
bfs
|
||||
btrfs
|
||||
cifs/cifsroot
|
||||
cifs/index
|
||||
ceph
|
||||
coda
|
||||
configfs
|
||||
|
|
13
MAINTAINERS
13
MAINTAINERS
|
@ -4620,7 +4620,7 @@ F: include/linux/clk/
|
|||
F: include/linux/of_clk.h
|
||||
X: drivers/clk/clkdev.c
|
||||
|
||||
COMMON INTERNET FILE SYSTEM (CIFS)
|
||||
COMMON INTERNET FILE SYSTEM CLIENT (CIFS)
|
||||
M: Steve French <sfrench@samba.org>
|
||||
L: linux-cifs@vger.kernel.org
|
||||
L: samba-technical@lists.samba.org (moderated for non-subscribers)
|
||||
|
@ -10113,6 +10113,17 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git
|
|||
F: Documentation/dev-tools/kselftest*
|
||||
F: tools/testing/selftests/
|
||||
|
||||
KERNEL SMB3 SERVER (KSMBD)
|
||||
M: Namjae Jeon <linkinjeon@kernel.org>
|
||||
M: Sergey Senozhatsky <senozhatsky@chromium.org>
|
||||
M: Steve French <sfrench@samba.org>
|
||||
M: Hyunchul Lee <hyc.lee@gmail.com>
|
||||
L: linux-cifs@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.samba.org/ksmbd.git
|
||||
F: fs/cifs_common/
|
||||
F: fs/ksmbd/
|
||||
|
||||
KERNEL UNIT TESTING FRAMEWORK (KUnit)
|
||||
M: Brendan Higgins <brendanhiggins@google.com>
|
||||
L: linux-kselftest@vger.kernel.org
|
||||
|
|
|
@ -349,6 +349,7 @@ config NFS_V4_2_SSC_HELPER
|
|||
source "net/sunrpc/Kconfig"
|
||||
source "fs/ceph/Kconfig"
|
||||
source "fs/cifs/Kconfig"
|
||||
source "fs/ksmbd/Kconfig"
|
||||
source "fs/coda/Kconfig"
|
||||
source "fs/afs/Kconfig"
|
||||
source "fs/9p/Kconfig"
|
||||
|
|
|
@ -97,6 +97,7 @@ obj-$(CONFIG_NLS) += nls/
|
|||
obj-$(CONFIG_UNICODE) += unicode/
|
||||
obj-$(CONFIG_SYSV_FS) += sysv/
|
||||
obj-$(CONFIG_CIFS) += cifs/
|
||||
obj-$(CONFIG_SMB_SERVER) += ksmbd/
|
||||
obj-$(CONFIG_HPFS_FS) += hpfs/
|
||||
obj-$(CONFIG_NTFS_FS) += ntfs/
|
||||
obj-$(CONFIG_UFS_FS) += ufs/
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
config SMB_SERVER
|
||||
tristate "SMB3 server support (EXPERIMENTAL)"
|
||||
depends on INET
|
||||
depends on MULTIUSER
|
||||
depends on FILE_LOCKING
|
||||
select NLS
|
||||
select NLS_UTF8
|
||||
select CRYPTO
|
||||
select CRYPTO_MD4
|
||||
select CRYPTO_MD5
|
||||
select CRYPTO_HMAC
|
||||
select CRYPTO_ECB
|
||||
select CRYPTO_LIB_DES
|
||||
select CRYPTO_SHA256
|
||||
select CRYPTO_CMAC
|
||||
select CRYPTO_SHA512
|
||||
select CRYPTO_AEAD2
|
||||
select CRYPTO_CCM
|
||||
select CRYPTO_GCM
|
||||
select ASN1
|
||||
select OID_REGISTRY
|
||||
default n
|
||||
help
|
||||
Choose Y here if you want to allow SMB3 compliant clients
|
||||
to access files residing on this system using SMB3 protocol.
|
||||
To compile the SMB3 server support as a module,
|
||||
choose M here: the module will be called ksmbd.
|
||||
|
||||
You may choose to use a samba server instead, in which
|
||||
case you can choose N here.
|
||||
|
||||
You also need to install user space programs which can be found
|
||||
in ksmbd-tools, available from
|
||||
https://github.com/cifsd-team/ksmbd-tools.
|
||||
More detail about how to run the ksmbd kernel server is
|
||||
available via README file
|
||||
(https://github.com/cifsd-team/ksmbd-tools/blob/master/README).
|
||||
|
||||
ksmbd kernel server includes support for auto-negotiation,
|
||||
Secure negotiate, Pre-authentication integrity, oplock/lease,
|
||||
compound requests, multi-credit, packet signing, RDMA(smbdirect),
|
||||
smb3 encryption, copy-offload, secure per-user session
|
||||
establishment via NTLM or NTLMv2.
|
||||
|
||||
config SMB_SERVER_SMBDIRECT
|
||||
bool "Support for SMB Direct protocol"
|
||||
depends on SMB_SERVER=m && INFINIBAND && INFINIBAND_ADDR_TRANS || SMB_SERVER=y && INFINIBAND=y && INFINIBAND_ADDR_TRANS=y
|
||||
select SG_POOL
|
||||
default n
|
||||
|
||||
help
|
||||
Enables SMB Direct support for SMB 3.0, 3.02 and 3.1.1.
|
||||
|
||||
SMB Direct allows transferring SMB packets over RDMA. If unsure,
|
||||
say N.
|
||||
|
||||
config SMB_SERVER_CHECK_CAP_NET_ADMIN
|
||||
bool "Enable check network administration capability"
|
||||
depends on SMB_SERVER
|
||||
default y
|
||||
|
||||
help
|
||||
Prevent unprivileged processes to start the ksmbd kernel server.
|
||||
|
||||
config SMB_SERVER_KERBEROS5
|
||||
bool "Support for Kerberos 5"
|
||||
depends on SMB_SERVER
|
||||
default n
|
|
@ -0,0 +1,20 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Makefile for Linux SMB3 kernel server
|
||||
#
|
||||
obj-$(CONFIG_SMB_SERVER) += ksmbd.o
|
||||
|
||||
ksmbd-y := unicode.o auth.o vfs.o vfs_cache.o server.o ndr.o \
|
||||
misc.o oplock.o connection.o ksmbd_work.o crypto_ctx.o \
|
||||
mgmt/ksmbd_ida.o mgmt/user_config.o mgmt/share_config.o \
|
||||
mgmt/tree_connect.o mgmt/user_session.o smb_common.o \
|
||||
transport_tcp.o transport_ipc.o smbacl.o smb2pdu.o \
|
||||
smb2ops.o smb2misc.o ksmbd_spnego_negtokeninit.asn1.o \
|
||||
ksmbd_spnego_negtokentarg.asn1.o asn1.o
|
||||
|
||||
$(obj)/asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.h $(obj)/ksmbd_spnego_negtokentarg.asn1.h
|
||||
|
||||
$(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c $(obj)/ksmbd_spnego_negtokeninit.asn1.h
|
||||
$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h
|
||||
|
||||
ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o
|
|
@ -0,0 +1,343 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
|
||||
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
|
||||
*
|
||||
* Copyright (c) 2000 RP Internet (www.rpi.net.au).
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/oid_registry.h>
|
||||
|
||||
#include "glob.h"
|
||||
|
||||
#include "asn1.h"
|
||||
#include "connection.h"
|
||||
#include "auth.h"
|
||||
#include "ksmbd_spnego_negtokeninit.asn1.h"
|
||||
#include "ksmbd_spnego_negtokentarg.asn1.h"
|
||||
|
||||
#define SPNEGO_OID_LEN 7
|
||||
#define NTLMSSP_OID_LEN 10
|
||||
#define KRB5_OID_LEN 7
|
||||
#define KRB5U2U_OID_LEN 8
|
||||
#define MSKRB5_OID_LEN 7
|
||||
static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 };
|
||||
static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 };
|
||||
static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 };
|
||||
static unsigned long KRB5U2U_OID[8] = { 1, 2, 840, 113554, 1, 2, 2, 3 };
|
||||
static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 };
|
||||
|
||||
static char NTLMSSP_OID_STR[NTLMSSP_OID_LEN] = { 0x2b, 0x06, 0x01, 0x04, 0x01,
|
||||
0x82, 0x37, 0x02, 0x02, 0x0a };
|
||||
|
||||
static bool
|
||||
asn1_subid_decode(const unsigned char **begin, const unsigned char *end,
|
||||
unsigned long *subid)
|
||||
{
|
||||
const unsigned char *ptr = *begin;
|
||||
unsigned char ch;
|
||||
|
||||
*subid = 0;
|
||||
|
||||
do {
|
||||
if (ptr >= end)
|
||||
return false;
|
||||
|
||||
ch = *ptr++;
|
||||
*subid <<= 7;
|
||||
*subid |= ch & 0x7F;
|
||||
} while ((ch & 0x80) == 0x80);
|
||||
|
||||
*begin = ptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool asn1_oid_decode(const unsigned char *value, size_t vlen,
|
||||
unsigned long **oid, size_t *oidlen)
|
||||
{
|
||||
const unsigned char *iptr = value, *end = value + vlen;
|
||||
unsigned long *optr;
|
||||
unsigned long subid;
|
||||
|
||||
vlen += 1;
|
||||
if (vlen < 2 || vlen > UINT_MAX / sizeof(unsigned long))
|
||||
goto fail_nullify;
|
||||
|
||||
*oid = kmalloc(vlen * sizeof(unsigned long), GFP_KERNEL);
|
||||
if (!*oid)
|
||||
return false;
|
||||
|
||||
optr = *oid;
|
||||
|
||||
if (!asn1_subid_decode(&iptr, end, &subid))
|
||||
goto fail;
|
||||
|
||||
if (subid < 40) {
|
||||
optr[0] = 0;
|
||||
optr[1] = subid;
|
||||
} else if (subid < 80) {
|
||||
optr[0] = 1;
|
||||
optr[1] = subid - 40;
|
||||
} else {
|
||||
optr[0] = 2;
|
||||
optr[1] = subid - 80;
|
||||
}
|
||||
|
||||
*oidlen = 2;
|
||||
optr += 2;
|
||||
|
||||
while (iptr < end) {
|
||||
if (++(*oidlen) > vlen)
|
||||
goto fail;
|
||||
|
||||
if (!asn1_subid_decode(&iptr, end, optr++))
|
||||
goto fail;
|
||||
}
|
||||
return true;
|
||||
|
||||
fail:
|
||||
kfree(*oid);
|
||||
fail_nullify:
|
||||
*oid = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool oid_eq(unsigned long *oid1, unsigned int oid1len,
|
||||
unsigned long *oid2, unsigned int oid2len)
|
||||
{
|
||||
if (oid1len != oid2len)
|
||||
return false;
|
||||
|
||||
return memcmp(oid1, oid2, oid1len) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
ksmbd_decode_negTokenInit(unsigned char *security_blob, int length,
|
||||
struct ksmbd_conn *conn)
|
||||
{
|
||||
return asn1_ber_decoder(&ksmbd_spnego_negtokeninit_decoder, conn,
|
||||
security_blob, length);
|
||||
}
|
||||
|
||||
int
|
||||
ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length,
|
||||
struct ksmbd_conn *conn)
|
||||
{
|
||||
return asn1_ber_decoder(&ksmbd_spnego_negtokentarg_decoder, conn,
|
||||
security_blob, length);
|
||||
}
|
||||
|
||||
static int compute_asn_hdr_len_bytes(int len)
|
||||
{
|
||||
if (len > 0xFFFFFF)
|
||||
return 4;
|
||||
else if (len > 0xFFFF)
|
||||
return 3;
|
||||
else if (len > 0xFF)
|
||||
return 2;
|
||||
else if (len > 0x7F)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void encode_asn_tag(char *buf, unsigned int *ofs, char tag, char seq,
|
||||
int length)
|
||||
{
|
||||
int i;
|
||||
int index = *ofs;
|
||||
char hdr_len = compute_asn_hdr_len_bytes(length);
|
||||
int len = length + 2 + hdr_len;
|
||||
|
||||
/* insert tag */
|
||||
buf[index++] = tag;
|
||||
|
||||
if (!hdr_len) {
|
||||
buf[index++] = len;
|
||||
} else {
|
||||
buf[index++] = 0x80 | hdr_len;
|
||||
for (i = hdr_len - 1; i >= 0; i--)
|
||||
buf[index++] = (len >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
/* insert seq */
|
||||
len = len - (index - *ofs);
|
||||
buf[index++] = seq;
|
||||
|
||||
if (!hdr_len) {
|
||||
buf[index++] = len;
|
||||
} else {
|
||||
buf[index++] = 0x80 | hdr_len;
|
||||
for (i = hdr_len - 1; i >= 0; i--)
|
||||
buf[index++] = (len >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
*ofs += (index - *ofs);
|
||||
}
|
||||
|
||||
int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
char *ntlm_blob, int ntlm_blob_len)
|
||||
{
|
||||
char *buf;
|
||||
unsigned int ofs = 0;
|
||||
int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1;
|
||||
int oid_len = 4 + compute_asn_hdr_len_bytes(NTLMSSP_OID_LEN) * 2 +
|
||||
NTLMSSP_OID_LEN;
|
||||
int ntlmssp_len = 4 + compute_asn_hdr_len_bytes(ntlm_blob_len) * 2 +
|
||||
ntlm_blob_len;
|
||||
int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len +
|
||||
oid_len + ntlmssp_len) * 2 +
|
||||
neg_result_len + oid_len + ntlmssp_len;
|
||||
|
||||
buf = kmalloc(total_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* insert main gss header */
|
||||
encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len + oid_len +
|
||||
ntlmssp_len);
|
||||
|
||||
/* insert neg result */
|
||||
encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1);
|
||||
buf[ofs++] = 1;
|
||||
|
||||
/* insert oid */
|
||||
encode_asn_tag(buf, &ofs, 0xa1, 0x06, NTLMSSP_OID_LEN);
|
||||
memcpy(buf + ofs, NTLMSSP_OID_STR, NTLMSSP_OID_LEN);
|
||||
ofs += NTLMSSP_OID_LEN;
|
||||
|
||||
/* insert response token - ntlmssp blob */
|
||||
encode_asn_tag(buf, &ofs, 0xa2, 0x04, ntlm_blob_len);
|
||||
memcpy(buf + ofs, ntlm_blob, ntlm_blob_len);
|
||||
ofs += ntlm_blob_len;
|
||||
|
||||
*pbuffer = buf;
|
||||
*buflen = total_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
int neg_result)
|
||||
{
|
||||
char *buf;
|
||||
unsigned int ofs = 0;
|
||||
int neg_result_len = 4 + compute_asn_hdr_len_bytes(1) * 2 + 1;
|
||||
int total_len = 4 + compute_asn_hdr_len_bytes(neg_result_len) * 2 +
|
||||
neg_result_len;
|
||||
|
||||
buf = kmalloc(total_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
/* insert main gss header */
|
||||
encode_asn_tag(buf, &ofs, 0xa1, 0x30, neg_result_len);
|
||||
|
||||
/* insert neg result */
|
||||
encode_asn_tag(buf, &ofs, 0xa0, 0x0a, 1);
|
||||
if (neg_result)
|
||||
buf[ofs++] = 2;
|
||||
else
|
||||
buf[ofs++] = 0;
|
||||
|
||||
*pbuffer = buf;
|
||||
*buflen = total_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_gssapi_this_mech(void *context, size_t hdrlen, unsigned char tag,
|
||||
const void *value, size_t vlen)
|
||||
{
|
||||
unsigned long *oid;
|
||||
size_t oidlen;
|
||||
int err = 0;
|
||||
|
||||
if (!asn1_oid_decode(value, vlen, &oid, &oidlen)) {
|
||||
err = -EBADMSG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!oid_eq(oid, oidlen, SPNEGO_OID, SPNEGO_OID_LEN))
|
||||
err = -EBADMSG;
|
||||
kfree(oid);
|
||||
out:
|
||||
if (err) {
|
||||
char buf[50];
|
||||
|
||||
sprint_oid(value, vlen, buf, sizeof(buf));
|
||||
ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int ksmbd_neg_token_init_mech_type(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
unsigned long *oid;
|
||||
size_t oidlen;
|
||||
int mech_type;
|
||||
char buf[50];
|
||||
|
||||
if (!asn1_oid_decode(value, vlen, &oid, &oidlen))
|
||||
goto fail;
|
||||
|
||||
if (oid_eq(oid, oidlen, NTLMSSP_OID, NTLMSSP_OID_LEN))
|
||||
mech_type = KSMBD_AUTH_NTLMSSP;
|
||||
else if (oid_eq(oid, oidlen, MSKRB5_OID, MSKRB5_OID_LEN))
|
||||
mech_type = KSMBD_AUTH_MSKRB5;
|
||||
else if (oid_eq(oid, oidlen, KRB5_OID, KRB5_OID_LEN))
|
||||
mech_type = KSMBD_AUTH_KRB5;
|
||||
else if (oid_eq(oid, oidlen, KRB5U2U_OID, KRB5U2U_OID_LEN))
|
||||
mech_type = KSMBD_AUTH_KRB5U2U;
|
||||
else
|
||||
goto fail;
|
||||
|
||||
conn->auth_mechs |= mech_type;
|
||||
if (conn->preferred_auth_mech == 0)
|
||||
conn->preferred_auth_mech = mech_type;
|
||||
|
||||
kfree(oid);
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
kfree(oid);
|
||||
sprint_oid(value, vlen, buf, sizeof(buf));
|
||||
ksmbd_debug(AUTH, "Unexpected OID: %s\n", buf);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
int ksmbd_neg_token_init_mech_token(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
|
||||
conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
|
||||
if (!conn->mechToken)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(conn->mechToken, value, vlen);
|
||||
conn->mechToken[vlen] = '\0';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_neg_token_targ_resp_token(void *context, size_t hdrlen,
|
||||
unsigned char tag, const void *value,
|
||||
size_t vlen)
|
||||
{
|
||||
struct ksmbd_conn *conn = context;
|
||||
|
||||
conn->mechToken = kmalloc(vlen + 1, GFP_KERNEL);
|
||||
if (!conn->mechToken)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(conn->mechToken, value, vlen);
|
||||
conn->mechToken[vlen] = '\0';
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* The ASB.1/BER parsing code is derived from ip_nat_snmp_basic.c which was in
|
||||
* turn derived from the gxsnmp package by Gregory McLean & Jochen Friedrich
|
||||
*
|
||||
* Copyright (c) 2000 RP Internet (www.rpi.net.au).
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __ASN1_H__
|
||||
#define __ASN1_H__
|
||||
|
||||
int ksmbd_decode_negTokenInit(unsigned char *security_blob, int length,
|
||||
struct ksmbd_conn *conn);
|
||||
int ksmbd_decode_negTokenTarg(unsigned char *security_blob, int length,
|
||||
struct ksmbd_conn *conn);
|
||||
int build_spnego_ntlmssp_neg_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
char *ntlm_blob, int ntlm_blob_len);
|
||||
int build_spnego_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
|
||||
int neg_result);
|
||||
#endif /* __ASN1_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __AUTH_H__
|
||||
#define __AUTH_H__
|
||||
|
||||
#include "ntlmssp.h"
|
||||
|
||||
#ifdef CONFIG_SMB_SERVER_KERBEROS5
|
||||
#define AUTH_GSS_LENGTH 96
|
||||
#define AUTH_GSS_PADDING 0
|
||||
#else
|
||||
#define AUTH_GSS_LENGTH 74
|
||||
#define AUTH_GSS_PADDING 6
|
||||
#endif
|
||||
|
||||
#define CIFS_HMAC_MD5_HASH_SIZE (16)
|
||||
#define CIFS_NTHASH_SIZE (16)
|
||||
|
||||
/*
|
||||
* Size of the ntlm client response
|
||||
*/
|
||||
#define CIFS_AUTH_RESP_SIZE 24
|
||||
#define CIFS_SMB1_SIGNATURE_SIZE 8
|
||||
#define CIFS_SMB1_SESSKEY_SIZE 16
|
||||
|
||||
#define KSMBD_AUTH_NTLMSSP 0x0001
|
||||
#define KSMBD_AUTH_KRB5 0x0002
|
||||
#define KSMBD_AUTH_MSKRB5 0x0004
|
||||
#define KSMBD_AUTH_KRB5U2U 0x0008
|
||||
|
||||
struct ksmbd_session;
|
||||
struct ksmbd_conn;
|
||||
struct kvec;
|
||||
|
||||
int ksmbd_crypt_message(struct ksmbd_conn *conn, struct kvec *iov,
|
||||
unsigned int nvec, int enc);
|
||||
void ksmbd_copy_gss_neg_header(void *buf);
|
||||
int ksmbd_auth_ntlm(struct ksmbd_session *sess, char *pw_buf);
|
||||
int ksmbd_auth_ntlmv2(struct ksmbd_session *sess, struct ntlmv2_resp *ntlmv2,
|
||||
int blen, char *domain_name);
|
||||
int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
|
||||
int blob_len, struct ksmbd_session *sess);
|
||||
int ksmbd_decode_ntlmssp_neg_blob(struct negotiate_message *negblob,
|
||||
int blob_len, struct ksmbd_session *sess);
|
||||
unsigned int
|
||||
ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
|
||||
struct ksmbd_session *sess);
|
||||
int ksmbd_krb5_authenticate(struct ksmbd_session *sess, char *in_blob,
|
||||
int in_len, char *out_blob, int *out_len);
|
||||
int ksmbd_sign_smb2_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig);
|
||||
int ksmbd_sign_smb3_pdu(struct ksmbd_conn *conn, char *key, struct kvec *iov,
|
||||
int n_vec, char *sig);
|
||||
int ksmbd_gen_smb30_signingkey(struct ksmbd_session *sess,
|
||||
struct ksmbd_conn *conn);
|
||||
int ksmbd_gen_smb311_signingkey(struct ksmbd_session *sess,
|
||||
struct ksmbd_conn *conn);
|
||||
int ksmbd_gen_smb30_encryptionkey(struct ksmbd_session *sess);
|
||||
int ksmbd_gen_smb311_encryptionkey(struct ksmbd_session *sess);
|
||||
int ksmbd_gen_preauth_integrity_hash(struct ksmbd_conn *conn, char *buf,
|
||||
__u8 *pi_hash);
|
||||
int ksmbd_gen_sd_hash(struct ksmbd_conn *conn, char *sd_buf, int len,
|
||||
__u8 *pi_hash);
|
||||
#endif
|
|
@ -0,0 +1,413 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <namjae.jeon@protocolfreedom.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "smb_common.h"
|
||||
#include "mgmt/ksmbd_ida.h"
|
||||
#include "connection.h"
|
||||
#include "transport_tcp.h"
|
||||
#include "transport_rdma.h"
|
||||
|
||||
static DEFINE_MUTEX(init_lock);
|
||||
|
||||
static struct ksmbd_conn_ops default_conn_ops;
|
||||
|
||||
LIST_HEAD(conn_list);
|
||||
DEFINE_RWLOCK(conn_list_lock);
|
||||
|
||||
/**
|
||||
* ksmbd_conn_free() - free resources of the connection instance
|
||||
*
|
||||
* @conn: connection instance to be cleand up
|
||||
*
|
||||
* During the thread termination, the corresponding conn instance
|
||||
* resources(sock/memory) are released and finally the conn object is freed.
|
||||
*/
|
||||
void ksmbd_conn_free(struct ksmbd_conn *conn)
|
||||
{
|
||||
write_lock(&conn_list_lock);
|
||||
list_del(&conn->conns_list);
|
||||
write_unlock(&conn_list_lock);
|
||||
|
||||
kvfree(conn->request_buf);
|
||||
kfree(conn->preauth_info);
|
||||
kfree(conn);
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_conn_alloc() - initialize a new connection instance
|
||||
*
|
||||
* Return: ksmbd_conn struct on success, otherwise NULL
|
||||
*/
|
||||
struct ksmbd_conn *ksmbd_conn_alloc(void)
|
||||
{
|
||||
struct ksmbd_conn *conn;
|
||||
|
||||
conn = kzalloc(sizeof(struct ksmbd_conn), GFP_KERNEL);
|
||||
if (!conn)
|
||||
return NULL;
|
||||
|
||||
conn->need_neg = true;
|
||||
conn->status = KSMBD_SESS_NEW;
|
||||
conn->local_nls = load_nls("utf8");
|
||||
if (!conn->local_nls)
|
||||
conn->local_nls = load_nls_default();
|
||||
atomic_set(&conn->req_running, 0);
|
||||
atomic_set(&conn->r_count, 0);
|
||||
init_waitqueue_head(&conn->req_running_q);
|
||||
INIT_LIST_HEAD(&conn->conns_list);
|
||||
INIT_LIST_HEAD(&conn->sessions);
|
||||
INIT_LIST_HEAD(&conn->requests);
|
||||
INIT_LIST_HEAD(&conn->async_requests);
|
||||
spin_lock_init(&conn->request_lock);
|
||||
spin_lock_init(&conn->credits_lock);
|
||||
ida_init(&conn->async_ida);
|
||||
|
||||
spin_lock_init(&conn->llist_lock);
|
||||
INIT_LIST_HEAD(&conn->lock_list);
|
||||
|
||||
write_lock(&conn_list_lock);
|
||||
list_add(&conn->conns_list, &conn_list);
|
||||
write_unlock(&conn_list_lock);
|
||||
return conn;
|
||||
}
|
||||
|
||||
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c)
|
||||
{
|
||||
struct ksmbd_conn *t;
|
||||
bool ret = false;
|
||||
|
||||
read_lock(&conn_list_lock);
|
||||
list_for_each_entry(t, &conn_list, conns_list) {
|
||||
if (memcmp(t->ClientGUID, c->ClientGUID, SMB2_CLIENT_GUID_SIZE))
|
||||
continue;
|
||||
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
read_unlock(&conn_list_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ksmbd_conn_enqueue_request(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct list_head *requests_queue = NULL;
|
||||
|
||||
if (conn->ops->get_cmd_val(work) != SMB2_CANCEL_HE) {
|
||||
requests_queue = &conn->requests;
|
||||
work->syncronous = true;
|
||||
}
|
||||
|
||||
if (requests_queue) {
|
||||
atomic_inc(&conn->req_running);
|
||||
spin_lock(&conn->request_lock);
|
||||
list_add_tail(&work->request_entry, requests_queue);
|
||||
spin_unlock(&conn->request_lock);
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
int ret = 1;
|
||||
|
||||
if (list_empty(&work->request_entry) &&
|
||||
list_empty(&work->async_request_entry))
|
||||
return 0;
|
||||
|
||||
if (!work->multiRsp)
|
||||
atomic_dec(&conn->req_running);
|
||||
spin_lock(&conn->request_lock);
|
||||
if (!work->multiRsp) {
|
||||
list_del_init(&work->request_entry);
|
||||
if (work->syncronous == false)
|
||||
list_del_init(&work->async_request_entry);
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock(&conn->request_lock);
|
||||
|
||||
wake_up_all(&conn->req_running_q);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ksmbd_conn_lock(struct ksmbd_conn *conn)
|
||||
{
|
||||
mutex_lock(&conn->srv_mutex);
|
||||
}
|
||||
|
||||
static void ksmbd_conn_unlock(struct ksmbd_conn *conn)
|
||||
{
|
||||
mutex_unlock(&conn->srv_mutex);
|
||||
}
|
||||
|
||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn)
|
||||
{
|
||||
wait_event(conn->req_running_q, atomic_read(&conn->req_running) < 2);
|
||||
}
|
||||
|
||||
int ksmbd_conn_write(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct smb_hdr *rsp_hdr = work->response_buf;
|
||||
size_t len = 0;
|
||||
int sent;
|
||||
struct kvec iov[3];
|
||||
int iov_idx = 0;
|
||||
|
||||
ksmbd_conn_try_dequeue_request(work);
|
||||
if (!rsp_hdr) {
|
||||
pr_err("NULL response header\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (work->tr_buf) {
|
||||
iov[iov_idx] = (struct kvec) { work->tr_buf,
|
||||
sizeof(struct smb2_transform_hdr) };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
}
|
||||
|
||||
if (work->aux_payload_sz) {
|
||||
iov[iov_idx] = (struct kvec) { rsp_hdr, work->resp_hdr_sz };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
iov[iov_idx] = (struct kvec) { work->aux_payload_buf, work->aux_payload_sz };
|
||||
len += iov[iov_idx++].iov_len;
|
||||
} else {
|
||||
if (work->tr_buf)
|
||||
iov[iov_idx].iov_len = work->resp_hdr_sz;
|
||||
else
|
||||
iov[iov_idx].iov_len = get_rfc1002_len(rsp_hdr) + 4;
|
||||
iov[iov_idx].iov_base = rsp_hdr;
|
||||
len += iov[iov_idx++].iov_len;
|
||||
}
|
||||
|
||||
ksmbd_conn_lock(conn);
|
||||
sent = conn->transport->ops->writev(conn->transport, &iov[0],
|
||||
iov_idx, len,
|
||||
work->need_invalidate_rkey,
|
||||
work->remote_key);
|
||||
ksmbd_conn_unlock(conn);
|
||||
|
||||
if (sent < 0) {
|
||||
pr_err("Failed to send message: %d\n", sent);
|
||||
return sent;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf,
|
||||
unsigned int buflen, u32 remote_key, u64 remote_offset,
|
||||
u32 remote_len)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (conn->transport->ops->rdma_read)
|
||||
ret = conn->transport->ops->rdma_read(conn->transport,
|
||||
buf, buflen,
|
||||
remote_key, remote_offset,
|
||||
remote_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf,
|
||||
unsigned int buflen, u32 remote_key,
|
||||
u64 remote_offset, u32 remote_len)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (conn->transport->ops->rdma_write)
|
||||
ret = conn->transport->ops->rdma_write(conn->transport,
|
||||
buf, buflen,
|
||||
remote_key, remote_offset,
|
||||
remote_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ksmbd_conn_alive(struct ksmbd_conn *conn)
|
||||
{
|
||||
if (!ksmbd_server_running())
|
||||
return false;
|
||||
|
||||
if (conn->status == KSMBD_SESS_EXITING)
|
||||
return false;
|
||||
|
||||
if (kthread_should_stop())
|
||||
return false;
|
||||
|
||||
if (atomic_read(&conn->stats.open_files_count) > 0)
|
||||
return true;
|
||||
|
||||
/*
|
||||
* Stop current session if the time that get last request from client
|
||||
* is bigger than deadtime user configured and opening file count is
|
||||
* zero.
|
||||
*/
|
||||
if (server_conf.deadtime > 0 &&
|
||||
time_after(jiffies, conn->last_active + server_conf.deadtime)) {
|
||||
ksmbd_debug(CONN, "No response from client in %lu minutes\n",
|
||||
server_conf.deadtime / SMB_ECHO_INTERVAL);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_conn_handler_loop() - session thread to listen on new smb requests
|
||||
* @p: connection instance
|
||||
*
|
||||
* One thread each per connection
|
||||
*
|
||||
* Return: 0 on success
|
||||
*/
|
||||
int ksmbd_conn_handler_loop(void *p)
|
||||
{
|
||||
struct ksmbd_conn *conn = (struct ksmbd_conn *)p;
|
||||
struct ksmbd_transport *t = conn->transport;
|
||||
unsigned int pdu_size;
|
||||
char hdr_buf[4] = {0,};
|
||||
int size;
|
||||
|
||||
mutex_init(&conn->srv_mutex);
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
if (t->ops->prepare && t->ops->prepare(t))
|
||||
goto out;
|
||||
|
||||
conn->last_active = jiffies;
|
||||
while (ksmbd_conn_alive(conn)) {
|
||||
if (try_to_freeze())
|
||||
continue;
|
||||
|
||||
kvfree(conn->request_buf);
|
||||
conn->request_buf = NULL;
|
||||
|
||||
size = t->ops->read(t, hdr_buf, sizeof(hdr_buf));
|
||||
if (size != sizeof(hdr_buf))
|
||||
break;
|
||||
|
||||
pdu_size = get_rfc1002_len(hdr_buf);
|
||||
ksmbd_debug(CONN, "RFC1002 header %u bytes\n", pdu_size);
|
||||
|
||||
/* make sure we have enough to get to SMB header end */
|
||||
if (!ksmbd_pdu_size_has_room(pdu_size)) {
|
||||
ksmbd_debug(CONN, "SMB request too short (%u bytes)\n",
|
||||
pdu_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 4 for rfc1002 length field */
|
||||
size = pdu_size + 4;
|
||||
conn->request_buf = kvmalloc(size, GFP_KERNEL);
|
||||
if (!conn->request_buf)
|
||||
continue;
|
||||
|
||||
memcpy(conn->request_buf, hdr_buf, sizeof(hdr_buf));
|
||||
if (!ksmbd_smb_request(conn))
|
||||
break;
|
||||
|
||||
/*
|
||||
* We already read 4 bytes to find out PDU size, now
|
||||
* read in PDU
|
||||
*/
|
||||
size = t->ops->read(t, conn->request_buf + 4, pdu_size);
|
||||
if (size < 0) {
|
||||
pr_err("sock_read failed: %d\n", size);
|
||||
break;
|
||||
}
|
||||
|
||||
if (size != pdu_size) {
|
||||
pr_err("PDU error. Read: %d, Expected: %d\n",
|
||||
size, pdu_size);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!default_conn_ops.process_fn) {
|
||||
pr_err("No connection request callback\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (default_conn_ops.process_fn(conn)) {
|
||||
pr_err("Cannot handle request\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
/* Wait till all reference dropped to the Server object*/
|
||||
while (atomic_read(&conn->r_count) > 0)
|
||||
schedule_timeout(HZ);
|
||||
|
||||
unload_nls(conn->local_nls);
|
||||
if (default_conn_ops.terminate_fn)
|
||||
default_conn_ops.terminate_fn(conn);
|
||||
t->ops->disconnect(t);
|
||||
module_put(THIS_MODULE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops)
|
||||
{
|
||||
default_conn_ops.process_fn = ops->process_fn;
|
||||
default_conn_ops.terminate_fn = ops->terminate_fn;
|
||||
}
|
||||
|
||||
int ksmbd_conn_transport_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&init_lock);
|
||||
ret = ksmbd_tcp_init();
|
||||
if (ret) {
|
||||
pr_err("Failed to init TCP subsystem: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ksmbd_rdma_init();
|
||||
if (ret) {
|
||||
pr_err("Failed to init RDMA subsystem: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&init_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void stop_sessions(void)
|
||||
{
|
||||
struct ksmbd_conn *conn;
|
||||
|
||||
again:
|
||||
read_lock(&conn_list_lock);
|
||||
list_for_each_entry(conn, &conn_list, conns_list) {
|
||||
struct task_struct *task;
|
||||
|
||||
task = conn->transport->handler;
|
||||
if (task)
|
||||
ksmbd_debug(CONN, "Stop session handler %s/%d\n",
|
||||
task->comm, task_pid_nr(task));
|
||||
conn->status = KSMBD_SESS_EXITING;
|
||||
}
|
||||
read_unlock(&conn_list_lock);
|
||||
|
||||
if (!list_empty(&conn_list)) {
|
||||
schedule_timeout_interruptible(HZ / 10); /* 100ms */
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void ksmbd_conn_transport_destroy(void)
|
||||
{
|
||||
mutex_lock(&init_lock);
|
||||
ksmbd_tcp_destroy();
|
||||
ksmbd_rdma_destroy();
|
||||
stop_sessions();
|
||||
mutex_unlock(&init_lock);
|
||||
}
|
|
@ -0,0 +1,213 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_CONNECTION_H__
|
||||
#define __KSMBD_CONNECTION_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/ip.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/tcp.h>
|
||||
#include <net/inet_connection_sock.h>
|
||||
#include <net/request_sock.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#include "smb_common.h"
|
||||
#include "ksmbd_work.h"
|
||||
|
||||
#define KSMBD_SOCKET_BACKLOG 16
|
||||
|
||||
/*
|
||||
* WARNING
|
||||
*
|
||||
* This is nothing but a HACK. Session status should move to channel
|
||||
* or to session. As of now we have 1 tcp_conn : 1 ksmbd_session, but
|
||||
* we need to change it to 1 tcp_conn : N ksmbd_sessions.
|
||||
*/
|
||||
enum {
|
||||
KSMBD_SESS_NEW = 0,
|
||||
KSMBD_SESS_GOOD,
|
||||
KSMBD_SESS_EXITING,
|
||||
KSMBD_SESS_NEED_RECONNECT,
|
||||
KSMBD_SESS_NEED_NEGOTIATE
|
||||
};
|
||||
|
||||
struct ksmbd_stats {
|
||||
atomic_t open_files_count;
|
||||
atomic64_t request_served;
|
||||
};
|
||||
|
||||
struct ksmbd_transport;
|
||||
|
||||
struct ksmbd_conn {
|
||||
struct smb_version_values *vals;
|
||||
struct smb_version_ops *ops;
|
||||
struct smb_version_cmds *cmds;
|
||||
unsigned int max_cmds;
|
||||
struct mutex srv_mutex;
|
||||
int status;
|
||||
unsigned int cli_cap;
|
||||
char *request_buf;
|
||||
struct ksmbd_transport *transport;
|
||||
struct nls_table *local_nls;
|
||||
struct list_head conns_list;
|
||||
/* smb session 1 per user */
|
||||
struct list_head sessions;
|
||||
unsigned long last_active;
|
||||
/* How many request are running currently */
|
||||
atomic_t req_running;
|
||||
/* References which are made for this Server object*/
|
||||
atomic_t r_count;
|
||||
unsigned short total_credits;
|
||||
unsigned short max_credits;
|
||||
spinlock_t credits_lock;
|
||||
wait_queue_head_t req_running_q;
|
||||
/* Lock to protect requests list*/
|
||||
spinlock_t request_lock;
|
||||
struct list_head requests;
|
||||
struct list_head async_requests;
|
||||
int connection_type;
|
||||
struct ksmbd_stats stats;
|
||||
char ClientGUID[SMB2_CLIENT_GUID_SIZE];
|
||||
union {
|
||||
/* pending trans request table */
|
||||
struct trans_state *recent_trans;
|
||||
/* Used by ntlmssp */
|
||||
char *ntlmssp_cryptkey;
|
||||
};
|
||||
|
||||
spinlock_t llist_lock;
|
||||
struct list_head lock_list;
|
||||
|
||||
struct preauth_integrity_info *preauth_info;
|
||||
|
||||
bool need_neg;
|
||||
unsigned int auth_mechs;
|
||||
unsigned int preferred_auth_mech;
|
||||
bool sign;
|
||||
bool use_spnego:1;
|
||||
__u16 cli_sec_mode;
|
||||
__u16 srv_sec_mode;
|
||||
/* dialect index that server chose */
|
||||
__u16 dialect;
|
||||
|
||||
char *mechToken;
|
||||
|
||||
struct ksmbd_conn_ops *conn_ops;
|
||||
|
||||
/* Preauth Session Table */
|
||||
struct list_head preauth_sess_table;
|
||||
|
||||
struct sockaddr_storage peer_addr;
|
||||
|
||||
/* Identifier for async message */
|
||||
struct ida async_ida;
|
||||
|
||||
__le16 cipher_type;
|
||||
__le16 compress_algorithm;
|
||||
bool posix_ext_supported;
|
||||
bool signing_negotiated;
|
||||
__le16 signing_algorithm;
|
||||
bool binding;
|
||||
};
|
||||
|
||||
struct ksmbd_conn_ops {
|
||||
int (*process_fn)(struct ksmbd_conn *conn);
|
||||
int (*terminate_fn)(struct ksmbd_conn *conn);
|
||||
};
|
||||
|
||||
struct ksmbd_transport_ops {
|
||||
int (*prepare)(struct ksmbd_transport *t);
|
||||
void (*disconnect)(struct ksmbd_transport *t);
|
||||
int (*read)(struct ksmbd_transport *t, char *buf, unsigned int size);
|
||||
int (*writev)(struct ksmbd_transport *t, struct kvec *iovs, int niov,
|
||||
int size, bool need_invalidate_rkey,
|
||||
unsigned int remote_key);
|
||||
int (*rdma_read)(struct ksmbd_transport *t, void *buf, unsigned int len,
|
||||
u32 remote_key, u64 remote_offset, u32 remote_len);
|
||||
int (*rdma_write)(struct ksmbd_transport *t, void *buf,
|
||||
unsigned int len, u32 remote_key, u64 remote_offset,
|
||||
u32 remote_len);
|
||||
};
|
||||
|
||||
struct ksmbd_transport {
|
||||
struct ksmbd_conn *conn;
|
||||
struct ksmbd_transport_ops *ops;
|
||||
struct task_struct *handler;
|
||||
};
|
||||
|
||||
#define KSMBD_TCP_RECV_TIMEOUT (7 * HZ)
|
||||
#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)
|
||||
#define KSMBD_TCP_PEER_SOCKADDR(c) ((struct sockaddr *)&((c)->peer_addr))
|
||||
|
||||
extern struct list_head conn_list;
|
||||
extern rwlock_t conn_list_lock;
|
||||
|
||||
bool ksmbd_conn_alive(struct ksmbd_conn *conn);
|
||||
void ksmbd_conn_wait_idle(struct ksmbd_conn *conn);
|
||||
struct ksmbd_conn *ksmbd_conn_alloc(void);
|
||||
void ksmbd_conn_free(struct ksmbd_conn *conn);
|
||||
bool ksmbd_conn_lookup_dialect(struct ksmbd_conn *c);
|
||||
int ksmbd_conn_write(struct ksmbd_work *work);
|
||||
int ksmbd_conn_rdma_read(struct ksmbd_conn *conn, void *buf,
|
||||
unsigned int buflen, u32 remote_key, u64 remote_offset,
|
||||
u32 remote_len);
|
||||
int ksmbd_conn_rdma_write(struct ksmbd_conn *conn, void *buf,
|
||||
unsigned int buflen, u32 remote_key, u64 remote_offset,
|
||||
u32 remote_len);
|
||||
void ksmbd_conn_enqueue_request(struct ksmbd_work *work);
|
||||
int ksmbd_conn_try_dequeue_request(struct ksmbd_work *work);
|
||||
void ksmbd_conn_init_server_callbacks(struct ksmbd_conn_ops *ops);
|
||||
int ksmbd_conn_handler_loop(void *p);
|
||||
int ksmbd_conn_transport_init(void);
|
||||
void ksmbd_conn_transport_destroy(void);
|
||||
|
||||
/*
|
||||
* WARNING
|
||||
*
|
||||
* This is a hack. We will move status to a proper place once we land
|
||||
* a multi-sessions support.
|
||||
*/
|
||||
static inline bool ksmbd_conn_good(struct ksmbd_work *work)
|
||||
{
|
||||
return work->conn->status == KSMBD_SESS_GOOD;
|
||||
}
|
||||
|
||||
static inline bool ksmbd_conn_need_negotiate(struct ksmbd_work *work)
|
||||
{
|
||||
return work->conn->status == KSMBD_SESS_NEED_NEGOTIATE;
|
||||
}
|
||||
|
||||
static inline bool ksmbd_conn_need_reconnect(struct ksmbd_work *work)
|
||||
{
|
||||
return work->conn->status == KSMBD_SESS_NEED_RECONNECT;
|
||||
}
|
||||
|
||||
static inline bool ksmbd_conn_exiting(struct ksmbd_work *work)
|
||||
{
|
||||
return work->conn->status == KSMBD_SESS_EXITING;
|
||||
}
|
||||
|
||||
static inline void ksmbd_conn_set_good(struct ksmbd_work *work)
|
||||
{
|
||||
work->conn->status = KSMBD_SESS_GOOD;
|
||||
}
|
||||
|
||||
static inline void ksmbd_conn_set_need_negotiate(struct ksmbd_work *work)
|
||||
{
|
||||
work->conn->status = KSMBD_SESS_NEED_NEGOTIATE;
|
||||
}
|
||||
|
||||
static inline void ksmbd_conn_set_need_reconnect(struct ksmbd_work *work)
|
||||
{
|
||||
work->conn->status = KSMBD_SESS_NEED_RECONNECT;
|
||||
}
|
||||
|
||||
static inline void ksmbd_conn_set_exiting(struct ksmbd_work *work)
|
||||
{
|
||||
work->conn->status = KSMBD_SESS_EXITING;
|
||||
}
|
||||
#endif /* __CONNECTION_H__ */
|
|
@ -0,0 +1,282 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "crypto_ctx.h"
|
||||
|
||||
struct crypto_ctx_list {
|
||||
spinlock_t ctx_lock;
|
||||
int avail_ctx;
|
||||
struct list_head idle_ctx;
|
||||
wait_queue_head_t ctx_wait;
|
||||
};
|
||||
|
||||
static struct crypto_ctx_list ctx_list;
|
||||
|
||||
static inline void free_aead(struct crypto_aead *aead)
|
||||
{
|
||||
if (aead)
|
||||
crypto_free_aead(aead);
|
||||
}
|
||||
|
||||
static void free_shash(struct shash_desc *shash)
|
||||
{
|
||||
if (shash) {
|
||||
crypto_free_shash(shash->tfm);
|
||||
kfree(shash);
|
||||
}
|
||||
}
|
||||
|
||||
static struct crypto_aead *alloc_aead(int id)
|
||||
{
|
||||
struct crypto_aead *tfm = NULL;
|
||||
|
||||
switch (id) {
|
||||
case CRYPTO_AEAD_AES_GCM:
|
||||
tfm = crypto_alloc_aead("gcm(aes)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_AEAD_AES_CCM:
|
||||
tfm = crypto_alloc_aead("ccm(aes)", 0, 0);
|
||||
break;
|
||||
default:
|
||||
pr_err("Does not support encrypt ahead(id : %d)\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR(tfm)) {
|
||||
pr_err("Failed to alloc encrypt aead : %ld\n", PTR_ERR(tfm));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return tfm;
|
||||
}
|
||||
|
||||
static struct shash_desc *alloc_shash_desc(int id)
|
||||
{
|
||||
struct crypto_shash *tfm = NULL;
|
||||
struct shash_desc *shash;
|
||||
|
||||
switch (id) {
|
||||
case CRYPTO_SHASH_HMACMD5:
|
||||
tfm = crypto_alloc_shash("hmac(md5)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_HMACSHA256:
|
||||
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_CMACAES:
|
||||
tfm = crypto_alloc_shash("cmac(aes)", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_SHA256:
|
||||
tfm = crypto_alloc_shash("sha256", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_SHA512:
|
||||
tfm = crypto_alloc_shash("sha512", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_MD4:
|
||||
tfm = crypto_alloc_shash("md4", 0, 0);
|
||||
break;
|
||||
case CRYPTO_SHASH_MD5:
|
||||
tfm = crypto_alloc_shash("md5", 0, 0);
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IS_ERR(tfm))
|
||||
return NULL;
|
||||
|
||||
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
|
||||
GFP_KERNEL);
|
||||
if (!shash)
|
||||
crypto_free_shash(tfm);
|
||||
else
|
||||
shash->tfm = tfm;
|
||||
return shash;
|
||||
}
|
||||
|
||||
static void ctx_free(struct ksmbd_crypto_ctx *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CRYPTO_SHASH_MAX; i++)
|
||||
free_shash(ctx->desc[i]);
|
||||
for (i = 0; i < CRYPTO_AEAD_MAX; i++)
|
||||
free_aead(ctx->ccmaes[i]);
|
||||
kfree(ctx);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *ksmbd_find_crypto_ctx(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
while (1) {
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
if (!list_empty(&ctx_list.idle_ctx)) {
|
||||
ctx = list_entry(ctx_list.idle_ctx.next,
|
||||
struct ksmbd_crypto_ctx,
|
||||
list);
|
||||
list_del(&ctx->list);
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
if (ctx_list.avail_ctx > num_online_cpus()) {
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wait_event(ctx_list.ctx_wait,
|
||||
!list_empty(&ctx_list.idle_ctx));
|
||||
continue;
|
||||
}
|
||||
|
||||
ctx_list.avail_ctx++;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
|
||||
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
ctx_list.avail_ctx--;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wait_event(ctx_list.ctx_wait,
|
||||
!list_empty(&ctx_list.idle_ctx));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx)
|
||||
{
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
spin_lock(&ctx_list.ctx_lock);
|
||||
if (ctx_list.avail_ctx <= num_online_cpus()) {
|
||||
list_add(&ctx->list, &ctx_list.idle_ctx);
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
wake_up(&ctx_list.ctx_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx_list.avail_ctx--;
|
||||
spin_unlock(&ctx_list.ctx_lock);
|
||||
ctx_free(ctx);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_shash_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
if (id >= CRYPTO_SHASH_MAX)
|
||||
return NULL;
|
||||
|
||||
ctx = ksmbd_find_crypto_ctx();
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
|
||||
ctx->desc[id] = alloc_shash_desc(id);
|
||||
if (ctx->desc[id])
|
||||
return ctx;
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACMD5);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_HMACSHA256);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_CMACAES);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA256);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_SHA512);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD4);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void)
|
||||
{
|
||||
return ____crypto_shash_ctx_find(CRYPTO_SHASH_MD5);
|
||||
}
|
||||
|
||||
static struct ksmbd_crypto_ctx *____crypto_aead_ctx_find(int id)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
if (id >= CRYPTO_AEAD_MAX)
|
||||
return NULL;
|
||||
|
||||
ctx = ksmbd_find_crypto_ctx();
|
||||
if (ctx->ccmaes[id])
|
||||
return ctx;
|
||||
|
||||
ctx->ccmaes[id] = alloc_aead(id);
|
||||
if (ctx->ccmaes[id])
|
||||
return ctx;
|
||||
ksmbd_release_crypto_ctx(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void)
|
||||
{
|
||||
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_GCM);
|
||||
}
|
||||
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void)
|
||||
{
|
||||
return ____crypto_aead_ctx_find(CRYPTO_AEAD_AES_CCM);
|
||||
}
|
||||
|
||||
void ksmbd_crypto_destroy(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
while (!list_empty(&ctx_list.idle_ctx)) {
|
||||
ctx = list_entry(ctx_list.idle_ctx.next,
|
||||
struct ksmbd_crypto_ctx,
|
||||
list);
|
||||
list_del(&ctx->list);
|
||||
ctx_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_crypto_create(void)
|
||||
{
|
||||
struct ksmbd_crypto_ctx *ctx;
|
||||
|
||||
spin_lock_init(&ctx_list.ctx_lock);
|
||||
INIT_LIST_HEAD(&ctx_list.idle_ctx);
|
||||
init_waitqueue_head(&ctx_list.ctx_wait);
|
||||
ctx_list.avail_ctx = 1;
|
||||
|
||||
ctx = kzalloc(sizeof(struct ksmbd_crypto_ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
list_add(&ctx->list, &ctx_list.idle_ctx);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __CRYPTO_CTX_H__
|
||||
#define __CRYPTO_CTX_H__
|
||||
|
||||
#include <crypto/hash.h>
|
||||
#include <crypto/aead.h>
|
||||
|
||||
enum {
|
||||
CRYPTO_SHASH_HMACMD5 = 0,
|
||||
CRYPTO_SHASH_HMACSHA256,
|
||||
CRYPTO_SHASH_CMACAES,
|
||||
CRYPTO_SHASH_SHA256,
|
||||
CRYPTO_SHASH_SHA512,
|
||||
CRYPTO_SHASH_MD4,
|
||||
CRYPTO_SHASH_MD5,
|
||||
CRYPTO_SHASH_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTO_AEAD_AES_GCM = 16,
|
||||
CRYPTO_AEAD_AES_CCM,
|
||||
CRYPTO_AEAD_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
CRYPTO_BLK_ECBDES = 32,
|
||||
CRYPTO_BLK_MAX,
|
||||
};
|
||||
|
||||
struct ksmbd_crypto_ctx {
|
||||
struct list_head list;
|
||||
|
||||
struct shash_desc *desc[CRYPTO_SHASH_MAX];
|
||||
struct crypto_aead *ccmaes[CRYPTO_AEAD_MAX];
|
||||
};
|
||||
|
||||
#define CRYPTO_HMACMD5(c) ((c)->desc[CRYPTO_SHASH_HMACMD5])
|
||||
#define CRYPTO_HMACSHA256(c) ((c)->desc[CRYPTO_SHASH_HMACSHA256])
|
||||
#define CRYPTO_CMACAES(c) ((c)->desc[CRYPTO_SHASH_CMACAES])
|
||||
#define CRYPTO_SHA256(c) ((c)->desc[CRYPTO_SHASH_SHA256])
|
||||
#define CRYPTO_SHA512(c) ((c)->desc[CRYPTO_SHASH_SHA512])
|
||||
#define CRYPTO_MD4(c) ((c)->desc[CRYPTO_SHASH_MD4])
|
||||
#define CRYPTO_MD5(c) ((c)->desc[CRYPTO_SHASH_MD5])
|
||||
|
||||
#define CRYPTO_HMACMD5_TFM(c) ((c)->desc[CRYPTO_SHASH_HMACMD5]->tfm)
|
||||
#define CRYPTO_HMACSHA256_TFM(c)\
|
||||
((c)->desc[CRYPTO_SHASH_HMACSHA256]->tfm)
|
||||
#define CRYPTO_CMACAES_TFM(c) ((c)->desc[CRYPTO_SHASH_CMACAES]->tfm)
|
||||
#define CRYPTO_SHA256_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA256]->tfm)
|
||||
#define CRYPTO_SHA512_TFM(c) ((c)->desc[CRYPTO_SHASH_SHA512]->tfm)
|
||||
#define CRYPTO_MD4_TFM(c) ((c)->desc[CRYPTO_SHASH_MD4]->tfm)
|
||||
#define CRYPTO_MD5_TFM(c) ((c)->desc[CRYPTO_SHASH_MD5]->tfm)
|
||||
|
||||
#define CRYPTO_GCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_GCM])
|
||||
#define CRYPTO_CCM(c) ((c)->ccmaes[CRYPTO_AEAD_AES_CCM])
|
||||
|
||||
void ksmbd_release_crypto_ctx(struct ksmbd_crypto_ctx *ctx);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacmd5(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_hmacsha256(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_cmacaes(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha512(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_sha256(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md4(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_md5(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_gcm(void);
|
||||
struct ksmbd_crypto_ctx *ksmbd_crypto_ctx_find_ccm(void);
|
||||
void ksmbd_crypto_destroy(void);
|
||||
int ksmbd_crypto_create(void);
|
||||
|
||||
#endif /* __CRYPTO_CTX_H__ */
|
|
@ -0,0 +1,49 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_GLOB_H
|
||||
#define __KSMBD_GLOB_H
|
||||
|
||||
#include <linux/ctype.h>
|
||||
|
||||
#include "unicode.h"
|
||||
#include "vfs_cache.h"
|
||||
|
||||
#define KSMBD_VERSION "3.1.9"
|
||||
|
||||
extern int ksmbd_debug_types;
|
||||
|
||||
#define KSMBD_DEBUG_SMB BIT(0)
|
||||
#define KSMBD_DEBUG_AUTH BIT(1)
|
||||
#define KSMBD_DEBUG_VFS BIT(2)
|
||||
#define KSMBD_DEBUG_OPLOCK BIT(3)
|
||||
#define KSMBD_DEBUG_IPC BIT(4)
|
||||
#define KSMBD_DEBUG_CONN BIT(5)
|
||||
#define KSMBD_DEBUG_RDMA BIT(6)
|
||||
#define KSMBD_DEBUG_ALL (KSMBD_DEBUG_SMB | KSMBD_DEBUG_AUTH | \
|
||||
KSMBD_DEBUG_VFS | KSMBD_DEBUG_OPLOCK | \
|
||||
KSMBD_DEBUG_IPC | KSMBD_DEBUG_CONN | \
|
||||
KSMBD_DEBUG_RDMA)
|
||||
|
||||
#ifdef pr_fmt
|
||||
#undef pr_fmt
|
||||
#endif
|
||||
|
||||
#ifdef SUBMOD_NAME
|
||||
#define pr_fmt(fmt) "ksmbd: " SUBMOD_NAME ": " fmt
|
||||
#else
|
||||
#define pr_fmt(fmt) "ksmbd: " fmt
|
||||
#endif
|
||||
|
||||
#define ksmbd_debug(type, fmt, ...) \
|
||||
do { \
|
||||
if (ksmbd_debug_types & KSMBD_DEBUG_##type) \
|
||||
pr_info(fmt, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define UNICODE_LEN(x) ((x) * 2)
|
||||
|
||||
#endif /* __KSMBD_GLOB_H */
|
|
@ -0,0 +1,395 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* linux-ksmbd-devel@lists.sourceforge.net
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_KSMBD_SERVER_H
|
||||
#define _LINUX_KSMBD_SERVER_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* This is a userspace ABI to communicate data between ksmbd and user IPC
|
||||
* daemon using netlink. This is added to track and cache user account DB
|
||||
* and share configuration info from userspace.
|
||||
*
|
||||
* - KSMBD_EVENT_HEARTBEAT_REQUEST(ksmbd_heartbeat)
|
||||
* This event is to check whether user IPC daemon is alive. If user IPC
|
||||
* daemon is dead, ksmbd keep existing connection till disconnecting and
|
||||
* new connection will be denied.
|
||||
*
|
||||
* - KSMBD_EVENT_STARTING_UP(ksmbd_startup_request)
|
||||
* This event is to receive the information that initializes the ksmbd
|
||||
* server from the user IPC daemon and to start the server. The global
|
||||
* section parameters are given from smb.conf as initialization
|
||||
* information.
|
||||
*
|
||||
* - KSMBD_EVENT_SHUTTING_DOWN(ksmbd_shutdown_request)
|
||||
* This event is to shutdown ksmbd server.
|
||||
*
|
||||
* - KSMBD_EVENT_LOGIN_REQUEST/RESPONSE(ksmbd_login_request/response)
|
||||
* This event is to get user account info to user IPC daemon.
|
||||
*
|
||||
* - KSMBD_EVENT_SHARE_CONFIG_REQUEST/RESPONSE(ksmbd_share_config_request/response)
|
||||
* This event is to get net share configuration info.
|
||||
*
|
||||
* - KSMBD_EVENT_TREE_CONNECT_REQUEST/RESPONSE(ksmbd_tree_connect_request/response)
|
||||
* This event is to get session and tree connect info.
|
||||
*
|
||||
* - KSMBD_EVENT_TREE_DISCONNECT_REQUEST(ksmbd_tree_disconnect_request)
|
||||
* This event is to send tree disconnect info to user IPC daemon.
|
||||
*
|
||||
* - KSMBD_EVENT_LOGOUT_REQUEST(ksmbd_logout_request)
|
||||
* This event is to send logout request to user IPC daemon.
|
||||
*
|
||||
* - KSMBD_EVENT_RPC_REQUEST/RESPONSE(ksmbd_rpc_command)
|
||||
* This event is to make DCE/RPC request like srvsvc, wkssvc, lsarpc,
|
||||
* samr to be processed in userspace.
|
||||
*
|
||||
* - KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST/RESPONSE(ksmbd_spnego_authen_request/response)
|
||||
* This event is to make kerberos authentication to be processed in
|
||||
* userspace.
|
||||
*/
|
||||
|
||||
#define KSMBD_GENL_NAME "SMBD_GENL"
|
||||
#define KSMBD_GENL_VERSION 0x01
|
||||
|
||||
#define KSMBD_REQ_MAX_ACCOUNT_NAME_SZ 48
|
||||
#define KSMBD_REQ_MAX_HASH_SZ 18
|
||||
#define KSMBD_REQ_MAX_SHARE_NAME 64
|
||||
|
||||
/*
|
||||
* IPC heartbeat frame to check whether user IPC daemon is alive.
|
||||
*/
|
||||
struct ksmbd_heartbeat {
|
||||
__u32 handle;
|
||||
};
|
||||
|
||||
/*
|
||||
* Global config flags.
|
||||
*/
|
||||
#define KSMBD_GLOBAL_FLAG_INVALID (0)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB2_LEASES BIT(0)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION BIT(1)
|
||||
#define KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL BIT(2)
|
||||
|
||||
/*
|
||||
* IPC request for ksmbd server startup
|
||||
*/
|
||||
struct ksmbd_startup_request {
|
||||
__u32 flags; /* Flags for global config */
|
||||
__s32 signing; /* Signing enabled */
|
||||
__s8 min_prot[16]; /* The minimum SMB protocol version */
|
||||
__s8 max_prot[16]; /* The maximum SMB protocol version */
|
||||
__s8 netbios_name[16];
|
||||
__s8 work_group[64]; /* Workgroup */
|
||||
__s8 server_string[64]; /* Server string */
|
||||
__u16 tcp_port; /* tcp port */
|
||||
__u16 ipc_timeout; /*
|
||||
* specifies the number of seconds
|
||||
* server will wait for the userspace to
|
||||
* reply to heartbeat frames.
|
||||
*/
|
||||
__u32 deadtime; /* Number of minutes of inactivity */
|
||||
__u32 file_max; /* Limits the maximum number of open files */
|
||||
__u32 smb2_max_write; /* MAX write size */
|
||||
__u32 smb2_max_read; /* MAX read size */
|
||||
__u32 smb2_max_trans; /* MAX trans size */
|
||||
__u32 share_fake_fscaps; /*
|
||||
* Support some special application that
|
||||
* makes QFSINFO calls to check whether
|
||||
* we set the SPARSE_FILES bit (0x40).
|
||||
*/
|
||||
__u32 sub_auth[3]; /* Subauth value for Security ID */
|
||||
__u32 ifc_list_sz; /* interfaces list size */
|
||||
__s8 ____payload[];
|
||||
};
|
||||
|
||||
#define KSMBD_STARTUP_CONFIG_INTERFACES(s) ((s)->____payload)
|
||||
|
||||
/*
|
||||
* IPC request to shutdown ksmbd server.
|
||||
*/
|
||||
struct ksmbd_shutdown_request {
|
||||
__s32 reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC user login request.
|
||||
*/
|
||||
struct ksmbd_login_request {
|
||||
__u32 handle;
|
||||
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC user login response.
|
||||
*/
|
||||
struct ksmbd_login_response {
|
||||
__u32 handle;
|
||||
__u32 gid; /* group id */
|
||||
__u32 uid; /* user id */
|
||||
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
|
||||
__u16 status;
|
||||
__u16 hash_sz; /* hash size */
|
||||
__s8 hash[KSMBD_REQ_MAX_HASH_SZ]; /* password hash */
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC request to fetch net share config.
|
||||
*/
|
||||
struct ksmbd_share_config_request {
|
||||
__u32 handle;
|
||||
__s8 share_name[KSMBD_REQ_MAX_SHARE_NAME]; /* share name */
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC response to the net share config request.
|
||||
*/
|
||||
struct ksmbd_share_config_response {
|
||||
__u32 handle;
|
||||
__u32 flags;
|
||||
__u16 create_mask;
|
||||
__u16 directory_mask;
|
||||
__u16 force_create_mode;
|
||||
__u16 force_directory_mode;
|
||||
__u16 force_uid;
|
||||
__u16 force_gid;
|
||||
__u32 veto_list_sz;
|
||||
__s8 ____payload[];
|
||||
};
|
||||
|
||||
#define KSMBD_SHARE_CONFIG_VETO_LIST(s) ((s)->____payload)
|
||||
|
||||
static inline char *
|
||||
ksmbd_share_config_path(struct ksmbd_share_config_response *sc)
|
||||
{
|
||||
char *p = sc->____payload;
|
||||
|
||||
if (sc->veto_list_sz)
|
||||
p += sc->veto_list_sz + 1;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
* IPC request for tree connection. This request include session and tree
|
||||
* connect info from client.
|
||||
*/
|
||||
struct ksmbd_tree_connect_request {
|
||||
__u32 handle;
|
||||
__u16 account_flags;
|
||||
__u16 flags;
|
||||
__u64 session_id;
|
||||
__u64 connect_id;
|
||||
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ];
|
||||
__s8 share[KSMBD_REQ_MAX_SHARE_NAME];
|
||||
__s8 peer_addr[64];
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC Response structure for tree connection.
|
||||
*/
|
||||
struct ksmbd_tree_connect_response {
|
||||
__u32 handle;
|
||||
__u16 status;
|
||||
__u16 connection_flags;
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC Request struture to disconnect tree connection.
|
||||
*/
|
||||
struct ksmbd_tree_disconnect_request {
|
||||
__u64 session_id; /* session id */
|
||||
__u64 connect_id; /* tree connection id */
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC Response structure to logout user account.
|
||||
*/
|
||||
struct ksmbd_logout_request {
|
||||
__s8 account[KSMBD_REQ_MAX_ACCOUNT_NAME_SZ]; /* user account name */
|
||||
};
|
||||
|
||||
/*
|
||||
* RPC command structure to send rpc request like srvsvc or wkssvc to
|
||||
* IPC user daemon.
|
||||
*/
|
||||
struct ksmbd_rpc_command {
|
||||
__u32 handle;
|
||||
__u32 flags;
|
||||
__u32 payload_sz;
|
||||
__u8 payload[];
|
||||
};
|
||||
|
||||
/*
|
||||
* IPC Request Kerberos authentication
|
||||
*/
|
||||
struct ksmbd_spnego_authen_request {
|
||||
__u32 handle;
|
||||
__u16 spnego_blob_len; /* the length of spnego_blob */
|
||||
__u8 spnego_blob[0]; /*
|
||||
* the GSS token from SecurityBuffer of
|
||||
* SMB2 SESSION SETUP request
|
||||
*/
|
||||
};
|
||||
|
||||
/*
|
||||
* Response data which includes the GSS token and the session key generated by
|
||||
* user daemon.
|
||||
*/
|
||||
struct ksmbd_spnego_authen_response {
|
||||
__u32 handle;
|
||||
struct ksmbd_login_response login_response; /*
|
||||
* the login response with
|
||||
* a user identified by the
|
||||
* GSS token from a client
|
||||
*/
|
||||
__u16 session_key_len; /* the length of the session key */
|
||||
__u16 spnego_blob_len; /*
|
||||
* the length of the GSS token which will be
|
||||
* stored in SecurityBuffer of SMB2 SESSION
|
||||
* SETUP response
|
||||
*/
|
||||
__u8 payload[]; /* session key + AP_REP */
|
||||
};
|
||||
|
||||
/*
|
||||
* This also used as NETLINK attribute type value.
|
||||
*
|
||||
* NOTE:
|
||||
* Response message type value should be equal to
|
||||
* request message type value + 1.
|
||||
*/
|
||||
enum ksmbd_event {
|
||||
KSMBD_EVENT_UNSPEC = 0,
|
||||
KSMBD_EVENT_HEARTBEAT_REQUEST,
|
||||
|
||||
KSMBD_EVENT_STARTING_UP,
|
||||
KSMBD_EVENT_SHUTTING_DOWN,
|
||||
|
||||
KSMBD_EVENT_LOGIN_REQUEST,
|
||||
KSMBD_EVENT_LOGIN_RESPONSE = 5,
|
||||
|
||||
KSMBD_EVENT_SHARE_CONFIG_REQUEST,
|
||||
KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
|
||||
|
||||
KSMBD_EVENT_TREE_CONNECT_REQUEST,
|
||||
KSMBD_EVENT_TREE_CONNECT_RESPONSE,
|
||||
|
||||
KSMBD_EVENT_TREE_DISCONNECT_REQUEST = 10,
|
||||
|
||||
KSMBD_EVENT_LOGOUT_REQUEST,
|
||||
|
||||
KSMBD_EVENT_RPC_REQUEST,
|
||||
KSMBD_EVENT_RPC_RESPONSE,
|
||||
|
||||
KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
|
||||
KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15,
|
||||
|
||||
KSMBD_EVENT_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* Enumeration for IPC tree connect status.
|
||||
*/
|
||||
enum KSMBD_TREE_CONN_STATUS {
|
||||
KSMBD_TREE_CONN_STATUS_OK = 0,
|
||||
KSMBD_TREE_CONN_STATUS_NOMEM,
|
||||
KSMBD_TREE_CONN_STATUS_NO_SHARE,
|
||||
KSMBD_TREE_CONN_STATUS_NO_USER,
|
||||
KSMBD_TREE_CONN_STATUS_INVALID_USER,
|
||||
KSMBD_TREE_CONN_STATUS_HOST_DENIED = 5,
|
||||
KSMBD_TREE_CONN_STATUS_CONN_EXIST,
|
||||
KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS,
|
||||
KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS,
|
||||
KSMBD_TREE_CONN_STATUS_ERROR,
|
||||
};
|
||||
|
||||
/*
|
||||
* User config flags.
|
||||
*/
|
||||
#define KSMBD_USER_FLAG_INVALID (0)
|
||||
#define KSMBD_USER_FLAG_OK BIT(0)
|
||||
#define KSMBD_USER_FLAG_BAD_PASSWORD BIT(1)
|
||||
#define KSMBD_USER_FLAG_BAD_UID BIT(2)
|
||||
#define KSMBD_USER_FLAG_BAD_USER BIT(3)
|
||||
#define KSMBD_USER_FLAG_GUEST_ACCOUNT BIT(4)
|
||||
|
||||
/*
|
||||
* Share config flags.
|
||||
*/
|
||||
#define KSMBD_SHARE_FLAG_INVALID (0)
|
||||
#define KSMBD_SHARE_FLAG_AVAILABLE BIT(0)
|
||||
#define KSMBD_SHARE_FLAG_BROWSEABLE BIT(1)
|
||||
#define KSMBD_SHARE_FLAG_WRITEABLE BIT(2)
|
||||
#define KSMBD_SHARE_FLAG_READONLY BIT(3)
|
||||
#define KSMBD_SHARE_FLAG_GUEST_OK BIT(4)
|
||||
#define KSMBD_SHARE_FLAG_GUEST_ONLY BIT(5)
|
||||
#define KSMBD_SHARE_FLAG_STORE_DOS_ATTRS BIT(6)
|
||||
#define KSMBD_SHARE_FLAG_OPLOCKS BIT(7)
|
||||
#define KSMBD_SHARE_FLAG_PIPE BIT(8)
|
||||
#define KSMBD_SHARE_FLAG_HIDE_DOT_FILES BIT(9)
|
||||
#define KSMBD_SHARE_FLAG_INHERIT_OWNER BIT(10)
|
||||
#define KSMBD_SHARE_FLAG_STREAMS BIT(11)
|
||||
#define KSMBD_SHARE_FLAG_FOLLOW_SYMLINKS BIT(12)
|
||||
#define KSMBD_SHARE_FLAG_ACL_XATTR BIT(13)
|
||||
|
||||
/*
|
||||
* Tree connect request flags.
|
||||
*/
|
||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB1 (0)
|
||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_IPV6 BIT(0)
|
||||
#define KSMBD_TREE_CONN_FLAG_REQUEST_SMB2 BIT(1)
|
||||
|
||||
/*
|
||||
* Tree connect flags.
|
||||
*/
|
||||
#define KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT BIT(0)
|
||||
#define KSMBD_TREE_CONN_FLAG_READ_ONLY BIT(1)
|
||||
#define KSMBD_TREE_CONN_FLAG_WRITABLE BIT(2)
|
||||
#define KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT BIT(3)
|
||||
|
||||
/*
|
||||
* RPC over IPC.
|
||||
*/
|
||||
#define KSMBD_RPC_METHOD_RETURN BIT(0)
|
||||
#define KSMBD_RPC_SRVSVC_METHOD_INVOKE BIT(1)
|
||||
#define KSMBD_RPC_SRVSVC_METHOD_RETURN (KSMBD_RPC_SRVSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_WKSSVC_METHOD_INVOKE BIT(2)
|
||||
#define KSMBD_RPC_WKSSVC_METHOD_RETURN (KSMBD_RPC_WKSSVC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_IOCTL_METHOD (BIT(3) | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_OPEN_METHOD BIT(4)
|
||||
#define KSMBD_RPC_WRITE_METHOD BIT(5)
|
||||
#define KSMBD_RPC_READ_METHOD (BIT(6) | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_CLOSE_METHOD BIT(7)
|
||||
#define KSMBD_RPC_RAP_METHOD (BIT(8) | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_RESTRICTED_CONTEXT BIT(9)
|
||||
#define KSMBD_RPC_SAMR_METHOD_INVOKE BIT(10)
|
||||
#define KSMBD_RPC_SAMR_METHOD_RETURN (KSMBD_RPC_SAMR_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN)
|
||||
#define KSMBD_RPC_LSARPC_METHOD_INVOKE BIT(11)
|
||||
#define KSMBD_RPC_LSARPC_METHOD_RETURN (KSMBD_RPC_LSARPC_METHOD_INVOKE | KSMBD_RPC_METHOD_RETURN)
|
||||
|
||||
/*
|
||||
* RPC status definitions.
|
||||
*/
|
||||
#define KSMBD_RPC_OK 0
|
||||
#define KSMBD_RPC_EBAD_FUNC 0x00000001
|
||||
#define KSMBD_RPC_EACCESS_DENIED 0x00000005
|
||||
#define KSMBD_RPC_EBAD_FID 0x00000006
|
||||
#define KSMBD_RPC_ENOMEM 0x00000008
|
||||
#define KSMBD_RPC_EBAD_DATA 0x0000000D
|
||||
#define KSMBD_RPC_ENOTIMPLEMENTED 0x00000040
|
||||
#define KSMBD_RPC_EINVALID_PARAMETER 0x00000057
|
||||
#define KSMBD_RPC_EMORE_DATA 0x000000EA
|
||||
#define KSMBD_RPC_EINVALID_LEVEL 0x0000007C
|
||||
#define KSMBD_RPC_SOME_NOT_MAPPED 0x00000107
|
||||
|
||||
#define KSMBD_CONFIG_OPT_DISABLED 0
|
||||
#define KSMBD_CONFIG_OPT_ENABLED 1
|
||||
#define KSMBD_CONFIG_OPT_AUTO 2
|
||||
#define KSMBD_CONFIG_OPT_MANDATORY 3
|
||||
|
||||
#endif /* _LINUX_KSMBD_SERVER_H */
|
|
@ -0,0 +1,31 @@
|
|||
GSSAPI ::=
|
||||
[APPLICATION 0] IMPLICIT SEQUENCE {
|
||||
thisMech
|
||||
OBJECT IDENTIFIER ({ksmbd_gssapi_this_mech}),
|
||||
negotiationToken
|
||||
NegotiationToken
|
||||
}
|
||||
|
||||
MechType ::= OBJECT IDENTIFIER ({ksmbd_neg_token_init_mech_type})
|
||||
|
||||
MechTypeList ::= SEQUENCE OF MechType
|
||||
|
||||
NegTokenInit ::=
|
||||
SEQUENCE {
|
||||
mechTypes
|
||||
[0] MechTypeList,
|
||||
reqFlags
|
||||
[1] BIT STRING OPTIONAL,
|
||||
mechToken
|
||||
[2] OCTET STRING OPTIONAL ({ksmbd_neg_token_init_mech_token}),
|
||||
mechListMIC
|
||||
[3] OCTET STRING OPTIONAL
|
||||
}
|
||||
|
||||
NegotiationToken ::=
|
||||
CHOICE {
|
||||
negTokenInit
|
||||
[0] NegTokenInit,
|
||||
negTokenTarg
|
||||
[1] ANY
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
GSSAPI ::=
|
||||
CHOICE {
|
||||
negTokenInit
|
||||
[0] ANY,
|
||||
negTokenTarg
|
||||
[1] NegTokenTarg
|
||||
}
|
||||
|
||||
NegTokenTarg ::=
|
||||
SEQUENCE {
|
||||
negResult
|
||||
[0] ENUMERATED OPTIONAL,
|
||||
supportedMech
|
||||
[1] OBJECT IDENTIFIER OPTIONAL,
|
||||
responseToken
|
||||
[2] OCTET STRING OPTIONAL ({ksmbd_neg_token_targ_resp_token}),
|
||||
mechListMIC
|
||||
[3] OCTET STRING OPTIONAL
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "server.h"
|
||||
#include "connection.h"
|
||||
#include "ksmbd_work.h"
|
||||
#include "mgmt/ksmbd_ida.h"
|
||||
|
||||
static struct kmem_cache *work_cache;
|
||||
static struct workqueue_struct *ksmbd_wq;
|
||||
|
||||
struct ksmbd_work *ksmbd_alloc_work_struct(void)
|
||||
{
|
||||
struct ksmbd_work *work = kmem_cache_zalloc(work_cache, GFP_KERNEL);
|
||||
|
||||
if (work) {
|
||||
work->compound_fid = KSMBD_NO_FID;
|
||||
work->compound_pfid = KSMBD_NO_FID;
|
||||
INIT_LIST_HEAD(&work->request_entry);
|
||||
INIT_LIST_HEAD(&work->async_request_entry);
|
||||
INIT_LIST_HEAD(&work->fp_entry);
|
||||
INIT_LIST_HEAD(&work->interim_entry);
|
||||
}
|
||||
return work;
|
||||
}
|
||||
|
||||
void ksmbd_free_work_struct(struct ksmbd_work *work)
|
||||
{
|
||||
WARN_ON(work->saved_cred != NULL);
|
||||
|
||||
kvfree(work->response_buf);
|
||||
kvfree(work->aux_payload_buf);
|
||||
kfree(work->tr_buf);
|
||||
kvfree(work->request_buf);
|
||||
if (work->async_id)
|
||||
ksmbd_release_id(&work->conn->async_ida, work->async_id);
|
||||
kmem_cache_free(work_cache, work);
|
||||
}
|
||||
|
||||
void ksmbd_work_pool_destroy(void)
|
||||
{
|
||||
kmem_cache_destroy(work_cache);
|
||||
}
|
||||
|
||||
int ksmbd_work_pool_init(void)
|
||||
{
|
||||
work_cache = kmem_cache_create("ksmbd_work_cache",
|
||||
sizeof(struct ksmbd_work), 0,
|
||||
SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!work_cache)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_workqueue_init(void)
|
||||
{
|
||||
ksmbd_wq = alloc_workqueue("ksmbd-io", 0, 0);
|
||||
if (!ksmbd_wq)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_workqueue_destroy(void)
|
||||
{
|
||||
flush_workqueue(ksmbd_wq);
|
||||
destroy_workqueue(ksmbd_wq);
|
||||
ksmbd_wq = NULL;
|
||||
}
|
||||
|
||||
bool ksmbd_queue_work(struct ksmbd_work *work)
|
||||
{
|
||||
return queue_work(ksmbd_wq, &work->work);
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_WORK_H__
|
||||
#define __KSMBD_WORK_H__
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
struct ksmbd_conn;
|
||||
struct ksmbd_session;
|
||||
struct ksmbd_tree_connect;
|
||||
|
||||
enum {
|
||||
KSMBD_WORK_ACTIVE = 0,
|
||||
KSMBD_WORK_CANCELLED,
|
||||
KSMBD_WORK_CLOSED,
|
||||
};
|
||||
|
||||
/* one of these for every pending CIFS request at the connection */
|
||||
struct ksmbd_work {
|
||||
/* Server corresponding to this mid */
|
||||
struct ksmbd_conn *conn;
|
||||
struct ksmbd_session *sess;
|
||||
struct ksmbd_tree_connect *tcon;
|
||||
|
||||
/* Pointer to received SMB header */
|
||||
void *request_buf;
|
||||
/* Response buffer */
|
||||
void *response_buf;
|
||||
|
||||
/* Read data buffer */
|
||||
void *aux_payload_buf;
|
||||
|
||||
/* Next cmd hdr in compound req buf*/
|
||||
int next_smb2_rcv_hdr_off;
|
||||
/* Next cmd hdr in compound rsp buf*/
|
||||
int next_smb2_rsp_hdr_off;
|
||||
|
||||
/*
|
||||
* Current Local FID assigned compound response if SMB2 CREATE
|
||||
* command is present in compound request
|
||||
*/
|
||||
u64 compound_fid;
|
||||
u64 compound_pfid;
|
||||
u64 compound_sid;
|
||||
|
||||
const struct cred *saved_cred;
|
||||
|
||||
/* Number of granted credits */
|
||||
unsigned int credits_granted;
|
||||
|
||||
/* response smb header size */
|
||||
unsigned int resp_hdr_sz;
|
||||
unsigned int response_sz;
|
||||
/* Read data count */
|
||||
unsigned int aux_payload_sz;
|
||||
|
||||
void *tr_buf;
|
||||
|
||||
unsigned char state;
|
||||
/* Multiple responses for one request e.g. SMB ECHO */
|
||||
bool multiRsp:1;
|
||||
/* No response for cancelled request */
|
||||
bool send_no_response:1;
|
||||
/* Request is encrypted */
|
||||
bool encrypted:1;
|
||||
/* Is this SYNC or ASYNC ksmbd_work */
|
||||
bool syncronous:1;
|
||||
bool need_invalidate_rkey:1;
|
||||
|
||||
unsigned int remote_key;
|
||||
/* cancel works */
|
||||
int async_id;
|
||||
void **cancel_argv;
|
||||
void (*cancel_fn)(void **argv);
|
||||
|
||||
struct work_struct work;
|
||||
/* List head at conn->requests */
|
||||
struct list_head request_entry;
|
||||
/* List head at conn->async_requests */
|
||||
struct list_head async_request_entry;
|
||||
struct list_head fp_entry;
|
||||
struct list_head interim_entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* ksmbd_resp_buf_next - Get next buffer on compound response.
|
||||
* @work: smb work containing response buffer
|
||||
*/
|
||||
static inline void *ksmbd_resp_buf_next(struct ksmbd_work *work)
|
||||
{
|
||||
return work->response_buf + work->next_smb2_rsp_hdr_off;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_req_buf_next - Get next buffer on compound request.
|
||||
* @work: smb work containing response buffer
|
||||
*/
|
||||
static inline void *ksmbd_req_buf_next(struct ksmbd_work *work)
|
||||
{
|
||||
return work->request_buf + work->next_smb2_rcv_hdr_off;
|
||||
}
|
||||
|
||||
struct ksmbd_work *ksmbd_alloc_work_struct(void);
|
||||
void ksmbd_free_work_struct(struct ksmbd_work *work);
|
||||
|
||||
void ksmbd_work_pool_destroy(void);
|
||||
int ksmbd_work_pool_init(void);
|
||||
|
||||
int ksmbd_workqueue_init(void);
|
||||
void ksmbd_workqueue_destroy(void);
|
||||
bool ksmbd_queue_work(struct ksmbd_work *work);
|
||||
|
||||
#endif /* __KSMBD_WORK_H__ */
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include "ksmbd_ida.h"
|
||||
|
||||
static inline int __acquire_id(struct ida *ida, int from, int to)
|
||||
{
|
||||
return ida_simple_get(ida, from, to, GFP_KERNEL);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_smb2_tid(struct ida *ida)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = __acquire_id(ida, 1, 0xFFFFFFFF);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int ksmbd_acquire_smb2_uid(struct ida *ida)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = __acquire_id(ida, 1, 0);
|
||||
if (id == 0xFFFE)
|
||||
id = __acquire_id(ida, 1, 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
int ksmbd_acquire_async_msg_id(struct ida *ida)
|
||||
{
|
||||
return __acquire_id(ida, 1, 0);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_id(struct ida *ida)
|
||||
{
|
||||
return __acquire_id(ida, 0, 0);
|
||||
}
|
||||
|
||||
void ksmbd_release_id(struct ida *ida, int id)
|
||||
{
|
||||
ida_simple_remove(ida, id);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_IDA_MANAGEMENT_H__
|
||||
#define __KSMBD_IDA_MANAGEMENT_H__
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
|
||||
/*
|
||||
* 2.2.1.6.7 TID Generation
|
||||
* The value 0xFFFF MUST NOT be used as a valid TID. All other
|
||||
* possible values for TID, including zero (0x0000), are valid.
|
||||
* The value 0xFFFF is used to specify all TIDs or no TID,
|
||||
* depending upon the context in which it is used.
|
||||
*/
|
||||
int ksmbd_acquire_smb2_tid(struct ida *ida);
|
||||
|
||||
/*
|
||||
* 2.2.1.6.8 UID Generation
|
||||
* The value 0xFFFE was declared reserved in the LAN Manager 1.0
|
||||
* documentation, so a value of 0xFFFE SHOULD NOT be used as a
|
||||
* valid UID.<21> All other possible values for a UID, excluding
|
||||
* zero (0x0000), are valid.
|
||||
*/
|
||||
int ksmbd_acquire_smb2_uid(struct ida *ida);
|
||||
int ksmbd_acquire_async_msg_id(struct ida *ida);
|
||||
|
||||
int ksmbd_acquire_id(struct ida *ida);
|
||||
|
||||
void ksmbd_release_id(struct ida *ida, int id);
|
||||
#endif /* __KSMBD_IDA_MANAGEMENT_H__ */
|
|
@ -0,0 +1,238 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/parser.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "share_config.h"
|
||||
#include "user_config.h"
|
||||
#include "user_session.h"
|
||||
#include "../transport_ipc.h"
|
||||
|
||||
#define SHARE_HASH_BITS 3
|
||||
static DEFINE_HASHTABLE(shares_table, SHARE_HASH_BITS);
|
||||
static DECLARE_RWSEM(shares_table_lock);
|
||||
|
||||
struct ksmbd_veto_pattern {
|
||||
char *pattern;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static unsigned int share_name_hash(char *name)
|
||||
{
|
||||
return jhash(name, strlen(name), 0);
|
||||
}
|
||||
|
||||
static void kill_share(struct ksmbd_share_config *share)
|
||||
{
|
||||
while (!list_empty(&share->veto_list)) {
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
p = list_entry(share->veto_list.next,
|
||||
struct ksmbd_veto_pattern,
|
||||
list);
|
||||
list_del(&p->list);
|
||||
kfree(p->pattern);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
if (share->path)
|
||||
path_put(&share->vfs_path);
|
||||
kfree(share->name);
|
||||
kfree(share->path);
|
||||
kfree(share);
|
||||
}
|
||||
|
||||
void __ksmbd_share_config_put(struct ksmbd_share_config *share)
|
||||
{
|
||||
down_write(&shares_table_lock);
|
||||
hash_del(&share->hlist);
|
||||
up_write(&shares_table_lock);
|
||||
|
||||
kill_share(share);
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *
|
||||
__get_share_config(struct ksmbd_share_config *share)
|
||||
{
|
||||
if (!atomic_inc_not_zero(&share->refcount))
|
||||
return NULL;
|
||||
return share;
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *__share_lookup(char *name)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
unsigned int key = share_name_hash(name);
|
||||
|
||||
hash_for_each_possible(shares_table, share, hlist, key) {
|
||||
if (!strcmp(name, share->name))
|
||||
return share;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_veto_list(struct ksmbd_share_config *share,
|
||||
char *veto_list,
|
||||
int veto_list_sz)
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
if (!veto_list_sz)
|
||||
return 0;
|
||||
|
||||
while (veto_list_sz > 0) {
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
sz = strlen(veto_list);
|
||||
if (!sz)
|
||||
break;
|
||||
|
||||
p = kzalloc(sizeof(struct ksmbd_veto_pattern), GFP_KERNEL);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->pattern = kstrdup(veto_list, GFP_KERNEL);
|
||||
if (!p->pattern) {
|
||||
kfree(p);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
list_add(&p->list, &share->veto_list);
|
||||
|
||||
veto_list += sz + 1;
|
||||
veto_list_sz -= (sz + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_share_config *share_config_request(char *name)
|
||||
{
|
||||
struct ksmbd_share_config_response *resp;
|
||||
struct ksmbd_share_config *share = NULL;
|
||||
struct ksmbd_share_config *lookup;
|
||||
int ret;
|
||||
|
||||
resp = ksmbd_ipc_share_config_request(name);
|
||||
if (!resp)
|
||||
return NULL;
|
||||
|
||||
if (resp->flags == KSMBD_SHARE_FLAG_INVALID)
|
||||
goto out;
|
||||
|
||||
share = kzalloc(sizeof(struct ksmbd_share_config), GFP_KERNEL);
|
||||
if (!share)
|
||||
goto out;
|
||||
|
||||
share->flags = resp->flags;
|
||||
atomic_set(&share->refcount, 1);
|
||||
INIT_LIST_HEAD(&share->veto_list);
|
||||
share->name = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
if (!test_share_config_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
|
||||
share->path = kstrdup(ksmbd_share_config_path(resp),
|
||||
GFP_KERNEL);
|
||||
if (share->path)
|
||||
share->path_sz = strlen(share->path);
|
||||
share->create_mask = resp->create_mask;
|
||||
share->directory_mask = resp->directory_mask;
|
||||
share->force_create_mode = resp->force_create_mode;
|
||||
share->force_directory_mode = resp->force_directory_mode;
|
||||
share->force_uid = resp->force_uid;
|
||||
share->force_gid = resp->force_gid;
|
||||
ret = parse_veto_list(share,
|
||||
KSMBD_SHARE_CONFIG_VETO_LIST(resp),
|
||||
resp->veto_list_sz);
|
||||
if (!ret && share->path) {
|
||||
ret = kern_path(share->path, 0, &share->vfs_path);
|
||||
if (ret) {
|
||||
ksmbd_debug(SMB, "failed to access '%s'\n",
|
||||
share->path);
|
||||
/* Avoid put_path() */
|
||||
kfree(share->path);
|
||||
share->path = NULL;
|
||||
}
|
||||
}
|
||||
if (ret || !share->name) {
|
||||
kill_share(share);
|
||||
share = NULL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&shares_table_lock);
|
||||
lookup = __share_lookup(name);
|
||||
if (lookup)
|
||||
lookup = __get_share_config(lookup);
|
||||
if (!lookup) {
|
||||
hash_add(shares_table, &share->hlist, share_name_hash(name));
|
||||
} else {
|
||||
kill_share(share);
|
||||
share = lookup;
|
||||
}
|
||||
up_write(&shares_table_lock);
|
||||
|
||||
out:
|
||||
kvfree(resp);
|
||||
return share;
|
||||
}
|
||||
|
||||
static void strtolower(char *share_name)
|
||||
{
|
||||
while (*share_name) {
|
||||
*share_name = tolower(*share_name);
|
||||
share_name++;
|
||||
}
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_share_config_get(char *name)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
|
||||
strtolower(name);
|
||||
|
||||
down_read(&shares_table_lock);
|
||||
share = __share_lookup(name);
|
||||
if (share)
|
||||
share = __get_share_config(share);
|
||||
up_read(&shares_table_lock);
|
||||
|
||||
if (share)
|
||||
return share;
|
||||
return share_config_request(name);
|
||||
}
|
||||
|
||||
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
|
||||
const char *filename)
|
||||
{
|
||||
struct ksmbd_veto_pattern *p;
|
||||
|
||||
list_for_each_entry(p, &share->veto_list, list) {
|
||||
if (match_wildcard(p->pattern, filename))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ksmbd_share_configs_cleanup(void)
|
||||
{
|
||||
struct ksmbd_share_config *share;
|
||||
struct hlist_node *tmp;
|
||||
int i;
|
||||
|
||||
down_write(&shares_table_lock);
|
||||
hash_for_each_safe(shares_table, i, tmp, share, hlist) {
|
||||
hash_del(&share->hlist);
|
||||
kill_share(share);
|
||||
}
|
||||
up_write(&shares_table_lock);
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __SHARE_CONFIG_MANAGEMENT_H__
|
||||
#define __SHARE_CONFIG_MANAGEMENT_H__
|
||||
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/path.h>
|
||||
|
||||
struct ksmbd_share_config {
|
||||
char *name;
|
||||
char *path;
|
||||
|
||||
unsigned int path_sz;
|
||||
unsigned int flags;
|
||||
struct list_head veto_list;
|
||||
|
||||
struct path vfs_path;
|
||||
|
||||
atomic_t refcount;
|
||||
struct hlist_node hlist;
|
||||
unsigned short create_mask;
|
||||
unsigned short directory_mask;
|
||||
unsigned short force_create_mode;
|
||||
unsigned short force_directory_mode;
|
||||
unsigned short force_uid;
|
||||
unsigned short force_gid;
|
||||
};
|
||||
|
||||
#define KSMBD_SHARE_INVALID_UID ((__u16)-1)
|
||||
#define KSMBD_SHARE_INVALID_GID ((__u16)-1)
|
||||
|
||||
static inline int share_config_create_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_create_mode) {
|
||||
if (!posix_mode)
|
||||
return share->create_mask;
|
||||
else
|
||||
return posix_mode & share->create_mask;
|
||||
}
|
||||
return share->force_create_mode & share->create_mask;
|
||||
}
|
||||
|
||||
static inline int share_config_directory_mode(struct ksmbd_share_config *share,
|
||||
umode_t posix_mode)
|
||||
{
|
||||
if (!share->force_directory_mode) {
|
||||
if (!posix_mode)
|
||||
return share->directory_mask;
|
||||
else
|
||||
return posix_mode & share->directory_mask;
|
||||
}
|
||||
|
||||
return share->force_directory_mode & share->directory_mask;
|
||||
}
|
||||
|
||||
static inline int test_share_config_flag(struct ksmbd_share_config *share,
|
||||
int flag)
|
||||
{
|
||||
return share->flags & flag;
|
||||
}
|
||||
|
||||
void __ksmbd_share_config_put(struct ksmbd_share_config *share);
|
||||
|
||||
static inline void ksmbd_share_config_put(struct ksmbd_share_config *share)
|
||||
{
|
||||
if (!atomic_dec_and_test(&share->refcount))
|
||||
return;
|
||||
__ksmbd_share_config_put(share);
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_share_config_get(char *name);
|
||||
bool ksmbd_share_veto_filename(struct ksmbd_share_config *share,
|
||||
const char *filename);
|
||||
void ksmbd_share_configs_cleanup(void);
|
||||
|
||||
#endif /* __SHARE_CONFIG_MANAGEMENT_H__ */
|
|
@ -0,0 +1,121 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "../transport_ipc.h"
|
||||
#include "../connection.h"
|
||||
|
||||
#include "tree_connect.h"
|
||||
#include "user_config.h"
|
||||
#include "share_config.h"
|
||||
#include "user_session.h"
|
||||
|
||||
struct ksmbd_tree_conn_status
|
||||
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name)
|
||||
{
|
||||
struct ksmbd_tree_conn_status status = {-EINVAL, NULL};
|
||||
struct ksmbd_tree_connect_response *resp = NULL;
|
||||
struct ksmbd_share_config *sc;
|
||||
struct ksmbd_tree_connect *tree_conn = NULL;
|
||||
struct sockaddr *peer_addr;
|
||||
int ret;
|
||||
|
||||
sc = ksmbd_share_config_get(share_name);
|
||||
if (!sc)
|
||||
return status;
|
||||
|
||||
tree_conn = kzalloc(sizeof(struct ksmbd_tree_connect), GFP_KERNEL);
|
||||
if (!tree_conn) {
|
||||
status.ret = -ENOMEM;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
tree_conn->id = ksmbd_acquire_tree_conn_id(sess);
|
||||
if (tree_conn->id < 0) {
|
||||
status.ret = -EINVAL;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
peer_addr = KSMBD_TCP_PEER_SOCKADDR(sess->conn);
|
||||
resp = ksmbd_ipc_tree_connect_request(sess,
|
||||
sc,
|
||||
tree_conn,
|
||||
peer_addr);
|
||||
if (!resp) {
|
||||
status.ret = -EINVAL;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
status.ret = resp->status;
|
||||
if (status.ret != KSMBD_TREE_CONN_STATUS_OK)
|
||||
goto out_error;
|
||||
|
||||
tree_conn->flags = resp->connection_flags;
|
||||
tree_conn->user = sess->user;
|
||||
tree_conn->share_conf = sc;
|
||||
status.tree_conn = tree_conn;
|
||||
|
||||
ret = xa_err(xa_store(&sess->tree_conns, tree_conn->id, tree_conn,
|
||||
GFP_KERNEL));
|
||||
if (ret) {
|
||||
status.ret = -ENOMEM;
|
||||
goto out_error;
|
||||
}
|
||||
kvfree(resp);
|
||||
return status;
|
||||
|
||||
out_error:
|
||||
if (tree_conn)
|
||||
ksmbd_release_tree_conn_id(sess, tree_conn->id);
|
||||
ksmbd_share_config_put(sc);
|
||||
kfree(tree_conn);
|
||||
kvfree(resp);
|
||||
return status;
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
|
||||
ksmbd_release_tree_conn_id(sess, tree_conn->id);
|
||||
xa_erase(&sess->tree_conns, tree_conn->id);
|
||||
ksmbd_share_config_put(tree_conn->share_conf);
|
||||
kfree(tree_conn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
unsigned int id)
|
||||
{
|
||||
return xa_load(&sess->tree_conns, id);
|
||||
}
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id)
|
||||
{
|
||||
struct ksmbd_tree_connect *tc;
|
||||
|
||||
tc = ksmbd_tree_conn_lookup(sess, id);
|
||||
if (tc)
|
||||
return tc->share_conf;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ksmbd_tree_connect *tc;
|
||||
unsigned long id;
|
||||
|
||||
xa_for_each(&sess->tree_conns, id, tc)
|
||||
ret |= ksmbd_tree_conn_disconnect(sess, tc);
|
||||
xa_destroy(&sess->tree_conns);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __TREE_CONNECT_MANAGEMENT_H__
|
||||
#define __TREE_CONNECT_MANAGEMENT_H__
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
|
||||
#include "../ksmbd_netlink.h"
|
||||
|
||||
struct ksmbd_share_config;
|
||||
struct ksmbd_user;
|
||||
|
||||
struct ksmbd_tree_connect {
|
||||
int id;
|
||||
|
||||
unsigned int flags;
|
||||
struct ksmbd_share_config *share_conf;
|
||||
struct ksmbd_user *user;
|
||||
|
||||
struct list_head list;
|
||||
|
||||
int maximal_access;
|
||||
bool posix_extensions;
|
||||
};
|
||||
|
||||
struct ksmbd_tree_conn_status {
|
||||
unsigned int ret;
|
||||
struct ksmbd_tree_connect *tree_conn;
|
||||
};
|
||||
|
||||
static inline int test_tree_conn_flag(struct ksmbd_tree_connect *tree_conn,
|
||||
int flag)
|
||||
{
|
||||
return tree_conn->flags & flag;
|
||||
}
|
||||
|
||||
struct ksmbd_session;
|
||||
|
||||
struct ksmbd_tree_conn_status
|
||||
ksmbd_tree_conn_connect(struct ksmbd_session *sess, char *share_name);
|
||||
|
||||
int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
|
||||
struct ksmbd_tree_connect *tree_conn);
|
||||
|
||||
struct ksmbd_tree_connect *ksmbd_tree_conn_lookup(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
struct ksmbd_share_config *ksmbd_tree_conn_share(struct ksmbd_session *sess,
|
||||
unsigned int id);
|
||||
|
||||
int ksmbd_tree_conn_session_logoff(struct ksmbd_session *sess);
|
||||
|
||||
#endif /* __TREE_CONNECT_MANAGEMENT_H__ */
|
|
@ -0,0 +1,69 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mm.h>
|
||||
|
||||
#include "user_config.h"
|
||||
#include "../transport_ipc.h"
|
||||
|
||||
struct ksmbd_user *ksmbd_login_user(const char *account)
|
||||
{
|
||||
struct ksmbd_login_response *resp;
|
||||
struct ksmbd_user *user = NULL;
|
||||
|
||||
resp = ksmbd_ipc_login_request(account);
|
||||
if (!resp)
|
||||
return NULL;
|
||||
|
||||
if (!(resp->status & KSMBD_USER_FLAG_OK))
|
||||
goto out;
|
||||
|
||||
user = ksmbd_alloc_user(resp);
|
||||
out:
|
||||
kvfree(resp);
|
||||
return user;
|
||||
}
|
||||
|
||||
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp)
|
||||
{
|
||||
struct ksmbd_user *user = NULL;
|
||||
|
||||
user = kmalloc(sizeof(struct ksmbd_user), GFP_KERNEL);
|
||||
if (!user)
|
||||
return NULL;
|
||||
|
||||
user->name = kstrdup(resp->account, GFP_KERNEL);
|
||||
user->flags = resp->status;
|
||||
user->gid = resp->gid;
|
||||
user->uid = resp->uid;
|
||||
user->passkey_sz = resp->hash_sz;
|
||||
user->passkey = kmalloc(resp->hash_sz, GFP_KERNEL);
|
||||
if (user->passkey)
|
||||
memcpy(user->passkey, resp->hash, resp->hash_sz);
|
||||
|
||||
if (!user->name || !user->passkey) {
|
||||
kfree(user->name);
|
||||
kfree(user->passkey);
|
||||
kfree(user);
|
||||
user = NULL;
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
void ksmbd_free_user(struct ksmbd_user *user)
|
||||
{
|
||||
ksmbd_ipc_logout_request(user->name);
|
||||
kfree(user->name);
|
||||
kfree(user->passkey);
|
||||
kfree(user);
|
||||
}
|
||||
|
||||
int ksmbd_anonymous_user(struct ksmbd_user *user)
|
||||
{
|
||||
if (user->name[0] == '\0')
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __USER_CONFIG_MANAGEMENT_H__
|
||||
#define __USER_CONFIG_MANAGEMENT_H__
|
||||
|
||||
#include "../glob.h"
|
||||
|
||||
struct ksmbd_user {
|
||||
unsigned short flags;
|
||||
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
|
||||
char *name;
|
||||
|
||||
size_t passkey_sz;
|
||||
char *passkey;
|
||||
};
|
||||
|
||||
static inline bool user_guest(struct ksmbd_user *user)
|
||||
{
|
||||
return user->flags & KSMBD_USER_FLAG_GUEST_ACCOUNT;
|
||||
}
|
||||
|
||||
static inline void set_user_flag(struct ksmbd_user *user, int flag)
|
||||
{
|
||||
user->flags |= flag;
|
||||
}
|
||||
|
||||
static inline int test_user_flag(struct ksmbd_user *user, int flag)
|
||||
{
|
||||
return user->flags & flag;
|
||||
}
|
||||
|
||||
static inline void set_user_guest(struct ksmbd_user *user)
|
||||
{
|
||||
}
|
||||
|
||||
static inline char *user_passkey(struct ksmbd_user *user)
|
||||
{
|
||||
return user->passkey;
|
||||
}
|
||||
|
||||
static inline char *user_name(struct ksmbd_user *user)
|
||||
{
|
||||
return user->name;
|
||||
}
|
||||
|
||||
static inline unsigned int user_uid(struct ksmbd_user *user)
|
||||
{
|
||||
return user->uid;
|
||||
}
|
||||
|
||||
static inline unsigned int user_gid(struct ksmbd_user *user)
|
||||
{
|
||||
return user->gid;
|
||||
}
|
||||
|
||||
struct ksmbd_user *ksmbd_login_user(const char *account);
|
||||
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp);
|
||||
void ksmbd_free_user(struct ksmbd_user *user);
|
||||
int ksmbd_anonymous_user(struct ksmbd_user *user);
|
||||
#endif /* __USER_CONFIG_MANAGEMENT_H__ */
|
|
@ -0,0 +1,369 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "ksmbd_ida.h"
|
||||
#include "user_session.h"
|
||||
#include "user_config.h"
|
||||
#include "tree_connect.h"
|
||||
#include "../transport_ipc.h"
|
||||
#include "../connection.h"
|
||||
#include "../vfs_cache.h"
|
||||
|
||||
static DEFINE_IDA(session_ida);
|
||||
|
||||
#define SESSION_HASH_BITS 3
|
||||
static DEFINE_HASHTABLE(sessions_table, SESSION_HASH_BITS);
|
||||
static DECLARE_RWSEM(sessions_table_lock);
|
||||
|
||||
struct ksmbd_session_rpc {
|
||||
int id;
|
||||
unsigned int method;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static void free_channel_list(struct ksmbd_session *sess)
|
||||
{
|
||||
struct channel *chann, *tmp;
|
||||
|
||||
list_for_each_entry_safe(chann, tmp, &sess->ksmbd_chann_list,
|
||||
chann_list) {
|
||||
list_del(&chann->chann_list);
|
||||
kfree(chann);
|
||||
}
|
||||
}
|
||||
|
||||
static void __session_rpc_close(struct ksmbd_session *sess,
|
||||
struct ksmbd_session_rpc *entry)
|
||||
{
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
resp = ksmbd_rpc_close(sess, entry->id);
|
||||
if (!resp)
|
||||
pr_err("Unable to close RPC pipe %d\n", entry->id);
|
||||
|
||||
kvfree(resp);
|
||||
ksmbd_rpc_id_free(entry->id);
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static void ksmbd_session_rpc_clear_list(struct ksmbd_session *sess)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
while (!list_empty(&sess->rpc_handle_list)) {
|
||||
entry = list_entry(sess->rpc_handle_list.next,
|
||||
struct ksmbd_session_rpc,
|
||||
list);
|
||||
|
||||
list_del(&entry->list);
|
||||
__session_rpc_close(sess, entry);
|
||||
}
|
||||
}
|
||||
|
||||
static int __rpc_method(char *rpc_name)
|
||||
{
|
||||
if (!strcmp(rpc_name, "\\srvsvc") || !strcmp(rpc_name, "srvsvc"))
|
||||
return KSMBD_RPC_SRVSVC_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "\\wkssvc") || !strcmp(rpc_name, "wkssvc"))
|
||||
return KSMBD_RPC_WKSSVC_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "LANMAN") || !strcmp(rpc_name, "lanman"))
|
||||
return KSMBD_RPC_RAP_METHOD;
|
||||
|
||||
if (!strcmp(rpc_name, "\\samr") || !strcmp(rpc_name, "samr"))
|
||||
return KSMBD_RPC_SAMR_METHOD_INVOKE;
|
||||
|
||||
if (!strcmp(rpc_name, "\\lsarpc") || !strcmp(rpc_name, "lsarpc"))
|
||||
return KSMBD_RPC_LSARPC_METHOD_INVOKE;
|
||||
|
||||
pr_err("Unsupported RPC: %s\n", rpc_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
int method;
|
||||
|
||||
method = __rpc_method(rpc_name);
|
||||
if (!method)
|
||||
return -EINVAL;
|
||||
|
||||
entry = kzalloc(sizeof(struct ksmbd_session_rpc), GFP_KERNEL);
|
||||
if (!entry)
|
||||
return -EINVAL;
|
||||
|
||||
list_add(&entry->list, &sess->rpc_handle_list);
|
||||
entry->method = method;
|
||||
entry->id = ksmbd_ipc_id_alloc();
|
||||
if (entry->id < 0)
|
||||
goto error;
|
||||
|
||||
resp = ksmbd_rpc_open(sess, entry->id);
|
||||
if (!resp)
|
||||
goto error;
|
||||
|
||||
kvfree(resp);
|
||||
return entry->id;
|
||||
error:
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id) {
|
||||
list_del(&entry->list);
|
||||
__session_rpc_close(sess, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
struct ksmbd_session_rpc *entry;
|
||||
|
||||
list_for_each_entry(entry, &sess->rpc_handle_list, list) {
|
||||
if (entry->id == id)
|
||||
return entry->method;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_session_destroy(struct ksmbd_session *sess)
|
||||
{
|
||||
if (!sess)
|
||||
return;
|
||||
|
||||
if (!atomic_dec_and_test(&sess->refcnt))
|
||||
return;
|
||||
|
||||
list_del(&sess->sessions_entry);
|
||||
|
||||
down_write(&sessions_table_lock);
|
||||
hash_del(&sess->hlist);
|
||||
up_write(&sessions_table_lock);
|
||||
|
||||
if (sess->user)
|
||||
ksmbd_free_user(sess->user);
|
||||
|
||||
ksmbd_tree_conn_session_logoff(sess);
|
||||
ksmbd_destroy_file_table(&sess->file_table);
|
||||
ksmbd_session_rpc_clear_list(sess);
|
||||
free_channel_list(sess);
|
||||
kfree(sess->Preauth_HashValue);
|
||||
ksmbd_release_id(&session_ida, sess->id);
|
||||
kfree(sess);
|
||||
}
|
||||
|
||||
static struct ksmbd_session *__session_lookup(unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
hash_for_each_possible(sessions_table, sess, hlist, id) {
|
||||
if (id == sess->id)
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ksmbd_session_register(struct ksmbd_conn *conn,
|
||||
struct ksmbd_session *sess)
|
||||
{
|
||||
sess->conn = conn;
|
||||
list_add(&sess->sessions_entry, &conn->sessions);
|
||||
}
|
||||
|
||||
void ksmbd_sessions_deregister(struct ksmbd_conn *conn)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
while (!list_empty(&conn->sessions)) {
|
||||
sess = list_entry(conn->sessions.next,
|
||||
struct ksmbd_session,
|
||||
sessions_entry);
|
||||
|
||||
ksmbd_session_destroy(sess);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ksmbd_session_id_match(struct ksmbd_session *sess,
|
||||
unsigned long long id)
|
||||
{
|
||||
return sess->id == id;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess = NULL;
|
||||
|
||||
list_for_each_entry(sess, &conn->sessions, sessions_entry) {
|
||||
if (ksmbd_session_id_match(sess, id))
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int get_session(struct ksmbd_session *sess)
|
||||
{
|
||||
return atomic_inc_not_zero(&sess->refcnt);
|
||||
}
|
||||
|
||||
void put_session(struct ksmbd_session *sess)
|
||||
{
|
||||
if (atomic_dec_and_test(&sess->refcnt))
|
||||
pr_err("get/%s seems to be mismatched.", __func__);
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
down_read(&sessions_table_lock);
|
||||
sess = __session_lookup(id);
|
||||
if (sess) {
|
||||
if (!get_session(sess))
|
||||
sess = NULL;
|
||||
}
|
||||
up_read(&sessions_table_lock);
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
|
||||
sess = ksmbd_session_lookup(conn, id);
|
||||
if (!sess && conn->binding)
|
||||
sess = ksmbd_session_lookup_slowpath(id);
|
||||
return sess;
|
||||
}
|
||||
|
||||
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
||||
u64 sess_id)
|
||||
{
|
||||
struct preauth_session *sess;
|
||||
|
||||
sess = kmalloc(sizeof(struct preauth_session), GFP_KERNEL);
|
||||
if (!sess)
|
||||
return NULL;
|
||||
|
||||
sess->id = sess_id;
|
||||
memcpy(sess->Preauth_HashValue, conn->preauth_info->Preauth_HashValue,
|
||||
PREAUTH_HASHVALUE_SIZE);
|
||||
list_add(&sess->preauth_entry, &conn->preauth_sess_table);
|
||||
|
||||
return sess;
|
||||
}
|
||||
|
||||
static bool ksmbd_preauth_session_id_match(struct preauth_session *sess,
|
||||
unsigned long long id)
|
||||
{
|
||||
return sess->id == id;
|
||||
}
|
||||
|
||||
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id)
|
||||
{
|
||||
struct preauth_session *sess = NULL;
|
||||
|
||||
list_for_each_entry(sess, &conn->preauth_sess_table, preauth_entry) {
|
||||
if (ksmbd_preauth_session_id_match(sess, id))
|
||||
return sess;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init_smb2_session(struct ksmbd_session *sess)
|
||||
{
|
||||
int id = ksmbd_acquire_smb2_uid(&session_ida);
|
||||
|
||||
if (id < 0)
|
||||
return -EINVAL;
|
||||
sess->id = id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_session *__session_create(int protocol)
|
||||
{
|
||||
struct ksmbd_session *sess;
|
||||
int ret;
|
||||
|
||||
sess = kzalloc(sizeof(struct ksmbd_session), GFP_KERNEL);
|
||||
if (!sess)
|
||||
return NULL;
|
||||
|
||||
if (ksmbd_init_file_table(&sess->file_table))
|
||||
goto error;
|
||||
|
||||
set_session_flag(sess, protocol);
|
||||
INIT_LIST_HEAD(&sess->sessions_entry);
|
||||
xa_init(&sess->tree_conns);
|
||||
INIT_LIST_HEAD(&sess->ksmbd_chann_list);
|
||||
INIT_LIST_HEAD(&sess->rpc_handle_list);
|
||||
sess->sequence_number = 1;
|
||||
atomic_set(&sess->refcnt, 1);
|
||||
|
||||
switch (protocol) {
|
||||
case CIFDS_SESSION_FLAG_SMB2:
|
||||
ret = __init_smb2_session(sess);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
ida_init(&sess->tree_conn_ida);
|
||||
|
||||
if (protocol == CIFDS_SESSION_FLAG_SMB2) {
|
||||
down_write(&sessions_table_lock);
|
||||
hash_add(sessions_table, &sess->hlist, sess->id);
|
||||
up_write(&sessions_table_lock);
|
||||
}
|
||||
return sess;
|
||||
|
||||
error:
|
||||
ksmbd_session_destroy(sess);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_smb2_session_create(void)
|
||||
{
|
||||
return __session_create(CIFDS_SESSION_FLAG_SMB2);
|
||||
}
|
||||
|
||||
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess)
|
||||
{
|
||||
int id = -EINVAL;
|
||||
|
||||
if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
|
||||
id = ksmbd_acquire_smb2_tid(&sess->tree_conn_ida);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id)
|
||||
{
|
||||
if (id >= 0)
|
||||
ksmbd_release_id(&sess->tree_conn_ida, id);
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __USER_SESSION_MANAGEMENT_H__
|
||||
#define __USER_SESSION_MANAGEMENT_H__
|
||||
|
||||
#include <linux/hashtable.h>
|
||||
#include <linux/xarray.h>
|
||||
|
||||
#include "../smb_common.h"
|
||||
#include "../ntlmssp.h"
|
||||
|
||||
#define CIFDS_SESSION_FLAG_SMB2 BIT(1)
|
||||
|
||||
#define PREAUTH_HASHVALUE_SIZE 64
|
||||
|
||||
struct ksmbd_file_table;
|
||||
|
||||
struct channel {
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
struct ksmbd_conn *conn;
|
||||
struct list_head chann_list;
|
||||
};
|
||||
|
||||
struct preauth_session {
|
||||
__u8 Preauth_HashValue[PREAUTH_HASHVALUE_SIZE];
|
||||
u64 id;
|
||||
struct list_head preauth_entry;
|
||||
};
|
||||
|
||||
struct ksmbd_session {
|
||||
u64 id;
|
||||
|
||||
struct ksmbd_user *user;
|
||||
struct ksmbd_conn *conn;
|
||||
unsigned int sequence_number;
|
||||
unsigned int flags;
|
||||
|
||||
bool sign;
|
||||
bool enc;
|
||||
bool is_anonymous;
|
||||
|
||||
int state;
|
||||
__u8 *Preauth_HashValue;
|
||||
|
||||
struct ntlmssp_auth ntlmssp;
|
||||
char sess_key[CIFS_KEY_SIZE];
|
||||
|
||||
struct hlist_node hlist;
|
||||
struct list_head ksmbd_chann_list;
|
||||
struct xarray tree_conns;
|
||||
struct ida tree_conn_ida;
|
||||
struct list_head rpc_handle_list;
|
||||
|
||||
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
|
||||
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
|
||||
|
||||
struct list_head sessions_entry;
|
||||
struct ksmbd_file_table file_table;
|
||||
atomic_t refcnt;
|
||||
};
|
||||
|
||||
static inline int test_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
return sess->flags & bit;
|
||||
}
|
||||
|
||||
static inline void set_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
sess->flags |= bit;
|
||||
}
|
||||
|
||||
static inline void clear_session_flag(struct ksmbd_session *sess, int bit)
|
||||
{
|
||||
sess->flags &= ~bit;
|
||||
}
|
||||
|
||||
struct ksmbd_session *ksmbd_smb2_session_create(void);
|
||||
|
||||
void ksmbd_session_destroy(struct ksmbd_session *sess);
|
||||
|
||||
struct ksmbd_session *ksmbd_session_lookup_slowpath(unsigned long long id);
|
||||
struct ksmbd_session *ksmbd_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
void ksmbd_session_register(struct ksmbd_conn *conn,
|
||||
struct ksmbd_session *sess);
|
||||
void ksmbd_sessions_deregister(struct ksmbd_conn *conn);
|
||||
struct ksmbd_session *ksmbd_session_lookup_all(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
struct preauth_session *ksmbd_preauth_session_alloc(struct ksmbd_conn *conn,
|
||||
u64 sess_id);
|
||||
struct preauth_session *ksmbd_preauth_session_lookup(struct ksmbd_conn *conn,
|
||||
unsigned long long id);
|
||||
|
||||
int ksmbd_acquire_tree_conn_id(struct ksmbd_session *sess);
|
||||
void ksmbd_release_tree_conn_id(struct ksmbd_session *sess, int id);
|
||||
|
||||
int ksmbd_session_rpc_open(struct ksmbd_session *sess, char *rpc_name);
|
||||
void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
|
||||
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
|
||||
int get_session(struct ksmbd_session *sess);
|
||||
void put_session(struct ksmbd_session *sess);
|
||||
#endif /* __USER_SESSION_MANAGEMENT_H__ */
|
|
@ -0,0 +1,338 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "smb_common.h"
|
||||
#include "connection.h"
|
||||
#include "vfs.h"
|
||||
|
||||
#include "mgmt/share_config.h"
|
||||
|
||||
/**
|
||||
* match_pattern() - compare a string with a pattern which might include
|
||||
* wildcard '*' and '?'
|
||||
* TODO : implement consideration about DOS_DOT, DOS_QM and DOS_STAR
|
||||
*
|
||||
* @string: string to compare with a pattern
|
||||
* @len: string length
|
||||
* @pattern: pattern string which might include wildcard '*' and '?'
|
||||
*
|
||||
* Return: 0 if pattern matched with the string, otherwise non zero value
|
||||
*/
|
||||
int match_pattern(const char *str, size_t len, const char *pattern)
|
||||
{
|
||||
const char *s = str;
|
||||
const char *p = pattern;
|
||||
bool star = false;
|
||||
|
||||
while (*s && len) {
|
||||
switch (*p) {
|
||||
case '?':
|
||||
s++;
|
||||
len--;
|
||||
p++;
|
||||
break;
|
||||
case '*':
|
||||
star = true;
|
||||
str = s;
|
||||
if (!*++p)
|
||||
return true;
|
||||
pattern = p;
|
||||
break;
|
||||
default:
|
||||
if (tolower(*s) == tolower(*p)) {
|
||||
s++;
|
||||
len--;
|
||||
p++;
|
||||
} else {
|
||||
if (!star)
|
||||
return false;
|
||||
str++;
|
||||
s = str;
|
||||
p = pattern;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p == '*')
|
||||
++p;
|
||||
return !*p;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_char_allowed() - check for valid character
|
||||
* @ch: input character to be checked
|
||||
*
|
||||
* Return: 1 if char is allowed, otherwise 0
|
||||
*/
|
||||
static inline int is_char_allowed(char ch)
|
||||
{
|
||||
/* check for control chars, wildcards etc. */
|
||||
if (!(ch & 0x80) &&
|
||||
(ch <= 0x1f ||
|
||||
ch == '?' || ch == '"' || ch == '<' ||
|
||||
ch == '>' || ch == '|' || ch == '*'))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int ksmbd_validate_filename(char *filename)
|
||||
{
|
||||
while (*filename) {
|
||||
char c = *filename;
|
||||
|
||||
filename++;
|
||||
if (!is_char_allowed(c)) {
|
||||
ksmbd_debug(VFS, "File name validation failed: 0x%x\n", c);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksmbd_validate_stream_name(char *stream_name)
|
||||
{
|
||||
while (*stream_name) {
|
||||
char c = *stream_name;
|
||||
|
||||
stream_name++;
|
||||
if (c == '/' || c == ':' || c == '\\') {
|
||||
pr_err("Stream name validation failed: %c\n", c);
|
||||
return -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_stream_name(char *filename, char **stream_name, int *s_type)
|
||||
{
|
||||
char *stream_type;
|
||||
char *s_name;
|
||||
int rc = 0;
|
||||
|
||||
s_name = filename;
|
||||
filename = strsep(&s_name, ":");
|
||||
ksmbd_debug(SMB, "filename : %s, streams : %s\n", filename, s_name);
|
||||
if (strchr(s_name, ':')) {
|
||||
stream_type = s_name;
|
||||
s_name = strsep(&stream_type, ":");
|
||||
|
||||
rc = ksmbd_validate_stream_name(s_name);
|
||||
if (rc < 0) {
|
||||
rc = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ksmbd_debug(SMB, "stream name : %s, stream type : %s\n", s_name,
|
||||
stream_type);
|
||||
if (!strncasecmp("$data", stream_type, 5))
|
||||
*s_type = DATA_STREAM;
|
||||
else if (!strncasecmp("$index_allocation", stream_type, 17))
|
||||
*s_type = DIR_STREAM;
|
||||
else
|
||||
rc = -ENOENT;
|
||||
}
|
||||
|
||||
*stream_name = s_name;
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_to_nt_pathname() - extract and return windows path string
|
||||
* whose share directory prefix was removed from file path
|
||||
* @filename : unix filename
|
||||
* @sharepath: share path string
|
||||
*
|
||||
* Return : windows path string or error
|
||||
*/
|
||||
|
||||
char *convert_to_nt_pathname(char *filename, char *sharepath)
|
||||
{
|
||||
char *ab_pathname;
|
||||
int len, name_len;
|
||||
|
||||
name_len = strlen(filename);
|
||||
ab_pathname = kmalloc(name_len, GFP_KERNEL);
|
||||
if (!ab_pathname)
|
||||
return NULL;
|
||||
|
||||
ab_pathname[0] = '\\';
|
||||
ab_pathname[1] = '\0';
|
||||
|
||||
len = strlen(sharepath);
|
||||
if (!strncmp(filename, sharepath, len) && name_len != len) {
|
||||
strscpy(ab_pathname, &filename[len], name_len);
|
||||
ksmbd_conv_path_to_windows(ab_pathname);
|
||||
}
|
||||
|
||||
return ab_pathname;
|
||||
}
|
||||
|
||||
int get_nlink(struct kstat *st)
|
||||
{
|
||||
int nlink;
|
||||
|
||||
nlink = st->nlink;
|
||||
if (S_ISDIR(st->mode))
|
||||
nlink--;
|
||||
|
||||
return nlink;
|
||||
}
|
||||
|
||||
void ksmbd_conv_path_to_unix(char *path)
|
||||
{
|
||||
strreplace(path, '\\', '/');
|
||||
}
|
||||
|
||||
void ksmbd_strip_last_slash(char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
|
||||
while (len && path[len - 1] == '/') {
|
||||
path[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
void ksmbd_conv_path_to_windows(char *path)
|
||||
{
|
||||
strreplace(path, '/', '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_extract_sharename() - get share name from tree connect request
|
||||
* @treename: buffer containing tree name and share name
|
||||
*
|
||||
* Return: share name on success, otherwise error
|
||||
*/
|
||||
char *ksmbd_extract_sharename(char *treename)
|
||||
{
|
||||
char *name = treename;
|
||||
char *dst;
|
||||
char *pos = strrchr(name, '\\');
|
||||
|
||||
if (pos)
|
||||
name = (pos + 1);
|
||||
|
||||
/* caller has to free the memory */
|
||||
dst = kstrdup(name, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**
|
||||
* convert_to_unix_name() - convert windows name to unix format
|
||||
* @path: name to be converted
|
||||
* @tid: tree id of mathing share
|
||||
*
|
||||
* Return: converted name on success, otherwise NULL
|
||||
*/
|
||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name)
|
||||
{
|
||||
int no_slash = 0, name_len, path_len;
|
||||
char *new_name;
|
||||
|
||||
if (name[0] == '/')
|
||||
name++;
|
||||
|
||||
path_len = share->path_sz;
|
||||
name_len = strlen(name);
|
||||
new_name = kmalloc(path_len + name_len + 2, GFP_KERNEL);
|
||||
if (!new_name)
|
||||
return new_name;
|
||||
|
||||
memcpy(new_name, share->path, path_len);
|
||||
if (new_name[path_len - 1] != '/') {
|
||||
new_name[path_len] = '/';
|
||||
no_slash = 1;
|
||||
}
|
||||
|
||||
memcpy(new_name + path_len + no_slash, name, name_len);
|
||||
path_len += name_len + no_slash;
|
||||
new_name[path_len] = 0x00;
|
||||
return new_name;
|
||||
}
|
||||
|
||||
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
|
||||
const struct nls_table *local_nls,
|
||||
int *conv_len)
|
||||
{
|
||||
char *conv;
|
||||
int sz = min(4 * d_info->name_len, PATH_MAX);
|
||||
|
||||
if (!sz)
|
||||
return NULL;
|
||||
|
||||
conv = kmalloc(sz, GFP_KERNEL);
|
||||
if (!conv)
|
||||
return NULL;
|
||||
|
||||
/* XXX */
|
||||
*conv_len = smbConvertToUTF16((__le16 *)conv, d_info->name,
|
||||
d_info->name_len, local_nls, 0);
|
||||
*conv_len *= 2;
|
||||
|
||||
/* We allocate buffer twice bigger than needed. */
|
||||
conv[*conv_len] = 0x00;
|
||||
conv[*conv_len + 1] = 0x00;
|
||||
return conv;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the NT UTC (based 1601-01-01, in hundred nanosecond units)
|
||||
* into Unix UTC (based 1970-01-01, in seconds).
|
||||
*/
|
||||
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
|
||||
/* Subtract the NTFS time offset, then convert to 1s intervals. */
|
||||
s64 t = le64_to_cpu(ntutc) - NTFS_TIME_OFFSET;
|
||||
u64 abs_t;
|
||||
|
||||
/*
|
||||
* Unfortunately can not use normal 64 bit division on 32 bit arch, but
|
||||
* the alternative, do_div, does not work with negative numbers so have
|
||||
* to special case them
|
||||
*/
|
||||
if (t < 0) {
|
||||
abs_t = -t;
|
||||
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
|
||||
ts.tv_nsec = -ts.tv_nsec;
|
||||
ts.tv_sec = -abs_t;
|
||||
} else {
|
||||
abs_t = t;
|
||||
ts.tv_nsec = do_div(abs_t, 10000000) * 100;
|
||||
ts.tv_sec = abs_t;
|
||||
}
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
/* Convert the Unix UTC into NT UTC. */
|
||||
inline u64 ksmbd_UnixTimeToNT(struct timespec64 t)
|
||||
{
|
||||
/* Convert to 100ns intervals and then add the NTFS time offset. */
|
||||
return (u64)t.tv_sec * 10000000 + t.tv_nsec / 100 + NTFS_TIME_OFFSET;
|
||||
}
|
||||
|
||||
inline long long ksmbd_systime(void)
|
||||
{
|
||||
struct timespec64 ts;
|
||||
|
||||
ktime_get_real_ts64(&ts);
|
||||
return ksmbd_UnixTimeToNT(ts);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_MISC_H__
|
||||
#define __KSMBD_MISC_H__
|
||||
|
||||
struct ksmbd_share_config;
|
||||
struct nls_table;
|
||||
struct kstat;
|
||||
struct ksmbd_file;
|
||||
|
||||
int match_pattern(const char *str, size_t len, const char *pattern);
|
||||
int ksmbd_validate_filename(char *filename);
|
||||
int parse_stream_name(char *filename, char **stream_name, int *s_type);
|
||||
char *convert_to_nt_pathname(char *filename, char *sharepath);
|
||||
int get_nlink(struct kstat *st);
|
||||
void ksmbd_conv_path_to_unix(char *path);
|
||||
void ksmbd_strip_last_slash(char *path);
|
||||
void ksmbd_conv_path_to_windows(char *path);
|
||||
char *ksmbd_extract_sharename(char *treename);
|
||||
char *convert_to_unix_name(struct ksmbd_share_config *share, char *name);
|
||||
|
||||
#define KSMBD_DIR_INFO_ALIGNMENT 8
|
||||
struct ksmbd_dir_info;
|
||||
char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
|
||||
const struct nls_table *local_nls,
|
||||
int *conv_len);
|
||||
|
||||
#define NTFS_TIME_OFFSET ((u64)(369 * 365 + 89) * 24 * 3600 * 10000000)
|
||||
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc);
|
||||
u64 ksmbd_UnixTimeToNT(struct timespec64 t);
|
||||
long long ksmbd_systime(void);
|
||||
#endif /* __KSMBD_MISC_H__ */
|
|
@ -0,0 +1,345 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2021 Samsung Electronics Co., Ltd.
|
||||
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "ndr.h"
|
||||
|
||||
static inline char *ndr_get_field(struct ndr *n)
|
||||
{
|
||||
return n->data + n->offset;
|
||||
}
|
||||
|
||||
static int try_to_realloc_ndr_blob(struct ndr *n, size_t sz)
|
||||
{
|
||||
char *data;
|
||||
|
||||
data = krealloc(n->data, n->offset + sz + 1024, GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
n->data = data;
|
||||
n->length += 1024;
|
||||
memset(n->data + n->offset, 0, 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ndr_write_int16(struct ndr *n, __u16 value)
|
||||
{
|
||||
if (n->length <= n->offset + sizeof(value))
|
||||
try_to_realloc_ndr_blob(n, sizeof(value));
|
||||
|
||||
*(__le16 *)ndr_get_field(n) = cpu_to_le16(value);
|
||||
n->offset += sizeof(value);
|
||||
}
|
||||
|
||||
static void ndr_write_int32(struct ndr *n, __u32 value)
|
||||
{
|
||||
if (n->length <= n->offset + sizeof(value))
|
||||
try_to_realloc_ndr_blob(n, sizeof(value));
|
||||
|
||||
*(__le32 *)ndr_get_field(n) = cpu_to_le32(value);
|
||||
n->offset += sizeof(value);
|
||||
}
|
||||
|
||||
static void ndr_write_int64(struct ndr *n, __u64 value)
|
||||
{
|
||||
if (n->length <= n->offset + sizeof(value))
|
||||
try_to_realloc_ndr_blob(n, sizeof(value));
|
||||
|
||||
*(__le64 *)ndr_get_field(n) = cpu_to_le64(value);
|
||||
n->offset += sizeof(value);
|
||||
}
|
||||
|
||||
static int ndr_write_bytes(struct ndr *n, void *value, size_t sz)
|
||||
{
|
||||
if (n->length <= n->offset + sz)
|
||||
try_to_realloc_ndr_blob(n, sz);
|
||||
|
||||
memcpy(ndr_get_field(n), value, sz);
|
||||
n->offset += sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndr_write_string(struct ndr *n, char *value)
|
||||
{
|
||||
size_t sz;
|
||||
|
||||
sz = strlen(value) + 1;
|
||||
if (n->length <= n->offset + sz)
|
||||
try_to_realloc_ndr_blob(n, sz);
|
||||
|
||||
memcpy(ndr_get_field(n), value, sz);
|
||||
n->offset += sz;
|
||||
n->offset = ALIGN(n->offset, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndr_read_string(struct ndr *n, void *value, size_t sz)
|
||||
{
|
||||
int len = strnlen(ndr_get_field(n), sz);
|
||||
|
||||
memcpy(value, ndr_get_field(n), len);
|
||||
len++;
|
||||
n->offset += len;
|
||||
n->offset = ALIGN(n->offset, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndr_read_bytes(struct ndr *n, void *value, size_t sz)
|
||||
{
|
||||
memcpy(value, ndr_get_field(n), sz);
|
||||
n->offset += sz;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __u16 ndr_read_int16(struct ndr *n)
|
||||
{
|
||||
__u16 ret;
|
||||
|
||||
ret = le16_to_cpu(*(__le16 *)ndr_get_field(n));
|
||||
n->offset += sizeof(__u16);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __u32 ndr_read_int32(struct ndr *n)
|
||||
{
|
||||
__u32 ret;
|
||||
|
||||
ret = le32_to_cpu(*(__le32 *)ndr_get_field(n));
|
||||
n->offset += sizeof(__u32);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __u64 ndr_read_int64(struct ndr *n)
|
||||
{
|
||||
__u64 ret;
|
||||
|
||||
ret = le64_to_cpu(*(__le64 *)ndr_get_field(n));
|
||||
n->offset += sizeof(__u64);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
|
||||
{
|
||||
char hex_attr[12] = {0};
|
||||
|
||||
n->offset = 0;
|
||||
n->length = 1024;
|
||||
n->data = kzalloc(n->length, GFP_KERNEL);
|
||||
if (!n->data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (da->version == 3) {
|
||||
snprintf(hex_attr, 10, "0x%x", da->attr);
|
||||
ndr_write_string(n, hex_attr);
|
||||
} else {
|
||||
ndr_write_string(n, "");
|
||||
}
|
||||
ndr_write_int16(n, da->version);
|
||||
ndr_write_int32(n, da->version);
|
||||
|
||||
ndr_write_int32(n, da->flags);
|
||||
ndr_write_int32(n, da->attr);
|
||||
if (da->version == 3) {
|
||||
ndr_write_int32(n, da->ea_size);
|
||||
ndr_write_int64(n, da->size);
|
||||
ndr_write_int64(n, da->alloc_size);
|
||||
} else {
|
||||
ndr_write_int64(n, da->itime);
|
||||
}
|
||||
ndr_write_int64(n, da->create_time);
|
||||
if (da->version == 3)
|
||||
ndr_write_int64(n, da->change_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da)
|
||||
{
|
||||
char *hex_attr;
|
||||
int version2;
|
||||
|
||||
hex_attr = kzalloc(n->length, GFP_KERNEL);
|
||||
if (!hex_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
n->offset = 0;
|
||||
ndr_read_string(n, hex_attr, n->length);
|
||||
kfree(hex_attr);
|
||||
da->version = ndr_read_int16(n);
|
||||
|
||||
if (da->version != 3 && da->version != 4) {
|
||||
pr_err("v%d version is not supported\n", da->version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version2 = ndr_read_int32(n);
|
||||
if (da->version != version2) {
|
||||
pr_err("ndr version mismatched(version: %d, version2: %d)\n",
|
||||
da->version, version2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ndr_read_int32(n);
|
||||
da->attr = ndr_read_int32(n);
|
||||
if (da->version == 4) {
|
||||
da->itime = ndr_read_int64(n);
|
||||
da->create_time = ndr_read_int64(n);
|
||||
} else {
|
||||
ndr_read_int32(n);
|
||||
ndr_read_int64(n);
|
||||
ndr_read_int64(n);
|
||||
da->create_time = ndr_read_int64(n);
|
||||
ndr_read_int64(n);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndr_encode_posix_acl_entry(struct ndr *n, struct xattr_smb_acl *acl)
|
||||
{
|
||||
int i;
|
||||
|
||||
ndr_write_int32(n, acl->count);
|
||||
n->offset = ALIGN(n->offset, 8);
|
||||
ndr_write_int32(n, acl->count);
|
||||
ndr_write_int32(n, 0);
|
||||
|
||||
for (i = 0; i < acl->count; i++) {
|
||||
n->offset = ALIGN(n->offset, 8);
|
||||
ndr_write_int16(n, acl->entries[i].type);
|
||||
ndr_write_int16(n, acl->entries[i].type);
|
||||
|
||||
if (acl->entries[i].type == SMB_ACL_USER) {
|
||||
n->offset = ALIGN(n->offset, 8);
|
||||
ndr_write_int64(n, acl->entries[i].uid);
|
||||
} else if (acl->entries[i].type == SMB_ACL_GROUP) {
|
||||
n->offset = ALIGN(n->offset, 8);
|
||||
ndr_write_int64(n, acl->entries[i].gid);
|
||||
}
|
||||
|
||||
/* push permission */
|
||||
ndr_write_int32(n, acl->entries[i].perm);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ndr_encode_posix_acl(struct ndr *n,
|
||||
struct user_namespace *user_ns,
|
||||
struct inode *inode,
|
||||
struct xattr_smb_acl *acl,
|
||||
struct xattr_smb_acl *def_acl)
|
||||
{
|
||||
int ref_id = 0x00020000;
|
||||
|
||||
n->offset = 0;
|
||||
n->length = 1024;
|
||||
n->data = kzalloc(n->length, GFP_KERNEL);
|
||||
if (!n->data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (acl) {
|
||||
/* ACL ACCESS */
|
||||
ndr_write_int32(n, ref_id);
|
||||
ref_id += 4;
|
||||
} else {
|
||||
ndr_write_int32(n, 0);
|
||||
}
|
||||
|
||||
if (def_acl) {
|
||||
/* DEFAULT ACL ACCESS */
|
||||
ndr_write_int32(n, ref_id);
|
||||
ref_id += 4;
|
||||
} else {
|
||||
ndr_write_int32(n, 0);
|
||||
}
|
||||
|
||||
ndr_write_int64(n, from_kuid(user_ns, inode->i_uid));
|
||||
ndr_write_int64(n, from_kgid(user_ns, inode->i_gid));
|
||||
ndr_write_int32(n, inode->i_mode);
|
||||
|
||||
if (acl) {
|
||||
ndr_encode_posix_acl_entry(n, acl);
|
||||
if (def_acl)
|
||||
ndr_encode_posix_acl_entry(n, def_acl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
|
||||
{
|
||||
int ref_id = 0x00020004;
|
||||
|
||||
n->offset = 0;
|
||||
n->length = 2048;
|
||||
n->data = kzalloc(n->length, GFP_KERNEL);
|
||||
if (!n->data)
|
||||
return -ENOMEM;
|
||||
|
||||
ndr_write_int16(n, acl->version);
|
||||
ndr_write_int32(n, acl->version);
|
||||
ndr_write_int16(n, 2);
|
||||
ndr_write_int32(n, ref_id);
|
||||
|
||||
/* push hash type and hash 64bytes */
|
||||
ndr_write_int16(n, acl->hash_type);
|
||||
ndr_write_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
|
||||
ndr_write_bytes(n, acl->desc, acl->desc_len);
|
||||
ndr_write_int64(n, acl->current_time);
|
||||
ndr_write_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
|
||||
|
||||
/* push ndr for security descriptor */
|
||||
ndr_write_bytes(n, acl->sd_buf, acl->sd_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl)
|
||||
{
|
||||
int version2;
|
||||
|
||||
n->offset = 0;
|
||||
acl->version = ndr_read_int16(n);
|
||||
if (acl->version != 4) {
|
||||
pr_err("v%d version is not supported\n", acl->version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version2 = ndr_read_int32(n);
|
||||
if (acl->version != version2) {
|
||||
pr_err("ndr version mismatched(version: %d, version2: %d)\n",
|
||||
acl->version, version2);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read Level */
|
||||
ndr_read_int16(n);
|
||||
/* Read Ref Id */
|
||||
ndr_read_int32(n);
|
||||
acl->hash_type = ndr_read_int16(n);
|
||||
ndr_read_bytes(n, acl->hash, XATTR_SD_HASH_SIZE);
|
||||
|
||||
ndr_read_bytes(n, acl->desc, 10);
|
||||
if (strncmp(acl->desc, "posix_acl", 9)) {
|
||||
pr_err("Invalid acl description : %s\n", acl->desc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Read Time */
|
||||
ndr_read_int64(n);
|
||||
/* Read Posix ACL hash */
|
||||
ndr_read_bytes(n, acl->posix_acl_hash, XATTR_SD_HASH_SIZE);
|
||||
acl->sd_size = n->length - n->offset;
|
||||
acl->sd_buf = kzalloc(acl->sd_size, GFP_KERNEL);
|
||||
if (!acl->sd_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ndr_read_bytes(n, acl->sd_buf, acl->sd_size);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2020 Samsung Electronics Co., Ltd.
|
||||
* Author(s): Namjae Jeon <linkinjeon@kernel.org>
|
||||
*/
|
||||
|
||||
struct ndr {
|
||||
char *data;
|
||||
int offset;
|
||||
int length;
|
||||
};
|
||||
|
||||
#define NDR_NTSD_OFFSETOF 0xA0
|
||||
|
||||
int ndr_encode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da);
|
||||
int ndr_decode_dos_attr(struct ndr *n, struct xattr_dos_attrib *da);
|
||||
int ndr_encode_posix_acl(struct ndr *n, struct user_namespace *user_ns,
|
||||
struct inode *inode, struct xattr_smb_acl *acl,
|
||||
struct xattr_smb_acl *def_acl);
|
||||
int ndr_encode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl);
|
||||
int ndr_encode_v3_ntacl(struct ndr *n, struct xattr_ntacl *acl);
|
||||
int ndr_decode_v4_ntacl(struct ndr *n, struct xattr_ntacl *acl);
|
|
@ -0,0 +1,543 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Unix SMB/Netbios implementation.
|
||||
* Version 1.9.
|
||||
* NT error code constants
|
||||
* Copyright (C) Andrew Tridgell 1992-2000
|
||||
* Copyright (C) John H Terpstra 1996-2000
|
||||
* Copyright (C) Luke Kenneth Casson Leighton 1996-2000
|
||||
* Copyright (C) Paul Ashton 1998-2000
|
||||
*/
|
||||
|
||||
#ifndef _NTERR_H
|
||||
#define _NTERR_H
|
||||
|
||||
/* Win32 Status codes. */
|
||||
#define NT_STATUS_MORE_ENTRIES 0x0105
|
||||
#define NT_ERROR_INVALID_PARAMETER 0x0057
|
||||
#define NT_ERROR_INSUFFICIENT_BUFFER 0x007a
|
||||
#define NT_STATUS_1804 0x070c
|
||||
#define NT_STATUS_NOTIFY_ENUM_DIR 0x010c
|
||||
#define NT_STATUS_INVALID_LOCK_RANGE (0xC0000000 | 0x01a1)
|
||||
/*
|
||||
* Win32 Error codes extracted using a loop in smbclient then printing a netmon
|
||||
* sniff to a file.
|
||||
*/
|
||||
|
||||
#define NT_STATUS_OK 0x0000
|
||||
#define NT_STATUS_SOME_UNMAPPED 0x0107
|
||||
#define NT_STATUS_BUFFER_OVERFLOW 0x80000005
|
||||
#define NT_STATUS_NO_MORE_ENTRIES 0x8000001a
|
||||
#define NT_STATUS_MEDIA_CHANGED 0x8000001c
|
||||
#define NT_STATUS_END_OF_MEDIA 0x8000001e
|
||||
#define NT_STATUS_MEDIA_CHECK 0x80000020
|
||||
#define NT_STATUS_NO_DATA_DETECTED 0x8000001c
|
||||
#define NT_STATUS_STOPPED_ON_SYMLINK 0x8000002d
|
||||
#define NT_STATUS_DEVICE_REQUIRES_CLEANING 0x80000288
|
||||
#define NT_STATUS_DEVICE_DOOR_OPEN 0x80000288
|
||||
#define NT_STATUS_UNSUCCESSFUL (0xC0000000 | 0x0001)
|
||||
#define NT_STATUS_NOT_IMPLEMENTED (0xC0000000 | 0x0002)
|
||||
#define NT_STATUS_INVALID_INFO_CLASS (0xC0000000 | 0x0003)
|
||||
#define NT_STATUS_INFO_LENGTH_MISMATCH (0xC0000000 | 0x0004)
|
||||
#define NT_STATUS_ACCESS_VIOLATION (0xC0000000 | 0x0005)
|
||||
#define NT_STATUS_IN_PAGE_ERROR (0xC0000000 | 0x0006)
|
||||
#define NT_STATUS_PAGEFILE_QUOTA (0xC0000000 | 0x0007)
|
||||
#define NT_STATUS_INVALID_HANDLE (0xC0000000 | 0x0008)
|
||||
#define NT_STATUS_BAD_INITIAL_STACK (0xC0000000 | 0x0009)
|
||||
#define NT_STATUS_BAD_INITIAL_PC (0xC0000000 | 0x000a)
|
||||
#define NT_STATUS_INVALID_CID (0xC0000000 | 0x000b)
|
||||
#define NT_STATUS_TIMER_NOT_CANCELED (0xC0000000 | 0x000c)
|
||||
#define NT_STATUS_INVALID_PARAMETER (0xC0000000 | 0x000d)
|
||||
#define NT_STATUS_NO_SUCH_DEVICE (0xC0000000 | 0x000e)
|
||||
#define NT_STATUS_NO_SUCH_FILE (0xC0000000 | 0x000f)
|
||||
#define NT_STATUS_INVALID_DEVICE_REQUEST (0xC0000000 | 0x0010)
|
||||
#define NT_STATUS_END_OF_FILE (0xC0000000 | 0x0011)
|
||||
#define NT_STATUS_WRONG_VOLUME (0xC0000000 | 0x0012)
|
||||
#define NT_STATUS_NO_MEDIA_IN_DEVICE (0xC0000000 | 0x0013)
|
||||
#define NT_STATUS_UNRECOGNIZED_MEDIA (0xC0000000 | 0x0014)
|
||||
#define NT_STATUS_NONEXISTENT_SECTOR (0xC0000000 | 0x0015)
|
||||
#define NT_STATUS_MORE_PROCESSING_REQUIRED (0xC0000000 | 0x0016)
|
||||
#define NT_STATUS_NO_MEMORY (0xC0000000 | 0x0017)
|
||||
#define NT_STATUS_CONFLICTING_ADDRESSES (0xC0000000 | 0x0018)
|
||||
#define NT_STATUS_NOT_MAPPED_VIEW (0xC0000000 | 0x0019)
|
||||
#define NT_STATUS_UNABLE_TO_FREE_VM (0x80000000 | 0x001a)
|
||||
#define NT_STATUS_UNABLE_TO_DELETE_SECTION (0xC0000000 | 0x001b)
|
||||
#define NT_STATUS_INVALID_SYSTEM_SERVICE (0xC0000000 | 0x001c)
|
||||
#define NT_STATUS_ILLEGAL_INSTRUCTION (0xC0000000 | 0x001d)
|
||||
#define NT_STATUS_INVALID_LOCK_SEQUENCE (0xC0000000 | 0x001e)
|
||||
#define NT_STATUS_INVALID_VIEW_SIZE (0xC0000000 | 0x001f)
|
||||
#define NT_STATUS_INVALID_FILE_FOR_SECTION (0xC0000000 | 0x0020)
|
||||
#define NT_STATUS_ALREADY_COMMITTED (0xC0000000 | 0x0021)
|
||||
#define NT_STATUS_ACCESS_DENIED (0xC0000000 | 0x0022)
|
||||
#define NT_STATUS_BUFFER_TOO_SMALL (0xC0000000 | 0x0023)
|
||||
#define NT_STATUS_OBJECT_TYPE_MISMATCH (0xC0000000 | 0x0024)
|
||||
#define NT_STATUS_NONCONTINUABLE_EXCEPTION (0xC0000000 | 0x0025)
|
||||
#define NT_STATUS_INVALID_DISPOSITION (0xC0000000 | 0x0026)
|
||||
#define NT_STATUS_UNWIND (0xC0000000 | 0x0027)
|
||||
#define NT_STATUS_BAD_STACK (0xC0000000 | 0x0028)
|
||||
#define NT_STATUS_INVALID_UNWIND_TARGET (0xC0000000 | 0x0029)
|
||||
#define NT_STATUS_NOT_LOCKED (0xC0000000 | 0x002a)
|
||||
#define NT_STATUS_PARITY_ERROR (0xC0000000 | 0x002b)
|
||||
#define NT_STATUS_UNABLE_TO_DECOMMIT_VM (0xC0000000 | 0x002c)
|
||||
#define NT_STATUS_NOT_COMMITTED (0xC0000000 | 0x002d)
|
||||
#define NT_STATUS_INVALID_PORT_ATTRIBUTES (0xC0000000 | 0x002e)
|
||||
#define NT_STATUS_PORT_MESSAGE_TOO_LONG (0xC0000000 | 0x002f)
|
||||
#define NT_STATUS_INVALID_PARAMETER_MIX (0xC0000000 | 0x0030)
|
||||
#define NT_STATUS_INVALID_QUOTA_LOWER (0xC0000000 | 0x0031)
|
||||
#define NT_STATUS_DISK_CORRUPT_ERROR (0xC0000000 | 0x0032)
|
||||
#define NT_STATUS_OBJECT_NAME_INVALID (0xC0000000 | 0x0033)
|
||||
#define NT_STATUS_OBJECT_NAME_NOT_FOUND (0xC0000000 | 0x0034)
|
||||
#define NT_STATUS_OBJECT_NAME_COLLISION (0xC0000000 | 0x0035)
|
||||
#define NT_STATUS_HANDLE_NOT_WAITABLE (0xC0000000 | 0x0036)
|
||||
#define NT_STATUS_PORT_DISCONNECTED (0xC0000000 | 0x0037)
|
||||
#define NT_STATUS_DEVICE_ALREADY_ATTACHED (0xC0000000 | 0x0038)
|
||||
#define NT_STATUS_OBJECT_PATH_INVALID (0xC0000000 | 0x0039)
|
||||
#define NT_STATUS_OBJECT_PATH_NOT_FOUND (0xC0000000 | 0x003a)
|
||||
#define NT_STATUS_OBJECT_PATH_SYNTAX_BAD (0xC0000000 | 0x003b)
|
||||
#define NT_STATUS_DATA_OVERRUN (0xC0000000 | 0x003c)
|
||||
#define NT_STATUS_DATA_LATE_ERROR (0xC0000000 | 0x003d)
|
||||
#define NT_STATUS_DATA_ERROR (0xC0000000 | 0x003e)
|
||||
#define NT_STATUS_CRC_ERROR (0xC0000000 | 0x003f)
|
||||
#define NT_STATUS_SECTION_TOO_BIG (0xC0000000 | 0x0040)
|
||||
#define NT_STATUS_PORT_CONNECTION_REFUSED (0xC0000000 | 0x0041)
|
||||
#define NT_STATUS_INVALID_PORT_HANDLE (0xC0000000 | 0x0042)
|
||||
#define NT_STATUS_SHARING_VIOLATION (0xC0000000 | 0x0043)
|
||||
#define NT_STATUS_QUOTA_EXCEEDED (0xC0000000 | 0x0044)
|
||||
#define NT_STATUS_INVALID_PAGE_PROTECTION (0xC0000000 | 0x0045)
|
||||
#define NT_STATUS_MUTANT_NOT_OWNED (0xC0000000 | 0x0046)
|
||||
#define NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED (0xC0000000 | 0x0047)
|
||||
#define NT_STATUS_PORT_ALREADY_SET (0xC0000000 | 0x0048)
|
||||
#define NT_STATUS_SECTION_NOT_IMAGE (0xC0000000 | 0x0049)
|
||||
#define NT_STATUS_SUSPEND_COUNT_EXCEEDED (0xC0000000 | 0x004a)
|
||||
#define NT_STATUS_THREAD_IS_TERMINATING (0xC0000000 | 0x004b)
|
||||
#define NT_STATUS_BAD_WORKING_SET_LIMIT (0xC0000000 | 0x004c)
|
||||
#define NT_STATUS_INCOMPATIBLE_FILE_MAP (0xC0000000 | 0x004d)
|
||||
#define NT_STATUS_SECTION_PROTECTION (0xC0000000 | 0x004e)
|
||||
#define NT_STATUS_EAS_NOT_SUPPORTED (0xC0000000 | 0x004f)
|
||||
#define NT_STATUS_EA_TOO_LARGE (0xC0000000 | 0x0050)
|
||||
#define NT_STATUS_NONEXISTENT_EA_ENTRY (0xC0000000 | 0x0051)
|
||||
#define NT_STATUS_NO_EAS_ON_FILE (0xC0000000 | 0x0052)
|
||||
#define NT_STATUS_EA_CORRUPT_ERROR (0xC0000000 | 0x0053)
|
||||
#define NT_STATUS_FILE_LOCK_CONFLICT (0xC0000000 | 0x0054)
|
||||
#define NT_STATUS_LOCK_NOT_GRANTED (0xC0000000 | 0x0055)
|
||||
#define NT_STATUS_DELETE_PENDING (0xC0000000 | 0x0056)
|
||||
#define NT_STATUS_CTL_FILE_NOT_SUPPORTED (0xC0000000 | 0x0057)
|
||||
#define NT_STATUS_UNKNOWN_REVISION (0xC0000000 | 0x0058)
|
||||
#define NT_STATUS_REVISION_MISMATCH (0xC0000000 | 0x0059)
|
||||
#define NT_STATUS_INVALID_OWNER (0xC0000000 | 0x005a)
|
||||
#define NT_STATUS_INVALID_PRIMARY_GROUP (0xC0000000 | 0x005b)
|
||||
#define NT_STATUS_NO_IMPERSONATION_TOKEN (0xC0000000 | 0x005c)
|
||||
#define NT_STATUS_CANT_DISABLE_MANDATORY (0xC0000000 | 0x005d)
|
||||
#define NT_STATUS_NO_LOGON_SERVERS (0xC0000000 | 0x005e)
|
||||
#define NT_STATUS_NO_SUCH_LOGON_SESSION (0xC0000000 | 0x005f)
|
||||
#define NT_STATUS_NO_SUCH_PRIVILEGE (0xC0000000 | 0x0060)
|
||||
#define NT_STATUS_PRIVILEGE_NOT_HELD (0xC0000000 | 0x0061)
|
||||
#define NT_STATUS_INVALID_ACCOUNT_NAME (0xC0000000 | 0x0062)
|
||||
#define NT_STATUS_USER_EXISTS (0xC0000000 | 0x0063)
|
||||
#define NT_STATUS_NO_SUCH_USER (0xC0000000 | 0x0064)
|
||||
#define NT_STATUS_GROUP_EXISTS (0xC0000000 | 0x0065)
|
||||
#define NT_STATUS_NO_SUCH_GROUP (0xC0000000 | 0x0066)
|
||||
#define NT_STATUS_MEMBER_IN_GROUP (0xC0000000 | 0x0067)
|
||||
#define NT_STATUS_MEMBER_NOT_IN_GROUP (0xC0000000 | 0x0068)
|
||||
#define NT_STATUS_LAST_ADMIN (0xC0000000 | 0x0069)
|
||||
#define NT_STATUS_WRONG_PASSWORD (0xC0000000 | 0x006a)
|
||||
#define NT_STATUS_ILL_FORMED_PASSWORD (0xC0000000 | 0x006b)
|
||||
#define NT_STATUS_PASSWORD_RESTRICTION (0xC0000000 | 0x006c)
|
||||
#define NT_STATUS_LOGON_FAILURE (0xC0000000 | 0x006d)
|
||||
#define NT_STATUS_ACCOUNT_RESTRICTION (0xC0000000 | 0x006e)
|
||||
#define NT_STATUS_INVALID_LOGON_HOURS (0xC0000000 | 0x006f)
|
||||
#define NT_STATUS_INVALID_WORKSTATION (0xC0000000 | 0x0070)
|
||||
#define NT_STATUS_PASSWORD_EXPIRED (0xC0000000 | 0x0071)
|
||||
#define NT_STATUS_ACCOUNT_DISABLED (0xC0000000 | 0x0072)
|
||||
#define NT_STATUS_NONE_MAPPED (0xC0000000 | 0x0073)
|
||||
#define NT_STATUS_TOO_MANY_LUIDS_REQUESTED (0xC0000000 | 0x0074)
|
||||
#define NT_STATUS_LUIDS_EXHAUSTED (0xC0000000 | 0x0075)
|
||||
#define NT_STATUS_INVALID_SUB_AUTHORITY (0xC0000000 | 0x0076)
|
||||
#define NT_STATUS_INVALID_ACL (0xC0000000 | 0x0077)
|
||||
#define NT_STATUS_INVALID_SID (0xC0000000 | 0x0078)
|
||||
#define NT_STATUS_INVALID_SECURITY_DESCR (0xC0000000 | 0x0079)
|
||||
#define NT_STATUS_PROCEDURE_NOT_FOUND (0xC0000000 | 0x007a)
|
||||
#define NT_STATUS_INVALID_IMAGE_FORMAT (0xC0000000 | 0x007b)
|
||||
#define NT_STATUS_NO_TOKEN (0xC0000000 | 0x007c)
|
||||
#define NT_STATUS_BAD_INHERITANCE_ACL (0xC0000000 | 0x007d)
|
||||
#define NT_STATUS_RANGE_NOT_LOCKED (0xC0000000 | 0x007e)
|
||||
#define NT_STATUS_DISK_FULL (0xC0000000 | 0x007f)
|
||||
#define NT_STATUS_SERVER_DISABLED (0xC0000000 | 0x0080)
|
||||
#define NT_STATUS_SERVER_NOT_DISABLED (0xC0000000 | 0x0081)
|
||||
#define NT_STATUS_TOO_MANY_GUIDS_REQUESTED (0xC0000000 | 0x0082)
|
||||
#define NT_STATUS_GUIDS_EXHAUSTED (0xC0000000 | 0x0083)
|
||||
#define NT_STATUS_INVALID_ID_AUTHORITY (0xC0000000 | 0x0084)
|
||||
#define NT_STATUS_AGENTS_EXHAUSTED (0xC0000000 | 0x0085)
|
||||
#define NT_STATUS_INVALID_VOLUME_LABEL (0xC0000000 | 0x0086)
|
||||
#define NT_STATUS_SECTION_NOT_EXTENDED (0xC0000000 | 0x0087)
|
||||
#define NT_STATUS_NOT_MAPPED_DATA (0xC0000000 | 0x0088)
|
||||
#define NT_STATUS_RESOURCE_DATA_NOT_FOUND (0xC0000000 | 0x0089)
|
||||
#define NT_STATUS_RESOURCE_TYPE_NOT_FOUND (0xC0000000 | 0x008a)
|
||||
#define NT_STATUS_RESOURCE_NAME_NOT_FOUND (0xC0000000 | 0x008b)
|
||||
#define NT_STATUS_ARRAY_BOUNDS_EXCEEDED (0xC0000000 | 0x008c)
|
||||
#define NT_STATUS_FLOAT_DENORMAL_OPERAND (0xC0000000 | 0x008d)
|
||||
#define NT_STATUS_FLOAT_DIVIDE_BY_ZERO (0xC0000000 | 0x008e)
|
||||
#define NT_STATUS_FLOAT_INEXACT_RESULT (0xC0000000 | 0x008f)
|
||||
#define NT_STATUS_FLOAT_INVALID_OPERATION (0xC0000000 | 0x0090)
|
||||
#define NT_STATUS_FLOAT_OVERFLOW (0xC0000000 | 0x0091)
|
||||
#define NT_STATUS_FLOAT_STACK_CHECK (0xC0000000 | 0x0092)
|
||||
#define NT_STATUS_FLOAT_UNDERFLOW (0xC0000000 | 0x0093)
|
||||
#define NT_STATUS_INTEGER_DIVIDE_BY_ZERO (0xC0000000 | 0x0094)
|
||||
#define NT_STATUS_INTEGER_OVERFLOW (0xC0000000 | 0x0095)
|
||||
#define NT_STATUS_PRIVILEGED_INSTRUCTION (0xC0000000 | 0x0096)
|
||||
#define NT_STATUS_TOO_MANY_PAGING_FILES (0xC0000000 | 0x0097)
|
||||
#define NT_STATUS_FILE_INVALID (0xC0000000 | 0x0098)
|
||||
#define NT_STATUS_ALLOTTED_SPACE_EXCEEDED (0xC0000000 | 0x0099)
|
||||
#define NT_STATUS_INSUFFICIENT_RESOURCES (0xC0000000 | 0x009a)
|
||||
#define NT_STATUS_DFS_EXIT_PATH_FOUND (0xC0000000 | 0x009b)
|
||||
#define NT_STATUS_DEVICE_DATA_ERROR (0xC0000000 | 0x009c)
|
||||
#define NT_STATUS_DEVICE_NOT_CONNECTED (0xC0000000 | 0x009d)
|
||||
#define NT_STATUS_DEVICE_POWER_FAILURE (0xC0000000 | 0x009e)
|
||||
#define NT_STATUS_FREE_VM_NOT_AT_BASE (0xC0000000 | 0x009f)
|
||||
#define NT_STATUS_MEMORY_NOT_ALLOCATED (0xC0000000 | 0x00a0)
|
||||
#define NT_STATUS_WORKING_SET_QUOTA (0xC0000000 | 0x00a1)
|
||||
#define NT_STATUS_MEDIA_WRITE_PROTECTED (0xC0000000 | 0x00a2)
|
||||
#define NT_STATUS_DEVICE_NOT_READY (0xC0000000 | 0x00a3)
|
||||
#define NT_STATUS_INVALID_GROUP_ATTRIBUTES (0xC0000000 | 0x00a4)
|
||||
#define NT_STATUS_BAD_IMPERSONATION_LEVEL (0xC0000000 | 0x00a5)
|
||||
#define NT_STATUS_CANT_OPEN_ANONYMOUS (0xC0000000 | 0x00a6)
|
||||
#define NT_STATUS_BAD_VALIDATION_CLASS (0xC0000000 | 0x00a7)
|
||||
#define NT_STATUS_BAD_TOKEN_TYPE (0xC0000000 | 0x00a8)
|
||||
#define NT_STATUS_BAD_MASTER_BOOT_RECORD (0xC0000000 | 0x00a9)
|
||||
#define NT_STATUS_INSTRUCTION_MISALIGNMENT (0xC0000000 | 0x00aa)
|
||||
#define NT_STATUS_INSTANCE_NOT_AVAILABLE (0xC0000000 | 0x00ab)
|
||||
#define NT_STATUS_PIPE_NOT_AVAILABLE (0xC0000000 | 0x00ac)
|
||||
#define NT_STATUS_INVALID_PIPE_STATE (0xC0000000 | 0x00ad)
|
||||
#define NT_STATUS_PIPE_BUSY (0xC0000000 | 0x00ae)
|
||||
#define NT_STATUS_ILLEGAL_FUNCTION (0xC0000000 | 0x00af)
|
||||
#define NT_STATUS_PIPE_DISCONNECTED (0xC0000000 | 0x00b0)
|
||||
#define NT_STATUS_PIPE_CLOSING (0xC0000000 | 0x00b1)
|
||||
#define NT_STATUS_PIPE_CONNECTED (0xC0000000 | 0x00b2)
|
||||
#define NT_STATUS_PIPE_LISTENING (0xC0000000 | 0x00b3)
|
||||
#define NT_STATUS_INVALID_READ_MODE (0xC0000000 | 0x00b4)
|
||||
#define NT_STATUS_IO_TIMEOUT (0xC0000000 | 0x00b5)
|
||||
#define NT_STATUS_FILE_FORCED_CLOSED (0xC0000000 | 0x00b6)
|
||||
#define NT_STATUS_PROFILING_NOT_STARTED (0xC0000000 | 0x00b7)
|
||||
#define NT_STATUS_PROFILING_NOT_STOPPED (0xC0000000 | 0x00b8)
|
||||
#define NT_STATUS_COULD_NOT_INTERPRET (0xC0000000 | 0x00b9)
|
||||
#define NT_STATUS_FILE_IS_A_DIRECTORY (0xC0000000 | 0x00ba)
|
||||
#define NT_STATUS_NOT_SUPPORTED (0xC0000000 | 0x00bb)
|
||||
#define NT_STATUS_REMOTE_NOT_LISTENING (0xC0000000 | 0x00bc)
|
||||
#define NT_STATUS_DUPLICATE_NAME (0xC0000000 | 0x00bd)
|
||||
#define NT_STATUS_BAD_NETWORK_PATH (0xC0000000 | 0x00be)
|
||||
#define NT_STATUS_NETWORK_BUSY (0xC0000000 | 0x00bf)
|
||||
#define NT_STATUS_DEVICE_DOES_NOT_EXIST (0xC0000000 | 0x00c0)
|
||||
#define NT_STATUS_TOO_MANY_COMMANDS (0xC0000000 | 0x00c1)
|
||||
#define NT_STATUS_ADAPTER_HARDWARE_ERROR (0xC0000000 | 0x00c2)
|
||||
#define NT_STATUS_INVALID_NETWORK_RESPONSE (0xC0000000 | 0x00c3)
|
||||
#define NT_STATUS_UNEXPECTED_NETWORK_ERROR (0xC0000000 | 0x00c4)
|
||||
#define NT_STATUS_BAD_REMOTE_ADAPTER (0xC0000000 | 0x00c5)
|
||||
#define NT_STATUS_PRINT_QUEUE_FULL (0xC0000000 | 0x00c6)
|
||||
#define NT_STATUS_NO_SPOOL_SPACE (0xC0000000 | 0x00c7)
|
||||
#define NT_STATUS_PRINT_CANCELLED (0xC0000000 | 0x00c8)
|
||||
#define NT_STATUS_NETWORK_NAME_DELETED (0xC0000000 | 0x00c9)
|
||||
#define NT_STATUS_NETWORK_ACCESS_DENIED (0xC0000000 | 0x00ca)
|
||||
#define NT_STATUS_BAD_DEVICE_TYPE (0xC0000000 | 0x00cb)
|
||||
#define NT_STATUS_BAD_NETWORK_NAME (0xC0000000 | 0x00cc)
|
||||
#define NT_STATUS_TOO_MANY_NAMES (0xC0000000 | 0x00cd)
|
||||
#define NT_STATUS_TOO_MANY_SESSIONS (0xC0000000 | 0x00ce)
|
||||
#define NT_STATUS_SHARING_PAUSED (0xC0000000 | 0x00cf)
|
||||
#define NT_STATUS_REQUEST_NOT_ACCEPTED (0xC0000000 | 0x00d0)
|
||||
#define NT_STATUS_REDIRECTOR_PAUSED (0xC0000000 | 0x00d1)
|
||||
#define NT_STATUS_NET_WRITE_FAULT (0xC0000000 | 0x00d2)
|
||||
#define NT_STATUS_PROFILING_AT_LIMIT (0xC0000000 | 0x00d3)
|
||||
#define NT_STATUS_NOT_SAME_DEVICE (0xC0000000 | 0x00d4)
|
||||
#define NT_STATUS_FILE_RENAMED (0xC0000000 | 0x00d5)
|
||||
#define NT_STATUS_VIRTUAL_CIRCUIT_CLOSED (0xC0000000 | 0x00d6)
|
||||
#define NT_STATUS_NO_SECURITY_ON_OBJECT (0xC0000000 | 0x00d7)
|
||||
#define NT_STATUS_CANT_WAIT (0xC0000000 | 0x00d8)
|
||||
#define NT_STATUS_PIPE_EMPTY (0xC0000000 | 0x00d9)
|
||||
#define NT_STATUS_CANT_ACCESS_DOMAIN_INFO (0xC0000000 | 0x00da)
|
||||
#define NT_STATUS_CANT_TERMINATE_SELF (0xC0000000 | 0x00db)
|
||||
#define NT_STATUS_INVALID_SERVER_STATE (0xC0000000 | 0x00dc)
|
||||
#define NT_STATUS_INVALID_DOMAIN_STATE (0xC0000000 | 0x00dd)
|
||||
#define NT_STATUS_INVALID_DOMAIN_ROLE (0xC0000000 | 0x00de)
|
||||
#define NT_STATUS_NO_SUCH_DOMAIN (0xC0000000 | 0x00df)
|
||||
#define NT_STATUS_DOMAIN_EXISTS (0xC0000000 | 0x00e0)
|
||||
#define NT_STATUS_DOMAIN_LIMIT_EXCEEDED (0xC0000000 | 0x00e1)
|
||||
#define NT_STATUS_OPLOCK_NOT_GRANTED (0xC0000000 | 0x00e2)
|
||||
#define NT_STATUS_INVALID_OPLOCK_PROTOCOL (0xC0000000 | 0x00e3)
|
||||
#define NT_STATUS_INTERNAL_DB_CORRUPTION (0xC0000000 | 0x00e4)
|
||||
#define NT_STATUS_INTERNAL_ERROR (0xC0000000 | 0x00e5)
|
||||
#define NT_STATUS_GENERIC_NOT_MAPPED (0xC0000000 | 0x00e6)
|
||||
#define NT_STATUS_BAD_DESCRIPTOR_FORMAT (0xC0000000 | 0x00e7)
|
||||
#define NT_STATUS_INVALID_USER_BUFFER (0xC0000000 | 0x00e8)
|
||||
#define NT_STATUS_UNEXPECTED_IO_ERROR (0xC0000000 | 0x00e9)
|
||||
#define NT_STATUS_UNEXPECTED_MM_CREATE_ERR (0xC0000000 | 0x00ea)
|
||||
#define NT_STATUS_UNEXPECTED_MM_MAP_ERROR (0xC0000000 | 0x00eb)
|
||||
#define NT_STATUS_UNEXPECTED_MM_EXTEND_ERR (0xC0000000 | 0x00ec)
|
||||
#define NT_STATUS_NOT_LOGON_PROCESS (0xC0000000 | 0x00ed)
|
||||
#define NT_STATUS_LOGON_SESSION_EXISTS (0xC0000000 | 0x00ee)
|
||||
#define NT_STATUS_INVALID_PARAMETER_1 (0xC0000000 | 0x00ef)
|
||||
#define NT_STATUS_INVALID_PARAMETER_2 (0xC0000000 | 0x00f0)
|
||||
#define NT_STATUS_INVALID_PARAMETER_3 (0xC0000000 | 0x00f1)
|
||||
#define NT_STATUS_INVALID_PARAMETER_4 (0xC0000000 | 0x00f2)
|
||||
#define NT_STATUS_INVALID_PARAMETER_5 (0xC0000000 | 0x00f3)
|
||||
#define NT_STATUS_INVALID_PARAMETER_6 (0xC0000000 | 0x00f4)
|
||||
#define NT_STATUS_INVALID_PARAMETER_7 (0xC0000000 | 0x00f5)
|
||||
#define NT_STATUS_INVALID_PARAMETER_8 (0xC0000000 | 0x00f6)
|
||||
#define NT_STATUS_INVALID_PARAMETER_9 (0xC0000000 | 0x00f7)
|
||||
#define NT_STATUS_INVALID_PARAMETER_10 (0xC0000000 | 0x00f8)
|
||||
#define NT_STATUS_INVALID_PARAMETER_11 (0xC0000000 | 0x00f9)
|
||||
#define NT_STATUS_INVALID_PARAMETER_12 (0xC0000000 | 0x00fa)
|
||||
#define NT_STATUS_REDIRECTOR_NOT_STARTED (0xC0000000 | 0x00fb)
|
||||
#define NT_STATUS_REDIRECTOR_STARTED (0xC0000000 | 0x00fc)
|
||||
#define NT_STATUS_STACK_OVERFLOW (0xC0000000 | 0x00fd)
|
||||
#define NT_STATUS_NO_SUCH_PACKAGE (0xC0000000 | 0x00fe)
|
||||
#define NT_STATUS_BAD_FUNCTION_TABLE (0xC0000000 | 0x00ff)
|
||||
#define NT_STATUS_DIRECTORY_NOT_EMPTY (0xC0000000 | 0x0101)
|
||||
#define NT_STATUS_FILE_CORRUPT_ERROR (0xC0000000 | 0x0102)
|
||||
#define NT_STATUS_NOT_A_DIRECTORY (0xC0000000 | 0x0103)
|
||||
#define NT_STATUS_BAD_LOGON_SESSION_STATE (0xC0000000 | 0x0104)
|
||||
#define NT_STATUS_LOGON_SESSION_COLLISION (0xC0000000 | 0x0105)
|
||||
#define NT_STATUS_NAME_TOO_LONG (0xC0000000 | 0x0106)
|
||||
#define NT_STATUS_FILES_OPEN (0xC0000000 | 0x0107)
|
||||
#define NT_STATUS_CONNECTION_IN_USE (0xC0000000 | 0x0108)
|
||||
#define NT_STATUS_MESSAGE_NOT_FOUND (0xC0000000 | 0x0109)
|
||||
#define NT_STATUS_PROCESS_IS_TERMINATING (0xC0000000 | 0x010a)
|
||||
#define NT_STATUS_INVALID_LOGON_TYPE (0xC0000000 | 0x010b)
|
||||
#define NT_STATUS_NO_GUID_TRANSLATION (0xC0000000 | 0x010c)
|
||||
#define NT_STATUS_CANNOT_IMPERSONATE (0xC0000000 | 0x010d)
|
||||
#define NT_STATUS_IMAGE_ALREADY_LOADED (0xC0000000 | 0x010e)
|
||||
#define NT_STATUS_ABIOS_NOT_PRESENT (0xC0000000 | 0x010f)
|
||||
#define NT_STATUS_ABIOS_LID_NOT_EXIST (0xC0000000 | 0x0110)
|
||||
#define NT_STATUS_ABIOS_LID_ALREADY_OWNED (0xC0000000 | 0x0111)
|
||||
#define NT_STATUS_ABIOS_NOT_LID_OWNER (0xC0000000 | 0x0112)
|
||||
#define NT_STATUS_ABIOS_INVALID_COMMAND (0xC0000000 | 0x0113)
|
||||
#define NT_STATUS_ABIOS_INVALID_LID (0xC0000000 | 0x0114)
|
||||
#define NT_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE (0xC0000000 | 0x0115)
|
||||
#define NT_STATUS_ABIOS_INVALID_SELECTOR (0xC0000000 | 0x0116)
|
||||
#define NT_STATUS_NO_LDT (0xC0000000 | 0x0117)
|
||||
#define NT_STATUS_INVALID_LDT_SIZE (0xC0000000 | 0x0118)
|
||||
#define NT_STATUS_INVALID_LDT_OFFSET (0xC0000000 | 0x0119)
|
||||
#define NT_STATUS_INVALID_LDT_DESCRIPTOR (0xC0000000 | 0x011a)
|
||||
#define NT_STATUS_INVALID_IMAGE_NE_FORMAT (0xC0000000 | 0x011b)
|
||||
#define NT_STATUS_RXACT_INVALID_STATE (0xC0000000 | 0x011c)
|
||||
#define NT_STATUS_RXACT_COMMIT_FAILURE (0xC0000000 | 0x011d)
|
||||
#define NT_STATUS_MAPPED_FILE_SIZE_ZERO (0xC0000000 | 0x011e)
|
||||
#define NT_STATUS_TOO_MANY_OPENED_FILES (0xC0000000 | 0x011f)
|
||||
#define NT_STATUS_CANCELLED (0xC0000000 | 0x0120)
|
||||
#define NT_STATUS_CANNOT_DELETE (0xC0000000 | 0x0121)
|
||||
#define NT_STATUS_INVALID_COMPUTER_NAME (0xC0000000 | 0x0122)
|
||||
#define NT_STATUS_FILE_DELETED (0xC0000000 | 0x0123)
|
||||
#define NT_STATUS_SPECIAL_ACCOUNT (0xC0000000 | 0x0124)
|
||||
#define NT_STATUS_SPECIAL_GROUP (0xC0000000 | 0x0125)
|
||||
#define NT_STATUS_SPECIAL_USER (0xC0000000 | 0x0126)
|
||||
#define NT_STATUS_MEMBERS_PRIMARY_GROUP (0xC0000000 | 0x0127)
|
||||
#define NT_STATUS_FILE_CLOSED (0xC0000000 | 0x0128)
|
||||
#define NT_STATUS_TOO_MANY_THREADS (0xC0000000 | 0x0129)
|
||||
#define NT_STATUS_THREAD_NOT_IN_PROCESS (0xC0000000 | 0x012a)
|
||||
#define NT_STATUS_TOKEN_ALREADY_IN_USE (0xC0000000 | 0x012b)
|
||||
#define NT_STATUS_PAGEFILE_QUOTA_EXCEEDED (0xC0000000 | 0x012c)
|
||||
#define NT_STATUS_COMMITMENT_LIMIT (0xC0000000 | 0x012d)
|
||||
#define NT_STATUS_INVALID_IMAGE_LE_FORMAT (0xC0000000 | 0x012e)
|
||||
#define NT_STATUS_INVALID_IMAGE_NOT_MZ (0xC0000000 | 0x012f)
|
||||
#define NT_STATUS_INVALID_IMAGE_PROTECT (0xC0000000 | 0x0130)
|
||||
#define NT_STATUS_INVALID_IMAGE_WIN_16 (0xC0000000 | 0x0131)
|
||||
#define NT_STATUS_LOGON_SERVER_CONFLICT (0xC0000000 | 0x0132)
|
||||
#define NT_STATUS_TIME_DIFFERENCE_AT_DC (0xC0000000 | 0x0133)
|
||||
#define NT_STATUS_SYNCHRONIZATION_REQUIRED (0xC0000000 | 0x0134)
|
||||
#define NT_STATUS_DLL_NOT_FOUND (0xC0000000 | 0x0135)
|
||||
#define NT_STATUS_OPEN_FAILED (0xC0000000 | 0x0136)
|
||||
#define NT_STATUS_IO_PRIVILEGE_FAILED (0xC0000000 | 0x0137)
|
||||
#define NT_STATUS_ORDINAL_NOT_FOUND (0xC0000000 | 0x0138)
|
||||
#define NT_STATUS_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0139)
|
||||
#define NT_STATUS_CONTROL_C_EXIT (0xC0000000 | 0x013a)
|
||||
#define NT_STATUS_LOCAL_DISCONNECT (0xC0000000 | 0x013b)
|
||||
#define NT_STATUS_REMOTE_DISCONNECT (0xC0000000 | 0x013c)
|
||||
#define NT_STATUS_REMOTE_RESOURCES (0xC0000000 | 0x013d)
|
||||
#define NT_STATUS_LINK_FAILED (0xC0000000 | 0x013e)
|
||||
#define NT_STATUS_LINK_TIMEOUT (0xC0000000 | 0x013f)
|
||||
#define NT_STATUS_INVALID_CONNECTION (0xC0000000 | 0x0140)
|
||||
#define NT_STATUS_INVALID_ADDRESS (0xC0000000 | 0x0141)
|
||||
#define NT_STATUS_DLL_INIT_FAILED (0xC0000000 | 0x0142)
|
||||
#define NT_STATUS_MISSING_SYSTEMFILE (0xC0000000 | 0x0143)
|
||||
#define NT_STATUS_UNHANDLED_EXCEPTION (0xC0000000 | 0x0144)
|
||||
#define NT_STATUS_APP_INIT_FAILURE (0xC0000000 | 0x0145)
|
||||
#define NT_STATUS_PAGEFILE_CREATE_FAILED (0xC0000000 | 0x0146)
|
||||
#define NT_STATUS_NO_PAGEFILE (0xC0000000 | 0x0147)
|
||||
#define NT_STATUS_INVALID_LEVEL (0xC0000000 | 0x0148)
|
||||
#define NT_STATUS_WRONG_PASSWORD_CORE (0xC0000000 | 0x0149)
|
||||
#define NT_STATUS_ILLEGAL_FLOAT_CONTEXT (0xC0000000 | 0x014a)
|
||||
#define NT_STATUS_PIPE_BROKEN (0xC0000000 | 0x014b)
|
||||
#define NT_STATUS_REGISTRY_CORRUPT (0xC0000000 | 0x014c)
|
||||
#define NT_STATUS_REGISTRY_IO_FAILED (0xC0000000 | 0x014d)
|
||||
#define NT_STATUS_NO_EVENT_PAIR (0xC0000000 | 0x014e)
|
||||
#define NT_STATUS_UNRECOGNIZED_VOLUME (0xC0000000 | 0x014f)
|
||||
#define NT_STATUS_SERIAL_NO_DEVICE_INITED (0xC0000000 | 0x0150)
|
||||
#define NT_STATUS_NO_SUCH_ALIAS (0xC0000000 | 0x0151)
|
||||
#define NT_STATUS_MEMBER_NOT_IN_ALIAS (0xC0000000 | 0x0152)
|
||||
#define NT_STATUS_MEMBER_IN_ALIAS (0xC0000000 | 0x0153)
|
||||
#define NT_STATUS_ALIAS_EXISTS (0xC0000000 | 0x0154)
|
||||
#define NT_STATUS_LOGON_NOT_GRANTED (0xC0000000 | 0x0155)
|
||||
#define NT_STATUS_TOO_MANY_SECRETS (0xC0000000 | 0x0156)
|
||||
#define NT_STATUS_SECRET_TOO_LONG (0xC0000000 | 0x0157)
|
||||
#define NT_STATUS_INTERNAL_DB_ERROR (0xC0000000 | 0x0158)
|
||||
#define NT_STATUS_FULLSCREEN_MODE (0xC0000000 | 0x0159)
|
||||
#define NT_STATUS_TOO_MANY_CONTEXT_IDS (0xC0000000 | 0x015a)
|
||||
#define NT_STATUS_LOGON_TYPE_NOT_GRANTED (0xC0000000 | 0x015b)
|
||||
#define NT_STATUS_NOT_REGISTRY_FILE (0xC0000000 | 0x015c)
|
||||
#define NT_STATUS_NT_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x015d)
|
||||
#define NT_STATUS_DOMAIN_CTRLR_CONFIG_ERROR (0xC0000000 | 0x015e)
|
||||
#define NT_STATUS_FT_MISSING_MEMBER (0xC0000000 | 0x015f)
|
||||
#define NT_STATUS_ILL_FORMED_SERVICE_ENTRY (0xC0000000 | 0x0160)
|
||||
#define NT_STATUS_ILLEGAL_CHARACTER (0xC0000000 | 0x0161)
|
||||
#define NT_STATUS_UNMAPPABLE_CHARACTER (0xC0000000 | 0x0162)
|
||||
#define NT_STATUS_UNDEFINED_CHARACTER (0xC0000000 | 0x0163)
|
||||
#define NT_STATUS_FLOPPY_VOLUME (0xC0000000 | 0x0164)
|
||||
#define NT_STATUS_FLOPPY_ID_MARK_NOT_FOUND (0xC0000000 | 0x0165)
|
||||
#define NT_STATUS_FLOPPY_WRONG_CYLINDER (0xC0000000 | 0x0166)
|
||||
#define NT_STATUS_FLOPPY_UNKNOWN_ERROR (0xC0000000 | 0x0167)
|
||||
#define NT_STATUS_FLOPPY_BAD_REGISTERS (0xC0000000 | 0x0168)
|
||||
#define NT_STATUS_DISK_RECALIBRATE_FAILED (0xC0000000 | 0x0169)
|
||||
#define NT_STATUS_DISK_OPERATION_FAILED (0xC0000000 | 0x016a)
|
||||
#define NT_STATUS_DISK_RESET_FAILED (0xC0000000 | 0x016b)
|
||||
#define NT_STATUS_SHARED_IRQ_BUSY (0xC0000000 | 0x016c)
|
||||
#define NT_STATUS_FT_ORPHANING (0xC0000000 | 0x016d)
|
||||
#define NT_STATUS_PARTITION_FAILURE (0xC0000000 | 0x0172)
|
||||
#define NT_STATUS_INVALID_BLOCK_LENGTH (0xC0000000 | 0x0173)
|
||||
#define NT_STATUS_DEVICE_NOT_PARTITIONED (0xC0000000 | 0x0174)
|
||||
#define NT_STATUS_UNABLE_TO_LOCK_MEDIA (0xC0000000 | 0x0175)
|
||||
#define NT_STATUS_UNABLE_TO_UNLOAD_MEDIA (0xC0000000 | 0x0176)
|
||||
#define NT_STATUS_EOM_OVERFLOW (0xC0000000 | 0x0177)
|
||||
#define NT_STATUS_NO_MEDIA (0xC0000000 | 0x0178)
|
||||
#define NT_STATUS_NO_SUCH_MEMBER (0xC0000000 | 0x017a)
|
||||
#define NT_STATUS_INVALID_MEMBER (0xC0000000 | 0x017b)
|
||||
#define NT_STATUS_KEY_DELETED (0xC0000000 | 0x017c)
|
||||
#define NT_STATUS_NO_LOG_SPACE (0xC0000000 | 0x017d)
|
||||
#define NT_STATUS_TOO_MANY_SIDS (0xC0000000 | 0x017e)
|
||||
#define NT_STATUS_LM_CROSS_ENCRYPTION_REQUIRED (0xC0000000 | 0x017f)
|
||||
#define NT_STATUS_KEY_HAS_CHILDREN (0xC0000000 | 0x0180)
|
||||
#define NT_STATUS_CHILD_MUST_BE_VOLATILE (0xC0000000 | 0x0181)
|
||||
#define NT_STATUS_DEVICE_CONFIGURATION_ERROR (0xC0000000 | 0x0182)
|
||||
#define NT_STATUS_DRIVER_INTERNAL_ERROR (0xC0000000 | 0x0183)
|
||||
#define NT_STATUS_INVALID_DEVICE_STATE (0xC0000000 | 0x0184)
|
||||
#define NT_STATUS_IO_DEVICE_ERROR (0xC0000000 | 0x0185)
|
||||
#define NT_STATUS_DEVICE_PROTOCOL_ERROR (0xC0000000 | 0x0186)
|
||||
#define NT_STATUS_BACKUP_CONTROLLER (0xC0000000 | 0x0187)
|
||||
#define NT_STATUS_LOG_FILE_FULL (0xC0000000 | 0x0188)
|
||||
#define NT_STATUS_TOO_LATE (0xC0000000 | 0x0189)
|
||||
#define NT_STATUS_NO_TRUST_LSA_SECRET (0xC0000000 | 0x018a)
|
||||
#define NT_STATUS_NO_TRUST_SAM_ACCOUNT (0xC0000000 | 0x018b)
|
||||
#define NT_STATUS_TRUSTED_DOMAIN_FAILURE (0xC0000000 | 0x018c)
|
||||
#define NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE (0xC0000000 | 0x018d)
|
||||
#define NT_STATUS_EVENTLOG_FILE_CORRUPT (0xC0000000 | 0x018e)
|
||||
#define NT_STATUS_EVENTLOG_CANT_START (0xC0000000 | 0x018f)
|
||||
#define NT_STATUS_TRUST_FAILURE (0xC0000000 | 0x0190)
|
||||
#define NT_STATUS_MUTANT_LIMIT_EXCEEDED (0xC0000000 | 0x0191)
|
||||
#define NT_STATUS_NETLOGON_NOT_STARTED (0xC0000000 | 0x0192)
|
||||
#define NT_STATUS_ACCOUNT_EXPIRED (0xC0000000 | 0x0193)
|
||||
#define NT_STATUS_POSSIBLE_DEADLOCK (0xC0000000 | 0x0194)
|
||||
#define NT_STATUS_NETWORK_CREDENTIAL_CONFLICT (0xC0000000 | 0x0195)
|
||||
#define NT_STATUS_REMOTE_SESSION_LIMIT (0xC0000000 | 0x0196)
|
||||
#define NT_STATUS_EVENTLOG_FILE_CHANGED (0xC0000000 | 0x0197)
|
||||
#define NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT (0xC0000000 | 0x0198)
|
||||
#define NT_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT (0xC0000000 | 0x0199)
|
||||
#define NT_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT (0xC0000000 | 0x019a)
|
||||
#define NT_STATUS_DOMAIN_TRUST_INCONSISTENT (0xC0000000 | 0x019b)
|
||||
#define NT_STATUS_FS_DRIVER_REQUIRED (0xC0000000 | 0x019c)
|
||||
#define NT_STATUS_NO_USER_SESSION_KEY (0xC0000000 | 0x0202)
|
||||
#define NT_STATUS_USER_SESSION_DELETED (0xC0000000 | 0x0203)
|
||||
#define NT_STATUS_RESOURCE_LANG_NOT_FOUND (0xC0000000 | 0x0204)
|
||||
#define NT_STATUS_INSUFF_SERVER_RESOURCES (0xC0000000 | 0x0205)
|
||||
#define NT_STATUS_INVALID_BUFFER_SIZE (0xC0000000 | 0x0206)
|
||||
#define NT_STATUS_INVALID_ADDRESS_COMPONENT (0xC0000000 | 0x0207)
|
||||
#define NT_STATUS_INVALID_ADDRESS_WILDCARD (0xC0000000 | 0x0208)
|
||||
#define NT_STATUS_TOO_MANY_ADDRESSES (0xC0000000 | 0x0209)
|
||||
#define NT_STATUS_ADDRESS_ALREADY_EXISTS (0xC0000000 | 0x020a)
|
||||
#define NT_STATUS_ADDRESS_CLOSED (0xC0000000 | 0x020b)
|
||||
#define NT_STATUS_CONNECTION_DISCONNECTED (0xC0000000 | 0x020c)
|
||||
#define NT_STATUS_CONNECTION_RESET (0xC0000000 | 0x020d)
|
||||
#define NT_STATUS_TOO_MANY_NODES (0xC0000000 | 0x020e)
|
||||
#define NT_STATUS_TRANSACTION_ABORTED (0xC0000000 | 0x020f)
|
||||
#define NT_STATUS_TRANSACTION_TIMED_OUT (0xC0000000 | 0x0210)
|
||||
#define NT_STATUS_TRANSACTION_NO_RELEASE (0xC0000000 | 0x0211)
|
||||
#define NT_STATUS_TRANSACTION_NO_MATCH (0xC0000000 | 0x0212)
|
||||
#define NT_STATUS_TRANSACTION_RESPONDED (0xC0000000 | 0x0213)
|
||||
#define NT_STATUS_TRANSACTION_INVALID_ID (0xC0000000 | 0x0214)
|
||||
#define NT_STATUS_TRANSACTION_INVALID_TYPE (0xC0000000 | 0x0215)
|
||||
#define NT_STATUS_NOT_SERVER_SESSION (0xC0000000 | 0x0216)
|
||||
#define NT_STATUS_NOT_CLIENT_SESSION (0xC0000000 | 0x0217)
|
||||
#define NT_STATUS_CANNOT_LOAD_REGISTRY_FILE (0xC0000000 | 0x0218)
|
||||
#define NT_STATUS_DEBUG_ATTACH_FAILED (0xC0000000 | 0x0219)
|
||||
#define NT_STATUS_SYSTEM_PROCESS_TERMINATED (0xC0000000 | 0x021a)
|
||||
#define NT_STATUS_DATA_NOT_ACCEPTED (0xC0000000 | 0x021b)
|
||||
#define NT_STATUS_NO_BROWSER_SERVERS_FOUND (0xC0000000 | 0x021c)
|
||||
#define NT_STATUS_VDM_HARD_ERROR (0xC0000000 | 0x021d)
|
||||
#define NT_STATUS_DRIVER_CANCEL_TIMEOUT (0xC0000000 | 0x021e)
|
||||
#define NT_STATUS_REPLY_MESSAGE_MISMATCH (0xC0000000 | 0x021f)
|
||||
#define NT_STATUS_MAPPED_ALIGNMENT (0xC0000000 | 0x0220)
|
||||
#define NT_STATUS_IMAGE_CHECKSUM_MISMATCH (0xC0000000 | 0x0221)
|
||||
#define NT_STATUS_LOST_WRITEBEHIND_DATA (0xC0000000 | 0x0222)
|
||||
#define NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID (0xC0000000 | 0x0223)
|
||||
#define NT_STATUS_PASSWORD_MUST_CHANGE (0xC0000000 | 0x0224)
|
||||
#define NT_STATUS_NOT_FOUND (0xC0000000 | 0x0225)
|
||||
#define NT_STATUS_NOT_TINY_STREAM (0xC0000000 | 0x0226)
|
||||
#define NT_STATUS_RECOVERY_FAILURE (0xC0000000 | 0x0227)
|
||||
#define NT_STATUS_STACK_OVERFLOW_READ (0xC0000000 | 0x0228)
|
||||
#define NT_STATUS_FAIL_CHECK (0xC0000000 | 0x0229)
|
||||
#define NT_STATUS_DUPLICATE_OBJECTID (0xC0000000 | 0x022a)
|
||||
#define NT_STATUS_OBJECTID_EXISTS (0xC0000000 | 0x022b)
|
||||
#define NT_STATUS_CONVERT_TO_LARGE (0xC0000000 | 0x022c)
|
||||
#define NT_STATUS_RETRY (0xC0000000 | 0x022d)
|
||||
#define NT_STATUS_FOUND_OUT_OF_SCOPE (0xC0000000 | 0x022e)
|
||||
#define NT_STATUS_ALLOCATE_BUCKET (0xC0000000 | 0x022f)
|
||||
#define NT_STATUS_PROPSET_NOT_FOUND (0xC0000000 | 0x0230)
|
||||
#define NT_STATUS_MARSHALL_OVERFLOW (0xC0000000 | 0x0231)
|
||||
#define NT_STATUS_INVALID_VARIANT (0xC0000000 | 0x0232)
|
||||
#define NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND (0xC0000000 | 0x0233)
|
||||
#define NT_STATUS_ACCOUNT_LOCKED_OUT (0xC0000000 | 0x0234)
|
||||
#define NT_STATUS_HANDLE_NOT_CLOSABLE (0xC0000000 | 0x0235)
|
||||
#define NT_STATUS_CONNECTION_REFUSED (0xC0000000 | 0x0236)
|
||||
#define NT_STATUS_GRACEFUL_DISCONNECT (0xC0000000 | 0x0237)
|
||||
#define NT_STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000000 | 0x0238)
|
||||
#define NT_STATUS_ADDRESS_NOT_ASSOCIATED (0xC0000000 | 0x0239)
|
||||
#define NT_STATUS_CONNECTION_INVALID (0xC0000000 | 0x023a)
|
||||
#define NT_STATUS_CONNECTION_ACTIVE (0xC0000000 | 0x023b)
|
||||
#define NT_STATUS_NETWORK_UNREACHABLE (0xC0000000 | 0x023c)
|
||||
#define NT_STATUS_HOST_UNREACHABLE (0xC0000000 | 0x023d)
|
||||
#define NT_STATUS_PROTOCOL_UNREACHABLE (0xC0000000 | 0x023e)
|
||||
#define NT_STATUS_PORT_UNREACHABLE (0xC0000000 | 0x023f)
|
||||
#define NT_STATUS_REQUEST_ABORTED (0xC0000000 | 0x0240)
|
||||
#define NT_STATUS_CONNECTION_ABORTED (0xC0000000 | 0x0241)
|
||||
#define NT_STATUS_BAD_COMPRESSION_BUFFER (0xC0000000 | 0x0242)
|
||||
#define NT_STATUS_USER_MAPPED_FILE (0xC0000000 | 0x0243)
|
||||
#define NT_STATUS_AUDIT_FAILED (0xC0000000 | 0x0244)
|
||||
#define NT_STATUS_TIMER_RESOLUTION_NOT_SET (0xC0000000 | 0x0245)
|
||||
#define NT_STATUS_CONNECTION_COUNT_LIMIT (0xC0000000 | 0x0246)
|
||||
#define NT_STATUS_LOGIN_TIME_RESTRICTION (0xC0000000 | 0x0247)
|
||||
#define NT_STATUS_LOGIN_WKSTA_RESTRICTION (0xC0000000 | 0x0248)
|
||||
#define NT_STATUS_IMAGE_MP_UP_MISMATCH (0xC0000000 | 0x0249)
|
||||
#define NT_STATUS_INSUFFICIENT_LOGON_INFO (0xC0000000 | 0x0250)
|
||||
#define NT_STATUS_BAD_DLL_ENTRYPOINT (0xC0000000 | 0x0251)
|
||||
#define NT_STATUS_BAD_SERVICE_ENTRYPOINT (0xC0000000 | 0x0252)
|
||||
#define NT_STATUS_LPC_REPLY_LOST (0xC0000000 | 0x0253)
|
||||
#define NT_STATUS_IP_ADDRESS_CONFLICT1 (0xC0000000 | 0x0254)
|
||||
#define NT_STATUS_IP_ADDRESS_CONFLICT2 (0xC0000000 | 0x0255)
|
||||
#define NT_STATUS_REGISTRY_QUOTA_LIMIT (0xC0000000 | 0x0256)
|
||||
#define NT_STATUS_PATH_NOT_COVERED (0xC0000000 | 0x0257)
|
||||
#define NT_STATUS_NO_CALLBACK_ACTIVE (0xC0000000 | 0x0258)
|
||||
#define NT_STATUS_LICENSE_QUOTA_EXCEEDED (0xC0000000 | 0x0259)
|
||||
#define NT_STATUS_PWD_TOO_SHORT (0xC0000000 | 0x025a)
|
||||
#define NT_STATUS_PWD_TOO_RECENT (0xC0000000 | 0x025b)
|
||||
#define NT_STATUS_PWD_HISTORY_CONFLICT (0xC0000000 | 0x025c)
|
||||
#define NT_STATUS_PLUGPLAY_NO_DEVICE (0xC0000000 | 0x025e)
|
||||
#define NT_STATUS_UNSUPPORTED_COMPRESSION (0xC0000000 | 0x025f)
|
||||
#define NT_STATUS_INVALID_HW_PROFILE (0xC0000000 | 0x0260)
|
||||
#define NT_STATUS_INVALID_PLUGPLAY_DEVICE_PATH (0xC0000000 | 0x0261)
|
||||
#define NT_STATUS_DRIVER_ORDINAL_NOT_FOUND (0xC0000000 | 0x0262)
|
||||
#define NT_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND (0xC0000000 | 0x0263)
|
||||
#define NT_STATUS_RESOURCE_NOT_OWNED (0xC0000000 | 0x0264)
|
||||
#define NT_STATUS_TOO_MANY_LINKS (0xC0000000 | 0x0265)
|
||||
#define NT_STATUS_QUOTA_LIST_INCONSISTENT (0xC0000000 | 0x0266)
|
||||
#define NT_STATUS_FILE_IS_OFFLINE (0xC0000000 | 0x0267)
|
||||
#define NT_STATUS_NETWORK_SESSION_EXPIRED (0xC0000000 | 0x035c)
|
||||
#define NT_STATUS_NO_SUCH_JOB (0xC0000000 | 0xEDE) /* scheduler */
|
||||
#define NT_STATUS_NO_PREAUTH_INTEGRITY_HASH_OVERLAP (0xC0000000 | 0x5D0000)
|
||||
#define NT_STATUS_PENDING 0x00000103
|
||||
#endif /* _NTERR_H */
|
|
@ -0,0 +1,169 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2002,2007
|
||||
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_NTLMSSP_H
|
||||
#define __KSMBD_NTLMSSP_H
|
||||
|
||||
#define NTLMSSP_SIGNATURE "NTLMSSP"
|
||||
|
||||
/* Security blob target info data */
|
||||
#define TGT_Name "KSMBD"
|
||||
|
||||
/*
|
||||
* Size of the crypto key returned on the negotiate SMB in bytes
|
||||
*/
|
||||
#define CIFS_CRYPTO_KEY_SIZE (8)
|
||||
#define CIFS_KEY_SIZE (40)
|
||||
|
||||
/*
|
||||
* Size of encrypted user password in bytes
|
||||
*/
|
||||
#define CIFS_ENCPWD_SIZE (16)
|
||||
#define CIFS_CPHTXT_SIZE (16)
|
||||
|
||||
/* Message Types */
|
||||
#define NtLmNegotiate cpu_to_le32(1)
|
||||
#define NtLmChallenge cpu_to_le32(2)
|
||||
#define NtLmAuthenticate cpu_to_le32(3)
|
||||
#define UnknownMessage cpu_to_le32(8)
|
||||
|
||||
/* Negotiate Flags */
|
||||
#define NTLMSSP_NEGOTIATE_UNICODE 0x01 /* Text strings are unicode */
|
||||
#define NTLMSSP_NEGOTIATE_OEM 0x02 /* Text strings are in OEM */
|
||||
#define NTLMSSP_REQUEST_TARGET 0x04 /* Srv returns its auth realm */
|
||||
/* define reserved9 0x08 */
|
||||
#define NTLMSSP_NEGOTIATE_SIGN 0x0010 /* Request signing capability */
|
||||
#define NTLMSSP_NEGOTIATE_SEAL 0x0020 /* Request confidentiality */
|
||||
#define NTLMSSP_NEGOTIATE_DGRAM 0x0040
|
||||
#define NTLMSSP_NEGOTIATE_LM_KEY 0x0080 /* Use LM session key */
|
||||
/* defined reserved 8 0x0100 */
|
||||
#define NTLMSSP_NEGOTIATE_NTLM 0x0200 /* NTLM authentication */
|
||||
#define NTLMSSP_NEGOTIATE_NT_ONLY 0x0400 /* Lanman not allowed */
|
||||
#define NTLMSSP_ANONYMOUS 0x0800
|
||||
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x1000 /* reserved6 */
|
||||
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x2000
|
||||
#define NTLMSSP_NEGOTIATE_LOCAL_CALL 0x4000 /* client/server same machine */
|
||||
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x8000 /* Sign. All security levels */
|
||||
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x10000
|
||||
#define NTLMSSP_TARGET_TYPE_SERVER 0x20000
|
||||
#define NTLMSSP_TARGET_TYPE_SHARE 0x40000
|
||||
#define NTLMSSP_NEGOTIATE_EXTENDED_SEC 0x80000 /* NB:not related to NTLMv2 pwd*/
|
||||
/* #define NTLMSSP_REQUEST_INIT_RESP 0x100000 */
|
||||
#define NTLMSSP_NEGOTIATE_IDENTIFY 0x100000
|
||||
#define NTLMSSP_REQUEST_ACCEPT_RESP 0x200000 /* reserved5 */
|
||||
#define NTLMSSP_REQUEST_NON_NT_KEY 0x400000
|
||||
#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x800000
|
||||
/* #define reserved4 0x1000000 */
|
||||
#define NTLMSSP_NEGOTIATE_VERSION 0x2000000 /* we do not set */
|
||||
/* #define reserved3 0x4000000 */
|
||||
/* #define reserved2 0x8000000 */
|
||||
/* #define reserved1 0x10000000 */
|
||||
#define NTLMSSP_NEGOTIATE_128 0x20000000
|
||||
#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
|
||||
#define NTLMSSP_NEGOTIATE_56 0x80000000
|
||||
|
||||
/* Define AV Pair Field IDs */
|
||||
enum av_field_type {
|
||||
NTLMSSP_AV_EOL = 0,
|
||||
NTLMSSP_AV_NB_COMPUTER_NAME,
|
||||
NTLMSSP_AV_NB_DOMAIN_NAME,
|
||||
NTLMSSP_AV_DNS_COMPUTER_NAME,
|
||||
NTLMSSP_AV_DNS_DOMAIN_NAME,
|
||||
NTLMSSP_AV_DNS_TREE_NAME,
|
||||
NTLMSSP_AV_FLAGS,
|
||||
NTLMSSP_AV_TIMESTAMP,
|
||||
NTLMSSP_AV_RESTRICTION,
|
||||
NTLMSSP_AV_TARGET_NAME,
|
||||
NTLMSSP_AV_CHANNEL_BINDINGS
|
||||
};
|
||||
|
||||
/* Although typedefs are not commonly used for structure definitions */
|
||||
/* in the Linux kernel, in this particular case they are useful */
|
||||
/* to more closely match the standards document for NTLMSSP from */
|
||||
/* OpenGroup and to make the code more closely match the standard in */
|
||||
/* appearance */
|
||||
|
||||
struct security_buffer {
|
||||
__le16 Length;
|
||||
__le16 MaximumLength;
|
||||
__le32 BufferOffset; /* offset to buffer */
|
||||
} __packed;
|
||||
|
||||
struct target_info {
|
||||
__le16 Type;
|
||||
__le16 Length;
|
||||
__u8 Content[0];
|
||||
} __packed;
|
||||
|
||||
struct negotiate_message {
|
||||
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
|
||||
__le32 MessageType; /* NtLmNegotiate = 1 */
|
||||
__le32 NegotiateFlags;
|
||||
struct security_buffer DomainName; /* RFC 1001 style and ASCII */
|
||||
struct security_buffer WorkstationName; /* RFC 1001 and ASCII */
|
||||
/*
|
||||
* struct security_buffer for version info not present since we
|
||||
* do not set the version is present flag
|
||||
*/
|
||||
char DomainString[0];
|
||||
/* followed by WorkstationString */
|
||||
} __packed;
|
||||
|
||||
struct challenge_message {
|
||||
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
|
||||
__le32 MessageType; /* NtLmChallenge = 2 */
|
||||
struct security_buffer TargetName;
|
||||
__le32 NegotiateFlags;
|
||||
__u8 Challenge[CIFS_CRYPTO_KEY_SIZE];
|
||||
__u8 Reserved[8];
|
||||
struct security_buffer TargetInfoArray;
|
||||
/*
|
||||
* struct security_buffer for version info not present since we
|
||||
* do not set the version is present flag
|
||||
*/
|
||||
} __packed;
|
||||
|
||||
struct authenticate_message {
|
||||
__u8 Signature[sizeof(NTLMSSP_SIGNATURE)];
|
||||
__le32 MessageType; /* NtLmsAuthenticate = 3 */
|
||||
struct security_buffer LmChallengeResponse;
|
||||
struct security_buffer NtChallengeResponse;
|
||||
struct security_buffer DomainName;
|
||||
struct security_buffer UserName;
|
||||
struct security_buffer WorkstationName;
|
||||
struct security_buffer SessionKey;
|
||||
__le32 NegotiateFlags;
|
||||
/*
|
||||
* struct security_buffer for version info not present since we
|
||||
* do not set the version is present flag
|
||||
*/
|
||||
char UserString[0];
|
||||
} __packed;
|
||||
|
||||
struct ntlmv2_resp {
|
||||
char ntlmv2_hash[CIFS_ENCPWD_SIZE];
|
||||
__le32 blob_signature;
|
||||
__u32 reserved;
|
||||
__le64 time;
|
||||
__u64 client_chal; /* random */
|
||||
__u32 reserved2;
|
||||
/* array of name entries could follow ending in minimum 4 byte struct */
|
||||
} __packed;
|
||||
|
||||
/* per smb session structure/fields */
|
||||
struct ntlmssp_auth {
|
||||
/* whether session key is per smb session */
|
||||
bool sesskey_per_smbsess;
|
||||
/* sent by client in type 1 ntlmsssp exchange */
|
||||
__u32 client_flags;
|
||||
/* sent by server in type 2 ntlmssp exchange */
|
||||
__u32 conn_flags;
|
||||
/* sent to server */
|
||||
unsigned char ciphertext[CIFS_CPHTXT_SIZE];
|
||||
/* used by ntlmssp */
|
||||
char cryptkey[CIFS_CRYPTO_KEY_SIZE];
|
||||
};
|
||||
#endif /* __KSMBD_NTLMSSP_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,131 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_OPLOCK_H
|
||||
#define __KSMBD_OPLOCK_H
|
||||
|
||||
#include "smb_common.h"
|
||||
|
||||
#define OPLOCK_WAIT_TIME (35 * HZ)
|
||||
|
||||
/* SMB2 Oplock levels */
|
||||
#define SMB2_OPLOCK_LEVEL_NONE 0x00
|
||||
#define SMB2_OPLOCK_LEVEL_II 0x01
|
||||
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
|
||||
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
|
||||
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
|
||||
|
||||
/* Oplock states */
|
||||
#define OPLOCK_STATE_NONE 0x00
|
||||
#define OPLOCK_ACK_WAIT 0x01
|
||||
#define OPLOCK_CLOSING 0x02
|
||||
|
||||
#define OPLOCK_WRITE_TO_READ 0x01
|
||||
#define OPLOCK_READ_HANDLE_TO_READ 0x02
|
||||
#define OPLOCK_WRITE_TO_NONE 0x04
|
||||
#define OPLOCK_READ_TO_NONE 0x08
|
||||
|
||||
#define SMB2_LEASE_KEY_SIZE 16
|
||||
|
||||
struct lease_ctx_info {
|
||||
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
__le32 req_state;
|
||||
__le32 flags;
|
||||
__le64 duration;
|
||||
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
int version;
|
||||
};
|
||||
|
||||
struct lease_table {
|
||||
char client_guid[SMB2_CLIENT_GUID_SIZE];
|
||||
struct list_head lease_list;
|
||||
struct list_head l_entry;
|
||||
spinlock_t lb_lock;
|
||||
};
|
||||
|
||||
struct lease {
|
||||
__u8 lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
__le32 state;
|
||||
__le32 new_state;
|
||||
__le32 flags;
|
||||
__le64 duration;
|
||||
__u8 parent_lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
int version;
|
||||
unsigned short epoch;
|
||||
struct lease_table *l_lb;
|
||||
};
|
||||
|
||||
struct oplock_info {
|
||||
struct ksmbd_conn *conn;
|
||||
struct ksmbd_session *sess;
|
||||
struct ksmbd_work *work;
|
||||
struct ksmbd_file *o_fp;
|
||||
int level;
|
||||
int op_state;
|
||||
unsigned long pending_break;
|
||||
u64 fid;
|
||||
atomic_t breaking_cnt;
|
||||
atomic_t refcount;
|
||||
__u16 Tid;
|
||||
bool is_lease;
|
||||
bool open_trunc; /* truncate on open */
|
||||
struct lease *o_lease;
|
||||
struct list_head interim_list;
|
||||
struct list_head op_entry;
|
||||
struct list_head lease_entry;
|
||||
wait_queue_head_t oplock_q; /* Other server threads */
|
||||
wait_queue_head_t oplock_brk; /* oplock breaking wait */
|
||||
struct rcu_head rcu_head;
|
||||
};
|
||||
|
||||
struct lease_break_info {
|
||||
__le32 curr_state;
|
||||
__le32 new_state;
|
||||
__le16 epoch;
|
||||
char lease_key[SMB2_LEASE_KEY_SIZE];
|
||||
};
|
||||
|
||||
struct oplock_break_info {
|
||||
int level;
|
||||
int open_trunc;
|
||||
int fid;
|
||||
};
|
||||
|
||||
int smb_grant_oplock(struct ksmbd_work *work, int req_op_level,
|
||||
u64 pid, struct ksmbd_file *fp, __u16 tid,
|
||||
struct lease_ctx_info *lctx, int share_ret);
|
||||
void smb_break_all_levII_oplock(struct ksmbd_work *work,
|
||||
struct ksmbd_file *fp, int is_trunc);
|
||||
int opinfo_write_to_read(struct oplock_info *opinfo);
|
||||
int opinfo_read_handle_to_read(struct oplock_info *opinfo);
|
||||
int opinfo_write_to_none(struct oplock_info *opinfo);
|
||||
int opinfo_read_to_none(struct oplock_info *opinfo);
|
||||
void close_id_del_oplock(struct ksmbd_file *fp);
|
||||
void smb_break_all_oplock(struct ksmbd_work *work, struct ksmbd_file *fp);
|
||||
struct oplock_info *opinfo_get(struct ksmbd_file *fp);
|
||||
void opinfo_put(struct oplock_info *opinfo);
|
||||
|
||||
/* Lease related functions */
|
||||
void create_lease_buf(u8 *rbuf, struct lease *lease);
|
||||
struct lease_ctx_info *parse_lease_state(void *open_req);
|
||||
__u8 smb2_map_lease_to_oplock(__le32 lease_state);
|
||||
int lease_read_to_write(struct oplock_info *opinfo);
|
||||
|
||||
/* Durable related functions */
|
||||
void create_durable_rsp_buf(char *cc);
|
||||
void create_durable_v2_rsp_buf(char *cc, struct ksmbd_file *fp);
|
||||
void create_mxac_rsp_buf(char *cc, int maximal_access);
|
||||
void create_disk_id_rsp_buf(char *cc, __u64 file_id, __u64 vol_id);
|
||||
void create_posix_rsp_buf(char *cc, struct ksmbd_file *fp);
|
||||
struct create_context *smb2_find_context_vals(void *open_req, const char *str);
|
||||
struct oplock_info *lookup_lease_in_table(struct ksmbd_conn *conn,
|
||||
char *lease_key);
|
||||
int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
|
||||
struct lease_ctx_info *lctx);
|
||||
void destroy_lease_table(struct ksmbd_conn *conn);
|
||||
int smb2_check_durable_oplock(struct ksmbd_file *fp,
|
||||
struct lease_ctx_info *lctx, char *name);
|
||||
#endif /* __KSMBD_OPLOCK_H */
|
|
@ -0,0 +1,633 @@
|
|||
// 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
|
||||
* @work: smb work containing server thread information
|
||||
*
|
||||
* 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)) {
|
||||
rsp_hdr = work->response_buf;
|
||||
rsp_hdr->Status.CifsError = STATUS_CONNECTION_DISCONNECTED;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SERVER_HANDLER_CONTINUE 0
|
||||
#define SERVER_HANDLER_ABORT 1
|
||||
|
||||
static int __process_request(struct ksmbd_work *work, struct ksmbd_conn *conn,
|
||||
u16 *cmd)
|
||||
{
|
||||
struct smb_version_cmds *cmds;
|
||||
u16 command;
|
||||
int ret;
|
||||
|
||||
if (check_conn_state(work))
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
|
||||
if (ksmbd_verify_smb_message(work))
|
||||
return SERVER_HANDLER_ABORT;
|
||||
|
||||
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);
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
}
|
||||
|
||||
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);
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
}
|
||||
|
||||
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);
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
return SERVER_HANDLER_ABORT;
|
||||
return SERVER_HANDLER_CONTINUE;
|
||||
}
|
||||
|
||||
static void __handle_ksmbd_work(struct ksmbd_work *work,
|
||||
struct ksmbd_conn *conn)
|
||||
{
|
||||
u16 command = 0;
|
||||
int rc;
|
||||
|
||||
if (conn->ops->allocate_rsp_buf(work))
|
||||
return;
|
||||
|
||||
if (conn->ops->is_transform_hdr &&
|
||||
conn->ops->is_transform_hdr(work->request_buf)) {
|
||||
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);
|
||||
if (rc == SERVER_HANDLER_ABORT)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (work->sess &&
|
||||
(work->sess->sign || smb3_11_final_sess_setup_resp(work) ||
|
||||
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 &&
|
||||
conn->ops->encrypt_resp) {
|
||||
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
|
||||
* @wk: smb work containing request command buffer
|
||||
*
|
||||
* 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) {
|
||||
pr_err("allocation for work failed\n");
|
||||
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);
|
||||
}
|
||||
|
||||
static ssize_t stats_show(struct class *class, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
/*
|
||||
* 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"
|
||||
};
|
||||
|
||||
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);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static ssize_t kill_server_store(struct class *class,
|
||||
struct class_attribute *attr, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
if (!sysfs_streq(buf, "hard"))
|
||||
return len;
|
||||
|
||||
pr_info("kill command received\n");
|
||||
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",
|
||||
"oplock", "ipc", "conn",
|
||||
"rdma"};
|
||||
|
||||
static ssize_t debug_show(struct class *class, struct class_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static ssize_t debug_store(struct class *class, struct class_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
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);
|
||||
ksmbd_work_pool_destroy();
|
||||
ksmbd_exit_file_cache();
|
||||
server_conf_free();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init ksmbd_server_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = class_register(&ksmbd_control_class);
|
||||
if (ret) {
|
||||
pr_err("Unable to register ksmbd-control class\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ksmbd_server_tcp_callbacks_init();
|
||||
|
||||
ret = server_conf_init();
|
||||
if (ret)
|
||||
goto err_unregister;
|
||||
|
||||
ret = ksmbd_work_pool_init();
|
||||
if (ret)
|
||||
goto err_unregister;
|
||||
|
||||
ret = ksmbd_init_file_cache();
|
||||
if (ret)
|
||||
goto err_destroy_work_pools;
|
||||
|
||||
ret = ksmbd_ipc_init();
|
||||
if (ret)
|
||||
goto err_exit_file_cache;
|
||||
|
||||
ret = ksmbd_init_global_file_table();
|
||||
if (ret)
|
||||
goto err_ipc_release;
|
||||
|
||||
ret = ksmbd_inode_hash_init();
|
||||
if (ret)
|
||||
goto err_destroy_file_table;
|
||||
|
||||
ret = ksmbd_crypto_create();
|
||||
if (ret)
|
||||
goto err_release_inode_hash;
|
||||
|
||||
ret = ksmbd_workqueue_init();
|
||||
if (ret)
|
||||
goto err_crypto_destroy;
|
||||
return 0;
|
||||
|
||||
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();
|
||||
err_exit_file_cache:
|
||||
ksmbd_exit_file_cache();
|
||||
err_destroy_work_pools:
|
||||
ksmbd_work_pool_destroy();
|
||||
err_unregister:
|
||||
class_unregister(&ksmbd_control_class);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_server_exit() - shutdown forker thread and free memory at module exit
|
||||
*/
|
||||
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: md4");
|
||||
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");
|
||||
module_init(ksmbd_server_init)
|
||||
module_exit(ksmbd_server_exit)
|
|
@ -0,0 +1,70 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __SERVER_H__
|
||||
#define __SERVER_H__
|
||||
|
||||
#include "smbacl.h"
|
||||
|
||||
/*
|
||||
* Server state type
|
||||
*/
|
||||
enum {
|
||||
SERVER_STATE_STARTING_UP,
|
||||
SERVER_STATE_RUNNING,
|
||||
SERVER_STATE_RESETTING,
|
||||
SERVER_STATE_SHUTTING_DOWN,
|
||||
};
|
||||
|
||||
/*
|
||||
* Server global config string index
|
||||
*/
|
||||
enum {
|
||||
SERVER_CONF_NETBIOS_NAME,
|
||||
SERVER_CONF_SERVER_STRING,
|
||||
SERVER_CONF_WORK_GROUP,
|
||||
};
|
||||
|
||||
struct ksmbd_server_config {
|
||||
unsigned int flags;
|
||||
unsigned int state;
|
||||
short signing;
|
||||
short enforced_signing;
|
||||
short min_protocol;
|
||||
short max_protocol;
|
||||
unsigned short tcp_port;
|
||||
unsigned short ipc_timeout;
|
||||
unsigned long ipc_last_active;
|
||||
unsigned long deadtime;
|
||||
unsigned int share_fake_fscaps;
|
||||
struct smb_sid domain_sid;
|
||||
unsigned int auth_mechs;
|
||||
|
||||
char *conf[SERVER_CONF_WORK_GROUP + 1];
|
||||
};
|
||||
|
||||
extern struct ksmbd_server_config server_conf;
|
||||
|
||||
int ksmbd_set_netbios_name(char *v);
|
||||
int ksmbd_set_server_string(char *v);
|
||||
int ksmbd_set_work_group(char *v);
|
||||
|
||||
char *ksmbd_netbios_name(void);
|
||||
char *ksmbd_server_string(void);
|
||||
char *ksmbd_work_group(void);
|
||||
|
||||
static inline int ksmbd_server_running(void)
|
||||
{
|
||||
return READ_ONCE(server_conf.state) == SERVER_STATE_RUNNING;
|
||||
}
|
||||
|
||||
static inline int ksmbd_server_configurable(void)
|
||||
{
|
||||
return READ_ONCE(server_conf.state) < SERVER_STATE_RESETTING;
|
||||
}
|
||||
|
||||
int server_queue_ctrl_init_work(void);
|
||||
int server_queue_ctrl_reset_work(void);
|
||||
#endif /* __SERVER_H__ */
|
|
@ -0,0 +1,438 @@
|
|||
// 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 "nterr.h"
|
||||
#include "smb2pdu.h"
|
||||
#include "smb_common.h"
|
||||
#include "smbstatus.h"
|
||||
#include "mgmt/user_session.h"
|
||||
#include "connection.h"
|
||||
|
||||
static int check_smb2_hdr(struct smb2_hdr *hdr)
|
||||
{
|
||||
/*
|
||||
* Make sure that this really is an SMB, that it is a response.
|
||||
*/
|
||||
if (hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following table defines the expected "StructureSize" of SMB2 requests
|
||||
* in order by SMB2 command. This is similar to "wct" in SMB/CIFS requests.
|
||||
*
|
||||
* Note that commands are defined in smb2pdu.h in le16 but the array below is
|
||||
* indexed by command in host byte order
|
||||
*/
|
||||
static const __le16 smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
|
||||
/* SMB2_NEGOTIATE */ cpu_to_le16(36),
|
||||
/* SMB2_SESSION_SETUP */ cpu_to_le16(25),
|
||||
/* SMB2_LOGOFF */ cpu_to_le16(4),
|
||||
/* SMB2_TREE_CONNECT */ cpu_to_le16(9),
|
||||
/* SMB2_TREE_DISCONNECT */ cpu_to_le16(4),
|
||||
/* SMB2_CREATE */ cpu_to_le16(57),
|
||||
/* SMB2_CLOSE */ cpu_to_le16(24),
|
||||
/* SMB2_FLUSH */ cpu_to_le16(24),
|
||||
/* SMB2_READ */ cpu_to_le16(49),
|
||||
/* SMB2_WRITE */ cpu_to_le16(49),
|
||||
/* SMB2_LOCK */ cpu_to_le16(48),
|
||||
/* SMB2_IOCTL */ cpu_to_le16(57),
|
||||
/* SMB2_CANCEL */ cpu_to_le16(4),
|
||||
/* SMB2_ECHO */ cpu_to_le16(4),
|
||||
/* SMB2_QUERY_DIRECTORY */ cpu_to_le16(33),
|
||||
/* SMB2_CHANGE_NOTIFY */ cpu_to_le16(32),
|
||||
/* SMB2_QUERY_INFO */ cpu_to_le16(41),
|
||||
/* SMB2_SET_INFO */ cpu_to_le16(33),
|
||||
/* use 44 for lease break */
|
||||
/* SMB2_OPLOCK_BREAK */ cpu_to_le16(36)
|
||||
};
|
||||
|
||||
/*
|
||||
* The size of the variable area depends on the offset and length fields
|
||||
* located in different fields for various SMB2 requests. SMB2 requests
|
||||
* with no variable length info, show an offset of zero for the offset field.
|
||||
*/
|
||||
static const bool has_smb2_data_area[NUMBER_OF_SMB2_COMMANDS] = {
|
||||
/* SMB2_NEGOTIATE */ true,
|
||||
/* SMB2_SESSION_SETUP */ true,
|
||||
/* SMB2_LOGOFF */ false,
|
||||
/* SMB2_TREE_CONNECT */ true,
|
||||
/* SMB2_TREE_DISCONNECT */ false,
|
||||
/* SMB2_CREATE */ true,
|
||||
/* SMB2_CLOSE */ false,
|
||||
/* SMB2_FLUSH */ false,
|
||||
/* SMB2_READ */ true,
|
||||
/* SMB2_WRITE */ true,
|
||||
/* SMB2_LOCK */ true,
|
||||
/* SMB2_IOCTL */ true,
|
||||
/* SMB2_CANCEL */ false, /* BB CHECK this not listed in documentation */
|
||||
/* SMB2_ECHO */ false,
|
||||
/* SMB2_QUERY_DIRECTORY */ true,
|
||||
/* SMB2_CHANGE_NOTIFY */ false,
|
||||
/* SMB2_QUERY_INFO */ true,
|
||||
/* SMB2_SET_INFO */ true,
|
||||
/* SMB2_OPLOCK_BREAK */ false
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the pointer to the beginning of the data area. Length of the data
|
||||
* area and the offset to it (from the beginning of the smb are also returned.
|
||||
*/
|
||||
static char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
|
||||
{
|
||||
*off = 0;
|
||||
*len = 0;
|
||||
|
||||
/* error reqeusts do not have data area */
|
||||
if (hdr->Status && hdr->Status != STATUS_MORE_PROCESSING_REQUIRED &&
|
||||
(((struct smb2_err_rsp *)hdr)->StructureSize) == SMB2_ERROR_STRUCTURE_SIZE2_LE)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Following commands have data areas so we have to get the location
|
||||
* of the data buffer offset and data buffer length for the particular
|
||||
* command.
|
||||
*/
|
||||
switch (hdr->Command) {
|
||||
case SMB2_SESSION_SETUP:
|
||||
*off = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferOffset);
|
||||
*len = le16_to_cpu(((struct smb2_sess_setup_req *)hdr)->SecurityBufferLength);
|
||||
break;
|
||||
case SMB2_TREE_CONNECT:
|
||||
*off = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathOffset);
|
||||
*len = le16_to_cpu(((struct smb2_tree_connect_req *)hdr)->PathLength);
|
||||
break;
|
||||
case SMB2_CREATE:
|
||||
{
|
||||
if (((struct smb2_create_req *)hdr)->CreateContextsLength) {
|
||||
*off = le32_to_cpu(((struct smb2_create_req *)
|
||||
hdr)->CreateContextsOffset);
|
||||
*len = le32_to_cpu(((struct smb2_create_req *)
|
||||
hdr)->CreateContextsLength);
|
||||
break;
|
||||
}
|
||||
|
||||
*off = le16_to_cpu(((struct smb2_create_req *)hdr)->NameOffset);
|
||||
*len = le16_to_cpu(((struct smb2_create_req *)hdr)->NameLength);
|
||||
break;
|
||||
}
|
||||
case SMB2_QUERY_INFO:
|
||||
*off = le16_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferOffset);
|
||||
*len = le32_to_cpu(((struct smb2_query_info_req *)hdr)->InputBufferLength);
|
||||
break;
|
||||
case SMB2_SET_INFO:
|
||||
*off = le16_to_cpu(((struct smb2_set_info_req *)hdr)->BufferOffset);
|
||||
*len = le32_to_cpu(((struct smb2_set_info_req *)hdr)->BufferLength);
|
||||
break;
|
||||
case SMB2_READ:
|
||||
*off = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoOffset);
|
||||
*len = le16_to_cpu(((struct smb2_read_req *)hdr)->ReadChannelInfoLength);
|
||||
break;
|
||||
case SMB2_WRITE:
|
||||
if (((struct smb2_write_req *)hdr)->DataOffset) {
|
||||
*off = le16_to_cpu(((struct smb2_write_req *)hdr)->DataOffset);
|
||||
*len = le32_to_cpu(((struct smb2_write_req *)hdr)->Length);
|
||||
break;
|
||||
}
|
||||
|
||||
*off = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoOffset);
|
||||
*len = le16_to_cpu(((struct smb2_write_req *)hdr)->WriteChannelInfoLength);
|
||||
break;
|
||||
case SMB2_QUERY_DIRECTORY:
|
||||
*off = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameOffset);
|
||||
*len = le16_to_cpu(((struct smb2_query_directory_req *)hdr)->FileNameLength);
|
||||
break;
|
||||
case SMB2_LOCK:
|
||||
{
|
||||
int lock_count;
|
||||
|
||||
/*
|
||||
* smb2_lock request size is 48 included single
|
||||
* smb2_lock_element structure size.
|
||||
*/
|
||||
lock_count = le16_to_cpu(((struct smb2_lock_req *)hdr)->LockCount) - 1;
|
||||
if (lock_count > 0) {
|
||||
*off = __SMB2_HEADER_STRUCTURE_SIZE + 48;
|
||||
*len = sizeof(struct smb2_lock_element) * lock_count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMB2_IOCTL:
|
||||
*off = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputOffset);
|
||||
*len = le32_to_cpu(((struct smb2_ioctl_req *)hdr)->InputCount);
|
||||
|
||||
break;
|
||||
default:
|
||||
ksmbd_debug(SMB, "no length check for command\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalid length or offset probably means data area is invalid, but
|
||||
* we have little choice but to ignore the data area in this case.
|
||||
*/
|
||||
if (*off > 4096) {
|
||||
ksmbd_debug(SMB, "offset %d too large, data area ignored\n",
|
||||
*off);
|
||||
*len = 0;
|
||||
*off = 0;
|
||||
} else if (*off < 0) {
|
||||
ksmbd_debug(SMB,
|
||||
"negative offset %d to data invalid ignore data area\n",
|
||||
*off);
|
||||
*off = 0;
|
||||
*len = 0;
|
||||
} else if (*len < 0) {
|
||||
ksmbd_debug(SMB,
|
||||
"negative data length %d invalid, data area ignored\n",
|
||||
*len);
|
||||
*len = 0;
|
||||
} else if (*len > 128 * 1024) {
|
||||
ksmbd_debug(SMB, "data area larger than 128K: %d\n", *len);
|
||||
*len = 0;
|
||||
}
|
||||
|
||||
/* return pointer to beginning of data area, ie offset from SMB start */
|
||||
if ((*off != 0) && (*len != 0))
|
||||
return (char *)hdr + *off;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the size of the SMB message based on the fixed header
|
||||
* portion, the number of word parameters and the data portion of the message.
|
||||
*/
|
||||
static unsigned int smb2_calc_size(void *buf)
|
||||
{
|
||||
struct smb2_pdu *pdu = (struct smb2_pdu *)buf;
|
||||
struct smb2_hdr *hdr = &pdu->hdr;
|
||||
int offset; /* the offset from the beginning of SMB to data area */
|
||||
int data_length; /* the length of the variable length data area */
|
||||
/* Structure Size has already been checked to make sure it is 64 */
|
||||
int len = le16_to_cpu(hdr->StructureSize);
|
||||
|
||||
/*
|
||||
* StructureSize2, ie length of fixed parameter area has already
|
||||
* been checked to make sure it is the correct length.
|
||||
*/
|
||||
len += le16_to_cpu(pdu->StructureSize2);
|
||||
|
||||
if (has_smb2_data_area[le16_to_cpu(hdr->Command)] == false)
|
||||
goto calc_size_exit;
|
||||
|
||||
smb2_get_data_area_len(&offset, &data_length, hdr);
|
||||
ksmbd_debug(SMB, "SMB2 data length %d offset %d\n", data_length,
|
||||
offset);
|
||||
|
||||
if (data_length > 0) {
|
||||
/*
|
||||
* Check to make sure that data area begins after fixed area,
|
||||
* Note that last byte of the fixed area is part of data area
|
||||
* for some commands, typically those with odd StructureSize,
|
||||
* so we must add one to the calculation.
|
||||
*/
|
||||
if (offset + 1 < len)
|
||||
ksmbd_debug(SMB,
|
||||
"data area offset %d overlaps SMB2 header %d\n",
|
||||
offset + 1, len);
|
||||
else
|
||||
len = offset + data_length;
|
||||
}
|
||||
calc_size_exit:
|
||||
ksmbd_debug(SMB, "SMB2 len %d\n", len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int smb2_query_info_req_len(struct smb2_query_info_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->InputBufferLength) +
|
||||
le32_to_cpu(h->OutputBufferLength);
|
||||
}
|
||||
|
||||
static inline int smb2_set_info_req_len(struct smb2_set_info_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->BufferLength);
|
||||
}
|
||||
|
||||
static inline int smb2_read_req_len(struct smb2_read_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->Length);
|
||||
}
|
||||
|
||||
static inline int smb2_write_req_len(struct smb2_write_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->Length);
|
||||
}
|
||||
|
||||
static inline int smb2_query_dir_req_len(struct smb2_query_directory_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->OutputBufferLength);
|
||||
}
|
||||
|
||||
static inline int smb2_ioctl_req_len(struct smb2_ioctl_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->InputCount) +
|
||||
le32_to_cpu(h->OutputCount);
|
||||
}
|
||||
|
||||
static inline int smb2_ioctl_resp_len(struct smb2_ioctl_req *h)
|
||||
{
|
||||
return le32_to_cpu(h->MaxInputResponse) +
|
||||
le32_to_cpu(h->MaxOutputResponse);
|
||||
}
|
||||
|
||||
static int smb2_validate_credit_charge(struct smb2_hdr *hdr)
|
||||
{
|
||||
int req_len = 0, expect_resp_len = 0, calc_credit_num, max_len;
|
||||
int credit_charge = le16_to_cpu(hdr->CreditCharge);
|
||||
void *__hdr = hdr;
|
||||
|
||||
switch (hdr->Command) {
|
||||
case SMB2_QUERY_INFO:
|
||||
req_len = smb2_query_info_req_len(__hdr);
|
||||
break;
|
||||
case SMB2_SET_INFO:
|
||||
req_len = smb2_set_info_req_len(__hdr);
|
||||
break;
|
||||
case SMB2_READ:
|
||||
req_len = smb2_read_req_len(__hdr);
|
||||
break;
|
||||
case SMB2_WRITE:
|
||||
req_len = smb2_write_req_len(__hdr);
|
||||
break;
|
||||
case SMB2_QUERY_DIRECTORY:
|
||||
req_len = smb2_query_dir_req_len(__hdr);
|
||||
break;
|
||||
case SMB2_IOCTL:
|
||||
req_len = smb2_ioctl_req_len(__hdr);
|
||||
expect_resp_len = smb2_ioctl_resp_len(__hdr);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
credit_charge = max(1, credit_charge);
|
||||
max_len = max(req_len, expect_resp_len);
|
||||
calc_credit_num = DIV_ROUND_UP(max_len, SMB2_MAX_BUFFER_SIZE);
|
||||
|
||||
if (credit_charge < calc_credit_num) {
|
||||
pr_err("Insufficient credit charge, given: %d, needed: %d\n",
|
||||
credit_charge, calc_credit_num);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ksmbd_smb2_check_message(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb2_pdu *pdu = work->request_buf;
|
||||
struct smb2_hdr *hdr = &pdu->hdr;
|
||||
int command;
|
||||
__u32 clc_len; /* calculated length */
|
||||
__u32 len = get_rfc1002_len(pdu);
|
||||
|
||||
if (work->next_smb2_rcv_hdr_off) {
|
||||
pdu = ksmbd_req_buf_next(work);
|
||||
hdr = &pdu->hdr;
|
||||
}
|
||||
|
||||
if (le32_to_cpu(hdr->NextCommand) > 0) {
|
||||
len = le32_to_cpu(hdr->NextCommand);
|
||||
} else if (work->next_smb2_rcv_hdr_off) {
|
||||
len -= work->next_smb2_rcv_hdr_off;
|
||||
len = round_up(len, 8);
|
||||
}
|
||||
|
||||
if (check_smb2_hdr(hdr))
|
||||
return 1;
|
||||
|
||||
if (hdr->StructureSize != SMB2_HEADER_STRUCTURE_SIZE) {
|
||||
ksmbd_debug(SMB, "Illegal structure size %u\n",
|
||||
le16_to_cpu(hdr->StructureSize));
|
||||
return 1;
|
||||
}
|
||||
|
||||
command = le16_to_cpu(hdr->Command);
|
||||
if (command >= NUMBER_OF_SMB2_COMMANDS) {
|
||||
ksmbd_debug(SMB, "Illegal SMB2 command %d\n", command);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (smb2_req_struct_sizes[command] != pdu->StructureSize2) {
|
||||
if (command != SMB2_OPLOCK_BREAK_HE &&
|
||||
(hdr->Status == 0 || pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2_LE)) {
|
||||
/* error packets have 9 byte structure size */
|
||||
ksmbd_debug(SMB,
|
||||
"Illegal request size %u for command %d\n",
|
||||
le16_to_cpu(pdu->StructureSize2), command);
|
||||
return 1;
|
||||
} else if (command == SMB2_OPLOCK_BREAK_HE &&
|
||||
hdr->Status == 0 &&
|
||||
le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_20 &&
|
||||
le16_to_cpu(pdu->StructureSize2) != OP_BREAK_STRUCT_SIZE_21) {
|
||||
/* special case for SMB2.1 lease break message */
|
||||
ksmbd_debug(SMB,
|
||||
"Illegal request size %d for oplock break\n",
|
||||
le16_to_cpu(pdu->StructureSize2));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((work->conn->vals->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU) &&
|
||||
smb2_validate_credit_charge(hdr)) {
|
||||
work->conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
|
||||
return 1;
|
||||
}
|
||||
|
||||
clc_len = smb2_calc_size(hdr);
|
||||
if (len != clc_len) {
|
||||
/* server can return one byte more due to implied bcc[0] */
|
||||
if (clc_len == len + 1)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Some windows servers (win2016) will pad also the final
|
||||
* PDU in a compound to 8 bytes.
|
||||
*/
|
||||
if (ALIGN(clc_len, 8) == len)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* windows client also pad up to 8 bytes when compounding.
|
||||
* If pad is longer than eight bytes, log the server behavior
|
||||
* (once), since may indicate a problem but allow it and
|
||||
* continue since the frame is parseable.
|
||||
*/
|
||||
if (clc_len < len) {
|
||||
ksmbd_debug(SMB,
|
||||
"cli req padded more than expected. Length %d not %d for cmd:%d mid:%llu\n",
|
||||
len, clc_len, command,
|
||||
le64_to_cpu(hdr->MessageId));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command == SMB2_LOCK_HE && len == 88)
|
||||
return 0;
|
||||
|
||||
ksmbd_debug(SMB,
|
||||
"cli req too short, len %d not %d. cmd:%d mid:%llu\n",
|
||||
len, clc_len, command,
|
||||
le64_to_cpu(hdr->MessageId));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb2_negotiate_request(struct ksmbd_work *work)
|
||||
{
|
||||
return ksmbd_smb_negotiate_common(work, SMB2_NEGOTIATE_HE);
|
||||
}
|
|
@ -0,0 +1,312 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include "glob.h"
|
||||
#include "smb2pdu.h"
|
||||
|
||||
#include "auth.h"
|
||||
#include "connection.h"
|
||||
#include "smb_common.h"
|
||||
#include "server.h"
|
||||
|
||||
static struct smb_version_values smb21_server_values = {
|
||||
.version_string = SMB21_VERSION_STRING,
|
||||
.protocol_id = SMB21_PROT_ID,
|
||||
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
|
||||
.max_read_size = SMB21_DEFAULT_IOSIZE,
|
||||
.max_write_size = SMB21_DEFAULT_IOSIZE,
|
||||
.max_trans_size = SMB21_DEFAULT_IOSIZE,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
|
||||
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||
.header_size = sizeof(struct smb2_hdr),
|
||||
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||
.lock_cmd = SMB2_LOCK,
|
||||
.cap_unix = 0,
|
||||
.cap_nt_find = SMB2_NT_FIND,
|
||||
.cap_large_files = SMB2_LARGE_FILES,
|
||||
.create_lease_size = sizeof(struct create_lease),
|
||||
.create_durable_size = sizeof(struct create_durable_rsp),
|
||||
.create_mxac_size = sizeof(struct create_mxac_rsp),
|
||||
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
|
||||
.create_posix_size = sizeof(struct create_posix_rsp),
|
||||
};
|
||||
|
||||
static struct smb_version_values smb30_server_values = {
|
||||
.version_string = SMB30_VERSION_STRING,
|
||||
.protocol_id = SMB30_PROT_ID,
|
||||
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
|
||||
.max_read_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_write_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
|
||||
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||
.header_size = sizeof(struct smb2_hdr),
|
||||
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||
.lock_cmd = SMB2_LOCK,
|
||||
.cap_unix = 0,
|
||||
.cap_nt_find = SMB2_NT_FIND,
|
||||
.cap_large_files = SMB2_LARGE_FILES,
|
||||
.create_lease_size = sizeof(struct create_lease_v2),
|
||||
.create_durable_size = sizeof(struct create_durable_rsp),
|
||||
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
|
||||
.create_mxac_size = sizeof(struct create_mxac_rsp),
|
||||
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
|
||||
.create_posix_size = sizeof(struct create_posix_rsp),
|
||||
};
|
||||
|
||||
static struct smb_version_values smb302_server_values = {
|
||||
.version_string = SMB302_VERSION_STRING,
|
||||
.protocol_id = SMB302_PROT_ID,
|
||||
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
|
||||
.max_read_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_write_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
|
||||
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||
.header_size = sizeof(struct smb2_hdr),
|
||||
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||
.lock_cmd = SMB2_LOCK,
|
||||
.cap_unix = 0,
|
||||
.cap_nt_find = SMB2_NT_FIND,
|
||||
.cap_large_files = SMB2_LARGE_FILES,
|
||||
.create_lease_size = sizeof(struct create_lease_v2),
|
||||
.create_durable_size = sizeof(struct create_durable_rsp),
|
||||
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
|
||||
.create_mxac_size = sizeof(struct create_mxac_rsp),
|
||||
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
|
||||
.create_posix_size = sizeof(struct create_posix_rsp),
|
||||
};
|
||||
|
||||
static struct smb_version_values smb311_server_values = {
|
||||
.version_string = SMB311_VERSION_STRING,
|
||||
.protocol_id = SMB311_PROT_ID,
|
||||
.capabilities = SMB2_GLOBAL_CAP_LARGE_MTU,
|
||||
.max_read_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_write_size = SMB3_DEFAULT_IOSIZE,
|
||||
.max_trans_size = SMB3_DEFAULT_TRANS_SIZE,
|
||||
.large_lock_type = 0,
|
||||
.exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE,
|
||||
.shared_lock_type = SMB2_LOCKFLAG_SHARED,
|
||||
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
||||
.header_size = sizeof(struct smb2_hdr),
|
||||
.max_header_size = MAX_SMB2_HDR_SIZE,
|
||||
.read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
||||
.lock_cmd = SMB2_LOCK,
|
||||
.cap_unix = 0,
|
||||
.cap_nt_find = SMB2_NT_FIND,
|
||||
.cap_large_files = SMB2_LARGE_FILES,
|
||||
.create_lease_size = sizeof(struct create_lease_v2),
|
||||
.create_durable_size = sizeof(struct create_durable_rsp),
|
||||
.create_durable_v2_size = sizeof(struct create_durable_v2_rsp),
|
||||
.create_mxac_size = sizeof(struct create_mxac_rsp),
|
||||
.create_disk_id_size = sizeof(struct create_disk_id_rsp),
|
||||
.create_posix_size = sizeof(struct create_posix_rsp),
|
||||
};
|
||||
|
||||
static struct smb_version_ops smb2_0_server_ops = {
|
||||
.get_cmd_val = get_smb2_cmd_val,
|
||||
.init_rsp_hdr = init_smb2_rsp_hdr,
|
||||
.set_rsp_status = set_smb2_rsp_status,
|
||||
.allocate_rsp_buf = smb2_allocate_rsp_buf,
|
||||
.set_rsp_credits = smb2_set_rsp_credits,
|
||||
.check_user_session = smb2_check_user_session,
|
||||
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
|
||||
.is_sign_req = smb2_is_sign_req,
|
||||
.check_sign_req = smb2_check_sign_req,
|
||||
.set_sign_rsp = smb2_set_sign_rsp
|
||||
};
|
||||
|
||||
static struct smb_version_ops smb3_0_server_ops = {
|
||||
.get_cmd_val = get_smb2_cmd_val,
|
||||
.init_rsp_hdr = init_smb2_rsp_hdr,
|
||||
.set_rsp_status = set_smb2_rsp_status,
|
||||
.allocate_rsp_buf = smb2_allocate_rsp_buf,
|
||||
.set_rsp_credits = smb2_set_rsp_credits,
|
||||
.check_user_session = smb2_check_user_session,
|
||||
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
|
||||
.is_sign_req = smb2_is_sign_req,
|
||||
.check_sign_req = smb3_check_sign_req,
|
||||
.set_sign_rsp = smb3_set_sign_rsp,
|
||||
.generate_signingkey = ksmbd_gen_smb30_signingkey,
|
||||
.generate_encryptionkey = ksmbd_gen_smb30_encryptionkey,
|
||||
.is_transform_hdr = smb3_is_transform_hdr,
|
||||
.decrypt_req = smb3_decrypt_req,
|
||||
.encrypt_resp = smb3_encrypt_resp
|
||||
};
|
||||
|
||||
static struct smb_version_ops smb3_11_server_ops = {
|
||||
.get_cmd_val = get_smb2_cmd_val,
|
||||
.init_rsp_hdr = init_smb2_rsp_hdr,
|
||||
.set_rsp_status = set_smb2_rsp_status,
|
||||
.allocate_rsp_buf = smb2_allocate_rsp_buf,
|
||||
.set_rsp_credits = smb2_set_rsp_credits,
|
||||
.check_user_session = smb2_check_user_session,
|
||||
.get_ksmbd_tcon = smb2_get_ksmbd_tcon,
|
||||
.is_sign_req = smb2_is_sign_req,
|
||||
.check_sign_req = smb3_check_sign_req,
|
||||
.set_sign_rsp = smb3_set_sign_rsp,
|
||||
.generate_signingkey = ksmbd_gen_smb311_signingkey,
|
||||
.generate_encryptionkey = ksmbd_gen_smb311_encryptionkey,
|
||||
.is_transform_hdr = smb3_is_transform_hdr,
|
||||
.decrypt_req = smb3_decrypt_req,
|
||||
.encrypt_resp = smb3_encrypt_resp
|
||||
};
|
||||
|
||||
static struct smb_version_cmds smb2_0_server_cmds[NUMBER_OF_SMB2_COMMANDS] = {
|
||||
[SMB2_NEGOTIATE_HE] = { .proc = smb2_negotiate_request, },
|
||||
[SMB2_SESSION_SETUP_HE] = { .proc = smb2_sess_setup, },
|
||||
[SMB2_TREE_CONNECT_HE] = { .proc = smb2_tree_connect,},
|
||||
[SMB2_TREE_DISCONNECT_HE] = { .proc = smb2_tree_disconnect,},
|
||||
[SMB2_LOGOFF_HE] = { .proc = smb2_session_logoff,},
|
||||
[SMB2_CREATE_HE] = { .proc = smb2_open},
|
||||
[SMB2_QUERY_INFO_HE] = { .proc = smb2_query_info},
|
||||
[SMB2_QUERY_DIRECTORY_HE] = { .proc = smb2_query_dir},
|
||||
[SMB2_CLOSE_HE] = { .proc = smb2_close},
|
||||
[SMB2_ECHO_HE] = { .proc = smb2_echo},
|
||||
[SMB2_SET_INFO_HE] = { .proc = smb2_set_info},
|
||||
[SMB2_READ_HE] = { .proc = smb2_read},
|
||||
[SMB2_WRITE_HE] = { .proc = smb2_write},
|
||||
[SMB2_FLUSH_HE] = { .proc = smb2_flush},
|
||||
[SMB2_CANCEL_HE] = { .proc = smb2_cancel},
|
||||
[SMB2_LOCK_HE] = { .proc = smb2_lock},
|
||||
[SMB2_IOCTL_HE] = { .proc = smb2_ioctl},
|
||||
[SMB2_OPLOCK_BREAK_HE] = { .proc = smb2_oplock_break},
|
||||
[SMB2_CHANGE_NOTIFY_HE] = { .proc = smb2_notify},
|
||||
};
|
||||
|
||||
int init_smb2_0_server(struct ksmbd_conn *conn)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_smb2_1_server() - initialize a smb server connection with smb2.1
|
||||
* command dispatcher
|
||||
* @conn: connection instance
|
||||
*/
|
||||
void init_smb2_1_server(struct ksmbd_conn *conn)
|
||||
{
|
||||
conn->vals = &smb21_server_values;
|
||||
conn->ops = &smb2_0_server_ops;
|
||||
conn->cmds = smb2_0_server_cmds;
|
||||
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
|
||||
conn->max_credits = SMB2_MAX_CREDITS;
|
||||
conn->signing_algorithm = SIGNING_ALG_HMAC_SHA256;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_smb3_0_server() - initialize a smb server connection with smb3.0
|
||||
* command dispatcher
|
||||
* @conn: connection instance
|
||||
*/
|
||||
void init_smb3_0_server(struct ksmbd_conn *conn)
|
||||
{
|
||||
conn->vals = &smb30_server_values;
|
||||
conn->ops = &smb3_0_server_ops;
|
||||
conn->cmds = smb2_0_server_cmds;
|
||||
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
|
||||
conn->max_credits = SMB2_MAX_CREDITS;
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_smb3_02_server() - initialize a smb server connection with smb3.02
|
||||
* command dispatcher
|
||||
* @conn: connection instance
|
||||
*/
|
||||
void init_smb3_02_server(struct ksmbd_conn *conn)
|
||||
{
|
||||
conn->vals = &smb302_server_values;
|
||||
conn->ops = &smb3_0_server_ops;
|
||||
conn->cmds = smb2_0_server_cmds;
|
||||
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
|
||||
conn->max_credits = SMB2_MAX_CREDITS;
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_ENCRYPTION &&
|
||||
conn->cli_cap & SMB2_GLOBAL_CAP_ENCRYPTION)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
|
||||
}
|
||||
|
||||
/**
|
||||
* init_smb3_11_server() - initialize a smb server connection with smb3.11
|
||||
* command dispatcher
|
||||
* @conn: connection instance
|
||||
*/
|
||||
int init_smb3_11_server(struct ksmbd_conn *conn)
|
||||
{
|
||||
conn->vals = &smb311_server_values;
|
||||
conn->ops = &smb3_11_server_ops;
|
||||
conn->cmds = smb2_0_server_cmds;
|
||||
conn->max_cmds = ARRAY_SIZE(smb2_0_server_cmds);
|
||||
conn->max_credits = SMB2_MAX_CREDITS;
|
||||
conn->signing_algorithm = SIGNING_ALG_AES_CMAC;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB2_LEASES)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_LEASING;
|
||||
|
||||
if (conn->cipher_type)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
|
||||
|
||||
if (server_conf.flags & KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL)
|
||||
conn->vals->capabilities |= SMB2_GLOBAL_CAP_MULTI_CHANNEL;
|
||||
|
||||
INIT_LIST_HEAD(&conn->preauth_sess_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_smb2_max_read_size(unsigned int sz)
|
||||
{
|
||||
smb21_server_values.max_read_size = sz;
|
||||
smb30_server_values.max_read_size = sz;
|
||||
smb302_server_values.max_read_size = sz;
|
||||
smb311_server_values.max_read_size = sz;
|
||||
}
|
||||
|
||||
void init_smb2_max_write_size(unsigned int sz)
|
||||
{
|
||||
smb21_server_values.max_write_size = sz;
|
||||
smb30_server_values.max_write_size = sz;
|
||||
smb302_server_values.max_write_size = sz;
|
||||
smb311_server_values.max_write_size = sz;
|
||||
}
|
||||
|
||||
void init_smb2_max_trans_size(unsigned int sz)
|
||||
{
|
||||
smb21_server_values.max_trans_size = sz;
|
||||
smb30_server_values.max_trans_size = sz;
|
||||
smb302_server_values.max_trans_size = sz;
|
||||
smb311_server_values.max_trans_size = sz;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,674 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
* Copyright (C) 2018 Namjae Jeon <linkinjeon@kernel.org>
|
||||
*/
|
||||
|
||||
#include "smb_common.h"
|
||||
#include "server.h"
|
||||
#include "misc.h"
|
||||
#include "smbstatus.h"
|
||||
#include "connection.h"
|
||||
#include "ksmbd_work.h"
|
||||
#include "mgmt/user_session.h"
|
||||
#include "mgmt/user_config.h"
|
||||
#include "mgmt/tree_connect.h"
|
||||
#include "mgmt/share_config.h"
|
||||
|
||||
/*for shortname implementation */
|
||||
static const char basechars[43] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
|
||||
#define MANGLE_BASE (sizeof(basechars) / sizeof(char) - 1)
|
||||
#define MAGIC_CHAR '~'
|
||||
#define PERIOD '.'
|
||||
#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
|
||||
#define KSMBD_MIN_SUPPORTED_HEADER_SIZE (sizeof(struct smb2_hdr))
|
||||
|
||||
struct smb_protocol {
|
||||
int index;
|
||||
char *name;
|
||||
char *prot;
|
||||
__u16 prot_id;
|
||||
};
|
||||
|
||||
static struct smb_protocol smb1_protos[] = {
|
||||
{
|
||||
SMB21_PROT,
|
||||
"\2SMB 2.1",
|
||||
"SMB2_10",
|
||||
SMB21_PROT_ID
|
||||
},
|
||||
{
|
||||
SMB2X_PROT,
|
||||
"\2SMB 2.???",
|
||||
"SMB2_22",
|
||||
SMB2X_PROT_ID
|
||||
},
|
||||
};
|
||||
|
||||
static struct smb_protocol smb2_protos[] = {
|
||||
{
|
||||
SMB21_PROT,
|
||||
"\2SMB 2.1",
|
||||
"SMB2_10",
|
||||
SMB21_PROT_ID
|
||||
},
|
||||
{
|
||||
SMB30_PROT,
|
||||
"\2SMB 3.0",
|
||||
"SMB3_00",
|
||||
SMB30_PROT_ID
|
||||
},
|
||||
{
|
||||
SMB302_PROT,
|
||||
"\2SMB 3.02",
|
||||
"SMB3_02",
|
||||
SMB302_PROT_ID
|
||||
},
|
||||
{
|
||||
SMB311_PROT,
|
||||
"\2SMB 3.1.1",
|
||||
"SMB3_11",
|
||||
SMB311_PROT_ID
|
||||
},
|
||||
};
|
||||
|
||||
unsigned int ksmbd_server_side_copy_max_chunk_count(void)
|
||||
{
|
||||
return 256;
|
||||
}
|
||||
|
||||
unsigned int ksmbd_server_side_copy_max_chunk_size(void)
|
||||
{
|
||||
return (2U << 30) - 1;
|
||||
}
|
||||
|
||||
unsigned int ksmbd_server_side_copy_max_total_size(void)
|
||||
{
|
||||
return (2U << 30) - 1;
|
||||
}
|
||||
|
||||
inline int ksmbd_min_protocol(void)
|
||||
{
|
||||
return SMB2_PROT;
|
||||
}
|
||||
|
||||
inline int ksmbd_max_protocol(void)
|
||||
{
|
||||
return SMB311_PROT;
|
||||
}
|
||||
|
||||
int ksmbd_lookup_protocol_idx(char *str)
|
||||
{
|
||||
int offt = ARRAY_SIZE(smb1_protos) - 1;
|
||||
int len = strlen(str);
|
||||
|
||||
while (offt >= 0) {
|
||||
if (!strncmp(str, smb1_protos[offt].prot, len)) {
|
||||
ksmbd_debug(SMB, "selected %s dialect idx = %d\n",
|
||||
smb1_protos[offt].prot, offt);
|
||||
return smb1_protos[offt].index;
|
||||
}
|
||||
offt--;
|
||||
}
|
||||
|
||||
offt = ARRAY_SIZE(smb2_protos) - 1;
|
||||
while (offt >= 0) {
|
||||
if (!strncmp(str, smb2_protos[offt].prot, len)) {
|
||||
ksmbd_debug(SMB, "selected %s dialect idx = %d\n",
|
||||
smb2_protos[offt].prot, offt);
|
||||
return smb2_protos[offt].index;
|
||||
}
|
||||
offt--;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_verify_smb_message() - check for valid smb2 request header
|
||||
* @work: smb work
|
||||
*
|
||||
* check for valid smb signature and packet direction(request/response)
|
||||
*
|
||||
* Return: 0 on success, otherwise 1
|
||||
*/
|
||||
int ksmbd_verify_smb_message(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb2_hdr *smb2_hdr = work->request_buf;
|
||||
|
||||
if (smb2_hdr->ProtocolId == SMB2_PROTO_NUMBER)
|
||||
return ksmbd_smb2_check_message(work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_smb_request() - check for valid smb request type
|
||||
* @conn: connection instance
|
||||
*
|
||||
* Return: true on success, otherwise false
|
||||
*/
|
||||
bool ksmbd_smb_request(struct ksmbd_conn *conn)
|
||||
{
|
||||
int type = *(char *)conn->request_buf;
|
||||
|
||||
switch (type) {
|
||||
case RFC1002_SESSION_MESSAGE:
|
||||
/* Regular SMB request */
|
||||
return true;
|
||||
case RFC1002_SESSION_KEEP_ALIVE:
|
||||
ksmbd_debug(SMB, "RFC 1002 session keep alive\n");
|
||||
break;
|
||||
default:
|
||||
ksmbd_debug(SMB, "RFC 1002 unknown request type 0x%x\n", type);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool supported_protocol(int idx)
|
||||
{
|
||||
if (idx == SMB2X_PROT &&
|
||||
(server_conf.min_protocol >= SMB21_PROT ||
|
||||
server_conf.max_protocol <= SMB311_PROT))
|
||||
return true;
|
||||
|
||||
return (server_conf.min_protocol <= idx &&
|
||||
idx <= server_conf.max_protocol);
|
||||
}
|
||||
|
||||
static char *next_dialect(char *dialect, int *next_off)
|
||||
{
|
||||
dialect = dialect + *next_off;
|
||||
*next_off = strlen(dialect);
|
||||
return dialect;
|
||||
}
|
||||
|
||||
static int ksmbd_lookup_dialect_by_name(char *cli_dialects, __le16 byte_count)
|
||||
{
|
||||
int i, seq_num, bcount, next;
|
||||
char *dialect;
|
||||
|
||||
for (i = ARRAY_SIZE(smb1_protos) - 1; i >= 0; i--) {
|
||||
seq_num = 0;
|
||||
next = 0;
|
||||
dialect = cli_dialects;
|
||||
bcount = le16_to_cpu(byte_count);
|
||||
do {
|
||||
dialect = next_dialect(dialect, &next);
|
||||
ksmbd_debug(SMB, "client requested dialect %s\n",
|
||||
dialect);
|
||||
if (!strcmp(dialect, smb1_protos[i].name)) {
|
||||
if (supported_protocol(smb1_protos[i].index)) {
|
||||
ksmbd_debug(SMB,
|
||||
"selected %s dialect\n",
|
||||
smb1_protos[i].name);
|
||||
if (smb1_protos[i].index == SMB1_PROT)
|
||||
return seq_num;
|
||||
return smb1_protos[i].prot_id;
|
||||
}
|
||||
}
|
||||
seq_num++;
|
||||
bcount -= (++next);
|
||||
} while (bcount > 0);
|
||||
}
|
||||
|
||||
return BAD_PROT_ID;
|
||||
}
|
||||
|
||||
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count)
|
||||
{
|
||||
int i;
|
||||
int count;
|
||||
|
||||
for (i = ARRAY_SIZE(smb2_protos) - 1; i >= 0; i--) {
|
||||
count = le16_to_cpu(dialects_count);
|
||||
while (--count >= 0) {
|
||||
ksmbd_debug(SMB, "client requested dialect 0x%x\n",
|
||||
le16_to_cpu(cli_dialects[count]));
|
||||
if (le16_to_cpu(cli_dialects[count]) !=
|
||||
smb2_protos[i].prot_id)
|
||||
continue;
|
||||
|
||||
if (supported_protocol(smb2_protos[i].index)) {
|
||||
ksmbd_debug(SMB, "selected %s dialect\n",
|
||||
smb2_protos[i].name);
|
||||
return smb2_protos[i].prot_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BAD_PROT_ID;
|
||||
}
|
||||
|
||||
static int ksmbd_negotiate_smb_dialect(void *buf)
|
||||
{
|
||||
__le32 proto;
|
||||
|
||||
proto = ((struct smb2_hdr *)buf)->ProtocolId;
|
||||
if (proto == SMB2_PROTO_NUMBER) {
|
||||
struct smb2_negotiate_req *req;
|
||||
|
||||
req = (struct smb2_negotiate_req *)buf;
|
||||
return ksmbd_lookup_dialect_by_id(req->Dialects,
|
||||
req->DialectCount);
|
||||
}
|
||||
|
||||
proto = *(__le32 *)((struct smb_hdr *)buf)->Protocol;
|
||||
if (proto == SMB1_PROTO_NUMBER) {
|
||||
struct smb_negotiate_req *req;
|
||||
|
||||
req = (struct smb_negotiate_req *)buf;
|
||||
return ksmbd_lookup_dialect_by_name(req->DialectsArray,
|
||||
req->ByteCount);
|
||||
}
|
||||
|
||||
return BAD_PROT_ID;
|
||||
}
|
||||
|
||||
#define SMB_COM_NEGOTIATE 0x72
|
||||
int ksmbd_init_smb_server(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
|
||||
if (conn->need_neg == false)
|
||||
return 0;
|
||||
|
||||
init_smb3_11_server(conn);
|
||||
|
||||
if (conn->ops->get_cmd_val(work) != SMB_COM_NEGOTIATE)
|
||||
conn->need_neg = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool ksmbd_pdu_size_has_room(unsigned int pdu)
|
||||
{
|
||||
return (pdu >= KSMBD_MIN_SUPPORTED_HEADER_SIZE - 4);
|
||||
}
|
||||
|
||||
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
|
||||
struct ksmbd_file *dir,
|
||||
struct ksmbd_dir_info *d_info,
|
||||
char *search_pattern,
|
||||
int (*fn)(struct ksmbd_conn *, int,
|
||||
struct ksmbd_dir_info *,
|
||||
struct user_namespace *,
|
||||
struct ksmbd_kstat *))
|
||||
{
|
||||
int i, rc = 0;
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
struct user_namespace *user_ns = file_mnt_user_ns(dir->filp);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct kstat kstat;
|
||||
struct ksmbd_kstat ksmbd_kstat;
|
||||
|
||||
if (!dir->dot_dotdot[i]) { /* fill dot entry info */
|
||||
if (i == 0) {
|
||||
d_info->name = ".";
|
||||
d_info->name_len = 1;
|
||||
} else {
|
||||
d_info->name = "..";
|
||||
d_info->name_len = 2;
|
||||
}
|
||||
|
||||
if (!match_pattern(d_info->name, d_info->name_len,
|
||||
search_pattern)) {
|
||||
dir->dot_dotdot[i] = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
ksmbd_kstat.kstat = &kstat;
|
||||
ksmbd_vfs_fill_dentry_attrs(work,
|
||||
user_ns,
|
||||
dir->filp->f_path.dentry->d_parent,
|
||||
&ksmbd_kstat);
|
||||
rc = fn(conn, info_level, d_info,
|
||||
user_ns, &ksmbd_kstat);
|
||||
if (rc)
|
||||
break;
|
||||
if (d_info->out_buf_len <= 0)
|
||||
break;
|
||||
|
||||
dir->dot_dotdot[i] = 1;
|
||||
if (d_info->flags & SMB2_RETURN_SINGLE_ENTRY) {
|
||||
d_info->out_buf_len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_extract_shortname() - get shortname from long filename
|
||||
* @conn: connection instance
|
||||
* @longname: source long filename
|
||||
* @shortname: destination short filename
|
||||
*
|
||||
* Return: shortname length or 0 when source long name is '.' or '..'
|
||||
* TODO: Though this function comforms the restriction of 8.3 Filename spec,
|
||||
* but the result is different with Windows 7's one. need to check.
|
||||
*/
|
||||
int ksmbd_extract_shortname(struct ksmbd_conn *conn, const char *longname,
|
||||
char *shortname)
|
||||
{
|
||||
const char *p;
|
||||
char base[9], extension[4];
|
||||
char out[13] = {0};
|
||||
int baselen = 0;
|
||||
int extlen = 0, len = 0;
|
||||
unsigned int csum = 0;
|
||||
const unsigned char *ptr;
|
||||
bool dot_present = true;
|
||||
|
||||
p = longname;
|
||||
if ((*p == '.') || (!(strcmp(p, "..")))) {
|
||||
/*no mangling required */
|
||||
return 0;
|
||||
}
|
||||
|
||||
p = strrchr(longname, '.');
|
||||
if (p == longname) { /*name starts with a dot*/
|
||||
strscpy(extension, "___", strlen("___"));
|
||||
} else {
|
||||
if (p) {
|
||||
p++;
|
||||
while (*p && extlen < 3) {
|
||||
if (*p != '.')
|
||||
extension[extlen++] = toupper(*p);
|
||||
p++;
|
||||
}
|
||||
extension[extlen] = '\0';
|
||||
} else {
|
||||
dot_present = false;
|
||||
}
|
||||
}
|
||||
|
||||
p = longname;
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
longname++;
|
||||
}
|
||||
while (*p && (baselen < 5)) {
|
||||
if (*p != '.')
|
||||
base[baselen++] = toupper(*p);
|
||||
p++;
|
||||
}
|
||||
|
||||
base[baselen] = MAGIC_CHAR;
|
||||
memcpy(out, base, baselen + 1);
|
||||
|
||||
ptr = longname;
|
||||
len = strlen(longname);
|
||||
for (; len > 0; len--, ptr++)
|
||||
csum += *ptr;
|
||||
|
||||
csum = csum % (MANGLE_BASE * MANGLE_BASE);
|
||||
out[baselen + 1] = mangle(csum / MANGLE_BASE);
|
||||
out[baselen + 2] = mangle(csum);
|
||||
out[baselen + 3] = PERIOD;
|
||||
|
||||
if (dot_present)
|
||||
memcpy(&out[baselen + 4], extension, 4);
|
||||
else
|
||||
out[baselen + 4] = '\0';
|
||||
smbConvertToUTF16((__le16 *)shortname, out, PATH_MAX,
|
||||
conn->local_nls, 0);
|
||||
len = strlen(out) * 2;
|
||||
return len;
|
||||
}
|
||||
|
||||
static int __smb2_negotiate(struct ksmbd_conn *conn)
|
||||
{
|
||||
return (conn->dialect >= SMB20_PROT_ID &&
|
||||
conn->dialect <= SMB311_PROT_ID);
|
||||
}
|
||||
|
||||
static int smb_handle_negotiate(struct ksmbd_work *work)
|
||||
{
|
||||
struct smb_negotiate_rsp *neg_rsp = work->response_buf;
|
||||
|
||||
ksmbd_debug(SMB, "Unsupported SMB protocol\n");
|
||||
neg_rsp->hdr.Status.CifsError = STATUS_INVALID_LOGON_TYPE;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command)
|
||||
{
|
||||
struct ksmbd_conn *conn = work->conn;
|
||||
int ret;
|
||||
|
||||
conn->dialect = ksmbd_negotiate_smb_dialect(work->request_buf);
|
||||
ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect);
|
||||
|
||||
if (command == SMB2_NEGOTIATE_HE) {
|
||||
struct smb2_hdr *smb2_hdr = work->request_buf;
|
||||
|
||||
if (smb2_hdr->ProtocolId != SMB2_PROTO_NUMBER) {
|
||||
ksmbd_debug(SMB, "Downgrade to SMB1 negotiation\n");
|
||||
command = SMB_COM_NEGOTIATE;
|
||||
}
|
||||
}
|
||||
|
||||
if (command == SMB2_NEGOTIATE_HE) {
|
||||
ret = smb2_handle_negotiate(work);
|
||||
init_smb2_neg_rsp(work);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (command == SMB_COM_NEGOTIATE) {
|
||||
if (__smb2_negotiate(conn)) {
|
||||
conn->need_neg = true;
|
||||
init_smb3_11_server(conn);
|
||||
init_smb2_neg_rsp(work);
|
||||
ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n");
|
||||
return 0;
|
||||
}
|
||||
return smb_handle_negotiate(work);
|
||||
}
|
||||
|
||||
pr_err("Unknown SMB negotiation command: %u\n", command);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enum SHARED_MODE_ERRORS {
|
||||
SHARE_DELETE_ERROR,
|
||||
SHARE_READ_ERROR,
|
||||
SHARE_WRITE_ERROR,
|
||||
FILE_READ_ERROR,
|
||||
FILE_WRITE_ERROR,
|
||||
FILE_DELETE_ERROR,
|
||||
};
|
||||
|
||||
static const char * const shared_mode_errors[] = {
|
||||
"Current access mode does not permit SHARE_DELETE",
|
||||
"Current access mode does not permit SHARE_READ",
|
||||
"Current access mode does not permit SHARE_WRITE",
|
||||
"Desired access mode does not permit FILE_READ",
|
||||
"Desired access mode does not permit FILE_WRITE",
|
||||
"Desired access mode does not permit FILE_DELETE",
|
||||
};
|
||||
|
||||
static void smb_shared_mode_error(int error, struct ksmbd_file *prev_fp,
|
||||
struct ksmbd_file *curr_fp)
|
||||
{
|
||||
ksmbd_debug(SMB, "%s\n", shared_mode_errors[error]);
|
||||
ksmbd_debug(SMB, "Current mode: 0x%x Desired mode: 0x%x\n",
|
||||
prev_fp->saccess, curr_fp->daccess);
|
||||
}
|
||||
|
||||
int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ksmbd_file *prev_fp;
|
||||
|
||||
/*
|
||||
* Lookup fp in master fp list, and check desired access and
|
||||
* shared mode between previous open and current open.
|
||||
*/
|
||||
read_lock(&curr_fp->f_ci->m_lock);
|
||||
list_for_each_entry(prev_fp, &curr_fp->f_ci->m_fp_list, node) {
|
||||
if (file_inode(filp) != file_inode(prev_fp->filp))
|
||||
continue;
|
||||
|
||||
if (filp == prev_fp->filp)
|
||||
continue;
|
||||
|
||||
if (ksmbd_stream_fd(prev_fp) && ksmbd_stream_fd(curr_fp))
|
||||
if (strcmp(prev_fp->stream.name, curr_fp->stream.name))
|
||||
continue;
|
||||
|
||||
if (prev_fp->attrib_only != curr_fp->attrib_only)
|
||||
continue;
|
||||
|
||||
if (!(prev_fp->saccess & FILE_SHARE_DELETE_LE) &&
|
||||
curr_fp->daccess & FILE_DELETE_LE) {
|
||||
smb_shared_mode_error(SHARE_DELETE_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Only check FILE_SHARE_DELETE if stream opened and
|
||||
* normal file opened.
|
||||
*/
|
||||
if (ksmbd_stream_fd(prev_fp) && !ksmbd_stream_fd(curr_fp))
|
||||
continue;
|
||||
|
||||
if (!(prev_fp->saccess & FILE_SHARE_READ_LE) &&
|
||||
curr_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE)) {
|
||||
smb_shared_mode_error(SHARE_READ_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(prev_fp->saccess & FILE_SHARE_WRITE_LE) &&
|
||||
curr_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE)) {
|
||||
smb_shared_mode_error(SHARE_WRITE_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev_fp->daccess & (FILE_EXECUTE_LE | FILE_READ_DATA_LE) &&
|
||||
!(curr_fp->saccess & FILE_SHARE_READ_LE)) {
|
||||
smb_shared_mode_error(FILE_READ_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev_fp->daccess & (FILE_WRITE_DATA_LE | FILE_APPEND_DATA_LE) &&
|
||||
!(curr_fp->saccess & FILE_SHARE_WRITE_LE)) {
|
||||
smb_shared_mode_error(FILE_WRITE_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (prev_fp->daccess & FILE_DELETE_LE &&
|
||||
!(curr_fp->saccess & FILE_SHARE_DELETE_LE)) {
|
||||
smb_shared_mode_error(FILE_DELETE_ERROR,
|
||||
prev_fp,
|
||||
curr_fp);
|
||||
rc = -EPERM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&curr_fp->f_ci->m_lock);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
bool is_asterisk(char *p)
|
||||
{
|
||||
return p && p[0] == '*';
|
||||
}
|
||||
|
||||
int ksmbd_override_fsids(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_session *sess = work->sess;
|
||||
struct ksmbd_share_config *share = work->tcon->share_conf;
|
||||
struct cred *cred;
|
||||
struct group_info *gi;
|
||||
unsigned int uid;
|
||||
unsigned int gid;
|
||||
|
||||
uid = user_uid(sess->user);
|
||||
gid = user_gid(sess->user);
|
||||
if (share->force_uid != KSMBD_SHARE_INVALID_UID)
|
||||
uid = share->force_uid;
|
||||
if (share->force_gid != KSMBD_SHARE_INVALID_GID)
|
||||
gid = share->force_gid;
|
||||
|
||||
cred = prepare_kernel_cred(NULL);
|
||||
if (!cred)
|
||||
return -ENOMEM;
|
||||
|
||||
cred->fsuid = make_kuid(current_user_ns(), uid);
|
||||
cred->fsgid = make_kgid(current_user_ns(), gid);
|
||||
|
||||
gi = groups_alloc(0);
|
||||
if (!gi) {
|
||||
abort_creds(cred);
|
||||
return -ENOMEM;
|
||||
}
|
||||
set_groups(cred, gi);
|
||||
put_group_info(gi);
|
||||
|
||||
if (!uid_eq(cred->fsuid, GLOBAL_ROOT_UID))
|
||||
cred->cap_effective = cap_drop_fs_set(cred->cap_effective);
|
||||
|
||||
WARN_ON(work->saved_cred);
|
||||
work->saved_cred = override_creds(cred);
|
||||
if (!work->saved_cred) {
|
||||
abort_creds(cred);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_revert_fsids(struct ksmbd_work *work)
|
||||
{
|
||||
const struct cred *cred;
|
||||
|
||||
WARN_ON(!work->saved_cred);
|
||||
|
||||
cred = current_cred();
|
||||
revert_creds(work->saved_cred);
|
||||
put_cred(cred);
|
||||
work->saved_cred = NULL;
|
||||
}
|
||||
|
||||
__le32 smb_map_generic_desired_access(__le32 daccess)
|
||||
{
|
||||
if (daccess & FILE_GENERIC_READ_LE) {
|
||||
daccess |= cpu_to_le32(GENERIC_READ_FLAGS);
|
||||
daccess &= ~FILE_GENERIC_READ_LE;
|
||||
}
|
||||
|
||||
if (daccess & FILE_GENERIC_WRITE_LE) {
|
||||
daccess |= cpu_to_le32(GENERIC_WRITE_FLAGS);
|
||||
daccess &= ~FILE_GENERIC_WRITE_LE;
|
||||
}
|
||||
|
||||
if (daccess & FILE_GENERIC_EXECUTE_LE) {
|
||||
daccess |= cpu_to_le32(GENERIC_EXECUTE_FLAGS);
|
||||
daccess &= ~FILE_GENERIC_EXECUTE_LE;
|
||||
}
|
||||
|
||||
if (daccess & FILE_GENERIC_ALL_LE) {
|
||||
daccess |= cpu_to_le32(GENERIC_ALL_FLAGS);
|
||||
daccess &= ~FILE_GENERIC_ALL_LE;
|
||||
}
|
||||
|
||||
return daccess;
|
||||
}
|
|
@ -0,0 +1,542 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __SMB_COMMON_H__
|
||||
#define __SMB_COMMON_H__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "nterr.h"
|
||||
#include "smb2pdu.h"
|
||||
|
||||
/* ksmbd's Specific ERRNO */
|
||||
#define ESHARE 50000
|
||||
|
||||
#define SMB1_PROT 0
|
||||
#define SMB2_PROT 1
|
||||
#define SMB21_PROT 2
|
||||
/* multi-protocol negotiate request */
|
||||
#define SMB2X_PROT 3
|
||||
#define SMB30_PROT 4
|
||||
#define SMB302_PROT 5
|
||||
#define SMB311_PROT 6
|
||||
#define BAD_PROT 0xFFFF
|
||||
|
||||
#define SMB1_VERSION_STRING "1.0"
|
||||
#define SMB20_VERSION_STRING "2.0"
|
||||
#define SMB21_VERSION_STRING "2.1"
|
||||
#define SMB30_VERSION_STRING "3.0"
|
||||
#define SMB302_VERSION_STRING "3.02"
|
||||
#define SMB311_VERSION_STRING "3.1.1"
|
||||
|
||||
/* Dialects */
|
||||
#define SMB10_PROT_ID 0x00
|
||||
#define SMB20_PROT_ID 0x0202
|
||||
#define SMB21_PROT_ID 0x0210
|
||||
/* multi-protocol negotiate request */
|
||||
#define SMB2X_PROT_ID 0x02FF
|
||||
#define SMB30_PROT_ID 0x0300
|
||||
#define SMB302_PROT_ID 0x0302
|
||||
#define SMB311_PROT_ID 0x0311
|
||||
#define BAD_PROT_ID 0xFFFF
|
||||
|
||||
#define SMB_ECHO_INTERVAL (60 * HZ)
|
||||
|
||||
#define CIFS_DEFAULT_IOSIZE (64 * 1024)
|
||||
#define MAX_CIFS_SMALL_BUFFER_SIZE 448 /* big enough for most */
|
||||
|
||||
/* RFC 1002 session packet types */
|
||||
#define RFC1002_SESSION_MESSAGE 0x00
|
||||
#define RFC1002_SESSION_REQUEST 0x81
|
||||
#define RFC1002_POSITIVE_SESSION_RESPONSE 0x82
|
||||
#define RFC1002_NEGATIVE_SESSION_RESPONSE 0x83
|
||||
#define RFC1002_RETARGET_SESSION_RESPONSE 0x84
|
||||
#define RFC1002_SESSION_KEEP_ALIVE 0x85
|
||||
|
||||
/* Responses when opening a file. */
|
||||
#define F_SUPERSEDED 0
|
||||
#define F_OPENED 1
|
||||
#define F_CREATED 2
|
||||
#define F_OVERWRITTEN 3
|
||||
|
||||
/*
|
||||
* File Attribute flags
|
||||
*/
|
||||
#define ATTR_READONLY 0x0001
|
||||
#define ATTR_HIDDEN 0x0002
|
||||
#define ATTR_SYSTEM 0x0004
|
||||
#define ATTR_VOLUME 0x0008
|
||||
#define ATTR_DIRECTORY 0x0010
|
||||
#define ATTR_ARCHIVE 0x0020
|
||||
#define ATTR_DEVICE 0x0040
|
||||
#define ATTR_NORMAL 0x0080
|
||||
#define ATTR_TEMPORARY 0x0100
|
||||
#define ATTR_SPARSE 0x0200
|
||||
#define ATTR_REPARSE 0x0400
|
||||
#define ATTR_COMPRESSED 0x0800
|
||||
#define ATTR_OFFLINE 0x1000
|
||||
#define ATTR_NOT_CONTENT_INDEXED 0x2000
|
||||
#define ATTR_ENCRYPTED 0x4000
|
||||
#define ATTR_POSIX_SEMANTICS 0x01000000
|
||||
#define ATTR_BACKUP_SEMANTICS 0x02000000
|
||||
#define ATTR_DELETE_ON_CLOSE 0x04000000
|
||||
#define ATTR_SEQUENTIAL_SCAN 0x08000000
|
||||
#define ATTR_RANDOM_ACCESS 0x10000000
|
||||
#define ATTR_NO_BUFFERING 0x20000000
|
||||
#define ATTR_WRITE_THROUGH 0x80000000
|
||||
|
||||
#define ATTR_READONLY_LE cpu_to_le32(ATTR_READONLY)
|
||||
#define ATTR_HIDDEN_LE cpu_to_le32(ATTR_HIDDEN)
|
||||
#define ATTR_SYSTEM_LE cpu_to_le32(ATTR_SYSTEM)
|
||||
#define ATTR_DIRECTORY_LE cpu_to_le32(ATTR_DIRECTORY)
|
||||
#define ATTR_ARCHIVE_LE cpu_to_le32(ATTR_ARCHIVE)
|
||||
#define ATTR_NORMAL_LE cpu_to_le32(ATTR_NORMAL)
|
||||
#define ATTR_TEMPORARY_LE cpu_to_le32(ATTR_TEMPORARY)
|
||||
#define ATTR_SPARSE_FILE_LE cpu_to_le32(ATTR_SPARSE)
|
||||
#define ATTR_REPARSE_POINT_LE cpu_to_le32(ATTR_REPARSE)
|
||||
#define ATTR_COMPRESSED_LE cpu_to_le32(ATTR_COMPRESSED)
|
||||
#define ATTR_OFFLINE_LE cpu_to_le32(ATTR_OFFLINE)
|
||||
#define ATTR_NOT_CONTENT_INDEXED_LE cpu_to_le32(ATTR_NOT_CONTENT_INDEXED)
|
||||
#define ATTR_ENCRYPTED_LE cpu_to_le32(ATTR_ENCRYPTED)
|
||||
#define ATTR_INTEGRITY_STREAML_LE cpu_to_le32(0x00008000)
|
||||
#define ATTR_NO_SCRUB_DATA_LE cpu_to_le32(0x00020000)
|
||||
#define ATTR_MASK_LE cpu_to_le32(0x00007FB7)
|
||||
|
||||
/* List of FileSystemAttributes - see 2.5.1 of MS-FSCC */
|
||||
#define FILE_SUPPORTS_SPARSE_VDL 0x10000000 /* faster nonsparse extend */
|
||||
#define FILE_SUPPORTS_BLOCK_REFCOUNTING 0x08000000 /* allow ioctl dup extents */
|
||||
#define FILE_SUPPORT_INTEGRITY_STREAMS 0x04000000
|
||||
#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
|
||||
#define FILE_SUPPORTS_OPEN_BY_FILE_ID 0x01000000
|
||||
#define FILE_SUPPORTS_EXTENDED_ATTRIBUTES 0x00800000
|
||||
#define FILE_SUPPORTS_HARD_LINKS 0x00400000
|
||||
#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
|
||||
#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
|
||||
#define FILE_READ_ONLY_VOLUME 0x00080000
|
||||
#define FILE_NAMED_STREAMS 0x00040000
|
||||
#define FILE_SUPPORTS_ENCRYPTION 0x00020000
|
||||
#define FILE_SUPPORTS_OBJECT_IDS 0x00010000
|
||||
#define FILE_VOLUME_IS_COMPRESSED 0x00008000
|
||||
#define FILE_SUPPORTS_REMOTE_STORAGE 0x00000100
|
||||
#define FILE_SUPPORTS_REPARSE_POINTS 0x00000080
|
||||
#define FILE_SUPPORTS_SPARSE_FILES 0x00000040
|
||||
#define FILE_VOLUME_QUOTAS 0x00000020
|
||||
#define FILE_FILE_COMPRESSION 0x00000010
|
||||
#define FILE_PERSISTENT_ACLS 0x00000008
|
||||
#define FILE_UNICODE_ON_DISK 0x00000004
|
||||
#define FILE_CASE_PRESERVED_NAMES 0x00000002
|
||||
#define FILE_CASE_SENSITIVE_SEARCH 0x00000001
|
||||
|
||||
#define FILE_READ_DATA 0x00000001 /* Data can be read from the file */
|
||||
#define FILE_WRITE_DATA 0x00000002 /* Data can be written to the file */
|
||||
#define FILE_APPEND_DATA 0x00000004 /* Data can be appended to the file */
|
||||
#define FILE_READ_EA 0x00000008 /* Extended attributes associated */
|
||||
/* with the file can be read */
|
||||
#define FILE_WRITE_EA 0x00000010 /* Extended attributes associated */
|
||||
/* with the file can be written */
|
||||
#define FILE_EXECUTE 0x00000020 /*Data can be read into memory from */
|
||||
/* the file using system paging I/O */
|
||||
#define FILE_DELETE_CHILD 0x00000040
|
||||
#define FILE_READ_ATTRIBUTES 0x00000080 /* Attributes associated with the */
|
||||
/* file can be read */
|
||||
#define FILE_WRITE_ATTRIBUTES 0x00000100 /* Attributes associated with the */
|
||||
/* file can be written */
|
||||
#define DELETE 0x00010000 /* The file can be deleted */
|
||||
#define READ_CONTROL 0x00020000 /* The access control list and */
|
||||
/* ownership associated with the */
|
||||
/* file can be read */
|
||||
#define WRITE_DAC 0x00040000 /* The access control list and */
|
||||
/* ownership associated with the */
|
||||
/* file can be written. */
|
||||
#define WRITE_OWNER 0x00080000 /* Ownership information associated */
|
||||
/* with the file can be written */
|
||||
#define SYNCHRONIZE 0x00100000 /* The file handle can waited on to */
|
||||
/* synchronize with the completion */
|
||||
/* of an input/output request */
|
||||
#define GENERIC_ALL 0x10000000
|
||||
#define GENERIC_EXECUTE 0x20000000
|
||||
#define GENERIC_WRITE 0x40000000
|
||||
#define GENERIC_READ 0x80000000
|
||||
/* In summary - Relevant file */
|
||||
/* access flags from CIFS are */
|
||||
/* file_read_data, file_write_data */
|
||||
/* file_execute, file_read_attributes*/
|
||||
/* write_dac, and delete. */
|
||||
|
||||
#define FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_READ_ATTRIBUTES)
|
||||
#define FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||
| FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES)
|
||||
#define FILE_EXEC_RIGHTS (FILE_EXECUTE)
|
||||
|
||||
#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA \
|
||||
| FILE_READ_ATTRIBUTES \
|
||||
| DELETE | READ_CONTROL | WRITE_DAC \
|
||||
| WRITE_OWNER | SYNCHRONIZE)
|
||||
#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \
|
||||
| FILE_WRITE_EA \
|
||||
| FILE_DELETE_CHILD \
|
||||
| FILE_WRITE_ATTRIBUTES \
|
||||
| DELETE | READ_CONTROL | WRITE_DAC \
|
||||
| WRITE_OWNER | SYNCHRONIZE)
|
||||
#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \
|
||||
| FILE_READ_ATTRIBUTES \
|
||||
| FILE_WRITE_ATTRIBUTES \
|
||||
| DELETE | READ_CONTROL | WRITE_DAC \
|
||||
| WRITE_OWNER | SYNCHRONIZE)
|
||||
|
||||
#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
|
||||
| READ_CONTROL | SYNCHRONIZE)
|
||||
|
||||
/* generic flags for file open */
|
||||
#define GENERIC_READ_FLAGS (READ_CONTROL | FILE_READ_DATA | \
|
||||
FILE_READ_ATTRIBUTES | \
|
||||
FILE_READ_EA | SYNCHRONIZE)
|
||||
|
||||
#define GENERIC_WRITE_FLAGS (READ_CONTROL | FILE_WRITE_DATA | \
|
||||
FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | \
|
||||
FILE_APPEND_DATA | SYNCHRONIZE)
|
||||
|
||||
#define GENERIC_EXECUTE_FLAGS (READ_CONTROL | FILE_EXECUTE | \
|
||||
FILE_READ_ATTRIBUTES | SYNCHRONIZE)
|
||||
|
||||
#define GENERIC_ALL_FLAGS (DELETE | READ_CONTROL | WRITE_DAC | \
|
||||
WRITE_OWNER | SYNCHRONIZE | FILE_READ_DATA | \
|
||||
FILE_WRITE_DATA | FILE_APPEND_DATA | \
|
||||
FILE_READ_EA | FILE_WRITE_EA | \
|
||||
FILE_EXECUTE | FILE_DELETE_CHILD | \
|
||||
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES)
|
||||
|
||||
#define SMB1_PROTO_NUMBER cpu_to_le32(0x424d53ff)
|
||||
|
||||
#define SMB1_CLIENT_GUID_SIZE (16)
|
||||
struct smb_hdr {
|
||||
__be32 smb_buf_length;
|
||||
__u8 Protocol[4];
|
||||
__u8 Command;
|
||||
union {
|
||||
struct {
|
||||
__u8 ErrorClass;
|
||||
__u8 Reserved;
|
||||
__le16 Error;
|
||||
} __packed DosError;
|
||||
__le32 CifsError;
|
||||
} __packed Status;
|
||||
__u8 Flags;
|
||||
__le16 Flags2; /* note: le */
|
||||
__le16 PidHigh;
|
||||
union {
|
||||
struct {
|
||||
__le32 SequenceNumber; /* le */
|
||||
__u32 Reserved; /* zero */
|
||||
} __packed Sequence;
|
||||
__u8 SecuritySignature[8]; /* le */
|
||||
} __packed Signature;
|
||||
__u8 pad[2];
|
||||
__le16 Tid;
|
||||
__le16 Pid;
|
||||
__le16 Uid;
|
||||
__le16 Mid;
|
||||
__u8 WordCount;
|
||||
} __packed;
|
||||
|
||||
struct smb_negotiate_req {
|
||||
struct smb_hdr hdr; /* wct = 0 */
|
||||
__le16 ByteCount;
|
||||
unsigned char DialectsArray[1];
|
||||
} __packed;
|
||||
|
||||
struct smb_negotiate_rsp {
|
||||
struct smb_hdr hdr; /* wct = 17 */
|
||||
__le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
|
||||
__u8 SecurityMode;
|
||||
__le16 MaxMpxCount;
|
||||
__le16 MaxNumberVcs;
|
||||
__le32 MaxBufferSize;
|
||||
__le32 MaxRawSize;
|
||||
__le32 SessionKey;
|
||||
__le32 Capabilities; /* see below */
|
||||
__le32 SystemTimeLow;
|
||||
__le32 SystemTimeHigh;
|
||||
__le16 ServerTimeZone;
|
||||
__u8 EncryptionKeyLength;
|
||||
__le16 ByteCount;
|
||||
union {
|
||||
unsigned char EncryptionKey[8]; /* cap extended security off */
|
||||
/* followed by Domain name - if extended security is off */
|
||||
/* followed by 16 bytes of server GUID */
|
||||
/* then security blob if cap_extended_security negotiated */
|
||||
struct {
|
||||
unsigned char GUID[SMB1_CLIENT_GUID_SIZE];
|
||||
unsigned char SecurityBlob[1];
|
||||
} __packed extended_response;
|
||||
} __packed u;
|
||||
} __packed;
|
||||
|
||||
struct filesystem_attribute_info {
|
||||
__le32 Attributes;
|
||||
__le32 MaxPathNameComponentLength;
|
||||
__le32 FileSystemNameLen;
|
||||
__le16 FileSystemName[1]; /* do not have to save this - get subset? */
|
||||
} __packed;
|
||||
|
||||
struct filesystem_device_info {
|
||||
__le32 DeviceType;
|
||||
__le32 DeviceCharacteristics;
|
||||
} __packed; /* device info level 0x104 */
|
||||
|
||||
struct filesystem_vol_info {
|
||||
__le64 VolumeCreationTime;
|
||||
__le32 SerialNumber;
|
||||
__le32 VolumeLabelSize;
|
||||
__le16 Reserved;
|
||||
__le16 VolumeLabel[1];
|
||||
} __packed;
|
||||
|
||||
struct filesystem_info {
|
||||
__le64 TotalAllocationUnits;
|
||||
__le64 FreeAllocationUnits;
|
||||
__le32 SectorsPerAllocationUnit;
|
||||
__le32 BytesPerSector;
|
||||
} __packed; /* size info, level 0x103 */
|
||||
|
||||
#define EXTENDED_INFO_MAGIC 0x43667364 /* Cfsd */
|
||||
#define STRING_LENGTH 28
|
||||
|
||||
struct fs_extended_info {
|
||||
__le32 magic;
|
||||
__le32 version;
|
||||
__le32 release;
|
||||
__u64 rel_date;
|
||||
char version_string[STRING_LENGTH];
|
||||
} __packed;
|
||||
|
||||
struct object_id_info {
|
||||
char objid[16];
|
||||
struct fs_extended_info extended_info;
|
||||
} __packed;
|
||||
|
||||
struct file_directory_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le64 EndOfFile;
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
char FileName[1];
|
||||
} __packed; /* level 0x101 FF resp data */
|
||||
|
||||
struct file_names_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le32 FileNameLength;
|
||||
char FileName[1];
|
||||
} __packed; /* level 0xc FF resp data */
|
||||
|
||||
struct file_full_directory_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le64 EndOfFile;
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
__le32 EaSize;
|
||||
char FileName[1];
|
||||
} __packed; /* level 0x102 FF resp */
|
||||
|
||||
struct file_both_directory_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le64 EndOfFile;
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
__le32 EaSize; /* length of the xattrs */
|
||||
__u8 ShortNameLength;
|
||||
__u8 Reserved;
|
||||
__u8 ShortName[24];
|
||||
char FileName[1];
|
||||
} __packed; /* level 0x104 FFrsp data */
|
||||
|
||||
struct file_id_both_directory_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le64 EndOfFile;
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
__le32 EaSize; /* length of the xattrs */
|
||||
__u8 ShortNameLength;
|
||||
__u8 Reserved;
|
||||
__u8 ShortName[24];
|
||||
__le16 Reserved2;
|
||||
__le64 UniqueId;
|
||||
char FileName[1];
|
||||
} __packed;
|
||||
|
||||
struct file_id_full_dir_info {
|
||||
__le32 NextEntryOffset;
|
||||
__u32 FileIndex;
|
||||
__le64 CreationTime;
|
||||
__le64 LastAccessTime;
|
||||
__le64 LastWriteTime;
|
||||
__le64 ChangeTime;
|
||||
__le64 EndOfFile;
|
||||
__le64 AllocationSize;
|
||||
__le32 ExtFileAttributes;
|
||||
__le32 FileNameLength;
|
||||
__le32 EaSize; /* EA size */
|
||||
__le32 Reserved;
|
||||
__le64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
|
||||
char FileName[1];
|
||||
} __packed; /* level 0x105 FF rsp data */
|
||||
|
||||
struct smb_version_values {
|
||||
char *version_string;
|
||||
__u16 protocol_id;
|
||||
__le16 lock_cmd;
|
||||
__u32 capabilities;
|
||||
__u32 max_read_size;
|
||||
__u32 max_write_size;
|
||||
__u32 max_trans_size;
|
||||
__u32 large_lock_type;
|
||||
__u32 exclusive_lock_type;
|
||||
__u32 shared_lock_type;
|
||||
__u32 unlock_lock_type;
|
||||
size_t header_size;
|
||||
size_t max_header_size;
|
||||
size_t read_rsp_size;
|
||||
unsigned int cap_unix;
|
||||
unsigned int cap_nt_find;
|
||||
unsigned int cap_large_files;
|
||||
__u16 signing_enabled;
|
||||
__u16 signing_required;
|
||||
size_t create_lease_size;
|
||||
size_t create_durable_size;
|
||||
size_t create_durable_v2_size;
|
||||
size_t create_mxac_size;
|
||||
size_t create_disk_id_size;
|
||||
size_t create_posix_size;
|
||||
};
|
||||
|
||||
struct filesystem_posix_info {
|
||||
/* For undefined recommended transfer size return -1 in that field */
|
||||
__le32 OptimalTransferSize; /* bsize on some os, iosize on other os */
|
||||
__le32 BlockSize;
|
||||
/* The next three fields are in terms of the block size.
|
||||
* (above). If block size is unknown, 4096 would be a
|
||||
* reasonable block size for a server to report.
|
||||
* Note that returning the blocks/blocksavail removes need
|
||||
* to make a second call (to QFSInfo level 0x103 to get this info.
|
||||
* UserBlockAvail is typically less than or equal to BlocksAvail,
|
||||
* if no distinction is made return the same value in each
|
||||
*/
|
||||
__le64 TotalBlocks;
|
||||
__le64 BlocksAvail; /* bfree */
|
||||
__le64 UserBlocksAvail; /* bavail */
|
||||
/* For undefined Node fields or FSID return -1 */
|
||||
__le64 TotalFileNodes;
|
||||
__le64 FreeFileNodes;
|
||||
__le64 FileSysIdentifier; /* fsid */
|
||||
/* NB Namelen comes from FILE_SYSTEM_ATTRIBUTE_INFO call */
|
||||
/* NB flags can come from FILE_SYSTEM_DEVICE_INFO call */
|
||||
} __packed;
|
||||
|
||||
struct smb_version_ops {
|
||||
u16 (*get_cmd_val)(struct ksmbd_work *swork);
|
||||
int (*init_rsp_hdr)(struct ksmbd_work *swork);
|
||||
void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err);
|
||||
int (*allocate_rsp_buf)(struct ksmbd_work *work);
|
||||
int (*set_rsp_credits)(struct ksmbd_work *work);
|
||||
int (*check_user_session)(struct ksmbd_work *work);
|
||||
int (*get_ksmbd_tcon)(struct ksmbd_work *work);
|
||||
bool (*is_sign_req)(struct ksmbd_work *work, unsigned int command);
|
||||
int (*check_sign_req)(struct ksmbd_work *work);
|
||||
void (*set_sign_rsp)(struct ksmbd_work *work);
|
||||
int (*generate_signingkey)(struct ksmbd_session *sess, struct ksmbd_conn *conn);
|
||||
int (*generate_encryptionkey)(struct ksmbd_session *sess);
|
||||
bool (*is_transform_hdr)(void *buf);
|
||||
int (*decrypt_req)(struct ksmbd_work *work);
|
||||
int (*encrypt_resp)(struct ksmbd_work *work);
|
||||
};
|
||||
|
||||
struct smb_version_cmds {
|
||||
int (*proc)(struct ksmbd_work *swork);
|
||||
};
|
||||
|
||||
static inline size_t
|
||||
smb2_hdr_size_no_buflen(struct smb_version_values *vals)
|
||||
{
|
||||
return vals->header_size - 4;
|
||||
}
|
||||
|
||||
int ksmbd_min_protocol(void);
|
||||
int ksmbd_max_protocol(void);
|
||||
|
||||
int ksmbd_lookup_protocol_idx(char *str);
|
||||
|
||||
int ksmbd_verify_smb_message(struct ksmbd_work *work);
|
||||
bool ksmbd_smb_request(struct ksmbd_conn *conn);
|
||||
|
||||
int ksmbd_lookup_dialect_by_id(__le16 *cli_dialects, __le16 dialects_count);
|
||||
|
||||
int ksmbd_init_smb_server(struct ksmbd_work *work);
|
||||
|
||||
bool ksmbd_pdu_size_has_room(unsigned int pdu);
|
||||
|
||||
struct ksmbd_kstat;
|
||||
int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work,
|
||||
int info_level,
|
||||
struct ksmbd_file *dir,
|
||||
struct ksmbd_dir_info *d_info,
|
||||
char *search_pattern,
|
||||
int (*fn)(struct ksmbd_conn *,
|
||||
int,
|
||||
struct ksmbd_dir_info *,
|
||||
struct user_namespace *,
|
||||
struct ksmbd_kstat *));
|
||||
|
||||
int ksmbd_extract_shortname(struct ksmbd_conn *conn,
|
||||
const char *longname,
|
||||
char *shortname);
|
||||
|
||||
int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command);
|
||||
|
||||
int ksmbd_smb_check_shared_mode(struct file *filp, struct ksmbd_file *curr_fp);
|
||||
int ksmbd_override_fsids(struct ksmbd_work *work);
|
||||
void ksmbd_revert_fsids(struct ksmbd_work *work);
|
||||
|
||||
unsigned int ksmbd_server_side_copy_max_chunk_count(void);
|
||||
unsigned int ksmbd_server_side_copy_max_chunk_size(void);
|
||||
unsigned int ksmbd_server_side_copy_max_total_size(void);
|
||||
bool is_asterisk(char *p);
|
||||
__le32 smb_map_generic_desired_access(__le32 daccess);
|
||||
|
||||
static inline unsigned int get_rfc1002_len(void *buf)
|
||||
{
|
||||
return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;
|
||||
}
|
||||
|
||||
static inline void inc_rfc1001_len(void *buf, int count)
|
||||
{
|
||||
be32_add_cpu((__be32 *)buf, count);
|
||||
}
|
||||
#endif /* __SMB_COMMON_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,212 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
/*
|
||||
* Copyright (c) International Business Machines Corp., 2007
|
||||
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||
* Modified by Namjae Jeon (linkinjeon@kernel.org)
|
||||
*/
|
||||
|
||||
#ifndef _SMBACL_H
|
||||
#define _SMBACL_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/posix_acl.h>
|
||||
|
||||
#include "mgmt/tree_connect.h"
|
||||
|
||||
#define NUM_AUTHS (6) /* number of authority fields */
|
||||
#define SID_MAX_SUB_AUTHORITIES (15) /* max number of sub authority fields */
|
||||
|
||||
/*
|
||||
* ACE types - see MS-DTYP 2.4.4.1
|
||||
*/
|
||||
enum {
|
||||
ACCESS_ALLOWED,
|
||||
ACCESS_DENIED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Security ID types
|
||||
*/
|
||||
enum {
|
||||
SIDOWNER = 1,
|
||||
SIDGROUP,
|
||||
SIDCREATOR_OWNER,
|
||||
SIDCREATOR_GROUP,
|
||||
SIDUNIX_USER,
|
||||
SIDUNIX_GROUP,
|
||||
SIDNFS_USER,
|
||||
SIDNFS_GROUP,
|
||||
SIDNFS_MODE,
|
||||
};
|
||||
|
||||
/* Revision for ACLs */
|
||||
#define SD_REVISION 1
|
||||
|
||||
/* Control flags for Security Descriptor */
|
||||
#define OWNER_DEFAULTED 0x0001
|
||||
#define GROUP_DEFAULTED 0x0002
|
||||
#define DACL_PRESENT 0x0004
|
||||
#define DACL_DEFAULTED 0x0008
|
||||
#define SACL_PRESENT 0x0010
|
||||
#define SACL_DEFAULTED 0x0020
|
||||
#define DACL_TRUSTED 0x0040
|
||||
#define SERVER_SECURITY 0x0080
|
||||
#define DACL_AUTO_INHERIT_REQ 0x0100
|
||||
#define SACL_AUTO_INHERIT_REQ 0x0200
|
||||
#define DACL_AUTO_INHERITED 0x0400
|
||||
#define SACL_AUTO_INHERITED 0x0800
|
||||
#define DACL_PROTECTED 0x1000
|
||||
#define SACL_PROTECTED 0x2000
|
||||
#define RM_CONTROL_VALID 0x4000
|
||||
#define SELF_RELATIVE 0x8000
|
||||
|
||||
/* ACE types - see MS-DTYP 2.4.4.1 */
|
||||
#define ACCESS_ALLOWED_ACE_TYPE 0x00
|
||||
#define ACCESS_DENIED_ACE_TYPE 0x01
|
||||
#define SYSTEM_AUDIT_ACE_TYPE 0x02
|
||||
#define SYSTEM_ALARM_ACE_TYPE 0x03
|
||||
#define ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04
|
||||
#define ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05
|
||||
#define ACCESS_DENIED_OBJECT_ACE_TYPE 0x06
|
||||
#define SYSTEM_AUDIT_OBJECT_ACE_TYPE 0x07
|
||||
#define SYSTEM_ALARM_OBJECT_ACE_TYPE 0x08
|
||||
#define ACCESS_ALLOWED_CALLBACK_ACE_TYPE 0x09
|
||||
#define ACCESS_DENIED_CALLBACK_ACE_TYPE 0x0A
|
||||
#define ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE 0x0B
|
||||
#define ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE 0x0C
|
||||
#define SYSTEM_AUDIT_CALLBACK_ACE_TYPE 0x0D
|
||||
#define SYSTEM_ALARM_CALLBACK_ACE_TYPE 0x0E /* Reserved */
|
||||
#define SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE 0x0F
|
||||
#define SYSTEM_ALARM_CALLBACK_OBJECT_ACE_TYPE 0x10 /* reserved */
|
||||
#define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11
|
||||
#define SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE 0x12
|
||||
#define SYSTEM_SCOPED_POLICY_ID_ACE_TYPE 0x13
|
||||
|
||||
/* ACE flags */
|
||||
#define OBJECT_INHERIT_ACE 0x01
|
||||
#define CONTAINER_INHERIT_ACE 0x02
|
||||
#define NO_PROPAGATE_INHERIT_ACE 0x04
|
||||
#define INHERIT_ONLY_ACE 0x08
|
||||
#define INHERITED_ACE 0x10
|
||||
#define SUCCESSFUL_ACCESS_ACE_FLAG 0x40
|
||||
#define FAILED_ACCESS_ACE_FLAG 0x80
|
||||
|
||||
/*
|
||||
* Maximum size of a string representation of a SID:
|
||||
*
|
||||
* The fields are unsigned values in decimal. So:
|
||||
*
|
||||
* u8: max 3 bytes in decimal
|
||||
* u32: max 10 bytes in decimal
|
||||
*
|
||||
* "S-" + 3 bytes for version field + 15 for authority field + NULL terminator
|
||||
*
|
||||
* For authority field, max is when all 6 values are non-zero and it must be
|
||||
* represented in hex. So "-0x" + 12 hex digits.
|
||||
*
|
||||
* Add 11 bytes for each subauthority field (10 bytes each + 1 for '-')
|
||||
*/
|
||||
#define SID_STRING_BASE_SIZE (2 + 3 + 15 + 1)
|
||||
#define SID_STRING_SUBAUTH_SIZE (11) /* size of a single subauth string */
|
||||
|
||||
#define DOMAIN_USER_RID_LE cpu_to_le32(513)
|
||||
|
||||
struct ksmbd_conn;
|
||||
|
||||
struct smb_ntsd {
|
||||
__le16 revision; /* revision level */
|
||||
__le16 type;
|
||||
__le32 osidoffset;
|
||||
__le32 gsidoffset;
|
||||
__le32 sacloffset;
|
||||
__le32 dacloffset;
|
||||
} __packed;
|
||||
|
||||
struct smb_sid {
|
||||
__u8 revision; /* revision level */
|
||||
__u8 num_subauth;
|
||||
__u8 authority[NUM_AUTHS];
|
||||
__le32 sub_auth[SID_MAX_SUB_AUTHORITIES]; /* sub_auth[num_subauth] */
|
||||
} __packed;
|
||||
|
||||
/* size of a struct cifs_sid, sans sub_auth array */
|
||||
#define CIFS_SID_BASE_SIZE (1 + 1 + NUM_AUTHS)
|
||||
|
||||
struct smb_acl {
|
||||
__le16 revision; /* revision level */
|
||||
__le16 size;
|
||||
__le32 num_aces;
|
||||
} __packed;
|
||||
|
||||
struct smb_ace {
|
||||
__u8 type;
|
||||
__u8 flags;
|
||||
__le16 size;
|
||||
__le32 access_req;
|
||||
struct smb_sid sid; /* ie UUID of user or group who gets these perms */
|
||||
} __packed;
|
||||
|
||||
struct smb_fattr {
|
||||
kuid_t cf_uid;
|
||||
kgid_t cf_gid;
|
||||
umode_t cf_mode;
|
||||
__le32 daccess;
|
||||
struct posix_acl *cf_acls;
|
||||
struct posix_acl *cf_dacls;
|
||||
};
|
||||
|
||||
struct posix_ace_state {
|
||||
u32 allow;
|
||||
u32 deny;
|
||||
};
|
||||
|
||||
struct posix_user_ace_state {
|
||||
union {
|
||||
kuid_t uid;
|
||||
kgid_t gid;
|
||||
};
|
||||
struct posix_ace_state perms;
|
||||
};
|
||||
|
||||
struct posix_ace_state_array {
|
||||
int n;
|
||||
struct posix_user_ace_state aces[];
|
||||
};
|
||||
|
||||
/*
|
||||
* while processing the nfsv4 ace, this maintains the partial permissions
|
||||
* calculated so far:
|
||||
*/
|
||||
|
||||
struct posix_acl_state {
|
||||
struct posix_ace_state owner;
|
||||
struct posix_ace_state group;
|
||||
struct posix_ace_state other;
|
||||
struct posix_ace_state everyone;
|
||||
struct posix_ace_state mask; /* deny unused in this case */
|
||||
struct posix_ace_state_array *users;
|
||||
struct posix_ace_state_array *groups;
|
||||
};
|
||||
|
||||
int parse_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
|
||||
int acl_len, struct smb_fattr *fattr);
|
||||
int build_sec_desc(struct user_namespace *user_ns, struct smb_ntsd *pntsd,
|
||||
struct smb_ntsd *ppntsd, int addition_info,
|
||||
__u32 *secdesclen, struct smb_fattr *fattr);
|
||||
int init_acl_state(struct posix_acl_state *state, int cnt);
|
||||
void free_acl_state(struct posix_acl_state *state);
|
||||
void posix_state_to_acl(struct posix_acl_state *state,
|
||||
struct posix_acl_entry *pace);
|
||||
int compare_sids(const struct smb_sid *ctsid, const struct smb_sid *cwsid);
|
||||
bool smb_inherit_flags(int flags, bool is_dir);
|
||||
int smb_inherit_dacl(struct ksmbd_conn *conn, struct path *path,
|
||||
unsigned int uid, unsigned int gid);
|
||||
int smb_check_perm_dacl(struct ksmbd_conn *conn, struct path *path,
|
||||
__le32 *pdaccess, int uid);
|
||||
int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon,
|
||||
struct path *path, struct smb_ntsd *pntsd, int ntsd_len,
|
||||
bool type_check);
|
||||
void id_to_sid(unsigned int cid, uint sidtype, struct smb_sid *ssid);
|
||||
void ksmbd_init_domain(u32 *sub_auth);
|
||||
#endif /* _SMBACL_H */
|
|
@ -0,0 +1,91 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||
/*
|
||||
* fs/cifs/smbfsctl.h: SMB, CIFS, SMB2 FSCTL definitions
|
||||
*
|
||||
* Copyright (c) International Business Machines Corp., 2002,2009
|
||||
* Author(s): Steve French (sfrench@us.ibm.com)
|
||||
*/
|
||||
|
||||
/* IOCTL information */
|
||||
/*
|
||||
* List of ioctl/fsctl function codes that are or could be useful in the
|
||||
* future to remote clients like cifs or SMB2 client. There is probably
|
||||
* a slightly larger set of fsctls that NTFS local filesystem could handle,
|
||||
* including the seven below that we do not have struct definitions for.
|
||||
* Even with protocol definitions for most of these now available, we still
|
||||
* need to do some experimentation to identify which are practical to do
|
||||
* remotely. Some of the following, such as the encryption/compression ones
|
||||
* could be invoked from tools via a specialized hook into the VFS rather
|
||||
* than via the standard vfs entry points
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_SMBFSCTL_H
|
||||
#define __KSMBD_SMBFSCTL_H
|
||||
|
||||
#define FSCTL_DFS_GET_REFERRALS 0x00060194
|
||||
#define FSCTL_DFS_GET_REFERRALS_EX 0x000601B0
|
||||
#define FSCTL_REQUEST_OPLOCK_LEVEL_1 0x00090000
|
||||
#define FSCTL_REQUEST_OPLOCK_LEVEL_2 0x00090004
|
||||
#define FSCTL_REQUEST_BATCH_OPLOCK 0x00090008
|
||||
#define FSCTL_LOCK_VOLUME 0x00090018
|
||||
#define FSCTL_UNLOCK_VOLUME 0x0009001C
|
||||
#define FSCTL_IS_PATHNAME_VALID 0x0009002C /* BB add struct */
|
||||
#define FSCTL_GET_COMPRESSION 0x0009003C /* BB add struct */
|
||||
#define FSCTL_SET_COMPRESSION 0x0009C040 /* BB add struct */
|
||||
#define FSCTL_QUERY_FAT_BPB 0x00090058 /* BB add struct */
|
||||
/* Verify the next FSCTL number, we had it as 0x00090090 before */
|
||||
#define FSCTL_FILESYSTEM_GET_STATS 0x00090060 /* BB add struct */
|
||||
#define FSCTL_GET_NTFS_VOLUME_DATA 0x00090064 /* BB add struct */
|
||||
#define FSCTL_GET_RETRIEVAL_POINTERS 0x00090073 /* BB add struct */
|
||||
#define FSCTL_IS_VOLUME_DIRTY 0x00090078 /* BB add struct */
|
||||
#define FSCTL_ALLOW_EXTENDED_DASD_IO 0x00090083 /* BB add struct */
|
||||
#define FSCTL_REQUEST_FILTER_OPLOCK 0x0009008C
|
||||
#define FSCTL_FIND_FILES_BY_SID 0x0009008F /* BB add struct */
|
||||
#define FSCTL_SET_OBJECT_ID 0x00090098 /* BB add struct */
|
||||
#define FSCTL_GET_OBJECT_ID 0x0009009C /* BB add struct */
|
||||
#define FSCTL_DELETE_OBJECT_ID 0x000900A0 /* BB add struct */
|
||||
#define FSCTL_SET_REPARSE_POINT 0x000900A4 /* BB add struct */
|
||||
#define FSCTL_GET_REPARSE_POINT 0x000900A8 /* BB add struct */
|
||||
#define FSCTL_DELETE_REPARSE_POINT 0x000900AC /* BB add struct */
|
||||
#define FSCTL_SET_OBJECT_ID_EXTENDED 0x000900BC /* BB add struct */
|
||||
#define FSCTL_CREATE_OR_GET_OBJECT_ID 0x000900C0 /* BB add struct */
|
||||
#define FSCTL_SET_SPARSE 0x000900C4 /* BB add struct */
|
||||
#define FSCTL_SET_ZERO_DATA 0x000980C8 /* BB add struct */
|
||||
#define FSCTL_SET_ENCRYPTION 0x000900D7 /* BB add struct */
|
||||
#define FSCTL_ENCRYPTION_FSCTL_IO 0x000900DB /* BB add struct */
|
||||
#define FSCTL_WRITE_RAW_ENCRYPTED 0x000900DF /* BB add struct */
|
||||
#define FSCTL_READ_RAW_ENCRYPTED 0x000900E3 /* BB add struct */
|
||||
#define FSCTL_READ_FILE_USN_DATA 0x000900EB /* BB add struct */
|
||||
#define FSCTL_WRITE_USN_CLOSE_RECORD 0x000900EF /* BB add struct */
|
||||
#define FSCTL_SIS_COPYFILE 0x00090100 /* BB add struct */
|
||||
#define FSCTL_RECALL_FILE 0x00090117 /* BB add struct */
|
||||
#define FSCTL_QUERY_SPARING_INFO 0x00090138 /* BB add struct */
|
||||
#define FSCTL_SET_ZERO_ON_DEALLOC 0x00090194 /* BB add struct */
|
||||
#define FSCTL_SET_SHORT_NAME_BEHAVIOR 0x000901B4 /* BB add struct */
|
||||
#define FSCTL_QUERY_ALLOCATED_RANGES 0x000940CF /* BB add struct */
|
||||
#define FSCTL_SET_DEFECT_MANAGEMENT 0x00098134 /* BB add struct */
|
||||
#define FSCTL_DUPLICATE_EXTENTS_TO_FILE 0x00098344
|
||||
#define FSCTL_SIS_LINK_FILES 0x0009C104
|
||||
#define FSCTL_PIPE_PEEK 0x0011400C /* BB add struct */
|
||||
#define FSCTL_PIPE_TRANSCEIVE 0x0011C017 /* BB add struct */
|
||||
/* strange that the number for this op is not sequential with previous op */
|
||||
#define FSCTL_PIPE_WAIT 0x00110018 /* BB add struct */
|
||||
#define FSCTL_REQUEST_RESUME_KEY 0x00140078
|
||||
#define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */
|
||||
#define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */
|
||||
#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204
|
||||
#define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC
|
||||
#define FSCTL_COPYCHUNK 0x001440F2
|
||||
#define FSCTL_COPYCHUNK_WRITE 0x001480F2
|
||||
|
||||
#define IO_REPARSE_TAG_MOUNT_POINT 0xA0000003
|
||||
#define IO_REPARSE_TAG_HSM 0xC0000004
|
||||
#define IO_REPARSE_TAG_SIS 0x80000007
|
||||
|
||||
/* WSL reparse tags */
|
||||
#define IO_REPARSE_TAG_LX_SYMLINK_LE cpu_to_le32(0xA000001D)
|
||||
#define IO_REPARSE_TAG_AF_UNIX_LE cpu_to_le32(0x80000023)
|
||||
#define IO_REPARSE_TAG_LX_FIFO_LE cpu_to_le32(0x80000024)
|
||||
#define IO_REPARSE_TAG_LX_CHR_LE cpu_to_le32(0x80000025)
|
||||
#define IO_REPARSE_TAG_LX_BLK_LE cpu_to_le32(0x80000026)
|
||||
#endif /* __KSMBD_SMBFSCTL_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,874 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "vfs_cache.h"
|
||||
#include "transport_ipc.h"
|
||||
#include "server.h"
|
||||
#include "smb_common.h"
|
||||
|
||||
#include "mgmt/user_config.h"
|
||||
#include "mgmt/share_config.h"
|
||||
#include "mgmt/user_session.h"
|
||||
#include "mgmt/tree_connect.h"
|
||||
#include "mgmt/ksmbd_ida.h"
|
||||
#include "connection.h"
|
||||
#include "transport_tcp.h"
|
||||
|
||||
#define IPC_WAIT_TIMEOUT (2 * HZ)
|
||||
|
||||
#define IPC_MSG_HASH_BITS 3
|
||||
static DEFINE_HASHTABLE(ipc_msg_table, IPC_MSG_HASH_BITS);
|
||||
static DECLARE_RWSEM(ipc_msg_table_lock);
|
||||
static DEFINE_MUTEX(startup_lock);
|
||||
|
||||
static DEFINE_IDA(ipc_ida);
|
||||
|
||||
static unsigned int ksmbd_tools_pid;
|
||||
|
||||
static bool ksmbd_ipc_validate_version(struct genl_info *m)
|
||||
{
|
||||
if (m->genlhdr->version != KSMBD_GENL_VERSION) {
|
||||
pr_err("%s. ksmbd: %d, kernel module: %d. %s.\n",
|
||||
"Daemon and kernel module version mismatch",
|
||||
m->genlhdr->version,
|
||||
KSMBD_GENL_VERSION,
|
||||
"User-space ksmbd should terminate");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ksmbd_ipc_msg {
|
||||
unsigned int type;
|
||||
unsigned int sz;
|
||||
unsigned char payload[];
|
||||
};
|
||||
|
||||
struct ipc_msg_table_entry {
|
||||
unsigned int handle;
|
||||
unsigned int type;
|
||||
wait_queue_head_t wait;
|
||||
struct hlist_node ipc_table_hlist;
|
||||
|
||||
void *response;
|
||||
};
|
||||
|
||||
static struct delayed_work ipc_timer_work;
|
||||
|
||||
static int handle_startup_event(struct sk_buff *skb, struct genl_info *info);
|
||||
static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info);
|
||||
static int handle_generic_event(struct sk_buff *skb, struct genl_info *info);
|
||||
static int ksmbd_ipc_heartbeat_request(void);
|
||||
|
||||
static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = {
|
||||
[KSMBD_EVENT_UNSPEC] = {
|
||||
.len = 0,
|
||||
},
|
||||
[KSMBD_EVENT_HEARTBEAT_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_heartbeat),
|
||||
},
|
||||
[KSMBD_EVENT_STARTING_UP] = {
|
||||
.len = sizeof(struct ksmbd_startup_request),
|
||||
},
|
||||
[KSMBD_EVENT_SHUTTING_DOWN] = {
|
||||
.len = sizeof(struct ksmbd_shutdown_request),
|
||||
},
|
||||
[KSMBD_EVENT_LOGIN_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_login_request),
|
||||
},
|
||||
[KSMBD_EVENT_LOGIN_RESPONSE] = {
|
||||
.len = sizeof(struct ksmbd_login_response),
|
||||
},
|
||||
[KSMBD_EVENT_SHARE_CONFIG_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_share_config_request),
|
||||
},
|
||||
[KSMBD_EVENT_SHARE_CONFIG_RESPONSE] = {
|
||||
.len = sizeof(struct ksmbd_share_config_response),
|
||||
},
|
||||
[KSMBD_EVENT_TREE_CONNECT_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_tree_connect_request),
|
||||
},
|
||||
[KSMBD_EVENT_TREE_CONNECT_RESPONSE] = {
|
||||
.len = sizeof(struct ksmbd_tree_connect_response),
|
||||
},
|
||||
[KSMBD_EVENT_TREE_DISCONNECT_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_tree_disconnect_request),
|
||||
},
|
||||
[KSMBD_EVENT_LOGOUT_REQUEST] = {
|
||||
.len = sizeof(struct ksmbd_logout_request),
|
||||
},
|
||||
[KSMBD_EVENT_RPC_REQUEST] = {
|
||||
},
|
||||
[KSMBD_EVENT_RPC_RESPONSE] = {
|
||||
},
|
||||
[KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST] = {
|
||||
},
|
||||
[KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE] = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_ops ksmbd_genl_ops[] = {
|
||||
{
|
||||
.cmd = KSMBD_EVENT_UNSPEC,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_HEARTBEAT_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_STARTING_UP,
|
||||
.doit = handle_startup_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_SHUTTING_DOWN,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_LOGIN_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_LOGIN_RESPONSE,
|
||||
.doit = handle_generic_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_SHARE_CONFIG_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_SHARE_CONFIG_RESPONSE,
|
||||
.doit = handle_generic_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_TREE_CONNECT_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_TREE_CONNECT_RESPONSE,
|
||||
.doit = handle_generic_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_TREE_DISCONNECT_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_LOGOUT_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_RPC_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_RPC_RESPONSE,
|
||||
.doit = handle_generic_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST,
|
||||
.doit = handle_unsupported_event,
|
||||
},
|
||||
{
|
||||
.cmd = KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE,
|
||||
.doit = handle_generic_event,
|
||||
},
|
||||
};
|
||||
|
||||
static struct genl_family ksmbd_genl_family = {
|
||||
.name = KSMBD_GENL_NAME,
|
||||
.version = KSMBD_GENL_VERSION,
|
||||
.hdrsize = 0,
|
||||
.maxattr = KSMBD_EVENT_MAX,
|
||||
.netnsok = true,
|
||||
.module = THIS_MODULE,
|
||||
.ops = ksmbd_genl_ops,
|
||||
.n_ops = ARRAY_SIZE(ksmbd_genl_ops),
|
||||
};
|
||||
|
||||
static void ksmbd_nl_init_fixup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ksmbd_genl_ops); i++)
|
||||
ksmbd_genl_ops[i].validate = GENL_DONT_VALIDATE_STRICT |
|
||||
GENL_DONT_VALIDATE_DUMP;
|
||||
|
||||
ksmbd_genl_family.policy = ksmbd_nl_policy;
|
||||
}
|
||||
|
||||
static int rpc_context_flags(struct ksmbd_session *sess)
|
||||
{
|
||||
if (user_guest(sess->user))
|
||||
return KSMBD_RPC_RESTRICTED_CONTEXT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ipc_update_last_active(void)
|
||||
{
|
||||
if (server_conf.ipc_timeout)
|
||||
server_conf.ipc_last_active = jiffies;
|
||||
}
|
||||
|
||||
static struct ksmbd_ipc_msg *ipc_msg_alloc(size_t sz)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
size_t msg_sz = sz + sizeof(struct ksmbd_ipc_msg);
|
||||
|
||||
msg = kvmalloc(msg_sz, GFP_KERNEL | __GFP_ZERO);
|
||||
if (msg)
|
||||
msg->sz = sz;
|
||||
return msg;
|
||||
}
|
||||
|
||||
static void ipc_msg_free(struct ksmbd_ipc_msg *msg)
|
||||
{
|
||||
kvfree(msg);
|
||||
}
|
||||
|
||||
static void ipc_msg_handle_free(int handle)
|
||||
{
|
||||
if (handle >= 0)
|
||||
ksmbd_release_id(&ipc_ida, handle);
|
||||
}
|
||||
|
||||
static int handle_response(int type, void *payload, size_t sz)
|
||||
{
|
||||
unsigned int handle = *(unsigned int *)payload;
|
||||
struct ipc_msg_table_entry *entry;
|
||||
int ret = 0;
|
||||
|
||||
ipc_update_last_active();
|
||||
down_read(&ipc_msg_table_lock);
|
||||
hash_for_each_possible(ipc_msg_table, entry, ipc_table_hlist, handle) {
|
||||
if (handle != entry->handle)
|
||||
continue;
|
||||
|
||||
entry->response = NULL;
|
||||
/*
|
||||
* Response message type value should be equal to
|
||||
* request message type + 1.
|
||||
*/
|
||||
if (entry->type + 1 != type) {
|
||||
pr_err("Waiting for IPC type %d, got %d. Ignore.\n",
|
||||
entry->type + 1, type);
|
||||
}
|
||||
|
||||
entry->response = kvmalloc(sz, GFP_KERNEL | __GFP_ZERO);
|
||||
if (!entry->response) {
|
||||
ret = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(entry->response, payload, sz);
|
||||
wake_up_interruptible(&entry->wait);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
up_read(&ipc_msg_table_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ipc_server_config_on_startup(struct ksmbd_startup_request *req)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ksmbd_set_fd_limit(req->file_max);
|
||||
server_conf.flags = req->flags;
|
||||
server_conf.signing = req->signing;
|
||||
server_conf.tcp_port = req->tcp_port;
|
||||
server_conf.ipc_timeout = req->ipc_timeout * HZ;
|
||||
server_conf.deadtime = req->deadtime * SMB_ECHO_INTERVAL;
|
||||
server_conf.share_fake_fscaps = req->share_fake_fscaps;
|
||||
ksmbd_init_domain(req->sub_auth);
|
||||
|
||||
if (req->smb2_max_read)
|
||||
init_smb2_max_read_size(req->smb2_max_read);
|
||||
if (req->smb2_max_write)
|
||||
init_smb2_max_write_size(req->smb2_max_write);
|
||||
if (req->smb2_max_trans)
|
||||
init_smb2_max_trans_size(req->smb2_max_trans);
|
||||
|
||||
ret = ksmbd_set_netbios_name(req->netbios_name);
|
||||
ret |= ksmbd_set_server_string(req->server_string);
|
||||
ret |= ksmbd_set_work_group(req->work_group);
|
||||
ret |= ksmbd_tcp_set_interfaces(KSMBD_STARTUP_CONFIG_INTERFACES(req),
|
||||
req->ifc_list_sz);
|
||||
if (ret) {
|
||||
pr_err("Server configuration error: %s %s %s\n",
|
||||
req->netbios_name, req->server_string,
|
||||
req->work_group);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (req->min_prot[0]) {
|
||||
ret = ksmbd_lookup_protocol_idx(req->min_prot);
|
||||
if (ret >= 0)
|
||||
server_conf.min_protocol = ret;
|
||||
}
|
||||
if (req->max_prot[0]) {
|
||||
ret = ksmbd_lookup_protocol_idx(req->max_prot);
|
||||
if (ret >= 0)
|
||||
server_conf.max_protocol = ret;
|
||||
}
|
||||
|
||||
if (server_conf.ipc_timeout)
|
||||
schedule_delayed_work(&ipc_timer_work, server_conf.ipc_timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_startup_event(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
|
||||
if (!netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
#endif
|
||||
|
||||
if (!ksmbd_ipc_validate_version(info))
|
||||
return -EINVAL;
|
||||
|
||||
if (!info->attrs[KSMBD_EVENT_STARTING_UP])
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&startup_lock);
|
||||
if (!ksmbd_server_configurable()) {
|
||||
mutex_unlock(&startup_lock);
|
||||
pr_err("Server reset is in progress, can't start daemon\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ksmbd_tools_pid) {
|
||||
if (ksmbd_ipc_heartbeat_request() == 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_err("Reconnect to a new user space daemon\n");
|
||||
} else {
|
||||
struct ksmbd_startup_request *req;
|
||||
|
||||
req = nla_data(info->attrs[info->genlhdr->cmd]);
|
||||
ret = ipc_server_config_on_startup(req);
|
||||
if (ret)
|
||||
goto out;
|
||||
server_queue_ctrl_init_work();
|
||||
}
|
||||
|
||||
ksmbd_tools_pid = info->snd_portid;
|
||||
ipc_update_last_active();
|
||||
|
||||
out:
|
||||
mutex_unlock(&startup_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
pr_err("Unknown IPC event: %d, ignore.\n", info->genlhdr->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int handle_generic_event(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
void *payload;
|
||||
int sz;
|
||||
int type = info->genlhdr->cmd;
|
||||
|
||||
#ifdef CONFIG_SMB_SERVER_CHECK_CAP_NET_ADMIN
|
||||
if (!netlink_capable(skb, CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
#endif
|
||||
|
||||
if (type >= KSMBD_EVENT_MAX) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ksmbd_ipc_validate_version(info))
|
||||
return -EINVAL;
|
||||
|
||||
if (!info->attrs[type])
|
||||
return -EINVAL;
|
||||
|
||||
payload = nla_data(info->attrs[info->genlhdr->cmd]);
|
||||
sz = nla_len(info->attrs[info->genlhdr->cmd]);
|
||||
return handle_response(type, payload, sz);
|
||||
}
|
||||
|
||||
static int ipc_msg_send(struct ksmbd_ipc_msg *msg)
|
||||
{
|
||||
struct genlmsghdr *nlh;
|
||||
struct sk_buff *skb;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!ksmbd_tools_pid)
|
||||
return ret;
|
||||
|
||||
skb = genlmsg_new(msg->sz, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
nlh = genlmsg_put(skb, 0, 0, &ksmbd_genl_family, 0, msg->type);
|
||||
if (!nlh)
|
||||
goto out;
|
||||
|
||||
ret = nla_put(skb, msg->type, msg->sz, msg->payload);
|
||||
if (ret) {
|
||||
genlmsg_cancel(skb, nlh);
|
||||
goto out;
|
||||
}
|
||||
|
||||
genlmsg_end(skb, nlh);
|
||||
ret = genlmsg_unicast(&init_net, skb, ksmbd_tools_pid);
|
||||
if (!ret)
|
||||
ipc_update_last_active();
|
||||
return ret;
|
||||
|
||||
out:
|
||||
nlmsg_free(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void *ipc_msg_send_request(struct ksmbd_ipc_msg *msg, unsigned int handle)
|
||||
{
|
||||
struct ipc_msg_table_entry entry;
|
||||
int ret;
|
||||
|
||||
if ((int)handle < 0)
|
||||
return NULL;
|
||||
|
||||
entry.type = msg->type;
|
||||
entry.response = NULL;
|
||||
init_waitqueue_head(&entry.wait);
|
||||
|
||||
down_write(&ipc_msg_table_lock);
|
||||
entry.handle = handle;
|
||||
hash_add(ipc_msg_table, &entry.ipc_table_hlist, entry.handle);
|
||||
up_write(&ipc_msg_table_lock);
|
||||
|
||||
ret = ipc_msg_send(msg);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = wait_event_interruptible_timeout(entry.wait,
|
||||
entry.response != NULL,
|
||||
IPC_WAIT_TIMEOUT);
|
||||
out:
|
||||
down_write(&ipc_msg_table_lock);
|
||||
hash_del(&entry.ipc_table_hlist);
|
||||
up_write(&ipc_msg_table_lock);
|
||||
return entry.response;
|
||||
}
|
||||
|
||||
static int ksmbd_ipc_heartbeat_request(void)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
int ret;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_heartbeat));
|
||||
if (!msg)
|
||||
return -EINVAL;
|
||||
|
||||
msg->type = KSMBD_EVENT_HEARTBEAT_REQUEST;
|
||||
ret = ipc_msg_send(msg);
|
||||
ipc_msg_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ksmbd_login_response *ksmbd_ipc_login_request(const char *account)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_login_request *req;
|
||||
struct ksmbd_login_response *resp;
|
||||
|
||||
if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
|
||||
return NULL;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_login_request));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_LOGIN_REQUEST;
|
||||
req = (struct ksmbd_login_request *)msg->payload;
|
||||
req->handle = ksmbd_acquire_id(&ipc_ida);
|
||||
strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_handle_free(req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_spnego_authen_response *
|
||||
ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_spnego_authen_request *req;
|
||||
struct ksmbd_spnego_authen_response *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_spnego_authen_request) +
|
||||
blob_len + 1);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST;
|
||||
req = (struct ksmbd_spnego_authen_request *)msg->payload;
|
||||
req->handle = ksmbd_acquire_id(&ipc_ida);
|
||||
req->spnego_blob_len = blob_len;
|
||||
memcpy(req->spnego_blob, spnego_blob, blob_len);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_handle_free(req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_tree_connect_response *
|
||||
ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
|
||||
struct ksmbd_share_config *share,
|
||||
struct ksmbd_tree_connect *tree_conn,
|
||||
struct sockaddr *peer_addr)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_tree_connect_request *req;
|
||||
struct ksmbd_tree_connect_response *resp;
|
||||
|
||||
if (strlen(user_name(sess->user)) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
|
||||
return NULL;
|
||||
|
||||
if (strlen(share->name) >= KSMBD_REQ_MAX_SHARE_NAME)
|
||||
return NULL;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_connect_request));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_TREE_CONNECT_REQUEST;
|
||||
req = (struct ksmbd_tree_connect_request *)msg->payload;
|
||||
|
||||
req->handle = ksmbd_acquire_id(&ipc_ida);
|
||||
req->account_flags = sess->user->flags;
|
||||
req->session_id = sess->id;
|
||||
req->connect_id = tree_conn->id;
|
||||
strscpy(req->account, user_name(sess->user), KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
|
||||
strscpy(req->share, share->name, KSMBD_REQ_MAX_SHARE_NAME);
|
||||
snprintf(req->peer_addr, sizeof(req->peer_addr), "%pIS", peer_addr);
|
||||
|
||||
if (peer_addr->sa_family == AF_INET6)
|
||||
req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_IPV6;
|
||||
if (test_session_flag(sess, CIFDS_SESSION_FLAG_SMB2))
|
||||
req->flags |= KSMBD_TREE_CONN_FLAG_REQUEST_SMB2;
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_handle_free(req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
|
||||
unsigned long long connect_id)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_tree_disconnect_request *req;
|
||||
int ret;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_tree_disconnect_request));
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->type = KSMBD_EVENT_TREE_DISCONNECT_REQUEST;
|
||||
req = (struct ksmbd_tree_disconnect_request *)msg->payload;
|
||||
req->session_id = session_id;
|
||||
req->connect_id = connect_id;
|
||||
|
||||
ret = ipc_msg_send(msg);
|
||||
ipc_msg_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ksmbd_ipc_logout_request(const char *account)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_logout_request *req;
|
||||
int ret;
|
||||
|
||||
if (strlen(account) >= KSMBD_REQ_MAX_ACCOUNT_NAME_SZ)
|
||||
return -EINVAL;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_logout_request));
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
msg->type = KSMBD_EVENT_LOGOUT_REQUEST;
|
||||
req = (struct ksmbd_logout_request *)msg->payload;
|
||||
strscpy(req->account, account, KSMBD_REQ_MAX_ACCOUNT_NAME_SZ);
|
||||
|
||||
ret = ipc_msg_send(msg);
|
||||
ipc_msg_free(msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct ksmbd_share_config_response *
|
||||
ksmbd_ipc_share_config_request(const char *name)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_share_config_request *req;
|
||||
struct ksmbd_share_config_response *resp;
|
||||
|
||||
if (strlen(name) >= KSMBD_REQ_MAX_SHARE_NAME)
|
||||
return NULL;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_share_config_request));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_SHARE_CONFIG_REQUEST;
|
||||
req = (struct ksmbd_share_config_request *)msg->payload;
|
||||
req->handle = ksmbd_acquire_id(&ipc_ida);
|
||||
strscpy(req->share_name, name, KSMBD_REQ_MAX_SHARE_NAME);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_handle_free(req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = handle;
|
||||
req->flags = ksmbd_session_rpc_method(sess, handle);
|
||||
req->flags |= KSMBD_RPC_OPEN_METHOD;
|
||||
req->payload_sz = 0;
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = handle;
|
||||
req->flags = ksmbd_session_rpc_method(sess, handle);
|
||||
req->flags |= KSMBD_RPC_CLOSE_METHOD;
|
||||
req->payload_sz = 0;
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle,
|
||||
void *payload, size_t payload_sz)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = handle;
|
||||
req->flags = ksmbd_session_rpc_method(sess, handle);
|
||||
req->flags |= rpc_context_flags(sess);
|
||||
req->flags |= KSMBD_RPC_WRITE_METHOD;
|
||||
req->payload_sz = payload_sz;
|
||||
memcpy(req->payload, payload, payload_sz);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command));
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = handle;
|
||||
req->flags = ksmbd_session_rpc_method(sess, handle);
|
||||
req->flags |= rpc_context_flags(sess);
|
||||
req->flags |= KSMBD_RPC_READ_METHOD;
|
||||
req->payload_sz = 0;
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle,
|
||||
void *payload, size_t payload_sz)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = handle;
|
||||
req->flags = ksmbd_session_rpc_method(sess, handle);
|
||||
req->flags |= rpc_context_flags(sess);
|
||||
req->flags |= KSMBD_RPC_IOCTL_METHOD;
|
||||
req->payload_sz = payload_sz;
|
||||
memcpy(req->payload, payload, payload_sz);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
|
||||
size_t payload_sz)
|
||||
{
|
||||
struct ksmbd_ipc_msg *msg;
|
||||
struct ksmbd_rpc_command *req;
|
||||
struct ksmbd_rpc_command *resp;
|
||||
|
||||
msg = ipc_msg_alloc(sizeof(struct ksmbd_rpc_command) + payload_sz + 1);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
msg->type = KSMBD_EVENT_RPC_REQUEST;
|
||||
req = (struct ksmbd_rpc_command *)msg->payload;
|
||||
req->handle = ksmbd_acquire_id(&ipc_ida);
|
||||
req->flags = rpc_context_flags(sess);
|
||||
req->flags |= KSMBD_RPC_RAP_METHOD;
|
||||
req->payload_sz = payload_sz;
|
||||
memcpy(req->payload, payload, payload_sz);
|
||||
|
||||
resp = ipc_msg_send_request(msg, req->handle);
|
||||
ipc_msg_handle_free(req->handle);
|
||||
ipc_msg_free(msg);
|
||||
return resp;
|
||||
}
|
||||
|
||||
static int __ipc_heartbeat(void)
|
||||
{
|
||||
unsigned long delta;
|
||||
|
||||
if (!ksmbd_server_running())
|
||||
return 0;
|
||||
|
||||
if (time_after(jiffies, server_conf.ipc_last_active)) {
|
||||
delta = (jiffies - server_conf.ipc_last_active);
|
||||
} else {
|
||||
ipc_update_last_active();
|
||||
schedule_delayed_work(&ipc_timer_work,
|
||||
server_conf.ipc_timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (delta < server_conf.ipc_timeout) {
|
||||
schedule_delayed_work(&ipc_timer_work,
|
||||
server_conf.ipc_timeout - delta);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ksmbd_ipc_heartbeat_request() == 0) {
|
||||
schedule_delayed_work(&ipc_timer_work,
|
||||
server_conf.ipc_timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex_lock(&startup_lock);
|
||||
WRITE_ONCE(server_conf.state, SERVER_STATE_RESETTING);
|
||||
server_conf.ipc_last_active = 0;
|
||||
ksmbd_tools_pid = 0;
|
||||
pr_err("No IPC daemon response for %lus\n", delta / HZ);
|
||||
mutex_unlock(&startup_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void ipc_timer_heartbeat(struct work_struct *w)
|
||||
{
|
||||
if (__ipc_heartbeat())
|
||||
server_queue_ctrl_reset_work();
|
||||
}
|
||||
|
||||
int ksmbd_ipc_id_alloc(void)
|
||||
{
|
||||
return ksmbd_acquire_id(&ipc_ida);
|
||||
}
|
||||
|
||||
void ksmbd_rpc_id_free(int handle)
|
||||
{
|
||||
ksmbd_release_id(&ipc_ida, handle);
|
||||
}
|
||||
|
||||
void ksmbd_ipc_release(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&ipc_timer_work);
|
||||
genl_unregister_family(&ksmbd_genl_family);
|
||||
}
|
||||
|
||||
void ksmbd_ipc_soft_reset(void)
|
||||
{
|
||||
mutex_lock(&startup_lock);
|
||||
ksmbd_tools_pid = 0;
|
||||
cancel_delayed_work_sync(&ipc_timer_work);
|
||||
mutex_unlock(&startup_lock);
|
||||
}
|
||||
|
||||
int ksmbd_ipc_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ksmbd_nl_init_fixup();
|
||||
INIT_DELAYED_WORK(&ipc_timer_work, ipc_timer_heartbeat);
|
||||
|
||||
ret = genl_register_family(&ksmbd_genl_family);
|
||||
if (ret) {
|
||||
pr_err("Failed to register KSMBD netlink interface %d\n", ret);
|
||||
cancel_delayed_work_sync(&ipc_timer_work);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_TRANSPORT_IPC_H__
|
||||
#define __KSMBD_TRANSPORT_IPC_H__
|
||||
|
||||
#include <linux/wait.h>
|
||||
|
||||
#define KSMBD_IPC_MAX_PAYLOAD 4096
|
||||
|
||||
struct ksmbd_login_response *
|
||||
ksmbd_ipc_login_request(const char *account);
|
||||
|
||||
struct ksmbd_session;
|
||||
struct ksmbd_share_config;
|
||||
struct ksmbd_tree_connect;
|
||||
struct sockaddr;
|
||||
|
||||
struct ksmbd_tree_connect_response *
|
||||
ksmbd_ipc_tree_connect_request(struct ksmbd_session *sess,
|
||||
struct ksmbd_share_config *share,
|
||||
struct ksmbd_tree_connect *tree_conn,
|
||||
struct sockaddr *peer_addr);
|
||||
int ksmbd_ipc_tree_disconnect_request(unsigned long long session_id,
|
||||
unsigned long long connect_id);
|
||||
int ksmbd_ipc_logout_request(const char *account);
|
||||
struct ksmbd_share_config_response *
|
||||
ksmbd_ipc_share_config_request(const char *name);
|
||||
struct ksmbd_spnego_authen_response *
|
||||
ksmbd_ipc_spnego_authen_request(const char *spnego_blob, int blob_len);
|
||||
int ksmbd_ipc_id_alloc(void);
|
||||
void ksmbd_rpc_id_free(int handle);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_open(struct ksmbd_session *sess, int handle);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_close(struct ksmbd_session *sess, int handle);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_write(struct ksmbd_session *sess, int handle,
|
||||
void *payload, size_t payload_sz);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_read(struct ksmbd_session *sess, int handle);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_ioctl(struct ksmbd_session *sess, int handle,
|
||||
void *payload, size_t payload_sz);
|
||||
struct ksmbd_rpc_command *ksmbd_rpc_rap(struct ksmbd_session *sess, void *payload,
|
||||
size_t payload_sz);
|
||||
void ksmbd_ipc_release(void);
|
||||
void ksmbd_ipc_soft_reset(void);
|
||||
int ksmbd_ipc_init(void);
|
||||
#endif /* __KSMBD_TRANSPORT_IPC_H__ */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,63 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2017, Microsoft Corporation.
|
||||
* Copyright (C) 2018, LG Electronics.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_TRANSPORT_RDMA_H__
|
||||
#define __KSMBD_TRANSPORT_RDMA_H__
|
||||
|
||||
#define SMB_DIRECT_PORT 5445
|
||||
|
||||
/* SMB DIRECT negotiation request packet [MS-SMBD] 2.2.1 */
|
||||
struct smb_direct_negotiate_req {
|
||||
__le16 min_version;
|
||||
__le16 max_version;
|
||||
__le16 reserved;
|
||||
__le16 credits_requested;
|
||||
__le32 preferred_send_size;
|
||||
__le32 max_receive_size;
|
||||
__le32 max_fragmented_size;
|
||||
} __packed;
|
||||
|
||||
/* SMB DIRECT negotiation response packet [MS-SMBD] 2.2.2 */
|
||||
struct smb_direct_negotiate_resp {
|
||||
__le16 min_version;
|
||||
__le16 max_version;
|
||||
__le16 negotiated_version;
|
||||
__le16 reserved;
|
||||
__le16 credits_requested;
|
||||
__le16 credits_granted;
|
||||
__le32 status;
|
||||
__le32 max_readwrite_size;
|
||||
__le32 preferred_send_size;
|
||||
__le32 max_receive_size;
|
||||
__le32 max_fragmented_size;
|
||||
} __packed;
|
||||
|
||||
#define SMB_DIRECT_RESPONSE_REQUESTED 0x0001
|
||||
|
||||
/* SMB DIRECT data transfer packet with payload [MS-SMBD] 2.2.3 */
|
||||
struct smb_direct_data_transfer {
|
||||
__le16 credits_requested;
|
||||
__le16 credits_granted;
|
||||
__le16 flags;
|
||||
__le16 reserved;
|
||||
__le32 remaining_data_length;
|
||||
__le32 data_offset;
|
||||
__le32 data_length;
|
||||
__le32 padding;
|
||||
__u8 buffer[];
|
||||
} __packed;
|
||||
|
||||
#ifdef CONFIG_SMB_SERVER_SMBDIRECT
|
||||
int ksmbd_rdma_init(void);
|
||||
int ksmbd_rdma_destroy(void);
|
||||
bool ksmbd_rdma_capable_netdev(struct net_device *netdev);
|
||||
#else
|
||||
static inline int ksmbd_rdma_init(void) { return 0; }
|
||||
static inline int ksmbd_rdma_destroy(void) { return 0; }
|
||||
static inline bool ksmbd_rdma_capable_netdev(struct net_device *netdev) { return false; }
|
||||
#endif
|
||||
|
||||
#endif /* __KSMBD_TRANSPORT_RDMA_H__ */
|
|
@ -0,0 +1,618 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include "smb_common.h"
|
||||
#include "server.h"
|
||||
#include "auth.h"
|
||||
#include "connection.h"
|
||||
#include "transport_tcp.h"
|
||||
|
||||
#define IFACE_STATE_DOWN BIT(0)
|
||||
#define IFACE_STATE_CONFIGURED BIT(1)
|
||||
|
||||
struct interface {
|
||||
struct task_struct *ksmbd_kthread;
|
||||
struct socket *ksmbd_socket;
|
||||
struct list_head entry;
|
||||
char *name;
|
||||
struct mutex sock_release_lock;
|
||||
int state;
|
||||
};
|
||||
|
||||
static LIST_HEAD(iface_list);
|
||||
|
||||
static int bind_additional_ifaces;
|
||||
|
||||
struct tcp_transport {
|
||||
struct ksmbd_transport transport;
|
||||
struct socket *sock;
|
||||
struct kvec *iov;
|
||||
unsigned int nr_iov;
|
||||
};
|
||||
|
||||
static struct ksmbd_transport_ops ksmbd_tcp_transport_ops;
|
||||
|
||||
static void tcp_stop_kthread(struct task_struct *kthread);
|
||||
static struct interface *alloc_iface(char *ifname);
|
||||
|
||||
#define KSMBD_TRANS(t) (&(t)->transport)
|
||||
#define TCP_TRANS(t) ((struct tcp_transport *)container_of(t, \
|
||||
struct tcp_transport, transport))
|
||||
|
||||
static inline void ksmbd_tcp_nodelay(struct socket *sock)
|
||||
{
|
||||
tcp_sock_set_nodelay(sock->sk);
|
||||
}
|
||||
|
||||
static inline void ksmbd_tcp_reuseaddr(struct socket *sock)
|
||||
{
|
||||
sock_set_reuseaddr(sock->sk);
|
||||
}
|
||||
|
||||
static inline void ksmbd_tcp_rcv_timeout(struct socket *sock, s64 secs)
|
||||
{
|
||||
lock_sock(sock->sk);
|
||||
if (secs && secs < MAX_SCHEDULE_TIMEOUT / HZ - 1)
|
||||
sock->sk->sk_rcvtimeo = secs * HZ;
|
||||
else
|
||||
sock->sk->sk_rcvtimeo = MAX_SCHEDULE_TIMEOUT;
|
||||
release_sock(sock->sk);
|
||||
}
|
||||
|
||||
static inline void ksmbd_tcp_snd_timeout(struct socket *sock, s64 secs)
|
||||
{
|
||||
sock_set_sndtimeo(sock->sk, secs);
|
||||
}
|
||||
|
||||
static struct tcp_transport *alloc_transport(struct socket *client_sk)
|
||||
{
|
||||
struct tcp_transport *t;
|
||||
struct ksmbd_conn *conn;
|
||||
|
||||
t = kzalloc(sizeof(*t), GFP_KERNEL);
|
||||
if (!t)
|
||||
return NULL;
|
||||
t->sock = client_sk;
|
||||
|
||||
conn = ksmbd_conn_alloc();
|
||||
if (!conn) {
|
||||
kfree(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conn->transport = KSMBD_TRANS(t);
|
||||
KSMBD_TRANS(t)->conn = conn;
|
||||
KSMBD_TRANS(t)->ops = &ksmbd_tcp_transport_ops;
|
||||
return t;
|
||||
}
|
||||
|
||||
static void free_transport(struct tcp_transport *t)
|
||||
{
|
||||
kernel_sock_shutdown(t->sock, SHUT_RDWR);
|
||||
sock_release(t->sock);
|
||||
t->sock = NULL;
|
||||
|
||||
ksmbd_conn_free(KSMBD_TRANS(t)->conn);
|
||||
kfree(t->iov);
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvec_array_init() - initialize a IO vector segment
|
||||
* @new: IO vector to be initialized
|
||||
* @iov: base IO vector
|
||||
* @nr_segs: number of segments in base iov
|
||||
* @bytes: total iovec length so far for read
|
||||
*
|
||||
* Return: Number of IO segments
|
||||
*/
|
||||
static unsigned int kvec_array_init(struct kvec *new, struct kvec *iov,
|
||||
unsigned int nr_segs, size_t bytes)
|
||||
{
|
||||
size_t base = 0;
|
||||
|
||||
while (bytes || !iov->iov_len) {
|
||||
int copy = min(bytes, iov->iov_len);
|
||||
|
||||
bytes -= copy;
|
||||
base += copy;
|
||||
if (iov->iov_len == base) {
|
||||
iov++;
|
||||
nr_segs--;
|
||||
base = 0;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(new, iov, sizeof(*iov) * nr_segs);
|
||||
new->iov_base += base;
|
||||
new->iov_len -= base;
|
||||
return nr_segs;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_conn_iovec() - get connection iovec for reading from socket
|
||||
* @t: TCP transport instance
|
||||
* @nr_segs: number of segments in iov
|
||||
*
|
||||
* Return: return existing or newly allocate iovec
|
||||
*/
|
||||
static struct kvec *get_conn_iovec(struct tcp_transport *t, unsigned int nr_segs)
|
||||
{
|
||||
struct kvec *new_iov;
|
||||
|
||||
if (t->iov && nr_segs <= t->nr_iov)
|
||||
return t->iov;
|
||||
|
||||
/* not big enough -- allocate a new one and release the old */
|
||||
new_iov = kmalloc_array(nr_segs, sizeof(*new_iov), GFP_KERNEL);
|
||||
if (new_iov) {
|
||||
kfree(t->iov);
|
||||
t->iov = new_iov;
|
||||
t->nr_iov = nr_segs;
|
||||
}
|
||||
return new_iov;
|
||||
}
|
||||
|
||||
static unsigned short ksmbd_tcp_get_port(const struct sockaddr *sa)
|
||||
{
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
return ntohs(((struct sockaddr_in *)sa)->sin_port);
|
||||
case AF_INET6:
|
||||
return ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_tcp_new_connection() - create a new tcp session on mount
|
||||
* @client_sk: socket associated with new connection
|
||||
*
|
||||
* whenever a new connection is requested, create a conn thread
|
||||
* (session thread) to handle new incoming smb requests from the connection
|
||||
*
|
||||
* Return: 0 on success, otherwise error
|
||||
*/
|
||||
static int ksmbd_tcp_new_connection(struct socket *client_sk)
|
||||
{
|
||||
struct sockaddr *csin;
|
||||
int rc = 0;
|
||||
struct tcp_transport *t;
|
||||
|
||||
t = alloc_transport(client_sk);
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
csin = KSMBD_TCP_PEER_SOCKADDR(KSMBD_TRANS(t)->conn);
|
||||
if (kernel_getpeername(client_sk, csin) < 0) {
|
||||
pr_err("client ip resolution failed\n");
|
||||
rc = -EINVAL;
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
KSMBD_TRANS(t)->handler = kthread_run(ksmbd_conn_handler_loop,
|
||||
KSMBD_TRANS(t)->conn,
|
||||
"ksmbd:%u",
|
||||
ksmbd_tcp_get_port(csin));
|
||||
if (IS_ERR(KSMBD_TRANS(t)->handler)) {
|
||||
pr_err("cannot start conn thread\n");
|
||||
rc = PTR_ERR(KSMBD_TRANS(t)->handler);
|
||||
free_transport(t);
|
||||
}
|
||||
return rc;
|
||||
|
||||
out_error:
|
||||
free_transport(t);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_kthread_fn() - listen to new SMB connections and callback server
|
||||
* @p: arguments to forker thread
|
||||
*
|
||||
* Return: Returns a task_struct or ERR_PTR
|
||||
*/
|
||||
static int ksmbd_kthread_fn(void *p)
|
||||
{
|
||||
struct socket *client_sk = NULL;
|
||||
struct interface *iface = (struct interface *)p;
|
||||
int ret;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
mutex_lock(&iface->sock_release_lock);
|
||||
if (!iface->ksmbd_socket) {
|
||||
mutex_unlock(&iface->sock_release_lock);
|
||||
break;
|
||||
}
|
||||
ret = kernel_accept(iface->ksmbd_socket, &client_sk,
|
||||
O_NONBLOCK);
|
||||
mutex_unlock(&iface->sock_release_lock);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
/* check for new connections every 100 msecs */
|
||||
schedule_timeout_interruptible(HZ / 10);
|
||||
continue;
|
||||
}
|
||||
|
||||
ksmbd_debug(CONN, "connect success: accepted new connection\n");
|
||||
client_sk->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
client_sk->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
|
||||
ksmbd_tcp_new_connection(client_sk);
|
||||
}
|
||||
|
||||
ksmbd_debug(CONN, "releasing socket\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_tcp_run_kthread() - start forker thread
|
||||
* @iface: pointer to struct interface
|
||||
*
|
||||
* start forker thread(ksmbd/0) at module init time to listen
|
||||
* on port 445 for new SMB connection requests. It creates per connection
|
||||
* server threads(ksmbd/x)
|
||||
*
|
||||
* Return: 0 on success or error number
|
||||
*/
|
||||
static int ksmbd_tcp_run_kthread(struct interface *iface)
|
||||
{
|
||||
int rc;
|
||||
struct task_struct *kthread;
|
||||
|
||||
kthread = kthread_run(ksmbd_kthread_fn, (void *)iface, "ksmbd-%s",
|
||||
iface->name);
|
||||
if (IS_ERR(kthread)) {
|
||||
rc = PTR_ERR(kthread);
|
||||
return rc;
|
||||
}
|
||||
iface->ksmbd_kthread = kthread;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_tcp_readv() - read data from socket in given iovec
|
||||
* @t: TCP transport instance
|
||||
* @iov_orig: base IO vector
|
||||
* @nr_segs: number of segments in base iov
|
||||
* @to_read: number of bytes to read from socket
|
||||
*
|
||||
* Return: on success return number of bytes read from socket,
|
||||
* otherwise return error number
|
||||
*/
|
||||
static int ksmbd_tcp_readv(struct tcp_transport *t, struct kvec *iov_orig,
|
||||
unsigned int nr_segs, unsigned int to_read)
|
||||
{
|
||||
int length = 0;
|
||||
int total_read;
|
||||
unsigned int segs;
|
||||
struct msghdr ksmbd_msg;
|
||||
struct kvec *iov;
|
||||
struct ksmbd_conn *conn = KSMBD_TRANS(t)->conn;
|
||||
|
||||
iov = get_conn_iovec(t, nr_segs);
|
||||
if (!iov)
|
||||
return -ENOMEM;
|
||||
|
||||
ksmbd_msg.msg_control = NULL;
|
||||
ksmbd_msg.msg_controllen = 0;
|
||||
|
||||
for (total_read = 0; to_read; total_read += length, to_read -= length) {
|
||||
try_to_freeze();
|
||||
|
||||
if (!ksmbd_conn_alive(conn)) {
|
||||
total_read = -ESHUTDOWN;
|
||||
break;
|
||||
}
|
||||
segs = kvec_array_init(iov, iov_orig, nr_segs, total_read);
|
||||
|
||||
length = kernel_recvmsg(t->sock, &ksmbd_msg,
|
||||
iov, segs, to_read, 0);
|
||||
|
||||
if (length == -EINTR) {
|
||||
total_read = -ESHUTDOWN;
|
||||
break;
|
||||
} else if (conn->status == KSMBD_SESS_NEED_RECONNECT) {
|
||||
total_read = -EAGAIN;
|
||||
break;
|
||||
} else if (length == -ERESTARTSYS || length == -EAGAIN) {
|
||||
usleep_range(1000, 2000);
|
||||
length = 0;
|
||||
continue;
|
||||
} else if (length <= 0) {
|
||||
total_read = -EAGAIN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return total_read;
|
||||
}
|
||||
|
||||
/**
|
||||
* ksmbd_tcp_read() - read data from socket in given buffer
|
||||
* @t: TCP transport instance
|
||||
* @buf: buffer to store read data from socket
|
||||
* @to_read: number of bytes to read from socket
|
||||
*
|
||||
* Return: on success return number of bytes read from socket,
|
||||
* otherwise return error number
|
||||
*/
|
||||
static int ksmbd_tcp_read(struct ksmbd_transport *t, char *buf, unsigned int to_read)
|
||||
{
|
||||
struct kvec iov;
|
||||
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = to_read;
|
||||
|
||||
return ksmbd_tcp_readv(TCP_TRANS(t), &iov, 1, to_read);
|
||||
}
|
||||
|
||||
static int ksmbd_tcp_writev(struct ksmbd_transport *t, struct kvec *iov,
|
||||
int nvecs, int size, bool need_invalidate,
|
||||
unsigned int remote_key)
|
||||
|
||||
{
|
||||
struct msghdr smb_msg = {.msg_flags = MSG_NOSIGNAL};
|
||||
|
||||
return kernel_sendmsg(TCP_TRANS(t)->sock, &smb_msg, iov, nvecs, size);
|
||||
}
|
||||
|
||||
static void ksmbd_tcp_disconnect(struct ksmbd_transport *t)
|
||||
{
|
||||
free_transport(TCP_TRANS(t));
|
||||
}
|
||||
|
||||
static void tcp_destroy_socket(struct socket *ksmbd_socket)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!ksmbd_socket)
|
||||
return;
|
||||
|
||||
/* set zero to timeout */
|
||||
ksmbd_tcp_rcv_timeout(ksmbd_socket, 0);
|
||||
ksmbd_tcp_snd_timeout(ksmbd_socket, 0);
|
||||
|
||||
ret = kernel_sock_shutdown(ksmbd_socket, SHUT_RDWR);
|
||||
if (ret)
|
||||
pr_err("Failed to shutdown socket: %d\n", ret);
|
||||
sock_release(ksmbd_socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* create_socket - create socket for ksmbd/0
|
||||
*
|
||||
* Return: Returns a task_struct or ERR_PTR
|
||||
*/
|
||||
static int create_socket(struct interface *iface)
|
||||
{
|
||||
int ret;
|
||||
struct sockaddr_in6 sin6;
|
||||
struct sockaddr_in sin;
|
||||
struct socket *ksmbd_socket;
|
||||
bool ipv4 = false;
|
||||
|
||||
ret = sock_create(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &ksmbd_socket);
|
||||
if (ret) {
|
||||
pr_err("Can't create socket for ipv6, try ipv4: %d\n", ret);
|
||||
ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP,
|
||||
&ksmbd_socket);
|
||||
if (ret) {
|
||||
pr_err("Can't create socket for ipv4: %d\n", ret);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
sin.sin_family = PF_INET;
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sin.sin_port = htons(server_conf.tcp_port);
|
||||
ipv4 = true;
|
||||
} else {
|
||||
sin6.sin6_family = PF_INET6;
|
||||
sin6.sin6_addr = in6addr_any;
|
||||
sin6.sin6_port = htons(server_conf.tcp_port);
|
||||
}
|
||||
|
||||
ksmbd_tcp_nodelay(ksmbd_socket);
|
||||
ksmbd_tcp_reuseaddr(ksmbd_socket);
|
||||
|
||||
ret = sock_setsockopt(ksmbd_socket,
|
||||
SOL_SOCKET,
|
||||
SO_BINDTODEVICE,
|
||||
KERNEL_SOCKPTR(iface->name),
|
||||
strlen(iface->name));
|
||||
if (ret != -ENODEV && ret < 0) {
|
||||
pr_err("Failed to set SO_BINDTODEVICE: %d\n", ret);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
if (ipv4)
|
||||
ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin,
|
||||
sizeof(sin));
|
||||
else
|
||||
ret = kernel_bind(ksmbd_socket, (struct sockaddr *)&sin6,
|
||||
sizeof(sin6));
|
||||
if (ret) {
|
||||
pr_err("Failed to bind socket: %d\n", ret);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
ksmbd_socket->sk->sk_rcvtimeo = KSMBD_TCP_RECV_TIMEOUT;
|
||||
ksmbd_socket->sk->sk_sndtimeo = KSMBD_TCP_SEND_TIMEOUT;
|
||||
|
||||
ret = kernel_listen(ksmbd_socket, KSMBD_SOCKET_BACKLOG);
|
||||
if (ret) {
|
||||
pr_err("Port listen() error: %d\n", ret);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
iface->ksmbd_socket = ksmbd_socket;
|
||||
ret = ksmbd_tcp_run_kthread(iface);
|
||||
if (ret) {
|
||||
pr_err("Can't start ksmbd main kthread: %d\n", ret);
|
||||
goto out_error;
|
||||
}
|
||||
iface->state = IFACE_STATE_CONFIGURED;
|
||||
|
||||
return 0;
|
||||
|
||||
out_error:
|
||||
tcp_destroy_socket(ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ksmbd_netdev_event(struct notifier_block *nb, unsigned long event,
|
||||
void *ptr)
|
||||
{
|
||||
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
|
||||
struct interface *iface;
|
||||
int ret, found = 0;
|
||||
|
||||
switch (event) {
|
||||
case NETDEV_UP:
|
||||
if (netdev->priv_flags & IFF_BRIDGE_PORT)
|
||||
return NOTIFY_OK;
|
||||
|
||||
list_for_each_entry(iface, &iface_list, entry) {
|
||||
if (!strcmp(iface->name, netdev->name)) {
|
||||
found = 1;
|
||||
if (iface->state != IFACE_STATE_DOWN)
|
||||
break;
|
||||
ret = create_socket(iface);
|
||||
if (ret)
|
||||
return NOTIFY_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found && bind_additional_ifaces) {
|
||||
iface = alloc_iface(kstrdup(netdev->name, GFP_KERNEL));
|
||||
if (!iface)
|
||||
return NOTIFY_OK;
|
||||
ret = create_socket(iface);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NETDEV_DOWN:
|
||||
list_for_each_entry(iface, &iface_list, entry) {
|
||||
if (!strcmp(iface->name, netdev->name) &&
|
||||
iface->state == IFACE_STATE_CONFIGURED) {
|
||||
tcp_stop_kthread(iface->ksmbd_kthread);
|
||||
iface->ksmbd_kthread = NULL;
|
||||
mutex_lock(&iface->sock_release_lock);
|
||||
tcp_destroy_socket(iface->ksmbd_socket);
|
||||
iface->ksmbd_socket = NULL;
|
||||
mutex_unlock(&iface->sock_release_lock);
|
||||
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block ksmbd_netdev_notifier = {
|
||||
.notifier_call = ksmbd_netdev_event,
|
||||
};
|
||||
|
||||
int ksmbd_tcp_init(void)
|
||||
{
|
||||
register_netdevice_notifier(&ksmbd_netdev_notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tcp_stop_kthread(struct task_struct *kthread)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!kthread)
|
||||
return;
|
||||
|
||||
ret = kthread_stop(kthread);
|
||||
if (ret)
|
||||
pr_err("failed to stop forker thread\n");
|
||||
}
|
||||
|
||||
void ksmbd_tcp_destroy(void)
|
||||
{
|
||||
struct interface *iface, *tmp;
|
||||
|
||||
unregister_netdevice_notifier(&ksmbd_netdev_notifier);
|
||||
|
||||
list_for_each_entry_safe(iface, tmp, &iface_list, entry) {
|
||||
list_del(&iface->entry);
|
||||
kfree(iface->name);
|
||||
kfree(iface);
|
||||
}
|
||||
}
|
||||
|
||||
static struct interface *alloc_iface(char *ifname)
|
||||
{
|
||||
struct interface *iface;
|
||||
|
||||
if (!ifname)
|
||||
return NULL;
|
||||
|
||||
iface = kzalloc(sizeof(struct interface), GFP_KERNEL);
|
||||
if (!iface) {
|
||||
kfree(ifname);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
iface->name = ifname;
|
||||
iface->state = IFACE_STATE_DOWN;
|
||||
list_add(&iface->entry, &iface_list);
|
||||
mutex_init(&iface->sock_release_lock);
|
||||
return iface;
|
||||
}
|
||||
|
||||
int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz)
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
if (!ifc_list_sz) {
|
||||
struct net_device *netdev;
|
||||
|
||||
rtnl_lock();
|
||||
for_each_netdev(&init_net, netdev) {
|
||||
if (netdev->priv_flags & IFF_BRIDGE_PORT)
|
||||
continue;
|
||||
if (!alloc_iface(kstrdup(netdev->name, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
}
|
||||
rtnl_unlock();
|
||||
bind_additional_ifaces = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (ifc_list_sz > 0) {
|
||||
if (!alloc_iface(kstrdup(ifc_list, GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
sz = strlen(ifc_list);
|
||||
if (!sz)
|
||||
break;
|
||||
|
||||
ifc_list += sz + 1;
|
||||
ifc_list_sz -= (sz + 1);
|
||||
}
|
||||
|
||||
bind_additional_ifaces = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_transport_ops ksmbd_tcp_transport_ops = {
|
||||
.read = ksmbd_tcp_read,
|
||||
.writev = ksmbd_tcp_writev,
|
||||
.disconnect = ksmbd_tcp_disconnect,
|
||||
};
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_TRANSPORT_TCP_H__
|
||||
#define __KSMBD_TRANSPORT_TCP_H__
|
||||
|
||||
int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz);
|
||||
int ksmbd_tcp_init(void);
|
||||
void ksmbd_tcp_destroy(void);
|
||||
|
||||
#endif /* __KSMBD_TRANSPORT_TCP_H__ */
|
|
@ -0,0 +1,384 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Some of the source code in this file came from fs/cifs/cifs_unicode.c
|
||||
*
|
||||
* Copyright (c) International Business Machines Corp., 2000,2009
|
||||
* Modified by Steve French (sfrench@us.ibm.com)
|
||||
* Modified by Namjae Jeon (linkinjeon@kernel.org)
|
||||
*/
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "glob.h"
|
||||
#include "unicode.h"
|
||||
#include "uniupr.h"
|
||||
#include "smb_common.h"
|
||||
|
||||
/*
|
||||
* smb_utf16_bytes() - how long will a string be after conversion?
|
||||
* @from: pointer to input string
|
||||
* @maxbytes: don't go past this many bytes of input string
|
||||
* @codepage: destination codepage
|
||||
*
|
||||
* Walk a utf16le string and return the number of bytes that the string will
|
||||
* be after being converted to the given charset, not including any null
|
||||
* termination required. Don't walk past maxbytes in the source buffer.
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static int smb_utf16_bytes(const __le16 *from, int maxbytes,
|
||||
const struct nls_table *codepage)
|
||||
{
|
||||
int i;
|
||||
int charlen, outlen = 0;
|
||||
int maxwords = maxbytes / 2;
|
||||
char tmp[NLS_MAX_CHARSET_SIZE];
|
||||
__u16 ftmp;
|
||||
|
||||
for (i = 0; i < maxwords; i++) {
|
||||
ftmp = get_unaligned_le16(&from[i]);
|
||||
if (ftmp == 0)
|
||||
break;
|
||||
|
||||
charlen = codepage->uni2char(ftmp, tmp, NLS_MAX_CHARSET_SIZE);
|
||||
if (charlen > 0)
|
||||
outlen += charlen;
|
||||
else
|
||||
outlen++;
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* cifs_mapchar() - convert a host-endian char to proper char in codepage
|
||||
* @target: where converted character should be copied
|
||||
* @src_char: 2 byte host-endian source character
|
||||
* @cp: codepage to which character should be converted
|
||||
* @mapchar: should character be mapped according to mapchars mount option?
|
||||
*
|
||||
* This function handles the conversion of a single character. It is the
|
||||
* responsibility of the caller to ensure that the target buffer is large
|
||||
* enough to hold the result of the conversion (at least NLS_MAX_CHARSET_SIZE).
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static int
|
||||
cifs_mapchar(char *target, const __u16 src_char, const struct nls_table *cp,
|
||||
bool mapchar)
|
||||
{
|
||||
int len = 1;
|
||||
|
||||
if (!mapchar)
|
||||
goto cp_convert;
|
||||
|
||||
/*
|
||||
* BB: Cannot handle remapping UNI_SLASH until all the calls to
|
||||
* build_path_from_dentry are modified, as they use slash as
|
||||
* separator.
|
||||
*/
|
||||
switch (src_char) {
|
||||
case UNI_COLON:
|
||||
*target = ':';
|
||||
break;
|
||||
case UNI_ASTERISK:
|
||||
*target = '*';
|
||||
break;
|
||||
case UNI_QUESTION:
|
||||
*target = '?';
|
||||
break;
|
||||
case UNI_PIPE:
|
||||
*target = '|';
|
||||
break;
|
||||
case UNI_GRTRTHAN:
|
||||
*target = '>';
|
||||
break;
|
||||
case UNI_LESSTHAN:
|
||||
*target = '<';
|
||||
break;
|
||||
default:
|
||||
goto cp_convert;
|
||||
}
|
||||
|
||||
out:
|
||||
return len;
|
||||
|
||||
cp_convert:
|
||||
len = cp->uni2char(src_char, target, NLS_MAX_CHARSET_SIZE);
|
||||
if (len <= 0) {
|
||||
*target = '?';
|
||||
len = 1;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* is_char_allowed() - check for valid character
|
||||
* @ch: input character to be checked
|
||||
*
|
||||
* Return: 1 if char is allowed, otherwise 0
|
||||
*/
|
||||
static inline int is_char_allowed(char *ch)
|
||||
{
|
||||
/* check for control chars, wildcards etc. */
|
||||
if (!(*ch & 0x80) &&
|
||||
(*ch <= 0x1f ||
|
||||
*ch == '?' || *ch == '"' || *ch == '<' ||
|
||||
*ch == '>' || *ch == '|'))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* smb_from_utf16() - convert utf16le string to local charset
|
||||
* @to: destination buffer
|
||||
* @from: source buffer
|
||||
* @tolen: destination buffer size (in bytes)
|
||||
* @fromlen: source buffer size (in bytes)
|
||||
* @codepage: codepage to which characters should be converted
|
||||
* @mapchar: should characters be remapped according to the mapchars option?
|
||||
*
|
||||
* Convert a little-endian utf16le string (as sent by the server) to a string
|
||||
* in the provided codepage. The tolen and fromlen parameters are to ensure
|
||||
* that the code doesn't walk off of the end of the buffer (which is always
|
||||
* a danger if the alignment of the source buffer is off). The destination
|
||||
* string is always properly null terminated and fits in the destination
|
||||
* buffer. Returns the length of the destination string in bytes (including
|
||||
* null terminator).
|
||||
*
|
||||
* Note that some windows versions actually send multiword UTF-16 characters
|
||||
* instead of straight UTF16-2. The linux nls routines however aren't able to
|
||||
* deal with those characters properly. In the event that we get some of
|
||||
* those characters, they won't be translated properly.
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
static int smb_from_utf16(char *to, const __le16 *from, int tolen, int fromlen,
|
||||
const struct nls_table *codepage, bool mapchar)
|
||||
{
|
||||
int i, charlen, safelen;
|
||||
int outlen = 0;
|
||||
int nullsize = nls_nullsize(codepage);
|
||||
int fromwords = fromlen / 2;
|
||||
char tmp[NLS_MAX_CHARSET_SIZE];
|
||||
__u16 ftmp;
|
||||
|
||||
/*
|
||||
* because the chars can be of varying widths, we need to take care
|
||||
* not to overflow the destination buffer when we get close to the
|
||||
* end of it. Until we get to this offset, we don't need to check
|
||||
* for overflow however.
|
||||
*/
|
||||
safelen = tolen - (NLS_MAX_CHARSET_SIZE + nullsize);
|
||||
|
||||
for (i = 0; i < fromwords; i++) {
|
||||
ftmp = get_unaligned_le16(&from[i]);
|
||||
if (ftmp == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* check to see if converting this character might make the
|
||||
* conversion bleed into the null terminator
|
||||
*/
|
||||
if (outlen >= safelen) {
|
||||
charlen = cifs_mapchar(tmp, ftmp, codepage, mapchar);
|
||||
if ((outlen + charlen) > (tolen - nullsize))
|
||||
break;
|
||||
}
|
||||
|
||||
/* put converted char into 'to' buffer */
|
||||
charlen = cifs_mapchar(&to[outlen], ftmp, codepage, mapchar);
|
||||
outlen += charlen;
|
||||
}
|
||||
|
||||
/* properly null-terminate string */
|
||||
for (i = 0; i < nullsize; i++)
|
||||
to[outlen++] = 0;
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* smb_strtoUTF16() - Convert character string to unicode string
|
||||
* @to: destination buffer
|
||||
* @from: source buffer
|
||||
* @len: destination buffer size (in bytes)
|
||||
* @codepage: codepage to which characters should be converted
|
||||
*
|
||||
* Return: string length after conversion
|
||||
*/
|
||||
int smb_strtoUTF16(__le16 *to, const char *from, int len,
|
||||
const struct nls_table *codepage)
|
||||
{
|
||||
int charlen;
|
||||
int i;
|
||||
wchar_t wchar_to; /* needed to quiet sparse */
|
||||
|
||||
/* special case for utf8 to handle no plane0 chars */
|
||||
if (!strcmp(codepage->charset, "utf8")) {
|
||||
/*
|
||||
* convert utf8 -> utf16, we assume we have enough space
|
||||
* as caller should have assumed conversion does not overflow
|
||||
* in destination len is length in wchar_t units (16bits)
|
||||
*/
|
||||
i = utf8s_to_utf16s(from, len, UTF16_LITTLE_ENDIAN,
|
||||
(wchar_t *)to, len);
|
||||
|
||||
/* if success terminate and exit */
|
||||
if (i >= 0)
|
||||
goto success;
|
||||
/*
|
||||
* if fails fall back to UCS encoding as this
|
||||
* function should not return negative values
|
||||
* currently can fail only if source contains
|
||||
* invalid encoded characters
|
||||
*/
|
||||
}
|
||||
|
||||
for (i = 0; len > 0 && *from; i++, from += charlen, len -= charlen) {
|
||||
charlen = codepage->char2uni(from, len, &wchar_to);
|
||||
if (charlen < 1) {
|
||||
/* A question mark */
|
||||
wchar_to = 0x003f;
|
||||
charlen = 1;
|
||||
}
|
||||
put_unaligned_le16(wchar_to, &to[i]);
|
||||
}
|
||||
|
||||
success:
|
||||
put_unaligned_le16(0, &to[i]);
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* smb_strndup_from_utf16() - copy a string from wire format to the local
|
||||
* codepage
|
||||
* @src: source string
|
||||
* @maxlen: don't walk past this many bytes in the source string
|
||||
* @is_unicode: is this a unicode string?
|
||||
* @codepage: destination codepage
|
||||
*
|
||||
* Take a string given by the server, convert it to the local codepage and
|
||||
* put it in a new buffer. Returns a pointer to the new string or NULL on
|
||||
* error.
|
||||
*
|
||||
* Return: destination string buffer or error ptr
|
||||
*/
|
||||
char *smb_strndup_from_utf16(const char *src, const int maxlen,
|
||||
const bool is_unicode,
|
||||
const struct nls_table *codepage)
|
||||
{
|
||||
int len, ret;
|
||||
char *dst;
|
||||
|
||||
if (is_unicode) {
|
||||
len = smb_utf16_bytes((__le16 *)src, maxlen, codepage);
|
||||
len += nls_nullsize(codepage);
|
||||
dst = kmalloc(len, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
ret = smb_from_utf16(dst, (__le16 *)src, len, maxlen, codepage,
|
||||
false);
|
||||
if (ret < 0) {
|
||||
kfree(dst);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
} else {
|
||||
len = strnlen(src, maxlen);
|
||||
len++;
|
||||
dst = kmalloc(len, GFP_KERNEL);
|
||||
if (!dst)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
strscpy(dst, src, len);
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert 16 bit Unicode pathname to wire format from string in current code
|
||||
* page. Conversion may involve remapping up the six characters that are
|
||||
* only legal in POSIX-like OS (if they are present in the string). Path
|
||||
* names are little endian 16 bit Unicode on the wire
|
||||
*/
|
||||
/*
|
||||
* smbConvertToUTF16() - convert string from local charset to utf16
|
||||
* @target: destination buffer
|
||||
* @source: source buffer
|
||||
* @srclen: source buffer size (in bytes)
|
||||
* @cp: codepage to which characters should be converted
|
||||
* @mapchar: should characters be remapped according to the mapchars option?
|
||||
*
|
||||
* Convert 16 bit Unicode pathname to wire format from string in current code
|
||||
* page. Conversion may involve remapping up the six characters that are
|
||||
* only legal in POSIX-like OS (if they are present in the string). Path
|
||||
* names are little endian 16 bit Unicode on the wire
|
||||
*
|
||||
* Return: char length after conversion
|
||||
*/
|
||||
int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
|
||||
const struct nls_table *cp, int mapchars)
|
||||
{
|
||||
int i, j, charlen;
|
||||
char src_char;
|
||||
__le16 dst_char;
|
||||
wchar_t tmp;
|
||||
|
||||
if (!mapchars)
|
||||
return smb_strtoUTF16(target, source, srclen, cp);
|
||||
|
||||
for (i = 0, j = 0; i < srclen; j++) {
|
||||
src_char = source[i];
|
||||
charlen = 1;
|
||||
switch (src_char) {
|
||||
case 0:
|
||||
put_unaligned(0, &target[j]);
|
||||
return j;
|
||||
case ':':
|
||||
dst_char = cpu_to_le16(UNI_COLON);
|
||||
break;
|
||||
case '*':
|
||||
dst_char = cpu_to_le16(UNI_ASTERISK);
|
||||
break;
|
||||
case '?':
|
||||
dst_char = cpu_to_le16(UNI_QUESTION);
|
||||
break;
|
||||
case '<':
|
||||
dst_char = cpu_to_le16(UNI_LESSTHAN);
|
||||
break;
|
||||
case '>':
|
||||
dst_char = cpu_to_le16(UNI_GRTRTHAN);
|
||||
break;
|
||||
case '|':
|
||||
dst_char = cpu_to_le16(UNI_PIPE);
|
||||
break;
|
||||
/*
|
||||
* FIXME: We can not handle remapping backslash (UNI_SLASH)
|
||||
* until all the calls to build_path_from_dentry are modified,
|
||||
* as they use backslash as separator.
|
||||
*/
|
||||
default:
|
||||
charlen = cp->char2uni(source + i, srclen - i, &tmp);
|
||||
dst_char = cpu_to_le16(tmp);
|
||||
|
||||
/*
|
||||
* if no match, use question mark, which at least in
|
||||
* some cases serves as wild card
|
||||
*/
|
||||
if (charlen < 1) {
|
||||
dst_char = cpu_to_le16(0x003f);
|
||||
charlen = 1;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* character may take more than one byte in the source string,
|
||||
* but will take exactly two bytes in the target string
|
||||
*/
|
||||
i += charlen;
|
||||
put_unaligned(dst_char, &target[j]);
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
|
@ -0,0 +1,357 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Some of the source code in this file came from fs/cifs/cifs_unicode.c
|
||||
* cifs_unicode: Unicode kernel case support
|
||||
*
|
||||
* Function:
|
||||
* Convert a unicode character to upper or lower case using
|
||||
* compressed tables.
|
||||
*
|
||||
* Copyright (c) International Business Machines Corp., 2000,2009
|
||||
*
|
||||
*
|
||||
* Notes:
|
||||
* These APIs are based on the C library functions. The semantics
|
||||
* should match the C functions but with expanded size operands.
|
||||
*
|
||||
* The upper/lower functions are based on a table created by mkupr.
|
||||
* This is a compressed table of upper and lower case conversion.
|
||||
*
|
||||
*/
|
||||
#ifndef _CIFS_UNICODE_H
|
||||
#define _CIFS_UNICODE_H
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/nls.h>
|
||||
|
||||
#define UNIUPR_NOLOWER /* Example to not expand lower case tables */
|
||||
|
||||
/*
|
||||
* Windows maps these to the user defined 16 bit Unicode range since they are
|
||||
* reserved symbols (along with \ and /), otherwise illegal to store
|
||||
* in filenames in NTFS
|
||||
*/
|
||||
#define UNI_ASTERISK ((__u16)('*' + 0xF000))
|
||||
#define UNI_QUESTION ((__u16)('?' + 0xF000))
|
||||
#define UNI_COLON ((__u16)(':' + 0xF000))
|
||||
#define UNI_GRTRTHAN ((__u16)('>' + 0xF000))
|
||||
#define UNI_LESSTHAN ((__u16)('<' + 0xF000))
|
||||
#define UNI_PIPE ((__u16)('|' + 0xF000))
|
||||
#define UNI_SLASH ((__u16)('\\' + 0xF000))
|
||||
|
||||
/* Just define what we want from uniupr.h. We don't want to define the tables
|
||||
* in each source file.
|
||||
*/
|
||||
#ifndef UNICASERANGE_DEFINED
|
||||
struct UniCaseRange {
|
||||
wchar_t start;
|
||||
wchar_t end;
|
||||
signed char *table;
|
||||
};
|
||||
#endif /* UNICASERANGE_DEFINED */
|
||||
|
||||
#ifndef UNIUPR_NOUPPER
|
||||
extern signed char SmbUniUpperTable[512];
|
||||
extern const struct UniCaseRange SmbUniUpperRange[];
|
||||
#endif /* UNIUPR_NOUPPER */
|
||||
|
||||
#ifndef UNIUPR_NOLOWER
|
||||
extern signed char CifsUniLowerTable[512];
|
||||
extern const struct UniCaseRange CifsUniLowerRange[];
|
||||
#endif /* UNIUPR_NOLOWER */
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int smb_strtoUTF16(__le16 *to, const char *from, int len,
|
||||
const struct nls_table *codepage);
|
||||
char *smb_strndup_from_utf16(const char *src, const int maxlen,
|
||||
const bool is_unicode,
|
||||
const struct nls_table *codepage);
|
||||
int smbConvertToUTF16(__le16 *target, const char *source, int srclen,
|
||||
const struct nls_table *cp, int mapchars);
|
||||
char *ksmbd_extract_sharename(char *treename);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* UniStrcat: Concatenate the second string to the first
|
||||
*
|
||||
* Returns:
|
||||
* Address of the first string
|
||||
*/
|
||||
static inline wchar_t *UniStrcat(wchar_t *ucs1, const wchar_t *ucs2)
|
||||
{
|
||||
wchar_t *anchor = ucs1; /* save a pointer to start of ucs1 */
|
||||
|
||||
while (*ucs1++)
|
||||
/*NULL*/; /* To end of first string */
|
||||
ucs1--; /* Return to the null */
|
||||
while ((*ucs1++ = *ucs2++))
|
||||
/*NULL*/; /* copy string 2 over */
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrchr: Find a character in a string
|
||||
*
|
||||
* Returns:
|
||||
* Address of first occurrence of character in string
|
||||
* or NULL if the character is not in the string
|
||||
*/
|
||||
static inline wchar_t *UniStrchr(const wchar_t *ucs, wchar_t uc)
|
||||
{
|
||||
while ((*ucs != uc) && *ucs)
|
||||
ucs++;
|
||||
|
||||
if (*ucs == uc)
|
||||
return (wchar_t *)ucs;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrcmp: Compare two strings
|
||||
*
|
||||
* Returns:
|
||||
* < 0: First string is less than second
|
||||
* = 0: Strings are equal
|
||||
* > 0: First string is greater than second
|
||||
*/
|
||||
static inline int UniStrcmp(const wchar_t *ucs1, const wchar_t *ucs2)
|
||||
{
|
||||
while ((*ucs1 == *ucs2) && *ucs1) {
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
}
|
||||
return (int)*ucs1 - (int)*ucs2;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrcpy: Copy a string
|
||||
*/
|
||||
static inline wchar_t *UniStrcpy(wchar_t *ucs1, const wchar_t *ucs2)
|
||||
{
|
||||
wchar_t *anchor = ucs1; /* save the start of result string */
|
||||
|
||||
while ((*ucs1++ = *ucs2++))
|
||||
/*NULL*/;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrlen: Return the length of a string (in 16 bit Unicode chars not bytes)
|
||||
*/
|
||||
static inline size_t UniStrlen(const wchar_t *ucs1)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (*ucs1++)
|
||||
i++;
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrnlen: Return the length (in 16 bit Unicode chars not bytes) of a
|
||||
* string (length limited)
|
||||
*/
|
||||
static inline size_t UniStrnlen(const wchar_t *ucs1, int maxlen)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
while (*ucs1++) {
|
||||
i++;
|
||||
if (i >= maxlen)
|
||||
break;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncat: Concatenate length limited string
|
||||
*/
|
||||
static inline wchar_t *UniStrncat(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
|
||||
{
|
||||
wchar_t *anchor = ucs1; /* save pointer to string 1 */
|
||||
|
||||
while (*ucs1++)
|
||||
/*NULL*/;
|
||||
ucs1--; /* point to null terminator of s1 */
|
||||
while (n-- && (*ucs1 = *ucs2)) { /* copy s2 after s1 */
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
}
|
||||
*ucs1 = 0; /* Null terminate the result */
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncmp: Compare length limited string
|
||||
*/
|
||||
static inline int UniStrncmp(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
|
||||
{
|
||||
if (!n)
|
||||
return 0; /* Null strings are equal */
|
||||
while ((*ucs1 == *ucs2) && *ucs1 && --n) {
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
}
|
||||
return (int)*ucs1 - (int)*ucs2;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncmp_le: Compare length limited string - native to little-endian
|
||||
*/
|
||||
static inline int
|
||||
UniStrncmp_le(const wchar_t *ucs1, const wchar_t *ucs2, size_t n)
|
||||
{
|
||||
if (!n)
|
||||
return 0; /* Null strings are equal */
|
||||
while ((*ucs1 == __le16_to_cpu(*ucs2)) && *ucs1 && --n) {
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
}
|
||||
return (int)*ucs1 - (int)__le16_to_cpu(*ucs2);
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncpy: Copy length limited string with pad
|
||||
*/
|
||||
static inline wchar_t *UniStrncpy(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
|
||||
{
|
||||
wchar_t *anchor = ucs1;
|
||||
|
||||
while (n-- && *ucs2) /* Copy the strings */
|
||||
*ucs1++ = *ucs2++;
|
||||
|
||||
n++;
|
||||
while (n--) /* Pad with nulls */
|
||||
*ucs1++ = 0;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrncpy_le: Copy length limited string with pad to little-endian
|
||||
*/
|
||||
static inline wchar_t *UniStrncpy_le(wchar_t *ucs1, const wchar_t *ucs2, size_t n)
|
||||
{
|
||||
wchar_t *anchor = ucs1;
|
||||
|
||||
while (n-- && *ucs2) /* Copy the strings */
|
||||
*ucs1++ = __le16_to_cpu(*ucs2++);
|
||||
|
||||
n++;
|
||||
while (n--) /* Pad with nulls */
|
||||
*ucs1++ = 0;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrstr: Find a string in a string
|
||||
*
|
||||
* Returns:
|
||||
* Address of first match found
|
||||
* NULL if no matching string is found
|
||||
*/
|
||||
static inline wchar_t *UniStrstr(const wchar_t *ucs1, const wchar_t *ucs2)
|
||||
{
|
||||
const wchar_t *anchor1 = ucs1;
|
||||
const wchar_t *anchor2 = ucs2;
|
||||
|
||||
while (*ucs1) {
|
||||
if (*ucs1 == *ucs2) {
|
||||
/* Partial match found */
|
||||
ucs1++;
|
||||
ucs2++;
|
||||
} else {
|
||||
if (!*ucs2) /* Match found */
|
||||
return (wchar_t *)anchor1;
|
||||
ucs1 = ++anchor1; /* No match */
|
||||
ucs2 = anchor2;
|
||||
}
|
||||
}
|
||||
|
||||
if (!*ucs2) /* Both end together */
|
||||
return (wchar_t *)anchor1; /* Match found */
|
||||
return NULL; /* No match */
|
||||
}
|
||||
|
||||
#ifndef UNIUPR_NOUPPER
|
||||
/*
|
||||
* UniToupper: Convert a unicode character to upper case
|
||||
*/
|
||||
static inline wchar_t UniToupper(register wchar_t uc)
|
||||
{
|
||||
register const struct UniCaseRange *rp;
|
||||
|
||||
if (uc < sizeof(SmbUniUpperTable)) {
|
||||
/* Latin characters */
|
||||
return uc + SmbUniUpperTable[uc]; /* Use base tables */
|
||||
}
|
||||
|
||||
rp = SmbUniUpperRange; /* Use range tables */
|
||||
while (rp->start) {
|
||||
if (uc < rp->start) /* Before start of range */
|
||||
return uc; /* Uppercase = input */
|
||||
if (uc <= rp->end) /* In range */
|
||||
return uc + rp->table[uc - rp->start];
|
||||
rp++; /* Try next range */
|
||||
}
|
||||
return uc; /* Past last range */
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrupr: Upper case a unicode string
|
||||
*/
|
||||
static inline __le16 *UniStrupr(register __le16 *upin)
|
||||
{
|
||||
register __le16 *up;
|
||||
|
||||
up = upin;
|
||||
while (*up) { /* For all characters */
|
||||
*up = cpu_to_le16(UniToupper(le16_to_cpu(*up)));
|
||||
up++;
|
||||
}
|
||||
return upin; /* Return input pointer */
|
||||
}
|
||||
#endif /* UNIUPR_NOUPPER */
|
||||
|
||||
#ifndef UNIUPR_NOLOWER
|
||||
/*
|
||||
* UniTolower: Convert a unicode character to lower case
|
||||
*/
|
||||
static inline wchar_t UniTolower(register wchar_t uc)
|
||||
{
|
||||
register const struct UniCaseRange *rp;
|
||||
|
||||
if (uc < sizeof(CifsUniLowerTable)) {
|
||||
/* Latin characters */
|
||||
return uc + CifsUniLowerTable[uc]; /* Use base tables */
|
||||
}
|
||||
|
||||
rp = CifsUniLowerRange; /* Use range tables */
|
||||
while (rp->start) {
|
||||
if (uc < rp->start) /* Before start of range */
|
||||
return uc; /* Uppercase = input */
|
||||
if (uc <= rp->end) /* In range */
|
||||
return uc + rp->table[uc - rp->start];
|
||||
rp++; /* Try next range */
|
||||
}
|
||||
return uc; /* Past last range */
|
||||
}
|
||||
|
||||
/*
|
||||
* UniStrlwr: Lower case a unicode string
|
||||
*/
|
||||
static inline wchar_t *UniStrlwr(register wchar_t *upin)
|
||||
{
|
||||
register wchar_t *up;
|
||||
|
||||
up = upin;
|
||||
while (*up) { /* For all characters */
|
||||
*up = UniTolower(*up);
|
||||
up++;
|
||||
}
|
||||
return upin; /* Return input pointer */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _CIFS_UNICODE_H */
|
|
@ -0,0 +1,268 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Some of the source code in this file came from fs/cifs/uniupr.h
|
||||
* Copyright (c) International Business Machines Corp., 2000,2002
|
||||
*
|
||||
* uniupr.h - Unicode compressed case ranges
|
||||
*
|
||||
*/
|
||||
#ifndef __KSMBD_UNIUPR_H
|
||||
#define __KSMBD_UNIUPR_H
|
||||
|
||||
#ifndef UNIUPR_NOUPPER
|
||||
/*
|
||||
* Latin upper case
|
||||
*/
|
||||
signed char SmbUniUpperTable[512] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 040-04f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 050-05f */
|
||||
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, -32, /* 060-06f */
|
||||
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, 0, 0, 0, 0, 0, /* 070-07f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0c0-0cf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0d0-0df */
|
||||
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, -32, -32, /* 0e0-0ef */
|
||||
-32, -32, -32, -32, -32, -32, -32, 0, -32, -32,
|
||||
-32, -32, -32, -32, -32, 121, /* 0f0-0ff */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 100-10f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 110-11f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 120-12f */
|
||||
0, 0, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 130-13f */
|
||||
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, /* 140-14f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 150-15f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 160-16f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, /* 170-17f */
|
||||
0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, /* 180-18f */
|
||||
0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, /* 190-19f */
|
||||
0, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, /* 1a0-1af */
|
||||
-1, 0, 0, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, -1, 0, 0, /* 1b0-1bf */
|
||||
0, 0, 0, 0, 0, -1, -2, 0, -1, -2, 0, -1, -2, 0, -1, 0, /* 1c0-1cf */
|
||||
-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, -79, 0, -1, /* 1d0-1df */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e0-1ef */
|
||||
0, 0, -1, -2, 0, -1, 0, 0, 0, -1, 0, -1, 0, -1, 0, -1, /* 1f0-1ff */
|
||||
};
|
||||
|
||||
/* Upper case range - Greek */
|
||||
static signed char UniCaseRangeU03a0[47] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -38, -37, -37, -37, /* 3a0-3af */
|
||||
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, /* 3b0-3bf */
|
||||
-32, -32, -31, -32, -32, -32, -32, -32, -32, -32, -32, -32, -64,
|
||||
-63, -63,
|
||||
};
|
||||
|
||||
/* Upper case range - Cyrillic */
|
||||
static signed char UniCaseRangeU0430[48] = {
|
||||
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, /* 430-43f */
|
||||
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, /* 440-44f */
|
||||
0, -80, -80, -80, -80, -80, -80, -80, -80, -80, -80,
|
||||
-80, -80, 0, -80, -80, /* 450-45f */
|
||||
};
|
||||
|
||||
/* Upper case range - Extended cyrillic */
|
||||
static signed char UniCaseRangeU0490[61] = {
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 490-49f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4a0-4af */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 4b0-4bf */
|
||||
0, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1,
|
||||
};
|
||||
|
||||
/* Upper case range - Extended latin and greek */
|
||||
static signed char UniCaseRangeU1e00[509] = {
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e00-1e0f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e10-1e1f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e20-1e2f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e30-1e3f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e40-1e4f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e50-1e5f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e60-1e6f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e70-1e7f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1e80-1e8f */
|
||||
0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, -59, 0, -1, 0, -1, /* 1e90-1e9f */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ea0-1eaf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1eb0-1ebf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ec0-1ecf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ed0-1edf */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, /* 1ee0-1eef */
|
||||
0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f00-1f0f */
|
||||
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f10-1f1f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f20-1f2f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f30-1f3f */
|
||||
8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f40-1f4f */
|
||||
0, 8, 0, 8, 0, 8, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f50-1f5f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f60-1f6f */
|
||||
74, 74, 86, 86, 86, 86, 100, 100, 0, 0, 112, 112,
|
||||
126, 126, 0, 0, /* 1f70-1f7f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f80-1f8f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f90-1f9f */
|
||||
8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fa0-1faf */
|
||||
8, 8, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fb0-1fbf */
|
||||
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fc0-1fcf */
|
||||
8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fd0-1fdf */
|
||||
8, 8, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1fe0-1fef */
|
||||
0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
/* Upper case range - Wide latin */
|
||||
static signed char UniCaseRangeUff40[27] = {
|
||||
0, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
-32, -32, -32, -32, -32, /* ff40-ff4f */
|
||||
-32, -32, -32, -32, -32, -32, -32, -32, -32, -32, -32,
|
||||
};
|
||||
|
||||
/*
|
||||
* Upper Case Range
|
||||
*/
|
||||
const struct UniCaseRange SmbUniUpperRange[] = {
|
||||
{0x03a0, 0x03ce, UniCaseRangeU03a0},
|
||||
{0x0430, 0x045f, UniCaseRangeU0430},
|
||||
{0x0490, 0x04cc, UniCaseRangeU0490},
|
||||
{0x1e00, 0x1ffc, UniCaseRangeU1e00},
|
||||
{0xff40, 0xff5a, UniCaseRangeUff40},
|
||||
{0}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef UNIUPR_NOLOWER
|
||||
/*
|
||||
* Latin lower case
|
||||
*/
|
||||
signed char CifsUniLowerTable[512] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 000-00f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 010-01f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 020-02f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 030-03f */
|
||||
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, /* 040-04f */
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 0, 0,
|
||||
0, 0, 0, /* 050-05f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 060-06f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 070-07f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 080-08f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 090-09f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0a0-0af */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0b0-0bf */
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, 32, /* 0c0-0cf */
|
||||
32, 32, 32, 32, 32, 32, 32, 0, 32, 32, 32, 32,
|
||||
32, 32, 32, 0, /* 0d0-0df */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0e0-0ef */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0f0-0ff */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 100-10f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 110-11f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 120-12f */
|
||||
0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, /* 130-13f */
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, /* 140-14f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 150-15f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 160-16f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, -121, 1, 0, 1, 0, 1, 0,
|
||||
0, /* 170-17f */
|
||||
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 79,
|
||||
0, /* 180-18f */
|
||||
0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 190-19f */
|
||||
1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, /* 1a0-1af */
|
||||
0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, /* 1b0-1bf */
|
||||
0, 0, 0, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 1, 0, 1, /* 1c0-1cf */
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, /* 1d0-1df */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e0-1ef */
|
||||
0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1f0-1ff */
|
||||
};
|
||||
|
||||
/* Lower case range - Greek */
|
||||
static signed char UniCaseRangeL0380[44] = {
|
||||
0, 0, 0, 0, 0, 0, 38, 0, 37, 37, 37, 0, 64, 0, 63, 63, /* 380-38f */
|
||||
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, /* 390-39f */
|
||||
32, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
};
|
||||
|
||||
/* Lower case range - Cyrillic */
|
||||
static signed char UniCaseRangeL0400[48] = {
|
||||
0, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
||||
0, 80, 80, /* 400-40f */
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, /* 410-41f */
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, 32, 32, /* 420-42f */
|
||||
};
|
||||
|
||||
/* Lower case range - Extended cyrillic */
|
||||
static signed char UniCaseRangeL0490[60] = {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 490-49f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4a0-4af */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 4b0-4bf */
|
||||
0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1,
|
||||
};
|
||||
|
||||
/* Lower case range - Extended latin and greek */
|
||||
static signed char UniCaseRangeL1e00[504] = {
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e00-1e0f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e10-1e1f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e20-1e2f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e30-1e3f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e40-1e4f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e50-1e5f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e60-1e6f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e70-1e7f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1e80-1e8f */
|
||||
1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, /* 1e90-1e9f */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ea0-1eaf */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1eb0-1ebf */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ec0-1ecf */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ed0-1edf */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, /* 1ee0-1eef */
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, /* 1ef0-1eff */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f00-1f0f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f10-1f1f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f20-1f2f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f30-1f3f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, 0, 0, /* 1f40-1f4f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, -8, 0, -8, 0, -8, 0, -8, /* 1f50-1f5f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f60-1f6f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1f70-1f7f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f80-1f8f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1f90-1f9f */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -8, -8, -8, -8, -8, -8, /* 1fa0-1faf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -74, -74, -9, 0, 0, 0, /* 1fb0-1fbf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -86, -86, -86, -86, -9, 0,
|
||||
0, 0, /* 1fc0-1fcf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -100, -100, 0, 0, 0, 0, /* 1fd0-1fdf */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, -8, -8, -112, -112, -7, 0,
|
||||
0, 0, /* 1fe0-1fef */
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
/* Lower case range - Wide latin */
|
||||
static signed char UniCaseRangeLff20[27] = {
|
||||
0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
32, /* ff20-ff2f */
|
||||
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
|
||||
};
|
||||
|
||||
/*
|
||||
* Lower Case Range
|
||||
*/
|
||||
const struct UniCaseRange CifsUniLowerRange[] = {
|
||||
{0x0380, 0x03ab, UniCaseRangeL0380},
|
||||
{0x0400, 0x042f, UniCaseRangeL0400},
|
||||
{0x0490, 0x04cb, UniCaseRangeL0490},
|
||||
{0x1e00, 0x1ff7, UniCaseRangeL1e00},
|
||||
{0xff20, 0xff3a, UniCaseRangeLff20},
|
||||
{0}
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif /* __KSMBD_UNIUPR_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,197 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2018 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __KSMBD_VFS_H__
|
||||
#define __KSMBD_VFS_H__
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <uapi/linux/xattr.h>
|
||||
#include <linux/posix_acl.h>
|
||||
|
||||
#include "smbacl.h"
|
||||
#include "xattr.h"
|
||||
|
||||
/*
|
||||
* Enumeration for stream type.
|
||||
*/
|
||||
enum {
|
||||
DATA_STREAM = 1, /* type $DATA */
|
||||
DIR_STREAM /* type $INDEX_ALLOCATION */
|
||||
};
|
||||
|
||||
/* CreateOptions */
|
||||
/* Flag is set, it must not be a file , valid for directory only */
|
||||
#define FILE_DIRECTORY_FILE_LE cpu_to_le32(0x00000001)
|
||||
#define FILE_WRITE_THROUGH_LE cpu_to_le32(0x00000002)
|
||||
#define FILE_SEQUENTIAL_ONLY_LE cpu_to_le32(0x00000004)
|
||||
|
||||
/* Should not buffer on server*/
|
||||
#define FILE_NO_INTERMEDIATE_BUFFERING_LE cpu_to_le32(0x00000008)
|
||||
/* MBZ */
|
||||
#define FILE_SYNCHRONOUS_IO_ALERT_LE cpu_to_le32(0x00000010)
|
||||
/* MBZ */
|
||||
#define FILE_SYNCHRONOUS_IO_NONALERT_LE cpu_to_le32(0x00000020)
|
||||
|
||||
/* Flaf must not be set for directory */
|
||||
#define FILE_NON_DIRECTORY_FILE_LE cpu_to_le32(0x00000040)
|
||||
|
||||
/* Should be zero */
|
||||
#define CREATE_TREE_CONNECTION cpu_to_le32(0x00000080)
|
||||
#define FILE_COMPLETE_IF_OPLOCKED_LE cpu_to_le32(0x00000100)
|
||||
#define FILE_NO_EA_KNOWLEDGE_LE cpu_to_le32(0x00000200)
|
||||
#define FILE_OPEN_REMOTE_INSTANCE cpu_to_le32(0x00000400)
|
||||
|
||||
/**
|
||||
* Doc says this is obsolete "open for recovery" flag should be zero
|
||||
* in any case.
|
||||
*/
|
||||
#define CREATE_OPEN_FOR_RECOVERY cpu_to_le32(0x00000400)
|
||||
#define FILE_RANDOM_ACCESS_LE cpu_to_le32(0x00000800)
|
||||
#define FILE_DELETE_ON_CLOSE_LE cpu_to_le32(0x00001000)
|
||||
#define FILE_OPEN_BY_FILE_ID_LE cpu_to_le32(0x00002000)
|
||||
#define FILE_OPEN_FOR_BACKUP_INTENT_LE cpu_to_le32(0x00004000)
|
||||
#define FILE_NO_COMPRESSION_LE cpu_to_le32(0x00008000)
|
||||
|
||||
/* Should be zero*/
|
||||
#define FILE_OPEN_REQUIRING_OPLOCK cpu_to_le32(0x00010000)
|
||||
#define FILE_DISALLOW_EXCLUSIVE cpu_to_le32(0x00020000)
|
||||
#define FILE_RESERVE_OPFILTER_LE cpu_to_le32(0x00100000)
|
||||
#define FILE_OPEN_REPARSE_POINT_LE cpu_to_le32(0x00200000)
|
||||
#define FILE_OPEN_NO_RECALL_LE cpu_to_le32(0x00400000)
|
||||
|
||||
/* Should be zero */
|
||||
#define FILE_OPEN_FOR_FREE_SPACE_QUERY_LE cpu_to_le32(0x00800000)
|
||||
#define CREATE_OPTIONS_MASK cpu_to_le32(0x00FFFFFF)
|
||||
#define CREATE_OPTION_READONLY 0x10000000
|
||||
/* system. NB not sent over wire */
|
||||
#define CREATE_OPTION_SPECIAL 0x20000000
|
||||
|
||||
struct ksmbd_work;
|
||||
struct ksmbd_file;
|
||||
struct ksmbd_conn;
|
||||
|
||||
struct ksmbd_dir_info {
|
||||
const char *name;
|
||||
char *wptr;
|
||||
char *rptr;
|
||||
int name_len;
|
||||
int out_buf_len;
|
||||
int num_entry;
|
||||
int data_count;
|
||||
int last_entry_offset;
|
||||
bool hide_dot_file;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct ksmbd_readdir_data {
|
||||
struct dir_context ctx;
|
||||
union {
|
||||
void *private;
|
||||
char *dirent;
|
||||
};
|
||||
|
||||
unsigned int used;
|
||||
unsigned int dirent_count;
|
||||
unsigned int file_attr;
|
||||
};
|
||||
|
||||
/* ksmbd kstat wrapper to get valid create time when reading dir entry */
|
||||
struct ksmbd_kstat {
|
||||
struct kstat *kstat;
|
||||
unsigned long long create_time;
|
||||
__le32 file_attributes;
|
||||
};
|
||||
|
||||
int ksmbd_vfs_lock_parent(struct dentry *parent, struct dentry *child);
|
||||
int ksmbd_vfs_may_delete(struct user_namespace *user_ns, struct dentry *dentry);
|
||||
int ksmbd_vfs_query_maximal_access(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, __le32 *daccess);
|
||||
int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode);
|
||||
int ksmbd_vfs_mkdir(struct ksmbd_work *work, const char *name, umode_t mode);
|
||||
int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
size_t count, loff_t *pos);
|
||||
int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *buf, size_t count, loff_t *pos, bool sync,
|
||||
ssize_t *written);
|
||||
int ksmbd_vfs_fsync(struct ksmbd_work *work, u64 fid, u64 p_id);
|
||||
int ksmbd_vfs_remove_file(struct ksmbd_work *work, char *name);
|
||||
int ksmbd_vfs_link(struct ksmbd_work *work,
|
||||
const char *oldname, const char *newname);
|
||||
int ksmbd_vfs_getattr(struct path *path, struct kstat *stat);
|
||||
int ksmbd_vfs_fp_rename(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
char *newname);
|
||||
int ksmbd_vfs_truncate(struct ksmbd_work *work, const char *name,
|
||||
struct ksmbd_file *fp, loff_t size);
|
||||
struct srv_copychunk;
|
||||
int ksmbd_vfs_copy_file_ranges(struct ksmbd_work *work,
|
||||
struct ksmbd_file *src_fp,
|
||||
struct ksmbd_file *dst_fp,
|
||||
struct srv_copychunk *chunks,
|
||||
unsigned int chunk_count,
|
||||
unsigned int *chunk_count_written,
|
||||
unsigned int *chunk_size_written,
|
||||
loff_t *total_size_written);
|
||||
ssize_t ksmbd_vfs_listxattr(struct dentry *dentry, char **list);
|
||||
ssize_t ksmbd_vfs_getxattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
char *xattr_name,
|
||||
char **xattr_buf);
|
||||
ssize_t ksmbd_vfs_casexattr_len(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, char *attr_name,
|
||||
int attr_name_len);
|
||||
int ksmbd_vfs_setxattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, const char *attr_name,
|
||||
const void *attr_value, size_t attr_size, int flags);
|
||||
int ksmbd_vfs_xattr_stream_name(char *stream_name, char **xattr_stream_name,
|
||||
size_t *xattr_stream_name_size, int s_type);
|
||||
int ksmbd_vfs_remove_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry, char *attr_name);
|
||||
int ksmbd_vfs_kern_path(char *name, unsigned int flags, struct path *path,
|
||||
bool caseless);
|
||||
int ksmbd_vfs_empty_dir(struct ksmbd_file *fp);
|
||||
void ksmbd_vfs_set_fadvise(struct file *filp, __le32 option);
|
||||
int ksmbd_vfs_zero_data(struct ksmbd_work *work, struct ksmbd_file *fp,
|
||||
loff_t off, loff_t len);
|
||||
struct file_allocated_range_buffer;
|
||||
int ksmbd_vfs_fqar_lseek(struct ksmbd_file *fp, loff_t start, loff_t length,
|
||||
struct file_allocated_range_buffer *ranges,
|
||||
int in_count, int *out_count);
|
||||
int ksmbd_vfs_unlink(struct user_namespace *user_ns,
|
||||
struct dentry *dir, struct dentry *dentry);
|
||||
void *ksmbd_vfs_init_kstat(char **p, struct ksmbd_kstat *ksmbd_kstat);
|
||||
int ksmbd_vfs_fill_dentry_attrs(struct ksmbd_work *work,
|
||||
struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct ksmbd_kstat *ksmbd_kstat);
|
||||
void ksmbd_vfs_posix_lock_wait(struct file_lock *flock);
|
||||
int ksmbd_vfs_posix_lock_wait_timeout(struct file_lock *flock, long timeout);
|
||||
void ksmbd_vfs_posix_lock_unblock(struct file_lock *flock);
|
||||
int ksmbd_vfs_remove_acl_xattrs(struct user_namespace *user_ns,
|
||||
struct dentry *dentry);
|
||||
int ksmbd_vfs_remove_sd_xattrs(struct user_namespace *user_ns,
|
||||
struct dentry *dentry);
|
||||
int ksmbd_vfs_set_sd_xattr(struct ksmbd_conn *conn,
|
||||
struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct smb_ntsd *pntsd, int len);
|
||||
int ksmbd_vfs_get_sd_xattr(struct ksmbd_conn *conn,
|
||||
struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct smb_ntsd **pntsd);
|
||||
int ksmbd_vfs_set_dos_attrib_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct xattr_dos_attrib *da);
|
||||
int ksmbd_vfs_get_dos_attrib_xattr(struct user_namespace *user_ns,
|
||||
struct dentry *dentry,
|
||||
struct xattr_dos_attrib *da);
|
||||
int ksmbd_vfs_set_init_posix_acl(struct user_namespace *user_ns,
|
||||
struct inode *inode);
|
||||
int ksmbd_vfs_inherit_posix_acl(struct user_namespace *user_ns,
|
||||
struct inode *inode,
|
||||
struct inode *parent_inode);
|
||||
#endif /* __KSMBD_VFS_H__ */
|
|
@ -0,0 +1,725 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2016 Namjae Jeon <linkinjeon@kernel.org>
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "glob.h"
|
||||
#include "vfs_cache.h"
|
||||
#include "oplock.h"
|
||||
#include "vfs.h"
|
||||
#include "connection.h"
|
||||
#include "mgmt/tree_connect.h"
|
||||
#include "mgmt/user_session.h"
|
||||
#include "smb_common.h"
|
||||
|
||||
#define S_DEL_PENDING 1
|
||||
#define S_DEL_ON_CLS 2
|
||||
#define S_DEL_ON_CLS_STREAM 8
|
||||
|
||||
static unsigned int inode_hash_mask __read_mostly;
|
||||
static unsigned int inode_hash_shift __read_mostly;
|
||||
static struct hlist_head *inode_hashtable __read_mostly;
|
||||
static DEFINE_RWLOCK(inode_hash_lock);
|
||||
|
||||
static struct ksmbd_file_table global_ft;
|
||||
static atomic_long_t fd_limit;
|
||||
static struct kmem_cache *filp_cache;
|
||||
|
||||
void ksmbd_set_fd_limit(unsigned long limit)
|
||||
{
|
||||
limit = min(limit, get_max_files());
|
||||
atomic_long_set(&fd_limit, limit);
|
||||
}
|
||||
|
||||
static bool fd_limit_depleted(void)
|
||||
{
|
||||
long v = atomic_long_dec_return(&fd_limit);
|
||||
|
||||
if (v >= 0)
|
||||
return false;
|
||||
atomic_long_inc(&fd_limit);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void fd_limit_close(void)
|
||||
{
|
||||
atomic_long_inc(&fd_limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* INODE hash
|
||||
*/
|
||||
|
||||
static unsigned long inode_hash(struct super_block *sb, unsigned long hashval)
|
||||
{
|
||||
unsigned long tmp;
|
||||
|
||||
tmp = (hashval * (unsigned long)sb) ^ (GOLDEN_RATIO_PRIME + hashval) /
|
||||
L1_CACHE_BYTES;
|
||||
tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> inode_hash_shift);
|
||||
return tmp & inode_hash_mask;
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *__ksmbd_inode_lookup(struct inode *inode)
|
||||
{
|
||||
struct hlist_head *head = inode_hashtable +
|
||||
inode_hash(inode->i_sb, inode->i_ino);
|
||||
struct ksmbd_inode *ci = NULL, *ret_ci = NULL;
|
||||
|
||||
hlist_for_each_entry(ci, head, m_hash) {
|
||||
if (ci->m_inode == inode) {
|
||||
if (atomic_inc_not_zero(&ci->m_count))
|
||||
ret_ci = ci;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret_ci;
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *ksmbd_inode_lookup(struct ksmbd_file *fp)
|
||||
{
|
||||
return __ksmbd_inode_lookup(file_inode(fp->filp));
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *ksmbd_inode_lookup_by_vfsinode(struct inode *inode)
|
||||
{
|
||||
struct ksmbd_inode *ci;
|
||||
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = __ksmbd_inode_lookup(inode);
|
||||
read_unlock(&inode_hash_lock);
|
||||
return ci;
|
||||
}
|
||||
|
||||
int ksmbd_query_inode_status(struct inode *inode)
|
||||
{
|
||||
struct ksmbd_inode *ci;
|
||||
int ret = KSMBD_INODE_STATUS_UNKNOWN;
|
||||
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = __ksmbd_inode_lookup(inode);
|
||||
if (ci) {
|
||||
ret = KSMBD_INODE_STATUS_OK;
|
||||
if (ci->m_flags & S_DEL_PENDING)
|
||||
ret = KSMBD_INODE_STATUS_PENDING_DELETE;
|
||||
atomic_dec(&ci->m_count);
|
||||
}
|
||||
read_unlock(&inode_hash_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp)
|
||||
{
|
||||
return (fp->f_ci->m_flags & S_DEL_PENDING);
|
||||
}
|
||||
|
||||
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp)
|
||||
{
|
||||
fp->f_ci->m_flags |= S_DEL_PENDING;
|
||||
}
|
||||
|
||||
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp)
|
||||
{
|
||||
fp->f_ci->m_flags &= ~S_DEL_PENDING;
|
||||
}
|
||||
|
||||
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
|
||||
int file_info)
|
||||
{
|
||||
if (ksmbd_stream_fd(fp)) {
|
||||
fp->f_ci->m_flags |= S_DEL_ON_CLS_STREAM;
|
||||
return;
|
||||
}
|
||||
|
||||
fp->f_ci->m_flags |= S_DEL_ON_CLS;
|
||||
}
|
||||
|
||||
static void ksmbd_inode_hash(struct ksmbd_inode *ci)
|
||||
{
|
||||
struct hlist_head *b = inode_hashtable +
|
||||
inode_hash(ci->m_inode->i_sb, ci->m_inode->i_ino);
|
||||
|
||||
hlist_add_head(&ci->m_hash, b);
|
||||
}
|
||||
|
||||
static void ksmbd_inode_unhash(struct ksmbd_inode *ci)
|
||||
{
|
||||
write_lock(&inode_hash_lock);
|
||||
hlist_del_init(&ci->m_hash);
|
||||
write_unlock(&inode_hash_lock);
|
||||
}
|
||||
|
||||
static int ksmbd_inode_init(struct ksmbd_inode *ci, struct ksmbd_file *fp)
|
||||
{
|
||||
ci->m_inode = file_inode(fp->filp);
|
||||
atomic_set(&ci->m_count, 1);
|
||||
atomic_set(&ci->op_count, 0);
|
||||
atomic_set(&ci->sop_count, 0);
|
||||
ci->m_flags = 0;
|
||||
ci->m_fattr = 0;
|
||||
INIT_LIST_HEAD(&ci->m_fp_list);
|
||||
INIT_LIST_HEAD(&ci->m_op_list);
|
||||
rwlock_init(&ci->m_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ksmbd_inode *ksmbd_inode_get(struct ksmbd_file *fp)
|
||||
{
|
||||
struct ksmbd_inode *ci, *tmpci;
|
||||
int rc;
|
||||
|
||||
read_lock(&inode_hash_lock);
|
||||
ci = ksmbd_inode_lookup(fp);
|
||||
read_unlock(&inode_hash_lock);
|
||||
if (ci)
|
||||
return ci;
|
||||
|
||||
ci = kmalloc(sizeof(struct ksmbd_inode), GFP_KERNEL);
|
||||
if (!ci)
|
||||
return NULL;
|
||||
|
||||
rc = ksmbd_inode_init(ci, fp);
|
||||
if (rc) {
|
||||
pr_err("inode initialized failed\n");
|
||||
kfree(ci);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
write_lock(&inode_hash_lock);
|
||||
tmpci = ksmbd_inode_lookup(fp);
|
||||
if (!tmpci) {
|
||||
ksmbd_inode_hash(ci);
|
||||
} else {
|
||||
kfree(ci);
|
||||
ci = tmpci;
|
||||
}
|
||||
write_unlock(&inode_hash_lock);
|
||||
return ci;
|
||||
}
|
||||
|
||||
static void ksmbd_inode_free(struct ksmbd_inode *ci)
|
||||
{
|
||||
ksmbd_inode_unhash(ci);
|
||||
kfree(ci);
|
||||
}
|
||||
|
||||
static void ksmbd_inode_put(struct ksmbd_inode *ci)
|
||||
{
|
||||
if (atomic_dec_and_test(&ci->m_count))
|
||||
ksmbd_inode_free(ci);
|
||||
}
|
||||
|
||||
int __init ksmbd_inode_hash_init(void)
|
||||
{
|
||||
unsigned int loop;
|
||||
unsigned long numentries = 16384;
|
||||
unsigned long bucketsize = sizeof(struct hlist_head);
|
||||
unsigned long size;
|
||||
|
||||
inode_hash_shift = ilog2(numentries);
|
||||
inode_hash_mask = (1 << inode_hash_shift) - 1;
|
||||
|
||||
size = bucketsize << inode_hash_shift;
|
||||
|
||||
/* init master fp hash table */
|
||||
inode_hashtable = vmalloc(size);
|
||||
if (!inode_hashtable)
|
||||
return -ENOMEM;
|
||||
|
||||
for (loop = 0; loop < (1U << inode_hash_shift); loop++)
|
||||
INIT_HLIST_HEAD(&inode_hashtable[loop]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_release_inode_hash(void)
|
||||
{
|
||||
vfree(inode_hashtable);
|
||||
}
|
||||
|
||||
static void __ksmbd_inode_close(struct ksmbd_file *fp)
|
||||
{
|
||||
struct dentry *dir, *dentry;
|
||||
struct ksmbd_inode *ci = fp->f_ci;
|
||||
int err;
|
||||
struct file *filp;
|
||||
|
||||
filp = fp->filp;
|
||||
if (ksmbd_stream_fd(fp) && (ci->m_flags & S_DEL_ON_CLS_STREAM)) {
|
||||
ci->m_flags &= ~S_DEL_ON_CLS_STREAM;
|
||||
err = ksmbd_vfs_remove_xattr(file_mnt_user_ns(filp),
|
||||
filp->f_path.dentry,
|
||||
fp->stream.name);
|
||||
if (err)
|
||||
pr_err("remove xattr failed : %s\n",
|
||||
fp->stream.name);
|
||||
}
|
||||
|
||||
if (atomic_dec_and_test(&ci->m_count)) {
|
||||
write_lock(&ci->m_lock);
|
||||
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
|
||||
dentry = filp->f_path.dentry;
|
||||
dir = dentry->d_parent;
|
||||
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
|
||||
write_unlock(&ci->m_lock);
|
||||
ksmbd_vfs_unlink(file_mnt_user_ns(filp), dir, dentry);
|
||||
write_lock(&ci->m_lock);
|
||||
}
|
||||
write_unlock(&ci->m_lock);
|
||||
|
||||
ksmbd_inode_free(ci);
|
||||
}
|
||||
}
|
||||
|
||||
static void __ksmbd_remove_durable_fd(struct ksmbd_file *fp)
|
||||
{
|
||||
if (!has_file_id(fp->persistent_id))
|
||||
return;
|
||||
|
||||
write_lock(&global_ft.lock);
|
||||
idr_remove(global_ft.idr, fp->persistent_id);
|
||||
write_unlock(&global_ft.lock);
|
||||
}
|
||||
|
||||
static void __ksmbd_remove_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
|
||||
{
|
||||
if (!has_file_id(fp->volatile_id))
|
||||
return;
|
||||
|
||||
write_lock(&fp->f_ci->m_lock);
|
||||
list_del_init(&fp->node);
|
||||
write_unlock(&fp->f_ci->m_lock);
|
||||
|
||||
write_lock(&ft->lock);
|
||||
idr_remove(ft->idr, fp->volatile_id);
|
||||
write_unlock(&ft->lock);
|
||||
}
|
||||
|
||||
static void __ksmbd_close_fd(struct ksmbd_file_table *ft, struct ksmbd_file *fp)
|
||||
{
|
||||
struct file *filp;
|
||||
struct ksmbd_lock *smb_lock, *tmp_lock;
|
||||
|
||||
fd_limit_close();
|
||||
__ksmbd_remove_durable_fd(fp);
|
||||
__ksmbd_remove_fd(ft, fp);
|
||||
|
||||
close_id_del_oplock(fp);
|
||||
filp = fp->filp;
|
||||
|
||||
__ksmbd_inode_close(fp);
|
||||
if (!IS_ERR_OR_NULL(filp))
|
||||
fput(filp);
|
||||
|
||||
/* because the reference count of fp is 0, it is guaranteed that
|
||||
* there are not accesses to fp->lock_list.
|
||||
*/
|
||||
list_for_each_entry_safe(smb_lock, tmp_lock, &fp->lock_list, flist) {
|
||||
spin_lock(&fp->conn->llist_lock);
|
||||
list_del(&smb_lock->clist);
|
||||
spin_unlock(&fp->conn->llist_lock);
|
||||
|
||||
list_del(&smb_lock->flist);
|
||||
locks_free_lock(smb_lock->fl);
|
||||
kfree(smb_lock);
|
||||
}
|
||||
|
||||
kfree(fp->filename);
|
||||
if (ksmbd_stream_fd(fp))
|
||||
kfree(fp->stream.name);
|
||||
kmem_cache_free(filp_cache, fp);
|
||||
}
|
||||
|
||||
static struct ksmbd_file *ksmbd_fp_get(struct ksmbd_file *fp)
|
||||
{
|
||||
if (!atomic_inc_not_zero(&fp->refcount))
|
||||
return NULL;
|
||||
return fp;
|
||||
}
|
||||
|
||||
static struct ksmbd_file *__ksmbd_lookup_fd(struct ksmbd_file_table *ft,
|
||||
u64 id)
|
||||
{
|
||||
struct ksmbd_file *fp;
|
||||
|
||||
if (!has_file_id(id))
|
||||
return NULL;
|
||||
|
||||
read_lock(&ft->lock);
|
||||
fp = idr_find(ft->idr, id);
|
||||
if (fp)
|
||||
fp = ksmbd_fp_get(fp);
|
||||
read_unlock(&ft->lock);
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void __put_fd_final(struct ksmbd_work *work, struct ksmbd_file *fp)
|
||||
{
|
||||
__ksmbd_close_fd(&work->sess->file_table, fp);
|
||||
atomic_dec(&work->conn->stats.open_files_count);
|
||||
}
|
||||
|
||||
static void set_close_state_blocked_works(struct ksmbd_file *fp)
|
||||
{
|
||||
struct ksmbd_work *cancel_work, *ctmp;
|
||||
|
||||
spin_lock(&fp->f_lock);
|
||||
list_for_each_entry_safe(cancel_work, ctmp, &fp->blocked_works,
|
||||
fp_entry) {
|
||||
list_del(&cancel_work->fp_entry);
|
||||
cancel_work->state = KSMBD_WORK_CLOSED;
|
||||
cancel_work->cancel_fn(cancel_work->cancel_argv);
|
||||
}
|
||||
spin_unlock(&fp->f_lock);
|
||||
}
|
||||
|
||||
int ksmbd_close_fd(struct ksmbd_work *work, u64 id)
|
||||
{
|
||||
struct ksmbd_file *fp;
|
||||
struct ksmbd_file_table *ft;
|
||||
|
||||
if (!has_file_id(id))
|
||||
return 0;
|
||||
|
||||
ft = &work->sess->file_table;
|
||||
read_lock(&ft->lock);
|
||||
fp = idr_find(ft->idr, id);
|
||||
if (fp) {
|
||||
set_close_state_blocked_works(fp);
|
||||
|
||||
if (!atomic_dec_and_test(&fp->refcount))
|
||||
fp = NULL;
|
||||
}
|
||||
read_unlock(&ft->lock);
|
||||
|
||||
if (!fp)
|
||||
return -EINVAL;
|
||||
|
||||
__put_fd_final(work, fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp)
|
||||
{
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
if (!atomic_dec_and_test(&fp->refcount))
|
||||
return;
|
||||
__put_fd_final(work, fp);
|
||||
}
|
||||
|
||||
static bool __sanity_check(struct ksmbd_tree_connect *tcon, struct ksmbd_file *fp)
|
||||
{
|
||||
if (!fp)
|
||||
return false;
|
||||
if (fp->tcon != tcon)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id)
|
||||
{
|
||||
return __ksmbd_lookup_fd(&work->sess->file_table, id);
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id)
|
||||
{
|
||||
struct ksmbd_file *fp = __ksmbd_lookup_fd(&work->sess->file_table, id);
|
||||
|
||||
if (__sanity_check(work->tcon, fp))
|
||||
return fp;
|
||||
|
||||
ksmbd_fd_put(work, fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
|
||||
u64 pid)
|
||||
{
|
||||
struct ksmbd_file *fp;
|
||||
|
||||
if (!has_file_id(id)) {
|
||||
id = work->compound_fid;
|
||||
pid = work->compound_pfid;
|
||||
}
|
||||
|
||||
fp = __ksmbd_lookup_fd(&work->sess->file_table, id);
|
||||
if (!__sanity_check(work->tcon, fp)) {
|
||||
ksmbd_fd_put(work, fp);
|
||||
return NULL;
|
||||
}
|
||||
if (fp->persistent_id != pid) {
|
||||
ksmbd_fd_put(work, fp);
|
||||
return NULL;
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id)
|
||||
{
|
||||
return __ksmbd_lookup_fd(&global_ft, id);
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid)
|
||||
{
|
||||
struct ksmbd_file *fp = NULL;
|
||||
unsigned int id;
|
||||
|
||||
read_lock(&global_ft.lock);
|
||||
idr_for_each_entry(global_ft.idr, fp, id) {
|
||||
if (!memcmp(fp->create_guid,
|
||||
cguid,
|
||||
SMB2_CREATE_GUID_SIZE)) {
|
||||
fp = ksmbd_fp_get(fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
read_unlock(&global_ft.lock);
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode)
|
||||
{
|
||||
struct ksmbd_file *lfp;
|
||||
struct ksmbd_inode *ci;
|
||||
|
||||
ci = ksmbd_inode_lookup_by_vfsinode(inode);
|
||||
if (!ci)
|
||||
return NULL;
|
||||
|
||||
read_lock(&ci->m_lock);
|
||||
list_for_each_entry(lfp, &ci->m_fp_list, node) {
|
||||
if (inode == file_inode(lfp->filp)) {
|
||||
atomic_dec(&ci->m_count);
|
||||
read_unlock(&ci->m_lock);
|
||||
return lfp;
|
||||
}
|
||||
}
|
||||
atomic_dec(&ci->m_count);
|
||||
read_unlock(&ci->m_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define OPEN_ID_TYPE_VOLATILE_ID (0)
|
||||
#define OPEN_ID_TYPE_PERSISTENT_ID (1)
|
||||
|
||||
static void __open_id_set(struct ksmbd_file *fp, u64 id, int type)
|
||||
{
|
||||
if (type == OPEN_ID_TYPE_VOLATILE_ID)
|
||||
fp->volatile_id = id;
|
||||
if (type == OPEN_ID_TYPE_PERSISTENT_ID)
|
||||
fp->persistent_id = id;
|
||||
}
|
||||
|
||||
static int __open_id(struct ksmbd_file_table *ft, struct ksmbd_file *fp,
|
||||
int type)
|
||||
{
|
||||
u64 id = 0;
|
||||
int ret;
|
||||
|
||||
if (type == OPEN_ID_TYPE_VOLATILE_ID && fd_limit_depleted()) {
|
||||
__open_id_set(fp, KSMBD_NO_FID, type);
|
||||
return -EMFILE;
|
||||
}
|
||||
|
||||
idr_preload(GFP_KERNEL);
|
||||
write_lock(&ft->lock);
|
||||
ret = idr_alloc_cyclic(ft->idr, fp, 0, INT_MAX - 1, GFP_NOWAIT);
|
||||
if (ret >= 0) {
|
||||
id = ret;
|
||||
ret = 0;
|
||||
} else {
|
||||
id = KSMBD_NO_FID;
|
||||
fd_limit_close();
|
||||
}
|
||||
|
||||
__open_id_set(fp, id, type);
|
||||
write_unlock(&ft->lock);
|
||||
idr_preload_end();
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp)
|
||||
{
|
||||
__open_id(&global_ft, fp, OPEN_ID_TYPE_PERSISTENT_ID);
|
||||
return fp->persistent_id;
|
||||
}
|
||||
|
||||
struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp)
|
||||
{
|
||||
struct ksmbd_file *fp;
|
||||
int ret;
|
||||
|
||||
fp = kmem_cache_zalloc(filp_cache, GFP_KERNEL);
|
||||
if (!fp) {
|
||||
pr_err("Failed to allocate memory\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&fp->blocked_works);
|
||||
INIT_LIST_HEAD(&fp->node);
|
||||
INIT_LIST_HEAD(&fp->lock_list);
|
||||
spin_lock_init(&fp->f_lock);
|
||||
atomic_set(&fp->refcount, 1);
|
||||
|
||||
fp->filp = filp;
|
||||
fp->conn = work->sess->conn;
|
||||
fp->tcon = work->tcon;
|
||||
fp->volatile_id = KSMBD_NO_FID;
|
||||
fp->persistent_id = KSMBD_NO_FID;
|
||||
fp->f_ci = ksmbd_inode_get(fp);
|
||||
|
||||
if (!fp->f_ci) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
ret = __open_id(&work->sess->file_table, fp, OPEN_ID_TYPE_VOLATILE_ID);
|
||||
if (ret) {
|
||||
ksmbd_inode_put(fp->f_ci);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
atomic_inc(&work->conn->stats.open_files_count);
|
||||
return fp;
|
||||
|
||||
err_out:
|
||||
kmem_cache_free(filp_cache, fp);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
__close_file_table_ids(struct ksmbd_file_table *ft,
|
||||
struct ksmbd_tree_connect *tcon,
|
||||
bool (*skip)(struct ksmbd_tree_connect *tcon,
|
||||
struct ksmbd_file *fp))
|
||||
{
|
||||
unsigned int id;
|
||||
struct ksmbd_file *fp;
|
||||
int num = 0;
|
||||
|
||||
idr_for_each_entry(ft->idr, fp, id) {
|
||||
if (skip(tcon, fp))
|
||||
continue;
|
||||
|
||||
set_close_state_blocked_works(fp);
|
||||
|
||||
if (!atomic_dec_and_test(&fp->refcount))
|
||||
continue;
|
||||
__ksmbd_close_fd(ft, fp);
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static bool tree_conn_fd_check(struct ksmbd_tree_connect *tcon,
|
||||
struct ksmbd_file *fp)
|
||||
{
|
||||
return fp->tcon != tcon;
|
||||
}
|
||||
|
||||
static bool session_fd_check(struct ksmbd_tree_connect *tcon,
|
||||
struct ksmbd_file *fp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work)
|
||||
{
|
||||
int num = __close_file_table_ids(&work->sess->file_table,
|
||||
work->tcon,
|
||||
tree_conn_fd_check);
|
||||
|
||||
atomic_sub(num, &work->conn->stats.open_files_count);
|
||||
}
|
||||
|
||||
void ksmbd_close_session_fds(struct ksmbd_work *work)
|
||||
{
|
||||
int num = __close_file_table_ids(&work->sess->file_table,
|
||||
work->tcon,
|
||||
session_fd_check);
|
||||
|
||||
atomic_sub(num, &work->conn->stats.open_files_count);
|
||||
}
|
||||
|
||||
int ksmbd_init_global_file_table(void)
|
||||
{
|
||||
return ksmbd_init_file_table(&global_ft);
|
||||
}
|
||||
|
||||
void ksmbd_free_global_file_table(void)
|
||||
{
|
||||
struct ksmbd_file *fp = NULL;
|
||||
unsigned int id;
|
||||
|
||||
idr_for_each_entry(global_ft.idr, fp, id) {
|
||||
__ksmbd_remove_durable_fd(fp);
|
||||
kmem_cache_free(filp_cache, fp);
|
||||
}
|
||||
|
||||
ksmbd_destroy_file_table(&global_ft);
|
||||
}
|
||||
|
||||
int ksmbd_file_table_flush(struct ksmbd_work *work)
|
||||
{
|
||||
struct ksmbd_file *fp = NULL;
|
||||
unsigned int id;
|
||||
int ret;
|
||||
|
||||
read_lock(&work->sess->file_table.lock);
|
||||
idr_for_each_entry(work->sess->file_table.idr, fp, id) {
|
||||
ret = ksmbd_vfs_fsync(work, fp->volatile_id, KSMBD_NO_FID);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
read_unlock(&work->sess->file_table.lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ksmbd_init_file_table(struct ksmbd_file_table *ft)
|
||||
{
|
||||
ft->idr = kzalloc(sizeof(struct idr), GFP_KERNEL);
|
||||
if (!ft->idr)
|
||||
return -ENOMEM;
|
||||
|
||||
idr_init(ft->idr);
|
||||
rwlock_init(&ft->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksmbd_destroy_file_table(struct ksmbd_file_table *ft)
|
||||
{
|
||||
if (!ft->idr)
|
||||
return;
|
||||
|
||||
__close_file_table_ids(ft, NULL, session_fd_check);
|
||||
idr_destroy(ft->idr);
|
||||
kfree(ft->idr);
|
||||
ft->idr = NULL;
|
||||
}
|
||||
|
||||
int ksmbd_init_file_cache(void)
|
||||
{
|
||||
filp_cache = kmem_cache_create("ksmbd_file_cache",
|
||||
sizeof(struct ksmbd_file), 0,
|
||||
SLAB_HWCACHE_ALIGN, NULL);
|
||||
if (!filp_cache)
|
||||
goto out;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
pr_err("failed to allocate file cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void ksmbd_exit_file_cache(void)
|
||||
{
|
||||
kmem_cache_destroy(filp_cache);
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2019 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __VFS_CACHE_H__
|
||||
#define __VFS_CACHE_H__
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
/* Windows style file permissions for extended response */
|
||||
#define FILE_GENERIC_ALL 0x1F01FF
|
||||
#define FILE_GENERIC_READ 0x120089
|
||||
#define FILE_GENERIC_WRITE 0x120116
|
||||
#define FILE_GENERIC_EXECUTE 0X1200a0
|
||||
|
||||
#define KSMBD_START_FID 0
|
||||
#define KSMBD_NO_FID (INT_MAX)
|
||||
#define SMB2_NO_FID (0xFFFFFFFFFFFFFFFFULL)
|
||||
|
||||
struct ksmbd_conn;
|
||||
struct ksmbd_session;
|
||||
|
||||
struct ksmbd_lock {
|
||||
struct file_lock *fl;
|
||||
struct list_head clist;
|
||||
struct list_head flist;
|
||||
struct list_head llist;
|
||||
unsigned int flags;
|
||||
int cmd;
|
||||
int zero_len;
|
||||
unsigned long long start;
|
||||
unsigned long long end;
|
||||
};
|
||||
|
||||
struct stream {
|
||||
char *name;
|
||||
ssize_t size;
|
||||
};
|
||||
|
||||
struct ksmbd_inode {
|
||||
rwlock_t m_lock;
|
||||
atomic_t m_count;
|
||||
atomic_t op_count;
|
||||
/* opinfo count for streams */
|
||||
atomic_t sop_count;
|
||||
struct inode *m_inode;
|
||||
unsigned int m_flags;
|
||||
struct hlist_node m_hash;
|
||||
struct list_head m_fp_list;
|
||||
struct list_head m_op_list;
|
||||
struct oplock_info *m_opinfo;
|
||||
__le32 m_fattr;
|
||||
};
|
||||
|
||||
struct ksmbd_file {
|
||||
struct file *filp;
|
||||
char *filename;
|
||||
u64 persistent_id;
|
||||
u64 volatile_id;
|
||||
|
||||
spinlock_t f_lock;
|
||||
|
||||
struct ksmbd_inode *f_ci;
|
||||
struct ksmbd_inode *f_parent_ci;
|
||||
struct oplock_info __rcu *f_opinfo;
|
||||
struct ksmbd_conn *conn;
|
||||
struct ksmbd_tree_connect *tcon;
|
||||
|
||||
atomic_t refcount;
|
||||
__le32 daccess;
|
||||
__le32 saccess;
|
||||
__le32 coption;
|
||||
__le32 cdoption;
|
||||
__u64 create_time;
|
||||
__u64 itime;
|
||||
|
||||
bool is_nt_open;
|
||||
bool attrib_only;
|
||||
|
||||
char client_guid[16];
|
||||
char create_guid[16];
|
||||
char app_instance_id[16];
|
||||
|
||||
struct stream stream;
|
||||
struct list_head node;
|
||||
struct list_head blocked_works;
|
||||
struct list_head lock_list;
|
||||
|
||||
int durable_timeout;
|
||||
|
||||
/* for SMB1 */
|
||||
int pid;
|
||||
|
||||
/* conflict lock fail count for SMB1 */
|
||||
unsigned int cflock_cnt;
|
||||
/* last lock failure start offset for SMB1 */
|
||||
unsigned long long llock_fstart;
|
||||
|
||||
int dirent_offset;
|
||||
|
||||
/* if ls is happening on directory, below is valid*/
|
||||
struct ksmbd_readdir_data readdir_data;
|
||||
int dot_dotdot[2];
|
||||
};
|
||||
|
||||
static inline void set_ctx_actor(struct dir_context *ctx,
|
||||
filldir_t actor)
|
||||
{
|
||||
ctx->actor = actor;
|
||||
}
|
||||
|
||||
#define KSMBD_NR_OPEN_DEFAULT BITS_PER_LONG
|
||||
|
||||
struct ksmbd_file_table {
|
||||
rwlock_t lock;
|
||||
struct idr *idr;
|
||||
};
|
||||
|
||||
static inline bool has_file_id(u64 id)
|
||||
{
|
||||
return id < KSMBD_NO_FID;
|
||||
}
|
||||
|
||||
static inline bool ksmbd_stream_fd(struct ksmbd_file *fp)
|
||||
{
|
||||
return fp->stream.name != NULL;
|
||||
}
|
||||
|
||||
int ksmbd_init_file_table(struct ksmbd_file_table *ft);
|
||||
void ksmbd_destroy_file_table(struct ksmbd_file_table *ft);
|
||||
int ksmbd_close_fd(struct ksmbd_work *work, u64 id);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_fast(struct ksmbd_work *work, u64 id);
|
||||
struct ksmbd_file *ksmbd_lookup_foreign_fd(struct ksmbd_work *work, u64 id);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_slow(struct ksmbd_work *work, u64 id,
|
||||
u64 pid);
|
||||
void ksmbd_fd_put(struct ksmbd_work *work, struct ksmbd_file *fp);
|
||||
struct ksmbd_file *ksmbd_lookup_durable_fd(unsigned long long id);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_cguid(char *cguid);
|
||||
struct ksmbd_file *ksmbd_lookup_fd_inode(struct inode *inode);
|
||||
unsigned int ksmbd_open_durable_fd(struct ksmbd_file *fp);
|
||||
struct ksmbd_file *ksmbd_open_fd(struct ksmbd_work *work, struct file *filp);
|
||||
void ksmbd_close_tree_conn_fds(struct ksmbd_work *work);
|
||||
void ksmbd_close_session_fds(struct ksmbd_work *work);
|
||||
int ksmbd_close_inode_fds(struct ksmbd_work *work, struct inode *inode);
|
||||
int ksmbd_init_global_file_table(void);
|
||||
void ksmbd_free_global_file_table(void);
|
||||
int ksmbd_file_table_flush(struct ksmbd_work *work);
|
||||
void ksmbd_set_fd_limit(unsigned long limit);
|
||||
|
||||
/*
|
||||
* INODE hash
|
||||
*/
|
||||
int __init ksmbd_inode_hash_init(void);
|
||||
void ksmbd_release_inode_hash(void);
|
||||
|
||||
enum KSMBD_INODE_STATUS {
|
||||
KSMBD_INODE_STATUS_OK,
|
||||
KSMBD_INODE_STATUS_UNKNOWN,
|
||||
KSMBD_INODE_STATUS_PENDING_DELETE,
|
||||
};
|
||||
|
||||
int ksmbd_query_inode_status(struct inode *inode);
|
||||
bool ksmbd_inode_pending_delete(struct ksmbd_file *fp);
|
||||
void ksmbd_set_inode_pending_delete(struct ksmbd_file *fp);
|
||||
void ksmbd_clear_inode_pending_delete(struct ksmbd_file *fp);
|
||||
void ksmbd_fd_set_delete_on_close(struct ksmbd_file *fp,
|
||||
int file_info);
|
||||
int ksmbd_init_file_cache(void);
|
||||
void ksmbd_exit_file_cache(void);
|
||||
#endif /* __VFS_CACHE_H__ */
|
|
@ -0,0 +1,122 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
/*
|
||||
* Copyright (C) 2021 Samsung Electronics Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef __XATTR_H__
|
||||
#define __XATTR_H__
|
||||
|
||||
/*
|
||||
* These are on-disk structures to store additional metadata into xattr to
|
||||
* reproduce windows filesystem semantics. And they are encoded with NDR to
|
||||
* compatible with samba's xattr meta format. The compatibility with samba
|
||||
* is important because it can lose the information(file attribute,
|
||||
* creation time, acls) about the existing files when switching between
|
||||
* ksmbd and samba.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Dos attribute flags used for what variable is valid.
|
||||
*/
|
||||
enum {
|
||||
XATTR_DOSINFO_ATTRIB = 0x00000001,
|
||||
XATTR_DOSINFO_EA_SIZE = 0x00000002,
|
||||
XATTR_DOSINFO_SIZE = 0x00000004,
|
||||
XATTR_DOSINFO_ALLOC_SIZE = 0x00000008,
|
||||
XATTR_DOSINFO_CREATE_TIME = 0x00000010,
|
||||
XATTR_DOSINFO_CHANGE_TIME = 0x00000020,
|
||||
XATTR_DOSINFO_ITIME = 0x00000040
|
||||
};
|
||||
|
||||
/*
|
||||
* Dos attribute structure which is compatible with samba's one.
|
||||
* Storing it into the xattr named "DOSATTRIB" separately from inode
|
||||
* allows ksmbd to faithfully reproduce windows filesystem semantics
|
||||
* on top of a POSIX filesystem.
|
||||
*/
|
||||
struct xattr_dos_attrib {
|
||||
__u16 version; /* version 3 or version 4 */
|
||||
__u32 flags; /* valid flags */
|
||||
__u32 attr; /* Dos attribute */
|
||||
__u32 ea_size; /* EA size */
|
||||
__u64 size;
|
||||
__u64 alloc_size;
|
||||
__u64 create_time; /* File creation time */
|
||||
__u64 change_time; /* File change time */
|
||||
__u64 itime; /* Invented/Initial time */
|
||||
};
|
||||
|
||||
/*
|
||||
* Enumeration is used for computing posix acl hash.
|
||||
*/
|
||||
enum {
|
||||
SMB_ACL_TAG_INVALID = 0,
|
||||
SMB_ACL_USER,
|
||||
SMB_ACL_USER_OBJ,
|
||||
SMB_ACL_GROUP,
|
||||
SMB_ACL_GROUP_OBJ,
|
||||
SMB_ACL_OTHER,
|
||||
SMB_ACL_MASK
|
||||
};
|
||||
|
||||
#define SMB_ACL_READ 4
|
||||
#define SMB_ACL_WRITE 2
|
||||
#define SMB_ACL_EXECUTE 1
|
||||
|
||||
struct xattr_acl_entry {
|
||||
int type;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
mode_t perm;
|
||||
};
|
||||
|
||||
/*
|
||||
* xattr_smb_acl structure is used for computing posix acl hash.
|
||||
*/
|
||||
struct xattr_smb_acl {
|
||||
int count;
|
||||
int next;
|
||||
struct xattr_acl_entry entries[0];
|
||||
};
|
||||
|
||||
/* 64bytes hash in xattr_ntacl is computed with sha256 */
|
||||
#define XATTR_SD_HASH_TYPE_SHA256 0x1
|
||||
#define XATTR_SD_HASH_SIZE 64
|
||||
|
||||
/*
|
||||
* xattr_ntacl is used for storing ntacl and hashes.
|
||||
* Hash is used for checking valid posix acl and ntacl in xattr.
|
||||
*/
|
||||
struct xattr_ntacl {
|
||||
__u16 version; /* version 4*/
|
||||
void *sd_buf;
|
||||
__u32 sd_size;
|
||||
__u16 hash_type; /* hash type */
|
||||
__u8 desc[10]; /* posix_acl description */
|
||||
__u16 desc_len;
|
||||
__u64 current_time;
|
||||
__u8 hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for ntacl */
|
||||
__u8 posix_acl_hash[XATTR_SD_HASH_SIZE]; /* 64bytes hash for posix acl */
|
||||
};
|
||||
|
||||
/* DOS ATTRIBUITE XATTR PREFIX */
|
||||
#define DOS_ATTRIBUTE_PREFIX "DOSATTRIB"
|
||||
#define DOS_ATTRIBUTE_PREFIX_LEN (sizeof(DOS_ATTRIBUTE_PREFIX) - 1)
|
||||
#define XATTR_NAME_DOS_ATTRIBUTE (XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX)
|
||||
#define XATTR_NAME_DOS_ATTRIBUTE_LEN \
|
||||
(sizeof(XATTR_USER_PREFIX DOS_ATTRIBUTE_PREFIX) - 1)
|
||||
|
||||
/* STREAM XATTR PREFIX */
|
||||
#define STREAM_PREFIX "DosStream."
|
||||
#define STREAM_PREFIX_LEN (sizeof(STREAM_PREFIX) - 1)
|
||||
#define XATTR_NAME_STREAM (XATTR_USER_PREFIX STREAM_PREFIX)
|
||||
#define XATTR_NAME_STREAM_LEN (sizeof(XATTR_NAME_STREAM) - 1)
|
||||
|
||||
/* SECURITY DESCRIPTOR(NTACL) XATTR PREFIX */
|
||||
#define SD_PREFIX "NTACL"
|
||||
#define SD_PREFIX_LEN (sizeof(SD_PREFIX) - 1)
|
||||
#define XATTR_NAME_SD (XATTR_SECURITY_PREFIX SD_PREFIX)
|
||||
#define XATTR_NAME_SD_LEN \
|
||||
(sizeof(XATTR_SECURITY_PREFIX SD_PREFIX) - 1)
|
||||
|
||||
#endif /* __XATTR_H__ */
|
Loading…
Reference in New Issue