cap_fileargs: add fileargs_lstat service

Add fileargs_lstat function to cap_fileargs casper service to be able to
lstat files while in capability mode.  It can only lstat files given in
fileargs_init.

Submitted by:	Bora Özarslan <borako.ozarslan@gmail.com>
Reviewed by:	oshogbo, cem (partial)
MFC after:	3 weeks
Relnotes:	Yes
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D19548
This commit is contained in:
Ed Maste 2019-04-17 16:02:57 +00:00
parent fc07a84f80
commit 7b558caee3
3 changed files with 207 additions and 29 deletions

View File

@ -24,7 +24,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd November 12, 2018
.Dd April 17, 2019
.Dt CAP_FILEARGS 3
.Os
.Sh NAME
@ -33,6 +33,7 @@
.Nm fileargs_init ,
.Nm fileargs_initnv ,
.Nm fileargs_free ,
.Nm fileargs_lstat ,
.Nm fileargs_open ,
.Nm fileargs_fopen
.Nd "library for handling files in capability mode"
@ -43,9 +44,9 @@
.In libcasper.h
.In casper/cap_fileargs.h
.Ft "fileargs_t *"
.Fn fileargs_init "int argc" "char *argv[]" "int flags" "mode_t mode" "cap_rights_t *rightsp"
.Fn fileargs_init "int argc" "char *argv[]" "int flags" "mode_t mode" "cap_rights_t *rightsp" "int operations"
.Ft "fileargs_t *"
.Fn fileargs_cinit "cap_channel_t *cas" "int argc" "char *argv[]" "int flags" "mode_t mode" "cap_rights_t *rightsp"
.Fn fileargs_cinit "cap_channel_t *cas" "int argc" "char *argv[]" "int flags" "mode_t mode" "cap_rights_t *rightsp" "int operations"
.Ft "fileargs_t *"
.Fn fileargs_cinitnv "cap_channel_t *cas" "nvlist_t *limits"
.Ft "fileargs_t *"
@ -53,6 +54,8 @@
.Ft "void"
.Fn fileargs_free "fileargs_t *fa"
.Ft "int"
.Fn fileargs_lstat "fileargs_t *fa" "const char *path" "struct stat *sb"
.Ft "int"
.Fn fileargs_open "fileargs_t *fa" "const char *name"
.Ft "FILE *"
.Fn fileargs_fopen "fileargs_t *fa" "const char *name" "const char *mode"
@ -97,6 +100,22 @@ The
argument contains a list of the capability rights which file should be limited to.
For more details of the capability rights see
.Xr cap_rights_init 3 .
The
.Fa operations
argument limits the operations that are available using
.Nm system.fileargs .
.Fa operations
is a combination of:
.Bl -ohang -offset indent
.It FA_OPEN
Allow
.Fn fileargs_open
and
.Fn fileargs_fopen .
.It FA_LSTAT
Allow
.Fn fileargs_lstat .
.El
.Pp
The function
.Fn fileargs_cinit
@ -126,6 +145,11 @@ The function handle
.Dv NULL
argument.
.Pp
The function
.Fn fileargs_lstat
is equivalent to
.Xr lstat 2 .
.Pp
The functions
.Fn fileargs_open
and
@ -165,6 +189,15 @@ must contain the
The
.Va mode
argument tells which what mode file should be created.
.It operations (NV_TYPE_NUMBER)
The
.Va operations
limits the usable operations for
.Fa system.fileargs .
The possible values are explained as
.Va operations
argument with
.Fn fileargs_init .
.El
.Pp
The
@ -201,7 +234,7 @@ argv += optind;
/* Create capability to the system.fileargs service. */
fa = fileargs_init(argc, argv, O_RDONLY, 0,
cap_rights_init(&rights, CAP_READ));
cap_rights_init(&rights, CAP_READ), FA_OPEN);
if (fa == NULL)
err(1, "unable to open system.fileargs service");
@ -222,6 +255,7 @@ fileargs_free(fa);
.Ed
.Sh SEE ALSO
.Xr cap_enter 2 ,
.Xr lstat 2 ,
.Xr open 2 ,
.Xr cap_rights_init 3 ,
.Xr err 3 ,

View File

