From 18fc03882d49ff46b41a53afc0d6232e1dfbbb35 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 17 Dec 2018 22:05:49 -0500 Subject: color: Support coloring files with capabilities --- Makefile | 2 +- color.c | 2 ++ util.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 23 +++++++++++++--- 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 460337a..f304ab1 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ LOCAL_LDLIBS := ifeq ($(OS),Linux) LOCAL_LDFLAGS += -Wl,--as-needed -LOCAL_LDLIBS += -lrt +LOCAL_LDLIBS += -lcap -lrt endif ALL_CPPFLAGS = $(LOCAL_CPPFLAGS) $(CPPFLAGS) diff --git a/color.c b/color.c index cdc79a6..0dc9f3d 100644 --- a/color.c +++ b/color.c @@ -327,6 +327,8 @@ static const char *file_color(const struct colors *colors, const char *filename, color = colors->setuid; } else if (sb->mode & S_ISGID) { color = colors->setgid; + } else if (bfs_check_capabilities(ftwbuf)) { + color = colors->capable; } else if (sb->mode & 0111) { color = colors->exec; } diff --git a/util.c b/util.c index b708527..0ec6dba 100644 --- a/util.c +++ b/util.c @@ -30,6 +30,10 @@ #include #include +#if BFS_HAS_POSIX1E_CAPABILITIES +# include +#endif + #if BFS_HAS_SYS_PARAM # include #endif @@ -377,3 +381,92 @@ int bfs_minor(dev_t dev) { return dev & 0xFF; #endif } + +#if 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); + } +} + +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 e95b67d..7506924 100644 --- a/util.h +++ b/util.h @@ -33,10 +33,18 @@ # define BFS_HAS_INCLUDE(header, fallback) fallback #endif -#define BFS_HAS_MNTENT BFS_HAS_INCLUDE(, __GLIBC__) -#define BFS_HAS_SYS_MKDEV BFS_HAS_INCLUDE(, false) -#define BFS_HAS_SYS_PARAM BFS_HAS_INCLUDE(, true) -#define BFS_HAS_SYS_SYSMACROS BFS_HAS_INCLUDE(, __GLIBC__) +#define BFS_HAS_MNTENT BFS_HAS_INCLUDE(, __GLIBC__) +#define BFS_HAS_SYS_CAPABILITY BFS_HAS_INCLUDE(, __linux__) +#define BFS_HAS_SYS_MKDEV BFS_HAS_INCLUDE(, false) +#define BFS_HAS_SYS_PARAM BFS_HAS_INCLUDE(, true) +#define BFS_HAS_SYS_SYSMACROS BFS_HAS_INCLUDE(, __GLIBC__) + +#if BFS_HAS_SYS_CAPABILITY +# include +# ifdef CAP_CHOWN +# define BFS_HAS_POSIX1E_CAPABILITIES true +# endif +#endif #if !defined(FNM_CASEFOLD) && defined(FNM_IGNORECASE) # define FNM_CASEFOLD FNM_IGNORECASE @@ -187,4 +195,11 @@ int bfs_major(dev_t dev); */ int bfs_minor(dev_t dev); +struct BFTW; + +/** + * 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