diff options
author | rwatson <rwatson@FreeBSD.org> | 2002-12-02 03:22:57 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2002-12-02 03:22:57 +0000 |
commit | 4af26db23aa536ce300778fff12649469e43dadc (patch) | |
tree | 7b74406a0401819ee806eb2cdb6b8c41a8c1c4a7 /usr.sbin/setfmac | |
parent | 88a7954786b4e5545bc8a616e88cb42ee217e725 (diff) | |
download | FreeBSD-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/Makefile | 4 | ||||
-rw-r--r-- | usr.sbin/setfmac/setfmac.8 | 81 | ||||
-rw-r--r-- | usr.sbin/setfmac/setfmac.c | 470 |
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(®exstr, "^%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); } |