]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add secure_getenv() support
authorGreg Hudson <ghudson@mit.edu>
Wed, 24 Apr 2019 19:56:43 +0000 (15:56 -0400)
committerGreg Hudson <ghudson@mit.edu>
Tue, 30 Apr 2019 20:40:27 +0000 (16:40 -0400)
On systems with secure_getenv() (glibc 2.17+) use it directly.  For
the fallback implementation, check the current process uids and gids
in a library initializer, looking at the saved uid and gid where
possible.  Include a comment about more aggressive approaches to
detecting elevated privilege.

ticket: 8800 (new)

src/configure.ac
src/include/k5-platform.h
src/util/support/Makefile.in
src/util/support/secure_getenv.c [new file with mode: 0644]

index 87d21350d95039a0a53d3c12623da0200a8c52eb..9bafee73e6348aee19c637a15b42cec900d89a33 100644 (file)
@@ -148,7 +148,7 @@ AC_SUBST(po)
 
 # for kdc
 AC_CHECK_HEADERS(sys/sockio.h ifaddrs.h unistd.h fnmatch.h)
-AC_CHECK_FUNCS(vsprintf vasprintf vsnprintf strlcpy fnmatch)
+AC_CHECK_FUNCS(vsprintf vasprintf vsnprintf strlcpy fnmatch secure_getenv)
 
 EXTRA_SUPPORT_SYMS=
 AC_CHECK_FUNC(strlcpy,
@@ -208,6 +208,18 @@ KRB5_NEED_PROTO([#include <string.h>
 #include <stdlib.h>
 ],swab,1)
 
+AC_CHECK_FUNC(secure_getenv,
+[SECURE_GETENV_ST_OBJ=
+SECURE_GETENV_OBJ=
+SECURE_GETENV_INIT=],
+[SECURE_GETENV_ST_OBJ=secure_getenv.o
+SECURE_GETENV_OBJ='$(OUTPRE)secure_getenv.$(OBJEXT)'
+SECURE_GETENV_INIT=k5_secure_getenv_init
+EXTRA_SUPPORT_SYMS="$EXTRA_SUPPORT_SYMS k5_secure_getenv"])
+AC_SUBST(SECURE_GETENV_OBJ)
+AC_SUBST(SECURE_GETENV_ST_OBJ)
+AC_SUBST(SECURE_GETENV_INIT)
+
 AC_PROG_AWK
 KRB5_AC_INET6
 KRB5_SOCKADDR_SA_LEN
@@ -442,7 +454,7 @@ AC_PROG_LEX
 AC_C_CONST
 AC_HEADER_DIRENT
 AC_FUNC_STRERROR_R
-AC_CHECK_FUNCS(strdup setvbuf seteuid setresuid setreuid setegid setresgid setregid setsid flock fchmod chmod strptime geteuid setenv unsetenv getenv gmtime_r localtime_r bswap16 bswap64 mkstemp getusershell access getcwd srand48 srand srandom stat strchr strerror timegm explicit_bzero explicit_memset)
+AC_CHECK_FUNCS(strdup setvbuf seteuid setresuid setreuid setegid setresgid setregid setsid flock fchmod chmod strptime geteuid setenv unsetenv getenv gmtime_r localtime_r bswap16 bswap64 mkstemp getusershell access getcwd srand48 srand srandom stat strchr strerror timegm explicit_bzero explicit_memset getresuid getresgid)
 
 AC_CHECK_FUNC(mkstemp,
 [MKSTEMP_ST_OBJ=
index 1fcd68e8c0ebe6ef3cdc75247cbb90f31fa03ed3..11249317f9e7ea229af2326a138ff2581010a21a 100644 (file)
@@ -45,6 +45,7 @@
  * + path manipulation
  * + _, N_, dgettext, bindtextdomain (for localization)
  * + getopt_long
+ * + secure_getenv
  * + fetching filenames from a directory
  */
 
@@ -1134,6 +1135,14 @@ extern int k5_getopt_long(int nargc, char **nargv, char *options,
 #define getopt_long k5_getopt_long
 #endif /* HAVE_GETOPT_LONG */
 
+#if defined(_WIN32)
+/* On Windows there is never a need to ignore the process environment. */
+#define secure_getenv getenv
+#elif !defined(HAVE_SECURE_GETENV)
+#define secure_getenv k5_secure_getenv
+extern char *k5_secure_getenv(const char *name);
+#endif
+
 /* Set *fnames_out to a null-terminated list of filenames within dirname,
  * sorted according to strcmp().  Return 0 on success, or ENOENT/ENOMEM. */
 int k5_dir_filenames(const char *dirname, char ***fnames_out);
index db7b030b864ca723cb8f378e1fa253a8dad33af2..86d5a950a65248d6ace4299bea757527dd7e0c2a 100644 (file)
@@ -15,7 +15,7 @@ LIBBASE=krb5support
 LIBMAJOR=@SUPPORTLIB_MAJOR@
 LIBMINOR=1
 
-LIBINITFUNC=krb5int_thread_support_init
+LIBINITFUNC=krb5int_thread_support_init @SECURE_GETENV_INIT@
 LIBFINIFUNC=krb5int_thread_support_fini
 
 GETTIMEOFDAY_ST_OBJ= @GETTIMEOFDAY_ST_OBJ@
@@ -53,6 +53,11 @@ GETOPT_LONG_OBJ= @GETOPT_LONG_OBJ@
 ##DOS##GETOPT_LONG_ST_OBJ= getopt_long.o
 ##DOS##GETOPT_LONG_OBJ= $(OUTPRE)getopt_long.$(OBJEXT)
 
+SECURE_GETENV_ST_OBJ= @SECURE_GETENV_ST_OBJ@
+SECURE_GETENV_OBJ= @SECURE_GETENV_OBJ@
+##DOS##SECURE_GETENV_ST_OBJ=
+##DOS##SECURE_GETENV_OBJ=
+
 IPC_ST_OBJ=
 IPC_OBJ=
 ##DOS##IPC_ST_OBJ= ipc_stream.o
@@ -93,7 +98,8 @@ STLIBOBJS= \
        $(PRINTF_ST_OBJ) \
        $(MKSTEMP_ST_OBJ) \
        $(GETOPT_ST_OBJ) \
-       $(GETOPT_LONG_ST_OBJ)
+       $(GETOPT_LONG_ST_OBJ) \
+       $(SECURE_GETENV_OBJ)
 
 LIBOBJS= \
        $(OUTPRE)threads.$(OBJEXT) \
@@ -121,7 +127,8 @@ LIBOBJS= \
        $(PRINTF_OBJ) \
        $(MKSTEMP_OBJ) \
        $(GETOPT_OBJ) \
-       $(GETOPT_LONG_OBJ)
+       $(GETOPT_LONG_OBJ) \
+       $(SECURE_GETENV_OBJ)
 
 SRCS=\
        $(srcdir)/threads.c \
@@ -156,7 +163,8 @@ SRCS=\
        $(srcdir)/t_utf8.c \
        $(srcdir)/t_utf16.c \
        $(srcdir)/getopt.c \
-       $(srcdir)/getopt_long.c
+       $(srcdir)/getopt_long.c \
+       $(srcdir)/secure_getenv.c
 
 SHLIB_EXPDEPS =
 # Add -lm if dumping thread stats, for sqrt.
diff --git a/src/util/support/secure_getenv.c b/src/util/support/secure_getenv.c
new file mode 100644 (file)
index 0000000..6df0591
--- /dev/null
@@ -0,0 +1,111 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* util/support/secure_getenv.c - secure_getenv() portability support */
+/*
+ * Copyright (C) 2019 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This file contains the fallback implementation for secure_getenv(), which is
+ * currently only provided by glibc 2.17 and later.  The goal is to ignore the
+ * environment if this process is (or previously was) running at elevated
+ * privilege compared to the calling process.
+ *
+ * In this fallback version we compare the real and effective uid/gid, and also
+ * compare the saved uid/gid if possible.  These comparisons detect a setuid or
+ * setgid process which is still running with elevated privilege; if we can
+ * fetch the saved uid/gid, we also detect a process which has temporarily
+ * dropped privilege with seteuid() or setegid().  These comparisons do not
+ * detect the case where a setuid or setgid process has permanently dropped
+ * privilege before the library initializer ran; this is not ideal because such
+ * a process may possess a privileged resource or have privileged information
+ * in its address space.
+ *
+ * Heimdal also looks at the ELF aux vector in /proc/self/auxv to determine the
+ * starting uid/euid/gid/euid on Solaris/Illumos and NetBSD.  On FreeBSD this
+ * approach can determine the executable path to do a stat() check.  We do not
+ * go to this length due to the amount of code required.
+ *
+ * The BSDs and Solaris provide issetugid(), but the FreeBSD and NetBSD
+ * versions are not useful; they return true if a non-setuid/setgid executable
+ * is run by root and drops privilege, such as Apache httpd.  We do not want to
+ * ignore the process environment in this case.
+ *
+ * On some platforms a process may have elevated privilege via mechanisms other
+ * than setuid/setgid.  glibc's secure_getenv() should detect these cases on
+ * Linux; we do not detect them in this fallback version.
+ */
+
+#include "k5-platform.h"
+
+static int elevated_privilege = 0;
+
+MAKE_INIT_FUNCTION(k5_secure_getenv_init);
+
+int
+k5_secure_getenv_init()
+{
+    int saved_errno = errno;
+
+#ifdef HAVE_GETRESUID
+    {
+        uid_t r, e, s;
+        if (getresuid(&r, &e, &s) == 0) {
+            if (r != e || r != s)
+                elevated_privilege = 1;
+        }
+    }
+#else
+    if (getuid() != geteuid())
+        elevated_privilege = 1;
+#endif
+
+#ifdef HAVE_GETRESGID
+    {
+        gid_t r, e, s;
+        if (!elevated_privilege && getresgid(&r, &e, &s) == 0) {
+            if (r != e || r != s)
+                elevated_privilege = 1;
+        }
+    }
+#else
+    if (!elevated_privilege && getgid() != getegid())
+        elevated_privilege = 1;
+#endif
+
+    errno = saved_errno;
+    return 0;
+}
+
+char *
+k5_secure_getenv(const char *name)
+{
+    if (CALL_INIT_FUNCTION(k5_secure_getenv_init) != 0)
+        return NULL;
+    return elevated_privilege ? NULL : getenv(name);
+}