chiark / gitweb /
bus-proxy: extract proxy into Proxy object
[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   Copyright 2013 Daniel Mack
8   Copyright 2014 Kay Sievers
9
10   systemd is free software; you can redistribute it and/or modify it
11   under the terms of the GNU Lesser General Public License as published by
12   the Free Software Foundation; either version 2.1 of the License, or
13   (at your option) any later version.
14
15   systemd is distributed in the hope that it will be useful, but
16   WITHOUT ANY WARRANTY; without even the implied warranty of
17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18   Lesser General Public License for more details.
19
20   You should have received a copy of the GNU Lesser General Public License
21   along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 ***/
23
24 #include <sys/socket.h>
25 #include <sys/un.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #include <sys/poll.h>
32 #include <stddef.h>
33 #include <getopt.h>
34
35 #include "log.h"
36 #include "util.h"
37 #include "socket-util.h"
38 #include "sd-daemon.h"
39 #include "sd-bus.h"
40 #include "bus-internal.h"
41 #include "bus-message.h"
42 #include "bus-util.h"
43 #include "build.h"
44 #include "strv.h"
45 #include "def.h"
46 #include "capability.h"
47 #include "bus-control.h"
48 #include "smack-util.h"
49 #include "set.h"
50 #include "bus-xml-policy.h"
51 #include "driver.h"
52 #include "proxy.h"
53 #include "synthesize.h"
54
55 static char *arg_address = NULL;
56 static char *arg_command_line_buffer = NULL;
57 static bool arg_drop_privileges = false;
58 static char **arg_configuration = NULL;
59
60 static int help(void) {
61
62         printf("%s [OPTIONS...]\n\n"
63                "Connect STDIO or a socket to a given bus address.\n\n"
64                "  -h --help               Show this help\n"
65                "     --version            Show package version\n"
66                "     --drop-privileges    Drop privileges\n"
67                "     --configuration=PATH Configuration file or directory\n"
68                "     --machine=MACHINE    Connect to specified machine\n"
69                "     --address=ADDRESS    Connect to the bus specified by ADDRESS\n"
70                "                          (default: " DEFAULT_SYSTEM_BUS_ADDRESS ")\n",
71                program_invocation_short_name);
72
73         return 0;
74 }
75
76 static int parse_argv(int argc, char *argv[]) {
77
78         enum {
79                 ARG_VERSION = 0x100,
80                 ARG_ADDRESS,
81                 ARG_DROP_PRIVILEGES,
82                 ARG_CONFIGURATION,
83                 ARG_MACHINE,
84         };
85
86         static const struct option options[] = {
87                 { "help",            no_argument,       NULL, 'h'                 },
88                 { "version",         no_argument,       NULL, ARG_VERSION         },
89                 { "address",         required_argument, NULL, ARG_ADDRESS         },
90                 { "drop-privileges", no_argument,       NULL, ARG_DROP_PRIVILEGES },
91                 { "configuration",   required_argument, NULL, ARG_CONFIGURATION   },
92                 { "machine",         required_argument, NULL, ARG_MACHINE         },
93                 {},
94         };
95
96         int c, r;
97
98         assert(argc >= 0);
99         assert(argv);
100
101         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
102
103                 switch (c) {
104
105                 case 'h':
106                         help();
107                         return 0;
108
109                 case ARG_VERSION:
110                         puts(PACKAGE_STRING);
111                         puts(SYSTEMD_FEATURES);
112                         return 0;
113
114                 case ARG_ADDRESS: {
115                         char *a;
116
117                         a = strdup(optarg);
118                         if (!a)
119                                 return log_oom();
120
121                         free(arg_address);
122                         arg_address = a;
123                         break;
124                 }
125
126                 case ARG_DROP_PRIVILEGES:
127                         arg_drop_privileges = true;
128                         break;
129
130                 case ARG_CONFIGURATION:
131                         r = strv_extend(&arg_configuration, optarg);
132                         if (r < 0)
133                                 return log_oom();
134                         break;
135
136                 case ARG_MACHINE: {
137                         _cleanup_free_ char *e = NULL;
138                         char *a;
139
140                         e = bus_address_escape(optarg);
141                         if (!e)
142                                 return log_oom();
143
144 #ifdef ENABLE_KDBUS
145                         a = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL);
146 #else
147                         a = strjoin("x-machine-unix:machine=", e, NULL);
148 #endif
149                         if (!a)
150                                 return log_oom();
151
152                         free(arg_address);
153                         arg_address = a;
154
155                         break;
156                 }
157
158                 case '?':
159                         return -EINVAL;
160
161                 default:
162                         assert_not_reached("Unhandled option");
163                 }
164
165         /* If the first command line argument is only "x" characters
166          * we'll write who we are talking to into it, so that "ps" is
167          * explanatory */
168         arg_command_line_buffer = argv[optind];
169         if (argc > optind + 1 || (arg_command_line_buffer && !in_charset(arg_command_line_buffer, "x"))) {
170                 log_error("Too many arguments");
171                 return -EINVAL;
172         }
173
174         if (!arg_address) {
175                 arg_address = strdup(DEFAULT_SYSTEM_BUS_ADDRESS);
176                 if (!arg_address)
177                         return log_oom();
178         }
179
180         return 1;
181 }
182
183 static int rename_service(sd_bus *a, sd_bus *b) {
184         _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
185         _cleanup_free_ char *p = NULL, *name = NULL;
186         const char *comm;
187         char **cmdline;
188         uid_t uid;
189         pid_t pid;
190         int r;
191
192         assert(a);
193         assert(b);
194
195         r = sd_bus_get_owner_creds(b, SD_BUS_CREDS_UID|SD_BUS_CREDS_PID|SD_BUS_CREDS_CMDLINE|SD_BUS_CREDS_COMM|SD_BUS_CREDS_AUGMENT, &creds);
196         if (r < 0)
197                 return r;
198
199         r = sd_bus_creds_get_uid(creds, &uid);
200         if (r < 0)
201                 return r;
202
203         r = sd_bus_creds_get_pid(creds, &pid);
204         if (r < 0)
205                 return r;
206
207         r = sd_bus_creds_get_cmdline(creds, &cmdline);
208         if (r < 0)
209                 return r;
210
211         r = sd_bus_creds_get_comm(creds, &comm);
212         if (r < 0)
213                 return r;
214
215         name = uid_to_name(uid);
216         if (!name)
217                 return -ENOMEM;
218
219         p = strv_join(cmdline, " ");
220         if (!p)
221                 return -ENOMEM;
222
223         /* The status string gets the full command line ... */
224         sd_notifyf(false,
225                    "STATUS=Processing requests from client PID "PID_FMT" (%s); UID "UID_FMT" (%s)",
226                    pid, p,
227                    uid, name);
228
229         /* ... and the argv line only the short comm */
230         if (arg_command_line_buffer) {
231                 size_t m, w;
232
233                 m = strlen(arg_command_line_buffer);
234                 w = snprintf(arg_command_line_buffer, m,
235                              "[PID "PID_FMT"/%s; UID "UID_FMT"/%s]",
236                              pid, comm,
237                              uid, name);
238
239                 if (m > w)
240                         memzero(arg_command_line_buffer + w, m - w);
241         }
242
243         log_debug("Running on behalf of PID "PID_FMT" (%s), UID "UID_FMT" (%s), %s",
244                   pid, p,
245                   uid, name,
246                   a->unique_name);
247
248         return 0;
249 }
250
251 int main(int argc, char *argv[]) {
252         _cleanup_(proxy_freep) Proxy *p = NULL;
253         int r, in_fd, out_fd;
254         uid_t original_uid;
255
256         log_set_target(LOG_TARGET_JOURNAL_OR_KMSG);
257         log_parse_environment();
258         log_open();
259
260         r = parse_argv(argc, argv);
261         if (r <= 0)
262                 goto finish;
263
264         r = sd_listen_fds(0);
265         if (r == 0) {
266                 in_fd = STDIN_FILENO;
267                 out_fd = STDOUT_FILENO;
268         } else if (r == 1) {
269                 in_fd = SD_LISTEN_FDS_START;
270                 out_fd = SD_LISTEN_FDS_START;
271         } else {
272                 log_error("Illegal number of file descriptors passed");
273                 goto finish;
274         }
275
276         original_uid = getuid();
277
278         if (arg_drop_privileges) {
279                 const char *user = "systemd-bus-proxy";
280                 uid_t uid;
281                 gid_t gid;
282
283                 r = get_user_creds(&user, &uid, &gid, NULL, NULL);
284                 if (r < 0) {
285                         log_error_errno(r, "Cannot resolve user name %s: %m", user);
286                         goto finish;
287                 }
288
289                 r = drop_privileges(uid, gid, 1ULL << CAP_IPC_OWNER);
290                 if (r < 0)
291                         goto finish;
292         }
293
294         r = proxy_new(&p, in_fd, out_fd, arg_address);
295         if (r < 0)
296                 goto finish;
297
298         r = proxy_load_policy(p, arg_configuration);
299         if (r < 0)
300                 goto finish;
301
302         r = proxy_hello_policy(p, original_uid);
303         if (r < 0)
304                 goto finish;
305
306         r = rename_service(p->dest_bus, p->local_bus);
307         if (r < 0)
308                 log_debug_errno(r, "Failed to rename process: %m");
309
310         r = proxy_run(p);
311
312 finish:
313         sd_notify(false,
314                   "STOPPING=1\n"
315                   "STATUS=Shutting down.");
316
317         strv_free(arg_configuration);
318         free(arg_address);
319
320         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
321 }