]> git.ipfire.org Git - thirdparty/strongswan.git/blob - src/libstrongswan/plugins/pkcs11/pkcs11_manager.c
Update copyright headers after acquisition by secunet
[thirdparty/strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_manager.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 *
4 * Copyright (C) secunet Security Networks AG
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "pkcs11_manager.h"
18
19 #include <utils/debug.h>
20 #include <collections/linked_list.h>
21 #include <threading/thread.h>
22
23 #include "pkcs11_library.h"
24
25 #include <processing/jobs/callback_job.h>
26
27 typedef struct private_pkcs11_manager_t private_pkcs11_manager_t;
28
29 /**
30 * Private data of an pkcs11_manager_t object.
31 */
32 struct private_pkcs11_manager_t {
33
34 /**
35 * Public pkcs11_manager_t interface.
36 */
37 pkcs11_manager_t public;
38
39 /**
40 * List of loaded libraries, as lib_entry_t
41 */
42 linked_list_t *libs;
43
44 /**
45 * Slot event callback function
46 */
47 pkcs11_manager_token_event_t cb;
48
49 /**
50 * Slot event user data
51 */
52 void *data;
53 };
54
55 /**
56 * Entry for a loaded library
57 */
58 typedef struct {
59 /* back reference to this */
60 private_pkcs11_manager_t *this;
61 /* associated library path */
62 char *path;
63 /* loaded library */
64 pkcs11_library_t *lib;
65 } lib_entry_t;
66
67 /**
68 * Destroy a lib_entry_t
69 */
70 static void lib_entry_destroy(lib_entry_t *entry)
71 {
72 entry->lib->destroy(entry->lib);
73 free(entry);
74 }
75
76 /**
77 * Print supported mechanisms of a token in a slot
78 */
79 static void print_mechs(lib_entry_t *entry, CK_SLOT_ID slot)
80 {
81 enumerator_t *enumerator;
82 CK_MECHANISM_TYPE type;
83 CK_MECHANISM_INFO info;
84
85 enumerator = entry->lib->create_mechanism_enumerator(entry->lib, slot);
86 while (enumerator->enumerate(enumerator, &type, &info))
87 {
88 DBG2(DBG_CFG, " %N %lu-%lu [ %s%s%s%s%s%s%s%s%s%s%s%s%s]",
89 ck_mech_names, type,
90 info.ulMinKeySize, info.ulMaxKeySize,
91 info.flags & CKF_HW ? "HW " : "",
92 info.flags & CKF_ENCRYPT ? "ENCR " : "",
93 info.flags & CKF_DECRYPT ? "DECR " : "",
94 info.flags & CKF_DIGEST ? "DGST " : "",
95 info.flags & CKF_SIGN ? "SIGN " : "",
96 info.flags & CKF_SIGN_RECOVER ? "SIGN_RCVR " : "",
97 info.flags & CKF_VERIFY ? "VRFY " : "",
98 info.flags & CKF_VERIFY_RECOVER ? "VRFY_RCVR " : "",
99 info.flags & CKF_GENERATE ? "GEN " : "",
100 info.flags & CKF_GENERATE_KEY_PAIR ? "GEN_KEY_PAIR " : "",
101 info.flags & CKF_WRAP ? "WRAP " : "",
102 info.flags & CKF_UNWRAP ? "UNWRAP " : "",
103 info.flags & CKF_DERIVE ? "DERIVE " : "");
104 }
105 enumerator->destroy(enumerator);
106 }
107
108 /**
109 * Handle a token
110 */
111 static void handle_token(lib_entry_t *entry, CK_SLOT_ID slot)
112 {
113 CK_TOKEN_INFO info;
114 CK_RV rv;
115
116 rv = entry->lib->f->C_GetTokenInfo(slot, &info);
117 if (rv != CKR_OK)
118 {
119 DBG1(DBG_CFG, "C_GetTokenInfo failed: %N", ck_rv_names, rv);
120 return;
121 }
122 pkcs11_library_trim(info.label, sizeof(info.label));
123 pkcs11_library_trim(info.manufacturerID, sizeof(info.manufacturerID));
124 pkcs11_library_trim(info.model, sizeof(info.model));
125 DBG1(DBG_CFG, " %s (%s: %s)",
126 info.label, info.manufacturerID, info.model);
127
128 print_mechs(entry, slot);
129 }
130
131 /**
132 * Handle slot changes
133 */
134 static void handle_slot(lib_entry_t *entry, CK_SLOT_ID slot, bool hot)
135 {
136 CK_SLOT_INFO info;
137 CK_RV rv;
138
139 rv = entry->lib->f->C_GetSlotInfo(slot, &info);
140 if (rv != CKR_OK)
141 {
142 DBG1(DBG_CFG, "C_GetSlotInfo failed: %N", ck_rv_names, rv);
143 return;
144 }
145
146 pkcs11_library_trim(info.slotDescription, sizeof(info.slotDescription));
147 if (info.flags & CKF_TOKEN_PRESENT)
148 {
149 DBG1(DBG_CFG, " found token in slot '%s':%lu (%s)",
150 entry->lib->get_name(entry->lib), slot, info.slotDescription);
151 handle_token(entry, slot);
152 if (hot)
153 {
154 entry->this->cb(entry->this->data, entry->lib, slot, TRUE);
155 }
156 }
157 else
158 {
159 DBG1(DBG_CFG, "token removed from slot '%s':%lu (%s)",
160 entry->lib->get_name(entry->lib), slot, info.slotDescription);
161 if (hot)
162 {
163 entry->this->cb(entry->this->data, entry->lib, slot, FALSE);
164 }
165 }
166 }
167
168 CALLBACK(dispatch_slot_events, job_requeue_t,
169 lib_entry_t *entry)
170 {
171 CK_SLOT_ID slot;
172 CK_RV rv;
173
174 rv = entry->lib->f->C_WaitForSlotEvent(0, &slot, NULL);
175 if (rv == CKR_FUNCTION_NOT_SUPPORTED || rv == CKR_NO_EVENT)
176 {
177 DBG1(DBG_CFG, "module '%s' does not support hot-plugging, canceled",
178 entry->lib->get_name(entry->lib));
179 return JOB_REQUEUE_NONE;
180 }
181 if (rv == CKR_CRYPTOKI_NOT_INITIALIZED)
182 { /* C_Finalize called, abort */
183 return JOB_REQUEUE_NONE;
184 }
185 if (rv != CKR_OK)
186 {
187 DBG1(DBG_CFG, "error in C_WaitForSlotEvent: %N", ck_rv_names, rv);
188 }
189 handle_slot(entry, slot, TRUE);
190
191 return JOB_REQUEUE_DIRECT;
192 }
193
194 CALLBACK(cancel_events, bool,
195 lib_entry_t *entry)
196 {
197 /* it's possible other threads still use the API after this call, but we
198 * have no other way to return from C_WaitForSlotEvent() if we can't cancel
199 * the thread because libraries hold locks they don't release */
200 entry->lib->f->C_Finalize(NULL);
201 return TRUE;
202 }
203
204 /**
205 * Get the slot list of a library
206 */
207 static CK_SLOT_ID_PTR get_slot_list(pkcs11_library_t *p11, CK_ULONG *out)
208 {
209 CK_SLOT_ID_PTR slots;
210 CK_ULONG count;
211 CK_RV rv;
212
213 rv = p11->f->C_GetSlotList(TRUE, NULL, &count);
214 if (rv != CKR_OK)
215 {
216 DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
217 return NULL;
218 }
219 if (count == 0)
220 {
221 return NULL;
222 }
223 slots = malloc(sizeof(CK_SLOT_ID) * count);
224 rv = p11->f->C_GetSlotList(TRUE, slots, &count);
225 if (rv != CKR_OK)
226 {
227 DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
228 free(slots);
229 return NULL;
230 }
231 *out = count;
232 return slots;
233 }
234
235 /**
236 * Query the slots for tokens
237 */
238 static void query_slots(lib_entry_t *entry)
239 {
240 CK_ULONG count;
241 CK_SLOT_ID_PTR slots;
242 int i;
243
244 slots = get_slot_list(entry->lib, &count);
245 if (slots)
246 {
247 for (i = 0; i < count; i++)
248 {
249 handle_slot(entry, slots[i], FALSE);
250 }
251 free(slots);
252 }
253 }
254
255 /**
256 * Token enumerator
257 */
258 typedef struct {
259 /* implements enumerator */
260 enumerator_t public;
261 /* inner enumerator over PKCS#11 libraries */
262 enumerator_t *inner;
263 /* active library entry */
264 lib_entry_t *entry;
265 /* slot list with tokens */
266 CK_SLOT_ID_PTR slots;
267 /* number of slots */
268 CK_ULONG count;
269 /* current slot */
270 int current;
271 } token_enumerator_t;
272
273 METHOD(enumerator_t, enumerate_token, bool,
274 token_enumerator_t *this, va_list args)
275 {
276 pkcs11_library_t **out;
277 CK_SLOT_ID *slot;
278
279 VA_ARGS_VGET(args, out, slot);
280
281 if (this->current >= this->count)
282 {
283 free(this->slots);
284 this->slots = NULL;
285 this->current = 0;
286 }
287 while (!this->slots)
288 {
289 if (!this->inner->enumerate(this->inner, &this->entry))
290 {
291 return FALSE;
292 }
293 this->slots = get_slot_list(this->entry->lib, &this->count);
294 }
295 *out = this->entry->lib;
296 *slot = this->slots[this->current++];
297 return TRUE;
298 }
299
300 METHOD(enumerator_t, destroy_token, void,
301 token_enumerator_t *this)
302 {
303 this->inner->destroy(this->inner);
304 free(this->slots);
305 free(this);
306 }
307
308 METHOD(pkcs11_manager_t, create_token_enumerator, enumerator_t*,
309 private_pkcs11_manager_t *this)
310 {
311 token_enumerator_t *enumerator;
312
313 INIT(enumerator,
314 .public = {
315 .enumerate = enumerator_enumerate_default,
316 .venumerate = _enumerate_token,
317 .destroy = _destroy_token,
318 },
319 .inner = this->libs->create_enumerator(this->libs),
320 );
321 return &enumerator->public;
322 }
323
324 METHOD(pkcs11_manager_t, destroy, void,
325 private_pkcs11_manager_t *this)
326 {
327 this->libs->destroy_function(this->libs, (void*)lib_entry_destroy);
328 free(this);
329 }
330
331 /**
332 * See header
333 */
334 pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
335 void *data)
336 {
337 private_pkcs11_manager_t *this;
338 enumerator_t *enumerator;
339 lib_entry_t *entry;
340 char *module;
341
342 INIT(this,
343 .public = {
344 .create_token_enumerator = _create_token_enumerator,
345 .destroy = _destroy,
346 },
347 .libs = linked_list_create(),
348 .cb = cb,
349 .data = data,
350 );
351
352 enumerator = lib->settings->create_section_enumerator(lib->settings,
353 "%s.plugins.pkcs11.modules", lib->ns);
354 while (enumerator->enumerate(enumerator, &module))
355 {
356 INIT(entry,
357 .this = this,
358 );
359
360 entry->path = lib->settings->get_str(lib->settings,
361 "%s.plugins.pkcs11.modules.%s.path", NULL, lib->ns, module);
362 if (!entry->path)
363 {
364 DBG1(DBG_CFG, "PKCS11 module '%s' lacks library path", module);
365 free(entry);
366 continue;
367 }
368 entry->lib = pkcs11_library_create(module, entry->path,
369 lib->settings->get_bool(lib->settings,
370 "%s.plugins.pkcs11.modules.%s.os_locking",
371 FALSE, lib->ns, module));
372 if (!entry->lib)
373 {
374 free(entry);
375 continue;
376 }
377 this->libs->insert_last(this->libs, entry);
378 }
379 enumerator->destroy(enumerator);
380
381 enumerator = this->libs->create_enumerator(this->libs);
382 while (enumerator->enumerate(enumerator, &entry))
383 {
384 query_slots(entry);
385 lib->processor->queue_job(lib->processor,
386 (job_t*)callback_job_create_with_prio(dispatch_slot_events,
387 entry, NULL, cancel_events, JOB_PRIO_CRITICAL));
388 }
389 enumerator->destroy(enumerator);
390
391 return &this->public;
392 }