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