---[  Phrack Magazine   Volume 8, Issue 54 Dec 25th, 1998, article 06 of 12


-------------------------[  The Belt And Suspenders Approach (September 1998)


--------[  route|daemon9 <route@infonexus.com>



----[  Introduction and Impetus


The OpenBSD project team.  Purveyors of a FREE, multi-platform 4.4BSD-based
UNIX-like operating system.  Their efforts place emphasis on portability,
standardization, correctness, security, and cryptography.  And OpenBSD
really concentrates on those last two.  OpenBSD is simply the best choice
for multi-user environments.

It is the flawed assumption that security mechanisms can be adequately
provided in layers above the operating system.  A perfect security application
cannot make up for flawed or absent security features within the OS kernel.
It is the classic example of building a castle on a swamp.  You can build
a strong fortress, but it makes no difference if it slowly sinks into the
ground.  In this article, we retrofit the OpenBSD kernel with some additional
security.

This article is about cracking the whip.  It's a prime example of security
being (possibly) inconvenient.  But by making things potentially a bit more
difficult for normal users, we hope to severely hamper would-be attackers.
Two effective ways of doing this are through limited program execution via
path and credential checks and privacy restrictions.

This article is a follow-up to my P52-06 article on hardening the Linux
kernel.  Herein the reader will find several patches designed to harden a
multi-user OpenBSD box.  These patches can be broken down into two areas:
privacy restriction and execution restriction (more on these below).  The
patches contained here should be used in conjunction with a savvy for
intelligent administration; if you can't recompile a kernel, stop here.


----[  Getting Sources

You will need an OpenBSD 2.4 box with full kernel sources for your
architecture and sources for the following programs: w, who, ps, fstat, and
ld.so.  Below are sample instructions for getting the sources you'll need
through anonymous CVS.

I.      Pick a server and set the appropriate environment variables:
            (assuming csh or tcsh)
            1. setenv CVSROOT anoncvs@anoncvs3.usa.openbsd.org:/cvs
            2. setenv CVS_RSH /usr/local/bin/ssh
            3. cd /usr

II.     Get the sources:
            1. cvs get src/usr.bin/fstat
            2. cvs get src/bin/ps
            3. cvs get src/usr.bin/w
            4. cvs get src/usr.bin/who
            5. cvs get src/gnu/usr.bin/ld/
            6. cvs get src/lib/libc/stdio/

III.    If you need kernel sources:
            (for i386-based machines, other architectures vary slightly)
            1. cvs get ksrc-i386 ksrc-common


----[  Privacy Patches

Tested on:  2.4-SNAP (Current as of 12.10.98)
Author:     route

Why should we allow anyone to be able to view information on processes they do
not own?

Normally, when a process wants system-wide process table information, it
retrieves it from the kernel virtual memory interface by making calls to a
kvm_*(3) derivative.  All that is required is that the process have
permissions to read from /dev/kmem (usually meaning the program file needs to
be sgid kmem).  I am of the school of thought that, unless you are really
cool, you don't need to see everyone else's processes on a host.  The
privacy patches work towards this end.


----[  Privacy Patches Modus Operandi

Simple credential check.  Before the command is allowed to dump savory
information, a UID check is made.  If you're not root, you're not going to
see other users' information.  Due to the somewhat lazy way this is
implemented, a savvy hacker could defeat this.  I leave this as an exercise to
the reader.


----[  Privacy Patches Installation

I.      Extract the code from this article:
            1. extract P54-06
            2. cd PP/

II.     Apply the userland diffs:
            1. cp Patch/PP-diff /usr/src
            2. cd /usr/src
            3. patch < PP-diff

III.    Next, cd to the relevant directories and build the executables:
            1. cd usr.bin/fstat; make; make install
            2. cd usr.bin/who; make; make install
            3. cd usr.bin/w; make; make install
            4. cd bin/ps; make; make install


----[  Trusted Path / ACL Execution Patches

Tested on:  2.4-SNAP (Current as of 12.10.98)
Author:     route

Why should we allow arbitrary code execution rights?

Before any call to sys_execve() is allowed to proceed, we take the vnode of
the parent directory that the targeted file lives in and grab the file
attributes via the VOP_GETATTR() macro.  We then check to see if the path
is trusted (root owned directory that isn't group or world writable) and,
barring that, we check to see if the user is trusted (on the kernel's trust
list).  If the last check fails, the file is denied execution privileges.

Oops!  By setting certain environment variables, users can still preload
libraries and modules filled with all sorts of arbitrary code.  This is a
no-no.  To prevent this, we provide a mechanism to effectively ignore
LD_PRELOAD and LD_LIBRARY_PATH environment variables.


----[   TPE Implementation Overview

The tpe suite consists of 4 components: the in-kernel mechanisms, a system
call, a userland agent and an ld.so component.  The kernel resident components
handle the path and credential verification as well as list maintenance.
The system call is the vessel used to convey information from userland to
the kernel and vice versa.  The userland agent consists of the tpe
administrative program used to manipulate the trust list (and enable/disable
the ld.so environment checker).  The ld.so piece is responsible for grooming
the environment of any illegal variables.


----[  TPE Trust List Kernel Interface and Abstract Data Types

The trust list inside the kernel is a static array of type `uid_t`.  The
decision was the made to use a static array to hold the trusted IDs for both
convenience and runtime efficiency.  By default, the list is elements long.
If this for some reason is not sufficient, it can be increased by changing the
CPP symbolic constant TPE_ACL_SIZE (however, you should first probably ask
yourself why you need more than 80 trusted users).

The speed in which user ID verification is done is absolutely essential, as
this check will be done for every call to exec that does not originate from a
trusted path.  This has the potential to be a huge bottle neck.  This was
taken into consideration and the bulk of processing overhead is offloaded to
list initialization and modification.

The list is kept ordered after all insertions and deletions via insertion sort.
Sorting is relatively costly (insertion sort has a running time of about
O(n^2)) and is done when response time is not absolutely critical, during list
additions and deletions.

Speed is essential when the lookups are done, and, since the list is ordered,
a binary search can be done in a worst case of O(lg N).  In fact, with the
default list size of 80 elements, we can be guaranteed no more than 7
comparisons will be done.  Compare that with a sequential search in an
ordered list which has a worst case of O(N) (80 comparisons).


----[  TPE ld.so protection

The dynamic linker is a great tool that allows us to write small programs 
that load external code at runtime.  On a macro scale, ld.so allows processes
to load arbitrary external code for execution.  This can be used to bypass
our execution restrictions.  A user could bypass path trust by simply loading
code dynamically via library or object code redirection.  This is against
our best interests.  To prevent this, we patch ld.so to strip the
LD_PRELOAD and LD_LIBRARY_PATH environment variables.

There is a global int, tpe_ld_check, that is set, cleared and checked via
the system call.  When set, ld.so checks the environment of any non UID 0
process and calls unsetenv if LD_PRELOAD and/or LD_LIBRARY_PATH exist.  The
variables still exist in the user's environment, but they are ignored
during the dynamic linking.


----[  What TPE will do

Trusted path execution will prevent arbitrary users from executing arbitrary
code.  This means that malicious users cannot execute exploit programs to
try and break root on your machine.  This also means that they can't execute
exploit programs and try to hack from your machine.  It affords an
administrator an extra level of confidence that her system is secure.


----[  What TPE will not do

TPE relies on auditing a call to one of exec(2) family of functions.  It
ensures that the program file that contains the code to be executed resides
in a trusted directory or is being executed by a trusted user.  Programs
living in a trusted directory that interpret symbolic code and link and
assemble at runtime (and call exec from a trusted path) can bypass our
TPE security mandate and must be audited differently.  These are programs
such as perl, any of the shell interpreters, sed, awk, etc...  While a
malicious user cannot just whip up a script in her home directory (it would be
denied execution rights because it lives in an untrusted directory) she could
specify the code on the command line or redirect it from a file.

There are different ways to tackle this problem, none of them very elegant.
Changing the file permissions and ownership to allow only members of a
certain group access to these files is a simple effort and an obvious
choice, but will not work for the shell interpreters.  Moving all of these
programs to a special non-trusted directory would also work (normal users
would not be able to execute them, but trusted users would), but again,
this will not work for the shell programs.

To prevent the shell programs from being to execute arbitrary code it seems
like the only real solution would be to patch them.  This way you can prevent
naughty activity and still get desired functionality.

Another area of trouble is command line buffer overflows.  If a trusted
program happens to contain a buffer overflow that is exploitable from the
command line, an attacker can bypass the TPE and get arbitrary code executed.
The overflow shellcode is passed in as standard command line argument and
is not illegal as far as TPE sees.  One possible fix is to audit or sanitize
the command arguments before granting execution rights.

The other noteworthy issue regarding TPE is the fact that it generally does not
protect the machine from remote attacks.  Daemons running as root or as a 
trusted user id (usually the case -- otherwise how would it be started in the
first place?) will be allowed execution rites.  If this code contains
remotely exploitable buffer overflows, TPE cannot prevent arbitrary code
execution.


----[  tpe_adm

The userland agent is painfully simple to use.  To show the kernel's trusted
user list:

    resentment:~# tpe_adm -s
    trusted users: root diablerie

To add a user to the list:

    resentment:~# tpe_adm -a devilish
    UID 1000 added to trust list
    resentment:~# tpe_adm -s
    trusted users: root diablerie devilish

To remove a user from the list:

    resentment:~# tpe_adm -d diablerie
    UID 1000 removed from trust list
    resentment:~# tpe_adm -s
    trusted users: root diablerie

To enable/disable ld.so environment checking:

    resentment:~# tpe_adm -le
    ld.so environment protection enabled
    resentment:~# tpe_adm -ls
    ld.so environment protection is currently on
    resentment:~# tpe_adm -ld
    ld.so environment protection disabled
    resentment:~# tpe_adm -ls
    ld.so environment protection is currently off


----[  TPE Installation

I.      Extract the code from this article:
            1. extract P54-06
            2. cd TPE/

II.     Apply the kernel diffs:
            1. cp Core/Patch/TPE-diff /usr/src/sys
            2. cd /usr/src/sys/
            3. patch < TPE-diff
            4. note any errors.  hope they are benign.

III.    Apply the ld.so diff:
            1. cp Core/Patch/ld.so-diff /usr/src/
            2. cd /usr/src/
            3. patch < ld.so-diff

IV.      Copy over the tpe core files:
            1. cp Core/kern/kern_tpe.c /usr/src/sys/kern
            2. cp Core/kern/kern_tpe_sys.c /usr/src/sys/kern
            3. cp Core/sys/kern_tpe.h /usr/src/sys/sys

V.      Rebuild your syscall table:
            1. cd /usr/src/sys/kern
            2. make

VI.     Copy over the syscall include files:
            1. cp /usr/src/sys/sys/syscall.h /usr/include/sys
            2. cp /usr/src/sys/sys/syscallargs.h /usr/include/sys

VII.    Reconfigure your kernel:
        (This step assumes you have a previously configured kernel named
         YOUR_KERNEL.  If you haven't, you need to config a kernel.  Refer
         to OpenBSD documentation on how to do this.)
            1. cd /usr/src/sys/arch/YOUR_ARCH/conf
            2. config YOUR_KERNEL

VIII.   Remake the dependencies and rebuild the kernel:
            1. cd /usr/src/sys/arch/YOUR_ARCH/compile/YOUR_KERNEL
            2. make depend ; make clean ; make
            3. note any errors.  hope you can fix them.
            3. cp /bsd /bsd.old ; cp bsd /
            4. reboot

IX.     Build the new ld.so
            1. cd /usr/src
            2. cp lib/libc/stdio/vfprintf.c /usr/src/gnu/usr.bin/ld/rtld
            3. cp lib/libc/stdio/local.h /usr/src/gnu/usr.bin/ld/rtld
            4. cp lib/libc/stdio/fvwrite.h /usr/src/gnu/usr.bin/ld/rtld
            5. cd /usr/src/gnu/usr.bin/ld/rtld
            6. make ; make install

X.      Build the TPE admin program:
            1. cd Core/Admin/ ; make
            2. make install

XI.     Test it out:
            1. As root, dump the current trust list:
               (Only UID 0 should be on it.)
               tpe_adm -s
               trusted users: root

            2. Try the following as an untrusted user (i.e. UID=1000):
               cat > foo.c << EOF ; gcc foo.c
                 int main(int argc, char **argv){ printf("Hello world\n"); }
                 EOF
               ./a.out
               EPERM should result.

            3. Now add the user to the trust list:
               tpe_adm -a UID

            4. Dump the list again:
               (You should see the user on the list.)
               tpe_adm -s

            5. Try to execute the command again as the user:
               ./a.out
               Hello world

            6. Add only the necessary UIDs to the list.

            7. NOTE TO QMAIL USERS:
               You may find that you will need to explicitly add the qmailq
               UID to the trust list.  Do this in an rc startup script that
               runs before the qmail daemons start.

            8. As root, ensure that ld.so environment protection is enabled:
               tpe_adm -le
               ld.so environment protection enabled

            9. As an unprivileged user:
               setenv LD_PRELOAD test.o
               ls -l
               Your environment contains illegal variables which are being
               stripped out for the execution of this program
               a.out  fo.c   foo.c

           10. As root, ensure that ld.so environment protection is disabled:
               tpe_adm -ld
               ld.so environment protection disabled

           11. As an unprivileged user:
               ls
               /usr/libexec/ld.so: preload: test.o: cannot map object     

           12. You're done.  Pat yourself on the back and buy something from
               Precious Roy.


----[  The Code

<++> TPE/Core/Admin/Makefile
#   $Id: P54-06,v 1.16 1998/12/10 00:01:28 route Exp $
#   Trusted path ACL implementation for OpenBSD 2.4
#   Copyright (c) 1998 route|daemon9 and Mike D. Schiffman
#   All rights reserved.
#
#   Originally published in Phrack Magazine (http://www.phrack.com).

tpe_adm:
	$(CC) tpe_adm.c -o tpe_adm

install: tpe_adm
	install -m 711 -o 0 tpe_adm /usr/local/sbin

clean:
	rm -rf core a.out tpe_adm

# EOF
<-->
<++> TPE/Core/Admin/tpe_adm.c
/*
 *  $Id: P54-06,v 1.16 1998/12/10 00:01:28 route Exp $
 *  Trusted path ACL userland administrative agent for OpenBSD 2.4
 *
 *  Copyright (c) 1998 route|daemon9 and Mike D. Schiffman
 *  All rights reserved.
 *  Originally published in Phrack Magazine (http://www.phrack.com).
 *
 *  Thanks to nirva for helping me choose an ADT.
 *  See <sys/kern_tpe.h> for more info.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <pwd.h>
#include "../sys/kern_tpe.h"

void usage();

int
main(int argc, char **argv)
{
    uid_t list[TPE_ACL_SIZE];
    int c, i, mode;
    uid_t candidate;
    struct passwd *pwd;

    if (geteuid() && getuid())
    {
        fprintf(stderr, "root access required\n");
        exit(1);
    }

    if (argc == 1 || argc > 3)
    {
        usage();
        exit(EXIT_SUCCESS);
    }

    while ((c = getopt(argc, argv, "a:d:l:s")) != EOF)
    {
        switch (c)
        {
            case 'a':
                if (isalpha(optarg[0]))
                {
                    pwd = getpwnam(optarg);
                    if(!pwd)
                    {
                        fprintf(stderr, "Unknown user: \"%s\"\n", optarg);
                        exit(EXIT_FAILURE);
                    }
                    candidate = pwd->pw_uid;
                }
                else if (!(candidate = (uid_t)atol(optarg)))
                {
                    fprintf(stderr, "invalid UID: \"%s\"\n", optarg);
                    exit(EXIT_FAILURE);
                }
                if (syscall(SYS_tpe_adm, TPE_ADD, candidate, NULL) == -1)
                {
                    printf("Full trust list\n");
                    exit(EXIT_FAILURE);
                }
                printf("UID %d added to trust list\n", candidate);
                break;
            case 'd':
                if (isalpha(optarg[0]))
                {
                    pwd = getpwnam(optarg);
                    if(!pwd)
                    {
                        fprintf(stderr, "Unknown user: \"%s\"\n", optarg);
                        exit(EXIT_FAILURE);
                    }
                    candidate = pwd->pw_uid;
                }
                else if (!(candidate = (uid_t)atol(optarg)))
                {
                    fprintf(stderr, "invalid UID: \"%s\"\n", optarg);
                    exit(EXIT_FAILURE);
                }
                if (syscall(SYS_tpe_adm, TPE_REMOVE, candidate, NULL) == -1)
                {
                    printf("UID %d not found on trust list\n", candidate);
                    exit(EXIT_FAILURE);
                }
                printf("UID %d removed from trust list\n", candidate);
                break;
            case 'l':
                if (optarg[0] == 'e')
                {
                    if (syscall(SYS_tpe_adm, TPE_LDCHECK_E, -1, NULL) == -1)
                    {
                        printf("Unknown internal error\n"); /* should NOT fail */
                        exit(EXIT_FAILURE);
                    }
                    printf("ld.so environment protection enabled\n");
                }
                else if (optarg[0] == 'd')
                {
                    if (syscall(SYS_tpe_adm, TPE_LDCHECK_D, -1, NULL) == -1)
                    {
                        printf("Unknown internal error\n"); /* should NOT fail */
                        exit(EXIT_FAILURE);
                    }
                    printf("ld.so environment protection disabled\n");
                }
                else if (optarg[0] == 's')
                {
                    if (syscall(SYS_tpe_adm, TPE_LDCHECK_S, -1, list) == -1)
                    {
                        printf("Unknown internal error\n"); /* should NOT fail */
                        exit(EXIT_FAILURE);
                    }
                    printf("ld.so environment protection is currently %s\n",
                    list[0] ? "on" : "off");
                }
                else
                {
                    fprintf(stderr, "Huh?\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 's':
                /*
                 *  It is Very Important that `list` is an array of size
                 *  TPE_ACL_SIZE.  The kernel expects this.  Failure to do
                 *  so can result in a panic.  However, only root can issue
                 *  the tpe_adm system call.
                 */
                if (syscall(SYS_tpe_adm, TPE_SHOW, -1, list) == -1)
                {
                    /*
                     *  Should NOT fail.
                     */
                    printf("Hideous internal error\n");
                    exit(EXIT_FAILURE);
                }
                printf("trusted users: ");
                for (i = 0; list[i] != TPE_INITIALIZER; i++)
                {
                    pwd = getpwuid(list[i]);
                    if (pwd)
                    {
                        printf("%s ", pwd->pw_name);
                    }
                    else
                    {
                        printf("%d ", (int)list[i]);
                    }
                }
                printf("\n"); 
                break;
            default:
                usage();
                exit(EXIT_SUCCESS);
        }
    }
    return (0);
}


void
usage()
{
    fprintf(stderr, "usage: tpe_adm [-a UID]    Add a UID to the trust list\n"
                                   "[-d UID]    Delete a UID from the list\n"
                                   "[-l e|n]    Toggle LD_* usage\n"
                                   "[-l s]      Show status of ld.so protection\n"
                                   "[-s]        Show the current list\n");
}
<-->
<++> TPE/Core/Patch/TPE-diff
--- ./kern/init_main.c	Tue Sep 15 23:21:08 1998
+++ ../Core/kern/init_main.c	Sun Oct 18 12:26:24 1998
@@ -80,6 +80,7 @@
 
 #include <sys/syscall.h>
 #include <sys/syscallargs.h>
+#include <sys/kern_tpe.h>
 
 #include <ufs/ufs/quota.h>
 
@@ -424,6 +425,16 @@
 	srandom((u_long)(rtv.tv_sec ^ rtv.tv_usec));
 
 	randompid = 1;
+
+        tpe_init();
+        printf("Trusted patch execution list initialized\n");
+        /*
+         *  root must be added hard at this point.  For safey's sake, the
+         *  userland agent can't do anything with UID 0 to prevent morons
+         *  from locking themselves out of their machines.
+         */
+        tpe_add(0);
+
 	/* The scheduler is an infinite loop. */
 	scheduler();
 	/* NOTREACHED */
--- ./kern/syscalls.master	Thu Sep 17 13:54:04 1998
+++ ../Core/kern/syscalls.master	Sun Oct 18 12:35:59 1998
@@ -479,7 +479,8 @@
 242	UNIMPL
 243	UNIMPL
 244	UNIMPL
-245	UNIMPL
+245	STD             { int sys_tpe_adm(int mode, uid_t candidate, \
+                             uid_t *list); }
 246	UNIMPL
 247	UNIMPL
 248	UNIMPL
--- ./kern/kern_exec.c	Thu Sep 24 11:49:31 1998
+++ ../Core/kern/kern_exec.c	Sun Oct 18 12:32:03 1998
@@ -51,12 +51,16 @@
 #include <sys/mman.h>
 #include <sys/signalvar.h>
 #include <sys/stat.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
 #ifdef SYSVSHM
 #include <sys/shm.h>
 #endif
 
 #include <sys/syscallargs.h>
-
+#include <sys/kern_tpe.h>
+#include <sys/systm.h>
+ 
 #include <vm/vm.h>
 #include <vm/vm_kern.h>
 
@@ -93,6 +97,7 @@
 	struct exec_package *epp;
 {
 	int error, i;
+        struct vattr at;
 	struct vnode *vp;
 	struct nameidata *ndp;
 	size_t resid;
@@ -146,6 +151,30 @@
 	if (error)
 		goto bad2;
 	epp->ep_hdrvalid = epp->ep_hdrlen - resid;
+
+        /*
+         *  Get the file attributes of the parent directory that the
+         *  executable lives in.
+         */
+        if ((error = VOP_GETATTR(ndp->ni_dvp, &at, NULL, NULL)) != 0)
+        {
+            goto bad2;
+        }
+
+        /*
+         *  Trusted path check.
+         */
+        if (!TRUSTED_PATH(at))
+        {
+                /*
+                 *  Trusted user check.
+                 */
+                if (!TRUSTED_USER(p->p_ucred->cr_uid))
+                {
+                        error = EACCES;
+                        goto bad2;
+                }
+        }
 
 	/*
 	 * set up the vmcmds for creation of the process
--- ./conf/files	Sun Sep 27 19:43:22 1998
+++ ../Core/conf/files	Sun Oct 18 12:40:28 1998
@@ -209,6 +209,8 @@
 file kern/kern_sysctl.c
 file kern/kern_synch.c
 file kern/kern_time.c
+file kern/kern_tpe.c
+file kern/kern_tpe_sys.c
 file kern/kern_xxx.c
 file kern/subr_autoconf.c
 file kern/subr_disk.c
<-->
<++> TPE/Core/kern/kern_tpe.c
/*
 *  $Id: P54-06,v 1.16 1998/12/10 00:01:28 route Exp $
 *  Trusted path ACL implementation for OpenBSD 2.4
 *
 *  Copyright (c) 1998 route|daemon9 and Mike D. Schiffman
 *  All rights reserved.
 *  Originally published in Phrack Magazine (http://www.phrack.com).
 *
 *  Thanks to nirva for helping me choose an ADT.
 *  See <sys/kern_tpe.h> for more info.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/kern_tpe.h>

void
tpe_init()
{
    memset(tpe_acl, TPE_INITIALIZER, sizeof(uid_t) * TPE_ACL_SIZE);
    tpe_acl_candidates = 0;
    tpe_ld_check = 1;
#if (AUTO_ADD_ROOT)
    tpe_acl[0] = 0;
#endif
}


void
tpe_show()
{
    int i;

    printf("%d trusted users: ", tpe_acl_candidates);
    for (i = 0; i < tpe_acl_candidates; i++)
    {
        printf("%d ", tpe_acl[i]);
    }
    printf("\n");
}


int
tpe_add(uid_t candidate)
{
    if (tpe_acl_candidates == TPE_ACL_SIZE)
    {
        /*
         *  Full list.
         */
        return (NACK);
    }

    /*
     *  Don't add duplicates.
     */
    if ((tpe_search(candidate, 0, tpe_acl_candidates)) == NACK)
    {
	/*
	 *	Add to the end of the list, then sort.
	 */
	tpe_acl_candidates++;
	tpe_acl[tpe_acl_candidates] = candidate;
	tpe_sort(0, tpe_acl_candidates);

        printf("tpe: UID %d added to trust list\n", candidate);
    }
    else
    {
        printf("tpe: duplicate UID %d not added\n", candidate);
    }
    return (ACK);
}


int
tpe_remove(uid_t candidate)
{
    int n;

    if (tpe_acl_candidates == 0)
    {
        /*
         *  Empty list.
         */
        return (NACK);
    }			
    if ((n = tpe_search(candidate, 0, tpe_acl_candidates)) != NACK)
    {
        /*
         *  Remove the candidate (mark the slot as unused), resort the list.
         */
        tpe_acl[n] = TPE_INITIALIZER;
        tpe_acl_candidates--;
        tpe_sort(0, tpe_acl_candidates);

        printf("tpe: UID %d removed from trust list\n", candidate);
        return (ACK);
    }
    /*
     *  Not found.
     */
    return (NACK);
}


int
tpe_verify(uid_t candidate)
{
    if ((tpe_search(candidate, 0, tpe_acl_candidates)) != NACK)
    {
        return (ACK);
    }
    else
    {
        return (NACK);
    }
}


void
tpe_sort(int low, int high)
{
    int i, j, n;

    /*
     *  Standard insertion sort.
     */	
    for (i = low + 1; i <= high; i++)
    {
        COMPSWAP(tpe_acl[low], tpe_acl[i]);
    }

    for (i = low + 2; i <= high; i++)
    {
        j = i;
        n = tpe_acl[i];
        while (LESS(n, tpe_acl[j - 1]))
        {
            tpe_acl[j] = tpe_acl[j - 1];
            j--;
        }
        tpe_acl[j] = n;
    }
}


int
tpe_search(uid_t candidate, int low, int high)
{
    int n;

    /*
     *  Standard binary search.  XXX - should be iterative.
     */
    n = (low + high) / 2;

    if (low > high)
    {
        return (NACK);
    }
    if (candidate == tpe_acl[n])
    {
        return (n);
    }
    if (low == high)
    {
        return (NACK);
    }
    if (LESS(candidate, tpe_acl[n]))
    {
        return (tpe_search(candidate, low, n - 1));
    }
    else
    {
        return (tpe_search(candidate, n + 1, high));
    }
}

/* EOF */
<-->
<++> TPE/Core/kern/kern_tpe_sys.c
/*
 *  $Id: P54-06,v 1.16 1998/12/10 00:01:28 route Exp $
 *  Trusted path ACL syscall implementation for OpenBSD 2.4
 *
 *  Copyright (c) 1998 route|daemon9 and Mike D. Schiffman
 *  All rights reserved.
 *  Originally published in Phrack Magazine (http://www.phrack.com).
 *
 *  Thanks to nirva for helping me choose an ADT.
 *  See <sys/kern_tpe.h> for more info.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#include <sys/kern_tpe.h>
#include <sys/systm.h>

#include <sys/mount.h>
#include <sys/syscallargs.h>


int
sys_tpe_adm(p, v, retval)
        struct proc *p;
        void *v;
        register_t *retval;
{
    struct sys_tpe_adm_args /* {
        syscallarg(int) mode;
        syscallarg(uid_t) candidate;
        syscallarg(uid_t *) list;
    } */ *uap = v;
    register struct pcred *pc = p->p_cred;
    register int i;
    register uid_t *lp;

    /*
     *  The only thing a non root user can do is check the status of the
     *  ld.so environment protection.  This is necessary because ld.so
     *  runs without elevated privilidges and needs to check this.
     */
    if (suser(pc->pc_ucred, &p->p_acflag) && SCARG(uap, mode) != TPE_LDCHECK_S)
    {
         return (EPERM);
    }

    switch (SCARG(uap, mode))
    {
        case TPE_ADD:
            if (tpe_add(SCARG(uap, candidate)) == ACK)
            {
                return (0);
            }
            else
            {
                return (ENOSPC);            /* Ugh.  Best we can do. */
            }
        case TPE_REMOVE:
            if (tpe_remove(SCARG(uap, candidate)) == ACK)
            {
                return (0);
            }
            else
            {
                return (ENOSPC);            /* Ugh. */
            }
        case TPE_SHOW:
            lp = SCARG(uap, list); 
            if (lp == NULL)
            {
                return (ENOSPC);
            }
            else
            {
                for (i = 0; i < TPE_ACL_SIZE; i++)
                {
                    lp[i] = tpe_acl[i];
                }
                return (0);
            }
        case TPE_LDCHECK_E:
            tpe_ld_check = 1;
            return (0);
        case TPE_LDCHECK_D:
            tpe_ld_check = 0;
            return (0);
        case TPE_LDCHECK_S:
            lp = SCARG(uap, list); 
            if (lp == NULL)
            {
                return (ENOSPC);
            }
            else    /* XXX - sysctl would be cleaner. */
            {
                lp[0] = tpe_ld_check;
                return (0);
            }
        default:
            return (ENXIO);                 /* Ugh. */
    }
    return (ENXIO);
}
<-->
<++> TPE/Core/sys/kern_tpe.h
/*
 *  $Id: P54-06,v 1.16 1998/12/10 00:01:28 route Exp $
 *  Trusted path ACL implementation for OpenBSD 2.4
 *
 *  Copyright (c) 1998 route|daemon9 and Mike D. Schiffman
 *  All rights reserved.
 *  Originally published in Phrack Magazine (http://www.phrack.com).
 *
 *  Thanks to nirva for helping me choose an ADT.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      Trusted path ACL implementation for OpenBSD 2.4
 *
 *      For the full write-up please see Phrack Magazine, issue 54, article 6
 *      http://www.phrack.com
 *
 *      Overview:
 *
 *  A trusted path/ACL execution implementation for OpenBSD.  We consider
 *  a path to be trusted if the parent directory is owned by root and is not
 *  group or world writable.  We consider a user to be trusted if she is on
 *  the kernels trust list.
 *
 *      Implementation details:
 *
 *  Inside the kern_exec function, we first check the path for trust, if that
 *  fails, we then check the user's credentials to see if she is  able to run
 *  binaries in an untrusted path.  Untrusted users are not allowed to execute
 *  programs from untrusted pathes.
 *
 *  The decision was the made to use a static array to hold the trusted IDs
 *  for both convienience and runtime efficiency.  We keep the list ordered
 *  after all insertions and deletions, and therefore, we can search the list
 *  (where speed is critical) in a worst case of O(lg N).  Compare that with a
 *  sequential search in an ordered list which has a worst case of O(N).
 * 	
 *  The speed in which user ID verification is done is absolutely essential,
 *  as this check will be done for every call to exec that does not originate
 *  from a trusted path.  This has the potential to be a huge bottle neck.
 *  This was taken into consideration and the bulk of processing overhead is
 *  offloaded to list initialization and modification.
 */

#ifndef __KERN_TPE_H
#define	__KERN_TPE_H

#ifdef _KERNEL
#include <sys/types.h>
#include <sys/cdefs.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/proc.h>
#endif 

/*
 *  syscall stuff
 */
#define TPE_ADD         0       /* add an entry */
#define TPE_REMOVE      1       /* delete an entry */
#define TPE_SHOW        2       /* show the list */
#define TPE_LDCHECK_E   3       /* enable ld.so environment checking */
#define TPE_LDCHECK_D   4       /* disable ld.so environment checking */
#define TPE_LDCHECK_S   5       /* show ld.so environment check status */

#define TPE_ACL_SIZE    80      /* Shouldn't need to be larger */
#define TPE_INITIALIZER	-1      /* A UID that isn't used */

#define ACK             1       /* positive acknowledgement */
#define NACK            -1      /* negative acknowledgement */

#define LESS(X, Y)      (X < Y)
#define SWAP(X, Y)      (X ^= Y, Y ^= X, X ^= Y)
#define COMPSWAP(X, Y)  if (LESS(Y, X)) SWAP(X, Y)

/*
 *  Verify the path.  This macro is passed a filled in attr struct via
 *  VOP_GETATTR.
 */
#define TRUSTED_PATH(AT) \
(!(AT.va_mode & (S_IWGRP | S_IWOTH)) && (AT.va_uid == 0))

/*
 *  Verify the user.  This macro is passed the user's ID from the u_cred
 *  struct.
 */
#define TRUSTED_USER(UID) (tpe_verify(UID) == ACK)

uid_t tpe_acl[TPE_ACL_SIZE];    /* trusted user list */
int tpe_acl_candidates;         /* number of users on the list */
int tpe_ld_check;               /* check ld.so env */

/*
 *  Initialize the array with default values (TPE_INITIALIZER).
 */
void
tpe_init __P((
    void
    ));


/*
 *  Dump the list.
 */
void
tpe_show __P((
    void
    ));


/*
 *  Attempt to add a candidate to the list.  Only fails if the list is full.
 */
int
tpe_add __P((
    uid_t	/* candidate user for addition */
    ));


/*
 *  Attempt to remove a candidate from the list.  Only fails if the entry is
 *  not there.
 */
int
tpe_remove __P((
    uid_t       /* candidate user for deletion */
    ));


/*
 *  Verify a candidate user.
 */
int
tpe_verify __P((
    uid_t       /* candidate user for verification */
    ));


/*
 *  Insertion sort the list.
 */
void
tpe_sort __P((
    int,        /* list low element */
    int         /* list high high element */
    ));


/*
 *  Locate a uid in the list, standard recursive binary search, running in
 *  worst case of lg N.
 */
int
tpe_search __P((
    uid_t,      /* candidate user to search for */
    int,        /* list low element */
    int         /* list high high element */
    ));

#endif	/* __KERN_TPE_H */
/* EOF */
<-->
<++> PP/Patch/PP-diff
--- ./usr.bin/fstat/fstat.c.orig	Tue Oct 20 10:43:58 1998
+++ ./usr.bin/fstat/fstat.c	Tue Oct 20 10:47:22 1998
@@ -158,6 +158,7 @@
 	char *memf, *nlistf;
 	char buf[_POSIX2_LINE_MAX];
 	int cnt;
+        pid_t __uid;
 
 	arg = 0;
 	what = KERN_PROC_ALL;
@@ -248,7 +249,12 @@
 	else
 		putchar('\n');
 
+        __uid = getuid();
 	for (plast = &p[cnt]; p < plast; ++p) {
+                if (__uid)
+                {
+                    if (p->kp_eproc.e_pcred.p_ruid != __uid) continue;
+                }
 		if (p->kp_proc.p_stat == SZOMB)
 			continue;
 		dofiles(p);
--- ./bin/ps/ps.c.orig	Tue Oct 20 10:48:40 1998
+++ ./bin/ps/ps.c	Tue Oct 20 10:51:26 1998
@@ -112,6 +112,7 @@
 	dev_t ttydev;
 	pid_t pid;
 	uid_t uid;
+        uid_t __uid;
 	int all, ch, flag, i, fmt, lineno, nentries;
 	int prtheader, wflag, what, xflg;
 	char *nlistf, *memf, *swapf, errbuf[_POSIX2_LINE_MAX];
@@ -281,6 +282,8 @@
 	if (!all && ttydev == NODEV && pid == -1)  /* XXX - should be cleaner */
 		uid = getuid();
 
+        __uid = getuid();
+
 	/*
 	 * scan requested variables, noting what structures are needed,
 	 * and adjusting header widths as appropiate.
@@ -330,6 +333,20 @@
 	for (i = lineno = 0; i < nentries; i++) {
 		KINFO *ki = &kinfo[i];
 
+                /*
+                 *  root gets to see the whole proccess list.
+                 */
+                if (__uid)
+                {
+                    /*
+                     *  If the process in question is not our own, we do not
+                     *  get to see it.
+                     */
+                    if (kinfo[i].ki_p->kp_eproc.e_pcred.p_ruid != __uid)
+                    {
+                        continue;
+                    }
+                }
 		if (xflg == 0 && (KI_EPROC(ki)->e_tdev == NODEV ||
 		    (KI_PROC(ki)->p_flag & P_CONTROLT ) == 0))
 			continue;
--- ./usr.bin/w/w.c.orig	Tue Oct 20 10:52:02 1998
+++ ./usr.bin/w/w.c	Tue Oct 20 10:54:46 1998
@@ -131,6 +131,7 @@
 	int ch, i, nentries, nusers, wcmd;
 	char *memf, *nlistf, *p, *x;
 	char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX];
+        uid_t __uid;
 
 	/* Are we w(1) or uptime(1)? */
 	p = __progname;
@@ -332,6 +333,14 @@
 			    ep->utmp.ut_host + UT_HOSTSIZE - x, x);
 			p = buf;
 		}
+                __uid = getuid();
+                if (__uid)
+                (void)printf("%-*.*s %-2.2s %-*.*s ",
+                    UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
+                    strncmp(ep->utmp.ut_line, "tty", 3) ?
+                    ep->utmp.ut_line : ep->utmp.ut_line + 3,
+                    UT_HOSTSIZE, UT_HOSTSIZE, "<skulking about>");
+                else
 		(void)printf("%-*.*s %-2.2s %-*.*s ",
 		    UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name,
 		    strncmp(ep->utmp.ut_line, "tty", 3) ?
@@ -339,7 +348,14 @@
 		    UT_HOSTSIZE, UT_HOSTSIZE, *p ? p : "-");
 		pr_attime(&ep->utmp.ut_time, &now);
 		pr_idle(ep->idle);
-		pr_args(ep->kp);
+                if (__uid)
+                {
+                    printf("<this n' that>");
+                }
+                else
+                {
+                    pr_args(ep->kp);                     
+                }
 		printf("\n");
 	}
 	exit(0);
--- ./usr.bin/who/who.c.orig	Tue Aug 19 22:37:21 1997
+++ ./usr.bin/who/who.c	Tue Oct 20 10:57:04 1998
@@ -227,6 +227,7 @@
 	char state = '?';
 	static time_t now = 0;
 	time_t idle = 0;
+        uid_t __uid;
 
 	if (show_term || show_idle) {
 		if (now == 0)
@@ -265,8 +266,15 @@
 			(void)printf(" old  ");
 	}
 	
-	if (*up->ut_host)
-		printf("\t(%.*s)", UT_HOSTSIZE, up->ut_host);
+        __uid = getuid();
+        if (__uid)
+        {
+            printf("\t<skulking about>");
+        }
+        else if (*up->ut_host)
+        {
+            printf("\t(%.*s)", UT_HOSTSIZE, up->ut_host);
+        }
 	(void)putchar('\n');
 }
<--> 
<++> TPE/Core/Patch/ld.so-diff
--- gnu/usr.bin/ld/rtld/rtld.c.old	Thu Oct 22 20:44:52 1998
+++ gnu/usr.bin/ld/rtld/rtld.c	Sat Oct 24 16:44:00 1998
@@ -39,6 +39,8 @@
 #include <sys/resource.h>
 #include <sys/errno.h>
 #include <sys/mman.h>
+#include <sys/syscall.h>
+#include "/usr/src/sys/sys/kern_tpe.h"
 #ifndef MAP_COPY
 #define MAP_COPY	MAP_PRIVATE
 #endif
@@ -150,7 +152,9 @@
 static uid_t		uid, euid;
 static gid_t		gid, egid;
 static int		careful;
+static int		tpe_ld_strip;
 static int		anon_fd = -1;
+static uid_t            list[TPE_ACL_SIZE];
 
 struct so_map		*link_map_head, *main_map;
 struct so_map		**link_map_tail = &link_map_head;
@@ -271,7 +275,20 @@
 
 	careful = (uid != euid) || (gid != egid);
 
-	if (careful) {
+        if (syscall(SYS_tpe_adm, TPE_LDCHECK_S, -1, list) == -1)
+        {
+            fprintf(stderr, "Unknown internal error\n"); /* should NOT fail */
+            exit(EXIT_FAILURE);
+        }
+        if (list[0] && uid)
+        {
+            if (getenv("LD_PRELOAD") || getenv("LD_LIBRARY_PATH"))
+            {
+                fprintf(stderr, "Your environment contains illegal variables which are being stripped out for the execution of this program.\n");
+            }
+            tpe_ld_strip = 1;
+        }
+	if (careful || tpe_ld_strip) {
 		unsetenv("LD_LIBRARY_PATH");
 		unsetenv("LD_PRELOAD");
 	}
<-->
----[  EOF
