| Commit | Line | Data |
|---|---|---|
| c8cf0f94 PA |
1 | /* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */ |
| 2 | ||
| 3 | /* | |
| 4 | * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994 | |
| 5 | * The Regents of the University of California. All rights reserved. | |
| 6 | * | |
| 7 | * Redistribution and use in source and binary forms, with or without | |
| 8 | * modification, are permitted provided that: (1) source code distributions | |
| 9 | * retain the above copyright notice and this paragraph in its entirety, (2) | |
| 10 | * distributions including binary code include the above copyright notice and | |
| 11 | * this paragraph in its entirety in the documentation or other materials | |
| 12 | * provided with the distribution, and (3) all advertising materials mentioning | |
| 13 | * features or use of this software display the following acknowledgement: | |
| 14 | * ``This product includes software developed by the University of California, | |
| 15 | * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of | |
| 16 | * the University nor the names of its contributors may be used to endorse | |
| 17 | * or promote products derived from this software without specific prior | |
| 18 | * written permission. | |
| 19 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED | |
| 20 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | |
| 21 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
| 22 | */ | |
| 23 | ||
| 24 | #ifndef lint | |
| 25 | static const char rcsid[] _U_ = | |
| 26 | "@(#) $Header: /tcpdump/master/tcpdump/print-esp.c,v 1.55.2.1 2005/04/21 06:44:57 guy Exp $ (LBL)"; | |
| 27 | #endif | |
| 28 | ||
| 29 | #ifdef HAVE_CONFIG_H | |
| 30 | #include "config.h" | |
| 31 | #endif | |
| 32 | ||
| 33 | #include <string.h> | |
| 34 | ||
| 35 | #include <tcpdump-stdinc.h> | |
| 36 | ||
| 37 | #include <stdlib.h> | |
| 38 | ||
| 39 | #ifdef HAVE_LIBCRYPTO | |
| 40 | #ifdef HAVE_OPENSSL_EVP_H | |
| 41 | #include <openssl/evp.h> | |
| 42 | #endif | |
| 43 | #endif | |
| 44 | ||
| 45 | #include <stdio.h> | |
| 46 | ||
| 47 | #include "ip.h" | |
| 48 | #include "esp.h" | |
| 49 | #ifdef INET6 | |
| 50 | #include "ip6.h" | |
| 51 | #endif | |
| 52 | ||
| 53 | #include "netdissect.h" | |
| 54 | #include "addrtoname.h" | |
| 55 | #include "extract.h" | |
| 56 | ||
| 57 | #ifndef HAVE_SOCKADDR_STORAGE | |
| 58 | #ifdef INET6 | |
| 59 | struct sockaddr_storage { | |
| 60 | union { | |
| 61 | struct sockaddr_in sin; | |
| 62 | struct sockaddr_in6 sin6; | |
| 63 | } un; | |
| 64 | }; | |
| 65 | #else | |
| 66 | #define sockaddr_storage sockaddr | |
| 67 | #endif | |
| 68 | #endif /* HAVE_SOCKADDR_STORAGE */ | |
| 69 | ||
| 70 | #ifdef HAVE_LIBCRYPTO | |
| 71 | struct sa_list { | |
| 72 | struct sa_list *next; | |
| 73 | struct sockaddr_storage daddr; | |
| 74 | u_int32_t spi; | |
| 75 | const EVP_CIPHER *evp; | |
| 76 | int ivlen; | |
| 77 | int authlen; | |
| 78 | u_char secret[256]; /* is that big enough for all secrets? */ | |
| 79 | int secretlen; | |
| 80 | }; | |
| 81 | ||
| 82 | static void esp_print_addsa(netdissect_options *ndo, | |
| 83 | struct sa_list *sa, int sa_def) | |
| 84 | { | |
| 85 | /* copy the "sa" */ | |
| 86 | ||
| 87 | struct sa_list *nsa; | |
| 88 | ||
| 89 | nsa = (struct sa_list *)malloc(sizeof(struct sa_list)); | |
| 90 | if (nsa == NULL) | |
| 91 | (*ndo->ndo_error)(ndo, "ran out of memory to allocate sa structure"); | |
| 92 | ||
| 93 | *nsa = *sa; | |
| 94 | ||
| 95 | if (sa_def) | |
| 96 | ndo->ndo_sa_default = nsa; | |
| 97 | ||
| 98 | nsa->next = ndo->ndo_sa_list_head; | |
| 99 | ndo->ndo_sa_list_head = nsa; | |
| 100 | } | |
| 101 | ||
| 102 | ||
| 103 | static u_int hexdigit(netdissect_options *ndo, char hex) | |
| 104 | { | |
| 105 | if (hex >= '0' && hex <= '9') | |
| 106 | return (hex - '0'); | |
| 107 | else if (hex >= 'A' && hex <= 'F') | |
| 108 | return (hex - 'A' + 10); | |
| 109 | else if (hex >= 'a' && hex <= 'f') | |
| 110 | return (hex - 'a' + 10); | |
| 111 | else { | |
| 112 | (*ndo->ndo_error)(ndo, "invalid hex digit %c in espsecret\n", hex); | |
| 113 | return 0; | |
| 114 | } | |
| 115 | } | |
| 116 | ||
| 117 | static u_int hex2byte(netdissect_options *ndo, char *hexstring) | |
| 118 | { | |
| 119 | u_int byte; | |
| 120 | ||
| 121 | byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]); | |
| 122 | return byte; | |
| 123 | } | |
| 124 | ||
| 125 | /* | |
| 126 | * decode the form: SPINUM@IP <tab> ALGONAME:0xsecret | |
| 127 | * | |
| 128 | * special form: file /name | |
| 129 | * causes us to go read from this file instead. | |
| 130 | * | |
| 131 | */ | |
| 132 | static void esp_print_decode_onesecret(netdissect_options *ndo, char *line) | |
| 133 | { | |
| 134 | struct sa_list sa1; | |
| 135 | int sa_def; | |
| 136 | ||
| 137 | char *spikey; | |
| 138 | char *decode; | |
| 139 | ||
| 140 | spikey = strsep(&line, " \t"); | |
| 141 | sa_def = 0; | |
| 142 | memset(&sa1, 0, sizeof(struct sa_list)); | |
| 143 | ||
| 144 | /* if there is only one token, then it is an algo:key token */ | |
| 145 | if (line == NULL) { | |
| 146 | decode = spikey; | |
| 147 | spikey = NULL; | |
| 148 | /* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */ | |
| 149 | /* sa1.spi = 0; */ | |
| 150 | sa_def = 1; | |
| 151 | } else | |
| 152 | decode = line; | |
| 153 | ||
| 154 | if (spikey && strcasecmp(spikey, "file") == 0) { | |
| 155 | /* open file and read it */ | |
| 156 | FILE *secretfile; | |
| 157 | char fileline[1024]; | |
| 158 | char *nl; | |
| 159 | ||
| 160 | secretfile = fopen(line, FOPEN_READ_TXT); | |
| 161 | if (secretfile == NULL) { | |
| 162 | perror(line); | |
| 163 | exit(3); | |
| 164 | } | |
| 165 | ||
| 166 | while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) { | |
| 167 | /* remove newline from the line */ | |
| 168 | nl = strchr(fileline, '\n'); | |
| 169 | if (nl) | |
| 170 | *nl = '\0'; | |
| 171 | if (fileline[0] == '#') continue; | |
| 172 | if (fileline[0] == '\0') continue; | |
| 173 | ||
| 174 | esp_print_decode_onesecret(ndo, fileline); | |
| 175 | } | |
| 176 | fclose(secretfile); | |
| 177 | ||
| 178 | return; | |
| 179 | } | |
| 180 | ||
| 181 | if (spikey) { | |
| 182 | char *spistr, *foo; | |
| 183 | u_int32_t spino; | |
| 184 | struct sockaddr_in *sin; | |
| 185 | #ifdef INET6 | |
| 186 | struct sockaddr_in6 *sin6; | |
| 187 | #endif | |
| 188 | ||
| 189 | spistr = strsep(&spikey, "@"); | |
| 190 | ||
| 191 | spino = strtoul(spistr, &foo, 0); | |
| 192 | if (spistr == foo || !spikey) { | |
| 193 | (*ndo->ndo_warning)(ndo, "print_esp: failed to decode spi# %s\n", foo); | |
| 194 | return; | |
| 195 | } | |
| 196 | ||
| 197 | sa1.spi = spino; | |
| 198 | ||
| 199 | sin = (struct sockaddr_in *)&sa1.daddr; | |
| 200 | #ifdef INET6 | |
| 201 | sin6 = (struct sockaddr_in6 *)&sa1.daddr; | |
| 202 | if (inet_pton(AF_INET6, spikey, &sin6->sin6_addr) == 1) { | |
| 203 | #ifdef HAVE_SOCKADDR_SA_LEN | |
| 204 | sin6->sin6_len = sizeof(struct sockaddr_in6); | |
| 205 | #endif | |
| 206 | sin6->sin6_family = AF_INET6; | |
| 207 | } else | |
| 208 | #endif | |
| 209 | if (inet_pton(AF_INET, spikey, &sin->sin_addr) == 1) { | |
| 210 | #ifdef HAVE_SOCKADDR_SA_LEN | |
| 211 | sin->sin_len = sizeof(struct sockaddr_in); | |
| 212 | #endif | |
| 213 | sin->sin_family = AF_INET; | |
| 214 | } else { | |
| 215 | (*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey); | |
| 216 | return; | |
| 217 | } | |
| 218 | } | |
| 219 | ||
| 220 | if (decode) { | |
| 221 | char *colon, *p; | |
| 222 | u_char espsecret_key[256]; | |
| 223 | int len; | |
| 224 | size_t i; | |
| 225 | const EVP_CIPHER *evp; | |
| 226 | int authlen = 0; | |
| 227 | ||
| 228 | /* skip any blank spaces */ | |
| 229 | while (isspace((unsigned char)*decode)) | |
| 230 | decode++; | |
| 231 | ||
| 232 | colon = strchr(decode, ':'); | |
| 233 | if (colon == NULL) { | |
| 234 | (*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode); | |
| 235 | return; | |
| 236 | } | |
| 237 | *colon = '\0'; | |
| 238 | ||
| 239 | len = colon - decode; | |
| 240 | if (strlen(decode) > strlen("-hmac96") && | |
| 241 | !strcmp(decode + strlen(decode) - strlen("-hmac96"), | |
| 242 | "-hmac96")) { | |
| 243 | p = strstr(decode, "-hmac96"); | |
| 244 | *p = '\0'; | |
| 245 | authlen = 12; | |
| 246 | } | |
| 247 | if (strlen(decode) > strlen("-cbc") && | |
| 248 | !strcmp(decode + strlen(decode) - strlen("-cbc"), "-cbc")) { | |
| 249 | p = strstr(decode, "-cbc"); | |
| 250 | *p = '\0'; | |
| 251 | } | |
| 252 | evp = EVP_get_cipherbyname(decode); | |
| 253 | if (!evp) { | |
| 254 | (*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode); | |
| 255 | sa1.evp = NULL; | |
| 256 | sa1.authlen = 0; | |
| 257 | sa1.ivlen = 0; | |
| 258 | return; | |
| 259 | } | |
| 260 | ||
| 261 | sa1.evp = evp; | |
| 262 | sa1.authlen = authlen; | |
| 263 | sa1.ivlen = EVP_CIPHER_iv_length(evp); | |
| 264 | ||
| 265 | colon++; | |
| 266 | if (colon[0] == '0' && colon[1] == 'x') { | |
| 267 | /* decode some hex! */ | |
| 268 | colon += 2; | |
| 269 | len = strlen(colon) / 2; | |
| 270 | ||
| 271 | if (len > 256) { | |
| 272 | (*ndo->ndo_warning)(ndo, "secret is too big: %d\n", len); | |
| 273 | return; | |
| 274 | } | |
| 275 | ||
| 276 | i = 0; | |
| 277 | while (colon[0] != '\0' && colon[1]!='\0') { | |
| 278 | espsecret_key[i] = hex2byte(ndo, colon); | |
| 279 | colon += 2; | |
| 280 | i++; | |
| 281 | } | |
| 282 | ||
| 283 | memcpy(sa1.secret, espsecret_key, i); | |
| 284 | sa1.secretlen = i; | |
| 285 | } else { | |
| 286 | i = strlen(colon); | |
| 287 | ||
| 288 | if (i < sizeof(sa1.secret)) { | |
| 289 | memcpy(sa1.secret, colon, i); | |
| 290 | sa1.secretlen = i; | |
| 291 | } else { | |
| 292 | memcpy(sa1.secret, colon, sizeof(sa1.secret)); | |
| 293 | sa1.secretlen = sizeof(sa1.secret); | |
| 294 | } | |
| 295 | } | |
| 296 | } | |
| 297 | ||
| 298 | esp_print_addsa(ndo, &sa1, sa_def); | |
| 299 | } | |
| 300 | ||
| 301 | static void esp_print_decodesecret(netdissect_options *ndo) | |
| 302 | { | |
| 303 | char *line; | |
| 304 | char *p; | |
| 305 | ||
| 306 | p = ndo->ndo_espsecret; | |
| 307 | ||
| 308 | while (ndo->ndo_espsecret && ndo->ndo_espsecret[0] != '\0') { | |
| 309 | /* pick out the first line or first thing until a comma */ | |
| 310 | if ((line = strsep(&ndo->ndo_espsecret, "\n,")) == NULL) { | |
| 311 | line = ndo->ndo_espsecret; | |
| 312 | ndo->ndo_espsecret = NULL; | |
| 313 | } | |
| 314 | ||
| 315 | esp_print_decode_onesecret(ndo, line); | |
| 316 | } | |
| 317 | } | |
| 318 | ||
| 319 | static void esp_init(netdissect_options *ndo _U_) | |
| 320 | { | |
| 321 | ||
| 322 | OpenSSL_add_all_algorithms(); | |
| 323 | EVP_add_cipher_alias(SN_des_ede3_cbc, "3des"); | |
| 324 | } | |
| 325 | #endif | |
| 326 | ||
| 327 | int | |
| 328 | esp_print(netdissect_options *ndo, | |
| 329 | const u_char *bp, const int length, const u_char *bp2 | |
| 330 | #ifndef HAVE_LIBCRYPTO | |
| 331 | _U_ | |
| 332 | #endif | |
| 333 | , | |
| 334 | int *nhdr | |
| 335 | #ifndef HAVE_LIBCRYPTO | |
| 336 | _U_ | |
| 337 | #endif | |
| 338 | , | |
| 339 | int *padlen | |
| 340 | #ifndef HAVE_LIBCRYPTO | |
| 341 | _U_ | |
| 342 | #endif | |
| 343 | ) | |
| 344 | { | |
| 345 | register const struct newesp *esp; | |
| 346 | register const u_char *ep; | |
| 347 | #ifdef HAVE_LIBCRYPTO | |
| 348 | struct ip *ip; | |
| 349 | struct sa_list *sa = NULL; | |
| 350 | int espsecret_keylen; | |
| 351 | #ifdef INET6 | |
| 352 | struct ip6_hdr *ip6 = NULL; | |
| 353 | #endif | |
| 354 | int advance; | |
| 355 | int len; | |
| 356 | u_char *secret; | |
| 357 | int ivlen = 0; | |
| 358 | u_char *ivoff; | |
| 359 | u_char *p; | |
| 360 | EVP_CIPHER_CTX ctx; | |
| 361 | int blocksz; | |
| 362 | static int initialized = 0; | |
| 363 | #endif | |
| 364 | ||
| 365 | esp = (struct newesp *)bp; | |
| 366 | ||
| 367 | #ifdef HAVE_LIBCRYPTO | |
| 368 | secret = NULL; | |
| 369 | advance = 0; | |
| 370 | ||
| 371 | if (!initialized) { | |
| 372 | esp_init(ndo); | |
| 373 | initialized = 1; | |
| 374 | } | |
| 375 | #endif | |
| 376 | ||
| 377 | #if 0 | |
| 378 | /* keep secret out of a register */ | |
| 379 | p = (u_char *)&secret; | |
| 380 | #endif | |
| 381 | ||
| 382 | /* 'ep' points to the end of available data. */ | |
| 383 | ep = ndo->ndo_snapend; | |
| 384 | ||
| 385 | if ((u_char *)(esp + 1) >= ep) { | |
| 386 | fputs("[|ESP]", stdout); | |
| 387 | goto fail; | |
| 388 | } | |
| 389 | (*ndo->ndo_printf)(ndo, "ESP(spi=0x%08x", EXTRACT_32BITS(&esp->esp_spi)); | |
| 390 | (*ndo->ndo_printf)(ndo, ",seq=0x%x)", EXTRACT_32BITS(&esp->esp_seq)); | |
| 391 | (*ndo->ndo_printf)(ndo, ", length %u", length); | |
| 392 | ||
| 393 | #ifndef HAVE_LIBCRYPTO | |
| 394 | goto fail; | |
| 395 | #else | |
| 396 | /* initiailize SAs */ | |
| 397 | if (ndo->ndo_sa_list_head == NULL) { | |
| 398 | if (!ndo->ndo_espsecret) | |
| 399 | goto fail; | |
| 400 | ||
| 401 | esp_print_decodesecret(ndo); | |
| 402 | } | |
| 403 | ||
| 404 | if (ndo->ndo_sa_list_head == NULL) | |
| 405 | goto fail; | |
| 406 | ||
| 407 | ip = (struct ip *)bp2; | |
| 408 | switch (IP_V(ip)) { | |
| 409 | #ifdef INET6 | |
| 410 | case 6: | |
| 411 | ip6 = (struct ip6_hdr *)bp2; | |
| 412 | /* we do not attempt to decrypt jumbograms */ | |
| 413 | if (!EXTRACT_16BITS(&ip6->ip6_plen)) | |
| 414 | goto fail; | |
| 415 | /* if we can't get nexthdr, we do not need to decrypt it */ | |
| 416 | len = sizeof(struct ip6_hdr) + EXTRACT_16BITS(&ip6->ip6_plen); | |
| 417 | ||
| 418 | /* see if we can find the SA, and if so, decode it */ | |
| 419 | for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) { | |
| 420 | struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&sa->daddr; | |
| 421 | if (sa->spi == ntohl(esp->esp_spi) && | |
| 422 | sin6->sin6_family == AF_INET6 && | |
| 423 | memcmp(&sin6->sin6_addr, &ip6->ip6_dst, | |
| 424 | sizeof(struct in6_addr)) == 0) { | |
| 425 | break; | |
| 426 | } | |
| 427 | } | |
| 428 | break; | |
| 429 | #endif /*INET6*/ | |
| 430 | case 4: | |
| 431 | /* nexthdr & padding are in the last fragment */ | |
| 432 | if (EXTRACT_16BITS(&ip->ip_off) & IP_MF) | |
| 433 | goto fail; | |
| 434 | len = EXTRACT_16BITS(&ip->ip_len); | |
| 435 | ||
| 436 | /* see if we can find the SA, and if so, decode it */ | |
| 437 | for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) { | |
| 438 | struct sockaddr_in *sin = (struct sockaddr_in *)&sa->daddr; | |
| 439 | if (sa->spi == ntohl(esp->esp_spi) && | |
| 440 | sin->sin_family == AF_INET && | |
| 441 | sin->sin_addr.s_addr == ip->ip_dst.s_addr) { | |
| 442 | break; | |
| 443 | } | |
| 444 | } | |
| 445 | break; | |
| 446 | default: | |
| 447 | goto fail; | |
| 448 | } | |
| 449 | ||
| 450 | /* if we didn't find the specific one, then look for | |
| 451 | * an unspecified one. | |
| 452 | */ | |
| 453 | if (sa == NULL) | |
| 454 | sa = ndo->ndo_sa_default; | |
| 455 | ||
| 456 | /* if not found fail */ | |
| 457 | if (sa == NULL) | |
| 458 | goto fail; | |
| 459 | ||
| 460 | /* if we can't get nexthdr, we do not need to decrypt it */ | |
| 461 | if (ep - bp2 < len) | |
| 462 | goto fail; | |
| 463 | if (ep - bp2 > len) { | |
| 464 | /* FCS included at end of frame (NetBSD 1.6 or later) */ | |
| 465 | ep = bp2 + len; | |
| 466 | } | |
| 467 | ||
| 468 | ivoff = (u_char *)(esp + 1) + 0; | |
| 469 | ivlen = sa->ivlen; | |
| 470 | secret = sa->secret; | |
| 471 | espsecret_keylen = sa->secretlen; | |
| 472 | ep = ep - sa->authlen; | |
| 473 | ||
| 474 | if (sa->evp) { | |
| 475 | memset(&ctx, 0, sizeof(ctx)); | |
| 476 | if (EVP_CipherInit(&ctx, sa->evp, secret, NULL, 0) < 0) | |
| 477 | (*ndo->ndo_warning)(ndo, "espkey init failed"); | |
| 478 | ||
| 479 | blocksz = EVP_CIPHER_CTX_block_size(&ctx); | |
| 480 | ||
| 481 | p = ivoff; | |
| 482 | EVP_CipherInit(&ctx, NULL, NULL, p, 0); | |
| 483 | EVP_Cipher(&ctx, p + ivlen, p + ivlen, ep - (p + ivlen)); | |
| 484 | advance = ivoff - (u_char *)esp + ivlen; | |
| 485 | } else | |
| 486 | advance = sizeof(struct newesp); | |
| 487 | ||
| 488 | /* sanity check for pad length */ | |
| 489 | if (ep - bp < *(ep - 2)) | |
| 490 | goto fail; | |
| 491 | ||
| 492 | if (padlen) | |
| 493 | *padlen = *(ep - 2) + 2; | |
| 494 | ||
| 495 | if (nhdr) | |
| 496 | *nhdr = *(ep - 1); | |
| 497 | ||
| 498 | (ndo->ndo_printf)(ndo, ": "); | |
| 499 | return advance; | |
| 500 | #endif | |
| 501 | ||
| 502 | fail: | |
| 503 | return -1; | |
| 504 | } | |
| 505 | ||
| 506 | /* | |
| 507 | * Local Variables: | |
| 508 | * c-style: whitesmith | |
| 509 | * c-basic-offset: 8 | |
| 510 | * End: | |
| 511 | */ |