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