Retry only for https protocol
[elinks.git] / src / util / secsave.c
blob10bd2ee049b2977790f8f7968c3af589aa630bc8
1 /** Secure file saving handling
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <errno.h>
9 #include <stdarg.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/types.h>
14 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
15 #ifdef HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
19 #include "elinks.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 *
40 * field named fp.
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.
59 * In both cases:
60 * ~~~~~~~~~~~~~
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)
76 struct stat st;
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;
88 return NULL;
91 ssi = mem_calloc(1, sizeof(*ssi));
92 if (!ssi) {
93 secsave_errno = SS_ERR_OUT_OF_MEM;
94 goto end;
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;
102 goto free_f;
105 /* Check properties of final file. */
106 #ifdef FS_UNIX_SOFTLINKS
107 if (lstat(ssi->file_name, &st)) {
108 #else
109 if (stat(ssi->file_name, &st)) {
110 #endif
111 /* We ignore error caused by file inexistence. */
112 if (errno != ENOENT) {
113 /* lstat() error. */
114 ssi->err = errno;
115 secsave_errno = SS_ERR_STAT;
116 goto free_file_name;
118 } else {
119 if (!S_ISREG(st.st_mode)) {
120 /* Not a regular file, secure_save is disabled. */
121 ssi->secure_save = 0;
122 } else {
123 #ifdef HAVE_ACCESS
124 /* XXX: access() do not work with setuid programs. */
125 if (access(ssi->file_name, R_OK | W_OK) < 0) {
126 ssi->err = errno;
127 secsave_errno = SS_ERR_ACCESS;
128 goto free_file_name;
130 #else
131 FILE *f1;
133 /* We still have a race condition here between
134 * [l]stat() and fopen() */
136 f1 = fopen(ssi->file_name, "rb+");
137 if (f1) {
138 fclose(f1);
139 } else {
140 ssi->err = errno;
141 secsave_errno = SS_ERR_OPEN_READ;
142 goto free_file_name;
144 #endif
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().
153 int fd;
154 unsigned char *randname = straconcat(ssi->file_name,
155 ".tmp_XXXXXX",
156 (unsigned char *) NULL);
158 if (!randname) {
159 secsave_errno = SS_ERR_OUT_OF_MEM;
160 goto free_file_name;
163 /* No need to use safe_mkstemp() here. --Zas */
164 fd = mkstemp(randname);
165 if (fd == -1) {
166 secsave_errno = SS_ERR_MKSTEMP;
167 mem_free(randname);
168 goto free_file_name;
171 ssi->fp = fdopen(fd, "w");
172 if (!ssi->fp) {
173 secsave_errno = SS_ERR_OPEN_WRITE;
174 ssi->err = errno;
175 mem_free(randname);
176 goto free_file_name;
179 ssi->tmp_file_name = randname;
180 } else {
181 /* No need to create a temporary file here. */
182 ssi->fp = fopen(ssi->file_name, "wb");
183 if (!ssi->fp) {
184 secsave_errno = SS_ERR_OPEN_WRITE;
185 ssi->err = errno;
186 goto free_file_name;
190 return ssi;
192 free_file_name:
193 mem_free(ssi->file_name);
194 ssi->file_name = NULL;
196 free_f:
197 mem_free(ssi);
198 ssi = NULL;
200 end:
201 return NULL;
204 /* @relates secure_save_info */
205 struct secure_save_info *
206 secure_open(unsigned char *file_name)
208 struct secure_save_info *ssi;
209 mode_t saved_mask;
210 #ifdef CONFIG_OS_WIN32
211 /* There is neither S_IRWXG nor S_IRWXO under crossmingw32-gcc */
212 const mode_t mask = 0177;
213 #else
214 const mode_t mask = S_IXUSR | S_IRWXG | S_IRWXO;
215 #endif
217 saved_mask = umask(mask);
218 ssi = secure_open_umask(file_name);
219 umask(saved_mask);
221 return ssi;
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)
230 int ret = -1;
232 if (!ssi) return ret;
233 if (!ssi->fp) goto free;
235 if (ssi->err) { /* Keep previous errno. */
236 ret = ssi->err;
237 fclose(ssi->fp); /* Close file */
238 goto free;
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) {
248 int fail = 0;
250 #ifdef HAVE_FFLUSH
251 fail = (fflush(ssi->fp) == EOF);
252 #endif
254 #ifdef HAVE_FSYNC
255 if (!fail && get_opt_bool("infofiles.secure_save_fsync", NULL))
256 fail = fsync(fileno(ssi->fp));
257 #endif
259 if (fail) {
260 ret = errno;
261 secsave_errno = SS_ERR_OTHER;
263 fclose(ssi->fp); /* Close file, ignore errors. */
264 goto free;
267 #endif
269 /* Close file. */
270 if (fclose(ssi->fp) == EOF) {
271 ret = errno;
272 secsave_errno = SS_ERR_OTHER;
273 goto free;
276 if (ssi->secure_save && ssi->file_name && ssi->tmp_file_name) {
277 #if defined(CONFIG_OS_OS2) || defined(CONFIG_OS_WIN32)
278 /* OS/2 needs this, however it breaks atomicity on
279 * UN*X. */
280 unlink(ssi->file_name);
281 #endif
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
285 * more..). */
286 if (rename(ssi->tmp_file_name, ssi->file_name) == -1) {
287 ret = errno;
288 secsave_errno = SS_ERR_RENAME;
289 goto free;
293 ret = 0; /* Success. */
295 free:
296 mem_free_if(ssi->tmp_file_name);
297 mem_free_if(ssi->file_name);
298 mem_free_if(ssi);
300 return ret;
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)
310 int ret;
312 if (!ssi || !ssi->fp || ssi->err) return EOF;
314 ret = fputs(s, ssi->fp);
315 if (ret == EOF) {
316 secsave_errno = SS_ERR_OTHER;
317 ssi->err = errno;
320 return ret;
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)
330 int ret;
332 if (!ssi || !ssi->fp || ssi->err) return EOF;
334 ret = fputc(c, ssi->fp);
335 if (ret == EOF) {
336 ssi->err = errno;
337 secsave_errno = SS_ERR_OTHER;
340 return ret;
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, ...)
349 va_list ap;
350 int ret;
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;
357 va_end(ap);
359 return ret;
362 unsigned char *
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);
368 case SS_ERR_STAT:
369 return _("Cannot get file status", term);
370 case SS_ERR_ACCESS:
371 return _("Cannot access the file", term);
372 case SS_ERR_MKSTEMP:
373 return _("Cannot create temp file", term);
374 case SS_ERR_RENAME:
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. */
383 case SS_ERR_OTHER:
384 default:
385 return _("Secure file saving error", term);