| /* $NetBSD: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */ |
| |
| /* $Id: pfkey.c,v 1.57 2011/03/15 13:20:14 vanhu Exp $ */ |
| |
| /* |
| * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of the project nor the names of its contributors |
| * may be used to endorse or promote products derived from this software |
| * without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <netdb.h> |
| #include <errno.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <netdb.h> |
| #include <netinet/in.h> |
| #include <arpa/inet.h> |
| |
| #ifdef ENABLE_NATT |
| # ifdef __linux__ |
| # include <linux/udp.h> |
| # endif |
| # if defined(__NetBSD__) || defined(__FreeBSD__) || \ |
| (defined(__APPLE__) && defined(__MACH__)) |
| # include <netinet/udp.h> |
| # endif |
| #endif |
| |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/socket.h> |
| #include <sys/queue.h> |
| #ifndef ANDROID_CHANGES |
| #include <sys/sysctl.h> |
| #endif |
| |
| #include <net/route.h> |
| #include <net/pfkeyv2.h> |
| |
| #include <netinet/in.h> |
| #include PATH_IPSEC_H |
| #include <fcntl.h> |
| |
| #include "libpfkey.h" |
| |
| #include "var.h" |
| #include "misc.h" |
| #include "vmbuf.h" |
| #include "plog.h" |
| #include "sockmisc.h" |
| #include "session.h" |
| #include "debug.h" |
| |
| #include "schedule.h" |
| #include "localconf.h" |
| #include "remoteconf.h" |
| #include "handler.h" |
| #include "policy.h" |
| #include "proposal.h" |
| #include "isakmp_var.h" |
| #include "isakmp.h" |
| #include "isakmp_inf.h" |
| #include "ipsec_doi.h" |
| #include "oakley.h" |
| #include "pfkey.h" |
| #include "algorithm.h" |
| #include "sainfo.h" |
| #include "admin.h" |
| #include "evt.h" |
| #include "privsep.h" |
| #include "strnames.h" |
| #include "backupsa.h" |
| #include "gcmalloc.h" |
| #include "nattraversal.h" |
| #include "crypto_openssl.h" |
| #include "grabmyaddr.h" |
| |
| #if defined(SADB_X_EALG_RIJNDAELCBC) && !defined(SADB_X_EALG_AESCBC) |
| #define SADB_X_EALG_AESCBC SADB_X_EALG_RIJNDAELCBC |
| #endif |
| |
| /* prototype */ |
| static u_int ipsecdoi2pfkey_aalg __P((u_int)); |
| static u_int ipsecdoi2pfkey_ealg __P((u_int)); |
| static u_int ipsecdoi2pfkey_calg __P((u_int)); |
| static u_int ipsecdoi2pfkey_alg __P((u_int, u_int)); |
| static u_int keylen_aalg __P((u_int)); |
| static u_int keylen_ealg __P((u_int, int)); |
| |
| static int pk_recvgetspi __P((caddr_t *)); |
| static int pk_recvupdate __P((caddr_t *)); |
| static int pk_recvadd __P((caddr_t *)); |
| static int pk_recvdelete __P((caddr_t *)); |
| static int pk_recvacquire __P((caddr_t *)); |
| static int pk_recvexpire __P((caddr_t *)); |
| static int pk_recvflush __P((caddr_t *)); |
| static int getsadbpolicy __P((caddr_t *, int *, int, struct ph2handle *)); |
| static int pk_recvspdupdate __P((caddr_t *)); |
| static int pk_recvspdadd __P((caddr_t *)); |
| static int pk_recvspddelete __P((caddr_t *)); |
| static int pk_recvspdexpire __P((caddr_t *)); |
| static int pk_recvspdget __P((caddr_t *)); |
| static int pk_recvspddump __P((caddr_t *)); |
| static int pk_recvspdflush __P((caddr_t *)); |
| #if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) |
| static int pk_recvmigrate __P((caddr_t *)); |
| #endif |
| static struct sadb_msg *pk_recv __P((int, int *)); |
| |
| static int (*pkrecvf[]) __P((caddr_t *)) = { |
| NULL, |
| pk_recvgetspi, |
| pk_recvupdate, |
| pk_recvadd, |
| pk_recvdelete, |
| NULL, /* SADB_GET */ |
| pk_recvacquire, |
| NULL, /* SABD_REGISTER */ |
| pk_recvexpire, |
| pk_recvflush, |
| NULL, /* SADB_DUMP */ |
| NULL, /* SADB_X_PROMISC */ |
| NULL, /* SADB_X_PCHANGE */ |
| pk_recvspdupdate, |
| pk_recvspdadd, |
| pk_recvspddelete, |
| pk_recvspdget, |
| NULL, /* SADB_X_SPDACQUIRE */ |
| pk_recvspddump, |
| pk_recvspdflush, |
| NULL, /* SADB_X_SPDSETIDX */ |
| pk_recvspdexpire, |
| NULL, /* SADB_X_SPDDELETE2 */ |
| NULL, /* SADB_X_NAT_T_NEW_MAPPING */ |
| #if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) |
| pk_recvmigrate, |
| #else |
| NULL, /* SADB_X_MIGRATE */ |
| #endif |
| #if (SADB_MAX > 24) |
| #error "SADB extra message?" |
| #endif |
| }; |
| |
| static int addnewsp __P((caddr_t *, struct sockaddr *, struct sockaddr *)); |
| |
| /* cope with old kame headers - ugly */ |
| #ifndef SADB_X_AALG_MD5 |
| #define SADB_X_AALG_MD5 SADB_AALG_MD5 |
| #endif |
| #ifndef SADB_X_AALG_SHA |
| #define SADB_X_AALG_SHA SADB_AALG_SHA |
| #endif |
| #ifndef SADB_X_AALG_NULL |
| #define SADB_X_AALG_NULL SADB_AALG_NULL |
| #endif |
| |
| #ifndef SADB_X_EALG_BLOWFISHCBC |
| #define SADB_X_EALG_BLOWFISHCBC SADB_EALG_BLOWFISHCBC |
| #endif |
| #ifndef SADB_X_EALG_CAST128CBC |
| #define SADB_X_EALG_CAST128CBC SADB_EALG_CAST128CBC |
| #endif |
| #ifndef SADB_X_EALG_RC5CBC |
| #ifdef SADB_EALG_RC5CBC |
| #define SADB_X_EALG_RC5CBC SADB_EALG_RC5CBC |
| #endif |
| #endif |
| |
| /* |
| * PF_KEY packet handler |
| * 0: success |
| * -1: fail |
| */ |
| static int |
| pfkey_handler(ctx, fd) |
| void *ctx; |
| int fd; |
| { |
| struct sadb_msg *msg; |
| int len; |
| caddr_t mhp[SADB_EXT_MAX + 1]; |
| int error = -1; |
| |
| /* receive pfkey message. */ |
| len = 0; |
| msg = (struct sadb_msg *) pk_recv(fd, &len); |
| if (msg == NULL) { |
| if (len < 0) { |
| /* do not report EAGAIN as error; well get |
| * called from main loop later. and it's normal |
| * when spd dump is received during reload and |
| * this function is called in loop. */ |
| if (errno == EAGAIN) |
| goto end; |
| |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to recv from pfkey (%s)\n", |
| strerror(errno)); |
| goto end; |
| } else { |
| /* short message - msg not ready */ |
| return 0; |
| } |
| } |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "got pfkey %s message\n", |
| s_pfkey_type(msg->sadb_msg_type)); |
| plogdump(LLV_DEBUG2, msg, msg->sadb_msg_len << 3); |
| |
| /* validity check */ |
| if (msg->sadb_msg_errno) { |
| int pri; |
| |
| /* when SPD is empty, treat the state as no error. */ |
| if (msg->sadb_msg_type == SADB_X_SPDDUMP && |
| msg->sadb_msg_errno == ENOENT) |
| pri = LLV_DEBUG; |
| else |
| pri = LLV_ERROR; |
| |
| plog(pri, LOCATION, NULL, |
| "pfkey %s failed: %s\n", |
| s_pfkey_type(msg->sadb_msg_type), |
| strerror(msg->sadb_msg_errno)); |
| |
| goto end; |
| } |
| |
| /* check pfkey message. */ |
| if (pfkey_align(msg, mhp)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed pfkey align (%s)\n", |
| ipsec_strerror()); |
| goto end; |
| } |
| if (pfkey_check(mhp)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed pfkey check (%s)\n", |
| ipsec_strerror()); |
| goto end; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| |
| /* safety check */ |
| if (msg->sadb_msg_type >= ARRAYLEN(pkrecvf)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unknown PF_KEY message type=%u\n", |
| msg->sadb_msg_type); |
| goto end; |
| } |
| |
| if (pkrecvf[msg->sadb_msg_type] == NULL) { |
| plog(LLV_INFO, LOCATION, NULL, |
| "unsupported PF_KEY message %s\n", |
| s_pfkey_type(msg->sadb_msg_type)); |
| goto end; |
| } |
| |
| if ((pkrecvf[msg->sadb_msg_type])(mhp) < 0) |
| goto end; |
| |
| error = 1; |
| end: |
| if (msg) |
| racoon_free(msg); |
| return(error); |
| } |
| |
| /* |
| * dump SADB |
| */ |
| vchar_t * |
| pfkey_dump_sadb(satype) |
| int satype; |
| { |
| int s; |
| vchar_t *buf = NULL; |
| pid_t pid = getpid(); |
| struct sadb_msg *msg = NULL; |
| size_t bl, ml; |
| int len; |
| int bufsiz; |
| |
| if ((s = privsep_socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed pfkey open: %s\n", |
| ipsec_strerror()); |
| return NULL; |
| } |
| |
| if ((bufsiz = pfkey_set_buffer_size(s, lcconf->pfkey_buffer_size)) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed pfkey set buffer size to %d: %s\n", |
| lcconf->pfkey_buffer_size, ipsec_strerror()); |
| return NULL; |
| } else if (bufsiz < lcconf->pfkey_buffer_size) { |
| plog(LLV_WARNING, LOCATION, NULL, |
| "pfkey socket receive buffer set to %dKB, instead of %d\n", |
| bufsiz, lcconf->pfkey_buffer_size); |
| } |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_dump\n"); |
| if (pfkey_send_dump(s, satype) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed dump: %s\n", ipsec_strerror()); |
| goto fail; |
| } |
| |
| while (1) { |
| if (msg) |
| racoon_free(msg); |
| msg = pk_recv(s, &len); |
| if (msg == NULL) { |
| if (len < 0) |
| goto done; |
| else |
| continue; |
| } |
| |
| if (msg->sadb_msg_type != SADB_DUMP || msg->sadb_msg_pid != pid) |
| { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "discarding non-sadb dump msg %p, our pid=%i\n", msg, pid); |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "type %i, pid %i\n", msg->sadb_msg_type, msg->sadb_msg_pid); |
| continue; |
| } |
| |
| |
| ml = msg->sadb_msg_len << 3; |
| bl = buf ? buf->l : 0; |
| buf = vrealloc(buf, bl + ml); |
| if (buf == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to reallocate buffer to dump.\n"); |
| goto fail; |
| } |
| memcpy(buf->v + bl, msg, ml); |
| |
| if (msg->sadb_msg_seq == 0) |
| break; |
| } |
| goto done; |
| |
| fail: |
| if (buf) |
| vfree(buf); |
| buf = NULL; |
| done: |
| if (msg) |
| racoon_free(msg); |
| close(s); |
| return buf; |
| } |
| |
| #ifdef ENABLE_ADMINPORT |
| /* |
| * flush SADB |
| */ |
| void |
| pfkey_flush_sadb(proto) |
| u_int proto; |
| { |
| int satype; |
| |
| /* convert to SADB_SATYPE */ |
| if ((satype = admin2pfkey_proto(proto)) < 0) |
| return; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_flush\n"); |
| if (pfkey_send_flush(lcconf->sock_pfkey, satype) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send flush (%s)\n", ipsec_strerror()); |
| return; |
| } |
| |
| return; |
| } |
| #endif |
| |
| /* |
| * These are the SATYPEs that we manage. We register to get |
| * PF_KEY messages related to these SATYPEs, and we also use |
| * this list to determine which SATYPEs to delete SAs for when |
| * we receive an INITIAL-CONTACT. |
| */ |
| const struct pfkey_satype pfkey_satypes[] = { |
| { SADB_SATYPE_AH, "AH" }, |
| { SADB_SATYPE_ESP, "ESP" }, |
| { SADB_X_SATYPE_IPCOMP, "IPCOMP" }, |
| }; |
| const int pfkey_nsatypes = |
| sizeof(pfkey_satypes) / sizeof(pfkey_satypes[0]); |
| |
| /* |
| * PF_KEY initialization |
| */ |
| int |
| pfkey_init() |
| { |
| int i, reg_fail; |
| int bufsiz; |
| |
| if ((lcconf->sock_pfkey = pfkey_open()) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed pfkey open (%s)\n", ipsec_strerror()); |
| return -1; |
| } |
| if ((bufsiz = pfkey_set_buffer_size(lcconf->sock_pfkey, |
| lcconf->pfkey_buffer_size)) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed to set pfkey buffer size to %d (%s)\n", |
| lcconf->pfkey_buffer_size, ipsec_strerror()); |
| return -1; |
| } else if (bufsiz < lcconf->pfkey_buffer_size) { |
| plog(LLV_WARNING, LOCATION, NULL, |
| "pfkey socket receive buffer set to %dKB, instead of %d\n", |
| bufsiz, lcconf->pfkey_buffer_size); |
| } |
| |
| if (fcntl(lcconf->sock_pfkey, F_SETFL, O_NONBLOCK) == -1) |
| plog(LLV_WARNING, LOCATION, NULL, |
| "failed to set the pfkey socket to NONBLOCK\n"); |
| |
| for (i = 0, reg_fail = 0; i < pfkey_nsatypes; i++) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "call pfkey_send_register for %s\n", |
| pfkey_satypes[i].ps_name); |
| if (pfkey_send_register(lcconf->sock_pfkey, |
| pfkey_satypes[i].ps_satype) < 0 || |
| pfkey_recv_register(lcconf->sock_pfkey) < 0) { |
| plog(LLV_WARNING, LOCATION, NULL, |
| "failed to register %s (%s)\n", |
| pfkey_satypes[i].ps_name, |
| ipsec_strerror()); |
| reg_fail++; |
| } |
| } |
| |
| if (reg_fail == pfkey_nsatypes) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to regist any protocol.\n"); |
| pfkey_close(lcconf->sock_pfkey); |
| return -1; |
| } |
| |
| initsp(); |
| |
| if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec sending spddump failed: %s\n", |
| ipsec_strerror()); |
| pfkey_close(lcconf->sock_pfkey); |
| return -1; |
| } |
| #if 0 |
| if (pfkey_promisc_toggle(1) < 0) { |
| pfkey_close(lcconf->sock_pfkey); |
| return -1; |
| } |
| #endif |
| monitor_fd(lcconf->sock_pfkey, pfkey_handler, NULL, 0); |
| return 0; |
| } |
| |
| int |
| pfkey_reload() |
| { |
| flushsp(); |
| |
| if (pfkey_send_spddump(lcconf->sock_pfkey) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec sending spddump failed: %s\n", |
| ipsec_strerror()); |
| return -1; |
| } |
| |
| while (pfkey_handler(NULL, lcconf->sock_pfkey) > 0) |
| continue; |
| |
| return 0; |
| } |
| |
| /* %%% for conversion */ |
| /* IPSECDOI_ATTR_AUTH -> SADB_AALG */ |
| static u_int |
| ipsecdoi2pfkey_aalg(hashtype) |
| u_int hashtype; |
| { |
| switch (hashtype) { |
| case IPSECDOI_ATTR_AUTH_HMAC_MD5: |
| return SADB_AALG_MD5HMAC; |
| case IPSECDOI_ATTR_AUTH_HMAC_SHA1: |
| return SADB_AALG_SHA1HMAC; |
| case IPSECDOI_ATTR_AUTH_HMAC_SHA2_256: |
| #if (defined SADB_X_AALG_SHA2_256) && !defined(SADB_X_AALG_SHA2_256HMAC) |
| return SADB_X_AALG_SHA2_256; |
| #else |
| return SADB_X_AALG_SHA2_256HMAC; |
| #endif |
| case IPSECDOI_ATTR_AUTH_HMAC_SHA2_384: |
| #if (defined SADB_X_AALG_SHA2_384) && !defined(SADB_X_AALG_SHA2_384HMAC) |
| return SADB_X_AALG_SHA2_384; |
| #else |
| return SADB_X_AALG_SHA2_384HMAC; |
| #endif |
| case IPSECDOI_ATTR_AUTH_HMAC_SHA2_512: |
| #if (defined SADB_X_AALG_SHA2_512) && !defined(SADB_X_AALG_SHA2_512HMAC) |
| return SADB_X_AALG_SHA2_512; |
| #else |
| return SADB_X_AALG_SHA2_512HMAC; |
| #endif |
| case IPSECDOI_ATTR_AUTH_KPDK: /* need special care */ |
| return SADB_AALG_NONE; |
| |
| /* not supported */ |
| case IPSECDOI_ATTR_AUTH_DES_MAC: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Not supported hash type: %u\n", hashtype); |
| return ~0; |
| |
| case 0: /* reserved */ |
| default: |
| return SADB_AALG_NONE; |
| |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid hash type: %u\n", hashtype); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* IPSECDOI_ESP -> SADB_EALG */ |
| static u_int |
| ipsecdoi2pfkey_ealg(t_id) |
| u_int t_id; |
| { |
| switch (t_id) { |
| case IPSECDOI_ESP_DES_IV64: /* sa_flags |= SADB_X_EXT_OLD */ |
| return SADB_EALG_DESCBC; |
| case IPSECDOI_ESP_DES: |
| return SADB_EALG_DESCBC; |
| case IPSECDOI_ESP_3DES: |
| return SADB_EALG_3DESCBC; |
| #ifdef SADB_X_EALG_RC5CBC |
| case IPSECDOI_ESP_RC5: |
| return SADB_X_EALG_RC5CBC; |
| #endif |
| case IPSECDOI_ESP_CAST: |
| return SADB_X_EALG_CAST128CBC; |
| case IPSECDOI_ESP_BLOWFISH: |
| return SADB_X_EALG_BLOWFISHCBC; |
| case IPSECDOI_ESP_DES_IV32: /* flags |= (SADB_X_EXT_OLD| |
| SADB_X_EXT_IV4B)*/ |
| return SADB_EALG_DESCBC; |
| case IPSECDOI_ESP_NULL: |
| return SADB_EALG_NULL; |
| #ifdef SADB_X_EALG_AESCBC |
| case IPSECDOI_ESP_AES: |
| return SADB_X_EALG_AESCBC; |
| #endif |
| #ifdef SADB_X_EALG_TWOFISHCBC |
| case IPSECDOI_ESP_TWOFISH: |
| return SADB_X_EALG_TWOFISHCBC; |
| #endif |
| #ifdef SADB_X_EALG_CAMELLIACBC |
| case IPSECDOI_ESP_CAMELLIA: |
| return SADB_X_EALG_CAMELLIACBC; |
| #endif |
| |
| /* not supported */ |
| case IPSECDOI_ESP_3IDEA: |
| case IPSECDOI_ESP_IDEA: |
| case IPSECDOI_ESP_RC4: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Not supported transform: %u\n", t_id); |
| return ~0; |
| |
| case 0: /* reserved */ |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid transform id: %u\n", t_id); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* IPCOMP -> SADB_CALG */ |
| static u_int |
| ipsecdoi2pfkey_calg(t_id) |
| u_int t_id; |
| { |
| switch (t_id) { |
| case IPSECDOI_IPCOMP_OUI: |
| return SADB_X_CALG_OUI; |
| case IPSECDOI_IPCOMP_DEFLATE: |
| return SADB_X_CALG_DEFLATE; |
| case IPSECDOI_IPCOMP_LZS: |
| return SADB_X_CALG_LZS; |
| |
| case 0: /* reserved */ |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid transform id: %u\n", t_id); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* IPSECDOI_PROTO -> SADB_SATYPE */ |
| u_int |
| ipsecdoi2pfkey_proto(proto) |
| u_int proto; |
| { |
| switch (proto) { |
| case IPSECDOI_PROTO_IPSEC_AH: |
| return SADB_SATYPE_AH; |
| case IPSECDOI_PROTO_IPSEC_ESP: |
| return SADB_SATYPE_ESP; |
| case IPSECDOI_PROTO_IPCOMP: |
| return SADB_X_SATYPE_IPCOMP; |
| |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid ipsec_doi proto: %u\n", proto); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| static u_int |
| ipsecdoi2pfkey_alg(algclass, type) |
| u_int algclass, type; |
| { |
| switch (algclass) { |
| case IPSECDOI_ATTR_AUTH: |
| return ipsecdoi2pfkey_aalg(type); |
| case IPSECDOI_PROTO_IPSEC_ESP: |
| return ipsecdoi2pfkey_ealg(type); |
| case IPSECDOI_PROTO_IPCOMP: |
| return ipsecdoi2pfkey_calg(type); |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid ipsec_doi algclass: %u\n", algclass); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* SADB_SATYPE -> IPSECDOI_PROTO */ |
| u_int |
| pfkey2ipsecdoi_proto(satype) |
| u_int satype; |
| { |
| switch (satype) { |
| case SADB_SATYPE_AH: |
| return IPSECDOI_PROTO_IPSEC_AH; |
| case SADB_SATYPE_ESP: |
| return IPSECDOI_PROTO_IPSEC_ESP; |
| case SADB_X_SATYPE_IPCOMP: |
| return IPSECDOI_PROTO_IPCOMP; |
| |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "Invalid pfkey proto: %u\n", satype); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */ |
| u_int |
| ipsecdoi2pfkey_mode(mode) |
| u_int mode; |
| { |
| switch (mode) { |
| case IPSECDOI_ATTR_ENC_MODE_TUNNEL: |
| #ifdef ENABLE_NATT |
| case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_RFC: |
| case IPSECDOI_ATTR_ENC_MODE_UDPTUNNEL_DRAFT: |
| #endif |
| return IPSEC_MODE_TUNNEL; |
| case IPSECDOI_ATTR_ENC_MODE_TRNS: |
| #ifdef ENABLE_NATT |
| case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC: |
| case IPSECDOI_ATTR_ENC_MODE_UDPTRNS_DRAFT: |
| #endif |
| return IPSEC_MODE_TRANSPORT; |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* IPSECDOI_ATTR_ENC_MODE -> IPSEC_MODE */ |
| u_int |
| pfkey2ipsecdoi_mode(mode) |
| u_int mode; |
| { |
| switch (mode) { |
| case IPSEC_MODE_TUNNEL: |
| return IPSECDOI_ATTR_ENC_MODE_TUNNEL; |
| case IPSEC_MODE_TRANSPORT: |
| return IPSECDOI_ATTR_ENC_MODE_TRNS; |
| case IPSEC_MODE_ANY: |
| return IPSECDOI_ATTR_ENC_MODE_ANY; |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, "Invalid mode type: %u\n", mode); |
| return ~0; |
| } |
| /*NOTREACHED*/ |
| } |
| |
| /* default key length for encryption algorithm */ |
| static u_int |
| keylen_aalg(hashtype) |
| u_int hashtype; |
| { |
| int res; |
| |
| if (hashtype == 0) |
| return SADB_AALG_NONE; |
| |
| res = alg_ipsec_hmacdef_hashlen(hashtype); |
| if (res == -1) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid hmac algorithm %u.\n", hashtype); |
| return ~0; |
| } |
| return res; |
| } |
| |
| /* default key length for encryption algorithm */ |
| static u_int |
| keylen_ealg(enctype, encklen) |
| u_int enctype; |
| int encklen; |
| { |
| int res; |
| |
| res = alg_ipsec_encdef_keylen(enctype, encklen); |
| if (res == -1) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encryption algorithm %u.\n", enctype); |
| return ~0; |
| } |
| return res; |
| } |
| |
| void |
| pk_fixup_sa_addresses(mhp) |
| caddr_t *mhp; |
| { |
| struct sockaddr *src, *dst; |
| |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| set_port(src, PORT_ISAKMP); |
| set_port(dst, PORT_ISAKMP); |
| |
| #ifdef ENABLE_NATT |
| if (PFKEY_ADDR_X_NATTYPE(mhp[SADB_X_EXT_NAT_T_TYPE])) { |
| /* NAT-T is enabled for this SADB entry; copy |
| * the ports from NAT-T extensions */ |
| if(mhp[SADB_X_EXT_NAT_T_SPORT] != NULL) |
| set_port(src, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_SPORT])); |
| if(mhp[SADB_X_EXT_NAT_T_DPORT] != NULL) |
| set_port(dst, PFKEY_ADDR_X_PORT(mhp[SADB_X_EXT_NAT_T_DPORT])); |
| } |
| #endif |
| } |
| |
| int |
| pfkey_convertfromipsecdoi(proto_id, t_id, hashtype, |
| e_type, e_keylen, a_type, a_keylen, flags) |
| u_int proto_id; |
| u_int t_id; |
| u_int hashtype; |
| u_int *e_type; |
| u_int *e_keylen; |
| u_int *a_type; |
| u_int *a_keylen; |
| u_int *flags; |
| { |
| *flags = 0; |
| switch (proto_id) { |
| case IPSECDOI_PROTO_IPSEC_ESP: |
| if ((*e_type = ipsecdoi2pfkey_ealg(t_id)) == ~0) |
| goto bad; |
| if ((*e_keylen = keylen_ealg(t_id, *e_keylen)) == ~0) |
| goto bad; |
| *e_keylen >>= 3; |
| |
| if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0) |
| goto bad; |
| if ((*a_keylen = keylen_aalg(hashtype)) == ~0) |
| goto bad; |
| *a_keylen >>= 3; |
| |
| if (*e_type == SADB_EALG_NONE) { |
| plog(LLV_ERROR, LOCATION, NULL, "no ESP algorithm.\n"); |
| goto bad; |
| } |
| break; |
| |
| case IPSECDOI_PROTO_IPSEC_AH: |
| if ((*a_type = ipsecdoi2pfkey_aalg(hashtype)) == ~0) |
| goto bad; |
| if ((*a_keylen = keylen_aalg(hashtype)) == ~0) |
| goto bad; |
| *a_keylen >>= 3; |
| |
| if (t_id == IPSECDOI_ATTR_AUTH_HMAC_MD5 |
| && hashtype == IPSECDOI_ATTR_AUTH_KPDK) { |
| /* AH_MD5 + Auth(KPDK) = RFC1826 keyed-MD5 */ |
| *a_type = SADB_X_AALG_MD5; |
| *flags |= SADB_X_EXT_OLD; |
| } |
| *e_type = SADB_EALG_NONE; |
| *e_keylen = 0; |
| if (*a_type == SADB_AALG_NONE) { |
| plog(LLV_ERROR, LOCATION, NULL, "no AH algorithm.\n"); |
| goto bad; |
| } |
| break; |
| |
| case IPSECDOI_PROTO_IPCOMP: |
| if ((*e_type = ipsecdoi2pfkey_calg(t_id)) == ~0) |
| goto bad; |
| *e_keylen = 0; |
| |
| *flags = SADB_X_EXT_RAWCPI; |
| |
| *a_type = SADB_AALG_NONE; |
| *a_keylen = 0; |
| if (*e_type == SADB_X_CALG_NONE) { |
| plog(LLV_ERROR, LOCATION, NULL, "no IPCOMP algorithm.\n"); |
| goto bad; |
| } |
| break; |
| |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, "unknown IPsec protocol.\n"); |
| goto bad; |
| } |
| |
| return 0; |
| |
| bad: |
| errno = EINVAL; |
| return -1; |
| } |
| |
| /*%%%*/ |
| /* send getspi message per ipsec protocol per remote address */ |
| /* |
| * the local address and remote address in ph1handle are dealed |
| * with destination address and source address respectively. |
| * Because SPI is decided by responder. |
| */ |
| int |
| pk_sendgetspi(iph2) |
| struct ph2handle *iph2; |
| { |
| struct sockaddr *src = NULL, *dst = NULL; |
| u_int satype, mode; |
| struct saprop *pp; |
| struct saproto *pr; |
| u_int32_t minspi, maxspi; |
| u_int8_t natt_type = 0; |
| u_int16_t sport = 0, dport = 0; |
| |
| if (iph2->side == INITIATOR) |
| pp = iph2->proposal; |
| else |
| pp = iph2->approval; |
| |
| if (iph2->sa_src && iph2->sa_dst) { |
| /* MIPv6: Use SA addresses, not IKE ones */ |
| src = dupsaddr(iph2->sa_src); |
| dst = dupsaddr(iph2->sa_dst); |
| } else { |
| /* Common case: SA addresses and IKE ones are the same */ |
| src = dupsaddr(iph2->src); |
| dst = dupsaddr(iph2->dst); |
| } |
| |
| if (src == NULL || dst == NULL) { |
| racoon_free(src); |
| racoon_free(dst); |
| return -1; |
| } |
| |
| for (pr = pp->head; pr != NULL; pr = pr->next) { |
| |
| /* validity check */ |
| satype = ipsecdoi2pfkey_proto(pr->proto_id); |
| if (satype == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", pr->proto_id); |
| racoon_free(src); |
| racoon_free(dst); |
| return -1; |
| } |
| /* this works around a bug in Linux kernel where it allocates 4 byte |
| spi's for IPCOMP */ |
| else if (satype == SADB_X_SATYPE_IPCOMP) { |
| minspi = 0x100; |
| maxspi = 0xffff; |
| } |
| else { |
| minspi = 0; |
| maxspi = 0; |
| } |
| mode = ipsecdoi2pfkey_mode(pr->encmode); |
| if (mode == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encmode %d\n", pr->encmode); |
| racoon_free(src); |
| racoon_free(dst); |
| return -1; |
| } |
| |
| #ifdef ENABLE_NATT |
| if (pr->udp_encap) { |
| natt_type = iph2->ph1->natt_options->encaps_type; |
| sport=extract_port(src); |
| dport=extract_port(dst); |
| } |
| #endif |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_getspi\n"); |
| if (pfkey_send_getspi_nat( |
| lcconf->sock_pfkey, |
| satype, |
| mode, |
| dst, /* src of SA */ |
| src, /* dst of SA */ |
| natt_type, |
| dport, |
| sport, |
| minspi, maxspi, |
| pr->reqid_in, iph2->seq) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "ipseclib failed send getspi (%s)\n", |
| ipsec_strerror()); |
| racoon_free(src); |
| racoon_free(dst); |
| return -1; |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "pfkey GETSPI sent: %s\n", |
| sadbsecas2str(dst, src, satype, 0, mode)); |
| } |
| |
| racoon_free(src); |
| racoon_free(dst); |
| return 0; |
| } |
| |
| /* |
| * receive GETSPI from kernel. |
| */ |
| static int |
| pk_recvgetspi(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_sa *sa; |
| struct ph2handle *iph2; |
| struct sockaddr *src, *dst; |
| int proto_id; |
| int allspiok, notfound; |
| struct saprop *pp; |
| struct saproto *pr; |
| |
| /* validity check */ |
| if (mhp[SADB_EXT_SA] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb getspi message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; |
| pk_fixup_sa_addresses(mhp); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); /* note SA dir */ |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| |
| /* the message has to be processed or not ? */ |
| if (msg->sadb_msg_pid != getpid()) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "%s message is not interesting " |
| "because pid %d is not mine.\n", |
| s_pfkey_type(msg->sadb_msg_type), |
| msg->sadb_msg_pid); |
| return -1; |
| } |
| |
| iph2 = getph2byseq(msg->sadb_msg_seq); |
| if (iph2 == NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "seq %d of %s message not interesting.\n", |
| msg->sadb_msg_seq, |
| s_pfkey_type(msg->sadb_msg_type)); |
| return -1; |
| } |
| |
| if (iph2->status != PHASE2ST_GETSPISENT) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "status mismatch (db:%d msg:%d)\n", |
| iph2->status, PHASE2ST_GETSPISENT); |
| return -1; |
| } |
| |
| /* set SPI, and check to get all spi whether or not */ |
| allspiok = 1; |
| notfound = 1; |
| proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); |
| pp = iph2->side == INITIATOR ? iph2->proposal : iph2->approval; |
| |
| for (pr = pp->head; pr != NULL; pr = pr->next) { |
| if (pr->proto_id == proto_id && pr->spi == 0) { |
| pr->spi = sa->sadb_sa_spi; |
| notfound = 0; |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "pfkey GETSPI succeeded: %s\n", |
| sadbsecas2str(dst, src, |
| msg->sadb_msg_satype, |
| sa->sadb_sa_spi, |
| ipsecdoi2pfkey_mode(pr->encmode))); |
| } |
| if (pr->spi == 0) |
| allspiok = 0; /* not get all spi */ |
| } |
| |
| if (notfound) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "get spi for unknown address %s\n", |
| saddrwop2str(dst)); |
| return -1; |
| } |
| |
| if (allspiok) { |
| /* update status */ |
| iph2->status = PHASE2ST_GETSPIDONE; |
| if (isakmp_post_getspi(iph2) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to start post getspi.\n"); |
| remph2(iph2); |
| delph2(iph2); |
| iph2 = NULL; |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * set inbound SA |
| */ |
| int |
| pk_sendupdate(iph2) |
| struct ph2handle *iph2; |
| { |
| struct saproto *pr; |
| struct pfkey_send_sa_args sa_args; |
| |
| /* sanity check */ |
| if (iph2->approval == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no approvaled SAs found.\n"); |
| return -1; |
| } |
| |
| /* fill in some needed for pfkey_send_update2 */ |
| memset (&sa_args, 0, sizeof (sa_args)); |
| sa_args.so = lcconf->sock_pfkey; |
| if (iph2->lifetime_secs) |
| sa_args.l_addtime = iph2->lifetime_secs; |
| else |
| sa_args.l_addtime = iph2->approval->lifetime; |
| sa_args.seq = iph2->seq; |
| sa_args.wsize = 4; |
| |
| if (iph2->sa_src && iph2->sa_dst) { |
| /* MIPv6: Use SA addresses, not IKE ones */ |
| sa_args.dst = dupsaddr(iph2->sa_src); |
| sa_args.src = dupsaddr(iph2->sa_dst); |
| } else { |
| /* Common case: SA addresses and IKE ones are the same */ |
| sa_args.dst = dupsaddr(iph2->src); |
| sa_args.src = dupsaddr(iph2->dst); |
| } |
| |
| if (sa_args.src == NULL || sa_args.dst == NULL) { |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { |
| /* validity check */ |
| sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id); |
| if (sa_args.satype == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", pr->proto_id); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) { |
| /* IPCOMP has no replay window */ |
| sa_args.wsize = 0; |
| } |
| #ifdef ENABLE_SAMODE_UNSPECIFIED |
| sa_args.mode = IPSEC_MODE_ANY; |
| #else |
| sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode); |
| if (sa_args.mode == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encmode %d\n", pr->encmode); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| #endif |
| /* set algorithm type and key length */ |
| sa_args.e_keylen = pr->head->encklen; |
| if (pfkey_convertfromipsecdoi( |
| pr->proto_id, |
| pr->head->trns_id, |
| pr->head->authtype, |
| &sa_args.e_type, &sa_args.e_keylen, |
| &sa_args.a_type, &sa_args.a_keylen, |
| &sa_args.flags) < 0){ |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| #if 0 |
| sa_args.l_bytes = iph2->approval->lifebyte * 1024, |
| #else |
| sa_args.l_bytes = 0; |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (*iph2->approval->sctx.ctx_str) { |
| sa_args.ctxdoi = iph2->approval->sctx.ctx_doi; |
| sa_args.ctxalg = iph2->approval->sctx.ctx_alg; |
| sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen; |
| sa_args.ctxstr = iph2->approval->sctx.ctx_str; |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| #ifdef ENABLE_NATT |
| if (pr->udp_encap) { |
| sa_args.l_natt_type = iph2->ph1->natt_options->encaps_type; |
| sa_args.l_natt_sport = extract_port(iph2->ph1->remote); |
| sa_args.l_natt_dport = extract_port(iph2->ph1->local); |
| sa_args.l_natt_oa = iph2->natoa_src; |
| #ifdef SADB_X_EXT_NAT_T_FRAG |
| sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag; |
| #endif |
| } |
| #endif |
| |
| /* more info to fill in */ |
| sa_args.spi = pr->spi; |
| sa_args.reqid = pr->reqid_in; |
| sa_args.keymat = pr->keymat->v; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_update2\n"); |
| if (pfkey_send_update2(&sa_args) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send update (%s)\n", |
| ipsec_strerror()); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| #ifndef ANDROID_PATCHED |
| if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) |
| continue; |
| |
| /* |
| * It maybe good idea to call backupsa_to_file() after |
| * racoon will receive the sadb_update messages. |
| * But it is impossible because there is not key in the |
| * information from the kernel. |
| */ |
| |
| /* change some things before backing up */ |
| sa_args.wsize = 4; |
| sa_args.l_bytes = iph2->approval->lifebyte * 1024; |
| |
| if (backupsa_to_file(&sa_args) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "backuped SA failed: %s\n", |
| sadbsecas2str(sa_args.src, sa_args.dst, |
| sa_args.satype, sa_args.spi, sa_args.mode)); |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "backuped SA: %s\n", |
| sadbsecas2str(sa_args.src, sa_args.dst, |
| sa_args.satype, sa_args.spi, sa_args.mode)); |
| #endif |
| } |
| |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return 0; |
| } |
| |
| static int |
| pk_recvupdate(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_sa *sa; |
| struct sockaddr *src, *dst; |
| struct ph2handle *iph2; |
| u_int proto_id, encmode, sa_mode; |
| int incomplete = 0; |
| struct saproto *pr; |
| |
| /* ignore this message because of local test mode. */ |
| if (f_local) |
| return 0; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_SA] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb update message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| pk_fixup_sa_addresses(mhp); |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; |
| |
| sa_mode = mhp[SADB_X_EXT_SA2] == NULL |
| ? IPSEC_MODE_ANY |
| : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; |
| |
| /* the message has to be processed or not ? */ |
| if (msg->sadb_msg_pid != getpid()) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "%s message is not interesting " |
| "because pid %d is not mine.\n", |
| s_pfkey_type(msg->sadb_msg_type), |
| msg->sadb_msg_pid); |
| return -1; |
| } |
| |
| iph2 = getph2byseq(msg->sadb_msg_seq); |
| if (iph2 == NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "seq %d of %s message not interesting.\n", |
| msg->sadb_msg_seq, |
| s_pfkey_type(msg->sadb_msg_type)); |
| return -1; |
| } |
| |
| if (iph2->status != PHASE2ST_ADDSA) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "status mismatch (db:%d msg:%d)\n", |
| iph2->status, PHASE2ST_ADDSA); |
| return -1; |
| } |
| |
| /* check to complete all keys ? */ |
| for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { |
| proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); |
| if (proto_id == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", msg->sadb_msg_satype); |
| return -1; |
| } |
| encmode = pfkey2ipsecdoi_mode(sa_mode); |
| if (encmode == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encmode %d\n", sa_mode); |
| return -1; |
| } |
| |
| if (pr->proto_id == proto_id |
| && pr->spi == sa->sadb_sa_spi) { |
| pr->ok = 1; |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "pfkey UPDATE succeeded: %s\n", |
| sadbsecas2str(dst, src, |
| msg->sadb_msg_satype, |
| sa->sadb_sa_spi, |
| sa_mode)); |
| |
| plog(LLV_INFO, LOCATION, NULL, |
| "IPsec-SA established: %s\n", |
| sadbsecas2str(dst, src, |
| msg->sadb_msg_satype, sa->sadb_sa_spi, |
| sa_mode)); |
| } |
| |
| if (pr->ok == 0) |
| incomplete = 1; |
| } |
| |
| if (incomplete) |
| return 0; |
| |
| /* turn off the timer for calling pfkey_timeover() */ |
| sched_cancel(&iph2->sce); |
| |
| /* update status */ |
| iph2->status = PHASE2ST_ESTABLISHED; |
| evt_phase2(iph2, EVT_PHASE2_UP, NULL); |
| |
| #ifdef ENABLE_STATS |
| gettimeofday(&iph2->end, NULL); |
| syslog(LOG_NOTICE, "%s(%s): %8.6f", |
| "phase2", "quick", timedelta(&iph2->start, &iph2->end)); |
| #endif |
| |
| /* turn off schedule */ |
| sched_cancel(&iph2->scr); |
| |
| /* |
| * since we are going to reuse the phase2 handler, we need to |
| * remain it and refresh all the references between ph1 and ph2 to use. |
| */ |
| sched_schedule(&iph2->sce, iph2->approval->lifetime, |
| isakmp_ph2expire_stub); |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "===\n"); |
| return 0; |
| } |
| |
| /* |
| * set outbound SA |
| */ |
| int |
| pk_sendadd(iph2) |
| struct ph2handle *iph2; |
| { |
| struct saproto *pr; |
| struct pfkey_send_sa_args sa_args; |
| |
| /* sanity check */ |
| if (iph2->approval == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no approvaled SAs found.\n"); |
| return -1; |
| } |
| |
| /* fill in some needed for pfkey_send_update2 */ |
| memset (&sa_args, 0, sizeof (sa_args)); |
| sa_args.so = lcconf->sock_pfkey; |
| if (iph2->lifetime_secs) |
| sa_args.l_addtime = iph2->lifetime_secs; |
| else |
| sa_args.l_addtime = iph2->approval->lifetime; |
| sa_args.seq = iph2->seq; |
| sa_args.wsize = 4; |
| |
| if (iph2->sa_src && iph2->sa_dst) { |
| /* MIPv6: Use SA addresses, not IKE ones */ |
| sa_args.src = dupsaddr(iph2->sa_src); |
| sa_args.dst = dupsaddr(iph2->sa_dst); |
| } else { |
| /* Common case: SA addresses and IKE ones are the same */ |
| sa_args.src = dupsaddr(iph2->src); |
| sa_args.dst = dupsaddr(iph2->dst); |
| } |
| |
| if (sa_args.src == NULL || sa_args.dst == NULL) { |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { |
| /* validity check */ |
| sa_args.satype = ipsecdoi2pfkey_proto(pr->proto_id); |
| if (sa_args.satype == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", pr->proto_id); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| else if (sa_args.satype == SADB_X_SATYPE_IPCOMP) { |
| /* no replay window for IPCOMP */ |
| sa_args.wsize = 0; |
| } |
| #ifdef ENABLE_SAMODE_UNSPECIFIED |
| sa_args.mode = IPSEC_MODE_ANY; |
| #else |
| sa_args.mode = ipsecdoi2pfkey_mode(pr->encmode); |
| if (sa_args.mode == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encmode %d\n", pr->encmode); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| #endif |
| |
| /* set algorithm type and key length */ |
| sa_args.e_keylen = pr->head->encklen; |
| if (pfkey_convertfromipsecdoi( |
| pr->proto_id, |
| pr->head->trns_id, |
| pr->head->authtype, |
| &sa_args.e_type, &sa_args.e_keylen, |
| &sa_args.a_type, &sa_args.a_keylen, |
| &sa_args.flags) < 0){ |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| #if 0 |
| sa_args.l_bytes = iph2->approval->lifebyte * 1024, |
| #else |
| sa_args.l_bytes = 0; |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (*iph2->approval->sctx.ctx_str) { |
| sa_args.ctxdoi = iph2->approval->sctx.ctx_doi; |
| sa_args.ctxalg = iph2->approval->sctx.ctx_alg; |
| sa_args.ctxstrlen = iph2->approval->sctx.ctx_strlen; |
| sa_args.ctxstr = iph2->approval->sctx.ctx_str; |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| #ifdef ENABLE_NATT |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2 " |
| "(NAT flavor)\n"); |
| |
| if (pr->udp_encap) { |
| sa_args.l_natt_type = UDP_ENCAP_ESPINUDP; |
| sa_args.l_natt_sport = extract_port(iph2->ph1->local); |
| sa_args.l_natt_dport = extract_port(iph2->ph1->remote); |
| sa_args.l_natt_oa = iph2->natoa_dst; |
| #ifdef SADB_X_EXT_NAT_T_FRAG |
| sa_args.l_natt_frag = iph2->ph1->rmconf->esp_frag; |
| #endif |
| } |
| #endif |
| /* more info to fill in */ |
| sa_args.spi = pr->spi_p; |
| sa_args.reqid = pr->reqid_out; |
| sa_args.keymat = pr->keymat_p->v; |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_add2\n"); |
| if (pfkey_send_add2(&sa_args) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send add (%s)\n", |
| ipsec_strerror()); |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return -1; |
| } |
| |
| #ifndef ANDROID_PATCHED |
| if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) |
| continue; |
| |
| /* |
| * It maybe good idea to call backupsa_to_file() after |
| * racoon will receive the sadb_update messages. |
| * But it is impossible because there is not key in the |
| * information from the kernel. |
| */ |
| if (backupsa_to_file(&sa_args) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "backuped SA failed: %s\n", |
| sadbsecas2str(sa_args.src, sa_args.dst, |
| sa_args.satype, sa_args.spi, sa_args.mode)); |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "backuped SA: %s\n", |
| sadbsecas2str(sa_args.src, sa_args.dst, |
| sa_args.satype, sa_args.spi, sa_args.mode)); |
| #endif |
| } |
| racoon_free(sa_args.src); |
| racoon_free(sa_args.dst); |
| return 0; |
| } |
| |
| static int |
| pk_recvadd(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_sa *sa; |
| struct sockaddr *src, *dst; |
| struct ph2handle *iph2; |
| u_int sa_mode; |
| |
| /* ignore this message because of local test mode. */ |
| if (f_local) |
| return 0; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_SA] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb add message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| pk_fixup_sa_addresses(mhp); |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; |
| |
| sa_mode = mhp[SADB_X_EXT_SA2] == NULL |
| ? IPSEC_MODE_ANY |
| : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; |
| |
| /* the message has to be processed or not ? */ |
| if (msg->sadb_msg_pid != getpid()) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "%s message is not interesting " |
| "because pid %d is not mine.\n", |
| s_pfkey_type(msg->sadb_msg_type), |
| msg->sadb_msg_pid); |
| return -1; |
| } |
| |
| iph2 = getph2byseq(msg->sadb_msg_seq); |
| if (iph2 == NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "seq %d of %s message not interesting.\n", |
| msg->sadb_msg_seq, |
| s_pfkey_type(msg->sadb_msg_type)); |
| return -1; |
| } |
| |
| /* |
| * NOTE don't update any status of phase2 handle |
| * because they must be updated by SADB_UPDATE message |
| */ |
| |
| plog(LLV_INFO, LOCATION, NULL, |
| "IPsec-SA established: %s\n", |
| sadbsecas2str(src, dst, |
| msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode)); |
| |
| plog(LLV_DEBUG, LOCATION, NULL, "===\n"); |
| return 0; |
| } |
| |
| static int |
| pk_recvexpire(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_sa *sa; |
| struct sockaddr *src, *dst; |
| struct ph2handle *iph2; |
| u_int proto_id, sa_mode; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_SA] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || (mhp[SADB_EXT_LIFETIME_HARD] != NULL |
| && mhp[SADB_EXT_LIFETIME_SOFT] != NULL)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb expire message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; |
| pk_fixup_sa_addresses(mhp); |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| |
| sa_mode = mhp[SADB_X_EXT_SA2] == NULL |
| ? IPSEC_MODE_ANY |
| : ((struct sadb_x_sa2 *)mhp[SADB_X_EXT_SA2])->sadb_x_sa2_mode; |
| |
| proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); |
| if (proto_id == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", msg->sadb_msg_satype); |
| return -1; |
| } |
| |
| plog(LLV_INFO, LOCATION, NULL, |
| "IPsec-SA expired: %s\n", |
| sadbsecas2str(src, dst, |
| msg->sadb_msg_satype, sa->sadb_sa_spi, sa_mode)); |
| |
| iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); |
| if (iph2 == NULL) { |
| /* |
| * Ignore it because two expire messages are come up. |
| * phase2 handler has been deleted already when 2nd message |
| * is received. |
| */ |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "no such a SA found: %s\n", |
| sadbsecas2str(src, dst, |
| msg->sadb_msg_satype, sa->sadb_sa_spi, |
| sa_mode)); |
| return 0; |
| } |
| |
| /* resent expiry message? */ |
| if (iph2->status > PHASE2ST_ESTABLISHED) |
| return 0; |
| |
| /* still negotiating? */ |
| if (iph2->status < PHASE2ST_ESTABLISHED) { |
| /* not a hard timeout? */ |
| if (mhp[SADB_EXT_LIFETIME_HARD] == NULL) |
| return 0; |
| |
| /* |
| * We were negotiating for that SA (w/o much success |
| * from current status) and kernel has decided our time |
| * is over trying (xfrm_larval_drop controls that and |
| * is enabled by default on Linux >= 2.6.28 kernels). |
| */ |
| plog(LLV_WARNING, LOCATION, NULL, |
| "PF_KEY EXPIRE message received from kernel for SA" |
| " being negotiated. Stopping negotiation.\n"); |
| } |
| |
| /* turn off the timer for calling isakmp_ph2expire() */ |
| sched_cancel(&iph2->sce); |
| |
| if (iph2->status == PHASE2ST_ESTABLISHED && |
| iph2->side == INITIATOR) { |
| struct ph1handle *iph1hint; |
| /* |
| * Active phase 2 expired and we were initiator. |
| * Begin new phase 2 exchange, so we can keep on sending |
| * traffic. |
| */ |
| |
| /* update status for re-use */ |
| iph1hint = iph2->ph1; |
| initph2(iph2); |
| iph2->status = PHASE2ST_STATUS2; |
| |
| /* start quick exchange */ |
| if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) { |
| plog(LLV_ERROR, LOCATION, iph2->dst, |
| "failed to begin ipsec sa " |
| "re-negotication.\n"); |
| remph2(iph2); |
| delph2(iph2); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * We are responder or the phase 2 was not established. |
| * Just remove the ph2handle to reflect SADB. |
| */ |
| iph2->status = PHASE2ST_EXPIRED; |
| remph2(iph2); |
| delph2(iph2); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvacquire(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_x_policy *xpl; |
| struct secpolicy *sp_out = NULL, *sp_in = NULL; |
| struct ph2handle *iph2; |
| struct sockaddr *src, *dst; /* IKE addresses (for exchanges) */ |
| struct sockaddr *sp_src, *sp_dst; /* SP addresses (selectors). */ |
| struct sockaddr *sa_src = NULL, *sa_dst = NULL ; /* SA addresses */ |
| #ifdef HAVE_SECCTX |
| struct sadb_x_sec_ctx *m_sec_ctx; |
| #endif /* HAVE_SECCTX */ |
| struct policyindex spidx; |
| |
| /* ignore this message because of local test mode. */ |
| if (f_local) |
| return 0; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb acquire message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| /* acquire does not have nat-t ports; so do not bother setting |
| * the default port 500; just use the port zero for wildcard |
| * matching the get a valid natted destination */ |
| sp_src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| sp_dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| |
| #ifdef HAVE_SECCTX |
| m_sec_ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| |
| if (m_sec_ctx != NULL) { |
| plog(LLV_INFO, LOCATION, NULL, "security context doi: %u\n", |
| m_sec_ctx->sadb_x_ctx_doi); |
| plog(LLV_INFO, LOCATION, NULL, |
| "security context algorithm: %u\n", |
| m_sec_ctx->sadb_x_ctx_alg); |
| plog(LLV_INFO, LOCATION, NULL, "security context length: %u\n", |
| m_sec_ctx->sadb_x_ctx_len); |
| plog(LLV_INFO, LOCATION, NULL, "security context: %s\n", |
| ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx))); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| /* ignore if type is not IPSEC_POLICY_IPSEC */ |
| if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "ignore ACQUIRE message. type is not IPsec.\n"); |
| return 0; |
| } |
| |
| /* ignore it if src or dst are multicast addresses. */ |
| if ((sp_dst->sa_family == AF_INET |
| && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_dst)->sin_addr.s_addr))) |
| #ifdef INET6 |
| || (sp_dst->sa_family == AF_INET6 |
| && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_dst)->sin6_addr)) |
| #endif |
| ) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "ignore due to multicast destination address: %s.\n", |
| saddrwop2str(sp_dst)); |
| return 0; |
| } |
| |
| if ((sp_src->sa_family == AF_INET |
| && IN_MULTICAST(ntohl(((struct sockaddr_in *)sp_src)->sin_addr.s_addr))) |
| #ifdef INET6 |
| || (sp_src->sa_family == AF_INET6 |
| && IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)sp_src)->sin6_addr)) |
| #endif |
| ) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "ignore due to multicast source address: %s.\n", |
| saddrwop2str(sp_src)); |
| return 0; |
| } |
| |
| /* search for proper policyindex */ |
| sp_out = getspbyspid(xpl->sadb_x_policy_id); |
| if (sp_out == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "no policy found: id:%d.\n", |
| xpl->sadb_x_policy_id); |
| return -1; |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "suitable outbound SP found: %s.\n", spidx2str(&sp_out->spidx)); |
| |
| /* Before going further, let first get the source and destination |
| * address that would be used for IKE negotiation. The logic is: |
| * - if SP from SPD image contains local and remote hints, we |
| * use them (provided by MIGRATE). |
| * - otherwise, we use the ones from the ipsecrequest, which means: |
| * - the addresses from the request for transport mode |
| * - the endpoints addresses for tunnel mode |
| * |
| * Note that: |
| * 1) racoon does not support negotiation of bundles which |
| * simplifies the lookup for the addresses in the ipsecrequest |
| * list, as we expect only one. |
| * 2) We do source and destination parts all together and do not |
| * accept semi-defined information. This is just a decision, |
| * there might be needs. |
| * |
| * --arno |
| */ |
| if (sp_out->req && sp_out->req->saidx.mode == IPSEC_MODE_TUNNEL) { |
| /* For Tunnel mode, SA addresses are the endpoints */ |
| src = (struct sockaddr *) &sp_out->req->saidx.src; |
| dst = (struct sockaddr *) &sp_out->req->saidx.dst; |
| } else { |
| /* Otherwise use requested addresses. |
| * |
| * We need to explicitly setup sa_src and sa_dst too, |
| * since the SA ports are different from IKE port. And |
| * src/dst ports will be overwritten when the matching |
| * phase1 is found. */ |
| src = sa_src = sp_src; |
| dst = sa_dst = sp_dst; |
| } |
| if (sp_out->local && sp_out->remote) { |
| /* hints available, let's use them */ |
| sa_src = src; |
| sa_dst = dst; |
| src = (struct sockaddr *) sp_out->local; |
| dst = (struct sockaddr *) sp_out->remote; |
| } |
| |
| /* |
| * If there is a phase 2 handler against the policy identifier in |
| * the acquire message, and if |
| * 1. its state is less than PHASE2ST_ESTABLISHED, then racoon |
| * should ignore such a acquire message because the phase 2 |
| * is just negotiating. |
| * 2. its state is equal to PHASE2ST_ESTABLISHED, then racoon |
| * has to prcesss such a acquire message because racoon may |
| * lost the expire message. |
| */ |
| iph2 = getph2byid(src, dst, xpl->sadb_x_policy_id); |
| if (iph2 != NULL) { |
| if (iph2->status < PHASE2ST_ESTABLISHED) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "ignore the acquire because ph2 found\n"); |
| return -1; |
| } |
| if (iph2->status == PHASE2ST_EXPIRED) |
| iph2 = NULL; |
| /*FALLTHROUGH*/ |
| } |
| |
| /* Check we are listening on source address. If not, ignore. */ |
| if (myaddr_getsport(src) == -1) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "Not listening on source address %s. Ignoring ACQUIRE.\n", |
| saddrwop2str(src)); |
| return 0; |
| } |
| |
| /* get inbound policy */ |
| { |
| |
| memset(&spidx, 0, sizeof(spidx)); |
| spidx.dir = IPSEC_DIR_INBOUND; |
| memcpy(&spidx.src, &sp_out->spidx.dst, sizeof(spidx.src)); |
| memcpy(&spidx.dst, &sp_out->spidx.src, sizeof(spidx.dst)); |
| spidx.prefs = sp_out->spidx.prefd; |
| spidx.prefd = sp_out->spidx.prefs; |
| spidx.ul_proto = sp_out->spidx.ul_proto; |
| |
| #ifdef HAVE_SECCTX |
| if (m_sec_ctx) { |
| spidx.sec_ctx.ctx_doi = m_sec_ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_alg = m_sec_ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_strlen = m_sec_ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, |
| ((char *)m_sec_ctx + sizeof(struct sadb_x_sec_ctx)), |
| spidx.sec_ctx.ctx_strlen); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp_in = getsp(&spidx); |
| if (sp_in) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "suitable inbound SP found: %s.\n", |
| spidx2str(&sp_in->spidx)); |
| } else { |
| plog(LLV_NOTIFY, LOCATION, NULL, |
| "no in-bound policy found: %s\n", |
| spidx2str(&spidx)); |
| } |
| } |
| |
| /* allocate a phase 2 */ |
| iph2 = newph2(); |
| if (iph2 == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to allocate phase2 entry.\n"); |
| return -1; |
| } |
| iph2->side = INITIATOR; |
| iph2->spid = xpl->sadb_x_policy_id; |
| iph2->satype = msg->sadb_msg_satype; |
| iph2->seq = msg->sadb_msg_seq; |
| iph2->status = PHASE2ST_STATUS2; |
| |
| /* set address used by IKE for the negotiation (might differ from |
| * SA address, i.e. might not be tunnel endpoints or addresses |
| * of transport mode SA) */ |
| iph2->dst = dupsaddr(dst); |
| if (iph2->dst == NULL) { |
| delph2(iph2); |
| return -1; |
| } |
| iph2->src = dupsaddr(src); |
| if (iph2->src == NULL) { |
| delph2(iph2); |
| return -1; |
| } |
| |
| /* If sa_src and sa_dst have been set, this mean we have to |
| * set iph2->sa_src and iph2->sa_dst to provide the addresses |
| * of the SA because iph2->src and iph2->dst are only the ones |
| * used for the IKE exchanges. Those that need these addresses |
| * are for instance pk_sendupdate() or pk_sendgetspi() */ |
| if (sa_src) { |
| iph2->sa_src = dupsaddr(sa_src); |
| iph2->sa_dst = dupsaddr(sa_dst); |
| } |
| |
| if (isakmp_get_sainfo(iph2, sp_out, sp_in) < 0) { |
| delph2(iph2); |
| return -1; |
| } |
| |
| #ifdef HAVE_SECCTX |
| if (m_sec_ctx) { |
| set_secctx_in_proposal(iph2, spidx); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| insph2(iph2); |
| |
| /* start isakmp initiation by using ident exchange */ |
| /* XXX should be looped if there are multiple phase 2 handler. */ |
| if (isakmp_post_acquire(iph2, NULL, TRUE) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to begin ipsec sa negotication.\n"); |
| remph2(iph2); |
| delph2(iph2); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvdelete(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_sa *sa; |
| struct sockaddr *src, *dst; |
| struct ph2handle *iph2 = NULL; |
| u_int proto_id; |
| |
| /* ignore this message because of local test mode. */ |
| if (f_local) |
| return 0; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_SA] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb delete message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; |
| pk_fixup_sa_addresses(mhp); |
| src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); |
| dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); |
| |
| /* the message has to be processed or not ? */ |
| if (msg->sadb_msg_pid == getpid()) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "%s message is not interesting " |
| "because the message was originated by me.\n", |
| s_pfkey_type(msg->sadb_msg_type)); |
| return -1; |
| } |
| |
| proto_id = pfkey2ipsecdoi_proto(msg->sadb_msg_satype); |
| if (proto_id == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", msg->sadb_msg_satype); |
| return -1; |
| } |
| |
| iph2 = getph2bysaidx(src, dst, proto_id, sa->sadb_sa_spi); |
| if (iph2 == NULL) { |
| /* ignore */ |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no iph2 found: %s\n", |
| sadbsecas2str(src, dst, msg->sadb_msg_satype, |
| sa->sadb_sa_spi, IPSEC_MODE_ANY)); |
| return 0; |
| } |
| |
| plog(LLV_ERROR, LOCATION, NULL, |
| "pfkey DELETE received: %s\n", |
| sadbsecas2str(src, dst, |
| msg->sadb_msg_satype, sa->sadb_sa_spi, IPSEC_MODE_ANY)); |
| |
| /* send delete information */ |
| if (iph2->status == PHASE2ST_ESTABLISHED) |
| isakmp_info_send_d2(iph2); |
| |
| remph2(iph2); |
| delph2(iph2); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvflush(mhp) |
| caddr_t *mhp; |
| { |
| /* ignore this message because of local test mode. */ |
| if (f_local) |
| return 0; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb flush message passed.\n"); |
| return -1; |
| } |
| |
| flushph2(); |
| |
| return 0; |
| } |
| |
| static int |
| getsadbpolicy(policy0, policylen0, type, iph2) |
| caddr_t *policy0; |
| int *policylen0, type; |
| struct ph2handle *iph2; |
| { |
| struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; |
| struct sockaddr *src = NULL, *dst = NULL; |
| struct sadb_x_policy *xpl; |
| struct sadb_x_ipsecrequest *xisr; |
| struct saproto *pr; |
| struct saproto **pr_rlist; |
| int rlist_len = 0; |
| caddr_t policy, p; |
| int policylen; |
| int xisrlen; |
| u_int satype, mode; |
| int len = 0; |
| #ifdef HAVE_SECCTX |
| int ctxlen = 0; |
| #endif /* HAVE_SECCTX */ |
| |
| |
| /* get policy buffer size */ |
| policylen = sizeof(struct sadb_x_policy); |
| if (type != SADB_X_SPDDELETE) { |
| if (iph2->sa_src && iph2->sa_dst) { |
| src = iph2->sa_src; /* MIPv6: Use SA addresses, */ |
| dst = iph2->sa_dst; /* not IKE ones */ |
| } else { |
| src = iph2->src; /* Common case: SA addresses */ |
| dst = iph2->dst; /* and IKE ones are the same */ |
| } |
| |
| for (pr = iph2->approval->head; pr; pr = pr->next) { |
| xisrlen = sizeof(*xisr); |
| if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) { |
| xisrlen += (sysdep_sa_len(src) + |
| sysdep_sa_len(dst)); |
| } |
| |
| policylen += PFKEY_ALIGN8(xisrlen); |
| } |
| } |
| |
| #ifdef HAVE_SECCTX |
| if (*spidx->sec_ctx.ctx_str) { |
| ctxlen = sizeof(struct sadb_x_sec_ctx) |
| + PFKEY_ALIGN8(spidx->sec_ctx.ctx_strlen); |
| policylen += ctxlen; |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| /* make policy structure */ |
| policy = racoon_malloc(policylen); |
| memset((void*)policy, 0xcd, policylen); |
| if (!policy) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "buffer allocation failed.\n"); |
| return -1; |
| } |
| |
| xpl = (struct sadb_x_policy *)policy; |
| xpl->sadb_x_policy_len = PFKEY_UNIT64(policylen); |
| xpl->sadb_x_policy_exttype = SADB_X_EXT_POLICY; |
| xpl->sadb_x_policy_type = IPSEC_POLICY_IPSEC; |
| xpl->sadb_x_policy_dir = spidx->dir; |
| xpl->sadb_x_policy_id = 0; |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| xpl->sadb_x_policy_priority = PRIORITY_DEFAULT; |
| #endif |
| len++; |
| |
| #ifdef HAVE_SECCTX |
| if (*spidx->sec_ctx.ctx_str) { |
| struct sadb_x_sec_ctx *p; |
| |
| p = (struct sadb_x_sec_ctx *)(xpl + len); |
| memset(p, 0, ctxlen); |
| p->sadb_x_sec_len = PFKEY_UNIT64(ctxlen); |
| p->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX; |
| p->sadb_x_ctx_len = spidx->sec_ctx.ctx_strlen; |
| p->sadb_x_ctx_doi = spidx->sec_ctx.ctx_doi; |
| p->sadb_x_ctx_alg = spidx->sec_ctx.ctx_alg; |
| |
| memcpy(p + 1,spidx->sec_ctx.ctx_str,spidx->sec_ctx.ctx_strlen); |
| len += ctxlen; |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| /* no need to append policy information any more if type is SPDDELETE */ |
| if (type == SADB_X_SPDDELETE) |
| goto end; |
| |
| xisr = (struct sadb_x_ipsecrequest *)(xpl + len); |
| |
| /* The order of things is reversed for use in add policy messages */ |
| for (pr = iph2->approval->head; pr; pr = pr->next) rlist_len++; |
| pr_rlist = racoon_malloc((rlist_len+1)*sizeof(struct saproto*)); |
| if (!pr_rlist) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "buffer allocation failed.\n"); |
| return -1; |
| } |
| pr_rlist[rlist_len--] = NULL; |
| for (pr = iph2->approval->head; pr; pr = pr->next) pr_rlist[rlist_len--] = pr; |
| rlist_len = 0; |
| |
| for (pr = pr_rlist[rlist_len++]; pr; pr = pr_rlist[rlist_len++]) { |
| |
| satype = doi2ipproto(pr->proto_id); |
| if (satype == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto_id %d\n", pr->proto_id); |
| goto err; |
| } |
| mode = ipsecdoi2pfkey_mode(pr->encmode); |
| if (mode == ~0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid encmode %d\n", pr->encmode); |
| goto err; |
| } |
| |
| /* |
| * the policy level cannot be unique because the policy |
| * is defined later than SA, so req_id cannot be bound to SA. |
| */ |
| xisr->sadb_x_ipsecrequest_proto = satype; |
| xisr->sadb_x_ipsecrequest_mode = mode; |
| if(iph2->proposal->head->reqid_in > 0){ |
| xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_UNIQUE; |
| xisr->sadb_x_ipsecrequest_reqid = iph2->proposal->head->reqid_in; |
| }else{ |
| xisr->sadb_x_ipsecrequest_level = IPSEC_LEVEL_REQUIRE; |
| xisr->sadb_x_ipsecrequest_reqid = 0; |
| } |
| p = (caddr_t)(xisr + 1); |
| |
| xisrlen = sizeof(*xisr); |
| |
| if (pr->encmode == IPSECDOI_ATTR_ENC_MODE_TUNNEL) { |
| int src_len, dst_len; |
| |
| src_len = sysdep_sa_len(src); |
| dst_len = sysdep_sa_len(dst); |
| xisrlen += src_len + dst_len; |
| |
| memcpy(p, src, src_len); |
| p += src_len; |
| |
| memcpy(p, dst, dst_len); |
| p += dst_len; |
| } |
| |
| xisr->sadb_x_ipsecrequest_len = PFKEY_ALIGN8(xisrlen); |
| xisr = (struct sadb_x_ipsecrequest *)p; |
| |
| } |
| racoon_free(pr_rlist); |
| |
| end: |
| *policy0 = policy; |
| *policylen0 = policylen; |
| |
| return 0; |
| |
| err: |
| if (policy) |
| racoon_free(policy); |
| if (pr_rlist) racoon_free(pr_rlist); |
| |
| return -1; |
| } |
| |
| int |
| pk_sendspdupdate2(iph2) |
| struct ph2handle *iph2; |
| { |
| struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; |
| caddr_t policy = NULL; |
| int policylen = 0; |
| u_int64_t ltime, vtime; |
| |
| ltime = iph2->approval->lifetime; |
| vtime = 0; |
| |
| if (getsadbpolicy(&policy, &policylen, SADB_X_SPDUPDATE, iph2)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "getting sadb policy failed.\n"); |
| return -1; |
| } |
| |
| if (pfkey_send_spdupdate2( |
| lcconf->sock_pfkey, |
| (struct sockaddr *)&spidx->src, |
| spidx->prefs, |
| (struct sockaddr *)&spidx->dst, |
| spidx->prefd, |
| spidx->ul_proto, |
| ltime, vtime, |
| policy, policylen, 0) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send spdupdate2 (%s)\n", |
| ipsec_strerror()); |
| goto end; |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdupdate2\n"); |
| |
| end: |
| if (policy) |
| racoon_free(policy); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspdupdate(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| struct sockaddr *local=NULL, *remote=NULL; |
| u_int64_t created; |
| int ret; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spdupdate message passed.\n"); |
| return -1; |
| } |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp = getsp(&spidx); |
| if (sp == NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "this policy did not exist for removal: \"%s\"\n", |
| spidx2str(&spidx)); |
| } else { |
| /* preserve hints before deleting the SP */ |
| local = sp->local; |
| remote = sp->remote; |
| sp->local = NULL; |
| sp->remote = NULL; |
| |
| remsp(sp); |
| delsp(sp); |
| } |
| |
| /* Add new SP (with old hints) */ |
| ret = addnewsp(mhp, local, remote); |
| |
| if (local != NULL) |
| racoon_free(local); |
| if (remote != NULL) |
| racoon_free(remote); |
| |
| if (ret < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* |
| * this function has to be used by responder side. |
| */ |
| int |
| pk_sendspdadd2(iph2) |
| struct ph2handle *iph2; |
| { |
| struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; |
| caddr_t policy = NULL; |
| int policylen = 0; |
| u_int64_t ltime, vtime; |
| |
| ltime = iph2->approval->lifetime; |
| vtime = 0; |
| |
| if (getsadbpolicy(&policy, &policylen, SADB_X_SPDADD, iph2)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "getting sadb policy failed.\n"); |
| return -1; |
| } |
| |
| if (pfkey_send_spdadd2( |
| lcconf->sock_pfkey, |
| (struct sockaddr *)&spidx->src, |
| spidx->prefs, |
| (struct sockaddr *)&spidx->dst, |
| spidx->prefd, |
| spidx->ul_proto, |
| ltime, vtime, |
| policy, policylen, 0) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send spdadd2 (%s)\n", |
| ipsec_strerror()); |
| goto end; |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spdadd2\n"); |
| |
| end: |
| if (policy) |
| racoon_free(policy); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspdadd(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| struct sockaddr *local = NULL, *remote = NULL; |
| u_int64_t created; |
| int ret; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spdadd message passed.\n"); |
| return -1; |
| } |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp = getsp(&spidx); |
| if (sp != NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "such policy already exists. " |
| "anyway replace it: %s\n", |
| spidx2str(&spidx)); |
| |
| /* preserve hints before deleting the SP */ |
| local = sp->local; |
| remote = sp->remote; |
| sp->local = NULL; |
| sp->remote = NULL; |
| |
| remsp(sp); |
| delsp(sp); |
| } |
| |
| /* Add new SP (with old hints) */ |
| ret = addnewsp(mhp, local, remote); |
| |
| if (local != NULL) |
| racoon_free(local); |
| if (remote != NULL) |
| racoon_free(remote); |
| |
| if (ret < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* |
| * this function has to be used by responder side. |
| */ |
| int |
| pk_sendspddelete(iph2) |
| struct ph2handle *iph2; |
| { |
| struct policyindex *spidx = (struct policyindex *)iph2->spidx_gen; |
| caddr_t policy = NULL; |
| int policylen; |
| |
| if (getsadbpolicy(&policy, &policylen, SADB_X_SPDDELETE, iph2)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "getting sadb policy failed.\n"); |
| return -1; |
| } |
| |
| if (pfkey_send_spddelete( |
| lcconf->sock_pfkey, |
| (struct sockaddr *)&spidx->src, |
| spidx->prefs, |
| (struct sockaddr *)&spidx->dst, |
| spidx->prefd, |
| spidx->ul_proto, |
| policy, policylen, 0) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "libipsec failed send spddelete (%s)\n", |
| ipsec_strerror()); |
| goto end; |
| } |
| plog(LLV_DEBUG, LOCATION, NULL, "call pfkey_send_spddelete\n"); |
| |
| end: |
| if (policy) |
| racoon_free(policy); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspddelete(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| u_int64_t created; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spddelete message passed.\n"); |
| return -1; |
| } |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp = getsp(&spidx); |
| if (sp == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no policy found: %s\n", |
| spidx2str(&spidx)); |
| return -1; |
| } |
| |
| remsp(sp); |
| delsp(sp); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspdexpire(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| u_int64_t created; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spdexpire message passed.\n"); |
| return -1; |
| } |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp = getsp(&spidx); |
| if (sp == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "no policy found: %s\n", |
| spidx2str(&spidx)); |
| return -1; |
| } |
| |
| remsp(sp); |
| delsp(sp); |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspdget(mhp) |
| caddr_t *mhp; |
| { |
| /* sanity check */ |
| if (mhp[0] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spdget message passed.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspddump(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_msg *msg; |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| struct sockaddr *local=NULL, *remote=NULL; |
| u_int64_t created; |
| int ret; |
| |
| /* sanity check */ |
| if (mhp[0] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spddump message passed.\n"); |
| return -1; |
| } |
| msg = (struct sadb_msg *)mhp[0]; |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| if (saddr == NULL || daddr == NULL || xpl == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spddump message passed.\n"); |
| return -1; |
| } |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(spidx.sec_ctx.ctx_str, ctx + 1, ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| sp = getsp(&spidx); |
| if (sp != NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "such policy already exists. " |
| "anyway replace it: %s\n", |
| spidx2str(&spidx)); |
| |
| /* preserve hints before deleting the SP */ |
| local = sp->local; |
| remote = sp->remote; |
| sp->local = NULL; |
| sp->remote = NULL; |
| |
| remsp(sp); |
| delsp(sp); |
| } |
| |
| /* Add new SP (with old hints) */ |
| ret = addnewsp(mhp, local, remote); |
| |
| if (local != NULL) |
| racoon_free(local); |
| if (remote != NULL) |
| racoon_free(remote); |
| |
| if (ret < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| pk_recvspdflush(mhp) |
| caddr_t *mhp; |
| { |
| /* sanity check */ |
| if (mhp[0] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spdflush message passed.\n"); |
| return -1; |
| } |
| |
| flushsp(); |
| |
| return 0; |
| } |
| |
| #if defined(SADB_X_MIGRATE) && defined(SADB_X_EXT_KMADDRESS) |
| |
| /* MIGRATE support (pk_recvmigrate() is the handler of MIGRATE message). |
| * |
| * pk_recvmigrate() |
| * 1) some preprocessing and checks |
| * 2) parsing of sadb_x_kmaddress extension |
| * 3) SP lookup using selectors and content of policy extension from MIGRATE |
| * 4) resolution of current local and remote IKE addresses |
| * 5) Use of addresses to get Phase 1 handler if any |
| * 6) Update of IKE addresses in Phase 1 (iph1->local and iph1->remote) |
| * 7) Update of IKE addresses in Phase 2 (iph2->src and iph2->dst) |
| * 8) Update of IKE addresses in SP (sp->local and sp->remote) |
| * 9) Loop on sadb_x_ipsecrequests pairs from MIGRATE |
| * - update of associated ipsecrequests entries in sp->req (should be |
| * only one as racoon does not support bundles), i.e. update of |
| * tunnel endpoints when required. |
| * - If tunnel mode endpoints have been updated, lookup of associated |
| * Phase 2 handle to also update sa_src and sa_dst entries |
| * |
| * XXX Note that we do not support yet the update of SA addresses for transport |
| * mode, but only the update of SA addresses for tunnel mode (endpoints). |
| * Reasons are: |
| * - there is no initial need for MIPv6 |
| * - racoon does not support bundles |
| * - this would imply more work to deal with sainfo update (if feasible). |
| */ |
| |
| /* Generic argument structure for migration callbacks */ |
| struct migrate_args { |
| struct sockaddr *local; |
| struct sockaddr *remote; |
| }; |
| |
| /* |
| * Update local and remote addresses of given Phase 1. Schedule removal |
| * if negotiation was going on and restart a one from updated address. |
| * |
| * -1 is returned on error. 0 if everything went right. |
| */ |
| static int |
| migrate_ph1_ike_addresses(iph1, arg) |
| struct ph1handle *iph1; |
| void *arg; |
| { |
| struct migrate_args *ma = (struct migrate_args *) arg; |
| struct remoteconf *rmconf; |
| u_int16_t port; |
| |
| /* Already up-to-date? */ |
| if (cmpsaddr(iph1->local, ma->local) == CMPSADDR_MATCH && |
| cmpsaddr(iph1->remote, ma->remote) == CMPSADDR_MATCH) |
| return 0; |
| |
| if (iph1->status < PHASE1ST_ESTABLISHED) { |
| /* Bad luck! We received a MIGRATE *while* negotiating |
| * Phase 1 (i.e. it was not established yet). If we act as |
| * initiator we need to restart the negotiation. As |
| * responder, our best bet is to update our addresses |
| * and wait for the initiator to do something */ |
| plog(LLV_WARNING, LOCATION, NULL, "MIGRATE received *during* " |
| "Phase 1 negotiation (%s).\n", |
| saddr2str_fromto("%s => %s", ma->local, ma->remote)); |
| |
| /* If we are not acting as initiator, let's just leave and |
| * let the remote peer handle the restart */ |
| rmconf = getrmconf(ma->remote, 0); |
| if (rmconf == NULL || !rmconf->passive) { |
| iph1->status = PHASE1ST_EXPIRED; |
| isakmp_ph1delete(iph1); |
| |
| /* This is unlikely, but let's just check if a Phase 1 |
| * for the new addresses already exist */ |
| if (getph1byaddr(ma->local, ma->remote, 0)) { |
| plog(LLV_WARNING, LOCATION, NULL, "No need " |
| "to start a new Phase 1 negotiation. One " |
| "already exists.\n"); |
| return 0; |
| } |
| |
| plog(LLV_WARNING, LOCATION, NULL, "As initiator, " |
| "restarting it.\n"); |
| /* Note that the insertion of the new Phase 1 will not |
| * interfere with the fact we are called from enumph1, |
| * because it is inserted as first element. --arno */ |
| isakmp_ph1begin_i(rmconf, ma->local, ma->remote); |
| |
| return 0; |
| } |
| } |
| |
| if (iph1->local != NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 local " |
| "address from %s\n", |
| saddr2str_fromto("%s to %s", iph1->local, ma->local)); |
| port = extract_port(iph1->local); |
| racoon_free(iph1->local); |
| } else |
| port = 0; |
| |
| iph1->local = dupsaddr(ma->local); |
| if (iph1->local == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " |
| "Phase 1 local address.\n"); |
| return -1; |
| } |
| set_port(iph1->local, port); |
| |
| if (iph1->remote != NULL) { |
| plog(LLV_DEBUG, LOCATION, NULL, "Migrating Phase 1 remote " |
| "address from %s\n", |
| saddr2str_fromto("%s to %s", iph1->remote, ma->remote)); |
| port = extract_port(iph1->remote); |
| racoon_free(iph1->remote); |
| } else |
| port = 0; |
| |
| iph1->remote = dupsaddr(ma->remote); |
| if (iph1->remote == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " |
| "Phase 1 remote address.\n"); |
| return -1; |
| } |
| set_port(iph1->remote, port); |
| |
| return 0; |
| } |
| |
| /* Update src and dst of all current Phase 2 handles. |
| * with provided local and remote addresses. |
| * Our intent is NOT to modify IPsec SA endpoints but IKE |
| * addresses so we need to take care to separate those if |
| * they are different. -1 is returned on error. 0 if everything |
| * went right. |
| * |
| * Note: we do not maintain port information as it is not |
| * expected to be meaningful --arno |
| */ |
| static int |
| migrate_ph2_ike_addresses(iph2, arg) |
| struct ph2handle *iph2; |
| void *arg; |
| { |
| struct migrate_args *ma = (struct migrate_args *) arg; |
| struct ph1handle *iph1; |
| |
| /* If Phase 2 has an associated Phase 1, migrate addresses */ |
| if (iph2->ph1) |
| migrate_ph1_ike_addresses(iph2->ph1, arg); |
| |
| /* Already up-to-date? */ |
| if (cmpsaddr(iph2->src, ma->local) == CMPSADDR_MATCH && |
| cmpsaddr(iph2->dst, ma->remote) == CMPSADDR_MATCH) |
| return 0; |
| |
| /* save src/dst as sa_src/sa_dst before rewriting */ |
| if (iph2->sa_src == NULL && iph2->sa_dst == NULL) { |
| iph2->sa_src = iph2->src; |
| iph2->sa_dst = iph2->dst; |
| iph2->src = NULL; |
| iph2->dst = NULL; |
| } |
| |
| if (iph2->src != NULL) |
| racoon_free(iph2->src); |
| iph2->src = dupsaddr(ma->local); |
| if (iph2->src == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unable to allocate Phase 2 src address.\n"); |
| return -1; |
| } |
| |
| if (iph2->dst != NULL) |
| racoon_free(iph2->dst); |
| iph2->dst = dupsaddr(ma->remote); |
| if (iph2->dst == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unable to allocate Phase 2 dst address.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Consider existing Phase 2 handles with given spid and update their source |
| * and destination addresses for SA. As racoon does not support bundles, if |
| * we modify multiple occurrences, this probably imply rekeying has happened. |
| * |
| * Both addresses passed to the function are expected not to be NULL and of |
| * same family. -1 is returned on error. 0 if everything went right. |
| * |
| * Specific care is needed to support Phase 2 for which negotiation has |
| * already started but are which not yet established. |
| */ |
| static int |
| migrate_ph2_sa_addresses(iph2, args) |
| struct ph2handle *iph2; |
| void *args; |
| { |
| struct migrate_args *ma = (struct migrate_args *) args; |
| |
| if (iph2->sa_src != NULL) { |
| racoon_free(iph2->sa_src); |
| iph2->sa_src = NULL; |
| } |
| |
| if (iph2->sa_dst != NULL) { |
| racoon_free(iph2->sa_dst); |
| iph2->sa_dst = NULL; |
| } |
| |
| iph2->sa_src = dupsaddr(ma->local); |
| if (iph2->sa_src == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unable to allocate Phase 2 sa_src address.\n"); |
| return -1; |
| } |
| |
| iph2->sa_dst = dupsaddr(ma->remote); |
| if (iph2->sa_dst == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "unable to allocate Phase 2 sa_dst address.\n"); |
| return -1; |
| } |
| |
| if (iph2->status < PHASE2ST_ESTABLISHED) { |
| struct remoteconf *rmconf; |
| /* We were negotiating for that SA when we received the MIGRATE. |
| * We cannot simply update the addresses and let the exchange |
| * go on. We have to restart the whole negotiation if we are |
| * the initiator. Otherwise (acting as responder), we just need |
| * to delete our ph2handle and wait for the initiator to start |
| * a new negotiation. */ |
| |
| if (iph2->ph1 && iph2->ph1->rmconf) |
| rmconf = iph2->ph1->rmconf; |
| else |
| rmconf = getrmconf(iph2->dst, 0); |
| |
| if (rmconf && !rmconf->passive) { |
| struct ph1handle *iph1hint; |
| |
| plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received " |
| "*during* IPsec SA negotiation. As initiator, " |
| "restarting it.\n"); |
| |
| /* Turn off expiration timer ...*/ |
| sched_cancel(&iph2->sce); |
| iph2->status = PHASE2ST_EXPIRED; |
| |
| /* ... clean Phase 2 handle ... */ |
| iph1hint = iph2->ph1; |
| initph2(iph2); |
| iph2->status = PHASE2ST_STATUS2; |
| |
| /* and start a new negotiation */ |
| if (isakmp_post_acquire(iph2, iph1hint, FALSE) < 0) { |
| plog(LLV_ERROR, LOCATION, iph2->dst, "failed " |
| "to begin IPsec SA renegotiation after " |
| "MIGRATE reception.\n"); |
| remph2(iph2); |
| delph2(iph2); |
| return -1; |
| } |
| } else { |
| plog(LLV_WARNING, LOCATION, iph2->dst, "MIGRATE received " |
| "*during* IPsec SA negotiation. As responder, let's" |
| "wait for the initiator to act.\n"); |
| |
| /* Simply schedule deletion */ |
| isakmp_ph2expire(iph2); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Update SP hints (local and remote addresses) for future IKE |
| * negotiations of SA associated with that SP. -1 is returned |
| * on error. 0 if everything went right. |
| * |
| * Note: we do not maintain port information as it is not |
| * expected to be meaningful --arno |
| */ |
| static int |
| migrate_sp_ike_addresses(sp, local, remote) |
| struct secpolicy *sp; |
| struct sockaddr *local, *remote; |
| { |
| if (sp == NULL || local == NULL || remote == NULL) |
| return -1; |
| |
| if (sp->local != NULL) |
| racoon_free(sp->local); |
| |
| sp->local = dupsaddr(local); |
| if (sp->local == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " |
| "local hint for SP.\n"); |
| return -1; |
| } |
| |
| if (sp->remote != NULL) |
| racoon_free(sp->remote); |
| |
| sp->remote = dupsaddr(remote); |
| if (sp->remote == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "unable to allocate " |
| "remote hint for SP.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Given current ipsecrequest (isr_cur) to be migrated in considered |
| tree, the function first checks that it matches the expected one |
| (xisr_old) provided in MIGRATE message and then updates the addresses |
| if it is tunnel mode (with content of xisr_new). Various other checks |
| are performed. For transport mode, structures are not modified, only |
| the checks are done. -1 is returned on error. */ |
| static int |
| migrate_ph2_one_isr(spid, isr_cur, xisr_old, xisr_new) |
| u_int32_t spid; |
| struct ipsecrequest *isr_cur; |
| struct sadb_x_ipsecrequest *xisr_old, *xisr_new; |
| { |
| struct secasindex *saidx = &isr_cur->saidx; |
| struct sockaddr *osaddr, *odaddr, *nsaddr, *ndaddr; |
| struct ph2selector ph2sel; |
| struct migrate_args ma; |
| |
| /* First, check that mode and proto do match */ |
| if (xisr_old->sadb_x_ipsecrequest_proto != saidx->proto || |
| xisr_old->sadb_x_ipsecrequest_mode != saidx->mode || |
| xisr_new->sadb_x_ipsecrequest_proto != saidx->proto || |
| xisr_new->sadb_x_ipsecrequest_mode != saidx->mode) |
| return -1; |
| |
| /* Then, verify reqid if necessary */ |
| if (isr_cur->saidx.reqid && |
| (xisr_old->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE || |
| xisr_new->sadb_x_ipsecrequest_reqid != IPSEC_LEVEL_UNIQUE || |
| isr_cur->saidx.reqid != xisr_old->sadb_x_ipsecrequest_reqid || |
| isr_cur->saidx.reqid != xisr_new->sadb_x_ipsecrequest_reqid)) |
| return -1; |
| |
| /* If not tunnel mode, our work is over */ |
| if (saidx->mode != IPSEC_MODE_TUNNEL) { |
| plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "non tunnel mode isr, skipping SA address migration.\n"); |
| return 0; |
| } |
| |
| /* Tunnel mode: let's check addresses do match and then update them. */ |
| osaddr = (struct sockaddr *)(xisr_old + 1); |
| odaddr = (struct sockaddr *)(((u_int8_t *)osaddr) + sysdep_sa_len(osaddr)); |
| nsaddr = (struct sockaddr *)(xisr_new + 1); |
| ndaddr = (struct sockaddr *)(((u_int8_t *)nsaddr) + sysdep_sa_len(nsaddr)); |
| |
| /* Check family does match */ |
| if (osaddr->sa_family != odaddr->sa_family || |
| nsaddr->sa_family != ndaddr->sa_family) |
| return -1; |
| |
| /* Check family does match */ |
| if (saidx->src.ss_family != osaddr->sa_family) |
| return -1; |
| |
| /* We log IPv4 to IPv6 and IPv6 to IPv4 switches */ |
| if (nsaddr->sa_family != osaddr->sa_family) |
| plog(LLV_INFO, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "changing address families (%d to %d) for endpoints.\n", |
| osaddr->sa_family, nsaddr->sa_family); |
| |
| if (cmpsaddr(osaddr, (struct sockaddr *) &saidx->src) != CMPSADDR_MATCH || |
| cmpsaddr(odaddr, (struct sockaddr *) &saidx->dst) != CMPSADDR_MATCH) { |
| plog(LLV_DEBUG, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "mismatch of addresses in saidx and xisr.\n"); |
| return -1; |
| } |
| |
| /* Excellent. Let's grab associated Phase 2 handle (if any) |
| * and update its sa_src and sa_dst entries. Note that we |
| * make the assumption that racoon does not support bundles |
| * and make the lookup using spid: we blindly update |
| * sa_src and sa_dst for _all_ found Phase 2 handles */ |
| memset(&ph2sel, 0, sizeof(ph2sel)); |
| ph2sel.spid = spid; |
| |
| memset(&ma, 0, sizeof(ma)); |
| ma.local = nsaddr; |
| ma.remote = ndaddr; |
| |
| if (enumph2(&ph2sel, migrate_ph2_sa_addresses, &ma) < 0) |
| return -1; |
| |
| /* Now we can do the update of endpoints in secasindex */ |
| memcpy(&saidx->src, nsaddr, sysdep_sa_len(nsaddr)); |
| memcpy(&saidx->dst, ndaddr, sysdep_sa_len(ndaddr)); |
| |
| return 0; |
| } |
| |
| /* Process the raw (unparsed yet) list of sadb_x_ipsecrequests of MIGRATE |
| * message. For each sadb_x_ipsecrequest pair (old followed by new), |
| * the corresponding ipsecrequest entry in the SP is updated. Associated |
| * existing Phase 2 handle is also updated (if any) */ |
| static int |
| migrate_sp_isr_list(sp, xisr_list, xisr_list_len) |
| struct secpolicy *sp; |
| struct sadb_x_ipsecrequest *xisr_list; |
| int xisr_list_len; |
| { |
| struct sadb_x_ipsecrequest *xisr_new, *xisr_old = xisr_list; |
| int xisr_old_len, xisr_new_len; |
| struct ipsecrequest *isr_cur; |
| |
| isr_cur = sp->req; /* ipsecrequest list from from sp */ |
| |
| while (xisr_list_len > 0 && isr_cur != NULL) { |
| /* Get old xisr (length field is in bytes) */ |
| xisr_old_len = xisr_old->sadb_x_ipsecrequest_len; |
| if (xisr_old_len < sizeof(*xisr_old) || |
| xisr_old_len + sizeof(*xisr_new) > xisr_list_len) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "invalid ipsecrequest length. Exiting.\n"); |
| return -1; |
| } |
| |
| /* Get new xisr with updated info */ |
| xisr_new = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_old) + xisr_old_len); |
| xisr_new_len = xisr_new->sadb_x_ipsecrequest_len; |
| if (xisr_new_len < sizeof(*xisr_new) || |
| xisr_new_len + xisr_old_len > xisr_list_len) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "invalid ipsecrequest length. Exiting.\n"); |
| return -1; |
| } |
| |
| /* Start by migrating current ipsecrequest from SP */ |
| if (migrate_ph2_one_isr(sp->id, isr_cur, xisr_old, xisr_new) == -1) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "Unable to match and migrate isr. Exiting.\n"); |
| return -1; |
| } |
| |
| /* Update pointers for next round */ |
| xisr_list_len -= xisr_old_len + xisr_new_len; |
| xisr_old = (struct sadb_x_ipsecrequest *)(((u_int8_t *)xisr_new) + |
| xisr_new_len); |
| |
| isr_cur = isr_cur->next; /* Get next ipsecrequest from SP */ |
| } |
| |
| /* Check we had the same amount of pairs in the MIGRATE |
| as the number of ipsecrequests in the SP */ |
| if ((xisr_list_len != 0) || isr_cur != NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "number of ipsecrequest does not match the one in SP.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Parse sadb_x_kmaddress extension and make local and remote |
| * parameters point to the new addresses (zero copy). -1 is |
| * returned on error, meaning that addresses are not usable */ |
| static int |
| parse_kmaddress(kmaddr, local, remote) |
| struct sadb_x_kmaddress *kmaddr; |
| struct sockaddr **local, **remote; |
| { |
| int addrslen, local_len=0; |
| struct ph1handle *iph1; |
| |
| if (kmaddr == NULL) |
| return -1; |
| |
| /* Grab addresses in sadb_x_kmaddress extension */ |
| addrslen = PFKEY_EXTLEN(kmaddr) - sizeof(*kmaddr); |
| if (addrslen < sizeof(struct sockaddr)) |
| return -1; |
| |
| *local = (struct sockaddr *)(kmaddr + 1); |
| |
| switch ((*local)->sa_family) { |
| case AF_INET: |
| local_len = sizeof(struct sockaddr_in); |
| break; |
| #ifdef INET6 |
| case AF_INET6: |
| local_len = sizeof(struct sockaddr_in6); |
| break; |
| #endif |
| default: |
| return -1; |
| } |
| |
| if (addrslen != PFKEY_ALIGN8(2*local_len)) |
| return -1; |
| |
| *remote = (struct sockaddr *)(((u_int8_t *)(*local)) + local_len); |
| |
| if ((*local)->sa_family != (*remote)->sa_family) |
| return -1; |
| |
| return 0; |
| } |
| |
| /* Handler of PF_KEY MIGRATE message. Helpers are above */ |
| static int |
| pk_recvmigrate(mhp) |
| caddr_t *mhp; |
| { |
| struct sadb_address *saddr, *daddr; |
| struct sockaddr *old_saddr, *new_saddr; |
| struct sockaddr *old_daddr, *new_daddr; |
| struct sockaddr *old_local, *old_remote; |
| struct sockaddr *local, *remote; |
| struct sadb_x_kmaddress *kmaddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_x_ipsecrequest *xisr_list; |
| struct sadb_lifetime *lt; |
| struct policyindex spidx; |
| struct secpolicy *sp; |
| struct ipsecrequest *isr_cur; |
| struct secasindex *oldsaidx; |
| struct ph2handle *iph2; |
| struct ph1handle *iph1; |
| struct ph2selector ph2sel; |
| struct ph1selector ph1sel; |
| u_int32_t spid; |
| u_int64_t created; |
| int xisr_list_len; |
| int ulproto; |
| struct migrate_args ma; |
| |
| /* Some sanity checks */ |
| |
| if (mhp[0] == NULL |
| || mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_KMADDRESS] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "SADB_X_MIGRATE: invalid MIGRATE message received.\n"); |
| return -1; |
| } |
| kmaddr = (struct sadb_x_kmaddress *)mhp[SADB_X_EXT_KMADDRESS]; |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if (lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| if (xpl->sadb_x_policy_type != IPSEC_POLICY_IPSEC) { |
| plog(LLV_WARNING, LOCATION, NULL,"SADB_X_MIGRATE: " |
| "found non IPsec policy in MIGRATE message. Exiting.\n"); |
| return -1; |
| } |
| |
| if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "invalid size for sadb_x_policy. Exiting.\n"); |
| return -1; |
| } |
| |
| /* Some logging to help debbugging */ |
| if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND) |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "SADB_X_MIGRATE: Outbound SA being migrated.\n"); |
| else |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "SADB_X_MIGRATE: Inbound SA being migrated.\n"); |
| |
| /* validity check */ |
| xisr_list = (struct sadb_x_ipsecrequest *)(xpl + 1); |
| xisr_list_len = PFKEY_EXTLEN(xpl) - sizeof(*xpl); |
| if (xisr_list_len < sizeof(*xisr_list)) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "invalid sadb_x_policy message length. Exiting.\n"); |
| return -1; |
| } |
| |
| if (parse_kmaddress(kmaddr, &local, &remote) == -1) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: " |
| "invalid sadb_x_kmaddress extension. Exiting.\n"); |
| return -1; |
| } |
| |
| /* 0 means ANY */ |
| if (saddr->sadb_address_proto == 0) |
| ulproto = IPSEC_ULPROTO_ANY; |
| else |
| ulproto = saddr->sadb_address_proto; |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| ulproto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| ulproto, |
| created, |
| &spidx); |
| #endif |
| |
| /* Everything seems ok, let's get the SP. |
| * |
| * XXX We could also do the lookup using the spid from xpl. |
| * I don't know which one is better. --arno */ |
| sp = getsp(&spidx); |
| if (sp == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "SADB_X_MIGRATE: Passed policy does not exist: %s\n", |
| spidx2str(&spidx)); |
| return -1; |
| } |
| |
| /* Get the best source and destination addresses used for IKE |
| * negotiation, to find and migrate existing Phase 1 */ |
| if (sp->local && sp->remote) { |
| /* hints available, let's use them */ |
| old_local = (struct sockaddr *)sp->local; |
| old_remote = (struct sockaddr *)sp->remote; |
| } else if (sp->req && sp->req->saidx.mode == IPSEC_MODE_TUNNEL) { |
| /* Tunnel mode and no hint, use endpoints */ |
| old_local = (struct sockaddr *)&sp->req->saidx.src; |
| old_remote = (struct sockaddr *)&sp->req->saidx.dst; |
| } else { |
| /* default, use selectors as fallback */ |
| old_local = (struct sockaddr *)&sp->spidx.src; |
| old_remote = (struct sockaddr *)&sp->spidx.dst; |
| } |
| |
| /* We migrate all Phase 1 that match our old local and remote |
| * addresses (no matter their state). |
| * |
| * XXX In fact, we should probably havea special treatment for |
| * Phase 1 that are being established when we receive a MIGRATE. |
| * This can happen if a movement occurs during the initial IKE |
| * negotiation. In that case, I wonder if should restart the |
| * negotiation from the new address or just update things like |
| * we do it now. |
| * |
| * XXX while looking at getph1byaddr(), the comment at the |
| * beginning of the function expects comparison to happen |
| * without ports considerations but it uses CMPSADDR() which |
| * relies either on cmpsaddrstrict() or cmpsaddrwop() based |
| * on NAT-T support being activated. That make me wonder if I |
| * should force ports to 0 (ANY) in local and remote values |
| * used below. |
| * |
| * -- arno */ |
| |
| /* Apply callback data ...*/ |
| memset(&ma, 0, sizeof(ma)); |
| ma.local = local; |
| ma.remote = remote; |
| |
| /* Fill phase1 match criteria ... */ |
| memset(&ph1sel, 0, sizeof(ph1sel)); |
| ph1sel.local = old_local; |
| ph1sel.remote = old_remote; |
| |
| |
| /* Have matching Phase 1 found and addresses updated. As this is a |
| * time consuming task on a busy responder, and MIGRATE messages |
| * are always sent for *both* inbound and outbound (and possibly |
| * forward), we only do that for outbound SP. */ |
| if (xpl->sadb_x_policy_dir == IPSEC_DIR_OUTBOUND && |
| enumph1(&ph1sel, migrate_ph1_ike_addresses, &ma) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable " |
| "to migrate Phase 1 addresses.\n"); |
| return -1; |
| } |
| |
| /* We can now update IKE addresses in Phase 2 handle. */ |
| memset(&ph2sel, 0, sizeof(ph2sel)); |
| ph2sel.spid = sp->id; |
| if (enumph2(&ph2sel, migrate_ph2_ike_addresses, &ma) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, "SADB_X_MIGRATE: Unable " |
| "to migrate Phase 2 IKE addresses.\n"); |
| return -1; |
| } |
| |
| /* and _then_ in SP. */ |
| if (migrate_sp_ike_addresses(sp, local, remote) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "SADB_X_MIGRATE: Unable to migrate SP IKE addresses.\n"); |
| return -1; |
| } |
| |
| /* Loop on sadb_x_ipsecrequest list to possibly update sp->req |
| * entries and associated live Phase 2 handles (their sa_src |
| * and sa_dst) */ |
| if (migrate_sp_isr_list(sp, xisr_list, xisr_list_len) < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "SADB_X_MIGRATE: Unable to migrate isr list.\n"); |
| return -1; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| #ifndef ANDROID_PATCHED |
| |
| /* |
| * send error against acquire message to kernel. |
| */ |
| int |
| pk_sendeacquire(iph2) |
| struct ph2handle *iph2; |
| { |
| struct sadb_msg *newmsg; |
| int len; |
| |
| len = sizeof(struct sadb_msg); |
| newmsg = racoon_calloc(1, len); |
| if (newmsg == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to get buffer to send acquire.\n"); |
| return -1; |
| } |
| |
| memset(newmsg, 0, len); |
| newmsg->sadb_msg_version = PF_KEY_V2; |
| newmsg->sadb_msg_type = SADB_ACQUIRE; |
| newmsg->sadb_msg_errno = ENOENT; /* XXX */ |
| newmsg->sadb_msg_satype = iph2->satype; |
| newmsg->sadb_msg_len = PFKEY_UNIT64(len); |
| newmsg->sadb_msg_reserved = 0; |
| newmsg->sadb_msg_seq = iph2->seq; |
| newmsg->sadb_msg_pid = (u_int32_t)getpid(); |
| |
| /* send message */ |
| len = pfkey_send(lcconf->sock_pfkey, newmsg, len); |
| |
| racoon_free(newmsg); |
| |
| return 0; |
| } |
| |
| #else |
| |
| int pk_sendeacquire(struct ph2handle *iph2) |
| { |
| exit(1); |
| } |
| |
| #endif |
| |
| /* |
| * check if the algorithm is supported or not. |
| * OUT 0: ok |
| * -1: ng |
| */ |
| int |
| pk_checkalg(class, calg, keylen) |
| int class, calg, keylen; |
| { |
| int sup, error; |
| u_int alg; |
| struct sadb_alg alg0; |
| |
| switch (algclass2doi(class)) { |
| case IPSECDOI_PROTO_IPSEC_ESP: |
| sup = SADB_EXT_SUPPORTED_ENCRYPT; |
| break; |
| case IPSECDOI_ATTR_AUTH: |
| sup = SADB_EXT_SUPPORTED_AUTH; |
| break; |
| case IPSECDOI_PROTO_IPCOMP: |
| plog(LLV_DEBUG, LOCATION, NULL, |
| "no check of compression algorithm; " |
| "not supported in sadb message.\n"); |
| return 0; |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid algorithm class.\n"); |
| return -1; |
| } |
| alg = ipsecdoi2pfkey_alg(algclass2doi(class), algtype2doi(class, calg)); |
| if (alg == ~0) |
| return -1; |
| |
| if (keylen == 0) { |
| if (ipsec_get_keylen(sup, alg, &alg0)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "%s.\n", ipsec_strerror()); |
| return -1; |
| } |
| keylen = alg0.sadb_alg_minbits; |
| } |
| |
| error = ipsec_check_keylen(sup, alg, keylen); |
| if (error) |
| plog(LLV_ERROR, LOCATION, NULL, |
| "%s.\n", ipsec_strerror()); |
| |
| return error; |
| } |
| |
| /* |
| * differences with pfkey_recv() in libipsec/pfkey.c: |
| * - never performs busy wait loop. |
| * - returns NULL and set *lenp to negative on fatal failures |
| * - returns NULL and set *lenp to non-negative on non-fatal failures |
| * - returns non-NULL on success |
| */ |
| static struct sadb_msg * |
| pk_recv(so, lenp) |
| int so; |
| int *lenp; |
| { |
| struct sadb_msg buf, *newmsg; |
| int reallen; |
| int retry = 0; |
| |
| *lenp = -1; |
| do |
| { |
| plog(LLV_DEBUG, LOCATION, NULL, "pk_recv: retry[%d] recv() \n", retry ); |
| *lenp = recv(so, (caddr_t)&buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT); |
| retry++; |
| } |
| while (*lenp < 0 && errno == EAGAIN && retry < 3); |
| |
| if (*lenp < 0) |
| return NULL; /*fatal*/ |
| |
| else if (*lenp < sizeof(buf)) |
| return NULL; |
| |
| reallen = PFKEY_UNUNIT64(buf.sadb_msg_len); |
| if (reallen < sizeof(buf)) { |
| *lenp = -1; |
| errno = EIO; |
| return NULL; /*fatal*/ |
| } |
| if ((newmsg = racoon_calloc(1, reallen)) == NULL) |
| return NULL; |
| |
| *lenp = recv(so, (caddr_t)newmsg, reallen, MSG_PEEK); |
| if (*lenp < 0) { |
| racoon_free(newmsg); |
| return NULL; /*fatal*/ |
| } else if (*lenp != reallen) { |
| racoon_free(newmsg); |
| return NULL; |
| } |
| |
| *lenp = recv(so, (caddr_t)newmsg, reallen, 0); |
| if (*lenp < 0) { |
| racoon_free(newmsg); |
| return NULL; /*fatal*/ |
| } else if (*lenp != reallen) { |
| racoon_free(newmsg); |
| return NULL; |
| } |
| |
| return newmsg; |
| } |
| |
| /* see handler.h */ |
| u_int32_t |
| pk_getseq() |
| { |
| return eay_random(); |
| } |
| |
| static int |
| addnewsp(mhp, local, remote) |
| caddr_t *mhp; |
| struct sockaddr *local, *remote; |
| { |
| struct secpolicy *new = NULL; |
| struct sadb_address *saddr, *daddr; |
| struct sadb_x_policy *xpl; |
| struct sadb_lifetime *lt; |
| u_int64_t created; |
| |
| /* sanity check */ |
| if (mhp[SADB_EXT_ADDRESS_SRC] == NULL |
| || mhp[SADB_EXT_ADDRESS_DST] == NULL |
| || mhp[SADB_X_EXT_POLICY] == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "inappropriate sadb spd management message passed.\n"); |
| goto bad; |
| } |
| |
| saddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_SRC]; |
| daddr = (struct sadb_address *)mhp[SADB_EXT_ADDRESS_DST]; |
| xpl = (struct sadb_x_policy *)mhp[SADB_X_EXT_POLICY]; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| lt = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_HARD]; |
| if(lt != NULL) |
| created = lt->sadb_lifetime_addtime; |
| else |
| created = 0; |
| |
| #ifdef __linux__ |
| /* bsd skips over per-socket policies because there will be no |
| * src and dst extensions in spddump messages. On Linux the only |
| * way to achieve the same is check for policy id. |
| */ |
| if (xpl->sadb_x_policy_id % 8 >= 3) return 0; |
| #endif |
| |
| new = newsp(); |
| if (new == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to allocate buffer\n"); |
| goto bad; |
| } |
| |
| new->spidx.dir = xpl->sadb_x_policy_dir; |
| new->id = xpl->sadb_x_policy_id; |
| new->policy = xpl->sadb_x_policy_type; |
| new->req = NULL; |
| |
| /* check policy */ |
| switch (xpl->sadb_x_policy_type) { |
| case IPSEC_POLICY_DISCARD: |
| case IPSEC_POLICY_NONE: |
| case IPSEC_POLICY_ENTRUST: |
| case IPSEC_POLICY_BYPASS: |
| break; |
| |
| case IPSEC_POLICY_IPSEC: |
| { |
| int tlen; |
| struct sadb_x_ipsecrequest *xisr; |
| struct ipsecrequest **p_isr = &new->req; |
| |
| /* validity check */ |
| if (PFKEY_EXTLEN(xpl) < sizeof(*xpl)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid msg length.\n"); |
| goto bad; |
| } |
| |
| tlen = PFKEY_EXTLEN(xpl) - sizeof(*xpl); |
| xisr = (struct sadb_x_ipsecrequest *)(xpl + 1); |
| |
| while (tlen > 0) { |
| |
| /* length check */ |
| if (xisr->sadb_x_ipsecrequest_len < sizeof(*xisr)) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid msg length.\n"); |
| goto bad; |
| } |
| |
| /* allocate request buffer */ |
| *p_isr = newipsecreq(); |
| if (*p_isr == NULL) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "failed to get new ipsecreq.\n"); |
| goto bad; |
| } |
| |
| /* set values */ |
| (*p_isr)->next = NULL; |
| |
| switch (xisr->sadb_x_ipsecrequest_proto) { |
| case IPPROTO_ESP: |
| case IPPROTO_AH: |
| case IPPROTO_IPCOMP: |
| break; |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid proto type: %u\n", |
| xisr->sadb_x_ipsecrequest_proto); |
| goto bad; |
| } |
| (*p_isr)->saidx.proto = xisr->sadb_x_ipsecrequest_proto; |
| |
| switch (xisr->sadb_x_ipsecrequest_mode) { |
| case IPSEC_MODE_TRANSPORT: |
| case IPSEC_MODE_TUNNEL: |
| break; |
| case IPSEC_MODE_ANY: |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid mode: %u\n", |
| xisr->sadb_x_ipsecrequest_mode); |
| goto bad; |
| } |
| (*p_isr)->saidx.mode = xisr->sadb_x_ipsecrequest_mode; |
| |
| switch (xisr->sadb_x_ipsecrequest_level) { |
| case IPSEC_LEVEL_DEFAULT: |
| case IPSEC_LEVEL_USE: |
| case IPSEC_LEVEL_REQUIRE: |
| break; |
| case IPSEC_LEVEL_UNIQUE: |
| (*p_isr)->saidx.reqid = |
| xisr->sadb_x_ipsecrequest_reqid; |
| break; |
| |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid level: %u\n", |
| xisr->sadb_x_ipsecrequest_level); |
| goto bad; |
| } |
| (*p_isr)->level = xisr->sadb_x_ipsecrequest_level; |
| |
| /* set IP addresses if there */ |
| if (xisr->sadb_x_ipsecrequest_len > sizeof(*xisr)) { |
| struct sockaddr *paddr; |
| |
| paddr = (struct sockaddr *)(xisr + 1); |
| bcopy(paddr, &(*p_isr)->saidx.src, |
| sysdep_sa_len(paddr)); |
| |
| paddr = (struct sockaddr *)((caddr_t)paddr |
| + sysdep_sa_len(paddr)); |
| bcopy(paddr, &(*p_isr)->saidx.dst, |
| sysdep_sa_len(paddr)); |
| } |
| |
| (*p_isr)->sp = new; |
| |
| /* initialization for the next. */ |
| p_isr = &(*p_isr)->next; |
| tlen -= xisr->sadb_x_ipsecrequest_len; |
| |
| /* validity check */ |
| if (tlen < 0) { |
| plog(LLV_ERROR, LOCATION, NULL, |
| "becoming tlen < 0\n"); |
| } |
| |
| xisr = (struct sadb_x_ipsecrequest *)((caddr_t)xisr |
| + xisr->sadb_x_ipsecrequest_len); |
| } |
| } |
| break; |
| default: |
| plog(LLV_ERROR, LOCATION, NULL, |
| "invalid policy type.\n"); |
| goto bad; |
| } |
| |
| #ifdef HAVE_PFKEY_POLICY_PRIORITY |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| xpl->sadb_x_policy_priority, |
| created, |
| &new->spidx); |
| #else |
| KEY_SETSECSPIDX(xpl->sadb_x_policy_dir, |
| saddr + 1, |
| daddr + 1, |
| saddr->sadb_address_prefixlen, |
| daddr->sadb_address_prefixlen, |
| saddr->sadb_address_proto, |
| created, |
| &new->spidx); |
| #endif |
| |
| #ifdef HAVE_SECCTX |
| if (mhp[SADB_X_EXT_SEC_CTX] != NULL) { |
| struct sadb_x_sec_ctx *ctx; |
| |
| ctx = (struct sadb_x_sec_ctx *)mhp[SADB_X_EXT_SEC_CTX]; |
| new->spidx.sec_ctx.ctx_alg = ctx->sadb_x_ctx_alg; |
| new->spidx.sec_ctx.ctx_doi = ctx->sadb_x_ctx_doi; |
| new->spidx.sec_ctx.ctx_strlen = ctx->sadb_x_ctx_len; |
| memcpy(new->spidx.sec_ctx.ctx_str,ctx + 1,ctx->sadb_x_ctx_len); |
| } |
| #endif /* HAVE_SECCTX */ |
| |
| /* Set local and remote hints for that SP, if available */ |
| if (local && remote) { |
| new->local = dupsaddr(local); |
| new->remote = dupsaddr(remote); |
| } |
| |
| inssp(new); |
| |
| return 0; |
| bad: |
| if (new != NULL) { |
| if (new->req != NULL) |
| racoon_free(new->req); |
| racoon_free(new); |
| } |
| return -1; |
| } |
| |
| /* proto/mode/src->dst spi */ |
| const char * |
| sadbsecas2str(src, dst, proto, spi, mode) |
| struct sockaddr *src, *dst; |
| int proto; |
| u_int32_t spi; |
| int mode; |
| { |
| static char buf[256]; |
| u_int doi_proto, doi_mode = 0; |
| char *p; |
| int blen, i; |
| |
| doi_proto = pfkey2ipsecdoi_proto(proto); |
| if (doi_proto == ~0) |
| return NULL; |
| if (mode) { |
| doi_mode = pfkey2ipsecdoi_mode(mode); |
| if (doi_mode == ~0) |
| return NULL; |
| } |
| |
| blen = sizeof(buf) - 1; |
| p = buf; |
| |
| i = snprintf(p, blen, "%s%s%s ", |
| s_ipsecdoi_proto(doi_proto), |
| mode ? "/" : "", |
| mode ? s_ipsecdoi_encmode(doi_mode) : ""); |
| if (i < 0 || i >= blen) |
| return NULL; |
| p += i; |
| blen -= i; |
| |
| i = snprintf(p, blen, "%s->", saddr2str(src)); |
| if (i < 0 || i >= blen) |
| return NULL; |
| p += i; |
| blen -= i; |
| |
| i = snprintf(p, blen, "%s ", saddr2str(dst)); |
| if (i < 0 || i >= blen) |
| return NULL; |
| p += i; |
| blen -= i; |
| |
| if (spi) { |
| snprintf(p, blen, "spi=%lu(0x%lx)", (unsigned long)ntohl(spi), |
| (unsigned long)ntohl(spi)); |
| } |
| |
| return buf; |
| } |