grafthistory: support curl
[elinks/elinks-j605.git] / src / util / secsave.c
blob5df514ec06ad815e29e84741f247555974eaefa5
1 /* Secure file saving handling */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <stdarg.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/types.h>
13 #include <sys/stat.h> /* OS/2 needs this after sys/types.h */
14 #ifdef HAVE_UNISTD_H
15 #include <unistd.h>
16 #endif
18 #include "elinks.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 *
38 * field named fp.
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.
57 * In both cases:
58 * ~~~~~~~~~~~~~
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)
74 struct stat st;
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;
86 return NULL;
89 ssi = mem_calloc(1, sizeof(*ssi));
90 if (!ssi) {
91 secsave_errno = SS_ERR_OUT_OF_MEM;
92 goto end;
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;
100 goto free_f;
103 /* Check properties of final file. */
104 #ifdef FS_UNIX_SOFTLINKS
105 if (lstat(ssi->file_name, &st)) {
106 #else
107 if (stat(ssi->file_name, &st)) {
108 #endif
109 /* We ignore error caused by file inexistence. */
110 if (errno != ENOENT) {
111 /* lstat() error. */
112 ssi->err = errno;
113 secsave_errno = SS_ERR_STAT;
114 goto free_file_name;
116 } else {
117 if (!S_ISREG(st.st_mode)) {
118 /* Not a regular file, secure_save is disabled. */
119 ssi->secure_save = 0;
120 } else {
121 #ifdef HAVE_ACCESS
122 /* XXX: access() do not work with setuid programs. */
123 if (access(ssi->file_name, R_OK | W_OK) < 0) {
124 ssi->err = errno;
125 secsave_errno = SS_ERR_ACCESS;
126 goto free_file_name;
128 #else
129 FILE *f1;
131 /* We still have a race condition here between
132 * [l]stat() and fopen() */
134 f1 = fopen(ssi->file_name, "rb+");
135 if (f1) {
136 fclose(f1);
137 } else {
138 ssi->err = errno;
139 secsave_errno = SS_ERR_OPEN_READ;
140 goto free_file_name;
142 #endif
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().
151 int fd;
152 unsigned char *randname = straconcat(ssi->file_name,
153 ".tmp_XXXXXX", NULL);
155 if (!randname) {
156 secsave_errno = SS_ERR_OUT_OF_MEM;
157 goto free_file_name;
160 /* No need to use safe_mkstemp() here. --Zas */
161 fd = mkstemp(randname);
162 if (fd == -1) {
163 secsave_errno = SS_ERR_MKSTEMP;
164 mem_free(randname);
165 goto free_file_name;
168 ssi->fp = fdopen(fd, "w");
169 if (!ssi->fp) {
170 secsave_errno = SS_ERR_OPEN_WRITE;
171 ssi->err = errno;
172 mem_free(randname);
173 goto free_file_name;
176 ssi->tmp_file_name = randname;
177 } else {
178 /* No need to create a temporary file here. */
179 ssi->fp = fopen(ssi->file_name, "wb");
180 if (!ssi->fp) {
181 secsave_errno = SS_ERR_OPEN_WRITE;
182 ssi->err = errno;
183 goto free_file_name;
187 return ssi;
189 free_file_name:
190 mem_free(ssi->file_name);
191 ssi->file_name = NULL;
193 free_f:
194 mem_free(ssi);
195 ssi = NULL;
197 end:
198 return NULL;
201 struct secure_save_info *
202 secure_open(unsigned char *file_name)
204 struct secure_save_info *ssi;
205 mode_t saved_mask;
206 const mode_t mask = S_IXUSR | S_IRWXG | S_IRWXO;
208 saved_mask = umask(mask);
209 ssi = secure_open_umask(file_name);
210 umask(saved_mask);
212 return ssi;
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)
220 int ret = -1;
222 if (!ssi) return ret;
223 if (!ssi->fp) goto free;
225 if (ssi->err) { /* Keep previous errno. */
226 ret = ssi->err;
227 fclose(ssi->fp); /* Close file */
228 goto free;
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) {
238 int fail = 0;
240 #ifdef HAVE_FFLUSH
241 fail = (fflush(ssi->fp) == EOF);
242 #endif
244 #ifdef HAVE_FSYNC
245 if (!fail && get_opt_bool("infofiles.secure_save_fsync"))
246 fail = fsync(fileno(ssi->fp));
247 #endif
249 if (fail) {
250 ret = errno;
251 secsave_errno = SS_ERR_OTHER;
253 fclose(ssi->fp); /* Close file, ignore errors. */
254 goto free;
257 #endif
259 /* Close file. */
260 if (fclose(ssi->fp) == EOF) {
261 ret = errno;
262 secsave_errno = SS_ERR_OTHER;
263 goto free;
266 if (ssi->secure_save && ssi->file_name && ssi->tmp_file_name) {
267 #ifdef CONFIG_OS2
268 /* OS/2 needs this, however it breaks atomicity on
269 * UN*X. */
270 unlink(ssi->file_name);
271 #endif
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
275 * more..). */
276 if (rename(ssi->tmp_file_name, ssi->file_name) == -1) {
277 ret = errno;
278 secsave_errno = SS_ERR_RENAME;
279 goto free;
283 ret = 0; /* Success. */
285 free:
286 mem_free_if(ssi->tmp_file_name);
287 mem_free_if(ssi->file_name);
288 mem_free_if(ssi);
290 return ret;
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)
299 int ret;
301 if (!ssi || !ssi->fp || ssi->err) return EOF;
303 ret = fputs(s, ssi->fp);
304 if (ret == EOF) {
305 secsave_errno = SS_ERR_OTHER;
306 ssi->err = errno;
309 return ret;
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)
318 int ret;
320 if (!ssi || !ssi->fp || ssi->err) return EOF;
322 ret = fputc(c, ssi->fp);
323 if (ret == EOF) {
324 ssi->err = errno;
325 secsave_errno = SS_ERR_OTHER;
328 return ret;
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, ...)
336 va_list ap;
337 int ret;
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;
344 va_end(ap);
346 return ret;