ugidfw.c revision 145432
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 145432 2005-04-23 02:20:35Z trhodes $
32 */
33#include <sys/param.h>
34#include <sys/errno.h>
35#include <sys/time.h>
36#include <sys/sysctl.h>
37
38#include <security/mac_bsdextended/mac_bsdextended.h>
39
40#include <grp.h>
41#include <pwd.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45
46#include "ugidfw.h"
47
48/*
49 * Text format for rules: rules contain subject and object elements, mode.
50 * Each element takes the form "[not] [uid number] [gid number]".
51 * The total form is "subject [element] object [element] mode [mode]".
52 * At least * one of a uid or gid entry must be present; both may also be
53 * present.
54 */
55
56#define	MIB	"security.mac.bsdextended"
57
58int
59bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen)
60{
61	struct group *grp;
62	struct passwd *pwd;
63	char *cur;
64	size_t left, len;
65	int anymode, unknownmode, truncated;
66
67	cur = buf;
68	left = buflen;
69	truncated = 0;
70
71	if (rule->mbr_subject.mbi_flags & (MBI_UID_DEFINED |
72	    MBI_GID_DEFINED)) {
73		len = snprintf(cur, left, "subject ");
74		if (len < 0 || len > left)
75			goto truncated;
76		left -= len;
77		cur += len;
78
79		if (rule->mbr_subject.mbi_flags & MBI_NEGATED) {
80			len = snprintf(cur, left, "not ");
81			if (len < 0 || len > left)
82				goto truncated;
83			left -= len;
84			cur += len;
85		}
86		if (rule->mbr_subject.mbi_flags & MBI_UID_DEFINED) {
87			pwd = getpwuid(rule->mbr_subject.mbi_uid);
88			if (pwd != NULL) {
89				len = snprintf(cur, left, "uid %s ",
90				    pwd->pw_name);
91				if (len < 0 || len > left)
92					goto truncated;
93				left -= len;
94				cur += len;
95			} else {
96				len = snprintf(cur, left, "uid %u ",
97				    rule->mbr_subject.mbi_uid);
98				if (len < 0 || len > left)
99					goto truncated;
100				left -= len;
101				cur += len;
102			}
103		}
104		if (rule->mbr_subject.mbi_flags & MBI_GID_DEFINED) {
105			grp = getgrgid(rule->mbr_subject.mbi_gid);
106			if (grp != NULL) {
107				len = snprintf(cur, left, "gid %s ",
108				    grp->gr_name);
109				if (len < 0 || len > left)
110					goto truncated;
111				left -= len;
112				cur += len;
113			} else {
114				len = snprintf(cur, left, "gid %u ",
115				    rule->mbr_subject.mbi_gid);
116				if (len < 0 || len > left)
117					goto truncated;
118				left -= len;
119				cur += len;
120			}
121		}
122	}
123	if (rule->mbr_object.mbi_flags & (MBI_UID_DEFINED |
124	    MBI_GID_DEFINED)) {
125		len = snprintf(cur, left, "object ");
126		if (len < 0 || len > left)
127			goto truncated;
128		left -= len;
129		cur += len;
130
131		if (rule->mbr_object.mbi_flags & MBI_NEGATED) {
132			len = snprintf(cur, left, "not ");
133			if (len < 0 || len > left)
134				goto truncated;
135			left -= len;
136			cur += len;
137		}
138		if (rule->mbr_object.mbi_flags & MBI_UID_DEFINED) {
139			pwd = getpwuid(rule->mbr_object.mbi_uid);
140			if (pwd != NULL) {
141				len = snprintf(cur, left, "uid %s ",
142				    pwd->pw_name);
143				if (len < 0 || len > left)
144					goto truncated;
145				left -= len;
146				cur += len;
147			} else {
148				len = snprintf(cur, left, "uid %u ",
149				    rule->mbr_object.mbi_uid);
150				left -= len;
151				cur += len;
152			}
153		}
154		if (rule->mbr_object.mbi_flags & MBI_GID_DEFINED) {
155			grp = getgrgid(rule->mbr_object.mbi_gid);
156			if (grp != NULL) {
157				len = snprintf(cur, left, "gid %s ",
158				    grp->gr_name);
159				if (len < 0 || len > left)
160					goto truncated;
161				left -= len;
162				cur += len;
163			} else {
164				len = snprintf(cur, left, "gid %u ",
165				    rule->mbr_object.mbi_gid);
166				if (len < 0 || len > left)
167					goto truncated;
168				left -= len;
169				cur += len;
170			}
171		}
172	}
173
174	len = snprintf(cur, left, "mode ");
175	if (len < 0 || len > left)
176		goto truncated;
177	left -= len;
178	cur += len;
179
180	anymode = (rule->mbr_mode & MBI_ALLPERM);
181	unknownmode = (rule->mbr_mode & ~MBI_ALLPERM);
182
183	if (rule->mbr_mode & MBI_ADMIN) {
184		len = snprintf(cur, left, "a");
185		if (len < 0 || len > left)
186			goto truncated;
187
188		left -= len;
189		cur += len;
190	}
191	if (rule->mbr_mode & MBI_READ) {
192		len = snprintf(cur, left, "r");
193		if (len < 0 || len > left)
194			goto truncated;
195
196		left -= len;
197		cur += len;
198	}
199	if (rule->mbr_mode & MBI_STAT) {
200		len = snprintf(cur, left, "s");
201		if (len < 0 || len > left)
202			goto truncated;
203
204		left -= len;
205		cur += len;
206	}
207	if (rule->mbr_mode & MBI_WRITE) {
208		len = snprintf(cur, left, "w");
209		if (len < 0 || len > left)
210			goto truncated;
211
212		left -= len;
213		cur += len;
214	}
215	if (rule->mbr_mode & MBI_EXEC) {
216		len = snprintf(cur, left, "x");
217		if (len < 0 || len > left)
218			goto truncated;
219
220		left -= len;
221		cur += len;
222	}
223	if (!anymode) {
224		len = snprintf(cur, left, "n");
225		if (len < 0 || len > left)
226			goto truncated;
227
228		left -= len;
229		cur += len;
230	}
231	if (unknownmode) {
232		len = snprintf(cur, left, "?");
233		if (len < 0 || len > left)
234			goto truncated;
235
236		left -= len;
237		cur += len;
238	}
239
240	return (0);
241
242truncated:
243	return (-1);
244}
245
246int
247bsde_parse_identity(int argc, char *argv[],
248    struct mac_bsdextended_identity *identity, size_t buflen, char *errstr)
249{
250	struct group *grp;
251	struct passwd *pwd;
252	int uid_seen, gid_seen, not_seen;
253	int current;
254	char *endp;
255	long value;
256	uid_t uid;
257	gid_t gid;
258	size_t len;
259
260	if (argc == 0) {
261		len = snprintf(errstr, buflen, "Identity must not be empty");
262		return (-1);
263	}
264
265	current = 0;
266
267	/* First element might be "not". */
268	if (strcmp("not", argv[0]) == 0) {
269		not_seen = 1;
270		current++;
271	} else
272		not_seen = 0;
273
274	if (current >= argc) {
275		len = snprintf(errstr, buflen, "Identity short");
276		return (-1);
277	}
278
279	uid_seen = 0;
280	uid = 0;
281	gid_seen = 0;
282	gid = 0;
283
284	/* First phrase: uid [uid] or gid [gid]. */
285	if (strcmp("uid", argv[current]) == 0) {
286		if (current + 2 > argc) {
287			len = snprintf(errstr, buflen, "uid short");
288			return (-1);
289		}
290		pwd = getpwnam(argv[current+1]);
291		if (pwd != NULL)
292			uid = pwd->pw_uid;
293		else {
294			value = strtol(argv[current+1], &endp, 10);
295			if (*endp != '\0') {
296				len = snprintf(errstr, buflen,
297				    "invalid uid: '%s'",
298				    argv[current+1]);
299				return (-1);
300			}
301			uid = value;
302		}
303		uid_seen = 1;
304		current += 2;
305	} else if (strcmp("gid", argv[current]) == 0) {
306		if (current + 2 > argc) {
307			len = snprintf(errstr, buflen, "gid short");
308			return (-1);
309		}
310		grp = getgrnam(argv[current+1]);
311		if (grp != NULL)
312			gid = grp->gr_gid;
313		else {
314			value = strtol(argv[current+1], &endp, 10);
315			if (*endp != '\0') {
316				len = snprintf(errstr, buflen,
317				    "invalid gid: '%s'",
318				    argv[current+1]);
319				return (-1);
320			}
321			gid = value;
322		}
323		gid_seen = 1;
324		current += 2;
325	} else {
326		len = snprintf(errstr, buflen, "'%s' not expected",
327		    argv[current]);
328		return (-1);
329	}
330
331	/* Onto optional second phrase. */
332	if (current + 1 < argc) {
333		/* Second phrase: uid [uid] or gid [gid], but not a repeat. */
334		if (strcmp("uid", argv[current]) == 0) {
335			if (uid_seen) {
336				len = snprintf(errstr, buflen,
337				    "Only one uid permitted per identity clause");
338				return (-1);
339			}
340			if (current + 2 > argc) {
341				len = snprintf(errstr, buflen, "uid short");
342				return (-1);
343			}
344			pwd = getpwnam(argv[current+1]);
345			if (pwd != NULL)
346				uid = pwd->pw_uid;
347			else {
348				value = strtol(argv[current+1], &endp, 10);
349				if (*endp != '\0') {
350					len = snprintf(errstr, buflen,
351					    "invalid uid: '%s'",
352					    argv[current+1]);
353					return (-1);
354				}
355				uid = value;
356			}
357			uid_seen = 1;
358			current += 2;
359		} else if (strcmp("gid", argv[current]) == 0) {
360			if (gid_seen) {
361				len = snprintf(errstr, buflen,
362				    "Only one gid permitted per identity clause");
363				return (-1);
364			}
365			if (current + 2 > argc) {
366				len = snprintf(errstr, buflen, "gid short");
367				return (-1);
368			}
369			grp = getgrnam(argv[current+1]);
370			if (grp != NULL)
371				gid = grp->gr_gid;
372			else {
373				value = strtol(argv[current+1], &endp, 10);
374				if (*endp != '\0') {
375					len = snprintf(errstr, buflen,
376					    "invalid gid: '%s'",
377					    argv[current+1]);
378					return (-1);
379				}
380				gid = value;
381			}
382			gid_seen = 1;
383			current += 2;
384		} else {
385			len = snprintf(errstr, buflen, "'%s' not expected",
386			    argv[current]);
387			return (-1);
388		}
389	}
390
391	if (current +1 < argc) {
392		len = snprintf(errstr, buflen, "'%s' not expected",
393		    argv[current]);
394		return (-1);
395	}
396
397	/* Fill out the identity. */
398	identity->mbi_flags = 0;
399
400	if (not_seen)
401		identity->mbi_flags |= MBI_NEGATED;
402
403	if (uid_seen) {
404		identity->mbi_flags |= MBI_UID_DEFINED;
405		identity->mbi_uid = uid;
406	} else
407		identity->mbi_uid = 0;
408
409	if (gid_seen) {
410		identity->mbi_flags |= MBI_GID_DEFINED;
411		identity->mbi_gid = gid;
412	} else
413		identity->mbi_gid = 0;
414
415	return (0);
416}
417
418int
419bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen,
420    char *errstr)
421{
422	size_t len;
423	int i;
424
425	if (argc == 0) {
426		len = snprintf(errstr, buflen, "mode expects mode value");
427		return (-1);
428	}
429
430	if (argc != 1) {
431		len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]);
432		return (-1);
433	}
434
435	*mode = 0;
436	for (i = 0; i < strlen(argv[0]); i++) {
437		switch (argv[0][i]) {
438		case 'a':
439			*mode |= MBI_ADMIN;
440			break;
441		case 'r':
442			*mode |= MBI_READ;
443			break;
444		case 's':
445			*mode |= MBI_STAT;
446			break;
447		case 'w':
448			*mode |= MBI_WRITE;
449			break;
450		case 'x':
451			*mode |= MBI_EXEC;
452			break;
453		case 'n':
454			/* ignore */
455			break;
456		default:
457			len = snprintf(errstr, buflen, "Unknown mode letter: %c",
458			    argv[0][i]);
459			return (-1);
460		}
461	}
462
463	return (0);
464}
465
466int
467bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
468    size_t buflen, char *errstr)
469{
470	int subject, subject_elements, subject_elements_length;
471	int object, object_elements, object_elements_length;
472	int mode, mode_elements, mode_elements_length;
473	int error, i;
474	size_t len;
475
476	bzero(rule, sizeof(*rule));
477
478	if (argc < 1) {
479		len = snprintf(errstr, buflen, "Rule must begin with subject");
480		return (-1);
481	}
482
483	if (strcmp(argv[0], "subject") != 0) {
484		len = snprintf(errstr, buflen, "Rule must begin with subject");
485		return (-1);
486	}
487	subject = 0;
488	subject_elements = 1;
489
490	/* Search forward for object. */
491
492	object = -1;
493	for (i = 1; i < argc; i++)
494		if (strcmp(argv[i], "object") == 0)
495			object = i;
496
497	if (object == -1) {
498		len = snprintf(errstr, buflen, "Rule must contain an object");
499		return (-1);
500	}
501
502	/* Search forward for mode. */
503	mode = -1;
504	for (i = object; i < argc; i++)
505		if (strcmp(argv[i], "mode") == 0)
506			mode = i;
507
508	if (mode == -1) {
509		len = snprintf(errstr, buflen, "Rule must contain mode");
510		return (-1);
511	}
512
513	subject_elements_length = object - subject - 1;
514	object_elements = object + 1;
515	object_elements_length = mode - object_elements;
516	mode_elements = mode + 1;
517	mode_elements_length = argc - mode_elements;
518
519	error = bsde_parse_identity(subject_elements_length,
520	    argv + subject_elements, &rule->mbr_subject, buflen, errstr);
521	if (error)
522		return (-1);
523
524	error = bsde_parse_identity(object_elements_length,
525	    argv + object_elements, &rule->mbr_object, buflen, errstr);
526	if (error)
527		return (-1);
528
529	error = bsde_parse_mode(mode_elements_length, argv + mode_elements,
530	    &rule->mbr_mode, buflen, errstr);
531	if (error)
532		return (-1);
533
534	return (0);
535}
536
537int
538bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
539    size_t buflen, char *errstr)
540{
541	char *stringdup, *stringp, *argv[20], **ap;
542	int argc, error;
543
544	stringp = stringdup = strdup(string);
545	while (*stringp == ' ' || *stringp == '\t')
546		stringp++;
547
548	argc = 0;
549	for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
550		argc++;
551		if (**ap != '\0')
552			if (++ap >= &argv[20])
553				break;
554	}
555
556	error = bsde_parse_rule(argc, argv, rule, buflen, errstr);
557
558	free(stringdup);
559
560	return (error);
561}
562
563int
564bsde_get_mib(const char *string, int *name, size_t *namelen)
565{
566	size_t len;
567	int error;
568
569	len = *namelen;
570	error = sysctlnametomib(string, name, &len);
571	if (error)
572		return (error);
573
574	*namelen = len;
575	return (0);
576}
577
578int
579bsde_get_rule_count(size_t buflen, char *errstr)
580{
581	size_t len;
582	int error;
583	int rule_count;
584
585	len = sizeof(rule_count);
586	error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0);
587	if (error) {
588		len = snprintf(errstr, buflen, strerror(errno));
589		return (-1);
590	}
591	if (len != sizeof(rule_count)) {
592		len = snprintf(errstr, buflen, "Data error in %s.rule_count",
593		    MIB);
594		return (-1);
595	}
596
597	return (rule_count);
598}
599
600int
601bsde_get_rule_slots(size_t buflen, char *errstr)
602{
603	size_t len;
604	int error;
605	int rule_slots;
606
607	len = sizeof(rule_slots);
608	error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0);
609	if (error) {
610		len = snprintf(errstr, buflen, strerror(errno));
611		return (-1);
612	}
613	if (len != sizeof(rule_slots)) {
614		len = snprintf(errstr, buflen, "Data error in %s.rule_slots",
615		    MIB);
616		return (-1);
617	}
618
619	return (rule_slots);
620}
621
622/*
623 * Returns 0 for success;
624 * Returns -1 for failure;
625 * Returns -2 for not present
626 */
627int
628bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
629    char *errstr)
630{
631	int name[10];
632	size_t len, size;
633	int error;
634
635	len = 10;
636	error = bsde_get_mib(MIB ".rules", name, &len);
637	if (error) {
638		len = snprintf(errstr, errlen, "%s: %s", MIB ".rules",
639		    strerror(errno));
640		return (-1);
641	}
642
643	size = sizeof(*rule);
644	name[len] = rulenum;
645	len++;
646	error = sysctl(name, len, rule, &size, NULL, 0);
647	if (error  == -1 && errno == ENOENT)
648		return (-2);
649	if (error) {
650		len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules",
651		    rulenum, strerror(errno));
652		return (-1);
653	} else if (size != sizeof(*rule)) {
654		len = snprintf(errstr, errlen, "Data error in %s.%d: %s",
655		    MIB ".rules", rulenum, strerror(errno));
656		return (-1);
657	}
658
659	return (0);
660}
661
662int
663bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
664{
665	struct mac_bsdextended_rule rule;
666	int name[10];
667	size_t len, size;
668	int error;
669
670	len = 10;
671	error = bsde_get_mib(MIB ".rules", name, &len);
672	if (error) {
673		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
674		    strerror(errno));
675		return (-1);
676	}
677
678	name[len] = rulenum;
679	len++;
680
681	size = sizeof(rule);
682	error = sysctl(name, len, NULL, NULL, &rule, 0);
683	if (error) {
684		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
685		    rulenum, strerror(errno));
686		return (-1);
687	}
688
689	return (0);
690}
691
692int
693bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
694    char *errstr)
695{
696	int name[10];
697	size_t len, size;
698	int error;
699
700	len = 10;
701	error = bsde_get_mib(MIB ".rules", name, &len);
702	if (error) {
703		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
704		    strerror(errno));
705		return (-1);
706	}
707
708	name[len] = rulenum;
709	len++;
710
711	size = sizeof(*rule);
712	error = sysctl(name, len, NULL, NULL, rule, size);
713	if (error) {
714		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
715		    rulenum, strerror(errno));
716		return (-1);
717	}
718
719	return (0);
720}
721
722int
723bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
724    char *errstr)
725{
726	char charstr[BUFSIZ];
727	int name[10];
728	size_t len, size;
729	int error, rule_slots;
730
731	len = 10;
732	error = bsde_get_mib(MIB ".rules", name, &len);
733	if (error) {
734		len = snprintf(errstr, buflen, "%s: %s", MIB ".rules",
735		    strerror(errno));
736		return (-1);
737	}
738
739	rule_slots = bsde_get_rule_slots(BUFSIZ, charstr);
740	if (rule_slots == -1) {
741		len = snprintf(errstr, buflen, "unable to get rule slots: %s",
742		    strerror(errno));
743		return (-1);
744	}
745
746	name[len] = rule_slots;
747	len++;
748
749	size = sizeof(*rule);
750	error = sysctl(name, len, NULL, NULL, rule, size);
751	if (error) {
752		len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules",
753		    rule_slots, strerror(errno));
754		return (-1);
755	}
756
757	if (rulenum != NULL)
758		*rulenum = rule_slots;
759
760	return (0);
761}
762