@ -35,6 +35,7 @@ __FBSDID("$FreeBSD$");
#include <sys/cnv.h>
#include <sys/dnv.h>
#include <sys/nv.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
@ -59,7 +60,36 @@ struct fileargs {
};
static int
fileargs_get_cache(fileargs_t *fa, const char *name)
fileargs_get_lstat_cache(fileargs_t *fa, const char *name, struct stat *sb)
{
const nvlist_t *nvl;
size_t size;
const void *buf;
assert(fa != NULL);
assert(fa->fa_magic == FILEARGS_MAGIC);
assert(name != NULL);
if (fa->fa_cache == NULL)
return (-1);
nvl = dnvlist_get_nvlist(fa->fa_cache, name, NULL);
if (nvl == NULL)
return (-1);
if (!nvlist_exists_binary(nvl, "stat")) {
return (-1);
}
buf = nvlist_get_binary(nvl, "stat", &size);
assert(size == sizeof(*sb));
memcpy(sb, buf, size);
return (0);
}
static int
fileargs_get_fd_cache(fileargs_t *fa, const char *name)
{
int fd;
const nvlist_t *nvl;
@ -80,6 +110,12 @@ fileargs_get_cache(fileargs_t *fa, const char *name)
return (-1);
tnvl = nvlist_take_nvlist(fa->fa_cache, name);
if (!nvlist_exists_descriptor(tnvl, "fd")) {
nvlist_destroy(tnvl);
return (-1);
}
fd = nvlist_take_descriptor(tnvl, "fd");
nvlist_destroy(tnvl);
@ -102,7 +138,7 @@ fileargs_set_cache(fileargs_t *fa, nvlist_t *nvl)
}
static nvlist_t*
fileargs_fetch(fileargs_t *fa, const char *name)
fileargs_fetch(fileargs_t *fa, const char *name, const char *cmd)
{
nvlist_t *nvl;
int serrno;
@ -111,7 +147,7 @@ fileargs_fetch(fileargs_t *fa, const char *name)
assert(name != NULL);
nvl = nvlist_create(NV_FLAG_NO_UNIQUE);
nvlist_add_string(nvl, "cmd", "open");
nvlist_add_string(nvl, "cmd", cmd);
nvlist_add_string(nvl, "name", name);
nvl = cap_xfer_nvlist(fa->fa_chann, nvl);
@ -130,7 +166,7 @@ fileargs_fetch(fileargs_t *fa, const char *name)
static nvlist_t *
fileargs_create_limit(int argc, const char * const *argv, int flags,
mode_t mode, cap_rights_t *rightsp)
mode_t mode, cap_rights_t *rightsp, int operations)
{
nvlist_t *limits;
int i;
@ -140,6 +176,7 @@ fileargs_create_limit(int argc, const char * const *argv, int flags,
return (NULL);
nvlist_add_number(limits, "flags", flags);
nvlist_add_number(limits, "operations", operations);
if (rightsp != NULL) {
nvlist_add_binary(limits, "cap_rights", rightsp,
sizeof(*rightsp));
@ -172,7 +209,7 @@ fileargs_create(cap_channel_t *chan, int fdflags)
fileargs_t *
fileargs_init(int argc, char *argv[], int flags, mode_t mode,
cap_rights_t *rightsp)
cap_rights_t *rightsp, int operations)
{
nvlist_t *limits;
@ -181,7 +218,7 @@ fileargs_init(int argc, char *argv[], int flags, mode_t mode,
}
limits = fileargs_create_limit(argc, (const char * const *)argv, flags,
mode, rightsp);
mode, rightsp, operations);
if (limits == NULL)
return (NULL);
@ -190,7 +227,7 @@ fileargs_init(int argc, char *argv[], int flags, mode_t mode,
fileargs_t *
fileargs_cinit(cap_channel_t *cas, int argc, char *argv[], int flags,
mode_t mode, cap_rights_t *rightsp)
mode_t mode, cap_rights_t *rightsp, int operations)
{
nvlist_t *limits;
@ -199,7 +236,7 @@ fileargs_cinit(cap_channel_t *cas, int argc, char *argv[], int flags,
}
limits = fileargs_create_limit(argc, (const char * const *)argv, flags,
mode, rightsp);
mode, rightsp, operations);
if (limits == NULL)
return (NULL);
@ -234,7 +271,7 @@ fileargs_cinitnv(cap_channel_t *cas, nvlist_t *limits)
cap_channel_t *chann;
fileargs_t *fa;
int serrno, ret;
int flags;
int flags, operations;
assert(cas != NULL);
@ -252,6 +289,7 @@ fileargs_cinitnv(cap_channel_t *cas, nvlist_t *limits)
}
flags = nvlist_get_number(limits, "flags");
operations = nvlist_get_number(limits, "operations");
/* Limits are consumed no need to free them. */
ret = cap_limit_set(chann, limits);
@ -291,11 +329,11 @@ fileargs_open(fileargs_t *fa, const char *name)
return (-1);
}
fd = fileargs_get_cache(fa, name);
fd = fileargs_get_fd_cache(fa, name);
if (fd != -1)
return (fd);
nvl = fileargs_fetch(fa, name);
nvl = fileargs_fetch(fa, name, "open");
if (nvl == NULL)
return (-1);
@ -322,6 +360,53 @@ fileargs_fopen(fileargs_t *fa, const char *name, const char *mode)
return (fdopen(fd, mode));
}
int
fileargs_lstat(fileargs_t *fa, const char *name, struct stat *sb)
{
nvlist_t *nvl;
const void *buf;
size_t size;
char *cmd;
assert(fa != NULL);
assert(fa->fa_magic == FILEARGS_MAGIC);
if (name == NULL) {
errno = EINVAL;
return (-1);
}
if (sb == NULL) {
errno = EFAULT;
return (-1);
}
if (fa->fa_chann == NULL) {
errno = ENOTCAPABLE;
return (-1);
}
if (fileargs_get_lstat_cache(fa, name, sb) != -1)
return (0);
nvl = fileargs_fetch(fa, name, "lstat");
if (nvl == NULL)
return (-1);
buf = nvlist_get_binary(nvl, "stat", &size);
assert(size == sizeof(*sb));
memcpy(sb, buf, size);
cmd = nvlist_take_string(nvl, "cmd");
if (strcmp(cmd, "cache") == 0)
fileargs_set_cache(fa, nvl);
else
nvlist_destroy(nvl);
free(cmd);
return (0);
}
void
fileargs_free(fileargs_t *fa)
{
@ -348,6 +433,7 @@ static void *cacheposition;
static bool allcached;
static const cap_rights_t *caprightsp;
static int capflags;
static int allowed_operations;
static mode_t capmode;
static int
@ -382,6 +468,7 @@ fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits,
void *cookie;
nvlist_t *new;
const char *fname;
struct stat sb;
if ((capflags & O_CREAT) != 0) {
allcached = true;
@ -409,14 +496,25 @@ fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits,
continue;
}
fd = open_file(fname);
if (fd < 0) {
i--;
continue;
new = nvlist_create(NV_FLAG_NO_UNIQUE);
if ((allowed_operations & FA_OPEN) != 0) {
fd = open_file(fname);
if (fd < 0) {
i--;
nvlist_destroy(new);
continue;
}
nvlist_move_descriptor(new, "fd", fd);
}
if ((allowed_operations & FA_LSTAT) != 0) {
if (lstat(fname, &sb) < 0) {
i--;
nvlist_destroy(new);
continue;
}
nvlist_add_binary(new, "stat", &sb, sizeof(sb));
}
new = nvlist_create(NV_FLAG_NO_UNIQUE);
nvlist_move_descriptor(new, "fd", fd);
nvlist_add_nvlist(nvlout, fname, new);
}
cacheposition = cookie;
@ -424,10 +522,13 @@ fileargs_add_cache(nvlist_t *nvlout, const nvlist_t *limits,
}
static bool
fileargs_allowed(const nvlist_t *limits, const nvlist_t *request)
fileargs_allowed(const nvlist_t *limits, const nvlist_t *request, int operation)
{
const char *name;
if ((allowed_operations & operation) == 0)
return (false);
name = dnvlist_get_string(request, "name", NULL);
if (name == NULL)
return (false);
@ -450,6 +551,7 @@ fileargs_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
return (ENOTCAPABLE);
capflags = (int)dnvlist_get_number(newlimits, "flags", 0);
allowed_operations = (int)dnvlist_get_number(newlimits, "operations", 0);
if ((capflags & O_CREAT) != 0)
capmode = (mode_t)nvlist_get_number(newlimits, "mode");
else
@ -460,6 +562,37 @@ fileargs_limit(const nvlist_t *oldlimits, const nvlist_t *newlimits)
return (0);
}
static int
fileargs_command_lstat(const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
{
int stat;
const char *name;
struct stat sb;
if (limits == NULL)
return (ENOTCAPABLE);
if (!fileargs_allowed(limits, nvlin, FA_LSTAT))
return (ENOTCAPABLE);
name = nvlist_get_string(nvlin, "name");
stat = lstat(name, &sb);
if (stat < 0)
return (errno);
if (!allcached && (lastname == NULL ||
strcmp(name, lastname) == 0)) {
nvlist_add_string(nvlout, "cmd", "cache");
fileargs_add_cache(nvlout, limits, name);
} else {
nvlist_add_string(nvlout, "cmd", "lstat");
}
nvlist_add_binary(nvlout, "stat", &sb, sizeof(sb));
return (0);
}
static int
fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin,
nvlist_t *nvlout)
@ -470,7 +603,7 @@ fileargs_command_open(const nvlist_t *limits, nvlist_t *nvlin,
if (limits == NULL)
return (ENOTCAPABLE);
if (!fileargs_allowed(limits, nvlin))
if (!fileargs_allowed(limits, nvlin, FA_OPEN))
return (ENOTCAPABLE);
name = nvlist_get_string(nvlin, "name");
@ -498,6 +631,9 @@ 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));
return (EINVAL);
}

