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