From c068e6b07e6ae74e3c7c8a93d22b2be5a1cd755f Mon Sep 17 00:00:00 2001 From: Guorui Yu Date: Wed, 2 Aug 2023 22:44:03 +0800 Subject: [PATCH] cryptsetup: if keyfile is specified as AF_UNIX socket in the fs, connect to it, and read key data from it Signed-off-by: Guorui Yu --- ...ding-a-full-file-into-memory-refuse-.patch | 120 ++++++++ ...explicit_bzero_safe-for-explicit-mem.patch | 61 ++++ ...util-introduce-erase_and_free-helper.patch | 48 ++++ ...READ_FULL_FILE_SECURE-flag-for-readi.patch | 207 +++++++++++++ ...roduce-warn_file_is_world_accessible.patch | 67 +++++ ...l_file_full-also-warns-when-file-is-.patch | 64 +++++ ...x-memory-leak-if-READ_FULL_FILE_SECU.patch | 30 ++ ...icit-flag-for-generating-world-execu.patch | 44 +++ ..._fd-parameter-to-read_full_file_full.patch | 142 +++++++++ ...ort-for-read_full_file-on-AF_UNIX-st.patch | 271 ++++++++++++++++++ ...READ_FULL_FILE_CONNECT_SOCKET-to-all.patch | 181 ++++++++++++ ...ad_full_file_full-to-read-from-offse.patch | 246 ++++++++++++++++ ...-cryptsetup-s-main-key-file-logic-ov.patch | 95 ++++++ systemd.spec | 30 +- 14 files changed, 1605 insertions(+), 1 deletion(-) create mode 100644 10016-fileio-when-reading-a-full-file-into-memory-refuse-.patch create mode 100644 10017-util-introduce-explicit_bzero_safe-for-explicit-mem.patch create mode 100644 10018-util-introduce-erase_and_free-helper.patch create mode 100644 10019-util-introduce-READ_FULL_FILE_SECURE-flag-for-readi.patch create mode 100644 10020-fileio-introduce-warn_file_is_world_accessible.patch create mode 100644 10021-fileio-read_full_file_full-also-warns-when-file-is-.patch create mode 100644 10022-basic-fileio-Fix-memory-leak-if-READ_FULL_FILE_SECU.patch create mode 100644 10023-fileio-add-explicit-flag-for-generating-world-execu.patch create mode 100644 10024-fileio-add-dir_fd-parameter-to-read_full_file_full.patch create mode 100644 10025-fileio-add-support-for-read_full_file-on-AF_UNIX-st.patch create mode 100644 10026-fileio-beef-up-READ_FULL_FILE_CONNECT_SOCKET-to-all.patch create mode 100644 10027-fileio-teach-read_full_file_full-to-read-from-offse.patch create mode 100644 10028-cryptsetup-port-cryptsetup-s-main-key-file-logic-ov.patch diff --git a/10016-fileio-when-reading-a-full-file-into-memory-refuse-.patch b/10016-fileio-when-reading-a-full-file-into-memory-refuse-.patch new file mode 100644 index 0000000..f2eeed5 --- /dev/null +++ b/10016-fileio-when-reading-a-full-file-into-memory-refuse-.patch @@ -0,0 +1,120 @@ +From 9f181efdd59bd3e9134cf94007953562ca8b57fa Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Sat, 15 Dec 2018 12:25:32 +0100 +Subject: [PATCH] fileio: when reading a full file into memory, refuse inner + NUL bytes + +Just some extra care to avoid any ambiguities in what we read. + +(cherry picked from commit beb90929913354eec50c3524086fe70d14f97e2f) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 25 +++++++++++++++++++------ + src/test/test-unit-file.c | 10 +++++----- + 2 files changed, 24 insertions(+), 11 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 733fb42463..9fef97ff0c 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -383,16 +383,20 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re + return 0; + } + +-int read_full_stream(FILE *f, char **contents, size_t *size) { ++int read_full_stream( ++ FILE *f, ++ char **ret_contents, ++ size_t *ret_size) { ++ + _cleanup_free_ char *buf = NULL; + struct stat st; + size_t n, l; + int fd; + + assert(f); +- assert(contents); ++ assert(ret_contents); + +- n = LINE_MAX; ++ n = 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 +@@ -448,11 +452,20 @@ int read_full_stream(FILE *f, char **contents, size_t *size) { + n = MIN(n * 2, READ_FULL_BYTES_MAX); + } + ++ if (!ret_size) { ++ /* Safety check: if the caller doesn't want to know the size of what we just read it will rely on the ++ * 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; ++ } ++ + buf[l] = 0; +- *contents = TAKE_PTR(buf); ++ *ret_contents = TAKE_PTR(buf); + +- if (size) +- *size = l; ++ if (ret_size) ++ *ret_size = l; + + return 0; + } +diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c +index 09b0179fa1..e64a27dd39 100644 +--- a/src/test/test-unit-file.c ++++ b/src/test/test-unit-file.c +@@ -532,7 +532,7 @@ static void test_load_env_file_1(void) { + + fd = mkostemp_safe(name); + assert_se(fd >= 0); +- assert_se(write(fd, env_file_1, sizeof(env_file_1)) == sizeof(env_file_1)); ++ assert_se(write(fd, env_file_1, strlen(env_file_1)) == strlen(env_file_1)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); +@@ -554,7 +554,7 @@ static void test_load_env_file_2(void) { + + fd = mkostemp_safe(name); + assert_se(fd >= 0); +- assert_se(write(fd, env_file_2, sizeof(env_file_2)) == sizeof(env_file_2)); ++ assert_se(write(fd, env_file_2, strlen(env_file_2)) == strlen(env_file_2)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); +@@ -571,7 +571,7 @@ static void test_load_env_file_3(void) { + + fd = mkostemp_safe(name); + assert_se(fd >= 0); +- assert_se(write(fd, env_file_3, sizeof(env_file_3)) == sizeof(env_file_3)); ++ assert_se(write(fd, env_file_3, strlen(env_file_3)) == strlen(env_file_3)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); +@@ -586,7 +586,7 @@ static void test_load_env_file_4(void) { + + fd = mkostemp_safe(name); + assert_se(fd >= 0); +- assert_se(write(fd, env_file_4, sizeof(env_file_4)) == sizeof(env_file_4)); ++ assert_se(write(fd, env_file_4, strlen(env_file_4)) == strlen(env_file_4)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); +@@ -605,7 +605,7 @@ static void test_load_env_file_5(void) { + + fd = mkostemp_safe(name); + assert_se(fd >= 0); +- assert_se(write(fd, env_file_5, sizeof(env_file_5)) == sizeof(env_file_5)); ++ assert_se(write(fd, env_file_5, strlen(env_file_5)) == strlen(env_file_5)); + + r = load_env_file(NULL, name, NULL, &data); + assert_se(r == 0); +-- +2.39.1 + diff --git a/10017-util-introduce-explicit_bzero_safe-for-explicit-mem.patch b/10017-util-introduce-explicit_bzero_safe-for-explicit-mem.patch new file mode 100644 index 0000000..c0ec4be --- /dev/null +++ b/10017-util-introduce-explicit_bzero_safe-for-explicit-mem.patch @@ -0,0 +1,61 @@ +From 17037ec625fca9e9a473a33954d011065f0088e3 Mon Sep 17 00:00:00 2001 +From: Guorui Yu +Date: Fri, 23 Jun 2023 13:01:24 +0800 +Subject: [PATCH] util: introduce explicit_bzero_safe for explicit memset + +(cherry picked from commit f441ae81ef70e9bdfddbb9e0a276bbb8ca2151d4) + +Signed-off-by: Guorui Yu +--- + src/basic/util.c | 18 ++++++++++++++++++ + src/basic/util.h | 11 +++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/src/basic/util.c b/src/basic/util.c +index 548e3652cc..bdfaca4aed 100644 +--- a/src/basic/util.c ++++ b/src/basic/util.c +@@ -684,3 +684,21 @@ void disable_coredumps(void) { + if (r < 0) + log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); + } ++ ++#if !HAVE_EXPLICIT_BZERO ++/* ++ * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that ++ * it points to any function in particular (such as memset(), which it then might further "optimize"). This ++ * approach is inspired by openssl's crypto/mem_clr.c. ++ */ ++typedef void *(*memset_t)(void *,int,size_t); ++ ++static volatile memset_t memset_func = memset; ++ ++void* explicit_bzero_safe(void *p, size_t l) { ++ if (l > 0) ++ memset_func(p, '\0', l); ++ ++ return p; ++} ++#endif +diff --git a/src/basic/util.h b/src/basic/util.h +index 195f02cf5f..ab3314f82e 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -240,3 +240,14 @@ int version(void); + int str_verscmp(const char *s1, const char *s2); + + void disable_coredumps(void); ++ ++#if HAVE_EXPLICIT_BZERO ++static inline void* explicit_bzero_safe(void *p, size_t l) { ++ if (l > 0) ++ explicit_bzero(p, l); ++ ++ return p; ++} ++#else ++void *explicit_bzero_safe(void *p, size_t l); ++#endif +-- +2.39.1 + diff --git a/10018-util-introduce-erase_and_free-helper.patch b/10018-util-introduce-erase_and_free-helper.patch new file mode 100644 index 0000000..43c42fc --- /dev/null +++ b/10018-util-introduce-erase_and_free-helper.patch @@ -0,0 +1,48 @@ +From 7c48fe64e3f1cdc61d9191d5e004d56d5244aa2c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 8 Aug 2019 19:53:17 +0200 +Subject: [PATCH] util: introduce erase_and_free() helper + +(cherry picked from commit a20dda788d5a0f3b300e0d8bb34e45be335e2915) + +Signed-off-by: Guorui Yu +--- + src/basic/util.h | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/src/basic/util.h b/src/basic/util.h +index ab3314f82e..4f4877b6b0 100644 +--- a/src/basic/util.h ++++ b/src/basic/util.h +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -251,3 +252,20 @@ static inline void* explicit_bzero_safe(void *p, size_t l) { + #else + void *explicit_bzero_safe(void *p, size_t l); + #endif ++ ++static inline void* erase_and_free(void *p) { ++ size_t l; ++ ++ if (!p) ++ return NULL; ++ ++ l = malloc_usable_size(p); ++ explicit_bzero_safe(p, l); ++ free(p); ++ ++ return NULL; ++} ++ ++static inline void erase_and_freep(void *p) { ++ erase_and_free(*(void**) p); ++} +-- +2.39.1 + diff --git a/10019-util-introduce-READ_FULL_FILE_SECURE-flag-for-readi.patch b/10019-util-introduce-READ_FULL_FILE_SECURE-flag-for-readi.patch new file mode 100644 index 0000000..a37d579 --- /dev/null +++ b/10019-util-introduce-READ_FULL_FILE_SECURE-flag-for-readi.patch @@ -0,0 +1,207 @@ +From bc781489901fc6447cbd27b8d33f4f4439d6a5db Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 8 Apr 2019 02:22:40 +0900 +Subject: [PATCH] util: introduce READ_FULL_FILE_SECURE flag for reading secure + data + +(cherry picked from commit e0721f97b05c0a5f782233711ea95c1e02ccba44) + +[Guorui Yu: include util.h for explicit_bzero_safe] +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 68 ++++++++++++++++++++++++++++++++-------------- + src/basic/fileio.h | 16 +++++++++-- + 2 files changed, 60 insertions(+), 24 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 9fef97ff0c..cf7c92ebc7 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -35,6 +35,7 @@ + #include "time-util.h" + #include "umask-util.h" + #include "utf8.h" ++#include "util.h" + + #define READ_FULL_BYTES_MAX (4U*1024U*1024U) + +@@ -383,26 +384,27 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re + return 0; + } + +-int read_full_stream( ++int read_full_stream_full( + FILE *f, ++ 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); + +- 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)) { +@@ -415,27 +417,41 @@ int read_full_stream( + * 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; + } + } + +- 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; +@@ -446,10 +462,12 @@ 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 = MIN(n * 2, READ_FULL_BYTES_MAX); ++ n_next = MIN(n * 2, READ_FULL_BYTES_MAX); + } + + if (!ret_size) { +@@ -457,8 +475,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; +@@ -468,21 +488,27 @@ 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; + +- assert(fn); ++ assert(filename); + assert(contents); + +- f = fopen(fn, "re"); ++ f = fopen(filename, "re"); + if (!f) + return -errno; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + +- return read_full_stream(f, contents, size); ++ return read_full_stream_full(f, flags, contents, size); + } + + static int parse_env_file_internal( +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index c6ad375b8d..06649ef7e6 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -24,6 +24,10 @@ typedef enum { + + } WriteStringFileFlags; + ++typedef enum { ++ READ_FULL_FILE_SECURE = 1 << 0, ++} ReadFullFileFlags; ++ + int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); + static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { + return write_string_stream_ts(f, line, flags, NULL); +@@ -35,9 +39,15 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin + + int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + +-int read_one_line_file(const char *fn, char **line); +-int read_full_file(const char *fn, char **contents, size_t *size); +-int read_full_stream(FILE *f, char **contents, size_t *size); ++int read_one_line_file(const char *filename, char **line); ++int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); ++static inline int read_full_file(const char *filename, char **contents, size_t *size) { ++ return read_full_file_full(filename, 0, contents, size); ++} ++int read_full_stream_full(FILE *f, ReadFullFileFlags flags, char **contents, size_t *size); ++static inline int read_full_stream(FILE *f, char **contents, size_t *size) { ++ return read_full_stream_full(f, 0, contents, size); ++} + int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); + + int verify_file(const char *fn, const char *blob, bool accept_extra_nl); +-- +2.39.1 + diff --git a/10020-fileio-introduce-warn_file_is_world_accessible.patch b/10020-fileio-introduce-warn_file_is_world_accessible.patch new file mode 100644 index 0000000..02f9518 --- /dev/null +++ b/10020-fileio-introduce-warn_file_is_world_accessible.patch @@ -0,0 +1,67 @@ +From e4c4f0bc712e43776c4f58712f47260711607098 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 8 Apr 2019 03:48:30 +0900 +Subject: [PATCH] fileio: introduce warn_file_is_world_accessible() + +(cherry picked from commit fc0895034d4811e8c6b263c0d902b31535613d76) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 25 +++++++++++++++++++++++++ + src/basic/fileio.h | 3 +++ + 2 files changed, 28 insertions(+) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index cf7c92ebc7..2e74aac554 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -1797,3 +1797,28 @@ int read_line(FILE *f, size_t limit, char **ret) { + + return (int) count; + } ++ ++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; ++} +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 06649ef7e6..2c9ce4355b 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -5,6 +5,7 @@ + #include + #include + #include ++#include + #include + + #include "macro.h" +@@ -105,3 +106,5 @@ int read_nul_string(FILE *f, char **ret); + int mkdtemp_malloc(const char *template, char **ret); + + int read_line(FILE *f, size_t limit, char **ret); ++ ++int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); +-- +2.39.1 + diff --git a/10021-fileio-read_full_file_full-also-warns-when-file-is-.patch b/10021-fileio-read_full_file_full-also-warns-when-file-is-.patch new file mode 100644 index 0000000..af813a5 --- /dev/null +++ b/10021-fileio-read_full_file_full-also-warns-when-file-is-.patch @@ -0,0 +1,64 @@ +From 0dbf69ccdfa7b1f99935c3932445fbfa16dbbe75 Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Mon, 8 Apr 2019 14:15:10 +0900 +Subject: [PATCH] fileio: read_full_file_full() also warns when file is world + readable and secure flag is set + +(cherry picked from commit 65dcd394d8223bc6bc194f3fe5bd70fed9d9a4fe) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 6 +++++- + src/basic/fileio.h | 4 ++-- + 2 files changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 2e74aac554..3abeb0d7f4 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -386,6 +386,7 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re + + int read_full_stream_full( + FILE *f, ++ const char *filename, + ReadFullFileFlags flags, + char **ret_contents, + size_t *ret_size) { +@@ -418,6 +419,9 @@ int read_full_stream_full( + * makes us notice the EOF. */ + if (st.st_size > 0) + n_next = st.st_size + 1; ++ ++ if (flags & READ_FULL_FILE_SECURE) ++ (void) warn_file_is_world_accessible(filename, &st, NULL, 0); + } + } + +@@ -508,7 +512,7 @@ int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **co + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + +- return read_full_stream_full(f, flags, contents, size); ++ return read_full_stream_full(f, filename, flags, contents, size); + } + + static int parse_env_file_internal( +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 2c9ce4355b..3e572dc0de 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -45,9 +45,9 @@ int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **co + static inline int read_full_file(const char *filename, char **contents, size_t *size) { + return read_full_file_full(filename, 0, contents, size); + } +-int read_full_stream_full(FILE *f, ReadFullFileFlags flags, char **contents, size_t *size); ++int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); + static inline int read_full_stream(FILE *f, char **contents, size_t *size) { +- return read_full_stream_full(f, 0, contents, size); ++ return read_full_stream_full(f, NULL, 0, contents, size); + } + int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); + +-- +2.39.1 + diff --git a/10022-basic-fileio-Fix-memory-leak-if-READ_FULL_FILE_SECU.patch b/10022-basic-fileio-Fix-memory-leak-if-READ_FULL_FILE_SECU.patch new file mode 100644 index 0000000..e434089 --- /dev/null +++ b/10022-basic-fileio-Fix-memory-leak-if-READ_FULL_FILE_SECU.patch @@ -0,0 +1,30 @@ +From 14e0760c251fd5fc51731f7b58079c73f5055d64 Mon Sep 17 00:00:00 2001 +From: Benjamin Robin +Date: Sun, 14 Apr 2019 17:21:27 +0200 +Subject: [PATCH] basic/fileio: Fix memory leak if READ_FULL_FILE_SECURE flag + is used + +The memory leak introduced in #12223 (15f8f02) + +(cherry picked from commit 315a51982af2d480de9f7539346f30425e37a01e) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 3abeb0d7f4..bb804e3afa 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -438,6 +438,7 @@ int read_full_stream_full( + } + memcpy_safe(t, buf, n); + explicit_bzero_safe(buf, n); ++ buf = mfree(buf); + } else { + t = realloc(buf, n_next + 1); + if (!t) +-- +2.39.1 + diff --git a/10023-fileio-add-explicit-flag-for-generating-world-execu.patch b/10023-fileio-add-explicit-flag-for-generating-world-execu.patch new file mode 100644 index 0000000..1a93b5a --- /dev/null +++ b/10023-fileio-add-explicit-flag-for-generating-world-execu.patch @@ -0,0 +1,44 @@ +From 1e0dcd6fa1abea9c561f46556f7f7561b2a46e62 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 17 Jul 2020 11:53:22 +0200 +Subject: [PATCH] fileio: add explicit flag for generating world executable + warning when reading file + +(cherry picked from commit 684aa979f1c4ce5f75ccdc131f32fc0434999918) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 2 +- + src/basic/fileio.h | 3 ++- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index bb804e3afa..833c55b030 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -420,7 +420,7 @@ int read_full_stream_full( + if (st.st_size > 0) + n_next = st.st_size + 1; + +- if (flags & READ_FULL_FILE_SECURE) ++ if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) + (void) warn_file_is_world_accessible(filename, &st, NULL, 0); + } + } +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 3e572dc0de..be10ac77b6 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -26,7 +26,8 @@ typedef enum { + } WriteStringFileFlags; + + typedef enum { +- READ_FULL_FILE_SECURE = 1 << 0, ++ READ_FULL_FILE_SECURE = 1 << 0, ++ READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, + } ReadFullFileFlags; + + int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); +-- +2.39.1 + diff --git a/10024-fileio-add-dir_fd-parameter-to-read_full_file_full.patch b/10024-fileio-add-dir_fd-parameter-to-read_full_file_full.patch new file mode 100644 index 0000000..f6dc153 --- /dev/null +++ b/10024-fileio-add-dir_fd-parameter-to-read_full_file_full.patch @@ -0,0 +1,142 @@ +From 3f4ca11498028756ebde239ae469c0f88e5d3ecc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 8 Jan 2019 18:29:36 +0100 +Subject: [PATCH] fileio: add 'dir_fd' parameter to read_full_file_full() + +Let's introduce an "at" version of read_full_file(). + +(cherry picked from commit f6be4db4530b7cfea191227c141343a4fb10d4c6) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 84 +++++++++++++++++++++++++++++++++++++++++++--- + src/basic/fileio.h | 5 +-- + 2 files changed, 83 insertions(+), 6 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 833c55b030..d7da834a74 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -501,15 +501,91 @@ finalize: + return r; + } + +-int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { ++static int mode_to_flags(const char *mode) { ++ const char *p; ++ int flags; ++ ++ if ((p = startswith(mode, "r+"))) ++ flags = O_RDWR; ++ else if ((p = startswith(mode, "r"))) ++ flags = O_RDONLY; ++ else if ((p = startswith(mode, "w+"))) ++ flags = O_RDWR|O_CREAT|O_TRUNC; ++ else if ((p = startswith(mode, "w"))) ++ flags = O_WRONLY|O_CREAT|O_TRUNC; ++ else if ((p = startswith(mode, "a+"))) ++ flags = O_RDWR|O_CREAT|O_APPEND; ++ else if ((p = startswith(mode, "a"))) ++ flags = O_WRONLY|O_CREAT|O_APPEND; ++ else ++ return -EINVAL; ++ ++ for (; *p != 0; p++) { ++ ++ switch (*p) { ++ ++ case 'e': ++ flags |= O_CLOEXEC; ++ break; ++ ++ case 'x': ++ flags |= O_EXCL; ++ break; ++ ++ case 'm': ++ /* ignore this here, fdopen() might care later though */ ++ break; ++ ++ case 'c': /* not sure what to do about this one */ ++ default: ++ return -EINVAL; ++ } ++ } ++ ++ return flags; ++} ++ ++static int xfopenat(int dir_fd, const char *path, const char *mode, int flags, FILE **ret) { ++ FILE *f; ++ ++ /* A combination of fopen() with openat() */ ++ ++ if (dir_fd == AT_FDCWD && flags == 0) { ++ f = fopen(path, mode); ++ if (!f) ++ return -errno; ++ } else { ++ int fd, mode_flags; ++ ++ mode_flags = mode_to_flags(mode); ++ if (mode_flags < 0) ++ return mode_flags; ++ ++ fd = openat(dir_fd, path, mode_flags | flags); ++ if (fd < 0) ++ return -errno; ++ ++ f = fdopen(fd, mode); ++ if (!f) { ++ safe_close(fd); ++ return -errno; ++ } ++ } ++ ++ *ret = f; ++ return 0; ++} ++ ++int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { + _cleanup_fclose_ FILE *f = NULL; ++ int r; + + assert(filename); + assert(contents); + +- f = fopen(filename, "re"); +- if (!f) +- return -errno; ++ r = xfopenat(dir_fd, filename, "re", 0, &f); ++ if (r < 0) ++ return r; + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index be10ac77b6..916ddc5e47 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + #include + + #include "macro.h" +@@ -42,9 +43,9 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin + int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + + int read_one_line_file(const char *filename, char **line); +-int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); ++int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); + static inline int read_full_file(const char *filename, char **contents, size_t *size) { +- return read_full_file_full(filename, 0, contents, size); ++ return read_full_file_full(AT_FDCWD, filename, 0, contents, size); + } + int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); + static inline int read_full_stream(FILE *f, char **contents, size_t *size) { +-- +2.39.1 + diff --git a/10025-fileio-add-support-for-read_full_file-on-AF_UNIX-st.patch b/10025-fileio-add-support-for-read_full_file-on-AF_UNIX-st.patch new file mode 100644 index 0000000..bb392bc --- /dev/null +++ b/10025-fileio-add-support-for-read_full_file-on-AF_UNIX-st.patch @@ -0,0 +1,271 @@ +From 054669a4cc4897792b6c209fd55ab1fc1d7b9bd5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Fri, 17 Jul 2020 12:26:01 +0200 +Subject: [PATCH] fileio: add support for read_full_file() on AF_UNIX stream + sockets + +Optionally, teach read_full_file() the ability to connect to an AF_UNIX +socket if the specified path points to one. + +(cherry picked from commit 412b888ec803cdf96fb1d005bb245d20abdb8f2e) + +[Guorui Yu: Adds sockaddr_un_set_path function to socket-util.{c,h}] +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 62 +++++++++++++++++++++++++++++++++++------ + src/basic/fileio.h | 1 + + src/basic/socket-util.c | 42 ++++++++++++++++++++++++++++ + src/basic/socket-util.h | 1 + + src/test/test-fileio.c | 50 +++++++++++++++++++++++++++++++++ + 5 files changed, 147 insertions(+), 9 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index d7da834a74..9cb0a2bd28 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -27,6 +27,7 @@ + #include "missing.h" + #include "parse-util.h" + #include "path-util.h" ++#include "socket-util.h" + #include "process-util.h" + #include "random-util.h" + #include "stdio-util.h" +@@ -450,21 +451,18 @@ int read_full_stream_full( + + errno = 0; + k = fread(buf + l, 1, n - l, f); +- if (k > 0) +- l += k; ++ ++ assert(k <= n - l); ++ l += k; + + if (ferror(f)) { + r = errno > 0 ? -errno : -EIO; + goto finalize; + } +- + if (feof(f)) + break; + +- /* We aren't expecting fread() to return a short read outside +- * of (error && eof), assert buffer is full and enlarge buffer. +- */ +- assert(l == n); ++ assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ + + /* Safety check */ + if (n >= READ_FULL_BYTES_MAX) { +@@ -584,8 +582,54 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag + assert(contents); + + r = xfopenat(dir_fd, filename, "re", 0, &f); +- if (r < 0) +- return r; ++ if (r < 0) { ++ _cleanup_close_ int dfd = -1, sk = -1; ++ union sockaddr_union sa; ++ ++ /* ENXIO is what Linux returns if we open a node that is an AF_UNIX socket */ ++ if (r != -ENXIO) ++ return r; ++ ++ /* If this is enabled, let's try to connect to it */ ++ if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) ++ return -ENXIO; ++ ++ if (dir_fd == AT_FDCWD) ++ r = sockaddr_un_set_path(&sa.un, filename); ++ else { ++ char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)]; ++ ++ /* If we shall operate relative to some directory, then let's use O_PATH first to ++ * open the socket inode, and then connect to it via /proc/self/fd/. We have to do ++ * this since there's not connectat() that takes a directory fd as first arg. */ ++ ++ dfd = openat(dir_fd, filename, O_PATH|O_CLOEXEC); ++ if (dfd < 0) ++ return -errno; ++ ++ xsprintf(procfs_path, "/proc/self/fd/%i", dfd); ++ r = sockaddr_un_set_path(&sa.un, procfs_path); ++ } ++ if (r < 0) ++ return r; ++ ++ sk = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); ++ if (sk < 0) ++ return -errno; ++ ++ if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) ++ return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is ++ * not a socket after all */ ++ ++ if (shutdown(sk, SHUT_WR) < 0) ++ return -errno; ++ ++ f = fdopen(sk, "r"); ++ if (!f) ++ return -errno; ++ ++ TAKE_FD(sk); ++ } + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 916ddc5e47..1a16e0fd13 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -29,6 +29,7 @@ typedef enum { + typedef enum { + READ_FULL_FILE_SECURE = 1 << 0, + READ_FULL_FILE_WARN_WORLD_READABLE = 1 << 3, ++ READ_FULL_FILE_CONNECT_SOCKET = 1 << 4, + } ReadFullFileFlags; + + int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); +diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c +index 7f8066123b..427c8b89bb 100644 +--- a/src/basic/socket-util.c ++++ b/src/basic/socket-util.c +@@ -1253,6 +1253,48 @@ int socket_ioctl_fd(void) { + return fd; + } + ++int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { ++ size_t l; ++ ++ assert(ret); ++ assert(path); ++ ++ /* Initialize ret->sun_path from the specified argument. This will interpret paths starting with '@' as ++ * abstract namespace sockets, and those starting with '/' as regular filesystem sockets. It won't accept ++ * anything else (i.e. no relative paths), to avoid ambiguities. Note that this function cannot be used to ++ * reference paths in the abstract namespace that include NUL bytes in the name. */ ++ ++ l = strlen(path); ++ if (l < 2) ++ return -EINVAL; ++ if (!IN_SET(path[0], '/', '@')) ++ return -EINVAL; ++ ++ /* Don't allow paths larger than the space in sockaddr_un. Note that we are a tiny bit more restrictive than ++ * the kernel is: we insist on NUL termination (both for abstract namespace and regular file system socket ++ * addresses!), which the kernel doesn't. We do this to reduce chance of incompatibility with other apps that ++ * do not expect non-NUL terminated file system path*/ ++ if (l+1 > sizeof(ret->sun_path)) ++ return -EINVAL; ++ ++ *ret = (struct sockaddr_un) { ++ .sun_family = AF_UNIX, ++ }; ++ ++ if (path[0] == '@') { ++ /* Abstract namespace socket */ ++ memcpy(ret->sun_path + 1, path + 1, l); /* copy *with* trailing NUL byte */ ++ return (int) (offsetof(struct sockaddr_un, sun_path) + l); /* 🔥 *don't* 🔥 include trailing NUL in size */ ++ ++ } else { ++ assert(path[0] == '/'); ++ ++ /* File system socket */ ++ memcpy(ret->sun_path, path, l + 1); /* copy *with* trailing NUL byte */ ++ return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ ++ } ++} ++ + int socket_pass_pktinfo(int fd, bool b) { + int af; + socklen_t sl = sizeof(af); +diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h +index 30baba6c03..36edc58caf 100644 +--- a/src/basic/socket-util.h ++++ b/src/basic/socket-util.h +@@ -186,6 +186,7 @@ struct cmsghdr* cmsg_find(struct msghdr *mh, int level, int type, socklen_t leng + }) + + int socket_ioctl_fd(void); ++int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path); + + static inline int setsockopt_int(int fd, int level, int optname, int value) { + if (setsockopt(fd, level, optname, &value, sizeof(value)) < 0) +diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c +index 14ba075144..82b7cb1242 100644 +--- a/src/test/test-fileio.c ++++ b/src/test/test-fileio.c +@@ -14,6 +14,8 @@ + #include "io-util.h" + #include "parse-util.h" + #include "process-util.h" ++#include "rm-rf.h" ++#include "socket-util.h" + #include "string-util.h" + #include "strv.h" + #include "util.h" +@@ -709,6 +711,53 @@ static void test_read_line3(void) { + assert_se(read_line(f, LINE_MAX, NULL) == 0); + } + ++static void test_read_full_file_socket(void) { ++ _cleanup_(rm_rf_physical_and_freep) char *z = NULL; ++ _cleanup_close_ int listener = -1; ++ _cleanup_free_ char *data = NULL; ++ union sockaddr_union sa; ++ const char *j; ++ size_t size; ++ pid_t pid; ++ int r; ++ ++ log_info("/* %s */", __func__); ++ ++ listener = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0); ++ assert_se(listener >= 0); ++ ++ assert_se(mkdtemp_malloc(NULL, &z) >= 0); ++ j = strjoina(z, "/socket"); ++ ++ assert_se(sockaddr_un_set_path(&sa.un, j) >= 0); ++ ++ assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0); ++ assert_se(listen(listener, 1) >= 0); ++ ++ r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid); ++ assert_se(r >= 0); ++ if (r == 0) { ++ _cleanup_close_ int rfd = -1; ++ /* child */ ++ ++ rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC); ++ assert_se(rfd >= 0); ++ ++#define TEST_STR "This is a test\nreally." ++ ++ assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR)); ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO); ++ assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0); ++ assert_se(size == strlen(TEST_STR)); ++ assert_se(streq(data, TEST_STR)); ++ ++ assert_se(wait_for_terminate_and_check("(server)", pid, WAIT_LOG) >= 0); ++#undef TEST_STR ++} ++ + int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); +@@ -733,6 +782,7 @@ int main(int argc, char *argv[]) { + test_read_line(); + test_read_line2(); + test_read_line3(); ++ test_read_full_file_socket(); + + return 0; + } +-- +2.39.1 + diff --git a/10026-fileio-beef-up-READ_FULL_FILE_CONNECT_SOCKET-to-all.patch b/10026-fileio-beef-up-READ_FULL_FILE_CONNECT_SOCKET-to-all.patch new file mode 100644 index 0000000..2edc538 --- /dev/null +++ b/10026-fileio-beef-up-READ_FULL_FILE_CONNECT_SOCKET-to-all.patch @@ -0,0 +1,181 @@ +From 0717de25e6508b10ea034fa1b96675f18100ac01 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 2 Nov 2020 12:07:51 +0100 +Subject: [PATCH] fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow + setting sender socket name + +This beefs up the READ_FULL_FILE_CONNECT_SOCKET logic of +read_full_file_full() a bit: when used a sender socket name may be +specified. If specified as NULL behaviour is as before: the client +socket name is picked by the kernel. But if specified as non-NULL the +client can pick a socket name to use when connecting. This is useful to +communicate a minimal amount of metainformation from client to server, +outside of the transport payload. + +Specifically, these beefs up the service credential logic to pass an +abstract AF_UNIX socket name as client socket name when connecting via +READ_FULL_FILE_CONNECT_SOCKET, that includes the requesting unit name +and the eventual credential name. This allows servers implementing the +trivial credential socket logic to distinguish clients: via a simple +getpeername() it can be determined which unit is requesting a +credential, and which credential specifically. + +Example: with this patch in place, in a unit file "waldo.service" a +configuration line like the following: + + LoadCredential=foo:/run/quux/creds.sock + +will result in a connection to the AF_UNIX socket /run/quux/creds.sock, +originating from an abstract namespace AF_UNIX socket: + + @$RANDOM/unit/waldo.service/foo + +(The $RANDOM is replaced by some randomized string. This is included in +the socket name order to avoid namespace squatting issues: the abstract +socket namespace is open to unprivileged users after all, and care needs +to be taken not to use guessable names) + +The services listening on the /run/quux/creds.sock socket may thus +easily retrieve the name of the unit the credential is requested for +plus the credential name, via a simpler getpeername(), discarding the +random preifx and the /unit/ string. + +This logic uses "/" as separator between the fields, since both unit +names and credential names appear in the file system, and thus are +designed to use "/" as outer separators. Given that it's a good safe +choice to use as separators here, too avoid any conflicts. + +This is a minimal patch only: the new logic is used only for the unit +file credential logic. For other places where we use +READ_FULL_FILE_CONNECT_SOCKET it is probably a good idea to use this +scheme too, but this should be done carefully in later patches, since +the socket names become API that way, and we should determine the right +amount of info to pass over. + +(cherry picked from commit 142e9756c98c69cdd5d03df4028700acb5739f72) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 22 +++++++++++++++++++++- + src/basic/fileio.h | 4 ++-- + src/test/test-fileio.c | 19 ++++++++++++++++--- + 3 files changed, 39 insertions(+), 6 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 9cb0a2bd28..35eaa3c1c7 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -574,7 +574,13 @@ static int xfopenat(int dir_fd, const char *path, const char *mode, int flags, F + return 0; + } + +-int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { ++int read_full_file_full( ++ int dir_fd, ++ const char *filename, ++ ReadFullFileFlags flags, ++ const char *bind_name, ++ char **contents, size_t *size) { ++ + _cleanup_fclose_ FILE *f = NULL; + int r; + +@@ -617,6 +623,20 @@ int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flag + if (sk < 0) + return -errno; + ++ if (bind_name) { ++ /* If the caller specified a socket name to bind to, do so before connecting. This is ++ * useful to communicate some minor, short meta-information token from the client to ++ * the server. */ ++ union sockaddr_union bsa; ++ ++ r = sockaddr_un_set_path(&bsa.un, bind_name); ++ if (r < 0) ++ return r; ++ ++ if (bind(sk, &bsa.sa, r) < 0) ++ return r; ++ } ++ + if (connect(sk, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) + return errno == ENOTSOCK ? -ENXIO : -errno; /* propagate original error if this is + * not a socket after all */ +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 1a16e0fd13..82897e209c 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -44,9 +44,9 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin + int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + + int read_one_line_file(const char *filename, char **line); +-int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); ++int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size); + static inline int read_full_file(const char *filename, char **contents, size_t *size) { +- return read_full_file_full(AT_FDCWD, filename, 0, contents, size); ++ return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size); + } + int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); + static inline int read_full_stream(FILE *f, char **contents, size_t *size) { +diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c +index 82b7cb1242..5ec70eec14 100644 +--- a/src/test/test-fileio.c ++++ b/src/test/test-fileio.c +@@ -14,6 +14,7 @@ + #include "io-util.h" + #include "parse-util.h" + #include "process-util.h" ++#include "random-util.h" + #include "rm-rf.h" + #include "socket-util.h" + #include "string-util.h" +@@ -714,7 +715,7 @@ static void test_read_line3(void) { + static void test_read_full_file_socket(void) { + _cleanup_(rm_rf_physical_and_freep) char *z = NULL; + _cleanup_close_ int listener = -1; +- _cleanup_free_ char *data = NULL; ++ _cleanup_free_ char *data = NULL, *clientname = NULL; + union sockaddr_union sa; + const char *j; + size_t size; +@@ -734,23 +735,35 @@ static void test_read_full_file_socket(void) { + assert_se(bind(listener, &sa.sa, SOCKADDR_UN_LEN(sa.un)) >= 0); + assert_se(listen(listener, 1) >= 0); + ++ /* Bind the *client* socket to some randomized name, to verify that this works correctly. */ ++ assert_se(asprintf(&clientname, "@%" PRIx64 "/test-bindname", random_u64()) >= 0); ++ + r = safe_fork("(server)", FORK_DEATHSIG|FORK_LOG, &pid); + assert_se(r >= 0); + if (r == 0) { ++ union sockaddr_union peer = {}; ++ socklen_t peerlen = sizeof(peer); + _cleanup_close_ int rfd = -1; + /* child */ + + rfd = accept4(listener, NULL, 0, SOCK_CLOEXEC); + assert_se(rfd >= 0); + ++ assert_se(getpeername(rfd, &peer.sa, &peerlen) >= 0); ++ ++ assert_se(peer.un.sun_family == AF_UNIX); ++ assert_se(peerlen > offsetof(struct sockaddr_un, sun_path)); ++ assert_se(peer.un.sun_path[0] == 0); ++ assert_se(streq(peer.un.sun_path + 1, clientname + 1)); ++ + #define TEST_STR "This is a test\nreally." + + assert_se(write(rfd, TEST_STR, strlen(TEST_STR)) == strlen(TEST_STR)); + _exit(EXIT_SUCCESS); + } + +- assert_se(read_full_file_full(AT_FDCWD, j, 0, &data, &size) == -ENXIO); +- assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, &data, &size) >= 0); ++ assert_se(read_full_file_full(AT_FDCWD, j, 0, NULL, &data, &size) == -ENXIO); ++ assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); + assert_se(size == strlen(TEST_STR)); + assert_se(streq(data, TEST_STR)); + +-- +2.39.1 + diff --git a/10027-fileio-teach-read_full_file_full-to-read-from-offse.patch b/10027-fileio-teach-read_full_file_full-to-read-from-offse.patch new file mode 100644 index 0000000..08e8f40 --- /dev/null +++ b/10027-fileio-teach-read_full_file_full-to-read-from-offse.patch @@ -0,0 +1,246 @@ +From 5be0e8a2c3e683c195fd872979d6e5741c80d13f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 4 Nov 2020 20:25:06 +0100 +Subject: [PATCH] fileio: teach read_full_file_full() to read from offset/with + maximum size + +(cherry picked from commit 7399b3f8083b65db4cb9acb17e4b5c897ba7946d) + +Signed-off-by: Guorui Yu +--- + src/basic/fileio.c | 60 ++++++++++++++++++++++++++++++------------ + src/basic/fileio.h | 12 ++++----- + src/test/test-fileio.c | 49 ++++++++++++++++++++++++++++++++-- + 3 files changed, 96 insertions(+), 25 deletions(-) + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 35eaa3c1c7..c14f9797bd 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -388,44 +388,58 @@ int read_full_virtual_file(const char *filename, char **ret_contents, size_t *re + int read_full_stream_full( + FILE *f, + const char *filename, ++ uint64_t offset, ++ size_t size, + ReadFullFileFlags flags, + char **ret_contents, + size_t *ret_size) { + + _cleanup_free_ char *buf = NULL; +- struct stat st; + size_t n, n_next, l; + int fd, r; + + assert(f); + assert(ret_contents); + +- n_next = LINE_MAX; /* Start size */ ++ if (offset != UINT64_MAX && offset > LONG_MAX) ++ return -ERANGE; ++ ++ n_next = size != SIZE_MAX ? size : 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 (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see ++ * fmemopen()), let's optimize our buffering */ ++ struct stat st; + + if (fstat(fd, &st) < 0) + return -errno; + + if (S_ISREG(st.st_mode)) { +- +- /* Safety check */ +- if (st.st_size > READ_FULL_BYTES_MAX) +- return -E2BIG; +- +- /* Start with the right file size. 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_next = st.st_size + 1; ++ if (size == SIZE_MAX) { ++ uint64_t rsize = ++ LESS_BY((uint64_t) st.st_size, offset == UINT64_MAX ? 0 : offset); ++ ++ /* Safety check */ ++ if (rsize > READ_FULL_BYTES_MAX) ++ return -E2BIG; ++ ++ /* Start with the right file size. Note that we increase the size to read ++ * here by one, so that the first read attempt already makes us notice the ++ * EOF. If the reported size of the file is zero, we avoid this logic ++ * however, since quite likely it might be a virtual file in procfs that all ++ * report a zero file size. */ ++ if (st.st_size > 0) ++ n_next = rsize + 1; ++ } + + if (flags & READ_FULL_FILE_WARN_WORLD_READABLE) + (void) warn_file_is_world_accessible(filename, &st, NULL, 0); + } + } + ++ if (offset != UINT64_MAX && fseek(f, offset, SEEK_SET) < 0) ++ return -errno; ++ + n = l = 0; + for (;;) { + char *t; +@@ -462,6 +476,11 @@ int read_full_stream_full( + if (feof(f)) + break; + ++ if (size != SIZE_MAX) { /* If we got asked to read some specific size, we already sized the buffer right, hence leave */ ++ assert(l == size); ++ break; ++ } ++ + assert(k > 0); /* we can't have read zero bytes because that would have been EOF */ + + /* Safety check */ +@@ -577,15 +596,18 @@ static int xfopenat(int dir_fd, const char *path, const char *mode, int flags, F + int read_full_file_full( + int dir_fd, + const char *filename, ++ uint64_t offset, ++ size_t size, + ReadFullFileFlags flags, + const char *bind_name, +- char **contents, size_t *size) { ++ char **ret_contents, ++ size_t *ret_size) { + + _cleanup_fclose_ FILE *f = NULL; + int r; + + assert(filename); +- assert(contents); ++ assert(ret_contents); + + r = xfopenat(dir_fd, filename, "re", 0, &f); + if (r < 0) { +@@ -600,6 +622,10 @@ int read_full_file_full( + if (!FLAGS_SET(flags, READ_FULL_FILE_CONNECT_SOCKET)) + return -ENXIO; + ++ /* Seeking is not supported on AF_UNIX sockets */ ++ if (offset != UINT64_MAX) ++ return -ESPIPE; ++ + if (dir_fd == AT_FDCWD) + r = sockaddr_un_set_path(&sa.un, filename); + else { +@@ -653,7 +679,7 @@ int read_full_file_full( + + (void) __fsetlocking(f, FSETLOCKING_BYCALLER); + +- return read_full_stream_full(f, filename, flags, contents, size); ++ return read_full_stream_full(f, filename, offset, size, flags, ret_contents, ret_size); + } + + static int parse_env_file_internal( +diff --git a/src/basic/fileio.h b/src/basic/fileio.h +index 82897e209c..03150ce776 100644 +--- a/src/basic/fileio.h ++++ b/src/basic/fileio.h +@@ -44,13 +44,13 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin + int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); + + int read_one_line_file(const char *filename, char **line); +-int read_full_file_full(int dir_fd, const char *filename, ReadFullFileFlags flags, const char *bind_name, char **contents, size_t *size); +-static inline int read_full_file(const char *filename, char **contents, size_t *size) { +- return read_full_file_full(AT_FDCWD, filename, 0, NULL, contents, size); ++int read_full_file_full(int dir_fd, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, const char *bind_name, char **ret_contents, size_t *ret_size); ++static inline int read_full_file(const char *filename, char **ret_contents, size_t *ret_size) { ++ return read_full_file_full(AT_FDCWD, filename, UINT64_MAX, SIZE_MAX, 0, NULL, ret_contents, ret_size); + } +-int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); +-static inline int read_full_stream(FILE *f, char **contents, size_t *size) { +- return read_full_stream_full(f, NULL, 0, contents, size); ++int read_full_stream_full(FILE *f, const char *filename, uint64_t offset, size_t size, ReadFullFileFlags flags, char **ret_contents, size_t *ret_size); ++static inline int read_full_stream(FILE *f, char **ret_contents, size_t *ret_size) { ++ return read_full_stream_full(f, NULL, UINT64_MAX, SIZE_MAX, 0, ret_contents, ret_size); + } + int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size); + +diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c +index 5ec70eec14..5d0006149b 100644 +--- a/src/test/test-fileio.c ++++ b/src/test/test-fileio.c +@@ -762,8 +762,8 @@ static void test_read_full_file_socket(void) { + _exit(EXIT_SUCCESS); + } + +- assert_se(read_full_file_full(AT_FDCWD, j, 0, NULL, &data, &size) == -ENXIO); +- assert_se(read_full_file_full(AT_FDCWD, j, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); ++ assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, 0, NULL, &data, &size) == -ENXIO); ++ assert_se(read_full_file_full(AT_FDCWD, j, UINT64_MAX, SIZE_MAX, READ_FULL_FILE_CONNECT_SOCKET, clientname, &data, &size) >= 0); + assert_se(size == strlen(TEST_STR)); + assert_se(streq(data, TEST_STR)); + +@@ -771,6 +771,50 @@ static void test_read_full_file_socket(void) { + #undef TEST_STR + } + ++static void test_read_full_file_offset_size(void) { ++ _cleanup_fclose_ FILE *f = NULL; ++ _cleanup_(unlink_and_freep) char *fn = NULL; ++ _cleanup_free_ char *rbuf = NULL; ++ size_t rbuf_size; ++ uint8_t buf[4711]; ++ ++ random_bytes(buf, sizeof(buf)); ++ ++ assert_se(tempfn_random_child(NULL, NULL, &fn) >= 0); ++ assert_se(f = fopen(fn, "we")); ++ assert_se(fwrite(buf, 1, sizeof(buf), f) == sizeof(buf)); ++ assert_se(fflush_and_check(f) >= 0); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == sizeof(buf)); ++ assert_se(memcmp(buf, rbuf, rbuf_size) == 0); ++ rbuf = mfree(rbuf); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, UINT64_MAX, 128, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == 128); ++ assert_se(memcmp(buf, rbuf, rbuf_size) == 0); ++ rbuf = mfree(rbuf); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, 1234, SIZE_MAX, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == sizeof(buf) - 1234); ++ assert_se(memcmp(buf + 1234, rbuf, rbuf_size) == 0); ++ rbuf = mfree(rbuf); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, 2345, 777, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == 777); ++ assert_se(memcmp(buf + 2345, rbuf, rbuf_size) == 0); ++ rbuf = mfree(rbuf); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, 4700, 20, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == 11); ++ assert_se(memcmp(buf + 4700, rbuf, rbuf_size) == 0); ++ rbuf = mfree(rbuf); ++ ++ assert_se(read_full_file_full(AT_FDCWD, fn, 10000, 99, 0, NULL, &rbuf, &rbuf_size) >= 0); ++ assert_se(rbuf_size == 0); ++ rbuf = mfree(rbuf); ++} ++ + int main(int argc, char *argv[]) { + log_set_max_level(LOG_DEBUG); + log_parse_environment(); +@@ -796,6 +840,7 @@ int main(int argc, char *argv[]) { + test_read_line2(); + test_read_line3(); + test_read_full_file_socket(); ++ test_read_full_file_offset_size(); + + return 0; + } +-- +2.39.1 + diff --git a/10028-cryptsetup-port-cryptsetup-s-main-key-file-logic-ov.patch b/10028-cryptsetup-port-cryptsetup-s-main-key-file-logic-ov.patch new file mode 100644 index 0000000..bb66170 --- /dev/null +++ b/10028-cryptsetup-port-cryptsetup-s-main-key-file-logic-ov.patch @@ -0,0 +1,95 @@ +From 8ef03861b75cf0a70511760c395cb4bd228c37b9 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 4 Nov 2020 17:24:53 +0100 +Subject: [PATCH] cryptsetup: port cryptsetup's main key file logic over to + read_full_file_full() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Previously, we'd load the file with libcryptsetup's calls. Let's do that +in our own, so that we can make use of READ_FULL_FILE_CONNECT_SOCKET, +i.e. read in keys via AF_UNIX sockets, so that people can plug key +providers into our logic. + +This provides functionality similar to Debian's keyscript= crypttab +option (see → #3007), as it allows key scripts to be run as socket +activated services, that have stdout connected to the activated socket. +In contrast to traditional keyscript= support this logic runs stuff out +of process however, which is beneficial, since it allows sandboxing and +similar. + +(cherry picked from commit 165a476841ff1aa3aab3508771db9495ab073c7a) + +Signed-off-by: Guorui Yu +--- + src/cryptsetup/cryptsetup.c | 37 ++++++++++++++++++++++++++++++++----- + 1 file changed, 32 insertions(+), 5 deletions(-) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index 11162eb722..9251e0eba8 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -17,6 +17,7 @@ + #include "mount-util.h" + #include "parse-util.h" + #include "path-util.h" ++#include "random-util.h" + #include "string-util.h" + #include "strv.h" + #include "util.h" +@@ -480,6 +481,15 @@ static int attach_tcrypt( + return 0; + } + ++static char *make_bindname(const char *volume) { ++ char *s; ++ ++ if (asprintf(&s, "@%" PRIx64"/cryptsetup/%s", random_u64(), volume) < 0) ++ return NULL; ++ ++ return s; ++} ++ + static int attach_luks_or_plain(struct crypt_device *cd, + const char *name, + const char *key_file, +@@ -553,13 +563,30 @@ static int attach_luks_or_plain(struct crypt_device *cd, + crypt_get_device_name(cd)); + + if (key_file) { +- r = crypt_activate_by_keyfile_offset(cd, name, arg_key_slot, key_file, arg_keyfile_size, arg_keyfile_offset, flags); +- if (r == -EPERM) { +- log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); ++ _cleanup_(erase_and_freep) char *kfdata = NULL; ++ _cleanup_free_ char *bindname = NULL; ++ size_t kfsize; ++ ++ /* If we read the key via AF_UNIX, make this client recognizable */ ++ bindname = make_bindname(name); ++ if (!bindname) ++ return log_oom(); ++ ++ r = read_full_file_full( ++ AT_FDCWD, key_file, ++ arg_keyfile_offset == 0 ? UINT64_MAX : arg_keyfile_offset, ++ arg_keyfile_size == 0 ? SIZE_MAX : arg_keyfile_size, ++ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, ++ bindname, ++ &kfdata, &kfsize); ++ if (r == -ENOENT) { ++ log_error_errno(r, "Failed to activate, key file '%s' missing.", key_file); + return -EAGAIN; /* Log actual error, but return EAGAIN */ + } +- if (r == -EINVAL) { +- log_error_errno(r, "Failed to activate with key file '%s'. (Key file missing?)", key_file); ++ ++ r = crypt_activate_by_passphrase(cd, name, arg_key_slot, kfdata, kfsize, flags); ++ if (r == -EPERM) { ++ log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); + return -EAGAIN; /* Log actual error, but return EAGAIN */ + } + if (r < 0) +-- +2.39.1 + diff --git a/systemd.spec b/systemd.spec index 38004ba..8368a4f 100644 --- a/systemd.spec +++ b/systemd.spec @@ -1,4 +1,4 @@ -%define anolis_release .0.2 +%define anolis_release .0.3 #global gitcommit 10e465b5321bd53c1fc59ffab27e724535c6bc0f %{?gitcommit:%global gitcommitshort %(c=%{gitcommit}; echo ${c:0:7})} @@ -972,6 +972,19 @@ Patch10012: 10012-seccomp-add-loongarch-support.patch Patch10013: 10013-pager-set-LESSSECURE-whenver-we-invoke-a-pager.patch Patch10014: 10014-pager-make-pager-secure-when-under-euid-is-changed.patch Patch10015: 10015-link-libsystemd_static-for-sd_pid_get_owner_uid-in-.patch +Patch10016: 10016-fileio-when-reading-a-full-file-into-memory-refuse-.patch +Patch10017: 10017-util-introduce-explicit_bzero_safe-for-explicit-mem.patch +Patch10018: 10018-util-introduce-erase_and_free-helper.patch +Patch10019: 10019-util-introduce-READ_FULL_FILE_SECURE-flag-for-readi.patch +Patch10020: 10020-fileio-introduce-warn_file_is_world_accessible.patch +Patch10021: 10021-fileio-read_full_file_full-also-warns-when-file-is-.patch +Patch10022: 10022-basic-fileio-Fix-memory-leak-if-READ_FULL_FILE_SECU.patch +Patch10023: 10023-fileio-add-explicit-flag-for-generating-world-execu.patch +Patch10024: 10024-fileio-add-dir_fd-parameter-to-read_full_file_full.patch +Patch10025: 10025-fileio-add-support-for-read_full_file-on-AF_UNIX-st.patch +Patch10026: 10026-fileio-beef-up-READ_FULL_FILE_CONNECT_SOCKET-to-all.patch +Patch10027: 10027-fileio-teach-read_full_file_full-to-read-from-offse.patch +Patch10028: 10028-cryptsetup-port-cryptsetup-s-main-key-file-logic-ov.patch %ifarch %{ix86} x86_64 aarch64 %global have_gnu_efi 1 @@ -1602,6 +1615,21 @@ fi %files tests -f .file-list-tests %changelog +* Tue Aug 01 2023 Guorui Yu - 239-74.0.3 +- fileio: when reading a full file into memory, refuse inner NUL bytes +- util: introduce explicit_bzero_safe for explicit memset +- util: introduce erase_and_free() helper +- util: introduce READ_FULL_FILE_SECURE flag for reading secure data +- fileio: introduce warn_file_is_world_accessible() +- fileio: read_full_file_full() also warns when file is world readable and secure flag is set +- basic/fileio: Fix memory leak if READ_FULL_FILE_SECURE flag is used +- fileio: add explicit flag for generating world executable warning when reading file +- fileio: add 'dir_fd' parameter to read_full_file_full() +- fileio: add support for read_full_file() on AF_UNIX stream sockets +- fileio: beef up READ_FULL_FILE_CONNECT_SOCKET to allow setting sender socket name +- fileio: teach read_full_file_full() to read from offset/with maximum size +- cryptsetup: port cryptsetup's main key file logic over to read_full_file_full() + * Wed Jun 28 2023 Liwei Ge - 239-74.0.2 - Make pager secure (CVE-2023-26604) -- Gitee