2 * Copyright (c) 2002, 2004 Networks Associates Technology, Inc.
5 * This software was developed for the FreeBSD Project by NAI Labs, the
6 * Security Research Division of Network Associates, Inc. under
7 * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
8 * CHATS research program.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 #include <sys/types.h>
36 #include <sys/types.h>
37 #include <sys/queue.h>
53 struct label_spec_entry {
54 regex_t regex; /* compiled regular expression to match */
55 char *regexstr; /* uncompiled regular expression */
56 mode_t mode; /* mode to possibly match */
57 const char *modestr; /* print-worthy ",-?" mode string */
58 char *mactext; /* MAC label to apply */
59 int flags; /* miscellaneous flags */
60 #define F_DONTLABEL 0x01
61 #define F_ALWAYSMATCH 0x02
62 } *entries, /* entries[0..nentries] */
63 *match; /* cached decision for MAC label to apply */
64 size_t nentries; /* size of entries list */
65 STAILQ_ENTRY(label_spec) link;
69 STAILQ_HEAD(label_specs_head, label_spec) head;
72 void usage(int) __dead2;
73 struct label_specs *new_specs(void);
74 void add_specs(struct label_specs *, const char *, int);
75 void add_setfmac_specs(struct label_specs *, char *);
76 void add_spec_line(const char *, int, struct label_spec_entry *, char *);
77 int apply_specs(struct label_specs *, FTSENT *, int, int);
78 int specs_empty(struct label_specs *);
83 main(int argc, char **argv)
87 struct label_specs *specs;
88 int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
92 bn = basename(argv[0]);
95 is_setfmac = strcmp(bn, "setfmac") == 0;
96 hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL;
98 while ((ch = getopt(argc, argv, is_setfmac ? "Rhq" : "ef:qs:vx")) !=
108 add_specs(specs, optarg, 0);
111 hflag = FTS_PHYSICAL;
117 add_specs(specs, optarg, 1);
135 add_setfmac_specs(specs, *argv);
139 if (argc == 0 || specs_empty(specs))
142 fts = fts_open(argv, hflag | xflag, NULL);
144 err(1, "cannot traverse filesystem%s", argc ? "s" : "");
145 while ((ftsent = fts_read(fts)) != NULL) {
146 switch (ftsent->fts_info) {
147 case FTS_DP: /* skip post-order */
149 case FTS_D: /* do pre-order */
150 case FTS_DC: /* do cyclic? */
151 /* don't ever recurse directories as setfmac(8) */
152 if (is_setfmac && !Rflag)
153 fts_set(fts, ftsent, FTS_SKIP);
154 case FTS_DEFAULT: /* do default */
155 case FTS_F: /* do regular */
156 case FTS_SL: /* do symlink */
157 case FTS_SLNONE: /* do symlink */
158 case FTS_W: /* do whiteout */
159 if (apply_specs(specs, ftsent, hflag, vflag)) {
161 errx(1, "labeling not supported in "
162 "%.*s", ftsent->fts_pathlen,
166 warnx("labeling not supported in %.*s",
169 fts_set(fts, ftsent, FTS_SKIP);
172 case FTS_DNR: /* die on all errors */
175 err(1, "traversing %.*s", ftsent->fts_pathlen,
178 errx(1, "CANNOT HAPPEN (%d) traversing %.*s",
179 ftsent->fts_info, ftsent->fts_pathlen,
188 usage(int is_setfmac)
192 fprintf(stderr, "usage: setfmac [-Rhq] label file ...\n");
194 fprintf(stderr, "usage: setfsmac [-ehqvx] [-f specfile [...]] [-s specfile [...]] file ...\n");
199 chomp_line(char **line, size_t *linesize)
204 for (s = *line; (unsigned)(s - *line) < *linesize; s++) {
213 memmove(*line, s, *linesize - (s - *line));
214 *linesize -= s - *line;
215 for (s = &(*line)[*linesize - 1]; s >= *line; s--) {
219 if (s != &(*line)[*linesize - 1]) {
220 *linesize = s - *line + 1;
222 s = malloc(*linesize + 1);
225 strncpy(s, *line, *linesize);
229 (*line)[*linesize] = '\0';
234 add_specs(struct label_specs *specs, const char *file, int is_sebsd)
236 struct label_spec *spec;
239 size_t nlines = 0, linesize;
242 spec = malloc(sizeof(*spec));
245 fp = fopen(file, "r");
247 err(1, "opening %s", file);
248 while ((line = fgetln(fp, &linesize)) != NULL) {
249 freeline = chomp_line(&line, &linesize);
250 if (linesize > 0) /* only allocate space for non-comments */
256 err(1, "fgetln on %s", file);
258 spec->entries = calloc(nlines, sizeof(*spec->entries));
259 if (spec->entries == NULL)
261 spec->nentries = nlines;
263 line = fgetln(fp, &linesize);
266 errx(1, "%s ended prematurely", file);
268 err(1, "failure reading %s", file);
270 freeline = chomp_line(&line, &linesize);
276 add_spec_line(file, is_sebsd, &spec->entries[--nlines], line);
282 warnx("%s: read %lu specifications", file,
283 (long)spec->nentries);
284 STAILQ_INSERT_TAIL(&specs->head, spec, link);
288 add_setfmac_specs(struct label_specs *specs, char *label)
290 struct label_spec *spec;
292 spec = malloc(sizeof(*spec));
296 spec->entries = calloc(spec->nentries, sizeof(*spec->entries));
297 if (spec->entries == NULL)
299 /* The _only_ thing specified here is the mactext! */
300 spec->entries->mactext = label;
301 spec->entries->flags |= F_ALWAYSMATCH;
302 STAILQ_INSERT_TAIL(&specs->head, spec, link);
306 add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry,
309 char *regexstr, *modestr, *macstr, *regerrorstr;
313 regexstr = strtok(line, " \t");
314 if (regexstr == NULL)
315 errx(1, "%s: need regular expression", file);
316 modestr = strtok(NULL, " \t");
318 errx(1, "%s: need a label", file);
319 macstr = strtok(NULL, " \t");
320 if (macstr == NULL) { /* the mode is just optional */
324 if (strtok(NULL, " \t") != NULL)
325 errx(1, "%s: extraneous fields at end of line", file);
326 /* assume we need to anchor this regex */
327 if (asprintf(®exstr, "^%s$", regexstr) == -1)
328 err(1, "%s: processing regular expression", file);
329 entry->regexstr = regexstr;
330 error = regcomp(&entry->regex, regexstr, REG_EXTENDED | REG_NOSUB);
332 size = regerror(error, &entry->regex, NULL, 0);
333 regerrorstr = malloc(size);
334 if (regerrorstr == NULL)
336 (void)regerror(error, &entry->regex, regerrorstr, size);
337 errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr);
340 entry->mactext = strdup(macstr);
341 if (entry->mactext == NULL)
344 if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1)
346 if (strcmp(macstr, "<<none>>") == 0)
347 entry->flags |= F_DONTLABEL;
349 if (modestr != NULL) {
350 if (strlen(modestr) != 2 || modestr[0] != '-')
351 errx(1, "%s: invalid mode string: %s", file, modestr);
352 switch (modestr[1]) {
354 entry->mode = S_IFBLK;
355 entry->modestr = ",-b";
358 entry->mode = S_IFCHR;
359 entry->modestr = ",-c";
362 entry->mode = S_IFDIR;
363 entry->modestr = ",-d";
366 entry->mode = S_IFIFO;
367 entry->modestr = ",-p";
370 entry->mode = S_IFLNK;
371 entry->modestr = ",-l";
374 entry->mode = S_IFSOCK;
375 entry->modestr = ",-s";
378 entry->mode = S_IFREG;
379 entry->modestr = ",--";
382 errx(1, "%s: invalid mode string: %s", file, modestr);
390 specs_empty(struct label_specs *specs)
393 return (STAILQ_EMPTY(&specs->head));
397 apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag)
400 struct label_spec *ls;
401 struct label_spec_entry *ent;
402 char *regerrorstr, *macstr;
405 int error, matchedby;
408 * Work through file context sources in order of specification
409 * on the command line, and through their entries in reverse
410 * order to find the "last" (hopefully "best") match.
413 STAILQ_FOREACH(ls, &specs->head, link) {
414 for (ls->match = NULL, ent = ls->entries;
415 ent < &ls->entries[ls->nentries]; ent++) {
416 if (ent->flags & F_ALWAYSMATCH)
418 if (ent->mode != 0 &&
419 (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode)
422 pmatch.rm_eo = ftsent->fts_pathlen;
423 error = regexec(&ent->regex, ftsent->fts_path, 1,
424 &pmatch, REG_STARTEND);
431 size = regerror(error, &ent->regex, NULL, 0);
432 regerrorstr = malloc(size);
433 if (regerrorstr == NULL)
435 (void)regerror(error, &ent->regex, regerrorstr,
437 errx(1, "%s: %s", ent->regexstr, regerrorstr);
442 if (matchedby == 0) {
443 printf("%.*s matched by ",
448 printf("%s(%s%s,%s)", matchedby == 2 ? "," : "",
449 ent->regexstr, ent->modestr, ent->mactext);
456 if (vflag && matchedby)
459 STAILQ_FOREACH(ls, &specs->head, link) {
460 /* cached match decision */
461 if (ls->match && (ls->match->flags & F_DONTLABEL) == 0)
462 /* add length of "x\0"/"y," */
463 size += strlen(ls->match->mactext) + 1;
467 macstr = malloc(size);
471 STAILQ_FOREACH(ls, &specs->head, link) {
472 /* cached match decision */
473 if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) {
476 strcat(macstr, ls->match->mactext);
479 if (mac_from_text(&mac, macstr))
480 err(1, "mac_from_text(%s)", macstr);
481 if ((hflag == FTS_PHYSICAL ? mac_set_link(ftsent->fts_accpath, mac) :
482 mac_set_file(ftsent->fts_accpath, mac)) != 0) {
483 if (errno == EOPNOTSUPP) {
488 err(1, "mac_set_link(%.*s, %s)", ftsent->fts_pathlen,
489 ftsent->fts_path, macstr);
499 struct label_specs *specs;
501 specs = malloc(sizeof(*specs));
504 STAILQ_INIT(&specs->head);