]> granicus.if.org Git - p11-kit/commitdiff
Don't load configs from user directory when setuid
authorStef Walter <stef@thewalter.net>
Wed, 17 Jul 2013 09:57:02 +0000 (11:57 +0200)
committerStef Walter <stef@thewalter.net>
Thu, 18 Jul 2013 06:45:57 +0000 (08:45 +0200)
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

17 files changed:
common/compat.c
common/compat.h
common/path.c
common/test.c
common/test.h
common/tests/Makefile.am
common/tests/frob-getauxval.c [new file with mode: 0644]
common/tests/test-compat.c
configure.ac
doc/manual/p11-kit-config.xml
doc/manual/pkcs11.conf.xml
p11-kit/conf.c
p11-kit/tests/Makefile.am
p11-kit/tests/files/system-modules/one.module
p11-kit/tests/files/user-modules/one.module
p11-kit/tests/frob-setuid.c [new file with mode: 0644]
p11-kit/tests/test-conf.c

index 5efc93214aa295c0c7394fa0ff4b0c43cc8b67ab..3b1361c9bf72181a258c3e0d36a10b6b97d12a3e 100644 (file)
@@ -759,3 +759,51 @@ 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___LIBC_ENABLE_SECURE)
+               extern int __libc_enable_secure;
+               secure = __libc_enable_secure;
+
+#elif 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 */
index 20f9a8109c0a8a19e97dd42a828b0bfa23e289a0..1cedc3515baadc43e3c6cf3a688c177f2f2e02c4 100644 (file)
@@ -298,4 +298,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__ */
index 0ff143175cbb1b7f3ac6a754182927ad2beae124..d807301b809efe877f266ce0bf299d3b281d429b 100644 (file)
@@ -99,6 +99,11 @@ expand_homedir (const char *remainder)
        if (remainder[0] == '\0')
                remainder = NULL;
 
+       if (getauxval (AT_SECURE)) {
+               errno = EPERM;
+               return NULL;
+       }
+
        env = getenv ("HOME");
        if (env && env[0]) {
                return p11_path_build (env, remainder, NULL);
index 0f5c4516b3b0ec13217434e633ec63a86a0e3397..eb0264553ed3d439938f64b62f2b6dae96780922 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 
+#ifdef OS_UNIX
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+
 enum {
        FIXTURE,
        TEST,
@@ -331,3 +337,96 @@ p11_test_directory (const char *prefix)
        free (templ);
        return directory;
 }
+
+#ifdef OS_UNIX
+
+static void
+copy_file (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);
+       assert (mmap != NULL);
+
+       while (size > 0) {
+               written = write (fd, data, size);
+               assert (written >= 0);
+
+               data += written;
+               size -= written;
+       }
+
+       p11_mmap_close (mmap);
+}
+
+char *
+p11_test_copy_setgid (const char *input)
+{
+       gid_t groups[128];
+               char *path;
+               gid_t group = 0;
+               int ret;
+               int fd;
+               int i;
+
+       ret = getgroups (128, groups);
+       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 NULL;
+       }
+
+       path = strdup ("/tmp/test-setgid.XXXXXX");
+       assert (path != NULL);
+
+       fd = mkstemp (path);
+       assert (fd >= 0);
+
+       copy_file (input, fd);
+       if (fchown (fd, getuid (), group) < 0)
+               assert_not_reached ();
+       if (fchmod (fd, 02750) < 0)
+               assert_not_reached ();
+       if (close (fd) < 0)
+               assert_not_reached ();
+
+       return path;
+}
+
+int
+p11_test_run_child (const char **argv,
+                    bool quiet_out)
+{
+       pid_t child;
+       int status;
+
+       child = fork ();
+       assert (child >= 0);
+
+       /* In the child process? */
+       if (child == 0) {
+               if (quiet_out)
+                       close (1); /* stdout */
+               execve (argv[0], (char **)argv, NULL);
+               assert_not_reached ();
+       }
+
+       if (waitpid (child, &status, 0) < 0)
+               assert_not_reached ();
+
+       assert (!WIFSIGNALED (status));
+       assert (WIFEXITED (status));
+
+       return WEXITSTATUS (status);
+}
+
+#endif /* OS_UNIX */
index 735459517dd09794a5959a4a6da9034f4ea4efce..c9f519a829f223533704e825fc3db2e342095fc3 100644 (file)
@@ -130,4 +130,13 @@ int         p11_test_run            (int argc,
 
 char *      p11_test_directory      (const char *prefix);
 
+#ifdef OS_UNIX
+
+char *      p11_test_copy_setgid    (const char *path);
+
+int         p11_test_run_child      (const char **argv,
+                                     bool quiet_out);
+
+#endif
+
 #endif /* P11_TEST_H_ */
index 637399b5a87635ebfd8e2100336e76b3a086facd..b1a42bd20001ebd5203cb4f095a89e052afa83e0 100644 (file)
@@ -7,7 +7,9 @@ AM_CPPFLAGS = \
        -I$(top_srcdir) \
        -I$(srcdir)/.. \
        -I$(COMMON) \
-       $(TEST_CFLAGS)
+       -DBUILDDIR=\"$(abs_builddir)\" \
+       $(TEST_CFLAGS) \
+       $(CUTEST_CFLAGS)
 
 LDADD = \
        $(NULL)
@@ -26,6 +28,7 @@ CHECK_PROGS = \
        $(NULL)
 
 noinst_PROGRAMS = \
+       frob-getauxval \
        $(CHECK_PROGS)
 
 TESTS = $(CHECK_PROGS)
diff --git a/common/tests/frob-getauxval.c b/common/tests/frob-getauxval.c
new file mode 100644 (file)
index 0000000..54ebea0
--- /dev/null
@@ -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;
+}
index f1960ceb4d964799550507cb6eb4561dc8c09917..a54123531829469992ae5602f1ed7a3db07eafe9 100644 (file)
@@ -35,6 +35,7 @@
 #include "config.h"
 #include "test.h"
 
