|
|
@@ -6,7 +6,12 @@
|
|
|
* Authors: Serge Hallyn <sergeh@us.ibm.com>
|
|
|
* Trent Jaeger <jaegert@us.ibm.com>
|
|
|
*
|
|
|
+ * Updated: Venkat Yekkirala <vyekkirala@TrustedCS.com>
|
|
|
+ *
|
|
|
+ * Granular IPSec Associations for use in MLS environments.
|
|
|
+ *
|
|
|
* Copyright (C) 2005 International Business Machines Corporation
|
|
|
+ * Copyright (C) 2006 Trusted Computer Solutions, Inc.
|
|
|
*
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
* it under the terms of the GNU General Public License version 2,
|
|
|
@@ -67,10 +72,10 @@ static inline int selinux_authorizable_xfrm(struct xfrm_state *x)
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * LSM hook implementation that authorizes that a socket can be used
|
|
|
- * with the corresponding xfrm_sec_ctx and direction.
|
|
|
+ * LSM hook implementation that authorizes that a flow can use
|
|
|
+ * a xfrm policy rule.
|
|
|
*/
|
|
|
-int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
|
|
|
+int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
|
|
|
{
|
|
|
int rc = 0;
|
|
|
u32 sel_sid = SECINITSID_UNLABELED;
|
|
|
@@ -84,27 +89,129 @@ int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 sk_sid, u8 dir)
|
|
|
sel_sid = ctx->ctx_sid;
|
|
|
}
|
|
|
|
|
|
- rc = avc_has_perm(sk_sid, sel_sid, SECCLASS_ASSOCIATION,
|
|
|
- ((dir == FLOW_DIR_IN) ? ASSOCIATION__RECVFROM :
|
|
|
- ((dir == FLOW_DIR_OUT) ? ASSOCIATION__SENDTO :
|
|
|
- (ASSOCIATION__SENDTO | ASSOCIATION__RECVFROM))),
|
|
|
+ rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
|
|
|
+ ASSOCIATION__POLMATCH,
|
|
|
NULL);
|
|
|
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * LSM hook implementation that authorizes that a state matches
|
|
|
+ * the given policy, flow combo.
|
|
|
+ */
|
|
|
+
|
|
|
+int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x, struct xfrm_policy *xp,
|
|
|
+ struct flowi *fl)
|
|
|
+{
|
|
|
+ u32 state_sid;
|
|
|
+ u32 pol_sid;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (x->security)
|
|
|
+ state_sid = x->security->ctx_sid;
|
|
|
+ else
|
|
|
+ state_sid = SECINITSID_UNLABELED;
|
|
|
+
|
|
|
+ if (xp->security)
|
|
|
+ pol_sid = xp->security->ctx_sid;
|
|
|
+ else
|
|
|
+ pol_sid = SECINITSID_UNLABELED;
|
|
|
+
|
|
|
+ err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION,
|
|
|
+ ASSOCIATION__POLMATCH,
|
|
|
+ NULL);
|
|
|
+
|
|
|
+ if (err)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ return selinux_xfrm_flow_state_match(fl, x);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * LSM hook implementation that authorizes that a particular outgoing flow
|
|
|
+ * can use a given security association.
|
|
|
+ */
|
|
|
+
|
|
|
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
|
|
|
+{
|
|
|
+ int rc = 0;
|
|
|
+ u32 sel_sid = SECINITSID_UNLABELED;
|
|
|
+ struct xfrm_sec_ctx *ctx;
|
|
|
+
|
|
|
+ /* Context sid is either set to label or ANY_ASSOC */
|
|
|
+ if ((ctx = xfrm->security)) {
|
|
|
+ if (!selinux_authorizable_ctx(ctx))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ sel_sid = ctx->ctx_sid;
|
|
|
+ }
|
|
|
+
|
|
|
+ rc = avc_has_perm(fl->secid, sel_sid, SECCLASS_ASSOCIATION,
|
|
|
+ ASSOCIATION__SENDTO,
|
|
|
+ NULL)? 0:1;
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * LSM hook implementation that determines the sid for the session.
|
|
|
+ */
|
|
|
+
|
|
|
+int selinux_xfrm_decode_session(struct sk_buff *skb, struct flowi *fl)
|
|
|
+{
|
|
|
+ struct sec_path *sp;
|
|
|
+
|
|
|
+ fl->secid = SECSID_NULL;
|
|
|
+
|
|
|
+ if (skb == NULL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ sp = skb->sp;
|
|
|
+ if (sp) {
|
|
|
+ int i, sid_set = 0;
|
|
|
+
|
|
|
+ for (i = sp->len-1; i >= 0; i--) {
|
|
|
+ struct xfrm_state *x = sp->xvec[i];
|
|
|
+ if (selinux_authorizable_xfrm(x)) {
|
|
|
+ struct xfrm_sec_ctx *ctx = x->security;
|
|
|
+
|
|
|
+ if (!sid_set) {
|
|
|
+ fl->secid = ctx->ctx_sid;
|
|
|
+ sid_set = 1;
|
|
|
+ }
|
|
|
+ else if (fl->secid != ctx->ctx_sid)
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Security blob allocation for xfrm_policy and xfrm_state
|
|
|
* CTX does not have a meaningful value on input
|
|
|
*/
|
|
|
-static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_user_sec_ctx *uctx)
|
|
|
+static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp,
|
|
|
+ struct xfrm_user_sec_ctx *uctx, struct xfrm_sec_ctx *pol, u32 sid)
|
|
|
{
|
|
|
int rc = 0;
|
|
|
struct task_security_struct *tsec = current->security;
|
|
|
- struct xfrm_sec_ctx *ctx;
|
|
|
+ struct xfrm_sec_ctx *ctx = NULL;
|
|
|
+ char *ctx_str = NULL;
|
|
|
+ u32 str_len;
|
|
|
+ u32 ctx_sid;
|
|
|
+
|
|
|
+ BUG_ON(uctx && pol);
|
|
|
+
|
|
|
+ if (pol)
|
|
|
+ goto from_policy;
|
|
|
|
|
|
BUG_ON(!uctx);
|
|
|
- BUG_ON(uctx->ctx_doi != XFRM_SC_ALG_SELINUX);
|
|
|
+
|
|
|
+ if (uctx->ctx_doi != XFRM_SC_ALG_SELINUX)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
if (uctx->ctx_len >= PAGE_SIZE)
|
|
|
return -ENOMEM;
|
|
|
@@ -141,9 +248,41 @@ static int selinux_xfrm_sec_ctx_alloc(struct xfrm_sec_ctx **ctxp, struct xfrm_us
|
|
|
|
|
|
return rc;
|
|
|
|
|
|
+from_policy:
|
|
|
+ BUG_ON(!pol);
|
|
|
+ rc = security_sid_mls_copy(pol->ctx_sid, sid, &ctx_sid);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ rc = security_sid_to_context(ctx_sid, &ctx_str, &str_len);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ *ctxp = ctx = kmalloc(sizeof(*ctx) +
|
|
|
+ str_len,
|
|
|
+ GFP_ATOMIC);
|
|
|
+
|
|
|
+ if (!ctx) {
|
|
|
+ rc = -ENOMEM;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ ctx->ctx_doi = XFRM_SC_DOI_LSM;
|
|
|
+ ctx->ctx_alg = XFRM_SC_ALG_SELINUX;
|
|
|
+ ctx->ctx_sid = ctx_sid;
|
|
|
+ ctx->ctx_len = str_len;
|
|
|
+ memcpy(ctx->ctx_str,
|
|
|
+ ctx_str,
|
|
|
+ str_len);
|
|
|
+
|
|
|
+ goto out2;
|
|
|
+
|
|
|
out:
|
|
|
*ctxp = NULL;
|
|
|
kfree(ctx);
|
|
|
+out2:
|
|
|
+ kfree(ctx_str);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
@@ -157,7 +296,7 @@ int selinux_xfrm_policy_alloc(struct xfrm_policy *xp, struct xfrm_user_sec_ctx *
|
|
|
|
|
|
BUG_ON(!xp);
|
|
|
|
|
|
- err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx);
|
|
|
+ err = selinux_xfrm_sec_ctx_alloc(&xp->security, uctx, NULL, 0);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
@@ -217,13 +356,14 @@ int selinux_xfrm_policy_delete(struct xfrm_policy *xp)
|
|
|
* LSM hook implementation that allocs and transfers sec_ctx spec to
|
|
|
* xfrm_state.
|
|
|
*/
|
|
|
-int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx)
|
|
|
+int selinux_xfrm_state_alloc(struct xfrm_state *x, struct xfrm_user_sec_ctx *uctx,
|
|
|
+ struct xfrm_sec_ctx *pol, u32 secid)
|
|
|
{
|
|
|
int err;
|
|
|
|
|
|
BUG_ON(!x);
|
|
|
|
|
|
- err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx);
|
|
|
+ err = selinux_xfrm_sec_ctx_alloc(&x->security, uctx, pol, secid);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
@@ -329,38 +469,30 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
|
|
|
* we need to check for unlabelled access since this may not have
|
|
|
* gone thru the IPSec process.
|
|
|
*/
|
|
|
-int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
|
|
|
+int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
|
|
+ struct avc_audit_data *ad)
|
|
|
{
|
|
|
int i, rc = 0;
|
|
|
struct sec_path *sp;
|
|
|
+ u32 sel_sid = SECINITSID_UNLABELED;
|
|
|
|
|
|
sp = skb->sp;
|
|
|
|
|
|
if (sp) {
|
|
|
- /*
|
|
|
- * __xfrm_policy_check does not approve unless xfrm_policy_ok
|
|
|
- * says that spi's match for policy and the socket.
|
|
|
- *
|
|
|
- * Only need to verify the existence of an authorizable sp.
|
|
|
- */
|
|
|
for (i = 0; i < sp->len; i++) {
|
|
|
struct xfrm_state *x = sp->xvec[i];
|
|
|
|
|
|
- if (x && selinux_authorizable_xfrm(x))
|
|
|
- goto accept;
|
|
|
+ if (x && selinux_authorizable_xfrm(x)) {
|
|
|
+ struct xfrm_sec_ctx *ctx = x->security;
|
|
|
+ sel_sid = ctx->ctx_sid;
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /* check SELinux sock for unlabelled access */
|
|
|
- rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
|
|
|
- ASSOCIATION__RECVFROM, NULL);
|
|
|
- if (rc)
|
|
|
- goto drop;
|
|
|
-
|
|
|
-accept:
|
|
|
- return 0;
|
|
|
+ rc = avc_has_perm(isec_sid, sel_sid, SECCLASS_ASSOCIATION,
|
|
|
+ ASSOCIATION__RECVFROM, ad);
|
|
|
|
|
|
-drop:
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
@@ -371,7 +503,8 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb)
|
|
|
* If we do have a authorizable security association, then it has already been
|
|
|
* checked in xfrm_policy_lookup hook.
|
|
|
*/
|
|
|
-int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
|
|
|
+int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
|
|
+ struct avc_audit_data *ad)
|
|
|
{
|
|
|
struct dst_entry *dst;
|
|
|
int rc = 0;
|
|
|
@@ -391,7 +524,7 @@ int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb)
|
|
|
}
|
|
|
|
|
|
rc = avc_has_perm(isec_sid, SECINITSID_UNLABELED, SECCLASS_ASSOCIATION,
|
|
|
- ASSOCIATION__SENDTO, NULL);
|
|
|
+ ASSOCIATION__SENDTO, ad);
|
|
|
out:
|
|
|
return rc;
|
|
|
}
|