Use CRITICAL job priority class for long running dispatcher jobs
[strongswan.git] / src / libstrongswan / plugins / pkcs11 / pkcs11_manager.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_manager.h"
17
18 #include <debug.h>
19 #include <utils/linked_list.h>
20 #include <threading/thread.h>
21
22 #include "pkcs11_library.h"
23
24 #include <processing/jobs/callback_job.h>
25
26 typedef struct private_pkcs11_manager_t private_pkcs11_manager_t;
27
28 /**
29 * Private data of an pkcs11_manager_t object.
30 */
31 struct private_pkcs11_manager_t {
32
33 /**
34 * Public pkcs11_manager_t interface.
35 */
36 pkcs11_manager_t public;
37
38 /**
39 * List of loaded libraries, as lib_entry_t
40 */
41 linked_list_t *libs;
42
43 /**
44 * Slot event callback function
45 */
46 pkcs11_manager_token_event_t cb;
47
48 /**
49 * Slot event user data
50 */
51 void *data;
52 };
53
54 /**
55 * Entry for a loaded library
56 */
57 typedef struct {
58 /* back reference to this */
59 private_pkcs11_manager_t *this;
60 /* associated library path */
61 char *path;
62 /* loaded library */
63 pkcs11_library_t *lib;
64 /* event dispatcher job */
65 callback_job_t *job;
66 } lib_entry_t;
67
68 /**
69 * Destroy a lib_entry_t
70 */
71 static void lib_entry_destroy(lib_entry_t *entry)
72 {
73 if (entry->job)
74 {
75 entry->job->cancel(entry->job);
76 }
77 entry->lib->destroy(entry->lib);
78 free(entry);
79 }
80
81 /**
82 * Print supported mechanisms of a token in a slot
83 */
84 static void print_mechs(lib_entry_t *entry, CK_SLOT_ID slot)
85 {
86 enumerator_t *enumerator;
87 CK_MECHANISM_TYPE type;
88 CK_MECHANISM_INFO info;
89
90 enumerator = entry->lib->create_mechanism_enumerator(entry->lib, slot);
91 while (enumerator->enumerate(enumerator, &type, &info))
92 {
93 DBG2(DBG_CFG, " %N %lu-%lu [ %s%s%s%s%s%s%s%s%s%s%s%s%s]",
94 ck_mech_names, type,
95 info.ulMinKeySize, info.ulMaxKeySize,
96 info.flags & CKF_HW ? "HW " : "",
97 info.flags & CKF_ENCRYPT ? "ENCR " : "",
98 info.flags & CKF_DECRYPT ? "DECR " : "",
99 info.flags & CKF_DIGEST ? "DGST " : "",
100 info.flags & CKF_SIGN ? "SIGN " : "",
101 info.flags & CKF_SIGN_RECOVER ? "SIGN_RCVR " : "",
102 info.flags & CKF_VERIFY ? "VRFY " : "",
103 info.flags & CKF_VERIFY_RECOVER ? "VRFY_RCVR " : "",
104 info.flags & CKF_GENERATE ? "GEN " : "",
105 info.flags & CKF_GENERATE_KEY_PAIR ? "GEN_KEY_PAIR " : "",
106 info.flags & CKF_WRAP ? "WRAP " : "",
107 info.flags & CKF_UNWRAP ? "UNWRAP " : "",
108 info.flags & CKF_DERIVE ? "DERIVE " : "");
109 }
110 enumerator->destroy(enumerator);
111 }
112
113 /**
114 * Handle a token
115 */
116 static void handle_token(lib_entry_t *entry, CK_SLOT_ID slot)
117 {
118 CK_TOKEN_INFO info;
119 CK_RV rv;
120
121 rv = entry->lib->f->C_GetTokenInfo(slot, &info);
122 if (rv != CKR_OK)
123 {
124 DBG1(DBG_CFG, "C_GetTokenInfo failed: %N", ck_rv_names, rv);
125 return;
126 }
127 pkcs11_library_trim(info.label, sizeof(info.label));
128 pkcs11_library_trim(info.manufacturerID, sizeof(info.manufacturerID));
129 pkcs11_library_trim(info.model, sizeof(info.model));
130 DBG1(DBG_CFG, " %s (%s: %s)",
131 info.label, info.manufacturerID, info.model);
132
133 print_mechs(entry, slot);
134 }
135
136 /**
137 * Handle slot changes
138 */
139 static void handle_slot(lib_entry_t *entry, CK_SLOT_ID slot, bool hot)
140 {
141 CK_SLOT_INFO info;
142 CK_RV rv;
143
144 rv = entry->lib->f->C_GetSlotInfo(slot, &info);
145 if (rv != CKR_OK)
146 {
147 DBG1(DBG_CFG, "C_GetSlotInfo failed: %N", ck_rv_names, rv);
148 return;
149 }
150
151 pkcs11_library_trim(info.slotDescription, sizeof(info.slotDescription));
152 if (info.flags & CKF_TOKEN_PRESENT)
153 {
154 DBG1(DBG_CFG, " found token in slot '%s':%lu (%s)",
155 entry->lib->get_name(entry->lib), slot, info.slotDescription);
156 handle_token(entry, slot);
157 if (hot)
158 {
159 entry->this->cb(entry->this->data, entry->lib, slot, TRUE);
160 }
161 }
162 else
163 {
164 DBG1(DBG_CFG, "token removed from slot '%s':%lu (%s)",
165 entry->lib->get_name(entry->lib), slot, info.slotDescription);
166 if (hot)
167 {
168 entry->this->cb(entry->this->data, entry->lib, slot, FALSE);
169 }
170 }
171 }
172
173 /**
174 * Dispatch slot events
175 */
176 static job_requeue_t dispatch_slot_events(lib_entry_t *entry)
177 {
178 CK_SLOT_ID slot;
179 CK_RV rv;
180 bool old;
181
182 old = thread_cancelability(TRUE);
183 rv = entry->lib->f->C_WaitForSlotEvent(0, &slot, NULL);
184 thread_cancelability(old);
185 if (rv == CKR_FUNCTION_NOT_SUPPORTED || rv == CKR_NO_EVENT)
186 {
187 DBG1(DBG_CFG, "module '%s' does not support hot-plugging, cancelled",
188 entry->lib->get_name(entry->lib));
189 return JOB_REQUEUE_NONE;
190 }
191 if (rv == CKR_CRYPTOKI_NOT_INITIALIZED)
192 { /* C_Finalize called, abort */
193 return JOB_REQUEUE_NONE;
194 }
195 if (rv != CKR_OK)
196 {
197 DBG1(DBG_CFG, "error in C_WaitForSlotEvent: %N", ck_rv_names, rv);
198 }
199 handle_slot(entry, slot, TRUE);
200
201 return JOB_REQUEUE_DIRECT;
202 }
203
204 /**
205 * End dispatching, unset job
206 */
207 static void end_dispatch(lib_entry_t *entry)
208 {
209 entry->job = NULL;
210 }
211
212 /**
213 * Get the slot list of a library
214 */
215 static CK_SLOT_ID_PTR get_slot_list(pkcs11_library_t *p11, CK_ULONG *out)
216 {
217 CK_SLOT_ID_PTR slots;
218 CK_ULONG count;
219 CK_RV rv;
220
221 rv = p11->f->C_GetSlotList(TRUE, NULL, &count);
222 if (rv != CKR_OK)
223 {
224 DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
225 return NULL;
226 }
227 if (count == 0)
228 {
229 return NULL;
230 }
231 slots = malloc(sizeof(CK_SLOT_ID) * count);
232 rv = p11->f->C_GetSlotList(TRUE, slots, &count);
233 if (rv != CKR_OK)
234 {
235 DBG1(DBG_CFG, "C_GetSlotList() failed: %N", ck_rv_names, rv);
236 free(slots);
237 return NULL;
238 }
239 *out = count;
240 return slots;
241 }
242
243 /**
244 * Query the slots for tokens
245 */
246 static void query_slots(lib_entry_t *entry)
247 {
248 CK_ULONG count;
249 CK_SLOT_ID_PTR slots;
250 int i;
251
252 slots = get_slot_list(entry->lib, &count);
253 if (slots)
254 {
255 for (i = 0; i < count; i++)
256 {
257 handle_slot(entry, slots[i], FALSE);
258 }
259 free(slots);
260 }
261 }
262
263 /**
264 * Token enumerator
265 */
266 typedef struct {
267 /* implements enumerator */
268 enumerator_t public;
269 /* inner enumerator over PKCS#11 libraries */
270 enumerator_t *inner;
271 /* active library entry */
272 lib_entry_t *entry;
273 /* slot list with tokens */
274 CK_SLOT_ID_PTR slots;
275 /* number of slots */
276 CK_ULONG count;
277 /* current slot */
278 int current;
279 } token_enumerator_t;
280
281 METHOD(enumerator_t, enumerate_token, bool,
282 token_enumerator_t *this, pkcs11_library_t **out, CK_SLOT_ID *slot)
283 {
284 if (this->current >= this->count)
285 {
286 free(this->slots);
287 this->slots = NULL;
288 this->current = 0;
289 }
290 while (!this->slots)
291 {
292 if (!this->inner->enumerate(this->inner, &this->entry))
293 {
294 return FALSE;
295 }
296 this->slots = get_slot_list(this->entry->lib, &this->count);
297 }
298 *out = this->entry->lib;
299 *slot = this->slots[this->current++];
300 return TRUE;
301 }
302
303 METHOD(enumerator_t, destroy_token, void,
304 token_enumerator_t *this)
305 {
306 this->inner->destroy(this->inner);
307 free(this->slots);
308 free(this);
309 }
310
311 METHOD(pkcs11_manager_t, create_token_enumerator, enumerator_t*,
312 private_pkcs11_manager_t *this)
313 {
314 token_enumerator_t *enumerator;
315
316 INIT(enumerator,
317 .public = {
318 .enumerate = (void*)_enumerate_token,
319 .destroy = _destroy_token,
320 },
321 .inner = this->libs->create_enumerator(this->libs),
322 );
323 return &enumerator->public;
324 }
325
326 /**
327 * Singleton instance
328 */
329 static private_pkcs11_manager_t *singleton = NULL;
330
331 METHOD(pkcs11_manager_t, destroy, void,
332 private_pkcs11_manager_t *this)
333 {
334 this->libs->destroy_function(this->libs, (void*)lib_entry_destroy);
335 free(this);
336 singleton = NULL;
337 }
338
339 /**
340 * See header
341 */
342 pkcs11_manager_t *pkcs11_manager_create(pkcs11_manager_token_event_t cb,
343 void *data)
344 {
345 private_pkcs11_manager_t *this;
346 enumerator_t *enumerator;
347 lib_entry_t *entry;
348 char *module;
349
350 INIT(this,
351 .public = {
352 .create_token_enumerator = _create_token_enumerator,
353 .destroy = _destroy,
354 },
355 .libs = linked_list_create(),
356 .cb = cb,
357 .data = data,
358 );
359
360 enumerator = lib->settings->create_section_enumerator(lib->settings,
361 "libstrongswan.plugins.pkcs11.modules");
362 while (enumerator->enumerate(enumerator, &module))
363 {
364 INIT(entry,
365 .this = this,
366 );
367
368 entry->path = lib->settings->get_str(lib->settings,
369 "libstrongswan.plugins.pkcs11.modules.%s.path", NULL, module);
370 if (!entry->path)
371 {
372 DBG1(DBG_CFG, "PKCS11 module '%s' lacks library path", module);
373 free(entry);
374 continue;
375 }
376 entry->lib = pkcs11_library_create(module, entry->path,
377 lib->settings->get_bool(lib->settings,
378 "libstrongswan.plugins.pkcs11.modules.%s.os_locking",
379 FALSE, module));
380 if (!entry->lib)
381 {
382 free(entry);
383 continue;
384 }
385 this->libs->insert_last(this->libs, entry);
386 }
387 enumerator->destroy(enumerator);
388
389 singleton = this;
390
391 enumerator = this->libs->create_enumerator(this->libs);
392 while (enumerator->enumerate(enumerator, &entry))
393 {
394 query_slots(entry);
395 entry->job = callback_job_create_with_prio((void*)dispatch_slot_events,
396 entry, (void*)end_dispatch, NULL, JOB_PRIO_CRITICAL);
397 lib->processor->queue_job(lib->processor, (job_t*)entry->job);
398 }
399 enumerator->destroy(enumerator);
400
401 return &this->public;
402 }
403
404 /**
405 * See header
406 */
407 pkcs11_manager_t *pkcs11_manager_get()
408 {
409 return (pkcs11_manager_t*)singleton;
410 }