📄 ipsec_doi.c
字号:
/* IPsec DOI and Oakley resolution routines * Copyright (C) 1997 Angelos D. Keromytis. * Copyright (C) 1998-2002 D. Hugh Redelmeier. * Copyright (C) 2003 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.279 2004/12/16 01:20:03 mcr 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"#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 "pluto_crypt.h"#include "x509more.h"/* OpenPGP Vendor ID needed for interoperability with PGPnet * * Note: it is a NUL-terminated ASCII string, but NUL won't go on the wire. */static char pgp_vendorid[] = "OpenPGP10171";static char dpd_vendorid[] = {0xAF, 0xCA, 0xD7, 0x13, 0x68, 0xA1, 0xF1, 0xC9, 0x6B, 0x86, 0x96, 0xFC, 0x77, 0x57, 0x01, 0x00};/* * continuation used */struct ke_continuation { struct pluto_crypto_req_cont ke_pcrc; struct msg_digest *md;};struct qke_continuation { struct pluto_crypto_req_cont qke_pcrc; struct state *st; /* need to use abstract # */ struct state *isakmp_sa; /* used in initiator */ so_serial_t replacing; struct msg_digest *md; /* used in responder */};/** 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 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; 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'; return pluto_vendorid;}typedef stf_status initiator_function(int whack_sock , struct connection *c , struct state *predecessor , lset_t policy , unsigned long try , enum crypto_importance importance);/* 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 */ }}/** Compute DH shared secret from our local secret and the peer's public value. * We make the leap that the length should be that of the group * (see quoted passage at start of ACCEPT_KE). */static voidcompute_dh_shared(struct state *st, const chunk_t g, const struct oakley_group_desc *group){ MP_INT mp_g, mp_shared; struct timeval tv0, tv1; unsigned long tv_diff; gettimeofday(&tv0, NULL); passert(st->st_sec_in_use); n_to_mpz(&mp_g, g.ptr, g.len); mpz_init(&mp_shared); mpz_powm(&mp_shared, &mp_g, &st->st_sec, group->modulus); mpz_clear(&mp_g); freeanychunk(st->st_shared); /* happens in odd error cases */ st->st_shared = mpz_to_n(&mp_shared, group->bytes); mpz_clear(&mp_shared); gettimeofday(&tv1, NULL); tv_diff=(tv1.tv_sec - tv0.tv_sec) * 1000000 + (tv1.tv_usec - tv0.tv_usec); DBG(DBG_CRYPT, DBG_log("compute_dh_shared(): time elapsed (%s): %ld usec" , enum_show(&oakley_group_names, st->st_oakley.group->group) , tv_diff); ); /* if took more than 200 msec ... */ if (tv_diff > 200000) { loglog(RC_LOG_SERIOUS, "WARNING: compute_dh_shared(): for %s took " "%ld usec" , enum_show(&oakley_group_names, st->st_oakley.group->group) , tv_diff); } DBG_cond_dump_chunk(DBG_CRYPT, "DH shared secret:\n", st->st_shared);}#if 0/** if we haven't already done so, compute a local DH secret (st->st_sec) and * the corresponding public value (g). This is emitted as a KE payload. */static boolbuild_and_ship_KE(struct state *st, chunk_t *g, const struct oakley_group_desc *group, pb_stream *outs, u_int8_t np){ if (!st->st_sec_in_use) { u_char tmp[LOCALSECRETSIZE]; MP_INT mp_g; get_rnd_bytes(tmp, LOCALSECRETSIZE); st->st_sec_in_use = TRUE; n_to_mpz(&st->st_sec, tmp, LOCALSECRETSIZE); mpz_init(&mp_g); mpz_powm(&mp_g, &groupgenerator, &st->st_sec, group->modulus); freeanychunk(*g); /* happens in odd error cases */ *g = mpz_to_n(&mp_g, group->bytes); mpz_clear(&mp_g); DBG(DBG_CRYPT, DBG_dump("Local DH secret:\n", tmp, LOCALSECRETSIZE); DBG_dump_chunk("Public DH value sent:\n", *g)); } return out_generic_chunk(np, &isakmp_keyex_desc, outs, *g, "keyex value");}#endif/* * package up the calculate KE value, and emit it as a KE payload. */static 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); } 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. */static 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;}/* 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;}#if 0static boolbuild_and_ship_nonce(chunk_t *n, pb_stream *outs, u_int8_t np , const char *name){ freeanychunk(*n); setchunk(*n, alloc_bytes(DEFAULT_NONCE_SIZE, name), DEFAULT_NONCE_SIZE); get_rnd_bytes(n->ptr, DEFAULT_NONCE_SIZE); return out_generic_chunk(np, &isakmp_nonce_desc, outs, *n, name);}#endifstatic 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; passert((sndst) && (sndst->st_connection)); switch(type) { case PAYLOAD_MALFORMED: 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_connection->spd.that.host_addr) , (unsigned)sndst->st_connection->spd.that.host_port); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -