From d89b1a6be28064e75f6100189c35bdeed9ef8c15 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Fri, 10 Aug 2012 11:59:26 -0400 Subject: [PATCH] Support for using SSSD (http://fedorahosted.org/sssd/) as a sudoers data source. From Daniel Kopecek and Pavel Brezina. --- INSTALL | 9 + NEWS | 3 + common/sudo_debug.c | 1 + configure | 40 +- configure.in | 20 + doc/CONTRIBUTORS | 1 + include/sudo_debug.h | 1 + mkdep.pl | 2 +- pathnames.h.in | 4 + plugins/sudoers/Makefile.in | 12 +- plugins/sudoers/sssd.c | 1333 +++++++++++++++++++++++++++++++++++ plugins/sudoers/sudo_nss.c | 30 +- plugins/sudoers/sudoers.c | 16 +- 13 files changed, 1460 insertions(+), 12 deletions(-) create mode 100644 plugins/sudoers/sssd.c diff --git a/INSTALL b/INSTALL index 71d306085..964d75ef2 100644 --- a/INSTALL +++ b/INSTALL @@ -201,6 +201,15 @@ Special features/options: this file instead of /etc/ldap.secret to read the secret password when rootbinddn is specified in the ldap config file. + --with-sssd + Enable support for using the System Security Services Daemon + (SSSD) as a sudoers data source. For more informaton on + SSD, see http://fedorahosted.org/sssd/ + + --with-sssd-lib=PATH + Specify the path to the SSSD shared library, which is loaded + at run-time. + --with-nsswitch[=PATH] Path to nsswitch.conf or "no" to disable nsswitch support. If specified, sudo uses this file instead of /etc/nsswitch.conf. diff --git a/NEWS b/NEWS index 0ee782c9f..6dc0e2179 100644 --- a/NEWS +++ b/NEWS @@ -42,6 +42,9 @@ What's new in Sudo 1.8.6? used to give a user the ability to run a command as themselves but with an expanded privilege set. + * Support for using the System Security Services Daemon (SSSD) as + a source of sudoers data. + What's new in Sudo 1.8.5p2? * Fixed use of the SUDO_ASKPASS environment variable which was diff --git a/common/sudo_debug.c b/common/sudo_debug.c index f41fa8b4c..b4ce80540 100644 --- a/common/sudo_debug.c +++ b/common/sudo_debug.c @@ -101,6 +101,7 @@ const char *const sudo_debug_subsystems[] = { "perms", "plugin", "hooks", + "sssd", NULL }; diff --git a/configure b/configure index be4bc05ed..12b596109 100755 --- a/configure +++ b/configure @@ -661,6 +661,7 @@ editor secure_path netsvc_conf nsswitch_conf +sssd_lib ldap_secret ldap_conf path_info @@ -722,6 +723,7 @@ mansectform mansectsu devdir SEMAN +PSMAN LCMAN BAMAN DEVEL @@ -805,6 +807,8 @@ with_rpath with_blibpath with_bsm_audit with_linux_audit +with_sssd +with_sssd_lib with_incpath with_libpath with_libraries @@ -1583,6 +1587,8 @@ Optional Packages: --with-blibpath=PATH pass -blibpath flag to ld for additional lib paths --with-bsm-audit enable BSM audit support --with-linux-audit enable Linux audit support + --with-sssd enable SSSD support + --with-sssd-lib path to the SSSD library --with-incpath additional places to look for include files --with-libpath additional places to look for libraries --with-libraries additional libraries to link with @@ -2904,6 +2910,8 @@ $as_echo "$as_me: Configuring Sudo version $PACKAGE_VERSION" >&6;} + + @@ -2962,6 +2970,7 @@ DEVEL= LDAP="#" BAMAN=0 LCMAN=0 +PSMAN=0 SEMAN=0 LIBINTL= ZLIB= @@ -3912,6 +3921,34 @@ fi +# Check whether --with-sssd was given. +if test "${with_sssd+set}" = set; then : + withval=$with_sssd; case $with_sssd in + yes) SUDOERS_OBJS="${SUDOERS_OBJS} sssd.lo" + $as_echo "#define HAVE_SSSD 1" >>confdefs.h + + ;; + no) ;; + *) as_fn_error $? "\"--with-sssd does not take an argument.\"" "$LINENO" 5 + ;; +esac +fi + + + +# Check whether --with-sssd-lib was given. +if test "${with_sssd_lib+set}" = set; then : + withval=$with_sssd_lib; +fi + +sssd_lib="\"LIBDIR\"" +test -n "$with_sssd_lib" && sssd_lib="$with_sssd_lib" +cat >>confdefs.h <>confdefs.h <<_ACEOF #define HAVE_PRIV_SET 1 _ACEOF - + PSMAN=1 fi done @@ -23099,5 +23136,6 @@ fi + diff --git a/configure.in b/configure.in index 422964105..a9783a77d 100644 --- a/configure.in +++ b/configure.in @@ -113,6 +113,7 @@ AC_SUBST([root_sudo]) AC_SUBST([path_info]) AC_SUBST([ldap_conf]) AC_SUBST([ldap_secret]) +AC_SUBST([sssd_lib]) AC_SUBST([nsswitch_conf]) AC_SUBST([netsvc_conf]) AC_SUBST([secure_path]) @@ -301,6 +302,24 @@ AC_ARG_WITH(linux-audit, [AS_HELP_STRING([--with-linux-audit], [enable Linux aud ;; esac]) +dnl +dnl Handle SSSD support. +dnl +AC_ARG_WITH(sssd, [AS_HELP_STRING([--with-sssd], [enable SSSD support])], +[case $with_sssd in + yes) SUDOERS_OBJS="${SUDOERS_OBJS} sssd.lo" + AC_DEFINE(HAVE_SSSD) + ;; + no) ;; + *) AC_MSG_ERROR(["--with-sssd does not take an argument."]) + ;; +esac]) + +AC_ARG_WITH(sssd-lib, [AS_HELP_STRING([--with-sssd-lib], [path to the SSSD library])]) +sssd_lib="\"LIBDIR\"" +test -n "$with_sssd_lib" && sssd_lib="$with_sssd_lib" +SUDO_DEFINE_UNQUOTED(_PATH_SSSD_LIB, "$sssd_lib", [Path to the SSSD library]) + AC_ARG_WITH(incpath, [AS_HELP_STRING([--with-incpath], [additional places to look for include files])], [case $with_incpath in yes) AC_MSG_ERROR(["must give --with-incpath an argument."]) @@ -3559,6 +3578,7 @@ AH_TEMPLATE(HAVE_LBER_H, [Define to 1 if your LDAP needs . (OpenLDAP doe AH_TEMPLATE(HAVE_LDAP, [Define to 1 if you use LDAP for sudoers.]) AH_TEMPLATE(HAVE_LIBINTL_H, [Define to 1 if you have the header file.]) AH_TEMPLATE(HAVE_LINUX_AUDIT, [Define to 1 to enable Linux audit support.]) +AH_TEMPLATE(HAVE_SSSD, [Define to 1 to enable SSSD support.]) AH_TEMPLATE(HAVE_OPIE, [Define to 1 if you use NRL OPIE.]) AH_TEMPLATE(HAVE_PAM, [Define to 1 if you use PAM authentication.]) AH_TEMPLATE(HAVE_PAM_LOGIN, [Define to 1 if you use a specific PAM session for sudo -i.]) diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS index 880cfb8dc..275446192 100644 --- a/doc/CONTRIBUTORS +++ b/doc/CONTRIBUTORS @@ -21,6 +21,7 @@ you believe you should be listed, please send a note to sudo@sudo.ws. Keith Garry Boyce Michael Brantley Rob Braun + Pavel Brezina Piete Brooks Jerry Brown Michael E Burr diff --git a/include/sudo_debug.h b/include/sudo_debug.h index 0031c1511..5f6871e13 100644 --- a/include/sudo_debug.h +++ b/include/sudo_debug.h @@ -71,6 +71,7 @@ #define SUDO_DEBUG_PERMS (23<<6) /* uid/gid swapping functions */ #define SUDO_DEBUG_PLUGIN (24<<6) /* main plugin functions */ #define SUDO_DEBUG_HOOKS (25<<6) /* hook functions */ +#define SUDO_DEBUG_SSSD (26<<6) /* sudoers SSSD */ #define SUDO_DEBUG_ALL 0xfff0 /* all subsystems */ /* Flag to include string version of errno in debug info. */ diff --git a/mkdep.pl b/mkdep.pl index 3388fd916..77902b997 100755 --- a/mkdep.pl +++ b/mkdep.pl @@ -52,7 +52,7 @@ sub mkdep { $makefile =~ s:\@DEV\@::g; $makefile =~ s:\@COMMON_OBJS\@:aix.lo:; $makefile =~ s:\@SUDO_OBJS\@:preload.o selinux.o sesh.o sudo_noexec.lo:; - $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo plugin_error.lo:; + $makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo plugin_error.lo sssd.lo:; # XXX - fill in AUTH_OBJS from contents of the auth dir instead $makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:; $makefile =~ s:\@LTLIBOBJS\@:closefrom.lo dlopen.lo fnmatch.lo getcwd.lo getgrouplist.lo getline.lo getprogname.lo glob.lo isblank.lo memrchr.lo mksiglist.lo mktemp.lo nanosleep.lo pw_dup.lo siglist.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo utimes.lo globtest.o fnm_test.o:; diff --git a/pathnames.h.in b/pathnames.h.in index 32bff86b7..e7a762c4e 100644 --- a/pathnames.h.in +++ b/pathnames.h.in @@ -157,6 +157,10 @@ #undef _PATH_LDAP_SECRET #endif /* _PATH_LDAP_SECRET */ +#ifndef _PATH_SSSD_LIB +#undef _PATH_SSSD_LIB +#endif /* _PATH_SSSD_LIB */ + #ifndef _PATH_NSSWITCH_CONF #undef _PATH_NSSWITCH_CONF #endif /* _PATH_NSSWITCH_CONF */ diff --git a/plugins/sudoers/Makefile.in b/plugins/sudoers/Makefile.in index 3b75b6664..e47bf7798 100644 --- a/plugins/sudoers/Makefile.in +++ b/plugins/sudoers/Makefile.in @@ -32,6 +32,7 @@ top_srcdir = @top_srcdir@ incdir = $(top_srcdir)/include docdir = @docdir@ timedir = @timedir@ +libdir = @libdir@ cross_compiling = @CROSS_COMPILING@ # Compiler & tools to use @@ -52,7 +53,7 @@ SUDOERS_LIBS = @SUDOERS_LIBS@ @AFS_LIBS@ @GETGROUPS_LIB@ $(LIBS) $(NET_LIBS) @ZL REPLAY_LIBS = @REPLAY_LIBS@ @ZLIB@ # C preprocessor flags -CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(devdir) -I$(srcdir) -I$(top_srcdir) @CPPFLAGS@ +CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(devdir) -I$(srcdir) -I$(top_srcdir) -DLIBDIR=\"$(libdir)\" @CPPFLAGS@ # Usually -O and/or -g CFLAGS = @CFLAGS@ @@ -719,6 +720,15 @@ sia.lo: $(authdir)/sia.c $(top_builddir)/config.h $(srcdir)/sudoers.h \ $(devdir)/def_data.h $(srcdir)/logging.h $(srcdir)/sudo_nss.h \ $(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h $(incdir)/gettext.h $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(DEFS) $(authdir)/sia.c +sssd.lo: $(srcdir)/sssd.c $(top_builddir)/config.h \ + $(top_srcdir)/compat/dlfcn.h $(srcdir)/sudoers.h \ + $(top_srcdir)/compat/stdbool.h $(top_builddir)/pathnames.h \ + $(incdir)/missing.h $(incdir)/error.h $(incdir)/alloc.h \ + $(incdir)/list.h $(incdir)/fileops.h $(srcdir)/defaults.h \ + $(devdir)/def_data.h $(srcdir)/logging.h $(srcdir)/sudo_nss.h \ + $(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h $(incdir)/gettext.h \ + $(srcdir)/parse.h $(incdir)/lbuf.h $(incdir)/sudo_debug.h + $(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(DEFS) $(srcdir)/sssd.c sudo_auth.lo: $(authdir)/sudo_auth.c $(top_builddir)/config.h \ $(srcdir)/sudoers.h $(top_srcdir)/compat/stdbool.h \ $(top_builddir)/pathnames.h $(incdir)/missing.h \ diff --git a/plugins/sudoers/sssd.c b/plugins/sudoers/sssd.c new file mode 100644 index 000000000..95a4776d5 --- /dev/null +++ b/plugins/sudoers/sssd.c @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 2003-2012 Todd C. Miller + * Copyright (c) 2011 Daniel Kopecek + * + * This code is derived from software contributed by Aaron Spangler. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include +#include +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#if TIME_WITH_SYS_TIME +# include +#endif +#ifdef HAVE_DLOPEN +# include +#else +# include "compat/dlfcn.h" +#endif +#include +#include +#include + +#include +#include + +#include "sudoers.h" +#include "parse.h" +#include "lbuf.h" +#include "sudo_debug.h" + +/* SSSD <--> SUDO interface - do not change */ +struct sss_sudo_attr { + char *name; + char **values; + unsigned int num_values; +}; + +struct sss_sudo_rule { + unsigned int num_attrs; + struct sss_sudo_attr *attrs; +}; + +struct sss_sudo_result { + unsigned int num_rules; + struct sss_sudo_rule *rules; +}; + +typedef int (*sss_sudo_send_recv_t)(uid_t, const char*, const char*, + uint32_t*, struct sss_sudo_result**); + +typedef int (*sss_sudo_send_recv_defaults_t)(uid_t, const char*, uint32_t*, + char**, struct sss_sudo_result**); + +typedef void (*sss_sudo_free_result_t)(struct sss_sudo_result*); + +typedef int (*sss_sudo_get_values_t)(struct sss_sudo_rule*, const char*, + char***); + +typedef void (*sss_sudo_free_values_t)(char**); + +/* sudo_nss implementation */ + +struct sudo_sss_handle { + char *domainname; + struct passwd *pw; + void *ssslib; + sss_sudo_send_recv_t fn_send_recv; + sss_sudo_send_recv_defaults_t fn_send_recv_defaults; + sss_sudo_free_result_t fn_free_result; + sss_sudo_get_values_t fn_get_values; + sss_sudo_free_values_t fn_free_values; +}; + +static int sudo_sss_open(struct sudo_nss *nss); +static int sudo_sss_close(struct sudo_nss *nss); +static int sudo_sss_parse(struct sudo_nss *nss); +static void sudo_sss_parse_options(struct sudo_sss_handle *handle, + struct sss_sudo_rule *rule); +static int sudo_sss_setdefs(struct sudo_nss *nss); +static int sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag); +static int sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw); +static int sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw, + struct lbuf *lbuf); + +static int sudo_sss_display_bound_defaults(struct sudo_nss *nss, + struct passwd *pw, struct lbuf *lbuf); + +static int sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw, + struct lbuf *lbuf); + + +static struct sss_sudo_result *sudo_sss_result_get(struct sudo_nss *nss, + struct passwd *pw, + uint32_t *state); + +static void +sudo_sss_attrcpy(struct sss_sudo_attr *dst, const struct sss_sudo_attr *src) +{ + int i; + debug_decl(sudo_sss_attrcpy, SUDO_DEBUG_SSSD) + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "dst=%p, src=%p", dst, src); + sudo_debug_printf(SUDO_DEBUG_INFO, "emalloc: cnt=%d", src->num_values); + + dst->name = estrdup(src->name); + dst->num_values = src->num_values; + dst->values = emalloc2(dst->num_values, sizeof(char *)); + + for (i = 0; i < dst->num_values; ++i) + dst->values[i] = estrdup(src->values[i]); + + debug_return; +} + +static void +sudo_sss_rulecpy(struct sss_sudo_rule *dst, const struct sss_sudo_rule *src) +{ + int i; + debug_decl(sudo_sss_rulecpy, SUDO_DEBUG_SSSD) + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "dst=%p, src=%p", dst, src); + sudo_debug_printf(SUDO_DEBUG_INFO, "emalloc: cnt=%d", src->num_attrs); + + dst->num_attrs = src->num_attrs; + dst->attrs = emalloc2(dst->num_attrs, sizeof(struct sss_sudo_attr)); + + for (i = 0; i < dst->num_attrs; ++i) + sudo_sss_attrcpy(dst->attrs + i, src->attrs + i); + + debug_return; +} + +#define _SUDO_SSS_FILTER_INCLUDE 0 +#define _SUDO_SSS_FILTER_EXCLUDE 1 + +#define _SUDO_SSS_STATE_HOSTMATCH 0x01 +#define _SUDO_SSS_STATE_USERMATCH 0x02 + +static struct sss_sudo_result * +sudo_sss_filter_result(struct sudo_sss_handle *handle, + struct sss_sudo_result *in_res, + int (*filterp)(struct sudo_sss_handle *, struct sss_sudo_rule *, void *), + int act, void *filterp_arg) +{ + struct sss_sudo_result *out_res; + int i, l, r; + debug_decl(sudo_sss_filter_result, SUDO_DEBUG_SSSD) + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "in_res=%p, count=%u, act=%s", + in_res, in_res->num_rules, + act == _SUDO_SSS_FILTER_EXCLUDE ? "EXCLUDE" : "INCLUDE"); + + if (in_res == NULL) + debug_return_ptr(NULL); + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "emalloc: cnt=%d", in_res->num_rules); + + out_res = emalloc(sizeof(struct sss_sudo_result)); + out_res->rules = in_res->num_rules > 0 ? + emalloc2(in_res->num_rules, sizeof(struct sss_sudo_rule)) : NULL; + out_res->num_rules = 0; + + for (i = l = 0; i < in_res->num_rules; ++i) { + r = filterp(handle, in_res->rules + i, filterp_arg); + + if (( r && act == _SUDO_SSS_FILTER_INCLUDE) || + (!r && act == _SUDO_SSS_FILTER_EXCLUDE)) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "COPY (%s): %p[%u] => %p[%u] (= %p)", + act == _SUDO_SSS_FILTER_EXCLUDE ? "not excluded" : "included", + in_res->rules, i, out_res->rules, l, in_res->rules + i); + + sudo_sss_rulecpy(out_res->rules + l, in_res->rules + i); + ++l; + } + } + + if (l < in_res->num_rules) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "reallocating result: %p (count: %u -> %u)", out_res->rules, + in_res->num_rules, l); + out_res->rules = erealloc3(out_res->rules, l, sizeof(struct sss_sudo_rule)); + } + + out_res->num_rules = l; + + debug_return_ptr(out_res); +} + +struct sudo_nss sudo_nss_sss = { + &sudo_nss_sss, + NULL, + sudo_sss_open, + sudo_sss_close, + sudo_sss_parse, + sudo_sss_setdefs, + sudo_sss_lookup, + sudo_sss_display_cmnd, + sudo_sss_display_defaults, + sudo_sss_display_bound_defaults, + sudo_sss_display_privs +}; + +/* sudo_nss implementation */ +// ok +static int sudo_sss_open(struct sudo_nss *nss) +{ + struct sudo_sss_handle *handle; + static const char path[] = _PATH_SSSD_LIB"/libsss_sudo.so"; + debug_decl(sudo_sss_open, SUDO_DEBUG_SSSD); + + /* Create a handle container. */ + handle = emalloc(sizeof(struct sudo_sss_handle)); + + /* Load symbols */ + handle->ssslib = dlopen(path, RTLD_LAZY); + if (handle->ssslib == NULL) { + warningx(_("Unable to dlopen %s: %s"), path, dlerror()); + warningx(_("Unable to initialize SSS source. Is SSSD installed on your machine?")); + debug_return_int(EFAULT); + } + + handle->fn_send_recv = dlsym(handle->ssslib, "sss_sudo_send_recv"); + if (handle->fn_send_recv == NULL) { + warningx(_("unable to find symbol \"%s\" in %s"), path, + "sss_sudo_send_recv"); + debug_return_int(EFAULT); + } + + handle->fn_send_recv_defaults = + dlsym(handle->ssslib, "sss_sudo_send_recv_defaults"); + if (handle->fn_send_recv_defaults == NULL) { + warningx(_("unable to find symbol \"%s\" in %s"), path, + "sss_sudo_send_recv_defaults"); + debug_return_int(EFAULT); + } + + handle->fn_free_result = dlsym(handle->ssslib, "sss_sudo_free_result"); + if (handle->fn_free_result == NULL) { + warningx(_("unable to find symbol \"%s\" in %s"), path, + "sss_sudo_free_result"); + debug_return_int(EFAULT); + } + + handle->fn_get_values = dlsym(handle->ssslib, "sss_sudo_get_values"); + if (handle->fn_get_values == NULL) { + warningx(_("unable to find symbol \"%s\" in %s"), path, + "sss_sudo_get_values"); + debug_return_int(EFAULT); + } + + handle->fn_free_values = dlsym(handle->ssslib, "sss_sudo_free_values"); + if (handle->fn_free_values == NULL) { + warningx(_("unable to find symbol \"%s\" in %s"), path, + "sss_sudo_free_values"); + debug_return_int(EFAULT); + } + + handle->domainname = NULL; + handle->pw = sudo_user.pw; + nss->handle = handle; + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "handle=%p", handle); + + debug_return_int(0); +} + +// ok +static int sudo_sss_close(struct sudo_nss *nss) +{ + struct sudo_sss_handle *handle; + debug_decl(sudo_sss_close, SUDO_DEBUG_SSSD); + + if (nss && nss->handle) { + handle = nss->handle; + dlclose(handle->ssslib); + } + + efree(nss->handle); + debug_return_int(0); +} + +// ok +static int sudo_sss_parse(struct sudo_nss *nss) +{ + debug_decl(sudo_sss_parse, SUDO_DEBUG_SSSD); + debug_return_int(0); +} + +static int sudo_sss_setdefs(struct sudo_nss *nss) +{ + struct sudo_sss_handle *handle = nss->handle; + + struct sss_sudo_result *sss_result; + struct sss_sudo_rule *sss_rule; + uint32_t sss_error; + int i; + debug_decl(sudo_sss_setdefs, SUDO_DEBUG_SSSD); + + if (handle == NULL) + debug_return_int(-1); + + sudo_debug_printf(SUDO_DEBUG_DIAG, "Looking for cn=defaults"); + + if (handle->fn_send_recv_defaults(handle->pw->pw_uid, handle->pw->pw_name, + &sss_error, &handle->domainname, + &sss_result) != 0) { + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_send_recv_defaults: != 0, sss_error=%u", sss_error); + debug_return_int(-1); + } + + if (sss_error == ENOENT) { + sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD."); + debug_return_int(-1); + } else if(sss_error != 0) { + sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error); + debug_return_int(-1); + } + + for (i = 0; i < sss_result->num_rules; ++i) { + sudo_debug_printf(SUDO_DEBUG_DIAG, + "Parsing cn=defaults, %d/%d", i, sss_result->num_rules); + sss_rule = sss_result->rules + i; + sudo_sss_parse_options(handle, sss_rule); + } + + handle->fn_free_result(sss_result); + debug_return_int(0); +} + +static int sudo_sss_checkpw(struct sudo_nss *nss, struct passwd *pw) +{ + struct sudo_sss_handle *handle = nss->handle; + debug_decl(sudo_sss_checkpw, SUDO_DEBUG_SSSD); + + if (pw->pw_name != handle->pw->pw_name || + pw->pw_uid != handle->pw->pw_uid) { + sudo_debug_printf(SUDO_DEBUG_DIAG, + "Requested name or uid don't match the initial once, reinitializing..."); + handle->pw = pw; + + if (sudo_sss_setdefs(nss) != 0) + debug_return_int(-1); + } + + debug_return_int(0); +} + +static int +sudo_sss_check_runas_user(struct sudo_sss_handle *handle, struct sss_sudo_rule *sss_rule) +{ + char **val_array = NULL; + char *val; + int ret = false, i; + debug_decl(sudo_sss_check_runas_user, SUDO_DEBUG_SSSD); + + if (!runas_pw) + debug_return_int(UNSPEC); + + /* get the runas user from the entry */ + switch (handle->fn_get_values(sss_rule, "sudoRunAsUser", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Trying old style (sudoRunAs)"); + + /* try old style */ + switch (handle->fn_get_values(sss_rule, "sudoRunAs", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Matching against runas_default"); + /* + * If there are no runas entries, match runas_default against + * what the user specified on the command line. + */ + return !strcasecmp(runas_pw->pw_name, def_runas_default); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAs): != 0"); + debug_return_int(UNSPEC); + } + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsUser): != 0"); + debug_return_int(UNSPEC); + } + + /* + * BUG: + * + * if runas is not specified on the command line, the only information + * as to which user to run as is in the runas_default option. We should + * check to see if we have the local option present. Unfortunately we + * don't parse these options until after this routine says yes or no. + * The query has already returned, so we could peek at the attribute + * values here though. + * + * For now just require users to always use -u option unless its set + * in the global defaults. This behaviour is no different than the global + * /etc/sudoers. + * + * Sigh - maybe add this feature later + */ + + /* walk through values returned, looking for a match */ + for (i = 0; val_array[i] != NULL && !ret; ++i) { + val = val_array[i]; + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); + + switch (val[0]) { + case '+': + sudo_debug_printf(SUDO_DEBUG_DEBUG, "netgr_"); + if (netgr_matches(val, NULL, NULL, runas_pw->pw_name)) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match"); + ret = true; + } + break; + case '%': + sudo_debug_printf(SUDO_DEBUG_DEBUG, "usergr_"); + if (usergr_matches(val, runas_pw->pw_name, runas_pw)) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "=> match"); + ret = true; + } + break; + case 'A': + if (strcmp(val, "ALL") == 0) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "ALL => match"); + ret = true; + break; + } + /* FALLTHROUGH */ + sudo_debug_printf(SUDO_DEBUG_DEBUG, "FALLTHROUGH"); + default: + if (strcasecmp(val, runas_pw->pw_name) == 0) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "%s == %s (pw_name) => match", val, runas_pw->pw_name); + ret = true; + } + break; + } + + sudo_debug_printf(SUDO_DEBUG_INFO, + "sssd/ldap sudoRunAsUser '%s' ... %s", val, ret ? "MATCH!" : "not"); + } + + handle->fn_free_values(val_array); /* cleanup */ + + debug_return_int(ret); +} + +static int +sudo_sss_check_runas_group(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) +{ + char **val_array = NULL; + char *val; + int ret = false, i; + debug_decl(sudo_sss_check_runas_group, SUDO_DEBUG_SSSD); + + /* runas_gr is only set if the user specified the -g flag */ + if (!runas_gr) + debug_return_int(UNSPEC); + + /* get the values from the entry */ + switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + debug_return_int(false); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoRunAsGroup): != 0"); + debug_return_int(UNSPEC); + } + + /* walk through values returned, looking for a match */ + for (i = 0; val_array[i] != NULL; ++i) { + val = val_array[i]; + sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); + + if (strcmp(val, "ALL") == 0 || group_matches(val, runas_gr)) + ret = true; + + sudo_debug_printf(SUDO_DEBUG_INFO, + "sssd/ldap sudoRunAsGroup '%s' ... %s", val, ret ? "MATCH!" : "not"); + } + + handle->fn_free_values(val_array); + + debug_return_int(ret); +} + +/* + * Walk through search results and return true if we have a runas match, + * else false. RunAs info is optional. + */ +static int +sudo_sss_check_runas(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) +{ + int ret; + debug_decl(sudo_sss_check_runas, SUDO_DEBUG_SSSD); + + if (rule == NULL) + debug_return_int(false); + + ret = sudo_sss_check_runas_user(handle, rule) != false && + sudo_sss_check_runas_group(handle, rule) != false; + + debug_return_int(ret); +} + +static int +sudo_sss_check_host(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) +{ + char **val_array, *val; + int ret = false, i; + debug_decl(sudo_sss_check_host, SUDO_DEBUG_SSSD); + + if (rule == NULL) + debug_return_int(ret); + + /* get the values from the rule */ + switch (handle->fn_get_values(rule, "sudoHost", &val_array)) + { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + debug_return_int(false); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoHost): != 0"); + debug_return_int(ret); + } + + /* walk through values */ + for (i = 0; val_array[i] != NULL; ++i) { + val = val_array[i]; + sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); + + /* match any or address or netgroup or hostname */ + if (!strcmp(val, "ALL") || addr_matches(val) || + netgr_matches(val, user_host, user_shost, NULL) || + hostname_matches(user_shost, user_host, val)) + ret = true; + + sudo_debug_printf(SUDO_DEBUG_INFO, + "sssd/ldap sudoHost '%s' ... %s", val, ret ? "MATCH!" : "not"); + } + + handle->fn_free_values(val_array); + + debug_return_int(ret); +} + +static int +sudo_sss_result_filterp(struct sudo_sss_handle *handle, + struct sss_sudo_rule *rule, void *unused) +{ + (void)unused; + debug_decl(sudo_sss_result_filterp, SUDO_DEBUG_SSSD); + + if (sudo_sss_check_host(handle, rule)) + debug_return_int(1); + else + debug_return_int(0); +} + +static struct sss_sudo_result * +sudo_sss_result_get(struct sudo_nss *nss, struct passwd *pw, uint32_t *state) +{ + struct sudo_sss_handle *handle = nss->handle; + struct sss_sudo_result *u_sss_result, *f_sss_result; + uint32_t sss_error = 0, ret; + debug_decl(sudo_sss_result_get, SUDO_DEBUG_SSSD); + + if (sudo_sss_checkpw(nss, pw) != 0) + debug_return_ptr(NULL); + + sudo_debug_printf(SUDO_DEBUG_DIAG, " username=%s", handle->pw->pw_name); + sudo_debug_printf(SUDO_DEBUG_DIAG, "domainname=%s", handle->domainname); + + u_sss_result = f_sss_result = NULL; + + ret = handle->fn_send_recv(handle->pw->pw_uid, handle->pw->pw_name, + handle->domainname, &sss_error, &u_sss_result); + + switch (ret) { + case 0: + switch (sss_error) { + case 0: + if (u_sss_result != NULL) { + if (state != NULL) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= USERMATCH"); + *state |= _SUDO_SSS_STATE_USERMATCH; + } + sudo_debug_printf(SUDO_DEBUG_INFO, "Received %u rule(s)", + u_sss_result->num_rules); + } else { + sudo_debug_printf(SUDO_DEBUG_INFO, + "Internal error: u_sss_result == NULL && sss_error == 0"); + debug_return_ptr(NULL); + } + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD."); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error); + debug_return_ptr(NULL); + } + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_send_recv: != 0: ret=%d", ret); + debug_return_ptr(NULL); + } + + f_sss_result = sudo_sss_filter_result(handle, u_sss_result, + sudo_sss_result_filterp, _SUDO_SSS_FILTER_INCLUDE, NULL); + + if (f_sss_result != NULL) { + if (f_sss_result->num_rules > 0) { + if (state != NULL) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "state |= HOSTMATCH"); + *state |= _SUDO_SSS_STATE_HOSTMATCH; + } + } + } + + sudo_debug_printf(SUDO_DEBUG_DEBUG, + "u_sss_result=(%p, %u) => f_sss_result=(%p, %u)", u_sss_result, + u_sss_result->num_rules, f_sss_result, f_sss_result->num_rules); + + handle->fn_free_result(u_sss_result); + + debug_return_ptr(f_sss_result); +} + +/* + * Search for boolean "option" in sudoOption. + * Returns true if found and allowed, false if negated, else UNSPEC. + */ +static int +sudo_sss_check_bool(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule, + char *option) +{ + char ch, *var, **val_array = NULL; + int i, ret = UNSPEC; + debug_decl(sudo_sss_check_bool, SUDO_DEBUG_SSSD); + + if (rule == NULL) + debug_return_int(ret); + + switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + debug_return_int(ret); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); + debug_return_int(ret); + } + + /* walk through options */ + for (i = 0; val_array[i] != NULL; ++i) { + var = val_array[i]; + sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoOption: '%s'", var); + + if ((ch = *var) == '!') + var++; + if (strcmp(var, option) == 0) + ret = (ch != '!'); + } + + handle->fn_free_values(val_array); + + debug_return_int(ret); +} + +/* + * Walk through search results and return true if we have a command match, + * false if disallowed and UNSPEC if not matched. + */ +static int +sudo_sss_check_command(struct sudo_sss_handle *handle, + struct sss_sudo_rule *rule, int *setenv_implied) +{ + char **val_array = NULL, *val; + char *allowed_cmnd, *allowed_args; + int i, foundbang, ret = UNSPEC; + debug_decl(sudo_sss_check_command, SUDO_DEBUG_SSSD); + + if (rule == NULL) + debug_return_int(ret); + + switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + debug_return_int(ret); + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); + debug_return_int(ret); + } + + for (i = 0; val_array[i] != NULL && ret != false; ++i) { + val = val_array[i]; + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "val[%d]=%s", i, val); + + /* Match against ALL ? */ + if (!strcmp(val, "ALL")) { + ret = true; + if (setenv_implied != NULL) + *setenv_implied = true; + sudo_debug_printf(SUDO_DEBUG_INFO, + "sssd/ldap sudoCommand '%s' ... MATCH!", val); + continue; + } + + /* check for !command */ + if (*val == '!') { + foundbang = true; + allowed_cmnd = estrdup(1 + val); /* !command */ + } else { + foundbang = false; + allowed_cmnd = estrdup(val); /* command */ + } + + /* split optional args away from command */ + allowed_args = strchr(allowed_cmnd, ' '); + if (allowed_args) + *allowed_args++ = '\0'; + + /* check the command like normal */ + if (command_matches(allowed_cmnd, allowed_args)) { + /* + * If allowed (no bang) set ret but keep on checking. + * If disallowed (bang), exit loop. + */ + ret = foundbang ? false : true; + } + + sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoCommand '%s' ... %s", + val, ret == true ? "MATCH!" : "not"); + efree(allowed_cmnd); /* cleanup */ + } + + handle->fn_free_values(val_array); /* more cleanup */ + + debug_return_int(ret); +} + +static void +sudo_sss_parse_options(struct sudo_sss_handle *handle, struct sss_sudo_rule *rule) +{ + int i; + char op, *v, *val; + char **val_array = NULL; + debug_decl(sudo_sss_parse_options, SUDO_DEBUG_SSSD); + + if (rule == NULL) + debug_return; + + switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + debug_return; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0"); + debug_return; + } + + /* walk through options */ + for (i = 0; val_array[i] != NULL; i++) { + sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap sudoOption: '%s'", + val_array[i]); + v = estrdup(val_array[i]); + + /* check for equals sign past first char */ + val = strchr(v, '='); + if (val > v) { + *val++ = '\0'; /* split on = and truncate var */ + op = *(val - 2); /* peek for += or -= cases */ + if (op == '+' || op == '-') { + *(val - 2) = '\0'; /* found, remove extra char */ + /* case var+=val or var-=val */ + set_default(v, val, (int) op); + } else { + /* case var=val */ + set_default(v, val, true); + } + } else if (*v == '!') { + /* case !var Boolean False */ + set_default(v + 1, NULL, false); + } else { + /* case var Boolean True */ + set_default(v, NULL, true); + } + efree(v); + } + + handle->fn_free_values(val_array); + debug_return; +} + +static int +sudo_sss_lookup(struct sudo_nss *nss, int ret, int pwflag) +{ + int rc, setenv_implied; + + struct sudo_sss_handle *handle = nss->handle; + struct sss_sudo_result *sss_result = NULL; + struct sss_sudo_rule *rule; + uint32_t i, state = 0; + debug_decl(sudo_sss_lookup, SUDO_DEBUG_SSSD); + + /* Fetch list of sudoRole entries that match user and host. */ + sss_result = sudo_sss_result_get(nss, sudo_user.pw, &state); + + /* + * The following queries are only determine whether or not a + * password is required, so the order of the entries doesn't matter. + */ + if (pwflag) { + int doauth = UNSPEC; + int matched = UNSPEC; + enum def_tuple pwcheck = + (pwflag == -1) ? never : sudo_defs_table[pwflag].sd_un.tuple; + + sudo_debug_printf(SUDO_DEBUG_INFO, "perform search for pwflag %d", pwflag); + if (sss_result != NULL) { + for (i = 0; i < sss_result->num_rules; i++) { + rule = sss_result->rules + i; + if ((pwcheck == any && doauth != false) || + (pwcheck == all && doauth == false)) { + doauth = sudo_sss_check_bool(handle, rule, "authenticate"); + } + /* Only check the command when listing another user. */ + if (user_uid == 0 || list_pw == NULL || + user_uid == list_pw->pw_uid || + sudo_sss_check_command(handle, rule, NULL)) { + matched = true; + break; + } + } + } + if (matched || user_uid == 0) { + SET(ret, VALIDATE_OK); + CLR(ret, VALIDATE_NOT_OK); + if (def_authenticate) { + switch (pwcheck) { + case always: + SET(ret, FLAG_CHECK_USER); + break; + case all: + case any: + if (doauth == false) + def_authenticate = false; + break; + case never: + def_authenticate = false; + break; + default: + break; + } + } + } + goto done; + } + + sudo_debug_printf(SUDO_DEBUG_DIAG, + "searching SSSD/LDAP for sudoers entries"); + + setenv_implied = false; + if (sss_result != NULL) { + for (i = 0; i < sss_result->num_rules; i++) { + rule = sss_result->rules + i; + if (!sudo_sss_check_runas(handle, rule)) + continue; + rc = sudo_sss_check_command(handle, rule, &setenv_implied); + if (rc != UNSPEC) { + /* We have a match. */ + sudo_debug_printf(SUDO_DEBUG_DIAG, "Command %sallowed", + rc == true ? "" : "NOT "); + if (rc == true) { + sudo_debug_printf(SUDO_DEBUG_DEBUG, "SSSD rule: %p", rule); + /* Apply entry-specific options. */ + if (setenv_implied) + def_setenv = true; + sudo_sss_parse_options(handle, rule); +#ifdef HAVE_SELINUX + /* Set role and type if not specified on command line. */ + if (user_role == NULL) + user_role = def_role; + if (user_type == NULL) + user_type = def_type; +#endif /* HAVE_SELINUX */ + SET(ret, VALIDATE_OK); + CLR(ret, VALIDATE_NOT_OK); + } else { + SET(ret, VALIDATE_NOT_OK); + CLR(ret, VALIDATE_OK); + } + break; + } + } + } +done: + sudo_debug_printf(SUDO_DEBUG_DIAG, "Done with LDAP searches"); + + if (!ISSET(ret, VALIDATE_OK)) { + /* No matching entries. */ + if (pwflag && list_pw == NULL) + SET(ret, FLAG_NO_CHECK); + } + + if (state & _SUDO_SSS_STATE_USERMATCH) + CLR(ret, FLAG_NO_USER); + if (state & _SUDO_SSS_STATE_HOSTMATCH) + CLR(ret, FLAG_NO_HOST); + + sudo_debug_printf(SUDO_DEBUG_DEBUG, "sudo_sss_lookup(%d)=0x%02x", + pwflag, ret); + + debug_return_int(ret); +} + +static int +sudo_sss_display_cmnd(struct sudo_nss *nss, struct passwd *pw) +{ + struct sudo_sss_handle *handle = nss->handle; + struct sss_sudo_result *sss_result = NULL; + struct sss_sudo_rule *rule; + int i, found = false; + debug_decl(sudo_sss_display_cmnd, SUDO_DEBUG_SSSD); + + if (handle == NULL) + goto done; + + if (sudo_sss_checkpw(nss, pw) != 0) + debug_return_int(-1); + + /* + * The sudo_sss_result_get() function returns all nodes that match + * the user and the host. + */ + sudo_debug_printf(SUDO_DEBUG_DIAG, "sssd/ldap search for command list"); + sss_result = sudo_sss_result_get(nss, pw, NULL); + + if (sss_result == NULL) + goto done; + + for (i = 0; i < sss_result->num_rules; i++) { + rule = sss_result->rules + i; + if (sudo_sss_check_command(handle, rule, NULL) && + sudo_sss_check_runas(handle, rule)) { + found = true; + goto done; + } + } + +done: + if (found) + printf("%s%s%s\n", safe_cmnd ? safe_cmnd : user_cmnd, + user_args ? " " : "", user_args ? user_args : ""); + + if (sss_result != NULL) + handle->fn_free_result(sss_result); + + debug_return_int(!found); +} + +static int +sudo_sss_display_defaults(struct sudo_nss *nss, struct passwd *pw, + struct lbuf *lbuf) +{ + struct sudo_sss_handle *handle = nss->handle; + + struct sss_sudo_rule *rule; + struct sss_sudo_result *sss_result = NULL; + + uint32_t sss_error = 0; + + char *prefix, *val, **val_array = NULL; + int count = 0, i, j; + + debug_decl(sudo_sss_display_defaults, SUDO_DEBUG_SSSD); + + if (handle == NULL) + goto done; + + if (handle->fn_send_recv_defaults(pw->pw_uid, pw->pw_name, + &sss_error, &handle->domainname, + &sss_result) != 0) { + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_send_recv_defaults: !=0, sss_error=%u", sss_error); + goto done; + } + + if (sss_error == ENOENT) { + sudo_debug_printf(SUDO_DEBUG_INFO, "The user was not found in SSSD."); + goto done; + } else if(sss_error != 0) { + sudo_debug_printf(SUDO_DEBUG_INFO, "sss_error=%u\n", sss_error); + goto done; + } + + handle->pw = pw; + + for (i = 0; i < sss_result->num_rules; ++i) { + rule = sss_result->rules + i; + + switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { + case 0: + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + continue; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values: != 0"); + continue; + } + + if (lbuf->len == 0 || isspace((unsigned char)lbuf->buf[lbuf->len - 1])) + prefix = " "; + else + prefix = ", "; + + for (j = 0; val_array[j] != NULL; ++j) { + val = val_array[j]; + lbuf_append(lbuf, "%s%s", prefix, val); + prefix = ", "; + count++; + } + + handle->fn_free_values(val_array); + val_array = NULL; + } + + handle->fn_free_result(sss_result); +done: + debug_return_int(count); +} + +// ok +static int +sudo_sss_display_bound_defaults(struct sudo_nss *nss, + struct passwd *pw, struct lbuf *lbuf) +{ + debug_decl(sudo_sss_display_bound_defaults, SUDO_DEBUG_SSSD); + debug_return_int(0); +} + +static int +sudo_sss_display_entry_long(struct sudo_sss_handle *handle, + struct sss_sudo_rule *rule, struct lbuf *lbuf) +{ + char **val_array = NULL; + int count = 0, i; + debug_decl(sudo_sss_display_entry_long, SUDO_DEBUG_SSSD); + + /* get the RunAsUser Values from the entry */ + lbuf_append(lbuf, " RunAsUsers: "); + switch (handle->fn_get_values(rule, "sudoRunAsUser", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + break; + case ENOENT: + switch (handle->fn_get_values(rule, "sudoRunAs", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + lbuf_append(lbuf, "%s", def_runas_default); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAs): != 0"); + debug_return_int(count); + } + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsUser): != 0"); + debug_return_int(count); + } + lbuf_append(lbuf, "\n"); + + /* get the RunAsGroup Values from the entry */ + switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) { + case 0: + lbuf_append(lbuf, " RunAsGroups: "); + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + lbuf_append(lbuf, "\n"); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoRunAsGroup): != 0"); + debug_return_int(count); + } + + /* get the Option Values from the entry */ + switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { + case 0: + lbuf_append(lbuf, " Options: "); + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + lbuf_append(lbuf, "\n"); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoOption): != 0"); + debug_return_int(count); + } + + /* Get the command values from the entry. */ + switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) { + case 0: + lbuf_append(lbuf, _(" Commands:\n")); + for (i = 0; val_array[i] != NULL; ++i) { + lbuf_append(lbuf, "\t%s\n", val_array[i]); + count++; + } + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoCommand): != 0"); + debug_return_int(count); + } + + debug_return_int(count); +} + +static int +sudo_sss_display_entry_short(struct sudo_sss_handle *handle, + struct sss_sudo_rule *rule, struct lbuf *lbuf) +{ + char **val_array = NULL; + int count = 0, i; + debug_decl(sudo_sss_display_entry_short, SUDO_DEBUG_SSSD); + + lbuf_append(lbuf, " ("); + + /* get the RunAsUser Values from the entry */ + switch (handle->fn_get_values(rule, "sudoRunAsUser", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result. Trying old style (sudoRunAs)."); + /* try old style */ + switch (handle->fn_get_values(rule, "sudoRunAs", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + lbuf_append(lbuf, "%s", def_runas_default); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoRunAs): != 0"); + debug_return_int(count); + } + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoRunAsUser): != 0"); + debug_return_int(count); + } + + /* get the RunAsGroup Values from the entry */ + switch (handle->fn_get_values(rule, "sudoRunAsGroup", &val_array)) { + case 0: + lbuf_append(lbuf, " : "); + for (i = 0; val_array[i] != NULL; ++i) + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, "handle->fn_get_values(sudoRunAsGroup): != 0"); + debug_return_int(count); + } + + lbuf_append(lbuf, ") "); + + /* get the Option Values from the entry */ + switch (handle->fn_get_values(rule, "sudoOption", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) { + char *cp = val_array[i]; + if (*cp == '!') + cp++; + if (strcmp(cp, "authenticate") == 0) + lbuf_append(lbuf, val_array[i][0] == '!' ? + "NOPASSWD: " : "PASSWD: "); + else if (strcmp(cp, "noexec") == 0) + lbuf_append(lbuf, val_array[i][0] == '!' ? + "EXEC: " : "NOEXEC: "); + else if (strcmp(cp, "setenv") == 0) + lbuf_append(lbuf, val_array[i][0] == '!' ? + "NOSETENV: " : "SETENV: "); + } + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoOption): != 0"); + debug_return_int(count); + } + + /* get the Command Values from the entry */ + switch (handle->fn_get_values(rule, "sudoCommand", &val_array)) { + case 0: + for (i = 0; val_array[i] != NULL; ++i) { + lbuf_append(lbuf, "%s%s", i != 0 ? ", " : "", val_array[i]); + count++; + } + handle->fn_free_values(val_array); + break; + case ENOENT: + sudo_debug_printf(SUDO_DEBUG_INFO, "No result."); + break; + default: + sudo_debug_printf(SUDO_DEBUG_INFO, + "handle->fn_get_values(sudoCommand): != 0"); + debug_return_int(count); + } + lbuf_append(lbuf, "\n"); + + debug_return_int(count); +} + +static int +sudo_sss_display_privs(struct sudo_nss *nss, struct passwd *pw, + struct lbuf *lbuf) +{ + struct sudo_sss_handle *handle = nss->handle; + + struct sss_sudo_result *sss_result = NULL; + struct sss_sudo_rule *rule; + unsigned int i, count = 0; + debug_decl(sudo_sss_display_privs, SUDO_DEBUG_SSSD); + + if (handle == NULL) + debug_return_int(-1); + if (sudo_sss_checkpw(nss, pw) != 0) + debug_return_int(-1); + + sudo_debug_printf(SUDO_DEBUG_INFO, "sssd/ldap search for command list"); + + sss_result = sudo_sss_result_get(nss, pw, NULL); + + if (sss_result == NULL) + debug_return_int(count); + + /* Display all matching entries. */ + for (i = 0; i < sss_result->num_rules; ++i) { + rule = sss_result->rules + i; + if (long_list) + count += sudo_sss_display_entry_long(handle, rule, lbuf); + else + count += sudo_sss_display_entry_short(handle, rule, lbuf); + } + + if (sss_result != NULL) + handle->fn_free_result(sss_result); + + debug_return_int(count); +} diff --git a/plugins/sudoers/sudo_nss.c b/plugins/sudoers/sudo_nss.c index 9ae0739a2..5a1b3e124 100644 --- a/plugins/sudoers/sudo_nss.c +++ b/plugins/sudoers/sudo_nss.c @@ -47,8 +47,11 @@ extern struct sudo_nss sudo_nss_file; #ifdef HAVE_LDAP extern struct sudo_nss sudo_nss_ldap; #endif +#ifdef HAVE_SSSD +extern struct sudo_nss sudo_nss_sss; +#endif -#if defined(HAVE_LDAP) && defined(_PATH_NSSWITCH_CONF) +#if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NSSWITCH_CONF) /* * Read in /etc/nsswitch.conf * Returns a tail queue of matches. @@ -58,6 +61,9 @@ sudo_read_nss(void) { FILE *fp; char *cp; +#ifdef HAVE_SSSD + bool saw_sss = false; +#endif bool saw_files = false; bool saw_ldap = false; bool got_match = false; @@ -84,6 +90,11 @@ sudo_read_nss(void) } else if (strcasecmp(cp, "ldap") == 0 && !saw_ldap) { tq_append(&snl, &sudo_nss_ldap); got_match = true; +#ifdef HAVE_SSSD + } else if (strcasecmp(cp, "sss") == 0 && !saw_sss) { + tq_append(&snl, &sudo_nss_sss); + got_match = true; +#endif } else if (strcasecmp(cp, "[NOTFOUND=return]") == 0 && got_match) { /* NOTFOUND affects the most recent entry */ tq_last(&snl)->ret_if_notfound = true; @@ -104,9 +115,9 @@ nomatch: debug_return_ptr(&snl); } -#else /* HAVE_LDAP && _PATH_NSSWITCH_CONF */ +#else /* (HAVE_LDAP || HAVE_SSSD) && _PATH_NSSWITCH_CONF */ -# if defined(HAVE_LDAP) && defined(_PATH_NETSVC_CONF) +# if (defined(HAVE_LDAP) || defined(HAVE_SSSD)) && defined(_PATH_NETSVC_CONF) /* * Read in /etc/netsvc.conf (like nsswitch.conf on AIX) @@ -117,6 +128,9 @@ sudo_read_nss(void) { FILE *fp; char *cp, *ep; +#ifdef HAVE_SSSD + bool saw_sss = false; +#endif bool saw_files = false; bool saw_ldap = false; bool got_match = false; @@ -156,6 +170,13 @@ sudo_read_nss(void) tq_append(&snl, &sudo_nss_ldap); got_match = true; ep = &cp[4]; +#ifdef HAVE_SSSD + } else if (!saw_sss && strncasecmp(cp, "sss", 3) == 0 && + (isspace((unsigned char)cp[3]) || cp[3] == '\0')) { + tq_append(&snl, &sudo_nss_sss); + got_match = true; + ep = &cp[3]; +#endif } else { got_match = false; } @@ -195,6 +216,9 @@ sudo_read_nss(void) static struct sudo_nss_list snl; debug_decl(sudo_read_nss, SUDO_DEBUG_NSS) +# ifdef HAVE_SSSD + tq_append(&snl, &sudo_nss_sss); +# endif # ifdef HAVE_LDAP tq_append(&snl, &sudo_nss_ldap); # endif diff --git a/plugins/sudoers/sudoers.c b/plugins/sudoers/sudoers.c index b37b0f674..1c011a876 100644 --- a/plugins/sudoers/sudoers.c +++ b/plugins/sudoers/sudoers.c @@ -145,6 +145,7 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation, volatile int sources = 0; sigaction_t sa; struct sudo_nss *nss; + struct sudo_nss *nss_next; debug_decl(sudoers_policy_open, SUDO_DEBUG_PLUGIN) sudo_version = version; @@ -201,12 +202,15 @@ sudoers_policy_open(unsigned int version, sudo_conv_t conversation, set_perms(PERM_ROOT); /* Open and parse sudoers, set global defaults */ - tq_foreach_fwd(snl, nss) { - if (nss->open(nss) == 0 && nss->parse(nss) == 0) { - sources++; - if (nss->setdefs(nss) != 0) - log_error(NO_STDERR, _("problem with defaults entries")); - } + for (nss = snl->first; nss != NULL; nss = nss_next) { + nss_next = nss->next; + if (nss->open(nss) == 0 && nss->parse(nss) == 0) { + sources++; + if (nss->setdefs(nss) != 0) + log_error(NO_STDERR, _("problem with defaults entries")); + } else { + tq_remove(snl, nss); + } } if (sources == 0) { warningx(_("no valid sudoers sources found, quitting"));