From 5fa75d0823b66a49456790a19e203fec0bc67220 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 2 Jan 2019 22:34:44 -0500 Subject: posix1e: Split out ACL and capability handling from util --- COPYING | 2 +- Makefile | 18 +++++- color.c | 1 + eval.c | 1 + posix1e.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ posix1e.h | 33 +++++++++++ tests.sh | 6 +- util.c | 168 ------------------------------------------------------ util.h | 12 ---- 9 files changed, 248 insertions(+), 185 deletions(-) create mode 100644 posix1e.c create mode 100644 posix1e.h diff --git a/COPYING b/COPYING index 670e870..ac9a6a7 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (C) 2015-2018 Tavian Barnes +Copyright (C) 2015-2019 Tavian Barnes Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. diff --git a/Makefile b/Makefile index c631c35..319680c 100644 --- a/Makefile +++ b/Makefile @@ -63,7 +63,23 @@ ALL_LDLIBS = $(LOCAL_LDLIBS) $(LDLIBS) all: bfs -bfs: bftw.o color.o diag.o dstring.o eval.o exec.o main.o mtab.o opt.o parse.o printf.o spawn.o stat.o typo.o util.o +bfs: \ + bftw.o \ + color.o \ + diag.o \ + dstring.o \ + eval.o \ + exec.o \ + main.o \ + mtab.o \ + opt.o \ + parse.o \ + posix1e.o \ + printf.o \ + spawn.o \ + stat.o \ + typo.o \ + util.o $(CC) $(ALL_LDFLAGS) $^ $(ALL_LDLIBS) -o $@ sanitized: CFLAGS := -g $(WFLAGS) -fsanitize=address -fsanitize=undefined diff --git a/color.c b/color.c index a877ff7..82076cf 100644 --- a/color.c +++ b/color.c @@ -16,6 +16,7 @@ #include "color.h" #include "bftw.h" +#include "posix1e.h" #include "stat.h" #include "util.h" #include diff --git a/eval.c b/eval.c index 5b55b8d..37f8bf5 100644 --- a/eval.c +++ b/eval.c @@ -22,6 +22,7 @@ #include "dstring.h" #include "exec.h" #include "mtab.h" +#include "posix1e.h" #include "printf.h" #include "stat.h" #include "util.h" diff --git a/posix1e.c b/posix1e.c new file mode 100644 index 0000000..00bff10 --- /dev/null +++ b/posix1e.c @@ -0,0 +1,192 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2019 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +#include "posix1e.h" +#include "bftw.h" +#include "util.h" +#include +#include +#include +#include +#include + +#if BFS_HAS_SYS_ACL +# include +#endif + +#if BFS_HAS_POSIX1E_CAPABILITIES +# include +#endif + +#if BFS_HAS_SYS_ACL || BFS_HAS_POSIX1E_CAPABILITIES + +static const char *open_path(const struct BFTW *ftwbuf, int *fd) { +#ifdef O_PATH + // The POSIX.1e APIS predate the *at() family of functions. We'd still + // like to do something to avoid path re-traversals and limit races + // though. Ideally we could just do openat(..., O_PATH) (since we may + // not have read access) and pass that fd to something like cap_get_fd() + // but that will fail since fgetxattr() needs read access to the file. + // The workaround is to use O_PATH to open an fd and then pass + // /proc/self/fd/ to cap_get_path(). Inspired by + // https://android.googlesource.com/platform/bionic/+/2825f10b7f61558c264231a536cf3affc0d84204 + int flags = O_PATH; + if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) { + flags |= O_NOFOLLOW; + } + + *fd = openat(ftwbuf->at_fd, ftwbuf->at_path, flags); + if (*fd < 0) { + return NULL; + } + + size_t size = strlen("/proc/self/fd/") + CHAR_BIT*sizeof(int) + 1; + char *path = malloc(size); + if (!path) { + close(*fd); + *fd = -1; + return NULL; + } + + snprintf(path, size, "/proc/self/fd/%d", *fd); + return path; +#else + *fd = -1; + return ftwbuf->path; +#endif +} + +static void close_path(const struct BFTW *ftwbuf, const char *path, int fd) { + if (path && path != ftwbuf->path) { + free((void *)path); + } + if (fd >= 0) { + close(fd); + } +} + +#endif // BFS_HAS_SYS_ACL || BFS_HAS_POSIX1E_CAPABILITIES + +#if BFS_HAS_SYS_ACL + +/** Check if any ACLs of the given type are non-trivial. */ +static bool bfs_check_acl_type(const char *path, acl_type_t type) { + acl_t acl = acl_get_file(path, type); + if (!acl) { + return false; + } + + bool ret = false; + acl_entry_t entry; + for (int status = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + status > 0; + status = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) { +#if defined(ACL_USER_OBJ) && defined(ACL_GROUP_OBJ) && defined(ACL_OTHER) + acl_tag_t tag; + if (acl_get_tag_type(entry, &tag) != 0) { + continue; + } + if (tag != ACL_USER_OBJ && tag != ACL_GROUP_OBJ && tag != ACL_OTHER) { + ret = true; + break; + } +#else + ret = true; + break; +#endif + } + + acl_free(acl); + return ret; +} + +bool bfs_check_acl(const struct BFTW *ftwbuf) { + if (ftwbuf->typeflag == BFTW_LNK) { + return false; + } + + int fd; + const char *path = open_path(ftwbuf, &fd); + if (!path) { + return false; + } + + bool ret = false; + if (bfs_check_acl_type(path, ACL_TYPE_ACCESS)) { + ret = true; + } else if (bfs_check_acl_type(path, ACL_TYPE_DEFAULT)) { + ret = true; +#ifdef ACL_TYPE_EXTENDED + } else if (bfs_check_acl_type(path, ACL_TYPE_EXTENDED)) { + ret = true; +#endif + } + + close_path(ftwbuf, path, fd); + return ret; +} + +#else // !BFS_HAS_SYS_ACL + +bool bfs_check_acl(const struct BFTW *ftwbuf) { + return false; +} + +#endif + +#if BFS_HAS_POSIX1E_CAPABILITIES + +bool bfs_check_capabilities(const struct BFTW *ftwbuf) { + bool ret = false; + + if (ftwbuf->typeflag == BFTW_LNK) { + goto out; + } + + int fd; + const char *path = open_path(ftwbuf, &fd); + if (!path) { + goto out; + } + + cap_t caps = cap_get_file(path); + if (!caps) { + goto out_close; + } + + // TODO: Any better way to check for a non-empty capability set? + char *text = cap_to_text(caps, NULL); + if (!text) { + goto out_free_caps; + } + ret = text[0]; + + cap_free(text); +out_free_caps: + cap_free(caps); +out_close: + close_path(ftwbuf, path, fd); +out: + return ret; +} + +#else // !BFS_HAS_POSIX1E_CAPABILITIES + +bool bfs_check_capabilities(const struct BFTW *ftwbuf) { + return false; +} + +#endif diff --git a/posix1e.h b/posix1e.h new file mode 100644 index 0000000..da7c2b4 --- /dev/null +++ b/posix1e.h @@ -0,0 +1,33 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2019 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +#ifndef BFS_POSIX1E_H +#define BFS_POSIX1E_H + +#include "bftw.h" +#include "util.h" + +/** + * Check if a file has a non-trvial Access Control List. + */ +bool bfs_check_acl(const struct BFTW *ftwbuf); + +/** + * Check if a file has a non-trvial capability set. + */ +bool bfs_check_capabilities(const struct BFTW *ftwbuf); + +#endif // BFS_POSIX1E_H diff --git a/tests.sh b/tests.sh index 0b2678d..5905d38 100755 --- a/tests.sh +++ b/tests.sh @@ -174,10 +174,10 @@ function make_rainbow() { touchp "$1/mh1" ln "$1/mh1" "$1/mh2" mkfifo "$1/pipe" - # XXX: block - # XXX: chardev + # TODO: block + # TODO: chardev ln -s nowhere "$1/broken" - # XXX: socket + # TODO: socket touchp "$1"/s{u,g,ug}id chmod u+s "$1"/su{,g}id chmod g+s "$1"/s{u,}gid diff --git a/util.c b/util.c index ba7b75e..b708527 100644 --- a/util.c +++ b/util.c @@ -30,14 +30,6 @@ #include #include -#if BFS_HAS_SYS_ACL -# include -#endif - -#if BFS_HAS_POSIX1E_CAPABILITIES -# include -#endif - #if BFS_HAS_SYS_PARAM # include #endif @@ -385,163 +377,3 @@ int bfs_minor(dev_t dev) { return dev & 0xFF; #endif } - -#if BFS_HAS_SYS_ACL || BFS_HAS_POSIX1E_CAPABILITIES - -static const char *open_path(const struct BFTW *ftwbuf, int *fd) { -#ifdef O_PATH - // The POSIX.1e APIS predate the *at() family of functions. We'd still - // like to do something to avoid path re-traversals and limit races - // though. Ideally we could just do openat(..., O_PATH) (since we may - // not have read access) and pass that fd to something like cap_get_fd() - // but that will fail since fgetxattr() needs read access to the file. - // The workaround is to use O_PATH to open an fd and then pass - // /proc/self/fd/ to cap_get_path(). Inspired by - // https://android.googlesource.com/platform/bionic/+/2825f10b7f61558c264231a536cf3affc0d84204 - int flags = O_PATH; - if (ftwbuf->at_flags & AT_SYMLINK_NOFOLLOW) { - flags |= O_NOFOLLOW; - } - - *fd = openat(ftwbuf->at_fd, ftwbuf->at_path, flags); - if (*fd < 0) { - return NULL; - } - - size_t size = strlen("/proc/self/fd/") + CHAR_BIT*sizeof(int) + 1; - char *path = malloc(size); - if (!path) { - close(*fd); - *fd = -1; - return NULL; - } - - snprintf(path, size, "/proc/self/fd/%d", *fd); - return path; -#else - *fd = -1; - return ftwbuf->path; -#endif -} - -static void close_path(const struct BFTW *ftwbuf, const char *path, int fd) { - if (path && path != ftwbuf->path) { - free((void *)path); - } - if (fd >= 0) { - close(fd); - } -} - -#endif // BFS_HAS_SYS_ACL || BFS_HAS_POSIX1E_CAPABILITIES - -#if BFS_HAS_SYS_ACL - -/** Check if any ACLs of the given type are non-trivial. */ -static bool bfs_check_acl_type(const char *path, acl_type_t type) { - acl_t acl = acl_get_file(path, type); - if (!acl) { - return false; - } - - bool ret = false; - acl_entry_t entry; - for (int status = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); - status > 0; - status = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) { -#if defined(ACL_USER_OBJ) && defined(ACL_GROUP_OBJ) && defined(ACL_OTHER) - acl_tag_t tag; - if (acl_get_tag_type(entry, &tag) != 0) { - continue; - } - if (tag != ACL_USER_OBJ && tag != ACL_GROUP_OBJ && tag != ACL_OTHER) { - ret = true; - break; - } -#else - ret = true; - break; -#endif - } - - acl_free(acl); - return ret; -} - -bool bfs_check_acl(const struct BFTW *ftwbuf) { - if (ftwbuf->typeflag == BFTW_LNK) { - return false; - } - - int fd; - const char *path = open_path(ftwbuf, &fd); - if (!path) { - return false; - } - - bool ret = false; - if (bfs_check_acl_type(path, ACL_TYPE_ACCESS)) { - ret = true; - } else if (bfs_check_acl_type(path, ACL_TYPE_DEFAULT)) { - ret = true; -#ifdef ACL_TYPE_EXTENDED - } else if (bfs_check_acl_type(path, ACL_TYPE_EXTENDED)) { - ret = true; -#endif - } - - close_path(ftwbuf, path, fd); - return ret; -} - -#else // !BFS_HAS_SYS_ACL - -bool bfs_check_acl(const struct BFTW *ftwbuf) { - return false; -} - -#endif - -#if BFS_HAS_POSIX1E_CAPABILITIES - -bool bfs_check_capabilities(const struct BFTW *ftwbuf) { - bool ret = false; - - if (ftwbuf->typeflag == BFTW_LNK) { - goto out; - } - - int fd; - const char *path = open_path(ftwbuf, &fd); - if (!path) { - goto out; - } - - cap_t caps = cap_get_file(path); - if (!caps) { - goto out_close; - } - - // TODO: Any better way to check for a non-empty capability set? - char *text = cap_to_text(caps, NULL); - if (!text) { - goto out_free_caps; - } - ret = text[0]; - - cap_free(text); -out_free_caps: - cap_free(caps); -out_close: - close_path(ftwbuf, path, fd); -out: - return ret; -} - -#else // !BFS_HAS_POSIX1E_CAPABILITIES - -bool bfs_check_capabilities(const struct BFTW *ftwbuf) { - return false; -} - -#endif diff --git a/util.h b/util.h index 4a9947c..2031c02 100644 --- a/util.h +++ b/util.h @@ -205,16 +205,4 @@ int bfs_major(dev_t dev); */ int bfs_minor(dev_t dev); -struct BFTW; - -/** - * Check if a file has a non-trvial Access Control List. - */ -bool bfs_check_acl(const struct BFTW *ftwbuf); - -/** - * Check if a file has a non-trvial capability set. - */ -bool bfs_check_capabilities(const struct BFTW *ftwbuf); - #endif // BFS_UTIL_H -- cgit v1.2.3