*     Check permission when a flow selects a xfrm_policy for processing
  *     XFRMs on a packet.  The hook is called when selecting either a
  *     per-socket policy or a generic xfrm policy.
- *     Return 0 if permission is granted.
+ *     Return 0 if permission is granted, -ESRCH otherwise, or -errno
+ *     on other errors.
  * @xfrm_state_pol_flow_match:
  *     @x contains the state to match.
  *     @xp contains the policy to check for a match.
  * @xfrm_flow_state_match:
  *     @fl contains the flow key to match.
  *     @xfrm points to the xfrm_state to match.
+ *     @xp points to the xfrm_policy to match.
  *     Return 1 if there is a match.
  * @xfrm_decode_session:
  *     @skb points to skb to decode.
        int (*xfrm_policy_lookup)(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
        int (*xfrm_state_pol_flow_match)(struct xfrm_state *x,
                        struct xfrm_policy *xp, struct flowi *fl);
-       int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm);
+       int (*xfrm_flow_state_match)(struct flowi *fl, struct xfrm_state *xfrm,
+                       struct xfrm_policy *xp);
        int (*xfrm_decode_session)(struct sk_buff *skb, u32 *secid, int ckall);
 #endif /* CONFIG_SECURITY_NETWORK_XFRM */
 
        return security_ops->xfrm_policy_alloc_security(xp, sec_ctx, NULL);
 }
 
-static inline int security_xfrm_sock_policy_alloc(struct xfrm_policy *xp, struct sock *sk)
-{
-       return security_ops->xfrm_policy_alloc_security(xp, NULL, sk);
-}
-
 static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
 {
        return security_ops->xfrm_policy_clone_security(old, new);
        return security_ops->xfrm_state_pol_flow_match(x, xp, fl);
 }
 
-static inline int security_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+static inline int security_xfrm_flow_state_match(struct flowi *fl,
+                       struct xfrm_state *xfrm, struct xfrm_policy *xp)
 {
-       return security_ops->xfrm_flow_state_match(fl, xfrm);
+       return security_ops->xfrm_flow_state_match(fl, xfrm, xp);
 }
 
 static inline int security_xfrm_decode_session(struct sk_buff *skb, u32 *secid)
        return 0;
 }
 
-static inline int security_xfrm_sock_policy_alloc(struct xfrm_policy *xp, struct sock *sk)
-{
-       return 0;
-}
-
 static inline int security_xfrm_policy_clone(struct xfrm_policy *old, struct xfrm_policy *new)
 {
        return 0;
 }
 
 static inline int security_xfrm_flow_state_match(struct flowi *fl,
-                                struct xfrm_state *xfrm)
+                       struct xfrm_state *xfrm, struct xfrm_policy *xp)
 {
        return 1;
 }
 
                                  int create, unsigned short family);
 extern void xfrm_policy_flush(u8 type);
 extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol);
-extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family, int strict);
+extern int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *xdst,
+                         struct flowi *fl, int family, int strict);
 extern void xfrm_init_pmtu(struct dst_entry *dst);
 
 extern wait_queue_head_t km_waitq;
 
                    xdst->u.rt.fl.fl4_dst == fl->fl4_dst &&
                    xdst->u.rt.fl.fl4_src == fl->fl4_src &&
                    xdst->u.rt.fl.fl4_tos == fl->fl4_tos &&
-                   xfrm_bundle_ok(xdst, fl, AF_INET, 0)) {
+                   xfrm_bundle_ok(policy, xdst, fl, AF_INET, 0)) {
                        dst_clone(dst);
                        break;
                }
 
                                 xdst->u.rt6.rt6i_src.plen);
                if (ipv6_addr_equal(&xdst->u.rt6.rt6i_dst.addr, &fl_dst_prefix) &&
                    ipv6_addr_equal(&xdst->u.rt6.rt6i_src.addr, &fl_src_prefix) &&
-                   xfrm_bundle_ok(xdst, fl, AF_INET6,
+                   xfrm_bundle_ok(policy, xdst, fl, AF_INET6,
                                   (xdst->u.rt6.rt6i_dst.plen != 128 ||
                                    xdst->u.rt6.rt6i_src.plen != 128))) {
                        dst_clone(dst);
 
                if (*dir)
                        goto out;
        }
-       else {
-               *dir = security_xfrm_sock_policy_alloc(xp, sk);
-               if (*dir)
-                       goto out;
-       }
 
        *dir = pol->sadb_x_policy_dir-1;
        return xp;
 
 
 static int stale_bundle(struct dst_entry *dst)
 {
-       return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
+       return !xfrm_bundle_ok(NULL, (struct xfrm_dst *)dst, NULL, AF_UNSPEC, 0);
 }
 
 void xfrm_dst_ifdown(struct dst_entry *dst, struct net_device *dev)
  * still valid.
  */
 
-int xfrm_bundle_ok(struct xfrm_dst *first, struct flowi *fl, int family, int strict)
+int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first,
+               struct flowi *fl, int family, int strict)
 {
        struct dst_entry *dst = &first->u.dst;
        struct xfrm_dst *last;
 
                if (fl && !xfrm_selector_match(&dst->xfrm->sel, fl, family))
                        return 0;
-               if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm))
+               if (fl && !security_xfrm_flow_state_match(fl, dst->xfrm, pol))
                        return 0;
                if (dst->xfrm->km.state != XFRM_STATE_VALID)
                        return 0;
 
        xp->type = XFRM_POLICY_TYPE_MAIN;
        copy_templates(xp, ut, nr);
 
