LTP GCOV extension - code coverage report
Current view: directory - trunk/libteredo - security.c
Test: lcov.info
Date: 2007-05-09 Instrumented lines: 66
Code covered: 92.4 % Executed lines: 61

       1                 : /*
       2                 :  * security.c - helpers for security-related stuff
       3                 :  * $Id: security.c 1828 2006-12-12 13:30:59Z remi $
       4                 :  */
       5                 : 
       6                 : /***********************************************************************
       7                 :  *  Copyright © 2004-2006 Rémi Denis-Courmont.                         *
       8                 :  *  This program is free software; you can redistribute and/or modify  *
       9                 :  *  it under the terms of the GNU General Public License as published  *
      10                 :  *  by the Free Software Foundation; version 2 of the license.         *
      11                 :  *                                                                     *
      12                 :  *  This program is distributed in the hope that it will be useful,    *
      13                 :  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
      14                 :  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
      15                 :  *  See the GNU General Public License for more details.               *
      16                 :  *                                                                     *
      17                 :  *  You should have received a copy of the GNU General Public License  *
      18                 :  *  along with this program; if not, you can get it from:              *
      19                 :  *  http://www.gnu.org/copyleft/gpl.html                               *
      20                 :  ***********************************************************************/
      21                 : 
      22                 : #ifdef HAVE_CONFIG_H
      23                 : # include <config.h>
      24                 : #endif
      25                 : 
      26                 : #include <stdbool.h>
      27                 : #include <string.h>
      28                 : #include <inttypes.h>
      29                 : #include <limits.h>
      30                 : #include <assert.h>
      31                 : 
      32                 : #include <sys/types.h>
      33                 : #include <fcntl.h> /* open() */
      34                 : #include <unistd.h> /* read(), close() */
      35                 : #include <pthread.h>
      36                 : #include <netinet/in.h> /* struct in6_addr */
      37                 : #include <errno.h>
      38                 : 
      39                 : #include "security.h"
      40                 : #include "debug.h"
      41                 : #include "md5.h"
      42                 : 
      43                 : #if defined (__OpenBSD__) || defined (__OpenBSD_kernel__)
      44                 : static const char randfile[] = "/dev/srandom";
      45                 : #else
      46                 : static const char randfile[] = "/dev/random";
      47                 : #endif
      48                 : 
      49                 : 
      50                 : /* HMAC authentication */
      51                 : #define LIBTEREDO_KEY_LEN 16
      52                 : #define HMAC_BLOCK_LEN 64 /* block size in bytes for MD5 (or SHA1) */
      53                 : #if LIBTEREDO_KEY_LEN > HMAC_BLOCK_LEN
      54                 : # error HMAC key too long.
      55                 : #endif
      56                 : 
      57                 : static union
      58                 : {
      59                 :         unsigned char key[LIBTEREDO_KEY_LEN];
      60                 :         unsigned char ipad[HMAC_BLOCK_LEN];
      61                 : } inner_key;
      62                 : 
      63                 : static union
      64                 : {
      65                 :         unsigned char key[LIBTEREDO_KEY_LEN];
      66                 :         unsigned char opad[HMAC_BLOCK_LEN];
      67                 : } outer_key;
      68                 : 
      69                 : // PID cannot be zero (otherwise, have fun using fork()!)
      70                 : static uint16_t hmac_pid = 0;
      71                 : 
      72                 : int teredo_init_HMAC (void)
      73               7 : {
      74                 :         static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
      75               7 :         int retval = -1;
      76                 : 
      77                 : #define return YOU_DONT_MEAN_return
      78               7 :         pthread_mutex_lock (&mutex);
      79                 : 
      80               7 :         if (hmac_pid != htons ((uint16_t)getpid ()))
      81                 :         {
      82                 :                 /* Get a non-predictable random key from the kernel PRNG */
      83               5 :                 int fd = open (randfile, O_RDONLY);
      84               5 :                 if (fd == -1)
      85               0 :                         goto error;
      86                 : 
      87               5 :                 memset (&inner_key, 0, sizeof (inner_key));
      88                 : 
      89              15 :                 for (unsigned len = 0; len < LIBTEREDO_KEY_LEN;)
      90                 :                 {
      91               5 :                         int val = read (fd, inner_key.key + len, LIBTEREDO_KEY_LEN - len);
      92               5 :                         if (val > 0)
      93               5 :                                 len -= val;
      94                 :                 }
      95               5 :                 close (fd);
      96                 : 
      97                 :                 /* Precomputes HMAC padding */
      98               5 :                 memcpy (&outer_key, &inner_key, sizeof (outer_key));
      99                 :         
     100             325 :                 for (unsigned i = 0; i < sizeof (inner_key); i++)
     101                 :                 {
     102             320 :                         inner_key.ipad[i] ^= 0x36;
     103             320 :                         outer_key.opad[i] ^= 0x5c;
     104                 :                 }
     105                 : 
     106               5 :                 hmac_pid = htons ((uint16_t)getpid ());
     107                 :         }
     108               7 :         retval = 0;
     109                 : 
     110               7 : error:
     111               7 :         pthread_mutex_unlock (&mutex);
     112                 : #undef return
     113                 : 
     114               7 :         return retval;
     115                 : }
     116                 : 
     117                 : 
     118                 : void teredo_deinit_HMAC (void)
     119               7 : {
     120               7 : }
     121                 : 
     122                 : 
     123                 : #define LIBTEREDO_HASH_LEN 16
     124                 : 
     125                 : static void
     126                 : teredo_hash (const void *src, size_t slen, const void *dst, size_t dlen,
     127                 :              uint8_t *restrict hash, uint32_t timestamp)
     128             153 : {
     129                 :         /* compute hash */
     130                 :         md5_state_t ctx;
     131             153 :         md5_init (&ctx);
     132             153 :         md5_append (&ctx, inner_key.ipad, sizeof (inner_key.ipad));
     133             153 :         md5_append (&ctx, (const unsigned char *)src, slen);
     134             153 :         md5_append (&ctx, (const unsigned char *)dst, dlen);
     135             153 :         md5_append (&ctx, (const unsigned char *)&hmac_pid, sizeof (hmac_pid));
     136             153 :         md5_append (&ctx, (const unsigned char *)&timestamp, sizeof (timestamp));
     137             153 :         md5_finish (&ctx, hash);
     138                 : 
     139             153 :         md5_init (&ctx);
     140             153 :         md5_append (&ctx, outer_key.opad, sizeof (outer_key.opad));
     141             153 :         md5_append (&ctx, hash, LIBTEREDO_HASH_LEN);
     142             153 :         md5_finish (&ctx, hash);
     143             153 : }
     144                 : 
     145                 : 
     146                 : #ifdef MIREDO_TEREDO_CLIENT
     147                 : /**
     148                 :  * Generates a cryptographically strong hash to use a payload for ping
     149                 :  * packets. That's how we authenticate the last hop of the echo reply
     150                 :  * (i.e. Teredo relay) before ourselves as being a legitimate first hop
     151                 :  * toward the echo request's destination.
     152                 :  *
     153                 :  * The hash includes a timestamp with a lifetime of 30 units (seconds),
     154                 :  * source and destination addresses, process ID, and a secret pseudo-random
     155                 :  * key.
     156                 :  */
     157                 : static inline void
     158                 : teredo_pinghash (const struct in6_addr *src, const struct in6_addr *dst,
     159                 :                  uint8_t *restrict hash, uint32_t timestamp)
     160             153 : {
     161             153 :         teredo_hash (src, sizeof (*src), dst, sizeof (*dst), hash, timestamp);
     162             153 : }
     163                 : 
     164                 : 
     165                 : #if 0
     166                 : typedef struct teredo_hmac
     167                 : {
     168                 :         uint16_t pid;  /* ICMPv6 Echo id */
     169                 :         uint16_t time; /* ICMPv6 Echo sequence */
     170                 :         uint16_t epoch;
     171                 :         unint8_t hash[LIBTEREDO_HASH_LEN]; /* ICMPv6 Echo payload */
     172                 : } teredo_hmac;
     173                 : #endif
     174                 : 
     175                 : #if (LIBTEREDO_HASH_LEN + 6) != LIBTEREDO_HMAC_LEN
     176                 : # error Inconsistent hash and HMAC length
     177                 : #endif
     178                 : 
     179                 : 
     180                 : void
     181                 : teredo_get_pinghash (uint32_t timestamp, const struct in6_addr *src,
     182                 :                      const struct in6_addr *dst, uint8_t *restrict hash)
     183               6 : {
     184                 :         /* save hash-protected data */
     185               6 :         memcpy (hash, &hmac_pid, sizeof (hmac_pid));
     186               6 :         hash += sizeof (hmac_pid);
     187                 : 
     188               6 :         timestamp = htonl (timestamp);
     189               6 :         memcpy (hash, ((uint8_t *)&timestamp) + 2, 2);
     190               6 :         hash += 2;
     191               6 :         memcpy (hash, &timestamp, 2);
     192               6 :         hash += 2;
     193                 : 
     194               6 :         teredo_pinghash (src, dst, hash, timestamp);
     195               6 : }
     196                 : 
     197                 : 
     198                 : int
     199                 : teredo_verify_pinghash (uint32_t now, const struct in6_addr *src,
     200                 :                         const struct in6_addr *dst,
     201                 :                         const uint8_t *restrict hash)
     202             174 : {
     203                 :         /* Check ICMPv6 ID */
     204             174 :         if (memcmp (hash, &hmac_pid, sizeof (hmac_pid)))
     205               6 :                 return -1;
     206             168 :         hash += sizeof (hmac_pid);
     207                 : 
     208                 :         /* Check ICMPv6 sequence */
     209                 :         uint32_t timestamp;
     210             168 :         memcpy (((uint8_t *)&timestamp) + 2, hash, 2);
     211             168 :         hash += 2;
     212             168 :         memcpy (&timestamp, hash, 2);
     213             168 :         hash += 2;
     214                 : 
     215             168 :         if (((now - ntohl (timestamp)) & 0xffffffff) >= 30)
     216              21 :                 return -1; /* replay attack */
     217                 : 
     218                 :         unsigned char h1[LIBTEREDO_HASH_LEN];
     219             147 :         teredo_pinghash (src, dst, h1, timestamp);
     220                 : 
     221                 :         /* compare HMAC hash */
     222             147 :         return memcmp (h1, hash, LIBTEREDO_HASH_LEN) ? -1 : 0;
     223                 : }
     224                 : #endif /* MIREDO_TEREDO_CLIENT */
     225                 : 
     226                 : 
     227                 : #if LIBTEREDO_HASH_LEN < LIBTEREDO_NONCE_LEN
     228                 : # error Inconsistent hash size
     229                 : #endif
     230                 : void
     231                 : teredo_get_nonce (uint32_t timestamp, uint32_t ipv4, uint16_t port,
     232                 :                   uint8_t *restrict nonce)
     233               0 : {
     234                 :         uint8_t buf[LIBTEREDO_HASH_LEN];
     235                 : 
     236               0 :         teredo_hash (&ipv4, 4, &port, 2, buf, timestamp);
     237               0 :         memcpy (nonce, buf, LIBTEREDO_NONCE_LEN);
     238               0 : }

Generated by: LTP GCOV extension version 1.5