]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-container.c
Merge pull request #84 from blueyed/zsh-optimize-filter_units_by_property
[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 "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 }