| Commit | Line | Data |
|---|---|---|
| cf0e588c MD |
1 | /* |
| 2 | * Copyright (c) 2009 The DragonFly Project. All rights reserved. | |
| 3 | * | |
| 4 | * This code is derived from software contributed to The DragonFly Project | |
| 5 | * by Alex Hornung <ahornung@gmail.com> | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that the following conditions | |
| 9 | * are met: | |
| 10 | * | |
| 11 | * 1. Redistributions of source code must retain the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer. | |
| 13 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 14 | * notice, this list of conditions and the following disclaimer in | |
| 15 | * the documentation and/or other materials provided with the | |
| 16 | * distribution. | |
| 17 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 18 | * contributors may be used to endorse or promote products derived | |
| 19 | * from this software without specific, prior written permission. | |
| 20 | * | |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 25 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 26 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 27 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 28 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 29 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 30 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 31 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 32 | * SUCH DAMAGE. | |
| 33 | */ | |
| 4cf905ba | 34 | |
| cf0e588c | 35 | #include <sys/types.h> |
| 5cb6a05c AH |
36 | #include <sys/cdefs.h> |
| 37 | #include <sys/syslimits.h> | |
| 4cf905ba AH |
38 | #include <sys/ioctl.h> |
| 39 | #include <sys/device.h> | |
| 40 | #include <sys/queue.h> | |
| b68ba70c | 41 | #include <sys/stat.h> |
| 2c1e28dd | 42 | #include <sys/devfs_rules.h> |
| 4cf905ba | 43 | |
| e87b69b2 | 44 | #include <ctype.h> |
| 4cf905ba | 45 | #include <err.h> |
| b68ba70c | 46 | #include <errno.h> |
| 4cf905ba AH |
47 | #include <fcntl.h> |
| 48 | #include <grp.h> | |
| 8212e700 | 49 | #include <libgen.h> |
| 4cf905ba AH |
50 | #include <pwd.h> |
| 51 | #include <stdarg.h> | |
| cf0e588c MD |
52 | #include <stdio.h> |
| 53 | #include <stdlib.h> | |
| 54 | #include <string.h> | |
| 4cf905ba | 55 | #include <unistd.h> |
| cf0e588c | 56 | |
| 5cb6a05c | 57 | |
| 4cf905ba AH |
58 | #include "devfsctl.h" |
| 59 | ||
| 60 | struct verb { | |
| 61 | const char *verb; | |
| 62 | rule_parser_t *parser; | |
| 63 | int min_args; | |
| 64 | }; | |
| 65 | ||
| 66 | struct devtype { | |
| 67 | const char *name; | |
| 68 | int value; | |
| 69 | }; | |
| 70 | ||
| 71 | ||
| 72 | ||
| 73 | static int parser_include(char **); | |
| 74 | static int parser_jail(char **); | |
| 75 | static int parser_hide(char **); | |
| 76 | static int parser_show(char **); | |
| 77 | static int parser_link(char **); | |
| 78 | static int parser_group(char **); | |
| 79 | static int parser_perm(char **); | |
| 80 | static int dump_config_entry(struct rule *, struct groupdevid *); | |
| 81 | static int rule_id_iterate(struct groupdevid *, struct rule *, | |
| 82 | rule_iterate_callback_t *); | |
| 5cb6a05c AH |
83 | static int rule_ioctl(unsigned long, struct devfs_rule_ioctl *); |
| 84 | static void rule_fill(struct devfs_rule_ioctl *, struct rule *, | |
| 4cf905ba AH |
85 | struct groupdevid *); |
| 86 | static int rule_send(struct rule *, struct groupdevid *); | |
| 87 | static int rule_check_num_args(char **, int); | |
| b68ba70c AH |
88 | static int process_line(FILE*, int); |
| 89 | static int rule_parser(char **tokens); | |
| 8212e700 | 90 | #if 0 |
| b68ba70c | 91 | static int ruletab_parser(char **tokens); |
| 8212e700 | 92 | #endif |
| 4cf905ba | 93 | static void usage(void); |
| cf0e588c MD |
94 | |
| 95 | static int dev_fd; | |
| 4cf905ba AH |
96 | |
| 97 | const char *config_name = NULL, *mountp = NULL; | |
| 98 | static int dflag = 0; | |
| b68ba70c | 99 | static int aflag = 0, cflag = 0, rflag = 0, tflag = 0; |
| 4cf905ba AH |
100 | static int line_stack[RULE_MAX_STACK]; |
| 101 | static char *file_stack[RULE_MAX_STACK]; | |
| 8212e700 | 102 | static char *cwd_stack[RULE_MAX_STACK]; |
| 4cf905ba | 103 | static int line_stack_depth = 0; |
| cf0e588c | 104 | static int jail = 0; |
| cf0e588c | 105 | |
| 4cf905ba AH |
106 | static TAILQ_HEAD(, rule) rule_list = |
| 107 | TAILQ_HEAD_INITIALIZER(rule_list); | |
| b68ba70c AH |
108 | static TAILQ_HEAD(, rule_tab) rule_tab_list = |
| 109 | TAILQ_HEAD_INITIALIZER(rule_tab_list); | |
| 4cf905ba AH |
110 | static TAILQ_HEAD(, groupdevid) group_list = |
| 111 | TAILQ_HEAD_INITIALIZER(group_list); | |
| cf0e588c | 112 | |
| cf0e588c | 113 | |
| 4cf905ba AH |
114 | static const struct verb parsers[] = { |
| 115 | { "include", parser_include, 1 }, | |
| 116 | { "jail", parser_jail, 1 }, | |
| 117 | { "group", parser_group, 2 }, | |
| 118 | { "perm", parser_perm, 2 }, | |
| 119 | { "link", parser_link, 2 }, | |
| 120 | { "hide", parser_hide, 2 }, | |
| 121 | { "show", parser_show, 2 }, | |
| 122 | { NULL, NULL, 0 } | |
| 123 | }; | |
| cf0e588c | 124 | |
| 4cf905ba AH |
125 | static const struct devtype devtypes[] = { |
| 126 | { "D_TAPE", D_TAPE }, | |
| 127 | { "D_DISK", D_DISK }, | |
| 128 | { "D_TTY", D_TTY }, | |
| 129 | { "D_MEM", D_MEM }, | |
| 130 | { NULL, 0 } | |
| 131 | }; | |
| cf0e588c | 132 | |
| 4cf905ba AH |
133 | int |
| 134 | syntax_error(const char *fmt, ...) | |
| 135 | { | |
| 136 | char buf[1024]; | |
| 137 | va_list ap; | |
| 138 | ||
| 139 | va_start(ap, fmt); | |
| 140 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
| 141 | va_end(ap); | |
| 142 | errx(1, "%s: syntax error on line %d: %s\n",file_stack[line_stack_depth], | |
| 143 | line_stack[line_stack_depth], buf); | |
| 144 | } | |
| cf0e588c | 145 | |
| 4cf905ba AH |
146 | static int |
| 147 | parser_include(char **tokens) | |
| 148 | { | |
| b68ba70c AH |
149 | struct stat sb; |
| 150 | int error; | |
| 151 | ||
| 152 | error = stat(tokens[1], &sb); | |
| 153 | ||
| 154 | if (error) | |
| 155 | syntax_error("could not stat %s on include, error: %s", | |
| 156 | tokens[1], strerror(errno)); | |
| 157 | ||
| 8212e700 AH |
158 | chdir(dirname(tokens[1])); |
| 159 | read_config(basename(tokens[1]), RULES_FILE); | |
| cf0e588c | 160 | |
| 4cf905ba | 161 | return 0; |
| cf0e588c MD |
162 | } |
| 163 | ||
| 4cf905ba AH |
164 | static int |
| 165 | parser_jail(char **tokens) | |
| cf0e588c | 166 | { |
| 4cf905ba AH |
167 | if (tokens[1][0] == 'y') { |
| 168 | jail = 1; | |
| 169 | } else if (tokens[1][0] == 'n') { | |
| 170 | jail = 0; | |
| cf0e588c | 171 | } else { |
| 4cf905ba | 172 | syntax_error("incorrect argument to 'jail'. Must be either y[es] or n[o]"); |
| cf0e588c MD |
173 | } |
| 174 | ||
| 4cf905ba | 175 | return 0; |
| cf0e588c MD |
176 | } |
| 177 | ||
| 4cf905ba AH |
178 | static int |
| 179 | parser_hide(char **tokens) | |
| cf0e588c | 180 | { |
| 4cf905ba AH |
181 | struct groupdevid *id; |
| 182 | struct rule *rule; | |
| cf0e588c | 183 | |
| 4cf905ba AH |
184 | id = get_id(tokens[1]); |
| 185 | rule = new_rule(rHIDE, id); | |
| 186 | add_rule(rule); | |
| cf0e588c | 187 | |
| cf0e588c MD |
188 | return 0; |
| 189 | } | |
| 190 | ||
| 4cf905ba AH |
191 | static int |
| 192 | parser_show(char **tokens) | |
| cf0e588c | 193 | { |
| 4cf905ba AH |
194 | struct groupdevid *id; |
| 195 | struct rule *rule; | |
| cf0e588c | 196 | |
| 4cf905ba AH |
197 | id = get_id(tokens[1]); |
| 198 | rule = new_rule(rSHOW, id); | |
| 199 | add_rule(rule); | |
| 908cd01a | 200 | |
| 4cf905ba AH |
201 | return 0; |
| 202 | } | |
| cf0e588c | 203 | |
| 4cf905ba AH |
204 | static int |
| 205 | parser_link(char **tokens) | |
| 206 | { | |
| 207 | struct groupdevid *id; | |
| 208 | struct rule *rule; | |
| 209 | ||
| 210 | id = get_id(tokens[1]); | |
| 211 | rule = new_rule(rLINK, id); | |
| 212 | rule->dest = strdup(tokens[2]); | |
| 213 | add_rule(rule); | |
| cf0e588c | 214 | |
| 4cf905ba | 215 | return 0; |
| cf0e588c MD |
216 | } |
| 217 | ||
| 4cf905ba AH |
218 | static int |
| 219 | parser_group(char **tokens) | |
| cf0e588c | 220 | { |
| 4cf905ba AH |
221 | struct groupdevid *gid, *id; |
| 222 | int i; | |
| 223 | size_t k; | |
| 224 | ||
| 225 | gid = get_group(tokens[1], 1); | |
| 226 | for (k = 0; gid->list[k] != NULL; k++) | |
| 227 | /* Do nothing */; | |
| 228 | for (i = 2; tokens[i] != NULL; i++) { | |
| 229 | id = get_id(tokens[i]); | |
| 230 | if (id == gid) { | |
| 231 | syntax_error("recursive group definition for group %s", gid->name); | |
| 232 | } else { | |
| 233 | if (k >= gid->listsize-1 ) { | |
| 234 | gid->list = realloc(gid->list, | |
| 235 | 2*gid->listsize*sizeof(struct groupdevid *)); | |
| 236 | gid->listsize *= 2; | |
| 38d3371e | 237 | } |
| cf0e588c | 238 | |
| 4cf905ba AH |
239 | gid->list[k++] = id; |
| 240 | } | |
| cf0e588c | 241 | } |
| 4cf905ba | 242 | gid->list[k] = NULL; |
| cf0e588c | 243 | |
| 4cf905ba | 244 | return 0; |
| cf0e588c MD |
245 | } |
| 246 | ||
| 4cf905ba AH |
247 | static int |
| 248 | parser_perm(char **tokens) | |
| cf0e588c | 249 | { |
| cf0e588c MD |
250 | struct passwd *pwd; |
| 251 | struct group *grp; | |
| 4cf905ba AH |
252 | struct groupdevid *id; |
| 253 | struct rule *rule; | |
| 254 | char *uname; | |
| 255 | char *grname; | |
| 256 | ||
| 257 | id = get_id(tokens[1]); | |
| 258 | rule = new_rule(rPERM, id); | |
| 259 | ||
| 260 | rule->mode = strtol(tokens[3], NULL, 8); | |
| 261 | uname = tokens[2]; | |
| 262 | grname = strchr(tokens[2], ':'); | |
| 263 | if (grname == NULL) | |
| 264 | syntax_error("invalid format for user/group (%s)", tokens[2]); | |
| 265 | ||
| 266 | *grname = '\0'; | |
| 267 | ++grname; | |
| 268 | if ((pwd = getpwnam(uname))) | |
| cf0e588c | 269 | rule->uid = pwd->pw_uid; |
| 4cf905ba AH |
270 | else |
| 271 | syntax_error("invalid user name %s", uname); | |
| 272 | ||
| 273 | if ((grp = getgrnam(grname))) | |
| cf0e588c | 274 | rule->gid = grp->gr_gid; |
| 4cf905ba AH |
275 | else |
| 276 | syntax_error("invalid group name %s", grname); | |
| 277 | ||
| 278 | add_rule(rule); | |
| 279 | return 0; | |
| cf0e588c MD |
280 | } |
| 281 | ||
| 4cf905ba AH |
282 | struct groupdevid * |
| 283 | new_id(const char *name, int type_in) | |
| cf0e588c | 284 | { |
| 4cf905ba AH |
285 | struct groupdevid *id; |
| 286 | int type = (type_in != 0)?(type_in):(isNAME), i; | |
| 287 | ||
| 288 | id = calloc(1, sizeof(*id)); | |
| 289 | if (id == NULL) | |
| 290 | err(1, NULL); | |
| 291 | ||
| 292 | if (type_in == 0) { | |
| 293 | for (i = 0; devtypes[i].name != NULL; i++) { | |
| 294 | if (!strcmp(devtypes[i].name, name)) { | |
| 295 | type = isTYPE; | |
| 296 | id->devtype = devtypes[i].value; | |
| 297 | break; | |
| 298 | } | |
| 299 | } | |
| 300 | } | |
| 301 | id->type = type; | |
| cf0e588c | 302 | |
| 4cf905ba AH |
303 | if ((type == isNAME) || (type == isGROUP)) { |
| 304 | id->name = strdup(name); | |
| 305 | } | |
| cf0e588c | 306 | |
| 4cf905ba AH |
307 | if (type == isGROUP) { |
| 308 | id->list = calloc(4, sizeof(struct groupdevid *)); | |
| 309 | memset(id->list, 0, 4 * sizeof(struct groupdevid *)); | |
| 310 | id->listsize = 4; | |
| 311 | } | |
| 312 | ||
| 313 | return (id); | |
| cf0e588c MD |
314 | } |
| 315 | ||
| 4cf905ba AH |
316 | struct groupdevid * |
| 317 | get_id(const char *name) | |
| cf0e588c | 318 | { |
| 4cf905ba | 319 | struct groupdevid *id; |
| cf0e588c | 320 | |
| 4cf905ba AH |
321 | if ((name[0] == '@') && (name[1] != '\0')) { |
| 322 | id = get_group(name+1, 0); | |
| 323 | if (id == NULL) | |
| 324 | syntax_error("unknown group name '%s', you " | |
| 325 | "have to use the 'group' verb first.", name+1); | |
| 326 | } | |
| 327 | else | |
| 328 | id = new_id(name, 0); | |
| cf0e588c | 329 | |
| 4cf905ba | 330 | return id; |
| cf0e588c MD |
331 | } |
| 332 | ||
| 4cf905ba AH |
333 | struct groupdevid * |
| 334 | get_group(const char *name, int expect) | |
| cf0e588c | 335 | { |
| 4cf905ba AH |
336 | struct groupdevid *g; |
| 337 | ||
| 338 | TAILQ_FOREACH(g, &group_list, link) { | |
| 339 | if (strcmp(g->name, name) == 0) | |
| 340 | return (g); | |
| 341 | } | |
| cf0e588c | 342 | |
| 4cf905ba AH |
343 | /* Caller doesn't expect to get a group no matter what */ |
| 344 | if (!expect) | |
| 345 | return NULL; | |
| cf0e588c | 346 | |
| 4cf905ba AH |
347 | g = new_id(name, isGROUP); |
| 348 | TAILQ_INSERT_TAIL(&group_list, g, link); | |
| 349 | return (g); | |
| cf0e588c MD |
350 | } |
| 351 | ||
| 4cf905ba AH |
352 | struct rule * |
| 353 | new_rule(int type, struct groupdevid *id) | |
| cf0e588c | 354 | { |
| 4cf905ba | 355 | struct rule *rule; |
| cf0e588c | 356 | |
| 4cf905ba AH |
357 | rule = calloc(1, sizeof(*rule)); |
| 358 | if (rule == NULL) | |
| 359 | err(1, NULL); | |
| cf0e588c | 360 | |
| 4cf905ba AH |
361 | rule->type = type; |
| 362 | rule->id = id; | |
| 363 | rule->jail = jail; | |
| 364 | return (rule); | |
| cf0e588c MD |
365 | } |
| 366 | ||
| 4cf905ba AH |
367 | void |
| 368 | add_rule(struct rule *rule) | |
| cf0e588c | 369 | { |
| 4cf905ba AH |
370 | TAILQ_INSERT_TAIL(&rule_list, rule, link); |
| 371 | } | |
| cf0e588c | 372 | |
| 4cf905ba AH |
373 | static int |
| 374 | dump_config_entry(struct rule *rule, struct groupdevid *id) | |
| 375 | { | |
| 376 | struct passwd *pwd; | |
| 377 | struct group *grp; | |
| 378 | int i; | |
| 379 | ||
| 380 | switch (rule->type) { | |
| 381 | case rPERM: printf("perm "); break; | |
| 382 | case rLINK: printf("link "); break; | |
| 383 | case rHIDE: printf("hide "); break; | |
| 384 | case rSHOW: printf("show "); break; | |
| 385 | default: errx(1, "invalid rule type"); | |
| 386 | } | |
| 387 | ||
| 388 | switch (id->type) { | |
| 389 | case isGROUP: printf("@"); /* FALLTHROUGH */ | |
| 390 | case isNAME: printf("%s", id->name); break; | |
| 391 | case isTYPE: | |
| 392 | for (i = 0; devtypes[i].name != NULL; i++) { | |
| 393 | if (devtypes[i].value == id->devtype) { | |
| 394 | printf("%s", devtypes[i].name); | |
| 395 | break; | |
| 396 | } | |
| 397 | } | |
| 398 | break; | |
| 399 | default: errx(1, "invalid id type %d", id->type); | |
| 908cd01a AH |
400 | } |
| 401 | ||
| 4cf905ba AH |
402 | switch (rule->type) { |
| 403 | case rPERM: | |
| 404 | pwd = getpwuid(rule->uid); | |
| 405 | grp = getgrgid(rule->gid); | |
| 406 | if (pwd && grp) { | |
| 407 | printf(" %s:%s 0%.03o", | |
| 408 | pwd->pw_name, | |
| 409 | grp->gr_name, | |
| 410 | rule->mode); | |
| 908cd01a | 411 | } else { |
| 4cf905ba AH |
412 | printf(" %d:%d 0%.03o", |
| 413 | rule->uid, | |
| 414 | rule->gid, | |
| 415 | rule->mode); | |
| 908cd01a | 416 | } |
| 4cf905ba AH |
417 | break; |
| 418 | case rLINK: | |
| 419 | printf(" %s", rule->dest); | |
| 420 | break; | |
| 421 | default: /* NOTHING */; | |
| cf0e588c MD |
422 | } |
| 423 | ||
| 4cf905ba AH |
424 | if (rule->jail) |
| 425 | printf("\t(only affects jails)"); | |
| 426 | ||
| 427 | printf("\n"); | |
| 428 | ||
| 429 | return 0; | |
| cf0e588c MD |
430 | } |
| 431 | ||
| 4cf905ba AH |
432 | static int |
| 433 | rule_id_iterate(struct groupdevid *id, struct rule *rule, | |
| 434 | rule_iterate_callback_t *callback) | |
| cf0e588c | 435 | { |
| 4cf905ba AH |
436 | int error = 0; |
| 437 | int i; | |
| 38d3371e | 438 | |
| 4cf905ba AH |
439 | if (id->type == isGROUP) { |
| 440 | for (i = 0; id->list[i] != NULL; i++) { | |
| 441 | if ((error = rule_id_iterate(id->list[i], rule, callback))) | |
| 442 | return error; | |
| 443 | } | |
| 444 | } else { | |
| 445 | error = callback(rule, id); | |
| 446 | } | |
| 38d3371e | 447 | |
| 4cf905ba AH |
448 | return error; |
| 449 | } | |
| 450 | ||
| 451 | void | |
| 452 | dump_config(void) | |
| 453 | { | |
| 454 | struct rule *rule; | |
| 38d3371e | 455 | |
| 4cf905ba AH |
456 | TAILQ_FOREACH(rule, &rule_list, link) { |
| 457 | rule_id_iterate(rule->id, rule, dump_config_entry); | |
| 458 | } | |
| cf0e588c MD |
459 | } |
| 460 | ||
| 4cf905ba | 461 | static int |
| 5cb6a05c | 462 | rule_ioctl(unsigned long cmd, struct devfs_rule_ioctl *rule) |
| cf0e588c | 463 | { |
| 4cf905ba AH |
464 | if (ioctl(dev_fd, cmd, rule) == -1) |
| 465 | err(1, "ioctl"); | |
| cf0e588c MD |
466 | |
| 467 | return 0; | |
| 468 | } | |
| 469 | ||
| 4cf905ba | 470 | static void |
| 5cb6a05c | 471 | rule_fill(struct devfs_rule_ioctl *dr, struct rule *r, struct groupdevid *id) |
| cf0e588c | 472 | { |
| 4cf905ba | 473 | dr->rule_type = 0; |
| 8212e700 | 474 | dr->rule_cmd = 0; |
| 4cf905ba AH |
475 | |
| 476 | switch (id->type) { | |
| 477 | default: | |
| 478 | errx(1, "invalid id type"); | |
| 479 | case isGROUP: | |
| 480 | errx(1, "internal error: can not fill group rule"); | |
| 481 | /* NOTREACHED */ | |
| 482 | case isNAME: | |
| 483 | dr->rule_type |= DEVFS_RULE_NAME; | |
| 5cb6a05c | 484 | strncpy(dr->name, id->name, PATH_MAX-1); |
| 4cf905ba AH |
485 | break; |
| 486 | case isTYPE: | |
| 487 | dr->rule_type |= DEVFS_RULE_TYPE; | |
| 488 | dr->dev_type = id->devtype; | |
| 489 | break; | |
| 490 | } | |
| 491 | ||
| 492 | switch (r->type) { | |
| 493 | case rPERM: | |
| 5cb6a05c | 494 | dr->rule_cmd |= DEVFS_RULE_PERM; |
| 4cf905ba AH |
495 | dr->uid = r->uid; |
| 496 | dr->gid = r->gid; | |
| 497 | dr->mode = r->mode; | |
| 498 | break; | |
| 499 | case rLINK: | |
| 5cb6a05c AH |
500 | dr->rule_cmd |= DEVFS_RULE_LINK; |
| 501 | strncpy(dr->linkname, r->dest, PATH_MAX-1); | |
| 4cf905ba AH |
502 | break; |
| 503 | case rHIDE: | |
| 5cb6a05c | 504 | dr->rule_cmd |= DEVFS_RULE_HIDE; |
| 4cf905ba AH |
505 | break; |
| 506 | case rSHOW: | |
| 5cb6a05c | 507 | dr->rule_cmd |= DEVFS_RULE_SHOW; |
| 4cf905ba AH |
508 | break; |
| 509 | } | |
| 510 | ||
| 511 | if (r->jail) | |
| 512 | dr->rule_type |= DEVFS_RULE_JAIL; | |
| 513 | } | |
| 514 | ||
| 515 | static int | |
| 516 | rule_send(struct rule *rule, struct groupdevid *id) | |
| 517 | { | |
| 5cb6a05c | 518 | struct devfs_rule_ioctl dr; |
| 4cf905ba AH |
519 | int r = 0; |
| 520 | ||
| 5cb6a05c | 521 | strncpy(dr.mntpoint, mountp, PATH_MAX-1); |
| 4cf905ba AH |
522 | |
| 523 | rule_fill(&dr, rule, id); | |
| 524 | r = rule_ioctl(DEVFS_RULE_ADD, &dr); | |
| 525 | ||
| 526 | return r; | |
| cf0e588c MD |
527 | } |
| 528 | ||
| 529 | int | |
| 4cf905ba | 530 | rule_apply(void) |
| cf0e588c | 531 | { |
| 5cb6a05c | 532 | struct devfs_rule_ioctl dr; |
| 4cf905ba AH |
533 | struct rule *rule; |
| 534 | int r = 0; | |
| 535 | ||
| 5cb6a05c | 536 | strncpy(dr.mntpoint, mountp, PATH_MAX-1); |
| 4cf905ba AH |
537 | |
| 538 | TAILQ_FOREACH(rule, &rule_list, link) { | |
| 539 | r = rule_id_iterate(rule->id, rule, rule_send); | |
| 540 | if (r != 0) | |
| 541 | return (-1); | |
| cf0e588c MD |
542 | } |
| 543 | ||
| 4cf905ba AH |
544 | return (rule_ioctl(DEVFS_RULE_APPLY, &dr)); |
| 545 | } | |
| 546 | ||
| 547 | static int | |
| 548 | rule_check_num_args(char **tokens, int num) | |
| 549 | { | |
| 6d2f0a97 SW |
550 | int i; |
| 551 | ||
| 552 | for (i = 0; tokens[i] != NULL; i++) | |
| 553 | ; | |
| 4cf905ba AH |
554 | |
| 555 | if (i < num) { | |
| 556 | syntax_error("at least %d tokens were expected but only %d were found", num, i); | |
| 557 | return 1; | |
| 558 | } | |
| cf0e588c MD |
559 | return 0; |
| 560 | } | |
| 561 | ||
| 562 | int | |
| b68ba70c | 563 | read_config(const char *name, int ftype) |
| cf0e588c MD |
564 | { |
| 565 | FILE *fd; | |
| 8212e700 | 566 | struct stat sb; |
| 4cf905ba | 567 | |
| cf0e588c MD |
568 | if ((fd = fopen(name, "r")) == NULL) { |
| 569 | printf("Error opening config file %s\n", name); | |
| 570 | perror("fopen"); | |
| 571 | return 1; | |
| 572 | } | |
| 573 | ||
| 8212e700 AH |
574 | if (fstat(fileno(fd), &sb) != 0) { |
| 575 | errx(1, "file %s could not be fstat'ed, aborting", name); | |
| 576 | } | |
| 577 | ||
| 578 | if (sb.st_uid != 0) | |
| 579 | errx(1, "file %s does not belong to root, aborting!", name); | |
| 580 | ||
| 4cf905ba AH |
581 | if (++line_stack_depth >= RULE_MAX_STACK) { |
| 582 | --line_stack_depth; | |
| 583 | syntax_error("Maximum include depth (%d) exceeded, " | |
| 584 | "check for recursion.", RULE_MAX_STACK); | |
| 585 | } | |
| 586 | ||
| 587 | line_stack[line_stack_depth] = 1; | |
| 588 | file_stack[line_stack_depth] = strdup(name); | |
| 8212e700 | 589 | cwd_stack[line_stack_depth] = getwd(NULL); |
| 4cf905ba | 590 | |
| b68ba70c | 591 | while (process_line(fd, ftype) == 0) |
| 4cf905ba | 592 | line_stack[line_stack_depth]++; |
| cf0e588c MD |
593 | |
| 594 | fclose(fd); | |
| 595 | ||
| 4cf905ba | 596 | free(file_stack[line_stack_depth]); |
| 8212e700 | 597 | free(cwd_stack[line_stack_depth]); |
| 4cf905ba | 598 | --line_stack_depth; |
| 8212e700 | 599 | chdir(cwd_stack[line_stack_depth]); |
| 4cf905ba | 600 | |
| cf0e588c MD |
601 | return 0; |
| 602 | } | |
| 603 | ||
| 4cf905ba | 604 | static int |
| b68ba70c | 605 | process_line(FILE* fd, int ftype) |
| cf0e588c MD |
606 | { |
| 607 | char buffer[4096]; | |
| 608 | char *tokens[256]; | |
| 609 | int c, n, i = 0; | |
| 4cf905ba | 610 | int quote = 0; |
| cf0e588c | 611 | int ret = 0; |
| cf0e588c MD |
612 | |
| 613 | while (((c = fgetc(fd)) != EOF) && (c != '\n')) { | |
| 614 | buffer[i++] = (char)c; | |
| 615 | if (i == (sizeof(buffer) -1)) | |
| 616 | break; | |
| 617 | } | |
| 618 | buffer[i] = '\0'; | |
| 4cf905ba | 619 | |
| cf0e588c MD |
620 | if (feof(fd) || ferror(fd)) |
| 621 | ret = 1; | |
| 622 | c = 0; | |
| 623 | while (((buffer[c] == ' ') || (buffer[c] == '\t')) && (c < i)) c++; | |
| 4cf905ba AH |
624 | /* |
| 625 | * If this line effectively (after indentation) begins with the comment | |
| 626 | * character #, we ignore the rest of the line. | |
| 627 | */ | |
| 628 | if (buffer[c] == '#') | |
| 629 | return 0; | |
| 630 | ||
| cf0e588c | 631 | tokens[0] = &buffer[c]; |
| 908cd01a | 632 | for (n = 1; c < i; c++) { |
| 4cf905ba AH |
633 | if (buffer[c] == '"') { |
| 634 | quote = !quote; | |
| 635 | if (quote) { | |
| 636 | if ((c >= 1) && (&buffer[c] != tokens[n-1])) { | |
| 637 | syntax_error("stray opening quote not at beginning of token"); | |
| 638 | /* NOTREACHED */ | |
| 639 | } | |
| 640 | tokens[n-1] = &buffer[c+1]; | |
| 641 | } else { | |
| 642 | if ((c < i-1) && (!iswhitespace(buffer[c+1]))) { | |
| 643 | syntax_error("stray closing quote not at end of token"); | |
| 644 | /* NOTREACHED */ | |
| 645 | } | |
| 646 | buffer[c] = '\0'; | |
| 647 | } | |
| 648 | } | |
| 649 | ||
| 650 | if (quote) { | |
| 651 | continue; | |
| 652 | } | |
| 653 | ||
| cf0e588c MD |
654 | if ((buffer[c] == ' ') || (buffer[c] == '\t')) { |
| 655 | buffer[c++] = '\0'; | |
| 4cf905ba AH |
656 | while ((iswhitespace(buffer[c])) && (c < i)) c++; |
| 657 | tokens[n++] = &buffer[c--]; | |
| cf0e588c MD |
658 | } |
| 659 | } | |
| 660 | tokens[n] = NULL; | |
| 661 | ||
| 908cd01a AH |
662 | /* |
| 663 | * If there are not enough arguments for any function or it is | |
| 4cf905ba AH |
664 | * a line full of whitespaces, we just return here. Or if a |
| 665 | * quote wasn't closed. | |
| 908cd01a | 666 | */ |
| 4cf905ba | 667 | if ((quote) || (n < 2) || (tokens[0][0] == '\0')) |
| cf0e588c MD |
668 | return ret; |
| 669 | ||
| b68ba70c AH |
670 | switch (ftype) { |
| 671 | case RULES_FILE: | |
| 672 | ret = rule_parser(tokens); | |
| 673 | break; | |
| 8212e700 | 674 | #if 0 |
| b68ba70c AH |
675 | case RULETAB_FILE: |
| 676 | ret = ruletab_parser(tokens); | |
| 677 | break; | |
| 8212e700 | 678 | #endif |
| b68ba70c AH |
679 | default: |
| 680 | ret = 1; | |
| 681 | } | |
| 682 | ||
| 683 | return ret; | |
| 684 | } | |
| 685 | ||
| 686 | static int | |
| 687 | rule_parser(char **tokens) | |
| 688 | { | |
| 6d2f0a97 | 689 | int i; |
| b68ba70c AH |
690 | int parsed = 0; |
| 691 | ||
| 4cf905ba AH |
692 | /* Convert the command/verb to lowercase */ |
| 693 | for (i = 0; tokens[0][i] != '\0'; i++) | |
| 694 | tokens[0][i] = tolower(tokens[0][i]); | |
| cf0e588c | 695 | |
| 4cf905ba | 696 | for (i = 0; parsers[i].verb; i++) { |
| 6d2f0a97 | 697 | if (rule_check_num_args(tokens, parsers[i].min_args) != 0) |
| 4cf905ba AH |
698 | continue; |
| 699 | ||
| 700 | if (!strcmp(tokens[0], parsers[i].verb)) { | |
| 701 | parsers[i].parser(tokens); | |
| 702 | parsed = 1; | |
| 703 | break; | |
| 704 | } | |
| 705 | } | |
| 706 | if (parsed == 0) { | |
| 707 | syntax_error("unknown verb/command %s", tokens[0]); | |
| cf0e588c | 708 | } |
| b68ba70c AH |
709 | |
| 710 | return 0; | |
| 711 | } | |
| 712 | ||
| 8212e700 | 713 | #if 0 |
| b68ba70c AH |
714 | static int |
| 715 | ruletab_parser(char **tokens) | |
| 716 | { | |
| 717 | struct rule_tab *rt; | |
| 718 | struct stat sb; | |
| 6d2f0a97 SW |
719 | int i; |
| 720 | int error; | |
| b68ba70c | 721 | |
| 6d2f0a97 | 722 | if (rule_check_num_args(tokens, 2) != 0) |
| b68ba70c AH |
723 | return 0; |
| 724 | ||
| 725 | error = stat(tokens[0], &sb); | |
| 726 | if (error) { | |
| 727 | printf("ruletab warning: could not stat %s: %s\n", | |
| 728 | tokens[0], strerror(errno)); | |
| 729 | } | |
| 730 | ||
| 731 | if (tokens[0][0] != '/') { | |
| 732 | errx(1, "ruletab error: entry %s does not seem to be an absolute path", | |
| 733 | tokens[0]); | |
| 734 | } | |
| 735 | ||
| 736 | for (i = 1; tokens[i] != NULL; i++) { | |
| 737 | rt = calloc(1, sizeof(struct rule_tab)); | |
| 738 | rt->mntpoint = strdup(tokens[0]); | |
| 739 | rt->rule_file = strdup(tokens[i]); | |
| 740 | TAILQ_INSERT_TAIL(&rule_tab_list, rt, link); | |
| 741 | } | |
| 742 | ||
| 743 | return 0; | |
| 744 | } | |
| 745 | ||
| 746 | void | |
| 6d2f0a97 SW |
747 | rule_tab(void) |
| 748 | { | |
| b68ba70c AH |
749 | struct rule_tab *rt; |
| 750 | int error; | |
| 751 | int mode; | |
| b68ba70c AH |
752 | |
| 753 | chdir("/etc/devfs"); | |
| 754 | error = read_config("ruletab", RULETAB_FILE); | |
| 755 | ||
| 756 | if (error) | |
| 757 | errx(1, "could not read/process ruletab file (/etc/devfs/ruletab)"); | |
| 758 | ||
| 759 | if (!strcmp(mountp, "*")) { | |
| 760 | mode = RULETAB_ALL; | |
| 761 | } else if (!strcmp(mountp, "boot")) { | |
| 762 | mode = RULETAB_ONLY_BOOT; | |
| 763 | } else if (mountp) { | |
| 764 | mode = RULETAB_SPECIFIC; | |
| 765 | } else { | |
| 766 | errx(1, "-t needs -m"); | |
| 767 | } | |
| 768 | ||
| 769 | dev_fd = open("/dev/devfs", O_RDWR); | |
| 770 | if (dev_fd == -1) | |
| 771 | err(1, "open(/dev/devfs)"); | |
| 772 | ||
| 773 | TAILQ_FOREACH(rt, &rule_tab_list, link) { | |
| 774 | switch(mode) { | |
| 775 | case RULETAB_ONLY_BOOT: | |
| 776 | if ((strcmp(rt->mntpoint, "*") != 0) && | |
| 777 | (strcmp(rt->mntpoint, "/dev") != 0)) { | |
| 778 | continue; | |
| 779 | } | |
| 780 | break; | |
| 781 | case RULETAB_SPECIFIC: | |
| 782 | if (strcmp(rt->mntpoint, mountp) != 0) | |
| 783 | continue; | |
| 784 | break; | |
| 785 | } | |
| 786 | delete_rules(); | |
| 787 | read_config(rt->rule_file, RULES_FILE); | |
| 788 | mountp = rt->mntpoint; | |
| 789 | rule_apply(); | |
| 790 | } | |
| 791 | ||
| 792 | close(dev_fd); | |
| 793 | ||
| 794 | return; | |
| 795 | } | |
| 796 | ||
| 797 | void | |
| 798 | delete_rules(void) | |
| 799 | { | |
| 800 | struct rule *rp; | |
| 801 | struct groupdevid *gdp; | |
| 802 | ||
| 803 | TAILQ_FOREACH(rp, &rule_list, link) { | |
| 804 | TAILQ_REMOVE(&rule_list, rp, link); | |
| 805 | } | |
| 806 | ||
| 807 | TAILQ_FOREACH(gdp, &group_list, link) { | |
| 808 | TAILQ_REMOVE(&group_list, gdp, link); | |
| 809 | } | |
| cf0e588c | 810 | } |
| 8212e700 | 811 | #endif |
| cf0e588c | 812 | |
| 4cf905ba | 813 | static void |
| cf0e588c MD |
814 | usage(void) |
| 815 | { | |
| 4cf905ba AH |
816 | fprintf(stderr, |
| 817 | "Usage: devfsctl <commands> [options]\n" | |
| 818 | "Valid commands are:\n" | |
| 819 | " -a\n" | |
| 820 | "\t Loads all read rules into the kernel and applies them\n" | |
| 821 | " -c\n" | |
| 822 | "\t Clears all rules stored in the kernel but does not reset the nodes\n" | |
| 823 | " -d\n" | |
| 824 | "\t Dumps the rules that have been loaded to the screen to verify syntax\n" | |
| 825 | " -r\n" | |
| 826 | "\t Resets all devfs_nodes but does not clear the rules stored\n" | |
| 827 | "\n" | |
| 828 | "Valid options and its arguments are:\n" | |
| 829 | " -f <config_file>\n" | |
| 830 | "\t Specifies the configuration file to be used\n" | |
| 831 | " -m <mount_point>\n" | |
| 832 | "\t Specifies a mount point to which the command will apply. Defaults to *\n" | |
| 833 | ); | |
| 834 | ||
| 835 | exit(1); | |
| cf0e588c MD |
836 | } |
| 837 | ||
| cf0e588c MD |
838 | int main(int argc, char *argv[]) |
| 839 | { | |
| 5cb6a05c | 840 | struct devfs_rule_ioctl dummy_rule; |
| 8212e700 AH |
841 | struct stat sb; |
| 842 | int ch, error; | |
| 4cf905ba | 843 | |
| 8212e700 | 844 | while ((ch = getopt(argc, argv, "acdf:hm:r")) != -1) { |
| 4cf905ba | 845 | switch (ch) { |
| cf0e588c | 846 | case 'f': |
| 4cf905ba | 847 | config_name = optarg; |
| cf0e588c MD |
848 | break; |
| 849 | case 'm': | |
| 4cf905ba | 850 | mountp = optarg; |
| cf0e588c MD |
851 | break; |
| 852 | case 'a': | |
| 853 | aflag = 1; | |
| 854 | break; | |
| 855 | case 'c': | |
| 856 | cflag = 1; | |
| 857 | break; | |
| 858 | case 'r': | |
| 859 | rflag = 1; | |
| 860 | break; | |
| 861 | case 'd': | |
| 862 | dflag = 1; | |
| 863 | break; | |
| 8212e700 | 864 | |
| a0d68d0c | 865 | case 'h': |
| 4cf905ba AH |
866 | case '?': |
| 867 | default: | |
| 868 | usage(); | |
| 869 | /* NOT REACHED */ | |
| 870 | } | |
| cf0e588c MD |
871 | } |
| 872 | ||
| 4cf905ba AH |
873 | argc -= optind; |
| 874 | argv += optind; | |
| cf0e588c | 875 | |
| 4cf905ba AH |
876 | /* |
| 877 | * Check arguments: | |
| 878 | * - need to use at least one mode | |
| 879 | * - can not use -d with any other mode | |
| b68ba70c | 880 | * - can not use -t with any other mode or -f |
| 4cf905ba | 881 | */ |
| 8212e700 AH |
882 | if (!(aflag || rflag || cflag || dflag) || |
| 883 | (dflag && (aflag || rflag || cflag || tflag))) { | |
| 4cf905ba AH |
884 | usage(); |
| 885 | /* NOT REACHED */ | |
| cf0e588c MD |
886 | } |
| 887 | ||
| 4cf905ba AH |
888 | if (mountp == NULL) |
| 889 | mountp = "*"; | |
| b68ba70c AH |
890 | else if (mountp[0] != '/') { |
| 891 | errx(1, "-m needs to be given an absolute path"); | |
| 892 | } | |
| 4cf905ba | 893 | |
| 5cb6a05c | 894 | strncpy(dummy_rule.mntpoint, mountp, PATH_MAX-1); |
| 4cf905ba | 895 | |
| 8212e700 AH |
896 | if (config_name != NULL) { |
| 897 | error = stat(config_name, &sb); | |
| 898 | ||
| 899 | if (error) { | |
| 900 | chdir("/etc/devfs"); | |
| 901 | error = stat(config_name, &sb); | |
| 902 | } | |
| 903 | ||
| 904 | if (error) | |
| 905 | err(1, "could not stat specified configuration file %s", config_name); | |
| 906 | ||
| 907 | if (config_name[0] == '/') | |
| 908 | chdir(dirname(config_name)); | |
| 909 | ||
| b68ba70c | 910 | read_config(config_name, RULES_FILE); |
| 8212e700 | 911 | } |
| 4cf905ba | 912 | |
| cf0e588c MD |
913 | if (dflag) { |
| 914 | dump_config(); | |
| 4cf905ba | 915 | exit(0); |
| cf0e588c MD |
916 | } |
| 917 | ||
| 4cf905ba AH |
918 | dev_fd = open("/dev/devfs", O_RDWR); |
| 919 | if (dev_fd == -1) | |
| 920 | err(1, "open(/dev/devfs)"); | |
| cf0e588c | 921 | |
| 4cf905ba AH |
922 | if (cflag) |
| 923 | rule_ioctl(DEVFS_RULE_CLEAR, &dummy_rule); | |
| cf0e588c | 924 | |
| 4cf905ba AH |
925 | if (rflag) |
| 926 | rule_ioctl(DEVFS_RULE_RESET, &dummy_rule); | |
| cf0e588c | 927 | |
| 4cf905ba AH |
928 | if (aflag) |
| 929 | rule_apply(); | |
| 930 | ||
| 931 | close(dev_fd); | |
| cf0e588c MD |
932 | |
| 933 | return 0; | |
| 934 | } |