1/*-
2 * Copyright (c) 2002-2005 Networks Associates Technology, Inc.
3 * All rights reserved.
4 *
5 * This software was developed for the FreeBSD Project by Network Associates
6 * Laboratories, the Security Research Division of Network Associates, Inc.
7 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
8 * DARPA CHATS research program.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 *
31 * $FreeBSD$
32 */
33#include <sys/param.h>
34#include <sys/errno.h>
35#include <sys/time.h>
36#include <sys/sysctl.h>
37#include <sys/ucred.h>
38#include <sys/mount.h>
39
40#include <security/mac_bsdextended/mac_bsdextended.h>
41
42#include <grp.h>
43#include <pwd.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47
48#include "ugidfw.h"
49
50/*
51 * Text format for rules: rules contain subject and object elements, mode.
52 * The total form is "subject [s_element] object [o_element] mode [mode]".
53 * At least * one of a uid or gid entry must be present; both may also be
54 * present.
55 */
56
57#define	MIB	"security.mac.bsdextended"
58
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	size_t len;
517
518	spec2 = spec;
519	spec1 = strsep(&spec2, ":");
520
521	pwd = getpwnam(spec1);
522	if (pwd != NULL)
523		uid1 = pwd->pw_uid;
524	else {
525		value = strtoul(spec1, &endp, 10);
526		if (*endp != '\0') {
527			len = snprintf(errstr, buflen,
528			    "invalid uid: '%s'", spec1);
529			return (-1);
530		}
531		uid1 = value;
532	}
533
534	if (spec2 == NULL) {
535		*max = *min = uid1;
536		return (0);
537	}
538
539	pwd = getpwnam(spec2);
540	if (pwd != NULL)
541		uid2 = pwd->pw_uid;
542	else {
543		value = strtoul(spec2, &endp, 10);
544		if (*endp != '\0') {
545			len = snprintf(errstr, buflen,
546			    "invalid uid: '%s'", spec2);
547			return (-1);
548		}
549		uid2 = value;
550	}
551
552	*min = uid1;
553	*max = uid2;
554
555	return (0);
556}
557
558int
559bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max,
560    size_t buflen, char *errstr){
561	struct group *grp;
562	gid_t gid1, gid2;
563	char *spec1, *spec2, *endp;
564	unsigned long value;
565	size_t len;
566
567	spec2 = spec;
568	spec1 = strsep(&spec2, ":");
569
570	grp = getgrnam(spec1);
571	if (grp != NULL)
572		gid1 = grp->gr_gid;
573	else {
574		value = strtoul(spec1, &endp, 10);
575		if (*endp != '\0') {
576			len = snprintf(errstr, buflen,
577			    "invalid gid: '%s'", spec1);
578			return (-1);
579		}
580		gid1 = value;
581	}
582
583	if (spec2 == NULL) {
584		*max = *min = gid1;
585		return (0);
586	}
587
588	grp = getgrnam(spec2);
589	if (grp != NULL)
590		gid2 = grp->gr_gid;
591	else {
592		value = strtoul(spec2, &endp, 10);
593		if (*endp != '\0') {
594			len = snprintf(errstr, buflen,
595			    "invalid gid: '%s'", spec2);
596			return (-1);
597		}
598		gid2 = value;
599	}
600
601	*min = gid1;
602	*max = gid2;
603
604	return (0);
605}
606
607int
608bsde_parse_subject(int argc, char *argv[],
609    struct mac_bsdextended_subject *subject, size_t buflen, char *errstr)
610{
611	int not_seen, flags;
612	int current, neg, nextnot;
613	char *endp;
614	uid_t uid_min, uid_max;
615	gid_t gid_min, gid_max;
616	int jid;
617	size_t len;
618	long value;
619
620	current = 0;
621	flags = 0;
622	neg = 0;
623	nextnot = 0;
624
625	if (strcmp("not", argv[current]) == 0) {
626		not_seen = 1;
627		current++;
628	} else
629		not_seen = 0;
630
631	while (current < argc) {
632		if (strcmp(argv[current], "uid") == 0) {
633			if (current + 2 > argc) {
634				len = snprintf(errstr, buflen, "uid short");
635				return (-1);
636			}
637			if (flags & MBS_UID_DEFINED) {
638				len = snprintf(errstr, buflen, "one uid only");
639				return (-1);
640			}
641			if (bsde_parse_uidrange(argv[current+1],
642			    &uid_min, &uid_max, buflen, errstr) < 0)
643				return (-1);
644			flags |= MBS_UID_DEFINED;
645			if (nextnot) {
646				neg ^= MBS_UID_DEFINED;
647				nextnot = 0;
648			}
649			current += 2;
650		} else if (strcmp(argv[current], "gid") == 0) {
651			if (current + 2 > argc) {
652				len = snprintf(errstr, buflen, "gid short");
653				return (-1);
654			}
655			if (flags & MBS_GID_DEFINED) {
656				len = snprintf(errstr, buflen, "one gid only");
657				return (-1);
658			}
659			if (bsde_parse_gidrange(argv[current+1],
660			    &gid_min, &gid_max, buflen, errstr) < 0)
661				return (-1);
662			flags |= MBS_GID_DEFINED;
663			if (nextnot) {
664				neg ^= MBS_GID_DEFINED;
665				nextnot = 0;
666			}
667			current += 2;
668		} else if (strcmp(argv[current], "jailid") == 0) {
669			if (current + 2 > argc) {
670				len = snprintf(errstr, buflen, "prison short");
671				return (-1);
672			}
673			if (flags & MBS_PRISON_DEFINED) {
674				len = snprintf(errstr, buflen, "one jail only");
675				return (-1);
676			}
677			value = strtol(argv[current+1], &endp, 10);
678			if (*endp != '\0') {
679				len = snprintf(errstr, buflen,
680				    "invalid jid: '%s'", argv[current+1]);
681				return (-1);
682			}
683			jid = value;
684			flags |= MBS_PRISON_DEFINED;
685			if (nextnot) {
686				neg ^= MBS_PRISON_DEFINED;
687				nextnot = 0;
688			}
689			current += 2;
690		} else if (strcmp(argv[current], "!") == 0) {
691			if (nextnot) {
692				len = snprintf(errstr, buflen,
693				    "double negative");
694				return (-1);
695			}
696			nextnot = 1;
697			current += 1;
698		} else {
699			len = snprintf(errstr, buflen, "'%s' not expected",
700			    argv[current]);
701			return (-1);
702		}
703	}
704
705	subject->mbs_flags = flags;
706	if (not_seen)
707		subject->mbs_neg = MBS_ALL_FLAGS ^ neg;
708	else
709		subject->mbs_neg = neg;
710	if (flags & MBS_UID_DEFINED) {
711		subject->mbs_uid_min = uid_min;
712		subject->mbs_uid_max = uid_max;
713	}
714	if (flags & MBS_GID_DEFINED) {
715		subject->mbs_gid_min = gid_min;
716		subject->mbs_gid_max = gid_max;
717	}
718	if (flags & MBS_PRISON_DEFINED)
719		subject->mbs_prison = jid;
720
721	return (0);
722}
723
724int
725bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr)
726{
727	size_t len;
728	int i;
729
730	*type = 0;
731	for (i = 0; i < strlen(spec); i++) {
732		switch (spec[i]) {
733		case 'r':
734		case '-':
735			*type |= MBO_TYPE_REG;
736			break;
737		case 'd':
738			*type |= MBO_TYPE_DIR;
739			break;
740		case 'b':
741			*type |= MBO_TYPE_BLK;
742			break;
743		case 'c':
744			*type |= MBO_TYPE_CHR;
745			break;
746		case 'l':
747			*type |= MBO_TYPE_LNK;
748			break;
749		case 's':
750			*type |= MBO_TYPE_SOCK;
751			break;
752		case 'p':
753			*type |= MBO_TYPE_FIFO;
754			break;
755		case 'a':
756			*type |= MBO_ALL_TYPE;
757			break;
758		default:
759			len = snprintf(errstr, buflen, "Unknown type code: %c",
760			    spec[i]);
761			return (-1);
762		}
763	}
764
765	return (0);
766}
767
768int
769bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr)
770{
771	size_t len;
772	struct statfs buf;
773
774	if (statfs(spec, &buf) < 0) {
775		len = snprintf(errstr, buflen, "Unable to get id for %s: %s",
776		    spec, strerror(errno));
777		return (-1);
778	}
779
780	*fsid = buf.f_fsid;
781
782	return (0);
783}
784
785int
786bsde_parse_object(int argc, char *argv[],
787    struct mac_bsdextended_object *object, size_t buflen, char *errstr)
788{
789	int not_seen, flags;
790	int current, neg, nextnot;
791	uid_t uid_min, uid_max;
792	gid_t gid_min, gid_max;
793	int type;
794	struct fsid fsid;
795	size_t len;
796
797	current = 0;
798	flags = 0;
799	neg = 0;
800	nextnot = 0;
801
802	if (strcmp("not", argv[current]) == 0) {
803		not_seen = 1;
804		current++;
805	} else
806		not_seen = 0;
807
808	while (current < argc) {
809		if (strcmp(argv[current], "uid") == 0) {
810			if (current + 2 > argc) {
811				len = snprintf(errstr, buflen, "uid short");
812				return (-1);
813			}
814			if (flags & MBO_UID_DEFINED) {
815				len = snprintf(errstr, buflen, "one uid only");
816				return (-1);
817			}
818			if (bsde_parse_uidrange(argv[current+1],
819			    &uid_min, &uid_max, buflen, errstr) < 0)
820				return (-1);
821			flags |= MBO_UID_DEFINED;
822			if (nextnot) {
823				neg ^= MBO_UID_DEFINED;
824				nextnot = 0;
825			}
826			current += 2;
827		} else if (strcmp(argv[current], "gid") == 0) {
828			if (current + 2 > argc) {
829				len = snprintf(errstr, buflen, "gid short");
830				return (-1);
831			}
832			if (flags & MBO_GID_DEFINED) {
833				len = snprintf(errstr, buflen, "one gid only");
834				return (-1);
835			}
836			if (bsde_parse_gidrange(argv[current+1],
837			    &gid_min, &gid_max, buflen, errstr) < 0)
838				return (-1);
839			flags |= MBO_GID_DEFINED;
840			if (nextnot) {
841				neg ^= MBO_GID_DEFINED;
842				nextnot = 0;
843			}
844			current += 2;
845		} else if (strcmp(argv[current], "filesys") == 0) {
846			if (current + 2 > argc) {
847				len = snprintf(errstr, buflen, "filesys short");
848				return (-1);
849			}
850			if (flags & MBO_FSID_DEFINED) {
851				len = snprintf(errstr, buflen, "one fsid only");
852				return (-1);
853			}
854			if (bsde_parse_fsid(argv[current+1], &fsid,
855			    buflen, errstr) < 0)
856				return (-1);
857			flags |= MBO_FSID_DEFINED;
858			if (nextnot) {
859				neg ^= MBO_FSID_DEFINED;
860				nextnot = 0;
861			}
862			current += 2;
863		} else if (strcmp(argv[current], "suid") == 0) {
864			flags |= MBO_SUID;
865			if (nextnot) {
866				neg ^= MBO_SUID;
867				nextnot = 0;
868			}
869			current += 1;
870		} else if (strcmp(argv[current], "sgid") == 0) {
871			flags |= MBO_SGID;
872			if (nextnot) {
873				neg ^= MBO_SGID;
874				nextnot = 0;
875			}
876			current += 1;
877		} else if (strcmp(argv[current], "uid_of_subject") == 0) {
878			flags |= MBO_UID_SUBJECT;
879			if (nextnot) {
880				neg ^= MBO_UID_SUBJECT;
881				nextnot = 0;
882			}
883			current += 1;
884		} else if (strcmp(argv[current], "gid_of_subject") == 0) {
885			flags |= MBO_GID_SUBJECT;
886			if (nextnot) {
887				neg ^= MBO_GID_SUBJECT;
888				nextnot = 0;
889			}
890			current += 1;
891		} else if (strcmp(argv[current], "type") == 0) {
892			if (current + 2 > argc) {
893				len = snprintf(errstr, buflen, "type short");
894				return (-1);
895			}
896			if (flags & MBO_TYPE_DEFINED) {
897				len = snprintf(errstr, buflen, "one type only");
898				return (-1);
899			}
900			if (bsde_parse_type(argv[current+1], &type,
901			    buflen, errstr) < 0)
902				return (-1);
903			flags |= MBO_TYPE_DEFINED;
904			if (nextnot) {
905				neg ^= MBO_TYPE_DEFINED;
906				nextnot = 0;
907			}
908			current += 2;
909		} else if (strcmp(argv[current], "!") == 0) {
910			if (nextnot) {
911				len = snprintf(errstr, buflen,
912				    "double negative'");
913				return (-1);
914			}
915			nextnot = 1;
916			current += 1;
917		} else {
918			len = snprintf(errstr, buflen, "'%s' not expected",
919			    argv[current]);
920			return (-1);
921		}
922	}
923
924	object->mbo_flags = flags;
925	if (not_seen)
926		object->mbo_neg = MBO_ALL_FLAGS ^ neg;
927	else
928		object->mbo_neg = neg;
929	if (flags & MBO_UID_DEFINED) {
930		object->mbo_uid_min = uid_min;
931		object->mbo_uid_max = uid_max;
932	}
933	if (flags & MBO_GID_DEFINED) {
934		object->mbo_gid_min = gid_min;
935		object->mbo_gid_max = gid_max;
936	}
937	if (flags & MBO_FSID_DEFINED)
938		object->mbo_fsid = fsid;
939	if (flags & MBO_TYPE_DEFINED)
940		object->mbo_type = type;
941
942	return (0);
943}
944
945int
946bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
947    char *errstr)
948{
949	size_t len;
950	int i;
951
952	if (argc == 0) {
953		len = snprintf(errstr, buflen, "mode expects mode value");
954		return (-1);
955	}
956
957	if (argc != 1) {
958		len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
959		return (-1);
960	}
961
962	*mode = 0;
963	for (i = 0; i < strlen(argv[0]); i++) {
964		switch (argv[0][i]) {
965		case 'a':
966			*mode |= MBI_ADMIN;
967			break;
968		case 'r':
969			*mode |= MBI_READ;
970			break;
971		case 's':
972			*mode |= MBI_STAT;
973			break;
974		case 'w':
975			*mode |= MBI_WRITE;
976			break;
977		case 'x':
978			*mode |= MBI_EXEC;
979			break;
980		case 'n':
981			/* ignore */
982			break;
983		default:
984			len = snprintf(errstr, buflen, "Unknown mode letter: %c",
985			    argv[0][i]);
986			return (-1);
987		}
988	}
989
990	return (0);
991}
992
993int
994bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
995    size_t buflen, char *errstr)
996{
997	int subject, subject_elements, subject_elements_length;
998	int object, object_elements, object_elements_length;
999	int mode, mode_elements, mode_elements_length;
1000	int error, i;
1001	size_t len;
1002
1003	bzero(rule, sizeof(*rule));
1004
1005	if (argc < 1) {
1006		len = snprintf(errstr, buflen, "Rule must begin with subject");
1007		return (-1);
1008	}
1009
1010	if (strcmp(argv[0], "subject") != 0) {
1011		len = snprintf(errstr, buflen, "Rule must begin with subject");
1012		return (-1);
1013	}
1014	subject = 0;
1015	subject_elements = 1;
1016
1017	/* Search forward for object. */
1018
1019	object = -1;
1020	for (i = 1; i < argc; i++)
1021		if (strcmp(argv[i], "object") == 0)
1022			object = i;
1023
1024	if (object == -1) {
1025		len = snprintf(errstr, buflen, "Rule must contain an object");
1026		return (-1);
1027	}
1028
1029	/* Search forward for mode. */
1030	mode = -1;
1031	for (i = object; i < argc; i++)
1032		if (strcmp(argv[i], "mode") == 0)
1033			mode = i;
1034
1035	if (mode == -1) {
1036		len = snprintf(errstr, buflen, "Rule must contain mode");
1037		return (-1);
1038	}
1039
1040	subject_elements_length = object - subject - 1;
1041	object_elements = object + 1;
1042	object_elements_length = mode - object_elements;
1043	mode_elements = mode + 1;
1044	mode_elements_length = argc - mode_elements;
1045
1046	error = bsde_parse_subject(subject_elements_length,
1047	    argv + subject_elements, &rule->mbr_subject, buflen, errstr);
1048	if (error)
1049		return (-1);
1050
1051	error = bsde_parse_object(object_elements_length,
1052	    argv + object_elements, &rule->mbr_object, buflen, errstr);
1053	if (error)
1054		return (-1);
1055
1056	error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
1057	    &rule->mbr_mode, buflen, errstr);
1058	if (error)
1059		return (-1);
1060
1061	return (0);
1062}
1063
1064int
1065bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
1066    size_t buflen, char *errstr)
1067{
1068	char *stringdup, *stringp, *argv[100], **ap;
1069	int argc, error;
1070
1071	stringp = stringdup = strdup(string);
1072	while (*stringp == ' ' || *stringp == '\t')
1073		stringp++;
1074
1075	argc = 0;
1076	for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
1077		argc++;
1078		if (**ap != '\0')
1079			if (++ap >= &argv[100])
1080				break;
1081	}
1082
1083	error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
1084
1085	free(stringdup);
1086
1087	return (error);
1088}
1089
1090int
1091bsde_get_mib(const char *string, int *name, size_t *namelen)
1092{
1093	size_t len;
1094	int error;
1095
1096	len = *namelen;
1097	error = sysctlnametomib(string, name, &len);
1098	if (error)
1099		return (error);
1100
1101	*namelen = len;
1102	return (0);
1103}
1104
1105int
1106bsde_check_version(size_t buflen, char *errstr)
1107{
1108	size_t len;
1109	int error;
1110	int version;
1111
1112	len = sizeof(version);
1113	error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0);
1114	if (error) {
1115		len = snprintf(errstr, buflen, "version check failed: %s",
1116		    strerror(errno));
1117		return (-1);
1118	}
1119	if (version != MB_VERSION) {
1120		len = snprintf(errstr, buflen, "module v%d != library v%d",
1121		    version, MB_VERSION);
1122		return (-1);
1123	}
1124	return (0);
1125}
1126
1127int
1128bsde_get_rule_count(size_t buflen, char *errstr)
1129{
1130	size_t len;
1131	int error;
1132	int rule_count;
1133
1134	len = sizeof(rule_count);
1135	error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
1136	if (error) {
1137		len = snprintf(errstr, buflen, "%s", strerror(errno));
1138		return (-1);
1139	}
1140	if (len != sizeof(rule_count)) {
1141		len = snprintf(errstr, buflen, "Data error in %s.rule_count",
1142		    MIB);
1143		return (-1);
1144	}
1145
1146	return (rule_count);
1147}
1148
1149int
1150bsde_get_rule_slots(size_t buflen, char *errstr)
1151{
1152	size_t len;
1153	int error;
1154	int rule_slots;
1155
1156	len = sizeof(rule_slots);
1157	error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
1158	if (error) {
1159		len = snprintf(errstr, buflen, "%s", strerror(errno));
1160		return (-1);
1161	}
1162	if (len != sizeof(rule_slots)) {
1163		len = snprintf(errstr, buflen, "Data error in %s.rule_slots",
1164		    MIB);
1165		return (-1);
1166	}
1167
1168	return (rule_slots);
1169}
1170
1171/*
1172 * Returns 0 for success;
1173 * Returns -1 for failure;
1174 * Returns -2 for not present
1175 */
1176int
1177bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
1178    char *errstr)
1179{
1180	int name[10];
1181	size_t len, size;
1182	int error;
1183
1184	if (bsde_check_version(errlen, errstr) != 0)
1185		return (-1);
1186
1187	len = 10;
1188	error = bsde_get_mib(MIB ".rules", name, &len);
1189	if (error) {
1190		len = snprintf(errstr, errlen, "%s: %s", MIB ".rules",
1191		    strerror(errno));
1192		return (-1);
1193	}
1194
1195	size = sizeof(*rule);
1196	name[len] = rulenum;
1197	len++;
1198	error = sysctl(name, len, rule, &size, NULL, 0);
1199	if (error  == -1 && errno == ENOENT)
1200		return (-2);
1201	if (error) {
1202		len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
1203		    rulenum, strerror(errno));
1204		return (-1);
1205	} else if (size != sizeof(*rule)) {
1206		len = snprintf(errstr, errlen, "Data error in %s.%d: %s",
1207		    MIB ".rules", rulenum, strerror(errno));
1208		return (-1);
1209	}
1210
1211	return (0);
1212}
1213
1214int
1215bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
1216{
1217	struct mac_bsdextended_rule rule;
1218	int name[10];
1219	size_t len, size;
1220	int error;
1221
1222	if (bsde_check_version(buflen, errstr) != 0)
1223		return (-1);
1224
1225	len = 10;
1226	error = bsde_get_mib(MIB ".rules", name, &len);
1227	if (error) {
1228		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1229		    strerror(errno));
1230		return (-1);
1231	}
1232
1233	name[len] = rulenum;
1234	len++;
1235
1236	size = sizeof(rule);
1237	error = sysctl(name, len, NULL, NULL, &rule, 0);
1238	if (error) {
1239		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1240		    rulenum, strerror(errno));
1241		return (-1);
1242	}
1243
1244	return (0);
1245}
1246
1247int
1248bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1249    char *errstr)
1250{
1251	int name[10];
1252	size_t len, size;
1253	int error;
1254
1255	if (bsde_check_version(buflen, errstr) != 0)
1256		return (-1);
1257
1258	len = 10;
1259	error = bsde_get_mib(MIB ".rules", name, &len);
1260	if (error) {
1261		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1262		    strerror(errno));
1263		return (-1);
1264	}
1265
1266	name[len] = rulenum;
1267	len++;
1268
1269	size = sizeof(*rule);
1270	error = sysctl(name, len, NULL, NULL, rule, size);
1271	if (error) {
1272		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1273		    rulenum, strerror(errno));
1274		return (-1);
1275	}
1276
1277	return (0);
1278}
1279
1280int
1281bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
1282    char *errstr)
1283{
1284	char charstr[BUFSIZ];
1285	int name[10];
1286	size_t len, size;
1287	int error, rule_slots;
1288
1289	if (bsde_check_version(buflen, errstr) != 0)
1290		return (-1);
1291
1292	len = 10;
1293	error = bsde_get_mib(MIB ".rules", name, &len);
1294	if (error) {
1295		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
1296		    strerror(errno));
1297		return (-1);
1298	}
1299
1300	rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
1301	if (rule_slots == -1) {
1302		len = snprintf(errstr, buflen, "unable to get rule slots: %s",
1303		    strerror(errno));
1304		return (-1);
1305	}
1306
1307	name[len] = rule_slots;
1308	len++;
1309
1310	size = sizeof(*rule);
1311	error = sysctl(name, len, NULL, NULL, rule, size);
1312	if (error) {
1313		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
1314		    rule_slots, strerror(errno));
1315		return (-1);
1316	}
1317
1318	if (rulenum != NULL)
1319		*rulenum = rule_slots;
1320
1321	return (0);
1322}
1323