commit:     a743806ea4868371cf182f783fdcfbf1b1f98202
Author:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
AuthorDate: Fri Aug 14 02:58:37 2015 +0000
Commit:     Mike Frysinger <vapier <AT> gentoo <DOT> org>
CommitDate: Thu Aug 20 14:38:35 2015 +0000
URL:        https://gitweb.gentoo.org/proj/pax-utils.git/commit/?id=a743806e

security: leverage namespaces to restrict the runtime a bit

In practice this isn't terribly useful as people aren't attacking these
tools, but might as well be paranoid.

It'd be nice to use mount & net namespaces too, but they're way too slow.

 Makefile    |  2 +-
 Makefile.am |  3 +++
 dumpelf.c   |  1 +
 paxinc.h    |  1 +
 porting.h   |  1 +
 pspax.c     |  4 +++
 scanelf.c   |  5 ++++
 scanmacho.c |  1 +
 security.c  | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 security.h  | 29 ++++++++++++++++++++
 10 files changed, 135 insertions(+), 1 deletion(-)

diff --git a/Makefile b/Makefile
index ba3b6a2..ac5e9cc 100644
--- a/Makefile
+++ b/Makefile
@@ -61,7 +61,7 @@ ELF_TARGETS  = scanelf dumpelf $(shell echo | $(CC) -dM -E - 
| grep -q __svr4__
 ELF_OBJS     = paxelf.o
 MACH_TARGETS = scanmacho
 MACH_OBJS    = paxmacho.o
-COMMON_OBJS  = paxinc.o xfuncs.o
+COMMON_OBJS  = paxinc.o security.o xfuncs.o
 TARGETS      = $(ELF_TARGETS) $(MACH_TARGETS)
 SCRIPTS_SH   = lddtree symtree
 SCRIPTS_PY   = lddtree

diff --git a/Makefile.am b/Makefile.am
index 5db3f75..e42dce4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -12,6 +12,7 @@ libpaxutils_la_SOURCES = \
        paxelf.c \
        paxinc.c \
        paxmacho.c \
+       security.c \
        xfuncs.c
 LDADD = libpaxutils.la $(top_builddir)/autotools/gnulib/libgnu.a
 
@@ -84,6 +85,8 @@ EXTRA_DIST += \
        pspax.c \
        scanelf.c \
        scanmacho.c \
+       security.c \
+       security.h \
        symtree.sh \
        tests/Makefile \
        tests/lddtree/Makefile \

diff --git a/dumpelf.c b/dumpelf.c
index 3035b24..e9b1771 100644
--- a/dumpelf.c
+++ b/dumpelf.c
@@ -384,6 +384,7 @@ static void parseargs(int argc, char *argv[])
 
 int main(int argc, char *argv[])
 {
+       security_init(false);
        if (argc < 2)
                usage(EXIT_FAILURE);
        parseargs(argc, argv);

diff --git a/paxinc.h b/paxinc.h
index 0a8e08a..a8d6d9b 100644
--- a/paxinc.h
+++ b/paxinc.h
@@ -13,6 +13,7 @@
 
 #include "porting.h"
 #include "xfuncs.h"
+#include "security.h"
 
 #ifndef VERSION
 # define VERSION "git"

diff --git a/porting.h b/porting.h
index 1f989d2..9dea528 100644
--- a/porting.h
+++ b/porting.h
@@ -29,6 +29,7 @@
 #include <limits.h>
 #include <pwd.h>
 #include <regex.h>
+#include <sched.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>

diff --git a/pspax.c b/pspax.c
index e27b7eb..c64472c 100644
--- a/pspax.c
+++ b/pspax.c
@@ -535,6 +535,10 @@ int main(int argc, char *argv[])
 {
        char *name = NULL;
 
+       /* We unshare pidns but don't actually enter it.  That means
+        * we still get to scan /proc, but just not fork children.  */
+       security_init(false);
+
        color_init(false);
        parseargs(argc, argv);
 

diff --git a/scanelf.c b/scanelf.c
index 99192b2..7e3b077 100644
--- a/scanelf.c
+++ b/scanelf.c
@@ -2472,6 +2472,10 @@ static int parseargs(int argc, char *argv[])
        }
        if (be_verbose > 2) printf("Format: %s\n", out_format);
 
+       /* Now lock down the pidns since we know whether we'll be forking. */
+       if (!show_textrels || !be_verbose)
+               security_init_pid();
+
        /* now lets actually do the scanning */
        if (load_cache_config)
                load_ld_cache_config(__PAX_UTILS_DEFAULT_LD_CACHE_CONFIG);
@@ -2570,6 +2574,7 @@ static void cleanup(void)
 int main(int argc, char *argv[])
 {
        int ret;
+       security_init(true);
        if (argc < 2)
                usage(EXIT_FAILURE);
        parseenv();

diff --git a/scanmacho.c b/scanmacho.c
index ee713f9..5a0afd5 100644
--- a/scanmacho.c
+++ b/scanmacho.c
@@ -764,6 +764,7 @@ static int parseargs(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
        int ret;
+       security_init(false);
        if (argc < 2)
                usage(EXIT_FAILURE);
        color_init(false);

diff --git a/security.c b/security.c
new file mode 100644
index 0000000..9b48a9a
--- /dev/null
+++ b/security.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2015 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ *
+ * Copyright 2015 Mike Frysinger  - <[email protected]>
+ */
+
+#include "paxinc.h"
+
+#ifdef __linux__
+
+#ifdef __SANITIZE_ADDRESS__
+/* ASAN does some weird stuff. */
+# define ALLOW_PIDNS 0
+#else
+# define ALLOW_PIDNS 1
+#endif
+
+static int ns_unshare(int flags)
+{
+       int flag, ret = 0;
+
+       /* Try to oneshot it.  Maybe we'll get lucky! */
+       if (unshare(flags) == 0)
+               return flags;
+       /* No access at all, so don't waste time below. */
+       else if (errno == EPERM)
+               return ret;
+
+       /*
+        * We have to run these one permission at a time because if any are
+        * not supported (too old a kernel, or it's disabled), then all of
+        * them will be rejected and we won't know which one is a problem.
+        */
+
+       /* First the ones that work against the current process.  */
+       flag = 1;
+       while (flags) {
+               if (flags & flag) {
+                       if (unshare(flag) == 0)
+                               ret |= flag;
+                       flags &= ~flag;
+               }
+               flag <<= 1;
+       }
+
+       return ret;
+}
+
+void security_init_pid(void)
+{
+       int flags;
+
+       if (!ALLOW_PIDNS)
+               return;
+
+       flags = ns_unshare(CLONE_NEWPID);
+       if (USE_SLOW_SECURITY) {
+               if (flags & CLONE_NEWPID)
+                       if (vfork() == 0)
+                               _exit(0);
+       }
+}
+
+void security_init(bool allow_forking)
+{
+       int flags;
+
+       if (!ALLOW_PIDNS)
+               allow_forking = true;
+
+       /* None of the pax tools need access to these features. */
+       flags = CLONE_NEWIPC | CLONE_NEWUTS;
+       /* Would be nice to leverage mount/net ns, but they're just way too 
slow. */
+       if (USE_SLOW_SECURITY)
+               flags |= CLONE_NEWNET | CLONE_NEWNS;
+       if (!allow_forking)
+               flags |= CLONE_NEWPID;
+       flags = ns_unshare(flags);
+
+       if (USE_SLOW_SECURITY) {
+               /* We spawn one child and kill it so the kernel will fail in 
the future. */
+               if (flags & CLONE_NEWPID)
+                       if (vfork() == 0)
+                               _exit(0);
+       }
+}
+
+#endif

diff --git a/security.h b/security.h
new file mode 100644
index 0000000..c93ec3e
--- /dev/null
+++ b/security.h
@@ -0,0 +1,29 @@
+/* Various security related features.
+ *
+ * Copyright 2015 Gentoo Foundation
+ * Distributed under the terms of the GNU General Public License v2
+ *
+ * Copyright 2015 Mike Frysinger  - <[email protected]>
+ */
+
+#ifndef _PAX_SECURITY_H
+#define _PAX_SECURITY_H
+
+/* Whether to enable features that significantly impact speed. */
+#ifdef SLOW_SECURITY
+# define USE_SLOW_SECURITY 1
+#else
+# define USE_SLOW_SECURITY 0
+#endif
+
+#ifdef __linux__
+/* Lock down the runtime; allow_forking controls whether to use a pidns. */
+void security_init(bool allow_forking);
+/* Disable forking; usable only when allow_forking above was true. */
+void security_init_pid(void);
+#else
+static inline void security_init(bool allow_forking) {}
+static inline void security_init_pid(void) {}
+#endif
+
+#endif

Reply via email to