From 4af26db23aa536ce300778fff12649469e43dadc Mon Sep 17 00:00:00 2001 From: rwatson Date: Mon, 2 Dec 2002 03:22:57 +0000 Subject: 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 --- usr.sbin/setfmac/setfmac.c | 470 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 431 insertions(+), 39 deletions(-) (limited to 'usr.sbin/setfmac/setfmac.c') 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 +#include #include +#include #include +#include +#include #include -#include +#include +#include +#include +#include #include #include #include -#include #include -#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, "<>") == 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); } -- cgit v1.1