fileargs: add support for realpath

This commit is contained in:
Mariusz Zaborski 2021-01-10 12:44:06 +01:00
parent 81b3a0a341
commit dcdad29947
5 changed files with 224 additions and 3 deletions

View File

@ -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>

View File

@ -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

View File

@ -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);
}

View File

@ -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)
{

View File

@ -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());
}