1 /* Secure file saving handling */
12 #include <sys/types.h>
13 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
20 #include "config/options.h"
21 #include "osdep/osdep.h" /* Needed for mkstemp() on win32 */
22 #include "util/memory.h"
23 #include "util/secsave.h"
24 #include "util/string.h"
27 /* If infofiles.secure_save is set:
28 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 * A call to secure_open("/home/me/.elinks/filename", mask) will open a file
31 * named "filename.tmp_XXXXXX" in /home/me/.elinks/ and return a pointer to a
32 * structure secure_save_info on success or NULL on error.
34 * filename.tmp_XXXXXX can't conflict with any file since it's created using
35 * mkstemp(). XXXXXX is a random string.
37 * Subsequent write operations are done using returned secure_save_info FILE *
40 * If an error is encountered, secure_save_info int field named err is set
41 * (automatically if using secure_fp*() functions or by programmer)
43 * When secure_close() is called, "filename.tmp_XXXXXX" is flushed and closed,
44 * and if secure_save_info err field has a value of zero, "filename.tmp_XXXXXX"
45 * is renamed to "filename". If this succeeded, then secure_close() returns 0.
47 * WARNING: since rename() is used, any symlink called "filename" may be
48 * replaced by a regular file. If destination file isn't a regular file,
49 * then secsave is disabled for that file.
51 * If infofiles.secure_save is unset:
52 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
54 * No temporary file is created, "filename" is truncated, all operations are
55 * done on it, no rename nor flush occur, symlinks are preserved.
60 * Access rights are affected by secure_open() mask parameter.
63 /* FIXME: locking system on files about to be rewritten ? */
64 /* FIXME: Low risk race conditions about ssi->file_name. */
66 enum secsave_errno secsave_errno
= SS_ERR_NONE
;
69 /* Open a file for writing in a secure way. It returns a pointer to a structure
70 * secure_save_info on success, or NULL on failure. */
71 static struct secure_save_info
*
72 secure_open_umask(unsigned char *file_name
)
75 struct secure_save_info
*ssi
;
77 secsave_errno
= SS_ERR_NONE
;
79 /* XXX: This is inherently evil and has no place in util/, which
80 * should be independent on such stuff. What do we do, except blaming
81 * Jonas for noticing it? --pasky */
82 if ((get_cmd_opt_bool("no-connect")
83 || get_cmd_opt_int("session-ring"))
84 && !get_cmd_opt_bool("touch-files")) {
85 secsave_errno
= SS_ERR_DISABLED
;
89 ssi
= mem_calloc(1, sizeof(*ssi
));
91 secsave_errno
= SS_ERR_OUT_OF_MEM
;
95 ssi
->secure_save
= get_opt_bool("infofiles.secure_save");
97 ssi
->file_name
= stracpy(file_name
);
98 if (!ssi
->file_name
) {
99 secsave_errno
= SS_ERR_OUT_OF_MEM
;
103 /* Check properties of final file. */
104 #ifdef FS_UNIX_SOFTLINKS
105 if (lstat(ssi
->file_name
, &st
)) {
107 if (stat(ssi
->file_name
, &st
)) {
109 /* We ignore error caused by file inexistence. */
110 if (errno
!= ENOENT
) {
113 secsave_errno
= SS_ERR_STAT
;
117 if (!S_ISREG(st
.st_mode
)) {
118 /* Not a regular file, secure_save is disabled. */
119 ssi
->secure_save
= 0;
122 /* XXX: access() do not work with setuid programs. */
123 if (access(ssi
->file_name
, R_OK
| W_OK
) < 0) {
125 secsave_errno
= SS_ERR_ACCESS
;
131 /* We still have a race condition here between
132 * [l]stat() and fopen() */
134 f1
= fopen(ssi
->file_name
, "rb+");
139 secsave_errno
= SS_ERR_OPEN_READ
;
146 if (ssi
->secure_save
) {
147 /* We use a random name for temporary file, mkstemp() opens
148 * the file and return a file descriptor named fd, which is
149 * then converted to FILE * using fdopen().
152 unsigned char *randname
= straconcat(ssi
->file_name
,
153 ".tmp_XXXXXX", NULL
);
156 secsave_errno
= SS_ERR_OUT_OF_MEM
;
160 /* No need to use safe_mkstemp() here. --Zas */
161 fd
= mkstemp(randname
);
163 secsave_errno
= SS_ERR_MKSTEMP
;
168 ssi
->fp
= fdopen(fd
, "w");
170 secsave_errno
= SS_ERR_OPEN_WRITE
;
176 ssi
->tmp_file_name
= randname
;
178 /* No need to create a temporary file here. */
179 ssi
->fp
= fopen(ssi
->file_name
, "wb");
181 secsave_errno
= SS_ERR_OPEN_WRITE
;
190 mem_free(ssi
->file_name
);
191 ssi
->file_name
= NULL
;
201 struct secure_save_info
*
202 secure_open(unsigned char *file_name
)
204 struct secure_save_info
*ssi
;
206 const mode_t mask
= S_IXUSR
| S_IRWXG
| S_IRWXO
;
208 saved_mask
= umask(mask
);
209 ssi
= secure_open_umask(file_name
);
215 /* Close a file opened with secure_open, and return 0 on success, errno
216 * or -1 on failure. */
218 secure_close(struct secure_save_info
*ssi
)
222 if (!ssi
) return ret
;
223 if (!ssi
->fp
) goto free
;
225 if (ssi
->err
) { /* Keep previous errno. */
227 fclose(ssi
->fp
); /* Close file */
231 /* Ensure data is effectively written to disk, we first flush libc buffers
232 * using fflush(), then fsync() to flush kernel buffers, and finally call
233 * fclose() (which call fflush() again, but the first one is needed since
234 * it doesn't make much sense to flush kernel buffers and then libc buffers,
235 * while closing file releases file descriptor we need to call fsync(). */
236 #if defined(HAVE_FFLUSH) || defined(HAVE_FSYNC)
237 if (ssi
->secure_save
) {
241 fail
= (fflush(ssi
->fp
) == EOF
);
245 if (!fail
&& get_opt_bool("infofiles.secure_save_fsync"))
246 fail
= fsync(fileno(ssi
->fp
));
251 secsave_errno
= SS_ERR_OTHER
;
253 fclose(ssi
->fp
); /* Close file, ignore errors. */
260 if (fclose(ssi
->fp
) == EOF
) {
262 secsave_errno
= SS_ERR_OTHER
;
266 if (ssi
->secure_save
&& ssi
->file_name
&& ssi
->tmp_file_name
) {
268 /* OS/2 needs this, however it breaks atomicity on
270 unlink(ssi
->file_name
);
272 /* FIXME: Race condition on ssi->file_name. The file
273 * named ssi->file_name may have changed since
274 * secure_open() call (where we stat() file and
276 if (rename(ssi
->tmp_file_name
, ssi
->file_name
) == -1) {
278 secsave_errno
= SS_ERR_RENAME
;
283 ret
= 0; /* Success. */
286 mem_free_if(ssi
->tmp_file_name
);
287 mem_free_if(ssi
->file_name
);
294 /* fputs() wrapper, set ssi->err to errno on error. If ssi->err is set when
295 * called, it immediatly returns EOF. */
297 secure_fputs(struct secure_save_info
*ssi
, const char *s
)
301 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return EOF
;
303 ret
= fputs(s
, ssi
->fp
);
305 secsave_errno
= SS_ERR_OTHER
;
313 /* fputc() wrapper, set ssi->err to errno on error. If ssi->err is set when
314 * called, it immediatly returns EOF. */
316 secure_fputc(struct secure_save_info
*ssi
, int c
)
320 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return EOF
;
322 ret
= fputc(c
, ssi
->fp
);
325 secsave_errno
= SS_ERR_OTHER
;
331 /* fprintf() wrapper, set ssi->err to errno on error and return a negative
332 * value. If ssi->err is set when called, it immediatly returns -1. */
334 secure_fprintf(struct secure_save_info
*ssi
, const char *format
, ...)
339 if (!ssi
|| !ssi
->fp
|| ssi
->err
) return -1;
341 va_start(ap
, format
);
342 ret
= vfprintf(ssi
->fp
, format
, ap
);
343 if (ret
< 0) ssi
->err
= errno
;