From sds at tycho.nsa.gov Mon Nov 3 11:23:05 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Mon, 03 Nov 2008 14:23:05 -0500 Subject: [fmac-discuss] Rebasing fmac-gate to onnv_101 Message-ID: <1225740185.3609.61.camel@moss-spartans.epoch.ncsc.mil> I'll be pushing the onnv_101 rebase to the fmac gate later today, along with the tmpfs change to restore tmpfs security labeling functionality. To build it, you will need to update to the corresponding build of the ON build tools and closed bins from: http://dlc.sun.com/osol/on/downloads/b101/ and the Sun Studio 12 compiler toolchain from: http://opensolaris.org/os/community/tools/sun_studio_tools/sun_studio_12_tools -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Tue Nov 4 11:09:00 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 04 Nov 2008 14:09:00 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks Message-ID: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> Updated version of the patch to add FMAC controls over the file DAC privilege checks. Changes since the prior version of the patch: 1) On exec, disable secid transitions if the filesystem is mounted nosuid. This ensures that executables on such filesystems cannot be used to escalate privileges via FMAC. 2) On exec, set the suid flags on the process if the execsetid permission is not granted between the old and new security contexts (same permission check that already determines whether to set the linker security flag). This ensures that further safeguards associated with setuid programs are also applied on context transitions where the old context is less trusted than the new context; we were already setting the linker security flag but hadn't been previously setting these process flags. 3) On file access, return any legacy policy denial if the requested privileges exceed the limit set for the process. This ensures that FMAC never escalates privileges beyond the limit set, preserving the meaning of the limit set as a hard upper bound for privilege escalation. Example of the impact of the latter change: $ id uid=123(sds) gid=1(other) $ pcon 101056: sysadm_u:sysadm_r:sysadm_t:unclassified $ sum /etc/shadow 34990 1 /etc/shadow (able to read /etc/shadow because FMAC is granting read and priv_read permissions between sysadm_t and shadow_t) $ ppriv -s L-file_dac_read $$ $ sum /etc/shadow sum: /etc/shadow Permission denied (no longer able to read /etc/shadow because file_dac_read was removed from the limit set and FMAC honors that restriction) The patch is otherwise the same as the original, so much of the description included in the original posting is still applicable: http://mail.opensolaris.org/pipermail/fmac-discuss/2008-October/000389.html With regard to compatibility, it should be noted that the only way to gain the file DAC privileges under this scheme is to transition to a security context that is allowed the desired privileges in the FMAC policy. setuid root programs will not automatically gain any privileges; they must be labeled with an entrypoint type that causes a transition into a domain with the right set of privileges, although a generic allprivs_exec_t type and allprivs_t domain could be defined as a fallback for use by admins for custom or third party setuid root programs. This also has implications for setuid root programs on filesystems that do not support security labeling; some workaround may be needed for that case. Webrev available at: http://cr.opensolaris.org/~sds/filedacpriv2/ diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -40,6 +40,9 @@ write read append + priv_execute + priv_write + priv_read open create link @@ -130,6 +133,7 @@ remove_name reparent search + priv_search rmdir mounton mountassociate diff --git a/usr/src/uts/common/fmac/avc.c b/usr/src/uts/common/fmac/avc.c --- a/usr/src/uts/common/fmac/avc.c +++ b/usr/src/uts/common/fmac/avc.c @@ -920,9 +920,10 @@ return (0); } -int -avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, - access_vector_t requested, avc_audit_data_t *auditdata) +static int +avc_has_perm_common(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata, boolean_t strict) { struct av_decision avd; access_vector_t denied; @@ -940,7 +941,7 @@ if (denied & avd.auditdeny) avc_audit(ssid, tsid, tclass, denied, AVC_AUDITDENY, auditdata); - if (fmac_enforcing) { + if (strict || fmac_enforcing) { return (EACCES); } else { (void) avc_update_cache(AVC_CALLBACK_GRANT, ssid, tsid, @@ -955,3 +956,20 @@ return (0); } + +int +avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, + access_vector_t requested, avc_audit_data_t *auditdata) +{ + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, + B_FALSE); +} + +int +avc_has_perm_strict(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata) +{ + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, + B_TRUE); +} diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -39,6 +39,7 @@ #include #include #include +#include #include /* Tunables */ @@ -240,7 +241,7 @@ if (!fmac_enabled) return (EINVAL); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; sclass = fmac_vtype_to_sclass(vtype); if (!sclass) @@ -339,7 +340,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; error = security_transition_sid(cr_secid, dvp->v_secid, sclass, &secid); @@ -423,7 +424,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = tdvp; @@ -454,7 +455,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = dvp; @@ -489,7 +490,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); @@ -545,7 +546,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = vp; @@ -564,14 +565,17 @@ if (!fmac_enabled) return (0); - prev_secid = crgetsecid(cr); - secid = crgetexecsecid(cr); + prev_secid = cr->cr_secid; + secid = cr->cr_exec_secid; if (!secid) { error = security_transition_sid(prev_secid, vp->v_secid, SECCLASS_PROCESS, &secid); if (error) return (error); } + + if (vp->v_vfsp->vfs_flag & VFS_NOSETUID) + secid = prev_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = vp; @@ -633,7 +637,7 @@ if (!fmac_enabled) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; sclass = fmac_vtype_to_sclass(vp->v_type); if (!sclass) @@ -697,7 +701,7 @@ if (!fmac_enabled) return (0); - return (avc_has_perm(crgetsecid((cred_t *)scr), crgetsecid(tcr), + return (avc_has_perm(scr->cr_secid, tcr->cr_secid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL)); } @@ -724,7 +728,65 @@ if (!fmac_enabled) return (0); - tsecid = crgetsecid((cred_t *)tcrp); - ssecid = crgetsecid((cred_t *)scrp); + tsecid = tcrp->cr_secid; + ssecid = scrp->cr_secid; return (avc_has_perm(ssecid, tsecid, SECCLASS_PROCESS, perms, NULL)); } + +int +fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) +{ + security_id_t cr_secid; + security_class_t sclass; + access_vector_t av; + avc_audit_data_t ad; + priv_set_t privs; + + /* If not enabled, just return the legacy policy decision. */ + if (!fmac_enabled) + return (err); + + /* + * If privilege aware, then honor any legacy policy denial + * since the program may have reduced its own privileges. + */ + if (err && (CR_FLAGS(cr) & PRIV_AWARE)) + return (err); + + cr_secid = cr->cr_secid; + + sclass = fmac_vtype_to_sclass(vp->v_type); + if (!sclass) + return (err); + + av = 0; + priv_emptyset(&privs); + if (mode & VREAD) { + av |= FILE__PRIV_READ; + priv_addset(&privs, PRIV_FILE_DAC_READ); + } + if (mode & VWRITE) { + av |= FILE__PRIV_WRITE; + priv_addset(&privs, PRIV_FILE_DAC_WRITE); + } + + if (mode & VEXEC) { + if (sclass == SECCLASS_DIR) { + av |= DIR__PRIV_SEARCH; + priv_addset(&privs, PRIV_FILE_DAC_SEARCH); + } else { + av |= FILE__PRIV_EXECUTE; + priv_addset(&privs, PRIV_FILE_DAC_EXECUTE); + } + } + + if (!av) + return (err); + + if (err && !priv_issubset(&privs, &CR_LPRIV(cr))) + return (err); + + AVC_AUDIT_DATA_INIT(&ad, FS); + ad.u.fs.vp = vp; + return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); +} diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -725,7 +725,7 @@ if (cred->cr_ruid != cred->cr_uid || (cred->cr_rgid != cred->cr_gid && !supgroupmember(cred->cr_gid, cred)) || - (privflags & PRIV_INCREASE) != 0) + (privflags & PRIV_INCREASE) != 0 || fmac_execsetid) suidflags = PSUIDFLAGS; else suidflags = 0; diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -834,13 +834,15 @@ int secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode) { + int err = 0; + if ((mode & VREAD) && priv_policy_va(cr, PRIV_FILE_DAC_READ, B_FALSE, EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE) != 0) { - return (EACCES); + err = EACCES; } - if (mode & VWRITE) { + if (!err && (mode & VWRITE)) { boolean_t allzone; if (owner == 0 && cr->cr_uid != 0) @@ -850,21 +852,22 @@ if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE) != 0) { - return (EACCES); + err = EACCES; } } - if (mode & VEXEC) { + if (!err && (mode & VEXEC)) { /* * Directories use file_dac_search to override the execute bit. */ int p = vp->v_type == VDIR ? PRIV_FILE_DAC_SEARCH : PRIV_FILE_DAC_EXECUTE; - return (priv_policy_va(cr, p, B_FALSE, EACCES, NULL, - KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE)); + err = priv_policy_va(cr, p, B_FALSE, EACCES, NULL, + KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE); } - return (0); + + return (fmac_vnode_priv_access(cr, vp, mode, err)); } /* diff --git a/usr/src/uts/common/sys/fmac/avc.h b/usr/src/uts/common/sys/fmac/avc.h --- a/usr/src/uts/common/sys/fmac/avc.h +++ b/usr/src/uts/common/sys/fmac/avc.h @@ -88,6 +88,11 @@ security_class_t tclass, access_vector_t requested, avc_audit_data_t *auditdata); +/* Check requested permissions without considering permissive mode/domains. */ +extern int avc_has_perm_strict(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata); + #define AVC_CALLBACK_GRANT 1 #define AVC_CALLBACK_TRY_REVOKE 2 #define AVC_CALLBACK_REVOKE 4 diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -105,6 +105,7 @@ access_vector_t fmac_sigtoav(int sig); int fmac_hasprocperm(const cred_t *tcrp, const cred_t *scrp, access_vector_t perms); +int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); #endif /* _KERNEL */ #ifdef __cplusplus -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Wed Nov 5 06:02:39 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 05 Nov 2008 09:02:39 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> On Tue, 2008-11-04 at 14:09 -0500, Stephen Smalley wrote: > Updated version of the patch to add FMAC controls over the file DAC > privilege checks. > > Changes since the prior version of the patch: > 1) On exec, disable secid transitions if the filesystem is mounted > nosuid. This ensures that executables on such filesystems cannot be > used to escalate privileges via FMAC. > > 2) On exec, set the suid flags on the process if the execsetid > permission is not granted between the old and new security contexts > (same permission check that already determines whether to set the linker > security flag). This ensures that further safeguards associated with > setuid programs are also applied on context transitions where the old > context is less trusted than the new context; we were already setting > the linker security flag but hadn't been previously setting these > process flags. > > 3) On file access, return any legacy policy denial if the requested > privileges exceed the limit set for the process. This ensures that FMAC > never escalates privileges beyond the limit set, preserving the meaning > of the limit set as a hard upper bound for privilege escalation. > > Example of the impact of the latter change: > $ id > uid=123(sds) gid=1(other) > $ pcon > 101056: sysadm_u:sysadm_r:sysadm_t:unclassified > $ sum /etc/shadow > 34990 1 /etc/shadow > (able to read /etc/shadow because FMAC is granting read and priv_read > permissions between sysadm_t and shadow_t) > $ ppriv -s L-file_dac_read $$ > $ sum /etc/shadow > sum: /etc/shadow Permission denied > (no longer able to read /etc/shadow because file_dac_read was removed > from the limit set and FMAC honors that restriction) This example suggests that we need to be careful about processes reducing their own limit set and then invoking some privileged program in an attempt to induce failure in a manner that leads to a vulnerability. The exec code applies a check to make sure that the limit set includes a minimal set of "unsafe" privileges (presently those for audit, setid, and resource allocation) when executing a setuid root program, so I guess we need some similar guarantee on at least certain security context transitions (likely the same ones where we want to enable linker security flags and set the suid flags on the process, so it would be triggered by lack of execsetid permission as well). I also realized that checking the limit set at file access time like this violates the documented behavior for the limit set (as per privileges man page and Solaris Internals), which isn't supposed to take effect until the next exec (so a process can reduce its limit set but still exercise the removed privileges until it exec's a new program). This is seemingly an issue also for klpd, as it also applies a limit set test at access time, although it perhaps can get away with that by virtue of only applying its tests when a klpd is registered for the process. One way to address this compatibility issue would be to add an additional privilege set (the "set-exec" limit set), change setppriv to modify that "set-exec" limit set rather than the current/active limit set, and replace the current/active limit set with the set-exec limit set on exec. Then both klpd and FMAC could test against the current/active limit set and be unaffected by setppriv calls until the next exec. I don't know how many applications rely on this behavior currently. On a different topic than the limit set, this patch only addresses secpolicy_vnode_access(), so we need to consider how this generalizes for other privilege checks. One issue is that there are direct calls to the priv_policy* functions (or PRIV_POLICY* macros) from other parts of the kernel, so inserting the FMAC hooks only at the secpolicy function layer will not capture such direct calls. But invoking FMAC from the secpolicy function layer is attractive since we know more about the context of the operation at that layer and have the arguments readily available w/o having to decode a varargs-style list, and since we can invoke a single FMAC hook (like fmac_vnode_priv_access as in this patch) rather than making multiple such calls (e.g. secpolicy_vnode_access makes 3 separate calls to priv_policy_va). Possibly the remaining direct calls to PRIV_POLICY* should all be converted to using new secpolicy functions and then we can just insert FMAC hooks into the secpolicy layer. We also need to ensure that all relevant arguments are passed to the secpolicy functions; I notice for example that most of the other secpolicy_vnode functions do not presently pass the vnode, making it impossible presently to apply object-based checks. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Wed Nov 5 11:30:17 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Wed, 05 Nov 2008 11:30:17 -0800 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4911F449.6060003@sun.com> Stephen Smalley wrote: > On Tue, 2008-11-04 at 14:09 -0500, Stephen Smalley wrote: > >> Updated version of the patch to add FMAC controls over the file DAC >> privilege checks. >> >> Changes since the prior version of the patch: >> 1) On exec, disable secid transitions if the filesystem is mounted >> nosuid. This ensures that executables on such filesystems cannot be >> used to escalate privileges via FMAC. >> >> 2) On exec, set the suid flags on the process if the execsetid >> permission is not granted between the old and new security contexts >> (same permission check that already determines whether to set the linker >> security flag). This ensures that further safeguards associated with >> setuid programs are also applied on context transitions where the old >> context is less trusted than the new context; we were already setting >> the linker security flag but hadn't been previously setting these >> process flags. >> >> 3) On file access, return any legacy policy denial if the requested >> privileges exceed the limit set for the process. This ensures that FMAC >> never escalates privileges beyond the limit set, preserving the meaning >> of the limit set as a hard upper bound for privilege escalation. >> >> Example of the impact of the latter change: >> $ id >> uid=123(sds) gid=1(other) >> $ pcon >> 101056: sysadm_u:sysadm_r:sysadm_t:unclassified >> $ sum /etc/shadow >> 34990 1 /etc/shadow >> (able to read /etc/shadow because FMAC is granting read and priv_read >> permissions between sysadm_t and shadow_t) >> $ ppriv -s L-file_dac_read $$ >> $ sum /etc/shadow >> sum: /etc/shadow Permission denied >> (no longer able to read /etc/shadow because file_dac_read was removed >> from the limit set and FMAC honors that restriction) >> > > This example suggests that we need to be careful about processes > reducing their own limit set and then invoking some privileged program > in an attempt to induce failure in a manner that leads to a > vulnerability. The exec code applies a check to make sure that the > limit set includes a minimal set of "unsafe" privileges (presently those > for audit, setid, and resource allocation) when executing a setuid root > program, so I guess we need some similar guarantee on at least certain > security context transitions (likely the same ones where we want to > enable linker security flags and set the suid flags on the process, so > it would be triggered by lack of execsetid permission as well). > > I also realized that checking the limit set at file access time like > this violates the documented behavior for the limit set (as per > privileges man page and Solaris Internals), which isn't supposed to take > effect until the next exec (so a process can reduce its limit set but > still exercise the removed privileges until it exec's a new program). > This is seemingly an issue also for klpd, as it also applies a limit set > test at access time, although it perhaps can get away with that by > virtue of only applying its tests when a klpd is registered for the > process. One way to address this compatibility issue would be to add an > additional privilege set (the "set-exec" limit set), change setppriv to > modify that "set-exec" limit set rather than the current/active limit > set, and replace the current/active limit set with the set-exec limit > set on exec. Then both klpd and FMAC could test against the > current/active limit set and be unaffected by setppriv calls until the > next exec. I don't know how many applications rely on this behavior > currently. > The issue of compatibility of the Solaris privilege model with the proposed changes to make FMAC authoritative is obviously complex. I don't think the issue of limit set checking at access time is going to be a problem. Because of other problems with the existing implementation, reducing the limit set is only done in a few places, like zone_enter, and pam_setcred. One problem with the existing implementation is that a root process with reduced privileges can't gain privilege by executing a setuid-to-root binary. This makes it impractical to try to remove privileges from the root role. Another issue is the policy for modifying root-owned objects. For example, the current requirement that all privileges are required to write any root-owned file, even /etc/mod, is not ideal. The proposed changes for making FMAC authoritative seem to ignore this overly restrictive policy because the returned value, err, in the code below: 852 if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, 853 NULL, KLPDARG_VNODE, vp, (char *)NULL, 854 KLPDARG_NOMORE) != 0) { 855 err = EACCES; 856 } which is passed into fmac_vnode_priv_access() is eventually ignored if FMAC is enforcing unless the current process is using privilege bracketing (PRIV_AWARE). Then there is the issue of privilege inheritance on exec. Although a child may inherit privileges from its parent, they won't be useful unless they corresponding FMAC permissions are granted to the type associated with the child process. This isn't really compatible, either. --Glenn From sds at tycho.nsa.gov Wed Nov 5 12:06:38 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 05 Nov 2008 15:06:38 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <4911F449.6060003@sun.com> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> Message-ID: <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> On Wed, 2008-11-05 at 11:30 -0800, Glenn Faden wrote: > Stephen Smalley wrote: > > On Tue, 2008-11-04 at 14:09 -0500, Stephen Smalley wrote: > > > >> Updated version of the patch to add FMAC controls over the file DAC > >> privilege checks. > >> > >> Changes since the prior version of the patch: > >> 1) On exec, disable secid transitions if the filesystem is mounted > >> nosuid. This ensures that executables on such filesystems cannot be > >> used to escalate privileges via FMAC. > >> > >> 2) On exec, set the suid flags on the process if the execsetid > >> permission is not granted between the old and new security contexts > >> (same permission check that already determines whether to set the linker > >> security flag). This ensures that further safeguards associated with > >> setuid programs are also applied on context transitions where the old > >> context is less trusted than the new context; we were already setting > >> the linker security flag but hadn't been previously setting these > >> process flags. > >> > >> 3) On file access, return any legacy policy denial if the requested > >> privileges exceed the limit set for the process. This ensures that FMAC > >> never escalates privileges beyond the limit set, preserving the meaning > >> of the limit set as a hard upper bound for privilege escalation. > >> > >> Example of the impact of the latter change: > >> $ id > >> uid=123(sds) gid=1(other) > >> $ pcon > >> 101056: sysadm_u:sysadm_r:sysadm_t:unclassified > >> $ sum /etc/shadow > >> 34990 1 /etc/shadow > >> (able to read /etc/shadow because FMAC is granting read and priv_read > >> permissions between sysadm_t and shadow_t) > >> $ ppriv -s L-file_dac_read $$ > >> $ sum /etc/shadow > >> sum: /etc/shadow Permission denied > >> (no longer able to read /etc/shadow because file_dac_read was removed > >> from the limit set and FMAC honors that restriction) > >> > > > > This example suggests that we need to be careful about processes > > reducing their own limit set and then invoking some privileged program > > in an attempt to induce failure in a manner that leads to a > > vulnerability. The exec code applies a check to make sure that the > > limit set includes a minimal set of "unsafe" privileges (presently those > > for audit, setid, and resource allocation) when executing a setuid root > > program, so I guess we need some similar guarantee on at least certain > > security context transitions (likely the same ones where we want to > > enable linker security flags and set the suid flags on the process, so > > it would be triggered by lack of execsetid permission as well). > > > > I also realized that checking the limit set at file access time like > > this violates the documented behavior for the limit set (as per > > privileges man page and Solaris Internals), which isn't supposed to take > > effect until the next exec (so a process can reduce its limit set but > > still exercise the removed privileges until it exec's a new program). > > This is seemingly an issue also for klpd, as it also applies a limit set > > test at access time, although it perhaps can get away with that by > > virtue of only applying its tests when a klpd is registered for the > > process. One way to address this compatibility issue would be to add an > > additional privilege set (the "set-exec" limit set), change setppriv to > > modify that "set-exec" limit set rather than the current/active limit > > set, and replace the current/active limit set with the set-exec limit > > set on exec. Then both klpd and FMAC could test against the > > current/active limit set and be unaffected by setppriv calls until the > > next exec. I don't know how many applications rely on this behavior > > currently. > > > > The issue of compatibility of the Solaris privilege model with the > proposed changes to make FMAC authoritative is obviously complex. I > don't think the issue of limit set checking at access time is going to > be a problem. Because of other problems with the existing > implementation, reducing the limit set is only done in a few places, > like zone_enter, and pam_setcred. One problem with the existing > implementation is that a root process with reduced privileges can't gain > privilege by executing a setuid-to-root binary. This makes it > impractical to try to remove privileges from the root role. Ok, so possibly we don't need to worry about the change in when the limit set gets enforced. > Another issue is the policy for modifying root-owned objects. For > example, the current requirement that all privileges are required to > write any root-owned file, even /etc/mod, is not ideal. The proposed > changes for making FMAC authoritative seem to ignore this overly > restrictive policy because the returned value, err, in the code below: > > 852 if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, > 853 NULL, KLPDARG_VNODE, vp, (char *)NULL, > 854 KLPDARG_NOMORE) != 0) { > 855 err = EACCES; > 856 } > > which is passed into fmac_vnode_priv_access() is eventually ignored if > FMAC is enforcing unless the current process is using privilege > bracketing (PRIV_AWARE). Yes, I think this is what we want though; writing to a root-owned object does not mean that elevation to full privileges is possible when using FMAC and thus we don't need to require that all privileges be allowed. The discussion of privileges in the Solaris Internals book actually mentions that MAC labeling would help with this issue. > Then there is the issue of privilege inheritance on exec. Although a > child may inherit privileges from its parent, they won't be useful > unless they corresponding FMAC permissions are granted to the type > associated with the child process. This isn't really compatible, either. That isn't really any different than any other FMAC permission denial though; we could just as easily deny the base access check rather than the privilege check and cause unexpected failure. However, compatibility in that sense is always possible via suitable policy configuration. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Wed Nov 5 17:17:32 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Wed, 05 Nov 2008 17:17:32 -0800 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <491245AC.3010805@sun.com> Stephen Smalley wrote: > On Wed, 2008-11-05 at 11:30 -0800, Glenn Faden wrote: > >> Another issue is the policy for modifying root-owned objects. For >> example, the current requirement that all privileges are required to >> write any root-owned file, even /etc/mod, is not ideal. The proposed >> changes for making FMAC authoritative seem to ignore this overly >> restrictive policy because the returned value, err, in the code below: >> >> 852 if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, >> 853 NULL, KLPDARG_VNODE, vp, (char *)NULL, >> 854 KLPDARG_NOMORE) != 0) { >> 855 err = EACCES; >> 856 } >> >> which is passed into fmac_vnode_priv_access() is eventually ignored if >> FMAC is enforcing unless the current process is using privilege >> bracketing (PRIV_AWARE). >> > > Yes, I think this is what we want though; writing to a root-owned object > does not mean that elevation to full privileges is possible when using > FMAC and thus we don't need to require that all privileges be allowed. > The discussion of privileges in the Solaris Internals book actually > mentions that MAC labeling would help with this issue. > > Agreed. In Trusted Solaris 8, we protected most root-owned objects with either the ADMIN_LOW or ADMIN HIGH Sensitivity labels. So the MAC policy was used to prevent privilege escalation. Since Solaris 10 has no per-file sensitivity labels, the requirement for all privileges was the expedient solution. We don't really want to preserve that behavior. >> Then there is the issue of privilege inheritance on exec. Although a >> child may inherit privileges from its parent, they won't be useful >> unless they corresponding FMAC permissions are granted to the type >> associated with the child process. This isn't really compatible, either. >> > > That isn't really any different than any other FMAC permission denial > though; we could just as easily deny the base access check rather than > the privilege check and cause unexpected failure. However, > compatibility in that sense is always possible via suitable policy > configuration. > > My concern isn't really about a the enforcement policy, but rather the confusion it will cause. For example, how should a command like ppriv(1) report that a process has an effective privilege set including file_dac_read, if the type for that process lacks the file_read permission? Unless a process is privilege aware, its current privilege sets won't affect the access control policy when FMAC is enabled. Please explain how to preserve backward compatibility with an appropriate policy. Wouldn't the policy expressed for the process's type have to include all the possible permissions corresponding to the potentially effective privileges? The output of truss, which reports which privileges are lacking won't be meaningful anymore. Would programs like pfexec still be supported when FMAC is enabled? --Glenn From sds at tycho.nsa.gov Thu Nov 6 06:42:48 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Thu, 06 Nov 2008 09:42:48 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <491245AC.3010805@sun.com> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> Message-ID: <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> On Wed, 2008-11-05 at 17:17 -0800, Glenn Faden wrote: > My concern isn't really about a the enforcement policy, but rather the > confusion it will cause. For example, how should a command like ppriv(1) > report that a process has an effective privilege set including > file_dac_read, if the type for that process lacks the file_read > permission? Unless a process is privilege aware, its current privilege > sets won't affect the access control policy when FMAC is enabled. Please > explain how to preserve backward compatibility with an appropriate > policy. Wouldn't the policy expressed for the process's type have to > include all the possible permissions corresponding to the potentially > effective privileges? I misunderstood your concern. With regard to ppriv, we are constrained in the extent to which its output is meaningful based on the design decisions thus far, namely that FMAC will be authoritative with respect to privileges and that privileges will be granted based on both the subject and the object. If we would prefer a more conservative approach, then making FMAC restrictive-only with respect to privileges and granting privileges based on subject-only would produce a system where ppriv output would remain at least somewhat meaningful (lack of privilege in the privilege sets would still be honored, but a privilege present in the sets could still be denied by FMAC). I think FGAP would have had the same issue - running ppriv on a process governed by a klpd would not have told you what actual privileges would be allowed nor under what conditions (particular object). In Linux, we did not mutate the capability sets of a process nor did we adjust the output of interfaces that displayed them, so they only show what capabilities might be allowed to the process subject to further restriction by SELinux. But SELinux does not ever grant a capability that would be denied, and we left capabilities as subject-only, although we can use the normal access checks to limit what objects can be accessed and thus indirectly limit the objects to which the capabilities can be applied. > The output of truss, which reports which privileges are lacking won't be > meaningful anymore. I suspect we could alter the avc_audit() function to report avc denials using the same mechanism as is presently used for privilege debugging (or at least offering that as an additional channel for processes marked for privilege debugging), and thus with an adjustment to truss and ppriv -D, we could get the cause of any denial reported in the same manner. > Would programs like pfexec still be supported when FMAC is enabled? I think it would get replaced by a trivial wrapper under FMAC, similar to FGAP. This discussion makes me wonder if we should reconsider making FMAC authoritative with respect to privileges, or at least make that more selective, e.g. we could identify specific domains in policy to be treated as authoritative while defaulting to restrictive-only for most domains. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Thu Nov 6 11:36:06 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Thu, 06 Nov 2008 11:36:06 -0800 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <49134726.7040109@sun.com> Stephen Smalley wrote: > On Wed, 2008-11-05 at 17:17 -0800, Glenn Faden wrote: > > This discussion makes me wonder if we should reconsider making FMAC > authoritative with respect to privileges, or at least make that more > selective, e.g. we could identify specific domains in policy to be > treated as authoritative while defaulting to restrictive-only for most > domains. > I have an alternative proposal which preserves the existing privilege sets, but allows FMAC to be authoritative in the case of lack of privilege, similar to the FGAP proposal. Instead of unconditionally calling fmac_vnode_priv_access(), only invoke it when the subject is lacking the required privilege. For example, the following in secpolicy_vnode_access(): 835 secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode) 836 { 837 int err = 0; 838 839 if ((mode & VREAD) && priv_policy_va(cr, PRIV_FILE_DAC_READ, B_FALSE, 840 EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, 841 KLPDARG_NOMORE) != 0) { 842 err = EACCES; 843 } 844 845 if (!err && (mode & VWRITE)) { 846 boolean_t allzone; 847 848 if (owner == 0 && cr->cr_uid != 0) 849 allzone = B_TRUE; 850 else 851 allzone = B_FALSE; 852 if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, 853 NULL, KLPDARG_VNODE, vp, (char *)NULL, 854 KLPDARG_NOMORE) != 0) { 855 err = EACCES; 856 } 857 } 858 859 if (!err && (mode & VEXEC)) { 860 /* 861 * Directories use file_dac_search to override the execute bit. 862 */ 863 int p = vp->v_type == VDIR ? PRIV_FILE_DAC_SEARCH : 864 PRIV_FILE_DAC_EXECUTE; 865 866 err = priv_policy_va(cr, p, B_FALSE, EACCES, NULL, 867 KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE); 868 } 869 if (err) return(fmac_vnode_priv_access(cr, vp, mode, err)); else 870 return 0 871 } 872 From sds at tycho.nsa.gov Thu Nov 6 12:07:50 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Thu, 06 Nov 2008 15:07:50 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <49134726.7040109@sun.com> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> <49134726.7040109@sun.com> Message-ID: <1226002070.814.154.camel@moss-spartans.epoch.ncsc.mil> On Thu, 2008-11-06 at 11:36 -0800, Glenn Faden wrote: > Stephen Smalley wrote: > > On Wed, 2008-11-05 at 17:17 -0800, Glenn Faden wrote: > > > > This discussion makes me wonder if we should reconsider making FMAC > > authoritative with respect to privileges, or at least make that more > > selective, e.g. we could identify specific domains in policy to be > > treated as authoritative while defaulting to restrictive-only for most > > domains. > > > I have an alternative proposal which preserves the existing privilege > sets, but allows FMAC to be authoritative in the case of lack of > privilege, similar to the FGAP proposal. Instead of unconditionally > calling fmac_vnode_priv_access(), only invoke it when the subject is > lacking the required privilege. That doesn't appear to support using FMAC to further restrict what privileges can be used by a uid-0 service daemon or a setuid-root program. That is a common application of SELinux. With the file DAC privileges, that may be less of a concern, since we already have the base file access checks applied by fmac_vnode_access() called from zfs_zaccess() and friends, although it does mean that we have no way to restrict DAC overrides in particular. For privileges where the privilege check itself is the only check applied to control a given operation, that becomes more of a concern, particularly when the privilege might allow subversion of the FMAC policy enforcement itself (e.g. raw access, administrative operations). One of the niceties of the SELinux capability checks is that we can analyze the policy and see what domains can ever use a given capability at all - it establishes the upper bound for capability usage in a centralized policy. One other concern with "authoritative" behavior that occurred to me is traditional Unix "privilege" bracketing via seteuid(). Such a process wouldn't be marked as privilege aware naturally and thus FMAC could potentially grant DAC privileges to the process even though the process tried to drop DAC privileges by switching to an unprivileged uid before calling open. I think that we need to be very careful about granting privileges authoritatively via FMAC, which would suggest making it a per-domain property and only marking domains authoritative for programs we know to operate safely under those conditions. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Thu Nov 6 13:00:05 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Thu, 06 Nov 2008 13:00:05 -0800 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1226002070.814.154.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> <49134726.7040109@sun.com> <1226002070.814.154.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <49135AD5.3010805@sun.com> Stephen Smalley wrote: > On Thu, 2008-11-06 at 11:36 -0800, Glenn Faden wrote: > >> I have an alternative proposal which preserves the existing privilege >> sets, but allows FMAC to be authoritative in the case of lack of >> privilege, similar to the FGAP proposal. Instead of unconditionally >> calling fmac_vnode_priv_access(), only invoke it when the subject is >> lacking the required privilege. >> > > That doesn't appear to support using FMAC to further restrict what > privileges can be used by a uid-0 service daemon or a setuid-root > program. That is a common application of SELinux. > > With the file DAC privileges, that may be less of a concern, since we > already have the base file access checks applied by fmac_vnode_access() > called from zfs_zaccess() and friends, although it does mean that we > have no way to restrict DAC overrides in particular. > > For privileges where the privilege check itself is the only check > applied to control a given operation, that becomes more of a concern, > particularly when the privilege might allow subversion of the FMAC > policy enforcement itself (e.g. raw access, administrative operations). > I guess I'm missing something. Isn't there going to be an avc_has_perm (restrictive) call for everything that might be allowed by the legacy policy? Aren't there FMAC permission checks for raw_access and similar operations? > One of the niceties of the SELinux capability checks is that we can > analyze the policy and see what domains can ever use a given capability > at all - it establishes the upper bound for capability usage in a > centralized policy. > I appreciate the nicety. But it doesn't seem to be possible to do this in a backward compatible manner. > One other concern with "authoritative" behavior that occurred to me is > traditional Unix "privilege" bracketing via seteuid(). Such a process > wouldn't be marked as privilege aware naturally and thus FMAC could > potentially grant DAC privileges to the process even though the process > tried to drop DAC privileges by switching to an unprivileged uid before > calling open. > Yes, this is another reason to allow the existing process privileges to be honored when FMAC is enabled. > I think that we need to be very careful about granting privileges > authoritatively via FMAC, which would suggest making it a per-domain > property and only marking domains authoritative for programs we know to > operate safely under those conditions. > I think the proposed mechanism for making FMAC authoritative should be complementary to the existing process privilege mechanism, rather than replacing it. I'm not sure if we are using the term "authoritative" in the same way. My primary interest for making FMAC authoritative is to provide a mechanism where process privileges (aka root privileges) are not required for specific "privileged" operations which are allowed according to the policy. I don't think FMAC should be the exclusive mechanism. I thought that the restrictive aspects of FMAC (i.e. Type Enforcement) would provide the necessary constraints lacking in the current process privilege model. From sds at tycho.nsa.gov Thu Nov 6 14:07:34 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Thu, 06 Nov 2008 17:07:34 -0500 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <49135AD5.3010805@sun.com> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> <49134726.7040109@sun.com> <1226002070.814.154.camel@moss-spartans.epoch.ncsc.mil> <49135AD5.3010805@sun.com> Message-ID: <1226009254.814.199.camel@moss-spartans.epoch.ncsc.mil> On Thu, 2008-11-06 at 13:00 -0800, Glenn Faden wrote: > Stephen Smalley wrote: > > On Thu, 2008-11-06 at 11:36 -0800, Glenn Faden wrote: > > > >> I have an alternative proposal which preserves the existing privilege > >> sets, but allows FMAC to be authoritative in the case of lack of > >> privilege, similar to the FGAP proposal. Instead of unconditionally > >> calling fmac_vnode_priv_access(), only invoke it when the subject is > >> lacking the required privilege. > >> > > > > That doesn't appear to support using FMAC to further restrict what > > privileges can be used by a uid-0 service daemon or a setuid-root > > program. That is a common application of SELinux. > > > > With the file DAC privileges, that may be less of a concern, since we > > already have the base file access checks applied by fmac_vnode_access() > > called from zfs_zaccess() and friends, although it does mean that we > > have no way to restrict DAC overrides in particular. > > > > For privileges where the privilege check itself is the only check > > applied to control a given operation, that becomes more of a concern, > > particularly when the privilege might allow subversion of the FMAC > > policy enforcement itself (e.g. raw access, administrative operations). > > > > I guess I'm missing something. Isn't there going to be an avc_has_perm > (restrictive) call for everything that might be allowed by the legacy > policy? Aren't there FMAC permission checks for raw_access and similar > operations? We would typically insert a hook into e.g. secpolicy_net_rawaccess, that might check a net_raw permission in a network security class (no real object there). That permission would control the ability of a process to have raw access to the network. How the enforcement logic combines that permission check with the legacy privilege model is what distinguishes restrictive (allow iff both the legacy privilege logic and the FMAC permission check authorizes the privilege) from authoritative (allow iff the FMAC permission check authorizes the privilege). The possible scenarios are: - The combining logic is always restrictive-only. - The combining logic is always authoritative. - The combining logic depends on some property of the process' security context in the policy, e.g. some domains are authoritative and some domains are restrictive, Or we could have two different permission checks, with one being a restrictive-only check and one being an authoritative check, distinguished either by permission (e.g. net_raw, net_raw_override) or by class (e.g. network, network_override). That's actually what I proposed for SELinux (capability vs cap_override) but mostly because that would preserve compatibility with legacy policies and not unwittingly grant capabilities to domains. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Thu Nov 6 14:39:48 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Thu, 06 Nov 2008 14:39:48 -0800 Subject: [fmac-discuss] [RFC][PATCH v2] Mediate file DAC privilege checks In-Reply-To: <1226009254.814.199.camel@moss-spartans.epoch.ncsc.mil> References: <1225825740.6654.66.camel@moss-spartans.epoch.ncsc.mil> <1225893759.19111.68.camel@moss-spartans.epoch.ncsc.mil> <4911F449.6060003@sun.com> <1225915598.19111.95.camel@moss-spartans.epoch.ncsc.mil> <491245AC.3010805@sun.com> <1225982568.814.46.camel@moss-spartans.epoch.ncsc.mil> <49134726.7040109@sun.com> <1226002070.814.154.camel@moss-spartans.epoch.ncsc.mil> <49135AD5.3010805@sun.com> <1226009254.814.199.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <49137234.2040103@sun.com> Stephen Smalley wrote: > On Thu, 2008-11-06 at 13:00 -0800, Glenn Faden wrote: > >> Stephen Smalley wrote: >> > We would typically insert a hook into e.g. secpolicy_net_rawaccess, that > might check a net_raw permission in a network security class (no real > object there). That permission would control the ability of a process > to have raw access to the network. How the enforcement logic combines > that permission check with the legacy privilege model is what > distinguishes restrictive (allow iff both the legacy privilege logic and > the FMAC permission check authorizes the privilege) from authoritative > (allow iff the FMAC permission check authorizes the privilege). > > The possible scenarios are: > - The combining logic is always restrictive-only. > - The combining logic is always authoritative. > - The combining logic depends on some property of the process' security > context in the policy, e.g. some domains are authoritative and some > domains are restrictive, > > Or we could have two different permission checks, with one being a > restrictive-only check and one being an authoritative check, > distinguished either by permission (e.g. net_raw, net_raw_override) or > by class (e.g. network, network_override). That's actually what I > proposed for SELinux (capability vs cap_override) but mostly because > that would preserve compatibility with legacy policies and not > unwittingly grant capabilities to domains. > In general, I think we need two different permission checks as you've suggested. Generally the authoritative check would be course-grained corresponding to the current process privileges which don't specify the objects to which they apply. Any relevant FMAC "authoritative" permissions would be interpreted here. The second check would be restrictive, taking into account the attributes of the object. I'm not sure why raw access has no object. Isn't the NIC an object? Anyway, we could still have a required domain for some special cases where there is no object. From sds at tycho.nsa.gov Thu Nov 13 05:44:04 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Thu, 13 Nov 2008 08:44:04 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks Message-ID: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> Take 3 of a patch showing how we can approach granting privileges in FMAC. This version takes Glenn's suggestion that the priv_* checks applied by fmac_vnode_priv_access() only be applied if the legacy policy would deny the privilege (i.e. to grant privileges that would normally be denied) similar to FGAP, while the normal read/write/execute checks applied by fmac_vnode_access() can be used restrictively. However, the priv_* checks are kept as object-based checks; this is necessary if we want to provide the same functionality as FGAP/klpd via FMAC. This patch also includes policy changes from John Weeks to allow /bin/passwd to run without being setuid root, specifically granting it priv_* permissions needed for accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This enables one to successfully run /bin/passwd as a non-root user even after removing the setuid bit from it. However, there is one "regression" in the behavior of /bin/passwd when run in this way; /etc/shadow is left with the ownership of the invoking user rather than root afterward since it is re-created on the transaction and /bin/passwd apparently doesn't explicitly preserve/set the owner in Solaris. Thus we would need to change /bin/passwd to preserve/set the ownership if we wanted to grant it privileges in this manner rather than via setuid. The overall permission checking logic after this patch is: 1) Check DAC permissions to the file. Denied? Go to 2. Granted? Go to 5. 2) Check file DAC privileges. Denied? Go to 3. Granted? Go to 5. 3) If using klpd, call it to check the privilege to the file. No klpd or Denied? Go to 4. Granted? Go to 5. 4) Check FMAC priv_* permissions to the file. Denied? Done - Permission denied. Granted? Go to 5. 5) Check FMAC read/write/execute to the file. Denied? Done - Permission denied. Granted? Done - Permission granted. Webrev is available at: http://cr.opensolaris.org/~sds/filedacpriv/ diff --git a/usr/src/cmd/fmac/policy/domains/program/passwd.te b/usr/src/cmd/fmac/policy/domains/program/passwd.te --- a/usr/src/cmd/fmac/policy/domains/program/passwd.te +++ b/usr/src/cmd/fmac/policy/domains/program/passwd.te @@ -44,14 +44,18 @@ allow passwd_t local_login_t:fd inherit_fd_perms; allow passwd_t remote_login_t:fd inherit_fd_perms; -# Update /etc/passwd. +# Update /etc/passwd and /etc/.pwd.lock. allow passwd_t etc_t:dir rw_dir_perms; +allow passwd_t etc_t:dir priv_write; allow passwd_t etc_t:file create_file_perms; +allow passwd_t etc_t:file priv_write; # Update /etc/shadow. allow passwd_t shadow_t:file create_file_perms; +allow passwd_t shadow_t:file { priv_read priv_write }; file_type_auto_trans(passwd_t, etc_t, shadow_t) # Update lastlog. allow passwd_t var_log_t:dir rw_dir_perms; allow passwd_t var_log_t:file create_file_perms; +allow passwd_t var_log_t:file priv_write; diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te --- a/usr/src/cmd/fmac/policy/macros.te +++ b/usr/src/cmd/fmac/policy/macros.te @@ -467,7 +467,7 @@ define(`unconfined_domain', ` allow $1 domain:process *; -allow $1 file_type:dir_file_class_set *; -allow $1 unlabeled_t:dir_file_class_set *; +allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; +allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; allow $1 security_t:security *; ') diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -40,6 +40,9 @@ write read append + priv_execute + priv_write + priv_read open create link @@ -130,6 +133,7 @@ remove_name reparent search + priv_search rmdir mounton mountassociate diff --git a/usr/src/uts/common/fmac/avc.c b/usr/src/uts/common/fmac/avc.c --- a/usr/src/uts/common/fmac/avc.c +++ b/usr/src/uts/common/fmac/avc.c @@ -920,9 +920,10 @@ return (0); } -int -avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, - access_vector_t requested, avc_audit_data_t *auditdata) +static int +avc_has_perm_common(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata, boolean_t strict) { struct av_decision avd; access_vector_t denied; @@ -940,7 +941,7 @@ if (denied & avd.auditdeny) avc_audit(ssid, tsid, tclass, denied, AVC_AUDITDENY, auditdata); - if (fmac_enforcing) { + if (strict || fmac_enforcing) { return (EACCES); } else { (void) avc_update_cache(AVC_CALLBACK_GRANT, ssid, tsid, @@ -955,3 +956,20 @@ return (0); } + +int +avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, + access_vector_t requested, avc_audit_data_t *auditdata) +{ + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, + B_FALSE); +} + +int +avc_has_perm_strict(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata) +{ + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, + B_TRUE); +} diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -39,6 +39,7 @@ #include #include #include +#include #include /* Tunables */ @@ -240,7 +241,7 @@ if (!fmac_enabled) return (EINVAL); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; sclass = fmac_vtype_to_sclass(vtype); if (!sclass) @@ -339,7 +340,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; error = security_transition_sid(cr_secid, dvp->v_secid, sclass, &secid); @@ -423,7 +424,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = tdvp; @@ -454,7 +455,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = dvp; @@ -489,7 +490,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); @@ -545,7 +546,7 @@ if (!sclass) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = vp; @@ -564,14 +565,17 @@ if (!fmac_enabled) return (0); - prev_secid = crgetsecid(cr); - secid = crgetexecsecid(cr); + prev_secid = cr->cr_secid; + secid = cr->cr_exec_secid; if (!secid) { error = security_transition_sid(prev_secid, vp->v_secid, SECCLASS_PROCESS, &secid); if (error) return (error); } + + if (vp->v_vfsp->vfs_flag & VFS_NOSETUID) + secid = prev_secid; AVC_AUDIT_DATA_INIT(&ad, FS); ad.u.fs.vp = vp; @@ -633,7 +637,7 @@ if (!fmac_enabled) return (0); - cr_secid = crgetsecid(cr); + cr_secid = cr->cr_secid; sclass = fmac_vtype_to_sclass(vp->v_type); if (!sclass) @@ -697,7 +701,7 @@ if (!fmac_enabled) return (0); - return (avc_has_perm(crgetsecid((cred_t *)scr), crgetsecid(tcr), + return (avc_has_perm(scr->cr_secid, tcr->cr_secid, SECCLASS_PROCESS, PROCESS__PTRACE, NULL)); } @@ -724,7 +728,65 @@ if (!fmac_enabled) return (0); - tsecid = crgetsecid((cred_t *)tcrp); - ssecid = crgetsecid((cred_t *)scrp); + tsecid = tcrp->cr_secid; + ssecid = scrp->cr_secid; return (avc_has_perm(ssecid, tsecid, SECCLASS_PROCESS, perms, NULL)); } + +int +fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) +{ + security_id_t cr_secid; + security_class_t sclass; + access_vector_t av; + avc_audit_data_t ad; + priv_set_t privs; + + /* If not enabled, just return the legacy policy decision. */ + if (!fmac_enabled) + return (err); + + /* + * If privilege aware, then honor any legacy policy denial + * since the program may have reduced its own privileges. + */ + if (err && (CR_FLAGS(cr) & PRIV_AWARE)) + return (err); + + cr_secid = cr->cr_secid; + + sclass = fmac_vtype_to_sclass(vp->v_type); + if (!sclass) + return (err); + + av = 0; + priv_emptyset(&privs); + if (mode & VREAD) { + av |= FILE__PRIV_READ; + priv_addset(&privs, PRIV_FILE_DAC_READ); + } + if (mode & VWRITE) { + av |= FILE__PRIV_WRITE; + priv_addset(&privs, PRIV_FILE_DAC_WRITE); + } + + if (mode & VEXEC) { + if (sclass == SECCLASS_DIR) { + av |= DIR__PRIV_SEARCH; + priv_addset(&privs, PRIV_FILE_DAC_SEARCH); + } else { + av |= FILE__PRIV_EXECUTE; + priv_addset(&privs, PRIV_FILE_DAC_EXECUTE); + } + } + + if (!av) + return (err); + + if (err && !priv_issubset(&privs, &CR_LPRIV(cr))) + return (err); + + AVC_AUDIT_DATA_INIT(&ad, FS); + ad.u.fs.vp = vp; + return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); +} diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -725,7 +725,7 @@ if (cred->cr_ruid != cred->cr_uid || (cred->cr_rgid != cred->cr_gid && !supgroupmember(cred->cr_gid, cred)) || - (privflags & PRIV_INCREASE) != 0) + (privflags & PRIV_INCREASE) != 0 || fmac_execsetid) suidflags = PSUIDFLAGS; else suidflags = 0; diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -834,13 +834,15 @@ int secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode) { + int err = 0; + if ((mode & VREAD) && priv_policy_va(cr, PRIV_FILE_DAC_READ, B_FALSE, EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE) != 0) { - return (EACCES); + err = EACCES; } - if (mode & VWRITE) { + if (!err && (mode & VWRITE)) { boolean_t allzone; if (owner == 0 && cr->cr_uid != 0) @@ -850,21 +852,25 @@ if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE) != 0) { - return (EACCES); + err = EACCES; } } - if (mode & VEXEC) { + if (!err && (mode & VEXEC)) { /* * Directories use file_dac_search to override the execute bit. */ int p = vp->v_type == VDIR ? PRIV_FILE_DAC_SEARCH : PRIV_FILE_DAC_EXECUTE; - return (priv_policy_va(cr, p, B_FALSE, EACCES, NULL, - KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE)); + err = priv_policy_va(cr, p, B_FALSE, EACCES, NULL, + KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE); } - return (0); + + if (err) + return (fmac_vnode_priv_access(cr, vp, mode, err)); + else + return (0); } /* diff --git a/usr/src/uts/common/sys/fmac/avc.h b/usr/src/uts/common/sys/fmac/avc.h --- a/usr/src/uts/common/sys/fmac/avc.h +++ b/usr/src/uts/common/sys/fmac/avc.h @@ -88,6 +88,11 @@ security_class_t tclass, access_vector_t requested, avc_audit_data_t *auditdata); +/* Check requested permissions without considering permissive mode/domains. */ +extern int avc_has_perm_strict(security_id_t ssid, security_id_t tsid, + security_class_t tclass, access_vector_t requested, + avc_audit_data_t *auditdata); + #define AVC_CALLBACK_GRANT 1 #define AVC_CALLBACK_TRY_REVOKE 2 #define AVC_CALLBACK_REVOKE 4 diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -105,6 +105,7 @@ access_vector_t fmac_sigtoav(int sig); int fmac_hasprocperm(const cred_t *tcrp, const cred_t *scrp, access_vector_t perms); +int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); #endif /* _KERNEL */ #ifdef __cplusplus -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Thu Nov 13 17:06:24 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Thu, 13 Nov 2008 17:06:24 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <491CCF10.9030806@sun.com> Stephen Smalley wrote: > Take 3 of a patch showing how we can approach granting privileges in > FMAC. This version takes Glenn's suggestion that the priv_* checks > applied by fmac_vnode_priv_access() only be applied if the legacy policy > would deny the privilege (i.e. to grant privileges that would normally > be denied) similar to FGAP, while the normal read/write/execute checks > applied by fmac_vnode_access() can be used restrictively. However, the > priv_* checks are kept as object-based checks; this is necessary if we > want to provide the same functionality as FGAP/klpd via FMAC. > > This patch also includes policy changes from John Weeks to > allow /bin/passwd to run without being setuid root, specifically > granting it priv_* permissions needed for > accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This > enables one to successfully run /bin/passwd as a non-root user even > after removing the setuid bit from it. However, there is one > "regression" in the behavior of /bin/passwd when run in this > way; /etc/shadow is left with the ownership of the invoking user rather > than root afterward since it is re-created on the transaction > and /bin/passwd apparently doesn't explicitly preserve/set the owner in > Solaris. Thus we would need to change /bin/passwd to preserve/set the > ownership if we wanted to grant it privileges in this manner rather than > via setuid. > This version seems to meet both the backward compatibility and MAC functionality. I've tried it out using John Weeks' test system and got some interesting results. Authoritative privileges are applied, and legacy process privileges still work when consistent with the Type Enforcement policy. There are still some confusing side effects. For example, using su (as root) to switch to another user doesn't currently change the process security context as newrole does. So the objects created by the new user still have the old user's security context. Fixing this will avoid a source of confusion. John's suggestion about running the passwd(1) program without requiring setuid-to-root, seems to break backward compatibility because it fails to preserve DAC attributes of root objects. Although the passwd(1) program could be modified to explicitly set the owner of the new /etc/shadow file, it is not a generic solution for similar situations. In Trusted Solaris 8 where we had forced privileges on executables, we didn't treat setuid-to-root as granting all privileges. But we ran into problems because (sometimes) it wasn't backward compatible enough with traditional UNIX. I propose having a new FMAC permission, e.g, priv_escalate, which would be required for an exec'ed process to get the current behavior of escalating its permitted and effective privileges to the limit set when the setuid-to-root bit is set. For specialized programs like passwd(1) with their own domain, they wouldn't be granted the priv_escalate permission. So the passwd(1) program would still be be suid-to-root, but would just run with the process privileges of its parent. For backward compatibility, for applications that really need to run with all privileges (maybe the su(1M) command itself?), we could have an unconfined domain that includes the priv_escalate permission. --Glenn From john.weeks at sun.com Thu Nov 13 23:44:46 2008 From: john.weeks at sun.com (John Weeks) Date: Thu, 13 Nov 2008 23:44:46 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <491CCF10.9030806@sun.com> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> Message-ID: <491D2C6E.9010003@sun.com> On 11/13/08 17:06, Glenn Faden wrote: > Stephen Smalley wrote: >> Take 3 of a patch showing how we can approach granting privileges in >> FMAC. This version takes Glenn's suggestion that the priv_* checks >> applied by fmac_vnode_priv_access() only be applied if the legacy policy >> would deny the privilege (i.e. to grant privileges that would normally >> be denied) similar to FGAP, while the normal read/write/execute checks >> applied by fmac_vnode_access() can be used restrictively. However, the >> priv_* checks are kept as object-based checks; this is necessary if we >> want to provide the same functionality as FGAP/klpd via FMAC. >> >> This patch also includes policy changes from John Weeks to >> allow /bin/passwd to run without being setuid root, specifically >> granting it priv_* permissions needed for >> accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This >> enables one to successfully run /bin/passwd as a non-root user even >> after removing the setuid bit from it. However, there is one >> "regression" in the behavior of /bin/passwd when run in this >> way; /etc/shadow is left with the ownership of the invoking user rather >> than root afterward since it is re-created on the transaction >> and /bin/passwd apparently doesn't explicitly preserve/set the owner in >> Solaris. Thus we would need to change /bin/passwd to preserve/set the >> ownership if we wanted to grant it privileges in this manner rather than >> via setuid. >> > This version seems to meet both the backward compatibility and MAC > functionality. I've tried it out using John Weeks' test system and got > some interesting results. Authoritative privileges are applied, and > legacy process privileges still work when consistent with the Type > Enforcement policy. > > There are still some confusing side effects. For example, using su (as > root) to switch to another user doesn't currently change the process > security context as newrole does. So the objects created by the new user > still have the old user's security context. Fixing this will avoid a > source of confusion. This was mentioned in the newrole patch. Please remember that this is an open source project and development happens incrementally. Would you like to work on the enhancements to su? We're always looking for contributors ;-) > > John's suggestion about running the passwd(1) program without requiring > setuid-to-root, seems to break backward compatibility because it fails > to preserve DAC attributes of root objects. Although the passwd(1) > program could be modified to explicitly set the owner of the new > /etc/shadow file, it is not a generic solution for similar situations. > In Trusted Solaris 8 where we had forced privileges on executables, we > didn't treat setuid-to-root as granting all privileges. But we ran into > problems because (sometimes) it wasn't backward compatible enough with > traditional UNIX. This is a demonstration of what can be done with FMAC. With the cyber threats we're faced with, I'll take security over compatibility any day. Don't take me wrong, I really DO want to maintain compatibility, but sometimes we need to make hard choices. > > I propose having a new FMAC permission, e.g, priv_escalate, which would > be required for an exec'ed process to get the current behavior of > escalating its permitted and effective privileges to the limit set when > the setuid-to-root bit is set. For specialized programs like passwd(1) > with their own domain, they wouldn't be granted the priv_escalate > permission. So the passwd(1) program would still be be suid-to-root, but > would just run with the process privileges of its parent. I do favor constraining the subject to only what it needs, and in this case reducing unnecessary P/E privileges would be desirable. Steve, what do you think? > > For backward compatibility, for applications that really need to run > with all privileges (maybe the su(1M) command itself?), we could have an > unconfined domain that includes the priv_escalate permission. Or you create a new domain for the application and allow process:priv_escalate. BTW - su already has its own domain. > > --Glenn > _______________________________________________ > fmac-discuss mailing list > fmac-discuss at opensolaris.org > http://mail.opensolaris.org/mailman/listinfo/fmac-discuss From sds at tycho.nsa.gov Fri Nov 14 06:05:50 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 14 Nov 2008 09:05:50 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <491D2C6E.9010003@sun.com> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> Message-ID: <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> On Thu, 2008-11-13 at 23:44 -0800, John Weeks wrote: > On 11/13/08 17:06, Glenn Faden wrote: > > Stephen Smalley wrote: > >> Take 3 of a patch showing how we can approach granting privileges in > >> FMAC. This version takes Glenn's suggestion that the priv_* checks > >> applied by fmac_vnode_priv_access() only be applied if the legacy policy > >> would deny the privilege (i.e. to grant privileges that would normally > >> be denied) similar to FGAP, while the normal read/write/execute checks > >> applied by fmac_vnode_access() can be used restrictively. However, the > >> priv_* checks are kept as object-based checks; this is necessary if we > >> want to provide the same functionality as FGAP/klpd via FMAC. > >> > >> This patch also includes policy changes from John Weeks to > >> allow /bin/passwd to run without being setuid root, specifically > >> granting it priv_* permissions needed for > >> accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This > >> enables one to successfully run /bin/passwd as a non-root user even > >> after removing the setuid bit from it. However, there is one > >> "regression" in the behavior of /bin/passwd when run in this > >> way; /etc/shadow is left with the ownership of the invoking user rather > >> than root afterward since it is re-created on the transaction > >> and /bin/passwd apparently doesn't explicitly preserve/set the owner in > >> Solaris. Thus we would need to change /bin/passwd to preserve/set the > >> ownership if we wanted to grant it privileges in this manner rather than > >> via setuid. > >> > > This version seems to meet both the backward compatibility and MAC > > functionality. I've tried it out using John Weeks' test system and got > > some interesting results. Authoritative privileges are applied, and > > legacy process privileges still work when consistent with the Type > > Enforcement policy. > > > > There are still some confusing side effects. For example, using su (as > > root) to switch to another user doesn't currently change the process > > security context as newrole does. So the objects created by the new user > > still have the old user's security context. Fixing this will avoid a > > source of confusion. > > This was mentioned in the newrole patch. Please remember that this is > an open source project and development happens incrementally. Would > you like to work on the enhancements to su? We're always looking for > contributors ;-) FWIW, John's original pam_unix_creds patch did switch contexts on su(1), but I recommended against that and he changed it to only change contexts in the PAM_ESTABLISH_CRED case. It could be changed back, but I should note that we've gone back and forth on this issue in SELinux, originally keeping su(1) and context changes entirely separate, then integrating context changes into su (via pam_selinux) during the early Fedora integration, and then separating them out again based both on the problems encountered with integrating them and on the desires of the MLS/LSPP crowd. Preserving caller context across su(1) (i.e. not changing the context based on the new uid) has the advantages of: 1) Ensuring that you can't break out of the restrictions imposed on the FMAC user identity established at login time, providing strong containment of the entire session by bounding the set of reachable roles and levels. You can still change to other roles/levels if allowed by policy via newrole, but the set of reachable roles/levels is bounded based on the user identity in which you started the login session and cannot be escalated beyond that set. 2) Avoiding undesirable context changes when only a uid change was desired, as when launching an application in a pseudo uid. We discovered that this is done by a number of application startup scripts at least in Linux and was incorrectly trying to switch into a context for the user rather than just preserving the base user/role/level and only changing domain on application execution. 3) Not placing any trust in su(1) as far as FMAC is concerned. Thus a flaw in su(1) does not represent a privilege escalation vulnerability wrt to FMAC. However, given the Solaris RBAC model and the plan to map Solaris role uids to FMAC roles, we seemingly have to change contexts upon su(1) at least when the target uid is a role, and I'm not clear that su/pam actually strongly distinguishes that case from any other su. I haven't looked into that yet, so help there would be welcome. > > > > John's suggestion about running the passwd(1) program without requiring > > setuid-to-root, seems to break backward compatibility because it fails > > to preserve DAC attributes of root objects. Although the passwd(1) > > program could be modified to explicitly set the owner of the new > > /etc/shadow file, it is not a generic solution for similar situations. > > In Trusted Solaris 8 where we had forced privileges on executables, we > > didn't treat setuid-to-root as granting all privileges. But we ran into > > problems because (sometimes) it wasn't backward compatible enough with > > traditional UNIX. > > This is a demonstration of what can be done with FMAC. With the cyber > threats we're faced with, I'll take security over compatibility any > day. Don't take me wrong, I really DO want to maintain compatibility, > but sometimes we need to make hard choices. I'm not clear on why changing passwd to explicitly set ownership on the files it creates isn't a generic solution or just plain good practice anyway. I believe that in Linux, the library functions for manipulating passwd and shadow are careful to save and restore the ownership and mode information whenever they act on those files. Regardless, we will have to be careful in general about converting setuid root programs to using this mechanism for privilege granting, particularly with regard to setuid root programs that currently do privilege bracketing via seteuid(), so I expect some application changes to sometimes be required when performing such a conversion. Note that fmac_vnode_priv_access() is careful to not grant a privilege for privilege aware processes and to not exceed the limit set, but this doesn't cover the case where an application is doing privilege bracketing via seteuid() IIUC. > > > > I propose having a new FMAC permission, e.g, priv_escalate, which would > > be required for an exec'ed process to get the current behavior of > > escalating its permitted and effective privileges to the limit set when > > the setuid-to-root bit is set. For specialized programs like passwd(1) > > with their own domain, they wouldn't be granted the priv_escalate > > permission. So the passwd(1) program would still be be suid-to-root, but > > would just run with the process privileges of its parent. > > I do favor constraining the subject to only what it needs, and in this > case reducing unnecessary P/E privileges would be desirable. Steve, > what do you think? As I understand it, it is already the case that the limit set will not be used as the observed effective/permitted sets if the process is privilege aware or if the uid is non-zero. Thus, by removing the setuid bit from passwd (which I think is the right approach, fixing the passwd program as needed to preserve ownership on files it re-creates during a transaction), we already get the desired behavior, or by making an application privilege aware, we also get the desired behavior. I wouldn't add yet another condition to the logic which decides what to use as the observed effective/permitted set. In terms of restricting privilege use based on FMAC policy, if we want to support that, then I'd favor adding yet another set of checks for that purpose, either at object granularity (priv_read_limit, priv_write_limit) checked from a hook called by secpolicy_vnode_access() when the privilege is allowed by the legacy privilege logic or at just a subject granularity (file_dac_read, file_dac_write, ... in a new privilege class) checked from a hook called by the priv_policy* functions. But understand what that means for compatibility - it means that we must label every setuid root program with an entrypoint type to a domain that is allowed the desired privileges (and the same would be true of this priv_escalate suggestion, just with less granularity), including legacy setuid root programs, third party setuid root programs, and most problematically, setuid root programs on filesystems that do not support labeling would no longer work unless called from a domain that was already allowed the privileges in the first place. > > For backward compatibility, for applications that really need to run > > with all privileges (maybe the su(1M) command itself?), we could have an > > unconfined domain that includes the priv_escalate permission. > > Or you create a new domain for the application and allow process:priv_escalate. BTW - su already has its own domain. True, but understand the compatibility implications of this for third party setuid root programs and for setuid root program execution on filesystems that do not support labeling. -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Fri Nov 14 06:31:40 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 14 Nov 2008 09:31:40 -0500 Subject: [fmac-discuss] [PATCH] Simplify FMAC exec logic Message-ID: <1226673100.9382.64.camel@moss-spartans.epoch.ncsc.mil> With the change in the prior patch to the exec code path to also set the process suid flags when fmac_execsetid is true (i.e. when execsetid permission is not allowed between the old and new contexts), we can simplify the FMAC processing in the exec code path. If we set PRIV_INCREASE in the privflags when fmac_execsetid is true, then the subsequent exec logic will set the linker security flag and the process suid flags without requiring any further checking of fmac_execsetid. As fmac_execsetid is no longer being used only to set the linker security flag, the fmac_execsetid boolean is renamed to setprivinc, and the "execsetid" FMAC permission is renamed to "noprivinc" (i.e. no need to assert privilege increase protections on this domain transition). There should be no actual change in behavior as a result, just an implementation that fits better into the existing logic. This patch applies on top of the prior one. Webrev available at: http://cr.opensolaris.org/~sds/privinc/ diff --git a/usr/src/cmd/fmac/policy/domains/program/newrole.te b/usr/src/cmd/fmac/policy/domains/program/newrole.te --- a/usr/src/cmd/fmac/policy/domains/program/newrole.te +++ b/usr/src/cmd/fmac/policy/domains/program/newrole.te @@ -52,8 +52,8 @@ # Allow newrole_t to transition to user domains. domain_trans(newrole_t, shell_exec_t, userdomain) -# No need for linker security flag setting on newrole -> user shell transition. -allow newrole_t { user_t sysadm_t }:process execsetid; +# No need for privilege increase protections on newrole -> user shell transition. +allow newrole_t { user_t sysadm_t }:process noprivinc; # Use capabilities. allow newrole_t self:capability { setuid setgid net_bind_service dac_override }; diff --git a/usr/src/cmd/fmac/policy/domains/program/su.te b/usr/src/cmd/fmac/policy/domains/program/su.te --- a/usr/src/cmd/fmac/policy/domains/program/su.te +++ b/usr/src/cmd/fmac/policy/domains/program/su.te @@ -46,8 +46,8 @@ # Revert to the user domain when a shell is executed. domain_auto_trans($1_su_t, shell_exec_t, $1_t) -# No need to set linker security flag on the su -> user shell transition. -allow $1_su_t $1_t:process execsetid; +# No need for privilege increase protections on su -> user shell transition. +allow $1_su_t $1_t:process noprivinc; # Create/update /var/adm/sulog. allow $1_su_t var_log_t:dir rw_dir_perms; diff --git a/usr/src/cmd/fmac/policy/domains/system/sshd.te b/usr/src/cmd/fmac/policy/domains/system/sshd.te --- a/usr/src/cmd/fmac/policy/domains/system/sshd.te +++ b/usr/src/cmd/fmac/policy/domains/system/sshd.te @@ -76,8 +76,8 @@ domain_trans(sshd_t, shell_exec_t, user_t) domain_trans(sshd_t, shell_exec_t, sysadm_t) -# No need for linker security flag setting on sshd -> user shell transition. -allow sshd_t { user_t sysadm_t }:process execsetid; +# No need for privilege increase protections on sshd -> user shell transition. +allow sshd_t { user_t sysadm_t }:process noprivinc; # Read /etc/shadow allow sshd_t shadow_t:file r_file_perms; diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te --- a/usr/src/cmd/fmac/policy/macros.te +++ b/usr/src/cmd/fmac/policy/macros.te @@ -229,9 +229,12 @@ allow $3 $2:file entrypoint; # -# Silence execsetid avc messages. +# Silence "no privilege increase" avc messages. +# These are only allowed where the calling domain is more +# privileged than the new domain. Controls setting of the +# linker security flag and process suid flags. # -dontaudit $1 $3:process execsetid; +dontaudit $1 $3:process noprivinc; ') ################################# diff --git a/usr/src/cmd/fmac/policy/mls b/usr/src/cmd/fmac/policy/mls --- a/usr/src/cmd/fmac/policy/mls +++ b/usr/src/cmd/fmac/policy/mls @@ -228,7 +228,7 @@ setpgid : write setexec : write setfscreate : write - execsetid : none + noprivinc : none } class sem diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -265,7 +265,7 @@ getattr setexec setfscreate - execsetid + noprivinc } diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -556,7 +556,7 @@ int fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, - boolean_t *execsetid, security_id_t *prev_secidp, security_id_t *secidp) + boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp) { security_id_t prev_secid, secid; int error; @@ -585,7 +585,7 @@ SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); if (error) return (error); - *execsetid = B_FALSE; + *setprivinc = B_FALSE; *setsecid = B_FALSE; *prev_secidp = *secidp = secid; return (0); @@ -602,11 +602,11 @@ return (error); error = avc_has_perm(prev_secid, secid, SECCLASS_PROCESS, - PROCESS__EXECSETID, &ad); + PROCESS__NOPRIVINC, &ad); if (error) - *execsetid = B_TRUE; + *setprivinc = B_TRUE; else - *execsetid = B_FALSE; + *setprivinc = B_FALSE; *setsecid = B_TRUE; *prev_secidp = prev_secid; diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -524,7 +524,7 @@ ssize_t resid; uid_t uid, gid; security_id_t prev_secid, secid; - boolean_t setsecid = B_FALSE, fmac_execsetid = B_FALSE; + boolean_t setsecid = B_FALSE, setprivinc = B_FALSE; struct vattr vattr; char magbuf[MAGIC_BYTES]; int setid; @@ -568,13 +568,16 @@ if (level == 0) { privflags = execsetid(vp, &vattr, &uid, &gid); - error = fmac_exec(CRED(), vp, &setsecid, &fmac_execsetid, + error = fmac_exec(CRED(), vp, &setsecid, &setprivinc, &prev_secid, &secid); if (error) goto bad; if (setsecid) privflags |= PRIV_SETUGID; + + if (setprivinc) + privflags |= PRIV_INCREASE; } if (level == 0 && privflags != 0) { @@ -677,9 +680,6 @@ if (setid & PRIV_INCREASE) setidfl |= EXECSETID_PRIVS; - if (fmac_execsetid) - setidfl |= EXECSETID_UGIDS; - error = (*eswp->exec_func)(vp, uap, args, idatap, level, execsz, setidfl, exec_file, cred, brand_action); rw_exit(eswp->exec_lock); @@ -725,7 +725,7 @@ if (cred->cr_ruid != cred->cr_uid || (cred->cr_rgid != cred->cr_gid && !supgroupmember(cred->cr_gid, cred)) || - (privflags & PRIV_INCREASE) != 0 || fmac_execsetid) + (privflags & PRIV_INCREASE) != 0) suidflags = PSUIDFLAGS; else suidflags = 0; diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -99,7 +99,7 @@ cred_t *cr); int fmac_vnode_setattr(vnode_t *, cred_t *); int fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, - boolean_t *execsetid, security_id_t *prev_secidp, security_id_t *secidp); + boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp); int fmac_vnode_access(vnode_t *, int, int, cred_t *, boolean_t); int fmac_priv_proc_cred_perm(const cred_t *scr, cred_t *tcr, int mode); access_vector_t fmac_sigtoav(int sig); -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Fri Nov 14 07:08:13 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 14 Nov 2008 10:08:13 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <1226675293.9382.79.camel@moss-spartans.epoch.ncsc.mil> On Fri, 2008-11-14 at 09:05 -0500, Stephen Smalley wrote: > On Thu, 2008-11-13 at 23:44 -0800, John Weeks wrote: > > On 11/13/08 17:06, Glenn Faden wrote: > > > Stephen Smalley wrote: > > >> Take 3 of a patch showing how we can approach granting privileges in > > >> FMAC. This version takes Glenn's suggestion that the priv_* checks > > >> applied by fmac_vnode_priv_access() only be applied if the legacy policy > > >> would deny the privilege (i.e. to grant privileges that would normally > > >> be denied) similar to FGAP, while the normal read/write/execute checks > > >> applied by fmac_vnode_access() can be used restrictively. However, the > > >> priv_* checks are kept as object-based checks; this is necessary if we > > >> want to provide the same functionality as FGAP/klpd via FMAC. > > >> > > >> This patch also includes policy changes from John Weeks to > > >> allow /bin/passwd to run without being setuid root, specifically > > >> granting it priv_* permissions needed for > > >> accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This > > >> enables one to successfully run /bin/passwd as a non-root user even > > >> after removing the setuid bit from it. However, there is one > > >> "regression" in the behavior of /bin/passwd when run in this > > >> way; /etc/shadow is left with the ownership of the invoking user rather > > >> than root afterward since it is re-created on the transaction > > >> and /bin/passwd apparently doesn't explicitly preserve/set the owner in > > >> Solaris. Thus we would need to change /bin/passwd to preserve/set the > > >> ownership if we wanted to grant it privileges in this manner rather than > > >> via setuid. > > >> > > > This version seems to meet both the backward compatibility and MAC > > > functionality. I've tried it out using John Weeks' test system and got > > > some interesting results. Authoritative privileges are applied, and > > > legacy process privileges still work when consistent with the Type > > > Enforcement policy. > > > > > > There are still some confusing side effects. For example, using su (as > > > root) to switch to another user doesn't currently change the process > > > security context as newrole does. So the objects created by the new user > > > still have the old user's security context. Fixing this will avoid a > > > source of confusion. > > > > This was mentioned in the newrole patch. Please remember that this is > > an open source project and development happens incrementally. Would > > you like to work on the enhancements to su? We're always looking for > > contributors ;-) > > FWIW, John's original pam_unix_creds patch did switch contexts on su(1), > but I recommended against that and he changed it to only change contexts > in the PAM_ESTABLISH_CRED case. It could be changed back, but I should > note that we've gone back and forth on this issue in SELinux, originally > keeping su(1) and context changes entirely separate, then integrating > context changes into su (via pam_selinux) during the early Fedora > integration, and then separating them out again based both on the > problems encountered with integrating them and on the desires of the > MLS/LSPP crowd. > > Preserving caller context across su(1) (i.e. not changing the context > based on the new uid) has the advantages of: > 1) Ensuring that you can't break out of the restrictions imposed on the > FMAC user identity established at login time, providing strong > containment of the entire session by bounding the set of reachable roles > and levels. You can still change to other roles/levels if allowed by > policy via newrole, but the set of reachable roles/levels is bounded > based on the user identity in which you started the login session and > cannot be escalated beyond that set. > > 2) Avoiding undesirable context changes when only a uid change was > desired, as when launching an application in a pseudo uid. We > discovered that this is done by a number of application startup scripts > at least in Linux and was incorrectly trying to switch into a context > for the user rather than just preserving the base user/role/level and > only changing domain on application execution. > > 3) Not placing any trust in su(1) as far as FMAC is concerned. Thus a > flaw in su(1) does not represent a privilege escalation vulnerability > wrt to FMAC. > > However, given the Solaris RBAC model and the plan to map Solaris role > uids to FMAC roles, we seemingly have to change contexts upon su(1) at > least when the target uid is a role, and I'm not clear that su/pam > actually strongly distinguishes that case from any other su. I haven't > looked into that yet, so help there would be welcome. One further caveat: If we start to switch contexts upon su(1), then we also need to be able to control the ability to su(1) without a password to prevent a root user from being able to assume any other user's context. In Linux, there was a pam_rootok module for that purpose, and we instrumented it with a corresponding Flask permission check. I'd guess that this would be controlled by an authorization in Solaris, and we already know that we need to instrument the authorization checks with our own checks. The same issue applies for being able to change other user's passwords via passwd and the like; we will need to instrument the Solaris userland in the same manner, and help would be welcome there. Also, su policy has to change if it is used to change contexts; the existing su policy doesn't allow it to switch to another FMAC user, role, or level, and sets up domain transitions to preserve the caller's domain when executing the user's shell. -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Fri Nov 14 07:36:52 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 14 Nov 2008 10:36:52 -0500 Subject: [fmac-discuss] [PATCH] libc style cleanups Message-ID: <1226677012.9382.91.camel@moss-spartans.epoch.ncsc.mil> Clean up some cstyle -cpP violations introduced by earlier patches in libc. diff --git a/usr/src/lib/libc/inc/libc.h b/usr/src/lib/libc/inc/libc.h --- a/usr/src/lib/libc/inc/libc.h +++ b/usr/src/lib/libc/inc/libc.h @@ -72,7 +72,7 @@ extern void callout_lock_exit(void); extern void *lmalloc(size_t); extern void lfree(void *, size_t); -extern int libc_nvlist_alloc(nvlist_t **,uint_t, int); +extern int libc_nvlist_alloc(nvlist_t **, uint_t, int); extern void libc_nvlist_free(nvlist_t *); extern int libc_nvlist_lookup_uint64(nvlist_t *, const char *, uint64_t *); extern int libc_nvlist_lookup_string(nvlist_t *, const char *, char **); diff --git a/usr/src/lib/libc/port/gen/attrat.c b/usr/src/lib/libc/port/gen/attrat.c --- a/usr/src/lib/libc/port/gen/attrat.c +++ b/usr/src/lib/libc/port/gen/attrat.c @@ -82,7 +82,7 @@ (freer = dlsym(handle, "nvlist_free")) == NULL || (looker = dlsym(handle, "nvlist_lookup_uint64")) == NULL || (lookupstr = dlsym(handle, "nvlist_lookup_string")) - == NULL || + == NULL || (addstr = dlsym(handle, "nvlist_add_string")) == NULL) { if (handle) dlclose(handle); diff --git a/usr/src/lib/libc/port/sys/fmacsys.c b/usr/src/lib/libc/port/sys/fmacsys.c --- a/usr/src/lib/libc/port/sys/fmacsys.c +++ b/usr/src/lib/libc/port/sys/fmacsys.c @@ -99,7 +99,7 @@ return (-1); } - if (dcontext=strdup(acontext)) { + if (dcontext = strdup(acontext)) { *context = dcontext; return (0); } else @@ -123,7 +123,7 @@ return (-1); } - if (dcontext=strdup(acontext)) { + if (dcontext = strdup(acontext)) { *context = dcontext; return (0); } else @@ -152,7 +152,7 @@ return (0); } - if (dcontext=strdup(acontext)) { + if (dcontext = strdup(acontext)) { *context = dcontext; return (0); } else @@ -187,7 +187,7 @@ return (0); } - if (dcontext=strdup(acontext)) { + if (dcontext = strdup(acontext)) { *context = dcontext; return (0); } else -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Fri Nov 14 11:30:45 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Fri, 14 Nov 2008 11:30:45 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <491DD1E5.7070303@sun.com> Stephen Smalley wrote: > On Thu, 2008-11-13 at 23:44 -0800, John Weeks wrote: > >> On 11/13/08 17:06, Glenn Faden wrote: >> >>> Stephen Smalley wrote: >>> >>>> Take 3 of a patch showing how we can approach granting privileges in >>>> FMAC. This version takes Glenn's suggestion that the priv_* checks >>>> applied by fmac_vnode_priv_access() only be applied if the legacy policy >>>> would deny the privilege (i.e. to grant privileges that would normally >>>> be denied) similar to FGAP, while the normal read/write/execute checks >>>> applied by fmac_vnode_access() can be used restrictively. However, the >>>> priv_* checks are kept as object-based checks; this is necessary if we >>>> want to provide the same functionality as FGAP/klpd via FMAC. >>>> >>>> This patch also includes policy changes from John Weeks to >>>> allow /bin/passwd to run without being setuid root, specifically >>>> granting it priv_* permissions needed for >>>> accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This >>>> enables one to successfully run /bin/passwd as a non-root user even >>>> after removing the setuid bit from it. However, there is one >>>> "regression" in the behavior of /bin/passwd when run in this >>>> way; /etc/shadow is left with the ownership of the invoking user rather >>>> than root afterward since it is re-created on the transaction >>>> and /bin/passwd apparently doesn't explicitly preserve/set the owner in >>>> Solaris. Thus we would need to change /bin/passwd to preserve/set the >>>> ownership if we wanted to grant it privileges in this manner rather than >>>> via setuid. >>>> >>>> >>> This version seems to meet both the backward compatibility and MAC >>> functionality. I've tried it out using John Weeks' test system and got >>> some interesting results. Authoritative privileges are applied, and >>> legacy process privileges still work when consistent with the Type >>> Enforcement policy. >>> >>> There are still some confusing side effects. For example, using su (as >>> root) to switch to another user doesn't currently change the process >>> security context as newrole does. So the objects created by the new user >>> still have the old user's security context. Fixing this will avoid a >>> source of confusion. >>> >> This was mentioned in the newrole patch. Please remember that this is >> an open source project and development happens incrementally. Would >> you like to work on the enhancements to su? We're always looking for >> contributors ;-) >> > > FWIW, John's original pam_unix_creds patch did switch contexts on su(1), > but I recommended against that and he changed it to only change contexts > in the PAM_ESTABLISH_CRED case. It could be changed back, but I should > note that we've gone back and forth on this issue in SELinux, originally > keeping su(1) and context changes entirely separate, then integrating > context changes into su (via pam_selinux) during the early Fedora > integration, and then separating them out again based both on the > problems encountered with integrating them and on the desires of the > MLS/LSPP crowd. > > Preserving caller context across su(1) (i.e. not changing the context > based on the new uid) has the advantages of: > 1) Ensuring that you can't break out of the restrictions imposed on the > FMAC user identity established at login time, providing strong > containment of the entire session by bounding the set of reachable roles > and levels. You can still change to other roles/levels if allowed by > policy via newrole, but the set of reachable roles/levels is bounded > based on the user identity in which you started the login session and > cannot be escalated beyond that set. > > 2) Avoiding undesirable context changes when only a uid change was > desired, as when launching an application in a pseudo uid. We > discovered that this is done by a number of application startup scripts > at least in Linux and was incorrectly trying to switch into a context > for the user rather than just preserving the base user/role/level and > only changing domain on application execution. > > 3) Not placing any trust in su(1) as far as FMAC is concerned. Thus a > flaw in su(1) does not represent a privilege escalation vulnerability > wrt to FMAC. > > However, given the Solaris RBAC model and the plan to map Solaris role > uids to FMAC roles, we seemingly have to change contexts upon su(1) at > least when the target uid is a role, and I'm not clear that su/pam > actually strongly distinguishes that case from any other su. I haven't > looked into that yet, so help there would be welcome. > > Yes, I had role assumption in mind when I was suggesting that su to a role should change the context. I agree we don't want to change the context when switching to another normal user. I've made some proposals on how to merge the Solaris and FMAC notion of roles, but the details remain unresolved. >>> John's suggestion about running the passwd(1) program without requiring >>> setuid-to-root, seems to break backward compatibility because it fails >>> to preserve DAC attributes of root objects. Although the passwd(1) >>> program could be modified to explicitly set the owner of the new >>> /etc/shadow file, it is not a generic solution for similar situations. >>> In Trusted Solaris 8 where we had forced privileges on executables, we >>> didn't treat setuid-to-root as granting all privileges. But we ran into >>> problems because (sometimes) it wasn't backward compatible enough with >>> traditional UNIX. >>> >> This is a demonstration of what can be done with FMAC. With the cyber >> threats we're faced with, I'll take security over compatibility any >> day. Don't take me wrong, I really DO want to maintain compatibility, >> but sometimes we need to make hard choices. >> > > I'm not clear on why changing passwd to explicitly set ownership on the > files it creates isn't a generic solution or just plain good practice > anyway. I believe that in Linux, the library functions for manipulating > passwd and shadow are careful to save and restore the ownership and mode > information whenever they act on those files. > In order for passwd(1) to set the ownership it will need some additional authoritative permission (file:priv_chown ?) if it isn't already running as root. I have two points here: 1. Requiring such changes to existing applications worries me because it may be just the tip of the iceberg with respect to breaking compatibility. John's comment about security being more important than compatibility implies that we can't have both, but we can in this case. My experience has taught me that binary compatibility is necessary for people to deploy new security solutions. Whenever we know how to preserve compatibility we should do so. 2. The notion that suid to root grants all privileges to a process is not an area where complete backward compatibility is required. Note that UNIX already qualifies this behavior with the "nosuid" mount option. While removing unnecessary setuid programs from Solaris is good idea, we can also restrict the behavior of setuid on a per-domain basis, so we should. > Regardless, we will have to be careful in general about converting > setuid root programs to using this mechanism for privilege granting, > particularly with regard to setuid root programs that currently do > privilege bracketing via seteuid(), so I expect some application changes > to sometimes be required when performing such a conversion. Note that > fmac_vnode_priv_access() is careful to not grant a privilege for > privilege aware processes and to not exceed the limit set, but this > doesn't cover the case where an application is doing privilege > bracketing via seteuid() IIUC. > Agreed. > >>> I propose having a new FMAC permission, e.g, priv_escalate, which would >>> be required for an exec'ed process to get the current behavior of >>> escalating its permitted and effective privileges to the limit set when >>> the setuid-to-root bit is set. For specialized programs like passwd(1) >>> with their own domain, they wouldn't be granted the priv_escalate >>> permission. So the passwd(1) program would still be be suid-to-root, but >>> would just run with the process privileges of its parent. >>> >> I do favor constraining the subject to only what it needs, and in this >> case reducing unnecessary P/E privileges would be desirable. Steve, >> what do you think? >> > > As I understand it, it is already the case that the limit set will not > be used as the observed effective/permitted sets if the process is > privilege aware or if the uid is non-zero. Thus, by removing the setuid > bit from passwd (which I think is the right approach, fixing the passwd > program as needed to preserve ownership on files it re-creates during a > transaction), we already get the desired behavior, or by making an > application privilege aware, we also get the desired behavior. I > wouldn't add yet another condition to the logic which decides what to > use as the observed effective/permitted set. > My suggestion only applies to the special case of privilege escalation when executing a setuid-to-root binary, and wouldn't affect functions like secpolicy_vnode_access(). > In terms of restricting privilege use based on FMAC policy, if we want > to support that, then I'd favor adding yet another set of checks for > that purpose, either at object granularity (priv_read_limit, > priv_write_limit) checked from a hook called by secpolicy_vnode_access() > when the privilege is allowed by the legacy privilege logic or at just a > subject granularity (file_dac_read, file_dac_write, ... in a new > privilege class) checked from a hook called by the priv_policy* > functions. But understand what that means for compatibility - it means > that we must label every setuid root program with an entrypoint type to > a domain that is allowed the desired privileges (and the same would be > true of this priv_escalate suggestion, just with less granularity), > including legacy setuid root programs, third party setuid root programs, > and most problematically, setuid root programs on filesystems that do > not support labeling would no longer work unless called from a domain > that was already allowed the privileges in the first place. > I don't think we need that level of granularity(priv_read_limit, priv_write_limit) or any additional checking in secpolicy_vnode_access(). > >>> For backward compatibility, for applications that really need to run >>> with all privileges (maybe the su(1M) command itself?), we could have an >>> unconfined domain that includes the priv_escalate permission. >>> >> Or you create a new domain for the application and allow process:priv_escalate. BTW - su already has its own domain. >> > > True, but understand the compatibility implications of this for third > party setuid root programs and for setuid root program execution on > filesystems that do not support labeling. > This is a case where compatibility vs. security would be configurable based on the policy. As I said before, we already have mount options to deal with interpreting suid on a filesystem basis. NFS presents the biggest problem in this area since it may be impossible to get remote security contexts. --Glenn > > From sds at tycho.nsa.gov Fri Nov 14 13:28:53 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 14 Nov 2008 16:28:53 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <491DD1E5.7070303@sun.com> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> <491DD1E5.7070303@sun.com> Message-ID: <1226698133.9382.165.camel@moss-spartans.epoch.ncsc.mil> On Fri, 2008-11-14 at 11:30 -0800, Glenn Faden wrote: > In order for passwd(1) to set the ownership it will need some additional > authoritative permission (file:priv_chown ?) if it isn't already running > as root. I have two points here: > > 1. Requiring such changes to existing applications worries me because it > may be just the tip of the iceberg with respect to breaking > compatibility. John's comment about security being more important than > compatibility implies that we can't have both, but we can in this case. > My experience has taught me that binary compatibility is necessary for > people to deploy new security solutions. Whenever we know how to > preserve compatibility we should do so. > > 2. The notion that suid to root grants all privileges to a process is > not an area where complete backward compatibility is required. Note that > UNIX already qualifies this behavior with the "nosuid" mount option. > While removing unnecessary setuid programs from Solaris is good idea, we > can also restrict the behavior of setuid on a per-domain basis, so we > should. I think I've lost the plot here. If we intend to keep passwd setuid root, then we don't need to grant privileges via FMAC - we can just use the setuid root mechanism for privilege granting, and then use FMAC to restrict the use of privileges just as we restrict the use of capabilities in SELinux. That's exactly how passwd works in SELinux - it remains setuid root, its effective capability (privilege) set is escalated upon exec by virtue of being setuid root, and SELinux applies a restrictive check on any attempted uses of capabilities (privileges) such that it can only use those privileges authorized by policy. This example then becomes a motivation for having restrictive privilege checks in FMAC, not for privilege granting via FMAC. > > As I understand it, it is already the case that the limit set will not > > be used as the observed effective/permitted sets if the process is > > privilege aware or if the uid is non-zero. Thus, by removing the setuid > > bit from passwd (which I think is the right approach, fixing the passwd > > program as needed to preserve ownership on files it re-creates during a > > transaction), we already get the desired behavior, or by making an > > application privilege aware, we also get the desired behavior. I > > wouldn't add yet another condition to the logic which decides what to > > use as the observed effective/permitted set. > > > My suggestion only applies to the special case of privilege escalation > when executing a setuid-to-root binary, and wouldn't affect functions > like secpolicy_vnode_access(). The privilege escalation doesn't really happen at exec time. It happens when the privilege is checked, when the CR_OEPRIV() macro dynamically selects which privilege set to use based on the PRIV_EISAWARE() macro. If the process is privilege aware or has a non-root uid, then the actual effective privilege set is used as the "observed" effective set. Otherwise, the limit set privilege set is used as the "observed" effective set. The actual sets don't get mutated at exec time. So I'm not entirely sure what you are proposing. We could apply some kind of permission check at exec time and use it to forcibly set the privilege awareness flag on a process, which would force it to always use the actual effective set at privilege check time. That would seemingly have the desired effect. But as above, I don't quite see the motivation for this vs. a restrictive check approach to this same problem, and it is a weaker guarantee than we get in SELinux from the restrictive checks, where we know for certain that a domain can only ever use a given privilege if it was allowed by SELinux policy. -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Fri Nov 14 14:05:05 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Fri, 14 Nov 2008 14:05:05 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226698133.9382.165.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> <491DD1E5.7070303@sun.com> <1226698133.9382.165.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <491DF611.5020101@sun.com> Stephen Smalley wrote: > On Fri, 2008-11-14 at 11:30 -0800, Glenn Faden wrote: > >> In order for passwd(1) to set the ownership it will need some additional >> authoritative permission (file:priv_chown ?) if it isn't already running >> as root. I have two points here: >> >> 1. Requiring such changes to existing applications worries me because it >> may be just the tip of the iceberg with respect to breaking >> compatibility. John's comment about security being more important than >> compatibility implies that we can't have both, but we can in this case. >> My experience has taught me that binary compatibility is necessary for >> people to deploy new security solutions. Whenever we know how to >> preserve compatibility we should do so. >> >> 2. The notion that suid to root grants all privileges to a process is >> not an area where complete backward compatibility is required. Note that >> UNIX already qualifies this behavior with the "nosuid" mount option. >> While removing unnecessary setuid programs from Solaris is good idea, we >> can also restrict the behavior of setuid on a per-domain basis, so we >> should. >> > > I think I've lost the plot here. If we intend to keep passwd setuid > root, then we don't need to grant privileges via FMAC - we can just use > the setuid root mechanism for privilege granting, and then use FMAC to > restrict the use of privileges just as we restrict the use of > capabilities in SELinux. That's exactly how passwd works in SELinux - > it remains setuid root, its effective capability (privilege) set is > escalated upon exec by virtue of being setuid root, and SELinux applies > a restrictive check on any attempted uses of capabilities (privileges) > such that it can only use those privileges authorized by policy. This > example then becomes a motivation for having restrictive privilege > checks in FMAC, not for privilege granting via FMAC. > Actually, I like the idea of removing setuid from passwd, in particular, since it is often used as an example of how Type Enforcement enhances security. I was just on a soapbox about trying to avoid requiring changes to existing programs, in general. My real motivation for making FMAC authoritative is to eliminate the need for administrative role processes to run with UNIX uid of root, as they apparently do in SELinux. However, the issue of applying the proper root ownership when creating new files does complicate this in a few cases. >>> >>> >> My suggestion only applies to the special case of privilege escalation >> when executing a setuid-to-root binary, and wouldn't affect functions >> like secpolicy_vnode_access(). >> > > The privilege escalation doesn't really happen at exec time. It happens > when the privilege is checked, when the CR_OEPRIV() macro dynamically > selects which privilege set to use based on the PRIV_EISAWARE() macro. > If the process is privilege aware or has a non-root uid, then the actual > effective privilege set is used as the "observed" effective set. > Otherwise, the limit set privilege set is used as the "observed" > effective set. The actual sets don't get mutated at exec time. So I'm > not entirely sure what you are proposing. > I forgot about how the macro works, so this is a bit more complex than I thought. :-[ > We could apply some kind of permission check at exec time and use it to > forcibly set the privilege awareness flag on a process, which would > force it to always use the actual effective set at privilege check time. > That would seemingly have the desired effect. I don't think the PRIV_AWARE flag has the correct semantics. Probably we would need another process flag, e.g. PRIV_CAN_ESCALATE, which would be set at exec based on whether the FMAC permission priv_escalate is associated with domain. Then the CR_OEPRIV() macro would need to check this additional process flag. > But as above, I don't > quite see the motivation for this vs. a restrictive check approach to > this same problem, and it is a weaker guarantee than we get in SELinux > from the restrictive checks, where we know for certain that a domain can > only ever use a given privilege if it was allowed by SELinux policy. > I never really liked the approach in Solaris 10 of setuid-to-root conveying all privileges, and I thought that making it configurable per domain would enhance security. --Glenn From john.weeks at sun.com Mon Nov 17 07:03:51 2008 From: john.weeks at sun.com (John Weeks) Date: Mon, 17 Nov 2008 07:03:51 -0800 Subject: [fmac-discuss] [PATCH] libc style cleanups In-Reply-To: <1226677012.9382.91.camel@moss-spartans.epoch.ncsc.mil> References: <1226677012.9382.91.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492187D7.9010506@sun.com> On 11/14/08 07:36, Stephen Smalley wrote: > Clean up some cstyle -cpP violations introduced by earlier patches in > libc. Acked-by: John Weeks > > diff --git a/usr/src/lib/libc/inc/libc.h b/usr/src/lib/libc/inc/libc.h > --- a/usr/src/lib/libc/inc/libc.h > +++ b/usr/src/lib/libc/inc/libc.h > @@ -72,7 +72,7 @@ > extern void callout_lock_exit(void); > extern void *lmalloc(size_t); > extern void lfree(void *, size_t); > -extern int libc_nvlist_alloc(nvlist_t **,uint_t, int); > +extern int libc_nvlist_alloc(nvlist_t **, uint_t, int); > extern void libc_nvlist_free(nvlist_t *); > extern int libc_nvlist_lookup_uint64(nvlist_t *, const char *, uint64_t *); > extern int libc_nvlist_lookup_string(nvlist_t *, const char *, char **); > diff --git a/usr/src/lib/libc/port/gen/attrat.c b/usr/src/lib/libc/port/gen/attrat.c > --- a/usr/src/lib/libc/port/gen/attrat.c > +++ b/usr/src/lib/libc/port/gen/attrat.c > @@ -82,7 +82,7 @@ > (freer = dlsym(handle, "nvlist_free")) == NULL || > (looker = dlsym(handle, "nvlist_lookup_uint64")) == NULL || > (lookupstr = dlsym(handle, "nvlist_lookup_string")) > - == NULL || > + == NULL || > (addstr = dlsym(handle, "nvlist_add_string")) == NULL) { > if (handle) > dlclose(handle); > diff --git a/usr/src/lib/libc/port/sys/fmacsys.c b/usr/src/lib/libc/port/sys/fmacsys.c > --- a/usr/src/lib/libc/port/sys/fmacsys.c > +++ b/usr/src/lib/libc/port/sys/fmacsys.c > @@ -99,7 +99,7 @@ > return (-1); > } > > - if (dcontext=strdup(acontext)) { > + if (dcontext = strdup(acontext)) { > *context = dcontext; > return (0); > } else > @@ -123,7 +123,7 @@ > return (-1); > } > > - if (dcontext=strdup(acontext)) { > + if (dcontext = strdup(acontext)) { > *context = dcontext; > return (0); > } else > @@ -152,7 +152,7 @@ > return (0); > } > > - if (dcontext=strdup(acontext)) { > + if (dcontext = strdup(acontext)) { > *context = dcontext; > return (0); > } else > @@ -187,7 +187,7 @@ > return (0); > } > > - if (dcontext=strdup(acontext)) { > + if (dcontext = strdup(acontext)) { > *context = dcontext; > return (0); > } else > From sds at tycho.nsa.gov Mon Nov 17 13:26:56 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Mon, 17 Nov 2008 16:26:56 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <491DF611.5020101@sun.com> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> <491DD1E5.7070303@sun.com> <1226698133.9382.165.camel@moss-spartans.epoch.ncsc.mil> <491DF611.5020101@sun.com> Message-ID: <1226957216.25156.163.camel@moss-spartans.epoch.ncsc.mil> On Fri, 2008-11-14 at 14:05 -0800, Glenn Faden wrote: > I don't think the PRIV_AWARE flag has the correct semantics. Probably > we would need another process flag, e.g. PRIV_CAN_ESCALATE, which would > be set at exec based on whether the FMAC permission priv_escalate is > associated with domain. Then the CR_OEPRIV() macro would need to check > this additional process flag. I've experimented with this idea, patch below relative to the prior ones. Unfortunately this yields an unbootable system in enforcing mode, although you can boot in permissive, login, switch to enforcing, and try running e.g. /bin/passwd or /bin/su to see the effect (success for the former by virtue of the FMAC priv_* checks; failure for the latter due to present lack of policy allowing priv_* permissions to the domain for su; no privilege escalation via setuid root in either case). I don't believe however that this is the best approach, for the following reasons: 1) There is a fundamental mismatch between this notion of controlling the ability to escalate privileges upon setuid root and the Solaris model of "observed" effective and permitted sets determined at privilege check time. Even the initial credential created by cred_init() relies on this behavior, only including basic privileges in the actual effective/permitted sets, such that if the initial credential lacks PRIV_ESCALATE, the system won't even startup. This is also true of all non-privilege-aware uid 0 system processes, which likewise would require priv_escalate for their domains just to continue to be checked against their limit sets. Thus we have to allow priv_escalate for far more than just setuid root programs, and tracking what programs can potentially ever be executed with priv_escalate becomes largely impossible (keeping in mind that system scripts will execute many helper programs without ever changing domains). 2) The priv_escalate model is an all-or-nothing model rather than providing any kind of granularity in assigning privileges. > I never really liked the approach in Solaris 10 of setuid-to-root > conveying all privileges, and I thought that making it configurable per > domain would enhance security. I agree with that aim, but I think we can do a better job of that by way of adding restrictive checks on privileges, whether at coarse-granularity (e.g. subject-only file_dac_read check) or at fine granularity (e.g. object-based priv_read_limit check). In any event, just for reference, below is the patch I tried on top of my prior ones. My next step though is to try a patch for adding restrictive privilege checks to the prior ones. diff --git a/usr/src/cmd/fmac/policy/domains/every.te b/usr/src/cmd/fmac/policy/domains/every.te --- a/usr/src/cmd/fmac/policy/domains/every.te +++ b/usr/src/cmd/fmac/policy/domains/every.te @@ -35,7 +35,7 @@ # # Access other processes in the same domain. -allow domain self:process *; +allow domain self:process ~privescalate; # Access file descriptions, pipes, and sockets # created by processes in the same domain. diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -266,6 +266,7 @@ setexec setfscreate noprivinc + privescalate } diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -556,7 +556,8 @@ int fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, - boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp) + boolean_t *setprivinc, boolean_t *privescalate, + security_id_t *prev_secidp, security_id_t *secidp) { security_id_t prev_secid, secid; int error; @@ -607,6 +608,13 @@ *setprivinc = B_TRUE; else *setprivinc = B_FALSE; + + error = avc_has_perm(secid, secid, SECCLASS_PROCESS, + PROCESS__PRIVESCALATE, &ad); + if (error == 0) + *privescalate = B_TRUE; + else + *privescalate = B_FALSE; *setsecid = B_TRUE; *prev_secidp = prev_secid; diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c --- a/usr/src/uts/common/os/cred.c +++ b/usr/src/uts/common/os/cred.c @@ -229,7 +229,7 @@ CR_EPRIV(kcred) = CR_PPRIV(kcred) = CR_IPRIV(kcred); - CR_FLAGS(kcred) = NET_MAC_AWARE; + CR_FLAGS(kcred) = NET_MAC_AWARE | PRIV_ESCALATE; kcred->cr_secid = SECINITSID_KERNEL; diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c --- a/usr/src/uts/common/os/exec.c +++ b/usr/src/uts/common/os/exec.c @@ -525,6 +525,7 @@ uid_t uid, gid; security_id_t prev_secid, secid; boolean_t setsecid = B_FALSE, setprivinc = B_FALSE; + boolean_t privescalate = B_FALSE; struct vattr vattr; char magbuf[MAGIC_BYTES]; int setid; @@ -569,7 +570,7 @@ privflags = execsetid(vp, &vattr, &uid, &gid); error = fmac_exec(CRED(), vp, &setsecid, &setprivinc, - &prev_secid, &secid); + &privescalate, &prev_secid, &secid); if (error) goto bad; @@ -587,6 +588,10 @@ if (setsecid) { cred->cr_secid = secid; cred->cr_exec_secid = SECSID_NULL; + if (privescalate) + CR_FLAGS(cred) |= PRIV_ESCALATE; + else + CR_FLAGS(cred) &= ~PRIV_ESCALATE; } /* If we can, drop the PA bit */ diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -99,7 +99,8 @@ cred_t *cr); int fmac_vnode_setattr(vnode_t *, cred_t *); int fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, - boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp); + boolean_t *setprivinc, boolean_t *privescalate, + security_id_t *prev_secidp, security_id_t *secidp); int fmac_vnode_access(vnode_t *, int, int, cred_t *, boolean_t); int fmac_priv_proc_cred_perm(const cred_t *scr, cred_t *tcr, int mode); access_vector_t fmac_sigtoav(int sig); diff --git a/usr/src/uts/common/sys/priv.h b/usr/src/uts/common/sys/priv.h --- a/usr/src/uts/common/sys/priv.h +++ b/usr/src/uts/common/sys/priv.h @@ -138,6 +138,7 @@ #define NET_MAC_AWARE 0x0010 /* Is MAC aware */ #define NET_MAC_AWARE_INHERIT 0x0020 /* Inherit MAC aware */ #define PRIV_XPOLICY 0x0080 /* Extended policy */ +#define PRIV_ESCALATE 0x0100 /* Escalate on uid 0 */ /* user-settable flags: */ #define PRIV_USER (PRIV_DEBUG | NET_MAC_AWARE | NET_MAC_AWARE_INHERIT |\ diff --git a/usr/src/uts/common/sys/priv_impl.h b/usr/src/uts/common/sys/priv_impl.h --- a/usr/src/uts/common/sys/priv_impl.h +++ b/usr/src/uts/common/sys/priv_impl.h @@ -69,10 +69,12 @@ #define PRIV_SETBYTES (PRIV_NSET * PRIV_SETSIZE * sizeof (priv_chunk_t)) -#define PRIV_EISAWARE(c) ((CR_FLAGS(c) & PRIV_AWARE) || (c)->cr_uid != 0) +#define PRIV_EISAWARE(c) ((CR_FLAGS(c) & PRIV_AWARE) || (c)->cr_uid != 0 || \ + !(CR_FLAGS(c) & PRIV_ESCALATE)) #define PRIV_PISAWARE(c) ((CR_FLAGS(c) & PRIV_AWARE) || \ ((c)->cr_uid != 0 && (c)->cr_suid != 0 && \ - (c)->cr_ruid != 0)) + (c)->cr_ruid != 0) || \ + !(CR_FLAGS(c) & PRIV_ESCALATE)) #define CR_OEPRIV(c) (*(PRIV_EISAWARE(c) ? &CR_EPRIV(c) : &CR_LPRIV(c))) #define CR_OPPRIV(c) (*(PRIV_PISAWARE(c) ? &CR_PPRIV(c) : &CR_LPRIV(c))) -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Mon Nov 17 14:18:26 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Mon, 17 Nov 2008 14:18:26 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226957216.25156.163.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <491CCF10.9030806@sun.com> <491D2C6E.9010003@sun.com> <1226671550.9382.53.camel@moss-spartans.epoch.ncsc.mil> <491DD1E5.7070303@sun.com> <1226698133.9382.165.camel@moss-spartans.epoch.ncsc.mil> <491DF611.5020101@sun.com> <1226957216.25156.163.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4921EDB2.6020508@sun.com> Stephen Smalley wrote: > On Fri, 2008-11-14 at 14:05 -0800, Glenn Faden wrote: > >> I don't think the PRIV_AWARE flag has the correct semantics. Probably >> we would need another process flag, e.g. PRIV_CAN_ESCALATE, which would >> be set at exec based on whether the FMAC permission priv_escalate is >> associated with domain. Then the CR_OEPRIV() macro would need to check >> this additional process flag. >> > > I've experimented with this idea, patch below relative to the prior > ones. Unfortunately this yields an unbootable system in enforcing mode, > although you can boot in permissive, login, switch to enforcing, and try > running e.g. /bin/passwd or /bin/su to see the effect (success for the > former by virtue of the FMAC priv_* checks; failure for the latter due > to present lack of policy allowing priv_* permissions to the domain for > su; no privilege escalation via setuid root in either case). I don't > believe however that this is the best approach, for the following > reasons: > > 1) There is a fundamental mismatch between this notion of controlling > the ability to escalate privileges upon setuid root and the Solaris > model of "observed" effective and permitted sets determined at privilege > check time. Even the initial credential created by cred_init() relies > on this behavior, only including basic privileges in the actual > effective/permitted sets, such that if the initial credential lacks > PRIV_ESCALATE, the system won't even startup. This is also true of all > non-privilege-aware uid 0 system processes, which likewise would require > priv_escalate for their domains just to continue to be checked against > their limit sets. Thus we have to allow priv_escalate for far more than > just setuid root programs, and tracking what programs can potentially > ever be executed with priv_escalate becomes largely impossible (keeping > in mind that system scripts will execute many helper programs without > ever changing domains). > > 2) The priv_escalate model is an all-or-nothing model rather than > providing any kind of granularity in assigning privileges. > > >> I never really liked the approach in Solaris 10 of setuid-to-root >> conveying all privileges, and I thought that making it configurable per >> domain would enhance security. >> > > I agree with that aim, but I think we can do a better job of that by way > of adding restrictive checks on privileges, whether at > coarse-granularity (e.g. subject-only file_dac_read check) or at fine > granularity (e.g. object-based priv_read_limit check). > > In any event, just for reference, below is the patch I tried on top of > my prior ones. My next step though is to try a patch for adding > restrictive privilege checks to the prior ones. > > > Stephen, Thanks for making the effort to prototype my suggestion. I agree with your conclusions. In particular, I had not initially realized that this change would affect root applications that were not themselves setuid. Based on your observations, I agree that this is not a practical approach, and that restricting effective privileges via FMAC permissions is a more practical solution. --Glenn From john.weeks at sun.com Tue Nov 18 08:17:23 2008 From: john.weeks at sun.com (John Weeks) Date: Tue, 18 Nov 2008 08:17:23 -0800 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4922EA93.7010606@sun.com> On 11/13/08 05:44, Stephen Smalley wrote: > Take 3 of a patch showing how we can approach granting privileges in > FMAC. This version takes Glenn's suggestion that the priv_* checks > applied by fmac_vnode_priv_access() only be applied if the legacy policy > would deny the privilege (i.e. to grant privileges that would normally > be denied) similar to FGAP, while the normal read/write/execute checks > applied by fmac_vnode_access() can be used restrictively. However, the > priv_* checks are kept as object-based checks; this is necessary if we > want to provide the same functionality as FGAP/klpd via FMAC. > > This patch also includes policy changes from John Weeks to > allow /bin/passwd to run without being setuid root, specifically > granting it priv_* permissions needed for > accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This > enables one to successfully run /bin/passwd as a non-root user even > after removing the setuid bit from it. However, there is one > "regression" in the behavior of /bin/passwd when run in this > way; /etc/shadow is left with the ownership of the invoking user rather > than root afterward since it is re-created on the transaction > and /bin/passwd apparently doesn't explicitly preserve/set the owner in > Solaris. Thus we would need to change /bin/passwd to preserve/set the > ownership if we wanted to grant it privileges in this manner rather than > via setuid. > > The overall permission checking logic after this patch is: > 1) Check DAC permissions to the file. > Denied? Go to 2. > Granted? Go to 5. > 2) Check file DAC privileges. > Denied? Go to 3. > Granted? Go to 5. > 3) If using klpd, call it to check the privilege to the file. > No klpd or Denied? Go to 4. > Granted? Go to 5. > 4) Check FMAC priv_* permissions to the file. > Denied? Done - Permission denied. > Granted? Go to 5. > 5) Check FMAC read/write/execute to the file. > Denied? Done - Permission denied. > Granted? Done - Permission granted. > > Webrev is available at: http://cr.opensolaris.org/~sds/filedacpriv/ > Acked-by: John Weeks > diff --git a/usr/src/cmd/fmac/policy/domains/program/passwd.te b/usr/src/cmd/fmac/policy/domains/program/passwd.te > --- a/usr/src/cmd/fmac/policy/domains/program/passwd.te > +++ b/usr/src/cmd/fmac/policy/domains/program/passwd.te > @@ -44,14 +44,18 @@ > allow passwd_t local_login_t:fd inherit_fd_perms; > allow passwd_t remote_login_t:fd inherit_fd_perms; > > -# Update /etc/passwd. > +# Update /etc/passwd and /etc/.pwd.lock. > allow passwd_t etc_t:dir rw_dir_perms; > +allow passwd_t etc_t:dir priv_write; > allow passwd_t etc_t:file create_file_perms; > +allow passwd_t etc_t:file priv_write; > > # Update /etc/shadow. > allow passwd_t shadow_t:file create_file_perms; > +allow passwd_t shadow_t:file { priv_read priv_write }; > file_type_auto_trans(passwd_t, etc_t, shadow_t) > > # Update lastlog. > allow passwd_t var_log_t:dir rw_dir_perms; > allow passwd_t var_log_t:file create_file_perms; > +allow passwd_t var_log_t:file priv_write; > diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te > --- a/usr/src/cmd/fmac/policy/macros.te > +++ b/usr/src/cmd/fmac/policy/macros.te > @@ -467,7 +467,7 @@ > > define(`unconfined_domain', ` > allow $1 domain:process *; > -allow $1 file_type:dir_file_class_set *; > -allow $1 unlabeled_t:dir_file_class_set *; > +allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; > +allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; > allow $1 security_t:security *; > ') > diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors > --- a/usr/src/common/fmac/policy/flask/access_vectors > +++ b/usr/src/common/fmac/policy/flask/access_vectors > @@ -40,6 +40,9 @@ > write > read > append > + priv_execute > + priv_write > + priv_read > open > create > link > @@ -130,6 +133,7 @@ > remove_name > reparent > search > + priv_search > rmdir > mounton > mountassociate > diff --git a/usr/src/uts/common/fmac/avc.c b/usr/src/uts/common/fmac/avc.c > --- a/usr/src/uts/common/fmac/avc.c > +++ b/usr/src/uts/common/fmac/avc.c > @@ -920,9 +920,10 @@ > return (0); > } > > -int > -avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, > - access_vector_t requested, avc_audit_data_t *auditdata) > +static int > +avc_has_perm_common(security_id_t ssid, security_id_t tsid, > + security_class_t tclass, access_vector_t requested, > + avc_audit_data_t *auditdata, boolean_t strict) > { > struct av_decision avd; > access_vector_t denied; > @@ -940,7 +941,7 @@ > if (denied & avd.auditdeny) > avc_audit(ssid, tsid, tclass, denied, AVC_AUDITDENY, > auditdata); > - if (fmac_enforcing) { > + if (strict || fmac_enforcing) { > return (EACCES); > } else { > (void) avc_update_cache(AVC_CALLBACK_GRANT, ssid, tsid, > @@ -955,3 +956,20 @@ > > return (0); > } > + > +int > +avc_has_perm(security_id_t ssid, security_id_t tsid, security_class_t tclass, > + access_vector_t requested, avc_audit_data_t *auditdata) > +{ > + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, > + B_FALSE); > +} > + > +int > +avc_has_perm_strict(security_id_t ssid, security_id_t tsid, > + security_class_t tclass, access_vector_t requested, > + avc_audit_data_t *auditdata) > +{ > + return avc_has_perm_common(ssid, tsid, tclass, requested, auditdata, > + B_TRUE); > +} > diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c > --- a/usr/src/uts/common/fmac/fmac.c > +++ b/usr/src/uts/common/fmac/fmac.c > @@ -39,6 +39,7 @@ > #include > #include > #include > +#include > #include > > /* Tunables */ > @@ -240,7 +241,7 @@ > if (!fmac_enabled) > return (EINVAL); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > sclass = fmac_vtype_to_sclass(vtype); > if (!sclass) > @@ -339,7 +340,7 @@ > if (!sclass) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > error = security_transition_sid(cr_secid, dvp->v_secid, sclass, > &secid); > @@ -423,7 +424,7 @@ > if (!sclass) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > AVC_AUDIT_DATA_INIT(&ad, FS); > ad.u.fs.vp = tdvp; > @@ -454,7 +455,7 @@ > if (!sclass) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > AVC_AUDIT_DATA_INIT(&ad, FS); > ad.u.fs.vp = dvp; > @@ -489,7 +490,7 @@ > if (!sclass) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > AVC_AUDIT_DATA_INIT(&ad, FS); > > @@ -545,7 +546,7 @@ > if (!sclass) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > AVC_AUDIT_DATA_INIT(&ad, FS); > ad.u.fs.vp = vp; > @@ -564,14 +565,17 @@ > if (!fmac_enabled) > return (0); > > - prev_secid = crgetsecid(cr); > - secid = crgetexecsecid(cr); > + prev_secid = cr->cr_secid; > + secid = cr->cr_exec_secid; > if (!secid) { > error = security_transition_sid(prev_secid, vp->v_secid, > SECCLASS_PROCESS, &secid); > if (error) > return (error); > } > + > + if (vp->v_vfsp->vfs_flag & VFS_NOSETUID) > + secid = prev_secid; > > AVC_AUDIT_DATA_INIT(&ad, FS); > ad.u.fs.vp = vp; > @@ -633,7 +637,7 @@ > if (!fmac_enabled) > return (0); > > - cr_secid = crgetsecid(cr); > + cr_secid = cr->cr_secid; > > sclass = fmac_vtype_to_sclass(vp->v_type); > if (!sclass) > @@ -697,7 +701,7 @@ > > if (!fmac_enabled) > return (0); > - return (avc_has_perm(crgetsecid((cred_t *)scr), crgetsecid(tcr), > + return (avc_has_perm(scr->cr_secid, tcr->cr_secid, > SECCLASS_PROCESS, PROCESS__PTRACE, NULL)); > } > > @@ -724,7 +728,65 @@ > if (!fmac_enabled) > return (0); > > - tsecid = crgetsecid((cred_t *)tcrp); > - ssecid = crgetsecid((cred_t *)scrp); > + tsecid = tcrp->cr_secid; > + ssecid = scrp->cr_secid; > return (avc_has_perm(ssecid, tsecid, SECCLASS_PROCESS, perms, NULL)); > } > + > +int > +fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) > +{ > + security_id_t cr_secid; > + security_class_t sclass; > + access_vector_t av; > + avc_audit_data_t ad; > + priv_set_t privs; > + > + /* If not enabled, just return the legacy policy decision. */ > + if (!fmac_enabled) > + return (err); > + > + /* > + * If privilege aware, then honor any legacy policy denial > + * since the program may have reduced its own privileges. > + */ > + if (err && (CR_FLAGS(cr) & PRIV_AWARE)) > + return (err); > + > + cr_secid = cr->cr_secid; > + > + sclass = fmac_vtype_to_sclass(vp->v_type); > + if (!sclass) > + return (err); > + > + av = 0; > + priv_emptyset(&privs); > + if (mode & VREAD) { > + av |= FILE__PRIV_READ; > + priv_addset(&privs, PRIV_FILE_DAC_READ); > + } > + if (mode & VWRITE) { > + av |= FILE__PRIV_WRITE; > + priv_addset(&privs, PRIV_FILE_DAC_WRITE); > + } > + > + if (mode & VEXEC) { > + if (sclass == SECCLASS_DIR) { > + av |= DIR__PRIV_SEARCH; > + priv_addset(&privs, PRIV_FILE_DAC_SEARCH); > + } else { > + av |= FILE__PRIV_EXECUTE; > + priv_addset(&privs, PRIV_FILE_DAC_EXECUTE); > + } > + } > + > + if (!av) > + return (err); > + > + if (err && !priv_issubset(&privs, &CR_LPRIV(cr))) > + return (err); > + > + AVC_AUDIT_DATA_INIT(&ad, FS); > + ad.u.fs.vp = vp; > + return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); > +} > diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c > --- a/usr/src/uts/common/os/exec.c > +++ b/usr/src/uts/common/os/exec.c > @@ -725,7 +725,7 @@ > if (cred->cr_ruid != cred->cr_uid || > (cred->cr_rgid != cred->cr_gid && > !supgroupmember(cred->cr_gid, cred)) || > - (privflags & PRIV_INCREASE) != 0) > + (privflags & PRIV_INCREASE) != 0 || fmac_execsetid) > suidflags = PSUIDFLAGS; > else > suidflags = 0; > diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c > --- a/usr/src/uts/common/os/policy.c > +++ b/usr/src/uts/common/os/policy.c > @@ -834,13 +834,15 @@ > int > secpolicy_vnode_access(const cred_t *cr, vnode_t *vp, uid_t owner, mode_t mode) > { > + int err = 0; > + > if ((mode & VREAD) && priv_policy_va(cr, PRIV_FILE_DAC_READ, B_FALSE, > EACCES, NULL, KLPDARG_VNODE, vp, (char *)NULL, > KLPDARG_NOMORE) != 0) { > - return (EACCES); > + err = EACCES; > } > > - if (mode & VWRITE) { > + if (!err && (mode & VWRITE)) { > boolean_t allzone; > > if (owner == 0 && cr->cr_uid != 0) > @@ -850,21 +852,25 @@ > if (priv_policy_va(cr, PRIV_FILE_DAC_WRITE, allzone, EACCES, > NULL, KLPDARG_VNODE, vp, (char *)NULL, > KLPDARG_NOMORE) != 0) { > - return (EACCES); > + err = EACCES; > } > } > > - if (mode & VEXEC) { > + if (!err && (mode & VEXEC)) { > /* > * Directories use file_dac_search to override the execute bit. > */ > int p = vp->v_type == VDIR ? PRIV_FILE_DAC_SEARCH : > PRIV_FILE_DAC_EXECUTE; > > - return (priv_policy_va(cr, p, B_FALSE, EACCES, NULL, > - KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE)); > + err = priv_policy_va(cr, p, B_FALSE, EACCES, NULL, > + KLPDARG_VNODE, vp, (char *)NULL, KLPDARG_NOMORE); > } > - return (0); > + > + if (err) > + return (fmac_vnode_priv_access(cr, vp, mode, err)); > + else > + return (0); > } > > /* > diff --git a/usr/src/uts/common/sys/fmac/avc.h b/usr/src/uts/common/sys/fmac/avc.h > --- a/usr/src/uts/common/sys/fmac/avc.h > +++ b/usr/src/uts/common/sys/fmac/avc.h > @@ -88,6 +88,11 @@ > security_class_t tclass, access_vector_t requested, > avc_audit_data_t *auditdata); > > +/* Check requested permissions without considering permissive mode/domains. */ > +extern int avc_has_perm_strict(security_id_t ssid, security_id_t tsid, > + security_class_t tclass, access_vector_t requested, > + avc_audit_data_t *auditdata); > + > #define AVC_CALLBACK_GRANT 1 > #define AVC_CALLBACK_TRY_REVOKE 2 > #define AVC_CALLBACK_REVOKE 4 > diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h > --- a/usr/src/uts/common/sys/fmac/fmac.h > +++ b/usr/src/uts/common/sys/fmac/fmac.h > @@ -105,6 +105,7 @@ > access_vector_t fmac_sigtoav(int sig); > int fmac_hasprocperm(const cred_t *tcrp, const cred_t *scrp, > access_vector_t perms); > +int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); > #endif /* _KERNEL */ > > #ifdef __cplusplus > > From john.weeks at sun.com Tue Nov 18 08:18:57 2008 From: john.weeks at sun.com (John Weeks) Date: Tue, 18 Nov 2008 08:18:57 -0800 Subject: [fmac-discuss] [PATCH] Simplify FMAC exec logic In-Reply-To: <1226673100.9382.64.camel@moss-spartans.epoch.ncsc.mil> References: <1226673100.9382.64.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4922EAF1.80702@sun.com> On 11/14/08 06:31, Stephen Smalley wrote: > With the change in the prior patch to the exec code path to also set the > process suid flags when fmac_execsetid is true (i.e. when execsetid > permission is not allowed between the old and new contexts), we can > simplify the FMAC processing in the exec code path. If we set > PRIV_INCREASE in the privflags when fmac_execsetid is true, then the > subsequent exec logic will set the linker security flag and the process > suid flags without requiring any further checking of fmac_execsetid. As > fmac_execsetid is no longer being used only to set the linker security > flag, the fmac_execsetid boolean is renamed to setprivinc, and the > "execsetid" FMAC permission is renamed to "noprivinc" (i.e. no need to > assert privilege increase protections on this domain transition). There > should be no actual change in behavior as a result, just an > implementation that fits better into the existing logic. This patch > applies on top of the prior one. > > Webrev available at: http://cr.opensolaris.org/~sds/privinc/ Acked-by: John Weeks > > diff --git a/usr/src/cmd/fmac/policy/domains/program/newrole.te b/usr/src/cmd/fmac/policy/domains/program/newrole.te > --- a/usr/src/cmd/fmac/policy/domains/program/newrole.te > +++ b/usr/src/cmd/fmac/policy/domains/program/newrole.te > @@ -52,8 +52,8 @@ > # Allow newrole_t to transition to user domains. > domain_trans(newrole_t, shell_exec_t, userdomain) > > -# No need for linker security flag setting on newrole -> user shell transition. > -allow newrole_t { user_t sysadm_t }:process execsetid; > +# No need for privilege increase protections on newrole -> user shell transition. > +allow newrole_t { user_t sysadm_t }:process noprivinc; > > # Use capabilities. > allow newrole_t self:capability { setuid setgid net_bind_service dac_override }; > diff --git a/usr/src/cmd/fmac/policy/domains/program/su.te b/usr/src/cmd/fmac/policy/domains/program/su.te > --- a/usr/src/cmd/fmac/policy/domains/program/su.te > +++ b/usr/src/cmd/fmac/policy/domains/program/su.te > @@ -46,8 +46,8 @@ > # Revert to the user domain when a shell is executed. > domain_auto_trans($1_su_t, shell_exec_t, $1_t) > > -# No need to set linker security flag on the su -> user shell transition. > -allow $1_su_t $1_t:process execsetid; > +# No need for privilege increase protections on su -> user shell transition. > +allow $1_su_t $1_t:process noprivinc; > > # Create/update /var/adm/sulog. > allow $1_su_t var_log_t:dir rw_dir_perms; > diff --git a/usr/src/cmd/fmac/policy/domains/system/sshd.te b/usr/src/cmd/fmac/policy/domains/system/sshd.te > --- a/usr/src/cmd/fmac/policy/domains/system/sshd.te > +++ b/usr/src/cmd/fmac/policy/domains/system/sshd.te > @@ -76,8 +76,8 @@ > domain_trans(sshd_t, shell_exec_t, user_t) > domain_trans(sshd_t, shell_exec_t, sysadm_t) > > -# No need for linker security flag setting on sshd -> user shell transition. > -allow sshd_t { user_t sysadm_t }:process execsetid; > +# No need for privilege increase protections on sshd -> user shell transition. > +allow sshd_t { user_t sysadm_t }:process noprivinc; > > # Read /etc/shadow > allow sshd_t shadow_t:file r_file_perms; > diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te > --- a/usr/src/cmd/fmac/policy/macros.te > +++ b/usr/src/cmd/fmac/policy/macros.te > @@ -229,9 +229,12 @@ > allow $3 $2:file entrypoint; > > # > -# Silence execsetid avc messages. > +# Silence "no privilege increase" avc messages. > +# These are only allowed where the calling domain is more > +# privileged than the new domain. Controls setting of the > +# linker security flag and process suid flags. > # > -dontaudit $1 $3:process execsetid; > +dontaudit $1 $3:process noprivinc; > ') > > ################################# > diff --git a/usr/src/cmd/fmac/policy/mls b/usr/src/cmd/fmac/policy/mls > --- a/usr/src/cmd/fmac/policy/mls > +++ b/usr/src/cmd/fmac/policy/mls > @@ -228,7 +228,7 @@ > setpgid : write > setexec : write > setfscreate : write > - execsetid : none > + noprivinc : none > } > > class sem > diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors > --- a/usr/src/common/fmac/policy/flask/access_vectors > +++ b/usr/src/common/fmac/policy/flask/access_vectors > @@ -265,7 +265,7 @@ > getattr > setexec > setfscreate > - execsetid > + noprivinc > } > > > diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c > --- a/usr/src/uts/common/fmac/fmac.c > +++ b/usr/src/uts/common/fmac/fmac.c > @@ -556,7 +556,7 @@ > > int > fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, > - boolean_t *execsetid, security_id_t *prev_secidp, security_id_t *secidp) > + boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp) > { > security_id_t prev_secid, secid; > int error; > @@ -585,7 +585,7 @@ > SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); > if (error) > return (error); > - *execsetid = B_FALSE; > + *setprivinc = B_FALSE; > *setsecid = B_FALSE; > *prev_secidp = *secidp = secid; > return (0); > @@ -602,11 +602,11 @@ > return (error); > > error = avc_has_perm(prev_secid, secid, SECCLASS_PROCESS, > - PROCESS__EXECSETID, &ad); > + PROCESS__NOPRIVINC, &ad); > if (error) > - *execsetid = B_TRUE; > + *setprivinc = B_TRUE; > else > - *execsetid = B_FALSE; > + *setprivinc = B_FALSE; > > *setsecid = B_TRUE; > *prev_secidp = prev_secid; > diff --git a/usr/src/uts/common/os/exec.c b/usr/src/uts/common/os/exec.c > --- a/usr/src/uts/common/os/exec.c > +++ b/usr/src/uts/common/os/exec.c > @@ -524,7 +524,7 @@ > ssize_t resid; > uid_t uid, gid; > security_id_t prev_secid, secid; > - boolean_t setsecid = B_FALSE, fmac_execsetid = B_FALSE; > + boolean_t setsecid = B_FALSE, setprivinc = B_FALSE; > struct vattr vattr; > char magbuf[MAGIC_BYTES]; > int setid; > @@ -568,13 +568,16 @@ > if (level == 0) { > privflags = execsetid(vp, &vattr, &uid, &gid); > > - error = fmac_exec(CRED(), vp, &setsecid, &fmac_execsetid, > + error = fmac_exec(CRED(), vp, &setsecid, &setprivinc, > &prev_secid, &secid); > if (error) > goto bad; > > if (setsecid) > privflags |= PRIV_SETUGID; > + > + if (setprivinc) > + privflags |= PRIV_INCREASE; > } > > if (level == 0 && privflags != 0) { > @@ -677,9 +680,6 @@ > if (setid & PRIV_INCREASE) > setidfl |= EXECSETID_PRIVS; > > - if (fmac_execsetid) > - setidfl |= EXECSETID_UGIDS; > - > error = (*eswp->exec_func)(vp, uap, args, idatap, level, execsz, > setidfl, exec_file, cred, brand_action); > rw_exit(eswp->exec_lock); > @@ -725,7 +725,7 @@ > if (cred->cr_ruid != cred->cr_uid || > (cred->cr_rgid != cred->cr_gid && > !supgroupmember(cred->cr_gid, cred)) || > - (privflags & PRIV_INCREASE) != 0 || fmac_execsetid) > + (privflags & PRIV_INCREASE) != 0) > suidflags = PSUIDFLAGS; > else > suidflags = 0; > diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h > --- a/usr/src/uts/common/sys/fmac/fmac.h > +++ b/usr/src/uts/common/sys/fmac/fmac.h > @@ -99,7 +99,7 @@ > cred_t *cr); > int fmac_vnode_setattr(vnode_t *, cred_t *); > int fmac_exec(cred_t *cr, vnode_t *vp, boolean_t *setsecid, > - boolean_t *execsetid, security_id_t *prev_secidp, security_id_t *secidp); > + boolean_t *setprivinc, security_id_t *prev_secidp, security_id_t *secidp); > int fmac_vnode_access(vnode_t *, int, int, cred_t *, boolean_t); > int fmac_priv_proc_cred_perm(const cred_t *scr, cred_t *tcr, int mode); > access_vector_t fmac_sigtoav(int sig); > From sds at tycho.nsa.gov Tue Nov 18 10:22:00 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 18 Nov 2008 13:22:00 -0500 Subject: [fmac-discuss] [PATCH v3] Mediate file DAC privilege checks In-Reply-To: <4922EA93.7010606@sun.com> References: <1226583844.32412.50.camel@moss-spartans.epoch.ncsc.mil> <4922EA93.7010606@sun.com> Message-ID: <1227032520.30767.17.camel@moss-spartans.epoch.ncsc.mil> On Tue, 2008-11-18 at 08:17 -0800, John Weeks wrote: > On 11/13/08 05:44, Stephen Smalley wrote: > > Take 3 of a patch showing how we can approach granting privileges in > > FMAC. This version takes Glenn's suggestion that the priv_* checks > > applied by fmac_vnode_priv_access() only be applied if the legacy policy > > would deny the privilege (i.e. to grant privileges that would normally > > be denied) similar to FGAP, while the normal read/write/execute checks > > applied by fmac_vnode_access() can be used restrictively. However, the > > priv_* checks are kept as object-based checks; this is necessary if we > > want to provide the same functionality as FGAP/klpd via FMAC. > > > > This patch also includes policy changes from John Weeks to > > allow /bin/passwd to run without being setuid root, specifically > > granting it priv_* permissions needed for > > accessing /etc/.pwd.lock, /etc/shadow, and /var/adm/lastlog. This > > enables one to successfully run /bin/passwd as a non-root user even > > after removing the setuid bit from it. However, there is one > > "regression" in the behavior of /bin/passwd when run in this > > way; /etc/shadow is left with the ownership of the invoking user rather > > than root afterward since it is re-created on the transaction > > and /bin/passwd apparently doesn't explicitly preserve/set the owner in > > Solaris. Thus we would need to change /bin/passwd to preserve/set the > > ownership if we wanted to grant it privileges in this manner rather than > > via setuid. > > > > The overall permission checking logic after this patch is: > > 1) Check DAC permissions to the file. > > Denied? Go to 2. > > Granted? Go to 5. > > 2) Check file DAC privileges. > > Denied? Go to 3. > > Granted? Go to 5. > > 3) If using klpd, call it to check the privilege to the file. > > No klpd or Denied? Go to 4. > > Granted? Go to 5. > > 4) Check FMAC priv_* permissions to the file. > > Denied? Done - Permission denied. > > Granted? Go to 5. > > 5) Check FMAC read/write/execute to the file. > > Denied? Done - Permission denied. > > Granted? Done - Permission granted. > > > > Webrev is available at: http://cr.opensolaris.org/~sds/filedacpriv/ > > > > Acked-by: John Weeks Thanks. Just to clarify for everyone: What is being committed here is just the support to grant file DAC privileges using FMAC when they would otherwise be denied. This patch also introduces the policy changes to allow this to be demonstrated with the /bin/passwd program, but does not change the mode on /bin/passwd, so it has no affect unless you remove setuid from /bin/passwd yourself. Next steps include: - applying the same approach as in this patch to the other secpolicy_vnode and ultimately all of the secpolicy hooks so that FMAC can grant any privilege. - introducing support for restricting privileges via FMAC that would otherwise be allowed, - integrating Solaris RBAC with FMAC such that assuming a Solaris role will automatically set the FMAC context appropriately -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Tue Nov 18 12:22:52 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 18 Nov 2008 15:22:52 -0500 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC Message-ID: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> This patch introduces restrictive checks for privileges, enabling FMAC to deny a privilege that would otherwise be allowed. This is comparable to the SELinux checking on Linux capabilities, and takes a generally similar approach to it. Unlike the support for granting privileges via FMAC, the restrictive check is based solely on the subject security context. The patch extends the existing privs.awk script such that it can be used to generate Flask definitions from the existing priv_defs, so that security classes and permissions can be automatically defined from the existing privilege definitions. This is applied both during policy build to generate the policy definitions and during kernel build to generate the constants used by the permission checking code. As the number of privileges exceeds the number of bits available in a single access vector (32 bits), multiple classes and access vectors are generated to represent the full set of privileges. The policy compiler and policy macros are adjusted by the patch such that a single policy rule can still be used to express the full set of privileges authorized for a given domain. The example policy is adjusted to allow the set of basic privileges to all domains (although this can of course be changed as desired), and to allow other privileges to various domains based on preliminary experience with booting the resulting system in enforcing mode. More work will be required to flesh out a complete working configuration. A new fmac_priv_restrict() hook function is defined for applying the restrictive privilege checks, and a call to this hook is inserted into the priv_policy* routines. The hook function maps the privilege value to an equivalent security class and access vector and invokes avc_has_perm(). The privilege value is also conveyed to the avc auditing code so that the value and its string name can be included in the output, which is helpful for confirming that the privileges are being mapped correctly and to provide further information if the privilege has no equivalent definition in FMAC. Cases that are not yet handled by this logic include: - Special privilege values like PRIV_ALL. Rather than mapping these to checking all privilege permissions in FMAC, I believe we should add more specific FMAC hooks to the callers and apply a specific permission check for the operation in those cases. - Required set checks. Some of these are testing for full privileges while others are more selective; we likely need to similarly introduce more specific hooks for them. - Dynamically registered privileges. These can likely be mapped to some general FMAC permission as a fallback. Support for these additional cases can be added as follow-up patches. Webrev available at: http://cr.opensolaris.org/~sds/privrestrict/ diff --git a/usr/src/cmd/fmac/checkpolicy/policy_parse.y b/usr/src/cmd/fmac/checkpolicy/policy_parse.y --- a/usr/src/cmd/fmac/checkpolicy/policy_parse.y +++ b/usr/src/cmd/fmac/checkpolicy/policy_parse.y @@ -810,7 +810,7 @@ MLS_BASE_READBY | MLS_BASE_WRITEBY; #endif - if (perdatum->value >= (sizeof(access_vector_t) * 8)) { + if (perdatum->value > (sizeof(access_vector_t) * 8)) { yyerror("too many permissions to fit in an access vector"); goto bad_perm; } @@ -929,7 +929,7 @@ /* actual value set in define_av_base */ #endif - if (perdatum->value >= (sizeof(access_vector_t) * 8)) { + if (perdatum->value > (sizeof(access_vector_t) * 8)) { yyerror("too many permissions to fit in an access vector"); goto bad; } @@ -1944,6 +1944,8 @@ for (k = ebitmap_startbit(tclasses); k < ebitmap_length(tclasses); k++) { if (!ebitmap_get_bit(tclasses, k)) continue; + if (!avp[k]) + continue; avkey.source_type = stype + 1; avkey.target_type = ttype + 1; avkey.target_class = k + 1; @@ -2078,9 +2080,9 @@ id); } } + if (!perdatum) { - sprintf(errormsg, "permission %s is not defined for class %s", id, policydbp->p_class_val_to_name[i]); - yyerror(errormsg); + /* Permission undefined for this class; skip. */ continue; } diff --git a/usr/src/cmd/fmac/policy/Makefile b/usr/src/cmd/fmac/policy/Makefile --- a/usr/src/cmd/fmac/policy/Makefile +++ b/usr/src/cmd/fmac/policy/Makefile @@ -47,6 +47,9 @@ M4FLAGS = -B81920 COMMONBASE = $(SRC)/common + +PRIVS_AWK = $(SRC)/uts/common/os/privs.awk +PRIVS_DEF = $(SRC)/uts/common/os/priv_defs # Detect if MLS is enabled CHECK_FOR_MLS = if $(EGREP) -s '^\#define.CONFIG_FLASK_MLS' \ @@ -123,8 +126,10 @@ ALL_TE = macros.te attrib.te $(TYPES) domains/every.te $(SYSTEM_DOMAINS) $(PROGRAM_DOMAINS) $(USER_DOMAINS) $(ADMIN_DOMAINS) assert.te FLASK_FILES = $(COMMONBASE)/fmac/policy/flask/security_classes \ + security_classes.priv \ $(COMMONBASE)/fmac/policy/flask/initial_sids \ - $(COMMONBASE)/fmac/policy/flask/access_vectors + $(COMMONBASE)/fmac/policy/flask/access_vectors \ + access_vectors.priv USERS_FILE = users$(MLS_SUFFIX) @@ -159,6 +164,12 @@ policy.conf: $(POLICYFILES) $(M4) $(M4FLAGS) -s $^ > policy.conf +access_vectors.priv: $(PRIVS_AWK) $(PRIVS_DEF) + $(NAWK) -f $(PRIVS_AWK) < $(PRIVS_DEF) fmacfile=$@ + +security_classes.priv: access_vectors.priv + grep '^class' $^ > $@ + fs_contexts.mls: fs_contexts $(SED) 's/_t/_t:u/g' $^ > $@ diff --git a/usr/src/cmd/fmac/policy/domains/every.te b/usr/src/cmd/fmac/policy/domains/every.te --- a/usr/src/cmd/fmac/policy/domains/every.te +++ b/usr/src/cmd/fmac/policy/domains/every.te @@ -33,6 +33,9 @@ # # Rules for every domain. # + +# Allow basic privileges. +allow domain self:privilege_class_set { file_link_any proc_exec proc_fork proc_info proc_session }; # Access other processes in the same domain. allow domain self:process *; diff --git a/usr/src/cmd/fmac/policy/domains/program/ifconfig.te b/usr/src/cmd/fmac/policy/domains/program/ifconfig.te --- a/usr/src/cmd/fmac/policy/domains/program/ifconfig.te +++ b/usr/src/cmd/fmac/policy/domains/program/ifconfig.te @@ -39,8 +39,8 @@ type ifconfig_t, domain, privlog; type ifconfig_exec_t, file_type, sysadmfile, exec_type; -# Use capabilities. -allow ifconfig_t ifconfig_t:capability { sys_module net_admin }; +# Use privileges. +allow ifconfig_t self:privilege_class_set { net_rawaccess sys_ip_config sys_net_config }; # Use system ops. allow ifconfig_t kernel_t:system { net_io_control }; diff --git a/usr/src/cmd/fmac/policy/domains/program/su.te b/usr/src/cmd/fmac/policy/domains/program/su.te --- a/usr/src/cmd/fmac/policy/domains/program/su.te +++ b/usr/src/cmd/fmac/policy/domains/program/su.te @@ -56,8 +56,8 @@ # Execute xauth. can_exec($1_su_t, bin_t) -# Use capabilities. -allow $1_su_t self:capability { setuid setgid net_bind_service }; +# Use privileges +allow $1_su_t self:privilege_class_set { file_dac_write proc_taskid proc_setid }; # Inherit and use descriptors from login. allow $1_su_t local_login_t:fd inherit_fd_perms; diff --git a/usr/src/cmd/fmac/policy/domains/system/inetd.te b/usr/src/cmd/fmac/policy/domains/system/inetd.te --- a/usr/src/cmd/fmac/policy/domains/system/inetd.te +++ b/usr/src/cmd/fmac/policy/domains/system/inetd.te @@ -45,8 +45,8 @@ # Inherit and use descriptors from init. allow inetd_t init_t:fd inherit_fd_perms; -# Use capabilities. -allow inetd_t inetd_t:capability { setgid net_bind_service }; +# Use privileges. +allow inetd_t self:privilege_class_set { contract_event sys_resource }; # Runs ksh during startup. # Possibly this should transition into a different domain. diff --git a/usr/src/cmd/fmac/policy/domains/system/portmap.te b/usr/src/cmd/fmac/policy/domains/system/portmap.te --- a/usr/src/cmd/fmac/policy/domains/system/portmap.te +++ b/usr/src/cmd/fmac/policy/domains/system/portmap.te @@ -53,8 +53,8 @@ can_udp_send(portmap_t, initrc_t) can_udp_send(portmap_t, lpd_t) -# Use capabilitiesl -allow portmap_t portmap_t:capability { net_bind_service setuid }; +# Use privileges +allow portmap_t self:privilege_class_set { file_chown proc_setid net_privaddr }; # Use net ioctls. allow portmap_t kernel_t:system { net_io_control }; diff --git a/usr/src/cmd/fmac/policy/domains/system/sendmail.te b/usr/src/cmd/fmac/policy/domains/system/sendmail.te --- a/usr/src/cmd/fmac/policy/domains/system/sendmail.te +++ b/usr/src/cmd/fmac/policy/domains/system/sendmail.te @@ -42,8 +42,8 @@ type sendmail_var_run_t, file_type, sysadmfile, pidfile; file_type_auto_trans(sendmail_t, var_run_t, sendmail_var_run_t) -# Use capabilities -allow sendmail_t sendmail_t:capability { setuid setgid net_bind_service sys_nice chown }; +# Use privileges. +allow sendmail_t self:privilege_class_set { proc_setid net_privaddr }; # Inherit and use descriptors from init. allow sendmail_t init_t:fd inherit_fd_perms; diff --git a/usr/src/cmd/fmac/policy/domains/system/sshd.te b/usr/src/cmd/fmac/policy/domains/system/sshd.te --- a/usr/src/cmd/fmac/policy/domains/system/sshd.te +++ b/usr/src/cmd/fmac/policy/domains/system/sshd.te @@ -46,8 +46,8 @@ # Can create pty's can_create_pty(sshd) -# Use capabilities. -allow sshd_t self:capability { chown fowner fsetid sys_tty_config dac_override net_bind_service }; +# Use privileges +allow sshd_t self:privilege_class_set { proc_setid net_privaddr contract_event file_dac_write proc_taskid }; # Create /var/run/sshd.pid type sshd_var_run_t, file_type, sysadmfile, pidfile; diff --git a/usr/src/cmd/fmac/policy/domains/system/syslogd.te b/usr/src/cmd/fmac/policy/domains/system/syslogd.te --- a/usr/src/cmd/fmac/policy/domains/system/syslogd.te +++ b/usr/src/cmd/fmac/policy/domains/system/syslogd.te @@ -47,8 +47,8 @@ type syslogd_var_run_t, file_type, sysadmfile, pidfile; file_type_auto_trans(syslogd_t, var_run_t, syslogd_var_run_t) -# Use the net_bind_service capability. -allow syslogd_t syslogd_t:capability { net_bind_service }; +# Use privileges +allow syslogd_t self:privilege_class_set { net_privaddr sys_mount }; # Create symlink to syslogd door. allow syslogd_t etc_t:dir add_name; diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te --- a/usr/src/cmd/fmac/policy/macros.te +++ b/usr/src/cmd/fmac/policy/macros.te @@ -64,6 +64,8 @@ define(`socket_class_set', `{ tcp_socket udp_socket rawip_socket netlink_socket packet_socket unix_stream_socket unix_dgram_socket }') + +define(`privilege_class_set', `{ priv0 priv1 priv2 }') # # Permissions for getting file attributes. @@ -469,6 +471,7 @@ ') define(`unconfined_domain', ` +allow $1 self:privilege_class_set *; allow $1 domain:process *; allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; diff --git a/usr/src/uts/common/fmac/avc.c b/usr/src/uts/common/fmac/avc.c --- a/usr/src/uts/common/fmac/avc.c +++ b/usr/src/uts/common/fmac/avc.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -552,6 +553,7 @@ { struct proc *p = curproc; struct vnode *vp; + const char *name; if (a && a->type == AVC_AUDIT_DATA_DONTAUDIT) return; @@ -570,6 +572,12 @@ avc_audit_append(" path=%s", vp->v_path); if (a->u.fs.name) avc_audit_append(" name=%s", a->u.fs.name); + break; + case AVC_AUDIT_DATA_PRIV: + avc_audit_append(" priv=%d", a->u.priv.priv); + name = priv_getbynum(a->u.priv.priv); + if (name) + avc_audit_append(" priv_name=%s", name); break; } } diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -790,3 +790,54 @@ ad.u.fs.vp = vp; return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); } + +int +fmac_priv_restrict(const cred_t *cr, int priv) +{ + security_id_t cr_secid; + security_class_t sclass; + access_vector_t av; + avc_audit_data_t ad; + const char *name; + + if (!fmac_enabled) + return (0); + + if (priv < 0) { + /* + * Need to handle special privileges like PRIV_ALL + * in the callers where we can map the operation to a + * specific FMAC permission. + */ + return (0); + } + + cr_secid = cr->cr_secid; + + switch (priv >> 5) { + case 0: + sclass = SECCLASS_PRIV0; + break; + case 1: + sclass = SECCLASS_PRIV1; + break; + case 2: + sclass = SECCLASS_PRIV2; + break; + default: + name = priv_getbynum(priv); + cmn_err(CE_WARN, "FMAC: Out of range privilege %d (%s)\n", + priv, name ? name : "undefined"); + if (fmac_enforcing) + return (EACCES); + else + return (0); + } + + /* Note: The access vector representation != privmask(priv). */ + av = 1 << (priv & 31); + + AVC_AUDIT_DATA_INIT(&ad, PRIV); + ad.u.priv.priv = priv; + return (avc_has_perm(cr_secid, cr_secid, sclass, av, &ad)); +} diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -380,9 +380,10 @@ priv_policy_ap(const cred_t *cr, int priv, boolean_t allzone, int err, const char *msg, va_list ap) { - if ((HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) || + if (((HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) || (!servicing_interrupt() && - priv_policy_override(cr, priv, allzone, ap) == 0)) { + priv_policy_override(cr, priv, allzone, ap) == 0)) && + fmac_priv_restrict(cr, priv) == 0) { if ((allzone || priv == PRIV_ALL || !PRIV_ISASSERT(priv_basic, priv)) && !servicing_interrupt()) { @@ -428,7 +429,8 @@ priv_policy_choice(const cred_t *cr, int priv, boolean_t allzone) { boolean_t res = HAS_PRIVILEGE(cr, priv) && - (!allzone || HAS_ALLZONEPRIVS(cr)); + (!allzone || HAS_ALLZONEPRIVS(cr)) && + !fmac_priv_restrict(cr, priv); /* Audit success only */ if (res && audit_active && @@ -451,7 +453,8 @@ priv_policy_only(const cred_t *cr, int priv, boolean_t allzone) { boolean_t res = HAS_PRIVILEGE(cr, priv) && - (!allzone || HAS_ALLZONEPRIVS(cr)); + (!allzone || HAS_ALLZONEPRIVS(cr)) && + !fmac_priv_restrict(cr, priv); if (res) { DTRACE_PROBE2(priv__ok, int, priv, boolean_t, allzone); diff --git a/usr/src/uts/common/os/privs.awk b/usr/src/uts/common/os/privs.awk --- a/usr/src/uts/common/os/privs.awk +++ b/usr/src/uts/common/os/privs.awk @@ -212,7 +212,7 @@ END { - if (!pubhfile && !privhfile && !cfile && !pnamesfile) { + if (!pubhfile && !privhfile && !cfile && !pnamesfile && !fmacfile) { print "Output file parameter not set" > "/dev/stderr" exit 1 } @@ -403,4 +403,13 @@ } } + if (fmacfile) { + for (i = 0; i < npriv / 32; i++) { + print "\nclass priv" i "\n{\n" > fmacfile + for (j = i * 32; j < npriv && j < (i+1)*32; j++) { + print "\t" privs[j] > fmacfile + } + print "}\n" > fmacfile + } + } } diff --git a/usr/src/uts/common/sys/fmac/Makefile b/usr/src/uts/common/sys/fmac/Makefile --- a/usr/src/uts/common/sys/fmac/Makefile +++ b/usr/src/uts/common/sys/fmac/Makefile @@ -32,11 +32,15 @@ COMMONBASE = $(SRC)/common +PRIVS_AWK = $(SRC)/uts/common/os/privs.awk +PRIVS_DEF = $(SRC)/uts/common/os/priv_defs + FMAC_MKFLASK_AWK = mkflask.awk FMAC_MKACCESSVECTOR_AWK = mkaccess_vector.awk DERIVED_FLASK_DEFS = \ $(COMMONBASE)/fmac/policy/flask/security_classes \ + security_classes.priv \ $(COMMONBASE)/fmac/policy/flask/initial_sids DERIVED_FLASK_FILES = \ @@ -45,7 +49,8 @@ initial_sid_to_string.h DERIVED_ACCESSVECTOR_DEFS = \ - $(COMMONBASE)/fmac/policy/flask/access_vectors + $(COMMONBASE)/fmac/policy/flask/access_vectors \ + access_vectors.priv DERIVED_ACCESSVECTOR_FILES = \ av_inherit.h \ @@ -67,3 +72,10 @@ $(DERIVED_ACCESSVECTOR_FILES): $(FMAC_MKACCESSVECTOR_AWK) $(DERIVED_ACCESSVECTOR_DEFS) $(NAWK) -f $(FMAC_MKACCESSVECTOR_AWK) $(DERIVED_ACCESSVECTOR_DEFS) + +access_vectors.priv: $(PRIVS_AWK) $(PRIVS_DEF) + $(NAWK) -f $(PRIVS_AWK) < $(PRIVS_DEF) fmacfile=$@ + +security_classes.priv: access_vectors.priv + grep '^class' $^ > $@ + diff --git a/usr/src/uts/common/sys/fmac/avc.h b/usr/src/uts/common/sys/fmac/avc.h --- a/usr/src/uts/common/sys/fmac/avc.h +++ b/usr/src/uts/common/sys/fmac/avc.h @@ -51,7 +51,7 @@ char type; #define AVC_AUDIT_DATA_FS 1 #define AVC_AUDIT_DATA_NET 2 -#define AVC_AUDIT_DATA_CAP 3 +#define AVC_AUDIT_DATA_PRIV 3 #define AVC_AUDIT_DATA_IPC 4 #define AVC_AUDIT_DATA_DONTAUDIT 5 /* never audit this permission check */ union { @@ -59,6 +59,9 @@ struct vnode *vp; char *name; } fs; + struct { + int priv; + } priv; } u; } avc_audit_data_t; diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -106,6 +106,7 @@ int fmac_hasprocperm(const cred_t *tcrp, const cred_t *scrp, access_vector_t perms); int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); +int fmac_priv_restrict(const cred_t *cr, int priv); #endif /* _KERNEL */ #ifdef __cplusplus -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Tue Nov 18 15:50:49 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Tue, 18 Nov 2008 15:50:49 -0800 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC In-Reply-To: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> References: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492354D9.6000704@sun.com> Stephen, The new controls you are implementing require some getting used to. To make sure I understand it, and to try to help explain this to others on the alias, I have attempted to put this in my own words. Please correct me if I'm wrong. Stephen's recent changes should be particularly interesting to those of you familiar with Trusted Solaris 8 (or earlier). TSOL 8 had two additional privileges sets that were not implemented in the Solaris 10. These sets were called F (Forced) and A (Allowed) privileges, and were maintained in the extended (shadow) inode of ELF files. Note that the words "privilege" and "permssion" are being used here to refer to different data structures and implementations. The word "privilege" refers to the process privilege sets in Solaris and Trusted Solaris. The term "permission" refers to the FMAC permissions that are defined for specific object classes. The F set concept is similar to the FMAC authoritative permissions that grant a domain authority to perform an operation that previously required a Solaris 10 privilege. However, instead of being associated with a specific ELF file, as the F set was, these new authoritative permissions are associated with the domain in which the process runs. Also they aren't merged into the existing privilege sets, so they aren't affected by privilege bracketing nor the computation of the new Permitted and Effective sets during exec. They are bound by the Limit set, however. One source of confusion is that these new permission names currently don't match their related privilege names. For example the FMAC authoritative permission, priv_write in the file object class, corresponds to the privilege file_dac_write. The A set concept is similar to the FMAC object class called privilege_class_set. The permission names and numeric values in this object class must match the existing Solaris privilege names since they are used to mask out specific privilege bits in the (observed) effective set when priv_policy() is checked. As with the new authoritative permissions, the allowed permissions in the privilege_class_set are associated with the domain in which the process runs. The interaction between the existing privilege system and the FMAC controls is complex. Looking the secpolicy_vnode_access() function in the webrev, line 883: http://cr.opensolaris.org/~sds/filedacpriv/usr/src/uts/common/os/policy.c.sdiff.html it is interesting to point out that restrictive aspect of the FMAC privilege_class_set is applied within the priv_policy_va() call (line 855), and then if it returns EACCESS (lack of privilege) then it call fmac_vnode_priv_access() , line 874, to see it there is an authoritative permission to grant access. So it seems that within a single policy file for a domain, I could specify that a process with an effective privilege set including file_dac_write is not permitted to use that privilege because it is not in the privilege_class_set for the current domain, but that it could succeed anyway if it had the priv_write permission for the file object class. Note that the policy flow here would that an administrator assigned the program the file_dac_write privilege (in either exec_attr or an SMF manifest), but then the FMAC policy prohibited the process already running with this privilege from using this privilege, but nevertheless granted it the equivalent permission via the same policy file. Then, upon successful return from secpolicy_vnode_access(), there is another FMAC call, avc_has_perm() to verify that the process has general write access to the specific vnode (not shown in this webrev). While this seems to meet the general compatibility and security requirements, it concerns me that it may be incomprehensible to most users, even administrators. --Glenn Stephen Smalley wrote: > This patch introduces restrictive checks for privileges, enabling FMAC > to deny a privilege that would otherwise be allowed. This is comparable > to the SELinux checking on Linux capabilities, and takes a generally > similar approach to it. Unlike the support for granting privileges via > FMAC, the restrictive check is based solely on the subject security > context. > > The patch extends the existing privs.awk script such that it can be used > to generate Flask definitions from the existing priv_defs, so that > security classes and permissions can be automatically defined from the > existing privilege definitions. This is applied both during policy > build to generate the policy definitions and during kernel build to > generate the constants used by the permission checking code. > > As the number of privileges exceeds the number of bits available in a > single access vector (32 bits), multiple classes and access vectors are > generated to represent the full set of privileges. The policy compiler > and policy macros are adjusted by the patch such that a single policy > rule can still be used to express the full set of privileges authorized > for a given domain. > > The example policy is adjusted to allow the set of basic privileges to > all domains (although this can of course be changed as desired), and to > allow other privileges to various domains based on preliminary > experience with booting the resulting system in enforcing mode. More > work will be required to flesh out a complete working configuration. > > A new fmac_priv_restrict() hook function is defined for applying the > restrictive privilege checks, and a call to this hook is inserted into > the priv_policy* routines. The hook function maps the privilege value > to an equivalent security class and access vector and invokes > avc_has_perm(). The privilege value is also conveyed to the avc > auditing code so that the value and its string name can be included in > the output, which is helpful for confirming that the privileges are > being mapped correctly and to provide further information if the > privilege has no equivalent definition in FMAC. > > Cases that are not yet handled by this logic include: > - Special privilege values like PRIV_ALL. Rather than mapping these to > checking all privilege permissions in FMAC, I believe we should add more > specific FMAC hooks to the callers and apply a specific permission check > for the operation in those cases. > - Required set checks. Some of these are testing for full privileges > while others are more selective; we likely need to similarly introduce > more specific hooks for them. > - Dynamically registered privileges. These can likely be mapped to some > general FMAC permission as a fallback. > > From sds at tycho.nsa.gov Wed Nov 19 05:38:06 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 19 Nov 2008 08:38:06 -0500 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC In-Reply-To: <492354D9.6000704@sun.com> References: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> <492354D9.6000704@sun.com> Message-ID: <1227101886.12003.36.camel@moss-spartans.epoch.ncsc.mil> On Tue, 2008-11-18 at 15:50 -0800, Glenn Faden wrote: > Stephen, > > The new controls you are implementing require some getting used to. To > make sure I understand it, and to try to help explain this to others on > the alias, I have attempted to put this in my own words. Please correct > me if I'm wrong. > > Stephen's recent changes should be particularly interesting to those of > you familiar with Trusted Solaris 8 (or earlier). TSOL 8 had two > additional privileges sets that were not implemented in the Solaris 10. > These sets were called F (Forced) and A (Allowed) privileges, and were > maintained in the extended (shadow) inode of ELF files. > > Note that the words "privilege" and "permssion" are being used here to > refer to different data structures and implementations. The word > "privilege" refers to the process privilege sets in Solaris and Trusted > Solaris. The term "permission" refers to the FMAC permissions that are > defined for specific object classes. > > The F set concept is similar to the FMAC authoritative permissions that > grant a domain authority to perform an operation that previously > required a Solaris 10 privilege. However, instead of being associated > with a specific ELF file, as the F set was, these new authoritative > permissions are associated with the domain in which the process runs. > Also they aren't merged into the existing privilege sets, so they aren't > affected by privilege bracketing nor the computation of the new > Permitted and Effective sets during exec. They are bound by the Limit > set, however. They are also bound by the effective set if the process is privilege aware - that was to preserve privilege bracketing via setppriv(). > One source of confusion is that these new permission names > currently don't match their related privilege names. For example the > FMAC authoritative permission, priv_write in the file object class, > corresponds to the privilege file_dac_write. If you'd rather use a different name, we can certainly do that. I only chose different names because we were making these checks object-based and relative to the file class, so they were already semantically different. But nothing prevents us from switching priv_read to file_dac_read and priv_write to file_dac_write, and they would remain distinct from the restrictive permissions by virtue of being in a different class. I've also thought about moving them into a separate class, so that allow rules that grant all permissions to a file do not automatically grant any privileges unwittingly. > The A set concept is similar to the FMAC object class called > privilege_class_set. The permission names and numeric values in this > object class must match the existing Solaris privilege names since they > are used to mask out specific privilege bits in the (observed) effective > set when priv_policy() is checked. As with the new authoritative > permissions, the allowed permissions in the privilege_class_set are > associated with the domain in which the process runs. > > The interaction between the existing privilege system and the FMAC > controls is complex. Looking the secpolicy_vnode_access() function in > the webrev, line 883: > > http://cr.opensolaris.org/~sds/filedacpriv/usr/src/uts/common/os/policy.c.sdiff.html > > it is interesting to point out that restrictive aspect of the FMAC > privilege_class_set is applied within the priv_policy_va() call (line > 855), and then if it returns EACCESS (lack of privilege) then it call > fmac_vnode_priv_access() , line 874, to see it there is an authoritative > permission to grant access. > > So it seems that within a single policy file for a domain, I could > specify that a process with an effective privilege set including > file_dac_write is not permitted to use that privilege because it is not > in the privilege_class_set for the current domain, but that it could > succeed anyway if it had the priv_write permission for the file object > class. Yes, that's intentional, and quite useful in the passwd example. Suppose that we want to keep passwd setuid-root in order to preserve ownership on the files it creates and to avoid the need for further privileges as per our earlier discussion, and yet we want to limit passwd to least privilege. So in the policy, we don't allow it any permissions in privilege_class_set beyond the base privilege set, thereby preventing it from exercising any privileges globally, and we allow it priv_read/priv_write as needed to specific objects, thereby allowing it to exercise the file_dac privileges on those specific objects. That is in fact precisely how passwd will after this patch is applied. > Note that the policy flow here would that an administrator assigned the > program the file_dac_write privilege (in either exec_attr or an SMF > manifest), but then the FMAC policy prohibited the process already > running with this privilege from using this privilege, but nevertheless > granted it the equivalent permission via the same policy file. Then, > upon successful return from secpolicy_vnode_access(), there is another > FMAC call, avc_has_perm() to verify that the process has general write > access to the specific vnode (not shown in this webrev). > > While this seems to meet the general compatibility and security > requirements, it concerns me that it may be incomprehensible to most > users, even administrators. The goal of course would be to hide the underlying details from most users and administrators, and provide them with higher level configuration tools and languages that map down to this mechanism. The complexity is largely driven by the goals of enabling FMAC to both grant and restrict privileges, to support object-based privilege granting, and to maintain compatibility. Tell me which goal you would like to sacrifice ;) -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Wed Nov 19 08:57:10 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Wed, 19 Nov 2008 08:57:10 -0800 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC In-Reply-To: <1227101886.12003.36.camel@moss-spartans.epoch.ncsc.mil> References: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> <492354D9.6000704@sun.com> <1227101886.12003.36.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <49244566.4000002@sun.com> Stephen Smalley wrote: > On Tue, 2008-11-18 at 15:50 -0800, Glenn Faden wrote: > > >> One source of confusion is that these new permission names >> currently don't match their related privilege names. For example the >> FMAC authoritative permission, priv_write in the file object class, >> corresponds to the privilege file_dac_write. >> > If you'd rather use a different name, we can certainly do that. I only > chose different names because we were making these checks object-based > and relative to the file class, so they were already semantically > different. But nothing prevents us from switching priv_read to > file_dac_read and priv_write to file_dac_write, and they would remain > distinct from the restrictive permissions by virtue of being in a > different class. > Yes, I think it will be more intuitive if the privilege names match the corresponding permissions for each object class. I realize the prefix may be redundant, but that's a good thing in this case. > I've also thought about moving them into a separate class, so that allow > rules that grant all permissions to a file do not automatically grant > any privileges unwittingly. > > No, I think that will make it more likely to get mixed up with the privilege_class_set. A better this for this class might be permitted_privilege_set. > The goal of course would be to hide the underlying details from most > users and administrators, and provide them with higher level > configuration tools and languages that map down to this mechanism. > I hope so. > The complexity is largely driven by the goals of enabling FMAC to both > grant and restrict privileges, to support object-based privilege > granting, and to maintain compatibility. Tell me which goal you would > like to sacrifice ;) > The privilege_class_set feature is generally redundant because the object-based permission checks already can be used to prevent most privilege abuse. However, I understand that some privileges don't apply to specific objects. From sds at tycho.nsa.gov Wed Nov 19 10:31:18 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 19 Nov 2008 13:31:18 -0500 Subject: [fmac-discuss] [PATCH] Rename priv_* permissions to match privilege names Message-ID: <1227119478.12003.127.camel@moss-spartans.epoch.ncsc.mil> Rename the priv_* permissions used to grant file DAC privileges to match the corresponding privilege names. diff --git a/usr/src/cmd/fmac/policy/domains/program/passwd.te b/usr/src/cmd/fmac/policy/domains/program/passwd.te --- a/usr/src/cmd/fmac/policy/domains/program/passwd.te +++ b/usr/src/cmd/fmac/policy/domains/program/passwd.te @@ -46,16 +46,16 @@ # Update /etc/passwd and /etc/.pwd.lock. allow passwd_t etc_t:dir rw_dir_perms; -allow passwd_t etc_t:dir priv_write; +allow passwd_t etc_t:dir file_dac_write; allow passwd_t etc_t:file create_file_perms; -allow passwd_t etc_t:file priv_write; +allow passwd_t etc_t:file file_dac_write; # Update /etc/shadow. allow passwd_t shadow_t:file create_file_perms; -allow passwd_t shadow_t:file { priv_read priv_write }; +allow passwd_t shadow_t:file { file_dac_read file_dac_write }; file_type_auto_trans(passwd_t, etc_t, shadow_t) # Update lastlog. allow passwd_t var_log_t:dir rw_dir_perms; allow passwd_t var_log_t:file create_file_perms; -allow passwd_t var_log_t:file priv_write; +allow passwd_t var_log_t:file file_dac_write; diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te --- a/usr/src/cmd/fmac/policy/macros.te +++ b/usr/src/cmd/fmac/policy/macros.te @@ -470,7 +470,7 @@ define(`unconfined_domain', ` allow $1 domain:process *; -allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; -allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; +allow $1 { file_type unlabeled_t }:dir ~{ file_dac_search file_dac_read file_dac_write }; +allow $1 { file_type unlabeled_t }:file_class_set ~{ file_dac_execute file_dac_read file_dac_write }; allow $1 security_t:security *; ') diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -40,9 +40,9 @@ write read append - priv_execute - priv_write - priv_read + file_dac_execute + file_dac_write + file_dac_read open create link @@ -133,7 +133,7 @@ remove_name reparent search - priv_search + file_dac_search rmdir mounton mountassociate diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -762,20 +762,20 @@ av = 0; priv_emptyset(&privs); if (mode & VREAD) { - av |= FILE__PRIV_READ; + av |= FILE__FILE_DAC_READ; priv_addset(&privs, PRIV_FILE_DAC_READ); } if (mode & VWRITE) { - av |= FILE__PRIV_WRITE; + av |= FILE__FILE_DAC_WRITE; priv_addset(&privs, PRIV_FILE_DAC_WRITE); } if (mode & VEXEC) { if (sclass == SECCLASS_DIR) { - av |= DIR__PRIV_SEARCH; + av |= DIR__FILE_DAC_SEARCH; priv_addset(&privs, PRIV_FILE_DAC_SEARCH); } else { - av |= FILE__PRIV_EXECUTE; + av |= FILE__FILE_DAC_EXECUTE; priv_addset(&privs, PRIV_FILE_DAC_EXECUTE); } } -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Wed Nov 19 10:54:46 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 19 Nov 2008 13:54:46 -0500 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC In-Reply-To: <49244566.4000002@sun.com> References: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> <492354D9.6000704@sun.com> <1227101886.12003.36.camel@moss-spartans.epoch.ncsc.mil> <49244566.4000002@sun.com> Message-ID: <1227120886.12003.150.camel@moss-spartans.epoch.ncsc.mil> On Wed, 2008-11-19 at 08:57 -0800, Glenn Faden wrote: > Stephen Smalley wrote: > > On Tue, 2008-11-18 at 15:50 -0800, Glenn Faden wrote: > > > > > >> One source of confusion is that these new permission names > >> currently don't match their related privilege names. For example the > >> FMAC authoritative permission, priv_write in the file object class, > >> corresponds to the privilege file_dac_write. > >> > > > If you'd rather use a different name, we can certainly do that. I only > > chose different names because we were making these checks object-based > > and relative to the file class, so they were already semantically > > different. But nothing prevents us from switching priv_read to > > file_dac_read and priv_write to file_dac_write, and they would remain > > distinct from the restrictive permissions by virtue of being in a > > different class. > > > Yes, I think it will be more intuitive if the privilege names match the > corresponding permissions for each object class. I realize the prefix > may be redundant, but that's a good thing in this case. Ok, done (see separate patch). > > I've also thought about moving them into a separate class, so that allow > > rules that grant all permissions to a file do not automatically grant > > any privileges unwittingly. > > > > > No, I think that will make it more likely to get mixed up with the > privilege_class_set. A better this for this class might be > permitted_privilege_set. The privilege_class_set is a macro in the policy configuration that expands to the set of Flask security classes (priv0, priv1, priv2) that correspond to privileges, which are automatically generated from priv_defs. There are likewise already file_class_set, socket_class_set, ... macros in the policy configuration that expand to the set of Flask security classes that represent file objects, socket objects, etc. It is a just a convention of the policy configuration. > > The complexity is largely driven by the goals of enabling FMAC to both > > grant and restrict privileges, to support object-based privilege > > granting, and to maintain compatibility. Tell me which goal you would > > like to sacrifice ;) > > > The privilege_class_set feature is generally redundant because the > object-based permission checks already can be used to prevent most > privilege abuse. However, I understand that some privileges don't apply > to specific objects. Yes, it handles subject-only privileges nicely, and it also provides a way to deny a DAC privilege even when operating within the same type, which you noted as a concern earlier. It is also a very clean and minimalist implementation of privilege restrictions compared to having to instrument all of the secpolicy functions and all direct callers to priv_policy*, although we still need to address PRIV_ALL and secpolicy_require_set. I'm almost tempted to rework the privilege granting logic along the same lines to avoid having to modify the individual secpolicy functions. But doing that would require extracting and interpreting the contents of the variable argument list if we wanted to preserve object-based checks, like klpd does. Seems cleaner to keep those at the secpolicy layer. -- Stephen Smalley National Security Agency From john.weeks at sun.com Thu Nov 20 12:00:22 2008 From: john.weeks at sun.com (John Weeks) Date: Thu, 20 Nov 2008 12:00:22 -0800 Subject: [fmac-discuss] [PATCH] Restrict privileges via FMAC In-Reply-To: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> References: <1227039772.30767.98.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4925C1D6.4060400@sun.com> On 11/18/08 12:22, Stephen Smalley wrote: > This patch introduces restrictive checks for privileges, enabling FMAC > to deny a privilege that would otherwise be allowed. This is comparable > to the SELinux checking on Linux capabilities, and takes a generally > similar approach to it. Unlike the support for granting privileges via > FMAC, the restrictive check is based solely on the subject security > context. > > The patch extends the existing privs.awk script such that it can be used > to generate Flask definitions from the existing priv_defs, so that > security classes and permissions can be automatically defined from the > existing privilege definitions. This is applied both during policy > build to generate the policy definitions and during kernel build to > generate the constants used by the permission checking code. > > As the number of privileges exceeds the number of bits available in a > single access vector (32 bits), multiple classes and access vectors are > generated to represent the full set of privileges. The policy compiler > and policy macros are adjusted by the patch such that a single policy > rule can still be used to express the full set of privileges authorized > for a given domain. > > The example policy is adjusted to allow the set of basic privileges to > all domains (although this can of course be changed as desired), and to > allow other privileges to various domains based on preliminary > experience with booting the resulting system in enforcing mode. More > work will be required to flesh out a complete working configuration. > > A new fmac_priv_restrict() hook function is defined for applying the > restrictive privilege checks, and a call to this hook is inserted into > the priv_policy* routines. The hook function maps the privilege value > to an equivalent security class and access vector and invokes > avc_has_perm(). The privilege value is also conveyed to the avc > auditing code so that the value and its string name can be included in > the output, which is helpful for confirming that the privileges are > being mapped correctly and to provide further information if the > privilege has no equivalent definition in FMAC. > > Cases that are not yet handled by this logic include: > - Special privilege values like PRIV_ALL. Rather than mapping these to > checking all privilege permissions in FMAC, I believe we should add more > specific FMAC hooks to the callers and apply a specific permission check > for the operation in those cases. > - Required set checks. Some of these are testing for full privileges > while others are more selective; we likely need to similarly introduce > more specific hooks for them. > - Dynamically registered privileges. These can likely be mapped to some > general FMAC permission as a fallback. > > Support for these additional cases can be added as follow-up patches. > > Webrev available at: http://cr.opensolaris.org/~sds/privrestrict/ Acked-by: John Weeks > > diff --git a/usr/src/cmd/fmac/checkpolicy/policy_parse.y b/usr/src/cmd/fmac/checkpolicy/policy_parse.y > --- a/usr/src/cmd/fmac/checkpolicy/policy_parse.y > +++ b/usr/src/cmd/fmac/checkpolicy/policy_parse.y > @@ -810,7 +810,7 @@ > MLS_BASE_READBY | MLS_BASE_WRITEBY; > #endif > > - if (perdatum->value >= (sizeof(access_vector_t) * 8)) { > + if (perdatum->value > (sizeof(access_vector_t) * 8)) { > yyerror("too many permissions to fit in an access vector"); > goto bad_perm; > } > @@ -929,7 +929,7 @@ > /* actual value set in define_av_base */ > #endif > > - if (perdatum->value >= (sizeof(access_vector_t) * 8)) { > + if (perdatum->value > (sizeof(access_vector_t) * 8)) { > yyerror("too many permissions to fit in an access vector"); > goto bad; > } > @@ -1944,6 +1944,8 @@ > for (k = ebitmap_startbit(tclasses); k < ebitmap_length(tclasses); k++) { > if (!ebitmap_get_bit(tclasses, k)) > continue; > + if (!avp[k]) > + continue; > avkey.source_type = stype + 1; > avkey.target_type = ttype + 1; > avkey.target_class = k + 1; > @@ -2078,9 +2080,9 @@ > id); > } > } > + > if (!perdatum) { > - sprintf(errormsg, "permission %s is not defined for class %s", id, policydbp->p_class_val_to_name[i]); > - yyerror(errormsg); > + /* Permission undefined for this class; skip. */ > continue; > } > > diff --git a/usr/src/cmd/fmac/policy/Makefile b/usr/src/cmd/fmac/policy/Makefile > --- a/usr/src/cmd/fmac/policy/Makefile > +++ b/usr/src/cmd/fmac/policy/Makefile > @@ -47,6 +47,9 @@ > M4FLAGS = -B81920 > > COMMONBASE = $(SRC)/common > + > +PRIVS_AWK = $(SRC)/uts/common/os/privs.awk > +PRIVS_DEF = $(SRC)/uts/common/os/priv_defs > > # Detect if MLS is enabled > CHECK_FOR_MLS = if $(EGREP) -s '^\#define.CONFIG_FLASK_MLS' \ > @@ -123,8 +126,10 @@ > ALL_TE = macros.te attrib.te $(TYPES) domains/every.te $(SYSTEM_DOMAINS) $(PROGRAM_DOMAINS) $(USER_DOMAINS) $(ADMIN_DOMAINS) assert.te > > FLASK_FILES = $(COMMONBASE)/fmac/policy/flask/security_classes \ > + security_classes.priv \ > $(COMMONBASE)/fmac/policy/flask/initial_sids \ > - $(COMMONBASE)/fmac/policy/flask/access_vectors > + $(COMMONBASE)/fmac/policy/flask/access_vectors \ > + access_vectors.priv > > USERS_FILE = users$(MLS_SUFFIX) > > @@ -159,6 +164,12 @@ > policy.conf: $(POLICYFILES) > $(M4) $(M4FLAGS) -s $^ > policy.conf > > +access_vectors.priv: $(PRIVS_AWK) $(PRIVS_DEF) > + $(NAWK) -f $(PRIVS_AWK) < $(PRIVS_DEF) fmacfile=$@ > + > +security_classes.priv: access_vectors.priv > + grep '^class' $^ > $@ > + > fs_contexts.mls: fs_contexts > $(SED) 's/_t/_t:u/g' $^ > $@ > > diff --git a/usr/src/cmd/fmac/policy/domains/every.te b/usr/src/cmd/fmac/policy/domains/every.te > --- a/usr/src/cmd/fmac/policy/domains/every.te > +++ b/usr/src/cmd/fmac/policy/domains/every.te > @@ -33,6 +33,9 @@ > # > # Rules for every domain. > # > + > +# Allow basic privileges. > +allow domain self:privilege_class_set { file_link_any proc_exec proc_fork proc_info proc_session }; > > # Access other processes in the same domain. > allow domain self:process *; > diff --git a/usr/src/cmd/fmac/policy/domains/program/ifconfig.te b/usr/src/cmd/fmac/policy/domains/program/ifconfig.te > --- a/usr/src/cmd/fmac/policy/domains/program/ifconfig.te > +++ b/usr/src/cmd/fmac/policy/domains/program/ifconfig.te > @@ -39,8 +39,8 @@ > type ifconfig_t, domain, privlog; > type ifconfig_exec_t, file_type, sysadmfile, exec_type; > > -# Use capabilities. > -allow ifconfig_t ifconfig_t:capability { sys_module net_admin }; > +# Use privileges. > +allow ifconfig_t self:privilege_class_set { net_rawaccess sys_ip_config sys_net_config }; > > # Use system ops. > allow ifconfig_t kernel_t:system { net_io_control }; > diff --git a/usr/src/cmd/fmac/policy/domains/program/su.te b/usr/src/cmd/fmac/policy/domains/program/su.te > --- a/usr/src/cmd/fmac/policy/domains/program/su.te > +++ b/usr/src/cmd/fmac/policy/domains/program/su.te > @@ -56,8 +56,8 @@ > # Execute xauth. > can_exec($1_su_t, bin_t) > > -# Use capabilities. > -allow $1_su_t self:capability { setuid setgid net_bind_service }; > +# Use privileges > +allow $1_su_t self:privilege_class_set { file_dac_write proc_taskid proc_setid }; > > # Inherit and use descriptors from login. > allow $1_su_t local_login_t:fd inherit_fd_perms; > diff --git a/usr/src/cmd/fmac/policy/domains/system/inetd.te b/usr/src/cmd/fmac/policy/domains/system/inetd.te > --- a/usr/src/cmd/fmac/policy/domains/system/inetd.te > +++ b/usr/src/cmd/fmac/policy/domains/system/inetd.te > @@ -45,8 +45,8 @@ > # Inherit and use descriptors from init. > allow inetd_t init_t:fd inherit_fd_perms; > > -# Use capabilities. > -allow inetd_t inetd_t:capability { setgid net_bind_service }; > +# Use privileges. > +allow inetd_t self:privilege_class_set { contract_event sys_resource }; > > # Runs ksh during startup. > # Possibly this should transition into a different domain. > diff --git a/usr/src/cmd/fmac/policy/domains/system/portmap.te b/usr/src/cmd/fmac/policy/domains/system/portmap.te > --- a/usr/src/cmd/fmac/policy/domains/system/portmap.te > +++ b/usr/src/cmd/fmac/policy/domains/system/portmap.te > @@ -53,8 +53,8 @@ > can_udp_send(portmap_t, initrc_t) > can_udp_send(portmap_t, lpd_t) > > -# Use capabilitiesl > -allow portmap_t portmap_t:capability { net_bind_service setuid }; > +# Use privileges > +allow portmap_t self:privilege_class_set { file_chown proc_setid net_privaddr }; > > # Use net ioctls. > allow portmap_t kernel_t:system { net_io_control }; > diff --git a/usr/src/cmd/fmac/policy/domains/system/sendmail.te b/usr/src/cmd/fmac/policy/domains/system/sendmail.te > --- a/usr/src/cmd/fmac/policy/domains/system/sendmail.te > +++ b/usr/src/cmd/fmac/policy/domains/system/sendmail.te > @@ -42,8 +42,8 @@ > type sendmail_var_run_t, file_type, sysadmfile, pidfile; > file_type_auto_trans(sendmail_t, var_run_t, sendmail_var_run_t) > > -# Use capabilities > -allow sendmail_t sendmail_t:capability { setuid setgid net_bind_service sys_nice chown }; > +# Use privileges. > +allow sendmail_t self:privilege_class_set { proc_setid net_privaddr }; > > # Inherit and use descriptors from init. > allow sendmail_t init_t:fd inherit_fd_perms; > diff --git a/usr/src/cmd/fmac/policy/domains/system/sshd.te b/usr/src/cmd/fmac/policy/domains/system/sshd.te > --- a/usr/src/cmd/fmac/policy/domains/system/sshd.te > +++ b/usr/src/cmd/fmac/policy/domains/system/sshd.te > @@ -46,8 +46,8 @@ > # Can create pty's > can_create_pty(sshd) > > -# Use capabilities. > -allow sshd_t self:capability { chown fowner fsetid sys_tty_config dac_override net_bind_service }; > +# Use privileges > +allow sshd_t self:privilege_class_set { proc_setid net_privaddr contract_event file_dac_write proc_taskid }; > > # Create /var/run/sshd.pid > type sshd_var_run_t, file_type, sysadmfile, pidfile; > diff --git a/usr/src/cmd/fmac/policy/domains/system/syslogd.te b/usr/src/cmd/fmac/policy/domains/system/syslogd.te > --- a/usr/src/cmd/fmac/policy/domains/system/syslogd.te > +++ b/usr/src/cmd/fmac/policy/domains/system/syslogd.te > @@ -47,8 +47,8 @@ > type syslogd_var_run_t, file_type, sysadmfile, pidfile; > file_type_auto_trans(syslogd_t, var_run_t, syslogd_var_run_t) > > -# Use the net_bind_service capability. > -allow syslogd_t syslogd_t:capability { net_bind_service }; > +# Use privileges > +allow syslogd_t self:privilege_class_set { net_privaddr sys_mount }; > > # Create symlink to syslogd door. > allow syslogd_t etc_t:dir add_name; > diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te > --- a/usr/src/cmd/fmac/policy/macros.te > +++ b/usr/src/cmd/fmac/policy/macros.te > @@ -64,6 +64,8 @@ > define(`socket_class_set', `{ tcp_socket udp_socket rawip_socket > netlink_socket packet_socket unix_stream_socket > unix_dgram_socket }') > + > +define(`privilege_class_set', `{ priv0 priv1 priv2 }') > > # > # Permissions for getting file attributes. > @@ -469,6 +471,7 @@ > ') > > define(`unconfined_domain', ` > +allow $1 self:privilege_class_set *; > allow $1 domain:process *; > allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; > allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; > diff --git a/usr/src/uts/common/fmac/avc.c b/usr/src/uts/common/fmac/avc.c > --- a/usr/src/uts/common/fmac/avc.c > +++ b/usr/src/uts/common/fmac/avc.c > @@ -51,6 +51,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -552,6 +553,7 @@ > { > struct proc *p = curproc; > struct vnode *vp; > + const char *name; > > if (a && a->type == AVC_AUDIT_DATA_DONTAUDIT) > return; > @@ -570,6 +572,12 @@ > avc_audit_append(" path=%s", vp->v_path); > if (a->u.fs.name) > avc_audit_append(" name=%s", a->u.fs.name); > + break; > + case AVC_AUDIT_DATA_PRIV: > + avc_audit_append(" priv=%d", a->u.priv.priv); > + name = priv_getbynum(a->u.priv.priv); > + if (name) > + avc_audit_append(" priv_name=%s", name); > break; > } > } > diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c > --- a/usr/src/uts/common/fmac/fmac.c > +++ b/usr/src/uts/common/fmac/fmac.c > @@ -790,3 +790,54 @@ > ad.u.fs.vp = vp; > return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); > } > + > +int > +fmac_priv_restrict(const cred_t *cr, int priv) > +{ > + security_id_t cr_secid; > + security_class_t sclass; > + access_vector_t av; > + avc_audit_data_t ad; > + const char *name; > + > + if (!fmac_enabled) > + return (0); > + > + if (priv < 0) { > + /* > + * Need to handle special privileges like PRIV_ALL > + * in the callers where we can map the operation to a > + * specific FMAC permission. > + */ > + return (0); > + } > + > + cr_secid = cr->cr_secid; > + > + switch (priv >> 5) { > + case 0: > + sclass = SECCLASS_PRIV0; > + break; > + case 1: > + sclass = SECCLASS_PRIV1; > + break; > + case 2: > + sclass = SECCLASS_PRIV2; > + break; > + default: > + name = priv_getbynum(priv); > + cmn_err(CE_WARN, "FMAC: Out of range privilege %d (%s)\n", > + priv, name ? name : "undefined"); > + if (fmac_enforcing) > + return (EACCES); > + else > + return (0); > + } > + > + /* Note: The access vector representation != privmask(priv). */ > + av = 1 << (priv & 31); > + > + AVC_AUDIT_DATA_INIT(&ad, PRIV); > + ad.u.priv.priv = priv; > + return (avc_has_perm(cr_secid, cr_secid, sclass, av, &ad)); > +} > diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c > --- a/usr/src/uts/common/os/policy.c > +++ b/usr/src/uts/common/os/policy.c > @@ -380,9 +380,10 @@ > priv_policy_ap(const cred_t *cr, int priv, boolean_t allzone, int err, > const char *msg, va_list ap) > { > - if ((HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) || > + if (((HAS_PRIVILEGE(cr, priv) && (!allzone || HAS_ALLZONEPRIVS(cr))) || > (!servicing_interrupt() && > - priv_policy_override(cr, priv, allzone, ap) == 0)) { > + priv_policy_override(cr, priv, allzone, ap) == 0)) && > + fmac_priv_restrict(cr, priv) == 0) { > if ((allzone || priv == PRIV_ALL || > !PRIV_ISASSERT(priv_basic, priv)) && > !servicing_interrupt()) { > @@ -428,7 +429,8 @@ > priv_policy_choice(const cred_t *cr, int priv, boolean_t allzone) > { > boolean_t res = HAS_PRIVILEGE(cr, priv) && > - (!allzone || HAS_ALLZONEPRIVS(cr)); > + (!allzone || HAS_ALLZONEPRIVS(cr)) && > + !fmac_priv_restrict(cr, priv); > > /* Audit success only */ > if (res && audit_active && > @@ -451,7 +453,8 @@ > priv_policy_only(const cred_t *cr, int priv, boolean_t allzone) > { > boolean_t res = HAS_PRIVILEGE(cr, priv) && > - (!allzone || HAS_ALLZONEPRIVS(cr)); > + (!allzone || HAS_ALLZONEPRIVS(cr)) && > + !fmac_priv_restrict(cr, priv); > > if (res) { > DTRACE_PROBE2(priv__ok, int, priv, boolean_t, allzone); > diff --git a/usr/src/uts/common/os/privs.awk b/usr/src/uts/common/os/privs.awk > --- a/usr/src/uts/common/os/privs.awk > +++ b/usr/src/uts/common/os/privs.awk > @@ -212,7 +212,7 @@ > > END { > > - if (!pubhfile && !privhfile && !cfile && !pnamesfile) { > + if (!pubhfile && !privhfile && !cfile && !pnamesfile && !fmacfile) { > print "Output file parameter not set" > "/dev/stderr" > exit 1 > } > @@ -403,4 +403,13 @@ > } > } > > + if (fmacfile) { > + for (i = 0; i < npriv / 32; i++) { > + print "\nclass priv" i "\n{\n" > fmacfile > + for (j = i * 32; j < npriv && j < (i+1)*32; j++) { > + print "\t" privs[j] > fmacfile > + } > + print "}\n" > fmacfile > + } > + } > } > diff --git a/usr/src/uts/common/sys/fmac/Makefile b/usr/src/uts/common/sys/fmac/Makefile > --- a/usr/src/uts/common/sys/fmac/Makefile > +++ b/usr/src/uts/common/sys/fmac/Makefile > @@ -32,11 +32,15 @@ > > COMMONBASE = $(SRC)/common > > +PRIVS_AWK = $(SRC)/uts/common/os/privs.awk > +PRIVS_DEF = $(SRC)/uts/common/os/priv_defs > + > FMAC_MKFLASK_AWK = mkflask.awk > FMAC_MKACCESSVECTOR_AWK = mkaccess_vector.awk > > DERIVED_FLASK_DEFS = \ > $(COMMONBASE)/fmac/policy/flask/security_classes \ > + security_classes.priv \ > $(COMMONBASE)/fmac/policy/flask/initial_sids > > DERIVED_FLASK_FILES = \ > @@ -45,7 +49,8 @@ > initial_sid_to_string.h > > DERIVED_ACCESSVECTOR_DEFS = \ > - $(COMMONBASE)/fmac/policy/flask/access_vectors > + $(COMMONBASE)/fmac/policy/flask/access_vectors \ > + access_vectors.priv > > DERIVED_ACCESSVECTOR_FILES = \ > av_inherit.h \ > @@ -67,3 +72,10 @@ > > $(DERIVED_ACCESSVECTOR_FILES): $(FMAC_MKACCESSVECTOR_AWK) $(DERIVED_ACCESSVECTOR_DEFS) > $(NAWK) -f $(FMAC_MKACCESSVECTOR_AWK) $(DERIVED_ACCESSVECTOR_DEFS) > + > +access_vectors.priv: $(PRIVS_AWK) $(PRIVS_DEF) > + $(NAWK) -f $(PRIVS_AWK) < $(PRIVS_DEF) fmacfile=$@ > + > +security_classes.priv: access_vectors.priv > + grep '^class' $^ > $@ > + > diff --git a/usr/src/uts/common/sys/fmac/avc.h b/usr/src/uts/common/sys/fmac/avc.h > --- a/usr/src/uts/common/sys/fmac/avc.h > +++ b/usr/src/uts/common/sys/fmac/avc.h > @@ -51,7 +51,7 @@ > char type; > #define AVC_AUDIT_DATA_FS 1 > #define AVC_AUDIT_DATA_NET 2 > -#define AVC_AUDIT_DATA_CAP 3 > +#define AVC_AUDIT_DATA_PRIV 3 > #define AVC_AUDIT_DATA_IPC 4 > #define AVC_AUDIT_DATA_DONTAUDIT 5 /* never audit this permission check */ > union { > @@ -59,6 +59,9 @@ > struct vnode *vp; > char *name; > } fs; > + struct { > + int priv; > + } priv; > } u; > > } avc_audit_data_t; > diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h > --- a/usr/src/uts/common/sys/fmac/fmac.h > +++ b/usr/src/uts/common/sys/fmac/fmac.h > @@ -106,6 +106,7 @@ > int fmac_hasprocperm(const cred_t *tcrp, const cred_t *scrp, > access_vector_t perms); > int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); > +int fmac_priv_restrict(const cred_t *cr, int priv); > #endif /* _KERNEL */ > > #ifdef __cplusplus > > From john.weeks at sun.com Thu Nov 20 12:48:20 2008 From: john.weeks at sun.com (John Weeks) Date: Thu, 20 Nov 2008 12:48:20 -0800 Subject: [fmac-discuss] [PATCH] Rename priv_* permissions to match privilege names In-Reply-To: <1227119478.12003.127.camel@moss-spartans.epoch.ncsc.mil> References: <1227119478.12003.127.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <4925CD14.6000901@sun.com> On 11/19/08 10:31, Stephen Smalley wrote: > Rename the priv_* permissions used to grant file DAC privileges to match > the corresponding privilege names. > Acked-by: John Weeks > diff --git a/usr/src/cmd/fmac/policy/domains/program/passwd.te b/usr/src/cmd/fmac/policy/domains/program/passwd.te > --- a/usr/src/cmd/fmac/policy/domains/program/passwd.te > +++ b/usr/src/cmd/fmac/policy/domains/program/passwd.te > @@ -46,16 +46,16 @@ > > # Update /etc/passwd and /etc/.pwd.lock. > allow passwd_t etc_t:dir rw_dir_perms; > -allow passwd_t etc_t:dir priv_write; > +allow passwd_t etc_t:dir file_dac_write; > allow passwd_t etc_t:file create_file_perms; > -allow passwd_t etc_t:file priv_write; > +allow passwd_t etc_t:file file_dac_write; > > # Update /etc/shadow. > allow passwd_t shadow_t:file create_file_perms; > -allow passwd_t shadow_t:file { priv_read priv_write }; > +allow passwd_t shadow_t:file { file_dac_read file_dac_write }; > file_type_auto_trans(passwd_t, etc_t, shadow_t) > > # Update lastlog. > allow passwd_t var_log_t:dir rw_dir_perms; > allow passwd_t var_log_t:file create_file_perms; > -allow passwd_t var_log_t:file priv_write; > +allow passwd_t var_log_t:file file_dac_write; > diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te > --- a/usr/src/cmd/fmac/policy/macros.te > +++ b/usr/src/cmd/fmac/policy/macros.te > @@ -470,7 +470,7 @@ > > define(`unconfined_domain', ` > allow $1 domain:process *; > -allow $1 { file_type unlabeled_t }:dir ~{priv_search priv_read priv_write }; > -allow $1 { file_type unlabeled_t }:file_class_set ~{priv_execute priv_read priv_write }; > +allow $1 { file_type unlabeled_t }:dir ~{ file_dac_search file_dac_read file_dac_write }; > +allow $1 { file_type unlabeled_t }:file_class_set ~{ file_dac_execute file_dac_read file_dac_write }; > allow $1 security_t:security *; > ') > diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors > --- a/usr/src/common/fmac/policy/flask/access_vectors > +++ b/usr/src/common/fmac/policy/flask/access_vectors > @@ -40,9 +40,9 @@ > write > read > append > - priv_execute > - priv_write > - priv_read > + file_dac_execute > + file_dac_write > + file_dac_read > open > create > link > @@ -133,7 +133,7 @@ > remove_name > reparent > search > - priv_search > + file_dac_search > rmdir > mounton > mountassociate > diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c > --- a/usr/src/uts/common/fmac/fmac.c > +++ b/usr/src/uts/common/fmac/fmac.c > @@ -762,20 +762,20 @@ > av = 0; > priv_emptyset(&privs); > if (mode & VREAD) { > - av |= FILE__PRIV_READ; > + av |= FILE__FILE_DAC_READ; > priv_addset(&privs, PRIV_FILE_DAC_READ); > } > if (mode & VWRITE) { > - av |= FILE__PRIV_WRITE; > + av |= FILE__FILE_DAC_WRITE; > priv_addset(&privs, PRIV_FILE_DAC_WRITE); > } > > if (mode & VEXEC) { > if (sclass == SECCLASS_DIR) { > - av |= DIR__PRIV_SEARCH; > + av |= DIR__FILE_DAC_SEARCH; > priv_addset(&privs, PRIV_FILE_DAC_SEARCH); > } else { > - av |= FILE__PRIV_EXECUTE; > + av |= FILE__FILE_DAC_EXECUTE; > priv_addset(&privs, PRIV_FILE_DAC_EXECUTE); > } > } > From john.weeks at sun.com Fri Nov 21 11:25:24 2008 From: john.weeks at sun.com (John Weeks) Date: Fri, 21 Nov 2008 11:25:24 -0800 Subject: [fmac-discuss] [PATCH] setfiles should handle large files Message-ID: <49270B24.9090506@sun.com> Setfiles should handle large files. -John diff --git a/usr/src/cmd/fmac/setfiles/Makefile b/usr/src/cmd/fmac/setfiles/Makefile --- a/usr/src/cmd/fmac/setfiles/Makefile +++ b/usr/src/cmd/fmac/setfiles/Makefile @@ -30,6 +30,8 @@ include ../../Makefile.cmd +CPPFLAGS += -D_FILE_OFFSET_BITS=64 + C99MODE = -xc99=%all C99LMODE = -Xc99=%all From sds at tycho.nsa.gov Fri Nov 21 12:02:14 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Fri, 21 Nov 2008 15:02:14 -0500 Subject: [fmac-discuss] [PATCH] setfiles should handle large files In-Reply-To: <49270B24.9090506@sun.com> References: <49270B24.9090506@sun.com> Message-ID: <1227297734.7319.56.camel@moss-spartans.epoch.ncsc.mil> On Fri, 2008-11-21 at 11:25 -0800, John Weeks wrote: > Setfiles should handle large files. > > -John > > diff --git a/usr/src/cmd/fmac/setfiles/Makefile b/usr/src/cmd/fmac/setfiles/Makefile > --- a/usr/src/cmd/fmac/setfiles/Makefile > +++ b/usr/src/cmd/fmac/setfiles/Makefile > @@ -30,6 +30,8 @@ > > include ../../Makefile.cmd > > +CPPFLAGS += -D_FILE_OFFSET_BITS=64 > + > C99MODE = -xc99=%all > C99LMODE = -Xc99=%all Acked-by: Stephen Smalley -- Stephen Smalley National Security Agency From john.weeks at sun.com Fri Nov 21 15:50:52 2008 From: john.weeks at sun.com (John Weeks) Date: Fri, 21 Nov 2008 15:50:52 -0800 Subject: [fmac-discuss] [PATCH] add FMAC functions to lint library Message-ID: <4927495C.6050504@sun.com> Add FMAC functions to lint library. -John diff --git a/usr/src/lib/libc/port/llib-lc b/usr/src/lib/libc/port/llib-lc --- a/usr/src/lib/libc/port/llib-lc +++ b/usr/src/lib/libc/port/llib-lc @@ -160,6 +160,7 @@ #if defined(__amd64) #include #endif +#include /* * This really comes from the crt*.s startup modules. From sds at tycho.nsa.gov Tue Nov 25 05:09:54 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 25 Nov 2008 08:09:54 -0500 Subject: [fmac-discuss] [PATCH] Don't trigger an assertion failure when a privilege is denied by FMAC Message-ID: <1227618594.683.16.camel@moss-spartans.epoch.ncsc.mil> The introduction of FMAC restrictive checks for privileges made it possible for a privilege to be denied even though it is set in the observed effective privilege set of the process. If a process is run under privilege debugging via ppriv -D or truss and a privilege is denied by FMAC that would otherwise be allowed, an ASSERT() in the existing privilege debugging code that requires the privilege to not be present in the current privilege set is triggered, thereby panicking the kernel. One option would be to alter the ASSERT() to take the FMAC decision into account, like so: ASSERT(!HAS_PRIVILEGE(cr, priv)||fmac_priv_restrict(cr, priv)); but this would cause the FMAC check to be applied twice and the decision could still possibly change between the time of the original check and this ASSERT() statement as a result of a policy reload. Therefore, this patch simply removes the ASSERT() altogether, as it is no longer a valid assertion with FMAC. Webrev at: http://cr.opensolaris.org/~sds/privdebug/ diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -365,7 +365,6 @@ if (allzone && !HAS_ALLZONEPRIVS(cr)) { priv_policy_errmsg(cr, PRIV_ALLZONE, msg); } else { - ASSERT(!HAS_PRIVILEGE(cr, priv)); priv_policy_errmsg(cr, priv, msg); } } -- Stephen Smalley National Security Agency From sds at tycho.nsa.gov Tue Nov 25 05:10:31 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 25 Nov 2008 08:10:31 -0500 Subject: [fmac-discuss] [PATCH] add FMAC functions to lint library In-Reply-To: <4927495C.6050504@sun.com> References: <4927495C.6050504@sun.com> Message-ID: <1227618631.683.17.camel@moss-spartans.epoch.ncsc.mil> On Fri, 2008-11-21 at 15:50 -0800, John Weeks wrote: > Add FMAC functions to lint library. > > -John > > diff --git a/usr/src/lib/libc/port/llib-lc b/usr/src/lib/libc/port/llib-lc > --- a/usr/src/lib/libc/port/llib-lc > +++ b/usr/src/lib/libc/port/llib-lc > @@ -160,6 +160,7 @@ > #if defined(__amd64) > #include > #endif > +#include > > /* > * This really comes from the crt*.s startup modules. Acked-by: Stephen Smalley -- Stephen Smalley National Security Agency From john.weeks at sun.com Tue Nov 25 08:21:27 2008 From: john.weeks at sun.com (John Weeks) Date: Tue, 25 Nov 2008 08:21:27 -0800 Subject: [fmac-discuss] [PATCH] Don't trigger an assertion failure when a privilege is denied by FMAC In-Reply-To: <1227618594.683.16.camel@moss-spartans.epoch.ncsc.mil> References: <1227618594.683.16.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492C2607.5080408@sun.com> On 11/25/08 05:09, Stephen Smalley wrote: > The introduction of FMAC restrictive checks for privileges made it > possible for a privilege to be denied even though it is set in the > observed effective privilege set of the process. If a process is run > under privilege debugging via ppriv -D or truss and a privilege is > denied by FMAC that would otherwise be allowed, an ASSERT() in the > existing privilege debugging code that requires the privilege to not be > present in the current privilege set is triggered, thereby panicking the > kernel. One option would be to alter the ASSERT() to take the FMAC > decision into account, like so: > ASSERT(!HAS_PRIVILEGE(cr, priv)||fmac_priv_restrict(cr, priv)); > but this would cause the FMAC check to be applied twice and the decision > could still possibly change between the time of the original check and > this ASSERT() statement as a result of a policy reload. Therefore, this > patch simply removes the ASSERT() altogether, as it is no longer a valid > assertion with FMAC. > > Webrev at: http://cr.opensolaris.org/~sds/privdebug/ Acked-by: John Weeks > > diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c > --- a/usr/src/uts/common/os/policy.c > +++ b/usr/src/uts/common/os/policy.c > @@ -365,7 +365,6 @@ > if (allzone && !HAS_ALLZONEPRIVS(cr)) { > priv_policy_errmsg(cr, PRIV_ALLZONE, msg); > } else { > - ASSERT(!HAS_PRIVILEGE(cr, priv)); > priv_policy_errmsg(cr, priv, msg); > } > } > > From sds at tycho.nsa.gov Tue Nov 25 13:04:11 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Tue, 25 Nov 2008 16:04:11 -0500 Subject: [fmac-discuss] [RFC][PATCH] Mediate setting/clearing of file flags Message-ID: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> This patch modifies secpolicy_xvattr() to enable FMAC to grant or restrict the setting of security-relevant file flags (immutable, append-only, nounlink, ...). This case differs in some interesting ways from the secpolicy_vnode_access() example. The existing secpolicy_xvattr() function uses the ATTR_FLAG_PRIV() macro in order to apply a privilege check when setting one of these file flags. If the flag value is being set (value is non-zero), then the PRIV_FILE_FLAG_SET privilege is checked. Otherwise, if the flag value is being cleared, all privileges (PRIV_ALL) are required. This is apparently to prevent privilege escalation by e.g. clearing the immutable flag on a security-critical file and then modifying it. However, this approach is rather limiting in its flexibility and prevents full least privilege from being enforced. The patch introduces a PRIV_FILE_FLAG_CLR privilege to control clearing of the flags and replaces the use of PRIV_ALL in ATTR_FLAG_PRIV() with this new privilege. This enables the restrictive privilege checks by FMAC to further restrict setting or clearing of the file flags. It also changes legacy privilege check from requiring all privileges to be set in the observed effective set to only requiring this new privilege; if preserving the old behavior is a hard requirement, then HAS_PRIVILEGE() could be changed to treat PRIV_FILE_FLAG_CLR the same as PRIV_ALL so that the legacy privilege check is unaffected and only FMAC would leverage the finer granularity of the new privilege. However, I'd prefer to not have to diverge the logic in that manner. To enable FMAC to grant setting or clearing of the file flags on a per-file basis when it would normally be denied, ATTR_FLAG_PRIV() is changed to call a new fmac_xvattr() hook function, passing the result of the PRIV_POLICY() call as an argument. If the base privilege check failed, then this hook function will performs a file_flag_set or a file_flag_clr permission check between the process and file security contexts. As an implementation detail, the logic common to both the new fmac_xvattr() function and the existing fmac_vnode_priv_access() function is moved into a common helper function, fmac_vnode_priv_common(). This function is also altered to always return EPERM as the error code rather than the default EACCES returned by avc_has_perm in order to match the existing privilege check error conventions, which otherwise was confusing ppriv -D, causing it to report an unknown error. Webrev at: http://cr.opensolaris.org/~sds/privxvattr/ diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te --- a/usr/src/cmd/fmac/policy/macros.te +++ b/usr/src/cmd/fmac/policy/macros.te @@ -66,6 +66,12 @@ unix_dgram_socket }') define(`privilege_class_set', `{ priv0 priv1 priv2 }') + +# +# Permissions for granting privileges that would otherwise by denied. +# +define(`file_priv_perms', `{ file_dac_execute file_dac_write file_dac_read file_flag_set file_flag_clr }') +define(`dir_priv_perms', `{ file_dac_search file_dac_write file_dac_read file_flag_set file_flag_clr }') # # Permissions for getting file attributes. @@ -473,7 +479,7 @@ define(`unconfined_domain', ` allow $1 self:privilege_class_set *; allow $1 domain:process *; -allow $1 { file_type unlabeled_t }:dir ~{ file_dac_search file_dac_read file_dac_write }; -allow $1 { file_type unlabeled_t }:file_class_set ~{ file_dac_execute file_dac_read file_dac_write }; +allow $1 { file_type unlabeled_t }:dir ~dir_priv_perms; +allow $1 { file_type unlabeled_t }:file_class_set ~file_priv_perms; allow $1 security_t:security *; ') diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors --- a/usr/src/common/fmac/policy/flask/access_vectors +++ b/usr/src/common/fmac/policy/flask/access_vectors @@ -43,6 +43,8 @@ file_dac_execute file_dac_write file_dac_read + file_flag_set + file_flag_clr open create link diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c --- a/usr/src/uts/common/fmac/fmac.c +++ b/usr/src/uts/common/fmac/fmac.c @@ -733,18 +733,11 @@ return (avc_has_perm(ssecid, tsecid, SECCLASS_PROCESS, perms, NULL)); } -int -fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) +static int +fmac_vnode_priv_common(cred_t *cr, vnode_t *vp, security_class_t sclass, + access_vector_t av, priv_set_t *privs, int err) { - security_id_t cr_secid; - security_class_t sclass; - access_vector_t av; avc_audit_data_t ad; - priv_set_t privs; - - /* If not enabled, just return the legacy policy decision. */ - if (!fmac_enabled) - return (err); /* * If privilege aware, then honor any legacy policy denial @@ -753,7 +746,28 @@ if (err && (CR_FLAGS(cr) & PRIV_AWARE)) return (err); - cr_secid = cr->cr_secid; + /* + * Do not allow FMAC to override the limit set. + */ + if (err && !priv_issubset(privs, &CR_LPRIV(cr))) + return (err); + + AVC_AUDIT_DATA_INIT(&ad, FS); + ad.u.fs.vp = vp; + return (avc_has_perm_strict(cr->cr_secid, vp->v_secid, sclass, + av, &ad) ? EPERM : 0); +} + +int +fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) +{ + security_class_t sclass; + access_vector_t av; + priv_set_t privs; + + /* If not enabled, just return the legacy policy decision. */ + if (!fmac_enabled) + return (err); sclass = fmac_vtype_to_sclass(vp->v_type); if (!sclass) @@ -783,12 +797,40 @@ if (!av) return (err); - if (err && !priv_issubset(&privs, &CR_LPRIV(cr))) + return (fmac_vnode_priv_common((cred_t *)cr, vp, sclass, av, + &privs, err)); +} + +int +fmac_xvattr(cred_t *cr, vnode_t *vp, int priv, int err) +{ + security_class_t sclass; + access_vector_t av; + priv_set_t privs; + + /* If not enabled, just return the legacy policy decision. */ + if (!fmac_enabled) return (err); - AVC_AUDIT_DATA_INIT(&ad, FS); - ad.u.fs.vp = vp; - return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); + /* Only go further if the base privilege check failed. */ + if (err == 0) + return (0); + + sclass = fmac_vtype_to_sclass(vp->v_type); + if (!sclass) + return (err); + + av = 0; + priv_emptyset(&privs); + priv_addset(&privs, priv); + if (priv == PRIV_FILE_FLAG_SET) + av |= FILE__FILE_FLAG_SET; + else { + ASSERT(priv == PRIV_FILE_FLAG_CLR); + av |= FILE__FILE_FLAG_CLR; + } + + return (fmac_vnode_priv_common(cr, vp, sclass, av, &privs, err)); } int diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c --- a/usr/src/uts/common/os/policy.c +++ b/usr/src/uts/common/os/policy.c @@ -1078,9 +1078,13 @@ return (0); } -#define ATTR_FLAG_PRIV(attr, value, cr) \ - PRIV_POLICY(cr, value ? PRIV_FILE_FLAG_SET : PRIV_ALL, \ - B_FALSE, EPERM, NULL) +#define PRIV_FLAG_SET_OR_CLR(value) \ + ((value) ? PRIV_FILE_FLAG_SET : PRIV_FILE_FLAG_CLR) + +#define ATTR_FLAG_PRIV(attr, value, cr, vp) \ + fmac_xvattr(cr, vp, PRIV_FLAG_SET_OR_CLR(value), \ + PRIV_POLICY(cr, PRIV_FLAG_SET_OR_CLR(value), B_FALSE, EPERM, NULL)) + /* * Check privileges for setting xvattr attributes @@ -1113,30 +1117,30 @@ if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) error = ATTR_FLAG_PRIV(XAT_IMMUTABLE, - xoap->xoa_immutable, cr); + xoap->xoa_immutable, cr, vp); if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) error = ATTR_FLAG_PRIV(XAT_NOUNLINK, - xoap->xoa_nounlink, cr); + xoap->xoa_nounlink, cr, vp); if (error == 0 && XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) error = ATTR_FLAG_PRIV(XAT_APPENDONLY, - xoap->xoa_appendonly, cr); + xoap->xoa_appendonly, cr, vp); if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NODUMP)) error = ATTR_FLAG_PRIV(XAT_NODUMP, - xoap->xoa_nodump, cr); + xoap->xoa_nodump, cr, vp); if (error == 0 && XVA_ISSET_REQ(xvap, XAT_OPAQUE)) error = EPERM; if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { error = ATTR_FLAG_PRIV(XAT_AV_QUARANTINED, - xoap->xoa_av_quarantined, cr); + xoap->xoa_av_quarantined, cr, vp); if (error == 0 && vtype != VREG && xoap->xoa_av_quarantined) error = EINVAL; } if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) error = ATTR_FLAG_PRIV(XAT_AV_MODIFIED, - xoap->xoa_av_modified, cr); + xoap->xoa_av_modified, cr, vp); if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { error = ATTR_FLAG_PRIV(XAT_AV_SCANSTAMP, - xoap->xoa_av_scanstamp, cr); + xoap->xoa_av_scanstamp, cr, vp); if (error == 0 && vtype != VREG) error = EINVAL; } diff --git a/usr/src/uts/common/os/priv_defs b/usr/src/uts/common/os/priv_defs --- a/usr/src/uts/common/os/priv_defs +++ b/usr/src/uts/common/os/priv_defs @@ -151,6 +151,11 @@ sensitivity label. This privilege is interpreted only if the system is configured with Trusted Extensions. + +privilege PRIV_FILE_FLAG_CLR + + Allows a process to clear immutable, nounlink or appendonly + file attributes. privilege PRIV_FILE_FLAG_SET diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h --- a/usr/src/uts/common/sys/fmac/fmac.h +++ b/usr/src/uts/common/sys/fmac/fmac.h @@ -107,6 +107,7 @@ access_vector_t perms); int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); int fmac_priv_restrict(const cred_t *cr, int priv); +int fmac_xvattr(cred_t *cr, vnode_t *vp, int priv, int err); #endif /* _KERNEL */ #ifdef __cplusplus -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Wed Nov 26 00:16:29 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Wed, 26 Nov 2008 00:16:29 -0800 Subject: [fmac-discuss] [RFC][PATCH] Mediate setting/clearing of file flags In-Reply-To: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> References: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492D05DD.1060902@sun.com> Stephen Smalley wrote: > This patch modifies secpolicy_xvattr() to enable FMAC to grant or > restrict the setting of security-relevant file flags (immutable, > append-only, nounlink, ...). This case differs in some interesting ways > from the secpolicy_vnode_access() example. > > The existing secpolicy_xvattr() function uses the ATTR_FLAG_PRIV() macro > in order to apply a privilege check when setting one of these file > flags. If the flag value is being set (value is non-zero), then the > PRIV_FILE_FLAG_SET privilege is checked. Otherwise, if the flag value > is being cleared, all privileges (PRIV_ALL) are required. This is > apparently to prevent privilege escalation by e.g. clearing the > immutable flag on a security-critical file and then modifying it. > However, this approach is rather limiting in its flexibility and > prevents full least privilege from being enforced. > > The patch introduces a PRIV_FILE_FLAG_CLR privilege to control clearing > of the flags and replaces the use of PRIV_ALL in ATTR_FLAG_PRIV() with > this new privilege. This enables the restrictive privilege checks by > FMAC to further restrict setting or clearing of the file flags. It also > changes legacy privilege check from requiring all privileges to be set > in the observed effective set to only requiring this new privilege; if > preserving the old behavior is a hard requirement, then HAS_PRIVILEGE() > could be changed to treat PRIV_FILE_FLAG_CLR the same as PRIV_ALL so > that the legacy privilege check is unaffected and only FMAC would > leverage the finer granularity of the new privilege. However, I'd > prefer to not have to diverge the logic in that manner. > Backward compatibility should not be required in the case. The new privilege PRIV_FILE_FLAG_SET and the related attributes it covers are only implemented in OpenSolaris, not Solaris 10. This code was part of the implementation of the cifs_server project, http://www.opensolaris.org/os/project/cifs-server/ where I think we have some flexibility in terms of privilege requirements. Because you are proposing a new privilege, PRIV_FILE_FLAG_CLR, this change should be integrated into OpenSolaris sooner, rather than waiting for the completion of the FMAC project. > To enable FMAC to grant setting or clearing of the file flags on a > per-file basis when it would normally be denied, ATTR_FLAG_PRIV() is > changed to call a new fmac_xvattr() hook function, passing the result of > the PRIV_POLICY() call as an argument. If the base privilege check > failed, then this hook function will performs a file_flag_set or a > file_flag_clr permission check between the process and file security > contexts. As an implementation detail, the logic common to both the new > fmac_xvattr() function and the existing fmac_vnode_priv_access() > function is moved into a common helper function, > fmac_vnode_priv_common(). This function is also altered to always > return EPERM as the error code rather than the default EACCES returned > by avc_has_perm in order to match the existing privilege check error > conventions, which otherwise was confusing ppriv -D, causing it to > report an unknown error. > > > These changes look good to me. --Glenn From sds at tycho.nsa.gov Wed Nov 26 04:26:05 2008 From: sds at tycho.nsa.gov (Stephen Smalley) Date: Wed, 26 Nov 2008 07:26:05 -0500 Subject: [fmac-discuss] [RFC][PATCH] Mediate setting/clearing of file flags In-Reply-To: <492D05DD.1060902@sun.com> References: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> <492D05DD.1060902@sun.com> Message-ID: <1227702365.5891.5.camel@moss-spartans.epoch.ncsc.mil> On Wed, 2008-11-26 at 00:16 -0800, Glenn Faden wrote: > Stephen Smalley wrote: > > This patch modifies secpolicy_xvattr() to enable FMAC to grant or > > restrict the setting of security-relevant file flags (immutable, > > append-only, nounlink, ...). This case differs in some interesting ways > > from the secpolicy_vnode_access() example. > > > > The existing secpolicy_xvattr() function uses the ATTR_FLAG_PRIV() macro > > in order to apply a privilege check when setting one of these file > > flags. If the flag value is being set (value is non-zero), then the > > PRIV_FILE_FLAG_SET privilege is checked. Otherwise, if the flag value > > is being cleared, all privileges (PRIV_ALL) are required. This is > > apparently to prevent privilege escalation by e.g. clearing the > > immutable flag on a security-critical file and then modifying it. > > However, this approach is rather limiting in its flexibility and > > prevents full least privilege from being enforced. > > > > The patch introduces a PRIV_FILE_FLAG_CLR privilege to control clearing > > of the flags and replaces the use of PRIV_ALL in ATTR_FLAG_PRIV() with > > this new privilege. This enables the restrictive privilege checks by > > FMAC to further restrict setting or clearing of the file flags. It also > > changes legacy privilege check from requiring all privileges to be set > > in the observed effective set to only requiring this new privilege; if > > preserving the old behavior is a hard requirement, then HAS_PRIVILEGE() > > could be changed to treat PRIV_FILE_FLAG_CLR the same as PRIV_ALL so > > that the legacy privilege check is unaffected and only FMAC would > > leverage the finer granularity of the new privilege. However, I'd > > prefer to not have to diverge the logic in that manner. > > > Backward compatibility should not be required in the case. The new > privilege PRIV_FILE_FLAG_SET and the related attributes it covers are > only implemented in OpenSolaris, not Solaris 10. This code was part of > the implementation of the cifs_server project, > http://www.opensolaris.org/os/project/cifs-server/ where I think we have > some flexibility in terms of privilege requirements. > > Because you are proposing a new privilege, PRIV_FILE_FLAG_CLR, this > change should be integrated into OpenSolaris sooner, rather than waiting > for the completion of the FMAC project. Ok, how do we arrange for that to happen? I also expect to replace other occurrences of PRIV_ALL and PRIV_FULLSET with specific privilege checks, at least as far as FMAC is concerned. Examples include secpolicy_zone_config(), secpolicy_modctl(), secpolicy_ucode_update(), etc. So I don't know if we should do this piecemeal or try to make a complete pass over all cases and then submit the entire set of new privileges. > > To enable FMAC to grant setting or clearing of the file flags on a > > per-file basis when it would normally be denied, ATTR_FLAG_PRIV() is > > changed to call a new fmac_xvattr() hook function, passing the result of > > the PRIV_POLICY() call as an argument. If the base privilege check > > failed, then this hook function will performs a file_flag_set or a > > file_flag_clr permission check between the process and file security > > contexts. As an implementation detail, the logic common to both the new > > fmac_xvattr() function and the existing fmac_vnode_priv_access() > > function is moved into a common helper function, > > fmac_vnode_priv_common(). This function is also altered to always > > return EPERM as the error code rather than the default EACCES returned > > by avc_has_perm in order to match the existing privilege check error > > conventions, which otherwise was confusing ppriv -D, causing it to > > report an unknown error. > > > > > > > These changes look good to me. > > --Glenn > _______________________________________________ > fmac-discuss mailing list > fmac-discuss at opensolaris.org > http://mail.opensolaris.org/mailman/listinfo/fmac-discuss -- Stephen Smalley National Security Agency From Glenn.Faden at Sun.COM Wed Nov 26 06:16:27 2008 From: Glenn.Faden at Sun.COM (Glenn Faden) Date: Wed, 26 Nov 2008 06:16:27 -0800 Subject: [fmac-discuss] [RFC][PATCH] Mediate setting/clearing of file flags In-Reply-To: <1227702365.5891.5.camel@moss-spartans.epoch.ncsc.mil> References: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> <492D05DD.1060902@sun.com> <1227702365.5891.5.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492D5A3B.6020508@sun.com> Stephen Smalley wrote: > On Wed, 2008-11-26 at 00:16 -0800, Glenn Faden wrote: > >> Stephen Smalley wrote: >> >>> This patch modifies secpolicy_xvattr() to enable FMAC to grant or >>> restrict the setting of security-relevant file flags (immutable, >>> append-only, nounlink, ...). This case differs in some interesting ways >>> from the secpolicy_vnode_access() example. >>> >>> The existing secpolicy_xvattr() function uses the ATTR_FLAG_PRIV() macro >>> in order to apply a privilege check when setting one of these file >>> flags. If the flag value is being set (value is non-zero), then the >>> PRIV_FILE_FLAG_SET privilege is checked. Otherwise, if the flag value >>> is being cleared, all privileges (PRIV_ALL) are required. This is >>> apparently to prevent privilege escalation by e.g. clearing the >>> immutable flag on a security-critical file and then modifying it. >>> However, this approach is rather limiting in its flexibility and >>> prevents full least privilege from being enforced. >>> >>> The patch introduces a PRIV_FILE_FLAG_CLR privilege to control clearing >>> of the flags and replaces the use of PRIV_ALL in ATTR_FLAG_PRIV() with >>> this new privilege. This enables the restrictive privilege checks by >>> FMAC to further restrict setting or clearing of the file flags. It also >>> changes legacy privilege check from requiring all privileges to be set >>> in the observed effective set to only requiring this new privilege; if >>> preserving the old behavior is a hard requirement, then HAS_PRIVILEGE() >>> could be changed to treat PRIV_FILE_FLAG_CLR the same as PRIV_ALL so >>> that the legacy privilege check is unaffected and only FMAC would >>> leverage the finer granularity of the new privilege. However, I'd >>> prefer to not have to diverge the logic in that manner. >>> >>> >> Backward compatibility should not be required in the case. The new >> privilege PRIV_FILE_FLAG_SET and the related attributes it covers are >> only implemented in OpenSolaris, not Solaris 10. This code was part of >> the implementation of the cifs_server project, >> http://www.opensolaris.org/os/project/cifs-server/ where I think we have >> some flexibility in terms of privilege requirements. >> >> Because you are proposing a new privilege, PRIV_FILE_FLAG_CLR, this >> change should be integrated into OpenSolaris sooner, rather than waiting >> for the completion of the FMAC project. >> > > Ok, how do we arrange for that to happen? > I also expect to replace other occurrences of PRIV_ALL and PRIV_FULLSET > with specific privilege checks, at least as far as FMAC is concerned. > Examples include secpolicy_zone_config(), secpolicy_modctl(), > secpolicy_ucode_update(), etc. So I don't know if we should do this > piecemeal or try to make a complete pass over all cases and then submit > the entire set of new privileges. > It would be best to make a complete pass over the privilege-related changes and then write them up a proposal which we can submit to PSARC. --Glenn From john.weeks at sun.com Wed Nov 26 07:46:57 2008 From: john.weeks at sun.com (John Weeks) Date: Wed, 26 Nov 2008 07:46:57 -0800 Subject: [fmac-discuss] [RFC][PATCH] Mediate setting/clearing of file flags In-Reply-To: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> References: <1227647051.683.99.camel@moss-spartans.epoch.ncsc.mil> Message-ID: <492D6F71.5090207@sun.com> On 11/25/08 13:04, Stephen Smalley wrote: > This patch modifies secpolicy_xvattr() to enable FMAC to grant or > restrict the setting of security-relevant file flags (immutable, > append-only, nounlink, ...). This case differs in some interesting ways > from the secpolicy_vnode_access() example. > > The existing secpolicy_xvattr() function uses the ATTR_FLAG_PRIV() macro > in order to apply a privilege check when setting one of these file > flags. If the flag value is being set (value is non-zero), then the > PRIV_FILE_FLAG_SET privilege is checked. Otherwise, if the flag value > is being cleared, all privileges (PRIV_ALL) are required. This is > apparently to prevent privilege escalation by e.g. clearing the > immutable flag on a security-critical file and then modifying it. > However, this approach is rather limiting in its flexibility and > prevents full least privilege from being enforced. > > The patch introduces a PRIV_FILE_FLAG_CLR privilege to control clearing > of the flags and replaces the use of PRIV_ALL in ATTR_FLAG_PRIV() with > this new privilege. This enables the restrictive privilege checks by > FMAC to further restrict setting or clearing of the file flags. It also > changes legacy privilege check from requiring all privileges to be set > in the observed effective set to only requiring this new privilege; if > preserving the old behavior is a hard requirement, then HAS_PRIVILEGE() > could be changed to treat PRIV_FILE_FLAG_CLR the same as PRIV_ALL so > that the legacy privilege check is unaffected and only FMAC would > leverage the finer granularity of the new privilege. However, I'd > prefer to not have to diverge the logic in that manner. > > To enable FMAC to grant setting or clearing of the file flags on a > per-file basis when it would normally be denied, ATTR_FLAG_PRIV() is > changed to call a new fmac_xvattr() hook function, passing the result of > the PRIV_POLICY() call as an argument. If the base privilege check > failed, then this hook function will performs a file_flag_set or a > file_flag_clr permission check between the process and file security > contexts. As an implementation detail, the logic common to both the new > fmac_xvattr() function and the existing fmac_vnode_priv_access() > function is moved into a common helper function, > fmac_vnode_priv_common(). This function is also altered to always > return EPERM as the error code rather than the default EACCES returned > by avc_has_perm in order to match the existing privilege check error > conventions, which otherwise was confusing ppriv -D, causing it to > report an unknown error. > > Webrev at: http://cr.opensolaris.org/~sds/privxvattr/ Acked-by: John Weeks > > diff --git a/usr/src/cmd/fmac/policy/macros.te b/usr/src/cmd/fmac/policy/macros.te > --- a/usr/src/cmd/fmac/policy/macros.te > +++ b/usr/src/cmd/fmac/policy/macros.te > @@ -66,6 +66,12 @@ > unix_dgram_socket }') > > define(`privilege_class_set', `{ priv0 priv1 priv2 }') > + > +# > +# Permissions for granting privileges that would otherwise by denied. > +# > +define(`file_priv_perms', `{ file_dac_execute file_dac_write file_dac_read file_flag_set file_flag_clr }') > +define(`dir_priv_perms', `{ file_dac_search file_dac_write file_dac_read file_flag_set file_flag_clr }') > > # > # Permissions for getting file attributes. > @@ -473,7 +479,7 @@ > define(`unconfined_domain', ` > allow $1 self:privilege_class_set *; > allow $1 domain:process *; > -allow $1 { file_type unlabeled_t }:dir ~{ file_dac_search file_dac_read file_dac_write }; > -allow $1 { file_type unlabeled_t }:file_class_set ~{ file_dac_execute file_dac_read file_dac_write }; > +allow $1 { file_type unlabeled_t }:dir ~dir_priv_perms; > +allow $1 { file_type unlabeled_t }:file_class_set ~file_priv_perms; > allow $1 security_t:security *; > ') > diff --git a/usr/src/common/fmac/policy/flask/access_vectors b/usr/src/common/fmac/policy/flask/access_vectors > --- a/usr/src/common/fmac/policy/flask/access_vectors > +++ b/usr/src/common/fmac/policy/flask/access_vectors > @@ -43,6 +43,8 @@ > file_dac_execute > file_dac_write > file_dac_read > + file_flag_set > + file_flag_clr > open > create > link > diff --git a/usr/src/uts/common/fmac/fmac.c b/usr/src/uts/common/fmac/fmac.c > --- a/usr/src/uts/common/fmac/fmac.c > +++ b/usr/src/uts/common/fmac/fmac.c > @@ -733,18 +733,11 @@ > return (avc_has_perm(ssecid, tsecid, SECCLASS_PROCESS, perms, NULL)); > } > > -int > -fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) > +static int > +fmac_vnode_priv_common(cred_t *cr, vnode_t *vp, security_class_t sclass, > + access_vector_t av, priv_set_t *privs, int err) > { > - security_id_t cr_secid; > - security_class_t sclass; > - access_vector_t av; > avc_audit_data_t ad; > - priv_set_t privs; > - > - /* If not enabled, just return the legacy policy decision. */ > - if (!fmac_enabled) > - return (err); > > /* > * If privilege aware, then honor any legacy policy denial > @@ -753,7 +746,28 @@ > if (err && (CR_FLAGS(cr) & PRIV_AWARE)) > return (err); > > - cr_secid = cr->cr_secid; > + /* > + * Do not allow FMAC to override the limit set. > + */ > + if (err && !priv_issubset(privs, &CR_LPRIV(cr))) > + return (err); > + > + AVC_AUDIT_DATA_INIT(&ad, FS); > + ad.u.fs.vp = vp; > + return (avc_has_perm_strict(cr->cr_secid, vp->v_secid, sclass, > + av, &ad) ? EPERM : 0); > +} > + > +int > +fmac_vnode_priv_access(const cred_t *cr, vnode_t *vp, int mode, int err) > +{ > + security_class_t sclass; > + access_vector_t av; > + priv_set_t privs; > + > + /* If not enabled, just return the legacy policy decision. */ > + if (!fmac_enabled) > + return (err); > > sclass = fmac_vtype_to_sclass(vp->v_type); > if (!sclass) > @@ -783,12 +797,40 @@ > if (!av) > return (err); > > - if (err && !priv_issubset(&privs, &CR_LPRIV(cr))) > + return (fmac_vnode_priv_common((cred_t *)cr, vp, sclass, av, > + &privs, err)); > +} > + > +int > +fmac_xvattr(cred_t *cr, vnode_t *vp, int priv, int err) > +{ > + security_class_t sclass; > + access_vector_t av; > + priv_set_t privs; > + > + /* If not enabled, just return the legacy policy decision. */ > + if (!fmac_enabled) > return (err); > > - AVC_AUDIT_DATA_INIT(&ad, FS); > - ad.u.fs.vp = vp; > - return (avc_has_perm_strict(cr_secid, vp->v_secid, sclass, av, &ad)); > + /* Only go further if the base privilege check failed. */ > + if (err == 0) > + return (0); > + > + sclass = fmac_vtype_to_sclass(vp->v_type); > + if (!sclass) > + return (err); > + > + av = 0; > + priv_emptyset(&privs); > + priv_addset(&privs, priv); > + if (priv == PRIV_FILE_FLAG_SET) > + av |= FILE__FILE_FLAG_SET; > + else { > + ASSERT(priv == PRIV_FILE_FLAG_CLR); > + av |= FILE__FILE_FLAG_CLR; > + } > + > + return (fmac_vnode_priv_common(cr, vp, sclass, av, &privs, err)); > } > > int > diff --git a/usr/src/uts/common/os/policy.c b/usr/src/uts/common/os/policy.c > --- a/usr/src/uts/common/os/policy.c > +++ b/usr/src/uts/common/os/policy.c > @@ -1078,9 +1078,13 @@ > return (0); > } > > -#define ATTR_FLAG_PRIV(attr, value, cr) \ > - PRIV_POLICY(cr, value ? PRIV_FILE_FLAG_SET : PRIV_ALL, \ > - B_FALSE, EPERM, NULL) > +#define PRIV_FLAG_SET_OR_CLR(value) \ > + ((value) ? PRIV_FILE_FLAG_SET : PRIV_FILE_FLAG_CLR) > + > +#define ATTR_FLAG_PRIV(attr, value, cr, vp) \ > + fmac_xvattr(cr, vp, PRIV_FLAG_SET_OR_CLR(value), \ > + PRIV_POLICY(cr, PRIV_FLAG_SET_OR_CLR(value), B_FALSE, EPERM, NULL)) > + > > /* > * Check privileges for setting xvattr attributes > @@ -1113,30 +1117,30 @@ > > if (XVA_ISSET_REQ(xvap, XAT_IMMUTABLE)) > error = ATTR_FLAG_PRIV(XAT_IMMUTABLE, > - xoap->xoa_immutable, cr); > + xoap->xoa_immutable, cr, vp); > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) > error = ATTR_FLAG_PRIV(XAT_NOUNLINK, > - xoap->xoa_nounlink, cr); > + xoap->xoa_nounlink, cr, vp); > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_APPENDONLY)) > error = ATTR_FLAG_PRIV(XAT_APPENDONLY, > - xoap->xoa_appendonly, cr); > + xoap->xoa_appendonly, cr, vp); > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_NODUMP)) > error = ATTR_FLAG_PRIV(XAT_NODUMP, > - xoap->xoa_nodump, cr); > + xoap->xoa_nodump, cr, vp); > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_OPAQUE)) > error = EPERM; > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_QUARANTINED)) { > error = ATTR_FLAG_PRIV(XAT_AV_QUARANTINED, > - xoap->xoa_av_quarantined, cr); > + xoap->xoa_av_quarantined, cr, vp); > if (error == 0 && vtype != VREG && xoap->xoa_av_quarantined) > error = EINVAL; > } > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_MODIFIED)) > error = ATTR_FLAG_PRIV(XAT_AV_MODIFIED, > - xoap->xoa_av_modified, cr); > + xoap->xoa_av_modified, cr, vp); > if (error == 0 && XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) { > error = ATTR_FLAG_PRIV(XAT_AV_SCANSTAMP, > - xoap->xoa_av_scanstamp, cr); > + xoap->xoa_av_scanstamp, cr, vp); > if (error == 0 && vtype != VREG) > error = EINVAL; > } > diff --git a/usr/src/uts/common/os/priv_defs b/usr/src/uts/common/os/priv_defs > --- a/usr/src/uts/common/os/priv_defs > +++ b/usr/src/uts/common/os/priv_defs > @@ -151,6 +151,11 @@ > sensitivity label. > This privilege is interpreted only if the system is configured > with Trusted Extensions. > + > +privilege PRIV_FILE_FLAG_CLR > + > + Allows a process to clear immutable, nounlink or appendonly > + file attributes. > > privilege PRIV_FILE_FLAG_SET > > diff --git a/usr/src/uts/common/sys/fmac/fmac.h b/usr/src/uts/common/sys/fmac/fmac.h > --- a/usr/src/uts/common/sys/fmac/fmac.h > +++ b/usr/src/uts/common/sys/fmac/fmac.h > @@ -107,6 +107,7 @@ > access_vector_t perms); > int fmac_vnode_priv_access(const cred_t *, vnode_t *, int, int); > int fmac_priv_restrict(const cred_t *cr, int priv); > +int fmac_xvattr(cred_t *cr, vnode_t *vp, int priv, int err); > #endif /* _KERNEL */ > > #ifdef __cplusplus > >