chiark / gitweb /
fa52a387b649afc3572b875e42c94d6bcb9fd0d8
[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_policy(sd_bus *a, sd_bus *b, sd_bus_message *m) {
262         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
263         int r;
264
265         assert(a);
266         assert(b);
267         assert(m);
268
269         if (!sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "GetAll"))
270                 return 0;
271
272         if (!streq_ptr(m->path, "/org/gnome/DisplayManager/Slave"))
273                 return 0;
274
275         r = sd_bus_message_new_method_errorf(m, &n, SD_BUS_ERROR_ACCESS_DENIED, "gdm, you are stupid");
276         if (r < 0)
277                 return r;
278
279         r = bus_message_append_sender(n, "org.freedesktop.DBus");
280         if (r < 0) {
281                 log_error("Failed to append sender to gdm reply: %s", strerror(-r));
282                 return r;
283         }
284
285         r = bus_seal_synthetic_message(b, n);
286         if (r < 0) {
287                 log_error("Failed to seal gdm reply: %s", strerror(-r));
288                 return r;
289         }
290
291         r = sd_bus_send(b, n, NULL);
292         if (r < 0) {
293                 log_error("Failed to send gdm reply: %s", strerror(-r));
294                 return r;
295         }
296
297         return 1;
298 }
299
300 static int process_hello(sd_bus *a, sd_bus *b, sd_bus_message *m, bool *got_hello) {
301         _cleanup_bus_message_unref_ sd_bus_message *n = NULL;
302         bool is_hello;
303         int r;
304
305         assert(a);
306         assert(b);
307         assert(m);
308         assert(got_hello);
309
310         /* As reaction to hello we need to respond with two messages:
311          * the callback reply and the NameAcquired for the unique
312          * name, since hello is otherwise obsolete on kdbus. */
313
314         is_hello =
315                 sd_bus_message_is_method_call(m, "org.freedesktop.DBus", "Hello") &&
316                 streq_ptr(m->destination, "org.freedesktop.DBus");
317
318         if (!is_hello) {
319
320                 if (*got_hello)
321                         return 0;
322
323                 log_error("First packet isn't hello (it's %s.%s), aborting.", m->interface, m->member);
324                 return -EIO;
325         }
326
327         if (*got_hello) {
328                 log_error("Got duplicate hello, aborting.");
329                 return -EIO;
330         }
331
332         *got_hello = true;
333
334         if (!a->is_kernel)
335                 return 0;
336
337         r = sd_bus_message_new_method_return(m, &n);
338         if (r < 0) {
339                 log_error("Failed to generate HELLO reply: %s", strerror(-r));
340                 return r;
341         }
342
343         r = sd_bus_message_append(n, "s", a->unique_name);
344         if (r < 0) {
345                 log_error("Failed to append unique name to HELLO reply: %s", strerror(-r));
346                 return r;
347         }
348
349         r = bus_message_append_sender(n, "org.freedesktop.DBus");
350         if (r < 0) {
351                 log_error("Failed to append sender to HELLO reply: %s", strerror(-r));
352                 return r;
353         }
354
355         r = bus_seal_synthetic_message(b, n);
356         if (r < 0) {
357                 log_error("Failed to seal HELLO reply: %s", strerror(-r));
358                 return r;
359         }
360
361         r = sd_bus_send(b, n, NULL);
362         if (r < 0) {
363                 log_error("Failed to send HELLO reply: %s", strerror(-r));
364                 return r;
365         }
366
367         n = sd_bus_message_unref(n);
368         r = sd_bus_message_new_signal(
369                         b,
370                         "/org/freedesktop/DBus",
371                         "org.freedesktop.DBus",
372                         "NameAcquired",
373                         &n);
374         if (r < 0) {
375                 log_error("Failed to allocate initial NameAcquired message: %s", strerror(-r));
376                 return r;
377         }
378
379         r = sd_bus_message_append(n, "s", a->unique_name);
380         if (r < 0) {
381                 log_error("Failed to append unique name to NameAcquired message: %s", strerror(-r));
382                 return r;
383         }
384
385         r = bus_message_append_sender(n, "org.freedesktop.DBus");
386         if (r < 0) {
387                 log_error("Failed to append sender to NameAcquired message: %s", strerror(-r));
388                 return r;
389         }
390
391         r = bus_seal_synthetic_message(b, n);
392         if (r < 0) {
393                 log_error("Failed to seal NameAcquired message: %s", strerror(-r));
394                 return r;
395         }
396
397         r = sd_bus_send(b, n, NULL);
398         if (r < 0) {
399                 log_error("Failed to send NameAcquired message: %s", strerror(-r));
400                 return r;
401         }
402
403         return 1;
404 }
405
406 int main(int argc, char *argv[]) {
407
408         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
409         sd_id128_t server_id;
410         int r, in_fd, out_fd;
411         bool got_hello = false;
412         bool is_unix;
413         struct ucred ucred = {};
414         _cleanup_free_ char *peersec = NULL;
415
416         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
417         log_parse_environment();
418         log_open();
419
420         r = parse_argv(argc, argv);
421         if (r <= 0)
422                 goto finish;
423
424         r = sd_listen_fds(0);
425         if (r == 0) {
426                 in_fd = STDIN_FILENO;
427                 out_fd = STDOUT_FILENO;
428         } else if (r == 1) {
429                 in_fd = SD_LISTEN_FDS_START;
430                 out_fd = SD_LISTEN_FDS_START;
431         } else {
432                 log_error("Illegal number of file descriptors passed");
433                 goto finish;
434         }
435
436         is_unix =
437                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
438                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
439
440         if (is_unix) {
441                 getpeercred(in_fd, &ucred);
442                 getpeersec(in_fd, &peersec);
443         }
444
445         r = sd_bus_new(&a);
446         if (r < 0) {
447                 log_error("Failed to allocate bus: %s", strerror(-r));
448                 goto finish;
449         }
450
451         r = sd_bus_set_address(a, arg_address);
452         if (r < 0) {
453                 log_error("Failed to set address to connect to: %s", strerror(-r));
454                 goto finish;
455         }
456
457         r = sd_bus_negotiate_fds(a, is_unix);
458         if (r < 0) {
459                 log_error("Failed to set FD negotiation: %s", strerror(-r));
460                 goto finish;
461         }
462
463         if (ucred.pid > 0) {
464                 a->fake_creds.pid = ucred.pid;
465                 a->fake_creds.uid = ucred.uid;
466                 a->fake_creds.gid = ucred.gid;
467                 a->fake_creds_valid = true;
468         }
469
470         if (peersec) {
471                 a->fake_label = peersec;
472                 peersec = NULL;
473         }
474
475         a->manual_peer_interface = true;
476
477         r = sd_bus_start(a);
478         if (r < 0) {
479                 log_error("Failed to start bus client: %s", strerror(-r));
480                 goto finish;
481         }
482
483         r = sd_bus_get_server_id(a, &server_id);
484         if (r < 0) {
485                 log_error("Failed to get server ID: %s", strerror(-r));
486                 goto finish;
487         }
488
489         r = sd_bus_new(&b);
490         if (r < 0) {
491                 log_error("Failed to allocate bus: %s", strerror(-r));
492                 goto finish;
493         }
494
495         r = sd_bus_set_fd(b, in_fd, out_fd);
496         if (r < 0) {
497                 log_error("Failed to set fds: %s", strerror(-r));
498                 goto finish;
499         }
500
501         r = sd_bus_set_server(b, 1, server_id);
502         if (r < 0) {
503                 log_error("Failed to set server mode: %s", strerror(-r));
504                 goto finish;
505         }
506
507         r = sd_bus_negotiate_fds(b, is_unix);
508         if (r < 0) {
509                 log_error("Failed to set FD negotiation: %s", strerror(-r));
510                 goto finish;
511         }
512
513         r = sd_bus_set_anonymous(b, true);
514         if (r < 0) {
515                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
516                 goto finish;
517         }
518
519         b->manual_peer_interface = true;
520
521         r = sd_bus_start(b);
522         if (r < 0) {
523                 log_error("Failed to start bus client: %s", strerror(-r));
524                 goto finish;
525         }
526
527         r = rename_service(a, b);
528         if (r < 0)
529                 log_debug("Failed to rename process: %s", strerror(-r));
530
531         if (a->is_kernel) {
532                 _cleanup_free_ char *match = NULL;
533                 const char *unique;
534
535                 r = sd_bus_get_unique_name(a, &unique);
536                 if (r < 0) {
537                         log_error("Failed to get unique name: %s", strerror(-r));
538                         goto finish;
539                 }
540
541                 match = strjoin("type='signal',"
542                                 "sender='org.freedesktop.DBus',"
543                                 "path='/org/freedesktop/DBus',"
544                                 "interface='org.freedesktop.DBus',"
545                                 "member='NameOwnerChanged',"
546                                 "arg1='",
547                                 unique,
548                                 "'",
549                                 NULL);
550                 if (!match) {
551                         log_oom();
552                         goto finish;
553                 }
554
555                 r = sd_bus_add_match(a, match, NULL, NULL);
556                 if (r < 0) {
557                         log_error("Failed to add match for NameLost: %s", strerror(-r));
558                         goto finish;
559                 }
560
561                 free(match);
562                 match = strjoin("type='signal',"
563                                 "sender='org.freedesktop.DBus',"
564                                 "path='/org/freedesktop/DBus',"
565                                 "interface='org.freedesktop.DBus',"
566                                 "member='NameOwnerChanged',"
567                                 "arg2='",
568                                 unique,
569                                 "'",
570                                 NULL);
571                 if (!match) {
572                         log_oom();
573                         goto finish;
574                 }
575
576                 r = sd_bus_add_match(a, match, NULL, NULL);
577                 if (r < 0) {
578                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
579                         goto finish;
580                 }
581         }
582
583         for (;;) {
584                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
585                 int events_a, events_b, fd;
586                 uint64_t timeout_a, timeout_b, t;
587                 struct timespec _ts, *ts;
588                 struct pollfd *pollfd;
589                 int k;
590
591                 if (got_hello) {
592                         r = sd_bus_process(a, &m);
593                         if (r < 0) {
594                                 /* treat 'connection reset by peer' as clean exit condition */
595                                 if (r == -ECONNRESET)
596                                         r = 0;
597                                 else
598                                         log_error("Failed to process bus a: %s", strerror(-r));
599
600                                 goto finish;
601                         }
602
603                         if (m) {
604                                 /* We officially got EOF, let's quit */
605                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
606                                         r = 0;
607                                         goto finish;
608                                 }
609
610                                 k = synthesize_name_acquired(a, b, m);
611                                 if (k < 0) {
612                                         r = k;
613                                         log_error("Failed to synthesize message: %s", strerror(-r));
614                                         goto finish;
615                                 }
616
617                                 k = sd_bus_send(b, 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                         if (r > 0)
626                                 continue;
627                 }
628
629                 r = sd_bus_process(b, &m);
630                 if (r < 0) {
631                         /* treat 'connection reset by peer' as clean exit condition */
632                         if (r == -ECONNRESET)
633                                 r = 0;
634                         else
635                                 log_error("Failed to process bus b: %s", strerror(-r));
636
637                         goto finish;
638                 }
639
640                 if (m) {
641                         /* We officially got EOF, let's quit */
642                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
643                                 r = 0;
644                                 goto finish;
645                         }
646
647                         k = process_hello(a, b, m, &got_hello);
648                         if (k < 0) {
649                                 r = k;
650                                 goto finish;
651                         }
652
653                         if (k > 0)
654                                 r = k;
655                         else {
656                                 k = process_policy(a, b, m);
657                                 if (k < 0) {
658                                         r = k;
659                                         goto finish;
660                                 }
661
662                                 k = sd_bus_send(a, m, NULL);
663                                 if (k < 0) {
664                                         r = k;
665                                         log_error("Failed to send message: %s", strerror(-r));
666                                         goto finish;
667                                 }
668                         }
669                 }
670
671                 if (r > 0)
672                         continue;
673
674                 fd = sd_bus_get_fd(a);
675                 if (fd < 0) {
676                         log_error("Failed to get fd: %s", strerror(-r));
677                         goto finish;
678                 }
679
680                 events_a = sd_bus_get_events(a);
681                 if (events_a < 0) {
682                         log_error("Failed to get events mask: %s", strerror(-r));
683                         goto finish;
684                 }
685
686                 r = sd_bus_get_timeout(a, &timeout_a);
687                 if (r < 0) {
688                         log_error("Failed to get timeout: %s", strerror(-r));
689                         goto finish;
690                 }
691
692                 events_b = sd_bus_get_events(b);
693                 if (events_b < 0) {
694                         log_error("Failed to get events mask: %s", strerror(-r));
695                         goto finish;
696                 }
697
698                 r = sd_bus_get_timeout(b, &timeout_b);
699                 if (r < 0) {
700                         log_error("Failed to get timeout: %s", strerror(-r));
701                         goto finish;
702                 }
703
704                 t = timeout_a;
705                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
706                         t = timeout_b;
707
708                 if (t == (uint64_t) -1)
709                         ts = NULL;
710                 else {
711                         usec_t nw;
712
713                         nw = now(CLOCK_MONOTONIC);
714                         if (t > nw)
715                                 t -= nw;
716                         else
717                                 t = 0;
718
719                         ts = timespec_store(&_ts, t);
720                 }
721
722                 pollfd = (struct pollfd[3]) {
723                         {.fd = fd,     .events = events_a,           },
724                         {.fd = in_fd,  .events = events_b & POLLIN,  },
725                         {.fd = out_fd, .events = events_b & POLLOUT, }
726                 };
727
728                 r = ppoll(pollfd, 3, ts, NULL);
729                 if (r < 0) {
730                         log_error("ppoll() failed: %m");
731                         goto finish;
732                 }
733         }
734
735         r = 0;
736
737 finish:
738         sd_bus_flush(a);
739         sd_bus_flush(b);
740
741         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
742 }