| /* $NetBSD: isakmp.c,v 1.20.6.13 2008/09/25 09:34:39 vanhu Exp $ */ |
| |
| /* Id: isakmp.c,v 1.74 2006/05/07 21:32:59 manubsd 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 <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 "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 "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 <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 |
| #ifdef ANDROID_CHANGES |
| #define __linux |
| #endif |
| # endif /* __linux__ */ |
| # 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 /* __NetBSD__ / __FreeBSD__ */ |
| |
| static int nostate1 __P((struct ph1handle *, vchar_t *)); |
| static int nostate2 __P((struct ph2handle *, vchar_t *)); |
| |
| extern caddr_t val2str(const char *, size_t); |
| |
| static int (*ph1exchange[][2][PHASE1ST_MAX]) |
| __P((struct ph1handle *, vchar_t *)) = { |
| /* error */ |
| { {}, {}, }, |
| /* Identity Protection exchange */ |
| { |
| { nostate1, ident_i1send, nostate1, ident_i2recv, ident_i2send, |
| ident_i3recv, ident_i3send, ident_i4recv, ident_i4send, nostate1, }, |
| { nostate1, ident_r1recv, ident_r1send, ident_r2recv, ident_r2send, |
| ident_r3recv, ident_r3send, nostate1, nostate1, nostate1, }, |
| }, |
| /* Aggressive exchange */ |
| { |
| { nostate1, agg_i1send, nostate1, agg_i2recv, agg_i2send, |
| nostate1, nostate1, nostate1, nostate1, nostate1, }, |
| { nostate1, agg_r1recv, agg_r1send, agg_r2recv, agg_r2send, |
| nostate1, nostate1, nostate1, nostate1, nostate1, }, |
| }, |
| /* Base exchange */ |
| { |
| { nostate1, base_i1send, nostate1, base_i2recv, base_i2send, |
| base_i3recv, base_i3send, nostate1, nostate1, nostate1, }, |
| { nostate1, base_r1recv, base_r1send, base_r2recv, base_r2send, |
| nostate1, nostate1, nostate1, nostate1, nostate1, }, |
| }, |
| }; |
| |
| static int (*ph2exchange[][2][PHASE2ST_MAX]) |
| __P((struct ph2handle *, vchar_t *)) = { |
| /* error */ |
| { {}, {}, }, |
| /* Quick mode for IKE */ |
| { |
| { 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 }; /* used to verify the r_ck. */ |
| |
| static int isakmp_main __P((vchar_t *, struct sockaddr *, struct sockaddr *)); |
| static int ph1_main __P((struct ph1handle *, vchar_t *)); |
| static int quick_main __P((struct ph2handle *, vchar_t *)); |
| static int isakmp_ph1begin_r __P((vchar_t *, |
| struct sockaddr *, struct sockaddr *, u_int8_t)); |
| static int isakmp_ph2begin_i __P((struct ph1handle *, struct ph2handle *)); |
| static int isakmp_ph2begin_r __P((struct ph1handle *, vchar_t *)); |
| static int etypesw1 __P((int)); |
| static int etypesw2 __P((int)); |
| #ifdef ENABLE_FRAG |
| static int frag_handler(struct ph1handle *, |
| vchar_t *, struct sockaddr *, struct sockaddr *); |
| #endif |
| |
| /* |
| * isakmp packet handler |
| */ |
| int |
| isakmp_handler(so_isakmp) |
| int so_isakmp; |
| { |
| struct isakmp isakmp; |
| union { |
| char buf[sizeof (isakmp) + 4]; |
| u_int32_t non_esp[2]; |
| char lbuf[sizeof(struct udphdr) + |
| #ifdef __linux |
| sizeof(struct iphdr) + |
| #else |
| sizeof(struct ip) + |
| #endif |
| sizeof(isakmp) + 4]; |
| } x; |
| struct sockaddr_storage remote; |
| struct sockaddr_storage local; |
| unsigned int remote_len = sizeof(remote); |
| unsigned int local_len = sizeof(local); |
| int len = 0, extralen = 0; |
| vchar_t *buf = NULL, *tmpbuf = NULL; |
| int error = -1, res; |
| |
| /* read message by MSG_PEEK */ |
| 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; |
| } |
| |
| /* keep-alive packet - ignore */ |
| if (len == 1 && (x.buf[0]&0xff) == 0xff) { |
| /* Pull the keep-alive packet */ |
| 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; |
| } |
| |
| /* Lucent IKE in UDP encapsulation */ |
| { |
| struct udphdr *udp; |
| #ifdef __linux__ |
| struct iphdr *ip; |
| |
| udp = (struct udphdr *)&x.lbuf[0]; |
| if (ntohs(udp->dest) == 501) { |
| ip = (struct iphdr *)(x.lbuf + sizeof(*udp)); |
| extralen += sizeof(*udp) + ip->ihl; |
| } |
| #else |
| struct ip *ip; |
| |
| udp = (struct udphdr *)&x.lbuf[0]; |
| if (ntohs(udp->uh_dport) == 501) { |
| ip = (struct ip *)(x.lbuf + sizeof(*udp)); |
| extralen += sizeof(*udp) + ip->ip_hl; |
| } |
| #endif |
| } |
| |
| #ifdef ENABLE_NATT |
| /* we don't know about portchange yet, |
| look for non-esp marker instead */ |
| if (x.non_esp[0] == 0 && x.non_esp[1] != 0) |
| extralen = NON_ESP_MARKER_LEN; |
| #endif |
| |
| /* now we know if there is an extra non-esp |
| marker at the beginning or not */ |
| memcpy ((char *)&isakmp, x.buf + extralen, sizeof (isakmp)); |
| |
| /* check isakmp header length, as well as sanity of header length */ |
| if (len < sizeof(isakmp) || ntohl(isakmp.len) < sizeof(isakmp)) { |
| plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, |
| "packet shorter than isakmp header size (%u, %u, %zu)\n", |
| len, ntohl(isakmp.len), sizeof(isakmp)); |
| /* dummy receive */ |
| 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; |
| } |
| |
| /* reject it if the size is tooooo big. */ |
| 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; |
| } |
| |
| /* read real message */ |
| 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); |
| /* dummy receive */ |
| 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 (%u 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 (%d != %zu), why ?\n", |
| len, buf->l); |
| goto end; |
| } |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "===\n"); |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "%d 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); |
| |
| /* avoid packets with malicious port/address */ |
| 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; |
| } |
| |
| /* XXX: check sender whether to be allowed or not to accept */ |
| |
| /* XXX: I don't know how to check isakmp half connection attack. */ |
| |
| /* simply reply if the packet was processed. */ |
| 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; |
| } |
| |
| /* isakmp main routine */ |
| 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); |
| } |
| |
| /* |
| * main processing to handle isakmp payload |
| */ |
| static int |
| isakmp_main(msg, remote, local) |
| vchar_t *msg; |
| struct sockaddr *remote, *local; |
| { |
| struct isakmp *isakmp = (struct isakmp *)msg->v; |
| isakmp_index *index = (isakmp_index *)isakmp; |
| u_int32_t msgid = isakmp->msgid; |
| struct ph1handle *iph1; |
| |
| #ifdef HAVE_PRINT_ISAKMP_C |
| isakmp_printpacket(msg, remote, local, 0); |
| #endif |
| |
| /* the initiator's cookie must not be zero */ |
| if (memcmp(&isakmp->i_ck, r_ck0, sizeof(cookie_t)) == 0) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "malformed cookie received.\n"); |
| return -1; |
| } |
| |
| /* Check the Major and Minor Version fields. */ |
| /* |
| * XXX Is is right to check version here ? |
| * I think it may no be here because the version depends |
| * on exchange status. |
| */ |
| 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 |
| } |
| |
| /* check the Flags field. */ |
| /* XXX How is the exclusive check, E and A ? */ |
| 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; |
| } |
| |
| /* ignore commit bit. */ |
| 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(index); |
| if (iph1 != NULL) { |
| /* validity check */ |
| 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 |
| /* Floating ports for NAT-T */ |
| if (NATT_AVAILABLE(iph1) && |
| ! (iph1->natt_flags & NAT_PORTS_CHANGED) && |
| ((cmpsaddrstrict(iph1->remote, remote) != 0) || |
| (cmpsaddrstrict(iph1->local, local) != 0))) |
| { |
| /* prevent memory leak */ |
| racoon_free(iph1->remote); |
| racoon_free(iph1->local); |
| iph1->remote = NULL; |
| iph1->local = NULL; |
| |
| /* copy-in new addresses */ |
| 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; |
| } |
| |
| /* set the flag to prevent further port floating |
| (FIXME: should we allow it? E.g. when the NAT gw |
| is rebooted?) */ |
| iph1->natt_flags |= NAT_PORTS_CHANGED | NAT_ADD_NON_ESP_MARKER; |
| |
| /* print some neat info */ |
| 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 |
| |
| /* must be same addresses in one stream of a phase at least. */ |
| if (cmpsaddrstrict(iph1->remote, remote) != 0) { |
| 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); |
| } |
| |
| /* |
| * don't check of exchange type here because other type will be |
| * with same index, for example, informational exchange. |
| */ |
| |
| /* XXX more acceptable check */ |
| } |
| |
| switch (isakmp->etype) { |
| case ISAKMP_ETYPE_IDENT: |
| case ISAKMP_ETYPE_AGG: |
| case ISAKMP_ETYPE_BASE: |
| /* phase 1 validity check */ |
| if (isakmp->msgid != 0) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "message id should be zero in phase1.\n"); |
| return -1; |
| } |
| |
| /* search for isakmp status record of phase 1 */ |
| if (iph1 == NULL) { |
| /* |
| * the packet must be the 1st message from a initiator |
| * or the 2nd message from the responder. |
| */ |
| |
| /* search for phase1 handle by index without r_ck */ |
| iph1 = getph1byindex0(index); |
| if (iph1 == NULL) { |
| /*it must be the 1st message from a initiator.*/ |
| 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; |
| } |
| |
| /* it must be responder's 1st exchange. */ |
| if (isakmp_ph1begin_r(msg, remote, local, |
| isakmp->etype) < 0) |
| return -1; |
| break; |
| |
| /*NOTREACHED*/ |
| } |
| |
| /* it must be the 2nd message from the responder. */ |
| 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; |
| } |
| } |
| |
| /* |
| * Don't delete phase 1 handler when the exchange type |
| * in handler is not equal to packet's one because of no |
| * authencication completed. |
| */ |
| 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 |
| |
| /* call main process of phase 1 */ |
| 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: |
| /* |
| * iph1 must be present for Information message. |
| * if iph1 is null then trying to get the phase1 status |
| * as the packet from responder againt initiator's 1st |
| * exchange in phase 1. |
| * NOTE: We think such informational exchange should be ignored. |
| */ |
| if (iph1 == NULL) { |
| iph1 = getph1byindex0(index); |
| if (iph1 == NULL) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "unknown Informational " |
| "exchange received.\n"); |
| return -1; |
| } |
| if (cmpsaddrstrict(iph1->remote, remote) != 0) { |
| 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 |
| /* Reinit the IVM if it's still there */ |
| 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 |
| |
| /* check status of phase 1 whether negotiated or not. */ |
| if (iph1->status != PHASE1ST_ESTABLISHED) { |
| 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; |
| } |
| |
| /* search isakmp phase 2 stauts record. */ |
| iph2 = getph2bymsgid(iph1, msgid); |
| if (iph2 == NULL) { |
| /* it must be new negotiation as responder */ |
| if (isakmp_ph2begin_r(iph1, msg) < 0) |
| return -1; |
| return 0; |
| /*NOTREACHED*/ |
| } |
| |
| /* commit bit. */ |
| /* XXX |
| * we keep to set commit bit during negotiation. |
| * When SA is configured, bit will be reset. |
| * XXX |
| * don't initiate commit bit. should be fixed in the future. |
| */ |
| if (ISSET(isakmp->flags, ISAKMP_FLAG_C)) |
| iph2->flags |= ISAKMP_FLAG_C; |
| |
| /* call main process of quick mode */ |
| if (quick_main(iph2, msg) < 0) { |
| plog(LLV_ERROR, LOCATION, iph1->remote, |
| "phase2 negotiation failed.\n"); |
| unbindph12(iph2); |
| 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; |
| } |
| |
| /* |
| * main function of phase 1. |
| */ |
| static int |
| ph1_main(iph1, msg) |
| struct ph1handle *iph1; |
| vchar_t *msg; |
| { |
| int error; |
| #ifdef ENABLE_STATS |
| struct timeval start, end; |
| #endif |
| |
| /* ignore a packet */ |
| if (iph1->status == PHASE1ST_ESTABLISHED) |
| return 0; |
| |
| #ifdef ENABLE_STATS |
| gettimeofday(&start, NULL); |
| #endif |
| /* receive */ |
| 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) { |
| |
| /* XXX |
| * When an invalid packet is received on phase1, it should |
| * be selected to process this packet. That is to respond |
| * with a notify and delete phase 1 handler, OR not to respond |
| * and keep phase 1 handler. However, in PHASE1ST_START when |
| * acting as RESPONDER we must not keep phase 1 handler or else |
| * it will stay forever. |
| */ |
| |
| if (iph1->side == RESPONDER && iph1->status == PHASE1ST_START) { |
| plog(LLV_ERROR, LOCATION, iph1->remote, |
| "failed to pre-process packet.\n"); |
| return -1; |
| } else { |
| /* ignore the error and keep phase 1 handler */ |
| return 0; |
| } |
| } |
| |
| #ifndef ENABLE_FRAG |
| /* free resend buffer */ |
| if (iph1->sendbuf == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no buffer found as sendbuf\n"); |
| return -1; |
| } |
| #endif |
| |
| VPTRINIT(iph1->sendbuf); |
| |
| /* turn off schedule */ |
| SCHED_KILL(iph1->scr); |
| |
| /* send */ |
| 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 packet.\n"); |
| 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 |
| |
| /* save created date. */ |
| (void)time(&iph1->created); |
| |
| /* add to the schedule to expire, and seve back pointer. */ |
| iph1->sce = sched_new(iph1->approval->lifetime, |
| isakmp_ph1expire_stub, iph1); |
| #ifdef ENABLE_HYBRID |
| if (iph1->mode_cfg->flags & ISAKMP_CFG_VENDORID_XAUTH) { |
| switch(AUTHMETHOD(iph1)) { |
| 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); |
| /* XXX Don't process INITIAL_CONTACT */ |
| iph1->rmconf->ini_contact = 0; |
| break; |
| default: |
| break; |
| } |
| } |
| #endif |
| #ifdef ENABLE_DPD |
| /* Schedule the r_u_there.... */ |
| if(iph1->dpd_support && iph1->rmconf->dpd_interval) |
| isakmp_sched_r_u(iph1, 0); |
| #endif |
| |
| /* INITIAL-CONTACT processing */ |
| /* don't anything if local test mode. */ |
| if (!f_local |
| && iph1->rmconf->ini_contact && !getcontacted(iph1->remote)) { |
| /* send INITIAL-CONTACT */ |
| isakmp_info_send_n1(iph1, |
| ISAKMP_NTYPE_INITIAL_CONTACT, NULL); |
| /* insert a node into contacted list. */ |
| if (inscontacted(iph1->remote) == -1) { |
| plog(LLV_ERROR, LOCATION, iph1->remote, |
| "failed to add contacted list.\n"); |
| /* ignore */ |
| } |
| } |
| |
| log_ph1established(iph1); |
| plog(LLV_DEBUG, LOCATION, NULL, "===\n"); |
| |
| /* |
| * SA up shell script hook: do it now,except if |
| * ISAKMP mode config was requested. In the later |
| * case it is done when we receive the configuration. |
| */ |
| if ((iph1->status == PHASE1ST_ESTABLISHED) && |
| !iph1->rmconf->mode_cfg) { |
| switch (AUTHMETHOD(iph1)) { |
| #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: |
| /* Unimplemeted... */ |
| 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; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * main function of quick mode. |
| */ |
| static int |
| quick_main(iph2, msg) |
| struct ph2handle *iph2; |
| vchar_t *msg; |
| { |
| struct isakmp *isakmp = (struct isakmp *)msg->v; |
| int error; |
| #ifdef ENABLE_STATS |
| struct timeval start, end; |
| #endif |
| |
| /* ignore a packet */ |
| if (iph2->status == PHASE2ST_ESTABLISHED |
| || iph2->status == PHASE2ST_GETSPISENT) |
| return 0; |
| |
| #ifdef ENABLE_STATS |
| gettimeofday(&start, NULL); |
| #endif |
| |
| /* receive */ |
| 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 packet.\n"); |
| if (error == ISAKMP_INTERNAL_ERROR) |
| return 0; |
| isakmp_info_send_n1(iph2->ph1, error, NULL); |
| return -1; |
| } |
| |
| /* when using commit bit, status will be reached here. */ |
| if (iph2->status == PHASE2ST_ADDSA) |
| return 0; |
| |
| /* free resend buffer */ |
| if (iph2->sendbuf == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no buffer found as sendbuf\n"); |
| return -1; |
| } |
| VPTRINIT(iph2->sendbuf); |
| |
| /* turn off schedule */ |
| SCHED_KILL(iph2->scr); |
| |
| /* send */ |
| 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 packet.\n"); |
| 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; |
| } |
| |
| /* new negotiation of phase 1 for initiator */ |
| int |
| isakmp_ph1begin_i(rmconf, remote, local) |
| struct remoteconf *rmconf; |
| struct sockaddr *remote, *local; |
| { |
| struct ph1handle *iph1; |
| #ifdef ENABLE_STATS |
| struct timeval start, end; |
| #endif |
| |
| /* get new entry to isakmp status table. */ |
| iph1 = newph1(); |
| if (iph1 == NULL) |
| return -1; |
| |
| 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 -1; |
| } |
| #endif |
| #ifdef ENABLE_FRAG |
| |
| if(rmconf->ike_frag == ISAKMP_FRAG_FORCE) |
| iph1->frag = 1; |
| else |
| iph1->frag = 0; |
| iph1->frag_chain = NULL; |
| #endif |
| iph1->approval = NULL; |
| |
| /* XXX copy remote address */ |
| if (copy_ph1addresses(iph1, rmconf, remote, local) < 0) { |
| delph1(iph1); |
| return -1; |
| } |
| |
| (void)insph1(iph1); |
| |
| /* start phase 1 exchange */ |
| 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 |
| /* start exchange */ |
| if ((ph1exchange[etypesw1(iph1->etype)] |
| [iph1->side] |
| [iph1->status])(iph1, NULL) != 0) { |
| /* failed to start phase 1 negotiation */ |
| 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; |
| } |
| |
| /* new negotiation of phase 1 for responder */ |
| static int |
| isakmp_ph1begin_r(msg, remote, local, etype) |
| vchar_t *msg; |
| struct sockaddr *remote, *local; |
| u_int8_t etype; |
| { |
| struct isakmp *isakmp = (struct isakmp *)msg->v; |
| struct remoteconf *rmconf; |
| struct ph1handle *iph1; |
| struct etypes *etypeok; |
| #ifdef ENABLE_STATS |
| struct timeval start, end; |
| #endif |
| |
| /* look for my configuration */ |
| rmconf = getrmconf(remote); |
| if (rmconf == NULL) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "couldn't find " |
| "configuration.\n"); |
| return -1; |
| } |
| |
| /* check to be acceptable exchange type */ |
| etypeok = check_etypeok(rmconf, etype); |
| if (etypeok == NULL) { |
| plog(LLV_ERROR, LOCATION, remote, |
| "not acceptable %s mode\n", s_isakmp_etype(etype)); |
| return -1; |
| } |
| |
| /* get new entry to isakmp status table. */ |
| 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->rmconf = rmconf; |
| iph1->flags = 0; |
| iph1->side = RESPONDER; |
| iph1->etype = etypeok->type; |
| 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_chain = NULL; |
| #endif |
| iph1->approval = NULL; |
| |
| #ifdef ENABLE_NATT |
| /* RFC3947 says that we MUST accept new phases1 on NAT-T floated port. |
| * We have to setup this flag now to correctly generate the first reply. |
| * Don't know if a better check could be done for that ? |
| */ |
| if(extract_port(local) == lcconf->port_isakmp_natt) |
| iph1->natt_flags |= (NAT_PORTS_CHANGED); |
| #endif |
| |
| /* copy remote address */ |
| if (copy_ph1addresses(iph1, rmconf, 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 |
| |
| /* start exchange */ |
| 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 packet.\n"); |
| 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 /* ENABLE_FRAG */ |
| |
| /* now that we have a phase1 handle, feed back into our |
| * main receive function to catch fragmented packets |
| */ |
| |
| return isakmp_main(msg, remote, local); |
| |
| #endif /* ENABLE_FRAG */ |
| |
| } |
| |
| /* new negotiation of phase 2 for initiator */ |
| static int |
| isakmp_ph2begin_i(iph1, iph2) |
| 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 |
| |
| /* found ISAKMP-SA. */ |
| 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 |
| /* found isakmp-sa */ |
| bindph12(iph1, iph2); |
| iph2->status = PHASE2ST_STATUS2; |
| |
| if ((ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)] |
| [iph2->side] |
| [iph2->status])(iph2, NULL) < 0) { |
| unbindph12(iph2); |
| /* release ipsecsa handler due to internal error. */ |
| remph2(iph2); |
| return -1; |
| } |
| return 0; |
| } |
| |
| /* new negotiation of phase 2 for responder */ |
| static int |
| isakmp_ph2begin_r(iph1, msg) |
| 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->ph1 = iph1; |
| 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); /* XXX should be considered */ |
| if (iph2->dst == NULL) { |
| delph2(iph2); |
| return -1; |
| } |
| iph2->src = dupsaddr(iph1->local); /* XXX should be considered */ |
| if (iph2->src == NULL) { |
| delph2(iph2); |
| return -1; |
| } |
| #if (!defined(ENABLE_NATT)) || (defined(BROKEN_NATT)) |
| if (set_port(iph2->dst, 0) == NULL || |
| set_port(iph2->src, 0) == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid family: %d\n", iph2->dst->sa_family); |
| delph2(iph2); |
| return -1; |
| } |
| #endif |
| |
| /* add new entry to isakmp status table */ |
| 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 packet.\n"); |
| if (error != ISAKMP_INTERNAL_ERROR) |
| isakmp_info_send_n1(iph2->ph1, error, NULL); |
| /* |
| * release handler because it's wrong that ph2handle is kept |
| * after failed to check message for responder's. |
| */ |
| unbindph12(iph2); |
| remph2(iph2); |
| delph2(iph2); |
| return -1; |
| } |
| |
| /* send */ |
| 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 packet.\n"); |
| /* don't release handler */ |
| 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; |
| } |
| |
| /* |
| * parse ISAKMP payloads, without ISAKMP base header. |
| */ |
| vchar_t * |
| isakmp_parsewoh(np0, gen, len) |
| 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"); |
| |
| /* |
| * 5 is a magic number, but any value larger than 2 should be fine |
| * as we do vrealloc() in the following loop. |
| */ |
| 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; |
| |
| /* parse through general headers */ |
| while (0 < tlen && np != ISAKMP_NPTYPE_NONE) { |
| if (tlen <= sizeof(struct isakmp_gen)) { |
| /* don't send information, see isakmp_ident_r1() */ |
| 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) { |
| int 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; |
| } |
| |
| /* |
| * parse ISAKMP payloads, including ISAKMP base header. |
| */ |
| vchar_t * |
| isakmp_parse(buf) |
| vchar_t *buf; |
| { |
| struct isakmp *isakmp = (struct isakmp *)buf->v; |
| struct isakmp_gen *gen; |
| int 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() |
| { |
| /* initialize a isakmp status table */ |
| initph1tree(); |
| initph2tree(); |
| initctdtree(); |
| init_recvdpkt(); |
| |
| if (isakmp_open() < 0) |
| goto err; |
| |
| return(0); |
| |
| err: |
| isakmp_close(); |
| return(-1); |
| } |
| |
| /* |
| * make strings containing i_cookie + r_cookie + msgid |
| */ |
| const char * |
| isakmp_pindex(index, msgid) |
| const isakmp_index *index; |
| const u_int32_t msgid; |
| { |
| static char buf[64]; |
| const u_char *p; |
| int i, j; |
| |
| memset(buf, 0, sizeof(buf)); |
| |
| /* copy index */ |
| p = (const u_char *)index; |
| 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; |
| |
| /* copy msgid */ |
| snprintf((char *)&buf[j], sizeof(buf) - j, ":%08x", ntohs(msgid)); |
| |
| return buf; |
| } |
| |
| /* open ISAKMP sockets. */ |
| int |
| isakmp_open() |
| { |
| const int yes = 1; |
| int ifnum = 0, encap_ifnum = 0; |
| #ifdef INET6 |
| int pktinfo; |
| #endif |
| struct myaddrs *p; |
| |
| for (p = lcconf->myaddrs; p; p = p->next) { |
| if (!p->addr) |
| continue; |
| |
| /* warn if wildcard address - should we forbid this? */ |
| switch (p->addr->sa_family) { |
| case AF_INET: |
| if (((struct sockaddr_in *)p->addr)->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_UNSPECIFIED(&((struct sockaddr_in6 *)p->addr)->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", |
| lcconf->default_af); |
| goto err_and_next; |
| } |
| |
| #ifdef INET6 |
| if (p->addr->sa_family == AF_INET6 && |
| IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *) |
| p->addr)->sin6_addr)) |
| { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "Ignoring multicast address %s\n", |
| saddr2str(p->addr)); |
| racoon_free(p->addr); |
| p->addr = NULL; |
| continue; |
| } |
| #endif |
| |
| if ((p->sock = socket(p->addr->sa_family, SOCK_DGRAM, 0)) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "socket (%s)\n", strerror(errno)); |
| goto err_and_next; |
| } |
| |
| if (fcntl(p->sock, F_SETFL, O_NONBLOCK) == -1) |
| plog(LLV_WARNING, LOCATION, NULL, |
| "failed to put socket in non-blocking mode\n"); |
| |
| /* receive my interface address on inbound packets. */ |
| switch (p->addr->sa_family) { |
| case AF_INET: |
| if (setsockopt(p->sock, 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_and_next; |
| } |
| break; |
| #ifdef INET6 |
| case AF_INET6: |
| #ifdef INET6_ADVAPI |
| #ifdef IPV6_RECVPKTINFO |
| pktinfo = IPV6_RECVPKTINFO; |
| #else /* old adv. API */ |
| pktinfo = IPV6_PKTINFO; |
| #endif /* IPV6_RECVPKTINFO */ |
| #else |
| pktinfo = IPV6_RECVDSTADDR; |
| #endif |
| if (setsockopt(p->sock, IPPROTO_IPV6, pktinfo, |
| (const void *)&yes, sizeof(yes)) < 0) |
| { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "setsockopt IPV6_RECVDSTADDR (%d):%s\n", |
| pktinfo, strerror(errno)); |
| goto err_and_next; |
| } |
| break; |
| #endif |
| } |
| |
| #ifdef IPV6_USE_MIN_MTU |
| if (p->addr->sa_family == AF_INET6 && |
| setsockopt(p->sock, IPPROTO_IPV6, IPV6_USE_MIN_MTU, |
| (void *)&yes, sizeof(yes)) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "setsockopt IPV6_USE_MIN_MTU (%s)\n", |
| strerror(errno)); |
| return -1; |
| } |
| #endif |
| |
| if (setsockopt_bypass(p->sock, p->addr->sa_family) < 0) |
| goto err_and_next; |
| |
| if (bind(p->sock, p->addr, sysdep_sa_len(p->addr)) < 0) { |
| plog(LLV_ERROR, LOCATION, p->addr, |
| "failed to bind to address %s (%s).\n", |
| saddr2str(p->addr), strerror(errno)); |
| close(p->sock); |
| goto err_and_next; |
| } |
| |
| ifnum++; |
| |
| plog(LLV_INFO, LOCATION, NULL, |
| "%s used as isakmp port (fd=%d)\n", |
| saddr2str(p->addr), p->sock); |
| |
| #ifdef ENABLE_NATT |
| if (p->addr->sa_family == AF_INET) { |
| int option = -1; |
| |
| |
| if(p->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){ |
| if (setsockopt (p->sock, 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)); |
| goto skip_encap; |
| } |
| else { |
| plog(LLV_INFO, LOCATION, NULL, |
| "%s used for NAT-T\n", |
| saddr2str(p->addr)); |
| encap_ifnum++; |
| } |
| } |
| } |
| skip_encap: |
| #endif |
| continue; |
| |
| err_and_next: |
| racoon_free(p->addr); |
| p->addr = NULL; |
| if (! lcconf->autograbaddr && lcconf->strict_address) |
| return -1; |
| continue; |
| } |
| |
| if (!ifnum) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no address could be bound.\n"); |
| return -1; |
| } |
| |
| #ifdef ENABLE_NATT |
| if (natt_enabled_in_rmconf() && !encap_ifnum) { |
| plog(LLV_WARNING, LOCATION, NULL, |
| "NAT-T is enabled in at least one remote{} section,\n"); |
| plog(LLV_WARNING, LOCATION, NULL, |
| "but no 'isakmp_natt' address was specified!\n"); |
| } |
| #endif |
| |
| return 0; |
| } |
| |
| void |
| isakmp_close() |
| { |
| #ifndef ANDROID_PATCHED |
| struct myaddrs *p, *next; |
| |
| for (p = lcconf->myaddrs; p; p = next) { |
| next = p->next; |
| |
| if (!p->addr) { |
| racoon_free(p); |
| continue; |
| } |
| close(p->sock); |
| racoon_free(p->addr); |
| racoon_free(p); |
| } |
| |
| lcconf->myaddrs = NULL; |
| #endif |
| } |
| |
| int |
| isakmp_send(iph1, sbuf) |
| 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; |
| |
| /* Check if NON_ESP_MARKER_LEN is already there (happens when resending packets) |
| */ |
| if(extralen == NON_ESP_MARKER_LEN && |
| *(u_int32_t *)sbuf->v == 0) |
| extralen = 0; |
| |
| #ifdef ENABLE_FRAG |
| /* |
| * Do not add the non ESP marker for a packet that will |
| * be fragmented. The non ESP marker should appear in |
| * all fragment's packets, but not in the fragmented packet |
| */ |
| if (iph1->frag && sbuf->l > ISAKMP_FRAG_MAXLEN) |
| extralen = 0; |
| #endif |
| if (extralen) |
| plog (LLV_DEBUG, LOCATION, NULL, "Adding NON-ESP marker\n"); |
| |
| /* If NAT-T port floating is in use, 4 zero bytes (non-ESP marker) |
| must added just before the packet itself. For this we must |
| allocate a new buffer and release it at the end. */ |
| if (extralen) { |
| if ((vbuf = vmalloc (sbuf->l + extralen)) == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "vbuf allocation failed\n"); |
| return -1; |
| } |
| *(u_int32_t *)vbuf->v = 0; |
| memcpy (vbuf->v + extralen, sbuf->v, sbuf->l); |
| /* ensures that the modified buffer will be sent back to the caller, so |
| * add_recvdpkt() will add the correct buffer |
| */ |
| swap = *sbuf; |
| *sbuf = *vbuf; |
| *vbuf = swap; |
| vfree(vbuf); |
| } |
| #endif |
| |
| /* select the socket to be sent */ |
| s = getsockmyaddr(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; |
| } |
| |
| /* called from scheduler */ |
| void |
| isakmp_ph1resend_stub(p) |
| void *p; |
| { |
| struct ph1handle *iph1; |
| |
| iph1=(struct ph1handle *)p; |
| if(isakmp_ph1resend(iph1) < 0){ |
| if(iph1->scr != NULL){ |
| /* Should not happen... |
| */ |
| sched_kill(iph1->scr); |
| iph1->scr=NULL; |
| } |
| |
| remph1(iph1); |
| delph1(iph1); |
| } |
| } |
| |
| int |
| isakmp_ph1resend(iph1) |
| struct ph1handle *iph1; |
| { |
| /* Note: NEVER do the rem/del here, it will be done by the caller or by the _stub function |
| */ |
| 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)); |
| EVT_PUSH(iph1->local, iph1->remote, |
| EVTT_PEER_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_PUSH(iph1->local, iph1->remote, |
| EVTT_PEER_NO_RESPONSE, NULL); |
| return -1; |
| } |
| |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "resend phase1 packet %s\n", |
| isakmp_pindex(&iph1->index, iph1->msgid)); |
| |
| iph1->retry_counter--; |
| |
| iph1->scr = sched_new(iph1->rmconf->retry_interval, |
| isakmp_ph1resend_stub, iph1); |
| |
| return 0; |
| } |
| |
| /* called from scheduler */ |
| void |
| isakmp_ph2resend_stub(p) |
| void *p; |
| { |
| struct ph2handle *iph2; |
| |
| iph2=(struct ph2handle *)p; |
| |
| if(isakmp_ph2resend(iph2) < 0){ |
| unbindph12(iph2); |
| remph2(iph2); |
| delph2(iph2); |
| } |
| } |
| |
| int |
| isakmp_ph2resend(iph2) |
| struct ph2handle *iph2; |
| { |
| /* Note: NEVER do the unbind/rem/del here, it will be done by the caller or by the _stub function |
| */ |
| 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_PUSH(iph2->src, iph2->dst, EVTT_PEER_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_PUSH(iph2->src, iph2->dst, EVTT_PEER_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--; |
| |
| iph2->scr = sched_new(iph2->ph1->rmconf->retry_interval, |
| isakmp_ph2resend_stub, iph2); |
| |
| return 0; |
| } |
| |
| /* called from scheduler */ |
| void |
| isakmp_ph1expire_stub(p) |
| void *p; |
| { |
| |
| isakmp_ph1expire((struct ph1handle *)p); |
| } |
| |
| void |
| isakmp_ph1expire(iph1) |
| struct ph1handle *iph1; |
| { |
| char *src, *dst; |
| |
| SCHED_KILL(iph1->sce); |
| |
| 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; |
| } |
| |
| /* |
| * the phase1 deletion is postponed until there is no phase2. |
| */ |
| if (LIST_FIRST(&iph1->ph2tree) != NULL) { |
| iph1->sce = sched_new(1, isakmp_ph1expire_stub, iph1); |
| return; |
| } |
| |
| iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1); |
| } |
| |
| /* called from scheduler */ |
| void |
| isakmp_ph1delete_stub(p) |
| void *p; |
| { |
| |
| isakmp_ph1delete((struct ph1handle *)p); |
| } |
| |
| void |
| isakmp_ph1delete(iph1) |
| struct ph1handle *iph1; |
| { |
| char *src, *dst; |
| |
| SCHED_KILL(iph1->sce); |
| |
| if (LIST_FIRST(&iph1->ph2tree) != NULL) { |
| iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1); |
| return; |
| } |
| |
| /* don't re-negosiation when the phase 1 SA expires. */ |
| |
| 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_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_DOWN, NULL); |
| racoon_free(src); |
| racoon_free(dst); |
| |
| remph1(iph1); |
| delph1(iph1); |
| |
| return; |
| } |
| |
| /* called from scheduler. |
| * this function will call only isakmp_ph2delete(). |
| * phase 2 handler remain forever if kernel doesn't cry a expire of phase 2 SA |
| * by something cause. That's why this function is called after phase 2 SA |
| * expires in the userland. |
| */ |
| void |
| isakmp_ph2expire_stub(p) |
| void *p; |
| { |
| |
| isakmp_ph2expire((struct ph2handle *)p); |
| } |
| |
| void |
| isakmp_ph2expire(iph2) |
| struct ph2handle *iph2; |
| { |
| char *src, *dst; |
| |
| SCHED_KILL(iph2->sce); |
| |
| 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; |
| |
| iph2->sce = sched_new(1, isakmp_ph2delete_stub, iph2); |
| |
| return; |
| } |
| |
| /* called from scheduler */ |
| void |
| isakmp_ph2delete_stub(p) |
| void *p; |
| { |
| |
| isakmp_ph2delete((struct ph2handle *)p); |
| } |
| |
| void |
| isakmp_ph2delete(iph2) |
| struct ph2handle *iph2; |
| { |
| char *src, *dst; |
| |
| SCHED_KILL(iph2->sce); |
| |
| 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); |
| |
| unbindph12(iph2); |
| remph2(iph2); |
| delph2(iph2); |
| |
| return; |
| } |
| |
| /* %%% |
| * Interface between PF_KEYv2 and ISAKMP |
| */ |
| /* |
| * receive ACQUIRE from kernel, and begin either phase1 or phase2. |
| * if phase1 has been finished, begin phase2. |
| */ |
| int |
| isakmp_post_acquire(iph2) |
| struct ph2handle *iph2; |
| { |
| struct remoteconf *rmconf; |
| struct ph1handle *iph1 = NULL; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "in post_acquire\n"); |
| |
| /* search appropreate configuration with masking port. */ |
| rmconf = getrmconf(iph2->dst); |
| if (rmconf == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no configuration found for %s.\n", |
| saddrwop2str(iph2->dst)); |
| return -1; |
| } |
| |
| /* if passive mode, ignore the acquire message */ |
| if (rmconf->passive) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "because of passive mode, " |
| "ignore the acquire message for %s.\n", |
| saddrwop2str(iph2->dst)); |
| return 0; |
| } |
| |
| /* |
| * Search isakmp status table by address and port |
| * If NAT-T is in use, consider null ports as a |
| * wildcard and use IKE ports instead. |
| */ |
| #ifdef ENABLE_NATT |
| if (!extract_port(iph2->src) && !extract_port(iph2->dst)) { |
| if ((iph1 = getph1byaddrwop(iph2->src, iph2->dst)) != NULL) { |
| set_port(iph2->src, extract_port(iph1->local)); |
| set_port(iph2->dst, extract_port(iph1->remote)); |
| } |
| } else { |
| iph1 = getph1byaddr(iph2->src, iph2->dst, 0); |
| } |
| #else |
| iph1 = getph1byaddr(iph2->src, iph2->dst, 0); |
| #endif |
| |
| /* no ISAKMP-SA found. */ |
| if (iph1 == NULL) { |
| struct sched *sc; |
| |
| iph2->retry_checkph1 = lcconf->retry_checkph1; |
| sc = sched_new(1, isakmp_chkph1there_stub, iph2); |
| plog(LLV_INFO, LOCATION, NULL, |
| "IPsec-SA request for %s queued " |
| "due to no phase1 found.\n", |
| saddrwop2str(iph2->dst)); |
| |
| /* start phase 1 negotiation as a initiator. */ |
| if (isakmp_ph1begin_i(rmconf, iph2->dst, iph2->src) < 0) { |
| SCHED_KILL(sc); |
| return -1; |
| } |
| |
| return 0; |
| /*NOTREACHED*/ |
| } |
| |
| /* found ISAKMP-SA, but on negotiation. */ |
| if (iph1->status != PHASE1ST_ESTABLISHED) { |
| iph2->retry_checkph1 = lcconf->retry_checkph1; |
| sched_new(1, isakmp_chkph1there_stub, iph2); |
| plog(LLV_INFO, LOCATION, iph2->dst, |
| "request for establishing IPsec-SA was queued " |
| "due to no phase1 found.\n"); |
| return 0; |
| /*NOTREACHED*/ |
| } |
| |
| /* found established ISAKMP-SA */ |
| /* i.e. iph1->status == PHASE1ST_ESTABLISHED */ |
| |
| /* found ISAKMP-SA. */ |
| plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n"); |
| |
| /* begin quick mode */ |
| if (isakmp_ph2begin_i(iph1, iph2)) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* |
| * receive GETSPI from kernel. |
| */ |
| int |
| isakmp_post_getspi(iph2) |
| struct ph2handle *iph2; |
| { |
| #ifdef ENABLE_STATS |
| struct timeval start, end; |
| #endif |
| |
| /* don't process it because there is no suitable phase1-sa. */ |
| 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; |
| } |
| |
| /* called by scheduler */ |
| void |
| isakmp_chkph1there_stub(p) |
| void *p; |
| { |
| isakmp_chkph1there((struct ph2handle *)p); |
| } |
| |
| void |
| isakmp_chkph1there(iph2) |
| 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->dst, iph2->src, |
| iph2->satype, 0, 0)); |
| plog(LLV_INFO, LOCATION, NULL, |
| "delete phase 2 handler.\n"); |
| |
| /* send acquire to kernel as error */ |
| pk_sendeacquire(iph2); |
| |
| unbindph12(iph2); |
| remph2(iph2); |
| delph2(iph2); |
| |
| return; |
| } |
| |
| /* |
| * Search isakmp status table by address and port |
| * If NAT-T is in use, consider null ports as a |
| * wildcard and use IKE ports instead. |
| */ |
| #ifdef ENABLE_NATT |
| if (!extract_port(iph2->src) && !extract_port(iph2->dst)) { |
| plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: extract_port.\n"); |
| if( (iph1 = getph1byaddrwop(iph2->src, iph2->dst)) != NULL){ |
| plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: found a ph1 wop.\n"); |
| } |
| } else { |
| plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: searching byaddr.\n"); |
| iph1 = getph1byaddr(iph2->src, iph2->dst, 0); |
| if(iph1 != NULL) |
| plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: found byaddr.\n"); |
| } |
| #else |
| iph1 = getph1byaddr(iph2->src, iph2->dst, 0); |
| #endif |
| |
| /* XXX Even if ph1 as responder is there, should we not start |
| * phase 2 negotiation ? */ |
| if (iph1 != NULL |
| && iph1->status == PHASE1ST_ESTABLISHED) { |
| /* found isakmp-sa */ |
| |
| 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)); |
| |
| /* begin quick mode */ |
| (void)isakmp_ph2begin_i(iph1, iph2); |
| return; |
| } |
| |
| plog(LLV_DEBUG2, LOCATION, NULL, "CHKPH1THERE: no established ph1 handler found\n"); |
| |
| /* no isakmp-sa found */ |
| sched_new(1, isakmp_chkph1there_stub, iph2); |
| |
| return; |
| } |
| |
| /* copy variable data into ALLOCATED buffer. */ |
| caddr_t |
| isakmp_set_attr_v(buf, type, val, len) |
| caddr_t buf; |
| int type; |
| caddr_t val; |
| int len; |
| { |
| struct isakmp_data *data; |
| |
| data = (struct isakmp_data *)buf; |
| data->type = htons((u_int16_t)type | ISAKMP_GEN_TLV); |
| data->lorv = htons((u_int16_t)len); |
| memcpy(data + 1, val, len); |
| |
| return buf + sizeof(*data) + len; |
| } |
| |
| /* copy fixed length data into ALLOCATED buffer. */ |
| caddr_t |
| isakmp_set_attr_l(buf, type, val) |
| caddr_t buf; |
| int type; |
| u_int32_t val; |
| { |
| struct isakmp_data *data; |
| |
| data = (struct isakmp_data *)buf; |
| data->type = htons((u_int16_t)type | ISAKMP_GEN_TV); |
| data->lorv = htons((u_int16_t)val); |
| |
| return buf + sizeof(*data); |
| } |
| |
| /* add a variable data attribute to the buffer by reallocating it. */ |
| vchar_t * |
| isakmp_add_attr_v(buf0, type, val, len) |
| vchar_t *buf0; |
| int type; |
| caddr_t val; |
| int len; |
| { |
| vchar_t *buf = NULL; |
| struct isakmp_data *data; |
| int tlen; |
| int 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((u_int16_t)type | ISAKMP_GEN_TLV); |
| data->lorv = htons((u_int16_t)len); |
| memcpy(data + 1, val, len); |
| |
| return buf; |
| } |
| |
| /* add a fixed data attribute to the buffer by reallocating it. */ |
| vchar_t * |
| isakmp_add_attr_l(buf0, type, val) |
| vchar_t *buf0; |
| int type; |
| u_int32_t val; |
| { |
| vchar_t *buf = NULL; |
| struct isakmp_data *data; |
| int tlen; |
| int 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((u_int16_t)type | ISAKMP_GEN_TV); |
| data->lorv = htons((u_int16_t)val); |
| |
| return buf; |
| } |
| |
| /* |
| * calculate cookie and set. |
| */ |
| int |
| isakmp_newcookie(place, remote, local) |
| caddr_t place; |
| struct sockaddr *remote; |
| struct sockaddr *local; |
| { |
| vchar_t *buf = NULL, *buf2 = NULL; |
| char *p; |
| int blen; |
| int 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; |
| |
| /* copy my address */ |
| memcpy(p, sa1, alen); |
| p += alen; |
| port = ((struct sockaddr_in *)remote)->sin_port; |
| memcpy(p, &port, sizeof(u_short)); |
| p += sizeof(u_short); |
| |
| /* copy target address */ |
| memcpy(p, sa2, alen); |
| p += alen; |
| port = ((struct sockaddr_in *)local)->sin_port; |
| memcpy(p, &port, sizeof(u_short)); |
| p += sizeof(u_short); |
| |
| /* copy time */ |
| t = time(0); |
| memcpy(p, (caddr_t)&t, sizeof(t)); |
| p += sizeof(t); |
| |
| /* copy random value */ |
| 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; |
| } |
| |
| /* |
| * save partner's(payload) data into phhandle. |
| */ |
| int |
| isakmp_p2ph(buf, gen) |
| vchar_t **buf; |
| struct isakmp_gen *gen; |
| { |
| /* XXX to be checked in each functions for logging. */ |
| 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; |
| } |
| |
| u_int32_t |
| isakmp_newmsgid2(iph1) |
| struct ph1handle *iph1; |
| { |
| u_int32_t msgid2; |
| |
| do { |
| msgid2 = eay_random(); |
| } while (getph2bymsgid(iph1, msgid2)); |
| |
| return msgid2; |
| } |
| |
| /* |
| * set values into allocated buffer of isakmp header for phase 1 |
| */ |
| static caddr_t |
| set_isakmp_header(vbuf, iph1, nptype, etype, flags, msgid) |
| vchar_t *vbuf; |
| struct ph1handle *iph1; |
| int nptype; |
| u_int8_t etype; |
| u_int8_t flags; |
| u_int32_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); |
| } |
| |
| /* |
| * set values into allocated buffer of isakmp header for phase 1 |
| */ |
| caddr_t |
| set_isakmp_header1(vbuf, iph1, nptype) |
| vchar_t *vbuf; |
| struct ph1handle *iph1; |
| int nptype; |
| { |
| return set_isakmp_header (vbuf, iph1, nptype, iph1->etype, iph1->flags, iph1->msgid); |
| } |
| |
| /* |
| * set values into allocated buffer of isakmp header for phase 2 |
| */ |
| caddr_t |
| set_isakmp_header2(vbuf, iph2, nptype) |
| vchar_t *vbuf; |
| struct ph2handle *iph2; |
| int nptype; |
| { |
| return set_isakmp_header (vbuf, iph2->ph1, nptype, ISAKMP_ETYPE_QUICK, iph2->flags, iph2->msgid); |
| } |
| |
| /* |
| * set values into allocated buffer of isakmp payload. |
| */ |
| caddr_t |
| set_isakmp_payload(buf, src, nptype) |
| 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(etype) |
| 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; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| static int |
| etypesw2(etype) |
| int etype; |
| { |
| switch (etype) { |
| case ISAKMP_ETYPE_QUICK: |
| return 1; |
| default: |
| return 0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| #ifdef HAVE_PRINT_ISAKMP_C |
| /* for print-isakmp.c */ |
| char *snapend; |
| extern void isakmp_print __P((const u_char *, u_int, const u_char *)); |
| |
| char *getname __P((const u_char *)); |
| #ifdef INET6 |
| char *getname6 __P((const u_char *)); |
| #endif |
| int safeputchar __P((int)); |
| |
| /* |
| * Return a name for the IP address pointed to by ap. This address |
| * is assumed to be in network byte order. |
| */ |
| 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 |
| /* |
| * Return a name for the IP6 address pointed to by ap. This address |
| * is assumed to be in network byte order. |
| */ |
| 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 /* INET6 */ |
| |
| 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, (u_int32_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 /*HAVE_PRINT_ISAKMP_C*/ |
| |
| int |
| copy_ph1addresses(iph1, rmconf, remote, local) |
| struct ph1handle *iph1; |
| struct remoteconf *rmconf; |
| struct sockaddr *remote, *local; |
| { |
| u_int16_t port; |
| |
| /* address portion must be grabbed from real remote address "remote" */ |
| iph1->remote = dupsaddr(remote); |
| if (iph1->remote == NULL) |
| return -1; |
| |
| /* |
| * if remote has no port # (in case of initiator - from ACQUIRE msg) |
| * - if remote.conf specifies port #, use that |
| * - if remote.conf does not, use 500 |
| * if remote has port # (in case of responder - from recvfrom(2)) |
| * respect content of "remote". |
| */ |
| if (extract_port(iph1->remote) == 0) { |
| port = extract_port(rmconf->remote); |
| if (port == 0) |
| port = 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) |
| set_port(iph1->local, PORT_ISAKMP); |
| |
| #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(iph1, msg) |
| struct ph1handle *iph1; |
| vchar_t *msg; |
| { |
| plog(LLV_ERROR, LOCATION, iph1->remote, "wrong state %u.\n", |
| iph1->status); |
| return -1; |
| } |
| |
| static int |
| nostate2(iph2, msg) |
| struct ph2handle *iph2; |
| vchar_t *msg; |
| { |
| plog(LLV_ERROR, LOCATION, iph2->ph1->remote, "wrong state %u.\n", |
| iph2->status); |
| return -1; |
| } |
| |
| void |
| log_ph1established(iph1) |
| 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_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_UP, NULL); |
| if(!iph1->rmconf->mode_cfg) |
| EVT_PUSH(iph1->local, iph1->remote, EVTT_NO_ISAKMP_CFG, NULL); |
| |
| racoon_free(src); |
| racoon_free(dst); |
| |
| return; |
| } |
| |
| struct payload_list * |
| isakmp_plist_append (struct payload_list *plist, vchar_t *payload, int payload_type) |
| { |
| 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; |
| |
| 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; |
| |
| /* Seek to the first item. */ |
| while (ptr->prev) ptr = ptr->prev; |
| first = ptr; |
| |
| /* Compute the whole length. */ |
| 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; |
| racoon_free (first); |
| /* ptr->prev = NULL; first = NULL; ... omitted. */ |
| n++; |
| } |
| |
| *plist = NULL; |
| |
| return buf; |
| end: |
| if (buf != NULL) |
| vfree(buf); |
| return NULL; |
| } |
| |
| #ifdef ENABLE_FRAG |
| int |
| frag_handler(iph1, msg, remote, local) |
| 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(iph1, script) |
| 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; |
| struct sockaddr_in *sin; |
| 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 |
| |
| /* local address */ |
| sin = (struct sockaddr_in *)iph1->local; |
| inet_ntop(sin->sin_family, &sin->sin_addr, addrstr, IP_MAX); |
| snprintf(portstr, PORT_MAX, "%d", ntohs(sin->sin_port)); |
| |
| 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; |
| } |
| |
| /* Peer address */ |
| if (iph1->remote != NULL) { |
| sin = (struct sockaddr_in *)iph1->remote; |
| inet_ntop(sin->sin_family, &sin->sin_addr, addrstr, IP_MAX); |
| snprintf(portstr, PORT_MAX, "%d", ntohs(sin->sin_port)); |
| |
| 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 (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(envp, envc, name, value) |
| char ***envp; |
| int *envc; |
| 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(script, name, envp) |
| char *script; |
| int name; |
| char *const envp[]; |
| { |
| char *argv[] = { NULL, NULL, NULL }; |
| |
| argv[0] = script; |
| argv[1] = 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); |
| break; |
| case -1: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Cannot fork: %s\n", strerror(errno)); |
| return -1; |
| break; |
| default: |
| break; |
| } |
| return 0; |
| |
| } |
| |
| void |
| purge_remote(iph1) |
| 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)); |
| |
| /* Mark as expired. */ |
| iph1->status = PHASE1ST_EXPIRED; |
| |
| /* Check if we have another, still valid, phase1 SA. */ |
| new_iph1 = getph1byaddr(iph1->local, iph1->remote, 1); |
| |
| /* |
| * Delete all orphaned or binded to the deleting ph1handle phase2 SAs. |
| * Keep all others phase2 SAs. |
| */ |
| 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; |
| } |
| 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; |
| } |
| |
| /* |
| * check in/outbound SAs. |
| * Select only SAs where src == local and dst == remote (outgoing) |
| * or src == remote and dst == local (incoming). |
| */ |
| if ((CMPSADDR(iph1->local, src) || CMPSADDR(iph1->remote, dst)) && |
| (CMPSADDR(iph1->local, dst) || CMPSADDR(iph1->remote, src))) { |
| msg = next; |
| continue; |
| } |
| |
| proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); |
| iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); |
| |
| /* Check if there is another valid ISAKMP-SA */ |
| if (new_iph1 != NULL) { |
| |
| if (iph2 == NULL) { |
| /* No handler... still send a pfkey_delete message, but log this !*/ |
| plog(LLV_INFO, LOCATION, NULL, |
| "Unknown IPsec-SA spi=%u, hmmmm?\n", |
| ntohl(sa->sadb_sa_spi)); |
| }else{ |
| |
| /* |
| * If we have a new ph1, do not purge IPsec-SAs binded |
| * to a different ISAKMP-SA |
| */ |
| if (iph2->ph1 != NULL && iph2->ph1 != iph1){ |
| msg = next; |
| continue; |
| } |
| |
| /* If the ph2handle is established, do not purge IPsec-SA */ |
| 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); |
| |
| /* delete a relative phase 2 handle. */ |
| if (iph2 != NULL) { |
| delete_spd(iph2, 0); |
| unbindph12(iph2); |
| 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); |
| |
| /* Mark the phase1 handler as EXPIRED */ |
| plog(LLV_INFO, LOCATION, NULL, |
| "purged ISAKMP-SA spi=%s.\n", |
| isakmp_pindex(&(iph1->index), iph1->msgid)); |
| |
| SCHED_KILL(iph1->sce); |
| |
| iph1->sce = sched_new(1, isakmp_ph1delete_stub, iph1); |
| } |
| |
| void |
| delete_spd(iph2, created) |
| struct ph2handle *iph2; |
| u_int64_t created; |
| { |
| struct policyindex spidx; |
| struct sockaddr_storage addr; |
| u_int8_t pref; |
| struct sockaddr *src; |
| struct sockaddr *dst; |
| int error; |
| int idi2type = 0;/* switch whether copy IDs into id[src,dst]. */ |
| |
| if (iph2 == NULL) |
| return; |
| |
| /* Delete the SPD entry if we generated it |
| */ |
| if (! iph2->generated_spidx ) |
| return; |
| |
| src = iph2->src; |
| dst = iph2->dst; |
| |
| plog(LLV_INFO, LOCATION, NULL, |
| "generated policy, deleting it.\n"); |
| |
| memset(&spidx, 0, sizeof(spidx)); |
| iph2->spidx_gen = (caddr_t )&spidx; |
| |
| /* make inbound policy */ |
| iph2->src = dst; |
| iph2->dst = src; |
| spidx.dir = IPSEC_DIR_INBOUND; |
| spidx.ul_proto = 0; |
| |
| /* |
| * Note: code from get_proposal_r |
| */ |
| |
| #define _XIDT(d) ((struct ipsecdoi_id_b *)(d)->v)->type |
| |
| /* |
| * make destination address in spidx from either ID payload |
| * or phase 1 address into a address in spidx. |
| */ |
| 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)) { |
| /* get a destination address of a policy */ |
| error = ipsecdoi_id2sockaddr(iph2->id, |
| (struct sockaddr *)&spidx.dst, |
| &spidx.prefd, &spidx.ul_proto); |
| if (error) |
| goto purge; |
| |
| #ifdef INET6 |
| /* |
| * get scopeid from the SA address. |
| * note that the phase 1 source address is used as |
| * a destination address to search for a inbound |
| * policy entry because rcoon is responder. |
| */ |
| 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"); |
| |
| /* |
| * copy the SOURCE address of IKE into the |
| * DESTINATION address of the key to search the |
| * SPD because the direction of policy is inbound. |
| */ |
| 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; |
| } |
| } |
| |
| /* make source address in spidx */ |
| 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)) { |
| /* get a source address of inbound SA */ |
| error = ipsecdoi_id2sockaddr(iph2->id_p, |
| (struct sockaddr *)&spidx.src, |
| &spidx.prefs, &spidx.ul_proto); |
| if (error) |
| goto purge; |
| |
| #ifdef INET6 |
| /* |
| * get scopeid from the SA address. |
| * for more detail, see above of this function. |
| */ |
| if (_XIDT(iph2->id_p) == IPSECDOI_ID_IPV6_ADDR) { |
| error = |
| setscopeid((struct sockaddr *)&spidx.src, |
| iph2->dst); |
| if (error) |
| goto purge; |
| } |
| #endif |
| |
| /* make id[src,dst] if both ID types are IP address and same */ |
| if (_XIDT(iph2->id_p) == idi2type |
| && spidx.dst.ss_family == spidx.src.ss_family) { |
| iph2->src_id = |
| dupsaddr((struct sockaddr *)&spidx.dst); |
| if (iph2->src_id == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "allocation failed\n"); |
| goto purge; |
| } |
| iph2->dst_id = |
| dupsaddr((struct sockaddr *)&spidx.src); |
| if (iph2->dst_id == 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"); |
| |
| /* see above comment. */ |
| 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); |
| |
| /* |
| * convert the ul_proto if it is 0 |
| * because 0 in ID payload means a wild card. |
| */ |
| if (spidx.ul_proto == 0) |
| spidx.ul_proto = IPSEC_ULPROTO_ANY; |
| |
| #undef _XIDT |
| |
| /* Check if the generated SPD has the same timestamp as the SA. |
| * If timestamps are different, this means that the SPD entry has been |
| * refreshed by another SA, and should NOT be deleted with the current SA. |
| */ |
| if( created ){ |
| struct secpolicy *p; |
| |
| p = getsp(&spidx); |
| if(p != NULL){ |
| /* just do no test if p is NULL, because this probably just means |
| * that the policy has already be deleted for some reason. |
| */ |
| if(p->spidx.created != created) |
| goto purge; |
| } |
| } |
| |
| /* End of code from get_proposal_r |
| */ |
| |
| 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 |
| /* make forward policy if required */ |
| 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 |
| |
| /* make outbound policy */ |
| 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 |
| u_int32_t |
| setscopeid(sp_addr0, sa_addr0) |
| struct sockaddr *sp_addr0, *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; |
| |
| /* this check should not be here ? */ |
| if (sa_addr->sin6_family != AF_INET6) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "can't get scope ID: family mismatch\n"); |
| return -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 -1; |
| } |
| |
| sp_addr->sin6_scope_id = sa_addr->sin6_scope_id; |
| |
| return 0; |
| } |
| #endif |