chiark / gitweb /
27122a5d717de9c39f69a7863ced5678da4070b3
[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 *a, 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(a);
136         assert(b);
137
138         r = sd_bus_get_peer_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM, &creds);
139         if (r < 0)
140                 return r;
141
142         r = sd_bus_creds_get_uid(creds, &uid);
143         if (r < 0)
144                 return r;
145
146         r = sd_bus_creds_get_pid(creds, &pid);
147         if (r < 0)
148                 return r;
149
150         r = sd_bus_creds_get_cmdline(creds, &cmdline);
151         if (r < 0)
152                 return r;
153
154         r = sd_bus_creds_get_comm(creds, &comm);
155         if (r < 0)
156                 return r;
157
158         name = uid_to_name(uid);
159         if (!name)
160                 return -ENOMEM;
161
162         p = strv_join(cmdline, " ");
163         if (!p)
164                 return -ENOMEM;
165
166         /* The status string gets the full command line ... */
167         sd_notifyf(false,
168                    "STATUS=Processing requests from client PID %lu (%s); UID %lu (%s)",
169                    (unsigned long) pid, p,
170                    (unsigned long) uid, name);
171
172         /* ... and the argv line only the short comm */
173         if (arg_command_line_buffer) {
174                 size_t m, w;
175
176                 m = strlen(arg_command_line_buffer);
177                 w = snprintf(arg_command_line_buffer, m,
178                              "[PID %lu/%s; UID %lu/%s]",
179                              (unsigned long) pid, comm,
180                              (unsigned long) uid, name);
181
182                 if (m > w)
183                         memset(arg_command_line_buffer + w, 0, m - w);
184         }
185
186         log_debug("Running on behalf of PID %lu (%s), UID %lu (%s), %s",
187                   (unsigned long) pid, p,
188                   (unsigned long) uid, name,
189                   a->unique_name);
190                 ;
191         return 0;
192 }
193
194 static int synthesize_name_acquired(sd_bus *a, sd_bus *b, sd_bus_message *m) {
195         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
196         const char *name, *old_owner, *new_owner;
197         int r;
198
199         assert(a);
200         assert(b);
201         assert(m);
202
203         /* If we get NameOwnerChanged for our own name, we need to
204          * synthesize NameLost/NameAcquired, since socket clients need
205          * that, even though it is obsoleted on kdbus */
206
207         if (!a->is_kernel)
208                 return 0;
209
210         if (!sd_bus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged") ||
211             !streq_ptr(m->path, "/org/freedesktop/DBus") ||
212             !streq_ptr(m->sender, "org.freedesktop.DBus"))
213                 return 0;
214
215         r = sd_bus_message_read(m, "sss", &name, &old_owner, &new_owner);
216         if (r < 0)
217                 return r;
218
219         r = sd_bus_message_rewind(m, true);
220         if (r < 0)
221                 return r;
222
223         if (streq(old_owner, a->unique_name)) {
224
225                 r = sd_bus_message_new_signal(
226                                 b,
227                                 "/org/freedesktop/DBus",
228                                 "org.freedesktop.DBus",
229                                 "NameLost",
230                                 &n);
231
232         } else if (streq(new_owner, a->unique_name)) {
233
234                 r = sd_bus_message_new_signal(
235                                 b,
236                                 "/org/freedesktop/DBus",
237                                 "org.freedesktop.DBus",
238                                 "NameAcquired",
239                                 &n);
240         } else
241                 return 0;
242
243         if (r < 0)
244                 return r;
245
246         r = sd_bus_message_append(n, "s", name);
247         if (r < 0)
248                 return r;
249
250         r = bus_message_append_sender(n, "org.freedesktop.DBus");
251         if (r < 0)
252                 return r;
253
254         r = bus_seal_synthetic_message(b, n);
255         if (r < 0)
256                 return r;
257
258         return sd_bus_send(b, n, NULL);
259 }
260
261 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
262         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
263         bool is_hello;
264         int r;
265
266         assert(a);
267         assert(b);
268         assert(m);
269         assert(got_hello);
270
271         /* As reaction to hello we need to respond with two messages:
272          * the callback reply and the NameAcquired for the unique
273          * name, since hello is otherwise obsolete on kdbus. */
274
275         is_hello =
276                 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
277                 streq_ptr(m->destination, "org.freedesktop.DBus");
278
279         if (!is_hello) {
280
281                 if (*got_hello)
282                         return 0;
283
284                 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
285                 return -EIO;
286         }
287
288         if (*got_hello) {
289                 log_error("Got duplicate hello, aborting.");
290                 return -EIO;
291         }
292
293         *got_hello = true;
294
295         if (!a->is_kernel)
296                 return 0;
297
298         r = sd_bus_message_new_method_return(m, &n);
299         if (r < 0) {
300                 log_error("Failed to generate HELLO reply: %s", strerror(-r));
301                 return r;
302         }
303
304         r = sd_bus_message_append(n, "s", a->unique_name);
305         if (r < 0) {
306                 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
307                 return r;
308         }
309
310         r = bus_message_append_sender(n, "org.freedesktop.DBus");
311         if (r < 0) {
312                 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
313                 return r;
314         }
315
316         r = bus_seal_synthetic_message(b, n);
317         if (r < 0) {
318                 log_error("Failed to seal HELLO reply: %s", strerror(-r));
319                 return r;
320         }
321
322         r = sd_bus_send(b, n, NULL);
323         if (r < 0) {
324                 log_error("Failed to send HELLO reply: %s", strerror(-r));
325                 return r;
326         }
327
328         n = sd_bus_message_unref(n);
329         r = sd_bus_message_new_signal(
330                         b,
331                         "/org/freedesktop/DBus",
332                         "org.freedesktop.DBus",
333                         "NameAcquired",
334                         &n);
335         if (r < 0) {
336                 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
337                 return r;
338         }
339
340         r = sd_bus_message_append(n, "s", a->unique_name);
341         if (r < 0) {
342                 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
343                 return r;
344         }
345
346         r = bus_message_append_sender(n, "org.freedesktop.DBus");
347         if (r < 0) {
348                 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
349                 return r;
350         }
351
352         r = bus_seal_synthetic_message(b, n);
353         if (r < 0) {
354                 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
355                 return r;
356         }
357
358         r = sd_bus_send(b, n, NULL);
359         if (r < 0) {
360                 log_error("Failed to send NameAcquired message: %s", strerror(-r));
361                 return r;
362         }
363
364         return 1;
365 }
366
367 int main(int argc, char *argv[]) {
368
369         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
370         sd_id128_t server_id;
371         int r, in_fd, out_fd;
372         bool got_hello = false;
373         bool is_unix;
374         struct ucred ucred = {};
375         _cleanup_free_ char *peersec = NULL;
376
377         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
378         log_parse_environment();
379         log_open();
380
381         r = parse_argv(argc, argv);
382         if (r <= 0)
383                 goto finish;
384
385         r = sd_listen_fds(0);
386         if (r == 0) {
387                 in_fd = STDIN_FILENO;
388                 out_fd = STDOUT_FILENO;
389         } else if (r == 1) {
390                 in_fd = SD_LISTEN_FDS_START;
391                 out_fd = SD_LISTEN_FDS_START;
392         } else {
393                 log_error("Illegal number of file descriptors passed");
394                 goto finish;
395         }
396
397         is_unix =
398                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
399                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
400
401         if (is_unix) {
402                 getpeercred(in_fd, &ucred);
403                 getpeersec(in_fd, &peersec);
404         }
405
406         r = sd_bus_new(&a);
407         if (r < 0) {
408                 log_error("Failed to allocate bus: %s", strerror(-r));
409                 goto finish;
410         }
411
412         r = sd_bus_set_address(a, arg_address);
413         if (r < 0) {
414                 log_error("Failed to set address to connect to: %s", strerror(-r));
415                 goto finish;
416         }
417
418         r = sd_bus_negotiate_fds(a, is_unix);
419         if (r < 0) {
420                 log_error("Failed to set FD negotiation: %s", strerror(-r));
421                 goto finish;
422         }
423
424         if (ucred.pid > 0) {
425                 a->fake_creds.pid = ucred.pid;
426                 a->fake_creds.uid = ucred.uid;
427                 a->fake_creds.gid = ucred.gid;
428                 a->fake_creds_valid = true;
429         }
430
431         if (peersec) {
432                 a->fake_label = peersec;
433                 peersec = NULL;
434         }
435
436         a->manual_peer_interface = true;
437
438         r = sd_bus_start(a);
439         if (r < 0) {
440                 log_error("Failed to start bus client: %s", strerror(-r));
441                 goto finish;
442         }
443
444         r = sd_bus_get_server_id(a, &server_id);
445         if (r < 0) {
446                 log_error("Failed to get server ID: %s", strerror(-r));
447                 goto finish;
448         }
449
450         r = sd_bus_new(&b);
451         if (r < 0) {
452                 log_error("Failed to allocate bus: %s", strerror(-r));
453                 goto finish;
454         }
455
456         r = sd_bus_set_fd(b, in_fd, out_fd);
457         if (r < 0) {
458                 log_error("Failed to set fds: %s", strerror(-r));
459                 goto finish;
460         }
461
462         r = sd_bus_set_server(b, 1, server_id);
463         if (r < 0) {
464                 log_error("Failed to set server mode: %s", strerror(-r));
465                 goto finish;
466         }
467
468         r = sd_bus_negotiate_fds(b, is_unix);
469         if (r < 0) {
470                 log_error("Failed to set FD negotiation: %s", strerror(-r));
471                 goto finish;
472         }
473
474         r = sd_bus_set_anonymous(b, true);
475         if (r < 0) {
476                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
477                 goto finish;
478         }
479
480         b->manual_peer_interface = true;
481
482         r = sd_bus_start(b);
483         if (r < 0) {
484                 log_error("Failed to start bus client: %s", strerror(-r));
485                 goto finish;
486         }
487
488         r = rename_service(a, b);
489         if (r < 0)
490                 log_debug("Failed to rename process: %s", strerror(-r));
491
492         if (a->is_kernel) {
493                 _cleanup_free_ char *match = NULL;
494                 const char *unique;
495
496                 r = sd_bus_get_unique_name(a, &unique);
497                 if (r < 0) {
498                         log_error("Failed to get unique name: %s", strerror(-r));
499                         goto finish;
500                 }
501
502                 match = strjoin("type='signal',"
503                                 "sender='org.freedesktop.DBus',"
504                                 "path='/org/freedesktop/DBus',"
505                                 "interface='org.freedesktop.DBus',"
506                                 "member='NameOwnerChanged',"
507                                 "arg1='",
508                                 unique,
509                                 "'",
510                                 NULL);
511                 if (!match) {
512                         log_oom();
513                         goto finish;
514                 }
515
516                 r = sd_bus_add_match(a, match, NULL, NULL);
517                 if (r < 0) {
518                         log_error("Failed to add match for NameLost: %s", strerror(-r));
519                         goto finish;
520                 }
521
522                 free(match);
523                 match = strjoin("type='signal',"
524                                 "sender='org.freedesktop.DBus',"
525                                 "path='/org/freedesktop/DBus',"
526                                 "interface='org.freedesktop.DBus',"
527                                 "member='NameOwnerChanged',"
528                                 "arg2='",
529                                 unique,
530                                 "'",
531                                 NULL);
532                 if (!match) {
533                         log_oom();
534                         goto finish;
535                 }
536
537                 r = sd_bus_add_match(a, match, NULL, NULL);
538                 if (r < 0) {
539                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
540                         goto finish;
541                 }
542         }
543
544         for (;;) {
545                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
546                 int events_a, events_b, fd;
547                 uint64_t timeout_a, timeout_b, t;
548                 struct timespec _ts, *ts;
549                 struct pollfd *pollfd;
550                 int k;
551
552                 if (got_hello) {
553                         r = sd_bus_process(a, &m);
554                         if (r < 0) {
555                                 /* treat 'connection reset by peer' as clean exit condition */
556                                 if (r == -ECONNRESET)
557                                         r = 0;
558                                 else
559                                         log_error("Failed to process bus a: %s", strerror(-r));
560
561                                 goto finish;
562                         }
563
564                         if (m) {
565                                 /* We officially got EOF, let's quit */
566                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
567                                         r = 0;
568                                         goto finish;
569                                 }
570
571                                 k = synthesize_name_acquired(a, b, m);
572                                 if (k < 0) {
573                                         r = k;
574                                         log_error("Failed to synthesize message: %s", strerror(-r));
575                                         goto finish;
576                                 }
577
578                                 k = sd_bus_send(b, m, NULL);
579                                 if (k < 0) {
580                                         r = k;
581                                         log_error("Failed to send message: %s", strerror(-r));
582                                         goto finish;
583                                 }
584                         }
585
586                         if (r > 0)
587                                 continue;
588                 }
589
590                 r = sd_bus_process(b, &m);
591                 if (r < 0) {
592                         /* treat 'connection reset by peer' as clean exit condition */
593                         if (r == -ECONNRESET)
594                                 r = 0;
595                         else
596                                 log_error("Failed to process bus b: %s", strerror(-r));
597
598                         goto finish;
599                 }
600
601                 if (m) {
602                         /* We officially got EOF, let's quit */
603                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
604                                 r = 0;
605                                 goto finish;
606                         }
607
608                         k = process_hello(a, b, m, &got_hello);
609                         if (k < 0) {
610                                 r = k;
611                                 goto finish;
612                         }
613
614                         if (k > 0)
615                                 r = k;
616                         else {
617                                 k = sd_bus_send(a, m, NULL);
618                                 if (k < 0) {
619                                         r = k;
620                                         log_error("Failed to send message: %s", strerror(-r));
621                                         goto finish;
622                                 }
623                         }
624                 }
625
626                 if (r > 0)
627                         continue;
628
629                 fd = sd_bus_get_fd(a);
630                 if (fd < 0) {
631                         log_error("Failed to get fd: %s", strerror(-r));
632                         goto finish;
633                 }
634
635                 events_a = sd_bus_get_events(a);
636                 if (events_a < 0) {
637                         log_error("Failed to get events mask: %s", strerror(-r));
638                         goto finish;
639                 }
640
641                 r = sd_bus_get_timeout(a, &timeout_a);
642                 if (r < 0) {
643                         log_error("Failed to get timeout: %s", strerror(-r));
644                         goto finish;
645                 }
646
647                 events_b = sd_bus_get_events(b);
648                 if (events_b < 0) {
649                         log_error("Failed to get events mask: %s", strerror(-r));
650                         goto finish;
651                 }
652
653                 r = sd_bus_get_timeout(b, &timeout_b);
654                 if (r < 0) {
655                         log_error("Failed to get timeout: %s", strerror(-r));
656                         goto finish;
657                 }
658
659                 t = timeout_a;
660                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
661                         t = timeout_b;
662
663                 if (t == (uint64_t) -1)
664                         ts = NULL;
665                 else {
666                         usec_t nw;
667
668                         nw = now(CLOCK_MONOTONIC);
669                         if (t > nw)
670                                 t -= nw;
671                         else
672                                 t = 0;
673
674                         ts = timespec_store(&_ts, t);
675                 }
676
677                 pollfd = (struct pollfd[3]) {
678                         {.fd = fd,     .events = events_a,           },
679                         {.fd = in_fd,  .events = events_b & POLLIN,  },
680                         {.fd = out_fd, .events = events_b & POLLOUT, }
681                 };
682
683                 r = ppoll(pollfd, 3, ts, NULL);
684                 if (r < 0) {
685                         log_error("ppoll() failed: %m");
686                         goto finish;
687                 }
688         }
689
690         r = 0;
691
692 finish:
693         sd_bus_flush(a);
694         sd_bus_flush(b);
695
696         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
697 }