/ libpkg / lua.c
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  }