Merge pull request #7198 from poettering/stdin-stdout

Add StandardInput=data, StandardInput=file:... and more
This commit is contained in:
Lennart Poettering
2017-11-19 19:49:11 +01:00
committed by GitHub
32 changed files with 1242 additions and 422 deletions
+2 -2
View File
@@ -1279,7 +1279,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
assert(spec);
if (*spec == '/') {
if (!path_is_safe(spec))
if (!path_is_normalized(spec))
return -EINVAL;
if (path) {
@@ -1332,7 +1332,7 @@ int cg_split_spec(const char *spec, char **controller, char **path) {
return -ENOMEM;
}
if (!path_is_safe(u) ||
if (!path_is_normalized(u) ||
!path_is_absolute(u)) {
free(t);
free(u);
+201
View File
@@ -27,8 +27,10 @@
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "macro.h"
#include "memfd-util.h"
#include "missing.h"
#include "parse-util.h"
#include "path-util.h"
@@ -378,3 +380,202 @@ int fd_get_path(int fd, char **ret) {
return r;
}
int move_fd(int from, int to, int cloexec) {
int r;
/* Move fd 'from' to 'to', make sure FD_CLOEXEC remains equal if requested, and release the old fd. If
* 'cloexec' is passed as -1, the original FD_CLOEXEC is inherited for the new fd. If it is 0, it is turned
* off, if it is > 0 it is turned on. */
if (from < 0)
return -EBADF;
if (to < 0)
return -EBADF;
if (from == to) {
if (cloexec >= 0) {
r = fd_cloexec(to, cloexec);
if (r < 0)
return r;
}
return to;
}
if (cloexec < 0) {
int fl;
fl = fcntl(from, F_GETFD, 0);
if (fl < 0)
return -errno;
cloexec = !!(fl & FD_CLOEXEC);
}
r = dup3(from, to, cloexec ? O_CLOEXEC : 0);
if (r < 0)
return -errno;
assert(r == to);
safe_close(from);
return to;
}
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
char procfs_path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
char pattern[] = "/dev/shm/data-fd-XXXXXX";
_cleanup_close_ int fd = -1;
int isz = 0, r;
ssize_t n;
off_t f;
assert(data || size == 0);
/* Acquire a read-only file descriptor that when read from returns the specified data. This is much more
* complex than I wish it was. But here's why:
*
* a) First we try to use memfds. They are the best option, as we can seal them nicely to make them
* read-only. Unfortunately they require kernel 3.17, and – at the time of writing – we still support 3.14.
*
* b) Then, we try classic pipes. They are the second best options, as we can close the writing side, retaining
* a nicely read-only fd in the reading side. However, they are by default quite small, and unprivileged
* clients can only bump their size to a system-wide limit, which might be quite low.
*
* c) Then, we try an O_TMPFILE file in /dev/shm (that dir is the only suitable one known to exist from
* earliest boot on). To make it read-only we open the fd a second time with O_RDONLY via
* /proc/self/<fd>. Unfortunately O_TMPFILE is not available on older kernels on tmpfs.
*
* d) Finally, we try creating a regular file in /dev/shm, which we then delete.
*
* It sucks a bit that depending on the situation we return very different objects here, but that's Linux I
* figure. */
if (size == 0 && ((flags & ACQUIRE_NO_DEV_NULL) == 0)) {
/* As a special case, return /dev/null if we have been called for an empty data block */
r = open("/dev/null", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (r < 0)
return -errno;
return r;
}
if ((flags & ACQUIRE_NO_MEMFD) == 0) {
fd = memfd_new("data-fd");
if (fd < 0)
goto try_pipe;
n = write(fd, data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
f = lseek(fd, 0, SEEK_SET);
if (f != 0)
return -errno;
r = memfd_set_sealed(fd);
if (r < 0)
return r;
r = fd;
fd = -1;
return r;
}
try_pipe:
if ((flags & ACQUIRE_NO_PIPE) == 0) {
if (pipe2(pipefds, O_CLOEXEC|O_NONBLOCK) < 0)
return -errno;
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
if ((size_t) isz < size) {
isz = (int) size;
if (isz < 0 || (size_t) isz != size)
return -E2BIG;
/* Try to bump the pipe size */
(void) fcntl(pipefds[1], F_SETPIPE_SZ, isz);
/* See if that worked */
isz = fcntl(pipefds[1], F_GETPIPE_SZ, 0);
if (isz < 0)
return -errno;
if ((size_t) isz < size)
goto try_dev_shm;
}
n = write(pipefds[1], data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
(void) fd_nonblock(pipefds[0], false);
r = pipefds[0];
pipefds[0] = -1;
return r;
}
try_dev_shm:
if ((flags & ACQUIRE_NO_TMPFILE) == 0) {
fd = open("/dev/shm", O_RDWR|O_TMPFILE|O_CLOEXEC, 0500);
if (fd < 0)
goto try_dev_shm_without_o_tmpfile;
n = write(fd, data, size);
if (n < 0)
return -errno;
if ((size_t) n != size)
return -EIO;
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
r = open(procfs_path, O_RDONLY|O_CLOEXEC);
if (r < 0)
return -errno;
return r;
}
try_dev_shm_without_o_tmpfile:
if ((flags & ACQUIRE_NO_REGULAR) == 0) {
fd = mkostemp_safe(pattern);
if (fd < 0)
return fd;
n = write(fd, data, size);
if (n < 0) {
r = -errno;
goto unlink_and_return;
}
if ((size_t) n != size) {
r = -EIO;
goto unlink_and_return;
}
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
r = open(pattern, O_RDONLY|O_CLOEXEC);
if (r < 0)
r = -errno;
unlink_and_return:
(void) unlink(pattern);
return r;
}
return -EOPNOTSUPP;
}
+12
View File
@@ -76,6 +76,18 @@ bool fdname_is_valid(const char *s);
int fd_get_path(int fd, char **ret);
int move_fd(int from, int to, int cloexec);
enum {
ACQUIRE_NO_DEV_NULL = 1 << 0,
ACQUIRE_NO_MEMFD = 1 << 1,
ACQUIRE_NO_PIPE = 1 << 2,
ACQUIRE_NO_TMPFILE = 1 << 3,
ACQUIRE_NO_REGULAR = 1 << 4,
};
int acquire_data_fd(const void *data, size_t size, unsigned flags);
/* Hint: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5 */
#define ERRNO_IS_DISCONNECT(r) \
IN_SET(r, ENOTCONN, ECONNRESET, ECONNREFUSED, ECONNABORTED, EPIPE, ENETUNREACH)
+2 -2
View File
@@ -509,7 +509,7 @@ static int getenv_tmp_dir(const char **ret_path) {
r = -ENOTDIR;
goto next;
}
if (!path_is_safe(e)) {
if (!path_is_normalized(e)) {
r = -EPERM;
goto next;
}
@@ -707,7 +707,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
if (errno == ENOENT &&
(flags & CHASE_NONEXISTENT) &&
(isempty(todo) || path_is_safe(todo))) {
(isempty(todo) || path_is_normalized(todo))) {
/* If CHASE_NONEXISTENT is set, and the path does not exist, then that's OK, return
* what we got so far. But don't allow this if the remaining path contains "../ or "./"
+37 -13
View File
@@ -97,7 +97,10 @@ int unhexmem(const char *p, size_t l, void **mem, size_t *len) {
assert(mem);
assert(len);
assert(p);
assert(p || l == 0);
if (l == (size_t) -1)
l = strlen(p);
if (l % 2 != 0)
return -EINVAL;
@@ -161,6 +164,8 @@ char *base32hexmem(const void *p, size_t l, bool padding) {
const uint8_t *x;
size_t len;
assert(p || l == 0);
if (padding)
/* five input bytes makes eight output bytes, padding is added so we must round up */
len = 8 * (l + 4) / 5;
@@ -270,7 +275,12 @@ int unbase32hexmem(const char *p, size_t l, bool padding, void **mem, size_t *_l
size_t len;
unsigned pad = 0;
assert(p);
assert(p || l == 0);
assert(mem);
assert(_len);
if (l == (size_t) -1)
l = strlen(p);
/* padding ensures any base32hex input has input divisible by 8 */
if (padding && l % 8 != 0)
@@ -520,6 +530,9 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
char *r, *z;
const uint8_t *x;
assert(p || l == 0);
assert(out);
/* three input bytes makes four output bytes, padding is added so we must round up */
z = r = malloc(4 * (l + 2) / 3 + 1);
if (!r)
@@ -555,10 +568,11 @@ ssize_t base64mem(const void *p, size_t l, char **out) {
return z - r;
}
static int base64_append_width(char **prefix, int plen,
const char *sep, int indent,
const void *p, size_t l,
int width) {
static int base64_append_width(
char **prefix, int plen,
const char *sep, int indent,
const void *p, size_t l,
int width) {
_cleanup_free_ char *x = NULL;
char *t, *s;
@@ -597,17 +611,18 @@ static int base64_append_width(char **prefix, int plen,
return 0;
}
int base64_append(char **prefix, int plen,
const void *p, size_t l,
int indent, int width) {
int base64_append(
char **prefix, int plen,
const void *p, size_t l,
int indent, int width) {
if (plen > width / 2 || plen + indent > width)
/* leave indent on the left, keep last column free */
return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1);
else
/* leave plen on the left, keep last column free */
return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1);
};
}
int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
_cleanup_free_ uint8_t *r = NULL;
@@ -616,7 +631,12 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
const char *x;
size_t len;
assert(p);
assert(p || l == 0);
assert(mem);
assert(_len);
if (l == (size_t) -1)
l = strlen(p);
/* padding ensures any base63 input has input divisible by 4 */
if (l % 4 != 0)
@@ -660,6 +680,7 @@ int unbase64mem(const char *p, size_t l, void **mem, size_t *_len) {
}
switch (l % 4) {
case 3:
a = unbase64char(x[0]);
if (a < 0)
@@ -717,7 +738,10 @@ void hexdump(FILE *f, const void *p, size_t s) {
const uint8_t *b = p;
unsigned n = 0;
assert(s == 0 || b);
assert(b || s == 0);
if (!f)
f = stdout;
while (s > 0) {
size_t i;
+1 -2
View File
@@ -722,7 +722,7 @@ bool filename_is_valid(const char *p) {
return true;
}
bool path_is_safe(const char *p) {
bool path_is_normalized(const char *p) {
if (isempty(p))
return false;
@@ -736,7 +736,6 @@ bool path_is_safe(const char *p) {
if (strlen(p)+1 > PATH_MAX)
return false;
/* The following two checks are not really dangerous, but hey, they still are confusing */
if (startswith(p, "./") || endswith(p, "/.") || strstr(p, "/./"))
return false;
+1 -1
View File
@@ -132,7 +132,7 @@ int parse_path_argument_and_warn(const char *path, bool suppress_root, char **ar
char* dirname_malloc(const char *path);
bool filename_is_valid(const char *p) _pure_;
bool path_is_safe(const char *p) _pure_;
bool path_is_normalized(const char *p) _pure_;
char *file_in_same_dir(const char *path, const char *filename);
+9 -5
View File
@@ -271,17 +271,21 @@ static const char * const rlmap_initrd[] = {
};
const char* runlevel_to_target(const char *word) {
const char * const *rlmap_ptr;
size_t i;
const char * const *rlmap_ptr = in_initrd() ? rlmap_initrd
: rlmap;
if (!word)
return NULL;
if (in_initrd() && (word = startswith(word, "rd.")) == NULL)
return NULL;
if (in_initrd()) {
word = startswith(word, "rd.");
if (!word)
return NULL;
}
for (i = 0; rlmap_ptr[i] != NULL; i += 2)
rlmap_ptr = in_initrd() ? rlmap_initrd : rlmap;
for (i = 0; rlmap_ptr[i]; i += 2)
if (streq(word, rlmap_ptr[i]))
return rlmap_ptr[i+1];
+2 -2
View File
@@ -383,7 +383,7 @@ int unit_name_path_escape(const char *f, char **ret) {
if (STR_IN_SET(p, "/", ""))
s = strdup("-");
else {
if (!path_is_safe(p))
if (!path_is_normalized(p))
return -EINVAL;
/* Truncate trailing slashes */
@@ -433,7 +433,7 @@ int unit_name_path_unescape(const char *f, char **ret) {
if (!s)
return -ENOMEM;
if (!path_is_safe(s)) {
if (!path_is_normalized(s)) {
free(s);
return -EINVAL;
}
+1 -1
View File
@@ -606,7 +606,7 @@ bool valid_home(const char *p) {
if (!path_is_absolute(p))
return false;
if (!path_is_safe(p))
if (!path_is_normalized(p))
return false;
/* Colons are used as field separators, and hence not OK */