2 * Copyright (C) 2015-2016 Andreas Steffen
3 * Copyright (C) 2016 Tobias Brunner
4 * HSR Hochschule fuer Technik Rapperswil
6 * Copyright (C) 2014 Martin Willi
7 * Copyright (C) 2014 revosec AG
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>.
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
21 #include "vici_cred.h"
22 #include "vici_builder.h"
23 #include "vici_cert_info.h"
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>
32 typedef struct private_vici_cred_t private_vici_cred_t
;
35 * Directory for saved X.509 CRLs
37 #define CRL_DIR SWANCTLDIR "/x509crl"
40 * Private data of an vici_cred_t object.
42 struct private_vici_cred_t
{
45 * Public vici_cred_t interface.
52 vici_dispatcher_t
*dispatcher
;
66 METHOD(credential_set_t
, cache_cert
, void,
67 private_vici_cred_t
*this, certificate_t
*cert
)
69 if (cert
->get_type(cert
) == CERT_X509_CRL
&& this->cachecrl
)
71 /* CRLs get written to /etc/swanctl/x509crl/<authkeyId>.crl */
72 crl_t
*crl
= (crl_t
*)cert
;
75 if (this->creds
->add_crl(this->creds
, crl
))
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" : "");
88 if (cert
->get_encoding(cert
, CERT_ASN1_DER
, &chunk
))
90 if (chunk_write(chunk
, buf
, 022, TRUE
))
92 DBG1(DBG_CFG
, " written crl file '%s' (%d bytes)",
97 DBG1(DBG_CFG
, " writing crl file '%s' failed: %s",
98 buf
, strerror(errno
));
107 * Create a (error) reply message
109 static vici_message_t
* create_reply(char *fmt
, ...)
111 vici_builder_t
*builder
;
114 builder
= vici_builder_create();
115 builder
->add_kv(builder
, "success", fmt ?
"no" : "yes");
119 builder
->vadd_kv(builder
, "errmsg", fmt
, args
);
122 return builder
->finalize(builder
);
125 CALLBACK(load_cert
, vici_message_t
*,
126 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
129 certificate_type_t type
;
130 x509_flag_t ext_flag
, flag
= X509_NONE
;
136 str
= message
->get_str(message
, NULL
, "type");
139 return create_reply("certificate type missing");
141 if (enum_from_name(certificate_type_names
, str
, &type
))
143 if (type
== CERT_X509
)
145 str
= message
->get_str(message
, "NONE", "flag");
146 if (!enum_from_name(x509_flag_names
, str
, &flag
))
148 return create_reply("invalid certificate flag '%s'", str
);
152 else if (!vici_cert_info_from_str(str
, &type
, &flag
))
154 return create_reply("invalid certificate type '%s'", str
);
157 data
= message
->get_value(message
, chunk_empty
, "data");
160 return create_reply("certificate data missing");
163 /* do not set CA flag externally */
164 ext_flag
= (flag
& X509_CA
) ? X509_NONE
: flag
;
166 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, type
,
167 BUILD_BLOB_PEM
, data
,
168 BUILD_X509_FLAG
, ext_flag
,
172 return create_reply("parsing %N certificate failed",
173 certificate_type_names
, type
);
175 DBG1(DBG_CFG
, "loaded certificate '%Y'", cert
->get_subject(cert
));
177 /* check if CA certificate has CA basic constraint set */
180 char err_msg
[] = "ca certificate lacks CA basic constraint, rejected";
181 x509
= (x509_t
*)cert
;
183 if (!(x509
->get_flags(x509
) & X509_CA
))
186 DBG1(DBG_CFG
, " %s", err_msg
);
187 return create_reply(err_msg
);
190 if (type
== CERT_X509_CRL
)
192 this->creds
->add_crl(this->creds
, (crl_t
*)cert
);
196 this->creds
->add_cert(this->creds
, trusted
, cert
);
198 return create_reply(NULL
);
201 CALLBACK(load_key
, vici_message_t
*,
202 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
209 str
= message
->get_str(message
, NULL
, "type");
212 return create_reply("key type missing");
214 if (strcaseeq(str
, "any"))
218 else if (strcaseeq(str
, "rsa"))
222 else if (strcaseeq(str
, "ecdsa"))
226 else if (strcaseeq(str
, "bliss"))
232 return create_reply("invalid key type: %s", str
);
234 data
= message
->get_value(message
, chunk_empty
, "data");
237 return create_reply("key data missing");
239 key
= lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
, type
,
240 BUILD_BLOB_PEM
, data
, BUILD_END
);
243 return create_reply("parsing %N private key failed",
244 key_type_names
, type
);
247 DBG1(DBG_CFG
, "loaded %N private key", key_type_names
, type
);
249 this->creds
->add_key(this->creds
, key
);
251 return create_reply(NULL
);
254 CALLBACK(unload_key
, vici_message_t
*,
255 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
258 char *hex
, *msg
= NULL
;
260 hex
= message
->get_str(message
, NULL
, "id");
263 return create_reply("key id missing");
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
))
269 msg
= "key not found";
272 return create_reply(msg
);
275 CALLBACK(get_keys
, vici_message_t
*,
276 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
278 vici_builder_t
*builder
;
279 enumerator_t
*enumerator
;
280 private_key_t
*private;
283 builder
= vici_builder_create();
284 builder
->begin_list(builder
, "keys");
286 enumerator
= this->creds
->set
.create_private_enumerator(&this->creds
->set
,
288 while (enumerator
->enumerate(enumerator
, &private))
290 if (private->get_fingerprint(private, KEYID_PUBKEY_SHA1
, &keyid
))
292 builder
->add_li(builder
, "%+B", &keyid
);
295 enumerator
->destroy(enumerator
);
297 builder
->end_list(builder
);
298 return builder
->finalize(builder
);
301 CALLBACK(shared_owners
, bool,
302 linked_list_t
*owners
, vici_message_t
*message
, char *name
, chunk_t value
)
304 if (streq(name
, "owners"))
308 if (!vici_stringify(value
, buf
, sizeof(buf
)))
312 owners
->insert_last(owners
, identification_create_from_string(buf
));
317 CALLBACK(load_shared
, vici_message_t
*,
318 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
320 shared_key_type_t type
;
321 linked_list_t
*owners
;
323 char *str
, buf
[512] = "";
324 enumerator_t
*enumerator
;
325 identification_t
*owner
;
328 str
= message
->get_str(message
, NULL
, "type");
331 return create_reply("shared key type missing");
333 if (strcaseeq(str
, "ike"))
337 else if (strcaseeq(str
, "eap") || streq(str
, "xauth"))
343 return create_reply("invalid shared key type: %s", str
);
345 data
= message
->get_value(message
, chunk_empty
, "data");
348 return create_reply("shared key data missing");
351 owners
= linked_list_create();
352 if (!message
->parse(message
, NULL
, NULL
, NULL
, shared_owners
, owners
))
354 owners
->destroy_offset(owners
, offsetof(identification_t
, destroy
));
355 return create_reply("parsing shared key owners failed");
357 if (owners
->get_count(owners
) == 0)
359 owners
->insert_last(owners
, identification_create_from_string("%any"));
362 enumerator
= owners
->create_enumerator(owners
);
363 while (enumerator
->enumerate(enumerator
, &owner
))
366 if (len
< sizeof(buf
))
368 snprintf(buf
+ len
, sizeof(buf
) - len
, "%s'%Y'",
369 len ?
", " : "", owner
);
372 enumerator
->destroy(enumerator
);
374 DBG1(DBG_CFG
, "loaded %N shared key for: %s",
375 shared_key_type_names
, type
, buf
);
377 this->creds
->add_shared_list(this->creds
,
378 shared_key_create(type
, chunk_clone(data
)), owners
);
380 return create_reply(NULL
);
383 CALLBACK(clear_creds
, vici_message_t
*,
384 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
386 this->creds
->clear(this->creds
);
387 lib
->credmgr
->flush_cache(lib
->credmgr
, CERT_ANY
);
389 return create_reply(NULL
);
392 CALLBACK(flush_certs
, vici_message_t
*,
393 private_vici_cred_t
*this, char *name
, u_int id
, vici_message_t
*message
)
395 certificate_type_t type
= CERT_ANY
;
396 x509_flag_t flag
= X509_NONE
;
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
))
403 return create_reply("invalid certificate type '%s'", str
);
405 lib
->credmgr
->flush_cache(lib
->credmgr
, type
);
407 return create_reply(NULL
);
410 static void manage_command(private_vici_cred_t
*this,
411 char *name
, vici_command_cb_t cb
, bool reg
)
413 this->dispatcher
->manage_command(this->dispatcher
, name
,
414 reg ? cb
: NULL
, this);
418 * (Un-)register dispatcher functions
420 static void manage_commands(private_vici_cred_t
*this, bool reg
)
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
);
431 METHOD(vici_cred_t
, add_cert
, certificate_t
*,
432 private_vici_cred_t
*this, certificate_t
*cert
)
434 return this->creds
->add_cert_ref(this->creds
, TRUE
, cert
);
437 METHOD(vici_cred_t
, destroy
, void,
438 private_vici_cred_t
*this)
440 manage_commands(this, FALSE
);
442 lib
->credmgr
->remove_set(lib
->credmgr
, &this->creds
->set
);
443 this->creds
->destroy(this->creds
);
450 vici_cred_t
*vici_cred_create(vici_dispatcher_t
*dispatcher
)
452 private_vici_cred_t
*this;
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
,
463 .add_cert
= _add_cert
,
466 .dispatcher
= dispatcher
,
467 .creds
= mem_cred_create(),
470 if (lib
->settings
->get_bool(lib
->settings
, "%s.cache_crls", FALSE
, lib
->ns
))
472 this->cachecrl
= TRUE
;
473 DBG1(DBG_CFG
, "crl caching to %s enabled", CRL_DIR
);
475 lib
->credmgr
->add_set(lib
->credmgr
, &this->creds
->set
);
477 manage_commands(this, TRUE
);
479 return &this->public;