From 64e6dc0ae3e1347a4488d6c30597b4cee1f0de80 Mon Sep 17 00:00:00 2001 From: jbj Date: Sun, 21 Dec 2003 17:17:57 +0000 Subject: [PATCH] Parse selinux file_contexts patterns. CVS patchset: 6987 CVS date: 2003/12/21 17:17:57 --- lib/Makefile.am | 12 +-- lib/rpmsx.c | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/tre.c | 131 +++++++++++++++++++++++++ 3 files changed, 427 insertions(+), 11 deletions(-) create mode 100644 lib/tre.c diff --git a/lib/Makefile.am b/lib/Makefile.am index 820beb0..34a552b 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -11,6 +11,7 @@ INCLUDES = -I. \ -I$(top_srcdir)/rpmio \ @WITH_BEECRYPT_INCLUDE@ \ -I$(top_srcdir)/popt \ + -I/usr/include/selinux \ @INCPATH@ EXTRA_DIST = getdate.y @@ -40,7 +41,8 @@ librpm_la_SOURCES = \ librpm_la_LDFLAGS = -release 4.3 $(LDFLAGS) \ $(top_builddir)/rpmdb/librpmdb.la \ $(top_builddir)/rpmio/librpmio.la \ - $(top_builddir)/popt/libpopt.la + $(top_builddir)/popt/libpopt.la \ + -lselinux getdate.c: getdate.y @echo expect 10 shift/reduce conflicts @@ -106,11 +108,9 @@ tthread: tthread.o librpm.la tsystem: tsystem.o $(top_builddir)/popt/libpopt.la $(LINK) $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< $(top_builddir)/rpmio/librpmio.la $(top_builddir)/popt/libpopt.la -tre: tre.o - $(LINK) @LDFLAGS_STATIC@ $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< \ - -lselinux \ - $(top_builddir)/rpmio/librpmio.la \ - $(top_builddir)/popt/libpopt.la +tre: tre.o librpm.la + $(LINK) $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< \ + -lselinux librpm.la tcpu: tcpu.o librpm.la $(LINK) @LDFLAGS_STATIC@ $(CFLAGS) $(DEFS) $(INCLUDES) -o $@ $< $(mylibs) diff --git a/lib/rpmsx.c b/lib/rpmsx.c index 1fe2e93..3bb22e8 100644 --- a/lib/rpmsx.c +++ b/lib/rpmsx.c @@ -74,6 +74,291 @@ fprintf(stderr, "*** sx %p\t%s[%d]\n", sx, __func__, sx->Count); return NULL; } +static int rpmsxpCompare(const void* A, const void* B) +{ + rpmsxp sxpA = (rpmsxp) A; + rpmsxp sxpB = (rpmsxp) B; + return (sxpA->hasMetaChars - sxpB->hasMetaChars); +} + +/* Determine if the regular expression specification has any meta characters. */ +static void rpmsxpHasMetaChars(rpmsxp sxp) +{ + const char * s = sxp->pattern; + size_t ns = strlen(s); + const char * se = s + ns; + + sxp->hasMetaChars = 0; + + /* Look at each character in the RE specification string for a + * meta character. Return when any meta character reached. */ + while (s != se) { + switch(*s) { + case '.': + case '^': + case '$': + case '?': + case '*': + case '+': + case '|': + case '[': + case '(': + case '{': + sxp->hasMetaChars = 1; + return; + break; + case '\\': /* skip the next character */ + s++; + break; + default: + break; + + } + s++; + } + return; +} + +/** + * Check for duplicate specifications. If a duplicate specification is found + * and the context is the same, give a warning to the user. If a duplicate + * specification is found and the context is different, give a warning + * to the user (This could be changed to error). Return of non-zero is an error. + * + * @param sx security context patterns + * @return 0 on success + */ +static int rpmsxpCheckNoDupes(const rpmsx sx) +{ + int i, j; + int rc = 0; + + for (i = 0; i < sx->Count; i++) { + rpmsxp sxpi = sx->sxp + i; + for (j = i + 1; j < sx->Count; j++) { + rpmsxp sxpj = sx->sxp + j; + + /* Check if same RE string */ + if (strcmp(sxpj->pattern, sxpi->pattern)) + continue; + if (sxpj->mode && sxpi->mode && sxpj->mode != sxpi->mode) + continue; + + /* Same RE string found */ + if (strcmp(sxpj->context, sxpi->context)) { + /* If different contexts, give warning */ + fprintf(stderr, + "ERROR: Multiple different specifications for %s (%s and %s).\n", + sxpi->pattern, sxpj->context, sxpi->context); + rc = -1; + } else { + /* If same contexts give warning */ + fprintf(stderr, + "WARNING: Multiple same specifications for %s.\n", + sxpi->pattern); + } + } + } + return rc; +} + +static int nerr; +#define inc_err() nerr++ + +int rpmsxParse(rpmsx sx, const char *fn) +{ + FILE * fp; + char errbuf[255 + 1]; + char buf[255 + 1]; + char * bp; + char * regex; + char * type; + char * context; + char * anchored_regex; + int items; + int len; + int lineno; + int pass; + int regerr; + + if (fn == NULL) + fn = "/etc/security/selinux/src/policy/file_contexts/file_contexts"; + + if ((fp = fopen(fn, "r")) == NULL) { + perror(fn); + return -1; + } + + nerr = 0; + + /* + * Perform two passes over the specification file. + * The first pass counts the number of specifications and + * performs simple validation of the input. At the end + * of the first pass, the spec array is allocated. + * The second pass performs detailed validation of the input + * and fills in the spec array. + */ + for (pass = 0; pass < 2; pass++) { + rpmsxp sxp; + + lineno = 0; + sx->Count = 0; + sxp = sx->sxp; + while (fgets(buf, sizeof buf, fp)) { + lineno++; + len = strlen(buf); + if (buf[len - 1] != '\n') { + fprintf(stderr, + _("%s: no newline on line number %d (only read %s)\n"), + fn, lineno, buf); + inc_err(); + continue; + } + buf[len - 1] = 0; + bp = buf; + while (isspace(*bp)) + bp++; + /* Skip comment lines and empty lines. */ + if (*bp == '#' || *bp == 0) + continue; + items = sscanf(buf, "%as %as %as", ®ex, &type, &context); + if (items < 2) { + fprintf(stderr, + _("%s: line number %d is missing fields (only read %s)\n"), + fn, lineno, buf); + inc_err(); + if (items == 1) + free(regex); + continue; + } else if (items == 2) { + /* The type field is optional. */ + free(context); + context = type; + type = 0; + } + + if (pass == 1) { + /* On the second pass, compile and store the specification in spec. */ + const char *reg_buf = regex; +#ifdef NOTYET + sxp->stem_id = find_stem_from_spec(®_buf); +#else + sxp->stem_id = -1; +#endif + sxp->pattern = regex; + + /* Anchor the regular expression. */ + len = strlen(reg_buf); + anchored_regex = xmalloc(len + 3); + sprintf(anchored_regex, "^%s$", reg_buf); + + /* Compile the regular expression. */ + sxp->preg = xcalloc(1, sizeof(*sxp->preg)); + regerr = regcomp(sxp->preg, anchored_regex, + REG_EXTENDED | REG_NOSUB); + if (regerr < 0) { + regerror(regerr, sxp->preg, errbuf, sizeof errbuf); + fprintf(stderr, + _("%s: unable to compile regular expression %s on line number %d: %s\n"), + fn, regex, lineno, + errbuf); + inc_err(); + } + free(anchored_regex); + + /* Convert the type string to a mode format */ + sxp->type = type; + sxp->mode = 0; + if (!type) + goto skip_type; + len = strlen(type); + if (type[0] != '-' || len != 2) { + fprintf(stderr, + _("%s: invalid type specifier %s on line number %d\n"), + fn, type, lineno); + inc_err(); + goto skip_type; + } + switch (type[1]) { + case 'b': + sxp->mode = S_IFBLK; + break; + case 'c': + sxp->mode = S_IFCHR; + break; + case 'd': + sxp->mode = S_IFDIR; + break; + case 'p': + sxp->mode = S_IFIFO; + break; + case 'l': + sxp->mode = S_IFLNK; + break; + case 's': + sxp->mode = S_IFSOCK; + break; + case '-': + sxp->mode = S_IFREG; + break; + default: + fprintf(stderr, + _("%s: invalid type specifier %s on line number %d\n"), + fn, type, lineno); + inc_err(); + } + + skip_type: + + sxp->context = context; + + if (strcmp(context, "<>")) { + if (security_check_context(context) < 0 && errno != ENOENT) { + fprintf(stderr, + _("%s: invalid context %s on line number %d\n"), + fn, context, lineno); + inc_err(); + } + } + + /* Determine if specification has + * any meta characters in the RE */ + rpmsxpHasMetaChars(sxp); + sxp++; + } + + sx->Count++; + if (pass == 0) { + free(regex); + if (type) + free(type); + free(context); + } + } + + if (nerr) + return -1; + + if (pass == 0) { + if (sx->Count == 0) + return 0; + sx->sxp = xcalloc(sx->Count, sizeof(*sx->sxp)); + rewind(fp); + } + } + fclose(fp); + + /* Sort the specifications with most general first */ + qsort(sx->sxp, sx->Count, sizeof(*sx->sxp), rpmsxpCompare); + + /* Verify no exact duplicates */ + if (rpmsxpCheckNoDupes(sx) != 0) + return -1; + + return 0; +} + rpmsx rpmsxNew(const char * fn) { rpmsx sx; @@ -83,12 +368,12 @@ rpmsx rpmsxNew(const char * fn) sx->i = -1; sx->sxp = NULL; -/*@-modfilesys@*/ -if (_rpmsx_debug < 0) -fprintf(stderr, "*** sx %p\t%s[%d]\n", sx, __func__, sx->Count); -/*@=modfilesys@*/ + (void) rpmsxLink(sx, __func__); + + if (rpmsxParse(sx, fn) != 0) + return rpmsxFree(sx); - return rpmsxLink(sx, (sx ? __func__ : NULL)); + return sx; } int rpmsxCount(const rpmsx sx) diff --git a/lib/tre.c b/lib/tre.c new file mode 100644 index 0000000..69f355a --- /dev/null +++ b/lib/tre.c @@ -0,0 +1,131 @@ +#include "system.h" + +#define _RPMSX_INTERNAL +#include +#include + +#include "debug.h" + +static int add_assoc = 1; + +/* + * Command-line options. + */ +static int debug = 0; +static int change = 1; +static int quiet = 0; +#define QPRINTF(args...) do { if (!quiet) printf(args); } while (0) +static int use_stdin = 0; +static int verbose = 0; +static int warn_no_match = 0; +static char *rootpath = NULL; +static int rootpathlen = 0; + +static struct poptOption optionsTable[] = { + { "debug", 'd', POPT_ARG_VAL, &debug, 1, + N_("show what specification matched each file"), NULL }, + { "nochange", 'n', POPT_ARG_VAL, &change, 0, + N_("do not change any file labels"), NULL }, + { "quiet", 'q', POPT_ARG_VAL, &quiet, 1, + N_("be quiet (suppress non-error output)"), NULL }, + { "root", 'r', POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT, &rootpath, 0, + N_("use an alternate root path"), N_("ROOT") }, + { "stdin", 's', POPT_ARG_VAL, &use_stdin, 1, + N_("use stdin for a list of files instead of searching a partition"), NULL }, + { "verbose", 'v', POPT_ARG_VAL, &warn_no_match, 1, + N_("show changes in file labels"), NULL }, + { "warn", 'W', POPT_ARG_VAL, &warn_no_match, 1, + N_("warn about entries that have no matching file"), NULL }, + + POPT_AUTOHELP + POPT_TABLEEND +}; + +int main(int argc, char **argv) +{ + poptContext optCon; + const char ** av; + rpmsx sx; + rpmsxp sxp; + int ec = EXIT_FAILURE; /* assume failure. */ + int rc; + int i; + +#if HAVE_MCHECK_H && HAVE_MTRACE + /*@-noeffect@*/ + mtrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */ + /*@=noeffect@*/ +#endif + + setprogname(argv[0]); /* Retrofit glibc __progname */ + /* XXX glibc churn sanity */ + if (__progname == NULL) { + if ((__progname = strrchr(argv[0], '/')) != NULL) __progname++; + else __progname = argv[0]; + } + + (void) setlocale(LC_ALL, "" ); + (void) bindtextdomain(PACKAGE, LOCALEDIR); + (void) textdomain(PACKAGE); + + optCon = poptGetContext(__progname, argc, (const char **)argv, optionsTable, 0); + + /* Process all options, whine if unknown. */ + while ((rc = poptGetNextOpt(optCon)) > 0) { + switch (rc) { + default: +/*@-nullpass@*/ + fprintf(stderr, _("%s: option table misconfigured (%d)\n"), + __progname, rc); +/*@=nullpass@*/ + goto exit; + /*@notreached@*/ /*@switchbreak@*/ break; + } + } + + if (rc < -1) { +/*@-nullpass@*/ + fprintf(stderr, "%s: %s: %s\n", __progname, + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(rc)); +/*@=nullpass@*/ + goto exit; + } + + /* trim trailing /, if present */ + if (rootpath != NULL) { + rootpathlen = strlen(rootpath); + while (rootpath[rootpathlen - 1] == '/') + rootpath[--rootpathlen] = 0; + } + + av = poptGetArgs(optCon); + + /* Parse the specification file. */ + sx = rpmsxNew(NULL); + + sx = rpmsxInit(sx); + if (sx != NULL) + while ((i = rpmsxNext(sx)) >= 0) { + sxp = sx->sxp + i; + fprintf(stderr, "%5d: %s\t%s\t%s\n", i, + sxp->pattern, (sxp->type ? sxp->type : ""), sxp->context); + } + sx = rpmsxFree(sx); + + /* + * Apply the specifications to the file systems. + */ + ec = 0; + +exit: + optCon = poptFreeContext(optCon); + +#if HAVE_MCHECK_H && HAVE_MTRACE + /*@-noeffect@*/ + muntrace(); /* Trace malloc only if MALLOC_TRACE=mtrace-output-file. */ + /*@=noeffect@*/ +#endif + + return ec; +} -- 2.7.4