ikev1_quick.c
来自「ipsec vpn」· C语言 代码 · 共 2,176 行 · 第 1/4 页
C
2,176 行
/* * IPsec IKEv1 DOI Quick Mode functions. * * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998-2002 D. Hugh Redelmeier. * Copyright (C) 2003-2005 Michael Richardson <mcr@xelerance.com> * * 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: ikev1_quick.c,v 1.3.2.2 2006/03/22 03:37:23 paul Exp $ */#include <stdio.h>#include <string.h>#include <stddef.h>#include <stdlib.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <resolv.h>#include <arpa/nameser.h> /* missing from <resolv.h> on old systems */#include <sys/queue.h>#include <sys/time.h> /* for gettimeofday */#include <openswan.h>#include <openswan/ipsec_policy.h>#include "constants.h"#include "defs.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" /* needs id.h */#include "keys.h"#include "packet.h"#include "demux.h" /* needs packet.h */#include "adns.h" /* needs <resolv.h> */#include "dnskey.h" /* needs keys.h and adns.h */#include "kernel.h" /* needs connections.h */#include "log.h"#include "cookie.h"#include "server.h"#include "spdb.h"#include "timer.h"#include "rnd.h"#include "ipsec_doi.h" /* needs demux.h and state.h */#include "whack.h"#include "fetch.h"#include "pkcs.h"#include "asn1.h"#include "sha1.h"#include "md5.h"#include "crypto.h" /* requires sha1.h and md5.h */#include "ike_alg.h"#include "kernel_alg.h"#include "plutoalg.h"#include "pluto_crypt.h"#include "ikev1.h"#include "ikev1_quick.h"#include "ikev1_continuations.h"#ifdef XAUTH#include "xauth.h"#endif#include "vendor.h"#ifdef NAT_TRAVERSAL#include "nat_traversal.h"#endif#ifdef VIRTUAL_IP#include "virtual.h"#endif#include "dpd.h"#include "x509more.h"/* accept_PFS_KE * * Check and accept optional Quick Mode KE payload for PFS. * Extends ACCEPT_PFS to check whether KE is allowed or required. */static notification_taccept_PFS_KE(struct msg_digest *md, chunk_t *dest, const char *val_name, const char *msg_name){ struct state *st = md->st; struct payload_digest *const ke_pd = md->chain[ISAKMP_NEXT_KE]; if (ke_pd == NULL) { if (st->st_pfs_group != NULL) { loglog(RC_LOG_SERIOUS, "missing KE payload in %s message", msg_name); return INVALID_KEY_INFORMATION; } } else { if (st->st_pfs_group == NULL) { loglog(RC_LOG_SERIOUS, "%s message KE payload requires a GROUP_DESCRIPTION attribute in SA" , msg_name); return INVALID_KEY_INFORMATION; } if (ke_pd->next != NULL) { loglog(RC_LOG_SERIOUS, "%s message contains several KE payloads; we accept at most one", msg_name); return INVALID_KEY_INFORMATION; /* ??? */ } return accept_KE(dest, val_name, st->st_pfs_group, &ke_pd->pbs); } return NOTHING_WRONG;}/* Initiate quick mode. * --> HDR*, HASH(1), SA, Nr [, KE ] [, IDci, IDcr ] * (see RFC 2409 "IKE" 5.5) * Note: this is not called from demux.c */static boolemit_subnet_id(ip_subnet *net , u_int8_t np , u_int8_t protoid , u_int16_t port , pb_stream *outs){ struct isakmp_ipsec_id id; pb_stream id_pbs; ip_address ta; const unsigned char *tbp; size_t tal; const struct af_info *ai; bool usehost = FALSE; int masklen; ai = aftoinfo(subnettypeof(net)); passert(ai != NULL); maskof(net, &ta); masklen = masktocount(&ta);#if 1 if(masklen == ai->mask_cnt) { usehost = TRUE; }#endif id.isaiid_np = np; id.isaiid_idtype = (usehost ? ai->id_addr : ai->id_subnet); id.isaiid_protoid = protoid; id.isaiid_port = port; if (!out_struct(&id, &isakmp_ipsec_identification_desc, outs, &id_pbs)) return FALSE; networkof(net, &ta); tal = addrbytesptr(&ta, &tbp); if (!out_raw(tbp, tal, &id_pbs, "client network")) return FALSE; if(!usehost) { maskof(net, &ta); tal = addrbytesptr(&ta, &tbp); if (!out_raw(tbp, tal, &id_pbs, "client mask")) return FALSE; } close_output_pbs(&id_pbs); return TRUE;}/* * Produce the new key material of Quick Mode. * RFC 2409 "IKE" section 5.5 * specifies how this is to be done. */static voidcompute_proto_keymat(struct state *st, u_int8_t protoid, struct ipsec_proto_info *pi){ size_t needed_len; /* bytes of keying material needed */ /* Add up the requirements for keying material * (It probably doesn't matter if we produce too much!) */ switch (protoid) { case PROTO_IPSEC_ESP: switch (pi->attrs.transid) { case ESP_NULL: needed_len = 0; break; case ESP_DES: needed_len = DES_CBC_BLOCK_SIZE; break; case ESP_3DES: needed_len = DES_CBC_BLOCK_SIZE * 3; break; case ESP_AES: needed_len = AES_CBC_BLOCK_SIZE; /* if an attribute is set, then use that! */ if(st->st_esp.attrs.key_len) { needed_len = st->st_esp.attrs.key_len/8; } break; default:#ifdef KERNEL_ALG if((needed_len=kernel_alg_esp_enc_keylen(pi->attrs.transid))>0) { /* XXX: check key_len "coupling with kernel.c's */ if (pi->attrs.key_len) { needed_len=pi->attrs.key_len/8; DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" "key_len=%d from peer", (int)needed_len)); } break; }#endif bad_case(pi->attrs.transid); } DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" "needed_len (after ESP enc)=%d", (int)needed_len)); switch (pi->attrs.auth) { case AUTH_ALGORITHM_NONE: break; case AUTH_ALGORITHM_HMAC_MD5: needed_len += HMAC_MD5_KEY_LEN; break; case AUTH_ALGORITHM_HMAC_SHA1: needed_len += HMAC_SHA1_KEY_LEN; break; default:#ifdef KERNEL_ALG if (kernel_alg_esp_auth_ok(pi->attrs.auth, NULL)) { needed_len += kernel_alg_esp_auth_keylen(pi->attrs.auth); break; } #endif case AUTH_ALGORITHM_DES_MAC: bad_case(pi->attrs.auth); break; } DBG(DBG_PARSING, DBG_log("compute_proto_keymat:" "needed_len (after ESP auth)=%d", (int)needed_len)); break; case PROTO_IPSEC_AH: switch (pi->attrs.transid) { case AH_MD5: needed_len = HMAC_MD5_KEY_LEN; break; case AH_SHA: needed_len = HMAC_SHA1_KEY_LEN; break; default: bad_case(pi->attrs.transid); } break; default: bad_case(protoid); } pi->keymat_len = needed_len; /* Allocate space for the keying material. * Although only needed_len bytes are desired, we * must round up to a multiple of ctx.hmac_digest_len * so that our buffer isn't overrun. */ { struct hmac_ctx ctx_me, ctx_peer; size_t needed_space; /* space needed for keying material (rounded up) */ size_t i; hmac_init_chunk(&ctx_me, st->st_oakley.hasher, st->st_skeyid_d); ctx_peer = ctx_me; /* duplicate initial conditions */ needed_space = needed_len + pad_up(needed_len, ctx_me.hmac_digest_len); replace(pi->our_keymat, alloc_bytes(needed_space, "keymat in compute_keymat()")); replace(pi->peer_keymat, alloc_bytes(needed_space, "peer_keymat in quick_inI1_outR1()")); for (i = 0;; ) { if (st->st_shared.ptr != NULL) { /* PFS: include the g^xy */ hmac_update_chunk(&ctx_me, st->st_shared); hmac_update_chunk(&ctx_peer, st->st_shared); } hmac_update(&ctx_me, &protoid, sizeof(protoid)); hmac_update(&ctx_peer, &protoid, sizeof(protoid)); hmac_update(&ctx_me, (u_char *)&pi->our_spi, sizeof(pi->our_spi)); hmac_update(&ctx_peer, (u_char *)&pi->attrs.spi, sizeof(pi->attrs.spi)); hmac_update_chunk(&ctx_me, st->st_ni); hmac_update_chunk(&ctx_peer, st->st_ni); hmac_update_chunk(&ctx_me, st->st_nr); hmac_update_chunk(&ctx_peer, st->st_nr); hmac_final(pi->our_keymat + i, &ctx_me); hmac_final(pi->peer_keymat + i, &ctx_peer); i += ctx_me.hmac_digest_len; if (i >= needed_space) break; /* more keying material needed: prepare to go around again */ hmac_reinit(&ctx_me); hmac_reinit(&ctx_peer); hmac_update(&ctx_me, pi->our_keymat + i - ctx_me.hmac_digest_len , ctx_me.hmac_digest_len); hmac_update(&ctx_peer, pi->peer_keymat + i - ctx_peer.hmac_digest_len , ctx_peer.hmac_digest_len); } } DBG(DBG_CRYPT, DBG_dump("KEYMAT computed:\n", pi->our_keymat, pi->keymat_len); DBG_dump("Peer KEYMAT computed:\n", pi->peer_keymat, pi->keymat_len));}static voidcompute_keymats(struct state *st){ if (st->st_ah.present) compute_proto_keymat(st, PROTO_IPSEC_AH, &st->st_ah); if (st->st_esp.present) compute_proto_keymat(st, PROTO_IPSEC_ESP, &st->st_esp);}/* Decode the variable part of an ID packet (during Quick Mode). * This is designed for packets that identify clients, not peers. * Rejects 0.0.0.0/32 or IPv6 equivalent because * (1) it is wrong and (2) we use this value for inband signalling. */static booldecode_net_id(struct isakmp_ipsec_id *id, pb_stream *id_pbs, ip_subnet *net, const char *which){ const struct af_info *afi = NULL; /* Note: the following may be a pointer into static memory * that may be recycled, but only if the type is not known. * That case is disposed of very early -- in the first switch. */ const char *idtypename = enum_show(&ident_names, id->isaiid_idtype); switch (id->isaiid_idtype) { case ID_IPV4_ADDR: case ID_IPV4_ADDR_SUBNET: case ID_IPV4_ADDR_RANGE: afi = &af_inet4_info; break; case ID_IPV6_ADDR: case ID_IPV6_ADDR_SUBNET: case ID_IPV6_ADDR_RANGE: afi = &af_inet6_info; break; case ID_FQDN: return TRUE; default: /* XXX support more */ loglog(RC_LOG_SERIOUS, "unsupported ID type %s" , idtypename); /* XXX Could send notification back */ return FALSE; } switch (id->isaiid_idtype) { case ID_IPV4_ADDR: case ID_IPV6_ADDR: { ip_address temp_address; err_t ugh; ugh = initaddr(id_pbs->cur, pbs_left(id_pbs), afi->af, &temp_address); if (ugh != NULL) { loglog(RC_LOG_SERIOUS, "%s ID payload %s has wrong length in Quick I1 (%s)" , which, idtypename, ugh); /* XXX Could send notification back */ return FALSE; } if (isanyaddr(&temp_address)) { loglog(RC_LOG_SERIOUS, "%s ID payload %s is invalid (%s) in Quick I1" , which, idtypename, ip_str(&temp_address)); /* XXX Could send notification back */ return FALSE; } happy(addrtosubnet(&temp_address, net)); DBG(DBG_PARSING | DBG_CONTROL , DBG_log("%s is %s", which, ip_str(&temp_address))); break; } case ID_IPV4_ADDR_SUBNET: case ID_IPV6_ADDR_SUBNET: { ip_address temp_address, temp_mask; err_t ugh; if (pbs_left(id_pbs) != 2 * afi->ia_sz) { loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1" , which, idtypename); /* XXX Could send notification back */ return FALSE; } ugh = initaddr(id_pbs->cur , afi->ia_sz, afi->af, &temp_address); if (ugh == NULL) ugh = initaddr(id_pbs->cur + afi->ia_sz , afi->ia_sz, afi->af, &temp_mask); if (ugh == NULL) ugh = initsubnet(&temp_address, masktocount(&temp_mask) , '0', net); if (ugh == NULL && subnetisnone(net)) ugh = "contains only anyaddr"; if (ugh != NULL) { loglog(RC_LOG_SERIOUS, "%s ID payload %s bad subnet in Quick I1 (%s)" , which, idtypename, ugh); /* XXX Could send notification back */ return FALSE; } DBG(DBG_PARSING | DBG_CONTROL, { char temp_buff[SUBNETTOT_BUF]; subnettot(net, 0, temp_buff, sizeof(temp_buff)); DBG_log("%s is subnet %s", which, temp_buff); }); break; } case ID_IPV4_ADDR_RANGE: case ID_IPV6_ADDR_RANGE: { ip_address temp_address_from, temp_address_to; err_t ugh; if (pbs_left(id_pbs) != 2 * afi->ia_sz) { loglog(RC_LOG_SERIOUS, "%s ID payload %s wrong length in Quick I1" , which, idtypename); /* XXX Could send notification back */ return FALSE; } ugh = initaddr(id_pbs->cur, afi->ia_sz, afi->af, &temp_address_from); if (ugh == NULL) ugh = initaddr(id_pbs->cur + afi->ia_sz , afi->ia_sz, afi->af, &temp_address_to); if (ugh != NULL) { loglog(RC_LOG_SERIOUS, "%s ID payload %s malformed (%s) in Quick I1" , which, idtypename, ugh); /* XXX Could send notification back */ return FALSE; } ugh = rangetosubnet(&temp_address_from, &temp_address_to, net); if (ugh == NULL && subnetisnone(net)) ugh = "contains only anyaddr"; if (ugh != NULL) { char temp_buff1[ADDRTOT_BUF], temp_buff2[ADDRTOT_BUF]; addrtot(&temp_address_from, 0, temp_buff1, sizeof(temp_buff1)); addrtot(&temp_address_to, 0, temp_buff2, sizeof(temp_buff2)); loglog(RC_LOG_SERIOUS, "%s ID payload in Quick I1, %s" " %s - %s unacceptable: %s" , which, idtypename, temp_buff1, temp_buff2, ugh); return FALSE; } DBG(DBG_PARSING | DBG_CONTROL, { char temp_buff[SUBNETTOT_BUF]; subnettot(net, 0, temp_buff, sizeof(temp_buff)); DBG_log("%s is subnet %s (received as range)" , which, temp_buff); }); break; } } /* set the port selector */ setportof(htons(id->isaiid_port), &net->addr); DBG(DBG_PARSING | DBG_CONTROL, DBG_log("%s protocol/port is %d/%d", which, id->isaiid_protoid, id->isaiid_port) ) return TRUE;}/* like decode, but checks that what is received matches what was sent */static boolcheck_net_id(struct isakmp_ipsec_id *id, pb_stream *id_pbs, u_int8_t *protoid, u_int16_t *port, ip_subnet *net
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?