1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012-2013 The FreeBSD Foundation
5 * Copyright (c) 2015 Mariusz Zaborski <[email protected]>
6 * All rights reserved.
7 *
8 * This software was developed by Pawel Jakub Dawidek under sponsorship from
9 * the FreeBSD Foundation.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 #include <sys/nv.h>
37 #include <sys/procdesc.h>
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <stdbool.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45
46 #include "libcasper.h"
47 #include "libcasper_impl.h"
48
49 #define CASPER_VALID_FLAGS (CASPER_NO_UNIQ)
50
51 /*
52 * Structure describing communication channel between two separated processes.
53 */
54 #define CAP_CHANNEL_MAGIC 0xcac8a31
55 struct cap_channel {
56 /*
57 * Magic value helps to ensure that a pointer to the right structure is
58 * passed to our functions.
59 */
60 int cch_magic;
61 /* Socket descriptor for IPC. */
62 int cch_sock;
63 /* Process descriptor for casper. */
64 int cch_pd;
65 /* Flags to communicate with casper. */
66 int cch_flags;
67 };
68
69 static bool
cap_add_pd(cap_channel_t * chan,int pd)70 cap_add_pd(cap_channel_t *chan, int pd)
71 {
72
73 if (!fd_is_valid(pd))
74 return (false);
75 chan->cch_pd = pd;
76 return (true);
77 }
78
79 int
cap_channel_flags(const cap_channel_t * chan)80 cap_channel_flags(const cap_channel_t *chan)
81 {
82
83 return (chan->cch_flags);
84 }
85
86 cap_channel_t *
cap_init(void)87 cap_init(void)
88 {
89 pid_t pid;
90 int sock[2], serrno, pfd;
91 bool ret;
92 cap_channel_t *chan;
93
94 if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
95 sock) == -1) {
96 return (NULL);
97 }
98
99 pid = pdfork(&pfd, 0);
100 if (pid == 0) {
101 /* Child. */
102 close(sock[0]);
103 casper_main_loop(sock[1]);
104 /* NOTREACHED. */
105 } else if (pid > 0) {
106 /* Parent. */
107 close(sock[1]);
108 chan = cap_wrap(sock[0], 0);
109 if (chan == NULL) {
110 serrno = errno;
111 close(sock[0]);
112 close(pfd);
113 errno = serrno;
114 return (NULL);
115 }
116 ret = cap_add_pd(chan, pfd);
117 assert(ret);
118 return (chan);
119 }
120
121 /* Error. */
122 serrno = errno;
123 close(sock[0]);
124 close(sock[1]);
125 errno = serrno;
126 return (NULL);
127 }
128
129 cap_channel_t *
cap_wrap(int sock,int flags)130 cap_wrap(int sock, int flags)
131 {
132 cap_channel_t *chan;
133
134 if (!fd_is_valid(sock))
135 return (NULL);
136
137 if ((flags & CASPER_VALID_FLAGS) != flags)
138 return (NULL);
139
140 chan = malloc(sizeof(*chan));
141 if (chan != NULL) {
142 chan->cch_sock = sock;
143 chan->cch_pd = -1;
144 chan->cch_flags = flags;
145 chan->cch_magic = CAP_CHANNEL_MAGIC;
146 }
147
148 return (chan);
149 }
150
151 int
cap_unwrap(cap_channel_t * chan,int * flags)152 cap_unwrap(cap_channel_t *chan, int *flags)
153 {
154 int sock;
155
156 assert(chan != NULL);
157 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
158
159 sock = chan->cch_sock;
160 if (chan->cch_pd != -1)
161 close(chan->cch_pd);
162 if (flags != NULL)
163 *flags = chan->cch_flags;
164 chan->cch_magic = 0;
165 free(chan);
166
167 return (sock);
168 }
169
170 cap_channel_t *
cap_clone(const cap_channel_t * chan)171 cap_clone(const cap_channel_t *chan)
172 {
173 cap_channel_t *newchan;
174 nvlist_t *nvl;
175 int newsock;
176
177 assert(chan != NULL);
178 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
179
180 nvl = nvlist_create(channel_nvlist_flags(chan));
181 nvlist_add_string(nvl, "cmd", "clone");
182 nvl = cap_xfer_nvlist(chan, nvl);
183 if (nvl == NULL)
184 return (NULL);
185 if (nvlist_get_number(nvl, "error") != 0) {
186 errno = (int)nvlist_get_number(nvl, "error");
187 nvlist_destroy(nvl);
188 return (NULL);
189 }
190 newsock = nvlist_take_descriptor(nvl, "sock");
191 nvlist_destroy(nvl);
192 newchan = cap_wrap(newsock, chan->cch_flags);
193 if (newchan == NULL) {
194 int serrno;
195
196 serrno = errno;
197 close(newsock);
198 errno = serrno;
199 }
200
201 return (newchan);
202 }
203
204 void
cap_close(cap_channel_t * chan)205 cap_close(cap_channel_t *chan)
206 {
207
208 assert(chan != NULL);
209 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
210
211 chan->cch_magic = 0;
212 if (chan->cch_pd != -1)
213 close(chan->cch_pd);
214 close(chan->cch_sock);
215 free(chan);
216 }
217
218 int
cap_sock(const cap_channel_t * chan)219 cap_sock(const cap_channel_t *chan)
220 {
221
222 assert(chan != NULL);
223 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
224
225 return (chan->cch_sock);
226 }
227
228 int
cap_limit_set(const cap_channel_t * chan,nvlist_t * limits)229 cap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
230 {
231 nvlist_t *nvlmsg;
232 int error;
233
234 nvlmsg = nvlist_create(channel_nvlist_flags(chan));
235 nvlist_add_string(nvlmsg, "cmd", "limit_set");
236 nvlist_add_nvlist(nvlmsg, "limits", limits);
237 nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
238 if (nvlmsg == NULL) {
239 nvlist_destroy(limits);
240 return (-1);
241 }
242 error = (int)nvlist_get_number(nvlmsg, "error");
243 nvlist_destroy(nvlmsg);
244 nvlist_destroy(limits);
245 if (error != 0) {
246 errno = error;
247 return (-1);
248 }
249 return (0);
250 }
251
252 int
cap_limit_get(const cap_channel_t * chan,nvlist_t ** limitsp)253 cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
254 {
255 nvlist_t *nvlmsg;
256 int error;
257
258 nvlmsg = nvlist_create(channel_nvlist_flags(chan));
259 nvlist_add_string(nvlmsg, "cmd", "limit_get");
260 nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
261 if (nvlmsg == NULL)
262 return (-1);
263 error = (int)nvlist_get_number(nvlmsg, "error");
264 if (error != 0) {
265 nvlist_destroy(nvlmsg);
266 errno = error;
267 return (-1);
268 }
269 if (nvlist_exists_null(nvlmsg, "limits"))
270 *limitsp = NULL;
271 else
272 *limitsp = nvlist_take_nvlist(nvlmsg, "limits");
273 nvlist_destroy(nvlmsg);
274 return (0);
275 }
276
277 int
cap_send_nvlist(const cap_channel_t * chan,const nvlist_t * nvl)278 cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
279 {
280
281 assert(chan != NULL);
282 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
283
284 return (nvlist_send(chan->cch_sock, nvl));
285 }
286
287 nvlist_t *
cap_recv_nvlist(const cap_channel_t * chan)288 cap_recv_nvlist(const cap_channel_t *chan)
289 {
290
291 assert(chan != NULL);
292 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
293
294 return (nvlist_recv(chan->cch_sock,
295 channel_nvlist_flags(chan)));
296 }
297
298 nvlist_t *
cap_xfer_nvlist(const cap_channel_t * chan,nvlist_t * nvl)299 cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl)
300 {
301
302 assert(chan != NULL);
303 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
304
305 return (nvlist_xfer(chan->cch_sock, nvl,
306 channel_nvlist_flags(chan)));
307 }
308
309 cap_channel_t *
cap_service_open(const cap_channel_t * chan,const char * name)310 cap_service_open(const cap_channel_t *chan, const char *name)
311 {
312 cap_channel_t *newchan;
313 nvlist_t *nvl;
314 int sock, error;
315 int flags;
316
317 sock = -1;
318
319 nvl = nvlist_create(channel_nvlist_flags(chan));
320 nvlist_add_string(nvl, "cmd", "open");
321 nvlist_add_string(nvl, "service", name);
322 nvl = cap_xfer_nvlist(chan, nvl);
323 if (nvl == NULL)
324 return (NULL);
325 error = (int)nvlist_get_number(nvl, "error");
326 if (error != 0) {
327 nvlist_destroy(nvl);
328 errno = error;
329 return (NULL);
330 }
331 sock = nvlist_take_descriptor(nvl, "chanfd");
332 flags = nvlist_take_number(nvl, "chanflags");
333 assert(sock >= 0);
334 nvlist_destroy(nvl);
335 nvl = NULL;
336 newchan = cap_wrap(sock, flags);
337 if (newchan == NULL)
338 goto fail;
339 return (newchan);
340 fail:
341 error = errno;
342 close(sock);
343 errno = error;
344 return (NULL);
345 }
346
347 int
cap_service_limit(const cap_channel_t * chan,const char * const * names,size_t nnames)348 cap_service_limit(const cap_channel_t *chan, const char * const *names,
349 size_t nnames)
350 {
351 nvlist_t *limits;
352 unsigned int i;
353
354 limits = nvlist_create(channel_nvlist_flags(chan));
355 for (i = 0; i < nnames; i++)
356 nvlist_add_null(limits, names[i]);
357 return (cap_limit_set(chan, limits));
358 }
359