Avoid sandboxing directory prerequisites

Landlock Make will no longer sandbox prerequisites that end with a
trailing slash. This means you can use use directory prerequisites
for detecting deleted files when using using globbing, without the
effect of unveiling the entire directory. When you do want make to
unveil directories, you can omit the trailing slash.
This commit is contained in:
Justine Tunney 2022-08-19 10:00:41 -07:00
parent 2827df688a
commit 8835b82a7c
14 changed files with 168 additions and 37 deletions

Binary file not shown.

View file

@ -25,7 +25,6 @@ o/%.o: o/%.S ; @$(COMPILE) -AOBJECTIFY.S $(OBJECTIFY.S) $(
o/%.lds: %.lds ; @$(COMPILE) -APREPROCESS $(PREPROCESS.lds) $(OUTPUT_OPTION) $<
o/%.inc: %.h ; @$(COMPILE) -APREPROCESS $(PREPROCESS) $(OUTPUT_OPTION) -D__ASSEMBLER__ -P $<
o/%.greg.o: %.greg.c ; @$(COMPILE) -AOBJECTIFY.greg $(OBJECTIFY.greg.c) $(OUTPUT_OPTION) $<
o/%.zip.o: o/% ; @$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%: o/$(MODE)/%.dbg ; @$(COMPILE) -AOBJCOPY -T$@ $(OBJCOPY) -S -O binary $< $@
o/$(MODE)/%.o: %.s ; @$(COMPILE) -AOBJECTIFY.s $(OBJECTIFY.s) $(OUTPUT_OPTION) $<
@ -45,7 +44,6 @@ o/$(MODE)/%.ncabi.o: %.ncabi.c ; @$(COMPILE) -AOBJECTIFY.nc $(OBJECTIFY.ncab
o/$(MODE)/%.real.o: %.c ; @$(COMPILE) -AOBJECTIFY.real $(OBJECTIFY.real.c) $(OUTPUT_OPTION) $<
o/$(MODE)/%.runs: o/$(MODE)/% ; @$(COMPILE) -ACHECK -wtT$@ $< $(TESTARGS)
o/$(MODE)/%.zip.o: % ; @$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%-gcc.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-gcc.asm: %.cc ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.cxx) -S -g0 $(OUTPUT_OPTION) $<
o/$(MODE)/%-clang.asm: %.c ; @$(COMPILE) -AOBJECTIFY.c $(OBJECTIFY.c) -S -g0 $(OUTPUT_OPTION) $<
@ -100,9 +98,36 @@ o/$(MODE)/%: o/$(MODE)/%.com o/$(MODE)/tool/build/cp.com o/$(MODE)/tool/build/as
@$(COMPILE) -wACP -T$@ o/$(MODE)/tool/build/cp.com $< $@
@$(COMPILE) -wAASSIMILATE -T$@ o/$(MODE)/tool/build/assimilate.com $@
################################################################################
# elf zip files
#
# zipobj.com lets us do fast incremental linking of compressed data.
# it's nice because if we link a hundred binaries that use the time zone
# database, then that database only needs to be DEFLATE'd once.
o/%.zip.o: o/%
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
o/$(MODE)/%.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
# an issue with sandboxing arises when creating directory entries in the
# zip file. we need the trailing slash (e.g. o//foo/.zip.o) but Landlock
# Make avoids sandboxing directory names that have a trailing slash (so
# they can be used to watch for deleted files, without creating overly
# broad unveiling). such rules need to be written more explicitly.
o/$(MODE)%/.zip.o: %
@$(COMPILE) -wAZIPOBJ $(ZIPOBJ) $(ZIPOBJ_FLAGS) $(OUTPUT_OPTION) $<
################################################################################
# strict header checking
# these rules are unsandboxed since they're kind of a sandboxing test themselves
#
# these rules are unsandboxed since they're already a sandboxing test,
# and it would be too costly in terms of make latency to have every
# header file depend on $(HDRS) and $(INCS).
o/%.h.ok: .UNSANDBOXED = 1
o/%.h.ok: %.h

View file

@ -66,6 +66,9 @@ o/$(MODE)/libc/time/now.o: private \
OVERRIDE_CFLAGS += \
-O3
o/$(MODE)/usr/share/zoneinfo/.zip.o: \
usr/share/zoneinfo
LIBC_TIME_LIBS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)))
LIBC_TIME_SRCS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_SRCS))
LIBC_TIME_HDRS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_HDRS))

