chiark / gitweb /
util: unify SO_PEERCRED/SO_PEERSEC invocations
[elogind.git] / src / libsystemd-bus / bus-message.c
index 2e355a74c3609023d3b3d2404f0cb3cb227cd937..f09f0d682df372a6a8589825f99c50072317bb14 100644 (file)
@@ -35,6 +35,7 @@
 #include "bus-type.h"
 #include "bus-signature.h"
 #include "bus-gvariant.h"
+#include "bus-util.h"
 
 static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored);
 
@@ -64,7 +65,7 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
                  * can't be sealed yet. */
 
                 if (!part->sealed)
-                        bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped);
+                        bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped, part->allocated);
                 else {
                         if (part->mapped > 0)
                                 assert_se(munmap(part->data, part->mapped) == 0);
@@ -113,7 +114,7 @@ static void message_reset_containers(sd_bus_message *m) {
         free(m->containers);
         m->containers = NULL;
 
-        m->n_containers = 0;
+        m->n_containers = m->containers_allocated = 0;
         m->root_container.index = 0;
 }
 
@@ -150,7 +151,7 @@ static void message_free(sd_bus_message *m) {
         free(m->root_container.signature);
         free(m->root_container.offsets);
 
-        free(m->peeked_signature);
+        free(m->root_container.peeked_signature);
 
         bus_creds_done(&m->creds);
         free(m);
@@ -791,7 +792,9 @@ _public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) {
 }
 
 _public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) {
-        assert_return(m, NULL);
+
+        if (!m)
+                return NULL;
 
         assert(m->n_ref > 0);
         m->n_ref--;
@@ -1046,20 +1049,27 @@ static int part_make_space(
                 return -ENOMEM;
 
         if (!part->data && part->memfd < 0)
-                part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped);
+                part->memfd = bus_kernel_pop_memfd(m->bus, &part->data, &part->mapped, &part->allocated);
 
         if (part->memfd >= 0) {
-                uint64_t u = sz;
 
-                r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &u);
-                if (r < 0) {
-                        m->poisoned = true;
-                        return -errno;
+                if (part->allocated == 0 || sz > part->allocated) {
+                        uint64_t new_allocated;
+
+                        new_allocated = PAGE_ALIGN(sz > 0 ? 2 * sz : 1);
+                        r = ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &new_allocated);
+                        if (r < 0) {
+                                m->poisoned = true;
+                                return -errno;
+                        }
+
+                        part->allocated = new_allocated;
                 }
 
                 if (!part->data || sz > part->mapped) {
-                        size_t psz = PAGE_ALIGN(sz > 0 ? sz : 1);
+                        size_t psz;
 
+                        psz = PAGE_ALIGN(sz > 0 ? sz : 1);
                         if (part->mapped <= 0)
                                 n = mmap(NULL, psz, PROT_READ|PROT_WRITE, MAP_SHARED, part->memfd, 0);
                         else
@@ -1076,14 +1086,20 @@ static int part_make_space(
 
                 part->munmap_this = true;
         } else {
-                n = realloc(part->data, MAX(sz, 1u));
-                if (!n) {
-                        m->poisoned = true;
-                        return -ENOMEM;
-                }
+                if (part->allocated == 0 || sz > part->allocated) {
+                        size_t new_allocated;
+
+                        new_allocated = sz > 0 ? 2 * sz : 64;
+                        n = realloc(part->data, new_allocated);
+                        if (!n) {
+                                m->poisoned = true;
+                                return -ENOMEM;
+                        }
 
-                part->data = n;
-                part->free_this = true;
+                        part->data = n;
+                        part->allocated = new_allocated;
+                        part->free_this = true;
+                }
         }
 
         if (q)
@@ -1107,7 +1123,7 @@ static int message_add_offset(sd_bus_message *m, size_t offset) {
         if (!c->need_offsets)
                 return 0;
 
-        if (!GREEDY_REALLOC(c->offsets, c->n_offsets_allocated, c->n_offsets + 1))
+        if (!GREEDY_REALLOC(c->offsets, c->offsets_allocated, c->n_offsets + 1))
                 return -ENOMEM;
 
         c->offsets[c->n_offsets++] = offset;
@@ -1131,9 +1147,7 @@ static void message_extend_containers(sd_bus_message *m, size_t expand) {
 }
 
 static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, bool add_offset) {
-        struct bus_body_part *part = NULL;
-        size_t start_body, end_body, padding, start_part, end_part, added;
-        bool add_new_part;
+        size_t start_body, end_body, padding, added;
         void *p;
         int r;
 
@@ -1156,54 +1170,61 @@ static void *message_extend_body(sd_bus_message *m, size_t align, size_t sz, boo
                 return NULL;
         }
 
-        add_new_part =
-                m->n_body_parts <= 0 ||
-                m->body_end->sealed ||
-                padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size;
+        if (added > 0) {
+                struct bus_body_part *part = NULL;
+                bool add_new_part;
+
+                add_new_part =
+                        m->n_body_parts <= 0 ||
+                        m->body_end->sealed ||
+                        padding != ALIGN_TO(m->body_end->size, align) - m->body_end->size;
+
+                if (add_new_part) {
+                        if (padding > 0) {
+                                part = message_append_part(m);
+                                if (!part)
+                                        return NULL;
+
+                                part_zero(part, padding);
+                        }
 
-        if (add_new_part) {
-                if (padding > 0) {
                         part = message_append_part(m);
                         if (!part)
                                 return NULL;
 
-                        part_zero(part, padding);
-                }
+                        r = part_make_space(m, part, sz, &p);
+                        if (r < 0)
+                                return NULL;
+                } else {
+                        struct bus_container *c;
+                        void *op;
+                        size_t os, start_part, end_part;
 
-                part = message_append_part(m);
-                if (!part)
-                        return NULL;
+                        part = m->body_end;
+                        op = part->data;
+                        os = part->size;
 
-                r = part_make_space(m, part, sz, &p);
-                if (r < 0)
-                        return NULL;
-        } else {
-                struct bus_container *c;
-                void *op;
-                size_t os;
+                        start_part = ALIGN_TO(part->size, align);
+                        end_part = start_part + sz;
 
-                part = m->body_end;
-                op = part->data;
-                os = part->size;
+                        r = part_make_space(m, part, end_part, &p);
+                        if (r < 0)
+                                return NULL;
 
-                start_part = ALIGN_TO(part->size, align);
-                end_part = start_part + sz;
+                        if (padding > 0) {
+                                memset(p, 0, padding);
+                                p = (uint8_t*) p + padding;
+                        }
 
-                r = part_make_space(m, part, end_part, &p);
-                if (r < 0)
-                        return NULL;
+                        /* Readjust pointers */
+                        for (c = m->containers; c < m->containers + m->n_containers; c++)
+                                c->array_size = adjust_pointer(c->array_size, op, os, part->data);
 
-                if (padding > 0) {
-                        memset(p, 0, padding);
-                        p = (uint8_t*) p + padding;
+                        m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data);
                 }
-
-                /* Readjust pointers */
-                for (c = m->containers; c < m->containers + m->n_containers; c++)
-                        c->array_size = adjust_pointer(c->array_size, op, os, part->data);
-
-                m->error.message = (const char*) adjust_pointer(m->error.message, op, os, part->data);
-        }
+        } else
+                /* Return something that is not NULL and is aligned */
+                p = (uint8_t *) NULL + align;
 
         m->header->body_size = end_body;
         message_extend_containers(m, added);
@@ -1836,14 +1857,11 @@ _public_ int sd_bus_message_open_container(
         assert_return(!m->poisoned, -ESTALE);
 
         /* Make sure we have space for one more container */
-        w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
-        if (!w) {
+        if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1)) {
                 m->poisoned = true;
                 return -ENOMEM;
         }
 
-        m->containers = w;
-
         c = message_get_container(m);
 
         signature = strdup(contents);
@@ -1874,14 +1892,14 @@ _public_ int sd_bus_message_open_container(
         }
 
         /* OK, let's fill it in */
-        w += m->n_containers++;
+        w = m->containers + m->n_containers++;
         w->enclosing = type;
         w->signature = signature;
         w->index = 0;
         w->array_size = array_size;
         w->before = before;
         w->begin = begin;
-        w->n_offsets = w->n_offsets_allocated = 0;
+        w->n_offsets = w->offsets_allocated = 0;
         w->offsets = NULL;
         w->need_offsets = need_offsets;
 
@@ -1889,9 +1907,7 @@ _public_ int sd_bus_message_open_container(
 }
 
 static size_t determine_word_size(size_t sz, size_t extra) {
-        if (sz <= 0 && extra == 0)
-                return 0;
-        else if (sz + extra <= 0xFF)
+        if (sz + extra <= 0xFF)
                 return 1;
         else if (sz + extra*2 <= 0xFFFF)
                 return 2;
@@ -2021,7 +2037,7 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
         if (!BUS_MESSAGE_IS_GVARIANT(m))
                 return 0;
 
-        p = c->signature;
+        p = strempty(c->signature);
         while (*p != 0) {
                 size_t n;
 
@@ -2039,7 +2055,7 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
                                 return r;
                 }
 
-                assert(i <= c->n_offsets);
+                assert(!c->need_offsets || i <= c->n_offsets);
 
                 /* We need to add an offset for each item that has a
                  * variable size and that is not the last one in the
@@ -2051,7 +2067,8 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
                 p += n;
         }
 
-        assert(i == c->n_offsets);
+        assert(!c->need_offsets || i == c->n_offsets);
+        assert(c->need_offsets || n_variable == 0);
 
         if (n_variable <= 0) {
                 a = message_extend_body(m, 1, 0, add_offset);
@@ -2069,7 +2086,7 @@ static int bus_message_close_struct(sd_bus_message *m, struct bus_container *c,
                 if (!a)
                         return -ENOMEM;
 
-                p = c->signature;
+                p = strempty(c->signature);
                 for (i = 0, j = 0; i < c->n_offsets; i++) {
                         unsigned k;
                         size_t n;
@@ -2399,10 +2416,12 @@ _public_ int sd_bus_message_append_array_space(
 
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
-        assert_return(bus_type_is_trivial(type), -EINVAL);
+        assert_return(bus_type_is_trivial(type) && type != SD_BUS_TYPE_BOOLEAN, -EINVAL);
         assert_return(ptr || size == 0, -EINVAL);
         assert_return(!m->poisoned, -ESTALE);
 
+        /* alignment and size of the trivial types (except bool) is
+         * identical for gvariant and dbus1 marshalling */
         align = bus_type_get_alignment(type);
         sz = bus_type_get_size(type);
 
@@ -2549,8 +2568,8 @@ _public_ int sd_bus_message_append_array_memfd(sd_bus_message *m,
         part->size = size;
         copy_fd = -1;
 
-        message_extend_containers(m, size);
         m->header->body_size += size;
+        message_extend_containers(m, size);
 
         return sd_bus_message_close_container(m);
 }
@@ -2662,6 +2681,124 @@ _public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) {
         return sd_bus_message_close_container(m);
 }
 
+static int bus_message_close_header(sd_bus_message *m) {
+        uint8_t *a;
+        size_t sz, i;
+
+        assert(m);
+
+        if (!BUS_MESSAGE_IS_GVARIANT(m))
+                return 0;
+
+        if (m->n_header_offsets < 1)
+                return 0;
+
+        assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]);
+
+        sz = determine_word_size(m->header->fields_size, m->n_header_offsets);
+
+        a = message_extend_fields(m, 1, sz * m->n_header_offsets, false);
+        if (!a)
+                return -ENOMEM;
+
+        for (i = 0; i < m->n_header_offsets; i++)
+                write_word_le(a + sz*i, sz, m->header_offsets[i]);
+
+        return 0;
+}
+
+int bus_message_seal(sd_bus_message *m, uint64_t serial, usec_t timeout) {
+        struct bus_body_part *part;
+        size_t l, a;
+        unsigned i;
+        int r;
+
+        assert(m);
+
+        if (m->sealed)
+                return -EPERM;
+
+        if (m->n_containers > 0)
+                return -EBADMSG;
+
+        if (m->poisoned)
+                return -ESTALE;
+
+        /* In vtables the return signature of method calls is listed,
+         * let's check if they match if this is a response */
+        if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN &&
+            m->enforced_reply_signature &&
+            !streq(strempty(m->root_container.signature), m->enforced_reply_signature))
+                return -ENOMSG;
+
+        /* If gvariant marshalling is used we need to close the body structure */
+        r = bus_message_close_struct(m, &m->root_container, false);
+        if (r < 0)
+                return r;
+
+        /* If there's a non-trivial signature set, then add it in here */
+        if (!isempty(m->root_container.signature)) {
+                r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
+                if (r < 0)
+                        return r;
+        }
+
+        if (m->n_fds > 0) {
+                r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
+                if (r < 0)
+                        return r;
+        }
+
+        r = bus_message_close_header(m);
+        if (r < 0)
+                return r;
+
+        m->header->serial = serial;
+        m->timeout = m->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED ? 0 : timeout;
+
+        /* Add padding at the end of the fields part, since we know
+         * the body needs to start at an 8 byte alignment. We made
+         * sure we allocated enough space for this, so all we need to
+         * do here is to zero it out. */
+        l = BUS_MESSAGE_FIELDS_SIZE(m);
+        a = ALIGN8(l) - l;
+        if (a > 0)
+                memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
+
+        /* If this is something we can send as memfd, then let's seal
+        the memfd now. Note that we can send memfds as payload only
+        for directed messages, and not for broadcasts. */
+        if (m->destination && m->bus && m->bus->use_memfd) {
+                MESSAGE_FOREACH_PART(part, i, m)
+                        if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
+                                uint64_t sz;
+
+                                /* Try to seal it if that makes
+                                 * sense. First, unmap our own map to
+                                 * make sure we don't keep it busy. */
+                                bus_body_part_unmap(part);
+
+                                /* Then, sync up real memfd size */
+                                sz = part->size;
+                                if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SIZE_SET, &sz) < 0)
+                                        return -errno;
+
+                                /* Finally, try to seal */
+                                if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
+                                        part->sealed = true;
+                        }
+        }
+
+        m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
+        m->root_container.index = 0;
+        m->root_container.offset_index = 0;
+        m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0;
+
+        m->sealed = true;
+
+        return 0;
+}
+
 int bus_body_part_map(struct bus_body_part *part) {
         void *p;
         size_t psz;
@@ -2858,8 +2995,12 @@ static int container_next_item(sd_bus_message *m, struct bus_container *c, size_
                         *rindex = ALIGN_TO(c->offsets[c->offset_index], alignment);
                         c->item_size = c->offsets[c->offset_index+1] - *rindex;
                 } else {
+
+                        if (c->offset_index+1 >= (c->end-c->begin)/sz)
+                                goto end;
+
                         /* Fixed-size array */
-                        *rindex += sz;
+                        *rindex = c->begin + (c->offset_index+1) * sz;
                         c->item_size = sz;
                 }
 
@@ -2927,9 +3068,6 @@ static int message_peek_body(
         assert(rindex);
         assert(align > 0);
 
-        if (message_end_of_array(m, *rindex))
-                return 0;
-
         start = ALIGN_TO((size_t) *rindex, align);
         padding = start - *rindex;
         end = start + nbytes;
@@ -2949,7 +3087,7 @@ static int message_peek_body(
         }
 
         part = find_part(m, start, nbytes, (void**) &q);
-        if (!part || !q)
+        if (!part || (nbytes > 0 && !q))
                 return -EBADMSG;
 
         *rindex = end;
@@ -2957,7 +3095,7 @@ static int message_peek_body(
         if (ret)
                 *ret = q;
 
-        return 1;
+        return 0;
 }
 
 static bool validate_nul(const char *s, size_t l) {
@@ -3036,7 +3174,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         bool ok;
 
                         r = message_peek_body(m, &rindex, 1, c->item_size, &q);
-                        if (r <= 0)
+                        if (r < 0)
                                 return r;
 
                         if (type == SD_BUS_TYPE_STRING)
@@ -3063,7 +3201,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         assert(align > 0);
 
                         r = message_peek_body(m, &rindex, align, c->item_size, &q);
-                        if (r <= 0)
+                        if (r < 0)
                                 return r;
 
                         switch (type) {
@@ -3127,15 +3265,13 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         bool ok;
 
                         r = message_peek_body(m, &rindex, 4, 4, &q);
-                        if (r <= 0)
+                        if (r < 0)
                                 return r;
 
                         l = BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q);
                         r = message_peek_body(m, &rindex, 1, l+1, &q);
                         if (r < 0)
                                 return r;
-                        if (r == 0)
-                                return -EBADMSG;
 
                         if (type == SD_BUS_TYPE_OBJECT_PATH)
                                 ok = validate_object_path(q, l);
@@ -3151,15 +3287,13 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         uint8_t l;
 
                         r = message_peek_body(m, &rindex, 1, 1, &q);
-                        if (r <= 0)
+                        if (r < 0)
                                 return r;
 
                         l = *(uint8_t*) q;
                         r = message_peek_body(m, &rindex, 1, l+1, &q);
                         if (r < 0)
                                 return r;
-                        if (r == 0)
-                                return -EBADMSG;
 
                         if (!validate_signature(q, l))
                                 return -EBADMSG;
@@ -3177,7 +3311,7 @@ _public_ int sd_bus_message_read_basic(sd_bus_message *m, char type, void *p) {
                         assert(sz > 0);
 
                         r = message_peek_body(m, &rindex, align, sz, &q);
-                        if (r <= 0)
+                        if (r < 0)
                                 return r;
 
                         switch (type) {
@@ -3276,7 +3410,7 @@ static int bus_message_enter_array(
                 /* dbus1 */
 
                 r = message_peek_body(m, &rindex, 4, 4, &q);
-                if (r <= 0)
+                if (r < 0)
                         return r;
 
                 if (BUS_MESSAGE_BSWAP32(m, *(uint32_t*) q) > BUS_ARRAY_MAX_SIZE)
@@ -3289,8 +3423,6 @@ static int bus_message_enter_array(
                 r = message_peek_body(m, &rindex, alignment, 0, NULL);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 *array_size = (uint32_t*) q;
 
@@ -3319,8 +3451,6 @@ static int bus_message_enter_array(
                 r = message_peek_body(m, &where, 1, sz, &q);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 framing = read_word_le(q, sz);
                 if (framing > c->item_size - sz)
@@ -3334,8 +3464,6 @@ static int bus_message_enter_array(
                 r = message_peek_body(m, &where, 1, *n_offsets * sz, &q);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 *offsets = new(size_t, *n_offsets);
                 if (!*offsets)
@@ -3406,8 +3534,6 @@ static int bus_message_enter_variant(
                 r = message_peek_body(m, &where, 1, 1+k, &q);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 if (*(char*) q != 0)
                         return -EBADMSG;
@@ -3421,15 +3547,11 @@ static int bus_message_enter_variant(
                 r = message_peek_body(m, &rindex, 1, 1, &q);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 l = *(uint8_t*) q;
                 r = message_peek_body(m, &rindex, 1, l+1, &q);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        return -EBADMSG;
 
                 if (!validate_signature(q, l))
                         return -EBADMSG;
@@ -3462,12 +3584,20 @@ static int build_struct_offsets(
         int r;
 
         assert(m);
-        assert(signature);
         assert(item_size);
         assert(offsets);
         assert(n_offsets);
 
+        if (isempty(signature)) {
+                *item_size = 0;
+                *offsets = NULL;
+                *n_offsets = 0;
+                return 0;
+        }
+
         sz = determine_word_size(size, 0);
+        if (sz <= 0)
+                return -EBADMSG;
 
         /* First, loop over signature and count variable elements and
          * elements in general. We use this to know how large the
@@ -3507,8 +3637,6 @@ static int build_struct_offsets(
         r = message_peek_body(m, &where, 1, n_variable * sz, &q);
         if (r < 0)
                 return r;
-        if (r == 0)
-                return -EBADMSG;
 
         v = n_variable;
 
@@ -3598,7 +3726,7 @@ static int enter_struct_or_dict_entry(
 
                 /* dbus1 */
                 r = message_peek_body(m, &m->rindex, 8, 0, NULL);
-                if (r <= 0)
+                if (r < 0)
                         return r;
 
         } else if (c->item_size <= 0) {
@@ -3717,7 +3845,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
 
                 /* Allow entering into anonymous containers */
                 r = sd_bus_message_peek_type(m, &tt, &cc);
-                if (r <= 0)
+                if (r < 0)
                         return r;
 
                 if (type != 0 && type != tt)
@@ -3749,10 +3877,8 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
         if (m->n_containers >= BUS_CONTAINER_DEPTH)
                 return -EBADMSG;
 
-        w = realloc(m->containers, sizeof(struct bus_container) * (m->n_containers + 1));
-        if (!w)
+        if (!GREEDY_REALLOC(m->containers, m->containers_allocated, m->n_containers + 1))
                 return -ENOMEM;
-        m->containers = w;
 
         if (message_end_of_signature(m))
                 return -ENXIO;
@@ -3787,9 +3913,10 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
         }
 
         /* OK, let's fill it in */
-        w += m->n_containers++;
+        w = m->containers + m->n_containers++;
         w->enclosing = type;
         w->signature = signature;
+        w->peeked_signature = NULL;
         w->index = 0;
 
         w->before = before;
@@ -3807,6 +3934,7 @@ _public_ int sd_bus_message_enter_container(sd_bus_message *m,
 
 _public_ int sd_bus_message_exit_container(sd_bus_message *m) {
         struct bus_container *c;
+        unsigned saved;
         int r;
 
         assert_return(m, -EINVAL);
@@ -3833,12 +3961,16 @@ _public_ int sd_bus_message_exit_container(sd_bus_message *m) {
         }
 
         free(c->signature);
+        free(c->peeked_signature);
         free(c->offsets);
         m->n_containers--;
 
         c = message_get_container(m);
 
+        saved = c->index;
+        c->index = c->saved_index;
         r = container_next_item(m, c, &m->rindex);
+        c->index = saved;
         if (r < 0)
                 return r;
 
@@ -3907,10 +4039,8 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
                         if (!sig)
                                 return -ENOMEM;
 
-                        free(m->peeked_signature);
-                        m->peeked_signature = sig;
-
-                        *contents = sig;
+                        free(c->peeked_signature);
+                        *contents = c->peeked_signature = sig;
                 }
 
                 if (type)
@@ -3935,10 +4065,8 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
                         if (!sig)
                                 return -ENOMEM;
 
-                        free(m->peeked_signature);
-                        m->peeked_signature = sig;
-
-                        *contents = sig;
+                        free(c->peeked_signature);
+                        *contents = c->peeked_signature = sig;
                 }
 
                 if (type)
@@ -3970,8 +4098,6 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
                                         r = message_peek_body(m, &where, 1, k, &q);
                                         if (r < 0)
                                                 return r;
-                                        if (r == 0)
-                                                goto eof;
 
                                         if (*(char*) q == 0)
                                                 break;
@@ -3980,15 +4106,15 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
                                 if (k > c->item_size)
                                         return -EBADMSG;
 
-                                free(m->peeked_signature);
-                                m->peeked_signature = strndup((char*) q + 1, k - 1);
-                                if (!m->peeked_signature)
+                                free(c->peeked_signature);
+                                c->peeked_signature = strndup((char*) q + 1, k - 1);
+                                if (!c->peeked_signature)
                                         return -ENOMEM;
 
-                                if (!signature_is_valid(m->peeked_signature, true))
+                                if (!signature_is_valid(c->peeked_signature, true))
                                         return -EBADMSG;
 
-                                *contents = m->peeked_signature;
+                                *contents = c->peeked_signature;
                         } else {
                                 size_t rindex, l;
 
@@ -3996,15 +4122,11 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
                                 r = message_peek_body(m, &rindex, 1, 1, &q);
                                 if (r < 0)
                                         return r;
-                                if (r == 0)
-                                        goto eof;
 
                                 l = *(uint8_t*) q;
                                 r = message_peek_body(m, &rindex, 1, l+1, &q);
                                 if (r < 0)
                                         return r;
-                                if (r == 0)
-                                        return -EBADMSG;
 
                                 if (!validate_signature(q, l))
                                         return -EBADMSG;
@@ -4048,6 +4170,9 @@ _public_ int sd_bus_message_rewind(sd_bus_message *m, int complete) {
                 m->rindex = c->begin;
         }
 
+        c->offset_index = 0;
+        c->item_size = (c->n_offsets > 0 ? c->offsets[0] : c->end) - c->begin;
+
         return !isempty(c->signature);
 }
 
@@ -4434,7 +4559,7 @@ _public_ int sd_bus_message_read_array(sd_bus_message *m,
                 if (align < 0)
                         return align;
 
-                sz = c->item_size;
+                sz = c->end - c->begin;
         } else {
                 align = bus_type_get_alignment(type);
                 if (align < 0)
@@ -4451,10 +4576,6 @@ _public_ int sd_bus_message_read_array(sd_bus_message *m,
                 r = message_peek_body(m, &m->rindex, align, sz, &p);
                 if (r < 0)
                         goto fail;
-                if (r == 0) {
-                        r = -EBADMSG;
-                        goto fail;
-                }
         }
 
         r = sd_bus_message_exit_container(m);
@@ -4750,7 +4871,7 @@ int bus_message_parse_fields(sd_bus_message *m) {
         uint32_t unix_fds = 0;
         void *offsets = NULL;
         unsigned n_offsets = 0;
-        size_t sz;
+        size_t sz = 0;
         unsigned i = 0;
 
         assert(m);
@@ -4984,9 +5105,6 @@ int bus_message_parse_fields(sd_bus_message *m) {
         if (m->n_fds != unix_fds)
                 return -EBADMSG;
 
-        if (isempty(m->root_container.signature) != (BUS_MESSAGE_BODY_SIZE(m) == 0))
-                return -EBADMSG;
-
         switch (m->header->type) {
 
         case SD_BUS_MESSAGE_SIGNAL:
@@ -5014,6 +5132,14 @@ int bus_message_parse_fields(sd_bus_message *m) {
                 break;
         }
 
+        /* Refuse non-local messages that claim they are local */
+        if (streq_ptr(m->path, "/org/freedesktop/DBus/Local"))
+                return -EBADMSG;
+        if (streq_ptr(m->interface, "org.freedesktop.DBus.Local"))
+                return -EBADMSG;
+        if (streq_ptr(m->sender, "org.freedesktop.DBus.Local"))
+                return -EBADMSG;
+
         m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
 
         if (BUS_MESSAGE_IS_GVARIANT(m)) {
@@ -5035,112 +5161,6 @@ int bus_message_parse_fields(sd_bus_message *m) {
         return 0;
 }
 
-static int bus_message_close_header(sd_bus_message *m) {
-        uint8_t *a;
-        size_t sz, i;
-
-        assert(m);
-
-        if (!BUS_MESSAGE_IS_GVARIANT(m))
-                return 0;
-
-        if (m->n_header_offsets < 1)
-                return 0;
-
-        assert(m->header->fields_size == m->header_offsets[m->n_header_offsets-1]);
-
-        sz = determine_word_size(m->header->fields_size, m->n_header_offsets);
-
-        a = message_extend_fields(m, 1, sz * m->n_header_offsets, false);
-        if (!a)
-                return -ENOMEM;
-
-        for (i = 0; i < m->n_header_offsets; i++)
-                write_word_le(a + sz*i, sz, m->header_offsets[i]);
-
-        return 0;
-}
-
-int bus_message_seal(sd_bus_message *m, uint64_t serial) {
-        struct bus_body_part *part;
-        size_t l, a;
-        unsigned i;
-        int r;
-
-        assert(m);
-
-        if (m->sealed)
-                return -EPERM;
-
-        if (m->n_containers > 0)
-                return -EBADMSG;
-
-        if (m->poisoned)
-                return -ESTALE;
-
-        /* In vtables the return signature of method calls is listed,
-         * let's check if they match if this is a response */
-        if (m->header->type == SD_BUS_MESSAGE_METHOD_RETURN &&
-            m->enforced_reply_signature &&
-            !streq(strempty(m->root_container.signature), m->enforced_reply_signature))
-                return -ENOMSG;
-
-        /* If gvariant marshalling is used we need to close the body structure */
-        r = bus_message_close_struct(m, &m->root_container, false);
-        if (r < 0)
-                return r;
-
-        /* If there's a non-trivial signature set, then add it in here */
-        if (!isempty(m->root_container.signature)) {
-                r = message_append_field_signature(m, BUS_MESSAGE_HEADER_SIGNATURE, m->root_container.signature, NULL);
-                if (r < 0)
-                        return r;
-        }
-
-        if (m->n_fds > 0) {
-                r = message_append_field_uint32(m, BUS_MESSAGE_HEADER_UNIX_FDS, m->n_fds);
-                if (r < 0)
-                        return r;
-        }
-
-        r = bus_message_close_header(m);
-        if (r < 0)
-                return r;
-
-        m->header->serial = serial;
-
-        /* Add padding at the end of the fields part, since we know
-         * the body needs to start at an 8 byte alignment. We made
-         * sure we allocated enough space for this, so all we need to
-         * do here is to zero it out. */
-        l = BUS_MESSAGE_FIELDS_SIZE(m);
-        a = ALIGN8(l) - l;
-        if (a > 0)
-                memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
-
-        /* If this is something we can send as memfd, then let's seal
-        the memfd now. Note that we can send memfds as payload only
-        for directed messages, and not for broadcasts. */
-        if (m->destination && m->bus && m->bus->use_memfd) {
-                MESSAGE_FOREACH_PART(part, i, m)
-                        if (part->memfd >= 0 && !part->sealed && (part->size > MEMFD_MIN_SIZE || m->bus->use_memfd < 0)) {
-                                bus_body_part_unmap(part);
-
-                                if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
-                                        part->sealed = true;
-                        }
-        }
-
-        m->root_container.end = BUS_MESSAGE_BODY_SIZE(m);
-        m->root_container.index = 0;
-        m->root_container.offset_index = 0;
-        m->root_container.item_size = m->root_container.n_offsets > 0 ? m->root_container.offsets[0] : 0;
-
-        m->sealed = true;
-
-        return 0;
-}
-
 _public_ int sd_bus_message_set_destination(sd_bus_message *m, const char *destination) {
         assert_return(m, -EINVAL);
         assert_return(destination, -EINVAL);
@@ -5307,7 +5327,7 @@ _public_ const char* sd_bus_message_get_signature(sd_bus_message *m, int complet
         assert_return(m, NULL);
 
         c = complete ? &m->root_container : message_get_container(m);
-        return c->signature ?: "";
+        return strempty(c->signature);
 }
 
 _public_ int sd_bus_message_copy(sd_bus_message *m, sd_bus_message *source, int all) {
@@ -5419,3 +5439,97 @@ _public_ sd_bus *sd_bus_message_get_bus(sd_bus_message *m) {
 
         return m->bus;
 }
+
+int bus_message_remarshal(sd_bus *bus, sd_bus_message **m) {
+        _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
+        usec_t timeout;
+        int r;
+
+        assert(bus);
+        assert(m);
+        assert(*m);
+
+        switch ((*m)->header->type) {
+
+        case SD_BUS_MESSAGE_SIGNAL:
+                r = sd_bus_message_new_signal(bus, (*m)->path, (*m)->interface, (*m)->member, &n);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case SD_BUS_MESSAGE_METHOD_CALL:
+                r = sd_bus_message_new_method_call(bus, (*m)->destination, (*m)->path, (*m)->interface, (*m)->member, &n);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case SD_BUS_MESSAGE_METHOD_RETURN:
+        case SD_BUS_MESSAGE_METHOD_ERROR:
+
+                n = message_new(bus, (*m)->header->type);
+                if (!n)
+                        return -ENOMEM;
+
+                n->reply_serial = (*m)->reply_serial;
+                r = message_append_field_uint32(n, BUS_MESSAGE_HEADER_REPLY_SERIAL, n->reply_serial);
+                if (r < 0)
+                        return r;
+
+                if ((*m)->header->type == SD_BUS_MESSAGE_METHOD_ERROR && (*m)->error.name) {
+                        r = message_append_field_string(n, BUS_MESSAGE_HEADER_ERROR_NAME, SD_BUS_TYPE_STRING, (*m)->error.name, &n->error.message);
+                        if (r < 0)
+                                return r;
+
+                        n->error._need_free = -1;
+                }
+
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        if ((*m)->destination && !n->destination) {
+                r = message_append_field_string(n, BUS_MESSAGE_HEADER_DESTINATION, SD_BUS_TYPE_STRING, (*m)->destination, &n->destination);
+                if (r < 0)
+                        return r;
+        }
+
+        if ((*m)->sender && !n->sender) {
+                r = message_append_field_string(n, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, (*m)->sender, &n->sender);
+                if (r < 0)
+                        return r;
+        }
+
+        n->header->flags |= (*m)->header->flags & (BUS_MESSAGE_NO_REPLY_EXPECTED|BUS_MESSAGE_NO_AUTO_START);
+
+        r = sd_bus_message_copy(n, *m, true);
+        if (r < 0)
+                return r;
+
+        timeout = (*m)->timeout;
+        if (timeout == 0 && !((*m)->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED))
+                timeout = BUS_DEFAULT_TIMEOUT;
+
+        r = bus_message_seal(n, (*m)->header->serial, timeout);
+        if (r < 0)
+                return r;
+
+        sd_bus_message_unref(*m);
+        *m = n;
+        n = NULL;
+
+        return 0;
+}
+
+int bus_message_append_sender(sd_bus_message *m, const char *sender) {
+        assert(m);
+        assert(sender);
+
+        assert_return(!m->sealed, -EPERM);
+        assert_return(!m->sender, -EPERM);
+
+        return message_append_field_string(m, BUS_MESSAGE_HEADER_SENDER, SD_BUS_TYPE_STRING, sender, &m->sender);
+}