1 /* $OpenBSD: print-ipsec.c,v 1.13 2009/02/18 01:53:06 mcbride Exp $ */ 2 3 /* 4 * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999 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 * Format and print IPsec (ESP/AH) packets. 24 * By Tero Kivinen <kivinen@ssh.fi>, Tero Mononen <tmo@ssh.fi>, 25 * Tatu Ylonen <ylo@ssh.fi> and Timo J. Rinne <tri@ssh.fi> 26 * in co-operation with SSH Communications Security, Espoo, Finland 27 */ 28 29 #ifndef lint 30 static const char rcsid[] = 31 "@(#) $Id: print-ipsec.c,v 1.13 2009/02/18 01:53:06 mcbride Exp $ (XXX)"; 32 #endif 33 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <sys/socket.h> 37 38 #include <netinet/in.h> 39 #include <netinet/in_systm.h> 40 #include <netinet/ip.h> 41 #include <netinet/ip_var.h> 42 #include <netinet/udp.h> 43 #include <netinet/udp_var.h> 44 #include <netinet/tcp.h> 45 #include <netinet/tcpip.h> 46 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 52 #ifdef INET6 53 #include <netinet/ip6.h> 54 #endif 55 56 #include "addrtoname.h" 57 #include "interface.h" 58 #include "extract.h" /* must come after interface.h */ 59 60 #include <openssl/evp.h> 61 #include <ctype.h> 62 63 /* 64 * IPsec/ESP header 65 */ 66 struct esp_hdr { 67 u_int esp_spi; 68 u_int esp_seq; 69 }; 70 71 static int espinit = 0; 72 static int espauthlen = 12; 73 static EVP_CIPHER_CTX ctx; 74 75 int 76 esp_init (char *espspec) 77 { 78 const EVP_CIPHER *evp; 79 char *p, *espkey, s[3], name[1024]; 80 u_char *key; 81 int i, klen, len; 82 83 evp = EVP_aes_128_cbc(); /* default */ 84 espkey = espspec; 85 if ((p = strchr(espspec, ':')) != NULL) { 86 len = p - espspec; 87 if (len >= sizeof(name)) 88 error("espalg too long"); 89 memcpy(name, espspec, len); 90 name[len] = '\0'; 91 espkey = p + 1; 92 93 /* strip auth alg */ 94 espauthlen = 0; 95 if ((p = strstr(name, "-hmac96")) != NULL) { 96 espauthlen = 12; 97 *p = '\0'; 98 } 99 OpenSSL_add_all_algorithms(); 100 if ((evp = EVP_get_cipherbyname(name)) == NULL) 101 error("espalg `%s' not supported", name); 102 } 103 klen = EVP_CIPHER_key_length(evp); 104 if (strlen(espkey) != klen * 2) 105 error("espkey size mismatch, %d bytes needed", klen); 106 if ((key = malloc(klen)) == NULL) 107 error("malloc failed"); 108 for (i = 0; i < klen; i++) { 109 s[0] = espkey[2*i]; 110 s[1] = espkey[2*i + 1]; 111 s[2] = 0; 112 if (!isxdigit(s[0]) || !isxdigit(s[1])) 113 error("espkey must be specified in hex"); 114 key[i] = strtoul(s, NULL, 16); 115 } 116 EVP_CIPHER_CTX_init(&ctx); 117 if (EVP_CipherInit(&ctx, evp, key, NULL, 0) < 0) { 118 free(key); 119 error("espkey init failed"); 120 } 121 free(key); 122 espinit = 1; 123 return (0); 124 } 125 126 void 127 esp_decrypt (const u_char *bp, u_int len, const u_char *bp2) 128 { 129 const struct ip *ip; 130 u_char *data, pad, nh; 131 int blocksz; 132 133 ip = (const struct ip *)bp2; 134 135 blocksz = EVP_CIPHER_CTX_block_size(&ctx); 136 137 /* Skip fragments and short packets */ 138 if (ntohs(ip->ip_off) & 0x3fff) 139 return; 140 if (snapend - bp < len) { 141 printf(" [|esp]"); 142 return; 143 } 144 /* 145 * Skip ESP header and ignore authentication trailer. 146 * For decryption we need at least 2 blocks: IV and 147 * one cipher block. 148 */ 149 if (len < sizeof(struct esp_hdr) + espauthlen + 2 * blocksz) { 150 printf(" [|esp]"); 151 return; 152 } 153 154 data = (char *)bp; 155 data += sizeof(struct esp_hdr); 156 len -= sizeof(struct esp_hdr); 157 len -= espauthlen; 158 159 /* the first block contains the IV */ 160 EVP_CipherInit(&ctx, NULL, NULL, data, 0); 161 len -= blocksz; 162 data += blocksz; 163 164 /* decrypt remaining payload */ 165 EVP_Cipher(&ctx, data, data, len); 166 167 nh = data[len - 1]; 168 pad = data[len - 2]; 169 170 /* verify padding */ 171 if (pad + 2 > len) 172 return; 173 if (data[len - 3] != pad) 174 return; 175 if (vflag > 1) 176 printf(" pad %d", pad); 177 len -= (pad + 2); 178 printf(": "); 179 switch (nh) { 180 case IPPROTO_TCP: 181 tcp_print(data, len, bp2); 182 break; 183 case IPPROTO_UDP: 184 udp_print(data, len, bp2); 185 break; 186 case IPPROTO_IPV6: 187 ip6_print(data, len); 188 break; 189 case IPPROTO_IPV4: 190 ip_print(data, len); 191 break; 192 case IPPROTO_ICMP: 193 icmp_print(data, bp2); 194 break; 195 case IPPROTO_ICMPV6: 196 icmp6_print(data, bp2); 197 break; 198 default: 199 printf("ip-proto-%d %d", nh, len); 200 break; 201 } 202 if (vflag) 203 printf(" (esp)"); 204 } 205 206 void 207 esp_print (register const u_char *bp, register u_int len, 208 register const u_char *bp2) 209 { 210 const struct ip *ip; 211 const struct esp_hdr *esp; 212 u_int plen = len; 213 #ifdef INET6 214 const struct ip6_hdr *ip6; 215 #endif 216 217 ip = (const struct ip *)bp2; 218 #ifdef INET6 219 if (ip->ip_v == 6) { 220 ip6 = (const struct ip6_hdr *)bp2; 221 printf("esp %s > %s", ip6addr_string(&ip6->ip6_src), 222 ip6addr_string(&ip6->ip6_dst)); 223 } else 224 #endif 225 { 226 printf("esp %s > %s", 227 ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); 228 } 229 230 if (plen < sizeof(struct esp_hdr)) { 231 printf("[|esp]"); 232 return; 233 } 234 esp = (const struct esp_hdr *)bp; 235 236 printf(" spi 0x%08x seq %d len %d", 237 ntohl(esp->esp_spi), ntohl(esp->esp_seq), len); 238 239 if (espinit) 240 esp_decrypt(bp, len, bp2); 241 } 242 243 /* 244 * IPsec/AH header 245 */ 246 struct ah_hdr { 247 u_char ah_nxt_hdr; 248 u_char ah_pl_len; 249 u_short ah_reserved; 250 u_int ah_spi; 251 u_int ah_seq; 252 }; 253 254 void 255 ah_print (register const u_char *bp, register u_int len, 256 register const u_char *bp2) 257 { 258 const struct ip *ip; 259 const struct ah_hdr *ah; 260 u_int pl_len = len; 261 #ifdef INET6 262 const struct ip6_hdr *ip6; 263 #endif 264 265 ip = (const struct ip *)bp2; 266 #ifdef INET6 267 if (ip->ip_v == 6) { 268 ip6 = (const struct ip6_hdr *)bp2; 269 printf("ah %s > %s", ip6addr_string(&ip6->ip6_src), 270 ip6addr_string(&ip6->ip6_dst)); 271 } else 272 #endif 273 { 274 printf("ah %s > %s", 275 ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); 276 } 277 278 if (pl_len < sizeof(struct ah_hdr)) { 279 printf("[|ah]"); 280 return; 281 } 282 ah = (const struct ah_hdr *)bp; 283 284 printf(" spi 0x%08X seq %d len %d", 285 ntohl(ah->ah_spi), ntohl(ah->ah_seq), len); 286 287 if (vflag) { 288 (void)printf("\n\t[ "); 289 290 pl_len = (ah->ah_pl_len + 2) << 2; /* RFC2402, sec 2.2 */ 291 292 if (len <= pl_len) { 293 (void)printf("truncated"); 294 goto out; 295 } 296 297 switch (ah->ah_nxt_hdr) { 298 299 case IPPROTO_IPIP: /* Tunnel Mode, IP-in-IP */ 300 ip_print(bp + pl_len, len - pl_len); 301 break; 302 303 case IPPROTO_ICMP: /* From here and down; Transport mode */ 304 icmp_print(bp + pl_len, (const u_char *) ip); 305 break; 306 307 case IPPROTO_ICMPV6: 308 icmp6_print(bp + pl_len, (const u_char *) ip); 309 break; 310 311 case IPPROTO_TCP: 312 tcp_print(bp + pl_len, len - pl_len, 313 (const u_char *) ip); 314 break; 315 316 case IPPROTO_UDP: 317 udp_print(bp + pl_len, len - pl_len, 318 (const u_char *) ip); 319 break; 320 321 case IPPROTO_ESP: 322 esp_print(bp + pl_len, len - pl_len, 323 (const u_char *) ip); 324 break; 325 326 case IPPROTO_AH: 327 ah_print(bp + pl_len, len - pl_len, 328 (const u_char *) ip); 329 break; 330 331 default: 332 (void)printf("ip-proto-%d len %d", ah->ah_nxt_hdr, 333 len - pl_len); 334 } 335 out: 336 (void)printf(" ]"); 337 } 338 339 } 340 341 struct ipcomp_hdr { 342 u_char ipcomp_nxt_hdr; 343 u_char ipcomp_flags; 344 u_short ipcomp_cpi; 345 }; 346 347 void 348 ipcomp_print (register const u_char *bp, register u_int len, 349 register const u_char *bp2) 350 { 351 const struct ip *ip; 352 const struct ipcomp_hdr *ipc; 353 u_int plen = len; 354 355 ip = (const struct ip *)bp2; 356 357 printf("ipcomp %s > %s", 358 ipaddr_string(&ip->ip_src), ipaddr_string(&ip->ip_dst)); 359 360 if (plen < sizeof(struct ipcomp_hdr)) { 361 printf("[|ipcomp]"); 362 return; 363 } 364 ipc = (const struct ipcomp_hdr *)bp; 365 366 printf(" cpi 0x%04X flags %x next %x", 367 ntohs(ipc->ipcomp_cpi), ipc->ipcomp_flags, ipc->ipcomp_nxt_hdr); 368 } 369