security hardening: ensure suid programs have valid stdin/out/err
authorRich Felker <dalias@aerifal.cx>
Tue, 23 Aug 2011 13:37:39 +0000 (09:37 -0400)
committerRich Felker <dalias@aerifal.cx>
Tue, 23 Aug 2011 13:37:39 +0000 (09:37 -0400)
this behavior (opening fds 0-2 for a suid program) is explicitly
allowed (but not required) by POSIX to protect badly-written suid
programs from clobbering files they later open.

this commit does add some cost in startup code, but the availability
of auxv and the security flag will be useful elsewhere in the future.
in particular auxv is needed for static-linked vdso support, which is
still waiting to be committed (sorry nik!)

arch/i386/atomic.h
arch/x86_64/atomic.h
src/env/__environ.c
src/env/__init_security.c [new file with mode: 0644]
src/env/__libc_start_main.c
src/internal/libc.h

index 66059af..77b0b3b 100644 (file)
@@ -119,5 +119,10 @@ static inline void a_spin()
        __asm__ __volatile__( "pause" : : : "memory" );
 }
 
+static inline void a_crash()
+{
+       __asm__ __volatile__( "hlt" : : : "memory" );
+}
+
 
 #endif
index 3235db1..0d3da6f 100644 (file)
@@ -118,5 +118,10 @@ static inline void a_spin()
        __asm__ __volatile__( "pause" : : : "memory" );
 }
 
+static inline void a_crash()
+{
+       __asm__ __volatile__( "hlt" : : : "memory" );
+}
+
 
 #endif
index d7bd5e5..0a2786f 100644 (file)
@@ -1,7 +1,6 @@
 #include "libc.h"
 
 #undef environ
-char **___environ = 0;
-weak_alias(___environ, __environ);
-weak_alias(___environ, _environ);
-weak_alias(___environ, environ);
+char **__environ = 0;
+weak_alias(__environ, _environ);
+weak_alias(__environ, environ);
diff --git a/src/env/__init_security.c b/src/env/__init_security.c
new file mode 100644 (file)
index 0000000..5fd12ec
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stddef.h>
+#include <elf.h>
+#include <poll.h>
+#include <fcntl.h>
+#include "syscall.h"
+#include "libc.h"
+#include "atomic.h"
+
+#define AUX_CNT 24
+
+void __init_security(size_t *auxv)
+{
+       size_t i, aux[AUX_CNT] = { 0 };
+       struct pollfd pfd[3] = { {.fd=0}, {.fd=1}, {.fd=2} };
+
+       for (; auxv[0]; auxv+=2) if (auxv[0]<AUX_CNT) aux[auxv[0]] = auxv[1];
+       if (aux[AT_UID]==aux[AT_EUID] && aux[AT_GID]==aux[AT_EGID]
+               && !aux[AT_SECURE]) return;
+
+       __syscall(SYS_poll, pfd, 3, 0);
+       for (i=0; i<3; i++)
+               if (pfd[i].revents&POLLNVAL)
+                       if (__syscall(SYS_open, "/dev/null", O_RDWR)<0)
+                               a_crash();
+       libc.secure = 1;
+}
index 70af77b..f31222b 100644 (file)
@@ -1,21 +1,21 @@
 #include "libc.h"
 
-/* Any use of __environ/environ will override this symbol. */
-char **__dummy_environ = (void *)-1;
-weak_alias(__dummy_environ, ___environ);
+void __init_security(size_t *);
 
 int __libc_start_main(
        int (*main)(int, char **, char **), int argc, char **argv,
        int (*init)(int, char **, char **), void (*fini)(void),
        void (*ldso_fini)(void))
 {
-       /* Save the environment if it may be used by libc/application */
-       char **envp = argv+argc+1;
-       if (___environ != (void *)-1) ___environ = envp;
+       char **envp = argv+argc+1, **auxv = envp;
 
-       /* Avoid writing 0 and triggering unnecessary COW */
-       if (ldso_fini) libc.ldso_fini = ldso_fini;
-       if (fini) libc.fini = fini;
+       __environ = envp;
+       do auxv++; while (*auxv);
+       libc.auxv = (void *)++auxv;
+       libc.ldso_fini = ldso_fini;
+       libc.fini = fini;
+
+       __init_security((void *)auxv);
 
        /* Execute constructors (static) linked into the application */
        if (init) init(argc, argv, envp);
index 115cd86..d985902 100644 (file)
@@ -7,13 +7,15 @@
 struct __libc {
        void *main_thread;
        int threaded;
-       int canceldisable;
+       int secure;
+       size_t *auxv;
        int (*atexit)(void (*)(void));
        void (*fini)(void);
        void (*ldso_fini)(void);
        volatile int threads_minus_1;
-       int ofl_lock;
+       int canceldisable;
        FILE *ofl_head;
+       int ofl_lock;
 };