lua.c
1 /*- 2 * Copyright (c) 2019-2025 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2023 Serenity Cyber Security, LLC 4 * Author: Gleb Popov <arrowd@FreeBSD.org> 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("Failed 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 }