embedded IPsec source code documentation


ah.c

Go to the documentation of this file.
00001 /*
00002  * embedded IPsec
00003  * Copyright (c) 2003 Niklaus Schild and Christian Scheurer, HTI Biel/Bienne
00004  * All rights reserved.
00005  *
00006  * Redistribution and use in source and binary forms, with or without modification,
00007  * are permitted provided that the following conditions are met:
00008  *
00009  * 1. Redistributions of source code must retain the above copyright notice,
00010  *    this list of conditions and the following disclaimer.
00011  * 2. Redistributions in binary form must reproduce the above copyright notice,
00012  *    this list of conditions and the following disclaimer in the documentation
00013  *    and/or other materials provided with the distribution.
00014  * 3. The name of the author may not be used to endorse or promote products
00015  *    derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00018  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00020  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00021  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00022  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00025  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00026  * OF SUCH DAMAGE.
00027  *
00028  */
00029 
00062 #include <string.h>
00063 
00064 #include "ipsec/ipsec.h"
00065 #include "ipsec/util.h"
00066 #include "ipsec/debug.h"
00067 
00068 #include "ipsec/sa.h"
00069 #include "ipsec/md5.h"
00070 #include "ipsec/sha1.h"
00071 
00072 #include "ipsec/ah.h"
00073 
00074 
00075 
00076 __u32 ipsec_ah_bitmap   = 0;                    
00079 __u32 ipsec_ah_lastSeq  = 0;                    
00100 int ipsec_ah_check(ipsec_ip_header *outer_packet, int *payload_offset, int *payload_size,
00101                                     sad_entry *sa)
00102 {
00103         int ret_val     = IPSEC_STATUS_NOT_INITIALIZED; /* by default, the return value is undefined */
00104         ipsec_ah_header *ah_header;
00105         int ah_len;
00106         int ah_offs;
00107         unsigned char orig_digest[IPSEC_MAX_AUTHKEY_LEN];
00108         unsigned char digest[IPSEC_MAX_AUTHKEY_LEN];
00109 
00110         IPSEC_LOG_TRC(IPSEC_TRACE_ENTER,
00111                       "ipsec_ah_check",
00112                                   ("outer_packet=%p, *payload_offset=%d, *payload_size=%d sa=%p",
00113                               (void *)outer_packet, *payload_offset, *payload_size, (void *)sa)
00114                                  );
00115 
00116         /* The AH header is expected to be 24 bytes since we support only 96 bit authentication values */
00117         ah_offs = ((outer_packet->v_hl & 0x0F) << 2);
00118         ah_len = (IPSEC_AH_HDR_SIZE - 4) + ( ((ipsec_ah_header *)((unsigned char *)outer_packet + ah_offs))->len << 2 );
00119 
00120         /* minimal AH header + ICV */
00121         if(ah_len != IPSEC_AH_HDR_SIZE + IPSEC_AUTH_ICV)
00122         {
00123                 IPSEC_LOG_DBG("ipsec_ah_check", IPSEC_STATUS_FAILURE, ("wrong AH header size: ah_len=%d (must be 24 bytes, only 96bit authentication values allowed)", ah_len) );
00124                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_check", ("return = %d", IPSEC_STATUS_FAILURE) );
00125                 return IPSEC_STATUS_FAILURE;
00126         }
00127         
00128         ah_header = ((ipsec_ah_header *)((unsigned char *)outer_packet + ah_offs));
00129 
00130         /* preliminary anti-replay check (without updating the global sequence number window)     */
00131         /* This check prevents useless ICV calculation if the Sequence Number is obviously wrong  */
00132         ret_val = ipsec_check_replay_window(ipsec_ntohl(ah_header->sequence), ipsec_ah_lastSeq, ipsec_ah_bitmap);
00133         if(ret_val != IPSEC_AUDIT_SUCCESS)
00134         {
00135                 IPSEC_LOG_AUD("ipsec_ah_check", IPSEC_AUDIT_SEQ_MISMATCH, ("packet rejected by anti-replay check (lastSeq=%08lx, seq=%08lx, window size=%d)", ipsec_ah_lastSeq, ipsec_ntohl(ah_header->sequence), IPSEC_SEQ_MAX_WINDOW) );
00136                 return ret_val;
00137         }
00138         
00139         /* zero all mutable fields prior to ICV calculation */
00140         /* mutuable fields according to RFC2402, 3.3.3.1.1.1. */
00141         outer_packet->tos               = 0;
00142         outer_packet->offset    = 0;
00143         outer_packet->ttl               = 0;
00144         outer_packet->chksum    = 0;
00145 
00146         /* backup 96bit HMAC before setting it to 0 */
00147         memcpy(orig_digest, ah_header->ah_data, IPSEC_AUTH_ICV);
00148         memset(((ipsec_ah_header *)((unsigned char *)outer_packet + ah_offs))->ah_data, '\0', IPSEC_AUTH_ICV);
00149 
00150         if(sa->mode != IPSEC_TUNNEL)
00151         {
00152                 IPSEC_LOG_ERR("ipsec_ah_check", IPSEC_STATUS_NOT_IMPLEMENTED, ("Can't handle mode %d. Only mode %d (IPSEC_TUNNEL) is implemented.", sa->mode, IPSEC_TUNNEL) );
00153                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_check", ("return = %d", IPSEC_STATUS_NOT_IMPLEMENTED) );
00154                 return IPSEC_STATUS_NOT_IMPLEMENTED;
00155         }
00156 
00157         switch(sa->auth_alg) {
00158 
00159                 case IPSEC_HMAC_MD5:
00160                         hmac_md5((unsigned char *)outer_packet, ipsec_ntohs(outer_packet->len),
00161                                  (unsigned char *)sa->authkey, IPSEC_AUTH_MD5_KEY_LEN, (unsigned char *)&digest);
00162                         break;
00163                 case IPSEC_HMAC_SHA1:
00164                         hmac_sha1((unsigned char *)outer_packet, ipsec_ntohs(outer_packet->len),
00165                                   (unsigned char *)sa->authkey, IPSEC_AUTH_SHA1_KEY_LEN, (unsigned char *)&digest);
00166                         break;
00167                 default:
00168                         IPSEC_LOG_ERR("ipsec_ah_check", IPSEC_STATUS_FAILURE, ("unknown HASH algorithm for this AH")) ;
00169                         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_check", ("return = %d", IPSEC_STATUS_FAILURE) );
00170                         return IPSEC_STATUS_FAILURE;
00171         }
00172 
00173         if(memcmp(orig_digest, digest, IPSEC_AUTH_ICV) != 0) {
00174                 IPSEC_LOG_ERR("ipsec_ah_check", IPSEC_STATUS_FAILURE, ("AH ICV does not match")) ;
00175                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_check", ("return = %d", IPSEC_STATUS_FAILURE) );
00176                 return IPSEC_STATUS_FAILURE;
00177         }
00178         
00179         /* post-ICV calculationn anti-replay check (this call will update the global sequence number window) */
00180         ret_val = ipsec_update_replay_window(ipsec_ntohl(ah_header->sequence), (__u32 *)&ipsec_ah_lastSeq, (__u32 *)&ipsec_ah_bitmap);
00181         if(ret_val != IPSEC_AUDIT_SUCCESS)
00182         {
00183                 IPSEC_LOG_AUD("ipsec_ah_check", IPSEC_AUDIT_SEQ_MISMATCH, ("packet rejected by anti-replay update (lastSeq=%08lx, seq=%08lx, window size=%d)", ipsec_ah_lastSeq, ipsec_ntohl(ah_header->sequence), IPSEC_SEQ_MAX_WINDOW) );
00184                 return ret_val;
00185         }
00186         
00187         *payload_offset = ah_offs + ah_len;
00188         *payload_size   = ipsec_ntohs(((ipsec_ip_header *)((unsigned char *)outer_packet + ah_offs + ah_len))->len);
00189 
00190         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_check", ("return = %d", IPSEC_STATUS_NOT_IMPLEMENTED) );
00191         return IPSEC_STATUS_SUCCESS;
00192 }
00193 
00194 
00214 int ipsec_ah_encapsulate(ipsec_ip_header *inner_packet, int *payload_offset, int *payload_size,
00215                                                  sad_entry *sa, __u32 src, __u32 dst
00216                                      )
00217 {
00218         int ret_val = IPSEC_STATUS_NOT_INITIALIZED;                     /* by default, the return value is undefined */
00219         ipsec_ip_header         *new_ip_header ;
00220         ipsec_ah_header         *new_ah_header;
00221         unsigned char           digest[IPSEC_MAX_AUTHKEY_LEN];
00222 
00223         IPSEC_LOG_TRC(IPSEC_TRACE_ENTER,
00224                       "ipsec_ah_encapsulate",
00225                                   ("inner_packet=%p, *payload_offset=%d, *payload_size=%d sa=%p, src=%lu, dst=%lu",
00226                               (void *)inner_packet, *payload_offset, *payload_size, (void *)sa, src, dst)
00227                                  );
00228 
00229 
00230         /* set new packet header pointers */
00231         new_ip_header = (ipsec_ip_header*)(((char*)inner_packet) - IPSEC_AH_HDR_SIZE - IPSEC_AUTH_ICV - IPSEC_MIN_IPHDR_SIZE) ;
00232         new_ah_header = (ipsec_ah_header*)(((char*)inner_packet) - IPSEC_AUTH_ICV - IPSEC_AH_HDR_SIZE) ;
00233 
00234         /* decrement and check TTL */
00236         // inner_packet->ttl--;
00237         // inner_packet->chksum = ip_chksum(inner_packet, sizeof(ip_header));
00238         if (inner_packet->ttl == 0)
00239         {
00240                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_encapsulate", ("return = %d", IPSEC_STATUS_TTL_EXPIRED) );
00241                 return IPSEC_STATUS_TTL_EXPIRED;
00242         }
00243 
00244         if(IPSEC_AUTH_ICV != 12)
00245         {
00246                 IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_encapsulate", ("return = %d", IPSEC_STATUS_NOT_IMPLEMENTED) );
00247                 return IPSEC_STATUS_NOT_IMPLEMENTED;
00248         }
00249 
00250 
00251         /* increment Sequence Number Field by 1 for each AH packet (1st packet has squ==1) */
00252         sa->sequence_number++;
00253 
00254         /* set header fields */
00255         new_ah_header->nexthdr  = 0x04; /* IP in IP */
00256         new_ah_header->len      = 0x04; /* length is 4 for AH with 96bit ICV */
00257         new_ah_header->reserved = 0x0000;
00258         new_ah_header->spi              = sa->spi;
00259         new_ah_header->sequence = ipsec_htonl(sa->sequence_number);
00260         memset(new_ah_header->ah_data, '\0', IPSEC_AUTH_ICV);
00261 
00262         /* setup IP header and zero all mutable fields prior to ICV calculation */
00263         /* mutable fields according to RFC2402, 3.3.3.1.1.1. */
00264         new_ip_header->v_hl     = 0x45;
00265         new_ip_header->tos              = 0;
00266         new_ip_header->len              = ipsec_htons(ipsec_ntohs(inner_packet->len) + IPSEC_AH_HDR_SIZE + IPSEC_AUTH_ICV + IPSEC_MIN_IPHDR_SIZE);
00267         new_ip_header->id               = 1000 ;        
00268         new_ip_header->offset   = 0;
00269         new_ip_header->ttl              = 0;
00270         new_ip_header->protocol = IPSEC_PROTO_AH;
00271         new_ip_header->chksum   = 0;
00272         new_ip_header->src              = src;
00273         new_ip_header->dest     = dst;
00274 
00275         /* calculate AH according the SA */
00276         switch(sa->auth_alg) {
00277 
00278                 case IPSEC_HMAC_MD5:
00279                         hmac_md5((unsigned char *)new_ip_header, ipsec_ntohs(new_ip_header->len),
00280                                  (unsigned char *)sa->authkey, IPSEC_AUTH_MD5_KEY_LEN, (unsigned char *)&digest);
00281                         break;
00282                 case IPSEC_HMAC_SHA1:
00283                         hmac_sha1((unsigned char *)new_ip_header, ipsec_ntohs(new_ip_header->len),
00284                                   (unsigned char *)sa->authkey, IPSEC_AUTH_SHA1_KEY_LEN, (unsigned char *)&digest);
00285                         break;
00286                 default:
00287                         IPSEC_LOG_ERR("ipsec_ah_encapsulate", IPSEC_STATUS_FAILURE, ("unknown HASH algorithm for this AH") );
00288                         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_encapsulate", ("return = %d", IPSEC_STATUS_FAILURE) );
00289                         return IPSEC_STATUS_FAILURE;
00290 
00291         }
00292 
00293         /* insert ICV */
00294         memcpy(new_ah_header->ah_data, digest, IPSEC_AUTH_ICV);
00295 
00296         /* update outer IP header */
00297         new_ip_header->tos = inner_packet->tos ;
00298         new_ip_header->ttl = 64 ;
00299 
00300         /* set checksum */
00301         new_ip_header->chksum = ipsec_ip_chksum(new_ip_header, sizeof(ipsec_ip_header)) ;
00302 
00303         /* setup return values */
00304         *payload_size   = ipsec_ntohs(new_ip_header->len);
00305         *payload_offset = (((char*)new_ip_header) - ((char*)inner_packet)) ;
00306 
00307         IPSEC_LOG_TRC(IPSEC_TRACE_RETURN, "ipsec_ah_encapsulate", ("return = %d", IPSEC_STATUS_SUCCESS) );
00308         return IPSEC_STATUS_SUCCESS;
00309 }

Copyright 2003 by Christian Scheurer and Niklaus Schild