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