View file

@ -48,6 +48,9 @@ $(NET_HTTPS_A).pkg: \
$(NET_HTTPS_A_OBJS) \
$(foreach x,$(NET_HTTPS_A_DIRECTDEPS),$($(x)_A).pkg)
o/$(MODE)/usr/share/ssl/root/.zip.o: \
usr/share/ssl/root
NET_HTTPS_LIBS = $(foreach x,$(NET_HTTPS_ARTIFACTS),$($(x)))
NET_HTTPS_SRCS = $(foreach x,$(NET_HTTPS_ARTIFACTS),$($(x)_SRCS))
NET_HTTPS_HDRS = $(foreach x,$(NET_HTTPS_ARTIFACTS),$($(x)_HDRS))

View file

@ -531,7 +531,10 @@ fatal_error_signal (int sig)
(void) remote_kill (c->pid, sig);
for (c = children; c != 0; c = c->next)
delete_child_targets (c);
{
delete_child_targets (c);
delete_tmpdir (c);
}
/* Clean up the children. We don't just use the call below because
we don't want to print the "Waiting for children" message. */

View file

@ -63,6 +63,8 @@ struct file
FILE_TIMESTAMP last_mtime; /* File's modtime, if already known. */
FILE_TIMESTAMP mtime_before_update; /* File's modtime before any updating
has been performed. */
FILE_TIMESTAMP touched; /* Set if file was created in order for
Landlock LSM to sandbox it. */
unsigned int considered; /* equal to 'considered' if file has been
considered on current scan of goal chain */
int command_flags; /* Flags OR'd in for cmds; see commands.h. */

View file

