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)
48LUA_API int   (lua_error) (lua_State *L) __dead2;
49#endif
50#if defined(LUA_ERRFILE) && defined(LUALIB_API)
51LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg) __dead2;
52LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname) __dead2;
53LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...) __dead2;
54#endif
55
56int luaopen_jail(lua_State *);
57
58typedef bool (*getparam_filter)(const char *, void *);
59
60static 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
64struct l_jail_iter {
65	struct jailparam	*params;
66	size_t			params_count;
67	int			jid;
68};
69
70static bool
71l_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
85static int
86l_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
146static int
147l_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
168static int
169l_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
239static void
240register_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
257static int
258l_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
274static int
275l_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
292static int
293l_allparams(lua_State *L)
294{
295	struct jailparam *params;
296	int params_count;
297
298	params_count = jailparam_all(&params);
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
314static void
315getparam_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(&params[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
349struct getparams_filter_args {
350	int	filter_type;
351};
352
353static bool
354l_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
372static int
373l_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(&params[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(&params[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(&params[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(&params[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, &params_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(&params[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
460static int
461l_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(&params[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(&params[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(&params[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(&params[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(&params[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(&params[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
573static int
574l_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
604static int
605l_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
635static 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
691int
692luaopen_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