chiark / gitweb /
bus: add new sd_bus_creds object to encapsulate process credentials
[elogind.git] / src / libsystemd-bus / bus-control.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 <stddef.h>
27 #include <errno.h>
28
29 #include "strv.h"
30
31 #include "sd-bus.h"
32 #include "bus-internal.h"
33 #include "bus-message.h"
34 #include "bus-control.h"
35 #include "bus-bloom.h"
36 #include "bus-util.h"
37
38 _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) {
39         int r;
40
41         if (!bus)
42                 return -EINVAL;
43         if (!unique)
44                 return -EINVAL;
45         if (bus_pid_changed(bus))
46                 return -ECHILD;
47
48         r = bus_ensure_running(bus);
49         if (r < 0)
50                 return r;
51
52         *unique = bus->unique_name;
53         return 0;
54 }
55
56 _public_ int sd_bus_request_name(sd_bus *bus, const char *name, int flags) {
57         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
58         uint32_t ret;
59         int r;
60
61         if (!bus)
62                 return -EINVAL;
63         if (!name)
64                 return -EINVAL;
65         if (!bus->bus_client)
66                 return -EINVAL;
67         if (!BUS_IS_OPEN(bus->state))
68                 return -ENOTCONN;
69         if (bus_pid_changed(bus))
70                 return -ECHILD;
71
72         if (bus->is_kernel) {
73                 struct kdbus_cmd_name *n;
74                 size_t l;
75
76                 l = strlen(name);
77                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
78                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
79                 n->flags = flags;
80                 memcpy(n->name, name, l+1);
81
82 #ifdef HAVE_VALGRIND_MEMCHECK_H
83                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
84 #endif
85
86                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_ACQUIRE, n);
87                 if (r < 0)
88                         return -errno;
89
90                 return n->flags;
91         } else {
92                 r = sd_bus_call_method(
93                                 bus,
94                                 "org.freedesktop.DBus",
95                                 "/",
96                                 "org.freedesktop.DBus",
97                                 "RequestName",
98                                 NULL,
99                                 &reply,
100                                 "su",
101                                 name,
102                                 flags);
103                 if (r < 0)
104                         return r;
105
106                 r = sd_bus_message_read(reply, "u", &ret);
107                 if (r < 0)
108                         return r;
109
110                 return ret;
111         }
112 }
113
114 _public_ int sd_bus_release_name(sd_bus *bus, const char *name) {
115         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
116         uint32_t ret;
117         int r;
118
119         if (!bus)
120                 return -EINVAL;
121         if (!name)
122                 return -EINVAL;
123         if (!bus->bus_client)
124                 return -EINVAL;
125         if (!BUS_IS_OPEN(bus->state))
126                 return -ENOTCONN;
127         if (bus_pid_changed(bus))
128                 return -ECHILD;
129
130         if (bus->is_kernel) {
131                 struct kdbus_cmd_name *n;
132                 size_t l;
133
134                 l = strlen(name);
135                 n = alloca0(offsetof(struct kdbus_cmd_name, name) + l + 1);
136                 n->size = offsetof(struct kdbus_cmd_name, name) + l + 1;
137                 memcpy(n->name, name, l+1);
138
139 #ifdef HAVE_VALGRIND_MEMCHECK_H
140                 VALGRIND_MAKE_MEM_DEFINED(n, n->size);
141 #endif
142                 r = ioctl(bus->input_fd, KDBUS_CMD_NAME_RELEASE, n);
143                 if (r < 0)
144                         return -errno;
145
146                 return n->flags;
147         } else {
148                 r = sd_bus_call_method(
149                                 bus,
150                                 "org.freedesktop.DBus",
151                                 "/",
152                                 "org.freedesktop.DBus",
153                                 "ReleaseName",
154                                 NULL,
155                                 &reply,
156                                 "s",
157                                 name);
158                 if (r < 0)
159                         return r;
160
161                 r = sd_bus_message_read(reply, "u", &ret);
162                 if (r < 0)
163                         return r;
164         }
165
166         return ret;
167 }
168
169 _public_ int sd_bus_list_names(sd_bus *bus, char ***l) {
170         _cleanup_bus_message_unref_ sd_bus_message *reply1 = NULL, *reply2 = NULL;
171         char **x = NULL;
172         int r;
173
174         if (!bus)
175                 return -EINVAL;
176         if (!l)
177                 return -EINVAL;
178         if (!BUS_IS_OPEN(bus->state))
179                 return -ENOTCONN;
180         if (bus_pid_changed(bus))
181                 return -ECHILD;
182
183         r = sd_bus_call_method(
184                         bus,
185                         "org.freedesktop.DBus",
186                         "/",
187                         "org.freedesktop.DBus",
188                         "ListNames",
189                         NULL,
190                         &reply1,
191                         NULL);
192         if (r < 0)
193                 return r;
194
195         r = sd_bus_call_method(
196                         bus,
197                         "org.freedesktop.DBus",
198                         "/",
199                         "org.freedesktop.DBus",
200                         "ListActivatableNames",
201                         NULL,
202                         &reply2,
203                         NULL);
204         if (r < 0)
205                 return r;
206
207         r = bus_message_read_strv_extend(reply1, &x);
208         if (r < 0) {
209                 strv_free(x);
210                 return r;
211         }
212
213         r = bus_message_read_strv_extend(reply2, &x);
214         if (r < 0) {
215                 strv_free(x);
216                 return r;
217         }
218
219         *l = strv_uniq(x);
220         return 0;
221 }
222
223 _public_ int sd_bus_get_owner(sd_bus *bus, const char *name, char **owner) {
224         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
225         const char *found;
226         int r;
227
228         assert_return(bus, -EINVAL);
229         assert_return(name, -EINVAL);
230         assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
231         assert_return(!bus_pid_changed(bus), -ECHILD);
232
233         r = sd_bus_call_method(
234                         bus,
235                         "org.freedesktop.DBus",
236                         "/",
237                         "org.freedesktop.DBus",
238                         "GetNameOwner",
239                         NULL,
240                         &reply,
241                         "s",
242                         name);
243         if (r < 0)
244                 return r;
245
246         r = sd_bus_message_read(reply, "s", &found);
247         if (r < 0)
248                 return r;
249
250         if (owner) {
251                 char *t;
252
253                 t = strdup(found);
254                 if (!t)
255                         return -ENOMEM;
256
257                 *owner = t;
258         }
259
260         return 0;
261 }
262
263 _public_ int sd_bus_get_owner_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds) {
264         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
265         _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
266         pid_t pid = 0;
267         int r;
268
269         assert_return(bus, -EINVAL);
270         assert_return(name, -EINVAL);
271         assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
272         assert_return(creds, -EINVAL);
273         assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
274         assert_return(!bus_pid_changed(bus), -ECHILD);
275
276         c = bus_creds_new();
277         if (!c)
278                 return -ENOMEM;
279
280         if ((mask & SD_BUS_CREDS_PID) ||
281             mask & ~(SD_BUS_CREDS_PID|SD_BUS_CREDS_UID|SD_BUS_CREDS_SELINUX_CONTEXT)) {
282                 uint32_t u;
283
284                 r = sd_bus_call_method(
285                                 bus,
286                                 "org.freedesktop.DBus",
287                                 "/",
288                                 "org.freedesktop.DBus",
289                                 "GetConnectionUnixProcessID",
290                                 NULL,
291                                 &reply,
292                                 "s",
293                                 name);
294                 if (r < 0)
295                         return r;
296
297                 r = sd_bus_message_read(reply, "u", &u);
298                 if (r < 0)
299                         return r;
300
301                 pid = u;
302                 if (mask & SD_BUS_CREDS_PID) {
303                         c->pid = u;
304                         c->mask |= SD_BUS_CREDS_PID;
305                 }
306
307                 reply = sd_bus_message_unref(reply);
308         }
309
310         if (mask & SD_BUS_CREDS_UID) {
311                 uint32_t u;
312
313                 r = sd_bus_call_method(
314                                 bus,
315                                 "org.freedesktop.DBus",
316                                 "/",
317                                 "org.freedesktop.DBus",
318                                 "GetConnectionUnixUser",
319                                 NULL,
320                                 &reply,
321                                 "s",
322                                 name);
323                 if (r < 0)
324                         return r;
325
326                 r = sd_bus_message_read(reply, "u", &u);
327                 if (r < 0)
328                         return r;
329
330                 c->uid = u;
331                 c->mask |= SD_BUS_CREDS_UID;
332
333                 reply = sd_bus_message_unref(reply);
334         }
335
336         if (mask & SD_BUS_CREDS_SELINUX_CONTEXT) {
337                 const void *p;
338                 size_t sz;
339
340                 r = sd_bus_call_method(
341                                 bus,
342                                 "org.freedesktop.DBus",
343                                 "/",
344                                 "org.freedesktop.DBus",
345                                 "GetConnectionSELinuxSecurityContext",
346                                 NULL,
347                                 &reply,
348                                 "s",
349                                 name);
350                 if (r < 0)
351                         return r;
352
353                 r = sd_bus_message_read_array(reply, 'y', &p, &sz);
354                 if (r < 0)
355                         return r;
356
357                 c->label = strndup(p, sz);
358                 if (!c->label)
359                         return -ENOMEM;
360
361                 c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
362         }
363
364         r = bus_creds_add_more(c, mask, pid, 0);
365         if (r < 0)
366                 return r;
367
368         *creds = c;
369         c = NULL;
370
371         return 0;
372 }
373
374 int bus_add_match_internal(
375                 sd_bus *bus,
376                 const char *match,
377                 struct bus_match_component *components,
378                 unsigned n_components,
379                 uint64_t cookie) {
380
381         int r;
382
383         assert(bus);
384         assert(match);
385
386         if (bus->is_kernel) {
387                 struct kdbus_cmd_match *m;
388                 struct kdbus_item *item;
389                 uint64_t bloom[BLOOM_SIZE/8];
390                 size_t sz;
391                 const char *sender = NULL;
392                 size_t sender_length = 0;
393                 uint64_t src_id = KDBUS_MATCH_SRC_ID_ANY;
394                 bool using_bloom = false;
395                 unsigned i;
396
397                 zero(bloom);
398
399                 sz = offsetof(struct kdbus_cmd_match, items);
400
401                 for (i = 0; i < n_components; i++) {
402                         struct bus_match_component *c = &components[i];
403
404                         switch (c->type) {
405
406                         case BUS_MATCH_SENDER:
407                                 r = bus_kernel_parse_unique_name(c->value_str, &src_id);
408                                 if (r < 0)
409                                         return r;
410
411                                 if (r > 0) {
412                                         sender = c->value_str;
413                                         sender_length = strlen(sender);
414                                         sz += ALIGN8(offsetof(struct kdbus_item, str) + sender_length + 1);
415                                 }
416
417                                 break;
418
419                         case BUS_MATCH_MESSAGE_TYPE:
420                                 bloom_add_pair(bloom, "message-type", bus_message_type_to_string(c->value_u8));
421                                 using_bloom = true;
422                                 break;
423
424                         case BUS_MATCH_INTERFACE:
425                                 bloom_add_pair(bloom, "interface", c->value_str);
426                                 using_bloom = true;
427                                 break;
428
429                         case BUS_MATCH_MEMBER:
430                                 bloom_add_pair(bloom, "member", c->value_str);
431                                 using_bloom = true;
432                                 break;
433
434                         case BUS_MATCH_PATH:
435                                 bloom_add_pair(bloom, "path", c->value_str);
436                                 using_bloom = true;
437                                 break;
438
439                         case BUS_MATCH_PATH_NAMESPACE:
440                                 if (!streq(c->value_str, "/")) {
441                                         bloom_add_pair(bloom, "path-slash-prefix", c->value_str);
442                                         using_bloom = true;
443                                 }
444                                 break;
445
446                         case BUS_MATCH_ARG...BUS_MATCH_ARG_LAST: {
447                                 char buf[sizeof("arg")-1 + 2 + 1];
448
449                                 snprintf(buf, sizeof(buf), "arg%u", c->type - BUS_MATCH_ARG);
450                                 bloom_add_pair(bloom, buf, c->value_str);
451                                 using_bloom = true;
452                                 break;
453                         }
454
455                         case BUS_MATCH_ARG_PATH...BUS_MATCH_ARG_PATH_LAST: {
456                                 char buf[sizeof("arg")-1 + 2 + sizeof("-slash-prefix")];
457
458                                 snprintf(buf, sizeof(buf), "arg%u-slash-prefix", c->type - BUS_MATCH_ARG_PATH);
459                                 bloom_add_pair(bloom, buf, c->value_str);
460                                 using_bloom = true;
461                                 break;
462                         }
463
464                         case BUS_MATCH_ARG_NAMESPACE...BUS_MATCH_ARG_NAMESPACE_LAST: {
465                                 char buf[sizeof("arg")-1 + 2 + sizeof("-dot-prefix")];
466
467                                 snprintf(buf, sizeof(buf), "arg%u-dot-prefix", c->type - BUS_MATCH_ARG_NAMESPACE);
468                                 bloom_add_pair(bloom, buf, c->value_str);
469                                 using_bloom = true;
470                                 break;
471                         }
472
473                         case BUS_MATCH_DESTINATION:
474                                 /* The bloom filter does not include
475                                    the destination, since it is only
476                                    available for broadcast messages
477                                    which do not carry a destination
478                                    since they are undirected. */
479                                 break;
480
481                         case BUS_MATCH_ROOT:
482                         case BUS_MATCH_VALUE:
483                         case BUS_MATCH_LEAF:
484                         case _BUS_MATCH_NODE_TYPE_MAX:
485                         case _BUS_MATCH_NODE_TYPE_INVALID:
486                                 assert_not_reached("Invalid match type?");
487                         }
488                 }
489
490                 if (using_bloom)
491                         sz += ALIGN8(offsetof(struct kdbus_item, data64) + BLOOM_SIZE);
492
493                 m = alloca0(sz);
494                 m->size = sz;
495                 m->cookie = cookie;
496                 m->src_id = src_id;
497
498                 item = m->items;
499
500                 if (using_bloom) {
501                         item->size = offsetof(struct kdbus_item, data64) + BLOOM_SIZE;
502                         item->type = KDBUS_MATCH_BLOOM;
503                         memcpy(item->data64, bloom, BLOOM_SIZE);
504
505                         item = KDBUS_ITEM_NEXT(item);
506                 }
507
508                 if (sender) {
509                         item->size = offsetof(struct kdbus_item, str) + sender_length + 1;
510                         item->type = KDBUS_MATCH_SRC_NAME;
511                         memcpy(item->str, sender, sender_length + 1);
512                 }
513
514                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_ADD, m);
515                 if (r < 0)
516                         return -errno;
517
518         } else {
519                 return sd_bus_call_method(
520                                 bus,
521                                 "org.freedesktop.DBus",
522                                 "/",
523                                 "org.freedesktop.DBus",
524                                 "AddMatch",
525                                 NULL,
526                                 NULL,
527                                 "s",
528                                 match);
529         }
530
531         return 0;
532 }
533
534 int bus_remove_match_internal(
535                 sd_bus *bus,
536                 const char *match,
537                 uint64_t cookie) {
538
539         int r;
540
541         assert(bus);
542         assert(match);
543
544         if (bus->is_kernel) {
545                 struct kdbus_cmd_match m;
546
547                 zero(m);
548                 m.size = offsetof(struct kdbus_cmd_match, items);
549                 m.cookie = cookie;
550
551                 r = ioctl(bus->input_fd, KDBUS_CMD_MATCH_REMOVE, &m);
552                 if (r < 0)
553                         return -errno;
554
555         } else {
556                 return sd_bus_call_method(
557                                 bus,
558                                 "org.freedesktop.DBus",
559                                 "/",
560                                 "org.freedesktop.DBus",
561                                 "RemoveMatch",
562                                 NULL,
563                                 NULL,
564                                 "s",
565                                 match);
566         }
567
568         return 0;
569 }
570
571 _public_ int sd_bus_get_owner_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine) {
572         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
573         const char *mid;
574         int r;
575
576         assert_return(bus, -EINVAL);
577         assert_return(name, -EINVAL);
578         assert_return(machine, -EINVAL);
579         assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
580         assert_return(!bus_pid_changed(bus), -ECHILD);
581
582         if (streq_ptr(name, bus->unique_name))
583                 return sd_id128_get_machine(machine);
584
585         r = sd_bus_message_new_method_call(
586                         bus,
587                         name,
588                         "/",
589                         "org.freedesktop.DBus.Peer",
590                         "GetMachineId", &m);
591         if (r < 0)
592                 return r;
593
594         r = sd_bus_message_set_no_auto_start(m, true);
595         if (r < 0)
596                 return r;
597
598         r = sd_bus_call(bus, m, 0, NULL, &reply);
599         if (r < 0)
600                 return r;
601
602         r = sd_bus_message_read(reply, "s", &mid);
603         if (r < 0)
604                 return r;
605
606         return sd_id128_from_string(mid, machine);
607 }