Parse selinux file_contexts patterns.
authorjbj <devnull@localhost>
Sun, 21 Dec 2003 17:17:57 +0000 (17:17 +0000)
committerjbj <devnull@localhost>
Sun, 21 Dec 2003 17:17:57 +0000 (17:17 +0000)
CVS patchset: 6987
CVS date: 2003/12/21 17:17:57

lib/Makefile.am
lib/rpmsx.c
lib/tre.c [new file with mode: 0644]

index 820beb0..34a552b 100644 (file)
@@ -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)
index 1fe2e93..3bb22e8 100644 (file)
@@ -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", &regex, &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(&reg_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, "<<none>>")) {
+                   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 (file)
index 0000000..69f355a
--- /dev/null
+++ b/lib/tre.c
@@ -0,0 +1,131 @@
+#include "system.h"
+
+#define        _RPMSX_INTERNAL
+#include <rpmsx.h>
+#include <popt.h>
+
+#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;
+}