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