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