+#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
@@ -56,10 +57,39 @@ test_strndup (void)
        free (res);
 }
 
+#ifdef OS_UNIX
+
+static void
+test_getauxval (void)
+{
+       /* 23 is AT_SECURE */
+       const char *args[] = { BUILDDIR "/frob-getauxval", "23", NULL };
+       char *path;
+       int ret;
+
+       ret = p11_test_run_child (args, true);
+       assert_num_eq (ret, 0);
+
+       path = p11_test_copy_setgid (args[0]);
+       if (path == NULL)
+               return;
+
+       args[0] = path;
+       ret = p11_test_run_child (args, true);
+       assert_num_cmp (ret, !=, 0);
+
+       if (unlink (path) < 0)
+               assert_fail ("unlink failed", strerror (errno));
+       free (path);
+}
+
+#endif /* OS_UNIX */
+
 int
 main (int argc,
       char *argv[])
 {
        p11_test (test_strndup, "/test/strndup");
+       p11_test (test_getauxval, "/test/getauxval");
        return p11_test_run (argc, argv);
 }
index b0bbbd50ae12c090b8c34112db884881d23b5890..2f92b8c1990a1731d1984b0f1740e00cb1611ce0 100644 (file)
@@ -79,6 +79,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])
@@ -100,6 +101,8 @@ if test "$os_unix" = "yes"; then
        AC_CHECK_DECLS([__progname])
        AC_LINK_IFELSE([AC_LANG_SOURCE([extern char *__progname; void main() { }])],
                [AC_DEFINE(HAVE___PROGNAME, [1], [Whether __progname available])])
+       AC_LINK_IFELSE([AC_LANG_SOURCE([extern int __libc_enable_secure; void main() { }])],
+               [AC_DEFINE(HAVE___LIBC_ENABLE_SECURE, [1], [Whether __libc_enable_secure available])])
 fi
 
 AC_CHECK_LIB(intl, dgettext)
