Logo Search packages:      
Sourcecode: ike version File versions

ike.cpp

/*
 * 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"

static char encap_ike[] = "IKE";
static char encap_nat[] = "NAT-T:IKE";

long _IKED::packet_ike_send( IDB_PH1 * ph1, IDB_XCH * xch, PACKET_IKE & packet, bool retry )
{
      //
      // if we are dumping decrytped ike
      // packets, we need to build an ip
      // packet using the un-encrypted
      // ike payload
      //

      if( dump_decrypt )
      {
            PACKET_IP packet_ip_dump;

            //
            // store the isakmp flags
            //

            unsigned char *   data = packet.buff();
            unsigned char     flags = data[ ISAKMP_FLAGS_OFFSET ];

            //
            // temporarily strip the encrypt flag
            //

            data[ ISAKMP_FLAGS_OFFSET ] = flags & ~ISAKMP_FLAG_ENCRYPT;

            //
            // create ip dump packet
            //

            packet_ike_encap(
                  packet,
                  packet_ip_dump,
                  ph1->tunnel->saddr_l,
                  ph1->tunnel->saddr_r,
                  ph1->tunnel->natt_version );

            //
            // obtain ethernet header
            //

            ETH_HEADER ethhdr;
            header( packet_ip_dump, ethhdr );

            //
            // dump the ike packet
            //

            pcap_decrypt.dump( ethhdr, packet_ip_dump );

            //
            // restore the isakmp flags
            //

            data[ ISAKMP_FLAGS_OFFSET ] = flags;
      }

      //
      // encrypt the packet if necessary
      //

      packet_ike_encrypt( ph1, packet, &xch->iv );

      //
      // clear any old packets associated
      // with this db object
      //

      xch->resend_clear( true, true );

      //
      // estimate the maximum packet size
      // after ike encapsulation overhead
      //

      size_t encap_size = 0;

      // account for udp header length
      encap_size += sizeof( UDP_HEADER );

      // account for non-esp marker
      if( ph1->tunnel->natt_version >= IPSEC_NATT_V02 )
            encap_size += sizeof( unsigned long );

      //
      // determine if ike fragmentation
      // will be used on this packet
      //

      bool packet_frag = false;
      size_t packet_left = packet.size();

      if( ph1->vendopts_l.flag.frag &&
            ph1->vendopts_r.flag.frag )
            if( ( packet_left + encap_size ) > ph1->tunnel->peer->frag_ike_size )
                  packet_frag = true;

      //
      // fragment and send the packet
      // or just send the packet
      //

      if( packet_frag )
      {
            //
            // calculate the max fragment payload size
            //

            size_t frag_max = ph1->tunnel->peer->frag_ike_size - encap_size;
            size_t frag_sent = packet.size() - packet_left;

            //
            // set out initial fragment index
            //

            unsigned char frag_index = 1;

            //
            // fragment the ike packet by
            // recursing on this function
            //

            while( packet_left > 0 )
            {
                  //
                  // send the remaining packet size
                  // as out next fragment size. the
                  // payload handler will adjust to
                  // accomodate the largest size
                  // possible
                  //

                  size_t frag_size = packet_left;

                  //
                  // create ike fragment using the
                  // original ike packet as a source
                  //

                  PACKET_IKE packet_frag;
                  packet_frag.write( ph1->cookies, ISAKMP_PAYLOAD_FRAGMENT, ph1->exchange, 0 );

                  payload_add_frag(
                        packet_frag,
                        frag_index,
                        packet.buff() + frag_sent,
                        frag_size,
                        frag_max );

                  packet_frag.done();

                  //
                  // transmit the packet
                  //

                  packet_ike_xmit( ph1, xch, packet_frag, retry );

                  //
                  // log the result
                  //

                  log.txt( LLOG_DEBUG,
                        "ii : ike packet fragment #%i sized to %i bytes\n",
                        frag_index,
                        frag_size );

                  //
                  // decrement sent packet size and
                  // increment fragment index
                  //

                  packet_left -= frag_size;
                  frag_sent += frag_size;
                  frag_index++;
            }
      }
      else
      {
            //
            // transmit the packet
            //

            packet_ike_xmit( ph1, xch, packet, retry );
      }

      //
      // potentially schedule resend
      //

      if( retry )
            xch->resend_sched( true );

      return LIBIKE_OK;
}

long _IKED::packet_ike_xmit( IDB_PH1 * ph1, IDB_XCH * xch, PACKET_IKE & packet, bool retry )
{
      //
      // prepare for log output
      //

      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 );

      char * encap_mode = encap_ike;
      if( ph1->tunnel->natt_version != IPSEC_NATT_NONE )
            encap_mode = encap_nat;

      //
      // encapsulate ike packet into UDP/IP packet
      //

      PACKET_IP packet_ip;
      packet_ike_encap(
            packet,
            packet_ip,
            ph1->tunnel->saddr_l,
            ph1->tunnel->saddr_r,
            ph1->tunnel->natt_version );

      //
      // log the result
      //

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            packet_ip.buff(),
            packet_ip.size(),
            "-> : send %s packet %s -> %s",
            encap_mode,
            txtaddr_l,
            txtaddr_r );

      //
      // send ike packet
      //

      ETH_HEADER header;

      long result = send_ip(
                              packet_ip,
                              &header );

      if( result == LIBIKE_SOCKET )
      {
            ph1->tunnel->close = XCH_FAILED_NETWORK;
            return LIBIKE_FAILED;
      }

      //
      // queue packet for resend
      //

      xch->resend_queue( packet_ip );

      //
      // dump for encoded packets
      //

      if( dump_encrypt )
            pcap_encrypt.dump( header, packet_ip );

      return LIBIKE_OK;
}

long _IKED::packet_ike_encap( PACKET_IKE & packet_ike, PACKET_IP & packet_ip, IKE_SADDR & src, IKE_SADDR & dst, long natt )
{
      PACKET_UDP packet_udp;

      packet_udp.write(
            src.saddr4.sin_port,
            dst.saddr4.sin_port );

      if( natt >= IPSEC_NATT_V02 )
            packet_udp.add_null( 4 );

      packet_udp.add( packet_ike );

      packet_udp.done(
            src.saddr4.sin_addr,
            dst.saddr4.sin_addr );

      packet_ip.write(
            src.saddr4.sin_addr,
            dst.saddr4.sin_addr,
            ident++,
            PROTO_IP_UDP );

      packet_ip.add( packet_udp );

      packet_ip.done();

      return true;
}

long _IKED::packet_ike_decrypt( IDB_PH1 * sa, PACKET_IKE & packet, BDATA * iv )
{
      log.txt( LLOG_INFO,
            "=< : cookies %08x%08x:%08x%08x\n",
            htonl( *( long * ) &sa->cookies.i[ 0 ] ),
            htonl( *( long * ) &sa->cookies.i[ 4 ] ),
            htonl( *( long * ) &sa->cookies.r[ 0 ] ),
            htonl( *( long * ) &sa->cookies.r[ 4 ] ) );

      log.txt( LLOG_INFO,
            "=< : message %08x\n",
            htonl( packet.get_msgid() ) );

      //
      // check if decrypt is required
      //

      unsigned char *   data = packet.buff();
      size_t                size = packet.size();

      if( !( data[ ISAKMP_FLAGS_OFFSET ] & ISAKMP_FLAG_ENCRYPT ) )
            return LIBIKE_OK;

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            iv->buff(),
            iv->size(),
            "=< : decrypt iv" );

      //
      // temporarily save enough
      // of the packet to store
      // as iv data post decrypt
      //

      unsigned char iv_data[ HMAC_MAX_MD_CBLOCK ];

      memcpy(
            iv_data,
            data + size - iv->size(),
            iv->size() );

      //
      // init cipher key and iv
      //

      EVP_CIPHER_CTX ctx_cipher;
      EVP_CIPHER_CTX_init( &ctx_cipher );

      EVP_CipherInit_ex(
            &ctx_cipher,
            sa->evp_cipher,
            NULL,
            NULL,
            NULL,
            0 );

      EVP_CIPHER_CTX_set_key_length(
            &ctx_cipher,
            ( int ) sa->key.size() );

      EVP_CipherInit_ex(
            &ctx_cipher,
            NULL,
            NULL,
            sa->key.buff(),
            iv->buff(),
            0 );

      //
      // decrypt all but header
      //

      EVP_Cipher(
            &ctx_cipher,
            data + sizeof( IKE_HEADER ),
            data + sizeof( IKE_HEADER ),
            ( int ) size - sizeof( IKE_HEADER ) );

      EVP_CIPHER_CTX_cleanup( &ctx_cipher );

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            data,
            size,
            "== : decrypt packet" );

      //
      // validate the packet integrity
      //

      IKE_HEADER * header = ( IKE_HEADER * ) packet.buff();

      size = sizeof( IKE_HEADER );

      if( packet.size() < size )
      {
            log.txt( LLOG_ERROR,
                  "!! : validate packet failed ( truncated header )\n" );

            return LIBIKE_FAILED;
      }

      while( true )
      {
            IKE_PAYLOAD * payload = ( IKE_PAYLOAD * )( packet.buff() + size );

            if( packet.size() < ( size + sizeof( IKE_PAYLOAD ) ) )
            {
                  log.txt( LLOG_ERROR,
                        "!! : validate packet failed ( truncated payload )\n" );

                  return LIBIKE_FAILED;
            }

            size += ntohs( payload->length );

            if( packet.size() < size )
            {
                  log.txt( LLOG_ERROR,
                        "!! : validate packet failed ( decryption error or corrupted )\n" );

                  return LIBIKE_FAILED;
            }

            if( payload->next == ISAKMP_PAYLOAD_NONE )
                  break;
      }

      //
      // validate packet padding. if the encrypted
      // packet size is equal the the ike message
      // length, we can skip this step. although the
      // RFC states there should at least be one pad
      // byte that describes the padding length, if
      // we are strict we will break compatibility
      // with many implementations including cisco
      //

      if( size < packet.size() )
      {
            //
            // trim packet padding
            //

            size_t diff = packet.size() - size;

            packet.size( size );

            log.txt( LLOG_DEBUG,
                  "<= : trimmed packet padding ( %i bytes )\n",
                   diff );

            header = ( IKE_HEADER * ) packet.buff();
            header->length = htonl( ( uint32_t ) size );
      }

      //
      // store cipher iv data
      //

      memcpy(
            iv->buff(),
            iv_data,
            iv->size() );

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            iv->buff(),
            iv->size(),
            "<= : stored iv" );
      
      return LIBIKE_OK;
}

long _IKED::packet_ike_encrypt( IDB_PH1 * sa, PACKET_IKE & packet, BDATA * iv )
{
      log.txt( LLOG_INFO,
            ">= : cookies %08x%08x:%08x%08x\n",
            htonl( *( long * ) &sa->cookies.i[ 0 ] ),
            htonl( *( long * ) &sa->cookies.i[ 4 ] ),
            htonl( *( long * ) &sa->cookies.r[ 0 ] ),
            htonl( *( long * ) &sa->cookies.r[ 4 ] ) );

      log.txt( LLOG_INFO,
            ">= : message %08x\n",
            htonl( packet.get_msgid() ) );

      //
      // check if encrypt is required
      //

      unsigned char *   data = packet.buff();
      size_t                size = packet.size();

      if( !( data[ ISAKMP_FLAGS_OFFSET ] & ISAKMP_FLAG_ENCRYPT ) )
            return LIBIKE_OK;

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            iv->buff(),
            iv->size(),
            ">= : encrypt iv" );

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            packet.buff(),
            packet.size(),
            "== : encrypt packet" );

      //
      // determine pad length
      //

      size_t plen = size - sizeof( IKE_HEADER );
      size_t blen = EVP_CIPHER_block_size( sa->evp_cipher );
      size_t padd = 0;

      if( plen % blen )
            padd += blen - ( plen % blen );

      packet.add_null( padd );

      data = packet.buff();
      size = packet.size();

      //
      // set new packet length in header
      //

      IKE_HEADER * header = ( IKE_HEADER * ) packet.buff();
      header->length = htonl( ( uint32_t ) size );

      //
      // init cipher key and iv and
      // encrypt all but header
      //

      EVP_CIPHER_CTX ctx_cipher;
      EVP_CIPHER_CTX_init( &ctx_cipher );

      EVP_CipherInit_ex(
            &ctx_cipher,
            sa->evp_cipher,
            NULL,
            NULL,
            NULL,
            1 );

      EVP_CIPHER_CTX_set_key_length(
            &ctx_cipher,
            ( int ) sa->key.size() );

      EVP_CipherInit_ex(
            &ctx_cipher,
            NULL,
            NULL,
            sa->key.buff(),
            iv->buff(),
            1 );

      EVP_Cipher(
            &ctx_cipher,
            data + sizeof( IKE_HEADER ),
            data + sizeof( IKE_HEADER ),
            ( int ) size - sizeof( IKE_HEADER ) );

      EVP_CIPHER_CTX_cleanup( &ctx_cipher );

      //
      // store cipher iv data
      //

      memcpy(
            iv->buff(),
            data + size - iv->size(),
            iv->size() );

      log.bin(
            LLOG_DEBUG,
            LLOG_DECODE,
            iv->buff(),
            iv->size(),
            "== : stored iv" );

      return LIBIKE_OK;
}


Generated by  Doxygen 1.6.0   Back to index