diff options
author | dd <dd@FreeBSD.org> | 2002-07-17 01:46:48 +0000 |
---|---|---|
committer | dd <dd@FreeBSD.org> | 2002-07-17 01:46:48 +0000 |
commit | 9498a983a938cec96851b64642f0b62bba7d1827 (patch) | |
tree | 403633d67f5bcbe849c414892c2fea74d94f0b09 /sbin/devfs/rule.c | |
parent | c57275f3471899132e94d39ef870d25599ec6f95 (diff) | |
download | FreeBSD-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.c | 419 |
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"); +} |