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