#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/queue.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include PATH_IPSEC_H
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
#include <netdb.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <ctype.h>
#ifdef ENABLE_HYBRID
#include <resolv.h>
#endif
#include "var.h"
#include "misc.h"
#include "vmbuf.h"
#include "plog.h"
#include "sockmisc.h"
#include "schedule.h"
#include "session.h"
#include "debug.h"
#include "remoteconf.h"
#include "localconf.h"
#include "grabmyaddr.h"
#include "admin.h"
#include "privsep.h"
#include "isakmp_var.h"
#include "isakmp.h"
#include "oakley.h"
#include "evt.h"
#include "handler.h"
#include "ipsec_doi.h"
#include "pfkey.h"
#include "crypto_openssl.h"
#include "policy.h"
#include "algorithm.h"
#include "proposal.h"
#include "sainfo.h"
#include "isakmp_ident.h"
#include "isakmp_agg.h"
#include "isakmp_base.h"
#include "isakmp_quick.h"
#include "isakmp_inf.h"
#include "isakmp_newg.h"
#ifdef ENABLE_HYBRID
#include "vendorid.h"
#include "isakmp_xauth.h"
#include "isakmp_unity.h"
#include "isakmp_cfg.h"
#endif
#ifdef ENABLE_FRAG
#include "isakmp_frag.h"
#endif
#include "strnames.h"
#include "str2val.h"
#include <fcntl.h>
#ifdef ENABLE_NATT
# include "nattraversal.h"
#endif
# ifdef __linux__
# include <linux/udp.h>
# include <linux/ip.h>
# ifndef SOL_UDP
# define SOL_UDP 17
# endif
# endif
# if defined(__NetBSD__) || defined(__FreeBSD__) || \
(defined(__APPLE__) && defined(__MACH__))
# include <netinet/in.h>
# include <netinet/udp.h>
# include <netinet/in_systm.h>
# include <netinet/ip.h>
# define SOL_UDP IPPROTO_UDP
# endif
static int nostate1(struct ph1handle *, vchar_t *);
static int nostate2(struct ph2handle *, vchar_t *);
static int (*ph1exchange[][2][PHASE1ST_MAX])(struct ph1handle *, vchar_t *) = {
{ { 0 }, { 0 }, },
{
{ nostate1, ident_i1send, nostate1, ident_i2recv, ident_i2send,
ident_i3recv, ident_i3send, ident_i4recv, ident_i4send, nostate1, nostate1,},
{ nostate1, ident_r1recv, ident_r1send, ident_r2recv, ident_r2send,
ident_r3recv, ident_r3send, nostate1, nostate1, nostate1, nostate1, },
},
{
{ nostate1, agg_i1send, nostate1, agg_i2recv, agg_i2send,
nostate1, nostate1, nostate1, nostate1, nostate1, nostate1, },
{ nostate1, agg_r1recv, agg_r1send, agg_r2recv, agg_r2send,
nostate1, nostate1, nostate1, nostate1, nostate1, nostate1, },
},
{
{ nostate1, base_i1send, nostate1, base_i2recv, base_i2send,
base_i3recv, base_i3send, nostate1, nostate1, nostate1, nostate1, },
{ nostate1, base_r1recv, base_r1send, base_r2recv, base_r2send,
nostate1, nostate1, nostate1, nostate1, nostate1, nostate1, },
},
};
static int (*ph2exchange[][2][PHASE2ST_MAX])(struct ph2handle *, vchar_t *) = {
{ { 0 }, { 0 }, },
{
{ nostate2, nostate2, quick_i1prep, nostate2, quick_i1send,
quick_i2recv, quick_i2send, quick_i3recv, nostate2, nostate2, },
{ nostate2, quick_r1recv, quick_r1prep, nostate2, quick_r2send,
quick_r3recv, quick_r3prep, quick_r3send, nostate2, nostate2, }
},
};
static u_char r_ck0[] = { 0,0,0,0,0,0,0,0 };
static int isakmp_main(vchar_t *, struct sockaddr *, struct sockaddr *);
static int ph1_main(struct ph1handle *, vchar_t *);
static int quick_main(struct ph2handle *, vchar_t *);
static int isakmp_ph1begin_r(vchar_t *, struct sockaddr *, struct sockaddr *,
uint8_t);
static int isakmp_ph2begin_i(struct ph1handle *, struct ph2handle *);
static int isakmp_ph2begin_r(struct ph1handle *, vchar_t *);
static int etypesw1(int);
static int etypesw2(int);
static int isakmp_ph1resend(struct ph1handle *);
static int isakmp_ph2resend(struct ph2handle *);
#ifdef ENABLE_FRAG
static int frag_handler(struct ph1handle *,
vchar_t *, struct sockaddr *, struct sockaddr *);
#endif
static int
isakmp_handler(void *ctx __unused, int so_isakmp)
{
struct isakmp isakmp;
union {
char buf[sizeof (isakmp) + 4];
uint32_t non_esp[2];
struct {
struct udphdr udp;
#ifdef __linux
struct iphdr ip;
#else
struct ip ip;
#endif
char buf[sizeof(isakmp) + 4];
} lbuf;
} x;
struct sockaddr_storage remote;
struct sockaddr_storage local;
unsigned int remote_len = sizeof(remote);
unsigned int local_len = sizeof(local);
ssize_t len = 0;
int extralen = 0;
vchar_t *buf = NULL, *tmpbuf = NULL;
int error = -1, res;
while ((len = recvfromto(so_isakmp, x.buf, sizeof(x),
MSG_PEEK, (struct sockaddr *)&remote, &remote_len,
(struct sockaddr *)&local, &local_len)) < 0) {
if (errno == EINTR)
continue;
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive isakmp packet: %s\n",
strerror (errno));
goto end;
}
if (len == 1 && (x.buf[0]&0xff) == 0xff) {
if ((len = recvfrom(so_isakmp, (char *)x.buf, 1,
0, (struct sockaddr *)&remote, &remote_len)) != 1) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive keep alive packet: %s\n",
strerror (errno));
}
goto end;
}
{
#ifdef __linux__
if (ntohs(x.lbuf.udp.dest) == 501) {
extralen += sizeof(x.lbuf.udp) + x.lbuf.ip.ihl;
}
#else
if (ntohs(x.lbuf.udp.uh_dport) == 501) {
extralen += sizeof(x.lbuf.udp) + x.lbuf.ip.ip_hl;
}
#endif
}
#ifdef ENABLE_NATT
if (x.non_esp[0] == 0 && x.non_esp[1] != 0)
extralen = NON_ESP_MARKER_LEN;
#endif
memcpy ((char *)&isakmp, x.buf + extralen, sizeof (isakmp));
if (len < sizeof(isakmp) || ntohl(isakmp.len) < sizeof(isakmp)) {
plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
"packet shorter than isakmp header size (%zu, %u, %zu)\n",
len, ntohl(isakmp.len), sizeof(isakmp));
if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
0, (struct sockaddr *)&remote, &remote_len)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive isakmp packet: %s\n",
strerror (errno));
}
goto end;
}
if (ntohl(isakmp.len) > 0xffff) {
plog(LLV_ERROR, LOCATION, NULL,
"the length in the isakmp header is too big.\n");
if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
0, (struct sockaddr *)&remote, &remote_len)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive isakmp packet: %s\n",
strerror (errno));
}
goto end;
}
if ((tmpbuf = vmalloc(ntohl(isakmp.len) + extralen)) == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to allocate reading buffer (%u Bytes)\n",
ntohl(isakmp.len) + extralen);
if ((len = recvfrom(so_isakmp, (char *)&isakmp, sizeof(isakmp),
0, (struct sockaddr *)&remote, &remote_len)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive isakmp packet: %s\n",
strerror (errno));
}
goto end;
}
while ((len = recvfromto(so_isakmp, (char *)tmpbuf->v, tmpbuf->l,
0, (struct sockaddr *)&remote, &remote_len,
(struct sockaddr *)&local, &local_len)) < 0) {
if (errno == EINTR)
continue;
plog(LLV_ERROR, LOCATION, NULL,
"failed to receive isakmp packet: %s\n",
strerror (errno));
goto end;
}
if ((buf = vmalloc(len - extralen)) == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to allocate reading buffer (%zu Bytes)\n",
(len - extralen));
goto end;
}
memcpy (buf->v, tmpbuf->v + extralen, buf->l);
len -= extralen;
if (len != buf->l) {
plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
"received invalid length (%zd != %zu), why ?\n",
len, buf->l);
goto end;
}
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
plog(LLV_DEBUG, LOCATION, NULL,
"%zd bytes message received %s\n",
len, saddr2str_fromto("from %s to %s",
(struct sockaddr *)&remote,
(struct sockaddr *)&local));
plogdump(LLV_DEBUG, buf->v, buf->l);
if (extract_port((struct sockaddr *)&remote) == 0) {
plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote,
"src port == 0 (valid as UDP but not with IKE)\n");
goto end;
}
res=check_recvdpkt((struct sockaddr *)&remote,(struct sockaddr *)&local, buf);
if (res) {
plog(LLV_NOTIFY, LOCATION, NULL,
"the packet is retransmitted by %s (%d).\n",
saddr2str((struct sockaddr *)&remote), res);
error = 0;
goto end;
}
if (isakmp_main(buf, (struct sockaddr *)&remote,
(struct sockaddr *)&local) != 0) goto end;
error = 0;
end:
if (tmpbuf != NULL)
vfree(tmpbuf);
if (buf != NULL)
vfree(buf);
return error;
}
static int
isakmp_main(vchar_t *msg, struct sockaddr *remote, struct sockaddr *local)
{
struct isakmp *isakmp = (struct isakmp *)msg->v;
isakmp_index *index1 = (isakmp_index *)isakmp;
uint32_t msgid = isakmp->msgid;
struct ph1handle *iph1;
#ifdef HAVE_PRINT_ISAKMP_C
isakmp_printpacket(msg, remote, local, 0);
#endif
if (memcmp(&isakmp->i_ck, r_ck0, sizeof(cookie_t)) == 0) {
plog(LLV_ERROR, LOCATION, remote,
"malformed cookie received.\n");
return -1;
}
if (isakmp->v < ISAKMP_VERSION_NUMBER) {
if (ISAKMP_GETMAJORV(isakmp->v) < ISAKMP_MAJOR_VERSION) {
plog(LLV_ERROR, LOCATION, remote,
"invalid major version %d.\n",
ISAKMP_GETMAJORV(isakmp->v));
return -1;
}
#if ISAKMP_MINOR_VERSION > 0
if (ISAKMP_GETMINORV(isakmp->v) < ISAKMP_MINOR_VERSION) {
plog(LLV_ERROR, LOCATION, remote,
"invalid minor version %d.\n",
ISAKMP_GETMINORV(isakmp->v));
return -1;
}
#endif
}
if (isakmp->flags & ~(ISAKMP_FLAG_E | ISAKMP_FLAG_C | ISAKMP_FLAG_A)) {
plog(LLV_ERROR, LOCATION, remote,
"invalid flag 0x%02x.\n", isakmp->flags);
return -1;
}
if (ISSET(isakmp->flags, ISAKMP_FLAG_C)) {
if (isakmp->msgid == 0) {
isakmp_info_send_nx(isakmp, remote, local,
ISAKMP_NTYPE_INVALID_FLAGS, NULL);
plog(LLV_ERROR, LOCATION, remote,
"Commit bit on phase1 forbidden.\n");
return -1;
}
}
iph1 = getph1byindex(index1);
if (iph1 != NULL) {
if (memcmp(&isakmp->r_ck, r_ck0, sizeof(cookie_t)) == 0 &&
iph1->side == INITIATOR) {
plog(LLV_DEBUG, LOCATION, remote,
"malformed cookie received or "
"the initiator's cookies collide.\n");
return -1;
}
#ifdef ENABLE_NATT
if (NATT_AVAILABLE(iph1) &&
! (iph1->natt_flags & NAT_PORTS_CHANGED) &&
((cmpsaddr(iph1->remote, remote) != CMPSADDR_MATCH) ||
(cmpsaddr(iph1->local, local) != CMPSADDR_MATCH)))
{
racoon_free(iph1->remote);
racoon_free(iph1->local);
iph1->remote = NULL;
iph1->local = NULL;
iph1->remote = dupsaddr(remote);
if (iph1->remote == NULL) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"phase1 failed: dupsaddr failed.\n");
remph1(iph1);
delph1(iph1);
return -1;
}
iph1->local = dupsaddr(local);
if (iph1->local == NULL) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"phase1 failed: dupsaddr failed.\n");
remph1(iph1);
delph1(iph1);
return -1;
}
iph1->natt_flags |= NAT_PORTS_CHANGED | NAT_ADD_NON_ESP_MARKER;
plog (LLV_INFO, LOCATION, NULL,
"NAT-T: ports changed to: %s\n",
saddr2str_fromto ("%s<->%s", iph1->remote, iph1->local));
natt_keepalive_add_ph1 (iph1);
}
#endif
if (cmpsaddr(iph1->remote, remote) != CMPSADDR_MATCH) {
char *saddr_db, *saddr_act;
saddr_db = racoon_strdup(saddr2str(iph1->remote));
saddr_act = racoon_strdup(saddr2str(remote));
STRDUP_FATAL(saddr_db);
STRDUP_FATAL(saddr_act);
plog(LLV_WARNING, LOCATION, remote,
"remote address mismatched. db=%s, act=%s\n",
saddr_db, saddr_act);
racoon_free(saddr_db);
racoon_free(saddr_act);
}
}
switch (isakmp->etype) {
case ISAKMP_ETYPE_IDENT:
case ISAKMP_ETYPE_AGG:
case ISAKMP_ETYPE_BASE:
if (isakmp->msgid != 0) {
plog(LLV_ERROR, LOCATION, remote,
"message id should be zero in phase1.\n");
return -1;
}
if (iph1 == NULL) {
iph1 = getph1byindex0(index1);
if (iph1 == NULL) {
if (memcmp(&isakmp->r_ck, r_ck0,
sizeof(cookie_t)) != 0) {
plog(LLV_DEBUG, LOCATION, remote,
"malformed cookie received "
"or the spi expired.\n");
return -1;
}
if (isakmp_ph1begin_r(msg, remote, local,
isakmp->etype) < 0)
return -1;
break;
}
if (iph1->side != INITIATOR) {
plog(LLV_DEBUG, LOCATION, remote,
"malformed cookie received. "
"it has to be as the initiator. %s\n",
isakmp_pindex(&iph1->index, 0));
return -1;
}
}
if (iph1->etype != isakmp->etype) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"exchange type is mismatched: "
"db=%s packet=%s, ignore it.\n",
s_isakmp_etype(iph1->etype),
s_isakmp_etype(isakmp->etype));
return -1;
}
#ifdef ENABLE_FRAG
if (isakmp->np == ISAKMP_NPTYPE_FRAG)
return frag_handler(iph1, msg, remote, local);
#endif
if (ph1_main(iph1, msg) < 0) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"phase1 negotiation failed.\n");
remph1(iph1);
delph1(iph1);
return -1;
}
break;
case ISAKMP_ETYPE_AUTH:
plog(LLV_INFO, LOCATION, remote,
"unsupported exchange %d received.\n",
isakmp->etype);
break;
case ISAKMP_ETYPE_INFO:
case ISAKMP_ETYPE_ACKINFO:
if (iph1 == NULL) {
iph1 = getph1byindex0(index1);
if (iph1 == NULL) {
plog(LLV_ERROR, LOCATION, remote,
"unknown Informational "
"exchange received.\n");
return -1;
}
if (cmpsaddr(iph1->remote, remote) != CMPSADDR_MATCH) {
plog(LLV_WARNING, LOCATION, remote,
"remote address mismatched. "
"db=%s\n",
saddr2str(iph1->remote));
}
}
#ifdef ENABLE_FRAG
if (isakmp->np == ISAKMP_NPTYPE_FRAG)
return frag_handler(iph1, msg, remote, local);
#endif
if (isakmp_info_recv(iph1, msg) < 0)
return -1;
break;
case ISAKMP_ETYPE_QUICK:
{
struct ph2handle *iph2;
if (iph1 == NULL) {
isakmp_info_send_nx(isakmp, remote, local,
ISAKMP_NTYPE_INVALID_COOKIE, NULL);
plog(LLV_ERROR, LOCATION, remote,
"can't start the quick mode, "
"there is no ISAKMP-SA, %s\n",
isakmp_pindex((isakmp_index *)&isakmp->i_ck,
isakmp->msgid));
return -1;
}
#ifdef ENABLE_HYBRID
if (iph1->mode_cfg && iph1->mode_cfg->ivm) {
oakley_delivm(iph1->mode_cfg->ivm);
iph1->mode_cfg->ivm = NULL;
}
#endif
#ifdef ENABLE_FRAG
if (isakmp->np == ISAKMP_NPTYPE_FRAG)
return frag_handler(iph1, msg, remote, local);
#endif
if (iph1->status != PHASE1ST_ESTABLISHED &&
iph1->status != PHASE1ST_DYING) {
plog(LLV_ERROR, LOCATION, remote,
"can't start the quick mode, "
"there is no valid ISAKMP-SA, %s\n",
isakmp_pindex(&iph1->index, iph1->msgid));
return -1;
}
iph2 = getph2bymsgid(iph1, msgid);
if (iph2 == NULL) {
if (isakmp_ph2begin_r(iph1, msg) < 0)
return -1;
return 0;
}
if (ISSET(isakmp->flags, ISAKMP_FLAG_C))
iph2->flags |= ISAKMP_FLAG_C;
if (quick_main(iph2, msg) < 0) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"phase2 negotiation failed.\n");
remph2(iph2);
delph2(iph2);
return -1;
}
}
break;
case ISAKMP_ETYPE_NEWGRP:
if (iph1 == NULL) {
plog(LLV_ERROR, LOCATION, remote,
"Unknown new group mode exchange, "
"there is no ISAKMP-SA.\n");
return -1;
}
#ifdef ENABLE_FRAG
if (isakmp->np == ISAKMP_NPTYPE_FRAG)
return frag_handler(iph1, msg, remote, local);
#endif
isakmp_newgroup_r(iph1, msg);
break;
#ifdef ENABLE_HYBRID
case ISAKMP_ETYPE_CFG:
if (iph1 == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"mode config %d from %s, "
"but we have no ISAKMP-SA.\n",
isakmp->etype, saddr2str(remote));
return -1;
}
#ifdef ENABLE_FRAG
if (isakmp->np == ISAKMP_NPTYPE_FRAG)
return frag_handler(iph1, msg, remote, local);
#endif
isakmp_cfg_r(iph1, msg);
break;
#endif
case ISAKMP_ETYPE_NONE:
default:
plog(LLV_ERROR, LOCATION, NULL,
"Invalid exchange type %d from %s.\n",
isakmp->etype, saddr2str(remote));
return -1;
}
return 0;
}
static int
ph1_main(struct ph1handle *iph1, vchar_t *msg)
{
int error;
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
if (iph1->status >= PHASE1ST_ESTABLISHED)
return 0;
#ifdef ENABLE_STATS
gettimeofday(&start, NULL);
#endif
if (ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status] == NULL) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"why isn't the function defined.\n");
return -1;
}
error = (ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status])(iph1, msg);
if (error != 0) {
if (iph1->side == RESPONDER && iph1->status == PHASE1ST_START) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"failed to pre-process ph1 packet (side: %d, status %d).\n",
iph1->side, iph1->status);
return -1;
} else {
return 0;
}
}
#ifndef ENABLE_FRAG
if (iph1->sendbuf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"no buffer found as sendbuf\n");
return -1;
}
#endif
VPTRINIT(iph1->sendbuf);
sched_cancel(&iph1->scr);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
if ((ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status])(iph1, msg) != 0) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"failed to process ph1 packet (side: %d, status: %d).\n",
iph1->side, iph1->status);
return -1;
}
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase1", s_isakmp_state(iph1->etype, iph1->side, iph1->status),
timedelta(&start, &end));
#endif
if (iph1->status == PHASE1ST_ESTABLISHED) {
#ifdef ENABLE_STATS
gettimeofday(&iph1->end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase1", s_isakmp_etype(iph1->etype),
timedelta(&iph1->start, &iph1->end));
#endif
(void)time(&iph1->created);
migrate_dying_ph12(iph1);
if (ph1_rekey_enabled(iph1)) {
sched_schedule(&iph1->sce,
iph1->approval->lifetime *
PFKEY_SOFT_LIFETIME_RATE / 100,
isakmp_ph1dying_stub);
} else {
sched_schedule(&iph1->sce, iph1->approval->lifetime,
isakmp_ph1expire_stub);
}
#ifdef ENABLE_HYBRID
if (iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) {
switch (iph1->approval->authmethod) {
case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
xauth_sendreq(iph1);
iph1->rmconf->ini_contact = 0;
break;
case OAKLEY_ATTR_AUTH_METHOD_RSASIG:
if (iph1->rmconf->mode_cfg)
error = isakmp_cfg_getconfig(iph1);
break;
default:
break;
}
}
#endif
#ifdef ENABLE_DPD
if(iph1->dpd_support && iph1->rmconf->dpd_interval)
isakmp_sched_r_u(iph1, 0);
#endif
if (!f_local
&& iph1->rmconf->ini_contact && !getcontacted(iph1->remote)) {
isakmp_info_send_n1(iph1,
ISAKMP_NTYPE_INITIAL_CONTACT, NULL);
if (inscontacted(iph1->remote) == -1) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"failed to add contacted list.\n");
}
}
if (iph1->initial_contact_received)
isakmp_info_recv_initialcontact(iph1, NULL);
log_ph1established(iph1);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
if ((iph1->status == PHASE1ST_ESTABLISHED) &&
!iph1->rmconf->mode_cfg) {
switch (iph1->approval->authmethod) {
#ifdef ENABLE_HYBRID
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_PSKEY_R:
case OAKLEY_ATTR_AUTH_METHOD_HYBRID_RSA_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSASIG_R:
case OAKLEY_ATTR_AUTH_METHOD_HYBRID_DSS_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_DSSSIG_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAENC_R:
case OAKLEY_ATTR_AUTH_METHOD_XAUTH_RSAREV_R:
break;
#endif
default:
script_hook(iph1, SCRIPT_PHASE1_UP);
break;
}
}
if ((iph1->rmconf->mode_cfg) &&
!(iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH)) {
error = isakmp_cfg_getconfig(iph1);
}
}
return 0;
}
static int
quick_main(struct ph2handle *iph2, vchar_t *msg)
{
struct isakmp *isakmp = (struct isakmp *)msg->v;
int error;
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
if (iph2->status == PHASE2ST_ESTABLISHED
|| iph2->status == PHASE2ST_GETSPISENT)
return 0;
#ifdef ENABLE_STATS
gettimeofday(&start, NULL);
#endif
if (ph2exchange[etypesw2(isakmp->etype)]
[iph2->side]
[iph2->status] == NULL) {
plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
"why isn't the function defined.\n");
return -1;
}
error = (ph2exchange[etypesw2(isakmp->etype)]
[iph2->side]
[iph2->status])(iph2, msg);
if (error != 0) {
plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
"failed to pre-process ph2 packet (side: %d, status %d).\n",
iph2->side, iph2->status);
if (error == ISAKMP_INTERNAL_ERROR)
return 0;
isakmp_info_send_n1(iph2->ph1, error, NULL);
return -1;
}
if (iph2->status == PHASE2ST_ADDSA)
return 0;
if (iph2->sendbuf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"no buffer found as sendbuf\n");
return -1;
}
VPTRINIT(iph2->sendbuf);
sched_cancel(&iph2->scr);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
if ((ph2exchange[etypesw2(isakmp->etype)]
[iph2->side]
[iph2->status])(iph2, msg) != 0) {
plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
"failed to process ph2 packet (side: %d, status: %d).\n",
iph2->side, iph2->status);
return -1;
}
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase2",
s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status),
timedelta(&start, &end));
#endif
return 0;
}
struct ph1handle *
isakmp_ph1begin_i(struct remoteconf *rmconf,
struct sockaddr *remote, struct sockaddr *local)
{
struct ph1handle *iph1;
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
iph1 = newph1();
if (iph1 == NULL)
return NULL;
iph1->status = PHASE1ST_START;
iph1->rmconf = rmconf;
iph1->side = INITIATOR;
iph1->version = ISAKMP_VERSION_NUMBER;
iph1->msgid = 0;
iph1->flags = 0;
iph1->ph2cnt = 0;
#ifdef HAVE_GSSAPI
iph1->gssapi_state = NULL;
#endif
#ifdef ENABLE_HYBRID
if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL) {
delph1(iph1);
return NULL;
}
#endif
#ifdef ENABLE_FRAG
if(rmconf->ike_frag == ISAKMP_FRAG_FORCE)
iph1->frag = 1;
else
iph1->frag = 0;
iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
if (copy_ph1addresses(iph1, rmconf, remote, local) < 0) {
delph1(iph1);
return NULL;
}
(void)insph1(iph1);
iph1->etype = rmconf->etypes->type;
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
{
char *a;
a = racoon_strdup(saddr2str(iph1->local));
STRDUP_FATAL(a);
plog(LLV_INFO, LOCATION, NULL,
"initiate new phase 1 negotiation: %s<=>%s\n",
a, saddr2str(iph1->remote));
racoon_free(a);
}
plog(LLV_INFO, LOCATION, NULL,
"begin %s mode.\n",
s_isakmp_etype(iph1->etype));
#ifdef ENABLE_STATS
gettimeofday(&iph1->start, NULL);
gettimeofday(&start, NULL);
#endif
if ((ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status])(iph1, NULL) != 0) {
remph1(iph1);
delph1(iph1);
return NULL;
}
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase1",
s_isakmp_state(iph1->etype, iph1->side, iph1->status),
timedelta(&start, &end));
#endif
return iph1;
}
static int
isakmp_ph1begin_r(vchar_t *msg, struct sockaddr *remote, struct sockaddr *local,
uint8_t etype)
{
struct isakmp *isakmp = (struct isakmp *)msg->v;
struct ph1handle *iph1;
struct rmconfselector rmsel;
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
memset(&rmsel, 0, sizeof(rmsel));
rmsel.remote = remote;
if (enumrmconf(&rmsel, check_etypeok, (void *) (intptr_t) etype) == 0) {
plog(LLV_ERROR, LOCATION, remote,
"exchange %s not allowed in any applicable rmconf.\n",
s_isakmp_etype(etype));
return -1;
}
iph1 = newph1();
if (iph1 == NULL)
return -1;
memcpy(&iph1->index.i_ck, &isakmp->i_ck, sizeof(iph1->index.i_ck));
iph1->status = PHASE1ST_START;
iph1->flags = 0;
iph1->side = RESPONDER;
iph1->etype = etype;
iph1->version = isakmp->v;
iph1->msgid = 0;
#ifdef HAVE_GSSAPI
iph1->gssapi_state = NULL;
#endif
#ifdef ENABLE_HYBRID
if ((iph1->mode_cfg = isakmp_cfg_mkstate()) == NULL) {
delph1(iph1);
return -1;
}
#endif
#ifdef ENABLE_FRAG
iph1->frag = 0;
iph1->frag_last_index = 0;
iph1->frag_chain = NULL;
#endif
iph1->approval = NULL;
#ifdef ENABLE_NATT
if(extract_port(local) == lcconf->port_isakmp_natt)
iph1->natt_flags |= (NAT_PORTS_CHANGED);
#endif
if (copy_ph1addresses(iph1, NULL, remote, local) < 0) {
delph1(iph1);
return -1;
}
(void)insph1(iph1);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
{
char *a;
a = racoon_strdup(saddr2str(iph1->local));
STRDUP_FATAL(a);
plog(LLV_INFO, LOCATION, NULL,
"respond new phase 1 negotiation: %s<=>%s\n",
a, saddr2str(iph1->remote));
racoon_free(a);
}
plog(LLV_INFO, LOCATION, NULL,
"begin %s mode.\n", s_isakmp_etype(etype));
#ifdef ENABLE_STATS
gettimeofday(&iph1->start, NULL);
gettimeofday(&start, NULL);
#endif
#ifndef ENABLE_FRAG
if ((ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status])(iph1, msg) < 0
|| (ph1exchange[etypesw1(iph1->etype)]
[iph1->side]
[iph1->status])(iph1, msg) < 0) {
plog(LLV_ERROR, LOCATION, remote,
"failed to process ph1 packet (side: %d, status: %d).\n",
iph1->side, iph1->status);
remph1(iph1);
delph1(iph1);
return -1;
}
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase1",
s_isakmp_state(iph1->etype, iph1->side, iph1->status),
timedelta(&start, &end));
#endif
return 0;
#else
return isakmp_main(msg, remote, local);
#endif
}
static int
isakmp_ph2begin_i(struct ph1handle *iph1, struct ph2handle *iph2)
{
#ifdef ENABLE_HYBRID
if (xauth_check(iph1) != 0) {
plog(LLV_ERROR, LOCATION, NULL,
"Attempt to start phase 2 whereas Xauth failed\n");
return -1;
}
#endif
if (extract_port(iph2->src) == 0)
set_port(iph2->src, extract_port(iph1->local));
if (extract_port(iph2->dst) == 0)
set_port(iph2->dst, extract_port(iph1->remote));
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n");
{
char *a;
a = racoon_strdup(saddr2str(iph2->src));
STRDUP_FATAL(a);
plog(LLV_INFO, LOCATION, NULL,
"initiate new phase 2 negotiation: %s<=>%s\n",
a, saddr2str(iph2->dst));
racoon_free(a);
}
#ifdef ENABLE_STATS
gettimeofday(&iph2->start, NULL);
#endif
if (iph2->status != PHASE2ST_EXPIRED)
bindph12(iph1, iph2);
iph2->status = PHASE2ST_STATUS2;
if ((ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
[iph2->side]
[iph2->status])(iph2, NULL) < 0) {
remph2(iph2);
return -1;
}
return 0;
}
static int
isakmp_ph2begin_r(struct ph1handle *iph1, vchar_t *msg)
{
struct isakmp *isakmp = (struct isakmp *)msg->v;
struct ph2handle *iph2 = 0;
int error;
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
#ifdef ENABLE_HYBRID
if (xauth_check(iph1) != 0) {
plog(LLV_ERROR, LOCATION, NULL,
"Attempt to start phase 2 whereas Xauth failed\n");
return -1;
}
#endif
iph2 = newph2();
if (iph2 == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to allocate phase2 entry.\n");
return -1;
}
iph2->side = RESPONDER;
iph2->status = PHASE2ST_START;
iph2->flags = isakmp->flags;
iph2->msgid = isakmp->msgid;
iph2->seq = pk_getseq();
iph2->ivm = oakley_newiv2(iph1, iph2->msgid);
if (iph2->ivm == NULL) {
delph2(iph2);
return -1;
}
iph2->dst = dupsaddr(iph1->remote);
if (iph2->dst == NULL) {
delph2(iph2);
return -1;
}
iph2->src = dupsaddr(iph1->local);
if (iph2->src == NULL) {
delph2(iph2);
return -1;
}
insph2(iph2);
bindph12(iph1, iph2);
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
{
char *a;
a = racoon_strdup(saddr2str(iph2->src));
STRDUP_FATAL(a);
plog(LLV_INFO, LOCATION, NULL,
"respond new phase 2 negotiation: %s<=>%s\n",
a, saddr2str(iph2->dst));
racoon_free(a);
}
#ifdef ENABLE_STATS
gettimeofday(&start, NULL);
#endif
error = (ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
[iph2->side]
[iph2->status])(iph2, msg);
if (error != 0) {
plog(LLV_ERROR, LOCATION, iph1->remote,
"failed to pre-process ph2 packet (side: %d, status: %d).\n",
iph2->side, iph2->status);
if (error != ISAKMP_INTERNAL_ERROR)
isakmp_info_send_n1(iph2->ph1, error, NULL);
remph2(iph2);
delph2(iph2);
return -1;
}
plog(LLV_DEBUG, LOCATION, NULL, "===\n");
if ((ph2exchange[etypesw2(isakmp->etype)]
[iph2->side]
[iph2->status])(iph2, msg) < 0) {
plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
"failed to process ph2 packet (side: %d, status: %d).\n",
iph2->side, iph2->status);
return -1;
}
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase2",
s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status),
timedelta(&start, &end));
#endif
return 0;
}
vchar_t *
isakmp_parsewoh(int np0, struct isakmp_gen *gen, int len)
{
u_char np = np0 & 0xff;
int tlen, plen;
vchar_t *result;
struct isakmp_parse_t *p, *ep;
plog(LLV_DEBUG, LOCATION, NULL, "begin.\n");
result = vmalloc(sizeof(struct isakmp_parse_t) * 5);
if (result == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get buffer.\n");
return NULL;
}
p = (struct isakmp_parse_t *)result->v;
ep = (struct isakmp_parse_t *)(result->v + result->l - sizeof(*ep));
tlen = len;
while (0 < tlen && np != ISAKMP_NPTYPE_NONE) {
if (tlen <= sizeof(struct isakmp_gen)) {
plog(LLV_ERROR, LOCATION, NULL,
"invalid length of payload\n");
vfree(result);
return NULL;
}
plog(LLV_DEBUG, LOCATION, NULL,
"seen nptype=%u(%s)\n", np, s_isakmp_nptype(np));
p->type = np;
p->len = ntohs(gen->len);
if (p->len < sizeof(struct isakmp_gen) || p->len > tlen) {
plog(LLV_DEBUG, LOCATION, NULL,
"invalid length of payload\n");
vfree(result);
return NULL;
}
p->ptr = gen;
p++;
if (ep <= p) {
off_t off;
off = p - (struct isakmp_parse_t *)result->v;
result = vrealloc(result, result->l * 2);
if (result == NULL) {
plog(LLV_DEBUG, LOCATION, NULL,
"failed to realloc buffer.\n");
vfree(result);
return NULL;
}
ep = (struct isakmp_parse_t *)
(result->v + result->l - sizeof(*ep));
p = (struct isakmp_parse_t *)result->v;
p += off;
}
np = gen->np;
plen = ntohs(gen->len);
gen = (struct isakmp_gen *)((caddr_t)gen + plen);
tlen -= plen;
}
p->type = ISAKMP_NPTYPE_NONE;
p->len = 0;
p->ptr = NULL;
plog(LLV_DEBUG, LOCATION, NULL, "succeed.\n");
return result;
}
vchar_t *
isakmp_parse(vchar_t *buf)
{
struct isakmp *isakmp = (struct isakmp *)buf->v;
struct isakmp_gen *gen;
size_t tlen;
vchar_t *result;
u_char np;
np = isakmp->np;
gen = (struct isakmp_gen *)(buf->v + sizeof(*isakmp));
tlen = buf->l - sizeof(struct isakmp);
result = isakmp_parsewoh(np, gen, tlen);
return result;
}
int
isakmp_init()
{
initph1tree();
initph2tree();
initctdtree();
init_recvdpkt();
return 0;
}
const char *
isakmp_pindex(const isakmp_index *index1, const uint32_t msgid)
{
static char buf[64];
const u_char *p;
int i, j;
memset(buf, 0, sizeof(buf));
p = (const u_char *)index1;
for (j = 0, i = 0; i < sizeof(isakmp_index); i++) {
snprintf((char *)&buf[j], sizeof(buf) - j, "%02x", p[i]);
j += 2;
switch (i) {
case 7:
buf[j++] = ':';
}
}
if (msgid == 0)
return buf;
snprintf((char *)&buf[j], sizeof(buf) - j, ":%08x", ntohs(msgid));
return buf;
}
int
isakmp_open(struct sockaddr *addr, int udp_encap)
{
const int yes = 1;
int fd;
struct sockaddr_in *sin = (struct sockaddr_in *) addr;
#ifdef INET6
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr;
int pktinfo;
#endif
#ifdef ENABLE_NATT
int option = -1;
#endif
switch (addr->sa_family) {
case AF_INET:
if (sin->sin_addr.s_addr == 0)
plog(LLV_WARNING, LOCATION, NULL,
"listening to wildcard address,"
"broadcast IKE packet may kill you\n");
break;
#ifdef INET6
case AF_INET6:
if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
plog(LLV_DEBUG, LOCATION, NULL,
"ignoring multicast address %s\n",
saddr2str(addr));
return -1;
}
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
plog(LLV_WARNING, LOCATION, NULL,
"listening to wildcard address, "
"broadcast IKE packet may kill you\n");
break;
#endif
default:
plog(LLV_ERROR, LOCATION, NULL,
"unsupported address family %d\n",
addr->sa_family);
return -1;
}
if ((fd = privsep_socket(addr->sa_family, SOCK_DGRAM, 0)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"socket(%s)\n", strerror(errno));
return -1;
}
close_on_exec(fd);
if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1)
plog(LLV_WARNING, LOCATION, NULL,
"failed to put socket in non-blocking mode\n");
switch (addr->sa_family) {
case AF_INET:
if (setsockopt(fd, IPPROTO_IP,
#ifdef __linux__
IP_PKTINFO,
#else
IP_RECVDSTADDR,
#endif
(const void *) &yes, sizeof(yes)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"setsockopt IP_RECVDSTADDR (%s)\n",
strerror(errno));
goto err;
}
#ifdef ENABLE_NATT
if (udp_encap)
option = UDP_ENCAP_ESPINUDP;
#if defined(ENABLE_NATT_00) || defined(ENABLE_NATT_01)
else
option = UDP_ENCAP_ESPINUDP_NON_IKE;
#endif
if (option == -1)
break;
if (setsockopt(fd, SOL_UDP,
UDP_ENCAP, &option,
sizeof(option)) < 0) {
plog(LLV_WARNING, LOCATION, NULL,
"setsockopt(%s): UDP_ENCAP %s\n",
option == UDP_ENCAP_ESPINUDP ? "UDP_ENCAP_ESPINUDP" : "UDP_ENCAP_ESPINUDP_NON_IKE",
strerror(errno));
} else {
plog(LLV_INFO, LOCATION, NULL,
"%s used for NAT-T\n",
saddr2str(addr));
}
#endif
break;
#ifdef INET6
case AF_INET6:
#if defined(INET6_ADVAPI)
#ifdef IPV6_RECVPKTINFO
pktinfo = IPV6_RECVPKTINFO;
#else
pktinfo = IPV6_PKTINFO;
#endif
#else
pktinfo = IPV6_RECVDSTADDR;
#endif
if (setsockopt(fd, IPPROTO_IPV6, pktinfo,
&yes, sizeof(yes)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"setsockopt IPV6_RECVDSTADDR (%d):%s\n",
pktinfo, strerror(errno));
goto err;
}
#ifdef IPV6_USE_MIN_MTU
if (setsockopt(fd, IPPROTO_IPV6, IPV6_USE_MIN_MTU,
&yes, sizeof(yes)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"setsockopt IPV6_USE_MIN_MTU (%s)\n",
strerror(errno));
goto err;
}
#endif
break;
#endif
}
if (setsockopt(fd, SOL_SOCKET,
#ifdef __linux__
SO_REUSEADDR,
#else
SO_REUSEPORT,
#endif
&yes, sizeof(yes)) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to set REUSE flag on %s (%s).\n",
saddr2str(addr), strerror(errno));
goto err;
}
if (setsockopt_bypass(fd, addr->sa_family) < 0)
goto err;
if (privsep_bind(fd, addr, sysdep_sa_len(addr)) < 0) {
plog(LLV_ERROR, LOCATION, addr,
"failed to bind to address %s (%s).\n",
saddr2str(addr), strerror(errno));
goto err;
}
plog(LLV_INFO, LOCATION, NULL,
"%s used as isakmp port (fd=%d)\n",
saddr2str(addr), fd);
monitor_fd(fd, isakmp_handler, NULL, 1);
return fd;
err:
close(fd);
return -1;
}
void
isakmp_close(int fd)
{
unmonitor_fd(fd);
close(fd);
}
int
isakmp_send(struct ph1handle *iph1, vchar_t *sbuf)
{
int len = 0;
int s;
vchar_t *vbuf = NULL, swap;
#ifdef ENABLE_NATT
size_t extralen = NON_ESP_MARKER_USE(iph1) ? NON_ESP_MARKER_LEN : 0;
if(extralen == NON_ESP_MARKER_LEN &&
*(uint32_t *)sbuf->v == 0)
extralen = 0;
#ifdef ENABLE_FRAG
if (iph1->frag && sbuf->l > ISAKMP_FRAG_MAXLEN)
extralen = 0;
#endif
if (extralen)
plog (LLV_DEBUG, LOCATION, NULL, "Adding NON-ESP marker\n");
if (extralen) {
if ((vbuf = vmalloc (sbuf->l + extralen)) == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"vbuf allocation failed\n");
return -1;
}
*(uint32_t *)vbuf->v = 0;
memcpy (vbuf->v + extralen, sbuf->v, sbuf->l);
swap = *sbuf;
*sbuf = *vbuf;
*vbuf = swap;
vfree(vbuf);
}
#endif
s = myaddr_getfd(iph1->local);
if (s == -1)
return -1;
plog (LLV_DEBUG, LOCATION, NULL, "%zu bytes %s\n", sbuf->l,
saddr2str_fromto("from %s to %s", iph1->local, iph1->remote));
#ifdef ENABLE_FRAG
if (iph1->frag && sbuf->l > ISAKMP_FRAG_MAXLEN) {
if (isakmp_sendfrags(iph1, sbuf) == -1) {
plog(LLV_ERROR, LOCATION, NULL,
"isakmp_sendfrags failed\n");
return -1;
}
} else
#endif
{
len = sendfromto(s, sbuf->v, sbuf->l,
iph1->local, iph1->remote, lcconf->count_persend);
if (len == -1) {
plog(LLV_ERROR, LOCATION, NULL, "sendfromto failed\n");
return -1;
}
}
return 0;
}
static void
isakmp_ph1resend_stub(struct sched *p)
{
struct ph1handle *iph1 = container_of(p, struct ph1handle, scr);
if (isakmp_ph1resend(iph1) < 0) {
remph1(iph1);
delph1(iph1);
}
}
static int
isakmp_ph1resend(struct ph1handle *iph1)
{
if (iph1->retry_counter <= 0) {
plog(LLV_ERROR, LOCATION, NULL,
"phase1 negotiation failed due to time up. %s\n",
isakmp_pindex(&iph1->index, iph1->msgid));
script_hook(iph1, SCRIPT_PHASE1_DEAD);
evt_phase1(iph1, EVT_PHASE1_NO_RESPONSE, NULL);
return -1;
}
if (isakmp_send(iph1, iph1->sendbuf) < 0){
plog(LLV_ERROR, LOCATION, NULL,
"phase1 negotiation failed due to send error. %s\n",
isakmp_pindex(&iph1->index, iph1->msgid));
evt_phase1(iph1, EVT_PHASE1_NO_RESPONSE, NULL);
return -1;
}
plog(LLV_DEBUG, LOCATION, NULL,
"resend phase1 packet %s\n",
isakmp_pindex(&iph1->index, iph1->msgid));
iph1->retry_counter--;
sched_schedule(&iph1->scr, lcconf->retry_interval,
isakmp_ph1resend_stub);
return 0;
}
int
isakmp_ph1send(struct ph1handle *iph1)
{
iph1->retry_counter = lcconf->retry_counter;
return isakmp_ph1resend(iph1);
}
static void
isakmp_ph2resend_stub(struct sched *p)
{
struct ph2handle *iph2 = container_of(p, struct ph2handle, scr);
if (isakmp_ph2resend(iph2) < 0) {
remph2(iph2);
delph2(iph2);
}
}
static int
isakmp_ph2resend(struct ph2handle *iph2)
{
if (iph2->ph1->status >= PHASE1ST_EXPIRED) {
plog(LLV_ERROR, LOCATION, NULL,
"phase2 negotiation failed due to phase1 expired. %s\n",
isakmp_pindex(&iph2->ph1->index, iph2->msgid));
return -1;
}
if (iph2->retry_counter <= 0) {
plog(LLV_ERROR, LOCATION, NULL,
"phase2 negotiation failed due to time up. %s\n",
isakmp_pindex(&iph2->ph1->index, iph2->msgid));
evt_phase2(iph2, EVT_PHASE2_NO_RESPONSE, NULL);
unbindph12(iph2);
return -1;
}
if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0){
plog(LLV_ERROR, LOCATION, NULL,
"phase2 negotiation failed due to send error. %s\n",
isakmp_pindex(&iph2->ph1->index, iph2->msgid));
evt_phase2(iph2, EVT_PHASE2_NO_RESPONSE, NULL);
return -1;
}
plog(LLV_DEBUG, LOCATION, NULL,
"resend phase2 packet %s\n",
isakmp_pindex(&iph2->ph1->index, iph2->msgid));
iph2->retry_counter--;
sched_schedule(&iph2->scr, lcconf->retry_interval,
isakmp_ph2resend_stub);
return 0;
}
int
isakmp_ph2send(struct ph2handle *iph2)
{
iph2->retry_counter = lcconf->retry_counter;
return isakmp_ph2resend(iph2);
}
void
isakmp_ph1dying_stub(struct sched *p)
{
isakmp_ph1dying(container_of(p, struct ph1handle, sce));
}
void
isakmp_ph1dying(struct ph1handle *iph1)
{
struct ph1handle *new_iph1;
struct ph2handle *p;
if (iph1->status >= PHASE1ST_DYING)
return;
iph1->status = PHASE1ST_DYING;
new_iph1 = getph1(iph1, iph1->local, iph1->remote, 1);
if (new_iph1 == NULL) {
LIST_FOREACH(p, &iph1->ph2tree, ph1bind) {
if (p->status != PHASE2ST_ESTABLISHED)
continue;
plog(LLV_INFO, LOCATION, NULL,
"renegotiating phase1 to %s due to "
"active phase2\n",
saddrwop2str(iph1->remote));
if (iph1->side == INITIATOR)
isakmp_ph1begin_i(iph1->rmconf, iph1->remote,
iph1->local);
break;
}
} else {
migrate_ph12(iph1, new_iph1);
}
sched_schedule(&iph1->sce, iph1->approval->lifetime *
(100 - PFKEY_SOFT_LIFETIME_RATE) / 100,
isakmp_ph1expire_stub);
}
void
isakmp_ph1expire_stub(struct sched *p)
{
isakmp_ph1expire(container_of(p, struct ph1handle, sce));
}
void
isakmp_ph1expire(struct ph1handle *iph1)
{
char *src, *dst;
if (iph1->status < PHASE1ST_EXPIRED) {
src = racoon_strdup(saddr2str(iph1->local));
dst = racoon_strdup(saddr2str(iph1->remote));
STRDUP_FATAL(src);
STRDUP_FATAL(dst);
plog(LLV_INFO, LOCATION, NULL,
"ISAKMP-SA expired %s-%s spi:%s\n",
src, dst,
isakmp_pindex(&iph1->index, 0));
racoon_free(src);
racoon_free(dst);
iph1->status = PHASE1ST_EXPIRED;
}
isakmp_ph1delete(iph1);
}
void
isakmp_ph1delete_stub(struct sched *p)
{
isakmp_ph1delete(container_of(p, struct ph1handle, sce));
}
void
isakmp_ph1delete(struct ph1handle *iph1)
{
struct ph2handle *p, *next;
struct ph1handle *new_iph1;
char *src, *dst;
new_iph1 = getph1(iph1, iph1->local, iph1->remote, 1);
if (new_iph1 != NULL)
migrate_ph12(iph1, new_iph1);
for (p = LIST_FIRST(&iph1->ph2tree); p; p = next) {
next = LIST_NEXT(p, ph1bind);
if (p->status == PHASE2ST_ESTABLISHED)
isakmp_info_send_d2(p);
delete_spd(p, 1);
remph2(p);
delph2(p);
}
src = racoon_strdup(saddr2str(iph1->local));
dst = racoon_strdup(saddr2str(iph1->remote));
STRDUP_FATAL(src);
STRDUP_FATAL(dst);
plog(LLV_INFO, LOCATION, NULL,
"ISAKMP-SA deleted %s-%s spi:%s\n",
src, dst, isakmp_pindex(&iph1->index, 0));
evt_phase1(iph1, EVT_PHASE1_DOWN, NULL);
if (new_iph1 == NULL && ph1_rekey_enabled(iph1))
script_hook(iph1, SCRIPT_PHASE1_DEAD);
racoon_free(src);
racoon_free(dst);
remph1(iph1);
delph1(iph1);
}
void
isakmp_ph2expire_stub(struct sched *p)
{
isakmp_ph2expire(container_of(p, struct ph2handle, sce));
}
void
isakmp_ph2expire(struct ph2handle *iph2)
{
char *src, *dst;
src = racoon_strdup(saddrwop2str(iph2->src));
dst = racoon_strdup(saddrwop2str(iph2->dst));
STRDUP_FATAL(src);
STRDUP_FATAL(dst);
plog(LLV_INFO, LOCATION, NULL,
"phase2 sa expired %s-%s\n", src, dst);
racoon_free(src);
racoon_free(dst);
iph2->status = PHASE2ST_EXPIRED;
sched_schedule(&iph2->sce, 1, isakmp_ph2delete_stub);
}
void
isakmp_ph2delete_stub(struct sched *p)
{
isakmp_ph2delete(container_of(p, struct ph2handle, sce));
}
void
isakmp_ph2delete(struct ph2handle *iph2)
{
char *src, *dst;
src = racoon_strdup(saddrwop2str(iph2->src));
dst = racoon_strdup(saddrwop2str(iph2->dst));
STRDUP_FATAL(src);
STRDUP_FATAL(dst);
plog(LLV_INFO, LOCATION, NULL,
"phase2 sa deleted %s-%s\n", src, dst);
racoon_free(src);
racoon_free(dst);
remph2(iph2);
delph2(iph2);
return;
}
int
isakmp_post_acquire(struct ph2handle *iph2, struct ph1handle *iph1hint,
int nopassive)
{
struct remoteconf *rmconf;
struct ph1handle *iph1 = NULL;
plog(LLV_DEBUG, LOCATION, NULL, "in post_acquire\n");
if (iph1hint == NULL || iph1hint->rmconf == NULL) {
rmconf = getrmconf(iph2->dst, nopassive ? GETRMCONF_F_NO_PASSIVE : 0);
if (rmconf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"no configuration found for %s.\n",
saddrwop2str(iph2->dst));
return -1;
}
} else {
rmconf = iph1hint->rmconf;
}
if (nopassive && rmconf->passive) {
plog(LLV_DEBUG, LOCATION, NULL,
"because of passive mode, "
"ignore the acquire message for %s.\n",
saddrwop2str(iph2->dst));
return -1;
}
iph1 = getph1(iph1hint, iph2->src, iph2->dst, 0);
if (iph1 == NULL) {
iph2->retry_checkph1 = lcconf->retry_checkph1;
sched_schedule(&iph2->sce, 1, isakmp_chkph1there_stub);
plog(LLV_INFO, LOCATION, NULL,
"IPsec-SA request for %s queued "
"due to no phase1 found.\n",
saddrwop2str(iph2->dst));
if (isakmp_ph1begin_i(rmconf, iph2->dst, iph2->src) == NULL) {
sched_cancel(&iph2->sce);
return -1;
}
return 0;
}
if (iph1->status < PHASE1ST_ESTABLISHED) {
iph2->retry_checkph1 = lcconf->retry_checkph1;
sched_schedule(&iph2->sce, 1, isakmp_chkph1there_stub);
plog(LLV_INFO, LOCATION, iph2->dst,
"request for establishing IPsec-SA was queued "
"due to no phase1 found.\n");
return 0;
}
plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n");
if (isakmp_ph2begin_i(iph1, iph2))
return -1;
return 0;
}
int
isakmp_get_sainfo(struct ph2handle *iph2, struct secpolicy *sp_out,
struct secpolicy *sp_in)
{
struct remoteconf *conf;
uint32_t remoteid = 0;
plog(LLV_DEBUG, LOCATION, NULL,
"new acquire %s\n", spidx2str(&sp_out->spidx));
{
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));
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);
return -1;
}
conf = getrmconf(iph2->dst, 0);
if (conf != NULL)
remoteid = conf->ph1id;
else
plog(LLV_DEBUG, LOCATION, NULL, "Warning: no valid rmconf !\n");
iph2->sainfo = getsainfo(idsrc, iddst, NULL, NULL, remoteid);
vfree(idsrc);
vfree(iddst);
if (iph2->sainfo == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get sainfo.\n");
return -1;
}
plog(LLV_DEBUG, LOCATION, NULL,
"selected sainfo: %s\n", sainfo2str(iph2->sainfo));
}
if (set_proposal_from_policy(iph2, sp_out, sp_in) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to create saprop.\n");
return -1;
}
return 0;
}
int
isakmp_post_getspi(struct ph2handle *iph2)
{
#ifdef ENABLE_STATS
struct timeval start, end;
#endif
if (iph2->ph1->status >= PHASE1ST_EXPIRED) {
plog(LLV_ERROR, LOCATION, iph2->ph1->remote,
"the negotiation is stopped, "
"because there is no suitable ISAKMP-SA.\n");
return -1;
}
#ifdef ENABLE_STATS
gettimeofday(&start, NULL);
#endif
if ((ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)]
[iph2->side]
[iph2->status])(iph2, NULL) != 0)
return -1;
#ifdef ENABLE_STATS
gettimeofday(&end, NULL);
syslog(LOG_NOTICE, "%s(%s): %8.6f",
"phase2",
s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status),
timedelta(&start, &end));
#endif
return 0;
}
void
isakmp_chkph1there_stub(struct sched *p)
{
isakmp_chkph1there(container_of(p, struct ph2handle, sce));
}
void
isakmp_chkph1there(struct ph2handle *iph2)
{
struct ph1handle *iph1;
iph2->retry_checkph1--;
if (iph2->retry_checkph1 < 0) {
plog(LLV_ERROR, LOCATION, iph2->dst,
"phase2 negotiation failed "
"due to time up waiting for phase1. %s\n",
sadbsecas2str(iph2->src, iph2->dst,
iph2->satype, 0, 0));
plog(LLV_INFO, LOCATION, NULL,
"delete phase 2 handler.\n");
pk_sendeacquire(iph2);
remph2(iph2);
delph2(iph2);
return;
}
iph1 = getph1byaddr(iph2->src, iph2->dst, 0);
if (iph1 != NULL
&& iph1->status == PHASE1ST_ESTABLISHED) {
plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: got a ph1 handler, setting ports.\n");
plog(LLV_DEBUG2, LOCATION, NULL, "iph1->local: %s\n", saddr2str(iph1->local));
plog(LLV_DEBUG2, LOCATION, NULL, "iph1->remote: %s\n", saddr2str(iph1->remote));
plog(LLV_DEBUG2, LOCATION, NULL, "before:\n");
plog(LLV_DEBUG2, LOCATION, NULL, "src: %s\n", saddr2str(iph2->src));
plog(LLV_DEBUG2, LOCATION, NULL, "dst: %s\n", saddr2str(iph2->dst));
set_port(iph2->src, extract_port(iph1->local));
set_port(iph2->dst, extract_port(iph1->remote));
plog(LLV_DEBUG2, LOCATION, NULL, "After:\n");
plog(LLV_DEBUG2, LOCATION, NULL, "src: %s\n", saddr2str(iph2->src));
plog(LLV_DEBUG2, LOCATION, NULL, "dst: %s\n", saddr2str(iph2->dst));
(void)isakmp_ph2begin_i(iph1, iph2);
return;
}
plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: no established ph1 handler found\n");
sched_schedule(&iph2->sce, 1, isakmp_chkph1there_stub);
return;
}
caddr_t
isakmp_set_attr_v(caddr_t buf, int type, caddr_t val, int len)
{
struct isakmp_data *data;
data = (struct isakmp_data *)buf;
data->type = htons((uint16_t)type | ISAKMP_GEN_TLV);
data->lorv = htons((uint16_t)len);
memcpy(data + 1, val, len);
return buf + sizeof(*data) + len;
}
caddr_t
isakmp_set_attr_l(caddr_t buf, int type, uint32_t val)
{
struct isakmp_data *data;
data = (struct isakmp_data *)buf;
data->type = htons((uint16_t)type | ISAKMP_GEN_TV);
data->lorv = htons((uint16_t)val);
return buf + sizeof(*data);
}
vchar_t *
isakmp_add_attr_v(vchar_t *buf0, int type, caddr_t val, int len)
{
vchar_t *buf = NULL;
struct isakmp_data *data;
size_t tlen;
size_t oldlen = 0;
tlen = sizeof(*data) + len;
if (buf0) {
oldlen = buf0->l;
buf = vrealloc(buf0, oldlen + tlen);
} else
buf = vmalloc(tlen);
if (!buf) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get a attribute buffer.\n");
return NULL;
}
data = (struct isakmp_data *)(buf->v + oldlen);
data->type = htons((uint16_t)type | ISAKMP_GEN_TLV);
data->lorv = htons((uint16_t)len);
memcpy(data + 1, val, len);
return buf;
}
vchar_t *
isakmp_add_attr_l(vchar_t *buf0, int type, uint32_t val)
{
vchar_t *buf = NULL;
struct isakmp_data *data;
size_t tlen;
size_t oldlen = 0;
tlen = sizeof(*data);
if (buf0) {
oldlen = buf0->l;
buf = vrealloc(buf0, oldlen + tlen);
} else
buf = vmalloc(tlen);
if (!buf) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get a attribute buffer.\n");
return NULL;
}
data = (struct isakmp_data *)(buf->v + oldlen);
data->type = htons((uint16_t)type | ISAKMP_GEN_TV);
data->lorv = htons((uint16_t)val);
return buf;
}
int
isakmp_newcookie(caddr_t place, struct sockaddr *remote, struct sockaddr *local)
{
vchar_t *buf = NULL, *buf2 = NULL;
char *p;
size_t blen;
size_t alen;
caddr_t sa1, sa2;
time_t t;
int error = -1;
u_short port;
if (remote->sa_family != local->sa_family) {
plog(LLV_ERROR, LOCATION, NULL,
"address family mismatch, remote:%d local:%d\n",
remote->sa_family, local->sa_family);
goto end;
}
switch (remote->sa_family) {
case AF_INET:
alen = sizeof(struct in_addr);
sa1 = (caddr_t)&((struct sockaddr_in *)remote)->sin_addr;
sa2 = (caddr_t)&((struct sockaddr_in *)local)->sin_addr;
break;
#ifdef INET6
case AF_INET6:
alen = sizeof(struct in6_addr);
sa1 = (caddr_t)&((struct sockaddr_in6 *)remote)->sin6_addr;
sa2 = (caddr_t)&((struct sockaddr_in6 *)local)->sin6_addr;
break;
#endif
default:
plog(LLV_ERROR, LOCATION, NULL,
"invalid family: %d\n", remote->sa_family);
goto end;
}
blen = (alen + sizeof(u_short)) * 2
+ sizeof(time_t) + lcconf->secret_size;
buf = vmalloc(blen);
if (buf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get a cookie.\n");
goto end;
}
p = buf->v;
memcpy(p, sa1, alen);
p += alen;
port = ((struct sockaddr_in *)remote)->sin_port;
memcpy(p, &port, sizeof(u_short));
p += sizeof(u_short);
memcpy(p, sa2, alen);
p += alen;
port = ((struct sockaddr_in *)local)->sin_port;
memcpy(p, &port, sizeof(u_short));
p += sizeof(u_short);
t = time(0);
memcpy(p, (caddr_t)&t, sizeof(t));
p += sizeof(t);
buf2 = eay_set_random(lcconf->secret_size);
if (buf2 == NULL)
goto end;
memcpy(p, buf2->v, lcconf->secret_size);
p += lcconf->secret_size;
vfree(buf2);
buf2 = eay_sha1_one(buf);
memcpy(place, buf2->v, sizeof(cookie_t));
sa1 = val2str(place, sizeof (cookie_t));
plog(LLV_DEBUG, LOCATION, NULL, "new cookie:\n%s\n", sa1);
racoon_free(sa1);
error = 0;
end:
if (buf != NULL)
vfree(buf);
if (buf2 != NULL)
vfree(buf2);
return error;
}
int
isakmp_p2ph( vchar_t **buf, struct isakmp_gen *gen)
{
if (*buf) {
plog(LLV_WARNING, LOCATION, NULL,
"ignore this payload, same payload type exist.\n");
return -1;
}
*buf = vmalloc(ntohs(gen->len) - sizeof(*gen));
if (*buf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get buffer.\n");
return -1;
}
memcpy((*buf)->v, gen + 1, (*buf)->l);
return 0;
}
uint32_t
isakmp_newmsgid2(struct ph1handle *iph1)
{
uint32_t msgid2;
do {
msgid2 = eay_random();
} while (getph2bymsgid(iph1, msgid2));
return msgid2;
}
static caddr_t
set_isakmp_header(vchar_t *vbuf, struct ph1handle *iph1, int nptype,
uint8_t etype, uint8_t flags, uint32_t msgid)
{
struct isakmp *isakmp;
if (vbuf->l < sizeof(*isakmp))
return NULL;
isakmp = (struct isakmp *)vbuf->v;
memcpy(&isakmp->i_ck, &iph1->index.i_ck, sizeof(cookie_t));
memcpy(&isakmp->r_ck, &iph1->index.r_ck, sizeof(cookie_t));
isakmp->np = nptype;
isakmp->v = iph1->version;
isakmp->etype = etype;
isakmp->flags = flags;
isakmp->msgid = msgid;
isakmp->len = htonl(vbuf->l);
return vbuf->v + sizeof(*isakmp);
}
caddr_t
set_isakmp_header1(vchar_t *vbuf, struct ph1handle *iph1, int nptype)
{
return set_isakmp_header (vbuf, iph1, nptype, iph1->etype, iph1->flags, iph1->msgid);
}
caddr_t
set_isakmp_header2(vchar_t *vbuf, struct ph2handle *iph2, int nptype)
{
return set_isakmp_header (vbuf, iph2->ph1, nptype, ISAKMP_ETYPE_QUICK, iph2->flags, iph2->msgid);
}
caddr_t
set_isakmp_payload(caddr_t buf, vchar_t *src, int nptype)
{
struct isakmp_gen *gen;
caddr_t p = buf;
plog(LLV_DEBUG, LOCATION, NULL, "add payload of len %zu, next type %d\n",
src->l, nptype);
gen = (struct isakmp_gen *)p;
gen->np = nptype;
gen->len = htons(sizeof(*gen) + src->l);
p += sizeof(*gen);
memcpy(p, src->v, src->l);
p += src->l;
return p;
}
static int
etypesw1(int etype)
{
switch (etype) {
case ISAKMP_ETYPE_IDENT:
return 1;
case ISAKMP_ETYPE_AGG:
return 2;
case ISAKMP_ETYPE_BASE:
return 3;
default:
return 0;
}
}
static int
etypesw2(int etype)
{
switch (etype) {
case ISAKMP_ETYPE_QUICK:
return 1;
default:
return 0;
}
}
#ifdef HAVE_PRINT_ISAKMP_C
char *snapend;
extern void isakmp_print(const u_char *, u_int, const u_char *);
char *getname(const u_char *);
#ifdef INET6
char *getname6(const u_char *);
#endif
int safeputchar(int);
char *
getname(ap)
const u_char *ap;
{
struct sockaddr_in addr;
static char ntop_buf[NI_MAXHOST];
memset(&addr, 0, sizeof(addr));
#ifndef __linux__
addr.sin_len = sizeof(struct sockaddr_in);
#endif
addr.sin_family = AF_INET;
memcpy(&addr.sin_addr, ap, sizeof(addr.sin_addr));
if (getnameinfo((struct sockaddr *)&addr, sizeof(addr),
ntop_buf, sizeof(ntop_buf), NULL, 0,
NI_NUMERICHOST | niflags))
strlcpy(ntop_buf, "?", sizeof(ntop_buf));
return ntop_buf;
}
#ifdef INET6
char *
getname6(ap)
const u_char *ap;
{
struct sockaddr_in6 addr;
static char ntop_buf[NI_MAXHOST];
memset(&addr, 0, sizeof(addr));
addr.sin6_len = sizeof(struct sockaddr_in6);
addr.sin6_family = AF_INET6;
memcpy(&addr.sin6_addr, ap, sizeof(addr.sin6_addr));
if (getnameinfo((struct sockaddr *)&addr, addr.sin6_len,
ntop_buf, sizeof(ntop_buf), NULL, 0,
NI_NUMERICHOST | niflags))
strlcpy(ntop_buf, "?", sizeof(ntop_buf));
return ntop_buf;
}
#endif
int
safeputchar(c)
int c;
{
unsigned char ch;
ch = (unsigned char)(c & 0xff);
if (c < 0x80 && isprint(c))
return printf("%c", c & 0xff);
else
return printf("\\%03o", c & 0xff);
}
void
isakmp_printpacket(msg, from, my, decoded)
vchar_t *msg;
struct sockaddr *from;
struct sockaddr *my;
int decoded;
{
#ifdef YIPS_DEBUG
struct timeval tv;
int s;
char hostbuf[NI_MAXHOST];
char portbuf[NI_MAXSERV];
struct isakmp *isakmp;
vchar_t *buf;
#endif
if (loglevel < LLV_DEBUG)
return;
#ifdef YIPS_DEBUG
plog(LLV_DEBUG, LOCATION, NULL, "begin.\n");
gettimeofday(&tv, NULL);
s = tv.tv_sec % 3600;
printf("%02d:%02d.%06u ", s / 60, s % 60, (uint32_t)tv.tv_usec);
if (from) {
if (getnameinfo(from, sysdep_sa_len(from), hostbuf, sizeof(hostbuf),
portbuf, sizeof(portbuf),
NI_NUMERICHOST | NI_NUMERICSERV | niflags)) {
strlcpy(hostbuf, "?", sizeof(hostbuf));
strlcpy(portbuf, "?", sizeof(portbuf));
}
printf("%s:%s", hostbuf, portbuf);
} else
printf("?");
printf(" -> ");
if (my) {
if (getnameinfo(my, sysdep_sa_len(my), hostbuf, sizeof(hostbuf),
portbuf, sizeof(portbuf),
NI_NUMERICHOST | NI_NUMERICSERV | niflags)) {
strlcpy(hostbuf, "?", sizeof(hostbuf));
strlcpy(portbuf, "?", sizeof(portbuf));
}
printf("%s:%s", hostbuf, portbuf);
} else
printf("?");
printf(": ");
buf = vdup(msg);
if (!buf) {
printf("(malloc fail)\n");
return;
}
if (decoded) {
isakmp = (struct isakmp *)buf->v;
if (isakmp->flags & ISAKMP_FLAG_E) {
#if 0
int pad;
pad = *(u_char *)(buf->v + buf->l - 1);
if (buf->l < pad && 2 < vflag)
printf("(wrong padding)");
#endif
isakmp->flags &= ~ISAKMP_FLAG_E;
}
}
snapend = buf->v + buf->l;
isakmp_print(buf->v, buf->l, NULL);
vfree(buf);
printf("\n");
fflush(stdout);
return;
#endif
}
#endif
int
copy_ph1addresses(struct ph1handle *iph1, struct remoteconf *rmconf,
struct sockaddr *remote, struct sockaddr *local)
{
uint16_t port = 0;
iph1->remote = dupsaddr(remote);
if (iph1->remote == NULL)
return -1;
if (extract_port(iph1->remote) == 0) {
port = 0;
if (rmconf != NULL)
port = extract_port(rmconf->remote);
if (port == 0)
port = lcconf->port_isakmp;
set_port(iph1->remote, port);
}
if (local == NULL)
iph1->local = getlocaladdr(iph1->remote);
else
iph1->local = dupsaddr(local);
if (iph1->local == NULL)
return -1;
if (extract_port(iph1->local) == 0) {
port = myaddr_getsport(iph1->local);
if (port == 0)
port = PORT_ISAKMP;
set_port(iph1->local, port);
}
#ifdef ENABLE_NATT
if (extract_port(iph1->local) == lcconf->port_isakmp_natt) {
plog(LLV_DEBUG, LOCATION, NULL, "Marking ports as changed\n");
iph1->natt_flags |= NAT_ADD_NON_ESP_MARKER;
}
#endif
return 0;
}
static int
nostate1(struct ph1handle *iph1, vchar_t *msg __unused)
{
plog(LLV_ERROR, LOCATION, iph1->remote, "wrong state %u.\n",
iph1->status);
return -1;
}
static int
nostate2(struct ph2handle *iph2, vchar_t *msg __unused)
{
plog(LLV_ERROR, LOCATION, iph2->ph1->remote, "wrong state %u.\n",
iph2->status);
return -1;
}
void
log_ph1established(const struct ph1handle *iph1)
{
char *src, *dst;
src = racoon_strdup(saddr2str(iph1->local));
dst = racoon_strdup(saddr2str(iph1->remote));
STRDUP_FATAL(src);
STRDUP_FATAL(dst);
plog(LLV_INFO, LOCATION, NULL,
"ISAKMP-SA established %s-%s spi:%s\n",
src, dst,
isakmp_pindex(&iph1->index, 0));
evt_phase1(iph1, EVT_PHASE1_UP, NULL);
if(!iph1->rmconf->mode_cfg)
evt_phase1(iph1, EVT_PHASE1_MODE_CFG, NULL);
racoon_free(src);
racoon_free(dst);
return;
}
struct payload_list *
isakmp_plist_append_full (struct payload_list *plist, vchar_t *payload,
uint8_t payload_type, uint8_t free_payload)
{
if (! plist) {
plist = racoon_malloc (sizeof (struct payload_list));
plist->prev = NULL;
}
else {
plist->next = racoon_malloc (sizeof (struct payload_list));
plist->next->prev = plist;
plist = plist->next;
}
plist->next = NULL;
plist->payload = payload;
plist->payload_type = payload_type;
plist->free_payload = free_payload;
return plist;
}
vchar_t *
isakmp_plist_set_all (struct payload_list **plist, struct ph1handle *iph1)
{
struct payload_list *ptr = *plist, *first;
size_t tlen = sizeof (struct isakmp), n = 0;
vchar_t *buf = NULL;
char *p;
while (ptr->prev) ptr = ptr->prev;
first = ptr;
while (ptr) {
tlen += ptr->payload->l + sizeof (struct isakmp_gen);
ptr = ptr->next;
}
buf = vmalloc(tlen);
if (buf == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"failed to get buffer to send.\n");
goto end;
}
ptr = first;
p = set_isakmp_header1(buf, iph1, ptr->payload_type);
if (p == NULL)
goto end;
while (ptr)
{
p = set_isakmp_payload (p, ptr->payload, ptr->next ? ptr->next->payload_type : ISAKMP_NPTYPE_NONE);
first = ptr;
ptr = ptr->next;
if (first->free_payload)
vfree(first->payload);
racoon_free (first);
n++;
}
*plist = NULL;
return buf;
end:
if (buf != NULL)
vfree(buf);
return NULL;
}
#ifdef ENABLE_FRAG
int
frag_handler(struct ph1handle *iph1, vchar_t *msg, struct sockaddr *remote,
struct sockaddr *local)
{
vchar_t *newmsg;
if (isakmp_frag_extract(iph1, msg) == 1) {
if ((newmsg = isakmp_frag_reassembly(iph1)) == NULL) {
plog(LLV_ERROR, LOCATION, remote,
"Packet reassembly failed\n");
return -1;
}
return isakmp_main(newmsg, remote, local);
}
return 0;
}
#endif
void
script_hook(struct ph1handle *iph1, int script)
{
#define IP_MAX 40
#define PORT_MAX 6
char addrstr[IP_MAX];
char portstr[PORT_MAX];
char **envp = NULL;
int envc = 1;
char **c;
if (iph1 == NULL ||
iph1->rmconf == NULL ||
iph1->rmconf->script[script] == NULL)
return;
#ifdef ENABLE_HYBRID
(void)isakmp_cfg_setenv(iph1, &envp, &envc);
#endif
GETNAMEINFO(iph1->local, addrstr, portstr);
if (script_env_append(&envp, &envc, "LOCAL_ADDR", addrstr) != 0) {
plog(LLV_ERROR, LOCATION, NULL, "Cannot set LOCAL_ADDR\n");
goto out;
}
if (script_env_append(&envp, &envc, "LOCAL_PORT", portstr) != 0) {
plog(LLV_ERROR, LOCATION, NULL, "Cannot set LOCAL_PORT\n");
goto out;
}
if (iph1->remote != NULL) {
GETNAMEINFO(iph1->remote, addrstr, portstr);
if (script_env_append(&envp, &envc,
"REMOTE_ADDR", addrstr) != 0) {
plog(LLV_ERROR, LOCATION, NULL,
"Cannot set REMOTE_ADDR\n");
goto out;
}
if (script_env_append(&envp, &envc,
"REMOTE_PORT", portstr) != 0) {
plog(LLV_ERROR, LOCATION, NULL,
"Cannot set REMOTEL_PORT\n");
goto out;
}
}
if (iph1->id_p != NULL) {
if (script_env_append(&envp, &envc, "REMOTE_ID",
ipsecdoi_id2str(iph1->id_p)) != 0) {
plog(LLV_ERROR, LOCATION, NULL,
"Cannot set REMOTE_ID\n");
goto out;
}
}
if (privsep_script_exec(iph1->rmconf->script[script]->v,
script, envp) != 0)
plog(LLV_ERROR, LOCATION, NULL,
"Script %s execution failed\n", script_names[script]);
out:
for (c = envp; *c; c++)
racoon_free(*c);
racoon_free(envp);
return;
}
int
script_env_append(char ***envp, int *envc, const char *name, char *value)
{
char *envitem;
char **newenvp;
int newenvc;
envitem = racoon_malloc(strlen(name) + 1 + strlen(value) + 1);
if (envitem == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"Cannot allocate memory: %s\n", strerror(errno));
return -1;
}
sprintf(envitem, "%s=%s", name, value);
newenvc = (*envc) + 1;
newenvp = racoon_realloc(*envp, newenvc * sizeof(char *));
if (newenvp == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"Cannot allocate memory: %s\n", strerror(errno));
racoon_free(envitem);
return -1;
}
newenvp[newenvc - 2] = envitem;
newenvp[newenvc - 1] = NULL;
*envp = newenvp;
*envc = newenvc;
return 0;
}
int
script_exec(char *script, int name, char *const envp[])
{
char *argv[] = { NULL, NULL, NULL };
argv[0] = script;
argv[1] = __UNCONST(script_names[name]);
argv[2] = NULL;
switch (fork()) {
case 0:
execve(argv[0], argv, envp);
plog(LLV_ERROR, LOCATION, NULL,
"execve(\"%s\") failed: %s\n",
argv[0], strerror(errno));
_exit(1);
case -1:
plog(LLV_ERROR, LOCATION, NULL,
"Cannot fork: %s\n", strerror(errno));
return -1;
default:
break;
}
return 0;
}
void
purge_remote(struct ph1handle *iph1)
{
vchar_t *buf = NULL;
struct sadb_msg *msg, *next, *end;
struct sadb_sa *sa;
struct sockaddr *src, *dst;
caddr_t mhp[SADB_EXT_MAX + 1];
u_int proto_id;
struct ph2handle *iph2;
struct ph1handle *new_iph1;
plog(LLV_INFO, LOCATION, NULL,
"purging ISAKMP-SA spi=%s.\n",
isakmp_pindex(&(iph1->index), iph1->msgid));
iph1->status = PHASE1ST_EXPIRED;
new_iph1 = getph1(iph1, iph1->local, iph1->remote, GETPH1_F_ESTABLISHED);
buf = pfkey_dump_sadb(SADB_SATYPE_UNSPEC);
if (buf == NULL) {
plog(LLV_DEBUG, LOCATION, NULL,
"pfkey_dump_sadb returned nothing.\n");
return;
}
msg = (struct sadb_msg *)buf->v;
end = (struct sadb_msg *)(buf->v + buf->l);
while (msg < end) {
if ((msg->sadb_msg_len << 3) < sizeof(*msg))
break;
next = (struct sadb_msg *)((caddr_t)msg + (msg->sadb_msg_len << 3));
if (msg->sadb_msg_type != SADB_DUMP) {
msg = next;
continue;
}
if (pfkey_align(msg, mhp) || pfkey_check(mhp)) {
plog(LLV_ERROR, LOCATION, NULL,
"pfkey_check (%s)\n", ipsec_strerror());
msg = next;
continue;
}
sa = (struct sadb_sa *)(mhp[SADB_EXT_SA]);
if (!sa ||
!mhp[SADB_EXT_ADDRESS_SRC] ||
!mhp[SADB_EXT_ADDRESS_DST]) {
msg = next;
continue;
}
pk_fixup_sa_addresses(mhp);
src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]);
dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]);
if (sa->sadb_sa_state != SADB_SASTATE_LARVAL &&
sa->sadb_sa_state != SADB_SASTATE_MATURE &&
sa->sadb_sa_state != SADB_SASTATE_DYING) {
msg = next;
continue;
}
if ((cmpsaddr(iph1->local, src) != CMPSADDR_MATCH ||
cmpsaddr(iph1->remote, dst) != CMPSADDR_MATCH) &&
(cmpsaddr(iph1->local, dst) != CMPSADDR_MATCH ||
cmpsaddr(iph1->remote, src) != CMPSADDR_MATCH)) {
msg = next;
continue;
}
proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype);
iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi);
if (new_iph1 != NULL) {
if (iph2 == NULL) {
plog(LLV_INFO, LOCATION, NULL,
"Unknown IPsec-SA spi=%u, hmmmm?\n",
ntohl(sa->sadb_sa_spi));
}else{
if (iph2->ph1 != NULL && iph2->ph1 != iph1){
msg = next;
continue;
}
if (iph2->status == PHASE2ST_ESTABLISHED ||
iph2->status == PHASE2ST_EXPIRED) {
plog(LLV_INFO, LOCATION, NULL,
"keeping IPsec-SA spi=%u - found valid ISAKMP-SA spi=%s.\n",
ntohl(sa->sadb_sa_spi),
isakmp_pindex(&(new_iph1->index), new_iph1->msgid));
msg = next;
continue;
}
}
}
pfkey_send_delete(lcconf->sock_pfkey,
msg->sadb_msg_satype,
IPSEC_MODE_ANY,
src, dst, sa->sadb_sa_spi);
if (iph2 != NULL) {
delete_spd(iph2, 0);
remph2(iph2);
delph2(iph2);
}
plog(LLV_INFO, LOCATION, NULL,
"purged IPsec-SA spi=%u.\n",
ntohl(sa->sadb_sa_spi));
msg = next;
}
if (buf)
vfree(buf);
plog(LLV_INFO, LOCATION, NULL,
"purged ISAKMP-SA spi=%s.\n",
isakmp_pindex(&(iph1->index), iph1->msgid));
isakmp_ph1delete(iph1);
}
void
delete_spd(struct ph2handle *iph2, u_int64_t created)
{
struct policyindex spidx;
struct sockaddr_storage addr;
uint8_t pref;
struct sockaddr *src;
struct sockaddr *dst;
int error;
int idi2type = 0;
if (iph2 == NULL)
return;
if (! iph2->generated_spidx )
return;
src = iph2->src;
dst = iph2->dst;
plog(LLV_INFO, LOCATION, NULL,
"deleting a generated policy.\n");
memset(&spidx, 0, sizeof(spidx));
iph2->spidx_gen = (caddr_t )&spidx;
iph2->src = dst;
iph2->dst = src;
spidx.dir = IPSEC_DIR_INBOUND;
spidx.ul_proto = 0;
#define _XIDT(d) ((struct ipsecdoi_id_b *)(d)->v)->type
if (iph2->id != NULL
&& (_XIDT(iph2->id) == IPSECDOI_ID_IPV4_ADDR
|| _XIDT(iph2->id) == IPSECDOI_ID_IPV6_ADDR
|| _XIDT(iph2->id) == IPSECDOI_ID_IPV4_ADDR_SUBNET
|| _XIDT(iph2->id) == IPSECDOI_ID_IPV6_ADDR_SUBNET)) {
error = ipsecdoi_id2sockaddr(iph2->id,
(struct sockaddr *)&spidx.dst,
&spidx.prefd, &spidx.ul_proto);
if (error)
goto purge;
#ifdef INET6
if (_XIDT(iph2->id) == IPSECDOI_ID_IPV6_ADDR) {
if ((error =
setscopeid((struct sockaddr *)&spidx.dst,
iph2->src)) != 0)
goto purge;
}
#endif
if (_XIDT(iph2->id) == IPSECDOI_ID_IPV4_ADDR
|| _XIDT(iph2->id) == IPSECDOI_ID_IPV6_ADDR)
idi2type = _XIDT(iph2->id);
} else {
plog(LLV_DEBUG, LOCATION, NULL,
"get a destination address of SP index "
"from phase1 address "
"due to no ID payloads found "
"OR because ID type is not address.\n");
memcpy(&spidx.dst, iph2->src, sysdep_sa_len(iph2->src));
switch (spidx.dst.ss_family) {
case AF_INET:
spidx.prefd =
sizeof(struct in_addr) << 3;
break;
#ifdef INET6
case AF_INET6:
spidx.prefd =
sizeof(struct in6_addr) << 3;
break;
#endif
default:
spidx.prefd = 0;
break;
}
}
if (iph2->id_p != NULL
&& (_XIDT(iph2->id_p) == IPSECDOI_ID_IPV4_ADDR
|| _XIDT(iph2->id_p) == IPSECDOI_ID_IPV6_ADDR
|| _XIDT(iph2->id_p) == IPSECDOI_ID_IPV4_ADDR_SUBNET
|| _XIDT(iph2->id_p) == IPSECDOI_ID_IPV6_ADDR_SUBNET)) {
error = ipsecdoi_id2sockaddr(iph2->id_p,
(struct sockaddr *)&spidx.src,
&spidx.prefs, &spidx.ul_proto);
if (error)
goto purge;
#ifdef INET6
if (_XIDT(iph2->id_p) == IPSECDOI_ID_IPV6_ADDR) {
error =
setscopeid((struct sockaddr *)&spidx.src,
iph2->dst);
if (error)
goto purge;
}
#endif
if (_XIDT(iph2->id_p) == idi2type
&& spidx.dst.ss_family == spidx.src.ss_family) {
iph2->sa_src =
dupsaddr((struct sockaddr *)&spidx.dst);
if (iph2->sa_src == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"allocation failed\n");
goto purge;
}
iph2->sa_dst =
dupsaddr((struct sockaddr *)&spidx.src);
if (iph2->sa_dst == NULL) {
plog(LLV_ERROR, LOCATION, NULL,
"allocation failed\n");
goto purge;
}
}
} else {
plog(LLV_DEBUG, LOCATION, NULL,
"get a source address of SP index "
"from phase1 address "
"due to no ID payloads found "
"OR because ID type is not address.\n");
memcpy(&spidx.src, iph2->dst, sysdep_sa_len(iph2->dst));
switch (spidx.src.ss_family) {
case AF_INET:
spidx.prefs =
sizeof(struct in_addr) << 3;
break;
#ifdef INET6
case AF_INET6:
spidx.prefs =
sizeof(struct in6_addr) << 3;
break;
#endif
default:
spidx.prefs = 0;
break;
}
}
#undef _XIDT
plog(LLV_DEBUG, LOCATION, NULL,
"get a src address from ID payload "
"%s prefixlen=%u ul_proto=%u\n",
saddr2str((struct sockaddr *)&spidx.src),
spidx.prefs, spidx.ul_proto);
plog(LLV_DEBUG, LOCATION, NULL,
"get dst address from ID payload "
"%s prefixlen=%u ul_proto=%u\n",
saddr2str((struct sockaddr *)&spidx.dst),
spidx.prefd, spidx.ul_proto);
if (spidx.ul_proto == 0)
spidx.ul_proto = IPSEC_ULPROTO_ANY;
#undef _XIDT
if( created ){
struct secpolicy *p;
p = getsp(&spidx);
if(p != NULL){
if(p->spidx.created != created)
goto purge;
}
}
if (pk_sendspddelete(iph2) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"pfkey spddelete(inbound) failed.\n");
}else{
plog(LLV_DEBUG, LOCATION, NULL,
"pfkey spddelete(inbound) sent.\n");
}
#ifdef HAVE_POLICY_FWD
if (tunnel_mode_prop(iph2->approval)) {
spidx.dir = IPSEC_DIR_FWD;
if (pk_sendspddelete(iph2) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"pfkey spddelete(forward) failed.\n");
}else{
plog(LLV_DEBUG, LOCATION, NULL,
"pfkey spddelete(forward) sent.\n");
}
}
#endif
iph2->src = src;
iph2->dst = dst;
spidx.dir = IPSEC_DIR_OUTBOUND;
addr = spidx.src;
spidx.src = spidx.dst;
spidx.dst = addr;
pref = spidx.prefs;
spidx.prefs = spidx.prefd;
spidx.prefd = pref;
if (pk_sendspddelete(iph2) < 0) {
plog(LLV_ERROR, LOCATION, NULL,
"pfkey spddelete(outbound) failed.\n");
}else{
plog(LLV_DEBUG, LOCATION, NULL,
"pfkey spddelete(outbound) sent.\n");
}
purge:
iph2->spidx_gen=NULL;
}
#ifdef INET6
uint32_t
setscopeid(struct sockaddr *sp_addr0, struct sockaddr *sa_addr0)
{
struct sockaddr_in6 *sp_addr, *sa_addr;
sp_addr = (struct sockaddr_in6 *)sp_addr0;
sa_addr = (struct sockaddr_in6 *)sa_addr0;
if (!IN6_IS_ADDR_LINKLOCAL(&sp_addr->sin6_addr)
&& !IN6_IS_ADDR_SITELOCAL(&sp_addr->sin6_addr)
&& !IN6_IS_ADDR_MULTICAST(&sp_addr->sin6_addr))
return 0;
if (sa_addr->sin6_family != AF_INET6) {
plog(LLV_ERROR, LOCATION, NULL,
"can't get scope ID: family mismatch\n");
return (uint32_t)-1;
}
if (!IN6_IS_ADDR_LINKLOCAL(&sa_addr->sin6_addr)) {
plog(LLV_ERROR, LOCATION, NULL,
"scope ID is not supported except of lladdr.\n");
return (uint32_t)-1;
}
sp_addr->sin6_scope_id = sa_addr->sin6_scope_id;
return 0;
}
#endif