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, "<<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;
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)
--- /dev/null
+#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;
+}