1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2012-2013 The FreeBSD Foundation
5 * Copyright (c) 2015 Mariusz Zaborski <oshogbo@FreeBSD.org>
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/types.h>
34 #include <sys/socket.h>
35 #include <sys/nv.h>
36 #include <sys/procdesc.h>
37
38 #include <assert.h>
39 #include <errno.h>
40 #include <stdbool.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44
45 #include "libcasper.h"
46 #include "libcasper_impl.h"
47
48 #define CASPER_VALID_FLAGS (CASPER_NO_UNIQ)
49
50 /*
51 * Structure describing communication channel between two separated processes.
52 */
53 #define CAP_CHANNEL_MAGIC 0xcac8a31
54 struct cap_channel {
55 /*
56 * Magic value helps to ensure that a pointer to the right structure is
57 * passed to our functions.
58 */
59 int cch_magic;
60 /* Socket descriptor for IPC. */
61 int cch_sock;
62 /* Process descriptor for casper. */
63 int cch_pd;
64 /* Flags to communicate with casper. */
65 int cch_flags;
66 };
67
68 static bool
cap_add_pd(cap_channel_t * chan,int pd)69 cap_add_pd(cap_channel_t *chan, int pd)
70 {
71
72 if (!fd_is_valid(pd))
73 return (false);
74 chan->cch_pd = pd;
75 return (true);
76 }
77
78 int
cap_channel_flags(const cap_channel_t * chan)79 cap_channel_flags(const cap_channel_t *chan)
80 {
81
82 return (chan->cch_flags);
83 }
84
85 cap_channel_t *
cap_init(void)86 cap_init(void)
87 {
88 pid_t pid;
89 int sock[2], serrno, pfd;
90 bool ret;
91 cap_channel_t *chan;
92
93 if (socketpair(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
94 sock) == -1) {
95 return (NULL);
96 }
97
98 pid = pdfork(&pfd, 0);
99 if (pid == 0) {
100 /* Child. */
101 close(sock[0]);
102 casper_main_loop(sock[1]);
103 /* NOTREACHED. */
104 } else if (pid > 0) {
105 /* Parent. */
106 close(sock[1]);
107 chan = cap_wrap(sock[0], 0);
108 if (chan == NULL) {
109 serrno = errno;
110 close(sock[0]);
111 close(pfd);
112 errno = serrno;
113 return (NULL);
114 }
115 ret = cap_add_pd(chan, pfd);
116 assert(ret);
117 return (chan);
118 }
119
120 /* Error. */
121 serrno = errno;
122 close(sock[0]);
123 close(sock[1]);
124 errno = serrno;
125 return (NULL);
126 }
127
128 cap_channel_t *
cap_wrap(int sock,int flags)129 cap_wrap(int sock, int flags)
130 {
131 cap_channel_t *chan;
132
133 if (!fd_is_valid(sock))
134 return (NULL);
135
136 if ((flags & CASPER_VALID_FLAGS) != flags)
137 return (NULL);
138
139 chan = malloc(sizeof(*chan));
140 if (chan != NULL) {
141 chan->cch_sock = sock;
142 chan->cch_pd = -1;
143 chan->cch_flags = flags;
144 chan->cch_magic = CAP_CHANNEL_MAGIC;
145 }
146
147 return (chan);
148 }
149
150 int
cap_unwrap(cap_channel_t * chan,int * flags)151 cap_unwrap(cap_channel_t *chan, int *flags)
152 {
153 int sock;
154
155 assert(chan != NULL);
156 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
157
158 sock = chan->cch_sock;
159 if (chan->cch_pd != -1)
160 close(chan->cch_pd);
161 if (flags != NULL)
162 *flags = chan->cch_flags;
163 chan->cch_magic = 0;
164 free(chan);
165
166 return (sock);
167 }
168
169 cap_channel_t *
cap_clone(const cap_channel_t * chan)170 cap_clone(const cap_channel_t *chan)
171 {
172 cap_channel_t *newchan;
173 nvlist_t *nvl;
174 int newsock;
175
176 assert(chan != NULL);
177 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
178
179 nvl = nvlist_create(channel_nvlist_flags(chan));
180 nvlist_add_string(nvl, "cmd", "clone");
181 nvl = cap_xfer_nvlist(chan, nvl);
182 if (nvl == NULL)
183 return (NULL);
184 if (nvlist_get_number(nvl, "error") != 0) {
185 errno = (int)nvlist_get_number(nvl, "error");
186 nvlist_destroy(nvl);
187 return (NULL);
188 }
189 newsock = nvlist_take_descriptor(nvl, "sock");
190 nvlist_destroy(nvl);
191 newchan = cap_wrap(newsock, chan->cch_flags);
192 if (newchan == NULL) {
193 int serrno;
194
195 serrno = errno;
196 close(newsock);
197 errno = serrno;
198 }
199
200 return (newchan);
201 }
202
203 void
cap_close(cap_channel_t * chan)204 cap_close(cap_channel_t *chan)
205 {
206
207 assert(chan != NULL);
208 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
209
210 chan->cch_magic = 0;
211 if (chan->cch_pd != -1)
212 close(chan->cch_pd);
213 close(chan->cch_sock);
214 free(chan);
215 }
216
217 int
cap_sock(const cap_channel_t * chan)218 cap_sock(const cap_channel_t *chan)
219 {
220
221 assert(chan != NULL);
222 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
223
224 return (chan->cch_sock);
225 }
226
227 int
cap_limit_set(const cap_channel_t * chan,nvlist_t * limits)228 cap_limit_set(const cap_channel_t *chan, nvlist_t *limits)
229 {
230 nvlist_t *nvlmsg;
231 int error;
232
233 nvlmsg = nvlist_create(channel_nvlist_flags(chan));
234 nvlist_add_string(nvlmsg, "cmd", "limit_set");
235 nvlist_add_nvlist(nvlmsg, "limits", limits);
236 nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
237 if (nvlmsg == NULL) {
238 nvlist_destroy(limits);
239 return (-1);
240 }
241 error = (int)nvlist_get_number(nvlmsg, "error");
242 nvlist_destroy(nvlmsg);
243 nvlist_destroy(limits);
244 if (error != 0) {
245 errno = error;
246 return (-1);
247 }
248 return (0);
249 }
250
251 int
cap_limit_get(const cap_channel_t * chan,nvlist_t ** limitsp)252 cap_limit_get(const cap_channel_t *chan, nvlist_t **limitsp)
253 {
254 nvlist_t *nvlmsg;
255 int error;
256
257 nvlmsg = nvlist_create(channel_nvlist_flags(chan));
258 nvlist_add_string(nvlmsg, "cmd", "limit_get");
259 nvlmsg = cap_xfer_nvlist(chan, nvlmsg);
260 if (nvlmsg == NULL)
261 return (-1);
262 error = (int)nvlist_get_number(nvlmsg, "error");
263 if (error != 0) {
264 nvlist_destroy(nvlmsg);
265 errno = error;
266 return (-1);
267 }
268 if (nvlist_exists_null(nvlmsg, "limits"))
269 *limitsp = NULL;
270 else
271 *limitsp = nvlist_take_nvlist(nvlmsg, "limits");
272 nvlist_destroy(nvlmsg);
273 return (0);
274 }
275
276 int
cap_send_nvlist(const cap_channel_t * chan,const nvlist_t * nvl)277 cap_send_nvlist(const cap_channel_t *chan, const nvlist_t *nvl)
278 {
279
280 assert(chan != NULL);
281 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
282
283 return (nvlist_send(chan->cch_sock, nvl));
284 }
285
286 nvlist_t *
cap_recv_nvlist(const cap_channel_t * chan)287 cap_recv_nvlist(const cap_channel_t *chan)
288 {
289
290 assert(chan != NULL);
291 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
292
293 return (nvlist_recv(chan->cch_sock,
294 channel_nvlist_flags(chan)));
295 }
296
297 nvlist_t *
cap_xfer_nvlist(const cap_channel_t * chan,nvlist_t * nvl)298 cap_xfer_nvlist(const cap_channel_t *chan, nvlist_t *nvl)
299 {
300
301 assert(chan != NULL);
302 assert(chan->cch_magic == CAP_CHANNEL_MAGIC);
303
304 return (nvlist_xfer(chan->cch_sock, nvl,
305 channel_nvlist_flags(chan)));
306 }
307
308 cap_channel_t *
cap_service_open(const cap_channel_t * chan,const char * name)309 cap_service_open(const cap_channel_t *chan, const char *name)
310 {
311 cap_channel_t *newchan;
312 nvlist_t *nvl;
313 int sock, error;
314 int flags;
315
316 sock = -1;
317
318 nvl = nvlist_create(channel_nvlist_flags(chan));
319 nvlist_add_string(nvl, "cmd", "open");
320 nvlist_add_string(nvl, "service", name);
321 nvl = cap_xfer_nvlist(chan, nvl);
322 if (nvl == NULL)
323 return (NULL);
324 error = (int)nvlist_get_number(nvl, "error");
325 if (error != 0) {
326 nvlist_destroy(nvl);
327 errno = error;
328 return (NULL);
329 }
330 sock = nvlist_take_descriptor(nvl, "chanfd");
331 flags = nvlist_take_number(nvl, "chanflags");
332 assert(sock >= 0);
333 nvlist_destroy(nvl);
334 nvl = NULL;
335 newchan = cap_wrap(sock, flags);
336 if (newchan == NULL)
337 goto fail;
338 return (newchan);
339 fail:
340 error = errno;
341 close(sock);
342 errno = error;
343 return (NULL);
344 }
345
346 int
cap_service_limit(const cap_channel_t * chan,const char * const * names,size_t nnames)347 cap_service_limit(const cap_channel_t *chan, const char * const *names,
348 size_t nnames)
349 {
350 nvlist_t *limits;
351 unsigned int i;
352
353 limits = nvlist_create(channel_nvlist_flags(chan));
354 for (i = 0; i < nnames; i++)
355 nvlist_add_null(limits, names[i]);
356 return (cap_limit_set(chan, limits));
357 }
358