summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@thewalter.net>2013-07-17 11:57:02 +0200
committerStef Walter <stef@thewalter.net>2013-07-18 10:18:21 +0200
commitd9808323cb288540c93f0a74b825714cab3c96eb (patch)
tree2087a065eaeed2d9d41fa31881a39ec70daa9b0c
parente75007c5dfa15b92359b91c5c141680475c0192b (diff)
downloadp11-kit-d9808323cb288540c93f0a74b825714cab3c96eb.tar.gz
Don't load configs from user directory when setuid
When running as setuid() or setgid() don't access the user's home directory, or use $HOME environment variables. https://bugzilla.redhat.com/show_bug.cgi?id=985014
-rw-r--r--common/compat.c44
-rw-r--r--common/compat.h12
-rw-r--r--common/path.c5
-rw-r--r--common/tests/Makefile.am2
-rw-r--r--common/tests/frob-getauxval.c63
-rw-r--r--common/tests/test-compat.c114
-rw-r--r--configure.ac1
-rw-r--r--doc/manual/p11-kit-config.xml3
-rw-r--r--doc/manual/pkcs11.conf.xml3
-rw-r--r--p11-kit/conf.c5
-rw-r--r--p11-kit/tests/Makefile.am1
-rw-r--r--p11-kit/tests/conf-test.c118
-rw-r--r--p11-kit/tests/files/system-modules/one.module3
-rw-r--r--p11-kit/tests/files/user-modules/one.module3
-rw-r--r--p11-kit/tests/frob-setuid.c97
15 files changed, 472 insertions, 2 deletions
diff --git a/common/compat.c b/common/compat.c
index 589a8a4..ed523c6 100644
--- a/common/compat.c
+++ b/common/compat.c
@@ -753,3 +753,47 @@ mkdtemp (char *template)
}
#endif /* HAVE_MKDTEMP */
+
+#ifndef HAVE_GETAUXVAL
+
+unsigned long
+getauxval (unsigned long type)
+{
+ static unsigned long secure = 0UL;
+ static bool check_secure_initialized = false;
+
+ /*
+ * This is the only one our stand-in impl supports and is
+ * also the only type we define in compat.h header
+ */
+ assert (type == AT_SECURE);
+
+ if (!check_secure_initialized) {
+#if defined(HAVE_ISSETUGID)
+ secure = issetugid ();
+
+#elif defined(OS_UNIX)
+ uid_t ruid, euid, suid; /* Real, effective and saved user ID's */
+ gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */
+
+#ifdef HAVE_GETRESUID
+ if (getresuid (&ruid, &euid, &suid) != 0 ||
+ getresgid (&rgid, &egid, &sgid) != 0)
+#endif /* HAVE_GETRESUID */
+ {
+ suid = ruid = getuid ();
+ sgid = rgid = getgid ();
+ euid = geteuid ();
+ egid = getegid ();
+ }
+
+ secure = (ruid != euid || ruid != suid ||
+ rgid != egid || rgid != sgid);
+#endif /* OS_UNIX */
+ check_secure_initialized = true;
+ }
+
+ return secure;
+}
+
+#endif /* HAVE_GETAUXVAL */
diff --git a/common/compat.h b/common/compat.h
index ab647b8..a78da1f 100644
--- a/common/compat.h
+++ b/common/compat.h
@@ -295,4 +295,16 @@ time_t timegm (struct tm *tm);
#endif /* HAVE_TIMEGM */
+#ifdef HAVE_GETAUXVAL
+
+#include <sys/auxv.h>
+
+#else /* !HAVE_GETAUXVAL */
+
+unsigned long getauxval (unsigned long type);
+
+#define AT_SECURE 23
+
+#endif /* !HAVE_GETAUXVAL */
+
#endif /* __COMPAT_H__ */
diff --git a/common/path.c b/common/path.c
index f03b156..4b9863d 100644
--- a/common/path.c
+++ b/common/path.c
@@ -106,6 +106,11 @@ expand_homedir (const char *remainder)
{
const char *env;
+ if (getauxval (AT_SECURE)) {
+ errno = EPERM;
+ return NULL;
+ }
+
while (remainder[0] && is_path_component_or_null (remainder[0]))
remainder++;
if (remainder[0] == '\0')
diff --git a/common/tests/Makefile.am b/common/tests/Makefile.am
index 15dd35c..c230f6e 100644
--- a/common/tests/Makefile.am
+++ b/common/tests/Makefile.am
@@ -7,6 +7,7 @@ INCLUDES = \
-I$(top_srcdir) \
-I$(srcdir)/.. \
-I$(COMMON) \
+ -DBUILDDIR=\"$(abs_builddir)\" \
$(CUTEST_CFLAGS)
LDADD = \
@@ -25,6 +26,7 @@ CHECK_PROGS = \
$(NULL)
noinst_PROGRAMS = \
+ frob-getauxval \
$(CHECK_PROGS)
if WITH_ASN1
diff --git a/common/tests/frob-getauxval.c b/common/tests/frob-getauxval.c
new file mode 100644
index 0000000..54ebea0
--- /dev/null
+++ b/common/tests/frob-getauxval.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013 Red Hat Inc.
+ *
+ * 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.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ * Author: Stef Walter <stefw@gnome.org>
+ */
+
+#include "config.h"
+#include "compat.h"
+
+#include <libtasn1.h>
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main (int argc,
+ char *argv[])
+{
+ unsigned long type = 0;
+ unsigned long ret;
+
+ if (argc == 2)
+ type = atoi (argv[1]);
+
+ if (type == 0) {
+ fprintf (stderr, "usage: frob-getauxval 23");
+ abort ();
+ }
+
+ ret = getauxval (type);
+ printf ("getauxval(%lu) == %lu\n", type, ret);
+ return (int)ret;
+}
diff --git a/common/tests/test-compat.c b/common/tests/test-compat.c
index 066e723..390502f 100644
--- a/common/tests/test-compat.c
+++ b/common/tests/test-compat.c
@@ -41,6 +41,12 @@
#include "compat.h"
+#ifdef OS_UNIX
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
static void
test_strndup (CuTest *tc)
{
@@ -56,6 +62,111 @@ test_strndup (CuTest *tc)
free (res);
}
+#ifdef OS_UNIX
+
+static void
+copy_file (CuTest *tc,
+ const char *input,
+ int fd)
+{
+ p11_mmap *mmap;
+ const char *data;
+ ssize_t written;
+ size_t size;
+
+ mmap = p11_mmap_open (input, (void **)&data, &size);
+ CuAssertPtrNotNull (tc, mmap);
+
+ while (size > 0) {
+ written = write (fd, data, size);
+ CuAssertTrue (tc, written >= 0);
+
+ data += written;
+ size -= written;
+ }
+
+ p11_mmap_close (mmap);
+}
+
+static int
+run_process_with_arg (CuTest *tc,
+ char *path,
+ char *arg)
+{
+ char *argv[] = { path, arg, NULL };
+ pid_t child;
+ int status;
+
+ child = fork ();
+ CuAssertTrue (tc, child >= 0);
+
+ /* In the child process? */
+ if (child == 0) {
+ close (1); /* stdout */
+ execve (path, argv, NULL);
+ abort ();
+ }
+
+ if (waitpid (child, &status, 0) < 0) {
+ CuFail (tc, "not reached");
+ }
+
+ CuAssertTrue (tc, !WIFSIGNALED (status));
+ CuAssertTrue (tc, WIFEXITED (status));
+
+ return WEXITSTATUS (status);
+}
+
+static void
+test_getauxval (CuTest *tc)
+{
+ gid_t groups[128];
+ char *path;
+ gid_t group = 0;
+ int ret;
+ int fd;
+ int i;
+
+ /* 23 is AT_SECURE */
+ ret = run_process_with_arg (tc, BUILDDIR "/frob-getauxval", "23");
+ CuAssertIntEquals (tc, ret, 0);
+
+ ret = getgroups (128, groups);
+ CuAssertTrue (tc, ret >= 0);
+ for (i = 0; i < ret; ++i) {
+ if (groups[i] != getgid ()) {
+ group = groups[i];
+ break;
+ }
+ }
+ if (i == ret) {
+ fprintf (stderr, "no suitable group, skipping test");
+ return;
+ }
+
+ path = strdup ("/tmp/frob-getauxval.XXXXXX");
+ CuAssertPtrNotNull (tc, path);
+
+ fd = mkstemp (path);
+ CuAssertTrue (tc, fd >= 0);
+ copy_file (tc, BUILDDIR "/frob-getauxval", fd);
+ if (fchown (fd, getuid (), group) < 0)
+ CuFail (tc, "fchown failed");
+ if (fchmod (fd, 02750) < 0)
+ CuFail (tc, "fchmod failed");
+ if (close (fd) < 0)
+ CuFail (tc, "close failed");
+
+ ret = run_process_with_arg (tc, path, "23");
+ CuAssertTrue (tc, ret != 0);
+
+ if (unlink (path) < 0)
+ CuFail (tc, "unlink failed");
+ free (path);
+}
+
+#endif /* OS_UNIX */
+
int
main (void)
{
@@ -64,6 +175,9 @@ main (void)
int ret;
SUITE_ADD_TEST (suite, test_strndup);
+#ifdef OS_UNIX
+ SUITE_ADD_TEST (suite, test_getauxval);
+#endif
CuSuiteRun (suite);
CuSuiteSummary (suite, output);
diff --git a/configure.ac b/configure.ac
index 33ec808..b139a40 100644
--- a/configure.ac
+++ b/configure.ac
@@ -78,6 +78,7 @@ if test "$os_unix" = "yes"; then
# These are thngs we can work around
AC_CHECK_MEMBERS([struct dirent.d_type],,,[#include <dirent.h>])
AC_CHECK_FUNCS([getprogname getexecname basename mkstemp mkdtemp])
+ AC_CHECK_FUNCS([getauxval issetugid getresuid])
AC_CHECK_FUNCS([strnstr memdup strndup])
AC_CHECK_FUNCS([asprintf vasprintf vsnprintf])
AC_CHECK_FUNCS([timegm])
diff --git a/doc/manual/p11-kit-config.xml b/doc/manual/p11-kit-config.xml
index 6d069dd..1df55b1 100644
--- a/doc/manual/p11-kit-config.xml
+++ b/doc/manual/p11-kit-config.xml
@@ -87,5 +87,8 @@ critical: yes
<para><link linkend="pkcs11.conf">See the manual page</link> for more details
on the format and available options.</para>
+
+ <para>Note that user configuration files are not loaded from the home
+ directory if running inside a setuid or setgid program.</para>
</section>
</chapter>
diff --git a/doc/manual/pkcs11.conf.xml b/doc/manual/pkcs11.conf.xml
index 1814377..4ecfa82 100644
--- a/doc/manual/pkcs11.conf.xml
+++ b/doc/manual/pkcs11.conf.xml
@@ -198,6 +198,9 @@ x-custom : text
file per module. In addition the <literal>~/.pkcs11/modules</literal> directory
can be used for modules installed by the user.</para>
+ <para>Note that user configuration files are not loaded from the home
+ directory if running inside a setuid or setgid program.</para>
+
<para>The default system config file and module directory can be changed
when building p11-kit. Always
<link linkend="devel-paths">lookup these paths</link> using
diff --git a/p11-kit/conf.c b/p11-kit/conf.c
index 7f3bc68..ed871ec 100644
--- a/p11-kit/conf.c
+++ b/p11-kit/conf.c
@@ -227,6 +227,11 @@ _p11_conf_load_globals (const char *system_conf, const char *user_conf,
goto finished;
}
+ if (mode != CONF_USER_NONE && getauxval (AT_SECURE)) {
+ p11_debug ("skipping user config in setuid or setgid program");
+ mode = CONF_USER_NONE;
+ }
+
if (mode != CONF_USER_NONE) {
path = p11_path_expand (user_conf);
if (!path) {
diff --git a/p11-kit/tests/Makefile.am b/p11-kit/tests/Makefile.am
index 0c53c55..ea71032 100644
--- a/p11-kit/tests/Makefile.am
+++ b/p11-kit/tests/Makefile.am
@@ -29,6 +29,7 @@ CHECK_PROGS = \
noinst_PROGRAMS = \
print-messages \
+ frob-setuid \
$(CHECK_PROGS)
TESTS = $(CHECK_PROGS)
diff --git a/p11-kit/tests/conf-test.c b/p11-kit/tests/conf-test.c
index d259cf8..0d4e78a 100644
--- a/p11-kit/tests/conf-test.c
+++ b/p11-kit/tests/conf-test.c
@@ -46,6 +46,12 @@
#include "p11-kit.h"
#include "private.h"
+#ifdef OS_UNIX
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
static void
test_parse_conf_1 (CuTest *tc)
{
@@ -391,6 +397,115 @@ test_parse_boolean (CuTest *tc)
CuAssertIntEquals (tc, true, _p11_conf_parse_boolean ("!!!", true));
}
+#ifdef OS_UNIX
+
+/*
+ * TODO: In git master and the post 0.19.x series, we should integrate this into
+ * our new testing code, rather than duplicating here and in test-compat.c
+ */
+
+static void
+copy_file (CuTest *tc,
+ const char *input,
+ int fd)
+{
+ p11_mmap *mmap;
+ const char *data;
+ ssize_t written;
+ size_t size;
+
+ mmap = p11_mmap_open (input, (void **)&data, &size);
+ CuAssertPtrNotNull (tc, mmap);
+
+ while (size > 0) {
+ written = write (fd, data, size);
+ CuAssertTrue (tc, written >= 0);
+
+ data += written;
+ size -= written;
+ }
+
+ p11_mmap_close (mmap);
+}
+
+static int
+run_process (CuTest *tc,
+ char *path)
+{
+ char *argv[] = { path, NULL };
+ pid_t child;
+ int status;
+
+ child = fork ();
+ CuAssertTrue (tc, child >= 0);
+
+ /* In the child process? */
+ if (child == 0) {
+ close (1); /* stdout */
+ execve (path, argv, NULL);
+ abort ();
+ }
+
+ if (waitpid (child, &status, 0) < 0)
+ CuFail (tc, "not reached");
+
+ CuAssertTrue (tc, !WIFSIGNALED (status));
+ CuAssertTrue (tc, WIFEXITED (status));
+ return WEXITSTATUS (status);
+}
+
+static void
+test_setuid (CuTest *tc)
+{
+ gid_t groups[128];
+ char *path;
+ gid_t group = 0;
+ int ret;
+ int fd;
+ int i;
+
+ /* This is the 'number' setting set in one.module user configuration. */
+ ret = run_process (tc, BUILDDIR "/frob-setuid");
+ CuAssertIntEquals (tc, ret, 33);
+
+ ret = getgroups (128, groups);
+ CuAssertTrue (tc, ret >= 0);
+ for (i = 0; i < ret; ++i) {
+ if (groups[i] != getgid ()) {
+ group = groups[i];
+ break;
+ }
+ }
+ if (i == ret) {
+ fprintf (stderr, "no suitable group, skipping test");
+ return;
+ }
+
+ path = strdup ("/tmp/frob-setuid.XXXXXX");
+ CuAssertPtrNotNull (tc, path);
+
+ fd = mkstemp (path);
+ CuAssertTrue (tc, fd >= 0);
+ copy_file (tc, BUILDDIR "/frob-setuid", fd);
+ if (fchown (fd, getuid (), group) < 0)
+ CuFail (tc, "fchown failed");
+ if (fchmod (fd, 02750) < 0)
+ CuFail (tc, "fchmod failed");
+ if (close (fd) < 0)
+ CuFail (tc, "close failed");
+
+ ret = run_process (tc, path);
+
+ /* This is the 'number' setting set in one.module system configuration. */
+ CuAssertIntEquals (tc, ret, 18);
+
+ if (unlink (path) < 0)
+ CuFail (tc, "unlink failed");
+ free (path);
+}
+
+#endif /* OS_UNIX */
+
int
main (void)
{
@@ -416,6 +531,9 @@ main (void)
SUITE_ADD_TEST (suite, test_load_modules_user_only);
SUITE_ADD_TEST (suite, test_load_modules_user_none);
SUITE_ADD_TEST (suite, test_parse_boolean);
+#ifdef OS_UNIX
+ SUITE_ADD_TEST (suite, test_setuid);
+#endif
CuSuiteRun (suite);
CuSuiteSummary (suite, output);
diff --git a/p11-kit/tests/files/system-modules/one.module b/p11-kit/tests/files/system-modules/one.module
index 3620869..9efc320 100644
--- a/p11-kit/tests/files/system-modules/one.module
+++ b/p11-kit/tests/files/system-modules/one.module
@@ -1,3 +1,4 @@
module: mock-one.so
-setting: system1 \ No newline at end of file
+setting: system1
+number: 18 \ No newline at end of file
diff --git a/p11-kit/tests/files/user-modules/one.module b/p11-kit/tests/files/user-modules/one.module
index c371e4a..093493b 100644
--- a/p11-kit/tests/files/user-modules/one.module
+++ b/p11-kit/tests/files/user-modules/one.module
@@ -1,2 +1,3 @@
-setting: user1 \ No newline at end of file
+setting: user1
+number: 33 \ No newline at end of file
diff --git a/p11-kit/tests/frob-setuid.c b/p11-kit/tests/frob-setuid.c
new file mode 100644
index 0000000..665ec22
--- /dev/null
+++ b/p11-kit/tests/frob-setuid.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2012 Red Hat Inc
+ *
+ * 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.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * 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 OWNER 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.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "compat.h"
+#include "p11-kit.h"
+
+int
+main (void)
+{
+ CK_FUNCTION_LIST **modules;
+ CK_FUNCTION_LIST *module;
+ char *field;
+ char *name;
+ CK_RV rv;
+ int ret;
+ int i;
+
+ /*
+ * Use 'chmod ug+s frob-setuid' to change this program
+ * and test the output with/without setuid or setgid.
+ */
+
+ putenv ("P11_KIT_STRICT=1");
+
+ rv = p11_kit_initialize_registered ();
+ assert (rv == CKR_OK);
+
+ /* This is a system configured module */
+ module = p11_kit_registered_name_to_module ("one");
+ assert (module != NULL);
+
+ field = p11_kit_registered_option (module, "setting");
+ printf ("'setting' on module 'one': %s\n", field ? field : "(null)");
+
+ assert (field != NULL);
+ if (getauxval (AT_SECURE))
+ assert (strcmp (field, "system1") == 0);
+ else
+ assert (strcmp (field, "user1") == 0);
+
+ free (field);
+
+ modules = p11_kit_registered_modules ();
+ for (i = 0; modules[i] != NULL; i++) {
+ name = p11_kit_registered_module_to_name (modules[i]);
+ printf ("%s\n", name);
+ free (name);
+ }
+ free (modules);
+
+ field = p11_kit_registered_option (module, "number");
+ printf ("'number' on module 'one': %s\n", field ? field : "(null)");
+
+ ret = atoi (field ? field : "0");
+ assert (ret != 0);
+
+ p11_kit_finalize_registered ();
+ return ret;
+}