View File

@ -36,16 +36,21 @@
#include <stdbool.h>
#define FA_OPEN 1
#define FA_LSTAT 2
#ifdef WITH_CASPER
struct fileargs;
typedef struct fileargs fileargs_t;
struct stat;
fileargs_t *fileargs_init(int argc, char *argv[], int flags, mode_t mode,
cap_rights_t *rightsp);
cap_rights_t *rightsp, int operations);
fileargs_t *fileargs_cinit(cap_channel_t *cas, int argc, char *argv[],
int flags, mode_t mode, cap_rights_t *rightsp);
int flags, mode_t mode, cap_rights_t *rightsp, int operations);
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);
void fileargs_free(fileargs_t *fa);
FILE *fileargs_fopen(fileargs_t *fa, const char *name, const char *mode);
@ -57,7 +62,7 @@ typedef struct fileargs {
static inline fileargs_t *
fileargs_init(int argc __unused, char *argv[] __unused, int flags, mode_t mode,
cap_rights_t *rightsp __unused) {
cap_rights_t *rightsp __unused, int operations __unused) {
fileargs_t *fa;
fa = malloc(sizeof(*fa));
@ -71,10 +76,10 @@ fileargs_init(int argc __unused, char *argv[] __unused, int flags, mode_t mode,
static inline fileargs_t *
fileargs_cinit(cap_channel_t *cas __unused, int argc, char *argv[], int flags,
mode_t mode, cap_rights_t *rightsp)
mode_t mode, cap_rights_t *rightsp, int operations)
{
return (fileargs_init(argc, argv, flags, mode, rightsp));
return (fileargs_init(argc, argv, flags, mode, rightsp, operations));
}
static inline fileargs_t *
@ -85,7 +90,8 @@ fileargs_initnv(nvlist_t *limits)
fa = fileargs_init(0, NULL,
nvlist_get_number(limits, "flags"),
dnvlist_get_number(limits, "mode", 0),
NULL);
NULL,
nvlist_get_number(limits, "operations"));
nvlist_destroy(limits);
return (fa);
@ -98,6 +104,8 @@ fileargs_cinitnv(cap_channel_t *cas __unused, nvlist_t *limits)
return (fileargs_initnv(limits));
}
#define fileargs_lstat(fa, name, sb) \
lstat(name, sb)
#define fileargs_open(fa, name) \
open(name, fa->fa_flags, fa->fa_mode)
#define fileargs_fopen(fa, name, mode) \