diff options
Diffstat (limited to 'plugins/group_file')
-rw-r--r-- | plugins/group_file/Makefile.in | 234 | ||||
-rw-r--r-- | plugins/group_file/getgrent.c | 193 | ||||
-rw-r--r-- | plugins/group_file/group_file.c | 129 | ||||
-rw-r--r-- | plugins/group_file/group_file.exp | 1 | ||||
-rw-r--r-- | plugins/group_file/plugin_test.c | 222 |
5 files changed, 779 insertions, 0 deletions
diff --git a/plugins/group_file/Makefile.in b/plugins/group_file/Makefile.in new file mode 100644 index 0000000..3223292 --- /dev/null +++ b/plugins/group_file/Makefile.in @@ -0,0 +1,234 @@ +# +# SPDX-License-Identifier: ISC +# +# Copyright (c) 2010-2023 Todd C. Miller <Todd.Miller@sudo.ws> +# +# 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. +# +# @configure_input@ +# + +#### Start of system configuration section. #### + +srcdir = @srcdir@ +abs_srcdir = @abs_srcdir@ +top_srcdir = @top_srcdir@ +abs_top_srcdir = @abs_top_srcdir@ +top_builddir = @top_builddir@ +abs_top_builddir = @abs_top_builddir@ +devdir = @devdir@ +scriptdir = $(top_srcdir)/scripts +incdir = $(top_srcdir)/include +cross_compiling = @CROSS_COMPILING@ + +# Compiler & tools to use +CC = @CC@ +LIBTOOL = @LIBTOOL@ +SED = @SED@ +AWK = @AWK@ + +# Our install program supports extra flags... +INSTALL = $(SHELL) $(scriptdir)/install-sh -c +INSTALL_OWNER = -o $(install_uid) -g $(install_gid) +INSTALL_BACKUP = @INSTALL_BACKUP@ + +# Libraries +LT_LIBS = $(top_builddir)/lib/util/libsudo_util.la +LIBS = $(LT_LIBS) + +# C preprocessor flags +CPPFLAGS = -I$(incdir) -I$(top_builddir) @CPPFLAGS@ + +# Usually -O and/or -g +CFLAGS = @CFLAGS@ + +# Flags to pass to the link stage +LDFLAGS = @LDFLAGS@ +LT_LDFLAGS = @LT_LDFLAGS@ @LT_LDEXPORTS@ + +# Flags to pass to libtool +LTFLAGS = --tag=disable-static + +# Address sanitizer flags +ASAN_CFLAGS = @ASAN_CFLAGS@ +ASAN_LDFLAGS = @ASAN_LDFLAGS@ + +# PIE flags +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ + +# Stack smashing protection flags +HARDENING_CFLAGS = @HARDENING_CFLAGS@ +HARDENING_LDFLAGS = @HARDENING_LDFLAGS@ + +# cppcheck options, usually set in the top-level Makefile +CPPCHECK_OPTS = -q --enable=warning,performance,portability --suppress=constStatement --suppress=compareBoolExpressionWithInt --error-exitcode=1 --inline-suppr -Dva_copy=va_copy -U__cplusplus -UQUAD_MAX -UQUAD_MIN -UUQUAD_MAX -U_POSIX_HOST_NAME_MAX -U_POSIX_PATH_MAX -U__NBBY -DNSIG=64 + +# splint options, usually set in the top-level Makefile +SPLINT_OPTS = -D__restrict= -checks + +# PVS-studio options +PVS_CFG = $(top_srcdir)/PVS-Studio.cfg +PVS_IGNORE = 'V707,V011,V002,V536' +PVS_LOG_OPTS = -a 'GA:1,2' -e -t errorfile -d $(PVS_IGNORE) + +# Where to install things... +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +sbindir = @sbindir@ +sysconfdir = @sysconfdir@ +adminconfdir = @adminconfdir@ +libexecdir = @libexecdir@ +datarootdir = @datarootdir@ +localstatedir = @localstatedir@ +plugindir = @plugindir@ + +# File mode and map file to use for shared libraries/objects +shlib_enable = @SHLIB_ENABLE@ +shlib_mode = @SHLIB_MODE@ +shlib_exp = $(srcdir)/group_file.exp +shlib_map = group_file.map +shlib_opt = group_file.opt + +# User and group ids the installed files should be "owned" by +install_uid = 0 +install_gid = 0 + +#### End of system configuration section. #### + +SHELL = @SHELL@ + +OBJS = group_file.lo getgrent.lo + +IOBJS = $(OBJS:.lo=.i) + +POBJS = $(IOBJS:.i=.plog) + +LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/ + +VERSION = @PACKAGE_VERSION@ + +all: group_file.la + +depend: + $(scriptdir)/mkdep.pl --srcdir=$(abs_top_srcdir) \ + --builddir=$(abs_top_builddir) plugins/group_file/Makefile.in + cd $(top_builddir) && ./config.status --file plugins/group_file/Makefile + +Makefile: $(srcdir)/Makefile.in + cd $(top_builddir) && ./config.status --file plugins/group_file/Makefile + +.SUFFIXES: .c .h .i .lo .plog + +.c.lo: + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $< + +.c.i: + $(CC) -E -o $@ $(CPPFLAGS) $< + +.i.plog: + ifile=$<; rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $${ifile%i}c --i-file $< --output-file $@ + +$(shlib_map): $(shlib_exp) + @$(AWK) 'BEGIN { print "{\n\tglobal:" } { print "\t\t"$$0";" } END { print "\tlocal:\n\t\t*;\n};" }' $(shlib_exp) > $@ + +$(shlib_opt): $(shlib_exp) + @$(SED) 's/^/+e /' $(shlib_exp) > $@ + +group_file.la: $(OBJS) $(LT_LIBS) @LT_LDDEP@ + $(LIBTOOL) $(LTFLAGS) --mode=link $(CC) $(LDFLAGS) $(ASAN_LDFLAGS) $(HARDENING_LDFLAGS) $(LT_LDFLAGS) -o $@ $(OBJS) $(LIBS) -module -avoid-version -rpath $(plugindir) -shrext .so + +pre-install: + +install: install-plugin + +install-dirs: + $(SHELL) $(scriptdir)/mkinstalldirs $(DESTDIR)$(plugindir) + +install-binaries: + +install-includes: + +install-doc: + +install-plugin: install-dirs group_file.la + if [ X"$(shlib_enable)" = X"yes" ]; then \ + INSTALL_BACKUP='$(INSTALL_BACKUP)' $(LIBTOOL) $(LTFLAGS) --mode=install $(INSTALL) $(INSTALL_OWNER) -m $(shlib_mode) group_file.la $(DESTDIR)$(plugindir); \ + fi + +install-fuzzer: + +uninstall: + -$(LIBTOOL) $(LTFLAGS) --mode=uninstall rm -f $(DESTDIR)$(plugindir)/group_file.la + -test -z "$(INSTALL_BACKUP)" || \ + rm -f $(DESTDIR)$(plugindir)/group_file.so$(INSTALL_BACKUP) + +splint: + splint $(SPLINT_OPTS) -I$(incdir) -I$(top_builddir) $(srcdir)/*.c + +cppcheck: + cppcheck $(CPPCHECK_OPTS) -I$(incdir) -I$(top_builddir) $(srcdir)/*.c + +pvs-log-files: $(POBJS) + +pvs-studio: $(POBJS) + plog-converter $(PVS_LOG_OPTS) $(POBJS) + +fuzz: + +check-fuzzer: + +check: check-fuzzer + +check-verbose: check + +clean: + -$(LIBTOOL) $(LTFLAGS) --mode=clean rm -f *.lo *.o *.la + -rm -f *.i *.plog stamp-* core *.core core.* + +mostlyclean: clean + +distclean: clean + -rm -rf Makefile .libs $(shlib_map) $(shlib_opt) + +clobber: distclean + +realclean: distclean + rm -f TAGS tags + +cleandir: realclean + +.PHONY: clean mostlyclean distclean cleandir clobber realclean + +# Autogenerated dependencies, do not modify +getgrent.lo: $(srcdir)/getgrent.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/getgrent.c +getgrent.i: $(srcdir)/getgrent.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_util.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +getgrent.plog: getgrent.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/getgrent.c --i-file $< --output-file $@ +group_file.lo: $(srcdir)/group_file.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_plugin.h \ + $(top_builddir)/config.h + $(LIBTOOL) $(LTFLAGS) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(HARDENING_CFLAGS) $(srcdir)/group_file.c +group_file.i: $(srcdir)/group_file.c $(incdir)/compat/stdbool.h \ + $(incdir)/sudo_compat.h $(incdir)/sudo_plugin.h \ + $(top_builddir)/config.h + $(CC) -E -o $@ $(CPPFLAGS) $< +group_file.plog: group_file.i + rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/group_file.c --i-file $< --output-file $@ diff --git a/plugins/group_file/getgrent.c b/plugins/group_file/getgrent.c new file mode 100644 index 0000000..031aed5 --- /dev/null +++ b/plugins/group_file/getgrent.c @@ -0,0 +1,193 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2005,2008,2010-2015,2022 Todd C. Miller <Todd.Miller@sudo.ws> + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +/* + * Trivial replacements for the libc getgrent() family of functions. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <grp.h> + +#include <sudo_compat.h> +#include <sudo_util.h> + +#undef GRMEM_MAX +#define GRMEM_MAX 200 + +static FILE *grf; +static const char *grfile = "/etc/group"; +static int gr_stayopen; + +void mysetgrfile(const char *); +void mysetgrent(void); +void myendgrent(void); +int mysetgroupent(int); +struct group *mygetgrent(void); +struct group *mygetgrnam(const char *); +struct group *mygetgrgid(gid_t); + +void +mysetgrfile(const char *file) +{ + grfile = file; + if (grf != NULL) + myendgrent(); +} + +static int +open_group(int reset) +{ + if (grf == NULL) { + grf = fopen(grfile, "r"); + if (grf != NULL) { + if (fcntl(fileno(grf), F_SETFD, FD_CLOEXEC) == -1) { + fclose(grf); + grf = NULL; + } + } + if (grf == NULL) + return 0; + } else if (reset) { + rewind(grf); + } + return 1; +} + +int +mysetgroupent(int stayopen) +{ + if (!open_group(1)) + return 0; + gr_stayopen = stayopen; + return 1; +} + +void +mysetgrent(void) +{ + mysetgroupent(0); +} + +void +myendgrent(void) +{ + if (grf != NULL) { + fclose(grf); + grf = NULL; + } + gr_stayopen = 0; +} + +struct group * +mygetgrent(void) +{ + static struct group gr; + static char grbuf[LINE_MAX], *gr_mem[GRMEM_MAX+1]; + size_t len; + id_t id; + char *cp, *colon; + const char *errstr; + int n; + + if (!open_group(0)) + return NULL; + +next_entry: + if ((colon = fgets(grbuf, sizeof(grbuf), grf)) == NULL) + return NULL; + + memset(&gr, 0, sizeof(gr)); + if ((colon = strchr(cp = colon, ':')) == NULL) + goto next_entry; + *colon++ = '\0'; + gr.gr_name = cp; + if ((colon = strchr(cp = colon, ':')) == NULL) + goto next_entry; + *colon++ = '\0'; + gr.gr_passwd = cp; + if ((colon = strchr(cp = colon, ':')) == NULL) + goto next_entry; + *colon++ = '\0'; + id = sudo_strtoid(cp, &errstr); + if (errstr != NULL) + goto next_entry; + gr.gr_gid = (gid_t)id; + len = strlen(colon); + if (len > 0 && colon[len - 1] == '\n') + colon[len - 1] = '\0'; + if (*colon != '\0') { + char *last; + + gr.gr_mem = gr_mem; + cp = strtok_r(colon, ",", &last); + for (n = 0; cp != NULL && n < GRMEM_MAX; n++) { + gr.gr_mem[n] = cp; + cp = strtok_r(NULL, ",", &last); + } + gr.gr_mem[n] = NULL; + } else + gr.gr_mem = NULL; + return &gr; +} + +struct group * +mygetgrnam(const char *name) +{ + struct group *gr; + + if (!open_group(1)) + return NULL; + while ((gr = mygetgrent()) != NULL) { + if (strcmp(gr->gr_name, name) == 0) + break; + } + if (!gr_stayopen) { + fclose(grf); + grf = NULL; + } + return gr; +} + +struct group * +mygetgrgid(gid_t gid) +{ + struct group *gr; + + if (!open_group(1)) + return NULL; + while ((gr = mygetgrent()) != NULL) { + if (gr->gr_gid == gid) + break; + } + if (!gr_stayopen) { + fclose(grf); + grf = NULL; + } + return gr; +} diff --git a/plugins/group_file/group_file.c b/plugins/group_file/group_file.c new file mode 100644 index 0000000..de9a491 --- /dev/null +++ b/plugins/group_file/group_file.c @@ -0,0 +1,129 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2010-2014 Todd C. Miller <Todd.Miller@sudo.ws> + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include <config.h> + +#include <sys/stat.h> + +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_STDBOOL_H +# include <stdbool.h> +#else +# include <compat/stdbool.h> +#endif /* HAVE_STDBOOL_H */ +#include <string.h> +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> + +#include <sudo_plugin.h> +#include <sudo_compat.h> + +/* + * Sample sudoers group plugin that uses an extra group file with the + * same format as /etc/group. + */ + +static sudo_printf_t sudo_log; + +extern void mysetgrfile(const char *); +extern int mysetgroupent(int); +extern void myendgrent(void); +extern struct group *mygetgrnam(const char *); + +static int +sample_init(int version, sudo_printf_t sudo_printf, char *const argv[]) +{ + struct stat sb; + + sudo_log = sudo_printf; + + if (SUDO_API_VERSION_GET_MAJOR(version) != GROUP_API_VERSION_MAJOR) { + sudo_log(SUDO_CONV_ERROR_MSG, + "group_file: incompatible major version %d, expected %d\n", + SUDO_API_VERSION_GET_MAJOR(version), + GROUP_API_VERSION_MAJOR); + return -1; + } + + /* Check that the group file exists and has a safe mode. */ + if (argv == NULL || argv[0] == NULL) { + sudo_log(SUDO_CONV_ERROR_MSG, + "group_file: path to group file not specified\n"); + return -1; + } + if (stat(argv[0], &sb) != 0) { + sudo_log(SUDO_CONV_ERROR_MSG, + "group_file: %s: %s\n", argv[0], strerror(errno)); + return -1; + } + if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) { + sudo_log(SUDO_CONV_ERROR_MSG, + "%s must be only be writable by owner\n", argv[0]); + return -1; + } + + mysetgrfile(argv[0]); + if (!mysetgroupent(1)) + return false; + + return true; +} + +static void +sample_cleanup(void) +{ + myendgrent(); +} + +/* + * Returns true if "user" is a member of "group", else false. + */ +static int +sample_query(const char *user, const char *group, const struct passwd *pwd) +{ + struct group *grp; + char **member; + + grp = mygetgrnam(group); + if (grp != NULL && grp->gr_mem != NULL) { + for (member = grp->gr_mem; *member != NULL; member++) { + if (strcasecmp(user, *member) == 0) + return true; + } + } + + return false; +} + +sudo_dso_public struct sudoers_group_plugin group_plugin = { + GROUP_API_VERSION, + sample_init, + sample_cleanup, + sample_query +}; diff --git a/plugins/group_file/group_file.exp b/plugins/group_file/group_file.exp new file mode 100644 index 0000000..a859d6c --- /dev/null +++ b/plugins/group_file/group_file.exp @@ -0,0 +1 @@ +group_plugin diff --git a/plugins/group_file/plugin_test.c b/plugins/group_file/plugin_test.c new file mode 100644 index 0000000..ec47f73 --- /dev/null +++ b/plugins/group_file/plugin_test.c @@ -0,0 +1,222 @@ +/* + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2010-2013 Todd C. Miller <Todd.Miller@sudo.ws> + * + * 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. + */ + +/* + * This is an open source non-commercial project. Dear PVS-Studio, please check it. + * PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + */ + +#include <config.h> +#include <ctype.h> +#include <dlfcn.h> +#include <errno.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <sudo_plugin.h> + +sudo_dso_public int main(int argc, char *argv[]); + +/* + * Simple driver to test sudoer group plugins. + * usage: plugin_test [-p "plugin.so plugin_args ..."] user:group ... + */ + +static void *group_handle; +static struct sudoers_group_plugin *group_plugin; + +static int +plugin_printf(int msg_type, const char * restrict fmt, ...) +{ + va_list ap; + FILE *fp; + + switch (msg_type) { + case SUDO_CONV_INFO_MSG: + fp = stdout; + break; + case SUDO_CONV_ERROR_MSG: + fp = stderr; + break; + default: + errno = EINVAL; + return -1; + } + + va_start(ap, fmt); + vfprintf(fp, fmt, ap); + va_end(ap); + + return 0; +} + +/* + * Load the specified plugin and run its init function. + * Returns -1 if unable to open the plugin, else it returns + * the value from the plugin's init function. + */ +static int +group_plugin_load(char *plugin_info) +{ + char *args, path[PATH_MAX], savedch; + char **argv = NULL; + int rc; + + /* + * Fill in .so path and split out args (if any). + */ + if ((args = strpbrk(plugin_info, " \t")) != NULL) { + savedch = *args; + *args = '\0'; + } + if (strlcpy(path, plugin_info, sizeof(path)) >= sizeof(path)) { + fprintf(stderr, "path too long: %s\n", plugin_info); + return -1; + } + if (args != NULL) + *args++ = savedch; + + /* Open plugin and map in symbol. */ + group_handle = dlopen(path, RTLD_LAZY); + if (!group_handle) { + fprintf(stderr, "unable to dlopen %s: %s\n", path, dlerror()); + return -1; + } + group_plugin = dlsym(group_handle, "group_plugin"); + if (group_plugin == NULL) { + fprintf(stderr, "unable to find symbol \"group_plugin\" in %s\n", path); + return -1; + } + + if (SUDO_API_VERSION_GET_MAJOR(group_plugin->version) != GROUP_API_VERSION_MAJOR) { + fprintf(stderr, + "%s: incompatible group plugin major version %u, expected %u\n", + path, SUDO_API_VERSION_GET_MAJOR(group_plugin->version), + GROUP_API_VERSION_MAJOR); + return -1; + } + + /* + * Split args into a vector if specified. + */ + if (args != NULL) { + int ac = 0, wasblank = 1; + char *cp, *last; + + for (cp = args; *cp != '\0'; cp++) { + if (isblank((unsigned char)*cp)) { + wasblank = 1; + } else if (wasblank) { + wasblank = 0; + ac++; + } + } + if (ac != 0) { + argv = malloc((ac + 1) * sizeof(char *)); + if (argv == NULL) { + perror(NULL); + return -1; + } + ac = 0; + cp = strtok_r(args, " \t", &last); + while (cp != NULL) { + argv[ac++] = cp; + cp = strtok_r(NULL, " \t", &last); + } + argv[ac] = NULL; + } + } + + rc = (group_plugin->init)(GROUP_API_VERSION, plugin_printf, argv); + + free(argv); + + return rc; +} + +static void +group_plugin_unload(void) +{ + (group_plugin->cleanup)(); + dlclose(group_handle); + group_handle = NULL; +} + +static int +group_plugin_query(const char *user, const char *group, + const struct passwd *pwd) +{ + return (group_plugin->query)(user, group, pwd); +} + +static void +usage(void) +{ + fputs("usage: plugin_test [-p \"plugin.so plugin_args ...\"] user:group ...\n", + stderr); + exit(EXIT_FAILURE); +} + +int +main(int argc, char *argv[]) +{ + int ch, found; + size_t i; + char *plugin = "group_file.so"; + char *user, *group; + struct passwd *pwd; + + while ((ch = getopt(argc, argv, "p:")) != -1) { + switch (ch) { + case 'p': + plugin = optarg; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (group_plugin_load(plugin) != 1) { + fprintf(stderr, "unable to load plugin: %s\n", plugin); + return EXIT_FAILURE; + } + + for (i = 0; argv[i] != NULL; i++) { + user = argv[i]; + group = strchr(argv[i], ':'); + if (group == NULL) + continue; + *group++ = '\0'; + pwd = getpwnam(user); + found = group_plugin_query(user, group, pwd); + printf("user %s %s in group %s\n", user, found ? "is" : "NOT ", group); + } + group_plugin_unload(); + + return EXIT_SUCCESS; +} + |