Package: racoon
Severity: important
Tags: upstream patch

Hi,

for quite a long time we were facing problems with one IPsec-secured network.
Especially after bootup or after restart of racoon hosts could not
re-establish IPsec-connections fully. Strange situations occured, like
you have to ping host B from A before IPsec would work.

Now it turns out, that it's a bug in racoon.

Situation:
We have two hosts: workstation and homeserver.
Host workstation is part of workstationnet.

IPsec policies on workstation:
# Exclude LDAP
spdadd workstation/32[any] homeserver/32[636] any -P out prio def +1 none;
spdadd homeserver/32[636] workstation/32[any] any -P in  prio def +1 none;

# Require IPsec for everything else
spdadd workstation/32[any] homeserver/32[any] any -P out ipsec 
esp/transport//require;
spdadd homeserver/32[any] workstation/32[any] any -P in  ipsec 
esp/transport//require;

IPsec policies on homeserver:
# Exclude LDAP
spdadd homeserver/32[636] workstationnet/25[any] any -P out prio def +1 none;
spdadd workstationnet/25[any] homeserver/32[636] any -P in  prio def +1 none;

# Require IPsec for everything else
spdadd homeserver/32[any] workstationnet/25[any] any -P out ipsec 
esp/transport//require;
spdadd workstationnet/25[any] homeserver/32[any] any -P in  ipsec 
esp/transport//require;

Now if the workstation wants to contact the homeserver and no
ISAKMP-association exists, racoon on the workstation contacts racoon on the
homeserver. The homeserver racoon tries to match the source and destiation
address from the security association payload with the addresses of its
in-memory policy database. This matching fails, because a 'cmpspidxstrict()'
match fails due to the network mask in the rule and a 'cmpspidxwild()' match
disregards ports and selects the LDAP rule with policy 'none', because it has
higher priority. But the LDAP rules need higher priority, because matching of
SPD entries in the linux kernel requires rules with narrower selectors to come
first and which of cause make sense.

The failing match is logged only for the outbound policy:
racoon: ERROR: policy found, but no IPsec required: homeserver/32[0] 
workstation/32[0] proto=any dir=out

The proposed fix is to introduce a new matching function cmpspidxnetmask()
which masks source and destination addresses but regards ports and use that in
getsp_r() instead of cmpspidxstrict().

Jan

-- System Information:
Debian Release: 7.8
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 3.2.0-4-amd64 (SMP w/4 CPU cores)
Locale: LANG=en_US.ISO-8859-15, LC_CTYPE=de_DE@euro (charmap=ISO-8859-15)
Shell: /bin/sh linked to /bin/dash
Index: zedat-ipsec-tools/src/racoon/policy.c
===================================================================
--- zedat-ipsec-tools.orig/src/racoon/policy.c	2015-02-24 16:17:39.066154085 +0100
+++ zedat-ipsec-tools/src/racoon/policy.c	2015-02-24 16:18:12.058713399 +0100
@@ -94,7 +94,7 @@
 	struct secpolicy *found = NULL;
 
 	for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
-		if (!cmpspidxstrict(spidx, &p->spidx))
+		if (!cmpspidxnetmask(spidx, &p->spidx))
 			return p;
 
 		if (!found && !cmpspidxwild(spidx, &p->spidx))
@@ -217,6 +217,60 @@
 }
 
 /*
+ * compare policyindex.
+ * a: subject b: db (sub addresses masked with db prefixes)
+ * OUT: 0:      equal
+ *      1:      not equal
+ */
+int
+cmpspidxnetmask(a, b)
+        struct policyindex *a, *b;
+{
+        struct sockaddr_storage sa1, sa2;
+
+        plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
+        plog(LLV_DEBUG, LOCATION, NULL, "db :%p: %s\n", b, spidx2str(b));
+
+        /* XXX don't check direction now, but it's to be checked carefully. */
+        if (a->dir != b->dir
+         || a->ul_proto != b->ul_proto)
+                return 1;
+
+        mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->src,
+                b->prefs);
+        mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->src,
+                b->prefs);
+        plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
+                a, b->prefs, saddr2str((struct sockaddr *)&sa1));
+        plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
+                b, b->prefs, saddr2str((struct sockaddr *)&sa2));
+        if (cmpsaddr((struct sockaddr *) &sa1,
+                     (struct sockaddr *) &sa2) != CMPSADDR_MATCH)
+                return 1;
+
+        mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->dst,
+                b->prefd);
+        mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->dst,
+                b->prefd);
+        plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
+                a, b->prefd, saddr2str((struct sockaddr *)&sa1));
+        plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
+                b, b->prefd, saddr2str((struct sockaddr *)&sa2));
+        if (cmpsaddr((struct sockaddr *) &sa1,
+                     (struct sockaddr *) &sa2) != CMPSADDR_MATCH)
+                return 1;
+
+#ifdef HAVE_SECCTX
+        if (a->sec_ctx.ctx_alg != b->sec_ctx.ctx_alg
+            || a->sec_ctx.ctx_doi != b->sec_ctx.ctx_doi
+            || !within_range(a->sec_ctx.ctx_str, b->sec_ctx.ctx_str))
+                return 1;
+#endif
+        return 0;
+}
+
+
+/*
  * compare policyindex, with wildcard address/protocol match.
  * a: subject b: db, can contain wildcard things.
  * OUT:	0:	equal
Index: zedat-ipsec-tools/src/racoon/policy.h
===================================================================
--- zedat-ipsec-tools.orig/src/racoon/policy.h	2015-02-24 16:18:48.247326873 +0100
+++ zedat-ipsec-tools/src/racoon/policy.h	2015-02-24 16:18:58.207495715 +0100
@@ -147,6 +147,7 @@
 extern struct secpolicy *getsp_r __P((struct policyindex *));
 struct secpolicy *getspbyspid __P((u_int32_t));
 extern int cmpspidxstrict __P((struct policyindex *, struct policyindex *));
+extern int cmpspidxnetmask __P((struct policyindex *, struct policyindex *));
 extern int cmpspidxwild __P((struct policyindex *, struct policyindex *));
 extern struct secpolicy *newsp __P((void));
 extern void delsp __P((struct secpolicy *));

Reply via email to