chiark / gitweb /
bus: don't calculate kmsg message too large
[elogind.git] / src / libsystemd-bus / bus-kernel.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #ifdef HAVE_VALGRIND_MEMCHECK_H
23 #include <valgrind/memcheck.h>
24 #endif
25
26 #include <fcntl.h>
27
28 #include "util.h"
29
30 #include "bus-internal.h"
31 #include "bus-message.h"
32 #include "bus-kernel.h"
33
34 #define KDBUS_MSG_FOREACH_DATA(d, k)                                    \
35         for ((d) = (k)->data;                                           \
36              (uint8_t*) (d) < (uint8_t*) (k) + (k)->size;               \
37              (d) = (struct kdbus_msg_data*) ((uint8_t*) (d) + ALIGN8((d)->size)))
38
39 static int parse_unique_name(const char *s, uint64_t *id) {
40         int r;
41
42         assert(s);
43         assert(id);
44
45         if (!startswith(s, ":1."))
46                 return 0;
47
48         r = safe_atou64(s + 3, id);
49         if (r < 0)
50                 return r;
51
52         return 1;
53 }
54
55 static void append_payload_vec(struct kdbus_msg_data **d, const void *p, size_t sz) {
56         assert(d);
57         assert(p);
58         assert(sz > 0);
59
60         *d = ALIGN8_PTR(*d);
61
62         (*d)->size = offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec);
63         (*d)->type = KDBUS_MSG_PAYLOAD_VEC;
64         (*d)->vec.address = (uint64_t) p;
65         (*d)->vec.size = sz;
66
67         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + (*d)->size);
68 }
69
70 static void append_destination(struct kdbus_msg_data **d, const char *s, size_t length) {
71         assert(d);
72         assert(d);
73
74         *d = ALIGN8_PTR(*d);
75
76         (*d)->size = offsetof(struct kdbus_msg_data, str) + length + 1;
77         (*d)->type = KDBUS_MSG_DST_NAME;
78         memcpy((*d)->str, s, length + 1);
79
80         *d = (struct kdbus_msg_data*) ((uint8_t*) *d + (*d)->size);
81 }
82
83 static int bus_message_setup_kmsg(sd_bus_message *m) {
84         struct kdbus_msg_data *d;
85         bool well_known;
86         uint64_t unique;
87         size_t sz, dl;
88         int r;
89
90         assert(m);
91         assert(m->sealed);
92
93         if (m->kdbus)
94                 return 0;
95
96         if (m->destination) {
97                 r = parse_unique_name(m->destination, &unique);
98                 if (r < 0)
99                         return r;
100
101                 well_known = r == 0;
102         } else
103                 well_known = false;
104
105         sz = offsetof(struct kdbus_msg, data);
106
107         /* Add in fixed header, fields header, fields header padding and payload */
108         sz += 4 * ALIGN8(offsetof(struct kdbus_msg_data, vec) + sizeof(struct kdbus_vec));
109
110         /* Add in well-known destination header */
111         if (well_known) {
112                 dl = strlen(m->destination);
113                 sz += ALIGN8(offsetof(struct kdbus_msg, data) + dl + 1);
114         }
115
116         m->kdbus = aligned_alloc(8, sz);
117         if (!m->kdbus)
118                 return -ENOMEM;
119
120         m->kdbus->flags =
121                 ((m->header->flags & SD_BUS_MESSAGE_NO_REPLY_EXPECTED) ? 0 : KDBUS_MSG_FLAGS_EXPECT_REPLY) |
122                 ((m->header->flags & SD_BUS_MESSAGE_NO_AUTO_START) ? KDBUS_MSG_FLAGS_NO_AUTO_START : 0);
123         m->kdbus->dst_id =
124                 well_known ? 0 :
125                 m->destination ? unique : (uint64_t) -1;
126         m->kdbus->payload_type = KDBUS_PAYLOAD_DBUS1;
127         m->kdbus->cookie = m->header->serial;
128
129         m->kdbus->timeout_ns = m->timeout * NSEC_PER_USEC;
130
131         d = m->kdbus->data;
132
133         if (well_known)
134                 append_destination(&d, m->destination, dl);
135
136         append_payload_vec(&d, m->header, sizeof(*m->header));
137
138         if (m->fields) {
139                 append_payload_vec(&d, m->fields, m->header->fields_size);
140
141                 if (m->header->fields_size % 8 != 0) {
142                         static const uint8_t padding[7] = {};
143
144                         append_payload_vec(&d, padding, 8 - (m->header->fields_size % 8));
145                 }
146         }
147
148         if (m->body)
149                 append_payload_vec(&d, m->body, m->header->body_size);
150
151         m->kdbus->size = (uint8_t*) d - (uint8_t*) m->kdbus;
152         assert(m->kdbus->size <= sz);
153
154         m->free_kdbus = true;
155
156         return 0;
157 }
158
159 int bus_kernel_take_fd(sd_bus *b) {
160         struct kdbus_cmd_hello hello = {};
161         int r;
162
163         assert(b);
164
165         if (b->is_server)
166                 return -EINVAL;
167
168         r = ioctl(b->input_fd, KDBUS_CMD_HELLO, &hello);
169         if (r < 0)
170                 return -errno;
171
172         if (asprintf(&b->unique_name, ":1.%llu", (unsigned long long) hello.id) < 0)
173                 return -ENOMEM;
174
175         b->is_kernel = true;
176         b->bus_client = true;
177
178         r = bus_start_running(b);
179         if (r < 0)
180                 return r;
181
182         return 1;
183 }
184
185 int bus_kernel_connect(sd_bus *b) {
186         assert(b);
187         assert(b->input_fd < 0);
188         assert(b->output_fd < 0);
189         assert(b->kernel);
190
191         if (b->is_server)
192                 return -EINVAL;
193
194         b->input_fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
195         if (b->input_fd < 0)
196                 return -errno;
197
198         b->output_fd = b->input_fd;
199
200         return bus_kernel_take_fd(b);
201 }
202
203 int bus_kernel_write_message(sd_bus *bus, sd_bus_message *m) {
204         int r;
205
206         assert(bus);
207         assert(m);
208         assert(bus->state == BUS_RUNNING);
209
210         r = bus_message_setup_kmsg(m);
211         if (r < 0)
212                 return r;
213
214         r = ioctl(bus->output_fd, KDBUS_CMD_MSG_SEND, m->kdbus);
215         if (r < 0)
216                 return errno == EAGAIN ? 0 : -errno;
217
218         return 1;
219 }
220
221 static void close_kdbus_msg(struct kdbus_msg *k) {
222         struct kdbus_msg_data *d;
223
224         KDBUS_MSG_FOREACH_DATA(d, k) {
225
226                 if (d->type != KDBUS_MSG_UNIX_FDS)
227                         continue;
228
229                 close_many(d->fds, (d->size - offsetof(struct kdbus_msg_data, fds)) / sizeof(int));
230         }
231 }
232
233 static int bus_kernel_make_message(sd_bus *bus, struct kdbus_msg *k, sd_bus_message **ret) {
234         sd_bus_message *m = NULL;
235         struct kdbus_msg_data *d;
236         unsigned n_payload = 0, n_fds = 0;
237         _cleanup_free_ int *fds = NULL;
238         struct bus_header *h = NULL;
239         size_t total, n_bytes = 0, idx = 0;
240         struct kdbus_creds *creds = NULL;
241         uint64_t nsec = 0;
242         const char *destination = NULL;
243         int r;
244
245         assert(bus);
246         assert(k);
247         assert(ret);
248
249         if (k->payload_type != KDBUS_PAYLOAD_DBUS1)
250                 return 0;
251
252         KDBUS_MSG_FOREACH_DATA(d, k) {
253                 size_t l;
254
255                 l = d->size - offsetof(struct kdbus_msg_data, data);
256
257                 if (d->type == KDBUS_MSG_PAYLOAD) {
258
259                         if (!h) {
260                                 if (l < sizeof(struct bus_header))
261                                         return -EBADMSG;
262
263                                 h = (struct bus_header*) d->data;
264                         }
265
266                         n_payload++;
267                         n_bytes += l;
268
269                 } else if (d->type == KDBUS_MSG_UNIX_FDS) {
270                         int *f;
271                         unsigned j;
272
273                         j = l / sizeof(int);
274                         f = realloc(fds, sizeof(int) * (n_fds + j));
275                         if (!f)
276                                 return -ENOMEM;
277
278                         fds = f;
279                         memcpy(fds + n_fds, d->fds, j);
280                         n_fds += j;
281
282                 } else if (d->type == KDBUS_MSG_SRC_CREDS)
283                         creds = &d->creds;
284                 else if (d->type == KDBUS_MSG_TIMESTAMP)
285                         nsec = d->ts_ns;
286                 else if (d->type == KDBUS_MSG_DST_NAME)
287                         destination = d->str;
288         }
289
290         if (!h)
291                 return -EBADMSG;
292
293         r = bus_header_size(h, &total);
294         if (r < 0)
295                 return r;
296
297         if (n_bytes != total)
298                 return -EBADMSG;
299
300         r = bus_message_from_header(h, sizeof(struct bus_header), fds, n_fds, NULL, NULL, 0, &m);
301         if (r < 0)
302                 return r;
303
304         KDBUS_MSG_FOREACH_DATA(d, k) {
305                 size_t l;
306
307                 if (d->type != KDBUS_MSG_PAYLOAD)
308                         continue;
309
310                 l = d->size - offsetof(struct kdbus_msg_data, data);
311                 if (idx == sizeof(struct bus_header) &&
312                     l == BUS_MESSAGE_FIELDS_SIZE(m))
313                         m->fields = d->data;
314                 else if (idx == sizeof(struct bus_header) + ALIGN8(BUS_MESSAGE_FIELDS_SIZE(m)) &&
315                          l == BUS_MESSAGE_BODY_SIZE(m))
316                         m->body = d->data;
317                 else if (!(idx == 0 && l == sizeof(struct bus_header)) &&
318                          !(idx == sizeof(struct bus_header) + BUS_MESSAGE_FIELDS_SIZE(m))) {
319                         sd_bus_message_unref(m);
320                         return -EBADMSG;
321                 }
322
323                 idx += l;
324         }
325
326         if (creds) {
327                 m->pid_starttime = creds->starttime / NSEC_PER_USEC;
328                 m->uid = creds->uid;
329                 m->gid = creds->gid;
330                 m->pid = creds->pid;
331                 m->tid = creds->tid;
332                 m->uid_valid = m->gid_valid = true;
333         }
334
335         m->timestamp = nsec / NSEC_PER_USEC;
336
337         r = bus_message_parse_fields(m);
338         if (r < 0) {
339                 sd_bus_message_unref(m);
340                 return r;
341         }
342
343         if (k->src_id == KDBUS_SRC_ID_KERNEL)
344                 m->sender = "org.freedesktop.DBus";
345         else {
346                 snprintf(m->sender_buffer, sizeof(m->sender_buffer), ":1.%llu", (unsigned long long) k->src_id);
347                 m->sender = m->sender_buffer;
348         }
349
350         if (!m->destination) {
351                 if (destination)
352                         m->destination = destination;
353                 else if (k->dst_id != KDBUS_DST_ID_WELL_KNOWN_NAME &&
354                          k->dst_id != KDBUS_DST_ID_BROADCAST) {
355                         snprintf(m->destination_buffer, sizeof(m->destination_buffer), ":1.%llu", (unsigned long long) k->dst_id);
356                         m->destination = m->destination_buffer;
357                 }
358         }
359
360         /* We take possession of the kmsg struct now */
361         m->kdbus = k;
362         m->free_kdbus = true;
363         m->free_fds = true;
364
365         fds = NULL;
366
367         *ret = m;
368         return 1;
369 }
370
371 int bus_kernel_read_message(sd_bus *bus, sd_bus_message **m) {
372         struct kdbus_msg *k;
373         size_t sz = 1024;
374         int r;
375
376         assert(bus);
377         assert(m);
378
379         for (;;) {
380                 void *q;
381
382                 q = aligned_alloc(8, sz);
383                 if (!q)
384                         return -errno;
385
386                 free(bus->rbuffer);
387                 k = bus->rbuffer = q;
388                 k->size = sz;
389
390                 /* Let's tell valgrind that there's really no need to
391                  * initialize this fully. This should be removed again
392                  * when valgrind learned the kdbus ioctls natively. */
393 #ifdef HAVE_VALGRIND_MEMCHECK_H
394                 VALGRIND_MAKE_MEM_DEFINED(k, sz);
395 #endif
396
397                 r = ioctl(bus->input_fd, KDBUS_CMD_MSG_RECV, bus->rbuffer);
398                 if (r >= 0)
399                         break;
400
401                 if (errno == EAGAIN)
402                         return 0;
403
404                 if (errno != ENOBUFS)
405                         return -errno;
406
407                 sz *= 2;
408         }
409
410         r = bus_kernel_make_message(bus, k, m);
411         if (r > 0)
412                 bus->rbuffer = NULL;
413         else
414                 close_kdbus_msg(k);
415
416         return r < 0 ? r : 1;
417 }
418
419 int bus_kernel_create(const char *name, char **s) {
420         struct kdbus_cmd_fname *fname;
421         size_t l;
422         int fd;
423         char *p;
424
425         assert(name);
426         assert(s);
427
428         fd = open("/dev/kdbus/control", O_RDWR|O_NOCTTY|O_CLOEXEC);
429         if (fd < 0)
430                 return -errno;
431
432         l = strlen(name);
433         fname = alloca(offsetof(struct kdbus_cmd_fname, name) + DECIMAL_STR_MAX(uid_t) + 1 + l + 1);
434         sprintf(fname->name, "%lu-%s", (unsigned long) getuid(), name);
435         fname->size = offsetof(struct kdbus_cmd_fname, name) + strlen(fname->name) + 1;
436         fname->kernel_flags = KDBUS_CMD_FNAME_ACCESS_WORLD | KDBUS_CMD_FNAME_POLICY_OPEN;
437         fname->user_flags = 0;
438
439         p = strjoin("/dev/kdbus/", fname->name, "/bus", NULL);
440         if (!p)
441                 return -ENOMEM;
442
443         if (ioctl(fd, KDBUS_CMD_BUS_MAKE, fname) < 0) {
444                 close_nointr_nofail(fd);
445                 free(p);
446                 return -errno;
447         }
448
449         if (s)
450                 *s = p;
451
452         return fd;
453 }