1/*	$NetBSD: ipsec_dump_policy.c,v 1.7.6.1 2007/08/01 11:52:17 vanhu Exp $	*/
2
3/* Id: ipsec_dump_policy.c,v 1.10 2005/06/29 09:12:37 manubsd Exp */
4
5/*
6 * Copyright (C) 1995, 1996, 1997, 1998, and 1999 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#ifdef HAVE_CONFIG_H
35#include "config.h"
36#endif
37
38#include <sys/types.h>
39#include <sys/param.h>
40#include <sys/socket.h>
41
42#include <netinet/in.h>
43#include PATH_IPSEC_H
44
45#include <arpa/inet.h>
46
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <netdb.h>
51
52#include "ipsec_strerror.h"
53#include "libpfkey.h"
54
55static const char *ipsp_dir_strs[] = {
56	"any", "in", "out", "fwd"
57};
58
59static const char *ipsp_policy_strs[] = {
60	"discard", "none", "ipsec", "entrust", "bypass",
61};
62
63static char *ipsec_dump_ipsecrequest __P((char *, size_t,
64	struct sadb_x_ipsecrequest *, size_t, int));
65static char *ipsec_dump_policy1 __P((void *, const char *, int));
66static int set_addresses __P((char *, size_t, struct sockaddr *,
67	struct sockaddr *, int));
68static char *set_address __P((char *, size_t, struct sockaddr *, int));
69
70/*
71 * policy is sadb_x_policy buffer.
72 * Must call free() later.
73 * When delimiter == NULL, alternatively ' '(space) is applied.
74 */
75char *
76ipsec_dump_policy(policy, delimiter)
77	ipsec_policy_t policy;
78	__ipsec_const char *delimiter;
79{
80	return ipsec_dump_policy1(policy, delimiter, 0);
81}
82
83char *
84ipsec_dump_policy_withports(policy, delimiter)
85	void *policy;
86	const char *delimiter;
87{
88	return ipsec_dump_policy1(policy, delimiter, 1);
89}
90
91static char *
92ipsec_dump_policy1(policy, delimiter, withports)
93	void *policy;
94	const char *delimiter;
95	int withports;
96{
97	struct sadb_x_policy *xpl = policy;
98	struct sadb_x_ipsecrequest *xisr;
99	size_t off, buflen;
100	char *buf;
101	char isrbuf[1024];
102	char *newbuf;
103
104#ifdef HAVE_PFKEY_POLICY_PRIORITY
105	int32_t priority_offset;
106	char *priority_str;
107	char operator;
108#endif
109
110	/* sanity check */
111	if (policy == NULL)
112		return NULL;
113	if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) {
114		__ipsec_errcode = EIPSEC_INVAL_EXTTYPE;
115		return NULL;
116	}
117
118	/* set delimiter */
119	if (delimiter == NULL)
120		delimiter = " ";
121
122#ifdef HAVE_PFKEY_POLICY_PRIORITY
123	if (xpl->sadb_x_policy_priority == 0)
124	{
125		priority_offset = 0;
126		priority_str = "";
127	}
128	/* find which constant the priority is closest to */
129	else if (xpl->sadb_x_policy_priority <
130	         (u_int32_t) (PRIORITY_DEFAULT / 4) * 3)
131	{
132		priority_offset = xpl->sadb_x_policy_priority - PRIORITY_HIGH;
133		priority_str = "prio high";
134	}
135	else if (xpl->sadb_x_policy_priority >=
136	         (u_int32_t) (PRIORITY_DEFAULT / 4) * 3 &&
137	         xpl->sadb_x_policy_priority <
138	         (u_int32_t) (PRIORITY_DEFAULT / 4) * 5)
139	{
140		priority_offset = xpl->sadb_x_policy_priority - PRIORITY_DEFAULT;
141		priority_str = "prio def";
142	}
143	else
144	{
145		priority_offset = xpl->sadb_x_policy_priority - PRIORITY_LOW;
146		priority_str = "prio low";
147	}
148
149	/* fix sign to match the way it is input */
150	priority_offset *= -1;
151	if (priority_offset < 0)
152	{
153		operator = '-';
154		priority_offset *= -1;
155	}
156	else
157	{
158		operator = '+';
159	}
160#endif
161
162	switch (xpl->sadb_x_policy_dir) {
163	case IPSEC_DIR_ANY:
164	case IPSEC_DIR_INBOUND:
165	case IPSEC_DIR_OUTBOUND:
166#ifdef HAVE_POLICY_FWD
167	case IPSEC_DIR_FWD:
168#endif
169		break;
170	default:
171		__ipsec_errcode = EIPSEC_INVAL_DIR;
172		return NULL;
173	}
174
175	switch (xpl->sadb_x_policy_type) {
176	case IPSEC_POLICY_DISCARD:
177	case IPSEC_POLICY_NONE:
178	case IPSEC_POLICY_IPSEC:
179	case IPSEC_POLICY_BYPASS:
180	case IPSEC_POLICY_ENTRUST:
181		break;
182	default:
183		__ipsec_errcode = EIPSEC_INVAL_POLICY;
184		return NULL;
185	}
186
187	buflen = strlen(ipsp_dir_strs[xpl->sadb_x_policy_dir])
188		+ 1	/* space */
189#ifdef HAVE_PFKEY_POLICY_PRIORITY
190		+ strlen(priority_str)
191		+ ((priority_offset != 0) ? 13 : 0) /* [space operator space int] */
192		+ ((strlen(priority_str) != 0) ? 1 : 0) /* space */
193#endif
194		+ strlen(ipsp_policy_strs[xpl->sadb_x_policy_type])
195		+ 1;	/* NUL */
196
197	if ((buf = malloc(buflen)) == NULL) {
198		__ipsec_errcode = EIPSEC_NO_BUFS;
199		return NULL;
200	}
201#ifdef HAVE_PFKEY_POLICY_PRIORITY
202	if (priority_offset != 0)
203	{
204		snprintf(buf, buflen, "%s %s %c %u %s",
205	    	ipsp_dir_strs[xpl->sadb_x_policy_dir], priority_str, operator,
206			priority_offset, ipsp_policy_strs[xpl->sadb_x_policy_type]);
207	}
208	else if (strlen (priority_str) != 0)
209	{
210		snprintf(buf, buflen, "%s %s %s",
211	    	ipsp_dir_strs[xpl->sadb_x_policy_dir], priority_str,
212			ipsp_policy_strs[xpl->sadb_x_policy_type]);
213	}
214	else
215	{
216		snprintf(buf, buflen, "%s %s",
217	    	ipsp_dir_strs[xpl->sadb_x_policy_dir],
218			ipsp_policy_strs[xpl->sadb_x_policy_type]);
219	}
220#else
221	snprintf(buf, buflen, "%s %s", ipsp_dir_strs[xpl->sadb_x_policy_dir],
222	    ipsp_policy_strs[xpl->sadb_x_policy_type]);
223#endif
224
225	if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
226		__ipsec_errcode = EIPSEC_NO_ERROR;
227		return buf;
228	}
229
230	/* count length of buffer for use */
231	off = sizeof(*xpl);
232	while (off < PFKEY_EXTLEN(xpl)) {
233		xisr = (void *)((caddr_t)(void *)xpl + off);
234		off += xisr->sadb_x_ipsecrequest_len;
235	}
236
237	/* validity check */
238	if (off != PFKEY_EXTLEN(xpl)) {
239		__ipsec_errcode = EIPSEC_INVAL_SADBMSG;
240		free(buf);
241		return NULL;
242	}
243
244	off = sizeof(*xpl);
245	while (off < PFKEY_EXTLEN(xpl)) {
246		int offset;
247		xisr = (void *)((caddr_t)(void *)xpl + off);
248
249		if (ipsec_dump_ipsecrequest(isrbuf, sizeof(isrbuf), xisr,
250		    PFKEY_EXTLEN(xpl) - off, withports) == NULL) {
251			free(buf);
252			return NULL;
253		}
254
255		offset = strlen(buf);
256		buflen = offset + strlen(delimiter) + strlen(isrbuf) + 1;
257		newbuf = (char *)realloc(buf, buflen);
258		if (newbuf == NULL) {
259			__ipsec_errcode = EIPSEC_NO_BUFS;
260			free(buf);
261			return NULL;
262		}
263		buf = newbuf;
264		snprintf(buf+offset, buflen-offset, "%s%s", delimiter, isrbuf);
265
266		off += xisr->sadb_x_ipsecrequest_len;
267	}
268
269	__ipsec_errcode = EIPSEC_NO_ERROR;
270	return buf;
271}
272
273static char *
274ipsec_dump_ipsecrequest(buf, len, xisr, bound, withports)
275	char *buf;
276	size_t len;
277	struct sadb_x_ipsecrequest *xisr;
278	size_t bound;	/* boundary */
279	int withports;
280{
281	const char *proto, *mode, *level;
282	char abuf[NI_MAXHOST * 2 + 2];
283
284	if (xisr->sadb_x_ipsecrequest_len > bound) {
285		__ipsec_errcode = EIPSEC_INVAL_PROTO;
286		return NULL;
287	}
288
289	switch (xisr->sadb_x_ipsecrequest_proto) {
290	case IPPROTO_ESP:
291		proto = "esp";
292		break;
293	case IPPROTO_AH:
294		proto = "ah";
295		break;
296	case IPPROTO_IPCOMP:
297		proto = "ipcomp";
298		break;
299	default:
300		__ipsec_errcode = EIPSEC_INVAL_PROTO;
301		return NULL;
302	}
303
304	switch (xisr->sadb_x_ipsecrequest_mode) {
305	case IPSEC_MODE_ANY:
306		mode = "any";
307		break;
308	case IPSEC_MODE_TRANSPORT:
309		mode = "transport";
310		break;
311	case IPSEC_MODE_TUNNEL:
312		mode = "tunnel";
313		break;
314	default:
315		__ipsec_errcode = EIPSEC_INVAL_MODE;
316		return NULL;
317	}
318
319	abuf[0] = '\0';
320	if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
321		struct sockaddr *sa1, *sa2;
322		caddr_t p;
323
324		p = (void *)(xisr + 1);
325		sa1 = (void *)p;
326		sa2 = (void *)(p + sysdep_sa_len(sa1));
327		if (sizeof(*xisr) + sysdep_sa_len(sa1) + sysdep_sa_len(sa2) !=
328		    xisr->sadb_x_ipsecrequest_len) {
329			__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
330			return NULL;
331		}
332		if (set_addresses(abuf, sizeof(abuf),
333		    sa1, sa2, withports) != 0) {
334			__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
335			return NULL;
336		}
337	}
338
339	switch (xisr->sadb_x_ipsecrequest_level) {
340	case IPSEC_LEVEL_DEFAULT:
341		level = "default";
342		break;
343	case IPSEC_LEVEL_USE:
344		level = "use";
345		break;
346	case IPSEC_LEVEL_REQUIRE:
347		level = "require";
348		break;
349	case IPSEC_LEVEL_UNIQUE:
350		level = "unique";
351		break;
352	default:
353		__ipsec_errcode = EIPSEC_INVAL_LEVEL;
354		return NULL;
355	}
356
357	if (xisr->sadb_x_ipsecrequest_reqid == 0)
358		snprintf(buf, len, "%s/%s/%s/%s", proto, mode, abuf, level);
359	else {
360		int ch;
361
362		if (xisr->sadb_x_ipsecrequest_reqid > IPSEC_MANUAL_REQID_MAX)
363			ch = '#';
364		else
365			ch = ':';
366		snprintf(buf, len, "%s/%s/%s/%s%c%u", proto, mode, abuf, level,
367		    ch, xisr->sadb_x_ipsecrequest_reqid);
368	}
369
370	return buf;
371}
372
373static int
374set_addresses(buf, len, sa1, sa2, withports)
375	char *buf;
376	size_t len;
377	struct sockaddr *sa1;
378	struct sockaddr *sa2;
379	int withports;
380{
381	char tmp1[NI_MAXHOST], tmp2[NI_MAXHOST];
382
383	if (set_address(tmp1, sizeof(tmp1), sa1, withports) == NULL ||
384	    set_address(tmp2, sizeof(tmp2), sa2, withports) == NULL)
385		return -1;
386	if (strlen(tmp1) + 1 + strlen(tmp2) + 1 > len)
387		return -1;
388	snprintf(buf, len, "%s-%s", tmp1, tmp2);
389	return 0;
390}
391
392static char *
393set_address(buf, len, sa, withports)
394	char *buf;
395	size_t len;
396	struct sockaddr *sa;
397	int withports;
398{
399	const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
400	char host[NI_MAXHOST];
401	char serv[NI_MAXSERV];
402
403	if (len < 1)
404		return NULL;
405	buf[0] = '\0';
406	if (getnameinfo(sa, (socklen_t)sysdep_sa_len(sa), host, sizeof(host),
407	    serv, sizeof(serv), niflags) != 0)
408		return NULL;
409
410	if (withports)
411		snprintf(buf, len, "%s[%s]", host, serv);
412	else
413		snprintf(buf, len, "%s", host);
414
415	return buf;
416}
417