]>
Commit | Line | Data |
---|---|---|
1da1ba01 TB |
1 | /* |
2 | * Copyright (C) 2018 Tobias Brunner | |
3 | * HSR Hochschule fuer Technik Rapperswil | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation; either version 2 of the License, or (at your | |
8 | * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, but | |
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
12 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | * for more details. | |
14 | */ | |
345cd468 CCG |
15 | /* |
16 | * Copyright (C) 2016 Codrut Cristian Grosu (codrut.cristian.grosu@gmail.com) | |
17 | * Copyright (C) 2016 IXIA (http://www.ixiacom.com) | |
18 | * | |
19 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
20 | * of this software and associated documentation files (the "Software"), to deal | |
21 | * in the Software without restriction, including without limitation the rights | |
22 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
23 | * copies of the Software, and to permit persons to whom the Software is | |
24 | * furnished to do so, subject to the following conditions: | |
25 | * | |
26 | * The above copyright notice and this permission notice shall be included in | |
27 | * all copies or substantial portions of the Software. | |
28 | * | |
29 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
30 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
31 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
32 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
33 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
34 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
35 | * THE SOFTWARE. | |
36 | */ | |
37 | ||
4be7db5f CCG |
38 | #define _GNU_SOURCE |
39 | ||
345cd468 CCG |
40 | #include "save_keys_listener.h" |
41 | ||
4be7db5f CCG |
42 | #include <stdio.h> |
43 | #include <inttypes.h> | |
44 | #include <errno.h> | |
45 | ||
46 | #include <daemon.h> | |
47 | ||
345cd468 | 48 | typedef struct private_save_keys_listener_t private_save_keys_listener_t; |
4be7db5f CCG |
49 | typedef struct algo_map_t algo_map_t; |
50 | ||
51 | /** | |
52 | * Name for IKEv1 decryption table file | |
53 | */ | |
54 | static char *ikev1_name = "ikev1_decryption_table"; | |
55 | ||
56 | /** | |
57 | * Name for IKEv2 decryption table file | |
58 | */ | |
59 | static char *ikev2_name = "ikev2_decryption_table"; | |
345cd468 | 60 | |
88e151d1 CCG |
61 | /** |
62 | * Name for esp decryption table file | |
63 | */ | |
64 | static char *esp_name = "esp_sa"; | |
65 | ||
345cd468 CCG |
66 | /** |
67 | * Private data. | |
68 | */ | |
69 | struct private_save_keys_listener_t { | |
70 | ||
71 | /** | |
72 | * Public interface. | |
73 | */ | |
74 | save_keys_listener_t public; | |
4be7db5f CCG |
75 | |
76 | /** | |
77 | * Path to the directory where the decryption tables will be stored. | |
78 | */ | |
79 | char *path; | |
1da1ba01 TB |
80 | |
81 | /** | |
82 | * Whether to save IKE keys | |
83 | */ | |
84 | bool ike; | |
85 | ||
86 | /** | |
87 | * Whether to save ESP keys | |
88 | */ | |
89 | bool esp; | |
345cd468 CCG |
90 | }; |
91 | ||
92 | METHOD(save_keys_listener_t, destroy, void, | |
93 | private_save_keys_listener_t *this) | |
94 | { | |
95 | free(this); | |
96 | } | |
97 | ||
4be7db5f CCG |
98 | /** |
99 | * Mapping strongSwan identifiers to Wireshark names | |
100 | */ | |
101 | struct algo_map_t { | |
102 | ||
103 | /** | |
104 | * IKE identifier | |
105 | */ | |
106 | const uint16_t ike; | |
107 | ||
108 | /** | |
109 | * Optional key length | |
110 | */ | |
111 | const int key_len; | |
112 | ||
113 | /** | |
114 | * Name of the algorithm in wireshark | |
115 | */ | |
116 | const char *name; | |
117 | }; | |
118 | ||
119 | /** | |
120 | * Map an algorithm identifier to a name | |
121 | */ | |
122 | static inline const char *algo_name(algo_map_t *map, int count, | |
123 | uint16_t alg, int key_len) | |
124 | { | |
125 | int i; | |
126 | ||
127 | for (i = 0; i < count; i++) | |
128 | { | |
129 | if (map[i].ike == alg) | |
130 | { | |
131 | if (map[i].key_len == -1 || map[i].key_len == key_len) | |
132 | { | |
133 | return map[i].name; | |
134 | } | |
135 | } | |
136 | } | |
137 | return NULL; | |
138 | } | |
139 | ||
140 | /** | |
141 | * Wireshark IKE algorithm identifiers for encryption | |
142 | */ | |
143 | static algo_map_t ike_encr[] = { | |
144 | { ENCR_3DES, -1, "3DES [RFC2451]" }, | |
145 | { ENCR_NULL, -1, "NULL [RFC2410]" }, | |
146 | { ENCR_AES_CBC, 128, "AES-CBC-128 [RFC3602]" }, | |
147 | { ENCR_AES_CBC, 192, "AES-CBC-192 [RFC3602]" }, | |
148 | { ENCR_AES_CBC, 256, "AES-CBC-256 [RFC3602]" }, | |
149 | { ENCR_AES_CTR, 128, "AES-CTR-128 [RFC5930]" }, | |
150 | { ENCR_AES_CTR, 192, "AES-CTR-192 [RFC5930]" }, | |
151 | { ENCR_AES_CTR, 256, "AES-CTR-256 [RFC5930]" }, | |
152 | { ENCR_AES_GCM_ICV8, 128, "AES-GCM-128 with 8 octet ICV [RFC5282]" }, | |
153 | { ENCR_AES_GCM_ICV8, 192, "AES-GCM-192 with 8 octet ICV [RFC5282]" }, | |
154 | { ENCR_AES_GCM_ICV8, 256, "AES-GCM-256 with 8 octet ICV [RFC5282]" }, | |
155 | { ENCR_AES_GCM_ICV12, 128, "AES-GCM-128 with 12 octet ICV [RFC5282]" }, | |
156 | { ENCR_AES_GCM_ICV12, 192, "AES-GCM-192 with 12 octet ICV [RFC5282]" }, | |
157 | { ENCR_AES_GCM_ICV12, 256, "AES-GCM-256 with 12 octet ICV [RFC5282]" }, | |
158 | { ENCR_AES_GCM_ICV16, 128, "AES-GCM-128 with 16 octet ICV [RFC5282]" }, | |
159 | { ENCR_AES_GCM_ICV16, 192, "AES-GCM-192 with 16 octet ICV [RFC5282]" }, | |
160 | { ENCR_AES_GCM_ICV16, 256, "AES-GCM-256 with 16 octet ICV [RFC5282]" }, | |
161 | { ENCR_AES_CCM_ICV8, 128, "AES-CCM-128 with 8 octet ICV [RFC5282]" }, | |
162 | { ENCR_AES_CCM_ICV8, 192, "AES-CCM-192 with 8 octet ICV [RFC5282]" }, | |
163 | { ENCR_AES_CCM_ICV8, 256, "AES-CCM-256 with 8 octet ICV [RFC5282]" }, | |
164 | { ENCR_AES_CCM_ICV12, 128, "AES-CCM-128 with 12 octet ICV [RFC5282]" }, | |
165 | { ENCR_AES_CCM_ICV12, 192, "AES-CCM-192 with 12 octet ICV [RFC5282]" }, | |
166 | { ENCR_AES_CCM_ICV12, 256, "AES-CCM-256 with 12 octet ICV [RFC5282]" }, | |
167 | { ENCR_AES_CCM_ICV16, 128, "AES-CCM-128 with 16 octet ICV [RFC5282]" }, | |
168 | { ENCR_AES_CCM_ICV16, 192, "AES-CCM-192 with 16 octet ICV [RFC5282]" }, | |
169 | { ENCR_AES_CCM_ICV16, 256, "AES-CCM-256 with 16 octet ICV [RFC5282]" }, | |
170 | }; | |
171 | ||
172 | /** | |
173 | * Wireshark IKE algorithms for integrity | |
174 | */ | |
175 | static algo_map_t ike_integ[] = { | |
176 | { AUTH_HMAC_MD5_96, -1, "HMAC_MD5_96 [RFC2403]" }, | |
177 | { AUTH_HMAC_SHA1_96, -1, "HMAC_SHA1_96 [RFC2404]" }, | |
178 | { AUTH_HMAC_MD5_128, -1, "HMAC_MD5_128 [RFC4595]" }, | |
179 | { AUTH_HMAC_SHA1_160, -1, "HMAC_SHA1_160 [RFC4595]" }, | |
180 | { AUTH_HMAC_SHA2_256_128, -1, "HMAC_SHA2_256_128 [RFC4868]" }, | |
181 | { AUTH_HMAC_SHA2_384_192, -1, "HMAC_SHA2_384_192 [RFC4868]" }, | |
182 | { AUTH_HMAC_SHA2_512_256, -1, "HMAC_SHA2_512_256 [RFC4868]" }, | |
183 | { AUTH_HMAC_SHA2_256_96, -1, "HMAC_SHA2_256_96 [draft-ietf-ipsec-ciph-sha-256-00]" }, | |
184 | { AUTH_UNDEFINED, -1, "NONE [RFC4306]" }, | |
185 | }; | |
186 | ||
187 | /** | |
188 | * Map an IKE proposal | |
189 | */ | |
190 | static inline void ike_names(proposal_t *proposal, const char **enc, | |
191 | const char **integ) | |
192 | { | |
193 | uint16_t alg, len; | |
194 | ||
195 | if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len)) | |
196 | { | |
197 | *enc = algo_name(ike_encr, countof(ike_encr), alg, len); | |
198 | } | |
199 | if (encryption_algorithm_is_aead(alg)) | |
200 | { | |
201 | alg = AUTH_UNDEFINED; | |
202 | } | |
203 | else if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) | |
204 | { | |
205 | return; | |
206 | } | |
207 | *integ = algo_name(ike_integ, countof(ike_integ), alg, -1); | |
208 | } | |
209 | ||
88e151d1 CCG |
210 | /** |
211 | * Wireshark ESP algorithm identifiers for encryption | |
212 | */ | |
213 | static algo_map_t esp_encr[] = { | |
214 | { ENCR_NULL, -1, "NULL" }, | |
215 | { ENCR_3DES, -1, "TripleDes-CBC [RFC2451]" }, | |
216 | { ENCR_AES_CBC, -1, "AES-CBC [RFC3602]" }, | |
217 | { ENCR_AES_CTR, -1, "AES-CTR [RFC3686]" }, | |
218 | { ENCR_DES, -1, "DES-CBC [RFC2405]" }, | |
219 | { ENCR_CAST, -1, "CAST5-CBC [RFC2144]" }, | |
220 | { ENCR_BLOWFISH, -1, "BLOWFISH-CBC [RFC2451]" }, | |
221 | { ENCR_TWOFISH_CBC, -1, "TWOFISH-CBC" }, | |
222 | { ENCR_AES_GCM_ICV8, -1, "AES-GCM [RFC4106]" }, | |
223 | { ENCR_AES_GCM_ICV12, -1, "AES-GCM [RFC4106]" }, | |
224 | { ENCR_AES_GCM_ICV16, -1, "AES-GCM [RFC4106]" }, | |
225 | }; | |
226 | ||
227 | /** | |
228 | * Wireshark ESP algorithms for integrity | |
229 | */ | |
230 | static algo_map_t esp_integ[] = { | |
231 | { AUTH_HMAC_SHA1_96, -1, "HMAC-SHA-1-96 [RFC2404]" }, | |
232 | { AUTH_HMAC_MD5_96, -1, "HMAC-MD5-96 [RFC2403]" }, | |
233 | { AUTH_HMAC_SHA2_256_128, -1, "HMAC-SHA-256-128 [RFC4868]" }, | |
234 | { AUTH_HMAC_SHA2_384_192, -1, "HMAC-SHA-384-192 [RFC4868]" }, | |
235 | { AUTH_HMAC_SHA2_512_256, -1, "HMAC-SHA-512-256 [RFC4868]" }, | |
236 | { AUTH_HMAC_SHA2_256_96, -1, "HMAC-SHA-256-96 [draft-ietf-ipsec-ciph-sha-256-00]" }, | |
237 | { AUTH_UNDEFINED, 64, "ANY 64 bit authentication [no checking]" }, | |
238 | { AUTH_UNDEFINED, 96, "ANY 96 bit authentication [no checking]" }, | |
239 | { AUTH_UNDEFINED, 128, "ANY 128 bit authentication [no checking]" }, | |
240 | { AUTH_UNDEFINED, 192, "ANY 192 bit authentication [no checking]" }, | |
241 | { AUTH_UNDEFINED, 256, "ANY 256 bit authentication [no checking]" }, | |
242 | { AUTH_UNDEFINED, -1, "NULL" }, | |
243 | }; | |
244 | ||
245 | /** | |
246 | * Map an ESP proposal | |
247 | */ | |
248 | static inline void esp_names(proposal_t *proposal, const char **enc, | |
249 | const char **integ) | |
250 | { | |
251 | uint16_t alg, len; | |
252 | ||
253 | if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len)) | |
254 | { | |
255 | *enc = algo_name(esp_encr, countof(esp_encr), alg, len); | |
256 | } | |
257 | len = -1; | |
258 | if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL)) | |
259 | { | |
260 | switch (alg) | |
261 | { | |
262 | case ENCR_AES_GCM_ICV8: | |
263 | len = 64; | |
264 | break; | |
265 | case ENCR_AES_GCM_ICV12: | |
266 | len = 64; | |
267 | break; | |
268 | case ENCR_AES_GCM_ICV16: | |
269 | len = 128; | |
270 | break; | |
271 | } | |
272 | alg = AUTH_UNDEFINED; | |
273 | } | |
274 | *integ = algo_name(esp_integ, countof(esp_integ), alg, len); | |
275 | } | |
4be7db5f CCG |
276 | |
277 | METHOD(listener_t, ike_derived_keys, bool, | |
278 | private_save_keys_listener_t *this, ike_sa_t *ike_sa, chunk_t sk_ei, | |
279 | chunk_t sk_er, chunk_t sk_ai, chunk_t sk_ar) | |
280 | { | |
281 | ike_version_t version; | |
282 | ike_sa_id_t *id; | |
283 | const char *enc = NULL, *integ = NULL; | |
284 | char *path, *name; | |
285 | FILE *file; | |
286 | ||
1da1ba01 | 287 | if (!this->path || !this->ike) |
4be7db5f CCG |
288 | { |
289 | return TRUE; | |
290 | } | |
291 | ||
292 | version = ike_sa->get_version(ike_sa); | |
293 | name = version == IKEV2 ? ikev2_name : ikev1_name; | |
294 | if (asprintf(&path, "%s/%s", this->path, name) < 0) | |
295 | { | |
296 | DBG1(DBG_IKE, "failed to build path to IKE key table"); | |
297 | return TRUE; | |
298 | } | |
299 | ||
300 | file = fopen(path, "a"); | |
301 | if (file) | |
302 | { | |
303 | id = ike_sa->get_id(ike_sa); | |
304 | if (version == IKEV2) | |
305 | { | |
306 | ike_names(ike_sa->get_proposal(ike_sa), &enc, &integ); | |
307 | if (enc && integ) | |
308 | { | |
309 | fprintf(file, "%.16"PRIx64",%.16"PRIx64",%+B,%+B,\"%s\"," | |
310 | "%+B,%+B,\"%s\"\n", be64toh(id->get_initiator_spi(id)), | |
311 | be64toh(id->get_responder_spi(id)), &sk_ei, &sk_er, | |
312 | enc, &sk_ai, &sk_ar, integ); | |
313 | } | |
314 | } | |
315 | else | |
316 | { | |
317 | fprintf(file, "%.16"PRIx64",%+B\n", | |
318 | be64toh(id->get_initiator_spi(id)), &sk_ei); | |
319 | } | |
320 | fclose(file); | |
321 | } | |
322 | else | |
323 | { | |
324 | DBG1(DBG_IKE, "failed to open IKE key table '%s': %s", path, | |
325 | strerror(errno)); | |
326 | } | |
327 | free(path); | |
328 | return TRUE; | |
329 | } | |
330 | ||
88e151d1 CCG |
331 | METHOD(listener_t, child_derived_keys, bool, |
332 | private_save_keys_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa, | |
333 | bool initiator, chunk_t encr_i, chunk_t encr_r, chunk_t integ_i, | |
334 | chunk_t integ_r) | |
335 | { | |
336 | host_t *init, *resp; | |
337 | uint32_t spi_i, spi_r; | |
338 | const char *enc = NULL, *integ = NULL; | |
339 | char *path, *family; | |
340 | FILE *file; | |
341 | ||
1da1ba01 TB |
342 | if (!this->path || !this->esp || |
343 | child_sa->get_protocol(child_sa) != PROTO_ESP) | |
88e151d1 CCG |
344 | { |
345 | return TRUE; | |
346 | } | |
347 | ||
348 | if (asprintf(&path, "%s/%s", this->path, esp_name) < 0) | |
349 | { | |
350 | DBG1(DBG_CHD, "failed to build path to ESP key table"); | |
351 | return TRUE; | |
352 | } | |
353 | ||
354 | file = fopen(path, "a"); | |
355 | if (file) | |
356 | { | |
357 | esp_names(child_sa->get_proposal(child_sa), &enc, &integ); | |
358 | if (enc && integ) | |
359 | { | |
360 | /* Since the IPs are printed this is not compatible with MOBIKE */ | |
361 | if (initiator) | |
362 | { | |
363 | init = ike_sa->get_my_host(ike_sa); | |
364 | resp = ike_sa->get_other_host(ike_sa); | |
365 | } | |
366 | else | |
367 | { | |
368 | init = ike_sa->get_other_host(ike_sa); | |
369 | resp = ike_sa->get_my_host(ike_sa); | |
370 | } | |
371 | spi_i = child_sa->get_spi(child_sa, initiator); | |
372 | spi_r = child_sa->get_spi(child_sa, !initiator); | |
373 | family = init->get_family(init) == AF_INET ? "IPv4" : "IPv6"; | |
374 | fprintf(file, "\"%s\",\"%H\",\"%H\",\"0x%.8x\",\"%s\",\"0x%+B\"," | |
375 | "\"%s\",\"0x%+B\"\n", family, init, resp, ntohl(spi_r), enc, | |
376 | &encr_i, integ, &integ_i); | |
377 | fprintf(file, "\"%s\",\"%H\",\"%H\",\"0x%.8x\",\"%s\",\"0x%+B\"," | |
378 | "\"%s\",\"0x%+B\"\n", family, resp, init, ntohl(spi_i), enc, | |
379 | &encr_r, integ, &integ_r); | |
380 | } | |
381 | fclose(file); | |
382 | } | |
383 | else | |
384 | { | |
385 | DBG1(DBG_CHD, "failed to open ESP key table '%s': %s", path, | |
386 | strerror(errno)); | |
387 | } | |
388 | free(path); | |
389 | return TRUE; | |
390 | } | |
391 | ||
345cd468 CCG |
392 | /** |
393 | * See header. | |
394 | */ | |
395 | save_keys_listener_t *save_keys_listener_create() | |
396 | { | |
397 | private_save_keys_listener_t *this; | |
398 | ||
399 | INIT(this, | |
400 | .public = { | |
401 | .listener = { | |
4be7db5f | 402 | .ike_derived_keys = _ike_derived_keys, |
88e151d1 | 403 | .child_derived_keys = _child_derived_keys, |
345cd468 CCG |
404 | }, |
405 | .destroy = _destroy, | |
406 | }, | |
4be7db5f CCG |
407 | .path = lib->settings->get_str(lib->settings, |
408 | "%s.plugins.save-keys.wireshark_keys", | |
409 | NULL, lib->ns), | |
1da1ba01 TB |
410 | .esp = lib->settings->get_bool(lib->settings, |
411 | "%s.plugins.save-keys.esp", | |
412 | FALSE, lib->ns), | |
413 | .ike = lib->settings->get_bool(lib->settings, | |
414 | "%s.plugins.save-keys.ike", | |
415 | FALSE, lib->ns), | |
345cd468 | 416 | ); |
4be7db5f | 417 | |
bac71410 TB |
418 | if (this->path && (this->ike || this->esp)) |
419 | { | |
420 | char *keys = "IKE"; | |
421 | ||
422 | if (this->ike && this->esp) | |
423 | { | |
424 | keys = "IKE AND ESP"; | |
425 | } | |
426 | else if (this->esp) | |
427 | { | |
428 | keys = "ESP"; | |
429 | } | |
430 | DBG0(DBG_DMN, "!!", keys, this->path); | |
431 | DBG0(DBG_DMN, "!! WARNING: SAVING %s KEYS TO '%s'", keys, this->path); | |
432 | DBG0(DBG_DMN, "!!", keys, this->path); | |
433 | } | |
345cd468 CCG |
434 | return &this->public; |
435 | } |