PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/usr.sbin/setfmac/setfmac.c

https://bitbucket.org/freebsd/freebsd-base
C | 500 lines | 430 code | 25 blank | 45 comment | 122 complexity | 0372dcdf5e14bcda458cf9a179c574b8 MD5 | raw file
  1. /*-
  2. * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  3. *
  4. * Copyright (c) 2002, 2004 Networks Associates Technology, Inc.
  5. * All rights reserved.
  6. *
  7. * This software was developed for the FreeBSD Project by NAI Labs, the
  8. * Security Research Division of Network Associates, Inc. under
  9. * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
  10. * CHATS research program.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions
  14. * are met:
  15. * 1. Redistributions of source code must retain the above copyright
  16. * notice, this list of conditions and the following disclaimer.
  17. * 2. Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the distribution.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  22. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * $FreeBSD$
  34. */
  35. #include <sys/types.h>
  36. #include <sys/mac.h>
  37. #include <sys/queue.h>
  38. #include <sys/stat.h>
  39. #include <ctype.h>
  40. #include <err.h>
  41. #include <errno.h>
  42. #include <fts.h>
  43. #include <libgen.h>
  44. #include <regex.h>
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <string.h>
  48. #include <unistd.h>
  49. struct label_spec {
  50. struct label_spec_entry {
  51. regex_t regex; /* compiled regular expression to match */
  52. char *regexstr; /* uncompiled regular expression */
  53. mode_t mode; /* mode to possibly match */
  54. const char *modestr; /* print-worthy ",-?" mode string */
  55. char *mactext; /* MAC label to apply */
  56. int flags; /* miscellaneous flags */
  57. #define F_DONTLABEL 0x01
  58. #define F_ALWAYSMATCH 0x02
  59. } *entries, /* entries[0..nentries] */
  60. *match; /* cached decision for MAC label to apply */
  61. size_t nentries; /* size of entries list */
  62. STAILQ_ENTRY(label_spec) link;
  63. };
  64. struct label_specs {
  65. STAILQ_HEAD(label_specs_head, label_spec) head;
  66. };
  67. void usage(int) __dead2;
  68. struct label_specs *new_specs(void);
  69. void add_specs(struct label_specs *, const char *, int);
  70. void add_setfmac_specs(struct label_specs *, char *);
  71. void add_spec_line(const char *, int, struct label_spec_entry *, char *);
  72. int apply_specs(struct label_specs *, FTSENT *, int, int);
  73. int specs_empty(struct label_specs *);
  74. static int qflag;
  75. int
  76. main(int argc, char **argv)
  77. {
  78. FTSENT *ftsent;
  79. FTS *fts;
  80. struct label_specs *specs;
  81. int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
  82. int ch, is_setfmac;
  83. char *bn;
  84. bn = basename(argv[0]);
  85. if (bn == NULL)
  86. err(1, "basename");
  87. is_setfmac = strcmp(bn, "setfmac") == 0;
  88. hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL;
  89. specs = new_specs();
  90. while ((ch = getopt(argc, argv, is_setfmac ? "Rhq" : "ef:qs:vx")) !=
  91. -1) {
  92. switch (ch) {
  93. case 'R':
  94. Rflag = 1;
  95. break;
  96. case 'e':
  97. eflag = 1;
  98. break;
  99. case 'f':
  100. add_specs(specs, optarg, 0);
  101. break;
  102. case 'h':
  103. hflag = FTS_PHYSICAL;
  104. break;
  105. case 'q':
  106. qflag = 1;
  107. break;
  108. case 's':
  109. add_specs(specs, optarg, 1);
  110. break;
  111. case 'v':
  112. vflag++;
  113. break;
  114. case 'x':
  115. xflag = FTS_XDEV;
  116. break;
  117. default:
  118. usage(is_setfmac);
  119. }
  120. }
  121. argc -= optind;
  122. argv += optind;
  123. if (is_setfmac) {
  124. if (argc <= 1)
  125. usage(is_setfmac);
  126. add_setfmac_specs(specs, *argv);
  127. argc--;
  128. argv++;
  129. } else {
  130. if (argc == 0 || specs_empty(specs))
  131. usage(is_setfmac);
  132. }
  133. fts = fts_open(argv, hflag | xflag, NULL);
  134. if (fts == NULL)
  135. err(1, "cannot traverse filesystem%s", argc ? "s" : "");
  136. while ((ftsent = fts_read(fts)) != NULL) {
  137. switch (ftsent->fts_info) {
  138. case FTS_DP: /* skip post-order */
  139. break;
  140. case FTS_D: /* do pre-order */
  141. case FTS_DC: /* do cyclic? */
  142. /* don't ever recurse directories as setfmac(8) */
  143. if (is_setfmac && !Rflag)
  144. fts_set(fts, ftsent, FTS_SKIP);
  145. case FTS_DEFAULT: /* do default */
  146. case FTS_F: /* do regular */
  147. case FTS_SL: /* do symlink */
  148. case FTS_SLNONE: /* do symlink */
  149. case FTS_W: /* do whiteout */
  150. if (apply_specs(specs, ftsent, hflag, vflag)) {
  151. if (eflag) {
  152. errx(1, "labeling not supported in %s",
  153. ftsent->fts_path);
  154. }
  155. if (!qflag)
  156. warnx("labeling not supported in %s",
  157. ftsent->fts_path);
  158. fts_set(fts, ftsent, FTS_SKIP);
  159. }
  160. break;
  161. case FTS_DNR: /* die on all errors */
  162. case FTS_ERR:
  163. case FTS_NS:
  164. err(1, "traversing %s", ftsent->fts_path);
  165. default:
  166. errx(1, "CANNOT HAPPEN (%d) traversing %s",
  167. ftsent->fts_info, ftsent->fts_path);
  168. }
  169. }
  170. fts_close(fts);
  171. exit(0);
  172. }
  173. void
  174. usage(int is_setfmac)
  175. {
  176. if (is_setfmac)
  177. fprintf(stderr, "usage: setfmac [-Rhq] label file ...\n");
  178. else
  179. fprintf(stderr, "usage: setfsmac [-ehqvx] [-f specfile [...]] [-s specfile [...]] file ...\n");
  180. exit(1);
  181. }
  182. static int
  183. chomp_line(char **line, size_t *linesize)
  184. {
  185. char *s;
  186. int freeme = 0;
  187. for (s = *line; (unsigned)(s - *line) < *linesize; s++) {
  188. if (!isspace(*s))
  189. break;
  190. }
  191. if (*s == '#') {
  192. **line = '\0';
  193. *linesize = 0;
  194. return (freeme);
  195. }
  196. memmove(*line, s, *linesize - (s - *line));
  197. *linesize -= s - *line;
  198. for (s = &(*line)[*linesize - 1]; s >= *line; s--) {
  199. if (!isspace(*s))
  200. break;
  201. }
  202. if (s != &(*line)[*linesize - 1]) {
  203. *linesize = s - *line + 1;
  204. } else {
  205. s = malloc(*linesize + 1);
  206. if (s == NULL)
  207. err(1, "malloc");
  208. strncpy(s, *line, *linesize);
  209. *line = s;
  210. freeme = 1;
  211. }
  212. (*line)[*linesize] = '\0';
  213. return (freeme);
  214. }
  215. void
  216. add_specs(struct label_specs *specs, const char *file, int is_sebsd)
  217. {
  218. struct label_spec *spec;
  219. FILE *fp;
  220. char *line;
  221. size_t nlines = 0, linesize;
  222. int freeline;
  223. spec = malloc(sizeof(*spec));
  224. if (spec == NULL)
  225. err(1, "malloc");
  226. fp = fopen(file, "r");
  227. if (fp == NULL)
  228. err(1, "opening %s", file);
  229. while ((line = fgetln(fp, &linesize)) != NULL) {
  230. freeline = chomp_line(&line, &linesize);
  231. if (linesize > 0) /* only allocate space for non-comments */
  232. nlines++;
  233. if (freeline)
  234. free(line);
  235. }
  236. if (ferror(fp))
  237. err(1, "fgetln on %s", file);
  238. rewind(fp);
  239. spec->entries = calloc(nlines, sizeof(*spec->entries));
  240. if (spec->entries == NULL)
  241. err(1, "malloc");
  242. spec->nentries = nlines;
  243. while (nlines > 0) {
  244. line = fgetln(fp, &linesize);
  245. if (line == NULL) {
  246. if (feof(fp))
  247. errx(1, "%s ended prematurely", file);
  248. else
  249. err(1, "failure reading %s", file);
  250. }
  251. freeline = chomp_line(&line, &linesize);
  252. if (linesize == 0) {
  253. if (freeline)
  254. free(line);
  255. continue;
  256. }
  257. add_spec_line(file, is_sebsd, &spec->entries[--nlines], line);
  258. if (freeline)
  259. free(line);
  260. }
  261. fclose(fp);
  262. if (!qflag)
  263. warnx("%s: read %lu specifications", file,
  264. (long)spec->nentries);
  265. STAILQ_INSERT_TAIL(&specs->head, spec, link);
  266. }
  267. void
  268. add_setfmac_specs(struct label_specs *specs, char *label)
  269. {
  270. struct label_spec *spec;
  271. spec = malloc(sizeof(*spec));
  272. if (spec == NULL)
  273. err(1, "malloc");
  274. spec->nentries = 1;
  275. spec->entries = calloc(spec->nentries, sizeof(*spec->entries));
  276. if (spec->entries == NULL)
  277. err(1, "malloc");
  278. /* The _only_ thing specified here is the mactext! */
  279. spec->entries->mactext = label;
  280. spec->entries->flags |= F_ALWAYSMATCH;
  281. STAILQ_INSERT_TAIL(&specs->head, spec, link);
  282. }
  283. void
  284. add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry,
  285. char *line)
  286. {
  287. char *regexstr, *modestr, *macstr, *regerrorstr;
  288. size_t size;
  289. int error;
  290. regexstr = strtok(line, " \t");
  291. if (regexstr == NULL)
  292. errx(1, "%s: need regular expression", file);
  293. modestr = strtok(NULL, " \t");
  294. if (modestr == NULL)
  295. errx(1, "%s: need a label", file);
  296. macstr = strtok(NULL, " \t");
  297. if (macstr == NULL) { /* the mode is just optional */
  298. macstr = modestr;
  299. modestr = NULL;
  300. }
  301. if (strtok(NULL, " \t") != NULL)
  302. errx(1, "%s: extraneous fields at end of line", file);
  303. /* assume we need to anchor this regex */
  304. if (asprintf(&regexstr, "^%s$", regexstr) == -1)
  305. err(1, "%s: processing regular expression", file);
  306. entry->regexstr = regexstr;
  307. error = regcomp(&entry->regex, regexstr, REG_EXTENDED | REG_NOSUB);
  308. if (error) {
  309. size = regerror(error, &entry->regex, NULL, 0);
  310. regerrorstr = malloc(size);
  311. if (regerrorstr == NULL)
  312. err(1, "malloc");
  313. (void)regerror(error, &entry->regex, regerrorstr, size);
  314. errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr);
  315. }
  316. if (!is_sebsd) {
  317. entry->mactext = strdup(macstr);
  318. if (entry->mactext == NULL)
  319. err(1, "strdup");
  320. } else {
  321. if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1)
  322. err(1, "asprintf");
  323. if (strcmp(macstr, "<<none>>") == 0)
  324. entry->flags |= F_DONTLABEL;
  325. }
  326. if (modestr != NULL) {
  327. if (strlen(modestr) != 2 || modestr[0] != '-')
  328. errx(1, "%s: invalid mode string: %s", file, modestr);
  329. switch (modestr[1]) {
  330. case 'b':
  331. entry->mode = S_IFBLK;
  332. entry->modestr = ",-b";
  333. break;
  334. case 'c':
  335. entry->mode = S_IFCHR;
  336. entry->modestr = ",-c";
  337. break;
  338. case 'd':
  339. entry->mode = S_IFDIR;
  340. entry->modestr = ",-d";
  341. break;
  342. case 'p':
  343. entry->mode = S_IFIFO;
  344. entry->modestr = ",-p";
  345. break;
  346. case 'l':
  347. entry->mode = S_IFLNK;
  348. entry->modestr = ",-l";
  349. break;
  350. case 's':
  351. entry->mode = S_IFSOCK;
  352. entry->modestr = ",-s";
  353. break;
  354. case '-':
  355. entry->mode = S_IFREG;
  356. entry->modestr = ",--";
  357. break;
  358. default:
  359. errx(1, "%s: invalid mode string: %s", file, modestr);
  360. }
  361. } else {
  362. entry->modestr = "";
  363. }
  364. }
  365. int
  366. specs_empty(struct label_specs *specs)
  367. {
  368. return (STAILQ_EMPTY(&specs->head));
  369. }
  370. int
  371. apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag)
  372. {
  373. regmatch_t pmatch;
  374. struct label_spec *ls;
  375. struct label_spec_entry *ent;
  376. char *regerrorstr, *macstr;
  377. size_t size;
  378. mac_t mac;
  379. int error, matchedby;
  380. /*
  381. * Work through file context sources in order of specification
  382. * on the command line, and through their entries in reverse
  383. * order to find the "last" (hopefully "best") match.
  384. */
  385. matchedby = 0;
  386. STAILQ_FOREACH(ls, &specs->head, link) {
  387. for (ls->match = NULL, ent = ls->entries;
  388. ent < &ls->entries[ls->nentries]; ent++) {
  389. if (ent->flags & F_ALWAYSMATCH)
  390. goto matched;
  391. if (ent->mode != 0 &&
  392. (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode)
  393. continue;
  394. pmatch.rm_so = 0;
  395. pmatch.rm_eo = ftsent->fts_pathlen;
  396. error = regexec(&ent->regex, ftsent->fts_path, 1,
  397. &pmatch, REG_STARTEND);
  398. switch (error) {
  399. case REG_NOMATCH:
  400. continue;
  401. case 0:
  402. break;
  403. default:
  404. size = regerror(error, &ent->regex, NULL, 0);
  405. regerrorstr = malloc(size);
  406. if (regerrorstr == NULL)
  407. err(1, "malloc");
  408. (void)regerror(error, &ent->regex, regerrorstr,
  409. size);
  410. errx(1, "%s: %s", ent->regexstr, regerrorstr);
  411. }
  412. matched:
  413. ls->match = ent;
  414. if (vflag) {
  415. if (matchedby == 0) {
  416. printf("%s matched by ",
  417. ftsent->fts_path);
  418. matchedby = 1;
  419. }
  420. printf("%s(%s%s,%s)", matchedby == 2 ? "," : "",
  421. ent->regexstr, ent->modestr, ent->mactext);
  422. if (matchedby == 1)
  423. matchedby = 2;
  424. }
  425. break;
  426. }
  427. }
  428. if (vflag && matchedby)
  429. printf("\n");
  430. size = 0;
  431. STAILQ_FOREACH(ls, &specs->head, link) {
  432. /* cached match decision */
  433. if (ls->match && (ls->match->flags & F_DONTLABEL) == 0)
  434. /* add length of "x\0"/"y," */
  435. size += strlen(ls->match->mactext) + 1;
  436. }
  437. if (size == 0)
  438. return (0);
  439. macstr = malloc(size);
  440. if (macstr == NULL)
  441. err(1, "malloc");
  442. *macstr = '\0';
  443. STAILQ_FOREACH(ls, &specs->head, link) {
  444. /* cached match decision */
  445. if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) {
  446. if (*macstr != '\0')
  447. strcat(macstr, ",");
  448. strcat(macstr, ls->match->mactext);
  449. }
  450. }
  451. if (mac_from_text(&mac, macstr))
  452. err(1, "mac_from_text(%s)", macstr);
  453. if ((hflag == FTS_PHYSICAL ? mac_set_link(ftsent->fts_accpath, mac) :
  454. mac_set_file(ftsent->fts_accpath, mac)) != 0) {
  455. if (errno == EOPNOTSUPP) {
  456. mac_free(mac);
  457. free(macstr);
  458. return (1);
  459. }
  460. err(1, "mac_set_link(%s, %s)", ftsent->fts_path, macstr);
  461. }
  462. mac_free(mac);
  463. free(macstr);
  464. return (0);
  465. }
  466. struct label_specs *
  467. new_specs(void)
  468. {
  469. struct label_specs *specs;
  470. specs = malloc(sizeof(*specs));
  471. if (specs == NULL)
  472. err(1, "malloc");
  473. STAILQ_INIT(&specs->head);
  474. return (specs);
  475. }