Index: head/lib/libugidfw/libugidfw.3 =================================================================== --- head/lib/libugidfw/libugidfw.3 (revision 157985) +++ head/lib/libugidfw/libugidfw.3 (revision 157986) @@ -1,125 +1,115 @@ .\" Copyright (c) 2003 Networks Associates Technology, Inc. .\" All rights reserved. .\" .\" This software was developed for the FreeBSD Project by Chris .\" Costello at Safeport Network Services and Network Associates .\" Laboratories, the Security Research Division of Network Associates, .\" Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part .\" of the DARPA CHATS research program. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd February 25, 2004 .Os .Dt LIBUGIDFW 3 .Sh NAME .Nm libugidfw .Nd "library interface to the file system firewall MAC policy" .Sh LIBRARY .Lb libugidfw .Sh SYNOPSIS .In sys/types.h .In security/mac_bsdextended/mac_bsdextended.h .In ugidfw.h .Sh DESCRIPTION The .Nm library routines provide an interface to the .Xr mac_bsdextended 4 file system firewall MAC policy. .Pp The .Nm library defines the following functions: .Bl -tag -width ".Fn bsde_parse_rule_string" .It Fn bsde_rule_to_string Converts the internal representation of a rule .Pq Vt "struct mac_bsdextended_rule" into its text representation; see .Xr bsde_rule_to_string 3 . -.It Fn bsde_parse_identity -Parses the identity of a subject or object; -see -.Xr bsde_parse_identity 3 . -.It Fn bsde_parse_mode -Parses the access mode for a ugidfw rule; -see -.Xr bsde_parse_mode 3 . .It Fn bsde_parse_rule Parses an entire rule (in argument array form); see .Xr bsde_parse_rule 3 . .It Fn bsde_parse_rule_string Parses an entire rule string; see .Xr bsde_parse_rule_string 3 . .It Fn bsde_get_rule_count Returns the total number of ugidfw rules being enforced in the system; see .Xr bsde_get_rule_count 3 . .It Fn bsde_get_rule_slots Returns the total number of used rule slots; see .Xr bsde_get_rule_slots 3 . .It Fn bsde_get_rule Returns a rule by its rule number; see .Xr bsde_get_rule 3 . .It Fn bsde_delete_rule Deletes a rule by its rule number; see .Xr bsde_delete_rule 3 . .It Fn bsde_set_rule Uploads the rule to the .Xr mac_bsdextended 4 module and applies it; see .Xr bsde_set_rule 3 . .It Fn bsde_add_rule Upload the rule to the module, automatically selecting the next available rule number; see .Xr bsde_add_rule 3 . .El .Sh SEE ALSO .Xr bsde_delete_rule 3 , .Xr bsde_get_rule 3 , .Xr bsde_get_rule_count 3 , .Xr bsde_get_rule_slots 3 , -.Xr bsde_parse_identity 3 , -.Xr bsde_parse_mode 3 , .Xr bsde_parse_rule 3 , .Xr bsde_parse_rule_string 3 , .Xr bsde_rule_to_string 3 , .Xr bsde_set_rule 3 .Sh AUTHORS This software was contributed to the .Fx Project by Network Associates Labs, the Security Research Division of Network Associates Inc. under DARPA/SPAWAR contract N66001-01-C-8035 .Pq Dq CBOSS , as part of the DARPA CHATS research program. Index: head/lib/libugidfw/ugidfw.c =================================================================== --- head/lib/libugidfw/ugidfw.c (revision 157985) +++ head/lib/libugidfw/ugidfw.c (revision 157986) @@ -1,761 +1,1323 @@ /*- * Copyright (c) 2002-2005 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Network Associates * Laboratories, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include +#include +#include #include #include #include #include #include #include #include "ugidfw.h" /* * Text format for rules: rules contain subject and object elements, mode. - * Each element takes the form "[not] [uid number] [gid number]". - * The total form is "subject [element] object [element] mode [mode]". + * The total form is "subject [s_element] object [o_element] mode [mode]". * At least * one of a uid or gid entry must be present; both may also be * present. */ #define MIB "security.mac.bsdextended" int bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen) { struct group *grp; struct passwd *pwd; - char *cur; + struct statfs *mntbuf; + char *cur, type[sizeof(rule->mbr_object.mbo_type) * CHAR_BIT + 1]; size_t left, len; - int anymode, unknownmode, truncated; + int anymode, unknownmode, truncated, numfs, i, notdone; cur = buf; left = buflen; truncated = 0; - if (rule->mbr_subject.mbi_flags & (MBI_UID_DEFINED | - MBI_GID_DEFINED)) { - len = snprintf(cur, left, "subject "); - if (len < 0 || len > left) - goto truncated; - left -= len; - cur += len; - - if (rule->mbr_subject.mbi_flags & MBI_NEGATED) { + len = snprintf(cur, left, "subject "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + if (rule->mbr_subject.mbs_flags) { + if (rule->mbr_subject.mbs_neg == MBS_ALL_FLAGS) { len = snprintf(cur, left, "not "); if (len < 0 || len > left) goto truncated; left -= len; cur += len; + notdone = 1; + } else { + notdone = 0; } - if (rule->mbr_subject.mbi_flags & MBI_UID_DEFINED) { - pwd = getpwuid(rule->mbr_subject.mbi_uid); + + if (!notdone && (rule->mbr_subject.mbs_neg & MBO_UID_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_subject.mbs_flags & MBO_UID_DEFINED) { + pwd = getpwuid(rule->mbr_subject.mbs_uid_min); if (pwd != NULL) { - len = snprintf(cur, left, "uid %s ", + len = snprintf(cur, left, "uid %s", pwd->pw_name); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } else { - len = snprintf(cur, left, "uid %u ", - rule->mbr_subject.mbi_uid); + len = snprintf(cur, left, "uid %u", + rule->mbr_subject.mbs_uid_min); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } + if (rule->mbr_subject.mbs_uid_min != + rule->mbr_subject.mbs_uid_max) { + pwd = getpwuid(rule->mbr_subject.mbs_uid_max); + if (pwd != NULL) { + len = snprintf(cur, left, ":%s ", + pwd->pw_name); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } else { + len = snprintf(cur, left, ":%u ", + rule->mbr_subject.mbs_uid_max); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + } else { + len = snprintf(cur, left, " "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } - if (rule->mbr_subject.mbi_flags & MBI_GID_DEFINED) { - grp = getgrgid(rule->mbr_subject.mbi_gid); + if (!notdone && (rule->mbr_subject.mbs_neg & MBO_GID_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_subject.mbs_flags & MBO_GID_DEFINED) { + grp = getgrgid(rule->mbr_subject.mbs_gid_min); if (grp != NULL) { - len = snprintf(cur, left, "gid %s ", + len = snprintf(cur, left, "gid %s", grp->gr_name); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } else { - len = snprintf(cur, left, "gid %u ", - rule->mbr_subject.mbi_gid); + len = snprintf(cur, left, "gid %u", + rule->mbr_subject.mbs_gid_min); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } + if (rule->mbr_subject.mbs_gid_min != + rule->mbr_subject.mbs_gid_max) { + grp = getgrgid(rule->mbr_subject.mbs_gid_max); + if (grp != NULL) { + len = snprintf(cur, left, ":%s ", + grp->gr_name); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } else { + len = snprintf(cur, left, ":%u ", + rule->mbr_subject.mbs_gid_max); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + } else { + len = snprintf(cur, left, " "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } + if (!notdone && (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) { + len = snprintf(cur, left, "jailid %d ", + rule->mbr_subject.mbs_prison); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } - if (rule->mbr_object.mbi_flags & (MBI_UID_DEFINED | - MBI_GID_DEFINED)) { - len = snprintf(cur, left, "object "); - if (len < 0 || len > left) - goto truncated; - left -= len; - cur += len; - if (rule->mbr_object.mbi_flags & MBI_NEGATED) { + len = snprintf(cur, left, "object "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + if (rule->mbr_object.mbo_flags) { + if (rule->mbr_object.mbo_neg == MBO_ALL_FLAGS) { len = snprintf(cur, left, "not "); if (len < 0 || len > left) goto truncated; left -= len; cur += len; + notdone = 1; + } else { + notdone = 0; } - if (rule->mbr_object.mbi_flags & MBI_UID_DEFINED) { - pwd = getpwuid(rule->mbr_object.mbi_uid); + + if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) { + pwd = getpwuid(rule->mbr_object.mbo_uid_min); if (pwd != NULL) { - len = snprintf(cur, left, "uid %s ", + len = snprintf(cur, left, "uid %s", pwd->pw_name); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } else { - len = snprintf(cur, left, "uid %u ", - rule->mbr_object.mbi_uid); + len = snprintf(cur, left, "uid %u", + rule->mbr_object.mbo_uid_min); + if (len < 0 || len > left) + goto truncated; left -= len; cur += len; } + if (rule->mbr_object.mbo_uid_min != + rule->mbr_object.mbo_uid_max) { + pwd = getpwuid(rule->mbr_object.mbo_uid_max); + if (pwd != NULL) { + len = snprintf(cur, left, ":%s ", + pwd->pw_name); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } else { + len = snprintf(cur, left, ":%u ", + rule->mbr_object.mbo_uid_max); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + } else { + len = snprintf(cur, left, " "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } - if (rule->mbr_object.mbi_flags & MBI_GID_DEFINED) { - grp = getgrgid(rule->mbr_object.mbi_gid); + if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) { + grp = getgrgid(rule->mbr_object.mbo_gid_min); if (grp != NULL) { - len = snprintf(cur, left, "gid %s ", + len = snprintf(cur, left, "gid %s", grp->gr_name); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } else { - len = snprintf(cur, left, "gid %u ", - rule->mbr_object.mbi_gid); + len = snprintf(cur, left, "gid %u", + rule->mbr_object.mbo_gid_min); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } + if (rule->mbr_object.mbo_gid_min != + rule->mbr_object.mbo_gid_max) { + grp = getgrgid(rule->mbr_object.mbo_gid_max); + if (grp != NULL) { + len = snprintf(cur, left, ":%s ", + grp->gr_name); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } else { + len = snprintf(cur, left, ":%u ", + rule->mbr_object.mbo_gid_max); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + } else { + len = snprintf(cur, left, " "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { + numfs = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < numfs; i++) + if (memcmp(&(rule->mbr_object.mbo_fsid), + &(mntbuf[i].f_fsid), + sizeof(mntbuf[i].f_fsid)) == 0) + break; + len = snprintf(cur, left, "filesys %s ", + i == numfs ? "???" : mntbuf[i].f_mntonname); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_SUID)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_SUID) { + len = snprintf(cur, left, "suid "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_SGID)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_SGID) { + len = snprintf(cur, left, "sgid "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) { + len = snprintf(cur, left, "uid_of_subject "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) { + len = snprintf(cur, left, "gid_of_subject "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (!notdone && (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED)) { + len = snprintf(cur, left, "! "); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } + if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) { + i = 0; + if (rule->mbr_object.mbo_type & MBO_TYPE_REG) + type[i++] = 'r'; + if (rule->mbr_object.mbo_type & MBO_TYPE_DIR) + type[i++] = 'd'; + if (rule->mbr_object.mbo_type & MBO_TYPE_BLK) + type[i++] = 'b'; + if (rule->mbr_object.mbo_type & MBO_TYPE_CHR) + type[i++] = 'c'; + if (rule->mbr_object.mbo_type & MBO_TYPE_LNK) + type[i++] = 'l'; + if (rule->mbr_object.mbo_type & MBO_TYPE_SOCK) + type[i++] = 's'; + if (rule->mbr_object.mbo_type & MBO_TYPE_FIFO) + type[i++] = 'p'; + if (rule->mbr_object.mbo_type == MBO_ALL_TYPE) { + i = 0; + type[i++] = 'a'; + } + type[i++] = '\0'; + len = snprintf(cur, left, "type %s ", type); + if (len < 0 || len > left) + goto truncated; + left -= len; + cur += len; + } } len = snprintf(cur, left, "mode "); if (len < 0 || len > left) goto truncated; left -= len; cur += len; anymode = (rule->mbr_mode & MBI_ALLPERM); unknownmode = (rule->mbr_mode & ~MBI_ALLPERM); if (rule->mbr_mode & MBI_ADMIN) { len = snprintf(cur, left, "a"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (rule->mbr_mode & MBI_READ) { len = snprintf(cur, left, "r"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (rule->mbr_mode & MBI_STAT) { len = snprintf(cur, left, "s"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (rule->mbr_mode & MBI_WRITE) { len = snprintf(cur, left, "w"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (rule->mbr_mode & MBI_EXEC) { len = snprintf(cur, left, "x"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (!anymode) { len = snprintf(cur, left, "n"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } if (unknownmode) { len = snprintf(cur, left, "?"); if (len < 0 || len > left) goto truncated; left -= len; cur += len; } return (0); truncated: return (-1); } int -bsde_parse_identity(int argc, char *argv[], - struct mac_bsdextended_identity *identity, size_t buflen, char *errstr) -{ - struct group *grp; +bsde_parse_uidrange(char *spec, uid_t *min, uid_t *max, + size_t buflen, char *errstr){ struct passwd *pwd; - int uid_seen, gid_seen, not_seen; - int current; - char *endp; - long value; - uid_t uid; - gid_t gid; + uid_t uid1, uid2; + char *spec1, *spec2, *endp; + unsigned long value; size_t len; - if (argc == 0) { - len = snprintf(errstr, buflen, "Identity must not be empty"); - return (-1); + spec2 = spec; + spec1 = strsep(&spec2, ":"); + + pwd = getpwnam(spec1); + if (pwd != NULL) + uid1 = pwd->pw_uid; + else { + value = strtoul(spec1, &endp, 10); + if (*endp != '\0') { + len = snprintf(errstr, buflen, + "invalid uid: '%s'", spec1); + return (-1); + } + uid1 = value; } - current = 0; + if (spec2 == NULL) { + *max = *min = uid1; + return (0); + } - /* First element might be "not". */ - if (strcmp("not", argv[0]) == 0) { - not_seen = 1; - current++; - } else - not_seen = 0; + pwd = getpwnam(spec2); + if (pwd != NULL) + uid2 = pwd->pw_uid; + else { + value = strtoul(spec2, &endp, 10); + if (*endp != '\0') { + len = snprintf(errstr, buflen, + "invalid uid: '%s'", spec2); + return (-1); + } + uid2 = value; + } - if (current >= argc) { - len = snprintf(errstr, buflen, "Identity short"); - return (-1); + *min = uid1; + *max = uid2; + + return (0); +} + +int +bsde_parse_gidrange(char *spec, gid_t *min, gid_t *max, + size_t buflen, char *errstr){ + struct group *grp; + gid_t gid1, gid2; + char *spec1, *spec2, *endp; + unsigned long value; + size_t len; + + spec2 = spec; + spec1 = strsep(&spec2, ":"); + + grp = getgrnam(spec1); + if (grp != NULL) + gid1 = grp->gr_gid; + else { + value = strtoul(spec1, &endp, 10); + if (*endp != '\0') { + len = snprintf(errstr, buflen, + "invalid gid: '%s'", spec1); + return (-1); + } + gid1 = value; } - uid_seen = 0; - uid = 0; - gid_seen = 0; - gid = 0; + if (spec2 == NULL) { + *max = *min = gid1; + return (0); + } - /* First phrase: uid [uid] or gid [gid]. */ - if (strcmp("uid", argv[current]) == 0) { - if (current + 2 > argc) { - len = snprintf(errstr, buflen, "uid short"); + grp = getgrnam(spec2); + if (grp != NULL) + gid2 = grp->gr_gid; + else { + value = strtoul(spec2, &endp, 10); + if (*endp != '\0') { + len = snprintf(errstr, buflen, + "invalid gid: '%s'", spec2); return (-1); } - pwd = getpwnam(argv[current+1]); - if (pwd != NULL) - uid = pwd->pw_uid; - else { + gid2 = value; + } + + *min = gid1; + *max = gid2; + + return (0); +} + +int +bsde_parse_subject(int argc, char *argv[], + struct mac_bsdextended_subject *subject, size_t buflen, char *errstr) +{ + int not_seen, flags; + int current, neg, nextnot; + char *endp; + uid_t uid_min, uid_max; + gid_t gid_min, gid_max; + int jid; + size_t len; + long value; + + current = 0; + flags = 0; + neg = 0; + nextnot = 0; + + if (strcmp("not", argv[current]) == 0) { + not_seen = 1; + current++; + } else + not_seen = 0; + + while (current < argc) { + if (strcmp(argv[current], "uid") == 0) { + if (current + 2 > argc) { + len = snprintf(errstr, buflen, "uid short"); + return (-1); + } + if (flags & MBS_UID_DEFINED) { + len = snprintf(errstr, buflen, "one uid only"); + return (-1); + } + if (bsde_parse_uidrange(argv[current+1], + &uid_min, &uid_max, buflen, errstr) < 0) + return (-1); + flags |= MBS_UID_DEFINED; + if (nextnot) { + neg ^= MBS_UID_DEFINED; + nextnot = 0; + } + current += 2; + } else if (strcmp(argv[current], "gid") == 0) { + if (current + 2 > argc) { + len = snprintf(errstr, buflen, "gid short"); + return (-1); + } + if (flags & MBS_GID_DEFINED) { + len = snprintf(errstr, buflen, "one gid only"); + return (-1); + } + if (bsde_parse_gidrange(argv[current+1], + &gid_min, &gid_max, buflen, errstr) < 0) + return (-1); + flags |= MBS_GID_DEFINED; + if (nextnot) { + neg ^= MBS_GID_DEFINED; + nextnot = 0; + } + current += 2; + } else if (strcmp(argv[current], "jailid") == 0) { + if (current + 2 > argc) { + len = snprintf(errstr, buflen, "prison short"); + return (-1); + } + if (flags & MBS_PRISON_DEFINED) { + len = snprintf(errstr, buflen, "one jail only"); + return (-1); + } value = strtol(argv[current+1], &endp, 10); if (*endp != '\0') { len = snprintf(errstr, buflen, - "invalid uid: '%s'", - argv[current+1]); + "invalid jid: '%s'", argv[current+1]); return (-1); } - uid = value; - } - uid_seen = 1; - current += 2; - } else if (strcmp("gid", argv[current]) == 0) { - if (current + 2 > argc) { - len = snprintf(errstr, buflen, "gid short"); - return (-1); - } - grp = getgrnam(argv[current+1]); - if (grp != NULL) - gid = grp->gr_gid; - else { - value = strtol(argv[current+1], &endp, 10); - if (*endp != '\0') { + jid = value; + flags |= MBS_PRISON_DEFINED; + if (nextnot) { + neg ^= MBS_PRISON_DEFINED; + nextnot = 0; + } + current += 2; + } else if (strcmp(argv[current], "!") == 0) { + if (nextnot) { len = snprintf(errstr, buflen, - "invalid gid: '%s'", - argv[current+1]); + "double negative"); return (-1); } - gid = value; + nextnot = 1; + current += 1; + } else { + len = snprintf(errstr, buflen, "'%s' not expected", + argv[current]); + return (-1); } - gid_seen = 1; - current += 2; - } else { - len = snprintf(errstr, buflen, "'%s' not expected", - argv[current]); + } + + subject->mbs_flags = flags; + if (not_seen) + subject->mbs_neg = MBS_ALL_FLAGS ^ neg; + else + subject->mbs_neg = neg; + if (flags & MBS_UID_DEFINED) { + subject->mbs_uid_min = uid_min; + subject->mbs_uid_max = uid_max; + } + if (flags & MBS_GID_DEFINED) { + subject->mbs_gid_min = gid_min; + subject->mbs_gid_max = gid_max; + } + if (flags & MBS_PRISON_DEFINED) + subject->mbs_prison = jid; + + return (0); +} + +int +bsde_parse_type(char *spec, int *type, size_t buflen, char *errstr) +{ + size_t len; + int i; + + *type = 0; + for (i = 0; i < strlen(spec); i++) { + switch (spec[i]) { + case 'r': + case '-': + *type |= MBO_TYPE_REG; + break; + case 'd': + *type |= MBO_TYPE_DIR; + break; + case 'b': + *type |= MBO_TYPE_BLK; + break; + case 'c': + *type |= MBO_TYPE_CHR; + break; + case 'l': + *type |= MBO_TYPE_LNK; + break; + case 's': + *type |= MBO_TYPE_SOCK; + break; + case 'p': + *type |= MBO_TYPE_FIFO; + break; + case 'a': + *type |= MBO_ALL_TYPE; + break; + default: + len = snprintf(errstr, buflen, "Unknown type code: %c", + spec[i]); + return (-1); + } + } + + return (0); +} + +int +bsde_parse_fsid(char *spec, struct fsid *fsid, size_t buflen, char *errstr) +{ + size_t len; + struct statfs buf; + int i; + + if (statfs(spec, &buf) < 0) { + len = snprintf(errstr, buflen, "Unable to get id for %s: %s", + spec, strerror(errno)); return (-1); } - /* Onto optional second phrase. */ - if (current + 1 < argc) { - /* Second phrase: uid [uid] or gid [gid], but not a repeat. */ - if (strcmp("uid", argv[current]) == 0) { - if (uid_seen) { - len = snprintf(errstr, buflen, - "Only one uid permitted per identity clause"); + *fsid = buf.f_fsid; + + return (0); +} + +int +bsde_parse_object(int argc, char *argv[], + struct mac_bsdextended_object *object, size_t buflen, char *errstr) +{ + int not_seen, flags; + int current, neg, nextnot; + uid_t uid_min, uid_max; + gid_t gid_min, gid_max; + int type; + struct fsid fsid; + size_t len; + + current = 0; + flags = 0; + neg = 0; + nextnot = 0; + + if (strcmp("not", argv[current]) == 0) { + not_seen = 1; + current++; + } else + not_seen = 0; + + while (current < argc) { + if (strcmp(argv[current], "uid") == 0) { + if (current + 2 > argc) { + len = snprintf(errstr, buflen, "uid short"); return (-1); } + if (flags & MBO_UID_DEFINED) { + len = snprintf(errstr, buflen, "one uid only"); + return (-1); + } + if (bsde_parse_uidrange(argv[current+1], + &uid_min, &uid_max, buflen, errstr) < 0) + return (-1); + flags |= MBO_UID_DEFINED; + if (nextnot) { + neg ^= MBO_UID_DEFINED; + nextnot = 0; + } + current += 2; + } else if (strcmp(argv[current], "gid") == 0) { if (current + 2 > argc) { - len = snprintf(errstr, buflen, "uid short"); + len = snprintf(errstr, buflen, "gid short"); return (-1); } - pwd = getpwnam(argv[current+1]); - if (pwd != NULL) - uid = pwd->pw_uid; - else { - value = strtol(argv[current+1], &endp, 10); - if (*endp != '\0') { - len = snprintf(errstr, buflen, - "invalid uid: '%s'", - argv[current+1]); - return (-1); - } - uid = value; + if (flags & MBO_GID_DEFINED) { + len = snprintf(errstr, buflen, "one gid only"); + return (-1); } - uid_seen = 1; + if (bsde_parse_gidrange(argv[current+1], + &gid_min, &gid_max, buflen, errstr) < 0) + return (-1); + flags |= MBO_GID_DEFINED; + if (nextnot) { + neg ^= MBO_GID_DEFINED; + nextnot = 0; + } current += 2; - } else if (strcmp("gid", argv[current]) == 0) { - if (gid_seen) { - len = snprintf(errstr, buflen, - "Only one gid permitted per identity clause"); + } else if (strcmp(argv[current], "filesys") == 0) { + if (current + 2 > argc) { + len = snprintf(errstr, buflen, "filesys short"); return (-1); } + if (flags & MBO_FSID_DEFINED) { + len = snprintf(errstr, buflen, "one fsid only"); + return (-1); + } + if (bsde_parse_fsid(argv[current+1], &fsid, + buflen, errstr) < 0) + return (-1); + flags |= MBO_FSID_DEFINED; + if (nextnot) { + neg ^= MBO_FSID_DEFINED; + nextnot = 0; + } + current += 2; + } else if (strcmp(argv[current], "suid") == 0) { + flags |= MBO_SUID; + if (nextnot) { + neg ^= MBO_SUID; + nextnot = 0; + } + current += 1; + } else if (strcmp(argv[current], "sgid") == 0) { + flags |= MBO_SGID; + if (nextnot) { + neg ^= MBO_SGID; + nextnot = 0; + } + current += 1; + } else if (strcmp(argv[current], "uid_of_subject") == 0) { + flags |= MBO_UID_SUBJECT; + if (nextnot) { + neg ^= MBO_UID_SUBJECT; + nextnot = 0; + } + current += 1; + } else if (strcmp(argv[current], "gid_of_subject") == 0) { + flags |= MBO_GID_SUBJECT; + if (nextnot) { + neg ^= MBO_GID_SUBJECT; + nextnot = 0; + } + current += 1; + } else if (strcmp(argv[current], "type") == 0) { if (current + 2 > argc) { - len = snprintf(errstr, buflen, "gid short"); + len = snprintf(errstr, buflen, "type short"); return (-1); } - grp = getgrnam(argv[current+1]); - if (grp != NULL) - gid = grp->gr_gid; - else { - value = strtol(argv[current+1], &endp, 10); - if (*endp != '\0') { - len = snprintf(errstr, buflen, - "invalid gid: '%s'", - argv[current+1]); - return (-1); - } - gid = value; + if (flags & MBO_TYPE_DEFINED) { + len = snprintf(errstr, buflen, "one type only"); + return (-1); } - gid_seen = 1; + if (bsde_parse_type(argv[current+1], &type, + buflen, errstr) < 0) + return (-1); + flags |= MBO_TYPE_DEFINED; + if (nextnot) { + neg ^= MBO_TYPE_DEFINED; + nextnot = 0; + } current += 2; + } else if (strcmp(argv[current], "!") == 0) { + if (nextnot) { + len = snprintf(errstr, buflen, + "double negative'"); + return (-1); + } + nextnot = 1; + current += 1; } else { len = snprintf(errstr, buflen, "'%s' not expected", argv[current]); return (-1); - } + } } - if (current +1 < argc) { - len = snprintf(errstr, buflen, "'%s' not expected", - argv[current]); - return (-1); + object->mbo_flags = flags; + if (not_seen) + object->mbo_neg = MBO_ALL_FLAGS ^ neg; + else + object->mbo_neg = neg; + if (flags & MBO_UID_DEFINED) { + object->mbo_uid_min = uid_min; + object->mbo_uid_max = uid_max; } + if (flags & MBO_GID_DEFINED) { + object->mbo_gid_min = gid_min; + object->mbo_gid_max = gid_max; + } + if (flags & MBO_FSID_DEFINED) + object->mbo_fsid = fsid; + if (flags & MBO_TYPE_DEFINED) + object->mbo_type = type; - /* Fill out the identity. */ - identity->mbi_flags = 0; - - if (not_seen) - identity->mbi_flags |= MBI_NEGATED; - - if (uid_seen) { - identity->mbi_flags |= MBI_UID_DEFINED; - identity->mbi_uid = uid; - } else - identity->mbi_uid = 0; - - if (gid_seen) { - identity->mbi_flags |= MBI_GID_DEFINED; - identity->mbi_gid = gid; - } else - identity->mbi_gid = 0; - return (0); } int bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, char *errstr) { size_t len; int i; if (argc == 0) { len = snprintf(errstr, buflen, "mode expects mode value"); return (-1); } if (argc != 1) { len = snprintf(errstr, buflen, "'%s' unexpected", argv[1]); return (-1); } *mode = 0; for (i = 0; i < strlen(argv[0]); i++) { switch (argv[0][i]) { case 'a': *mode |= MBI_ADMIN; break; case 'r': *mode |= MBI_READ; break; case 's': *mode |= MBI_STAT; break; case 'w': *mode |= MBI_WRITE; break; case 'x': *mode |= MBI_EXEC; break; case 'n': /* ignore */ break; default: len = snprintf(errstr, buflen, "Unknown mode letter: %c", argv[0][i]); return (-1); } } return (0); } int bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, size_t buflen, char *errstr) { int subject, subject_elements, subject_elements_length; int object, object_elements, object_elements_length; int mode, mode_elements, mode_elements_length; int error, i; size_t len; bzero(rule, sizeof(*rule)); if (argc < 1) { len = snprintf(errstr, buflen, "Rule must begin with subject"); return (-1); } if (strcmp(argv[0], "subject") != 0) { len = snprintf(errstr, buflen, "Rule must begin with subject"); return (-1); } subject = 0; subject_elements = 1; /* Search forward for object. */ object = -1; for (i = 1; i < argc; i++) if (strcmp(argv[i], "object") == 0) object = i; if (object == -1) { len = snprintf(errstr, buflen, "Rule must contain an object"); return (-1); } /* Search forward for mode. */ mode = -1; for (i = object; i < argc; i++) if (strcmp(argv[i], "mode") == 0) mode = i; if (mode == -1) { len = snprintf(errstr, buflen, "Rule must contain mode"); return (-1); } subject_elements_length = object - subject - 1; object_elements = object + 1; object_elements_length = mode - object_elements; mode_elements = mode + 1; mode_elements_length = argc - mode_elements; - error = bsde_parse_identity(subject_elements_length, + error = bsde_parse_subject(subject_elements_length, argv + subject_elements, &rule->mbr_subject, buflen, errstr); if (error) return (-1); - error = bsde_parse_identity(object_elements_length, + error = bsde_parse_object(object_elements_length, argv + object_elements, &rule->mbr_object, buflen, errstr); if (error) return (-1); error = bsde_parse_mode(mode_elements_length, argv + mode_elements, &rule->mbr_mode, buflen, errstr); if (error) return (-1); return (0); } int bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr) { - char *stringdup, *stringp, *argv[20], **ap; + char *stringdup, *stringp, *argv[100], **ap; int argc, error; stringp = stringdup = strdup(string); while (*stringp == ' ' || *stringp == '\t') stringp++; argc = 0; for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) { argc++; if (**ap != '\0') - if (++ap >= &argv[20]) + if (++ap >= &argv[100]) break; } error = bsde_parse_rule(argc, argv, rule, buflen, errstr); free(stringdup); return (error); } int bsde_get_mib(const char *string, int *name, size_t *namelen) { size_t len; int error; len = *namelen; error = sysctlnametomib(string, name, &len); if (error) return (error); *namelen = len; return (0); } int +bsde_check_version(size_t buflen, char *errstr) +{ + size_t len; + int error; + int version; + + len = sizeof(version); + error = sysctlbyname(MIB ".rule_version", &version, &len, NULL, 0); + if (error) { + len = snprintf(errstr, buflen, "version check failed: %s", + strerror(errno)); + return (-1); + } + if (version != MB_VERSION) { + len = snprintf(errstr, buflen, "module v%d != library v%d", + version, MB_VERSION); + return (-1); + } + return (0); +} + +int bsde_get_rule_count(size_t buflen, char *errstr) { size_t len; int error; int rule_count; len = sizeof(rule_count); error = sysctlbyname(MIB ".rule_count", &rule_count, &len, NULL, 0); if (error) { len = snprintf(errstr, buflen, strerror(errno)); return (-1); } if (len != sizeof(rule_count)) { len = snprintf(errstr, buflen, "Data error in %s.rule_count", MIB); return (-1); } return (rule_count); } int bsde_get_rule_slots(size_t buflen, char *errstr) { size_t len; int error; int rule_slots; len = sizeof(rule_slots); error = sysctlbyname(MIB ".rule_slots", &rule_slots, &len, NULL, 0); if (error) { len = snprintf(errstr, buflen, strerror(errno)); return (-1); } if (len != sizeof(rule_slots)) { len = snprintf(errstr, buflen, "Data error in %s.rule_slots", MIB); return (-1); } return (rule_slots); } /* * Returns 0 for success; * Returns -1 for failure; * Returns -2 for not present */ int bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, char *errstr) { int name[10]; size_t len, size; int error; + if (bsde_check_version(errlen, errstr) != 0) + return (-1); + len = 10; error = bsde_get_mib(MIB ".rules", name, &len); if (error) { len = snprintf(errstr, errlen, "%s: %s", MIB ".rules", strerror(errno)); return (-1); } size = sizeof(*rule); name[len] = rulenum; len++; error = sysctl(name, len, rule, &size, NULL, 0); if (error == -1 && errno == ENOENT) return (-2); if (error) { len = snprintf(errstr, errlen, "%s.%d: %s", MIB ".rules", rulenum, strerror(errno)); return (-1); } else if (size != sizeof(*rule)) { len = snprintf(errstr, errlen, "Data error in %s.%d: %s", MIB ".rules", rulenum, strerror(errno)); return (-1); } return (0); } int bsde_delete_rule(int rulenum, size_t buflen, char *errstr) { struct mac_bsdextended_rule rule; int name[10]; size_t len, size; int error; + if (bsde_check_version(buflen, errstr) != 0) + return (-1); + len = 10; error = bsde_get_mib(MIB ".rules", name, &len); if (error) { len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", strerror(errno)); return (-1); } name[len] = rulenum; len++; size = sizeof(rule); error = sysctl(name, len, NULL, NULL, &rule, 0); if (error) { len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", rulenum, strerror(errno)); return (-1); } return (0); } int bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr) { int name[10]; size_t len, size; int error; + if (bsde_check_version(buflen, errstr) != 0) + return (-1); + len = 10; error = bsde_get_mib(MIB ".rules", name, &len); if (error) { len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", strerror(errno)); return (-1); } name[len] = rulenum; len++; size = sizeof(*rule); error = sysctl(name, len, NULL, NULL, rule, size); if (error) { len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", rulenum, strerror(errno)); return (-1); } return (0); } int bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr) { char charstr[BUFSIZ]; int name[10]; size_t len, size; int error, rule_slots; + + if (bsde_check_version(buflen, errstr) != 0) + return (-1); len = 10; error = bsde_get_mib(MIB ".rules", name, &len); if (error) { len = snprintf(errstr, buflen, "%s: %s", MIB ".rules", strerror(errno)); return (-1); } rule_slots = bsde_get_rule_slots(BUFSIZ, charstr); if (rule_slots == -1) { len = snprintf(errstr, buflen, "unable to get rule slots: %s", strerror(errno)); return (-1); } name[len] = rule_slots; len++; size = sizeof(*rule); error = sysctl(name, len, NULL, NULL, rule, size); if (error) { len = snprintf(errstr, buflen, "%s.%d: %s", MIB ".rules", rule_slots, strerror(errno)); return (-1); } if (rulenum != NULL) *rulenum = rule_slots; return (0); } Index: head/lib/libugidfw/ugidfw.h =================================================================== --- head/lib/libugidfw/ugidfw.h (revision 157985) +++ head/lib/libugidfw/ugidfw.h (revision 157986) @@ -1,61 +1,58 @@ /*- * Copyright (c) 2002, 2004 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by Network Associates * Laboratories, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _UGIDFW_H #define _UGIDFW_H __BEGIN_DECLS int bsde_rule_to_string(struct mac_bsdextended_rule *rule, char *buf, size_t buflen); -int bsde_parse_identity(int argc, char *argv[], - struct mac_bsdextended_identity *identity, size_t buflen, - char *errstr); int bsde_parse_mode(int argc, char *argv[], mode_t *mode, size_t buflen, char *errstr); int bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule, size_t buflen, char *errstr); int bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr); int bsde_get_mib(const char *string, int *name, size_t *namelen); int bsde_get_rule_count(size_t buflen, char *errstr); int bsde_get_rule_slots(size_t buflen, char *errstr); int bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen, char *errstr); int bsde_delete_rule(int rulenum, size_t buflen, char *errstr); int bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr); int bsde_add_rule(int *rulename, struct mac_bsdextended_rule *rule, size_t buflen, char *errstr); __END_DECLS #endif Index: head/sys/security/mac_bsdextended/mac_bsdextended.c =================================================================== --- head/sys/security/mac_bsdextended/mac_bsdextended.c (revision 157985) +++ head/sys/security/mac_bsdextended/mac_bsdextended.c (revision 157986) @@ -1,654 +1,787 @@ /*- * Copyright (c) 2005 Tom Rhodes * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2005 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * It was later enhanced by Tom Rhodes for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Developed by the TrustedBSD Project. * "BSD Extended" MAC policy, allowing the administrator to impose * mandatory rules regarding users and some system objects. */ #include #include #include #include #include +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include #include static struct mtx mac_bsdextended_mtx; SYSCTL_DECL(_security_mac); SYSCTL_NODE(_security_mac, OID_AUTO, bsdextended, CTLFLAG_RW, 0, "TrustedBSD extended BSD MAC policy controls"); static int mac_bsdextended_enabled = 1; SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, enabled, CTLFLAG_RW, &mac_bsdextended_enabled, 0, "Enforce extended BSD policy"); TUNABLE_INT("security.mac.bsdextended.enabled", &mac_bsdextended_enabled); MALLOC_DEFINE(M_MACBSDEXTENDED, "mac_bsdextended", "BSD Extended MAC rule"); #define MAC_BSDEXTENDED_MAXRULES 250 static struct mac_bsdextended_rule *rules[MAC_BSDEXTENDED_MAXRULES]; static int rule_count = 0; static int rule_slots = 0; +static int rule_version = MB_VERSION; SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_count, CTLFLAG_RD, &rule_count, 0, "Number of defined rules\n"); SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_slots, CTLFLAG_RD, &rule_slots, 0, "Number of used rule slots\n"); +SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, rule_version, CTLFLAG_RD, + &rule_version, 0, "Version number for API\n"); /* * This is just used for logging purposes, eventually we would like * to log much more then failed requests. */ static int mac_bsdextended_logging; SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, logging, CTLFLAG_RW, &mac_bsdextended_logging, 0, "Log failed authorization requests"); /* * This tunable is here for compatibility. It will allow the user * to switch between the new mode (first rule matches) and the old * functionality (all rules match). */ static int mac_bsdextended_firstmatch_enabled; SYSCTL_INT(_security_mac_bsdextended, OID_AUTO, firstmatch_enabled, CTLFLAG_RW, &mac_bsdextended_firstmatch_enabled, 1, "Disable/enable match first rule functionality"); static int mac_bsdextended_rule_valid(struct mac_bsdextended_rule *rule) { - if ((rule->mbr_subject.mbi_flags | MBI_BITS) != MBI_BITS) + if ((rule->mbr_subject.mbs_flags | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) return (EINVAL); - if ((rule->mbr_object.mbi_flags | MBI_BITS) != MBI_BITS) + if ((rule->mbr_subject.mbs_neg | MBS_ALL_FLAGS) != MBS_ALL_FLAGS) return (EINVAL); + if ((rule->mbr_object.mbo_flags | MBO_ALL_FLAGS) != MBO_ALL_FLAGS) + return (EINVAL); + + if ((rule->mbr_object.mbo_neg | MBO_ALL_FLAGS) != MBO_ALL_FLAGS) + return (EINVAL); + + if ((rule->mbr_object.mbo_neg | MBO_TYPE_DEFINED) && + (rule->mbr_object.mbo_type | MBO_ALL_TYPE) != MBO_ALL_TYPE) + return (EINVAL); + if ((rule->mbr_mode | MBI_ALLPERM) != MBI_ALLPERM) return (EINVAL); return (0); } static int sysctl_rule(SYSCTL_HANDLER_ARGS) { struct mac_bsdextended_rule temprule, *ruleptr; u_int namelen; int error, index, *name; error = 0; name = (int *)arg1; namelen = arg2; /* printf("bsdextended sysctl handler (namelen %d)\n", namelen); */ if (namelen != 1) return (EINVAL); index = name[0]; if (index >= MAC_BSDEXTENDED_MAXRULES) return (ENOENT); ruleptr = NULL; if (req->newptr && req->newlen != 0) { error = SYSCTL_IN(req, &temprule, sizeof(temprule)); if (error) return (error); MALLOC(ruleptr, struct mac_bsdextended_rule *, sizeof(*ruleptr), M_MACBSDEXTENDED, M_WAITOK | M_ZERO); } mtx_lock(&mac_bsdextended_mtx); if (req->oldptr) { if (index < 0 || index > rule_slots + 1) { error = ENOENT; goto out; } if (rules[index] == NULL) { error = ENOENT; goto out; } temprule = *rules[index]; } if (req->newptr && req->newlen == 0) { /* printf("deletion\n"); */ KASSERT(ruleptr == NULL, ("sysctl_rule: ruleptr != NULL")); ruleptr = rules[index]; if (ruleptr == NULL) { error = ENOENT; goto out; } rule_count--; rules[index] = NULL; } else if (req->newptr) { error = mac_bsdextended_rule_valid(&temprule); if (error) goto out; if (rules[index] == NULL) { /* printf("addition\n"); */ *ruleptr = temprule; rules[index] = ruleptr; ruleptr = NULL; if (index + 1 > rule_slots) rule_slots = index + 1; rule_count++; } else { /* printf("replacement\n"); */ *rules[index] = temprule; } } out: mtx_unlock(&mac_bsdextended_mtx); if (ruleptr != NULL) FREE(ruleptr, M_MACBSDEXTENDED); if (req->oldptr && error == 0) error = SYSCTL_OUT(req, &temprule, sizeof(temprule)); return (error); } SYSCTL_NODE(_security_mac_bsdextended, OID_AUTO, rules, CTLFLAG_RW, sysctl_rule, "BSD extended MAC rules"); static void mac_bsdextended_init(struct mac_policy_conf *mpc) { /* Initialize ruleset lock. */ mtx_init(&mac_bsdextended_mtx, "mac_bsdextended lock", NULL, MTX_DEF); /* Register dynamic sysctl's for rules. */ } static void mac_bsdextended_destroy(struct mac_policy_conf *mpc) { /* Destroy ruleset lock. */ mtx_destroy(&mac_bsdextended_mtx); /* Tear down sysctls. */ } static int mac_bsdextended_rulecheck(struct mac_bsdextended_rule *rule, - struct ucred *cred, uid_t object_uid, gid_t object_gid, int acc_mode) + struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode) { int match; + int i; /* * Is there a subject match? */ mtx_assert(&mac_bsdextended_mtx, MA_OWNED); - if (rule->mbr_subject.mbi_flags & MBI_UID_DEFINED) { - match = (rule->mbr_subject.mbi_uid == cred->cr_uid || - rule->mbr_subject.mbi_uid == cred->cr_ruid || - rule->mbr_subject.mbi_uid == cred->cr_svuid); + if (rule->mbr_subject.mbs_flags & MBS_UID_DEFINED) { + match = ((cred->cr_uid <= rule->mbr_subject.mbs_uid_max && + cred->cr_uid >= rule->mbr_subject.mbs_uid_min) || + (cred->cr_ruid <= rule->mbr_subject.mbs_uid_max && + cred->cr_ruid >= rule->mbr_subject.mbs_uid_min) || + (cred->cr_svuid <= rule->mbr_subject.mbs_uid_max && + cred->cr_svuid >= rule->mbr_subject.mbs_uid_min)); - if (rule->mbr_subject.mbi_flags & MBI_NEGATED) + if (rule->mbr_subject.mbs_neg & MBS_UID_DEFINED) match = !match; if (!match) return (0); } - if (rule->mbr_subject.mbi_flags & MBI_GID_DEFINED) { - match = (groupmember(rule->mbr_subject.mbi_gid, cred) || - rule->mbr_subject.mbi_gid == cred->cr_rgid || - rule->mbr_subject.mbi_gid == cred->cr_svgid); + if (rule->mbr_subject.mbs_flags & MBS_GID_DEFINED) { + match = ((cred->cr_rgid <= rule->mbr_subject.mbs_gid_max && + cred->cr_rgid >= rule->mbr_subject.mbs_gid_min) || + (cred->cr_svgid <= rule->mbr_subject.mbs_gid_max && + cred->cr_svgid >= rule->mbr_subject.mbs_gid_min)); - if (rule->mbr_subject.mbi_flags & MBI_NEGATED) + if (!match) { + for (i = 0; i < cred->cr_ngroups; i++) + if (cred->cr_groups[i] + <= rule->mbr_subject.mbs_gid_max && + cred->cr_groups[i] + >= rule->mbr_subject.mbs_gid_min) { + match = 1; + break; + } + } + + if (rule->mbr_subject.mbs_neg & MBS_GID_DEFINED) match = !match; if (!match) return (0); } + if (rule->mbr_subject.mbs_flags & MBS_PRISON_DEFINED) { + match = (cred->cr_prison != NULL && + cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison); + + if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED) + match = !match; + + if (!match) + return (0); + } + /* * Is there an object match? */ - if (rule->mbr_object.mbi_flags & MBI_UID_DEFINED) { - match = (rule->mbr_object.mbi_uid == object_uid); + if (rule->mbr_object.mbo_flags & MBO_UID_DEFINED) { + match = (vap->va_uid <= rule->mbr_object.mbo_uid_max && + vap->va_uid >= rule->mbr_object.mbo_uid_min); - if (rule->mbr_object.mbi_flags & MBI_NEGATED) + if (rule->mbr_object.mbo_neg & MBO_UID_DEFINED) match = !match; if (!match) return (0); } - if (rule->mbr_object.mbi_flags & MBI_GID_DEFINED) { - match = (rule->mbr_object.mbi_gid == object_gid); + if (rule->mbr_object.mbo_flags & MBO_GID_DEFINED) { + match = (vap->va_gid <= rule->mbr_object.mbo_gid_max && + vap->va_gid >= rule->mbr_object.mbo_gid_min); - if (rule->mbr_object.mbi_flags & MBI_NEGATED) + if (rule->mbr_object.mbo_neg & MBO_GID_DEFINED) match = !match; if (!match) return (0); } + if (rule->mbr_object.mbo_flags & MBO_FSID_DEFINED) { + match = (bcmp(&(vp->v_mount->mnt_stat.f_fsid), + &(rule->mbr_object.mbo_fsid), + sizeof(rule->mbr_object.mbo_fsid)) == 0); + + if (rule->mbr_object.mbo_neg & MBO_FSID_DEFINED) + match = !match; + + if (!match) + return 0; + } + + if (rule->mbr_object.mbo_flags & MBO_SUID) { + match = (vap->va_mode & VSUID); + + if (rule->mbr_object.mbo_neg & MBO_SUID) + match = !match; + + if (!match) + return 0; + } + + if (rule->mbr_object.mbo_flags & MBO_SGID) { + match = (vap->va_mode & VSGID); + + if (rule->mbr_object.mbo_neg & MBO_SGID) + match = !match; + + if (!match) + return 0; + } + + if (rule->mbr_object.mbo_flags & MBO_UID_SUBJECT) { + match = (vap->va_uid == cred->cr_uid || + vap->va_uid == cred->cr_ruid || + vap->va_uid == cred->cr_svuid); + + if (rule->mbr_object.mbo_neg & MBO_UID_SUBJECT) + match = !match; + + if (!match) + return 0; + } + + if (rule->mbr_object.mbo_flags & MBO_GID_SUBJECT) { + match = (groupmember(vap->va_gid, cred) || + vap->va_gid == cred->cr_rgid || + vap->va_gid == cred->cr_svgid); + + if (rule->mbr_object.mbo_neg & MBO_GID_SUBJECT) + match = !match; + + if (!match) + return 0; + } + + if (rule->mbr_object.mbo_flags & MBO_TYPE_DEFINED) { + switch (vap->va_type) { + case VREG: + match = (rule->mbr_object.mbo_type & MBO_TYPE_REG); + break; + case VDIR: + match = (rule->mbr_object.mbo_type & MBO_TYPE_DIR); + break; + case VBLK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_BLK); + break; + case VCHR: + match = (rule->mbr_object.mbo_type & MBO_TYPE_CHR); + break; + case VLNK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_LNK); + break; + case VSOCK: + match = (rule->mbr_object.mbo_type & MBO_TYPE_SOCK); + break; + case VFIFO: + match = (rule->mbr_object.mbo_type & MBO_TYPE_FIFO); + break; + default: + match = 0; + } + + if (rule->mbr_object.mbo_neg & MBO_TYPE_DEFINED) + match = !match; + + if (!match) + return 0; + } + /* * Is the access permitted? */ if ((rule->mbr_mode & acc_mode) != acc_mode) { if (mac_bsdextended_logging) log(LOG_AUTHPRIV, "mac_bsdextended: %d:%d request %d" " on %d:%d failed. \n", cred->cr_ruid, - cred->cr_rgid, acc_mode, object_uid, object_gid); + cred->cr_rgid, acc_mode, vap->va_uid, vap->va_gid); return (EACCES); /* Matching rule denies access */ } /* * If the rule matched, permits access, and first match is enabled, * return success. */ if (mac_bsdextended_firstmatch_enabled) return (EJUSTRETURN); else return(0); } static int -mac_bsdextended_check(struct ucred *cred, uid_t object_uid, gid_t object_gid, +mac_bsdextended_check(struct ucred *cred, struct vnode *vp, struct vattr *vap, int acc_mode) { int error, i; if (suser_cred(cred, 0) == 0) return (0); mtx_lock(&mac_bsdextended_mtx); for (i = 0; i < rule_slots; i++) { if (rules[i] == NULL) continue; /* * Since we do not separately handle append, map append to * write. */ if (acc_mode & MBI_APPEND) { acc_mode &= ~MBI_APPEND; acc_mode |= MBI_WRITE; } - error = mac_bsdextended_rulecheck(rules[i], cred, object_uid, - object_gid, acc_mode); + error = mac_bsdextended_rulecheck(rules[i], cred, + vp, vap, acc_mode); if (error == EJUSTRETURN) break; if (error) { mtx_unlock(&mac_bsdextended_mtx); return (error); } } mtx_unlock(&mac_bsdextended_mtx); return (0); } static int mac_bsdextended_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode) { int error; struct vattr vap; if (!mac_bsdextended_enabled) return (0); error = VOP_GETATTR(vp, &vap, cred, curthread); if (error) return (error); - return (mac_bsdextended_check(cred, vap.va_uid, vap.va_gid, - acc_mode)); + return (mac_bsdextended_check(cred, vp, &vap, acc_mode)); } static int mac_bsdextended_check_system_swapon(struct ucred *cred, struct vnode *vp, struct label *label) { return (mac_bsdextended_check_vp(cred, vp, MBI_WRITE)); } static int mac_bsdextended_check_vnode_access(struct ucred *cred, struct vnode *vp, struct label *label, int acc_mode) { return (mac_bsdextended_check_vp(cred, vp, acc_mode)); } static int mac_bsdextended_check_vnode_chdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { return (mac_bsdextended_check_vp(cred, dvp, MBI_EXEC)); } static int mac_bsdextended_check_vnode_chroot(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { return (mac_bsdextended_check_vp(cred, dvp, MBI_EXEC)); } static int mac_bsdextended_check_create_vnode(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp, struct vattr *vap) { return (mac_bsdextended_check_vp(cred, dvp, MBI_WRITE)); } static int mac_bsdextended_check_vnode_delete(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { int error; error = mac_bsdextended_check_vp(cred, dvp, MBI_WRITE); if (error) return (error); return (mac_bsdextended_check_vp(cred, vp, MBI_WRITE)); } static int mac_bsdextended_check_vnode_deleteacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_deleteextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name) { return (mac_bsdextended_check_vp(cred, vp, MBI_WRITE)); } static int mac_bsdextended_check_vnode_exec(struct ucred *cred, struct vnode *vp, struct label *label, struct image_params *imgp, struct label *execlabel) { return (mac_bsdextended_check_vp(cred, vp, MBI_READ|MBI_EXEC)); } static int mac_bsdextended_check_vnode_getacl(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type) { return (mac_bsdextended_check_vp(cred, vp, MBI_STAT)); } static int mac_bsdextended_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio) { return (mac_bsdextended_check_vp(cred, vp, MBI_READ)); } static int mac_bsdextended_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { int error; error = mac_bsdextended_check_vp(cred, dvp, MBI_WRITE); if (error) return (error); error = mac_bsdextended_check_vp(cred, vp, MBI_WRITE); if (error) return (error); return (0); } static int mac_bsdextended_check_vnode_listextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace) { return (mac_bsdextended_check_vp(cred, vp, MBI_READ)); } static int mac_bsdextended_check_vnode_lookup(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct componentname *cnp) { return (mac_bsdextended_check_vp(cred, dvp, MBI_EXEC)); } static int mac_bsdextended_check_vnode_open(struct ucred *cred, struct vnode *vp, struct label *filelabel, int acc_mode) { return (mac_bsdextended_check_vp(cred, vp, acc_mode)); } static int mac_bsdextended_check_vnode_readdir(struct ucred *cred, struct vnode *dvp, struct label *dlabel) { return (mac_bsdextended_check_vp(cred, dvp, MBI_READ)); } static int mac_bsdextended_check_vnode_readdlink(struct ucred *cred, struct vnode *vp, struct label *label) { return (mac_bsdextended_check_vp(cred, vp, MBI_READ)); } static int mac_bsdextended_check_vnode_rename_from(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) { int error; error = mac_bsdextended_check_vp(cred, dvp, MBI_WRITE); if (error) return (error); error = mac_bsdextended_check_vp(cred, vp, MBI_WRITE); return (error); } static int mac_bsdextended_check_vnode_rename_to(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, int samedir, struct componentname *cnp) { int error; error = mac_bsdextended_check_vp(cred, dvp, MBI_WRITE); if (error) return (error); if (vp != NULL) error = mac_bsdextended_check_vp(cred, vp, MBI_WRITE); return (error); } static int mac_bsdextended_check_vnode_revoke(struct ucred *cred, struct vnode *vp, struct label *label) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_setacl_vnode(struct ucred *cred, struct vnode *vp, struct label *label, acl_type_t type, struct acl *acl) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_setextattr(struct ucred *cred, struct vnode *vp, struct label *label, int attrnamespace, const char *name, struct uio *uio) { return (mac_bsdextended_check_vp(cred, vp, MBI_WRITE)); } static int mac_bsdextended_check_vnode_setflags(struct ucred *cred, struct vnode *vp, struct label *label, u_long flags) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_setmode(struct ucred *cred, struct vnode *vp, struct label *label, mode_t mode) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_setowner(struct ucred *cred, struct vnode *vp, struct label *label, uid_t uid, gid_t gid) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_setutimes(struct ucred *cred, struct vnode *vp, struct label *label, struct timespec atime, struct timespec utime) { return (mac_bsdextended_check_vp(cred, vp, MBI_ADMIN)); } static int mac_bsdextended_check_vnode_stat(struct ucred *active_cred, struct ucred *file_cred, struct vnode *vp, struct label *label) { return (mac_bsdextended_check_vp(active_cred, vp, MBI_STAT)); } static struct mac_policy_ops mac_bsdextended_ops = { .mpo_destroy = mac_bsdextended_destroy, .mpo_init = mac_bsdextended_init, .mpo_check_system_swapon = mac_bsdextended_check_system_swapon, .mpo_check_vnode_access = mac_bsdextended_check_vnode_access, .mpo_check_vnode_chdir = mac_bsdextended_check_vnode_chdir, .mpo_check_vnode_chroot = mac_bsdextended_check_vnode_chroot, .mpo_check_vnode_create = mac_bsdextended_check_create_vnode, .mpo_check_vnode_delete = mac_bsdextended_check_vnode_delete, .mpo_check_vnode_deleteacl = mac_bsdextended_check_vnode_deleteacl, .mpo_check_vnode_deleteextattr = mac_bsdextended_check_vnode_deleteextattr, .mpo_check_vnode_exec = mac_bsdextended_check_vnode_exec, .mpo_check_vnode_getacl = mac_bsdextended_check_vnode_getacl, .mpo_check_vnode_getextattr = mac_bsdextended_check_vnode_getextattr, .mpo_check_vnode_link = mac_bsdextended_check_vnode_link, .mpo_check_vnode_listextattr = mac_bsdextended_check_vnode_listextattr, .mpo_check_vnode_lookup = mac_bsdextended_check_vnode_lookup, .mpo_check_vnode_open = mac_bsdextended_check_vnode_open, .mpo_check_vnode_readdir = mac_bsdextended_check_vnode_readdir, .mpo_check_vnode_readlink = mac_bsdextended_check_vnode_readdlink, .mpo_check_vnode_rename_from = mac_bsdextended_check_vnode_rename_from, .mpo_check_vnode_rename_to = mac_bsdextended_check_vnode_rename_to, .mpo_check_vnode_revoke = mac_bsdextended_check_vnode_revoke, .mpo_check_vnode_setacl = mac_bsdextended_check_setacl_vnode, .mpo_check_vnode_setextattr = mac_bsdextended_check_vnode_setextattr, .mpo_check_vnode_setflags = mac_bsdextended_check_vnode_setflags, .mpo_check_vnode_setmode = mac_bsdextended_check_vnode_setmode, .mpo_check_vnode_setowner = mac_bsdextended_check_vnode_setowner, .mpo_check_vnode_setutimes = mac_bsdextended_check_vnode_setutimes, .mpo_check_vnode_stat = mac_bsdextended_check_vnode_stat, }; MAC_POLICY_SET(&mac_bsdextended_ops, mac_bsdextended, "TrustedBSD MAC/BSD Extended", MPC_LOADTIME_FLAG_UNLOADOK, NULL); Index: head/sys/security/mac_bsdextended/mac_bsdextended.h =================================================================== --- head/sys/security/mac_bsdextended/mac_bsdextended.h (revision 157985) +++ head/sys/security/mac_bsdextended/mac_bsdextended.h (revision 157986) @@ -1,72 +1,114 @@ /*- * Copyright (c) 1999-2002 Robert N. M. Watson * Copyright (c) 2001-2004 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed by Robert Watson for the TrustedBSD Project. * * This software was developed for the FreeBSD Project in part by Network * Associates Laboratories, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), * as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_SECURITY_MAC_BSDEXTENDED_H #define _SYS_SECURITY_MAC_BSDEXTENDED_H -#define MBI_UID_DEFINED 0x00000001 /* uid field should be used */ -#define MBI_GID_DEFINED 0x00000002 /* gid field should be used */ -#define MBI_NEGATED 0x00000004 /* negate uid/gid matches */ -#define MBI_BITS (MBI_UID_DEFINED | MBI_GID_DEFINED | MBI_NEGATED) +#define MB_VERSION 2 /* Used to check library and kernel are the same. */ /* * Rights that can be represented in mbr_mode. These have the same values * as the V* rights in vnode.h, but in order to avoid sharing user and * kernel constants, we define them here. That will also improve ABI * stability if the in-kernel values change. */ #define MBI_EXEC 000100 #define MBI_WRITE 000200 #define MBI_READ 000400 #define MBI_ADMIN 010000 #define MBI_STAT 020000 #define MBI_APPEND 040000 #define MBI_ALLPERM (MBI_EXEC | MBI_WRITE | MBI_READ | MBI_ADMIN | \ MBI_STAT | MBI_APPEND) -struct mac_bsdextended_identity { - int mbi_flags; - uid_t mbi_uid; - gid_t mbi_gid; +#define MBS_UID_DEFINED 0x00000001 /* uid field should be matched */ +#define MBS_GID_DEFINED 0x00000002 /* gid field should be matched */ +#define MBS_PRISON_DEFINED 0x00000004 /* prison field should be matched */ + +#define MBS_ALL_FLAGS (MBS_UID_DEFINED | MBS_GID_DEFINED | MBS_PRISON_DEFINED) + +struct mac_bsdextended_subject { + int mbs_flags; + int mbs_neg; + uid_t mbs_uid_min; + uid_t mbs_uid_max; + gid_t mbs_gid_min; + gid_t mbs_gid_max; + int mbs_prison; }; +#define MBO_UID_DEFINED 0x00000001 /* uid field should be matched */ +#define MBO_GID_DEFINED 0x00000002 /* gid field should be matched */ +#define MBO_FSID_DEFINED 0x00000004 /* fsid field should be matched */ +#define MBO_SUID 0x00000008 /* object must be suid */ +#define MBO_SGID 0x00000010 /* object must be sgid */ +#define MBO_UID_SUBJECT 0x00000020 /* uid must match subject */ +#define MBO_GID_SUBJECT 0x00000040 /* gid must match subject */ +#define MBO_TYPE_DEFINED 0x00000080 /* object type should be matched */ + +#define MBO_ALL_FLAGS (MBO_UID_DEFINED | MBO_GID_DEFINED | MBO_FSID_DEFINED | \ + MBO_SUID | MBO_SGID | MBO_UID_SUBJECT | MBO_GID_SUBJECT | \ + MBO_TYPE_DEFINED) + +#define MBO_TYPE_REG 0x00000001 +#define MBO_TYPE_DIR 0x00000002 +#define MBO_TYPE_BLK 0x00000004 +#define MBO_TYPE_CHR 0x00000008 +#define MBO_TYPE_LNK 0x00000010 +#define MBO_TYPE_SOCK 0x00000020 +#define MBO_TYPE_FIFO 0x00000040 + +#define MBO_ALL_TYPE (MBO_TYPE_REG | MBO_TYPE_DIR | MBO_TYPE_BLK | \ + MBO_TYPE_CHR | MBO_TYPE_LNK | MBO_TYPE_SOCK | MBO_TYPE_FIFO) + +struct mac_bsdextended_object { + int mbo_flags; + int mbo_neg; + uid_t mbo_uid_min; + uid_t mbo_uid_max; + gid_t mbo_gid_min; + gid_t mbo_gid_max; + struct fsid mbo_fsid; + int mbo_type; +}; + struct mac_bsdextended_rule { - struct mac_bsdextended_identity mbr_subject; - struct mac_bsdextended_identity mbr_object; + struct mac_bsdextended_subject mbr_subject; + struct mac_bsdextended_object mbr_object; mode_t mbr_mode; /* maximum access */ }; #endif /* _SYS_SECURITY_MAC_BSDEXTENDED_H */ Index: head/tools/regression/mac/mac_bsdextended/test_matches.sh =================================================================== --- head/tools/regression/mac/mac_bsdextended/test_matches.sh (nonexistent) +++ head/tools/regression/mac/mac_bsdextended/test_matches.sh (revision 157986) @@ -0,0 +1,167 @@ +#!/bin/sh +# +# $FreeBSD$ +# + +uidrange="60000:100000" +gidrange="60000:100000" +uidinrange="nobody" +uidoutrange="daemon" +gidinrange="nobody" # We expect $uidinrange in this group +gidoutrange="daemon" # We expect $uidinrange in this group + +playground="/stuff/nobody/" # Must not be on root fs + +# +# Setup +# +rm -f $playground/test* +ugidfw remove 1 + +file1=$playground/test-$uidinrange +file2=$playground/test-$uidoutrange +cat < $playground/test-script.pl +if (open(F, ">" . shift)) { exit 0; } else { exit 1; } +EOF +command1="perl $playground/test-script.pl $file1" +command2="perl $playground/test-script.pl $file2" + +echo -n "$uidinrange file: " +su -m $uidinrange -c "$command1 && echo good" +chown "$uidinrange":"$gidinrange" $file1 +chmod a+w $file1 + +echo -n "$uidoutrange file: " +$command2 && echo good +chown "$uidoutrange":"$gidoutrange" $file2 +chmod a+w $file2 + +# +# No rules +# +echo -n "no rules $uidinrange: " +su -fm $uidinrange -c "$command1 && echo good" +echo -n "no rules $uidoutrange: " +su -fm $uidoutrange -c "$command1 && echo good" + +# +# Subject Match on uid +# +ugidfw set 1 subject uid $uidrange object mode rasx +echo -n "subject uid in range: " +su -fm $uidinrange -c "$command1 || echo good" +echo -n "subject uid out range: " +su -fm $uidoutrange -c "$command1 && echo good" + +# +# Subject Match on gid +# +ugidfw set 1 subject gid $gidrange object mode rasx +echo -n "subject gid in range: " +su -fm $uidinrange -c "$command1 || echo good" +echo -n "subject gid out range: " +su -fm $uidoutrange -c "$command1 && echo good" + +# +# Subject Match on jail +# +echo -n "subject matching jailid: " +rm -f $playground/test-jail +jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 3; touch $playground/test-jail) &"` +ugidfw set 1 subject jailid $jailid object mode rasx +sleep 6 +if [ ! -f $playground/test-jail ] ; then echo good ; fi + +echo -n "subject nonmatching jailid: " +rm -f $playground/test-jail +jailid=`jail -i / localhost 127.0.0.1 /usr/sbin/daemon -f /bin/sh -c "(sleep 3; touch $playground/test-jail) &"` +sleep 6 +if [ -f $playground/test-jail ] ; then echo good ; fi + +# +# Object uid +# +ugidfw set 1 subject object uid $uidrange mode rasx +echo -n "object uid in range: " +su -fm $uidinrange -c "$command1 || echo good" +echo -n "object uid out range: " +su -fm $uidinrange -c "$command2 && echo good" +ugidfw set 1 subject object uid $uidrange mode rasx +echo -n "object uid in range (differennt subject): " +su -fm $uidoutrange -c "$command1 || echo good" +echo -n "object uid out range (differennt subject): " +su -fm $uidoutrange -c "$command2 && echo good" + +# +# Object gid +# +ugidfw set 1 subject object gid $uidrange mode rasx +echo -n "object gid in range: " +su -fm $uidinrange -c "$command1 || echo good" +echo -n "object gid out range: " +su -fm $uidinrange -c "$command2 && echo good" +echo -n "object gid in range (differennt subject): " +su -fm $uidoutrange -c "$command1 || echo good" +echo -n "object gid out range (differennt subject): " +su -fm $uidoutrange -c "$command2 && echo good" + +# +# Object filesys +# +ugidfw set 1 subject uid $uidrange object filesys / mode rasx +echo -n "object out of filesys: " +su -fm $uidinrange -c "$command1 && echo good" +ugidfw set 1 subject uid $uidrange object filesys $playground mode rasx +echo -n "object in filesys: " +su -fm $uidinrange -c "$command1 || echo good" + +# +# Object suid +# +ugidfw set 1 subject uid $uidrange object suid mode rasx +echo -n "object notsuid: " +su -fm $uidinrange -c "$command1 && echo good" +chmod u+s $file1 +echo -n "object suid: " +su -fm $uidinrange -c "$command1 || echo good" +chmod u-s $file1 + +# +# Object sgid +# +ugidfw set 1 subject uid $uidrange object sgid mode rasx +echo -n "object notsgid: " +su -fm $uidinrange -c "$command1 && echo good" +chmod g+s $file1 +echo -n "object sgid: " +su -fm $uidinrange -c "$command1 || echo good" +chmod g-s $file1 + +# +# Object uid matches subject +# +ugidfw set 1 subject uid $uidrange object uid_of_subject mode rasx +echo -n "object uid notmatches subject: " +su -fm $uidinrange -c "$command2 && echo good" +echo -n "object uid matches subject: " +su -fm $uidinrange -c "$command1 || echo good" + +# +# Object gid matches subject +# +ugidfw set 1 subject uid $uidrange object gid_of_subject mode rasx +echo -n "object gid notmatches subject: " +su -fm $uidinrange -c "$command2 && echo good" +echo -n "object gid matches subject: " +su -fm $uidinrange -c "$command1 || echo good" + +# +# Object type +# +ugidfw set 1 subject uid $uidrange object type dbclsp mode rasx +echo -n "object not type: " +su -fm $uidinrange -c "$command1 && echo good" +ugidfw set 1 subject uid $uidrange object type r mode rasx +echo -n "object type: " +su -fm $uidinrange -c "$command1 || echo good" + Property changes on: head/tools/regression/mac/mac_bsdextended/test_matches.sh ___________________________________________________________________ Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: head/tools/regression/mac/mac_bsdextended/test_ugidfw.c =================================================================== --- head/tools/regression/mac/mac_bsdextended/test_ugidfw.c (revision 157985) +++ head/tools/regression/mac/mac_bsdextended/test_ugidfw.c (revision 157986) @@ -1,196 +1,238 @@ /*- * Copyright (c) 2005 McAfee, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -#include +#include #include +#include #include #include #include #include #include #include #include #include /* * Starting point for a regression test for mac_bsdextended(4) and the * supporting libugidfw(3). */ void usage(void) { fprintf(stderr, "test_ugidfw\n"); exit(-1); } /* * This section of the regression test passes some test cases through the * rule<->string routines to confirm they work approximately as desired. */ /* * List of users and groups we must check exists before we can begin, since * they are used in the string test rules. We use users and groups that will * always exist in a default install used for regression testing. */ static const char *test_users[] = { "root", "daemon", "operator", "bin", }; static const int test_users_len = sizeof(test_users) / sizeof(char *); static const char *test_groups[] = { "wheel", "daemon", "operator", "bin", }; static const int test_groups_len = sizeof(test_groups) / sizeof(char *); /* * List of test strings that must go in (and come out) of libugidfw intact. */ static const char *test_strings[] = { /* Variations on subject and object uids. */ "subject uid root object uid root mode n", "subject uid root object uid daemon mode n", "subject uid daemon object uid root mode n", "subject uid daemon object uid daemon mode n", /* Variations on mode. */ "subject uid root object uid root mode a", "subject uid root object uid root mode r", "subject uid root object uid root mode s", "subject uid root object uid root mode w", "subject uid root object uid root mode x", "subject uid root object uid root mode arswx", /* Variations on subject and object gids. */ "subject gid wheel object gid wheel mode n", "subject gid wheel object gid daemon mode n", "subject gid daemon object gid wheel mode n", "subject gid daemon object gid daemon mode n", /* Subject uids and subject gids. */ "subject uid bin gid daemon object uid operator gid wheel mode n", /* Not */ "subject not uid operator object uid bin mode n", "subject uid bin object not uid operator mode n", "subject not uid daemon object not uid operator mode n", + /* Ranges */ + "subject uid root:operator object gid wheel:bin mode n", + /* Jail ID */ + "subject jailid 1 object uid root mode n", + /* Filesys */ + "subject uid root object filesys / mode n", + "subject uid root object filesys /dev mode n", + /* S/UGID */ + "subject not uid root object sgid mode n", + "subject not uid root object sgid mode n", + /* Matching uid/gid */ + "subject not uid root:operator object not uid_of_subject mode n", + "subject not gid wheel:bin object not gid_of_subject mode n", + /* Object types */ + "subject uid root object type a mode a", + "subject uid root object type r mode a", + "subject uid root object type d mode a", + "subject uid root object type b mode a", + "subject uid root object type c mode a", + "subject uid root object type l mode a", + "subject uid root object type s mode a", + "subject uid root object type rbc mode a", + "subject uid root object type dls mode a", + /* Empty rules always match */ + "subject object mode a", + /* Partial negations */ + "subject ! uid root object mode n", + "subject ! gid wheel object mode n", + "subject ! jailid 2 object mode n", + "subject object ! uid root mode n", + "subject object ! gid wheel mode n", + "subject object ! filesys / mode n", + "subject object ! suid mode n", + "subject object ! sgid mode n", + "subject object ! uid_of_subject mode n", + "subject object ! gid_of_subject mode n", + "subject object ! type d mode n", + /* All out nonsense */ + "subject uid root ! gid wheel:bin ! jailid 1 " + "object ! uid root:daemon gid daemon filesys / suid sgid uid_of_subject gid_of_subject ! type r " + "mode rsx", }; static const int test_strings_len = sizeof(test_strings) / sizeof(char *); static void test_libugidfw_strings(void) { struct mac_bsdextended_rule rule; - char errorstr[128]; - char rulestr[128]; + char errorstr[256]; + char rulestr[256]; int i, error; for (i = 0; i < test_users_len; i++) { if (getpwnam(test_users[i]) == NULL) err(-1, "test_libugidfw_strings: getpwnam: %s", test_users[i]); } for (i = 0; i < test_groups_len; i++) { if (getgrnam(test_groups[i]) == NULL) err(-1, "test_libugidfw_strings: getgrnam: %s", test_groups[i]); } for (i = 0; i < test_strings_len; i++) { error = bsde_parse_rule_string(test_strings[i], &rule, - 128, errorstr); + sizeof(errorstr), errorstr); if (error == -1) errx(-1, "bsde_parse_rule_string: '%s' (%d): %s", test_strings[i], i, errorstr); - error = bsde_rule_to_string(&rule, rulestr, 128); + error = bsde_rule_to_string(&rule, rulestr, sizeof(rulestr)); if (error < 0) errx(-1, "bsde_rule_to_string: rule for '%s' " "returned %d", test_strings[i], error); if (strcmp(test_strings[i], rulestr) != 0) errx(-1, "test_libugidfw: '%s' in, '%s' out", test_strings[i], rulestr); } } int main(int argc, char *argv[]) { - char errorstr[128]; + char errorstr[256]; int count, slots; if (argc != 1) usage(); /* Print an error if a non-root user attemps to run the tests. */ if (getuid() != 0) { fprintf(stderr, "Error! Only root may run this utility\n"); return (EXIT_FAILURE); } /* * We can test some parts of the library without the MAC Framework * and policy loaded, so run those tests before calling * mac_is_present(). */ test_libugidfw_strings(); switch (mac_is_present("bsdextended")) { case -1: err(-1, "mac_is_present"); case 1: break; case 0: default: errx(-1, "mac_bsdextended not loaded"); } /* * Some simple up-front checks to see if we're able to query the * policy for basic state. We want the rule count to be 0 before * starting, but "slots" is a property of prior runs and so we ignore * the return value. */ - count = bsde_get_rule_count(128, errorstr); + count = bsde_get_rule_count(sizeof(errorstr), errorstr); if (count == -1) errx(-1, "bsde_get_rule_count: %s", errorstr); if (count != 0) errx(-1, "bsde_get_rule_count: %d rules", count); - slots = bsde_get_rule_slots(128, errorstr); + slots = bsde_get_rule_slots(sizeof(errorstr), errorstr); if (slots == -1) errx(-1, "bsde_get_rule_slots: %s", errorstr); return (0); } Index: head/usr.sbin/ugidfw/ugidfw.8 =================================================================== --- head/usr.sbin/ugidfw/ugidfw.8 (revision 157985) +++ head/usr.sbin/ugidfw/ugidfw.8 (revision 157986) @@ -1,210 +1,361 @@ .\" Copyright (c) 2002, 2004 Networks Associates Technology, Inc. .\" All rights reserved. .\" .\" This software was developed for the FreeBSD Project by Chris .\" Costello at Safeport Network Services and NAI Labs, the Security .\" Research Division of Network Associates, Inc. under DARPA/SPAWAR .\" contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS .\" research program. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd February 24, 2004 .Dt UGIDFW 8 .Os .Sh NAME .Nm ugidfw .Nd "firewall-like access controls for file system objects" .Sh SYNOPSIS .Nm .Cm add .Cm subject .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm jailid Ad jailid +.Oc .Cm object .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm filesys Ad path +.Oc +.Oo +.Op Cm \&! +.Cm suid +.Oc +.Oo +.Op Cm \&! +.Cm sgid +.Oc +.Oo +.Op Cm \&! +.Cm uid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm gid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm type Ar ardbclsp +.Oc .Cm mode .Ar arswxn .Nm .Cm list .Nm .Cm set .Ar rulenum .Cm subject .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm jailid Ad jailid +.Oc .Cm object .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm filesys Ad path +.Oc +.Oo +.Op Cm \&! +.Cm suid +.Oc +.Oo +.Op Cm \&! +.Cm sgid +.Oc +.Oo +.Op Cm \&! +.Cm uid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm gid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm type Ar ardbclsp +.Oc .Cm mode .Ar arswxn .Nm .Cm remove .Ar rulenum .Sh DESCRIPTION The .Nm utility provides an .Xr ipfw 8 Ns -like interface to manage access to file system objects by UID and GID, supported by the .Xr mac_bsdextended 4 .Xr mac 9 policy. .Pp The arguments are as follows: .Bl -tag -width indent -offset indent -.It Cm add -Add a new -.Nm -rule. .It Xo .Cm add .Cm subject -.Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Ar ... .Cm object -.Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Ar ... .Cm mode .Ar arswxn .Xc Add a new rule, automatically selecting the rule number. See the description of .Cm set for syntax information. .It Cm list Produces a list of all the current .Nm rules in the system. .It Xo .Cm set Ar rulenum .Cm subject -.Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Ar ... .Cm object -.Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Ar ... .Cm mode .Ar arswxn .Xc Add a new rule or modify an existing rule. The arguments are as follows: .Bl -tag -width ".Ar rulenum" .It Ar rulenum Rule number. Entries with a lower rule number are applied first; placing the most frequently-matched rules at the beginning of the list (i.e., lower-numbered) will yield a slight performance increase. .It Xo .Cm subject .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm jailid Ad jailid +.Oc .Xc -Subjects performing an operation must match -(or, if +Subjects performing an operation must match all the conditions given. +A leading .Cm not -is specified, must -.Em not -match) -the user and group specified by +means that the subject should not match the remainder of the specification. +A condition may be prefixed by +.Cm \&! +to indicate that particular condition must not match the subject. +The subject can be required to have a particular .Ar uid and/or -.Ar gid -for the rule to be applied. +.Ar gid . +A range of uids/gids can be specified, +seperated by a colon. +The subject can be required to be in a particular jail with the +.Ar jailid . .It Xo .Cm object .Op Cm not -.Op Cm uid Ar uid -.Op Cm gid Ar gid +.Oo +.Op Cm \&! +.Cm uid Ar uid | minuid:maxuid +.Oc +.Oo +.Op Cm \&! +.Cm gid Ar gid | mingid:maxgid +.Oc +.Oo +.Op Cm \&! +.Cm filesys Ad path +.Oc +.Oo +.Op Cm \&! +.Cm suid +.Oc +.Oo +.Op Cm \&! +.Cm sgid +.Oc +.Oo +.Op Cm \&! +.Cm uid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm gid_of_subject +.Oc +.Oo +.Op Cm \&! +.Cm type Ar ardbclsp +.Oc .Xc -Objects must be owned by -(or, if +The rule will apply only to objects matching all the specified conditions. +A leading .Cm not -is specified, must -.Em not -be owned by) -the user and/or group specified by +means that the object should not match all the remaining conditions. +A condition may be prefixed by +.Cm \&! +to indicate that particular condition must not match the object. +Objects can be required to be owned by the user and/or group specified by .Ar uid and/or -.Ar gid -for the rule to be applied. +.Ar gid . +A range of uids/gids can be specified, seperated by a colon. +The object can be required to be in a particular filesystem by +specifing the filesystem using +.Cm filesys . +Note, +if the filesystem is unmounted and remounted, +then the rule may need to be reapplied to ensure the correct filesystem +id is used. +The object can be required to have the +.Cm suid +or +.Cm sgid +bits set. +The owner of the object can be required to match the +.Cm uid_of_subject +or the +.Cm gid_of_subject +attempting the operation. +The type of the object can be restricted to a subset of +the following types. +.Pp +.Bl -tag -width ".Cm w" -compact -offset indent +.It Cm a +any file type +.It Cm r +a regular file +.It Cm d +a directory +.It Cm b +a block special device +.It Cm c +a character special device +.It Cm l +a symbolic link +.It Cm s +a unix domain socket +.It Cm p +a named pipe (FIFO) +.El .It Cm mode Ar arswxn Similar to .Xr chmod 1 , each character represents an access mode. If the rule applies, the specified access permissions are enforced for the object. When a character is specified in the rule, the rule will allow for the operation. Conversely, not including it will cause the operation to be denied. The definitions of each character are as follows: .Pp .Bl -tag -width ".Cm w" -compact -offset indent .It Cm a administrative operations .It Cm r read access .It Cm s access to file attributes .It Cm w write access .It Cm x execute access .It Cm n none .El .El .It Cm remove Ar rulenum Disable and remove the rule with the specified rule number. .El .Sh SEE ALSO .Xr mac_bsdextended 4 , .Xr mac 9 .Sh HISTORY The .Nm utility first appeared in .Fx 5.0 . .Sh AUTHORS This software was contributed to the .Fx Project by NAI Labs, the Security Research Division of Network Associates Inc.\& under DARPA/SPAWAR contract N66001-01-C-8035 .Pq Dq CBOSS , as part of the DARPA CHATS research program. Index: head/usr.sbin/ugidfw/ugidfw.c =================================================================== --- head/usr.sbin/ugidfw/ugidfw.c (revision 157985) +++ head/usr.sbin/ugidfw/ugidfw.c (revision 157986) @@ -1,210 +1,211 @@ /*- * Copyright (c) 2002, 2004 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by NAI Labs, the * Security Research Division of Network Associates, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include +#include #include #include #include #include #include #include #include #include void add_rule(int argc, char *argv[]); void list_rules(void); void remove_rule(int argc, char *argv[]); void set_rule(int argc, char *argv[]); void usage(void); void usage(void) { fprintf(stderr, "usage: ugidfw add [subject [not] [uid uid] [gid gid]]" " [object [not] [uid uid] \\\n"); fprintf(stderr, " [gid gid]] mode arswxn\n"); fprintf(stderr, " ugidfw list\n"); fprintf(stderr, " ugidfw set rulenum [subject [not] [uid uid] [gid gid]]" " [object [not] \\\n"); fprintf(stderr, " [uid uid] [gid gid]] mode arswxn\n"); fprintf(stderr, " ugidfw remove rulenum\n"); exit(1); } void add_rule(int argc, char *argv[]) { char errstr[BUFSIZ]; struct mac_bsdextended_rule rule; int error, rulenum; error = bsde_parse_rule(argc, argv, &rule, BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } error = bsde_add_rule(&rulenum, &rule, BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } printf("Added rule %d\n", rulenum); } void list_rules(void) { char errstr[BUFSIZ], charstr[BUFSIZ]; struct mac_bsdextended_rule rule; int error, i, rule_count, rule_slots; rule_slots = bsde_get_rule_slots(BUFSIZ, errstr); if (rule_slots == -1) { warnx("unable to get rule slots; mac_bsdextended.ko " "may not be loaded"); errx(1, "bsde_get_rule_slots: %s", errstr); } rule_count = bsde_get_rule_count(BUFSIZ, errstr); if (rule_count == -1) errx(1, "bsde_get_rule_count: %s", errstr); printf("%d slots, %d rules\n", rule_slots, rule_count); for (i = 0; i < rule_slots; i++) { error = bsde_get_rule(i, &rule, BUFSIZ, errstr); switch (error) { case -2: continue; case -1: warnx("rule %d: %s", i, errstr); continue; case 0: break; } if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1) warnx("unable to translate rule %d to string", i); else printf("%d %s\n", i, charstr); } } void set_rule(int argc, char *argv[]) { char errstr[BUFSIZ]; struct mac_bsdextended_rule rule; long value; int error, rulenum; char *endp; if (argc < 1) usage(); value = strtol(argv[0], &endp, 10); if (*endp != '\0') usage(); if ((long) value != (int) value || value < 0) usage(); rulenum = value; error = bsde_parse_rule(argc - 1, argv + 1, &rule, BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } error = bsde_set_rule(rulenum, &rule, BUFSIZ, errstr); if (error) { warnx("%s", errstr); return; } } void remove_rule(int argc, char *argv[]) { char errstr[BUFSIZ]; long value; int error, rulenum; char *endp; if (argc != 1) usage(); value = strtol(argv[0], &endp, 10); if (*endp != '\0') usage(); if ((long) value != (int) value || value < 0) usage(); rulenum = value; error = bsde_delete_rule(rulenum, BUFSIZ, errstr); if (error) warnx("%s", errstr); } int main(int argc, char *argv[]) { if (argc < 2) usage(); if (strcmp("add", argv[1]) == 0) { add_rule(argc-2, argv+2); } else if (strcmp("list", argv[1]) == 0) { if (argc != 2) usage(); list_rules(); } else if (strcmp("set", argv[1]) == 0) { set_rule(argc-2, argv+2); } else if (strcmp("remove", argv[1]) == 0) { remove_rule(argc-2, argv+2); } else usage(); return (0); }