COMMAND

    open()

SYSTEMS AFFECTED

    FreeBSD 2.1.*, FreeBSD 2.2.*, FreeBSD-stable and  FreeBSD-current,
    NetBSD, OpenBSD

PROBLEM

    In  FreeBSD,  the  open()  system  call  is  used  in  normal file
    operations.  When calling open(), the caller should specify if the
    file is to be  opened for reading, for  writing or for both.   The
    right to reading  from and/or writing  to a file  is controlled by
    the file's  mode bits  in the  filesystem.   In FreeBSD, open() is
    also used to obtain the right to do privileged io instructions.

    A  problem  exists  in  the  open()  syscall that allows processes
    to obtain  a valid  file descriptor  without having  read or write
    permissions  on  the  file  being  opened.  This is normally not a
    problem.  The  FreeBSD  way  of  obtaining  the  right  to  do  io
    instructions however,  is based  on the  right to  open a specific
    file  (/dev/io).   The  problem  can  be  used  by any user on the
    system to do unauthorised io instructions.

    This is a pretty serious hole in open() and permissions...   Note,
    in  the  following,  open()  succeeds,  and  ioctls  are  probably
    executed...  This was posted by explorer.

    /*
     * This will give you a file descriptor on a device you should not
     * have access  to.  This  seems really, really  screwed up, since
     * holding a fd lets you do a lot of ioctls that you should not be
     * able to do...
     */
    #include <fcntl.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <err.h>

    int
    main(int argc, char **argv)
    {
      int fd;

      fd = open("/dev/rsd0a", -1, 0);

      if (fd < 0)
      err(1, "open");
    }

    This is a variant  of a bug Theo  de Raadt found in  SunOS back in
    the 1980s.  The  basic issue is that  the code that guards  access
    to the device-specific open() routine checks explicitly for FREAD,
    FWRITE, and O_TRUNC, and passes the call through if none of  these
    are set.  Theo's bug involved using "3" for the open() flag.

    The problem here is that before calls to open() are even passed to
    the vnode  open() routine  (after the  vnode is  looked up  by the
    generic vfs_syscalls open() syscall  handler), the flags field  is
    incremented by one:

    vfs_syscalls.c:open()

            ...

            flags = FFLAGS(uap->flags);

            ...

    where FFLAGS() is:

    ./sys/fcntl.h:#define  FFLAGS(oflags)  ((oflags) + 1)

    As you can see,  passing a "-1" to  open() will result in  "flags"
    becoming "0"  - open()  ordinarily never  passes "0"  to the vnode
    code,  since  "0"  becomes  "1"  after  being  converted to fflags
    format.

    A fun game you can play with practically no programming ability is
    to exploit the fact  that some devices will  initialize themselves
    immediately upon being opened  - particularly amusing is  the SCSI
    tape driver, sys/scsi/st.c, which will rewind itself when  opened.
    Simply run  the previously  posted example  code on  /dev/rst0 and
    destroy tonight's backup.

SOLUTION

    You may want to get closer look ar FreeBSD patch:

        ftp://freebsd.org/pub/CERT/patches/SA-97:05/