Commit e24c567b authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents b91db6a0 7d5d8d71
===============================
CIFS
===============================
.. toctree::
:maxdepth: 1
ksmbd
cifsroot
.. 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
......
......@@ -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/
......
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
# 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
// 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;
}
/* 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__ */
This diff is collapsed.
/* 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
// 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);
}
/* 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__ */
// 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;
}
/* 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__ */
/* 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 */
This diff is collapsed.
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
}
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
}
// 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);
}
/* 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__ */
// 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);
}
/* 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__ */
// 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);
}
/* 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__ */
// 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;
}
/* 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__ */
// 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;
}
/* 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__ */
// 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);
}
/* 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__ */
This diff is collapsed.
/* 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__ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment