/* * Copyright (c) 2007 * Shrew Soft Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Redistributions in any form must be accompanied by information on * how to obtain complete source code for the software and any * accompanying software that uses the software. The source code * must either be included in the distribution or be available for no * more than the cost of distribution plus a nominal fee, and must be * freely redistributable under reasonable conditions. For an * executable file, complete source code means the source code for all * modules it contains. It does not include source code for modules or * files that typically accompany the major components of the operating * system on which the executable file runs. * * THIS SOFTWARE IS PROVIDED BY SHREW SOFT INC ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SHREW SOFT INC * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * AUTHOR : Matthew Grooms * mgrooms@shrew.net * */ #include "iked.h" long _IKED::process_phase1_recv( IDB_PH1 * ph1, PACKET_IKE & packet, unsigned char payload ) { long result = LIBIKE_OK; // // log packet type // log.txt( LLOG_INFO, "ii : processing phase1 packet ( %i bytes )\n", packet.size() ); // // make sure we are not dealing // with an sa marked for delete // if( ph1->status() == XCH_STATUS_DEAD ) { log.txt( LLOG_ERROR, "!! : phase1 packet ignored ( phase1 marked for death )\n" ); return LIBIKE_OK; } // // make sure we are not dealing // with a mature sa // if( ph1->status() >= XCH_STATUS_MATURE ) { log.txt( LLOG_ERROR, "!! : phase1 packet ignored, resending last packet ( phase1 already mature )\n" ); ph1->resend(); return LIBIKE_OK; } // // check for duplicate exchange packets. // if we don't discard these before our // processing, we run the risk of errors // assiciated with erroneous data caused // by decrypting packets using invalid // cipher initialization vector data. // bool duplicate = false; if( ph1->exchange == ISAKMP_EXCH_IDENT_PROTECT ) { switch( payload ) { case ISAKMP_PAYLOAD_SA: if( ph1->xstate & XSTATE_RECV_SA ) duplicate = true; break; case ISAKMP_PAYLOAD_KEX: if( ph1->xstate & XSTATE_RECV_KE ) duplicate = true; break; case ISAKMP_PAYLOAD_IDENT: if( ph1->xstate & XSTATE_RECV_ID ) duplicate = true; break; case ISAKMP_PAYLOAD_HASH: if( ph1->xstate & XSTATE_RECV_HA ) duplicate = true; break; } } if( ph1->exchange == ISAKMP_EXCH_AGGRESSIVE ) { switch( payload ) { case ISAKMP_PAYLOAD_SA: if( ph1->xstate & XSTATE_RECV_SA ) duplicate = true; break; case ISAKMP_PAYLOAD_CERT: if( ph1->xstate & XSTATE_RECV_CT ) duplicate = true; break; case ISAKMP_PAYLOAD_HASH: if( ph1->xstate & XSTATE_RECV_HA ) duplicate = true; break; } } if( duplicate ) { log.txt( LLOG_ERROR, "!! : phase1 packet ignored ( duplicate leading %s payload )\n", find_name( NAME_PAYLOAD, payload ) ); return LIBIKE_OK; } // // attempt to decrypt our packet // if( packet_ike_decrypt( ph1, packet, &ph1->iv ) != LIBIKE_OK ) { log.txt( LLOG_ERROR, "!! : phase1 packet ignored ( packet decryption error )\n" ); return LIBIKE_OK; } // // if we are dumping decrypted packets, // we need to rebuild a full packet to // dump to pcap format // if( dump_decrypt ) { // // strip the isakmp encryption flag // packet.buff()[ ISAKMP_FLAGS_OFFSET ] &= ~ISAKMP_FLAG_ENCRYPT; // // build ip dump packet // PACKET_IP packet_ip_dump; packet_ike_encap( packet, packet_ip_dump, ph1->tunnel->saddr_r, ph1->tunnel->saddr_l, ph1->tunnel->natt_version ); // // obtain ethernet header // ETH_HEADER ethhdr; header( packet_ip_dump, ethhdr ); // // dump the packet // pcap_decrypt.dump( ethhdr, packet_ip_dump ); } // // read and process all payloads // uint8_t next_payload; while( payload != ISAKMP_PAYLOAD_NONE ) { // // read the payload header // if( !packet.get_payload( false, next_payload ) ) break; // // call the appropriate payload handler // switch( payload ) { // // security association payload // case ISAKMP_PAYLOAD_SA: if( ph1->xstate & XSTATE_RECV_SA ) log.txt( LLOG_INFO, "<< : ignoring duplicate security association payload\n" ); else { size_t beg = packet.oset(); result = payload_get_sa( packet, ph1->plist_r ); size_t end = packet.oset(); if( !ph1->initiator ) ph1->hda.set( packet.buff() + beg, end - beg ); } ph1->xstate |= XSTATE_RECV_SA; break; // // key exchange payload // case ISAKMP_PAYLOAD_KEX: if( ph1->xstate & XSTATE_RECV_KE ) log.txt( LLOG_INFO, "<< : ignoring duplicate key excahnge payload\n" ); else result = payload_get_kex( packet, ph1->xr ); ph1->xstate |= XSTATE_RECV_KE; break; // // nonce payload // case ISAKMP_PAYLOAD_NONCE: if( ph1->xstate & XSTATE_RECV_NO ) log.txt( LLOG_INFO, "<< : ignoring duplicate nonce payload\n" ); else result = payload_get_nonce( packet, ph1->nonce_r ); ph1->xstate |= XSTATE_RECV_NO; break; // // identity payload // case ISAKMP_PAYLOAD_IDENT: if( ph1->xstate & XSTATE_RECV_ID ) log.txt( LLOG_INFO, "<< : ignoring duplicate id payload\n" ); else { size_t beg = packet.oset(); result = payload_get_ph1id( packet, ph1->ph1id_r ); size_t end = packet.oset(); if( ph1->initiator ) ph1->idr.set( packet.buff() + beg, end - beg ); else ph1->idi.set( packet.buff() + beg, end - beg ); } ph1->xstate |= XSTATE_RECV_ID; break; // // certificate payload // case ISAKMP_PAYLOAD_CERT: { BDATA cert; result = payload_get_cert( packet, ph1->ctype_r, cert ); if( result == LIBIKE_OK ) ph1->certs_r.add( cert ); ph1->xstate |= XSTATE_RECV_CT; break; } // // certificate request payload // case ISAKMP_PAYLOAD_CERT_REQ: if( ph1->xstate & XSTATE_RECV_CR ) log.txt( LLOG_INFO, "<< : ignoring duplicate cert request payload\n" ); else result = payload_get_creq( packet, ph1->ctype_l ); ph1->xstate |= XSTATE_RECV_CR; break; // // signature payload // case ISAKMP_PAYLOAD_SIGNATURE: if( ph1->xstate & XSTATE_RECV_SI ) log.txt( LLOG_INFO, "<< : ignoring duplicate signature payload\n" ); else result = payload_get_sign( packet, ph1->sign_r ); ph1->xstate |= XSTATE_RECV_SI; break; // // hash payload // case ISAKMP_PAYLOAD_HASH: if( ph1->xstate & XSTATE_RECV_HA ) log.txt( LLOG_INFO, "<< : ignoring duplicate hash payload\n" ); else result = payload_get_hash( packet, ph1->hash_r, ph1->hash_size ); ph1->xstate |= XSTATE_RECV_HA; break; // // nat discovery payload // case ISAKMP_PAYLOAD_NAT_VXX_DISC: case ISAKMP_PAYLOAD_NAT_RFC_DISC: { if( !( ph1->xstate & XSTATE_RECV_NDL ) ) { result = payload_get_natd( packet, ph1->natd_rd, ph1->hash_size ); ph1->xstate |= XSTATE_RECV_NDL; break; } if( !( ph1->xstate & XSTATE_RECV_NDR ) ) { result = payload_get_natd( packet, ph1->natd_rs, ph1->hash_size ); ph1->xstate |= XSTATE_RECV_NDR; break; } break; } // // vendor id payload // case ISAKMP_PAYLOAD_VEND: { BDATA vend; result = payload_get_vend( packet, vend ); if( result == LIBIKE_OK ) phase1_chk_vend( ph1, vend ); break; } // // notify payload // case ISAKMP_PAYLOAD_NOTIFY: { IKE_NOTIFY notify; result = payload_get_notify( packet, ¬ify ); if( result == LIBIKE_OK ) ph1->notifications.add( notify ); break; } // // unhandled payload // default: log.txt( LLOG_ERROR, "!! : unhandled phase1 payload \'%s\' ( %i )\n", find_name( NAME_PAYLOAD, payload ), payload ); packet.notify = ISAKMP_N_INVALID_PAYLOAD_TYPE; result = LIBIKE_DECODE; break; } // // check that the entire payload was read // if( packet.get_payload_left() ) log.txt( LLOG_ERROR, "!! : unprocessed payload data\n" ); // // now that we have decoded the payload, // perform any required processing here // while( result == LIBIKE_OK ) { // // verify the peers proposal // if( ( ph1->xstate & XSTATE_RECV_SA ) && !( ph1->lstate & LSTATE_CHKPROP ) ) { // // select an acceptable proposal // result = phase1_sel_prop( ph1 ); if( result != LIBIKE_OK ) { packet.notify = ISAKMP_N_NO_PROPOSAL_CHOSEN; break; } // // obtain negotiated authentication type // IKE_PROPOSAL * proposal; ph1->plist_l.get( &proposal, 0 ); ph1->auth_id = proposal->auth_id; if( ( ph1->auth_id == XAUTH_AUTH_INIT_PSK ) || ( ph1->auth_id == XAUTH_AUTH_INIT_RSA ) || ( ph1->auth_id == HYBRID_AUTH_INIT_RSA ) ) ph1->vendopts_l.flag.xauth = true; // // setup the sa dhgroup for main mode // if( ph1->exchange == ISAKMP_EXCH_IDENT_PROTECT ) ph1->setup_dhgrp( proposal ); // // setup the sa transform // ph1->setup_xform( proposal ); ph1->lstate |= LSTATE_CHKPROP; } // // verify the peers identifier // if( ( ph1->xstate & XSTATE_RECV_ID ) && !( ph1->lstate & LSTATE_CHKIDS ) ) { result = phase1_chk_idr( ph1 ); if( result != LIBIKE_OK ) { packet.notify = ISAKMP_N_INVALID_ID_INFORMATION; break; } ph1->lstate |= LSTATE_CHKIDS; } break; } // // check the final paylaod process result // if( result != LIBIKE_OK ) { ph1->status( XCH_STATUS_DEAD, XCH_FAILED_MSG_FORMAT, packet.notify ); return result; } // // read next payload // payload = next_payload; } // // now build and send any response // packets that may be necessary // if( ph1->status() <= XCH_STATUS_DEAD ) process_phase1_send( ph1 ); return LIBIKE_OK; } long _IKED::process_phase1_send( IDB_PH1 * ph1 ) { // // main mode exchange // if( ph1->exchange == ISAKMP_EXCH_IDENT_PROTECT ) { // // isakmp initiator // if( ph1->initiator ) { // // sa + [ + vid's ] packet // if( !( ph1->xstate & XSTATE_SENT_SA ) ) { // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_SA, ph1->exchange, 0 ); // // add payloads // size_t beg = packet.size() + 4; payload_add_sa( packet, ph1->plist_l, ISAKMP_PAYLOAD_VEND ); size_t end = packet.size(); ph1->hda.set( packet.buff() + beg, end - beg ); phase1_add_vend( ph1, packet, ISAKMP_PAYLOAD_NONE ); packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, true ); ph1->xstate |= XSTATE_SENT_SA; } // // ke + nonce packet // if( ( ph1->xstate & XSTATE_RECV_SA ) && !( ph1->xstate & XSTATE_SENT_KE ) ) { // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_KEX, ph1->exchange, 0 ); // // add payloads for authentication type // payload_add_kex( packet, ph1->xl, ISAKMP_PAYLOAD_NONCE ); switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: { payload_add_nonce( packet, ph1->nonce_l, ph1->natt_pldtype ); break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: case HYBRID_AUTH_INIT_RSA: { payload_add_nonce( packet, ph1->nonce_l, ISAKMP_PAYLOAD_CERT_REQ ); payload_add_creq( packet, ISAKMP_CERT_X509_SIG, ph1->natt_pldtype ); ph1->xstate |= XSTATE_SENT_CR; break; } } // // optionally add nat discovery hash payloads // if( ph1->natt_version != IPSEC_NATT_NONE ) { phase1_gen_natd( ph1 ); payload_add_natd( packet, ph1->natd_ld, ph1->natt_pldtype ); payload_add_natd( packet, ph1->natd_ls, ISAKMP_PAYLOAD_NONE ); } packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, true ); ph1->xstate |= XSTATE_SENT_KE; ph1->xstate |= XSTATE_SENT_NO; } // // id + hash || id + [ cert ] + sig + [ cert req ] packet // if( ( ph1->xstate & XSTATE_RECV_KE ) && ( ph1->xstate & XSTATE_RECV_NO ) && !( ph1->xstate & XSTATE_SENT_ID ) ) { // // perform nat traversal check // phase1_chk_natd( ph1 ); // // calculate key material // phase1_gen_keys( ph1 ); // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_IDENT, ph1->exchange, ISAKMP_FLAG_ENCRYPT ); // // add payloads for authentication type // switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: case HYBRID_AUTH_INIT_RSA: { size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_HASH ); size_t pld_end = packet.size(); ph1->idi.set( packet.buff() + pld_beg, pld_end - pld_beg ); phase1_gen_hash_i( ph1, ph1->hash_l ); payload_add_hash( packet, ph1->hash_l, ISAKMP_PAYLOAD_NONE ); ph1->xstate |= XSTATE_SENT_HA; break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: { size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_CERT ); size_t pld_end = packet.size(); ph1->idi.set( packet.buff() + pld_beg, pld_end - pld_beg ); payload_add_cert( packet, ISAKMP_CERT_X509_SIG, ph1->tunnel->peer->cert_l, ISAKMP_PAYLOAD_SIGNATURE ); BDATA sign; phase1_gen_hash_i( ph1, ph1->hash_l ); prvkey_rsa_encrypt( ph1->tunnel->peer->key, ph1->hash_l, sign ); payload_add_sign( packet, sign, ISAKMP_PAYLOAD_NONE ); ph1->xstate |= XSTATE_SENT_CT; ph1->xstate |= XSTATE_SENT_SI; break; } } packet.done(); // // encrypt and send packet // packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_ID; } } // // isakmp responder // if( !ph1->initiator ) { // // sa packet // if( ( ph1->xstate & XSTATE_RECV_SA ) && !( ph1->xstate & XSTATE_SENT_SA ) ) { // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_SA, ph1->exchange, 0 ); // // add payloads // payload_add_sa( packet, ph1->plist_l, ISAKMP_PAYLOAD_VEND ); phase1_add_vend( ph1, packet, ISAKMP_PAYLOAD_NONE ); packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, true ); ph1->xstate |= XSTATE_SENT_SA; } // // ke + nonce packet // if( ( ph1->xstate & XSTATE_RECV_KE ) && ( ph1->xstate & XSTATE_RECV_NO ) && !( ph1->xstate & XSTATE_SENT_KE ) ) { // // write the packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_KEX, ph1->exchange, 0 ); // // add payloads for authentication type // payload_add_kex( packet, ph1->xl, ISAKMP_PAYLOAD_NONCE ); switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: { payload_add_nonce( packet, ph1->nonce_l, ph1->natt_pldtype ); break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: case HYBRID_AUTH_INIT_RSA: { payload_add_nonce( packet, ph1->nonce_l, ISAKMP_PAYLOAD_CERT_REQ ); payload_add_creq( packet, ISAKMP_CERT_X509_SIG, ph1->natt_pldtype ); ph1->xstate |= XSTATE_SENT_CR; break; } } // // optionally add nat discovery hash payloads // if( ph1->natt_version != IPSEC_NATT_NONE ) { phase1_gen_natd( ph1 ); payload_add_natd( packet, ph1->natd_ld, ph1->natt_pldtype ); payload_add_natd( packet, ph1->natd_ls, ISAKMP_PAYLOAD_NONE ); } packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, true ); ph1->xstate |= XSTATE_SENT_KE; ph1->xstate |= XSTATE_SENT_NO; // // calculate key material // phase1_gen_keys( ph1 ); } // // id + hash || id + [ cert ] + sign packet // if( ( ph1->xstate & XSTATE_RECV_ID ) && !( ph1->xstate & XSTATE_SENT_ID ) ) { // // perform nat traversal check // phase1_chk_natd( ph1 ); // // build packet for authentication type // switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: { if( !( ph1->xstate & XSTATE_RECV_HA ) ) break; // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_IDENT, ph1->exchange, ISAKMP_FLAG_ENCRYPT ); // // add payloads // size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_HASH ); size_t pld_end = packet.size(); ph1->idr.set( packet.buff() + pld_beg, pld_end - pld_beg ); phase1_gen_hash_r( ph1, ph1->hash_l ); payload_add_hash( packet, ph1->hash_l, ISAKMP_PAYLOAD_NONE ); packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_ID; ph1->xstate |= XSTATE_SENT_HA; break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: case HYBRID_AUTH_INIT_RSA: { if( ph1->auth_id == XAUTH_AUTH_INIT_RSA ) { // // mutual rsa modes we should see a signature // if( !( ph1->xstate & XSTATE_RECV_SI ) ) break; } else { // // hybrid rsa modes we should see a hash // if( !( ph1->xstate & XSTATE_RECV_HA ) ) break; } // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_IDENT, ph1->exchange, ISAKMP_FLAG_ENCRYPT ); // // add payloads // size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_CERT ); size_t pld_end = packet.size(); ph1->idr.set( packet.buff() + pld_beg, pld_end - pld_beg ); payload_add_cert( packet, ISAKMP_CERT_X509_SIG, ph1->tunnel->peer->cert_l, ISAKMP_PAYLOAD_SIGNATURE ); BDATA sign; phase1_gen_hash_r( ph1, ph1->hash_l ); prvkey_rsa_encrypt( ph1->tunnel->peer->key, ph1->hash_l, sign ); payload_add_sign( packet, sign, ISAKMP_PAYLOAD_NONE ); // // send packet // packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_ID; ph1->xstate |= XSTATE_SENT_CT; ph1->xstate |= XSTATE_SENT_SI; break; } } } } } // // aggressive mode exchange // if( ph1->exchange == ISAKMP_EXCH_AGGRESSIVE ) { // // isakmp initiator // if( ph1->initiator ) { // // sa + ke + no + id [ + vid's ] packet // if( !( ph1->xstate & XSTATE_SENT_SA ) ) { // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_SA, ph1->exchange, 0 ); // // add payloads for authentication type // size_t beg = packet.size() + 4; payload_add_sa( packet, ph1->plist_l, ISAKMP_PAYLOAD_KEX ); size_t end = packet.size(); ph1->hda.set( packet.buff() + beg, end - beg ); payload_add_kex( packet, ph1->xl, ISAKMP_PAYLOAD_NONCE ); switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: { payload_add_nonce( packet, ph1->nonce_l, ISAKMP_PAYLOAD_IDENT ); break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: case HYBRID_AUTH_INIT_RSA: { payload_add_nonce( packet, ph1->nonce_l, ISAKMP_PAYLOAD_CERT_REQ ); payload_add_creq( packet, ISAKMP_CERT_X509_SIG, ISAKMP_PAYLOAD_IDENT ); ph1->xstate |= XSTATE_SENT_CR; break; } } beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_VEND ); end = packet.size(); ph1->idi.set( packet.buff() + beg, end - beg ); phase1_add_vend( ph1, packet, ISAKMP_PAYLOAD_NONE ); packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, true ); ph1->xstate |= XSTATE_SENT_SA; ph1->xstate |= XSTATE_SENT_KE; ph1->xstate |= XSTATE_SENT_NO; ph1->xstate |= XSTATE_SENT_ID; } // // hash || [ cert + ] sig [ + natd + natd ] packet // if( ( ph1->xstate & XSTATE_RECV_KE ) && ( ph1->xstate & XSTATE_RECV_NO ) && ( ph1->xstate & XSTATE_RECV_ID ) ) { // // perform nat traversal check // phase1_chk_natd( ph1 ); // // build packet for authentication type // switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: case HYBRID_AUTH_INIT_RSA: { if( ( ( ( ph1->auth_id == IKE_AUTH_PRESHARED_KEY ) || ( ph1->auth_id == XAUTH_AUTH_INIT_PSK ) ) && ( ph1->xstate & XSTATE_RECV_HA ) && !( ph1->xstate & XSTATE_SENT_HA ) ) || ( ( ph1->auth_id == HYBRID_AUTH_INIT_RSA ) && ( ph1->xstate & XSTATE_RECV_SI ) && !( ph1->xstate & XSTATE_SENT_HA ) ) ) { // // calculate key material // phase1_gen_keys( ph1 ); // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_HASH, ph1->exchange, ISAKMP_FLAG_ENCRYPT ); // // add payloads // phase1_gen_hash_i( ph1, ph1->hash_l ); payload_add_hash( packet, ph1->hash_l, ph1->natt_pldtype ); // // optionally add nat discovery hash payloads // if( ph1->natt_version != IPSEC_NATT_NONE ) { phase1_gen_natd( ph1 ); payload_add_natd( packet, ph1->natd_ld, ph1->natt_pldtype ); payload_add_natd( packet, ph1->natd_ls, ISAKMP_PAYLOAD_NONE ); } packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_HA; } break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: { if( ( ph1->xstate & XSTATE_RECV_SI ) && !( ph1->xstate & XSTATE_SENT_SI ) ) { phase1_gen_keys( ph1 ); // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_CERT, ph1->exchange, ISAKMP_FLAG_ENCRYPT ); // // add payloads // payload_add_cert( packet, ISAKMP_CERT_X509_SIG, ph1->tunnel->peer->cert_l, ISAKMP_PAYLOAD_SIGNATURE ); BDATA sign; phase1_gen_hash_i( ph1, ph1->hash_l ); prvkey_rsa_encrypt( ph1->tunnel->peer->key, ph1->hash_l, sign ); payload_add_sign( packet, sign, ph1->natt_pldtype ); // // optionally add nat discovery hash payloads // if( ph1->natt_version != IPSEC_NATT_NONE ) { phase1_gen_natd( ph1 ); payload_add_natd( packet, ph1->natd_ld, ph1->natt_pldtype ); payload_add_natd( packet, ph1->natd_ls, ISAKMP_PAYLOAD_NONE ); } packet.done(); // // send packet // packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_CT; ph1->xstate |= XSTATE_SENT_SI; } break; } } } } // // isakmp responder // if( !ph1->initiator ) { // // sa + ke + no + hash || [ cert + ] sig [ + natd + natd ] [ + vid's ] packet // if( ( ph1->xstate & XSTATE_RECV_SA ) && ( ph1->xstate & XSTATE_RECV_KE ) && ( ph1->xstate & XSTATE_RECV_NO ) && ( ph1->xstate & XSTATE_RECV_ID ) && !( ph1->xstate & XSTATE_SENT_SA ) ) { // // generate our keys // phase1_gen_keys( ph1 ); // // write packet header // PACKET_IKE packet; packet.write( ph1->cookies, ISAKMP_PAYLOAD_SA, ph1->exchange, 0 ); // // add payloads // payload_add_sa( packet, ph1->plist_l, ISAKMP_PAYLOAD_KEX ); payload_add_kex( packet, ph1->xl, ISAKMP_PAYLOAD_NONCE ); payload_add_nonce( packet, ph1->nonce_l, ISAKMP_PAYLOAD_IDENT ); // // add payloads for authentication type // switch( ph1->auth_id ) { case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: case HYBRID_AUTH_INIT_RSA: { size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_HASH ); size_t pld_end = packet.size(); ph1->idr.set( packet.buff() + pld_beg, pld_end - pld_beg ); phase1_gen_hash_r( ph1, ph1->hash_l ); payload_add_hash( packet, ph1->hash_l, ISAKMP_PAYLOAD_VEND ); ph1->xstate |= XSTATE_SENT_HA; break; } case IKE_AUTH_SIG_RSA: case XAUTH_AUTH_INIT_RSA: { size_t pld_beg = packet.size() + 4; payload_add_ph1id( packet, ph1->ph1id_l, ISAKMP_PAYLOAD_CERT ); size_t pld_end = packet.size(); ph1->idr.set( packet.buff() + pld_beg, pld_end - pld_beg ); payload_add_cert( packet, ISAKMP_CERT_X509_SIG, ph1->tunnel->peer->cert_l, ISAKMP_PAYLOAD_CERT_REQ ); payload_add_creq( packet, ISAKMP_CERT_X509_SIG, ISAKMP_PAYLOAD_SIGNATURE ); BDATA sign; phase1_gen_hash_r( ph1, ph1->hash_l ); prvkey_rsa_encrypt( ph1->tunnel->peer->key, ph1->hash_l, sign ); payload_add_sign( packet, sign, ISAKMP_PAYLOAD_VEND ); ph1->xstate |= XSTATE_SENT_CT; ph1->xstate |= XSTATE_SENT_CR; ph1->xstate |= XSTATE_SENT_SI; break; } } phase1_add_vend( ph1, packet, ph1->natt_pldtype ); // // optionally add nat discovery hash payloads // if( ph1->natt_version != IPSEC_NATT_NONE ) { phase1_gen_natd( ph1 ); payload_add_natd( packet, ph1->natd_ld, ph1->natt_pldtype ); payload_add_natd( packet, ph1->natd_ls, ISAKMP_PAYLOAD_NONE ); } // // send packet // packet.done(); packet_ike_send( ph1, ph1, packet, false ); ph1->xstate |= XSTATE_SENT_SA; ph1->xstate |= XSTATE_SENT_KE; ph1->xstate |= XSTATE_SENT_NO; ph1->xstate |= XSTATE_SENT_ID; } // // post negotiation processing // if( ( ph1->xstate & XSTATE_RECV_HA ) || ( ph1->xstate & XSTATE_RECV_CT ) ) { // // check and enable natt if required // phase1_chk_natd( ph1 ); } } } // // is it time to verify our // peers hash or signature // if( ( ph1->status() < XCH_STATUS_MATURE ) && ( ph1->lstate & LSTATE_HASKEYS ) && ( ph1->xstate & XSTATE_RECV_ID ) ) { // // check the peers hash value // if( ( ph1->auth_id == IKE_AUTH_PRESHARED_KEY ) || ( ph1->auth_id == XAUTH_AUTH_INIT_PSK ) || ( ph1->auth_id == HYBRID_AUTH_INIT_RSA && !ph1->initiator ) ) { if( ph1->xstate & XSTATE_RECV_HA ) { if( phase1_chk_hash( ph1 ) == LIBIKE_OK ) ph1->status( XCH_STATUS_MATURE, XCH_NORMAL, 0 ); else ph1->status( XCH_STATUS_DEAD, XCH_FAILED_PEER_AUTH, ISAKMP_N_AUTHENTICATION_FAILED ); ph1->clean(); } } // // check the peers signature value // if( ( ph1->auth_id == IKE_AUTH_SIG_RSA ) || ( ph1->auth_id == XAUTH_AUTH_INIT_RSA ) || ( ph1->auth_id == HYBRID_AUTH_INIT_RSA && ph1->initiator ) ) { if( ph1->xstate & XSTATE_RECV_SI ) { if( phase1_chk_sign( ph1 ) == LIBIKE_OK ) ph1->status( XCH_STATUS_MATURE, XCH_NORMAL, 0 ); else ph1->status( XCH_STATUS_DEAD, XCH_FAILED_PEER_AUTH, ISAKMP_N_AUTHENTICATION_FAILED ); ph1->clean(); } } } // // if this tunnel has just now // completed initialization, we // may need to do a few things // if( ph1->status() == XCH_STATUS_MATURE ) { // // special handling for tunnel initialization // if( !( ph1->tunnel->tstate & TSTATE_INITIALIZED ) ) { // // send our inital contact notification // inform_new_notify( ph1, NULL, ISAKMP_N_INITIAL_CONTACT ); // // add tunnel natt event // if( ph1->tunnel->natt_version != IPSEC_NATT_NONE ) { ph1->tunnel->stats.natt = true; ph1->tunnel->inc( true ); ph1->tunnel->event_natt.delay = ph1->tunnel->peer->natt_rate * 1000; ith_timer.add( &ph1->tunnel->event_natt ); } // // add tunnel dpd event // if( ( ph1->tunnel->peer->dpd_mode == IPSEC_DPD_FORCE ) || ( ph1->vendopts_l.flag.dpdv1 && ph1->vendopts_r.flag.dpdv1 ) ) { ph1->tunnel->stats.dpd = true; ph1->tunnel->inc( true ); ph1->tunnel->event_dpd.delay = ph1->tunnel->peer->dpd_delay * 1000; ith_timer.add( &ph1->tunnel->event_dpd ); } // // flag ph1->tunnel as initialized // ph1->tunnel->tstate |= TSTATE_INITIALIZED; } // // if we are to generate a policy // list before config, do this now // if( !ph1->initiator ) if( ph1->tunnel->peer->plcy_mode == POLICY_MODE_COMPAT ) policy_list_create( ph1->tunnel, ph1->initiator ); // // determine if client configurtaion // processing should be initiated // switch( ph1->tunnel->peer->contact ) { case IPSEC_CONTACT_RESP: case IPSEC_CONTACT_BOTH: { // // inititate xauth operation if required // if( !ph1->initiator && ph1->vendopts_l.flag.xauth ) { IDB_CFG * cfg = new IDB_CFG( ph1, true ); cfg->add( true ); process_config_send( ph1, cfg ); cfg->dec( true ); } break; } case IPSEC_CONTACT_CLIENT: { // // peer will initiate xauth if required // if( ph1->initiator && !ph1->vendopts_l.flag.xauth ) { // // initiate the client configuration // processing unless in push mode // if( ph1->tunnel->peer->xconf_mode != CONFIG_MODE_PUSH ) { IDB_CFG * cfg = new IDB_CFG( ph1, true ); cfg->add( true ); process_config_send( ph1, cfg ); cfg->dec( true ); } } break; } } // // obtain our negotiated proposal // IKE_PROPOSAL * proposal; ph1->plist_l.get( &proposal, 0 ); // // add pahse1 soft expire event // ph1->inc( true ); ph1->event_soft.delay = proposal->life_sec; ph1->event_soft.delay *= PFKEY_SOFT_LIFETIME_RATE; ph1->event_soft.delay /= 100; ph1->event_soft.delay *= 1000; ith_timer.add( &ph1->event_soft ); // // add pahse1 hard expire event // ph1->inc( true ); ph1->event_hard.delay = proposal->life_sec; ph1->event_hard.delay *= 1000; ith_timer.add( &ph1->event_hard ); // // add pahse1 dead event // ph1->inc( true ); ph1->event_dead.delay = proposal->life_sec + 6; ph1->event_dead.delay *= 1000; ith_timer.add( &ph1->event_dead ); // // enable fragmentation support // if( ph1->vendopts_l.flag.frag && ph1->vendopts_r.flag.frag ) ph1->tunnel->stats.frag = true; // // locate any pending phase2 // handles for this tunnel and // begin negotiataions // IDB_PH2 * ph2; while( idb_list_ph2.find( true, &ph2, ph1->tunnel, XCH_STATUS_PENDING, XCH_STATUS_PENDING, NULL, NULL, NULL, NULL ) ) { // // begin negotiations // process_phase2_send( ph1, ph2 ); // // remove pending flag // ph2->status( XCH_STATUS_LARVAL, XCH_NORMAL, 0 ); ph2->dec( true ); } } return LIBIKE_OK; } long _IKED::phase1_gen_keys( IDB_PH1 * ph1 ) { // // obtain our negotiated proposal // IKE_PROPOSAL * proposal; ph1->plist_l.get( &proposal, 0 ); // // determine shared secret // if( level >= LLOG_DECODE ) { BDATA prv; prv.size( ph1->dh_size ); BN_bn2bin( ph1->dh->priv_key, prv.buff() ); log.bin( LLOG_DECODE, LLOG_DECODE, prv.buff(), prv.size(), "ii : computed DH private key" ); log.bin( LLOG_DECODE, LLOG_DECODE, ph1->xl.buff(), ph1->xl.size(), "ii : computed DH public key" ); log.bin( LLOG_DECODE, LLOG_DECODE, ph1->xr.buff(), ph1->xr.size(), "ii : received DH public key" ); } // // validate the dh group size // if( ph1->xr.size() != ph1->dh_size ) { log.txt( LLOG_ERROR, "!! : DH group size mismatch ( %d != %d )\n", ph1->xr.size(), ph1->dh_size ); ph1->status( XCH_STATUS_DEAD, XCH_FAILED_MSG_FORMAT, 0 ); return LIBIKE_FAILED; } // // determine shared secret // BIGNUM * gx = BN_new(); BN_bin2bn( ph1->xr.buff(), ph1->dh_size, gx ); BDATA shared; shared.set( 0, ph1->dh_size ); long result = DH_compute_key( shared.buff(), gx, ph1->dh ); BN_free( gx ); if( result < 0 ) { log.txt( LLOG_ERROR, "!! : failed to compute DH shared secret\n" ); ph1->status( XCH_STATUS_DEAD, XCH_FAILED_MSG_CRYPTO, 0 ); return LIBIKE_FAILED; } // // fixup shared secret buffer alignment // if( ph1->dh_size > result ) { log.txt( LLOG_DEBUG, "ww : short DH shared secret computed\n" ); shared.size( result ); shared.ins( 0, ph1->dh_size - result ); } log.bin( LLOG_DEBUG, LLOG_DECODE, shared.buff(), shared.size(), "== : DH shared secret" ); // // compute SKEYID // unsigned char skeyid_data[ HMAC_MAX_MD_CBLOCK ]; long skeyid_size = EVP_MD_size( ph1->evp_hash ); switch( proposal->auth_id ) { // // preshared key // // SKEYID = prf( pre-shared-key, Ni_b | Nr_b ) // case IKE_AUTH_PRESHARED_KEY: case XAUTH_AUTH_INIT_PSK: case XAUTH_AUTH_RESP_PSK: { HMAC_CTX ctx_prf; HMAC_Init( &ctx_prf, ph1->tunnel->peer->psk.buff(), ( int ) ph1->tunnel->peer->psk.size(), ph1->evp_hash ); if( ph1->initiator ) { HMAC_Update( &ctx_prf, ph1->nonce_l.buff(), ph1->nonce_l.size() ); HMAC_Update( &ctx_prf, ph1->nonce_r.buff(), ph1->nonce_r.size() ); } else { HMAC_Update( &ctx_prf, ph1->nonce_r.buff(), ph1->nonce_r.size() ); HMAC_Update( &ctx_prf, ph1->nonce_l.buff(), ph1->nonce_l.size() ); } HMAC_Final( &ctx_prf, skeyid_data, NULL ); HMAC_cleanup( &ctx_prf ); break; } // // digital signature // // SKEYID = prf( Ni_b | Nr_b, g^xy ) // case IKE_AUTH_SIG_RSA: case HYBRID_AUTH_INIT_RSA: case HYBRID_AUTH_RESP_RSA: case XAUTH_AUTH_INIT_RSA: case XAUTH_AUTH_RESP_RSA: { BDATA nonce; if( ph1->initiator ) { nonce.add( ph1->nonce_l ); nonce.add( ph1->nonce_r ); } else { nonce.add( ph1->nonce_r ); nonce.add( ph1->nonce_l ); } HMAC_CTX ctx_prf; HMAC_Init( &ctx_prf, nonce.buff(), ( int ) nonce.size(), ph1->evp_hash ); HMAC_Update( &ctx_prf, shared.buff(), shared.size() ); HMAC_Final( &ctx_prf, skeyid_data, NULL ); HMAC_cleanup( &ctx_prf ); break; } } ph1->skeyid.set( skeyid_data, skeyid_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, skeyid_data, skeyid_size, "== : SETKEYID" ); // // compute SKEYID_d // HMAC_CTX ctx_prf; HMAC_Init( &ctx_prf, ph1->skeyid.buff(), ( int ) ph1->skeyid.size(), ph1->evp_hash ); HMAC_Update( &ctx_prf, shared.buff(), shared.size() ); HMAC_Update( &ctx_prf, ph1->cookies.i, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ph1->cookies.r, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ( unsigned char * ) "\0", 1 ); HMAC_Final( &ctx_prf, skeyid_data, NULL ); HMAC_cleanup( &ctx_prf ); ph1->skeyid_d.set( skeyid_data, skeyid_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, skeyid_data, skeyid_size, "== : SETKEYID_d" ); // // compute SKEYID_a // HMAC_Init( &ctx_prf, ph1->skeyid.buff(), ( int ) ph1->skeyid.size(), ph1->evp_hash ); HMAC_Update( &ctx_prf, skeyid_data, skeyid_size ); HMAC_Update( &ctx_prf, shared.buff(), shared.size() ); HMAC_Update( &ctx_prf, ph1->cookies.i, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ph1->cookies.r, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ( unsigned char * ) "\1", 1 ); HMAC_Final( &ctx_prf, skeyid_data, NULL ); HMAC_cleanup( &ctx_prf ); ph1->skeyid_a.set( skeyid_data, skeyid_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, skeyid_data, skeyid_size, "== : SETKEYID_a" ); // // compute SKEYID_e // HMAC_Init( &ctx_prf, ph1->skeyid.buff(), ( int ) ph1->skeyid.size(), ph1->evp_hash ); HMAC_Update( &ctx_prf, skeyid_data, skeyid_size ); HMAC_Update( &ctx_prf, shared.buff(), shared.size() ); HMAC_Update( &ctx_prf, ph1->cookies.i, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ph1->cookies.r, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, ( unsigned char * ) "\2", 1 ); HMAC_Final( &ctx_prf, skeyid_data, NULL ); HMAC_cleanup( &ctx_prf ); ph1->skeyid_e.set( skeyid_data, skeyid_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, skeyid_data, skeyid_size, "== : SETKEYID_e" ); // // clobber dh shared secret // shared.del( true ); // compute cipher key data unsigned char key_data[ EVP_MAX_KEY_LENGTH + HMAC_MAX_MD_CBLOCK ]; long key_size = EVP_CIPHER_key_length( ph1->evp_cipher ); if( proposal->ciph_kl ) key_size = ( proposal->ciph_kl + 7 ) / 8; // // if the cipher requires more key material // than SKEYID_e provides, extend it using // a recursive algorithm // if( skeyid_size < key_size ) { // grow our key to be a multiple // of SKEYID_e ( HMAC block size ) if( key_size % skeyid_size ) key_size += skeyid_size - ( key_size % skeyid_size ); // create extended key data HMAC_Init( &ctx_prf, skeyid_data, skeyid_size, ph1->evp_hash ); HMAC_Update( &ctx_prf, ( unsigned char * ) "\0", 1 ); HMAC_Final( &ctx_prf, key_data, NULL ); for( long size = skeyid_size; size < key_size; size += skeyid_size ) { HMAC_Init( &ctx_prf, skeyid_data, skeyid_size, ph1->evp_hash ); HMAC_Update( &ctx_prf, key_data + size - skeyid_size, skeyid_size ); HMAC_Final( &ctx_prf, key_data + size, NULL ); } HMAC_cleanup( &ctx_prf ); } else { // copy the key data memcpy( key_data, skeyid_data, key_size ); } if( proposal->ciph_kl ) key_size = ( proposal->ciph_kl + 7 ) / 8; ph1->key.set( key_data, key_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, key_data, key_size, "== : cipher key" ); // // compute cipher iv data // unsigned char iv_data[ HMAC_MAX_MD_CBLOCK ]; unsigned long iv_size = EVP_CIPHER_iv_length( ph1->evp_cipher ); EVP_MD_CTX ctx_hash; EVP_DigestInit( &ctx_hash, ph1->evp_hash ); if( ph1->initiator ) { EVP_DigestUpdate( &ctx_hash, ph1->xl.buff(), ph1->xl.size() ); EVP_DigestUpdate( &ctx_hash, ph1->xr.buff(), ph1->xr.size() ); } else { EVP_DigestUpdate( &ctx_hash, ph1->xr.buff(), ph1->xr.size() ); EVP_DigestUpdate( &ctx_hash, ph1->xl.buff(), ph1->xl.size() ); } EVP_DigestFinal( &ctx_hash, iv_data, NULL ); EVP_MD_CTX_cleanup( &ctx_hash ); ph1->iv.set( iv_data, iv_size ); log.bin( LLOG_DEBUG, LLOG_DECODE, iv_data, iv_size, "== : cipher iv" ); // // flag key material calculated // ph1->lstate |= LSTATE_HASKEYS; return LIBIKE_OK; } long _IKED::phase1_gen_hash_i( IDB_PH1 * sa, BDATA & hash ) { // // compute the initiators hash // hash.size( sa->hash_size ); HMAC_CTX ctx_prf; HMAC_Init( &ctx_prf, sa->skeyid.buff(), ( int ) sa->skeyid.size(), sa->evp_hash ); if( sa->initiator ) { HMAC_Update( &ctx_prf, sa->xl.buff(), sa->xl.size() ); HMAC_Update( &ctx_prf, sa->xr.buff(), sa->xr.size() ); } else { HMAC_Update( &ctx_prf, sa->xr.buff(), sa->xr.size() ); HMAC_Update( &ctx_prf, sa->xl.buff(), sa->xl.size() ); } HMAC_Update( &ctx_prf, sa->cookies.i, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, sa->cookies.r, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, sa->hda.buff(), sa->hda.size() ); HMAC_Update( &ctx_prf, sa->idi.buff(), sa->idi.size() ); HMAC_Final( &ctx_prf, hash.buff(), NULL ); HMAC_cleanup( &ctx_prf ); log.bin( LLOG_DEBUG, LLOG_DECODE, hash.buff(), hash.size(), "== : phase1 hash_i ( computed )" ); return LIBIKE_OK; } long _IKED::phase1_gen_hash_r( IDB_PH1 * sa, BDATA & hash ) { // // compute the responders hash // hash.size( sa->hash_size ); HMAC_CTX ctx_prf; HMAC_Init( &ctx_prf, sa->skeyid.buff(), ( int ) sa->skeyid.size(), sa->evp_hash ); if( sa->initiator ) { HMAC_Update( &ctx_prf, sa->xr.buff(), sa->xr.size() ); HMAC_Update( &ctx_prf, sa->xl.buff(), sa->xl.size() ); } else { HMAC_Update( &ctx_prf, sa->xl.buff(), sa->xl.size() ); HMAC_Update( &ctx_prf, sa->xr.buff(), sa->xr.size() ); } HMAC_Update( &ctx_prf, sa->cookies.r, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, sa->cookies.i, ISAKMP_COOKIE_SIZE ); HMAC_Update( &ctx_prf, sa->hda.buff(), sa->hda.size() ); HMAC_Update( &ctx_prf, sa->idr.buff(), sa->idr.size() ); HMAC_Final( &ctx_prf, hash.buff(), NULL ); HMAC_cleanup( &ctx_prf ); log.bin( LLOG_DEBUG, LLOG_DECODE, hash.buff(), hash.size(), "== : phase1 hash_r ( computed )" ); return LIBIKE_OK; } inline uint8_t vendpld( long & count, uint8_t next ) { if( --count ) return ISAKMP_PAYLOAD_VEND; else return next; } long _IKED::phase1_add_vend( IDB_PH1 * ph1, PACKET_IKE & packet, uint8_t next ) { // // determine vendor id count // long vid_count = 0; if( ph1->vendopts_l.flag.xauth ) vid_count++; if( ph1->vendopts_l.flag.natt ) { if( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) vid_count += 5; if( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_DRAFT ) vid_count += 2; if( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_RFC ) vid_count += 3; } if( ph1->vendopts_l.flag.frag ) vid_count++; if( ph1->vendopts_l.flag.dpdv1 ) vid_count++; if( ph1->vendopts_l.flag.ssoft ) vid_count++; if( ph1->vendopts_l.flag.unity ) vid_count++; if( ph1->vendopts_l.flag.netsc ) vid_count++; if( ph1->vendopts_l.flag.swind ) vid_count++; if( ph1->vendopts_l.flag.chkpt ) vid_count++; // // optionally add xauth vendor id payload // if( ph1->vendopts_l.flag.xauth ) { payload_add_vend( packet, vend_xauth, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports XAUTH\n" ); } // // optionally add natt vendor id payloads // if( ph1->vendopts_l.flag.natt ) { // // add natt draft 00-01 id paylaods // if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_DRAFT ) ) { payload_add_vend( packet, vend_natt_v00, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports nat-t ( draft v00 )\n" ); payload_add_vend( packet, vend_natt_v01, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports nat-t ( draft v01 )\n" ); } // // add natt draft 02-rfc id paylaods // if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_RFC ) ) { payload_add_vend( packet, vend_natt_v02, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports nat-t ( draft v02 )\n" ); payload_add_vend( packet, vend_natt_v03, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports nat-t ( draft v03 )\n" ); payload_add_vend( packet, vend_natt_rfc, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports nat-t ( rfc )\n" ); } } // // optionally add fragmentation vendor id payload // if( ph1->vendopts_l.flag.frag ) { payload_add_vend( packet, vend_frag, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports FRAGMENTATION\n" ); } // // optionally add dpd vendor id payload // if( ph1->vendopts_l.flag.dpdv1 ) { payload_add_vend( packet, vend_dpd1, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local supports DPDv1\n" ); } // // optionally add shrew soft vendor id payload // if( ph1->vendopts_l.flag.ssoft ) { payload_add_vend( packet, vend_ssoft, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local is SHREW SOFT compatible\n" ); } // // optionally add unity vendor id payload // if( ph1->vendopts_l.flag.unity ) { payload_add_vend( packet, vend_unity, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local is CISCO UNITY compatible\n" ); } // // optionally add netscreen vendor payload // if( ph1->vendopts_l.flag.netsc ) { payload_add_vend( packet, vend_netsc, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local is NETSCREEN compatible\n" ); } // // optionally add sidewinder vendor payload // if( ph1->vendopts_l.flag.swind ) { payload_add_vend( packet, vend_swind, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local is SIDEWINDER compatible\n" ); } // // prepair and optionally add checkpoint vendor // id payload ( must be last ) // if( ph1->vendopts_l.flag.chkpt ) { uint32_t prod; uint32_t vers; uint32_t feat; if( ph1->tunnel->peer->contact != IPSEC_CONTACT_CLIENT ) prod = htonl( 1 ); // 01 == gateway else prod = htonl( 2 ); // 02 == client vers = htonl( 5006 ); // NG AI R56 feat = htonl( 0x18800000 ); // all features BDATA vend_chkpt2; vend_chkpt2.add( vend_chkpt ); // base vendor id vend_chkpt2.add( &prod, 4 ); // client vend_chkpt2.add( &vers, 4 ); // version vend_chkpt2.add( 0, 4 ); // timestamp vend_chkpt2.add( 0, 4 ); // reserved vend_chkpt2.add( &feat, 4 ); // features payload_add_vend( packet, vend_chkpt2, vendpld( vid_count, next ) ); log.txt( LLOG_INFO, "ii : local is CHECKPOINT compatible\n" ); } return LIBIKE_OK; } inline bool vendcmp( BDATA & vend1, BDATA & vend2, bool prefix = false ) { if( prefix ) { if( vend1.size() < vend2.size() ) return false; } else { if( vend1.size() != vend2.size() ) return false; } return !memcmp( vend1.buff(), vend2.buff(), vend2.size() ); } long _IKED::phase1_chk_vend( IDB_PH1 * ph1, BDATA & vend ) { // // check for xauth vendor id // if( vendcmp( vend, vend_xauth ) ) { ph1->vendopts_r.flag.xauth = true; log.txt( LLOG_INFO, "ii : peer supports XAUTH\n" ); return LIBIKE_OK; } // // check for fragmentation vendor id // if( vendcmp( vend, vend_frag ) ) { ph1->vendopts_r.flag.frag = true; log.txt( LLOG_INFO, "ii : peer supports FRAGMENTATION\n" ); return LIBIKE_OK; } // // check for dead peer detection vendor id // if( vendcmp( vend, vend_dpd1 ) ) { ph1->vendopts_r.flag.dpdv1 = true; log.txt( LLOG_INFO, "ii : peer supports DPDv1\n" ); return LIBIKE_OK; } // // check for heartbeat notify detection vendor id // if( vendcmp( vend, vend_hbeat ) ) { ph1->vendopts_r.flag.hbeat = true; log.txt( LLOG_INFO, "ii : peer supports HEARTBEAT-NOTIFY\n" ); return LIBIKE_OK; } // // check for natt v00 vendor id // if( vendcmp( vend, vend_natt_v00 ) ) { if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_DRAFT ) ) { if( ph1->natt_version < IPSEC_NATT_V00 ) { ph1->vendopts_r.flag.natt = true; ph1->natt_version = IPSEC_NATT_V00; ph1->natt_pldtype = ISAKMP_PAYLOAD_NAT_VXX_DISC; } } log.txt( LLOG_INFO, "ii : peer supports nat-t ( draft v00 )\n" ); return LIBIKE_OK; } // // check for natt v01 vendor id // if( vendcmp( vend, vend_natt_v01 ) ) { if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_DRAFT ) ) { if( ph1->natt_version < IPSEC_NATT_V01 ) { ph1->vendopts_r.flag.natt = true; ph1->natt_version = IPSEC_NATT_V01; ph1->natt_pldtype = ISAKMP_PAYLOAD_NAT_VXX_DISC; } } log.txt( LLOG_INFO, "ii : peer supports nat-t ( draft v01 )\n" ); return LIBIKE_OK; } // // check for natt v02 vendor id // if( vendcmp( vend, vend_natt_v02 ) ) { if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_RFC ) ) { if( ph1->natt_version < IPSEC_NATT_V02 ) { ph1->vendopts_r.flag.natt = true; ph1->natt_version = IPSEC_NATT_V02; ph1->natt_pldtype = ISAKMP_PAYLOAD_NAT_VXX_DISC; } } log.txt( LLOG_INFO, "ii : peer supports nat-t ( draft v02 )\n" ); return LIBIKE_OK; } // // check for natt v03 vendor id // if( vendcmp( vend, vend_natt_v03 ) ) { if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_RFC ) ) { if( ph1->natt_version < IPSEC_NATT_V03 ) { ph1->vendopts_r.flag.natt = true; ph1->natt_version = IPSEC_NATT_V03; ph1->natt_pldtype = ISAKMP_PAYLOAD_NAT_VXX_DISC; } } log.txt( LLOG_INFO, "ii : peer supports nat-t ( draft v03 )\n" ); return LIBIKE_OK; } // // check for natt rfc vendor id // if( vendcmp( vend, vend_natt_rfc ) ) { if( ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_ENABLE ) || ( ph1->tunnel->peer->natt_mode == IPSEC_NATT_FORCE_RFC ) ) { ph1->vendopts_r.flag.natt = true; ph1->natt_version = IPSEC_NATT_RFC; ph1->natt_pldtype = ISAKMP_PAYLOAD_NAT_RFC_DISC; } log.txt( LLOG_INFO, "ii : peer supports nat-t ( rfc )\n" ); return LIBIKE_OK; } // // check for shrew soft vendor id // if( vendcmp( vend, vend_ssoft ) ) { ph1->vendopts_r.flag.ssoft = true; ph1->vendopts_r.flag.unity = true; log.txt( LLOG_INFO, "ii : peer is SHREW SOFT compatible\n" ); return LIBIKE_OK; } // // check for kame vendor id // if( vendcmp( vend, vend_kame ) ) { ph1->vendopts_r.flag.kame = true; log.txt( LLOG_INFO, "ii : peer is IPSEC-TOOLS compatible\n" ); return LIBIKE_OK; } // // check for unity vendor id // if( vendcmp( vend, vend_unity ) ) { ph1->vendopts_r.flag.unity = true; log.txt( LLOG_INFO, "ii : peer is CISCO UNITY compatible\n" ); return LIBIKE_OK; } // // check for netscreen vendor id // if( vendcmp( vend, vend_netsc ) ) { ph1->vendopts_r.flag.netsc = true; log.txt( LLOG_INFO, "ii : peer is NETSCREEN compatible\n" ); return LIBIKE_OK; } // // check for zywall vendor id // if( vendcmp( vend, vend_zwall ) ) { ph1->vendopts_r.flag.zwall = true; log.txt( LLOG_INFO, "ii : peer is ZYWALL compatible\n" ); return LIBIKE_OK; } // // check for sidewinder vendor id // if( vendcmp( vend, vend_swind ) ) { ph1->vendopts_r.flag.swind = true; log.txt( LLOG_INFO, "ii : peer is SIDEWINDER compatible\n" ); return LIBIKE_OK; } // // check for checkpoint vendor id // if( vendcmp( vend, vend_chkpt, true ) ) { ph1->vendopts_r.flag.chkpt = true; log.txt( LLOG_INFO, "ii : peer is CHECKPOINT compatible\n" ); return LIBIKE_OK; } log.bin( LLOG_DEBUG, LLOG_DEBUG, vend.buff(), vend.size(), "ii : unknown vendor id" ); return LIBIKE_OK; } long _IKED::phase1_chk_hash( IDB_PH1 * ph1 ) { // // generate hash data for comparison // BDATA hash; if( ph1->initiator ) { phase1_gen_hash_r( ph1, hash ); log.bin( LLOG_DEBUG, LLOG_DECODE, ph1->hash_r.buff(), hash.size(), "== : phase1 hash_r ( received )" ); } else { phase1_gen_hash_i( ph1, hash ); log.bin( LLOG_DEBUG, LLOG_DECODE, ph1->hash_r.buff(), hash.size(), "== : phase1 hash_i ( received )" ); } char txtaddr_l[ LIBIKE_MAX_TEXTADDR ]; char txtaddr_r[ LIBIKE_MAX_TEXTADDR ]; text_addr( txtaddr_l, &ph1->tunnel->saddr_l, true ); text_addr( txtaddr_r, &ph1->tunnel->saddr_r, true ); long result = memcmp( hash.buff(), ph1->hash_r.buff(), ph1->hash_size ); if( result ) { log.txt( LLOG_INFO, "!! : phase1 sa rejected, invalid auth data\n" "!! : %s <-> %s\n" "!! : %04x%04x:%04x%04x\n", txtaddr_l, txtaddr_r, htonl( *( long * ) &ph1->cookies.i[ 0 ] ), htonl( *( long * ) &ph1->cookies.i[ 4 ] ), htonl( *( long * ) &ph1->cookies.r[ 0 ] ), htonl( *( long * ) &ph1->cookies.r[ 4 ] ) ); return LIBIKE_FAILED; } log.txt( LLOG_INFO, "ii : phase1 sa established\n" "ii : %s <-> %s\n" "ii : %04x%04x:%04x%04x\n", txtaddr_r, txtaddr_l, htonl( *( long * ) &ph1->cookies.i[ 0 ] ), htonl( *( long * ) &ph1->cookies.i[ 4 ] ), htonl( *( long * ) &ph1->cookies.r[ 0 ] ), htonl( *( long * ) &ph1->cookies.r[ 4 ] ) ); return LIBIKE_OK; } long _IKED::phase1_chk_sign( IDB_PH1 * ph1 ) { // // verify the peer certificates // using the ca cert specified // in the peer configuration // BDATA cert; if( !cert_verify( ph1->certs_r, ph1->tunnel->peer->cert_r, cert ) ) { log.txt( LLOG_ERROR, "!! : unable to verify remote peer certificate\n" ); return LIBIKE_FAILED; } // // read the public key from the // peer provided certificate // EVP_PKEY * evp_pkey = NULL; if( !pubkey_rsa_read( cert, &evp_pkey ) ) { log.txt( LLOG_ERROR, "!! : unable to extract public key from remote peer certificate\n" ); return LIBIKE_FAILED; } // // use the public key to decrypt // the signiature data provided // by the remote peer // if( !pubkey_rsa_decrypt( evp_pkey, ph1->sign_r, ph1->hash_r ) ) { log.txt( LLOG_ERROR, "!! : unable to compute remote peer signed hash\n" ); return LIBIKE_FAILED; } // // check hash value // return phase1_chk_hash( ph1 ); } long _IKED::phase1_gen_natd( IDB_PH1 * ph1 ) { // // compute the nat discovery // hash for local address // if( !ph1->natd_ls.size( ph1->hash_size ) ) return LIBIKE_MEMORY; EVP_MD_CTX ctx_hash; EVP_DigestInit( &ctx_hash, ph1->evp_hash ); EVP_DigestUpdate( &ctx_hash, ph1->cookies.i, ISAKMP_COOKIE_SIZE ); EVP_DigestUpdate( &ctx_hash, ph1->cookies.r, ISAKMP_COOKIE_SIZE ); EVP_DigestUpdate( &ctx_hash, &ph1->tunnel->saddr_l.saddr4.sin_addr.s_addr, 4 ); EVP_DigestUpdate( &ctx_hash, &ph1->tunnel->saddr_l.saddr4.sin_port, 2 ); EVP_DigestFinal( &ctx_hash, ph1->natd_ls.buff(), NULL ); EVP_MD_CTX_cleanup( &ctx_hash ); // // compute the nat discovery // hash for remote address // if( !ph1->natd_ld.size( ph1->hash_size ) ) return LIBIKE_MEMORY; EVP_DigestInit( &ctx_hash, ph1->evp_hash ); EVP_DigestUpdate( &ctx_hash, ph1->cookies.i, ISAKMP_COOKIE_SIZE ); EVP_DigestUpdate( &ctx_hash, ph1->cookies.r, ISAKMP_COOKIE_SIZE ); EVP_DigestUpdate( &ctx_hash, &ph1->tunnel->saddr_r.saddr4.sin_addr.s_addr, 4 ); EVP_DigestUpdate( &ctx_hash, &ph1->tunnel->saddr_r.saddr4.sin_port, 2 ); EVP_DigestFinal( &ctx_hash, ph1->natd_ld.buff(), NULL ); EVP_MD_CTX_cleanup( &ctx_hash ); ph1->lstate |= LSTATE_GENNATD; return LIBIKE_OK; } bool _IKED::phase1_chk_natd( IDB_PH1 * ph1 ) { // // if we are rekeying, skip this // if( ph1->tunnel->lstate & TSTATE_NATT_FLOAT ) return true; // // verify that both support natt // switch( ph1->tunnel->peer->natt_mode ) { case IPSEC_NATT_DISABLE: { log.txt( LLOG_INFO, "ii : nat-t is disabled locally\n" ); break; } case IPSEC_NATT_ENABLE: { bool xlated = false; // // make sure remote peer negotiated natt // if( !ph1->natt_version ) { log.txt( LLOG_INFO, "ii : nat-t is unsupported by remote peer\n" ); break; } // // generate nat discovery if neccessary // if( !( ph1->lstate & LSTATE_GENNATD ) ) phase1_gen_natd( ph1 ); // // compare the remote destination // hash to the local source hash // if( ph1->natd_rd.size() == ph1->natd_ls.size() ) { if( memcmp( ph1->natd_rd.buff(), ph1->natd_ls.buff(), ph1->natd_ls.size() ) ) { log.txt( LLOG_INFO, "ii : nat discovery - local address is translated\n" ); xlated = true; } } // // compare the remote source hash // to the local destination hash // if( ph1->natd_rs.size() == ph1->natd_ld.size() ) { if( memcmp( ph1->natd_rs.buff(), ph1->natd_ld.buff(), ph1->natd_ld.size() ) ) { log.txt( LLOG_INFO, "ii : nat discovery - remote address is translated\n" ); xlated = true; } } // // only set the nat-t port if translation was detected // if( xlated ) { ph1->tunnel->natt_version = ph1->natt_version; break; } log.txt( LLOG_INFO, "ii : disabled nat-t ( no nat detected )\n" ); break; } case IPSEC_NATT_FORCE_DRAFT: log.txt( LLOG_INFO, "ii : forcing nat-t to enabled ( draft )\n" ); // // set natt to negotiated version or draft v00 // if( ph1->natt_version != IPSEC_NATT_NONE ) ph1->tunnel->natt_version = ph1->natt_version; else ph1->tunnel->natt_version = IPSEC_NATT_V00; break; case IPSEC_NATT_FORCE_RFC: log.txt( LLOG_INFO, "ii : forcing nat-t to enabled ( rfc )\n" ); // // set natt to negotiated version or rfc // if( ph1->natt_version != IPSEC_NATT_NONE ) ph1->tunnel->natt_version = ph1->natt_version; else ph1->tunnel->natt_version = IPSEC_NATT_RFC; break; } // // switch to natt ports if required // ph1->tunnel->lstate |= TSTATE_NATT_FLOAT; if( ph1->tunnel->natt_version >= IPSEC_NATT_V02 ) { // // switch our port to natt // ph1->tunnel->saddr_l.saddr4.sin_port = ph1->tunnel->peer->natt_port; // // switch the peer port to natt // ph1->tunnel->saddr_r.saddr4.sin_port = ph1->tunnel->peer->natt_port; log.txt( LLOG_INFO, "ii : switching to nat-t udp port %u\n", ntohs( ph1->tunnel->peer->natt_port ) ); // // setup our filter // #ifdef WIN32 iked.tunnel_filter_add( ph1->tunnel, true ); #endif return true; } return false; } bool _IKED::phase1_chk_port( IDB_PH1 * ph1, IKE_SADDR * saddr_r, IKE_SADDR * saddr_l ) { // // check if our peer has floated ports // if( saddr_r->saddr4.sin_port != ph1->tunnel->saddr_r.saddr4.sin_port ) { if( ph1->initiator ) { if( ph1->tunnel->peer->natt_mode == IPSEC_NATT_NONE ) { log.txt( LLOG_INFO, "ii : initiator port values floated but nat-t is disabled\n" ); return false; } if( ph1->tunnel->lstate & TSTATE_NATT_FLOAT ) { log.txt( LLOG_INFO, "ww : initiator port values should only float once per session\n" ); return true; } } else { log.txt( LLOG_DEBUG, "ii : responder port values have floated\n" ); } // // float our port to natt // ph1->tunnel->saddr_l.saddr4.sin_port = saddr_l->saddr4.sin_port; // // float the peer port to natt // ph1->tunnel->saddr_r.saddr4.sin_port = saddr_r->saddr4.sin_port; // // switch the tunnel to the // negotiated natt version // ph1->tunnel->natt_version = ph1->natt_version; log.txt( LLOG_INFO, "ii : floating to nat-t udp ports %u -> %u\n", ntohs( ph1->tunnel->saddr_r.saddr4.sin_port ), ntohs( ph1->tunnel->saddr_l.saddr4.sin_port ) ); // // setup our filter // #ifdef WIN32 iked.tunnel_filter_add( ph1->tunnel, true ); #endif ph1->tunnel->lstate |= TSTATE_NATT_FLOAT; } return true; } long _IKED::phase1_chk_idr( IDB_PH1 * ph1 ) { // // create a phase1 remote id based // on our tunnel configuration data // IKE_PH1ID idt; if( !gen_ph1id_r( ph1, idt ) ) return LIBIKE_FAILED; // // compare the id values // if( !cmp_ph1id( idt, ph1->ph1id_r, ph1->vendopts_l.flag.natt ) ) return LIBIKE_FAILED; return LIBIKE_OK; }