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 #include #include +#include +#include #include @@ -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 (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; } } @@ -244,173 +507,438 @@ 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) -{ - 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) { + 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; + } + + *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; + } + + if (spec2 == NULL) { + *max = *min = gid1; + return (0); + } + + 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); + } + 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; - if (current >= argc) { - len = snprintf(errstr, buflen, "Identity short"); - return (-1); - } - - uid_seen = 0; - uid = 0; - gid_seen = 0; - gid = 0; - - /* First phrase: uid [uid] or gid [gid]. */ - if (strcmp("uid", argv[current]) == 0) { - if (current + 2 > argc) { - len = snprintf(errstr, buflen, "uid 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; - } - 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') { - len = snprintf(errstr, buflen, - "invalid gid: '%s'", - argv[current+1]); - return (-1); - } - gid = value; - } - gid_seen = 1; - current += 2; - } else { - len = snprintf(errstr, buflen, "'%s' not expected", - argv[current]); - 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"); - return (-1); - } + while (current < argc) { + if (strcmp(argv[current], "uid") == 0) { if (current + 2 > argc) { len = snprintf(errstr, buflen, "uid 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; - } - 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"); + 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); } - 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 & 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; } - gid_seen = 1; 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 jid: '%s'", argv[current+1]); + return (-1); + } + 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, + "double negative"); + return (-1); + } + nextnot = 1; + current += 1; } else { len = snprintf(errstr, buflen, "'%s' not expected", argv[current]); return (-1); + } + } + + 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); } } - if (current +1 < argc) { - len = snprintf(errstr, buflen, "'%s' not expected", - argv[current]); + 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); } - /* Fill out the identity. */ - identity->mbi_flags = 0; + *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, "gid short"); + return (-1); + } + 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; + } + current += 2; + } 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, "type short"); + return (-1); + } + 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; + } + 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); + } + } + + 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; } @@ -575,6 +1103,28 @@ bsde_get_mib(const char *string, int *name, size_t *namelen) 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) { @@ -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 #include #include +#include #include #include #include @@ -62,6 +63,7 @@ #include #include #include +#include #include #include @@ -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.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) @@ -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 < $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 +#include #include +#include #include @@ -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 #include +#include #include #include