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