bzip2recover: create output block files safely with O_EXCL

bzip2recover writes each recovered block to a file whose name is fully
predictable ("recNNNNN" + basename of the input, e.g. rec00001foo.bz2)
and which is created in the directory of the input file.  The output
file was opened with a plain fopen(outFileName, "wb"), which follows
symbolic links and truncates whatever it finds.

If the input file lives in a directory that an attacker can write to
(a shared spool/incoming directory, or any directory whose path the
attacker supplied), the attacker can pre-create a symlink at the
predictable output name pointing at an arbitrary file.  When the victim
runs bzip2recover, the plain fopen follows that symlink and overwrites
the target (CWE-59).  Existing regular files are likewise clobbered,
and the files are created with lax umask permissions.

The main bzip2 tool already guards against this in fopen_output_safely()
by creating output files with open(O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|
S_IRUSR); bzip2recover was never given the same treatment.  Add a small
fopenOutputSafely() helper mirroring that function and use it for the
block files: O_EXCL makes the create fail rather than follow a symlink
or overwrite an existing file, and the mode creates the file 0600.
Non-Unix builds keep the previous fopen behaviour.

Signed-off-by: Naveed Khan <naveed@digiscrypt.com>
diff --git a/bzip2recover.c b/bzip2recover.c
index e6c0a99..c17206b 100644
--- a/bzip2recover.c
+++ b/bzip2recover.c
@@ -25,6 +25,12 @@
 #include <stdlib.h>
 #include <string.h>
 
+#ifndef _WIN32
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+
 
 /* This program records bit locations in the file to be recovered.
    That means that if 64-bit ints are not supported, we will not
@@ -272,6 +278,30 @@
 }
 
 
+/*---------------------------------------------*/
+/* Create an output block file safely.  The block file names are
+   predictable and are written into the directory of the (possibly
+   untrusted) input file, so a plain fopen() would follow a
+   pre-planted symlink and overwrite an arbitrary file, or silently
+   clobber an existing one.  Mirror fopen_output_safely() in bzip2.c:
+   create with O_EXCL and restrictive permissions so we never follow a
+   symlink and never overwrite an existing file. */
+static FILE* fopenOutputSafely ( Char* name )
+{
+#ifdef _WIN32
+   return fopen ( name, "wb" );
+#else
+   FILE* fp;
+   Int32 fh;
+   fh = open ( name, O_WRONLY|O_CREAT|O_EXCL, S_IWUSR|S_IRUSR );
+   if (fh == -1) return NULL;
+   fp = fdopen ( fh, "wb" );
+   if (fp == NULL) close ( fh );
+   return fp;
+#endif
+}
+
+
 /*---------------------------------------------------*/
 /*---                                             ---*/
 /*---------------------------------------------------*/
@@ -499,7 +529,7 @@
          fprintf ( stderr, "   writing block %d to `%s' ...\n",
                            wrBlock+1, outFileName );
 
-         outFile = fopen ( outFileName, "wb" );
+         outFile = fopenOutputSafely ( outFileName );
          if (outFile == NULL) {
             fprintf ( stderr, "%s: can't write `%s'\n",
                       progName, outFileName );