summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2018-12-17 22:05:49 -0500
committerTavian Barnes <tavianator@tavianator.com>2018-12-17 22:05:49 -0500
commit18fc03882d49ff46b41a53afc0d6232e1dfbbb35 (patch)
treedec8b26d1801c4d170cfb148c3758e95dfd4e1c9
parent50b3caa2625b4377b03fe8ca6967f769cdd6ee95 (diff)
downloadbfs-18fc03882d49ff46b41a53afc0d6232e1dfbbb35.tar.xz
color: Support coloring files with capabilities
-rw-r--r--Makefile2
-rw-r--r--color.c2
-rw-r--r--util.c93
-rw-r--r--util.h23
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 <sys/types.h>
#include <unistd.h>
+#if BFS_HAS_POSIX1E_CAPABILITIES
+# include <sys/capability.h>
+#endif
+
#if BFS_HAS_SYS_PARAM
# include <sys/param.h>
#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/<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(<mntent.h>, __GLIBC__)
-#define BFS_HAS_SYS_MKDEV BFS_HAS_INCLUDE(<sys/mkdev.h>, false)
-#define BFS_HAS_SYS_PARAM BFS_HAS_INCLUDE(<sys/param.h>, true)
-#define BFS_HAS_SYS_SYSMACROS BFS_HAS_INCLUDE(<sys/sysmacros.h>, __GLIBC__)
+#define BFS_HAS_MNTENT BFS_HAS_INCLUDE(<mntent.h>, __GLIBC__)
+#define BFS_HAS_SYS_CAPABILITY BFS_HAS_INCLUDE(<sys/capability.h>, __linux__)
+#define BFS_HAS_SYS_MKDEV BFS_HAS_INCLUDE(<sys/mkdev.h>, false)
+#define BFS_HAS_SYS_PARAM BFS_HAS_INCLUDE(<sys/param.h>, true)
+#define BFS_HAS_SYS_SYSMACROS BFS_HAS_INCLUDE(<sys/sysmacros.h>, __GLIBC__)
+
+#if BFS_HAS_SYS_CAPABILITY
+# include <sys/capability.h>
+# 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