1/*	$NetBSD: policy.c,v 1.6.4.1 2007/08/01 11:52:21 vanhu Exp $	*/
2
3/*	$KAME: policy.c,v 1.46 2001/11/16 04:08:10 sakane Exp $	*/
4
5/*
6 * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the project nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "config.h"
35
36#include <sys/param.h>
37#include <sys/types.h>
38#include <sys/socket.h>
39#include <sys/queue.h>
40
41#include <netinet/in.h>
42#include PATH_IPSEC_H
43
44#include <stdlib.h>
45#include <stdio.h>
46#include <string.h>
47#include <errno.h>
48
49#include "var.h"
50#include "misc.h"
51#include "vmbuf.h"
52#include "plog.h"
53#include "sockmisc.h"
54#include "debug.h"
55
56#include "policy.h"
57#include "localconf.h"
58#include "isakmp_var.h"
59#include "isakmp.h"
60#include "oakley.h"
61#include "handler.h"
62#include "strnames.h"
63#include "gcmalloc.h"
64
65static TAILQ_HEAD(_sptree, secpolicy) sptree;
66
67/* perform exact match against security policy table. */
68struct secpolicy *
69getsp(spidx)
70	struct policyindex *spidx;
71{
72	struct secpolicy *p;
73
74	for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
75		if (!cmpspidxstrict(spidx, &p->spidx))
76			return p;
77	}
78
79	return NULL;
80}
81
82/*
83 * perform non-exact match against security policy table, only if this is
84 * transport mode SA negotiation.  for example, 0.0.0.0/0 -> 0.0.0.0/0
85 * entry in policy.txt can be returned when we're negotiating transport
86 * mode SA.  this is how the kernel works.
87 */
88#if 1
89struct secpolicy *
90getsp_r(spidx)
91	struct policyindex *spidx;
92{
93	struct secpolicy *p;
94
95	for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
96		if (!cmpspidxwild(spidx, &p->spidx))
97			return p;
98	}
99
100	return NULL;
101}
102#else
103struct secpolicy *
104getsp_r(spidx, iph2)
105	struct policyindex *spidx;
106	struct ph2handle *iph2;
107{
108	struct secpolicy *p;
109	u_int8_t prefixlen;
110
111	plog(LLV_DEBUG, LOCATION, NULL, "checking for transport mode\n");
112
113	if (spidx->src.ss_family != spidx->dst.ss_family) {
114		plog(LLV_ERROR, LOCATION, NULL,
115			"address family mismatch, src:%d dst:%d\n",
116				spidx->src.ss_family,
117				spidx->dst.ss_family);
118		return NULL;
119	}
120	switch (spidx->src.ss_family) {
121	case AF_INET:
122		prefixlen = sizeof(struct in_addr) << 3;
123		break;
124#ifdef INET6
125	case AF_INET6:
126		prefixlen = sizeof(struct in6_addr) << 3;
127		break;
128#endif
129	default:
130		plog(LLV_ERROR, LOCATION, NULL,
131			"invalid family: %d\n", spidx->src.ss_family);
132		return NULL;
133	}
134
135	/* is it transport mode SA negotiation? */
136	plog(LLV_DEBUG, LOCATION, NULL, "src1: %s\n",
137		saddr2str(iph2->src));
138	plog(LLV_DEBUG, LOCATION, NULL, "src2: %s\n",
139		saddr2str((struct sockaddr *)&spidx->src));
140	if (cmpsaddrwop(iph2->src, (struct sockaddr *)&spidx->src)
141	 || spidx->prefs != prefixlen)
142		return NULL;
143
144	plog(LLV_DEBUG, LOCATION, NULL, "dst1: %s\n",
145		saddr2str(iph2->dst));
146	plog(LLV_DEBUG, LOCATION, NULL, "dst2: %s\n",
147		saddr2str((struct sockaddr *)&spidx->dst));
148	if (cmpsaddrwop(iph2->dst, (struct sockaddr *)&spidx->dst)
149	 || spidx->prefd != prefixlen)
150		return NULL;
151
152	plog(LLV_DEBUG, LOCATION, NULL, "looks to be transport mode\n");
153
154	for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
155		if (!cmpspidx_wild(spidx, &p->spidx))
156			return p;
157	}
158
159	return NULL;
160}
161#endif
162
163struct secpolicy *
164getspbyspid(spid)
165	u_int32_t spid;
166{
167	struct secpolicy *p;
168
169	for (p = TAILQ_FIRST(&sptree); p; p = TAILQ_NEXT(p, chain)) {
170		if (p->id == spid)
171			return p;
172	}
173
174	return NULL;
175}
176
177/*
178 * compare policyindex.
179 * a: subject b: db
180 * OUT:	0:	equal
181 *	1:	not equal
182 */
183int
184cmpspidxstrict(a, b)
185	struct policyindex *a, *b;
186{
187	plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
188	plog(LLV_DEBUG, LOCATION, NULL, "db :%p: %s\n", b, spidx2str(b));
189
190	/* XXX don't check direction now, but it's to be checked carefully. */
191	if (a->dir != b->dir
192	 || a->prefs != b->prefs
193	 || a->prefd != b->prefd
194	 || a->ul_proto != b->ul_proto)
195		return 1;
196
197	if (cmpsaddrstrict((struct sockaddr *)&a->src,
198			   (struct sockaddr *)&b->src))
199		return 1;
200	if (cmpsaddrstrict((struct sockaddr *)&a->dst,
201			   (struct sockaddr *)&b->dst))
202		return 1;
203
204#ifdef HAVE_SECCTX
205	if (a->sec_ctx.ctx_alg != b->sec_ctx.ctx_alg
206	    || a->sec_ctx.ctx_doi != b->sec_ctx.ctx_doi
207	    || !within_range(a->sec_ctx.ctx_str, b->sec_ctx.ctx_str))
208		return 1;
209#endif
210	return 0;
211}
212
213/*
214 * compare policyindex, with wildcard address/protocol match.
215 * a: subject b: db, can contain wildcard things.
216 * OUT:	0:	equal
217 *	1:	not equal
218 */
219int
220cmpspidxwild(a, b)
221	struct policyindex *a, *b;
222{
223	struct sockaddr_storage sa1, sa2;
224
225	plog(LLV_DEBUG, LOCATION, NULL, "sub:%p: %s\n", a, spidx2str(a));
226	plog(LLV_DEBUG, LOCATION, NULL, "db: %p: %s\n", b, spidx2str(b));
227
228	if (!(b->dir == IPSEC_DIR_ANY || a->dir == b->dir))
229		return 1;
230
231	if (!(a->ul_proto == IPSEC_ULPROTO_ANY ||
232	      b->ul_proto == IPSEC_ULPROTO_ANY ||
233	      a->ul_proto == b->ul_proto))
234		return 1;
235
236	if (a->src.ss_family != b->src.ss_family)
237		return 1;
238	if (a->dst.ss_family != b->dst.ss_family)
239		return 1;
240
241#ifndef __linux__
242	/* compare src address */
243	if (sizeof(sa1) < a->src.ss_len || sizeof(sa2) < b->src.ss_len) {
244		plog(LLV_ERROR, LOCATION, NULL,
245			"unexpected error: "
246			"src.ss_len:%d dst.ss_len:%d\n",
247			a->src.ss_len, b->src.ss_len);
248		return 1;
249	}
250#endif
251	mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->src,
252		b->prefs);
253	mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->src,
254		b->prefs);
255	plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
256		a, b->prefs, saddr2str((struct sockaddr *)&sa1));
257	plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
258		b, b->prefs, saddr2str((struct sockaddr *)&sa2));
259	if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
260		return 1;
261
262#ifndef __linux__
263	/* compare dst address */
264	if (sizeof(sa1) < a->dst.ss_len || sizeof(sa2) < b->dst.ss_len) {
265		plog(LLV_ERROR, LOCATION, NULL, "unexpected error\n");
266		exit(1);
267	}
268#endif
269	mask_sockaddr((struct sockaddr *)&sa1, (struct sockaddr *)&a->dst,
270		b->prefd);
271	mask_sockaddr((struct sockaddr *)&sa2, (struct sockaddr *)&b->dst,
272		b->prefd);
273	plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
274		a, b->prefd, saddr2str((struct sockaddr *)&sa1));
275	plog(LLV_DEBUG, LOCATION, NULL, "%p masked with /%d: %s\n",
276		b, b->prefd, saddr2str((struct sockaddr *)&sa2));
277	if (cmpsaddrwild((struct sockaddr *)&sa1, (struct sockaddr *)&sa2))
278		return 1;
279
280#ifdef HAVE_SECCTX
281	if (a->sec_ctx.ctx_alg != b->sec_ctx.ctx_alg
282	    || a->sec_ctx.ctx_doi != b->sec_ctx.ctx_doi
283	    || !within_range(a->sec_ctx.ctx_str, b->sec_ctx.ctx_str))
284		return 1;
285#endif
286	return 0;
287}
288
289struct secpolicy *
290newsp()
291{
292	struct secpolicy *new;
293
294	new = racoon_calloc(1, sizeof(*new));
295	if (new == NULL)
296		return NULL;
297
298	return new;
299}
300
301void
302delsp(sp)
303	struct secpolicy *sp;
304{
305	struct ipsecrequest *req = NULL, *next;
306
307	for (req = sp->req; req; req = next) {
308		next = req->next;
309		racoon_free(req);
310	}
311
312	racoon_free(sp);
313}
314
315void
316delsp_bothdir(spidx0)
317	struct policyindex *spidx0;
318{
319	struct policyindex spidx;
320	struct secpolicy *sp;
321	struct sockaddr_storage src, dst;
322	u_int8_t prefs, prefd;
323
324	memcpy(&spidx, spidx0, sizeof(spidx));
325	switch (spidx.dir) {
326	case IPSEC_DIR_INBOUND:
327#ifdef HAVE_POLICY_FWD
328	case IPSEC_DIR_FWD:
329#endif
330		src   = spidx.src;
331		dst   = spidx.dst;
332		prefs = spidx.prefs;
333		prefd = spidx.prefd;
334		break;
335	case IPSEC_DIR_OUTBOUND:
336		src   = spidx.dst;
337		dst   = spidx.src;
338		prefs = spidx.prefd;
339		prefd = spidx.prefs;
340		break;
341	default:
342		return;
343	}
344
345	spidx.src   = src;
346	spidx.dst   = dst;
347	spidx.prefs = prefs;
348	spidx.prefd = prefd;
349	spidx.dir   = IPSEC_DIR_INBOUND;
350
351	sp = getsp(&spidx);
352	if (sp) {
353		remsp(sp);
354		delsp(sp);
355	}
356
357#ifdef HAVE_POLICY_FWD
358	spidx.dir   = IPSEC_DIR_FWD;
359
360	sp = getsp(&spidx);
361	if (sp) {
362		remsp(sp);
363		delsp(sp);
364	}
365#endif
366
367	spidx.src   = dst;
368	spidx.dst   = src;
369	spidx.prefs = prefd;
370	spidx.prefd = prefs;
371	spidx.dir   = IPSEC_DIR_OUTBOUND;
372
373	sp = getsp(&spidx);
374	if (sp) {
375		remsp(sp);
376		delsp(sp);
377	}
378}
379
380void
381inssp(new)
382	struct secpolicy *new;
383{
384#ifdef HAVE_PFKEY_POLICY_PRIORITY
385	struct secpolicy *p;
386
387	TAILQ_FOREACH(p, &sptree, chain) {
388		if (new->spidx.priority < p->spidx.priority) {
389			TAILQ_INSERT_BEFORE(p, new, chain);
390			return;
391		}
392	}
393	if (p == NULL)
394#endif
395		TAILQ_INSERT_TAIL(&sptree, new, chain);
396
397	return;
398}
399
400void
401remsp(sp)
402	struct secpolicy *sp;
403{
404	TAILQ_REMOVE(&sptree, sp, chain);
405}
406
407void
408flushsp()
409{
410	struct secpolicy *p, *next;
411
412	for (p = TAILQ_FIRST(&sptree); p; p = next) {
413		next = TAILQ_NEXT(p, chain);
414		remsp(p);
415		delsp(p);
416	}
417}
418
419void
420initsp()
421{
422	TAILQ_INIT(&sptree);
423}
424
425struct ipsecrequest *
426newipsecreq()
427{
428	struct ipsecrequest *new;
429
430	new = racoon_calloc(1, sizeof(*new));
431	if (new == NULL)
432		return NULL;
433
434	return new;
435}
436
437const char *
438spidx2str(spidx)
439	const struct policyindex *spidx;
440{
441	/* addr/pref[port] addr/pref[port] ul dir act */
442	static char buf[256];
443	char *p, *a, *b;
444	int blen, i;
445
446	blen = sizeof(buf) - 1;
447	p = buf;
448
449	a = saddr2str((const struct sockaddr *)&spidx->src);
450	for (b = a; *b != '\0'; b++)
451		if (*b == '[') {
452			*b = '\0';
453			b++;
454			break;
455		}
456	i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefs, b);
457	if (i < 0 || i >= blen)
458		return NULL;
459	p += i;
460	blen -= i;
461
462	a = saddr2str((const struct sockaddr *)&spidx->dst);
463	for (b = a; *b != '\0'; b++)
464		if (*b == '[') {
465			*b = '\0';
466			b++;
467			break;
468		}
469	i = snprintf(p, blen, "%s/%d[%s ", a, spidx->prefd, b);
470	if (i < 0 || i >= blen)
471		return NULL;
472	p += i;
473	blen -= i;
474
475	i = snprintf(p, blen, "proto=%s dir=%s",
476		s_proto(spidx->ul_proto), s_direction(spidx->dir));
477
478#ifdef HAVE_SECCTX
479	if (spidx->sec_ctx.ctx_strlen) {
480		p += i;
481		blen -= i;
482		snprintf(p, blen, " sec_ctx:doi=%d,alg=%d,len=%d,str=%s",
483			 spidx->sec_ctx.ctx_doi, spidx->sec_ctx.ctx_alg,
484			 spidx->sec_ctx.ctx_strlen, spidx->sec_ctx.ctx_str);
485	}
486#endif
487	return buf;
488}
489