ugidfw.c revision 325462
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 325462 2017-11-05 22:36:32Z ngie $
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
59int
60bsde_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
505truncated:
506	return (-1);
507}
508
509int
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
555int
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
601int
602bsde_parse_subject(int argc, char *argv[],
603    struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
604{
605	int not_seen, flags;
606	int current, neg, nextnot;
607	char *endp;
608	uid_t uid_min, uid_max;
609	gid_t gid_min, gid_max;
610	int jid;
611	long value;
612
613	current = 0;
614	flags = 0;
615	neg = 0;
616	nextnot = 0;
617
618	if (strcmp("not", argv[current]) == 0) {
619		not_seen = 1;
620		current++;
621	} else
622		not_seen = 0;
623
624	while (current < argc) {
625		if (strcmp(argv[current], "uid") == 0) {
626			if (current + 2 > argc) {
627				snprintf(errstr, buflen, "uid short");
628				return (-1);
629			}
630			if (flags & MBS_UID_DEFINED) {
631				snprintf(errstr, buflen, "one uid only");
632				return (-1);
633			}
634			if (bsde_parse_uidrange(argv[current+1],
635			    &uid_min, &uid_max, buflen, errstr) < 0)
636				return (-1);
637			flags |= MBS_UID_DEFINED;
638			if (nextnot) {
639				neg ^= MBS_UID_DEFINED;
640				nextnot = 0;
641			}
642			current += 2;
643		} else if (strcmp(argv[current], "gid") == 0) {
644			if (current + 2 > argc) {
645				snprintf(errstr, buflen, "gid short");
646				return (-1);
647			}
648			if (flags & MBS_GID_DEFINED) {
649				snprintf(errstr, buflen, "one gid only");
650				return (-1);
651			}
652			if (bsde_parse_gidrange(argv[current+1],
653			    &gid_min, &gid_max, buflen, errstr) < 0)
654				return (-1);
655			flags |= MBS_GID_DEFINED;
656			if (nextnot) {
657				neg ^= MBS_GID_DEFINED;
658				nextnot = 0;
659			}
660			current += 2;
661		} else if (strcmp(argv[current], "jailid") == 0) {
662			if (current + 2 > argc) {
663				snprintf(errstr, buflen, "prison short");
664				return (-1);
665			}
666			if (flags & MBS_PRISON_DEFINED) {
667				snprintf(errstr, buflen, "one jail only");
668				return (-1);
669			}
670			value = strtol(argv[current+1], &endp, 10);
671			if (*endp != '\0') {
672				snprintf(errstr, buflen, "invalid jid: '%s'",
673				    argv[current+1]);
674				return (-1);
675			}
676			jid = value;
677			flags |= MBS_PRISON_DEFINED;
678			if (nextnot) {
679				neg ^= MBS_PRISON_DEFINED;
680				nextnot = 0;
681			}
682			current += 2;
683		} else if (strcmp(argv[current], "!") == 0) {
684			if (nextnot) {
685				snprintf(errstr, buflen, "double negative");
686				return (-1);
687			}
688			nextnot = 1;
689			current += 1;
690		} else {
691			snprintf(errstr, buflen, "'%s' not expected",
692			    argv[current]);
693			return (-1);
694		}
695	}
696
697	subject->mbs_flags = flags;
698	if (not_seen)
699		subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
700	else
701		subject->mbs_neg = neg;
702	if (flags & MBS_UID_DEFINED) {
703		subject->mbs_uid_min = uid_min;
704		subject->mbs_uid_max = uid_max;
705	}
706	if (flags & MBS_GID_DEFINED) {
707		subject->mbs_gid_min = gid_min;
708		subject->mbs_gid_max = gid_max;
709	}
710	if (flags & MBS_PRISON_DEFINED)
711		subject->mbs_prison = jid;
712
713	return (0);
714}
715
716int
717bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
718{
719	int i;
720
721	*type = 0;
722	for (i = 0; i < strlen(spec); i++) {
723		switch (spec[i]) {
724		case 'r':
725		case '-':
726			*type |= MBO_TYPE_REG;
727			break;
728		case 'd':
729			*type |= MBO_TYPE_DIR;
730			break;
731		case 'b':
732			*type |= MBO_TYPE_BLK;
733			break;
734		case 'c':
735			*type |= MBO_TYPE_CHR;
736			break;
737		case 'l':
738			*type |= MBO_TYPE_LNK;
739			break;
740		case 's':
741			*type |= MBO_TYPE_SOCK;
742			break;
743		case 'p':
744			*type |= MBO_TYPE_FIFO;
745			break;
746		case 'a':
747			*type |= MBO_ALL_TYPE;
748			break;
749		default:
750			snprintf(errstr, buflen, "Unknown type code: %c",
751			    spec[i]);
752			return (-1);
753		}
754	}
755
756	return (0);
757}
758
759int
760bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
761{
762	struct statfs buf;
763
764	if (statfs(spec, &buf) < 0) {
765		snprintf(errstr, buflen, "Unable to get id for %s: %s",
766		    spec, strerror(errno));
767		return (-1);
768	}
769
770	*fsid = buf.f_fsid;
771
772	return (0);
773}
774
775int
776bsde_parse_object(int argc, char *argv[],
777    struct mac_bsdextended_object *object, size_t buflen, char *errstr)
778{
779	int not_seen, flags;
780	int current, neg, nextnot;
781	uid_t uid_min, uid_max;
782	gid_t gid_min, gid_max;
783	int type;
784	struct fsid fsid;
785
786	current = 0;
787	flags = 0;
788	neg = 0;
789	nextnot = 0;
790
791	if (strcmp("not", argv[current]) == 0) {
792		not_seen = 1;
793		current++;
794	} else
795		not_seen = 0;
796
797	while (current < argc) {
798		if (strcmp(argv[current], "uid") == 0) {
799			if (current + 2 > argc) {
800				snprintf(errstr, buflen, "uid short");
801				return (-1);
802			}
803			if (flags & MBO_UID_DEFINED) {
804				snprintf(errstr, buflen, "one uid only");
805				return (-1);
806			}
807			if (bsde_parse_uidrange(argv[current+1],
808			    &uid_min, &uid_max, buflen, errstr) < 0)
809				return (-1);
810			flags |= MBO_UID_DEFINED;
811			if (nextnot) {
812				neg ^= MBO_UID_DEFINED;
813				nextnot = 0;
814			}
815			current += 2;
816		} else if (strcmp(argv[current], "gid") == 0) {
817			if (current + 2 > argc) {
818				snprintf(errstr, buflen, "gid short");
819				return (-1);
820			}
821			if (flags & MBO_GID_DEFINED) {
822				snprintf(errstr, buflen, "one gid only");
823				return (-1);
824			}
825			if (bsde_parse_gidrange(argv[current+1],
826			    &gid_min, &gid_max, buflen, errstr) < 0)
827				return (-1);
828			flags |= MBO_GID_DEFINED;
829			if (nextnot) {
830				neg ^= MBO_GID_DEFINED;
831				nextnot = 0;
832			}
833			current += 2;
834		} else if (strcmp(argv[current], "filesys") == 0) {
835			if (current + 2 > argc) {
836				snprintf(errstr, buflen, "filesys short");
837				return (-1);
838			}
839			if (flags & MBO_FSID_DEFINED) {
840				snprintf(errstr, buflen, "one fsid only");
841				return (-1);
842			}
843			if (bsde_parse_fsid(argv[current+1], &fsid,
844			    buflen, errstr) < 0)
845				return (-1);
846			flags |= MBO_FSID_DEFINED;
847			if (nextnot) {
848				neg ^= MBO_FSID_DEFINED;
849				nextnot = 0;
850			}
851			current += 2;
852		} else if (strcmp(argv[current], "suid") == 0) {
853			flags |= MBO_SUID;
854			if (nextnot) {
855				neg ^= MBO_SUID;
856				nextnot = 0;
857			}
858			current += 1;
859		} else if (strcmp(argv[current], "sgid") == 0) {
860			flags |= MBO_SGID;
861			if (nextnot) {
862				neg ^= MBO_SGID;
863				nextnot = 0;
864			}
865			current += 1;
866		} else if (strcmp(argv[current], "uid_of_subject") == 0) {
867			flags |= MBO_UID_SUBJECT;
868			if (nextnot) {
869				neg ^= MBO_UID_SUBJECT;
870				nextnot = 0;
871			}
872			current += 1;
873		} else if (strcmp(argv[current], "gid_of_subject") == 0) {
874			flags |= MBO_GID_SUBJECT;
875			if (nextnot) {
876				neg ^= MBO_GID_SUBJECT;
877				nextnot = 0;
878			}
879			current += 1;
880		} else if (strcmp(argv[current], "type") == 0) {
881			if (current + 2 > argc) {
882				snprintf(errstr, buflen, "type short");
883				return (-1);
884			}
885			if (flags & MBO_TYPE_DEFINED) {
886				snprintf(errstr, buflen, "one type only");
887				return (-1);
888			}
889			if (bsde_parse_type(argv[current+1], &type,
890			    buflen, errstr) < 0)
891				return (-1);
892			flags |= MBO_TYPE_DEFINED;
893			if (nextnot) {
894				neg ^= MBO_TYPE_DEFINED;
895				nextnot = 0;
896			}
897			current += 2;
898		} else if (strcmp(argv[current], "!") == 0) {
899			if (nextnot) {
900				snprintf(errstr, buflen,
901				    "double negative'");
902				return (-1);
903			}
904			nextnot = 1;
905			current += 1;
906		} else {
907			snprintf(errstr, buflen, "'%s' not expected",
908			    argv[current]);
909			return (-1);
910		}
911	}
912
913	object->mbo_flags = flags;
914	if (not_seen)
915		object->mbo_neg = MBO_ALL_FLAGS ^ neg;
916	else
917		object->mbo_neg = neg;
918	if (flags & MBO_UID_DEFINED) {
919		object->mbo_uid_min = uid_min;
920		object->mbo_uid_max = uid_max;
921	}
922	if (flags & MBO_GID_DEFINED) {
923		object->mbo_gid_min = gid_min;
924		object->mbo_gid_max = gid_max;
925	}
926	if (flags & MBO_FSID_DEFINED)
927		object->mbo_fsid = fsid;
928	if (flags & MBO_TYPE_DEFINED)
929		object->mbo_type = type;
930
931	return (0);
932}
933
934int
935bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
936    char *errstr)
937{
938	int i;
939
940	if (argc == 0) {
941		snprintf(errstr, buflen, "mode expects mode value");
942		return (-1);
943	}
944
945	if (argc != 1) {
946		snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
947		return (-1);
948	}
949
950	*mode = 0;
951	for (i = 0; i < strlen(argv[0]); i++) {
952		switch (argv[0][i]) {
953		case 'a':
954			*mode |= MBI_ADMIN;
955			break;
956		case 'r':
957			*mode |= MBI_READ;
958			break;
959		case 's':
960			*mode |= MBI_STAT;
961			break;
962		case 'w':
963			*mode |= MBI_WRITE;
964			break;
965		case 'x':
966			*mode |= MBI_EXEC;
967			break;
968		case 'n':
969			/* ignore */
970			break;
971		default:
972			snprintf(errstr, buflen, "Unknown mode letter: %c",
973			    argv[0][i]);
974			return (-1);
975		}
976	}
977
978	return (0);
979}
980
981int
982bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
983    size_t buflen, char *errstr)
984{
985	int subject, subject_elements, subject_elements_length;
986	int object, object_elements, object_elements_length;
987	int mode, mode_elements, mode_elements_length;
988	int error, i;
989
990	bzero(rule, sizeof(*rule));
991
992	if (argc < 1) {
993		snprintf(errstr, buflen, "Rule must begin with subject");
994		return (-1);
995	}
996
997	if (strcmp(argv[0], "subject") != 0) {
998		snprintf(errstr, buflen, "Rule must begin with subject");
999		return (-1);
1000	}
1001	subject = 0;
1002	subject_elements = 1;
1003
1004	/* Search forward for object. */
1005
1006	object = -1;
1007	for (i = 1; i < argc; i++)
1008		if (strcmp(argv[i], "object") == 0)
1009			object = i;
1010
1011	if (object == -1) {
1012		snprintf(errstr, buflen, "Rule must contain an object");
1013		return (-1);
1014	}
1015
1016	/* Search forward for mode. */
1017	mode = -1;
1018	for (i = object; i < argc; i++)
1019		if (strcmp(argv[i], "mode") == 0)
1020			mode = i;
1021
1022	if (mode == -1) {
1023		snprintf(errstr, buflen, "Rule must contain mode");
1024		return (-1);
1025	}
1026
1027	subject_elements_length = object - subject - 1;
1028	object_elements = object + 1;
1029	object_elements_length = mode - object_elements;
1030	mode_elements = mode + 1;
1031	mode_elements_length = argc - mode_elements;
1032
1033	error = bsde_parse_subject(subject_elements_length,
1034	    argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1035	if (error)
1036		return (-1);
1037
1038	error = bsde_parse_object(object_elements_length,
1039	    argv + object_elements, &rule->mbr_object, buflen, errstr);
1040	if (error)
1041		return (-1);
1042
1043	error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1044	    &rule->mbr_mode, buflen, errstr);
1045	if (error)
1046		return (-1);
1047
1048	return (0);
1049}
1050
1051int
1052bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1053    size_t buflen, char *errstr)
1054{
1055	char *stringdup, *stringp, *argv[100], **ap;
1056	int argc, error;
1057
1058	stringp = stringdup = strdup(string);
1059	while (*stringp == ' ' || *stringp == '\t')
1060		stringp++;
1061
1062	argc = 0;
1063	for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1064		argc++;
1065		if (**ap != '\0')
1066			if (++ap >= &argv[100])
1067				break;
1068	}
1069
1070	error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1071
1072	free(stringdup);
1073
1074	return (error);
1075}
1076
1077int
1078bsde_get_mib(const char *string, int *name, size_t *namelen)
1079{
1080	size_t len;
1081	int error;
1082
1083	len = *namelen;
1084	error = sysctlnametomib(string, name, &len);
1085	if (error)
1086		return (error);
1087
1088	*namelen = len;
1089	return (0);
1090}
1091
1092int
1093bsde_check_version(size_t buflen, char *errstr)
1094{
1095	size_t len;
1096	int error;
1097	int version;
1098
1099	len = sizeof(version);
1100	error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1101	if (error) {
1102		snprintf(errstr, buflen, "version check failed: %s",
1103		    strerror(errno));
1104		return (-1);
1105	}
1106	if (version != MB_VERSION) {
1107		snprintf(errstr, buflen, "module v%d != library v%d",
1108		    version, MB_VERSION);
1109		return (-1);
1110	}
1111	return (0);
1112}
1113
1114int
1115bsde_get_rule_count(size_t buflen, char *errstr)
1116{
1117	size_t len;
1118	int error;
1119	int rule_count;
1120
1121	len = sizeof(rule_count);
1122	error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1123	if (error) {
1124		snprintf(errstr, buflen, "%s", strerror(errno));
1125		return (-1);
1126	}
1127	if (len != sizeof(rule_count)) {
1128		snprintf(errstr, buflen, "Data error in %s.rule_count",
1129		    MIB);
1130		return (-1);
1131	}
1132
1133	return (rule_count);
1134}
1135
1136int
1137bsde_get_rule_slots(size_t buflen, char *errstr)
1138{
1139	size_t len;
1140	int error;
1141	int rule_slots;
1142
1143	len = sizeof(rule_slots);
1144	error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1145	if (error) {
1146		snprintf(errstr, buflen, "%s", strerror(errno));
1147		return (-1);
1148	}
1149	if (len != sizeof(rule_slots)) {
1150		snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB);
1151		return (-1);
1152	}
1153
1154	return (rule_slots);
1155}
1156
1157/*
1158 * Returns 0 for success;
1159 * Returns -1 for failure;
1160 * Returns -2 for not present
1161 */
1162int
1163bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1164    char *errstr)
1165{
1166	int name[10];
1167	size_t len, size;
1168	int error;
1169
1170	if (bsde_check_version(errlen, errstr) != 0)
1171		return (-1);
1172
1173	len = 10;
1174	error = bsde_get_mib(MIB ".rules", name, &len);
1175	if (error) {
1176		snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1177		    strerror(errno));
1178		return (-1);
1179	}
1180
1181	size = sizeof(*rule);
1182	name[len] = rulenum;
1183	len++;
1184	error = sysctl(name, len, rule, &size, NULL, 0);
1185	if (error  == -1 && errno == ENOENT)
1186		return (-2);
1187	if (error) {
1188		snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1189		    rulenum, strerror(errno));
1190		return (-1);
1191	} else if (size != sizeof(*rule)) {
1192		snprintf(errstr, errlen, "Data error in %s.%d: %s",
1193		    MIB ".rules", rulenum, strerror(errno));
1194		return (-1);
1195	}
1196
1197	return (0);
1198}
1199
1200int
1201bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1202{
1203	struct mac_bsdextended_rule rule;
1204	int name[10];
1205	size_t len, size;
1206	int error;
1207
1208	if (bsde_check_version(buflen, errstr) != 0)
1209		return (-1);
1210
1211	len = 10;
1212	error = bsde_get_mib(MIB ".rules", name, &len);
1213	if (error) {
1214		snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1215		    strerror(errno));
1216		return (-1);
1217	}
1218
1219	name[len] = rulenum;
1220	len++;
1221
1222	size = sizeof(rule);
1223	error = sysctl(name, len, NULL, NULL, &rule, 0);
1224	if (error) {
1225		snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1226		    rulenum, strerror(errno));
1227		return (-1);
1228	}
1229
1230	return (0);
1231}
1232
1233int
1234bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1235    char *errstr)
1236{
1237	int name[10];
1238	size_t len, size;
1239	int error;
1240
1241	if (bsde_check_version(buflen, errstr) != 0)
1242		return (-1);
1243
1244	len = 10;
1245	error = bsde_get_mib(MIB ".rules", name, &len);
1246	if (error) {
1247		snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1248		    strerror(errno));
1249		return (-1);
1250	}
1251
1252	name[len] = rulenum;
1253	len++;
1254
1255	size = sizeof(*rule);
1256	error = sysctl(name, len, NULL, NULL, rule, size);
1257	if (error) {
1258		snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1259		    rulenum, strerror(errno));
1260		return (-1);
1261	}
1262
1263	return (0);
1264}
1265
1266int
1267bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1268    char *errstr)
1269{
1270	char charstr[BUFSIZ];
1271	int name[10];
1272	size_t len, size;
1273	int error, rule_slots;
1274
1275	if (bsde_check_version(buflen, errstr) != 0)
1276		return (-1);
1277
1278	len = 10;
1279	error = bsde_get_mib(MIB ".rules", name, &len);
1280	if (error) {
1281		snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1282		    strerror(errno));
1283		return (-1);
1284	}
1285
1286	rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1287	if (rule_slots == -1) {
1288		snprintf(errstr, buflen, "unable to get rule slots: %s",
1289		    strerror(errno));
1290		return (-1);
1291	}
1292
1293	name[len] = rule_slots;
1294	len++;
1295
1296	size = sizeof(*rule);
1297	error = sysctl(name, len, NULL, NULL, rule, size);
1298	if (error) {
1299		snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1300		    rule_slots, strerror(errno));
1301		return (-1);
1302	}
1303
1304	if (rulenum != NULL)
1305		*rulenum = rule_slots;
1306
1307	return (0);
1308}
1309