index 6d069ddff2ad568a21660c330a17e09e38f348d4..1df55b1ee335931c372bed6524eb93c165721e91 100644 (file)
@@ -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>
index 1ff25620fcbd21066893b6a900abfb0a2d6c5bc1..cda02ee185513d012c557aae0e3fb5272114625f 100644 (file)
@@ -241,6 +241,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
index e699e66ab7c6329c418ef467dc8b14b716dc80ab..d29d9ecec15791494fbe7319ecbc410f49adfddc 100644 (file)
@@ -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) {
index 6963850f432a42878cb35665d31e8691852771a5..16ba28013352b02998c9f86817d938df4deaa96e 100644 (file)
@@ -40,6 +40,7 @@ endif
 
 noinst_PROGRAMS = \
        print-messages \
+       frob-setuid \
        $(CHECK_PROGS)
 
 TESTS = $(CHECK_PROGS)
index 15cb7f22d073209a4c3d5f38daf01d69eb650b69..5f49a8fa899e93321117cab85b078f24b81aae4f 100644 (file)
@@ -1,4 +1,5 @@
 
 module: mock-one.so
 setting: system1
-trust-policy: yes
\ No newline at end of file
+trust-policy: yes
+number: 18
index 6f1a2e8dfe642ec1c37b5d8e2ddced5fb698ab2e..5197daf25bc71af6b473695e497b798cf6c291aa 100644 (file)
@@ -1,3 +1,4 @@
 
 setting: user1
-managed: yes
\ No newline at end of file
+managed: yes
+number: 33
diff --git a/p11-kit/tests/frob-setuid.c b/p11-kit/tests/frob-setuid.c
new file mode 100644 (file)
index 0000000..e546ece
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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;
+       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");
+
+       modules = p11_kit_modules_load_and_initialize (0);
+       assert (modules != NULL);
+
+       /* This is a system configured module */
+       module = p11_kit_module_for_name (modules, "one");
+       assert (module != NULL);
+
+       field = p11_kit_config_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);
+
+       for (i = 0; modules[i] != NULL; i++) {
+               name = p11_kit_module_get_name (modules[i]);
+               printf ("%s\n", name);
+               free (name);
+       }
+
+       field = p11_kit_config_option (module, "number");
+       printf ("'number' on module 'one': %s\n", field ? field : "(null)");
+
+       ret = atoi (field ? field : "0");
+       assert (ret != 0);
+       free (field);
+
+       p11_kit_modules_finalize_and_release (modules);
+       return ret;
+}
index c214bac5b82a73eaf39d565ecec9d8b73186587e..3a94c125badd7eb87dc077ffc279ffb0930105d5 100644 (file)
 #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 (void)
 {
@@ -391,6 +397,36 @@ test_parse_boolean (void)
        assert_num_eq (true, _p11_conf_parse_boolean ("!!!", true));
 }
 
+#ifdef OS_UNIX
+
+static void
+test_setuid (void)
+{
+       const char *args[] = { BUILDDIR "/frob-setuid", NULL, };
+       char *path;
+       int ret;
+
+       /* This is the 'number' setting set in one.module user configuration. */
+       ret = p11_test_run_child (args, true);
+       assert_num_eq (ret, 33);
+
+       path = p11_test_copy_setgid (args[0]);
+       if (path == NULL)
+               return;
+
+       args[0] = path;
+
+       /* This is the 'number' setting set in one.module system configuration. */
+       ret = p11_test_run_child (args, true);
+       assert_num_eq (ret, 18);
+
+       if (unlink (path) < 0)
+               assert_fail ("unlink failed", strerror (errno));
+       free (path);
+}
+
+#endif /* OS_UNIX */
+
 int
 main (int argc,
       char *argv[])
@@ -410,5 +446,8 @@ main (int argc,
        p11_test (test_load_modules_user_only, "/conf/test_load_modules_user_only");
        p11_test (test_load_modules_user_none, "/conf/test_load_modules_user_none");
        p11_test (test_parse_boolean, "/conf/test_parse_boolean");
+#ifdef OS_UNIX
+       p11_test (test_setuid, "/conf/setuid");
+#endif
        return p11_test_run (argc, argv);
 }