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