From ec031d49239e57b45533903e045aebb509b6f768 Mon Sep 17 00:00:00 2001 From: rwatson Date: Mon, 21 Oct 2002 16:35:54 +0000 Subject: [PATCH] Cleanup of relabel authorization checks -- almost identical logic, we just break out some of the tests better. Minor change in that we now better support incremental update of labels. Obtained from: TrustedBSD Project Sponsored by: DARPA, Network Associates Laboratories --- sys/security/mac_biba/mac_biba.c | 261 +++++++++++++++++++++++------- sys/security/mac_mls/mac_mls.c | 264 ++++++++++++++++++++++++------- 2 files changed, 409 insertions(+), 116 deletions(-) diff --git a/sys/security/mac_biba/mac_biba.c b/sys/security/mac_biba/mac_biba.c index d7349f38f3c1..6a394a8c9495 100644 --- a/sys/security/mac_biba/mac_biba.c +++ b/sys/security/mac_biba/mac_biba.c @@ -141,6 +141,15 @@ biba_free(struct mac_biba *mac_biba) atomic_add_int(&destroyed_not_inited, 1); } +static int +biba_atmostflags(struct mac_biba *mac_biba, int flags) +{ + + if ((mac_biba->mb_flags & flags) != mac_biba->mb_flags) + return (EINVAL); + return (0); +} + static int mac_biba_dominate_element(struct mac_biba_element *a, struct mac_biba_element *b) @@ -249,6 +258,50 @@ mac_biba_equal_single(struct mac_biba *a, struct mac_biba *b) return (mac_biba_equal_element(&a->mb_single, &b->mb_single)); } +static int +mac_biba_contains_equal(struct mac_biba *mac_biba) +{ + + if (mac_biba->mb_flags & MAC_BIBA_FLAG_SINGLE) + if (mac_biba->mb_single.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + + if (mac_biba->mb_flags & MAC_BIBA_FLAG_RANGE) { + if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + if (mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (1); + } + + return (0); +} + +static int +mac_biba_subject_equal_ok(struct mac_biba *mac_biba) +{ + + KASSERT((mac_biba->mb_flags & MAC_BIBA_FLAGS_BOTH) == + MAC_BIBA_FLAGS_BOTH, + ("mac_biba_subject_equal_ok: subject doesn't have both labels")); + + /* If the single is EQUAL, it's ok. */ + if (mac_biba->mb_single.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (0); + + /* If either range endpoint is EQUAL, it's ok. */ + if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_EQUAL || + mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_EQUAL) + return (0); + + /* If the range is low-high, it's ok. */ + if (mac_biba->mb_rangelow.mbe_type == MAC_BIBA_TYPE_LOW && + mac_biba->mb_rangehigh.mbe_type == MAC_BIBA_TYPE_HIGH) + return (0); + + /* It's not ok. */ + return (EPERM); +} + static int mac_biba_valid(struct mac_biba *mac_biba) { @@ -1029,36 +1082,56 @@ static int mac_biba_check_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_biba *subj, *new; + int error; subj = SLOT(&cred->cr_label); new = SLOT(newlabel); - if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAGS_BOTH) - return (EINVAL); + /* + * If there is a Biba label update for the credential, it may + * be an update of the single, range, or both. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); + if (error) + return (error); /* - * XXX: Allow processes with root privilege to set labels outside - * their range, so suid things like "su" work. This WILL go away - * when we figure out the 'correct' solution... + * If the Biba label is to be changed, authorize as appropriate. */ - if (!suser_cred(cred, 0)) - return (0); + if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { + /* + * To change the Biba single label on a credential, the + * new single label must be in the current range. + */ + if (new->mb_flags & MAC_BIBA_FLAG_SINGLE && + !mac_biba_single_in_range(new, subj)) + return (EPERM); - /* - * The new single must be in the old range. - */ - if (!mac_biba_single_in_range(new, subj)) - return (EPERM); + /* + * To change the Biba range on a credential, the new + * range label must be in the current range. + */ + if (new->mb_flags & MAC_BIBA_FLAG_RANGE && + !mac_biba_range_in_range(new, subj)) + return (EPERM); - /* - * The new range must be in the old range. - */ - if (!mac_biba_range_in_range(new, subj)) - return (EPERM); + /* + * To have EQUAL in any component of the new credential + * Biba label, the subject must already have EQUAL in + * their label. + */ + if (mac_biba_contains_equal(new)) { + error = mac_biba_subject_equal_ok(subj); + if (error) + return (error); + } - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * XXXMAC: Additional consistency tests regarding the + * single and range of the new label might be performed + * here. + */ + } return (0); } @@ -1086,14 +1159,39 @@ mac_biba_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_biba *subj, *new; + int error; subj = SLOT(&cred->cr_label); new = SLOT(newlabel); - if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAGS_BOTH) - return (EINVAL); + /* + * If there is a Biba label update for the interface, it may + * be an update of the single, range, or both. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAGS_BOTH); + if (error) + return (error); - return (suser_cred(cred, 0)); + /* + * If the Biba label is to be changed, authorize as appropriate. + */ + if (new->mb_flags & MAC_BIBA_FLAGS_BOTH) { + /* + * Rely on the traditional superuser status for the Biba + * interface relabel requirements. XXXMAC: This will go + * away. + */ + error = suser_cred(cred, 0); + if (error) + return (EPERM); + + /* + * XXXMAC: Additional consistency tests regarding the single + * and the range of the new label might be performed here. + */ + } + + return (0); } static int @@ -1183,31 +1281,48 @@ mac_biba_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; + int error; new = SLOT(newlabel); subj = SLOT(&cred->cr_label); obj = SLOT(pipelabel); - if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_SINGLE) - return (EINVAL); + /* + * If there is a Biba label update for a pipe, it must be a + * single update. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a pipe, the old pipe label must be in the subject - * range. + * To perform a relabel of a pipe (Biba label or not), Biba must + * authorize the relabel. */ if (!mac_biba_single_in_range(obj, subj)) return (EPERM); /* - * To relabel a pipe, the new pipe label must be in the subject - * range. + * If the Biba label is to be changed, authorize as appropriate. */ - if (!mac_biba_single_in_range(new, subj)) - return (EPERM); + if (new->mb_flags & MAC_BIBA_FLAG_SINGLE) { + /* + * To change the Biba label on a pipe, the new pipe label + * must be in the subject range. + */ + if (!mac_biba_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the Biba label on a pipe to be EQUAL, the + * subject must have appropriate privilege. + */ + if (mac_biba_contains_equal(new)) { + error = mac_biba_subject_equal_ok(subj); + if (error) + return (error); + } + } return (0); } @@ -1328,31 +1443,48 @@ mac_biba_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_biba *subj, *obj, *new; + int error; new = SLOT(newlabel); subj = SLOT(&cred->cr_label); obj = SLOT(socketlabel); - if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_SINGLE) - return (EINVAL); + /* + * If there is a Biba label update for the socket, it may be + * an update of single. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a socket, the old socket label must be in the subject + * To relabel a socket, the old socket single must be in the subject * range. */ if (!mac_biba_single_in_range(obj, subj)) return (EPERM); /* - * To relabel a socket, the new socket label must be in the subject - * range. + * If the Biba label is to be changed, authorize as appropriate. */ - if (!mac_biba_single_in_range(new, subj)) - return (EPERM); + if (new->mb_flags & MAC_BIBA_FLAG_SINGLE) { + /* + * To relabel a socket, the new socket single must be in + * the subject range. + */ + if (!mac_biba_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the Biba label on the socket to contain EQUAL, + * the subject must have appropriate privilege. + */ + if (mac_biba_contains_equal(new)) { + error = mac_biba_subject_equal_ok(subj); + if (error) + return (error); + } + } return (0); } @@ -1702,33 +1834,50 @@ mac_biba_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel) { struct mac_biba *old, *new, *subj; + int error; old = SLOT(vnodelabel); new = SLOT(newlabel); subj = SLOT(&cred->cr_label); - if ((new->mb_flags & MAC_BIBA_FLAGS_BOTH) != MAC_BIBA_FLAG_SINGLE) - return (EINVAL); + /* + * If there is a Biba label update for the vnode, it must be a + * single label. + */ + error = biba_atmostflags(new, MAC_BIBA_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a vnode, the old vnode label must be in the subject - * range. + * To perform a relabel of the vnode (Biba label or not), Biba must + * authorize the relabel. */ if (!mac_biba_single_in_range(old, subj)) return (EPERM); /* - * To relabel a vnode, the new vnode label must be in the subject - * range. + * If the Biba label is to be changed, authorize as appropriate. */ - if (!mac_biba_single_in_range(new, subj)) - return (EPERM); + if (new->mb_flags & MAC_BIBA_FLAG_SINGLE) { + /* + * To change the Biba label on a vnode, the new vnode label + * must be in the subject range. + */ + if (!mac_biba_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the Biba label on the vnode to be EQUAL, + * the subject must have appropriate privilege. + */ + if (mac_biba_contains_equal(new)) { + error = mac_biba_subject_equal_ok(subj); + if (error) + return (error); + } + } - return (suser_cred(cred, 0)); + return (0); } static int diff --git a/sys/security/mac_mls/mac_mls.c b/sys/security/mac_mls/mac_mls.c index 752a7adfd10d..345592baa0e9 100644 --- a/sys/security/mac_mls/mac_mls.c +++ b/sys/security/mac_mls/mac_mls.c @@ -130,6 +130,15 @@ mls_free(struct mac_mls *mac_mls) atomic_add_int(&destroyed_not_inited, 1); } +static int +mls_atmostflags(struct mac_mls *mac_mls, int flags) +{ + + if ((mac_mls->mm_flags & flags) != mac_mls->mm_flags) + return (EINVAL); + return (0); +} + static int mac_mls_dominate_element(struct mac_mls_element *a, struct mac_mls_element *b) @@ -238,6 +247,49 @@ mac_mls_equal_single(struct mac_mls *a, struct mac_mls *b) return (mac_mls_equal_element(&a->mm_single, &b->mm_single)); } +static int +mac_mls_contains_equal(struct mac_mls *mac_mls) +{ + + if (mac_mls->mm_flags & MAC_MLS_FLAG_SINGLE) + if (mac_mls->mm_single.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + + if (mac_mls->mm_flags & MAC_MLS_FLAG_RANGE) { + if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + if (mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) + return (1); + } + + return (0); +} + +static int +mac_mls_subject_equal_ok(struct mac_mls *mac_mls) +{ + + KASSERT((mac_mls->mm_flags & MAC_MLS_FLAGS_BOTH) == MAC_MLS_FLAGS_BOTH, + ("mac_mls_subject_equal_ok: subject doesn't have both labels")); + + /* If the single is EQUAL, it's ok. */ + if (mac_mls->mm_single.mme_type == MAC_MLS_TYPE_EQUAL) + return (0); + + /* If either range endpoint is EQUAL, it's ok. */ + if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_EQUAL || + mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_EQUAL) + return (0); + + /* If the range is low-high, it's ok. */ + if (mac_mls->mm_rangelow.mme_type == MAC_MLS_TYPE_LOW && + mac_mls->mm_rangehigh.mme_type == MAC_MLS_TYPE_HIGH) + return (0); + + /* It's not ok. */ + return (EPERM); +} + static int mac_mls_valid(struct mac_mls *mac_mls) { @@ -985,41 +1037,59 @@ static int mac_mls_check_cred_relabel(struct ucred *cred, struct label *newlabel) { struct mac_mls *subj, *new; + int error; subj = SLOT(&cred->cr_label); new = SLOT(newlabel); - if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAGS_BOTH) - return (EINVAL); + /* + * If there is an MLS label update for the credential, it may be + * an update of single, range, or both. + */ + error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); + if (error) + return (error); /* - * XXX: Allow processes with root privilege to set labels outside - * their range, so suid things like "su" work. This WILL go away - * when we figure out the 'correct' solution... + * If the MLS label is to be changed, authorize as appropriate. */ - if (!suser_cred(cred, 0)) - return (0); + if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { + /* + * To change the MLS single label on a credential, the + * new single label must be in the current range. + */ + if (new->mm_flags & MAC_MLS_FLAG_SINGLE && + !mac_mls_single_in_range(new, subj)) + return (EPERM); - /* - * The new single must be in the old range. - */ - if (!mac_mls_single_in_range(new, subj)) - return (EPERM); + /* + * To change the MLS range label on a credential, the + * new range label must be in the current range. + */ + if (new->mm_flags & MAC_MLS_FLAG_RANGE && + !mac_mls_range_in_range(new, subj)) + return (EPERM); - /* - * The new range must be in the old range. - */ - if (!mac_mls_range_in_range(new, subj)) - return (EPERM); + /* + * To have EQUAL in any component of the new credential + * MLS label, the subject must already have EQUAL in + * their label. + */ + if (mac_mls_contains_equal(new)) { + error = mac_mls_subject_equal_ok(subj); + if (error) + return (error); + } - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * XXXMAC: Additional consistency tests regarding the single + * and range of the new label might be performed here. + */ + } return (0); } - static int mac_mls_check_cred_visible(struct ucred *u1, struct ucred *u2) { @@ -1043,16 +1113,39 @@ mac_mls_check_ifnet_relabel(struct ucred *cred, struct ifnet *ifnet, struct label *ifnetlabel, struct label *newlabel) { struct mac_mls *subj, *new; + int error; subj = SLOT(&cred->cr_label); new = SLOT(newlabel); - if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAGS_BOTH) - return (EINVAL); + /* + * If there is an MLS label update for the interface, it may + * be an update of single, range, or both. + */ + error = mls_atmostflags(new, MAC_MLS_FLAGS_BOTH); + if (error) + return (error); - /* XXX: privilege model here? */ + /* + * If the MLS label is to be changed, authorize as appropriate. + */ + if (new->mm_flags & MAC_MLS_FLAGS_BOTH) { + /* + * Rely on traditional superuser status for the MLS + * interface relabel requirements. XXX: This will go + * away. + */ + error = suser_cred(cred, 0); + if (error) + return (EPERM); - return (suser_cred(cred, 0)); + /* + * XXXMAC: Additional consistency tests regarding the single + * and the range of the new label might be performed here. + */ + } + + return (0); } static int @@ -1142,31 +1235,48 @@ mac_mls_check_pipe_relabel(struct ucred *cred, struct pipe *pipe, struct label *pipelabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; + int error; new = SLOT(newlabel); subj = SLOT(&cred->cr_label); obj = SLOT(pipelabel); - if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_SINGLE) - return (EINVAL); + /* + * If there is an MLS label update for a pipe, it must be a + * single update. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a pipe, the old pipe label must be in the subject - * range. + * To perform a relabel of a pipe (MLS label or not), MLS must + * authorize the relabel. */ if (!mac_mls_single_in_range(obj, subj)) return (EPERM); /* - * To relabel a pipe, the new pipe label must be in the subject - * range. + * If the MLS label is to be changed, authorize as appropriate. */ - if (!mac_mls_single_in_range(new, subj)) - return (EPERM); + if (new->mm_flags & MAC_MLS_FLAG_SINGLE) { + /* + * To change the MLS label on a pipe, the new pipe label + * must be in the subject range. + */ + if (!mac_mls_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the MLS label on a pipe to be EQUAL, the + * subject must have appropriate privilege. + */ + if (mac_mls_contains_equal(new)) { + error = mac_mls_subject_equal_ok(subj); + if (error) + return (error); + } + } return (0); } @@ -1287,31 +1397,48 @@ mac_mls_check_socket_relabel(struct ucred *cred, struct socket *socket, struct label *socketlabel, struct label *newlabel) { struct mac_mls *subj, *obj, *new; + int error; new = SLOT(newlabel); subj = SLOT(&cred->cr_label); obj = SLOT(socketlabel); - if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_SINGLE) - return (EINVAL); + /* + * If there is an MLS label update for the socket, it may be + * an update of single. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a socket, the old socket label must be in the subject + * To relabel a socket, the old socket single must be in the subject * range. */ if (!mac_mls_single_in_range(obj, subj)) return (EPERM); /* - * To relabel a socket, the new socket label must be in the subject - * range. + * If the MLS label is to be changed, authorize as appropriate. */ - if (!mac_mls_single_in_range(new, subj)) - return (EPERM); + if (new->mm_flags & MAC_MLS_FLAG_SINGLE) { + /* + * To relabel a socket, the new socket single must be in + * the subject range. + */ + if (!mac_mls_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the MLS label on the socket to contain EQUAL, + * the subject must have appropriate privilege. + */ + if (mac_mls_contains_equal(new)) { + error = mac_mls_subject_equal_ok(subj); + if (error) + return (error); + } + } return (0); } @@ -1492,7 +1619,7 @@ mac_mls_check_vnode_getextattr(struct ucred *cred, struct vnode *vp, return (0); } -static int +static int mac_mls_check_vnode_link(struct ucred *cred, struct vnode *dvp, struct label *dlabel, struct vnode *vp, struct label *label, struct componentname *cnp) @@ -1558,7 +1685,7 @@ mac_mls_check_vnode_mmap(struct ucred *cred, struct vnode *vp, return (EACCES); } - return (0); + return (0); } static int @@ -1663,33 +1790,50 @@ mac_mls_check_vnode_relabel(struct ucred *cred, struct vnode *vp, struct label *vnodelabel, struct label *newlabel) { struct mac_mls *old, *new, *subj; + int error; old = SLOT(vnodelabel); new = SLOT(newlabel); subj = SLOT(&cred->cr_label); - if ((new->mm_flags & MAC_MLS_FLAGS_BOTH) != MAC_MLS_FLAG_SINGLE) - return (EINVAL); + /* + * If there is an MLS label update for the vnode, it must be a + * single label. + */ + error = mls_atmostflags(new, MAC_MLS_FLAG_SINGLE); + if (error) + return (error); /* - * To relabel a vnode, the old vnode label must be in the subject - * range. + * To perform a relabel of the vnode (MLS label or not), MLS must + * authorize the relabel. */ if (!mac_mls_single_in_range(old, subj)) return (EPERM); /* - * To relabel a vnode, the new vnode label must be in the subject - * range. + * If the MLS label is to be changed, authorize as appropriate. */ - if (!mac_mls_single_in_range(new, subj)) - return (EPERM); + if (new->mm_flags & MAC_MLS_FLAG_SINGLE) { + /* + * To change the MLS label on a vnode, the new vnode label + * must be in the subject range. + */ + if (!mac_mls_single_in_range(new, subj)) + return (EPERM); - /* - * XXX: Don't permit EQUAL in a label unless the subject has EQUAL. - */ + /* + * To change the MLS label on the vnode to be EQUAL, + * the subject must have appropriate privilege. + */ + if (mac_mls_contains_equal(new)) { + error = mac_mls_subject_equal_ok(subj); + if (error) + return (error); + } + } - return (suser_cred(cred, 0)); + return (0); }