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