chiark / gitweb /
bus: when connecting to a container's system bus, double fork after joining the conta...
[elogind.git] / src / libsystemd-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 "fileio.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, rootfd = -1;
33         pid_t leader, 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
41         r = container_get_leader(b->machine, &leader);
42         if (r < 0)
43                 return r;
44
45         r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
46         if (r < 0)
47                 return r;
48
49         b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
50         if (b->input_fd < 0)
51                 return -errno;
52
53         b->output_fd = b->input_fd;
54
55         r = bus_socket_setup(b);
56         if (r < 0)
57                 return r;
58
59         child = fork();
60         if (child < 0)
61                 return -errno;
62
63         if (child == 0) {
64                 pid_t grandchild;
65
66                 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
67                 if (r < 0)
68                         _exit(255);
69
70                 /* We just changed PID namespace, however it will only
71                  * take effect on the children we now fork. Hence,
72                  * let's fork another time, and connect from this
73                  * grandchild, so that SO_PEERCRED of our connection
74                  * comes from a process from within the container, and
75                  * not outside of it */
76
77                 grandchild = fork();
78                 if (grandchild < 0)
79                         _exit(255);
80
81                 if (grandchild == 0) {
82
83                         r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
84                         if (r < 0) {
85                                 if (errno == EINPROGRESS)
86                                         _exit(1);
87
88                                 _exit(255);
89                         }
90
91                         _exit(EXIT_SUCCESS);
92                 }
93
94                 r = wait_for_terminate(grandchild, &si);
95                 if (r < 0)
96                         _exit(255);
97
98                 if (si.si_code != CLD_EXITED)
99                         _exit(255);
100
101                 _exit(si.si_status);
102         }
103
104         r = wait_for_terminate(child, &si);
105         if (r < 0)
106                 return r;
107
108         if (si.si_code != CLD_EXITED)
109                 return -EIO;
110
111         if (si.si_status == 1)
112                 return 1;
113
114         if (si.si_status != EXIT_SUCCESS)
115                 return -EIO;
116
117         return bus_socket_start_auth(b);
118 }
119
120 int bus_container_connect_kernel(sd_bus *b) {
121         _cleanup_close_pipe_ int pair[2] = { -1, -1 };
122         _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
123         union {
124                 struct cmsghdr cmsghdr;
125                 uint8_t buf[CMSG_SPACE(sizeof(int))];
126         } control = {};
127         struct msghdr mh = {
128                 .msg_control = &control,
129                 .msg_controllen = sizeof(control),
130         };
131         struct cmsghdr *cmsg;
132         pid_t leader, child;
133         siginfo_t si;
134         int r;
135         _cleanup_close_ int fd = -1;
136
137         assert(b);
138         assert(b->input_fd < 0);
139         assert(b->output_fd < 0);
140
141         r = container_get_leader(b->machine, &leader);
142         if (r < 0)
143                 return r;
144
145         r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
146         if (r < 0)
147                 return r;
148
149         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
150                 return -errno;
151
152         child = fork();
153         if (child < 0)
154                 return -errno;
155
156         if (child == 0) {
157                 pid_t grandchild;
158
159                 close_nointr_nofail(pair[0]);
160                 pair[0] = -1;
161
162                 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
163                 if (r < 0)
164                         _exit(EXIT_FAILURE);
165
166                 /* We just changed PID namespace, however it will only
167                  * take effect on the children we now fork. Hence,
168                  * let's fork another time, and connect from this
169                  * grandchild, so that kdbus only sees the credentials
170                  * of this process which comes from within the
171                  * container, and not outside of it */
172
173                 grandchild = fork();
174                 if (grandchild < 0)
175                         _exit(EXIT_FAILURE);
176
177                 if (grandchild == 0) {
178
179                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
180                         if (fd < 0)
181                                 _exit(EXIT_FAILURE);
182
183                         cmsg = CMSG_FIRSTHDR(&mh);
184                         cmsg->cmsg_level = SOL_SOCKET;
185                         cmsg->cmsg_type = SCM_RIGHTS;
186                         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
187                         memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
188
189                         mh.msg_controllen = cmsg->cmsg_len;
190
191                         if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
192                                 _exit(EXIT_FAILURE);
193
194                         _exit(EXIT_SUCCESS);
195                 }
196
197                 r = wait_for_terminate(grandchild, &si);
198                 if (r < 0)
199                         _exit(EXIT_FAILURE);
200
201                 if (si.si_code != CLD_EXITED)
202                         _exit(EXIT_FAILURE);
203
204                 _exit(si.si_status);
205         }
206
207         close_nointr_nofail(pair[1]);
208         pair[1] = -1;
209
210         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
211                 return -errno;
212
213         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
214                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
215                         int *fds;
216                         unsigned n_fds;
217
218                         fds = (int*) CMSG_DATA(cmsg);
219                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
220
221                         if (n_fds != 1) {
222                                 close_many(fds, n_fds);
223                                 return -EIO;
224                         }
225
226                         fd = fds[0];
227                 }
228
229         r = wait_for_terminate(child, &si);
230         if (r < 0)
231                 return r;
232
233         if (si.si_code != CLD_EXITED)
234                 return -EIO;
235
236         if (si.si_status != EXIT_SUCCESS)
237                 return -EIO;
238
239         b->input_fd = b->output_fd = fd;
240         fd = -1;
241
242         return bus_kernel_take_fd(b);
243 }