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