ipsec_doi.c

来自「ipsec vpn」· C语言 代码 · 共 2,351 行 · 第 1/5 页

C
2,351
字号
/* IPsec DOI and Oakley resolution routines * 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: ipsec_doi.c,v 1.304.2.10 2006/03/20 13:34:36 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 "pfkeyv2.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_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"/** tools for sending Pluto Vendor ID. */#ifdef PLUTO_SENDS_VENDORID#define SEND_PLUTO_VID	1#else /* !PLUTO_SENDS_VENDORID */#define SEND_PLUTO_VID	0#endif /* !PLUTO_SENDS_VENDORID *//* Pluto's Vendor ID * * Note: it is a NUL-terminated ASCII string, but NUL won't go on the wire. */#define PLUTO_VENDORID_SIZE 12static bool pluto_vendorid_built = FALSE;static char pluto_vendorid[PLUTO_VENDORID_SIZE + 1];const char *init_pluto_vendorid(void){    MD5_CTX hc;    unsigned char hash[MD5_DIGEST_SIZE];    const unsigned char *v = ipsec_version_string();    int i;    if(pluto_vendorid_built) {	return pluto_vendorid;    }    osMD5Init(&hc);    osMD5Update(&hc, v, strlen(v));    osMD5Update(&hc, compile_time_interop_options	, strlen(compile_time_interop_options));    osMD5Final(hash, &hc);    pluto_vendorid[0] = 'O';	/* Opportunistic Encryption Rules */    pluto_vendorid[1] = 'E';#if PLUTO_VENDORID_SIZE - 2 <= MD5_DIGEST_SIZE    /* truncate hash to fit our vendor ID */    memcpy(pluto_vendorid + 2, hash, PLUTO_VENDORID_SIZE - 2);#else    /* pad to fill our vendor ID */    memcpy(pluto_vendorid + 2, hash, MD5_DIGEST_SIZE);    memset(pluto_vendorid + 2 + MD5_DIGEST_SIZE, '\0'	, PLUTO_VENDORID_SIZE - 2 - MD5_DIGEST_SIZE);#endif    /* Make it printable!  Hahaha - MCR */    for (i = 2; i < PLUTO_VENDORID_SIZE; i++)    {	/* Reset bit 7, force bit 6.  Puts it into 64-127 range */	pluto_vendorid[i] &= 0x7f;	pluto_vendorid[i] |= 0x40;    }    pluto_vendorid[PLUTO_VENDORID_SIZE] = '\0';    pluto_vendorid_built = TRUE;    return pluto_vendorid;}/* MAGIC: perform f, a function that returns notification_t * and return from the ENCLOSING stf_status returning function if it fails. */#define RETURN_STF_FAILURE2(f, xf)					\    { int r = (f); if (r != NOTHING_WRONG) { \	  if((xf)!=NULL) pfree(xf);	     \	  return STF_FAIL + r; }}#define RETURN_STF_FAILURE(f) RETURN_STF_FAILURE2(f, NULL)/* create output HDR as replica of input HDR */voidecho_hdr(struct msg_digest *md, bool enc, u_int8_t np){    struct isakmp_hdr r_hdr = md->hdr;	/* mostly same as incoming header */    r_hdr.isa_flags &= ~ISAKMP_FLAG_COMMIT;	/* we won't ever turn on this bit */    if (enc)	r_hdr.isa_flags |= ISAKMP_FLAG_ENCRYPTION;    /* some day, we may have to set r_hdr.isa_version */    r_hdr.isa_np = np;    if (!out_struct(&r_hdr, &isakmp_hdr_desc, &md->reply, &md->rbody)) {	impossible();	/* surely must have room and be well-formed */    }}/* * package up the calculate KE value, and emit it as a KE payload. */boolship_KE(struct state *st	, struct pluto_crypto_req *r	, chunk_t *g	, pb_stream *outs, u_int8_t np){    struct pcr_kenonce *kn = &r->pcr_d.kn;    if (!st->st_sec_in_use)    {	st->st_sec_in_use = TRUE;	freeanychunk(*g);	/* happens in odd error cases */	clonetochunk(*g, wire_chunk_ptr(kn, &(kn->gi))		     , kn->gi.len, "saved gi value");	n_to_mpz(&st->st_sec		 , wire_chunk_ptr(kn, &(kn->secret))		 , kn->secret.len);	clonetochunk(st->st_sec_chunk		     , wire_chunk_ptr(kn, &(kn->secret))		     , kn->secret.len, "long term secret");	    }    return out_generic_chunk(np, &isakmp_keyex_desc, outs, *g, "keyex value");}/* accept_ke * * Check and accept DH public value (Gi or Gr) from peer's message. * According to RFC2409 "The Internet key exchange (IKE)" 5: *  The Diffie-Hellman public value passed in a KE payload, in either *  a phase 1 or phase 2 exchange, MUST be the length of the negotiated *  Diffie-Hellman group enforced, if necessary, by pre-pending the *  value with zeros. */notification_taccept_KE(chunk_t *dest, const char *val_name	  , const struct oakley_group_desc *gr	  , pb_stream *pbs){    if (pbs_left(pbs) != gr->bytes)    {	loglog(RC_LOG_SERIOUS, "KE has %u byte DH public value; %u required"	    , (unsigned) pbs_left(pbs), (unsigned) gr->bytes);	/* XXX Could send notification back */	return INVALID_KEY_INFORMATION;    }    clonereplacechunk(*dest, pbs->cur, pbs_left(pbs), val_name);    DBG_cond_dump_chunk(DBG_CRYPT, "DH public value received:\n", *dest);    return NOTHING_WRONG;}boolship_nonce(chunk_t *n, struct pluto_crypto_req *r	   , pb_stream *outs, u_int8_t np	   , const char *name){    struct pcr_kenonce *kn = &r->pcr_d.kn;    freeanychunk(*n);    clonetochunk(*n, wire_chunk_ptr(kn, &(kn->n))		 , DEFAULT_NONCE_SIZE, "initiator nonce");    return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name);}/* Send a notification to the peer.  We could decide * whether to send the notification, based on the type and the * destination, if we care to. */static voidsend_notification(struct state *sndst, u_int16_t type, struct state *encst,		  msgid_t msgid, u_char *icookie, u_char *rcookie,		  u_char *spi, size_t spisize, u_char protoid){    u_char buffer[1024];    pb_stream pbs, r_hdr_pbs;    u_char *r_hashval, *r_hash_start;    static time_t last_malformed;    time_t n = time((time_t)NULL);    r_hashval = NULL;    r_hash_start = NULL;    passert((sndst) && (sndst->st_connection));    switch(type) {    case PAYLOAD_MALFORMED:	/* only send one per second. */	if(n == last_malformed) {	    return;	}	last_malformed = n;	sndst->hidden_variables.st_malformed_sent++;	if(sndst->hidden_variables.st_malformed_sent > MAXIMUM_MALFORMED_NOTIFY) {	    openswan_log("too many (%d) malformed payloads. Deleting state"			 , sndst->hidden_variables.st_malformed_sent);	    delete_state(sndst);	    return;	}	/*	 * do not encrypt notification, since #1 reason for malformed	 * payload is that the keys are all messed up.	 */	encst = NULL;	break;	    case INVALID_FLAGS:	/*	 * invalid flags usually includes encryption flags, so do not	 * send encrypted.	 */	encst = NULL;	break;    }        if(encst!=NULL && !IS_ISAKMP_ENCRYPTED(encst->st_state)) {	encst = NULL;    }    openswan_log("sending %snotification %s to %s:%u"		 , encst ? "encrypted " : ""		 , enum_name(&ipsec_notification_names, type)		 , ip_str(&sndst->st_remoteaddr)		 , sndst->st_remoteport);    memset(buffer, 0, sizeof(buffer));    init_pbs(&pbs, buffer, sizeof(buffer), "notification msg");    /* HDR* */    {	struct isakmp_hdr hdr;	hdr.isa_version = ISAKMP_MAJOR_VERSION << ISA_MAJ_SHIFT | ISAKMP_MINOR_VERSION;	hdr.isa_np = encst ? ISAKMP_NEXT_HASH : ISAKMP_NEXT_N;	hdr.isa_xchg = ISAKMP_XCHG_INFO;	hdr.isa_msgid = msgid;	hdr.isa_flags = encst ? ISAKMP_FLAG_ENCRYPTION : 0;	if (icookie)	    memcpy(hdr.isa_icookie, icookie, COOKIE_SIZE);	if (rcookie)	    memcpy(hdr.isa_rcookie, rcookie, COOKIE_SIZE);	if (!out_struct(&hdr, &isakmp_hdr_desc, &pbs, &r_hdr_pbs))	    impossible();    }    /* HASH -- value to be filled later */    if (encst)    {	pb_stream hash_pbs;	if (!out_generic(ISAKMP_NEXT_N, &isakmp_hash_desc, &r_hdr_pbs,	    &hash_pbs))	    impossible();	r_hashval = hash_pbs.cur;  /* remember where to plant value */	if (!out_zero(	    encst->st_oakley.hasher->hash_digest_len,	    &hash_pbs, "HASH(1)"))	    impossible();	close_output_pbs(&hash_pbs);	r_hash_start = r_hdr_pbs.cur; /* hash from after HASH(1) */    }    /* Notification Payload */    {	pb_stream not_pbs;	struct isakmp_notification isan;	isan.isan_doi = ISAKMP_DOI_IPSEC;	isan.isan_np = ISAKMP_NEXT_NONE;	isan.isan_type = type;	isan.isan_spisize = spisize;	isan.isan_protoid = protoid;	if(!out_struct(&isan, &isakmp_notification_desc		       , &r_hdr_pbs, &not_pbs))  {	    openswan_log("failed to build notification in send_notification\n");	    return;	}	if(spisize > 0) {	    if(out_raw(spi, spisize, &not_pbs, "spi")) {		openswan_log("failed to build notification for spisize=%d\n", (int)spisize);		return;	    }	}	close_output_pbs(&not_pbs);    }    /* calculate hash value and patch into Hash Payload */    if (encst)    {	struct hmac_ctx ctx;	hmac_init_chunk(&ctx, encst->st_oakley.hasher, encst->st_skeyid_a);	hmac_update(&ctx, (u_char *) &msgid, sizeof(msgid_t));	hmac_update(&ctx, r_hash_start, r_hdr_pbs.cur-r_hash_start);	hmac_final(r_hashval, &ctx);	DBG(DBG_CRYPT,	    DBG_log("HASH(1) computed:");	    DBG_dump("", r_hashval, ctx.hmac_digest_len);	)    }    /* Encrypt message (preserve st_iv) */    if (encst)    {	u_char old_iv[MAX_DIGEST_LEN];	u_int old_iv_len = encst->st_iv_len;	if (old_iv_len > MAX_DIGEST_LEN)	    impossible();	memcpy(old_iv, encst->st_iv, old_iv_len);		if (!IS_ISAKMP_SA_ESTABLISHED(encst->st_state))	{	    if (encst->st_new_iv_len > MAX_DIGEST_LEN)		impossible();	    memcpy(encst->st_iv, encst->st_new_iv, encst->st_new_iv_len);	    encst->st_iv_len = encst->st_new_iv_len;	}	init_phase2_iv(encst, &msgid);	if (!encrypt_message(&r_hdr_pbs, encst))	    impossible();	    	/* restore preserved st_iv*/	memcpy(encst->st_iv, old_iv, old_iv_len);	encst->st_iv_len = old_iv_len;    }    else    {	close_output_pbs(&r_hdr_pbs);    }    /* Send packet (preserve st_tpacket) */    {	chunk_t saved_tpacket = sndst->st_tpacket;	setchunk(sndst->st_tpacket, pbs.start, pbs_offset(&pbs));	send_packet(sndst, "notification packet", TRUE);	sndst->st_tpacket = saved_tpacket;    }}voidsend_notification_from_state(struct state *st, enum state_kind state,    u_int16_t type){    struct state *p1st;    passert(st);    if (state == STATE_UNDEFINED)	state = st->st_state;    if (IS_QUICK(state)) {	p1st = find_phase1_state(st->st_connection, ISAKMP_SA_ESTABLISHED_STATES);	if ((p1st == NULL) || (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state))) {	    loglog(RC_LOG_SERIOUS,		"no Phase1 state for Quick mode notification");	    return;	}	send_notification(st, type, p1st, generate_msgid(p1st),	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);    }    else if (IS_ISAKMP_ENCRYPTED(state)) {	send_notification(st, type, st, generate_msgid(st),	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);    }    else {	/* no ISAKMP SA established - don't encrypt notification */	send_notification(st, type, NULL, 0,	    st->st_icookie, st->st_rcookie, NULL, 0, PROTO_ISAKMP);    }}voidsend_notification_from_md(struct msg_digest *md, u_int16_t type){    /**     * Create a dummy state to be able to use send_packet in     * send_notification     *     * we need to set:     *   st_connection->that.host_addr     *   st_connection->that.host_port

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?