@@ -221,16 +221,17 @@ static int process_http_upload(
journal_remote_server_global->seal);
if (r == -EAGAIN)
break;
else if (r < 0) {
log_warning("Failed to process data for connection %p", connection);
if (r == -E2BIG)
return mhd_respondf(connection,
r, MHD_HTTP_PAYLOAD_TOO_LARGE,
"Entry is too large, maximum is " STRINGIFY(DATA_SIZE_MAX) " bytes.");
if (r < 0) {
if (r == -ENOBUFS)
log_warning_errno(r, "Entry is above the maximum of %u, aborting connection %p.",
DATA_SIZE_MAX, connection);
else if (r == -E2BIG)
log_warning_errno(r, "Entry with more fields than the maximum of %u, aborting connection %p.",
ENTRY_FIELD_COUNT_MAX, connection);
else
return mhd_respondf(connection,
r, MHD_HTTP_UNPROCESSABLE_ENTITY,
"Processing failed: %m.");
log_warning_errno(r, "Failed to process data, aborting connection %p: %m",
connection);
return MHD_NO;
}
}

@@ -264,6 +265,7 @@ static int request_handler(
const char *header;
int r, code, fd;
_cleanup_free_ char *hostname = NULL;
size_t len;

assert(connection);
assert(connection_cls);
@@ -283,12 +285,27 @@ static int request_handler(
if (!streq(url, "/upload"))
return mhd_respond(connection, MHD_HTTP_NOT_FOUND, "Not found.");

header = MHD_lookup_connection_value(connection,
MHD_HEADER_KIND, "Content-Type");
header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Type");
if (!header || !streq(header, "application/vnd.fdo.journal"))
return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE,
"Content-Type: application/vnd.fdo.journal is required.");

header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length");
if (!header)
return mhd_respond(connection, MHD_HTTP_LENGTH_REQUIRED,
"Content-Length header is required.");
r = safe_atozu(header, &len);
if (r < 0)
return mhd_respondf(connection, r, MHD_HTTP_LENGTH_REQUIRED,
"Content-Length: %s cannot be parsed: %m", header);

if (len > ENTRY_SIZE_MAX)
/* When serialized, an entry of maximum size might be slightly larger,
* so this does not correspond exactly to the limit in journald. Oh well.
*/
return mhd_respondf(connection, 0, MHD_HTTP_PAYLOAD_TOO_LARGE,
"Payload larger than maximum size of %u bytes", ENTRY_SIZE_MAX);

{
const union MHD_ConnectionInfo *ci;

@@ -407,6 +407,9 @@ int journal_remote_handle_raw_source(
log_debug("%zu active sources remaining", s->active);
return 0;
} else if (r == -E2BIG) {
log_notice("Entry with too many fields, skipped");
return 1;
} else if (r == -ENOBUFS) {
log_notice("Entry too big, skipped");
return 1;
} else if (r == -EAGAIN) {
@@ -32,21 +32,16 @@ static int mhd_respond_internal(struct MHD_Connection *connection,
const char *buffer,
size_t size,
enum MHD_ResponseMemoryMode mode) {
struct MHD_Response *response;
int r;

assert(connection);

response = MHD_create_response_from_buffer(size, (char*) buffer, mode);
_cleanup_(MHD_destroy_responsep) struct MHD_Response *response
= MHD_create_response_from_buffer(size, (char*) buffer, mode);
if (!response)
return MHD_NO;

log_debug("Queueing response %u: %s", code, buffer);
MHD_add_response_header(response, "Content-Type", "text/plain");
r = MHD_queue_response(connection, code, response);
MHD_destroy_response(response);

return r;
return MHD_queue_response(connection, code, response);
}

int mhd_respond(struct MHD_Connection *connection,
@@ -75,3 +75,4 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn
int setup_gnutls_logger(char **categories);

DEFINE_TRIVIAL_CLEANUP_FUNC(struct MHD_Daemon*, MHD_stop_daemon);
DEFINE_TRIVIAL_CLEANUP_FUNC(struct MHD_Response*, MHD_destroy_response);
@@ -110,7 +110,7 @@ static int server_process_entry(
int priority = LOG_INFO;
pid_t object_pid = 0;
const char *p;
int r = 0;
int r = 1;

p = buffer;

@@ -122,8 +122,7 @@ static int server_process_entry(
if (!e) {
/* Trailing noise, let's ignore it, and flush what we collected */
log_debug("Received message with trailing noise, ignoring.");
r = 1; /* finish processing of the message */
break;
break; /* finish processing of the message */
}

if (e == p) {
@@ -133,22 +132,25 @@ static int server_process_entry(
}

if (IN_SET(*p, '.', '#')) {
/* Ignore control commands for now, and
* comments too. */
/* Ignore control commands for now, and comments too. */
*remaining -= (e - p) + 1;
p = e + 1;
continue;
}

/* A property follows */
if (n > ENTRY_FIELD_COUNT_MAX) {
log_debug("Received an entry that has more than " STRINGIFY(ENTRY_FIELD_COUNT_MAX) " fields, ignoring entry.");
goto finish;
}

/* n existing properties, 1 new, +1 for _TRANSPORT */
if (!GREEDY_REALLOC(iovec, m,
n + 2 +
N_IOVEC_META_FIELDS + N_IOVEC_OBJECT_FIELDS +
client_context_extra_fields_n_iovec(context))) {
r = log_oom();
break;
goto finish;
}

q = memchr(p, '=', e - p);
@@ -157,6 +159,16 @@ static int server_process_entry(
size_t l;

l = e - p;
if (l > DATA_SIZE_MAX) {
log_debug("Received text block of %zu bytes is too large, ignoring entry.", l);
goto finish;
}

if (entry_size + l + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
log_debug("Entry is too big (%zu bytes after processing %zu entries), ignoring entry.",
entry_size + l, n + 1);
goto finish;
}

/* If the field name starts with an underscore, skip the variable, since that indicates
* a trusted field */
@@ -174,7 +186,7 @@ static int server_process_entry(
p = e + 1;
continue;
} else {
uint64_t l;
uint64_t l, total;
char *k;

if (*remaining < e - p + 1 + sizeof(uint64_t) + 1) {
@@ -183,10 +195,16 @@ static int server_process_entry(
}

l = unaligned_read_le64(e + 1);

if (l > DATA_SIZE_MAX) {
log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
break;
log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring entry.", l);
goto finish;
}

total = (e - p) + 1 + l;
if (entry_size + total + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
log_debug("Entry is too big (%"PRIu64"bytes after processing %zu fields), ignoring.",
entry_size + total, n + 1);
goto finish;
}

if ((uint64_t) *remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
@@ -195,7 +213,7 @@ static int server_process_entry(
break;
}

k = malloc((e - p) + 1 + l);
k = malloc(total);
if (!k) {
log_oom();
break;
@@ -223,15 +241,8 @@ static int server_process_entry(
}
}

if (n <= 0) {
r = 1;
if (n <= 0)
goto finish;
}

if (!client_context_test_priority(context, priority)) {
r = 0;
goto finish;
}

tn = n++;
iovec[tn] = IOVEC_MAKE_STRING("_TRANSPORT=journal");
@@ -242,6 +253,11 @@ static int server_process_entry(
goto finish;
}

r = 0; /* Success, we read the message. */

if (!client_context_test_priority(context, priority))
goto finish;

if (message) {
if (s->forward_to_syslog)
server_forward_syslog(s, syslog_fixup_facility(priority), identifier, message, ucred, tv);
@@ -313,15 +329,13 @@ void server_process_native_file(
bool sealed;
int r;

/* Data is in the passed fd, since it didn't fit in a
* datagram. */
/* Data is in the passed fd, probably it didn't fit in a datagram. */

assert(s);
assert(fd >= 0);

/* If it's a memfd, check if it is sealed. If so, we can just
* use map it and use it, and do not need to copy the data
* out. */
* mmap it and use it, and do not need to copy the data out. */
sealed = memfd_get_sealed(fd) > 0;

if (!sealed && (!ucred || ucred->uid != 0)) {
@@ -362,8 +376,10 @@ void server_process_native_file(
if (st.st_size <= 0)
return;

if (st.st_size > ENTRY_SIZE_MAX) {
log_error("File passed too large. Ignoring.");
/* When !sealed, set a lower memory limit. We have to read the file,
* effectively doubling memory use. */
if (st.st_size > ENTRY_SIZE_MAX / (sealed ? 1 : 2)) {
log_error("File passed too large (%"PRIu64" bytes). Ignoring.", (uint64_t) st.st_size);
return;
}

@@ -388,7 +404,7 @@ void server_process_native_file(
ssize_t n;

if (fstatvfs(fd, &vfs) < 0) {
log_error_errno(errno, "Failed to stat file system of passed file, ignoring: %m");
log_error_errno(errno, "Failed to stat file system of passed file, not processing it: %m");
return;
}

@@ -398,7 +414,7 @@ void server_process_native_file(
* https://github.com/systemd/systemd/issues/1822
*/
if (vfs.f_flag & ST_MANDLOCK) {
log_error("Received file descriptor from file system with mandatory locking enabled, refusing.");
log_error("Received file descriptor from file system with mandatory locking enabled, not processing it.");
return;
}

@@ -411,13 +427,13 @@ void server_process_native_file(
* and so is SMB. */
r = fd_nonblock(fd, true);
if (r < 0) {
log_error_errno(r, "Failed to make fd non-blocking, ignoring: %m");
log_error_errno(r, "Failed to make fd non-blocking, not processing it: %m");
return;
}

/* The file is not sealed, we can't map the file here, since
* clients might then truncate it and trigger a SIGBUS for
* us. So let's stupidly read it */
* us. So let's stupidly read it. */

p = malloc(st.st_size);
if (!p) {
@@ -905,6 +905,7 @@ static void dispatch_message_real(
pid_t object_pid) {

char source_time[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
_cleanup_free_ char *cmdline1 = NULL, *cmdline2 = NULL;
uid_t journal_uid;
ClientContext *o;

@@ -921,20 +922,23 @@ static void dispatch_message_real(
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->uid, uid_t, uid_is_valid, UID_FMT, "_UID");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->gid, gid_t, gid_is_valid, GID_FMT, "_GID");

IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM");
IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE");
IOVEC_ADD_STRING_FIELD(iovec, n, c->cmdline, "_CMDLINE");
IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE");
IOVEC_ADD_STRING_FIELD(iovec, n, c->comm, "_COMM"); /* At most TASK_COMM_LENGTH (16 bytes) */
IOVEC_ADD_STRING_FIELD(iovec, n, c->exe, "_EXE"); /* A path, so at most PATH_MAX (4096 bytes) */

IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
if (c->cmdline)
/* At most _SC_ARG_MAX (2MB usually), which is too much to put on stack.
* Let's use a heap allocation for this one. */
cmdline1 = set_iovec_string_field(iovec, &n, "_CMDLINE=", c->cmdline);

IOVEC_ADD_STRING_FIELD(iovec, n, c->capeff, "_CAP_EFFECTIVE"); /* Read from /proc/.../status */
IOVEC_ADD_SIZED_FIELD(iovec, n, c->label, c->label_size, "_SELINUX_CONTEXT");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "_AUDIT_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->loginuid, uid_t, uid_is_valid, UID_FMT, "_AUDIT_LOGINUID");

IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP");
IOVEC_ADD_STRING_FIELD(iovec, n, c->cgroup, "_SYSTEMD_CGROUP"); /* A path */
IOVEC_ADD_STRING_FIELD(iovec, n, c->session, "_SYSTEMD_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, c->owner_uid, uid_t, uid_is_valid, UID_FMT, "_SYSTEMD_OWNER_UID");
IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT");
IOVEC_ADD_STRING_FIELD(iovec, n, c->unit, "_SYSTEMD_UNIT"); /* Unit names are bounded by UNIT_NAME_MAX */
IOVEC_ADD_STRING_FIELD(iovec, n, c->user_unit, "_SYSTEMD_USER_UNIT");
IOVEC_ADD_STRING_FIELD(iovec, n, c->slice, "_SYSTEMD_SLICE");
IOVEC_ADD_STRING_FIELD(iovec, n, c->user_slice, "_SYSTEMD_USER_SLICE");
@@ -955,13 +959,14 @@ static void dispatch_message_real(
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->uid, uid_t, uid_is_valid, UID_FMT, "OBJECT_UID");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->gid, gid_t, gid_is_valid, GID_FMT, "OBJECT_GID");

/* See above for size limits, only ->cmdline may be large, so use a heap allocation for it. */
IOVEC_ADD_STRING_FIELD(iovec, n, o->comm, "OBJECT_COMM");
IOVEC_ADD_STRING_FIELD(iovec, n, o->exe, "OBJECT_EXE");
IOVEC_ADD_STRING_FIELD(iovec, n, o->cmdline, "OBJECT_CMDLINE");
IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
if (o->cmdline)
cmdline2 = set_iovec_string_field(iovec, &n, "OBJECT_CMDLINE=", o->cmdline);

IOVEC_ADD_STRING_FIELD(iovec, n, o->capeff, "OBJECT_CAP_EFFECTIVE");
IOVEC_ADD_SIZED_FIELD(iovec, n, o->label, o->label_size, "OBJECT_SELINUX_CONTEXT");

IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->auditid, uint32_t, audit_session_is_valid, "%" PRIu32, "OBJECT_AUDIT_SESSION");
IOVEC_ADD_NUMERIC_FIELD(iovec, n, o->loginuid, uid_t, uid_is_valid, UID_FMT, "OBJECT_AUDIT_LOGINUID");

@@ -1276,8 +1281,7 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
return log_error_errno(errno, "recvmsg() failed: %m");
}

CMSG_FOREACH(cmsg, &msghdr) {

CMSG_FOREACH(cmsg, &msghdr)
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_CREDENTIALS &&
cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)))
@@ -1295,7 +1299,6 @@ int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void
fds = (int*) CMSG_DATA(cmsg);
n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
}
}

/* And a trailing NUL, just in case */
s->buffer[n] = 0;
@@ -23,6 +23,9 @@ enum {
};

static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
if (iovw->count >= ENTRY_FIELD_COUNT_MAX)
return -E2BIG;

if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
return log_oom();

@@ -97,7 +100,7 @@ static int get_line(JournalImporter *imp, char **line, size_t *size) {

imp->scanned = imp->filled;
if (imp->scanned >= DATA_SIZE_MAX)
return log_error_errno(SYNTHETIC_ERRNO(E2BIG),
return log_error_errno(SYNTHETIC_ERRNO(ENOBUFS),
"Entry is bigger than %u bytes.",
DATA_SIZE_MAX);

@@ -21,6 +21,9 @@
#endif
#define LINE_CHUNK 8*1024u

/* The maximum number of fields in an entry */
#define ENTRY_FIELD_COUNT_MAX 1024

struct iovec_wrapper {
struct iovec *iovec;
size_t size_bytes;