Revert part of the previous patch, let the caller take care
[geeqie.git] / src / secure_save.c
1 /*
2  * Geeqie
3  *
4  * Author: Vladimir Nadvornik
5  * based on the code developped for ELinks by Laurent Monin
6  *
7  * This software is released under the GNU General Public License (GNU GPL).
8  * Please read the included file COPYING for more information.
9  * This software comes with no warranty of any kind, use at your own risk!
10  */
11
12 #include <glib/gstdio.h>
13 #include <errno.h>
14
15 #include "main.h"
16 #include "secure_save.h"
17
18 /* ABOUT SECURE SAVE */
19 /* This code was borrowed from the ELinks project (http://elinks.cz)
20  * It was originally written by me (Laurent Monin aka Zas) and heavily
21  * modified and improved by all ELinks contributors.
22  * This code was released under the GPLv2 licence.
23  * It was modified to be included in geeqie on 2008/04/05 */
24
25 /* If ssi->secure_save is TRUE:
26  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27  *
28  * A call to secure_open("/home/me/.confdir/filename", mask) will open a file
29  * named "filename.tmp_XXXXXX" in /home/me/.confdir/ and return a pointer to a
30  * structure SecureSaveInfo on success or NULL on error.
31  *
32  * filename.tmp_XXXXXX can't conflict with any file since it's created using
33  * mkstemp(). XXXXXX is a random string.
34  *
35  * Subsequent write operations are done using returned SecureSaveInfo FILE *
36  * field named fp.
37  *
38  * If an error is encountered, SecureSaveInfo int field named err is set
39  * (automatically if using secure_fp*() functions or by programmer)
40  *
41  * When secure_close() is called, "filename.tmp_XXXXXX" is flushed and closed,
42  * and if SecureSaveInfo err field has a value of zero, "filename.tmp_XXXXXX"
43  * is renamed to "filename". If this succeeded, then secure_close() returns 0.
44  *
45  * WARNING: since rename() is used, any symlink called "filename" may be
46  * replaced by a regular file. If destination file isn't a regular file,
47  * then secsave is disabled for that file.
48  *
49  * If ssi->secure_save is FALSE:
50  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51  *
52  * No temporary file is created, "filename" is truncated, all operations are
53  * done on it, no rename nor flush occur, symlinks are preserved.
54  *
55  * In both cases:
56  * ~~~~~~~~~~~~~
57  *
58  * Access rights are affected by secure_open() mask parameter.
59  */
60
61 /* FIXME: locking system on files about to be rewritten ? */
62 /* FIXME: Low risk race conditions about ssi->file_name. */
63
64 SecureSaveErrno secsave_errno = SS_ERR_NONE;
65
66
67 /** Open a file for writing in a secure way. @returns a pointer to a
68  * structure secure_save_info on success, or NULL on failure. */
69 static SecureSaveInfo *
70 secure_open_umask(const gchar *file_name)
71 {
72         struct stat st;
73         SecureSaveInfo *ssi;
74
75         secsave_errno = SS_ERR_NONE;
76
77         ssi = g_new0(SecureSaveInfo, 1);
78         if (!ssi) {
79                 secsave_errno = SS_ERR_OUT_OF_MEM;
80                 goto end;
81         }
82
83         ssi->secure_save = TRUE;
84
85         ssi->file_name = g_strdup(file_name);
86         if (!ssi->file_name) {
87                 secsave_errno = SS_ERR_OUT_OF_MEM;
88                 goto free_f;
89         }
90
91         /* Check properties of final file. */
92 #ifndef NO_UNIX_SOFTLINKS
93         if (g_lstat(ssi->file_name, &st)) {
94 #else
95         if (g_stat(ssi->file_name, &st)) {
96 #endif
97                 /* We ignore error caused by file inexistence. */
98                 if (errno != ENOENT) {
99                         /* lstat() error. */
100                         ssi->err = errno;
101                         secsave_errno = SS_ERR_STAT;
102                         goto free_file_name;
103                 }
104         } else {
105                 if (!S_ISREG(st.st_mode)) {
106                         /* Not a regular file, secure_save is disabled. */
107                         ssi->secure_save = 0;
108                 } else {
109 #ifdef HAVE_ACCESS
110                         /* XXX: access() do not work with setuid programs. */
111                         if (g_access(ssi->file_name, R_OK | W_OK) < 0) {
112                                 ssi->err = errno;
113                                 secsave_errno = SS_ERR_ACCESS;
114                                 goto free_file_name;
115                         }
116 #else
117                         FILE *f1;
118
119                         /* We still have a race condition here between
120                          * [l]stat() and fopen() */
121
122                         f1 = g_fopen(ssi->file_name, "rb+");
123                         if (f1) {
124                                 fclose(f1);
125                         } else {
126                                 ssi->err = errno;
127                                 secsave_errno = SS_ERR_OPEN_READ;
128                                 goto free_file_name;
129                         }
130 #endif
131                 }
132         }
133
134         if (ssi->secure_save) {
135                 /* We use a random name for temporary file, mkstemp() opens
136                  * the file and return a file descriptor named fd, which is
137                  * then converted to FILE * using fdopen().
138                  */
139                 gint fd;
140                 gchar *randname = g_strconcat(ssi->file_name, ".tmp_XXXXXX", NULL);
141
142                 if (!randname) {
143                         secsave_errno = SS_ERR_OUT_OF_MEM;
144                         goto free_file_name;
145                 }
146
147                 /* No need to use safe_mkstemp() here. --Zas */
148                 fd = g_mkstemp(randname);
149                 if (fd == -1) {
150                         secsave_errno = SS_ERR_MKSTEMP;
151                         g_free(randname);
152                         goto free_file_name;
153                 }
154
155                 ssi->fp = fdopen(fd, "wb");
156                 if (!ssi->fp) {
157                         secsave_errno = SS_ERR_OPEN_WRITE;
158                         ssi->err = errno;
159                         g_free(randname);
160                         goto free_file_name;
161                 }
162
163                 ssi->tmp_file_name = randname;
164         } else {
165                 /* No need to create a temporary file here. */
166                 ssi->fp = g_fopen(ssi->file_name, "wb");
167                 if (!ssi->fp) {
168                         secsave_errno = SS_ERR_OPEN_WRITE;
169                         ssi->err = errno;
170                         goto free_file_name;
171                 }
172         }
173
174         return ssi;
175
176 free_file_name:
177         g_free(ssi->file_name);
178         ssi->file_name = NULL;
179
180 free_f:
181         g_free(ssi);
182         ssi = NULL;
183
184 end:
185         return NULL;
186 }
187
188 SecureSaveInfo *
189 secure_open(const gchar *file_name)
190 {
191         SecureSaveInfo *ssi;
192         mode_t saved_mask;
193 #ifdef CONFIG_OS_WIN32
194         /* There is neither S_IRWXG nor S_IRWXO under crossmingw32-gcc */
195         const mode_t mask = 0177;
196 #else
197         const mode_t mask = S_IXUSR | S_IRWXG | S_IRWXO;
198 #endif
199
200         saved_mask = umask(mask);
201         ssi = secure_open_umask(file_name);
202         umask(saved_mask);
203
204         return ssi;
205 }
206
207 /** Close a file opened with secure_open(). Rreturns 0 on success,
208  * errno or -1 on failure.
209  */
210 gint
211 secure_close(SecureSaveInfo *ssi)
212 {
213         gint ret = -1;
214
215         if (!ssi) return ret;
216         if (!ssi->fp) goto free;
217
218         if (ssi->err) { /* Keep previous errno. */
219                 ret = ssi->err;
220                 fclose(ssi->fp); /* Close file */
221                 goto free;
222         }
223
224         /* Ensure data is effectively written to disk, we first flush libc buffers
225          * using fflush(), then fsync() to flush kernel buffers, and finally call
226          * fclose() (which call fflush() again, but the first one is needed since
227          * it doesn't make much sense to flush kernel buffers and then libc buffers,
228          * while closing file releases file descriptor we need to call fsync(). */
229 #if defined(HAVE_FFLUSH) || defined(HAVE_FSYNC)
230         if (ssi->secure_save) {
231                 int fail = 0;
232
233 #ifdef HAVE_FFLUSH
234                 fail = (fflush(ssi->fp) == EOF);
235 #endif
236
237 #ifdef HAVE_FSYNC
238                 if (!fail) fail = fsync(fileno(ssi->fp));
239 #endif
240
241                 if (fail) {
242                         ret = errno;
243                         secsave_errno = SS_ERR_OTHER;
244
245                         fclose(ssi->fp); /* Close file, ignore errors. */
246                         goto free;
247                 }
248         }
249 #endif
250
251         /* Close file. */
252         if (fclose(ssi->fp) == EOF) {
253                 ret = errno;
254                 secsave_errno = SS_ERR_OTHER;
255                 goto free;
256         }
257
258         if (ssi->secure_save && ssi->file_name && ssi->tmp_file_name) {
259                 /* FIXME: Race condition on ssi->file_name. The file
260                  * named ssi->file_name may have changed since
261                  * secure_open() call (where we stat() file and
262                  * more..).  */
263                 if (debug > 2) g_printf("rename %s -> %s", ssi->tmp_file_name, ssi->file_name);
264                 if (g_rename(ssi->tmp_file_name, ssi->file_name) == -1) {
265                         ret = errno;
266                         secsave_errno = SS_ERR_RENAME;
267                         goto free;
268                 }
269         }
270
271         ret = 0;        /* Success. */
272
273 free:
274         if (ssi->tmp_file_name) g_free(ssi->tmp_file_name);
275         if (ssi->file_name) g_free(ssi->file_name);
276         if (ssi) g_free(ssi);
277
278         return ret;
279 }
280
281
282 /** fputs() wrapper, set ssi->err to errno on error. If ssi->err is set when
283  * called, it immediatly returns EOF.
284  */
285 gint
286 secure_fputs(SecureSaveInfo *ssi, const gchar *s)
287 {
288         gint ret;
289
290         if (!ssi || !ssi->fp || ssi->err) return EOF;
291
292         ret = fputs(s, ssi->fp);
293         if (ret == EOF) {
294                 secsave_errno = SS_ERR_OTHER;
295                 ssi->err = errno;
296         }
297
298         return ret;
299 }
300
301
302 /** fputc() wrapper, set ssi->err to errno on error. If ssi->err is set when
303  * called, it immediatly returns EOF.
304  */
305 gint
306 secure_fputc(SecureSaveInfo *ssi, gint c)
307 {
308         gint ret;
309
310         if (!ssi || !ssi->fp || ssi->err) return EOF;
311
312         ret = fputc(c, ssi->fp);
313         if (ret == EOF) {
314                 ssi->err = errno;
315                 secsave_errno = SS_ERR_OTHER;
316         }
317
318         return ret;
319 }
320
321 /** fprintf() wrapper, set ssi->err to errno on error and return a negative
322  * value. If ssi->err is set when called, it immediatly returns -1.
323  */
324 gint
325 secure_fprintf(SecureSaveInfo *ssi, const gchar *format, ...)
326 {
327         va_list ap;
328         gint ret;
329
330         if (!ssi || !ssi->fp || ssi->err) return -1;
331
332         va_start(ap, format);
333         ret = vfprintf(ssi->fp, format, ap);
334         if (ret < 0) ssi->err = errno;
335         va_end(ap);
336
337         return ret;
338 }
339
340 gchar *
341 secsave_strerror(SecureSaveErrno secsave_error)
342 {
343         switch (secsave_error) {
344         case SS_ERR_OPEN_READ:
345                 return _("Cannot read the file");
346         case SS_ERR_STAT:
347                 return _("Cannot get file status");
348         case SS_ERR_ACCESS:
349                 return _("Cannot access the file");
350         case SS_ERR_MKSTEMP:
351                 return _("Cannot create temp file");
352         case SS_ERR_RENAME:
353                 return _("Cannot rename the file");
354         case SS_ERR_DISABLED:
355                 return _("File saving disabled by option");
356         case SS_ERR_OUT_OF_MEM:
357                 return _("Out of memory");
358         case SS_ERR_OPEN_WRITE:
359                 return _("Cannot write the file");
360         case SS_ERR_NONE: /* Impossible. */
361         case SS_ERR_OTHER:
362         default:
363                 return _("Secure file saving error");
364         }
365 }
366