Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/lua.c
2645 views
1
/*-
2
* Copyright (c) 2019-2025 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2023 Serenity Cyber Security, LLC
4
* Author: Gleb Popov <[email protected]>
5
* All rights reserved.
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
* in this position and unchanged.
13
* 2. Redistributions in binary form must reproduce the above copyright
14
* notice, this list of conditions and the following disclaimer in the
15
* documentation and/or other materials provided with the distribution.
16
*
17
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
*/
28
29
#include "pkg_config.h"
30
31
#if __has_include(<sys/capsicum.h>)
32
#define HAVE_CAPSICUM 1
33
#include <sys/capsicum.h>
34
#endif
35
36
#include <sys/stat.h>
37
#include <sys/mman.h>
38
#include <sys/wait.h>
39
40
#include <dirent.h>
41
#include <errno.h>
42
#include <fcntl.h>
43
#include <spawn.h>
44
#include <stdbool.h>
45
#include <unistd.h>
46
#include <xstring.h>
47
48
#include "private/pkg.h"
49
#include "private/event.h"
50
#include "private/lua.h"
51
52
#ifndef DEFFILEMODE
53
#define DEFFILEMODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
54
#endif
55
56
extern char **environ;
57
58
lua_CFunction
59
stack_dump(lua_State *L)
60
{
61
int i;
62
int top = lua_gettop(L);
63
xstring *stack;
64
char *stackstr;
65
66
stack = xstring_new();
67
68
fputs("\nLua Stack\n---------\n"
69
"\tType Data\n\t-----------\n", stack->fp);
70
71
for (i = 1; i <= top; i++) { /* repeat for each level */
72
int t = lua_type(L, i);
73
fprintf(stack->fp, "%i", i);
74
switch (t) {
75
case LUA_TSTRING: /* strings */
76
fprintf(stack->fp, "\tString: `%s'\n", lua_tostring(L, i));
77
break;
78
case LUA_TBOOLEAN: /* booleans */
79
fprintf(stack->fp, "\tBoolean: %s", lua_toboolean(L, i) ? "\ttrue\n" : "\tfalse\n");
80
break;
81
case LUA_TNUMBER: /* numbers */
82
fprintf(stack->fp, "\tNumber: %g\n", lua_tonumber(L, i));
83
break;
84
default: /* other values */
85
fprintf(stack->fp, "\tOther: %s\n", lua_typename(L, t));
86
break;
87
}
88
}
89
stackstr = xstring_get(stack);
90
pkg_emit_error("%s\n", stackstr);
91
free(stackstr);
92
93
return (0);
94
}
95
96
int
97
lua_print_msg(lua_State *L)
98
{
99
int n = lua_gettop(L);
100
luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
101
"pkg.print_msg takes exactly one argument");
102
const char* str = luaL_checkstring(L, 1);
103
lua_getglobal(L, "msgfd");
104
int fd = lua_tointeger(L, -1);
105
106
dprintf(fd, "%s\n", str);
107
108
return (0);
109
}
110
111
112
static const char**
113
luaL_checkarraystrings(lua_State *L, int arg) {
114
const char **ret;
115
lua_Integer n, i;
116
int t;
117
int abs_arg = lua_absindex(L, arg);
118
luaL_checktype(L, abs_arg, LUA_TTABLE);
119
n = lua_rawlen(L, abs_arg);
120
ret = lua_newuserdata(L, (n+1)*sizeof(char*));
121
for (i=0; i<n; i++) {
122
t = lua_rawgeti(L, abs_arg, i+1);
123
if (t == LUA_TNIL)
124
break;
125
luaL_argcheck(L, t == LUA_TSTRING, arg, "expected array of strings");
126
ret[i] = lua_tostring(L, -1);
127
lua_pop(L, 1);
128
}
129
ret[i] = NULL;
130
return ret;
131
}
132
133
int
134
lua_exec(lua_State *L)
135
{
136
int r, pstat;
137
posix_spawn_file_actions_t action;
138
int stdin_pipe[2] = {-1, -1};
139
pid_t pid;
140
const char **argv;
141
int n = lua_gettop(L);
142
luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
143
"pkg.exec takes exactly one argument");
144
145
#ifdef HAVE_CAPSICUM
146
unsigned int capmode;
147
if (cap_getmode(&capmode) == 0 && capmode > 0) {
148
return (luaL_error(L, "pkg.exec not available in sandbox"));
149
}
150
#endif
151
if (pipe(stdin_pipe) < 0)
152
return (EPKG_FATAL);
153
154
posix_spawn_file_actions_init(&action);
155
posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
156
posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
157
158
argv = luaL_checkarraystrings(L, 1);
159
if (0 != (r = posix_spawnp(&pid, argv[0], &action, NULL,
160
(char*const*)argv, environ))) {
161
lua_pushnil(L);
162
lua_pushstring(L, strerror(r));
163
lua_pushinteger(L, r);
164
return 3;
165
}
166
while (waitpid(pid, &pstat, 0) == -1) {
167
if (errno != EINTR) {
168
lua_pushnil(L);
169
lua_pushstring(L, strerror(r));
170
lua_pushinteger(L, r);
171
return 3;
172
}
173
}
174
175
if (WEXITSTATUS(pstat) != 0) {
176
lua_pushnil(L);
177
lua_pushstring(L, "Abnormal termination");
178
lua_pushinteger(L, r);
179
return 3;
180
}
181
182
posix_spawn_file_actions_destroy(&action);
183
184
if (stdin_pipe[0] != -1)
185
close(stdin_pipe[0]);
186
if (stdin_pipe[1] != -1)
187
close(stdin_pipe[1]);
188
lua_pushinteger(L, pid);
189
return 1;
190
}
191
192
int
193
lua_pkg_copy(lua_State *L)
194
{
195
int n = lua_gettop(L);
196
luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
197
"pkg.copy takes exactly two arguments");
198
const char* src = luaL_checkstring(L, 1);
199
const char* dst = luaL_checkstring(L, 2);
200
struct stat s1;
201
int fd1, fd2;
202
struct timespec ts[2];
203
204
#ifdef HAVE_CHFLAGSAT
205
bool install_as_user = (getenv("INSTALL_AS_USER") != NULL);
206
#endif
207
208
lua_getglobal(L, "rootfd");
209
int rootfd = lua_tointeger(L, -1);
210
211
if (fstatat(rootfd, RELATIVE_PATH(src), &s1, 0) == -1) {
212
lua_pushinteger(L, 2);
213
return (1);
214
}
215
fd1 = openat(rootfd, RELATIVE_PATH(src), O_RDONLY, DEFFILEMODE);
216
if (fd1 == -1) {
217
lua_pushinteger(L, 2);
218
return (1);
219
}
220
221
fd2 = openat(rootfd, RELATIVE_PATH(dst), O_RDWR | O_CREAT | O_TRUNC | O_EXCL, s1.st_mode);
222
if (fd2 == -1) {
223
lua_pushinteger(L, 2);
224
return (1);
225
}
226
227
if (!pkg_copy_file(fd1, fd2)) {
228
lua_pushinteger(L, 2);
229
return (1);
230
}
231
if (fchown(fd2, s1.st_uid, s1.st_gid) == -1) {
232
lua_pushinteger(L, 2);
233
return (1);
234
}
235
236
fsync(fd2);
237
close(fd1);
238
close(fd2);
239
240
#ifdef HAVE_STRUCT_STAT_ST_MTIM
241
ts[0] = s1.st_atim;
242
ts[1] = s1.st_mtim;
243
#else
244
#if defined(_DARWIN_C_SOURCE) || defined(__APPLE__)
245
ts[0] = s1.st_atimespec;
246
ts[1] = s1.st_mtimespec;
247
#else
248
ts[0].tv_sec = s1.st_atime;
249
ts[0].tv_nsec = 0;
250
ts[1].tv_sec = s1.st_mtime;
251
ts[1].tv_nsec = 0;
252
#endif
253
#endif
254
255
if (set_attrsat(rootfd, RELATIVE_PATH(dst), s1.st_mode, s1.st_uid,
256
s1.st_gid, &ts[0], &ts[1]) != EPKG_OK) {
257
lua_pushinteger(L, -1);
258
return (1);
259
}
260
261
#ifdef HAVE_CHFLAGSAT
262
if (!install_as_user && s1.st_flags != 0) {
263
if (chflagsat(rootfd, RELATIVE_PATH(dst),
264
s1.st_flags, AT_SYMLINK_NOFOLLOW) == -1) {
265
pkg_fatal_errno("Fail to chflags %s", dst);
266
lua_pushinteger(L, -1);
267
return (1);
268
}
269
}
270
#endif
271
return (0);
272
}
273
274
int
275
lua_pkg_filecmp(lua_State *L)
276
{
277
int n = lua_gettop(L);
278
luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
279
"pkg.filecmp takes exactly two arguments");
280
const char* file1 = luaL_checkstring(L, 1);
281
const char* file2 = luaL_checkstring(L, 2);
282
char *buf1, *buf2;
283
struct stat s1, s2;
284
int fd1, fd2;
285
int ret = 0;
286
287
lua_getglobal(L, "rootfd");
288
int rootfd = lua_tointeger(L, -1);
289
290
if (fstatat(rootfd, RELATIVE_PATH(file1), &s1, 0) == -1) {
291
lua_pushinteger(L, 2);
292
return (1);
293
}
294
if (fstatat(rootfd, RELATIVE_PATH(file2), &s2, 0) == -1) {
295
lua_pushinteger(L, 2);
296
return (1);
297
}
298
if (s1.st_size != s2.st_size) {
299
lua_pushinteger(L, 1);
300
return (1);
301
}
302
fd1 = openat(rootfd, RELATIVE_PATH(file1), O_RDONLY, DEFFILEMODE);
303
if (fd1 == -1) {
304
lua_pushinteger(L, 2);
305
return (1);
306
}
307
buf1 = mmap(NULL, s1.st_size, PROT_READ, MAP_SHARED, fd1, 0);
308
close(fd1);
309
if (buf1 == NULL) {
310
lua_pushinteger(L, -1);
311
return (1);
312
}
313
fd2 = openat(rootfd, RELATIVE_PATH(file2), O_RDONLY, DEFFILEMODE);
314
if (fd2 == -1) {
315
lua_pushinteger(L, 2);
316
return (1);
317
}
318
319
buf2 = mmap(NULL, s2.st_size, PROT_READ, MAP_SHARED, fd2, 0);
320
close(fd2);
321
if (buf2 == NULL) {
322
lua_pushinteger(L, -1);
323
return (1);
324
}
325
if (memcmp(buf1, buf2, s1.st_size) != 0)
326
ret = 1;
327
328
munmap(buf1, s1.st_size);
329
munmap(buf2, s2.st_size);
330
331
lua_pushinteger(L, ret);
332
return (1);
333
}
334
335
int
336
lua_pkg_symlink(lua_State *L)
337
{
338
int n = lua_gettop(L);
339
luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
340
"pkg.symlink takes exactly two arguments");
341
const char *from = luaL_checkstring(L, 1);
342
const char *to = luaL_checkstring(L, 2);
343
lua_getglobal(L, "rootfd");
344
int rootfd = lua_tointeger(L, -1);
345
if (symlinkat(from, rootfd, RELATIVE_PATH(to)) == -1)
346
return (luaL_fileresult(L, 0, from));
347
return (1);
348
}
349
350
int
351
lua_prefix_path(lua_State *L)
352
{
353
int n = lua_gettop(L);
354
luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
355
"pkg.prefix_path takes exactly one argument");
356
const char *str = luaL_checkstring(L, 1);
357
lua_getglobal(L, "package");
358
struct pkg *p = lua_touserdata(L, -1);
359
360
char path[MAXPATHLEN];
361
path[0] = '\0';
362
363
if (*str == '/') {
364
strlcat(path, str, MAXPATHLEN);
365
} else {
366
strlcat(path, p->prefix, MAXPATHLEN);
367
strlcat(path, "/", MAXPATHLEN);
368
strlcat(path, str, MAXPATHLEN);
369
}
370
371
lua_pushstring(L, path);
372
return (1);
373
}
374
375
int
376
lua_stat(lua_State *L)
377
{
378
int n = lua_gettop(L);
379
luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
380
"pkg.stat takes exactly one argument");
381
const char *path = RELATIVE_PATH(luaL_checkstring(L, 1));
382
lua_getglobal(L, "rootfd");
383
int rootfd = lua_tointeger(L, -1);
384
struct stat s;
385
const char *type = "unknown";
386
387
if (fstatat(rootfd, path, &s, AT_SYMLINK_NOFOLLOW) == -1) {
388
return lua_pushnil(L), 1;
389
}
390
391
lua_settop(L, 2);
392
if (!lua_istable(L, 2))
393
lua_newtable(L);
394
395
lua_pushinteger(L, s.st_size);
396
lua_setfield(L, -2, "size");
397
lua_pushinteger(L, s.st_uid);
398
lua_setfield(L, -2, "uid");
399
lua_pushinteger(L, s.st_gid);
400
lua_setfield(L, -2, "gid");
401
if (S_ISREG(s.st_mode))
402
type = "reg";
403
else if (S_ISDIR(s.st_mode))
404
type = "dir";
405
else if (S_ISCHR(s.st_mode))
406
type = "chr";
407
else if (S_ISLNK(s.st_mode))
408
type = "lnk";
409
else if (S_ISSOCK(s.st_mode))
410
type = "sock";
411
else if (S_ISBLK(s.st_mode))
412
type = "blk";
413
else if (S_ISFIFO(s.st_mode))
414
type = "fifo";
415
lua_pushstring(L, type);
416
lua_setfield(L, -2, "type");
417
418
return (1);
419
}
420
421
/* stolen from lua.c */
422
void
423
lua_args_table(lua_State *L, char **argv, int argc)
424
{
425
lua_createtable(L, argc, 1);
426
for (int i = 0; i < argc; i++) {
427
lua_pushstring(L, argv[i]);
428
/* lua starts counting by 1 */
429
lua_rawseti(L, -2, i + 1);
430
}
431
lua_setglobal(L, "arg");
432
}
433
434
435
/*
436
* this is a copy of lua code to be able to override open
437
* merge of newprefile and newfile
438
*/
439
440
static int
441
my_iofclose(lua_State *L)
442
{
443
luaL_Stream *p = ((luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE));
444
int res = fclose(p->f);
445
return (luaL_fileresult(L, (res == 0), NULL));
446
}
447
448
static luaL_Stream *
449
newfile(lua_State *L) {
450
luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream));
451
p->f = NULL;
452
p->closef = &my_iofclose;
453
luaL_setmetatable(L, LUA_FILEHANDLE);
454
return (p);
455
}
456
457
static int
458
lua_io_open(lua_State *L)
459
{
460
const char *filename = luaL_checkstring(L, 1);
461
const char *mode = luaL_optstring(L, 2, "r");
462
lua_getglobal(L, "rootfd");
463
int rootfd = lua_tointeger(L, -1);
464
int oflags;
465
luaL_Stream *p = newfile(L);
466
const char *md = mode;
467
luaL_argcheck(L, checkflags(md, &oflags), 2, "invalid mode");
468
int fd = openat(rootfd, RELATIVE_PATH(filename), oflags, DEFFILEMODE);
469
if (fd == -1)
470
return (luaL_fileresult(L, 0, filename));
471
p->f = fdopen(fd, mode);
472
return ((p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1);
473
}
474
475
static int
476
lua_os_remove(lua_State *L) {
477
const char *filename = RELATIVE_PATH(luaL_checkstring(L, 1));
478
lua_getglobal(L, "rootfd");
479
int rootfd = lua_tointeger(L, -1);
480
int flag = 0;
481
struct stat st;
482
483
if (fstatat(rootfd, filename, &st, AT_SYMLINK_NOFOLLOW) == -1)
484
return (luaL_fileresult(L, 1, NULL));
485
486
if (S_ISDIR(st.st_mode))
487
flag = AT_REMOVEDIR;
488
489
return (luaL_fileresult(L, unlinkat(rootfd, filename, flag) == 0, NULL));
490
}
491
492
static int
493
lua_os_rename(lua_State *L)
494
{
495
const char *fromname = RELATIVE_PATH(luaL_checkstring(L, 1));
496
const char *toname = RELATIVE_PATH(luaL_checkstring(L, 2));
497
lua_getglobal(L, "rootfd");
498
int rootfd = lua_tointeger(L, -1);
499
return luaL_fileresult(L, renameat(rootfd, fromname, rootfd, toname) == 0, NULL);
500
}
501
502
static int
503
lua_os_execute(lua_State *L)
504
{
505
return (luaL_error(L, "os.execute not available"));
506
}
507
508
static int
509
lua_os_exit(lua_State *L)
510
{
511
return (luaL_error(L, "os.exit not available"));
512
}
513
514
void
515
lua_override_ios(lua_State *L, bool sandboxed)
516
{
517
lua_getglobal(L, "io");
518
lua_pushcfunction(L, lua_io_open);
519
lua_setfield(L, -2, "open");
520
521
lua_getglobal(L, "os");
522
lua_pushcfunction(L, lua_os_remove);
523
lua_setfield(L, -2, "remove");
524
lua_pushcfunction(L, lua_os_rename);
525
lua_setfield(L, -2, "rename");
526
if (sandboxed) {
527
lua_pushcfunction(L, lua_os_execute);
528
lua_setfield(L, -2, "execute");
529
}
530
lua_pushcfunction(L, lua_os_exit);
531
lua_setfield(L, -2, "exit");
532
}
533
534
int
535
lua_readdir(lua_State *L)
536
{
537
int n = lua_gettop(L);
538
luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
539
"pkg.readdir takes exactly one argument");
540
const char *path = luaL_checkstring(L, 1);
541
int fd = -1;
542
543
if (*path == '/') {
544
lua_getglobal(L, "rootfd");
545
int rootfd = lua_tointeger(L, -1);
546
if (strlen(path) > 1) {
547
fd = openat(rootfd, path +1, O_DIRECTORY);
548
} else {
549
fd = dup(rootfd);
550
}
551
} else {
552
fd = open(path, O_DIRECTORY);
553
}
554
if (fd == -1)
555
return (luaL_fileresult(L, 0, path));
556
557
DIR *dir = fdopendir(fd);
558
if (!dir)
559
return (luaL_fileresult(L, 0, path));
560
lua_newtable(L);
561
struct dirent *e;
562
int i = 0;
563
while ((e = readdir(dir))) {
564
char *name = e->d_name;
565
if (STREQ(name, ".") || STREQ(name, ".."))
566
continue;
567
lua_pushinteger(L, ++i);
568
lua_pushstring(L, name);
569
lua_settable(L, -3);
570
}
571
return 1;
572
}
573
574
int
575
lua_metalog_copy(lua_State *L)
576
{
577
int n = lua_gettop(L);
578
luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
579
"pkg.metalog_copy takes exactly two arguments");
580
const char *src = luaL_checkstring(L, 1);
581
const char *dst = luaL_checkstring(L, 2);
582
lua_getglobal(L, "package");
583
struct pkg *p = lua_touserdata(L, -1);
584
struct pkg_file *f = pkg_get_file(p, src);
585
if (f == NULL) {
586
lua_pushnil(L);
587
lua_pushstring(L, "Unknown source file");
588
return (2);
589
}
590
/* TODO: what about symlinks ? */
591
metalog_add(PKG_METALOG_FILE, RELATIVE_PATH(dst),
592
f->uname, f->gname, f->perm & ~S_IFREG, f->fflags, f->symlink_target);
593
return (1);
594
}
595
596