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