Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/flua/libjail/lua_jail.c
34923 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
#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
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
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
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
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
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
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
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
293
l_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
314
static void
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(&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
349
struct getparams_filter_args {
350
int filter_type;
351
};
352
353
static bool
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
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(&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
if (params[i].jp_flags & JP_KEYVALUE &&
449
params[i].jp_valuelen == 0) {
450
/* Communicate back a missing key. */
451
lua_pushnil(L);
452
} else {
453
value = jailparam_export(&params[i]);
454
lua_pushstring(L, value);
455
free(value);
456
}
457
458
lua_setfield(L, -2, params[i].jp_name);
459
}
460
461
jailparam_free(params, params_count);
462
free(params);
463
464
return (2);
465
}
466
467
static int
468
l_setparams(lua_State *L)
469
{
470
const char *name;
471
struct jailparam *params;
472
size_t params_count;
473
int flags, jid, type;
474
475
type = lua_type(L, 1);
476
luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
477
"expected a jail name (string) or id (integer)");
478
luaL_checktype(L, 2, LUA_TTABLE);
479
480
lua_pushnil(L);
481
for (params_count = 1; lua_next(L, 2) != 0; ++params_count)
482
lua_pop(L, 1);
483
484
flags = luaL_optinteger(L, 3, 0);
485
486
params = malloc(params_count * sizeof(struct jailparam));
487
if (params == NULL)
488
return (luaL_error(L, "malloc: %s", strerror(errno)));
489
490
/*
491
* Set the jail name or id param as determined by the first arg.
492
*/
493
494
if (type == LUA_TSTRING) {
495
if (jailparam_init(&params[0], "name") == -1) {
496
free(params);
497
return (luaL_error(L, "jailparam_init: %s",
498
jail_errmsg));
499
}
500
name = lua_tostring(L, 1);
501
if (jailparam_import(&params[0], name) == -1) {
502
jailparam_free(params, 1);
503
free(params);
504
return (luaL_error(L, "jailparam_import: %s",
505
jail_errmsg));
506
}
507
} else /* type == LUA_TNUMBER */ {
508
if (jailparam_init(&params[0], "jid") == -1) {
509
free(params);
510
return (luaL_error(L, "jailparam_init: %s",
511
jail_errmsg));
512
}
513
jid = lua_tointeger(L, 1);
514
if (jailparam_import_raw(&params[0], &jid, sizeof(jid)) == -1) {
515
jailparam_free(params, 1);
516
free(params);
517
return (luaL_error(L, "jailparam_import_raw: %s",
518
jail_errmsg));
519
}
520
}
521
522
/*
523
* Set the rest of the provided params.
524
*/
525
526
lua_pushnil(L);
527
for (size_t i = 1; i < params_count && lua_next(L, 2) != 0; ++i) {
528
const char *value;
529
530
name = lua_tostring(L, -2);
531
if (name == NULL) {
532
jailparam_free(params, i);
533
free(params);
534
return (luaL_argerror(L, 2,
535
"param names must be strings"));
536
}
537
if (jailparam_init(&params[i], name) == -1) {
538
jailparam_free(params, i);
539
free(params);
540
return (luaL_error(L, "jailparam_init: %s",
541
jail_errmsg));
542
}
543
544
value = lua_tostring(L, -1);
545
/* Allow passing NULL for key removal. */
546
if (value == NULL && !(params[i].jp_flags & JP_KEYVALUE)) {
547
jailparam_free(params, i + 1);
548
free(params);
549
return (luaL_argerror(L, 2,
550
"param values must be strings"));
551
}
552
if (jailparam_import(&params[i], value) == -1) {
553
jailparam_free(params, i + 1);
554
free(params);
555
return (luaL_error(L, "jailparam_import: %s",
556
jail_errmsg));
557
}
558
559
lua_pop(L, 1);
560
}
561
562
/*
563
* Attempt to set the params.
564
*/
565
566
jid = jailparam_set(params, params_count, flags);
567
if (jid == -1) {
568
jailparam_free(params, params_count);
569
free(params);
570
lua_pushnil(L);
571
lua_pushstring(L, jail_errmsg);
572
return (2);
573
}
574
lua_pushinteger(L, jid);
575
576
jailparam_free(params, params_count);
577
free(params);
578
return (1);
579
}
580
581
static int
582
l_attach(lua_State *L)
583
{
584
int jid, type;
585
586
type = lua_type(L, 1);
587
luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
588
"expected a jail name (string) or id (integer)");
589
590
if (lua_isstring(L, 1)) {
591
/* Resolve it to a jid. */
592
jid = jail_getid(lua_tostring(L, 1));
593
if (jid == -1) {
594
lua_pushnil(L);
595
lua_pushstring(L, jail_errmsg);
596
return (2);
597
}
598
} else {
599
jid = lua_tointeger(L, 1);
600
}
601
602
if (jail_attach(jid) == -1) {
603
lua_pushnil(L);
604
lua_pushstring(L, strerror(errno));
605
return (2);
606
}
607
608
lua_pushboolean(L, 1);
609
return (1);
610
}
611
612
static int
613
l_remove(lua_State *L)
614
{
615
int jid, type;
616
617
type = lua_type(L, 1);
618
luaL_argcheck(L, type == LUA_TSTRING || type == LUA_TNUMBER, 1,
619
"expected a jail name (string) or id (integer)");
620
621
if (lua_isstring(L, 1)) {
622
/* Resolve it to a jid. */
623
jid = jail_getid(lua_tostring(L, 1));
624
if (jid == -1) {
625
lua_pushnil(L);
626
lua_pushstring(L, jail_errmsg);
627
return (2);
628
}
629
} else {
630
jid = lua_tointeger(L, 1);
631
}
632
633
if (jail_remove(jid) == -1) {
634
lua_pushnil(L);
635
lua_pushstring(L, strerror(errno));
636
return (2);
637
}
638
639
lua_pushboolean(L, 1);
640
return (1);
641
}
642
643
static const struct luaL_Reg l_jail[] = {
644
/** Get id of a jail by name.
645
* @param name jail name (string)
646
* @return jail id (integer)
647
* or nil, error (string) on error
648
*/
649
{"getid", l_getid},
650
/** Get name of a jail by id.
651
* @param jid jail id (integer)
652
* @return jail name (string)
653
* or nil, error (string) on error
654
*/
655
{"getname", l_getname},
656
/** Get a list of all known jail parameters.
657
* @return list of jail parameter names (table of strings)
658
* or nil, error (string) on error
659
*/
660
{"allparams", l_allparams},
661
/** Get the listed params for a given jail.
662
* @param jail jail name (string) or id (integer)
663
* @param params list of parameter names (table of strings)
664
* @param flags optional flags (integer)
665
* @return jid (integer), params (table of [string] = string)
666
* or nil, error (string) on error
667
*/
668
{"getparams", l_getparams},
669
/** Set params for a given jail.
670
* @param jail jail name (string) or id (integer)
671
* @param params params and values (table of [string] = string)
672
* @param flags optional flags (integer)
673
* @return jid (integer)
674
* or nil, error (string) on error
675
*/
676
{"setparams", l_setparams},
677
/** Get a list of jail parameters for running jails on the system.
678
* @param params optional list of parameter names (table of
679
* strings)
680
* @return iterator (function), jail_obj (object) with next and
681
* close methods
682
*/
683
{"list", l_list},
684
/** Attach to a running jail.
685
* @param jail jail name (string) or id (integer)
686
* @return true (boolean)
687
* or nil, error (string) on error
688
*/
689
{"attach", l_attach},
690
/** Remove a running jail.
691
* @param jail jail name (string) or id (integer)
692
* @return true (boolean)
693
* or nil, error (string) on error
694
*/
695
{"remove", l_remove},
696
{NULL, NULL}
697
};
698
699
int
700
luaopen_jail(lua_State *L)
701
{
702
lua_newtable(L);
703
704
luaL_setfuncs(L, l_jail, 0);
705
706
lua_pushinteger(L, JAIL_CREATE);
707
lua_setfield(L, -2, "CREATE");
708
lua_pushinteger(L, JAIL_UPDATE);
709
lua_setfield(L, -2, "UPDATE");
710
lua_pushinteger(L, JAIL_ATTACH);
711
lua_setfield(L, -2, "ATTACH");
712
lua_pushinteger(L, JAIL_DYING);
713
lua_setfield(L, -2, "DYING");
714
715
register_jail_metatable(L);
716
717
return (1);
718
}
719
720