📄 nat_traversal.c
字号:
/* Openswan NAT-Traversal * Copyright (C) 2002-2003 Mathieu Lafon - Arkoon Network Security * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * RCSID $Id: nat_traversal.c,v 1.15 2004/11/30 02:31:14 mcr Exp $ */#ifdef NAT_TRAVERSAL#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <stdarg.h>#include <syslog.h>#include <errno.h>#include <string.h>#include <unistd.h>#include <signal.h> /* used only if MSG_NOSIGNAL not defined */#include <sys/queue.h>#include <openswan.h>#include <openswan/ipsec_policy.h>#include <pfkeyv2.h>#include <pfkey.h>#include "constants.h"#include "oswlog.h"#include "defs.h"#include "log.h"#include "server.h"#include "state.h"#include "id.h"#include "x509.h"#include "pgp.h"#include "certs.h"#include "smartcard.h"#ifdef XAUTH_USEPAM#include <security/pam_appl.h>#endif#include "connections.h"#include "packet.h"#include "demux.h"#include "kernel.h"#include "whack.h"#include "timer.h"#include "ike_alg.h"#include "cookie.h"#include "sha1.h"#include "md5.h"#include "crypto.h"#include "vendor.h"#include "nat_traversal.h"/* #define FORCE_NAT_TRAVERSAL */#define NAT_D_DEBUG#define NAT_T_SUPPORT_LAST_DRAFTS#ifndef SOL_UDP#define SOL_UDP 17#endif#ifndef UDP_ESPINUDP#define UDP_ESPINUDP 100#endif#define DEFAULT_KEEP_ALIVE_PERIOD 20bool nat_traversal_enabled = FALSE;bool nat_traversal_support_non_ike = FALSE;bool nat_traversal_support_port_floating = FALSE;static unsigned int _kap = 0;static unsigned int _ka_evt = 0;static bool _force_ka = 0;static const char *natt_version = "0.6c";void init_nat_traversal (bool activate, unsigned int keep_alive_period, bool fka, bool spf){ nat_traversal_enabled = activate; nat_traversal_support_non_ike = activate;#ifdef NAT_T_SUPPORT_LAST_DRAFTS nat_traversal_support_port_floating = activate ? spf : FALSE; openswan_log("Setting port floating to %s" , nat_traversal_support_port_floating ? "on" : "off"); openswan_log("port floating activate %d/%d" , activate, spf);#endif _force_ka = fka; _kap = keep_alive_period ? keep_alive_period : DEFAULT_KEEP_ALIVE_PERIOD; plog(" including NAT-Traversal patch (Version %s)%s%s%s", natt_version, activate ? "" : " [disabled]", activate & fka ? " [Force KeepAlive]" : "", activate & !spf ? " [Port Floating disabled]" : "");}static void disable_nat_traversal(int type){ if (type == ESPINUDP_WITH_NON_IKE) nat_traversal_support_non_ike = FALSE; else { openswan_log("port floating turned off"); nat_traversal_support_port_floating = FALSE; } if (!nat_traversal_support_non_ike && !nat_traversal_support_port_floating) nat_traversal_enabled = FALSE; }static void _natd_hash(const struct hash_desc *hasher, char *hash, u_int8_t *icookie, u_int8_t *rcookie, const ip_address *ip, u_int16_t port){ union hash_ctx ctx; if (is_zero_cookie(icookie)) DBG_log("_natd_hash: Warning, icookie is zero !!"); if (is_zero_cookie(rcookie)) DBG_log("_natd_hash: Warning, rcookie is zero !!"); /** * draft-ietf-ipsec-nat-t-ike-01.txt * * HASH = HASH(CKY-I | CKY-R | IP | Port) * * All values in network order */ hasher->hash_init(&ctx); hasher->hash_update(&ctx, icookie, COOKIE_SIZE); hasher->hash_update(&ctx, rcookie, COOKIE_SIZE); switch (addrtypeof(ip)) { case AF_INET: hasher->hash_update(&ctx, (const u_char *)&ip->u.v4.sin_addr.s_addr, sizeof(ip->u.v4.sin_addr.s_addr)); break; case AF_INET6: hasher->hash_update(&ctx, (const u_char *)&ip->u.v6.sin6_addr.s6_addr, sizeof(ip->u.v6.sin6_addr.s6_addr)); break; } hasher->hash_update(&ctx, (const u_char *)&port, sizeof(u_int16_t)); hasher->hash_final(hash, &ctx);#ifdef NAT_D_DEBUG DBG(DBG_NATT, DBG_log("_natd_hash: hasher=%p(%d)", hasher, (int)hasher->hash_digest_len); DBG_dump("_natd_hash: icookie=", icookie, COOKIE_SIZE); DBG_dump("_natd_hash: rcookie=", rcookie, COOKIE_SIZE); switch (addrtypeof(ip)) { case AF_INET: DBG_dump("_natd_hash: ip=", &ip->u.v4.sin_addr.s_addr, sizeof(ip->u.v4.sin_addr.s_addr)); break; } DBG_log("_natd_hash: port=%d", port); DBG_dump("_natd_hash: hash=", hash, hasher->hash_digest_len); );#endif}/** * Add NAT-Traversal VIDs (supported ones) * * Used when we're Initiator */bool nat_traversal_add_vid(u_int8_t np, pb_stream *outs){ bool r = TRUE; if (nat_traversal_support_port_floating) {#if 0 if (r) r = out_vendorid(np, outs, VID_NATT_RFC);#endif if (r) r = out_vendorid(np, outs, VID_NATT_IETF_03); if (r) r = out_vendorid(np, outs, VID_NATT_IETF_02); } if (nat_traversal_support_non_ike) { if (r) r = out_vendorid(np, outs, VID_NATT_IETF_00); } return r;}u_int32_t nat_traversal_vid_to_method(unsigned short nat_t_vid){ switch (nat_t_vid) { case VID_NATT_IETF_00: return LELEM(NAT_TRAVERSAL_IETF_00_01); break; case VID_NATT_IETF_02: case VID_NATT_IETF_02_N: case VID_NATT_IETF_03: return LELEM(NAT_TRAVERSAL_IETF_02_03); break; case VID_NATT_RFC: return LELEM(NAT_TRAVERSAL_RFC); break; } return 0;}void nat_traversal_natd_lookup(struct msg_digest *md){ char hash[MAX_DIGEST_LEN]; struct payload_digest *p; struct state *st = md->st; int i; if (!st || !md->iface || !st->st_oakley.hasher) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d", __FILE__, __LINE__); return; } /** Count NAT-D **/ for (p = md->chain[ISAKMP_NEXT_NATD_RFC], i=0; p != NULL; p = p->next, i++); /** * We need at least 2 NAT-D (1 for us, many for peer) */ if (i < 2) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: Only %d NAT-D - Aborting NAT-Traversal negociation", i); st->nat_traversal = 0; return; } /** * First one with my IP & port */ p = md->chain[ISAKMP_NEXT_NATD_RFC]; _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, &(md->iface->addr), ntohs(st->st_connection->spd.this.host_port)); if (!( (pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len) && (memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len)==0) )) {#ifdef NAT_D_DEBUG DBG(DBG_NATT, DBG_log("NAT_TRAVERSAL_NAT_BHND_ME"); DBG_dump("expected NAT-D:", hash, st->st_oakley.hasher->hash_digest_len); DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); );#endif st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); } /** * The others with sender IP & port */ _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, st->st_rcookie, &(md->sender), ntohs(md->sender_port)); for (p = p->next, i=0 ; p != NULL; p = p->next) { if ( (pbs_left(&p->pbs) == st->st_oakley.hasher->hash_digest_len) && (memcmp(p->pbs.cur, hash, st->st_oakley.hasher->hash_digest_len)==0) ) { i++; } } if (!i) {#ifdef NAT_D_DEBUG DBG(DBG_NATT, DBG_log("NAT_TRAVERSAL_NAT_BHND_PEER"); DBG_dump("expected NAT-D:", hash, st->st_oakley.hasher->hash_digest_len); p = md->chain[ISAKMP_NEXT_NATD_RFC]; for (p = p->next, i=0 ; p != NULL; p = p->next) { DBG_dump("received NAT-D:", p->pbs.cur, pbs_left(&p->pbs)); } );#endif st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); } if(st->st_connection->forceencaps) { st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME); }#ifdef FORCE_NAT_TRAVERSAL st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_PEER); st->nat_traversal |= LELEM(NAT_TRAVERSAL_NAT_BHND_ME);#endif}bool nat_traversal_add_natd(u_int8_t np, pb_stream *outs, struct msg_digest *md){ char hash[MAX_DIGEST_LEN]; struct state *st = md->st; if (!st || !st->st_oakley.hasher) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d", __FILE__, __LINE__); return FALSE; } DBG(DBG_EMITTING, DBG_log("sending NATD payloads")); if (!out_modify_previous_np((st->nat_traversal & NAT_T_WITH_RFC_VALUES ? ISAKMP_NEXT_NATD_RFC : ISAKMP_NEXT_NATD_DRAFTS), outs)) { return FALSE; } /** * First one with sender IP & port */ if(st->st_connection->forceencaps) { _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, &(md->sender),0); } else { _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, &(md->sender),#ifdef FORCE_NAT_TRAVERSAL 0#else ntohs(md->sender_port)#endif ); } if (!out_generic_raw((st->nat_traversal & NAT_T_WITH_RFC_VALUES ? ISAKMP_NEXT_NATD_RFC : ISAKMP_NEXT_NATD_DRAFTS), &isakmp_nat_d, outs, hash, st->st_oakley.hasher->hash_digest_len, "NAT-D")) { return FALSE; } /** * Second one with my IP & port */ if(st->st_connection->forceencaps) { _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, &(md->iface->addr),0); } else { _natd_hash(st->st_oakley.hasher, hash, st->st_icookie, is_zero_cookie(st->st_rcookie) ? md->hdr.isa_rcookie : st->st_rcookie, &(md->iface->addr),#ifdef FORCE_NAT_TRAVERSAL 0#else ntohs(st->st_connection->spd.this.host_port)#endif ); } return (out_generic_raw(np, &isakmp_nat_d, outs, hash, st->st_oakley.hasher->hash_digest_len, "NAT-D"));}/** * nat_traversal_natoa_lookup() * * Look for NAT-OA in message */void nat_traversal_natoa_lookup(struct msg_digest *md){ struct payload_digest *p; struct state *st = md->st; int i; ip_address ip; if (!st || !md->iface) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: assert failed %s:%d", __FILE__, __LINE__); return; } /** Initialize NAT-OA */ anyaddr(AF_INET, &st->nat_oa); /** Count NAT-OA **/ for (p = md->chain[ISAKMP_NEXT_NATOA_RFC], i=0; p != NULL; p = p->next, i++); DBG(DBG_NATT, DBG_log("NAT-Traversal: received %d NAT-OA.", i); ); if (i==0) { return; } else if (!(st->nat_traversal & LELEM(NAT_TRAVERSAL_NAT_BHND_PEER))) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: received %d NAT-OA. " "ignored because peer is not NATed", i); return; } else if (i>1) { loglog(RC_LOG_SERIOUS, "NAT-Traversal: received %d NAT-OA. " "using first, ignoring others", i); } /** Take first **/ p = md->chain[ISAKMP_NEXT_NATOA_RFC]; DBG(DBG_PARSING, DBG_dump("NAT-OA:", p->pbs.start, pbs_room(&p->pbs)); ); switch (p->payload.nat_oa.isanoa_idtype) { case ID_IPV4_ADDR: if (pbs_left(&p->pbs) == sizeof(struct in_addr)) { initaddr(p->pbs.cur, pbs_left(&p->pbs), AF_INET, &ip); } else { loglog(RC_LOG_SERIOUS, "NAT-Traversal: received IPv4 NAT-OA " "with invalid IP size (%d)", (int)pbs_left(&p->pbs)); return; } break; case ID_IPV6_ADDR: if (pbs_left(&p->pbs) == sizeof(struct in6_addr)) { initaddr(p->pbs.cur, pbs_left(&p->pbs), AF_INET6, &ip); } else { loglog(RC_LOG_SERIOUS, "NAT-Traversal: received IPv6 NAT-OA " "with invalid IP size (%d)", (int)pbs_left(&p->pbs)); return; } break; default: loglog(RC_LOG_SERIOUS, "NAT-Traversal: " "invalid ID Type (%d) in NAT-OA - ignored", p->payload.nat_oa.isanoa_idtype); return; break; } DBG(DBG_NATT,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -