]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/bus-proxyd/bus-proxyd.c
bus: fake client side creds in the proxy to the caller's creds
[thirdparty/systemd.git] / src / bus-proxyd / bus-proxyd.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 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 <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/types.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/poll.h>
30 #include <stddef.h>
31 #include <getopt.h>
32
33 #include "log.h"
34 #include "util.h"
35 #include "socket-util.h"
36 #include "sd-daemon.h"
37 #include "sd-bus.h"
38 #include "bus-internal.h"
39 #include "bus-message.h"
40 #include "bus-util.h"
41 #include "build.h"
42 #include "strv.h"
43
44 #define UNIX_BUS_PATH "unix:path=/run/dbus/system_bus_socket"
45 #define KERNEL_BUS_PATH "kernel:path=/dev/kdbus/0-system/bus"
46
47 #ifdef ENABLE_KDBUS
48 # define DEFAULT_BUS_PATH KERNEL_BUS_PATH ";" UNIX_BUS_PATH
49 #else
50 # define DEFAULT_BUS_PATH UNIX_BUS_PATH
51 #endif
52
53 static const char *arg_address = DEFAULT_BUS_PATH;
54 static char *arg_command_line_buffer = NULL;
55
56 static int help(void) {
57
58 printf("%s [OPTIONS...]\n\n"
59 "Connect STDIO or a socket to a given bus address.\n\n"
60 " -h --help Show this help\n"
61 " --version Show package version\n"
62 " --address=ADDRESS Connect to the bus specified by ADDRESS\n"
63 " (default: " DEFAULT_BUS_PATH ")\n",
64 program_invocation_short_name);
65
66 return 0;
67 }
68
69 static int parse_argv(int argc, char *argv[]) {
70
71 enum {
72 ARG_VERSION = 0x100,
73 ARG_ADDRESS,
74 };
75
76 static const struct option options[] = {
77 { "help", no_argument, NULL, 'h' },
78 { "version", no_argument, NULL, ARG_VERSION },
79 { "address", required_argument, NULL, ARG_ADDRESS },
80 { NULL, 0, NULL, 0 }
81 };
82
83 int c;
84
85 assert(argc >= 0);
86 assert(argv);
87
88 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
89
90 switch (c) {
91
92 case 'h':
93 help();
94 return 0;
95
96 case ARG_VERSION:
97 puts(PACKAGE_STRING);
98 puts(SYSTEMD_FEATURES);
99 return 0;
100
101 case ARG_ADDRESS:
102 arg_address = optarg;
103 break;
104
105 case '?':
106 return -EINVAL;
107
108 default:
109 assert_not_reached("Unhandled option");
110 }
111 }
112
113 /* If the first command line argument is only "x" characters
114 * we'll write who we are talking to into it, so that "ps" is
115 * explanatory */
116 arg_command_line_buffer = argv[optind];
117 if (argc > optind + 1 ||
118 (arg_command_line_buffer && arg_command_line_buffer[strspn(arg_command_line_buffer, "x")] != 0)) {
119 log_error("Too many arguments");
120 return -EINVAL;
121 }
122
123 return 1;
124 }
125
126 static int rename_service(sd_bus *b) {
127 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
128 _cleanup_free_ char *p = NULL, *name = NULL;
129 const char *comm;
130 char **cmdline;
131 uid_t uid;
132 pid_t pid;
133 int r;
134
135 assert(b);
136
137 r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
138 if (r < 0)
139 return r;
140
141 r = sd_bus_creds_get_uid(creds, &uid);
142 if (r < 0)
143 return r;
144
145 r = sd_bus_creds_get_pid(creds, &pid);
146 if (r < 0)
147 return r;
148
149 r = sd_bus_creds_get_cmdline(creds, &cmdline);
150 if (r < 0)
151 return r;
152
153 r = sd_bus_creds_get_comm(creds, &comm);
154 if (r < 0)
155 return r;
156
157 name = uid_to_name(uid);
158 if (!name)
159 return -ENOMEM;
160
161 p = strv_join(cmdline, " ");
162 if (!p)
163 return -ENOMEM;
164
165 /* The status string gets the full command line ... */
166 sd_notifyf(false,
167 "STATUS=Processing requests from client PID %lu (%s); UID %lu (%s)",
168 (unsigned long) pid, p,
169 (unsigned long) uid, name);
170
171 /* ... and the argv line only the short comm */
172 if (arg_command_line_buffer) {
173 size_t m, w;
174
175 m = strlen(arg_command_line_buffer);
176 w = snprintf(arg_command_line_buffer, m,
177 "[PID %lu/%s; UID %lu/%s]",
178 (unsigned long) pid, comm,
179 (unsigned long) uid, name);
180
181 if (m > w)
182 memset(arg_command_line_buffer + w, 0, m - w);
183 }
184
185 return 0;
186 }
187
188 static int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
189 _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
190 const char *name, *old_owner, *new_owner;
191 int r;
192
193 assert(a);
194 assert(b);
195 assert(m);
196
197 /* If we get NameOwnerChanged for our own name, we need to
198 * synthesize NameLost/NameAcquired, since socket clients need
199 * that, even though it is obsoleted on kdbus */
200
201 if (!a->is_kernel)
202 return 0;
203
204 if (!sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged") ||
205 !streq_ptr(m->path, "/org/freedesktop/DBus") ||
206 !streq_ptr(m->sender, "org.freedesktop.DBus"))
207 return 0;
208
209 r = sd_bus_message_read(m, "sss", &name, &old_owner, &new_owner);
210 if (r < 0)
211 return r;
212
213 r = sd_bus_message_rewind(m, true);
214 if (r < 0)
215 return r;
216
217 if (streq(old_owner, a->unique_name)) {
218
219 r = sd_bus_message_new_signal(
220 b,
221 "/org/freedesktop/DBus",
222 "org.freedesktop.DBus",
223 "NameLost",
224 &n);
225
226 } else if (streq(new_owner, a->unique_name)) {
227
228 r = sd_bus_message_new_signal(
229 b,
230 "/org/freedesktop/DBus",
231 "org.freedesktop.DBus",
232 "NameAcquired",
233 &n);
234 } else
235 return 0;
236
237 if (r < 0)
238 return r;
239
240 r = sd_bus_message_append(n, "s", name);
241 if (r < 0)
242 return r;
243
244 r = bus_message_append_sender(n, "org.freedesktop.DBus");
245 if (r < 0)
246 return r;
247
248 r = bus_seal_synthetic_message(b, n);
249 if (r < 0)
250 return r;
251
252 return sd_bus_send(b, n, NULL);
253 }
254
255 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
256 _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
257 bool is_hello;
258 int r;
259
260 assert(a);
261 assert(b);
262 assert(m);
263 assert(got_hello);
264
265 /* As reaction to hello we need to respond with two messages:
266 * the callback reply and the NameAcquired for the unique
267 * name, since hello is otherwise obsolete on kdbus. */
268
269 is_hello =
270 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
271 streq_ptr(m->destination, "org.freedesktop.DBus");
272
273 if (!is_hello) {
274
275 if (*got_hello)
276 return 0;
277
278 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
279 return -EIO;
280 }
281
282 if (*got_hello) {
283 log_error("Got duplicate hello, aborting.");
284 return -EIO;
285 }
286
287 *got_hello = true;
288
289 if (!a->is_kernel)
290 return 0;
291
292 r = sd_bus_message_new_method_return(m, &n);
293 if (r < 0) {
294 log_error("Failed to generate HELLO reply: %s", strerror(-r));
295 return r;
296 }
297
298 r = sd_bus_message_append(n, "s", a->unique_name);
299 if (r < 0) {
300 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
301 return r;
302 }
303
304 r = bus_message_append_sender(n, "org.freedesktop.DBus");
305 if (r < 0) {
306 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
307 return r;
308 }
309
310 r = bus_seal_synthetic_message(b, n);
311 if (r < 0) {
312 log_error("Failed to seal HELLO reply: %s", strerror(-r));
313 return r;
314 }
315
316 r = sd_bus_send(b, n, NULL);
317 if (r < 0) {
318 log_error("Failed to send HELLO reply: %s", strerror(-r));
319 return r;
320 }
321
322 n = sd_bus_message_unref(n);
323 r = sd_bus_message_new_signal(
324 b,
325 "/org/freedesktop/DBus",
326 "org.freedesktop.DBus",
327 "NameAcquired",
328 &n);
329 if (r < 0) {
330 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
331 return r;
332 }
333
334 r = sd_bus_message_append(n, "s", a->unique_name);
335 if (r < 0) {
336 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
337 return r;
338 }
339
340 r = bus_message_append_sender(n, "org.freedesktop.DBus");
341 if (r < 0) {
342 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
343 return r;
344 }
345
346 r = bus_seal_synthetic_message(b, n);
347 if (r < 0) {
348 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
349 return r;
350 }
351
352 r = sd_bus_send(b, n, NULL);
353 if (r < 0) {
354 log_error("Failed to send NameAcquired message: %s", strerror(-r));
355 return r;
356 }
357
358 return 1;
359 }
360
361 static int getpeersec(int fd, char **ret) {
362 socklen_t n = 64;
363 char *s;
364 int r;
365
366 assert(fd >= 0);
367 assert(ret);
368
369 s = new0(char, n);
370 if (!s)
371 return -ENOMEM;
372
373 r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
374 if (r < 0) {
375 free(s);
376
377 if (errno != ERANGE)
378 return r;
379
380 s = new0(char, n);
381 if (!s)
382 return -ENOMEM;
383
384 r = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, s, &n);
385 if (r < 0) {
386 free(s);
387 return r;
388 }
389 }
390
391 *ret = s;
392 return 0;
393 }
394
395 int main(int argc, char *argv[]) {
396
397 _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
398 sd_id128_t server_id;
399 int r, in_fd, out_fd;
400 bool got_hello = false;
401 bool is_unix;
402 struct ucred ucred = {};
403 _cleanup_free_ char *peersec = NULL;
404
405 log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
406 log_parse_environment();
407 log_open();
408
409 r = parse_argv(argc, argv);
410 if (r <= 0)
411 goto finish;
412
413 r = sd_listen_fds(0);
414 if (r == 0) {
415 in_fd = STDIN_FILENO;
416 out_fd = STDOUT_FILENO;
417 } else if (r == 1) {
418 in_fd = SD_LISTEN_FDS_START;
419 out_fd = SD_LISTEN_FDS_START;
420 } else {
421 log_error("Illegal number of file descriptors passed\n");
422 goto finish;
423 }
424
425 is_unix =
426 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
427 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
428
429 if (is_unix) {
430 socklen_t l = sizeof(ucred);
431
432 r = getsockopt(in_fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l);
433 if (r < 0) {
434 r = -errno;
435 goto finish;
436 }
437
438 assert(l == sizeof(ucred));
439
440 getpeersec(in_fd, &peersec);
441 }
442
443 r = sd_bus_new(&a);
444 if (r < 0) {
445 log_error("Failed to allocate bus: %s", strerror(-r));
446 goto finish;
447 }
448
449 r = sd_bus_set_address(a, arg_address);
450 if (r < 0) {
451 log_error("Failed to set address to connect to: %s", strerror(-r));
452 goto finish;
453 }
454
455 r = sd_bus_negotiate_fds(a, is_unix);
456 if (r < 0) {
457 log_error("Failed to set FD negotiation: %s", strerror(-r));
458 goto finish;
459 }
460
461 if (ucred.pid > 0) {
462 a->fake_creds.pid = ucred.pid;
463 a->fake_creds.uid = ucred.uid;
464 a->fake_creds.gid = ucred.gid;
465 a->fake_creds_valid = true;
466 }
467
468 if (peersec) {
469 a->fake_label = peersec;
470 peersec = NULL;
471 }
472
473 r = sd_bus_start(a);
474 if (r < 0) {
475 log_error("Failed to start bus client: %s", strerror(-r));
476 goto finish;
477 }
478
479 r = sd_bus_get_server_id(a, &server_id);
480 if (r < 0) {
481 log_error("Failed to get server ID: %s", strerror(-r));
482 goto finish;
483 }
484
485 r = sd_bus_new(&b);
486 if (r < 0) {
487 log_error("Failed to allocate bus: %s", strerror(-r));
488 goto finish;
489 }
490
491 r = sd_bus_set_fd(b, in_fd, out_fd);
492 if (r < 0) {
493 log_error("Failed to set fds: %s", strerror(-r));
494 goto finish;
495 }
496
497 r = sd_bus_set_server(b, 1, server_id);
498 if (r < 0) {
499 log_error("Failed to set server mode: %s", strerror(-r));
500 goto finish;
501 }
502
503 r = sd_bus_negotiate_fds(b, is_unix);
504 if (r < 0) {
505 log_error("Failed to set FD negotiation: %s", strerror(-r));
506 goto finish;
507 }
508
509 r = sd_bus_set_anonymous(b, true);
510 if (r < 0) {
511 log_error("Failed to set anonymous authentication: %s", strerror(-r));
512 goto finish;
513 }
514
515 r = sd_bus_start(b);
516 if (r < 0) {
517 log_error("Failed to start bus client: %s", strerror(-r));
518 goto finish;
519 }
520
521 r = rename_service(b);
522 if (r < 0)
523 log_debug("Failed to rename process: %s", strerror(-r));
524
525 if (a->is_kernel) {
526 _cleanup_free_ char *match = NULL;
527 const char *unique;
528
529 r = sd_bus_get_unique_name(a, &unique);
530 if (r < 0) {
531 log_error("Failed to get unique name: %s", strerror(-r));
532 goto finish;
533 }
534
535 match = strjoin("type='signal',"
536 "sender='org.freedesktop.DBus',"
537 "path='/org/freedesktop/DBus',"
538 "interface='org.freedesktop.DBus',"
539 "member='NameOwnerChanged',"
540 "arg1='",
541 unique,
542 "'",
543 NULL);
544 if (!match) {
545 log_oom();
546 goto finish;
547 }
548
549 r = sd_bus_add_match(a, match, NULL, NULL);
550 if (r < 0) {
551 log_error("Failed to add match for NameLost: %s", strerror(-r));
552 goto finish;
553 }
554
555 free(match);
556 match = strjoin("type='signal',"
557 "sender='org.freedesktop.DBus',"
558 "path='/org/freedesktop/DBus',"
559 "interface='org.freedesktop.DBus',"
560 "member='NameOwnerChanged',"
561 "arg2='",
562 unique,
563 "'",
564 NULL);
565 if (!match) {
566 log_oom();
567 goto finish;
568 }
569
570 r = sd_bus_add_match(a, match, NULL, NULL);
571 if (r < 0) {
572 log_error("Failed to add match for NameAcquired: %s", strerror(-r));
573 goto finish;
574 }
575 }
576
577 for (;;) {
578 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
579 int events_a, events_b, fd;
580 uint64_t timeout_a, timeout_b, t;
581 struct timespec _ts, *ts;
582 struct pollfd *pollfd;
583 int k;
584
585 if (got_hello) {
586 r = sd_bus_process(a, &m);
587 if (r < 0) {
588 /* treat 'connection reset by peer' as clean exit condition */
589 if (r == -ECONNRESET)
590 r = 0;
591 else
592 log_error("Failed to process bus a: %s", strerror(-r));
593
594 goto finish;
595 }
596
597 if (m) {
598 /* We officially got EOF, let's quit */
599 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
600 r = 0;
601 goto finish;
602 }
603
604 k = synthesize_name_acquired(a, b, m);
605 if (k < 0) {
606 r = k;
607 log_error("Failed to synthesize message: %s", strerror(-r));
608 goto finish;
609 }
610
611 k = sd_bus_send(b, m, NULL);
612 if (k < 0) {
613 r = k;
614 log_error("Failed to send message: %s", strerror(-r));
615 goto finish;
616 }
617 }
618
619 if (r > 0)
620 continue;
621 }
622
623 r = sd_bus_process(b, &m);
624 if (r < 0) {
625 /* treat 'connection reset by peer' as clean exit condition */
626 if (r == -ECONNRESET)
627 r = 0;
628 else
629 log_error("Failed to process bus b: %s", strerror(-r));
630
631 goto finish;
632 }
633
634 if (m) {
635 /* We officially got EOF, let's quit */
636 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
637 r = 0;
638 goto finish;
639 }
640
641 k = process_hello(a, b, m, &got_hello);
642 if (k < 0) {
643 r = k;
644 goto finish;
645 }
646
647 if (k > 0)
648 r = k;
649 else {
650 k = sd_bus_send(a, m, NULL);
651 if (k < 0) {
652 r = k;
653 log_error("Failed to send message: %s", strerror(-r));
654 goto finish;
655 }
656 }
657 }
658
659 if (r > 0)
660 continue;
661
662 fd = sd_bus_get_fd(a);
663 if (fd < 0) {
664 log_error("Failed to get fd: %s", strerror(-r));
665 goto finish;
666 }
667
668 events_a = sd_bus_get_events(a);
669 if (events_a < 0) {
670 log_error("Failed to get events mask: %s", strerror(-r));
671 goto finish;
672 }
673
674 r = sd_bus_get_timeout(a, &timeout_a);
675 if (r < 0) {
676 log_error("Failed to get timeout: %s", strerror(-r));
677 goto finish;
678 }
679
680 events_b = sd_bus_get_events(b);
681 if (events_b < 0) {
682 log_error("Failed to get events mask: %s", strerror(-r));
683 goto finish;
684 }
685
686 r = sd_bus_get_timeout(b, &timeout_b);
687 if (r < 0) {
688 log_error("Failed to get timeout: %s", strerror(-r));
689 goto finish;
690 }
691
692 t = timeout_a;
693 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
694 t = timeout_b;
695
696 if (t == (uint64_t) -1)
697 ts = NULL;
698 else {
699 usec_t nw;
700
701 nw = now(CLOCK_MONOTONIC);
702 if (t > nw)
703 t -= nw;
704 else
705 t = 0;
706
707 ts = timespec_store(&_ts, t);
708 }
709
710 pollfd = (struct pollfd[3]) {
711 {.fd = fd, .events = events_a, },
712 {.fd = in_fd, .events = events_b & POLLIN, },
713 {.fd = out_fd, .events = events_b & POLLOUT, }
714 };
715
716 r = ppoll(pollfd, 3, ts, NULL);
717 if (r < 0) {
718 log_error("ppoll() failed: %m");
719 goto finish;
720 }
721 }
722
723 r = 0;
724
725 finish:
726 sd_bus_flush(a);
727 sd_bus_flush(b);
728
729 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
730 }