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, ¬_pbs)) { openswan_log("failed to build notification in send_notification\n"); return; } if(spisize > 0) { if(out_raw(spi, spisize, ¬_pbs, "spi")) { openswan_log("failed to build notification for spisize=%d\n", (int)spisize); return; } } close_output_pbs(¬_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 + -
显示快捷键?