chiark / gitweb /
b87dffe0e8b77202d4858feda63e8f3b6858b63e
[elogind.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 }