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 (fsidcmp(&rule->mbr_object.mbo_fsid,
338 &mntbuf[i].f_fsid) == 0)
339 break;
340 len = snprintf(cur, left, "filesys %s ",
341 i == numfs ? "???" : mntbuf[i].f_mntonname);
342 if (len < 0 || len > left)
343 goto truncated;
344 left -= len;
345 cur += len;
346 }
347 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) {
348 len = snprintf(cur, left, "! ");
349 if (len < 0 || len > left)
350 goto truncated;
351 left -= len;
352 cur += len;
353 }
354 if (rule->mbr_object.mbo_flags & MBO_SUID) {
355 len = snprintf(cur, left, "suid ");
356 if (len < 0 || len > left)
357 goto truncated;
358 left -= len;
359 cur += len;
360 }
361 if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) {
362 len = snprintf(cur, left, "! ");
363 if (len < 0 || len > left)
364 goto truncated;
365 left -= len;
366 cur += len;
367 }
368 if (rule->mbr_object.mbo_flags & MBO_SGID) {
369 len = snprintf(cur, left, "sgid ");
370 if (len < 0 || len > left)
371 goto truncated;
372 left -= len;
373 cur += len;
374 }
375 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) {
376 len = snprintf(cur, left, "! ");
377 if (len < 0 || len > left)
378 goto truncated;
379 left -= len;
380 cur += len;
381 }
382 if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) {
383 len = snprintf(cur, left, "uid_of_subject ");
384 if (len < 0 || len > left)
385 goto truncated;
386 left -= len;
387 cur += len;
388 }
389 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) {
390 len = snprintf(cur, left, "! ");
391 if (len < 0 || len > left)
392 goto truncated;
393 left -= len;
394 cur += len;
395 }
396 if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) {
397 len = snprintf(cur, left, "gid_of_subject ");
398 if (len < 0 || len > left)
399 goto truncated;
400 left -= len;
401 cur += len;
402 }
403 if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) {
404 len = snprintf(cur, left, "! ");
405 if (len < 0 || len > left)
406 goto truncated;
407 left -= len;
408 cur += len;
409 }
410 if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) {
411 i = 0;
412 if (rule->mbr_object.mbo_type & MBO_TYPE_REG)
413 type[i++] = 'r';
414 if (rule->mbr_object.mbo_type & MBO_TYPE_DIR)
415 type[i++] = 'd';
416 if (rule->mbr_object.mbo_type & MBO_TYPE_BLK)
417 type[i++] = 'b';
418 if (rule->mbr_object.mbo_type & MBO_TYPE_CHR)
419 type[i++] = 'c';
420 if (rule->mbr_object.mbo_type & MBO_TYPE_LNK)
421 type[i++] = 'l';
422 if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK)
423 type[i++] = 's';
424 if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO)
425 type[i++] = 'p';
426 if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) {
427 i = 0;
428 type[i++] = 'a';
429 }
430 type[i++] = '\0';
431 len = snprintf(cur, left, "type %s ", type);
432 if (len < 0 || len > left)
433 goto truncated;
434 left -= len;
435 cur += len;
436 }
437 }
438
439 len = snprintf(cur, left, "mode ");
440 if (len < 0 || len > left)
441 goto truncated;
442 left -= len;
443 cur += len;
444
445 anymode = (rule->mbr_mode & MBI_ALLPERM);
446 unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
447
448 if (rule->mbr_mode & MBI_ADMIN) {
449 len = snprintf(cur, left, "a");
450 if (len < 0 || len > left)
451 goto truncated;
452
453 left -= len;
454 cur += len;
455 }
456 if (rule->mbr_mode & MBI_READ) {
457 len = snprintf(cur, left, "r");
458 if (len < 0 || len > left)
459 goto truncated;
460
461 left -= len;
462 cur += len;
463 }
464 if (rule->mbr_mode & MBI_STAT) {
465 len = snprintf(cur, left, "s");
466 if (len < 0 || len > left)
467 goto truncated;
468
469 left -= len;
470 cur += len;
471 }
472 if (rule->mbr_mode & MBI_WRITE) {
473 len = snprintf(cur, left, "w");
474 if (len < 0 || len > left)
475 goto truncated;
476
477 left -= len;
478 cur += len;
479 }
480 if (rule->mbr_mode & MBI_EXEC) {
481 len = snprintf(cur, left, "x");
482 if (len < 0 || len > left)
483 goto truncated;
484
485 left -= len;
486 cur += len;
487 }
488 if (!anymode) {
489 len = snprintf(cur, left, "n");
490 if (len < 0 || len > left)
491 goto truncated;
492
493 left -= len;
494 cur += len;
495 }
496 if (unknownmode) {
497 len = snprintf(cur, left, "?");
498 if (len < 0 || len > left)
499 goto truncated;
500
501 left -= len;
502 cur += len;
503 }
504
505 return (0);
506
507 truncated:
508 return (-1);
509 }
510
511 static int
bsde_parse_uidrange(char * spec,uid_t * min,uid_t * max,size_t buflen,char * errstr)512 bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max,
513 size_t buflen, char *errstr){
514 struct passwd *pwd;
515 uid_t uid1, uid2;
516 char *spec1, *spec2, *endp;
517 unsigned long value;
518
519 spec2 = spec;
520 spec1 = strsep(&spec2, ":");
521
522 pwd = getpwnam(spec1);
523 if (pwd != NULL)
524 uid1 = pwd->pw_uid;
525 else {
526 value = strtoul(spec1, &endp, 10);
527 if (*endp != '\0') {
528 snprintf(errstr, buflen, "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 snprintf(errstr, buflen, "invalid uid: '%s'", spec2);
546 return (-1);
547 }
548 uid2 = value;
549 }
550
551 *min = uid1;
552 *max = uid2;
553
554 return (0);
555 }
556
557 static int
bsde_parse_gidrange(char * spec,gid_t * min,gid_t * max,size_t buflen,char * errstr)558 bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
559 size_t buflen, char *errstr){
560 struct group *grp;
561 gid_t gid1, gid2;
562 char *spec1, *spec2, *endp;
563 unsigned long value;
564
565 spec2 = spec;
566 spec1 = strsep(&spec2, ":");
567
568 grp = getgrnam(spec1);
569 if (grp != NULL)
570 gid1 = grp->gr_gid;
571 else {
572 value = strtoul(spec1, &endp, 10);
573 if (*endp != '\0') {
574 snprintf(errstr, buflen, "invalid gid: '%s'", spec1);
575 return (-1);
576 }
577 gid1 = value;
578 }
579
580 if (spec2 == NULL) {
581 *max = *min = gid1;
582 return (0);
583 }
584
585 grp = getgrnam(spec2);
586 if (grp != NULL)
587 gid2 = grp->gr_gid;
588 else {
589 value = strtoul(spec2, &endp, 10);
590 if (*endp != '\0') {
591 snprintf(errstr, buflen, "invalid gid: '%s'", spec2);
592 return (-1);
593 }
594 gid2 = value;
595 }
596
597 *min = gid1;
598 *max = gid2;
599
600 return (0);
601 }
602
603 static int
bsde_get_jailid(const char * name,size_t buflen,char * errstr)604 bsde_get_jailid(const char *name, size_t buflen, char *errstr)
605 {
606 char *ep;
607 int jid;
608 struct iovec jiov[4];
609
610 /* Copy jail_getid(3) instead of messing with library dependancies */
611 jid = strtoul(name, &ep, 10);
612 if (*name && !*ep)
613 return jid;
614 jiov[0].iov_base = __DECONST(char *, "name");
615 jiov[0].iov_len = sizeof("name");
616 jiov[1].iov_len = strlen(name) + 1;
617 jiov[1].iov_base = alloca(jiov[1].iov_len);
618 strcpy(jiov[1].iov_base, name);
619 if (errstr && buflen) {
620 jiov[2].iov_base = __DECONST(char *, "errmsg");
621 jiov[2].iov_len = sizeof("errmsg");
622 jiov[3].iov_base = errstr;
623 jiov[3].iov_len = buflen;
624 errstr[0] = 0;
625 jid = jail_get(jiov, 4, 0);
626 if (jid < 0 && !errstr[0])
627 snprintf(errstr, buflen, "jail_get: %s",
628 strerror(errno));
629 } else
630 jid = jail_get(jiov, 2, 0);
631 return jid;
632 }
633
634 static int
bsde_parse_subject(int argc,char * argv[],struct mac_bsdextended_subject * subject,size_t buflen,char * errstr)635 bsde_parse_subject(int argc, char *argv[],
636 struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
637 {
638 int not_seen, flags;
639 int current, neg, nextnot;
640 uid_t uid_min, uid_max;
641 gid_t gid_min, gid_max;
642 int jid = 0;
643
644 current = 0;
645 flags = 0;
646 neg = 0;
647 nextnot = 0;
648
649 if (strcmp("not", argv[current]) == 0) {
650 not_seen = 1;
651 current++;
652 } else
653 not_seen = 0;
654
655 while (current < argc) {
656 if (strcmp(argv[current], "uid") == 0) {
657 if (current + 2 > argc) {
658 snprintf(errstr, buflen, "uid short");
659 return (-1);
660 }
661 if (flags & MBS_UID_DEFINED) {
662 snprintf(errstr, buflen, "one uid only");
663 return (-1);
664 }
665 if (bsde_parse_uidrange(argv[current+1],
666 &uid_min, &uid_max, buflen, errstr) < 0)
667 return (-1);
668 flags |= MBS_UID_DEFINED;
669 if (nextnot) {
670 neg ^= MBS_UID_DEFINED;
671 nextnot = 0;
672 }
673 current += 2;
674 } else if (strcmp(argv[current], "gid") == 0) {
675 if (current + 2 > argc) {
676 snprintf(errstr, buflen, "gid short");
677 return (-1);
678 }
679 if (flags & MBS_GID_DEFINED) {
680 snprintf(errstr, buflen, "one gid only");
681 return (-1);
682 }
683 if (bsde_parse_gidrange(argv[current+1],
684 &gid_min, &gid_max, buflen, errstr) < 0)
685 return (-1);
686 flags |= MBS_GID_DEFINED;
687 if (nextnot) {
688 neg ^= MBS_GID_DEFINED;
689 nextnot = 0;
690 }
691 current += 2;
692 } else if (strcmp(argv[current], "jailid") == 0) {
693 if (current + 2 > argc) {
694 snprintf(errstr, buflen, "prison short");
695 return (-1);
696 }
697 if (flags & MBS_PRISON_DEFINED) {
698 snprintf(errstr, buflen, "one jail only");
699 return (-1);
700 }
701 jid = bsde_get_jailid(argv[current+1], buflen, errstr);
702 if (jid < 0)
703 return (-1);
704 flags |= MBS_PRISON_DEFINED;
705 if (nextnot) {
706 neg ^= MBS_PRISON_DEFINED;
707 nextnot = 0;
708 }
709 current += 2;
710 } else if (strcmp(argv[current], "!") == 0) {
711 if (nextnot) {
712 snprintf(errstr, buflen, "double negative");
713 return (-1);
714 }
715 nextnot = 1;
716 current += 1;
717 } else {
718 snprintf(errstr, buflen, "'%s' not expected",
719 argv[current]);
720 return (-1);
721 }
722 }
723
724 subject->mbs_flags = flags;
725 if (not_seen)
726 subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
727 else
728 subject->mbs_neg = neg;
729 if (flags & MBS_UID_DEFINED) {
730 subject->mbs_uid_min = uid_min;
731 subject->mbs_uid_max = uid_max;
732 }
733 if (flags & MBS_GID_DEFINED) {
734 subject->mbs_gid_min = gid_min;
735 subject->mbs_gid_max = gid_max;
736 }
737 if (flags & MBS_PRISON_DEFINED)
738 subject->mbs_prison = jid;
739
740 return (0);
741 }
742
743 static int
bsde_parse_type(char * spec,int * type,size_t buflen,char * errstr)744 bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
745 {
746 int i;
747
748 *type = 0;
749 for (i = 0; i < strlen(spec); i++) {
750 switch (spec[i]) {
751 case 'r':
752 case '-':
753 *type |= MBO_TYPE_REG;
754 break;
755 case 'd':
756 *type |= MBO_TYPE_DIR;
757 break;
758 case 'b':
759 *type |= MBO_TYPE_BLK;
760 break;
761 case 'c':
762 *type |= MBO_TYPE_CHR;
763 break;
764 case 'l':
765 *type |= MBO_TYPE_LNK;
766 break;
767 case 's':
768 *type |= MBO_TYPE_SOCK;
769 break;
770 case 'p':
771 *type |= MBO_TYPE_FIFO;
772 break;
773 case 'a':
774 *type |= MBO_ALL_TYPE;
775 break;
776 default:
777 snprintf(errstr, buflen, "Unknown type code: %c",
778 spec[i]);
779 return (-1);
780 }
781 }
782
783 return (0);
784 }
785
786 static int
bsde_parse_fsid(char * spec,struct fsid * fsid,size_t buflen,char * errstr)787 bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
788 {
789 struct statfs buf;
790
791 if (statfs(spec, &buf) < 0) {
792 snprintf(errstr, buflen, "Unable to get id for %s: %s",
793 spec, strerror(errno));
794 return (-1);
795 }
796
797 *fsid = buf.f_fsid;
798
799 return (0);
800 }
801
802 static int
bsde_parse_object(int argc,char * argv[],struct mac_bsdextended_object * object,size_t buflen,char * errstr)803 bsde_parse_object(int argc, char *argv[],
804 struct mac_bsdextended_object *object, size_t buflen, char *errstr)
805 {
806 int not_seen, flags;
807 int current, neg, nextnot;
808 int type;
809 uid_t uid_min, uid_max;
810 gid_t gid_min, gid_max;
811 struct fsid fsid;
812
813 current = 0;
814 flags = 0;
815 neg = 0;
816 nextnot = 0;
817 type = 0;
818
819 if (strcmp("not", argv[current]) == 0) {
820 not_seen = 1;
821 current++;
822 } else
823 not_seen = 0;
824
825 while (current < argc) {
826 if (strcmp(argv[current], "uid") == 0) {
827 if (current + 2 > argc) {
828 snprintf(errstr, buflen, "uid short");
829 return (-1);
830 }
831 if (flags & MBO_UID_DEFINED) {
832 snprintf(errstr, buflen, "one uid only");
833 return (-1);
834 }
835 if (bsde_parse_uidrange(argv[current+1],
836 &uid_min, &uid_max, buflen, errstr) < 0)
837 return (-1);
838 flags |= MBO_UID_DEFINED;
839 if (nextnot) {
840 neg ^= MBO_UID_DEFINED;
841 nextnot = 0;
842 }
843 current += 2;
844 } else if (strcmp(argv[current], "gid") == 0) {
845 if (current + 2 > argc) {
846 snprintf(errstr, buflen, "gid short");
847 return (-1);
848 }
849 if (flags & MBO_GID_DEFINED) {
850 snprintf(errstr, buflen, "one gid only");
851 return (-1);
852 }
853 if (bsde_parse_gidrange(argv[current+1],
854 &gid_min, &gid_max, buflen, errstr) < 0)
855 return (-1);
856 flags |= MBO_GID_DEFINED;
857 if (nextnot) {
858 neg ^= MBO_GID_DEFINED;
859 nextnot = 0;
860 }
861 current += 2;
862 } else if (strcmp(argv[current], "filesys") == 0) {
863 if (current + 2 > argc) {
864 snprintf(errstr, buflen, "filesys short");
865 return (-1);
866 }
867 if (flags & MBO_FSID_DEFINED) {
868 snprintf(errstr, buflen, "one fsid only");
869 return (-1);
870 }
871 if (bsde_parse_fsid(argv[current+1], &fsid,
872 buflen, errstr) < 0)
873 return (-1);
874 flags |= MBO_FSID_DEFINED;
875 if (nextnot) {
876 neg ^= MBO_FSID_DEFINED;
877 nextnot = 0;
878 }
879 current += 2;
880 } else if (strcmp(argv[current], "suid") == 0) {
881 flags |= MBO_SUID;
882 if (nextnot) {
883 neg ^= MBO_SUID;
884 nextnot = 0;
885 }
886 current += 1;
887 } else if (strcmp(argv[current], "sgid") == 0) {
888 flags |= MBO_SGID;
889 if (nextnot) {
890 neg ^= MBO_SGID;
891 nextnot = 0;
892 }
893 current += 1;
894 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
895 flags |= MBO_UID_SUBJECT;
896 if (nextnot) {
897 neg ^= MBO_UID_SUBJECT;
898 nextnot = 0;
899 }
900 current += 1;
901 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
902 flags |= MBO_GID_SUBJECT;
903 if (nextnot) {
904 neg ^= MBO_GID_SUBJECT;
905 nextnot = 0;
906 }
907 current += 1;
908 } else if (strcmp(argv[current], "type") == 0) {
909 if (current + 2 > argc) {
910 snprintf(errstr, buflen, "type short");
911 return (-1);
912 }
913 if (flags & MBO_TYPE_DEFINED) {
914 snprintf(errstr, buflen, "one type only");
915 return (-1);
916 }
917 if (bsde_parse_type(argv[current+1], &type,
918 buflen, errstr) < 0)
919 return (-1);
920 flags |= MBO_TYPE_DEFINED;
921 if (nextnot) {
922 neg ^= MBO_TYPE_DEFINED;
923 nextnot = 0;
924 }
925 current += 2;
926 } else if (strcmp(argv[current], "!") == 0) {
927 if (nextnot) {
928 snprintf(errstr, buflen,
929 "double negative'");
930 return (-1);
931 }
932 nextnot = 1;
933 current += 1;
934 } else {
935 snprintf(errstr, buflen, "'%s' not expected",
936 argv[current]);
937 return (-1);
938 }
939 }
940
941 object->mbo_flags = flags;
942 if (not_seen)
943 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
944 else
945 object->mbo_neg = neg;
946 if (flags & MBO_UID_DEFINED) {
947 object->mbo_uid_min = uid_min;
948 object->mbo_uid_max = uid_max;
949 }
950 if (flags & MBO_GID_DEFINED) {
951 object->mbo_gid_min = gid_min;
952 object->mbo_gid_max = gid_max;
953 }
954 if (flags & MBO_FSID_DEFINED)
955 object->mbo_fsid = fsid;
956 if (flags & MBO_TYPE_DEFINED)
957 object->mbo_type = type;
958
959 return (0);
960 }
961
962 int
bsde_parse_mode(int argc,char * argv[],mode_t * mode,size_t buflen,char * errstr)963 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
964 char *errstr)
965 {
966 int i;
967
968 if (argc == 0) {
969 snprintf(errstr, buflen, "mode expects mode value");
970 return (-1);
971 }
972
973 if (argc != 1) {
974 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
975 return (-1);
976 }
977
978 *mode = 0;
979 for (i = 0; i < strlen(argv[0]); i++) {
980 switch (argv[0][i]) {
981 case 'a':
982 *mode |= MBI_ADMIN;
983 break;
984 case 'r':
985 *mode |= MBI_READ;
986 break;
987 case 's':
988 *mode |= MBI_STAT;
989 break;
990 case 'w':
991 *mode |= MBI_WRITE;
992 break;
993 case 'x':
994 *mode |= MBI_EXEC;
995 break;
996 case 'n':
997 /* ignore */
998 break;
999 default:
1000 snprintf(errstr, buflen, "Unknown mode letter: %c",
1001 argv[0][i]);
1002 return (-1);
1003 }
1004 }
1005
1006 return (0);
1007 }
1008
1009 int
bsde_parse_rule(int argc,char * argv[],struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1010 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
1011 size_t buflen, char *errstr)
1012 {
1013 int subject, subject_elements, subject_elements_length;
1014 int object, object_elements, object_elements_length;
1015 int mode, mode_elements, mode_elements_length;
1016 int error, i;
1017
1018 bzero(rule, sizeof(*rule));
1019
1020 if (argc < 1) {
1021 snprintf(errstr, buflen, "Rule must begin with subject");
1022 return (-1);
1023 }
1024
1025 if (strcmp(argv[0], "subject") != 0) {
1026 snprintf(errstr, buflen, "Rule must begin with subject");
1027 return (-1);
1028 }
1029 subject = 0;
1030 subject_elements = 1;
1031
1032 /* Search forward for object. */
1033
1034 object = -1;
1035 for (i = 1; i < argc; i++)
1036 if (strcmp(argv[i], "object") == 0)
1037 object = i;
1038
1039 if (object == -1) {
1040 snprintf(errstr, buflen, "Rule must contain an object");
1041 return (-1);
1042 }
1043
1044 /* Search forward for mode. */
1045 mode = -1;
1046 for (i = object; i < argc; i++)
1047 if (strcmp(argv[i], "mode") == 0)
1048 mode = i;
1049
1050 if (mode == -1) {
1051 snprintf(errstr, buflen, "Rule must contain mode");
1052 return (-1);
1053 }
1054
1055 subject_elements_length = object - subject - 1;
1056 object_elements = object + 1;
1057 object_elements_length = mode - object_elements;
1058 mode_elements = mode + 1;
1059 mode_elements_length = argc - mode_elements;
1060
1061 error = bsde_parse_subject(subject_elements_length,
1062 argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1063 if (error)
1064 return (-1);
1065
1066 error = bsde_parse_object(object_elements_length,
1067 argv + object_elements, &rule->mbr_object, buflen, errstr);
1068 if (error)
1069 return (-1);
1070
1071 error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1072 &rule->mbr_mode, buflen, errstr);
1073 if (error)
1074 return (-1);
1075
1076 return (0);
1077 }
1078
1079 int
bsde_parse_rule_string(const char * string,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1080 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1081 size_t buflen, char *errstr)
1082 {
1083 char *stringdup, *stringp, *argv[100], **ap;
1084 int argc, error;
1085
1086 stringp = stringdup = strdup(string);
1087 while (*stringp == ' ' || *stringp == '\t')
1088 stringp++;
1089
1090 argc = 0;
1091 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1092 argc++;
1093 if (**ap != '\0')
1094 if (++ap >= &argv[100])
1095 break;
1096 }
1097
1098 error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1099
1100 free(stringdup);
1101
1102 return (error);
1103 }
1104
1105 int
bsde_get_mib(const char * string,int * name,size_t * namelen)1106 bsde_get_mib(const char *string, int *name, size_t *namelen)
1107 {
1108 size_t len;
1109 int error;
1110
1111 len = *namelen;
1112 error = sysctlnametomib(string, name, &len);
1113 if (error)
1114 return (error);
1115
1116 *namelen = len;
1117 return (0);
1118 }
1119
1120 static int
bsde_check_version(size_t buflen,char * errstr)1121 bsde_check_version(size_t buflen, char *errstr)
1122 {
1123 size_t len;
1124 int error;
1125 int version;
1126
1127 len = sizeof(version);
1128 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1129 if (error) {
1130 snprintf(errstr, buflen, "version check failed: %s",
1131 strerror(errno));
1132 return (-1);
1133 }
1134 if (version != MB_VERSION) {
1135 snprintf(errstr, buflen, "module v%d != library v%d",
1136 version, MB_VERSION);
1137 return (-1);
1138 }
1139 return (0);
1140 }
1141
1142 int
bsde_get_rule_count(size_t buflen,char * errstr)1143 bsde_get_rule_count(size_t buflen, char *errstr)
1144 {
1145 size_t len;
1146 int error;
1147 int rule_count;
1148
1149 len = sizeof(rule_count);
1150 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1151 if (error) {
1152 snprintf(errstr, buflen, "%s", strerror(errno));
1153 return (-1);
1154 }
1155 if (len != sizeof(rule_count)) {
1156 snprintf(errstr, buflen, "Data error in %s.rule_count",
1157 MIB);
1158 return (-1);
1159 }
1160
1161 return (rule_count);
1162 }
1163
1164 int
bsde_get_rule_slots(size_t buflen,char * errstr)1165 bsde_get_rule_slots(size_t buflen, char *errstr)
1166 {
1167 size_t len;
1168 int error;
1169 int rule_slots;
1170
1171 len = sizeof(rule_slots);
1172 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1173 if (error) {
1174 snprintf(errstr, buflen, "%s", strerror(errno));
1175 return (-1);
1176 }
1177 if (len != sizeof(rule_slots)) {
1178 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1179 return (-1);
1180 }
1181
1182 return (rule_slots);
1183 }
1184
1185 /*
1186 * Returns 0 for success;
1187 * Returns -1 for failure;
1188 * Returns -2 for not present
1189 */
1190 int
bsde_get_rule(int rulenum,struct mac_bsdextended_rule * rule,size_t errlen,char * errstr)1191 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1192 char *errstr)
1193 {
1194 int name[10];
1195 size_t len, size;
1196 int error;
1197
1198 if (bsde_check_version(errlen, errstr) != 0)
1199 return (-1);
1200
1201 len = 10;
1202 error = bsde_get_mib(MIB ".rules", name, &len);
1203 if (error) {
1204 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1205 strerror(errno));
1206 return (-1);
1207 }
1208
1209 size = sizeof(*rule);
1210 name[len] = rulenum;
1211 len++;
1212 error = sysctl(name, len, rule, &size, NULL, 0);
1213 if (error == -1 && errno == ENOENT)
1214 return (-2);
1215 if (error) {
1216 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1217 rulenum, strerror(errno));
1218 return (-1);
1219 } else if (size != sizeof(*rule)) {
1220 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1221 MIB ".rules", rulenum, strerror(errno));
1222 return (-1);
1223 }
1224
1225 return (0);
1226 }
1227
1228 int
bsde_delete_rule(int rulenum,size_t buflen,char * errstr)1229 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1230 {
1231 struct mac_bsdextended_rule rule;
1232 int name[10];
1233 size_t len;
1234 int error;
1235
1236 if (bsde_check_version(buflen, errstr) != 0)
1237 return (-1);
1238
1239 len = 10;
1240 error = bsde_get_mib(MIB ".rules", name, &len);
1241 if (error) {
1242 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1243 strerror(errno));
1244 return (-1);
1245 }
1246
1247 name[len] = rulenum;
1248 len++;
1249
1250 error = sysctl(name, len, NULL, NULL, &rule, 0);
1251 if (error) {
1252 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1253 rulenum, strerror(errno));
1254 return (-1);
1255 }
1256
1257 return (0);
1258 }
1259
1260 int
bsde_set_rule(int rulenum,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1261 bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1262 char *errstr)
1263 {
1264 int name[10];
1265 size_t len;
1266 int error;
1267
1268 if (bsde_check_version(buflen, errstr) != 0)
1269 return (-1);
1270
1271 len = 10;
1272 error = bsde_get_mib(MIB ".rules", name, &len);
1273 if (error) {
1274 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1275 strerror(errno));
1276 return (-1);
1277 }
1278
1279 name[len] = rulenum;
1280 len++;
1281
1282 error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1283 if (error) {
1284 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1285 rulenum, strerror(errno));
1286 return (-1);
1287 }
1288
1289 return (0);
1290 }
1291
1292 int
bsde_add_rule(int * rulenum,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1293 bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1294 char *errstr)
1295 {
1296 char charstr[BUFSIZ];
1297 int name[10];
1298 size_t len;
1299 int error, rule_slots;
1300
1301 if (bsde_check_version(buflen, errstr) != 0)
1302 return (-1);
1303
1304 len = 10;
1305 error = bsde_get_mib(MIB ".rules", name, &len);
1306 if (error) {
1307 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1308 strerror(errno));
1309 return (-1);
1310 }
1311
1312 rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1313 if (rule_slots == -1) {
1314 snprintf(errstr, buflen, "unable to get rule slots: %s",
1315 strerror(errno));
1316 return (-1);
1317 }
1318
1319 name[len] = rule_slots;
1320 len++;
1321
1322 error = sysctl(name, len, NULL, NULL, rule, sizeof(*rule));
1323 if (error) {
1324 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1325 rule_slots, strerror(errno));
1326 return (-1);
1327 }
1328
1329 if (rulenum != NULL)
1330 *rulenum = rule_slots;
1331
1332 return (0);
1333 }
1334