From d9fc4acc572c6647a4f27b838d35d27d805d190e Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sun, 28 Aug 2005 08:37:44 +0000 Subject: Migration (without history) of the current stable line to subversion. svn path=/main/branches/2.0/; revision=1941 --- src/bsd-flags/PKG-INFO | 10 + src/bsd-flags/chflags.c | 161 ++++ src/bsd-flags/setup.cfg | 6 + src/bsd-flags/setup.py | 24 + src/python-missingos/ChangeLog | 16 + src/python-missingos/PKG-INFO | 10 + src/python-missingos/README | 15 + src/python-missingos/missingos.c | 120 +++ src/python-missingos/setup.cfg | 8 + src/python-missingos/setup.py | 24 + src/sandbox-1.1/ChangeLog | 265 ++++++ src/sandbox-1.1/Makefile | 81 ++ src/sandbox-1.1/canonicalize.c | 173 ++++ src/sandbox-1.1/create-localdecls | 115 +++ src/sandbox-1.1/getcwd.c | 511 ++++++++++ src/sandbox-1.1/libctest.c | 7 + src/sandbox-1.1/libsandbox.c | 1383 ++++++++++++++++++++++++++++ src/sandbox-1.1/sandbox.bashrc | 8 + src/sandbox-1.1/sandbox.c | 863 +++++++++++++++++ src/sandbox-1.1/sandbox.h | 68 ++ src/sandbox-1.1/sandbox_futils.c | 513 +++++++++++ src/sandbox-dev/ChangeLog | 91 ++ src/sandbox-dev/Makefile | 62 ++ src/sandbox-dev/canonicalize.c | 194 ++++ src/sandbox-dev/create-localdecls | 95 ++ src/sandbox-dev/libctest.c | 6 + src/sandbox-dev/libsandbox.c | 1214 ++++++++++++++++++++++++ src/sandbox-dev/sandbox.bashrc | 8 + src/sandbox-dev/sandbox.c | 816 ++++++++++++++++ src/sandbox-dev/sandbox.h | 69 ++ src/sandbox-dev/sandbox_futils.c | 352 +++++++ src/sandbox/Makefile | 30 + src/sandbox/libsandbox.c | 873 ++++++++++++++++++ src/sandbox/problems/Makefile | 31 + src/sandbox/problems/libsandbox_emacsbug.c | 34 + src/sandbox/problems/libsandbox_muttbug.c | 24 + src/sandbox/problems/sandbox_dev_fd_foo.c | 42 + src/sandbox/problems/sandbox_muttbug.c | 43 + src/sandbox/sandbox.bashrc | 8 + src/sandbox/sandbox.c | 921 ++++++++++++++++++ src/tbz2tool.c | 228 +++++ 41 files changed, 9522 insertions(+) create mode 100644 src/bsd-flags/PKG-INFO create mode 100644 src/bsd-flags/chflags.c create mode 100644 src/bsd-flags/setup.cfg create mode 100755 src/bsd-flags/setup.py create mode 100644 src/python-missingos/ChangeLog create mode 100644 src/python-missingos/PKG-INFO create mode 100644 src/python-missingos/README create mode 100644 src/python-missingos/missingos.c create mode 100644 src/python-missingos/setup.cfg create mode 100755 src/python-missingos/setup.py create mode 100644 src/sandbox-1.1/ChangeLog create mode 100644 src/sandbox-1.1/Makefile create mode 100644 src/sandbox-1.1/canonicalize.c create mode 100755 src/sandbox-1.1/create-localdecls create mode 100644 src/sandbox-1.1/getcwd.c create mode 100644 src/sandbox-1.1/libctest.c create mode 100644 src/sandbox-1.1/libsandbox.c create mode 100644 src/sandbox-1.1/sandbox.bashrc create mode 100644 src/sandbox-1.1/sandbox.c create mode 100644 src/sandbox-1.1/sandbox.h create mode 100644 src/sandbox-1.1/sandbox_futils.c create mode 100644 src/sandbox-dev/ChangeLog create mode 100644 src/sandbox-dev/Makefile create mode 100644 src/sandbox-dev/canonicalize.c create mode 100755 src/sandbox-dev/create-localdecls create mode 100644 src/sandbox-dev/libctest.c create mode 100644 src/sandbox-dev/libsandbox.c create mode 100644 src/sandbox-dev/sandbox.bashrc create mode 100644 src/sandbox-dev/sandbox.c create mode 100644 src/sandbox-dev/sandbox.h create mode 100644 src/sandbox-dev/sandbox_futils.c create mode 100644 src/sandbox/Makefile create mode 100644 src/sandbox/libsandbox.c create mode 100644 src/sandbox/problems/Makefile create mode 100644 src/sandbox/problems/libsandbox_emacsbug.c create mode 100644 src/sandbox/problems/libsandbox_muttbug.c create mode 100644 src/sandbox/problems/sandbox_dev_fd_foo.c create mode 100644 src/sandbox/problems/sandbox_muttbug.c create mode 100644 src/sandbox/sandbox.bashrc create mode 100644 src/sandbox/sandbox.c create mode 100644 src/tbz2tool.c (limited to 'src') diff --git a/src/bsd-flags/PKG-INFO b/src/bsd-flags/PKG-INFO new file mode 100644 index 00000000..f2cb5c58 --- /dev/null +++ b/src/bsd-flags/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: bsd-chflags +Version: 0.1 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: Stephen Bennett +Author-email: spb@gentoo.org +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/src/bsd-flags/chflags.c b/src/bsd-flags/chflags.c new file mode 100644 index 00000000..77b89a28 --- /dev/null +++ b/src/bsd-flags/chflags.c @@ -0,0 +1,161 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/bsd-flags/chflags.c,v 1.1.2.2 2005/02/13 10:48:31 jstubbs Exp $ */ + +#include "Python.h" + +#include + +static char chflags_lchflags__doc__[]; +static PyObject * chflags_lchflags(PyObject *self, PyObject *args); +static char chflags_lgetflags__doc__[]; +static PyObject * chflags_lgetflags(PyObject *self, PyObject *args); +static char chflags_lhasproblems__doc__[]; +static PyObject * chflags_lhasproblems(PyObject *self, PyObject *args); + +static char chflags__doc__[] = "Provide some operations for manipulating" \ + "FreeBSD's filesystem flags"; + +static PyMethodDef chflags_methods[] = { + {"lchflags", chflags_lchflags, METH_VARARGS, chflags_lchflags__doc__}, + {"lgetflags", chflags_lgetflags, METH_VARARGS, chflags_lgetflags__doc__}, + {"lhasproblems", chflags_lhasproblems, METH_VARARGS, chflags_lhasproblems__doc__}, + {NULL, NULL} +}; + +static char chflags_lchflags__doc__[] = +"lchflags(path, flags) -> None\n\ +Change the flags on path to equal flags."; + +static char chflags_lgetflags__doc__[] = +"lgetflags(path) -> Integer\n\ +Returns the file flags on path."; + +static char chflags_lhasproblems__doc__[] = +"lhasproblems(path) -> Integer\n\ +Returns 1 if path has any flags set that prevent write operations;\n\ +0 otherwise."; + +static const unsigned long problemflags=0x00160016; + +#if defined __FreeBSD__ +static PyObject *chflags_lchflags(PyObject *self, PyObject *args) +{ + char *path = NULL; + int flags; + int res; + + if (!PyArg_ParseTuple(args, "eti:lchflags", + Py_FileSystemDefaultEncoding, &path, + &flags)) + { + return NULL; + } + + res = lchflags(path, flags); + + PyMem_Free(path); + return PyInt_FromLong((long)res); +} + +static PyObject *chflags_lhasproblems(PyObject *self, PyObject *args) +{ + char *path = NULL; + struct stat sb; + int res; + + if (!PyArg_ParseTuple(args, "et:lhasproblems", + Py_FileSystemDefaultEncoding, &path)) + { + return NULL; + } + + res = lstat(path, &sb); + + PyMem_Free(path); + + if (res < 0) + { + return PyInt_FromLong((long)res); + } + + if (sb.st_flags & problemflags) + return PyInt_FromLong(1); + else + return PyInt_FromLong(0); +} + +static PyObject *chflags_lgetflags(PyObject *self, PyObject *args) +{ + char *path = NULL; + struct stat sb; + int res; + + if (!PyArg_ParseTuple(args, "et:lgetflags", + Py_FileSystemDefaultEncoding, &path)) + { + return NULL; + } + + res = lstat(path, &sb); + + if (res < 0) + { + PyMem_Free(path); + return PyInt_FromLong((long)res); + } + + PyMem_Free(path); + + return PyInt_FromLong((long)sb.st_flags); +} + +#else +#warning Not on FreeBSD; building dummy lchflags + +static PyObject *chflags_lgetflags(PyObject *self, PyObject *args) +{ + /* Obviously we can't set flags if the OS/filesystem doesn't support them. */ + return PyInt_FromLong(0); +} + +static PyObject *chflags_lchflags(PyObject *self, PyObject *args) +{ + /* If file system flags aren't supported, just return 0, + as the effect is basically the same. */ + return PyInt_FromLong(0); +} + +static PyObject *chflags_lhasproblems(PyObject *self, PyObject *args) +{ + return PyInt_FromLong(0); +} + +#endif + +static int ins(PyObject *m, char *symbolname, int value) +{ + return PyModule_AddIntConstant(m, symbolname, value); +} + +DL_EXPORT(void) initchflags(void) +{ + PyObject *m; + m = Py_InitModule4("chflags", chflags_methods, chflags__doc__, + (PyObject*)NULL, PYTHON_API_VERSION); + + ins(m, "UF_SETTABLE", 0x0000ffff); + ins(m, "UF_NODUMP", 0x00000001); + ins(m, "UF_IMMUTABLE", 0x00000002); + ins(m, "UF_APPEND", 0x00000004); + ins(m, "UF_OPAQUE", 0x00000008); + ins(m, "UF_NOUNLINK", 0x00000010); + + ins(m, "SF_SETTABLE", 0xffff0000); + ins(m, "SF_NODUMP", 0x00010000); + ins(m, "SF_IMMUTABLE", 0x00020000); + ins(m, "SF_APPEND", 0x00040000); + ins(m, "SF_OPAQUE", 0x00080000); + ins(m, "SF_NOUNLINK", 0x00100000); + ins(m, "SF_SNAPSHOT", 0x00200000); + + ins(m, "PROBLEM_FLAGS", 0x00160016); +} diff --git a/src/bsd-flags/setup.cfg b/src/bsd-flags/setup.cfg new file mode 100644 index 00000000..7bb13404 --- /dev/null +++ b/src/bsd-flags/setup.cfg @@ -0,0 +1,6 @@ +# bsd-flags +# $Header: /var/cvsroot/gentoo-src/portage/src/bsd-flags/setup.cfg,v 1.1.2.1 2005/02/06 12:56:40 carpaski Exp $ + +[bdist_rpm] +release = 1 +python=python2 diff --git a/src/bsd-flags/setup.py b/src/bsd-flags/setup.py new file mode 100755 index 00000000..6f4a12ba --- /dev/null +++ b/src/bsd-flags/setup.py @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# $Header: /var/cvsroot/gentoo-src/portage/src/bsd-flags/setup.py,v 1.1.2.1 2005/02/06 12:56:40 carpaski Exp $ + +from os import chdir, stat +from distutils.core import setup, Extension + +setup (# Distribution meta-data + name = "bsd-chflags", + version = "0.1", + description = "", + author = "Stephen Bennett", + author_email = "spb@gentoo.org", + license = "", + long_description = \ + '''''', + ext_modules = [ Extension( + "chflags", + ["chflags.c"], + libraries=[], + ) + ], + url = "", + ) + diff --git a/src/python-missingos/ChangeLog b/src/python-missingos/ChangeLog new file mode 100644 index 00000000..1c80b2e6 --- /dev/null +++ b/src/python-missingos/ChangeLog @@ -0,0 +1,16 @@ +# $Header: /var/cvsroot/gentoo-src/portage/src/python-missingos/ChangeLog,v 1.4 2003/03/22 14:24:38 carpaski Exp $ + +python-missingos ChangeLog +Jonathon D Nelson +2 April 2002 + + 2 April 2002 14:15 jnelson + * Add optional 'mode' parameter to mknod + * Change from missingosmodule to missingos. + * Add some __doc__ entries + * Remove stupid hack for Python 1.5.2 support, + 1.5.2 doesn't have Py_FileSystemDefaultEncoding anyway. + * Return description of error when mode not p,b,c,u in mknod + + 2 April 2002 12:10 jnelson + * Populate files diff --git a/src/python-missingos/PKG-INFO b/src/python-missingos/PKG-INFO new file mode 100644 index 00000000..cb8087f4 --- /dev/null +++ b/src/python-missingos/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: python-missingos +Version: 0.2 +Summary: UNKNOWN +Home-page: UNKNOWN +Author: Jonathon D Nelson +Author-email: jnelson@gentoo.org +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/src/python-missingos/README b/src/python-missingos/README new file mode 100644 index 00000000..de3467d5 --- /dev/null +++ b/src/python-missingos/README @@ -0,0 +1,15 @@ +# $Header: /var/cvsroot/gentoo-src/portage/src/python-missingos/README,v 1.5.2.1 2004/10/22 16:53:30 carpaski Exp $ + +python-missingos +Jonathon D Nelson +2 April 2002 + +INTRO +===== +This module provides some missing file operations that don't +seem to be provided by the standard os and posix modules. +This list currently includes lchown and mknod. + +COPYRIGHT +========= +GPL diff --git a/src/python-missingos/missingos.c b/src/python-missingos/missingos.c new file mode 100644 index 00000000..cbcde5bf --- /dev/null +++ b/src/python-missingos/missingos.c @@ -0,0 +1,120 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/python-missingos/missingos.c,v 1.5.2.1 2004/10/22 16:53:30 carpaski Exp $ */ + +#include "Python.h" + +#include +#include +#include +#include + +static char missingos_lchown__doc__[]; +static PyObject * missingos_lchown(PyObject *self, PyObject *args); +static char missingos_mknod__doc__[]; +static PyObject * missingos_mknod(PyObject *self, PyObject *args); + +static char missingos__doc__[] = "Provide some operations that\ + are missing from the standard os / posix modules."; + +static PyMethodDef missingos_methods[] = { + {"lchown", missingos_lchown, METH_VARARGS, missingos_lchown__doc__}, + {"mknod", missingos_mknod, METH_VARARGS, missingos_mknod__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +posix_error_with_allocated_filename(char* name) +{ + PyObject *rc = PyErr_SetFromErrnoWithFilename(PyExc_OSError, name); + PyMem_Free(name); + return rc; +} + +static char missingos_lchown__doc__[] = +"lchown(path, uid, gid) -> None\n\ +Change the owner and group id of path to the numeric uid and gid."; + +static PyObject * +missingos_lchown(PyObject *self, PyObject *args) { + char *path = NULL; + int uid, gid; + int res; + if (!PyArg_ParseTuple(args, "etii:lchown", + Py_FileSystemDefaultEncoding, &path, + &uid, &gid)) + return NULL; + res = lchown(path, (uid_t) uid, (gid_t) gid); + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + +static char missingos_mknod__doc__[] = +"mknod(path, type, major, minor [, mode=0600 ]) -> None\n\ +Create a special file. Mode fixed at 0600.\ +Note that for type 'p' major and minor are ignored.\ +"; + +static PyObject * +missingos_mknod(PyObject *self, PyObject *args) { + char *path = NULL; + char *type = NULL; + int major = 0; + int minor = 0; + mode_t real_mode; + dev_t real_dev; + int mode = 0600; + + int res; + if (!PyArg_ParseTuple(args, "etsii|i:mknod", + Py_FileSystemDefaultEncoding, &path, + &type, &major, &minor, &mode)) + return NULL; + /* type can be *one* of b, c, u, p */ + /* major/minor are forbidden for p, reqd otherwise */ + if (!strcmp(type, "p")) { + /* pipe */ + if (major != 0 || minor != 0) { + return NULL; + } + real_mode = S_IFIFO; + major = 0; + minor = 0; + } else if (!strcmp(type, "b")) { + /* block */ + real_mode = S_IFBLK; + } else if (!strcmp(type, "c")) { + real_mode = S_IFCHR; + /* char */ + } else if (!strcmp(type, "u")) { + real_mode = S_IFCHR; + /* unbuffered char */ + } else { + /* error */ + PyErr_SetString(PyExc_ValueError, "type must be one of p,b,c,u"); + return NULL; + } + + real_mode |= mode; + real_dev = (major << 8) | minor; + + /* use mode to modify real_mode */ + + res = mknod(path, real_mode, real_dev); + if (res < 0) + return posix_error_with_allocated_filename(path); + PyMem_Free(path); + Py_INCREF(Py_None); + return Py_None; +} + + +DL_EXPORT(void) +initmissingos(void) { + PyObject *m; + + m = Py_InitModule4("missingos", missingos_methods, + missingos__doc__, (PyObject *)NULL, + PYTHON_API_VERSION); +} diff --git a/src/python-missingos/setup.cfg b/src/python-missingos/setup.cfg new file mode 100644 index 00000000..8677f66f --- /dev/null +++ b/src/python-missingos/setup.cfg @@ -0,0 +1,8 @@ +# python-missingos +# $Header: /var/cvsroot/gentoo-src/portage/src/python-missingos/setup.cfg,v 1.4.2.1 2004/10/22 16:53:30 carpaski Exp $ + +[bdist_rpm] +release = 1 +doc_files = ChangeLog + README +python=python2 diff --git a/src/python-missingos/setup.py b/src/python-missingos/setup.py new file mode 100755 index 00000000..b82e3e51 --- /dev/null +++ b/src/python-missingos/setup.py @@ -0,0 +1,24 @@ +#! /usr/bin/env python2.2 +# $Header: /var/cvsroot/gentoo-src/portage/src/python-missingos/setup.py,v 1.5.2.1 2004/10/22 16:53:30 carpaski Exp $ + +from os import chdir, stat +from distutils.core import setup, Extension + +setup (# Distribution meta-data + name = "python-missingos", + version = "0.2", + description = "", + author = "Jonathon D Nelson", + author_email = "jnelson@gentoo.org", + license = "", + long_description = \ + '''''', + ext_modules = [ Extension( + "missingos", + ["missingos.c"], + libraries=[], + ) + ], + url = "", + ) + diff --git a/src/sandbox-1.1/ChangeLog b/src/sandbox-1.1/ChangeLog new file mode 100644 index 00000000..8da8ec9f --- /dev/null +++ b/src/sandbox-1.1/ChangeLog @@ -0,0 +1,265 @@ +# ChangeLog for Path Sandbox +# Copyright 1999-2004 Gentoo Foundation; Distributed under the GPL v2 +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/ChangeLog,v 1.37.2.3 2004/12/01 22:14:09 carpaski Exp $ + + 01 Dec 2004; Nicholas Jones libsandbox.c, sandbox.c: + Added ferringb's code to handle the sandbox pid overflow problem. + + 07 Nov 2004; Brian Harring libsandbox.c: c99 standard + allowing data and code mixing in code isn't available for gcc 2.95- should fix + bug #70351. + + 03 Nov 2004; Brian Harring libsandbox.c, sandbox_futils.c: + Fixups, and a hole closed regarding verifying SANDBOX_(|DEBUG_)LOG is sane. + + 02 Aug 2004; Nicholas Jones libsandbox.c: Code from + Seth Robertson that tracked down all adjuct flags for read operations that + do not invoke a write operation. + + 04 Apr 2004; Nicholas Jones libsandbox.c, sandbox.c: + Another fix from jstubbs regarding a free() on a stack variable for the + environment -- tracking now prevents extraneous free()'s segfault. + + 04 Apr 2004; Nicholas Jones libsandbox.c, sandbox.c: + J. Stubbs tracked down a new bug where mkdir was failing to the patch on + the lstat in mkdir... it now only returns 0 or -1 as documented for mkdir. + Also remove the errno = ESUCCESS settings as documentation points out that + a library isn't allowed to do that. + + 04 Apr 2004; Nicholas Jones libsandbox.c: Added a + file_security_check() function to check random potential exploits on files + that sandbox is to load and read -- Normally sandboxpids.tmp. This fixes + the 'system-crippling' exploits (bug 21923) and catches a few other + potential problems. + + 20 Mar 2004; Nicholas Jones Makefile: Updates for + 32/64 bit sandbox. Made CC and LD '?=' values to allow passed in CC to work. + + 20 Mar 2004; Nicholas Jones libsandbox.c: + bug 42048 -- Fixed the lstat/errno conditions for mkdir . + Added the 64/32 bit sandbox patch for AMD64 bug 32963 . + + 29 Feb 2004; Martin Schlemmer sandbox.c, sandbox_futils.c : + Fix permissions and group of pids file and logs. Permissions should be 0664 + and group should be 'portage'. Bug #34260. + + 28 Feb 2004; Martin Schlemmer libsandbox.c : + Besides a small cleanup, redo how we replace LD_PRELOAD in the environ passed + to the real execve (in our execve wrapper). Seems that on some arches (sparc + among others) do not allow us to tamper with the readonly copy passed to + execve, so pass our own copy of the environment. Bug #42290. + + 11 Jan 2004; Nicholas Jones create-decls: + Changed tail to head and added a notice about duration of glibc check. + + 21 Dec 2003; Nicholas Jones create-decls: + Changed the glibc subversion check to use /usr/bin/* instead of /bin/sh + as there isn't a guarentee that it is dynamic. + + 02 Nov 2003; Martin Schlemmer libsandbox.c : + If 'file' passed to before_syscall(const char *func, const char *file) is + invalid, we should set errno to ENOENT, and not EINVAL. This should + close bug #32238. + + 14 Oct 2003; Martin Schlemmer libsandbox.c : + Fix a bug that occurs mainly on 64bit arch, where the file passed to + the functions we wrap, is invalid, and then cause canonicalize to pass + garbage to before_syscall(), thanks to great detective work from + Andrea Luzzardi (bug #29846). + + 13 Oct 2003; Martin Schlemmer create-localdecls : + Add a uClibc detection patch from Peter S. Mazinger . + + 13 Oct 2003; Martin Schlemmer libsandbox.c : + Fix a bug in libsandbox.c 's checking in the rename wrapper - it basically + only checked the destination patch, and not the source, so we could move + a protected file to a unprotected directory, and then delete/modify it. + Thanks to Andrea Luzzardi (scox) , bug #30992, for this fix. + + 12 Oct 2003; Nicholas Jones sandbox.c : + Added python2.3 to the predict section/variable. + + 28 Sep 2003; Martin Schlemmer libsandbox.c, sandbox.c, + sandbox.h, sandbox_futils.c : + Add support to set the pids file via SANDBOX_PIDS_FILE at startup. If + it is not set, it will revert to its old value. + + 27 Sep 2003; Martin Schlemmer libsandbox.c : + Fix our mkdir wrapper to check if the dir exist, and return EEXIST if so, + rather than failing with a violation, bug #29748. + + 27 Jul 2003; Martin Schlemmer libsandbox.c : + Fix canonicalize() to ignore calls with path = "". + + 27 Jul 2003; Martin Schlemmer getcwd.c, libsandbox.c, + sandbox_futils.c, canonicalize.c : + Once again coreutils fails, as my systems had 2.5 kernel, the getcwd system + call handled strings larger than PATH_MAX (bug #21766). It however does not + work the same on 2.4 kernels. + + To fix, I added the posix implementation of getcwd() (from glibc cvs) that + do not need the system call. We use the default getcwd() function via a + wrapper (egetcwd), and then lstat the returned path. If lstat fails, it + means the current directory was removed, OR that the the system call for + getcwd failed (curious is that it do not fail and return NULL or set + errno, but rather just truncate the retured directory - usually from the + start), and if so, we use the generic getcwd() function (__egetcwd). Note + that we do not use the generic version all the time, as it calls lstat() + a great number of times, and performance degrade much. + + 29 Jun 2003; Martin Schlemmer create-localdecls, + libsandbox.c : + Make sure SB_PATH_MAX will not wrap. Fix two possible memory leaks. + + 22 Jun 2003; Martin Schlemmer libsandbox.c, canonicalize.c + create-localdecls : + When checking path names of files accessed, we need to canonicalize it, else + it may be a symlink in a 'write allowed' directory pointing to a file in a + directory we should not have write access to. + + With something like coreutils-5.0, we have two problems: + 1) One of the tests checks if getcwd() can return a path longer than + PATH_MAX. This test then tries to create a dir which even while + created local (mkdir("conftest2")), it ends up being resolved with + a name that is much larger than PATH_MAX. The problem now is that + canonicalize() have undefined behaviour when the path was too long + (returned wrongly truncated paths, etc), and pass the wrong path to + before_syscall() (causing the bogus sandbox violations). + 2) The ecanonicalize() function we used, along with the canonicalize() + function did not support longer than PATH_MAX. This is partly a + cause for 1), but the error checking (rather lack of it) of calls + to erealpath() in canonicalize() was the prime reason for 1). + + As we do not use this canonicalized name to call the function, we resolve this + by fixing canonicalize() to do better error checking, and ecanonicalize() as + well as all functions in libsandbox.c to use a PATH_MAX of 'PATH_MAX * 2'. + While they will resolve paths properly now, and can check if a write/read is + allowed, the functions called from the sandboxed environment will still work + as expected. + + This should resolve bug #21766. + + 06 Apr 2003; Martin Schlemmer libsandbox.c : + For some reason sandbox fails with a 'open_wr' if you run 'locale -a' under + it (bug #16298). + + Problem is that for some reason locale fopen's locale.alias with mode "rm". + + ------------------------------------------------------- + nosferatu root # grep fopen locale.log + fopen("/usr/share/locale/locale.alias", "rm"ACCESS DENIED open_wr: /usr/share/locale/locale.alias + nosferatu root # + -------------------------------------------------------- + + I checked the source of locale, but it have fopen with mode 'r', so + not sure where the "rm" mode comes from. Anyhow, changed the check in + before_syscall_open_char() to also see mode "rm" as readonly. + + 23 Feb 2003; Martin Schlemmer create-localdecls : + + Add glibc-2.3 support. + + 22 Feb 2003; Martin Schlemmer sandbox.c : + + Some /etc/ld.so.preload fixes. Just changed the #if defines to cover all + operations releated to preload, as well as only try to modify ld.so.preload + if we can. Also modify to write the pid to /tmp/sandboxpids.tmp even when + not using ld.so.preload. Fix to not write this instance of sandbox's pid + to /tmp/sandboxpids.tmp on exit if this is not the last sandbox running. + + 22 Feb 2003; Nicholas Jones Makefile : + + Changed the LD to CC for hppa. + + 22 Feb 2003; Nicholas Jones create-localdecls : + + Killed the previous changes I made. + + 17 Feb 2003; Nicholas Jones create-localdecls : + + Added parisc to BROKEN_RTLD_ARCHLIST to see if it we can fix the relocation probs. + + 09 Jan 2003; J Robert Ray sandbox.c : + + Don't segfault if $HOME isn't set, set $HOME to "/" instead. Fixes bug 10868. + + 16 Dec 2002; Martin Schlemmer create-localdecls : + + Fix memory leak for mips, bug #12236. Thanks to Torgeir Hansen + for this fix. + + 4 Dec 2002; J Robert Ray sandbox.h sandbox_futils.c : + + sandbox_futils defined a dirname() function that was masking the same + function in glibc and was broken (e.g.: SANDBOX_DIR was being set to + '/usr/lib/portage/bi/'). Fixed function to return expected results and + renamed it to sb_dirname() to no longer mask the glibc function. Closes bug + 11231. + + 4 Dec 2002; Martin Schlemmer : + + Fix a segfault in libsandbox.c if canonicalize() was called with + first parameter = NULL. + + 1 Sep 2002; Martin Schlemmer : + + Fix my braindead 'return 1;' in a void function. Updated sandbox.c, + cleanup() for this. + + Change cleanup() in sandbox.c not to exit with fail status if + the pidsfile is missing. We really should still display sandbox + violations if they occured. + + 31 Aug 2002; Martin Schlemmer : + + Update cleanup() in sandbox.c to remove the PIDSFILE if this is + the last sandbox running. + + 25 Aug 2002; Martin Schlemmer : + + Major cleanups to mainly libsandbox.c again. + + 22 Aug 2002; Martin Schlemmer : + + Add copyrights to sandbox.h and sandbox_futils.h. If wrong, the + parties involved should please contact me so that we can fix it. + + Add opendir wrapper to libsandbox.c. + + 21 Aug 2002; Martin Schlemmer : + + Do some more cleanups to ecanonicalize(), as it dropped filenames in + rare cases (after my symlink cleanups), and caused glibc to bork. + These fixes went into canonicalize.c. + + 20 Aug 2002; Martin Schlemmer : + + Fix spawn_shell() and main() in sandbox.c to properly return fail + status. + + 19 Aug 2002; Martin Schlemmer : + + The new canonicalize() function in libsandbox.c also resolved symlinks, + which caused on cleaning sandbox errors if the symlink pointed to a + file in the live root. Ripped out canonicalize() and realpath() from + glibc; removed the symlink stuff, and changed them to ecanonicalize() + and erealpath(). + + 18 Aug 2002; Martin Schlemmer : + + Ripped out all the wrappers, and implemented those of InstallWatch. + Losts of cleanups and bugfixes. Implement a execve that forces + $LIBSANDBOX in $LD_PRELOAD. We can now thus do away with the feared + /etc/ld.so.preload (*g*) ... Made the needed changes to sandbox.c, + sandbox.h and sandbox_futils.c. Rewrote the Makefile for most + parts; it now have an install target. + + Reformat the whole thing to look somewhat like the reworked sandbox.c + and new sandbox.h and sandbox_futils.c from: + + Brad House . + + Additional Copyrights now due to the InstallWatch code: + + Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro + diff --git a/src/sandbox-1.1/Makefile b/src/sandbox-1.1/Makefile new file mode 100644 index 00000000..b451676a --- /dev/null +++ b/src/sandbox-1.1/Makefile @@ -0,0 +1,81 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# +# Modified 15 Apr 2002 Jon Nelson +# Clean up Makefile somewhat, and use make's implicit rules +# +# Modified 19 Aug 2002; Martin Schlemmer +# Major rewrite to support new stuff +# +# Indent: indent -kr -i2 -ts2 -sob -l80 -ss -bs -psl +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/Makefile,v 1.7.2.2 2004/11/29 08:41:28 carpaski Exp $ + +CC ?= gcc +LD ?= ld +CFLAGS = +ARCH_CFLAGS = +OBJ_CFLAGS = -D_GNU_SOURCE -DPIC -fPIC -D_REENTRANT +LIBS = +LDFLAGS = +DESTDIR = + +HAVE_64BIT_ARCH = + +ifneq ($(HAVE_64BIT_ARCH),) + TARGETS = libsandbox.so libsandbox32.so sandbox + ARCH_CFLAGS += -m64 + OBJ_CFLAGS += -DSB_HAVE_64BIT_ARCH +else + TARGETS = libsandbox.so sandbox +endif + +all: $(TARGETS) + +sandbox: sandbox.o sandbox_futils.o getcwd.c + $(CC) $(CFLAGS) $(ARCH_CFLAGS) $(OBJ_CFLAGS) -Wall $^ -ldl -lc -o $@ + +sandbox.o: sandbox.c sandbox.h + $(CC) $(CFLAGS) $(ARCH_CFLAGS) $(OBJ_CFLAGS) -Wall -c sandbox.c -o $@ + +sandbox_futils.o: localdecls.h sandbox_futils.c sandbox.h + $(CC) $(CFLAGS) $(ARCH_CFLAGS) $(OBJ_CFLAGS) -Wall -c sandbox_futils.c -o $@ + +libsandbox.so: libsandbox.o sandbox_futils.o + $(CC) $^ -shared $(ARCH_CFLAGS) -fPIC -ldl -lc -nostdlib -lgcc -o $@ + +libsandbox.o: localdecls.h libsandbox.c canonicalize.c getcwd.c + $(CC) $(CFLAGS) $(ARCH_CFLAGS) $(OBJ_CFLAGS) -Wall -c libsandbox.c + +sandbox_futils32.o: sandbox_futils.c sandbox.h + $(CC) $(CFLAGS) -m32 $(OBJ_CFLAGS) -Wall -c sandbox_futils.c -o $@ + +libsandbox32.so: libsandbox32.o sandbox_futils32.o + $(CC) $^ -shared -m32 -fPIC -ldl -lc -nostdlib -lgcc -o $@ + +libsandbox32.o: libsandbox.c localdecls.h canonicalize.c getcwd.c + $(CC) $(CFLAGS) -m32 $(OBJ_CFLAGS) -Wall -c libsandbox.c -o $@ + +localdecls.h: create-localdecls libctest.c + ./create-localdecls + + +install: all + install -d -m 0755 $(DESTDIR)/lib + $(if $(HAVE_64BIT_ARCH),install -d -m 0755 $(DESTDIR)/lib32) + install -d -m 0755 $(DESTDIR)/usr/lib/portage/bin + install -d -m 0755 $(DESTDIR)/usr/lib/portage/lib + install -m 0755 libsandbox.so $(DESTDIR)/lib + $(if $(HAVE_64BIT_ARCH),install -m 0755 libsandbox32.so $(DESTDIR)/lib32/libsandbox.so) + install -m 0755 sandbox $(DESTDIR)/usr/lib/portage/bin + install -m 0644 sandbox.bashrc $(DESTDIR)/usr/lib/portage/lib + + +clean: + rm -f $(TARGETS) + rm -f *.o *~ core + rm -f localdecls.h + + +# vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/canonicalize.c b/src/sandbox-1.1/canonicalize.c new file mode 100644 index 00000000..85ca77f7 --- /dev/null +++ b/src/sandbox-1.1/canonicalize.c @@ -0,0 +1,173 @@ +/* Return the canonical absolute name of a given file. + Copyright (C) 1996-2001, 2002 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/canonicalize.c,v 1.5.2.1 2004/10/22 16:53:30 carpaski Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +/* Return the canonical absolute name of file NAME. A canonical name + does not contain any `.', `..' components nor any repeated path + separators ('/') or symlinks. All path components must exist. If + RESOLVED is null, the result is malloc'd; otherwise, if the + canonical name is SB_PATH_MAX chars or more, returns null with `errno' + set to ENAMETOOLONG; if the name fits in fewer than SB_PATH_MAX chars, + returns the name in RESOLVED. If the name cannot be resolved and + RESOLVED is non-NULL, it contains the path of the first component + that cannot be resolved. If the path can be resolved, RESOLVED + holds the same value as the value returned. */ + +/* Modified: 19 Aug 2002; Martin Schlemmer + * + * Cleaned up unneeded stuff, and change so that it will not + * resolve symlinks. Also prepended a 'e' to functions that + * I did not rip out. + * + */ + +char * +erealpath(const char *name, char *resolved) +{ + char *rpath, *dest; + const char *start, *end, *rpath_limit; + long int path_max; + + if (name == NULL) { + /* As per Single Unix Specification V2 we must return an error if + either parameter is a null pointer. We extend this to allow + the RESOLVED parameter to be NULL in case the we are expected to + allocate the room for the return value. */ + __set_errno(EINVAL); + return NULL; + } + + if (name[0] == '\0') { + /* As per Single Unix Specification V2 we must return an error if + the name argument points to an empty string. */ + __set_errno(ENOENT); + return NULL; + } +#ifdef SB_PATH_MAX + path_max = SB_PATH_MAX; +#else + path_max = pathconf(name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + if (resolved == NULL) { + rpath = malloc(path_max); + if (rpath == NULL) + return NULL; + } else + rpath = resolved; + rpath_limit = rpath + path_max; + + if (name[0] != '/') { + if (!egetcwd(rpath, path_max)) { + rpath[0] = '\0'; + goto error; + } + dest = strchr(rpath, '\0'); + } else { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) { + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */ ; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */ ; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/') ; + } else { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) { + ptrdiff_t dest_offset = dest - rpath; + char *new_rpath; + + if (resolved) { + __set_errno(ENAMETOOLONG); + if (dest > rpath + 1) + dest--; + *dest = '\0'; + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + new_rpath = (char *) realloc(rpath, new_size); + if (new_rpath == NULL) + goto error; + rpath = new_rpath; + rpath_limit = rpath + new_size; + + dest = rpath + dest_offset; + } + + dest = __mempcpy(dest, start, end - start); + *dest = '\0'; + } + } +#if 1 + if (dest > rpath + 1 && dest[-1] == '/') + --dest; +#endif + *dest = '\0'; + + return resolved ? memcpy(resolved, rpath, dest - rpath + 1) : rpath; + +error: + if (resolved) + strcpy(resolved, rpath); + else + free(rpath); + return NULL; +} + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/create-localdecls b/src/sandbox-1.1/create-localdecls new file mode 100755 index 00000000..816723d5 --- /dev/null +++ b/src/sandbox-1.1/create-localdecls @@ -0,0 +1,115 @@ +#!/bin/sh + +# This is a quick'n'dirty hack to make the program behave correctly +# under different systems. +# Example: +# when using libc5, (f)trucate's offset argument type is size_t with +# libc5, but it's off_t with libc6 (glibc2). +# +# Uhm... time to learn GNU autoconf :-) +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/create-localdecls,v 1.11.2.1 2004/10/22 16:53:30 carpaski Exp $ + +OUTFILE='localdecls.h' + +# if your arch needs to dlopen() glibc, add it here separated by space :] +BROKEN_RTLD_ARCHLIST="mips" + +echo '/* This file is automatically generated *' > $OUTFILE +echo ' * Modify create-localdecls instead of this */' >> $OUTFILE +echo >> $OUTFILE +echo '#ifndef __LOCALDECLS_H_' >> $OUTFILE +echo '#define __LOCALDECLS_H_' >> $OUTFILE +echo >> $OUTFILE + +### +### +### + +echo -n 'Checking truncate argument type... ' +if grep -q 'truncate.*size_t' /usr/include/unistd.h ; then + echo 'size_t' + echo '#define TRUNCATE_T size_t' >> $OUTFILE +else + echo 'off_t' # At least, I HOPE it's off_t :-) + echo '#define TRUNCATE_T off_t' >> $OUTFILE +fi + +### +### +### + +echo -n 'Checking libc version... ' +gcc -Wall -o libctest libctest.c +VERSION=`ldd libctest | grep libc\\.so | grep -v 'ld-uClibc' | awk '{print $1}'` + +echo $VERSION +echo "#define LIBC_VERSION \"$VERSION\"" >> $OUTFILE +if test "$VERSION" = 'libc.so.5' ; then + echo '#define BROKEN_RTLD_NEXT' >> $OUTFILE + echo '#define LIBC 5' >> $OUTFILE +else + # for the arch's that need to dlopen() libc to fetch real funcs! + # 16.12.02 -Torgeir Hansen + MYARCH=`/bin/uname -m` + for x in $BROKEN_RTLD_ARCHLIST; do + if [ $x = $MYARCH ]; then + echo '#define BROKEN_RTLD_NEXT' >> $OUTFILE + fi + done + +fi + +if test "$VERSION" = 'libc.so.6' ; then + echo -n 'Checking glibc subversion...' + tmp="$(ldd libctest 2>/dev/null | grep libc.so 2>/dev/null | head -n 1)" + LibcPath=`expr "$tmp" : '[^/]*\(/[^ ]*\)'` + tmp="`strings $LibcPath | grep -i 'c library'`" + OsLibcMajor=`expr "$tmp" : '.* \([0-9][0-9]*\)'` + OsLibcMinor=`expr "$tmp" : '.* [0-9][0-9]*\.\([0-9][0-9]*\)'` + echo " ${OsLibcMajor}.${OsLibcMinor}" + case "$OsLibcMajor" in + 2) + # 2 is the glibc version + case "$OsLibcMinor" in + 0) + echo '#define GLIBC_MINOR 0' >> $OUTFILE + SUBVERSION='glibc-2.0' ;; + 1) + echo '#define GLIBC_MINOR 1' >> $OUTFILE + SUBVERSION='glibc-2.1' ;; + 2) + echo '#define GLIBC_MINOR 2' >> $OUTFILE + SUBVERSION='glibc-2.2' ;; + 3) + echo '#define GLIBC_MINOR 3' >> $OUTFILE + SUBVERSION='glibc-2.3' ;; + *) + echo 'Treated as glibc >= 2.1 (finger crossed)' + echo '#define GLIBC_MINOR 1' >> $OUTFILE + SUBVERSION='glibc-2.1' ;; + esac + ;; + esac +fi + +rm libctest + +echo ' +#ifdef PATH_MAX +# define SB_PATH_MAX PATH_MAX * 2 +# if (SB_PATH_MAX >= INT_MAX) || (SB_PATH_MAX < PATH_MAX) +# undef SB_PATH_MAX +# define SB_PATH_MAX PATH_MAX + 25 +# if (SB_PATH_MAX >= INT_MAX) || (SB_PATH_MAX < PATH_MAX) +# error SB_PATH_MAX too big! +# endif +# endif +#else +# error PATH_MAX not defined! +#endif' >> $OUTFILE + +echo >> $OUTFILE +echo '#endif' >> $OUTFILE +echo + diff --git a/src/sandbox-1.1/getcwd.c b/src/sandbox-1.1/getcwd.c new file mode 100644 index 00000000..552e41a0 --- /dev/null +++ b/src/sandbox-1.1/getcwd.c @@ -0,0 +1,511 @@ +/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Wants: + AC_STDC_HEADERS + AC_DIR_HEADER + AC_UNISTD_H + AC_MEMORY_H + AC_CONST + AC_ALLOCA + */ + +/* + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/getcwd.c,v 1.1.2.1 2004/10/22 16:53:30 carpaski Exp $ + */ + +/* Modified: 26 July 2003; Martin Schlemmer + * + * Cleaned up unneeded stuff. Add a wrapper to try and detect when + * we have a kernel whose getcwd system call do not handle directory + * names longer than PATH_MAX, and if so, use our generic version. + * To work truly with > PATH_MAX lengh CWDs, I had to increase the + * size of the dots[] array. Also prepended a 'e' to functions that + * I did not rip out. + * + */ + +/* AIX requires this to be the first thing in the file. */ +#if defined _AIX && !defined __GNUC__ +#pragma alloca +#endif + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#ifdef STDC_HEADERS +# include +#endif + +#if !defined __GNU_LIBRARY__ && !defined STDC_HEADERS +extern int errno; +#endif +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +#ifndef NULL +# define NULL 0 +#endif + +#if defined USGr3 && !defined DIRENT +# define DIRENT +#endif /* USGr3 */ +#if defined Xenix && !defined SYSNDIR +# define SYSNDIR +#endif /* Xenix */ + +#if defined POSIX || defined DIRENT || defined __GNU_LIBRARY__ +# include +# ifndef __GNU_LIBRARY__ +# define D_NAMLEN(d) strlen((d)->d_name) +# else +# define HAVE_D_NAMLEN +# define D_NAMLEN(d) ((d)->d_namlen) +# endif +#else /* not POSIX or DIRENT */ +# define dirent direct +# define D_NAMLEN(d) ((d)->d_namlen) +# define HAVE_D_NAMLEN +# if defined USG && !defined sgi +# if defined SYSNDIR +# include +# else /* Not SYSNDIR */ +# include "ndir.h" +# endif /* SYSNDIR */ +# else /* not USG */ +# include +# endif /* USG */ +#endif /* POSIX or DIRENT or __GNU_LIBRARY__ */ + +#if defined HAVE_UNISTD_H || defined __GNU_LIBRARY__ +# include +#endif + +#if defined STDC_HEADERS || defined __GNU_LIBRARY__ || defined POSIX +# include +# include +# define ANSI_STRING +#else /* No standard headers. */ + +# ifdef USG + +# include +# ifdef NEED_MEMORY_H +# include +# endif +# define ANSI_STRING + +# else /* Not USG. */ + +# ifdef NeXT + +# include + +# else /* Not NeXT. */ + +# include + +# ifndef bcmp +extern int bcmp(); +# endif +# ifndef bzero +extern void bzero(); +# endif +# ifndef bcopy +extern void bcopy(); +# endif + +# endif /* NeXT. */ + +# endif /* USG. */ + +extern char *malloc(), *realloc(); +extern void free(); + +#endif /* Standard headers. */ + +#ifndef ANSI_STRING +# define memcpy(d, s, n) bcopy((s), (d), (n)) +# define memmove memcpy +#endif /* Not ANSI_STRING. */ + +#ifndef MAX +# define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif + +#ifdef _LIBC +# ifndef mempcpy +# define mempcpy __mempcpy +# endif +# define HAVE_MEMPCPY 1 +#endif + +#if !defined __alloca && !defined __GNU_LIBRARY__ + +# ifdef __GNUC__ +# undef alloca +# define alloca(n) __builtin_alloca (n) +# else /* Not GCC. */ +# if defined sparc || defined HAVE_ALLOCA_H +# include +# else /* Not sparc or HAVE_ALLOCA_H. */ +# ifndef _AIX +extern char *alloca(); +# endif /* Not _AIX. */ +# endif /* sparc or HAVE_ALLOCA_H. */ +# endif /* GCC. */ + +# define __alloca alloca + +#endif + +#if defined HAVE_LIMITS_H || defined STDC_HEADERS || defined __GNU_LIBRARY__ +# include +#else +# include +#endif + +#ifndef PATH_MAX +# ifdef MAXPATHLEN +# define PATH_MAX MAXPATHLEN +# else +# define PATH_MAX 1024 +# endif +#endif + +#if !defined STDC_HEADERS && !defined __GNU_LIBRARY__ +# undef size_t +# define size_t unsigned int +#endif + +#if !__STDC__ && !defined const +# define const +#endif + +#ifndef __GNU_LIBRARY__ +# define __lstat stat +#endif + +#ifndef _LIBC +# define __getcwd getcwd +#endif + +#ifndef GETCWD_RETURN_TYPE +# define GETCWD_RETURN_TYPE char * +#endif + +#ifndef SB_PATH_MAX +# include "localdecls.h" +# define OUTSIDE_LIBSANDBOX +#endif + +#ifndef __LIBC +# define __lstat lstat +# define __readdir readdir +# define __closedir closedir +#endif + +/* Get the pathname of the current working directory, and put it in SIZE + bytes of BUF. Returns NULL if the directory couldn't be determined or + SIZE was too small. If successful, returns BUF. In GNU, if BUF is + NULL, an array is allocated with `malloc'; the array is SIZE bytes long, + unless SIZE == 0, in which case it is as big as necessary. */ + +GETCWD_RETURN_TYPE +__egetcwd(buf, size) +char *buf; +size_t size; +{ + static const char dots[] + = "../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../../\ +../../../../../../../../../../../../../../../../../../../../../../../../../.."; + const char *dotp = &dots[sizeof (dots)]; + const char *dotlist = dots; + size_t dotsize = sizeof (dots) - 1; + dev_t rootdev, thisdev; + ino_t rootino, thisino; + char *path; + register char *pathp; + struct stat st; + int prev_errno = errno; + size_t allocated = size; + + if (size == 0) { + if (buf != NULL) { + __set_errno(EINVAL); + return NULL; + } + + allocated = SB_PATH_MAX + 1; + } + + if (buf != NULL) + path = buf; + else { + path = malloc(allocated); + if (path == NULL) + return NULL; + } + + pathp = path + allocated; + *--pathp = '\0'; + + if (__lstat(".", &st) < 0) + goto lose2; + thisdev = st.st_dev; + thisino = st.st_ino; + + if (__lstat("/", &st) < 0) + goto lose2; + rootdev = st.st_dev; + rootino = st.st_ino; + + while (!(thisdev == rootdev && thisino == rootino)) { + register DIR *dirstream; + struct dirent *d; + dev_t dotdev; + ino_t dotino; + char mount_point; + + /* Look at the parent directory. */ + if (dotp == dotlist) { + /* My, what a deep directory tree you have, Grandma. */ + char *new; + if (dotlist == dots) { + new = malloc(dotsize * 2 + 1); + if (new == NULL) + goto lose; +#ifdef HAVE_MEMPCPY + dotp = mempcpy(new, dots, dotsize); +#else + memcpy(new, dots, dotsize); + dotp = &new[dotsize]; +#endif + } else { + new = realloc((__ptr_t) dotlist, dotsize * 2 + 1); + if (new == NULL) + goto lose; + dotp = &new[dotsize]; + } +#ifdef HAVE_MEMPCPY + *((char *) mempcpy((char *) dotp, new, dotsize)) = '\0'; + dotsize *= 2; +#else + memcpy((char *) dotp, new, dotsize); + dotsize *= 2; + new[dotsize] = '\0'; +#endif + dotlist = new; + } + + dotp -= 3; + + /* Figure out if this directory is a mount point. */ + if (__lstat(dotp, &st) < 0) + goto lose; + dotdev = st.st_dev; + dotino = st.st_ino; + mount_point = dotdev != thisdev; + + /* Search for the last directory. */ +#ifdef OUTSIDE_LIBSANDBOX + dirstream = opendir(dotp); +#else + dirstream = true_opendir(dotp); +#endif + if (dirstream == NULL) + goto lose; + /* Clear errno to distinguish EOF from error if readdir returns + NULL. */ + __set_errno(0); + while ((d = __readdir(dirstream)) != NULL) { + if (d->d_name[0] == '.' && + (d->d_name[1] == '\0' + || (d->d_name[1] == '.' && d->d_name[2] == '\0'))) + continue; + if (mount_point || (ino_t) d->d_ino == thisino) { + char name[dotlist + dotsize - dotp + 1 + _D_ALLOC_NAMLEN(d)]; +#ifdef HAVE_MEMPCPY + char *tmp = mempcpy(name, dotp, + dotlist + dotsize - dotp); + *tmp++ = '/'; + strcpy(tmp, d->d_name); +#else + memcpy(name, dotp, dotlist + dotsize - dotp); + name[dotlist + dotsize - dotp] = '/'; + strcpy(&name[dotlist + dotsize - dotp + 1], d->d_name); +#endif + /* We don't fail here if we cannot stat() a directory entry. + This can happen when (network) filesystems fail. If this + entry is in fact the one we are looking for we will find + out soon as we reach the end of the directory without + having found anything. */ + if (__lstat(name, &st) >= 0 && st.st_dev == thisdev + && st.st_ino == thisino) + break; + } + } + if (d == NULL) { + int save = errno; + (void) __closedir(dirstream); + if (save == 0) + /* EOF on dirstream, which means that the current directory + has been removed. */ + save = ENOENT; + __set_errno(save); + goto lose; + } else { + size_t namlen = _D_EXACT_NAMLEN(d); + + if ((size_t) (pathp - path) <= namlen) { + if (size != 0) { + (void) __closedir(dirstream); + __set_errno(ERANGE); + goto lose; + } else { + char *tmp; + size_t oldsize = allocated; + + allocated = 2 * MAX(allocated, namlen); + tmp = realloc(path, allocated); + if (tmp == NULL) { + (void) __closedir(dirstream); + __set_errno(ENOMEM); /* closedir might have changed it. */ + goto lose; + } + + /* Move current contents up to the end of the buffer. + This is guaranteed to be non-overlapping. */ + pathp = + memcpy(tmp + allocated - + (path + oldsize - pathp), + tmp + (pathp - path), path + oldsize - pathp); + path = tmp; + } + } + pathp -= namlen; + (void) memcpy(pathp, d->d_name, namlen); + *--pathp = '/'; + (void) __closedir(dirstream); + } + + thisdev = dotdev; + thisino = dotino; + } + + if (pathp == &path[allocated - 1]) + *--pathp = '/'; + + if (dotlist != dots) + free((__ptr_t) dotlist); + + memmove(path, pathp, path + allocated - pathp); + + /* Restore errno on successful return. */ + __set_errno(prev_errno); + + return path; + +lose: + if (dotlist != dots) + free((__ptr_t) dotlist); +lose2: + if (buf == NULL) + free(path); + return NULL; +} + +GETCWD_RETURN_TYPE +egetcwd(buf, size) +char *buf; +size_t size; +{ + struct stat st; + char *tmpbuf; + + __set_errno(0); + tmpbuf = getcwd(buf, size); + + if (tmpbuf) { + __lstat(buf, &st); + } else { + return tmpbuf; + } + + if (errno) { + /* If lstat() failed with eerror = ENOENT, then its + * possible that we are running on an older kernel, + * so use our generic version which *should* not fail. + */ + if (errno == ENOENT) { + return __egetcwd(buf, size); + } else { + return tmpbuf; + } + } + + return tmpbuf; +} + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/libctest.c b/src/sandbox-1.1/libctest.c new file mode 100644 index 00000000..5365a20c --- /dev/null +++ b/src/sandbox-1.1/libctest.c @@ -0,0 +1,7 @@ +/* Dummy program to check your libc version */ + +int +main(void) +{ + return 0; +} diff --git a/src/sandbox-1.1/libsandbox.c b/src/sandbox-1.1/libsandbox.c new file mode 100644 index 00000000..be4efc4f --- /dev/null +++ b/src/sandbox-1.1/libsandbox.c @@ -0,0 +1,1383 @@ +/* + * Path sandbox for the gentoo linux portage package system, initially + * based on the ROCK Linux Wrapper for getting a list of created files + * + * to integrate with bash, bash should have been built like this + * + * ./configure --prefix= --host= --without-gnu-malloc + * + * it's very important that the --enable-static-link option is NOT specified + * + * Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com + * Distributed under the terms of the GNU General Public License, v2 or later + * Author : Geert Bevin + * + * Post Bevin leaving Gentoo ranks: + * -------------------------------- + * Ripped out all the wrappers, and implemented those of InstallWatch. + * Losts of cleanups and bugfixes. Implement a execve that forces $LIBSANDBOX + * in $LD_PRELOAD. Reformat the whole thing to look somewhat like the reworked + * sandbox.c from Brad House . + * + * Martin Schlemmer (18 Aug 2002) + * + * Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro , + * as some of the InstallWatch code was used. + * + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/libsandbox.c,v 1.22.2.3 2004/12/01 22:14:09 carpaski Exp $ + * + */ + +/* Uncomment below to enable wrapping of mknod(). + * This is broken currently. */ +/* #define WRAP_MKNOD 1 */ + +/* Uncomment below to enable the use of strtok_r(). */ +#define REENTRANT_STRTOK 1 + +/* Uncomment below to enable memory debugging. */ +/* #define SB_MEM_DEBUG 1 */ + +#define open xxx_open +#define open64 xxx_open64 + +/* Wrapping mknod, do not have any effect, and + * wrapping __xmknod causes calls to it to segfault + */ +#ifdef WRAP_MKNOD +# define __xmknod xxx___xmknod +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SB_MEM_DEBUG +# include +#endif + +#ifdef WRAP_MKNOD +# undef __xmknod +#endif + +#undef open +#undef open64 + +#include "localdecls.h" +#include "sandbox.h" + +/* Macros to check if a function should be executed */ +#define FUNCTION_SANDBOX_SAFE(func, path) \ + ((0 == is_sandbox_on()) || (1 == before_syscall(func, path))) + +#define FUNCTION_SANDBOX_SAFE_INT(func, path, flags) \ + ((0 == is_sandbox_on()) || (1 == before_syscall_open_int(func, path, flags))) + +#define FUNCTION_SANDBOX_SAFE_CHAR(func, path, mode) \ + ((0 == is_sandbox_on()) || (1 == before_syscall_open_char(func, path, mode))) + +/* Macro to check if a wrapper is defined, if not + * then try to resolve it again. */ +#define check_dlsym(name) \ +{ \ + int old_errno=errno; \ + if (!true_ ## name) true_ ## name=get_dlsym(#name); \ + errno=old_errno; \ +} + +/* Macro to check if we could canonicalize a path. It returns an integer on + * failure. */ +#define canonicalize_int(path, resolved_path) \ +{ \ + if (0 != canonicalize(path, resolved_path)) \ + return -1; \ +} + +/* Macro to check if we could canonicalize a path. It returns a NULL pointer on + * failure. */ +#define canonicalize_ptr(path, resolved_path) \ +{ \ + if (0 != canonicalize(path, resolved_path)) \ + return NULL; \ +} + +static char sandbox_lib[255]; +//static char sandbox_pids_file[255]; +static char *sandbox_pids_file; + +typedef struct { + int show_access_violation; + char **deny_prefixes; + int num_deny_prefixes; + char **read_prefixes; + int num_read_prefixes; + char **write_prefixes; + int num_write_prefixes; + char **predict_prefixes; + int num_predict_prefixes; + char **write_denied_prefixes; + int num_write_denied_prefixes; +} sbcontext_t; + +/* glibc modified realpath() functions */ +char *erealpath(const char *name, char *resolved); +/* glibc modified getcwd() functions */ +char *egetcwd(char *, size_t); + +static void init_wrappers(void); +static void *get_dlsym(const char *); +static int canonicalize(const char *, char *); +static int check_access(sbcontext_t *, const char *, const char *); +static int check_syscall(sbcontext_t *, const char *, const char *); +static int before_syscall(const char *, const char *); +static int before_syscall_open_int(const char *, const char *, int); +static int before_syscall_open_char(const char *, const char *, const char *); +static void clean_env_entries(char ***, int *); +static void init_context(sbcontext_t *); +static void init_env_entries(char ***, int *, char *, int); +static char *filter_path(const char *); +static int is_sandbox_on(); +static int is_sandbox_pid(); + +/* Wrapped functions */ + +extern int chmod(const char *, mode_t); +static int (*true_chmod) (const char *, mode_t); +extern int chown(const char *, uid_t, gid_t); +static int (*true_chown) (const char *, uid_t, gid_t); +extern int creat(const char *, mode_t); +static int (*true_creat) (const char *, mode_t); +extern FILE *fopen(const char *, const char *); +static FILE *(*true_fopen) (const char *, const char *); +extern int lchown(const char *, uid_t, gid_t); +static int (*true_lchown) (const char *, uid_t, gid_t); +extern int link(const char *, const char *); +static int (*true_link) (const char *, const char *); +extern int mkdir(const char *, mode_t); +static int (*true_mkdir) (const char *, mode_t); +extern DIR *opendir(const char *); +static DIR *(*true_opendir) (const char *); +#ifdef WRAP_MKNOD +extern int __xmknod(const char *, mode_t, dev_t); +static int (*true___xmknod) (const char *, mode_t, dev_t); +#endif +extern int open(const char *, int, ...); +static int (*true_open) (const char *, int, ...); +extern int rename(const char *, const char *); +static int (*true_rename) (const char *, const char *); +extern int rmdir(const char *); +static int (*true_rmdir) (const char *); +extern int symlink(const char *, const char *); +static int (*true_symlink) (const char *, const char *); +extern int truncate(const char *, TRUNCATE_T); +static int (*true_truncate) (const char *, TRUNCATE_T); +extern int unlink(const char *); +static int (*true_unlink) (const char *); + +#if (GLIBC_MINOR >= 1) + +extern int creat64(const char *, __mode_t); +static int (*true_creat64) (const char *, __mode_t); +extern FILE *fopen64(const char *, const char *); +static FILE *(*true_fopen64) (const char *, const char *); +extern int open64(const char *, int, ...); +static int (*true_open64) (const char *, int, ...); +extern int truncate64(const char *, __off64_t); +static int (*true_truncate64) (const char *, __off64_t); + +#endif + +extern int execve(const char *filename, char *const argv[], char *const envp[]); +static int (*true_execve) (const char *, char *const[], char *const[]); + +/* + * Initialize the shabang + */ + +static void +init_wrappers(void) +{ + void *libc_handle = NULL; + +#ifdef BROKEN_RTLD_NEXT +// printf ("RTLD_LAZY"); + libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); +#else +// printf ("RTLD_NEXT"); + libc_handle = RTLD_NEXT; +#endif + + true_chmod = dlsym(libc_handle, "chmod"); + true_chown = dlsym(libc_handle, "chown"); + true_creat = dlsym(libc_handle, "creat"); + true_fopen = dlsym(libc_handle, "fopen"); + true_lchown = dlsym(libc_handle, "lchown"); + true_link = dlsym(libc_handle, "link"); + true_mkdir = dlsym(libc_handle, "mkdir"); + true_opendir = dlsym(libc_handle, "opendir"); +#ifdef WRAP_MKNOD + true___xmknod = dlsym(libc_handle, "__xmknod"); +#endif + true_open = dlsym(libc_handle, "open"); + true_rename = dlsym(libc_handle, "rename"); + true_rmdir = dlsym(libc_handle, "rmdir"); + true_symlink = dlsym(libc_handle, "symlink"); + true_truncate = dlsym(libc_handle, "truncate"); + true_unlink = dlsym(libc_handle, "unlink"); + +#if (GLIBC_MINOR >= 1) + true_creat64 = dlsym(libc_handle, "creat64"); + true_fopen64 = dlsym(libc_handle, "fopen64"); + true_open64 = dlsym(libc_handle, "open64"); + true_truncate64 = dlsym(libc_handle, "truncate64"); +#endif + + true_execve = dlsym(libc_handle, "execve"); +} + +void +_fini(void) +{ + free(sandbox_pids_file); +} + +void +_init(void) +{ + int old_errno = errno; + char *tmp_string = NULL; + +#ifdef SB_MEM_DEBUG + mtrace(); +#endif + + init_wrappers(); + + /* Get the path and name to this library */ + tmp_string = get_sandbox_lib("/"); + strncpy(sandbox_lib, tmp_string, sizeof(sandbox_lib)-1); + if (tmp_string) + free(tmp_string); + tmp_string = NULL; + + /* Generate sandbox pids-file path */ + sandbox_pids_file = get_sandbox_pids_file(); + + errno = old_errno; +} + +static int +canonicalize(const char *path, char *resolved_path) +{ + int old_errno = errno; + char *retval; + + *resolved_path = '\0'; + + /* If path == NULL, return or we get a segfault */ + if (NULL == path) { + errno = EINVAL; + return -1; + } + + /* Do not try to resolve an empty path */ + if ('\0' == path[0]) { + errno = old_errno; + return 0; + } + + retval = erealpath(path, resolved_path); + + if ((!retval) && (path[0] != '/')) { + /* The path could not be canonicalized, append it + * to the current working directory if it was not + * an absolute path + */ + if (errno == ENAMETOOLONG) + return -1; + + egetcwd(resolved_path, SB_PATH_MAX - 2); + strcat(resolved_path, "/"); + strncat(resolved_path, path, SB_PATH_MAX - 1); + + if (!erealpath(resolved_path, resolved_path)) { + if (errno == ENAMETOOLONG) { + /* The resolved path is too long for the buffer to hold */ + return -1; + } else { + /* Whatever it resolved, is not a valid path */ + errno = ENOENT; + return -1; + } + } + + } else if ((!retval) && (path[0] == '/')) { + /* Whatever it resolved, is not a valid path */ + errno = ENOENT; + return -1; + } + + errno = old_errno; + return 0; +} + +static void * +get_dlsym(const char *symname) +{ + void *libc_handle = NULL; + void *symaddr = NULL; + +#ifdef BROKEN_RTLD_NEXT + libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); + if (!libc_handle) { + printf("libsandbox.so: Can't dlopen libc: %s\n", dlerror()); + abort(); + } +#else + libc_handle = RTLD_NEXT; +#endif + + symaddr = dlsym(libc_handle, symname); + if (!symaddr) { + printf("libsandbox.so: Can't resolve %s: %s\n", symname, dlerror()); + abort(); + } + + return symaddr; +} + +/* + * Wrapper Functions + */ + +int +chmod(const char *path, mode_t mode) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(path, canonic); + + if FUNCTION_SANDBOX_SAFE + ("chmod", canonic) { + check_dlsym(chmod); + result = true_chmod(path, mode); + } + + return result; +} + +int +chown(const char *path, uid_t owner, gid_t group) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(path, canonic); + + if FUNCTION_SANDBOX_SAFE + ("chown", canonic) { + check_dlsym(chown); + result = true_chown(path, owner, group); + } + + return result; +} + +int +creat(const char *pathname, mode_t mode) +{ +/* Is it a system call? */ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE + ("creat", canonic) { + check_dlsym(open); + result = true_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); + } + + return result; +} + +FILE * +fopen(const char *pathname, const char *mode) +{ + FILE *result = NULL; + char canonic[SB_PATH_MAX]; + + canonicalize_ptr(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_CHAR + ("fopen", canonic, mode) { + check_dlsym(fopen); + result = true_fopen(pathname, mode); + } + + return result; +} + +int +lchown(const char *path, uid_t owner, gid_t group) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(path, canonic); + + if FUNCTION_SANDBOX_SAFE + ("lchown", canonic) { + check_dlsym(lchown); + result = true_lchown(path, owner, group); + } + + return result; +} + +int +link(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; + + canonicalize_int(oldpath, old_canonic); + canonicalize_int(newpath, new_canonic); + + if FUNCTION_SANDBOX_SAFE + ("link", new_canonic) { + check_dlsym(link); + result = true_link(oldpath, newpath); + } + + return result; +} + +int +mkdir(const char *pathname, mode_t mode) +// returns 0 success, or -1 if an error occurred +{ + int result = -1, my_errno = errno; + char canonic[SB_PATH_MAX]; + struct stat st; + + canonicalize_int(pathname, canonic); + + /* Check if the directory exist, return EEXIST rather than failing */ + if (0 == lstat(canonic, &st)) { + errno = EEXIST; + return -1; + } + errno = my_errno; + + if FUNCTION_SANDBOX_SAFE + ("mkdir", canonic) { + check_dlsym(mkdir); + result = true_mkdir(pathname, mode); + } + + return result; +} + +DIR * +opendir(const char *name) +{ + DIR *result = NULL; + char canonic[SB_PATH_MAX]; + + canonicalize_ptr(name, canonic); + + if FUNCTION_SANDBOX_SAFE + ("opendir", canonic) { + check_dlsym(opendir); + result = true_opendir(name); + } + + return result; +} + +#ifdef WRAP_MKNOD + +int +__xmknod(const char *pathname, mode_t mode, dev_t dev) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE + ("__xmknod", canonic) { + check_dlsym(__xmknod); + result = true___xmknod(pathname, mode, dev); + } + + return result; +} + +#endif + +int +open(const char *pathname, int flags, ...) +{ +/* Eventually, there is a third parameter: it's mode_t mode */ + va_list ap; + mode_t mode = 0; + int result = -1; + char canonic[SB_PATH_MAX]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_INT + ("open", canonic, flags) { + /* We need to resolve open() realtime in some cases, + * else we get a segfault when running /bin/ps, etc + * in a sandbox */ + check_dlsym(open); + result = true_open(pathname, flags, mode); + } + + return result; +} + +int +rename(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; + + canonicalize_int(oldpath, old_canonic); + canonicalize_int(newpath, new_canonic); + + if (FUNCTION_SANDBOX_SAFE("rename", old_canonic) && + FUNCTION_SANDBOX_SAFE("rename", new_canonic)) { + check_dlsym(rename); + result = true_rename(oldpath, newpath); + } + + return result; +} + +int +rmdir(const char *pathname) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE + ("rmdir", canonic) { + check_dlsym(rmdir); + result = true_rmdir(pathname); + } + + return result; +} + +int +symlink(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[SB_PATH_MAX], new_canonic[SB_PATH_MAX]; + + canonicalize_int(oldpath, old_canonic); + canonicalize_int(newpath, new_canonic); + + if FUNCTION_SANDBOX_SAFE + ("symlink", new_canonic) { + check_dlsym(symlink); + result = true_symlink(oldpath, newpath); + } + + return result; +} + +int +truncate(const char *path, TRUNCATE_T length) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(path, canonic); + + if FUNCTION_SANDBOX_SAFE + ("truncate", canonic) { + check_dlsym(truncate); + result = true_truncate(path, length); + } + + return result; +} + +int +unlink(const char *pathname) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE + ("unlink", canonic) { + check_dlsym(unlink); + result = true_unlink(pathname); + } + + return result; +} + +#if (GLIBC_MINOR >= 1) + +int +creat64(const char *pathname, __mode_t mode) +{ +/* Is it a system call? */ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE + ("creat64", canonic) { + check_dlsym(open64); + result = true_open64(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); + } + + return result; +} + +FILE * +fopen64(const char *pathname, const char *mode) +{ + FILE *result = NULL; + char canonic[SB_PATH_MAX]; + + canonicalize_ptr(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_CHAR + ("fopen64", canonic, mode) { + check_dlsym(fopen64); + result = true_fopen(pathname, mode); + } + + return result; +} + +int +open64(const char *pathname, int flags, ...) +{ +/* Eventually, there is a third parameter: it's mode_t mode */ + va_list ap; + mode_t mode = 0; + int result = -1; + char canonic[SB_PATH_MAX]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize_int(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_INT + ("open64", canonic, flags) { + check_dlsym(open64); + result = true_open64(pathname, flags, mode); + } + + return result; +} + +int +truncate64(const char *path, __off64_t length) +{ + int result = -1; + char canonic[SB_PATH_MAX]; + + canonicalize_int(path, canonic); + + if FUNCTION_SANDBOX_SAFE + ("truncate64", canonic) { + check_dlsym(truncate64); + result = true_truncate64(path, length); + } + + return result; +} + +#endif /* GLIBC_MINOR >= 1 */ + +/* + * Exec Wrappers + */ + +int +execve(const char *filename, char *const argv[], char *const envp[]) +{ + int old_errno = errno; + int result = -1; + int count = 0; + int env_len = 0; + char canonic[SB_PATH_MAX]; + char **my_env = NULL; + int kill_env = 1; + /* We limit the size LD_PRELOAD can be here, but it should be enough */ + char tmp_str[4096]; + + canonicalize_int(filename, canonic); + + if FUNCTION_SANDBOX_SAFE + ("execve", canonic) { + while (envp[count] != NULL) { + if (strstr(envp[count], "LD_PRELOAD=") == envp[count]) { + if (NULL != strstr(envp[count], sandbox_lib)) { + my_env = (char **) envp; + kill_env = 0; + break; + } else { + int i = 0; + const int max_envp_len = + strlen(envp[count]) + strlen(sandbox_lib) + 1; + + /* Fail safe ... */ + if (max_envp_len > 4096) { + fprintf(stderr, "sandbox: max_envp_len too big!\n"); + errno = ENOMEM; + return result; + } + + /* Calculate envp size */ + my_env = (char **) envp; + do + env_len += 1; + while (*my_env++); + + my_env = (char **) malloc((env_len + 2) * sizeof (char *)); + if (NULL == my_env) { + errno = ENOMEM; + return result; + } + /* Copy envp to my_env */ + do + my_env[i] = envp[i]; + while (envp[i++]); + + /* Set tmp_str to envp[count] */ + strncpy(tmp_str, envp[count], max_envp_len - 1); + + /* LD_PRELOAD already have variables other than sandbox_lib, + * thus we have to add sandbox_lib seperated via a whitespace. */ + if (0 != strncmp(envp[count], "LD_PRELOAD=", max_envp_len - 1)) { + strncat(tmp_str, " ", max_envp_len - strlen(tmp_str)); + strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str)); + } else { + strncat(tmp_str, sandbox_lib, max_envp_len - strlen(tmp_str)); + } + + /* Valid string? */ + tmp_str[max_envp_len] = '\0'; + + /* Ok, replace my_env[count] with our version that contains + * sandbox_lib ... */ + my_env[count] = tmp_str; + + break; + } + } + count++; + } + + errno = old_errno; + check_dlsym(execve); + result = true_execve(filename, argv, my_env); + old_errno = errno; + + if (my_env && kill_env) { + free(my_env); + my_env = NULL; + } + } + + errno = old_errno; + + return result; +} + +/* + * Internal Functions + */ + +#if (GLIBC_MINOR == 1) + +/* This hack is needed for glibc 2.1.1 (and others?) + * (not really needed, but good example) */ +extern int fclose(FILE *); +static int (*true_fclose) (FILE *) = NULL; +int +fclose(FILE * file) +{ + int result = -1; + + check_dlsym(fclose); + result = true_fclose(file); + + return result; +} + +#endif /* GLIBC_MINOR == 1 */ + +static void +init_context(sbcontext_t * context) +{ + context->show_access_violation = 1; + context->deny_prefixes = NULL; + context->num_deny_prefixes = 0; + context->read_prefixes = NULL; + context->num_read_prefixes = 0; + context->write_prefixes = NULL; + context->num_write_prefixes = 0; + context->predict_prefixes = NULL; + context->num_predict_prefixes = 0; + context->write_denied_prefixes = NULL; + context->num_write_denied_prefixes = 0; +} + +static int +is_sandbox_pid() +{ + int old_errno = errno; + int result = 0; + FILE *pids_stream = NULL; + int pids_file = -1; + int current_pid = 0; + int tmp_pid = 0; + + init_wrappers(); + + pids_stream = true_fopen(sandbox_pids_file, "r"); + + if (NULL == pids_stream) { + perror(">>> pids file fopen"); + } else { + pids_file = fileno(pids_stream); + + if (pids_file < 0) { + perror(">>> pids file fileno"); + } else { + current_pid = getpid(); + + while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) { + if (tmp_pid == current_pid) { + result = 1; + break; + } + } + } + if (EOF == fclose(pids_stream)) { + perror(">>> pids file fclose"); + } + pids_stream = NULL; + pids_file = -1; + } + + errno = old_errno; + + return result; +} + +static void +clean_env_entries(char ***prefixes_array, int *prefixes_num) +{ + int old_errno = errno; + int i = 0; + + if (NULL != *prefixes_array) { + for (i = 0; i < *prefixes_num; i++) { + if (NULL != (*prefixes_array)[i]) { + free((*prefixes_array)[i]); + (*prefixes_array)[i] = NULL; + } + } + if (*prefixes_array) + free(*prefixes_array); + *prefixes_array = NULL; + *prefixes_num = 0; + } + + errno = old_errno; +} + +static void +init_env_entries(char ***prefixes_array, int *prefixes_num, char *env, int warn) +{ + int old_errno = errno; + char *prefixes_env = getenv(env); + + if (NULL == prefixes_env) { + fprintf(stderr, + "Sandbox error : the %s environmental variable should be defined.\n", + env); + } else { + char *buffer = NULL; + int prefixes_env_length = strlen(prefixes_env); + int i = 0; + int num_delimiters = 0; + char *token = NULL; + char *prefix = NULL; + + for (i = 0; i < prefixes_env_length; i++) { + if (':' == prefixes_env[i]) { + num_delimiters++; + } + } + + if (num_delimiters > 0) { + *prefixes_array = + (char **) malloc((num_delimiters + 1) * sizeof (char *)); + buffer = strndupa(prefixes_env, prefixes_env_length); + +#ifdef REENTRANT_STRTOK + token = strtok_r(buffer, ":", &buffer); +#else + token = strtok(buffer, ":"); +#endif + + while ((NULL != token) && (strlen(token) > 0)) { + prefix = strndup(token, strlen(token)); + (*prefixes_array)[(*prefixes_num)++] = filter_path(prefix); + +#ifdef REENTRANT_STRTOK + token = strtok_r(NULL, ":", &buffer); +#else + token = strtok(NULL, ":"); +#endif + + if (prefix) + free(prefix); + prefix = NULL; + } + } else if (prefixes_env_length > 0) { + (*prefixes_array) = (char **) malloc(sizeof (char *)); + + (*prefixes_array)[(*prefixes_num)++] = filter_path(prefixes_env); + } + } + + errno = old_errno; +} + +static char * +filter_path(const char *path) +{ + int old_errno = errno; + char *filtered_path = (char *) malloc(SB_PATH_MAX * sizeof (char)); + + canonicalize_ptr(path, filtered_path); + + errno = old_errno; + + return filtered_path; +} + +static int +check_access(sbcontext_t * sbcontext, const char *func, const char *path) +{ + int old_errno = errno; + int result = -1; + int i = 0; + char *filtered_path = filter_path(path); + + if ('/' != filtered_path[0]) { + errno = old_errno; + + if (filtered_path) + free(filtered_path); + filtered_path = NULL; + + return 0; + } + + if ((0 == strncmp(filtered_path, "/etc/ld.so.preload", 18)) + && (is_sandbox_pid())) { + result = 1; + } + + if (-1 == result) { + if (NULL != sbcontext->deny_prefixes) { + for (i = 0; i < sbcontext->num_deny_prefixes; i++) { + if (NULL != sbcontext->deny_prefixes[i]) { + if (0 == strncmp(filtered_path, + sbcontext-> + deny_prefixes[i], + strlen(sbcontext->deny_prefixes[i]))) { + result = 0; + break; + } + } + } + } + + if (-1 == result) { + if ((NULL != sbcontext->read_prefixes) && + ((0 == strncmp(func, "open_rd", 7)) || + (0 == strncmp(func, "popen", 5)) || + (0 == strncmp(func, "opendir", 7)) || + (0 == strncmp(func, "system", 6)) || + (0 == strncmp(func, "execl", 5)) || + (0 == strncmp(func, "execlp", 6)) || + (0 == strncmp(func, "execle", 6)) || + (0 == strncmp(func, "execv", 5)) || + (0 == strncmp(func, "execvp", 6)) || + (0 == strncmp(func, "execve", 6)) + ) + ) { + for (i = 0; i < sbcontext->num_read_prefixes; i++) { + if (NULL != sbcontext->read_prefixes[i]) { + if (0 == strncmp(filtered_path, + sbcontext-> + read_prefixes[i], + strlen(sbcontext->read_prefixes[i]))) { + result = 1; + break; + } + } + } + } else if ((NULL != sbcontext->write_prefixes) && + ((0 == strncmp(func, "open_wr", 7)) || + (0 == strncmp(func, "creat", 5)) || + (0 == strncmp(func, "creat64", 7)) || + (0 == strncmp(func, "mkdir", 5)) || + (0 == strncmp(func, "mknod", 5)) || + (0 == strncmp(func, "mkfifo", 6)) || + (0 == strncmp(func, "link", 4)) || + (0 == strncmp(func, "symlink", 7)) || + (0 == strncmp(func, "rename", 6)) || + (0 == strncmp(func, "utime", 5)) || + (0 == strncmp(func, "utimes", 6)) || + (0 == strncmp(func, "unlink", 6)) || + (0 == strncmp(func, "rmdir", 5)) || + (0 == strncmp(func, "chown", 5)) || + (0 == strncmp(func, "lchown", 6)) || + (0 == strncmp(func, "chmod", 5)) || + (0 == strncmp(func, "truncate", 8)) || + (0 == strncmp(func, "ftruncate", 9)) || + (0 == strncmp(func, "truncate64", 10)) || + (0 == strncmp(func, "ftruncate64", 11)) + ) + ) { + struct stat tmp_stat; + + for (i = 0; i < sbcontext->num_write_denied_prefixes; i++) { + if (NULL != sbcontext->write_denied_prefixes[i]) { + if (0 == + strncmp(filtered_path, + sbcontext-> + write_denied_prefixes + [i], strlen(sbcontext->write_denied_prefixes[i]))) { + result = 0; + break; + } + } + } + + if (-1 == result) { + for (i = 0; i < sbcontext->num_write_prefixes; i++) { + if (NULL != sbcontext->write_prefixes[i]) { + if (0 == + strncmp + (filtered_path, + sbcontext->write_prefixes[i], + strlen(sbcontext->write_prefixes[i]))) { + result = 1; + break; + } + } + } + + if (-1 == result) { + /* hack to prevent mkdir of existing dirs to show errors */ + if (0 == strncmp(func, "mkdir", 5)) { + if (0 == stat(filtered_path, &tmp_stat)) { + sbcontext->show_access_violation = 0; + result = 0; + } + } + + if (-1 == result) { + for (i = 0; i < sbcontext->num_predict_prefixes; i++) { + if (NULL != sbcontext->predict_prefixes[i]) { + if (0 == + strncmp + (filtered_path, + sbcontext-> + predict_prefixes[i], + strlen(sbcontext->predict_prefixes[i]))) { + sbcontext->show_access_violation = 0; + result = 0; + break; + } + } + } + } + } + } + } + } + } + + if (-1 == result) { + result = 0; + } + + if (filtered_path) + free(filtered_path); + filtered_path = NULL; + + errno = old_errno; + + return result; +} + +static int +check_syscall(sbcontext_t * sbcontext, const char *func, const char *file) +{ + int old_errno = errno; + int result = 1; + struct stat log_stat; + char *log_path = NULL; + char *absolute_path = NULL; + char *tmp_buffer = NULL; + int log_file = 0; + struct stat debug_log_stat; + char *debug_log_env = NULL; + char *debug_log_path = NULL; + int debug_log_file = 0; + char buffer[512]; + char *dpath = NULL; + + init_wrappers(); + + if ('/' == file[0]) { + absolute_path = (char *) malloc((strlen(file) + 1) * sizeof (char)); + sprintf(absolute_path, "%s", file); + } else { + tmp_buffer = (char *) malloc(SB_PATH_MAX * sizeof (char)); + egetcwd(tmp_buffer, SB_PATH_MAX - 1); + absolute_path = (char *) malloc((strlen(tmp_buffer) + 1 + strlen(file) + 1) * sizeof (char)); + sprintf(absolute_path, "%s/%s", tmp_buffer, file); + if (tmp_buffer) + free(tmp_buffer); + tmp_buffer = NULL; + } + + log_path = getenv("SANDBOX_LOG"); + debug_log_env = getenv("SANDBOX_DEBUG"); + debug_log_path = getenv("SANDBOX_DEBUG_LOG"); + + if (((NULL == log_path) || + (0 != strncmp(absolute_path, log_path, strlen(log_path)))) && + ((NULL == debug_log_env) || + (NULL == debug_log_path) || + (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path)))) + && (0 == check_access(sbcontext, func, absolute_path)) + ) { + if (1 == sbcontext->show_access_violation) { + fprintf(stderr, + "\e[31;01mACCESS DENIED\033[0m %s:%*s%s\n", + func, (int) (10 - strlen(func)), "", absolute_path); + + if (NULL != log_path) { + sprintf(buffer, "%s:%*s%s\n", func, (int) (10 - strlen(func)), "", + absolute_path); + // log_path somehow gets corrupted. figuring out why would be good. + dpath = strdup(log_path); + if ((0 == lstat(log_path, &log_stat)) + && (0 == S_ISREG(log_stat.st_mode)) + ) { + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", + dpath); + } else if (0 == check_access(sbcontext, "open_wr", dpath)) { + unsetenv("SANDBOX_LOG"); + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m SANDBOX_LOG %s isn't allowed via SANDBOX_WRITE\n", + dpath); + } else { + log_file = true_open(dpath, + O_APPEND | O_WRONLY + | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (log_file >= 0) { + write(log_file, buffer, strlen(buffer)); + close(log_file); + } + } + free(dpath); + } + } + + result = 0; + } else if (NULL != debug_log_env) { + if (NULL != debug_log_path) { + if (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path))) { + sprintf(buffer, "%s:%*s%s\n", func, (int) (10 - strlen(func)), "", + absolute_path); + //debug_log_path somehow gets corupted, same thing as log_path above. + dpath = strdup(debug_log_path); + if ((0 == lstat(debug_log_path, &debug_log_stat)) + && (0 == S_ISREG(debug_log_stat.st_mode)) + ) { + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", + debug_log_path); + } else if (0 == check_access(sbcontext, "open_wr", dpath)) { + unsetenv("SANDBOX_DEBUG"); + unsetenv("SANDBOX_DEBUG_LOG"); + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m SANDBOX_DEBUG_LOG %s isn't allowed by SANDBOX_WRITE.\n", + dpath); + } else { + debug_log_file = + true_open(dpath, + O_APPEND | O_WRONLY | + O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (debug_log_file >= 0) { + write(debug_log_file, buffer, strlen(buffer)); + close(debug_log_file); + } + } + free(dpath); + } + } else { + fprintf(stderr, + "\e[32;01mACCESS ALLOWED\033[0m %s:%*s%s\n", + func, (int) (10 - strlen(func)), "", absolute_path); + } + } + + if (absolute_path) + free(absolute_path); + absolute_path = NULL; + + errno = old_errno; + + return result; +} + +static int +is_sandbox_on() +{ + int old_errno = errno; + + /* $SANDBOX_ACTIVE is an env variable that should ONLY + * be used internal by sandbox.c and libsanbox.c. External + * sources should NEVER set it, else the sandbox is enabled + * in some cases when run in parallel with another sandbox, + * but not even in the sandbox shell. + * + * Azarah (3 Aug 2002) + */ + if ((NULL != getenv("SANDBOX_ON")) && + (0 == strncmp(getenv("SANDBOX_ON"), "1", 1)) && + (NULL != getenv("SANDBOX_ACTIVE")) && + (0 == strncmp(getenv("SANDBOX_ACTIVE"), "armedandready", 13)) + ) { + errno = old_errno; + + return 1; + } else { + errno = old_errno; + + return 0; + } +} + +static int +before_syscall(const char *func, const char *file) +{ + int old_errno = errno; + int result = 1; + sbcontext_t sbcontext; + + if (!strlen(file)) { + /* The file/directory does not exist */ + errno = ENOENT; + return 0; + } + + init_context(&sbcontext); + + init_env_entries(&(sbcontext.deny_prefixes), + &(sbcontext.num_deny_prefixes), "SANDBOX_DENY", 1); + init_env_entries(&(sbcontext.read_prefixes), + &(sbcontext.num_read_prefixes), "SANDBOX_READ", 1); + init_env_entries(&(sbcontext.write_prefixes), + &(sbcontext.num_write_prefixes), "SANDBOX_WRITE", 1); + init_env_entries(&(sbcontext.predict_prefixes), + &(sbcontext.num_predict_prefixes), "SANDBOX_PREDICT", 1); + + result = check_syscall(&sbcontext, func, file); + + clean_env_entries(&(sbcontext.deny_prefixes), &(sbcontext.num_deny_prefixes)); + clean_env_entries(&(sbcontext.read_prefixes), &(sbcontext.num_read_prefixes)); + clean_env_entries(&(sbcontext.write_prefixes), + &(sbcontext.num_write_prefixes)); + clean_env_entries(&(sbcontext.predict_prefixes), + &(sbcontext.num_predict_prefixes)); + + errno = old_errno; + + if (0 == result) { + errno = EACCES; + } + + return result; +} + +static int +before_syscall_open_int(const char *func, const char *file, int flags) +{ + if ((flags & O_WRONLY) || (flags & O_RDWR)) { + return before_syscall("open_wr", file); + } else { + return before_syscall("open_rd", file); + } +} + +static int +before_syscall_open_char(const char *func, const char *file, const char *mode) +{ + if (*mode == 'r' && ((strcmp(mode, "r") == 0) || + /* The strspn accept args are known non-writable modifiers */ + (strlen(++mode) == strspn(mode, "xbtmc")))) { + return before_syscall("open_rd", file); + } else { + return before_syscall("open_wr", file); + } +} + +#include "getcwd.c" +#include "canonicalize.c" + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/sandbox.bashrc b/src/sandbox-1.1/sandbox.bashrc new file mode 100644 index 00000000..5986f726 --- /dev/null +++ b/src/sandbox-1.1/sandbox.bashrc @@ -0,0 +1,8 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/sandbox.bashrc,v 1.2.4.1 2004/10/22 16:53:30 carpaski Exp $ +source /etc/profile +export LD_PRELOAD="$SANDBOX_LIB" +alias make="make LD_PRELOAD=$SANDBOX_LIB" +alias su="su -c '/bin/bash -rcfile $SANDBOX_DIR/sandbox.bashrc'" diff --git a/src/sandbox-1.1/sandbox.c b/src/sandbox-1.1/sandbox.c new file mode 100644 index 00000000..f7a2a320 --- /dev/null +++ b/src/sandbox-1.1/sandbox.c @@ -0,0 +1,863 @@ +/* +** Path sandbox for the gentoo linux portage package system, initially +** based on the ROCK Linux Wrapper for getting a list of created files +** +** to integrate with bash, bash should have been built like this +** +** ./configure --prefix= --host= --without-gnu-malloc +** +** it's very important that the --enable-static-link option is NOT specified +** +** Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +** Distributed under the terms of the GNU General Public License, v2 or later +** Author : Geert Bevin +** $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/sandbox.c,v 1.20.2.1 2004/12/01 22:14:09 carpaski Exp $ +*/ + +/* #define _GNU_SOURCE */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sandbox.h" + +int preload_adaptable = 1; +int cleaned_up = 0; +int print_debug = 0; +int stop_called = 0; + +/* Read pids file, and load active pids into an array. Return number of pids in array */ +int +load_active_pids(int fd, int **pids) +{ + char *data = NULL; + char *ptr = NULL, *ptr2 = NULL; + int my_pid; + int num_pids = 0; + long len; + + pids[0] = NULL; + + len = file_length(fd); + + /* Allocate and zero datablock to read pids file */ + data = (char *) malloc((len + 1) * sizeof (char)); + memset(data, 0, len + 1); + + /* Start at beginning of file */ + lseek(fd, 0L, SEEK_SET); + + /* read entire file into a buffer */ + read(fd, data, len); + + ptr = data; + + /* Loop and read all pids */ + while (1) { + /* Find new line */ + ptr2 = strchr(ptr, '\n'); + if (ptr2 == NULL) + break; /* No more PIDs */ + + /* Clear the \n. And ptr should have a null-terminated decimal string */ + ptr2[0] = 0; + + my_pid = atoi(ptr); + + /* If the PID is still alive, add it to our array */ + if ((0 != my_pid) && (0 == kill(my_pid, 0))) { + pids[0] = (int *) realloc(pids[0], (num_pids + 1) * sizeof (int)); + pids[0][num_pids] = my_pid; + num_pids++; + } + + /* Put ptr past the NULL we just wrote */ + ptr = ptr2 + 1; + } + + if (data) + free(data); + data = NULL; + + return num_pids; +} + +/* Read ld.so.preload file, and loads dirs into an array. Return number of entries in array */ +int +load_preload_libs(int fd, char ***preloads) +{ + char *data = NULL; + char *ptr = NULL, *ptr2 = NULL; + int num_entries = 0; + long len; + + preloads[0] = NULL; + + len = file_length(fd); + + /* Allocate and zero datablock to read pids file */ + data = (char *) malloc((len + 1) * sizeof (char)); + memset(data, 0, len + 1); + + /* Start at beginning of file */ + lseek(fd, 0L, SEEK_SET); + + /* read entire file into a buffer */ + read(fd, data, len); + + ptr = data; + + /* Loop and read all pids */ + while (1) { + /* Find new line */ + ptr2 = strchr(ptr, '\n'); + + /* Clear the \n. And ptr should have a null-terminated decimal string + * Don't break from the loop though because the last line may not + * terminated with a \n + */ + if (NULL != ptr2) + ptr2[0] = 0; + + /* If listing does not match our libname, add it to the array */ + if ((strlen(ptr)) && (NULL == strstr(ptr, LIB_NAME))) { + preloads[0] = + (char **) realloc(preloads[0], (num_entries + 1) * sizeof (char **)); + preloads[0][num_entries] = strdup(ptr); + num_entries++; + } + + if (NULL == ptr2) + break; /* No more PIDs */ + + /* Put ptr past the NULL we just wrote */ + ptr = ptr2 + 1; + } + + if (data) + free(data); + data = NULL; + + return num_entries; +} + +void +cleanup() +{ + int i = 0; + int success = 1; + int pids_file = -1, num_of_pids = 0; + int *pids_array = NULL; + char pid_string[255]; + char *sandbox_pids_file; +#ifdef USE_LD_SO_PRELOAD + int preload_file = -1, num_of_preloads = 0; + char preload_entry[255]; + char **preload_array = NULL; +#endif + + /* Generate sandbox pids-file path */ + sandbox_pids_file = get_sandbox_pids_file(); + + /* Remove this sandbox's bash pid from the global pids + * file if it has rights to adapt the ld.so.preload file */ + if ((1 == preload_adaptable) && (0 == cleaned_up)) { + cleaned_up = 1; + success = 1; + + if (print_debug) + printf("Cleaning up pids file.\n"); + + /* Stat the PIDs file, make sure it exists and is a regular file */ + if (file_exist(sandbox_pids_file, 1) <= 0) { + fprintf(stderr, ">>> pids file is not a regular file"); + success = 0; + /* We should really not fail if the pidsfile is missing here, but + * rather just exit cleanly, as there is still some cleanup to do */ + return; + } + + pids_file = file_open(sandbox_pids_file, "r+", 1, 0664, "portage"); + if (-1 == pids_file) { + success = 0; + /* Nothing more to do here */ + return; + } + + /* Load "still active" pids into an array */ + num_of_pids = load_active_pids(pids_file, &pids_array); + //printf("pids: %d\r\n", num_of_pids); + +#ifdef USE_LD_SO_PRELOAD + /* clean the /etc/ld.so.preload file if no other sandbox + * processes are running anymore */ + if (1 == num_of_pids) { + success = 1; + + if (print_debug) + printf("Cleaning up /etc/ld.so.preload.\n"); + + preload_file = file_open("/etc/ld.so.preload", "r+", 1, 0644); + if (-1 != preload_file) { + /* Load all the preload libraries into an array */ + num_of_preloads = load_preload_libs(preload_file, &preload_array); + //printf("num preloads: %d\r\n", num_of_preloads); + /* Clear file */ + file_truncate(preload_file); + + /* store the other preload libraries back into the /etc/ld.so.preload file */ + if (num_of_preloads > 0) { + for (i = 0; i < num_of_preloads; i++) { + sprintf(preload_entry, "%s\n", preload_array[i]); + if (write + (preload_file, + preload_entry, + strlen(preload_entry)) != strlen(preload_entry)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + } + + /* Free memory used to store preload array */ + for (i = 0; i < num_of_preloads; i++) { + if (preload_array[i]) + free(preload_array[i]); + preload_array[i] = NULL; + } + if (preload_array) + free(preload_array); + preload_array = NULL; + + file_close(preload_file); + preload_file = -1; + } + } +#endif + + file_truncate(pids_file); + + /* if pids are still running, write only the running pids back to the file */ + if (num_of_pids > 1) { + for (i = 0; i < num_of_pids; i++) { + if (pids_array[i] != getpid()) { + sprintf(pid_string, "%d\n", pids_array[i]); + + if (write(pids_file, pid_string, strlen(pid_string)) != + strlen(pid_string)) { + perror(">>> pids file write"); + success = 0; + break; + } + } + } + + file_close(pids_file); + pids_file = -1; + } else { + + file_close(pids_file); + pids_file = -1; + + /* remove the pidsfile, as this was the last sandbox */ + unlink(sandbox_pids_file); + } + + if (pids_array != NULL) + free(pids_array); + pids_array = NULL; + } + + free(sandbox_pids_file); + if (0 == success) + return; +} + +void +stop(int signum) +{ + if (stop_called == 0) { + stop_called = 1; + printf("Caught signal %d in pid %d\r\n", signum, getpid()); + cleanup(); + } else { + fprintf(stderr, "Pid %d alreadly caught signal and is still cleaning up\n", getpid()); + } +} + +void +setenv_sandbox_write(char *home_dir, char *portage_tmp_dir, char *var_tmp_dir, + char *tmp_dir) +{ + char buf[1024]; + + /* bzero out entire buffer then append trailing 0 */ + memset(buf, 0, sizeof(buf)); + + if (!getenv(ENV_SANDBOX_WRITE)) { + /* these could go into make.globals later on */ + snprintf(buf, sizeof(buf), + "%s:%s/.gconfd/lock:%s/.bash_history:", \ + "/dev/zero:/dev/fd/:/dev/null:/dev/pts/:" \ + "/dev/vc/:/dev/tty:/tmp/:" \ + "/dev/shm/ngpt:/var/log/scrollkeeper.log:" \ + "/usr/tmp/conftest:/usr/lib/conftest:" \ + "/usr/lib32/conftest:/usr/lib64/conftest:" \ + "/usr/tmp/cf:/usr/lib/cf:/usr/lib32/cf:/usr/lib64/cf", + home_dir, home_dir); + + if (NULL == portage_tmp_dir) { + strncat(buf, tmp_dir, sizeof(buf)); + strncat(buf, ":", sizeof(buf)); + strncat(buf, var_tmp_dir, sizeof(buf)); + strncat(buf, ":/tmp/:/var/tmp/", sizeof(buf)); + } else { + strncat(buf, portage_tmp_dir, sizeof(buf)); + strncat(buf, ":", sizeof(buf)); + strncat(buf, tmp_dir, sizeof(buf)); + strncat(buf, ":", sizeof(buf)); + strncat(buf, var_tmp_dir, sizeof(buf)); + strncat(buf, ":/tmp/:/var/tmp/", sizeof(buf)); + } + buf[sizeof(buf) - 1] = '\0'; + setenv(ENV_SANDBOX_WRITE, buf, 1); + } +} + +void +setenv_sandbox_predict(char *home_dir) +{ + char buf[1024]; + + memset(buf, 0, sizeof(buf)); + + if (!getenv(ENV_SANDBOX_PREDICT)) { + /* these should go into make.globals later on */ + snprintf(buf, sizeof(buf), "%s/.:" \ + "/usr/lib/python2.0/:" \ + "/usr/lib/python2.1/:" \ + "/usr/lib/python2.2/:" \ + "/usr/lib/python2.3/:" \ + "/usr/lib/python2.4/:" \ + "/usr/lib/python2.5/:" \ + "/usr/lib/python3.0/:", + home_dir); + + buf[sizeof(buf) - 1] = '\0'; + setenv(ENV_SANDBOX_PREDICT, buf, 1); + } +} + +int +print_sandbox_log(char *sandbox_log) +{ + int sandbox_log_file = -1; + char *beep_count_env = NULL; + int i, color, beep_count = 0; + long len = 0; + char *buffer = NULL; + + sandbox_log_file = file_open(sandbox_log, "r", 1, 0664, "portage"); + if (-1 == sandbox_log_file) + return 0; + + len = file_length(sandbox_log_file); + buffer = (char *) malloc((len + 1) * sizeof (char)); + memset(buffer, 0, len + 1); + read(sandbox_log_file, buffer, len); + file_close(sandbox_log_file); + + color = ( (getenv("NOCOLOR") != NULL) ? 0 : 1); + + if (color) printf("\e[31;01m"); + printf("--------------------------- ACCESS VIOLATION SUMMARY ---------------------------"); + if (color) printf("\033[0m"); + if (color) printf("\e[31;01m"); + printf("\nLOG FILE = \"%s\"", sandbox_log); + if (color) printf("\033[0m"); + printf("\n\n"); + printf("%s", buffer); + if (buffer) + free(buffer); + buffer = NULL; + printf + ("\e[31;01m--------------------------------------------------------------------------------\033[0m\n"); + + beep_count_env = getenv(ENV_SANDBOX_BEEP); + if (beep_count_env) + beep_count = atoi(beep_count_env); + else + beep_count = DEFAULT_BEEP_COUNT; + + for (i = 0; i < beep_count; i++) { + fputc('\a', stderr); + if (i < beep_count - 1) + sleep(1); + } + return 1; +} + +int +spawn_shell(char *argv_bash[]) +{ +#ifdef USE_SYSTEM_SHELL + int i = 0; + char *sh = NULL; + int first = 1; + int ret; + long len = 0; + + while (1) { + if (NULL == argv_bash[i]) + break; + if (NULL != sh) + len = strlen(sh); + sh = (char *) realloc(sh, len + strlen(argv_bash[i]) + 5); + if (first) { + sh[0] = 0; + first = 0; + } + strcat(sh, "\""); + strcat(sh, argv_bash[i]); + strcat(sh, "\" "); + + //printf("%s\n", argv_bash[i]); + i++; + } + printf("%s\n", sh); + ret = system(sh); + if (sh) + free(sh); + sh = NULL; + + if (-1 == ret) + return 0; + return 1; + +#else +# ifndef NO_FORK + int pid; + int status = 0; + int ret = 0; + + pid = fork(); + + /* Child's process */ + if (0 == pid) { +# endif + execv(argv_bash[0], argv_bash); +# ifndef NO_FORK + return 0; + } else if (pid < 0) { + return 0; + } + ret = waitpid(pid, &status, 0); + if ((-1 == ret) || (status > 0)) + return 0; +# endif + return 1; +#endif +} + +int +main(int argc, char **argv) +{ + int i = 0, success = 1; +#ifdef USE_LD_SO_PRELOAD + int preload_file = -1; +#endif + int sandbox_log_presence = 0; + int sandbox_log_file = -1; + int pids_file = -1; + long len; + + int *pids_array = NULL; + int num_of_pids = 0; + + // char run_arg[255]; + char portage_tmp_dir[PATH_MAX]; + char var_tmp_dir[PATH_MAX]; + char tmp_dir[PATH_MAX]; + char sandbox_log[255]; + char sandbox_debug_log[255]; + char sandbox_dir[255]; + char sandbox_lib[255]; + char *sandbox_pids_file; + char sandbox_rc[255]; + char pid_string[255]; + char **argv_bash = NULL; + + char *run_str = "-c"; + char *home_dir = NULL; + char *tmp_string = NULL; +#ifdef USE_LD_SO_PRELOAD + char **preload_array = NULL; + int num_of_preloads = 0; +#endif + + /* Only print info if called with no arguments .... */ + if (argc < 2) + print_debug = 1; + + if (print_debug) + printf + ("========================== Gentoo linux path sandbox ===========================\n"); + + /* check if a sandbox is already running */ + if (NULL != getenv(ENV_SANDBOX_ON)) { + fprintf(stderr, + "Not launching a new sandbox instance\nAnother one is already running in this process hierarchy.\n"); + exit(1); + } else { + + /* determine the location of all the sandbox support files */ + if (print_debug) + printf("Detection of the support files.\n"); + + /* Generate base sandbox path */ + tmp_string = get_sandbox_path(argv[0]); + strncpy(sandbox_dir, tmp_string, 254); + if (tmp_string) + free(tmp_string); + tmp_string = NULL; + strcat(sandbox_dir, "/"); + + /* Generate sandbox lib path */ + tmp_string = get_sandbox_lib(sandbox_dir); + strncpy(sandbox_lib, tmp_string, 254); + if (tmp_string) + free(tmp_string); + tmp_string = NULL; + + /* Generate sandbox pids-file path */ + sandbox_pids_file = get_sandbox_pids_file(); + + /* Generate sandbox bashrc path */ + tmp_string = get_sandbox_rc(sandbox_dir); + strncpy(sandbox_rc, tmp_string, 254); + if (tmp_string) + free(tmp_string); + tmp_string = NULL; + + /* verify the existance of required files */ + if (print_debug) + printf("Verification of the required files.\n"); + +#ifndef SB_HAVE_64BIT_ARCH + if (file_exist(sandbox_lib, 0) <= 0) { + fprintf(stderr, "Could not open the sandbox library at '%s'.\n", + sandbox_lib); + return -1; + } +#endif + if (file_exist(sandbox_rc, 0) <= 0) { + fprintf(stderr, "Could not open the sandbox rc file at '%s'.\n", + sandbox_rc); + return -1; + } +#ifdef USE_LD_SO_PRELOAD + /* ensure that the /etc/ld.so.preload file contains an entry for the sandbox lib */ + if (print_debug) + printf("Setting up the ld.so.preload file.\n"); + + /* check if the /etc/ld.so.preload is a regular file */ + if (file_exist("/etc/ld.so.preload", 1) < 0) { + fprintf(stderr, ">>> /etc/ld.so.preload file is not a regular file\n"); + exit(1); + } + + if (getuid() == 0) { + /* Our r+ also will create the file if it doesn't exist */ + preload_file = file_open("/etc/ld.so.preload", "r+", 1, 0644); + if (-1 == preload_file) { + preload_adaptable = 0; +/* exit(1);*/ + } + } else { + /* Avoid permissions warnings if we're not root */ + preload_adaptable = 0; + } + + /* Only update /etc/ld.so.preload if we can write to it ... */ + if (1 == preload_adaptable) { + /* Load entries of preload table */ + num_of_preloads = load_preload_libs(preload_file, &preload_array); + + /* Zero out our ld.so.preload file */ + file_truncate(preload_file); + + /* Write contents of preload file */ + for (i = 0; i < num_of_preloads + 1; i++) { + /* First entry should be our sandbox library */ + if (0 == i) { + if (write + (preload_file, sandbox_lib, + strlen(sandbox_lib)) != strlen(sandbox_lib)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } else { + /* Output all other preload entries */ + if (write + (preload_file, preload_array[i - 1], + strlen(preload_array[i - 1])) != strlen(preload_array[i - 1])) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + /* Don't forget the return character after each line! */ + if (1 != write(preload_file, "\n", 1)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + + for (i = 0; i < num_of_preloads; i++) { + if (preload_array[i]) + free(preload_array[i]); + preload_array[i] = NULL; + } + if (preload_array) + free(preload_array); + num_of_preloads = 0; + preload_array = NULL; + } + + /* That's all we needed to do with the preload file */ + if (0 < preload_file) + file_close(preload_file); + preload_file = -1; +#endif + + /* set up the required environment variables */ + if (print_debug) + printf("Setting up the required environment variables.\n"); + + /* Generate sandbox log full path */ + tmp_string = get_sandbox_log(); + strncpy(sandbox_log, tmp_string, 254); + if (tmp_string) + free(tmp_string); + tmp_string = NULL; + + setenv(ENV_SANDBOX_LOG, sandbox_log, 1); + + snprintf(sandbox_debug_log, sizeof(sandbox_debug_log), "%s%s%s", + DEBUG_LOG_FILE_PREFIX, pid_string, LOG_FILE_EXT); + setenv(ENV_SANDBOX_DEBUG_LOG, sandbox_debug_log, 1); + + home_dir = getenv("HOME"); + if (!home_dir) { + home_dir = "/tmp"; + setenv("HOME", home_dir, 1); + } + + /* drobbins: we need to expand these paths using realpath() so that PORTAGE_TMPDIR + * can contain symlinks (example, /var is a symlink, /var/tmp is a symlink.) Without + * this, access is denied to /var/tmp, hurtin' ebuilds. + */ + + { char *e; + e = getenv("PORTAGE_TMPDIR"); + if ( e && ( strlen(e) < sizeof(portage_tmp_dir)-1 ) && (strlen(e) > 1) ) + realpath(e, portage_tmp_dir); + + } + realpath("/var/tmp", var_tmp_dir); + realpath("/tmp", tmp_dir); + + setenv(ENV_SANDBOX_DIR, sandbox_dir, 1); + setenv(ENV_SANDBOX_LIB, sandbox_lib, 1); + setenv("LD_PRELOAD", sandbox_lib, 1); + + if (!getenv(ENV_SANDBOX_DENY)) + setenv(ENV_SANDBOX_DENY, LD_PRELOAD_FILE, 1); + + if (!getenv(ENV_SANDBOX_READ)) + setenv(ENV_SANDBOX_READ, "/", 1); + + /* Set up Sandbox Write path */ + setenv_sandbox_write(home_dir, portage_tmp_dir, var_tmp_dir, tmp_dir); + setenv_sandbox_predict(home_dir); + + setenv(ENV_SANDBOX_ON, "1", 0); + + /* if the portage temp dir was present, cd into it */ + if (NULL != portage_tmp_dir) + chdir(portage_tmp_dir); + + argv_bash = (char **) malloc(6 * sizeof (char *)); + argv_bash[0] = strdup("/bin/bash"); + argv_bash[1] = strdup("-rcfile"); + argv_bash[2] = strdup(sandbox_rc); + + if (argc < 2) + argv_bash[3] = NULL; + else + argv_bash[3] = strdup(run_str); /* "-c" */ + + argv_bash[4] = NULL; /* strdup(run_arg); */ + argv_bash[5] = NULL; + + if (argc >= 2) { + for (i = 1; i < argc; i++) { + if (NULL == argv_bash[4]) + len = 0; + else + len = strlen(argv_bash[4]); + + argv_bash[4] = + (char *) realloc(argv_bash[4], + (len + strlen(argv[i]) + 2) * sizeof (char)); + + if (0 == len) + argv_bash[4][0] = 0; + if (1 != i) + strcat(argv_bash[4], " "); + + strcat(argv_bash[4], argv[i]); + } + } + + /* set up the required signal handlers */ + signal(SIGHUP, &stop); + signal(SIGINT, &stop); + signal(SIGQUIT, &stop); + signal(SIGTERM, &stop); + + /* this one should NEVER be set in ebuilds, as it is the one + * private thing libsandbox.so use to test if the sandbox + * should be active for this pid, or not. + * + * azarah (3 Aug 2002) + */ + + setenv("SANDBOX_ACTIVE", "armedandready", 1); + + /* Load our PID into PIDs file */ + success = 1; + if (file_exist(sandbox_pids_file, 1) < 0) { + success = 0; + fprintf(stderr, ">>> %s is not a regular file\n", sandbox_pids_file); + } else { + pids_file = file_open(sandbox_pids_file, "r+", 1, 0664, "portage"); + if (-1 == pids_file) + success = 0; + } + if (1 == success) { + /* Grab still active pids */ + num_of_pids = load_active_pids(pids_file, &pids_array); + + /* Zero out file */ + file_truncate(pids_file); + + /* Output active pids, and append our pid */ + for (i = 0; i < num_of_pids + 1; i++) { + /* Time for our entry */ + if (i == num_of_pids) + sprintf(pid_string, "%d\n", getpid()); + else + sprintf(pid_string, "%d\n", pids_array[i]); + + if (write(pids_file, pid_string, strlen(pid_string)) != + strlen(pid_string)) { + perror(">>> pids file write"); + success = 0; + break; + } + } + /* Clean pids_array */ + if (pids_array) + free(pids_array); + pids_array = NULL; + num_of_pids = 0; + + /* We're done with the pids file */ + file_close(pids_file); + } + + /* Something went wrong, bail out */ + if (0 == success) { + perror(">>> pids file write"); + exit(1); + } + + /* STARTING PROTECTED ENVIRONMENT */ + if (print_debug) { + printf("The protected environment has been started.\n"); + printf + ("--------------------------------------------------------------------------------\n"); + } + + if (print_debug) + printf("Shell being started in forked process.\n"); + + /* Start Bash */ + if (!spawn_shell(argv_bash)) { + if (print_debug) + fprintf(stderr, ">>> shell process failed to spawn\n"); + success = 0; + } + + /* Free bash stuff */ + for (i = 0; i < 6; i++) { + if (argv_bash[i]) + free(argv_bash[i]); + argv_bash[i] = NULL; + } + if (argv_bash) + free(argv_bash); + argv_bash = NULL; + + if (print_debug) + printf("Cleaning up sandbox process\n"); + + cleanup(); + + if (print_debug) { + printf + ("========================== Gentoo linux path sandbox ===========================\n"); + printf("The protected environment has been shut down.\n"); + } + + if (file_exist(sandbox_log, 0)) { + sandbox_log_presence = 1; + success = 1; + if (!print_sandbox_log(sandbox_log)) + success = 0; + +#if 0 + if (!success) + exit(1); +#endif + + sandbox_log_file = -1; + } else if (print_debug) { + printf + ("--------------------------------------------------------------------------------\n"); + } + + if ((sandbox_log_presence) || (!success)) + return 1; + else + return 0; + } +} + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/sandbox.h b/src/sandbox-1.1/sandbox.h new file mode 100644 index 00000000..67a415dd --- /dev/null +++ b/src/sandbox-1.1/sandbox.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2002 Brad House , + * Possibly based on code from Geert Bevin, Uwyn, http://www.uwyn.com + * Distributed under the terms of the GNU General Public License, v2 or later + * Author: Brad House + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/sandbox.h,v 1.5.2.1 2004/10/22 16:53:30 carpaski Exp $ + */ + +#ifndef __SANDBOX_H__ +#define __SANDBOX_H__ + +/* Uncomment below to use flock instead of fcntl (POSIX way) to lock/unlock files */ +/* #define USE_FLOCK */ + +/* Uncomment below to use system() to execute the shell rather than execv */ +/* #define USE_SYSTEM_SHELL */ + +/* Uncomment below to use /etc/ld.so.preload (could be very intrusive!!) */ +/* #define USE_LD_SO_PRELOAD */ + +/* Uncommend to not have the protected shell forked, just run in parent process */ +/* ONLY FOR DEBUGGING PURPOSES!! (strace needs it like that) */ +/* #define NO_FORK */ + +#define LD_PRELOAD_FILE "/etc/ld.so.preload" +#define LIB_NAME "libsandbox.so" +#define BASHRC_NAME "sandbox.bashrc" +#define PIDS_FILE "/tmp/sandboxpids.tmp" +#define LOG_FILE_PREFIX "/tmp/sandbox-" +#define DEBUG_LOG_FILE_PREFIX "/tmp/sandbox-debug-" +#define LOG_FILE_EXT ".log" + +#define ENV_SANDBOX_DEBUG_LOG "SANDBOX_DEBUG_LOG" +#define ENV_SANDBOX_LOG "SANDBOX_LOG" +#define ENV_SANDBOX_DIR "SANDBOX_DIR" +#define ENV_SANDBOX_LIB "SANDBOX_LIB" + +#define ENV_SANDBOX_DENY "SANDBOX_DENY" +#define ENV_SANDBOX_READ "SANDBOX_READ" +#define ENV_SANDBOX_WRITE "SANDBOX_WRITE" +#define ENV_SANDBOX_PREDICT "SANDBOX_PREDICT" + +#define ENV_SANDBOX_ON "SANDBOX_ON" +#define ENV_SANDBOX_BEEP "SANDBOX_BEEP" + +#define DEFAULT_BEEP_COUNT 3 + +char *get_sandbox_path(char *argv0); +char *get_sandbox_lib(char *sb_path); +char *get_sandbox_pids_file(void); +char *get_sandbox_rc(char *sb_path); +char *get_sandbox_log(); +char *sb_dirname(const char *path); +int file_getmode(char *mode); +long file_tell(int fp); +int file_lock(int fd, int lock, char *filename); +int file_unlock(int fd); +int file_locktype(char *mode); +int file_open(char *filename, char *mode, int perm_specified, ...); +void file_close(int fd); +long file_length(int fd); +int file_truncate(int fd); +int file_exist(char *filename, int checkmode); + +#endif + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-1.1/sandbox_futils.c b/src/sandbox-1.1/sandbox_futils.c new file mode 100644 index 00000000..f9ddb342 --- /dev/null +++ b/src/sandbox-1.1/sandbox_futils.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2002 Brad House + * Distributed under the terms of the GNU General Public License, v2 or later + * Author: Brad House + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-1.1/Attic/sandbox_futils.c,v 1.11.2.1 2004/11/03 13:12:55 ferringb Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "sandbox.h" + +/* BEGIN Prototypes */ +int file_security_check(char *filename); +/* END Prototypes */ + + +/* glibc modified getcwd() functions */ +char *egetcwd(char *, size_t); + +char * +get_sandbox_path(char *argv0) +{ + char path[255]; + char *cwd = NULL; + + memset(path, 0, sizeof(path)); + /* ARGV[0] specifies full path */ + if (argv0[0] == '/') { + strncpy(path, argv0, sizeof(path)-1); + + /* ARGV[0] specifies relative path */ + } else { + egetcwd(cwd, sizeof(path)-2); + snprintf(path, sizeof(path), "%s/%s", cwd, argv0); + if (cwd) + free(cwd); + cwd = NULL; + } + + /* Return just directory */ + return (sb_dirname(path)); +} + +char * +get_sandbox_lib(char *sb_path) +{ + char path[255]; + +#ifdef SB_HAVE_64BIT_ARCH + snprintf(path, sizeof(path), "%s", LIB_NAME); +#else + snprintf(path, sizeof(path), "/lib/%s", LIB_NAME); + if (file_exist(path, 0) <= 0) { + snprintf(path, sizeof(path), "%s%s", sb_path, LIB_NAME); + } +#endif + return (strdup(path)); +} + +char * +get_sandbox_pids_file(void) +{ + if (0 < getenv("SANDBOX_PIDS_FILE")) { + return (strdup(getenv("SANDBOX_PIDS_FILE"))); + } + return (strdup(PIDS_FILE)); +} + +char * +get_sandbox_rc(char *sb_path) +{ + char path[255]; + + snprintf(path, sizeof(path), "/usr/lib/portage/lib/%s", BASHRC_NAME); + if (file_exist(path, 0) <= 0) { + snprintf(path, sizeof(path), "%s%s", sb_path, BASHRC_NAME); + } + return (strdup(path)); +} + +char * +get_sandbox_log() +{ + char path[255]; + char *sandbox_log_env = NULL; + + /* THIS CHUNK BREAK THINGS BY DOING THIS: + * SANDBOX_LOG=/tmp/sandbox-app-admin/superadduser-1.0.7-11063.log + */ + + sandbox_log_env = getenv(ENV_SANDBOX_LOG); + snprintf(path, sizeof(path)-1, "%s%s%s%d%s", LOG_FILE_PREFIX, + ( sandbox_log_env == NULL ? "" : sandbox_log_env ), + ( sandbox_log_env == NULL ? "" : "-" ), + getpid(), LOG_FILE_EXT); + return (strdup(path)); +} + +/* Obtain base directory name. Do not allow trailing / */ +char * +sb_dirname(const char *path) +{ + char *ret = NULL; + char *ptr = NULL; + int loc = 0, i; + int cut_len = -1; + + /* don't think NULL will ever be passed, but just in case */ + if (NULL == path) + return (strdup(".")); + + /* Grab pointer to last slash */ + ptr = strrchr(path, '/'); + if (NULL == ptr) { + return (strdup(".")); + } + + /* decimal location of pointer */ + loc = ptr - path; + + /* Remove any trailing slash */ + for (i = loc - 1; i >= 0; i--) { + if (path[i] != '/') { + cut_len = i + 1; /* make cut_len the length of the string to keep */ + break; + } + } + + /* It could have been just a plain /, return a 1byte 0 filled string */ + if (-1 == cut_len) + return (strdup("")); + + /* Allocate memory, and return the directory */ + ret = (char *) malloc((cut_len + 1) * sizeof (char)); + memcpy(ret, path, cut_len); + ret[cut_len] = 0; + + return (ret); +} + +/* +char* dirname(const char* path) +{ + char* base = NULL; + unsigned int length = 0; + + base = strrchr(path, '/'); + if (NULL == base) + { + return strdup("."); + } + while (base > path && *base == '/') + { + base--; + } + length = (unsigned int) 1 + base - path; + + base = malloc(sizeof(char)*(length+1)); + memmove(base, path, length); + base[length] = 0; + + return base; +}*/ + +/* Convert text (string) modes to integer values */ +int +file_getmode(char *mode) +{ + int mde = 0; + if (0 == strcasecmp(mode, "r+")) { + mde = O_RDWR | O_CREAT; + } else if (0 == strcasecmp(mode, "w+")) { + mde = O_RDWR | O_CREAT | O_TRUNC; + } else if (0 == strcasecmp(mode, "a+")) { + mde = O_RDWR | O_CREAT | O_APPEND; + } else if (0 == strcasecmp(mode, "r")) { + mde = O_RDONLY; + } else if (0 == strcasecmp(mode, "w")) { + mde = O_WRONLY | O_CREAT | O_TRUNC; + } else if (0 == strcasecmp(mode, "a")) { + mde = O_WRONLY | O_APPEND | O_CREAT; + } else { + mde = O_RDONLY; + } + return (mde); +} + +/* Get current position in file */ +long +file_tell(int fp) +{ + return (lseek(fp, 0L, SEEK_CUR)); +} + +/* lock the file, preferrably the POSIX way */ +int +file_lock(int fd, int lock, char *filename) +{ + int err; +#ifdef USE_FLOCK + if (flock(fd, lock) < 0) { + err = errno; + fprintf(stderr, ">>> %s flock file lock: %s\n", filename, strerror(err)); + return 0; + } +#else + struct flock fl; + fl.l_type = lock; + fl.l_whence = SEEK_SET; + fl.l_start = 0L; + fl.l_len = 0L; + fl.l_pid = getpid(); + if (fcntl(fd, F_SETLKW, &fl) < 0) { + err = errno; + fprintf(stderr, ">>> %s fcntl file lock: %s\n", filename, strerror(err)); + return 0; + } +#endif + return 1; +} + +/* unlock the file, preferrably the POSIX way */ +int +file_unlock(int fd) +{ +#ifdef USE_FLOCK + if (flock(fd, LOCK_UN) < 0) { + perror(">>> flock file unlock"); + return 0; + } +#else + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0L; + fl.l_len = 0L; + fl.l_pid = getpid(); + if (fcntl(fd, F_SETLKW, &fl) < 0) { + perror(">>> fcntl file unlock"); + return 0; + } +#endif + return 1; +} + +/* Auto-determine from how the file was opened, what kind of lock to lock + * the file with + */ +int +file_locktype(char *mode) +{ +#ifdef USE_FLOCK + if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) + || (NULL != strchr(mode, 'a'))) + return (LOCK_EX); + return (LOCK_SH); +#else + if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) + || (NULL != strchr(mode, 'a'))) + return (F_WRLCK); + return (F_RDLCK); +#endif +} + +/* Use standard fopen style modes to open the specified file. Also auto-determines and + * locks the file either in shared or exclusive mode depending on opening mode + */ +int +file_open(char *filename, char *mode, int perm_specified, ...) +{ + int fd; + char error[250]; + va_list ap; + int perm; + char *group = NULL; + struct group *group_struct; + + file_security_check(filename); + + if (perm_specified) { + va_start(ap, perm_specified); + perm = va_arg(ap, int); + group = va_arg(ap, char *); + va_end(ap); + } + fd = open(filename, file_getmode(mode)); + file_security_check(filename); + if (-1 == fd) { + snprintf(error, sizeof(error), ">>> %s file mode: %s open", filename, mode); + perror(error); + return (fd); + } + if (perm_specified) { + if (fchmod(fd, 0664) && (0 == getuid())) { + snprintf(error, sizeof(error), ">>> Could not set mode: %s", filename); + perror(error); + } + } + if (NULL != group) { + group_struct = getgrnam(group); + if (NULL == group) { + snprintf(error, sizeof(error), ">>> Could not get grp number: %s", group); + perror(error); + } else { + if (fchown(fd, -1, group_struct->gr_gid) && (0 == getuid())) { + snprintf(error, sizeof(error), ">>> Could not set group: %s", filename); + perror(error); + } + } + } + /* Only lock the file if opening succeeded */ + if (-1 != fd) { + if(file_security_check(filename) != 0) { + /* Security violation occured between the last check and the */ + /* creation of the file. As SpanKY pointed out there is a race */ + /* condition here, so if there is a problem here we'll mesg and */ + /* bail out to avoid it until we can work and test a better fix. */ + fprintf(stderr, "\n\nSECURITY RACE CONDITION: Problem recurred after creation!\nBAILING OUT\n\n"); + exit(127); + } + + if (0 == file_lock(fd, file_locktype(mode), filename)) { + close(fd); + return -1; + } + } else { + snprintf(error, sizeof(error), ">>> %s file mode:%s open", filename, mode); + perror(error); + } + return (fd); +} + +/* Close and unlock file */ +void +file_close(int fd) +{ + if (-1 != fd) { + file_unlock(fd); + close(fd); + } +} + +/* Return length of file */ +long +file_length(int fd) +{ + long pos, len; + pos = file_tell(fd); + len = lseek(fd, 0L, SEEK_END); + lseek(fd, pos, SEEK_SET); + return (len); +} + +/* Zero out file */ +int +file_truncate(int fd) +{ + lseek(fd, 0L, SEEK_SET); + if (ftruncate(fd, 0) < 0) { + perror(">>> file truncate"); + return 0; + } + return 1; +} + +/* Check to see if a file exists Return: 1 success, 0 file not found, -1 error */ +int +file_exist(char *filename, int checkmode) +{ + struct stat mystat; + + /* Verify file exists and is regular file (not sym link) */ + if (checkmode) { + if (-1 == lstat(filename, &mystat)) { + /* file doesn't exist */ + if (ENOENT == errno) { + return 0; + } else { /* permission denied or other error */ + perror(">>> stat file"); + return -1; + } + } + if (!S_ISREG(mystat.st_mode)) + return -1; + + /* Just plain verify the file exists */ + } else { + if (-1 == stat(filename, &mystat)) { + /* file does not exist */ + if (ENOENT == errno) { + return 0; + } else { /* permission denied or other error */ + perror(">>> stat file"); + return -1; + } + } + } + + return 1; +} + +int file_security_check(char *filename) { /* 0 == fine, >0 == problem */ + struct stat stat_buf; + struct group *group_buf; + struct passwd *passwd_buf; + + passwd_buf = getpwnam("portage"); + group_buf = getgrnam("portage"); + + if((lstat(filename, &stat_buf) == -1) && (errno == ENOENT)) { + /* Doesn't exist. */ + return 0; + } + else { + if((stat_buf.st_nlink) > 1) { /* Security: We are handlinked... */ + if(unlink(filename)) { + fprintf(stderr, + "Unable to delete file in security violation (hardlinked): %s\n", + filename); + exit(127); + } + fprintf(stderr, + "File in security violation (hardlinked): %s\n", + filename); + return 1; + } + else if(S_ISLNK(stat_buf.st_mode)) { /* Security: We are a symlink? */ + fprintf(stderr, + "File in security violation (symlink): %s\n", + filename); + exit(127); + } + else if(0 == S_ISREG(stat_buf.st_mode)) { /* Security: special file */ + fprintf(stderr, + "File in security violation (not regular): %s\n", + filename); + exit(127); + } + else if(stat_buf.st_mode & S_IWOTH) { /* Security: We are o+w? */ + if(unlink(filename)) { + fprintf(stderr, + "Unable to delete file in security violation (world write): %s\n", + filename); + exit(127); + } + fprintf(stderr, + "File in security violation (world write): %s\n", + filename); + return 1; + } + else if( + !((stat_buf.st_uid == 0) || (stat_buf.st_uid == getuid()) || ((passwd_buf!=NULL) && (stat_buf.st_uid == passwd_buf->pw_uid))) || + !((stat_buf.st_gid == 0) || (stat_buf.st_gid == getgid()) || ((group_buf !=NULL) && (stat_buf.st_gid == group_buf->gr_gid))) + ) { /* Security: Owner/Group isn't right. */ + + /* uid = 0 or myuid or portage */ + /* gid = 0 or mygid or portage */ + + if(0) { + fprintf(stderr, "--1: %d,%d,%d,%d\n--2: %d,%d,%d,%d\n", + + (stat_buf.st_uid == 0), + (stat_buf.st_uid == getuid()), + (passwd_buf!=NULL), + (passwd_buf!=NULL)? (stat_buf.st_uid == passwd_buf->pw_uid) : -1, + + (stat_buf.st_gid == 0), + (stat_buf.st_gid == getgid()), + (group_buf !=NULL), + (group_buf !=NULL)? (stat_buf.st_gid == group_buf->gr_gid) : -1); + } + + /* manpage: "The return value may point to static area" */ + /* DO NOT ACTUALLY FREE THIS... It'll segfault. */ + /* if(passwd_buf != NULL) { free(passwd_buf); } */ + /* if(group_buf != NULL) { free(group_buf); } */ + + if(unlink(filename)) { + fprintf(stderr, + "Unable to delete file in security violation (bad owner/group): %s\n", + filename); + exit(127); + } + fprintf(stderr, + "File in security violation (bad owner/group): %s\n", + filename); + return 1; + } + } /* Stat */ + return 0; +} + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/ChangeLog b/src/sandbox-dev/ChangeLog new file mode 100644 index 00000000..3a052a12 --- /dev/null +++ b/src/sandbox-dev/ChangeLog @@ -0,0 +1,91 @@ +# ChangeLog for Path Sandbox +# Copyright 1999-2004 Gentoo Foundation; Distributed under the GPL v2 +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/ChangeLog,v 1.9 2004/10/04 14:08:46 vapier Exp $ + + 16 Dec 2002; J Robert Ray Makefile libsandbox.c : + + Instead of parsing the SANDBOX_* env variables on each syscall, save the + result in a global sbcontext pointer and cache the value of the env vars + to detect later on if they have changed and need to be re-parsed. Works + around bug 233. + + 16 Dec 2002; Martin Schlemmer create-localdecls : + + Fix memory leak for mips, bug #12236. Thanks to Torgeir Hansen + for this fix. + + 4 Dec 2002; J Robert Ray sandbox.h sandbox_futils.c : + + sandbox_futils defined a dirname() function that was masking the same + function in glibc and was broken (e.g.: SANDBOX_DIR was being set to + '/usr/lib/portage/bi/'). Fixed function to return expected results and + renamed it to sb_dirname() to no longer mask the glibc function. Closes bug + 11231. + + 4 Dec 2002; Martin Schlemmer : + + Fix a segfault in libsandbox.c if canonicalize() was called with + first parameter = NULL. + + 1 Sep 2002; Martin Schlemmer : + + Fix my braindead 'return 1;' in a void function. Updated sandbox.c, + cleanup() for this. + + Change cleanup() in sandbox.c not to exit with fail status if + the pidsfile is missing. We really should still display sandbox + violations if they occured. + + 31 Aug 2002; Martin Schlemmer : + + Update cleanup() in sandbox.c to remove the PIDSFILE if this is + the last sandbox running. + + 25 Aug 2002; Martin Schlemmer : + + Major cleanups to mainly libsandbox.c again. + + 22 Aug 2002; Martin Schlemmer : + + Add copyrights to sandbox.h and sandbox_futils.h. If wrong, the + parties involved should please contact me so that we can fix it. + + Add opendir wrapper to libsandbox.c. + + 21 Aug 2002; Martin Schlemmer : + + Do some more cleanups to ecanonicalize(), as it dropped filenames in + rare cases (after my symlink cleanups), and caused glibc to bork. + These fixes went into canonicalize.c. + + 20 Aug 2002; Martin Schlemmer : + + Fix spawn_shell() and main() in sandbox.c to properly return fail + status. + + 19 Aug 2002; Martin Schlemmer : + + The new canonicalize() function in libsandbox.c also resolved symlinks, + which caused on cleaning sandbox errors if the symlink pointed to a + file in the live root. Ripped out canonicalize() and realpath() from + glibc; removed the symlink stuff, and changed them to ecanonicalize() + and erealpath(). + + 18 Aug 2002; Martin Schlemmer : + + Ripped out all the wrappers, and implemented those of InstallWatch. + Losts of cleanups and bugfixes. Implement a execve that forces + $LIBSANDBOX in $LD_PRELOAD. We can now thus do away with the feared + /etc/ld.so.preload (*g*) ... Made the needed changes to sandbox.c, + sandbox.h and sandbox_futils.c. Rewrote the Makefile for most + parts; it now have an install target. + + Reformat the whole thing to look somewhat like the reworked sandbox.c + and new sandbox.h and sandbox_futils.c from: + + Brad House . + + Additional Copyrights now due to the InstallWatch code: + + Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro + diff --git a/src/sandbox-dev/Makefile b/src/sandbox-dev/Makefile new file mode 100644 index 00000000..83b00e30 --- /dev/null +++ b/src/sandbox-dev/Makefile @@ -0,0 +1,62 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# +# Modified 15 Apr 2002 Jon Nelson +# Clean up Makefile somewhat, and use make's implicit rules +# +# Modified 19 Aug 2002; Martin Schlemmer +# Major rewrite to support new stuff +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/Makefile,v 1.3 2002/12/16 22:28:05 jrray Exp $ + +CC = gcc +LD = ld +CFLAGS = +OBJ_DEFINES = -D_GNU_SOURCE -DPIC -fPIC -D_REENTRANT +LIBS = +LDFLAGS = +DESTDIR = + +TARGETS = libsandbox.so sandbox + +all: $(TARGETS) + +sandbox: sandbox.o sandbox_futils.o + $(CC) $^ -ldl -lc -o $@ + +sandbox.o: sandbox.c sandbox.h + $(CC) $(CFLAGS) -Wall -c sandbox.c + +sandbox_futils.o: sandbox_futils.c sandbox.h + $(CC) $(CFLAGS) -Wall -c $(OBJ_DEFINES) sandbox_futils.c + +libsandbox.so: libsandbox.o sandbox_futils.o canonicalize.o + $(LD) $^ -shared -fPIC -ldl -lc -lpthread -o $@ + +libsandbox.o: libsandbox.c localdecls.h + $(CC) $(CFLAGS) -Wall -c $(OBJ_DEFINES) libsandbox.c + +canonicalize.o: canonicalize.c + $(CC) $(CFLAGS) -Wall -c $(OBJ_DEFINES) canonicalize.c + +localdecls.h: create-localdecls libctest.c + ./create-localdecls + + +install: all + install -d -m 0755 $(DESTDIR)/lib + install -d -m 0755 $(DESTDIR)/usr/lib/portage/bin + install -d -m 0755 $(DESTDIR)/usr/lib/portage/lib + install -m 0755 libsandbox.so $(DESTDIR)/lib + install -m 0755 sandbox $(DESTDIR)/usr/lib/portage/bin + install -m 0644 sandbox.bashrc $(DESTDIR)/usr/lib/portage/lib + + +clean: + rm -f $(TARGETS) + rm -f *.o *~ core + rm -f localdecls.h + + +# vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/canonicalize.c b/src/sandbox-dev/canonicalize.c new file mode 100644 index 00000000..7ecd57ec --- /dev/null +++ b/src/sandbox-dev/canonicalize.c @@ -0,0 +1,194 @@ +/* Return the canonical absolute name of a given file. + Copyright (C) 1996, 1997, 1998, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/canonicalize.c,v 1.2 2002/08/26 03:28:30 azarah Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +/* Return the canonical absolute name of file NAME. A canonical name + does not contain any `.', `..' components nor any repeated path + separators ('/') or symlinks. All path components must exist. If + RESOLVED is null, the result is malloc'd; otherwise, if the + canonical name is PATH_MAX chars or more, returns null with `errno' + set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, + returns the name in RESOLVED. If the name cannot be resolved and + RESOLVED is non-NULL, it contains the path of the first component + that cannot be resolved. If the path can be resolved, RESOLVED + holds the same value as the value returned. */ + +/* Modified: 19 Aug 2002; Martin Schlemmer + * + * Cleaned up unneeded stuff, and change so that it will not + * resolve symlinks. Also prepended a 'e' to functions that + * I did not rip out. + * + */ + +static char * +ecanonicalize (const char *name, char *resolved) +{ + char *rpath, *dest; + const char *start, *end, *rpath_limit; + long int path_max; + + if (name == NULL) + { + /* As per Single Unix Specification V2 we must return an error if + either parameter is a null pointer. We extend this to allow + the RESOLVED parameter to be NULL in case the we are expected to + allocate the room for the return value. */ + __set_errno (EINVAL); + return NULL; + } + + if (name[0] == '\0') + { + /* As per Single Unix Specification V2 we must return an error if + the name argument points to an empty string. */ + __set_errno (ENOENT); + return NULL; + } + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf (name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + rpath = resolved ? alloca (path_max) : malloc (path_max); + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!getcwd (rpath, path_max)) + { + rpath[0] = '\0'; + goto error; + } + dest = strchr (rpath, '\0'); + } + else + { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) + { + /* Skip sequence of multiple path-separators. */ + while (*start == '/') + ++start; + + /* Find end of path component. */ + for (end = start; *end && *end != '/'; ++end) + /* Nothing. */; + + if (end - start == 0) + break; + else if (end - start == 1 && start[0] == '.') + /* nothing */; + else if (end - start == 2 && start[0] == '.' && start[1] == '.') + { + /* Back up to previous component, ignore if at root already. */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/'); + } + else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + ptrdiff_t dest_offset = dest - rpath; + + if (resolved) + { + __set_errno (ENAMETOOLONG); + if (dest > rpath + 1) + dest--; + *dest = '\0'; + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + rpath = realloc (rpath, new_size); + rpath_limit = rpath + new_size; + if (rpath == NULL) + return NULL; + + dest = rpath + dest_offset; + } + + dest = __mempcpy (dest, start, end - start); + *dest = '\0'; + + } + } +#if 0 + if (dest > rpath + 1 && dest[-1] == '/') + --dest; +#endif + *dest = '\0'; + + return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath; + +error: + if (resolved) + strcpy (resolved, rpath); + else + free (rpath); + return NULL; +} + + +char * +erealpath (const char *name, char *resolved) +{ + if (resolved == NULL) + { + __set_errno (EINVAL); + return NULL; + } + + return ecanonicalize (name, resolved); +} + + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/create-localdecls b/src/sandbox-dev/create-localdecls new file mode 100755 index 00000000..5cb1a4d0 --- /dev/null +++ b/src/sandbox-dev/create-localdecls @@ -0,0 +1,95 @@ +#!/bin/sh + +# This is a quick'n'dirty hack to make the program behave correctly +# under different systems. +# Example: +# when using libc5, (f)trucate's offset argument type is size_t with +# libc5, but it's off_t with libc6 (glibc2). +# +# Uhm... time to learn GNU autoconf :-) +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/create-localdecls,v 1.2 2002/12/16 19:19:27 azarah Exp $ + +OUTFILE='localdecls.h' + +# if your arch needs to dlopen() glibc, add it here separated by space :] +BROKEN_RTLD_ARCHLIST="mips" + +echo '/* This file is automatically generated *' > $OUTFILE +echo ' * Modify create-localdecls instead of this */' >> $OUTFILE +echo >> $OUTFILE +echo '#ifndef __LOCALDECLS_H_' >> $OUTFILE +echo '#define __LOCALDECLS_H_' >> $OUTFILE +echo >> $OUTFILE + +### +### +### + +echo -n 'Checking truncate argument type... ' +if grep -q 'truncate.*size_t' /usr/include/unistd.h ; then + echo 'size_t' + echo '#define TRUNCATE_T size_t' >> $OUTFILE +else + echo 'off_t' # At least, I HOPE it's off_t :-) + echo '#define TRUNCATE_T off_t' >> $OUTFILE +fi + +### +### +### + +echo -n 'Checking libc version... ' +gcc -Wall -o libctest libctest.c +VERSION=`ldd libctest | grep libc\\.so | awk '{print $1}'` +rm libctest +echo $VERSION +echo "#define LIBC_VERSION \"$VERSION\"" >> $OUTFILE +if test "$VERSION" = 'libc.so.5' ; then + echo '#define BROKEN_RTLD_NEXT' >> $OUTFILE + echo '#define LIBC 5' >> $OUTFILE +else + # for the arch's that need to dlopen() libc to fetch real funcs! + # 16.12.02 -Torgeir Hansen + MYARCH=`/bin/uname -m` + for x in $BROKEN_RTLD_ARCHLIST; do + if [ $x = $MYARCH ]; then + echo '#define BROKEN_RTLD_NEXT' >> $OUTFILE + fi + done + +fi + +if test "$VERSION" = 'libc.so.6' ; then + echo -n 'Checking glibc subversion... ' + tmp="`ldd /bin/sh | grep libc.so 2> /dev/null`" + LibcPath=`expr "$tmp" : '[^/]*\(/[^ ]*\)'` + tmp="`strings $LibcPath | grep -i 'c library'`" + OsLibcMajor=`expr "$tmp" : '.* \([0-9][0-9]*\)'` + OsLibcMinor=`expr "$tmp" : '.* [0-9][0-9]*\.\([0-9][0-9]*\)'` + case "$OsLibcMajor" in + 2) + # 2 is the glibc version + case "$OsLibcMinor" in + 0) + echo '#define GLIBC_MINOR 0' >> $OUTFILE + SUBVERSION='glibc-2.0' ;; + 1) + echo '#define GLIBC_MINOR 1' >> $OUTFILE + SUBVERSION='glibc-2.1' ;; + 2) + echo '#define GLIBC_MINOR 2' >> $OUTFILE + SUBVERSION='glibc-2.2' ;; + *) + echo 'Treated as glibc >= 2.1 (finger crossed)' + echo '#define GLIBC_MINOR 1' >> $OUTFILE + SUBVERSION='glibc-2.1' ;; + esac + ;; + esac +fi + +echo >> $OUTFILE +echo '#endif' >> $OUTFILE +echo + diff --git a/src/sandbox-dev/libctest.c b/src/sandbox-dev/libctest.c new file mode 100644 index 00000000..5fc92b50 --- /dev/null +++ b/src/sandbox-dev/libctest.c @@ -0,0 +1,6 @@ +/* Dummy program to check your libc version */ + +int main(void) { + return 0; +} + diff --git a/src/sandbox-dev/libsandbox.c b/src/sandbox-dev/libsandbox.c new file mode 100644 index 00000000..ffb50359 --- /dev/null +++ b/src/sandbox-dev/libsandbox.c @@ -0,0 +1,1214 @@ +/* + * Path sandbox for the gentoo linux portage package system, initially + * based on the ROCK Linux Wrapper for getting a list of created files + * + * to integrate with bash, bash should have been built like this + * + * ./configure --prefix= --host= --without-gnu-malloc + * + * it's very important that the --enable-static-link option is NOT specified + * + * Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com + * Distributed under the terms of the GNU General Public License, v2 or later + * Author : Geert Bevin + * + * Post Bevin leaving Gentoo ranks: + * -------------------------------- + * Ripped out all the wrappers, and implemented those of InstallWatch. + * Losts of cleanups and bugfixes. Implement a execve that forces $LIBSANDBOX + * in $LD_PRELOAD. Reformat the whole thing to look somewhat like the reworked + * sandbox.c from Brad House . + * + * Martin Schlemmer (18 Aug 2002) + * + * Partly Copyright (C) 1998-9 Pancrazio `Ezio' de Mauro , + * as some of the InstallWatch code was used. + * + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/libsandbox.c,v 1.4 2002/12/16 22:28:05 jrray Exp $ + * + */ + +/* Uncomment below to enable wrapping of mknod(). + * This is broken currently. */ +/* #define WRAP_MKNOD */ + + +#define open xxx_open +#define open64 xxx_open64 + +/* Wrapping mknod, do not have any effect, and + * wrapping __xmknod causes calls to it to segfault + */ +#ifdef WRAP_MKNOD +# define __xmknod xxx___xmknod +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef WRAP_MKNOD +# undef __xmknod +#endif + +#undef open +#undef open64 + +#include "localdecls.h" +#include "sandbox.h" + +#define PIDS_FILE "/tmp/sandboxpids.tmp" + +#define FUNCTION_SANDBOX_SAFE(func, path) \ + ((0 == is_sandbox_on()) || (1 == before_syscall(func, path))) + +#define FUNCTION_SANDBOX_SAFE_INT(func, path, flags) \ + ((0 == is_sandbox_on()) || (1 == before_syscall_open_int(func, path, flags))) + +#define FUNCTION_SANDBOX_SAFE_CHAR(func, path, mode) \ + ((0 == is_sandbox_on()) || (1 == before_syscall_open_char(func, path, mode))) + + +/* Macro to check if a wrapper is defined, if not + * then try to resolve it again. */ +#define check_dlsym(name) \ +{ \ + int old_errno=errno; \ + if (!true_ ## name) true_ ## name=get_dlsym(#name); \ + errno=old_errno; \ +} + +static char sandbox_lib[255]; + +typedef struct { + char *last_env; + int count; + char **strs; +} sbprefix_t; + +typedef struct { + int show_access_violation; + sbprefix_t deny; + sbprefix_t read; + sbprefix_t write; + sbprefix_t predict; +} sbcontext_t; + +/* glibc modified realpath() functions */ +char *erealpath (const char *name, char *resolved); + +static void init_wrappers(void); +static void *get_dlsym(const char *); +static void canonicalize(const char *, char *); +static int check_access(sbcontext_t *, const char *, const char *); +static int check_syscall(sbcontext_t *, const char *, const char *); +static int before_syscall(const char *, const char *); +static int before_syscall_open_int(const char *, const char *, int); +static int before_syscall_open_char(const char *, const char *, const char *); +static void clean_env_entries(sbprefix_t *); +static void init_context(sbcontext_t *); +static void init_env_entries(sbprefix_t *, char *); +static char* filter_path(const char*); +static int is_sandbox_on(); +static int is_sandbox_pid(); + +/* Wrapped functions */ + +extern int chmod(const char *, mode_t); +static int(*true_chmod)(const char *, mode_t); +extern int chown(const char *, uid_t, gid_t); +static int(*true_chown)(const char *, uid_t, gid_t); +extern int creat(const char *, mode_t); +static int(*true_creat)(const char *, mode_t); +extern FILE *fopen(const char *,const char*); +static FILE *(*true_fopen)(const char *,const char*); +extern int lchown(const char *, uid_t, gid_t); +static int(*true_lchown)(const char *, uid_t, gid_t); +extern int link(const char *, const char *); +static int(*true_link)(const char *, const char *); +extern int mkdir(const char *, mode_t); +static int(*true_mkdir)(const char *, mode_t); +extern DIR *opendir(const char *); +static DIR *(*true_opendir)(const char *); +#ifdef WRAP_MKNOD +extern int __xmknod(const char *, mode_t, dev_t); +static int(*true___xmknod)(const char *, mode_t, dev_t); +#endif +extern int open(const char *, int, ...); +static int(*true_open)(const char *, int, ...); +extern int rename(const char *, const char *); +static int(*true_rename)(const char *, const char *); +extern int rmdir(const char *); +static int(*true_rmdir)(const char *); +extern int symlink(const char *, const char *); +static int(*true_symlink)(const char *, const char *); +extern int truncate(const char *, TRUNCATE_T); +static int(*true_truncate)(const char *, TRUNCATE_T); +extern int unlink(const char *); +static int(*true_unlink)(const char *); + +#if (GLIBC_MINOR >= 1) + +extern int creat64(const char *, __mode_t); +static int(*true_creat64)(const char *, __mode_t); +extern FILE *fopen64(const char *,const char *); +static FILE *(*true_fopen64)(const char *,const char *); +extern int open64(const char *, int, ...); +static int(*true_open64)(const char *, int, ...); +extern int truncate64(const char *, __off64_t); +static int(*true_truncate64)(const char *, __off64_t); + +#endif + +extern int execve(const char *filename, char *const argv [], char *const envp[]); +static int (*true_execve)(const char *, char *const [], char *const []); + +static sbcontext_t* sbcontext = NULL; +static sem_t ctxsem; + +/* + * Initialize the shabang + */ + +static void init_wrappers(void) +{ + void *libc_handle = NULL; + +#ifdef BROKEN_RTLD_NEXT +// printf ("RTLD_LAZY"); + libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); +#else +// printf ("RTLD_NEXT"); + libc_handle = RTLD_NEXT; +#endif + + true_chmod = dlsym(libc_handle, "chmod"); + true_chown = dlsym(libc_handle, "chown"); + true_creat = dlsym(libc_handle, "creat"); + true_fopen = dlsym(libc_handle, "fopen"); + true_lchown = dlsym(libc_handle, "lchown"); + true_link = dlsym(libc_handle, "link"); + true_mkdir = dlsym(libc_handle, "mkdir"); + true_opendir = dlsym(libc_handle, "opendir"); +#ifdef WRAP_MKNOD + true___xmknod = dlsym(libc_handle, "__xmknod"); +#endif + true_open = dlsym(libc_handle, "open"); + true_rename = dlsym(libc_handle, "rename"); + true_rmdir = dlsym(libc_handle, "rmdir"); + true_symlink = dlsym(libc_handle, "symlink"); + true_truncate = dlsym(libc_handle, "truncate"); + true_unlink = dlsym(libc_handle, "unlink"); + +#if (GLIBC_MINOR >= 1) + true_creat64 = dlsym(libc_handle, "creat64"); + true_fopen64 = dlsym(libc_handle, "fopen64"); + true_open64 = dlsym(libc_handle, "open64"); + true_truncate64 = dlsym(libc_handle, "truncate64"); +#endif + + true_execve = dlsym(libc_handle, "execve"); +} + +void _init(void) +{ + int old_errno = errno; + char *tmp_string = NULL; + + if (sem_init(&ctxsem, 0, 1)) { + fprintf(stderr, "Failed to create semaphore\n"); + abort(); + } + + init_wrappers(); + + /* Get the path and name to this library */ + tmp_string = get_sandbox_lib("/"); + strncpy(sandbox_lib, tmp_string, 254); + + if (tmp_string) free(tmp_string); + tmp_string = NULL; + + errno = old_errno; +} + +void _fini(void) +{ + if (sbcontext) { + clean_env_entries(&sbcontext->deny); + clean_env_entries(&sbcontext->read); + clean_env_entries(&sbcontext->write); + clean_env_entries(&sbcontext->predict); + free(sbcontext); + sbcontext = NULL; + } + + /* free the semaphore */ + sem_destroy(&ctxsem); +} + +static void canonicalize(const char *path, char *resolved_path) +{ + int old_errno = errno; + + /* If path == NULL, return or we get a segfault */ + if (NULL == path) return; + + if(!erealpath(path, resolved_path) && (path[0] != '/')) { + /* The path could not be canonicalized, append it + * to the current working directory if it was not + * an absolute path + */ + getcwd(resolved_path, MAXPATHLEN - 2); + strcat(resolved_path, "/"); + strncat(resolved_path, path, MAXPATHLEN - 1 - strlen(resolved_path)); + erealpath(resolved_path, resolved_path); + } + + errno = old_errno; +} + +static void *get_dlsym(const char *symname) +{ + void *libc_handle = NULL; + void *symaddr = NULL; + +#ifdef BROKEN_RTLD_NEXT + libc_handle = dlopen(LIBC_VERSION, RTLD_LAZY); + if (!libc_handle) { + printf("libsandbox.so: Can't dlopen libc: %s\n", dlerror()); + abort(); + } +#else + libc_handle = RTLD_NEXT; +#endif + + symaddr = dlsym(libc_handle, symname); + if (!symaddr) { + printf("libsandbox.so: Can't resolve %s: %s\n", symname, dlerror()); + abort(); + } + + return symaddr; +} + +/* + * Wrapper Functions + */ + +int chmod(const char *path, mode_t mode) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("chmod", canonic) { + check_dlsym(chmod); + result = true_chmod(path, mode); + } + + return result; +} + +int chown(const char *path, uid_t owner, gid_t group) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("chown", canonic) { + check_dlsym(chown); + result = true_chown(path, owner, group); + } + + return result; +} + +int creat(const char *pathname, mode_t mode) +{ +/* Is it a system call? */ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("creat", canonic) { + check_dlsym(open); + result = true_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); + } + + return result; +} + +FILE *fopen(const char *pathname, const char *mode) +{ + FILE *result = NULL; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_CHAR("fopen", canonic, mode) { + check_dlsym(fopen); + result = true_fopen(pathname,mode); + } + + return result; +} + +int lchown(const char *path, uid_t owner, gid_t group) +{ +/* Linux specific? */ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("lchown", canonic) { + check_dlsym(chown); + result = true_chown(path, owner, group); + } + + return result; +} + +int link(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(newpath, new_canonic); + + if FUNCTION_SANDBOX_SAFE("link", new_canonic) { + check_dlsym(link); + result = true_link(oldpath, newpath); + } + + return result; +} + +int mkdir(const char *pathname, mode_t mode) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("mkdir", canonic) { + check_dlsym(mkdir); + result = true_mkdir(pathname, mode); + } + + return result; +} + +DIR *opendir(const char *name) +{ + DIR *result = NULL; + char canonic[MAXPATHLEN]; + + canonicalize(name, canonic); + + if FUNCTION_SANDBOX_SAFE("opendir", canonic) { + check_dlsym(opendir); + result = true_opendir(name); + } + + return result; +} + +#ifdef WRAP_MKNOD + +int __xmknod(const char *pathname, mode_t mode, dev_t dev) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("__xmknod", canonic) { + check_dlsym(__xmknod); + result = true___xmknod(pathname, mode, dev); + } + + return result; +} + +#endif + +int open(const char *pathname, int flags, ...) +{ +/* Eventually, there is a third parameter: it's mode_t mode */ + va_list ap; + mode_t mode = 0; + int result = -1; + char canonic[MAXPATHLEN]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_INT("open", canonic, flags) { + /* We need to resolve open() realtime in some cases, + * else we get a segfault when running /bin/ps, etc + * in a sandbox */ + check_dlsym(open); + result=true_open(pathname, flags, mode); + } + + return result; +} + +int rename(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(newpath, new_canonic); + + if FUNCTION_SANDBOX_SAFE("rename", new_canonic) { + check_dlsym(rename); + result = true_rename(oldpath, newpath); + } + + return result; +} + +int rmdir(const char *pathname) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("rmdir", canonic) { + check_dlsym(rmdir); + result = true_rmdir(pathname); + } + + return result; +} + +int symlink(const char *oldpath, const char *newpath) +{ + int result = -1; + char old_canonic[MAXPATHLEN], new_canonic[MAXPATHLEN]; + + canonicalize(oldpath, old_canonic); + canonicalize(newpath, new_canonic); + + if FUNCTION_SANDBOX_SAFE("symlink", new_canonic) { + check_dlsym(symlink); + result = true_symlink(oldpath, newpath); + } + + return result; +} + +int truncate(const char *path, TRUNCATE_T length) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("truncate", canonic) { + check_dlsym(truncate); + result = true_truncate(path, length); + } + + return result; +} + +int unlink(const char *pathname) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("unlink", canonic) { + check_dlsym(unlink); + result = true_unlink(pathname); + } + + return result; +} + +#if (GLIBC_MINOR >= 1) + +int creat64(const char *pathname, __mode_t mode) +{ +/* Is it a system call? */ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE("creat64", canonic) { + check_dlsym(open64); + result = true_open64(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode); + } + + return result; +} + +FILE *fopen64(const char *pathname, const char *mode) +{ + FILE *result = NULL; + char canonic[MAXPATHLEN]; + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_CHAR("fopen64", canonic, mode) { + check_dlsym(fopen64); + result = true_fopen(pathname,mode); + } + + return result; +} + +int open64(const char *pathname, int flags, ...) +{ +/* Eventually, there is a third parameter: it's mode_t mode */ + va_list ap; + mode_t mode = 0; + int result = -1; + char canonic[MAXPATHLEN]; + + if (flags & O_CREAT) { + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + } + + canonicalize(pathname, canonic); + + if FUNCTION_SANDBOX_SAFE_INT("open64", canonic, flags) { + check_dlsym(open64); + result=true_open64(pathname, flags, mode); + } + + return result; +} + +int truncate64(const char *path, __off64_t length) +{ + int result = -1; + char canonic[MAXPATHLEN]; + + canonicalize(path, canonic); + + if FUNCTION_SANDBOX_SAFE("truncate64", canonic) { + check_dlsym(truncate64); + result = true_truncate64(path, length); + } + + return result; +} + +#endif /* GLIBC_MINOR >= 1 */ + +/* + * Exec Wrappers + */ + +int execve(const char *filename, char *const argv [], char *const envp[]) +{ + int old_errno = errno; + int result = -1; + int count = 0; + char canonic[MAXPATHLEN]; + char *old_envp = NULL; + char *new_envp = NULL; + + canonicalize(filename, canonic); + + if FUNCTION_SANDBOX_SAFE("execve", canonic) { + while (envp[count] != NULL) { + if (strstr(envp[count], "LD_PRELOAD=") == envp[count]) { + if (NULL != strstr(envp[count], sandbox_lib)) { + break; + } else { + const int max_envp_len = strlen(envp[count]) + strlen(sandbox_lib) + 1; + + /* Backup envp[count], and set it to our own one which + * contains sandbox_lib */ + old_envp = envp[count]; + new_envp = strndupa(old_envp, max_envp_len - 1); + + /* LD_PRELOAD already have variables other than sandbox_lib, + * thus we have to add sandbox_lib via a white space. */ + if (0 != strcmp(envp[count], "LD_PRELOAD=")) { + strncpy(new_envp + strlen(old_envp), ":", + max_envp_len - strlen(new_envp)); + strncpy(new_envp + strlen(old_envp) + 1, sandbox_lib, + max_envp_len - strlen(new_envp)); + } else { + strncpy(new_envp + strlen(old_envp), sandbox_lib, + max_envp_len - strlen(new_envp)); + } + + /* Valid string? */ + new_envp[max_envp_len] = '\0'; + + /* envp[count] = new_envp; + * + * Get rid of the "read-only" warnings */ + memcpy((void *)&envp[count], &new_envp, sizeof(new_envp)); + + break; + } + } + count++; + } + + errno = old_errno; + check_dlsym(execve); + result = true_execve(filename, argv, envp); + old_errno = errno; + + if (old_envp) { + /* Restore envp[count] again. + * + * envp[count] = old_envp; */ + memcpy((void *)&envp[count], &old_envp, sizeof(old_envp)); + old_envp = NULL; + } + } + + errno = old_errno; + + return result; +} + +/* + * Internal Functions + */ + +#if (GLIBC_MINOR == 1) + +/* This hack is needed for glibc 2.1.1 (and others?) + * (not really needed, but good example) */ +extern int fclose(FILE *); +static int (*true_fclose)(FILE *) = NULL; +int fclose(FILE *file) +{ + int result = - 1; + + check_dlsym(fclose); + result = true_fclose(file); + + return result; +} + +#endif /* GLIBC_MINOR == 1 */ + +static void init_context(sbcontext_t* context) +{ + memset(context, 0, sizeof(sbcontext_t)); + context->show_access_violation = 1; +} + +static int is_sandbox_pid() +{ + int old_errno = errno; + int result = 0; + FILE* pids_stream = NULL; + int pids_file = -1; + int current_pid = 0; + int tmp_pid = 0; + + init_wrappers(); + + pids_stream = true_fopen(PIDS_FILE, "r"); + + if (NULL == pids_stream) { + perror(">>> pids file fopen"); + } + else + { + pids_file = fileno(pids_stream); + + if (pids_file < 0) { + perror(">>> pids file fileno"); + } else { + current_pid = getpid(); + + while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) { + if (tmp_pid == current_pid) { + result = 1; + break; + } + } + } + if (EOF == fclose(pids_stream)) { + perror(">>> pids file fclose"); + } + pids_stream = NULL; + pids_file = -1; + } + + errno = old_errno; + + return result; +} + +static void clean_env_entries(sbprefix_t* prefix) +{ + int old_errno = errno; + int i = 0; + + if (NULL != prefix->strs) { + for (i = 0; i < prefix->count; i++) { + if (NULL != prefix->strs[i]) { + free(prefix->strs[i]); + prefix->strs[i] = NULL; + } + } + free(prefix->strs); + prefix->strs = NULL; + prefix->count = 0; + } + if (prefix->last_env) { + free(prefix->last_env); + prefix->last_env = NULL; + } + + errno = old_errno; +} + +static void init_env_entries(sbprefix_t* prefix, char* env) +{ + int old_errno = errno; + char* prefixes_env = getenv(env); + + if (NULL == prefixes_env) { + fprintf(stderr, + "Sandbox error : the %s environmental variable should be defined.\n", + env); + } else { + char *ptr; + int num_colons = 0; + + /* Check to see if the env value has changed since the + last time this was initalized, don't do the work again + if it hasn't. + */ + + if (prefix->last_env && !strcmp(prefix->last_env, prefixes_env)) { + errno = old_errno; + return; + } + + /* Clean any existing entries */ + clean_env_entries(prefix); + + /* Env value is different, update the cached copy */ + prefix->last_env = strdup(prefixes_env); + + ptr = prefixes_env; + while (*ptr) { + if (*ptr++ == ':') ++num_colons; + } + + if (prefix->strs) { + free(prefix->strs); + prefix->strs = 0; + } + prefix->strs = (char**)malloc((num_colons+1) * sizeof(char*)); + if (!prefix->strs) return; + memset(prefix->strs, 0, (num_colons+1) * sizeof(char*)); + prefix->count = 0; + + ptr = prefixes_env; + while (*ptr) { + char *next_colon = strchr(ptr, ':'); + if (next_colon) { + if (next_colon != ptr) { + char *str = strndup(ptr, next_colon-ptr); + if (!str) return; + prefix->strs[prefix->count++] = filter_path(str); + free(str); + } + } else { + prefix->strs[prefix->count++] = filter_path(ptr); + break; + } + + ptr = next_colon+1; + } + } + errno = old_errno; +} + +static char* filter_path(const char* path) +{ + int old_errno = errno; + char* filtered_path = (char *)malloc(MAXPATHLEN * sizeof(char)); + filtered_path[0] = 0; + + canonicalize(path, filtered_path); + + errno = old_errno; + + return filtered_path; +} + +static int check_access(sbcontext_t* sbcontext, const char* func, const char* path) +{ + int old_errno = errno; + int result = -1; + int i = 0; + char* filtered_path = filter_path(path); + + if (!filtered_path) { + errno = old_errno; + return 0; + } + + if ('/' != filtered_path[0]) { + free(filtered_path); + errno = old_errno; + return 0; + } + + if ((0 == strncmp(filtered_path, "/etc/ld.so.preload", 18)) && (is_sandbox_pid())) { + result = 1; + } + + if (-1 == result) { + if (NULL != sbcontext->deny.strs) { + for (i = 0; i < sbcontext->deny.count; i++) { + if (NULL != sbcontext->deny.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->deny.strs[i], + strlen(sbcontext->deny.strs[i]))) { + result = 0; + break; + } + } + } + } + + if (-1 == result) { + if ((NULL != sbcontext->read.strs) && + ((0 == strncmp(func, "open_rd", 7)) || + (0 == strncmp(func, "popen", 5)) || + (0 == strncmp(func, "opendir", 7)) || + (0 == strncmp(func, "system", 6)) || + (0 == strncmp(func, "execl", 5)) || + (0 == strncmp(func, "execlp", 6)) || + (0 == strncmp(func, "execle", 6)) || + (0 == strncmp(func, "execv", 5)) || + (0 == strncmp(func, "execvp", 6)) || + (0 == strncmp(func, "execve", 6)) + ) + ) { + for (i = 0; i < sbcontext->read.count; i++) { + if (NULL != sbcontext->read.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->read.strs[i], + strlen(sbcontext->read.strs[i]))) { + result = 1; + break; + } + } + } + } + else if ((NULL != sbcontext->write.strs) && + ((0 == strncmp(func, "open_wr", 7)) || + (0 == strncmp(func, "creat", 5)) || + (0 == strncmp(func, "creat64", 7)) || + (0 == strncmp(func, "mkdir", 5)) || + (0 == strncmp(func, "mknod", 5)) || + (0 == strncmp(func, "mkfifo", 6)) || + (0 == strncmp(func, "link", 4)) || + (0 == strncmp(func, "symlink", 7)) || + (0 == strncmp(func, "rename", 6)) || + (0 == strncmp(func, "utime", 5)) || + (0 == strncmp(func, "utimes", 6)) || + (0 == strncmp(func, "unlink", 6)) || + (0 == strncmp(func, "rmdir", 5)) || + (0 == strncmp(func, "chown", 5)) || + (0 == strncmp(func, "lchown", 6)) || + (0 == strncmp(func, "chmod", 5)) || + (0 == strncmp(func, "truncate", 8)) || + (0 == strncmp(func, "ftruncate", 9)) || + (0 == strncmp(func, "truncate64", 10)) || + (0 == strncmp(func, "ftruncate64", 11)) + ) + ) { + struct stat tmp_stat; + +#if 0 // write_denied is never set + + for (i = 0; i < sbcontext->write_denied.count; i++) { + if (NULL != sbcontext->write_denied.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->write_denied.strs[i], + strlen(sbcontext->write_denied.strs[i]))) { + result = 0; + break; + } + } + } +#endif + + if (-1 == result) { + for (i = 0; i < sbcontext->write.count; i++) { + if (NULL != sbcontext->write.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->write.strs[i], + strlen(sbcontext->write.strs[i]))) { + result = 1; + break; + } + } + } + + if (-1 == result) { + /* hack to prevent mkdir of existing dirs to show errors */ + if (0 == strncmp(func, "mkdir", 5)) { + if (0 == stat(filtered_path, &tmp_stat)) { + sbcontext->show_access_violation = 0; + result = 0; + } + } + + if (-1 == result) { + for (i = 0; i < sbcontext->predict.count; i++) { + if (NULL != sbcontext->predict.strs[i]) { + if (0 == strncmp(filtered_path, + sbcontext->predict.strs[i], + strlen(sbcontext->predict.strs[i]))) { + sbcontext->show_access_violation = 0; + result = 0; + break; + } + } + } + } + } + } + } + } + } + + if (-1 == result) { + result = 0; + } + + if (filtered_path) free(filtered_path); + filtered_path = NULL; + + errno = old_errno; + + return result; +} + +static int check_syscall(sbcontext_t* sbcontext, const char* func, const char* file) +{ + int old_errno = errno; + int result = 1; + struct stat log_stat; + char* log_path = NULL; + char* absolute_path = NULL; + char* tmp_buffer = NULL; + int log_file = 0; + struct stat debug_log_stat; + char* debug_log_env = NULL; + char* debug_log_path = NULL; + int debug_log_file = 0; + char buffer[512]; + + init_wrappers(); + + if ('/' == file[0]) { + absolute_path = (char *)malloc((strlen(file) + 1) * sizeof(char)); + sprintf(absolute_path, "%s", file); + } else { + tmp_buffer = get_current_dir_name(); + absolute_path = (char *)malloc((strlen(tmp_buffer) + 1 + strlen(file) + 1) * sizeof(char)); + sprintf(absolute_path,"%s/%s", tmp_buffer, file); + + if (tmp_buffer) free(tmp_buffer); + tmp_buffer = NULL; + } + + log_path = getenv("SANDBOX_LOG"); + debug_log_env = getenv("SANDBOX_DEBUG"); + debug_log_path = getenv("SANDBOX_DEBUG_LOG"); + + if (((NULL == log_path) || + (0 != strncmp(absolute_path, log_path, strlen(log_path)))) && + ((NULL == debug_log_env) || + (NULL == debug_log_path) || + (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path)))) && + (0 == check_access(sbcontext, func, absolute_path)) + ) { + if (1 == sbcontext->show_access_violation) { + fprintf(stderr, "\e[31;01mACCESS DENIED\033[0m %s:%*s%s\n", + func, (int)(10 - strlen(func)), "", absolute_path); + + if (NULL != log_path) { + sprintf(buffer, "%s:%*s%s\n", func, (int)(10 - strlen(func)), "", absolute_path); + + if ((0 == lstat(log_path, &log_stat)) && + (0 == S_ISREG(log_stat.st_mode)) + ) { + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", + log_path); + } else { + log_file = true_open(log_path, + O_APPEND | O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(log_file >= 0) { + write(log_file, buffer, strlen(buffer)); + close(log_file); + } + } + } + } + + result = 0; + } + else if (NULL != debug_log_env) { + if (NULL != debug_log_path) { + if (0 != strncmp(absolute_path, debug_log_path, strlen(debug_log_path))) { + sprintf(buffer, "%s:%*s%s\n", func, (int)(10 - strlen(func)), "", absolute_path); + + if ((0 == lstat(debug_log_path, &debug_log_stat)) && + (0 == S_ISREG(debug_log_stat.st_mode)) + ) { + fprintf(stderr, + "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", + log_path); + } else { + debug_log_file = true_open(debug_log_path, + O_APPEND | O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if(debug_log_file >= 0) { + write(debug_log_file, buffer, strlen(buffer)); + close(debug_log_file); + } + } + } + } else { + fprintf(stderr, "\e[32;01mACCESS ALLOWED\033[0m %s:%*s%s\n", + func, (int)(10 - strlen(func)), "", absolute_path); + } + } + + if (absolute_path) free(absolute_path); + absolute_path = NULL; + + errno = old_errno; + + return result; +} + +static int is_sandbox_on() +{ + int old_errno = errno; + + /* $SANDBOX_ACTIVE is an env variable that should ONLY + * be used internal by sandbox.c and libsanbox.c. External + * sources should NEVER set it, else the sandbox is enabled + * in some cases when run in parallel with another sandbox, + * but not even in the sandbox shell. + * + * Azarah (3 Aug 2002) + */ + if ((NULL != getenv("SANDBOX_ON")) && + (0 == strncmp(getenv("SANDBOX_ON"), "1", 1)) && + (NULL != getenv("SANDBOX_ACTIVE")) && + (0 == strncmp(getenv("SANDBOX_ACTIVE"), "armedandready", 13)) + ) { + errno = old_errno; + + return 1; + } else { + errno = old_errno; + + return 0; + } +} + +static int before_syscall(const char* func, const char* file) +{ + int old_errno = errno; + int result = 1; + + /* Only allow one thread to access sbcontext at a time */ + sem_wait(&ctxsem); + + if (!sbcontext) { + sbcontext = (sbcontext_t*)malloc(sizeof(sbcontext_t)); + init_context(sbcontext); + } else { + /* sometimes this value gets set to 0 */ + sbcontext->show_access_violation = 1; + } + + init_env_entries(&sbcontext->deny, "SANDBOX_DENY"); + init_env_entries(&sbcontext->read, "SANDBOX_READ"); + init_env_entries(&sbcontext->write, "SANDBOX_WRITE"); + init_env_entries(&sbcontext->predict, "SANDBOX_PREDICT"); + + result = check_syscall(sbcontext, func, file); + + if (sem_post(&ctxsem)) { + fprintf(stderr, "Failed trying to release semaphore\n"); + } + + errno = old_errno; + + if (0 == result) { + errno = EACCES; + } + + return result; +} + +static int before_syscall_open_int(const char* func, const char* file, int flags) +{ + if ((flags & O_WRONLY) || (flags & O_RDWR)) { + return before_syscall("open_wr", file); + } else { + return before_syscall("open_rd", file); + } +} + +static int before_syscall_open_char(const char* func, const char* file, const char* mode) +{ + if ((strcmp(mode, "r") == 0) || (strcmp(mode, "rb") == 0)) { + return before_syscall("open_rd", file); + } else { + return before_syscall("open_wr", file); + } +} + + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/sandbox.bashrc b/src/sandbox-dev/sandbox.bashrc new file mode 100644 index 00000000..35fa89b6 --- /dev/null +++ b/src/sandbox-dev/sandbox.bashrc @@ -0,0 +1,8 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/sandbox.bashrc,v 1.1 2002/08/25 06:09:05 azarah Exp $ +source /etc/profile +export LD_PRELOAD="$SANDBOX_LIB" +alias make="make LD_PRELOAD=$SANDBOX_LIB" +alias su="su -c '/bin/bash -rcfile $SANDBOX_DIR/sandbox.bashrc'" diff --git a/src/sandbox-dev/sandbox.c b/src/sandbox-dev/sandbox.c new file mode 100644 index 00000000..7dccf62b --- /dev/null +++ b/src/sandbox-dev/sandbox.c @@ -0,0 +1,816 @@ +/* +** Path sandbox for the gentoo linux portage package system, initially +** based on the ROCK Linux Wrapper for getting a list of created files +** +** to integrate with bash, bash should have been built like this +** +** ./configure --prefix= --host= --without-gnu-malloc +** +** it's very important that the --enable-static-link option is NOT specified +** +** Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +** Distributed under the terms of the GNU General Public License, v2 or later +** Author : Geert Bevin +** $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/sandbox.c,v 1.4 2002/10/20 21:37:30 azarah Exp $ +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sandbox.h" + +int preload_adaptable = 1; +int cleaned_up = 0; +int print_debug = 0; + +/* Read pids file, and load active pids into an array. Return number of pids in array */ +int load_active_pids(int fd, int **pids) +{ + char *data = NULL; + char *ptr = NULL, *ptr2 = NULL; + int my_pid; + int num_pids = 0; + long len; + + pids[0] = NULL; + + len = file_length(fd); + + /* Allocate and zero datablock to read pids file */ + data = (char *)malloc((len + 1)*sizeof(char)); + memset(data, 0, len + 1); + + /* Start at beginning of file */ + lseek(fd, 0L, SEEK_SET); + + /* read entire file into a buffer */ + read(fd, data, len); + + ptr = data; + + /* Loop and read all pids */ + while (1) { + /* Find new line */ + ptr2 = strchr(ptr, '\n'); + if (ptr2 == NULL) break; /* No more PIDs */ + + /* clear the \n. And ptr should have a null-terminated decimal string */ + ptr2[0] = 0; + + my_pid = atoi(ptr); + + /* If the PID is still alive, add it to our array */ + if ((0 != my_pid) && (0 == kill(my_pid, 0))) { + pids[0] = (int *)realloc(pids[0], (num_pids + 1)*sizeof(int)); + pids[0][num_pids] = my_pid; + num_pids++; + } + + /* Put ptr past the NULL we just wrote */ + ptr = ptr2 + 1; + } + + if (data) free(data); + + return num_pids; +} + +/* Read ld.so.preload file, and loads dirs into an array. Return number of entries in array */ +int load_preload_libs(int fd, char ***preloads) +{ + char *data = NULL; + char *ptr = NULL, *ptr2 = NULL; + int num_entries = 0; + long len; + + preloads[0] = NULL; + + len = file_length(fd); + + /* Allocate and zero datablock to read pids file */ + data = (char *)malloc((len + 1)*sizeof(char)); + memset(data, 0, len + 1); + + /* Start at beginning of file */ + lseek(fd, 0L, SEEK_SET); + + /* read entire file into a buffer */ + read(fd, data, len); + + ptr = data; + + /* Loop and read all pids */ + while (1) { + /* Find new line */ + ptr2 = strchr(ptr, '\n'); + + /* clear the \n. And ptr should have a null-terminated decimal string + * Don't break from the loop though because the last line may not + * terminated with a \n + */ + if (NULL != ptr2) ptr2[0] = 0; + + /* If listing does not match our libname, add it to the array */ + if ((strlen(ptr)) && (NULL == strstr(ptr, LIB_NAME))) { + preloads[0] = (char **)realloc(preloads[0], (num_entries + 1)*sizeof(char **)); + preloads[0][num_entries] = strdup(ptr); + num_entries++; + } + + if (NULL == ptr2) break; /* No more PIDs */ + + /* Put ptr past the NULL we just wrote */ + ptr = ptr2 + 1; + } + + if (data) free(data); + + return num_entries; +} + + +void cleanup() +{ + int i = 0; + int success = 1; + int pids_file = -1, num_of_pids = 0; + int *pids_array = NULL; + char pid_string[255]; +#ifdef USE_LD_SO_PRELOAD + int preload_file = -1, num_of_preloads = 0; + char preload_entry[255]; + char **preload_array = NULL; +#endif + + + /* remove this sandbox's bash pid from the global pids + * file if it has rights to adapt the ld.so.preload file */ + if ((1 == preload_adaptable) && (0 == cleaned_up)) { + cleaned_up = 1; + success = 1; + + if (print_debug) printf("Cleaning up pids file.\n"); + + /* Stat the PIDs file, make sure it exists and is a regular file */ + if (file_exist(PIDS_FILE, 1) <= 0) { + perror(">>> pids file is not a regular file"); + success = 0; + /* We should really not fail if the pidsfile is missing here, but + * rather just exit cleanly, as there is still some cleanup to do */ + return; + } + + pids_file = file_open(PIDS_FILE, "r+", 0); + if (-1 == pids_file) { + success = 0; + /* Nothing more to do here */ + return; + } + + /* Load "still active" pids into an array */ + num_of_pids = load_active_pids(pids_file, &pids_array); + //printf("pids: %d\r\n", num_of_pids); + +#ifdef USE_LD_SO_PRELOAD + /* clean the /etc/ld.so.preload file if no other sandbox + * processes are running anymore */ + if (1 == num_of_pids) { + success = 1; + + if (print_debug) printf("Cleaning up /etc/ld.so.preload.\n"); + + preload_file = file_open("/etc/ld.so.preload", "r+", 0); + if (-1 != preload_file) { + /* Load all the preload libraries into an array */ + num_of_preloads = load_preload_libs(preload_file, &preload_array); + //printf("num preloads: %d\r\n", num_of_preloads); + /* Clear file */ + file_truncate(preload_file); + + /* store the other preload libraries back into the /etc/ld.so.preload file */ + if(num_of_preloads > 0) { + for (i = 0; i < num_of_preloads; i++) { + sprintf(preload_entry, "%s\n", preload_array[i]); + if (write(preload_file, preload_entry, strlen(preload_entry)) != strlen(preload_entry)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + } + + /* Free memory used to store preload array */ + for (i = 0; i < num_of_preloads; i++) { + if (preload_array[i]) free(preload_array[i]); + preload_array[i] = NULL; + } + if (preload_array) free(preload_array); + preload_array = NULL; + + file_close(preload_file); + preload_file = -1; + } + } +#endif + + file_truncate(pids_file); + + /* if pids are still running, write only the running pids back to the file */ + if(num_of_pids > 1) { + for (i = 0; i < num_of_pids; i++) { + sprintf(pid_string, "%d\n", pids_array[i]); + if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) { + perror(">>> pids file write"); + success = 0; + break; + } + } + + file_close(pids_file); + pids_file = -1; + } else { + + file_close(pids_file); + pids_file = -1; + + /* remove the pidsfile, as this was the last sandbox */ + unlink(PIDS_FILE); + } + + if (pids_array != NULL) { + free(pids_array); + pids_array = NULL; + } + } + + if (0 == success) { + return; + } +} + +void stop(int signum) +{ + printf("Caught signal %d\r\n", signum); + cleanup(); +} + +void setenv_sandbox_write(char *home_dir, char *portage_tmp_dir, char *var_tmp_dir, char *tmp_dir) +{ + char sandbox_write_var[1024]; + + if (!getenv(ENV_SANDBOX_WRITE)) { + /* these should go into make.globals later on */ + strcpy(sandbox_write_var, ""); + strcat(sandbox_write_var, "/dev/zero:/dev/fd/:/dev/null:/dev/pts/:/dev/vc/:/dev/tty:/tmp/"); + strcat(sandbox_write_var, ":"); + /* NGPT support */ + strcat(sandbox_write_var, "/dev/shm/ngpt"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/log/scrollkeeper.log"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, home_dir); + strcat(sandbox_write_var, "/.gconfd/lock"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, home_dir); + strcat(sandbox_write_var, "/.bash_history"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/tmp/conftest"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/lib/conftest"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/tmp/cf"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/lib/cf"); + strcat(sandbox_write_var, ":"); + if (NULL == portage_tmp_dir) { + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + + /* How the heck is this possible?? we just set it above! */ + } else if (0 == strcmp(sandbox_write_var, "/var/tmp/")) { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + + /* Still don't think this is possible, am I just stupid or something? */ + } else if (0 == strcmp(sandbox_write_var, "/tmp/")) { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + + /* Amazing, one I think is possible */ + } else { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + } + + setenv(ENV_SANDBOX_WRITE, sandbox_write_var, 1); + } +} + + +void setenv_sandbox_predict(char *home_dir) +{ + char sandbox_predict_var[1024]; + + if (!getenv(ENV_SANDBOX_PREDICT)) { + /* these should go into make.globals later on */ + strcpy(sandbox_predict_var, ""); + strcat(sandbox_predict_var, home_dir); + strcat(sandbox_predict_var, "/."); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.0/"); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.1/"); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.2/"); + setenv(ENV_SANDBOX_PREDICT, sandbox_predict_var, 1); + } +} + +int print_sandbox_log(char *sandbox_log) +{ + int sandbox_log_file = -1; + char *beep_count_env = NULL; + int i, beep_count = 0; + long len = 0; + char *buffer = NULL; + + sandbox_log_file=file_open(sandbox_log, "r", 0); + if (-1 == sandbox_log_file) { + return 0; + } + + len = file_length(sandbox_log_file); + buffer = (char *)malloc((len + 1)*sizeof(char)); + memset(buffer, 0, len + 1); + read(sandbox_log_file, buffer, len); + file_close(sandbox_log_file); + + printf("\e[31;01m--------------------------- ACCESS VIOLATION SUMMARY ---------------------------\033[0m\n"); + printf("\e[31;01mLOG FILE = \"%s\"\033[0m\n", sandbox_log); + printf("\n"); + printf("%s", buffer); + if (buffer) free(buffer); buffer = NULL; + printf("\e[31;01m--------------------------------------------------------------------------------\033[0m\n"); + + beep_count_env = getenv(ENV_SANDBOX_BEEP); + if (beep_count_env) { + beep_count = atoi(beep_count_env); + } else { + beep_count = DEFAULT_BEEP_COUNT; + } + + for (i = 0; i < beep_count; i++) { + fputc('\a', stderr); + if (i < beep_count -1) { + sleep(1); + } + } + return 1; +} + +int spawn_shell(char *argv_bash[]) +{ +#ifdef USE_SYSTEM_SHELL + int i = 0; + char *sh = NULL; + int first = 1; + int ret; + long len = 0; + + while (1) { + if (NULL == argv_bash[i]) break; + if (NULL != sh) len = strlen(sh); + sh = (char *)realloc(sh, len+strlen(argv_bash[i]) + 5); + if (first) { + sh[0] = 0; + first = 0; + } + strcat(sh, "\""); + strcat(sh, argv_bash[i]); + strcat(sh, "\" "); + + //printf("%s\n", argv_bash[i]); + i++; + } + printf("%s\n", sh); + ret = system(sh); + if (sh) free(sh); + sh = NULL; + + if (-1 == ret) return 0; + return 1; + +#else +# ifndef NO_FORK + int pid; + int status = 0; + int ret = 0; + + pid = fork(); + + /* Child's process */ + if (0 == pid) { +# endif + execv(argv_bash[0], argv_bash); +# ifndef NO_FORK + return 0; + } else if (pid < 0) { + return 0; + } + ret = waitpid(pid, &status, 0); + if ((-1 == ret) || (status > 0)) return 0; +# endif + return 1; +#endif +} + +int main(int argc, char** argv) +{ + int i = 0, success = 1; + int preload_file = -1; + int sandbox_log_presence = 0; + int sandbox_log_file = -1; + int pids_file = -1; + long len; + + int *pids_array = NULL; + int num_of_pids = 0; + + // char run_arg[255]; + char portage_tmp_dir[PATH_MAX]; + char var_tmp_dir[PATH_MAX]; + char tmp_dir[PATH_MAX]; + char sandbox_log[255]; + char sandbox_debug_log[255]; + char sandbox_dir[255]; + char sandbox_lib[255]; + char sandbox_rc[255]; + char pid_string[255]; + char **argv_bash = NULL; + + char *run_str = "-c"; + char *home_dir = NULL; + char *tmp_string = NULL; +#ifdef USE_LD_SO_PRELOAD + char **preload_array = NULL; + int num_of_preloads = 0; +#endif + + /* Only print info if called with no arguments .... */ + if (argc < 2) { + print_debug = 1; + } + + if (print_debug) printf("========================== Gentoo linux path sandbox ===========================\n"); + + + /* check if a sandbox is already running */ + if (NULL != getenv(ENV_SANDBOX_ON)) { + fprintf(stderr, "Not launching a new sandbox instance\nAnother one is already running in this process hierarchy.\n"); + exit(1); + } else { + + /* determine the location of all the sandbox support files */ + if (print_debug) printf("Detection of the support files.\n"); + + /* Generate base sandbox path */ + tmp_string = get_sandbox_path(argv[0]); + strncpy(sandbox_dir, tmp_string, 254); + if (tmp_string) free(tmp_string); + tmp_string = NULL; + strcat(sandbox_dir, "/"); + + /* Generate sandbox lib path */ + tmp_string = get_sandbox_lib(sandbox_dir); + strncpy(sandbox_lib, tmp_string, 254); + if (tmp_string) free(tmp_string); + tmp_string = NULL; + + /* Generate sandbox bashrc path */ + tmp_string = get_sandbox_rc(sandbox_dir); + strncpy(sandbox_rc, tmp_string, 254); + if (tmp_string) free(tmp_string); + tmp_string = NULL; + + /* verify the existance of required files */ + if (print_debug) printf("Verification of the required files.\n"); + + if (file_exist(sandbox_lib, 0) <= 0) { + fprintf(stderr, "Could not open the sandbox library at '%s'.\n", sandbox_lib); + return -1; + } else if (file_exist(sandbox_rc, 0) <= 0) { + fprintf(stderr, "Could not open the sandbox rc file at '%s'.\n", sandbox_rc); + return -1; + } + +#ifdef USE_LD_SO_PRELOAD + /* ensure that the /etc/ld.so.preload file contains an entry for the sandbox lib */ + if (print_debug) printf("Setting up the ld.so.preload file.\n"); +#endif + + /* check if the /etc/ld.so.preload is a regular file */ + if (file_exist("/etc/ld.so.preload", 1) < 0) { + fprintf(stderr, ">>> /etc/ld.so.preload file is not a regular file\n"); + exit(1); + } + + /* Our r+ also will create the file if it doesn't exist */ + preload_file=file_open("/etc/ld.so.preload", "r+", 1, 0644); + if (-1 == preload_file) { + preload_adaptable = 0; +/* exit(1);*/ + } + +#ifdef USE_LD_SO_PRELOAD + /* Load entries of preload table */ + num_of_preloads = load_preload_libs(preload_file, &preload_array); + + /* Zero out our ld.so.preload file */ + file_truncate(preload_file); + + /* Write contents of preload file */ + for (i = 0; i < num_of_preloads + 1; i++) { + /* First entry should be our sandbox library */ + if (0 == i) { + if (write(preload_file, sandbox_lib, strlen(sandbox_lib)) != strlen(sandbox_lib)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } else { + /* Output all other preload entries */ + if (write(preload_file, preload_array[i - 1], strlen(preload_array[i - 1])) != strlen(preload_array[i - 1])) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + /* Don't forget the return character after each line! */ + if (1 != write(preload_file, "\n", 1)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + + for (i = 0; i < num_of_preloads; i++) { + if (preload_array[i]) free(preload_array[i]); + preload_array[i] = NULL; + } + if (preload_array) free(preload_array); + num_of_preloads = 0; + preload_array = NULL; +#endif + + /* That's all we needed to do with the preload file */ + file_close(preload_file); + preload_file = -1; + + /* set up the required environment variables */ + if (print_debug) printf("Setting up the required environment variables.\n"); + + /* Generate sandbox log full path */ + tmp_string=get_sandbox_log(); + strncpy(sandbox_log, tmp_string, 254); + if (tmp_string) free(tmp_string); + tmp_string = NULL; + + setenv(ENV_SANDBOX_LOG, sandbox_log, 1); + + snprintf(sandbox_debug_log, 254, "%s%s%s", DEBUG_LOG_FILE_PREFIX, pid_string, LOG_FILE_EXT); + setenv(ENV_SANDBOX_DEBUG_LOG, sandbox_debug_log, 1); + + home_dir = getenv("HOME"); + + /* drobbins: we need to expand these paths using realpath() so that PORTAGE_TMPDIR + * can contain symlinks (example, /var is a symlink, /var/tmp is a symlink.) Without + * this, access is denied to /var/tmp, hurtin' ebuilds. + */ + + realpath(getenv("PORTAGE_TMPDIR"),portage_tmp_dir); + realpath("/var/tmp",var_tmp_dir); + realpath("/tmp",tmp_dir); + + setenv(ENV_SANDBOX_DIR, sandbox_dir, 1); + setenv(ENV_SANDBOX_LIB, sandbox_lib, 1); + setenv("LD_PRELOAD", sandbox_lib, 1); + + if (!getenv(ENV_SANDBOX_DENY)) { + setenv(ENV_SANDBOX_DENY, LD_PRELOAD_FILE, 1); + } + + if (!getenv(ENV_SANDBOX_READ)) { + setenv(ENV_SANDBOX_READ, "/", 1); + } + + /* Set up Sandbox Write path */ + setenv_sandbox_write(home_dir, portage_tmp_dir, var_tmp_dir, tmp_dir); + setenv_sandbox_predict(home_dir); + + setenv(ENV_SANDBOX_ON, "1", 0); + + /* if the portage temp dir was present, cd into it */ + if (NULL != portage_tmp_dir) { + chdir(portage_tmp_dir); + } + + argv_bash=(char **)malloc(6 * sizeof(char *)); + argv_bash[0] = strdup("/bin/bash"); + argv_bash[1] = strdup("-rcfile"); + argv_bash[2] = strdup(sandbox_rc); + if (argc < 2) { + argv_bash[3] = NULL; + } else { + argv_bash[3] = strdup(run_str); /* "-c" */ + } + argv_bash[4] = NULL; /* strdup(run_arg); */ + argv_bash[5] = NULL; + + if (argc >= 2) { + for (i = 1; i< argc; i++) { + if (NULL == argv_bash[4]) len = 0; + else len = strlen(argv_bash[4]); + argv_bash[4]=(char *)realloc(argv_bash[4], (len + strlen(argv[i]) + 2) * sizeof(char)); + if (0 == len) argv_bash[4][0] = 0; + if (1 != i) strcat(argv_bash[4], " "); + strcat(argv_bash[4], argv[i]); + } + } +#if 0 + char* argv_bash[] = { + "/bin/bash", + "-rcfile", + NULL, + NULL, + NULL, + NULL + }; + + /* adding additional bash arguments */ + for (i = 1; i < argc; i++) { + if (1 == i) { + argv_bash[3] = run_str; + argv_bash[4] = run_arg; + strcpy(argv_bash[4], argv[i]); + } else { + strcat(argv_bash[4], " "); + strcat(argv_bash[4], argv[i]); + } + } +#endif + + /* set up the required signal handlers */ + signal(SIGHUP, &stop); + signal(SIGINT, &stop); + signal(SIGQUIT, &stop); + signal(SIGTERM, &stop); + + /* this one should NEVER be set in ebuilds, as it is the one + * private thing libsandbox.so use to test if the sandbox + * should be active for this pid, or not. + * + * azarah (3 Aug 2002) + */ + + setenv("SANDBOX_ACTIVE", "armedandready", 1); + + + /* Load our PID into PIDs file if environment is adaptable */ + if (preload_adaptable) { + success = 1; + if (file_exist(PIDS_FILE, 1) < 0) { + success = 0; + fprintf(stderr, ">>> pids file is not a regular file"); + } else { + pids_file=file_open(PIDS_FILE, "r+", 1, 0644); + if (-1 == pids_file) { + success = 0; + } else { + /* Grab still active pids */ + num_of_pids = load_active_pids(pids_file, &pids_array); + + /* Zero out file */ + file_truncate(pids_file); + + /* Output active pids, and append our pid */ + for (i = 0; i < num_of_pids + 1; i++) { + /* Time for our entry */ + if (i == num_of_pids) { + sprintf(pid_string, "%d\n", getpid()); + } else { + sprintf(pid_string, "%d\n", pids_array[i]); + } + if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + /* Clean pids_array */ + if (pids_array) free(pids_array); + pids_array = NULL; + num_of_pids = 0; + + /* We're done with the pids file */ + file_close(pids_file); + } + } + + /* Something went wrong, bail out */ + if (success == 0) + exit(1); + } + + /* STARTING PROTECTED ENVIRONMENT */ + if (print_debug) { + printf("The protected environment has been started.\n"); + printf("--------------------------------------------------------------------------------\n"); + } + + if (print_debug) printf("Shell being started in forked process.\n"); + + /* Start Bash */ + if (!spawn_shell(argv_bash)) { + if (print_debug) fprintf(stderr, ">>> shell process failed to spawn\n"); + success = 0; + } + + /* Free bash stuff */ + for (i = 0; i < 6; i++) { + if (argv_bash[i]) free(argv_bash[i]); + argv_bash[i] = NULL; + } + if (argv_bash) free(argv_bash); + argv_bash = NULL; + + if (print_debug) { + printf("Cleaning up sandbox process\n"); + } + + cleanup(); + + if (print_debug) { + printf("========================== Gentoo linux path sandbox ===========================\n"); + printf("The protected environment has been shut down.\n"); + } + + if (file_exist(sandbox_log, 0)) { + sandbox_log_presence = 1; + success = 1; + if (!print_sandbox_log(sandbox_log)) { + success = 0; + } + +#if 0 + if (!success) { + exit(1); + } +#endif + sandbox_log_file = -1; + } else if (print_debug) { + printf("--------------------------------------------------------------------------------\n"); + } + + if ((sandbox_log_presence) || (!success)) { + return 1; + } else { + return 0; + } + } +} + + + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/sandbox.h b/src/sandbox-dev/sandbox.h new file mode 100644 index 00000000..d750fdd7 --- /dev/null +++ b/src/sandbox-dev/sandbox.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2002 Brad House , + * Possibly based on code from Geert Bevin, Uwyn, http://www.uwyn.com + * Distributed under the terms of the GNU General Public License, v2 or later + * Author: Brad House + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/sandbox.h,v 1.2 2002/12/04 18:11:32 azarah Exp $ + */ + +#ifndef __SANDBOX_H__ +#define __SANDBOX_H__ + +/* Uncomment below to use flock instead of fcntl (POSIX way) to lock/unlock files */ +/* #define USE_FLOCK */ + +/* Uncomment below to use system() to execute the shell rather than execv */ +/* #define USE_SYSTEM_SHELL */ + +/* Uncomment below to use /etc/ld.so.preload (could be very intrusive!!) */ +/* #define USE_LD_SO_PRELOAD */ + +/* Uncommend to not have the protected shell forked, just run in parent process */ +/* ONLY FOR DEBUGGING PURPOSES!! (strace needs it like that) */ +/* #define NO_FORK */ + + +#define LD_PRELOAD_FILE "/etc/ld.so.preload" +#define LIB_NAME "libsandbox.so" +#define BASHRC_NAME "sandbox.bashrc" +#define PIDS_FILE "/tmp/sandboxpids.tmp" +#define LOG_FILE_PREFIX "/tmp/sandbox-" +#define DEBUG_LOG_FILE_PREFIX "/tmp/sandbox-debug-" +#define LOG_FILE_EXT ".log" + +#define ENV_SANDBOX_DEBUG_LOG "SANDBOX_DEBUG_LOG" +#define ENV_SANDBOX_LOG "SANDBOX_LOG" +#define ENV_SANDBOX_DIR "SANDBOX_DIR" +#define ENV_SANDBOX_LIB "SANDBOX_LIB" + +#define ENV_SANDBOX_DENY "SANDBOX_DENY" +#define ENV_SANDBOX_READ "SANDBOX_READ" +#define ENV_SANDBOX_WRITE "SANDBOX_WRITE" +#define ENV_SANDBOX_PREDICT "SANDBOX_PREDICT" + +#define ENV_SANDBOX_ON "SANDBOX_ON" +#define ENV_SANDBOX_BEEP "SANDBOX_BEEP" + +#define DEFAULT_BEEP_COUNT 3 + +char *get_sandbox_path(char *argv0); +char *get_sandbox_lib(char *sb_path); +char *get_sandbox_rc(char *sb_path); +char *get_sandbox_log(); +char *sb_dirname(const char *path); +int file_getmode(char *mode); +long file_tell(int fp); +int file_lock(int fd, int lock, char *filename); +int file_unlock(int fd); +int file_locktype(char *mode); +int file_open(char *filename, char *mode, int perm_specified, ...); +void file_close(int fd); +long file_length(int fd); +int file_truncate(int fd); +int file_exist(char *filename, int checkmode); + +#endif + + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox-dev/sandbox_futils.c b/src/sandbox-dev/sandbox_futils.c new file mode 100644 index 00000000..e2aab3f6 --- /dev/null +++ b/src/sandbox-dev/sandbox_futils.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2002 Brad House + * Distributed under the terms of the GNU General Public License, v2 or later + * Author: Brad House + * + * $Header: /var/cvsroot/gentoo-src/portage/src/sandbox-dev/Attic/sandbox_futils.c,v 1.3 2002/12/04 18:11:32 azarah Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sandbox.h" + + +char *get_sandbox_path(char *argv0) +{ + char path[255]; + char *cwd = NULL; + + /* ARGV[0] specifies full path */ + if (argv0[0] == '/') { + strncpy(path, argv0, 254); + + /* ARGV[0] specifies relative path */ + } else { + getcwd(cwd, 253); + sprintf(path, "%s/%s", cwd, argv0); + if (cwd) free(cwd); + cwd = NULL; + } + + /* Return just directory */ + return(sb_dirname(path)); +} + +char *get_sandbox_lib(char *sb_path) +{ + char path[255]; + + snprintf(path, 254, "/lib/%s", LIB_NAME); + if (file_exist(path, 0) <= 0) { + snprintf(path, 254, "%s%s", sb_path, LIB_NAME); + } + return(strdup(path)); +} + +char *get_sandbox_rc(char *sb_path) +{ + char path[255]; + + snprintf(path, 254, "/usr/lib/portage/lib/%s", BASHRC_NAME); + if (file_exist(path, 0) <= 0) { + snprintf(path, 254, "%s%s", sb_path, BASHRC_NAME); + } + return(strdup(path)); +} + +char *get_sandbox_log() +{ + char path[255]; + char pid_string[20]; + char *sandbox_log_env = NULL; + + sprintf(pid_string, "%d", getpid()); + + strcpy(path, LOG_FILE_PREFIX); + sandbox_log_env = getenv(ENV_SANDBOX_LOG); + if (sandbox_log_env) { + strcat(path, sandbox_log_env); + strcat(path, "-"); + } + strcat(path, pid_string); + strcat(path, LOG_FILE_EXT); + return(strdup(path)); +} + +/* Obtain base directory name. Do not allow trailing / */ +char *sb_dirname(const char *path) +{ + char *ret = NULL; + char *ptr = NULL; + int loc = 0, i; + int cut_len = -1; + + /* don't think NULL will ever be passed, but just in case */ + if (NULL == path) return(strdup(".")); + + /* Grab pointer to last slash */ + ptr = strrchr(path, '/'); + if (NULL == ptr) { + return(strdup(".")); + } + + /* decimal location of pointer */ + loc = ptr - path; + + /* Remove any trailing slash */ + for (i = loc-1; i >= 0; i--) { + if (path[i] != '/') { + cut_len = i + 1; /* make cut_len the length of the string to keep */ + break; + } + } + + /* It could have been just a plain /, return a 1byte 0 filled string */ + if (-1 == cut_len) return(strdup("")); + + /* Allocate memory, and return the directory */ + ret = (char *)malloc((cut_len + 1) * sizeof(char)); + memcpy(ret, path, cut_len); + ret[cut_len] = 0; + + return(ret); +} + +/* +char* dirname(const char* path) +{ + char* base = NULL; + unsigned int length = 0; + + base = strrchr(path, '/'); + if (NULL == base) + { + return strdup("."); + } + while (base > path && *base == '/') + { + base--; + } + length = (unsigned int) 1 + base - path; + + base = malloc(sizeof(char)*(length+1)); + memmove(base, path, length); + base[length] = 0; + + return base; +}*/ + +/* Convert text (string) modes to integer values */ +int file_getmode(char *mode) +{ + int mde = 0; + if (0 == strcasecmp(mode, "r+")) { + mde = O_RDWR | O_CREAT; + } else if (0 == strcasecmp(mode, "w+")) { + mde = O_RDWR | O_CREAT | O_TRUNC; + } else if (0 == strcasecmp(mode, "a+")) { + mde = O_RDWR | O_CREAT | O_APPEND; + } else if (0 == strcasecmp(mode, "r")) { + mde = O_RDONLY; + } else if (0 == strcasecmp(mode, "w")) { + mde = O_WRONLY | O_CREAT | O_TRUNC; + } else if (0 == strcasecmp(mode, "a")) { + mde = O_WRONLY | O_APPEND | O_CREAT; + } else { + mde = O_RDONLY; + } + return(mde); +} + +/* Get current position in file */ +long file_tell(int fp) +{ + return(lseek(fp, 0L, SEEK_CUR)); +} + +/* lock the file, preferrably the POSIX way */ +int file_lock(int fd, int lock, char *filename) +{ + int err; +#ifdef USE_FLOCK + if (flock(fd, lock) < 0) { + err = errno; + fprintf(stderr, ">>> %s flock file lock: %s\n", filename, strerror(err)); + return 0; + } +#else + struct flock fl; + fl.l_type = lock; + fl.l_whence = SEEK_SET; + fl.l_start = 0L; + fl.l_len = 0L; + fl.l_pid = getpid(); + if (fcntl(fd, F_SETLKW, &fl) < 0) { + err = errno; + fprintf(stderr, ">>> %s fcntl file lock: %s\n", filename, strerror(err)); + return 0; + } +#endif + return 1; +} + +/* unlock the file, preferrably the POSIX way */ +int file_unlock(int fd) +{ +#ifdef USE_FLOCK + if (flock(fd, LOCK_UN) < 0) { + perror(">>> flock file unlock"); + return 0; + } +#else + struct flock fl; + fl.l_type = F_UNLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0L; + fl.l_len = 0L; + fl.l_pid = getpid(); + if (fcntl(fd, F_SETLKW, &fl) < 0) { + perror(">>> fcntl file unlock"); + return 0; + } +#endif + return 1; +} + +/* Auto-determine from how the file was opened, what kind of lock to lock + * the file with + */ +int file_locktype(char *mode) +{ +#ifdef USE_FLOCK + if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) || (NULL != strchr(mode, 'a'))) + return(LOCK_EX); + return(LOCK_SH); +#else + if (NULL != (strchr(mode, 'w')) || (NULL != strchr(mode, '+')) || (NULL != strchr(mode, 'a'))) + return(F_WRLCK); + return(F_RDLCK); +#endif +} + +/* Use standard fopen style modes to open the specified file. Also auto-determines and + * locks the file either in shared or exclusive mode depending on opening mode + */ +int file_open(char *filename, char *mode, int perm_specified, ...) +{ + int fd; + char error[250]; + va_list ap; + int perm; + + if (perm_specified) { + va_start(ap, perm_specified); + perm = va_arg(ap, int); + va_end(ap); + } + if (perm_specified) { + fd = open(filename, file_getmode(mode), perm); + } else { + fd = open(filename, file_getmode(mode)); + } + if (-1 == fd) { + snprintf(error, 249, ">>> %s file mode: %s open", filename, mode); + perror(error); + return(fd); + } + /* Only lock the file if opening succeeded */ + if (-1 != fd) { + if (0 == file_lock(fd, file_locktype(mode), filename)) { + close(fd); + return -1; + } + } else { + snprintf(error, 249, ">>> %s file mode:%s open", filename, mode); + perror(error); + } + return(fd); +} + +/* Close and unlock file */ +void file_close(int fd) +{ + if (-1 != fd) { + file_unlock(fd); + close(fd); + } +} + +/* Return length of file */ +long file_length(int fd) +{ + long pos, len; + pos = file_tell(fd); + len = lseek(fd, 0L, SEEK_END); + lseek(fd, pos, SEEK_SET); + return(len); +} + +/* Zero out file */ +int file_truncate(int fd) +{ + lseek(fd, 0L, SEEK_SET); + if (ftruncate(fd, 0) < 0) { + perror(">>> file truncate"); + return 0; + } + return 1; +} + +/* Check to see if a file exists Return: 1 success, 0 file not found, -1 error */ +int file_exist(char *filename, int checkmode) +{ + struct stat mystat; + + /* Verify file exists and is regular file (not sym link) */ + if (checkmode) { + if (-1 == lstat(filename, &mystat)) { + /* file doesn't exist */ + if (ENOENT == errno) { + return 0; + } else { /* permission denied or other error */ + perror(">>> stat file"); + return -1; + } + } + if (!S_ISREG(mystat.st_mode)) + return -1; + + /* Just plain verify the file exists */ + } else { + if (-1 == stat(filename, &mystat)) { + /* file does not exist */ + if (ENOENT == errno) { + return 0; + } else { /* permission denied or other error */ + perror(">>> stat file"); + return -1; + } + } + } + + return 1; +} + + +// vim:expandtab noai:cindent ai diff --git a/src/sandbox/Makefile b/src/sandbox/Makefile new file mode 100644 index 00000000..9c8fe960 --- /dev/null +++ b/src/sandbox/Makefile @@ -0,0 +1,30 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# +# Modified 15 Apr 2002 Jon Nelson +# Clean up Makefile somewhat, and use make's implicit rules +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/Attic/Makefile,v 1.5 2002/08/05 16:44:34 drobbins Exp $ + +.SUFFIXES: +.SUFFIXES: .c .o .so +.PRECIOUS: %.o + +%.so: LIBS=-ldl +%.so: LDFLAGS=--shared +%.so: %.o + $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ $(LIBS) $(LDFLAGS) + +CC = gcc +CFLAGS = -Wall -O0 -fPIC +LIBS = +LDFLAGS = + +TARGETS = sandbox libsandbox.so + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) + rm -f *.o *~ diff --git a/src/sandbox/libsandbox.c b/src/sandbox/libsandbox.c new file mode 100644 index 00000000..d850554c --- /dev/null +++ b/src/sandbox/libsandbox.c @@ -0,0 +1,873 @@ +/* +** Path sandbox for the gentoo linux portage package system, initially +** based on the ROCK Linux Wrapper for getting a list of created files +** +** to integrate with bash, bash should have been built like this +** +** ./configure --prefix= --host= --without-gnu-malloc +** +** it's very important that the --enable-static-link option is NOT specified +** +** Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +** Distributed under the terms of the GNU General Public License, v2 or later +** Author : Geert Bevin +** $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/Attic/libsandbox.c,v 1.8 2002/08/05 05:51:39 drobbins Exp $ +*/ + +#define _GNU_SOURCE +#define _REENTRANT + +#define open xxx_open +#define open64 xxx_open64 +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#undef open +#undef open64 + +#define PIDS_FILE "/tmp/sandboxpids.tmp" + +typedef struct { + int show_access_violation; + char** deny_prefixes; + int num_deny_prefixes; + char** read_prefixes; + int num_read_prefixes; + char** write_prefixes; + int num_write_prefixes; + char** predict_prefixes; + int num_predict_prefixes; + char** write_denied_prefixes; + int num_write_denied_prefixes; +} sbcontext_t; + +int check_access(sbcontext_t*, const char*, const char*); +int check_syscall(sbcontext_t*, const char*, const char*); +int before_syscall(const char*, const char*); +int before_syscall_open_int(const char*, const char*, int); +int before_syscall_open_char(const char*, const char*, const char*); +void clean_env_entries(char***, int*); +char* filter_path(const char*); +void* get_dl_symbol(char*); +void init_context(sbcontext_t*); +void init_env_entries(char***, int*, char*, int); +int is_sandbox_on(); +int is_sandbox_pid(); + +/* Wrapper macros and functions */ + +/* macro definition to wrap functions before and after the + execution of basic file related system-calls. + + nr : the argument number of the system-call's argument that + contains the file name to monitor + rt : the return type of the system call + name : the name of the function call + arg1, arg2, arg3 : the types of the function call's arguments + fl : the argument number of the system-call's argument that + contains the file access flags + md : the argument number of the system-call's argument that + contains the file access mode +*/ +#define wrsysc3(nr, rt, name, arg1, arg2, arg3) \ + \ +/* the function call is defined externally from this file */ \ +extern rt name(arg1, arg2, arg3); \ + \ +/* orig_ ## name is a pointer to a function with three arguments and the + return type of the system call. This will be used to store the pointer + to the system call function and call it. */ \ +rt (*orig_ ## name)(arg1, arg2, arg3) = NULL; \ + \ +rt name(arg1 a1, arg2 a2, arg3 a3) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2, a3); \ + } \ + return result; \ +} + +#define wrsysc2(nr, rt, name, arg1, arg2) \ +extern rt name(arg1, arg2); \ +rt (*orig_ ## name)(arg1, arg2) = NULL; \ + \ +rt name(arg1 a1, arg2 a2) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2); \ + } \ + return result; \ +} + +#define wrsysc1(nr, rt, name, arg1) \ +extern rt name(arg1); \ +rt (*orig_ ## name)(arg1) = NULL; \ + \ +rt name(arg1 a1) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1); \ + } \ + return result; \ +} + +#define wrsysc1ptr(nr, rt, name, arg1) \ +extern rt name(arg1); \ +rt (*orig_ ## name)(arg1) = NULL; \ + \ +rt name(arg1 a1) \ +{ \ + rt result = NULL; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1); \ + } \ + return result; \ +} + +#define wropenint3(nr, fl, rt, name, arg1, arg2, arg3) \ +extern rt name(arg1, arg2, arg3); \ +rt (*orig_ ## name)(arg1, arg2, arg3) = NULL; \ + \ +rt name(arg1 a1, arg2 a2, arg3 a3) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || \ + 1 == before_syscall_open_int(#name, a ## nr, a ## fl)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2, a3); \ + } \ + return result; \ +} + +#define wropenchar2(nr, md, rt, name, arg1, arg2) \ +extern rt name(arg1, arg2); \ +rt (*orig_ ## name)(arg1, arg2) = NULL; \ + \ +rt name(arg1 a1, arg2 a2) \ +{ \ + rt result = NULL; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || \ + 1 == before_syscall_open_char(#name, a ## nr, a ## md)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2); \ + } \ + return result; \ +} + +#define wropenchar3(nr, md, rt, name, arg1, arg2, arg3) \ +extern rt name(arg1, arg2, arg3); \ +rt (*orig_ ## name)(arg1, arg2, arg3) = NULL; \ + \ +rt name(arg1 a1, arg2 a2, arg3 a3) \ +{ \ + rt result = NULL; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || \ + 1 == before_syscall_open_char(#name, a ## nr, a ## md)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2, a3); \ + } \ + return result; \ +} + +#define wrexec3(nr, rt, name, arg1, arg2, arg3) \ +extern rt name(arg1, arg2, arg3); \ +rt (*orig_ ## name)(arg1, arg2, arg3) = NULL; \ + \ +rt name(arg1 a1, arg2 a2, arg3 a3) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2, a3); \ + } \ + return result; \ +} + +#define wrexec2(nr, rt, name, arg1, arg2) \ +extern rt name(arg1, arg2); \ +rt (*orig_ ## name)(arg1, arg2) = NULL; \ + \ +rt name(arg1 a1, arg2 a2) \ +{ \ + rt result = -1; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = orig_ ## name(a1, a2); \ + } \ + return result; \ +} + +#define wrexec2va(nr, rt, name, arg1, arg2) \ +extern rt name(arg1, arg2, ...); \ +rt (*orig_ ## name)(arg1, arg2, ...) = NULL; \ + \ +rt name(arg1 a1, arg2 a2, ...) \ +{ \ + void* result = NULL; \ + int old_errno = errno; \ + if (0 == is_sandbox_on() || 1 == before_syscall(#name, a ## nr)) \ + { \ + if (!orig_ ## name) \ + { \ + orig_ ## name = get_dl_symbol(#name); \ + } \ + errno = old_errno; \ + result = __builtin_apply( (void(*)()) orig_ ## name, \ + __builtin_apply_args(), 32 ); \ + old_errno = errno; \ + } \ + if (NULL == result) \ + { \ + return -1; \ + } \ + else \ + { \ + __builtin_return(result); \ + } \ +} + +wropenint3(1, 2, int, open, const char*, int, mode_t) +wropenint3(1, 2, int, open64, const char*, int, mode_t) + +wropenchar2(1, 2, FILE*, fopen, const char*, const char*) +wropenchar2(1, 2, FILE*, fopen64, const char*, const char*) +wropenchar3(1, 2, FILE*, freopen, const char*, const char*, FILE*) + +wropenchar2(1, 2, FILE*, popen, const char*, const char*) + +// write syscalls + +wrsysc2(1, int, creat, const char*, mode_t) +wrsysc2(1, int, creat64, const char*, mode_t) + +wrsysc2(1, int, mkdir, const char*, mode_t) +wrsysc3(1, int, mknod, const char*, mode_t, dev_t) +wrsysc2(1, int, mkfifo, const char*, mode_t) + +wrsysc2(2, int, link, const char*, const char*) +wrsysc2(2, int, symlink, const char*, const char*) +wrsysc2(2, int, rename, const char*, const char*) + +wrsysc2(1, int, utime, const char*, const struct utimbuf*) +wrsysc2(1, int, utimes, const char*, struct timeval*) + +wrsysc1(1, int, unlink, const char*) +wrsysc1(1, int, rmdir, const char*) + +wrsysc3(1, int, chown, const char*, uid_t, gid_t) +wrsysc3(1, int, lchown, const char*, uid_t, gid_t) + +wrsysc2(1, int, chmod, const char*, mode_t) + +/* read syscalls */ + +wrsysc1ptr(1, DIR*, opendir, const char*) + +/* execution syscalls */ +wrsysc1(1, int, system, const char*) + +wrexec2va(1, int, execl, const char*, const char*) +wrexec2va(1, int, execle, const char*, const char*) +wrexec2(1, int, execv, const char*, char* const*) +wrexec3(1, int, execve, const char*, char* const*, char* const*) +/* execlp is redirected to execvp */ +/* execvp is special since it should search the PATH var entries */ +extern int execvp(const char*, char* const*); +int(*orig_execvp)(const char*, char* const*) = NULL; +int execvp(const char* file, char* const* argv) +{ + int result = -1; + int old_errno = errno; + int i = 0; + int allowed = 1; + char** path_entries = NULL; + int num_path_entries = 0; + char constructed_path[255]; + + if (1 == is_sandbox_on()) + { + init_env_entries(&path_entries, &num_path_entries, "PATH", 0); + for (i = 0; i < num_path_entries; i++) + { + strcpy(constructed_path, path_entries[i]); + strcat(constructed_path, "/"); + strcat(constructed_path, file); + if (0 == before_syscall("execvp", constructed_path)) + { + allowed = 0; + break; + } + } + clean_env_entries(&path_entries, &num_path_entries); + } + + if (1 == allowed) + { + if (!orig_execvp) + { + orig_execvp = get_dl_symbol("execvp"); + } + errno = old_errno; + result = orig_execvp(file, argv); + old_errno = errno; + } + errno = old_errno; + return result; +} + +/* lseek, lseek64, fdopen, fchown, fchmod, fcntl, lockf + are not wrapped since they can't be used if open is wrapped correctly + and unaccessible file descriptors are not possible to create */ + +void* get_dl_symbol(char* symname) +{ + void* result = dlsym(RTLD_NEXT, symname); + if (0 == result) + { + fprintf(stderr, "Sandbox : can't resolve %s: %s.\n", symname, dlerror()); + abort(); + } + return result; +} + +void init_context(sbcontext_t* context) +{ + context->show_access_violation = 1; + context->deny_prefixes = NULL; + context->num_deny_prefixes = 0; + context->read_prefixes = NULL; + context->num_read_prefixes = 0; + context->write_prefixes = NULL; + context->num_write_prefixes = 0; + context->predict_prefixes = NULL; + context->num_predict_prefixes = 0; + context->write_denied_prefixes = NULL; + context->num_write_denied_prefixes = 0; +} + +int is_sandbox_pid() +{ + int result = 0; + FILE* pids_stream = NULL; + int pids_file = -1; + int current_pid = 0; + int tmp_pid = 0; + + pids_stream = fopen(PIDS_FILE, "r"); + if (NULL == pids_stream) + { + perror(">>> pids file fopen"); + } + else + { + pids_file = fileno(pids_stream); + if (pids_file < 0) + { + perror(">>> pids file fileno"); + } + else + { + current_pid = getpid(); + + while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) + { + if (tmp_pid == current_pid) + { + result = 1; + break; + } + } + } + if (EOF == fclose(pids_stream)) + { + perror(">>> pids file fclose"); + } + pids_stream = NULL; + pids_file = -1; + } + + return result; +} + +void clean_env_entries(char*** prefixes_array, int* prefixes_num) +{ + int i = 0; + if (NULL != *prefixes_array) + { + for (i = 0; i < *prefixes_num; i++) + { + if (NULL != (*prefixes_array)[i]) + { + free((*prefixes_array)[i]); + (*prefixes_array)[i] = NULL; + } + } + free(*prefixes_array); + *prefixes_array = NULL; + + *prefixes_num = 0; + } +} + +void init_env_entries(char*** prefixes_array, int* prefixes_num, char* env, int warn) +{ + char* prefixes_env = getenv(env); + + if (NULL == prefixes_env) + { + fprintf(stderr, "Sandbox error : the %s environmental variable should be defined.\n", env); + } + else + { + char* buffer = NULL; + int prefixes_env_length = strlen(prefixes_env); + int i = 0; + int num_delimiters = 0; + char* token = NULL; + char* prefix = NULL; + + for (i = 0; i < prefixes_env_length; i++) + { + if (':' == prefixes_env[i]) + { + num_delimiters++; + } + } + + if (num_delimiters > 0) + { + buffer = (char*)malloc(sizeof(char)*(prefixes_env_length+1)); + *prefixes_array = (char**)malloc(sizeof(char*)*(num_delimiters+1)); + + strcpy(buffer, prefixes_env); + token = strtok(buffer, ":"); + while (NULL != token && + strlen(token) > 0) + { + prefix = (char*)malloc(sizeof(char)*(strlen(token)+1)); + strcpy(prefix, token); + (*prefixes_array)[(*prefixes_num)++] = filter_path(prefix); + free(prefix); + token = strtok(NULL, ":"); + } + free(buffer); + buffer = NULL; + } + else if(prefixes_env_length > 0) + { + (*prefixes_array) = (char**)malloc(sizeof(char*)); + + prefix = (char*)malloc(sizeof(char)*(prefixes_env_length+1)); + strcpy(prefix, prefixes_env); + (*prefixes_array)[(*prefixes_num)++] = filter_path(prefix); + free(prefix); + } + } +} + +char* filter_path(const char* path) +{ + int initial_path_length = strlen(path); + char* filtered_path = (char*)malloc(sizeof(char)*(initial_path_length+1)); + int i = 0; + int j = 0; + + for (i = 0, j = 0; i < initial_path_length;) + { + filtered_path[j] = path[i]; + if ('/' == filtered_path[j]) + { + while ('/' == path[i] && + i < initial_path_length) + { + i++; + } + } + else + { + i++; + } + j++; + } + filtered_path[j] = 0; + + return filtered_path; +} + +int check_access(sbcontext_t* sbcontext, const char* func, const char* path) +{ + int result = -1; + int i = 0; + char* filtered_path = filter_path(path); + + if ('/' != path[0]) + { + return 0; + } + + if (0 == strcmp(filtered_path, "/etc/ld.so.preload") && + is_sandbox_pid()) + { + result = 1; + } + + if (-1 == result) + { + if (NULL != sbcontext->deny_prefixes) + { + for (i = 0; i < sbcontext->num_deny_prefixes; i++) + { + if (0 == strncmp(filtered_path, sbcontext->deny_prefixes[i], strlen(sbcontext->deny_prefixes[i]))) + { + result = 0; + break; + } + } + } + + if (-1 == result) + { + if (NULL != sbcontext->read_prefixes && + (0 == strcmp(func, "open_rd") || + 0 == strcmp(func, "popen") || + 0 == strcmp(func, "opendir") || + 0 == strcmp(func, "system") || + 0 == strcmp(func, "execl") || + 0 == strcmp(func, "execlp") || + 0 == strcmp(func, "execle") || + 0 == strcmp(func, "execv") || + 0 == strcmp(func, "execvp") || + 0 == strcmp(func, "execve"))) + { + for (i = 0; i < sbcontext->num_read_prefixes; i++) + { + if (0 == strncmp(filtered_path, sbcontext->read_prefixes[i], strlen(sbcontext->read_prefixes[i]))) + { + result = 1; + break; + } + } + } + else if (NULL != sbcontext->write_prefixes && + (0 == strcmp(func, "open_wr") || + 0 == strcmp(func, "creat") || + 0 == strcmp(func, "creat64") || + 0 == strcmp(func, "mkdir") || + 0 == strcmp(func, "mknod") || + 0 == strcmp(func, "mkfifo") || + 0 == strcmp(func, "link") || + 0 == strcmp(func, "symlink") || + 0 == strcmp(func, "rename") || + 0 == strcmp(func, "utime") || + 0 == strcmp(func, "utimes") || + 0 == strcmp(func, "unlink") || + 0 == strcmp(func, "rmdir") || + 0 == strcmp(func, "chown") || + 0 == strcmp(func, "lchown") || + 0 == strcmp(func, "chmod"))) + { + struct stat tmp_stat; + + for (i = 0; i < sbcontext->num_write_denied_prefixes; i++) + { + if (0 == strncmp(filtered_path, sbcontext->write_denied_prefixes[i], strlen(sbcontext->write_denied_prefixes[i]))) + { + result = 0; + break; + } + } + if (-1 == result) + { + for (i = 0; i < sbcontext->num_write_prefixes; i++) + { + if (0 == strncmp(filtered_path, sbcontext->write_prefixes[i], strlen(sbcontext->write_prefixes[i]))) + { + result = 1; + break; + } + } + + if (-1 == result) + { + /* hack to prevent mkdir of existing dirs to show errors */ + if (strcmp(func, "mkdir") == 0) + { + if (0 == stat(filtered_path, &tmp_stat)) + { + sbcontext->show_access_violation = 0; + result = 0; + } + } + + if (-1 == result) + { + for (i = 0; i < sbcontext->num_predict_prefixes; i++) + { + if (0 == strncmp(filtered_path, sbcontext->predict_prefixes[i], strlen(sbcontext->predict_prefixes[i]))) + { + sbcontext->show_access_violation = 0; + result = 0; + break; + } + } + } + } + } + } + } + } + + if (-1 == result) + { + result = 0; + } + + free(filtered_path); + + return result; +} + +int check_syscall(sbcontext_t* sbcontext, const char* func, const char* file) +{ + int result = 1; + char* absolute_path = NULL; + char* tmp_buffer = NULL; + struct stat log_stat; + char* log_path = NULL; + int log_file = 0; + struct stat debug_log_stat; + char* debug_log_env = NULL; + char* debug_log_path = NULL; + int debug_log_file = 0; + char buffer[512]; + + if ('/' == file[0]) + { + absolute_path = (char*)malloc(sizeof(char)*(strlen(file)+1)); + sprintf(absolute_path, "%s", file); + } + else + { + tmp_buffer = get_current_dir_name(); + absolute_path = (char*)malloc(sizeof(char)*(strlen(tmp_buffer)+1+strlen(file)+1)); + sprintf(absolute_path,"%s/%s", tmp_buffer, file); + free(tmp_buffer); + tmp_buffer = NULL; + } + + log_path = getenv("SANDBOX_LOG"); + debug_log_env = getenv("SANDBOX_DEBUG"); + debug_log_path = getenv("SANDBOX_DEBUG_LOG"); + + if ((NULL == log_path || 0 != strcmp(absolute_path, log_path)) && + (NULL == debug_log_env || NULL == debug_log_path || 0 != strcmp(absolute_path, debug_log_path)) && + 0 == check_access(sbcontext, func, absolute_path)) + { + if (1 == sbcontext->show_access_violation) + { + fprintf(stderr, "\e[31;01mACCESS DENIED\033[0m %s:%*s%s\n", func, (int)(10-strlen(func)), "", absolute_path); + + if (NULL != log_path) + { + sprintf(buffer, "%s:%*s%s\n", func, (int)(10-strlen(func)), "", absolute_path); + if (0 == lstat(log_path, &log_stat) && + 0 == S_ISREG(log_stat.st_mode)) + { + fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", log_path); + } + else + { + log_file = open(log_path, O_APPEND|O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if(log_file >= 0) + { + write(log_file, buffer, strlen(buffer)); + close(log_file); + } + } + } + } + + result = 0; + } + else if (NULL != debug_log_env) + { + if (NULL != debug_log_path) + { + if (0 != strcmp(absolute_path, debug_log_path)) + { + sprintf(buffer, "%s:%*s%s\n", func, (int)(10-strlen(func)), "", absolute_path); + if (0 == lstat(debug_log_path, &debug_log_stat) && + 0 == S_ISREG(debug_log_stat.st_mode)) + { + fprintf(stderr, "\e[31;01mSECURITY BREACH\033[0m %s already exists and is not a regular file.\n", log_path); + } + else + { + debug_log_file = open(debug_log_path, O_APPEND|O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); + if(debug_log_file >= 0) + { + write(debug_log_file, buffer, strlen(buffer)); + close(debug_log_file); + } + } + } + } + else + { + fprintf(stderr, "\e[32;01mACCESS ALLOWED\033[0m %s:%*s%s\n", func, (int)(10-strlen(func)), "", absolute_path); + } + } + + free(absolute_path); + absolute_path = NULL; + + return result; +} + +int is_sandbox_on() +{ + /* $SANDBOX_ACTIVE is an env variable that should ONLY + * be used internal by sandbox.c and libsanbox.c. External + * sources should NEVER set it, else the sandbox is enabled + * in some cases when run in parallel with another sandbox, + * but not even in the sandbox shell. + * + * Azarah (3 Aug 2002) + */ + if (NULL != getenv("SANDBOX_ON") && + 0 == strcmp(getenv("SANDBOX_ON"), "1") && + NULL != getenv("SANDBOX_ACTIVE") && + 0 == strcmp(getenv("SANDBOX_ACTIVE"), "armedandready")) + { + return 1; + } + else + { + return 0; + } +} + +int before_syscall(const char* func, const char* file) +{ + int result = 1; + + sbcontext_t sbcontext; + + init_context(&sbcontext); + + init_env_entries(&(sbcontext.deny_prefixes), &(sbcontext.num_deny_prefixes), "SANDBOX_DENY", 1); + init_env_entries(&(sbcontext.read_prefixes), &(sbcontext.num_read_prefixes), "SANDBOX_READ", 1); + init_env_entries(&(sbcontext.write_prefixes), &(sbcontext.num_write_prefixes), "SANDBOX_WRITE", 1); + init_env_entries(&(sbcontext.predict_prefixes), &(sbcontext.num_predict_prefixes), "SANDBOX_PREDICT", 1); + + result = check_syscall(&sbcontext, func, file); + + clean_env_entries(&(sbcontext.deny_prefixes), &(sbcontext.num_deny_prefixes)); + clean_env_entries(&(sbcontext.read_prefixes), &(sbcontext.num_read_prefixes)); + clean_env_entries(&(sbcontext.write_prefixes), &(sbcontext.num_write_prefixes)); + clean_env_entries(&(sbcontext.predict_prefixes), &(sbcontext.num_predict_prefixes)); + + if (0 == result) + { + errno = EACCES; + } + + return result; +} + +int before_syscall_open_int(const char* func, const char* file, int flags) +{ + if (flags & O_WRONLY || + flags & O_RDWR) + { + return before_syscall("open_wr", file); + } + else + { + return before_syscall("open_rd", file); + } +} + +int before_syscall_open_char(const char* func, const char* file, const char* mode) +{ + if (strcmp(mode, "r") == 0 || + strcmp(mode, "rb") == 0) + { + return before_syscall("open_rd", file); + } + else + { + return before_syscall("open_wr", file); + } +} diff --git a/src/sandbox/problems/Makefile b/src/sandbox/problems/Makefile new file mode 100644 index 00000000..b44189bd --- /dev/null +++ b/src/sandbox/problems/Makefile @@ -0,0 +1,31 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# +# Modified 15 Apr 2002 Jon Nelson +# Clean up Makefile somewhat, and use make's implicit rules +# +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/problems/Attic/Makefile,v 1.2 2002/04/16 01:06:55 jnelson Exp $ + +.SUFFIXES: +.SUFFIXES: .c .o .so +.PRECIOUS: %.o + +%.so: LIBS=-ldl +%.so: LDFLAGS=--shared +%.so: %.o + $(CC) $(CFLAGS) $(CPPFLAGS) $< -o $@ $(LIBS) $(LDFLAGS) + +CC = gcc +CFLAGS = -Wall -O2 +LIBS = +LDFLAGS = + +TARGETS = sandbox_muttbug sandbox_dev_fd_foo \ + libsandbox_muttbug.so libsandbox_emacsbug.so + +all: $(TARGETS) + +clean: + rm -f $(TARGETS) + rm -f *.o *~ diff --git a/src/sandbox/problems/libsandbox_emacsbug.c b/src/sandbox/problems/libsandbox_emacsbug.c new file mode 100644 index 00000000..11e861b9 --- /dev/null +++ b/src/sandbox/problems/libsandbox_emacsbug.c @@ -0,0 +1,34 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/problems/Attic/libsandbox_emacsbug.c,v 1.2 2003/03/22 14:24:38 carpaski Exp $ */ + +#define _GNU_SOURCE +#define _REENTRANT + +#define open xxx_open +# include +# include +# include +# include +# include +# include +#undef open + +extern int open(const char*, int, mode_t); +int (*orig_open)(const char*, int, mode_t) = NULL; +int open(const char* pathname, int flags, mode_t mode) +{ + int old_errno = errno; + + /* code that makes xemacs' compilation produce a segfaulting executable */ +/* char** test = NULL; + test = (char**)malloc(sizeof(char*)); + free(test);*/ + /* end of that code */ + + if (!orig_open) + { + orig_open = dlsym(RTLD_NEXT, "open"); + } + errno = old_errno; + return orig_open(pathname, flags, mode); +} + diff --git a/src/sandbox/problems/libsandbox_muttbug.c b/src/sandbox/problems/libsandbox_muttbug.c new file mode 100644 index 00000000..1d6e18c4 --- /dev/null +++ b/src/sandbox/problems/libsandbox_muttbug.c @@ -0,0 +1,24 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/problems/Attic/libsandbox_muttbug.c,v 1.2 2003/03/22 14:24:38 carpaski Exp $ */ + +#define _GNU_SOURCE +#define _REENTRANT + +#define open xxx_open +#include +#include +#include +#undef open + +extern FILE* fopen(const char*, const char*); +FILE* (*orig_fopen)(const char*, const char*) = 0; +FILE* fopen(const char* a1, const char* a2) +{ + int old_errno = errno; + if (!orig_fopen) + { + orig_fopen = dlsym(RTLD_NEXT, "fopen"); + } + errno = old_errno; + return orig_fopen(a1, a2); +} + diff --git a/src/sandbox/problems/sandbox_dev_fd_foo.c b/src/sandbox/problems/sandbox_dev_fd_foo.c new file mode 100644 index 00000000..c36a095c --- /dev/null +++ b/src/sandbox/problems/sandbox_dev_fd_foo.c @@ -0,0 +1,42 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/problems/Attic/sandbox_dev_fd_foo.c,v 1.2 2003/03/22 14:24:38 carpaski Exp $ */ + +#include +#include +#include +#include +#include + +void cleanup_1(void) +{ + puts("Unlinking file..."); + unlink("/tmp/_sandbox_test.file"); +} + +int main(void) +{ + struct stat s1, s2; + FILE *fp1, *fp2; + char *file = "/tmp/_sandbox_test.file"; + char devfd[32]; + + printf("Opening file...\n"); + if (!(fp1 = fopen(file, "w"))) + exit(1); + atexit(cleanup_1); + printf("fstat'ing file...\n"); + if (fstat(fileno(fp1), &s1) < 0) + exit(2); + sprintf(devfd, "/dev/fd/%d", fileno(fp1)); + printf("fopening %s...\n", devfd); + if (!(fp2 = fopen(devfd, "w"))) + exit(3); + printf("fstat'ing %s...\n", devfd); + if (fstat(fileno(fp2), &s2) < 0) + exit(4); + printf("Checking %ld == %ld and %ld == %ld...\n", + (long int) s1.st_dev, (long int) s2.st_dev, s1.st_ino, s2.st_ino); + if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) + exit(5); + printf("Success!\n"); + return(0); +} diff --git a/src/sandbox/problems/sandbox_muttbug.c b/src/sandbox/problems/sandbox_muttbug.c new file mode 100644 index 00000000..cccdc43e --- /dev/null +++ b/src/sandbox/problems/sandbox_muttbug.c @@ -0,0 +1,43 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/problems/Attic/sandbox_muttbug.c,v 1.3 2003/03/22 14:24:38 carpaski Exp $ */ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + FILE *fd ; + + printf("unlink\n"); + unlink("/tmp/test"); + printf("... done\n"); + + printf("fopen\n"); + fd = fopen("/tmp/test", "a+"); + printf("... done\n"); + + printf("fputc\n"); + fputc('7', fd); + printf("... done\n"); + + printf("fseek\n"); + fseek(fd, 0, SEEK_SET); + printf("... done\n"); + + printf("freopen\n"); + fd = freopen("/tmp/test", "r", fd); + printf("... done\n"); + + printf("fgetc "); + printf("%c\n", fgetc(fd)); + printf("... done\n"); + + printf("fseek\n"); + fseek(fd, 0, SEEK_SET); + printf("... done\n"); + + printf("fclose\n"); + fclose(fd); + printf("... done\n"); + return 0; +} diff --git a/src/sandbox/sandbox.bashrc b/src/sandbox/sandbox.bashrc new file mode 100644 index 00000000..be25349d --- /dev/null +++ b/src/sandbox/sandbox.bashrc @@ -0,0 +1,8 @@ +# Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +# Distributed under the terms of the GNU General Public License, v2 or later +# Author : Geert Bevin +# $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/Attic/sandbox.bashrc,v 1.2 2002/03/06 09:51:02 gbevin Exp $ +source /etc/profile +export LD_PRELOAD="$SANDBOX_LIB" +alias make="make LD_PRELOAD=$SANDBOX_LIB" +alias su="su -c '/bin/bash -rcfile $SANDBOX_DIR/sandbox.bashrc'" diff --git a/src/sandbox/sandbox.c b/src/sandbox/sandbox.c new file mode 100644 index 00000000..5a2295e7 --- /dev/null +++ b/src/sandbox/sandbox.c @@ -0,0 +1,921 @@ +/* +** Path sandbox for the gentoo linux portage package system, initially +** based on the ROCK Linux Wrapper for getting a list of created files +** +** to integrate with bash, bash should have been built like this +** +** ./configure --prefix= --host= --without-gnu-malloc +** +** it's very important that the --enable-static-link option is NOT specified +** +** Copyright (C) 2001 Geert Bevin, Uwyn, http://www.uwyn.com +** Distributed under the terms of the GNU General Public License, v2 or later +** Author : Geert Bevin +** $Header: /var/cvsroot/gentoo-src/portage/src/sandbox/Attic/sandbox.c,v 1.13 2002/08/05 05:51:39 drobbins Exp $ +*/ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LD_PRELOAD_FILE "/etc/ld.so.preload" +#define LIB_NAME "libsandbox.so" +#define BASHRC_NAME "sandbox.bashrc" +#define PIDS_FILE "/tmp/sandboxpids.tmp" +#define LOG_FILE_PREFIX "/tmp/sandbox-" +#define DEBUG_LOG_FILE_PREFIX "/tmp/sandbox-debug-" +#define LOG_FILE_EXT ".log" + +#define ENV_SANDBOX_DEBUG_LOG "SANDBOX_DEBUG_LOG" +#define ENV_SANDBOX_LOG "SANDBOX_LOG" +#define ENV_SANDBOX_DIR "SANDBOX_DIR" +#define ENV_SANDBOX_LIB "SANDBOX_LIB" + +#define ENV_SANDBOX_DENY "SANDBOX_DENY" +#define ENV_SANDBOX_READ "SANDBOX_READ" +#define ENV_SANDBOX_WRITE "SANDBOX_WRITE" +#define ENV_SANDBOX_PREDICT "SANDBOX_PREDICT" + +#define ENV_SANDBOX_ON "SANDBOX_ON" +#define ENV_SANDBOX_BEEP "SANDBOX_BEEP" + +#define DEFAULT_BEEP_COUNT 3 + +int preload_adaptable = 1; +int cleaned_up = 0; + +char* dirname(const char* path) +{ + char* base = NULL; + unsigned int length = 0; + + base = strrchr(path, '/'); + if (NULL == base) + { + return strdup("."); + } + while (base > path && + *base == '/') + { + base--; + } + length = (unsigned int) 1 + base - path; + + base = malloc(sizeof(char)*(length+1)); + memmove(base, path, length); + base[length] = 0; + + return base; +} + +void cleanup() +{ + int i = 0; + int success = 1; + + FILE* preload_stream = NULL; + int preload_file = -1; + char preload_entry[255]; + char** preload_array = NULL; + int num_of_preloads = 0; + + FILE* pids_stream = NULL; + struct stat pids_stat; + int pids_file = -1; + char pid_string[255]; + int tmp_pid = 0; + int* pids_array = NULL; + int num_of_pids = 0; + + /* remove this sandbox's bash pid from the global pids file if it has rights to adapt the ld.so.preload file*/ + if (1 == preload_adaptable && + 0 == cleaned_up) + { + cleaned_up = 1; + success = 1; + if (0 == lstat(PIDS_FILE, &pids_stat) && + 0 == S_ISREG(pids_stat.st_mode)) + { + perror(">>> pids file is not a regular file"); + success = 0; + } + else + { + pids_stream = fopen(PIDS_FILE, "r+"); + if (NULL == pids_stream) + { + perror(">>> pids file fopen"); + success = 0; + } + else + { + pids_file = fileno(pids_stream); + if (pids_file < 0) + { + perror(">>> pids file fileno"); + success = 0; + } + else + { + if (flock(pids_file, LOCK_EX) < 0) + { + perror(">>> pids file lock"); + success = 0; + } + else + { + /* check which sandbox pids are still running */ + while (EOF != fscanf(pids_stream, "%d\n", &tmp_pid)) + { + if (0 == kill(tmp_pid, 0)) + { + if (NULL == pids_array) + { + pids_array = (int*)malloc(sizeof(int)); + } + else + { + pids_array = (int*)realloc(pids_array, sizeof(int)*(num_of_pids+1)); + } + pids_array[num_of_pids++] = tmp_pid; + } + } + + /* clean the /etc/ld.so.preload file if no other sandbox processes are running anymore*/ + if(num_of_pids == 1) + { + success = 1; + preload_stream = fopen("/etc/ld.so.preload", "r+"); + if (NULL == preload_stream) + { + perror(">>> /etc/ld.so.preload file fopen"); + success = 0; + } + else + { + preload_file = fileno(preload_stream); + if (preload_file < 0) + { + perror(">>> /etc/ld.so.preload file fileno"); + success = 0; + } + else + { + if (flock(preload_file, LOCK_EX) < 0) + { + perror(">>> /etc/ld.so.preload file lock"); + success = 0; + } + else + { + /* only get the entries that don't contain the sandbox library from the /etc/ld.so.preload file */ + while (EOF != fscanf(preload_stream, "%s\n", preload_entry)) + { + if (NULL == strstr(preload_entry, LIB_NAME)) + { + if (NULL == preload_array) + { + preload_array = (char**)malloc(sizeof(char*)); + } + else + { + preload_array = (char**)realloc(pids_array, sizeof(char*)*(num_of_preloads+1)); + } + preload_array[num_of_preloads++] = strdup(preload_entry); + } + } + + if (fseek(preload_stream, 0, SEEK_SET) < 0) + { + perror(">>> /etc/ld.so.preload file fseek"); + success = 0; + } + else + { + /* empty the /etc/ld.so.preload file */ + if (ftruncate(preload_file, 0) < 0) + { + perror(">>> /etc/ld.so.preload file ftruncate"); + success = 0; + } + else + { + /* store the other preload libraries back into the /etc/ld.so.preload file */ + if(num_of_preloads > 0) + { + for (i = 0; i < num_of_preloads; i++) + { + sprintf(preload_entry, "%s\n", preload_array[i]); + if (write(preload_file, preload_entry, strlen(preload_entry)) != strlen(preload_entry)) + { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + break; + } + } + } + } + } + + if (NULL != preload_array) + { + for (i = 0; i < num_of_preloads; i++) + { + free(preload_array[i]); + preload_array[i] = NULL; + } + free(preload_array); + preload_array = NULL; + } + + if (flock(preload_file, LOCK_UN) < 0) + { + perror(">>> /etc/ld.so.preload file unlock"); + success = 0; + } + } + } + if (EOF == fclose(preload_stream)) + { + perror(">>> /etc/ld.so.preload file fclose"); + success = 0; + } + preload_stream = NULL; + preload_file = -1; + } + } + + if (fseek(pids_stream, 0, SEEK_SET) < 0) + { + perror(">>> pids file fseek"); + success = 0; + } + else + { + /* empty the pids file */ + if (ftruncate(pids_file, 0) < 0) + { + perror(">>> pids file ftruncate"); + success = 0; + } + else + { + /* if pids are still running, write only the running pids back to the file */ + if(num_of_pids > 1) + { + for (i = 0; i < num_of_pids; i++) + { + sprintf(pid_string, "%d\n", pids_array[i]); + if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) + { + perror(">>> pids file write"); + success = 0; + break; + } + } + } + } + } + + if (NULL != pids_array) + { + free(pids_array); + pids_array = NULL; + } + + if (flock(pids_file, LOCK_UN) < 0) + { + perror(">>> pids file unlock"); + success = 0; + } + } + } + if (EOF == fclose(pids_stream)) + { + perror(">>> pids file fclose"); + success = 0; + } + pids_stream = NULL; + pids_file = -1; + } + } + if (0 == success) + { + exit(1); + } + } +} + +void stop(int signum) +{ + cleanup(); +} + +int main(int argc, char** argv) +{ + int i = 0; + int success = 1; + int status = 0; + char* run_str = "-c"; + char run_arg[255]; + + struct stat preload_stat; + FILE* preload_stream = NULL; + int preload_file = -1; + char preload_entry[255]; + int preload_lib_present = 0; + + int bash_pid = 0; + char* home_dir = NULL; + char portage_tmp_dir[PATH_MAX]; + char var_tmp_dir[PATH_MAX]; + char tmp_dir[PATH_MAX]; + char sandbox_write_var[255]; + char sandbox_predict_var[255]; + char* tmp_string = NULL; + char full_sandbox_path[255]; + char sandbox_log[255]; + char* sandbox_log_env; + struct stat sandbox_log_stat; + int sandbox_log_presence = 0; + int sandbox_log_file = -1; + char sandbox_debug_log[255]; + char sandbox_dir[255]; + char sandbox_lib[255]; + struct stat sandbox_lib_stat; + char sandbox_rc[255]; + struct stat sandbox_rc_stat; + + struct stat pids_stat; + int pids_file = -1; + char pid_string[255]; + + // Only print info if called with no arguments .... + if (argc < 2) + { + printf("========================== Gentoo linux path sandbox ===========================\n"); + } + + /* check if a sandbox is already running */ + if (NULL != getenv(ENV_SANDBOX_ON)) + { + fprintf(stderr, "Not launching a new sandbox instance\nAnother one is already running in this process hierarchy.\n"); + + exit(1); + } + else + { + char* argv_bash[] = + { + "/bin/bash", + "-rcfile", + NULL, + NULL, + NULL, + NULL + }; + + /* determine the location of all the sandbox support files */ + if (argc < 2) + printf("Detection of the support files.\n"); + if ('/' == argv[0][0]) + { + strcpy(full_sandbox_path, argv[0]); + } + else + { + tmp_string = get_current_dir_name(); + strcpy(full_sandbox_path, tmp_string); + free(tmp_string); + tmp_string = NULL; + strcat(full_sandbox_path, "/"); + strcat(full_sandbox_path, argv[0]); + } + tmp_string = dirname(full_sandbox_path); + strcpy(sandbox_dir, tmp_string); + free(tmp_string); + tmp_string = NULL; + strcat(sandbox_dir, "/"); + strcpy(sandbox_lib, "/lib/"); + strcat(sandbox_lib, LIB_NAME); + if (-1 == stat(sandbox_lib, &sandbox_lib_stat)) + { + strcpy(sandbox_lib, sandbox_dir); + strcat(sandbox_lib, LIB_NAME); + } + strcpy(sandbox_rc, "/usr/lib/portage/lib/"); + strcat(sandbox_rc, BASHRC_NAME); + if (-1 == stat(sandbox_rc, &sandbox_rc_stat)) + { + strcpy(sandbox_rc, sandbox_dir); + strcat(sandbox_rc, BASHRC_NAME); + } + + /* verify the existance of required files */ + if (argc < 2) + { + printf("Verification of the required files.\n"); + } + if (-1 == stat(sandbox_lib, &sandbox_lib_stat)) + { + fprintf(stderr, "Could not open the sandbox library at '%s'.\n", sandbox_lib); + return -1; + } + else if (-1 == stat(sandbox_rc, &sandbox_rc_stat)) + { + fprintf(stderr, "Could not open the sandbox rc file at '%s'.\n", sandbox_rc); + return -1; + } + else + { + /* ensure that the /etc/ld.so.preload file contains an entry for the sandbox lib */ + if (argc < 2) + { + printf("Setting up the ld.so.preload file.\n"); + } + + /* check if the /etc/ld.so.preload file exists */ + if (stat("/etc/ld.so.preload", &preload_stat) < 0 && + ENOENT == errno) + { + /* if not, try to create it and write the path of the sandbox lib to it */ + success = 1; + preload_file = open("/etc/ld.so.preload", O_WRONLY|O_CREAT, 0644); + if (preload_file < 0) + { + /* if access was denied, warn the user about it */ + if (EACCES == errno) + { + preload_adaptable = 0; + printf(">>> Couldn't adapt the /etc/ld.so.preload file.\n>>> It's possible that not all function calls are trapped\n"); + } + else + { + perror(">>> /etc/ld.so.preload file open"); + success = 0; + } + } + else + { + if (flock(preload_file, LOCK_EX) < 0) + { + perror(">>> /etc/ld.so.preload file lock"); + success = 0; + } + else + { + if (write(preload_file, sandbox_lib, strlen(sandbox_lib)) != strlen(sandbox_lib)) + { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + } + + if (flock(preload_file, LOCK_UN) < 0) + { + perror(">>> /etc/ld.so.preload file unlock"); + success = 0; + } + } + if (close(preload_file) < 0) + { + perror(">>> /etc/ld.so.preload file close"); + success = 0; + } + pids_file = -1; + } + if (0 == success) + { + exit(1); + } + } + else + { + /* if the /etc/ld.so.preload file exists, try to open it in read/write mode */ + success = 1; + if (0 == S_ISREG(preload_stat.st_mode)) + { + perror(">>> /etc/ld.so.preload file is not a regular file"); + success = 0; + } + else + { + preload_stream = fopen("/etc/ld.so.preload", "r+"); + if (NULL == preload_stream) + { + if (EACCES == errno) + { + /* if access was denied, warn the user about it */ + preload_adaptable = 0; + printf(">>> Couldn't adapt the /etc/ld.so.preload file.\n>>> It's possible that not all function calls are trapped\n"); + } + else + { + perror(">>> /etc/ld.so.preload file fopen"); + success = 0; + } + } + else + { + preload_file = fileno(preload_stream); + if (preload_file < 0) + { + perror(">>> /etc/ld.so.preload file fileno"); + success = 0; + } + else + { + if (flock(preload_file, LOCK_EX) < 0) + { + perror(">>> /etc/ld.so.preload file lock"); + success = 0; + } + else + { + /* check if the sandbox library is already present in the /etc/ld.so.preload file */ + while (EOF != fscanf(preload_stream, "%s\n", preload_entry)) + { + if (NULL != strstr(preload_entry, LIB_NAME)) + { + preload_lib_present = 1; + break; + } + } + + /* if it's not present, add the sandbox lib path to the end of the /etc/ld.so.preload file */ + if (0 == preload_lib_present) + { + if (fseek(preload_stream, 0, SEEK_END) < 0) + { + perror(">>> /etc/ld.so.preload file fseek"); + success = 0; + } + else + { + if (write(preload_file, sandbox_lib, strlen(sandbox_lib)) != strlen(sandbox_lib)) + { + perror(">>> /etc/ld.so.preload file write"); + success = 0; + } + } + } + + if (flock(preload_file, LOCK_UN) < 0) + { + perror(">>> /etc/ld.so.preload file unlock"); + success = 0; + } + } + } + if (EOF == fclose(preload_stream)) + { + perror(">>> /etc/ld.so.preload file fclose"); + success = 0; + } + preload_stream = NULL; + preload_file = -1; + } + } + if (0 == success) + { + exit(1); + } + } + + /* set up the required environment variables */ + if (argc < 2) + { + printf("Setting up the required environment variables.\n"); + } + argv_bash[2] = sandbox_rc; + + sprintf(pid_string, "%d", getpid()); + strcpy(sandbox_log, LOG_FILE_PREFIX); + sandbox_log_env = getenv(ENV_SANDBOX_LOG); + if (sandbox_log_env) + { + strcat(sandbox_log, sandbox_log_env); + strcat(sandbox_log, "-"); + } + strcat(sandbox_log, pid_string); + strcat(sandbox_log, LOG_FILE_EXT); + setenv(ENV_SANDBOX_LOG, sandbox_log, 1); + strcpy(sandbox_debug_log, DEBUG_LOG_FILE_PREFIX); + strcat(sandbox_debug_log, pid_string); + strcat(sandbox_debug_log, LOG_FILE_EXT); + setenv(ENV_SANDBOX_DEBUG_LOG, sandbox_debug_log, 1); + home_dir = getenv("HOME"); + + // drobbins: we need to expand these paths using realpath() so that PORTAGE_TMPDIR + // can contain symlinks (example, /var is a symlink, /var/tmp is a symlink.) Without + // this, access is denied to /var/tmp, hurtin' ebuilds. + + realpath(getenv("PORTAGE_TMPDIR"),portage_tmp_dir); + realpath("/var/tmp",var_tmp_dir); + realpath("/tmp",tmp_dir); + + setenv(ENV_SANDBOX_DIR, sandbox_dir, 1); + setenv(ENV_SANDBOX_LIB, sandbox_lib, 1); + setenv("LD_PRELOAD", sandbox_lib, 1); + if (NULL == getenv(ENV_SANDBOX_DENY)) + { + setenv(ENV_SANDBOX_DENY, LD_PRELOAD_FILE, 1); + } + if (NULL == getenv(ENV_SANDBOX_READ)) + { + setenv(ENV_SANDBOX_READ, "/", 1); + } + if (NULL == getenv(ENV_SANDBOX_WRITE)) + { + /* these should go into make.globals later on */ + strcpy(sandbox_write_var, ""); + strcat(sandbox_write_var, "/dev/zero:/dev/fd/:/dev/null:/dev/pts/:/dev/vc/:/dev/tty:/tmp/"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/log/scrollkeeper.log"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, home_dir); + strcat(sandbox_write_var, "/.gconfd/lock"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, home_dir); + strcat(sandbox_write_var, "/.bash_history"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/tmp/conftest"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/lib/conftest"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/tmp/cf"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/usr/lib/cf"); + strcat(sandbox_write_var, ":"); + if (NULL == portage_tmp_dir) + { + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + } + else if (0 == strcmp(sandbox_write_var, "/var/tmp/")) + { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + } + else if (0 == strcmp(sandbox_write_var, "/tmp/")) + { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + } + else + { + strcat(sandbox_write_var, portage_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, var_tmp_dir); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/tmp/"); + strcat(sandbox_write_var, ":"); + strcat(sandbox_write_var, "/var/tmp/"); + } + /* */ + setenv(ENV_SANDBOX_WRITE, sandbox_write_var, 1); + } + if (NULL == getenv(ENV_SANDBOX_PREDICT)) + { + /* these should go into make.globals later on */ + strcpy(sandbox_predict_var, ""); + strcat(sandbox_predict_var, home_dir); + strcat(sandbox_predict_var, "/."); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.0/"); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.1/"); + strcat(sandbox_predict_var, ":"); + strcat(sandbox_predict_var, "/usr/lib/python2.2/"); + setenv(ENV_SANDBOX_PREDICT, sandbox_predict_var, 1); + /* */ + } + setenv(ENV_SANDBOX_ON, "1", 0); + + /* if the portage temp dir was present, cd into it */ + if (NULL != portage_tmp_dir) + { + chdir(portage_tmp_dir); + } + + /* adding additional bash arguments */ + for (i = 1; i < argc; i++) + { + if (1 == i) + { + argv_bash[3] = run_str; + argv_bash[4] = run_arg; + strcpy(argv_bash[4], argv[i]); + } + else + { + strcat(argv_bash[4], " "); + strcat(argv_bash[4], argv[i]); + } + } + + /* set up the required signal handlers */ + signal(SIGHUP, &stop); + signal(SIGINT, &stop); + signal(SIGQUIT, &stop); + signal(SIGTERM, &stop); + + /* this one should NEVER be set in ebuilds, as it is the one + * private thing libsandbox.so use to test if the sandbox + * should be active for this pid, or not. + * + * azarah (3 Aug 2002) + */ + setenv("SANDBOX_ACTIVE", "armedandready", 1); + + /* fork to executing bash */ + if (argc < 2) + { + printf("Creating a seperate process the run the shell in.\n"); + } + bash_pid = fork(); + + if (0 == bash_pid) + { + /* launch bash */ + execv(argv_bash[0], argv_bash); + } + else + { + int wait_pid = 0; + + if (argc < 2) + { + printf("The protected environment has been started.\n"); + printf("--------------------------------------------------------------------------------\n"); + } + + /* store his sandbox's bash pid in the global pids file if it has rights to adapt the ld.so.preload file*/ + if (1 == preload_adaptable) + { + success = 1; + if (0 == lstat(PIDS_FILE, &pids_stat) && + 0 == S_ISREG(pids_stat.st_mode)) + { + perror(">>> pids file is not a regular file"); + success = 0; + } + else + { + pids_file = open(PIDS_FILE, O_WRONLY|O_CREAT|O_APPEND, 0644); + if (pids_file < 0) + { + perror(">>> pids file open"); + success = 0; + } + else + { + if (flock(pids_file, LOCK_EX) < 0) + { + perror(">>> pids file lock"); + success = 0; + } + else + { + sprintf(pid_string, "%d\n", getpid()); + if (write(pids_file, pid_string, strlen(pid_string)) != strlen(pid_string)) + { + perror(">>> pids file write"); + success = 0; + } + + if (flock(pids_file, LOCK_UN) < 0) + { + perror(">>> pids file unlock"); + success = 0; + } + } + if (close(pids_file) < 0) + { + perror(">>> pids file close"); + success = 0; + } + pids_file = -1; + } + } + if (0 == success) + { + exit(1); + } + } + + /* wait until bash exits */ + wait_pid = waitpid(bash_pid, &status, 0); + } + } + + cleanup(); + + if (argc < 2) + { + printf("========================== Gentoo linux path sandbox ===========================\n"); + printf("The protected environment has been shut down.\n"); + } + if (0 == stat(sandbox_log, &sandbox_log_stat)) + { + sandbox_log_presence = 1; + success = 1; + sandbox_log_file = open(sandbox_log, O_RDONLY, 0); + if (sandbox_log_file < 0) + { + perror(">>> sandbox log file open"); + success = 0; + } + else + { + int i = 0; + char* beep_count_env = NULL; + int beep_count = 0; + int length = 0; + char buffer[255]; + + printf("\e[31;01m--------------------------- ACCESS VIOLATION SUMMARY ---------------------------\033[0m\n"); + printf("\e[31;01mLOG FILE = \"%s\"\033[0m\n", sandbox_log); + printf("\n"); + while ((length = read(sandbox_log_file, buffer, sizeof(buffer)-1)) > 0) + { + if (length < sizeof(buffer)) + { + buffer[length] = 0; + } + printf("%s", buffer); + } + printf("\e[31;01m--------------------------------------------------------------------------------\033[0m\n"); + + if (close(sandbox_log_file) < 0) + { + perror(">>> sandbox log close"); + success = 0; + } + + beep_count_env = getenv(ENV_SANDBOX_BEEP); + if (beep_count_env) + { + beep_count = atoi(beep_count_env); + } + else + { + beep_count = DEFAULT_BEEP_COUNT; + } + for (i = 0; i < beep_count; i++) + { + fputc('\a', stderr); + if (i < beep_count -1) + { + sleep(1); + } + } + + } + if (0 == success) + { + exit(1); + } + sandbox_log_file = -1; + } + else if (argc < 2) + { + printf("--------------------------------------------------------------------------------\n"); + } + + if (status > 0 || + 1 == sandbox_log_presence) + { + return 1; + } + else + { + return 0; + } + } +} diff --git a/src/tbz2tool.c b/src/tbz2tool.c new file mode 100644 index 00000000..c740d443 --- /dev/null +++ b/src/tbz2tool.c @@ -0,0 +1,228 @@ +/* $Header: /var/cvsroot/gentoo-src/portage/src/tbz2tool.c,v 1.3.2.1 2004/11/05 13:49:22 jstubbs Exp $ */ + +#include +#include +#include +#include +#include +#include +#include + +/*buffered reading/writing size*/ +#define BUFLEN 262144 +char *myname="tbz2tool"; +struct stat *mystat=NULL; +void *mybuf; +FILE *datafile, *dbfile, *outfile, *infile; +unsigned char endbuf[8]; +long seekto,insize; + +int exists(const char *myfile) { + int result; + result=stat(myfile,mystat); + if (result==-1) + return 0; + return 1; +} + +void writefile(FILE *src, FILE *dest) { + int count=1; + while (count) { + count=fread(mybuf, 1, BUFLEN, src); + fwrite(mybuf, 1, count, dest); + } +} + +void writefileto(FILE *src, FILE *dest, int endpos) { + int pos=ftell(src); + int thiscount; + while (pos < endpos) { + /* thiscount=how much to read */ + thiscount=endpos-pos; + if (thiscount>BUFLEN) + thiscount=BUFLEN; + thiscount=fread(mybuf, 1, thiscount , src); + /* thiscount=how much we actually did read */ + if (thiscount==0) + /* eof -- shouldn't happen */ + break; + /* update internal position counter */ + pos+=thiscount; + fwrite(mybuf, 1, thiscount, dest); + } +} + +int main(int argc, char **argv) { + if ((argc==2) && (!(strcmp(argv[1],"--help")))) + goto usage; + if (argc!=5) { + printf("%s: four arguments expected\n",myname); + goto error; + } + if (!(mystat=(struct stat *) malloc(sizeof(struct stat)))) + goto memalloc; + + if (!(mybuf=(void *) malloc(BUFLEN))) { + free(mystat); + goto memalloc; + } + + /* JOIN MODE */ + if (!(strcmp(argv[1],"join"))) { + + /* check if datafile exists */ + if (!(exists(argv[2]))) { + printf("%s: %s doesn't exist\n",myname,argv[2]); + free(mystat); + goto error; + } + + /* check if dbfile exists */ + if (!(exists(argv[3]))) { + printf("%s: %s doesn't exist\n",myname,argv[3]); + free(mystat); + goto error; + } + /* create end buffer for later use */ + endbuf[0]=((mystat->st_size) & 0xff000000) >> 24; + endbuf[1]=((mystat->st_size) & 0x00ff0000) >> 16; + endbuf[2]=((mystat->st_size) & 0x0000ff00) >> 8; + endbuf[3]=(mystat->st_size) & 0x000000ff; + endbuf[4]='S'; + endbuf[5]='T'; + endbuf[6]='O'; + endbuf[7]='P'; + + /* if outfile exists, unlink first (safer) */ + if (exists(argv[4])) + unlink(argv[4]); + + /* open datafile for reading */ + if ((datafile=fopen(argv[2],"r"))==NULL) { + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[2]); + goto error; + } + + /* open dbfile for reading */ + if ((dbfile=fopen(argv[3],"r"))==NULL) { + fclose(datafile); + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[3]); + goto error; + } + + /* open outfile for writing */ + if ((outfile=fopen(argv[4],"a"))==NULL) { + fclose(dbfile); + fclose(datafile); + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[4]); + goto error; + } + + writefile(datafile,outfile); + writefile(dbfile,outfile); + fwrite(endbuf,1,8,outfile); + fclose(outfile); + fclose(dbfile); + fclose(datafile); + free(mybuf); + free(mystat); + exit(0); + + /* SPLIT MODE */ + } else if (!(strcmp(argv[1],"split"))) { + + /* check if infile exists */ + if (!(exists(argv[2]))) { + printf("%s: %s doesn't exist\n",myname,argv[2]); + free(mystat); + goto error; + } + + /* store infile size for later use */ + + insize=mystat->st_size; + + /* if datafile exists, unlink first (safer) */ + if (exists(argv[3])) + unlink(argv[3]); + + /* if dbfile exists, unlink first (safer) */ + if (exists(argv[4])) + unlink(argv[4]); + + /* open infile for reading */ + if ((infile=fopen(argv[2],"r"))==NULL) { + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[2]); + goto error; + } + + /* read in end buffer */ + fseek(infile,-8,SEEK_END); + fread(endbuf,1,8,infile); + /* quick end buffer read and verification */ + if ( (endbuf[4]!='S') || (endbuf[5]!='T') || (endbuf[6]!='O') || (endbuf[7]!='P') ) { + fclose(infile); + free(mybuf); + free(mystat); + printf("%s: %s appears to be corrupt (end buffer invalid)\n",myname,argv[2]); + goto error; + } + + seekto=0; + seekto=seekto+endbuf[0]*256*256*256; + seekto=seekto+endbuf[1]*256*256; + seekto=seekto+endbuf[2]*256; + seekto=seekto+endbuf[3]; + + /* open datafile for writing */ + if ((datafile=fopen(argv[3],"a"))==NULL) { + fclose(infile); + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[3]); + goto error; + } + + /* open dbfile for writing */ + if ((dbfile=fopen(argv[4],"a"))==NULL) { + fclose(datafile); + fclose(infile); + free(mybuf); + free(mystat); + printf("%s: Error opening %s\n",myname,argv[4]); + goto error; + } + + rewind(infile); + writefileto(infile,datafile,insize-(seekto+8)); + fseek(infile,-(seekto+8),SEEK_END); + writefileto(infile,dbfile,insize-8); + fclose(infile); + fclose(dbfile); + fclose(datafile); + free(mybuf); + free(mystat); + exit(0); + + } else { + free(mybuf); + free(mystat); + goto usage; + } + + usage: + printf("Usage: %s join DATAFILE DBFILE OUTFILE (datafile + dbfile -> outfile)\n %s split INFILE DATAFILE DBFILE (infile -> datafile + dbfile)\n",myname,myname); +error: + exit(1); +memalloc: + printf("%s: memory allocation error\n",myname); + exit(2); +} -- cgit v1.2.3-18-g5258