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