COMMAND

    make

SYSTEMS AFFECTED

    All  FreeBSD  versions  before  the  correction date (2000-01-16),
    NetBSD, OpenBSD

PROBLEM

    Following is based on FreeBSD-SA-00:01 Security Advisory  FreeBSD.
    The  make(1)  program  is  typically  used to schedule building of
    source code.   It has a  switch ('-j') to  allow parallel building
    by spawning multiple child processes.

    The  -j  option  to  make(1)  uses  temporary  files  in  /tmp  to
    communicate with its child processes by storing the shell  command
    the  child  should  execute.  This  is  useful  on multi-processor
    architectures for making use of all of the available CPUs, and  is
    also  widely  used  on   uniprocessor  systems  to  minimize   the
    scheduling latency of the build process.

    However  make(1)  uses  the  temporary  file  in  an insecure way,
    repeatedly deleting and reusing the same file name for the  entire
    life of the program.  This makes it vulnerable to a race condition
    wherein a malicious user could  observe the name of the  temporary
    file being used, and replace  the contents of a later  instance of
    the file with her  desired commands after the  legitimate commands
    have been written.

    This vulnerability was discovered as part of the FreeBSD  Auditing
    Project,  an  ongoing  effort  to  identify  and  correct security
    vulnerabilities in the FreeBSD operating system.

    All  versions  of  NetBSD  and  OpenBSD  are  also  believed to be
    vulnerable to  this problem.   Other systems  using a  BSD-derived
    make(1) binary may also be vulnerable.  Local users could  execute
    arbitrary shell commands as part of the build process scheduled by
    "make -j" by another user.

SOLUTION

    Avoid using the '-j' flag to make(1).  Upgrade your system to  one
    that is listed above as having the problem resolved, or patch your
    present system.   To patch  your present  system: save  the  patch
    below into a file, and execute the following commands as root:

        cd /usr/src/usr.bin/make
        patch < /path/to/patch/file
        make all
        make install

    Patches  for  3.4-STABLE  and   4.0-CURRENT  systems  before   the
    resolution date:

    Index: job.c
    ===================================================================
    RCS file: /home/ncvs/src/usr.bin/make/job.c,v
    retrieving revision 1.16
    diff -u -r1.16 job.c
    --- job.c   1999/09/11 13:08:01     1.16
    +++ job.c   2000/01/17 01:42:57
    @@ -163,14 +163,6 @@
     #define JOB_STOPPED        3       /* The job is stopped */
    
     /*
    - * tfile is the name of a file into which all shell commands are put. It is
    - * used over by removing it before the child shell is executed. The XXXXXXXXXX
    - * in the string are replaced by mkstemp(3).
    - */
    -static char     tfile[sizeof(TMPPAT)];
    -
    -
    -/*
      * Descriptions for various shells.
      */
     static Shell    shells[] = {
    @@ -993,7 +985,7 @@
        /*
         * If we are aborting and the job table is now empty, we finish.
         */
    -   (void) eunlink(tfile);
    +   (void) eunlink(job->tfile);
        Finish(errors);
         }
     }
    @@ -1668,6 +1660,7 @@
     Boolean          cmdsOK;     /* true if the nodes commands were all right */
         Boolean          local;      /* Set true if the job was run locally */
         Boolean          noExec;     /* Set true if we decide not to run the job */
    +    int              tfd;        /* File descriptor for temp file */
    
         if (previous != NULL) {
        previous->flags &= ~(JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE);
    @@ -1697,6 +1690,12 @@
         }
         job->flags |= flags;
    
    +    (void) strcpy(job->tfile, TMPPAT);
    +    if ((tfd = mkstemp(job->tfile)) == -1)
    +   Punt("cannot create temp file: %s", strerror(errno));
    +    else
    +   (void) close(tfd);
    +
         /*
          * Check the commands now so any attributes from .DEFAULT have a chance
          * to migrate to the node
    @@ -1722,9 +1721,9 @@
            DieHorribly();
        }
    
    -   job->cmdFILE = fopen(tfile, "w+");
    +   job->cmdFILE = fopen(job->tfile, "w+");
        if (job->cmdFILE == NULL) {
    -       Punt("Could not open %s", tfile);
    +       Punt("Could not open %s", job->tfile);
        }
        (void) fcntl(FILENO(job->cmdFILE), F_SETFD, 1);
        /*
    @@ -1830,7 +1829,7 @@
         * Unlink and close the command file if we opened one
         */
        if (job->cmdFILE != stdout) {
    -       (void) eunlink(tfile);
    +       (void) eunlink(job->tfile);
            if (job->cmdFILE != NULL)
                (void) fclose(job->cmdFILE);
        } else {
    @@ -1859,7 +1858,7 @@
        }
         } else {
        (void) fflush(job->cmdFILE);
    -   (void) eunlink(tfile);
    +   (void) eunlink(job->tfile);
         }
    
         /*
    @@ -2403,13 +2402,6 @@
                             * be running at once. */
     {
         GNode         *begin;     /* node for commands to do at the very start */
    -    int              tfd;
    -
    -    (void) strcpy(tfile, TMPPAT);
    -    if ((tfd = mkstemp(tfile)) == -1)
    -   Punt("cannot create temp file: %s", strerror(errno));
    -    else
    -   (void) close(tfd);
    
         jobs =           Lst_Init(FALSE);
         stoppedJobs = Lst_Init(FALSE);
    @@ -2914,7 +2906,7 @@
            }
        }
         }
    -    (void) eunlink(tfile);
    +    (void) eunlink(job->tfile);
     }
    
     /*
    @@ -2948,7 +2940,6 @@
            }
        }
         }
    -    (void) eunlink(tfile);
         return(errors);
     }
    
    @@ -3024,6 +3015,7 @@
            KILL(job->pid, SIGINT);
            KILL(job->pid, SIGKILL);
     #endif /* RMT_WANTS_SIGNALS */
    +       (void) eunlink(job->tfile);
        }
         }
    
    @@ -3032,7 +3024,6 @@
          */
         while (waitpid((pid_t) -1, &foo, WNOHANG) > 0)
        continue;
    -    (void) eunlink(tfile);
     }
    
     #ifdef REMOTE
    Index: job.h
    ===================================================================
    RCS file: /home/ncvs/src/usr.bin/make/job.h,v
    retrieving revision 1.10
    diff -u -r1.10 job.h
    --- job.h   1999/08/28 01:03:31     1.10
    +++ job.h   2000/01/17 01:42:31
    @@ -93,6 +93,8 @@
     #define JOB_BUFSIZE        1024
     typedef struct Job {
         int            pid;        /* The child's process ID */
    +    char   tfile[sizeof(TMPPAT)];
    +                       /* Temporary file to use for job */
         GNode          *node;      /* The target the child is making */
         LstNode        tailCmds;   /* The node of the first command to be
                             * saved when the job has been run */