]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/fileio.c
Add open_memstream_unlocked() wrapper
[thirdparty/systemd.git] / src / basic / fileio.c
index 91e0c9ec8bc8acbdae10c22744eb85c952505fad..99efc2410c9ddcb7ac7050faed2b0964830c47d2 100644 (file)
@@ -17,6 +17,7 @@
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "hexdecoct.h"
 #include "log.h"
 #include "macro.h"
 #include "missing.h"
 
 #define READ_FULL_BYTES_MAX (4U*1024U*1024U)
 
+int fopen_unlocked(const char *path, const char *options, FILE **ret) {
+        assert(ret);
+
+        FILE *f = fopen(path, options);
+        if (!f)
+                return -errno;
+
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+        *ret = f;
+        return 0;
+}
+
+int fdopen_unlocked(int fd, const char *options, FILE **ret) {
+        assert(ret);
+
+        FILE *f = fdopen(fd, options);
+        if (!f)
+                return -errno;
+
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+        *ret = f;
+        return 0;
+}
+
+FILE* open_memstream_unlocked(char **ptr, size_t *sizeloc) {
+        FILE *f = open_memstream(ptr, sizeloc);
+        if (!f)
+                return NULL;
+
+        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+
+        return f;
+}
+
 int write_string_stream_ts(
                 FILE *f,
                 const char *line,
@@ -94,7 +131,6 @@ static int write_string_file_atomic(
         if (r < 0)
                 return r;
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
         (void) fchmod_umask(fileno(f), 0644);
 
         r = write_string_stream_ts(f, line, flags, ts);
@@ -140,11 +176,9 @@ int write_string_file_ts(
                 assert(!ts);
 
         if (flags & WRITE_STRING_FILE_CREATE) {
-                f = fopen(fn, "we");
-                if (!f) {
-                        r = -errno;
+                r = fopen_unlocked(fn, "we", &f);
+                if (r < 0)
                         goto fail;
-                }
         } else {
                 int fd;
 
@@ -156,16 +190,13 @@ int write_string_file_ts(
                         goto fail;
                 }
 
-                f = fdopen(fd, "w");
-                if (!f) {
-                        r = -errno;
+                r = fdopen_unlocked(fd, "w", &f);
+                if (r < 0) {
                         safe_close(fd);
                         goto fail;
                 }
         }
 
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
-
         if (flags & WRITE_STRING_FILE_DISABLE_BUFFER)
                 setvbuf(f, NULL, _IONBF, 0);
 
@@ -212,15 +243,14 @@ int write_string_filef(
 
 int read_one_line_file(const char *fn, char **line) {
         _cleanup_fclose_ FILE *f = NULL;
+        int r;
 
         assert(fn);
         assert(line);
 
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+        r = fopen_unlocked(fn, "re", &f);
+        if (r < 0)
+                return r;
 
         return read_line(f, LONG_LINE_MAX, line);
 }
@@ -229,6 +259,7 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         _cleanup_fclose_ FILE *f = NULL;
         _cleanup_free_ char *buf = NULL;
         size_t l, k;
+        int r;
 
         assert(fn);
         assert(blob);
@@ -242,11 +273,9 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         if (!buf)
                 return -ENOMEM;
 
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+        r = fopen_unlocked(fn, "re", &f);
+        if (r < 0)
+                return r;
 
         /* We try to read one byte more than we need, so that we know whether we hit eof */
         errno = 0;
@@ -264,26 +293,29 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) {
         return 1;
 }
 
-int read_full_stream(
+int read_full_stream_full(
                 FILE *f,
+                const char *filename,
+                ReadFullFileFlags flags,
                 char **ret_contents,
                 size_t *ret_size) {
 
         _cleanup_free_ char *buf = NULL;
         struct stat st;
-        size_t n, l;
-        int fd;
+        size_t n, n_next, l;
+        int fd, r;
 
         assert(f);
         assert(ret_contents);
+        assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size);
 
-        n = LINE_MAX; /* Start size */
+        n_next = LINE_MAX; /* Start size */
 
         fd = fileno(f);
         if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's
                         * optimize our buffering) */
 
-                if (fstat(fileno(f), &st) < 0)
+                if (fstat(fd, &st) < 0)
                         return -errno;
 
                 if (S_ISREG(st.st_mode)) {
@@ -296,27 +328,44 @@ int read_full_stream(
                          * size of 0. Note that we increase the size to read here by one, so that the first read attempt
                          * already makes us notice the EOF. */
                         if (st.st_size > 0)
-                                n = st.st_size + 1;
+                                n_next = st.st_size + 1;
+
+                        if (flags & READ_FULL_FILE_SECURE)
+                                (void) warn_file_is_world_accessible(filename, &st, NULL, 0);
                 }
         }
 
-        l = 0;
+        n = l = 0;
         for (;;) {
                 char *t;
                 size_t k;
 
-                t = realloc(buf, n + 1);
-                if (!t)
-                        return -ENOMEM;
+                if (flags & READ_FULL_FILE_SECURE) {
+                        t = malloc(n_next + 1);
+                        if (!t) {
+                                r = -ENOMEM;
+                                goto finalize;
+                        }
+                        memcpy_safe(t, buf, n);
+                        explicit_bzero_safe(buf, n);
+                } else {
+                        t = realloc(buf, n_next + 1);
+                        if (!t)
+                                return -ENOMEM;
+                }
 
                 buf = t;
+                n = n_next;
+
                 errno = 0;
                 k = fread(buf + l, 1, n - l, f);
                 if (k > 0)
                         l += k;
 
-                if (ferror(f))
-                        return errno > 0 ? -errno : -EIO;
+                if (ferror(f)) {
+                        r = errno > 0 ? -errno : -EIO;
+                        goto finalize;
+                }
 
                 if (feof(f))
                         break;
@@ -327,10 +376,18 @@ int read_full_stream(
                 assert(l == n);
 
                 /* Safety check */
-                if (n >= READ_FULL_BYTES_MAX)
-                        return -E2BIG;
+                if (n >= READ_FULL_BYTES_MAX) {
+                        r = -E2BIG;
+                        goto finalize;
+                }
+
+                n_next = MIN(n * 2, READ_FULL_BYTES_MAX);
+        }
 
-                n = MIN(n * 2, READ_FULL_BYTES_MAX);
+        if (flags & READ_FULL_FILE_UNBASE64) {
+                buf[l++] = 0;
+                r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size);
+                goto finalize;
         }
 
         if (!ret_size) {
@@ -338,8 +395,10 @@ int read_full_stream(
                  * trailing NUL byte. But if there's an embedded NUL byte, then we should refuse operation as otherwise
                  * there'd be ambiguity about what we just read. */
 
-                if (memchr(buf, 0, l))
-                        return -EBADMSG;
+                if (memchr(buf, 0, l)) {
+                        r = -EBADMSG;
+                        goto finalize;
+                }
         }
 
         buf[l] = 0;
@@ -349,21 +408,26 @@ int read_full_stream(
                 *ret_size = l;
 
         return 0;
+
+finalize:
+        if (flags & READ_FULL_FILE_SECURE)
+                explicit_bzero_safe(buf, n);
+
+        return r;
 }
 
-int read_full_file(const char *fn, char **contents, size_t *size) {
+int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) {
         _cleanup_fclose_ FILE *f = NULL;
+        int r;
 
-        assert(fn);
+        assert(filename);
         assert(contents);
 
-        f = fopen(fn, "re");
-        if (!f)
-                return -errno;
-
-        (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
+        r = fopen_unlocked(filename, "re", &f);
+        if (r < 0)
+                return r;
 
-        return read_full_stream(f, contents, size);
+        return read_full_stream_full(f, filename, flags, contents, size);
 }
 
 int executable_is_script(const char *path, char **interpreter) {
@@ -818,3 +882,28 @@ int safe_fgetc(FILE *f, char *ret) {
 
         return 1;
 }
+
+int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) {
+        struct stat _st;
+
+        if (!filename)
+                return 0;
+
+        if (!st) {
+                if (stat(filename, &_st) < 0)
+                        return -errno;
+                st = &_st;
+        }
+
+        if ((st->st_mode & S_IRWXO) == 0)
+                return 0;
+
+        if (unit)
+                log_syntax(unit, LOG_WARNING, filename, line, 0,
+                           "%s has %04o mode that is too permissive, please adjust the access mode.",
+                           filename, st->st_mode & 07777);
+        else
+                log_warning("%s has %04o mode that is too permissive, please adjust the access mode.",
+                            filename, st->st_mode & 07777);
+        return 0;
+}