compat/regress/glob/files
compat/regress/glob/globtest.c
compat/regress/glob/globtest.in
-compat/setenv.c
compat/siglist.in
compat/snprintf.c
compat/stdbool.h
compat/strlcpy.c
compat/strsignal.c
compat/timespec.h
-compat/unsetenv.c
compat/utime.h
compat/utimes.c
config.guess
pp
src/Makefile.in
src/conversation.c
+src/env_hooks.c
src/error.c
src/exec.c
src/exec_common.c
src/exec_pty.c
src/get_pty.c
+src/hooks.c
src/load_plugins.c
src/net_ifs.c
src/parse_args.c
fi
])
+dnl
+dnl check putenv() argument for const
+dnl
+AC_DEFUN([SUDO_FUNC_PUTENV_CONST],
+[AC_CACHE_CHECK([whether putenv takes a const argument],
+sudo_cv_func_putenv_const,
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT
+int putenv(const char *string) {return 0;}], [])],
+ [sudo_cv_func_putenv_const=yes],
+ [sudo_cv_func_putenv_const=no])
+ ])
+ if test $sudo_cv_func_putenv_const = yes; then
+ AC_DEFINE(PUTENV_CONST, const, [Define to const if the `putenv' takes a const argument.])
+ else
+ AC_DEFINE(PUTENV_CONST, [])
+ fi
+])
+
dnl
dnl check for sa_len field in struct sockaddr
dnl
"rbtree",
"perms",
"plugin",
+ "hooks",
NULL
};
nanosleep.lo: $(srcdir)/nanosleep.c $(top_builddir)/config.h \
$(top_srcdir)/compat/timespec.h $(incdir)/missing.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/nanosleep.c
-setenv.lo: $(srcdir)/setenv.c $(top_builddir)/config.h $(incdir)/missing.h
- $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/setenv.c
+pw_dup.lo: $(srcdir)/pw_dup.c $(top_builddir)/config.h
+ $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/pw_dup.c
siglist.lo: siglist.c $(top_builddir)/config.h $(incdir)/missing.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) siglist.c
snprintf.lo: $(srcdir)/snprintf.c $(top_builddir)/config.h $(incdir)/missing.h
strsignal.lo: $(srcdir)/strsignal.c $(top_builddir)/config.h \
$(incdir)/missing.h $(incdir)/gettext.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/strsignal.c
-unsetenv.lo: $(srcdir)/unsetenv.c $(top_builddir)/config.h $(incdir)/missing.h
- $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/unsetenv.c
utimes.lo: $(srcdir)/utimes.c $(top_builddir)/config.h \
$(top_srcdir)/compat/utime.h $(incdir)/missing.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/utimes.c
+++ /dev/null
-/*
- * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * 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 <config.h>
-
-#include <sys/types.h>
-
-#include <stdio.h>
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif /* HAVE_STRING_H */
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif /* HAVE_STRINGS_H */
-#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
-# include <malloc.h>
-#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
-#include <errno.h>
-
-#include "missing.h"
-
-int
-setenv(const char *var, const char *val, int overwrite)
-{
- char *envstr, *dst;
- const char *src;
- size_t esize;
-
- if (!var || *var == '\0') {
- errno = EINVAL;
- return -1;
- }
-
- /*
- * POSIX says a var name with '=' is an error but BSD
- * just ignores the '=' and anything after it.
- */
- for (src = var; *src != '\0' && *src != '='; src++)
- ;
- esize = (size_t)(src - var) + 2;
- if (val) {
- esize += strlen(val); /* glibc treats a NULL val as "" */
- }
-
- /* Allocate and fill in envstr. */
- if ((envstr = malloc(esize)) == NULL)
- return -1;
- for (src = var, dst = envstr; *src != '\0' && *src != '=';)
- *dst++ = *src++;
- *dst++ = '=';
- if (val) {
- for (src = val; *src != '\0';)
- *dst++ = *src++;
- }
- *dst = '\0';
-
- if (!overwrite && getenv(var) != NULL) {
- free(envstr);
- return 0;
- }
- return putenv(envstr);
-}
+++ /dev/null
-/*
- * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * 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 <config.h>
-
-#include <sys/types.h>
-
-#include <stdio.h>
-#ifdef STDC_HEADERS
-# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
-#endif /* STDC_HEADERS */
-#ifdef HAVE_STRING_H
-# include <string.h>
-#endif /* HAVE_STRING_H */
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif /* HAVE_STRINGS_H */
-#include <errno.h>
-
-#include "missing.h"
-
-extern char **environ; /* global environment */
-
-#ifdef UNSETENV_VOID
-void
-#else
-int
-#endif
-unsetenv(const char *var)
-{
- char **ep = environ;
- size_t len;
-
- if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
- errno = EINVAL;
-#ifdef UNSETENV_VOID
- return;
-#else
- return -1;
-#endif
- }
-
- len = strlen(var);
- while (*ep != NULL) {
- if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
- /* Found it; shift remainder + NULL over by one. */
- char **cur = ep;
- while ((*cur = *(cur + 1)) != NULL)
- cur++;
- /* Keep going, could be multiple instances of the var. */
- } else {
- ep++;
- }
- }
-#ifndef UNSETENV_VOID
- return 0;
-#endif
-}
/* The syslog priority sudo will use for successful attempts. */
#undef PRI_SUCCESS
+/* Define to const if the `putenv' takes a const argument. */
+#undef PUTENV_CONST
+
/* The default value of preloaded objects (if any). */
#undef RTLD_PRELOAD_DEFAULT
fi
LIBS=$ac_save_LIBS
-for ac_func in strrchr sysconf tzset strftime \
+for ac_func in strrchr sysconf tzset strftime setenv \
regcomp setlocale nl_langinfo mbr_check_membership \
setrlimit64
do :
fi
+fi
+done
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether putenv takes a const argument" >&5
+$as_echo_n "checking whether putenv takes a const argument... " >&6; }
+if ${sudo_cv_func_putenv_const+:} false; then :
+ $as_echo_n "(cached) " >&6
else
- case " $LIBOBJS " in
- *" unsetenv.$ac_objext "* ) ;;
- *) LIBOBJS="$LIBOBJS unsetenv.$ac_objext"
- ;;
-esac
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+int putenv(const char *string) {return 0;}
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ sudo_cv_func_putenv_const=yes
+else
+ sudo_cv_func_putenv_const=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
-done
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $sudo_cv_func_putenv_const" >&5
+$as_echo "$sudo_cv_func_putenv_const" >&6; }
+ if test $sudo_cv_func_putenv_const = yes; then
+
+$as_echo "#define PUTENV_CONST const" >>confdefs.h
+
+ else
+ $as_echo "#define PUTENV_CONST /**/" >>confdefs.h
+
+ fi
if test -z "$SKIP_SETRESUID"; then
for ac_func in setresuid
fi
-ac_fn_c_check_func "$LINENO" "setenv" "ac_cv_func_setenv"
-if test "x$ac_cv_func_setenv" = xyes; then :
- $as_echo "#define HAVE_SETENV 1" >>confdefs.h
-
-else
- case " $LIBOBJS " in
- *" setenv.$ac_objext "* ) ;;
- *) LIBOBJS="$LIBOBJS setenv.$ac_objext"
- ;;
-esac
-
-fi
-
for ac_func in nanosleep
do :
dnl Function checks
dnl
AC_FUNC_GETGROUPS
-AC_CHECK_FUNCS(strrchr sysconf tzset strftime \
+AC_CHECK_FUNCS(strrchr sysconf tzset strftime setenv \
regcomp setlocale nl_langinfo mbr_check_membership \
setrlimit64)
AC_REPLACE_FUNCS(getgrouplist)
])
])
])
-AC_CHECK_FUNCS(unsetenv, [SUDO_FUNC_UNSETENV_VOID], [AC_LIBOBJ(unsetenv)])
+AC_CHECK_FUNCS(unsetenv, [SUDO_FUNC_UNSETENV_VOID], [])
+SUDO_FUNC_PUTENV_CONST
if test -z "$SKIP_SETRESUID"; then
AC_CHECK_FUNCS(setresuid, [
SKIP_SETREUID=yes
COMPAT_TEST_PROGS="${COMPAT_TEST_PROGS}${COMPAT_TEST_PROGS+ }fnm_test"
])
SUDO_FUNC_ISBLANK
-AC_REPLACE_FUNCS(memrchr pw_dup strlcpy strlcat setenv)
+AC_REPLACE_FUNCS(memrchr pw_dup strlcpy strlcat)
AC_CHECK_FUNCS(nanosleep, [], [
# On Solaris, nanosleep is in librt
AC_CHECK_LIB(rt, nanosleep, [REPLAY_LIBS="${REPLAY_LIBS} -lrt"], [AC_LIBOBJ(nanosleep)])
#define SUDO_DEBUG_RBTREE (22<<4) /* red-black tree functions */
#define SUDO_DEBUG_PERMS (23<<4) /* uid/gid swapping functions */
#define SUDO_DEBUG_PLUGIN (24<<4) /* main plugin functions */
+#define SUDO_DEBUG_HOOKS (25<<4) /* hook functions */
#define SUDO_DEBUG_ALL 0xfff0 /* all subsystems */
/* Extract priority and convert to an index. */
/*
- * Copyright (c) 2009-2011 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2009-2012 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#define SUDO_API_VERSION_SET_MAJOR(vp, n) do { \
*(vp) = (*(vp) & 0x0000ffff) | ((n) << 16); \
} while(0)
-#define SUDO_VERSION_SET_MINOR(vp, n) do { \
+#define SUDO_API_VERSION_SET_MINOR(vp, n) do { \
*(vp) = (*(vp) & 0xffff0000) | (n); \
} while(0)
struct sudo_conv_reply replies[]);
typedef int (*sudo_printf_t)(int msg_type, const char *fmt, ...);
+/*
+ * Hook structure for the optional plugin hook list.
+ * This allows the plugin to hook into specific sudo and/or libc functions.
+ */
+struct sudo_hook {
+ int hook_version;
+ int hook_type;
+ int (*hook_fn)();
+ void *closure;
+};
+
+/* Hook API version major/minor */
+#define SUDO_HOOK_VERSION_MAJOR 1
+#define SUDO_HOOK_VERSION_MINOR 0
+#define SUDO_HOOK_MKVERSION(x, y) ((x << 16) | y)
+#define SUDO_HOOK_VERSION SUDO_HOOK_MKVERSION(SUDO_HOOK_VERSION_MAJOR, SUDO_HOOK_VERSION_MINOR)
+
+/* Getters and setters for hook API version */
+#define SUDO_HOOK_VERSION_GET_MAJOR(v) ((v) >> 16)
+#define SUDO_HOOK_VERSION_GET_MINOR(v) ((v) & 0xffff)
+#define SUDO_HOOK_VERSION_SET_MAJOR(vp, n) do { \
+ *(vp) = (*(vp) & 0x0000ffff) | ((n) << 16); \
+} while(0)
+#define SUDO_HOOK_VERSION_SET_MINOR(vp, n) do { \
+ *(vp) = (*(vp) & 0xffff0000) | (n); \
+} while(0)
+
+/*
+ * Hook function return values.
+ */
+#define SUDO_HOOK_RET_ERROR -1 /* error */
+#define SUDO_HOOK_RET_NEXT 0 /* go to the next hook in the list */
+#define SUDO_HOOK_RET_STOP 1 /* stop here, skip the rest of tghe list */
+
+/*
+ * Hooks for setenv/unsetenv/putenv/getenv.
+ * This allows the plugin to be notified when a PAM module modifies
+ * the environment so it can update the copy of the environment that
+ * is passed to execve().
+ */
+#define SUDO_HOOK_SETENV 1
+#define SUDO_HOOK_UNSETENV 2
+#define SUDO_HOOK_PUTENV 3
+#define SUDO_HOOK_GETENV 4
+
+/*
+ * Hook functions types.
+ */
+typedef int (*sudo_hook_fn_t)();
+typedef int (*sudo_hook_fn_setenv_t)(const char *name, const char *value, int overwrite, void *closure);
+typedef int (*sudo_hook_fn_putenv_t)(char *string, void *closure);
+typedef int (*sudo_hook_fn_getenv_t)(const char *name, char **value, void *closure);
+typedef int (*sudo_hook_fn_unsetenv_t)(const char *name, void *closure);
+
/* Policy plugin type and defines */
struct passwd;
struct policy_plugin {
int (*validate)(void);
void (*invalidate)(int remove);
int (*init_session)(struct passwd *pwd);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
};
/* I/O plugin type and defines */
int (*log_stdin)(const char *buf, unsigned int len);
int (*log_stdout)(const char *buf, unsigned int len);
int (*log_stderr)(const char *buf, unsigned int len);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
};
/* Sudoers group plugin version major/minor */
$makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo plugin_error.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 setenv.lo siglist.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo unsetenv.lo utimes.lo globtest.o fnm_test.o:;
+ $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:;
# Parse OBJS lines
my %objs;
debug_decl(sudo_aix_cleanup, SUDO_DEBUG_AUTH)
/* Unset AUTHSTATE as it may not be correct for the runas user. */
- unsetenv("AUTHSTATE");
+ sudo_unsetenv("AUTHSTATE");
debug_return_int(AUTH_SUCCESS);
}
size_t env_len; /* number of slots used, not counting NULL */
};
-/*
- * Prototypes
- */
-static void sudo_setenv(const char *, const char *, int);
-static void sudo_putenv(char *, int, int);
-
/*
* Copy of the sudo-managed environment.
*/
size_t len;
debug_decl(env_init, SUDO_DEBUG_ENV)
- for (ep = envp; *ep != NULL; ep++)
- continue;
- len = (size_t)(ep - envp);
+ if (envp == NULL) {
+ /* Reset to initial state. */
+ memset(&env, 0, sizeof(env));
+ } else {
+ /* Make private copy of envp. */
+ for (ep = envp; *ep != NULL; ep++)
+ continue;
+ len = (size_t)(ep - envp);
- env.env_len = len;
- env.env_size = len + 1 + 128;
- env.envp = emalloc2(env.env_size, sizeof(char *));
+ env.env_len = len;
+ env.env_size = len + 1 + 128;
+ env.envp = emalloc2(env.env_size, sizeof(char *));
#ifdef ENV_DEBUG
- memset(env.envp, 0, env.env_size * sizeof(char *));
+ memset(env.envp, 0, env.env_size * sizeof(char *));
#endif
- memcpy(env.envp, envp, len * sizeof(char *));
- env.envp[len] = '\0';
+ memcpy(env.envp, envp, len * sizeof(char *));
+ env.envp[len] = '\0';
+ }
debug_return;
}
+/*
+ * Getter for private copy of the environment.
+ */
char **
env_get(void)
{
return env.envp;
}
-/*
- * Similar to setenv(3) but operates on sudo's private copy of the environment
- * (not environ) and it always overwrites. The dupcheck param determines
- * whether we need to verify that the variable is not already set.
- */
-static void
-sudo_setenv(const char *var, const char *val, int dupcheck)
-{
- char *estring;
- size_t esize;
- debug_decl(sudo_setenv, SUDO_DEBUG_ENV)
-
- esize = strlen(var) + 1 + strlen(val) + 1;
- estring = emalloc(esize);
-
- /* Build environment string and insert it. */
- if (strlcpy(estring, var, esize) >= esize ||
- strlcat(estring, "=", esize) >= esize ||
- strlcat(estring, val, esize) >= esize) {
-
- errorx(1, _("internal error, sudo_setenv() overflow"));
- }
- sudo_putenv(estring, dupcheck, true);
-
- debug_return;
-}
-
/*
* Similar to putenv(3) but operates on sudo's private copy of the
* environment (not environ) and it always overwrites. The dupcheck param
* determines whether we need to verify that the variable is not already set.
* Will only overwrite an existing variable if overwrite is set.
+ * Does not include warnings or debugging to avoid recursive calls.
*/
-static void
-sudo_putenv(char *str, int dupcheck, int overwrite)
+static int
+sudo_putenv_nodebug(char *str, bool dupcheck, bool overwrite)
{
char **ep;
size_t len;
bool found = false;
- debug_decl(sudo_putenv, SUDO_DEBUG_ENV)
/* Make sure there is room for the new entry plus a NULL. */
if (env.env_len + 2 > env.env_size) {
- env.env_size += 128;
- env.envp = erealloc3(env.envp, env.env_size, sizeof(char *));
+ char **nenvp;
+ size_t nsize = env.env_size + 128;
+ nenvp = env.envp ? realloc(env.envp, nsize * sizeof(char *)) :
+ malloc(nsize * sizeof(char *));
+ if (nenvp == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ env.envp = nenvp;
+ env.env_size = nsize;
#ifdef ENV_DEBUG
memset(env.envp + env.env_len, 0,
(env.env_size - env.env_len) * sizeof(char *));
}
#ifdef ENV_DEBUG
- if (env.envp[env.env_len] != NULL)
- errorx(1, _("sudo_putenv: corrupted envp, length mismatch"));
+ if (env.envp[env.env_len] != NULL) {
+ errno = EINVAL;
+ return -1;
+ }
#endif
if (dupcheck) {
*ep++ = str;
*ep = NULL;
}
- debug_return;
+ return 0;
+}
+
+/*
+ * Similar to putenv(3) but operates on sudo's private copy of the
+ * environment (not environ) and it always overwrites. The dupcheck param
+ * determines whether we need to verify that the variable is not already set.
+ * Will only overwrite an existing variable if overwrite is set.
+ */
+static int
+sudo_putenv(char *str, bool dupcheck, bool overwrite)
+{
+ int rval;
+ debug_decl(sudo_putenv, SUDO_DEBUG_ENV)
+
+ rval = sudo_putenv_nodebug(str, dupcheck, overwrite);
+ if (rval == -1) {
+#ifdef ENV_DEBUG
+ if (env.envp[env.env_len] != NULL)
+ errorx(1, _("sudo_putenv: corrupted envp, length mismatch"));
+#endif
+ errorx(1, _("unable to allocate memory"));
+ }
+ debug_return_int(rval);
+}
+
+/*
+ * Similar to setenv(3) but operates on a private copy of the environment.
+ * The dupcheck param determines whether we need to verify that the variable
+ * is not already set.
+ */
+static int
+sudo_setenv2(const char *var, const char *val, bool dupcheck, bool overwrite)
+{
+ char *estring;
+ size_t esize;
+ debug_decl(sudo_setenv2, SUDO_DEBUG_ENV)
+
+ esize = strlen(var) + 1 + strlen(val) + 1;
+ estring = emalloc(esize);
+
+ /* Build environment string and insert it. */
+ if (strlcpy(estring, var, esize) >= esize ||
+ strlcat(estring, "=", esize) >= esize ||
+ strlcat(estring, val, esize) >= esize) {
+
+ errorx(1, _("internal error, sudo_setenv2() overflow"));
+ }
+ debug_return_int(sudo_putenv(estring, dupcheck, overwrite));
+}
+
+/*
+ * Similar to setenv(3) but operates on a private copy of the environment.
+ * Does not include warnings or debugging to avoid recursive calls.
+ */
+static int
+sudo_setenv_nodebug(const char *var, const char *val, int overwrite)
+{
+ char *estring;
+ size_t esize;
+
+ esize = strlen(var) + 1 + strlen(val) + 1;
+ if ((estring = malloc(esize)) == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Build environment string and insert it. */
+ if (strlcpy(estring, var, esize) >= esize ||
+ strlcat(estring, "=", esize) >= esize ||
+ strlcat(estring, val, esize) >= esize) {
+
+ errno = EINVAL;
+ return -1;
+ }
+ return sudo_putenv_nodebug(estring, true, overwrite);
+}
+
+/*
+ * Similar to setenv(3) but operates on a private copy of the environment.
+ */
+int
+sudo_setenv(const char *var, const char *val, int overwrite)
+{
+ int rval;
+ debug_decl(sudo_setenv, SUDO_DEBUG_ENV)
+
+ rval = sudo_setenv_nodebug(var, val, overwrite);
+ if (rval == -1) {
+ if (errno == EINVAL)
+ errorx(1, _("internal error, sudo_setenv() overflow"));
+ errorx(1, _("unable to allocate memory"));
+ }
+ debug_return_int(rval);
+}
+
+/*
+ * Similar to unsetenv(3) but operates on a private copy of the environment.
+ * Does not include warnings or debugging to avoid recursive calls.
+ */
+static int
+sudo_unsetenv_nodebug(const char *var)
+{
+ char **ep = env.envp;
+ size_t len;
+
+ if (ep == NULL || var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = strlen(var);
+ while (*ep != NULL) {
+ if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
+ /* Found it; shift remainder + NULL over by one. */
+ char **cur = ep;
+ while ((*cur = *(cur + 1)) != NULL)
+ cur++;
+ /* Keep going, could be multiple instances of the var. */
+ } else {
+ ep++;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Similar to unsetenv(3) but operates on a private copy of the environment.
+ */
+int
+sudo_unsetenv(const char *name)
+{
+ int rval;
+ debug_decl(sudo_unsetenv, SUDO_DEBUG_ENV)
+
+ rval = sudo_unsetenv_nodebug(name);
+
+ debug_return_int(rval);
+}
+
+/*
+ * Similar to getenv(3) but operates on a private copy of the environment.
+ * Does not include warnings or debugging to avoid recursive calls.
+ */
+static char *
+sudo_getenv_nodebug(const char *name)
+{
+ char **ep, *val = NULL;
+ size_t namelen = 0;
+
+ if (env.env_len != 0) {
+ /* For BSD compatibility, treat '=' in name like end of string. */
+ while (name[namelen] != '\0' && name[namelen] != '=')
+ namelen++;
+ for (ep = env.envp; *ep != NULL; ep++) {
+ if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
+ val = *ep + namelen + 1;
+ break;
+ }
+ }
+ }
+ return val;
+}
+
+/*
+ * Similar to getenv(3) but operates on a private copy of the environment.
+ */
+char *
+sudo_getenv(const char *name)
+{
+ char *val;
+ debug_decl(sudo_getenv, SUDO_DEBUG_ENV)
+
+ val = sudo_getenv_nodebug(name);
+
+ debug_return_str(val);
}
/*
* on sudoers options).
*/
if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
- sudo_setenv("SHELL", runas_pw->pw_shell, ISSET(didvar, DID_SHELL));
- sudo_setenv("LOGNAME", runas_pw->pw_name,
- ISSET(didvar, DID_LOGNAME));
- sudo_setenv("USER", runas_pw->pw_name, ISSET(didvar, DID_USER));
- sudo_setenv("USERNAME", runas_pw->pw_name,
- ISSET(didvar, DID_USERNAME));
+ sudo_setenv2("SHELL", runas_pw->pw_shell,
+ ISSET(didvar, DID_SHELL), true);
+ sudo_setenv2("LOGNAME", runas_pw->pw_name,
+ ISSET(didvar, DID_LOGNAME), true);
+ sudo_setenv2("USER", runas_pw->pw_name,
+ ISSET(didvar, DID_USER), true);
+ sudo_setenv2("USERNAME", runas_pw->pw_name,
+ ISSET(didvar, DID_USERNAME), true);
} else {
if (!ISSET(didvar, DID_SHELL))
- sudo_setenv("SHELL", sudo_user.pw->pw_shell, false);
+ sudo_setenv2("SHELL", sudo_user.pw->pw_shell, false, true);
if (!ISSET(didvar, DID_LOGNAME))
- sudo_setenv("LOGNAME", user_name, false);
+ sudo_setenv2("LOGNAME", user_name, false, true);
if (!ISSET(didvar, DID_USER))
- sudo_setenv("USER", user_name, false);
+ sudo_setenv2("USER", user_name, false, true);
if (!ISSET(didvar, DID_USERNAME))
- sudo_setenv("USERNAME", user_name, false);
+ sudo_setenv2("USERNAME", user_name, false, true);
}
/* If we didn't keep HOME, reset it based on target user. */
}
/* Replace the PATH envariable with a secure one? */
if (def_secure_path && !user_is_exempt()) {
- sudo_setenv("PATH", def_secure_path, true);
+ sudo_setenv2("PATH", def_secure_path, true, true);
SET(didvar, DID_PATH);
}
*/
if (def_set_logname && !ISSET(sudo_mode, MODE_LOGIN_SHELL|MODE_EDIT)) {
if (!ISSET(didvar, KEPT_LOGNAME))
- sudo_setenv("LOGNAME", runas_pw->pw_name, true);
+ sudo_setenv2("LOGNAME", runas_pw->pw_name, true, true);
if (!ISSET(didvar, KEPT_USER))
- sudo_setenv("USER", runas_pw->pw_name, true);
+ sudo_setenv2("USER", runas_pw->pw_name, true, true);
if (!ISSET(didvar, KEPT_USERNAME))
- sudo_setenv("USERNAME", runas_pw->pw_name, true);
+ sudo_setenv2("USERNAME", runas_pw->pw_name, true, true);
}
/* Set $HOME to target user if not preserving user's value. */
if (reset_home)
- sudo_setenv("HOME", runas_pw->pw_dir, true);
+ sudo_setenv2("HOME", runas_pw->pw_dir, true, true);
/* Provide default values for $TERM and $PATH if they are not set. */
if (!ISSET(didvar, DID_TERM))
sudo_putenv("TERM=unknown", false, false);
if (!ISSET(didvar, DID_PATH))
- sudo_setenv("PATH", _PATH_STDPATH, false);
+ sudo_setenv2("PATH", _PATH_STDPATH, false, true);
/* Set PS1 if SUDO_PS1 is set. */
if (ps1 != NULL)
/* Add the SUDO_COMMAND envariable (cmnd + args). */
if (user_args) {
easprintf(&cp, "%s %s", user_cmnd, user_args);
- sudo_setenv("SUDO_COMMAND", cp, true);
+ sudo_setenv2("SUDO_COMMAND", cp, true, true);
efree(cp);
} else {
- sudo_setenv("SUDO_COMMAND", user_cmnd, true);
+ sudo_setenv2("SUDO_COMMAND", user_cmnd, true, true);
}
/* Add the SUDO_USER, SUDO_UID, SUDO_GID environment variables. */
- sudo_setenv("SUDO_USER", user_name, true);
+ sudo_setenv2("SUDO_USER", user_name, true, true);
snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_uid);
- sudo_setenv("SUDO_UID", idbuf, true);
+ sudo_setenv2("SUDO_UID", idbuf, true, true);
snprintf(idbuf, sizeof(idbuf), "%u", (unsigned int) user_gid);
- sudo_setenv("SUDO_GID", idbuf, true);
+ sudo_setenv2("SUDO_GID", idbuf, true, true);
/* Free old environment. */
efree(old_envp);
def_env_keep = cur;
}
}
+
+int
+sudoers_hook_getenv(const char *name, char **value, void *closure)
+{
+ static bool in_progress = false; /* avoid recursion */
+
+ if (in_progress || env.envp == NULL)
+ return SUDO_HOOK_RET_NEXT;
+
+ in_progress = true;
+ *value = sudo_getenv_nodebug(name);
+ in_progress = false;
+ return SUDO_HOOK_RET_STOP;
+}
+
+int
+sudoers_hook_putenv(char *string, void *closure)
+{
+ static bool in_progress = false; /* avoid recursion */
+
+ if (in_progress || env.envp == NULL)
+ return SUDO_HOOK_RET_NEXT;
+
+ in_progress = true;
+ sudo_putenv_nodebug(string, true, true);
+ in_progress = false;
+ return SUDO_HOOK_RET_STOP;
+}
+
+int
+sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure)
+{
+ static bool in_progress = false; /* avoid recursion */
+
+ if (in_progress || env.envp == NULL)
+ return SUDO_HOOK_RET_NEXT;
+
+ in_progress = true;
+ sudo_setenv_nodebug(name, value, overwrite);
+ in_progress = false;
+ return SUDO_HOOK_RET_STOP;
+}
+
+int
+sudoers_hook_unsetenv(const char *name, void *closure)
+{
+ static bool in_progress = false; /* avoid recursion */
+
+ if (in_progress || env.envp == NULL)
+ return SUDO_HOOK_RET_NEXT;
+
+ in_progress = true;
+ sudo_unsetenv_nodebug(name);
+ in_progress = false;
+ return SUDO_HOOK_RET_STOP;
+}
DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
}
# else
- setenv("KRB5CCNAME", ldap_conf.krb5_ccname, true);
+ sudo_setenv("KRB5CCNAME", ldap_conf.krb5_ccname, true);
# endif
}
rc = ldap_sasl_interactive_bind_s(ld, ldap_conf.binddn, "GSSAPI",
DPRINTF(("gss_krb5_ccache_name() failed: %d", status), 1);
# else
if (old_ccname != NULL)
- setenv("KRB5CCNAME", old_ccname, true);
+ sudo_setenv("KRB5CCNAME", old_ccname, true);
else
- unsetenv("KRB5CCNAME");
+ sudo_unsetenv("KRB5CCNAME");
# endif
}
if (rc != LDAP_SUCCESS) {
debug_return_int(-1);
/* Prevent reading of user ldaprc and system defaults. */
- if (getenv("LDAPNOINIT") == NULL) {
+ if (sudo_getenv("LDAPNOINIT") == NULL) {
ldapnoinit = true;
- setenv("LDAPNOINIT", "1", true);
+ sudo_setenv("LDAPNOINIT", "1", true);
}
/* Connect to LDAP server */
}
if (ldapnoinit)
- unsetenv("LDAPNOINIT");
+ sudo_unsetenv("LDAPNOINIT");
/* Set LDAP options */
if (sudo_ldap_set_options(ld) < 0)
*command_infop = command_info;
*argv_out = edit_argv ? edit_argv : NewArgv;
- *user_env_out = env_get(); /* our private copy */
+
+ /* Get private version of the environment and zero out stashed copy. */
+ *user_env_out = env_get();
+ env_init(NULL);
goto done;
}
#endif /* USE_ADMIN_FLAG */
+static void
+sudoers_policy_register_hooks(int version, int (*register_hook)(struct sudo_hook *hook))
+{
+ struct sudo_hook hook;
+
+ memset(&hook, 0, sizeof(hook));
+ hook.hook_version = SUDO_HOOK_VERSION;
+
+ hook.hook_type = SUDO_HOOK_SETENV;
+ hook.hook_fn = sudoers_hook_setenv;
+ register_hook(&hook);
+
+ hook.hook_type = SUDO_HOOK_UNSETENV;
+ hook.hook_fn = sudoers_hook_unsetenv;
+ register_hook(&hook);
+
+ hook.hook_type = SUDO_HOOK_GETENV;
+ hook.hook_fn = sudoers_hook_getenv;
+ register_hook(&hook);
+
+ hook.hook_type = SUDO_HOOK_PUTENV;
+ hook.hook_fn = sudoers_hook_putenv;
+ register_hook(&hook);
+}
+
struct policy_plugin sudoers_policy = {
SUDO_POLICY_PLUGIN,
SUDO_API_VERSION,
sudoers_policy_list,
sudoers_policy_validate,
sudoers_policy_invalidate,
- sudoers_policy_init_session
+ sudoers_policy_init_session,
+ sudoers_policy_register_hooks
};
void read_env_file(const char *, int);
void rebuild_env(void);
void validate_env_vars(char * const envp[]);
+int sudo_setenv(const char *var, const char *val, int overwrite);
+int sudo_unsetenv(const char *var);
+char *sudo_getenv(const char *name);
+int sudoers_hook_getenv(const char *name, char **value, void *closure);
+int sudoers_hook_putenv(char *string, void *closure);
+int sudoers_hook_setenv(const char *name, const char *value, int overwrite, void *closure);
+int sudoers_hook_unsetenv(const char *name, void *closure);
/* fmt_string.c */
char *fmt_string(const char *, const char *);
PROGS = @PROGS@
-OBJS = conversation.o error.o exec.o exec_common.o exec_pty.o get_pty.o \
- net_ifs.o load_plugins.o parse_args.o sudo.o sudo_edit.o tgetpass.o \
- ttyname.o ttysize.o utmp.o @SUDO_OBJS@
+OBJS = conversation.o env_hooks.o error.o exec.o exec_common.o exec_pty.o \
+ get_pty.o hooks.o net_ifs.o load_plugins.o parse_args.o sudo.o \
+ sudo_edit.o tgetpass.o ttyname.o ttysize.o utmp.o @SUDO_OBJS@
LIBOBJDIR = $(top_builddir)/@ac_config_libobj_dir@/
$(incdir)/sudo_debug.h $(incdir)/gettext.h \
$(incdir)/sudo_plugin.h $(srcdir)/sudo_plugin_int.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/conversation.c
+env_hooks.o: $(srcdir)/env_hooks.c $(top_builddir)/config.h \
+ $(top_srcdir)/compat/dlfcn.h $(srcdir)/sudo.h \
+ $(top_builddir)/pathnames.h $(top_srcdir)/compat/stdbool.h \
+ $(incdir)/missing.h $(incdir)/alloc.h $(incdir)/error.h \
+ $(incdir)/fileops.h $(incdir)/list.h $(incdir)/sudo_conf.h \
+ $(incdir)/list.h $(incdir)/sudo_debug.h $(incdir)/gettext.h \
+ $(incdir)/sudo_plugin.h
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/env_hooks.c
error.o: $(srcdir)/error.c $(top_builddir)/config.h $(incdir)/missing.h \
$(incdir)/error.h $(incdir)/gettext.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/error.c
$(incdir)/fileops.h $(incdir)/list.h $(incdir)/sudo_conf.h \
$(incdir)/list.h $(incdir)/sudo_debug.h $(incdir)/gettext.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/get_pty.c
+hooks.o: $(srcdir)/hooks.c $(top_builddir)/config.h $(srcdir)/sudo.h \
+ $(top_builddir)/pathnames.h $(top_srcdir)/compat/stdbool.h \
+ $(incdir)/missing.h $(incdir)/alloc.h $(incdir)/error.h \
+ $(incdir)/fileops.h $(incdir)/list.h $(incdir)/sudo_conf.h \
+ $(incdir)/list.h $(incdir)/sudo_debug.h $(incdir)/gettext.h \
+ $(incdir)/sudo_plugin.h $(srcdir)/sudo_plugin_int.h \
+ $(incdir)/sudo_debug.h
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/hooks.c
load_plugins.o: $(srcdir)/load_plugins.c $(top_builddir)/config.h \
$(top_srcdir)/compat/dlfcn.h $(srcdir)/sudo.h \
$(top_builddir)/pathnames.h $(top_srcdir)/compat/stdbool.h \
--- /dev/null
+/*
+ * Copyright (c) 2010, 2012 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <config.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
+# include <malloc.h>
+#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
+#include <errno.h>
+#ifdef HAVE_DLOPEN
+# include <dlfcn.h>
+#else
+# include "compat/dlfcn.h"
+#endif
+
+#include "sudo.h"
+#include "sudo_plugin.h"
+
+extern char **environ; /* global environment pointer */
+static char **priv_environ; /* private environment pointer */
+
+static char *
+rpl_getenv(const char *name)
+{
+ char **ep, *val = NULL;
+ size_t namelen = 0;
+
+ /* For BSD compatibility, treat '=' in name like end of string. */
+ while (name[namelen] != '\0' && name[namelen] != '=')
+ namelen++;
+ for (ep = environ; *ep != NULL; ep++) {
+ if (strncmp(*ep, name, namelen) == 0 && (*ep)[namelen] == '=') {
+ val = *ep + namelen + 1;
+ break;
+ }
+ }
+ return val;
+}
+
+char *
+getenv(const char *name)
+{
+ char *val = NULL;
+
+ switch (process_hooks_getenv(name, &val)) {
+ case SUDO_HOOK_RET_STOP:
+ return val;
+ case SUDO_HOOK_RET_ERROR:
+ return NULL;
+ default: {
+#if defined(HAVE_DLOPEN)
+ char * (*fn)(const char *);
+
+ fn = dlsym(RTLD_NEXT, "getenv");
+ if (fn != NULL)
+ return fn(name);
+#endif /* HAVE_DLOPEN */
+ return rpl_getenv(name);
+ }
+ }
+}
+
+static int
+rpl_putenv(PUTENV_CONST char *string)
+{
+ char **ep;
+ size_t len;
+ bool found = false;
+
+ /* Look for existing entry. */
+ len = (strchr(string, '=') - string) + 1;
+ for (ep = environ; *ep != NULL; ep++) {
+ if (strncmp(string, *ep, len) == 0) {
+ *ep = string;
+ found = true;
+ break;
+ }
+ }
+ /* Prune out duplicate variables. */
+ if (found) {
+ while (*ep != NULL) {
+ if (strncmp(string, *ep, len) == 0) {
+ char **cur = ep;
+ while ((*cur = *(cur + 1)) != NULL)
+ cur++;
+ } else {
+ ep++;
+ }
+ }
+ }
+
+ /* Append at the end if not already found. */
+ if (!found) {
+ size_t env_len = (size_t)(ep - environ);
+ char **envp = erealloc3(priv_environ, env_len + 2, sizeof(char *));
+ if (environ != priv_environ)
+ memcpy(envp, environ, env_len * sizeof(char *));
+ envp[env_len++] = string;
+ envp[env_len] = NULL;
+ priv_environ = environ = envp;
+ }
+ return 0;
+}
+
+int
+putenv(PUTENV_CONST char *string)
+{
+ switch (process_hooks_putenv((char *)string)) {
+ case SUDO_HOOK_RET_STOP:
+ return 0;
+ case SUDO_HOOK_RET_ERROR:
+ return -1;
+ default: {
+#if defined(HAVE_DLOPEN)
+ int (*fn)(PUTENV_CONST char *);
+
+ fn = dlsym(RTLD_NEXT, "putenv");
+ if (fn != NULL)
+ return fn(string);
+#endif /* HAVE_DLOPEN */
+ return rpl_putenv(string);
+ }
+ }
+}
+
+static int
+rpl_setenv(const char *var, const char *val, int overwrite)
+{
+ char *envstr, *dst;
+ const char *src;
+ size_t esize;
+
+ if (!var || *var == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * POSIX says a var name with '=' is an error but BSD
+ * just ignores the '=' and anything after it.
+ */
+ for (src = var; *src != '\0' && *src != '='; src++)
+ ;
+ esize = (size_t)(src - var) + 2;
+ if (val) {
+ esize += strlen(val); /* glibc treats a NULL val as "" */
+ }
+
+ /* Allocate and fill in envstr. */
+ if ((envstr = malloc(esize)) == NULL)
+ return -1;
+ for (src = var, dst = envstr; *src != '\0' && *src != '=';)
+ *dst++ = *src++;
+ *dst++ = '=';
+ if (val) {
+ for (src = val; *src != '\0';)
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+
+ if (!overwrite && getenv(var) != NULL) {
+ free(envstr);
+ return 0;
+ }
+ return rpl_putenv(envstr);
+}
+
+int
+setenv(const char *var, const char *val, int overwrite)
+{
+ switch (process_hooks_setenv(var, val, overwrite)) {
+ case SUDO_HOOK_RET_STOP:
+ return 0;
+ case SUDO_HOOK_RET_ERROR:
+ return -1;
+ default: {
+#if defined(HAVE_SETENV) && defined(HAVE_DLOPEN)
+ int (*fn)(const char *, const char *, int);
+
+ fn = dlsym(RTLD_NEXT, "setenv");
+ if (fn != NULL)
+ return fn(var, val, overwrite);
+#endif /* HAVE_SETENV && HAVE_DLOPEN */
+ return rpl_setenv(var, val, overwrite);
+ }
+ }
+}
+
+#ifdef UNSETENV_VOID
+static void
+#else
+int
+#endif
+rpl_unsetenv(const char *var)
+{
+ char **ep = environ;
+ size_t len;
+
+ if (var == NULL || *var == '\0' || strchr(var, '=') != NULL) {
+ errno = EINVAL;
+#ifdef UNSETENV_VOID
+ return;
+#else
+ return -1;
+#endif
+ }
+
+ len = strlen(var);
+ while (*ep != NULL) {
+ if (strncmp(var, *ep, len) == 0 && (*ep)[len] == '=') {
+ /* Found it; shift remainder + NULL over by one. */
+ char **cur = ep;
+ while ((*cur = *(cur + 1)) != NULL)
+ cur++;
+ /* Keep going, could be multiple instances of the var. */
+ } else {
+ ep++;
+ }
+ }
+#ifndef UNSETENV_VOID
+ return 0;
+#endif
+}
+
+#ifdef UNSETENV_VOID
+void
+unsetenv(const char *var)
+{
+ switch (process_hooks_unsetenv(var)) {
+ case SUDO_HOOK_RET_STOP:
+ return 0;
+ case SUDO_HOOK_RET_ERROR:
+ return -1;
+ default: {
+#if defined(HAVE_UNSETENV) && defined(HAVE_DLOPEN)
+ void (*fn)(const char *);
+
+ fn = dlsym(RTLD_NEXT, "unsetenv");
+ if (fn != NULL)
+ fn(var);
+ else
+#endif /* HAVE_UNSETENV && HAVE_DLOPEN */
+ rpl_unsetenv(var);
+ }
+ }
+}
+#else
+int
+unsetenv(const char *var)
+{
+ switch (process_hooks_unsetenv(var)) {
+ case SUDO_HOOK_RET_STOP:
+ return 0;
+ case SUDO_HOOK_RET_ERROR:
+ return -1;
+ default: {
+#if defined(HAVE_UNSETENV) && defined(HAVE_DLOPEN)
+ int (*fn)(const char *);
+
+ fn = dlsym(RTLD_NEXT, "unsetenv");
+ if (fn != NULL)
+ return fn(var);
+#endif /* HAVE_UNSETENV && HAVE_DLOPEN */
+ return rpl_unsetenv(var);
+ }
+ }
+}
+#endif /* UNSETENV_VOID */
--- /dev/null
+/*
+ * Copyright (c) 2012 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * 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 <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#ifdef STDC_HEADERS
+# include <stdlib.h>
+# include <stddef.h>
+#else
+# ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+# endif
+#endif /* STDC_HEADERS */
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#include "sudo.h"
+#include "sudo_plugin.h"
+#include "sudo_plugin_int.h"
+#include "sudo_debug.h"
+
+/* XXX - autogen from config file? */
+/* XXX - implement deregister_hook */
+
+/* HOOK: setenv */
+
+static struct sudo_hook_setenv {
+ struct sudo_hook_setenv *next;
+ sudo_hook_fn_setenv_t hook_fn;
+ void *closure;
+} *sudo_hook_setenv_list;
+
+static void
+register_hook_setenv(int (*hook_fn)(), void *closure)
+{
+ struct sudo_hook_setenv *hook;
+ debug_decl(add_hook_setenv, SUDO_DEBUG_HOOKS)
+
+ hook = emalloc(sizeof(*hook));
+ hook->hook_fn = (sudo_hook_fn_setenv_t)hook_fn;
+ hook->closure = closure;
+ hook->next = sudo_hook_setenv_list;
+ sudo_hook_setenv_list = hook;
+
+ debug_return;
+}
+
+int
+process_hooks_setenv(const char *name, const char *value, int overwrite)
+{
+ struct sudo_hook_setenv *hook;
+ int rc = SUDO_HOOK_RET_NEXT;
+ debug_decl(process_hooks_setenv, SUDO_DEBUG_HOOKS)
+
+ /* First process the hooks. */
+ for (hook = sudo_hook_setenv_list; hook != NULL; hook = hook->next) {
+ rc = hook->hook_fn(name, value, overwrite, hook->closure);
+ switch (rc) {
+ case SUDO_HOOK_RET_NEXT:
+ break;
+ case SUDO_HOOK_RET_ERROR:
+ case SUDO_HOOK_RET_STOP:
+ goto done;
+ default:
+ warningx("invalid setenv hook return value: %d", rc);
+ break;
+ }
+ }
+done:
+ debug_return_int(rc);
+}
+
+/* HOOK: putenv */
+
+static struct sudo_hook_putenv {
+ struct sudo_hook_putenv *next;
+ sudo_hook_fn_putenv_t hook_fn;
+ void *closure;
+} *sudo_hook_putenv_list;
+
+static void
+register_hook_putenv(int (*hook_fn)(), void *closure)
+{
+ struct sudo_hook_putenv *hook;
+ debug_decl(add_hook_putenv, SUDO_DEBUG_HOOKS)
+
+ hook = emalloc(sizeof(*hook));
+ hook->hook_fn = (sudo_hook_fn_putenv_t)hook_fn;
+ hook->closure = closure;
+ hook->next = sudo_hook_putenv_list;
+ sudo_hook_putenv_list = hook;
+
+ debug_return;
+}
+
+int
+process_hooks_putenv(char *string)
+{
+ struct sudo_hook_putenv *hook;
+ int rc = SUDO_HOOK_RET_NEXT;
+ debug_decl(process_hooks_putenv, SUDO_DEBUG_HOOKS)
+
+ /* First process the hooks. */
+ for (hook = sudo_hook_putenv_list; hook != NULL; hook = hook->next) {
+ rc = hook->hook_fn(string, hook->closure);
+ switch (rc) {
+ case SUDO_HOOK_RET_NEXT:
+ break;
+ case SUDO_HOOK_RET_ERROR:
+ case SUDO_HOOK_RET_STOP:
+ goto done;
+ default:
+ warningx("invalid putenv hook return value: %d", rc);
+ break;
+ }
+ }
+done:
+ debug_return_int(rc);
+}
+
+/* HOOK: getenv */
+
+static struct sudo_hook_getenv {
+ struct sudo_hook_getenv *next;
+ sudo_hook_fn_getenv_t hook_fn;
+ void *closure;
+} *sudo_hook_getenv_list;
+
+static void
+register_hook_getenv(int (*hook_fn)(), void *closure)
+{
+ struct sudo_hook_getenv *hook;
+ debug_decl(add_hook_putenv, SUDO_DEBUG_HOOKS)
+
+ hook = emalloc(sizeof(*hook));
+ hook->hook_fn = (sudo_hook_fn_getenv_t)hook_fn;
+ hook->closure = closure;
+ hook->next = sudo_hook_getenv_list;
+ sudo_hook_getenv_list = hook;
+
+ debug_return;
+}
+
+int
+process_hooks_getenv(const char *name, char **value)
+{
+ struct sudo_hook_getenv *hook;
+ char *val = NULL;
+ int rc = SUDO_HOOK_RET_NEXT;
+ debug_decl(process_hooks_getenv, SUDO_DEBUG_HOOKS)
+
+ /* First process the hooks. */
+ for (hook = sudo_hook_getenv_list; hook != NULL; hook = hook->next) {
+ rc = hook->hook_fn(name, &val, hook->closure);
+ switch (rc) {
+ case SUDO_HOOK_RET_NEXT:
+ break;
+ case SUDO_HOOK_RET_ERROR:
+ case SUDO_HOOK_RET_STOP:
+ goto done;
+ default:
+ warningx("invalid getenv hook return value: %d", rc);
+ break;
+ }
+ }
+done:
+ if (val != NULL)
+ *value = val;
+ debug_return_int(rc);
+}
+
+/* HOOK: unsetenv */
+
+static struct sudo_hook_unsetenv {
+ struct sudo_hook_unsetenv *next;
+ sudo_hook_fn_unsetenv_t hook_fn;
+ void *closure;
+} *sudo_hook_unsetenv_list;
+
+static void
+register_hook_unsetenv(int (*hook_fn)(), void *closure)
+{
+ struct sudo_hook_unsetenv *hook;
+ debug_decl(add_hook_unsetenv, SUDO_DEBUG_HOOKS)
+
+ hook = emalloc(sizeof(*hook));
+ hook->hook_fn = (sudo_hook_fn_unsetenv_t)hook_fn;
+ hook->closure = closure;
+ hook->next = sudo_hook_unsetenv_list;
+ sudo_hook_unsetenv_list = hook;
+
+ debug_return;
+}
+
+int
+process_hooks_unsetenv(const char *name)
+{
+ struct sudo_hook_unsetenv *hook;
+ int rc = SUDO_HOOK_RET_NEXT;
+ debug_decl(process_hooks_unsetenv, SUDO_DEBUG_HOOKS)
+
+ /* First process the hooks. */
+ for (hook = sudo_hook_unsetenv_list; hook != NULL; hook = hook->next) {
+ rc = hook->hook_fn(name, hook->closure);
+ switch (rc) {
+ case SUDO_HOOK_RET_NEXT:
+ break;
+ case SUDO_HOOK_RET_ERROR:
+ case SUDO_HOOK_RET_STOP:
+ goto done;
+ default:
+ warningx("invalid unsetenv hook return value: %d", rc);
+ break;
+ }
+ }
+done:
+ debug_return_int(rc);
+}
+
+/* Register the specified hook. */
+int
+register_hook(struct sudo_hook *hook)
+{
+ int rval = 0;
+ debug_decl(register_hook, SUDO_DEBUG_HOOKS)
+
+ if (SUDO_HOOK_VERSION_GET_MAJOR(hook->hook_version) != SUDO_HOOK_VERSION_MAJOR) {
+ /* Major versions must match. */
+ rval = -1;
+ } else {
+ switch (hook->hook_type) {
+ case SUDO_HOOK_GETENV:
+ register_hook_getenv(hook->hook_fn, hook->closure);
+ break;
+ case SUDO_HOOK_PUTENV:
+ register_hook_putenv(hook->hook_fn, hook->closure);
+ break;
+ case SUDO_HOOK_SETENV:
+ register_hook_setenv(hook->hook_fn, hook->closure);
+ break;
+ case SUDO_HOOK_UNSETENV:
+ register_hook_unsetenv(hook->hook_fn, hook->closure);
+ break;
+ default:
+ /* XXX - use define for unknown value */
+ rval = 1;
+ break;
+ }
+ }
+
+ debug_return_int(rval);
+}
goto done;
}
+ /* Install hooks (XXX - later). */
+ if (policy_plugin->u.policy->version >= SUDO_API_MKVERSION(1, 2)) {
+ if (policy_plugin->u.policy->register_hooks != NULL)
+ policy_plugin->u.policy->register_hooks(SUDO_HOOK_VERSION, register_hook);
+ tq_foreach_fwd(io_plugins, container) {
+ if (container->u.io->register_hooks != NULL)
+ container->u.io->register_hooks(SUDO_HOOK_VERSION, register_hook);
+ }
+ }
+
rval = true;
done:
aix_restoreauthdb();
#endif
+ /*
+ * Swap in the plugin-supplied environment in case session init
+ * modifies the environment. Also needed for LOGIN_SETENV.
+ * This is kind of a hack.
+ */
+ environ = details->envp;
+
/*
* Call policy plugin's session init before other setup occurs.
* The session init code is expected to print an error as needed.
flags = LOGIN_SETALL;
CLR(flags, LOGIN_SETGROUP|LOGIN_SETLOGIN|LOGIN_SETUSER);
CLR(details->flags, CD_SET_UMASK); /* LOGIN_UMASK instead */
- /* Swap in the plugin-supplied environment for LOGIN_SETENV */
- environ = details->envp;
} else {
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
}
} else
warning(_("unable to set user context"));
}
- if (ISSET(sudo_mode, MODE_LOGIN_SHELL)) {
- /* Stash the updated environment pointer in command details */
- details->envp = environ;
- }
}
#endif /* HAVE_LOGIN_CAP_H */
}
+ /* Update the environment pointer in command details */
+ details->envp = environ;
+
/*
* Set groups, including supplementary group vector.
*/
void aix_restoreauthdb(void);
void aix_setauthdb(char *user);
+/* hooks.c */
+/* XXX - move to sudo_plugin_int.h? */
+struct sudo_hook;
+int register_hook(struct sudo_hook *hook);
+int deregister_hook(struct sudo_hook *hook);
+int process_hooks_getenv(const char *name, char **val);
+int process_hooks_setenv(const char *name, const char *value, int overwrite);
+int process_hooks_putenv(char *string);
+int process_hooks_unsetenv(const char *name);
+
/* interfaces.c */
int get_net_ifs(char **addrinfo);
/*
- * Copyright (c) 2010-2011 Todd C. Miller <Todd.Miller@courtesan.com>
+ * Copyright (c) 2010-2012 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above