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