]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/setfmac/setfmac.c
This commit was generated by cvs2svn to compensate for changes in r157184,
[FreeBSD/FreeBSD.git] / usr.sbin / setfmac / setfmac.c
1 /*-
2  * Copyright (c) 2002, 2004 Networks Associates Technology, Inc.
3  * All rights reserved.
4  *
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.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
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.
18  *
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
29  * SUCH DAMAGE.
30  *
31  * $FreeBSD$
32  */
33
34 #include <sys/types.h>
35 #include <sys/mac.h>
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <sys/mac.h>
39 #include <sys/stat.h>
40
41 #include <ctype.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <fts.h>
45 #include <libgen.h>
46 #include <regex.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 struct label_spec {
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;
66 };
67
68 struct label_specs {
69         STAILQ_HEAD(label_specs_head, label_spec) head;
70 };
71
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 *);
79
80 static int qflag;
81
82 int
83 main(int argc, char **argv)
84 {
85         FTSENT *ftsent;
86         FTS *fts;
87         struct label_specs *specs;
88         int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
89         int ch, is_setfmac;
90         char *bn;
91
92         bn = basename(argv[0]);
93         if (bn == NULL)
94                 err(1, "basename");
95         is_setfmac = strcmp(bn, "setfmac") == 0;
96         hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL;
97         specs = new_specs();
98         while ((ch = getopt(argc, argv, is_setfmac ? "Rhq" : "ef:qs:vx")) !=
99             -1) {
100                 switch (ch) {
101                 case 'R':
102                         Rflag = 1;
103                         break;
104                 case 'e':
105                         eflag = 1;
106                         break;
107                 case 'f':
108                         add_specs(specs, optarg, 0);
109                         break;
110                 case 'h':
111                         hflag = FTS_PHYSICAL;
112                         break;
113                 case 'q':
114                         qflag = 1;
115                         break;
116                 case 's':
117                         add_specs(specs, optarg, 1);
118                         break;
119                 case 'v':
120                         vflag++;
121                         break;
122                 case 'x':
123                         xflag = FTS_XDEV;
124                         break;
125                 default:
126                         usage(is_setfmac);
127                 }
128         }
129         argc -= optind;
130         argv += optind;
131
132         if (is_setfmac) {
133                 if (argc <= 1)  
134                         usage(is_setfmac);
135                 add_setfmac_specs(specs, *argv);
136                 argc--;
137                 argv++;
138         } else {
139                 if (argc == 0 || specs_empty(specs))
140                         usage(is_setfmac);
141         }
142         fts = fts_open(argv, hflag | xflag, NULL);
143         if (fts == 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 */
148                         break;
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)) {
160                                 if (eflag) {
161                                         errx(1, "labeling not supported in "
162                                             "%.*s", ftsent->fts_pathlen,
163                                             ftsent->fts_path);
164                                 }
165                                 if (!qflag)
166                                         warnx("labeling not supported in %.*s",
167                                             ftsent->fts_pathlen,
168                                             ftsent->fts_path);
169                                 fts_set(fts, ftsent, FTS_SKIP);
170                         }
171                         break;
172                 case FTS_DNR:           /* die on all errors */
173                 case FTS_ERR:
174                 case FTS_NS:
175                         err(1, "traversing %.*s", ftsent->fts_pathlen,
176                             ftsent->fts_path);
177                 default:
178                         errx(1, "CANNOT HAPPEN (%d) traversing %.*s",
179                             ftsent->fts_info, ftsent->fts_pathlen,
180                             ftsent->fts_path);
181                 }
182         }
183         fts_close(fts);
184         exit(0);
185 }
186
187 void
188 usage(int is_setfmac)
189 {
190
191         if (is_setfmac)
192                 fprintf(stderr, "usage: setfmac [-Rhq] label file ...\n");
193         else
194                 fprintf(stderr, "usage: setfsmac [-ehqvx] [-f specfile [...]] [-s specfile [...]] file ...\n");
195         exit(1);
196 }
197
198 static int
199 chomp_line(char **line, size_t *linesize)
200 {
201         char *s;
202         int freeme = 0;
203         
204         for (s = *line; (unsigned)(s - *line) < *linesize; s++) {
205                 if (!isspace(*s))
206                         break;
207         }
208         if (*s == '#') {
209                 **line = '\0';
210                 *linesize = 0;
211                 return (freeme);
212         }
213         memmove(*line, s, *linesize - (s - *line));
214         *linesize -= s - *line;
215         for (s = &(*line)[*linesize - 1]; s >= *line; s--) {
216                 if (!isspace(*s))
217                         break;
218         }
219         if (s != &(*line)[*linesize - 1]) {
220                 *linesize = s - *line + 1;
221         } else {
222                 s = malloc(*linesize + 1);
223                 if (s == NULL)
224                         err(1, "malloc");
225                 strncpy(s, *line, *linesize);
226                 *line = s;
227                 freeme = 1;
228         }
229         (*line)[*linesize] = '\0';
230         return (freeme);
231 }
232
233 void
234 add_specs(struct label_specs *specs, const char *file, int is_sebsd)
235 {
236         struct label_spec *spec;
237         FILE *fp;
238         char *line;
239         size_t nlines = 0, linesize;
240         int freeline;
241
242         spec = malloc(sizeof(*spec));
243         if (spec == NULL)
244                 err(1, "malloc");
245         fp = fopen(file, "r");
246         if (fp == NULL)
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 */
251                         nlines++;
252                 if (freeline)
253                         free(line);
254         }
255         if (ferror(fp))
256                 err(1, "fgetln on %s", file);
257         rewind(fp);
258         spec->entries = calloc(nlines, sizeof(*spec->entries));
259         if (spec->entries == NULL)
260                 err(1, "malloc");
261         spec->nentries = nlines;
262         while (nlines > 0) {
263                 line = fgetln(fp, &linesize);
264                 if (line == NULL) {
265                         if (feof(fp))
266                                 errx(1, "%s ended prematurely", file);
267                         else
268                                 err(1, "failure reading %s", file);
269                 }
270                 freeline = chomp_line(&line, &linesize);
271                 if (linesize == 0) {
272                         if (freeline)
273                                 free(line);
274                         continue;
275                 }
276                 add_spec_line(file, is_sebsd, &spec->entries[--nlines], line);
277                 if (freeline)
278                         free(line);
279         }
280         fclose(fp);
281         if (!qflag)
282                 warnx("%s: read %lu specifications", file,
283                     (long)spec->nentries);
284         STAILQ_INSERT_TAIL(&specs->head, spec, link);
285 }
286
287 void
288 add_setfmac_specs(struct label_specs *specs, char *label)
289 {
290         struct label_spec *spec;
291
292         spec = malloc(sizeof(*spec));
293         if (spec == NULL)
294                 err(1, "malloc");
295         spec->nentries = 1;
296         spec->entries = calloc(spec->nentries, sizeof(*spec->entries));
297         if (spec->entries == NULL)
298                 err(1, "malloc");
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);
303 }
304
305 void
306 add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry,
307     char *line)
308 {
309         char *regexstr, *modestr, *macstr, *regerrorstr;
310         size_t size;
311         int error;
312
313         regexstr = strtok(line, " \t");
314         if (regexstr == NULL)
315                 errx(1, "%s: need regular expression", file);
316         modestr = strtok(NULL, " \t");
317         if (modestr == NULL)
318                 errx(1, "%s: need a label", file);
319         macstr = strtok(NULL, " \t");
320         if (macstr == NULL) {   /* the mode is just optional */
321                 macstr = modestr;
322                 modestr = NULL;
323         }
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(&regexstr, "^%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);
331         if (error) {
332                 size = regerror(error, &entry->regex, NULL, 0);
333                 regerrorstr = malloc(size);
334                 if (regerrorstr == NULL)
335                         err(1, "malloc");
336                 (void)regerror(error, &entry->regex, regerrorstr, size);
337                 errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr);
338         }
339         if (!is_sebsd) {
340                 entry->mactext = strdup(macstr);
341                 if (entry->mactext == NULL)
342                         err(1, "strdup");
343         } else {
344                 if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1)
345                         err(1, "asprintf");
346                 if (strcmp(macstr, "<<none>>") == 0)
347                         entry->flags |= F_DONTLABEL;
348         }
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]) {
353                 case 'b':
354                         entry->mode = S_IFBLK;
355                         entry->modestr = ",-b";
356                         break;
357                 case 'c':
358                         entry->mode = S_IFCHR;
359                         entry->modestr = ",-c";
360                         break;
361                 case 'd':
362                         entry->mode = S_IFDIR;
363                         entry->modestr = ",-d";
364                         break;
365                 case 'p':
366                         entry->mode = S_IFIFO;
367                         entry->modestr = ",-p";
368                         break;
369                 case 'l':
370                         entry->mode = S_IFLNK;
371                         entry->modestr = ",-l";
372                         break;
373                 case 's':
374                         entry->mode = S_IFSOCK;
375                         entry->modestr = ",-s";
376                         break;
377                 case '-':
378                         entry->mode = S_IFREG;
379                         entry->modestr = ",--";
380                         break;
381                 default:
382                         errx(1, "%s: invalid mode string: %s", file, modestr);
383                 }
384         } else {
385                 entry->modestr = "";
386         }
387 }
388
389 int
390 specs_empty(struct label_specs *specs)
391 {
392
393         return (STAILQ_EMPTY(&specs->head));
394 }
395
396 int
397 apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag)
398 {
399         regmatch_t pmatch;
400         struct label_spec *ls;
401         struct label_spec_entry *ent;
402         char *regerrorstr, *macstr;
403         size_t size;
404         mac_t mac;
405         int error, matchedby;
406
407         /*
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.
411          */
412         matchedby = 0;
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)
417                                 goto matched;
418                         if (ent->mode != 0 &&
419                             (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode)
420                                 continue;
421                         pmatch.rm_so = 0;
422                         pmatch.rm_eo = ftsent->fts_pathlen;
423                         error = regexec(&ent->regex, ftsent->fts_path, 1,
424                             &pmatch, REG_STARTEND);
425                         switch (error) {
426                         case REG_NOMATCH:
427                                 continue;
428                         case 0:
429                                 break;
430                         default:
431                                 size = regerror(error, &ent->regex, NULL, 0);
432                                 regerrorstr = malloc(size);
433                                 if (regerrorstr == NULL)
434                                         err(1, "malloc");
435                                 (void)regerror(error, &ent->regex, regerrorstr,
436                                     size);
437                                 errx(1, "%s: %s", ent->regexstr, regerrorstr);
438                         }
439                 matched:
440                         ls->match = ent;
441                         if (vflag) {
442                                 if (matchedby == 0) {
443                                         printf("%.*s matched by ",
444                                             ftsent->fts_pathlen,
445                                             ftsent->fts_path);
446                                         matchedby = 1;
447                                 }
448                                 printf("%s(%s%s,%s)", matchedby == 2 ? "," : "",
449                                     ent->regexstr, ent->modestr, ent->mactext);
450                                 if (matchedby == 1)
451                                         matchedby = 2;
452                         }
453                         break;
454                 }
455         }
456         if (vflag && matchedby)
457                 printf("\n");
458         size = 0;
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;
464         }
465         if (size == 0)
466                 return (0);
467         macstr = malloc(size);
468         if (macstr == NULL)
469                 err(1, "malloc");
470         *macstr = '\0';
471         STAILQ_FOREACH(ls, &specs->head, link) {
472                 /* cached match decision */
473                 if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) {
474                         if (*macstr != '\0')
475                                 strcat(macstr, ",");
476                         strcat(macstr, ls->match->mactext);
477                 }
478         }
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) {
484                         mac_free(mac);
485                         free(macstr);
486                         return (1);
487                 }
488                 err(1, "mac_set_link(%.*s, %s)", ftsent->fts_pathlen,
489                     ftsent->fts_path, macstr);
490         }
491         mac_free(mac);
492         free(macstr);
493         return (0);
494 }
495
496 struct label_specs *
497 new_specs(void)
498 {
499         struct label_specs *specs;
500
501         specs = malloc(sizeof(*specs));
502         if (specs == NULL)
503                 err(1, "malloc");
504         STAILQ_INIT(&specs->head);
505         return (specs);
506 }