diff options
author | Stef Walter <stef@thewalter.net> | 2013-07-17 11:57:02 +0200 |
---|---|---|
committer | Stef Walter <stef@thewalter.net> | 2013-07-18 10:18:21 +0200 |
commit | d9808323cb288540c93f0a74b825714cab3c96eb (patch) | |
tree | 2087a065eaeed2d9d41fa31881a39ec70daa9b0c | |
parent | e75007c5dfa15b92359b91c5c141680475c0192b (diff) | |
download | p11-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.c | 44 | ||||
-rw-r--r-- | common/compat.h | 12 | ||||
-rw-r--r-- | common/path.c | 5 | ||||
-rw-r--r-- | common/tests/Makefile.am | 2 | ||||
-rw-r--r-- | common/tests/frob-getauxval.c | 63 | ||||
-rw-r--r-- | common/tests/test-compat.c | 114 | ||||
-rw-r--r-- | configure.ac | 1 | ||||
-rw-r--r-- | doc/manual/p11-kit-config.xml | 3 | ||||
-rw-r--r-- | doc/manual/pkcs11.conf.xml | 3 | ||||
-rw-r--r-- | p11-kit/conf.c | 5 | ||||
-rw-r--r-- | p11-kit/tests/Makefile.am | 1 | ||||
-rw-r--r-- | p11-kit/tests/conf-test.c | 118 | ||||
-rw-r--r-- | p11-kit/tests/files/system-modules/one.module | 3 | ||||
-rw-r--r-- | p11-kit/tests/files/user-modules/one.module | 3 | ||||
-rw-r--r-- | p11-kit/tests/frob-setuid.c | 97 |
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; +} |