chiark / gitweb /
api: in constructor function calls, always put the returned object pointer first...
[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                         memzero(arg_command_line_buffer + w, 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                                 &n,
220                                 "/org/freedesktop/DBus",
221                                 "org.freedesktop.DBus",
222                                 "NameLost");
223
224         } else if (streq(new_owner, a->unique_name)) {
225
226                 r = sd_bus_message_new_signal(
227                                 b,
228                                 &n,
229                                 "/org/freedesktop/DBus",
230                                 "org.freedesktop.DBus",
231                                 "NameAcquired");
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                         &n,
363                         "/org/freedesktop/DBus",
364                         "org.freedesktop.DBus",
365                         "NameAcquired");
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 static int patch_sender(sd_bus *a, sd_bus_message *m) {
399         char **well_known = NULL;
400         sd_bus_creds *c;
401         int r;
402
403         assert(a);
404         assert(m);
405
406         if (!a->is_kernel)
407                 return 0;
408
409         /* We will change the sender of messages from the bus driver
410          * so that they originate from the bus driver. This is a
411          * speciality originating from dbus1, where the bus driver did
412          * not have a unique id, but only the well-known name. */
413
414         c = sd_bus_message_get_creds(m);
415         if (!c)
416                 return 0;
417
418         r = sd_bus_creds_get_well_known_names(c, &well_known);
419         if (r < 0)
420                 return r;
421
422         if (strv_contains(well_known, "org.freedesktop.DBus"))
423                 m->sender = "org.freedesktop.DBus";
424
425         return 0;
426 }
427
428 int main(int argc, char *argv[]) {
429
430         _cleanup_bus_unref_ sd_bus *a = NULL, *b = NULL;
431         sd_id128_t server_id;
432         int r, in_fd, out_fd;
433         bool got_hello = false;
434         bool is_unix;
435         struct ucred ucred = {};
436         _cleanup_free_ char *peersec = NULL;
437
438         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
439         log_parse_environment();
440         log_open();
441
442         r = parse_argv(argc, argv);
443         if (r <= 0)
444                 goto finish;
445
446         r = sd_listen_fds(0);
447         if (r == 0) {
448                 in_fd = STDIN_FILENO;
449                 out_fd = STDOUT_FILENO;
450         } else if (r == 1) {
451                 in_fd = SD_LISTEN_FDS_START;
452                 out_fd = SD_LISTEN_FDS_START;
453         } else {
454                 log_error("Illegal number of file descriptors passed");
455                 goto finish;
456         }
457
458         is_unix =
459                 sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 &&
460                 sd_is_socket(out_fd, AF_UNIX, 0, 0) > 0;
461
462         if (is_unix) {
463                 getpeercred(in_fd, &ucred);
464                 getpeersec(in_fd, &peersec);
465         }
466
467         r = sd_bus_new(&a);
468         if (r < 0) {
469                 log_error("Failed to allocate bus: %s", strerror(-r));
470                 goto finish;
471         }
472
473         r = sd_bus_set_name(a, "sd-proxy");
474         if (r < 0) {
475                 log_error("Failed to set bus name: %s", strerror(-r));
476                 goto finish;
477         }
478
479         r = sd_bus_set_address(a, arg_address);
480         if (r < 0) {
481                 log_error("Failed to set address to connect to: %s", strerror(-r));
482                 goto finish;
483         }
484
485         r = sd_bus_negotiate_fds(a, is_unix);
486         if (r < 0) {
487                 log_error("Failed to set FD negotiation: %s", strerror(-r));
488                 goto finish;
489         }
490
491         if (ucred.pid > 0) {
492                 a->fake_creds.pid = ucred.pid;
493                 a->fake_creds.uid = ucred.uid;
494                 a->fake_creds.gid = ucred.gid;
495                 a->fake_creds_valid = true;
496         }
497
498         if (peersec) {
499                 a->fake_label = peersec;
500                 peersec = NULL;
501         }
502
503         a->manual_peer_interface = true;
504
505         r = sd_bus_start(a);
506         if (r < 0) {
507                 log_error("Failed to start bus client: %s", strerror(-r));
508                 goto finish;
509         }
510
511         r = sd_bus_get_server_id(a, &server_id);
512         if (r < 0) {
513                 log_error("Failed to get server ID: %s", strerror(-r));
514                 goto finish;
515         }
516
517         r = sd_bus_new(&b);
518         if (r < 0) {
519                 log_error("Failed to allocate bus: %s", strerror(-r));
520                 goto finish;
521         }
522
523         r = sd_bus_set_fd(b, in_fd, out_fd);
524         if (r < 0) {
525                 log_error("Failed to set fds: %s", strerror(-r));
526                 goto finish;
527         }
528
529         r = sd_bus_set_server(b, 1, server_id);
530         if (r < 0) {
531                 log_error("Failed to set server mode: %s", strerror(-r));
532                 goto finish;
533         }
534
535         r = sd_bus_negotiate_fds(b, is_unix);
536         if (r < 0) {
537                 log_error("Failed to set FD negotiation: %s", strerror(-r));
538                 goto finish;
539         }
540
541         r = sd_bus_set_anonymous(b, true);
542         if (r < 0) {
543                 log_error("Failed to set anonymous authentication: %s", strerror(-r));
544                 goto finish;
545         }
546
547         b->manual_peer_interface = true;
548
549         r = sd_bus_start(b);
550         if (r < 0) {
551                 log_error("Failed to start bus client: %s", strerror(-r));
552                 goto finish;
553         }
554
555         r = rename_service(a, b);
556         if (r < 0)
557                 log_debug("Failed to rename process: %s", strerror(-r));
558
559         if (a->is_kernel) {
560                 _cleanup_free_ char *match = NULL;
561                 const char *unique;
562
563                 r = sd_bus_get_unique_name(a, &unique);
564                 if (r < 0) {
565                         log_error("Failed to get unique name: %s", strerror(-r));
566                         goto finish;
567                 }
568
569                 match = strjoin("type='signal',"
570                                 "sender='org.freedesktop.DBus',"
571                                 "path='/org/freedesktop/DBus',"
572                                 "interface='org.freedesktop.DBus',"
573                                 "member='NameOwnerChanged',"
574                                 "arg1='",
575                                 unique,
576                                 "'",
577                                 NULL);
578                 if (!match) {
579                         log_oom();
580                         goto finish;
581                 }
582
583                 r = sd_bus_add_match(a, match, NULL, NULL);
584                 if (r < 0) {
585                         log_error("Failed to add match for NameLost: %s", strerror(-r));
586                         goto finish;
587                 }
588
589                 free(match);
590                 match = strjoin("type='signal',"
591                                 "sender='org.freedesktop.DBus',"
592                                 "path='/org/freedesktop/DBus',"
593                                 "interface='org.freedesktop.DBus',"
594                                 "member='NameOwnerChanged',"
595                                 "arg2='",
596                                 unique,
597                                 "'",
598                                 NULL);
599                 if (!match) {
600                         log_oom();
601                         goto finish;
602                 }
603
604                 r = sd_bus_add_match(a, match, NULL, NULL);
605                 if (r < 0) {
606                         log_error("Failed to add match for NameAcquired: %s", strerror(-r));
607                         goto finish;
608                 }
609         }
610
611         for (;;) {
612                 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
613                 int events_a, events_b, fd;
614                 uint64_t timeout_a, timeout_b, t;
615                 struct timespec _ts, *ts;
616                 struct pollfd *pollfd;
617                 int k;
618
619                 if (got_hello) {
620                         r = sd_bus_process(a, &m);
621                         if (r < 0) {
622                                 /* treat 'connection reset by peer' as clean exit condition */
623                                 if (r == -ECONNRESET)
624                                         r = 0;
625                                 else
626                                         log_error("Failed to process bus a: %s", strerror(-r));
627
628                                 goto finish;
629                         }
630
631                         if (m) {
632                                 /* We officially got EOF, let's quit */
633                                 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
634                                         r = 0;
635                                         goto finish;
636                                 }
637
638                                 k = synthesize_name_acquired(a, b, m);
639                                 if (k < 0) {
640                                         r = k;
641                                         log_error("Failed to synthesize message: %s", strerror(-r));
642                                         goto finish;
643                                 }
644
645                                 patch_sender(a, m);
646
647                                 k = sd_bus_send(b, m, NULL);
648                                 if (k < 0) {
649                                         if (k == -ECONNRESET)
650                                                 r = 0;
651                                         else {
652                                                 r = k;
653                                                 log_error("Failed to send message: %s", strerror(-r));
654                                         }
655
656                                         goto finish;
657                                 }
658                         }
659
660                         if (r > 0)
661                                 continue;
662                 }
663
664                 r = sd_bus_process(b, &m);
665                 if (r < 0) {
666                         /* treat 'connection reset by peer' as clean exit condition */
667                         if (r == -ECONNRESET)
668                                 r = 0;
669                         else
670                                 log_error("Failed to process bus b: %s", strerror(-r));
671
672                         goto finish;
673                 }
674
675                 if (m) {
676                         /* We officially got EOF, let's quit */
677                         if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
678                                 r = 0;
679                                 goto finish;
680                         }
681
682                         k = process_hello(a, b, m, &got_hello);
683                         if (k < 0) {
684                                 r = k;
685                                 log_error("Failed to process HELLO: %s", strerror(-r));
686                                 goto finish;
687                         }
688
689                         if (k > 0)
690                                 r = k;
691                         else {
692                                 k = process_policy(a, b, m);
693                                 if (k < 0) {
694                                         r = k;
695                                         log_error("Failed to process policy: %s", strerror(-r));
696                                         goto finish;
697                                 }
698
699                                 k = sd_bus_send(a, m, NULL);
700                                 if (k < 0) {
701                                         if (r == -ECONNRESET)
702                                                 r = 0;
703                                         else {
704                                                 r = k;
705                                                 log_error("Failed to send message: %s", strerror(-r));
706                                         }
707
708                                         goto finish;
709                                 }
710                         }
711                 }
712
713                 if (r > 0)
714                         continue;
715
716                 fd = sd_bus_get_fd(a);
717                 if (fd < 0) {
718                         log_error("Failed to get fd: %s", strerror(-r));
719                         goto finish;
720                 }
721
722                 events_a = sd_bus_get_events(a);
723                 if (events_a < 0) {
724                         log_error("Failed to get events mask: %s", strerror(-r));
725                         goto finish;
726                 }
727
728                 r = sd_bus_get_timeout(a, &timeout_a);
729                 if (r < 0) {
730                         log_error("Failed to get timeout: %s", strerror(-r));
731                         goto finish;
732                 }
733
734                 events_b = sd_bus_get_events(b);
735                 if (events_b < 0) {
736                         log_error("Failed to get events mask: %s", strerror(-r));
737                         goto finish;
738                 }
739
740                 r = sd_bus_get_timeout(b, &timeout_b);
741                 if (r < 0) {
742                         log_error("Failed to get timeout: %s", strerror(-r));
743                         goto finish;
744                 }
745
746                 t = timeout_a;
747                 if (t == (uint64_t) -1 || (timeout_b != (uint64_t) -1 && timeout_b < timeout_a))
748                         t = timeout_b;
749
750                 if (t == (uint64_t) -1)
751                         ts = NULL;
752                 else {
753                         usec_t nw;
754
755                         nw = now(CLOCK_MONOTONIC);
756                         if (t > nw)
757                                 t -= nw;
758                         else
759                                 t = 0;
760
761                         ts = timespec_store(&_ts, t);
762                 }
763
764                 pollfd = (struct pollfd[3]) {
765                         {.fd = fd,     .events = events_a,           },
766                         {.fd = in_fd,  .events = events_b & POLLIN,  },
767                         {.fd = out_fd, .events = events_b & POLLOUT, }
768                 };
769
770                 r = ppoll(pollfd, 3, ts, NULL);
771                 if (r < 0) {
772                         log_error("ppoll() failed: %m");
773                         goto finish;
774                 }
775         }
776
777         r = 0;
778
779 finish:
780         sd_bus_flush(a);
781         sd_bus_flush(b);
782
783         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
784 }