/* $NetBSD: pfkey.c,v 1.18.4.5 2008/03/05 22:14:24 mgrooms Exp $ */
|
|
/* $Id: pfkey.c,v 1.18.4.5 2008/03/05 22:14:24 mgrooms Exp $ */
|
|
/*
|
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
|
* All rights reserved.
|
*
|
* Redistribution and use in source and binary forms, with or without
|
* modification, are permitted provided that the following conditions
|
* are met:
|
* 1. Redistributions of source code must retain the above copyright
|
* notice, this list of conditions and the following disclaimer.
|
* 2. Redistributions in binary form must reproduce the above copyright
|
* notice, this list of conditions and the following disclaimer in the
|
* documentation and/or other materials provided with the distribution.
|
* 3. Neither the name of the project nor the names of its contributors
|
* may be used to endorse or promote products derived from this software
|
* without specific prior written permission.
|
*
|
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
* SUCH DAMAGE.
|
*/
|
|
#include "config.h"
|
|
#include <stdlib.h>
|
#include <string.h>
|
#include <stdio.h>
|
#include <netdb.h>
|
#include <errno.h>
|
#ifdef HAVE_UNISTD_H
|
#include <unistd.h>
|
#endif
|
#include <netdb.h>
|
#include <netinet/in.h>
|
#include <arpa/inet.h>
|
|
#ifdef ENABLE_NATT
|
# ifdef __linux__
|
# include <linux/udp.h>
|
# endif
|
# if defined(__NetBSD__) || defined(__FreeBSD__) || \
|
(defined(__APPLE__) && defined(__MACH__))
|
# include <netinet/udp.h>
|
# endif
|
#endif
|
|
#include <sys/types.h>
|
#include <sys/param.h>
|
#include <sys/socket.h>
|
#include <sys/queue.h>
|
#ifndef ANDROID_CHANGES
|
#include <sys/sysctl.h>
|
#endif
|
|
#include <net/route.h>
|
#include <net/pfkeyv2.h>
|
|
#include <netinet/in.h>
|
#include PATH_IPSEC_H
|
#include <fcntl.h>
|
|
#include "libpfkey.h"
|
|
#include "var.h"
|
#include "misc.h"
|
#include "vmbuf.h"
|
#include "plog.h"
|
#include "sockmisc.h"
|
#include "debug.h"
|
|
#include "schedule.h"
|
#include "localconf.h"
|
#include "remoteconf.h"
|
#include "handler.h"
|
#include "policy.h"
|
#include "proposal.h"
|
#include "isakmp_var.h"
|
#include "isakmp.h"
|
#include "isakmp_inf.h"
|
#include "ipsec_doi.h"
|
#include "oakley.h"
|
#include "pfkey.h"
|
#include "algorithm.h"
|
#include "sainfo.h"
|
#include "admin.h"
|
#include "privsep.h"
|
#include "strnames.h"
|
#include "backupsa.h"
|
#include "gcmalloc.h"
|
#include "nattraversal.h"
|
#include "crypto_openssl.h"
|
#include "grabmyaddr.h"
|
|
#if defined(SADB_X_EALG_RIJNDAELCBC) && !defined(SADB_X_EALG_AESCBC)
|
#define SADB_X_EALG_AESCBC SADB_X_EALG_RIJNDAELCBC
|
#endif
|
|
/* prototype */
|
static u_int ipsecdoi2pfkey_aalg __P((u_int));
|
static u_int ipsecdoi2pfkey_ealg __P((u_int));
|
static u_int ipsecdoi2pfkey_calg __P((u_int));
|
static u_int ipsecdoi2pfkey_alg __P((u_int, u_int));
|
static u_int keylen_aalg __P((u_int));
|
static u_int keylen_ealg __P((u_int, int));
|
|
static int pk_recvgetspi __P((caddr_t *));
|
static int pk_recvupdate __P((caddr_t *));
|
static int pk_recvadd __P((caddr_t *));
|
static int pk_recvdelete __P((caddr_t *));
|
static int pk_recvacquire __P((caddr_t *));
|
static int pk_recvexpire __P((caddr_t *));
|
static int pk_recvflush __P((caddr_t *));
|
static int getsadbpolicy __P((caddr_t *, int *, int, struct ph2handle *));
|
static int pk_recvspdupdate __P((caddr_t *));
|
static int pk_recvspdadd __P((caddr_t *));
|
static int pk_recvspddelete __P((caddr_t *));
|
static int pk_recvspdexpire __P((caddr_t *));
|
static int pk_recvspdget __P((caddr_t *));
|
static int pk_recvspddump __P((caddr_t *));
|
static int pk_recvspdflush __P((caddr_t *));
|
static struct sadb_msg *pk_recv __P((int, int *));
|
|
static int (*pkrecvf[]) __P((caddr_t *)) = {
|
NULL,
|
pk_recvgetspi,
|
pk_recvupdate,
|
pk_recvadd,
|
pk_recvdelete,
|
NULL, /* SADB_GET */
|
pk_recvacquire,
|
NULL, /* SABD_REGISTER */
|
pk_recvexpire,
|
pk_recvflush,
|
NULL, /* SADB_DUMP */
|
NULL, /* SADB_X_PROMISC */
|
NULL, /* SADB_X_PCHANGE */
|
pk_recvspdupdate,
|
pk_recvspdadd,
|
pk_recvspddelete,
|
pk_recvspdget,
|
NULL, /* SADB_X_SPDACQUIRE */
|
pk_recvspddump,
|
pk_recvspdflush,
|
NULL, /* SADB_X_SPDSETIDX */
|
pk_recvspdexpire,
|
NULL, /* SADB_X_SPDDELETE2 */
|
NULL, /* SADB_X_NAT_T_NEW_MAPPING */
|
NULL, /* SADB_X_MIGRATE */
|
#if (SADB_MAX > 24)
|
#error "SADB extra message?"
|
#endif
|
};
|
|
static int addnewsp __P((caddr_t *));
|
|
/* cope with old kame headers - ugly */
|
#ifndef SADB_X_AALG_MD5
|
#define SADB_X_AALG_MD5 SADB_AALG_MD5
|
#endif
|
#ifndef SADB_X_AALG_SHA
|
#define SADB_X_AALG_SHA SADB_AALG_SHA
|
#endif
|
#ifndef SADB_X_AALG_NULL
|
#define SADB_X_AALG_NULL SADB_AALG_NULL
|
#endif
|
|
#ifndef SADB_X_EALG_BLOWFISHCBC
|
#define SADB_X_EALG_BLOWFISHCBC SADB_EALG_BLOWFISHCBC
|
#endif
|
#ifndef SADB_X_EALG_CAST128CBC
|
#define SADB_X_EALG_CAST128CBC SADB_EALG_CAST128CBC
|
#endif
|
#ifndef SADB_X_EALG_RC5CBC
|
#ifdef SADB_EALG_RC5CBC
|
#define SADB_X_EALG_RC5CBC SADB_EALG_RC5CBC
|
#endif
|
#endif
|
|
/*
|
* PF_KEY packet handler
|
* 0: success
|
* -1: fail
|
*/
|
int
|
pfkey_handler()
|
{
|
struct sadb_msg *msg;
|
int len;
|
caddr_t mhp[SADB_EXT_MAX + 1];
|
int error = -1;
|
|
/* receive pfkey message. */
|
len = 0;
|
msg = (struct sadb_msg *)pk_recv(lcconf->sock_pfkey, &len);
|
if (msg == NULL) {
|
if (len < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to recv from pfkey (%s)\n",
|
strerror(errno));
|
goto end;
|
} else {
|
/* short message - msg not ready */
|
return 0;
|
}
|
}
|
|
plog(LLV_DEBUG, LOCATION, NULL, "get pfkey %s message\n",
|
s_pfkey_type(msg->sadb_msg_type));
|
plogdump(LLV_DEBUG2, msg, msg->sadb_msg_len << 3);
|
|
/* validity check */
|
if (msg->sadb_msg_errno) {
|
int pri;
|
|
/* when SPD is empty, treat the state as no error. */
|
if (msg->sadb_msg_type == SADB_X_SPDDUMP &&
|
msg->sadb_msg_errno == ENOENT)
|
pri = LLV_DEBUG;
|
else
|
pri = LLV_ERROR;
|
|
plog(pri, LOCATION, NULL,
|
"pfkey %s failed: %s\n",
|
s_pfkey_type(msg->sadb_msg_type),
|
strerror(msg->sadb_msg_errno));
|
|
goto end;
|
}
|
|
/* check pfkey message. */
|
if (pfkey_align(msg, mhp)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed pfkey align (%s)\n",
|
ipsec_strerror());
|
goto end;
|
}
|
if (pfkey_check(mhp)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed pfkey check (%s)\n",
|
ipsec_strerror());
|
goto end;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
|
/* safety check */
|
if (msg->sadb_msg_type >= ARRAYLEN(pkrecvf)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"unknown PF_KEY message type=%u\n",
|
msg->sadb_msg_type);
|
goto end;
|
}
|
|
if (pkrecvf[msg->sadb_msg_type] == NULL) {
|
plog(LLV_INFO, LOCATION, NULL,
|
"unsupported PF_KEY message %s\n",
|
s_pfkey_type(msg->sadb_msg_type));
|
goto end;
|
}
|
|
if ((pkrecvf[msg->sadb_msg_type])(mhp) < 0)
|
goto end;
|
|
error = 0;
|
end:
|
if (msg)
|
racoon_free(msg);
|
return(error);
|
}
|
|
/*
|
* dump SADB
|
*/
|
vchar_t *
|
pfkey_dump_sadb(satype)
|
int satype;
|
{
|
int s = -1;
|
vchar_t *buf = NULL;
|
pid_t pid = getpid();
|
struct sadb_msg *msg = NULL;
|
size_t bl, ml;
|
int len;
|
|
if ((s = privsep_pfkey_open()) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed pfkey open: %s\n",
|
ipsec_strerror());
|
return NULL;
|
}
|
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_dump\n");
|
if (pfkey_send_dump(s, satype) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed dump: %s\n", ipsec_strerror());
|
goto fail;
|
}
|
|
while (1) {
|
if (msg)
|
racoon_free(msg);
|
msg = pk_recv(s, &len);
|
if (msg == NULL) {
|
if (len < 0)
|
goto done;
|
else
|
continue;
|
}
|
|
if (msg->sadb_msg_type != SADB_DUMP || msg->sadb_msg_pid != pid)
|
{
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"discarding non-sadb dump msg %p, our pid=%i\n", msg, pid);
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"type %i, pid %i\n", msg->sadb_msg_type, msg->sadb_msg_pid);
|
continue;
|
}
|
|
|
ml = msg->sadb_msg_len << 3;
|
bl = buf ? buf->l : 0;
|
buf = vrealloc(buf, bl + ml);
|
if (buf == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to reallocate buffer to dump.\n");
|
goto fail;
|
}
|
memcpy(buf->v + bl, msg, ml);
|
|
if (msg->sadb_msg_seq == 0)
|
break;
|
}
|
goto done;
|
|
fail:
|
if (buf)
|
vfree(buf);
|
buf = NULL;
|
done:
|
if (msg)
|
racoon_free(msg);
|
if (s >= 0)
|
privsep_pfkey_close(s);
|
return buf;
|
}
|
|
#ifdef ENABLE_ADMINPORT
|
/*
|
* flush SADB
|
*/
|
void
|
pfkey_flush_sadb(proto)
|
u_int proto;
|
{
|
int satype;
|
|
/* convert to SADB_SATYPE */
|
if ((satype = admin2pfkey_proto(proto)) < 0)
|
return;
|
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_flush\n");
|
if (pfkey_send_flush(lcconf->sock_pfkey, satype) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send flush (%s)\n", ipsec_strerror());
|
return;
|
}
|
|
return;
|
}
|
#endif
|
|
/*
|
* These are the SATYPEs that we manage. We register to get
|
* PF_KEY messages related to these SATYPEs, and we also use
|
* this list to determine which SATYPEs to delete SAs for when
|
* we receive an INITIAL-CONTACT.
|
*/
|
const struct pfkey_satype pfkey_satypes[] = {
|
{ SADB_SATYPE_AH, "AH" },
|
{ SADB_SATYPE_ESP, "ESP" },
|
{ SADB_X_SATYPE_IPCOMP, "IPCOMP" },
|
};
|
const int pfkey_nsatypes =
|
sizeof(pfkey_satypes) / sizeof(pfkey_satypes[0]);
|
|
/*
|
* PF_KEY initialization
|
*/
|
int
|
pfkey_init()
|
{
|
int i, reg_fail;
|
|
if ((lcconf->sock_pfkey = privsep_pfkey_open()) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed pfkey open (%s)\n", ipsec_strerror());
|
return -1;
|
}
|
if (fcntl(lcconf->sock_pfkey, F_SETFL, O_NONBLOCK) == -1)
|
plog(LLV_WARNING, LOCATION, NULL,
|
"failed to set the pfkey socket to NONBLOCK\n");
|
|
for (i = 0, reg_fail = 0; i < pfkey_nsatypes; i++) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"call pfkey_send_register for %s\n",
|
pfkey_satypes[i].ps_name);
|
if (pfkey_send_register(lcconf->sock_pfkey,
|
pfkey_satypes[i].ps_satype) < 0 ||
|
pfkey_recv_register(lcconf->sock_pfkey) < 0) {
|
plog(LLV_WARNING, LOCATION, NULL,
|
"failed to register %s (%s)\n",
|
pfkey_satypes[i].ps_name,
|
ipsec_strerror());
|
reg_fail++;
|
}
|
}
|
|
if (reg_fail == pfkey_nsatypes) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to regist any protocol.\n");
|
pfkey_close(lcconf->sock_pfkey);
|
return -1;
|
}
|
|
initsp();
|
|
if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec sending spddump failed: %s\n",
|
ipsec_strerror());
|
pfkey_close(lcconf->sock_pfkey);
|
return -1;
|
}
|
#if 0
|
if (pfkey_promisc_toggle(1) < 0) {
|
pfkey_close(lcconf->sock_pfkey);
|
return -1;
|
}
|
#endif
|
return 0;
|
}
|
|
/* %%% for conversion */
|
/* IPSECDOI_ATTR_AUTH -> SADB_AALG */
|
static u_int
|
ipsecdoi2pfkey_aalg(hashtype)
|
u_int hashtype;
|
{
|
switch (hashtype) {
|
case IPSECDOI_ATTR_AUTH_HMAC_MD5:
|
return SADB_AALG_MD5HMAC;
|
case IPSECDOI_ATTR_AUTH_HMAC_SHA1:
|
return SADB_AALG_SHA1HMAC;
|
case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256:
|
#if (defined SADB_X_AALG_SHA2_256) && !defined(SADB_X_AALG_SHA2_256HMAC)
|
return SADB_X_AALG_SHA2_256;
|
#else
|
return SADB_X_AALG_SHA2_256HMAC;
|
#endif
|
case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384:
|
#if (defined SADB_X_AALG_SHA2_384) && !defined(SADB_X_AALG_SHA2_384HMAC)
|
return SADB_X_AALG_SHA2_384;
|
#else
|
return SADB_X_AALG_SHA2_384HMAC;
|
#endif
|
case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512:
|
#if (defined SADB_X_AALG_SHA2_512) && !defined(SADB_X_AALG_SHA2_512HMAC)
|
return SADB_X_AALG_SHA2_512;
|
#else
|
return SADB_X_AALG_SHA2_512HMAC;
|
#endif
|
case IPSECDOI_ATTR_AUTH_KPDK: /* need special care */
|
return SADB_AALG_NONE;
|
|
/* not supported */
|
case IPSECDOI_ATTR_AUTH_DES_MAC:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Not supported hash type: %u\n", hashtype);
|
return ~0;
|
|
case 0: /* reserved */
|
default:
|
return SADB_AALG_NONE;
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid hash type: %u\n", hashtype);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* IPSECDOI_ESP -> SADB_EALG */
|
static u_int
|
ipsecdoi2pfkey_ealg(t_id)
|
u_int t_id;
|
{
|
switch (t_id) {
|
case IPSECDOI_ESP_DES_IV64: /* sa_flags |= SADB_X_EXT_OLD */
|
return SADB_EALG_DESCBC;
|
case IPSECDOI_ESP_DES:
|
return SADB_EALG_DESCBC;
|
case IPSECDOI_ESP_3DES:
|
return SADB_EALG_3DESCBC;
|
#ifdef SADB_X_EALG_RC5CBC
|
case IPSECDOI_ESP_RC5:
|
return SADB_X_EALG_RC5CBC;
|
#endif
|
case IPSECDOI_ESP_CAST:
|
return SADB_X_EALG_CAST128CBC;
|
case IPSECDOI_ESP_BLOWFISH:
|
return SADB_X_EALG_BLOWFISHCBC;
|
case IPSECDOI_ESP_DES_IV32: /* flags |= (SADB_X_EXT_OLD|
|
SADB_X_EXT_IV4B)*/
|
return SADB_EALG_DESCBC;
|
case IPSECDOI_ESP_NULL:
|
return SADB_EALG_NULL;
|
#ifdef SADB_X_EALG_AESCBC
|
case IPSECDOI_ESP_AES:
|
return SADB_X_EALG_AESCBC;
|
#endif
|
#ifdef SADB_X_EALG_TWOFISHCBC
|
case IPSECDOI_ESP_TWOFISH:
|
return SADB_X_EALG_TWOFISHCBC;
|
#endif
|
#ifdef SADB_X_EALG_CAMELLIACBC
|
case IPSECDOI_ESP_CAMELLIA:
|
return SADB_X_EALG_CAMELLIACBC;
|
#endif
|
|
/* not supported */
|
case IPSECDOI_ESP_3IDEA:
|
case IPSECDOI_ESP_IDEA:
|
case IPSECDOI_ESP_RC4:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Not supported transform: %u\n", t_id);
|
return ~0;
|
|
case 0: /* reserved */
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid transform id: %u\n", t_id);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* IPCOMP -> SADB_CALG */
|
static u_int
|
ipsecdoi2pfkey_calg(t_id)
|
u_int t_id;
|
{
|
switch (t_id) {
|
case IPSECDOI_IPCOMP_OUI:
|
return SADB_X_CALG_OUI;
|
case IPSECDOI_IPCOMP_DEFLATE:
|
return SADB_X_CALG_DEFLATE;
|
case IPSECDOI_IPCOMP_LZS:
|
return SADB_X_CALG_LZS;
|
|
case 0: /* reserved */
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid transform id: %u\n", t_id);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* IPSECDOI_PROTO -> SADB_SATYPE */
|
u_int
|
ipsecdoi2pfkey_proto(proto)
|
u_int proto;
|
{
|
switch (proto) {
|
case IPSECDOI_PROTO_IPSEC_AH:
|
return SADB_SATYPE_AH;
|
case IPSECDOI_PROTO_IPSEC_ESP:
|
return SADB_SATYPE_ESP;
|
case IPSECDOI_PROTO_IPCOMP:
|
return SADB_X_SATYPE_IPCOMP;
|
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid ipsec_doi proto: %u\n", proto);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
static u_int
|
ipsecdoi2pfkey_alg(algclass, type)
|
u_int algclass, type;
|
{
|
switch (algclass) {
|
case IPSECDOI_ATTR_AUTH:
|
return ipsecdoi2pfkey_aalg(type);
|
case IPSECDOI_PROTO_IPSEC_ESP:
|
return ipsecdoi2pfkey_ealg(type);
|
case IPSECDOI_PROTO_IPCOMP:
|
return ipsecdoi2pfkey_calg(type);
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid ipsec_doi algclass: %u\n", algclass);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* SADB_SATYPE -> IPSECDOI_PROTO */
|
u_int
|
pfkey2ipsecdoi_proto(satype)
|
u_int satype;
|
{
|
switch (satype) {
|
case SADB_SATYPE_AH:
|
return IPSECDOI_PROTO_IPSEC_AH;
|
case SADB_SATYPE_ESP:
|
return IPSECDOI_PROTO_IPSEC_ESP;
|
case SADB_X_SATYPE_IPCOMP:
|
return IPSECDOI_PROTO_IPCOMP;
|
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"Invalid pfkey proto: %u\n", satype);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
|
u_int
|
ipsecdoi2pfkey_mode(mode)
|
u_int mode;
|
{
|
switch (mode) {
|
case IPSECDOI_ATTR_ENC_MODE_TUNNEL:
|
#ifdef ENABLE_NATT
|
case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC:
|
case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT:
|
#endif
|
return IPSEC_MODE_TUNNEL;
|
case IPSECDOI_ATTR_ENC_MODE_TRNS:
|
#ifdef ENABLE_NATT
|
case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC:
|
case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT:
|
#endif
|
return IPSEC_MODE_TRANSPORT;
|
default:
|
plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */
|
u_int
|
pfkey2ipsecdoi_mode(mode)
|
u_int mode;
|
{
|
switch (mode) {
|
case IPSEC_MODE_TUNNEL:
|
return IPSECDOI_ATTR_ENC_MODE_TUNNEL;
|
case IPSEC_MODE_TRANSPORT:
|
return IPSECDOI_ATTR_ENC_MODE_TRNS;
|
case IPSEC_MODE_ANY:
|
return IPSECDOI_ATTR_ENC_MODE_ANY;
|
default:
|
plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode);
|
return ~0;
|
}
|
/*NOTREACHED*/
|
}
|
|
/* default key length for encryption algorithm */
|
static u_int
|
keylen_aalg(hashtype)
|
u_int hashtype;
|
{
|
int res;
|
|
if (hashtype == 0)
|
return SADB_AALG_NONE;
|
|
res = alg_ipsec_hmacdef_hashlen(hashtype);
|
if (res == -1) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid hmac algorithm %u.\n", hashtype);
|
return ~0;
|
}
|
return res;
|
}
|
|
/* default key length for encryption algorithm */
|
static u_int
|
keylen_ealg(enctype, encklen)
|
u_int enctype;
|
int encklen;
|
{
|
int res;
|
|
res = alg_ipsec_encdef_keylen(enctype, encklen);
|
if (res == -1) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encryption algorithm %u.\n", enctype);
|
return ~0;
|
}
|
return res;
|
}
|
|
int
|
pfkey_convertfromipsecdoi(proto_id, t_id, hashtype,
|
e_type, e_keylen, a_type, a_keylen, flags)
|
u_int proto_id;
|
u_int t_id;
|
u_int hashtype;
|
u_int *e_type;
|
u_int *e_keylen;
|
u_int *a_type;
|
u_int *a_keylen;
|
u_int *flags;
|
{
|
*flags = 0;
|
switch (proto_id) {
|
case IPSECDOI_PROTO_IPSEC_ESP:
|
if ((*e_type = ipsecdoi2pfkey_ealg(t_id)) == ~0)
|
goto bad;
|
if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0)
|
goto bad;
|
*e_keylen >>= 3;
|
|
if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
|
goto bad;
|
if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
|
goto bad;
|
*a_keylen >>= 3;
|
|
if (*e_type == SADB_EALG_NONE) {
|
plog(LLV_ERROR, LOCATION, NULL, "no ESP algorithm.\n");
|
goto bad;
|
}
|
break;
|
|
case IPSECDOI_PROTO_IPSEC_AH:
|
if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0)
|
goto bad;
|
if ((*a_keylen = keylen_aalg(hashtype)) == ~0)
|
goto bad;
|
*a_keylen >>= 3;
|
|
if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5
|
&& hashtype == IPSECDOI_ATTR_AUTH_KPDK) {
|
/* AH_MD5 + Auth(KPDK) = RFC1826 keyed-MD5 */
|
*a_type = SADB_X_AALG_MD5;
|
*flags |= SADB_X_EXT_OLD;
|
}
|
*e_type = SADB_EALG_NONE;
|
*e_keylen = 0;
|
if (*a_type == SADB_AALG_NONE) {
|
plog(LLV_ERROR, LOCATION, NULL, "no AH algorithm.\n");
|
goto bad;
|
}
|
break;
|
|
case IPSECDOI_PROTO_IPCOMP:
|
if ((*e_type = ipsecdoi2pfkey_calg(t_id)) == ~0)
|
goto bad;
|
*e_keylen = 0;
|
|
*flags = SADB_X_EXT_RAWCPI;
|
|
*a_type = SADB_AALG_NONE;
|
*a_keylen = 0;
|
if (*e_type == SADB_X_CALG_NONE) {
|
plog(LLV_ERROR, LOCATION, NULL, "no IPCOMP algorithm.\n");
|
goto bad;
|
}
|
break;
|
|
default:
|
plog(LLV_ERROR, LOCATION, NULL, "unknown IPsec protocol.\n");
|
goto bad;
|
}
|
|
return 0;
|
|
bad:
|
errno = EINVAL;
|
return -1;
|
}
|
|
/* called from scheduler */
|
void
|
pfkey_timeover_stub(p)
|
void *p;
|
{
|
|
pfkey_timeover((struct ph2handle *)p);
|
}
|
|
void
|
pfkey_timeover(iph2)
|
struct ph2handle *iph2;
|
{
|
plog(LLV_ERROR, LOCATION, NULL,
|
"%s give up to get IPsec-SA due to time up to wait.\n",
|
saddrwop2str(iph2->dst));
|
SCHED_KILL(iph2->sce);
|
|
/* If initiator side, send error to kernel by SADB_ACQUIRE. */
|
if (iph2->side == INITIATOR)
|
pk_sendeacquire(iph2);
|
|
unbindph12(iph2);
|
remph2(iph2);
|
delph2(iph2);
|
|
return;
|
}
|
|
/*%%%*/
|
/* send getspi message per ipsec protocol per remote address */
|
/*
|
* the local address and remote address in ph1handle are dealed
|
* with destination address and source address respectively.
|
* Because SPI is decided by responder.
|
*/
|
int
|
pk_sendgetspi(iph2)
|
struct ph2handle *iph2;
|
{
|
struct sockaddr *src = NULL, *dst = NULL;
|
u_int satype, mode;
|
struct saprop *pp;
|
struct saproto *pr;
|
u_int32_t minspi, maxspi;
|
int proxy = 0;
|
|
if (iph2->side == INITIATOR) {
|
pp = iph2->proposal;
|
proxy = iph2->ph1->rmconf->support_proxy;
|
} else {
|
pp = iph2->approval;
|
if (iph2->sainfo && iph2->sainfo->id_i)
|
proxy = 1;
|
}
|
|
/* for mobile IPv6 */
|
if (proxy && iph2->src_id && iph2->dst_id &&
|
ipsecdoi_transportmode(pp)) {
|
src = iph2->src_id;
|
dst = iph2->dst_id;
|
} else {
|
src = iph2->src;
|
dst = iph2->dst;
|
}
|
|
for (pr = pp->head; pr != NULL; pr = pr->next) {
|
|
/* validity check */
|
satype = ipsecdoi2pfkey_proto(pr->proto_id);
|
if (satype == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", pr->proto_id);
|
return -1;
|
}
|
/* this works around a bug in Linux kernel where it allocates 4 byte
|
spi's for IPCOMP */
|
else if (satype == SADB_X_SATYPE_IPCOMP) {
|
minspi = 0x100;
|
maxspi = 0xffff;
|
}
|
else {
|
minspi = 0;
|
maxspi = 0;
|
}
|
mode = ipsecdoi2pfkey_mode(pr->encmode);
|
if (mode == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encmode %d\n", pr->encmode);
|
return -1;
|
}
|
|
#ifdef ENABLE_NATT
|
/* XXX should we do a copy of src/dst for each pr ?
|
*/
|
if (! pr->udp_encap) {
|
/* Remove port information, that SA doesn't use it */
|
set_port(src, 0);
|
set_port(dst, 0);
|
}
|
#endif
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_getspi\n");
|
if (pfkey_send_getspi(
|
lcconf->sock_pfkey,
|
satype,
|
mode,
|
dst, /* src of SA */
|
src, /* dst of SA */
|
minspi, maxspi,
|
pr->reqid_in, iph2->seq) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"ipseclib failed send getspi (%s)\n",
|
ipsec_strerror());
|
return -1;
|
}
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"pfkey GETSPI sent: %s\n",
|
sadbsecas2str(dst, src, satype, 0, mode));
|
}
|
|
return 0;
|
}
|
|
/*
|
* receive GETSPI from kernel.
|
*/
|
static int
|
pk_recvgetspi(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_sa *sa;
|
struct ph2handle *iph2;
|
struct sockaddr *dst;
|
int proto_id;
|
int allspiok, notfound;
|
struct saprop *pp;
|
struct saproto *pr;
|
|
/* validity check */
|
if (mhp[SADB_EXT_SA] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb getspi message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); /* note SA dir */
|
|
/* the message has to be processed or not ? */
|
if (msg->sadb_msg_pid != getpid()) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"%s message is not interesting "
|
"because pid %d is not mine.\n",
|
s_pfkey_type(msg->sadb_msg_type),
|
msg->sadb_msg_pid);
|
return -1;
|
}
|
|
iph2 = getph2byseq(msg->sadb_msg_seq);
|
if (iph2 == NULL) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"seq %d of %s message not interesting.\n",
|
msg->sadb_msg_seq,
|
s_pfkey_type(msg->sadb_msg_type));
|
return -1;
|
}
|
|
if (iph2->status != PHASE2ST_GETSPISENT) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"status mismatch (db:%d msg:%d)\n",
|
iph2->status, PHASE2ST_GETSPISENT);
|
return -1;
|
}
|
|
/* set SPI, and check to get all spi whether or not */
|
allspiok = 1;
|
notfound = 1;
|
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
|
pp = iph2->side == INITIATOR ? iph2->proposal : iph2->approval;
|
|
for (pr = pp->head; pr != NULL; pr = pr->next) {
|
if (pr->proto_id == proto_id && pr->spi == 0) {
|
pr->spi = sa->sadb_sa_spi;
|
notfound = 0;
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"pfkey GETSPI succeeded: %s\n",
|
sadbsecas2str(iph2->dst, iph2->src,
|
msg->sadb_msg_satype,
|
sa->sadb_sa_spi,
|
ipsecdoi2pfkey_mode(pr->encmode)));
|
}
|
if (pr->spi == 0)
|
allspiok = 0; /* not get all spi */
|
}
|
|
if (notfound) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"get spi for unknown address %s\n",
|
saddrwop2str(iph2->dst));
|
return -1;
|
}
|
|
if (allspiok) {
|
/* update status */
|
iph2->status = PHASE2ST_GETSPIDONE;
|
if (isakmp_post_getspi(iph2) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to start post getspi.\n");
|
unbindph12(iph2);
|
remph2(iph2);
|
delph2(iph2);
|
iph2 = NULL;
|
return -1;
|
}
|
}
|
|
return 0;
|
}
|
|
/*
|
* set inbound SA
|
*/
|
int
|
pk_sendupdate(iph2)
|
struct ph2handle *iph2;
|
{
|
struct saproto *pr;
|
struct pfkey_send_sa_args sa_args;
|
int proxy = 0;
|
|
/* sanity check */
|
if (iph2->approval == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"no approvaled SAs found.\n");
|
}
|
|
if (iph2->side == INITIATOR)
|
proxy = iph2->ph1->rmconf->support_proxy;
|
else if (iph2->sainfo && iph2->sainfo->id_i)
|
proxy = 1;
|
|
/* fill in some needed for pfkey_send_update2 */
|
memset (&sa_args, 0, sizeof (sa_args));
|
sa_args.so = lcconf->sock_pfkey;
|
sa_args.l_addtime = iph2->approval->lifetime;
|
sa_args.seq = iph2->seq;
|
sa_args.wsize = 4;
|
|
/* for mobile IPv6 */
|
if (proxy && iph2->src_id && iph2->dst_id &&
|
ipsecdoi_transportmode(iph2->approval)) {
|
sa_args.dst = iph2->src_id;
|
sa_args.src = iph2->dst_id;
|
} else {
|
sa_args.dst = iph2->src;
|
sa_args.src = iph2->dst;
|
}
|
|
for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
|
/* validity check */
|
sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
|
if (sa_args.satype == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", pr->proto_id);
|
return -1;
|
}
|
else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) {
|
/* IPCOMP has no replay window */
|
sa_args.wsize = 0;
|
}
|
#ifdef ENABLE_SAMODE_UNSPECIFIED
|
sa_args.mode = IPSEC_MODE_ANY;
|
#else
|
sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode);
|
if (sa_args.mode == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encmode %d\n", pr->encmode);
|
return -1;
|
}
|
#endif
|
/* set algorithm type and key length */
|
sa_args.e_keylen = pr->head->encklen;
|
if (pfkey_convertfromipsecdoi(
|
pr->proto_id,
|
pr->head->trns_id,
|
pr->head->authtype,
|
&sa_args.e_type, &sa_args.e_keylen,
|
&sa_args.a_type, &sa_args.a_keylen,
|
&sa_args.flags) < 0)
|
return -1;
|
|
#if 0
|
sa_args.l_bytes = iph2->approval->lifebyte * 1024,
|
#else
|
sa_args.l_bytes = 0;
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (*iph2->approval->sctx.ctx_str) {
|
sa_args.ctxdoi = iph2->approval->sctx.ctx_doi;
|
sa_args.ctxalg = iph2->approval->sctx.ctx_alg;
|
sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen;
|
sa_args.ctxstr = iph2->approval->sctx.ctx_str;
|
}
|
#endif /* HAVE_SECCTX */
|
|
#ifdef ENABLE_NATT
|
if (pr->udp_encap) {
|
sa_args.l_natt_type = iph2->ph1->natt_options->encaps_type;
|
sa_args.l_natt_sport = extract_port (iph2->ph1->remote);
|
sa_args.l_natt_dport = extract_port (iph2->ph1->local);
|
sa_args.l_natt_oa = NULL; // FIXME: Here comes OA!!!
|
#ifdef SADB_X_EXT_NAT_T_FRAG
|
sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag;
|
#endif
|
} else {
|
/* Remove port information, that SA doesn't use it */
|
set_port(sa_args.src, 0);
|
set_port(sa_args.dst, 0);
|
}
|
|
#endif
|
/* more info to fill in */
|
sa_args.spi = pr->spi;
|
sa_args.reqid = pr->reqid_in;
|
sa_args.keymat = pr->keymat->v;
|
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update2\n");
|
if (pfkey_send_update2(&sa_args) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send update (%s)\n",
|
ipsec_strerror());
|
return -1;
|
}
|
|
#ifndef ANDROID_PATCHED
|
if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA])
|
continue;
|
|
/*
|
* It maybe good idea to call backupsa_to_file() after
|
* racoon will receive the sadb_update messages.
|
* But it is impossible because there is not key in the
|
* information from the kernel.
|
*/
|
|
/* change some things before backing up */
|
sa_args.wsize = 4;
|
sa_args.l_bytes = iph2->approval->lifebyte * 1024;
|
|
if (backupsa_to_file(&sa_args) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"backuped SA failed: %s\n",
|
sadbsecas2str(sa_args.src, sa_args.dst,
|
sa_args.satype, sa_args.spi, sa_args.mode));
|
}
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"backuped SA: %s\n",
|
sadbsecas2str(sa_args.src, sa_args.dst,
|
sa_args.satype, sa_args.spi, sa_args.mode));
|
#endif
|
}
|
|
return 0;
|
}
|
|
static int
|
pk_recvupdate(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_sa *sa;
|
struct sockaddr *src, *dst;
|
struct ph2handle *iph2;
|
u_int proto_id, encmode, sa_mode;
|
int incomplete = 0;
|
struct saproto *pr;
|
|
/* ignore this message because of local test mode. */
|
if (f_local)
|
return 0;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_SA] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb update message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
|
|
sa_mode = mhp[SADB_X_EXT_SA2] == NULL
|
? IPSEC_MODE_ANY
|
: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
|
|
/* the message has to be processed or not ? */
|
if (msg->sadb_msg_pid != getpid()) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"%s message is not interesting "
|
"because pid %d is not mine.\n",
|
s_pfkey_type(msg->sadb_msg_type),
|
msg->sadb_msg_pid);
|
return -1;
|
}
|
|
iph2 = getph2byseq(msg->sadb_msg_seq);
|
if (iph2 == NULL) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"seq %d of %s message not interesting.\n",
|
msg->sadb_msg_seq,
|
s_pfkey_type(msg->sadb_msg_type));
|
return -1;
|
}
|
|
if (iph2->status != PHASE2ST_ADDSA) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"status mismatch (db:%d msg:%d)\n",
|
iph2->status, PHASE2ST_ADDSA);
|
return -1;
|
}
|
|
/* check to complete all keys ? */
|
for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
|
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
|
if (proto_id == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", msg->sadb_msg_satype);
|
return -1;
|
}
|
encmode = pfkey2ipsecdoi_mode(sa_mode);
|
if (encmode == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encmode %d\n", sa_mode);
|
return -1;
|
}
|
|
if (pr->proto_id == proto_id
|
&& pr->spi == sa->sadb_sa_spi) {
|
pr->ok = 1;
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"pfkey UPDATE succeeded: %s\n",
|
sadbsecas2str(iph2->dst, iph2->src,
|
msg->sadb_msg_satype,
|
sa->sadb_sa_spi,
|
sa_mode));
|
|
plog(LLV_INFO, LOCATION, NULL,
|
"IPsec-SA established: %s\n",
|
sadbsecas2str(iph2->dst, iph2->src,
|
msg->sadb_msg_satype, sa->sadb_sa_spi,
|
sa_mode));
|
}
|
|
if (pr->ok == 0)
|
incomplete = 1;
|
}
|
|
if (incomplete)
|
return 0;
|
|
/* turn off the timer for calling pfkey_timeover() */
|
SCHED_KILL(iph2->sce);
|
|
/* update status */
|
iph2->status = PHASE2ST_ESTABLISHED;
|
|
#ifdef ENABLE_STATS
|
gettimeofday(&iph2->end, NULL);
|
syslog(LOG_NOTICE, "%s(%s): %8.6f",
|
"phase2", "quick", timedelta(&iph2->start, &iph2->end));
|
#endif
|
|
/* count up */
|
iph2->ph1->ph2cnt++;
|
|
/* turn off schedule */
|
SCHED_KILL(iph2->scr);
|
|
/* Force the update of ph2's ports, as there is at least one
|
* situation where they'll mismatch with ph1's values
|
*/
|
|
#ifdef ENABLE_NATT
|
set_port(iph2->src, extract_port(iph2->ph1->local));
|
set_port(iph2->dst, extract_port(iph2->ph1->remote));
|
#endif
|
|
/*
|
* since we are going to reuse the phase2 handler, we need to
|
* remain it and refresh all the references between ph1 and ph2 to use.
|
*/
|
unbindph12(iph2);
|
|
iph2->sce = sched_new(iph2->approval->lifetime,
|
isakmp_ph2expire_stub, iph2);
|
|
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
|
return 0;
|
}
|
|
/*
|
* set outbound SA
|
*/
|
int
|
pk_sendadd(iph2)
|
struct ph2handle *iph2;
|
{
|
struct saproto *pr;
|
int proxy = 0;
|
struct pfkey_send_sa_args sa_args;
|
|
/* sanity check */
|
if (iph2->approval == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"no approvaled SAs found.\n");
|
return -1;
|
}
|
|
if (iph2->side == INITIATOR)
|
proxy = iph2->ph1->rmconf->support_proxy;
|
else if (iph2->sainfo && iph2->sainfo->id_i)
|
proxy = 1;
|
|
/* fill in some needed for pfkey_send_update2 */
|
memset (&sa_args, 0, sizeof (sa_args));
|
sa_args.so = lcconf->sock_pfkey;
|
sa_args.l_addtime = iph2->approval->lifetime;
|
sa_args.seq = iph2->seq;
|
sa_args.wsize = 4;
|
|
/* for mobile IPv6 */
|
if (proxy && iph2->src_id && iph2->dst_id &&
|
ipsecdoi_transportmode(iph2->approval)) {
|
sa_args.src = iph2->src_id;
|
sa_args.dst = iph2->dst_id;
|
} else {
|
sa_args.src = iph2->src;
|
sa_args.dst = iph2->dst;
|
}
|
|
for (pr = iph2->approval->head; pr != NULL; pr = pr->next) {
|
/* validity check */
|
sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id);
|
if (sa_args.satype == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", pr->proto_id);
|
return -1;
|
}
|
else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) {
|
/* no replay window for IPCOMP */
|
sa_args.wsize = 0;
|
}
|
#ifdef ENABLE_SAMODE_UNSPECIFIED
|
sa_args.mode = IPSEC_MODE_ANY;
|
#else
|
sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode);
|
if (sa_args.mode == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encmode %d\n", pr->encmode);
|
return -1;
|
}
|
#endif
|
|
/* set algorithm type and key length */
|
sa_args.e_keylen = pr->head->encklen;
|
if (pfkey_convertfromipsecdoi(
|
pr->proto_id,
|
pr->head->trns_id,
|
pr->head->authtype,
|
&sa_args.e_type, &sa_args.e_keylen,
|
&sa_args.a_type, &sa_args.a_keylen,
|
&sa_args.flags) < 0)
|
return -1;
|
|
#if 0
|
sa_args.l_bytes = iph2->approval->lifebyte * 1024,
|
#else
|
sa_args.l_bytes = 0;
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (*iph2->approval->sctx.ctx_str) {
|
sa_args.ctxdoi = iph2->approval->sctx.ctx_doi;
|
sa_args.ctxalg = iph2->approval->sctx.ctx_alg;
|
sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen;
|
sa_args.ctxstr = iph2->approval->sctx.ctx_str;
|
}
|
#endif /* HAVE_SECCTX */
|
|
#ifdef ENABLE_NATT
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2 "
|
"(NAT flavor)\n");
|
|
if (pr->udp_encap) {
|
sa_args.l_natt_type = UDP_ENCAP_ESPINUDP;
|
sa_args.l_natt_sport = extract_port(iph2->ph1->local);
|
sa_args.l_natt_dport = extract_port(iph2->ph1->remote);
|
sa_args.l_natt_oa = NULL; // FIXME: Here comes OA!!!
|
#ifdef SADB_X_EXT_NAT_T_FRAG
|
sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag;
|
#endif
|
} else {
|
/* Remove port information, that SA doesn't use it */
|
set_port(sa_args.src, 0);
|
set_port(sa_args.dst, 0);
|
}
|
|
#else
|
/* Remove port information, it is not used without NAT-T */
|
set_port(sa_args.src, 0);
|
set_port(sa_args.dst, 0);
|
#endif
|
|
/* more info to fill in */
|
sa_args.spi = pr->spi_p;
|
sa_args.reqid = pr->reqid_out;
|
sa_args.keymat = pr->keymat_p->v;
|
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2\n");
|
if (pfkey_send_add2(&sa_args) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send add (%s)\n",
|
ipsec_strerror());
|
return -1;
|
}
|
|
#ifndef ANDROID_PATCHED
|
if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA])
|
continue;
|
|
/*
|
* It maybe good idea to call backupsa_to_file() after
|
* racoon will receive the sadb_update messages.
|
* But it is impossible because there is not key in the
|
* information from the kernel.
|
*/
|
if (backupsa_to_file(&sa_args) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"backuped SA failed: %s\n",
|
sadbsecas2str(sa_args.src, sa_args.dst,
|
sa_args.satype, sa_args.spi, sa_args.mode));
|
}
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"backuped SA: %s\n",
|
sadbsecas2str(sa_args.src, sa_args.dst,
|
sa_args.satype, sa_args.spi, sa_args.mode));
|
#endif
|
}
|
return 0;
|
}
|
|
static int
|
pk_recvadd(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_sa *sa;
|
struct sockaddr *src, *dst;
|
struct ph2handle *iph2;
|
u_int sa_mode;
|
|
/* ignore this message because of local test mode. */
|
if (f_local)
|
return 0;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_SA] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb add message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
|
|
sa_mode = mhp[SADB_X_EXT_SA2] == NULL
|
? IPSEC_MODE_ANY
|
: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
|
|
/* the message has to be processed or not ? */
|
if (msg->sadb_msg_pid != getpid()) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"%s message is not interesting "
|
"because pid %d is not mine.\n",
|
s_pfkey_type(msg->sadb_msg_type),
|
msg->sadb_msg_pid);
|
return -1;
|
}
|
|
iph2 = getph2byseq(msg->sadb_msg_seq);
|
if (iph2 == NULL) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"seq %d of %s message not interesting.\n",
|
msg->sadb_msg_seq,
|
s_pfkey_type(msg->sadb_msg_type));
|
return -1;
|
}
|
|
/*
|
* NOTE don't update any status of phase2 handle
|
* because they must be updated by SADB_UPDATE message
|
*/
|
|
plog(LLV_INFO, LOCATION, NULL,
|
"IPsec-SA established: %s\n",
|
sadbsecas2str(iph2->src, iph2->dst,
|
msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));
|
|
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
|
return 0;
|
}
|
|
static int
|
pk_recvexpire(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_sa *sa;
|
struct sockaddr *src, *dst;
|
struct ph2handle *iph2;
|
u_int proto_id, sa_mode;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_SA] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| (mhp[SADB_EXT_LIFETIME_HARD] != NULL
|
&& mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb expire message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
|
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
|
sa_mode = mhp[SADB_X_EXT_SA2] == NULL
|
? IPSEC_MODE_ANY
|
: ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode;
|
|
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
|
if (proto_id == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", msg->sadb_msg_satype);
|
return -1;
|
}
|
|
plog(LLV_INFO, LOCATION, NULL,
|
"IPsec-SA expired: %s\n",
|
sadbsecas2str(src, dst,
|
msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode));
|
|
iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
|
if (iph2 == NULL) {
|
/*
|
* Ignore it because two expire messages are come up.
|
* phase2 handler has been deleted already when 2nd message
|
* is received.
|
*/
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"no such a SA found: %s\n",
|
sadbsecas2str(src, dst,
|
msg->sadb_msg_satype, sa->sadb_sa_spi,
|
sa_mode));
|
return 0;
|
}
|
if (iph2->status != PHASE2ST_ESTABLISHED) {
|
/*
|
* If the status is not equal to PHASE2ST_ESTABLISHED,
|
* racoon ignores this expire message. There are two reason.
|
* One is that the phase 2 probably starts because there is
|
* a potential that racoon receives the acquire message
|
* without receiving a expire message. Another is that racoon
|
* may receive the multiple expire messages from the kernel.
|
*/
|
plog(LLV_WARNING, LOCATION, NULL,
|
"the expire message is received "
|
"but the handler has not been established.\n");
|
return 0;
|
}
|
|
/* turn off the timer for calling isakmp_ph2expire() */
|
SCHED_KILL(iph2->sce);
|
|
iph2->status = PHASE2ST_EXPIRED;
|
|
/* INITIATOR, begin phase 2 exchange. */
|
/* allocate buffer for status management of pfkey message */
|
if (iph2->side == INITIATOR) {
|
|
initph2(iph2);
|
|
/* update status for re-use */
|
iph2->status = PHASE2ST_STATUS2;
|
|
/* start isakmp initiation by using ident exchange */
|
if (isakmp_post_acquire(iph2) < 0) {
|
plog(LLV_ERROR, LOCATION, iph2->dst,
|
"failed to begin ipsec sa "
|
"re-negotication.\n");
|
unbindph12(iph2);
|
remph2(iph2);
|
delph2(iph2);
|
return -1;
|
}
|
|
return 0;
|
/*NOTREACHED*/
|
}
|
|
/* If not received SADB_EXPIRE, INITIATOR delete ph2handle. */
|
/* RESPONDER always delete ph2handle, keep silent. RESPONDER doesn't
|
* manage IPsec SA, so delete the list */
|
unbindph12(iph2);
|
remph2(iph2);
|
delph2(iph2);
|
|
return 0;
|
}
|
|
static int
|
pk_recvacquire(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_x_policy *xpl;
|
struct secpolicy *sp_out = NULL, *sp_in = NULL;
|
#define MAXNESTEDSA 5 /* XXX */
|
struct ph2handle *iph2[MAXNESTEDSA];
|
struct sockaddr *src, *dst;
|
int n; /* # of phase 2 handler */
|
int remoteid=0;
|
#ifdef HAVE_SECCTX
|
struct sadb_x_sec_ctx *m_sec_ctx;
|
#endif /* HAVE_SECCTX */
|
struct policyindex spidx;
|
|
|
/* ignore this message because of local test mode. */
|
if (f_local)
|
return 0;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb acquire message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
|
#ifdef HAVE_SECCTX
|
m_sec_ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
|
if (m_sec_ctx != NULL) {
|
plog(LLV_INFO, LOCATION, NULL, "security context doi: %u\n",
|
m_sec_ctx->sadb_x_ctx_doi);
|
plog(LLV_INFO, LOCATION, NULL,
|
"security context algorithm: %u\n",
|
m_sec_ctx->sadb_x_ctx_alg);
|
plog(LLV_INFO, LOCATION, NULL, "security context length: %u\n",
|
m_sec_ctx->sadb_x_ctx_len);
|
plog(LLV_INFO, LOCATION, NULL, "security context: %s\n",
|
((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)));
|
}
|
#endif /* HAVE_SECCTX */
|
|
/* ignore if type is not IPSEC_POLICY_IPSEC */
|
if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"ignore ACQUIRE message. type is not IPsec.\n");
|
return 0;
|
}
|
|
/* ignore it if src is multicast address */
|
{
|
struct sockaddr *sa = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
|
if ((sa->sa_family == AF_INET
|
&& IN_MULTICAST(ntohl(((struct sockaddr_in *)sa)->sin_addr.s_addr)))
|
#ifdef INET6
|
|| (sa->sa_family == AF_INET6
|
&& IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sa)->sin6_addr))
|
#endif
|
) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"ignore due to multicast address: %s.\n",
|
saddrwop2str(sa));
|
return 0;
|
}
|
}
|
|
/* ignore, if we do not listen on source address */
|
{
|
/* reasons behind:
|
* - if we'll contact peer from address we do not listen -
|
* we will be unable to complete negotiation;
|
* - if we'll negotiate using address we're listening -
|
* remote peer will send packets to address different
|
* than one in the policy, so kernel will drop them;
|
* => therefore this acquire is not for us! --Aidas
|
*/
|
struct sockaddr *sa = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
struct myaddrs *p;
|
int do_listen = 0;
|
for (p = lcconf->myaddrs; p; p = p->next) {
|
if (!cmpsaddrwop(p->addr, sa)) {
|
do_listen = 1;
|
break;
|
}
|
}
|
|
if (!do_listen) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"ignore because do not listen on source address : %s.\n",
|
saddrwop2str(sa));
|
return 0;
|
}
|
}
|
|
/*
|
* If there is a phase 2 handler against the policy identifier in
|
* the acquire message, and if
|
* 1. its state is less than PHASE2ST_ESTABLISHED, then racoon
|
* should ignore such a acquire message because the phase 2
|
* is just negotiating.
|
* 2. its state is equal to PHASE2ST_ESTABLISHED, then racoon
|
* has to prcesss such a acquire message because racoon may
|
* lost the expire message.
|
*/
|
iph2[0] = getph2byid(src, dst, xpl->sadb_x_policy_id);
|
if (iph2[0] != NULL) {
|
if (iph2[0]->status < PHASE2ST_ESTABLISHED) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"ignore the acquire because ph2 found\n");
|
return -1;
|
}
|
if (iph2[0]->status == PHASE2ST_EXPIRED)
|
iph2[0] = NULL;
|
/*FALLTHROUGH*/
|
}
|
|
/* search for proper policyindex */
|
sp_out = getspbyspid(xpl->sadb_x_policy_id);
|
if (sp_out == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL, "no policy found: id:%d.\n",
|
xpl->sadb_x_policy_id);
|
return -1;
|
}
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"suitable outbound SP found: %s.\n", spidx2str(&sp_out->spidx));
|
|
/* get inbound policy */
|
{
|
|
memset(&spidx, 0, sizeof(spidx));
|
spidx.dir = IPSEC_DIR_INBOUND;
|
memcpy(&spidx.src, &sp_out->spidx.dst, sizeof(spidx.src));
|
memcpy(&spidx.dst, &sp_out->spidx.src, sizeof(spidx.dst));
|
spidx.prefs = sp_out->spidx.prefd;
|
spidx.prefd = sp_out->spidx.prefs;
|
spidx.ul_proto = sp_out->spidx.ul_proto;
|
|
#ifdef HAVE_SECCTX
|
if (m_sec_ctx) {
|
spidx.sec_ctx.ctx_doi = m_sec_ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_alg = m_sec_ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_strlen = m_sec_ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str,
|
((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)),
|
spidx.sec_ctx.ctx_strlen);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp_in = getsp(&spidx);
|
if (sp_in) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"suitable inbound SP found: %s.\n",
|
spidx2str(&sp_in->spidx));
|
} else {
|
plog(LLV_NOTIFY, LOCATION, NULL,
|
"no in-bound policy found: %s\n",
|
spidx2str(&spidx));
|
}
|
}
|
|
memset(iph2, 0, MAXNESTEDSA);
|
|
n = 0;
|
|
/* allocate a phase 2 */
|
iph2[n] = newph2();
|
if (iph2[n] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to allocate phase2 entry.\n");
|
return -1;
|
}
|
iph2[n]->side = INITIATOR;
|
iph2[n]->spid = xpl->sadb_x_policy_id;
|
iph2[n]->satype = msg->sadb_msg_satype;
|
iph2[n]->seq = msg->sadb_msg_seq;
|
iph2[n]->status = PHASE2ST_STATUS2;
|
|
/* set end addresses of SA */
|
iph2[n]->dst = dupsaddr(PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]));
|
if (iph2[n]->dst == NULL) {
|
delph2(iph2[n]);
|
return -1;
|
}
|
iph2[n]->src = dupsaddr(PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]));
|
if (iph2[n]->src == NULL) {
|
delph2(iph2[n]);
|
return -1;
|
}
|
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"new acquire %s\n", spidx2str(&sp_out->spidx));
|
|
/* get sainfo */
|
{
|
vchar_t *idsrc, *iddst;
|
|
idsrc = ipsecdoi_sockaddr2id((struct sockaddr *)&sp_out->spidx.src,
|
sp_out->spidx.prefs, sp_out->spidx.ul_proto);
|
if (idsrc == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to get ID for %s\n",
|
spidx2str(&sp_out->spidx));
|
delph2(iph2[n]);
|
return -1;
|
}
|
iddst = ipsecdoi_sockaddr2id((struct sockaddr *)&sp_out->spidx.dst,
|
sp_out->spidx.prefd, sp_out->spidx.ul_proto);
|
if (iddst == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to get ID for %s\n",
|
spidx2str(&sp_out->spidx));
|
vfree(idsrc);
|
delph2(iph2[n]);
|
return -1;
|
}
|
{
|
struct remoteconf *conf;
|
conf = getrmconf(iph2[n]->dst);
|
if (conf != NULL)
|
remoteid=conf->ph1id;
|
else{
|
plog(LLV_DEBUG, LOCATION, NULL, "Warning: no valid rmconf !\n");
|
remoteid=0;
|
}
|
}
|
iph2[n]->sainfo = getsainfo(idsrc, iddst, NULL, remoteid);
|
vfree(idsrc);
|
vfree(iddst);
|
if (iph2[n]->sainfo == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to get sainfo.\n");
|
delph2(iph2[n]);
|
return -1;
|
/* XXX should use the algorithm list from register message */
|
}
|
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"selected sainfo: %s\n", sainfo2str(iph2[n]->sainfo));
|
}
|
|
if (set_proposal_from_policy(iph2[n], sp_out, sp_in) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to create saprop.\n");
|
delph2(iph2[n]);
|
return -1;
|
}
|
#ifdef HAVE_SECCTX
|
if (m_sec_ctx) {
|
set_secctx_in_proposal(iph2[n], spidx);
|
}
|
#endif /* HAVE_SECCTX */
|
|
insph2(iph2[n]);
|
|
/* start isakmp initiation by using ident exchange */
|
/* XXX should be looped if there are multiple phase 2 handler. */
|
if (isakmp_post_acquire(iph2[n]) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to begin ipsec sa negotication.\n");
|
goto err;
|
}
|
|
return 0;
|
|
err:
|
while (n >= 0) {
|
unbindph12(iph2[n]);
|
remph2(iph2[n]);
|
delph2(iph2[n]);
|
iph2[n] = NULL;
|
n--;
|
}
|
return -1;
|
}
|
|
static int
|
pk_recvdelete(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_sa *sa;
|
struct sockaddr *src, *dst;
|
struct ph2handle *iph2 = NULL;
|
u_int proto_id;
|
|
/* ignore this message because of local test mode. */
|
if (f_local)
|
return 0;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_SA] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb delete message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
sa = (struct sadb_sa *)mhp[SADB_EXT_SA];
|
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
|
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
|
|
/* the message has to be processed or not ? */
|
if (msg->sadb_msg_pid == getpid()) {
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"%s message is not interesting "
|
"because the message was originated by me.\n",
|
s_pfkey_type(msg->sadb_msg_type));
|
return -1;
|
}
|
|
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
|
if (proto_id == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", msg->sadb_msg_satype);
|
return -1;
|
}
|
|
iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
|
if (iph2 == NULL) {
|
/* ignore */
|
plog(LLV_ERROR, LOCATION, NULL,
|
"no iph2 found: %s\n",
|
sadbsecas2str(src, dst, msg->sadb_msg_satype,
|
sa->sadb_sa_spi, IPSEC_MODE_ANY));
|
return 0;
|
}
|
|
plog(LLV_ERROR, LOCATION, NULL,
|
"pfkey DELETE received: %s\n",
|
sadbsecas2str(iph2->src, iph2->dst,
|
msg->sadb_msg_satype, sa->sadb_sa_spi, IPSEC_MODE_ANY));
|
|
/* send delete information */
|
if (iph2->status == PHASE2ST_ESTABLISHED)
|
isakmp_info_send_d2(iph2);
|
|
unbindph12(iph2);
|
remph2(iph2);
|
delph2(iph2);
|
|
return 0;
|
}
|
|
static int
|
pk_recvflush(mhp)
|
caddr_t *mhp;
|
{
|
/* ignore this message because of local test mode. */
|
if (f_local)
|
return 0;
|
|
/* sanity check */
|
if (mhp[0] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb flush message passed.\n");
|
return -1;
|
}
|
|
flushph2();
|
|
return 0;
|
}
|
|
static int
|
getsadbpolicy(policy0, policylen0, type, iph2)
|
caddr_t *policy0;
|
int *policylen0, type;
|
struct ph2handle *iph2;
|
{
|
struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
|
struct sadb_x_policy *xpl;
|
struct sadb_x_ipsecrequest *xisr;
|
struct saproto *pr;
|
struct saproto **pr_rlist;
|
int rlist_len = 0;
|
caddr_t policy, p;
|
int policylen;
|
int xisrlen;
|
u_int satype, mode;
|
int len = 0;
|
#ifdef HAVE_SECCTX
|
int ctxlen = 0;
|
#endif /* HAVE_SECCTX */
|
|
|
/* get policy buffer size */
|
policylen = sizeof(struct sadb_x_policy);
|
if (type != SADB_X_SPDDELETE) {
|
for (pr = iph2->approval->head; pr; pr = pr->next) {
|
xisrlen = sizeof(*xisr);
|
if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) {
|
xisrlen += (sysdep_sa_len(iph2->src)
|
+ sysdep_sa_len(iph2->dst));
|
}
|
|
policylen += PFKEY_ALIGN8(xisrlen);
|
}
|
}
|
|
#ifdef HAVE_SECCTX
|
if (*spidx->sec_ctx.ctx_str) {
|
ctxlen = sizeof(struct sadb_x_sec_ctx)
|
+ PFKEY_ALIGN8(spidx->sec_ctx.ctx_strlen);
|
policylen += ctxlen;
|
}
|
#endif /* HAVE_SECCTX */
|
|
/* make policy structure */
|
policy = racoon_malloc(policylen);
|
memset((void*)policy, 0xcd, policylen);
|
if (!policy) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"buffer allocation failed.\n");
|
return -1;
|
}
|
|
xpl = (struct sadb_x_policy *)policy;
|
xpl->sadb_x_policy_len = PFKEY_UNIT64(policylen);
|
xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY;
|
xpl->sadb_x_policy_type = IPSEC_POLICY_IPSEC;
|
xpl->sadb_x_policy_dir = spidx->dir;
|
xpl->sadb_x_policy_id = 0;
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
xpl->sadb_x_policy_priority = PRIORITY_DEFAULT;
|
#endif
|
len++;
|
|
#ifdef HAVE_SECCTX
|
if (*spidx->sec_ctx.ctx_str) {
|
struct sadb_x_sec_ctx *p;
|
|
p = (struct sadb_x_sec_ctx *)(xpl + len);
|
memset(p, 0, ctxlen);
|
p->sadb_x_sec_len = PFKEY_UNIT64(ctxlen);
|
p->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX;
|
p->sadb_x_ctx_len = spidx->sec_ctx.ctx_strlen;
|
p->sadb_x_ctx_doi = spidx->sec_ctx.ctx_doi;
|
p->sadb_x_ctx_alg = spidx->sec_ctx.ctx_alg;
|
|
memcpy(p + 1,spidx->sec_ctx.ctx_str,spidx->sec_ctx.ctx_strlen);
|
len += ctxlen;
|
}
|
#endif /* HAVE_SECCTX */
|
|
/* no need to append policy information any more if type is SPDDELETE */
|
if (type == SADB_X_SPDDELETE)
|
goto end;
|
|
xisr = (struct sadb_x_ipsecrequest *)(xpl + len);
|
|
/* The order of things is reversed for use in add policy messages */
|
for (pr = iph2->approval->head; pr; pr = pr->next) rlist_len++;
|
pr_rlist = racoon_malloc((rlist_len+1)*sizeof(struct saproto*));
|
if (!pr_rlist) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"buffer allocation failed.\n");
|
return -1;
|
}
|
pr_rlist[rlist_len--] = NULL;
|
for (pr = iph2->approval->head; pr; pr = pr->next) pr_rlist[rlist_len--] = pr;
|
rlist_len = 0;
|
|
for (pr = pr_rlist[rlist_len++]; pr; pr = pr_rlist[rlist_len++]) {
|
|
satype = doi2ipproto(pr->proto_id);
|
if (satype == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto_id %d\n", pr->proto_id);
|
goto err;
|
}
|
mode = ipsecdoi2pfkey_mode(pr->encmode);
|
if (mode == ~0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid encmode %d\n", pr->encmode);
|
goto err;
|
}
|
|
/*
|
* the policy level cannot be unique because the policy
|
* is defined later than SA, so req_id cannot be bound to SA.
|
*/
|
xisr->sadb_x_ipsecrequest_proto = satype;
|
xisr->sadb_x_ipsecrequest_mode = mode;
|
if(iph2->proposal->head->reqid_in > 0){
|
xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE;
|
xisr->sadb_x_ipsecrequest_reqid = iph2->proposal->head->reqid_in;
|
}else{
|
xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE;
|
xisr->sadb_x_ipsecrequest_reqid = 0;
|
}
|
p = (caddr_t)(xisr + 1);
|
|
xisrlen = sizeof(*xisr);
|
|
if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) {
|
int src_len, dst_len;
|
|
src_len = sysdep_sa_len(iph2->src);
|
dst_len = sysdep_sa_len(iph2->dst);
|
xisrlen += src_len + dst_len;
|
|
memcpy(p, iph2->src, src_len);
|
p += src_len;
|
|
memcpy(p, iph2->dst, dst_len);
|
p += dst_len;
|
}
|
|
xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(xisrlen);
|
xisr = (struct sadb_x_ipsecrequest *)p;
|
|
}
|
racoon_free(pr_rlist);
|
|
end:
|
*policy0 = policy;
|
*policylen0 = policylen;
|
|
return 0;
|
|
err:
|
if (policy)
|
racoon_free(policy);
|
if (pr_rlist) racoon_free(pr_rlist);
|
|
return -1;
|
}
|
|
int
|
pk_sendspdupdate2(iph2)
|
struct ph2handle *iph2;
|
{
|
struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
|
caddr_t policy = NULL;
|
int policylen = 0;
|
u_int64_t ltime, vtime;
|
|
ltime = iph2->approval->lifetime;
|
vtime = 0;
|
|
if (getsadbpolicy(&policy, &policylen, SADB_X_SPDUPDATE, iph2)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"getting sadb policy failed.\n");
|
return -1;
|
}
|
|
if (pfkey_send_spdupdate2(
|
lcconf->sock_pfkey,
|
(struct sockaddr *)&spidx->src,
|
spidx->prefs,
|
(struct sockaddr *)&spidx->dst,
|
spidx->prefd,
|
spidx->ul_proto,
|
ltime, vtime,
|
policy, policylen, 0) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send spdupdate2 (%s)\n",
|
ipsec_strerror());
|
goto end;
|
}
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdupdate2\n");
|
|
end:
|
if (policy)
|
racoon_free(policy);
|
|
return 0;
|
}
|
|
static int
|
pk_recvspdupdate(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
struct policyindex spidx;
|
struct secpolicy *sp;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spdupdate message passed.\n");
|
return -1;
|
}
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp = getsp(&spidx);
|
if (sp == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"such policy does not already exist: \"%s\"\n",
|
spidx2str(&spidx));
|
} else {
|
remsp(sp);
|
delsp(sp);
|
}
|
|
if (addnewsp(mhp) < 0)
|
return -1;
|
|
return 0;
|
}
|
|
/*
|
* this function has to be used by responder side.
|
*/
|
int
|
pk_sendspdadd2(iph2)
|
struct ph2handle *iph2;
|
{
|
struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
|
caddr_t policy = NULL;
|
int policylen = 0;
|
u_int64_t ltime, vtime;
|
|
ltime = iph2->approval->lifetime;
|
vtime = 0;
|
|
if (getsadbpolicy(&policy, &policylen, SADB_X_SPDADD, iph2)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"getting sadb policy failed.\n");
|
return -1;
|
}
|
|
if (pfkey_send_spdadd2(
|
lcconf->sock_pfkey,
|
(struct sockaddr *)&spidx->src,
|
spidx->prefs,
|
(struct sockaddr *)&spidx->dst,
|
spidx->prefd,
|
spidx->ul_proto,
|
ltime, vtime,
|
policy, policylen, 0) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send spdadd2 (%s)\n",
|
ipsec_strerror());
|
goto end;
|
}
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdadd2\n");
|
|
end:
|
if (policy)
|
racoon_free(policy);
|
|
return 0;
|
}
|
|
static int
|
pk_recvspdadd(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
struct policyindex spidx;
|
struct secpolicy *sp;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spdadd message passed.\n");
|
return -1;
|
}
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp = getsp(&spidx);
|
if (sp != NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"such policy already exists. "
|
"anyway replace it: %s\n",
|
spidx2str(&spidx));
|
remsp(sp);
|
delsp(sp);
|
}
|
|
if (addnewsp(mhp) < 0)
|
return -1;
|
|
return 0;
|
}
|
|
/*
|
* this function has to be used by responder side.
|
*/
|
int
|
pk_sendspddelete(iph2)
|
struct ph2handle *iph2;
|
{
|
struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen;
|
caddr_t policy = NULL;
|
int policylen;
|
|
if (getsadbpolicy(&policy, &policylen, SADB_X_SPDDELETE, iph2)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"getting sadb policy failed.\n");
|
return -1;
|
}
|
|
if (pfkey_send_spddelete(
|
lcconf->sock_pfkey,
|
(struct sockaddr *)&spidx->src,
|
spidx->prefs,
|
(struct sockaddr *)&spidx->dst,
|
spidx->prefd,
|
spidx->ul_proto,
|
policy, policylen, 0) < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"libipsec failed send spddelete (%s)\n",
|
ipsec_strerror());
|
goto end;
|
}
|
plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spddelete\n");
|
|
end:
|
if (policy)
|
racoon_free(policy);
|
|
return 0;
|
}
|
|
static int
|
pk_recvspddelete(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
struct policyindex spidx;
|
struct secpolicy *sp;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spddelete message passed.\n");
|
return -1;
|
}
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp = getsp(&spidx);
|
if (sp == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"no policy found: %s\n",
|
spidx2str(&spidx));
|
return -1;
|
}
|
|
remsp(sp);
|
delsp(sp);
|
|
return 0;
|
}
|
|
static int
|
pk_recvspdexpire(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
struct policyindex spidx;
|
struct secpolicy *sp;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[0] == NULL
|
|| mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spdexpire message passed.\n");
|
return -1;
|
}
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp = getsp(&spidx);
|
if (sp == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"no policy found: %s\n",
|
spidx2str(&spidx));
|
return -1;
|
}
|
|
remsp(sp);
|
delsp(sp);
|
|
return 0;
|
}
|
|
static int
|
pk_recvspdget(mhp)
|
caddr_t *mhp;
|
{
|
/* sanity check */
|
if (mhp[0] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spdget message passed.\n");
|
return -1;
|
}
|
|
return 0;
|
}
|
|
static int
|
pk_recvspddump(mhp)
|
caddr_t *mhp;
|
{
|
struct sadb_msg *msg;
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
struct policyindex spidx;
|
struct secpolicy *sp;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[0] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spddump message passed.\n");
|
return -1;
|
}
|
msg = (struct sadb_msg *)mhp[0];
|
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
if (saddr == NULL || daddr == NULL || xpl == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spddump message passed.\n");
|
return -1;
|
}
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
sp = getsp(&spidx);
|
if (sp != NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"such policy already exists. "
|
"anyway replace it: %s\n",
|
spidx2str(&spidx));
|
remsp(sp);
|
delsp(sp);
|
}
|
|
if (addnewsp(mhp) < 0)
|
return -1;
|
|
return 0;
|
}
|
|
static int
|
pk_recvspdflush(mhp)
|
caddr_t *mhp;
|
{
|
/* sanity check */
|
if (mhp[0] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spdflush message passed.\n");
|
return -1;
|
}
|
|
flushsp();
|
|
return 0;
|
}
|
|
#ifndef ANDROID_PATCHED
|
|
/*
|
* send error against acquire message to kenrel.
|
*/
|
int
|
pk_sendeacquire(iph2)
|
struct ph2handle *iph2;
|
{
|
struct sadb_msg *newmsg;
|
int len;
|
|
len = sizeof(struct sadb_msg);
|
newmsg = racoon_calloc(1, len);
|
if (newmsg == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to get buffer to send acquire.\n");
|
return -1;
|
}
|
|
memset(newmsg, 0, len);
|
newmsg->sadb_msg_version = PF_KEY_V2;
|
newmsg->sadb_msg_type = SADB_ACQUIRE;
|
newmsg->sadb_msg_errno = ENOENT; /* XXX */
|
newmsg->sadb_msg_satype = iph2->satype;
|
newmsg->sadb_msg_len = PFKEY_UNIT64(len);
|
newmsg->sadb_msg_reserved = 0;
|
newmsg->sadb_msg_seq = iph2->seq;
|
newmsg->sadb_msg_pid = (u_int32_t)getpid();
|
|
/* send message */
|
len = pfkey_send(lcconf->sock_pfkey, newmsg, len);
|
|
racoon_free(newmsg);
|
|
return 0;
|
}
|
|
#else
|
|
int pk_sendeacquire(struct ph2handle *iph2)
|
{
|
exit(1);
|
}
|
|
#endif
|
|
/*
|
* check if the algorithm is supported or not.
|
* OUT 0: ok
|
* -1: ng
|
*/
|
int
|
pk_checkalg(class, calg, keylen)
|
int class, calg, keylen;
|
{
|
int sup, error;
|
u_int alg;
|
struct sadb_alg alg0;
|
|
switch (algclass2doi(class)) {
|
case IPSECDOI_PROTO_IPSEC_ESP:
|
sup = SADB_EXT_SUPPORTED_ENCRYPT;
|
break;
|
case IPSECDOI_ATTR_AUTH:
|
sup = SADB_EXT_SUPPORTED_AUTH;
|
break;
|
case IPSECDOI_PROTO_IPCOMP:
|
plog(LLV_DEBUG, LOCATION, NULL,
|
"compression algorithm can not be checked "
|
"because sadb message doesn't support it.\n");
|
return 0;
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid algorithm class.\n");
|
return -1;
|
}
|
alg = ipsecdoi2pfkey_alg(algclass2doi(class), algtype2doi(class, calg));
|
if (alg == ~0)
|
return -1;
|
|
if (keylen == 0) {
|
if (ipsec_get_keylen(sup, alg, &alg0)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"%s.\n", ipsec_strerror());
|
return -1;
|
}
|
keylen = alg0.sadb_alg_minbits;
|
}
|
|
error = ipsec_check_keylen(sup, alg, keylen);
|
if (error)
|
plog(LLV_ERROR, LOCATION, NULL,
|
"%s.\n", ipsec_strerror());
|
|
return error;
|
}
|
|
/*
|
* differences with pfkey_recv() in libipsec/pfkey.c:
|
* - never performs busy wait loop.
|
* - returns NULL and set *lenp to negative on fatal failures
|
* - returns NULL and set *lenp to non-negative on non-fatal failures
|
* - returns non-NULL on success
|
*/
|
static struct sadb_msg *
|
pk_recv(so, lenp)
|
int so;
|
int *lenp;
|
{
|
struct sadb_msg buf, *newmsg;
|
int reallen;
|
int retry = 0;
|
|
*lenp = -1;
|
do
|
{
|
plog(LLV_DEBUG, LOCATION, NULL, "pk_recv: retry[%d] recv() \n", retry );
|
*lenp = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT);
|
retry++;
|
}
|
while (*lenp < 0 && errno == EAGAIN && retry < 3);
|
|
if (*lenp < 0)
|
return NULL; /*fatal*/
|
|
else if (*lenp < sizeof(buf))
|
return NULL;
|
|
reallen = PFKEY_UNUNIT64(buf.sadb_msg_len);
|
if ((newmsg = racoon_calloc(1, reallen)) == NULL)
|
return NULL;
|
|
*lenp = recv(so, (caddr_t)newmsg, reallen, MSG_PEEK);
|
if (*lenp < 0) {
|
racoon_free(newmsg);
|
return NULL; /*fatal*/
|
} else if (*lenp != reallen) {
|
racoon_free(newmsg);
|
return NULL;
|
}
|
|
*lenp = recv(so, (caddr_t)newmsg, reallen, 0);
|
if (*lenp < 0) {
|
racoon_free(newmsg);
|
return NULL; /*fatal*/
|
} else if (*lenp != reallen) {
|
racoon_free(newmsg);
|
return NULL;
|
}
|
|
return newmsg;
|
}
|
|
/* see handler.h */
|
u_int32_t
|
pk_getseq()
|
{
|
return eay_random();
|
}
|
|
static int
|
addnewsp(mhp)
|
caddr_t *mhp;
|
{
|
struct secpolicy *new = NULL;
|
struct sadb_address *saddr, *daddr;
|
struct sadb_x_policy *xpl;
|
struct sadb_lifetime *lt;
|
u_int64_t created;
|
|
/* sanity check */
|
if (mhp[SADB_EXT_ADDRESS_SRC] == NULL
|
|| mhp[SADB_EXT_ADDRESS_DST] == NULL
|
|| mhp[SADB_X_EXT_POLICY] == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"inappropriate sadb spd management message passed.\n");
|
goto bad;
|
}
|
|
saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC];
|
daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST];
|
xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY];
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD];
|
if(lt != NULL)
|
created = lt->sadb_lifetime_addtime;
|
else
|
created = 0;
|
|
#ifdef __linux__
|
/* bsd skips over per-socket policies because there will be no
|
* src and dst extensions in spddump messages. On Linux the only
|
* way to achieve the same is check for policy id.
|
*/
|
if (xpl->sadb_x_policy_id % 8 >= 3) return 0;
|
#endif
|
|
new = newsp();
|
if (new == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to allocate buffer\n");
|
goto bad;
|
}
|
|
new->spidx.dir = xpl->sadb_x_policy_dir;
|
new->id = xpl->sadb_x_policy_id;
|
new->policy = xpl->sadb_x_policy_type;
|
new->req = NULL;
|
|
/* check policy */
|
switch (xpl->sadb_x_policy_type) {
|
case IPSEC_POLICY_DISCARD:
|
case IPSEC_POLICY_NONE:
|
case IPSEC_POLICY_ENTRUST:
|
case IPSEC_POLICY_BYPASS:
|
break;
|
|
case IPSEC_POLICY_IPSEC:
|
{
|
int tlen;
|
struct sadb_x_ipsecrequest *xisr;
|
struct ipsecrequest **p_isr = &new->req;
|
|
/* validity check */
|
if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid msg length.\n");
|
goto bad;
|
}
|
|
tlen = PFKEY_EXTLEN(xpl) - sizeof(*xpl);
|
xisr = (struct sadb_x_ipsecrequest *)(xpl + 1);
|
|
while (tlen > 0) {
|
|
/* length check */
|
if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid msg length.\n");
|
goto bad;
|
}
|
|
/* allocate request buffer */
|
*p_isr = newipsecreq();
|
if (*p_isr == NULL) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"failed to get new ipsecreq.\n");
|
goto bad;
|
}
|
|
/* set values */
|
(*p_isr)->next = NULL;
|
|
switch (xisr->sadb_x_ipsecrequest_proto) {
|
case IPPROTO_ESP:
|
case IPPROTO_AH:
|
case IPPROTO_IPCOMP:
|
break;
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid proto type: %u\n",
|
xisr->sadb_x_ipsecrequest_proto);
|
goto bad;
|
}
|
(*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto;
|
|
switch (xisr->sadb_x_ipsecrequest_mode) {
|
case IPSEC_MODE_TRANSPORT:
|
case IPSEC_MODE_TUNNEL:
|
break;
|
case IPSEC_MODE_ANY:
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid mode: %u\n",
|
xisr->sadb_x_ipsecrequest_mode);
|
goto bad;
|
}
|
(*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode;
|
|
switch (xisr->sadb_x_ipsecrequest_level) {
|
case IPSEC_LEVEL_DEFAULT:
|
case IPSEC_LEVEL_USE:
|
case IPSEC_LEVEL_REQUIRE:
|
break;
|
case IPSEC_LEVEL_UNIQUE:
|
(*p_isr)->saidx.reqid =
|
xisr->sadb_x_ipsecrequest_reqid;
|
break;
|
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid level: %u\n",
|
xisr->sadb_x_ipsecrequest_level);
|
goto bad;
|
}
|
(*p_isr)->level = xisr->sadb_x_ipsecrequest_level;
|
|
/* set IP addresses if there */
|
if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) {
|
struct sockaddr *paddr;
|
|
paddr = (struct sockaddr *)(xisr + 1);
|
bcopy(paddr, &(*p_isr)->saidx.src,
|
sysdep_sa_len(paddr));
|
|
paddr = (struct sockaddr *)((caddr_t)paddr
|
+ sysdep_sa_len(paddr));
|
bcopy(paddr, &(*p_isr)->saidx.dst,
|
sysdep_sa_len(paddr));
|
}
|
|
(*p_isr)->sp = new;
|
|
/* initialization for the next. */
|
p_isr = &(*p_isr)->next;
|
tlen -= xisr->sadb_x_ipsecrequest_len;
|
|
/* validity check */
|
if (tlen < 0) {
|
plog(LLV_ERROR, LOCATION, NULL,
|
"becoming tlen < 0\n");
|
}
|
|
xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr
|
+ xisr->sadb_x_ipsecrequest_len);
|
}
|
}
|
break;
|
default:
|
plog(LLV_ERROR, LOCATION, NULL,
|
"invalid policy type.\n");
|
goto bad;
|
}
|
|
#ifdef HAVE_PFKEY_POLICY_PRIORITY
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
xpl->sadb_x_policy_priority,
|
created,
|
&new->spidx);
|
#else
|
KEY_SETSECSPIDX(xpl->sadb_x_policy_dir,
|
saddr + 1,
|
daddr + 1,
|
saddr->sadb_address_prefixlen,
|
daddr->sadb_address_prefixlen,
|
saddr->sadb_address_proto,
|
created,
|
&new->spidx);
|
#endif
|
|
#ifdef HAVE_SECCTX
|
if (mhp[SADB_X_EXT_SEC_CTX] != NULL) {
|
struct sadb_x_sec_ctx *ctx;
|
|
ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX];
|
new->spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg;
|
new->spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi;
|
new->spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len;
|
memcpy(new->spidx.sec_ctx.ctx_str,ctx + 1,ctx->sadb_x_ctx_len);
|
}
|
#endif /* HAVE_SECCTX */
|
|
inssp(new);
|
|
return 0;
|
bad:
|
if (new != NULL) {
|
if (new->req != NULL)
|
racoon_free(new->req);
|
racoon_free(new);
|
}
|
return -1;
|
}
|
|
/* proto/mode/src->dst spi */
|
const char *
|
sadbsecas2str(src, dst, proto, spi, mode)
|
struct sockaddr *src, *dst;
|
int proto;
|
u_int32_t spi;
|
int mode;
|
{
|
static char buf[256];
|
u_int doi_proto, doi_mode = 0;
|
char *p;
|
int blen, i;
|
|
doi_proto = pfkey2ipsecdoi_proto(proto);
|
if (doi_proto == ~0)
|
return NULL;
|
if (mode) {
|
doi_mode = pfkey2ipsecdoi_mode(mode);
|
if (doi_mode == ~0)
|
return NULL;
|
}
|
|
blen = sizeof(buf) - 1;
|
p = buf;
|
|
i = snprintf(p, blen, "%s%s%s ",
|
s_ipsecdoi_proto(doi_proto),
|
mode ? "/" : "",
|
mode ? s_ipsecdoi_encmode(doi_mode) : "");
|
if (i < 0 || i >= blen)
|
return NULL;
|
p += i;
|
blen -= i;
|
|
i = snprintf(p, blen, "%s->", saddr2str(src));
|
if (i < 0 || i >= blen)
|
return NULL;
|
p += i;
|
blen -= i;
|
|
i = snprintf(p, blen, "%s ", saddr2str(dst));
|
if (i < 0 || i >= blen)
|
return NULL;
|
p += i;
|
blen -= i;
|
|
if (spi) {
|
snprintf(p, blen, "spi=%lu(0x%lx)", (unsigned long)ntohl(spi),
|
(unsigned long)ntohl(spi));
|
}
|
|
return buf;
|
}
|