From c7dd070d9ba193dc7e4d5aedfdf449025129a934 Mon Sep 17 00:00:00 2001 From: Etienne AUJAMES Date: Tue, 15 Mar 2022 13:15:26 +0100 Subject: [PATCH] fslist: search by selinux labels This patch add a filtering mechanism with callbacks: several filters can be used (with logical "and" between each filters). This patch enable to retrieve and filter files with "security.selinux" xattr. e.g: $ lester -A fslist -a show_ctx -a format=lustre -a newer=/bin/chmod \ -a context=".*nfs_t.*" -agenhit=/tmp/hit -o /dev/null $ cat /tmp/hit 1647343596|1647343604|1647343596|0|0|100644|0|8908546|0:e2|\ unconfined_u:object_r:nfs_t:s0|/ROOT/test Signed-off-by: Etienne AUJAMES --- action-fslist.c | 236 ++++++++++++++++++++++++++++++++++++++++-------- lustre_lov.h | 3 + 2 files changed, 201 insertions(+), 38 deletions(-) diff --git a/action-fslist.c b/action-fslist.c index b187263..00a21f9 100644 --- a/action-fslist.c +++ b/action-fslist.c @@ -12,6 +12,7 @@ #include #include #include +#include #include enum { @@ -22,18 +23,122 @@ enum { FORMAT_NE2SCAN, }; +enum filter_type { + FIL_NONE = 0x0, + FIL_BEFORE = 0x1, + FIL_NEWER = 0x2, + FIL_CONTEXT = 0x4, + FIL_ALL = 0x7, +}; + +struct filter_elt; + +typedef int (*filter_cb_t)(struct filter_elt *elt, + struct ext2_inode *i, + struct ea_info *ctx); + +struct filter_elt { + filter_cb_t fe_cb; + int fe_not; + union { + time_t time; + regex_t regex; + char *str; + }fe_d; +}; + +#define MAX_FILTER_NUMBER 10 +struct filter_array { + int fa_msk; + int fa_cnt; + struct filter_elt fa_array[MAX_FILTER_NUMBER]; +}; + static int fslist_format = FORMAT_NE2SCAN; static const char *fsname = "UNKNOWN"; static const char *user_note = ""; static int show_dirs = 0; static int show_fid = 0; +static int show_ctx = 0; static int show_one = 0; static const char *target_dev; -static time_t cutoff_time; -static int accessed_before; -static int newer_than; +static struct filter_array filters = { 0 }; static FILE *genhit; +static int add_to_filters(enum filter_type type, filter_cb_t cb, int not, + void *data, size_t len) +{ + struct filter_elt *elt; + + if (filters.fa_cnt >= MAX_FILTER_NUMBER || len > sizeof(elt->fe_d) || + type > FIL_ALL) + return -1; + + elt = &filters.fa_array[filters.fa_cnt]; + elt->fe_cb = cb; + elt->fe_not = not; + memcpy(&elt->fe_d, data, len); + + filters.fa_cnt++; + filters.fa_msk |= type; + + return 0; +} + +static int is_filters_match( struct ext2_inode *inode, struct ea_info *ctx) +{ + int i; + int match = 0; + struct filter_elt *elt = &filters.fa_array[0]; + + for (i = 0; i < filters.fa_cnt; i++, elt++) { + match = elt->fe_cb(elt, inode, ctx); + + if (elt->fe_not) + match = !match; + if (!match) + break; + } + + return match; +} + +static int filter_accessed_before(struct filter_elt *elt, struct ext2_inode *i, + struct ea_info *ctx) +{ + time_t cutoff_time = elt->fe_d.time; + + if (i->i_atime < cutoff_time && + i->i_mtime < cutoff_time && + i->i_ctime < cutoff_time) + return 1; + + return 0; +} + +static int filter_newer_than(struct filter_elt *elt, struct ext2_inode *i, + struct ea_info *ctx) +{ + time_t cutoff_time = elt->fe_d.time; + + if (i->i_mtime >= cutoff_time && + i->i_ctime >= cutoff_time) + return 1; + + return 0; +} + +static int filter_context(struct filter_elt *elt, struct ext2_inode *i, + struct ea_info *ctx) +{ + regex_t *reg = &elt->fe_d.regex; + + if (!ctx) + return 0; + + return regexec(reg, ctx->value, 0, NULL, 0) == 0; +} + static void report_fid(FILE *f, struct ea_info *lov) { struct lustre_mdt_attrs *lma = lov->value; @@ -41,6 +146,13 @@ static void report_fid(FILE *f, struct ea_info *lov) lma->lma_self_fid.f_oid, lma->lma_self_fid.f_ver); } +static void report_ctx(FILE *f, struct ea_info *ctx) +{ + char *ctx_str = ctx->value; + + fprintf(f, "%s", ctx_str); +} + static void report_osts(FILE *f, struct ea_info *lov) { struct lov_mds_md_v1 *lov1; @@ -111,10 +223,13 @@ static void fslist_help(void) fprintf(stderr, " show_dirs\t\tAlso show directory names\n"); fprintf(stderr, " show_fid\t\tAlso show FID in lov, ne2scan " "format\n"); + fprintf(stderr, " show_ctx\t\tAlso show selinux context, ne2scan " + "format\n"); fprintf(stderr, " note=MSG\t\tAdd MSG to ne2scan header\n"); fprintf(stderr, " fs=NAME\t\tName filesystem for ne2scan output\n"); fprintf(stderr, " newer=FILE\t\tFiles newer than FILE\n"); fprintf(stderr, " before=FILE\t\tFiles not accessed since FILE\n"); + fprintf(stderr, " context=REGEX\t\tFiles match context regex\n"); fprintf(stderr, " genhit=FILE\t\tCopy entries matching newer/before " "options to FILE\n"); fprintf(stderr, "\t\t\t (Main output will get all files, matching " @@ -124,13 +239,23 @@ static void fslist_help(void) static int fslist_init(const char *dev, int argc, const char **argv) { const char *genhit_name = NULL; + int rc; target_dev = dev; while (argc--) { + int not = 0; + + if (!bcmp(*argv, "not_", 4)) { + not = 1; + *argv += 4; + } + if (!strcmp(*argv, "show_dirs")) show_dirs = 1; else if (!strcmp(*argv, "show_fid")) show_fid = 1; + else if (!strcmp(*argv, "show_ctx")) + show_ctx = 1; else if (!strcmp(*argv, "show_one")) show_one = 1; else if (!strncmp(*argv, "fs=", 3)) @@ -138,23 +263,48 @@ static int fslist_init(const char *dev, int argc, const char **argv) else if (!strncmp(*argv, "note=", 5)) user_note = *argv + 5; else if (!strncmp(*argv, "newer=", 6)) { - if (newer_than || accessed_before) { - fprintf(stderr, "Only one newer= or before= " - "option allowed\n"); + time_t f_time; + + if (get_timestamp(*argv + 6, NULL, &f_time)) return 1; - } - if (get_timestamp(*argv + 6, NULL, &cutoff_time)) + + rc = add_to_filters(FIL_NEWER, filter_newer_than, not, + &f_time, sizeof(f_time)); + if (rc) { + fprintf(stderr, "Fail to add filter '%s'\n", + *argv); return 1; - newer_than = 1; + } + } else if (!strncmp(*argv, "before=", 7)) { - if (newer_than || accessed_before) { - fprintf(stderr, "Only one newer= or before= " - "option allowed\n"); + time_t f_time; + + if (get_timestamp(*argv + 7, NULL, &f_time)) + return 1; + + rc = add_to_filters(FIL_BEFORE, filter_accessed_before, + not, &f_time, sizeof(f_time)); + if (rc) { + fprintf(stderr, "Fail to add filter '%s'\n", + *argv); + return 1; + } + } else if (!strncmp(*argv, "context=", 8)) { + const char *context_str = *argv + 8; + regex_t reg; + + if (regcomp(®, context_str, REG_EXTENDED)) { + fprintf(stderr, "Could not compile context regex\n"); return 1; } - if (get_timestamp(*argv + 7, &cutoff_time, NULL)) + + rc = add_to_filters(FIL_CONTEXT, filter_context, not, + ®, sizeof(reg)); + if (rc) { + fprintf(stderr, "Fail to add filter '%s'\n", + *argv); return 1; - accessed_before = 1; + } } else if (!strncmp(*argv, "format=", 7)) { if (!strcmp(*argv, "format=ne2scan")) fslist_format = FORMAT_NE2SCAN; @@ -182,9 +332,9 @@ static int fslist_init(const char *dev, int argc, const char **argv) } if (genhit_name) { - if (!(newer_than || accessed_before)) { - fprintf(stderr, "genhit only makes sense with newer= " - "or before=\n"); + if (!(filters.fa_msk & FIL_ALL)) { + fprintf(stderr, "genhit only makes sense with a filter" + "option\n"); return 1; } @@ -215,12 +365,7 @@ static int fslist_iscan(ext2_ino_t ino, struct ext2_inode *inode, * Note, if we're sending data to a separate genhit file, then * we actually want everything for the main file. */ - if (!genhit && accessed_before && (inode->i_atime >= cutoff_time || - inode->i_mtime >= cutoff_time || - inode->i_ctime >= cutoff_time)) - return ACTION_COMPLETE; - if (!genhit && newer_than && inode->i_ctime < cutoff_time && - inode->i_mtime < cutoff_time) + if (!genhit && is_filters_match(inode, NULL)) return ACTION_COMPLETE; return ACTION_WANT_PATH | ACTION_WANT_INODE; @@ -255,7 +400,8 @@ static int fslist_dscan_begin(void) static int fslist_output(FILE *f, ext2_ino_t ino, struct ext2_inode *inode, int offset, const char *name, int namelen, - struct ea_info *lov, struct ea_info *lma) + struct ea_info *lov, struct ea_info *lma, + struct ea_info *ctx) { if (fslist_format > FORMAT_INUM) { fprintf(f, "%u|%u|%u|%u|%u|%o|%lu|%u", inode->i_atime, @@ -276,6 +422,12 @@ static int fslist_output(FILE *f, ext2_ino_t ino, struct ext2_inode *inode, report_fid(f, lma); } + if (show_ctx) { + fprintf(f, "|"); + if (ctx) + report_ctx(f, ctx); + } + fprintf(f, "|"); } else if (fslist_format == FORMAT_INUM) { fprintf(f, "%lu ", ino); @@ -288,8 +440,10 @@ static int fslist_dscan(ext2_ino_t ino, struct ext2_inode *inode, struct dentry *parent, const char *name, int namelen, struct ea_info *eas) { + int need_ctx = show_ctx || (filters.fa_msk | FIL_CONTEXT); struct ea_info *lov = NULL; struct ea_info *lma = NULL; + struct ea_info *ctx = NULL; struct ea_info *ea; int requested = 0; int offset; @@ -297,8 +451,23 @@ static int fslist_dscan(ext2_ino_t ino, struct ext2_inode *inode, if (!inode) return ACTION_WANT_INODE | ACTION_WANT_ATTRS; - if (fslist_format >= FORMAT_LUSTRE) { + if (fslist_format >= FORMAT_LUSTRE || need_ctx) { for (ea = eas; ea->name; ea++) { + if (need_ctx && + ea->index == EXT2_XATTR_INDEX_SECURITY && + ea->name_len == 7 && + !strncmp(ea->name, "selinux", 7)) { + char *ctx_str = ea->value; + + ctx = ea; + if (!ctx_str) { + ea->requested = 1; + requested++; + } else { + ctx_str[ctx->value_len - 1] = '\0'; + } + } + if (ea->index != EXT2_XATTR_INDEX_TRUSTED && ea->index != EXT2_XATTR_INDEX_LUSTRE) continue; @@ -327,20 +496,11 @@ static int fslist_dscan(ext2_ino_t ino, struct ext2_inode *inode, return ACTION_WANT_READ_ATTRS; offset = build_path(parent, 0); - fslist_output(outfile, ino, inode, offset, name, namelen, lov, lma); + fslist_output(outfile, ino, inode, offset, name, namelen, lov, lma, ctx); - if (genhit) { - if (accessed_before && (inode->i_atime < cutoff_time && - inode->i_mtime < cutoff_time && - inode->i_ctime < cutoff_time)) { - fslist_output(genhit, ino, inode, offset, name, - namelen, lov, lma); - } else if (newer_than && (inode->i_ctime >= cutoff_time || - inode->i_mtime >= cutoff_time)) { - fslist_output(genhit, ino, inode, offset, name, - namelen, lov, lma); - } - } + if (genhit && is_filters_match(inode, ctx)) + fslist_output(genhit, ino, inode, offset, name, + namelen, lov, lma, ctx); if (show_one) return ACTION_COMPLETE | ACTION_IGNORE_FILE; diff --git a/lustre_lov.h b/lustre_lov.h index 3f75d21..7fc4a57 100644 --- a/lustre_lov.h +++ b/lustre_lov.h @@ -22,6 +22,9 @@ #ifndef EXT2_XATTR_INDEX_LUSTRE #define EXT2_XATTR_INDEX_LUSTRE 5 #endif +#ifndef EXT2_XATTR_INDEX_SECURITY +#define EXT2_XATTR_INDEX_SECURITY 6 +#endif /* From lustre_idl.h */ #define LOV_MAGIC_V1 0x0BD10BD0