summaryrefslogtreecommitdiffstats
path: root/sbin/devfs/rule.c
diff options
context:
space:
mode:
authordd <dd@FreeBSD.org>2002-07-17 01:46:48 +0000
committerdd <dd@FreeBSD.org>2002-07-17 01:46:48 +0000
commit9498a983a938cec96851b64642f0b62bba7d1827 (patch)
tree403633d67f5bcbe849c414892c2fea74d94f0b09 /sbin/devfs/rule.c
parentc57275f3471899132e94d39ef870d25599ec6f95 (diff)
downloadFreeBSD-src-9498a983a938cec96851b64642f0b62bba7d1827.zip
FreeBSD-src-9498a983a938cec96851b64642f0b62bba7d1827.tar.gz
Introduce the DEVFS "rule" subsystem. DEVFS rules permit the
administrator to define certain properties of new devfs nodes before they become visible to the userland. Both static (e.g., /dev/speaker) and dynamic (e.g., /dev/bpf*, some removable devices) nodes are supported. Each DEVFS mount may have a different ruleset assigned to it, permitting different policies to be implemented for things like jails. Approved by: phk
Diffstat (limited to 'sbin/devfs/rule.c')
-rw-r--r--sbin/devfs/rule.c419
1 files changed, 419 insertions, 0 deletions
diff --git a/sbin/devfs/rule.c b/sbin/devfs/rule.c
new file mode 100644
index 0000000..27ad2a6
--- /dev/null
+++ b/sbin/devfs/rule.c
@@ -0,0 +1,419 @@
+/*-
+ * Copyright (c) 2002 Dima Dorfman.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Rule subsystem manipulation.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/ioctl.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void rulespec_intok(struct devfs_rule *dr, int ac, char **av,
+ devfs_rsnum rsnum);
+static void rulespec_outfp(FILE *fp, struct devfs_rule *dr);
+
+static command_t rule_add, rule_apply, rule_applyset;
+static command_t rule_del, rule_delset, rule_show, rule_showsets;
+
+static ctbl_t ctbl_rule = {
+ { "add", rule_add },
+ { "apply", rule_apply },
+ { "applyset", rule_applyset },
+ { "del", rule_del },
+ { "delset", rule_delset },
+ { "show", rule_show },
+ { "showsets", rule_showsets },
+ { NULL, NULL }
+};
+
+static struct intstr ist_type[] = {
+ { "disk", D_DISK },
+ { "mem", D_MEM },
+ { "tape", D_TAPE },
+ { "tty", D_TTY },
+ { NULL, -1 }
+};
+
+devfs_rsnum in_rsnum;
+
+int
+rule_main(int ac, char **av)
+{
+ struct cmd *c;
+ char ch;
+
+ setprogname("devfs rule");
+ optreset = optind = 1;
+ while ((ch = getopt(ac, av, "s:")) != -1)
+ switch (ch) {
+ case 's':
+ in_rsnum = eatonum(optarg);
+ break;
+ default:
+ usage();
+ }
+ ac -= optind;
+ av += optind;
+ if (ac < 1)
+ usage();
+
+ for (c = ctbl_rule; c->name != NULL; ++c)
+ if (strcmp(c->name, av[0]) == 0)
+ exit((*c->handler)(ac, av));
+ errx(1, "unknown command: %s", av[0]);
+}
+
+static int
+rule_add(int ac, char **av)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RADD, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RADD");
+ return (0);
+}
+
+static int
+rule_apply(int ac __unused, char **av __unused)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ devfs_rid rid;
+ int rv;
+
+ if (ac < 2)
+ usage();
+ if (!atonum(av[1], &rnum)) {
+ rulespec_intok(&dr, ac - 1, av + 1, in_rsnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLY, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLY");
+ } else {
+ rid = mkrid(in_rsnum, rnum);
+ rv = ioctl(mpfd, DEVFSIO_RAPPLYID, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RAPPLYID");
+ }
+ return (0);
+}
+
+static int
+rule_applyset(int ac, char **av __unused)
+{
+ int rv;
+
+ if (ac != 1)
+ usage();
+ rv = ioctl(mpfd, DEVFSIO_SAPPLY, &in_rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SAPPLY");
+ return (0);
+}
+
+static int
+rule_del(int ac __unused, char **av)
+{
+ devfs_rid rid;
+ int rv;
+
+ if (av[1] == NULL)
+ usage();
+ rid = mkrid(in_rsnum, eatoi(av[1]));
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &rid);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ return (0);
+}
+
+static int
+rule_delset(int ac, char **av __unused)
+{
+ struct devfs_rule dr;
+ int rv;
+
+ if (ac != 1)
+ usage();
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1) {
+ rv = ioctl(mpfd, DEVFSIO_RDEL, &dr.dr_id);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RDEL");
+ }
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ return (0);
+}
+
+static int
+rule_show(int ac __unused, char **av)
+{
+ struct devfs_rule dr;
+ devfs_rnum rnum;
+ int rv;
+
+ memset(&dr, '\0', sizeof(dr));
+ dr.dr_magic = DEVFS_MAGIC;
+ if (av[1] != NULL) {
+ rnum = eatoi(av[1]);
+ dr.dr_id = mkrid(in_rsnum, rnum - 1);
+ rv = ioctl(mpfd, DEVFSIO_RGETNEXT, &dr);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ if (rid2rn(dr.dr_id) == rnum)
+ rulespec_outfp(stdout, &dr);
+ } else {
+ dr.dr_id = mkrid(in_rsnum, 0);
+ while (ioctl(mpfd, DEVFSIO_RGETNEXT, &dr) != -1)
+ rulespec_outfp(stdout, &dr);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_RGETNEXT");
+ }
+ return (0);
+}
+
+static int
+rule_showsets(int ac, char **av __unused)
+{
+ devfs_rsnum rsnum;
+
+ if (ac != 1)
+ usage();
+ rsnum = 0;
+ while (ioctl(mpfd, DEVFSIO_SGETNEXT, &rsnum) != -1)
+ printf("%d\n", rsnum);
+ if (errno != ENOENT)
+ err(1, "ioctl DEVFSIO_SGETNEXT");
+ return (0);
+}
+
+int
+ruleset_main(int ac, char **av)
+{
+ devfs_rsnum rsnum;
+ int rv;
+
+ setprogname("devfs ruleset");
+ if (ac < 2)
+ usage();
+ rsnum = eatonum(av[1]);
+ rv = ioctl(mpfd, DEVFSIO_SUSE, &rsnum);
+ if (rv == -1)
+ err(1, "ioctl DEVFSIO_SUSE");
+ return (0);
+}
+
+
+/*
+ * Construct a /struct devfs_rule/ from ac and av.
+ */
+static void
+rulespec_intok(struct devfs_rule *dr, int ac __unused, char **av,
+ devfs_rsnum rsnum)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+ devfs_rnum rnum;
+ char *cp;
+ long l;
+
+ memset(dr, '\0', sizeof(*dr));
+
+ /*
+ * We don't maintain ac hereinafter.
+ */
+ if (av[0] == NULL)
+ errx(1, "unexpected end of rulespec");
+
+ /* If the first argument is an integer, treat it as a rule number. */
+ if (!atonum(av[0], &rnum))
+ rnum = 0; /* auto-number */
+ else
+ ++av;
+
+ /*
+ * These aren't table-driven since that would result in more
+ * tiny functions than I care to deal with.
+ */
+ for (;;) {
+ if (av[0] == NULL)
+ break;
+ else if (strcmp(av[0], "type") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for type");
+ for (is = ist_type; is->s != NULL; ++is)
+ if (strcmp(av[1], is->s) == 0) {
+ dr->dr_dswflags |= is->i;
+ break;
+ }
+ if (is->s == NULL)
+ errx(1, "unknown type: %s", av[1]);
+ dr->dr_icond |= DRC_DSWFLAGS;
+ av += 2;
+ } else if (strcmp(av[0], "path") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for path");
+ if (strlcpy(dr->dr_pathptrn, av[1], DEVFS_MAXPTRNLEN)
+ >= DEVFS_MAXPTRNLEN)
+ warnx("pattern specified too long; truncated");
+ dr->dr_icond |= DRC_PATHPTRN;
+ av += 2;
+ } else if (strcmp(av[0], "major") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for major");
+ dr->dr_major = eatoi(av[1]);
+ dr->dr_icond |= DRC_MAJOR;
+ av += 2;
+ } else
+ break;
+ }
+ for (;;) {
+ if (av[0] == NULL)
+ break;
+ else if (strcmp(av[0], "hide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_HIDE;
+ ++av;
+ } else if (strcmp(av[0], "unhide") == 0) {
+ dr->dr_iacts |= DRA_BACTS;
+ dr->dr_bacts |= DRB_UNHIDE;
+ ++av;
+ } else if (strcmp(av[0], "user") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for user");
+ dr->dr_iacts |= DRA_UID;
+ pw = getpwnam(av[1]);
+ if (pw != NULL)
+ dr->dr_uid = pw->pw_uid;
+ else
+ dr->dr_uid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "group") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for group");
+ dr->dr_iacts |= DRA_GID;
+ gr = getgrnam(av[1]);
+ if (gr != NULL)
+ dr->dr_gid = gr->gr_gid;
+ else
+ dr->dr_gid = eatoi(av[1]); /* XXX overflow */
+ av += 2;
+ } else if (strcmp(av[0], "mode") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for mode");
+ dr->dr_iacts |= DRA_MODE;
+ l = strtol(av[1], &cp, 8);
+ if (l > (1 << (sizeof(dr->dr_mode) * 8)) - 1 ||
+ *cp != '\0')
+ errx(1, "invalid mode: %s", av[1]);
+ dr->dr_mode = l;
+ av += 2;
+ } else if (strcmp(av[0], "include") == 0) {
+ if (av[1] == NULL)
+ errx(1, "expecting argument for include");
+ dr->dr_iacts |= DRA_INCSET;
+ dr->dr_incset = eatonum(av[1]);
+ av += 2;
+ } else
+ errx(1, "unknown argument: %s", av[0]);
+ }
+
+ dr->dr_id = mkrid(rsnum, rnum);
+ dr->dr_magic = DEVFS_MAGIC;
+}
+
+/*
+ * Write a human-readable (and machine-parsable, by rulespec_in*())
+ * representation of dr to bufp. *bufp should be free(3)'d when the
+ * caller is finished with it.
+ */
+static void
+rulespec_outfp(FILE *fp, struct devfs_rule *dr)
+{
+ struct intstr *is;
+ struct passwd *pw;
+ struct group *gr;
+
+ fprintf(fp, "%d", rid2rn(dr->dr_id));
+
+ if (dr->dr_icond & DRC_DSWFLAGS)
+ for (is = ist_type; is->s != NULL; ++is)
+ if (dr->dr_dswflags & is->i)
+ fprintf(fp, " type %s", is->s);
+ if (dr->dr_icond & DRC_PATHPTRN)
+ fprintf(fp, " path %s", dr->dr_pathptrn);
+ if (dr->dr_icond & DRC_MAJOR)
+ fprintf(fp, " major %d", dr->dr_major);
+
+ if (dr->dr_iacts & DRA_BACTS) {
+ if (dr->dr_bacts & DRB_HIDE)
+ fprintf(fp, " hide");
+ if (dr->dr_bacts & DRB_UNHIDE)
+ fprintf(fp, " unhide");
+ }
+ if (dr->dr_iacts & DRA_UID) {
+ pw = getpwuid(dr->dr_uid);
+ if (pw == NULL)
+ fprintf(fp, " user %d", dr->dr_uid);
+ else
+ fprintf(fp, " user %s", pw->pw_name);
+ }
+ if (dr->dr_iacts & DRA_GID) {
+ gr = getgrgid(dr->dr_gid);
+ if (gr == NULL)
+ fprintf(fp, " group %d", dr->dr_gid);
+ else
+ fprintf(fp, " group %s", gr->gr_name);
+ }
+ if (dr->dr_iacts & DRA_MODE)
+ fprintf(fp, " mode %o", dr->dr_mode);
+ if (dr->dr_iacts & DRA_INCSET)
+ fprintf(fp, " include %d", dr->dr_incset);
+
+ fprintf(fp, "\n");
+}
OpenPOWER on IntegriCloud