All of lore.kernel.org
 help / color / mirror / Atom feed
* Help with LANDLOCK_ACCESS_FS_EXECUTE
@ 2024-07-01 14:25 Andrea Cervesato
  2024-07-01 15:16 ` Mickaël Salaün
  0 siblings, 1 reply; 3+ messages in thread
From: Andrea Cervesato @ 2024-07-01 14:25 UTC (permalink / raw)
  To: landlock

[-- Attachment #1: Type: text/plain, Size: 835 bytes --]

Hi all,

I'm actually writing a test for LANDLOCK_ACCESS_FS_EXECUTE flag in LTP [1].
The test is really simple: it applies the EXECUTE landlock rule inside a 
folder and it verifies that a binary inside it can be executed.
A similar test applies the rule only to the specific binary and check 
again its execution.

But while I was writing the test, I encountered an issue with the 
specific rule setup, since EACCES is raised unexpectedly during binary 
execution.
So I wrote a reproducer, assuming that LTP might be the issue, but it's 
not. The reproducer actually shows that binary can't be executed after 
applying the EXECUTE rule.

I will attach the source code to this email. Can you please tell me if 
there's something wrong with it?

Best regards,
Andrea Cervesato


[1] https://linux-test-project.readthedocs.io/en/latest/

[-- Attachment #2: exec-tester.c --]
[-- Type: text/x-csrc, Size: 3207 bytes --]

/*
 * Compile with:
 *    gcc -lc exec-tester.c -o exec-tester
 *
 * Ensure that "landlock_exec" is present and it's executable via "execve".
 * Can be a compiled source like:
 *
 *    int main(void) { return 0; }
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/landlock.h>

#define FILE_EXEC "landlock_exec"

static inline int landlock_create_ruleset(const struct landlock_ruleset_attr *attr,
	size_t size, uint32_t flags)
{
	return syscall(SYS_landlock_create_ruleset,
		attr, size, flags);
}

static inline int landlock_add_rule(int ruleset_fd, enum landlock_rule_type rule_type,
	const void *rule_attr, uint32_t flags)
{
	return syscall(SYS_landlock_add_rule,
		ruleset_fd, rule_type, rule_attr, flags);
}

static inline int landlock_restrict_self(int ruleset_fd, int flags)
{
	return syscall(SYS_landlock_restrict_self,
		ruleset_fd, flags);
}

static void run_test(const int with_landlock)
{
	int rval;
	pid_t pid;
	int status;
	int fd = -1;
	int ruleset_fd = -1;

	if (with_landlock) {
		struct landlock_ruleset_attr ruleset_attr = {
			.handled_access_fs =
				LANDLOCK_ACCESS_FS_EXECUTE |
				LANDLOCK_ACCESS_FS_WRITE_FILE |
				LANDLOCK_ACCESS_FS_READ_FILE
		};

		ruleset_fd = landlock_create_ruleset(
			&ruleset_attr, sizeof(struct landlock_ruleset_attr), 0);
		if (ruleset_fd == -1) {
			printf("landlock_create_ruleset() error %s\n", strerror(errno));
			goto end;
		}

		fd = open(FILE_EXEC, O_PATH | O_CLOEXEC, 0744);
		if (fd == -1) {
			printf("Can't open file '%s'\n", FILE_EXEC);
			goto end;
		}

		struct landlock_path_beneath_attr path_beneath_attr = {
			.allowed_access = LANDLOCK_ACCESS_FS_EXECUTE,
			.parent_fd = fd,
		};

		rval = landlock_add_rule(
			ruleset_fd,
			LANDLOCK_RULE_PATH_BENEATH,
			&path_beneath_attr,
			0);
		if (rval == -1) {
			printf("landlock_add_rule error: %s\n", strerror(errno));
			goto end;
		}

		rval = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
		if (rval == -1) {
			printf("prctl error\n");
			goto end;
		}

		rval = landlock_restrict_self(ruleset_fd, 0);
		if (rval == -1) {
			printf("landlock_restrict_self error: %s\n", strerror(errno));
			goto end;
		}
	}

	pid = fork();
	if (pid == -1) {
		printf("execve() error: %s\n", strerror(errno));
		goto end;
	}

	if (!pid) {
		char *args[] = {(char *)FILE_EXEC, NULL};

		rval = execve(FILE_EXEC, args, NULL);
		if (rval == -1)
			printf("Failed to execute test binary: %s\n", strerror(errno));

		_exit(1);
	}

	waitpid(pid, &status, 0);
	if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
		goto end;

	printf("Binary executed\n");

end:
	if (ruleset_fd != -1)
		close(ruleset_fd);

	if (fd != -1)
		close(fd);
}

int main(void)
{
	printf("\nTesting LANDLOCK_ACCESS_FS_EXECUTE functionality\n\n");
	printf("----------------------------------------\n");
	printf("\tRunning test without landlock\n");
	printf("\t");
	run_test(0);
	printf("\n");

	printf("----------------------------------------\n");
	printf("\tRunning test with landlock activated\n");
	printf("\t");
	run_test(1);
	printf("\n");

	return 0;
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Help with LANDLOCK_ACCESS_FS_EXECUTE
  2024-07-01 14:25 Help with LANDLOCK_ACCESS_FS_EXECUTE Andrea Cervesato
@ 2024-07-01 15:16 ` Mickaël Salaün
  2024-07-17  8:51   ` Günther Noack
  0 siblings, 1 reply; 3+ messages in thread
From: Mickaël Salaün @ 2024-07-01 15:16 UTC (permalink / raw)
  To: Andrea Cervesato; +Cc: landlock

Hi Andrea,

On Mon, Jul 01, 2024 at 04:25:53PM +0200, Andrea Cervesato wrote:
> Hi all,
> 
> I'm actually writing a test for LANDLOCK_ACCESS_FS_EXECUTE flag in LTP [1].
> The test is really simple: it applies the EXECUTE landlock rule inside a
> folder and it verifies that a binary inside it can be executed.
> A similar test applies the rule only to the specific binary and check again
> its execution.

Good to know you're working on that!

> 
> But while I was writing the test, I encountered an issue with the specific
> rule setup, since EACCES is raised unexpectedly during binary execution.
> So I wrote a reproducer, assuming that LTP might be the issue, but it's not.
> The reproducer actually shows that binary can't be executed after applying
> the EXECUTE rule.
> 
> I will attach the source code to this email. Can you please tell me if
> there's something wrong with it?

I guess the binary you're trying to execute is dynamically linked, which
means that the kernel needs to open the related .so files on behalf of
the calling (sandboxed) process, which means that
LANDLOCK_ACCESS_FS_READ_FILE needs to be allowed on these files.  You
can use a static binary to avoid this kind of issue, or just not handle
LANDLOCK_ACCESS_FS_READ_FILE.

> 
> Best regards,
> Andrea Cervesato
> 
> 
> [1] https://linux-test-project.readthedocs.io/en/latest/



^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Help with LANDLOCK_ACCESS_FS_EXECUTE
  2024-07-01 15:16 ` Mickaël Salaün
