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