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