From de64b43e3cf7a3cb3501dfde3d8b1fbfe7478c76 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 13 Oct 2022 12:27:07 -0700 Subject: [PATCH] ksmbd-tools: update to 3.4.6 Switch to git tarball as the meson files did not get added to the official one. Backport busybox style binaries. Saves on size. Signed-off-by: Rosen Penev --- net/ksmbd-tools/Makefile | 49 +- net/ksmbd-tools/patches/010-meson.patch | 151 - net/ksmbd-tools/patches/010-muon.patch | 69 + net/ksmbd-tools/patches/020-meson.patch | 9190 +++++++++++++++++++++++ net/ksmbd-tools/patches/030-glib.patch | 10 + 5 files changed, 9279 insertions(+), 190 deletions(-) delete mode 100644 net/ksmbd-tools/patches/010-meson.patch create mode 100644 net/ksmbd-tools/patches/010-muon.patch create mode 100644 net/ksmbd-tools/patches/020-meson.patch create mode 100644 net/ksmbd-tools/patches/030-glib.patch diff --git a/net/ksmbd-tools/Makefile b/net/ksmbd-tools/Makefile index e8a7482f7..8cb698111 100644 --- a/net/ksmbd-tools/Makefile +++ b/net/ksmbd-tools/Makefile @@ -1,12 +1,12 @@ include $(TOPDIR)/rules.mk PKG_NAME:=ksmbd-tools -PKG_VERSION:=3.4.5 PKG_RELEASE:=$(AUTORELEASE) -PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz -PKG_SOURCE_URL:=https://codeload.github.com/cifsd-team/ksmbd-tools/tar.gz/$(PKG_VERSION)? -PKG_HASH:=e22e5bef29ffa2670fc82c490ad4dc6eb00963b4f963dd1852c811b437c77ee1 +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/cifsd-team/ksmbd-tools +PKG_SOURCE_VERSION:=3.4.6 +PKG_MIRROR_HASH:=c78dace3320cf8a273738b8f3e67bed24c812695f2fab2fbaeae06ec8a15cb77 PKG_LICENSE:=GPL-2.0-or-later PKG_LICENSE_FILES:=COPYING @@ -17,9 +17,6 @@ include $(INCLUDE_DIR)/package.mk include $(INCLUDE_DIR)/nls.mk include $(INCLUDE_DIR)/meson.mk -TAR_OPTIONS+= --strip-components 1 -TAR_CMD=$(HOST_TAR) -C $(1) $(TAR_OPTIONS) - define Package/ksmbd-tools/Default SECTION:=net CATEGORY:=Network @@ -50,27 +47,6 @@ define Package/ksmbd-server/config select PACKAGE_wsdd2 endef -define Package/ksmbd-utils - $(call Package/ksmbd-tools/Default) - TITLE+= user management-util -endef - -define Package/ksmbd-utils/description - installs: ksmbd.adduser (ksmbd.addshare) - - Tool needed to create the ksmbdpwd.db, to manage per user share passwords. - NOTE: Not needed for 'guest only' shares. -endef - -define Package/ksmbd-utils/config - config KSMBD_UTILS_SHAREADD - bool "Add ksmbd.addshare util" - depends on PACKAGE_ksmbd-utils - help - Add the ksmbd.addshare tool, to directly manipulate the /etc/ksmbd/smb.conf. - default n -endef - define Package/ksmbd-avahi-service $(call Package/ksmbd-tools/Default) TITLE+= (Avahi service) @@ -98,23 +74,19 @@ TARGET_CFLAGS += -ffunction-sections -fdata-sections TARGET_LDFLAGS += -Wl,--gc-sections,--as-needed define Package/ksmbd-server/install + $(INSTALL_DIR) $(1)/usr/libexec + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/libexec/ksmbd.tools $(1)/usr/libexec/ $(INSTALL_DIR) $(1)/usr/sbin - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.mountd $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.addshare $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.adduser $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.control $(1)/usr/sbin/ + $(CP) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.mountd $(1)/usr/sbin/ $(INSTALL_DIR) $(1)/etc/config $(1)/etc/ksmbd $(1)/etc/init.d $(INSTALL_CONF) ./files/ksmbd.config $(1)/etc/config/ksmbd $(INSTALL_DATA) ./files/smb.conf.template $(1)/etc/ksmbd/ $(INSTALL_BIN) ./files/ksmbd.init $(1)/etc/init.d/ksmbd # copy examples until we have a wiki page $(INSTALL_DATA) ./files/ksmbd.config.example $(1)/etc/ksmbd/ - $(INSTALL_DATA) $(PKG_BUILD_DIR)/Documentation/configuration.txt $(1)/etc/ksmbd/ -endef - -define Package/ksmbd-utils/install - $(INSTALL_DIR) $(1)/usr/sbin - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.adduser $(1)/usr/sbin/ -ifeq ($(CONFIG_KSMBD_UTILS_SHAREADD),y) - $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ksmbd.addshare $(1)/usr/sbin/ -endif endef define Package/ksmbd-avahi-service/install @@ -139,6 +111,5 @@ define Package/ksmbd-avahi-service/conffiles endef $(eval $(call BuildPackage,ksmbd-server)) -$(eval $(call BuildPackage,ksmbd-utils)) $(eval $(call BuildPackage,ksmbd-avahi-service)) $(eval $(call BuildPackage,ksmbd-hotplug)) diff --git a/net/ksmbd-tools/patches/010-meson.patch b/net/ksmbd-tools/patches/010-meson.patch deleted file mode 100644 index 2fffcd4ca..000000000 --- a/net/ksmbd-tools/patches/010-meson.patch +++ /dev/null @@ -1,151 +0,0 @@ -From dc80281ba0d325e71ff6cd2c1d7d525c889b3996 Mon Sep 17 00:00:00 2001 -From: Rosen Penev -Date: Sat, 2 Jan 2021 21:05:53 -0800 -Subject: [PATCH] add meson build - -meson compiles faster and is simpler than autotools. - -Signed-off-by: Rosen Penev ---- - addshare/meson.build | 11 +++++++++++ - adduser/meson.build | 13 +++++++++++++ - control/meson.build | 9 +++++++++ - lib/meson.build | 18 ++++++++++++++++++ - meson.build | 33 +++++++++++++++++++++++++++++++++ - meson_options.txt | 4 ++++ - mountd/meson.build | 17 +++++++++++++++++ - 7 files changed, 105 insertions(+) - create mode 100644 addshare/meson.build - create mode 100644 adduser/meson.build - create mode 100644 control/meson.build - create mode 100644 lib/meson.build - create mode 100644 meson.build - create mode 100644 meson_options.txt - create mode 100644 mountd/meson.build - ---- /dev/null -+++ b/addshare/meson.build -@@ -0,0 +1,11 @@ -+addshare = executable( -+ 'ksmbd.addshare', -+ 'share_admin.c', -+ 'addshare.c', -+ 'share_admin.h', -+ dependencies: [glib_dep, netlink_dep], -+ include_directories: tools_incdir, -+ link_with: libksmbdtools, -+ install: true, -+ install_dir: get_option('sbindir'), -+) ---- /dev/null -+++ b/adduser/meson.build -@@ -0,0 +1,13 @@ -+adduser = executable( -+ 'ksmbd.adduser', -+ 'md4_hash.c', -+ 'user_admin.c', -+ 'adduser.c', -+ 'md4_hash.h', -+ 'user_admin.h', -+ dependencies: [glib_dep, netlink_dep], -+ include_directories: tools_incdir, -+ link_with: libksmbdtools, -+ install: true, -+ install_dir: get_option('sbindir'), -+) ---- /dev/null -+++ b/control/meson.build -@@ -0,0 +1,9 @@ -+control = executable( -+ 'ksmbd.control', -+ 'control.c', -+ dependencies: [glib_dep, netlink_dep], -+ include_directories: tools_incdir, -+ link_with: libksmbdtools, -+ install: true, -+ install_dir: get_option('sbindir'), -+) ---- /dev/null -+++ b/lib/meson.build -@@ -0,0 +1,18 @@ -+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]) ---- /dev/null -+++ b/meson.build -@@ -0,0 +1,33 @@ -+project('ksmbsd-tools', 'c', -+ version: run_command(find_program('awk'), '''/define KSMBD_TOOLS_VERSION / \ -+ { gsub(/"/,"",$3); printf "%s", $3 }''', 'include/version.h', check: true).stdout(), -+ default_options: 'c_std=gnu99') -+ -+tools_incdir = include_directories(['include', '.']) -+ -+glib_dep = dependency('glib-2.0', static: true) -+netlink_dep = dependency('libnl-genl-3.0') -+krb5_dep = dependency('krb5', required: get_option('krb5')) -+ -+cc = meson.get_compiler('c') -+ -+cdata = configuration_data() -+add_project_arguments('-DHAVE_CONFIG_H', language: 'c') -+if krb5_dep.found() -+ cdata.set('CONFIG_KRB5', krb5_dep.found()) -+ cdata.set('HAVE_KRB5_KEYBLOCK_KEYVALUE', cc.has_member('krb5_keyblock', 'keyvalue', prefix: '#include ')) -+ cdata.set('HAVE_KRB5_AUTHENTICATOR_CLIENT', cc.has_member('krb5_authenticator', 'client', prefix: '#include ')) -+ cdata.set('HAVE_KRB5_AUTH_CON_GETRECVSUBKEY', cc.has_function('krb5_auth_con_getrecvsubkey', dependencies: krb5_dep)) -+ cdata.set('HAVE_KRB5_AUTH_CON_GETAUTHENTICATOR_DOUBLE_POINTER', cc.compiles('''#include -+ krb5_error_code krb5_auth_con_getauthenticator(krb5_context, krb5_auth_context, krb5_authenticator**);''', dependencies: krb5_dep)) -+endif -+cfile = configure_file( -+ output: 'config.h', -+ configuration: cdata, -+) -+ -+subdir('lib') -+subdir('addshare') -+subdir('adduser') -+subdir('control') -+subdir('mountd') ---- /dev/null -+++ b/meson_options.txt -@@ -0,0 +1,4 @@ -+option('krb5', type : 'feature', -+ description : 'Build with Kerberos support', -+ value : 'disabled', -+) ---- /dev/null -+++ b/mountd/meson.build -@@ -0,0 +1,17 @@ -+mountd = executable( -+ 'ksmbd.mountd', -+ 'worker.c', -+ 'ipc.c', -+ 'rpc.c', -+ 'rpc_srvsvc.c', -+ 'rpc_wkssvc.c', -+ 'mountd.c', -+ 'smbacl.c', -+ 'rpc_samr.c', -+ 'rpc_lsarpc.c', -+ dependencies: [glib_dep, netlink_dep], -+ include_directories: tools_incdir, -+ link_with: libksmbdtools, -+ install: true, -+ install_dir: get_option('sbindir'), -+) diff --git a/net/ksmbd-tools/patches/010-muon.patch b/net/ksmbd-tools/patches/010-muon.patch new file mode 100644 index 000000000..967c92702 --- /dev/null +++ b/net/ksmbd-tools/patches/010-muon.patch @@ -0,0 +1,69 @@ +From 3281f325c820a72057ea639e0d11ad68d5703b43 Mon Sep 17 00:00:00 2001 +From: Rosen Penev +Date: Thu, 6 Oct 2022 18:07:01 -0700 +Subject: [PATCH] ksmbd-tools: run meson through muon analyze +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Mostly unused variable removals. Removed pointless [] in +include_directories. + +Signed-off-by: Rosen Penev +Acked-by: Atte Heikkilä +Signed-off-by: Namjae Jeon +--- + addshare/meson.build | 2 +- + adduser/meson.build | 2 +- + control/meson.build | 2 +- + meson.build | 4 ++-- + mountd/meson.build | 2 +- + 5 files changed, 6 insertions(+), 6 deletions(-) + +--- a/addshare/meson.build ++++ b/addshare/meson.build +@@ -1,4 +1,4 @@ +-addshare = executable( ++executable( + 'ksmbd.addshare', + 'share_admin.c', + 'addshare.c', +--- a/adduser/meson.build ++++ b/adduser/meson.build +@@ -1,4 +1,4 @@ +-adduser = executable( ++executable( + 'ksmbd.adduser', + 'md4_hash.c', + 'user_admin.c', +--- a/control/meson.build ++++ b/control/meson.build +@@ -1,4 +1,4 @@ +-control = executable( ++executable( + 'ksmbd.control', + 'control.c', + dependencies: [ +--- a/meson.build ++++ b/meson.build +@@ -13,10 +13,10 @@ exec awk '/define KSMBD_TOOLS_VERSION / + meson_version: '>= 0.51.0', + ) + +-tools_incdir = include_directories([ ++tools_incdir = include_directories( + '.', + 'include', +-]) ++) + + glib_dep = dependency( + 'glib-2.0', +--- a/mountd/meson.build ++++ b/mountd/meson.build +@@ -1,4 +1,4 @@ +-mountd = executable( ++executable( + 'ksmbd.mountd', + 'worker.c', + 'ipc.c', diff --git a/net/ksmbd-tools/patches/020-meson.patch b/net/ksmbd-tools/patches/020-meson.patch new file mode 100644 index 000000000..adc01b655 --- /dev/null +++ b/net/ksmbd-tools/patches/020-meson.patch @@ -0,0 +1,9190 @@ +From 6b74583bd62e2b6ed6b76ff72d8fc7c2d8f26510 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Atte=20Heikkil=C3=A4?= +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ä +--- + .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 + + #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 + + #include +-#include ++#include + + #include + +--- 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 + + #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 + + #include +-#include ++#include + + #include + #include +--- 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 + #include + +-#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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#ifdef HAVE_CONFIG_H +-#include +-#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef HAVE_CONFIG_H ++#include ++#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 + +-#include ++#include + #include + #include + #include +--- 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 ++#include + + #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 + #include + #include +-#include ++#include + + static GHashTable *pipes_table; + static GRWLock pipes_table_lock; +--- a/mountd/rpc_lsarpc.c ++++ b/mountd/rpc_lsarpc.c +@@ -16,7 +16,7 @@ + #include + #include + #include +-#include ++#include + + #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 + #include + #include +-#include ++#include + + #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 + #include +-#include ++#include + + #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 + #include +-#include ++#include + + #define WKSSVC_NETWKSTA_GET_INFO (0) + +--- a/mountd/smbacl.c ++++ b/mountd/smbacl.c +@@ -7,7 +7,7 @@ + */ + + #include +-#include ++#include + #include + + 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 + #include + +-#include ++#include + #include + #include + #include +--- 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +- +-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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 +-#include +-#include +- +-#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 ++#include ++#include ++ ++#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 +-#include +-#include +-#include +-#include +-#include +- +-#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +-#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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#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 +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#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 [/[@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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#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 [/[@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 +-#include +-#include +- +-#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 ++#include ++#include ++ ++#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 +-#include +-#include +- +-#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 ++#include ++#include ++ ++#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 +- +-#include +-#include +-#include +- +-#include +-#include +- +-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 ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 +-#include +-#include +-#include +-#include +- +-#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 ++#include ++#include ++#include ++#include ++ ++#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 diff --git a/net/ksmbd-tools/patches/030-glib.patch b/net/ksmbd-tools/patches/030-glib.patch new file mode 100644 index 000000000..1fb240a95 --- /dev/null +++ b/net/ksmbd-tools/patches/030-glib.patch @@ -0,0 +1,10 @@ +--- a/meson.build ++++ b/meson.build +@@ -21,6 +21,7 @@ include_dirs = include_directories( + glib_dep = dependency( + 'glib-2.0', + version: '>= 2.40', ++ static: true, + ) + libnl_dep = dependency( + 'libnl-genl-3.0',