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