1 /*-
2 * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by Network Associates
6 * Laboratories, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA 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 #include <sys/param.h>
34 #include <sys/errno.h>
35 #include <sys/time.h>
36 #include <sys/sysctl.h>
37 #include <sys/ucred.h>
38 #include <sys/mount.h>
39
40 #include <security/mac_bsdextended/mac_bsdextended.h>
41
42 #include <grp.h>
43 #include <pwd.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include "ugidfw.h"
49
50 /*
51 * Text format for rules: rules contain subject and object elements, mode.
52 * The total form is "subject [s_element] object [o_element] mode [mode]".
53 * At least * one of a uid or gid entry must be present; both may also be
54 * present.
55 */
56
57 #define MIB "security.mac.bsdextended"
58
59 int
bsde_rule_to_string(struct mac_bsdextended_rule * rule,char * buf,size_t buflen)60 bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen)
61 {
62 struct group *grp;
63 struct passwd *pwd;
64 struct statfs *mntbuf;
65 char *cur, type[sizeof(rule->mbr_object.mbo_type) * CHAR_BIT + 1];
66 size_t left, len;
67 int anymode, unknownmode, truncated, numfs, i, notdone;
68
69 cur = buf;
70 left = buflen;
71 truncated = 0;
72
73 len = snprintf(cur, left, "subject ");
74 if (len < 0 || len > left)
75 goto truncated;
76 left -= len;
77 cur += len;
78 if (rule->mbr_subject.mbs_flags) {
79 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) {
80 len = snprintf(cur, left, "not ");
81 if (len < 0 || len > left)
82 goto truncated;
83 left -= len;
84 cur += len;
85 notdone = 1;
86 } else {
87 notdone = 0;
88 }
89
90 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) {
91 len = snprintf(cur, left, "! ");
92 if (len < 0 || len > left)
93 goto truncated;
94 left -= len;
95 cur += len;
96 }
97 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) {
98 pwd = getpwuid(rule->mbr_subject.mbs_uid_min);
99 if (pwd != NULL) {
100 len = snprintf(cur, left, "uid %s",
101 pwd->pw_name);
102 if (len < 0 || len > left)
103 goto truncated;
104 left -= len;
105 cur += len;
106 } else {
107 len = snprintf(cur, left, "uid %u",
108 rule->mbr_subject.mbs_uid_min);
109 if (len < 0 || len > left)
110 goto truncated;
111 left -= len;
112 cur += len;
113 }
114 if (rule->mbr_subject.mbs_uid_min !=
115 rule->mbr_subject.mbs_uid_max) {
116 pwd = getpwuid(rule->mbr_subject.mbs_uid_max);
117 if (pwd != NULL) {
118 len = snprintf(cur, left, ":%s ",
119 pwd->pw_name);
120 if (len < 0 || len > left)
121 goto truncated;
122 left -= len;
123 cur += len;
124 } else {
125 len = snprintf(cur, left, ":%u ",
126 rule->mbr_subject.mbs_uid_max);
127 if (len < 0 || len > left)
128 goto truncated;
129 left -= len;
130 cur += len;
131 }
132 } else {
133 len = snprintf(cur, left, " ");
134 if (len < 0 || len > left)
135 goto truncated;
136 left -= len;
137 cur += len;
138 }
139 }
140 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) {
141 len = snprintf(cur, left, "! ");
142 if (len < 0 || len > left)
143 goto truncated;
144 left -= len;
145 cur += len;
146 }
147 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) {
148 grp = getgrgid(rule->mbr_subject.mbs_gid_min);
149 if (grp != NULL) {
150 len = snprintf(cur, left, "gid %s",
151 grp->gr_name);
152 if (len < 0 || len > left)
153 goto truncated;
154 left -= len;
155 cur += len;
156 } else {
157 len = snprintf(cur, left, "gid %u",
158 rule->mbr_subject.mbs_gid_min);
159 if (len < 0 || len > left)
160 goto truncated;
161 left -= len;
162 cur += len;
163 }
164 if (rule->mbr_subject.mbs_gid_min !=
165 rule->mbr_subject.mbs_gid_max) {
166 grp = getgrgid(rule->mbr_subject.mbs_gid_max);
167 if (grp != NULL) {
168 len = snprintf(cur, left, ":%s ",
169 grp->gr_name);
170 if (len < 0 || len > left)
171 goto truncated;
172 left -= len;
173 cur += len;
174 } else {
175 len = snprintf(cur, left, ":%u ",
176 rule->mbr_subject.mbs_gid_max);
177 if (len < 0 || len > left)
178 goto truncated;
179 left -= len;
180 cur += len;
181 }
182 } else {
183 len = snprintf(cur, left, " ");
184 if (len < 0 || len > left)
185 goto truncated;
186 left -= len;
187 cur += len;
188 }
189 }
190 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) {
191 len = snprintf(cur, left, "! ");
192 if (len < 0 || len > left)
193 goto truncated;
194 left -= len;
195 cur += len;
196 }
197 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
198 len = snprintf(cur, left, "jailid %d ",
199 rule->mbr_subject.mbs_prison);
200 if (len < 0 || len > left)
201 goto truncated;
202 left -= len;
203 cur += len;
204 }
205 }
206
207 len = snprintf(cur, left, "object ");
208 if (len < 0 || len > left)
209 goto truncated;
210 left -= len;
211 cur += len;
212 if (rule->mbr_object.mbo_flags) {
213 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) {
214 len = snprintf(cur, left, "not ");
215 if (len < 0 || len > left)
216 goto truncated;
217 left -= len;
218 cur += len;
219 notdone = 1;
220 } else {
221 notdone = 0;
222 }
223
224 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) {
225 len = snprintf(cur, left, "! ");
226 if (len < 0 || len > left)
227 goto truncated;
228 left -= len;
229 cur += len;
230 }
231 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
232 pwd = getpwuid(rule->mbr_object.mbo_uid_min);
233 if (pwd != NULL) {
234 len = snprintf(cur, left, "uid %s",
235 pwd->pw_name);
236 if (len < 0 || len > left)
237 goto truncated;
238 left -= len;
239 cur += len;
240 } else {
241 len = snprintf(cur, left, "uid %u",
242 rule->mbr_object.mbo_uid_min);
243 if (len < 0 || len > left)
244 goto truncated;
245 left -= len;
246 cur += len;
247 }
248 if (rule->mbr_object.mbo_uid_min !=
249 rule->mbr_object.mbo_uid_max) {
250 pwd = getpwuid(rule->mbr_object.mbo_uid_max);
251 if (pwd != NULL) {
252 len = snprintf(cur, left, ":%s ",
253 pwd->pw_name);
254 if (len < 0 || len > left)
255 goto truncated;
256 left -= len;
257 cur += len;
258 } else {
259 len = snprintf(cur, left, ":%u ",
260 rule->mbr_object.mbo_uid_max);
261 if (len < 0 || len > left)
262 goto truncated;
263 left -= len;
264 cur += len;
265 }
266 } else {
267 len = snprintf(cur, left, " ");
268 if (len < 0 || len > left)
269 goto truncated;
270 left -= len;
271 cur += len;
272 }
273 }
274 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) {
275 len = snprintf(cur, left, "! ");
276 if (len < 0 || len > left)
277 goto truncated;
278 left -= len;
279 cur += len;
280 }
281 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
282 grp = getgrgid(rule->mbr_object.mbo_gid_min);
283 if (grp != NULL) {
284 len = snprintf(cur, left, "gid %s",
285 grp->gr_name);
286 if (len < 0 || len > left)
287 goto truncated;
288 left -= len;
289 cur += len;
290 } else {
291 len = snprintf(cur, left, "gid %u",
292 rule->mbr_object.mbo_gid_min);
293 if (len < 0 || len > left)
294 goto truncated;
295 left -= len;
296 cur += len;
297 }
298 if (rule->mbr_object.mbo_gid_min !=
299 rule->mbr_object.mbo_gid_max) {
300 grp = getgrgid(rule->mbr_object.mbo_gid_max);
301 if (grp != NULL) {
302 len = snprintf(cur, left, ":%s ",
303 grp->gr_name);
304 if (len < 0 || len > left)
305 goto truncated;
306 left -= len;
307 cur += len;
308 } else {
309 len = snprintf(cur, left, ":%u ",
310 rule->mbr_object.mbo_gid_max);
311 if (len < 0 || len > left)
312 goto truncated;
313 left -= len;
314 cur += len;
315 }
316 } else {
317 len = snprintf(cur, left, " ");
318 if (len < 0 || len > left)
319 goto truncated;
320 left -= len;
321 cur += len;
322 }
323 }
324 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) {
325 len = snprintf(cur, left, "! ");
326 if (len < 0 || len > left)
327 goto truncated;
328 left -= len;
329 cur += len;
330 }
331 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
332 numfs = getmntinfo(&mntbuf, MNT_NOWAIT);
333 for (i = 0; i < numfs; i++)
334 if (memcmp(&(rule->mbr_object.mbo_fsid),
335 &(mntbuf[i].f_fsid),
336 sizeof(mntbuf[i].f_fsid)) == 0)
337 break;
338 len = snprintf(cur, left, "filesys %s ",
339 i == numfs ? "???" : mntbuf[i].f_mntonname);
340 if (len < 0 || len > left)
341 goto truncated;
342 left -= len;
343 cur += len;
344 }
345 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
346 len = snprintf(cur, left, "! ");
347 if (len < 0 || len > left)
348 goto truncated;
349 left -= len;
350 cur += len;
351 }
352 if (rule->mbr_object.mbo_flags & MBO_SUID) {
353 len = snprintf(cur, left, "suid ");
354 if (len < 0 || len > left)
355 goto truncated;
356 left -= len;
357 cur += len;
358 }
359 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) {
360 len = snprintf(cur, left, "! ");
361 if (len < 0 || len > left)
362 goto truncated;
363 left -= len;
364 cur += len;
365 }
366 if (rule->mbr_object.mbo_flags & MBO_SGID) {
367 len = snprintf(cur, left, "sgid ");
368 if (len < 0 || len > left)
369 goto truncated;
370 left -= len;
371 cur += len;
372 }
373 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) {
374 len = snprintf(cur, left, "! ");
375 if (len < 0 || len > left)
376 goto truncated;
377 left -= len;
378 cur += len;
379 }
380 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
381 len = snprintf(cur, left, "uid_of_subject ");
382 if (len < 0 || len > left)
383 goto truncated;
384 left -= len;
385 cur += len;
386 }
387 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) {
388 len = snprintf(cur, left, "! ");
389 if (len < 0 || len > left)
390 goto truncated;
391 left -= len;
392 cur += len;
393 }
394 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
395 len = snprintf(cur, left, "gid_of_subject ");
396 if (len < 0 || len > left)
397 goto truncated;
398 left -= len;
399 cur += len;
400 }
401 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) {
402 len = snprintf(cur, left, "! ");
403 if (len < 0 || len > left)
404 goto truncated;
405 left -= len;
406 cur += len;
407 }
408 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
409 i = 0;
410 if (rule->mbr_object.mbo_type & MBO_TYPE_REG)
411 type[i++] = 'r';
412 if (rule->mbr_object.mbo_type & MBO_TYPE_DIR)
413 type[i++] = 'd';
414 if (rule->mbr_object.mbo_type & MBO_TYPE_BLK)
415 type[i++] = 'b';
416 if (rule->mbr_object.mbo_type & MBO_TYPE_CHR)
417 type[i++] = 'c';
418 if (rule->mbr_object.mbo_type & MBO_TYPE_LNK)
419 type[i++] = 'l';
420 if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK)
421 type[i++] = 's';
422 if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO)
423 type[i++] = 'p';
424 if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) {
425 i = 0;
426 type[i++] = 'a';
427 }
428 type[i++] = '\0';
429 len = snprintf(cur, left, "type %s ", type);
430 if (len < 0 || len > left)
431 goto truncated;
432 left -= len;
433 cur += len;
434 }
435 }
436
437 len = snprintf(cur, left, "mode ");
438 if (len < 0 || len > left)
439 goto truncated;
440 left -= len;
441 cur += len;
442
443 anymode = (rule->mbr_mode & MBI_ALLPERM);
444 unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
445
446 if (rule->mbr_mode & MBI_ADMIN) {
447 len = snprintf(cur, left, "a");
448 if (len < 0 || len > left)
449 goto truncated;
450
451 left -= len;
452 cur += len;
453 }
454 if (rule->mbr_mode & MBI_READ) {
455 len = snprintf(cur, left, "r");
456 if (len < 0 || len > left)
457 goto truncated;
458
459 left -= len;
460 cur += len;
461 }
462 if (rule->mbr_mode & MBI_STAT) {
463 len = snprintf(cur, left, "s");
464 if (len < 0 || len > left)
465 goto truncated;
466
467 left -= len;
468 cur += len;
469 }
470 if (rule->mbr_mode & MBI_WRITE) {
471 len = snprintf(cur, left, "w");
472 if (len < 0 || len > left)
473 goto truncated;
474
475 left -= len;
476 cur += len;
477 }
478 if (rule->mbr_mode & MBI_EXEC) {
479 len = snprintf(cur, left, "x");
480 if (len < 0 || len > left)
481 goto truncated;
482
483 left -= len;
484 cur += len;
485 }
486 if (!anymode) {
487 len = snprintf(cur, left, "n");
488 if (len < 0 || len > left)
489 goto truncated;
490
491 left -= len;
492 cur += len;
493 }
494 if (unknownmode) {
495 len = snprintf(cur, left, "?");
496 if (len < 0 || len > left)
497 goto truncated;
498
499 left -= len;
500 cur += len;
501 }
502
503 return (0);
504
505 truncated:
506 return (-1);
507 }
508
509 int
bsde_parse_uidrange(char * spec,uid_t * min,uid_t * max,size_t buflen,char * errstr)510 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max,
511 size_t buflen, char *errstr){
512 struct passwd *pwd;
513 uid_t uid1, uid2;
514 char *spec1, *spec2, *endp;
515 unsigned long value;
516 size_t len;
517
518 spec2 = spec;
519 spec1 = strsep(&spec2, ":");
520
521 pwd = getpwnam(spec1);
522 if (pwd != NULL)
523 uid1 = pwd->pw_uid;
524 else {
525 value = strtoul(spec1, &endp, 10);
526 if (*endp != '\0') {
527 len = snprintf(errstr, buflen,
528 "invalid uid: '%s'", spec1);
529 return (-1);
530 }
531 uid1 = value;
532 }
533
534 if (spec2 == NULL) {
535 *max = *min = uid1;
536 return (0);
537 }
538
539 pwd = getpwnam(spec2);
540 if (pwd != NULL)
541 uid2 = pwd->pw_uid;
542 else {
543 value = strtoul(spec2, &endp, 10);
544 if (*endp != '\0') {
545 len = snprintf(errstr, buflen,
546 "invalid uid: '%s'", spec2);
547 return (-1);
548 }
549 uid2 = value;
550 }
551
552 *min = uid1;
553 *max = uid2;
554
555 return (0);
556 }
557
558 int
bsde_parse_gidrange(char * spec,gid_t * min,gid_t * max,size_t buflen,char * errstr)559 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
560 size_t buflen, char *errstr){
561 struct group *grp;
562 gid_t gid1, gid2;
563 char *spec1, *spec2, *endp;
564 unsigned long value;
565 size_t len;
566
567 spec2 = spec;
568 spec1 = strsep(&spec2, ":");
569
570 grp = getgrnam(spec1);
571 if (grp != NULL)
572 gid1 = grp->gr_gid;
573 else {
574 value = strtoul(spec1, &endp, 10);
575 if (*endp != '\0') {
576 len = snprintf(errstr, buflen,
577 "invalid gid: '%s'", spec1);
578 return (-1);
579 }
580 gid1 = value;
581 }
582
583 if (spec2 == NULL) {
584 *max = *min = gid1;
585 return (0);
586 }
587
588 grp = getgrnam(spec2);
589 if (grp != NULL)
590 gid2 = grp->gr_gid;
591 else {
592 value = strtoul(spec2, &endp, 10);
593 if (*endp != '\0') {
594 len = snprintf(errstr, buflen,
595 "invalid gid: '%s'", spec2);
596 return (-1);
597 }
598 gid2 = value;
599 }
600
601 *min = gid1;
602 *max = gid2;
603
604 return (0);
605 }
606
607 int
bsde_parse_subject(int argc,char * argv[],struct mac_bsdextended_subject * subject,size_t buflen,char * errstr)608 bsde_parse_subject(int argc, char *argv[],
609 struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
610 {
611 int not_seen, flags;
612 int current, neg, nextnot;
613 char *endp;
614 uid_t uid_min, uid_max;
615 gid_t gid_min, gid_max;
616 int jid;
617 size_t len;
618 long value;
619
620 current = 0;
621 flags = 0;
622 neg = 0;
623 nextnot = 0;
624
625 if (strcmp("not", argv[current]) == 0) {
626 not_seen = 1;
627 current++;
628 } else
629 not_seen = 0;
630
631 while (current < argc) {
632 if (strcmp(argv[current], "uid") == 0) {
633 if (current + 2 > argc) {
634 len = snprintf(errstr, buflen, "uid short");
635 return (-1);
636 }
637 if (flags & MBS_UID_DEFINED) {
638 len = snprintf(errstr, buflen, "one uid only");
639 return (-1);
640 }
641 if (bsde_parse_uidrange(argv[current+1],
642 &uid_min, &uid_max, buflen, errstr) < 0)
643 return (-1);
644 flags |= MBS_UID_DEFINED;
645 if (nextnot) {
646 neg ^= MBS_UID_DEFINED;
647 nextnot = 0;
648 }
649 current += 2;
650 } else if (strcmp(argv[current], "gid") == 0) {
651 if (current + 2 > argc) {
652 len = snprintf(errstr, buflen, "gid short");
653 return (-1);
654 }
655 if (flags & MBS_GID_DEFINED) {
656 len = snprintf(errstr, buflen, "one gid only");
657 return (-1);
658 }
659 if (bsde_parse_gidrange(argv[current+1],
660 &gid_min, &gid_max, buflen, errstr) < 0)
661 return (-1);
662 flags |= MBS_GID_DEFINED;
663 if (nextnot) {
664 neg ^= MBS_GID_DEFINED;
665 nextnot = 0;
666 }
667 current += 2;
668 } else if (strcmp(argv[current], "jailid") == 0) {
669 if (current + 2 > argc) {
670 len = snprintf(errstr, buflen, "prison short");
671 return (-1);
672 }
673 if (flags & MBS_PRISON_DEFINED) {
674 len = snprintf(errstr, buflen, "one jail only");
675 return (-1);
676 }
677 value = strtol(argv[current+1], &endp, 10);
678 if (*endp != '\0') {
679 len = snprintf(errstr, buflen,
680 "invalid jid: '%s'", argv[current+1]);
681 return (-1);
682 }
683 jid = value;
684 flags |= MBS_PRISON_DEFINED;
685 if (nextnot) {
686 neg ^= MBS_PRISON_DEFINED;
687 nextnot = 0;
688 }
689 current += 2;
690 } else if (strcmp(argv[current], "!") == 0) {
691 if (nextnot) {
692 len = snprintf(errstr, buflen,
693 "double negative");
694 return (-1);
695 }
696 nextnot = 1;
697 current += 1;
698 } else {
699 len = snprintf(errstr, buflen, "'%s' not expected",
700 argv[current]);
701 return (-1);
702 }
703 }
704
705 subject->mbs_flags = flags;
706 if (not_seen)
707 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
708 else
709 subject->mbs_neg = neg;
710 if (flags & MBS_UID_DEFINED) {
711 subject->mbs_uid_min = uid_min;
712 subject->mbs_uid_max = uid_max;
713 }
714 if (flags & MBS_GID_DEFINED) {
715 subject->mbs_gid_min = gid_min;
716 subject->mbs_gid_max = gid_max;
717 }
718 if (flags & MBS_PRISON_DEFINED)
719 subject->mbs_prison = jid;
720
721 return (0);
722 }
723
724 int
bsde_parse_type(char * spec,int * type,size_t buflen,char * errstr)725 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
726 {
727 size_t len;
728 int i;
729
730 *type = 0;
731 for (i = 0; i < strlen(spec); i++) {
732 switch (spec[i]) {
733 case 'r':
734 case '-':
735 *type |= MBO_TYPE_REG;
736 break;
737 case 'd':
738 *type |= MBO_TYPE_DIR;
739 break;
740 case 'b':
741 *type |= MBO_TYPE_BLK;
742 break;
743 case 'c':
744 *type |= MBO_TYPE_CHR;
745 break;
746 case 'l':
747 *type |= MBO_TYPE_LNK;
748 break;
749 case 's':
750 *type |= MBO_TYPE_SOCK;
751 break;
752 case 'p':
753 *type |= MBO_TYPE_FIFO;
754 break;
755 case 'a':
756 *type |= MBO_ALL_TYPE;
757 break;
758 default:
759 len = snprintf(errstr, buflen, "Unknown type code: %c",
760 spec[i]);
761 return (-1);
762 }
763 }
764
765 return (0);
766 }
767
768 int
bsde_parse_fsid(char * spec,struct fsid * fsid,size_t buflen,char * errstr)769 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
770 {
771 size_t len;
772 struct statfs buf;
773
774 if (statfs(spec, &buf) < 0) {
775 len = snprintf(errstr, buflen, "Unable to get id for %s: %s",
776 spec, strerror(errno));
777 return (-1);
778 }
779
780 *fsid = buf.f_fsid;
781
782 return (0);
783 }
784
785 int
bsde_parse_object(int argc,char * argv[],struct mac_bsdextended_object * object,size_t buflen,char * errstr)786 bsde_parse_object(int argc, char *argv[],
787 struct mac_bsdextended_object *object, size_t buflen, char *errstr)
788 {
789 int not_seen, flags;
790 int current, neg, nextnot;
791 uid_t uid_min, uid_max;
792 gid_t gid_min, gid_max;
793 int type;
794 struct fsid fsid;
795 size_t len;
796
797 current = 0;
798 flags = 0;
799 neg = 0;
800 nextnot = 0;
801
802 if (strcmp("not", argv[current]) == 0) {
803 not_seen = 1;
804 current++;
805 } else
806 not_seen = 0;
807
808 while (current < argc) {
809 if (strcmp(argv[current], "uid") == 0) {
810 if (current + 2 > argc) {
811 len = snprintf(errstr, buflen, "uid short");
812 return (-1);
813 }
814 if (flags & MBO_UID_DEFINED) {
815 len = snprintf(errstr, buflen, "one uid only");
816 return (-1);
817 }
818 if (bsde_parse_uidrange(argv[current+1],
819 &uid_min, &uid_max, buflen, errstr) < 0)
820 return (-1);
821 flags |= MBO_UID_DEFINED;
822 if (nextnot) {
823 neg ^= MBO_UID_DEFINED;
824 nextnot = 0;
825 }
826 current += 2;
827 } else if (strcmp(argv[current], "gid") == 0) {
828 if (current + 2 > argc) {
829 len = snprintf(errstr, buflen, "gid short");
830 return (-1);
831 }
832 if (flags & MBO_GID_DEFINED) {
833 len = snprintf(errstr, buflen, "one gid only");
834 return (-1);
835 }
836 if (bsde_parse_gidrange(argv[current+1],
837 &gid_min, &gid_max, buflen, errstr) < 0)
838 return (-1);
839 flags |= MBO_GID_DEFINED;
840 if (nextnot) {
841 neg ^= MBO_GID_DEFINED;
842 nextnot = 0;
843 }
844 current += 2;
845 } else if (strcmp(argv[current], "filesys") == 0) {
846 if (current + 2 > argc) {
847 len = snprintf(errstr, buflen, "filesys short");
848 return (-1);
849 }
850 if (flags & MBO_FSID_DEFINED) {
851 len = snprintf(errstr, buflen, "one fsid only");
852 return (-1);
853 }
854 if (bsde_parse_fsid(argv[current+1], &fsid,
855 buflen, errstr) < 0)
856 return (-1);
857 flags |= MBO_FSID_DEFINED;
858 if (nextnot) {
859 neg ^= MBO_FSID_DEFINED;
860 nextnot = 0;
861 }
862 current += 2;
863 } else if (strcmp(argv[current], "suid") == 0) {
864 flags |= MBO_SUID;
865 if (nextnot) {
866 neg ^= MBO_SUID;
867 nextnot = 0;
868 }
869 current += 1;
870 } else if (strcmp(argv[current], "sgid") == 0) {
871 flags |= MBO_SGID;
872 if (nextnot) {
873 neg ^= MBO_SGID;
874 nextnot = 0;
875 }
876 current += 1;
877 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
878 flags |= MBO_UID_SUBJECT;
879 if (nextnot) {
880 neg ^= MBO_UID_SUBJECT;
881 nextnot = 0;
882 }
883 current += 1;
884 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
885 flags |= MBO_GID_SUBJECT;
886 if (nextnot) {
887 neg ^= MBO_GID_SUBJECT;
888 nextnot = 0;
889 }
890 current += 1;
891 } else if (strcmp(argv[current], "type") == 0) {
892 if (current + 2 > argc) {
893 len = snprintf(errstr, buflen, "type short");
894 return (-1);
895 }
896 if (flags & MBO_TYPE_DEFINED) {
897 len = snprintf(errstr, buflen, "one type only");
898 return (-1);
899 }
900 if (bsde_parse_type(argv[current+1], &type,
901 buflen, errstr) < 0)
902 return (-1);
903 flags |= MBO_TYPE_DEFINED;
904 if (nextnot) {
905 neg ^= MBO_TYPE_DEFINED;
906 nextnot = 0;
907 }
908 current += 2;
909 } else if (strcmp(argv[current], "!") == 0) {
910 if (nextnot) {
911 len = snprintf(errstr, buflen,
912 "double negative'");
913 return (-1);
914 }
915 nextnot = 1;
916 current += 1;
917 } else {
918 len = snprintf(errstr, buflen, "'%s' not expected",
919 argv[current]);
920 return (-1);
921 }
922 }
923
924 object->mbo_flags = flags;
925 if (not_seen)
926 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
927 else
928 object->mbo_neg = neg;
929 if (flags & MBO_UID_DEFINED) {
930 object->mbo_uid_min = uid_min;
931 object->mbo_uid_max = uid_max;
932 }
933 if (flags & MBO_GID_DEFINED) {
934 object->mbo_gid_min = gid_min;
935 object->mbo_gid_max = gid_max;
936 }
937 if (flags & MBO_FSID_DEFINED)
938 object->mbo_fsid = fsid;
939 if (flags & MBO_TYPE_DEFINED)
940 object->mbo_type = type;
941
942 return (0);
943 }
944
945 int
bsde_parse_mode(int argc,char * argv[],mode_t * mode,size_t buflen,char * errstr)946 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
947 char *errstr)
948 {
949 size_t len;
950 int i;
951
952 if (argc == 0) {
953 len = snprintf(errstr, buflen, "mode expects mode value");
954 return (-1);
955 }
956
957 if (argc != 1) {
958 len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
959 return (-1);
960 }
961
962 *mode = 0;
963 for (i = 0; i < strlen(argv[0]); i++) {
964 switch (argv[0][i]) {
965 case 'a':
966 *mode |= MBI_ADMIN;
967 break;
968 case 'r':
969 *mode |= MBI_READ;
970 break;
971 case 's':
972 *mode |= MBI_STAT;
973 break;
974 case 'w':
975 *mode |= MBI_WRITE;
976 break;
977 case 'x':
978 *mode |= MBI_EXEC;
979 break;
980 case 'n':
981 /* ignore */
982 break;
983 default:
984 len = snprintf(errstr, buflen, "Unknown mode letter: %c",
985 argv[0][i]);
986 return (-1);
987 }
988 }
989
990 return (0);
991 }
992
993 int
bsde_parse_rule(int argc,char * argv[],struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)994 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
995 size_t buflen, char *errstr)
996 {
997 int subject, subject_elements, subject_elements_length;
998 int object, object_elements, object_elements_length;
999 int mode, mode_elements, mode_elements_length;
1000 int error, i;
1001 size_t len;
1002
1003 bzero(rule, sizeof(*rule));
1004
1005 if (argc < 1) {
1006 len = snprintf(errstr, buflen, "Rule must begin with subject");
1007 return (-1);
1008 }
1009
1010 if (strcmp(argv[0], "subject") != 0) {
1011 len = snprintf(errstr, buflen, "Rule must begin with subject");
1012 return (-1);
1013 }
1014 subject = 0;
1015 subject_elements = 1;
1016
1017 /* Search forward for object. */
1018
1019 object = -1;
1020 for (i = 1; i < argc; i++)
1021 if (strcmp(argv[i], "object") == 0)
1022 object = i;
1023
1024 if (object == -1) {
1025 len = snprintf(errstr, buflen, "Rule must contain an object");
1026 return (-1);
1027 }
1028
1029 /* Search forward for mode. */
1030 mode = -1;
1031 for (i = object; i < argc; i++)
1032 if (strcmp(argv[i], "mode") == 0)
1033 mode = i;
1034
1035 if (mode == -1) {
1036 len = snprintf(errstr, buflen, "Rule must contain mode");
1037 return (-1);
1038 }
1039
1040 subject_elements_length = object - subject - 1;
1041 object_elements = object + 1;
1042 object_elements_length = mode - object_elements;
1043 mode_elements = mode + 1;
1044 mode_elements_length = argc - mode_elements;
1045
1046 error = bsde_parse_subject(subject_elements_length,
1047 argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1048 if (error)
1049 return (-1);
1050
1051 error = bsde_parse_object(object_elements_length,
1052 argv + object_elements, &rule->mbr_object, buflen, errstr);
1053 if (error)
1054 return (-1);
1055
1056 error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1057 &rule->mbr_mode, buflen, errstr);
1058 if (error)
1059 return (-1);
1060
1061 return (0);
1062 }
1063
1064 int
bsde_parse_rule_string(const char * string,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1065 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1066 size_t buflen, char *errstr)
1067 {
1068 char *stringdup, *stringp, *argv[100], **ap;
1069 int argc, error;
1070
1071 stringp = stringdup = strdup(string);
1072 while (*stringp == ' ' || *stringp == '\t')
1073 stringp++;
1074
1075 argc = 0;
1076 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1077 argc++;
1078 if (**ap != '\0')
1079 if (++ap >= &argv[100])
1080 break;
1081 }
1082
1083 error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1084
1085 free(stringdup);
1086
1087 return (error);
1088 }
1089
1090 int
bsde_get_mib(const char * string,int * name,size_t * namelen)1091 bsde_get_mib(const char *string, int *name, size_t *namelen)
1092 {
1093 size_t len;
1094 int error;
1095
1096 len = *namelen;
1097 error = sysctlnametomib(string, name, &len);
1098 if (error)
1099 return (error);
1100
1101 *namelen = len;
1102 return (0);
1103 }
1104
1105 int
bsde_check_version(size_t buflen,char * errstr)1106 bsde_check_version(size_t buflen, char *errstr)
1107 {
1108 size_t len;
1109 int error;
1110 int version;
1111
1112 len = sizeof(version);
1113 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1114 if (error) {
1115 len = snprintf(errstr, buflen, "version check failed: %s",
1116 strerror(errno));
1117 return (-1);
1118 }
1119 if (version != MB_VERSION) {
1120 len = snprintf(errstr, buflen, "module v%d != library v%d",
1121 version, MB_VERSION);
1122 return (-1);
1123 }
1124 return (0);
1125 }
1126
1127 int
bsde_get_rule_count(size_t buflen,char * errstr)1128 bsde_get_rule_count(size_t buflen, char *errstr)
1129 {
1130 size_t len;
1131 int error;
1132 int rule_count;
1133
1134 len = sizeof(rule_count);
1135 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1136 if (error) {
1137 len = snprintf(errstr, buflen, "%s", strerror(errno));
1138 return (-1);
1139 }
1140 if (len != sizeof(rule_count)) {
1141 len = snprintf(errstr, buflen, "Data error in %s.rule_count",
1142 MIB);
1143 return (-1);
1144 }
1145
1146 return (rule_count);
1147 }
1148
1149 int
bsde_get_rule_slots(size_t buflen,char * errstr)1150 bsde_get_rule_slots(size_t buflen, char *errstr)
1151 {
1152 size_t len;
1153 int error;
1154 int rule_slots;
1155
1156 len = sizeof(rule_slots);
1157 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1158 if (error) {
1159 len = snprintf(errstr, buflen, "%s", strerror(errno));
1160 return (-1);
1161 }
1162 if (len != sizeof(rule_slots)) {
1163 len = snprintf(errstr, buflen, "Data error in %s.rule_slots",
1164 MIB);
1165 return (-1);
1166 }
1167
1168 return (rule_slots);
1169 }
1170
1171 /*
1172 * Returns 0 for success;
1173 * Returns -1 for failure;
1174 * Returns -2 for not present
1175 */
1176 int
bsde_get_rule(int rulenum,struct mac_bsdextended_rule * rule,size_t errlen,char * errstr)1177 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1178 char *errstr)
1179 {
1180 int name[10];
1181 size_t len, size;
1182 int error;
1183
1184 if (bsde_check_version(errlen, errstr) != 0)
1185 return (-1);
1186
1187 len = 10;
1188 error = bsde_get_mib(MIB ".rules", name, &len);
1189 if (error) {
1190 len = snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1191 strerror(errno));
1192 return (-1);
1193 }
1194
1195 size = sizeof(*rule);
1196 name[len] = rulenum;
1197 len++;
1198 error = sysctl(name, len, rule, &size, NULL, 0);
1199 if (error == -1 && errno == ENOENT)
1200 return (-2);
1201 if (error) {
1202 len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1203 rulenum, strerror(errno));
1204 return (-1);
1205 } else if (size != sizeof(*rule)) {
1206 len = snprintf(errstr, errlen, "Data error in %s.%d: %s",
1207 MIB ".rules", rulenum, strerror(errno));
1208 return (-1);
1209 }
1210
1211 return (0);
1212 }
1213
1214 int
bsde_delete_rule(int rulenum,size_t buflen,char * errstr)1215 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1216 {
1217 struct mac_bsdextended_rule rule;
1218 int name[10];
1219 size_t len, size;
1220 int error;
1221
1222 if (bsde_check_version(buflen, errstr) != 0)
1223 return (-1);
1224
1225 len = 10;
1226 error = bsde_get_mib(MIB ".rules", name, &len);
1227 if (error) {
1228 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1229 strerror(errno));
1230 return (-1);
1231 }
1232
1233 name[len] = rulenum;
1234 len++;
1235
1236 size = sizeof(rule);
1237 error = sysctl(name, len, NULL, NULL, &rule, 0);
1238 if (error) {
1239 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1240 rulenum, strerror(errno));
1241 return (-1);
1242 }
1243
1244 return (0);
1245 }
1246
1247 int
bsde_set_rule(int rulenum,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1248 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1249 char *errstr)
1250 {
1251 int name[10];
1252 size_t len, size;
1253 int error;
1254
1255 if (bsde_check_version(buflen, errstr) != 0)
1256 return (-1);
1257
1258 len = 10;
1259 error = bsde_get_mib(MIB ".rules", name, &len);
1260 if (error) {
1261 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1262 strerror(errno));
1263 return (-1);
1264 }
1265
1266 name[len] = rulenum;
1267 len++;
1268
1269 size = sizeof(*rule);
1270 error = sysctl(name, len, NULL, NULL, rule, size);
1271 if (error) {
1272 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1273 rulenum, strerror(errno));
1274 return (-1);
1275 }
1276
1277 return (0);
1278 }
1279
1280 int
bsde_add_rule(int * rulenum,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1281 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1282 char *errstr)
1283 {
1284 char charstr[BUFSIZ];
1285 int name[10];
1286 size_t len, size;
1287 int error, rule_slots;
1288
1289 if (bsde_check_version(buflen, errstr) != 0)
1290 return (-1);
1291
1292 len = 10;
1293 error = bsde_get_mib(MIB ".rules", name, &len);
1294 if (error) {
1295 len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1296 strerror(errno));
1297 return (-1);
1298 }
1299
1300 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1301 if (rule_slots == -1) {
1302 len = snprintf(errstr, buflen, "unable to get rule slots: %s",
1303 strerror(errno));
1304 return (-1);
1305 }
1306
1307 name[len] = rule_slots;
1308 len++;
1309
1310 size = sizeof(*rule);
1311 error = sysctl(name, len, NULL, NULL, rule, size);
1312 if (error) {
1313 len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1314 rule_slots, strerror(errno));
1315 return (-1);
1316 }
1317
1318 if (rulenum != NULL)
1319 *rulenum = rule_slots;
1320
1321 return (0);
1322 }
1323