-       if (!xp->security) {
-               int err = security_xfrm_sock_policy_alloc(xp, sk);
-               if (err) {
-                       kfree(xp);
-                       *dir = err;
-                       return NULL;
-               }
-       }
-
        *dir = p->dir;
 
        return xp;
 
        return 1;
 }
 
-static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+static int dummy_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm,
+                               struct xfrm_policy *xp)
 {
        return 1;
 }
 
 int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir);
 int selinux_xfrm_state_pol_flow_match(struct xfrm_state *x,
                        struct xfrm_policy *xp, struct flowi *fl);
-int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm);
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm,
+                       struct xfrm_policy *xp);
 
 
 /*
 
  */
 int selinux_xfrm_policy_lookup(struct xfrm_policy *xp, u32 fl_secid, u8 dir)
 {
-       int rc = 0;
-       u32 sel_sid = SECINITSID_UNLABELED;
+       int rc;
+       u32 sel_sid;
        struct xfrm_sec_ctx *ctx;
 
        /* Context sid is either set to label or ANY_ASSOC */
 
                sel_sid = ctx->ctx_sid;
        }
+       else
+               /*
+                * All flows should be treated as polmatch'ing an
+                * otherwise applicable "non-labeled" policy. This
+                * would prevent inadvertent "leaks".
+                */
+               return 0;
 
        rc = avc_has_perm(fl_secid, sel_sid, SECCLASS_ASSOCIATION,
                          ASSOCIATION__POLMATCH,
                          NULL);
 
+       if (rc == -EACCES)
+               rc = -ESRCH;
+
        return rc;
 }
 
        u32 pol_sid;
        int err;
 
-       if (x->security)
-               state_sid = x->security->ctx_sid;
-       else
-               state_sid = SECINITSID_UNLABELED;
-
-       if (xp->security)
+       if (xp->security) {
+               if (!x->security)
+                       /* unlabeled SA and labeled policy can't match */
+                       return 0;
+               else
+                       state_sid = x->security->ctx_sid;
                pol_sid = xp->security->ctx_sid;
-       else
-               pol_sid = SECINITSID_UNLABELED;
+       } else
+               if (x->security)
+                       /* unlabeled policy and labeled SA can't match */
+                       return 0;
+               else
+                       /* unlabeled policy and unlabeled SA match all flows */
+                       return 1;
 
        err = avc_has_perm(state_sid, pol_sid, SECCLASS_ASSOCIATION,
                          ASSOCIATION__POLMATCH,
        if (err)
                return 0;
 
-       return selinux_xfrm_flow_state_match(fl, x);
+       err = avc_has_perm(fl->secid, state_sid, SECCLASS_ASSOCIATION,
+                         ASSOCIATION__SENDTO,
+                         NULL)? 0:1;
+
+       return err;
 }
 
 /*
  * can use a given security association.
  */
 
-int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm)
+int selinux_xfrm_flow_state_match(struct flowi *fl, struct xfrm_state *xfrm,
+                                 struct xfrm_policy *xp)
 {
        int rc = 0;
        u32 sel_sid = SECINITSID_UNLABELED;
        struct xfrm_sec_ctx *ctx;
 
+       if (!xp->security)
+               if (!xfrm->security)
+                       return 1;
+               else
+                       return 0;
+       else
+               if (!xfrm->security)
+                       return 0;
+
        /* Context sid is either set to label or ANY_ASSOC */
        if ((ctx = xfrm->security)) {
                if (!selinux_authorizable_ctx(ctx))