chiark / gitweb /
sd-bus: when connecting to a kdbus container bus pass error up
[elogind.git] / src / libelogind / sd-bus / bus-container.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 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 <unistd.h>
23 #include <fcntl.h>
24
25 #include "util.h"
26 #include "process-util.h"
27 #include "bus-internal.h"
28 #include "bus-socket.h"
29 #include "bus-container.h"
30
31 int bus_container_connect_socket(sd_bus *b) {
32         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
33         pid_t child;
34         siginfo_t si;
35         int r;
36
37         assert(b);
38         assert(b->input_fd < 0);
39         assert(b->output_fd < 0);
40         assert(b->nspid > 0 || b->machine);
41
42         if (b->nspid <= 0) {
43                 r = container_get_leader(b->machine, &b->nspid);
44                 if (r < 0)
45                         return r;
46         }
47
48         r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
49         if (r < 0)
50                 return r;
51
52         b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
53         if (b->input_fd < 0)
54                 return -errno;
55
56         b->output_fd = b->input_fd;
57
58         bus_socket_setup(b);
59
60         child = fork();
61         if (child < 0)
62                 return -errno;
63
64         if (child == 0) {
65                 pid_t grandchild;
66
67                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
68                 if (r < 0)
69                         _exit(255);
70
71                 /* We just changed PID namespace, however it will only
72                  * take effect on the children we now fork. Hence,
73                  * let's fork another time, and connect from this
74                  * grandchild, so that SO_PEERCRED of our connection
75                  * comes from a process from within the container, and
76                  * not outside of it */
77
78                 grandchild = fork();
79                 if (grandchild < 0)
80                         _exit(255);
81
82                 if (grandchild == 0) {
83
84                         r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
85                         if (r < 0) {
86                                 if (errno == EINPROGRESS)
87                                         _exit(1);
88
89                                 _exit(255);
90                         }
91
92                         _exit(EXIT_SUCCESS);
93                 }
94
95                 r = wait_for_terminate(grandchild, &si);
96                 if (r < 0)
97                         _exit(255);
98
99                 if (si.si_code != CLD_EXITED)
100                         _exit(255);
101
102                 _exit(si.si_status);
103         }
104
105         r = wait_for_terminate(child, &si);
106         if (r < 0)
107                 return r;
108
109         if (si.si_code != CLD_EXITED)
110                 return -EIO;
111
112         if (si.si_status == 1)
113                 return 1;
114
115         if (si.si_status != EXIT_SUCCESS)
116                 return -EIO;
117
118         return bus_socket_start_auth(b);
119 }
120
121 int bus_container_connect_kernel(sd_bus *b) {
122         _cleanup_close_pair_ int pair[2] = { -1, -1 };
123         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
124         union {
125                 struct cmsghdr cmsghdr;
126                 uint8_t buf[CMSG_SPACE(sizeof(int))];
127         } control = {};
128         int error_buf = 0;
129         struct iovec iov = {
130                 .iov_base = &error_buf,
131                 .iov_len = sizeof(error_buf),
132         };
133         struct msghdr mh = {
134                 .msg_control = &control,
135                 .msg_controllen = sizeof(control),
136                 .msg_iov = &iov,
137                 .msg_iovlen = 1,
138         };
139         struct cmsghdr *cmsg;
140         pid_t child;
141         siginfo_t si;
142         int r, fd = -1;
143         ssize_t n;
144
145         assert(b);
146         assert(b->input_fd < 0);
147         assert(b->output_fd < 0);
148         assert(b->nspid > 0 || b->machine);
149
150         if (b->nspid <= 0) {
151                 r = container_get_leader(b->machine, &b->nspid);
152                 if (r < 0)
153                         return r;
154         }
155
156         r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &usernsfd, &rootfd);
157         if (r < 0)
158                 return r;
159
160         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
161                 return -errno;
162
163         child = fork();
164         if (child < 0)
165                 return -errno;
166
167         if (child == 0) {
168                 pid_t grandchild;
169
170                 pair[0] = safe_close(pair[0]);
171
172                 r = namespace_enter(pidnsfd, mntnsfd, -1, usernsfd, rootfd);
173                 if (r < 0)
174                         _exit(EXIT_FAILURE);
175
176                 /* We just changed PID namespace, however it will only
177                  * take effect on the children we now fork. Hence,
178                  * let's fork another time, and connect from this
179                  * grandchild, so that kdbus only sees the credentials
180                  * of this process which comes from within the
181                  * container, and not outside of it */
182
183                 grandchild = fork();
184                 if (grandchild < 0)
185                         _exit(EXIT_FAILURE);
186
187                 if (grandchild == 0) {
188                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
189                         if (fd < 0) {
190                                 /* Try to send error up */
191                                 error_buf = errno;
192                                 (void) write(pair[1], &error_buf, sizeof(error_buf));
193                                 _exit(EXIT_FAILURE);
194                         }
195
196                         cmsg = CMSG_FIRSTHDR(&mh);
197                         cmsg->cmsg_level = SOL_SOCKET;
198                         cmsg->cmsg_type = SCM_RIGHTS;
199                         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
200                         memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
201
202                         mh.msg_controllen = cmsg->cmsg_len;
203
204                         if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
205                                 _exit(EXIT_FAILURE);
206
207                         _exit(EXIT_SUCCESS);
208                 }
209
210                 r = wait_for_terminate(grandchild, &si);
211                 if (r < 0)
212                         _exit(EXIT_FAILURE);
213
214                 if (si.si_code != CLD_EXITED)
215                         _exit(EXIT_FAILURE);
216
217                 _exit(si.si_status);
218         }
219
220         pair[1] = safe_close(pair[1]);
221
222         r = wait_for_terminate(child, &si);
223         if (r < 0)
224                 return r;
225
226         n = recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC);
227         if (n < 0)
228                 return -errno;
229
230         CMSG_FOREACH(cmsg, &mh) {
231                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
232                         int *fds;
233                         unsigned n_fds;
234
235                         assert(fd < 0);
236
237                         fds = (int*) CMSG_DATA(cmsg);
238                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
239
240                         if (n_fds != 1) {
241                                 close_many(fds, n_fds);
242                                 return -EIO;
243                         }
244
245                         fd = fds[0];
246                 }
247         }
248
249         /* If there's an fd passed, we are good. */
250         if (fd >= 0) {
251                 b->input_fd = b->output_fd = fd;
252                 return bus_kernel_take_fd(b);
253         }
254
255         /* If there's an error passed, use it */
256         if (n == sizeof(error_buf) && error_buf > 0)
257                 return -error_buf;
258
259         /* Otherwise, we have no clue */
260         return -EIO;
261 }