~ubuntu-branches/ubuntu/intrepid/tcpreen/intrepid

« back to all changes in this revision

Viewing changes to logs/safopen.c

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Kleineidam
  • Date: 2006-05-02 21:24:00 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20060502212400-v5m70qcaqwdmt2bg
Tags: 1.4.3-0.1
* NMU, with permission from maintainer.
* New upstream release (Closes: #269639, #211956)
* Standards version 3.7.0.0
* Use debhelper v5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * safopen.c - hopefully secure wrappper for fopen()
 
3
 * $Id: safopen.c,v 1.5 2005/05/10 13:49:17 rdenisc Exp $
 
4
 */
 
5
 
 
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.         *
 
11
 *                                                                     *
 
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.               *
 
16
 *                                                                     *
 
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
 ***********************************************************************/
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
# include <config.h>
 
24
#endif
 
25
 
 
26
#include <stdio.h>
 
27
#include <string.h> /* memset(), memcmp() */
 
28
#include <fcntl.h> /* open() */
 
29
#include <sys/stat.h> /* lstat(), fstat() */
 
30
#include <unistd.h> /* close(), lseek() */
 
31
#include <errno.h>
 
32
 
 
33
#include "secstdio.h"
 
34
 
 
35
#ifdef WINSOCK
 
36
FILE *
 
37
secure_fopen (const char *path, const char *mode)
 
38
{
 
39
        return fopen (path, mode);
 
40
}
 
41
#else
 
42
 
 
43
# ifndef O_NOFOLLOW
 
44
#  define O_NOFOLLOW 0 /* only *BSD & Linux and maybe few others have that */
 
45
# endif
 
46
 
 
47
# ifndef O_LARGEFILE
 
48
#  define O_LARGEFILE 0
 
49
# endif
 
50
 
 
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. */
 
54
# define HAVE_LSTAT 1
 
55
#endif
 
56
 
 
57
/*
 
58
 * As secure as possible replacement fopen(path, "a").
 
59
 * This function is used to open all log files (excepted stdout).
 
60
 *
 
61
 * DO submit patches against this function if you think it is still
 
62
 * insecure, and have other ideas.
 
63
 *
 
64
 * I currently assume that the situation is as follow:
 
65
 *
 
66
 * # Read-only open mode is secure.
 
67
 *
 
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.
 
71
 *
 
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,
 
76
 * FIFOs).
 
77
 *
 
78
 * It is up to the caller to set adequate eUID, eGID and umask before calling
 
79
 * this function.
 
80
 */
 
81
FILE *
 
82
secure_fopen (const char *path, const char *mode)
 
83
{
 
84
        int fd, flags;
 
85
        const char *modep;
 
86
        struct
 
87
        {
 
88
                unsigned readf:1;
 
89
                unsigned writf:1;
 
90
                unsigned creaf:1;
 
91
                unsigned trunf:1;
 
92
                unsigned appef:1;
 
93
        } opts = { 0, 0, 0, 0, 0 };
 
94
        FILE *stream;
 
95
#ifdef HAVE_LSTAT
 
96
        struct stat st1, st2;
 
97
#endif
 
98
 
 
99
        /* Digests open mode */
 
100
        for (modep = mode; *modep; modep++)
 
101
                switch (*modep)
 
102
                {
 
103
                        case 'r':
 
104
                                opts.readf = 1;
 
105
                                break;
 
106
                                
 
107
                        case 'w':
 
108
                                opts.writf = opts.creaf = opts.trunf = 1;
 
109
                                break;
 
110
                                
 
111
                        case 'a':
 
112
                                opts.writf = opts.creaf = opts.appef = 1;
 
113
                                break;
 
114
                                
 
115
                        case '+':
 
116
                                opts.readf = opts.writf = 1;
 
117
                                break;
 
118
                }
 
119
 
 
120
        if (opts.readf)
 
121
                flags = (opts.writf) ? O_RDWR : O_RDONLY;
 
122
        else
 
123
        {
 
124
                if (opts.writf)
 
125
                        flags = O_WRONLY;
 
126
                else
 
127
                {
 
128
                        errno = EACCES;
 
129
                        return NULL; /* don't read, nor write! */
 
130
                }
 
131
        }
 
132
 
 
133
        if (opts.trunf)
 
134
                flags |= O_TRUNC;
 
135
 
 
136
#ifdef HAVE_LSTAT
 
137
        /* Gets current file state */
 
138
        memset (&st1, 0, sizeof (st1));
 
139
        if (lstat (path, &st1))
 
140
        {
 
141
                if ((errno != ENOENT) || !opts.creaf)
 
142
                        return NULL;
 
143
 
 
144
                flags |= O_CREAT|O_EXCL;
 
145
                /*
 
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.
 
150
                 */
 
151
        }
 
152
        else    /* The file already exists. We don't want to create it. */
 
153
        {
 
154
                opts.creaf = 0;
 
155
                if (opts.writf)
 
156
                /*
 
157
                 * If the file is to be opened in write mode, we have some
 
158
                 * safety checks:
 
159
                 * 
 
160
                 * Refuses to open a non-regular file. This includes:
 
161
                 * # links: might be used to modify files holding important
 
162
                 *   data
 
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.
 
167
                 *
 
168
                 * Refuses to open hard-linked file for the same reason as
 
169
                 * links.
 
170
                 */
 
171
                {
 
172
                        if ((st1.st_nlink != 1) || !S_ISREG (st1.st_mode))
 
173
                        {
 
174
                                errno = EPERM;
 
175
                                return NULL;
 
176
                        }
 
177
                        flags |= O_NOFOLLOW;
 
178
                }
 
179
        }
 
180
#else
 
181
        if (opts.creaf)
 
182
                flags |= O_CREAT;
 
183
#endif
 
184
 
 
185
        fd = open (path, flags | O_LARGEFILE, 0666);
 
186
        if (fd == -1)
 
187
                return NULL;
 
188
        
 
189
#ifdef HAVE_LSTAT
 
190
        if (!opts.creaf)
 
191
        {
 
192
                /*
 
193
                 * Now, we have to make sure the file was not changed
 
194
                 * between lstat() and open().
 
195
                 */ 
 
196
                memset (&st2, 0, sizeof (st2));
 
197
                if (fstat (fd, &st2)
 
198
                 || memcmp (&st1, &st2, sizeof (struct stat)))
 
199
                {
 
200
                        close (fd);
 
201
                        errno = EPERM;
 
202
                        return NULL;
 
203
                }
 
204
        }
 
205
#endif
 
206
        /* Goes to end of file if needed */
 
207
        if (opts.appef && (lseek (fd, 0, SEEK_END) == -1))
 
208
        {
 
209
                close (fd);
 
210
                return NULL;
 
211
        }
 
212
        
 
213
        stream = fdopen (fd, mode);
 
214
        if (stream == NULL)
 
215
                close (fd);
 
216
        return stream;
 
217
}
 
218
 
 
219
#endif /* WINSOCK */