summaryrefslogtreecommitdiffstats
path: root/src/tools
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/analyzer/Makefile.am22
-rw-r--r--src/tools/analyzer/Makefile.in853
-rw-r--r--src/tools/analyzer/__init__.py0
-rw-r--r--src/tools/analyzer/modules/__init__.py0
-rw-r--r--src/tools/analyzer/modules/request.py320
-rw-r--r--src/tools/analyzer/parser.py60
-rw-r--r--src/tools/analyzer/source_files.py76
-rw-r--r--src/tools/analyzer/source_journald.py46
-rw-r--r--src/tools/analyzer/source_reader.py27
-rwxr-xr-xsrc/tools/analyzer/sss_analyze5
-rw-r--r--src/tools/analyzer/sss_analyze.py104
-rw-r--r--src/tools/common/sss_colondb.c317
-rw-r--r--src/tools/common/sss_colondb.h96
-rw-r--r--src/tools/common/sss_process.c124
-rw-r--r--src/tools/common/sss_process.h29
-rw-r--r--src/tools/common/sss_tools.c617
-rw-r--r--src/tools/common/sss_tools.h105
-rw-r--r--src/tools/sss_cache.c1028
-rw-r--r--src/tools/sss_obfuscate123
-rw-r--r--src/tools/sss_override.c1975
-rw-r--r--src/tools/sss_seed.c896
-rw-r--r--src/tools/sss_signal.c38
-rw-r--r--src/tools/sssctl/sssctl.c354
-rw-r--r--src/tools/sssctl/sssctl.h152
-rw-r--r--src/tools/sssctl/sssctl_access_report.c422
-rw-r--r--src/tools/sssctl/sssctl_cache.c718
-rw-r--r--src/tools/sssctl/sssctl_cert.c289
-rw-r--r--src/tools/sssctl/sssctl_config.c201
-rw-r--r--src/tools/sssctl/sssctl_data.c538
-rw-r--r--src/tools/sssctl/sssctl_domains.c408
-rw-r--r--src/tools/sssctl/sssctl_logs.c608
-rw-r--r--src/tools/sssctl/sssctl_passkey.c42
-rw-r--r--src/tools/sssctl/sssctl_systemd.c95
-rw-r--r--src/tools/sssctl/sssctl_user_checks.c321
-rw-r--r--src/tools/sssd_check_socket_activated_responders.c147
-rw-r--r--src/tools/tools_mc_util.c237
-rw-r--r--src/tools/tools_util.c72
-rw-r--r--src/tools/tools_util.h51
-rw-r--r--src/tools/wrappers/sss_debuglevel.in4
39 files changed, 11520 insertions, 0 deletions
diff --git a/src/tools/analyzer/Makefile.am b/src/tools/analyzer/Makefile.am
new file mode 100644
index 0000000..b40043d
--- /dev/null
+++ b/src/tools/analyzer/Makefile.am
@@ -0,0 +1,22 @@
+sss_analyze_pythondir = $(libexecdir)/sssd
+
+dist_sss_analyze_python_SCRIPTS = \
+ sss_analyze \
+ $(NULL)
+
+pkgpythondir = $(python3dir)/sssd
+
+dist_pkgpython_DATA = \
+ __init__.py \
+ source_files.py \
+ source_journald.py \
+ source_reader.py \
+ parser.py \
+ sss_analyze.py \
+ $(NULL)
+
+modulesdir = $(pkgpythondir)/modules
+dist_modules_DATA = \
+ modules/__init__.py \
+ modules/request.py \
+ $(NULL)
diff --git a/src/tools/analyzer/Makefile.in b/src/tools/analyzer/Makefile.in
new file mode 100644
index 0000000..0018403
--- /dev/null
+++ b/src/tools/analyzer/Makefile.in
@@ -0,0 +1,853 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = src/tools/analyzer
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/lib-ld.m4 \
+ $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/version.m4 $(top_srcdir)/src/build_macros.m4 \
+ $(top_srcdir)/src/external/platform.m4 \
+ $(top_srcdir)/src/conf_macros.m4 \
+ $(top_srcdir)/src/external/pkg.m4 \
+ $(top_srcdir)/src/external/libpopt.m4 \
+ $(top_srcdir)/src/external/libtalloc.m4 \
+ $(top_srcdir)/src/external/libtdb.m4 \
+ $(top_srcdir)/src/external/libtevent.m4 \
+ $(top_srcdir)/src/external/libldb.m4 \
+ $(top_srcdir)/src/external/libdhash.m4 \
+ $(top_srcdir)/src/external/libini_config.m4 \
+ $(top_srcdir)/src/external/libgssapi_krb5.m4 \
+ $(top_srcdir)/src/external/pam.m4 \
+ $(top_srcdir)/src/external/ldap.m4 \
+ $(top_srcdir)/src/external/libpcre.m4 \
+ $(top_srcdir)/src/external/krb5.m4 \
+ $(top_srcdir)/src/external/libcares.m4 \
+ $(top_srcdir)/src/external/libcmocka.m4 \
+ $(top_srcdir)/src/external/docbook.m4 \
+ $(top_srcdir)/src/external/sizes.m4 \
+ $(top_srcdir)/src/external/python.m4 \
+ $(top_srcdir)/src/external/selinux.m4 \
+ $(top_srcdir)/src/external/crypto.m4 \
+ $(top_srcdir)/src/external/nsupdate.m4 \
+ $(top_srcdir)/src/external/libkeyutils.m4 \
+ $(top_srcdir)/src/external/libkrad.m4 \
+ $(top_srcdir)/src/external/libnl.m4 \
+ $(top_srcdir)/src/external/systemd.m4 \
+ $(top_srcdir)/src/external/pac_responder.m4 \
+ $(top_srcdir)/src/external/cifsidmap.m4 \
+ $(top_srcdir)/src/external/signal.m4 \
+ $(top_srcdir)/src/external/inotify.m4 \
+ $(top_srcdir)/src/external/samba.m4 \
+ $(top_srcdir)/src/external/sasl.m4 \
+ $(top_srcdir)/src/external/libnfsidmap.m4 \
+ $(top_srcdir)/src/external/cwrap.m4 \
+ $(top_srcdir)/src/external/libresolv.m4 \
+ $(top_srcdir)/src/external/intgcheck.m4 \
+ $(top_srcdir)/src/external/systemtap.m4 \
+ $(top_srcdir)/src/external/service.m4 \
+ $(top_srcdir)/src/external/test_ca.m4 \
+ $(top_srcdir)/src/external/ax_valgrind_check.m4 \
+ $(top_srcdir)/src/external/libjansson.m4 \
+ $(top_srcdir)/src/external/libcurl.m4 \
+ $(top_srcdir)/src/external/libjose.m4 \
+ $(top_srcdir)/src/external/libuuid.m4 \
+ $(top_srcdir)/src/external/libunistring.m4 \
+ $(top_srcdir)/src/external/libpasskey.m4 \
+ $(top_srcdir)/src/external/p11-kit.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_sss_analyze_python_SCRIPTS) \
+ $(dist_modules_DATA) $(dist_pkgpython_DATA) $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(sss_analyze_pythondir)" \
+ "$(DESTDIR)$(modulesdir)" "$(DESTDIR)$(pkgpythondir)"
+SCRIPTS = $(dist_sss_analyze_python_SCRIPTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+DATA = $(dist_modules_DATA) $(dist_pkgpython_DATA)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build/mkinstalldirs
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CARES_CFLAGS = @CARES_CFLAGS@
+CARES_LIBS = @CARES_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CHECK_CFLAGS = @CHECK_CFLAGS@
+CHECK_LIBS = @CHECK_LIBS@
+CMOCKA_CFLAGS = @CMOCKA_CFLAGS@
+CMOCKA_LIBS = @CMOCKA_LIBS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CRYPTO_CFLAGS = @CRYPTO_CFLAGS@
+CRYPTO_LIBS = @CRYPTO_LIBS@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CURL_CFLAGS = @CURL_CFLAGS@
+CURL_LIBS = @CURL_LIBS@
+CYGPATH_W = @CYGPATH_W@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_LIBS = @DBUS_LIBS@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHASH_CFLAGS = @DHASH_CFLAGS@
+DHASH_LIBS = @DHASH_LIBS@
+DLLTOOL = @DLLTOOL@
+DOCBOOK_XSLT = @DOCBOOK_XSLT@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DTRACE = @DTRACE@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_VALGRIND_drd = @ENABLE_VALGRIND_drd@
+ENABLE_VALGRIND_helgrind = @ENABLE_VALGRIND_helgrind@
+ENABLE_VALGRIND_memcheck = @ENABLE_VALGRIND_memcheck@
+ENABLE_VALGRIND_sgcheck = @ENABLE_VALGRIND_sgcheck@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FAKETIME = @FAKETIME@
+FGREP = @FGREP@
+FIDO2_CFLAGS = @FIDO2_CFLAGS@
+FIDO2_LIBS = @FIDO2_LIBS@
+FILECMD = @FILECMD@
+GDM_PAM_EXTENSIONS_CFLAGS = @GDM_PAM_EXTENSIONS_CFLAGS@
+GDM_PAM_EXTENSIONS_LIBS = @GDM_PAM_EXTENSIONS_LIBS@
+GMSGFMT = @GMSGFMT@
+GPO_DEFAULT = @GPO_DEFAULT@
+GREP = @GREP@
+GSSAPI_KRB5_CFLAGS = @GSSAPI_KRB5_CFLAGS@
+GSSAPI_KRB5_LIBS = @GSSAPI_KRB5_LIBS@
+HAVE_FAKEROOT = @HAVE_FAKEROOT@
+HAVE_LDAPMODIFY = @HAVE_LDAPMODIFY@
+HAVE_MANPAGES = @HAVE_MANPAGES@
+HAVE_NSS_WRAPPER = @HAVE_NSS_WRAPPER@
+HAVE_PAM_WRAPPER = @HAVE_PAM_WRAPPER@
+HAVE_PYTHON2 = @HAVE_PYTHON2@
+HAVE_PYTHON2_BINDINGS = @HAVE_PYTHON2_BINDINGS@
+HAVE_PYTHON3 = @HAVE_PYTHON3@
+HAVE_PYTHON3_BINDINGS = @HAVE_PYTHON3_BINDINGS@
+HAVE_SELINUX = @HAVE_SELINUX@
+HAVE_SEMANAGE = @HAVE_SEMANAGE@
+HAVE_UID_WRAPPER = @HAVE_UID_WRAPPER@
+INI_CONFIG_CFLAGS = @INI_CONFIG_CFLAGS@
+INI_CONFIG_LIBS = @INI_CONFIG_LIBS@
+INI_CONFIG_V0_CFLAGS = @INI_CONFIG_V0_CFLAGS@
+INI_CONFIG_V0_LIBS = @INI_CONFIG_V0_LIBS@
+INI_CONFIG_V1_1_CFLAGS = @INI_CONFIG_V1_1_CFLAGS@
+INI_CONFIG_V1_1_LIBS = @INI_CONFIG_V1_1_LIBS@
+INI_CONFIG_V1_3_CFLAGS = @INI_CONFIG_V1_3_CFLAGS@
+INI_CONFIG_V1_3_LIBS = @INI_CONFIG_V1_3_LIBS@
+INI_CONFIG_V1_CFLAGS = @INI_CONFIG_V1_CFLAGS@
+INI_CONFIG_V1_LIBS = @INI_CONFIG_V1_LIBS@
+INOTIFY_LIBS = @INOTIFY_LIBS@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+JANSSON_CFLAGS = @JANSSON_CFLAGS@
+JANSSON_LIBS = @JANSSON_LIBS@
+JOSE_CFLAGS = @JOSE_CFLAGS@
+JOSE_LIBS = @JOSE_LIBS@
+JOURNALD_CFLAGS = @JOURNALD_CFLAGS@
+JOURNALD_LIBS = @JOURNALD_LIBS@
+KEYUTILS_LIBS = @KEYUTILS_LIBS@
+KRAD_LIBS = @KRAD_LIBS@
+KRB5_CFLAGS = @KRB5_CFLAGS@
+KRB5_CONFIG = @KRB5_CONFIG@
+KRB5_LIBS = @KRB5_LIBS@
+LD = @LD@
+LDB_CFLAGS = @LDB_CFLAGS@
+LDB_LIBS = @LDB_LIBS@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBADD_TIMER = @LIBADD_TIMER@
+LIBCLOCK_GETTIME = @LIBCLOCK_GETTIME@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBNL1_CFLAGS = @LIBNL1_CFLAGS@
+LIBNL1_LIBS = @LIBNL1_LIBS@
+LIBNL3_CFLAGS = @LIBNL3_CFLAGS@
+LIBNL3_LIBS = @LIBNL3_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NDR_KRB5PAC_CFLAGS = @NDR_KRB5PAC_CFLAGS@
+NDR_KRB5PAC_LIBS = @NDR_KRB5PAC_LIBS@
+NDR_NBT_CFLAGS = @NDR_NBT_CFLAGS@
+NDR_NBT_LIBS = @NDR_NBT_LIBS@
+NFSIDMAP_CFLAGS = @NFSIDMAP_CFLAGS@
+NFSIDMAP_LIBS = @NFSIDMAP_LIBS@
+NFSIDMAP_OBJ = @NFSIDMAP_OBJ@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NSUPDATE = @NSUPDATE@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OPENLDAP_CFLAGS = @OPENLDAP_CFLAGS@
+OPENLDAP_LIBS = @OPENLDAP_LIBS@
+OPENSSL = @OPENSSL@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+P11TOOL = @P11TOOL@
+P11_KIT_CFLAGS = @P11_KIT_CFLAGS@
+P11_KIT_LIBS = @P11_KIT_LIBS@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PAM_LIBS = @PAM_LIBS@
+PAM_MISC_LIBS = @PAM_MISC_LIBS@
+PASSKEY_CFLAGS = @PASSKEY_CFLAGS@
+PASSKEY_LIBS = @PASSKEY_LIBS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PCRE_CFLAGS = @PCRE_CFLAGS@
+PCRE_LIBS = @PCRE_LIBS@
+PKG_CONFIG = @PKG_CONFIG@
+PO4A = @PO4A@
+POPT_CFLAGS = @POPT_CFLAGS@
+POPT_LIBS = @POPT_LIBS@
+POSUB = @POSUB@
+PRERELEASE_VERSION = @PRERELEASE_VERSION@
+PYTHON = @PYTHON@
+PYTHON2 = @PYTHON2@
+PYTHON2_CFLAGS = @PYTHON2_CFLAGS@
+PYTHON2_EXEC_PREFIX = @PYTHON2_EXEC_PREFIX@
+PYTHON2_INCLUDES = @PYTHON2_INCLUDES@
+PYTHON2_LIBS = @PYTHON2_LIBS@
+PYTHON2_PREFIX = @PYTHON2_PREFIX@
+PYTHON2_VERSION = @PYTHON2_VERSION@
+PYTHON3 = @PYTHON3@
+PYTHON3_CFLAGS = @PYTHON3_CFLAGS@
+PYTHON3_EXEC_PREFIX = @PYTHON3_EXEC_PREFIX@
+PYTHON3_INCLUDES = @PYTHON3_INCLUDES@
+PYTHON3_LIBS = @PYTHON3_LIBS@
+PYTHON3_PREFIX = @PYTHON3_PREFIX@
+PYTHON3_VERSION = @PYTHON3_VERSION@
+PYTHON_CONFIG = @PYTHON_CONFIG@
+PYTHON_EXEC = @PYTHON_EXEC@
+PYTHON_EXEC_INTG = @PYTHON_EXEC_INTG@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+RESOLV_CFLAGS = @RESOLV_CFLAGS@
+RESOLV_LIBS = @RESOLV_LIBS@
+SAMBA_UTIL_CFLAGS = @SAMBA_UTIL_CFLAGS@
+SAMBA_UTIL_LIBS = @SAMBA_UTIL_LIBS@
+SASL_CFLAGS = @SASL_CFLAGS@
+SASL_LIBS = @SASL_LIBS@
+SED = @SED@
+SELINUX_LIBS = @SELINUX_LIBS@
+SEMANAGE_LIBS = @SEMANAGE_LIBS@
+SERVICE = @SERVICE@
+SET_MAKE = @SET_MAKE@
+SGML_CATALOG_FILES = @SGML_CATALOG_FILES@
+SHELL = @SHELL@
+SLAPD = @SLAPD@
+SMBCLIENT_CFLAGS = @SMBCLIENT_CFLAGS@
+SMBCLIENT_LIBS = @SMBCLIENT_LIBS@
+SOFTHSM2_PATH = @SOFTHSM2_PATH@
+SOFTHSM2_UTIL = @SOFTHSM2_UTIL@
+SSH_KEYGEN = @SSH_KEYGEN@
+SSL_CFLAGS = @SSL_CFLAGS@
+SSL_LIBS = @SSL_LIBS@
+SSSD_USER = @SSSD_USER@
+STRIP = @STRIP@
+SYSTEMD_DAEMON_CFLAGS = @SYSTEMD_DAEMON_CFLAGS@
+SYSTEMD_DAEMON_LIBS = @SYSTEMD_DAEMON_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+TALLOC_CFLAGS = @TALLOC_CFLAGS@
+TALLOC_LIBS = @TALLOC_LIBS@
+TDB_CFLAGS = @TDB_CFLAGS@
+TDB_LIBS = @TDB_LIBS@
+TEST_DIR = @TEST_DIR@
+TEVENT_CFLAGS = @TEVENT_CFLAGS@
+TEVENT_LIBS = @TEVENT_LIBS@
+UNICODE_LIBS = @UNICODE_LIBS@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND = @VALGRIND@
+VALGRIND_ENABLED = @VALGRIND_ENABLED@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XMLLINT = @XMLLINT@
+XSLTPROC = @XSLTPROC@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+appmodpath = @appmodpath@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+cifspluginpath = @cifspluginpath@
+config_def_ccache_dir = @config_def_ccache_dir@
+config_def_ccname_template = @config_def_ccname_template@
+datadir = @datadir@
+datarootdir = @datarootdir@
+dbpath = @dbpath@
+docdir = @docdir@
+dvidir = @dvidir@
+environment_file = @environment_file@
+exec_prefix = @exec_prefix@
+gpocachepath = @gpocachepath@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+initdir = @initdir@
+install_sh = @install_sh@
+krb5authdatapluginpath = @krb5authdatapluginpath@
+krb5pluginpath = @krb5pluginpath@
+krb5rcachedir = @krb5rcachedir@
+ldblibdir = @ldblibdir@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+logpath = @logpath@
+mandir = @mandir@
+mcpath = @mcpath@
+mkdir_p = @mkdir_p@
+nfsidmaplibdir = @nfsidmaplibdir@
+nfslibpath = @nfslibpath@
+nsslibdir = @nsslibdir@
+oldincludedir = @oldincludedir@
+pammoddir = @pammoddir@
+pdfdir = @pdfdir@
+pidpath = @pidpath@
+pipepath = @pipepath@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = $(python3dir)/sssd
+pluginpath = @pluginpath@
+polkitdir = @polkitdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pubconfpath = @pubconfpath@
+py2execdir = @py2execdir@
+py3execdir = @py3execdir@
+pyexecdir = @pyexecdir@
+python2dir = @python2dir@
+python3dir = @python3dir@
+pythondir = @pythondir@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+secdbpath = @secdbpath@
+session_recording_shell = @session_recording_shell@
+sharedbuilddir = @sharedbuilddir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subidlibpath = @subidlibpath@
+sudolibpath = @sudolibpath@
+sysconfdir = @sysconfdir@
+systemdconfdir = @systemdconfdir@
+systemdunitdir = @systemdunitdir@
+tapset_dir = @tapset_dir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+valgrind_enabled_tools = @valgrind_enabled_tools@
+valgrind_tools = @valgrind_tools@
+winbindpluginpath = @winbindpluginpath@
+sss_analyze_pythondir = $(libexecdir)/sssd
+dist_sss_analyze_python_SCRIPTS = \
+ sss_analyze \
+ $(NULL)
+
+dist_pkgpython_DATA = \
+ __init__.py \
+ source_files.py \
+ source_journald.py \
+ source_reader.py \
+ parser.py \
+ sss_analyze.py \
+ $(NULL)
+
+modulesdir = $(pkgpythondir)/modules
+dist_modules_DATA = \
+ modules/__init__.py \
+ modules/request.py \
+ $(NULL)
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/tools/analyzer/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign src/tools/analyzer/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-dist_sss_analyze_pythonSCRIPTS: $(dist_sss_analyze_python_SCRIPTS)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_sss_analyze_python_SCRIPTS)'; test -n "$(sss_analyze_pythondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sss_analyze_pythondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sss_analyze_pythondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n' \
+ -e 'h;s|.*|.|' \
+ -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+ if (++n[d] == $(am__install_max)) { \
+ print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+ else { print "f", d "/" $$4, $$1 } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sss_analyze_pythondir)$$dir'"; \
+ $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sss_analyze_pythondir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-dist_sss_analyze_pythonSCRIPTS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_sss_analyze_python_SCRIPTS)'; test -n "$(sss_analyze_pythondir)" || exit 0; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 's,.*/,,;$(transform)'`; \
+ dir='$(DESTDIR)$(sss_analyze_pythondir)'; $(am__uninstall_files_from_dir)
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-dist_modulesDATA: $(dist_modules_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(modulesdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(modulesdir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(modulesdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(modulesdir)" || exit $$?; \
+ done
+
+uninstall-dist_modulesDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_modules_DATA)'; test -n "$(modulesdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(modulesdir)'; $(am__uninstall_files_from_dir)
+install-dist_pkgpythonDATA: $(dist_pkgpython_DATA)
+ @$(NORMAL_INSTALL)
+ @list='$(dist_pkgpython_DATA)'; test -n "$(pkgpythondir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(pkgpythondir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(pkgpythondir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgpythondir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgpythondir)" || exit $$?; \
+ done
+
+uninstall-dist_pkgpythonDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_pkgpython_DATA)'; test -n "$(pkgpythondir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(pkgpythondir)'; $(am__uninstall_files_from_dir)
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(SCRIPTS) $(DATA)
+installdirs:
+ for dir in "$(DESTDIR)$(sss_analyze_pythondir)" "$(DESTDIR)$(modulesdir)" "$(DESTDIR)$(pkgpythondir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_modulesDATA install-dist_pkgpythonDATA \
+ install-dist_sss_analyze_pythonSCRIPTS
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-dist_modulesDATA uninstall-dist_pkgpythonDATA \
+ uninstall-dist_sss_analyze_pythonSCRIPTS
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-dist_modulesDATA install-dist_pkgpythonDATA \
+ install-dist_sss_analyze_pythonSCRIPTS install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am uninstall-dist_modulesDATA \
+ uninstall-dist_pkgpythonDATA \
+ uninstall-dist_sss_analyze_pythonSCRIPTS
+
+.PRECIOUS: Makefile
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/tools/analyzer/__init__.py b/src/tools/analyzer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/analyzer/__init__.py
diff --git a/src/tools/analyzer/modules/__init__.py b/src/tools/analyzer/modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/tools/analyzer/modules/__init__.py
diff --git a/src/tools/analyzer/modules/request.py b/src/tools/analyzer/modules/request.py
new file mode 100644
index 0000000..d661ddd
--- /dev/null
+++ b/src/tools/analyzer/modules/request.py
@@ -0,0 +1,320 @@
+import re
+import logging
+
+from sssd.parser import SubparsersAction
+from sssd.parser import Option
+
+logger = logging.getLogger()
+
+
+class RequestAnalyzer:
+ """
+ A request analyzer module, handles request tracking logic
+ and analysis. Parses input generated from a source Reader.
+ """
+ module_parser = None
+ consumed_logs = []
+ list_opts = [
+ Option('--verbose', 'Verbose output', bool, '-v'),
+ Option('--pam', 'Filter only PAM requests', bool),
+ ]
+
+ show_opts = [
+ Option('cid', 'Track request with this ID', int),
+ Option('--child', 'Include child process logs', bool),
+ Option('--merge', 'Merge logs together sorted by timestamp', bool),
+ Option('--pam', 'Track only PAM requests', bool),
+ ]
+
+ def print_module_help(self, args):
+ """
+ Print the module parser help output
+
+ Args:
+ args (Namespace): argparse parsed arguments
+ """
+ self.module_parser.print_help()
+
+ def setup_args(self, parser_grp, cli):
+ """
+ Setup module parser, subcommands, and options
+
+ Args:
+ parser_grp (argparse.Action): Parser group to nest
+ module and subcommands under
+ """
+ desc = "Analyze request tracking module"
+ self.module_parser = parser_grp.add_parser('request',
+ description=desc,
+ help='Request tracking')
+
+ subparser = self.module_parser.add_subparsers(title=None,
+ dest='subparser',
+ action=SubparsersAction,
+ metavar='COMMANDS')
+
+ subcmd_grp = subparser.add_parser_group('Operation Modes')
+ cli.add_subcommand(subcmd_grp, 'list', 'List recent requests',
+ self.list_requests, self.list_opts)
+ cli.add_subcommand(subcmd_grp, 'show', 'Track individual request ID',
+ self.track_request, self.show_opts)
+
+ self.module_parser.set_defaults(func=self.print_module_help)
+
+ return self.module_parser
+
+ def load(self, args):
+ """
+ Load the appropriate source reader.
+
+ Args:
+ args (Namespace): argparse parsed arguments
+
+ Returns:
+ Instantiated source object
+ """
+ if args.source == "journald":
+ from sssd.source_journald import Journald
+ source = Journald()
+ else:
+ from sssd.source_files import Files
+ source = Files(args.logdir)
+ return source
+
+ def matched_line(self, source, patterns):
+ """
+ Yield lines which match any number of patterns (OR) in
+ provided patterns list.
+
+ Args:
+ source (Reader): source Reader object
+ Yields:
+ lines matching the provided pattern(s)
+ """
+ for line in source:
+ for pattern in patterns:
+ re_obj = re.compile(pattern)
+ if re_obj.search(line):
+ if line.startswith(' * '):
+ continue
+ yield line
+
+ def get_linked_ids(self, source, pattern, regex):
+ """
+ Retrieve list of associated REQ_TRACE ids. Filter
+ only source lines by pattern, then parse out the
+ linked id with the provided regex.
+
+ Args:
+ source (Reader): source Reader object
+ pattern (list of str): regex pattern(s) used for finding
+ linked ids
+ regex (str): regular expression used to extract linked id
+
+ Returns:
+ List of linked ids discovered
+ """
+ linked_ids = []
+ for match in self.matched_line(source, pattern):
+ id_re = re.compile(regex)
+ match = id_re.search(match)
+ if match:
+ found = match.group(0)
+ linked_ids.append(found)
+ return linked_ids
+
+ def consume_line(self, line, source, consume):
+ """
+ Print or consume a line, if merge cli option is provided then consume
+ boolean is set to True
+
+ Args:
+ line (str): line to process
+ source (Reader): source Reader object
+ consume (bool): If True, line is added to consume_logs
+ list, otherwise print line
+
+ Returns:
+ True if line was processed, otherwise False
+ """
+ found_results = True
+ if consume:
+ self.consumed_logs.append(line.rstrip(line[-1]))
+ else:
+ # files source includes newline
+ if type(source).__name__ == 'Files':
+ print(line, end='')
+ else:
+ print(line)
+ return found_results
+
+ def print_formatted_verbose(self, source):
+ """
+ Parse log file and print formatted verbose list_requests output
+
+ Args:
+ source (Reader): source Reader object
+ """
+ data = {}
+ # collect cid log lines from single run through of parsing the log
+ # into dictionary # (cid, ts) -> logline_output
+ for line in source:
+ if "CID#" not in line:
+ continue
+
+ # parse CID and ts from line, key is a tuple of (cid,ts)
+ fields = line.split("[")
+ # timestamp to the minute, cut off seconds, ms
+ ts = fields[0][:17]
+ result = re.search('CID#[0-9]*', fields[3])
+ cid = result.group(0)
+
+ # if mapping exists, append line to output. Otherwise create new mapping
+ if (cid, ts) in data.keys():
+ data[(cid, ts)] += line
+ else:
+ data[(cid, ts)] = line
+
+ # pretty print the data
+ for k, v in data.items():
+ cr_done = []
+ id_done = []
+ for cidline in v.splitlines():
+ plugin = ""
+ name = ""
+ id = ""
+
+ # CR number
+ fields = cidline.split("[")
+ cr_field = fields[3][7:]
+ cr = cr_field.split(":")[0][4:]
+ # Client connected, top-level info line
+ if re.search(r'\[cmd', cidline):
+ self.print_formatted(cidline)
+ # CR Plugin name
+ if re.search("cache_req_send", cidline):
+ plugin = cidline.split('\'')[1]
+ id_done.clear()
+ # Extract CR number
+ fields = cidline.split("[")
+ cr_field = fields[3][7:]
+ cr = cr_field.split(":")[0][4:]
+ # CR Input name
+ elif re.search("cache_req_process_input", cidline):
+ name = cidline.rsplit('[')[-1]
+ # CR Input id
+ elif re.search("cache_req_search_send", cidline):
+ id = cidline.rsplit()[-1]
+
+ if plugin:
+ print(" - " + plugin)
+ if name:
+ # Avoid duplicate output with the same CR #
+ if cr not in cr_done:
+ print(" - " + name[:-1])
+ cr_done.append(cr)
+ if (id and ("UID" in cidline or "GID" in cidline)):
+ if id not in id_done and bool(re.search(r'\d', id)):
+ print(" - " + id)
+ id_done.append(id)
+
+ def print_formatted(self, line):
+ """
+ Parse line and print formatted list_requests output
+
+ Args:
+ line (str): line to parse
+ Returns:
+ Client ID from printed line, 0 otherwise
+ """
+ # exclude backtrace logs
+ if line.startswith(' * '):
+ return 0
+ if "refreshed" in line:
+ return 0
+ ts = line.split(")")[0]
+ ts = ts[1:]
+ fields = line.split("[")
+ cid = fields[3][4:-9]
+ cmd = fields[4][4:-1]
+ uid = fields[5][4:-1]
+ if not uid.isnumeric():
+ uid = fields[6][4:-1]
+ print(f'{ts}: [uid {uid}] CID #{cid}: {cmd}')
+ return cid
+
+ def list_requests(self, args):
+ """
+ List component (default: NSS) responder requests
+
+ Args:
+ args (Namespace): populated argparse namespace
+ """
+ source = self.load(args)
+ component = source.Component.NSS
+ resp = "nss"
+ # Log messages matching the following regex patterns contain
+ # the useful info we need to produce list output
+ patterns = [r'\[cmd']
+ if args.pam:
+ component = source.Component.PAM
+ resp = "pam"
+
+ logger.info(f"******** Listing {resp} client requests ********")
+ source.set_component(component, False)
+
+ if args.verbose:
+ self.print_formatted_verbose(source)
+ else:
+ for line in self.matched_line(source, patterns):
+ if type(source).__name__ == 'Journald':
+ print(line)
+ else:
+ self.print_formatted(line)
+
+ def track_request(self, args):
+ """
+ Print Logs pertaining to individual SSSD client request
+
+ Args:
+ args (Namespace): populated argparse namespace
+ """
+ source = self.load(args)
+ cid = args.cid
+ resp_results = False
+ be_results = False
+ component = source.Component.NSS
+ resp = "nss"
+ pattern = [rf"\[CID#{cid}\]"]
+
+ if args.pam:
+ component = source.Component.PAM
+ resp = "pam"
+
+ logger.info(f"******** Checking {resp} responder for Client ID"
+ f" {cid} *******")
+ source.set_component(component, args.child)
+ for match in self.matched_line(source, pattern):
+ resp_results = self.consume_line(match, source, args.merge)
+
+ logger.info(f"********* Checking Backend for Client ID {cid} ********")
+ pattern = [rf'REQ_TRACE.*\[sssd.{resp} CID #{cid}\]']
+ source.set_component(source.Component.BE, args.child)
+
+ be_id_regex = r'\[RID#[0-9]+\]'
+ be_ids = self.get_linked_ids(source, pattern, be_id_regex)
+
+ pattern.clear()
+ [pattern.append(f'\\{id}') for id in be_ids]
+
+ for match in self.matched_line(source, pattern):
+ be_results = self.consume_line(match, source, args.merge)
+
+ if args.merge:
+ # sort by date/timestamp
+ sorted_list = sorted(self.consumed_logs,
+ key=lambda s: s.split(')')[0])
+ for entry in sorted_list:
+ print(entry)
+ if not resp_results and not be_results:
+ logger.warn(f"ID {cid} not found in logs!")
diff --git a/src/tools/analyzer/parser.py b/src/tools/analyzer/parser.py
new file mode 100644
index 0000000..742694e
--- /dev/null
+++ b/src/tools/analyzer/parser.py
@@ -0,0 +1,60 @@
+import argparse
+
+
+# Based on patch from https://bugs.python.org/issue9341
+class SubparsersAction(argparse._SubParsersAction):
+ """
+ Provide a subparser action that can create subparsers with ability of
+ grouping arguments.
+
+ It is based on the patch from:
+
+ - https://bugs.python.org/issue9341
+ """
+
+ class _PseudoGroup(argparse.Action):
+ def __init__(self, container, title):
+ super().__init__(option_strings=[], dest=title)
+ self.container = container
+ self._choices_actions = []
+
+ def add_parser(self, name, **kwargs):
+ # add the parser to the main Action, but move the pseudo action
+ # in the group's own list
+ parser = self.container.add_parser(name, **kwargs)
+ choice_action = self.container._choices_actions.pop()
+ self._choices_actions.append(choice_action)
+ return parser
+
+ def _get_subactions(self):
+ return self._choices_actions
+
+ def add_parser_group(self, title):
+ # the formatter can handle recursive subgroups
+ grp = SubparsersAction._PseudoGroup(self, title)
+ self._choices_actions.append(grp)
+ return grp
+
+ def add_parser_group(self, title):
+ """
+ Add new parser group.
+
+ :param title: Title.
+ :type title: str
+ :return: Parser group that can have additional parsers attached.
+ :rtype: ``argparse.Action`` extended with ``add_parser`` method
+ """
+ grp = self._PseudoGroup(self, title)
+ self._choices_actions.append(grp)
+ return grp
+
+
+class Option:
+ """
+ Group option attributes for command/subcommand options
+ """
+ def __init__(self, name, help_msg, opt_type, short_opt=None):
+ self.name = name
+ self.short_opt = short_opt
+ self.help_msg = help_msg
+ self.opt_type = opt_type
diff --git a/src/tools/analyzer/source_files.py b/src/tools/analyzer/source_files.py
new file mode 100644
index 0000000..0cadf99
--- /dev/null
+++ b/src/tools/analyzer/source_files.py
@@ -0,0 +1,76 @@
+import glob
+import logging
+
+from sssd.source_reader import Reader
+
+logger = logging.getLogger()
+
+
+class Files(Reader):
+ """
+ A class used to represent a Log Files Reader
+
+ Args:
+ path -- the path where SSSD logs are to
+ be read (default /var/log/sssd/)
+ """
+
+ def __init__(self, path):
+ super().__init__()
+ self.log_files = []
+ self.path = self.resolve_path(path)
+ self.domains = self.get_domain_logfiles()
+
+ def __iter__(self):
+ """
+ Yields:
+ str: The next line in the log file
+ """
+ for files in self.log_files:
+ try:
+ with open(files) as file:
+ for line in file:
+ yield line
+ except FileNotFoundError as err:
+ logger.warning("Could not find domain log file, skipping")
+ logger.warning(err)
+ continue
+
+ def resolve_path(self, path):
+ if path.endswith("/"):
+ return path
+ else:
+ return path + "/"
+
+ def get_domain_logfiles(self, child=False):
+ """ Retrieve list of SSSD log files, exclude rotated (.gz) files """
+ domain_files = []
+ exclude_list = ["ifp", "nss", "pam", "sudo", "autofs",
+ "ssh", "pac", "kcm", ".gz"]
+ if child:
+ file_list = glob.glob(self.path + "*.log")
+ else:
+ file_list = glob.glob(self.path + "sssd_*")
+ for file in file_list:
+ if not any(s in file for s in exclude_list):
+ domain_files.append(file)
+
+ return domain_files
+
+ def set_component(self, component, child):
+ """
+ Switch the reader to interact with a certain SSSD component
+ NSS, PAM, BE
+ """
+ self.log_files = []
+ if component == self.Component.NSS:
+ self.log_files.append(self.path + "sssd_nss.log")
+ elif component == self.Component.PAM:
+ self.log_files.append(self.path + "sssd_pam.log")
+ elif component == self.Component.BE:
+ domains = self.get_domain_logfiles(child)
+ if not domains:
+ raise IOError
+ # error: No domains found?
+ for dom in domains:
+ self.log_files.append(dom)
diff --git a/src/tools/analyzer/source_journald.py b/src/tools/analyzer/source_journald.py
new file mode 100644
index 0000000..22ec2ed
--- /dev/null
+++ b/src/tools/analyzer/source_journald.py
@@ -0,0 +1,46 @@
+from systemd import journal
+
+from sssd.source_reader import Reader
+
+_EXE_PREFIX = "/usr/libexec/sssd/"
+_NSS_MATCH = _EXE_PREFIX + "sssd_nss"
+_PAM_MATCH = _EXE_PREFIX + "sssd_pam"
+_BE_MATCH = _EXE_PREFIX + "sssd_be"
+
+
+class Journald(Reader):
+ """
+ A class used to represent a Journald Reader
+ """
+ def __init__(self):
+ super().__init__()
+ self.reader = journal.Reader()
+ self.reader.this_boot()
+ self.reader.seek_head()
+
+ def __iter__(self):
+ """
+ Yields:
+ str: The next journal entry message, with timestamp if found
+ """
+ self.reader.seek_head()
+ for entry in self.reader:
+ ts = entry['__REALTIME_TIMESTAMP']
+ msg = entry['MESSAGE']
+ if ts:
+ yield f'{ts}: {msg}'
+ else:
+ yield msg
+
+ def set_component(self, component, child):
+ """
+ Switch the reader to interact with a certain SSSD component
+ NSS, PAM, BE
+ """
+ self.reader.flush_matches()
+ if component == self.Component.NSS:
+ self.reader.add_match(_EXE=_NSS_MATCH)
+ elif component == self.Component.PAM:
+ self.reader.add_match(_EXE=_PAM_MATCH)
+ elif component == self.Component.BE:
+ self.reader.add_match(_EXE=_BE_MATCH)
diff --git a/src/tools/analyzer/source_reader.py b/src/tools/analyzer/source_reader.py
new file mode 100644
index 0000000..10d2d37
--- /dev/null
+++ b/src/tools/analyzer/source_reader.py
@@ -0,0 +1,27 @@
+from enum import Enum
+
+from abc import ABC, abstractmethod
+
+
+class Reader(ABC):
+ """
+ An abstract class used to represent a source Reader
+ """
+
+ class Component(Enum):
+ """ SSSD component to enable for reading """
+ NSS = 1 # NSS Responder
+ PAM = 2 # PAM Responder
+ BE = 3 # Backend
+
+ @abstractmethod
+ def __init__(self):
+ pass
+
+ @abstractmethod
+ def __iter__(self):
+ pass
+
+ @abstractmethod
+ def set_component(self):
+ pass
diff --git a/src/tools/analyzer/sss_analyze b/src/tools/analyzer/sss_analyze
new file mode 100755
index 0000000..3f1beaf
--- /dev/null
+++ b/src/tools/analyzer/sss_analyze
@@ -0,0 +1,5 @@
+#!/usr/bin/env python
+
+from sssd import sss_analyze
+
+sss_analyze.run()
diff --git a/src/tools/analyzer/sss_analyze.py b/src/tools/analyzer/sss_analyze.py
new file mode 100644
index 0000000..18b998f
--- /dev/null
+++ b/src/tools/analyzer/sss_analyze.py
@@ -0,0 +1,104 @@
+import argparse
+
+from sssd.modules import request
+from sssd.parser import SubparsersAction
+
+
+class Analyzer:
+ def add_subcommand(self, subcmd_grp, name, help_msg, func, opts):
+ """
+ Add subcommand to existing subcommand group
+
+ Args:
+ name(str): Subcommand name
+ help_msg(str): Help message for subcommand
+ func(function): Function to call on execution
+ opts(list of Object()): List of Option objects to add to subcommand
+ """
+ # Create parser
+ req_parser = subcmd_grp.add_parser(name, help=help_msg)
+
+ # Add subcommand options
+ self._add_subcommand_options(req_parser, opts)
+
+ # Execute func() when argument is called
+ req_parser.set_defaults(func=func)
+
+ def _add_subcommand_options(self, parser, opts):
+ """
+ Add subcommand options to subcommand parser
+
+ Args:
+ parser(str): Subcommand group parser
+ opts(list of Object()): List of Option objects to add to subcommand
+ """
+ for opt in opts:
+ if opt.opt_type is bool:
+ if opt.short_opt is None:
+ parser.add_argument(opt.name, help=opt.help_msg,
+ action='store_true')
+ else:
+ parser.add_argument(opt.name, opt.short_opt,
+ help=opt.help_msg, action='store_true')
+ if opt.opt_type is int:
+ parser.add_argument(opt.name, help=opt.help_msg,
+ type=int)
+
+ def load_modules(self, parser, parser_grp):
+ """
+ Initialize analyzer modules from modules/*
+
+ Args:
+ parser (ArgumentParser): Base parser object
+ parser_grp (argparse.Action): Parser group that can have
+ additional parsers attached.
+ """
+ # Currently only the 'request' module exists
+ req = request.RequestAnalyzer()
+ cli = Analyzer()
+
+ req.setup_args(parser_grp, cli)
+
+ def setup_args(self):
+ """
+ Top-level argument setup function.
+ Setup analyzer argument parsers and subcommand parser/options.
+
+ Returns:
+ parser (ArgumentParser): Base parser object
+ """
+ # top level parser
+ formatter = argparse.RawTextHelpFormatter
+ parser = argparse.ArgumentParser(description='Analyzer tool to assist '
+ 'with SSSD log parsing',
+ formatter_class=formatter)
+ parser.add_argument('--source', default='files', choices=['files',
+ 'journald'])
+ parser.add_argument('--logdir', default='/var/log/sssd/',
+ help='SSSD Log directory to parse log files from')
+
+ # Modules parser group
+ subparser = parser.add_subparsers(title=None,
+ action=SubparsersAction,
+ metavar='COMMANDS')
+ parser_grp = subparser.add_parser_group('Modules')
+
+ # Load modules, subcommands are added in module.setup_args()
+ self.load_modules(parser, parser_grp)
+
+ return parser
+
+ def main(self):
+ parser = self.setup_args()
+ args = parser.parse_args()
+
+ if not hasattr(args, 'func'):
+ parser.print_help()
+ return 0
+
+ args.func(args)
+
+
+def run():
+ analyzer = Analyzer()
+ analyzer.main()
diff --git a/src/tools/common/sss_colondb.c b/src/tools/common/sss_colondb.c
new file mode 100644
index 0000000..41e6c3a
--- /dev/null
+++ b/src/tools/common/sss_colondb.c
@@ -0,0 +1,317 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "tools/common/sss_colondb.h"
+
+#define IS_STD_FILE(db) ((db)->file == stdin || (db)->file == stdout)
+
+static char *read_field_as_string(char *line,
+ const char **_value)
+{
+ char *rest;
+ char *value;
+
+ if (line == NULL || *line == '\n' || *line == '\0') {
+ /* There is nothing else to read. */
+ rest = NULL;
+ value = NULL;
+ goto done;
+ }
+
+ if (*line == ':') {
+ /* Special case for empty value. */
+ *line = '\0';
+ rest = line + 1;
+ value = NULL;
+ goto done;
+ }
+
+ /* Value starts at current position. */
+ value = line;
+
+ /* Find next field delimiter. */
+ rest = strchr(line, ':');
+ if (rest == NULL) {
+ /* There is no more field. Remove \n from the end. */
+ rest = strchr(line, '\n');
+ if (rest != NULL) {
+ *rest = '\0';
+ rest = NULL;
+ }
+ goto done;
+ }
+
+ /* Remove it and step one character further. */
+ *rest = '\0';
+ rest++;
+
+done:
+ *_value = value;
+
+ return rest;
+}
+
+static char *read_field_as_uint32(char *line,
+ uint32_t *_value)
+{
+ const char *str;
+ char *rest;
+ errno_t ret;
+ char *endptr;
+
+ rest = read_field_as_string(line, &str);
+ if (str == NULL) {
+ *_value = 0;
+ return rest;
+ }
+
+ *_value = strtouint32(str, &endptr, 10);
+ if ((errno != 0) || *endptr || (str == endptr)) {
+ ret = errno ? errno : EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse number [%d]: %s\n",
+ ret, sss_strerror(ret));
+
+ *_value = 0;
+ }
+
+ return rest;
+}
+
+struct sss_colondb {
+ FILE *file;
+ enum sss_colondb_mode mode;
+};
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+ struct sss_colondb *db,
+ struct sss_colondb_read_field *table)
+{
+ int readchars;
+ size_t linelen = 0;
+ char *line = NULL;
+ char *tcline;
+ char *rest;
+ errno_t ret;
+ int i;
+
+ if (db->mode != SSS_COLONDB_READ) {
+ return ERR_INTERNAL;
+ }
+
+ readchars = getline(&line, &linelen, db->file);
+ if (readchars == -1) {
+ /* Nothing was read. */
+
+ free(line);
+ line = NULL;
+
+ if (errno != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read line [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOF;
+ }
+
+ /* Copy line to mem_ctx. */
+ tcline = talloc_strdup(mem_ctx, line);
+
+ free(line);
+ line = NULL;
+
+ if (tcline == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ return ENOMEM;
+ }
+
+ rest = tcline;
+ for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+ switch (table[i].type) {
+ case SSS_COLONDB_UINT32:
+ rest = read_field_as_uint32(rest, table[i].data.uint32);
+ break;
+ case SSS_COLONDB_STRING:
+ rest = read_field_as_string(rest, table[i].data.str);
+ break;
+ case SSS_COLONDB_SENTINEL:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (rest == NULL && table[i + 1].type != SSS_COLONDB_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Line contains less values than expected!\n");
+ ret = EINVAL;
+ goto done;
+ } else if (rest != NULL && table[i + 1].type == SSS_COLONDB_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Line contains more values than expected!\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(tcline);
+ }
+
+ return ret;
+}
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+ struct sss_colondb_write_field *table)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *line = NULL;
+ errno_t ret;
+ int i;
+
+ if (db->mode != SSS_COLONDB_WRITE) {
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ line = talloc_strdup(tmp_ctx, "");
+ if (line == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+ switch (table[i].type) {
+ case SSS_COLONDB_UINT32:
+ if (table[i].data.uint32 == 0) {
+ line = talloc_asprintf_append(line, ":");
+ } else {
+ line = talloc_asprintf_append(line, ":%u", table[i].data.uint32);
+ }
+ break;
+ case SSS_COLONDB_STRING:
+ if (table[i].data.str == NULL) {
+ line = talloc_asprintf_append(line, ":");
+ } else {
+ line = talloc_asprintf_append(line, ":%s", table[i].data.str);
+ }
+ break;
+ case SSS_COLONDB_SENTINEL:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (line == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* Remove starting : */
+ line++;
+
+ fprintf(db->file, "%s\n", line);
+ fflush(db->file);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int sss_colondb_close(void *pvt)
+{
+ struct sss_colondb *db = talloc_get_type(pvt, struct sss_colondb);
+
+ if (db->file == NULL || IS_STD_FILE(db)) {
+ return 0;
+ }
+
+ fclose(db->file);
+ db->file = NULL;
+
+ return 0;
+}
+
+static FILE *open_db(const char *filename, enum sss_colondb_mode mode)
+{
+ FILE *fp = NULL;
+ errno_t ret;
+
+ errno = 0;
+
+ switch (mode) {
+ case SSS_COLONDB_READ:
+ fp = filename == NULL ? stdin : fopen(filename, "r");
+ break;
+ case SSS_COLONDB_WRITE:
+ fp = filename == NULL ? stdout : fopen(filename, "w");
+ break;
+ }
+
+ if (fp == NULL && filename != NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s [%d]: %s\n",
+ filename, ret, sss_strerror(ret));
+ }
+
+ return fp;
+}
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+ enum sss_colondb_mode mode,
+ const char *filename)
+{
+ struct sss_colondb *db;
+
+ db = talloc_zero(mem_ctx, struct sss_colondb);
+ if (db == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return NULL;
+ }
+
+ db->file = open_db(filename, mode);
+ db->mode = mode;
+
+ if (db->file == NULL) {
+ talloc_free(db);
+ return NULL;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *)db, sss_colondb_close);
+
+ return db;
+}
diff --git a/src/tools/common/sss_colondb.h b/src/tools/common/sss_colondb.h
new file mode 100644
index 0000000..cb90400
--- /dev/null
+++ b/src/tools/common/sss_colondb.h
@@ -0,0 +1,96 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_COLONDB_H_
+#define _SSS_COLONDB_H_
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <talloc.h>
+
+struct sss_colondb;
+
+enum sss_colondb_mode {
+ SSS_COLONDB_READ,
+ SSS_COLONDB_WRITE
+};
+
+enum sss_colondb_type {
+ SSS_COLONDB_UINT32,
+ SSS_COLONDB_STRING,
+ SSS_COLONDB_SENTINEL
+};
+
+union sss_colondb_write_data {
+ uint32_t uint32;
+ const char *str;
+};
+
+union sss_colondb_read_data {
+ uint32_t *uint32;
+ const char **str;
+};
+
+struct sss_colondb_write_field {
+ enum sss_colondb_type type;
+ union sss_colondb_write_data data;
+};
+
+struct sss_colondb_read_field {
+ enum sss_colondb_type type;
+ union sss_colondb_read_data data;
+};
+
+/**
+ * Open colon DB and return connection.
+ * @param[in|out] mem_ctx Memory context. Internal sss_colondb_close() is set
+ * on destructor of this memory context.
+ * @param[in] mode Open mode of db: SSS_COLONDB_READ or SSS_COLONDB_WRITE.
+ * @param[in] filename Name of file.
+ * @return Pointer to structure holding DB connection, or NULL if fail.
+ */
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+ enum sss_colondb_mode mode,
+ const char *filename);
+
+/**
+ * Read line from colon DB.
+ * @param[in|out] mem_ctx Memory context.
+ * @param[in] db Pointer to structure holding DB connection.
+ * @param[in|out] table Array of expected structure of line. It is expected
+ * that last item has SSS_COLONDB_SENTINEL type.
+ * @return EOK if success, else error code.
+ */
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+ struct sss_colondb *db,
+ struct sss_colondb_read_field *table);
+
+/**
+ * Write line to colon DB.
+ * @param[in] db Pointer to structure holding DB connection.
+ * @param[in] table Array with data. It is expected that last item has
+ * SSS_COLONDB_SENTINEL type.
+ * @return EOK if success, else error code.
+ */
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+ struct sss_colondb_write_field *table);
+
+#endif /* _SSS_COLONDB_H_ */
diff --git a/src/tools/common/sss_process.c b/src/tools/common/sss_process.c
new file mode 100644
index 0000000..fc710a5
--- /dev/null
+++ b/src/tools/common/sss_process.c
@@ -0,0 +1,124 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+
+#include "util/util.h"
+#include "tools/common/sss_process.h"
+
+static pid_t parse_pid(const char *strpid)
+{
+ long value;
+ char *endptr;
+
+ errno = 0;
+ value = strtol(strpid, &endptr, 10);
+ if ((errno != 0) || (endptr == strpid)
+ || ((*endptr != '\0') && (*endptr != '\n'))) {
+ return 0;
+ }
+
+ return value;
+}
+
+static errno_t sss_pid(pid_t *out_pid)
+{
+ int ret;
+ size_t fsize;
+ FILE *pid_file;
+ char pid_str[MAX_PID_LENGTH] = {'\0'};
+
+ *out_pid = 0;
+
+ errno = 0;
+ pid_file = fopen(SSSD_PIDFILE, "r");
+ if (pid_file == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to open pid file \"%s\": %s\n",
+ SSSD_PIDFILE, strerror(ret));
+ goto done;
+ }
+
+ fsize = fread(pid_str, sizeof(char), MAX_PID_LENGTH * sizeof(char),
+ pid_file);
+ if (!feof(pid_file)) {
+ /* eof not reached */
+ ret = ferror(pid_file);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read from file \"%s\": %s\n",
+ SSSD_PIDFILE, strerror(ret));
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains invalid pid.\n",
+ SSSD_PIDFILE);
+ }
+ goto done;
+ }
+ if (fsize == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "File \"%s\" contains no pid.\n",
+ SSSD_PIDFILE);
+ ret = EINVAL;
+ goto done;
+ }
+
+ pid_str[MAX_PID_LENGTH-1] = '\0';
+ *out_pid = parse_pid(pid_str);
+ if (*out_pid == 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "File \"%s\" contains invalid pid.\n", SSSD_PIDFILE);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (pid_file != NULL) {
+ fclose(pid_file);
+ }
+ return ret;
+}
+
+bool sss_daemon_running(void)
+{
+ return sss_signal(0) == EOK;
+}
+
+errno_t sss_signal(int signum)
+{
+ int ret;
+ pid_t pid;
+
+ ret = sss_pid(&pid);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (kill(pid, signum) != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not send signal %d to process %d: %s\n",
+ signum, pid, strerror(errno));
+ return ret;
+ }
+
+ return EOK;
+}
diff --git a/src/tools/common/sss_process.h b/src/tools/common/sss_process.h
new file mode 100644
index 0000000..6bbb094
--- /dev/null
+++ b/src/tools/common/sss_process.h
@@ -0,0 +1,29 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_PROCESS_H_
+#define _SSS_PROCESS_H_
+
+#include "util/util.h"
+
+bool sss_daemon_running(void);
+errno_t sss_signal(int signum);
+
+#endif /* _SSS_PROCESS_H_ */
diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
new file mode 100644
index 0000000..e67de3a
--- /dev/null
+++ b/src/tools/common/sss_tools.c
@@ -0,0 +1,617 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <popt.h>
+
+#include "config.h"
+#include "util/util.h"
+#include "confdb/confdb.h"
+#include "confdb/confdb_setup.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+
+static void sss_tool_print_common_opts(int min_len)
+{
+ ERROR("Help options:\n");
+ fprintf(stderr, " %-*s\t %s\n", min_len, "-?, --help",
+ _("Show this for a command"));
+ fprintf(stderr, " %-*s\t %s\n", min_len, "--usage",
+ _("Show brief usage message for a command"));
+ ERROR("\n");
+
+ ERROR("Debug options:\n");
+ fprintf(stderr, " %-*s\t %s\n", min_len, "--debug",
+ _("Enable debug log level of sssctl tool"));
+}
+
+static struct poptOption *sss_tool_common_opts_table(void)
+{
+ static struct poptOption common_opts[] = {
+ {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, NULL,
+ 0, NULL, NULL },
+ POPT_TABLEEND
+ };
+
+ common_opts[0].descrip = _("The debug level to run with");
+
+ return common_opts;
+}
+
+static void sss_tool_common_opts(struct sss_tool_ctx *tool_ctx,
+ int *argc, const char **argv)
+{
+ poptContext pc;
+ int debug = SSSDBG_TOOLS_DEFAULT;
+ int orig_argc = *argc;
+ int help = 0;
+
+ struct poptOption options[] = {
+ {"debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_STRIP, &debug,
+ 0, _("The debug level to run with"), NULL },
+ {"help", '?', POPT_ARG_VAL | POPT_ARGFLAG_DOC_HIDDEN, &help,
+ 1, NULL, NULL },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], orig_argc, argv, options, 0);
+ while (poptGetNextOpt(pc) != -1) {
+ /* do nothing */
+ }
+
+ /* Strip common options from arguments. We will discard_const here,
+ * since it is not worth the trouble to convert it back and forth. */
+ *argc = poptStrippedArgv(pc, orig_argc, discard_const_p(char *, argv));
+ tool_ctx->print_help = help;
+
+ DEBUG_CLI_INIT(debug);
+
+ poptFreeContext(pc);
+}
+
+static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx **_confdb)
+{
+ struct confdb_ctx *confdb;
+ char *path;
+ errno_t ret;
+
+ path = talloc_asprintf(mem_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (path == NULL) {
+ return ENOMEM;
+ }
+
+ ret = confdb_setup(mem_ctx, path,
+ SSSD_CONFIG_FILE, CONFDB_DEFAULT_CONFIG_DIR,
+ NULL,
+ &confdb);
+ talloc_zfree(path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup ConfDB [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (_confdb != NULL) {
+ *_confdb = confdb;
+ }
+
+ return EOK;
+}
+
+static errno_t sss_tool_domains_init(TALLOC_CTX *mem_ctx,
+ struct confdb_ctx *confdb,
+ struct sss_domain_info **_domains)
+{
+ struct sss_domain_info *domains;
+ struct sss_domain_info *dom;
+ errno_t ret;
+
+ ret = confdb_expand_app_domains(confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to expand application domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = confdb_get_domains(confdb, &domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ ret = sysdb_init(mem_ctx, domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the sysdb\n");
+ return ret;
+ }
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ if (!IS_SUBDOMAIN(dom)) {
+ /* Get flat name and domain ID (SID) from the cache
+ * if available */
+ ret = sysdb_master_domain_update(dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to update domain %s.\n",
+ dom->name);
+ }
+
+ /* Update list of subdomains for this domain */
+ ret = sysdb_update_subdomains(dom, confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to update subdomains for domain %s.\n",
+ dom->name);
+ }
+ }
+ }
+
+ for (dom = domains; dom != NULL;
+ dom = get_next_domain(dom, SSS_GND_DESCEND)) {
+ ret = sss_names_init(mem_ctx, confdb, dom->name, &dom->names);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_names_init() failed\n");
+ return ret;
+ }
+ }
+
+ *_domains = domains;
+
+ return ret;
+}
+
+static errno_t sss_tool_init(TALLOC_CTX *mem_ctx,
+ int *argc, const char **argv,
+ struct sss_tool_ctx **_tool_ctx)
+{
+ struct sss_tool_ctx *tool_ctx;
+
+ tool_ctx = talloc_zero(mem_ctx, struct sss_tool_ctx);
+ if (tool_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return ENOMEM;
+ }
+
+ sss_tool_common_opts(tool_ctx, argc, argv);
+ *_tool_ctx = tool_ctx;
+
+ return EOK;
+}
+
+static bool sss_tool_is_delimiter(struct sss_route_cmd *command)
+{
+ if (command->command != NULL && command->command[0] == '\0') {
+ return true;
+ }
+
+ return false;
+}
+
+static bool sss_tools_handles_init_error(struct sss_route_cmd *command,
+ errno_t init_err)
+{
+ if (init_err == EOK) {
+ return true;
+ }
+
+ return command->handles_init_err == init_err;
+}
+
+static size_t sss_tool_max_length(struct sss_route_cmd *commands)
+{
+ size_t max = 0;
+ size_t len;
+ int i;
+
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ continue;
+ }
+
+ len = strlen(commands[i].command);
+ if (max < len) {
+ max = len;
+ }
+ }
+
+ return max;
+}
+
+static void sss_tool_usage(const char *tool_name, struct sss_route_cmd *commands)
+{
+ int min_len;
+ int i;
+
+ ERROR("Usage:\n%s COMMAND COMMAND-ARGS\n\n", tool_name);
+ ERROR("Available commands:\n");
+
+ min_len = sss_tool_max_length(commands);
+
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ fprintf(stderr, "\n%s\n", commands[i].description);
+ continue;
+ }
+
+ if (commands[i].description == NULL) {
+ fprintf(stderr, "* %40s\n", commands[i].command);
+ } else {
+ fprintf(stderr, "* %-*s\t %s\n",
+ min_len, commands[i].command, commands[i].description);
+ }
+ }
+
+ ERROR("\n");
+ sss_tool_print_common_opts(min_len);
+}
+
+static int tool_cmd_init(struct sss_tool_ctx *tool_ctx,
+ struct sss_route_cmd *command)
+{
+ int ret;
+ uid_t uid;
+
+ if (!(command->flags & SSS_TOOL_FLAG_SKIP_ROOT_CHECK)) {
+ uid = getuid();
+ if (uid != 0) {
+ ERROR("'%s' must be run as root\n", command->command);
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (command->flags & SSS_TOOL_FLAG_SKIP_CMD_INIT) {
+ return EOK;
+ }
+
+ /* Connect to confdb. */
+ ret = sss_tool_confdb_init(tool_ctx, &tool_ctx->confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open confdb [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Setup domains. */
+ ret = sss_tool_domains_init(tool_ctx, tool_ctx->confdb, &tool_ctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to setup domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = confdb_get_string(tool_ctx->confdb, tool_ctx,
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_MONITOR_DEFAULT_DOMAIN,
+ NULL, &tool_ctx->default_domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get the default domain [%d]: %s\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static errno_t sss_tool_route(int argc, const char **argv,
+ struct sss_tool_ctx *tool_ctx,
+ struct sss_route_cmd *commands,
+ void *pvt)
+{
+ struct sss_cmdline cmdline;
+ const char *cmd;
+ int i;
+ int ret;
+
+ if (commands == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Bug: commands can't be NULL!\n");
+ return EINVAL;
+ }
+
+ if (argc < 2) {
+ sss_tool_usage(argv[0], commands);
+ return EINVAL;
+ }
+
+ cmd = argv[1];
+ for (i = 0; commands[i].command != NULL; i++) {
+ if (sss_tool_is_delimiter(&commands[i])) {
+ continue;
+ }
+
+ if (strcmp(commands[i].command, cmd) == 0) {
+ cmdline.exec = argv[0];
+ cmdline.command = argv[1];
+ cmdline.argc = argc - 2;
+ cmdline.argv = argv + 2;
+
+ if (!tool_ctx->print_help) {
+ ret = tool_cmd_init(tool_ctx, &commands[i]);
+
+ if (!sss_tools_handles_init_error(&commands[i], ret)) {
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "Command %s does not handle initialization error [%d] %s\n",
+ cmdline.command, ret, sss_strerror(ret));
+ return ret;
+ }
+ }
+
+ return commands[i].fn(&cmdline, tool_ctx, pvt);
+ }
+ }
+
+ sss_tool_usage(argv[0], commands);
+ return EINVAL;
+}
+
+static struct poptOption *nonnull_popt_table(struct poptOption *options)
+{
+ static struct poptOption empty[] = {
+ POPT_TABLEEND
+ };
+
+ if (options == NULL) {
+ return empty;
+ }
+
+ return options;
+}
+
+errno_t sss_tool_popt_ex(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt,
+ const char *fopt_name,
+ const char *fopt_help,
+ enum sss_tool_opt fopt_require,
+ const char **_fopt,
+ bool *_opt_set)
+{
+ struct poptOption opts_table[] = {
+ {NULL, '\0', POPT_ARG_INCLUDE_TABLE, nonnull_popt_table(options), \
+ 0, _("Command options:"), NULL },
+ {NULL, '\0', POPT_ARG_INCLUDE_TABLE, sss_tool_common_opts_table(), \
+ 0, NULL, NULL },
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+ const char *fopt;
+ char *help;
+ poptContext pc;
+ bool opt_set;
+ int ret;
+
+ /* Set output parameter _fopt to NULL value if present. */
+ if (_fopt != NULL) {
+ *_fopt = NULL;
+ }
+
+ /* Create help option string. We always need to append command name since
+ * we use POPT_CONTEXT_KEEP_FIRST. */
+ if (fopt_name == NULL) {
+ help = talloc_asprintf(NULL, "%s %s %s", cmdline->exec,
+ cmdline->command, _("[OPTIONS...]"));
+ } else {
+ help = talloc_asprintf(NULL, "%s %s %s %s", cmdline->exec,
+ cmdline->command, fopt_name, _("[OPTIONS...]"));
+ }
+ if (help == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ return ENOMEM;
+ }
+
+ /* Create popt context. This function is supposed to be called on
+ * command argv which does not contain executable (argv[0]), therefore
+ * we need to use KEEP_FIRST that ensures argv[0] is also processed. */
+ pc = poptGetContext(cmdline->exec, cmdline->argc, cmdline->argv,
+ opts_table, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, help);
+
+ /* Parse options. Invoke custom function if provided. If no parsing
+ * function is provided, print error on unknown option. */
+ while ((ret = poptGetNextOpt(pc)) != -1) {
+ if (popt_fn != NULL) {
+ ret = popt_fn(pc, ret, popt_fn_pvt);
+ if (ret != EOK) {
+ goto done;
+ }
+ } else {
+ ERROR("Invalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(ret));
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ /* Parse free option which is required if requested and fopt_require
+ * is SSS_TOOL_OPT_REQUIRED */
+ opt_set = true;
+ fopt = poptGetArg(pc);
+ if (_fopt != NULL) {
+ if (fopt == NULL) {
+ if (fopt_require == SSS_TOOL_OPT_REQUIRED) {
+ ERROR("Missing option: %s\n\n", fopt_help);
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ opt_set = false;
+ }
+
+ /* No more arguments expected. If something follows it is an error. */
+ if (poptGetArg(pc)) {
+ ERROR("Only one free argument is expected!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (fopt != NULL) {
+ *_fopt = strdup(fopt);
+ if (*_fopt == NULL) {
+ ERROR("Out of memory!");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ } else if (_fopt == NULL && fopt != NULL) {
+ /* Unexpected free argument. */
+ ERROR("Unexpected parameter: %s\n\n", fopt);
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+
+ if ((_fopt != NULL && fopt_require == SSS_TOOL_OPT_REQUIRED && cmdline->argc < 2)
+ || cmdline->argc < 1) {
+ opt_set = false;
+
+ /* If at least one option is required and not provided, print error. */
+ if (require_option == SSS_TOOL_OPT_REQUIRED) {
+ ERROR("At least one option is required!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (_opt_set != NULL) {
+ *_opt_set = opt_set;
+ }
+
+ ret = EOK;
+
+done:
+ poptFreeContext(pc);
+ talloc_free(help);
+ if (ret != EOK && _fopt != NULL) {
+ free(discard_const(*_fopt));
+ *_fopt = NULL;
+ }
+
+ return ret;
+}
+
+errno_t sss_tool_popt(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt)
+{
+ return sss_tool_popt_ex(cmdline, options, require_option,
+ popt_fn, popt_fn_pvt, NULL, NULL,
+ SSS_TOOL_OPT_REQUIRED, NULL, NULL);
+}
+
+int sss_tool_main(int argc, const char **argv,
+ struct sss_route_cmd *commands,
+ void *pvt)
+{
+ struct sss_tool_ctx *tool_ctx;
+ errno_t ret;
+
+ ret = sss_tool_init(NULL, &argc, argv, &tool_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create tool context\n");
+ return EXIT_FAILURE;
+ }
+
+ ret = sss_tool_route(argc, argv, tool_ctx, commands, pvt);
+ SYSDB_VERSION_ERROR(ret);
+ talloc_free(tool_ctx);
+ if (ret != EOK) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+errno_t sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+ struct sss_tool_ctx *tool_ctx,
+ const char *input,
+ const char **_username,
+ struct sss_domain_info **_domain)
+{
+ char *username = NULL;
+ char *domname = NULL;
+ struct sss_domain_info *domain;
+ int ret;
+
+ ret = sss_parse_name_for_domains(mem_ctx, tool_ctx->domains,
+ tool_ctx->default_domain, input,
+ &domname, &username);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain. The domain name may "
+ "be a subdomain that was not yet found.\n");
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ domain = find_domain_by_name(tool_ctx->domains, domname, true);
+
+ *_username = username;
+ *_domain = domain;
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_zfree(username);
+ talloc_zfree(domname);
+ }
+
+ return ret;
+}
+
+errno_t sss_tool_connect_to_confdb(TALLOC_CTX *ctx, struct confdb_ctx **cdb_ctx)
+{
+ int ret;
+ char *confdb_path = NULL;
+
+ confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (confdb_path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for confdb path\n");
+ return ENOMEM;
+ }
+
+ ret = confdb_init(ctx, cdb_ctx, confdb_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the confdb\n");
+ }
+
+ talloc_free(confdb_path);
+ return ret;
+}
diff --git a/src/tools/common/sss_tools.h b/src/tools/common/sss_tools.h
new file mode 100644
index 0000000..af49675
--- /dev/null
+++ b/src/tools/common/sss_tools.h
@@ -0,0 +1,105 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSS_TOOLS_H_
+#define _SSS_TOOLS_H_
+
+#include <talloc.h>
+#include <popt.h>
+
+#include "confdb/confdb.h"
+
+struct sss_tool_ctx {
+ struct confdb_ctx *confdb;
+
+ bool print_help;
+ char *default_domain;
+ struct sss_domain_info *domains;
+};
+
+struct sss_cmdline {
+ const char *exec; /* argv[0] */
+ const char *command; /* command name */
+ int argc; /* rest of arguments */
+ const char **argv;
+};
+
+typedef errno_t
+(*sss_route_fn)(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+#define SSS_TOOL_COMMAND_FLAGS(cmd, msg, err, fn, flags) \
+ {cmd, _(msg), err, fn, flags}
+#define SSS_TOOL_COMMAND(cmd, msg, err, fn) \
+ {cmd, _(msg), err, fn, 0}
+#define SSS_TOOL_COMMAND_NOMSG(cmd, err, fn) {cmd, NULL, err, fn, 0}
+#define SSS_TOOL_DELIMITER(message) {"", _(message), 0, NULL, 0}
+#define SSS_TOOL_LAST {NULL, NULL, 0, NULL, 0}
+
+#define SSS_TOOL_FLAG_SKIP_CMD_INIT 0x01
+#define SSS_TOOL_FLAG_SKIP_ROOT_CHECK 0x02
+
+struct sss_route_cmd {
+ const char *command;
+ const char *description;
+ errno_t handles_init_err;
+ sss_route_fn fn;
+ int flags;
+};
+
+typedef errno_t (*sss_popt_fn)(poptContext pc, char option, void *pvt);
+
+enum sss_tool_opt {
+ SSS_TOOL_OPT_REQUIRED,
+ SSS_TOOL_OPT_OPTIONAL
+};
+
+errno_t sss_tool_popt_ex(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt,
+ const char *fopt_name,
+ const char *fopt_help,
+ enum sss_tool_opt fopt_require,
+ const char **_fopt,
+ bool *_opt_set);
+
+errno_t sss_tool_popt(struct sss_cmdline *cmdline,
+ struct poptOption *options,
+ enum sss_tool_opt require_option,
+ sss_popt_fn popt_fn,
+ void *popt_fn_pvt);
+
+int sss_tool_main(int argc, const char **argv,
+ struct sss_route_cmd *commands,
+ void *pvt);
+
+errno_t sss_tool_parse_name(TALLOC_CTX *mem_ctx,
+ struct sss_tool_ctx *tool_ctx,
+ const char *input,
+ const char **_username,
+ struct sss_domain_info **_domain);
+
+
+errno_t sss_tool_connect_to_confdb(TALLOC_CTX *ctx, struct confdb_ctx **cdb_ctx);
+
+#endif /* SRC_TOOLS_COMMON_SSS_TOOLS_H_ */
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
new file mode 100644
index 0000000..79de13a
--- /dev/null
+++ b/src/tools/sss_cache.c
@@ -0,0 +1,1028 @@
+/*
+ SSSD
+
+ sss_cache
+
+ Copyright (C) Jan Zeleny <jzeleny@redhat.com> 2011
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <popt.h>
+#include <sys/types.h>
+
+#include "util/util.h"
+#include "tools/tools_util.h"
+#include "db/sysdb.h"
+#include "db/sysdb_services.h"
+#include "db/sysdb_autofs.h"
+#include "db/sysdb_ssh.h"
+#include "db/sysdb_sudo.h"
+
+#define INVALIDATE_NONE 0
+#define INVALIDATE_USERS 1
+#define INVALIDATE_GROUPS 2
+#define INVALIDATE_NETGROUPS 4
+#define INVALIDATE_SERVICES 8
+#define INVALIDATE_AUTOFSMAPS 16
+#define INVALIDATE_SSH_HOSTS 32
+#define INVALIDATE_SUDO_RULES 64
+
+#ifdef BUILD_AUTOFS
+#ifdef BUILD_SSH
+#define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
+ INVALIDATE_NETGROUPS | INVALIDATE_SERVICES | \
+ INVALIDATE_AUTOFSMAPS | INVALIDATE_SSH_HOSTS )
+#else /* BUILD_SSH */
+#define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
+ INVALIDATE_NETGROUPS | INVALIDATE_SERVICES | \
+ INVALIDATE_AUTOFSMAPS )
+#endif /* BUILD_SSH */
+#else /* BUILD_AUTOFS */
+#ifdef BUILD_SSH
+#define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
+ INVALIDATE_NETGROUPS | INVALIDATE_SERVICES | \
+ INVALIDATE_SSH_HOSTS )
+#else /* BUILD_SSH */
+#define INVALIDATE_EVERYTHING (INVALIDATE_USERS | INVALIDATE_GROUPS | \
+ INVALIDATE_NETGROUPS | INVALIDATE_SERVICES )
+#endif /* BUILD_SSH */
+#endif /* BUILD_AUTOFS */
+
+enum sss_cache_entry {
+ TYPE_USER=0,
+ TYPE_GROUP,
+ TYPE_NETGROUP,
+ TYPE_SERVICE,
+ TYPE_AUTOFSMAP,
+ TYPE_SSH_HOST,
+ TYPE_SUDO_RULE
+};
+
+static errno_t search_autofsmaps(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter, const char **attrs,
+ size_t *msgs_count, struct ldb_message ***msgs);
+
+struct input_values {
+ char *domain;
+ char *group;
+ char *map;
+ char *netgroup;
+ char *service;
+ char *ssh_host;
+ char *sudo_rule;
+ char *user;
+};
+
+struct cache_tool_ctx {
+ struct confdb_ctx *confdb;
+ struct sss_domain_info *domains;
+
+ char *user_filter;
+ char *group_filter;
+ char *netgroup_filter;
+ char *service_filter;
+ char *autofs_filter;
+ char *ssh_host_filter;
+ char *sudo_rule_filter;
+
+ char *user_name;
+ char *group_name;
+ char *netgroup_name;
+ char *service_name;
+ char *autofs_name;
+ char *ssh_host_name;
+ char *sudo_rule_name;
+
+ bool update_user_filter;
+ bool update_group_filter;
+ bool update_netgroup_filter;
+ bool update_service_filter;
+ bool update_autofs_filter;
+ bool update_ssh_host_filter;
+ bool update_sudo_rule_filter;
+};
+
+static void free_input_values(struct input_values *values);
+static bool is_filter_valid(struct cache_tool_ctx *ctx,
+ struct input_values *values, int idb);
+static errno_t init_domains(struct cache_tool_ctx *ctx,
+ const char *domain);
+static errno_t init_context(int argc, const char *argv[],
+ struct cache_tool_ctx **tctx);
+static errno_t invalidate_entry(TALLOC_CTX *ctx,
+ struct sss_domain_info *domain,
+ const char *name, int entry_type);
+static bool invalidate_entries(TALLOC_CTX *ctx,
+ struct sss_domain_info *dinfo,
+ enum sss_cache_entry entry_type,
+ const char *filter, const char *name);
+static errno_t update_all_filters(struct cache_tool_ctx *tctx,
+ struct sss_domain_info *dinfo);
+static int sysdb_invalidate_user_cache_entry(struct sss_domain_info *domain,
+ const char *name);
+static int sysdb_invalidate_group_cache_entry(struct sss_domain_info *domain,
+ const char *name);
+
+int main(int argc, const char *argv[])
+{
+ errno_t ret;
+ struct cache_tool_ctx *tctx = NULL;
+ struct sysdb_ctx *sysdb;
+ bool skipped = true;
+ struct sss_domain_info *dinfo;
+
+ /* If systemd is in offline mode,
+ * there's not going to be a sssd instance
+ * running. This occurs for both e.g. yum --installroot
+ * as well as rpm-ostree offline updates.
+ *
+ * So let's just quickly do nothing. (Though note today
+ * yum --installroot doesn't set this variable, rpm-ostree
+ * does)
+ *
+ * For more information on the variable, see:
+ * https://github.com/systemd/systemd/pull/7631
+ */
+ const char *systemd_offline = getenv ("SYSTEMD_OFFLINE");
+ if (systemd_offline && strcmp (systemd_offline, "1") == 0) {
+ return 0;
+ }
+
+ ret = init_context(argc, argv, &tctx);
+ if (ret == ERR_NO_DOMAIN_ENABLED) {
+ /* nothing to invalidate; no reason to fail */
+ ret = EOK;
+ goto done;
+ } else if (ret == ERR_DOMAIN_NOT_FOUND) {
+ /* Cannot find domain specified in the parameter --domain.
+ * It might be a typo and therefore we will fail.
+ */
+ ret = ENOENT;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Error initializing context for the application\n");
+ goto done;
+ }
+
+ for (dinfo = tctx->domains; dinfo;
+ dinfo = get_next_domain(dinfo, SSS_GND_DESCEND)) {
+ if (!IS_SUBDOMAIN(dinfo)) {
+ /* Update list of subdomains for this domain */
+ ret = sysdb_update_subdomains(dinfo, tctx->confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to update subdomains for domain %s.\n", dinfo->name);
+ }
+ }
+
+ sysdb = dinfo->sysdb;
+ /* Update filters for each domain */
+ ret = update_all_filters(tctx, dinfo);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to update filters.\n");
+ goto done;
+ }
+
+ ret = sysdb_transaction_start(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not start the transaction!\n");
+ goto done;
+ }
+
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_USER,
+ tctx->user_filter,
+ tctx->user_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_GROUP,
+ tctx->group_filter,
+ tctx->group_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_NETGROUP,
+ tctx->netgroup_filter,
+ tctx->netgroup_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_SERVICE,
+ tctx->service_filter,
+ tctx->service_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_AUTOFSMAP,
+ tctx->autofs_filter,
+ tctx->autofs_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_SSH_HOST,
+ tctx->ssh_host_filter,
+ tctx->ssh_host_name);
+ skipped &= !invalidate_entries(tctx, dinfo, TYPE_SUDO_RULE,
+ tctx->sudo_rule_filter,
+ tctx->sudo_rule_name);
+
+ ret = sysdb_transaction_commit(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not commit the transaction!\n");
+ ret = sysdb_transaction_cancel(sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to cancel transaction\n");
+ }
+ }
+ }
+
+ if (skipped == true) {
+ ERROR("No cache object matched the specified search\n");
+ ret = ENOENT;
+ goto done;
+ } else {
+ ret = sss_memcache_clear_all();
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to clear memory cache.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ if (tctx) talloc_free(tctx);
+ return ret;
+}
+
+static void free_input_values(struct input_values *values)
+{
+ free(values->domain);
+ free(values->group);
+ free(values->map);
+ free(values->netgroup);
+ free(values->service);
+ free(values->ssh_host);
+ free(values->sudo_rule);
+ free(values->user);
+}
+
+static errno_t update_filter(struct cache_tool_ctx *tctx,
+ struct sss_domain_info *dinfo,
+ char *name, bool update, const char *fmt,
+ enum sss_cache_entry entry_type,
+ bool force_case_sensitivity,
+ char **_filter)
+{
+ errno_t ret;
+ char *parsed_domain = NULL;
+ char *parsed_name = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *use_name = NULL;
+ char *filter;
+ char *sanitized;
+ char *lc_sanitized;
+
+ if (!name || !update) {
+ /* Nothing to do */
+ return EOK;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory.\n");
+ return ENOMEM;
+ }
+
+ ret = sss_parse_name(tmp_ctx, dinfo->names, name,
+ &parsed_domain, &parsed_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_parse_name failed\n");
+ goto done;
+ }
+
+ if (parsed_domain != NULL && strcasecmp(dinfo->name, parsed_domain) != 0) {
+ /* We were able to parse the domain from given fqdn, but it
+ * does not match with currently processed domain. */
+ filter = NULL;
+ ret = EOK;
+ goto done;
+ }
+
+ if (!dinfo->case_sensitive && !force_case_sensitivity) {
+ use_name = sss_tc_utf8_str_tolower(tmp_ctx, parsed_name);
+ if (!use_name) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ } else {
+ use_name = parsed_name;
+ }
+
+ switch (entry_type) {
+ case TYPE_USER:
+ case TYPE_GROUP:
+ use_name = sss_create_internal_fqname(tmp_ctx, use_name, dinfo->name);
+ default:
+ break;
+ }
+ if (!use_name) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize_for_dom(tmp_ctx, use_name, dinfo,
+ &sanitized, &lc_sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to sanitize the given name.\n");
+ goto done;
+ }
+
+ if (fmt) {
+ if (!dinfo->case_sensitive && !force_case_sensitivity) {
+ filter = talloc_asprintf(tmp_ctx, "(|(%s=%s)(%s=%s))",
+ SYSDB_NAME_ALIAS, lc_sanitized,
+ SYSDB_NAME_ALIAS, sanitized);
+ } else {
+ filter = talloc_asprintf(tmp_ctx, fmt, SYSDB_NAME, sanitized);
+ }
+ } else {
+ filter = talloc_strdup(tmp_ctx, sanitized);
+ }
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret == EOK) {
+ talloc_free(*_filter);
+ *_filter = talloc_steal(tctx, filter);
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+
+}
+
+/* This function updates all filters for specified domain using this
+ * domains regex to parse string into domain and name (if exists). */
+static errno_t update_all_filters(struct cache_tool_ctx *tctx,
+ struct sss_domain_info *dinfo)
+{
+ errno_t ret;
+
+ /* Update user filter */
+ ret = update_filter(tctx, dinfo, tctx->user_name,
+ tctx->update_user_filter, "(%s=%s)",
+ TYPE_USER, false,
+ &tctx->user_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update group filter */
+ ret = update_filter(tctx, dinfo, tctx->group_name,
+ tctx->update_group_filter, "(%s=%s)",
+ TYPE_GROUP, false,
+ &tctx->group_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update netgroup filter */
+ ret = update_filter(tctx, dinfo, tctx->netgroup_name,
+ tctx->update_netgroup_filter, "(%s=%s)",
+ TYPE_NETGROUP, false,
+ &tctx->netgroup_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update service filter */
+ ret = update_filter(tctx, dinfo, tctx->service_name,
+ tctx->update_service_filter, "(%s=%s)",
+ TYPE_SERVICE, false,
+ &tctx->service_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update autofs filter */
+ ret = update_filter(tctx, dinfo, tctx->autofs_name,
+ tctx->update_autofs_filter,
+ "(&(objectclass="SYSDB_AUTOFS_MAP_OC")(%s=%s))",
+ TYPE_AUTOFSMAP, true,
+ &tctx->autofs_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update ssh host filter */
+ ret = update_filter(tctx, dinfo, tctx->ssh_host_name,
+ tctx->update_ssh_host_filter, "(%s=%s)",
+ TYPE_SSH_HOST, false,
+ &tctx->ssh_host_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ /* Update sudo rule filter */
+ ret = update_filter(tctx, dinfo, tctx->sudo_rule_name,
+ tctx->update_sudo_rule_filter,
+ "(%s=%s)", TYPE_SUDO_RULE, false,
+ &tctx->sudo_rule_filter);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return EOK;
+}
+
+static bool invalidate_entries(TALLOC_CTX *ctx,
+ struct sss_domain_info *dinfo,
+ enum sss_cache_entry entry_type,
+ const char *filter, const char *name)
+{
+ const char *attrs[] = {SYSDB_NAME, NULL};
+ size_t msg_count = 0;
+ struct ldb_message **msgs;
+ const char *type_string = "unknown";
+ errno_t ret = EINVAL;
+ int i;
+ const char *c_name;
+ bool iret;
+
+ if (!filter) return false;
+ switch (entry_type) {
+ case TYPE_USER:
+ type_string = "user";
+ ret = sysdb_search_users(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+ break;
+ case TYPE_GROUP:
+ type_string = "group";
+ ret = sysdb_search_groups(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+ break;
+ case TYPE_NETGROUP:
+ type_string = "netgroup";
+ ret = sysdb_search_netgroups(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+ break;
+ case TYPE_SERVICE:
+ type_string = "service";
+ ret = sysdb_search_services(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+ break;
+ case TYPE_AUTOFSMAP:
+ type_string = "autofs map";
+ ret = search_autofsmaps(ctx, dinfo, filter, attrs, &msg_count, &msgs);
+ break;
+ case TYPE_SSH_HOST:
+ type_string = "ssh_host";
+#ifdef BUILD_SSH
+ ret = sysdb_search_ssh_hosts(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+#else /* BUILD_SSH */
+ ret = ENOSYS;
+#endif /* BUILD_SSH */
+ break;
+ case TYPE_SUDO_RULE:
+ type_string = "sudo_rule";
+#ifdef BUILD_SUDO
+ ret = sysdb_search_sudo_rules(ctx, dinfo,
+ filter, attrs, &msg_count, &msgs);
+#else /* BUILD_SUDO */
+ ret = ENOSYS;
+#endif /* BUILD_SUDO */
+ break;
+ }
+
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "'%s' %s: Not found in domain '%s'\n",
+ type_string, name ? name : "", dinfo->name);
+ if (name == NULL) {
+ /* nothing to invalidate in that domain, no reason to fail */
+ return true;
+ } else {
+ /* we failed to invalidate explicit name; inform about it */
+ return false;
+ }
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Searching for %s in domain %s with filter %s failed\n",
+ type_string, dinfo->name, filter);
+ }
+ return false;
+ }
+
+ iret = true;
+ for (i = 0; i < msg_count; i++) {
+ c_name = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+ if (c_name == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Something bad happened, can't find attribute %s\n",
+ SYSDB_NAME);
+ ERROR("Couldn't invalidate %1$s\n", type_string);
+ iret = false;
+ } else {
+ ret = invalidate_entry(ctx, dinfo, c_name, entry_type);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Couldn't invalidate %s %s\n", type_string, c_name);
+ ERROR("Couldn't invalidate %1$s %2$s\n", type_string, c_name);
+ iret = false;
+ }
+ }
+ }
+ talloc_zfree(msgs);
+ return iret;
+}
+
+static errno_t invalidate_entry(TALLOC_CTX *ctx,
+ struct sss_domain_info *domain,
+ const char *name, int entry_type)
+{
+ struct sysdb_attrs *sys_attrs = NULL;
+ errno_t ret;
+
+ sys_attrs = sysdb_new_attrs(ctx);
+ if (sys_attrs) {
+ ret = sysdb_attrs_add_time_t(sys_attrs,
+ SYSDB_CACHE_EXPIRE, 1);
+ if (ret == EOK) {
+ switch (entry_type) {
+ case TYPE_USER:
+ /* For users, we also need to reset the initgroups
+ * cache expiry */
+ ret = sysdb_attrs_add_time_t(sys_attrs,
+ SYSDB_INITGR_EXPIRE, 1);
+ if (ret != EOK) return ret;
+ ret = sysdb_attrs_add_string(sys_attrs,
+ SYSDB_ORIG_MODSTAMP, "1");
+ if (ret != EOK) return ret;
+ ret = sysdb_attrs_add_uint32(sys_attrs,
+ SYSDB_USN, 1);
+ if (ret != EOK) return ret;
+
+ ret = sysdb_set_user_attr(domain, name, sys_attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) break;
+
+ /* WARNING: Direct writing to persistent cache!! */
+ ret = sysdb_invalidate_user_cache_entry(domain, name);
+ break;
+ case TYPE_GROUP:
+ ret = sysdb_attrs_add_string(sys_attrs,
+ SYSDB_ORIG_MODSTAMP, "1");
+ if (ret != EOK) return ret;
+ ret = sysdb_attrs_add_uint32(sys_attrs,
+ SYSDB_USN, 1);
+ if (ret != EOK) return ret;
+
+ ret = sysdb_set_group_attr(domain, name, sys_attrs,
+ SYSDB_MOD_REP);
+ if (ret != EOK) break;
+
+ /* WARNING: Direct writing to persistent cache!! */
+ ret = sysdb_invalidate_group_cache_entry(domain, name);
+ break;
+ case TYPE_NETGROUP:
+ ret = sysdb_set_netgroup_attr(domain, name, sys_attrs,
+ SYSDB_MOD_REP);
+ break;
+ case TYPE_SERVICE:
+ ret = sysdb_set_service_attr(domain, name,
+ sys_attrs, SYSDB_MOD_REP);
+ break;
+ case TYPE_AUTOFSMAP:
+ /* For users, we also need to reset the enumeration
+ * expiration time. */
+ ret = sysdb_attrs_add_time_t(sys_attrs,
+ SYSDB_ENUM_EXPIRE, 1);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sysdb_set_autofsmap_attr(domain, name,
+ sys_attrs, SYSDB_MOD_REP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not invalidate "
+ "autofs map %s\n", name);
+ break;
+ }
+
+ ret = sysdb_invalidate_autofs_entries(domain, name);
+ break;
+ case TYPE_SSH_HOST:
+#ifdef BUILD_SSH
+ ret = sysdb_set_ssh_host_attr(domain, name,
+ sys_attrs, SYSDB_MOD_REP);
+#else /* BUILD_SSH */
+ ret = ENOSYS;
+#endif /* BUILD_SSH */
+ break;
+ case TYPE_SUDO_RULE:
+#ifdef BUILD_SUDO
+ ret = sysdb_set_sudo_rule_attr(domain, name,
+ sys_attrs, SYSDB_MOD_REP);
+#else /* BUILD_SUDO */
+ ret = ENOSYS;
+#endif /* BUILD_SUDO */
+ break;
+ default:
+ return EINVAL;
+ }
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not set entry attributes\n");
+ }
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Could not add expiration time to attributes\n");
+ }
+ talloc_zfree(sys_attrs);
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Could not create sysdb attributes\n");
+ ret = ENOMEM;
+ }
+ return ret;
+}
+
+static errno_t init_domains(struct cache_tool_ctx *ctx,
+ const char *domain)
+{
+ char *confdb_path;
+ int ret;
+ struct sss_domain_info *dinfo;
+
+ confdb_path = talloc_asprintf(ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (confdb_path == NULL) {
+ return ENOMEM;
+ }
+
+ /* Connect to the conf db */
+ ret = confdb_init(ctx, &ctx->confdb, confdb_path);
+ talloc_free(confdb_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the confdb\n");
+ return ret;
+ }
+
+ if (domain) {
+ ret = sssd_domain_init(ctx, ctx->confdb,
+ domain, DB_PATH, &ctx->domains);
+ if (ret != EOK) {
+ SYSDB_VERSION_ERROR(ret);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the sysdb\n");
+ return ret;
+ }
+
+ } else {
+ ret = confdb_get_domains(ctx->confdb, &ctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not initialize domains\n");
+ return ret;
+ }
+
+ ret = sysdb_init(ctx, ctx->domains);
+ SYSDB_VERSION_ERROR(ret);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the sysdb\n");
+ return ret;
+ }
+ }
+
+ for (dinfo = ctx->domains; dinfo; dinfo = get_next_domain(dinfo, 0)) {
+ ret = sss_names_init(ctx, ctx->confdb, dinfo->name, &dinfo->names);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_names_init() failed\n");
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t init_context(int argc, const char *argv[],
+ struct cache_tool_ctx **tctx)
+{
+ struct cache_tool_ctx *ctx = NULL;
+ int idb = INVALIDATE_NONE;
+ struct input_values values = { 0 };
+ int debug = SSSDBG_TOOLS_DEFAULT;
+ errno_t ret = EOK;
+
+ poptContext pc = NULL;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &debug,
+ 0, _("The debug level to run with"), NULL },
+ { "everything", 'E', POPT_ARG_NONE, NULL, 'e',
+ _("Invalidate all cached entries"), NULL },
+ { "user", 'u', POPT_ARG_STRING, &(values.user), 0,
+ _("Invalidate particular user"), NULL },
+ { "users", 'U', POPT_ARG_NONE, NULL, 'u',
+ _("Invalidate all users"), NULL },
+ { "group", 'g', POPT_ARG_STRING, &(values.group), 0,
+ _("Invalidate particular group"), NULL },
+ { "groups", 'G', POPT_ARG_NONE, NULL, 'g',
+ _("Invalidate all groups"), NULL },
+ { "netgroup", 'n', POPT_ARG_STRING, &(values.netgroup), 0,
+ _("Invalidate particular netgroup"), NULL },
+ { "netgroups", 'N', POPT_ARG_NONE, NULL, 'n',
+ _("Invalidate all netgroups"), NULL },
+ { "service", 's', POPT_ARG_STRING, &(values.service), 0,
+ _("Invalidate particular service"), NULL },
+ { "services", 'S', POPT_ARG_NONE, NULL, 's',
+ _("Invalidate all services"), NULL },
+#ifdef BUILD_AUTOFS
+ { "autofs-map", 'a', POPT_ARG_STRING, &(values.map), 0,
+ _("Invalidate particular autofs map"), NULL },
+ { "autofs-maps", 'A', POPT_ARG_NONE, NULL, 'a',
+ _("Invalidate all autofs maps"), NULL },
+#endif /* BUILD_AUTOFS */
+#ifdef BUILD_SSH
+ { "ssh-host", 'h', POPT_ARG_STRING, &(values.ssh_host), 0,
+ _("Invalidate particular SSH host"), NULL },
+ { "ssh-hosts", 'H', POPT_ARG_NONE, NULL, 'h',
+ _("Invalidate all SSH hosts"), NULL },
+#endif /* BUILD_SSH */
+#ifdef BUILD_SUDO
+ { "sudo-rule", 'r', POPT_ARG_STRING, &(values.sudo_rule), 0,
+ _("Invalidate particular sudo rule"), NULL },
+ { "sudo-rules", 'R', POPT_ARG_NONE, NULL, 'r',
+ _("Invalidate all cached sudo rules"), NULL },
+#endif /* BUILD_SUDO */
+ { "domain", 'd', POPT_ARG_STRING, &(values.domain), 0,
+ _("Only invalidate entries from a particular domain"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = set_locale();
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "set_locale failed (%d): %s\n", ret, strerror(ret));
+ ERROR("Error setting the locale\n");
+ goto fini;
+ }
+
+ pc = poptGetContext(NULL, argc, argv, long_options, 0);
+ while ((ret = poptGetNextOpt(pc)) > 0) {
+ switch (ret) {
+ case 'u':
+ idb |= INVALIDATE_USERS;
+ break;
+ case 'g':
+ idb |= INVALIDATE_GROUPS;
+ break;
+ case 'n':
+ idb |= INVALIDATE_NETGROUPS;
+ break;
+ case 's':
+ idb |= INVALIDATE_SERVICES;
+ break;
+ case 'a':
+ idb |= INVALIDATE_AUTOFSMAPS;
+ break;
+ case 'h':
+ idb |= INVALIDATE_SSH_HOSTS;
+ break;
+ case 'r':
+ idb |= INVALIDATE_SUDO_RULES;
+ break;
+ case 'e':
+ idb = INVALIDATE_EVERYTHING;
+#ifdef BUILD_SUDO
+ idb |= INVALIDATE_SUDO_RULES;
+#endif /* BUILD_SUDO */
+ break;
+ }
+ }
+
+ DEBUG_CLI_INIT(debug);
+ debug_prg_name = argv[0];
+
+ if (ret != -1) {
+ BAD_POPT_PARAMS(pc, poptStrerror(ret), ret, fini);
+ }
+
+ if (poptGetArg(pc)) {
+ BAD_POPT_PARAMS(pc,
+ _("Unexpected argument(s) provided, options that "
+ "invalidate a single object only accept a single "
+ "provided argument.\n"),
+ ret, fini);
+ }
+
+ if (idb == INVALIDATE_NONE && !values.user && !values.group &&
+ !values.netgroup && !values.service && !values.map &&
+ !values.ssh_host && !values.sudo_rule) {
+ BAD_POPT_PARAMS(pc,
+ _("Please select at least one object to invalidate\n"),
+ ret, fini);
+ }
+
+ CHECK_ROOT(ret, debug_prg_name);
+
+ ctx = talloc_zero(NULL, struct cache_tool_ctx);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for tools context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ if (idb & INVALIDATE_USERS) {
+ ctx->user_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_user_filter = false;
+ } else if (values.user) {
+ ctx->user_name = talloc_strdup(ctx, values.user);
+ ctx->update_user_filter = true;
+ }
+
+ if (idb & INVALIDATE_GROUPS) {
+ ctx->group_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_group_filter = false;
+ } else if (values.group) {
+ ctx->group_name = talloc_strdup(ctx, values.group);
+ ctx->update_group_filter = true;
+ }
+
+ if (idb & INVALIDATE_NETGROUPS) {
+ ctx->netgroup_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_netgroup_filter = false;
+ } else if (values.netgroup) {
+ ctx->netgroup_name = talloc_strdup(ctx, values.netgroup);
+ ctx->update_netgroup_filter = true;
+ }
+
+ if (idb & INVALIDATE_SERVICES) {
+ ctx->service_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_service_filter = false;
+ } else if (values.service) {
+ ctx->service_name = talloc_strdup(ctx, values.service);
+ ctx->update_service_filter = true;
+ }
+
+ if (idb & INVALIDATE_AUTOFSMAPS) {
+ ctx->autofs_filter = talloc_asprintf(ctx, "(&(objectclass=%s)(%s=*))",
+ SYSDB_AUTOFS_MAP_OC, SYSDB_NAME);
+ ctx->update_autofs_filter = false;
+ } else if (values.map) {
+ ctx->autofs_name = talloc_strdup(ctx, values.map);
+ ctx->update_autofs_filter = true;
+ }
+
+ if (idb & INVALIDATE_SSH_HOSTS) {
+ ctx->ssh_host_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_ssh_host_filter = false;
+ } else if (values.ssh_host) {
+ ctx->ssh_host_name = talloc_strdup(ctx, values.ssh_host);
+ ctx->update_ssh_host_filter = true;
+ }
+
+ if (idb & INVALIDATE_SUDO_RULES) {
+ ctx->sudo_rule_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+ ctx->update_sudo_rule_filter = false;
+ } else if (values.sudo_rule) {
+ ctx->sudo_rule_name = talloc_strdup(ctx, values.sudo_rule);
+ ctx->update_sudo_rule_filter = true;
+ }
+
+ if (is_filter_valid(ctx, &values, idb) == false) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Construction of filters failed\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ ret = init_domains(ctx, values.domain);
+ if (ret == ERR_NO_DOMAIN_ENABLED && values.domain == NULL) {
+ /* Nothing to invalidate; do not log confusing messages. */
+ goto fini;
+ } else if (ret != EOK) {
+ if (values.domain) {
+ ERROR("Could not open domain %1$s. If the domain is a subdomain "
+ "(trusted domain), use fully qualified name instead of "
+ "--domain/-d parameter.\n", values.domain);
+ ret = ERR_DOMAIN_NOT_FOUND;
+ } else {
+ ERROR("Could not open available domains\n");
+ }
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Initialization of sysdb connections failed\n");
+ goto fini;
+ }
+
+ ret = EOK;
+
+fini:
+ poptFreeContext(pc);
+ free_input_values(&values);
+ if (ret != EOK && ctx) {
+ talloc_zfree(ctx);
+ }
+ if (ret == EOK) {
+ *tctx = ctx;
+ }
+ return ret;
+}
+
+static bool is_filter_valid(struct cache_tool_ctx *ctx,
+ struct input_values *values, int idb)
+{
+ if ((idb & INVALIDATE_USERS) && ctx->user_filter == NULL) {
+ return false;
+ }
+
+ if ((idb & INVALIDATE_GROUPS) && ctx->group_filter == NULL) {
+ return false;
+ }
+
+ if ((idb & INVALIDATE_NETGROUPS) && ctx->netgroup_filter == NULL) {
+ return false;
+ }
+
+ if ((idb & INVALIDATE_SERVICES) && ctx->service_filter == NULL) {
+ return false;
+ }
+
+ if ((idb & INVALIDATE_AUTOFSMAPS) && ctx->autofs_filter == NULL) {
+ return false;
+ }
+
+ if ((idb & INVALIDATE_SSH_HOSTS) && ctx->ssh_host_filter == NULL) {
+ return false;
+ }
+
+ if (values->user && ctx->user_name == NULL) {
+ return false;
+ }
+
+ if (values->group && ctx->group_name == NULL) {
+ return false;
+ }
+
+ if (values->netgroup && ctx->netgroup_name == NULL) {
+ return false;
+ }
+
+ if (values->service && ctx->service_name == NULL) {
+ return false;
+ }
+
+ if (values->map && ctx->autofs_name == NULL) {
+ return false;
+ }
+
+ if (values->ssh_host && ctx->ssh_host_name == NULL) {
+ return false;
+ }
+
+ if (values->sudo_rule && ctx->sudo_rule_name == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+static errno_t
+search_autofsmaps(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *sub_filter, const char **attrs,
+ size_t *msgs_count, struct ldb_message ***msgs)
+{
+#ifdef BUILD_AUTOFS
+ return sysdb_search_custom(mem_ctx, domain, sub_filter,
+ AUTOFS_MAP_SUBDIR, attrs,
+ msgs_count, msgs);
+#else
+ return ENOSYS;
+#endif /* BUILD_AUTOFS */
+}
+
+/* WARNING: Direct writing to persistent cache!! */
+static int sysdb_invalidate_user_cache_entry(struct sss_domain_info *domain,
+ const char *name)
+{
+ return sysdb_invalidate_cache_entry(domain, name, true);
+}
+
+/* WARNING: Direct writing to persistent cache!! */
+static int sysdb_invalidate_group_cache_entry(struct sss_domain_info *domain,
+ const char *name)
+{
+ return sysdb_invalidate_cache_entry(domain, name, false);
+}
diff --git a/src/tools/sss_obfuscate b/src/tools/sss_obfuscate
new file mode 100644
index 0000000..5981e81
--- /dev/null
+++ b/src/tools/sss_obfuscate
@@ -0,0 +1,123 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+
+import sys
+from optparse import OptionParser
+
+import pysss
+import SSSDConfig
+import getpass
+
+
+def parse_options():
+ parser = OptionParser()
+ parser.set_description("sss_obfuscate converts a given password into \
+ human-unreadable format and places it into \
+ appropriate domain section of the SSSD config \
+ file. The password can be passed in by stdin, \
+ specified on the command-line or entered \
+ interactively")
+ parser.add_option("-s", "--stdin", action="store_true",
+ dest="stdin", default=False,
+ help="Read the password from stdin.")
+ parser.add_option("-d", "--domain",
+ dest="domain", default=None,
+ help="The domain to use the password in (mandatory)",
+ metavar="DOMNAME")
+ parser.add_option("-f", "--file",
+ dest="filename", default=None,
+ help="Set input file to FILE (default: Use system "
+ "default, usually /etc/sssd/sssd.conf)",
+ metavar="FILE")
+ (options, args) = parser.parse_args()
+
+ return options, args
+
+
+def main():
+ options, args = parse_options()
+ if not options:
+ print("Cannot parse options", file=sys.stderr)
+ return 1
+
+ if not options.domain:
+ print("No domain specified", file=sys.stderr)
+ return 1
+
+ if not options.stdin:
+ try:
+ pprompt = lambda: (getpass.getpass("Enter password: "),
+ getpass.getpass("Re-enter password: "))
+ p1, p2 = pprompt()
+
+ # Work around bug in Python 2.6
+ if '\x03' in p1 or '\x03' in p2:
+ raise KeyboardInterrupt
+
+ while p1 != p2:
+ print('Passwords do not match. Try again')
+ p1, p2 = pprompt()
+
+ # Work around bug in Python 2.6
+ if '\x03' in p1 or '\x03' in p2:
+ raise KeyboardInterrupt
+ password = p1
+
+ except EOFError:
+ print('\nUnexpected end-of-file. Password change aborted',
+ file=sys.stderr)
+ return 1
+ except KeyboardInterrupt:
+ return 1
+
+ else:
+ try:
+ password = sys.stdin.read()
+ except KeyboardInterrupt:
+ return 1
+
+ # Obfuscate the password
+ obfobj = pysss.password()
+ obfpwd = obfobj.encrypt(password, obfobj.AES_256)
+
+ # Save the obfuscated password into the domain
+ try:
+ sssdconfig = SSSDConfig.SSSDConfig()
+ except IOError:
+ print("Cannot read internal configuration files.")
+ return 1
+ try:
+ sssdconfig.import_config(options.filename)
+ except IOError:
+ print("Permissions error reading config file")
+ return 1
+
+ try:
+ domain = sssdconfig.get_domain(options.domain)
+ except SSSDConfig.NoDomainError:
+ print("No such domain %s" % options.domain)
+ return 1
+
+ try:
+ domain.set_option('ldap_default_authtok_type', 'obfuscated_password')
+ domain.set_option('ldap_default_authtok', obfpwd)
+ except SSSDConfig.NoOptionError:
+ print("The domain %s does not seem to support the required options"
+ % options.domain)
+ return 1
+
+ sssdconfig.save_domain(domain)
+ try:
+ sssdconfig.write()
+ except IOError:
+ # File could not be written
+ print("Could not write to config file. Check that you have the "
+ "appropriate permissions to edit this file.", file=sys.stderr)
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ ret = main()
+ sys.exit(ret)
diff --git a/src/tools/sss_override.c b/src/tools/sss_override.c
new file mode 100644
index 0000000..cfd8f17
--- /dev/null
+++ b/src/tools/sss_override.c
@@ -0,0 +1,1975 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_colondb.h"
+
+#define LOCALVIEW SYSDB_LOCAL_VIEW_NAME
+#define ORIGNAME "originalName"
+
+struct override_user {
+ const char *input_name;
+ const char *orig_name;
+ const char *sysdb_name;
+ struct sss_domain_info *domain;
+
+ const char *name;
+ uid_t uid;
+ gid_t gid;
+ const char *home;
+ const char *shell;
+ const char *gecos;
+ const char *cert;
+};
+
+struct override_group {
+ const char *input_name;
+ const char *orig_name;
+ const char *sysdb_name;
+ struct sss_domain_info *domain;
+
+ const char *name;
+ gid_t gid;
+};
+
+static errno_t parse_cmdline(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct poptOption *options,
+ const char **_input_name,
+ const char **_orig_name,
+ struct sss_domain_info **_domain)
+{
+ enum sss_tool_opt require;
+ const char *input_name = NULL;
+ const char *orig_name;
+ struct sss_domain_info *domain;
+ errno_t ret;
+
+ *_input_name = NULL;
+ require = options == NULL ? SSS_TOOL_OPT_OPTIONAL : SSS_TOOL_OPT_REQUIRED;
+
+ ret = sss_tool_popt_ex(cmdline, options, require,
+ NULL, NULL, "NAME", _("Specify name."),
+ SSS_TOOL_OPT_REQUIRED, &input_name, NULL);
+ if (ret != EXIT_SUCCESS) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ ret = sss_tool_parse_name(tool_ctx, tool_ctx, input_name,
+ &orig_name, &domain);
+ if (ret != EOK) {
+ ERROR("Unable to parse name %s.\n", input_name);
+ free(discard_const(input_name));
+ return ret;
+ }
+
+ *_input_name = input_name;
+ *_orig_name = orig_name;
+ *_domain = domain;
+
+ return EXIT_SUCCESS;
+}
+
+static errno_t parse_cmdline_user_add(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_user *user)
+{
+ struct poptOption options[] = {
+ {"name", 'n', POPT_ARG_STRING, &user->name, 0, _("Override name"), NULL },
+ {"uid", 'u', POPT_ARG_INT, &user->uid, 0, _("Override uid (non-zero value)"), NULL },
+ {"gid", 'g', POPT_ARG_INT, &user->gid, 0, _("Override gid (non-zero value)"), NULL },
+ {"home", 'h', POPT_ARG_STRING, &user->home, 0, _("Override home directory"), NULL },
+ {"shell", 's', POPT_ARG_STRING, &user->shell, 0, _("Override shell"), NULL },
+ {"gecos", 'c', POPT_ARG_STRING, &user->gecos, 0, _("Override gecos"), NULL },
+ {"certificate", 'x', POPT_ARG_STRING, &user->cert, 0, _("Override certificate"), NULL },
+ POPT_TABLEEND
+ };
+
+ return parse_cmdline(cmdline, tool_ctx, options, &user->input_name,
+ &user->orig_name, &user->domain);
+}
+
+static errno_t parse_cmdline_user_del(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_user *user)
+{
+ return parse_cmdline(cmdline, tool_ctx, NULL, &user->input_name,
+ &user->orig_name, &user->domain);
+}
+
+static errno_t parse_cmdline_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_user *user)
+{
+ return parse_cmdline(cmdline, tool_ctx, NULL, &user->input_name,
+ &user->orig_name, &user->domain);
+}
+
+static errno_t parse_cmdline_group_add(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_group *group)
+{
+ struct poptOption options[] = {
+ {"name", 'n', POPT_ARG_STRING, &group->name, 0, _("Override name"), NULL },
+ {"gid", 'g', POPT_ARG_INT, &group->gid, 0, _("Override gid"), NULL },
+ POPT_TABLEEND
+ };
+
+ return parse_cmdline(cmdline, tool_ctx, options, &group->input_name,
+ &group->orig_name, &group->domain);
+}
+
+static errno_t parse_cmdline_group_del(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_group *group)
+{
+ return parse_cmdline(cmdline, tool_ctx, NULL, &group->input_name,
+ &group->orig_name, &group->domain);
+}
+
+static errno_t parse_cmdline_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct override_group *group)
+{
+ return parse_cmdline(cmdline, tool_ctx, NULL, &group->input_name,
+ &group->orig_name, &group->domain);
+}
+
+static errno_t parse_cmdline_find(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info **_dom)
+{
+ struct sss_domain_info *dom;
+ const char *domname = NULL;
+ errno_t ret;
+ struct poptOption options[] = {
+ {"domain", 'd', POPT_ARG_STRING | POPT_ARGFLAG_OPTIONAL,
+ &domname, 0, _("Domain name"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, NULL, NULL, SSS_TOOL_OPT_REQUIRED,
+ NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (domname == NULL) {
+ *_dom = NULL;
+ return EOK;
+ }
+
+ dom = find_domain_by_name(tool_ctx->domains, domname, true);
+ if (dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find domain %s\n", domname);
+ ERROR("Unable to find domain %s\n", domname);
+ return EINVAL;
+ }
+
+ *_dom = dom;
+
+ return EOK;
+}
+
+static errno_t parse_cmdline_import(struct sss_cmdline *cmdline,
+ const char **_file)
+{
+ errno_t ret;
+
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "FILE", "File to import the data from.",
+ SSS_TOOL_OPT_REQUIRED, _file, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t parse_cmdline_export(struct sss_cmdline *cmdline,
+ const char **_file)
+{
+ errno_t ret;
+
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "FILE", "File to export the data to.",
+ SSS_TOOL_OPT_REQUIRED, _file, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static errno_t prepare_view(struct sss_domain_info *domain)
+{
+ char *viewname = NULL;
+ errno_t ret;
+
+ ret = sysdb_get_view_name(NULL, domain->sysdb, &viewname);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_get_view_name() failed.\n");
+ return ret;
+ }
+
+ if (ret == EOK) {
+ if (is_local_view(viewname)) {
+ DEBUG(SSSDBG_TRACE_FUNC, "%s view is already present.\n", viewname);
+ ret = EOK;
+ goto done;
+ } else if (viewname != NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "There already exists view %s. "
+ "Only one view is supported. Nothing to do.\n", viewname);
+ ret = EEXIST;
+ goto done;
+ }
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Creating %s view.\n", LOCALVIEW);
+
+ ret = sysdb_update_view_name(domain->sysdb, LOCALVIEW);
+ if (ret == EOK) {
+ printf("SSSD needs to be restarted for the changes to take effect.\n");
+ }
+
+done:
+ talloc_free(viewname);
+ return ret;
+}
+
+errno_t prepare_view_msg(struct sss_domain_info *domain)
+{
+ errno_t ret;
+
+ ret = prepare_view(domain);
+ if (ret == EEXIST) {
+ ERROR("Other than " LOCALVIEW " view already exists "
+ "in domain %s.\n", domain->name);
+ } else if (ret != EOK) {
+ ERROR("Unable to prepare " LOCALVIEW
+ " view in domain %s.\n", domain->name);
+ }
+
+ return ret;
+}
+
+static char *build_anchor(TALLOC_CTX *mem_ctx, const char *obj_dn)
+{
+ char *anchor;
+ char *safe_dn;
+ errno_t ret;
+
+ ret = sysdb_dn_sanitize(mem_ctx, obj_dn, &safe_dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_dn_sanitize() failed\n");
+ return NULL;
+ }
+
+ anchor = talloc_asprintf(mem_ctx, ":%s:%s", LOCALVIEW, safe_dn);
+
+ talloc_free(safe_dn);
+
+ return anchor;
+}
+
+static struct sysdb_attrs *build_attrs(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *name,
+ uid_t uid,
+ gid_t gid,
+ const char *home,
+ const char *shell,
+ const char *gecos,
+ const char *cert)
+{
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+ char *fqname;
+
+ attrs = sysdb_new_attrs(mem_ctx);
+ if (attrs == NULL) {
+ return NULL;
+ }
+
+ if (name != NULL) {
+ fqname = sss_create_internal_fqname(attrs, name, dom->name);
+ if (fqname == NULL) {
+ return NULL;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_NAME, fqname);
+ talloc_free(fqname);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (uid != 0) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_UIDNUM, uid);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (gid != 0) {
+ ret = sysdb_attrs_add_uint32(attrs, SYSDB_GIDNUM, gid);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (home != NULL) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_HOMEDIR, home);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (shell != NULL) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_SHELL, shell);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (gecos != NULL) {
+ ret = sysdb_attrs_add_string(attrs, SYSDB_GECOS, gecos);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (cert != NULL) {
+ ret = sysdb_attrs_add_base64_blob(attrs, SYSDB_USER_CERT, cert);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(attrs);
+ return NULL;
+ }
+
+ return attrs;
+}
+
+static struct sysdb_attrs *build_user_attrs(TALLOC_CTX *mem_ctx,
+ struct override_user *user)
+{
+ return build_attrs(mem_ctx, user->domain, user->name, user->uid, user->gid,
+ user->home, user->shell, user->gecos, user->cert);
+}
+
+static struct sysdb_attrs *build_group_attrs(TALLOC_CTX *mem_ctx,
+ struct override_group *group)
+{
+ return build_attrs(mem_ctx, group->domain, group->name, 0, group->gid,
+ 0, NULL, NULL, NULL);
+}
+
+static char *get_fqname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ char *fqname = NULL;
+ char *dummy_domain = NULL;
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ char *shortname;
+ struct sss_domain_info *dom;
+
+ if (domain == NULL || domain->names == NULL) {
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ /* the name stored in sysdb already contains the lowercased domain */
+ ret = sss_parse_internal_fqname(tmp_ctx, name, &shortname, &dummy_domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "sss_parse_internal_fqname failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ dom = find_domain_by_name(get_domains_head(domain), dummy_domain, true);
+ if (dom == NULL) {
+ goto done;
+ }
+
+ /* Get length. */
+ fqname = sss_tc_fqname(mem_ctx, dom->names, dom, shortname);
+done:
+ talloc_free(tmp_ctx);
+ return fqname;
+}
+
+static char *get_sysname(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *name)
+{
+ if (domain == NULL || !domain->fqnames) {
+ return talloc_strdup(mem_ctx, name);
+ }
+
+ return sss_tc_fqname(mem_ctx, domain->names, domain, name);
+}
+
+static struct sss_domain_info *
+get_object_domain(enum sysdb_member_type type,
+ const char *name,
+ struct sss_domain_info *domain,
+ struct sss_domain_info *domains)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom = NULL;
+ struct ldb_result *res;
+ const char *strtype;
+ char *sysname;
+ char *fqname = NULL;
+ bool check_next;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ sysname = get_sysname(tmp_ctx, domain, name);
+ if (sysname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* Ensure that the object is in cache. */
+ switch (type) {
+ case SYSDB_MEMBER_USER:
+ if (getpwnam(sysname) == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+ break;
+ case SYSDB_MEMBER_GROUP:
+ if (getgrnam(sysname) == NULL) {
+ ret = ENOENT;
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ /* Find domain if it is unknown. */
+ if (domain == NULL) {
+ check_next = true;
+ dom = domains;
+ } else {
+ check_next = false;
+ dom = domain;
+ }
+
+ do {
+ talloc_zfree(fqname);
+ fqname = sss_create_internal_fqname(tmp_ctx, name, dom->name);
+ if (fqname == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ switch (type) {
+ case SYSDB_MEMBER_USER:
+ DEBUG(SSSDBG_TRACE_FUNC, "Trying to find user %s@%s\n",
+ name, dom->name);
+ ret = sysdb_getpwnam(tmp_ctx, dom, fqname, &res);
+ strtype = "user";
+ break;
+ case SYSDB_MEMBER_GROUP:
+ DEBUG(SSSDBG_TRACE_FUNC, "Trying to find group %s@%s\n",
+ name, dom->name);
+ ret = sysdb_getgrnam(tmp_ctx, dom, fqname, &res);
+ strtype = "group";
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (ret == EOK && res->count == 0) {
+ ret = ENOENT;
+
+ if (check_next) {
+ dom = dom->next;
+ continue;
+ }
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find %s %s@%s [%d]: %s\n",
+ strtype, name, dom->name, ret, sss_strerror(ret));
+ goto done;
+ } else if (res->count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one %s found?\n", strtype);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ check_next = false;
+ } while (check_next && dom != NULL);
+
+ if (dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No domain match for %s\n", name);
+ ret = ENOENT;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Domain of %s %s is %s\n",
+ strtype, name, dom->name);
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return dom;
+}
+
+static errno_t get_user_domain_msg(struct sss_tool_ctx *tool_ctx,
+ struct override_user *user)
+{
+ struct sss_domain_info *newdom;
+ const char *domname;
+
+ newdom = get_object_domain(SYSDB_MEMBER_USER, user->orig_name,
+ user->domain, tool_ctx->domains);
+ if (newdom == NULL) {
+ domname = user->domain == NULL ? "[unknown]" : user->domain->name;
+ ERROR("Unable to find user %s@%s.\n", user->orig_name, domname);
+ return ENOENT;
+ }
+
+ user->sysdb_name = sss_create_internal_fqname(tool_ctx, user->orig_name,
+ newdom->name);
+ if (user->sysdb_name == NULL) {
+ return ENOMEM;
+ }
+
+ user->domain = newdom;
+ return EOK;
+}
+
+static errno_t get_group_domain_msg(struct sss_tool_ctx *tool_ctx,
+ struct override_group *group)
+{
+ struct sss_domain_info *newdom;
+ const char *domname;
+
+ newdom = get_object_domain(SYSDB_MEMBER_GROUP, group->orig_name,
+ group->domain, tool_ctx->domains);
+ if (newdom == NULL) {
+ domname = group->domain == NULL ? "[unknown]" : group->domain->name;
+ ERROR("Unable to find group %s@%s.\n", group->orig_name, domname);
+ return ENOENT;
+ }
+
+ group->sysdb_name = sss_create_internal_fqname(tool_ctx, group->orig_name,
+ newdom->name);
+ if (group->sysdb_name == NULL) {
+ return ENOMEM;
+ }
+
+ group->domain = newdom;
+ return EOK;
+}
+
+static errno_t get_object_dn(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ enum sysdb_member_type type,
+ const char *name,
+ struct ldb_dn **_ldb_dn,
+ const char **_str_dn)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *ldb_dn;
+ const char *str_dn;
+ errno_t ret;
+ struct ldb_result *res;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ switch (type) {
+ case SYSDB_MEMBER_USER:
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &res);
+ break;
+ case SYSDB_MEMBER_GROUP:
+ ret = sysdb_getgrnam(tmp_ctx, domain, name, &res);
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported member type %d\n", type);
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to look up original object in cache.\n");
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Original object not found in cache.\n");
+ ret = ENOENT;
+ goto done;
+ } else if (res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "There are multiple object with name [%s] in the cache.\n", name);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ldb_dn = res->msgs[0]->dn;
+
+ if (ldb_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (_str_dn != NULL) {
+ str_dn = talloc_strdup(tmp_ctx, ldb_dn_get_linearized(ldb_dn));
+ if (str_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_str_dn = talloc_steal(mem_ctx, str_dn);
+ }
+
+ if (_ldb_dn != NULL) {
+ *_ldb_dn = talloc_steal(mem_ctx, ldb_dn);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t override_object_add(struct sss_domain_info *domain,
+ enum sysdb_member_type type,
+ struct sysdb_attrs *attrs,
+ const char *name)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *anchor;
+ struct ldb_dn *ldb_dn;
+ const char *str_dn;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = get_object_dn(tmp_ctx, domain, type, name, &ldb_dn, &str_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ anchor = build_anchor(tmp_ctx, str_dn);
+ if (anchor == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(attrs, SYSDB_OVERRIDE_ANCHOR_UUID, anchor);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Creating override for %s\n", str_dn);
+
+ ret = sysdb_store_override(domain, LOCALVIEW, type, attrs, ldb_dn);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t override_fqn(TALLOC_CTX *mem_ctx,
+ struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain,
+ const char *input,
+ const char **_name)
+{
+ struct sss_domain_info *dom;
+ errno_t ret;
+
+ if (input == NULL) {
+ return EOK;
+ }
+
+ ret = sss_tool_parse_name(mem_ctx, tool_ctx, input, _name, &dom);
+ if (ret == EAGAIN) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to find domain from "
+ "fqn %s\n", input);
+ ERROR("Changing domain is not allowed!\n");
+ ret = EINVAL;
+ } else if (ret == EOK && dom != NULL && dom != domain) {
+ DEBUG(SSSDBG_OP_FAILURE, "Trying to change domain from "
+ "%s to %s, not allowed!\n", domain->name, dom->name);
+ ERROR("Changing domain is not allowed!\n");
+ ret = EINVAL;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse name %s [%d]: %s\n",
+ input, ret, sss_strerror(ret));
+ }
+
+ return ret;
+}
+
+static errno_t override_user(struct sss_tool_ctx *tool_ctx,
+ struct override_user *input_user)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_user user;
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ user = *input_user;
+
+ /* We need to parse the name and ensure that domain did not change. */
+ ret = override_fqn(tmp_ctx, tool_ctx, user.domain, user.name, &user.name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = prepare_view_msg(user.domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ attrs = build_user_attrs(tool_ctx, &user);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = override_object_add(user.domain, SYSDB_MEMBER_USER, attrs,
+ user.sysdb_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t override_group(struct sss_tool_ctx *tool_ctx,
+ struct override_group *input_group)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_group group;
+ struct sysdb_attrs *attrs;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ group = *input_group;
+
+ /* We need to parse the name and ensure that domain did not change. */
+ ret = override_fqn(tmp_ctx, tool_ctx, group.domain, group.name,
+ &group.name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = prepare_view_msg(group.domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ attrs = build_group_attrs(tool_ctx, &group);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to build sysdb attrs.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = override_object_add(group.domain, SYSDB_MEMBER_GROUP, attrs,
+ group.sysdb_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add override object.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t override_object_del(struct sss_domain_info *domain,
+ enum sysdb_member_type type,
+ const char *name)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message *msg;
+ struct ldb_dn *override_dn;
+ struct ldb_dn *ldb_dn;
+ const char *str_dn;
+ const char *anchor;
+ errno_t ret;
+ int sret;
+ bool in_transaction = false;
+ struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb);
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ ret = get_object_dn(tmp_ctx, domain, type, name, &ldb_dn, &str_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ anchor = build_anchor(tmp_ctx, str_dn);
+ if (anchor == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ override_dn = ldb_dn_new_fmt(tmp_ctx, ldb,
+ SYSDB_TMPL_OVERRIDE, anchor, LOCALVIEW);
+ if (override_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Removing override for %s\n", str_dn);
+
+ ret = sysdb_transaction_start(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_transaction_start() failed.\n");
+ goto done;
+ }
+ in_transaction = true;
+
+ ret = sysdb_delete_entry(domain->sysdb, override_dn, true);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sysdb_delete_entry() failed.\n");
+ goto done;
+ }
+
+ msg = ldb_msg_new(tmp_ctx);
+ if (msg == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ msg->dn = talloc_steal(msg, ldb_dn);
+ if (msg->dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_empty(msg, SYSDB_OVERRIDE_DN, LDB_FLAG_MOD_DELETE, NULL);
+ if (ret != LDB_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_empty() failed\n");
+ ret = sss_ldb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = ldb_modify(ldb, msg);
+ if (ret != LDB_SUCCESS && ret != LDB_ERR_NO_SUCH_ATTRIBUTE) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "ldb_modify() failed: [%s](%d)[%s]\n",
+ ldb_strerror(ret), ret, ldb_errstring(ldb));
+ ret = sss_ldb_error_to_errno(ret);
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(domain->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to commit transaction\n");
+ goto done;
+ }
+ in_transaction = false;
+
+ ret = EOK;
+
+done:
+ if (in_transaction) {
+ sret = sysdb_transaction_cancel(domain->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not cancel transaction\n");
+ }
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t append_name(struct sss_domain_info *domain,
+ struct ldb_message *override)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb);
+ struct ldb_dn *dn;
+ struct ldb_message **msgs;
+ const char *attrs[] = {SYSDB_NAME, NULL};
+ const char *name;
+ const char *fqname;
+ size_t count;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ dn = ldb_msg_find_attr_as_dn(ldb, tmp_ctx, override,
+ SYSDB_OVERRIDE_OBJECT_DN);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing overrideObjectDN?\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_BASE,
+ NULL, attrs, &count, &msgs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry() failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ } else if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "More than one user found?\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ name = ldb_msg_find_attr_as_string(msgs[0], SYSDB_NAME, NULL);
+ if (name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Object with no name?\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ fqname = get_fqname(tmp_ctx, domain, name);
+ if (fqname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get fqname\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ldb_msg_add_string(override, ORIGNAME, fqname);
+ if (ret != LDB_SUCCESS) {
+ ret = sss_ldb_error_to_errno(ret);
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to add attribute to msg\n");
+ goto done;
+ }
+
+ talloc_steal(override, fqname);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t list_overrides(TALLOC_CTX *mem_ctx,
+ const char *base_filter,
+ const char *ext_filter,
+ const char **attrs,
+ struct sss_domain_info *domain,
+ size_t *_count,
+ struct ldb_message ***_msgs)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn;
+ struct ldb_context *ldb = sysdb_ctx_get_ldb(domain->sysdb);
+ size_t count;
+ struct ldb_message **msgs;
+ const char *filter;
+ size_t i;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ filter = base_filter;
+ if (ext_filter != NULL) {
+ filter = talloc_asprintf(tmp_ctx, "(&%s%s)", filter, ext_filter);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* Acquire list of override objects. */
+ dn = ldb_dn_new_fmt(tmp_ctx, ldb, SYSDB_TMPL_VIEW_SEARCH_BASE, LOCALVIEW);
+ if (dn == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "ldb_dn_new_fmt() failed.\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ *_msgs = NULL;
+ *_count = 0;
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb_search_entry() failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ /* Amend messages with original name. */
+ for (i = 0; i < count; i++) {
+ ret = append_name(domain, msgs[i]);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to append name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ }
+
+ *_msgs = talloc_steal(mem_ctx, msgs);
+ *_count = count;
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static struct override_user *
+list_user_overrides(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_user *objs = NULL;
+ struct ldb_message **msgs;
+ size_t count;
+ size_t i;
+ errno_t ret;
+ const char *attrs[] = SYSDB_PW_ATTRS;
+ struct ldb_message_element *el;
+ const char *fqname;
+ char *name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return NULL;
+ }
+
+ ret = list_overrides(tmp_ctx, "(objectClass=" SYSDB_OVERRIDE_USER_CLASS ")",
+ filter, attrs, domain, &count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ objs = talloc_zero_array(tmp_ctx, struct override_user, count + 1);
+ if (objs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ objs[i].orig_name = ldb_msg_find_attr_as_string(msgs[i], ORIGNAME,
+ NULL);
+ if (objs[i].orig_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing name?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+ if (fqname != NULL) {
+ ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
+ if (ret != EOK) {
+ ret = ERR_WRONG_NAME_FORMAT;
+ goto done;
+ }
+ objs[i].name = talloc_steal(objs, name);
+ }
+
+ objs[i].uid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_UIDNUM, 0);
+ objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
+ objs[i].home = ldb_msg_find_attr_as_string(msgs[i], SYSDB_HOMEDIR, NULL);
+ objs[i].shell = ldb_msg_find_attr_as_string(msgs[i], SYSDB_SHELL, NULL);
+ objs[i].gecos = ldb_msg_find_attr_as_string(msgs[i], SYSDB_GECOS, NULL);
+
+ el = ldb_msg_find_element(msgs[i], SYSDB_USER_CERT);
+ if (el != NULL && el->num_values > 0) {
+ /* Currently we support only 1 certificate override */
+ objs[i].cert = sss_base64_encode(objs, el->values[0].data,
+ el->values[0].length);
+ if (objs[i].cert == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_base64_encode failed.\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ } else {
+ objs[i].cert = NULL;
+ }
+
+ talloc_steal(objs, objs[i].orig_name);
+ talloc_steal(objs, objs[i].home);
+ talloc_steal(objs, objs[i].shell);
+ talloc_steal(objs, objs[i].gecos);
+ }
+
+ talloc_steal(mem_ctx, objs);
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return objs;
+}
+
+static struct override_group *
+list_group_overrides(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_group *objs = NULL;
+ struct ldb_message **msgs;
+ size_t count;
+ size_t i;
+ errno_t ret;
+ const char *attrs[] = SYSDB_GRSRC_ATTRS;
+ const char *fqname;
+ char *name;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return NULL;
+ }
+
+ ret = list_overrides(tmp_ctx, "(objectClass=" SYSDB_OVERRIDE_GROUP_CLASS ")",
+ filter, attrs, domain, &count, &msgs);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ objs = talloc_zero_array(tmp_ctx, struct override_group, count + 1);
+ if (objs == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < count; i++) {
+ objs[i].orig_name = ldb_msg_find_attr_as_string(msgs[i], ORIGNAME,
+ NULL);
+ if (objs[i].orig_name == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing name?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+ talloc_steal(objs, objs[i].orig_name);
+
+ fqname = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+ if (fqname != NULL) {
+ ret = sss_parse_internal_fqname(tmp_ctx, fqname, &name, NULL);
+ if (ret != EOK) {
+ ret = ERR_WRONG_NAME_FORMAT;
+ goto done;
+ }
+ objs[i].name = talloc_steal(objs, name);
+ }
+
+ objs[i].gid = ldb_msg_find_attr_as_uint(msgs[i], SYSDB_GIDNUM, 0);
+ }
+
+ talloc_steal(mem_ctx, objs);
+
+done:
+ talloc_free(tmp_ctx);
+
+ if (ret != EOK) {
+ return NULL;
+ }
+
+ return objs;
+}
+
+static errno_t user_export(const char *filename,
+ struct sss_domain_info *dom,
+ bool iterate,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_colondb *db;
+ struct override_user *objs;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE, filename);
+ if (db == NULL) {
+ ERROR("Unable to open %s.\n",
+ filename == NULL ? "stdout" : filename);
+ ret = EIO;
+ goto done;
+ }
+
+ do {
+ objs = list_user_overrides(tmp_ctx, dom, filter);
+ if (objs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; objs[i].orig_name != NULL; i++) {
+ /**
+ * Format: orig_name:name:uid:gid:gecos:home:shell:certificate
+ */
+ struct sss_colondb_write_field table[] = {
+ {SSS_COLONDB_STRING, {.str = objs[i].orig_name}},
+ {SSS_COLONDB_STRING, {.str = objs[i].name}},
+ {SSS_COLONDB_UINT32, {.uint32 = objs[i].uid}},
+ {SSS_COLONDB_UINT32, {.uint32 = objs[i].gid}},
+ {SSS_COLONDB_STRING, {.str = objs[i].gecos}},
+ {SSS_COLONDB_STRING, {.str = objs[i].home}},
+ {SSS_COLONDB_STRING, {.str = objs[i].shell}},
+ {SSS_COLONDB_STRING, {.str = objs[i].cert}},
+ {SSS_COLONDB_SENTINEL, {0}}
+ };
+
+ ret = sss_colondb_writeline(db, table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write line to db\n");
+ goto done;
+ }
+ }
+
+ /* All overrides are under the same subtree, so we don't want to
+ * descent into subdomains. */
+ dom = get_next_domain(dom, false);
+ } while (dom != NULL && iterate);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t group_export(const char *filename,
+ struct sss_domain_info *dom,
+ bool iterate,
+ const char *filter)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_colondb *db;
+ struct override_group *objs;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+
+ db = sss_colondb_open(tmp_ctx, SSS_COLONDB_WRITE, filename);
+ if (db == NULL) {
+ ERROR("Unable to open %s.\n",
+ filename == NULL ? "stdout" : filename);
+ ret = EIO;
+ goto done;
+ }
+
+ do {
+ objs = list_group_overrides(tmp_ctx, dom, filter);
+ if (objs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get override objects\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; objs[i].orig_name != NULL; i++) {
+ /**
+ * Format: orig_name:name:gid
+ */
+ struct sss_colondb_write_field table[] = {
+ {SSS_COLONDB_STRING, {.str = objs[i].orig_name}},
+ {SSS_COLONDB_STRING, {.str = objs[i].name}},
+ {SSS_COLONDB_UINT32, {.uint32 = objs[i].gid}},
+ {SSS_COLONDB_SENTINEL, {0}}
+ };
+
+ ret = sss_colondb_writeline(db, table);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to write line to db\n");
+ goto done;
+ }
+ }
+
+ /* All overrides are under the same subtree, so we don't want to
+ * descent into subdomains. */
+ dom = get_next_domain(dom, false);
+ } while (dom != NULL && iterate);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int override_user_add(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct override_user user = {NULL};
+ errno_t ret;
+
+ ret = parse_cmdline_user_add(cmdline, tool_ctx, &user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_user_domain_msg(tool_ctx, &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_user(tool_ctx, &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(user.input_name));
+
+ return ret;
+}
+
+static int override_user_del(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct override_user user = {NULL};
+ errno_t ret;
+
+ ret = parse_cmdline_user_del(cmdline, tool_ctx, &user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_user_domain_msg(tool_ctx, &user);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_object_del(user.domain, SYSDB_MEMBER_USER, user.sysdb_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(user.input_name));
+
+ return ret;
+}
+
+static int override_user_find(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sss_domain_info *dom;
+ bool iterate;
+ errno_t ret;
+
+ ret = parse_cmdline_find(cmdline, tool_ctx, &dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ return ret;
+ }
+
+ if (dom == NULL) {
+ dom = tool_ctx->domains;
+ iterate = true;
+ } else {
+ iterate = false;
+ }
+
+ ret = user_export(NULL, dom, iterate, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static int override_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_user input = {NULL};
+ const char *dn;
+ char *anchor;
+ const char *filter;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ ret = parse_cmdline_user_show(cmdline, tool_ctx, &input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_user_domain_msg(tool_ctx, &input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object domain\n");
+ goto done;
+ }
+
+ ret = get_object_dn(tmp_ctx, input.domain, SYSDB_MEMBER_USER,
+ input.sysdb_name, NULL, &dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object dn\n");
+ goto done;
+ }
+
+ anchor = build_anchor(tmp_ctx, dn);
+ if (anchor == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, anchor, &anchor);
+ if (ret != EOK) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
+ SYSDB_OVERRIDE_ANCHOR_UUID, anchor);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = user_export(NULL, input.domain, false, filter);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(input.input_name));
+
+ return ret;
+}
+
+static int override_user_import(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_colondb *db;
+ const char *filename = NULL;
+ struct override_user obj = {0};
+ int linenum = 1;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return EXIT_FAILURE;
+ }
+
+ /**
+ * Format: orig_name:name:uid:gid:gecos:home:shell:certificate
+ */
+ struct sss_colondb_read_field table[] = {
+ {SSS_COLONDB_STRING, {.str = &obj.input_name}},
+ {SSS_COLONDB_STRING, {.str = &obj.name}},
+ {SSS_COLONDB_UINT32, {.uint32 = &obj.uid}},
+ {SSS_COLONDB_UINT32, {.uint32 = &obj.gid}},
+ {SSS_COLONDB_STRING, {.str = &obj.gecos}},
+ {SSS_COLONDB_STRING, {.str = &obj.home}},
+ {SSS_COLONDB_STRING, {.str = &obj.shell}},
+ {SSS_COLONDB_STRING, {.str = &obj.cert}},
+ {SSS_COLONDB_SENTINEL, {0}}
+ };
+
+ ret = parse_cmdline_import(cmdline, &filename);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename);
+ if (db == NULL) {
+ ERROR("Unable to open %s.\n", filename);
+ ret = EIO;
+ goto done;
+ }
+
+ while ((ret = sss_colondb_readline(tmp_ctx, db, table)) == EOK) {
+ linenum++;
+
+ ret = sss_tool_parse_name(tool_ctx, tool_ctx, obj.input_name,
+ &obj.orig_name, &obj.domain);
+ if (ret != EOK) {
+ ERROR("Unable to parse name %s.\n", obj.input_name);
+ goto done;
+ }
+
+ ret = get_user_domain_msg(tool_ctx, &obj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_user(tool_ctx, &obj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ talloc_free_children(tmp_ctx);
+ }
+
+ if (ret != EOF) {
+ ERROR("Invalid format on line %d. "
+ "Use --debug option for more information.\n", linenum);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(filename));
+
+ return ret;
+}
+
+static int override_user_export(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *filename = NULL;
+ errno_t ret;
+
+ ret = parse_cmdline_export(cmdline, &filename);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = user_export(filename, tool_ctx->domains, true, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export users\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(filename));
+
+ return ret;
+}
+
+static int override_group_add(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct override_group group = {NULL};
+ errno_t ret;
+
+ ret = parse_cmdline_group_add(cmdline, tool_ctx, &group);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_group_domain_msg(tool_ctx, &group);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_group(tool_ctx, &group);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(group.input_name));
+
+ return ret;
+}
+
+static int override_group_del(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct override_group group = {NULL};
+ errno_t ret;
+
+ ret = parse_cmdline_group_del(cmdline, tool_ctx, &group);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_group_domain_msg(tool_ctx, &group);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_object_del(group.domain, SYSDB_MEMBER_GROUP,
+ group.sysdb_name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to delete override object.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(group.input_name));
+
+ return ret;
+}
+
+static int override_group_find(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sss_domain_info *dom;
+ bool iterate;
+ errno_t ret;
+
+ ret = parse_cmdline_find(cmdline, tool_ctx, &dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ return ret;
+ }
+
+ if (dom == NULL) {
+ dom = tool_ctx->domains;
+ iterate = true;
+ } else {
+ iterate = false;
+ }
+
+ ret = group_export(NULL, dom, iterate, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n");
+ return ret;
+ }
+
+ return EOK;
+}
+
+static int override_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct override_group input = {NULL};
+ const char *dn;
+ char *anchor;
+ const char *filter;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ ret = parse_cmdline_group_show(cmdline, tool_ctx, &input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = get_group_domain_msg(tool_ctx, &input);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object domain\n");
+ goto done;
+ }
+
+ ret = get_object_dn(tmp_ctx, input.domain, SYSDB_MEMBER_GROUP,
+ input.sysdb_name, NULL, &dn);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get object dn\n");
+ goto done;
+ }
+
+ anchor = build_anchor(tmp_ctx, dn);
+ if (anchor == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, anchor, &anchor);
+ if (ret != EOK) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(%s=%s)",
+ SYSDB_OVERRIDE_ANCHOR_UUID, anchor);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = group_export(NULL, input.domain, false, filter);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(input.input_name));
+
+ return ret;
+}
+
+static int override_group_import(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_colondb *db;
+ const char *filename = NULL;
+ struct override_group obj = {0};
+ int linenum = 1;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ /**
+ * Format: orig_name:name:gid
+ */
+ struct sss_colondb_read_field table[] = {
+ {SSS_COLONDB_STRING, {.str = &obj.input_name}},
+ {SSS_COLONDB_STRING, {.str = &obj.name}},
+ {SSS_COLONDB_UINT32, {.uint32 = &obj.gid}},
+ {SSS_COLONDB_SENTINEL, {0}}
+ };
+
+ ret = parse_cmdline_import(cmdline, &filename);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ db = sss_colondb_open(tool_ctx, SSS_COLONDB_READ, filename);
+ if (db == NULL) {
+ ERROR("Unable to open %s.\n", filename);
+ ret = EIO;
+ goto done;
+ }
+
+ while ((ret = sss_colondb_readline(tmp_ctx, db, table)) == EOK) {
+ linenum++;
+
+ ret = sss_tool_parse_name(tool_ctx, tool_ctx, obj.input_name,
+ &obj.orig_name, &obj.domain);
+ if (ret != EOK) {
+ ERROR("Unable to parse name %s.\n", obj.input_name);
+ goto done;
+ }
+
+ ret = get_group_domain_msg(tool_ctx, &obj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = override_group(tool_ctx, &obj);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ talloc_free_children(tmp_ctx);
+ }
+
+ if (ret != EOF) {
+ ERROR("Invalid format on line %d. "
+ "Use --debug option for more information.\n", linenum);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(filename));
+
+ return ret;
+}
+
+static int override_group_export(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *filename = NULL;
+ errno_t ret;
+
+ ret = parse_cmdline_export(cmdline, &filename);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command line.\n");
+ goto done;
+ }
+
+ ret = group_export(filename, tool_ctx->domains, true, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to export groups\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(filename));
+
+ return ret;
+}
+
+int main(int argc, const char **argv)
+{
+ struct sss_route_cmd commands[] = {
+ SSS_TOOL_COMMAND_NOMSG("user-add", 0, override_user_add),
+ SSS_TOOL_COMMAND_NOMSG("user-del", 0, override_user_del),
+ SSS_TOOL_COMMAND_NOMSG("user-find", 0, override_user_find),
+ SSS_TOOL_COMMAND_NOMSG("user-show", 0, override_user_show),
+ SSS_TOOL_COMMAND_NOMSG("user-import", 0, override_user_import),
+ SSS_TOOL_COMMAND_NOMSG("user-export", 0, override_user_export),
+ SSS_TOOL_COMMAND_NOMSG("group-add", 0, override_group_add),
+ SSS_TOOL_COMMAND_NOMSG("group-del", 0, override_group_del),
+ SSS_TOOL_COMMAND_NOMSG("group-find", 0, override_group_find),
+ SSS_TOOL_COMMAND_NOMSG("group-show", 0, override_group_show),
+ SSS_TOOL_COMMAND_NOMSG("group-import", 0, override_group_import),
+ SSS_TOOL_COMMAND_NOMSG("group-export", 0, override_group_export),
+ SSS_TOOL_LAST
+ };
+
+ return sss_tool_main(argc, argv, commands, NULL);
+}
diff --git a/src/tools/sss_seed.c b/src/tools/sss_seed.c
new file mode 100644
index 0000000..07327da
--- /dev/null
+++ b/src/tools/sss_seed.c
@@ -0,0 +1,896 @@
+/*
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <talloc.h>
+#include <popt.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "tools/tools_util.h"
+#include "confdb/confdb.h"
+
+#ifndef BUFSIZE
+#define BUFSIZE 1024
+#endif
+
+#ifndef PASS_MAX
+#define PASS_MAX 64
+#endif
+
+enum seed_pass_method {
+ PASS_PROMPT,
+ PASS_FILE
+};
+
+struct user_ctx {
+ char *domain_name;
+
+ char *name;
+ uid_t uid;
+ gid_t gid;
+ char *gecos;
+ char *home;
+ char *shell;
+
+ char *password;
+};
+
+struct seed_ctx {
+ struct confdb_ctx *confdb;
+ struct sss_domain_info *domain;
+ struct sysdb_ctx *sysdb;
+
+ struct user_ctx *uctx;
+
+ char *password_file;
+ enum seed_pass_method password_method;
+
+ bool interact;
+ bool user_cached;
+};
+
+
+static int seed_prompt(const char *req)
+{
+ ssize_t len = 0;
+ size_t i = 0;
+ char *prompt = NULL;
+ int ret = EOK;
+
+ prompt = talloc_asprintf(NULL, _("Enter %s:"), req);
+ if (prompt == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ while (prompt[i] != '\0') {
+ errno = 0;
+ len = sss_atomic_write_s(STDOUT_FILENO, &prompt[i++], 1);
+ if (len == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+done:
+ talloc_free(prompt);
+ return ret;
+}
+
+static int seed_str_input(TALLOC_CTX *mem_ctx,
+ const char *req,
+ char **_input)
+{
+ char buf[BUFSIZE+1];
+ size_t len = 0;
+ size_t bytes_read = 0;
+ int ret = EOK;
+
+ ret = seed_prompt(req);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ errno = 0;
+ while ((bytes_read = sss_atomic_read_s(STDIN_FILENO, buf+len, 1)) != 0) {
+ if (bytes_read == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n",
+ ret, strerror(ret));
+ return ret;
+ }
+ if (buf[len] == '\n' || len == BUFSIZE) {
+ buf[len] = '\0';
+ break;
+ }
+ len += bytes_read;
+ }
+
+ *_input = talloc_strdup(mem_ctx, buf);
+ if (*_input == NULL) {
+ ret = ENOMEM;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate input\n");
+ }
+
+ return ret;
+}
+
+static int seed_id_input(const char *req,
+ uid_t *_id_input)
+{
+ char buf[BUFSIZE+1];
+ size_t len = 0;
+ size_t bytes_read = 0;
+ char *endptr = NULL;
+ int ret = EOK;
+
+ ret = seed_prompt(req);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ errno = 0;
+ while ((bytes_read = sss_atomic_read_s(STDIN_FILENO, buf+len, 1)) != 0) {
+ if (bytes_read == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n",
+ ret, strerror(ret));
+ return ret;
+ }
+ if (buf[len] == '\n' || len == BUFSIZE) {
+ buf[len] = '\0';
+ break;
+ }
+ len += bytes_read;
+ }
+
+ if (isdigit(*buf)) {
+ errno = 0;
+ *_id_input = (uid_t)strtoll(buf, &endptr, 10);
+ if (errno != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE, "strtoll failed on [%s]: [%d][%s].\n",
+ (char *)buf, ret, strerror(ret));
+ return ret;
+ }
+ if (*endptr != '\0') {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "extra characters [%s] after ID [%"SPRIuid"]\n",
+ endptr, *_id_input);
+ }
+ } else {
+ ret = EINVAL;
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get %s input.\n", req);
+ }
+
+ return ret;
+}
+
+static int seed_password_input_prompt(TALLOC_CTX *mem_ctx, char **_password)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *password = NULL;
+ char *temp = NULL;
+ int ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate temp context\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ temp = getpass("Enter temporary password:");
+ if (temp == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get prompted password\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* Do not allow empty passwords */
+ if (strlen(temp) == 0) {
+ ERROR("Empty passwords are not allowed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ password = talloc_strdup(tmp_ctx, temp);
+ sss_erase_mem_securely(temp, strlen(temp));
+ if (password == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *)password,
+ sss_erase_talloc_mem_securely);
+
+ temp = getpass("Enter temporary password again:");
+ if (temp == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get prompted password\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strncmp(temp,password,strlen(password)) != 0) {
+ ERROR("Passwords do not match\n");
+ DEBUG(SSSDBG_MINOR_FAILURE, "Provided passwords do not match\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ *_password = talloc_steal(mem_ctx, password);
+
+done:
+ talloc_free(tmp_ctx);
+ if (temp != NULL) {
+ sss_erase_mem_securely(temp, strlen(temp));
+ }
+ return ret;
+}
+
+static int seed_password_input_file(TALLOC_CTX *mem_ctx,
+ char *filename,
+ char **_password)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *password = NULL;
+ int len = 0;
+ uint8_t buf[PASS_MAX+1];
+ int fd = -1;
+ int ret = EOK;
+ int valid_i;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not allocate temp context\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to open password file "
+ "[%s] [%d][%s]\n",
+ filename, errno, strerror(errno));
+ ret = EINVAL;
+ goto done;
+ }
+
+ errno = 0;
+ len = sss_atomic_read_s(fd, buf, PASS_MAX + 1);
+ if (len == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to read password from file "
+ "[%s] [%d][%s]\n",
+ filename, ret, strerror(ret));
+ close(fd);
+ goto done;
+ }
+
+ close(fd);
+
+ if (len > PASS_MAX) {
+ ERROR("Password file too big.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ buf[len] = '\0';
+
+ /* Only the first line is valid (without '\n'). */
+ for (valid_i = -1; valid_i + 1 < len; valid_i++) {
+ if (buf[valid_i + 1] == '\n') {
+ buf[valid_i + 1] = '\0';
+ break;
+ }
+ }
+
+ /* Do not allow empty passwords. */
+ if (valid_i < 0) {
+ ERROR("Empty passwords are not allowed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ /* valid_i is the last valid index of the password followed by \0.
+ * If characters other than \n occur int the rest of the file, it
+ * is an error. */
+ for (i = valid_i + 2; i < len; i++) {
+ if (buf[i] != '\n') {
+ ERROR("Multi-line passwords are not allowed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ password = talloc_strdup(tmp_ctx, (char *)buf);
+ if (password == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *)password,
+ sss_erase_talloc_mem_securely);
+
+ *_password = talloc_steal(mem_ctx, password);
+
+done:
+ talloc_free(tmp_ctx);
+ sss_erase_mem_securely(buf, sizeof(buf));
+ return ret;
+}
+
+static int seed_interactive_input(TALLOC_CTX *mem_ctx,
+ struct user_ctx *uctx,
+ struct user_ctx **_uctx)
+{
+ struct user_ctx *input_uctx = NULL;
+ int ret = EOK;
+
+ input_uctx = talloc_zero(NULL, struct user_ctx);
+ if (input_uctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (uctx->name == NULL) {
+ ret = seed_str_input(input_uctx, _("username"), &input_uctx->name);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Username interactive input failed.\n");
+ goto done;
+ }
+ } else {
+ input_uctx->name = talloc_strdup(input_uctx, uctx->name);
+ if (input_uctx->name == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (uctx->uid == 0) {
+ ret = seed_id_input(_("UID"), &input_uctx->uid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "UID interactive input failed.\n");
+ goto done;
+ }
+ } else {
+ input_uctx->uid = uctx->uid;
+ }
+
+ if (uctx->gid == 0) {
+ ret = seed_id_input(_("GID"), &input_uctx->gid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "GID interactive input failed.\n");
+ goto done;
+ }
+ } else {
+ input_uctx->gid = uctx->gid;
+ }
+
+ if (uctx->gecos == NULL) {
+ ret = seed_str_input(input_uctx, _("user comment (gecos)"),
+ &input_uctx->gecos);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Gecos interactive input failed.\n");
+ goto done;
+ }
+ } else {
+ input_uctx->gecos = talloc_strdup(input_uctx, uctx->gecos);
+ if (input_uctx->gecos == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (uctx->home == NULL) {
+ ret = seed_str_input(input_uctx, _("home directory"),
+ &input_uctx->home);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Home directory interactive input fialed.\n");
+ goto done;
+ }
+ } else {
+ input_uctx->home = talloc_strdup(input_uctx, uctx->home);
+ if (input_uctx->home == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ if (uctx->shell == NULL) {
+ ret = seed_str_input(input_uctx, _("user login shell"),
+ &input_uctx->shell);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Shell interactive input failed\n");
+ goto done;
+ }
+ } else {
+ input_uctx->shell = talloc_strdup(input_uctx, uctx->shell);
+ if (input_uctx->shell == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+done:
+ if (ret == EOK) {
+ *_uctx = talloc_steal(mem_ctx, input_uctx);
+ } else {
+ ERROR("Interactive input failed.\n");
+ talloc_zfree(input_uctx);
+ }
+ return ret;
+}
+
+static int seed_init(TALLOC_CTX *mem_ctx,
+ const int argc,
+ const char **argv,
+ struct seed_ctx **_sctx)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ int pc_debug = SSSDBG_TOOLS_DEFAULT;
+ const char *pc_domain = NULL;
+ const char *pc_name = NULL;
+ uid_t pc_uid = 0;
+ gid_t pc_gid = 0;
+ const char *pc_gecos = NULL;
+ const char *pc_home = NULL;
+ const char *pc_shell = NULL;
+ const char *pc_password_file = NULL;
+
+ struct seed_ctx *sctx = NULL;
+
+ int ret = EOK;
+
+ poptContext pc = NULL;
+ struct poptOption options[] = {
+ POPT_AUTOHELP
+ { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &pc_debug, 0,
+ _("The debug level to run with"), NULL },
+ { "domain", 'D', POPT_ARG_STRING, &pc_domain, 0, _("Domain"), NULL },
+ { "username", 'n', POPT_ARG_STRING, &pc_name, 0, _("Username"), NULL},
+ { "uid", 'u', POPT_ARG_INT, &pc_uid, 0, _("User UID"), NULL },
+ { "gid", 'g', POPT_ARG_INT, &pc_gid, 0, _("User GID"), NULL },
+ { "gecos", 'c', POPT_ARG_STRING, &pc_gecos, 0,
+ _("Comment string"), NULL},
+ { "home", 'h', POPT_ARG_STRING, &pc_home, 0,
+ _("Home directory"), NULL },
+ { "shell", 's', POPT_ARG_STRING, &pc_shell, 0, _("Login Shell"), NULL },
+ { "interactive", 'i', POPT_ARG_NONE, NULL, 'i',
+ _("Use interactive mode to enter user data"), NULL },
+ { "password-file", 'p', POPT_ARG_STRING, &pc_password_file, 0,
+ _("File from which user's password is read "
+ "(default is to prompt for password)"),NULL },
+ POPT_TABLEEND
+ };
+
+ /* init contexts */
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ sctx = talloc_zero(tmp_ctx, struct seed_ctx);
+ if (sctx == NULL) {
+ ERROR("Could not allocate tools context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ sctx->uctx = talloc_zero(sctx, struct user_ctx);
+ if (sctx->uctx == NULL) {
+ ERROR("Could not allocate user data context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ debug_prg_name = argv[0];
+ ret = set_locale();
+ if (ret != EOK) {
+ ERROR("set_locale failed (%d): %s\n", ret, strerror(ret));
+ ret = EINVAL;
+ goto fini;
+ }
+
+ /* parse arguments */
+ pc = poptGetContext(NULL, argc, argv, options, 0);
+ if (argc < 2) {
+ poptPrintUsage(pc,stderr,0);
+ ret = EINVAL;
+ goto fini;
+ }
+
+ poptSetOtherOptionHelp(pc, "[OPTIONS] -D <domain> -n <username>");
+ while ((ret = poptGetNextOpt(pc)) > 0) {
+ switch (ret) {
+ case 'i':
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Interactive mode selected\n");
+ sctx->interact = true;
+ break;
+ }
+ }
+
+ if (ret != -1) {
+ BAD_POPT_PARAMS(pc, poptStrerror(ret), ret, fini);
+ }
+
+ DEBUG_CLI_INIT(pc_debug);
+
+ CHECK_ROOT(ret, argv[0]);
+
+ /* check username provided */
+ if (pc_name == NULL) {
+ BAD_POPT_PARAMS(pc, _("Username must be specified\n"), ret, fini);
+ }
+
+ /* check domain is provided */
+ if (pc_domain == NULL) {
+ BAD_POPT_PARAMS(pc, _("Domain must be specified.\n"), ret, fini);
+ }
+
+ sctx->uctx->domain_name = talloc_strdup(sctx->uctx, pc_domain);
+ if (sctx->uctx->domain_name == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ sctx->uctx->name = sss_create_internal_fqname(sctx->uctx,
+ pc_name, pc_domain);
+ if (sctx->uctx->name == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ poptFreeContext(pc);
+
+ ret = EOK;
+
+ /* copy all information provided from popt */
+ sctx->uctx->uid = pc_uid;
+ sctx->uctx->gid = pc_gid;
+ if (pc_gecos != NULL) {
+ sctx->uctx->gecos = talloc_strdup(sctx->uctx, pc_gecos);
+ if (sctx->uctx->gecos == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+ }
+ if (pc_home != NULL) {
+ sctx->uctx->home = talloc_strdup(sctx->uctx, pc_home);
+ if (sctx->uctx->home == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+ }
+ if (pc_shell != NULL) {
+ sctx->uctx->shell = talloc_strdup(sctx->uctx, pc_shell);
+ if (sctx->uctx->shell == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+ }
+
+ /* check if password file provided */
+ if (pc_password_file != NULL) {
+ sctx->password_file = talloc_strdup(sctx, pc_password_file);
+ if (sctx->password_file == NULL) {
+ ret = ENOMEM;
+ goto fini;
+ }
+ sctx->password_method = PASS_FILE;
+ } else {
+ sctx->password_method = PASS_PROMPT;
+ }
+
+ *_sctx = talloc_steal(mem_ctx, sctx);
+
+fini:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int seed_init_db(TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ struct confdb_ctx **_confdb,
+ struct sss_domain_info **_domain,
+ struct sysdb_ctx **_sysdb)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *confdb_path = NULL;
+ struct confdb_ctx *confdb = NULL;
+ struct sss_domain_info *domain = NULL;
+ int ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ /* setup confdb */
+ confdb_path = talloc_asprintf(tmp_ctx, "%s/%s", DB_PATH, CONFDB_FILE);
+ if (confdb_path == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = confdb_init(tmp_ctx, &confdb, confdb_path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to the confdb\n");
+ ERROR("Could not initialize connection to the confdb\n");
+ goto done;
+ }
+
+ ret = sssd_domain_init(tmp_ctx, confdb, domain_name, DB_PATH, &domain);
+ if (ret != EOK) {
+ SYSDB_VERSION_ERROR(ret);
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not initialize connection to domain '%s' in sysdb.%s\n",
+ domain_name, ret == ENOENT ? " Domain not found." : "");
+ ERROR("Could not initialize connection to domain '%1$s' in sysdb.%2$s\n",
+ domain_name, ret == ENOENT ? " Domain not found." : "");
+
+ goto done;
+ }
+
+ *_confdb = talloc_steal(mem_ctx, confdb);
+ *_domain = domain;
+ *_sysdb = domain->sysdb;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int seed_domain_user_info(const char *name,
+ struct sss_domain_info *domain,
+ bool *is_cached)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct passwd *passwd = NULL;
+ struct ldb_result *res = NULL;
+ int ret = EOK;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ errno = 0;
+ passwd = getpwnam(name);
+ if (passwd == NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "getpwnam failed [%d] [%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* look for user in cache */
+ ret = sysdb_getpwnam(tmp_ctx, domain, name, &res);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Couldn't lookup user (%s) in the cache\n", name);
+ goto done;
+ }
+
+ if (res->count == 0) {
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "User (%s) wasn't found in the cache\n", name);
+ *is_cached = false;
+ ret = ENOENT;
+ goto done;
+ } else if (res->count > 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Multiple user (%s) entries were found in the cache\n", name);
+ ret = EINVAL;
+ goto done;
+ } else {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "User found in cache\n");
+ *is_cached = true;
+
+ errno = 0;
+ ret = initgroups(name, passwd->pw_gid);
+ if (ret != EOK) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "initgroups failed [%d] [%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ }
+
+done:
+ if (ret == ENOMEM) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate user information\n");
+ }
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static int seed_cache_user(struct seed_ctx *sctx)
+{
+ bool in_transaction = false;
+ int ret = EOK;
+ errno_t sret;
+
+ ret = sysdb_transaction_start(sctx->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb transaction start failure\n");
+ goto done;
+ }
+
+ in_transaction = true;
+
+ if (sctx->user_cached == false) {
+ ret = sysdb_add_user(sctx->domain, sctx->uctx->name,
+ sctx->uctx->uid, sctx->uctx->gid,
+ sctx->uctx->gecos, sctx->uctx->home,
+ sctx->uctx->shell, NULL, NULL, 0, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to add user to the cache. (%d)[%s]\n",
+ ret, strerror(ret));
+ ERROR("Failed to create user cache entry\n");
+ goto done;
+ }
+ }
+
+ ret = sysdb_cache_password(sctx->domain, sctx->uctx->name,
+ sctx->uctx->password);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password. (%d)[%s]\n",
+ ret, strerror(ret));
+ ERROR("Failed to cache password\n");
+ goto done;
+ }
+
+ ret = sysdb_transaction_commit(sctx->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sysdb transaction commit failure\n");
+ goto done;
+ }
+
+ in_transaction = false;
+
+done:
+ if (in_transaction == true) {
+ sret = sysdb_transaction_cancel(sctx->sysdb);
+ if (sret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to cancel transaction\n");
+ }
+ }
+
+ return ret;
+}
+
+int main(int argc, const char **argv)
+{
+ struct seed_ctx *sctx = NULL;
+ struct user_ctx *input_uctx = NULL;
+ int ret = EOK;
+
+ /* initialize seed context and parse options */
+ ret = seed_init(sctx, argc, argv, &sctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,"Seed init failed [%d][%s]\n",
+ ret, strerror(ret));
+ goto done;
+ }
+
+ /* set up confdb,sysdb and domain */
+ ret = seed_init_db(sctx, sctx->uctx->domain_name, &sctx->confdb,
+ &sctx->domain, &sctx->sysdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize db and domain\n");
+ goto done;
+ }
+
+ /* get user info from domain */
+ ret = seed_domain_user_info(sctx->uctx->name,
+ sctx->domain, &sctx->user_cached);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed lookup of user [%s] in domain [%s]\n",
+ sctx->uctx->name, sctx->uctx->domain_name);
+ }
+
+ /* interactive mode to fill in user information */
+ if (sctx->interact == true) {
+ if (sctx->user_cached == true) {
+ ERROR("User entry already exists in the cache.\n");
+ ret = EEXIST;
+ goto done;
+ } else {
+ ret = seed_interactive_input(sctx, sctx->uctx, &input_uctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to get seed input.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ talloc_zfree(sctx->uctx);
+ sctx->uctx = input_uctx;
+ }
+ }
+
+ if (sctx->user_cached == false) {
+ if (sctx->uctx->uid == 0 || sctx->uctx->gid == 0) {
+ /* require username, UID, and GID to continue */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Not enough information provided\n");
+ ERROR("UID and primary GID not provided.\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ /* password input */
+ if (sctx->password_method == PASS_FILE) {
+ ret = seed_password_input_file(sctx->uctx, sctx->password_file,
+ &sctx->uctx->password);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Password input failure\n");
+ goto done;
+ }
+ } else {
+ ret = seed_password_input_prompt(sctx->uctx, &sctx->uctx->password);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Password input failure\n");
+ goto done;
+ }
+ }
+
+ /* Add user info and password to sysdb cache */
+ ret = seed_cache_user(sctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Failed to modify cache.\n");
+ goto done;
+ } else {
+ if (sctx->user_cached == false) {
+ PRINT("User cache entry created for %1$s\n", sctx->uctx->name);
+ }
+ PRINT("Temporary password added to cache entry for %1$s\n",
+ sctx->uctx->name);
+ }
+
+done:
+ talloc_zfree(sctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Exit error: [%d] [%s]\n",
+ ret, strerror(ret));
+ ret = EXIT_FAILURE;
+ } else {
+ ret = EXIT_SUCCESS;
+ }
+ exit(ret);
+}
diff --git a/src/tools/sss_signal.c b/src/tools/sss_signal.c
new file mode 100644
index 0000000..c4d07f9
--- /dev/null
+++ b/src/tools/sss_signal.c
@@ -0,0 +1,38 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+
+ Copyright (C) 2014 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <signal.h>
+
+#include "config.h"
+#include "tools/common/sss_process.h"
+
+int main(int argc, const char **argv)
+{
+ int ret;
+
+ ret = sss_signal(SIGUSR2);
+ if (ret != EOK) {
+ ERROR("Could not signal SSSD. Is SSSD running?\n");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/src/tools/sssctl/sssctl.c b/src/tools/sssctl/sssctl.c
new file mode 100644
index 0000000..85d0d09
--- /dev/null
+++ b/src/tools/sssctl/sssctl.c
@@ -0,0 +1,354 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "util/util.h"
+#include "tools/sssctl/sssctl.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+
+static const char *
+sssctl_prompt_str(enum sssctl_prompt_result result)
+{
+ switch (result) {
+ case SSSCTL_PROMPT_YES:
+ return _("yes");
+ case SSSCTL_PROMPT_NO:
+ return _("no");
+ case SSSCTL_PROMPT_ERROR:
+ return _("error");
+ }
+
+ return _("Invalid result.");
+}
+
+enum sssctl_prompt_result
+sssctl_prompt(const char *message,
+ enum sssctl_prompt_result defval)
+{
+ char answer[255] = {0};
+ int c;
+ const char *yes = sssctl_prompt_str(SSSCTL_PROMPT_YES);
+ const char *no = sssctl_prompt_str(SSSCTL_PROMPT_NO);
+ int attempts = 0;
+ int ret;
+
+ do {
+ if (defval != SSSCTL_PROMPT_ERROR) {
+ printf("%s (%s/%s) [%s] ", message, yes, no,
+ sssctl_prompt_str(defval));
+
+ /* Detect empty line. */
+ c = getchar();
+ if (c == '\n') {
+ return defval;
+ } else {
+ ungetc(c, stdin);
+ }
+ } else {
+ printf("%s (%s/%s)", message, yes, no);
+ }
+
+ ret = scanf("%254s", answer);
+
+ /* Clear stdin. */
+ while ((c = getchar()) != '\n' && c != EOF);
+
+ if (ret != 1) {
+ ERROR("Unable to read user input\n");
+ return SSSCTL_PROMPT_ERROR;
+ }
+
+
+ if (strcasecmp(yes, answer) == 0) {
+ return SSSCTL_PROMPT_YES;
+ }
+
+ if (strcasecmp(no, answer) == 0) {
+ return SSSCTL_PROMPT_NO;
+ }
+
+ ERROR("Invalid input, please provide either "
+ "'%s' or '%s'.\n", yes, no);
+
+ attempts++;
+ } while (attempts < 3);
+
+ return SSSCTL_PROMPT_ERROR;
+}
+
+errno_t sssctl_wrap_command(const char *command,
+ const char *subcommand,
+ struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ if (subcommand != NULL) {
+ cmdline->argc++;
+ }
+
+ const char **args = talloc_array_size(tool_ctx,
+ sizeof(char *),
+ cmdline->argc + 2);
+ if (!args) {
+ return ENOMEM;
+ }
+
+ args[0] = command;
+
+ if (subcommand != NULL) {
+ args[1] = subcommand;
+ memcpy(&args[2], cmdline->argv, sizeof(char *) * cmdline->argc);
+ } else {
+ memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
+ }
+
+ args[cmdline->argc + 1] = NULL;
+
+ ret = sssctl_run_command(args);
+
+ talloc_free(args);
+
+ return ret;
+}
+
+errno_t sssctl_run_command(const char *const argv[])
+{
+ int ret;
+ int wstatus;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Running '%s'\n", argv[0]);
+
+ ret = fork();
+ if (ret == -1) {
+ ERROR("Error while executing external command\n");
+ return EFAULT;
+ }
+
+ if (ret == 0) {
+ /* cast is safe - see
+ https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
+ "The statement about argv[] and envp[] being constants ... "
+ */
+ execvp(argv[0], discard_const_p(char * const, argv));
+ ERROR("Error while executing external command\n");
+ _exit(1);
+ } else {
+ if (waitpid(ret, &wstatus, 0) == -1) {
+ ERROR("Error while executing external command '%s'\n", argv[0]);
+ return EFAULT;
+ } else if (WEXITSTATUS(wstatus) != 0) {
+ ERROR("Command '%s' failed with [%d]\n",
+ argv[0], WEXITSTATUS(wstatus));
+ return EIO;
+ }
+ }
+
+ return EOK;
+}
+
+static errno_t sssctl_manage_service(enum sssctl_svc_action action)
+{
+#ifdef HAVE_SYSTEMD
+ switch (action) {
+ case SSSCTL_SVC_START:
+ return sssctl_systemd_start();
+ case SSSCTL_SVC_STOP:
+ return sssctl_systemd_stop();
+ case SSSCTL_SVC_RESTART:
+ return sssctl_systemd_restart();
+ }
+#elif defined(HAVE_SERVICE)
+ switch (action) {
+ case SSSCTL_SVC_START:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "start", NULL});
+ case SSSCTL_SVC_STOP:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "stop", NULL});
+ case SSSCTL_SVC_RESTART:
+ return sssctl_run_command(
+ (const char *[]){SERVICE_PATH, "sssd", "restart", NULL});
+ }
+#endif
+
+ return ENOSYS;
+}
+
+bool sssctl_start_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (sss_daemon_running()) {
+ return true;
+ }
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD needs to be running. Start SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_START);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Starting SSSD automatically is not supported "
+ "on this platform, please start the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to start SSSD!\n");
+ return false;
+ }
+
+ return true;
+}
+
+bool sssctl_stop_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (!sss_daemon_running()) {
+ return true;
+ }
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD must not be running. Stop SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_STOP);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Stopping SSSD automatically is not supported "
+ "on this platform, please stop the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to stop SSSD!\n");
+ return false;
+ }
+
+
+ return true;
+}
+
+bool sssctl_restart_sssd(bool force)
+{
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ if (!force) {
+ prompt = sssctl_prompt(_("SSSD needs to be restarted. Restart SSSD now?"),
+ SSSCTL_PROMPT_YES);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ case SSSCTL_PROMPT_ERROR:
+ return false;
+ }
+ }
+
+ ret = sssctl_manage_service(SSSCTL_SVC_RESTART);
+ switch(ret) {
+ case EOK:
+ return true;
+ case ENOSYS:
+ fprintf(stderr, "Restarting SSSD automatically is not supported "
+ "on this platform, please restart the service "
+ "manually\n");
+ return false;
+ default:
+ fprintf(stderr, "Unable to restart SSSD!\n");
+ return false;
+ }
+
+ return true;
+}
+
+int main(int argc, const char **argv)
+{
+ struct sss_route_cmd commands[] = {
+ SSS_TOOL_DELIMITER("SSSD Status:"),
+ SSS_TOOL_COMMAND("domain-list", "List available domains", 0, sssctl_domain_list),
+ SSS_TOOL_COMMAND("domain-status", "Print information about domain", 0, sssctl_domain_status),
+ SSS_TOOL_COMMAND_FLAGS("user-checks", "Print information about a user and check authentication", 0, sssctl_user_checks, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+ SSS_TOOL_COMMAND("access-report", "Generate access report for a domain", 0, sssctl_access_report),
+ SSS_TOOL_DELIMITER("Information about cached content:"),
+ SSS_TOOL_COMMAND("user-show", "Information about cached user", 0, sssctl_user_show),
+ SSS_TOOL_COMMAND("group-show", "Information about cached group", 0, sssctl_group_show),
+ SSS_TOOL_COMMAND("netgroup-show", "Information about cached netgroup", 0, sssctl_netgroup_show),
+ SSS_TOOL_DELIMITER("Local data tools:"),
+ SSS_TOOL_COMMAND("client-data-backup", "Backup local data", 0, sssctl_client_data_backup),
+ SSS_TOOL_COMMAND("client-data-restore", "Restore local data from backup", 0, sssctl_client_data_restore),
+ SSS_TOOL_COMMAND("cache-remove", "Backup local data and remove cached content", 0, sssctl_cache_remove),
+ SSS_TOOL_COMMAND("cache-upgrade", "Perform cache upgrade", ERR_SYSDB_VERSION_TOO_OLD, sssctl_cache_upgrade),
+ SSS_TOOL_COMMAND("cache-expire", "Invalidate cached objects", 0, sssctl_cache_expire),
+ SSS_TOOL_COMMAND("cache-index", "Manage cache indexes", 0, sssctl_cache_index),
+ SSS_TOOL_DELIMITER("Log files tools:"),
+ SSS_TOOL_COMMAND("logs-remove", "Remove existing SSSD log files", 0, sssctl_logs_remove),
+ SSS_TOOL_COMMAND("logs-fetch", "Archive SSSD log files in tarball", 0, sssctl_logs_fetch),
+ SSS_TOOL_COMMAND("debug-level", "Change or print information about SSSD debug level", 0, sssctl_debug_level),
+ SSS_TOOL_COMMAND_FLAGS("analyze", "Analyze logged data", 0, sssctl_analyze, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+ SSS_TOOL_DELIMITER("Configuration files tools:"),
+ SSS_TOOL_COMMAND_FLAGS("config-check", "Perform static analysis of SSSD configuration", 0, sssctl_config_check, SSS_TOOL_FLAG_SKIP_CMD_INIT),
+#endif
+ SSS_TOOL_DELIMITER("Certificate related tools:"),
+ SSS_TOOL_COMMAND_FLAGS("cert-show", "Print information about the certificate", 0, sssctl_cert_show, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+ SSS_TOOL_COMMAND("cert-map", "Show users mapped to the certificate", 0, sssctl_cert_map),
+ SSS_TOOL_COMMAND_FLAGS("cert-eval-rule", "Check mapping and matching rule with a certificate", 0, sssctl_cert_eval_rule, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#ifdef BUILD_PASSKEY
+ SSS_TOOL_DELIMITER("Passkey related tools:"),
+ SSS_TOOL_COMMAND_FLAGS("passkey-register", "Perform passkey registration", 0, sssctl_passkey_register, SSS_TOOL_FLAG_SKIP_CMD_INIT|SSS_TOOL_FLAG_SKIP_ROOT_CHECK),
+#endif
+ SSS_TOOL_LAST
+ };
+
+ return sss_tool_main(argc, argv, commands, NULL);
+}
diff --git a/src/tools/sssctl/sssctl.h b/src/tools/sssctl/sssctl.h
new file mode 100644
index 0000000..3a53a89
--- /dev/null
+++ b/src/tools/sssctl/sssctl.h
@@ -0,0 +1,152 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _SSSCTL_H_
+#define _SSSCTL_H_
+
+#include "tools/common/sss_tools.h"
+
+#define PRINT_IFP_WARNING(ret) do { \
+ if (ret == ERR_SBUS_UNKNOWN_SERVICE || ret == ERR_SBUS_NO_REPLY || ret == ETIMEDOUT) { \
+ fprintf(stderr, _("InfoPipe operation failed. Check that SSSD " \
+ "is running and the InfoPipe responder is enabled. Make sure " \
+ "'ifp' is listed in the 'services' option in sssd.conf.")); \
+ } \
+} while (0)
+
+enum sssctl_prompt_result {
+ SSSCTL_PROMPT_YES,
+ SSSCTL_PROMPT_NO,
+ SSSCTL_PROMPT_ERROR
+};
+
+enum sssctl_svc_action {
+ SSSCTL_SVC_START,
+ SSSCTL_SVC_STOP,
+ SSSCTL_SVC_RESTART
+};
+
+enum sssctl_prompt_result
+sssctl_prompt(const char *message,
+ enum sssctl_prompt_result defval);
+
+errno_t sssctl_wrap_command(const char *command,
+ const char *subcommand,
+ struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+errno_t sssctl_run_command(const char *const argv[]); /* argv[0] - command */
+bool sssctl_start_sssd(bool force);
+bool sssctl_stop_sssd(bool force);
+bool sssctl_restart_sssd(bool force);
+
+errno_t sssctl_systemd_start(void);
+errno_t sssctl_systemd_stop(void);
+errno_t sssctl_systemd_restart(void);
+
+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cache_index(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_debug_level(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_analyze(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_config_check(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cert_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+
+errno_t sssctl_cert_map(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+#ifdef BUILD_PASSKEY
+errno_t sssctl_passkey_register(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+#endif /* BUILD_PASSKEY */
+
+errno_t sssctl_cert_eval_rule(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt);
+#endif /* _SSSCTL_H_ */
diff --git a/src/tools/sssctl/sssctl_access_report.c b/src/tools/sssctl/sssctl_access_report.c
new file mode 100644
index 0000000..5ae873d
--- /dev/null
+++ b/src/tools/sssctl/sssctl_access_report.c
@@ -0,0 +1,422 @@
+/*
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "sbus/sbus_opath.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+/*
+ * We're searching the cache directly..
+ */
+#include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ipa/ipa_rules_common.h"
+
+typedef errno_t (*sssctl_dom_access_reporter_fn)(struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain);
+
+static errno_t get_rdn_value(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ const char *dn_attr,
+ const char **_rdn_value)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_dn *dn = NULL;
+ const struct ldb_val *rdn_val;
+ const char *rdn_str;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rdn_val = ldb_dn_get_rdn_val(dn);
+ if (rdn_val == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "No RDN value?\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ rdn_str = talloc_strndup(tmp_ctx,
+ (const char *)rdn_val->data,
+ rdn_val->length);
+ if (rdn_str == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = EOK;
+ *_rdn_value = talloc_steal(mem_ctx, rdn_str);
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static errno_t is_member_group(struct sss_domain_info *dom,
+ const char *dn_attr,
+ const char *group_rdn,
+ bool *_is_group)
+{
+ const char *comp_name;
+ const struct ldb_val *comp_val;
+ TALLOC_CTX *tmp_ctx;
+ bool is_group = false;
+ errno_t ret;
+ struct ldb_dn *dn = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ dn = ldb_dn_new(tmp_ctx, sysdb_ctx_get_ldb(dom->sysdb), dn_attr);
+ if (dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ comp_name = ldb_dn_get_component_name(dn, 1);
+ comp_val = ldb_dn_get_component_val(dn, 1);
+ if (strcasecmp("cn", comp_name) == 0
+ && strncasecmp(group_rdn,
+ (const char *) comp_val->data,
+ comp_val->length) == 0) {
+ is_group = true;
+ }
+
+ ret = EOK;
+done:
+ *_is_group = is_group;
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+static void print_category(struct sss_domain_info *domain,
+ struct ldb_message *rule_msg,
+ const char *category_attr_name,
+ const char *category_label)
+{
+ struct ldb_message_element *category_attr;
+
+ category_attr = ldb_msg_find_element(rule_msg, category_attr_name);
+ if (category_attr == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", category_attr_name);
+ return;
+ }
+
+ if (category_attr->num_values > 0) {
+ PRINT("\t%s: ", category_label);
+ for (unsigned i = 0; i < category_attr->num_values; i++) {
+ PRINT("%s%s",
+ i > 0 ? ", " : "",
+ (const char *) category_attr->values[i].data);
+ }
+ PRINT("\n");
+ }
+}
+
+static void print_member_attr(struct sss_domain_info *domain,
+ struct ldb_message *rule_msg,
+ const char *member_attr_name,
+ const char *group_rdn,
+ const char *object_label,
+ const char *group_label)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char **member_names = NULL;
+ size_t name_count = 0;
+ const char **member_group_names = NULL;
+ size_t group_count = 0;
+ struct ldb_message_element *member_attr = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ member_attr = ldb_msg_find_element(rule_msg, member_attr_name);
+ if (member_attr == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot find %s\n", member_attr_name);
+ goto done;
+ }
+
+ member_names = talloc_zero_array(tmp_ctx,
+ const char *,
+ member_attr->num_values + 1);
+ member_group_names = talloc_zero_array(tmp_ctx,
+ const char *,
+ member_attr->num_values + 1);
+ if (member_names == NULL || member_group_names == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "OOM?\n");
+ goto done;
+ }
+
+ for (size_t i = 0; i < member_attr->num_values; i++) {
+ bool is_group;
+ const char *rdn_string;
+ const char *dn_attr;
+
+ dn_attr = (const char *) member_attr->values[i].data;
+
+ ret = is_member_group(domain, dn_attr, group_rdn, &is_group);
+ if (ret != EOK) {
+ continue;
+ }
+
+ ret = get_rdn_value(tmp_ctx, domain, dn_attr, &rdn_string);
+ if (ret != EOK) {
+ continue;
+ }
+
+ if (is_group == false) {
+ member_names[name_count] = talloc_steal(member_names,
+ rdn_string);
+ if (member_names[name_count] == NULL) {
+ goto done;
+ }
+ name_count++;
+ } else {
+ member_group_names[group_count] = talloc_strdup(member_group_names,
+ rdn_string);
+ if (member_group_names[group_count] == NULL) {
+ goto done;
+ }
+ group_count++;
+ }
+ }
+
+ if (member_names[0] != NULL) {
+ PRINT("\t%s: ", object_label);
+ for (int i = 0; member_names[i]; i++) {
+ PRINT("%s%s", i > 0 ? ", " : "", member_names[i]);
+ }
+ PRINT("\n");
+ }
+
+ if (member_group_names[0] != NULL) {
+ PRINT("\t%s: ", group_label);
+ for (int i = 0; member_group_names[i]; i++) {
+ PRINT("%s%s", i > 0 ? ", " : "", member_group_names[i]);
+ }
+ PRINT("\n");
+ }
+
+done:
+ talloc_free(tmp_ctx);
+}
+
+static void print_ipa_hbac_rule(struct sss_domain_info *domain,
+ struct ldb_message *rule_msg)
+{
+ struct ldb_message_element *el;
+
+ el = ldb_msg_find_element(rule_msg, IPA_CN);
+ if (el == NULL || el->num_values < 1) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "A rule with no name\n");
+ return;
+ }
+
+ PRINT("Rule name: %1$s\n", el->values[0].data);
+
+ print_member_attr(domain,
+ rule_msg,
+ IPA_MEMBER_USER,
+ "groups",
+ _("Member users"),
+ _("Member groups"));
+ print_category(domain,
+ rule_msg,
+ IPA_USER_CATEGORY,
+ _("User category"));
+
+ print_member_attr(domain,
+ rule_msg,
+ IPA_MEMBER_SERVICE,
+ "hbacservicegroups",
+ _("Member services"),
+ _("Member service groups"));
+ print_category(domain,
+ rule_msg,
+ IPA_SERVICE_CATEGORY,
+ _("Service category"));
+
+ PRINT("\n");
+}
+
+static errno_t refresh_hbac_rules(struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char *path;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ path = sbus_opath_compose(tmp_ctx, IFP_PATH_DOMAINS, domain->name);
+ if (path == NULL) {
+ PRINT("Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_domain_RefreshAccessRules(conn, IFP_BUS, path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to refresh HBAC rules [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_ipa_access_report(struct sss_tool_ctx *tool_ctx,
+ struct sss_domain_info *domain)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *filter = NULL;
+ errno_t ret;
+ const char *attrs[] = {
+ OBJECTCLASS,
+ IPA_CN,
+ IPA_MEMBER_USER,
+ IPA_USER_CATEGORY,
+ IPA_MEMBER_SERVICE,
+ IPA_SERVICE_CATEGORY,
+ IPA_MEMBER_HOST,
+ IPA_HOST_CATEGORY,
+ NULL,
+ };
+ size_t rule_count;
+ struct ldb_message **msgs = NULL;
+
+ /* Run the pam account phase to make sure the rules are fetched by SSSD */
+ ret = refresh_hbac_rules(tool_ctx, domain);
+ if (ret != EOK) {
+ ERROR("Unable to refresh HBAC rules, using cached content\n");
+ /* Non-fatal */
+ }
+
+ tmp_ctx = talloc_new(tool_ctx);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ filter = talloc_asprintf(tmp_ctx, "(objectClass=%s)", IPA_HBAC_RULE);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_search_custom(tmp_ctx, domain, filter,
+ HBAC_RULES_SUBDIR, attrs,
+ &rule_count, &msgs);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Error looking up HBAC rules\n");
+ goto done;
+ }
+
+ if (ret == ENOENT) {
+ PRINT("No cached rules. All users will be denied access\n");
+ ret = EOK;
+ goto done;
+ }
+
+ PRINT("%1$zu rules cached\n\n", rule_count);
+
+ for (size_t i = 0; i < rule_count; i++) {
+ print_ipa_hbac_rule(domain, msgs[i]);
+ }
+
+ ret = EOK;
+done:
+ talloc_zfree(tmp_ctx);
+ return ret;
+}
+
+sssctl_dom_access_reporter_fn get_report_fn(const char *provider)
+{
+ if (strcmp(provider, "ipa") == 0) {
+ return sssctl_ipa_access_report;
+ }
+
+ return NULL;
+}
+
+errno_t sssctl_access_report(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+ const char *domname = NULL;
+ sssctl_dom_access_reporter_fn reporter;
+ struct sss_domain_info *dom;
+
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "DOMAIN", _("Specify domain name."),
+ SSS_TOOL_OPT_REQUIRED, &domname, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ dom = find_domain_by_name(tool_ctx->domains, domname, true);
+ if (dom == NULL) {
+ ERROR("Cannot find domain %1$s\n", domname);
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ reporter = get_report_fn(dom->provider);
+ if (reporter == NULL) {
+ ERROR("Access report not implemented for domains of type %1$s\n",
+ dom->provider);
+ goto done;
+ }
+
+ ret = reporter(tool_ctx, dom);
+
+done:
+ free(discard_const(domname));
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
new file mode 100644
index 0000000..5a62a26
--- /dev/null
+++ b/src/tools/sssctl/sssctl_cache.c
@@ -0,0 +1,718 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+
+#define NOT_FOUND_MSG(obj) _(obj " %s is not present in cache.\n")
+
+#define SSSCTL_CACHE_NAME {_("Name"), SYSDB_NAME, get_attr_name}
+#define SSSCTL_CACHE_CREATE {_("Cache entry creation date"), SYSDB_CREATE_TIME, get_attr_time}
+#define SSSCTL_CACHE_UPDATE {_("Cache entry last update time"), SYSDB_LAST_UPDATE, get_attr_time}
+#define SSSCTL_CACHE_EXPIRE {_("Cache entry expiration time"), SYSDB_CACHE_EXPIRE, get_attr_expire}
+#define SSSCTL_CACHE_IFP {_("Cached in InfoPipe"), SYSDB_IFP_CACHED, get_attr_yesno}
+#define SSSCTL_CACHE_NULL {NULL, NULL, NULL}
+
+enum cache_object {
+ CACHED_USER,
+ CACHED_GROUP,
+ CACHED_NETGROUP,
+};
+
+typedef errno_t (*sssctl_attr_fn)(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value);
+
+typedef struct ldb_dn *(*sssctl_basedn_fn)(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domain);
+
+struct sssctl_object_info {
+ const char *msg;
+ const char *attr;
+ sssctl_attr_fn attr_fn;
+};
+
+static errno_t time_to_string(TALLOC_CTX *mem_ctx,
+ time_t timestamp,
+ const char **_value)
+{
+ const char *value;
+ struct tm *tm;
+ char str[255];
+ size_t ret;
+
+ tm = localtime(&timestamp);
+ if (tm == NULL) {
+ return ENOMEM;
+ }
+
+ ret = strftime(str, 255, "%x %X", tm);
+ if (ret == 0) {
+ return ERANGE;
+ }
+
+ value = talloc_strdup(mem_ctx, str);
+ if (value == NULL) {
+ return ENOMEM;
+ }
+
+ *_value = value;
+
+ return EOK;
+}
+
+static errno_t get_attr_name(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ errno_t ret;
+ const char *orig_name;
+ char *tmp_name;
+ char *outname;
+
+ ret = sysdb_attrs_get_string(entry, attr, &orig_name);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ tmp_name = sss_output_name(mem_ctx, orig_name, dom->case_preserve, 0);
+ if (tmp_name == NULL) {
+ return ENOMEM;
+ }
+
+ if (dom->fqnames) {
+ outname = sss_tc_fqname(mem_ctx, dom->names, dom, tmp_name);
+ talloc_free(tmp_name);
+ if (outname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "sss_tc_fqname() failed\n");
+ return ENOMEM;
+ }
+ } else {
+ outname = tmp_name;
+ }
+
+ *_value = outname;
+ return EOK;
+}
+
+static errno_t get_attr_time(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t get_attr_expire(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ if (is_files_provider(dom)) {
+ *_value = "Never";
+ return EOK;
+ }
+
+ if (value < time(NULL)) {
+ *_value = "Expired";
+ return EOK;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t attr_initgr(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ uint32_t value;
+ errno_t ret;
+
+ ret = sysdb_attrs_get_uint32_t(entry, attr, &value);
+ if (ret == ENOENT || (ret == EOK && value == 0)) {
+ *_value = "Initgroups were not yet performed";
+ return EOK;
+ } else if (ret != EOK) {
+ return ret;
+ }
+
+ if (is_files_provider(dom)) {
+ *_value = "Never";
+ return EOK;
+ }
+
+ if (value < time(NULL)) {
+ *_value = "Expired";
+ return EOK;
+ }
+
+ return time_to_string(mem_ctx, value, _value);
+}
+
+static errno_t get_attr_yesno(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *entry,
+ struct sss_domain_info *dom,
+ const char *attr,
+ const char **_value)
+{
+ errno_t ret;
+ bool val;
+
+ ret = sysdb_attrs_get_bool(entry, attr, &val);
+ if (ret == ENOENT) {
+ val = 0;
+ } else if (ret != EOK) {
+ return ret;
+ }
+
+ *_value = val ? "Yes" : "No";
+
+ return EOK;
+}
+
+static const char **sssctl_build_attrs(TALLOC_CTX *mem_ctx,
+ struct sssctl_object_info *info)
+{
+ const char **attrs;
+ size_t count;
+ int i;
+
+ for (count = 0; info[count].attr != NULL; count++) {
+ /* no op */
+ }
+
+ attrs = talloc_zero_array(mem_ctx, const char *, count + 1);
+ if (attrs == NULL) {
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++) {
+ attrs[i] = talloc_strdup(attrs, info[i].attr);
+ if (attrs[i] == NULL) {
+ talloc_free(attrs);
+ return NULL;
+ }
+ }
+
+ return attrs;
+}
+
+static errno_t sssctl_query_cache(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *sysdb,
+ struct ldb_dn *base_dn,
+ const char *filter,
+ const char **attrs,
+ struct sysdb_attrs **_entry)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs **sysdb_attrs;
+ struct ldb_message **msgs;
+ size_t count;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ ret = sysdb_search_entry(tmp_ctx, sysdb, base_dn, LDB_SCOPE_SUBTREE,
+ filter, attrs, &count, &msgs);
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC, "No result\n");
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to search sysdb "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (count != 1) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Search returned more than one result!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ ret = sysdb_msg2attrs(tmp_ctx, count, msgs, &sysdb_attrs);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to convert message to sysdb attrs "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_entry = talloc_steal(mem_ctx, sysdb_attrs[0]);
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *dom,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value)
+{
+ const char *class;
+ const char *filter;
+ char *filter_value;
+ bool qualify_attr = false;
+
+ if (strcmp(attr_name, SYSDB_NAME) == 0) {
+ if (obj_type == CACHED_USER || obj_type == CACHED_GROUP) {
+ qualify_attr = true;
+ }
+ }
+
+ switch (obj_type) {
+ case CACHED_USER:
+ class = SYSDB_USER_CLASS;
+ break;
+ case CACHED_GROUP:
+ class = SYSDB_GROUP_CLASS;
+ break;
+ case CACHED_NETGROUP:
+ class = SYSDB_NETGROUP_CLASS;
+ break;
+ default:
+ DEBUG(SSSDBG_FATAL_FAILURE,
+ "sssctl doesn't handle this object type (type=%d)\n", obj_type);
+ return NULL;
+ }
+
+ if (qualify_attr) {
+ filter_value = sss_create_internal_fqname(NULL, attr_value, dom->name);
+ } else {
+ filter_value = talloc_strdup(NULL, attr_value);
+ }
+ if (filter_value == NULL) {
+ return NULL;
+ }
+
+ if (dom->case_sensitive == false) {
+ char *filter_value_old;
+
+ filter_value_old = filter_value;
+ filter_value = sss_tc_utf8_str_tolower(mem_ctx, filter_value_old);
+ talloc_free(filter_value_old);
+ }
+
+ filter = talloc_asprintf(mem_ctx, "(&(%s=%s)(|(%s=%s)(%s=%s)))",
+ obj_type == CACHED_NETGROUP ? SYSDB_OBJECTCLASS : SYSDB_OBJECTCATEGORY,
+ class, attr_name, filter_value,
+ SYSDB_NAME_ALIAS, filter_value);
+
+ talloc_free(filter_value);
+
+ return filter;
+}
+
+static errno_t sssctl_find_object(TALLOC_CTX *mem_ctx,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value,
+ const char **attrs,
+ struct sysdb_attrs **_entry,
+ struct sss_domain_info **_dom)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sss_domain_info *dom;
+ struct sysdb_attrs *entry = NULL;
+ struct ldb_dn *base_dn;
+ bool fqn_provided;
+ const char *filter;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ dom = domain == NULL ? domains : domain;
+ fqn_provided = domain == NULL ? false : true;
+ while (dom != NULL) {
+ if (!fqn_provided && dom->fqnames) {
+ dom = get_next_domain(dom, 0);
+ continue;
+ }
+
+ base_dn = basedn_fn(tmp_ctx, dom);
+ if (base_dn == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ filter = sssctl_create_filter(tmp_ctx, dom, obj_type,
+ attr_name, attr_value);
+ if (filter == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create filter\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sssctl_query_cache(tmp_ctx, dom->sysdb, base_dn, filter,
+ attrs, &entry);
+ switch(ret) {
+ case EOK:
+ /* Entry was found. */
+ *_entry = talloc_steal(mem_ctx, entry);
+ *_dom = dom;
+ goto done;
+ case ENOENT:
+ if (fqn_provided) {
+ /* Not found but a domain was provided in input. We're done. */
+ goto done;
+ }
+ break;
+ default:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to query cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ dom = get_next_domain(dom, 0);
+ }
+
+ ret = ENOENT;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sssctl_fetch_object(TALLOC_CTX *mem_ctx,
+ struct sssctl_object_info *info,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value,
+ struct sysdb_attrs **_entry,
+ struct sss_domain_info **_dom)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *entry = NULL;
+ struct sss_domain_info *dom = NULL;
+ const char **attrs;
+ char *sanitized;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sss_filter_sanitize(tmp_ctx, attr_value, &sanitized);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to sanitize input [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ attrs = sssctl_build_attrs(tmp_ctx, info);
+ if (attrs == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get attribute list!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sssctl_find_object(tmp_ctx, domains, domain, basedn_fn,
+ obj_type, attr_name, sanitized, attrs,
+ &entry, &dom);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to query cache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ *_entry = talloc_steal(mem_ctx, entry);
+ *_dom = dom;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t sssctl_print_object(struct sssctl_object_info *info,
+ struct sss_domain_info *domains,
+ struct sss_domain_info *domain,
+ sssctl_basedn_fn basedn_fn,
+ const char *noent_fmt,
+ enum cache_object obj_type,
+ const char *attr_name,
+ const char *attr_value)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sysdb_attrs *entry = NULL;
+ const char *value;
+ errno_t ret;
+ int i;
+ struct sss_domain_info *dom = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sssctl_fetch_object(tmp_ctx, info, domains, domain, basedn_fn,
+ obj_type, attr_name, attr_value,
+ &entry, &dom);
+ if (ret == ENOENT) {
+ printf(noent_fmt, attr_value);
+ ret = EOK;
+ goto done;
+ } else if (ret != EOK) {
+ ERROR("Error: Unable to get object [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (dom == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine object domain\n");
+ ret = ERR_DOMAIN_NOT_FOUND;
+ goto done;
+ }
+
+ for (i = 0; info[i].attr != NULL; i++) {
+ ret = info[i].attr_fn(tmp_ctx, entry, dom, info[i].attr, &value);
+ if (ret == ENOENT) {
+ continue;
+ } else if (ret != EOK) {
+ ERROR("%s: Unable to read value [%d]: %s\n",
+ info[i].msg, ret, sss_strerror(ret));
+ continue;
+ }
+
+ printf("%s: %s\n", info[i].msg, value);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t parse_cmdline(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ struct poptOption *options,
+ const char **_orig_name,
+ struct sss_domain_info **_domain)
+{
+ const char *input_name = NULL;
+ const char *orig_name;
+ struct sss_domain_info *domain;
+ int ret;
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "NAME", _("Specify name."),
+ SSS_TOOL_OPT_REQUIRED, &input_name, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ ret = sss_tool_parse_name(tool_ctx, tool_ctx, input_name,
+ &orig_name, &domain);
+ if (ret != EOK) {
+ ERROR("Unable to parse name %s.\n", input_name);
+ goto done;
+ }
+
+ *_orig_name = orig_name;
+ *_domain = domain;
+
+done:
+ free(discard_const(input_name));
+
+ return ret;
+}
+
+struct sssctl_cache_opts {
+ struct sss_domain_info *domain;
+ const char *value;
+ int sid;
+ int id;
+};
+
+errno_t sssctl_user_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ const char *attr;
+ errno_t ret;
+
+ struct poptOption options[] = {
+ {"sid", 's', POPT_ARG_NONE , &opts.sid, 0, _("Search by SID"), NULL },
+ {"uid", 'u', POPT_ARG_NONE, &opts.id, 0, _("Search by user ID"), NULL },
+ POPT_TABLEEND
+ };
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ {_("Initgroups expiration time"), SYSDB_INITGR_EXPIRE, attr_initgr},
+ SSSCTL_CACHE_IFP,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, options, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ attr = SYSDB_NAME;
+ if (opts.sid) {
+ attr = SYSDB_SID;
+ } else if (opts.id) {
+ attr = SYSDB_UIDNUM;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_user_base_dn, NOT_FOUND_MSG("User"),
+ CACHED_USER, attr, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
+
+errno_t sssctl_group_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ const char *attr;
+ errno_t ret;
+
+ struct poptOption options[] = {
+ {"sid", 's', POPT_ARG_NONE , &opts.sid, 0, _("Search by SID"), NULL },
+ {"gid", 'g', POPT_ARG_NONE, &opts.id, 0, _("Search by group ID"), NULL },
+ POPT_TABLEEND
+ };
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ SSSCTL_CACHE_IFP,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, options, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ attr = SYSDB_NAME;
+ if (opts.sid) {
+ attr = SYSDB_SID;
+ } else if (opts.id) {
+ attr = SYSDB_GIDNUM;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_group_base_dn, NOT_FOUND_MSG("Group"),
+ CACHED_GROUP, attr, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
+
+errno_t sssctl_netgroup_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_cache_opts opts = {0};
+ errno_t ret;
+
+ struct sssctl_object_info info[] = {
+ SSSCTL_CACHE_NAME,
+ SSSCTL_CACHE_CREATE,
+ SSSCTL_CACHE_UPDATE,
+ SSSCTL_CACHE_EXPIRE,
+ SSSCTL_CACHE_NULL
+ };
+
+ ret = parse_cmdline(cmdline, tool_ctx, NULL, &opts.value, &opts.domain);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ ret = sssctl_print_object(info, tool_ctx->domains, opts.domain,
+ sysdb_netgroup_base_dn, NOT_FOUND_MSG("Netgroup"),
+ CACHED_NETGROUP, SYSDB_NAME, opts.value);
+ if (ret != EOK) {
+ return ret;
+ }
+
+
+ return EOK;
+}
diff --git a/src/tools/sssctl/sssctl_cert.c b/src/tools/sssctl/sssctl_cert.c
new file mode 100644
index 0000000..d2cdc2d
--- /dev/null
+++ b/src/tools/sssctl/sssctl_cert.c
@@ -0,0 +1,289 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Certificate related utilities
+
+ Copyright (C) 2018 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "lib/certmap/sss_certmap.h"
+#include "util/crypto/sss_crypto.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#define PEM_HEAD "-----BEGIN CERTIFICATE-----\n"
+#define PEM_FOOT "-----END CERTIFICATE-----"
+
+errno_t sssctl_cert_show(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ char *desc;
+ uint8_t *der_cert = NULL;
+ size_t der_size;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ der_cert = sss_base64_decode(tmp_ctx, cert_b64, &der_size);
+ if (der_cert == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to decode base64 string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_certmap_display_cert_content(tmp_ctx, der_cert, der_size, &desc);
+ if (ret != 0) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parsed certificate.\n");
+ goto done;
+ }
+
+ printf("%s\n", desc);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(cert_b64));
+
+ return ret;
+}
+
+errno_t sssctl_cert_map(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ char *cert_pem = NULL;
+ struct sbus_sync_connection *conn;
+ const char **paths;
+ size_t c;
+ const char *name;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cert_pem = talloc_asprintf(tmp_ctx, "%s%s\n%s",
+ PEM_HEAD, cert_b64, PEM_FOOT);
+ if (cert_pem == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_users_ListByCertificate(tmp_ctx, conn, IFP_BUS,
+ IFP_PATH_USERS, cert_pem, -1,
+ &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to map certificate [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (paths != NULL) {
+ for (c = 0; paths[c] != NULL; c++) {
+ ret = sbus_get_ifp_user_name(tmp_ctx, conn, IFP_BUS, paths[c],
+ &name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ puts(name);
+ }
+ } else {
+ PRINT(" - no mapped users found -");
+ }
+
+ ret = EOK;
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(cert_b64));
+
+ return ret;
+}
+
+struct priv_sss_debug {
+ bool verbose;
+};
+
+void certmap_ext_debug(void *private, const char *file, long line,
+ const char *function, const char *format, ...)
+{
+ va_list ap;
+ struct priv_sss_debug *data = private;
+
+ if (data != NULL && data->verbose) {
+ va_start(ap, format);
+ fprintf(stdout, "%s:%ld [%s]: ", file, line, function);
+ vfprintf(stdout, format, ap);
+ fprintf(stdout, "\n");
+ va_end(ap);
+ }
+}
+
+errno_t sssctl_cert_eval_rule(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ errno_t ret;
+ int verbose = 0;
+ const char *cert_b64 = NULL;
+ const char *map = NULL;
+ const char *match = NULL;
+ struct sss_certmap_ctx *sss_certmap_ctx = NULL;
+ struct priv_sss_debug priv_sss_debug;
+ uint8_t *der_cert = NULL;
+ size_t der_size;
+ char *filter = NULL;
+ char **domains = NULL;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"map", 'p', POPT_ARG_STRING, &map, 0, _("Mapping rule"), NULL },
+ {"match", 't', POPT_ARG_STRING, &match, 0, _("Matching rule"), NULL },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show debug information"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "CERTIFICATE-BASE64-ENCODED",
+ _("Specify base64 encoded certificate."),
+ SSS_TOOL_OPT_REQUIRED, &cert_b64, NULL);
+ if (ret != EOK) {
+ ERROR("Unable to parse command arguments\n");
+ return ret;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ERROR("Out of memory!\n");
+ return ENOMEM;
+ }
+
+ priv_sss_debug.verbose = (verbose != 0);
+
+ ret = sss_certmap_init(tmp_ctx, certmap_ext_debug, &priv_sss_debug,
+ &sss_certmap_ctx);
+ if (ret != EOK) {
+ ERROR("Failed to setup certmap context.\n");
+ goto done;
+ }
+
+ ret = sss_certmap_add_rule(sss_certmap_ctx, 1, match, map, NULL);
+ if (ret != EOK) {
+ ERROR("Failed to add mapping and matching rules with error [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ der_cert = sss_base64_decode(tmp_ctx, cert_b64, &der_size);
+ if (der_cert == NULL) {
+ ERROR("Failed to decode base64 string.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sss_certmap_match_cert(sss_certmap_ctx, der_cert, der_size);
+ switch (ret) {
+ case 0:
+ PRINT("Certificate matches rule.\n");
+ break;
+ case ENOENT:
+ PRINT("Certificate does not match rule.\n");
+ break;
+ default:
+ ERROR("Error during certificate matching [%d][%s].\n",
+ ret, sss_strerror(ret));
+ }
+
+ ret = sss_certmap_get_search_filter(sss_certmap_ctx, der_cert, der_size,
+ &filter, &domains);
+ if (ret != 0) {
+ ERROR("Failed to generate mapping filter [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+ PRINT("Mapping filter:\n\n %s\n\n", filter);
+ sss_certmap_free_filter_and_domains(filter, domains);
+
+ ret = EOK;
+
+done:
+
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_config.c b/src/tools/sssctl/sssctl_config.c
new file mode 100644
index 0000000..f009151
--- /dev/null
+++ b/src/tools/sssctl/sssctl_config.c
@@ -0,0 +1,201 @@
+/*
+ Authors:
+ Michal Židek <mzidek@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <popt.h>
+#include <stdio.h>
+#include <ini_configobj.h>
+
+#include "util/util.h"
+#include "util/sss_ini.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+#include "confdb/confdb.h"
+
+
+
+#ifdef HAVE_LIBINI_CONFIG_V1_3
+
+static char *sssctl_config_snippet_path(TALLOC_CTX *ctx, const char *path)
+{
+ char *tmp = NULL;
+ const char delimiter = '/';
+ char *dpos = NULL;
+
+ tmp = talloc_strdup(ctx, path);
+ if (!tmp) {
+ return NULL;
+ }
+
+ dpos = strrchr(tmp, delimiter);
+ if (dpos != NULL) {
+ ++dpos;
+ *dpos = '\0';
+ } else {
+ *tmp = '\0';
+ }
+
+ return talloc_strdup_append(tmp, CONFDB_DEFAULT_CONFIG_DIR_NAME);
+}
+
+errno_t sssctl_config_check(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+ struct sss_ini *init_data;
+ struct ref_array *ra_error, *ra_success;
+ char *msg;
+ uint32_t i = 0;
+ size_t num_errors;
+ size_t num_ra_error, num_ra_success;
+ char **strs = NULL;
+ TALLOC_CTX *tmp_ctx = NULL;
+ const char *config_path = NULL;
+ const char *config_snippet_path = NULL;
+ struct poptOption long_options[] = {
+ {"config", 'c', POPT_ARG_STRING, &config_path,
+ 0, _("Specify a non-default config file"), NULL},
+ {"snippet", 's', POPT_ARG_STRING, &config_snippet_path,
+ 0, _("Specify a non-default snippet dir (The default is to look in "
+ "the same place where the main config file is located. For "
+ "example if the config is set to \"/my/path/sssd.conf\", "
+ "the snippet dir \"/my/path/conf.d\" is used)"), NULL},
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, long_options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ init_data = sss_ini_new(tmp_ctx);
+ if (!init_data) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (config_path == NULL) {
+ config_path = SSSD_CONFIG_FILE;
+ }
+
+ if (config_snippet_path == NULL) {
+ config_snippet_path = sssctl_config_snippet_path(tmp_ctx, config_path);
+ if (config_snippet_path == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to create snippet path\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ ret = sss_ini_read_sssd_conf(init_data,
+ config_path,
+ config_snippet_path);
+
+ if (ret == ERR_INI_OPEN_FAILED) {
+ PRINT("Failed to open %s\n", config_path);
+ goto done;
+ }
+
+ if (!sss_ini_exists(init_data)) {
+ PRINT("File %1$s does not exist.\n", config_path);
+ }
+
+ if (ret == ERR_INI_INVALID_PERMISSION) {
+ PRINT("File ownership and permissions check failed. "
+ "Expected root:root and 0600.\n");
+ goto done;
+ }
+
+ if (ret == ERR_INI_PARSE_FAILED) {
+ PRINT("Failed to load configuration from %s.\n",
+ config_path);
+ goto done;
+ }
+
+ if (ret == ERR_INI_ADD_SNIPPETS_FAILED) {
+ PRINT("Error while reading configuration directory.\n");
+ goto done;
+ }
+
+ /* Used snippet files */
+ ra_success = sss_ini_get_ra_success_list(init_data);
+ num_ra_success = ref_array_len(ra_success);
+ if ((sss_ini_exists(init_data) == false) && (num_ra_success == 0)) {
+ PRINT("There is no configuration.\n");
+ ret = ERR_INI_OPEN_FAILED;
+ goto done;
+ }
+
+ /* Run validators */
+ ret = sss_ini_call_validators_strs(tmp_ctx, init_data,
+ SSSDDATADIR"/cfg_rules.ini",
+ &strs, &num_errors);
+ if (ret) {
+ PRINT("Failed to run validators");
+ goto done;
+ }
+
+ PRINT("Issues identified by validators: %zu\n", num_errors);
+ for (i = 0; i < num_errors; i++) {
+ printf("%s\n", strs[i]);
+ }
+
+ printf("\n");
+
+ /* Merging issues */
+ ra_error = sss_ini_get_ra_error_list(init_data);
+ num_ra_error = ref_array_len(ra_error);
+
+ PRINT("Messages generated during configuration merging: %zu\n", num_ra_error);
+
+ i = 0;
+ while (ref_array_get(ra_error, i, &msg) != NULL) {
+ printf("%s\n", msg);
+ i++;
+ }
+
+ printf("\n");
+
+ /* Used snippets */
+ PRINT("Used configuration snippet files: %zu\n", num_ra_success);
+
+ i = 0;
+ while (ref_array_get(ra_success, i, &msg) != NULL) {
+ printf("%s\n", msg);
+ i++;
+ }
+
+ if (num_errors != 0 || num_ra_error != 0) {
+ ret = EINVAL;
+ } else {
+ ret = EOK;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+#endif /* HAVE_LIBINI_CONFIG_V1_3 */
diff --git a/src/tools/sssctl/sssctl_data.c b/src/tools/sssctl/sssctl_data.c
new file mode 100644
index 0000000..82f80c6
--- /dev/null
+++ b/src/tools/sssctl/sssctl_data.c
@@ -0,0 +1,538 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+#include "db/sysdb_private.h"
+#include "confdb/confdb.h"
+#include "confdb/confdb_private.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+
+#define SSS_BACKUP_DIR SSS_STATEDIR "/backup"
+#define SSS_BACKUP_USER_OVERRIDES SSS_BACKUP_DIR "/sssd_user_overrides.bak"
+#define SSS_BACKUP_GROUP_OVERRIDES SSS_BACKUP_DIR "/sssd_group_overrides.bak"
+#define SSS_CACHE "sss_cache"
+
+struct sssctl_data_opts {
+ int override;
+ int restore;
+ int start;
+ int stop;
+ int restart;
+};
+
+static errno_t sssctl_create_backup_dir(const char *path)
+{
+ mode_t old_umask;
+ errno_t ret;
+
+ old_umask = umask(SSS_DFL_X_UMASK);
+ ret = mkdir(path, 0700);
+ umask(old_umask);
+ if (ret != EOK && errno != EEXIST) {
+ ret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to create backup directory "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOK;
+}
+
+static bool sssctl_backup_file_exists(const char *file)
+{
+ return access(file, F_OK) == 0;
+}
+
+static bool sssctl_backup_exist(const char **files)
+{
+ int i;
+
+ for (i = 0; files[i] != NULL; i++) {
+ if (sssctl_backup_file_exists(files[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static errno_t sssctl_backup(bool force)
+{
+ const char *files[] = {SSS_BACKUP_USER_OVERRIDES,
+ SSS_BACKUP_GROUP_OVERRIDES,
+ NULL};
+ enum sssctl_prompt_result prompt;
+ errno_t ret;
+
+ ret = sssctl_create_backup_dir(SSS_BACKUP_DIR);
+ if (ret != EOK) {
+ ERROR("Unable to create backup directory [%d]: %s",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ if (sssctl_backup_exist(files) && !force) {
+ prompt = sssctl_prompt(_("SSSD backup of local data already exists, "
+ "override?"), SSSCTL_PROMPT_NO);
+ switch (prompt) {
+ case SSSCTL_PROMPT_YES:
+ /* continue */
+ break;
+ case SSSCTL_PROMPT_NO:
+ return EEXIST;
+ case SSSCTL_PROMPT_ERROR:
+ return EIO;
+ }
+ }
+
+ ret = sssctl_run_command((const char *[]){"sss_override", "user-export",
+ SSS_BACKUP_USER_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to export user overrides\n");
+ return ret;
+ }
+
+ ret = sssctl_run_command((const char *[]){"sss_override", "group-export",
+ SSS_BACKUP_GROUP_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to export group overrides\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+errno_t sssctl_client_data_backup(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"override", 'o', POPT_ARG_NONE, &opts.override, 0, _("Override existing backup"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ ret = sssctl_backup(opts.override);
+ if (ret == EEXIST) {
+ return EOK;
+ }
+
+ return ret;
+}
+
+static errno_t sssctl_restore(bool force_start, bool force_restart)
+{
+ errno_t ret;
+
+ if (!sssctl_start_sssd(force_start)) {
+ return ERR_SSSD_NOT_RUNNING;
+ }
+
+ if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+ ret = sssctl_run_command((const char *[]){"sss_override", "user-import",
+ SSS_BACKUP_USER_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to import user overrides\n");
+ return ret;
+ }
+ }
+
+ if (sssctl_backup_file_exists(SSS_BACKUP_USER_OVERRIDES)) {
+ ret = sssctl_run_command((const char *[]){"sss_override", "group-import",
+ SSS_BACKUP_GROUP_OVERRIDES, NULL});
+ if (ret != EOK) {
+ ERROR("Unable to import group overrides\n");
+ return ret;
+ }
+ }
+
+ sssctl_restart_sssd(force_restart);
+
+ ret = EOK;
+
+ return ret;
+}
+
+errno_t sssctl_client_data_restore(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"start", 's', POPT_ARG_NONE, &opts.start, 0, _("Start SSSD if it is not running"), NULL },
+ {"restart", 'r', POPT_ARG_NONE, &opts.restart, 0, _("Restart SSSD after data import"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ return sssctl_restore(opts.start, opts.restart);
+}
+
+errno_t sssctl_cache_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_data_opts opts = {0};
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"override", 'o', POPT_ARG_NONE, &opts.override, 0, _("Override existing backup"), NULL },
+ {"restore", 'r', POPT_ARG_NONE, &opts.restore, 0, _("Create clean cache files and import local data"), NULL },
+ {"stop", 'p', POPT_ARG_NONE, &opts.stop, 0, _("Stop SSSD before removing the cache"), NULL },
+ {"start", 's', POPT_ARG_NONE, &opts.start, 0, _("Start SSSD when the cache is removed"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (!sssctl_stop_sssd(opts.stop)) {
+ fprintf(stderr, "Unable to remove the cache unless SSSD is stopped.\n");
+ return ERR_SSSD_RUNNING;
+ }
+
+ PRINT("Creating backup of local data...\n");
+ ret = sssctl_backup(opts.override);
+ if (ret != EOK) {
+ ERROR("Unable to create backup of local data,"
+ " can not remove the cache.\n");
+ return ret;
+ }
+
+ PRINT("Removing cache files...\n");
+ ret = sss_remove_subtree(DB_PATH);
+ if (ret != EOK) {
+ ERROR("Unable to remove cache files\n");
+ return ret;
+ }
+
+ if (opts.restore) {
+ PRINT("Restoring local data...\n");
+ sssctl_restore(opts.start, opts.start);
+ } else {
+ sssctl_start_sssd(opts.start);
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_cache_upgrade(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sysdb_upgrade_ctx db_up_ctx;
+ errno_t ret;
+
+ ret = sss_tool_popt(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (sss_daemon_running()) {
+ return ERR_SSSD_RUNNING;
+ }
+
+ ret = confdb_get_domains(tool_ctx->confdb, &tool_ctx->domains);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "No domains configured.\n");
+ return ret;
+ }
+
+ db_up_ctx.cdb = tool_ctx->confdb;
+ ret = sysdb_init_ext(tool_ctx, tool_ctx->domains, &db_up_ctx,
+ true, 0, 0);
+ if (ret != EOK) {
+ SYSDB_VERSION_ERROR_DAEMON(ret);
+ return ret;
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_cache_expire(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ const char **args = talloc_array_size(tool_ctx,
+ sizeof(char *),
+ cmdline->argc + 2);
+ if (!args) {
+ return ENOMEM;
+ }
+ memcpy(&args[1], cmdline->argv, sizeof(char *) * cmdline->argc);
+ args[0] = SSS_CACHE;
+ args[cmdline->argc + 1] = NULL;
+
+ ret = sssctl_run_command(args);
+
+ talloc_free(args);
+ return ret;
+}
+
+errno_t get_confdb_domains(TALLOC_CTX *ctx, struct confdb_ctx *confdb,
+ char ***_domains)
+{
+ int ret;
+ int domain_count = 0;
+ int i;
+ struct sss_domain_info *domain = NULL;
+ struct sss_domain_info *domain_list = NULL;
+ char **domains;
+
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* get domains */
+ ret = confdb_get_domains(confdb, &domain_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain list\n");
+ goto done;
+ }
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0)) {
+ domain_count++;
+ }
+
+ /* allocate output space */
+ domains = talloc_array(tmp_ctx, char *, domain_count + 1);
+ if (domains == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for domains\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (domain = domain_list, i = 0;
+ domain != NULL;
+ domain = get_next_domain(domain, 0), i++) {
+ domains[i] = talloc_asprintf(domains, "%s", domain->name);
+ if (domains[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* add NULL to the end */
+ domains[i] = NULL;
+
+ *_domains = talloc_steal(ctx, domains);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_cache_index_action(enum sysdb_index_actions action,
+ const char **domains,
+ const char *attr)
+{
+ errno_t ret;
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct confdb_ctx *confdb = NULL;
+ char *cache;
+ const char **domain;
+ const char **index;
+ const char **indexes = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to allocate the context\n");
+ return ENOMEM;
+ }
+
+ if (domains == NULL) {
+ /* If the user selected no domain, act on all of them */
+ ret = sss_tool_connect_to_confdb(tmp_ctx, &confdb);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Could not connect to configuration database.\n");
+ goto done;
+ }
+
+ ret = get_confdb_domains(tmp_ctx, confdb, discard_const(&domains));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not list all the domains.\n");
+ goto done;
+ }
+ }
+
+ for (domain = domains; *domain != NULL; domain++) {
+ if (action == SYSDB_IDX_CREATE) {
+ PRINT("Creating cache index for domain %1$s\n", *domain);
+ } else if (action == SYSDB_IDX_DELETE) {
+ PRINT("Deleting cache index for domain %1$s\n", *domain);
+ } else if (action == SYSDB_IDX_LIST) {
+ PRINT("Indexes for domain %1$s:\n", *domain);
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Invalid action: %i\n", action);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = sysdb_get_db_file(tmp_ctx, NULL, *domain, DB_PATH, &cache, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get the cache db name\n");
+ goto done;
+ }
+
+ ret = sysdb_manage_index(tmp_ctx, action, cache, attr, &indexes);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (action == SYSDB_IDX_LIST) {
+ for (index = indexes; *index != NULL; index++) {
+ PRINT(" Attribute: %1$s\n", *index);
+ }
+ talloc_zfree(indexes);
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sssctl_cache_index(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *attr = NULL;
+ const char *action_str = NULL;
+ const char **domains = NULL;
+ const char **p;
+ enum sysdb_index_actions action;
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ { "domain", 'd', POPT_ARG_ARGV, &domains,
+ 0, _("Target a specific domain"), _("domain") },
+ { "attribute", 'a', POPT_ARG_STRING, &attr,
+ 0, _("Attribute to index"), _("attribute") },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+ "ACTION", "create | delete | list",
+ SSS_TOOL_OPT_REQUIRED, &action_str, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ if (action_str == NULL) {
+ ERROR("Action not provided\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (strcmp(action_str, "list") == 0) {
+ action = SYSDB_IDX_LIST;
+ } else {
+ if (strcmp(action_str, "create") == 0) {
+ action = SYSDB_IDX_CREATE;
+ } else if (strcmp(action_str, "delete") == 0) {
+ action = SYSDB_IDX_DELETE;
+ } else {
+ ERROR("Unknown action: %1$s\nValid actions are "
+ "\"%2$s\", \"%3$s and \"%4$s\"\n",
+ action_str, "create", "delete", "list");
+ ret = EINVAL;
+ goto done;
+ }
+
+ if (attr == NULL) {
+ ERROR("Attribute (-a) not provided\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = sssctl_cache_index_action(action, domains, attr);
+ if (ret == ENOENT) {
+ ERROR("Attribute %1$s not indexed.\n", attr);
+ goto done;
+ } if (ret == EEXIST) {
+ ERROR("Attribute %1$s already indexed.\n", attr);
+ goto done;
+ } else if (ret != EOK) {
+ ERROR("Index operation failed: %1$s\n", sss_strerror(ret));
+ goto done;
+ }
+
+ if (action != SYSDB_IDX_LIST) {
+ PRINT("Don't forget to also update the indexes on the remote providers.\n");
+ }
+
+ ret = EOK;
+
+done:
+ free(discard_const(action_str));
+ free(discard_const(attr));
+ if (domains != NULL) {
+ for (p = domains; *p != NULL; p++) {
+ free(discard_const(*p));
+ }
+ free(discard_const(domains));
+ }
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_domains.c b/src/tools/sssctl/sssctl_domains.c
new file mode 100644
index 0000000..ee8cb13
--- /dev/null
+++ b/src/tools/sssctl/sssctl_domains.c
@@ -0,0 +1,408 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "sbus/sbus_opath.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+static errno_t
+sssctl_domain_list_get_properties(TALLOC_CTX *mem_ctx,
+ struct sbus_sync_connection *conn,
+ const char *path,
+ const char **_name,
+ bool *_is_subdom)
+{
+ errno_t ret;
+
+ if (_name != NULL) {
+ ret = sbus_get_ifp_domains_name(mem_ctx, conn, IFP_BUS, path, _name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (_is_subdom != NULL) {
+ ret = sbus_get_ifp_domains_subdomain(conn, IFP_BUS, path, _is_subdom);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain property [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ }
+
+ return ret;
+}
+
+errno_t sssctl_domain_list(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char **paths;
+ const char *name;
+ bool is_subdom;
+ int start = 0;
+ int verbose = 0;
+ errno_t ret;
+ int i;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"start", 's', POPT_ARG_NONE, &start, 0, _("Start SSSD if it is not running"), NULL },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, _("Show domain list including primary or trusted domain type"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (!sssctl_start_sssd(start)) {
+ return ERR_SSSD_NOT_RUNNING;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_ListDomains(tmp_ctx, conn, IFP_BUS, IFP_PATH, &paths);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to list domains [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (verbose) {
+ for (i = 0; paths[i] != NULL; i++) {
+ ret = sssctl_domain_list_get_properties(tmp_ctx, conn, paths[i],
+ &name, &is_subdom);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (is_subdom) {
+ printf("Trusted domain: %s\n", name);
+ } else {
+ printf("Primary domain: %s\n", name);
+ }
+ }
+
+ return EOK;
+ }
+
+ for (i = 0; paths[i] != NULL; i++) {
+ ret = sssctl_domain_list_get_properties(tmp_ctx, conn, paths[i],
+ &name, NULL);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ puts(name);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static errno_t
+sssctl_domain_status_online(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ bool is_online;
+ errno_t ret;
+
+ ret = sbus_call_ifp_domain_IsOnline(conn, IFP_BUS, domain_path, &is_online);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain status [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ return ret;
+ }
+
+ PRINT("Online status: %s\n", is_online ? _("Online") : _("Offline"));
+
+ return EOK;
+}
+
+static const char *proper_service_name(const char *service)
+{
+ if (strcasecmp(service, "AD_GC") == 0) {
+ return "AD Global Catalog";
+ } else if (strcasecmp(service, "AD") == 0) {
+ return "AD Domain Controller";
+ } else if (strncasecmp(service, "sd_gc_", strlen("sd_gc_")) == 0) {
+ return "AD Global Catalog";
+ } else if (strncasecmp(service, "sd_", strlen("sd_")) == 0) {
+ return "AD Domain Controller";
+ }
+
+ return service;
+}
+
+static errno_t
+sssctl_domain_status_active_server(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char *server;
+ const char **services;
+ errno_t ret;
+ int i;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sbus_call_ifp_domain_ListServices(tmp_ctx, conn, IFP_BUS,
+ domain_path, &services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain services [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (services == NULL) {
+ PRINT("This domain has no active servers.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ PRINT("Active servers:\n");
+ for (i = 0; services[i] != NULL; i++) {
+ ret = sbus_call_ifp_domain_ActiveServer(tmp_ctx, conn, IFP_BUS,
+ domain_path, services[i], &server);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get active server [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ /* SBUS_REQ_STRING_DEFAULT handles (server == NULL) case gracefully */
+ server = SBUS_REQ_STRING_DEFAULT(server, _("not connected"));
+ printf("%s: %s\n", proper_service_name(services[i]), server);
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t
+sssctl_domain_status_server_list(struct sbus_sync_connection *conn,
+ const char *domain_path)
+{
+ TALLOC_CTX *tmp_ctx;
+ const char **servers;
+ const char **services;
+ errno_t ret;
+ int i, j;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = sbus_call_ifp_domain_ListServices(tmp_ctx, conn, IFP_BUS,
+ domain_path, &services);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain services [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (services == NULL) {
+ PRINT("No servers discovered.\n");
+ ret = EOK;
+ goto done;
+ }
+
+ for (i = 0; services[i] != NULL; i++) {
+ PRINT("Discovered %s servers:\n", proper_service_name(services[i]));
+
+ ret = sbus_call_ifp_domain_ListServers(tmp_ctx, conn, IFP_BUS,
+ domain_path, services[i], &servers);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain servers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ if (servers == NULL || servers[0] == NULL) {
+ PRINT("None so far.\n");
+ continue;
+ }
+
+ for (j = 0; servers[j] != NULL; j++) {
+ printf("- %s\n", servers[j]);
+ }
+
+ printf("\n");
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+struct sssctl_domain_status_opts {
+ const char *domain;
+ int online;
+ int last;
+ int active;
+ int servers;
+ int force_start;
+};
+
+errno_t sssctl_domain_status(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ struct sssctl_domain_status_opts opts = {0};
+ struct sbus_sync_connection *conn;
+ const char *path;
+ bool opt_set;
+ errno_t ret;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"online", 'o', POPT_ARG_NONE , &opts.online, 0, _("Show online status"), NULL },
+ {"active-server", 'a', POPT_ARG_NONE, &opts.active, 0, _("Show information about active server"), NULL },
+ {"servers", 'r', POPT_ARG_NONE, &opts.servers, 0, _("Show list of discovered servers"), NULL },
+ {"start", 's', POPT_ARG_NONE, &opts.force_start, 0, _("Start SSSD if it is not running"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "DOMAIN", _("Specify domain name."),
+ SSS_TOOL_OPT_REQUIRED, &opts.domain, &opt_set);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ if (opt_set == false) {
+ opts.online = true;
+ opts.last = true;
+ opts.active = true;
+ opts.servers = true;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ path = sbus_opath_compose(tmp_ctx, IFP_PATH_DOMAINS, opts.domain);
+ if (path == NULL) {
+ PRINT("Out of memory!\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (!sssctl_start_sssd(opts.force_start)) {
+ ret = ERR_SSSD_NOT_RUNNING;
+ goto done;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ if (opts.online) {
+ ret = sssctl_domain_status_online(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get online status\n");
+ goto done;
+ }
+
+ printf("\n");
+ }
+
+ if (opts.active) {
+ ret = sssctl_domain_status_active_server(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get online status\n");
+ goto done;
+ }
+
+ printf("\n");
+ }
+
+ if (opts.servers) {
+ ret = sssctl_domain_status_server_list(conn, path);
+ if (ret != EOK) {
+ ERROR("Unable to get server list\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ free(discard_const(opts.domain));
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_logs.c b/src/tools/sssctl/sssctl_logs.c
new file mode 100644
index 0000000..f8f5a65
--- /dev/null
+++ b/src/tools/sssctl/sssctl_logs.c
@@ -0,0 +1,608 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <limits.h>
+#include <talloc.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <signal.h>
+#include <utime.h>
+#include <ldb.h>
+#include <popt.h>
+#include <stdio.h>
+#include <glob.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/common/sss_process.h"
+#include "tools/sssctl/sssctl.h"
+#include "tools/tools_util.h"
+#include "confdb/confdb.h"
+#include "sss_iface/sss_iface_sync.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#define LOG_FILE(file) " " LOG_PATH "/" file
+#define LOG_FILES LOG_FILE("*.log")
+#define SSS_ANALYZE SSSD_LIBEXEC_PATH"/sss_analyze"
+
+#define CHECK(expr, done, msg) do { \
+ if (expr) { \
+ ERROR(msg "\n"); \
+ goto done; \
+ } \
+} while(0)
+
+#define POPT_SERV_OPTION(NAME, VAR, DESC) \
+ {services[SERV_ ## NAME].name, '\0', POPT_BIT_SET, &VAR, \
+ services[SERV_ ## NAME].mask, DESC, NULL}
+
+#define STARTS_WITH(s, p) (strncmp((s), (p), strlen(p)) == 0)
+#define REMOVE_PREFIX(s, p) (STARTS_WITH(s, p) ? (s) + strlen(p) : (s))
+#define IS_DOMAIN(c) STARTS_WITH((c), "domain/")
+#define DOMAIN_NAME(c) REMOVE_PREFIX((c), "domain/")
+#define EMPTY_TARGETS(t) ((t)[0] == NULL)
+
+enum debug_level_action {
+ ACTION_SET,
+ ACTION_GET
+};
+
+struct debuglevel_tool_ctx {
+ struct confdb_ctx *confdb;
+ char **sections;
+};
+
+struct sssctl_logs_opts {
+ int delete;
+ int archived;
+};
+
+struct sssctl_service_desc {
+ const char *name;
+ int mask;
+};
+
+enum serv_idx {
+ SERV_SSSD,
+ SERV_NSS,
+ SERV_PAM,
+ SERV_SUDO,
+ SERV_AUTOFS,
+ SERV_SSH,
+ SERV_PAC,
+ SERV_IFP,
+ SERV_COUNT
+};
+
+struct sssctl_service_desc services[] = {
+ { "sssd", 1U << SERV_SSSD },
+ { "nss", 1U << SERV_NSS },
+ { "pam", 1U << SERV_PAM },
+ { "sudo", 1U << SERV_SUDO },
+ { "autofs", 1U << SERV_AUTOFS},
+ { "ssh", 1U << SERV_SSH },
+ { "pac", 1U << SERV_PAC },
+ { "ifp", 1U << SERV_IFP }
+};
+
+static struct sbus_sync_connection *connect_to_sbus(TALLOC_CTX *mem_ctx)
+{
+ struct sbus_sync_connection *conn;
+
+ conn = sbus_sync_connect_private(mem_ctx, SSS_MONITOR_ADDRESS, NULL);
+ if (conn == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to connect to the sbus monitor\n");
+ }
+
+ return conn;
+}
+
+static const char *get_busname(TALLOC_CTX *mem_ctx, struct confdb_ctx *confdb,
+ const char *component)
+{
+ errno_t ret;
+ const char *busname;
+ struct sss_domain_info *domain;
+
+ if (strcmp(component, "sssd") == 0) {
+ busname = talloc_strdup(mem_ctx, SSS_BUS_MONITOR);
+ } else if (IS_DOMAIN(component)) {
+ ret = confdb_get_domain(confdb, DOMAIN_NAME(component), &domain);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown domain: %s\n", component);
+ busname = NULL;
+ goto done;
+ }
+
+ busname = sss_iface_domain_bus(mem_ctx, domain);
+ } else {
+ busname = talloc_asprintf(mem_ctx, "sssd.%s", component);
+ }
+
+done:
+ return busname;
+}
+
+/* in_out_value is an input argument when action is ACTION_SET; it is an output
+ * argument when action is ACTION_GET. */
+static errno_t do_debug_level(enum debug_level_action action,
+ struct sbus_sync_connection *conn,
+ struct confdb_ctx *confdb,
+ const char *component,
+ uint32_t *in_out_value)
+{
+ errno_t ret;
+ uint32_t value;
+ const char *busname;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ busname = get_busname(tmp_ctx, confdb, component);
+ if (busname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to create the bus name for %s\n",
+ component);
+ }
+
+ if (action == ACTION_GET) {
+ ret = sbus_get_service_debug_level(conn, busname, SSS_BUS_PATH, &value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ *in_out_value = value;
+ } else {
+ ret = sbus_set_service_debug_level(conn, busname, SSS_BUS_PATH,
+ *in_out_value);
+ if (ret != EOK) {
+ ret = ENOENT;
+ goto done;
+ }
+ }
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static errno_t sssctl_do_debug_level(enum debug_level_action action,
+ struct debuglevel_tool_ctx *tool_ctx,
+ const char **targets,
+ uint32_t debug_to_set)
+{
+ bool all_targets = EMPTY_TARGETS(targets);
+ errno_t ret = EOK;
+ errno_t final_ret = EOK;
+ uint32_t current_level = SSSDBG_INVALID;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+ const char *stripped_target;
+ const char **curr_target;
+ struct sbus_sync_connection *conn;
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ conn = connect_to_sbus(tmp_ctx);
+ if (conn == NULL) {
+ ERROR("SSSD is not running.\n");
+ ret = EIO;
+ goto fini;
+ }
+
+ curr_target = (all_targets ?
+ discard_const_p(const char *, tool_ctx->sections) : targets);
+ while (*curr_target != NULL) {
+ stripped_target = REMOVE_PREFIX(*curr_target, "config/");
+
+ if (action == ACTION_GET) {
+ ret = do_debug_level(ACTION_GET, conn, tool_ctx->confdb,
+ stripped_target, &current_level);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not read the debug level.");
+
+ if (ret == EOK) {
+ PRINT(_("%1$-25s %2$#.4x\n"), stripped_target, current_level);
+ } else {
+ if (!all_targets) {
+ if (IS_DOMAIN(stripped_target)) {
+ PRINT(_("%1$-25s Unknown domain\n"), stripped_target);
+ } else {
+ PRINT(_("%1$-25s Unreachable service\n"), stripped_target);
+ }
+ final_ret = ENOENT;
+ }
+ }
+ } else {
+ ret = do_debug_level(ACTION_SET, conn, tool_ctx->confdb,
+ stripped_target, &debug_to_set);
+ CHECK(ret != EOK && ret != ENOENT, fini,
+ "Could not set the debug level.");
+ if (ret == ENOENT && !all_targets) {
+ final_ret = ret;
+ }
+ }
+ curr_target++;
+ }
+
+ if (ret == EOK) {
+ ret = final_ret;
+ }
+
+fini:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t get_confdb_sections(TALLOC_CTX *ctx, struct confdb_ctx *confdb,
+ char ***output_sections)
+{
+ int ret;
+ int domain_count = 0;
+ int i = 0;
+ struct sss_domain_info *domain = NULL;
+ struct sss_domain_info *domain_list = NULL;
+ char **sections;
+ const char *known_services[] = {
+ CONFDB_MONITOR_CONF_ENTRY,
+ CONFDB_NSS_CONF_ENTRY,
+ CONFDB_PAM_CONF_ENTRY,
+ CONFDB_PAC_CONF_ENTRY,
+ CONFDB_SSH_CONF_ENTRY,
+ CONFDB_SUDO_CONF_ENTRY,
+ CONFDB_AUTOFS_CONF_ENTRY,
+ CONFDB_IFP_CONF_ENTRY,
+ };
+ static const int known_services_count = sizeof(known_services)
+ / sizeof(*known_services);
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ /* get domains */
+ ret = confdb_get_domains(confdb, &domain_list);
+ if (ret != EOK)
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get domain list\n");
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0)) {
+ domain_count++;
+ }
+
+ /* allocate output space */
+ sections = talloc_array(ctx, char *,
+ domain_count + known_services_count + 1);
+ if (sections == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for sections\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < known_services_count; i++) {
+ sections[i] = talloc_strdup(tmp_ctx, known_services[i]);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ for (domain = domain_list;
+ domain;
+ domain = get_next_domain(domain, 0), i++) {
+ sections[i] = talloc_asprintf(tmp_ctx, CONFDB_DOMAIN_PATH_TMPL,
+ domain->name);
+ if (sections[i] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf() failed\n");
+ ret = ENOMEM;
+ goto fail;
+ }
+ }
+
+ /* add NULL to the end */
+ sections[i] = NULL;
+
+ *output_sections = talloc_steal(ctx, sections);
+
+ return EOK;
+fail:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static const char **get_targets(TALLOC_CTX *mem_ctx, int services_mask,
+ const char **domainv)
+{
+ int i;
+ int count = 1;
+ const char **targets = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_new(NULL);
+
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return NULL;
+ }
+
+ targets = talloc_zero_array(tmp_ctx, const char *, count);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+
+ if (services_mask != 0) {
+ for (i = 0; i < SERV_COUNT; i++) {
+ if (services_mask == 0 || (services_mask & services[i].mask) != 0) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ targets[count - 1] = talloc_strdup(tmp_ctx, services[i].name);
+ targets[count++] = NULL;
+ }
+ }
+ }
+
+ if (domainv != NULL) {
+ for (; *domainv != NULL; domainv++) {
+ targets = talloc_realloc(tmp_ctx, targets, const char *, count + 1);
+ if (targets == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for the list of targets\n");
+ goto done;
+ }
+ if (IS_DOMAIN(*domainv)) {
+ targets[count - 1] = talloc_strdup(tmp_ctx, *domainv);
+ } else {
+ targets[count - 1] = talloc_asprintf(tmp_ctx, "domain/%s", *domainv);
+ }
+ targets[count++] = NULL;
+ }
+ }
+
+ targets = talloc_steal(mem_ctx, targets);
+ for (i = 0; i < count; i++) {
+ targets[i] = talloc_steal(mem_ctx, targets[i]);
+ }
+
+done:
+ talloc_free(tmp_ctx);
+ return targets;
+}
+
+int parse_debug_level(const char *strlevel)
+{
+ long value;
+ char *endptr;
+
+ errno = 0;
+ value = strtol(strlevel, &endptr, 0);
+ if ((errno != 0) || (endptr == strlevel) || (*endptr != '\0')) {
+ return SSSDBG_INVALID;
+ }
+
+ return debug_convert_old_level(value);
+}
+
+errno_t sssctl_logs_remove(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ struct sssctl_logs_opts opts = {0};
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ {"delete", 'd', POPT_ARG_NONE, &opts.delete, 0, _("Delete log files instead of truncating"), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt(cmdline, options, SSS_TOOL_OPT_OPTIONAL, NULL, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ return ret;
+ }
+
+ if (opts.delete) {
+ PRINT("Deleting log files...\n");
+ ret = sss_remove_subtree(LOG_PATH);
+ if (ret != EOK) {
+ ERROR("Unable to remove log files\n");
+ return ret;
+ }
+
+ sss_signal(SIGHUP);
+ } else {
+ globbuf.gl_offs = 4;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ return ret;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "truncate");
+ globbuf.gl_pathv[1] = discard_const_p(char, "--no-create");
+ globbuf.gl_pathv[2] = discard_const_p(char, "--size");
+ globbuf.gl_pathv[3] = discard_const_p(char, "0");
+
+ PRINT("Truncating log files...\n");
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to truncate log files\n");
+ return ret;
+ }
+ }
+
+ return EOK;
+}
+
+errno_t sssctl_logs_fetch(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ const char *file = NULL;
+ errno_t ret;
+ glob_t globbuf;
+
+ /* Parse command line. */
+ ret = sss_tool_popt_ex(cmdline, NULL, SSS_TOOL_OPT_OPTIONAL, NULL, NULL,
+ "FILE", "Output file", SSS_TOOL_OPT_REQUIRED,
+ &file, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ globbuf.gl_offs = 3;
+ ret = glob(LOG_PATH"/*.log", GLOB_ERR|GLOB_DOOFFS, NULL, &globbuf);
+ if (ret != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to expand log files list\n");
+ goto done;
+ }
+ globbuf.gl_pathv[0] = discard_const_p(char, "tar");
+ globbuf.gl_pathv[1] = discard_const_p(char, "-czf");
+ globbuf.gl_pathv[2] = discard_const_p(char, file);
+
+ PRINT("Archiving log files into %s...\n", file);
+ ret = sssctl_run_command((const char * const*)globbuf.gl_pathv);
+ globfree(&globbuf);
+ if (ret != EOK) {
+ ERROR("Unable to archive log files\n");
+ goto done;
+ }
+
+done:
+ free(discard_const(file));
+
+ return ret;
+}
+
+errno_t sssctl_debug_level(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ int ret;
+ int pc_services = 0;
+ uint32_t debug_to_set = SSSDBG_INVALID;
+ const char **pc_domains = NULL;
+ const char **targets = NULL;
+ const char *debug_as_string = NULL;
+
+ struct debuglevel_tool_ctx *ctx = NULL;
+ struct poptOption long_options[] = {
+ {"domain", '\0', POPT_ARG_ARGV, &pc_domains,
+ 0, _("Target a specific domain"), _("domain")},
+ POPT_SERV_OPTION(SSSD, pc_services, _("Target the SSSD service")),
+ POPT_SERV_OPTION(NSS, pc_services, _("Target the NSS service")),
+ POPT_SERV_OPTION(PAM, pc_services, _("Target the PAM service")),
+ POPT_SERV_OPTION(SUDO, pc_services, _("Target the SUDO service")),
+ POPT_SERV_OPTION(AUTOFS, pc_services, _("Target the AUTOFS service")),
+ POPT_SERV_OPTION(SSH, pc_services, _("Target the SSH service")),
+ POPT_SERV_OPTION(PAC, pc_services, _("Target the PAC service")),
+ POPT_SERV_OPTION(IFP, pc_services, _("Target the IFP service")),
+ POPT_TABLEEND
+ };
+
+ /* allocate context */
+ ctx = talloc_zero(NULL, struct debuglevel_tool_ctx);
+ if (ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Could not allocate memory for tools context\n");
+ ret = ENOMEM;
+ goto fini;
+ }
+
+ ret = sss_tool_popt_ex(cmdline, long_options, SSS_TOOL_OPT_OPTIONAL, NULL,
+ NULL, "DEBUG_LEVEL_TO_SET",
+ _("Specify debug level you want to set"),
+ SSS_TOOL_OPT_OPTIONAL, &debug_as_string, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto fini;
+ }
+
+ CHECK_ROOT(ret, debug_prg_name);
+
+ if (debug_as_string != NULL) {
+ debug_to_set = (uint32_t) parse_debug_level(debug_as_string);
+ CHECK(debug_to_set == SSSDBG_INVALID, fini, "Invalid debug level.");
+ }
+
+ /* Create a list with all the target names (services + domains) */
+ targets = get_targets(ctx, pc_services, pc_domains);
+ CHECK(targets == NULL, fini, "Could not allocate memory.");
+
+ ret = sss_tool_connect_to_confdb(ctx, &ctx->confdb);
+ CHECK(ret != EOK, fini, "Could not connect to configuration database.");
+
+ ret = get_confdb_sections(ctx, ctx->confdb, &ctx->sections);
+ CHECK(ret != EOK, fini, "Could not get all configuration sections.");
+
+ if (debug_as_string == NULL) {
+ ret = sssctl_do_debug_level(ACTION_GET, ctx, targets, 0);
+ } else {
+ ret = sssctl_do_debug_level(ACTION_SET, ctx, targets, debug_to_set);
+ }
+
+ /* Only report missing components that the user requested,
+ except for the monitor (sssd not running) */
+ if (ret != ENOENT && ret != EIO && EMPTY_TARGETS(targets)) {
+ ret = EOK;
+ }
+
+fini:
+ talloc_free(ctx);
+ free(discard_const(debug_as_string));
+
+ return ret;
+}
+
+errno_t sssctl_analyze(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+#ifndef BUILD_CHAIN_ID
+ PRINT("ERROR: Tevent chain ID support missing, log analyzer is unsupported.\n");
+ return EOK;
+#endif
+ errno_t ret;
+
+ ret = sssctl_wrap_command(SSS_ANALYZE, NULL, cmdline, tool_ctx, pvt);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_passkey.c b/src/tools/sssctl/sssctl_passkey.c
new file mode 100644
index 0000000..34cab00
--- /dev/null
+++ b/src/tools/sssctl/sssctl_passkey.c
@@ -0,0 +1,42 @@
+/*
+ Authors:
+ Justin Stephenson <jstephen@redhat.com>
+
+ Passkey related utilities
+
+ Copyright (C) 2022 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <stdio.h>
+#include <talloc.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+
+#define SSS_PASSKEY_CHILD SSSD_LIBEXEC_PATH"/passkey_child"
+
+errno_t sssctl_passkey_register(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+ errno_t ret;
+
+ ret = sssctl_wrap_command(SSS_PASSKEY_CHILD, "--register", cmdline, tool_ctx, pvt);
+
+ return ret;
+}
diff --git a/src/tools/sssctl/sssctl_systemd.c b/src/tools/sssctl/sssctl_systemd.c
new file mode 100644
index 0000000..1d30558
--- /dev/null
+++ b/src/tools/sssctl/sssctl_systemd.c
@@ -0,0 +1,95 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <dbus/dbus.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "tools/sssctl/sssctl.h"
+#include "sss_iface/sss_iface_sync.h"
+
+#define SSS_SYSTEMD_BUS "org.freedesktop.systemd1"
+#define SSS_SYSTEMD_PATH "/org/freedesktop/systemd1"
+#define SSS_SYSTEMD_UNIT "sssd.service"
+#define SSS_SYSTEMD_MODE "replace" /* replace queued job if present */
+
+typedef errno_t
+(*systemd_method)(TALLOC_CTX *, struct sbus_sync_connection *,
+ const char *, const char *, const char *, const char *,
+ const char **);
+
+static errno_t sssctl_systemd_call(systemd_method method)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ const char *job;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_FATAL_FAILURE, "Out of memory!\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = method(tmp_ctx, conn, SSS_SYSTEMD_BUS,
+ SSS_SYSTEMD_PATH, SSS_SYSTEMD_UNIT,
+ SSS_SYSTEMD_MODE, &job);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "systemd operation failed [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "New systemd job created: %s\n", job);
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t sssctl_systemd_start(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Starting SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_StartUnit);
+}
+
+errno_t sssctl_systemd_stop(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Stopping SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_StopUnit);
+}
+
+errno_t sssctl_systemd_restart(void)
+{
+ DEBUG(SSSDBG_TRACE_FUNC, "Restarting SSSD via systemd...\n");
+
+ return sssctl_systemd_call(sbus_call_systemd_RestartUnit);
+}
diff --git a/src/tools/sssctl/sssctl_user_checks.c b/src/tools/sssctl/sssctl_user_checks.c
new file mode 100644
index 0000000..41cf765
--- /dev/null
+++ b/src/tools/sssctl/sssctl_user_checks.c
@@ -0,0 +1,321 @@
+/*
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <dlfcn.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <nss.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include <security/pam_appl.h>
+
+#include "util/util.h"
+#include "tools/common/sss_tools.h"
+#include "tools/sssctl/sssctl.h"
+#include "responder/ifp/ifp_iface/ifp_iface_sync.h"
+
+#ifdef HAVE_SECURITY_PAM_MISC_H
+# include <security/pam_misc.h>
+#elif defined(HAVE_SECURITY_OPENPAM_H)
+# include <security/openpam.h>
+#endif
+
+#ifdef HAVE_SECURITY_PAM_MISC_H
+static struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+#elif defined(HAVE_SECURITY_OPENPAM_H)
+static struct pam_conv conv = {
+ openpam_ttyconv,
+ NULL
+};
+#else
+# error "Missing text based pam conversation function"
+#endif
+
+#define DEFAULT_ACTION "acct"
+#define DEFAULT_SERVICE "system-auth"
+
+#define DEFAULT_BUFSIZE 4096
+
+#define PRINT_IFP_PROPERTY(all, name, fmt) do { \
+ if (all->name.is_set) { \
+ fprintf(stdout, " - %s: %" fmt "\n", #name, user->name.value); \
+ } else { \
+ fprintf(stdout, " - %s: not set\n", #name); \
+ } \
+} while (0)
+
+static errno_t get_ifp_user(const char *username)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct sbus_sync_connection *conn;
+ struct sbus_all_ifp_user *user;
+ const char *path;
+ struct hash_iter_context_t *extra_iter;
+ char **extra_values;
+ hash_entry_t *extra_entry;
+ int extra_idx;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ conn = sbus_sync_connect_system(tmp_ctx, NULL);
+ if (conn == NULL) {
+ ERROR("Unable to connect to system bus!\n");
+ ret = EIO;
+ goto done;
+ }
+
+ ret = sbus_call_ifp_users_FindByName(tmp_ctx, conn, IFP_BUS, IFP_PATH_USERS,
+ username, &path);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find user by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ ret = sbus_getall_ifp_user(tmp_ctx, conn, IFP_BUS, path, &user);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to get user properties [%d]: %s\n",
+ ret, sss_strerror(ret));
+ PRINT_IFP_WARNING(ret);
+ goto done;
+ }
+
+ PRINT("SSSD InfoPipe user lookup result:\n");
+ PRINT_IFP_PROPERTY(user, name, "s");
+ PRINT_IFP_PROPERTY(user, uidNumber, PRIu32);
+ PRINT_IFP_PROPERTY(user, gidNumber, PRIu32);
+ PRINT_IFP_PROPERTY(user, gecos, "s");
+ PRINT_IFP_PROPERTY(user, homeDirectory, "s");
+ PRINT_IFP_PROPERTY(user, loginShell, "s");
+
+ /* print extra attributes */
+ if (user->extraAttributes.is_set) {
+ extra_iter = new_hash_iter_context(user->extraAttributes.value);
+ if (extra_iter == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "new_hash_iter_context failed.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ while ((extra_entry = extra_iter->next(extra_iter)) != NULL) {
+ extra_values = extra_entry->value.ptr;
+ for(extra_idx = 0; extra_values[extra_idx] != NULL; ++extra_idx) {
+ fprintf(stdout, " - %s: %s\n", extra_entry->key.str, extra_values[extra_idx]);
+ }
+ }
+ }
+
+ fprintf(stdout, "\n");
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int sss_getpwnam_check(const char *user)
+{
+ void *dl_handle = NULL;
+ enum nss_status (*sss_getpwnam_r)(const char *name, struct passwd *result,
+ char *buffer, size_t buflen,
+ int *errnop);
+ struct passwd pwd = { 0 };
+ enum nss_status status;
+ char *buffer = NULL;
+ size_t buflen;
+ int nss_errno;
+ int ret;
+
+ dl_handle = dlopen("libnss_sss.so.2", RTLD_NOW);
+ if (dl_handle == NULL) {
+ ERROR("dlopen failed with [%s].\n", dlerror());
+ ret = EIO;
+ goto done;
+ }
+
+ sss_getpwnam_r = dlsym(dl_handle, "_nss_sss_getpwnam_r");
+ if (sss_getpwnam_r == NULL) {
+ ERROR("dlsym failed with [%s].\n", dlerror());
+ ret = EIO;
+ goto done;
+ }
+
+ buflen = DEFAULT_BUFSIZE;
+ buffer = malloc(buflen);
+ if (buffer == NULL) {
+ ERROR("malloc failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ status = sss_getpwnam_r(user, &pwd, buffer, buflen, &nss_errno);
+ if (status != NSS_STATUS_SUCCESS) {
+ ERROR("sss_getpwnam_r failed with [%d].\n", status);
+ ret = EIO;
+ goto done;
+ }
+
+ PRINT("SSSD nss user lookup result:\n");
+ PRINT(" - user name: %s\n", pwd.pw_name);
+ PRINT(" - user id: %d\n", pwd.pw_uid);
+ PRINT(" - group id: %d\n", pwd.pw_gid);
+ PRINT(" - gecos: %s\n", pwd.pw_gecos);
+ PRINT(" - home directory: %s\n", pwd.pw_dir);
+ PRINT(" - shell: %s\n\n", pwd.pw_shell);
+
+ ret = 0;
+
+done:
+ if (dl_handle != NULL) {
+ dlclose(dl_handle);
+ }
+
+ free(buffer);
+
+ return ret;
+}
+
+errno_t sssctl_user_checks(struct sss_cmdline *cmdline,
+ struct sss_tool_ctx *tool_ctx,
+ void *pvt)
+{
+
+ pam_handle_t *pamh;
+ const char *user = NULL;
+ const char *action = DEFAULT_ACTION;
+ const char *service = DEFAULT_SERVICE;
+ int ret;
+ int pret;
+ const char *pam_user = NULL;
+ size_t c;
+ char **pam_env;
+
+ /* Parse command line. */
+ struct poptOption options[] = {
+ { "action", 'a', POPT_ARG_STRING, &action, 0,
+ _("PAM action [auth|acct|setc|chau|open|clos], default: "
+ DEFAULT_ACTION), NULL },
+ { "service", 's', POPT_ARG_STRING, &service, 0,
+ _("PAM service, default: " DEFAULT_SERVICE), NULL },
+ POPT_TABLEEND
+ };
+
+ ret = sss_tool_popt_ex(cmdline, options, SSS_TOOL_OPT_OPTIONAL,
+ NULL, NULL, "USERNAME", _("Specify user name."),
+ SSS_TOOL_OPT_REQUIRED, &user, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse command arguments\n");
+ goto done;
+ }
+
+ PRINT("user: %s\naction: %s\nservice: %s\n\n", user, action, service);
+
+ if (*user != '\0') {
+ ret = sss_getpwnam_check(user);
+ if (ret != 0) {
+ ERROR("User name lookup with [%s] failed.\n", user);
+ }
+
+ ret = get_ifp_user(user);
+ if (ret != 0) {
+ ERROR("InfoPipe User lookup with [%s] failed.\n", user);
+ }
+ }
+
+ ret = pam_start(service, user, &conv, &pamh);
+ if (ret != PAM_SUCCESS) {
+ ERROR("pam_start failed: %s\n", pam_strerror(pamh, ret));
+ ret = EPERM;
+ goto done;
+ }
+
+ if ( strncmp(action, "auth", 4)== 0 ) {
+ PRINT("testing pam_authenticate\n\n");
+ ret = pam_authenticate(pamh, 0);
+ pret = pam_get_item(pamh, PAM_USER, (const void **) &pam_user);
+ if (pret != PAM_SUCCESS) {
+ ERROR("pam_get_item failed: %s\n", pam_strerror(pamh, pret));
+ pam_user = "- not available -";
+ }
+ ERROR("pam_authenticate for user [%s]: %s\n\n", pam_user,
+ pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "chau", 4)== 0 ) {
+ PRINT("testing pam_chauthtok\n\n");
+ ret = pam_chauthtok(pamh, 0);
+ ERROR("pam_chauthtok: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "acct", 4)== 0 ) {
+ PRINT("testing pam_acct_mgmt\n\n");
+ ret = pam_acct_mgmt(pamh, 0);
+ ERROR("pam_acct_mgmt: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "setc", 4)== 0 ) {
+ PRINT("testing pam_setcred\n\n");
+ ret = pam_setcred(pamh, 0);
+ ERROR("pam_setcred: [%s]\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "open", 4)== 0 ) {
+ PRINT("testing pam_open_session\n\n");
+ ret = pam_open_session(pamh, 0);
+ ERROR("pam_open_session: %s\n\n", pam_strerror(pamh, ret));
+ } else if ( strncmp(action, "clos", 4)== 0 ) {
+ PRINT("testing pam_close_session\n\n");
+ ret = pam_close_session(pamh, 0);
+ ERROR("pam_close_session: %s\n\n", pam_strerror(pamh, ret));
+ } else {
+ ERROR("unknown action\n");
+ }
+
+ ERROR("PAM Environment:\n");
+ pam_env = pam_getenvlist(pamh);
+ if (pam_env != NULL && pam_env[0] != NULL) {
+ for (c = 0; pam_env[c] != NULL; c++) {
+ fprintf(stderr, " - %s\n", pam_env[c]);
+ free(pam_env[c]);
+ }
+ } else {
+ ERROR(" - no env -\n");
+ }
+ free(pam_env);
+
+ pam_end(pamh, ret);
+ ret = EOK;
+
+done:
+ free(discard_const(user));
+
+ return ret;
+}
diff --git a/src/tools/sssd_check_socket_activated_responders.c b/src/tools/sssd_check_socket_activated_responders.c
new file mode 100644
index 0000000..dbd2331
--- /dev/null
+++ b/src/tools/sssd_check_socket_activated_responders.c
@@ -0,0 +1,147 @@
+/*
+ Authors:
+ Fabiano Fidêncio <fidencio@redhat.com>
+
+ Copyright (C) 2017 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <popt.h>
+#include <stdio.h>
+
+#include "util/util.h"
+#include "util/sss_ini.h"
+#include "confdb/confdb.h"
+
+static errno_t check_socket_activated_responder(const char *responder)
+{
+ errno_t ret;
+ const char *services;
+ const char *str;
+ TALLOC_CTX *tmp_ctx;
+ struct sss_ini *init_data;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ return ENOMEM;
+ }
+
+ init_data = sss_ini_new(tmp_ctx);
+ if (init_data == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_ini_read_sssd_conf(init_data,
+ SSSD_CONFIG_FILE,
+ CONFDB_DEFAULT_CONFIG_DIR);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to read configuration: [%d] [%s]",
+ ret,
+ sss_strerror(ret));
+ goto done;
+ }
+
+ ret = sss_ini_get_cfgobj(init_data, "sssd", "services");
+
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_ini_get_cfgobj() failed [%d].\n", ret);
+ goto done;
+ }
+
+ ret = sss_ini_check_config_obj(init_data);
+ if (ret == ENOENT) {
+ /* In case there's no services' line at all, just return EOK. */
+ ret = EOK;
+ goto done;
+ }
+
+ services = sss_ini_get_string_config_value(init_data, &ret);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "sss_ini_get_string_config_value() failed [%d]\n",
+ ret);
+ goto done;
+ }
+
+ str = strstr(services, responder);
+ if (str != NULL) {
+ ret = EEXIST;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+int main(int argc, const char *argv[])
+{
+ int ret;
+ int opt;
+ poptContext pc;
+ char *responder = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"responders", 'r', POPT_ARG_STRING, &responder, 0,
+ _("The name of the responder to be checked"), NULL},
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+ }
+
+ if (responder == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ ret = check_socket_activated_responder(responder);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_DEFAULT,
+ "Misconfiguration found for the %s responder.\n"
+ "The %s responder has been configured to be socket-activated "
+ "but it's still mentioned in the services' line in %s.\n"
+ "Please, consider either adjusting your services' line in %s "
+ "or disabling the %s's socket by calling:\n"
+ "\"systemctl disable sssd-%s.socket\"",
+ responder, responder, SSSD_CONFIG_FILE, SSSD_CONFIG_FILE,
+ responder, responder);
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ poptFreeContext(pc);
+ return ret;
+}
diff --git a/src/tools/tools_mc_util.c b/src/tools/tools_mc_util.c
new file mode 100644
index 0000000..a4f3a81
--- /dev/null
+++ b/src/tools/tools_mc_util.c
@@ -0,0 +1,237 @@
+/*
+ SSSD
+
+ tools_mc_util - interface to the memcache for userspace tools
+
+ Copyright (C) Red Hat 2013
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <talloc.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "db/sysdb.h"
+#include "util/util.h"
+#include "util/mmap_cache.h"
+#include "util/sss_cli_cmd.h"
+#include "sss_client/sss_cli.h"
+#include "tools/common/sss_process.h"
+
+/* This is a copy of sss_mc_set_recycled present in
+ * src/responder/nss/nsssrv_mmap_cache.c. If you modify this function,
+ * you should modify the original function too. */
+static errno_t sss_mc_set_recycled(int fd)
+{
+ uint32_t w = SSS_MC_HEADER_RECYCLED;
+ off_t offset;
+ off_t pos;
+ ssize_t written;
+
+ offset = offsetof(struct sss_mc_header, status);
+
+ pos = lseek(fd, offset, SEEK_SET);
+ if (pos == -1) {
+ /* What do we do now? */
+ return errno;
+ }
+
+ errno = 0;
+ written = sss_atomic_write_s(fd, (uint8_t *)&w, sizeof(w));
+ if (written == -1) {
+ return errno;
+ }
+
+ if (written != sizeof(w)) {
+ /* Write error */
+ return EIO;
+ }
+
+ return EOK;
+}
+
+static errno_t sss_memcache_invalidate(const char *mc_filename)
+{
+ int mc_fd = -1;
+ errno_t ret;
+ errno_t pret;
+ useconds_t t = 50000;
+ int retries = 2;
+
+ if (!mc_filename) {
+ return EINVAL;
+ }
+
+ mc_fd = open(mc_filename, O_RDWR);
+ if (mc_fd == -1) {
+ ret = errno;
+ if (ret == ENOENT) {
+ DEBUG(SSSDBG_TRACE_FUNC,"Memory cache file %s "
+ "does not exist.\n", mc_filename);
+ return EOK;
+ } else {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s: %s\n",
+ mc_filename, strerror(ret));
+ return ret;
+ }
+ }
+
+ ret = sss_br_lock_file(mc_fd, 0, 1, retries, t);
+ if (ret == EACCES) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "File %s already locked by someone else.\n", mc_filename);
+ goto done;
+ } else if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to lock file %s.\n", mc_filename);
+ goto done;
+ }
+ /* Mark the mc file as recycled. */
+ ret = sss_mc_set_recycled(mc_fd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to mark memory cache file %s "
+ "as recycled.\n", mc_filename);
+ goto done;
+ }
+
+ ret = EOK;
+done:
+ if (mc_fd != -1) {
+ /* Closing the file also releases the lock */
+ close(mc_fd);
+
+ /* Only unlink the file if invalidation was successful */
+ if (ret == EOK) {
+ pret = unlink(mc_filename);
+ if (pret == -1) {
+ pret = errno;
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Failed to unlink file %s, %d [%s]. "
+ "Will be unlinked later by sssd_nss.\n",
+ mc_filename, pret, strerror(pret));
+ }
+ }
+ }
+ return ret;
+}
+
+static int clear_memcache(bool *sssd_nss_is_off)
+{
+ int ret;
+ ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/passwd");
+ if (ret != EOK) {
+ if (ret == EACCES) {
+ *sssd_nss_is_off = false;
+ return EOK;
+ } else {
+ return ret;
+ }
+ }
+
+ ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/group");
+ if (ret != EOK) {
+ if (ret == EACCES) {
+ *sssd_nss_is_off = false;
+ return EOK;
+ } else {
+ return ret;
+ }
+ }
+
+ ret = sss_memcache_invalidate(SSS_NSS_MCACHE_DIR"/initgroups");
+ if (ret != EOK) {
+ if (ret == EACCES) {
+ *sssd_nss_is_off = false;
+ return EOK;
+ } else {
+ return ret;
+ }
+ }
+
+ *sssd_nss_is_off = true;
+ return EOK;
+}
+
+static errno_t wait_till_nss_responder_invalidate_cache(void)
+{
+ struct stat stat_buf = { 0 };
+ const time_t max_wait = 1000000; /* 1 second */
+ const __useconds_t step_time = 5000; /* 5 milliseconds */
+ const size_t steps_count = max_wait / step_time;
+ int ret;
+
+ for (size_t i = 0; i < steps_count; ++i) {
+ ret = stat(SSS_NSS_MCACHE_DIR "/" CLEAR_MC_FLAG, &stat_buf);
+ if (ret == -1) {
+ ret = errno;
+ if (ret == ENOENT) {
+ /* nss responder has already invalidated memory caches */
+ return EOK;
+ }
+
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "stat failed: %s (%d)\n", sss_strerror(ret), ret);
+ }
+
+ usleep(step_time);
+ }
+
+ return EAGAIN;
+}
+
+errno_t sss_memcache_clear_all(void)
+{
+ errno_t ret;
+ bool sssd_nss_is_off = false;
+ FILE *clear_mc_flag;
+
+ ret = clear_memcache(&sssd_nss_is_off);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to clear caches.\n");
+ return EIO;
+ }
+ if (!sssd_nss_is_off) {
+ /* sssd_nss is running -> signal monitor to invalidate memcache */
+ clear_mc_flag = fopen(SSS_NSS_MCACHE_DIR"/"CLEAR_MC_FLAG, "w");
+ if (clear_mc_flag == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to create clear_mc_flag file. "
+ "Memory cache will not be cleared.\n");
+ return EIO;
+ }
+ ret = fclose(clear_mc_flag);
+ if (ret != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Unable to close file descriptor: %s\n",
+ strerror(ret));
+ return EIO;
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Sending SIGHUP to monitor.\n");
+ ret = sss_signal(SIGHUP);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Failed to send SIGHUP to monitor.\n");
+ return EIO;
+ }
+
+ ret = wait_till_nss_responder_invalidate_cache();
+ if (ret != EOK) {
+ ERROR("The memcache was not invalidated by NSS responder.\n");
+ }
+ }
+
+ return EOK;
+}
diff --git a/src/tools/tools_util.c b/src/tools/tools_util.c
new file mode 100644
index 0000000..eabc112
--- /dev/null
+++ b/src/tools/tools_util.c
@@ -0,0 +1,72 @@
+/*
+ SSSD
+
+ tools_utils.c
+
+ Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <popt.h>
+#include <errno.h>
+
+#include "util/util.h"
+#include "tools/tools_util.h"
+
+/*
+ * Print poptUsage as well as our error message
+ */
+void usage(poptContext pc, const char *error)
+{
+ size_t lentmp;
+
+ poptPrintUsage(pc, stderr, 0);
+
+ if (error) {
+ lentmp = strlen(error);
+ if ((lentmp > 0) && (error[lentmp - 1] != '\n')) {
+ fprintf(stderr, "%s\n", error);
+ return;
+ }
+
+ fprintf(stderr, "%s", error);
+ }
+}
+
+int set_locale(void)
+{
+ char *c;
+
+ c = setlocale(LC_ALL, "");
+ if (c == NULL) {
+ /* If setlocale fails, continue with the default
+ * locale. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unable to set locale\n");
+ }
+
+ errno = 0;
+ c = bindtextdomain(PACKAGE, LOCALEDIR);
+ if (c == NULL) {
+ return errno;
+ }
+
+ errno = 0;
+ c = textdomain(PACKAGE);
+ if (c == NULL) {
+ return errno;
+ }
+
+ return EOK;
+}
diff --git a/src/tools/tools_util.h b/src/tools/tools_util.h
new file mode 100644
index 0000000..a6f71b3
--- /dev/null
+++ b/src/tools/tools_util.h
@@ -0,0 +1,51 @@
+/*
+ Authors:
+ Jakub Hrozek <jhrozek@redhat.com>
+ Simo Sorce <ssorce@redhat.com>
+
+ Copyright (C) 2009 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#ifndef __TOOLS_UTIL_H__
+#define __TOOLS_UTIL_H__
+
+#define BAD_POPT_PARAMS(pc, msg, val, label) do { \
+ usage(pc, msg); \
+ val = EXIT_FAILURE; \
+ goto label; \
+} while(0)
+
+#define CHECK_ROOT(val, prg_name) do { \
+ val = getuid(); \
+ if (val != 0) { \
+ DEBUG(SSSDBG_CRIT_FAILURE, "Running under %d, must be root\n", val); \
+ ERROR("%1$s must be run as root\n", prg_name); \
+ val = EXIT_FAILURE; \
+ goto fini; \
+ } \
+} while(0)
+
+void usage(poptContext pc, const char *error);
+
+int set_locale(void);
+
+errno_t sss_signal(int signum);
+
+/* tools_mc_util.c */
+errno_t sss_memcache_clear_all(void);
+
+#endif /* __TOOLS_UTIL_H__ */
diff --git a/src/tools/wrappers/sss_debuglevel.in b/src/tools/wrappers/sss_debuglevel.in
new file mode 100644
index 0000000..a55afcd
--- /dev/null
+++ b/src/tools/wrappers/sss_debuglevel.in
@@ -0,0 +1,4 @@
+#!/bin/sh
+sbindir=@sbindir@
+echo "Redirecting to $sbindir/sssctl debug-level" >&2
+exec $sbindir/sssctl debug-level "$@"