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_method_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_method_return_strv(m
, names
);
329 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "ListQueuedOwners")) {
330 struct kdbus_cmd_list cmd
= {
331 .flags
= KDBUS_LIST_QUEUED
,
334 struct kdbus_info
*name_list
, *name
;
335 _cleanup_strv_free_
char **owners
= NULL
;
336 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
340 if (!sd_bus_message_has_signature(m
, "s"))
341 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
343 r
= sd_bus_message_read(m
, "s", &arg0
);
345 return synthetic_reply_method_errno(m
, r
, NULL
);
347 r
= sd_bus_get_name_creds(a
, arg0
, 0, NULL
);
348 if (r
== -ESRCH
|| r
== -ENXIO
) {
349 sd_bus_error_setf(&error
, SD_BUS_ERROR_NAME_HAS_NO_OWNER
, "Could not get owners of name '%s': no such name.", arg0
);
350 return synthetic_reply_method_errno(m
, r
, &error
);
353 return synthetic_reply_method_errno(m
, r
, NULL
);
355 r
= ioctl(a
->input_fd
, KDBUS_CMD_LIST
, &cmd
);
357 return synthetic_reply_method_errno(m
, -errno
, NULL
);
359 name_list
= (struct kdbus_info
*) ((uint8_t *) a
->kdbus_buffer
+ cmd
.offset
);
361 KDBUS_FOREACH(name
, name_list
, cmd
.list_size
) {
362 const char *entry_name
= NULL
;
363 struct kdbus_item
*item
;
366 KDBUS_ITEM_FOREACH(item
, name
, items
)
367 if (item
->type
== KDBUS_ITEM_OWNED_NAME
)
368 entry_name
= item
->name
.name
;
370 if (!streq_ptr(entry_name
, arg0
))
373 if (asprintf(&n
, ":1.%llu", (unsigned long long) name
->id
) < 0) {
378 r
= strv_consume(&owners
, n
);
385 r
= bus_kernel_cmd_free(a
, cmd
.offset
);
387 return synthetic_reply_method_errno(m
, r
, NULL
);
390 return synthetic_reply_method_errno(m
, err
, NULL
);
392 return synthetic_reply_method_return_strv(m
, owners
);
394 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "NameHasOwner")) {
397 if (!sd_bus_message_has_signature(m
, "s"))
398 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
400 r
= sd_bus_message_read(m
, "s", &name
);
402 return synthetic_reply_method_errno(m
, r
, NULL
);
404 if (streq(name
, "org.freedesktop.DBus"))
405 return synthetic_reply_method_return(m
, "b", true);
407 r
= sd_bus_get_name_creds(a
, name
, 0, NULL
);
408 if (r
< 0 && r
!= -ESRCH
&& r
!= -ENXIO
)
409 return synthetic_reply_method_errno(m
, r
, NULL
);
411 return synthetic_reply_method_return(m
, "b", r
>= 0);
413 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "ReleaseName")) {
416 if (!sd_bus_message_has_signature(m
, "s"))
417 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
419 r
= sd_bus_message_read(m
, "s", &name
);
421 return synthetic_reply_method_errno(m
, r
, NULL
);
423 r
= sd_bus_release_name(a
, name
);
426 return synthetic_reply_method_return(m
, "u", BUS_NAME_NON_EXISTENT
);
427 if (r
== -EADDRINUSE
)
428 return synthetic_reply_method_return(m
, "u", BUS_NAME_NOT_OWNER
);
430 return synthetic_reply_method_errno(m
, r
, NULL
);
433 set_remove(owned_names
, (char*) name
);
435 return synthetic_reply_method_return(m
, "u", BUS_NAME_RELEASED
);
437 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "ReloadConfig")) {
438 if (!sd_bus_message_has_signature(m
, ""))
439 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
441 r
= shared_policy_reload(sp
);
443 return synthetic_reply_method_errno(m
, r
, NULL
);
445 return synthetic_reply_method_return(m
, NULL
);
447 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "RequestName")) {
449 uint32_t flags
, param
;
452 if (!sd_bus_message_has_signature(m
, "su"))
453 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
455 r
= sd_bus_message_read(m
, "su", &name
, &flags
);
457 return synthetic_reply_method_errno(m
, r
, NULL
);
463 policy
= shared_policy_acquire(sp
);
464 denied
= !policy_check_own(policy
, ucred
->uid
, ucred
->gid
, name
);
465 shared_policy_release(sp
, policy
);
467 return synthetic_reply_method_errno(m
, -EPERM
, NULL
);
470 if ((flags
& ~(BUS_NAME_ALLOW_REPLACEMENT
|BUS_NAME_REPLACE_EXISTING
|BUS_NAME_DO_NOT_QUEUE
)) != 0)
471 return synthetic_reply_method_errno(m
, -EINVAL
, NULL
);
474 if (flags
& BUS_NAME_ALLOW_REPLACEMENT
)
475 param
|= SD_BUS_NAME_ALLOW_REPLACEMENT
;
476 if (flags
& BUS_NAME_REPLACE_EXISTING
)
477 param
|= SD_BUS_NAME_REPLACE_EXISTING
;
478 if (!(flags
& BUS_NAME_DO_NOT_QUEUE
))
479 param
|= SD_BUS_NAME_QUEUE
;
481 r
= set_put_strdup(owned_names
, name
);
483 return synthetic_reply_method_errno(m
, r
, NULL
);
485 r
= sd_bus_request_name(a
, name
, param
);
488 return synthetic_reply_method_return(m
, "u", BUS_NAME_ALREADY_OWNER
);
490 set_remove(owned_names
, (char*) name
);
493 return synthetic_reply_method_return(m
, "u", BUS_NAME_EXISTS
);
494 return synthetic_reply_method_errno(m
, r
, NULL
);
500 return synthetic_reply_method_return(m
, "u", BUS_NAME_IN_QUEUE
);
502 return synthetic_reply_method_return(m
, "u", BUS_NAME_PRIMARY_OWNER
);
504 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "StartServiceByName")) {
505 _cleanup_bus_message_unref_ sd_bus_message
*msg
= NULL
;
509 if (!sd_bus_message_has_signature(m
, "su"))
510 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
512 r
= sd_bus_message_read(m
, "su", &name
, &flags
);
514 return synthetic_reply_method_errno(m
, r
, NULL
);
517 return synthetic_reply_method_errno(m
, -EINVAL
, NULL
);
519 r
= sd_bus_get_name_creds(a
, name
, 0, NULL
);
520 if (r
>= 0 || streq(name
, "org.freedesktop.DBus"))
521 return synthetic_reply_method_return(m
, "u", BUS_START_REPLY_ALREADY_RUNNING
);
523 return synthetic_reply_method_errno(m
, r
, NULL
);
525 r
= sd_bus_message_new_method_call(
530 "org.freedesktop.DBus.Peer",
533 return synthetic_reply_method_errno(m
, r
, NULL
);
535 r
= sd_bus_send(a
, msg
, NULL
);
537 return synthetic_reply_method_errno(m
, r
, NULL
);
539 return synthetic_reply_method_return(m
, "u", BUS_START_REPLY_SUCCESS
);
541 } else if (sd_bus_message_is_method_call(m
, "org.freedesktop.DBus", "UpdateActivationEnvironment")) {
542 _cleanup_bus_message_unref_ sd_bus_message
*msg
= NULL
;
543 _cleanup_strv_free_
char **args
= NULL
;
545 if (!sd_bus_message_has_signature(m
, "a{ss}"))
546 return synthetic_reply_method_error(m
, &SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS
, "Invalid parameters"));
548 r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_ARRAY
, "{ss}");
550 return synthetic_reply_method_errno(m
, r
, NULL
);
552 while ((r
= sd_bus_message_enter_container(m
, SD_BUS_TYPE_DICT_ENTRY
, "ss")) > 0) {
553 _cleanup_free_
char *s
= NULL
;
557 r
= sd_bus_message_read(m
, "ss", &key
, &value
);
559 return synthetic_reply_method_errno(m
, r
, NULL
);
561 s
= strjoin(key
, "=", value
, NULL
);
563 return synthetic_reply_method_errno(m
, -ENOMEM
, NULL
);
565 r
= strv_extend(&args
, s
);
567 return synthetic_reply_method_errno(m
, r
, NULL
);
569 r
= sd_bus_message_exit_container(m
);
571 return synthetic_reply_method_errno(m
, r
, NULL
);
574 r
= sd_bus_message_exit_container(m
);
576 return synthetic_reply_method_errno(m
, r
, NULL
);
579 return synthetic_reply_method_errno(m
, -EINVAL
, NULL
);
581 r
= sd_bus_message_new_method_call(
584 "org.freedesktop.systemd1",
585 "/org/freedesktop/systemd1",
586 "org.freedesktop.systemd1.Manager",
589 return synthetic_reply_method_errno(m
, r
, NULL
);
591 r
= sd_bus_message_append_strv(msg
, args
);
593 return synthetic_reply_method_errno(m
, r
, NULL
);
595 r
= sd_bus_call(a
, msg
, 0, NULL
, NULL
);
597 return synthetic_reply_method_errno(m
, r
, NULL
);
599 return synthetic_reply_method_return(m
, NULL
);
602 _cleanup_bus_error_free_ sd_bus_error error
= SD_BUS_ERROR_NULL
;
604 r
= sd_bus_error_setf(&error
, SD_BUS_ERROR_UNKNOWN_METHOD
, "Unknown method '%s'.", m
->member
);
606 return synthetic_reply_method_errno(m
, r
, &error
);