summaryrefslogtreecommitdiffstats
path: root/usr.sbin/setfmac
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2002-12-02 03:22:57 +0000
committerrwatson <rwatson@FreeBSD.org>2002-12-02 03:22:57 +0000
commit4af26db23aa536ce300778fff12649469e43dadc (patch)
tree7b74406a0401819ee806eb2cdb6b8c41a8c1c4a7 /usr.sbin/setfmac
parent88a7954786b4e5545bc8a616e88cb42ee217e725 (diff)
downloadFreeBSD-src-4af26db23aa536ce300778fff12649469e43dadc.zip
FreeBSD-src-4af26db23aa536ce300778fff12649469e43dadc.tar.gz
Add support for -R for file relabel operations.
Add 'setfsmac' link, which permits labels to be provided in a label specification file, making it easier to provide initial file system labeling specifications. This is used by the new mac_lomac to provide initial system labeling and policy, and by sebsd, the port of SELinux FLASK/TE to FreeBSD. Approved by: re (jhb) Obtained from: TrustedBSD Project Sponsored by: DARPA, Network Associates Laboratories
Diffstat (limited to 'usr.sbin/setfmac')
-rw-r--r--usr.sbin/setfmac/Makefile4
-rw-r--r--usr.sbin/setfmac/setfmac.881
-rw-r--r--usr.sbin/setfmac/setfmac.c470
3 files changed, 508 insertions, 47 deletions
diff --git a/usr.sbin/setfmac/Makefile b/usr.sbin/setfmac/Makefile
index d991e98..e7171f7 100644
--- a/usr.sbin/setfmac/Makefile
+++ b/usr.sbin/setfmac/Makefile
@@ -4,6 +4,10 @@ PROG= setfmac
MAN= setfmac.8
SRCS= setfmac.c
+LINKS+= ${BINDIR}/setfmac ${BINDIR}/setfsmac
+
+MLINKS+= setfmac.8 setfsmac.8
+
WARNS?= 2
.include <bsd.prog.mk>
diff --git a/usr.sbin/setfmac/setfmac.8 b/usr.sbin/setfmac/setfmac.8
index 7d22ba6..62312e2 100644
--- a/usr.sbin/setfmac/setfmac.8
+++ b/usr.sbin/setfmac/setfmac.8
@@ -15,9 +15,6 @@
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
-.\" 3. The names of the authors may not be used to endorse or promote
-.\" products derived from this software without specific prior written
-.\" permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -35,18 +32,86 @@
.Dd June 27, 2002
.Dt SETFMAC 8
.Sh NAME
-.Nm setfmac
+.Nm setfmac ,
+.Nm setfsmac
.Nd set MAC label for a file system object
.Sh SYNOPSIS
-.Nm
+.Nm setfmac
+.Op Fl hR
.Ar label
-.Ar
+.Op Ar file ...
+.Nm setfsmac
+.Op Fl ehvx
+.Op Fl f Ar specfile
+.Op Fl s Ar specfile
+.Ar path
+.Op Ar file ...
.Sh DESCRIPTION
The
-.Nm
-utility associates the specified MAC label to the specified files.
+.Nm setfmac
+utility assigns the specified MAC label to the specified files.
+The following options are available:
+.Bl -tag -width indent
+.It Fl R
+Set the labels on the file hierarchies rooted in the files instead of
+just the files themselves.
+.It Fl h
+If the file is a symbolic link, change the label of the link rather
+than the file that the link points to.
+.El
+.Pp
+The
+.Nm setfsmac
+utility accepts a list of specification files as input and sets the MAC
+labels on the specified file system hierarchies.
+Path names specified will be visited in order as given on the command line,
+and each tree will be traversed in pre-order.
+(Generally, it will not be very useful to use relative, instead of absolute,
+paths.)
+The labels that match a file will be combined and set in a single
+transaction.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl e
+Treat any filesystems encountered which do not support MAC labelling as
+errors, instead of warning and skipping past them.
+.It Fl f Ar specfile
+Add the specifications in
+.Ar specfile
+as a set of which at most one will be applied to each file traversed per
+.Fl f Ar specfile
+given.
+.It Fl h
+If the file is a symbolic link, change the label of the link rather
+than the file that the link points to.
+.It Fl s Ar specfile
+Add the specification in
+.Ar specfile ,
+but assume that the specification format is that used in the port
+of SELinux to FreeBSD as SEBSD.
+At most one of the specifications will be applied to each file traversed per
+.Fl f Ar specfile
+given.
+The prefix
+.Dq sebsd/
+will automatically be prepended to the labels in this file, and labels
+matching
+.Dq <<none>>
+will be explicitly not relabeled.
+This permits SEBSD to re-use existing SELinux policy specification files
+unmodified.
+.It Fl v
+Increase the degree of verbosity.
+When given, information detailing the labelling operation is printed while
+in progress.
+.It Fl x
+Do not cross recurse into new filesystems when traversing them.
+.El
.Sh SEE ALSO
.Xr mac 3 ,
.Xr mac_set_file 3 ,
+.Xr mac_set_link 3 ,
+.Xr re_format 7 ,
.Xr getfmac 8 ,
.Xr mac 9
diff --git a/usr.sbin/setfmac/setfmac.c b/usr.sbin/setfmac/setfmac.c
index 19d679c..a54e29f 100644
--- a/usr.sbin/setfmac/setfmac.c
+++ b/usr.sbin/setfmac/setfmac.c
@@ -15,9 +15,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. The names of the authors may not be used to endorse or promote
- * products derived from this software without specific prior written
- * permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -33,71 +30,466 @@
*
* $FreeBSD$
*/
+
+#include <sys/types.h>
+#include <sys/mac.h>
#include <sys/types.h>
+#include <sys/queue.h>
#include <sys/mac.h>
+#include <sys/stat.h>
+#include <ctype.h>
#include <err.h>
-#include <paths.h>
+#include <errno.h>
+#include <fts.h>
+#include <libgen.h>
+#include <regex.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sysexits.h>
#include <unistd.h>
-#define MAXELEMENTS 32
+struct label_spec {
+ struct label_spec_entry {
+ regex_t regex; /* compiled regular expression to match */
+ char *regexstr; /* uncompiled regular expression */
+ mode_t mode; /* mode to possibly match */
+ char *modestr; /* print-worthy ",-?" mode string */
+ char *mactext; /* MAC label to apply */
+ int flags; /* miscellaneous flags */
+#define F_DONTLABEL 0x01
+#define F_ALWAYSMATCH 0x02
+ } *entries, /* entries[0..nentries] */
+ *match; /* cached decision for MAC label to apply */
+ size_t nentries; /* size of entries list */
+ STAILQ_ENTRY(label_spec) link;
+};
-void
-usage(void)
-{
+struct label_specs {
+ STAILQ_HEAD(label_specs_head, label_spec) head;
+};
- fprintf(stderr, "setfmac [-h] [label] [file1] [file2 ...]\n");
- exit (EX_USAGE);
-}
+void usage(int) __dead2;
+struct label_specs *new_specs(void);
+void add_specs(struct label_specs *, const char *, int);
+void add_setfmac_specs(struct label_specs *, char *);
+void add_spec_line(const char *, int, struct label_spec_entry *, char *);
+int apply_specs(struct label_specs *, FTSENT *, int, int);
+int specs_empty(struct label_specs *);
int
-main(int argc, char *argv[])
+main(int argc, char **argv)
{
- char ch;
- mac_t label;
- int hflag;
- int error, i;
+ FTSENT *ftsent;
+ FTS *fts;
+ struct label_specs *specs;
+ int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
+ int ch, is_setfmac;
+ char *bn;
- hflag = 0;
- while ((ch = getopt(argc, argv, "h")) != -1) {
+ bn = basename(argv[0]);
+ if (bn == NULL)
+ err(1, "basename");
+ is_setfmac = strcmp(bn, "setfmac") == 0;
+ hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL;
+ specs = new_specs();
+ while ((ch = getopt(argc, argv, is_setfmac ? "Rh" : "ef:s:vx")) != -1) {
switch (ch) {
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ add_specs(specs, optarg, 0);
+ break;
case 'h':
- hflag = 1;
+ hflag = FTS_PHYSICAL;
+ break;
+ case 's':
+ add_specs(specs, optarg, 1);
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'x':
+ xflag = FTS_XDEV;
break;
default:
- usage();
+ usage(is_setfmac);
}
}
-
- argv += optind;
argc -= optind;
+ argv += optind;
- if (argc < 2)
- usage();
+ if (is_setfmac) {
+ if (argc <= 1)
+ usage(is_setfmac);
+ add_setfmac_specs(specs, *argv);
+ argc--;
+ argv++;
+ } else {
+ if (argc == 0 || specs_empty(specs))
+ usage(is_setfmac);
+ }
+ fts = fts_open(argv, hflag | xflag, NULL);
+ if (fts == NULL)
+ err(1, "cannot traverse filesystem%s", argc ? "s" : "");
+ while ((ftsent = fts_read(fts)) != NULL) {
+ switch (ftsent->fts_info) {
+ case FTS_DP: /* skip post-order */
+ break;
+ case FTS_D: /* do pre-order */
+ case FTS_DC: /* do cyclic? */
+ /* don't ever recurse directories as setfmac(8) */
+ if (is_setfmac && !Rflag)
+ fts_set(fts, ftsent, FTS_SKIP);
+ case FTS_DEFAULT: /* do default */
+ case FTS_F: /* do regular */
+ case FTS_SL: /* do symlink */
+ case FTS_W: /* do whiteout */
+ if (apply_specs(specs, ftsent, hflag, vflag)) {
+ if (eflag) {
+ errx(1, "labeling not supported in "
+ "%.*s", ftsent->fts_pathlen,
+ ftsent->fts_path);
+ }
+ warnx("labeling not supported in %.*s",
+ ftsent->fts_pathlen, ftsent->fts_path);
+ fts_set(fts, ftsent, FTS_SKIP);
+ }
+ break;
+ case FTS_DNR: /* die on all errors */
+ case FTS_ERR:
+ case FTS_NS:
+ err(1, "traversing %.*s", ftsent->fts_pathlen,
+ ftsent->fts_path);
+ default:
+ errx(1, "CANNOT HAPPEN (%d) traversing %.*s",
+ ftsent->fts_info, ftsent->fts_pathlen,
+ ftsent->fts_path);
+ }
+ }
+ fts_close(fts);
+ exit(0);
+}
+
+void
+usage(int is_setfmac)
+{
+
+ if (is_setfmac)
+ fprintf(stderr, "usage: setfmac [-Rh] label path [...]\n");
+ else
+ fprintf(stderr, "usage: setfsmac [-evx] [-f specfile [...]] [-s specfile [...]] path [...]\n");
+ exit(1);
+}
+
+int
+chomp_line(char **line, size_t *linesize)
+{
+ char *s;
+ int freeme = 0;
+
+ for (s = *line; s - *line < *linesize; s++) {
+ if (!isspace(*s))
+ break;
+ }
+ if (*s == '#') {
+ **line = '\0';
+ *linesize = 0;
+ return (freeme);
+ }
+ memmove(*line, s, *linesize - (s - *line));
+ *linesize -= s - *line;
+ for (s = &(*line)[*linesize - 1]; s >= *line; s--) {
+ if (!isspace(*s))
+ break;
+ }
+ if (s != &(*line)[*linesize - 1]) {
+ *linesize = s - *line + 1;
+ } else {
+ s = malloc(*linesize + 1);
+ if (s == NULL)
+ err(1, "malloc");
+ strncpy(s, *line, *linesize);
+ *line = s;
+ freeme = 1;
+ }
+ (*line)[*linesize] = '\0';
+ return (freeme);
+}
- error = mac_from_text(&label, argv[0]);
- if (error != 0) {
- perror("mac_from_text");
- return (-1);
+void
+add_specs(struct label_specs *specs, const char *file, int is_sebsd)
+{
+ struct label_spec *spec;
+ FILE *fp;
+ char *line;
+ size_t nlines = 0, linesize;
+ int freeline;
+
+ spec = malloc(sizeof(*spec));
+ if (spec == NULL)
+ err(1, "malloc");
+ fp = fopen(file, "r");
+ if (fp == NULL)
+ err(1, "opening %s", file);
+ while ((line = fgetln(fp, &linesize)) != NULL) {
+ freeline = chomp_line(&line, &linesize);
+ if (linesize > 0) /* only allocate space for non-comments */
+ nlines++;
+ if (freeline)
+ free(line);
+ }
+ if (ferror(fp))
+ err(1, "fgetln on %s", file);
+ rewind(fp);
+ spec->entries = calloc(nlines, sizeof(*spec->entries));
+ if (spec->entries == NULL)
+ err(1, "malloc");
+ spec->nentries = nlines;
+ while (nlines > 0) {
+ line = fgetln(fp, &linesize);
+ if (line == NULL) {
+ if (feof(fp))
+ errx(1, "%s ended prematurely", file);
+ else
+ err(1, "failure reading %s", file);
+ }
+ freeline = chomp_line(&line, &linesize);
+ if (linesize == 0) {
+ if (freeline)
+ free(line);
+ continue;
+ }
+ add_spec_line(file, is_sebsd, &spec->entries[--nlines], line);
+ if (freeline)
+ free(line);
}
+ fclose(fp);
+ warnx("%s: read %u specifications", file, spec->nentries);
+ STAILQ_INSERT_TAIL(&specs->head, spec, link);
+}
+
+void
+add_setfmac_specs(struct label_specs *specs, char *label)
+{
+ struct label_spec *spec;
+
+ spec = malloc(sizeof(*spec));
+ if (spec == NULL)
+ err(1, "malloc");
+ spec->nentries = 1;
+ spec->entries = calloc(spec->nentries, sizeof(*spec->entries));
+ if (spec->entries == NULL)
+ err(1, "malloc");
+ /* The _only_ thing specified here is the mactext! */
+ spec->entries->mactext = label;
+ spec->entries->flags |= F_ALWAYSMATCH;
+ STAILQ_INSERT_TAIL(&specs->head, spec, link);
+}
+
+void
+add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry,
+ char *line)
+{
+ char *regexstr, *modestr, *macstr, *regerrorstr;
+ size_t size;
+ int error;
- for (i = 1; i < argc; i++) {
- if (hflag)
- error = mac_set_link(argv[i], label);
- else
- error = mac_set_file(argv[i], label);
- if (error != 0) {
- perror(argv[i]);
- return (-1);
+ regexstr = strtok(line, " \t");
+ if (regexstr == NULL)
+ errx(1, "%s: need regular expression", file);
+ modestr = strtok(NULL, " \t");
+ if (modestr == NULL)
+ errx(1, "%s: need a label", file);
+ macstr = strtok(NULL, " \t");
+ if (macstr == NULL) { /* the mode is just optional */
+ macstr = modestr;
+ modestr = NULL;
+ }
+ if (strtok(NULL, " \t") != NULL)
+ errx(1, "%s: extraneous fields at end of line", file);
+ /* assume we need to anchor this regex */
+ if (asprintf(&regexstr, "^%s$", regexstr) == -1)
+ err(1, "%s: processing regular expression", file);
+ entry->regexstr = regexstr;
+ error = regcomp(&entry->regex, regexstr, REG_EXTENDED | REG_NOSUB);
+ if (error) {
+ size = regerror(error, &entry->regex, NULL, 0);
+ regerrorstr = malloc(size);
+ if (regerrorstr == NULL)
+ err(1, "malloc");
+ (void)regerror(error, &entry->regex, regerrorstr, size);
+ errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr);
+ }
+ if (!is_sebsd) {
+ entry->mactext = strdup(macstr);
+ if (entry->mactext == NULL)
+ err(1, "strdup");
+ } else {
+ if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1)
+ err(1, "asprintf");
+ if (strcmp(macstr, "<<none>>") == 0)
+ entry->flags |= F_DONTLABEL;
+ }
+ if (modestr != NULL) {
+ if (strlen(modestr) != 2 || modestr[0] != '-')
+ errx(1, "%s: invalid mode string: %s", file, modestr);
+ switch (modestr[1]) {
+ case 'b':
+ entry->mode = S_IFBLK;
+ entry->modestr = ",-b";
+ break;
+ case 'c':
+ entry->mode = S_IFCHR;
+ entry->modestr = ",-c";
+ break;
+ case 'd':
+ entry->mode = S_IFDIR;
+ entry->modestr = ",-d";
+ break;
+ case 'p':
+ entry->mode = S_IFIFO;
+ entry->modestr = ",-p";
+ break;
+ case 'l':
+ entry->mode = S_IFLNK;
+ entry->modestr = ",-l";
+ break;
+ case 's':
+ entry->mode = S_IFSOCK;
+ entry->modestr = ",-s";
+ break;
+ case '-':
+ entry->mode = S_IFREG;
+ entry->modestr = ",--";
+ break;
+ default:
+ errx(1, "%s: invalid mode string: %s", file, modestr);
}
+ } else {
+ entry->modestr = "";
+ }
+}
+
+int
+specs_empty(struct label_specs *specs)
+{
+
+ return (STAILQ_EMPTY(&specs->head));
+}
+
+int
+apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag)
+{
+ regmatch_t pmatch;
+ struct label_spec *ls;
+ struct label_spec_entry *ent;
+ char *regerrorstr, *macstr;
+ size_t size;
+ mac_t mac;
+ int error, matchedby;
+ /*
+ * Work through file context sources in order of specification
+ * on the command line, and through their entries in reverse
+ * order to find the "last" (hopefully "best") match.
+ */
+ matchedby = 0;
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ for (ls->match = NULL, ent = ls->entries;
+ ent < &ls->entries[ls->nentries]; ent++) {
+ if (ent->flags & F_ALWAYSMATCH)
+ goto matched;
+ if (ent->mode != 0 &&
+ (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode)
+ continue;
+ pmatch.rm_so = 0;
+ pmatch.rm_eo = ftsent->fts_pathlen;
+ error = regexec(&ent->regex, ftsent->fts_path, 1,
+ &pmatch, REG_STARTEND);
+ switch (error) {
+ case REG_NOMATCH:
+ continue;
+ case 0:
+ break;
+ default:
+ size = regerror(error, &ent->regex, NULL, 0);
+ regerrorstr = malloc(size);
+ if (regerrorstr == NULL)
+ err(1, "malloc");
+ (void)regerror(error, &ent->regex, regerrorstr,
+ size);
+ errx(1, "%s: %s", ent->regexstr, regerrorstr);
+ }
+ matched:
+ ls->match = ent;
+ if (vflag) {
+ if (matchedby == 0) {
+ printf("%.*s matched by ",
+ ftsent->fts_pathlen,
+ ftsent->fts_path);
+ matchedby = 1;
+ }
+ printf("%s(%s%s,%s)", matchedby == 2 ? "," : "",
+ ent->regexstr, ent->modestr, ent->mactext);
+ if (matchedby == 1)
+ matchedby = 2;
+ }
+ break;
+ }
+ }
+ if (vflag && matchedby)
+ printf("\n");
+ size = 0;
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ /* cached match decision */
+ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0)
+ /* add length of "x\0"/"y," */
+ size += strlen(ls->match->mactext) + 1;
+ }
+ if (size == 0)
+ return (0);
+ macstr = malloc(size);
+ if (macstr == NULL)
+ err(1, "malloc");
+ *macstr = '\0';
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ /* cached match decision */
+ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) {
+ if (*macstr != '\0')
+ strcat(macstr, ",");
+ strcat(macstr, ls->match->mactext);
+ }
+ }
+ if (mac_from_text(&mac, macstr))
+ err(1, "mac_from_text(%s)", macstr);
+ if ((hflag == FTS_PHYSICAL ? mac_set_link(ftsent->fts_accpath, mac) :
+ mac_set_file(ftsent->fts_accpath, mac)) != 0) {
+ if (errno == EOPNOTSUPP) {
+ mac_free(mac);
+ free(macstr);
+ return (1);
+ }
+ err(1, "mac_set_link(%.*s, %s)", ftsent->fts_pathlen,
+ ftsent->fts_path, macstr);
}
+ mac_free(mac);
+ free(macstr);
+ return (0);
+}
- mac_free(label);
+struct label_specs *
+new_specs(void)
+{
+ struct label_specs *specs;
- exit(EX_OK);
+ specs = malloc(sizeof(*specs));
+ if (specs == NULL)
+ err(1, "malloc");
+ STAILQ_INIT(&specs->head);
+ return (specs);
}
OpenPOWER on IntegriCloud