1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
5 * Copyright (c) 2020, Kyle Evans <kevans@FreeBSD.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/param.h>
30 #include <sys/jail.h>
31 #include <errno.h>
32 #include <jail.h>
33 #include <stdbool.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #include <lua.h>
38 #include <lauxlib.h>
39 #include <lualib.h>
40
41 #define JAIL_METATABLE "jail iterator metatable"
42
43 /*
44 * Taken from RhodiumToad's lspawn implementation, let static analyzers make
45 * better decisions about the behavior after we raise an error.
46 */
47 #if defined(LUA_VERSION_NUM) && defined(LUA_API)
48 LUA_API int (lua_error) (lua_State *L) __dead2;
49 #endif
50 #if defined(LUA_ERRFILE) && defined(LUALIB_API)
51 LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg) __dead2;
52 LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname) __dead2;
53 LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...) __dead2;
54 #endif
55
56 int luaopen_jail(lua_State *);
57
58 typedef bool (*getparam_filter)(const char *, void *);
59
60 static void getparam_table(lua_State *L, int paramindex,
61 struct jailparam *params, size_t paramoff, size_t *params_countp,
62 getparam_filter keyfilt, void *udata);
63
64 struct l_jail_iter {
65 struct jailparam *params;
66 size_t params_count;
67 int jid;
68 };
69
70 static bool
l_jail_filter(const char * param_name,void * data __unused)71 l_jail_filter(const char *param_name, void *data __unused)
72 {
73
74 /*
75 * Allowing lastjid will mess up our iteration over all jails on the
76 * system, as this is a special parameter that indicates where the search
77 * starts from. We'll always add jid and name, so just silently remove
78 * these.
79 */
80 return (strcmp(param_name, "lastjid") != 0 &&
81 strcmp(param_name, "jid") != 0 &&
82 strcmp(param_name, "name") != 0);
83 }
84
85 static int
l_jail_iter_next(lua_State * L)86 l_jail_iter_next(lua_State *L)
87 {
88 struct l_jail_iter *iter, **iterp;
89 struct jailparam *jp;
90 int serrno;
91
92 iterp = (struct l_jail_iter **)luaL_checkudata(L, 1, JAIL_METATABLE);
93 iter = *iterp;
94 luaL_argcheck(L, iter != NULL, 1, "closed jail iterator");
95
96 jp = iter->params;
97 /* Populate lastjid; we must keep it in params[0] for our sake. */
98 if (jailparam_import_raw(&jp[0], &iter->jid, sizeof(iter->jid))) {
99 jailparam_free(jp, iter->params_count);
100 free(jp);
101 free(iter);
102 *iterp = NULL;
103 return (luaL_error(L, "jailparam_import_raw: %s", jail_errmsg));
104 }
105
106 /* The list of requested params was populated back in l_list(). */
107 iter->jid = jailparam_get(jp, iter->params_count, 0);
108 if (iter->jid == -1) {
109 /*
110 * We probably got an ENOENT to signify the end of the jail
111 * listing, but just in case we didn't; stash it off and start
112 * cleaning up. We'll handle non-ENOENT errors later.
113 */
114 serrno = errno;
115 jailparam_free(jp, iter->params_count);
116 free(iter->params);
117 free(iter);
118 *iterp = NULL;
119 if (serrno != ENOENT)
120 return (luaL_error(L, "jailparam_get: %s",
121 strerror(serrno)));
122 return (0);
123 }
124
125 /*
126 * Finally, we'll fill in the return table with whatever parameters the
127 * user requested, in addition to the ones we forced with exception to
128 * lastjid.
129 */
130 lua_newtable(L);
131 for (size_t i = 0; i < iter->params_count; ++i) {
132 char *value;
133
134 jp = &iter->params[i];
135 if (strcmp(jp->jp_name, "lastjid") == 0)
136 continue;
137 value = jailparam_export(jp);
138 lua_pushstring(L, value);
139 lua_setfield(L, -2, jp->jp_name);
140 free(value);
141 }
142
143 return (1);
144 }
145
146 static int
l_jail_iter_close(lua_State * L)147 l_jail_iter_close(lua_State *L)
148 {
149 struct l_jail_iter *iter, **iterp;
150
151 /*
152 * Since we're using this as the __gc method as well, there's a good
153 * chance that it's already been cleaned up by iterating to the end of
154 * the list.
155 */
156 iterp = (struct l_jail_iter **)lua_touserdata(L, 1);
157 iter = *iterp;
158 if (iter == NULL)
159 return (0);
160
161 jailparam_free(iter->params, iter->params_count);
162 free(iter->params);
163 free(iter);
164 *iterp = NULL;
165 return (0);
166 }
167
168 static int
l_list(lua_State * L)169 l_list(lua_State *L)
170 {
171 struct l_jail_iter *iter;
172 int nargs;
173
174 nargs = lua_gettop(L);
175 if (nargs >= 1)
176 luaL_checktype(L, 1, LUA_TTABLE);
177
178 iter = malloc(sizeof(*iter));
179 if (iter == NULL)
180 return (luaL_error(L, "malloc: %s", strerror(errno)));
181
182 /*
183 * lastjid, jid, name + length of the table. This may be too much if
184 * we have duplicated one of those fixed parameters.
185 */
186 iter->params_count = 3 + (nargs != 0 ? lua_rawlen(L, 1) : 0);
187 iter->params = malloc(iter->params_count * sizeof(*iter->params));
188 if (iter->params == NULL) {
189 free(iter);
190 return (luaL_error(L, "malloc params: %s", strerror(errno)));
191 }
192
193 /* The :next() method will populate lastjid before jail_getparam(). */
194 if (jailparam_init(&iter->params[0], "lastjid") == -1) {
195 free(iter->params);
196 free(iter);
197 return (luaL_error(L, "jailparam_init: %s", jail_errmsg));
198 }
199 /* These two will get populated by jail_getparam(). */
200 if (jailparam_init(&iter->params[1], "jid") == -1) {
201 jailparam_free(iter->params, 1);
202 free(iter->params);
203 free(iter);
204 return (luaL_error(L, "jailparam_init: %s",
205 jail_errmsg));
206 }
207 if (jailparam_init(&iter->params[2], "name") == -1) {
208 jailparam_free(iter->params, 2);
209 free(iter->params);
210 free(iter);
211 return (luaL_error(L, "jailparam_init: %s",
212 jail_errmsg));
213 }
214
215 /*
216 * We only need to process additional arguments if we were given any.
217 * That is, we don't descend into getparam_table if we're passed nothing
218 * or an empty table.
219 */
220 iter->jid = 0;
221 if (iter->params_count != 3)
222 getparam_table(L, 1, iter->params, 2, &iter->params_count,
223 l_jail_filter, NULL);
224
225 /*
226 * Part of the iterator magic. We give it an iterator function with a
227 * metatable defining next() and close() that can be used for manual
228 * iteration. iter->jid is how we track which jail we last iterated, to
229 * be supplied as "lastjid".
230 */
231 lua_pushcfunction(L, l_jail_iter_next);
232 *(struct l_jail_iter **)lua_newuserdata(L,
233 sizeof(struct l_jail_iter **)) = iter;
234 luaL_getmetatable(L, JAIL_METATABLE);
235 lua_setmetatable(L, -2);
236 return (2);
237 }
238
239 static void
register_jail_metatable(lua_State * L)240 register_jail_metatable(lua_State *L)
241 {
242 luaL_newmetatable(L, JAIL_METATABLE);
243 lua_newtable(L);
244 lua_pushcfunction(L, l_jail_iter_next);
245 lua_setfield(L, -2, "next");
246 lua_pushcfunction(L, l_jail_iter_close);
247 lua_setfield(L, -2, "close");
248
249 lua_setfield(L, -2, "__index");
250
251 lua_pushcfunction(L, l_jail_iter_close);
252 lua_setfield(L, -2, "__gc");
253
254 lua_pop(L, 1);
255 }
256
257 static int
l_getid(lua_State * L)258 l_getid(lua_State *L)
259 {
260 const char *name;
261 int jid;
262
263 name = luaL_checkstring(L, 1);
264 jid = jail_getid(name);
265 if (jid == -1) {
266 lua_pushnil(L);
267 lua_pushstring(L, jail_errmsg);
268 return (2);
269 }
270 lua_pushinteger(L, jid);
271 return (1);
272 }
273
274 static int
l_getname(lua_State * L)275 l_getname(lua_State *L)
276 {
277 char *name;
278 int jid;
279
280 jid = luaL_checkinteger(L, 1);
281 name = jail_getname(jid);
282 if (name == NULL) {
283 lua_pushnil(L);
284 lua_pushstring(L, jail_errmsg);
285 return (2);
286 }
287 lua_pushstring(L, name);
288 free(name);
289 return (1);
290 }
291
292 static int
l_allparams(lua_State * L)293 l_allparams(lua_State *L)
294 {
295 struct jailparam *params;
296 int params_count;
297
298 params_count = jailparam_all(¶ms);
299 if (params_count == -1) {
300 lua_pushnil(L);
301 lua_pushstring(L, jail_errmsg);
302 return (2);
303 }
304 lua_newtable(L);
305 for (int i = 0; i < params_count; ++i) {
306 lua_pushstring(L, params[i].jp_name);
307 lua_rawseti(L, -2, i + 1);
308 }
309 jailparam_free(params, params_count);
310 free(params);
311 return (1);
312 }
313
314 static void
getparam_table(lua_State * L,int paramindex,struct jailparam * params,size_t params_off,size_t * params_countp,getparam_filter keyfilt,void * udata)315 getparam_table(lua_State *L, int paramindex, struct jailparam *params,
316 size_t params_off, size_t *params_countp, getparam_filter keyfilt,
317 void *udata)
318 {
319 size_t params_count;
320 int skipped;
321
322 params_count = *params_countp;
323 skipped = 0;
324 for (size_t i = 1 + params_off; i < params_count; ++i) {
325 const char *param_name;
326
327 lua_rawgeti(L, -1, i - params_off);
328 param_name = lua_tostring(L, -1);
329 if (param_name == NULL) {
330 jailparam_free(params, i - skipped);
331 free(params);
332 luaL_argerror(L, paramindex,
333 "param names must be strings");
334 }
335 lua_pop(L, 1);
336 if (keyfilt != NULL && !keyfilt(param_name, udata)) {
337 ++skipped;
338 continue;
339 }
340 if (jailparam_init(¶ms[i - skipped], param_name) == -1) {
341 jailparam_free(params, i - skipped);
342 free(params);
343 luaL_error(L, "jailparam_init: %s", jail_errmsg);
344 }
345 }
346 *params_countp -= skipped;
347 }
348
349 struct getparams_filter_args {
350 int filter_type;
351 };
352
353 static bool
l_getparams_filter(const char * param_name,void * udata)354 l_getparams_filter(const char *param_name, void *udata)
355 {
356 struct getparams_filter_args *gpa;
357
358 gpa = udata;
359
360 /* Skip name or jid, whichever was given. */
361 if (gpa->filter_type == LUA_TSTRING) {
362 if (strcmp(param_name, "name") == 0)
363 return (false);
364 } else /* type == LUA_TNUMBER */ {
365 if (strcmp(param_name, "jid") == 0)
366 return (false);
367 }
368
369 return (true);
370 }
371
372 static int
l_getparams(lua_State * L)373 l_getparams(lua_State *L)
374 {
375 const char *name;
376 struct jailparam *params;
377 size_t params_count;
378 struct getparams_filter_args gpa;
379 int flags, jid, type;
380
381 type = lua_type(L, 1);
382 luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
383 "expected a jail name (string) or id (integer)");
384 luaL_checktype(L, 2, LUA_TTABLE);
385 params_count = 1 + lua_rawlen(L, 2);
386 flags = luaL_optinteger(L, 3, 0);
387
388 params = malloc(params_count * sizeof(struct jailparam));
389 if (params == NULL)
390 return (luaL_error(L, "malloc: %s", strerror(errno)));
391
392 /*
393 * Set the jail name or id param as determined by the first arg.
394 */
395
396 if (type == LUA_TSTRING) {
397 if (jailparam_init(¶ms[0], "name") == -1) {
398 free(params);
399 return (luaL_error(L, "jailparam_init: %s",
400 jail_errmsg));
401 }
402 name = lua_tostring(L, 1);
403 if (jailparam_import(¶ms[0], name) == -1) {
404 jailparam_free(params, 1);
405 free(params);
406 return (luaL_error(L, "jailparam_import: %s",
407 jail_errmsg));
408 }
409 } else /* type == LUA_TNUMBER */ {
410 if (jailparam_init(¶ms[0], "jid") == -1) {
411 free(params);
412 return (luaL_error(L, "jailparam_init: %s",
413 jail_errmsg));
414 }
415 jid = lua_tointeger(L, 1);
416 if (jailparam_import_raw(¶ms[0], &jid, sizeof(jid)) == -1) {
417 jailparam_free(params, 1);
418 free(params);
419 return (luaL_error(L, "jailparam_import_raw: %s",
420 jail_errmsg));
421 }
422 }
423
424 /*
425 * Set the remaining param names being requested.
426 */
427 gpa.filter_type = type;
428 getparam_table(L, 2, params, 0, ¶ms_count, l_getparams_filter, &gpa);
429
430 /*
431 * Get the values and convert to a table.
432 */
433
434 jid = jailparam_get(params, params_count, flags);
435 if (jid == -1) {
436 jailparam_free(params, params_count);
437 free(params);
438 lua_pushnil(L);
439 lua_pushstring(L, jail_errmsg);
440 return (2);
441 }
442 lua_pushinteger(L, jid);
443
444 lua_newtable(L);
445 for (size_t i = 0; i < params_count; ++i) {
446 char *value;
447
448 value = jailparam_export(¶ms[i]);
449 lua_pushstring(L, value);
450 free(value);
451 lua_setfield(L, -2, params[i].jp_name);
452 }
453
454 jailparam_free(params, params_count);
455 free(params);
456
457 return (2);
458 }
459
460 static int
l_setparams(lua_State * L)461 l_setparams(lua_State *L)
462 {
463 const char *name;
464 struct jailparam *params;
465 size_t params_count;
466 int flags, jid, type;
467
468 type = lua_type(L, 1);
469 luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
470 "expected a jail name (string) or id (integer)");
471 luaL_checktype(L, 2, LUA_TTABLE);
472
473 lua_pushnil(L);
474 for (params_count = 1; lua_next(L, 2) != 0; ++params_count)
475 lua_pop(L, 1);
476
477 flags = luaL_optinteger(L, 3, 0);
478
479 params = malloc(params_count * sizeof(struct jailparam));
480 if (params == NULL)
481 return (luaL_error(L, "malloc: %s", strerror(errno)));
482
483 /*
484 * Set the jail name or id param as determined by the first arg.
485 */
486
487 if (type == LUA_TSTRING) {
488 if (jailparam_init(¶ms[0], "name") == -1) {
489 free(params);
490 return (luaL_error(L, "jailparam_init: %s",
491 jail_errmsg));
492 }
493 name = lua_tostring(L, 1);
494 if (jailparam_import(¶ms[0], name) == -1) {
495 jailparam_free(params, 1);
496 free(params);
497 return (luaL_error(L, "jailparam_import: %s",
498 jail_errmsg));
499 }
500 } else /* type == LUA_TNUMBER */ {
501 if (jailparam_init(¶ms[0], "jid") == -1) {
502 free(params);
503 return (luaL_error(L, "jailparam_init: %s",
504 jail_errmsg));
505 }
506 jid = lua_tointeger(L, 1);
507 if (jailparam_import_raw(¶ms[0], &jid, sizeof(jid)) == -1) {
508 jailparam_free(params, 1);
509 free(params);
510 return (luaL_error(L, "jailparam_import_raw: %s",
511 jail_errmsg));
512 }
513 }
514
515 /*
516 * Set the rest of the provided params.
517 */
518
519 lua_pushnil(L);
520 for (size_t i = 1; i < params_count && lua_next(L, 2) != 0; ++i) {
521 const char *value;
522
523 name = lua_tostring(L, -2);
524 if (name == NULL) {
525 jailparam_free(params, i);
526 free(params);
527 return (luaL_argerror(L, 2,
528 "param names must be strings"));
529 }
530 if (jailparam_init(¶ms[i], name) == -1) {
531 jailparam_free(params, i);
532 free(params);
533 return (luaL_error(L, "jailparam_init: %s",
534 jail_errmsg));
535 }
536
537 value = lua_tostring(L, -1);
538 if (value == NULL) {
539 jailparam_free(params, i + 1);
540 free(params);
541 return (luaL_argerror(L, 2,
542 "param values must be strings"));
543 }
544 if (jailparam_import(¶ms[i], value) == -1) {
545 jailparam_free(params, i + 1);
546 free(params);
547 return (luaL_error(L, "jailparam_import: %s",
548 jail_errmsg));
549 }
550
551 lua_pop(L, 1);
552 }
553
554 /*
555 * Attempt to set the params.
556 */
557
558 jid = jailparam_set(params, params_count, flags);
559 if (jid == -1) {
560 jailparam_free(params, params_count);
561 free(params);
562 lua_pushnil(L);
563 lua_pushstring(L, jail_errmsg);
564 return (2);
565 }
566 lua_pushinteger(L, jid);
567
568 jailparam_free(params, params_count);
569 free(params);
570 return (1);
571 }
572
573 static int
l_attach(lua_State * L)574 l_attach(lua_State *L)
575 {
576 int jid, type;
577
578 type = lua_type(L, 1);
579 luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
580 "expected a jail name (string) or id (integer)");
581
582 if (lua_isstring(L, 1)) {
583 /* Resolve it to a jid. */
584 jid = jail_getid(lua_tostring(L, 1));
585 if (jid == -1) {
586 lua_pushnil(L);
587 lua_pushstring(L, jail_errmsg);
588 return (2);
589 }
590 } else {
591 jid = lua_tointeger(L, 1);
592 }
593
594 if (jail_attach(jid) == -1) {
595 lua_pushnil(L);
596 lua_pushstring(L, strerror(errno));
597 return (2);
598 }
599
600 lua_pushboolean(L, 1);
601 return (1);
602 }
603
604 static int
l_remove(lua_State * L)605 l_remove(lua_State *L)
606 {
607 int jid, type;
608
609 type = lua_type(L, 1);
610 luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
611 "expected a jail name (string) or id (integer)");
612
613 if (lua_isstring(L, 1)) {
614 /* Resolve it to a jid. */
615 jid = jail_getid(lua_tostring(L, 1));
616 if (jid == -1) {
617 lua_pushnil(L);
618 lua_pushstring(L, jail_errmsg);
619 return (2);
620 }
621 } else {
622 jid = lua_tointeger(L, 1);
623 }
624
625 if (jail_remove(jid) == -1) {
626 lua_pushnil(L);
627 lua_pushstring(L, strerror(errno));
628 return (2);
629 }
630
631 lua_pushboolean(L, 1);
632 return (1);
633 }
634
635 static const struct luaL_Reg l_jail[] = {
636 /** Get id of a jail by name.
637 * @param name jail name (string)
638 * @return jail id (integer)
639 * or nil, error (string) on error
640 */
641 {"getid", l_getid},
642 /** Get name of a jail by id.
643 * @param jid jail id (integer)
644 * @return jail name (string)
645 * or nil, error (string) on error
646 */
647 {"getname", l_getname},
648 /** Get a list of all known jail parameters.
649 * @return list of jail parameter names (table of strings)
650 * or nil, error (string) on error
651 */
652 {"allparams", l_allparams},
653 /** Get the listed params for a given jail.
654 * @param jail jail name (string) or id (integer)
655 * @param params list of parameter names (table of strings)
656 * @param flags optional flags (integer)
657 * @return jid (integer), params (table of [string] = string)
658 * or nil, error (string) on error
659 */
660 {"getparams", l_getparams},
661 /** Set params for a given jail.
662 * @param jail jail name (string) or id (integer)
663 * @param params params and values (table of [string] = string)
664 * @param flags optional flags (integer)
665 * @return jid (integer)
666 * or nil, error (string) on error
667 */
668 {"setparams", l_setparams},
669 /** Get a list of jail parameters for running jails on the system.
670 * @param params optional list of parameter names (table of
671 * strings)
672 * @return iterator (function), jail_obj (object) with next and
673 * close methods
674 */
675 {"list", l_list},
676 /** Attach to a running jail.
677 * @param jail jail name (string) or id (integer)
678 * @return true (boolean)
679 * or nil, error (string) on error
680 */
681 {"attach", l_attach},
682 /** Remove a running jail.
683 * @param jail jail name (string) or id (integer)
684 * @return true (boolean)
685 * or nil, error (string) on error
686 */
687 {"remove", l_remove},
688 {NULL, NULL}
689 };
690
691 int
luaopen_jail(lua_State * L)692 luaopen_jail(lua_State *L)
693 {
694 lua_newtable(L);
695
696 luaL_setfuncs(L, l_jail, 0);
697
698 lua_pushinteger(L, JAIL_CREATE);
699 lua_setfield(L, -2, "CREATE");
700 lua_pushinteger(L, JAIL_UPDATE);
701 lua_setfield(L, -2, "UPDATE");
702 lua_pushinteger(L, JAIL_ATTACH);
703 lua_setfield(L, -2, "ATTACH");
704 lua_pushinteger(L, JAIL_DYING);
705 lua_setfield(L, -2, "DYING");
706
707 register_jail_metatable(L);
708
709 return (1);
710 }
711