| /* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */ |
| |
| /* |
| * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that: (1) source code distributions |
| * retain the above copyright notice and this paragraph in its entirety, (2) |
| * distributions including binary code include the above copyright notice and |
| * this paragraph in its entirety in the documentation or other materials |
| * provided with the distribution, and (3) all advertising materials mentioning |
| * features or use of this software display the following acknowledgement: |
| * ``This product includes software developed by the University of California, |
| * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of |
| * the University nor the names of its contributors may be used to endorse |
| * or promote products derived from this software without specific prior |
| * written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #ifndef lint |
| static const char rcsid[] = |
| "@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.39 2003-05-02 08:43:28 guy Exp $ (LBL)"; |
| #endif |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <string.h> |
| |
| #include <tcpdump-stdinc.h> |
| |
| #include <stdlib.h> |
| |
| #ifdef HAVE_LIBCRYPTO |
| #include <openssl/des.h> |
| #include <openssl/blowfish.h> |
| #ifdef HAVE_OPENSSL_RC5_H |
| #include <openssl/rc5.h> |
| #endif |
| #ifdef HAVE_OPENSSL_CAST_H |
| #include <openssl/cast.h> |
| #endif |
| #endif |
| |
| #include <stdio.h> |
| |
| #include "ip.h" |
| #include "esp.h" |
| #ifdef INET6 |
| #include "ip6.h" |
| #endif |
| |
| #if defined(__MINGW32__) || defined(__WATCOMC__) |
| extern char *strsep (char **stringp, const char *delim); /* Missing/strsep.c */ |
| #endif |
| |
| #define AVOID_CHURN 1 |
| #include "interface.h" |
| #include "addrtoname.h" |
| #include "extract.h" |
| |
| enum cipher { NONE, |
| DESCBC, |
| BLOWFISH, |
| RC5, |
| CAST128, |
| DES3CBC}; |
| |
| |
| |
| struct esp_algorithm { |
| const char *name; |
| enum cipher algo; |
| int ivlen; |
| int authlen; |
| int replaysize; /* number of bytes, in excess of 4, |
| may be negative */ |
| }; |
| |
| struct esp_algorithm esp_xforms[]={ |
| {"none", NONE, 0, 0, 0}, |
| {"des-cbc", DESCBC, 8, 0, 0}, |
| {"des-cbc-hmac96", DESCBC, 8, 12, 0}, |
| {"blowfish-cbc", BLOWFISH,8, 0, 0}, |
| {"blowfish-cbc-hmac96", BLOWFISH,8, 12, 0}, |
| {"rc5-cbc", RC5, 8, 0, 0}, |
| {"rc5-cbc-hmac96", RC5, 8, 12, 0}, |
| {"cast128-cbc", CAST128, 8, 0, 0}, |
| {"cast128-cbc-hmac96", CAST128, 8, 12, 0}, |
| {"3des-cbc-hmac96", DES3CBC, 8, 12, 0}, |
| {NULL, NONE, 0, 0, 0} |
| }; |
| |
| struct esp_algorithm *null_xf = |
| &esp_xforms[sizeof(esp_xforms)/sizeof(esp_xforms[0]) - 1]; |
| |
| #ifndef HAVE_SOCKADDR_STORAGE |
| #ifdef INET6 |
| struct sockaddr_storage { |
| union { |
| struct sockaddr_in sin; |
| struct sockaddr_in6 sin6; |
| } un; |
| }; |
| #else |
| #define sockaddr_storage sockaddr |
| #endif |
| #endif /* HAVE_SOCKADDR_STORAGE */ |
| |
| struct sa_list { |
| struct sa_list *next; |
| struct sockaddr_storage daddr; |
| u_int32_t spi; |
| struct esp_algorithm *xform; |
| char secret[256]; /* is that big enough for all secrets? */ |
| int secretlen; |
| }; |
| |
| static struct sa_list *sa_list_head=NULL; |
| static struct sa_list *sa_default=NULL; |
| |
| static void esp_print_addsa(struct sa_list *sa, int sa_def) |
| { |
| /* copy the "sa" */ |
| |
| struct sa_list *nsa; |
| |
| nsa = (struct sa_list *)malloc(sizeof(struct sa_list)); |
| if (nsa == NULL) |
| error("ran out of memory to allocate sa structure"); |
| |
| *nsa = *sa; |
| |
| if(sa_def) { |
| sa_default = nsa; |
| } |
| |
| nsa->next = sa_list_head; |
| sa_list_head = nsa; |
| } |
| |
| |
| static int hexdigit(char hex) |
| { |
| if(hex >= '0' && hex <= '9') { |
| return (hex - '0'); |
| } else if(hex >= 'A' && hex <= 'F') { |
| return (hex - 'A' + 10); |
| } else if(hex >= 'a' && hex <= 'f') { |
| return (hex - 'a' + 10); |
| } else { |
| printf("invalid hex digit %c in espsecret\n", hex); |
| return 0; |
| } |
| } |
| |
| static int hex2byte(char *hexstring) |
| { |
| int byte; |
| |
| byte = (hexdigit(hexstring[0]) << 4) + |
| hexdigit(hexstring[1]); |
| return byte; |
| } |
| |
| /* |
| * decode the form: SPINUM@IP <tab> ALGONAME:0xsecret |
| * |
| * special form: file /name |
| * causes us to go read from this file instead. |
| * |
| */ |
| |
| static void esp_print_decode_onesecret(char *line) |
| { |
| struct esp_algorithm *xf; |
| struct sa_list sa1; |
| int sa_def; |
| |
| char *spikey; |
| char *decode; |
| |
| spikey = strsep(&line, " \t"); |
| sa_def = 0; |
| memset(&sa1, 0, sizeof(struct sa_list)); |
| |
| /* if there is only one token, then it is an algo:key token */ |
| if(line == NULL) { |
| decode = spikey; |
| spikey = NULL; |
| /* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */ |
| /* sa1.spi = 0; */ |
| sa_def = 1; |
| } else { |
| decode = line; |
| } |
| |
| if(spikey && strcasecmp(spikey, "file")==0) { |
| /* open file and read it */ |
| FILE *secretfile; |
| char fileline[1024]; |
| char *nl; |
| |
| secretfile = fopen(line, FOPEN_READ_TXT); |
| if(secretfile == NULL) { |
| perror(line); |
| exit(3); |
| } |
| |
| while(fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) { |
| |
| /* remove newline from the line */ |
| nl = strchr(fileline, '\n'); |
| if(nl) { |
| *nl = '\0'; |
| } |
| if(fileline[0]=='#') continue; |
| if(fileline[0]=='\0') continue; |
| |
| esp_print_decode_onesecret(fileline); |
| } |
| fclose(secretfile); |
| |
| return; |
| } |
| |
| if(spikey) { |
| char *spistr, *foo; |
| u_int32_t spino; |
| struct sockaddr_in *sin; |
| #ifdef INET6 |
| struct sockaddr_in6 *sin6; |
| #endif |
| |
| spistr = strsep(&spikey, "@"); |
| |
| spino = strtoul(spistr, &foo, 0); |
| if(spistr == foo || !spikey) { |
| printf("print_esp: failed to decode spi# %s\n", foo); |
| return; |
| } |
| |
| sa1.spi = spino; |
| |
| sin = (struct sockaddr_in *)&sa1.daddr; |
| #ifdef INET6 |
| sin6 = (struct sockaddr_in6 *)&sa1.daddr; |
| if(inet_pton(AF_INET6, spikey, &sin6->sin6_addr) == 1) { |
| #ifdef HAVE_SOCKADDR_SA_LEN |
| sin6->sin6_len = sizeof(struct sockaddr_in6); |
| #endif |
| sin6->sin6_family = AF_INET6; |
| } else |
| #endif |
| if(inet_pton(AF_INET, spikey, &sin->sin_addr) == 1) { |
| #ifdef HAVE_SOCKADDR_SA_LEN |
| sin->sin_len = sizeof(struct sockaddr_in); |
| #endif |
| sin->sin_family = AF_INET; |
| } else { |
| printf("print_esp: can not decode IP# %s\n", spikey); |
| return; |
| } |
| } |
| |
| if(decode) { |
| char *colon; |
| char espsecret_key[256]; |
| unsigned int len, i; |
| |
| /* skip any blank spaces */ |
| while(isspace((unsigned char)*decode)) decode++; |
| |
| colon = strchr(decode, ':'); |
| if(colon == NULL) { |
| printf("failed to decode espsecret: %s\n", decode); |
| return; |
| } |
| |
| len = colon - decode; |
| xf = esp_xforms; |
| while(xf->name && strncasecmp(decode, xf->name, len)!=0) { |
| xf++; |
| } |
| if(xf->name == NULL) { |
| printf("failed to find cipher algo %s\n", decode); |
| /* set to NULL transform */ |
| return; |
| } |
| sa1.xform = xf; |
| |
| colon++; |
| if(colon[0]=='0' && colon[1]=='x') { |
| /* decode some hex! */ |
| colon+=2; |
| len = strlen(colon) / 2; |
| |
| if(len > 256) { |
| printf("secret is too big: %d\n", len); |
| return; |
| } |
| |
| i = 0; |
| while(colon[0] != '\0' && colon[1]!='\0') { |
| espsecret_key[i]=hex2byte(colon); |
| colon+=2; |
| i++; |
| } |
| memcpy(sa1.secret, espsecret_key, i); |
| sa1.secretlen=i; |
| } else { |
| i = strlen(colon); |
| if(i < sizeof(sa1.secret)) { |
| memcpy(sa1.secret, espsecret_key, i); |
| sa1.secretlen = i; |
| } else { |
| memcpy(sa1.secret, espsecret_key, sizeof(sa1.secret)); |
| sa1.secretlen = sizeof(sa1.secret); |
| } |
| } |
| |
| } |
| |
| esp_print_addsa(&sa1, sa_def); |
| } |
| |
| |
| static void esp_print_decodesecret(void) |
| { |
| char *line; |
| char *p; |
| |
| if(espsecret == NULL) { |
| sa_list_head = NULL; |
| return; |
| } |
| |
| if(sa_list_head != NULL) { |
| return; |
| } |
| |
| p=espsecret; |
| |
| while(espsecret && espsecret[0]!='\0') { |
| /* pick out the first line or first thing until a comma */ |
| if((line = strsep(&espsecret, "\n,"))==NULL) { |
| line=espsecret; |
| espsecret=NULL; |
| } |
| |
| esp_print_decode_onesecret(line); |
| } |
| } |
| |
| int |
| esp_print(register const u_char *bp, register const u_char *bp2, |
| int *nhdr, int *padlen) |
| { |
| register const struct newesp *esp; |
| register const u_char *ep; |
| struct ip *ip; |
| struct sa_list *sa = NULL; |
| int espsecret_keylen; |
| #ifdef INET6 |
| struct ip6_hdr *ip6 = NULL; |
| #endif |
| int advance; |
| int len; |
| char *secret; |
| int ivlen = 0; |
| u_char *ivoff; |
| #ifdef HAVE_LIBCRYPTO |
| u_char *p; |
| #endif |
| |
| esp = (struct newesp *)bp; |
| secret = NULL; |
| advance = 0; |
| |
| #if 0 |
| /* keep secret out of a register */ |
| p = (u_char *)&secret; |
| #endif |
| |
| /* 'ep' points to the end of available data. */ |
| ep = snapend; |
| |
| if ((u_char *)(esp + 1) >= ep) { |
| fputs("[|ESP]", stdout); |
| goto fail; |
| } |
| printf("ESP(spi=0x%08x", EXTRACT_32BITS(&esp->esp_spi)); |
| printf(",seq=0x%x", EXTRACT_32BITS(&esp->esp_seq)); |
| printf(")"); |
| |
| /* if we don't have decryption key, we can't decrypt this packet. */ |
| if(sa_list_head == NULL) { |
| if (!espsecret) { |
| goto fail; |
| } |
| esp_print_decodesecret(); |
| } |
| |
| if(sa_list_head == NULL) { |
| goto fail; |
| } |
| |
| ip = (struct ip *)bp2; |
| switch (IP_V(ip)) { |
| #ifdef INET6 |
| case 6: |
| ip6 = (struct ip6_hdr *)bp2; |
| /* we do not attempt to decrypt jumbograms */ |
| if (!EXTRACT_16BITS(&ip6->ip6_plen)) |
| goto fail; |
| /* if we can't get nexthdr, we do not need to decrypt it */ |
| len = sizeof(struct ip6_hdr) + EXTRACT_16BITS(&ip6->ip6_plen); |
| |
| /* see if we can find the SA, and if so, decode it */ |
| for (sa = sa_list_head; sa != NULL; sa = sa->next) { |
| struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa->daddr; |
| if (sa->spi == ntohl(esp->esp_spi) && |
| sin6->sin6_family == AF_INET6 && |
| memcmp(&sin6->sin6_addr, &ip6->ip6_dst, |
| sizeof(struct in6_addr)) == 0) { |
| break; |
| } |
| } |
| break; |
| #endif /*INET6*/ |
| case 4: |
| /* nexthdr & padding are in the last fragment */ |
| if (EXTRACT_16BITS(&ip->ip_off) & IP_MF) |
| goto fail; |
| len = EXTRACT_16BITS(&ip->ip_len); |
| |
| /* see if we can find the SA, and if so, decode it */ |
| for (sa = sa_list_head; sa != NULL; sa = sa->next) { |
| struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr; |
| if (sa->spi == ntohl(esp->esp_spi) && |
| sin->sin_family == AF_INET && |
| sin->sin_addr.s_addr == ip->ip_dst.s_addr) { |
| break; |
| } |
| } |
| break; |
| default: |
| goto fail; |
| } |
| |
| /* if we didn't find the specific one, then look for |
| * an unspecified one. |
| */ |
| if(sa == NULL) { |
| sa = sa_default; |
| } |
| |
| /* if not found fail */ |
| if(sa == NULL) |
| goto fail; |
| |
| /* if we can't get nexthdr, we do not need to decrypt it */ |
| if (ep - bp2 < len) |
| goto fail; |
| if (ep - bp2 > len) { |
| /* FCS included at end of frame (NetBSD 1.6 or later) */ |
| ep = bp2 + len; |
| } |
| |
| ivoff = (u_char *)(esp + 1) + sa->xform->replaysize; |
| ivlen = sa->xform->ivlen; |
| secret = sa->secret; |
| espsecret_keylen = sa->secretlen; |
| |
| switch (sa->xform->algo) { |
| case DESCBC: |
| #ifdef HAVE_LIBCRYPTO |
| { |
| u_char iv[8]; |
| #if OPENSSL_VERSION_NUMBER >= 0x00907000L |
| DES_key_schedule schedule; |
| #else |
| des_key_schedule schedule; |
| #endif |
| |
| switch (ivlen) { |
| case 4: |
| memcpy(iv, ivoff, 4); |
| memcpy(&iv[4], ivoff, 4); |
| p = &iv[4]; |
| *p++ ^= 0xff; |
| *p++ ^= 0xff; |
| *p++ ^= 0xff; |
| *p++ ^= 0xff; |
| break; |
| case 8: |
| memcpy(iv, ivoff, 8); |
| break; |
| default: |
| goto fail; |
| } |
| p = ivoff + ivlen; |
| |
| if (espsecret_keylen != 8) |
| goto fail; |
| |
| #if OPENSSL_VERSION_NUMBER >= 0x00908000L |
| DES_set_key_unchecked((const_DES_cblock *)secret, &schedule); |
| |
| DES_cbc_encrypt((const unsigned char *)p, p, |
| (long)(ep - p), &schedule, (DES_cblock *)iv, |
| DES_DECRYPT); |
| |
| #elif OPENSSL_VERSION_NUMBER >= 0x00907000L |
| DES_set_key_unchecked((DES_cblock *)secret, schedule); |
| |
| DES_cbc_encrypt((const unsigned char *)p, p, |
| (long)(ep - p), schedule, (DES_cblock *)iv, |
| DES_DECRYPT); |
| #else |
| des_check_key = 0; |
| des_set_key((void *)secret, schedule); |
| |
| des_cbc_encrypt((void *)p, (void *)p, |
| (long)(ep - p), schedule, (void *)iv, |
| DES_DECRYPT); |
| #endif |
| advance = ivoff - (u_char *)esp + ivlen; |
| break; |
| } |
| #else |
| goto fail; |
| #endif /*HAVE_LIBCRYPTO*/ |
| |
| case BLOWFISH: |
| #ifdef HAVE_LIBCRYPTO |
| { |
| BF_KEY schedule; |
| |
| if (espsecret_keylen < 5 || espsecret_keylen > 56) |
| goto fail; |
| BF_set_key(&schedule, espsecret_keylen, secret); |
| |
| p = ivoff + ivlen; |
| BF_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff, |
| BF_DECRYPT); |
| advance = ivoff - (u_char *)esp + ivlen; |
| break; |
| } |
| #else |
| goto fail; |
| #endif /*HAVE_LIBCRYPTO*/ |
| |
| case RC5: |
| #if defined(HAVE_LIBCRYPTO) && defined(HAVE_RC5_H) |
| { |
| RC5_32_KEY schedule; |
| |
| if (espsecret_keylen < 5 || espsecret_keylen > 255) |
| goto fail; |
| RC5_32_set_key(&schedule, espsecret_keylen, secret, |
| RC5_16_ROUNDS); |
| |
| p = ivoff + ivlen; |
| RC5_32_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff, |
| RC5_DECRYPT); |
| advance = ivoff - (u_char *)esp + ivlen; |
| break; |
| } |
| #else |
| goto fail; |
| #endif /*HAVE_LIBCRYPTO*/ |
| |
| case CAST128: |
| #if defined(HAVE_LIBCRYPTO) && defined(HAVE_CAST_H) && !defined(HAVE_BUGGY_CAST128) |
| { |
| CAST_KEY schedule; |
| |
| if (espsecret_keylen < 5 || espsecret_keylen > 16) |
| goto fail; |
| CAST_set_key(&schedule, espsecret_keylen, secret); |
| |
| p = ivoff + ivlen; |
| CAST_cbc_encrypt(p, p, (long)(ep - p), &schedule, ivoff, |
| CAST_DECRYPT); |
| advance = ivoff - (u_char *)esp + ivlen; |
| break; |
| } |
| #else |
| goto fail; |
| #endif /*HAVE_LIBCRYPTO*/ |
| |
| case DES3CBC: |
| #if defined(HAVE_LIBCRYPTO) |
| { |
| #if OPENSSL_VERSION_NUMBER >= 0x00907000L |
| DES_key_schedule s1, s2, s3; |
| |
| if (espsecret_keylen != 24) |
| goto fail; |
| DES_set_odd_parity((DES_cblock *)secret); |
| DES_set_odd_parity((DES_cblock *)(secret + 8)); |
| DES_set_odd_parity((DES_cblock *)(secret + 16)); |
| #if OPENSSL_VERSION_NUMBER >= 0x00908000L |
| if(DES_set_key_checked((const_DES_cblock *)secret, &s1) != 0) { |
| printf("failed to schedule key 1\n"); |
| } |
| if(DES_set_key_checked((const_DES_cblock *)(secret + 8), &s2)!=0) { |
| printf("failed to schedule key 2\n"); |
| } |
| if(DES_set_key_checked((const_DES_cblock *)(secret + 16), &s3)!=0) { |
| printf("failed to schedule key 3\n"); |
| } |
| #else |
| if(DES_set_key_checked((DES_cblock *)secret, s1) != 0) { |
| printf("failed to schedule key 1\n"); |
| } |
| if(DES_set_key_checked((DES_cblock *)(secret + 8), s2)!=0) { |
| printf("failed to schedule key 2\n"); |
| } |
| if(DES_set_key_checked((DES_cblock *)(secret + 16), s3)!=0) { |
| printf("failed to schedule key 3\n"); |
| } |
| #endif |
| |
| p = ivoff + ivlen; |
| DES_ede3_cbc_encrypt((const unsigned char *)p, p, |
| (long)(ep - p), |
| &s1, &s2, &s3, |
| (DES_cblock *)ivoff, DES_DECRYPT); |
| #else |
| des_key_schedule s1, s2, s3; |
| |
| if (espsecret_keylen != 24) |
| goto fail; |
| des_check_key = 1; |
| des_set_odd_parity((void *)secret); |
| des_set_odd_parity((void *)(secret + 8)); |
| des_set_odd_parity((void *)(secret + 16)); |
| if(des_set_key((void *)secret, s1) != 0) { |
| printf("failed to schedule key 1\n"); |
| } |
| if(des_set_key((void *)(secret + 8), s2)!=0) { |
| printf("failed to schedule key 2\n"); |
| } |
| if(des_set_key((void *)(secret + 16), s3)!=0) { |
| printf("failed to schedule key 3\n"); |
| } |
| |
| p = ivoff + ivlen; |
| des_ede3_cbc_encrypt((void *)p, (void *)p, |
| (long)(ep - p), |
| s1, s2, s3, |
| (void *)ivoff, DES_DECRYPT); |
| #endif |
| advance = ivoff - (u_char *)esp + ivlen; |
| break; |
| } |
| #else |
| goto fail; |
| #endif /*HAVE_LIBCRYPTO*/ |
| |
| case NONE: |
| default: |
| advance = sizeof(struct newesp) + sa->xform->replaysize; |
| break; |
| } |
| |
| ep = ep - sa->xform->authlen; |
| /* sanity check for pad length */ |
| if (ep - bp < *(ep - 2)) |
| goto fail; |
| |
| if (padlen) |
| *padlen = *(ep - 2) + 2; |
| |
| if (nhdr) |
| *nhdr = *(ep - 1); |
| |
| printf(": "); |
| return advance; |
| |
| fail: |
| if (nhdr) |
| *nhdr = -1; |
| return 65536; |
| } |