From a4313fe967f7fffd3d3aabb54392ea4d902c44b2 Mon Sep 17 00:00:00 2001
From: Morgan Weetman
Date: Fri, 8 Jan 2016 08:50:03 +1100
Subject: [PATCH] find: added xattr and cap predicates
* find/defs.h (typedef PREDICATEFUNCTION): added pred_xattr and pred_cap
* find/parser.c:
(parse_xattr): added function
(parse_ixattr): added function
(parse_cap): added function
(parse_icap): added function
(insert_cap): added function
(insert_xattr): added function
(parse_table[]): added entries
* find/pred.c
(pred_cap): added function
(pred_xattr): added function
(pred_table[]): added entries for pred_cap and pred_xattr
* find/tree.c (costlookup[]): added entries for pred_cap and pred_xattr
* find/find.1:
added -xattr predicate information
added -ixattr predicate information
added -cap predicate information
added -icap predicate information
* find/Makefile.am: include libcap for -cap
* find/testsuite/excuses.txt: -xattr and -cap require root privileges
to test, some planning needed
---
find/Makefile.am | 3 +-
find/defs.h | 2 +
find/find.1 | 25 +++++++++++
find/parser.c | 102 +++++++++++++++++++++++++++++++++++++++++++++
find/pred.c | 83 ++++++++++++++++++++++++++++++++++++
find/testsuite/excuses.txt | 2 +
find/tree.c | 2 +
7 files changed, 218 insertions(+), 1 deletion(-)
diff --git a/find/Makefile.am b/find/Makefile.am
index 3207fc5..af75f8e 100644
--- a/find/Makefile.am
+++ b/find/Makefile.am
@@ -1,6 +1,7 @@
AUTOMAKE_OPTIONS = std-options
AM_CFLAGS = $(WARN_CFLAGS)
localedir = $(datadir)/locale
+LIBCAP = -lcap
noinst_LIBRARIES = libfindtools.a
libfindtools_a_SOURCES = finddata.c fstype.c parser.c pred.c exec.c tree.c util.c sharefile.c print.c
@@ -16,7 +17,7 @@ man_MANS = find.1
EXTRA_DIST = defs.h sharefile.h print.h $(man_MANS)
AM_CPPFLAGS = -I../gl/lib -I$(top_srcdir)/lib -I$(top_srcdir)/gl/lib -DLOCALEDIR=\"$(localedir)\"
-LDADD = ./libfindtools.a ../lib/libfind.a ../gl/lib/libgnulib.a $(LIBINTL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX) $(LIB_CLOSE) $(MODF_LIBM) $(FINDLIBS) $(GETHOSTNAME_LIB) $(LIB_EACCESS)
+LDADD = ./libfindtools.a ../lib/libfind.a ../gl/lib/libgnulib.a $(LIBCAP) $(LIBINTL) $(LIB_CLOCK_GETTIME) $(LIB_EACCESS) $(LIB_SELINUX) $(LIB_CLOSE) $(MODF_LIBM) $(FINDLIBS) $(GETHOSTNAME_LIB) $(LIB_EACCESS)
# gnulib advises we link against because we use :
# $(GETHOSTNAME_LIB) uname
# $(LIB_CLOCK_GETTIME) (some indirect dependency)
diff --git a/find/defs.h b/find/defs.h
index 71184a5..f57352c 100644
--- a/find/defs.h
+++ b/find/defs.h
@@ -398,6 +398,7 @@ PREDICATEFUNCTION pred_and;
PREDICATEFUNCTION pred_anewer;
PREDICATEFUNCTION pred_atime;
PREDICATEFUNCTION pred_closeparen;
+PREDICATEFUNCTION pred_cap;
PREDICATEFUNCTION pred_cmin;
PREDICATEFUNCTION pred_cnewer;
PREDICATEFUNCTION pred_comma;
@@ -449,6 +450,7 @@ PREDICATEFUNCTION pred_uid;
PREDICATEFUNCTION pred_used;
PREDICATEFUNCTION pred_user;
PREDICATEFUNCTION pred_writable;
+PREDICATEFUNCTION pred_xattr;
PREDICATEFUNCTION pred_xtype;
PREDICATEFUNCTION pred_context;
diff --git a/find/find.1 b/find/find.1
index 7827aca..c910efb 100644
--- a/find/find.1
+++ b/find/find.1
@@ -580,6 +580,13 @@ a file has to have been accessed at least
.I two
days ago.
+.IP "\-cap \fIpattern\fR"
+Match file capabilities against the regular expression
+\fIpattern\fR. For example to search for files that have
+CAP_SETUID you could use '.*setuid.*'. This option
+also takes advantage of
+.B \-regextype
+
.IP "\-cmin \fIn\fR"
File's status was last changed \fIn\fR minutes ago.
@@ -636,6 +643,11 @@ File's numeric group ID is \fIn\fR.
.IP "\-group \fIgname\fR"
File belongs to group \fIgname\fR (numeric group ID allowed).
+.IP "\-icap \fIpattern\fR"
+Like
+.BR \-cap ,
+but the match is case insensitive.
+
.IP "\-ilname \fIpattern\fR"
Like
.BR \-lname ,
@@ -675,6 +687,11 @@ but the match is case insensitive.
See \-ipath. This alternative is less portable than
.BR \-ipath .
+.IP "\-ixattr \fIpattern\fR"
+Like
+.BR \-xattr ,
+but the match is case insensitive.
+
.IP "\-links \fIn\fR"
File has \fIn\fR links.
@@ -975,6 +992,14 @@ mapping (or root-squashing), since many systems implement
in the client's kernel and so cannot make use of the UID mapping
information held on the server.
+.IP "\-xattr \fIpattern\fR"
+Match extended attributes against the regular expression
+\fIpattern\fR. For example to search for files that have
+capabilities set you could use '.*capa.*'. This option
+also takes advantage of
+.B \-regextype
+
+
.IP "\-xtype \fIc\fR"
The same as
.B \-type
diff --git a/find/parser.c b/find/parser.c
index 4c1a2ed..2fe17d2 100644
--- a/find/parser.c
+++ b/find/parser.c
@@ -87,6 +87,7 @@ static bool parse_accesscheck (const struct parser_table*, char *argv[], int *
static bool parse_amin (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_and (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_anewer (const struct parser_table*, char *argv[], int *arg_ptr);
+static bool parse_cap (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_cmin (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_cnewer (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_comma (const struct parser_table*, char *argv[], int *arg_ptr);
@@ -106,11 +107,13 @@ static bool parse_fprint0 (const struct parser_table*, char *argv[], int *
static bool parse_fstype (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_gid (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_group (const struct parser_table*, char *argv[], int *arg_ptr);
+static bool parse_icap (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_ilname (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_iname (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_inum (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_ipath (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_iregex (const struct parser_table*, char *argv[], int *arg_ptr);
+static bool parse_ixattr (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_iwholename (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_links (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_lname (const struct parser_table*, char *argv[], int *arg_ptr);
@@ -149,6 +152,7 @@ static bool parse_xdev (const struct parser_table*, char *argv[], int *
static bool parse_ignore_race (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_noignore_race (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_warn (const struct parser_table*, char *argv[], int *arg_ptr);
+static bool parse_xattr (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_xtype (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_quit (const struct parser_table*, char *argv[], int *arg_ptr);
static bool parse_context (const struct parser_table*, char *argv[], int *arg_ptr);
@@ -168,6 +172,12 @@ static bool insert_type (char **argv, int *arg_ptr,
static bool insert_regex (char *argv[], int *arg_ptr,
const struct parser_table *entry,
int regex_options);
+static bool insert_xattr (char *argv[], int *arg_ptr,
+ const struct parser_table *entry,
+ int regex_options);
+static bool insert_cap (char *argv[], int *arg_ptr,
+ const struct parser_table *entry,
+ int regex_options);
static bool insert_exec_ok (const char *action,
const struct parser_table *entry,
char *argv[],
@@ -237,6 +247,7 @@ static struct parser_table const parse_table[] =
PARSE_PUNCTUATION("and", and), /* GNU */
PARSE_TEST ("anewer", anewer), /* GNU */
{ARG_TEST, "atime", parse_time, pred_atime}, /* POSIX */
+ PARSE_TEST ("cap", cap), /* GNU */
PARSE_TEST ("cmin", cmin), /* GNU */
PARSE_TEST ("cnewer", cnewer), /* GNU */
{ARG_TEST, "ctime", parse_time, pred_ctime}, /* POSIX */
@@ -257,12 +268,14 @@ static struct parser_table const parse_table[] =
PARSE_TEST ("fstype", fstype), /* GNU, Unix */
PARSE_TEST ("gid", gid), /* GNU */
PARSE_TEST ("group", group), /* POSIX */
+ PARSE_TEST_NP ("icap", icap), /* GNU */
PARSE_OPTION ("ignore_readdir_race", ignore_race), /* GNU */
PARSE_TEST ("ilname", ilname), /* GNU */
PARSE_TEST ("iname", iname), /* GNU */
PARSE_TEST ("inum", inum), /* GNU, Unix */
PARSE_TEST ("ipath", ipath), /* GNU, deprecated in favour of iwholename */
PARSE_TEST_NP ("iregex", iregex), /* GNU */
+ PARSE_TEST_NP ("ixattr", ixattr), /* GNU */
PARSE_TEST_NP ("iwholename", iwholename), /* GNU */
PARSE_TEST ("links", links), /* POSIX */
PARSE_TEST ("lname", lname), /* GNU */
@@ -310,6 +323,7 @@ static struct parser_table const parse_table[] =
PARSE_TEST_NP ("wholename", wholename), /* GNU, replaced -path, but anyway -path will soon be in POSIX */
{ARG_TEST, "writable", parse_accesscheck, pred_writable}, /* GNU, 4.3.0+ */
PARSE_OPTION ("xdev", xdev), /* POSIX */
+ PARSE_TEST ("xattr", xattr), /* GNU */
PARSE_TEST ("xtype", xtype), /* GNU */
#ifdef UNIMPLEMENTED_UNIX
/* It's pretty ugly for find to know about archive formats.
@@ -804,6 +818,12 @@ parse_anewer (const struct parser_table* entry, char **argv, int *arg_ptr)
return false;
}
+static bool
+parse_cap (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+ return insert_cap (argv, arg_ptr, entry, options.regex_options);
+}
+
bool
parse_closeparen (const struct parser_table* entry, char **argv, int *arg_ptr)
{
@@ -1284,6 +1304,12 @@ estimate_pattern_match_rate (const char *pattern, int is_regex)
}
static bool
+parse_icap (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+ return insert_cap (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
+}
+
+static bool
parse_ilname (const struct parser_table* entry, char **argv, int *arg_ptr)
{
const char *name;
@@ -1396,6 +1422,12 @@ parse_iregex (const struct parser_table* entry, char **argv, int *arg_ptr)
}
static bool
+parse_ixattr (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+ return insert_xattr (argv, arg_ptr, entry, RE_ICASE|options.regex_options);
+}
+
+static bool
parse_links (const struct parser_table* entry, char **argv, int *arg_ptr)
{
struct predicate *p = insert_num (argv, arg_ptr, entry);
@@ -2701,6 +2733,44 @@ parse_warn (const struct parser_table* entry, char **argv, int *arg_ptr)
}
static bool
+parse_xattr (const struct parser_table* entry, char **argv, int *arg_ptr)
+{
+ return insert_xattr (argv, arg_ptr, entry, options.regex_options);
+}
+
+static bool
+insert_xattr (char **argv,
+ int *arg_ptr,
+ const struct parser_table *entry,
+ int regex_options)
+{
+ const char *rx;
+ if (collect_arg (argv, arg_ptr, &rx))
+ {
+ struct re_pattern_buffer *re;
+ const char *error_message;
+ struct predicate *our_pred = insert_primary_withpred (entry, pred_xattr, rx);
+ our_pred->need_stat = our_pred->need_type = false;
+ re = xmalloc (sizeof (struct re_pattern_buffer));
+ our_pred->args.regex = re;
+ re->allocated = 100;
+ re->buffer = xmalloc (re->allocated);
+ re->fastmap = NULL;
+
+ re_set_syntax (regex_options);
+ re->syntax = regex_options;
+ re->translate = NULL;
+
+ error_message = re_compile_pattern (rx, strlen (rx), re);
+ if (error_message)
+ error (EXIT_FAILURE, 0, "%s", error_message);
+ our_pred->est_success_rate = estimate_pattern_match_rate (rx, 1);
+ return true;
+ }
+ return false;
+}
+
+static bool
parse_xtype (const struct parser_table* entry, char **argv, int *arg_ptr)
{
return insert_type (argv, arg_ptr, entry, pred_xtype);
@@ -2839,6 +2909,38 @@ insert_type (char **argv, int *arg_ptr,
return false;
}
+static bool
+insert_cap (char **argv,
+ int *arg_ptr,
+ const struct parser_table *entry,
+ int regex_options)
+{
+ const char *rx;
+ if (collect_arg (argv, arg_ptr, &rx))
+ {
+ struct re_pattern_buffer *re;
+ const char *error_message;
+ struct predicate *our_pred = insert_primary_withpred (entry, pred_cap, rx);
+ our_pred->need_stat = true;
+ our_pred->need_type = false;
+ re = xmalloc (sizeof (struct re_pattern_buffer));
+ our_pred->args.regex = re;
+ re->allocated = 100;
+ re->buffer = xmalloc (re->allocated);
+ re->fastmap = NULL;
+
+ re_set_syntax (regex_options);
+ re->syntax = regex_options;
+ re->translate = NULL;
+
+ error_message = re_compile_pattern (rx, strlen (rx), re);
+ if (error_message)
+ error (EXIT_FAILURE, 0, "%s", error_message);
+ our_pred->est_success_rate = estimate_pattern_match_rate (rx, 1);
+ return true;
+ }
+ return false;
+}
/* Return true if the file accessed via FP is a terminal.
*/
diff --git a/find/pred.c b/find/pred.c
index d633ab9..fc9ac7a 100644
--- a/find/pred.c
+++ b/find/pred.c
@@ -33,8 +33,10 @@
#include
#include
#include
+#include
#include
#include /* for unlinkat() */
+#include /* for pred_cap() */
/* gnulib headers. */
#include "areadlink.h"
@@ -82,6 +84,7 @@ struct pred_assoc pred_table[] =
{pred_and, "and "},
{pred_anewer, "anewer "},
{pred_atime, "atime "},
+ {pred_cap, "cap "},
{pred_closeparen, ") "},
{pred_cmin, "cmin "},
{pred_cnewer, "cnewer "},
@@ -134,6 +137,7 @@ struct pred_assoc pred_table[] =
{pred_used, "used "},
{pred_user, "user "},
{pred_writable, "writable "},
+ {pred_xattr, "xattr "},
{pred_xtype, "xtype "},
{pred_context, "context"},
{0, "none "}
@@ -251,6 +255,32 @@ pred_atime (const char *pathname, struct stat *stat_buf, struct predicate *pred_
}
bool
+pred_cap (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
+{
+ (void) pathname;
+ char *cap_str;
+ cap_t caps;
+ bool ret = false;
+
+ // get capabilities
+ caps = cap_get_file(pathname);
+ cap_str = cap_to_text(caps, NULL);
+ if ( cap_str == NULL ) {
+ cap_free(caps);
+ return(ret);
+ }
+
+ // perform regex match
+ if (regexec(pred_ptr->args.regex, cap_str, (size_t) 0, NULL, 0) == 0)
+ ret = true;
+
+ // clean up
+ cap_free(caps);
+ cap_free(cap_str);
+ return(ret);
+}
+
+bool
pred_closeparen (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
(void) &pathname;
@@ -1132,6 +1162,59 @@ pred_user (const char *pathname, struct stat *stat_buf, struct predicate *pred_p
}
bool
+pred_xattr (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
+{
+ (void) pathname;
+ char empty[0];
+ char *list;
+ char *substrings;
+ ssize_t list_size;
+ int i, j;
+ bool ret = false;
+
+ // get size of xattr list for the given path by passing an empty list
+ if (options.symlink_handling == SYMLINK_NEVER_DEREF) {
+ list_size = llistxattr(pathname, empty, 0);
+ } else {
+ list_size = listxattr(pathname, empty, 0);
+ }
+
+ // allocate just enough memory to hold all xattrs
+ list = malloc(list_size);
+
+ // used to hold individual attributes (substrings)
+ // allocate same size as list just in case there's only one xattr
+ substrings = malloc(list_size);
+
+ // retrieve the list of xattrs
+ if (options.symlink_handling == SYMLINK_NEVER_DEREF) {
+ llistxattr(pathname, list, list_size);
+ } else {
+ listxattr(pathname, list, list_size);
+ }
+
+ // break list into asciiz strings
+ for (i = 0, j = 0; i < list_size; i++) {
+ substrings[j] = list[i];
+ if (list[i] == 0) {
+ // perform regex match against substring
+ j = 0;
+ if (regexec(pred_ptr->args.regex, substrings, (size_t) 0, NULL, 0) == 0) {
+ ret = true;
+ }
+ continue;
+ }
+ j++;
+ }
+
+ // clean up
+ free(list);
+ free(substrings);
+ return(ret);
+
+}
+
+bool
pred_xtype (const char *pathname, struct stat *stat_buf, struct predicate *pred_ptr)
{
struct stat sbuf; /* local copy, not stat_buf because we're using a different stat method */
diff --git a/find/testsuite/excuses.txt b/find/testsuite/excuses.txt
index ac4515a..d4aee6d 100644
--- a/find/testsuite/excuses.txt
+++ b/find/testsuite/excuses.txt
@@ -22,6 +22,7 @@
## Things that are hard to test because they rely on features of
## the environment
+"cap", cap), /* GNU */
"gid", gid), /* GNU */
"uid", uid), /* GNU */
"user", user),
@@ -34,6 +35,7 @@
"ignore_readdir_race", ignore_race), /* GNU */
"noignore_readdir_race", noignore_race), /* GNU */
"noleaf", noleaf), /* GNU */
+"xattr", xattr), /* GNU */
## Things that might be testable but which I have not yet thought
## about enough.
diff --git a/find/tree.c b/find/tree.c
index e471279..4cef0df 100644
--- a/find/tree.c
+++ b/find/tree.c
@@ -941,6 +941,7 @@ static struct pred_cost_lookup costlookup[] =
{ pred_and , NeedsNothing, },
{ pred_anewer , NeedsStatInfo, },
{ pred_atime , NeedsStatInfo, },
+ { pred_cap , NeedsNothing, },
{ pred_closeparen, NeedsNothing },
{ pred_cmin , NeedsStatInfo, },
{ pred_cnewer , NeedsStatInfo, },
@@ -995,6 +996,7 @@ static struct pred_cost_lookup costlookup[] =
{ pred_used , NeedsStatInfo },
{ pred_user , NeedsStatInfo },
{ pred_writable , NeedsAccessInfo },
+ { pred_xattr , NeedsNothing },
{ pred_xtype , NeedsType } /* roughly correct unless most files are symlinks */
};
static int pred_table_sorted = 0;
--
2.5.0