chiark / gitweb /
Merge commit 'b39a2770ba55637da80e2e389222c59dbea73507'
[elogind.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, NULL, &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, -1, 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_pair_ 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, NULL, &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                 pair[0] = safe_close(pair[0]);
158
159                 r = namespace_enter(pidnsfd, mntnsfd, -1, rootfd);
160                 if (r < 0)
161                         _exit(EXIT_FAILURE);
162
163                 /* We just changed PID namespace, however it will only
164                  * take effect on the children we now fork. Hence,
165                  * let's fork another time, and connect from this
166                  * grandchild, so that kdbus only sees the credentials
167                  * of this process which comes from within the
168                  * container, and not outside of it */
169
170                 grandchild = fork();
171                 if (grandchild < 0)
172                         _exit(EXIT_FAILURE);
173
174                 if (grandchild == 0) {
175
176                         fd = open(b->kernel, O_RDWR|O_NOCTTY|O_CLOEXEC);
177                         if (fd < 0)
178                                 _exit(EXIT_FAILURE);
179
180                         cmsg = CMSG_FIRSTHDR(&mh);
181                         cmsg->cmsg_level = SOL_SOCKET;
182                         cmsg->cmsg_type = SCM_RIGHTS;
183                         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
184                         memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
185
186                         mh.msg_controllen = cmsg->cmsg_len;
187
188                         if (sendmsg(pair[1], &mh, MSG_NOSIGNAL) < 0)
189                                 _exit(EXIT_FAILURE);
190
191                         _exit(EXIT_SUCCESS);
192                 }
193
194                 r = wait_for_terminate(grandchild, &si);
195                 if (r < 0)
196                         _exit(EXIT_FAILURE);
197
198                 if (si.si_code != CLD_EXITED)
199                         _exit(EXIT_FAILURE);
200
201                 _exit(si.si_status);
202         }
203
204         pair[1] = safe_close(pair[1]);
205
206         r = wait_for_terminate(child, &si);
207         if (r < 0)
208                 return r;
209
210         if (si.si_code != CLD_EXITED)
211                 return -EIO;
212
213         if (si.si_status != EXIT_SUCCESS)
214                 return -EIO;
215
216         if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
217                 return -errno;
218
219         for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
220                 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
221                         int *fds;
222                         unsigned n_fds;
223
224                         fds = (int*) CMSG_DATA(cmsg);
225                         n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
226
227                         if (n_fds != 1) {
228                                 close_many(fds, n_fds);
229                                 return -EIO;
230                         }
231
232                         fd = fds[0];
233                 }
234
235         b->input_fd = b->output_fd = fd;
236         fd = -1;
237
238         return bus_kernel_take_fd(b);
239 }