|
@@ -988,6 +988,23 @@ error:
|
|
|
}
|
|
|
EXPORT_SYMBOL(xfrm_lookup);
|
|
|
|
|
|
+static inline int
|
|
|
+xfrm_secpath_reject(int idx, struct sk_buff *skb, struct flowi *fl)
|
|
|
+{
|
|
|
+ struct xfrm_state *x;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!skb->sp || idx < 0 || idx >= skb->sp->len)
|
|
|
+ return 0;
|
|
|
+ x = skb->sp->xvec[idx];
|
|
|
+ if (!x->type->reject)
|
|
|
+ return 0;
|
|
|
+ xfrm_state_hold(x);
|
|
|
+ err = x->type->reject(x, skb, fl);
|
|
|
+ xfrm_state_put(x);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/* When skb is transformed back to its "native" form, we have to
|
|
|
* check policy restrictions. At the moment we make this in maximally
|
|
|
* stupid way. Shame on me. :-) Of course, connected sockets must
|
|
@@ -1010,6 +1027,13 @@ xfrm_state_ok(struct xfrm_tmpl *tmpl, struct xfrm_state *x,
|
|
|
xfrm_state_addr_cmp(tmpl, x, family));
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * 0 or more than 0 is returned when validation is succeeded (either bypass
|
|
|
+ * because of optional transport mode, or next index of the mathced secpath
|
|
|
+ * state with the template.
|
|
|
+ * -1 is returned when no matching template is found.
|
|
|
+ * Otherwise "-2 - errored_index" is returned.
|
|
|
+ */
|
|
|
static inline int
|
|
|
xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
|
|
|
unsigned short family)
|
|
@@ -1024,8 +1048,11 @@ xfrm_policy_ok(struct xfrm_tmpl *tmpl, struct sec_path *sp, int start,
|
|
|
for (; idx < sp->len; idx++) {
|
|
|
if (xfrm_state_ok(tmpl, sp->xvec[idx], family))
|
|
|
return ++idx;
|
|
|
- if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT)
|
|
|
+ if (sp->xvec[idx]->props.mode != XFRM_MODE_TRANSPORT) {
|
|
|
+ if (start == -1)
|
|
|
+ start = -2-idx;
|
|
|
break;
|
|
|
+ }
|
|
|
}
|
|
|
return start;
|
|
|
}
|
|
@@ -1046,11 +1073,14 @@ xfrm_decode_session(struct sk_buff *skb, struct flowi *fl, unsigned short family
|
|
|
}
|
|
|
EXPORT_SYMBOL(xfrm_decode_session);
|
|
|
|
|
|
-static inline int secpath_has_nontransport(struct sec_path *sp, int k)
|
|
|
+static inline int secpath_has_nontransport(struct sec_path *sp, int k, int *idxp)
|
|
|
{
|
|
|
for (; k < sp->len; k++) {
|
|
|
- if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT)
|
|
|
+ if (sp->xvec[k]->props.mode != XFRM_MODE_TRANSPORT) {
|
|
|
+ if (idxp)
|
|
|
+ *idxp = k;
|
|
|
return 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
@@ -1062,6 +1092,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
struct xfrm_policy *pol;
|
|
|
struct flowi fl;
|
|
|
u8 fl_dir = policy_to_flow_dir(dir);
|
|
|
+ int xerr_idx = -1;
|
|
|
+ int *xerr_idxp = &xerr_idx;
|
|
|
|
|
|
if (xfrm_decode_session(skb, &fl, family) < 0)
|
|
|
return 0;
|
|
@@ -1086,8 +1118,13 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
pol = flow_cache_lookup(&fl, family, fl_dir,
|
|
|
xfrm_policy_lookup);
|
|
|
|
|
|
- if (!pol)
|
|
|
- return !skb->sp || !secpath_has_nontransport(skb->sp, 0);
|
|
|
+ if (!pol) {
|
|
|
+ if (skb->sp && secpath_has_nontransport(skb->sp, 0, xerr_idxp)) {
|
|
|
+ xfrm_secpath_reject(xerr_idx, skb, &fl);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
|
|
|
pol->curlft.use_time = (unsigned long)xtime.tv_sec;
|
|
|
|
|
@@ -1107,11 +1144,14 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
*/
|
|
|
for (i = pol->xfrm_nr-1, k = 0; i >= 0; i--) {
|
|
|
k = xfrm_policy_ok(pol->xfrm_vec+i, sp, k, family);
|
|
|
- if (k < 0)
|
|
|
+ if (k < 0) {
|
|
|
+ if (k < -1 && xerr_idxp)
|
|
|
+ *xerr_idxp = -(2+k);
|
|
|
goto reject;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if (secpath_has_nontransport(sp, k))
|
|
|
+ if (secpath_has_nontransport(sp, k, xerr_idxp))
|
|
|
goto reject;
|
|
|
|
|
|
xfrm_pol_put(pol);
|
|
@@ -1119,6 +1159,7 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb,
|
|
|
}
|
|
|
|
|
|
reject:
|
|
|
+ xfrm_secpath_reject(xerr_idx, skb, &fl);
|
|
|
xfrm_pol_put(pol);
|
|
|
return 0;
|
|
|
}
|