1 /** Secure file saving handling
13 #include <sys/types.h>
14 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
21 #include "config/options.h"
22 #include "intl/gettext/libintl.h"
23 #include "osdep/osdep.h" /* Needed for mkstemp() on win32 */
24 #include "util/memory.h"
25 #include "util/secsave.h"
26 #include "util/string.h"
29 /* If infofiles.secure_save is set:
30 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
32 * A call to secure_open("/home/me/.elinks/filename", mask) will open a file
33 * named "filename.tmp_XXXXXX" in /home/me/.elinks/ and return a pointer to a
34 * structure secure_save_info on success or NULL on error.
36 * filename.tmp_XXXXXX can't conflict with any file since it's created using
37 * mkstemp(). XXXXXX is a random string.
39 * Subsequent write operations are done using returned secure_save_info FILE *
42 * If an error is encountered, secure_save_info int field named err is set
43 * (automatically if using secure_fp*() functions or by programmer)
45 * When secure_close() is called, "filename.tmp_XXXXXX" is flushed and closed,
46 * and if secure_save_info err field has a value of zero, "filename.tmp_XXXXXX"
47 * is renamed to "filename". If this succeeded, then secure_close() returns 0.
49 * WARNING: since rename() is used, any symlink called "filename" may be
50 * replaced by a regular file. If destination file isn't a regular file,
51 * then secsave is disabled for that file.
53 * If infofiles.secure_save is unset:
54 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
56 * No temporary file is created, "filename" is truncated, all operations are
57 * done on it, no rename nor flush occur, symlinks are preserved.
62 * Access rights are affected by secure_open() mask parameter.
65 /* FIXME: locking system on files about to be rewritten ? */
66 /* FIXME: Low risk race conditions about ssi->file_name. */
68 enum secsave_errno secsave_errno
= SS_ERR_NONE
;
71 /** Open a file for writing in a secure way. @returns a pointer to a
72 * structure secure_save_info on success, or NULL on failure. */
73 static struct secure_save_info
*
74 secure_open_umask(unsigned char *file_name
)
77 struct secure_save_info
*ssi
;
79 secsave_errno
= SS_ERR_NONE
;
81 /* XXX: This is inherently evil and has no place in util/, which
82 * should be independent on such stuff. What do we do, except blaming
83 * Jonas for noticing it? --pasky */
84 if ((get_cmd_opt_bool("no-connect")
85 || get_cmd_opt_int("session-ring"))
86 && !get_cmd_opt_bool("touch-files")) {
87 secsave_errno
= SS_ERR_DISABLED
;
91 ssi
= mem_calloc(1, sizeof(*ssi
));
93 secsave_errno
= SS_ERR_OUT_OF_MEM
;
97 ssi
->secure_save
= get_opt_bool("infofiles.secure_save", NULL
);
99 ssi
->file_name
= stracpy(file_name
);
100 if (!ssi
->file_name
) {
101 secsave_errno
= SS_ERR_OUT_OF_MEM
;
105 /* Check properties of final file. */
106 #ifdef FS_UNIX_SOFTLINKS
107 if (lstat(ssi
->file_name
, &st
)) {
109 if (stat(ssi
->file_name
, &st
)) {
111 /* We ignore error caused by file inexistence. */
112 if (errno
!= ENOENT
) {
115 secsave_errno
= SS_ERR_STAT
;
119 if (!S_ISREG(st
.st_mode
)) {
120 /* Not a regular file, secure_save is disabled. */
121 ssi
->secure_save
= 0;
124 /* XXX: access() do not work with setuid programs. */
125 if (access(ssi
->file_name
, R_OK
| W_OK
) < 0) {
127 secsave_errno
= SS_ERR_ACCESS
;
133 /* We still have a race condition here between
134 * [l]stat() and fopen() */
136 f1
= fopen(ssi
->file_name
, "rb+");
141 secsave_errno
= SS_ERR_OPEN_READ
;
148 if (ssi
->secure_save
) {
149 /* We use a random name for temporary file, mkstemp() opens
150 * the file and return a file descriptor named fd, which is
151 * then converted to FILE * using fdopen().
154 unsigned char *randname
= straconcat(ssi
->file_name
,
156 (unsigned char *) NULL
);
159 secsave_errno
= SS_ERR_OUT_OF_MEM
;
163 /* No need to use safe_mkstemp() here. --Zas */
164 fd
= mkstemp(randname
);
166 secsave_errno
= SS_ERR_MKSTEMP
;
171 ssi
->fp
= fdopen(fd
, "w");
173 secsave_errno
= SS_ERR_OPEN_WRITE
;
179 ssi
->tmp_file_name
= randname
;
181 /* No need to create a temporary file here. */
182 ssi
->fp
= fopen(ssi
->file_name
, "wb");
184 secsave_errno
= SS_ERR_OPEN_WRITE
;
193 mem_free(ssi
->file_name
);
194 ssi
->file_name
= NULL
;
204 /* @relates secure_save_info */
205 struct secure_save_info
*
206 secure_open(unsigned char *file_name
)
208 struct secure_save_info
*ssi
;
210 #ifdef CONFIG_OS_WIN32
211 /* There is neither S_IRWXG nor S_IRWXO under crossmingw32-gcc */
212 const mode_t mask
= 0177;
214 const mode_t mask
= S_IXUSR
| S_IRWXG
| S_IRWXO
;
217 saved_mask
= umask(mask
);
218 ssi
= secure_open_umask(file_name
);
224 /** Close a file opened with secure_open(). @returns 0 on success,
225 * errno or -1 on failure.
226 * @relates secure_save_info */
228 secure_close(struct secure_save_info
*ssi
)
232 if (!ssi
) return ret
;
233 if (!ssi
->fp
) goto free
;
235 if (ssi
->err
) { /* Keep previous errno. */
237 fclose(ssi
->fp
); /* Close file */
241 /* Ensure data is effectively written to disk, we first flush libc buffers
242 * using fflush(), then fsync() to flush kernel buffers, and finally call
243 * fclose() (which call fflush() again, but the first one is needed since
244 * it doesn't make much sense to flush kernel buffers and then libc buffers,
245 * while closing file releases file descriptor we need to call fsync(). */
246 #if defined(HAVE_FFLUSH) || defined(HAVE_FSYNC)
247 if (ssi
->secure_save
) {
251 fail
= (fflush(ssi
->fp
) == EOF
);
255 if (!fail
&& get_opt_bool("infofiles.secure_save_fsync", NULL
))
256 fail
= fsync(fileno(ssi
->fp
));
261 secsave_errno
= SS_ERR_OTHER
;
263 fclose(ssi
->fp
); /* Close file, ignore errors. */
270 if (fclose(ssi
->fp
) == EOF
) {
272 secsave_errno
= SS_ERR_OTHER
;
276 if (ssi
->secure_save
&& ssi
->file_name
&& ssi
->tmp_file_name
) {
278 /* OS/2 needs this, however it breaks atomicity on
280 unlink(ssi
->file_name
);
282 /* FIXME: Race condition on ssi->file_name. The file
283 * named ssi->file_name may have changed since
284 * secure_open() call (where we stat() file and
286 if (rename(ssi
->tmp_file_name
, ssi
->file_name
) == -1) {
288 secsave_errno
= SS_ERR_RENAME
;
293 ret
= 0; /* Success. */
296 mem_free_if(ssi
->tmp_file_name
);
297 mem_free_if(ssi
->file_name
);
304 /** fputs() wrapper, set ssi->err to errno on error. If ssi->err is set when
305 * called, it immediatly returns EOF.
306 * @relates secure_save_info */
308 secure_fputs(struct secure_save_info
*ssi
, const char *s
)
312 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return EOF
;
314 ret
= fputs(s
, ssi
->fp
);
316 secsave_errno
= SS_ERR_OTHER
;
324 /** fputc() wrapper, set ssi->err to errno on error. If ssi->err is set when
325 * called, it immediatly returns EOF.
326 * @relates secure_save_info */
328 secure_fputc(struct secure_save_info
*ssi
, int c
)
332 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return EOF
;
334 ret
= fputc(c
, ssi
->fp
);
337 secsave_errno
= SS_ERR_OTHER
;
343 /** fprintf() wrapper, set ssi->err to errno on error and return a negative
344 * value. If ssi->err is set when called, it immediatly returns -1.
345 * @relates secure_save_info */
347 secure_fprintf(struct secure_save_info
*ssi
, const char *format
, ...)
352 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return -1;
354 va_start(ap
, format
);
355 ret
= vfprintf(ssi
->fp
, format
, ap
);
356 if (ret
< 0) ssi
->err
= errno
;
363 secsave_strerror(enum secsave_errno secsave_error
, struct terminal
*term
)
365 switch (secsave_error
) {
366 case SS_ERR_OPEN_READ
:
367 return _("Cannot read the file", term
);
369 return _("Cannot get file status", term
);
371 return _("Cannot access the file", term
);
373 return _("Cannot create temp file", term
);
375 return _("Cannot rename the file", term
);
376 case SS_ERR_DISABLED
:
377 return _("File saving disabled by option", term
);
378 case SS_ERR_OUT_OF_MEM
:
379 return _("Out of memory", term
);
380 case SS_ERR_OPEN_WRITE
:
381 return _("Cannot write the file", term
);
382 case SS_ERR_NONE
: /* Impossible. */
385 return _("Secure file saving error", term
);