git: dcdad299479e - main - fileargs: add support for realpath

Mariusz Zaborski oshogbo at FreeBSD.org
Sun Jan 10 11:44:14 UTC 2021


The branch main has been updated by oshogbo:

URL: https://cgit.FreeBSD.org/src/commit/?id=dcdad299479e2d224448cd8a09a33afc1328aa13

commit dcdad299479e2d224448cd8a09a33afc1328aa13
Author:     Mariusz Zaborski <oshogbo at FreeBSD.org>
AuthorDate: 2021-01-10 11:44:06 +0000
Commit:     Mariusz Zaborski <oshogbo at FreeBSD.org>
CommitDate: 2021-01-10 11:44:06 +0000

    fileargs: add support for realpath
---
 lib/libcasper/services/cap_fileargs/Makefile       |   1 +
 lib/libcasper/services/cap_fileargs/cap_fileargs.3 |  13 +-
 lib/libcasper/services/cap_fileargs/cap_fileargs.c |  62 ++++++++-
 lib/libcasper/services/cap_fileargs/cap_fileargs.h |   6 +
 .../services/cap_fileargs/tests/fileargs_test.c    | 145 +++++++++++++++++++++
 5 files changed, 224 insertions(+), 3 deletions(-)

diff --git a/lib/libcasper/services/cap_fileargs/Makefile b/lib/libcasper/services/cap_fileargs/Makefile
index 04787c01db35..22230f82d9f4 100644
--- a/lib/libcasper/services/cap_fileargs/Makefile
+++ b/lib/libcasper/services/cap_fileargs/Makefile
@@ -35,5 +35,6 @@ MLINKS+=cap_fileargs.3 fileargs_init.3
 MLINKS+=cap_fileargs.3 fileargs_initnv.3
 MLINKS+=cap_fileargs.3 fileargs_lstat.3
 MLINKS+=cap_fileargs.3 fileargs_open.3
+MLINKS+=cap_fileargs.3 fileargs_realpath.3
 
 .include <bsd.lib.mk>
diff --git a/lib/libcasper/services/cap_fileargs/cap_fileargs.3 b/lib/libcasper/services/cap_fileargs/cap_fileargs.3
index b59d30b2d595..acf51e4ed62b 100644
--- a/lib/libcasper/services/cap_fileargs/cap_fileargs.3
+++ b/lib/libcasper/services/cap_fileargs/cap_fileargs.3
@@ -24,7 +24,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd May 5, 2020
+.Dd January 10, 2021
 .Dt CAP_FILEARGS 3
 .Os
 .Sh NAME
@@ -59,6 +59,8 @@
 .Fn fileargs_open "fileargs_t *fa" "const char *name"
 .Ft "FILE *"
 .Fn fileargs_fopen "fileargs_t *fa" "const char *name" "const char *mode"
+.Ft "char *"
+.Fn fileargs_realpath "fileargs_t *fa" "const char *pathname" "char *reserved_path"
 .Sh DESCRIPTION
 The library is used to simplify Capsicumizing a tools that are using file system.
 Idea behind the library is that we are passing a remaining
@@ -115,6 +117,9 @@ and
 .It FA_LSTAT
 Allow
 .Fn fileargs_lstat .
+.It FA_REALPATH
+Allow
+.Fn fileargs_realpath .
 .El
 .Pp
 The function
@@ -161,6 +166,11 @@ and
 expect that all arguments are fetched from the
 .Va fileargs_t
 structure.
+.Pp
+The function
+.Fn fileargs_realpath
+is equivalent to
+.Xr realpath 3 .
 .Sh LIMITS
 This section describe which values and types should be used to pass arguments to the
 .Fa system.fileargs
@@ -261,6 +271,7 @@ fileargs_free(fa);
 .Xr err 3 ,
 .Xr fopen 3 ,
 .Xr getopt 3 ,
+.Xr realpath 3 ,
 .Xr capsicum 4 ,
 .Xr nv 9
 .Sh HISTORY
