]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-bus/bus-container.c
bus: make sure to request peer cred only after connect(), not before
[thirdparty/systemd.git] / src / libsystemd-bus / bus-container.c
CommitLineData
a7893c6b
LP
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
bc9fd78c 31int bus_container_connect_socket(sd_bus *b) {
a4475f57 32 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
a7893c6b 33 pid_t leader, child;
bc9fd78c 34 siginfo_t si;
a7893c6b
LP
35 int r;
36
37 assert(b);
38 assert(b->input_fd < 0);
39 assert(b->output_fd < 0);
40
bc9fd78c 41 r = container_get_leader(b->machine, &leader);
a7893c6b
LP
42 if (r < 0)
43 return r;
a7893c6b 44
a4475f57 45 r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
a7893c6b
LP
46 if (r < 0)
47 return r;
a7893c6b
LP
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
8f04d2eb 55 bus_socket_setup(b);
a7893c6b
LP
56
57 child = fork();
58 if (child < 0)
59 return -errno;
60
61 if (child == 0) {
5d6cf65f 62 pid_t grandchild;
a7893c6b 63
a4475f57 64 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
bc9fd78c 65 if (r < 0)
a7893c6b
LP
66 _exit(255);
67
5d6cf65f
LP
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 */
a7893c6b 74
5d6cf65f
LP
75 grandchild = fork();
76 if (grandchild < 0)
a7893c6b 77 _exit(255);
5d6cf65f
LP
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);
a7893c6b
LP
90 }
91
5d6cf65f
LP
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);
a7893c6b
LP
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
bc9fd78c 112 if (si.si_status != EXIT_SUCCESS)
a7893c6b
LP
113 return -EIO;
114
115 return bus_socket_start_auth(b);
116}
bc9fd78c
LP
117
118int bus_container_connect_kernel(sd_bus *b) {
119 _cleanup_close_pipe_ int pair[2] = { -1, -1 };
a4475f57 120 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1;
bc9fd78c
LP
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
a4475f57 143 r = namespace_open(leader, &pidnsfd, &mntnsfd, &rootfd);
bc9fd78c
LP
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) {
5d6cf65f
LP
155 pid_t grandchild;
156
bc9fd78c
LP
157 close_nointr_nofail(pair[0]);
158 pair[0] = -1;
159
a4475f57 160 r = namespace_enter(pidnsfd, mntnsfd, rootfd);
bc9fd78c
LP
161 if (r < 0)
162 _exit(EXIT_FAILURE);
163
5d6cf65f
LP
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)
bc9fd78c
LP
173 _exit(EXIT_FAILURE);
174
5d6cf65f
LP
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);
bc9fd78c 191
5d6cf65f
LP
192 _exit(EXIT_SUCCESS);
193 }
194
195 r = wait_for_terminate(grandchild, &si);
196 if (r < 0)
197 _exit(EXIT_FAILURE);
bc9fd78c 198
5d6cf65f 199 if (si.si_code != CLD_EXITED)
bc9fd78c
LP
200 _exit(EXIT_FAILURE);
201
5d6cf65f 202 _exit(si.si_status);
bc9fd78c
LP
203 }
204
205 close_nointr_nofail(pair[1]);
206 pair[1] = -1;
207
208 if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
209 return -errno;
210
211 for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
212 if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
213 int *fds;
214 unsigned n_fds;
215
216 fds = (int*) CMSG_DATA(cmsg);
217 n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
218
219 if (n_fds != 1) {
220 close_many(fds, n_fds);
221 return -EIO;
222 }
223
224 fd = fds[0];
225 }
226
227 r = wait_for_terminate(child, &si);
228 if (r < 0)
229 return r;
230
231 if (si.si_code != CLD_EXITED)
232 return -EIO;
233
234 if (si.si_status != EXIT_SUCCESS)
235 return -EIO;
236
237 b->input_fd = b->output_fd = fd;
238 fd = -1;
239
240 return bus_kernel_take_fd(b);
241}