/* $KAME: isakmp.c,v 1.172 2002/01/02 09:06:53 jinmei 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 #include #include #include #include #include #include #include #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #ifdef HAVE_UNISTD_H #include #endif #include #if !defined(HAVE_GETADDRINFO) || !defined(HAVE_GETNAMEINFO) #include "addrinfo.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 "isakmp_var.h" #include "isakmp.h" #include "oakley.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" #include "strnames.h" 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)); /* * isakmp packet handler */ int isakmp_handler(so_isakmp) int so_isakmp; { struct isakmp isakmp; struct sockaddr_storage remote; struct sockaddr_storage local; int remote_len = sizeof(remote); int local_len = sizeof(local); int len; u_short port; vchar_t *buf = NULL; int error = -1; /* read message by MSG_PEEK */ while ((len = recvfromto(so_isakmp, (char *)&isakmp, sizeof(isakmp), 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\n"); goto end; } /* check isakmp header length */ if (len < sizeof(isakmp)) { plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, "packet shorter than isakmp header size.\n"); /* 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\n"); } goto end; } /* read real message */ if ((buf = vmalloc(ntohl(isakmp.len))) == NULL) { plog(LLV_ERROR, LOCATION, NULL, "failed to allocate reading buffer\n"); /* 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\n"); } goto end; } while ((len = recvfromto(so_isakmp, buf->v, buf->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\n"); goto end; } if (len != buf->l) { plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, "received invalid length, why ?\n"); goto end; } plog(LLV_DEBUG, LOCATION, NULL, "===\n"); plog(LLV_DEBUG, LOCATION, (struct sockaddr *)&local, "%d bytes message received from %s\n", len, saddr2str((struct sockaddr *)&remote)); plogdump(LLV_DEBUG, buf->v, buf->l); /* avoid packets with malicious port/address */ switch (remote.ss_family) { case AF_INET: port = ((struct sockaddr_in *)&remote)->sin_port; break; #ifdef INET6 case AF_INET6: port = ((struct sockaddr_in6 *)&remote)->sin6_port; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", remote.ss_family); goto end; } if (port == 0) { plog(LLV_ERROR, LOCATION, (struct sockaddr *)&remote, "src port == 0 (valid as UDP but not with IKE)\n"); goto end; } /* simply reply if the packet was processed. */ if (check_recvdpkt((struct sockaddr *)&remote, (struct sockaddr *)&local, buf)) { plog(LLV_NOTIFY, LOCATION, NULL, "the packet is retransmitted by %s.\n", saddr2str((struct sockaddr *)&remote)); error = 0; goto end; } /* isakmp main routine */ if (isakmp_main(buf, (struct sockaddr *)&remote, (struct sockaddr *)&local) != 0) goto end; error = 0; end: 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. */ 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. */ 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"); //LLL return -1; } /* 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 = strdup(saddr2str(iph1->remote)); saddr_act = strdup(saddr2str(remote)); 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. */ } 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; } /* 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)); } } 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; } /* 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. */ 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; } isakmp_newgroup_r(iph1, msg); break; 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) { /* ignore the error and keep phase 1 handler */ return 0; } /* free resend buffer */ if (iph1->sendbuf == NULL) { plog(LLV_ERROR, LOCATION, NULL, "no buffer found as sendbuf\n"); return -1; } vfree(iph1->sendbuf); iph1->sendbuf = NULL; /* turn off schedule */ if (iph1->scr) 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); /* 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"); } 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; } vfree(iph2->sendbuf); iph2->sendbuf = NULL; /* turn off schedule */ if (iph2->scr) 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) struct remoteconf *rmconf; struct sockaddr *remote; { 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 iph1->approval = NULL; if (copy_ph1addresses(iph1, rmconf, remote, NULL) < 0) return -1; (void)insph1(iph1); /* start phase 1 exchange */ iph1->etype = rmconf->etypes->type; plog(LLV_DEBUG, LOCATION, NULL, "===\n"); { char *a; a = strdup(saddr2str(iph1->local)); 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 iph1->approval = NULL; /* copy remote address */ if (copy_ph1addresses(iph1, rmconf, remote, local) < 0) return -1; (void)insph1(iph1); plog(LLV_DEBUG, LOCATION, NULL, "===\n"); { char *a; a = strdup(saddr2str(iph1->local)); 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 /* 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; } /* new negotiation of phase 2 for initiator */ static int isakmp_ph2begin_i(iph1, iph2) struct ph1handle *iph1; struct ph2handle *iph2; { /* found ISAKMP-SA. */ plog(LLV_DEBUG, LOCATION, NULL, "===\n"); plog(LLV_DEBUG, LOCATION, NULL, "begin QUICK mode.\n"); { char *a; a = strdup(saddr2str(iph2->src)); 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); delph2(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 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); if (iph2->dst == NULL) { delph2(iph2); return -1; } switch (iph2->dst->sa_family) { case AF_INET: ((struct sockaddr_in *)iph2->dst)->sin_port = 0; break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)iph2->dst)->sin6_port = 0; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph2->dst->sa_family); delph2(iph2); return -1; } iph2->src = dupsaddr(iph1->local); if (iph2->src == NULL) { delph2(iph2); return -1; } switch (iph2->src->sa_family) { case AF_INET: ((struct sockaddr_in *)iph2->src)->sin_port = 0; break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)iph2->src)->sin6_port = 0; break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph2->src->sa_family); delph2(iph2); return -1; } /* add new entry to isakmp status table */ insph2(iph2); bindph12(iph1, iph2); plog(LLV_DEBUG, LOCATION, NULL, "===\n"); { char *a; a = strdup(saddr2str(iph2->src)); 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 == 0 || 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(); srandom(time(0)); 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; #ifdef INET6 int pktinfo; #endif struct myaddrs *p; ifnum = 0; 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; } 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; } /* receive my interface address on inbound packets. */ switch (p->addr->sa_family) { case AF_INET: #ifndef __linux__ if (setsockopt(p->sock, IPPROTO_IP, IP_RECVDSTADDR, (const void *)&yes, sizeof(yes)) < 0) { plog(LLV_ERROR, LOCATION, NULL, "setsockopt (%s)\n", strerror(errno)); goto err_and_next; } #else if (setsockopt(p->sock, IPPROTO_IP, IP_PKTINFO, (const void *)&yes, sizeof(yes)) < 0) { plog(LLV_ERROR, LOCATION, NULL, "setsockopt (%s)\n", strerror(errno)); goto err_and_next; } #endif break; #ifdef INET6 case AF_INET6: #ifdef 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(%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 (%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 (%s).\n", 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); 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; } return 0; } void isakmp_close() { struct myaddrs *p, *next; for (p = lcconf->myaddrs; p; p = next) { next = p->next; if (!p->addr) continue; close(p->sock); racoon_free(p->addr); racoon_free(p); } lcconf->myaddrs = NULL; } int isakmp_send(iph1, sbuf) struct ph1handle *iph1; vchar_t *sbuf; { int len = 0; int s; /* select the socket to be sent */ s = getsockmyaddr(iph1->local); if (s == -1) return -1; 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; { (void)isakmp_ph1resend((struct ph1handle *)p); } int isakmp_ph1resend(iph1) struct ph1handle *iph1; { if (iph1->retry_counter < 0) { plog(LLV_ERROR, LOCATION, NULL, "phase1 negotiation failed due to time up. %s\n", isakmp_pindex(&iph1->index, iph1->msgid)); remph1(iph1); delph1(iph1); return -1; } if (isakmp_send(iph1, iph1->sendbuf) < 0) 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; { (void)isakmp_ph2resend((struct ph2handle *)p); } int isakmp_ph2resend(iph2) struct ph2handle *iph2; { 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)); unbindph12(iph2); remph2(iph2); delph2(iph2); return -1; } if (isakmp_send(iph2->ph1, iph2->sendbuf) < 0) 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; src = strdup(saddr2str(iph1->local)); dst = strdup(saddr2str(iph1->remote)); 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); SCHED_KILL(iph1->sce); 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 = strdup(saddr2str(iph1->local)); dst = strdup(saddr2str(iph1->remote)); plog(LLV_INFO, LOCATION, NULL, "ISAKMP-SA deleted %s-%s spi:%s\n", src, dst, isakmp_pindex(&iph1->index, 0)); 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 = strdup(saddrwop2str(iph2->src)); dst = strdup(saddrwop2str(iph2->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 = strdup(saddrwop2str(iph2->src)); dst = strdup(saddrwop2str(iph2->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; /* 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 with masking port */ iph1 = getph1byaddr(iph2->src, iph2->dst); /* 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) < 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 == PHASE2ST_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; } iph1 = getph1byaddr(iph2->src, iph2->dst); if (iph1 != NULL && iph1->status == PHASE1ST_ESTABLISHED) { /* found isakmp-sa */ /* begin quick mode */ (void)isakmp_ph2begin_i(iph1, iph2); return; } /* 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 in_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; { 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 = random(); } while (getph2bymsgid(iph1, msgid2)); return msgid2; } /* * set values into allocated buffer of isakmp header for phase 1 */ caddr_t set_isakmp_header(vbuf, iph1, nptype) vchar_t *vbuf; struct ph1handle *iph1; int nptype; { 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 = iph1->etype; isakmp->flags = iph1->flags; isakmp->msgid = iph1->msgid; isakmp->len = htonl(vbuf->l); return vbuf->v + sizeof(*isakmp); } /* * 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; { struct isakmp *isakmp; if (vbuf->l < sizeof(*isakmp)) return NULL; isakmp = (struct isakmp *)vbuf->v; memcpy(&isakmp->i_ck, &iph2->ph1->index.i_ck, sizeof(cookie_t)); memcpy(&isakmp->r_ck, &iph2->ph1->index.r_ck, sizeof(cookie_t)); isakmp->np = nptype; isakmp->v = iph2->ph1->version; isakmp->etype = ISAKMP_ETYPE_QUICK; isakmp->flags = iph2->flags; memcpy(&isakmp->msgid, &iph2->msgid, sizeof(isakmp->msgid)); isakmp->len = htonl(vbuf->l); return vbuf->v + sizeof(*isakmp); } /* * 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 %d, 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)); //addr.sin_len = sizeof(struct sockaddr_in); 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)) strncpy(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)) strncpy(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)) { strncpy(hostbuf, "?", sizeof(hostbuf)); strncpy(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)) { strncpy(hostbuf, "?", sizeof(hostbuf)); strncpy(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) { 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_short *port = NULL; /* address portion must be grabbed from real remote address "remote" */ iph1->remote = dupsaddr(remote); if (iph1->remote == NULL) { delph1(iph1); 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". */ switch (iph1->remote->sa_family) { case AF_INET: port = &((struct sockaddr_in *)iph1->remote)->sin_port; if (*port) break; *port = ((struct sockaddr_in *)rmconf->remote)->sin_port; if (*port) break; *port = htons(PORT_ISAKMP); break; #ifdef INET6 case AF_INET6: port = &((struct sockaddr_in6 *)iph1->remote)->sin6_port; if (*port) break; *port = ((struct sockaddr_in6 *)rmconf->remote)->sin6_port; if (*port) break; *port = htons(PORT_ISAKMP); break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph1->remote->sa_family); return -1; } if (local == NULL) iph1->local = getlocaladdr(iph1->remote); else iph1->local = dupsaddr(local); if (iph1->local == NULL) { delph1(iph1); return -1; } switch (AF_INET) { case AF_INET: ((struct sockaddr_in *)iph1->local)->sin_port = getmyaddrsport(iph1->local); break; #ifdef INET6 case AF_INET6: ((struct sockaddr_in6 *)iph1->local)->sin6_port = getmyaddrsport(iph1->local); break; #endif default: plog(LLV_ERROR, LOCATION, NULL, "invalid family: %d\n", iph1->local->sa_family); delph1(iph1); return -1; } 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 = strdup(saddr2str(iph1->local)); dst = strdup(saddr2str(iph1->remote)); plog(LLV_INFO, LOCATION, NULL, "ISAKMP-SA established %s-%s spi:%s\n", src, dst, isakmp_pindex(&iph1->index, 0)); racoon_free(src); racoon_free(dst); return; }