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