2 * Copyright (C) 2010 Martin Willi
4 * Copyright (C) secunet Security Networks AG
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>.
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
17 #include "pkcs11_manager.h"
19 #include <utils/debug.h>
20 #include <collections/linked_list.h>
21 #include <threading/thread.h>
23 #include "pkcs11_library.h"
25 #include <processing/jobs/callback_job.h>
27 typedef struct private_pkcs11_manager_t private_pkcs11_manager_t
;
30 * Private data of an pkcs11_manager_t object.
32 struct private_pkcs11_manager_t
{
35 * Public pkcs11_manager_t interface.
37 pkcs11_manager_t
public;
40 * List of loaded libraries, as lib_entry_t
45 * Slot event callback function
47 pkcs11_manager_token_event_t cb
;
50 * Slot event user data
56 * Entry for a loaded library
59 /* back reference to this */
60 private_pkcs11_manager_t
*this;
61 /* associated library path */
64 pkcs11_library_t
*lib
;
68 * Destroy a lib_entry_t
70 static void lib_entry_destroy(lib_entry_t
*entry
)
72 entry
->lib
->destroy(entry
->lib
);
77 * Print supported mechanisms of a token in a slot
79 static void print_mechs(lib_entry_t
*entry
, CK_SLOT_ID slot
)
81 enumerator_t
*enumerator
;
82 CK_MECHANISM_TYPE type
;
83 CK_MECHANISM_INFO info
;
85 enumerator
= entry
->lib
->create_mechanism_enumerator(entry
->lib
, slot
);
86 while (enumerator
->enumerate(enumerator
, &type
, &info
))
88 DBG2(DBG_CFG
, " %N %lu-%lu [ %s%s%s%s%s%s%s%s%s%s%s%s%s]",
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 " : "");
105 enumerator
->destroy(enumerator
);
111 static void handle_token(lib_entry_t
*entry
, CK_SLOT_ID slot
)
116 rv
= entry
->lib
->f
->C_GetTokenInfo(slot
, &info
);
119 DBG1(DBG_CFG
, "C_GetTokenInfo failed: %N", ck_rv_names
, rv
);
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
);
128 print_mechs(entry
, slot
);
132 * Handle slot changes
134 static void handle_slot(lib_entry_t
*entry
, CK_SLOT_ID slot
, bool hot
)
139 rv
= entry
->lib
->f
->C_GetSlotInfo(slot
, &info
);
142 DBG1(DBG_CFG
, "C_GetSlotInfo failed: %N", ck_rv_names
, rv
);
146 pkcs11_library_trim(info
.slotDescription
, sizeof(info
.slotDescription
));
147 if (info
.flags
& CKF_TOKEN_PRESENT
)
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
);
154 entry
->this->cb(entry
->this->data
, entry
->lib
, slot
, TRUE
);
159 DBG1(DBG_CFG
, "token removed from slot '%s':%lu (%s)",
160 entry
->lib
->get_name(entry
->lib
), slot
, info
.slotDescription
);
163 entry
->this->cb(entry
->this->data
, entry
->lib
, slot
, FALSE
);
168 CALLBACK(dispatch_slot_events
, job_requeue_t
,
174 rv
= entry
->lib
->f
->C_WaitForSlotEvent(0, &slot
, NULL
);
175 if (rv
== CKR_FUNCTION_NOT_SUPPORTED
|| rv
== CKR_NO_EVENT
)
177 DBG1(DBG_CFG
, "module '%s' does not support hot-plugging, canceled",
178 entry
->lib
->get_name(entry
->lib
));
179 return JOB_REQUEUE_NONE
;
181 if (rv
== CKR_CRYPTOKI_NOT_INITIALIZED
)
182 { /* C_Finalize called, abort */
183 return JOB_REQUEUE_NONE
;
187 DBG1(DBG_CFG
, "error in C_WaitForSlotEvent: %N", ck_rv_names
, rv
);
189 handle_slot(entry
, slot
, TRUE
);
191 return JOB_REQUEUE_DIRECT
;
194 CALLBACK(cancel_events
, bool,
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
);
205 * Get the slot list of a library
207 static CK_SLOT_ID_PTR
get_slot_list(pkcs11_library_t
*p11
, CK_ULONG
*out
)
209 CK_SLOT_ID_PTR slots
;
213 rv
= p11
->f
->C_GetSlotList(TRUE
, NULL
, &count
);
216 DBG1(DBG_CFG
, "C_GetSlotList() failed: %N", ck_rv_names
, rv
);
223 slots
= malloc(sizeof(CK_SLOT_ID
) * count
);
224 rv
= p11
->f
->C_GetSlotList(TRUE
, slots
, &count
);
227 DBG1(DBG_CFG
, "C_GetSlotList() failed: %N", ck_rv_names
, rv
);
236 * Query the slots for tokens
238 static void query_slots(lib_entry_t
*entry
)
241 CK_SLOT_ID_PTR slots
;
244 slots
= get_slot_list(entry
->lib
, &count
);
247 for (i
= 0; i
< count
; i
++)
249 handle_slot(entry
, slots
[i
], FALSE
);
259 /* implements enumerator */
261 /* inner enumerator over PKCS#11 libraries */
263 /* active library entry */
265 /* slot list with tokens */
266 CK_SLOT_ID_PTR slots
;
267 /* number of slots */
271 } token_enumerator_t
;
273 METHOD(enumerator_t
, enumerate_token
, bool,
274 token_enumerator_t
*this, va_list args
)
276 pkcs11_library_t
**out
;
279 VA_ARGS_VGET(args
, out
, slot
);
281 if (this->current
>= this->count
)
289 if (!this->inner
->enumerate(this->inner
, &this->entry
))
293 this->slots
= get_slot_list(this->entry
->lib
, &this->count
);
295 *out
= this->entry
->lib
;
296 *slot
= this->slots
[this->current
++];
300 METHOD(enumerator_t
, destroy_token
, void,
301 token_enumerator_t
*this)
303 this->inner
->destroy(this->inner
);
308 METHOD(pkcs11_manager_t
, create_token_enumerator
, enumerator_t
*,
309 private_pkcs11_manager_t
*this)
311 token_enumerator_t
*enumerator
;
315 .enumerate
= enumerator_enumerate_default
,
316 .venumerate
= _enumerate_token
,
317 .destroy
= _destroy_token
,
319 .inner
= this->libs
->create_enumerator(this->libs
),
321 return &enumerator
->public;
324 METHOD(pkcs11_manager_t
, destroy
, void,
325 private_pkcs11_manager_t
*this)
327 this->libs
->destroy_function(this->libs
, (void*)lib_entry_destroy
);
334 pkcs11_manager_t
*pkcs11_manager_create(pkcs11_manager_token_event_t cb
,
337 private_pkcs11_manager_t
*this;
338 enumerator_t
*enumerator
;
344 .create_token_enumerator
= _create_token_enumerator
,
347 .libs
= linked_list_create(),
352 enumerator
= lib
->settings
->create_section_enumerator(lib
->settings
,
353 "%s.plugins.pkcs11.modules", lib
->ns
);
354 while (enumerator
->enumerate(enumerator
, &module
))
360 entry
->path
= lib
->settings
->get_str(lib
->settings
,
361 "%s.plugins.pkcs11.modules.%s.path", NULL
, lib
->ns
, module
);
364 DBG1(DBG_CFG
, "PKCS11 module '%s' lacks library path", module
);
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
));
377 this->libs
->insert_last(this->libs
, entry
);
379 enumerator
->destroy(enumerator
);
381 enumerator
= this->libs
->create_enumerator(this->libs
);
382 while (enumerator
->enumerate(enumerator
, &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
));
389 enumerator
->destroy(enumerator
);
391 return &this->public;