diff --git a/lib/libcasper/services/cap_fileargs/cap_fileargs.c b/lib/libcasper/services/cap_fileargs/cap_fileargs.c
index a777647b1720..ecab23004fcf 100644
--- a/lib/libcasper/services/cap_fileargs/cap_fileargs.c
+++ b/lib/libcasper/services/cap_fileargs/cap_fileargs.c
@@ -1,7 +1,7 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
- * Copyright (c) 2018 Mariusz Zaborski <oshogbo at FreeBSD.org>
+ * Copyright (c) 2018-2021 Mariusz Zaborski <oshogbo at FreeBSD.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -412,6 +412,41 @@ fileargs_lstat(fileargs_t *fa, const char *name, struct stat *sb)
 	return (0);
 }
 
+char *
+fileargs_realpath(fileargs_t *fa, const char *pathname, char *reserved_path)
+{
+	nvlist_t *nvl;
+	char *ret;
+
+	assert(fa != NULL);
+	assert(fa->fa_magic == FILEARGS_MAGIC);
+
+	if (pathname == NULL) {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	if (fa->fa_chann == NULL) {
+		errno = ENOTCAPABLE;
+		return (NULL);
+	}
+
+	nvl = fileargs_fetch(fa, pathname, "realpath");
+	if (nvl == NULL)
+		return (NULL);
+
+	if (reserved_path != NULL) {
+		ret = reserved_path;
+		strcpy(reserved_path,
+		    nvlist_get_string(nvl, "realpath"));
+	} else {
+		ret = nvlist_take_string(nvl, "realpath");
+	}
+	nvlist_destroy(nvl);
+
+	return (ret);
+}
+
 void
 fileargs_free(fileargs_t *fa)
 {
@@ -631,6 +666,28 @@ fileargs_command_lstat(const nvlist_t *limits, nvlist_t *nvlin,
 	return (0);
 }
 
+static int
+fileargs_command_realpath(const nvlist_t *limits, nvlist_t *nvlin,
+    nvlist_t *nvlout)
+{
+	const char *pathname;
+	char *resolvedpath;
+
+	if (limits == NULL)
+		return (ENOTCAPABLE);
+
+	if (!fileargs_allowed(limits, nvlin, FA_REALPATH))
+		return (ENOTCAPABLE);
+
+	pathname = nvlist_get_string(nvlin, "name");
+	resolvedpath = realpath(pathname, NULL);
+	if (resolvedpath == NULL)
+		return (errno);
+
+	nvlist_move_string(nvlout, "realpath", resolvedpath);
+	return (0);
+}
+
 static int
 fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin,
     nvlist_t *nvlout)
@@ -668,9 +725,10 @@ fileargs_command(const char *cmd, const nvlist_t *limits,
 
 	if (strcmp(cmd, "open") == 0)
 		return (fileargs_command_open(limits, nvlin, nvlout));
-
 	if (strcmp(cmd, "lstat") == 0)
 		return (fileargs_command_lstat(limits, nvlin, nvlout));
+	if (strcmp(cmd, "realpath") == 0)
+		return (fileargs_command_realpath(limits, nvlin, nvlout));
 
 	return (EINVAL);
 }
diff --git a/lib/libcasper/services/cap_fileargs/cap_fileargs.h b/lib/libcasper/services/cap_fileargs/cap_fileargs.h
index 03ff5c29d6c0..6e8523cb9423 100644
--- a/lib/libcasper/services/cap_fileargs/cap_fileargs.h
+++ b/lib/libcasper/services/cap_fileargs/cap_fileargs.h
@@ -39,6 +39,7 @@
 
 #define	FA_OPEN		1
 #define	FA_LSTAT	2
+#define	FA_REALPATH	4
 
 #ifdef WITH_CASPER
 struct fileargs;
@@ -55,6 +56,8 @@ fileargs_t *fileargs_initnv(nvlist_t *limits);
 fileargs_t *fileargs_cinitnv(cap_channel_t *cas, nvlist_t *limits);
 int fileargs_lstat(fileargs_t *fa, const char *name, struct stat *sb);
 int fileargs_open(fileargs_t *fa, const char *name);
+char *fileargs_realpath(fileargs_t *fa, const char *pathname,
+    char *reserved_path);
 void fileargs_free(fileargs_t *fa);
 FILE *fileargs_fopen(fileargs_t *fa, const char *name, const char *mode);
 
@@ -117,6 +120,9 @@ fileargs_cinitnv(cap_channel_t *cas __unused, nvlist_t *limits)
 	lstat(name, sb)
 #define	fileargs_open(fa, name)							\
 	open(name, fa->fa_flags, fa->fa_mode)
+#define	fileargs_realpath(fa, pathname, reserved_path)				\
+	realpath(pathname, reserved_path)
+
 static inline
 FILE *fileargs_fopen(fileargs_t *fa, const char *name, const char *mode)
 {
diff --git a/lib/libcasper/services/cap_fileargs/tests/fileargs_test.c b/lib/libcasper/services/cap_fileargs/tests/fileargs_test.c
index ad889bb2986f..9a7f9dfcb9aa 100644
--- a/lib/libcasper/services/cap_fileargs/tests/fileargs_test.c
+++ b/lib/libcasper/services/cap_fileargs/tests/fileargs_test.c
@@ -141,6 +141,57 @@ test_file_lstat(fileargs_t *fa, const char *file)
 	return (0);
 }
 
+static int
+test_file_realpath_static(fileargs_t *fa, const char *file)
+{
+	char fapath[PATH_MAX], origpath[PATH_MAX];
+
+	if (fileargs_realpath(fa, file, fapath) == NULL)
+		return (errno);
+
+	ATF_REQUIRE(realpath(file, origpath) != NULL);
+
+	if (strcmp(fapath, origpath) != 0)
+		return (EINVAL);
+
+	return (0);
+}
+
+static int
+test_file_realpath_alloc(fileargs_t *fa, const char *file)
+{
+	char *fapath, *origpath;
+	int serrno;
+
+	fapath = fileargs_realpath(fa, file, NULL);
+	if (fapath == NULL)
+		return (errno);
+
+	origpath = realpath(file, NULL);
+	ATF_REQUIRE(origpath != NULL);
+
+	serrno = 0;
+	if (strcmp(fapath, origpath) != 0)
+		serrno = EINVAL;
+
+	free(fapath);
+	free(origpath);
+
+	return (serrno);
+}
+
+static int
+test_file_realpath(fileargs_t *fa, const char *file)
+{
+	int serrno;
+
+	serrno = test_file_realpath_static(fa, file);
+	if (serrno != 0)
+		return serrno;
+
+	return (test_file_realpath_alloc(fa, file));
+}
+
 static int
 test_file_mode(int fd, int mode)
 {
@@ -254,6 +305,8 @@ ATF_TC_BODY(fileargs__open_read, tc)
 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
 		ATF_REQUIRE(test_file_write(fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(close(fd) == 0);
@@ -297,6 +350,8 @@ ATF_TC_BODY(fileargs__open_write, tc)
 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
 		ATF_REQUIRE(test_file_read(fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(close(fd) == 0);
@@ -337,6 +392,8 @@ ATF_TC_BODY(fileargs__open_create, tc)
 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(close(fd) == 0);
@@ -417,6 +474,8 @@ ATF_TC_BODY(fileargs__fopen_read, tc)
 		    ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
 		ATF_REQUIRE(test_file_fwrite(pfile) == EBADF);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(fclose(pfile) == 0);
@@ -463,6 +522,8 @@ ATF_TC_BODY(fileargs__fopen_write, tc)
 		    ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
 		ATF_REQUIRE(test_file_fread(pfile) == EBADF);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(fclose(pfile) == 0);
@@ -504,6 +565,8 @@ ATF_TC_BODY(fileargs__fopen_create, tc)
 		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_fopen(fa, TEST_FILE, "w+", NULL) ==
 		    ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(fclose(pfile) == 0);
@@ -535,6 +598,8 @@ ATF_TC_BODY(fileargs__lstat, tc)
 		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_lstat(fa, TEST_FILE) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, &fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 	}
 }
 ATF_TC_CLEANUP(fileargs__lstat, tc)
@@ -542,6 +607,36 @@ ATF_TC_CLEANUP(fileargs__lstat, tc)
 	clear_files();
 }
 
+ATF_TC_WITH_CLEANUP(fileargs__realpath);
+ATF_TC_HEAD(fileargs__realpath, tc) {}
+ATF_TC_BODY(fileargs__realpath, tc)
+{
+	fileargs_t *fa;
+	size_t i;
+	int fd;
+
+	prepare_files(MAX_FILES, true);
+
+	fa = fileargs_init(MAX_FILES, files, 0, 0, NULL, FA_REALPATH);
+	ATF_REQUIRE(fa != NULL);
+
+	for (i = 0; i < MAX_FILES; i++) {
+		/* ALLOWED */
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == 0);
+
+		/* DISALLOWED */
+		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_lstat(fa, TEST_FILE) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_open(fa, TEST_FILE, &fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
+	}
+}
+ATF_TC_CLEANUP(fileargs__realpath, tc)
+{
+	clear_files();
+}
+
 ATF_TC_WITH_CLEANUP(fileargs__open_lstat);
 ATF_TC_HEAD(fileargs__open_lstat, tc) {}
 ATF_TC_BODY(fileargs__open_lstat, tc)
@@ -576,6 +671,8 @@ ATF_TC_BODY(fileargs__open_lstat, tc)
 		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
 		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
 		ATF_REQUIRE(test_file_write(fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_realpath(fa, TEST_FILE) == ENOTCAPABLE);
 
 		/* CLOSE */
 		ATF_REQUIRE(close(fd) == 0);
@@ -586,6 +683,51 @@ ATF_TC_CLEANUP(fileargs__open_lstat, tc)
 	clear_files();
 }
 
+ATF_TC_WITH_CLEANUP(fileargs__open_realpath);
+ATF_TC_HEAD(fileargs__open_realpath, tc) {}
+ATF_TC_BODY(fileargs__open_realpath, tc)
+{
+	cap_rights_t rights, norights;
+	fileargs_t *fa;
+	size_t i;
+	int fd;
+
+	prepare_files(MAX_FILES, true);
+
+	cap_rights_init(&rights, CAP_READ | CAP_FCNTL);
+	cap_rights_init(&norights, CAP_WRITE);
+	fa = fileargs_init(MAX_FILES, files, O_RDONLY, 0, &rights,
+	    FA_OPEN | FA_REALPATH);
+	ATF_REQUIRE(fa != NULL);
+
+	for (i = 0; i < MAX_FILES; i++) {
+		/* ALLOWED */
+		/* We open file twice to check if we can. */
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == 0);
+		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
+		ATF_REQUIRE(close(fd) == 0);
+
+		ATF_REQUIRE(test_file_open(fa, files[i], &fd) == 0);
+		ATF_REQUIRE(test_file_realpath(fa, files[i]) == 0);
+		ATF_REQUIRE(test_file_mode(fd, O_RDONLY) == 0);
+		ATF_REQUIRE(test_file_cap(fd, &rights) == true);
+		ATF_REQUIRE(test_file_read(fd) == 0);
+
+		/* DISALLOWED */
+		ATF_REQUIRE(test_file_open(fa, TEST_FILE, NULL) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_cap(fd, &norights) == false);
+		ATF_REQUIRE(test_file_write(fd) == ENOTCAPABLE);
+		ATF_REQUIRE(test_file_lstat(fa, files[i]) == ENOTCAPABLE);
+
+		/* CLOSE */
+		ATF_REQUIRE(close(fd) == 0);
+	}
+}
+ATF_TC_CLEANUP(fileargs__open_realpath, tc)
+{
+	clear_files();
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
@@ -600,7 +742,10 @@ ATF_TP_ADD_TCS(tp)
 
 	ATF_TP_ADD_TC(tp, fileargs__lstat);
 
+	ATF_TP_ADD_TC(tp, fileargs__realpath);
+
 	ATF_TP_ADD_TC(tp, fileargs__open_lstat);
+	ATF_TP_ADD_TC(tp, fileargs__open_realpath);
 
 	return (atf_no_error());
 }


More information about the dev-commits-src-all mailing list