Logo Search packages:      
Sourcecode: tcpreen version File versions  Download package

safopen.c

/*
 * safopen.c - secure_append_fopen() as_secure_as_I_can file
 * opening-for-append function.
 * $Id: safopen.c,v 1.4 2003/01/26 09:09:08 rdenisc Exp $
 */

/***********************************************************************
 *  Copyright (C) 2002-2003 Rémi Denis-Courmont.                       *
 *  This program is free software; you can redistribute and/or modify  *
 *  it under the terms of the GNU General Public License as published  *
 *  by the Free Software Foundation; version 2 of the license.         *
 *                                                                     *
 *  This program 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 General Public License for more details.               *
 *                                                                     *
 *  You should have received a copy of the GNU General Pulic License   *
 *  along with this program; if not, you can get it from:              *
 *  http://www.gnu.org/copyleft/gpl.html                               *
 ***********************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#ifdef HAVE_FCNTL_H
# include <fcntl.h> /* open() */
#endif
#ifdef HAVE_SYS_STAT_H
# include <sys/stat.h> /* lstat(), fstat() */
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h> /* close(), lseek() */
#endif
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif

#ifndef O_NOFOLLOW
# define O_NOFOLLOW 0 /* only FreeBSD+Linux and the likes have this */
#endif

/*
 * As secure as possible replacement fopen(path, "a").
 * This function is used to open all log files (excepted stdout).
 *
 * DO submit patches against this function if you think it is still
 * insecure, and have other ideas.
 */
FILE *
secure_append_fopen (const char *path)
{
      int fd, flags;
      FILE *stream;
#ifdef HAVE_LSTAT
      struct stat st1, st2;

      memset (&st1, 0, sizeof(st1));
      memset (&st2, 0, sizeof(st2));

      if (lstat (path, &st1))
      {
            if (errno != ENOENT)
                  return NULL;
            flags = O_CREAT|O_EXCL; /* The file DID NOT exists at
                  the time of lstat(). O_CREAT will create it,
                  and O_EXCL will prevent a race condition if it
                  is created between lstat() and open() by
                  another concurrent process. */
      }
      else
      { /* Refuses to open a non-regular file. This includes:
      # links: might be used to modify files holding sensible data
      # FIFO: may block output to log file, and cause a Denial of
        service (tcpreen waiting forever for write() to return).
      Other file types are normally not useable (sockets, devices
      directories). */
            if (!S_ISREG (st1.st_mode)
      /* Refuses to open a file we do not own, even if we can. */
             || (st1.st_uid != geteuid())
      /* Refuses to open a hard-linked file. */
             || (st1.st_nlink != 1))
            {
                  errno = EPERM;
                  return NULL;
            }
            flags = 0;
      }
#else
      flags = O_CREAT;
#endif
      
      if ((fd = open (path, O_WRONLY|O_NOFOLLOW|flags, 0600
                  /* paranoid file permissions */)) == -1)
            return NULL;
#ifdef HAVE_LSTAT
      if (flags == 0)
      {
      /* Now, we have to make sure the file was not changed between
      lstat() and open(). */
            if (fstat (fd, &st2))
            {
                  close (fd);
                  return NULL;
            }
            if (memcmp (&st1, &st2, sizeof(st1) /* = sizeof(st2) */)) {
                  close (fd);
                  errno = EPERM;
                  return NULL;
            }
      }
#endif
      /* We use lseek(SEEK_END) instead of open(O_APPEND) because it
      does not work in some cases (namely NFS). */
      if ((lseek (fd, 0, SEEK_END) == -1)
       || ((stream = fdopen (fd, "w")) == NULL)) {
            int saved_errno;

            saved_errno = errno;
            close (fd);
            errno = saved_errno;
            return NULL;
      }
      return stream;
}


Generated by  Doxygen 1.6.0   Back to index