@ -512,6 +512,18 @@ new_tmpdir (const char *tmp, struct file *file)
return tmpdir;
}
bool
get_file_timestamp (struct file *file)
{
int e;
struct stat st;
EINTRLOOP (e, stat (file->name, &st));
if (e == 0)
return FILE_TIMESTAMP_STAT_MODTIME (file->name, st);
else
return NONEXISTENT_MTIME;
}
void
delete_tmpdir (struct child *c)
{
@ -782,8 +794,16 @@ reap_children (int block, int err)
delete_on_error = f != 0 && f->is_target;
}
if (exit_sig != 0 || delete_on_error)
delete_child_targets (c);
delete_tmpdir (c);
{
delete_child_targets (c);
delete_tmpdir (c);
}
else if (c->file->touched &&
c->file->touched != get_file_timestamp (c->file))
/* If file was created just so it could be sandboxed, then
delete that file even if .DELETE_ON_ERROR isn't used,
but only if the command hasn't modified it. */
unlink (c->file->name);
}
else
{
@ -831,8 +851,11 @@ reap_children (int block, int err)
}
if (c->file->update_status != us_success)
/* We failed to start the commands. */
delete_child_targets (c);
{
/* We failed to start the commands. */
delete_child_targets (c);
delete_tmpdir (c);
}
}
else
{
@ -2273,20 +2296,25 @@ child_execute_job (struct childbase *child,
if (!c->file->phony &&
strlen(c->file->name) < PATH_MAX)
{
int fd, rc, err = errno;
strcpy (outpathbuf, c->file->name);
if (makedirs (dirname (outpathbuf), 0777) == -1)
errno = err;
fd = open (c->file->name, O_RDWR | O_CREAT, 0777);
if (fd != -1)
close (fd);
else if (errno == EEXIST)
errno = err;
else
int fd, rc, err;
if (c->file->last_mtime == NONEXISTENT_MTIME)
{
OSS (error, NILF, "%s: touch target failed %s",
c->file->name, strerror (errno));
_Exit (127);
strcpy (outpathbuf, c->file->name);
err = errno;
if (makedirs (dirname (outpathbuf), 0777) == -1)
errno = err;
fd = open (c->file->name, O_RDWR | O_CREAT, 0777);
if (fd != -1)
close (fd);
else if (errno == EEXIST)
errno = err;
else
{
OSS (error, NILF, "%s: touch target failed %s",
c->file->name, strerror (errno));
_Exit (127);
}
c->file->touched = get_file_timestamp (c->file);
}
DB (DB_JOBS, (_("Unveiling %s with permissions %s\n"),
c->file->name, "rwx"));
@ -2298,11 +2326,26 @@ child_execute_job (struct childbase *child,
}
}
/* unveil target prerequisites */
/*
* unveil target prerequisites
*
* directories get special treatment:
*
* - libc/nt
* shall unveil everything beneath dir
*
* - libc/nt/
* no sandboxing due to trailing slash
* intended to be timestamp check only
*/
for (d = c->file->deps; d; d = d->next)
{
size_t n;
n = strlen (d->file->name);
if (n && d->file->name[n - 1] == '/')
continue;
RETURN_ON_ERROR (Unveil (d->file->name, "rx"));
if (endswith (d->file->name, ".com"))
if (n > 4 && READ32LE(d->file->name + n - 4) == READ32LE(".com"))
{
s = xstrcat (d->file->name, ".dbg");
RETURN_ON_ERROR (Unveil (s, "rx"));

View file

@ -85,3 +85,5 @@ void unblock_all_sigs (void);
extern unsigned int job_slots_used;
extern unsigned int jobserver_tokens;
void delete_tmpdir (struct child *);

View file

@ -45,7 +45,7 @@ this program. If not, see <http://www.gnu.org/licenses/>. */
#include "third_party/make/getopt.h"
// clang-format off
STATIC_STACK_SIZE(0x200000); // 2mb stack
STATIC_STACK_SIZE(0x00800000); // 8mb stack
#define HAVE_WAIT_NOHANG
@ -1034,6 +1034,7 @@ main (int argc, char **argv, char **envp)
FATAL_SIG (SIGTERM);
FATAL_SIG (SIGXCPU);
FATAL_SIG (SIGXFSZ);
FATAL_SIG (SIGPIPE); /* [jart] handle case of piped into less */
#undef FATAL_SIG

View file

@ -1365,3 +1365,18 @@ o/$(MODE)/third_party/mbedtls/test/secp384r1_test.com.dbg: \
@$(APELINK)
o/$(MODE)/third_party/mbedtls/test/test_suite_asn1parse.com.runs: private QUOTA = -M512m
# these need to be explictly defined because landlock make won't sandbox
# prerequisites with a trailing slash.
o/$(MODE)/third_party/mbedtls/test/data/.zip.o: \
third_party/mbedtls/test/data
o/$(MODE)/third_party/mbedtls/test/data/dir-maxpath/.zip.o: \
third_party/mbedtls/test/data/dir-maxpath
o/$(MODE)/third_party/mbedtls/test/data/dir1/.zip.o: \
third_party/mbedtls/test/data/dir1
o/$(MODE)/third_party/mbedtls/test/data/dir2/.zip.o: \
third_party/mbedtls/test/data/dir2
o/$(MODE)/third_party/mbedtls/test/data/dir3/.zip.o: \
third_party/mbedtls/test/data/dir3
o/$(MODE)/third_party/mbedtls/test/data/dir4/.zip.o: \
third_party/mbedtls/test/data/dir4

View file

@ -4490,6 +4490,29 @@ o/$(MODE)/third_party/python/hello.com.dbg: \
$(THIRD_PARTY_PYTHON_HELLO_OBJS): private PYFLAGS += -C2 -m
# these need to be explictly defined because landlock make won't sandbox
# prerequisites with a trailing slash.
o/$(MODE)/third_party/python/Lib/pydoc_data/.zip.o: \
third_party/python/Lib/pydoc_data
o/$(MODE)/third_party/python/Lib/test/xmltestdata/.zip.o: \
third_party/python/Lib/test/xmltestdata
o/$(MODE)/third_party/python/Lib/test/sndhdrdata/.zip.o: \
third_party/python/Lib/test/sndhdrdata
o/$(MODE)/third_party/python/Lib/test/imghdrdata/.zip.o: \
third_party/python/Lib/test/imghdrdata
o/$(MODE)/third_party/python/Lib/test/decimaltestdata/.zip.o: \
third_party/python/Lib/test/decimaltestdata
o/$(MODE)/third_party/python/Lib/test/dtracedata/.zip.o: \
third_party/python/Lib/test/dtracedata
o/$(MODE)/third_party/python/Lib/test/cjkencodings/.zip.o: \
third_party/python/Lib/test/cjkencodings
o/$(MODE)/third_party/python/Modules/.zip.o: \
third_party/python/Modules
o/$(MODE)/third_party/python/Objects/.zip.o: \
third_party/python/Objects
o/$(MODE)/third_party/python/Lib/test/.zip.o: \
third_party/python/Lib/test
################################################################################
.PHONY: o/$(MODE)/third_party/python

View file

@ -16,10 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/arraylist2.internal.h"
#include "libc/assert.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/stat.h"
@ -27,10 +24,13 @@
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.internal.h"
#include "libc/mem/arraylist2.internal.h"
#include "libc/mem/io.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
@ -233,12 +233,12 @@ int main(int argc, char *argv[]) {
for (i = 0;; ++i) {
TryAgain:
if (!(arg = getargs_next(&ga))) break;
if (endswith(arg, "/")) goto TryAgain;
if (endswith(arg, ".pkg")) goto TryAgain;
CHECK_NE(-1, stat(arg, st), "%s", arg);
if (!st->st_size || S_ISDIR(st->st_mode)) goto TryAgain;
CHECK_NE(-1, (fd = open(arg, O_RDONLY)), "%s", arg);
CHECK_NE(-1, fstat(fd, st));
CHECK_LT(st->st_size, 0x7ffff000);
if (!st->st_size || S_ISDIR(st->st_mode) || endswith(arg, ".pkg")) {
goto TryAgain;
}
AppendArg(&args, xstrdup(arg));
AppendInt(&names, filenames.i);
AppendInt(&sizes, st->st_size);

View file

@ -16,17 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/alg.h"
#include "libc/mem/arraylist.internal.h"
#include "libc/intrin/bswap.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/elf/elf.h"
#include "libc/elf/struct/shdr.h"
#include "libc/elf/struct/sym.h"
#include "libc/intrin/bswap.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/mem/alg.h"
#include "libc/mem/arraylist.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
@ -134,8 +134,10 @@ struct Package *LoadPackage(const char *path) {
CHECK(fileexists(path), "%s: %s: %s\n", "error", path, "not found");
CHECK_NE(-1, (fd = open(path, O_RDONLY)), "%s", path);
CHECK_NE(-1, fstat(fd, &st));
CHECK_NE(MAP_FAILED, (pkg = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE, fd, 0)));
CHECK_NE(MAP_FAILED,
(pkg = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
fd, 0)),
"path=%s", path);
CHECK_NE(-1, close(fd));
CHECK_EQ(PACKAGE_MAGIC, pkg->magic, "corrupt package: %`'s", path);
pkg->strings.p = (char *)((intptr_t)pkg->strings.p + (intptr_t)pkg);

View file

@ -286,6 +286,15 @@ o/$(MODE)/tool/net/redbean-original.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/tool/net/demo/.lua/.zip.o: \
tool/net/demo/.lua
o/$(MODE)/tool/net/demo/.zip.o: \
tool/net/demo
o/$(MODE)/tool/net/.zip.o: \
tool/net
o/$(MODE)/tool/.zip.o: \
tool
.PHONY: o/$(MODE)/tool/net
o/$(MODE)/tool/net: \
$(TOOL_NET_BINS) \