@ 2024-07-17  8:51   ` Günther Noack
  0 siblings, 0 replies; 3+ messages in thread
From: Günther Noack @ 2024-07-17  8:51 UTC (permalink / raw)
  To: Mickaël Salaün; +Cc: Andrea Cervesato, landlock

Hello!

(Re-sending this message from July 1st for the record, as it was
previously rejected by the mailing list and only went to the
discussion participants.  The problem was resolved in the end.)

On Mon, Jul 01, 2024 at 05:16:19PM +0200, Mickaël Salaün wrote:
> On Mon, Jul 01, 2024 at 04:25:53PM +0200, Andrea Cervesato wrote:
> > Hi all,
> > 
> > I'm actually writing a test for LANDLOCK_ACCESS_FS_EXECUTE flag in LTP [1].
> > The test is really simple: it applies the EXECUTE landlock rule inside a
> > folder and it verifies that a binary inside it can be executed.
> > A similar test applies the rule only to the specific binary and check again
> > its execution.
> 
> Good to know you're working on that!
> 
> > 
> > But while I was writing the test, I encountered an issue with the specific
> > rule setup, since EACCES is raised unexpectedly during binary execution.
> > So I wrote a reproducer, assuming that LTP might be the issue, but it's not.
> > The reproducer actually shows that binary can't be executed after applying
> > the EXECUTE rule.
> > 
> > I will attach the source code to this email. Can you please tell me if
> > there's something wrong with it?
> 
> I guess the binary you're trying to execute is dynamically linked, which
> means that the kernel needs to open the related .so files on behalf of
> the calling (sandboxed) process, which means that
> LANDLOCK_ACCESS_FS_READ_FILE needs to be allowed on these files.  You
> can use a static binary to avoid this kind of issue, or just not handle
> LANDLOCK_ACCESS_FS_READ_FILE.

I've tried this out, it seems that it actually needs both
LANDLOCK_ACCESS_FS_READ_FILE and LANDLOCK_ACCESS_FS_EXECUTE on that
file in order to execute it, even if the file is statically linked
(LDFLAGS=3D-stat= ic make landlock_exec).

Once you have execve(2) succeeding, it gets more complicated with
shared libraries in the mix.  If you are looking to support something
like that, it helps to read the man page ld.so(8) to understand what
paths need to be whitelisted for shared libraries. The XDG Base
Directory specification can help with the location of config files. (
https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.htm=
l).

Your program runs nicely under `strace -f`, which helps to debug the
exact place where it fails - I tend to use something like that as a
driver for making it work.

–-Günther

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2024-07-17  8:51 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-07-01 14:25 Help with LANDLOCK_ACCESS_FS_EXECUTE Andrea Cervesato
2024-07-01 15:16 ` Mickaël Salaün
2024-07-17  8:51   ` Günther Noack

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.