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
This commit is contained in:
Robert Watson 2002-12-02 03:22:57 +00:00
parent 5b8429c7b3
commit a8896b02e0
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=107489
3 changed files with 517 additions and 56 deletions

View File

@ -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>

View File

@ -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

View File

@ -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;
};
struct label_specs {
STAILQ_HEAD(label_specs_head, label_spec) head;
};
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)
{
FTSENT *ftsent;
FTS *fts;
struct label_specs *specs;
int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
int ch, is_setfmac;
char *bn;
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 = FTS_PHYSICAL;
break;
case 's':
add_specs(specs, optarg, 1);
break;
case 'v':
vflag++;
break;
case 'x':
xflag = FTS_XDEV;
break;
default:
usage(is_setfmac);
}
}
argc -= optind;
argv += optind;
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(void)
usage(int is_setfmac)
{
fprintf(stderr, "setfmac [-h] [label] [file1] [file2 ...]\n");
exit (EX_USAGE);
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
main(int argc, char *argv[])
chomp_line(char **line, size_t *linesize)
{
char ch;
mac_t label;
int hflag;
int error, i;
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);
}
hflag = 0;
while ((ch = getopt(argc, argv, "h")) != -1) {
switch (ch) {
case 'h':
hflag = 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;
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:
usage();
errx(1, "%s: invalid mode string: %s", file, modestr);
}
} else {
entry->modestr = "";
}
argv += optind;
argc -= optind;
if (argc < 2)
usage();
error = mac_from_text(&label, argv[0]);
if (error != 0) {
perror("mac_from_text");
return (-1);
}
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);
}
}
mac_free(label);
exit(EX_OK);
}
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);
}
struct label_specs *
new_specs(void)
{
struct label_specs *specs;
specs = malloc(sizeof(*specs));
if (specs == NULL)
err(1, "malloc");
STAILQ_INIT(&specs->head);
return (specs);
}