--- /dev/null
+From 6b74583bd62e2b6ed6b76ff72d8fc7c2d8f26510 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Atte=20Heikkil=C3=A4?= <atteh.mailbox@gmail.com>
+Date: Thu, 13 Oct 2022 20:08:47 +0300
+Subject: [PATCH] ksmbd-tools: build utilities as a single binary
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Rather than have four different binaries, i.e. ksmbd.addshare,
+ksmbd.adduser, ksmbd.control, and ksmbd.mountd, each one including
+its own libksmbdtools.a copy, build them instead as a single binary.
+This resulting ksmbd.tools acts like BusyBox in that its behavior
+depends on the name by which it is called. Then, each utility is made
+into a symlink to it, meaning that users keep using the utilities as
+usual. ksmbd.tools itself is installed to libexecdir as it is not
+something the user should run.
+
+Instead of libksmbdtools.a, each utility becomes its own static
+library, i.e. libaddshare.a, libadduser.a, libcontrol.a, libmountd.a,
+which are all linked to the single binary. Note that the single binary
+approach is also beneficial when statically linking to any of the
+external dependencies, e.g. GLib, as it greatly reduces the overall
+binary size when there is overlap in copies otherwise made separately
+for multiple utilities.
+
+Due to install_symlink(), minimum meson version is bumped to 0.62.1,
+meaning that it has to be installed using `pip' in Travis, since no
+Ubuntu release currently packages that version or newer. However, bump
+to Ubuntu Jammy anyways, just for the sake of building against newer
+versions of ksmbd-tools' dependencies.
+
+Signed-off-by: Atte Heikkilä <atteh.mailbox@gmail.com>
+---
+ .travis.yml | 6 ++++--
+ Makefile.am | 2 +-
+ addshare/Makefile.am | 15 ++++++++-----
+ addshare/addshare.c | 4 ++--
+ addshare/meson.build | 22 ++++++++++---------
+ addshare/share_admin.c | 2 +-
+ adduser/Makefile.am | 16 ++++++++------
+ adduser/adduser.c | 4 ++--
+ adduser/meson.build | 22 ++++++++++---------
+ adduser/user_admin.c | 2 +-
+ configure.ac | 8 ++++---
+ control/Makefile.am | 15 ++++++++-----
+ control/control.c | 4 ++--
+ control/meson.build | 22 ++++++++++---------
+ include/{ksmbdtools.h => tools.h} | 11 +++++++---
+ ksmbd-tools.spec | 3 ++-
+ meson.build | 6 +++---
+ mountd/Makefile.am | 17 +++++++++------
+ mountd/ipc.c | 2 +-
+ mountd/meson.build | 23 +++++++++++---------
+ mountd/mountd.c | 4 ++--
+ mountd/rpc.c | 2 +-
+ mountd/rpc_lsarpc.c | 2 +-
+ mountd/rpc_samr.c | 2 +-
+ mountd/rpc_srvsvc.c | 2 +-
+ mountd/rpc_wkssvc.c | 2 +-
+ mountd/smbacl.c | 2 +-
+ mountd/worker.c | 2 +-
+ {lib => tools}/Makefile.am | 15 +++++++------
+ {lib => tools}/asn1.c | 0
+ {lib => tools}/config_parser.c | 2 +-
+ {lib => tools}/management/session.c | 2 +-
+ {lib => tools}/management/share.c | 2 +-
+ {lib => tools}/management/spnego.c | 2 +-
+ {lib => tools}/management/spnego_krb5.c | 2 +-
+ {lib => tools}/management/spnego_mech.h | 0
+ {lib => tools}/management/tree_conn.c | 2 +-
+ {lib => tools}/management/user.c | 2 +-
+ {lib => tools}/meson.build | 28 ++++++++++++++++---------
+ lib/ksmbdtools.c => tools/tools.c | 27 +++++++++++++++++++++++-
+ 40 files changed, 191 insertions(+), 117 deletions(-)
+ rename include/{ksmbdtools.h => tools.h} (94%)
+ rename {lib => tools}/Makefile.am (53%)
+ rename {lib => tools}/asn1.c (100%)
+ rename {lib => tools}/config_parser.c (99%)
+ rename {lib => tools}/management/session.c (99%)
+ rename {lib => tools}/management/share.c (99%)
+ rename {lib => tools}/management/spnego.c (99%)
+ rename {lib => tools}/management/spnego_krb5.c (99%)
+ rename {lib => tools}/management/spnego_mech.h (100%)
+ rename {lib => tools}/management/tree_conn.c (99%)
+ rename {lib => tools}/management/user.c (99%)
+ rename {lib => tools}/meson.build (60%)
+ rename lib/ksmbdtools.c => tools/tools.c (89%)
+
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -1,4 +1,4 @@
+-dist: focal
++dist: jammy
+
+ language: c
+
+@@ -6,9 +6,11 @@ notifications:
+ - email: true
+
+ before_install:
+- - sudo apt-get install libnl-3-dev libnl-genl-3-dev krb5-multidev heimdal-multidev meson
++ - sudo apt-get install libnl-3-dev libnl-genl-3-dev krb5-multidev heimdal-multidev ninja-build
+ - gcc --version
+ - g++ --version
++ - pip3 install --user meson
++ - PATH=$HOME/.local/bin:$PATH
+
+ jobs:
+ include:
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -1,6 +1,6 @@
+ ACLOCAL_AMFLAGS = -I m4
+
+-SUBDIRS = lib mountd adduser addshare control
++SUBDIRS = addshare adduser control mountd tools
+
+ EXTRA_DIST = include \
+ README.md \
+--- a/addshare/Makefile.am
++++ b/addshare/Makefile.am
+@@ -1,11 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_addshare_LDADD = $(top_builddir)/lib/libksmbdtools.a
+
+-sbin_PROGRAMS = ksmbd.addshare
+-
+-ksmbd_addshare_SOURCES = share_admin.c addshare.c share_admin.h
++noinst_LIBRARIES = libaddshare.a
++libaddshare_a_SOURCES = share_admin.c addshare.c share_admin.h
+
+ EXTRA_DIST = ksmbd.addshare.8.in
+
+@@ -13,3 +10,11 @@ man_MANS = ksmbd.addshare.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++ $(MKDIR_P) $(DESTDIR)$(sbindir)
++ ( cd $(DESTDIR)$(sbindir) && \
++ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.addshare )
++
++uninstall-hook:
++ -rm $(DESTDIR)$(sbindir)/ksmbd.addshare
+--- a/addshare/addshare.c
++++ b/addshare/addshare.c
+@@ -18,7 +18,7 @@
+ #include <ctype.h>
+
+ #include "config_parser.h"
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "management/share.h"
+ #include "linux/ksmbd_server.h"
+ #include "share_admin.h"
+@@ -111,7 +111,7 @@ static int sanity_check_share_name_simpl
+ return 0;
+ }
+
+-int main(int argc, char *argv[])
++int addshare_main(int argc, char **argv)
+ {
+ int ret = -EINVAL;
+ char *share = NULL, *options = NULL, *smbconf = NULL;
+--- a/addshare/meson.build
++++ b/addshare/meson.build
+@@ -1,20 +1,16 @@
+-executable(
+- 'ksmbd.addshare',
++addshare_lib = static_library(
++ 'addshare',
+ 'share_admin.c',
+ 'addshare.c',
+ 'share_admin.h',
+- dependencies: [
+- glib_dep,
+- libnl_dep,
+- ],
+- include_directories: tools_incdir,
+- link_with: libksmbdtools,
+- install: true,
+- install_dir: get_option('sbindir'),
++ include_directories: include_dirs,
+ c_args: [
+ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+ '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+ ],
++ dependencies: [
++ glib_dep,
++ ],
+ )
+
+ configure_file(
+@@ -23,3 +19,9 @@ configure_file(
+ install_dir: get_option('mandir') / 'man8',
+ configuration: in_data,
+ )
++
++install_symlink(
++ 'ksmbd.addshare',
++ install_dir: get_option('sbindir'),
++ pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/addshare/share_admin.c
++++ b/addshare/share_admin.c
+@@ -14,7 +14,7 @@
+ #include <fcntl.h>
+
+ #include <config_parser.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #include <management/share.h>
+
+--- a/adduser/Makefile.am
++++ b/adduser/Makefile.am
+@@ -1,12 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_adduser_LDADD = $(top_builddir)/lib/libksmbdtools.a
+
+-sbin_PROGRAMS = ksmbd.adduser
+-
+-ksmbd_adduser_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h \
+- user_admin.h
++noinst_LIBRARIES = libadduser.a
++libadduser_a_SOURCES = md4_hash.c user_admin.c adduser.c md4_hash.h user_admin.h
+
+ EXTRA_DIST = ksmbd.adduser.8.in
+
+@@ -14,3 +10,11 @@ man_MANS = ksmbd.adduser.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++ $(MKDIR_P) $(DESTDIR)$(sbindir)
++ ( cd $(DESTDIR)$(sbindir) && \
++ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.adduser )
++
++uninstall-hook:
++ -rm $(DESTDIR)$(sbindir)/ksmbd.adduser
+--- a/adduser/adduser.c
++++ b/adduser/adduser.c
+@@ -18,7 +18,7 @@
+ #include <ctype.h>
+
+ #include "config_parser.h"
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "management/user.h"
+ #include "management/share.h"
+ #include "user_admin.h"
+@@ -121,7 +121,7 @@ static int sanity_check_user_name_simple
+ return 0;
+ }
+
+-int main(int argc, char *argv[])
++int adduser_main(int argc, char **argv)
+ {
+ int ret = -EINVAL;
+ char *account = NULL, *password = NULL, *pwddb = NULL, *smbconf = NULL;
+--- a/adduser/meson.build
++++ b/adduser/meson.build
+@@ -1,22 +1,18 @@
+-executable(
+- 'ksmbd.adduser',
++adduser_lib = static_library(
++ 'adduser',
+ 'md4_hash.c',
+ 'user_admin.c',
+ 'adduser.c',
+ 'md4_hash.h',
+ 'user_admin.h',
+- dependencies: [
+- glib_dep,
+- libnl_dep,
+- ],
+- include_directories: tools_incdir,
+- link_with: libksmbdtools,
+- install: true,
+- install_dir: get_option('sbindir'),
++ include_directories: include_dirs,
+ c_args: [
+ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+ '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+ ],
++ dependencies: [
++ glib_dep,
++ ],
+ )
+
+ configure_file(
+@@ -25,3 +21,9 @@ configure_file(
+ install_dir: get_option('mandir') / 'man8',
+ configuration: in_data,
+ )
++
++install_symlink(
++ 'ksmbd.adduser',
++ install_dir: get_option('sbindir'),
++ pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/adduser/user_admin.c
++++ b/adduser/user_admin.c
+@@ -15,7 +15,7 @@
+ #include <termios.h>
+
+ #include <config_parser.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #include <md4_hash.h>
+ #include <user_admin.h>
+--- a/configure.ac
++++ b/configure.ac
+@@ -24,6 +24,8 @@ AC_PROG_CC_STDC
+ AM_SILENT_RULES([yes])
+ AC_PROG_LIBTOOL
+ AC_PROG_SED
++AC_PROG_MKDIR_P
++AC_PROG_LN_S
+
+ AC_SUBST([in_script], [[\
+ '$(SED) -e "s,[@]sbindir[@],$(sbindir),g" \
+@@ -143,11 +145,11 @@ AM_CONDITIONAL(HAVE_LIBKRB5, [test "x$en
+
+ AC_CONFIG_FILES([
+ Makefile
+- lib/Makefile
+- mountd/Makefile
+- adduser/Makefile
+ addshare/Makefile
++ adduser/Makefile
+ control/Makefile
++ mountd/Makefile
++ tools/Makefile
+ ])
+
+ AC_OUTPUT
+--- a/control/Makefile.am
++++ b/control/Makefile.am
+@@ -1,11 +1,8 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-ksmbd_control_LDADD = $(top_builddir)/lib/libksmbdtools.a
+
+-sbin_PROGRAMS = ksmbd.control
+-
+-ksmbd_control_SOURCES = control.c
++noinst_LIBRARIES = libcontrol.a
++libcontrol_a_SOURCES = control.c
+
+ EXTRA_DIST = ksmbd.control.8.in
+
+@@ -13,3 +10,11 @@ man_MANS = ksmbd.control.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++ $(MKDIR_P) $(DESTDIR)$(sbindir)
++ ( cd $(DESTDIR)$(sbindir) && \
++ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.control )
++
++uninstall-hook:
++ -rm $(DESTDIR)$(sbindir)/ksmbd.control
+--- a/control/control.c
++++ b/control/control.c
+@@ -9,7 +9,7 @@
+ #include <fcntl.h>
+ #include <errno.h>
+
+-#include "ksmbdtools.h"
++#include "tools.h"
+ #include "version.h"
+
+ static void usage(int status)
+@@ -146,7 +146,7 @@ out:
+ return ret;
+ }
+
+-int main(int argc, char *argv[])
++int control_main(int argc, char **argv)
+ {
+ int ret = -EINVAL;
+ int c;
+--- a/control/meson.build
++++ b/control/meson.build
+@@ -1,18 +1,14 @@
+-executable(
+- 'ksmbd.control',
++control_lib = static_library(
++ 'control',
+ 'control.c',
+- dependencies: [
+- glib_dep,
+- libnl_dep,
+- ],
+- include_directories: tools_incdir,
+- link_with: libksmbdtools,
+- install: true,
+- install_dir: get_option('sbindir'),
++ include_directories: include_dirs,
+ c_args: [
+ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+ '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+ ],
++ dependencies: [
++ glib_dep,
++ ],
+ )
+
+ configure_file(
+@@ -21,3 +17,9 @@ configure_file(
+ install_dir: get_option('mandir') / 'man8',
+ configuration: in_data,
+ )
++
++install_symlink(
++ 'ksmbd.control',
++ install_dir: get_option('sbindir'),
++ pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/include/ksmbdtools.h
++++ /dev/null
+@@ -1,172 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#ifndef __KSMBDTOOLS_H__
+-#define __KSMBDTOOLS_H__
+-
+-#ifndef _GNU_SOURCE
+-#define _GNU_SOURCE 1
+-#endif
+-
+-#include <errno.h>
+-#include <getopt.h>
+-#include <glib.h>
+-#include <poll.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <sys/types.h>
+-#include <sys/wait.h>
+-#include <time.h>
+-#include <unistd.h>
+-
+-#ifdef HAVE_CONFIG_H
+-#include <config.h>
+-#endif
+-
+-struct smbconf_global {
+- int flags;
+- int map_to_guest;
+- char *guest_account;
+-
+- char *server_string;
+- char *work_group;
+- char *netbios_name;
+- char *server_min_protocol;
+- char *server_max_protocol;
+- char *root_dir;
+- int server_signing;
+- int sessions_cap;
+- int restrict_anon;
+- unsigned short tcp_port;
+- unsigned short ipc_timeout;
+- unsigned int deadtime;
+- int bind_interfaces_only;
+- char **interfaces;
+- unsigned long file_max;
+- unsigned int smb2_max_read;
+- unsigned int smb2_max_write;
+- unsigned int smb2_max_trans;
+- unsigned int smb2_max_credits;
+- unsigned int smbd_max_io_size;
+- unsigned int share_fake_fscaps;
+- unsigned int gen_subauth[3];
+- char *krb5_keytab_file;
+- char *krb5_service_name;
+- char *pwddb;
+- char *smbconf;
+-};
+-
+-#define KSMBD_LOCK_FILE RUNSTATEDIR "/ksmbd.lock"
+-
+-#define KSMBD_RESTRICT_ANON_TYPE_1 1
+-#define KSMBD_RESTRICT_ANON_TYPE_2 2
+-
+-extern struct smbconf_global global_conf;
+-
+-#define KSMBD_CONF_MAP_TO_GUEST_NEVER (0)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_USER (1 << 0)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD (1 << 1)
+-#define KSMBD_CONF_MAP_TO_GUEST_BAD_UID (1 << 2)
+-
+-#define KSMBD_CONF_DEFAULT_NETBIOS_NAME "KSMBD SERVER"
+-#define KSMBD_CONF_DEFAULT_SERVER_STRING "SMB SERVER"
+-#define KSMBD_CONF_DEFAULT_WORK_GROUP "WORKGROUP"
+-
+-#define KSMBD_CONF_DEFAULT_GUEST_ACCOUNT "nobody"
+-
+-#define KSMBD_CONF_DEFAULT_SESS_CAP 1024
+-#define KSMBD_CONF_DEFAULT_TCP_PORT 445
+-
+-#define KSMBD_CONF_FILE_MAX 10000
+-
+-#define PATH_PWDDB SYSCONFDIR "/ksmbd/ksmbdpwd.db"
+-#define PATH_SMBCONF SYSCONFDIR "/ksmbd/ksmbd.conf"
+-#define PATH_SMBCONF_FALLBACK SYSCONFDIR "/ksmbd/smb.conf"
+-#define PATH_SUBAUTH SYSCONFDIR "/ksmbd/ksmbd.subauth"
+-
+-#define KSMBD_HEALTH_START (0)
+-#define KSMBD_HEALTH_RUNNING (1 << 0)
+-#define KSMBD_SHOULD_RELOAD_CONFIG (1 << 1)
+-
+-extern int ksmbd_health_status;
+-
+-#define TRACING_DUMP_NL_MSG 0
+-
+-#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
+-
+-#define STR_HELPER(x) #x
+-#define STR(x) STR_HELPER(x)
+-
+-//---------------------------------------------------------------//
+-#define LOGAPP "[%s/%d]:"
+-#define PRERR LOGAPP" ERROR: "
+-#define PRINF LOGAPP" INFO: "
+-#define PRDEBUG LOGAPP" DEBUG: "
+-
+-#define PR_ERROR 0
+-#define PR_INFO 1
+-#define PR_DEBUG 2
+-
+-extern int log_level;
+-
+-#define PR_LOGGER_STDIO 0
+-#define PR_LOGGER_SYSLOG 1
+-
+-G_GNUC_PRINTF(2, 3)
+-extern void __pr_log(int level, const char *fmt, ...);
+-extern void set_logger_app_name(const char *an);
+-extern const char *get_logger_app_name(void);
+-extern void pr_logger_init(int flags);
+-extern int set_log_level(int level);
+-
+-#define pr_log(l, f, ...) \
+- do { \
+- if ((l) <= log_level) \
+- __pr_log((l), (f), get_logger_app_name(), \
+- getpid(), \
+- ##__VA_ARGS__); \
+- } while (0)
+-
+-#define pr_debug(f, ...) \
+- pr_log(PR_DEBUG, PRDEBUG f, ##__VA_ARGS__)
+-#define pr_info(f, ...) \
+- pr_log(PR_INFO, PRINF f, ##__VA_ARGS__)
+-#define pr_err(f, ...) \
+- pr_log(PR_ERROR, PRERR f, ##__VA_ARGS__)
+-
+-//---------------------------------------------------------------//
+-
+-void pr_hex_dump(const void *mem, size_t sz);
+-
+-char *base64_encode(unsigned char *src, size_t srclen);
+-unsigned char *base64_decode(char const *src, size_t *dstlen);
+-
+-gchar *ksmbd_gconvert(const gchar *str,
+- gssize str_len,
+- int to_codeset,
+- int from_codeset,
+- gsize *bytes_read,
+- gsize *bytes_written);
+-
+-enum charset_idx {
+- KSMBD_CHARSET_UTF8 = 0,
+- KSMBD_CHARSET_UTF16LE,
+- KSMBD_CHARSET_UCS2LE,
+- KSMBD_CHARSET_UTF16BE,
+- KSMBD_CHARSET_UCS2BE,
+- KSMBD_CHARSET_MAX = 5,
+-};
+-
+-#define KSMBD_CHARSET_DEFAULT KSMBD_CHARSET_UTF8
+-
+-extern char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1];
+-
+-int send_signal_to_ksmbd_mountd(int signo);
+-int test_file_access(char *conf);
+-
+-#endif /* __KSMBDTOOLS_H__ */
+--- /dev/null
++++ b/include/tools.h
+@@ -0,0 +1,177 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#ifndef __TOOLS_H__
++#define __TOOLS_H__
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE 1
++#endif
++
++#include <errno.h>
++#include <getopt.h>
++#include <glib.h>
++#include <poll.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <time.h>
++#include <unistd.h>
++
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++struct smbconf_global {
++ int flags;
++ int map_to_guest;
++ char *guest_account;
++
++ char *server_string;
++ char *work_group;
++ char *netbios_name;
++ char *server_min_protocol;
++ char *server_max_protocol;
++ char *root_dir;
++ int server_signing;
++ int sessions_cap;
++ int restrict_anon;
++ unsigned short tcp_port;
++ unsigned short ipc_timeout;
++ unsigned int deadtime;
++ int bind_interfaces_only;
++ char **interfaces;
++ unsigned long file_max;
++ unsigned int smb2_max_read;
++ unsigned int smb2_max_write;
++ unsigned int smb2_max_trans;
++ unsigned int smb2_max_credits;
++ unsigned int smbd_max_io_size;
++ unsigned int share_fake_fscaps;
++ unsigned int gen_subauth[3];
++ char *krb5_keytab_file;
++ char *krb5_service_name;
++ char *pwddb;
++ char *smbconf;
++};
++
++#define KSMBD_LOCK_FILE RUNSTATEDIR "/ksmbd.lock"
++
++#define KSMBD_RESTRICT_ANON_TYPE_1 1
++#define KSMBD_RESTRICT_ANON_TYPE_2 2
++
++extern struct smbconf_global global_conf;
++
++#define KSMBD_CONF_MAP_TO_GUEST_NEVER (0)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_USER (1 << 0)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD (1 << 1)
++#define KSMBD_CONF_MAP_TO_GUEST_BAD_UID (1 << 2)
++
++#define KSMBD_CONF_DEFAULT_NETBIOS_NAME "KSMBD SERVER"
++#define KSMBD_CONF_DEFAULT_SERVER_STRING "SMB SERVER"
++#define KSMBD_CONF_DEFAULT_WORK_GROUP "WORKGROUP"
++
++#define KSMBD_CONF_DEFAULT_GUEST_ACCOUNT "nobody"
++
++#define KSMBD_CONF_DEFAULT_SESS_CAP 1024
++#define KSMBD_CONF_DEFAULT_TCP_PORT 445
++
++#define KSMBD_CONF_FILE_MAX 10000
++
++#define PATH_PWDDB SYSCONFDIR "/ksmbd/ksmbdpwd.db"
++#define PATH_SMBCONF SYSCONFDIR "/ksmbd/ksmbd.conf"
++#define PATH_SMBCONF_FALLBACK SYSCONFDIR "/ksmbd/smb.conf"
++#define PATH_SUBAUTH SYSCONFDIR "/ksmbd/ksmbd.subauth"
++
++#define KSMBD_HEALTH_START (0)
++#define KSMBD_HEALTH_RUNNING (1 << 0)
++#define KSMBD_SHOULD_RELOAD_CONFIG (1 << 1)
++
++extern int ksmbd_health_status;
++
++#define TRACING_DUMP_NL_MSG 0
++
++#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
++
++#define STR_HELPER(x) #x
++#define STR(x) STR_HELPER(x)
++
++//---------------------------------------------------------------//
++#define LOGAPP "[%s/%d]:"
++#define PRERR LOGAPP" ERROR: "
++#define PRINF LOGAPP" INFO: "
++#define PRDEBUG LOGAPP" DEBUG: "
++
++#define PR_ERROR 0
++#define PR_INFO 1
++#define PR_DEBUG 2
++
++extern int log_level;
++
++#define PR_LOGGER_STDIO 0
++#define PR_LOGGER_SYSLOG 1
++
++G_GNUC_PRINTF(2, 3)
++extern void __pr_log(int level, const char *fmt, ...);
++extern void set_logger_app_name(const char *an);
++extern const char *get_logger_app_name(void);
++extern void pr_logger_init(int flags);
++extern int set_log_level(int level);
++
++#define pr_log(l, f, ...) \
++ do { \
++ if ((l) <= log_level) \
++ __pr_log((l), (f), get_logger_app_name(), \
++ getpid(), \
++ ##__VA_ARGS__); \
++ } while (0)
++
++#define pr_debug(f, ...) \
++ pr_log(PR_DEBUG, PRDEBUG f, ##__VA_ARGS__)
++#define pr_info(f, ...) \
++ pr_log(PR_INFO, PRINF f, ##__VA_ARGS__)
++#define pr_err(f, ...) \
++ pr_log(PR_ERROR, PRERR f, ##__VA_ARGS__)
++
++//---------------------------------------------------------------//
++
++void pr_hex_dump(const void *mem, size_t sz);
++
++char *base64_encode(unsigned char *src, size_t srclen);
++unsigned char *base64_decode(char const *src, size_t *dstlen);
++
++gchar *ksmbd_gconvert(const gchar *str,
++ gssize str_len,
++ int to_codeset,
++ int from_codeset,
++ gsize *bytes_read,
++ gsize *bytes_written);
++
++enum charset_idx {
++ KSMBD_CHARSET_UTF8 = 0,
++ KSMBD_CHARSET_UTF16LE,
++ KSMBD_CHARSET_UCS2LE,
++ KSMBD_CHARSET_UTF16BE,
++ KSMBD_CHARSET_UCS2BE,
++ KSMBD_CHARSET_MAX = 5,
++};
++
++#define KSMBD_CHARSET_DEFAULT KSMBD_CHARSET_UTF8
++
++extern char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1];
++
++int send_signal_to_ksmbd_mountd(int signo);
++int test_file_access(char *conf);
++
++int addshare_main(int argc, char **argv);
++int adduser_main(int argc, char **argv);
++int control_main(int argc, char **argv);
++int mountd_main(int argc, char **argv);
++
++#endif /* __TOOLS_H__ */
+--- a/ksmbd-tools.spec
++++ b/ksmbd-tools.spec
+@@ -16,7 +16,7 @@
+ #
+
+ Name: ksmbd-tools
+-Version: 3.4.6
++Version: master
+ Release: 0
+ Summary: ksmbd kernel server userspace utilities
+ License: GPL-2.0-or-later
+@@ -53,6 +53,7 @@ make %{?_smp_mflags}
+ %{_sbindir}/ksmbd.adduser
+ %{_sbindir}/ksmbd.control
+ %{_sbindir}/ksmbd.mountd
++%{_libexecdir}/ksmbd.tools
+ %{_mandir}/man8/ksmbd.addshare.8*
+ %{_mandir}/man8/ksmbd.adduser.8*
+ %{_mandir}/man8/ksmbd.control.8*
+--- a/meson.build
++++ b/meson.build
+@@ -10,10 +10,10 @@ exec awk '/define KSMBD_TOOLS_VERSION /
+ check: true,
+ ).stdout(),
+ default_options: 'c_std=gnu99',
+- meson_version: '>= 0.51.0',
++ meson_version: '>= 0.61.5',
+ )
+
+-tools_incdir = include_directories(
++include_dirs = include_directories(
+ '.',
+ 'include',
+ )
+@@ -151,8 +151,8 @@ configure_file(
+ configuration: in_data,
+ )
+
+-subdir('lib')
+ subdir('addshare')
+ subdir('adduser')
+ subdir('control')
+ subdir('mountd')
++subdir('tools')
+--- a/mountd/Makefile.am
++++ b/mountd/Makefile.am
+@@ -1,12 +1,9 @@
+ AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBNL_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS)
+-ksmbd_mountd_LDADD = $(top_builddir)/lib/libksmbdtools.a
+
+-sbin_PROGRAMS = ksmbd.mountd
+-
+-ksmbd_mountd_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c \
+- mountd.c smbacl.c rpc_samr.c rpc_lsarpc.c
++noinst_LIBRARIES = libmountd.a
++libmountd_a_SOURCES = worker.c ipc.c rpc.c rpc_srvsvc.c rpc_wkssvc.c mountd.c \
++ smbacl.c rpc_samr.c rpc_lsarpc.c
+
+ EXTRA_DIST = ksmbd.mountd.8.in
+
+@@ -14,3 +11,11 @@ man_MANS = ksmbd.mountd.8
+ $(man_MANS): %: %.in; @$(in_script) $< >$@
+
+ CLEANFILES = $(man_MANS)
++
++install-exec-hook: uninstall-hook
++ $(MKDIR_P) $(DESTDIR)$(sbindir)
++ ( cd $(DESTDIR)$(sbindir) && \
++ $(LN_S) $(libexecdir)/ksmbd.tools ksmbd.mountd )
++
++uninstall-hook:
++ -rm $(DESTDIR)$(sbindir)/ksmbd.mountd
+--- a/mountd/ipc.c
++++ b/mountd/ipc.c
+@@ -17,7 +17,7 @@
+
+ #include <linux/ksmbd_server.h>
+
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <ipc.h>
+ #include <worker.h>
+ #include <config_parser.h>
+--- a/mountd/meson.build
++++ b/mountd/meson.build
+@@ -1,5 +1,5 @@
+-executable(
+- 'ksmbd.mountd',
++mountd_lib = static_library(
++ 'mountd',
+ 'worker.c',
+ 'ipc.c',
+ 'rpc.c',
+@@ -9,18 +9,15 @@ executable(
+ 'smbacl.c',
+ 'rpc_samr.c',
+ 'rpc_lsarpc.c',
+- dependencies: [
+- glib_dep,
+- libnl_dep,
+- ],
+- include_directories: tools_incdir,
+- link_with: libksmbdtools,
+- install: true,
+- install_dir: get_option('sbindir'),
++ include_directories: include_dirs,
+ c_args: [
+ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+ '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+ ],
++ dependencies: [
++ glib_dep,
++ libnl_dep,
++ ],
+ )
+
+ configure_file(
+@@ -29,3 +26,9 @@ configure_file(
+ install_dir: get_option('mandir') / 'man8',
+ configuration: in_data,
+ )
++
++install_symlink(
++ 'ksmbd.mountd',
++ install_dir: get_option('sbindir'),
++ pointing_to: '..' / get_option('libexecdir') / 'ksmbd.tools',
++)
+--- a/mountd/mountd.c
++++ b/mountd/mountd.c
+@@ -5,7 +5,7 @@
+ * linux-cifsd-devel@lists.sourceforge.net
+ */
+
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE
+@@ -509,7 +509,7 @@ out:
+ return 0;
+ }
+
+-int main(int argc, char *argv[])
++int mountd_main(int argc, char **argv)
+ {
+ int ret = -EINVAL;
+ int c;
+--- a/mountd/rpc.c
++++ b/mountd/rpc.c
+@@ -16,7 +16,7 @@
+ #include <rpc_wkssvc.h>
+ #include <rpc_samr.h>
+ #include <rpc_lsarpc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ static GHashTable *pipes_table;
+ static GRWLock pipes_table_lock;
+--- a/mountd/rpc_lsarpc.c
++++ b/mountd/rpc_lsarpc.c
+@@ -16,7 +16,7 @@
+ #include <rpc.h>
+ #include <rpc_lsarpc.h>
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #define LSARPC_OPNUM_DS_ROLE_GET_PRIMARY_DOMAIN_INFO 0
+ #define LSARPC_OPNUM_OPEN_POLICY2 44
+--- a/mountd/rpc_samr.c
++++ b/mountd/rpc_samr.c
+@@ -15,7 +15,7 @@
+ #include <rpc.h>
+ #include <rpc_samr.h>
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #define SAMR_OPNUM_CONNECT5 64
+ #define SAMR_OPNUM_ENUM_DOMAIN 6
+--- a/mountd/rpc_srvsvc.c
++++ b/mountd/rpc_srvsvc.c
+@@ -15,7 +15,7 @@
+
+ #include <rpc.h>
+ #include <rpc_srvsvc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #define SHARE_TYPE_TEMP 0x40000000
+ #define SHARE_TYPE_HIDDEN 0x80000000
+--- a/mountd/rpc_wkssvc.c
++++ b/mountd/rpc_wkssvc.c
+@@ -15,7 +15,7 @@
+
+ #include <rpc.h>
+ #include <rpc_wkssvc.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+
+ #define WKSSVC_NETWKSTA_GET_INFO (0)
+
+--- a/mountd/smbacl.c
++++ b/mountd/smbacl.c
+@@ -7,7 +7,7 @@
+ */
+
+ #include <smbacl.h>
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <glib.h>
+
+ static const struct smb_sid sid_domain = {1, 1, {0, 0, 0, 0, 0, 5},
+--- a/mountd/worker.c
++++ b/mountd/worker.c
+@@ -9,7 +9,7 @@
+ #include <errno.h>
+ #include <linux/ksmbd_server.h>
+
+-#include <ksmbdtools.h>
++#include <tools.h>
+ #include <worker.h>
+ #include <ipc.h>
+ #include <rpc.h>
+--- a/lib/Makefile.am
++++ /dev/null
+@@ -1,18 +0,0 @@
+-AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
+- -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBKRB5_CFLAGS) -fno-common
+-LIBS = $(GLIB_LIBS)
+-
+-noinst_LIBRARIES = libksmbdtools.a
+-libksmbdtools_a_SOURCES = management/tree_conn.c \
+- management/user.c \
+- management/share.c \
+- management/session.c \
+- config_parser.c \
+- ksmbdtools.c
+-
+-if HAVE_LIBKRB5
+-libksmbdtools_a_SOURCES += management/spnego.c \
+- asn1.c \
+- management/spnego_krb5.c \
+- management/spnego_mech.h
+-endif
+--- /dev/null
++++ b/tools/Makefile.am
+@@ -0,0 +1,21 @@
++AM_CFLAGS = -DSYSCONFDIR='"${sysconfdir}"' -DRUNSTATEDIR='"${runstatedir}"' \
++ -I$(top_srcdir)/include $(GLIB_CFLAGS) $(LIBKRB5_CFLAGS) -fno-common
++LIBS = $(GLIB_LIBS) $(LIBNL_LIBS) $(LIBKRB5_LIBS)
++
++libexec_PROGRAMS = ksmbd.tools
++ksmbd_tools_SOURCES = management/tree_conn.c \
++ management/user.c \
++ management/share.c \
++ management/session.c \
++ config_parser.c \
++ tools.c
++if HAVE_LIBKRB5
++ksmbd_tools_SOURCES += management/spnego.c \
++ asn1.c \
++ management/spnego_krb5.c \
++ management/spnego_mech.h
++endif
++ksmbd_tools_LDADD = $(top_builddir)/addshare/libaddshare.a \
++ $(top_builddir)/adduser/libadduser.a \
++ $(top_builddir)/control/libcontrol.a \
++ $(top_builddir)/mountd/libmountd.a
+--- a/lib/config_parser.c
++++ /dev/null
+@@ -1,770 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <glib.h>
+-#include <string.h>
+-#include <glib/gstdio.h>
+-#include <sys/stat.h>
+-#include <sys/types.h>
+-#include <unistd.h>
+-#include <fcntl.h>
+-#include <linux/ksmbd_server.h>
+-
+-#include <config_parser.h>
+-#include <ksmbdtools.h>
+-#include <management/user.h>
+-#include <management/share.h>
+-
+-struct smbconf_global global_conf;
+-struct smbconf_parser parser;
+-struct smbconf_group *global_group, *ipc_group;
+-
+-unsigned long long memparse(const char *v)
+-{
+- char *eptr;
+-
+- unsigned long long ret = strtoull(v, &eptr, 0);
+-
+- switch (*eptr) {
+- case 'E':
+- case 'e':
+- ret <<= 10;
+- case 'P':
+- case 'p':
+- ret <<= 10;
+- case 'T':
+- case 't':
+- ret <<= 10;
+- case 'G':
+- case 'g':
+- ret <<= 10;
+- case 'M':
+- case 'm':
+- ret <<= 10;
+- case 'K':
+- case 'k':
+- ret <<= 10;
+- }
+-
+- return ret;
+-}
+-
+-static void kv_release_cb(gpointer p)
+-{
+- g_free(p);
+-}
+-
+-static int is_ascii_space_tab(char c)
+-{
+- return c == ' ' || c == '\t';
+-}
+-
+-static int is_a_comment(char *line)
+-{
+- return (*line == 0x00 || *line == ';' || *line == '\n' || *line == '#');
+-}
+-
+-static int is_a_group(char *line)
+-{
+- char *p = line;
+-
+- if (*p != '[')
+- return 0;
+- p++;
+- while (*p && *p != ']')
+- p = g_utf8_find_next_char(p, NULL);
+- if (*p != ']')
+- return 0;
+- return 1;
+-}
+-
+-static int add_new_group(char *line)
+-{
+- char *begin = line;
+- char *end = line;
+- char *name = NULL;
+- struct smbconf_group *group = NULL;
+- struct smbconf_group *lookup;
+-
+- while (*end && *end != ']')
+- end = g_utf8_find_next_char(end, NULL);
+-
+- name = g_strndup(begin + 1, end - begin - 1);
+- if (!name)
+- goto out_free;
+-
+- lookup = g_hash_table_lookup(parser.groups, name);
+- if (lookup) {
+- parser.current = lookup;
+- pr_info("Multiple definitions for group `%s'\n", name);
+- g_free(name);
+- return 0;
+- }
+-
+- group = g_malloc(sizeof(struct smbconf_group));
+- group->cb_mode = GROUPS_CALLBACK_NONE;
+- group->name = name;
+- group->kv = g_hash_table_new_full(g_str_hash,
+- g_str_equal,
+- kv_release_cb,
+- kv_release_cb);
+- if (!group->kv)
+- goto out_free;
+-
+- parser.current = group;
+- g_hash_table_insert(parser.groups, group->name, group);
+- return 0;
+-
+-out_free:
+- g_free(name);
+- if (group && group->kv)
+- g_hash_table_destroy(group->kv);
+- g_free(group);
+- return -ENOMEM;
+-}
+-
+-static int add_group_key_value(char *line)
+-{
+- char *key, *value;
+-
+- key = strchr(line, '=');
+- if (!key)
+- return -EINVAL;
+-
+- value = key;
+- *key = 0x00;
+- key--;
+- value++;
+-
+- while (is_ascii_space_tab(*key))
+- key--;
+- while (is_ascii_space_tab(*value))
+- value++;
+-
+- if (is_a_comment(value))
+- return 0;
+-
+- if (g_hash_table_lookup(parser.current->kv, key)) {
+- pr_info("Multiple key-value definitions [%s] %s\n",
+- parser.current->name, key);
+- return 0;
+- }
+-
+- key = g_strndup(line, key - line + 1);
+- value = g_strdup(value);
+-
+- if (!key || !value) {
+- g_free(key);
+- g_free(value);
+- return -ENOMEM;
+- }
+-
+- g_hash_table_insert(parser.current->kv, key, value);
+- return 0;
+-}
+-
+-static int process_smbconf_entry(char *data)
+-{
+- while (is_ascii_space_tab(*data))
+- data++;
+-
+- if (is_a_comment(data))
+- return 0;
+-
+- if (is_a_group(data))
+- return add_new_group(data);
+-
+- return add_group_key_value(data);
+-}
+-
+-static int __mmap_parse_file(const char *fname, int (*callback)(char *data))
+-{
+- GMappedFile *file;
+- GError *err = NULL;
+- gchar *contents;
+- int len;
+- char *delim;
+- int fd, ret = 0;
+-
+- fd = g_open(fname, O_RDONLY, 0);
+- if (fd == -1) {
+- ret = -errno;
+- pr_debug("Can't open `%s': %m\n", fname);
+- return ret;
+- }
+-
+- file = g_mapped_file_new_from_fd(fd, FALSE, &err);
+- if (err) {
+- pr_err("Can't map `%s' to memory: %s\n", fname, err->message);
+- g_error_free(err);
+- ret = -EINVAL;
+- goto out;
+- }
+-
+- contents = g_mapped_file_get_contents(file);
+- if (!contents)
+- goto out;
+-
+- len = g_mapped_file_get_length(file);
+- while (len > 0) {
+- delim = strchr(contents, '\n');
+- if (!delim)
+- delim = contents + len - 1;
+-
+- if (delim) {
+- size_t sz = delim - contents;
+- char *data;
+-
+- if (delim == contents) {
+- contents = delim + 1;
+- len--;
+- continue;
+- }
+-
+- if (!sz)
+- break;
+-
+- data = g_strndup(contents, sz);
+- ret = callback(data);
+- if (ret) {
+- g_free(data);
+- goto out;
+- }
+-
+- g_free(data);
+- contents = delim + 1;
+- len -= (sz + 1);
+- }
+- }
+-
+- ret = 0;
+-out:
+- if (file)
+- g_mapped_file_unref(file);
+-
+- if (fd) {
+- g_close(fd, &err);
+- if (err) {
+- pr_err("Can't close `%s': %s\n", fname, err->message);
+- g_error_free(err);
+- }
+- }
+- return ret;
+-}
+-
+-static int init_smbconf_parser(void)
+-{
+- if (parser.groups)
+- return 0;
+-
+- parser.groups = g_hash_table_new(shm_share_name_hash,
+- shm_share_name_equal);
+- if (!parser.groups)
+- return -ENOMEM;
+- return 0;
+-}
+-
+-static void release_smbconf_group(gpointer k, gpointer v, gpointer user_data)
+-{
+- struct smbconf_group *g = v;
+-
+- g_hash_table_destroy(g->kv);
+- g_free(g->name);
+- g_free(g);
+-}
+-
+-static void release_smbconf_parser(void)
+-{
+- if (!parser.groups)
+- return;
+-
+- g_hash_table_foreach(parser.groups, release_smbconf_group, NULL);
+- g_hash_table_destroy(parser.groups);
+- parser.groups = NULL;
+-}
+-
+-char *cp_ltrim(char *v)
+-{
+- if (!v)
+- return NULL;
+-
+- while (*v && *v == ' ')
+- v++;
+- if (*v == 0x00)
+- return NULL;
+- return v;
+-}
+-
+-int cp_key_cmp(char *k, char *v)
+-{
+- if (!k || !v)
+- return -1;
+- return g_ascii_strncasecmp(k, v, strlen(v));
+-}
+-
+-char *cp_get_group_kv_string(char *v)
+-{
+- return g_strdup(v);
+-}
+-
+-int cp_get_group_kv_bool(char *v)
+-{
+- if (!g_ascii_strncasecmp(v, "yes", 3) ||
+- !g_ascii_strncasecmp(v, "1", 1) ||
+- !g_ascii_strncasecmp(v, "true", 4) ||
+- !g_ascii_strncasecmp(v, "enable", 6))
+- return 1;
+- return 0;
+-}
+-
+-int cp_get_group_kv_config_opt(char *v)
+-{
+- if (!g_ascii_strncasecmp(v, "disabled", 8))
+- return KSMBD_CONFIG_OPT_DISABLED;
+- if (!g_ascii_strncasecmp(v, "enabled", 7))
+- return KSMBD_CONFIG_OPT_ENABLED;
+- if (!g_ascii_strncasecmp(v, "auto", 4))
+- return KSMBD_CONFIG_OPT_AUTO;
+- if (!g_ascii_strncasecmp(v, "mandatory", 9))
+- return KSMBD_CONFIG_OPT_MANDATORY;
+- return KSMBD_CONFIG_OPT_DISABLED;
+-}
+-
+-unsigned long cp_get_group_kv_long_base(char *v, int base)
+-{
+- return strtoul(v, NULL, base);
+-}
+-
+-unsigned long cp_get_group_kv_long(char *v)
+-{
+- return cp_get_group_kv_long_base(v, 10);
+-}
+-
+-char **cp_get_group_kv_list(char *v)
+-{
+- /*
+- * SMB conf lists are "tabs, spaces, commas" separated.
+- */
+- return g_strsplit_set(v, "\t ,", -1);
+-}
+-
+-void cp_group_kv_list_free(char **list)
+-{
+- g_strfreev(list);
+-}
+-
+-static int cp_add_global_guest_account(gpointer _v)
+-{
+- struct ksmbd_user *user;
+-
+- if (usm_add_new_user(cp_get_group_kv_string(_v),
+- g_strdup("NULL"))) {
+- pr_err("Unable to add guest account\n");
+- return -ENOMEM;
+- }
+-
+- user = usm_lookup_user(_v);
+- if (!user) {
+- pr_err("Unable to find user `%s'\n", (char *) _v);
+- return -EINVAL;
+- }
+-
+- set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
+- put_ksmbd_user(user);
+- global_conf.guest_account = cp_get_group_kv_string(_v);
+- return 0;
+-}
+-
+-static gboolean global_group_kv(gpointer _k, gpointer _v, gpointer user_data)
+-{
+- if (!cp_key_cmp(_k, "server string")) {
+- global_conf.server_string = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "workgroup")) {
+- global_conf.work_group = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "netbios name")) {
+- global_conf.netbios_name = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "server min protocol")) {
+- global_conf.server_min_protocol = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "server signing")) {
+- global_conf.server_signing = cp_get_group_kv_config_opt(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "server max protocol")) {
+- global_conf.server_max_protocol = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "guest account")) {
+- cp_add_global_guest_account(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "max active sessions")) {
+- global_conf.sessions_cap = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "tcp port")) {
+- if (!global_conf.tcp_port)
+- global_conf.tcp_port = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "ipc timeout")) {
+- global_conf.ipc_timeout = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "max open files")) {
+- global_conf.file_max = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "restrict anonymous")) {
+- global_conf.restrict_anon = cp_get_group_kv_long(_v);
+- if (global_conf.restrict_anon > KSMBD_RESTRICT_ANON_TYPE_2 ||
+- global_conf.restrict_anon < 0) {
+- global_conf.restrict_anon = 0;
+- pr_err("Invalid restrict anonymous value\n");
+- }
+-
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "map to guest")) {
+- global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER;
+- if (!cp_key_cmp(_v, "bad user"))
+- global_conf.map_to_guest =
+- KSMBD_CONF_MAP_TO_GUEST_BAD_USER;
+- if (!cp_key_cmp(_v, "bad password"))
+- global_conf.map_to_guest =
+- KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD;
+- if (!cp_key_cmp(_v, "bad uid"))
+- global_conf.map_to_guest =
+- KSMBD_CONF_MAP_TO_GUEST_BAD_UID;
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "bind interfaces only")) {
+- global_conf.bind_interfaces_only = cp_get_group_kv_bool(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "interfaces")) {
+- global_conf.interfaces = cp_get_group_kv_list(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "deadtime")) {
+- global_conf.deadtime = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb2 leases")) {
+- if (cp_get_group_kv_bool(_v))
+- global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB2_LEASES;
+- else
+- global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB2_LEASES;
+-
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "root directory")) {
+- global_conf.root_dir = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb2 max read")) {
+- global_conf.smb2_max_read = memparse(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb2 max write")) {
+- global_conf.smb2_max_write = memparse(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb2 max trans")) {
+- global_conf.smb2_max_trans = memparse(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb3 encryption")) {
+- if (cp_get_group_kv_bool(_v))
+- global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
+- else
+- global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
+-
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "share:fake_fscaps")) {
+- global_conf.share_fake_fscaps = cp_get_group_kv_long(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "kerberos service name")) {
+- global_conf.krb5_service_name = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "kerberos keytab file")) {
+- global_conf.krb5_keytab_file = cp_get_group_kv_string(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "server multi channel support")) {
+- if (cp_get_group_kv_bool(_v))
+- global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
+- else
+- global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
+-
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smb2 max credits")) {
+- global_conf.smb2_max_credits = memparse(_v);
+- return TRUE;
+- }
+-
+- if (!cp_key_cmp(_k, "smbd max io size")) {
+- global_conf.smbd_max_io_size = memparse(_v);
+- return TRUE;
+- }
+-
+- /* At this point, this is an option that must be applied to all shares */
+- return FALSE;
+-}
+-
+-static void global_conf_default(void)
+-{
+- /* The SPARSE_FILES file system capability flag is set by default */
+- global_conf.share_fake_fscaps = 64;
+-}
+-
+-static void global_conf_create(void)
+-{
+- if (!global_group || global_group->cb_mode != GROUPS_CALLBACK_INIT)
+- return;
+-
+- /*
+- * This will transfer server options to global_conf, and leave behind
+- * in the global parser group, the options that must be applied to every
+- * share
+- */
+- g_hash_table_foreach_remove(global_group->kv, global_group_kv, NULL);
+-}
+-
+-static void append_key_value(gpointer _k, gpointer _v, gpointer user_data)
+-{
+- GHashTable *receiver = (GHashTable *)user_data;
+-
+- /* Don't override local share options */
+- if (!g_hash_table_lookup(receiver, _k))
+- g_hash_table_insert(receiver, g_strdup(_k), g_strdup(_v));
+-}
+-
+-static void global_conf_update(struct smbconf_group *group)
+-{
+- if (!global_group)
+- return;
+-
+- g_hash_table_foreach(global_group->kv, append_key_value, group->kv);
+-}
+-
+-static void global_conf_fixup_missing(void)
+-{
+- /*
+- * Set default global parameters which were not specified
+- * in smb.conf
+- */
+- if (!global_conf.file_max)
+- global_conf.file_max = KSMBD_CONF_FILE_MAX;
+- if (!global_conf.server_string)
+- global_conf.server_string =
+- cp_get_group_kv_string(
+- KSMBD_CONF_DEFAULT_SERVER_STRING);
+- if (!global_conf.netbios_name)
+- global_conf.netbios_name =
+- cp_get_group_kv_string(KSMBD_CONF_DEFAULT_NETBIOS_NAME);
+- if (!global_conf.work_group)
+- global_conf.work_group =
+- cp_get_group_kv_string(KSMBD_CONF_DEFAULT_WORK_GROUP);
+- if (!global_conf.tcp_port)
+- global_conf.tcp_port = KSMBD_CONF_DEFAULT_TCP_PORT;
+-
+- if (global_conf.sessions_cap <= 0)
+- global_conf.sessions_cap = KSMBD_CONF_DEFAULT_SESS_CAP;
+-
+- if (!global_conf.guest_account &&
+- cp_add_global_guest_account(KSMBD_CONF_DEFAULT_GUEST_ACCOUNT))
+- pr_err("Unable to add guest account\n");
+-}
+-
+-static void groups_callback(gpointer _k, gpointer _v, gpointer user_data)
+-{
+- struct smbconf_group *group = (struct smbconf_group *)_v;
+- unsigned short cb_mode = *(unsigned short *)user_data;
+-
+- if (group == global_group)
+- return;
+-
+- group->cb_mode = cb_mode;
+-
+- if (group != ipc_group)
+- global_conf_update(group);
+-
+- shm_add_new_share(group);
+-}
+-
+-static int cp_add_ipc_group(void)
+-{
+- char *comment = NULL, *guest = NULL;
+- int ret = 0;
+-
+- if (ipc_group)
+- return ret;
+-
+- comment = g_strdup("comment = IPC share");
+- guest = g_strdup("guest ok = yes");
+- ret = add_new_group("[IPC$]");
+- ret |= add_group_key_value(comment);
+- ret |= add_group_key_value(guest);
+- if (ret) {
+- pr_err("Unable to add IPC$ share\n");
+- ret = -EINVAL;
+- goto out;
+- }
+-
+- ipc_group = g_hash_table_lookup(parser.groups, "ipc$");
+-out:
+- g_free(comment);
+- g_free(guest);
+- return ret;
+-}
+-
+-static int __cp_parse_smbconfig(const char *smbconf, GHFunc cb,
+- unsigned short cb_mode)
+-{
+- int ret;
+-
+- global_conf_default();
+-
+- ret = cp_smbconfig_hash_create(smbconf);
+- if (ret)
+- return ret;
+-
+- ret = cp_add_ipc_group();
+- if (ret)
+- goto out;
+-
+- global_group = g_hash_table_lookup(parser.groups, "global");
+- if (global_group)
+- global_group->cb_mode = cb_mode;
+-
+- global_conf_create();
+- g_hash_table_foreach(parser.groups, groups_callback, &cb_mode);
+- global_conf_fixup_missing();
+-out:
+- cp_smbconfig_destroy();
+- return ret;
+-}
+-
+-int cp_parse_reload_smbconf(const char *smbconf)
+-{
+- return __cp_parse_smbconfig(smbconf, groups_callback,
+- GROUPS_CALLBACK_REINIT);
+-}
+-
+-int cp_parse_smbconf(const char *smbconf)
+-{
+- return __cp_parse_smbconfig(smbconf, groups_callback,
+- GROUPS_CALLBACK_INIT);
+-}
+-
+-int cp_parse_pwddb(const char *pwddb)
+-{
+- return __mmap_parse_file(pwddb, usm_add_update_user_from_pwdentry);
+-}
+-
+-int cp_smbconfig_hash_create(const char *smbconf)
+-{
+- int ret = init_smbconf_parser();
+-
+- if (ret)
+- return ret;
+- return __mmap_parse_file(smbconf, process_smbconf_entry);
+-}
+-
+-int cp_parse_subauth(void)
+-{
+- return __mmap_parse_file(PATH_SUBAUTH, usm_add_subauth_global_conf);
+-}
+-
+-void cp_smbconfig_destroy(void)
+-{
+- release_smbconf_parser();
+-}
+-
+-int cp_parse_external_smbconf_group(char *name, char *opts)
+-{
+- char *pos;
+- int i, len;
+-
+- if (init_smbconf_parser())
+- return -EINVAL;
+-
+- if (!opts || !name)
+- return -EINVAL;
+-
+- len = strlen(opts);
+- /* fake smb.conf input */
+- for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) {
+- pos = strstr(opts, KSMBD_SHARE_CONF[i]);
+- if (!pos)
+- continue;
+- if (pos != opts)
+- *(pos - 1) = '\n';
+- }
+-
+- if (add_new_group(name))
+- goto error;
+-
+- /* split input and feed to normal process_smbconf_entry() */
+- while (len) {
+- char *delim = strchr(opts, '\n');
+-
+- if (delim) {
+- *delim = 0x00;
+- len -= delim - opts;
+- } else {
+- len = 0;
+- }
+-
+- process_smbconf_entry(opts);
+- if (delim)
+- opts = delim + 1;
+- }
+- return 0;
+-
+-error:
+- cp_smbconfig_destroy();
+- return -EINVAL;
+-}
+--- /dev/null
++++ b/tools/config_parser.c
+@@ -0,0 +1,770 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <glib.h>
++#include <string.h>
++#include <glib/gstdio.h>
++#include <sys/stat.h>
++#include <sys/types.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <linux/ksmbd_server.h>
++
++#include <config_parser.h>
++#include <tools.h>
++#include <management/user.h>
++#include <management/share.h>
++
++struct smbconf_global global_conf;
++struct smbconf_parser parser;
++struct smbconf_group *global_group, *ipc_group;
++
++unsigned long long memparse(const char *v)
++{
++ char *eptr;
++
++ unsigned long long ret = strtoull(v, &eptr, 0);
++
++ switch (*eptr) {
++ case 'E':
++ case 'e':
++ ret <<= 10;
++ case 'P':
++ case 'p':
++ ret <<= 10;
++ case 'T':
++ case 't':
++ ret <<= 10;
++ case 'G':
++ case 'g':
++ ret <<= 10;
++ case 'M':
++ case 'm':
++ ret <<= 10;
++ case 'K':
++ case 'k':
++ ret <<= 10;
++ }
++
++ return ret;
++}
++
++static void kv_release_cb(gpointer p)
++{
++ g_free(p);
++}
++
++static int is_ascii_space_tab(char c)
++{
++ return c == ' ' || c == '\t';
++}
++
++static int is_a_comment(char *line)
++{
++ return (*line == 0x00 || *line == ';' || *line == '\n' || *line == '#');
++}
++
++static int is_a_group(char *line)
++{
++ char *p = line;
++
++ if (*p != '[')
++ return 0;
++ p++;
++ while (*p && *p != ']')
++ p = g_utf8_find_next_char(p, NULL);
++ if (*p != ']')
++ return 0;
++ return 1;
++}
++
++static int add_new_group(char *line)
++{
++ char *begin = line;
++ char *end = line;
++ char *name = NULL;
++ struct smbconf_group *group = NULL;
++ struct smbconf_group *lookup;
++
++ while (*end && *end != ']')
++ end = g_utf8_find_next_char(end, NULL);
++
++ name = g_strndup(begin + 1, end - begin - 1);
++ if (!name)
++ goto out_free;
++
++ lookup = g_hash_table_lookup(parser.groups, name);
++ if (lookup) {
++ parser.current = lookup;
++ pr_info("Multiple definitions for group `%s'\n", name);
++ g_free(name);
++ return 0;
++ }
++
++ group = g_malloc(sizeof(struct smbconf_group));
++ group->cb_mode = GROUPS_CALLBACK_NONE;
++ group->name = name;
++ group->kv = g_hash_table_new_full(g_str_hash,
++ g_str_equal,
++ kv_release_cb,
++ kv_release_cb);
++ if (!group->kv)
++ goto out_free;
++
++ parser.current = group;
++ g_hash_table_insert(parser.groups, group->name, group);
++ return 0;
++
++out_free:
++ g_free(name);
++ if (group && group->kv)
++ g_hash_table_destroy(group->kv);
++ g_free(group);
++ return -ENOMEM;
++}
++
++static int add_group_key_value(char *line)
++{
++ char *key, *value;
++
++ key = strchr(line, '=');
++ if (!key)
++ return -EINVAL;
++
++ value = key;
++ *key = 0x00;
++ key--;
++ value++;
++
++ while (is_ascii_space_tab(*key))
++ key--;
++ while (is_ascii_space_tab(*value))
++ value++;
++
++ if (is_a_comment(value))
++ return 0;
++
++ if (g_hash_table_lookup(parser.current->kv, key)) {
++ pr_info("Multiple key-value definitions [%s] %s\n",
++ parser.current->name, key);
++ return 0;
++ }
++
++ key = g_strndup(line, key - line + 1);
++ value = g_strdup(value);
++
++ if (!key || !value) {
++ g_free(key);
++ g_free(value);
++ return -ENOMEM;
++ }
++
++ g_hash_table_insert(parser.current->kv, key, value);
++ return 0;
++}
++
++static int process_smbconf_entry(char *data)
++{
++ while (is_ascii_space_tab(*data))
++ data++;
++
++ if (is_a_comment(data))
++ return 0;
++
++ if (is_a_group(data))
++ return add_new_group(data);
++
++ return add_group_key_value(data);
++}
++
++static int __mmap_parse_file(const char *fname, int (*callback)(char *data))
++{
++ GMappedFile *file;
++ GError *err = NULL;
++ gchar *contents;
++ int len;
++ char *delim;
++ int fd, ret = 0;
++
++ fd = g_open(fname, O_RDONLY, 0);
++ if (fd == -1) {
++ ret = -errno;
++ pr_debug("Can't open `%s': %m\n", fname);
++ return ret;
++ }
++
++ file = g_mapped_file_new_from_fd(fd, FALSE, &err);
++ if (err) {
++ pr_err("Can't map `%s' to memory: %s\n", fname, err->message);
++ g_error_free(err);
++ ret = -EINVAL;
++ goto out;
++ }
++
++ contents = g_mapped_file_get_contents(file);
++ if (!contents)
++ goto out;
++
++ len = g_mapped_file_get_length(file);
++ while (len > 0) {
++ delim = strchr(contents, '\n');
++ if (!delim)
++ delim = contents + len - 1;
++
++ if (delim) {
++ size_t sz = delim - contents;
++ char *data;
++
++ if (delim == contents) {
++ contents = delim + 1;
++ len--;
++ continue;
++ }
++
++ if (!sz)
++ break;
++
++ data = g_strndup(contents, sz);
++ ret = callback(data);
++ if (ret) {
++ g_free(data);
++ goto out;
++ }
++
++ g_free(data);
++ contents = delim + 1;
++ len -= (sz + 1);
++ }
++ }
++
++ ret = 0;
++out:
++ if (file)
++ g_mapped_file_unref(file);
++
++ if (fd) {
++ g_close(fd, &err);
++ if (err) {
++ pr_err("Can't close `%s': %s\n", fname, err->message);
++ g_error_free(err);
++ }
++ }
++ return ret;
++}
++
++static int init_smbconf_parser(void)
++{
++ if (parser.groups)
++ return 0;
++
++ parser.groups = g_hash_table_new(shm_share_name_hash,
++ shm_share_name_equal);
++ if (!parser.groups)
++ return -ENOMEM;
++ return 0;
++}
++
++static void release_smbconf_group(gpointer k, gpointer v, gpointer user_data)
++{
++ struct smbconf_group *g = v;
++
++ g_hash_table_destroy(g->kv);
++ g_free(g->name);
++ g_free(g);
++}
++
++static void release_smbconf_parser(void)
++{
++ if (!parser.groups)
++ return;
++
++ g_hash_table_foreach(parser.groups, release_smbconf_group, NULL);
++ g_hash_table_destroy(parser.groups);
++ parser.groups = NULL;
++}
++
++char *cp_ltrim(char *v)
++{
++ if (!v)
++ return NULL;
++
++ while (*v && *v == ' ')
++ v++;
++ if (*v == 0x00)
++ return NULL;
++ return v;
++}
++
++int cp_key_cmp(char *k, char *v)
++{
++ if (!k || !v)
++ return -1;
++ return g_ascii_strncasecmp(k, v, strlen(v));
++}
++
++char *cp_get_group_kv_string(char *v)
++{
++ return g_strdup(v);
++}
++
++int cp_get_group_kv_bool(char *v)
++{
++ if (!g_ascii_strncasecmp(v, "yes", 3) ||
++ !g_ascii_strncasecmp(v, "1", 1) ||
++ !g_ascii_strncasecmp(v, "true", 4) ||
++ !g_ascii_strncasecmp(v, "enable", 6))
++ return 1;
++ return 0;
++}
++
++int cp_get_group_kv_config_opt(char *v)
++{
++ if (!g_ascii_strncasecmp(v, "disabled", 8))
++ return KSMBD_CONFIG_OPT_DISABLED;
++ if (!g_ascii_strncasecmp(v, "enabled", 7))
++ return KSMBD_CONFIG_OPT_ENABLED;
++ if (!g_ascii_strncasecmp(v, "auto", 4))
++ return KSMBD_CONFIG_OPT_AUTO;
++ if (!g_ascii_strncasecmp(v, "mandatory", 9))
++ return KSMBD_CONFIG_OPT_MANDATORY;
++ return KSMBD_CONFIG_OPT_DISABLED;
++}
++
++unsigned long cp_get_group_kv_long_base(char *v, int base)
++{
++ return strtoul(v, NULL, base);
++}
++
++unsigned long cp_get_group_kv_long(char *v)
++{
++ return cp_get_group_kv_long_base(v, 10);
++}
++
++char **cp_get_group_kv_list(char *v)
++{
++ /*
++ * SMB conf lists are "tabs, spaces, commas" separated.
++ */
++ return g_strsplit_set(v, "\t ,", -1);
++}
++
++void cp_group_kv_list_free(char **list)
++{
++ g_strfreev(list);
++}
++
++static int cp_add_global_guest_account(gpointer _v)
++{
++ struct ksmbd_user *user;
++
++ if (usm_add_new_user(cp_get_group_kv_string(_v),
++ g_strdup("NULL"))) {
++ pr_err("Unable to add guest account\n");
++ return -ENOMEM;
++ }
++
++ user = usm_lookup_user(_v);
++ if (!user) {
++ pr_err("Unable to find user `%s'\n", (char *) _v);
++ return -EINVAL;
++ }
++
++ set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
++ put_ksmbd_user(user);
++ global_conf.guest_account = cp_get_group_kv_string(_v);
++ return 0;
++}
++
++static gboolean global_group_kv(gpointer _k, gpointer _v, gpointer user_data)
++{
++ if (!cp_key_cmp(_k, "server string")) {
++ global_conf.server_string = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "workgroup")) {
++ global_conf.work_group = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "netbios name")) {
++ global_conf.netbios_name = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "server min protocol")) {
++ global_conf.server_min_protocol = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "server signing")) {
++ global_conf.server_signing = cp_get_group_kv_config_opt(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "server max protocol")) {
++ global_conf.server_max_protocol = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "guest account")) {
++ cp_add_global_guest_account(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "max active sessions")) {
++ global_conf.sessions_cap = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "tcp port")) {
++ if (!global_conf.tcp_port)
++ global_conf.tcp_port = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "ipc timeout")) {
++ global_conf.ipc_timeout = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "max open files")) {
++ global_conf.file_max = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "restrict anonymous")) {
++ global_conf.restrict_anon = cp_get_group_kv_long(_v);
++ if (global_conf.restrict_anon > KSMBD_RESTRICT_ANON_TYPE_2 ||
++ global_conf.restrict_anon < 0) {
++ global_conf.restrict_anon = 0;
++ pr_err("Invalid restrict anonymous value\n");
++ }
++
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "map to guest")) {
++ global_conf.map_to_guest = KSMBD_CONF_MAP_TO_GUEST_NEVER;
++ if (!cp_key_cmp(_v, "bad user"))
++ global_conf.map_to_guest =
++ KSMBD_CONF_MAP_TO_GUEST_BAD_USER;
++ if (!cp_key_cmp(_v, "bad password"))
++ global_conf.map_to_guest =
++ KSMBD_CONF_MAP_TO_GUEST_BAD_PASSWORD;
++ if (!cp_key_cmp(_v, "bad uid"))
++ global_conf.map_to_guest =
++ KSMBD_CONF_MAP_TO_GUEST_BAD_UID;
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "bind interfaces only")) {
++ global_conf.bind_interfaces_only = cp_get_group_kv_bool(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "interfaces")) {
++ global_conf.interfaces = cp_get_group_kv_list(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "deadtime")) {
++ global_conf.deadtime = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb2 leases")) {
++ if (cp_get_group_kv_bool(_v))
++ global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB2_LEASES;
++ else
++ global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB2_LEASES;
++
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "root directory")) {
++ global_conf.root_dir = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb2 max read")) {
++ global_conf.smb2_max_read = memparse(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb2 max write")) {
++ global_conf.smb2_max_write = memparse(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb2 max trans")) {
++ global_conf.smb2_max_trans = memparse(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb3 encryption")) {
++ if (cp_get_group_kv_bool(_v))
++ global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
++ else
++ global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_ENCRYPTION;
++
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "share:fake_fscaps")) {
++ global_conf.share_fake_fscaps = cp_get_group_kv_long(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "kerberos service name")) {
++ global_conf.krb5_service_name = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "kerberos keytab file")) {
++ global_conf.krb5_keytab_file = cp_get_group_kv_string(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "server multi channel support")) {
++ if (cp_get_group_kv_bool(_v))
++ global_conf.flags |= KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
++ else
++ global_conf.flags &= ~KSMBD_GLOBAL_FLAG_SMB3_MULTICHANNEL;
++
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smb2 max credits")) {
++ global_conf.smb2_max_credits = memparse(_v);
++ return TRUE;
++ }
++
++ if (!cp_key_cmp(_k, "smbd max io size")) {
++ global_conf.smbd_max_io_size = memparse(_v);
++ return TRUE;
++ }
++
++ /* At this point, this is an option that must be applied to all shares */
++ return FALSE;
++}
++
++static void global_conf_default(void)
++{
++ /* The SPARSE_FILES file system capability flag is set by default */
++ global_conf.share_fake_fscaps = 64;
++}
++
++static void global_conf_create(void)
++{
++ if (!global_group || global_group->cb_mode != GROUPS_CALLBACK_INIT)
++ return;
++
++ /*
++ * This will transfer server options to global_conf, and leave behind
++ * in the global parser group, the options that must be applied to every
++ * share
++ */
++ g_hash_table_foreach_remove(global_group->kv, global_group_kv, NULL);
++}
++
++static void append_key_value(gpointer _k, gpointer _v, gpointer user_data)
++{
++ GHashTable *receiver = (GHashTable *)user_data;
++
++ /* Don't override local share options */
++ if (!g_hash_table_lookup(receiver, _k))
++ g_hash_table_insert(receiver, g_strdup(_k), g_strdup(_v));
++}
++
++static void global_conf_update(struct smbconf_group *group)
++{
++ if (!global_group)
++ return;
++
++ g_hash_table_foreach(global_group->kv, append_key_value, group->kv);
++}
++
++static void global_conf_fixup_missing(void)
++{
++ /*
++ * Set default global parameters which were not specified
++ * in smb.conf
++ */
++ if (!global_conf.file_max)
++ global_conf.file_max = KSMBD_CONF_FILE_MAX;
++ if (!global_conf.server_string)
++ global_conf.server_string =
++ cp_get_group_kv_string(
++ KSMBD_CONF_DEFAULT_SERVER_STRING);
++ if (!global_conf.netbios_name)
++ global_conf.netbios_name =
++ cp_get_group_kv_string(KSMBD_CONF_DEFAULT_NETBIOS_NAME);
++ if (!global_conf.work_group)
++ global_conf.work_group =
++ cp_get_group_kv_string(KSMBD_CONF_DEFAULT_WORK_GROUP);
++ if (!global_conf.tcp_port)
++ global_conf.tcp_port = KSMBD_CONF_DEFAULT_TCP_PORT;
++
++ if (global_conf.sessions_cap <= 0)
++ global_conf.sessions_cap = KSMBD_CONF_DEFAULT_SESS_CAP;
++
++ if (!global_conf.guest_account &&
++ cp_add_global_guest_account(KSMBD_CONF_DEFAULT_GUEST_ACCOUNT))
++ pr_err("Unable to add guest account\n");
++}
++
++static void groups_callback(gpointer _k, gpointer _v, gpointer user_data)
++{
++ struct smbconf_group *group = (struct smbconf_group *)_v;
++ unsigned short cb_mode = *(unsigned short *)user_data;
++
++ if (group == global_group)
++ return;
++
++ group->cb_mode = cb_mode;
++
++ if (group != ipc_group)
++ global_conf_update(group);
++
++ shm_add_new_share(group);
++}
++
++static int cp_add_ipc_group(void)
++{
++ char *comment = NULL, *guest = NULL;
++ int ret = 0;
++
++ if (ipc_group)
++ return ret;
++
++ comment = g_strdup("comment = IPC share");
++ guest = g_strdup("guest ok = yes");
++ ret = add_new_group("[IPC$]");
++ ret |= add_group_key_value(comment);
++ ret |= add_group_key_value(guest);
++ if (ret) {
++ pr_err("Unable to add IPC$ share\n");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ipc_group = g_hash_table_lookup(parser.groups, "ipc$");
++out:
++ g_free(comment);
++ g_free(guest);
++ return ret;
++}
++
++static int __cp_parse_smbconfig(const char *smbconf, GHFunc cb,
++ unsigned short cb_mode)
++{
++ int ret;
++
++ global_conf_default();
++
++ ret = cp_smbconfig_hash_create(smbconf);
++ if (ret)
++ return ret;
++
++ ret = cp_add_ipc_group();
++ if (ret)
++ goto out;
++
++ global_group = g_hash_table_lookup(parser.groups, "global");
++ if (global_group)
++ global_group->cb_mode = cb_mode;
++
++ global_conf_create();
++ g_hash_table_foreach(parser.groups, groups_callback, &cb_mode);
++ global_conf_fixup_missing();
++out:
++ cp_smbconfig_destroy();
++ return ret;
++}
++
++int cp_parse_reload_smbconf(const char *smbconf)
++{
++ return __cp_parse_smbconfig(smbconf, groups_callback,
++ GROUPS_CALLBACK_REINIT);
++}
++
++int cp_parse_smbconf(const char *smbconf)
++{
++ return __cp_parse_smbconfig(smbconf, groups_callback,
++ GROUPS_CALLBACK_INIT);
++}
++
++int cp_parse_pwddb(const char *pwddb)
++{
++ return __mmap_parse_file(pwddb, usm_add_update_user_from_pwdentry);
++}
++
++int cp_smbconfig_hash_create(const char *smbconf)
++{
++ int ret = init_smbconf_parser();
++
++ if (ret)
++ return ret;
++ return __mmap_parse_file(smbconf, process_smbconf_entry);
++}
++
++int cp_parse_subauth(void)
++{
++ return __mmap_parse_file(PATH_SUBAUTH, usm_add_subauth_global_conf);
++}
++
++void cp_smbconfig_destroy(void)
++{
++ release_smbconf_parser();
++}
++
++int cp_parse_external_smbconf_group(char *name, char *opts)
++{
++ char *pos;
++ int i, len;
++
++ if (init_smbconf_parser())
++ return -EINVAL;
++
++ if (!opts || !name)
++ return -EINVAL;
++
++ len = strlen(opts);
++ /* fake smb.conf input */
++ for (i = 0; i < KSMBD_SHARE_CONF_MAX; i++) {
++ pos = strstr(opts, KSMBD_SHARE_CONF[i]);
++ if (!pos)
++ continue;
++ if (pos != opts)
++ *(pos - 1) = '\n';
++ }
++
++ if (add_new_group(name))
++ goto error;
++
++ /* split input and feed to normal process_smbconf_entry() */
++ while (len) {
++ char *delim = strchr(opts, '\n');
++
++ if (delim) {
++ *delim = 0x00;
++ len -= delim - opts;
++ } else {
++ len = 0;
++ }
++
++ process_smbconf_entry(opts);
++ if (delim)
++ opts = delim + 1;
++ }
++ return 0;
++
++error:
++ cp_smbconfig_destroy();
++ return -EINVAL;
++}
+--- a/lib/management/session.c
++++ /dev/null
+@@ -1,235 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/session.h"
+-#include "management/tree_conn.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-static GHashTable *sessions_table;
+-static GRWLock sessions_table_lock;
+-
+-static void __free_func(gpointer data, gpointer user_data)
+-{
+- struct ksmbd_tree_conn *tree_conn;
+-
+- tree_conn = (struct ksmbd_tree_conn *)data;
+- tcm_tree_conn_free(tree_conn);
+-}
+-
+-static void kill_ksmbd_session(struct ksmbd_session *sess)
+-{
+- g_list_foreach(sess->tree_conns, __free_func, NULL);
+- g_list_free(sess->tree_conns);
+- g_rw_lock_clear(&sess->update_lock);
+- g_free(sess);
+-}
+-
+-static struct ksmbd_session *new_ksmbd_session(unsigned long long id,
+- struct ksmbd_user *user)
+-{
+- struct ksmbd_session *sess;
+-
+- sess = g_try_malloc0(sizeof(struct ksmbd_session));
+- if (!sess)
+- return NULL;
+-
+- g_rw_lock_init(&sess->update_lock);
+- sess->ref_counter = 1;
+- sess->id = id;
+- sess->user = user;
+- return sess;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
+-{
+- kill_ksmbd_session(s);
+-}
+-
+-static void sm_clear_sessions(void)
+-{
+- g_hash_table_foreach(sessions_table, free_hash_entry, NULL);
+-}
+-
+-static int __sm_remove_session(struct ksmbd_session *sess)
+-{
+- int ret = -EINVAL;
+-
+- g_rw_lock_writer_lock(&sessions_table_lock);
+- if (g_hash_table_remove(sessions_table, &sess->id))
+- ret = 0;
+- g_rw_lock_writer_unlock(&sessions_table_lock);
+-
+- if (!ret)
+- kill_ksmbd_session(sess);
+- return ret;
+-}
+-
+-static struct ksmbd_session *__get_session(struct ksmbd_session *sess)
+-{
+- struct ksmbd_session *ret = NULL;
+-
+- g_rw_lock_writer_lock(&sess->update_lock);
+- if (sess->ref_counter != 0) {
+- sess->ref_counter++;
+- ret = sess;
+- } else {
+- ret = NULL;
+- }
+- g_rw_lock_writer_unlock(&sess->update_lock);
+- return ret;
+-}
+-
+-static void __put_session(struct ksmbd_session *sess)
+-{
+- int drop = 0;
+-
+- g_rw_lock_writer_lock(&sess->update_lock);
+- sess->ref_counter--;
+- drop = !sess->ref_counter;
+- g_rw_lock_writer_unlock(&sess->update_lock);
+-
+- if (drop)
+- __sm_remove_session(sess);
+-}
+-
+-static struct ksmbd_session *__sm_lookup_session(unsigned long long id)
+-{
+- return g_hash_table_lookup(sessions_table, &id);
+-}
+-
+-static struct ksmbd_session *sm_lookup_session(unsigned long long id)
+-{
+- struct ksmbd_session *sess;
+-
+- g_rw_lock_reader_lock(&sessions_table_lock);
+- sess = __sm_lookup_session(id);
+- if (sess)
+- sess = __get_session(sess);
+- g_rw_lock_reader_unlock(&sessions_table_lock);
+- return sess;
+-}
+-
+-int sm_handle_tree_connect(unsigned long long id,
+- struct ksmbd_user *user,
+- struct ksmbd_tree_conn *tree_conn)
+-{
+- struct ksmbd_session *sess, *lookup;
+-
+-retry:
+- sess = sm_lookup_session(id);
+- if (!sess) {
+- sess = new_ksmbd_session(id, user);
+- if (!sess)
+- return -EINVAL;
+-
+- g_rw_lock_writer_lock(&sessions_table_lock);
+- lookup = __sm_lookup_session(id);
+- if (lookup)
+- lookup = __get_session(lookup);
+- if (lookup) {
+- kill_ksmbd_session(sess);
+- sess = lookup;
+- }
+- if (!g_hash_table_insert(sessions_table, &(sess->id), sess)) {
+- kill_ksmbd_session(sess);
+- sess = NULL;
+- }
+- g_rw_lock_writer_unlock(&sessions_table_lock);
+-
+- if (!sess)
+- goto retry;
+- }
+-
+- g_rw_lock_writer_lock(&sess->update_lock);
+- sess->tree_conns = g_list_insert(sess->tree_conns, tree_conn, -1);
+- g_rw_lock_writer_unlock(&sess->update_lock);
+- return 0;
+-}
+-
+-int sm_check_sessions_capacity(unsigned long long id)
+-{
+- int ret = 0;
+- struct ksmbd_session *sess;
+-
+- sess = sm_lookup_session(id);
+- if (sess) {
+- __put_session(sess);
+- return ret;
+- }
+-
+- if (g_atomic_int_add(&global_conf.sessions_cap, -1) < 1) {
+- ret = -EINVAL;
+- g_atomic_int_inc(&global_conf.sessions_cap);
+- }
+- return ret;
+-}
+-
+-static gint lookup_tree_conn(gconstpointer data, gconstpointer user_data)
+-{
+- struct ksmbd_tree_conn *tree_conn = (struct ksmbd_tree_conn *)data;
+- struct ksmbd_tree_conn *dummy = (struct ksmbd_tree_conn *)user_data;
+-
+- if (tree_conn->id == dummy->id)
+- return 0;
+- return 1;
+-}
+-
+-int sm_handle_tree_disconnect(unsigned long long sess_id,
+- unsigned long long tree_conn_id)
+-{
+- struct ksmbd_tree_conn dummy;
+- struct ksmbd_session *sess;
+- GList *tc_list;
+-
+- sess = sm_lookup_session(sess_id);
+- if (!sess)
+- return 0;
+-
+- g_atomic_int_inc(&global_conf.sessions_cap);
+- g_rw_lock_writer_lock(&sess->update_lock);
+- dummy.id = tree_conn_id;
+- tc_list = g_list_find_custom(sess->tree_conns,
+- &dummy,
+- lookup_tree_conn);
+- if (tc_list) {
+- struct ksmbd_tree_conn *tree_conn;
+-
+- tree_conn = (struct ksmbd_tree_conn *)tc_list->data;
+- sess->tree_conns = g_list_remove(sess->tree_conns, tree_conn);
+- sess->ref_counter--;
+- tcm_tree_conn_free(tree_conn);
+- }
+- g_rw_lock_writer_unlock(&sess->update_lock);
+-
+- put_ksmbd_user(sess->user);
+- __put_session(sess);
+- return 0;
+-}
+-
+-void sm_destroy(void)
+-{
+- if (sessions_table) {
+- sm_clear_sessions();
+- g_hash_table_destroy(sessions_table);
+- }
+- g_rw_lock_clear(&sessions_table_lock);
+-}
+-
+-int sm_init(void)
+-{
+- sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal);
+- if (!sessions_table)
+- return -ENOMEM;
+- g_rw_lock_init(&sessions_table_lock);
+- return 0;
+-}
+--- /dev/null
++++ b/tools/management/session.c
+@@ -0,0 +1,235 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/session.h"
++#include "management/tree_conn.h"
++#include "management/user.h"
++#include "tools.h"
++
++static GHashTable *sessions_table;
++static GRWLock sessions_table_lock;
++
++static void __free_func(gpointer data, gpointer user_data)
++{
++ struct ksmbd_tree_conn *tree_conn;
++
++ tree_conn = (struct ksmbd_tree_conn *)data;
++ tcm_tree_conn_free(tree_conn);
++}
++
++static void kill_ksmbd_session(struct ksmbd_session *sess)
++{
++ g_list_foreach(sess->tree_conns, __free_func, NULL);
++ g_list_free(sess->tree_conns);
++ g_rw_lock_clear(&sess->update_lock);
++ g_free(sess);
++}
++
++static struct ksmbd_session *new_ksmbd_session(unsigned long long id,
++ struct ksmbd_user *user)
++{
++ struct ksmbd_session *sess;
++
++ sess = g_try_malloc0(sizeof(struct ksmbd_session));
++ if (!sess)
++ return NULL;
++
++ g_rw_lock_init(&sess->update_lock);
++ sess->ref_counter = 1;
++ sess->id = id;
++ sess->user = user;
++ return sess;
++}
++
++static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
++{
++ kill_ksmbd_session(s);
++}
++
++static void sm_clear_sessions(void)
++{
++ g_hash_table_foreach(sessions_table, free_hash_entry, NULL);
++}
++
++static int __sm_remove_session(struct ksmbd_session *sess)
++{
++ int ret = -EINVAL;
++
++ g_rw_lock_writer_lock(&sessions_table_lock);
++ if (g_hash_table_remove(sessions_table, &sess->id))
++ ret = 0;
++ g_rw_lock_writer_unlock(&sessions_table_lock);
++
++ if (!ret)
++ kill_ksmbd_session(sess);
++ return ret;
++}
++
++static struct ksmbd_session *__get_session(struct ksmbd_session *sess)
++{
++ struct ksmbd_session *ret = NULL;
++
++ g_rw_lock_writer_lock(&sess->update_lock);
++ if (sess->ref_counter != 0) {
++ sess->ref_counter++;
++ ret = sess;
++ } else {
++ ret = NULL;
++ }
++ g_rw_lock_writer_unlock(&sess->update_lock);
++ return ret;
++}
++
++static void __put_session(struct ksmbd_session *sess)
++{
++ int drop = 0;
++
++ g_rw_lock_writer_lock(&sess->update_lock);
++ sess->ref_counter--;
++ drop = !sess->ref_counter;
++ g_rw_lock_writer_unlock(&sess->update_lock);
++
++ if (drop)
++ __sm_remove_session(sess);
++}
++
++static struct ksmbd_session *__sm_lookup_session(unsigned long long id)
++{
++ return g_hash_table_lookup(sessions_table, &id);
++}
++
++static struct ksmbd_session *sm_lookup_session(unsigned long long id)
++{
++ struct ksmbd_session *sess;
++
++ g_rw_lock_reader_lock(&sessions_table_lock);
++ sess = __sm_lookup_session(id);
++ if (sess)
++ sess = __get_session(sess);
++ g_rw_lock_reader_unlock(&sessions_table_lock);
++ return sess;
++}
++
++int sm_handle_tree_connect(unsigned long long id,
++ struct ksmbd_user *user,
++ struct ksmbd_tree_conn *tree_conn)
++{
++ struct ksmbd_session *sess, *lookup;
++
++retry:
++ sess = sm_lookup_session(id);
++ if (!sess) {
++ sess = new_ksmbd_session(id, user);
++ if (!sess)
++ return -EINVAL;
++
++ g_rw_lock_writer_lock(&sessions_table_lock);
++ lookup = __sm_lookup_session(id);
++ if (lookup)
++ lookup = __get_session(lookup);
++ if (lookup) {
++ kill_ksmbd_session(sess);
++ sess = lookup;
++ }
++ if (!g_hash_table_insert(sessions_table, &(sess->id), sess)) {
++ kill_ksmbd_session(sess);
++ sess = NULL;
++ }
++ g_rw_lock_writer_unlock(&sessions_table_lock);
++
++ if (!sess)
++ goto retry;
++ }
++
++ g_rw_lock_writer_lock(&sess->update_lock);
++ sess->tree_conns = g_list_insert(sess->tree_conns, tree_conn, -1);
++ g_rw_lock_writer_unlock(&sess->update_lock);
++ return 0;
++}
++
++int sm_check_sessions_capacity(unsigned long long id)
++{
++ int ret = 0;
++ struct ksmbd_session *sess;
++
++ sess = sm_lookup_session(id);
++ if (sess) {
++ __put_session(sess);
++ return ret;
++ }
++
++ if (g_atomic_int_add(&global_conf.sessions_cap, -1) < 1) {
++ ret = -EINVAL;
++ g_atomic_int_inc(&global_conf.sessions_cap);
++ }
++ return ret;
++}
++
++static gint lookup_tree_conn(gconstpointer data, gconstpointer user_data)
++{
++ struct ksmbd_tree_conn *tree_conn = (struct ksmbd_tree_conn *)data;
++ struct ksmbd_tree_conn *dummy = (struct ksmbd_tree_conn *)user_data;
++
++ if (tree_conn->id == dummy->id)
++ return 0;
++ return 1;
++}
++
++int sm_handle_tree_disconnect(unsigned long long sess_id,
++ unsigned long long tree_conn_id)
++{
++ struct ksmbd_tree_conn dummy;
++ struct ksmbd_session *sess;
++ GList *tc_list;
++
++ sess = sm_lookup_session(sess_id);
++ if (!sess)
++ return 0;
++
++ g_atomic_int_inc(&global_conf.sessions_cap);
++ g_rw_lock_writer_lock(&sess->update_lock);
++ dummy.id = tree_conn_id;
++ tc_list = g_list_find_custom(sess->tree_conns,
++ &dummy,
++ lookup_tree_conn);
++ if (tc_list) {
++ struct ksmbd_tree_conn *tree_conn;
++
++ tree_conn = (struct ksmbd_tree_conn *)tc_list->data;
++ sess->tree_conns = g_list_remove(sess->tree_conns, tree_conn);
++ sess->ref_counter--;
++ tcm_tree_conn_free(tree_conn);
++ }
++ g_rw_lock_writer_unlock(&sess->update_lock);
++
++ put_ksmbd_user(sess->user);
++ __put_session(sess);
++ return 0;
++}
++
++void sm_destroy(void)
++{
++ if (sessions_table) {
++ sm_clear_sessions();
++ g_hash_table_destroy(sessions_table);
++ }
++ g_rw_lock_clear(&sessions_table_lock);
++}
++
++int sm_init(void)
++{
++ sessions_table = g_hash_table_new(g_int64_hash, g_int64_equal);
++ if (!sessions_table)
++ return -ENOMEM;
++ g_rw_lock_init(&sessions_table_lock);
++ return 0;
++}
+--- a/lib/management/share.c
++++ /dev/null
+@@ -1,868 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-#include <sys/types.h>
+-#include <pwd.h>
+-#include <grp.h>
+-
+-#include "config_parser.h"
+-#include "linux/ksmbd_server.h"
+-#include "management/share.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-#define KSMBD_SHARE_STATE_FREEING 1
+-
+-/*
+- * WARNING:
+- *
+- * This must match KSMBD_SHARE_CONF enum 1:1.
+- * Add new entries ONLY to the bottom.
+- */
+-char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = {
+- "comment", /* 0 */
+- "path",
+- "guest ok",
+- "guest account",
+- "read only",
+- "browseable", /* 5 */
+- "write ok",
+- "writeable",
+- "store dos attributes",
+- "oplocks",
+- "create mask", /* 10 */
+- "directory mask",
+- "force create mode",
+- "force directory mode",
+- "force group",
+- "force user", /* 15 */
+- "hide dot files",
+- "valid users",
+- "invalid users",
+- "read list",
+- "write list", /* 20 */
+- "admin users",
+- "hosts allow",
+- "hosts deny",
+- "max connections",
+- "veto files", /* 25 */
+- "inherit owner",
+- "follow symlinks",
+- "vfs objects",
+- "writable",
+-};
+-
+-static GHashTable *shares_table;
+-static GRWLock shares_table_lock;
+-
+-int shm_share_config(char *k, enum KSMBD_SHARE_CONF c)
+-{
+- if (c >= KSMBD_SHARE_CONF_MAX)
+- return 0;
+-
+- return !cp_key_cmp(k, KSMBD_SHARE_CONF[c]);
+-}
+-
+-static void list_hosts_callback(gpointer k, gpointer v, gpointer user_data)
+-{
+- free(k);
+- free(v);
+-}
+-
+-static void free_hosts_map(GHashTable *map)
+-{
+- if (map) {
+- g_hash_table_foreach(map, list_hosts_callback, NULL);
+- g_hash_table_destroy(map);
+- }
+-}
+-
+-static void list_user_callback(gpointer k, gpointer u, gpointer user_data)
+-{
+- put_ksmbd_user((struct ksmbd_user *)u);
+-}
+-
+-static void free_user_map(GHashTable *map)
+-{
+- if (map) {
+- g_hash_table_foreach(map, list_user_callback, NULL);
+- g_hash_table_destroy(map);
+- }
+-}
+-
+-static void kill_ksmbd_share(struct ksmbd_share *share)
+-{
+- int i;
+-
+- pr_debug("Kill share `%s'\n", share->name);
+-
+- for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
+- free_user_map(share->maps[i]);
+-
+- free_hosts_map(share->hosts_allow_map);
+- free_hosts_map(share->hosts_deny_map);
+-
+- g_rw_lock_clear(&share->maps_lock);
+-
+- free(share->name);
+- free(share->path);
+- free(share->comment);
+- free(share->veto_list);
+- free(share->guest_account);
+- g_rw_lock_clear(&share->update_lock);
+- g_free(share);
+-}
+-
+-static int __shm_remove_share(struct ksmbd_share *share)
+-{
+- int ret = 0;
+-
+- if (share->state != KSMBD_SHARE_STATE_FREEING) {
+- g_rw_lock_writer_lock(&shares_table_lock);
+- if (!g_hash_table_remove(shares_table, share->name))
+- ret = -EINVAL;
+- g_rw_lock_writer_unlock(&shares_table_lock);
+- }
+- if (!ret)
+- kill_ksmbd_share(share);
+- return ret;
+-}
+-
+-struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share)
+-{
+- g_rw_lock_writer_lock(&share->update_lock);
+- if (share->ref_count != 0) {
+- share->ref_count++;
+- g_rw_lock_writer_unlock(&share->update_lock);
+- } else {
+- g_rw_lock_writer_unlock(&share->update_lock);
+- share = NULL;
+- }
+-
+- return share;
+-}
+-
+-void put_ksmbd_share(struct ksmbd_share *share)
+-{
+- int drop;
+-
+- if (!share)
+- return;
+-
+- g_rw_lock_writer_lock(&share->update_lock);
+- share->ref_count--;
+- drop = !share->ref_count;
+- g_rw_lock_writer_unlock(&share->update_lock);
+-
+- if (!drop)
+- return;
+-
+- __shm_remove_share(share);
+-}
+-
+-static gboolean put_share_callback(gpointer _k, gpointer _v, gpointer data)
+-{
+- struct ksmbd_share *share = (struct ksmbd_share *)_v;
+-
+- share->state = KSMBD_SHARE_STATE_FREEING;
+- put_ksmbd_share(share);
+- return TRUE;
+-}
+-
+-void shm_remove_all_shares(void)
+-{
+- g_rw_lock_writer_lock(&shares_table_lock);
+- g_hash_table_foreach_remove(shares_table, put_share_callback, NULL);
+- g_rw_lock_writer_unlock(&shares_table_lock);
+-}
+-
+-static struct ksmbd_share *new_ksmbd_share(void)
+-{
+- struct ksmbd_share *share;
+- int i;
+-
+- share = g_try_malloc0(sizeof(struct ksmbd_share));
+- if (!share)
+- return NULL;
+-
+- share->ref_count = 1;
+- /*
+- * Create maps as needed. NULL maps means that share
+- * does not have a corresponding shmbconf entry.
+- */
+- for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
+- share->maps[i] = NULL;
+-
+- share->hosts_allow_map = NULL;
+- share->hosts_deny_map = NULL;
+- g_rw_lock_init(&share->maps_lock);
+- g_rw_lock_init(&share->update_lock);
+-
+- return share;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
+-{
+- kill_ksmbd_share(s);
+-}
+-
+-static void shm_clear_shares(void)
+-{
+- g_hash_table_foreach(shares_table, free_hash_entry, NULL);
+-}
+-
+-void shm_destroy(void)
+-{
+- if (shares_table) {
+- shm_clear_shares();
+- g_hash_table_destroy(shares_table);
+- }
+- g_rw_lock_clear(&shares_table_lock);
+-}
+-
+-static char *shm_casefold_share_name(const char *name, size_t len)
+-{
+- char *nfdi_name, *nfdicf_name;
+-
+- nfdi_name = g_utf8_normalize(name, len, G_NORMALIZE_NFD);
+- if (!nfdi_name)
+- goto out_ascii;
+-
+- nfdicf_name = g_utf8_casefold(nfdi_name, strlen(nfdi_name));
+- g_free(nfdi_name);
+- return nfdicf_name;
+-out_ascii:
+- g_free(nfdi_name);
+- return g_ascii_strdown(name, len);
+-}
+-
+-guint shm_share_name_hash(gconstpointer name)
+-{
+- char *cf_name;
+- guint hash;
+-
+- cf_name = shm_casefold_share_name(name, strlen(name));
+- hash = g_str_hash(cf_name);
+- g_free(cf_name);
+- return hash;
+-}
+-
+-gboolean shm_share_name_equal(gconstpointer lname, gconstpointer rname)
+-{
+- char *cf_lname, *cf_rname;
+- gboolean equal;
+-
+- cf_lname = shm_casefold_share_name(lname, strlen(lname));
+- cf_rname = shm_casefold_share_name(rname, strlen(rname));
+- equal = g_str_equal(cf_lname, cf_rname);
+- g_free(cf_lname);
+- g_free(cf_rname);
+- return equal;
+-}
+-
+-int shm_init(void)
+-{
+- shares_table = g_hash_table_new(shm_share_name_hash,
+- shm_share_name_equal);
+- if (!shares_table)
+- return -ENOMEM;
+- g_rw_lock_init(&shares_table_lock);
+- return 0;
+-}
+-
+-static struct ksmbd_share *__shm_lookup_share(char *name)
+-{
+- return g_hash_table_lookup(shares_table, name);
+-}
+-
+-struct ksmbd_share *shm_lookup_share(char *name)
+-{
+- struct ksmbd_share *share, *ret;
+-
+- g_rw_lock_reader_lock(&shares_table_lock);
+- share = __shm_lookup_share(name);
+- if (share) {
+- ret = get_ksmbd_share(share);
+- if (!ret)
+- share = NULL;
+- }
+- g_rw_lock_reader_unlock(&shares_table_lock);
+- return share;
+-}
+-
+-static GHashTable *parse_list(GHashTable *map, char **list, char grc)
+-{
+- int i;
+-
+- if (!list)
+- return map;
+-
+- if (!map)
+- map = g_hash_table_new(g_str_hash, g_str_equal);
+- if (!map)
+- return map;
+-
+- for (i = 0; list[i] != NULL; i++) {
+- struct ksmbd_user *user;
+- char *p = list[i];
+-
+- p = cp_ltrim(p);
+- if (!p)
+- continue;
+-
+- if (*p == grc) {
+- struct group *gr;
+-
+- gr = getgrnam(p + 1);
+- if (gr)
+- parse_list(map, gr->gr_mem, 0x00);
+- continue;
+- }
+-
+- user = usm_lookup_user(p);
+- if (!user) {
+- pr_info("Drop non-existing user `%s'\n", p);
+- continue;
+- }
+-
+- if (g_hash_table_lookup(map, user->name)) {
+- pr_debug("User `%s' already exists in a map\n",
+- user->name);
+- continue;
+- }
+-
+- g_hash_table_insert(map, user->name, user);
+- }
+-
+- return map;
+-}
+-
+-static void make_veto_list(struct ksmbd_share *share)
+-{
+- int i;
+-
+- for (i = 0; i < share->veto_list_sz; i++) {
+- if (share->veto_list[i] == '/')
+- share->veto_list[i] = 0x00;
+- }
+-}
+-
+-static void force_group(struct ksmbd_share *share, char *name)
+-{
+- struct group *grp;
+-
+- grp = getgrnam(name);
+- if (grp) {
+- share->force_gid = grp->gr_gid;
+- if (share->force_gid == KSMBD_SHARE_INVALID_GID)
+- pr_err("Invalid force GID: %u\n", share->force_gid);
+- } else
+- pr_err("Unable to lookup up `/etc/group' entry: %s\n", name);
+-}
+-
+-static void force_user(struct ksmbd_share *share, char *name)
+-{
+- struct passwd *passwd;
+-
+- passwd = getpwnam(name);
+- if (passwd) {
+- share->force_uid = passwd->pw_uid;
+- /*
+- * smb.conf 'force group' has higher priority than
+- * 'force user'.
+- */
+- if (share->force_gid == KSMBD_SHARE_INVALID_GID)
+- share->force_gid = passwd->pw_gid;
+- if (share->force_uid == KSMBD_SHARE_INVALID_UID ||
+- share->force_gid == KSMBD_SHARE_INVALID_GID)
+- pr_err("Invalid force UID/GID: %u/%u\n",
+- share->force_uid, share->force_gid);
+- } else {
+- pr_err("Unable to lookup up `/etc/passwd' entry: %s\n", name);
+- }
+-}
+-
+-static void process_group_kv(gpointer _k, gpointer _v, gpointer user_data)
+-{
+- struct ksmbd_share *share = user_data;
+- char *k = _k;
+- char *v = _v;
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) {
+- share->comment = cp_get_group_kv_string(v);
+- if (share->comment == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_PATH)) {
+- share->path = cp_get_group_kv_string(v);
+- if (share->path == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_OK)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) {
+- struct ksmbd_user *user;
+-
+- if (usm_add_new_user(cp_get_group_kv_string(_v),
+- g_strdup("NULL"))) {
+- pr_err("Unable to add guest account\n");
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- return;
+- }
+-
+- user = usm_lookup_user(_v);
+- if (user) {
+- set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
+- put_ksmbd_user(user);
+- }
+- share->guest_account = cp_get_group_kv_string(_v);
+- if (!share->guest_account)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_READ_ONLY)) {
+- if (cp_get_group_kv_bool(v)) {
+- set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+- clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+- } else {
+- clear_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+- set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+- }
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_BROWSEABLE)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+- else
+- clear_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_OK) ||
+- shm_share_config(k, KSMBD_SHARE_CONF_WRITEABLE) ||
+- shm_share_config(k, KSMBD_SHARE_CONF_WRITABLE)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+- else
+- clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+- else
+- clear_share_flag(share,
+- KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_OPLOCKS)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+- else
+- clear_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_CREATE_MASK)) {
+- share->create_mask = cp_get_group_kv_long_base(v, 8);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_DIRECTORY_MASK)) {
+- share->directory_mask = cp_get_group_kv_long_base(v, 8);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_CREATE_MODE)) {
+- share->force_create_mode = cp_get_group_kv_long_base(v, 8);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE)) {
+- share->force_directory_mode = cp_get_group_kv_long_base(v, 8);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_GROUP)) {
+- force_group(share, v);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) {
+- force_user(share, v);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_HIDE_DOT_FILES)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+- else
+- clear_share_flag(share,
+- KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) {
+- char **users_list;
+-
+- users_list = cp_get_group_kv_list(v);
+- share->maps[KSMBD_SHARE_VALID_USERS_MAP] =
+- parse_list(share->maps[KSMBD_SHARE_VALID_USERS_MAP],
+- users_list, '@');
+- if (share->maps[KSMBD_SHARE_VALID_USERS_MAP] == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(users_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) {
+- char **users_list;
+-
+- users_list = cp_get_group_kv_list(v);
+- share->maps[KSMBD_SHARE_INVALID_USERS_MAP] =
+- parse_list(share->maps[KSMBD_SHARE_INVALID_USERS_MAP],
+- users_list, '@');
+- if (share->maps[KSMBD_SHARE_INVALID_USERS_MAP] == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(users_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) {
+- char **users_list;
+-
+- users_list = cp_get_group_kv_list(v);
+- share->maps[KSMBD_SHARE_READ_LIST_MAP] =
+- parse_list(share->maps[KSMBD_SHARE_READ_LIST_MAP],
+- users_list, '@');
+- if (share->maps[KSMBD_SHARE_READ_LIST_MAP] == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(users_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) {
+- char **users_list;
+-
+- users_list = cp_get_group_kv_list(v);
+- share->maps[KSMBD_SHARE_WRITE_LIST_MAP] =
+- parse_list(share->maps[KSMBD_SHARE_WRITE_LIST_MAP],
+- users_list, '@');
+- if (share->maps[KSMBD_SHARE_WRITE_LIST_MAP] == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(users_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) {
+- char **users_list;
+-
+- users_list = cp_get_group_kv_list(v);
+- share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] =
+- parse_list(share->maps[KSMBD_SHARE_ADMIN_USERS_MAP],
+- users_list, '@');
+- if (share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(users_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) {
+- char **hosts_list;
+-
+- hosts_list = cp_get_group_kv_list(v);
+- share->hosts_allow_map = parse_list(share->hosts_allow_map,
+- hosts_list, 0x00);
+- if (share->hosts_allow_map == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(hosts_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) {
+- char **hosts_list;
+-
+- hosts_list = cp_get_group_kv_list(v);
+- share->hosts_deny_map = parse_list(share->hosts_deny_map,
+- hosts_list, 0x00);
+- if (share->hosts_deny_map == NULL)
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- cp_group_kv_list_free(hosts_list);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) {
+- share->max_connections = cp_get_group_kv_long_base(v, 10);
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) {
+- share->veto_list = cp_get_group_kv_string(v + 1);
+- if (share->veto_list == NULL) {
+- set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
+- } else {
+- share->veto_list_sz = strlen(share->veto_list);
+- make_veto_list(share);
+- }
+- return;
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_INHERIT_OWNER)) {
+- if (cp_get_group_kv_bool(v))
+- set_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
+- else
+- clear_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
+- }
+-
+- if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) {
+- char *p;
+- int i;
+- char **objects = cp_get_group_kv_list(v);
+-
+- if (objects) {
+- clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
+- clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
+- for (i = 0; objects[i] != NULL; i++) {
+- p = cp_ltrim(objects[i]);
+- if (!p)
+- continue;
+- if (!strcmp(p, "acl_xattr"))
+- set_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
+- else if (!strcmp(p, "streams_xattr"))
+- set_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
+- }
+- cp_group_kv_list_free(objects);
+- }
+- }
+-
+-}
+-
+-static void fixup_missing_fields(struct ksmbd_share *share)
+-{
+- if (!share->comment)
+- share->comment = strdup("");
+-}
+-
+-static void init_share_from_group(struct ksmbd_share *share,
+- struct smbconf_group *group)
+-{
+- share->name = g_strdup(group->name);
+- share->create_mask = KSMBD_SHARE_DEFAULT_CREATE_MASK;
+- share->directory_mask = KSMBD_SHARE_DEFAULT_DIRECTORY_MASK;
+- share->force_create_mode = 0;
+- share->force_directory_mode = 0;
+-
+- share->force_uid = KSMBD_SHARE_INVALID_UID;
+- share->force_gid = KSMBD_SHARE_INVALID_GID;
+-
+- set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE);
+- set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
+- set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
+- set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
+- set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
+- set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
+-
+- if (!g_ascii_strcasecmp(share->name, "ipc$"))
+- set_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
+-
+- if (group->cb_mode == GROUPS_CALLBACK_REINIT)
+- set_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
+-
+- g_hash_table_foreach(group->kv, process_group_kv, share);
+-
+- fixup_missing_fields(share);
+-}
+-
+-int shm_add_new_share(struct smbconf_group *group)
+-{
+- int ret = 0;
+- struct ksmbd_share *share = new_ksmbd_share();
+-
+- if (!share)
+- return -ENOMEM;
+-
+- init_share_from_group(share, group);
+- if (test_share_flag(share, KSMBD_SHARE_FLAG_INVALID)) {
+- pr_err("Share `%s' is invalid\n", share->name);
+- kill_ksmbd_share(share);
+- return 0;
+- }
+-
+- g_rw_lock_writer_lock(&shares_table_lock);
+- if (__shm_lookup_share(share->name)) {
+- g_rw_lock_writer_unlock(&shares_table_lock);
+- pr_info("Share `%s' already exists\n", share->name);
+- kill_ksmbd_share(share);
+- return 0;
+- }
+-
+- pr_debug("New share `%s'\n", share->name);
+- if (!g_hash_table_insert(shares_table, share->name, share)) {
+- kill_ksmbd_share(share);
+- ret = -EINVAL;
+- }
+- g_rw_lock_writer_unlock(&shares_table_lock);
+- return ret;
+-}
+-
+-int shm_lookup_users_map(struct ksmbd_share *share,
+- enum share_users map,
+- char *name)
+-{
+- int ret = -ENOENT;
+-
+- if (map >= KSMBD_SHARE_USERS_MAX) {
+- pr_err("Invalid users map index: %d\n", map);
+- return 0;
+- }
+-
+- if (!share->maps[map])
+- return -EINVAL;
+-
+- g_rw_lock_reader_lock(&share->maps_lock);
+- if (g_hash_table_lookup(share->maps[map], name))
+- ret = 0;
+- g_rw_lock_reader_unlock(&share->maps_lock);
+-
+- return ret;
+-}
+-
+-/*
+- * FIXME
+- * Do a real hosts lookup. IP masks, etc.
+- */
+-int shm_lookup_hosts_map(struct ksmbd_share *share,
+- enum share_hosts map,
+- char *host)
+-{
+- GHashTable *lookup_map = NULL;
+- int ret = -ENOENT;
+-
+- if (map >= KSMBD_SHARE_HOSTS_MAX) {
+- pr_err("Invalid hosts map index: %d\n", map);
+- return 0;
+- }
+-
+- if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP)
+- lookup_map = share->hosts_allow_map;
+- if (map == KSMBD_SHARE_HOSTS_DENY_MAP)
+- lookup_map = share->hosts_deny_map;
+-
+- if (!lookup_map)
+- return -EINVAL;
+-
+- g_rw_lock_reader_lock(&share->maps_lock);
+- if (g_hash_table_lookup(lookup_map, host))
+- ret = 0;
+- g_rw_lock_reader_unlock(&share->maps_lock);
+-
+- return ret;
+-}
+-
+-int shm_open_connection(struct ksmbd_share *share)
+-{
+- int ret = 0;
+-
+- g_rw_lock_writer_lock(&share->update_lock);
+- share->num_connections++;
+- if (share->max_connections) {
+- if (share->num_connections >= share->max_connections)
+- ret = -EINVAL;
+- }
+- g_rw_lock_writer_unlock(&share->update_lock);
+- return ret;
+-}
+-
+-int shm_close_connection(struct ksmbd_share *share)
+-{
+- if (!share)
+- return 0;
+-
+- g_rw_lock_writer_lock(&share->update_lock);
+- share->num_connections--;
+- g_rw_lock_writer_unlock(&share->update_lock);
+- return 0;
+-}
+-
+-void for_each_ksmbd_share(walk_shares cb, gpointer user_data)
+-{
+- g_rw_lock_reader_lock(&shares_table_lock);
+- g_hash_table_foreach(shares_table, cb, user_data);
+- g_rw_lock_reader_unlock(&shares_table_lock);
+-}
+-
+-int shm_share_config_payload_size(struct ksmbd_share *share)
+-{
+- int sz = 1;
+-
+- if (share && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
+- if (share->path)
+- sz += strlen(share->path);
+- if (global_conf.root_dir)
+- sz += strlen(global_conf.root_dir) + 1;
+- if (share->veto_list_sz)
+- sz += share->veto_list_sz + 1;
+- }
+-
+- return sz;
+-}
+-
+-int shm_handle_share_config_request(struct ksmbd_share *share,
+- struct ksmbd_share_config_response *resp)
+-{
+- unsigned char *config_payload;
+-
+- if (!share)
+- return -EINVAL;
+-
+- resp->flags = share->flags;
+- resp->create_mask = share->create_mask;
+- resp->directory_mask = share->directory_mask;
+- resp->force_create_mode = share->force_create_mode;
+- resp->force_directory_mode = share->force_directory_mode;
+- resp->force_uid = share->force_uid;
+- resp->force_gid = share->force_gid;
+- *resp->share_name = 0x00;
+- strncat(resp->share_name, share->name, KSMBD_REQ_MAX_SHARE_NAME - 1);
+- resp->veto_list_sz = share->veto_list_sz;
+-
+- if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE))
+- return 0;
+-
+- if (!share->path)
+- return 0;
+-
+- config_payload = KSMBD_SHARE_CONFIG_VETO_LIST(resp);
+- if (resp->veto_list_sz) {
+- memcpy(config_payload,
+- share->veto_list,
+- resp->veto_list_sz);
+- config_payload += resp->veto_list_sz + 1;
+- }
+- if (global_conf.root_dir)
+- sprintf(config_payload,
+- "%s%s",
+- global_conf.root_dir,
+- share->path);
+- else
+- sprintf(config_payload, "%s", share->path);
+- return 0;
+-}
+--- /dev/null
++++ b/tools/management/share.c
+@@ -0,0 +1,868 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++#include <sys/types.h>
++#include <pwd.h>
++#include <grp.h>
++
++#include "config_parser.h"
++#include "linux/ksmbd_server.h"
++#include "management/share.h"
++#include "management/user.h"
++#include "tools.h"
++
++#define KSMBD_SHARE_STATE_FREEING 1
++
++/*
++ * WARNING:
++ *
++ * This must match KSMBD_SHARE_CONF enum 1:1.
++ * Add new entries ONLY to the bottom.
++ */
++char *KSMBD_SHARE_CONF[KSMBD_SHARE_CONF_MAX] = {
++ "comment", /* 0 */
++ "path",
++ "guest ok",
++ "guest account",
++ "read only",
++ "browseable", /* 5 */
++ "write ok",
++ "writeable",
++ "store dos attributes",
++ "oplocks",
++ "create mask", /* 10 */
++ "directory mask",
++ "force create mode",
++ "force directory mode",
++ "force group",
++ "force user", /* 15 */
++ "hide dot files",
++ "valid users",
++ "invalid users",
++ "read list",
++ "write list", /* 20 */
++ "admin users",
++ "hosts allow",
++ "hosts deny",
++ "max connections",
++ "veto files", /* 25 */
++ "inherit owner",
++ "follow symlinks",
++ "vfs objects",
++ "writable",
++};
++
++static GHashTable *shares_table;
++static GRWLock shares_table_lock;
++
++int shm_share_config(char *k, enum KSMBD_SHARE_CONF c)
++{
++ if (c >= KSMBD_SHARE_CONF_MAX)
++ return 0;
++
++ return !cp_key_cmp(k, KSMBD_SHARE_CONF[c]);
++}
++
++static void list_hosts_callback(gpointer k, gpointer v, gpointer user_data)
++{
++ free(k);
++ free(v);
++}
++
++static void free_hosts_map(GHashTable *map)
++{
++ if (map) {
++ g_hash_table_foreach(map, list_hosts_callback, NULL);
++ g_hash_table_destroy(map);
++ }
++}
++
++static void list_user_callback(gpointer k, gpointer u, gpointer user_data)
++{
++ put_ksmbd_user((struct ksmbd_user *)u);
++}
++
++static void free_user_map(GHashTable *map)
++{
++ if (map) {
++ g_hash_table_foreach(map, list_user_callback, NULL);
++ g_hash_table_destroy(map);
++ }
++}
++
++static void kill_ksmbd_share(struct ksmbd_share *share)
++{
++ int i;
++
++ pr_debug("Kill share `%s'\n", share->name);
++
++ for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
++ free_user_map(share->maps[i]);
++
++ free_hosts_map(share->hosts_allow_map);
++ free_hosts_map(share->hosts_deny_map);
++
++ g_rw_lock_clear(&share->maps_lock);
++
++ free(share->name);
++ free(share->path);
++ free(share->comment);
++ free(share->veto_list);
++ free(share->guest_account);
++ g_rw_lock_clear(&share->update_lock);
++ g_free(share);
++}
++
++static int __shm_remove_share(struct ksmbd_share *share)
++{
++ int ret = 0;
++
++ if (share->state != KSMBD_SHARE_STATE_FREEING) {
++ g_rw_lock_writer_lock(&shares_table_lock);
++ if (!g_hash_table_remove(shares_table, share->name))
++ ret = -EINVAL;
++ g_rw_lock_writer_unlock(&shares_table_lock);
++ }
++ if (!ret)
++ kill_ksmbd_share(share);
++ return ret;
++}
++
++struct ksmbd_share *get_ksmbd_share(struct ksmbd_share *share)
++{
++ g_rw_lock_writer_lock(&share->update_lock);
++ if (share->ref_count != 0) {
++ share->ref_count++;
++ g_rw_lock_writer_unlock(&share->update_lock);
++ } else {
++ g_rw_lock_writer_unlock(&share->update_lock);
++ share = NULL;
++ }
++
++ return share;
++}
++
++void put_ksmbd_share(struct ksmbd_share *share)
++{
++ int drop;
++
++ if (!share)
++ return;
++
++ g_rw_lock_writer_lock(&share->update_lock);
++ share->ref_count--;
++ drop = !share->ref_count;
++ g_rw_lock_writer_unlock(&share->update_lock);
++
++ if (!drop)
++ return;
++
++ __shm_remove_share(share);
++}
++
++static gboolean put_share_callback(gpointer _k, gpointer _v, gpointer data)
++{
++ struct ksmbd_share *share = (struct ksmbd_share *)_v;
++
++ share->state = KSMBD_SHARE_STATE_FREEING;
++ put_ksmbd_share(share);
++ return TRUE;
++}
++
++void shm_remove_all_shares(void)
++{
++ g_rw_lock_writer_lock(&shares_table_lock);
++ g_hash_table_foreach_remove(shares_table, put_share_callback, NULL);
++ g_rw_lock_writer_unlock(&shares_table_lock);
++}
++
++static struct ksmbd_share *new_ksmbd_share(void)
++{
++ struct ksmbd_share *share;
++ int i;
++
++ share = g_try_malloc0(sizeof(struct ksmbd_share));
++ if (!share)
++ return NULL;
++
++ share->ref_count = 1;
++ /*
++ * Create maps as needed. NULL maps means that share
++ * does not have a corresponding shmbconf entry.
++ */
++ for (i = 0; i < KSMBD_SHARE_USERS_MAX; i++)
++ share->maps[i] = NULL;
++
++ share->hosts_allow_map = NULL;
++ share->hosts_deny_map = NULL;
++ g_rw_lock_init(&share->maps_lock);
++ g_rw_lock_init(&share->update_lock);
++
++ return share;
++}
++
++static void free_hash_entry(gpointer k, gpointer s, gpointer user_data)
++{
++ kill_ksmbd_share(s);
++}
++
++static void shm_clear_shares(void)
++{
++ g_hash_table_foreach(shares_table, free_hash_entry, NULL);
++}
++
++void shm_destroy(void)
++{
++ if (shares_table) {
++ shm_clear_shares();
++ g_hash_table_destroy(shares_table);
++ }
++ g_rw_lock_clear(&shares_table_lock);
++}
++
++static char *shm_casefold_share_name(const char *name, size_t len)
++{
++ char *nfdi_name, *nfdicf_name;
++
++ nfdi_name = g_utf8_normalize(name, len, G_NORMALIZE_NFD);
++ if (!nfdi_name)
++ goto out_ascii;
++
++ nfdicf_name = g_utf8_casefold(nfdi_name, strlen(nfdi_name));
++ g_free(nfdi_name);
++ return nfdicf_name;
++out_ascii:
++ g_free(nfdi_name);
++ return g_ascii_strdown(name, len);
++}
++
++guint shm_share_name_hash(gconstpointer name)
++{
++ char *cf_name;
++ guint hash;
++
++ cf_name = shm_casefold_share_name(name, strlen(name));
++ hash = g_str_hash(cf_name);
++ g_free(cf_name);
++ return hash;
++}
++
++gboolean shm_share_name_equal(gconstpointer lname, gconstpointer rname)
++{
++ char *cf_lname, *cf_rname;
++ gboolean equal;
++
++ cf_lname = shm_casefold_share_name(lname, strlen(lname));
++ cf_rname = shm_casefold_share_name(rname, strlen(rname));
++ equal = g_str_equal(cf_lname, cf_rname);
++ g_free(cf_lname);
++ g_free(cf_rname);
++ return equal;
++}
++
++int shm_init(void)
++{
++ shares_table = g_hash_table_new(shm_share_name_hash,
++ shm_share_name_equal);
++ if (!shares_table)
++ return -ENOMEM;
++ g_rw_lock_init(&shares_table_lock);
++ return 0;
++}
++
++static struct ksmbd_share *__shm_lookup_share(char *name)
++{
++ return g_hash_table_lookup(shares_table, name);
++}
++
++struct ksmbd_share *shm_lookup_share(char *name)
++{
++ struct ksmbd_share *share, *ret;
++
++ g_rw_lock_reader_lock(&shares_table_lock);
++ share = __shm_lookup_share(name);
++ if (share) {
++ ret = get_ksmbd_share(share);
++ if (!ret)
++ share = NULL;
++ }
++ g_rw_lock_reader_unlock(&shares_table_lock);
++ return share;
++}
++
++static GHashTable *parse_list(GHashTable *map, char **list, char grc)
++{
++ int i;
++
++ if (!list)
++ return map;
++
++ if (!map)
++ map = g_hash_table_new(g_str_hash, g_str_equal);
++ if (!map)
++ return map;
++
++ for (i = 0; list[i] != NULL; i++) {
++ struct ksmbd_user *user;
++ char *p = list[i];
++
++ p = cp_ltrim(p);
++ if (!p)
++ continue;
++
++ if (*p == grc) {
++ struct group *gr;
++
++ gr = getgrnam(p + 1);
++ if (gr)
++ parse_list(map, gr->gr_mem, 0x00);
++ continue;
++ }
++
++ user = usm_lookup_user(p);
++ if (!user) {
++ pr_info("Drop non-existing user `%s'\n", p);
++ continue;
++ }
++
++ if (g_hash_table_lookup(map, user->name)) {
++ pr_debug("User `%s' already exists in a map\n",
++ user->name);
++ continue;
++ }
++
++ g_hash_table_insert(map, user->name, user);
++ }
++
++ return map;
++}
++
++static void make_veto_list(struct ksmbd_share *share)
++{
++ int i;
++
++ for (i = 0; i < share->veto_list_sz; i++) {
++ if (share->veto_list[i] == '/')
++ share->veto_list[i] = 0x00;
++ }
++}
++
++static void force_group(struct ksmbd_share *share, char *name)
++{
++ struct group *grp;
++
++ grp = getgrnam(name);
++ if (grp) {
++ share->force_gid = grp->gr_gid;
++ if (share->force_gid == KSMBD_SHARE_INVALID_GID)
++ pr_err("Invalid force GID: %u\n", share->force_gid);
++ } else
++ pr_err("Unable to lookup up `/etc/group' entry: %s\n", name);
++}
++
++static void force_user(struct ksmbd_share *share, char *name)
++{
++ struct passwd *passwd;
++
++ passwd = getpwnam(name);
++ if (passwd) {
++ share->force_uid = passwd->pw_uid;
++ /*
++ * smb.conf 'force group' has higher priority than
++ * 'force user'.
++ */
++ if (share->force_gid == KSMBD_SHARE_INVALID_GID)
++ share->force_gid = passwd->pw_gid;
++ if (share->force_uid == KSMBD_SHARE_INVALID_UID ||
++ share->force_gid == KSMBD_SHARE_INVALID_GID)
++ pr_err("Invalid force UID/GID: %u/%u\n",
++ share->force_uid, share->force_gid);
++ } else {
++ pr_err("Unable to lookup up `/etc/passwd' entry: %s\n", name);
++ }
++}
++
++static void process_group_kv(gpointer _k, gpointer _v, gpointer user_data)
++{
++ struct ksmbd_share *share = user_data;
++ char *k = _k;
++ char *v = _v;
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_COMMENT)) {
++ share->comment = cp_get_group_kv_string(v);
++ if (share->comment == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_PATH)) {
++ share->path = cp_get_group_kv_string(v);
++ if (share->path == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_OK)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_GUEST_ACCOUNT)) {
++ struct ksmbd_user *user;
++
++ if (usm_add_new_user(cp_get_group_kv_string(_v),
++ g_strdup("NULL"))) {
++ pr_err("Unable to add guest account\n");
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ return;
++ }
++
++ user = usm_lookup_user(_v);
++ if (user) {
++ set_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT);
++ put_ksmbd_user(user);
++ }
++ share->guest_account = cp_get_group_kv_string(_v);
++ if (!share->guest_account)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_READ_ONLY)) {
++ if (cp_get_group_kv_bool(v)) {
++ set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++ clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++ } else {
++ clear_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++ set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++ }
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_BROWSEABLE)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++ else
++ clear_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_OK) ||
++ shm_share_config(k, KSMBD_SHARE_CONF_WRITEABLE) ||
++ shm_share_config(k, KSMBD_SHARE_CONF_WRITABLE)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++ else
++ clear_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_STORE_DOS_ATTRIBUTES)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++ else
++ clear_share_flag(share,
++ KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_OPLOCKS)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++ else
++ clear_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_CREATE_MASK)) {
++ share->create_mask = cp_get_group_kv_long_base(v, 8);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_DIRECTORY_MASK)) {
++ share->directory_mask = cp_get_group_kv_long_base(v, 8);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_CREATE_MODE)) {
++ share->force_create_mode = cp_get_group_kv_long_base(v, 8);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_DIRECTORY_MODE)) {
++ share->force_directory_mode = cp_get_group_kv_long_base(v, 8);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_GROUP)) {
++ force_group(share, v);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_FORCE_USER)) {
++ force_user(share, v);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_HIDE_DOT_FILES)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++ else
++ clear_share_flag(share,
++ KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_VALID_USERS)) {
++ char **users_list;
++
++ users_list = cp_get_group_kv_list(v);
++ share->maps[KSMBD_SHARE_VALID_USERS_MAP] =
++ parse_list(share->maps[KSMBD_SHARE_VALID_USERS_MAP],
++ users_list, '@');
++ if (share->maps[KSMBD_SHARE_VALID_USERS_MAP] == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(users_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_INVALID_USERS)) {
++ char **users_list;
++
++ users_list = cp_get_group_kv_list(v);
++ share->maps[KSMBD_SHARE_INVALID_USERS_MAP] =
++ parse_list(share->maps[KSMBD_SHARE_INVALID_USERS_MAP],
++ users_list, '@');
++ if (share->maps[KSMBD_SHARE_INVALID_USERS_MAP] == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(users_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_READ_LIST)) {
++ char **users_list;
++
++ users_list = cp_get_group_kv_list(v);
++ share->maps[KSMBD_SHARE_READ_LIST_MAP] =
++ parse_list(share->maps[KSMBD_SHARE_READ_LIST_MAP],
++ users_list, '@');
++ if (share->maps[KSMBD_SHARE_READ_LIST_MAP] == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(users_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_WRITE_LIST)) {
++ char **users_list;
++
++ users_list = cp_get_group_kv_list(v);
++ share->maps[KSMBD_SHARE_WRITE_LIST_MAP] =
++ parse_list(share->maps[KSMBD_SHARE_WRITE_LIST_MAP],
++ users_list, '@');
++ if (share->maps[KSMBD_SHARE_WRITE_LIST_MAP] == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(users_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_ADMIN_USERS)) {
++ char **users_list;
++
++ users_list = cp_get_group_kv_list(v);
++ share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] =
++ parse_list(share->maps[KSMBD_SHARE_ADMIN_USERS_MAP],
++ users_list, '@');
++ if (share->maps[KSMBD_SHARE_ADMIN_USERS_MAP] == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(users_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_ALLOW)) {
++ char **hosts_list;
++
++ hosts_list = cp_get_group_kv_list(v);
++ share->hosts_allow_map = parse_list(share->hosts_allow_map,
++ hosts_list, 0x00);
++ if (share->hosts_allow_map == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(hosts_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_HOSTS_DENY)) {
++ char **hosts_list;
++
++ hosts_list = cp_get_group_kv_list(v);
++ share->hosts_deny_map = parse_list(share->hosts_deny_map,
++ hosts_list, 0x00);
++ if (share->hosts_deny_map == NULL)
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ cp_group_kv_list_free(hosts_list);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_MAX_CONNECTIONS)) {
++ share->max_connections = cp_get_group_kv_long_base(v, 10);
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_VETO_FILES)) {
++ share->veto_list = cp_get_group_kv_string(v + 1);
++ if (share->veto_list == NULL) {
++ set_share_flag(share, KSMBD_SHARE_FLAG_INVALID);
++ } else {
++ share->veto_list_sz = strlen(share->veto_list);
++ make_veto_list(share);
++ }
++ return;
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_INHERIT_OWNER)) {
++ if (cp_get_group_kv_bool(v))
++ set_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
++ else
++ clear_share_flag(share, KSMBD_SHARE_FLAG_INHERIT_OWNER);
++ }
++
++ if (shm_share_config(k, KSMBD_SHARE_CONF_VFS_OBJECTS)) {
++ char *p;
++ int i;
++ char **objects = cp_get_group_kv_list(v);
++
++ if (objects) {
++ clear_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
++ clear_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
++ for (i = 0; objects[i] != NULL; i++) {
++ p = cp_ltrim(objects[i]);
++ if (!p)
++ continue;
++ if (!strcmp(p, "acl_xattr"))
++ set_share_flag(share, KSMBD_SHARE_FLAG_ACL_XATTR);
++ else if (!strcmp(p, "streams_xattr"))
++ set_share_flag(share, KSMBD_SHARE_FLAG_STREAMS);
++ }
++ cp_group_kv_list_free(objects);
++ }
++ }
++
++}
++
++static void fixup_missing_fields(struct ksmbd_share *share)
++{
++ if (!share->comment)
++ share->comment = strdup("");
++}
++
++static void init_share_from_group(struct ksmbd_share *share,
++ struct smbconf_group *group)
++{
++ share->name = g_strdup(group->name);
++ share->create_mask = KSMBD_SHARE_DEFAULT_CREATE_MASK;
++ share->directory_mask = KSMBD_SHARE_DEFAULT_DIRECTORY_MASK;
++ share->force_create_mode = 0;
++ share->force_directory_mode = 0;
++
++ share->force_uid = KSMBD_SHARE_INVALID_UID;
++ share->force_gid = KSMBD_SHARE_INVALID_GID;
++
++ set_share_flag(share, KSMBD_SHARE_FLAG_AVAILABLE);
++ set_share_flag(share, KSMBD_SHARE_FLAG_BROWSEABLE);
++ set_share_flag(share, KSMBD_SHARE_FLAG_READONLY);
++ set_share_flag(share, KSMBD_SHARE_FLAG_HIDE_DOT_FILES);
++ set_share_flag(share, KSMBD_SHARE_FLAG_OPLOCKS);
++ set_share_flag(share, KSMBD_SHARE_FLAG_STORE_DOS_ATTRS);
++
++ if (!g_ascii_strcasecmp(share->name, "ipc$"))
++ set_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
++
++ if (group->cb_mode == GROUPS_CALLBACK_REINIT)
++ set_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
++
++ g_hash_table_foreach(group->kv, process_group_kv, share);
++
++ fixup_missing_fields(share);
++}
++
++int shm_add_new_share(struct smbconf_group *group)
++{
++ int ret = 0;
++ struct ksmbd_share *share = new_ksmbd_share();
++
++ if (!share)
++ return -ENOMEM;
++
++ init_share_from_group(share, group);
++ if (test_share_flag(share, KSMBD_SHARE_FLAG_INVALID)) {
++ pr_err("Share `%s' is invalid\n", share->name);
++ kill_ksmbd_share(share);
++ return 0;
++ }
++
++ g_rw_lock_writer_lock(&shares_table_lock);
++ if (__shm_lookup_share(share->name)) {
++ g_rw_lock_writer_unlock(&shares_table_lock);
++ pr_info("Share `%s' already exists\n", share->name);
++ kill_ksmbd_share(share);
++ return 0;
++ }
++
++ pr_debug("New share `%s'\n", share->name);
++ if (!g_hash_table_insert(shares_table, share->name, share)) {
++ kill_ksmbd_share(share);
++ ret = -EINVAL;
++ }
++ g_rw_lock_writer_unlock(&shares_table_lock);
++ return ret;
++}
++
++int shm_lookup_users_map(struct ksmbd_share *share,
++ enum share_users map,
++ char *name)
++{
++ int ret = -ENOENT;
++
++ if (map >= KSMBD_SHARE_USERS_MAX) {
++ pr_err("Invalid users map index: %d\n", map);
++ return 0;
++ }
++
++ if (!share->maps[map])
++ return -EINVAL;
++
++ g_rw_lock_reader_lock(&share->maps_lock);
++ if (g_hash_table_lookup(share->maps[map], name))
++ ret = 0;
++ g_rw_lock_reader_unlock(&share->maps_lock);
++
++ return ret;
++}
++
++/*
++ * FIXME
++ * Do a real hosts lookup. IP masks, etc.
++ */
++int shm_lookup_hosts_map(struct ksmbd_share *share,
++ enum share_hosts map,
++ char *host)
++{
++ GHashTable *lookup_map = NULL;
++ int ret = -ENOENT;
++
++ if (map >= KSMBD_SHARE_HOSTS_MAX) {
++ pr_err("Invalid hosts map index: %d\n", map);
++ return 0;
++ }
++
++ if (map == KSMBD_SHARE_HOSTS_ALLOW_MAP)
++ lookup_map = share->hosts_allow_map;
++ if (map == KSMBD_SHARE_HOSTS_DENY_MAP)
++ lookup_map = share->hosts_deny_map;
++
++ if (!lookup_map)
++ return -EINVAL;
++
++ g_rw_lock_reader_lock(&share->maps_lock);
++ if (g_hash_table_lookup(lookup_map, host))
++ ret = 0;
++ g_rw_lock_reader_unlock(&share->maps_lock);
++
++ return ret;
++}
++
++int shm_open_connection(struct ksmbd_share *share)
++{
++ int ret = 0;
++
++ g_rw_lock_writer_lock(&share->update_lock);
++ share->num_connections++;
++ if (share->max_connections) {
++ if (share->num_connections >= share->max_connections)
++ ret = -EINVAL;
++ }
++ g_rw_lock_writer_unlock(&share->update_lock);
++ return ret;
++}
++
++int shm_close_connection(struct ksmbd_share *share)
++{
++ if (!share)
++ return 0;
++
++ g_rw_lock_writer_lock(&share->update_lock);
++ share->num_connections--;
++ g_rw_lock_writer_unlock(&share->update_lock);
++ return 0;
++}
++
++void for_each_ksmbd_share(walk_shares cb, gpointer user_data)
++{
++ g_rw_lock_reader_lock(&shares_table_lock);
++ g_hash_table_foreach(shares_table, cb, user_data);
++ g_rw_lock_reader_unlock(&shares_table_lock);
++}
++
++int shm_share_config_payload_size(struct ksmbd_share *share)
++{
++ int sz = 1;
++
++ if (share && !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE)) {
++ if (share->path)
++ sz += strlen(share->path);
++ if (global_conf.root_dir)
++ sz += strlen(global_conf.root_dir) + 1;
++ if (share->veto_list_sz)
++ sz += share->veto_list_sz + 1;
++ }
++
++ return sz;
++}
++
++int shm_handle_share_config_request(struct ksmbd_share *share,
++ struct ksmbd_share_config_response *resp)
++{
++ unsigned char *config_payload;
++
++ if (!share)
++ return -EINVAL;
++
++ resp->flags = share->flags;
++ resp->create_mask = share->create_mask;
++ resp->directory_mask = share->directory_mask;
++ resp->force_create_mode = share->force_create_mode;
++ resp->force_directory_mode = share->force_directory_mode;
++ resp->force_uid = share->force_uid;
++ resp->force_gid = share->force_gid;
++ *resp->share_name = 0x00;
++ strncat(resp->share_name, share->name, KSMBD_REQ_MAX_SHARE_NAME - 1);
++ resp->veto_list_sz = share->veto_list_sz;
++
++ if (test_share_flag(share, KSMBD_SHARE_FLAG_PIPE))
++ return 0;
++
++ if (!share->path)
++ return 0;
++
++ config_payload = KSMBD_SHARE_CONFIG_VETO_LIST(resp);
++ if (resp->veto_list_sz) {
++ memcpy(config_payload,
++ share->veto_list,
++ resp->veto_list_sz);
++ config_payload += resp->veto_list_sz + 1;
++ }
++ if (global_conf.root_dir)
++ sprintf(config_payload,
++ "%s%s",
++ global_conf.root_dir,
++ share->path);
++ else
++ sprintf(config_payload, "%s", share->path);
++ return 0;
++}
+--- a/lib/management/spnego.c
++++ /dev/null
+@@ -1,348 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2020 LG Electronics
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include "ksmbdtools.h"
+-
+-#ifndef _GNU_SOURCE
+-#define _GNU_SOURCE
+-#endif
+-
+-#include <stdlib.h>
+-#include <stdio.h>
+-#include <unistd.h>
+-#include <sys/types.h>
+-#include <fcntl.h>
+-#include <stdint.h>
+-#include <stdbool.h>
+-
+-#include <linux/ksmbd_server.h>
+-#include <management/spnego.h>
+-#include <asn1.h>
+-#include "spnego_mech.h"
+-
+-static struct spnego_mech_ctx mech_ctxs[SPNEGO_MAX_MECHS];
+-
+-static struct spnego_mech_ctx *get_mech(int mech_type)
+-{
+- if (mech_type >= SPNEGO_MAX_MECHS)
+- return NULL;
+- return &mech_ctxs[mech_type];
+-}
+-
+-int spnego_init(void)
+-{
+- struct spnego_mech_ctx *mech_ctx;
+- int i;
+-
+- mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5];
+- mech_ctx->ops = &spnego_mskrb5_operations;
+- if (global_conf.krb5_service_name)
+- mech_ctx->params.krb5.service_name =
+- strdup(global_conf.krb5_service_name);
+- if (global_conf.krb5_keytab_file)
+- mech_ctx->params.krb5.keytab_name =
+- strdup(global_conf.krb5_keytab_file);
+-
+- mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5];
+- mech_ctx->ops = &spnego_krb5_operations;
+- if (global_conf.krb5_service_name)
+- mech_ctx->params.krb5.service_name =
+- strdup(global_conf.krb5_service_name);
+- if (global_conf.krb5_keytab_file)
+- mech_ctx->params.krb5.keytab_name =
+- strdup(global_conf.krb5_keytab_file);
+-
+- for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
+- if (mech_ctxs[i].ops->setup &&
+- mech_ctxs[i].ops->setup(&mech_ctxs[i])) {
+- pr_err("Failed to init Kerberos 5\n");
+- goto out_err;
+- }
+- }
+-
+- return 0;
+-out_err:
+- for (; i >= 0; i--) {
+- if (mech_ctxs[i].ops->cleanup)
+- mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
+- }
+- return -ENOTSUP;
+-}
+-
+-void spnego_destroy(void)
+-{
+- int i;
+-
+- for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
+- if (mech_ctxs[i].ops && mech_ctxs[i].ops->cleanup)
+- mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
+- }
+-}
+-
+-static int compare_oid(unsigned long *oid1, unsigned int oid1len,
+- unsigned long *oid2, unsigned int oid2len)
+-{
+- unsigned int i;
+-
+- if (oid1len != oid2len)
+- return 1;
+-
+- for (i = 0; i < oid1len; i++) {
+- if (oid1[i] != oid2[i])
+- return 1;
+- }
+- return 0;
+-}
+-
+-static bool is_supported_mech(unsigned long *oid, unsigned int len,
+- int *mech_type)
+-{
+- if (!compare_oid(oid, len, MSKRB5_OID, ARRAY_SIZE(MSKRB5_OID))) {
+- *mech_type = SPNEGO_MECH_MSKRB5;
+- return true;
+- }
+-
+- if (!compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) {
+- *mech_type = SPNEGO_MECH_KRB5;
+- return true;
+- }
+-
+- *mech_type = SPNEGO_MAX_MECHS;
+- return false;
+-}
+-
+-static int decode_asn1_header(struct asn1_ctx *ctx, unsigned char **end,
+- unsigned int cls, unsigned int con, unsigned int tag)
+-{
+- unsigned int d_cls, d_con, d_tag;
+-
+- if (asn1_header_decode(ctx, end, &d_cls, &d_con, &d_tag) == 0 ||
+- (d_cls != cls || d_con != con || d_tag != tag))
+- return -EINVAL;
+- return 0;
+-}
+-
+-static int decode_negTokenInit(unsigned char *negToken, int token_len,
+- int *mech_type, unsigned char **krb5_ap_req,
+- unsigned int *req_len)
+-{
+- struct asn1_ctx ctx;
+- unsigned char *end, *mech_types_end, *id;
+- unsigned long *oid = NULL;
+- unsigned int len;
+-
+- asn1_open(&ctx, negToken, token_len);
+-
+- /* GSSAPI header */
+- if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
+- pr_debug("Error decoding SPNEGO application tag\n");
+- return -EINVAL;
+- }
+-
+- /* SPNEGO oid */
+- if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
+- asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
+- compare_oid(oid, len, SPNEGO_OID, SPNEGO_OID_LEN)) {
+- pr_debug("Error decoding SPNEGO OID\n");
+- g_free(oid);
+- return -EINVAL;
+- }
+- g_free(oid);
+-
+- /* negoTokenInit */
+- if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
+- decode_asn1_header(&ctx, &end,
+- ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
+- pr_debug("Error decoding negTokenInit tag\n");
+- return -EINVAL;
+- }
+-
+- /* mechTypes */
+- if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
+- decode_asn1_header(&ctx, &end,
+- ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
+- pr_debug("Error decoding mechTypes tag\n");
+- return -EINVAL;
+- }
+-
+- mech_types_end = end;
+- if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
+- asn1_oid_decode(&ctx, end, &oid, &len) == 0) {
+- pr_debug("Error decoding Kerberos 5 OIDs\n");
+- return -EINVAL;
+- }
+-
+- if (!is_supported_mech(oid, len, mech_type)) {
+- g_free(oid);
+- pr_debug("Not a supported mechanism\n");
+- return -EINVAL;
+- }
+- g_free(oid);
+-
+- ctx.pointer = mech_types_end;
+- /* mechToken */
+- if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 2) ||
+- decode_asn1_header(&ctx, &end,
+- ASN1_UNI, ASN1_PRI, ASN1_OTS)) {
+- pr_debug("Error decoding krb5_blob\n");
+- return -EINVAL;
+- }
+-
+- if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
+- pr_debug("Error decoding GSSAPI application tag\n");
+- return -EINVAL;
+- }
+-
+- /* Kerberos 5 oid */
+- if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI)) {
+- pr_debug("Error decoding Kerberos 5 OID tag\n");
+- return -EINVAL;
+- }
+-
+- if (asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
+- compare_oid(oid, len, KRB5_OID,
+- ARRAY_SIZE(KRB5_OID))) {
+- pr_debug("Not a Kerberos 5 OID\n");
+- g_free(oid);
+- return -EINVAL;
+- }
+- g_free(oid);
+-
+- /* AP_REQ id */
+- if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) {
+- if (id)
+- free(id);
+- pr_debug("Error decoding AP_REQ ID\n");
+- return -EINVAL;
+- }
+- free(id);
+-
+- /* AP_REQ */
+- *req_len = (unsigned int)(ctx.end - ctx.pointer);
+- *krb5_ap_req = ctx.pointer;
+- return 0;
+-}
+-
+-static int encode_negTokenTarg(char *in_blob, int in_len,
+- const unsigned long *oid, int oid_len,
+- char **out_blob, int *out_len)
+-{
+- unsigned char *buf;
+- unsigned char *sup_oid, *krb5_oid;
+- int sup_oid_len, krb5_oid_len;
+- unsigned int neg_result_len, sup_mech_len, rep_token_len, len;
+-
+- if (asn1_oid_encode(oid, oid_len, &sup_oid, &sup_oid_len))
+- return -ENOMEM;
+- if (asn1_oid_encode(KRB5_OID, ARRAY_SIZE(KRB5_OID),
+- &krb5_oid, &krb5_oid_len)) {
+- g_free(sup_oid);
+- return -ENOMEM;
+- }
+-
+- neg_result_len = asn1_header_len(1, 2);
+- sup_mech_len = asn1_header_len(sup_oid_len, 2);
+- rep_token_len = asn1_header_len(krb5_oid_len, 1);
+- rep_token_len += 2 + in_len;
+- rep_token_len = asn1_header_len(rep_token_len, 3);
+-
+- *out_len = asn1_header_len(
+- neg_result_len + sup_mech_len + rep_token_len, 2);
+- *out_blob = g_try_malloc0(*out_len);
+- if (*out_blob == NULL)
+- return -ENOMEM;
+- buf = *out_blob;
+-
+- /* negTokenTarg */
+- len = *out_len;
+- asn1_header_encode(&buf,
+- ASN1_CTX, ASN1_CON, 1,
+- &len);
+- asn1_header_encode(&buf,
+- ASN1_UNI, ASN1_CON, ASN1_SEQ,
+- &len);
+-
+- /* negTokenTarg/negResult */
+- len = neg_result_len;
+- asn1_header_encode(&buf,
+- ASN1_CTX, ASN1_CON, 0,
+- &len);
+- asn1_header_encode(&buf,
+- ASN1_UNI, ASN1_PRI, ASN1_ENUM,
+- &len);
+- *buf++ = 0;
+-
+- /* negTokenTarg/supportedMechType */
+- len = sup_mech_len;
+- asn1_header_encode(&buf,
+- ASN1_CTX, ASN1_CON, 1,
+- &len);
+- asn1_header_encode(&buf,
+- ASN1_UNI, ASN1_PRI, ASN1_OJI,
+- &len);
+- memcpy(buf, sup_oid, sup_oid_len);
+- buf += len;
+-
+- /* negTokenTarg/responseToken */
+- len = rep_token_len;
+- asn1_header_encode(&buf,
+- ASN1_CTX, ASN1_CON, 2,
+- &len);
+- asn1_header_encode(&buf,
+- ASN1_UNI, ASN1_PRI, ASN1_OTS,
+- &len);
+- asn1_header_encode(&buf,
+- ASN1_APL, ASN1_CON, 0,
+- &len);
+- /* negTokenTarg/responseToken/OID */
+- len = asn1_header_len(krb5_oid_len, 1);
+- asn1_header_encode(&buf,
+- ASN1_UNI, ASN1_PRI, ASN1_OJI,
+- &len);
+- /* negTokenTarg/responseToken/mechToken*/
+- memcpy(buf, krb5_oid, krb5_oid_len);
+- buf += len;
+- /* AP_REP id */
+- *buf++ = 2;
+- *buf++ = 0;
+- memcpy(buf, in_blob, in_len);
+-
+- g_free(sup_oid);
+- g_free(krb5_oid);
+-}
+-
+-int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req,
+- struct ksmbd_spnego_auth_out *auth_out)
+-{
+- struct spnego_mech_ctx *mech_ctx;
+- unsigned char *mech_token;
+- int token_len, mech_type;
+- int retval = 0;
+-
+- if (decode_negTokenInit(req->spnego_blob, (int)req->spnego_blob_len,
+- &mech_type, &mech_token, &token_len)) {
+- pr_info("Error decoding negTokenInit\n");
+- return -EINVAL;
+- }
+-
+- mech_ctx = get_mech(mech_type);
+- if (!mech_ctx) {
+- retval = -ENOTSUP;
+- pr_info("No support for Kerberos 5\n");
+- goto out;
+- }
+-
+- if (mech_ctx->ops->handle_authen(mech_ctx,
+- mech_token, token_len,
+- auth_out, encode_negTokenTarg)) {
+- retval = -EPERM;
+- pr_info("Error authenticating\n");
+- goto out;
+- }
+-out:
+- return retval;
+-}
+--- /dev/null
++++ b/tools/management/spnego.c
+@@ -0,0 +1,348 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2020 LG Electronics
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include "tools.h"
++
++#ifndef _GNU_SOURCE
++#define _GNU_SOURCE
++#endif
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <fcntl.h>
++#include <stdint.h>
++#include <stdbool.h>
++
++#include <linux/ksmbd_server.h>
++#include <management/spnego.h>
++#include <asn1.h>
++#include "spnego_mech.h"
++
++static struct spnego_mech_ctx mech_ctxs[SPNEGO_MAX_MECHS];
++
++static struct spnego_mech_ctx *get_mech(int mech_type)
++{
++ if (mech_type >= SPNEGO_MAX_MECHS)
++ return NULL;
++ return &mech_ctxs[mech_type];
++}
++
++int spnego_init(void)
++{
++ struct spnego_mech_ctx *mech_ctx;
++ int i;
++
++ mech_ctx = &mech_ctxs[SPNEGO_MECH_MSKRB5];
++ mech_ctx->ops = &spnego_mskrb5_operations;
++ if (global_conf.krb5_service_name)
++ mech_ctx->params.krb5.service_name =
++ strdup(global_conf.krb5_service_name);
++ if (global_conf.krb5_keytab_file)
++ mech_ctx->params.krb5.keytab_name =
++ strdup(global_conf.krb5_keytab_file);
++
++ mech_ctx = &mech_ctxs[SPNEGO_MECH_KRB5];
++ mech_ctx->ops = &spnego_krb5_operations;
++ if (global_conf.krb5_service_name)
++ mech_ctx->params.krb5.service_name =
++ strdup(global_conf.krb5_service_name);
++ if (global_conf.krb5_keytab_file)
++ mech_ctx->params.krb5.keytab_name =
++ strdup(global_conf.krb5_keytab_file);
++
++ for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
++ if (mech_ctxs[i].ops->setup &&
++ mech_ctxs[i].ops->setup(&mech_ctxs[i])) {
++ pr_err("Failed to init Kerberos 5\n");
++ goto out_err;
++ }
++ }
++
++ return 0;
++out_err:
++ for (; i >= 0; i--) {
++ if (mech_ctxs[i].ops->cleanup)
++ mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
++ }
++ return -ENOTSUP;
++}
++
++void spnego_destroy(void)
++{
++ int i;
++
++ for (i = 0; i < SPNEGO_MAX_MECHS; i++) {
++ if (mech_ctxs[i].ops && mech_ctxs[i].ops->cleanup)
++ mech_ctxs[i].ops->cleanup(&mech_ctxs[i]);
++ }
++}
++
++static int compare_oid(unsigned long *oid1, unsigned int oid1len,
++ unsigned long *oid2, unsigned int oid2len)
++{
++ unsigned int i;
++
++ if (oid1len != oid2len)
++ return 1;
++
++ for (i = 0; i < oid1len; i++) {
++ if (oid1[i] != oid2[i])
++ return 1;
++ }
++ return 0;
++}
++
++static bool is_supported_mech(unsigned long *oid, unsigned int len,
++ int *mech_type)
++{
++ if (!compare_oid(oid, len, MSKRB5_OID, ARRAY_SIZE(MSKRB5_OID))) {
++ *mech_type = SPNEGO_MECH_MSKRB5;
++ return true;
++ }
++
++ if (!compare_oid(oid, len, KRB5_OID, ARRAY_SIZE(KRB5_OID))) {
++ *mech_type = SPNEGO_MECH_KRB5;
++ return true;
++ }
++
++ *mech_type = SPNEGO_MAX_MECHS;
++ return false;
++}
++
++static int decode_asn1_header(struct asn1_ctx *ctx, unsigned char **end,
++ unsigned int cls, unsigned int con, unsigned int tag)
++{
++ unsigned int d_cls, d_con, d_tag;
++
++ if (asn1_header_decode(ctx, end, &d_cls, &d_con, &d_tag) == 0 ||
++ (d_cls != cls || d_con != con || d_tag != tag))
++ return -EINVAL;
++ return 0;
++}
++
++static int decode_negTokenInit(unsigned char *negToken, int token_len,
++ int *mech_type, unsigned char **krb5_ap_req,
++ unsigned int *req_len)
++{
++ struct asn1_ctx ctx;
++ unsigned char *end, *mech_types_end, *id;
++ unsigned long *oid = NULL;
++ unsigned int len;
++
++ asn1_open(&ctx, negToken, token_len);
++
++ /* GSSAPI header */
++ if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
++ pr_debug("Error decoding SPNEGO application tag\n");
++ return -EINVAL;
++ }
++
++ /* SPNEGO oid */
++ if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
++ asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
++ compare_oid(oid, len, SPNEGO_OID, SPNEGO_OID_LEN)) {
++ pr_debug("Error decoding SPNEGO OID\n");
++ g_free(oid);
++ return -EINVAL;
++ }
++ g_free(oid);
++
++ /* negoTokenInit */
++ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
++ decode_asn1_header(&ctx, &end,
++ ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
++ pr_debug("Error decoding negTokenInit tag\n");
++ return -EINVAL;
++ }
++
++ /* mechTypes */
++ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 0) ||
++ decode_asn1_header(&ctx, &end,
++ ASN1_UNI, ASN1_CON, ASN1_SEQ)) {
++ pr_debug("Error decoding mechTypes tag\n");
++ return -EINVAL;
++ }
++
++ mech_types_end = end;
++ if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI) ||
++ asn1_oid_decode(&ctx, end, &oid, &len) == 0) {
++ pr_debug("Error decoding Kerberos 5 OIDs\n");
++ return -EINVAL;
++ }
++
++ if (!is_supported_mech(oid, len, mech_type)) {
++ g_free(oid);
++ pr_debug("Not a supported mechanism\n");
++ return -EINVAL;
++ }
++ g_free(oid);
++
++ ctx.pointer = mech_types_end;
++ /* mechToken */
++ if (decode_asn1_header(&ctx, &end, ASN1_CTX, ASN1_CON, 2) ||
++ decode_asn1_header(&ctx, &end,
++ ASN1_UNI, ASN1_PRI, ASN1_OTS)) {
++ pr_debug("Error decoding krb5_blob\n");
++ return -EINVAL;
++ }
++
++ if (decode_asn1_header(&ctx, &end, ASN1_APL, ASN1_CON, ASN1_EOC)) {
++ pr_debug("Error decoding GSSAPI application tag\n");
++ return -EINVAL;
++ }
++
++ /* Kerberos 5 oid */
++ if (decode_asn1_header(&ctx, &end, ASN1_UNI, ASN1_PRI, ASN1_OJI)) {
++ pr_debug("Error decoding Kerberos 5 OID tag\n");
++ return -EINVAL;
++ }
++
++ if (asn1_oid_decode(&ctx, end, &oid, &len) == 0 ||
++ compare_oid(oid, len, KRB5_OID,
++ ARRAY_SIZE(KRB5_OID))) {
++ pr_debug("Not a Kerberos 5 OID\n");
++ g_free(oid);
++ return -EINVAL;
++ }
++ g_free(oid);
++
++ /* AP_REQ id */
++ if (asn1_read(&ctx, &id, 2) == 0 || id[0] != 1 || id[1] != 0) {
++ if (id)
++ free(id);
++ pr_debug("Error decoding AP_REQ ID\n");
++ return -EINVAL;
++ }
++ free(id);
++
++ /* AP_REQ */
++ *req_len = (unsigned int)(ctx.end - ctx.pointer);
++ *krb5_ap_req = ctx.pointer;
++ return 0;
++}
++
++static int encode_negTokenTarg(char *in_blob, int in_len,
++ const unsigned long *oid, int oid_len,
++ char **out_blob, int *out_len)
++{
++ unsigned char *buf;
++ unsigned char *sup_oid, *krb5_oid;
++ int sup_oid_len, krb5_oid_len;
++ unsigned int neg_result_len, sup_mech_len, rep_token_len, len;
++
++ if (asn1_oid_encode(oid, oid_len, &sup_oid, &sup_oid_len))
++ return -ENOMEM;
++ if (asn1_oid_encode(KRB5_OID, ARRAY_SIZE(KRB5_OID),
++ &krb5_oid, &krb5_oid_len)) {
++ g_free(sup_oid);
++ return -ENOMEM;
++ }
++
++ neg_result_len = asn1_header_len(1, 2);
++ sup_mech_len = asn1_header_len(sup_oid_len, 2);
++ rep_token_len = asn1_header_len(krb5_oid_len, 1);
++ rep_token_len += 2 + in_len;
++ rep_token_len = asn1_header_len(rep_token_len, 3);
++
++ *out_len = asn1_header_len(
++ neg_result_len + sup_mech_len + rep_token_len, 2);
++ *out_blob = g_try_malloc0(*out_len);
++ if (*out_blob == NULL)
++ return -ENOMEM;
++ buf = *out_blob;
++
++ /* negTokenTarg */
++ len = *out_len;
++ asn1_header_encode(&buf,
++ ASN1_CTX, ASN1_CON, 1,
++ &len);
++ asn1_header_encode(&buf,
++ ASN1_UNI, ASN1_CON, ASN1_SEQ,
++ &len);
++
++ /* negTokenTarg/negResult */
++ len = neg_result_len;
++ asn1_header_encode(&buf,
++ ASN1_CTX, ASN1_CON, 0,
++ &len);
++ asn1_header_encode(&buf,
++ ASN1_UNI, ASN1_PRI, ASN1_ENUM,
++ &len);
++ *buf++ = 0;
++
++ /* negTokenTarg/supportedMechType */
++ len = sup_mech_len;
++ asn1_header_encode(&buf,
++ ASN1_CTX, ASN1_CON, 1,
++ &len);
++ asn1_header_encode(&buf,
++ ASN1_UNI, ASN1_PRI, ASN1_OJI,
++ &len);
++ memcpy(buf, sup_oid, sup_oid_len);
++ buf += len;
++
++ /* negTokenTarg/responseToken */
++ len = rep_token_len;
++ asn1_header_encode(&buf,
++ ASN1_CTX, ASN1_CON, 2,
++ &len);
++ asn1_header_encode(&buf,
++ ASN1_UNI, ASN1_PRI, ASN1_OTS,
++ &len);
++ asn1_header_encode(&buf,
++ ASN1_APL, ASN1_CON, 0,
++ &len);
++ /* negTokenTarg/responseToken/OID */
++ len = asn1_header_len(krb5_oid_len, 1);
++ asn1_header_encode(&buf,
++ ASN1_UNI, ASN1_PRI, ASN1_OJI,
++ &len);
++ /* negTokenTarg/responseToken/mechToken*/
++ memcpy(buf, krb5_oid, krb5_oid_len);
++ buf += len;
++ /* AP_REP id */
++ *buf++ = 2;
++ *buf++ = 0;
++ memcpy(buf, in_blob, in_len);
++
++ g_free(sup_oid);
++ g_free(krb5_oid);
++}
++
++int spnego_handle_authen_request(struct ksmbd_spnego_authen_request *req,
++ struct ksmbd_spnego_auth_out *auth_out)
++{
++ struct spnego_mech_ctx *mech_ctx;
++ unsigned char *mech_token;
++ int token_len, mech_type;
++ int retval = 0;
++
++ if (decode_negTokenInit(req->spnego_blob, (int)req->spnego_blob_len,
++ &mech_type, &mech_token, &token_len)) {
++ pr_info("Error decoding negTokenInit\n");
++ return -EINVAL;
++ }
++
++ mech_ctx = get_mech(mech_type);
++ if (!mech_ctx) {
++ retval = -ENOTSUP;
++ pr_info("No support for Kerberos 5\n");
++ goto out;
++ }
++
++ if (mech_ctx->ops->handle_authen(mech_ctx,
++ mech_token, token_len,
++ auth_out, encode_negTokenTarg)) {
++ retval = -EPERM;
++ pr_info("Error authenticating\n");
++ goto out;
++ }
++out:
++ return retval;
++}
+--- a/lib/management/spnego_krb5.c
++++ /dev/null
+@@ -1,408 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2020 LG Electronics
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include "ksmbdtools.h"
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <unistd.h>
+-#include <sys/types.h>
+-#include <sys/socket.h>
+-#include <netdb.h>
+-#include <krb5.h>
+-
+-#include <management/spnego.h>
+-#include <asn1.h>
+-#include "spnego_mech.h"
+-
+-#ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
+-krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context,
+- krb5_auth_context auth_context, krb5_keyblock **keyblock)
+-{
+- return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
+-}
+-#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
+-
+-#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
+-#define KRB5_KEY_TYPE(k) ((k)->keytype)
+-#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
+-#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
+-#else
+-#define KRB5_KEY_TYPE(k) ((k)->enctype)
+-#define KRB5_KEY_LENGTH(k) ((k)->length)
+-#define KRB5_KEY_DATA(k) ((k)->contents)
+-#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
+-
+-struct spnego_krb5_ctx {
+- krb5_context context;
+- krb5_keytab keytab;
+- krb5_creds creds;
+-};
+-
+-#define SERVICE_NAME "cifs"
+-
+-#define pr_krb5_err(_context, _retval, _fmt, ...) \
+- do { \
+- const char *msg = krb5_get_error_message(_context, _retval); \
+- pr_err("%s: " _fmt, msg, ##__VA_ARGS__); \
+- krb5_free_error_message(_context, msg); \
+- } while (0)
+-
+-static char *get_service_name(void)
+-{
+- return strdup(SERVICE_NAME);
+-}
+-
+-static char *get_host_name(void)
+-{
+- struct addrinfo hint, *ai;
+- char *host_name;
+- char hostname[NI_MAXHOST];
+-
+- if (gethostname(hostname, sizeof(hostname)))
+- return NULL;
+-
+- memset(&hint, 0, sizeof(hint));
+- hint.ai_family = AF_UNSPEC;
+- hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
+- if (getaddrinfo(hostname, NULL, &hint, &ai))
+- return NULL;
+-
+- host_name = strdup(ai->ai_canonname);
+- freeaddrinfo(ai);
+- return host_name;
+-}
+-
+-/* Service full name is <service name>[/<host FQDN>[@REALM>]] */
+-static int parse_service_full_name(char *service_full_name,
+- char **service_name,
+- char **host_name)
+-{
+- char *name, *delim;
+-
+- *service_name = NULL;
+- *host_name = NULL;
+-
+- if (!service_full_name) {
+- *service_name = get_service_name();
+- *host_name = get_host_name();
+- goto out;
+- }
+-
+- name = service_full_name;
+- delim = strchr(name, '/');
+- if (!delim) {
+- *service_name = strdup(name);
+- *host_name = get_host_name();
+- goto out;
+- }
+- *service_name = strndup(name, delim - name);
+- if (*service_name == NULL)
+- return -ENOMEM;
+-
+- name = delim + 1;
+- delim = strchr(name, '@');
+- if (!delim) {
+- *host_name = strdup(name);
+- goto out;
+- }
+- *host_name = strndup(name, delim - name);
+- if (*host_name == NULL) {
+- free(*service_name);
+- *service_name = NULL;
+- return -ENOMEM;
+- }
+-out:
+- /* we assume the host name is FQDN if it has "." */
+- if (*host_name && strchr(*host_name, '.'))
+- return 0;
+-
+- free(*service_name);
+- free(*host_name);
+- *service_name = NULL;
+- *host_name = NULL;
+- return -EINVAL;
+-}
+-
+-static krb5_error_code acquire_creds_from_keytab(krb5_context context,
+- char *service_full_name, char *keytab_name,
+- krb5_creds *out_creds, krb5_keytab *keytab)
+-{
+- krb5_error_code retval;
+- krb5_principal sprinc = NULL;
+- char *host_name = NULL, *service_name = NULL;
+-
+- if (keytab_name)
+- retval = krb5_kt_resolve(context, keytab_name, keytab);
+- else
+- retval = krb5_kt_default(context, keytab);
+- if (retval) {
+- pr_krb5_err(context, retval, "while resolving keytab\n");
+- return retval;
+- }
+-
+- if (parse_service_full_name(service_full_name,
+- &service_name, &host_name)) {
+- retval = KRB5_ERR_HOST_REALM_UNKNOWN;
+- pr_krb5_err(context, retval, "while getting host name\n");
+- goto out_err;
+- }
+-
+- retval = krb5_sname_to_principal(context, host_name, service_name,
+- KRB5_NT_UNKNOWN, &sprinc);
+- if (retval) {
+- pr_krb5_err(context, retval, "while generating service name\n");
+- goto out_err;
+- }
+-
+- retval = krb5_get_init_creds_keytab(context, out_creds, sprinc,
+- *keytab, 0, NULL, NULL);
+- if (retval) {
+- char *name;
+-
+- krb5_unparse_name(context, sprinc, &name);
+- pr_krb5_err(context, retval,
+- "while getting credentials for %s\n", name);
+- krb5_free_unparsed_name(context, name);
+- goto out_err;
+- }
+-
+- free(host_name);
+- free(service_name);
+- return 0;
+-out_err:
+- if (sprinc)
+- krb5_free_principal(context, sprinc);
+- if (service_name)
+- free(service_name);
+- if (host_name)
+- free(host_name);
+- if (*keytab)
+- krb5_kt_close(context, *keytab);
+- return retval;
+-}
+-
+-static int handle_krb5_authen(struct spnego_mech_ctx *mech_ctx,
+- char *in_blob, unsigned int in_len,
+- struct ksmbd_spnego_auth_out *auth_out,
+- spnego_encode_t spnego_encode)
+-{
+- struct spnego_krb5_ctx *krb5_ctx;
+- char *client_name;
+- krb5_auth_context auth_context;
+- krb5_data packet, ap_rep;
+- krb5_ticket *ticket = NULL;
+- krb5_keyblock *session_key;
+-#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
+- krb5_authenticator *authenti;
+-#else
+- krb5_authenticator authenti;
+-#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
+- krb5_principal client;
+- int retval = -EINVAL;
+- krb5_error_code krb_retval;
+-
+- krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
+- if (!krb5_ctx)
+- return -EINVAL;
+-
+- krb_retval = krb5_auth_con_init(krb5_ctx->context, &auth_context);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while initializing auth context\n");
+- return -EINVAL;
+- }
+-
+- packet.length = in_len;
+- packet.data = (krb5_pointer)in_blob;
+- krb_retval = krb5_rd_req(krb5_ctx->context, &auth_context, &packet,
+- krb5_ctx->creds.client, krb5_ctx->keytab,
+- NULL, &ticket);
+- if (krb_retval) {
+- char *name;
+-
+- krb5_unparse_name(krb5_ctx->context, krb5_ctx->creds.client,
+- &name);
+- krb5_auth_con_free(krb5_ctx->context, auth_context);
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while decoding AP_REQ with %s creds\n", name);
+- krb5_free_unparsed_name(krb5_ctx->context, name);
+- return -EINVAL;
+- }
+-
+- krb_retval = krb5_auth_con_getrecvsubkey(krb5_ctx->context,
+- auth_context, &session_key);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while reading session key\n");
+- goto out_free_con_auth;
+- }
+-
+- krb_retval = krb5_mk_rep(krb5_ctx->context, auth_context, &ap_rep);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while making AP_REP\n");
+- goto out_free_key;
+- }
+-
+- krb_retval = krb5_auth_con_getauthenticator(krb5_ctx->context,
+- auth_context, &authenti);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while getting authenticator\n");
+- goto out_free_rep;
+- }
+-
+-#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
+- krb_retval = krb5_build_principal_ext(krb5_ctx->context, &client,
+- strlen(authenti->crealm), authenti->crealm, 0);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while getting authenticator client\n");
+- goto out_free_auth;
+- }
+- krb_retval = copy_PrincipalName(&authenti->cname, &client->name);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while copying authenticator client name\n");
+- goto out_free_client;
+- }
+-#else
+- client = authenti->client;
+-#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
+-
+- krb_retval = krb5_unparse_name_flags(krb5_ctx->context,
+- client,
+- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &client_name);
+- if (krb_retval) {
+- pr_krb5_err(krb5_ctx->context, krb_retval,
+- "while unparsing client name\n");
+- goto out_free_client;
+- }
+-
+- memset(auth_out, 0, sizeof(*auth_out));
+- auth_out->user_name = strdup(client_name);
+- if (!auth_out->user_name) {
+- krb5_free_unparsed_name(krb5_ctx->context, client_name);
+- retval = -ENOMEM;
+- goto out_free_client;
+- }
+- krb5_free_unparsed_name(krb5_ctx->context, client_name);
+-
+- auth_out->sess_key = malloc(KRB5_KEY_LENGTH(session_key));
+- if (!auth_out->sess_key) {
+- free(auth_out->user_name);
+- retval = -ENOMEM;
+- goto out_free_client;
+- }
+- memcpy(auth_out->sess_key, KRB5_KEY_DATA(session_key), KRB5_KEY_LENGTH(session_key));
+- auth_out->key_len = KRB5_KEY_LENGTH(session_key);
+-
+- if (spnego_encode(ap_rep.data, ap_rep.length,
+- mech_ctx->oid, mech_ctx->oid_len,
+- &auth_out->spnego_blob, &auth_out->blob_len)) {
+- free(auth_out->user_name);
+- free(auth_out->sess_key);
+- goto out_free_client;
+- }
+-
+- pr_info("Authenticated user `%s'\n", auth_out->user_name);
+- retval = 0;
+-
+-out_free_client:
+-#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
+- krb5_free_principal(krb5_ctx->context, client);
+-#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
+-out_free_auth:
+-#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
+- krb5_free_authenticator(krb5_ctx->context, authenti);
+-#else
+- krb5_free_authenticator(krb5_ctx->context, &authenti);
+-#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
+-out_free_rep:
+- krb5_free_data_contents(krb5_ctx->context, &ap_rep);
+-out_free_key:
+- krb5_free_keyblock(krb5_ctx->context, session_key);
+-out_free_con_auth:
+- krb5_free_ticket(krb5_ctx->context, ticket);
+- krb5_auth_con_free(krb5_ctx->context, auth_context);
+- return retval;
+-}
+-
+-static int setup_krb5_ctx(struct spnego_mech_ctx *mech_ctx)
+-{
+- struct spnego_krb5_ctx *krb5_ctx;
+- krb5_error_code krb_retval;
+-
+- krb5_ctx = g_try_malloc0(sizeof(*krb5_ctx));
+- if (!krb5_ctx)
+- return -ENOMEM;
+-
+- krb_retval = krb5_init_context(&krb5_ctx->context);
+- if (krb_retval) {
+- g_free(krb5_ctx);
+- pr_err("while initializing krb5 context\n");
+- return -EINVAL;
+- }
+-
+- krb_retval = acquire_creds_from_keytab(krb5_ctx->context,
+- mech_ctx->params.krb5.service_name,
+- mech_ctx->params.krb5.keytab_name,
+- &krb5_ctx->creds, &krb5_ctx->keytab);
+- if (krb_retval) {
+- krb5_free_context(krb5_ctx->context);
+- g_free(krb5_ctx);
+- return -EINVAL;
+- }
+-
+- mech_ctx->private = krb5_ctx;
+- return 0;
+-}
+-
+-static int setup_krb5(struct spnego_mech_ctx *mech_ctx)
+-{
+- mech_ctx->oid = KRB5_OID;
+- mech_ctx->oid_len = ARRAY_SIZE(KRB5_OID);
+- return setup_krb5_ctx(mech_ctx);
+-}
+-
+-static int setup_mskrb5(struct spnego_mech_ctx *mech_ctx)
+-{
+- mech_ctx->oid = MSKRB5_OID;
+- mech_ctx->oid_len = ARRAY_SIZE(MSKRB5_OID);
+- return setup_krb5_ctx(mech_ctx);
+-}
+-
+-static void cleanup_krb5(struct spnego_mech_ctx *mech_ctx)
+-{
+- if (mech_ctx->private) {
+- struct spnego_krb5_ctx *krb5_ctx;
+-
+- krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
+- krb5_free_cred_contents(krb5_ctx->context, &krb5_ctx->creds);
+- krb5_kt_close(krb5_ctx->context, krb5_ctx->keytab);
+- krb5_free_context(krb5_ctx->context);
+- g_free(krb5_ctx);
+- mech_ctx->private = NULL;
+- }
+- if (mech_ctx->params.krb5.service_name)
+- free(mech_ctx->params.krb5.service_name);
+- if (mech_ctx->params.krb5.keytab_name)
+- free(mech_ctx->params.krb5.keytab_name);
+-}
+-
+-struct spnego_mech_operations spnego_krb5_operations = {
+- .setup = setup_krb5,
+- .cleanup = cleanup_krb5,
+- .handle_authen = handle_krb5_authen,
+-};
+-
+-struct spnego_mech_operations spnego_mskrb5_operations = {
+- .setup = setup_mskrb5,
+- .cleanup = cleanup_krb5,
+- .handle_authen = handle_krb5_authen,
+-};
+--- /dev/null
++++ b/tools/management/spnego_krb5.c
+@@ -0,0 +1,408 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2020 LG Electronics
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include "tools.h"
++
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/socket.h>
++#include <netdb.h>
++#include <krb5.h>
++
++#include <management/spnego.h>
++#include <asn1.h>
++#include "spnego_mech.h"
++
++#ifndef HAVE_KRB5_AUTH_CON_GETRECVSUBKEY
++krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context,
++ krb5_auth_context auth_context, krb5_keyblock **keyblock)
++{
++ return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
++}
++#endif /* HAVE_KRB5_AUTH_CON_GETRECVSUBKEY */
++
++#ifdef HAVE_KRB5_KEYBLOCK_KEYVALUE
++#define KRB5_KEY_TYPE(k) ((k)->keytype)
++#define KRB5_KEY_LENGTH(k) ((k)->keyvalue.length)
++#define KRB5_KEY_DATA(k) ((k)->keyvalue.data)
++#else
++#define KRB5_KEY_TYPE(k) ((k)->enctype)
++#define KRB5_KEY_LENGTH(k) ((k)->length)
++#define KRB5_KEY_DATA(k) ((k)->contents)
++#endif /* HAVE_KRB5_KEYBLOCK_KEYVALUE */
++
++struct spnego_krb5_ctx {
++ krb5_context context;
++ krb5_keytab keytab;
++ krb5_creds creds;
++};
++
++#define SERVICE_NAME "cifs"
++
++#define pr_krb5_err(_context, _retval, _fmt, ...) \
++ do { \
++ const char *msg = krb5_get_error_message(_context, _retval); \
++ pr_err("%s: " _fmt, msg, ##__VA_ARGS__); \
++ krb5_free_error_message(_context, msg); \
++ } while (0)
++
++static char *get_service_name(void)
++{
++ return strdup(SERVICE_NAME);
++}
++
++static char *get_host_name(void)
++{
++ struct addrinfo hint, *ai;
++ char *host_name;
++ char hostname[NI_MAXHOST];
++
++ if (gethostname(hostname, sizeof(hostname)))
++ return NULL;
++
++ memset(&hint, 0, sizeof(hint));
++ hint.ai_family = AF_UNSPEC;
++ hint.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
++ if (getaddrinfo(hostname, NULL, &hint, &ai))
++ return NULL;
++
++ host_name = strdup(ai->ai_canonname);
++ freeaddrinfo(ai);
++ return host_name;
++}
++
++/* Service full name is <service name>[/<host FQDN>[@REALM>]] */
++static int parse_service_full_name(char *service_full_name,
++ char **service_name,
++ char **host_name)
++{
++ char *name, *delim;
++
++ *service_name = NULL;
++ *host_name = NULL;
++
++ if (!service_full_name) {
++ *service_name = get_service_name();
++ *host_name = get_host_name();
++ goto out;
++ }
++
++ name = service_full_name;
++ delim = strchr(name, '/');
++ if (!delim) {
++ *service_name = strdup(name);
++ *host_name = get_host_name();
++ goto out;
++ }
++ *service_name = strndup(name, delim - name);
++ if (*service_name == NULL)
++ return -ENOMEM;
++
++ name = delim + 1;
++ delim = strchr(name, '@');
++ if (!delim) {
++ *host_name = strdup(name);
++ goto out;
++ }
++ *host_name = strndup(name, delim - name);
++ if (*host_name == NULL) {
++ free(*service_name);
++ *service_name = NULL;
++ return -ENOMEM;
++ }
++out:
++ /* we assume the host name is FQDN if it has "." */
++ if (*host_name && strchr(*host_name, '.'))
++ return 0;
++
++ free(*service_name);
++ free(*host_name);
++ *service_name = NULL;
++ *host_name = NULL;
++ return -EINVAL;
++}
++
++static krb5_error_code acquire_creds_from_keytab(krb5_context context,
++ char *service_full_name, char *keytab_name,
++ krb5_creds *out_creds, krb5_keytab *keytab)
++{
++ krb5_error_code retval;
++ krb5_principal sprinc = NULL;
++ char *host_name = NULL, *service_name = NULL;
++
++ if (keytab_name)
++ retval = krb5_kt_resolve(context, keytab_name, keytab);
++ else
++ retval = krb5_kt_default(context, keytab);
++ if (retval) {
++ pr_krb5_err(context, retval, "while resolving keytab\n");
++ return retval;
++ }
++
++ if (parse_service_full_name(service_full_name,
++ &service_name, &host_name)) {
++ retval = KRB5_ERR_HOST_REALM_UNKNOWN;
++ pr_krb5_err(context, retval, "while getting host name\n");
++ goto out_err;
++ }
++
++ retval = krb5_sname_to_principal(context, host_name, service_name,
++ KRB5_NT_UNKNOWN, &sprinc);
++ if (retval) {
++ pr_krb5_err(context, retval, "while generating service name\n");
++ goto out_err;
++ }
++
++ retval = krb5_get_init_creds_keytab(context, out_creds, sprinc,
++ *keytab, 0, NULL, NULL);
++ if (retval) {
++ char *name;
++
++ krb5_unparse_name(context, sprinc, &name);
++ pr_krb5_err(context, retval,
++ "while getting credentials for %s\n", name);
++ krb5_free_unparsed_name(context, name);
++ goto out_err;
++ }
++
++ free(host_name);
++ free(service_name);
++ return 0;
++out_err:
++ if (sprinc)
++ krb5_free_principal(context, sprinc);
++ if (service_name)
++ free(service_name);
++ if (host_name)
++ free(host_name);
++ if (*keytab)
++ krb5_kt_close(context, *keytab);
++ return retval;
++}
++
++static int handle_krb5_authen(struct spnego_mech_ctx *mech_ctx,
++ char *in_blob, unsigned int in_len,
++ struct ksmbd_spnego_auth_out *auth_out,
++ spnego_encode_t spnego_encode)
++{
++ struct spnego_krb5_ctx *krb5_ctx;
++ char *client_name;
++ krb5_auth_context auth_context;
++ krb5_data packet, ap_rep;
++ krb5_ticket *ticket = NULL;
++ krb5_keyblock *session_key;
++#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
++ krb5_authenticator *authenti;
++#else
++ krb5_authenticator authenti;
++#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
++ krb5_principal client;
++ int retval = -EINVAL;
++ krb5_error_code krb_retval;
++
++ krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
++ if (!krb5_ctx)
++ return -EINVAL;
++
++ krb_retval = krb5_auth_con_init(krb5_ctx->context, &auth_context);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while initializing auth context\n");
++ return -EINVAL;
++ }
++
++ packet.length = in_len;
++ packet.data = (krb5_pointer)in_blob;
++ krb_retval = krb5_rd_req(krb5_ctx->context, &auth_context, &packet,
++ krb5_ctx->creds.client, krb5_ctx->keytab,
++ NULL, &ticket);
++ if (krb_retval) {
++ char *name;
++
++ krb5_unparse_name(krb5_ctx->context, krb5_ctx->creds.client,
++ &name);
++ krb5_auth_con_free(krb5_ctx->context, auth_context);
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while decoding AP_REQ with %s creds\n", name);
++ krb5_free_unparsed_name(krb5_ctx->context, name);
++ return -EINVAL;
++ }
++
++ krb_retval = krb5_auth_con_getrecvsubkey(krb5_ctx->context,
++ auth_context, &session_key);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while reading session key\n");
++ goto out_free_con_auth;
++ }
++
++ krb_retval = krb5_mk_rep(krb5_ctx->context, auth_context, &ap_rep);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while making AP_REP\n");
++ goto out_free_key;
++ }
++
++ krb_retval = krb5_auth_con_getauthenticator(krb5_ctx->context,
++ auth_context, &authenti);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while getting authenticator\n");
++ goto out_free_rep;
++ }
++
++#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
++ krb_retval = krb5_build_principal_ext(krb5_ctx->context, &client,
++ strlen(authenti->crealm), authenti->crealm, 0);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while getting authenticator client\n");
++ goto out_free_auth;
++ }
++ krb_retval = copy_PrincipalName(&authenti->cname, &client->name);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while copying authenticator client name\n");
++ goto out_free_client;
++ }
++#else
++ client = authenti->client;
++#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
++
++ krb_retval = krb5_unparse_name_flags(krb5_ctx->context,
++ client,
++ KRB5_PRINCIPAL_UNPARSE_NO_REALM, &client_name);
++ if (krb_retval) {
++ pr_krb5_err(krb5_ctx->context, krb_retval,
++ "while unparsing client name\n");
++ goto out_free_client;
++ }
++
++ memset(auth_out, 0, sizeof(*auth_out));
++ auth_out->user_name = strdup(client_name);
++ if (!auth_out->user_name) {
++ krb5_free_unparsed_name(krb5_ctx->context, client_name);
++ retval = -ENOMEM;
++ goto out_free_client;
++ }
++ krb5_free_unparsed_name(krb5_ctx->context, client_name);
++
++ auth_out->sess_key = malloc(KRB5_KEY_LENGTH(session_key));
++ if (!auth_out->sess_key) {
++ free(auth_out->user_name);
++ retval = -ENOMEM;
++ goto out_free_client;
++ }
++ memcpy(auth_out->sess_key, KRB5_KEY_DATA(session_key), KRB5_KEY_LENGTH(session_key));
++ auth_out->key_len = KRB5_KEY_LENGTH(session_key);
++
++ if (spnego_encode(ap_rep.data, ap_rep.length,
++ mech_ctx->oid, mech_ctx->oid_len,
++ &auth_out->spnego_blob, &auth_out->blob_len)) {
++ free(auth_out->user_name);
++ free(auth_out->sess_key);
++ goto out_free_client;
++ }
++
++ pr_info("Authenticated user `%s'\n", auth_out->user_name);
++ retval = 0;
++
++out_free_client:
++#ifndef HAVE_KRB5_AUTHENTICATOR_CLIENT
++ krb5_free_principal(krb5_ctx->context, client);
++#endif /* HAVE_KRB5_AUTHENTICATOR_CLIENT */
++out_free_auth:
++#ifdef HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER
++ krb5_free_authenticator(krb5_ctx->context, authenti);
++#else
++ krb5_free_authenticator(krb5_ctx->context, &authenti);
++#endif /* HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER */
++out_free_rep:
++ krb5_free_data_contents(krb5_ctx->context, &ap_rep);
++out_free_key:
++ krb5_free_keyblock(krb5_ctx->context, session_key);
++out_free_con_auth:
++ krb5_free_ticket(krb5_ctx->context, ticket);
++ krb5_auth_con_free(krb5_ctx->context, auth_context);
++ return retval;
++}
++
++static int setup_krb5_ctx(struct spnego_mech_ctx *mech_ctx)
++{
++ struct spnego_krb5_ctx *krb5_ctx;
++ krb5_error_code krb_retval;
++
++ krb5_ctx = g_try_malloc0(sizeof(*krb5_ctx));
++ if (!krb5_ctx)
++ return -ENOMEM;
++
++ krb_retval = krb5_init_context(&krb5_ctx->context);
++ if (krb_retval) {
++ g_free(krb5_ctx);
++ pr_err("while initializing krb5 context\n");
++ return -EINVAL;
++ }
++
++ krb_retval = acquire_creds_from_keytab(krb5_ctx->context,
++ mech_ctx->params.krb5.service_name,
++ mech_ctx->params.krb5.keytab_name,
++ &krb5_ctx->creds, &krb5_ctx->keytab);
++ if (krb_retval) {
++ krb5_free_context(krb5_ctx->context);
++ g_free(krb5_ctx);
++ return -EINVAL;
++ }
++
++ mech_ctx->private = krb5_ctx;
++ return 0;
++}
++
++static int setup_krb5(struct spnego_mech_ctx *mech_ctx)
++{
++ mech_ctx->oid = KRB5_OID;
++ mech_ctx->oid_len = ARRAY_SIZE(KRB5_OID);
++ return setup_krb5_ctx(mech_ctx);
++}
++
++static int setup_mskrb5(struct spnego_mech_ctx *mech_ctx)
++{
++ mech_ctx->oid = MSKRB5_OID;
++ mech_ctx->oid_len = ARRAY_SIZE(MSKRB5_OID);
++ return setup_krb5_ctx(mech_ctx);
++}
++
++static void cleanup_krb5(struct spnego_mech_ctx *mech_ctx)
++{
++ if (mech_ctx->private) {
++ struct spnego_krb5_ctx *krb5_ctx;
++
++ krb5_ctx = (struct spnego_krb5_ctx *)mech_ctx->private;
++ krb5_free_cred_contents(krb5_ctx->context, &krb5_ctx->creds);
++ krb5_kt_close(krb5_ctx->context, krb5_ctx->keytab);
++ krb5_free_context(krb5_ctx->context);
++ g_free(krb5_ctx);
++ mech_ctx->private = NULL;
++ }
++ if (mech_ctx->params.krb5.service_name)
++ free(mech_ctx->params.krb5.service_name);
++ if (mech_ctx->params.krb5.keytab_name)
++ free(mech_ctx->params.krb5.keytab_name);
++}
++
++struct spnego_mech_operations spnego_krb5_operations = {
++ .setup = setup_krb5,
++ .cleanup = cleanup_krb5,
++ .handle_authen = handle_krb5_authen,
++};
++
++struct spnego_mech_operations spnego_mskrb5_operations = {
++ .setup = setup_mskrb5,
++ .cleanup = cleanup_krb5,
++ .handle_authen = handle_krb5_authen,
++};
+--- a/lib/management/tree_conn.c
++++ /dev/null
+@@ -1,231 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/tree_conn.h"
+-#include "management/session.h"
+-#include "management/share.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void)
+-{
+- struct ksmbd_tree_conn *conn;
+-
+- conn = g_try_malloc0(sizeof(struct ksmbd_tree_conn));
+- if (!conn)
+- return NULL;
+-
+- conn->id = 0;
+- return conn;
+-}
+-
+-void tcm_tree_conn_free(struct ksmbd_tree_conn *conn)
+-{
+- shm_close_connection(conn->share);
+- put_ksmbd_share(conn->share);
+- g_free(conn);
+-}
+-
+-int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req,
+- struct ksmbd_tree_connect_response *resp)
+-{
+- struct ksmbd_user *user = NULL;
+- struct ksmbd_share *share = NULL;
+- struct ksmbd_tree_conn *conn = new_ksmbd_tree_conn();
+- int ret;
+-
+- if (!conn) {
+- resp->status = KSMBD_TREE_CONN_STATUS_NOMEM;
+- return -ENOMEM;
+- }
+-
+- if (sm_check_sessions_capacity(req->session_id)) {
+- resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS;
+- pr_debug("treecon: Too many active sessions\n");
+- goto out_error;
+- }
+-
+- if (global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) {
+- if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
+- resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+- pr_debug("treecon: Bad user password\n");
+- goto out_error;
+- }
+- }
+-
+- share = shm_lookup_share(req->share);
+- if (!share) {
+- resp->status = KSMBD_TREE_CONN_STATUS_NO_SHARE;
+- pr_err("treecon: Unknown net share: %s\n", req->share);
+- goto out_error;
+- }
+-
+- if (test_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE))
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+- if (test_share_flag(share, KSMBD_SHARE_FLAG_READONLY))
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
+- if (test_share_flag(share, KSMBD_SHARE_FLAG_UPDATE))
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_UPDATE);
+-
+- if (shm_open_connection(share)) {
+- resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS;
+- pr_debug("treecon: Too many connections to net share\n");
+- goto out_error;
+- }
+-
+- ret = shm_lookup_hosts_map(share,
+- KSMBD_SHARE_HOSTS_ALLOW_MAP,
+- req->peer_addr);
+- if (ret == -ENOENT) {
+- resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
+- pr_debug("treecon: Host denied: %s\n", req->peer_addr);
+- goto out_error;
+- }
+-
+- if (ret != 0) {
+- ret = shm_lookup_hosts_map(share,
+- KSMBD_SHARE_HOSTS_DENY_MAP,
+- req->peer_addr);
+- if (ret == 0) {
+- resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
+- pr_err("treecon: Host denied: %s\n", req->peer_addr);
+- goto out_error;
+- }
+- }
+-
+- if (global_conf.restrict_anon >= KSMBD_RESTRICT_ANON_TYPE_1) {
+- int deny;
+-
+- deny = !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
+- deny |= test_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
+-
+- if (req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT &&
+- deny) {
+- pr_debug("treecon: Deny, restricted session\n");
+- resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
+- goto out_error;
+- }
+- }
+-
+- if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
+- !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE) &&
+- !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
+- pr_debug("treecon: Deny, guest not allowed\n");
+- resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
+- goto out_error;
+- }
+-
+- if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
+- test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
+- pr_debug("treecon: Net share permits guest login\n");
+- user = usm_lookup_user(share->guest_account);
+- if (user) {
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+- goto bind;
+- }
+-
+- user = usm_lookup_user(global_conf.guest_account);
+- if (user) {
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+- goto bind;
+- }
+- }
+-
+- user = usm_lookup_user(req->account);
+- if (!user) {
+- resp->status = KSMBD_TREE_CONN_STATUS_NO_USER;
+- pr_err("treecon: User `%s' not found\n", req->account);
+- goto out_error;
+- }
+-
+- user->failed_login_count = 0;
+- user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
+-
+- if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
+-
+- ret = shm_lookup_users_map(share,
+- KSMBD_SHARE_ADMIN_USERS_MAP,
+- req->account);
+- if (ret == 0) {
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT);
+- goto bind;
+- }
+-
+- ret = shm_lookup_users_map(share,
+- KSMBD_SHARE_INVALID_USERS_MAP,
+- req->account);
+- if (ret == 0) {
+- resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+- pr_err("treecon: User is on invalid users list\n");
+- goto out_error;
+- }
+-
+- ret = shm_lookup_users_map(share,
+- KSMBD_SHARE_READ_LIST_MAP,
+- req->account);
+- if (ret == 0) {
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
+- clear_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+- goto bind;
+- }
+-
+- ret = shm_lookup_users_map(share,
+- KSMBD_SHARE_WRITE_LIST_MAP,
+- req->account);
+- if (ret == 0) {
+- set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
+- goto bind;
+- }
+-
+- ret = shm_lookup_users_map(share,
+- KSMBD_SHARE_VALID_USERS_MAP,
+- req->account);
+- if (ret == 0)
+- goto bind;
+- if (ret == -ENOENT) {
+- resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
+- pr_err("treecon: User is not on valid users list\n");
+- goto out_error;
+- }
+-
+-bind:
+- conn->id = req->connect_id;
+- conn->share = share;
+- resp->status = KSMBD_TREE_CONN_STATUS_OK;
+- resp->connection_flags = conn->flags;
+-
+- if (sm_handle_tree_connect(req->session_id, user, conn)) {
+- pr_err("treecon: Unable to bind tree connection\n");
+- tcm_tree_conn_free(conn);
+- put_ksmbd_user(user);
+- }
+-
+- g_rw_lock_writer_lock(&share->update_lock);
+- clear_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
+- g_rw_lock_writer_unlock(&share->update_lock);
+-
+- return 0;
+-out_error:
+- tcm_tree_conn_free(conn);
+- shm_close_connection(share);
+- put_ksmbd_share(share);
+- put_ksmbd_user(user);
+- return -EINVAL;
+-}
+-
+-int tcm_handle_tree_disconnect(unsigned long long sess_id,
+- unsigned long long tree_conn_id)
+-{
+- sm_handle_tree_disconnect(sess_id, tree_conn_id);
+- return 0;
+-}
+--- /dev/null
++++ b/tools/management/tree_conn.c
+@@ -0,0 +1,231 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/tree_conn.h"
++#include "management/session.h"
++#include "management/share.h"
++#include "management/user.h"
++#include "tools.h"
++
++static struct ksmbd_tree_conn *new_ksmbd_tree_conn(void)
++{
++ struct ksmbd_tree_conn *conn;
++
++ conn = g_try_malloc0(sizeof(struct ksmbd_tree_conn));
++ if (!conn)
++ return NULL;
++
++ conn->id = 0;
++ return conn;
++}
++
++void tcm_tree_conn_free(struct ksmbd_tree_conn *conn)
++{
++ shm_close_connection(conn->share);
++ put_ksmbd_share(conn->share);
++ g_free(conn);
++}
++
++int tcm_handle_tree_connect(struct ksmbd_tree_connect_request *req,
++ struct ksmbd_tree_connect_response *resp)
++{
++ struct ksmbd_user *user = NULL;
++ struct ksmbd_share *share = NULL;
++ struct ksmbd_tree_conn *conn = new_ksmbd_tree_conn();
++ int ret;
++
++ if (!conn) {
++ resp->status = KSMBD_TREE_CONN_STATUS_NOMEM;
++ return -ENOMEM;
++ }
++
++ if (sm_check_sessions_capacity(req->session_id)) {
++ resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_SESSIONS;
++ pr_debug("treecon: Too many active sessions\n");
++ goto out_error;
++ }
++
++ if (global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER) {
++ if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
++ resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++ pr_debug("treecon: Bad user password\n");
++ goto out_error;
++ }
++ }
++
++ share = shm_lookup_share(req->share);
++ if (!share) {
++ resp->status = KSMBD_TREE_CONN_STATUS_NO_SHARE;
++ pr_err("treecon: Unknown net share: %s\n", req->share);
++ goto out_error;
++ }
++
++ if (test_share_flag(share, KSMBD_SHARE_FLAG_WRITEABLE))
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++ if (test_share_flag(share, KSMBD_SHARE_FLAG_READONLY))
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
++ if (test_share_flag(share, KSMBD_SHARE_FLAG_UPDATE))
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_UPDATE);
++
++ if (shm_open_connection(share)) {
++ resp->status = KSMBD_TREE_CONN_STATUS_TOO_MANY_CONNS;
++ pr_debug("treecon: Too many connections to net share\n");
++ goto out_error;
++ }
++
++ ret = shm_lookup_hosts_map(share,
++ KSMBD_SHARE_HOSTS_ALLOW_MAP,
++ req->peer_addr);
++ if (ret == -ENOENT) {
++ resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
++ pr_debug("treecon: Host denied: %s\n", req->peer_addr);
++ goto out_error;
++ }
++
++ if (ret != 0) {
++ ret = shm_lookup_hosts_map(share,
++ KSMBD_SHARE_HOSTS_DENY_MAP,
++ req->peer_addr);
++ if (ret == 0) {
++ resp->status = KSMBD_TREE_CONN_STATUS_HOST_DENIED;
++ pr_err("treecon: Host denied: %s\n", req->peer_addr);
++ goto out_error;
++ }
++ }
++
++ if (global_conf.restrict_anon >= KSMBD_RESTRICT_ANON_TYPE_1) {
++ int deny;
++
++ deny = !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK);
++ deny |= test_share_flag(share, KSMBD_SHARE_FLAG_PIPE);
++
++ if (req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT &&
++ deny) {
++ pr_debug("treecon: Deny, restricted session\n");
++ resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
++ goto out_error;
++ }
++ }
++
++ if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
++ !test_share_flag(share, KSMBD_SHARE_FLAG_PIPE) &&
++ !test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
++ pr_debug("treecon: Deny, guest not allowed\n");
++ resp->status = KSMBD_TREE_CONN_STATUS_ERROR;
++ goto out_error;
++ }
++
++ if ((req->account_flags & KSMBD_USER_FLAG_GUEST_ACCOUNT) &&
++ test_share_flag(share, KSMBD_SHARE_FLAG_GUEST_OK)) {
++ pr_debug("treecon: Net share permits guest login\n");
++ user = usm_lookup_user(share->guest_account);
++ if (user) {
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++ goto bind;
++ }
++
++ user = usm_lookup_user(global_conf.guest_account);
++ if (user) {
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++ goto bind;
++ }
++ }
++
++ user = usm_lookup_user(req->account);
++ if (!user) {
++ resp->status = KSMBD_TREE_CONN_STATUS_NO_USER;
++ pr_err("treecon: User `%s' not found\n", req->account);
++ goto out_error;
++ }
++
++ user->failed_login_count = 0;
++ user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
++
++ if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_GUEST_ACCOUNT);
++
++ ret = shm_lookup_users_map(share,
++ KSMBD_SHARE_ADMIN_USERS_MAP,
++ req->account);
++ if (ret == 0) {
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_ADMIN_ACCOUNT);
++ goto bind;
++ }
++
++ ret = shm_lookup_users_map(share,
++ KSMBD_SHARE_INVALID_USERS_MAP,
++ req->account);
++ if (ret == 0) {
++ resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++ pr_err("treecon: User is on invalid users list\n");
++ goto out_error;
++ }
++
++ ret = shm_lookup_users_map(share,
++ KSMBD_SHARE_READ_LIST_MAP,
++ req->account);
++ if (ret == 0) {
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_READ_ONLY);
++ clear_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++ goto bind;
++ }
++
++ ret = shm_lookup_users_map(share,
++ KSMBD_SHARE_WRITE_LIST_MAP,
++ req->account);
++ if (ret == 0) {
++ set_conn_flag(conn, KSMBD_TREE_CONN_FLAG_WRITABLE);
++ goto bind;
++ }
++
++ ret = shm_lookup_users_map(share,
++ KSMBD_SHARE_VALID_USERS_MAP,
++ req->account);
++ if (ret == 0)
++ goto bind;
++ if (ret == -ENOENT) {
++ resp->status = KSMBD_TREE_CONN_STATUS_INVALID_USER;
++ pr_err("treecon: User is not on valid users list\n");
++ goto out_error;
++ }
++
++bind:
++ conn->id = req->connect_id;
++ conn->share = share;
++ resp->status = KSMBD_TREE_CONN_STATUS_OK;
++ resp->connection_flags = conn->flags;
++
++ if (sm_handle_tree_connect(req->session_id, user, conn)) {
++ pr_err("treecon: Unable to bind tree connection\n");
++ tcm_tree_conn_free(conn);
++ put_ksmbd_user(user);
++ }
++
++ g_rw_lock_writer_lock(&share->update_lock);
++ clear_share_flag(share, KSMBD_SHARE_FLAG_UPDATE);
++ g_rw_lock_writer_unlock(&share->update_lock);
++
++ return 0;
++out_error:
++ tcm_tree_conn_free(conn);
++ shm_close_connection(share);
++ put_ksmbd_share(share);
++ put_ksmbd_user(user);
++ return -EINVAL;
++}
++
++int tcm_handle_tree_disconnect(unsigned long long sess_id,
++ unsigned long long tree_conn_id)
++{
++ sm_handle_tree_disconnect(sess_id, tree_conn_id);
++ return 0;
++}
+--- a/lib/management/user.c
++++ /dev/null
+@@ -1,412 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <stdlib.h>
+-#include <string.h>
+-#include <glib.h>
+-
+-#include "linux/ksmbd_server.h"
+-#include "management/user.h"
+-#include "ksmbdtools.h"
+-
+-#define KSMBD_USER_STATE_FREEING 1
+-
+-static GHashTable *users_table;
+-static GRWLock users_table_lock;
+-
+-static void kill_ksmbd_user(struct ksmbd_user *user)
+-{
+- pr_debug("Kill user `%s'\n", user->name);
+-
+- free(user->name);
+- free(user->pass_b64);
+- free(user->pass);
+- g_rw_lock_clear(&user->update_lock);
+- g_free(user);
+-}
+-
+-static int __usm_remove_user(struct ksmbd_user *user)
+-{
+- int ret = 0;
+-
+- if (user->state != KSMBD_USER_STATE_FREEING) {
+- g_rw_lock_writer_lock(&users_table_lock);
+- if (!g_hash_table_remove(users_table, user->name))
+- ret = -EINVAL;
+- g_rw_lock_writer_unlock(&users_table_lock);
+- }
+- if (!ret)
+- kill_ksmbd_user(user);
+- return ret;
+-}
+-
+-struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user)
+-{
+- g_rw_lock_writer_lock(&user->update_lock);
+- if (user->ref_count != 0) {
+- user->ref_count++;
+- g_rw_lock_writer_unlock(&user->update_lock);
+- } else {
+- g_rw_lock_writer_unlock(&user->update_lock);
+- user = NULL;
+- }
+- return user;
+-}
+-
+-void put_ksmbd_user(struct ksmbd_user *user)
+-{
+- int drop;
+-
+- if (!user)
+- return;
+-
+- g_rw_lock_writer_lock(&user->update_lock);
+- user->ref_count--;
+- drop = !user->ref_count;
+- g_rw_lock_writer_unlock(&user->update_lock);
+-
+- if (!drop)
+- return;
+-
+- __usm_remove_user(user);
+-}
+-
+-static gboolean put_user_callback(gpointer _k, gpointer _v, gpointer data)
+-{
+- struct ksmbd_user *user = (struct ksmbd_user *)_v;
+-
+- user->state = KSMBD_USER_STATE_FREEING;
+- put_ksmbd_user(user);
+- return TRUE;
+-}
+-
+-void usm_remove_all_users(void)
+-{
+- g_rw_lock_writer_lock(&users_table_lock);
+- g_hash_table_foreach_remove(users_table, put_user_callback, NULL);
+- g_rw_lock_writer_unlock(&users_table_lock);
+-}
+-
+-static struct ksmbd_user *new_ksmbd_user(char *name, char *pwd)
+-{
+- struct ksmbd_user *user;
+- struct passwd *passwd;
+- size_t pass_sz;
+-
+- user = g_try_malloc0(sizeof(struct ksmbd_user));
+- if (!user)
+- return NULL;
+-
+- g_rw_lock_init(&user->update_lock);
+- user->name = name;
+- user->pass_b64 = pwd;
+- user->ref_count = 1;
+- user->gid = 9999;
+- user->uid = 9999;
+- passwd = getpwnam(name);
+- if (passwd) {
+- user->uid = passwd->pw_uid;
+- user->gid = passwd->pw_gid;
+- }
+-
+- user->pass = base64_decode(user->pass_b64, &pass_sz);
+- user->pass_sz = (int)pass_sz;
+- return user;
+-}
+-
+-static void free_hash_entry(gpointer k, gpointer u, gpointer user_data)
+-{
+- kill_ksmbd_user(u);
+-}
+-
+-static void usm_clear_users(void)
+-{
+- g_hash_table_foreach(users_table, free_hash_entry, NULL);
+-}
+-
+-void usm_destroy(void)
+-{
+- if (users_table) {
+- usm_clear_users();
+- g_hash_table_destroy(users_table);
+- }
+- g_rw_lock_clear(&users_table_lock);
+-}
+-
+-int usm_init(void)
+-{
+- users_table = g_hash_table_new(g_str_hash, g_str_equal);
+- if (!users_table)
+- return -ENOMEM;
+- g_rw_lock_init(&users_table_lock);
+- return 0;
+-}
+-
+-static struct ksmbd_user *__usm_lookup_user(char *name)
+-{
+- return g_hash_table_lookup(users_table, name);
+-}
+-
+-struct ksmbd_user *usm_lookup_user(char *name)
+-{
+- struct ksmbd_user *user, *ret;
+-
+- if (!name)
+- return NULL;
+-
+- g_rw_lock_reader_lock(&users_table_lock);
+- user = __usm_lookup_user(name);
+- if (user) {
+- ret = get_ksmbd_user(user);
+- if (!ret)
+- user = NULL;
+- }
+- g_rw_lock_reader_unlock(&users_table_lock);
+- return user;
+-}
+-
+-int usm_add_new_user(char *name, char *pwd)
+-{
+- int ret = 0;
+- struct ksmbd_user *user = new_ksmbd_user(name, pwd);
+-
+- if (!user) {
+- free(name);
+- free(pwd);
+- return -ENOMEM;
+- }
+-
+- g_rw_lock_writer_lock(&users_table_lock);
+- if (__usm_lookup_user(name)) {
+- g_rw_lock_writer_unlock(&users_table_lock);
+- pr_info("User `%s' already exists\n", name);
+- kill_ksmbd_user(user);
+- return 0;
+- }
+-
+- pr_debug("New user `%s'\n", user->name);
+- if (!g_hash_table_insert(users_table, user->name, user)) {
+- kill_ksmbd_user(user);
+- ret = -EINVAL;
+- }
+- g_rw_lock_writer_unlock(&users_table_lock);
+- return ret;
+-}
+-
+-int usm_add_update_user_from_pwdentry(char *data)
+-{
+- struct ksmbd_user *user;
+- char *name;
+- char *pwd;
+- char *pos = strchr(data, ':');
+- int ret;
+-
+- if (!pos) {
+- pr_err("Invalid pwd entry: %s\n", data);
+- return -EINVAL;
+- }
+-
+- *pos = 0x00;
+- name = g_strdup(data);
+- pwd = g_strdup(pos + 1);
+-
+- if (!name || !pwd) {
+- free(name);
+- free(pwd);
+- return -ENOMEM;
+- }
+-
+- user = usm_lookup_user(name);
+- if (user) {
+- ret = usm_update_user_password(user, pwd);
+- put_ksmbd_user(user);
+-
+- free(name);
+- free(pwd);
+- return ret;
+- }
+- return usm_add_new_user(name, pwd);
+-}
+-
+-int usm_add_subauth_global_conf(char *data)
+-{
+- char *pos = data;
+- char *spos;
+-
+- if (!pos)
+- return -EINVAL;
+-
+- spos = strchr(pos, ':');
+- if (!spos) {
+- pr_err("Invalid subauth entry: %s\n", data);
+- return -EINVAL;
+- }
+-
+- *spos = 0x00;
+- global_conf.gen_subauth[0] = atoi(pos);
+- pos = spos + 1;
+-
+- spos = strchr(pos, ':');
+- if (!spos) {
+- pr_err("Invalid subauth entry: %s\n", data);
+- return -EINVAL;
+- }
+- *spos = 0x00;
+- global_conf.gen_subauth[1] = atoi(pos);
+- global_conf.gen_subauth[2] = atoi(spos + 1);
+-
+- return 0;
+-}
+-
+-void for_each_ksmbd_user(walk_users cb, gpointer user_data)
+-{
+- g_rw_lock_reader_lock(&users_table_lock);
+- g_hash_table_foreach(users_table, cb, user_data);
+- g_rw_lock_reader_unlock(&users_table_lock);
+-}
+-
+-int usm_update_user_password(struct ksmbd_user *user, char *pswd)
+-{
+- size_t pass_sz;
+- char *pass_b64 = g_strdup(pswd);
+- char *pass = base64_decode(pass_b64, &pass_sz);
+-
+- if (!pass_b64 || !pass) {
+- free(pass_b64);
+- free(pass);
+- pr_err("Out of memory\n");
+- return -ENOMEM;
+- }
+-
+- pr_debug("Update user password: %s\n", user->name);
+- g_rw_lock_writer_lock(&user->update_lock);
+- free(user->pass_b64);
+- free(user->pass);
+- user->pass_b64 = pass_b64;
+- user->pass = pass;
+- user->pass_sz = (int)pass_sz;
+- g_rw_lock_writer_unlock(&user->update_lock);
+-
+- return 0;
+-}
+-
+-static int usm_copy_user_passhash(struct ksmbd_user *user,
+- char *pass,
+- size_t sz)
+-{
+- int ret = -ENOSPC;
+-
+- if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+- return 0;
+-
+- g_rw_lock_reader_lock(&user->update_lock);
+- if (sz >= user->pass_sz) {
+- memcpy(pass, user->pass, user->pass_sz);
+- ret = user->pass_sz;
+- }
+- g_rw_lock_reader_unlock(&user->update_lock);
+-
+- return ret;
+-}
+-
+-static int usm_copy_user_account(struct ksmbd_user *user,
+- char *account,
+- size_t sz)
+-{
+- int account_sz;
+-
+- if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
+- return 0;
+-
+- account_sz = strlen(user->name);
+- if (sz >= account_sz) {
+- memcpy(account, user->name, account_sz);
+- return 0;
+- }
+- pr_err("Cannot copy user data, buffer overrun\n");
+- return -ENOSPC;
+-}
+-
+-static void __handle_login_request(struct ksmbd_login_response *resp,
+- struct ksmbd_user *user)
+-{
+- int hash_sz;
+-
+- resp->gid = user->gid;
+- resp->uid = user->uid;
+- resp->status = user->flags;
+- resp->status |= KSMBD_USER_FLAG_OK;
+-
+- hash_sz = usm_copy_user_passhash(user,
+- resp->hash,
+- sizeof(resp->hash));
+- if (hash_sz < 0) {
+- resp->status = KSMBD_USER_FLAG_INVALID;
+- } else {
+- resp->hash_sz = (unsigned short)hash_sz;
+- if (usm_copy_user_account(user,
+- resp->account,
+- sizeof(resp->account)))
+- resp->status = KSMBD_USER_FLAG_INVALID;
+- }
+-}
+-
+-int usm_handle_login_request(struct ksmbd_login_request *req,
+- struct ksmbd_login_response *resp)
+-{
+- struct ksmbd_user *user = NULL;
+- int null_session = 0;
+-
+- if (req->account[0] == '\0')
+- null_session = 1;
+-
+- if (!null_session)
+- user = usm_lookup_user(req->account);
+- if (user) {
+- __handle_login_request(resp, user);
+- put_ksmbd_user(user);
+- return 0;
+- }
+-
+- resp->status = KSMBD_USER_FLAG_BAD_USER;
+- if (!null_session &&
+- global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER)
+- return 0;
+-
+- if (null_session ||
+- global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_BAD_USER)
+- user = usm_lookup_user(global_conf.guest_account);
+-
+- if (!user)
+- return 0;
+-
+- __handle_login_request(resp, user);
+- put_ksmbd_user(user);
+- return 0;
+-}
+-
+-int usm_handle_logout_request(struct ksmbd_logout_request *req)
+-{
+- struct ksmbd_user *user;
+-
+- user = usm_lookup_user(req->account);
+- if (!user)
+- return -ENOENT;
+-
+- if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
+- if (user->failed_login_count < 10)
+- user->failed_login_count++;
+- else
+- user->flags |= KSMBD_USER_FLAG_DELAY_SESSION;
+- } else {
+- user->failed_login_count = 0;
+- user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
+- }
+-
+- put_ksmbd_user(user);
+- return 0;
+-}
+--- /dev/null
++++ b/tools/management/user.c
+@@ -0,0 +1,412 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <stdlib.h>
++#include <string.h>
++#include <glib.h>
++
++#include "linux/ksmbd_server.h"
++#include "management/user.h"
++#include "tools.h"
++
++#define KSMBD_USER_STATE_FREEING 1
++
++static GHashTable *users_table;
++static GRWLock users_table_lock;
++
++static void kill_ksmbd_user(struct ksmbd_user *user)
++{
++ pr_debug("Kill user `%s'\n", user->name);
++
++ free(user->name);
++ free(user->pass_b64);
++ free(user->pass);
++ g_rw_lock_clear(&user->update_lock);
++ g_free(user);
++}
++
++static int __usm_remove_user(struct ksmbd_user *user)
++{
++ int ret = 0;
++
++ if (user->state != KSMBD_USER_STATE_FREEING) {
++ g_rw_lock_writer_lock(&users_table_lock);
++ if (!g_hash_table_remove(users_table, user->name))
++ ret = -EINVAL;
++ g_rw_lock_writer_unlock(&users_table_lock);
++ }
++ if (!ret)
++ kill_ksmbd_user(user);
++ return ret;
++}
++
++struct ksmbd_user *get_ksmbd_user(struct ksmbd_user *user)
++{
++ g_rw_lock_writer_lock(&user->update_lock);
++ if (user->ref_count != 0) {
++ user->ref_count++;
++ g_rw_lock_writer_unlock(&user->update_lock);
++ } else {
++ g_rw_lock_writer_unlock(&user->update_lock);
++ user = NULL;
++ }
++ return user;
++}
++
++void put_ksmbd_user(struct ksmbd_user *user)
++{
++ int drop;
++
++ if (!user)
++ return;
++
++ g_rw_lock_writer_lock(&user->update_lock);
++ user->ref_count--;
++ drop = !user->ref_count;
++ g_rw_lock_writer_unlock(&user->update_lock);
++
++ if (!drop)
++ return;
++
++ __usm_remove_user(user);
++}
++
++static gboolean put_user_callback(gpointer _k, gpointer _v, gpointer data)
++{
++ struct ksmbd_user *user = (struct ksmbd_user *)_v;
++
++ user->state = KSMBD_USER_STATE_FREEING;
++ put_ksmbd_user(user);
++ return TRUE;
++}
++
++void usm_remove_all_users(void)
++{
++ g_rw_lock_writer_lock(&users_table_lock);
++ g_hash_table_foreach_remove(users_table, put_user_callback, NULL);
++ g_rw_lock_writer_unlock(&users_table_lock);
++}
++
++static struct ksmbd_user *new_ksmbd_user(char *name, char *pwd)
++{
++ struct ksmbd_user *user;
++ struct passwd *passwd;
++ size_t pass_sz;
++
++ user = g_try_malloc0(sizeof(struct ksmbd_user));
++ if (!user)
++ return NULL;
++
++ g_rw_lock_init(&user->update_lock);
++ user->name = name;
++ user->pass_b64 = pwd;
++ user->ref_count = 1;
++ user->gid = 9999;
++ user->uid = 9999;
++ passwd = getpwnam(name);
++ if (passwd) {
++ user->uid = passwd->pw_uid;
++ user->gid = passwd->pw_gid;
++ }
++
++ user->pass = base64_decode(user->pass_b64, &pass_sz);
++ user->pass_sz = (int)pass_sz;
++ return user;
++}
++
++static void free_hash_entry(gpointer k, gpointer u, gpointer user_data)
++{
++ kill_ksmbd_user(u);
++}
++
++static void usm_clear_users(void)
++{
++ g_hash_table_foreach(users_table, free_hash_entry, NULL);
++}
++
++void usm_destroy(void)
++{
++ if (users_table) {
++ usm_clear_users();
++ g_hash_table_destroy(users_table);
++ }
++ g_rw_lock_clear(&users_table_lock);
++}
++
++int usm_init(void)
++{
++ users_table = g_hash_table_new(g_str_hash, g_str_equal);
++ if (!users_table)
++ return -ENOMEM;
++ g_rw_lock_init(&users_table_lock);
++ return 0;
++}
++
++static struct ksmbd_user *__usm_lookup_user(char *name)
++{
++ return g_hash_table_lookup(users_table, name);
++}
++
++struct ksmbd_user *usm_lookup_user(char *name)
++{
++ struct ksmbd_user *user, *ret;
++
++ if (!name)
++ return NULL;
++
++ g_rw_lock_reader_lock(&users_table_lock);
++ user = __usm_lookup_user(name);
++ if (user) {
++ ret = get_ksmbd_user(user);
++ if (!ret)
++ user = NULL;
++ }
++ g_rw_lock_reader_unlock(&users_table_lock);
++ return user;
++}
++
++int usm_add_new_user(char *name, char *pwd)
++{
++ int ret = 0;
++ struct ksmbd_user *user = new_ksmbd_user(name, pwd);
++
++ if (!user) {
++ free(name);
++ free(pwd);
++ return -ENOMEM;
++ }
++
++ g_rw_lock_writer_lock(&users_table_lock);
++ if (__usm_lookup_user(name)) {
++ g_rw_lock_writer_unlock(&users_table_lock);
++ pr_info("User `%s' already exists\n", name);
++ kill_ksmbd_user(user);
++ return 0;
++ }
++
++ pr_debug("New user `%s'\n", user->name);
++ if (!g_hash_table_insert(users_table, user->name, user)) {
++ kill_ksmbd_user(user);
++ ret = -EINVAL;
++ }
++ g_rw_lock_writer_unlock(&users_table_lock);
++ return ret;
++}
++
++int usm_add_update_user_from_pwdentry(char *data)
++{
++ struct ksmbd_user *user;
++ char *name;
++ char *pwd;
++ char *pos = strchr(data, ':');
++ int ret;
++
++ if (!pos) {
++ pr_err("Invalid pwd entry: %s\n", data);
++ return -EINVAL;
++ }
++
++ *pos = 0x00;
++ name = g_strdup(data);
++ pwd = g_strdup(pos + 1);
++
++ if (!name || !pwd) {
++ free(name);
++ free(pwd);
++ return -ENOMEM;
++ }
++
++ user = usm_lookup_user(name);
++ if (user) {
++ ret = usm_update_user_password(user, pwd);
++ put_ksmbd_user(user);
++
++ free(name);
++ free(pwd);
++ return ret;
++ }
++ return usm_add_new_user(name, pwd);
++}
++
++int usm_add_subauth_global_conf(char *data)
++{
++ char *pos = data;
++ char *spos;
++
++ if (!pos)
++ return -EINVAL;
++
++ spos = strchr(pos, ':');
++ if (!spos) {
++ pr_err("Invalid subauth entry: %s\n", data);
++ return -EINVAL;
++ }
++
++ *spos = 0x00;
++ global_conf.gen_subauth[0] = atoi(pos);
++ pos = spos + 1;
++
++ spos = strchr(pos, ':');
++ if (!spos) {
++ pr_err("Invalid subauth entry: %s\n", data);
++ return -EINVAL;
++ }
++ *spos = 0x00;
++ global_conf.gen_subauth[1] = atoi(pos);
++ global_conf.gen_subauth[2] = atoi(spos + 1);
++
++ return 0;
++}
++
++void for_each_ksmbd_user(walk_users cb, gpointer user_data)
++{
++ g_rw_lock_reader_lock(&users_table_lock);
++ g_hash_table_foreach(users_table, cb, user_data);
++ g_rw_lock_reader_unlock(&users_table_lock);
++}
++
++int usm_update_user_password(struct ksmbd_user *user, char *pswd)
++{
++ size_t pass_sz;
++ char *pass_b64 = g_strdup(pswd);
++ char *pass = base64_decode(pass_b64, &pass_sz);
++
++ if (!pass_b64 || !pass) {
++ free(pass_b64);
++ free(pass);
++ pr_err("Out of memory\n");
++ return -ENOMEM;
++ }
++
++ pr_debug("Update user password: %s\n", user->name);
++ g_rw_lock_writer_lock(&user->update_lock);
++ free(user->pass_b64);
++ free(user->pass);
++ user->pass_b64 = pass_b64;
++ user->pass = pass;
++ user->pass_sz = (int)pass_sz;
++ g_rw_lock_writer_unlock(&user->update_lock);
++
++ return 0;
++}
++
++static int usm_copy_user_passhash(struct ksmbd_user *user,
++ char *pass,
++ size_t sz)
++{
++ int ret = -ENOSPC;
++
++ if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++ return 0;
++
++ g_rw_lock_reader_lock(&user->update_lock);
++ if (sz >= user->pass_sz) {
++ memcpy(pass, user->pass, user->pass_sz);
++ ret = user->pass_sz;
++ }
++ g_rw_lock_reader_unlock(&user->update_lock);
++
++ return ret;
++}
++
++static int usm_copy_user_account(struct ksmbd_user *user,
++ char *account,
++ size_t sz)
++{
++ int account_sz;
++
++ if (test_user_flag(user, KSMBD_USER_FLAG_GUEST_ACCOUNT))
++ return 0;
++
++ account_sz = strlen(user->name);
++ if (sz >= account_sz) {
++ memcpy(account, user->name, account_sz);
++ return 0;
++ }
++ pr_err("Cannot copy user data, buffer overrun\n");
++ return -ENOSPC;
++}
++
++static void __handle_login_request(struct ksmbd_login_response *resp,
++ struct ksmbd_user *user)
++{
++ int hash_sz;
++
++ resp->gid = user->gid;
++ resp->uid = user->uid;
++ resp->status = user->flags;
++ resp->status |= KSMBD_USER_FLAG_OK;
++
++ hash_sz = usm_copy_user_passhash(user,
++ resp->hash,
++ sizeof(resp->hash));
++ if (hash_sz < 0) {
++ resp->status = KSMBD_USER_FLAG_INVALID;
++ } else {
++ resp->hash_sz = (unsigned short)hash_sz;
++ if (usm_copy_user_account(user,
++ resp->account,
++ sizeof(resp->account)))
++ resp->status = KSMBD_USER_FLAG_INVALID;
++ }
++}
++
++int usm_handle_login_request(struct ksmbd_login_request *req,
++ struct ksmbd_login_response *resp)
++{
++ struct ksmbd_user *user = NULL;
++ int null_session = 0;
++
++ if (req->account[0] == '\0')
++ null_session = 1;
++
++ if (!null_session)
++ user = usm_lookup_user(req->account);
++ if (user) {
++ __handle_login_request(resp, user);
++ put_ksmbd_user(user);
++ return 0;
++ }
++
++ resp->status = KSMBD_USER_FLAG_BAD_USER;
++ if (!null_session &&
++ global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_NEVER)
++ return 0;
++
++ if (null_session ||
++ global_conf.map_to_guest == KSMBD_CONF_MAP_TO_GUEST_BAD_USER)
++ user = usm_lookup_user(global_conf.guest_account);
++
++ if (!user)
++ return 0;
++
++ __handle_login_request(resp, user);
++ put_ksmbd_user(user);
++ return 0;
++}
++
++int usm_handle_logout_request(struct ksmbd_logout_request *req)
++{
++ struct ksmbd_user *user;
++
++ user = usm_lookup_user(req->account);
++ if (!user)
++ return -ENOENT;
++
++ if (req->account_flags & KSMBD_USER_FLAG_BAD_PASSWORD) {
++ if (user->failed_login_count < 10)
++ user->failed_login_count++;
++ else
++ user->flags |= KSMBD_USER_FLAG_DELAY_SESSION;
++ } else {
++ user->failed_login_count = 0;
++ user->flags &= ~KSMBD_USER_FLAG_DELAY_SESSION;
++ }
++
++ put_ksmbd_user(user);
++ return 0;
++}
+--- a/lib/meson.build
++++ /dev/null
+@@ -1,31 +0,0 @@
+-core_files = [
+- 'management/tree_conn.c',
+- 'management/user.c',
+- 'management/share.c',
+- 'management/session.c',
+- 'config_parser.c',
+- 'ksmbdtools.c',
+-]
+-
+-if krb5_dep.found()
+- core_files += [
+- 'management/spnego.c',
+- 'asn1.c',
+- 'management/spnego_krb5.c',
+- ]
+-endif
+-
+-libksmbdtools = static_library(
+- 'ksmbdtools',
+- core_files,
+- include_directories: tools_incdir,
+- dependencies: [
+- glib_dep,
+- krb5_dep,
+- asn1_lib,
+- ],
+- c_args: [
+- '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
+- '-DRUNSTATEDIR="@0@"'.format(runstatedir),
+- ],
+-)
+--- /dev/null
++++ b/tools/meson.build
+@@ -0,0 +1,39 @@
++ksmbd_tools_files = [
++ 'management/tree_conn.c',
++ 'management/user.c',
++ 'management/share.c',
++ 'management/session.c',
++ 'config_parser.c',
++ 'tools.c',
++]
++
++if krb5_dep.found()
++ ksmbd_tools_files += [
++ 'management/spnego.c',
++ 'asn1.c',
++ 'management/spnego_krb5.c',
++ ]
++endif
++
++executable(
++ 'ksmbd.tools',
++ ksmbd_tools_files,
++ include_directories: include_dirs,
++ c_args: [
++ '-DSYSCONFDIR="@0@"'.format(get_option('prefix') / get_option('sysconfdir')),
++ '-DRUNSTATEDIR="@0@"'.format(runstatedir),
++ ],
++ dependencies: [
++ glib_dep,
++ krb5_dep,
++ asn1_lib,
++ ],
++ link_with: [
++ addshare_lib,
++ adduser_lib,
++ control_lib,
++ mountd_lib,
++ ],
++ install: true,
++ install_dir: get_option('libexecdir'),
++)
+--- a/lib/ksmbdtools.c
++++ /dev/null
+@@ -1,276 +0,0 @@
+-// SPDX-License-Identifier: GPL-2.0-or-later
+-/*
+- * Copyright (C) 2018 Samsung Electronics Co., Ltd.
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#include <syslog.h>
+-
+-#include <unistd.h>
+-#include <sys/stat.h>
+-#include <fcntl.h>
+-
+-#include <stdio.h>
+-#include <ksmbdtools.h>
+-
+-int log_level = PR_INFO;
+-int ksmbd_health_status;
+-
+-static const char *app_name = "unknown";
+-static int log_open;
+-
+-typedef void (*logger)(int level, const char *fmt, va_list list);
+-
+-char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1] = {
+- "UTF-8",
+- "UTF-16LE",
+- "UCS-2LE",
+- "UTF-16BE",
+- "UCS-2BE",
+- "OOPS"
+-};
+-
+-static int syslog_level(int level)
+-{
+- if (level == PR_ERROR)
+- return LOG_ERR;
+- if (level == PR_INFO)
+- return LOG_INFO;
+- if (level == PR_DEBUG)
+- return LOG_DEBUG;
+-
+- return LOG_ERR;
+-}
+-
+-G_GNUC_PRINTF(2, 0)
+-static void __pr_log_stdio(int level, const char *fmt, va_list list)
+-{
+- char buf[1024];
+-
+- vsnprintf(buf, sizeof(buf), fmt, list);
+- printf("%s", buf);
+-}
+-
+-G_GNUC_PRINTF(2, 0)
+-static void __pr_log_syslog(int level, const char *fmt, va_list list)
+-{
+- vsyslog(syslog_level(level), fmt, list);
+-}
+-
+-static logger __logger = __pr_log_stdio;
+-
+-void set_logger_app_name(const char *an)
+-{
+- app_name = an;
+-}
+-
+-const char *get_logger_app_name(void)
+-{
+- return app_name;
+-}
+-
+-void __pr_log(int level, const char *fmt, ...)
+-{
+- va_list list;
+-
+- va_start(list, fmt);
+- __logger(level, fmt, list);
+- va_end(list);
+-}
+-
+-void pr_logger_init(int flag)
+-{
+- if (flag == PR_LOGGER_SYSLOG) {
+- if (log_open) {
+- closelog();
+- log_open = 0;
+- }
+- openlog("ksmbd", LOG_NDELAY, LOG_LOCAL5);
+- __logger = __pr_log_syslog;
+- log_open = 1;
+- }
+-}
+-
+-int set_log_level(int level)
+-{
+- int old_level;
+-
+- if (log_level == PR_DEBUG)
+- return log_level;
+-
+- old_level = log_level;
+- log_level = level;
+- return old_level;
+-}
+-
+-#if TRACING_DUMP_NL_MSG
+-#define PR_HEX_DUMP_WIDTH 160
+-void pr_hex_dump(const void *mem, size_t sz)
+-{
+- char xline[PR_HEX_DUMP_WIDTH];
+- char sline[PR_HEX_DUMP_WIDTH];
+- int xi = 0, si = 0, mi = 0;
+-
+- while (mi < sz) {
+- char c = *((char *)mem + mi);
+-
+- mi++;
+- xi += sprintf(xline + xi, "%02X ", 0xff & c);
+- if (c > ' ' && c < '~')
+- si += sprintf(sline + si, "%c", c);
+- else
+- si += sprintf(sline + si, ".");
+- if (xi >= PR_HEX_DUMP_WIDTH / 2) {
+- pr_err("%s %s\n", xline, sline);
+- xi = 0;
+- si = 0;
+- }
+- }
+-
+- if (xi) {
+- int sz = PR_HEX_DUMP_WIDTH / 2 - xi + 1;
+-
+- if (sz > 0) {
+- memset(xline + xi, ' ', sz);
+- xline[PR_HEX_DUMP_WIDTH / 2 + 1] = 0x00;
+- }
+- pr_err("%s %s\n", xline, sline);
+- }
+-}
+-#else
+-void pr_hex_dump(const void *mem, size_t sz)
+-{
+-}
+-#endif
+-
+-char *base64_encode(unsigned char *src, size_t srclen)
+-{
+- return g_base64_encode(src, srclen);
+-}
+-
+-unsigned char *base64_decode(char const *src, size_t *dstlen)
+-{
+- unsigned char *ret = g_base64_decode(src, dstlen);
+-
+- if (ret)
+- ret[*dstlen] = 0x00;
+- return ret;
+-}
+-
+-static int codeset_has_altname(int codeset)
+-{
+- if (codeset == KSMBD_CHARSET_UTF16LE ||
+- codeset == KSMBD_CHARSET_UTF16BE)
+- return 1;
+- return 0;
+-}
+-
+-gchar *ksmbd_gconvert(const gchar *str,
+- gssize str_len,
+- int to_codeset,
+- int from_codeset,
+- gsize *bytes_read,
+- gsize *bytes_written)
+-{
+- gchar *converted;
+- GError *err;
+-
+-retry:
+- err = NULL;
+- if (from_codeset >= KSMBD_CHARSET_MAX) {
+- pr_err("Unknown source codeset: %d\n", from_codeset);
+- return NULL;
+- }
+-
+- if (to_codeset >= KSMBD_CHARSET_MAX) {
+- pr_err("Unknown target codeset: %d\n", to_codeset);
+- return NULL;
+- }
+-
+- converted = g_convert(str,
+- str_len,
+- ksmbd_conv_charsets[to_codeset],
+- ksmbd_conv_charsets[from_codeset],
+- bytes_read,
+- bytes_written,
+- &err);
+- if (err) {
+- int has_altname = 0;
+-
+- if (codeset_has_altname(to_codeset)) {
+- to_codeset++;
+- has_altname = 1;
+- }
+-
+- if (codeset_has_altname(from_codeset)) {
+- from_codeset++;
+- has_altname = 1;
+- }
+-
+- pr_info("%s\n", err->message);
+- g_error_free(err);
+-
+- if (has_altname) {
+- pr_info("Will try `%s' and `%s'\n",
+- ksmbd_conv_charsets[to_codeset],
+- ksmbd_conv_charsets[from_codeset]);
+- goto retry;
+- }
+-
+- pr_err("Can't convert string: %s\n", err->message);
+- g_error_free(err);
+- return NULL;
+- }
+-
+- return converted;
+-}
+-
+-int send_signal_to_ksmbd_mountd(int signo)
+-{
+- int fd, ret = -EINVAL;
+- char pid_buf[10] = {0};
+- int pid;
+-
+- fd = open(KSMBD_LOCK_FILE, O_RDONLY);
+- if (fd < 0) {
+- pr_debug("Can't open `%s': %m\n", KSMBD_LOCK_FILE);
+- return ret;
+- }
+-
+- if (read(fd, &pid_buf, sizeof(pid_buf)) == -1) {
+- pr_err("Can't read manager PID: %m\n");
+- goto out;
+- }
+-
+- pid = strtol(pid_buf, NULL, 10);
+- if (signo)
+- pr_debug("Send signal %d (%s) to PID %d\n",
+- signo, strsignal(signo), pid);
+- if (kill(pid, signo) == -1) {
+- ret = -errno;
+- if (signo)
+- pr_err("Unable to send signal %d (%s) to PID %d: %m\n",
+- signo, strsignal(signo), pid);
+- goto out;
+- }
+-
+- ret = 0;
+-out:
+- close(fd);
+- return ret;
+-}
+-
+-int test_file_access(char *conf)
+-{
+- int fd;
+-
+- fd = open(conf, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP);
+- if (fd < 0) {
+- pr_debug("Can't open `%s': %m\n", conf);
+- return -EINVAL;
+- }
+-
+- close(fd);
+- return 0;
+-}
+--- /dev/null
++++ b/tools/tools.c
+@@ -0,0 +1,301 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * Copyright (C) 2018 Samsung Electronics Co., Ltd.
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#include <syslog.h>
++
++#include <unistd.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++
++#include <stdio.h>
++#include <tools.h>
++
++int log_level = PR_INFO;
++int ksmbd_health_status;
++
++static const char *app_name = "unknown";
++static int log_open;
++
++typedef void (*logger)(int level, const char *fmt, va_list list);
++
++char *ksmbd_conv_charsets[KSMBD_CHARSET_MAX + 1] = {
++ "UTF-8",
++ "UTF-16LE",
++ "UCS-2LE",
++ "UTF-16BE",
++ "UCS-2BE",
++ "OOPS"
++};
++
++static int syslog_level(int level)
++{
++ if (level == PR_ERROR)
++ return LOG_ERR;
++ if (level == PR_INFO)
++ return LOG_INFO;
++ if (level == PR_DEBUG)
++ return LOG_DEBUG;
++
++ return LOG_ERR;
++}
++
++G_GNUC_PRINTF(2, 0)
++static void __pr_log_stdio(int level, const char *fmt, va_list list)
++{
++ char buf[1024];
++
++ vsnprintf(buf, sizeof(buf), fmt, list);
++ printf("%s", buf);
++}
++
++G_GNUC_PRINTF(2, 0)
++static void __pr_log_syslog(int level, const char *fmt, va_list list)
++{
++ vsyslog(syslog_level(level), fmt, list);
++}
++
++static logger __logger = __pr_log_stdio;
++
++void set_logger_app_name(const char *an)
++{
++ app_name = an;
++}
++
++const char *get_logger_app_name(void)
++{
++ return app_name;
++}
++
++void __pr_log(int level, const char *fmt, ...)
++{
++ va_list list;
++
++ va_start(list, fmt);
++ __logger(level, fmt, list);
++ va_end(list);
++}
++
++void pr_logger_init(int flag)
++{
++ if (flag == PR_LOGGER_SYSLOG) {
++ if (log_open) {
++ closelog();
++ log_open = 0;
++ }
++ openlog("ksmbd", LOG_NDELAY, LOG_LOCAL5);
++ __logger = __pr_log_syslog;
++ log_open = 1;
++ }
++}
++
++int set_log_level(int level)
++{
++ int old_level;
++
++ if (log_level == PR_DEBUG)
++ return log_level;
++
++ old_level = log_level;
++ log_level = level;
++ return old_level;
++}
++
++#if TRACING_DUMP_NL_MSG
++#define PR_HEX_DUMP_WIDTH 160
++void pr_hex_dump(const void *mem, size_t sz)
++{
++ char xline[PR_HEX_DUMP_WIDTH];
++ char sline[PR_HEX_DUMP_WIDTH];
++ int xi = 0, si = 0, mi = 0;
++
++ while (mi < sz) {
++ char c = *((char *)mem + mi);
++
++ mi++;
++ xi += sprintf(xline + xi, "%02X ", 0xff & c);
++ if (c > ' ' && c < '~')
++ si += sprintf(sline + si, "%c", c);
++ else
++ si += sprintf(sline + si, ".");
++ if (xi >= PR_HEX_DUMP_WIDTH / 2) {
++ pr_err("%s %s\n", xline, sline);
++ xi = 0;
++ si = 0;
++ }
++ }
++
++ if (xi) {
++ int sz = PR_HEX_DUMP_WIDTH / 2 - xi + 1;
++
++ if (sz > 0) {
++ memset(xline + xi, ' ', sz);
++ xline[PR_HEX_DUMP_WIDTH / 2 + 1] = 0x00;
++ }
++ pr_err("%s %s\n", xline, sline);
++ }
++}
++#else
++void pr_hex_dump(const void *mem, size_t sz)
++{
++}
++#endif
++
++char *base64_encode(unsigned char *src, size_t srclen)
++{
++ return g_base64_encode(src, srclen);
++}
++
++unsigned char *base64_decode(char const *src, size_t *dstlen)
++{
++ unsigned char *ret = g_base64_decode(src, dstlen);
++
++ if (ret)
++ ret[*dstlen] = 0x00;
++ return ret;
++}
++
++static int codeset_has_altname(int codeset)
++{
++ if (codeset == KSMBD_CHARSET_UTF16LE ||
++ codeset == KSMBD_CHARSET_UTF16BE)
++ return 1;
++ return 0;
++}
++
++gchar *ksmbd_gconvert(const gchar *str,
++ gssize str_len,
++ int to_codeset,
++ int from_codeset,
++ gsize *bytes_read,
++ gsize *bytes_written)
++{
++ gchar *converted;
++ GError *err;
++
++retry:
++ err = NULL;
++ if (from_codeset >= KSMBD_CHARSET_MAX) {
++ pr_err("Unknown source codeset: %d\n", from_codeset);
++ return NULL;
++ }
++
++ if (to_codeset >= KSMBD_CHARSET_MAX) {
++ pr_err("Unknown target codeset: %d\n", to_codeset);
++ return NULL;
++ }
++
++ converted = g_convert(str,
++ str_len,
++ ksmbd_conv_charsets[to_codeset],
++ ksmbd_conv_charsets[from_codeset],
++ bytes_read,
++ bytes_written,
++ &err);
++ if (err) {
++ int has_altname = 0;
++
++ if (codeset_has_altname(to_codeset)) {
++ to_codeset++;
++ has_altname = 1;
++ }
++
++ if (codeset_has_altname(from_codeset)) {
++ from_codeset++;
++ has_altname = 1;
++ }
++
++ pr_info("%s\n", err->message);
++ g_error_free(err);
++
++ if (has_altname) {
++ pr_info("Will try `%s' and `%s'\n",
++ ksmbd_conv_charsets[to_codeset],
++ ksmbd_conv_charsets[from_codeset]);
++ goto retry;
++ }
++
++ pr_err("Can't convert string: %s\n", err->message);
++ g_error_free(err);
++ return NULL;
++ }
++
++ return converted;
++}
++
++int send_signal_to_ksmbd_mountd(int signo)
++{
++ int fd, ret = -EINVAL;
++ char pid_buf[10] = {0};
++ int pid;
++
++ fd = open(KSMBD_LOCK_FILE, O_RDONLY);
++ if (fd < 0) {
++ pr_debug("Can't open `%s': %m\n", KSMBD_LOCK_FILE);
++ return ret;
++ }
++
++ if (read(fd, &pid_buf, sizeof(pid_buf)) == -1) {
++ pr_err("Can't read manager PID: %m\n");
++ goto out;
++ }
++
++ pid = strtol(pid_buf, NULL, 10);
++ if (signo)
++ pr_debug("Send signal %d (%s) to PID %d\n",
++ signo, strsignal(signo), pid);
++ if (kill(pid, signo) == -1) {
++ ret = -errno;
++ if (signo)
++ pr_err("Unable to send signal %d (%s) to PID %d: %m\n",
++ signo, strsignal(signo), pid);
++ goto out;
++ }
++
++ ret = 0;
++out:
++ close(fd);
++ return ret;
++}
++
++int test_file_access(char *conf)
++{
++ int fd;
++
++ fd = open(conf, O_RDWR | O_CREAT, S_IRWXU | S_IRGRP);
++ if (fd < 0) {
++ pr_debug("Can't open `%s': %m\n", conf);
++ return -EINVAL;
++ }
++
++ close(fd);
++ return 0;
++}
++
++int main(int argc, char **argv)
++{
++ char *base_name;
++
++ set_logger_app_name("ksmbd.tools");
++
++ if (!*argv)
++ return EXIT_FAILURE;
++
++ base_name = strrchr(*argv, '/');
++ base_name = base_name ? base_name + 1 : *argv;
++
++ if (!strcmp(base_name, "ksmbd.addshare"))
++ return addshare_main(argc, argv);
++ if (!strcmp(base_name, "ksmbd.adduser"))
++ return adduser_main(argc, argv);
++ if (!strcmp(base_name, "ksmbd.control"))
++ return control_main(argc, argv);
++ if (!strcmp(base_name, "ksmbd.mountd"))
++ return mountd_main(argc, argv);
++
++ pr_err("Unknown base name: %s\n", base_name);
++ return EXIT_FAILURE;
++}
+--- a/lib/asn1.c
++++ /dev/null
+@@ -1,391 +0,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).
+- */
+-
+-/*****************************************************************************
+- *
+- * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
+- *
+- *****************************************************************************/
+-#include <stdlib.h>
+-#include <limits.h>
+-#include <string.h>
+-#include <errno.h>
+-#include <glib.h>
+-
+-#include "asn1.h"
+-
+-void
+-asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
+-{
+- ctx->begin = buf;
+- ctx->end = buf + len;
+- ctx->pointer = buf;
+- ctx->error = ASN1_ERR_NOERROR;
+-}
+-
+-static unsigned char
+-asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
+-{
+- if (ctx->pointer >= ctx->end) {
+- ctx->error = ASN1_ERR_DEC_EMPTY;
+- return 0;
+- }
+- *ch = *(ctx->pointer)++;
+- return 1;
+-}
+-
+-static unsigned char
+-asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
+-{
+- unsigned char ch;
+-
+- *tag = 0;
+-
+- do {
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+- *tag <<= 7;
+- *tag |= ch & 0x7F;
+- } while ((ch & 0x80) == 0x80);
+- return 1;
+-}
+-
+-static unsigned char
+-asn1_id_decode(struct asn1_ctx *ctx,
+- unsigned int *cls, unsigned int *con, unsigned int *tag)
+-{
+- unsigned char ch;
+-
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+-
+- *cls = (ch & 0xC0) >> 6;
+- *con = (ch & 0x20) >> 5;
+- *tag = (ch & 0x1F);
+-
+- if (*tag == 0x1F) {
+- if (!asn1_tag_decode(ctx, tag))
+- return 0;
+- }
+- return 1;
+-}
+-
+-static unsigned char
+-asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
+-{
+- unsigned char ch, cnt;
+-
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+-
+- if (ch == 0x80)
+- *def = 0;
+- else {
+- *def = 1;
+-
+- if (ch < 0x80)
+- *len = ch;
+- else {
+- cnt = (unsigned char) (ch & 0x7F);
+- *len = 0;
+-
+- while (cnt > 0) {
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+- *len <<= 8;
+- *len |= ch;
+- cnt--;
+- }
+- }
+- }
+-
+- /* don't trust len bigger than ctx buffer */
+- if (*len > ctx->end - ctx->pointer)
+- return 0;
+-
+- return 1;
+-}
+-
+-unsigned char
+-asn1_header_decode(struct asn1_ctx *ctx,
+- unsigned char **eoc,
+- unsigned int *cls, unsigned int *con, unsigned int *tag)
+-{
+- unsigned int def = 0;
+- unsigned int len = 0;
+-
+- if (!asn1_id_decode(ctx, cls, con, tag))
+- return 0;
+-
+- if (!asn1_length_decode(ctx, &def, &len))
+- return 0;
+-
+- /* primitive shall be definite, indefinite shall be constructed */
+- if (*con == ASN1_PRI && !def)
+- return 0;
+-
+- if (def)
+- *eoc = ctx->pointer + len;
+- else
+- *eoc = NULL;
+- return 1;
+-}
+-
+-static unsigned char
+-asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
+-{
+- unsigned char ch;
+-
+- if (eoc == NULL) {
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+-
+- if (ch != 0x00) {
+- ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+- return 0;
+- }
+-
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+-
+- if (ch != 0x00) {
+- ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
+- return 0;
+- }
+- return 1;
+- }
+-
+- if (ctx->pointer != eoc) {
+- ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
+- return 0;
+- }
+- return 1;
+-}
+-
+-unsigned char
+-asn1_octets_decode(struct asn1_ctx *ctx,
+- unsigned char *eoc,
+- unsigned char **octets, unsigned int *len)
+-{
+- unsigned char *ptr;
+-
+- *len = 0;
+-
+- *octets = malloc(eoc - ctx->pointer);
+- if (*octets == NULL)
+- return 0;
+-
+- ptr = *octets;
+- while (ctx->pointer < eoc) {
+- if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
+- free(*octets);
+- *octets = NULL;
+- return 0;
+- }
+- (*len)++;
+- }
+- return 1;
+-}
+-
+-unsigned char asn1_read(struct asn1_ctx *ctx,
+- unsigned char **buf, unsigned int len)
+-{
+- *buf = NULL;
+- if (ctx->end - ctx->pointer < len) {
+- ctx->error = ASN1_ERR_DEC_EMPTY;
+- return 0;
+- }
+-
+- *buf = malloc(len);
+- if (!*buf)
+- return 0;
+- memcpy(*buf, ctx->pointer, len);
+- ctx->pointer += len;
+- return 1;
+-}
+-
+-static unsigned char
+-asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
+-{
+- unsigned char ch;
+-
+- *subid = 0;
+-
+- do {
+- if (!asn1_octet_decode(ctx, &ch))
+- return 0;
+-
+- *subid <<= 7;
+- *subid |= ch & 0x7F;
+- } while ((ch & 0x80) == 0x80);
+- return 1;
+-}
+-
+-int
+-asn1_oid_decode(struct asn1_ctx *ctx,
+- unsigned char *eoc, unsigned long **oid, unsigned int *len)
+-{
+- unsigned long subid;
+- unsigned int size;
+- unsigned long *optr;
+-
+- size = eoc - ctx->pointer + 1;
+-
+- /* first subid actually encodes first two subids */
+- if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
+- return 0;
+-
+- *oid = g_try_malloc0_n(size, sizeof(unsigned long));
+- if (*oid == NULL)
+- return 0;
+-
+- optr = *oid;
+-
+- if (!asn1_subid_decode(ctx, &subid)) {
+- g_free(*oid);
+- *oid = NULL;
+- return 0;
+- }
+-
+- 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;
+- }
+-
+- *len = 2;
+- optr += 2;
+-
+- while (ctx->pointer < eoc) {
+- if (++(*len) > size) {
+- ctx->error = ASN1_ERR_DEC_BADVALUE;
+- g_free(*oid);
+- *oid = NULL;
+- return 0;
+- }
+-
+- if (!asn1_subid_decode(ctx, optr++)) {
+- g_free(*oid);
+- *oid = NULL;
+- return 0;
+- }
+- }
+- return 1;
+-}
+-
+-/* return the size of @depth-nested headers + payload */
+-int asn1_header_len(unsigned int payload_len, int depth)
+-{
+- unsigned int len;
+- int i;
+-
+- len = payload_len;
+- for (i = 0; i < depth; i++) {
+- /* length */
+- if (len >= (1 << 24))
+- len += 5;
+- else if (len >= (1 << 16))
+- len += 4;
+- else if (len >= (1 << 8))
+- len += 3;
+- else if (len >= (1 << 7))
+- len += 2;
+- else
+- len += 1;
+- /* 1-byte header */
+- len += 1;
+- }
+- return len;
+-}
+-
+-int asn1_oid_encode(const unsigned long *in_oid, int in_len,
+- unsigned char **out_oid, int *out_len)
+-{
+- unsigned char *oid;
+- unsigned long id;
+- int i;
+-
+- *out_oid = g_try_malloc0_n(in_len, 5);
+- if (*out_oid == NULL)
+- return -ENOMEM;
+-
+- oid = *out_oid;
+- *oid++ = (unsigned char)(40 * in_oid[0] + in_oid[1]);
+- for (i = 2; i < in_len; i++) {
+- id = in_oid[i];
+- if (id >= (1 << 28))
+- *oid++ = (0x80 | ((id>>28) & 0x7F));
+- if (id >= (1 << 21))
+- *oid++ = (0x80 | ((id>>21) & 0x7F));
+- if (id >= (1 << 14))
+- *oid++ = (0x80 | ((id>>14) & 0x7F));
+- if (id >= (1 << 7))
+- *oid++ = (0x80 | ((id>>7) & 0x7F));
+- *oid++ = id & 0x7F;
+- }
+- *out_len = (int)(oid - *out_oid);
+- return 0;
+-}
+-
+-/*
+- * @len is the sum of all sizes of header, length and payload.
+- * it will be decreased by the sum of sizes of header and length.
+- */
+-int asn1_header_encode(unsigned char **buf,
+- unsigned int cls, unsigned int con, unsigned int tag,
+- unsigned int *len)
+-{
+- unsigned char *loc;
+- unsigned int r_len;
+-
+- /* at least, 1-byte header + 1-byte length is needed. */
+- if (*len < 2)
+- return -EINVAL;
+-
+- loc = *buf;
+- r_len = *len;
+-
+- *loc++ = ((cls & 0x3) << 6) | ((con & 0x1) << 5) | (tag & 0x1F);
+- r_len -= 1;
+-
+- if (r_len - 1 < (1 << 7)) {
+- r_len -= 1;
+- *loc++ = (unsigned char)(r_len & 0x7F);
+- } else if (r_len - 2 < (1 << 8)) {
+- r_len -= 2;
+- *loc++ = 0x81;
+- *loc++ = (unsigned char)(r_len & 0xFF);
+- } else if (r_len - 3 < (1 << 16)) {
+- r_len -= 3;
+- *loc++ = 0x82;
+- *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+- *loc++ = (unsigned char)(r_len & 0xFF);
+- } else if (r_len - 4 < (1 << 24)) {
+- r_len -= 4;
+- *loc++ = 0x83;
+- *loc++ = (unsigned char)((r_len>>16) & 0xFF);
+- *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+- *loc++ = (unsigned char)(r_len & 0xFF);
+- } else {
+- r_len -= 5;
+- *loc++ = 0x84;
+- *loc++ = (unsigned char)((r_len>>24) & 0xFF);
+- *loc++ = (unsigned char)((r_len>>16) & 0xFF);
+- *loc++ = (unsigned char)((r_len>>8) & 0xFF);
+- *loc++ = (unsigned char)(r_len & 0xFF);
+- }
+-
+- *buf = loc;
+- *len = r_len;
+- return 0;
+-}
+--- a/lib/management/spnego_mech.h
++++ /dev/null
+@@ -1,48 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0-or-later */
+-/*
+- * Copyright (C) 2020 LG Electronics
+- *
+- * linux-cifsd-devel@lists.sourceforge.net
+- */
+-
+-#ifndef _SPNEGO_MECH_H_
+-#define _SPNEGO_MECH_H_
+-
+-enum {
+- SPNEGO_MECH_MSKRB5 = 0,
+- SPNEGO_MECH_KRB5,
+- SPNEGO_MAX_MECHS,
+-};
+-
+-struct spnego_mech_ctx;
+-
+-typedef int (*spnego_encode_t)(char *in_blob, int in_len,
+- const unsigned long *oid, int oid_len,
+- char **out_blob, int *out_len);
+-
+-struct spnego_mech_operations {
+- int (*setup)(struct spnego_mech_ctx *mech_ctx);
+- void (*cleanup)(struct spnego_mech_ctx *mech_ctx);
+- int (*handle_authen)(struct spnego_mech_ctx *mech_ctx,
+- char *in_blob, unsigned int in_len,
+- struct ksmbd_spnego_auth_out *auth_out,
+- spnego_encode_t encode);
+-};
+-
+-struct spnego_mech_ctx {
+- const unsigned long *oid;
+- int oid_len;
+- void *private;
+- union {
+- struct {
+- void *keytab_name;
+- void *service_name;
+- } krb5;
+- } params;
+- struct spnego_mech_operations *ops;
+-};
+-
+-extern struct spnego_mech_operations spnego_krb5_operations;
+-extern struct spnego_mech_operations spnego_mskrb5_operations;
+-
+-#endif
+--- /dev/null
++++ b/tools/asn1.c
+@@ -0,0 +1,391 @@
++// 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).
++ */
++
++/*****************************************************************************
++ *
++ * Basic ASN.1 decoding routines (gxsnmp author Dirk Wisse)
++ *
++ *****************************************************************************/
++#include <stdlib.h>
++#include <limits.h>
++#include <string.h>
++#include <errno.h>
++#include <glib.h>
++
++#include "asn1.h"
++
++void
++asn1_open(struct asn1_ctx *ctx, unsigned char *buf, unsigned int len)
++{
++ ctx->begin = buf;
++ ctx->end = buf + len;
++ ctx->pointer = buf;
++ ctx->error = ASN1_ERR_NOERROR;
++}
++
++static unsigned char
++asn1_octet_decode(struct asn1_ctx *ctx, unsigned char *ch)
++{
++ if (ctx->pointer >= ctx->end) {
++ ctx->error = ASN1_ERR_DEC_EMPTY;
++ return 0;
++ }
++ *ch = *(ctx->pointer)++;
++ return 1;
++}
++
++static unsigned char
++asn1_tag_decode(struct asn1_ctx *ctx, unsigned int *tag)
++{
++ unsigned char ch;
++
++ *tag = 0;
++
++ do {
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++ *tag <<= 7;
++ *tag |= ch & 0x7F;
++ } while ((ch & 0x80) == 0x80);
++ return 1;
++}
++
++static unsigned char
++asn1_id_decode(struct asn1_ctx *ctx,
++ unsigned int *cls, unsigned int *con, unsigned int *tag)
++{
++ unsigned char ch;
++
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++
++ *cls = (ch & 0xC0) >> 6;
++ *con = (ch & 0x20) >> 5;
++ *tag = (ch & 0x1F);
++
++ if (*tag == 0x1F) {
++ if (!asn1_tag_decode(ctx, tag))
++ return 0;
++ }
++ return 1;
++}
++
++static unsigned char
++asn1_length_decode(struct asn1_ctx *ctx, unsigned int *def, unsigned int *len)
++{
++ unsigned char ch, cnt;
++
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++
++ if (ch == 0x80)
++ *def = 0;
++ else {
++ *def = 1;
++
++ if (ch < 0x80)
++ *len = ch;
++ else {
++ cnt = (unsigned char) (ch & 0x7F);
++ *len = 0;
++
++ while (cnt > 0) {
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++ *len <<= 8;
++ *len |= ch;
++ cnt--;
++ }
++ }
++ }
++
++ /* don't trust len bigger than ctx buffer */
++ if (*len > ctx->end - ctx->pointer)
++ return 0;
++
++ return 1;
++}
++
++unsigned char
++asn1_header_decode(struct asn1_ctx *ctx,
++ unsigned char **eoc,
++ unsigned int *cls, unsigned int *con, unsigned int *tag)
++{
++ unsigned int def = 0;
++ unsigned int len = 0;
++
++ if (!asn1_id_decode(ctx, cls, con, tag))
++ return 0;
++
++ if (!asn1_length_decode(ctx, &def, &len))
++ return 0;
++
++ /* primitive shall be definite, indefinite shall be constructed */
++ if (*con == ASN1_PRI && !def)
++ return 0;
++
++ if (def)
++ *eoc = ctx->pointer + len;
++ else
++ *eoc = NULL;
++ return 1;
++}
++
++static unsigned char
++asn1_eoc_decode(struct asn1_ctx *ctx, unsigned char *eoc)
++{
++ unsigned char ch;
++
++ if (eoc == NULL) {
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++
++ if (ch != 0x00) {
++ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
++ return 0;
++ }
++
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++
++ if (ch != 0x00) {
++ ctx->error = ASN1_ERR_DEC_EOC_MISMATCH;
++ return 0;
++ }
++ return 1;
++ }
++
++ if (ctx->pointer != eoc) {
++ ctx->error = ASN1_ERR_DEC_LENGTH_MISMATCH;
++ return 0;
++ }
++ return 1;
++}
++
++unsigned char
++asn1_octets_decode(struct asn1_ctx *ctx,
++ unsigned char *eoc,
++ unsigned char **octets, unsigned int *len)
++{
++ unsigned char *ptr;
++
++ *len = 0;
++
++ *octets = malloc(eoc - ctx->pointer);
++ if (*octets == NULL)
++ return 0;
++
++ ptr = *octets;
++ while (ctx->pointer < eoc) {
++ if (!asn1_octet_decode(ctx, (unsigned char *) ptr++)) {
++ free(*octets);
++ *octets = NULL;
++ return 0;
++ }
++ (*len)++;
++ }
++ return 1;
++}
++
++unsigned char asn1_read(struct asn1_ctx *ctx,
++ unsigned char **buf, unsigned int len)
++{
++ *buf = NULL;
++ if (ctx->end - ctx->pointer < len) {
++ ctx->error = ASN1_ERR_DEC_EMPTY;
++ return 0;
++ }
++
++ *buf = malloc(len);
++ if (!*buf)
++ return 0;
++ memcpy(*buf, ctx->pointer, len);
++ ctx->pointer += len;
++ return 1;
++}
++
++static unsigned char
++asn1_subid_decode(struct asn1_ctx *ctx, unsigned long *subid)
++{
++ unsigned char ch;
++
++ *subid = 0;
++
++ do {
++ if (!asn1_octet_decode(ctx, &ch))
++ return 0;
++
++ *subid <<= 7;
++ *subid |= ch & 0x7F;
++ } while ((ch & 0x80) == 0x80);
++ return 1;
++}
++
++int
++asn1_oid_decode(struct asn1_ctx *ctx,
++ unsigned char *eoc, unsigned long **oid, unsigned int *len)
++{
++ unsigned long subid;
++ unsigned int size;
++ unsigned long *optr;
++
++ size = eoc - ctx->pointer + 1;
++
++ /* first subid actually encodes first two subids */
++ if (size < 2 || size > UINT_MAX/sizeof(unsigned long))
++ return 0;
++
++ *oid = g_try_malloc0_n(size, sizeof(unsigned long));
++ if (*oid == NULL)
++ return 0;
++
++ optr = *oid;
++
++ if (!asn1_subid_decode(ctx, &subid)) {
++ g_free(*oid);
++ *oid = NULL;
++ return 0;
++ }
++
++ 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;
++ }
++
++ *len = 2;
++ optr += 2;
++
++ while (ctx->pointer < eoc) {
++ if (++(*len) > size) {
++ ctx->error = ASN1_ERR_DEC_BADVALUE;
++ g_free(*oid);
++ *oid = NULL;
++ return 0;
++ }
++
++ if (!asn1_subid_decode(ctx, optr++)) {
++ g_free(*oid);
++ *oid = NULL;
++ return 0;
++ }
++ }
++ return 1;
++}
++
++/* return the size of @depth-nested headers + payload */
++int asn1_header_len(unsigned int payload_len, int depth)
++{
++ unsigned int len;
++ int i;
++
++ len = payload_len;
++ for (i = 0; i < depth; i++) {
++ /* length */
++ if (len >= (1 << 24))
++ len += 5;
++ else if (len >= (1 << 16))
++ len += 4;
++ else if (len >= (1 << 8))
++ len += 3;
++ else if (len >= (1 << 7))
++ len += 2;
++ else
++ len += 1;
++ /* 1-byte header */
++ len += 1;
++ }
++ return len;
++}
++
++int asn1_oid_encode(const unsigned long *in_oid, int in_len,
++ unsigned char **out_oid, int *out_len)
++{
++ unsigned char *oid;
++ unsigned long id;
++ int i;
++
++ *out_oid = g_try_malloc0_n(in_len, 5);
++ if (*out_oid == NULL)
++ return -ENOMEM;
++
++ oid = *out_oid;
++ *oid++ = (unsigned char)(40 * in_oid[0] + in_oid[1]);
++ for (i = 2; i < in_len; i++) {
++ id = in_oid[i];
++ if (id >= (1 << 28))
++ *oid++ = (0x80 | ((id>>28) & 0x7F));
++ if (id >= (1 << 21))
++ *oid++ = (0x80 | ((id>>21) & 0x7F));
++ if (id >= (1 << 14))
++ *oid++ = (0x80 | ((id>>14) & 0x7F));
++ if (id >= (1 << 7))
++ *oid++ = (0x80 | ((id>>7) & 0x7F));
++ *oid++ = id & 0x7F;
++ }
++ *out_len = (int)(oid - *out_oid);
++ return 0;
++}
++
++/*
++ * @len is the sum of all sizes of header, length and payload.
++ * it will be decreased by the sum of sizes of header and length.
++ */
++int asn1_header_encode(unsigned char **buf,
++ unsigned int cls, unsigned int con, unsigned int tag,
++ unsigned int *len)
++{
++ unsigned char *loc;
++ unsigned int r_len;
++
++ /* at least, 1-byte header + 1-byte length is needed. */
++ if (*len < 2)
++ return -EINVAL;
++
++ loc = *buf;
++ r_len = *len;
++
++ *loc++ = ((cls & 0x3) << 6) | ((con & 0x1) << 5) | (tag & 0x1F);
++ r_len -= 1;
++
++ if (r_len - 1 < (1 << 7)) {
++ r_len -= 1;
++ *loc++ = (unsigned char)(r_len & 0x7F);
++ } else if (r_len - 2 < (1 << 8)) {
++ r_len -= 2;
++ *loc++ = 0x81;
++ *loc++ = (unsigned char)(r_len & 0xFF);
++ } else if (r_len - 3 < (1 << 16)) {
++ r_len -= 3;
++ *loc++ = 0x82;
++ *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++ *loc++ = (unsigned char)(r_len & 0xFF);
++ } else if (r_len - 4 < (1 << 24)) {
++ r_len -= 4;
++ *loc++ = 0x83;
++ *loc++ = (unsigned char)((r_len>>16) & 0xFF);
++ *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++ *loc++ = (unsigned char)(r_len & 0xFF);
++ } else {
++ r_len -= 5;
++ *loc++ = 0x84;
++ *loc++ = (unsigned char)((r_len>>24) & 0xFF);
++ *loc++ = (unsigned char)((r_len>>16) & 0xFF);
++ *loc++ = (unsigned char)((r_len>>8) & 0xFF);
++ *loc++ = (unsigned char)(r_len & 0xFF);
++ }
++
++ *buf = loc;
++ *len = r_len;
++ return 0;
++}
+--- /dev/null
++++ b/tools/management/spnego_mech.h
+@@ -0,0 +1,48 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++/*
++ * Copyright (C) 2020 LG Electronics
++ *
++ * linux-cifsd-devel@lists.sourceforge.net
++ */
++
++#ifndef _SPNEGO_MECH_H_
++#define _SPNEGO_MECH_H_
++
++enum {
++ SPNEGO_MECH_MSKRB5 = 0,
++ SPNEGO_MECH_KRB5,
++ SPNEGO_MAX_MECHS,
++};
++
++struct spnego_mech_ctx;
++
++typedef int (*spnego_encode_t)(char *in_blob, int in_len,
++ const unsigned long *oid, int oid_len,
++ char **out_blob, int *out_len);
++
++struct spnego_mech_operations {
++ int (*setup)(struct spnego_mech_ctx *mech_ctx);
++ void (*cleanup)(struct spnego_mech_ctx *mech_ctx);
++ int (*handle_authen)(struct spnego_mech_ctx *mech_ctx,
++ char *in_blob, unsigned int in_len,
++ struct ksmbd_spnego_auth_out *auth_out,
++ spnego_encode_t encode);
++};
++
++struct spnego_mech_ctx {
++ const unsigned long *oid;
++ int oid_len;
++ void *private;
++ union {
++ struct {
++ void *keytab_name;
++ void *service_name;
++ } krb5;
++ } params;
++ struct spnego_mech_operations *ops;
++};
++
++extern struct spnego_mech_operations spnego_krb5_operations;
++extern struct spnego_mech_operations spnego_mskrb5_operations;
++
++#endif