fileargs: add support for realpath
This commit is contained in:
parent
81b3a0a341
commit
dcdad29947
@ -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>
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2018 Mariusz Zaborski <oshogbo@FreeBSD.org>
|
||||
* Copyright (c) 2018-2021 Mariusz Zaborski <oshogbo@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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user