chiark / gitweb /
util: introduce CMSG_FOREACH() macro and make use of it everywhere
[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, 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, &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, 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, rootfd = -1;
124         union {
125                 struct cmsghdr cmsghdr;
126                 uint8_t buf[CMSG_SPACE(sizeof(int))];
127         } control = {};
128         struct msghdr mh = {
129                 .msg_control = &control,
130                 .msg_controllen = sizeof(control),
131         };
132         struct cmsghdr *cmsg;
133         pid_t child;
134         siginfo_t si;
135         int r;
136         _cleanup_close_ int fd = -1;
137
138         assert(b);
139         assert(b->input_fd < 0);
140         assert(b->output_fd < 0);
141         assert(b->nspid > 0 || b->machine);
142
143         if (b->nspid <= 0) {
144                 r = container_get_leader(b->machine, &b->nspid);
145                 if (r < 0)
146                         return r;
147         }
148
149         r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
150         if (r < 0)
151                 return r;
152
153         if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
154                 return -errno;
155
156         child = fork();
157         if (child < 0)
158                 return -errno;
159
160         if (child == 0) {
161                 pid_t grandchild;
162
163                 pair[0] = safe_close(pair[0]);
164
165                 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
166                 if (r < 0)
167                         _exit(EXIT_FAILURE);
168
169                 /* We just changed PID namespace, however it will only
170                  * take effect on the children we now fork. Hence,
171                  * let's fork another time, and connect from this
172                  * grandchild, so that kdbus only sees the credentials
173                  * of this process which comes from within the
174                  * container, and not outside of it */
175
176                 grandchild = fork();
177                 if (grandchild < 0)
178                         _exit(EXIT_FAILURE);
179
180                 if (grandchild == 0) {
181
182                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
183                         if (fd < 0)
184                                 _exit(EXIT_FAILURE);
185
186                         cmsg = CMSG_FIRSTHDR(&mh);
187                         cmsg->cmsg_level = SOL_SOCKET;
188                         cmsg->cmsg_type = SCM_RIGHTS;
189                         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
190                         memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
191
192                         mh.msg_controllen = cmsg->cmsg_len;
193
194                         if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
195                                 _exit(EXIT_FAILURE);
196
197                         _exit(EXIT_SUCCESS);
198                 }
199
200                 r = wait_for_terminate(grandchild, &si);
201                 if (r < 0)
202                         _exit(EXIT_FAILURE);
203
204                 if (si.si_code != CLD_EXITED)
205                         _exit(EXIT_FAILURE);
206
207                 _exit(si.si_status);
208         }
209
210         pair[1] = safe_close(pair[1]);
211
212         r = wait_for_terminate(child, &si);
213         if (r < 0)
214                 return r;
215
216         if (si.si_code != CLD_EXITED)
217                 return -EIO;
218
219         if (si.si_status != EXIT_SUCCESS)
220                 return -EIO;
221
222         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
223                 return -errno;
224
225         CMSG_FOREACH(cmsg, &mh)
226                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
227                         int *fds;
228                         unsigned n_fds;
229
230                         fds = (int*) CMSG_DATA(cmsg);
231                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
232
233                         if (n_fds != 1) {
234                                 close_many(fds, n_fds);
235                                 return -EIO;
236                         }
237
238                         fd = fds[0];
239                 }
240
241         b->input_fd = b->output_fd = fd;
242         fd = -1;
243
244         return bus_kernel_take_fd(b);
245 }