]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-convenience.c
sd-bus: properly handle uninitialized audit creds from kdbus
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-convenience.c
CommitLineData
992c052c
LP
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#include "bus-internal.h"
23#include "bus-message.h"
24#include "bus-signature.h"
40ca29a1 25#include "bus-util.h"
e5609878 26#include "bus-type.h"
992c052c 27
d9f644e2 28_public_ int sd_bus_emit_signal(
992c052c
LP
29 sd_bus *bus,
30 const char *path,
31 const char *interface,
32 const char *member,
33 const char *types, ...) {
34
35 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
36 int r;
37
f69dc9a3 38 assert_return(bus, -EINVAL);
f69dc9a3 39 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 40
a3d59cd1
LP
41 if (!BUS_IS_OPEN(bus->state))
42 return -ENOTCONN;
43
151b9b96 44 r = sd_bus_message_new_signal(bus, &m, path, interface, member);
992c052c
LP
45 if (r < 0)
46 return r;
47
48 if (!isempty(types)) {
49 va_list ap;
50
51 va_start(ap, types);
52 r = bus_message_append_ap(m, types, ap);
53 va_end(ap);
54 if (r < 0)
55 return r;
56 }
57
58 return sd_bus_send(bus, m, NULL);
59}
60
d9f644e2 61_public_ int sd_bus_call_method(
992c052c
LP
62 sd_bus *bus,
63 const char *destination,
64 const char *path,
65 const char *interface,
66 const char *member,
67 sd_bus_error *error,
68 sd_bus_message **reply,
69 const char *types, ...) {
70
71 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
72 int r;
73
f69dc9a3 74 assert_return(bus, -EINVAL);
f69dc9a3 75 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 76
a3d59cd1
LP
77 if (!BUS_IS_OPEN(bus->state))
78 return -ENOTCONN;
79
151b9b96 80 r = sd_bus_message_new_method_call(bus, &m, destination, path, interface, member);
992c052c
LP
81 if (r < 0)
82 return r;
83
84 if (!isempty(types)) {
85 va_list ap;
86
87 va_start(ap, types);
88 r = bus_message_append_ap(m, types, ap);
89 va_end(ap);
90 if (r < 0)
91 return r;
92 }
93
c49b30a2 94 return sd_bus_call(bus, m, 0, error, reply);
992c052c
LP
95}
96
d9f644e2 97_public_ int sd_bus_reply_method_return(
992c052c
LP
98 sd_bus_message *call,
99 const char *types, ...) {
100
101 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
102 int r;
103
f69dc9a3
LP
104 assert_return(call, -EINVAL);
105 assert_return(call->sealed, -EPERM);
40ca29a1 106 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
a3d59cd1 107 assert_return(call->bus, -EINVAL);
df2d202e 108 assert_return(!bus_pid_changed(call->bus), -ECHILD);
992c052c 109
a3d59cd1
LP
110 if (!BUS_IS_OPEN(call->bus->state))
111 return -ENOTCONN;
112
0461f8cd 113 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
992c052c
LP
114 return 0;
115
df2d202e 116 r = sd_bus_message_new_method_return(call, &m);
992c052c
LP
117 if (r < 0)
118 return r;
119
120 if (!isempty(types)) {
121 va_list ap;
122
123 va_start(ap, types);
124 r = bus_message_append_ap(m, types, ap);
125 va_end(ap);
126 if (r < 0)
127 return r;
128 }
129
df2d202e 130 return sd_bus_send(call->bus, m, NULL);
992c052c
LP
131}
132
d9f644e2 133_public_ int sd_bus_reply_method_error(
992c052c
LP
134 sd_bus_message *call,
135 const sd_bus_error *e) {
136
137 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
138 int r;
139
f69dc9a3
LP
140 assert_return(call, -EINVAL);
141 assert_return(call->sealed, -EPERM);
40ca29a1 142 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
f69dc9a3 143 assert_return(sd_bus_error_is_set(e), -EINVAL);
a3d59cd1 144 assert_return(call->bus, -EINVAL);
df2d202e 145 assert_return(!bus_pid_changed(call->bus), -ECHILD);
992c052c 146
a3d59cd1
LP
147 if (!BUS_IS_OPEN(call->bus->state))
148 return -ENOTCONN;
149
0461f8cd 150 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
992c052c
LP
151 return 0;
152
151b9b96 153 r = sd_bus_message_new_method_error(call, &m, e);
992c052c
LP
154 if (r < 0)
155 return r;
156
df2d202e 157 return sd_bus_send(call->bus, m, NULL);
992c052c
LP
158}
159
d9f644e2 160_public_ int sd_bus_reply_method_errorf(
992c052c
LP
161 sd_bus_message *call,
162 const char *name,
163 const char *format,
164 ...) {
165
166 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
167 va_list ap;
992c052c 168
f69dc9a3
LP
169 assert_return(call, -EINVAL);
170 assert_return(call->sealed, -EPERM);
40ca29a1 171 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
a3d59cd1 172 assert_return(call->bus, -EINVAL);
df2d202e 173 assert_return(!bus_pid_changed(call->bus), -ECHILD);
f69dc9a3 174
a3d59cd1
LP
175 if (!BUS_IS_OPEN(call->bus->state))
176 return -ENOTCONN;
177
0461f8cd 178 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
f69dc9a3
LP
179 return 0;
180
40ca29a1 181 va_start(ap, format);
780896a4 182 bus_error_setfv(&error, name, format, ap);
40ca29a1 183 va_end(ap);
992c052c 184
df2d202e 185 return sd_bus_reply_method_error(call, &error);
40ca29a1 186}
992c052c 187
d9f644e2 188_public_ int sd_bus_reply_method_errno(
40ca29a1
LP
189 sd_bus_message *call,
190 int error,
191 const sd_bus_error *p) {
992c052c 192
40ca29a1
LP
193 _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
194
40ca29a1
LP
195 assert_return(call, -EINVAL);
196 assert_return(call->sealed, -EPERM);
197 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
a3d59cd1 198 assert_return(call->bus, -EINVAL);
df2d202e 199 assert_return(!bus_pid_changed(call->bus), -ECHILD);
40ca29a1 200
a3d59cd1
LP
201 if (!BUS_IS_OPEN(call->bus->state))
202 return -ENOTCONN;
203
0461f8cd 204 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
40ca29a1
LP
205 return 0;
206
207 if (sd_bus_error_is_set(p))
df2d202e 208 return sd_bus_reply_method_error(call, p);
40ca29a1
LP
209
210 sd_bus_error_set_errno(&berror, error);
211
df2d202e 212 return sd_bus_reply_method_error(call, &berror);
40ca29a1
LP
213}
214
d9f644e2 215_public_ int sd_bus_reply_method_errnof(
40ca29a1
LP
216 sd_bus_message *call,
217 int error,
218 const char *format,
219 ...) {
220
221 _cleanup_bus_error_free_ sd_bus_error berror = SD_BUS_ERROR_NULL;
222 va_list ap;
223
40ca29a1
LP
224 assert_return(call, -EINVAL);
225 assert_return(call->sealed, -EPERM);
226 assert_return(call->header->type == SD_BUS_MESSAGE_METHOD_CALL, -EINVAL);
a3d59cd1 227 assert_return(call->bus, -EINVAL);
df2d202e 228 assert_return(!bus_pid_changed(call->bus), -ECHILD);
40ca29a1 229
a3d59cd1
LP
230 if (!BUS_IS_OPEN(call->bus->state))
231 return -ENOTCONN;
232
0461f8cd 233 if (call->header->flags & BUS_MESSAGE_NO_REPLY_EXPECTED)
40ca29a1
LP
234 return 0;
235
236 va_start(ap, format);
237 bus_error_set_errnofv(&berror, error, format, ap);
238 va_end(ap);
239
df2d202e 240 return sd_bus_reply_method_error(call, &berror);
992c052c
LP
241}
242
d9f644e2 243_public_ int sd_bus_get_property(
992c052c
LP
244 sd_bus *bus,
245 const char *destination,
246 const char *path,
247 const char *interface,
248 const char *member,
249 sd_bus_error *error,
250 sd_bus_message **reply,
251 const char *type) {
252
253 sd_bus_message *rep = NULL;
254 int r;
255
f69dc9a3
LP
256 assert_return(bus, -EINVAL);
257 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
258 assert_return(member_name_is_valid(member), -EINVAL);
259 assert_return(reply, -EINVAL);
260 assert_return(signature_is_single(type, false), -EINVAL);
f69dc9a3 261 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 262
a3d59cd1
LP
263 if (!BUS_IS_OPEN(bus->state))
264 return -ENOTCONN;
265
992c052c
LP
266 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &rep, "ss", strempty(interface), member);
267 if (r < 0)
268 return r;
269
270 r = sd_bus_message_enter_container(rep, 'v', type);
271 if (r < 0) {
272 sd_bus_message_unref(rep);
273 return r;
274 }
275
276 *reply = rep;
277 return 0;
278}
279
d9f644e2 280_public_ int sd_bus_get_property_trivial(
e5609878
LP
281 sd_bus *bus,
282 const char *destination,
283 const char *path,
284 const char *interface,
285 const char *member,
286 sd_bus_error *error,
287 char type, void *ptr) {
288
289 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
290 int r;
291
292 assert_return(bus, -EINVAL);
293 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
294 assert_return(member_name_is_valid(member), -EINVAL);
295 assert_return(bus_type_is_trivial(type), -EINVAL);
296 assert_return(ptr, -EINVAL);
e5609878
LP
297 assert_return(!bus_pid_changed(bus), -ECHILD);
298
a3d59cd1
LP
299 if (!BUS_IS_OPEN(bus->state))
300 return -ENOTCONN;
301
e5609878
LP
302 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
303 if (r < 0)
304 return r;
305
306 r = sd_bus_message_enter_container(reply, 'v', CHAR_TO_STR(type));
307 if (r < 0)
308 return r;
309
310 r = sd_bus_message_read_basic(reply, type, ptr);
311 if (r < 0)
312 return r;
313
314 return 0;
315}
316
63be1989
LP
317_public_ int sd_bus_get_property_string(
318 sd_bus *bus,
319 const char *destination,
320 const char *path,
321 const char *interface,
322 const char *member,
323 sd_bus_error *error,
324 char **ret) {
325
326 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
327 const char *s;
328 char *n;
329 int r;
330
331 assert_return(bus, -EINVAL);
332 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
333 assert_return(member_name_is_valid(member), -EINVAL);
334 assert_return(ret, -EINVAL);
63be1989
LP
335 assert_return(!bus_pid_changed(bus), -ECHILD);
336
a3d59cd1
LP
337 if (!BUS_IS_OPEN(bus->state))
338 return -ENOTCONN;
339
63be1989
LP
340 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
341 if (r < 0)
342 return r;
343
344 r = sd_bus_message_enter_container(reply, 'v', "s");
345 if (r < 0)
346 return r;
347
348 r = sd_bus_message_read_basic(reply, 's', &s);
349 if (r < 0)
350 return r;
351
352 n = strdup(s);
353 if (!n)
354 return -ENOMEM;
355
356 *ret = n;
357 return 0;
358}
359
360_public_ int sd_bus_get_property_strv(
361 sd_bus *bus,
362 const char *destination,
363 const char *path,
364 const char *interface,
365 const char *member,
366 sd_bus_error *error,
367 char ***ret) {
368
369 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
370 int r;
371
372 assert_return(bus, -EINVAL);
373 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
374 assert_return(member_name_is_valid(member), -EINVAL);
375 assert_return(ret, -EINVAL);
63be1989
LP
376 assert_return(!bus_pid_changed(bus), -ECHILD);
377
a3d59cd1
LP
378 if (!BUS_IS_OPEN(bus->state))
379 return -ENOTCONN;
380
63be1989
LP
381 r = sd_bus_call_method(bus, destination, path, "org.freedesktop.DBus.Properties", "Get", error, &reply, "ss", strempty(interface), member);
382 if (r < 0)
383 return r;
384
385 r = sd_bus_message_enter_container(reply, 'v', NULL);
386 if (r < 0)
387 return r;
388
389 r = sd_bus_message_read_strv(reply, ret);
390 if (r < 0)
391 return r;
392
393 return 0;
394}
395
d9f644e2 396_public_ int sd_bus_set_property(
992c052c
LP
397 sd_bus *bus,
398 const char *destination,
399 const char *path,
400 const char *interface,
401 const char *member,
402 sd_bus_error *error,
403 const char *type, ...) {
404
405 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
406 va_list ap;
407 int r;
408
f69dc9a3
LP
409 assert_return(bus, -EINVAL);
410 assert_return(isempty(interface) || interface_name_is_valid(interface), -EINVAL);
411 assert_return(member_name_is_valid(member), -EINVAL);
412 assert_return(signature_is_single(type, false), -EINVAL);
f69dc9a3 413 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 414
a3d59cd1
LP
415 if (!BUS_IS_OPEN(bus->state))
416 return -ENOTCONN;
417
151b9b96 418 r = sd_bus_message_new_method_call(bus, &m, destination, path, "org.freedesktop.DBus.Properties", "Set");
992c052c
LP
419 if (r < 0)
420 return r;
421
422 r = sd_bus_message_append(m, "ss", strempty(interface), member);
423 if (r < 0)
424 return r;
425
426 r = sd_bus_message_open_container(m, 'v', type);
427 if (r < 0)
428 return r;
429
430 va_start(ap, type);
431 r = bus_message_append_ap(m, type, ap);
432 va_end(ap);
433 if (r < 0)
434 return r;
435
436 r = sd_bus_message_close_container(m);
437 if (r < 0)
438 return r;
439
c49b30a2 440 return sd_bus_call(bus, m, 0, error, NULL);
992c052c 441}
5b12334d
LP
442
443_public_ int sd_bus_query_sender_creds(sd_bus_message *call, uint64_t mask, sd_bus_creds **creds) {
444 sd_bus_creds *c;
445
446 assert_return(call, -EINVAL);
447 assert_return(call->sealed, -EPERM);
a3d59cd1 448 assert_return(call->bus, -EINVAL);
5b12334d
LP
449 assert_return(!bus_pid_changed(call->bus), -ECHILD);
450
a3d59cd1
LP
451 if (!BUS_IS_OPEN(call->bus->state))
452 return -ENOTCONN;
453
5b12334d
LP
454 c = sd_bus_message_get_creds(call);
455
456 /* All data we need? */
457 if (c && (mask & ~c->mask) == 0) {
458 *creds = sd_bus_creds_ref(c);
459 return 0;
460 }
461
462 /* No data passed? Or not enough data passed to retrieve the missing bits? */
463 if (!c || !(c->mask & SD_BUS_CREDS_PID)) {
464 /* We couldn't read anything from the call, let's try
465 * to get it from the sender or peer */
466
467 if (call->sender)
056f95d0 468 return sd_bus_get_name_creds(call->bus, call->sender, mask, creds);
5b12334d 469 else
056f95d0 470 return sd_bus_get_owner_creds(call->bus, mask, creds);
5b12334d
LP
471 }
472
49b832c5 473 return bus_creds_extend_by_pid(c, mask, creds);
5b12334d 474}
def9a7aa
LP
475
476_public_ int sd_bus_query_sender_privilege(sd_bus_message *call, int capability) {
477 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
478 uid_t our_uid;
705a415f 479 bool know_caps = false;
def9a7aa
LP
480 int r;
481
482 assert_return(call, -EINVAL);
483 assert_return(call->sealed, -EPERM);
484 assert_return(call->bus, -EINVAL);
485 assert_return(!bus_pid_changed(call->bus), -ECHILD);
486
487 if (!BUS_IS_OPEN(call->bus->state))
488 return -ENOTCONN;
489
705a415f
LP
490 if (capability >= 0) {
491 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
def9a7aa
LP
492 if (r < 0)
493 return r;
494
705a415f
LP
495 /* Note that not even on kdbus we might have the caps
496 * field, due to faked identities, or namespace
497 * translation issues. */
def9a7aa
LP
498 r = sd_bus_creds_has_effective_cap(creds, capability);
499 if (r > 0)
500 return 1;
705a415f
LP
501 if (r == 0)
502 know_caps = true;
def9a7aa 503 } else {
705a415f 504 r = sd_bus_query_sender_creds(call, SD_BUS_CREDS_UID|SD_BUS_CREDS_EUID, &creds);
def9a7aa
LP
505 if (r < 0)
506 return r;
507 }
508
509 /* Now, check the UID, but only if the capability check wasn't
510 * sufficient */
511 our_uid = getuid();
705a415f 512 if (our_uid != 0 || !know_caps || capability < 0) {
def9a7aa
LP
513 uid_t sender_uid;
514
705a415f
LP
515 /* Try to use the EUID, if we have it. */
516 r = sd_bus_creds_get_euid(creds, &sender_uid);
517 if (r < 0)
518 r = sd_bus_creds_get_uid(creds, &sender_uid);
519
def9a7aa
LP
520 if (r >= 0) {
521 /* Sender has same UID as us, then let's grant access */
522 if (sender_uid == our_uid)
523 return 1;
524
525 /* Sender is root, we are not root. */
526 if (our_uid != 0 && sender_uid == 0)
527 return 1;
528 }
529 }
530
531 return 0;
532}