vici: Add commands to enumerate and remove private keys
[strongswan.git] / src / libcharon / plugins / vici / vici_cred.c
1 /*
2 * Copyright (C) 2015-2016 Andreas Steffen
3 * Copyright (C) 2016 Tobias Brunner
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * Copyright (C) 2014 Martin Willi
7 * Copyright (C) 2014 revosec AG
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
14 *
15 * This program is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 * for more details.
19 */
20
21 #include "vici_cred.h"
22 #include "vici_builder.h"
23 #include "vici_cert_info.h"
24
25 #include <credentials/sets/mem_cred.h>
26 #include <credentials/certificates/ac.h>
27 #include <credentials/certificates/crl.h>
28 #include <credentials/certificates/x509.h>
29
30 #include <errno.h>
31
32 typedef struct private_vici_cred_t private_vici_cred_t;
33
34 /**
35 * Directory for saved X.509 CRLs
36 */
37 #define CRL_DIR SWANCTLDIR "/x509crl"
38
39 /**
40 * Private data of an vici_cred_t object.
41 */
42 struct private_vici_cred_t {
43
44 /**
45 * Public vici_cred_t interface.
46 */
47 vici_cred_t public;
48
49 /**
50 * Dispatcher
51 */
52 vici_dispatcher_t *dispatcher;
53
54 /**
55 * credentials
56 */
57 mem_cred_t *creds;
58
59 /**
60 * cache CRLs to disk?
61 */
62 bool cachecrl;
63
64 };
65
66 METHOD(credential_set_t, cache_cert, void,
67 private_vici_cred_t *this, certificate_t *cert)
68 {
69 if (cert->get_type(cert) == CERT_X509_CRL && this->cachecrl)
70 {
71 /* CRLs get written to /etc/swanctl/x509crl/<authkeyId>.crl */
72 crl_t *crl = (crl_t*)cert;
73
74 cert->get_ref(cert);
75 if (this->creds->add_crl(this->creds, crl))
76 {
77 char buf[BUF_LEN];
78 chunk_t chunk, hex;
79 bool is_delta_crl;
80
81 is_delta_crl = crl->is_delta_crl(crl, NULL);
82 chunk = crl->get_authKeyIdentifier(crl);
83 hex = chunk_to_hex(chunk, NULL, FALSE);
84 snprintf(buf, sizeof(buf), "%s/%s%s.crl", CRL_DIR, hex.ptr,
85 is_delta_crl ? "_delta" : "");
86 free(hex.ptr);
87
88 if (cert->get_encoding(cert, CERT_ASN1_DER, &chunk))
89 {
90 if (chunk_write(chunk, buf, 022, TRUE))
91 {
92 DBG1(DBG_CFG, " written crl file '%s' (%d bytes)",
93 buf, chunk.len);
94 }
95 else
96 {
97 DBG1(DBG_CFG, " writing crl file '%s' failed: %s",
98 buf, strerror(errno));
99 }
100 free(chunk.ptr);
101 }
102 }
103 }
104 }
105
106 /**
107 * Create a (error) reply message
108 */
109 static vici_message_t* create_reply(char *fmt, ...)
110 {
111 vici_builder_t *builder;
112 va_list args;
113
114 builder = vici_builder_create();
115 builder->add_kv(builder, "success", fmt ? "no" : "yes");
116 if (fmt)
117 {
118 va_start(args, fmt);
119 builder->vadd_kv(builder, "errmsg", fmt, args);
120 va_end(args);
121 }
122 return builder->finalize(builder);
123 }
124
125 CALLBACK(load_cert, vici_message_t*,
126 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
127 {
128 certificate_t *cert;
129 certificate_type_t type;
130 x509_flag_t ext_flag, flag = X509_NONE;
131 x509_t *x509;
132 chunk_t data;
133 bool trusted = TRUE;
134 char *str;
135
136 str = message->get_str(message, NULL, "type");
137 if (!str)
138 {
139 return create_reply("certificate type missing");
140 }
141 if (enum_from_name(certificate_type_names, str, &type))
142 {
143 if (type == CERT_X509)
144 {
145 str = message->get_str(message, "NONE", "flag");
146 if (!enum_from_name(x509_flag_names, str, &flag))
147 {
148 return create_reply("invalid certificate flag '%s'", str);
149 }
150 }
151 }
152 else if (!vici_cert_info_from_str(str, &type, &flag))
153 {
154 return create_reply("invalid certificate type '%s'", str);
155 }
156
157 data = message->get_value(message, chunk_empty, "data");
158 if (!data.len)
159 {
160 return create_reply("certificate data missing");
161 }
162
163 /* do not set CA flag externally */
164 ext_flag = (flag & X509_CA) ? X509_NONE : flag;
165
166 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, type,
167 BUILD_BLOB_PEM, data,
168 BUILD_X509_FLAG, ext_flag,
169 BUILD_END);
170 if (!cert)
171 {
172 return create_reply("parsing %N certificate failed",
173 certificate_type_names, type);
174 }
175 DBG1(DBG_CFG, "loaded certificate '%Y'", cert->get_subject(cert));
176
177 /* check if CA certificate has CA basic constraint set */
178 if (flag & X509_CA)
179 {
180 char err_msg[] = "ca certificate lacks CA basic constraint, rejected";
181 x509 = (x509_t*)cert;
182
183 if (!(x509->get_flags(x509) & X509_CA))
184 {
185 cert->destroy(cert);
186 DBG1(DBG_CFG, " %s", err_msg);
187 return create_reply(err_msg);
188 }
189 }
190 if (type == CERT_X509_CRL)
191 {
192 this->creds->add_crl(this->creds, (crl_t*)cert);
193 }
194 else
195 {
196 this->creds->add_cert(this->creds, trusted, cert);
197 }
198 return create_reply(NULL);
199 }
200
201 CALLBACK(load_key, vici_message_t*,
202 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
203 {
204 key_type_t type;
205 private_key_t *key;
206 chunk_t data;
207 char *str;
208
209 str = message->get_str(message, NULL, "type");
210 if (!str)
211 {
212 return create_reply("key type missing");
213 }
214 if (strcaseeq(str, "any"))
215 {
216 type = KEY_ANY;
217 }
218 else if (strcaseeq(str, "rsa"))
219 {
220 type = KEY_RSA;
221 }
222 else if (strcaseeq(str, "ecdsa"))
223 {
224 type = KEY_ECDSA;
225 }
226 else if (strcaseeq(str, "bliss"))
227 {
228 type = KEY_BLISS;
229 }
230 else
231 {
232 return create_reply("invalid key type: %s", str);
233 }
234 data = message->get_value(message, chunk_empty, "data");
235 if (!data.len)
236 {
237 return create_reply("key data missing");
238 }
239 key = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
240 BUILD_BLOB_PEM, data, BUILD_END);
241 if (!key)
242 {
243 return create_reply("parsing %N private key failed",
244 key_type_names, type);
245 }
246
247 DBG1(DBG_CFG, "loaded %N private key", key_type_names, type);
248
249 this->creds->add_key(this->creds, key);
250
251 return create_reply(NULL);
252 }
253
254 CALLBACK(unload_key, vici_message_t*,
255 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
256 {
257 chunk_t keyid;
258 char *hex, *msg = NULL;
259
260 hex = message->get_str(message, NULL, "id");
261 if (!hex)
262 {
263 return create_reply("key id missing");
264 }
265 keyid = chunk_from_hex(chunk_from_str(hex), NULL);
266 DBG1(DBG_CFG, "unloaded private key with id %+B", &keyid);
267 if (!this->creds->remove_key(this->creds, keyid))
268 {
269 msg = "key not found";
270 }
271 chunk_free(&keyid);
272 return create_reply(msg);
273 }
274
275 CALLBACK(get_keys, vici_message_t*,
276 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
277 {
278 vici_builder_t *builder;
279 enumerator_t *enumerator;
280 private_key_t *private;
281 chunk_t keyid;
282
283 builder = vici_builder_create();
284 builder->begin_list(builder, "keys");
285
286 enumerator = this->creds->set.create_private_enumerator(&this->creds->set,
287 KEY_ANY, NULL);
288 while (enumerator->enumerate(enumerator, &private))
289 {
290 if (private->get_fingerprint(private, KEYID_PUBKEY_SHA1, &keyid))
291 {
292 builder->add_li(builder, "%+B", &keyid);
293 }
294 }
295 enumerator->destroy(enumerator);
296
297 builder->end_list(builder);
298 return builder->finalize(builder);
299 }
300
301 CALLBACK(shared_owners, bool,
302 linked_list_t *owners, vici_message_t *message, char *name, chunk_t value)
303 {
304 if (streq(name, "owners"))
305 {
306 char buf[256];
307
308 if (!vici_stringify(value, buf, sizeof(buf)))
309 {
310 return FALSE;
311 }
312 owners->insert_last(owners, identification_create_from_string(buf));
313 }
314 return TRUE;
315 }
316
317 CALLBACK(load_shared, vici_message_t*,
318 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
319 {
320 shared_key_type_t type;
321 linked_list_t *owners;
322 chunk_t data;
323 char *str, buf[512] = "";
324 enumerator_t *enumerator;
325 identification_t *owner;
326 int len;
327
328 str = message->get_str(message, NULL, "type");
329 if (!str)
330 {
331 return create_reply("shared key type missing");
332 }
333 if (strcaseeq(str, "ike"))
334 {
335 type = SHARED_IKE;
336 }
337 else if (strcaseeq(str, "eap") || streq(str, "xauth"))
338 {
339 type = SHARED_EAP;
340 }
341 else
342 {
343 return create_reply("invalid shared key type: %s", str);
344 }
345 data = message->get_value(message, chunk_empty, "data");
346 if (!data.len)
347 {
348 return create_reply("shared key data missing");
349 }
350
351 owners = linked_list_create();
352 if (!message->parse(message, NULL, NULL, NULL, shared_owners, owners))
353 {
354 owners->destroy_offset(owners, offsetof(identification_t, destroy));
355 return create_reply("parsing shared key owners failed");
356 }
357 if (owners->get_count(owners) == 0)
358 {
359 owners->insert_last(owners, identification_create_from_string("%any"));
360 }
361
362 enumerator = owners->create_enumerator(owners);
363 while (enumerator->enumerate(enumerator, &owner))
364 {
365 len = strlen(buf);
366 if (len < sizeof(buf))
367 {
368 snprintf(buf + len, sizeof(buf) - len, "%s'%Y'",
369 len ? ", " : "", owner);
370 }
371 }
372 enumerator->destroy(enumerator);
373
374 DBG1(DBG_CFG, "loaded %N shared key for: %s",
375 shared_key_type_names, type, buf);
376
377 this->creds->add_shared_list(this->creds,
378 shared_key_create(type, chunk_clone(data)), owners);
379
380 return create_reply(NULL);
381 }
382
383 CALLBACK(clear_creds, vici_message_t*,
384 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
385 {
386 this->creds->clear(this->creds);
387 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
388
389 return create_reply(NULL);
390 }
391
392 CALLBACK(flush_certs, vici_message_t*,
393 private_vici_cred_t *this, char *name, u_int id, vici_message_t *message)
394 {
395 certificate_type_t type = CERT_ANY;
396 x509_flag_t flag = X509_NONE;
397 char *str;
398
399 str = message->get_str(message, NULL, "type");
400 if (str && !enum_from_name(certificate_type_names, str, &type) &&
401 !vici_cert_info_from_str(str, &type, &flag))
402 {
403 return create_reply("invalid certificate type '%s'", str);
404 }
405 lib->credmgr->flush_cache(lib->credmgr, type);
406
407 return create_reply(NULL);
408 }
409
410 static void manage_command(private_vici_cred_t *this,
411 char *name, vici_command_cb_t cb, bool reg)
412 {
413 this->dispatcher->manage_command(this->dispatcher, name,
414 reg ? cb : NULL, this);
415 }
416
417 /**
418 * (Un-)register dispatcher functions
419 */
420 static void manage_commands(private_vici_cred_t *this, bool reg)
421 {
422 manage_command(this, "clear-creds", clear_creds, reg);
423 manage_command(this, "flush-certs", flush_certs, reg);
424 manage_command(this, "load-cert", load_cert, reg);
425 manage_command(this, "load-key", load_key, reg);
426 manage_command(this, "unload-key", unload_key, reg);
427 manage_command(this, "get-keys", get_keys, reg);
428 manage_command(this, "load-shared", load_shared, reg);
429 }
430
431 METHOD(vici_cred_t, add_cert, certificate_t*,
432 private_vici_cred_t *this, certificate_t *cert)
433 {
434 return this->creds->add_cert_ref(this->creds, TRUE, cert);
435 }
436
437 METHOD(vici_cred_t, destroy, void,
438 private_vici_cred_t *this)
439 {
440 manage_commands(this, FALSE);
441
442 lib->credmgr->remove_set(lib->credmgr, &this->creds->set);
443 this->creds->destroy(this->creds);
444 free(this);
445 }
446
447 /**
448 * See header
449 */
450 vici_cred_t *vici_cred_create(vici_dispatcher_t *dispatcher)
451 {
452 private_vici_cred_t *this;
453
454 INIT(this,
455 .public = {
456 .set = {
457 .create_private_enumerator = (void*)return_null,
458 .create_cert_enumerator = (void*)return_null,
459 .create_shared_enumerator = (void*)return_null,
460 .create_cdp_enumerator = (void*)return_null,
461 .cache_cert = (void*)_cache_cert,
462 },
463 .add_cert = _add_cert,
464 .destroy = _destroy,
465 },
466 .dispatcher = dispatcher,
467 .creds = mem_cred_create(),
468 );
469
470 if (lib->settings->get_bool(lib->settings, "%s.cache_crls", FALSE, lib->ns))
471 {
472 this->cachecrl = TRUE;
473 DBG1(DBG_CFG, "crl caching to %s enabled", CRL_DIR);
474 }
475 lib->credmgr->add_set(lib->credmgr, &this->creds->set);
476
477 manage_commands(this, TRUE);
478
479 return &this->public;
480 }