chiark / gitweb /
hwdb: Update database of Bluetooth company identifiers
[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, &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 }