Moved data structures to new collections subfolder
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_creds.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
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 */
15
16 #include "pkcs11_creds.h"
17 #include "pkcs11_manager.h"
18
19 #include <debug.h>
20 #include <collections/linked_list.h>
21
22 typedef struct private_pkcs11_creds_t private_pkcs11_creds_t;
23
24 /**
25 * Private data of an pkcs11_creds_t object.
26 */
27 struct private_pkcs11_creds_t {
28
29 /**
30 * Public pkcs11_creds_t interface.
31 */
32 pkcs11_creds_t public;
33
34 /**
35 * PKCS# library
36 */
37 pkcs11_library_t *lib;
38
39 /**
40 * Token slot
41 */
42 CK_SLOT_ID slot;
43
44 /**
45 * List of trusted certificates
46 */
47 linked_list_t *trusted;
48
49 /**
50 * List of untrusted certificates
51 */
52 linked_list_t *untrusted;
53 };
54
55 /**
56 * Find certificates, optionally trusted
57 */
58 static void find_certificates(private_pkcs11_creds_t *this,
59 CK_SESSION_HANDLE session)
60 {
61 CK_OBJECT_CLASS class = CKO_CERTIFICATE;
62 CK_CERTIFICATE_TYPE type = CKC_X_509;
63 CK_BBOOL trusted = TRUE;
64 CK_ATTRIBUTE tmpl[] = {
65 {CKA_CLASS, &class, sizeof(class)},
66 {CKA_CERTIFICATE_TYPE, &type, sizeof(type)},
67 };
68 CK_OBJECT_HANDLE object;
69 CK_ATTRIBUTE attr[] = {
70 {CKA_VALUE, NULL, 0},
71 {CKA_LABEL, NULL, 0},
72 {CKA_TRUSTED, &trusted, sizeof(trusted)}
73 };
74 enumerator_t *enumerator;
75 linked_list_t *raw;
76 certificate_t *cert;
77 struct {
78 chunk_t value;
79 chunk_t label;
80 bool trusted;
81 } *entry;
82 int count = countof(attr);
83
84 /* store result in a temporary list, avoid recursive operation */
85 raw = linked_list_create();
86 /* do not use trusted argument if not supported */
87 if (!(this->lib->get_features(this->lib) & PKCS11_TRUSTED_CERTS))
88 {
89 count--;
90 }
91 enumerator = this->lib->create_object_enumerator(this->lib,
92 session, tmpl, countof(tmpl), attr, count);
93 while (enumerator->enumerate(enumerator, &object))
94 {
95 entry = malloc(sizeof(*entry));
96 entry->value = chunk_clone(
97 chunk_create(attr[0].pValue, attr[0].ulValueLen));
98 entry->label = chunk_clone(
99 chunk_create(attr[1].pValue, attr[1].ulValueLen));
100 entry->trusted = trusted;
101 raw->insert_last(raw, entry);
102 }
103 enumerator->destroy(enumerator);
104
105 while (raw->remove_first(raw, (void**)&entry) == SUCCESS)
106 {
107 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
108 BUILD_BLOB_ASN1_DER, entry->value,
109 BUILD_END);
110 if (cert)
111 {
112 DBG1(DBG_CFG, " loaded %strusted cert '%.*s'",
113 entry->trusted ? "" : "un", (int)entry->label.len,
114 entry->label.ptr);
115 /* trusted certificates are also returned as untrusted */
116 this->untrusted->insert_last(this->untrusted, cert);
117 if (entry->trusted)
118 {
119 this->trusted->insert_last(this->trusted, cert->get_ref(cert));
120 }
121 }
122 else
123 {
124 DBG1(DBG_CFG, " loading cert '%.*s' failed",
125 (int)entry->label.len, entry->label.ptr);
126 }
127 free(entry->value.ptr);
128 free(entry->label.ptr);
129 free(entry);
130 }
131 raw->destroy(raw);
132 }
133
134 /**
135 * Load in the certificates from the token
136 */
137 static bool load_certificates(private_pkcs11_creds_t *this)
138 {
139 CK_SESSION_HANDLE session;
140 CK_RV rv;
141
142 rv = this->lib->f->C_OpenSession(this->slot, CKF_SERIAL_SESSION,
143 NULL, NULL, &session);
144 if (rv != CKR_OK)
145 {
146 DBG1(DBG_CFG, "opening session failed: %N", ck_rv_names, rv);
147 return FALSE;
148 }
149
150 find_certificates(this, session);
151
152 this->lib->f->C_CloseSession(session);
153 return TRUE;
154 }
155
156 /**
157 * filter function for certs enumerator
158 */
159 static bool certs_filter(identification_t *id,
160 certificate_t **in, certificate_t **out)
161 {
162 public_key_t *public;
163 certificate_t *cert = *in;
164
165 if (id == NULL || cert->has_subject(cert, id))
166 {
167 *out = *in;
168 return TRUE;
169 }
170 public = cert->get_public_key(cert);
171 if (public)
172 {
173 if (public->has_fingerprint(public, id->get_encoding(id)))
174 {
175 public->destroy(public);
176 *out = *in;
177 return TRUE;
178 }
179 public->destroy(public);
180 }
181 return FALSE;
182 }
183
184 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
185 private_pkcs11_creds_t *this, certificate_type_t cert, key_type_t key,
186 identification_t *id, bool trusted)
187 {
188 enumerator_t *inner;
189
190 if (cert != CERT_X509 && cert != CERT_ANY)
191 {
192 return NULL;
193 }
194 if (trusted)
195 {
196 inner = this->trusted->create_enumerator(this->trusted);
197 }
198 else
199 {
200 inner = this->untrusted->create_enumerator(this->untrusted);
201 }
202 return enumerator_create_filter(inner, (void*)certs_filter, id, NULL);
203 }
204
205 METHOD(pkcs11_creds_t, get_library, pkcs11_library_t*,
206 private_pkcs11_creds_t *this)
207 {
208 return this->lib;
209 }
210
211 METHOD(pkcs11_creds_t, get_slot, CK_SLOT_ID,
212 private_pkcs11_creds_t *this)
213 {
214 return this->slot;
215 }
216
217 METHOD(pkcs11_creds_t, destroy, void,
218 private_pkcs11_creds_t *this)
219 {
220 this->trusted->destroy_offset(this->trusted,
221 offsetof(certificate_t, destroy));
222 this->untrusted->destroy_offset(this->untrusted,
223 offsetof(certificate_t, destroy));
224 free(this);
225 }
226
227 /**
228 * See header
229 */
230 pkcs11_creds_t *pkcs11_creds_create(pkcs11_library_t *p11, CK_SLOT_ID slot)
231 {
232 private_pkcs11_creds_t *this;
233
234 INIT(this,
235 .public = {
236 .set = {
237 .create_shared_enumerator = (void*)enumerator_create_empty,
238 .create_private_enumerator = (void*)enumerator_create_empty,
239 .create_cert_enumerator = _create_cert_enumerator,
240 .create_cdp_enumerator = (void*)enumerator_create_empty,
241 .cache_cert = (void*)nop,
242 },
243 .get_library = _get_library,
244 .get_slot = _get_slot,
245 .destroy = _destroy,
246 },
247 .lib = p11,
248 .slot = slot,
249 .trusted = linked_list_create(),
250 .untrusted = linked_list_create(),
251 );
252
253 if (!load_certificates(this))
254 {
255 destroy(this);
256 return NULL;
257 }
258
259 return &this->public;
260 }
261
262 /**
263 * See header.
264 */
265 certificate_t *pkcs11_creds_load(certificate_type_t type, va_list args)
266 {
267 chunk_t keyid = chunk_empty, data = chunk_empty;
268 enumerator_t *enumerator, *certs;
269 pkcs11_manager_t *manager;
270 pkcs11_library_t *p11;
271 certificate_t *cert = NULL;
272 CK_SLOT_ID current, slot = -1;
273 char *module = NULL;
274
275 while (TRUE)
276 {
277 switch (va_arg(args, builder_part_t))
278 {
279 case BUILD_PKCS11_KEYID:
280 keyid = va_arg(args, chunk_t);
281 continue;
282 case BUILD_PKCS11_SLOT:
283 slot = va_arg(args, int);
284 continue;
285 case BUILD_PKCS11_MODULE:
286 module = va_arg(args, char*);
287 continue;
288 case BUILD_END:
289 break;
290 default:
291 return NULL;
292 }
293 break;
294 }
295 if (!keyid.len)
296 {
297 return NULL;
298 }
299
300 manager = lib->get(lib, "pkcs11-manager");
301 if (!manager)
302 {
303 return NULL;
304 }
305 enumerator = manager->create_token_enumerator(manager);
306 while (enumerator->enumerate(enumerator, &p11, &current))
307 {
308 CK_OBJECT_CLASS class = CKO_CERTIFICATE;
309 CK_CERTIFICATE_TYPE type = CKC_X_509;
310 CK_ATTRIBUTE tmpl[] = {
311 {CKA_CLASS, &class, sizeof(class)},
312 {CKA_CERTIFICATE_TYPE, &type, sizeof(type)},
313 {CKA_ID, keyid.ptr, keyid.len},
314 };
315 CK_ATTRIBUTE attr[] = {
316 {CKA_VALUE, NULL, 0},
317 };
318 CK_OBJECT_HANDLE object;
319 CK_SESSION_HANDLE session;
320 CK_RV rv;
321
322 if (slot != -1 && slot != current)
323 {
324 continue;
325 }
326 if (module && !streq(module, p11->get_name(p11)))
327 {
328 continue;
329 }
330
331 rv = p11->f->C_OpenSession(current, CKF_SERIAL_SESSION, NULL, NULL,
332 &session);
333 if (rv != CKR_OK)
334 {
335 DBG1(DBG_CFG, "opening PKCS#11 session failed: %N", ck_rv_names, rv);
336 continue;
337 }
338 certs = p11->create_object_enumerator(p11, session,
339 tmpl, countof(tmpl), attr, countof(attr));
340 if (certs->enumerate(certs, &object))
341 {
342 data = chunk_clone(chunk_create(attr[0].pValue, attr[0].ulValueLen));
343 }
344 certs->destroy(certs);
345 p11->f->C_CloseSession(session);
346
347 if (data.ptr)
348 {
349 break;
350 }
351 }
352 enumerator->destroy(enumerator);
353
354 if (data.ptr)
355 {
356 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
357 BUILD_BLOB_ASN1_DER, data, BUILD_END);
358 free(data.ptr);
359 if (!cert)
360 {
361 DBG1(DBG_CFG, "parsing PKCS#11 certificate %#B failed", &keyid);
362 }
363 }
364 else
365 {
366 DBG1(DBG_CFG, "PKCS#11 certificate %#B not found", &keyid);
367 }
368 return cert;
369 }