Line data Source code
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 0 : int bus_container_connect_socket(sd_bus *b) {
32 0 : _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
33 : pid_t child;
34 : siginfo_t si;
35 : int r;
36 :
37 0 : assert(b);
38 0 : assert(b->input_fd < 0);
39 0 : assert(b->output_fd < 0);
40 0 : assert(b->nspid > 0 || b->machine);
41 :
42 0 : if (b->nspid <= 0) {
43 0 : r = container_get_leader(b->machine, &b->nspid);
44 0 : if (r < 0)
45 0 : return r;
46 : }
47 :
48 0 : r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
49 0 : if (r < 0)
50 0 : return r;
51 :
52 0 : b->input_fd = socket(b->sockaddr.sa.sa_family, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
53 0 : if (b->input_fd < 0)
54 0 : return -errno;
55 :
56 0 : b->output_fd = b->input_fd;
57 :
58 0 : bus_socket_setup(b);
59 :
60 0 : child = fork();
61 0 : if (child < 0)
62 0 : return -errno;
63 :
64 0 : if (child == 0) {
65 : pid_t grandchild;
66 :
67 0 : r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
68 0 : if (r < 0)
69 0 : _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 0 : grandchild = fork();
79 0 : if (grandchild < 0)
80 0 : _exit(255);
81 :
82 0 : if (grandchild == 0) {
83 :
84 0 : r = connect(b->input_fd, &b->sockaddr.sa, b->sockaddr_size);
85 0 : if (r < 0) {
86 0 : if (errno == EINPROGRESS)
87 0 : _exit(1);
88 :
89 0 : _exit(255);
90 : }
91 :
92 0 : _exit(EXIT_SUCCESS);
93 : }
94 :
95 0 : r = wait_for_terminate(grandchild, &si);
96 0 : if (r < 0)
97 0 : _exit(255);
98 :
99 0 : if (si.si_code != CLD_EXITED)
100 0 : _exit(255);
101 :
102 0 : _exit(si.si_status);
103 : }
104 :
105 0 : r = wait_for_terminate(child, &si);
106 0 : if (r < 0)
107 0 : return r;
108 :
109 0 : if (si.si_code != CLD_EXITED)
110 0 : return -EIO;
111 :
112 0 : if (si.si_status == 1)
113 0 : return 1;
114 :
115 0 : if (si.si_status != EXIT_SUCCESS)
116 0 : return -EIO;
117 :
118 0 : return bus_socket_start_auth(b);
119 : }
120 :
121 0 : int bus_container_connect_kernel(sd_bus *b) {
122 0 : _cleanup_close_pair_ int pair[2] = { -1, -1 };
123 0 : _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
124 : union {
125 : struct cmsghdr cmsghdr;
126 : uint8_t buf[CMSG_SPACE(sizeof(int))];
127 0 : } control = {};
128 0 : 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 0 : _cleanup_close_ int fd = -1;
137 :
138 0 : assert(b);
139 0 : assert(b->input_fd < 0);
140 0 : assert(b->output_fd < 0);
141 0 : assert(b->nspid > 0 || b->machine);
142 :
143 0 : if (b->nspid <= 0) {
144 0 : r = container_get_leader(b->machine, &b->nspid);
145 0 : if (r < 0)
146 0 : return r;
147 : }
148 :
149 0 : r = namespace_open(b->nspid, &pidnsfd, &mntnsfd, NULL, &rootfd);
150 0 : if (r < 0)
151 0 : return r;
152 :
153 0 : if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
154 0 : return -errno;
155 :
156 0 : child = fork();
157 0 : if (child < 0)
158 0 : return -errno;
159 :
160 0 : if (child == 0) {
161 : pid_t grandchild;
162 :
163 0 : pair[0] = safe_close(pair[0]);
164 :
165 0 : r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
166 0 : if (r < 0)
167 0 : _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 0 : grandchild = fork();
177 0 : if (grandchild < 0)
178 0 : _exit(EXIT_FAILURE);
179 :
180 0 : if (grandchild == 0) {
181 :
182 0 : fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
183 0 : if (fd < 0)
184 0 : _exit(EXIT_FAILURE);
185 :
186 0 : cmsg = CMSG_FIRSTHDR(&mh);
187 0 : cmsg->cmsg_level = SOL_SOCKET;
188 0 : cmsg->cmsg_type = SCM_RIGHTS;
189 0 : cmsg->cmsg_len = CMSG_LEN(sizeof(int));
190 0 : memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
191 :
192 0 : mh.msg_controllen = cmsg->cmsg_len;
193 :
194 0 : if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
195 0 : _exit(EXIT_FAILURE);
196 :
197 0 : _exit(EXIT_SUCCESS);
198 : }
199 :
200 0 : r = wait_for_terminate(grandchild, &si);
201 0 : if (r < 0)
202 0 : _exit(EXIT_FAILURE);
203 :
204 0 : if (si.si_code != CLD_EXITED)
205 0 : _exit(EXIT_FAILURE);
206 :
207 0 : _exit(si.si_status);
208 : }
209 :
210 0 : pair[1] = safe_close(pair[1]);
211 :
212 0 : r = wait_for_terminate(child, &si);
213 0 : if (r < 0)
214 0 : return r;
215 :
216 0 : if (si.si_code != CLD_EXITED)
217 0 : return -EIO;
218 :
219 0 : if (si.si_status != EXIT_SUCCESS)
220 0 : return -EIO;
221 :
222 0 : if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
223 0 : return -errno;
224 :
225 0 : CMSG_FOREACH(cmsg, &mh)
226 0 : if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
227 : int *fds;
228 : unsigned n_fds;
229 :
230 0 : fds = (int*) CMSG_DATA(cmsg);
231 0 : n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
232 :
233 0 : if (n_fds != 1) {
234 0 : close_many(fds, n_fds);
235 0 : return -EIO;
236 : }
237 :
238 0 : fd = fds[0];
239 : }
240 :
241 0 : b->input_fd = b->output_fd = fd;
242 0 : fd = -1;
243 :
244 0 : return bus_kernel_take_fd(b);
245 : }
|