2
* safopen.c - hopefully secure wrappper for fopen()
3
* $Id: safopen.c,v 1.5 2005/05/10 13:49:17 rdenisc Exp $
6
/***********************************************************************
7
* Copyright (C) 2002-2004 Remi Denis-Courmont. *
8
* This program is free software; you can redistribute and/or modify *
9
* it under the terms of the GNU General Public License as published *
10
* by the Free Software Foundation; version 2 of the license. *
12
* This program is distributed in the hope that it will be useful, *
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
15
* See the GNU General Public License for more details. *
17
* You should have received a copy of the GNU General Public License *
18
* along with this program; if not, you can get it from: *
19
* http://www.gnu.org/copyleft/gpl.html *
20
***********************************************************************/
27
#include <string.h> /* memset(), memcmp() */
28
#include <fcntl.h> /* open() */
29
#include <sys/stat.h> /* lstat(), fstat() */
30
#include <unistd.h> /* close(), lseek() */
37
secure_fopen (const char *path, const char *mode)
39
return fopen (path, mode);
44
# define O_NOFOLLOW 0 /* only *BSD & Linux and maybe few others have that */
48
# define O_LARGEFILE 0
51
#if (defined (HAVE_STAT) && !defined (HAVE_LSTAT))
52
# define lstat( filename, structure ) stat (filename, structure)
53
/* lstat() is not defined by POSIX. We use stat() instead. */
58
* As secure as possible replacement fopen(path, "a").
59
* This function is used to open all log files (excepted stdout).
61
* DO submit patches against this function if you think it is still
62
* insecure, and have other ideas.
64
* I currently assume that the situation is as follow:
66
* # Read-only open mode is secure.
68
* # Append open mode (whether read is enabled or not) is secure as long as
69
* the file is regular or does not exists. We need lstat() and fstat().
70
* To prevent a race condition, we check the file after it has been opened.
72
* # Write open mode, which causes the file to be erased automatically, is
73
* never absolutely secure. The file has to be regular or not to exists.
74
* To minimize race condition risks, we need O_NOFOLLOW (but this does not
75
* protect from non link, non regular files that we may open, for example,
78
* It is up to the caller to set adequate eUID, eGID and umask before calling
82
secure_fopen (const char *path, const char *mode)
93
} opts = { 0, 0, 0, 0, 0 };
99
/* Digests open mode */
100
for (modep = mode; *modep; modep++)
108
opts.writf = opts.creaf = opts.trunf = 1;
112
opts.writf = opts.creaf = opts.appef = 1;
116
opts.readf = opts.writf = 1;
121
flags = (opts.writf) ? O_RDWR : O_RDONLY;
129
return NULL; /* don't read, nor write! */
137
/* Gets current file state */
138
memset (&st1, 0, sizeof (st1));
139
if (lstat (path, &st1))
141
if ((errno != ENOENT) || !opts.creaf)
144
flags |= O_CREAT|O_EXCL;
146
* The file did NOT exists at the time of lstat().
147
* O_CREAT will create it, while O_EXCL will prevent a race
148
* condition if it is created between lstat() and open() by
149
* another concurrent process.
152
else /* The file already exists. We don't want to create it. */
157
* If the file is to be opened in write mode, we have some
160
* Refuses to open a non-regular file. This includes:
161
* # links: might be used to modify files holding important
163
* # FIFO, some devices: may block output to log file and I
164
* know no legitimate use them anyway.
165
* # sockets, directories, other devices: usually cannot be
166
* opened or used properly anyway.
168
* Refuses to open hard-linked file for the same reason as
172
if ((st1.st_nlink != 1) || !S_ISREG (st1.st_mode))
185
fd = open (path, flags | O_LARGEFILE, 0666);
193
* Now, we have to make sure the file was not changed
194
* between lstat() and open().
196
memset (&st2, 0, sizeof (st2));
198
|| memcmp (&st1, &st2, sizeof (struct stat)))
206
/* Goes to end of file if needed */
207
if (opts.appef && (lseek (fd, 0, SEEK_END) == -1))
213
stream = fdopen (fd, mode);