1 /*-
2 * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by Network Associates
6 * Laboratories, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD: stable/10/lib/libugidfw/ugidfw.c 336039 2018-07-06 19:10:07Z jamie $
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, truncated, numfs, i, notdone;
70
71 cur = buf;
72 left = buflen;
73 truncated = 0;
74
75 len = snprintf(cur, left, "subject ");
76 if (len < 0 || len > left)
77 goto truncated;
78 left -= len;
79 cur += len;
80 if (rule->mbr_subject.mbs_flags) {
81 if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) {
82 len = snprintf(cur, left, "not ");
83 if (len < 0 || len > left)
84 goto truncated;
85 left -= len;
86 cur += len;
87 notdone = 1;
88 } else {
89 notdone = 0;
90 }
91
92 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) {
93 len = snprintf(cur, left, "! ");
94 if (len < 0 || len > left)
95 goto truncated;
96 left -= len;
97 cur += len;
98 }
99 if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) {
100 pwd = getpwuid(rule->mbr_subject.mbs_uid_min);
101 if (pwd != NULL) {
102 len = snprintf(cur, left, "uid %s",
103 pwd->pw_name);
104 if (len < 0 || len > left)
105 goto truncated;
106 left -= len;
107 cur += len;
108 } else {
109 len = snprintf(cur, left, "uid %u",
110 rule->mbr_subject.mbs_uid_min);
111 if (len < 0 || len > left)
112 goto truncated;
113 left -= len;
114 cur += len;
115 }
116 if (rule->mbr_subject.mbs_uid_min !=
117 rule->mbr_subject.mbs_uid_max) {
118 pwd = getpwuid(rule->mbr_subject.mbs_uid_max);
119 if (pwd != NULL) {
120 len = snprintf(cur, left, ":%s ",
121 pwd->pw_name);
122 if (len < 0 || len > left)
123 goto truncated;
124 left -= len;
125 cur += len;
126 } else {
127 len = snprintf(cur, left, ":%u ",
128 rule->mbr_subject.mbs_uid_max);
129 if (len < 0 || len > left)
130 goto truncated;
131 left -= len;
132 cur += len;
133 }
134 } else {
135 len = snprintf(cur, left, " ");
136 if (len < 0 || len > left)
137 goto truncated;
138 left -= len;
139 cur += len;
140 }
141 }
142 if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) {
143 len = snprintf(cur, left, "! ");
144 if (len < 0 || len > left)
145 goto truncated;
146 left -= len;
147 cur += len;
148 }
149 if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) {
150 grp = getgrgid(rule->mbr_subject.mbs_gid_min);
151 if (grp != NULL) {
152 len = snprintf(cur, left, "gid %s",
153 grp->gr_name);
154 if (len < 0 || len > left)
155 goto truncated;
156 left -= len;
157 cur += len;
158 } else {
159 len = snprintf(cur, left, "gid %u",
160 rule->mbr_subject.mbs_gid_min);
161 if (len < 0 || len > left)
162 goto truncated;
163 left -= len;
164 cur += len;
165 }
166 if (rule->mbr_subject.mbs_gid_min !=
167 rule->mbr_subject.mbs_gid_max) {
168 grp = getgrgid(rule->mbr_subject.mbs_gid_max);
169 if (grp != NULL) {
170 len = snprintf(cur, left, ":%s ",
171 grp->gr_name);
172 if (len < 0 || len > left)
173 goto truncated;
174 left -= len;
175 cur += len;
176 } else {
177 len = snprintf(cur, left, ":%u ",
178 rule->mbr_subject.mbs_gid_max);
179 if (len < 0 || len > left)
180 goto truncated;
181 left -= len;
182 cur += len;
183 }
184 } else {
185 len = snprintf(cur, left, " ");
186 if (len < 0 || len > left)
187 goto truncated;
188 left -= len;
189 cur += len;
190 }
191 }
192 if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) {
193 len = snprintf(cur, left, "! ");
194 if (len < 0 || len > left)
195 goto truncated;
196 left -= len;
197 cur += len;
198 }
199 if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) {
200 len = snprintf(cur, left, "jailid %d ",
201 rule->mbr_subject.mbs_prison);
202 if (len < 0 || len > left)
203 goto truncated;
204 left -= len;
205 cur += len;
206 }
207 }
208
209 len = snprintf(cur, left, "object ");
210 if (len < 0 || len > left)
211 goto truncated;
212 left -= len;
213 cur += len;
214 if (rule->mbr_object.mbo_flags) {
215 if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) {
216 len = snprintf(cur, left, "not ");
217 if (len < 0 || len > left)
218 goto truncated;
219 left -= len;
220 cur += len;
221 notdone = 1;
222 } else {
223 notdone = 0;
224 }
225
226 if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) {
227 len = snprintf(cur, left, "! ");
228 if (len < 0 || len > left)
229 goto truncated;
230 left -= len;
231 cur += len;
232 }
233 if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) {
234 pwd = getpwuid(rule->mbr_object.mbo_uid_min);
235 if (pwd != NULL) {
236 len = snprintf(cur, left, "uid %s",
237 pwd->pw_name);
238 if (len < 0 || len > left)
239 goto truncated;
240 left -= len;
241 cur += len;
242 } else {
243 len = snprintf(cur, left, "uid %u",
244 rule->mbr_object.mbo_uid_min);
245 if (len < 0 || len > left)
246 goto truncated;
247 left -= len;
248 cur += len;
249 }
250 if (rule->mbr_object.mbo_uid_min !=
251 rule->mbr_object.mbo_uid_max) {
252 pwd = getpwuid(rule->mbr_object.mbo_uid_max);
253 if (pwd != NULL) {
254 len = snprintf(cur, left, ":%s ",
255 pwd->pw_name);
256 if (len < 0 || len > left)
257 goto truncated;
258 left -= len;
259 cur += len;
260 } else {
261 len = snprintf(cur, left, ":%u ",
262 rule->mbr_object.mbo_uid_max);
263 if (len < 0 || len > left)
264 goto truncated;
265 left -= len;
266 cur += len;
267 }
268 } else {
269 len = snprintf(cur, left, " ");
270 if (len < 0 || len > left)
271 goto truncated;
272 left -= len;
273 cur += len;
274 }
275 }
276 if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) {
277 len = snprintf(cur, left, "! ");
278 if (len < 0 || len > left)
279 goto truncated;
280 left -= len;
281 cur += len;
282 }
283 if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) {
284 grp = getgrgid(rule->mbr_object.mbo_gid_min);
285 if (grp != NULL) {
286 len = snprintf(cur, left, "gid %s",
287 grp->gr_name);
288 if (len < 0 || len > left)
289 goto truncated;
290 left -= len;
291 cur += len;
292 } else {
293 len = snprintf(cur, left, "gid %u",
294 rule->mbr_object.mbo_gid_min);
295 if (len < 0 || len > left)
296 goto truncated;
297 left -= len;
298 cur += len;
299 }
300 if (rule->mbr_object.mbo_gid_min !=
301 rule->mbr_object.mbo_gid_max) {
302 grp = getgrgid(rule->mbr_object.mbo_gid_max);
303 if (grp != NULL) {
304 len = snprintf(cur, left, ":%s ",
305 grp->gr_name);
306 if (len < 0 || len > left)
307 goto truncated;
308 left -= len;
309 cur += len;
310 } else {
311 len = snprintf(cur, left, ":%u ",
312 rule->mbr_object.mbo_gid_max);
313 if (len < 0 || len > left)
314 goto truncated;
315 left -= len;
316 cur += len;
317 }
318 } else {
319 len = snprintf(cur, left, " ");
320 if (len < 0 || len > left)
321 goto truncated;
322 left -= len;
323 cur += len;
324 }
325 }
326 if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) {
327 len = snprintf(cur, left, "! ");
328 if (len < 0 || len > left)
329 goto truncated;
330 left -= len;
331 cur += len;
332 }
333 if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) {
334 numfs = getmntinfo(&mntbuf, MNT_NOWAIT);
335 for (i = 0; i < numfs; i++)
336 if (memcmp(&(rule->mbr_object.mbo_fsid),
337 &(mntbuf[i].f_fsid),
338 sizeof(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 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 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 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;
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 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 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 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 uid_t uid_min, uid_max;
809 gid_t gid_min, gid_max;
810 int type;
811 struct fsid fsid;
812
813 current = 0;
814 flags = 0;
815 neg = 0;
816 nextnot = 0;
817
818 if (strcmp("not", argv[current]) == 0) {
819 not_seen = 1;
820 current++;
821 } else
822 not_seen = 0;
823
824 while (current < argc) {
825 if (strcmp(argv[current], "uid") == 0) {
826 if (current + 2 > argc) {
827 snprintf(errstr, buflen, "uid short");
828 return (-1);
829 }
830 if (flags & MBO_UID_DEFINED) {
831 snprintf(errstr, buflen, "one uid only");
832 return (-1);
833 }
834 if (bsde_parse_uidrange(argv[current+1],
835 &uid_min, &uid_max, buflen, errstr) < 0)
836 return (-1);
837 flags |= MBO_UID_DEFINED;
838 if (nextnot) {
839 neg ^= MBO_UID_DEFINED;
840 nextnot = 0;
841 }
842 current += 2;
843 } else if (strcmp(argv[current], "gid") == 0) {
844 if (current + 2 > argc) {
845 snprintf(errstr, buflen, "gid short");
846 return (-1);
847 }
848 if (flags & MBO_GID_DEFINED) {
849 snprintf(errstr, buflen, "one gid only");
850 return (-1);
851 }
852 if (bsde_parse_gidrange(argv[current+1],
853 &gid_min, &gid_max, buflen, errstr) < 0)
854 return (-1);
855 flags |= MBO_GID_DEFINED;
856 if (nextnot) {
857 neg ^= MBO_GID_DEFINED;
858 nextnot = 0;
859 }
860 current += 2;
861 } else if (strcmp(argv[current], "filesys") == 0) {
862 if (current + 2 > argc) {
863 snprintf(errstr, buflen, "filesys short");
864 return (-1);
865 }
866 if (flags & MBO_FSID_DEFINED) {
867 snprintf(errstr, buflen, "one fsid only");
868 return (-1);
869 }
870 if (bsde_parse_fsid(argv[current+1], &fsid,
871 buflen, errstr) < 0)
872 return (-1);
873 flags |= MBO_FSID_DEFINED;
874 if (nextnot) {
875 neg ^= MBO_FSID_DEFINED;
876 nextnot = 0;
877 }
878 current += 2;
879 } else if (strcmp(argv[current], "suid") == 0) {
880 flags |= MBO_SUID;
881 if (nextnot) {
882 neg ^= MBO_SUID;
883 nextnot = 0;
884 }
885 current += 1;
886 } else if (strcmp(argv[current], "sgid") == 0) {
887 flags |= MBO_SGID;
888 if (nextnot) {
889 neg ^= MBO_SGID;
890 nextnot = 0;
891 }
892 current += 1;
893 } else if (strcmp(argv[current], "uid_of_subject") == 0) {
894 flags |= MBO_UID_SUBJECT;
895 if (nextnot) {
896 neg ^= MBO_UID_SUBJECT;
897 nextnot = 0;
898 }
899 current += 1;
900 } else if (strcmp(argv[current], "gid_of_subject") == 0) {
901 flags |= MBO_GID_SUBJECT;
902 if (nextnot) {
903 neg ^= MBO_GID_SUBJECT;
904 nextnot = 0;
905 }
906 current += 1;
907 } else if (strcmp(argv[current], "type") == 0) {
908 if (current + 2 > argc) {
909 snprintf(errstr, buflen, "type short");
910 return (-1);
911 }
912 if (flags & MBO_TYPE_DEFINED) {
913 snprintf(errstr, buflen, "one type only");
914 return (-1);
915 }
916 if (bsde_parse_type(argv[current+1], &type,
917 buflen, errstr) < 0)
918 return (-1);
919 flags |= MBO_TYPE_DEFINED;
920 if (nextnot) {
921 neg ^= MBO_TYPE_DEFINED;
922 nextnot = 0;
923 }
924 current += 2;
925 } else if (strcmp(argv[current], "!") == 0) {
926 if (nextnot) {
927 snprintf(errstr, buflen,
928 "double negative'");
929 return (-1);
930 }
931 nextnot = 1;
932 current += 1;
933 } else {
934 snprintf(errstr, buflen, "'%s' not expected",
935 argv[current]);
936 return (-1);
937 }
938 }
939
940 object->mbo_flags = flags;
941 if (not_seen)
942 object->mbo_neg = MBO_ALL_FLAGS ^ neg;
943 else
944 object->mbo_neg = neg;
945 if (flags & MBO_UID_DEFINED) {
946 object->mbo_uid_min = uid_min;
947 object->mbo_uid_max = uid_max;
948 }
949 if (flags & MBO_GID_DEFINED) {
950 object->mbo_gid_min = gid_min;
951 object->mbo_gid_max = gid_max;
952 }
953 if (flags & MBO_FSID_DEFINED)
954 object->mbo_fsid = fsid;
955 if (flags & MBO_TYPE_DEFINED)
956 object->mbo_type = type;
957
958 return (0);
959 }
960
961 int
bsde_parse_mode(int argc,char * argv[],mode_t * mode,size_t buflen,char * errstr)962 bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
963 char *errstr)
964 {
965 int i;
966
967 if (argc == 0) {
968 snprintf(errstr, buflen, "mode expects mode value");
969 return (-1);
970 }
971
972 if (argc != 1) {
973 snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
974 return (-1);
975 }
976
977 *mode = 0;
978 for (i = 0; i < strlen(argv[0]); i++) {
979 switch (argv[0][i]) {
980 case 'a':
981 *mode |= MBI_ADMIN;
982 break;
983 case 'r':
984 *mode |= MBI_READ;
985 break;
986 case 's':
987 *mode |= MBI_STAT;
988 break;
989 case 'w':
990 *mode |= MBI_WRITE;
991 break;
992 case 'x':
993 *mode |= MBI_EXEC;
994 break;
995 case 'n':
996 /* ignore */
997 break;
998 default:
999 snprintf(errstr, buflen, "Unknown mode letter: %c",
1000 argv[0][i]);
1001 return (-1);
1002 }
1003 }
1004
1005 return (0);
1006 }
1007
1008 int
bsde_parse_rule(int argc,char * argv[],struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1009 bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
1010 size_t buflen, char *errstr)
1011 {
1012 int subject, subject_elements, subject_elements_length;
1013 int object, object_elements, object_elements_length;
1014 int mode, mode_elements, mode_elements_length;
1015 int error, i;
1016
1017 bzero(rule, sizeof(*rule));
1018
1019 if (argc < 1) {
1020 snprintf(errstr, buflen, "Rule must begin with subject");
1021 return (-1);
1022 }
1023
1024 if (strcmp(argv[0], "subject") != 0) {
1025 snprintf(errstr, buflen, "Rule must begin with subject");
1026 return (-1);
1027 }
1028 subject = 0;
1029 subject_elements = 1;
1030
1031 /* Search forward for object. */
1032
1033 object = -1;
1034 for (i = 1; i < argc; i++)
1035 if (strcmp(argv[i], "object") == 0)
1036 object = i;
1037
1038 if (object == -1) {
1039 snprintf(errstr, buflen, "Rule must contain an object");
1040 return (-1);
1041 }
1042
1043 /* Search forward for mode. */
1044 mode = -1;
1045 for (i = object; i < argc; i++)
1046 if (strcmp(argv[i], "mode") == 0)
1047 mode = i;
1048
1049 if (mode == -1) {
1050 snprintf(errstr, buflen, "Rule must contain mode");
1051 return (-1);
1052 }
1053
1054 subject_elements_length = object - subject - 1;
1055 object_elements = object + 1;
1056 object_elements_length = mode - object_elements;
1057 mode_elements = mode + 1;
1058 mode_elements_length = argc - mode_elements;
1059
1060 error = bsde_parse_subject(subject_elements_length,
1061 argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1062 if (error)
1063 return (-1);
1064
1065 error = bsde_parse_object(object_elements_length,
1066 argv + object_elements, &rule->mbr_object, buflen, errstr);
1067 if (error)
1068 return (-1);
1069
1070 error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1071 &rule->mbr_mode, buflen, errstr);
1072 if (error)
1073 return (-1);
1074
1075 return (0);
1076 }
1077
1078 int
bsde_parse_rule_string(const char * string,struct mac_bsdextended_rule * rule,size_t buflen,char * errstr)1079 bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1080 size_t buflen, char *errstr)
1081 {
1082 char *stringdup, *stringp, *argv[100], **ap;
1083 int argc, error;
1084
1085 stringp = stringdup = strdup(string);
1086 while (*stringp == ' ' || *stringp == '\t')
1087 stringp++;
1088
1089 argc = 0;
1090 for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1091 argc++;
1092 if (**ap != '\0')
1093 if (++ap >= &argv[100])
1094 break;
1095 }
1096
1097 error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1098
1099 free(stringdup);
1100
1101 return (error);
1102 }
1103
1104 int
bsde_get_mib(const char * string,int * name,size_t * namelen)1105 bsde_get_mib(const char *string, int *name, size_t *namelen)
1106 {
1107 size_t len;
1108 int error;
1109
1110 len = *namelen;
1111 error = sysctlnametomib(string, name, &len);
1112 if (error)
1113 return (error);
1114
1115 *namelen = len;
1116 return (0);
1117 }
1118
1119 int
bsde_check_version(size_t buflen,char * errstr)1120 bsde_check_version(size_t buflen, char *errstr)
1121 {
1122 size_t len;
1123 int error;
1124 int version;
1125
1126 len = sizeof(version);
1127 error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1128 if (error) {
1129 snprintf(errstr, buflen, "version check failed: %s",
1130 strerror(errno));
1131 return (-1);
1132 }
1133 if (version != MB_VERSION) {
1134 snprintf(errstr, buflen, "module v%d != library v%d",
1135 version, MB_VERSION);
1136 return (-1);
1137 }
1138 return (0);
1139 }
1140
1141 int
bsde_get_rule_count(size_t buflen,char * errstr)1142 bsde_get_rule_count(size_t buflen, char *errstr)
1143 {
1144 size_t len;
1145 int error;
1146 int rule_count;
1147
1148 len = sizeof(rule_count);
1149 error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1150 if (error) {
1151 snprintf(errstr, buflen, "%s", strerror(errno));
1152 return (-1);
1153 }
1154 if (len != sizeof(rule_count)) {
1155 snprintf(errstr, buflen, "Data error in %s.rule_count",
1156 MIB);
1157 return (-1);
1158 }
1159
1160 return (rule_count);
1161 }
1162
1163 int
bsde_get_rule_slots(size_t buflen,char * errstr)1164 bsde_get_rule_slots(size_t buflen, char *errstr)
1165 {
1166 size_t len;
1167 int error;
1168 int rule_slots;
1169
1170 len = sizeof(rule_slots);
1171 error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1172 if (error) {
1173 snprintf(errstr, buflen, "%s", strerror(errno));
1174 return (-1);
1175 }
1176 if (len != sizeof(rule_slots)) {
1177 snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1178 return (-1);
1179 }
1180
1181 return (rule_slots);
1182 }
1183
1184 /*
1185 * Returns 0 for success;
1186 * Returns -1 for failure;
1187 * Returns -2 for not present
1188 */
1189 int
bsde_get_rule(int rulenum,struct mac_bsdextended_rule * rule,size_t errlen,char * errstr)1190 bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1191 char *errstr)
1192 {
1193 int name[10];
1194 size_t len, size;
1195 int error;
1196
1197 if (bsde_check_version(errlen, errstr) != 0)
1198 return (-1);
1199
1200 len = 10;
1201 error = bsde_get_mib(MIB ".rules", name, &len);
1202 if (error) {
1203 snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1204 strerror(errno));
1205 return (-1);
1206 }
1207
1208 size = sizeof(*rule);
1209 name[len] = rulenum;
1210 len++;
1211 error = sysctl(name, len, rule, &size, NULL, 0);
1212 if (error == -1 && errno == ENOENT)
1213 return (-2);
1214 if (error) {
1215 snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1216 rulenum, strerror(errno));
1217 return (-1);
1218 } else if (size != sizeof(*rule)) {
1219 snprintf(errstr, errlen, "Data error in %s.%d: %s",
1220 MIB ".rules", rulenum, strerror(errno));
1221 return (-1);
1222 }
1223
1224 return (0);
1225 }
1226
1227 int
bsde_delete_rule(int rulenum,size_t buflen,char * errstr)1228 bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1229 {
1230 struct mac_bsdextended_rule rule;
1231 int name[10];
1232 size_t len, size;
1233 int error;
1234
1235 if (bsde_check_version(buflen, errstr) != 0)
1236 return (-1);
1237
1238 len = 10;
1239 error = bsde_get_mib(MIB ".rules", name, &len);
1240 if (error) {
1241 snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1242 strerror(errno));
1243 return (-1);
1244 }
1245
1246 name[len] = rulenum;
1247 len++;
1248
1249 size = sizeof(rule);
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, size;
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 size = sizeof(*rule);
1283 error = sysctl(name, len, NULL, NULL, rule, size);
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, size;
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 size = sizeof(*rule);
1324 error = sysctl(name, len, NULL, NULL, rule, size);
1325 if (error) {
1326 snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1327 rule_slots, strerror(errno));
1328 return (-1);
1329 }
1330
1331 if (rulenum != NULL)
1332 *rulenum = rule_slots;
1333
1334 return (0);
1335 }
1336