1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
7 Copyright 2013 Daniel Mack
8 Copyright 2014 Kay Sievers
10 systemd is free software; you can redistribute it and/or modify it
11 under the terms of the GNU Lesser General Public License as published by
12 the Free Software Foundation; either version 2.1 of the License, or
13 (at your option) any later version.
15 systemd is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public License
21 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
33 #include "bus-internal.h"
34 #include "bus-message.h"
39 #include "capability.h"
40 #include "bus-control.h"
43 #include "synthesize.h"
45 static int get_creds_by_name(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
46 _cleanup_bus_creds_unref_ sd_bus_creds *c = NULL;
53 r = sd_bus_get_name_creds(bus, name, mask, &c);
54 if (r == -ESRCH || r == -ENXIO)
55 return sd_bus_error_setf(error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Name %s is currently not owned by anyone.", name);
59 if ((c->mask & mask) != mask)
68 static int get_creds_by_message(sd_bus *bus, sd_bus_message *m, uint64_t mask, sd_bus_creds **_creds, sd_bus_error *error) {
76 r = sd_bus_message_read(m, "s", &name);
80 return get_creds_by_name(bus, name, mask, _creds, error);
83 int bus_proxy_process_driver(sd_bus *a, sd_bus *b, sd_bus_message *m, SharedPolicy *sp, const struct ucred *ucred, Set *owned_names) {
93 if (!streq_ptr(sd_bus_message_get_destination(m), "org.freedesktop.DBus"))
96 /* The "Hello()" call is is handled in process_hello() */
98 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
100 if (!sd_bus_message_has_signature(m, ""))
101 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
103 return synthetic_reply_method_return(m, "s",
104 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" "
105 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
107 " <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
108 " <method name=\"Introspect\">\n"
109 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"
112 " <interface name=\"org.freedesktop.DBus\">\n"
113 " <method name=\"AddMatch\">\n"
114 " <arg type=\"s\" direction=\"in\"/>\n"
116 " <method name=\"RemoveMatch\">\n"
117 " <arg type=\"s\" direction=\"in\"/>\n"
119 " <method name=\"GetConnectionSELinuxSecurityContext\">\n"
120 " <arg type=\"s\" direction=\"in\"/>\n"
121 " <arg type=\"ay\" direction=\"out\"/>\n"
123 " <method name=\"GetConnectionUnixProcessID\">\n"
124 " <arg type=\"s\" direction=\"in\"/>\n"
125 " <arg type=\"u\" direction=\"out\"/>\n"
127 " <method name=\"GetConnectionUnixUser\">\n"
128 " <arg type=\"s\" direction=\"in\"/>\n"
129 " <arg type=\"u\" direction=\"out\"/>\n"
131 " <method name=\"GetId\">\n"
132 " <arg type=\"s\" direction=\"out\"/>\n"
134 " <method name=\"GetNameOwner\">\n"
135 " <arg type=\"s\" direction=\"in\"/>\n"
136 " <arg type=\"s\" direction=\"out\"/>\n"
138 " <method name=\"Hello\">\n"
139 " <arg type=\"s\" direction=\"out\"/>\n"
141 " <method name=\"ListActivatableNames\">\n"
142 " <arg type=\"as\" direction=\"out\"/>\n"
144 " <method name=\"ListNames\">\n"
145 " <arg type=\"as\" direction=\"out\"/>\n"
147 " <method name=\"ListQueuedOwners\">\n"
148 " <arg type=\"s\" direction=\"in\"/>\n"
149 " <arg type=\"as\" direction=\"out\"/>\n"
151 " <method name=\"NameHasOwner\">\n"
152 " <arg type=\"s\" direction=\"in\"/>\n"
153 " <arg type=\"b\" direction=\"out\"/>\n"
155 " <method name=\"ReleaseName\">\n"
156 " <arg type=\"s\" direction=\"in\"/>\n"
157 " <arg type=\"u\" direction=\"out\"/>\n"
159 " <method name=\"ReloadConfig\">\n"
161 " <method name=\"RequestName\">\n"
162 " <arg type=\"s\" direction=\"in\"/>\n"
163 " <arg type=\"u\" direction=\"in\"/>\n"
164 " <arg type=\"u\" direction=\"out\"/>\n"
166 " <method name=\"StartServiceByName\">\n"
167 " <arg type=\"s\" direction=\"in\"/>\n"
168 " <arg type=\"u\" direction=\"in\"/>\n"
169 " <arg type=\"u\" direction=\"out\"/>\n"
171 " <method name=\"UpdateActivationEnvironment\">\n"
172 " <arg type=\"a{ss}\" direction=\"in\"/>\n"
174 " <signal name=\"NameAcquired\">\n"
175 " <arg type=\"s\"/>\n"
177 " <signal name=\"NameLost\">\n"
178 " <arg type=\"s\"/>\n"
180 " <signal name=\"NameOwnerChanged\">\n"
181 " <arg type=\"s\"/>\n"
182 " <arg type=\"s\"/>\n"
183 " <arg type=\"s\"/>\n"
188 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "AddMatch")) {
191 if (!sd_bus_message_has_signature(m, "s"))
192 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
194 r = sd_bus_message_read(m, "s", &match);
196 return synthetic_reply_method_errno(m, r, NULL);
198 r = sd_bus_add_match(a, NULL, match, NULL, NULL);
200 return synthetic_reply_method_errno(m, r, NULL);
202 return synthetic_reply_method_return(m, NULL);
204 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RemoveMatch")) {
207 if (!sd_bus_message_has_signature(m, "s"))
208 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
210 r = sd_bus_message_read(m, "s", &match);
212 return synthetic_reply_method_errno(m, r, NULL);
214 r = bus_remove_match_by_string(a, match, NULL, NULL);
216 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_MATCH_RULE_NOT_FOUND, "Match rule not found"));
218 return synthetic_reply_method_errno(m, r, NULL);
220 return synthetic_reply_method_return(m, NULL);
222 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionSELinuxSecurityContext")) {
223 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
224 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
226 if (!sd_bus_message_has_signature(m, "s"))
227 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
229 r = get_creds_by_message(a, m, SD_BUS_CREDS_SELINUX_CONTEXT, &creds, &error);
231 return synthetic_reply_method_errno(m, r, &error);
233 return synthetic_reply_method_return(m, "y", creds->label, strlen(creds->label));
235 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixProcessID")) {
236 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
237 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
239 if (!sd_bus_message_has_signature(m, "s"))
240 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
242 r = get_creds_by_message(a, m, SD_BUS_CREDS_PID, &creds, &error);
244 return synthetic_reply_method_errno(m, r, &error);
246 return synthetic_reply_method_return(m, "u", (uint32_t) creds->pid);
248 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetConnectionUnixUser")) {
249 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
250 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
252 if (!sd_bus_message_has_signature(m, "s"))
253 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
255 r = get_creds_by_message(a, m, SD_BUS_CREDS_EUID, &creds, &error);
257 return synthetic_reply_method_errno(m, r, &error);
259 return synthetic_reply_method_return(m, "u", (uint32_t) creds->euid);
261 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetId")) {
262 sd_id128_t server_id;
263 char buf[SD_ID128_STRING_MAX];
265 if (!sd_bus_message_has_signature(m, ""))
266 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
268 r = sd_bus_get_bus_id(a, &server_id);
270 return synthetic_reply_method_errno(m, r, NULL);
272 return synthetic_reply_method_return(m, "s", sd_id128_to_string(server_id, buf));
274 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "GetNameOwner")) {
276 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
277 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
279 if (!sd_bus_message_has_signature(m, "s"))
280 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
282 r = sd_bus_message_read(m, "s", &name);
284 return synthetic_reply_method_errno(m, r, NULL);
286 if (streq(name, "org.freedesktop.DBus"))
287 return synthetic_reply_method_return(m, "s", "org.freedesktop.DBus");
289 r = get_creds_by_name(a, name, SD_BUS_CREDS_UNIQUE_NAME, &creds, &error);
291 return synthetic_reply_method_errno(m, r, &error);
293 return synthetic_reply_method_return(m, "s", creds->unique_name);
295 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListActivatableNames")) {
296 _cleanup_strv_free_ char **names = NULL;
298 if (!sd_bus_message_has_signature(m, ""))
299 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
301 r = sd_bus_list_names(a, NULL, &names);
303 return synthetic_reply_method_errno(m, r, NULL);
305 /* Let's sort the names list to make it stable */
308 return synthetic_reply_return_strv(m, names);
310 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListNames")) {
311 _cleanup_strv_free_ char **names = NULL;
313 if (!sd_bus_message_has_signature(m, ""))
314 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
316 r = sd_bus_list_names(a, &names, NULL);
318 return synthetic_reply_method_errno(m, r, NULL);
320 r = strv_extend(&names, "org.freedesktop.DBus");
322 return synthetic_reply_method_errno(m, r, NULL);
324 /* Let's sort the names list to make it stable */
327 return synthetic_reply_return_strv(m, names);
329 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ListQueuedOwners")) {
330 struct kdbus_cmd_name_list cmd = {};
331 struct kdbus_name_list *name_list;
332 struct kdbus_name_info *name;
333 _cleanup_strv_free_ char **owners = NULL;
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
338 if (!sd_bus_message_has_signature(m, "s"))
339 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
341 r = sd_bus_message_read(m, "s", &arg0);
343 return synthetic_reply_method_errno(m, r, NULL);
345 r = sd_bus_get_name_creds(a, arg0, 0, NULL);
346 if (r == -ESRCH || r == -ENXIO) {
347 sd_bus_error_setf(&error, SD_BUS_ERROR_NAME_HAS_NO_OWNER, "Could not get owners of name '%s': no such name.", arg0);
348 return synthetic_reply_method_errno(m, r, &error);
351 return synthetic_reply_method_errno(m, r, NULL);
353 cmd.flags = KDBUS_NAME_LIST_QUEUED;
354 r = ioctl(a->input_fd, KDBUS_CMD_NAME_LIST, &cmd);
356 return synthetic_reply_method_errno(m, -errno, NULL);
358 name_list = (struct kdbus_name_list *) ((uint8_t *) a->kdbus_buffer + cmd.offset);
360 KDBUS_ITEM_FOREACH(name, name_list, names) {
361 const char *entry_name = NULL;
362 struct kdbus_item *item;
365 KDBUS_ITEM_FOREACH(item, name, items)
366 if (item->type == KDBUS_ITEM_OWNED_NAME)
367 entry_name = item->name.name;
369 if (!streq_ptr(entry_name, arg0))
372 if (asprintf(&n, ":1.%llu", (unsigned long long) name->owner_id) < 0) {
377 r = strv_consume(&owners, n);
384 r = bus_kernel_cmd_free(a, cmd.offset);
386 return synthetic_reply_method_errno(m, r, NULL);
389 return synthetic_reply_method_errno(m, err, NULL);
391 return synthetic_reply_return_strv(m, owners);
393 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "NameHasOwner")) {
396 if (!sd_bus_message_has_signature(m, "s"))
397 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
399 r = sd_bus_message_read(m, "s", &name);
401 return synthetic_reply_method_errno(m, r, NULL);
403 if (streq(name, "org.freedesktop.DBus"))
404 return synthetic_reply_method_return(m, "b", true);
406 r = sd_bus_get_name_creds(a, name, 0, NULL);
407 if (r < 0 && r != -ESRCH && r != -ENXIO)
408 return synthetic_reply_method_errno(m, r, NULL);
410 return synthetic_reply_method_return(m, "b", r >= 0);
412 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReleaseName")) {
415 if (!sd_bus_message_has_signature(m, "s"))
416 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
418 r = sd_bus_message_read(m, "s", &name);
420 return synthetic_reply_method_errno(m, r, NULL);
422 r = sd_bus_release_name(a, name);
425 return synthetic_reply_method_return(m, "u", BUS_NAME_NON_EXISTENT);
426 if (r == -EADDRINUSE)
427 return synthetic_reply_method_return(m, "u", BUS_NAME_NOT_OWNER);
429 return synthetic_reply_method_errno(m, r, NULL);
432 set_remove(owned_names, (char*) name);
434 return synthetic_reply_method_return(m, "u", BUS_NAME_RELEASED);
436 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "ReloadConfig")) {
437 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
439 if (!sd_bus_message_has_signature(m, ""))
440 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
442 r = shared_policy_reload(sp);
444 return synthetic_reply_method_errno(m, r, NULL);
446 return synthetic_reply_method_return(m, NULL);
448 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "RequestName")) {
450 uint32_t flags, param;
453 if (!sd_bus_message_has_signature(m, "su"))
454 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
456 r = sd_bus_message_read(m, "su", &name, &flags);
458 return synthetic_reply_method_errno(m, r, NULL);
464 policy = shared_policy_acquire(sp);
465 denied = !policy_check_own(policy, ucred->uid, ucred->gid, name);
466 shared_policy_release(sp, policy);
468 return synthetic_reply_method_errno(m, -EPERM, NULL);
471 if ((flags & ~(BUS_NAME_ALLOW_REPLACEMENT|BUS_NAME_REPLACE_EXISTING|BUS_NAME_DO_NOT_QUEUE)) != 0)
472 return synthetic_reply_method_errno(m, -EINVAL, NULL);
475 if (flags & BUS_NAME_ALLOW_REPLACEMENT)
476 param |= SD_BUS_NAME_ALLOW_REPLACEMENT;
477 if (flags & BUS_NAME_REPLACE_EXISTING)
478 param |= SD_BUS_NAME_REPLACE_EXISTING;
479 if (!(flags & BUS_NAME_DO_NOT_QUEUE))
480 param |= SD_BUS_NAME_QUEUE;
482 r = set_put_strdup(owned_names, name);
484 return synthetic_reply_method_errno(m, r, NULL);
486 r = sd_bus_request_name(a, name, param);
489 return synthetic_reply_method_return(m, "u", BUS_NAME_ALREADY_OWNER);
491 set_remove(owned_names, (char*) name);
494 return synthetic_reply_method_return(m, "u", BUS_NAME_EXISTS);
495 return synthetic_reply_method_errno(m, r, NULL);
501 return synthetic_reply_method_return(m, "u", BUS_NAME_IN_QUEUE);
503 return synthetic_reply_method_return(m, "u", BUS_NAME_PRIMARY_OWNER);
505 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "StartServiceByName")) {
506 _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
510 if (!sd_bus_message_has_signature(m, "su"))
511 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
513 r = sd_bus_message_read(m, "su", &name, &flags);
515 return synthetic_reply_method_errno(m, r, NULL);
518 return synthetic_reply_method_errno(m, -EINVAL, NULL);
520 r = sd_bus_get_name_creds(a, name, 0, NULL);
521 if (r >= 0 || streq(name, "org.freedesktop.DBus"))
522 return synthetic_reply_method_return(m, "u", BUS_START_REPLY_ALREADY_RUNNING);
524 return synthetic_reply_method_errno(m, r, NULL);
526 r = sd_bus_message_new_method_call(
531 "org.freedesktop.DBus.Peer",
534 return synthetic_reply_method_errno(m, r, NULL);
536 r = sd_bus_send(a, msg, NULL);
538 return synthetic_reply_method_errno(m, r, NULL);
540 return synthetic_reply_method_return(m, "u", BUS_START_REPLY_SUCCESS);
542 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
543 _cleanup_bus_message_unref_ sd_bus_message *msg = NULL;
544 _cleanup_strv_free_ char **args = NULL;
546 if (!sd_bus_message_has_signature(m, "a{ss}"))
547 return synthetic_reply_method_error(m, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid parameters"));
549 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{ss}");
551 return synthetic_reply_method_errno(m, r, NULL);
553 while ((r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "ss")) > 0) {
554 _cleanup_free_ char *s = NULL;
558 r = sd_bus_message_read(m, "ss", &key, &value);
560 return synthetic_reply_method_errno(m, r, NULL);
562 s = strjoin(key, "=", value, NULL);
564 return synthetic_reply_method_errno(m, -ENOMEM, NULL);
566 r = strv_extend(&args, s);
568 return synthetic_reply_method_errno(m, r, NULL);
570 r = sd_bus_message_exit_container(m);
572 return synthetic_reply_method_errno(m, r, NULL);
575 r = sd_bus_message_exit_container(m);
577 return synthetic_reply_method_errno(m, r, NULL);
580 return synthetic_reply_method_errno(m, -EINVAL, NULL);
582 r = sd_bus_message_new_method_call(
585 "org.freedesktop.systemd1",
586 "/org/freedesktop/systemd1",
587 "org.freedesktop.systemd1.Manager",
590 return synthetic_reply_method_errno(m, r, NULL);
592 r = sd_bus_message_append_strv(msg, args);
594 return synthetic_reply_method_errno(m, r, NULL);
596 r = sd_bus_call(a, msg, 0, NULL, NULL);
598 return synthetic_reply_method_errno(m, r, NULL);
600 return synthetic_reply_method_return(m, NULL);
603 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
605 r = sd_bus_error_setf(&error, SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s'.", m->member);
607 return synthetic_reply_method_errno(m, r, &error);