aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malone <dwmalone@FreeBSD.org>2006-04-23 17:06:18 +0000
committerDavid Malone <dwmalone@FreeBSD.org>2006-04-23 17:06:18 +0000
commit89ddbd45e5e8e521f98c3e4a42fe969499f1a70c (patch)
tree27f43660d8de323a7adc792ac8d26a820c3b6015
parent19af5f6b1785009364113fc543433f41fa8f8219 (diff)
downloadsrc-89ddbd45e5e8e521f98c3e4a42fe969499f1a70c.tar.gz
src-89ddbd45e5e8e521f98c3e4a42fe969499f1a70c.zip
Add some new options to mac_bsdestended. We can now match on:
subject: ranges of uid, ranges of gid, jail id objects: ranges of uid, ranges of gid, filesystem, object is suid, object is sgid, object matches subject uid/gid object type We can also negate individual conditions. The ruleset language is a superset of the previous language, so old rules should continue to work. These changes require a change to the API between libugidfw and the mac_bsdextended module. Add a version number, so we can tell if we're running mismatched versions. Update man pages to reflect changes, add extra test cases to test_ugidfw.c and add a shell script that checks that the the module seems to do what we expect. Suggestions from: rwatson, trhodes Reviewed by: trhodes MFC after: 2 months
Notes
Notes: svn path=/head/; revision=157986
-rw-r--r--lib/libugidfw/libugidfw.310
-rw-r--r--lib/libugidfw/ugidfw.c896
-rw-r--r--lib/libugidfw/ugidfw.h3
-rw-r--r--sys/security/mac_bsdextended/mac_bsdextended.c183
-rw-r--r--sys/security/mac_bsdextended/mac_bsdextended.h62
-rw-r--r--tools/regression/mac/mac_bsdextended/test_matches.sh167
-rw-r--r--tools/regression/mac/mac_bsdextended/test_ugidfw.c58
-rw-r--r--usr.sbin/ugidfw/ugidfw.8239
-rw-r--r--usr.sbin/ugidfw/ugidfw.c1
9 files changed, 1352 insertions, 267 deletions
diff --git a/lib/libugidfw/libugidfw.3 b/lib/libugidfw/libugidfw.3
index 7e8c75193a6f..3ff407c1609b 100644
--- a/lib/libugidfw/libugidfw.3
+++ b/lib/libugidfw/libugidfw.3
@@ -59,14 +59,6 @@ Converts the internal representation of a 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);
@@ -108,8 +100,6 @@ rule number; see
.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 ,
diff --git a/lib/libugidfw/ugidfw.c b/lib/libugidfw/ugidfw.c
index 1f46e50e2e48..341ac25f4a6b 100644
--- a/lib/libugidfw/ugidfw.c
+++ b/lib/libugidfw/ugidfw.c
@@ -34,6 +34,8 @@
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/sysctl.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
#include <security/mac_bsdextended/mac_bsdextended.h>
@@ -47,8 +49,7 @@
/*
* 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.
*/
@@ -60,114 +61,376 @@ 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 (!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.mbi_flags & MBI_UID_DEFINED) {
- pwd = getpwuid(rule->mbr_subject.mbi_uid);
+ 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 (!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.mbi_flags & MBI_GID_DEFINED) {
- grp = getgrgid(rule->mbr_subject.mbi_gid);
+ 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 (!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.mbi_flags & MBI_UID_DEFINED) {
- pwd = getpwuid(rule->mbr_object.mbi_uid);
+ 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;
}
}
@@ -244,173 +507,438 @@ truncated:
}
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);
+ }
+ 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;
}
- uid_seen = 1;
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);
+ }
+ 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;
}
- gid_seen = 1;
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);
+ }
}
- /* Fill out the identity. */
- identity->mbi_flags = 0;
-
+ object->mbo_flags = flags;
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;
+ 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;
return (0);
}
@@ -516,12 +1044,12 @@ bsde_parse_rule(int argc, char *argv[], struct mac_bsdextended_rule *rule,
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);
@@ -538,7 +1066,7 @@ 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);
@@ -549,7 +1077,7 @@ bsde_parse_rule_string(const char *string, struct mac_bsdextended_rule *rule,
for (ap = argv; (*ap = strsep(&stringp, " \t")) != NULL;) {
argc++;
if (**ap != '\0')
- if (++ap >= &argv[20])
+ if (++ap >= &argv[100])
break;
}
@@ -576,6 +1104,28 @@ bsde_get_mib(const char *string, int *name, size_t *namelen)
}
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;
@@ -632,6 +1182,9 @@ bsde_get_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t errlen,
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) {
@@ -667,6 +1220,9 @@ bsde_delete_rule(int rulenum, size_t buflen, char *errstr)
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) {
@@ -697,6 +1253,9 @@ bsde_set_rule(int rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
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) {
@@ -728,6 +1287,9 @@ bsde_add_rule(int *rulenum, struct mac_bsdextended_rule *rule, size_t buflen,
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) {
diff --git a/lib/libugidfw/ugidfw.h b/lib/libugidfw/ugidfw.h
index 7637ca5a5346..5b7fcf25a9f9 100644
--- a/lib/libugidfw/ugidfw.h
+++ b/lib/libugidfw/ugidfw.h
@@ -37,9 +37,6 @@
__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[],
diff --git a/sys/security/mac_bsdextended/mac_bsdextended.c b/sys/security/mac_bsdextended/mac_bsdextended.c
index 5dd69b148869..7dec0d16d3c9 100644
--- a/sys/security/mac_bsdextended/mac_bsdextended.c
+++ b/sys/security/mac_bsdextended/mac_bsdextended.c
@@ -47,6 +47,7 @@
#include <sys/acl.h>
#include <sys/conf.h>
#include <sys/kernel.h>
+#include <sys/jail.h>
#include <sys/lock.h>
#include <sys/mac.h>
#include <sys/malloc.h>
@@ -62,6 +63,7 @@
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
+#include <sys/ucred.h>
#include <net/bpfdesc.h>
#include <net/if.h>
@@ -92,11 +94,14 @@ MALLOC_DEFINE(M_MACBSDEXTENDED, "mac_bsdextended", "BSD Extended MAC rule");
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
@@ -121,10 +126,20 @@ 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)
@@ -240,32 +255,59 @@ mac_bsdextended_destroy(struct mac_policy_conf *mpc)
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.mbs_neg & MBS_UID_DEFINED)
+ match = !match;
- if (rule->mbr_subject.mbi_flags & MBI_NEGATED)
+ if (!match)
+ return (0);
+ }
+
+ 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 (!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.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_PRISON_DEFINED) {
+ match = (cred->cr_prison != NULL &&
+ cred->cr_prison->pr_id == rule->mbr_subject.mbs_prison);
- if (rule->mbr_subject.mbi_flags & MBI_NEGATED)
+ if (rule->mbr_subject.mbs_neg & MBS_PRISON_DEFINED)
match = !match;
if (!match)
@@ -275,26 +317,118 @@ mac_bsdextended_rulecheck(struct mac_bsdextended_rule *rule,
/*
* 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?
*/
@@ -302,7 +436,7 @@ mac_bsdextended_rulecheck(struct mac_bsdextended_rule *rule,
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 */
}
@@ -317,7 +451,7 @@ mac_bsdextended_rulecheck(struct mac_bsdextended_rule *rule,
}
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;
@@ -339,8 +473,8 @@ mac_bsdextended_check(struct ucred *cred, uid_t object_uid, gid_t object_gid,
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) {
@@ -365,8 +499,7 @@ mac_bsdextended_check_vp(struct ucred *cred, struct vnode *vp, int acc_mode)
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
diff --git a/sys/security/mac_bsdextended/mac_bsdextended.h b/sys/security/mac_bsdextended/mac_bsdextended.h
index 4ddc21e15b29..af3ae8f78fb5 100644
--- a/sys/security/mac_bsdextended/mac_bsdextended.h
+++ b/sys/security/mac_bsdextended/mac_bsdextended.h
@@ -37,10 +37,7 @@
#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
@@ -57,15 +54,60 @@
#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 */
};
diff --git a/tools/regression/mac/mac_bsdextended/test_matches.sh b/tools/regression/mac/mac_bsdextended/test_matches.sh
new file mode 100644
index 000000000000..99d6b621cfb4
--- /dev/null
+++ b/tools/regression/mac/mac_bsdextended/test_matches.sh
@@ -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 <<EOF> $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"
+
diff --git a/tools/regression/mac/mac_bsdextended/test_ugidfw.c b/tools/regression/mac/mac_bsdextended/test_ugidfw.c
index 398b9a3a6761..63e25f0beae5 100644
--- a/tools/regression/mac/mac_bsdextended/test_ugidfw.c
+++ b/tools/regression/mac/mac_bsdextended/test_ugidfw.c
@@ -26,8 +26,9 @@
* $FreeBSD$
*/
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/mac.h>
+#include <sys/mount.h>
#include <security/mac_bsdextended/mac_bsdextended.h>
@@ -104,6 +105,47 @@ static const char *test_strings[] = {
"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 *);
@@ -111,8 +153,8 @@ 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++) {
@@ -129,11 +171,11 @@ test_libugidfw_strings(void)
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);
@@ -147,7 +189,7 @@ test_libugidfw_strings(void)
int
main(int argc, char *argv[])
{
- char errorstr[128];
+ char errorstr[256];
int count, slots;
if (argc != 1)
@@ -182,13 +224,13 @@ main(int argc, char *argv[])
* 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);
diff --git a/usr.sbin/ugidfw/ugidfw.8 b/usr.sbin/ugidfw/ugidfw.8
index eedd172784ca..cdd4293eb79b 100644
--- a/usr.sbin/ugidfw/ugidfw.8
+++ b/usr.sbin/ugidfw/ugidfw.8
@@ -41,12 +41,52 @@
.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
@@ -56,12 +96,52 @@
.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
@@ -80,20 +160,12 @@ 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
@@ -108,13 +180,9 @@ 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
@@ -131,37 +199,120 @@ 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 ,
diff --git a/usr.sbin/ugidfw/ugidfw.c b/usr.sbin/ugidfw/ugidfw.c
index 00bc15334502..24e022870d52 100644
--- a/usr.sbin/ugidfw/ugidfw.c
+++ b/usr.sbin/ugidfw/ugidfw.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/errno.h>
+#include <sys/mount.h>
#include <sys/time.h>
#include <sys/sysctl.h>