]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libcharon/plugins/save_keys/save_keys_listener.c
save-keys: Store derived IKE_SA keys in Wireshark format
[thirdparty/strongswan.git] / src / libcharon / plugins / save_keys / save_keys_listener.c
1 /*
2 * Copyright (C) 2016 Codrut Cristian Grosu (codrut.cristian.grosu@gmail.com)
3 * Copyright (C) 2016 IXIA (http://www.ixiacom.com)
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24 #define _GNU_SOURCE
25
26 #include "save_keys_listener.h"
27
28 #include <stdio.h>
29 #include <inttypes.h>
30 #include <errno.h>
31
32 #include <daemon.h>
33
34 typedef struct private_save_keys_listener_t private_save_keys_listener_t;
35 typedef struct algo_map_t algo_map_t;
36
37 /**
38 * Name for IKEv1 decryption table file
39 */
40 static char *ikev1_name = "ikev1_decryption_table";
41
42 /**
43 * Name for IKEv2 decryption table file
44 */
45 static char *ikev2_name = "ikev2_decryption_table";
46
47 /**
48 * Private data.
49 */
50 struct private_save_keys_listener_t {
51
52 /**
53 * Public interface.
54 */
55 save_keys_listener_t public;
56
57 /**
58 * Path to the directory where the decryption tables will be stored.
59 */
60 char *path;
61 };
62
63 METHOD(save_keys_listener_t, destroy, void,
64 private_save_keys_listener_t *this)
65 {
66 free(this);
67 }
68
69 /**
70 * Mapping strongSwan identifiers to Wireshark names
71 */
72 struct algo_map_t {
73
74 /**
75 * IKE identifier
76 */
77 const uint16_t ike;
78
79 /**
80 * Optional key length
81 */
82 const int key_len;
83
84 /**
85 * Name of the algorithm in wireshark
86 */
87 const char *name;
88 };
89
90 /**
91 * Map an algorithm identifier to a name
92 */
93 static inline const char *algo_name(algo_map_t *map, int count,
94 uint16_t alg, int key_len)
95 {
96 int i;
97
98 for (i = 0; i < count; i++)
99 {
100 if (map[i].ike == alg)
101 {
102 if (map[i].key_len == -1 || map[i].key_len == key_len)
103 {
104 return map[i].name;
105 }
106 }
107 }
108 return NULL;
109 }
110
111 /**
112 * Wireshark IKE algorithm identifiers for encryption
113 */
114 static algo_map_t ike_encr[] = {
115 { ENCR_3DES, -1, "3DES [RFC2451]" },
116 { ENCR_NULL, -1, "NULL [RFC2410]" },
117 { ENCR_AES_CBC, 128, "AES-CBC-128 [RFC3602]" },
118 { ENCR_AES_CBC, 192, "AES-CBC-192 [RFC3602]" },
119 { ENCR_AES_CBC, 256, "AES-CBC-256 [RFC3602]" },
120 { ENCR_AES_CTR, 128, "AES-CTR-128 [RFC5930]" },
121 { ENCR_AES_CTR, 192, "AES-CTR-192 [RFC5930]" },
122 { ENCR_AES_CTR, 256, "AES-CTR-256 [RFC5930]" },
123 { ENCR_AES_GCM_ICV8, 128, "AES-GCM-128 with 8 octet ICV [RFC5282]" },
124 { ENCR_AES_GCM_ICV8, 192, "AES-GCM-192 with 8 octet ICV [RFC5282]" },
125 { ENCR_AES_GCM_ICV8, 256, "AES-GCM-256 with 8 octet ICV [RFC5282]" },
126 { ENCR_AES_GCM_ICV12, 128, "AES-GCM-128 with 12 octet ICV [RFC5282]" },
127 { ENCR_AES_GCM_ICV12, 192, "AES-GCM-192 with 12 octet ICV [RFC5282]" },
128 { ENCR_AES_GCM_ICV12, 256, "AES-GCM-256 with 12 octet ICV [RFC5282]" },
129 { ENCR_AES_GCM_ICV16, 128, "AES-GCM-128 with 16 octet ICV [RFC5282]" },
130 { ENCR_AES_GCM_ICV16, 192, "AES-GCM-192 with 16 octet ICV [RFC5282]" },
131 { ENCR_AES_GCM_ICV16, 256, "AES-GCM-256 with 16 octet ICV [RFC5282]" },
132 { ENCR_AES_CCM_ICV8, 128, "AES-CCM-128 with 8 octet ICV [RFC5282]" },
133 { ENCR_AES_CCM_ICV8, 192, "AES-CCM-192 with 8 octet ICV [RFC5282]" },
134 { ENCR_AES_CCM_ICV8, 256, "AES-CCM-256 with 8 octet ICV [RFC5282]" },
135 { ENCR_AES_CCM_ICV12, 128, "AES-CCM-128 with 12 octet ICV [RFC5282]" },
136 { ENCR_AES_CCM_ICV12, 192, "AES-CCM-192 with 12 octet ICV [RFC5282]" },
137 { ENCR_AES_CCM_ICV12, 256, "AES-CCM-256 with 12 octet ICV [RFC5282]" },
138 { ENCR_AES_CCM_ICV16, 128, "AES-CCM-128 with 16 octet ICV [RFC5282]" },
139 { ENCR_AES_CCM_ICV16, 192, "AES-CCM-192 with 16 octet ICV [RFC5282]" },
140 { ENCR_AES_CCM_ICV16, 256, "AES-CCM-256 with 16 octet ICV [RFC5282]" },
141 };
142
143 /**
144 * Wireshark IKE algorithms for integrity
145 */
146 static algo_map_t ike_integ[] = {
147 { AUTH_HMAC_MD5_96, -1, "HMAC_MD5_96 [RFC2403]" },
148 { AUTH_HMAC_SHA1_96, -1, "HMAC_SHA1_96 [RFC2404]" },
149 { AUTH_HMAC_MD5_128, -1, "HMAC_MD5_128 [RFC4595]" },
150 { AUTH_HMAC_SHA1_160, -1, "HMAC_SHA1_160 [RFC4595]" },
151 { AUTH_HMAC_SHA2_256_128, -1, "HMAC_SHA2_256_128 [RFC4868]" },
152 { AUTH_HMAC_SHA2_384_192, -1, "HMAC_SHA2_384_192 [RFC4868]" },
153 { AUTH_HMAC_SHA2_512_256, -1, "HMAC_SHA2_512_256 [RFC4868]" },
154 { AUTH_HMAC_SHA2_256_96, -1, "HMAC_SHA2_256_96 [draft-ietf-ipsec-ciph-sha-256-00]" },
155 { AUTH_UNDEFINED, -1, "NONE [RFC4306]" },
156 };
157
158 /**
159 * Map an IKE proposal
160 */
161 static inline void ike_names(proposal_t *proposal, const char **enc,
162 const char **integ)
163 {
164 uint16_t alg, len;
165
166 if (proposal->get_algorithm(proposal, ENCRYPTION_ALGORITHM, &alg, &len))
167 {
168 *enc = algo_name(ike_encr, countof(ike_encr), alg, len);
169 }
170 if (encryption_algorithm_is_aead(alg))
171 {
172 alg = AUTH_UNDEFINED;
173 }
174 else if (!proposal->get_algorithm(proposal, INTEGRITY_ALGORITHM, &alg, NULL))
175 {
176 return;
177 }
178 *integ = algo_name(ike_integ, countof(ike_integ), alg, -1);
179 }
180
181
182 METHOD(listener_t, ike_derived_keys, bool,
183 private_save_keys_listener_t *this, ike_sa_t *ike_sa, chunk_t sk_ei,
184 chunk_t sk_er, chunk_t sk_ai, chunk_t sk_ar)
185 {
186 ike_version_t version;
187 ike_sa_id_t *id;
188 const char *enc = NULL, *integ = NULL;
189 char *path, *name;
190 FILE *file;
191
192 if (!this->path)
193 {
194 return TRUE;
195 }
196
197 version = ike_sa->get_version(ike_sa);
198 name = version == IKEV2 ? ikev2_name : ikev1_name;
199 if (asprintf(&path, "%s/%s", this->path, name) < 0)
200 {
201 DBG1(DBG_IKE, "failed to build path to IKE key table");
202 return TRUE;
203 }
204
205 file = fopen(path, "a");
206 if (file)
207 {
208 id = ike_sa->get_id(ike_sa);
209 if (version == IKEV2)
210 {
211 ike_names(ike_sa->get_proposal(ike_sa), &enc, &integ);
212 if (enc && integ)
213 {
214 fprintf(file, "%.16"PRIx64",%.16"PRIx64",%+B,%+B,\"%s\","
215 "%+B,%+B,\"%s\"\n", be64toh(id->get_initiator_spi(id)),
216 be64toh(id->get_responder_spi(id)), &sk_ei, &sk_er,
217 enc, &sk_ai, &sk_ar, integ);
218 }
219 }
220 else
221 {
222 fprintf(file, "%.16"PRIx64",%+B\n",
223 be64toh(id->get_initiator_spi(id)), &sk_ei);
224 }
225 fclose(file);
226 }
227 else
228 {
229 DBG1(DBG_IKE, "failed to open IKE key table '%s': %s", path,
230 strerror(errno));
231 }
232 free(path);
233 return TRUE;
234 }
235
236 /**
237 * See header.
238 */
239 save_keys_listener_t *save_keys_listener_create()
240 {
241 private_save_keys_listener_t *this;
242
243 INIT(this,
244 .public = {
245 .listener = {
246 .ike_derived_keys = _ike_derived_keys,
247 },
248 .destroy = _destroy,
249 },
250 .path = lib->settings->get_str(lib->settings,
251 "%s.plugins.save-keys.wireshark_keys",
252 NULL, lib->ns),
253 );
254
255 return &this->public;
256 }