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 + -
显示快捷键?