1121572Sume/*	$KAME: ipsec_dump_policy.c,v 1.13 2002/06/27 14:35:11 itojun Exp $	*/
262583Sitojun
355505Sshin/*
455505Sshin * Copyright (C) 1995, 1996, 1997, 1998, and 1999 WIDE Project.
555505Sshin * All rights reserved.
655505Sshin *
755505Sshin * Redistribution and use in source and binary forms, with or without
855505Sshin * modification, are permitted provided that the following conditions
955505Sshin * are met:
1055505Sshin * 1. Redistributions of source code must retain the above copyright
1155505Sshin *    notice, this list of conditions and the following disclaimer.
1255505Sshin * 2. Redistributions in binary form must reproduce the above copyright
1355505Sshin *    notice, this list of conditions and the following disclaimer in the
1455505Sshin *    documentation and/or other materials provided with the distribution.
1555505Sshin * 3. Neither the name of the project nor the names of its contributors
1655505Sshin *    may be used to endorse or promote products derived from this software
1755505Sshin *    without specific prior written permission.
1855505Sshin *
1955505Sshin * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
2055505Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2155505Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2255505Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
2355505Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2455505Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2555505Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2655505Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2755505Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2855505Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2955505Sshin * SUCH DAMAGE.
3055505Sshin */
3155505Sshin
3284208Sdillon#include <sys/cdefs.h>
3384208Sdillon__FBSDID("$FreeBSD$");
3484208Sdillon
3555505Sshin#include <sys/types.h>
3655505Sshin#include <sys/param.h>
3755505Sshin#include <sys/socket.h>
3855505Sshin
39171135Sgnn#include <netipsec/key_var.h>
4055505Sshin#include <netinet/in.h>
41171135Sgnn#include <netipsec/ipsec.h>
4255505Sshin
4355505Sshin#include <arpa/inet.h>
4455505Sshin
4562583Sitojun#include <stdio.h>
4655505Sshin#include <stdlib.h>
4755505Sshin#include <string.h>
4855505Sshin#include <netdb.h>
4955505Sshin
5055505Sshin#include "ipsec_strerror.h"
5155505Sshin
5255505Sshinstatic const char *ipsp_dir_strs[] = {
5355505Sshin	"any", "in", "out",
5455505Sshin};
5555505Sshin
5655505Sshinstatic const char *ipsp_policy_strs[] = {
5755505Sshin	"discard", "none", "ipsec", "entrust", "bypass",
5855505Sshin};
5955505Sshin
6092941Sobrienstatic char *ipsec_dump_ipsecrequest(char *, size_t,
6192941Sobrien	struct sadb_x_ipsecrequest *, size_t);
6292941Sobrienstatic int set_addresses(char *, size_t, struct sockaddr *, struct sockaddr *);
6392917Sobrienstatic char *set_address(char *, size_t, struct sockaddr *);
6455505Sshin
6555505Sshin/*
6655505Sshin * policy is sadb_x_policy buffer.
6755505Sshin * Must call free() later.
6855505Sshin * When delimiter == NULL, alternatively ' '(space) is applied.
6955505Sshin */
7055505Sshinchar *
7155505Sshinipsec_dump_policy(policy, delimiter)
7255505Sshin	caddr_t policy;
7355505Sshin	char *delimiter;
7455505Sshin{
7555505Sshin	struct sadb_x_policy *xpl = (struct sadb_x_policy *)policy;
7655505Sshin	struct sadb_x_ipsecrequest *xisr;
7762583Sitojun	size_t off, buflen;
7855505Sshin	char *buf;
7962583Sitojun	char isrbuf[1024];
8062583Sitojun	char *newbuf;
8155505Sshin
8255505Sshin	/* sanity check */
8355505Sshin	if (policy == NULL)
8455505Sshin		return NULL;
8555505Sshin	if (xpl->sadb_x_policy_exttype != SADB_X_EXT_POLICY) {
8662583Sitojun		__ipsec_errcode = EIPSEC_INVAL_EXTTYPE;
8755505Sshin		return NULL;
8855505Sshin	}
8955505Sshin
9055505Sshin	/* set delimiter */
9155505Sshin	if (delimiter == NULL)
9255505Sshin		delimiter = " ";
9355505Sshin
9455505Sshin	switch (xpl->sadb_x_policy_dir) {
9555505Sshin	case IPSEC_DIR_ANY:
9655505Sshin	case IPSEC_DIR_INBOUND:
9755505Sshin	case IPSEC_DIR_OUTBOUND:
9855505Sshin		break;
9955505Sshin	default:
10062583Sitojun		__ipsec_errcode = EIPSEC_INVAL_DIR;
10155505Sshin		return NULL;
10255505Sshin	}
10355505Sshin
10455505Sshin	switch (xpl->sadb_x_policy_type) {
10555505Sshin	case IPSEC_POLICY_DISCARD:
10655505Sshin	case IPSEC_POLICY_NONE:
10755505Sshin	case IPSEC_POLICY_IPSEC:
10855505Sshin	case IPSEC_POLICY_BYPASS:
10955505Sshin	case IPSEC_POLICY_ENTRUST:
11055505Sshin		break;
11155505Sshin	default:
11262583Sitojun		__ipsec_errcode = EIPSEC_INVAL_POLICY;
11355505Sshin		return NULL;
11455505Sshin	}
11555505Sshin
11655505Sshin	buflen = strlen(ipsp_dir_strs[xpl->sadb_x_policy_dir])
11755505Sshin		+ 1	/* space */
11855505Sshin		+ strlen(ipsp_policy_strs[xpl->sadb_x_policy_type])
11955505Sshin		+ 1;	/* NUL */
12055505Sshin
12155505Sshin	if ((buf = malloc(buflen)) == NULL) {
12262583Sitojun		__ipsec_errcode = EIPSEC_NO_BUFS;
12355505Sshin		return NULL;
12455505Sshin	}
12562583Sitojun	snprintf(buf, buflen, "%s %s", ipsp_dir_strs[xpl->sadb_x_policy_dir],
12662583Sitojun	    ipsp_policy_strs[xpl->sadb_x_policy_type]);
12755505Sshin
12855505Sshin	if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
12962583Sitojun		__ipsec_errcode = EIPSEC_NO_ERROR;
13055505Sshin		return buf;
13155505Sshin	}
13255505Sshin
13355505Sshin	/* count length of buffer for use */
13462583Sitojun	off = sizeof(*xpl);
13562583Sitojun	while (off < PFKEY_EXTLEN(xpl)) {
13662583Sitojun		xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xpl + off);
13762583Sitojun		off += xisr->sadb_x_ipsecrequest_len;
13855505Sshin	}
13955505Sshin
14055505Sshin	/* validity check */
14162583Sitojun	if (off != PFKEY_EXTLEN(xpl)) {
14262583Sitojun		__ipsec_errcode = EIPSEC_INVAL_SADBMSG;
14355505Sshin		free(buf);
14455505Sshin		return NULL;
14555505Sshin	}
14655505Sshin
14762583Sitojun	off = sizeof(*xpl);
14862583Sitojun	while (off < PFKEY_EXTLEN(xpl)) {
14962583Sitojun		xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xpl + off);
15055505Sshin
15162583Sitojun		if (ipsec_dump_ipsecrequest(isrbuf, sizeof(isrbuf), xisr,
15262583Sitojun		    PFKEY_EXTLEN(xpl) - off) == NULL) {
15355505Sshin			free(buf);
15455505Sshin			return NULL;
15555505Sshin		}
15655505Sshin
15762583Sitojun		buflen = strlen(buf) + strlen(delimiter) + strlen(isrbuf) + 1;
15862583Sitojun		newbuf = (char *)realloc(buf, buflen);
15962583Sitojun		if (newbuf == NULL) {
16062583Sitojun			__ipsec_errcode = EIPSEC_NO_BUFS;
16155505Sshin			free(buf);
16255505Sshin			return NULL;
16355505Sshin		}
16462583Sitojun		buf = newbuf;
165216369Skevlo		snprintf(buf + strlen(buf), buflen - strlen(buf),
166216369Skevlo		    "%s%s", delimiter, isrbuf);
16755505Sshin
16862583Sitojun		off += xisr->sadb_x_ipsecrequest_len;
16962583Sitojun	}
17055505Sshin
17162583Sitojun	__ipsec_errcode = EIPSEC_NO_ERROR;
17262583Sitojun	return buf;
17362583Sitojun}
17455505Sshin
17562583Sitojunstatic char *
17662583Sitojunipsec_dump_ipsecrequest(buf, len, xisr, bound)
17762583Sitojun	char *buf;
17862583Sitojun	size_t len;
17962583Sitojun	struct sadb_x_ipsecrequest *xisr;
18062583Sitojun	size_t bound;	/* boundary */
18162583Sitojun{
18262583Sitojun	const char *proto, *mode, *level;
18362583Sitojun	char abuf[NI_MAXHOST * 2 + 2];
18462583Sitojun
18562583Sitojun	if (xisr->sadb_x_ipsecrequest_len > bound) {
18662583Sitojun		__ipsec_errcode = EIPSEC_INVAL_PROTO;
18762583Sitojun		return NULL;
18862583Sitojun	}
18962583Sitojun
19062583Sitojun	switch (xisr->sadb_x_ipsecrequest_proto) {
19162583Sitojun	case IPPROTO_ESP:
19262583Sitojun		proto = "esp";
19362583Sitojun		break;
19462583Sitojun	case IPPROTO_AH:
19562583Sitojun		proto = "ah";
19662583Sitojun		break;
19762583Sitojun	case IPPROTO_IPCOMP:
19862583Sitojun		proto = "ipcomp";
19962583Sitojun		break;
200125681Sbms	case IPPROTO_TCP:
201125681Sbms		proto = "tcp";
202264870Sbrueffer		break;
20362583Sitojun	default:
20462583Sitojun		__ipsec_errcode = EIPSEC_INVAL_PROTO;
20562583Sitojun		return NULL;
20662583Sitojun	}
20762583Sitojun
20862583Sitojun	switch (xisr->sadb_x_ipsecrequest_mode) {
20962583Sitojun	case IPSEC_MODE_ANY:
21062583Sitojun		mode = "any";
21162583Sitojun		break;
21262583Sitojun	case IPSEC_MODE_TRANSPORT:
21362583Sitojun		mode = "transport";
21462583Sitojun		break;
21562583Sitojun	case IPSEC_MODE_TUNNEL:
21662583Sitojun		mode = "tunnel";
21762583Sitojun		break;
21862583Sitojun	default:
21962583Sitojun		__ipsec_errcode = EIPSEC_INVAL_MODE;
22062583Sitojun		return NULL;
22162583Sitojun	}
22262583Sitojun
22362583Sitojun	abuf[0] = '\0';
22462583Sitojun	if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
22562583Sitojun		struct sockaddr *sa1, *sa2;
22662583Sitojun		caddr_t p;
22762583Sitojun
22862583Sitojun		p = (caddr_t)(xisr + 1);
22962583Sitojun		sa1 = (struct sockaddr *)p;
23062583Sitojun		sa2 = (struct sockaddr *)(p + sa1->sa_len);
23162583Sitojun		if (sizeof(*xisr) + sa1->sa_len + sa2->sa_len !=
23262583Sitojun		    xisr->sadb_x_ipsecrequest_len) {
23362583Sitojun			__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
23455505Sshin			return NULL;
23555505Sshin		}
23662583Sitojun		if (set_addresses(abuf, sizeof(abuf), sa1, sa2) != 0) {
23762583Sitojun			__ipsec_errcode = EIPSEC_INVAL_ADDRESS;
23862583Sitojun			return NULL;
23962583Sitojun		}
24062583Sitojun	}
24155505Sshin
24262583Sitojun	switch (xisr->sadb_x_ipsecrequest_level) {
24362583Sitojun	case IPSEC_LEVEL_DEFAULT:
24462583Sitojun		level = "default";
24562583Sitojun		break;
24662583Sitojun	case IPSEC_LEVEL_USE:
24762583Sitojun		level = "use";
24862583Sitojun		break;
24962583Sitojun	case IPSEC_LEVEL_REQUIRE:
25062583Sitojun		level = "require";
25162583Sitojun		break;
25262583Sitojun	case IPSEC_LEVEL_UNIQUE:
25362583Sitojun		level = "unique";
25462583Sitojun		break;
25562583Sitojun	default:
25662583Sitojun		__ipsec_errcode = EIPSEC_INVAL_LEVEL;
25762583Sitojun		return NULL;
25855505Sshin	}
25955505Sshin
26062583Sitojun	if (xisr->sadb_x_ipsecrequest_reqid == 0)
26162583Sitojun		snprintf(buf, len, "%s/%s/%s/%s", proto, mode, abuf, level);
26262583Sitojun	else {
26362583Sitojun		int ch;
26462583Sitojun
26562583Sitojun		if (xisr->sadb_x_ipsecrequest_reqid > IPSEC_MANUAL_REQID_MAX)
26662583Sitojun			ch = '#';
26762583Sitojun		else
26862583Sitojun			ch = ':';
269121572Sume		snprintf(buf, len, "%s/%s/%s/%s%c%u", proto, mode, abuf, level,
27062583Sitojun		    ch, xisr->sadb_x_ipsecrequest_reqid);
27162583Sitojun	}
27262583Sitojun
27355505Sshin	return buf;
27455505Sshin}
27555505Sshin
27655505Sshinstatic int
27762583Sitojunset_addresses(buf, len, sa1, sa2)
27855505Sshin	char *buf;
27962583Sitojun	size_t len;
28062583Sitojun	struct sockaddr *sa1;
28162583Sitojun	struct sockaddr *sa2;
28255505Sshin{
28362583Sitojun	char tmp1[NI_MAXHOST], tmp2[NI_MAXHOST];
28455505Sshin
28562583Sitojun	if (set_address(tmp1, sizeof(tmp1), sa1) == NULL ||
28662583Sitojun	    set_address(tmp2, sizeof(tmp2), sa2) == NULL)
28762583Sitojun		return -1;
28862583Sitojun	if (strlen(tmp1) + 1 + strlen(tmp2) + 1 > len)
28962583Sitojun		return -1;
29062583Sitojun	snprintf(buf, len, "%s-%s", tmp1, tmp2);
29162583Sitojun	return 0;
29262583Sitojun}
29355505Sshin
29462583Sitojunstatic char *
29562583Sitojunset_address(buf, len, sa)
29662583Sitojun	char *buf;
29762583Sitojun	size_t len;
29862583Sitojun	struct sockaddr *sa;
29962583Sitojun{
30062583Sitojun	const int niflags = NI_NUMERICHOST;
30155505Sshin
30262583Sitojun	if (len < 1)
30362583Sitojun		return NULL;
30462583Sitojun	buf[0] = '\0';
30562583Sitojun	if (getnameinfo(sa, sa->sa_len, buf, len, NULL, 0, niflags) != 0)
30662583Sitojun		return NULL;
30762583Sitojun	return buf;
30855505Sshin}
309