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