1 /*
2 * slot.c: reader, smart card and slot related management functions
3 *
4 * Copyright (C) 2002 Timo Teräs <timo.teras@iki.fi>
5 * Copyright (C) 2009 Martin Paljak <martin@martinpaljak.net>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22 #include "config.h"
23 #include "libopensc/opensc.h"
24
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include "sc-pkcs11.h"
29
30 /* Print virtual_slots list. Called by DEBUG_VSS(S, C) */
_debug_virtual_slots(sc_pkcs11_slot_t * p)31 void _debug_virtual_slots(sc_pkcs11_slot_t *p)
32 {
33 int i, vs_size;
34 sc_pkcs11_slot_t * slot;
35
36 vs_size = list_size(&virtual_slots);
37 _sc_debug(context, 10,
38 "VSS size:%d", vs_size);
39 _sc_debug(context, 10,
40 "VSS [i] id flags LU events nsessions slot_info.flags reader p11card description");
41 for (i = 0; i < vs_size; i++) {
42 slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
43 if (slot) {
44 _sc_debug(context, 10,
45 "VSS %s[%d] 0x%2.2lx 0x%4.4x %d %d %d %4.4lx %p %p %.64s",
46 ((slot == p) ? "*" : " "),
47 i, slot->id, slot->flags, slot->login_user, slot->events, slot->nsessions,
48 slot->slot_info.flags,
49 slot->reader, slot->p11card,
50 slot->slot_info.slotDescription);
51 }
52 }
53 _sc_debug(context, 10, "VSS END");
54 }
55
56 static struct sc_pkcs11_framework_ops *frameworks[] = {
57 &framework_pkcs15,
58 #ifdef USE_PKCS15_INIT
59 /* This should be the last framework, because it
60 * will assume the card is blank and try to initialize it */
61 &framework_pkcs15init,
62 #endif
63 NULL
64 };
65
reader_reclaim_slot(sc_reader_t * reader)66 static struct sc_pkcs11_slot * reader_reclaim_slot(sc_reader_t *reader)
67 {
68 unsigned int i;
69 CK_UTF8CHAR slotDescription[64];
70 CK_UTF8CHAR manufacturerID[32];
71
72 strcpy_bp(slotDescription, reader->name, 64);
73 strcpy_bp(manufacturerID, reader->vendor, 32);
74
75 /* Locate a slot related to the reader */
76 for (i = 0; i<list_size(&virtual_slots); i++) {
77 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
78 if (slot->reader == NULL && reader != NULL
79 && 0 == memcmp(slot->slot_info.slotDescription, slotDescription, 64)
80 && 0 == memcmp(slot->slot_info.manufacturerID, manufacturerID, 32)
81 && slot->slot_info.hardwareVersion.major == reader->version_major
82 && slot->slot_info.hardwareVersion.minor == reader->version_minor) {
83 return slot;
84 }
85 }
86 return NULL;
87 }
88
init_slot_info(CK_SLOT_INFO_PTR pInfo,sc_reader_t * reader)89 void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader)
90 {
91 if (reader) {
92 strcpy_bp(pInfo->slotDescription, reader->name, 64);
93 strcpy_bp(pInfo->manufacturerID, reader->vendor, 32);
94 pInfo->hardwareVersion.major = reader->version_major;
95 pInfo->hardwareVersion.minor = reader->version_minor;
96 } else {
97 strcpy_bp(pInfo->slotDescription, "Virtual hotplug slot", 64);
98 strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, 32);
99 pInfo->hardwareVersion.major = OPENSC_VERSION_MAJOR;
100 pInfo->hardwareVersion.minor = OPENSC_VERSION_MINOR;
101 }
102 pInfo->flags = CKF_REMOVABLE_DEVICE | CKF_HW_SLOT;
103 pInfo->firmwareVersion.major = 0;
104 pInfo->firmwareVersion.minor = 0;
105 }
106
107 /* simclist helpers to locate interesting objects by ID */
object_list_seeker(const void * el,const void * key)108 static int object_list_seeker(const void *el, const void *key)
109 {
110 const struct sc_pkcs11_object *object = (struct sc_pkcs11_object *)el;
111
112 if ((el == NULL) || (key == NULL))
113 return 0;
114 if (object->handle == *(CK_OBJECT_HANDLE*)key)
115 return 1;
116 return 0;
117 }
118
create_slot(sc_reader_t * reader)119 CK_RV create_slot(sc_reader_t *reader)
120 {
121 /* find unused slots previously allocated for the same reader */
122 struct sc_pkcs11_slot *slot = reader_reclaim_slot(reader);
123
124 /* create a new slot if no empty slot is available */
125 if (!slot) {
126 sc_log(context, "Creating new slot");
127 if (list_size(&virtual_slots) >= sc_pkcs11_conf.max_virtual_slots)
128 return CKR_FUNCTION_FAILED;
129
130 slot = (struct sc_pkcs11_slot *)calloc(1, sizeof(struct sc_pkcs11_slot));
131 if (!slot)
132 return CKR_HOST_MEMORY;
133
134 list_append(&virtual_slots, slot);
135 if (0 != list_init(&slot->objects)) {
136 return CKR_HOST_MEMORY;
137 }
138 list_attributes_seeker(&slot->objects, object_list_seeker);
139
140 if (0 != list_init(&slot->logins)) {
141 return CKR_HOST_MEMORY;
142 }
143 } else {
144 DEBUG_VSS(slot, "Reusing this old slot");
145
146 /* reuse the old list of logins/objects since they should be empty */
147 list_t logins = slot->logins;
148 list_t objects = slot->objects;
149
150 memset(slot, 0, sizeof *slot);
151
152 slot->logins = logins;
153 slot->objects = objects;
154 }
155
156 slot->login_user = -1;
157 slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot);
158 init_slot_info(&slot->slot_info, reader);
159 slot->reader = reader;
160
161 DEBUG_VSS(slot, "Finished initializing this slot");
162
163 return CKR_OK;
164 }
165
sc_pkcs11_card_free(struct sc_pkcs11_card * p11card)166 void sc_pkcs11_card_free(struct sc_pkcs11_card *p11card)
167 {
168 if (p11card) {
169 size_t i;
170 if (p11card->framework && p11card->framework->unbind)
171 p11card->framework->unbind(p11card);
172 sc_disconnect_card(p11card->card);
173 for (i=0; i < p11card->nmechanisms; ++i) {
174 if (p11card->mechanisms[i]->free_mech_data) {
175 p11card->mechanisms[i]->free_mech_data(p11card->mechanisms[i]->mech_data);
176 }
177 free(p11card->mechanisms[i]);
178 }
179 free(p11card->mechanisms);
180 free(p11card);
181 }
182 }
183
card_removed(sc_reader_t * reader)184 CK_RV card_removed(sc_reader_t * reader)
185 {
186 unsigned int i;
187 struct sc_pkcs11_card *p11card = NULL;
188 /* Mark all slots as "token not present" */
189 sc_log(context, "%s: card removed", reader->name);
190
191
192 for (i=0; i < list_size(&virtual_slots); i++) {
193 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
194 if (slot->reader == reader) {
195 /* Save the "card" object */
196 if (slot->p11card)
197 p11card = slot->p11card;
198 slot_token_removed(slot->id);
199 }
200 }
201
202 sc_pkcs11_card_free(p11card);
203
204 return CKR_OK;
205 }
206
207
card_detect(sc_reader_t * reader)208 CK_RV card_detect(sc_reader_t *reader)
209 {
210 struct sc_pkcs11_card *p11card = NULL;
211 int free_p11card = 0;
212 int rc;
213 CK_RV rv;
214 unsigned int i;
215 int j;
216
217 sc_log(context, "%s: Detecting smart card", reader->name);
218 /* Check if someone inserted a card */
219 again:
220 rc = sc_detect_card_presence(reader);
221 if (rc < 0) {
222 sc_log(context, "%s: failed, %s", reader->name, sc_strerror(rc));
223 return sc_to_cryptoki_error(rc, NULL);
224 }
225 if (rc == 0) {
226 sc_log(context, "%s: card absent", reader->name);
227 card_removed(reader); /* Release all resources */
228 return CKR_TOKEN_NOT_PRESENT;
229 }
230
231 /* If the card was changed, disconnect the current one */
232 if (rc & SC_READER_CARD_CHANGED) {
233 sc_log(context, "%s: Card changed", reader->name);
234 /* The following should never happen - but if it
235 * does we'll be stuck in an endless loop.
236 * So better be fussy.
237 if (!retry--)
238 return CKR_TOKEN_NOT_PRESENT; */
239 card_removed(reader);
240 goto again;
241 }
242
243 /* Locate a slot related to the reader */
244 for (i=0; i<list_size(&virtual_slots); i++) {
245 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
246 if (slot->reader == reader) {
247 p11card = slot->p11card;
248 break;
249 }
250 }
251
252 /* Detect the card if it's not known already */
253 if (p11card == NULL) {
254 sc_log(context, "%s: First seen the card ", reader->name);
255 p11card = (struct sc_pkcs11_card *)calloc(1, sizeof(struct sc_pkcs11_card));
256 if (!p11card)
257 return CKR_HOST_MEMORY;
258 free_p11card = 1;
259 p11card->reader = reader;
260 }
261
262 if (p11card->card == NULL) {
263 sc_log(context, "%s: Connecting ... ", reader->name);
264 rc = sc_connect_card(reader, &p11card->card);
265 if (rc != SC_SUCCESS) {
266 sc_log(context, "%s: SC connect card error %i", reader->name, rc);
267 rv = sc_to_cryptoki_error(rc, NULL);
268 goto fail;
269 }
270
271 /* escape commands are only guaranteed to be working with a card
272 * inserted. That's why by now, after sc_connect_card() the reader's
273 * metadata may have changed. We re-initialize the metadata for every
274 * slot of this reader here. */
275 if (reader->flags & SC_READER_ENABLE_ESCAPE) {
276 for (i = 0; i<list_size(&virtual_slots); i++) {
277 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
278 if (slot->reader == reader)
279 init_slot_info(&slot->slot_info, reader);
280 }
281 }
282
283 sc_log(context, "%s: Connected SC card %p", reader->name, p11card->card);
284 }
285
286 /* Detect the framework */
287 if (p11card->framework == NULL) {
288 struct sc_app_info *app_generic = sc_pkcs15_get_application_by_type(p11card->card, "generic");
289
290 sc_log(context, "%s: Detecting Framework. %i on-card applications", reader->name, p11card->card->app_count);
291 sc_log(context, "%s: generic application %s", reader->name, app_generic ? app_generic->label : "<none>");
292
293 for (i = 0; frameworks[i]; i++)
294 if (frameworks[i]->bind != NULL)
295 break;
296 /*TODO: only first framework is used: pkcs15init framework is not reachable here */
297 if (frameworks[i] == NULL) {
298 rv = CKR_GENERAL_ERROR;
299 goto fail;
300 }
301
302 p11card->framework = frameworks[i];
303
304 /* Initialize framework */
305 sc_log(context, "%s: Detected framework %d. Creating tokens.", reader->name, i);
306 /* Bind 'generic' application or (emulated?) card without applications */
307 if (app_generic || !p11card->card->app_count) {
308 scconf_block *conf_block = NULL;
309 int enable_InitToken = 0;
310
311 conf_block = sc_match_atr_block(p11card->card->ctx, NULL,
312 &p11card->reader->atr);
313 if (!conf_block) /* check default block */
314 conf_block = sc_get_conf_block(context,
315 "framework", "pkcs15", 1);
316
317 enable_InitToken = scconf_get_bool(conf_block,
318 "pkcs11_enable_InitToken", 0);
319
320 sc_log(context, "%s: Try to bind 'generic' token.", reader->name);
321 rv = frameworks[i]->bind(p11card, app_generic);
322 if (rv == CKR_TOKEN_NOT_RECOGNIZED && enable_InitToken) {
323 sc_log(context, "%s: 'InitToken' enabled -- accept non-binded card", reader->name);
324 rv = CKR_OK;
325 }
326 if (rv != CKR_OK) {
327 sc_log(context,
328 "%s: cannot bind 'generic' token: rv 0x%lX",
329 reader->name, rv);
330 goto fail;
331 }
332
333 sc_log(context, "%s: Creating 'generic' token.", reader->name);
334 rv = frameworks[i]->create_tokens(p11card, app_generic);
335 if (rv != CKR_OK) {
336 sc_log(context,
337 "%s: create 'generic' token error 0x%lX",
338 reader->name, rv);
339 goto fail;
340 }
341 /* p11card is now bound to some slot */
342 free_p11card = 0;
343 }
344
345 /* Now bind the rest of applications that are not 'generic' */
346 for (j = 0; j < p11card->card->app_count; j++) {
347 struct sc_app_info *app_info = p11card->card->app[j];
348 char *app_name = app_info ? app_info->label : "<anonymous>";
349
350 if (app_generic && app_generic == p11card->card->app[j])
351 continue;
352
353 sc_log(context, "%s: Binding %s token.", reader->name, app_name);
354 rv = frameworks[i]->bind(p11card, app_info);
355 if (rv != CKR_OK) {
356 sc_log(context, "%s: bind %s token error Ox%lX",
357 reader->name, app_name, rv);
358 continue;
359 }
360
361 sc_log(context, "%s: Creating %s token.", reader->name, app_name);
362 rv = frameworks[i]->create_tokens(p11card, app_info);
363 if (rv != CKR_OK) {
364 sc_log(context,
365 "%s: create %s token error 0x%lX",
366 reader->name, app_name, rv);
367 goto fail;
368 }
369 /* p11card is now bound to some slot */
370 free_p11card = 0;
371 }
372 }
373
374 sc_log(context, "%s: Detection ended", reader->name);
375 rv = CKR_OK;
376
377 fail:
378 if (free_p11card) {
379 sc_pkcs11_card_free(p11card);
380 }
381
382 return rv;
383 }
384
385
386 CK_RV
card_detect_all(void)387 card_detect_all(void)
388 {
389 unsigned int i, j;
390
391 sc_log(context, "Detect all cards");
392 /* Detect cards in all initialized readers */
393 for (i=0; i< sc_ctx_get_reader_count(context); i++) {
394 sc_reader_t *reader = sc_ctx_get_reader(context, i);
395
396 if (reader->flags & SC_READER_REMOVED) {
397 card_removed(reader);
398 /* do not remove slots related to this reader which would be
399 * possible according to PKCS#11 2.20 and later, because NSS can't
400 * handle a shrinking slot list
401 * https://bugzilla.mozilla.org/show_bug.cgi?id=1613632 */
402
403 /* Instead, remove the relation between reader and slot */
404 for (j = 0; j<list_size(&virtual_slots); j++) {
405 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, j);
406 if (slot->reader == reader) {
407 slot->reader = NULL;
408 }
409 }
410 } else {
411 /* Locate a slot related to the reader */
412 int found = 0;
413 for (j = 0; j<list_size(&virtual_slots); j++) {
414 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, j);
415 if (slot->reader == reader) {
416 found = 1;
417 break;
418 }
419 }
420 if (!found) {
421 for (j = 0; j < sc_pkcs11_conf.slots_per_card; j++) {
422 CK_RV rv = create_slot(reader);
423 if (rv != CKR_OK)
424 return rv;
425 }
426 }
427 card_detect(reader);
428 }
429 }
430 sc_log(context, "All cards detected");
431 return CKR_OK;
432 }
433
434 /* Allocates an existing slot to a card */
slot_allocate(struct sc_pkcs11_slot ** slot,struct sc_pkcs11_card * p11card)435 CK_RV slot_allocate(struct sc_pkcs11_slot ** slot, struct sc_pkcs11_card * p11card)
436 {
437 unsigned int i;
438 struct sc_pkcs11_slot *tmp_slot = NULL;
439
440 /* Locate a free slot for this reader */
441 for (i=0; i< list_size(&virtual_slots); i++) {
442 tmp_slot = (struct sc_pkcs11_slot *)list_get_at(&virtual_slots, i);
443 if (tmp_slot->reader == p11card->reader && tmp_slot->p11card == NULL)
444 break;
445 }
446 if (!tmp_slot || (i == list_size(&virtual_slots)))
447 return CKR_FUNCTION_FAILED;
448 sc_log(context, "Allocated slot 0x%lx for card in reader %s", tmp_slot->id, p11card->reader->name);
449 tmp_slot->p11card = p11card;
450 tmp_slot->events = SC_EVENT_CARD_INSERTED;
451 *slot = tmp_slot;
452 return CKR_OK;
453 }
454
slot_get_slot(CK_SLOT_ID id,struct sc_pkcs11_slot ** slot)455 CK_RV slot_get_slot(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot)
456 {
457 if (context == NULL)
458 return CKR_CRYPTOKI_NOT_INITIALIZED;
459
460 *slot = list_seek(&virtual_slots, &id); /* FIXME: check for null? */
461 if (!*slot)
462 return CKR_SLOT_ID_INVALID;
463 return CKR_OK;
464 }
465
slot_get_token(CK_SLOT_ID id,struct sc_pkcs11_slot ** slot)466 CK_RV slot_get_token(CK_SLOT_ID id, struct sc_pkcs11_slot ** slot)
467 {
468 CK_RV rv;
469
470 sc_log(context, "Slot(id=0x%lX): get token", id);
471 rv = slot_get_slot(id, slot);
472 if (rv != CKR_OK)
473 return rv;
474
475 if (!((*slot)->slot_info.flags & CKF_TOKEN_PRESENT)) {
476 if ((*slot)->reader == NULL)
477 return CKR_TOKEN_NOT_PRESENT;
478 sc_log(context, "Slot(id=0x%lX): get token: now detect card", id);
479 rv = card_detect((*slot)->reader);
480 if (rv != CKR_OK)
481 return rv;
482 }
483
484 if (!((*slot)->slot_info.flags & CKF_TOKEN_PRESENT)) {
485 sc_log(context, "card detected, but slot not presenting token");
486 return CKR_TOKEN_NOT_PRESENT;
487 }
488 sc_log(context, "Slot-get-token returns OK");
489 return CKR_OK;
490 }
491
slot_token_removed(CK_SLOT_ID id)492 CK_RV slot_token_removed(CK_SLOT_ID id)
493 {
494 CK_RV rv;
495 int token_was_present;
496 struct sc_pkcs11_slot *slot;
497 struct sc_pkcs11_object *object;
498
499 sc_log(context, "slot_token_removed(0x%lx)", id);
500 rv = slot_get_slot(id, &slot);
501 if (rv != CKR_OK)
502 return rv;
503
504 token_was_present = (slot->slot_info.flags & CKF_TOKEN_PRESENT);
505
506 /* Terminate active sessions */
507 sc_pkcs11_close_all_sessions(id);
508
509 while ((object = list_fetch(&slot->objects))) {
510 if (object->ops->release)
511 object->ops->release(object);
512 }
513
514 /* Release framework stuff */
515 if (slot->p11card != NULL) {
516 if (slot->fw_data != NULL && slot->p11card->framework != NULL
517 && slot->p11card->framework->release_token != NULL) {
518 slot->p11card->framework->release_token(slot->p11card, slot->fw_data);
519 slot->fw_data = NULL;
520 }
521 slot->p11card = NULL;
522 }
523
524 /* Reset relevant slot properties */
525 slot->slot_info.flags &= ~CKF_TOKEN_PRESENT;
526 slot->login_user = -1;
527 pop_all_login_states(slot);
528
529 if (token_was_present)
530 slot->events = SC_EVENT_CARD_REMOVED;
531
532 memset(&slot->token_info, 0, sizeof slot->token_info);
533
534 return CKR_OK;
535 }
536
537 /* Called from C_WaitForSlotEvent */
slot_find_changed(CK_SLOT_ID_PTR idp,int mask)538 CK_RV slot_find_changed(CK_SLOT_ID_PTR idp, int mask)
539 {
540 unsigned int i;
541 LOG_FUNC_CALLED(context);
542
543 card_detect_all();
544 for (i=0; i<list_size(&virtual_slots); i++) {
545 sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
546 sc_log(context, "slot 0x%lx token: %lu events: 0x%02X",
547 slot->id, (slot->slot_info.flags & CKF_TOKEN_PRESENT),
548 slot->events);
549 if ((slot->events & SC_EVENT_CARD_INSERTED)
550 && !(slot->slot_info.flags & CKF_TOKEN_PRESENT)) {
551 /* If a token has not been initialized, clear the inserted event */
552 slot->events &= ~SC_EVENT_CARD_INSERTED;
553 }
554 sc_log(context, "mask: 0x%02X events: 0x%02X result: %d", mask, slot->events, (slot->events & mask));
555
556 if (slot->events & mask) {
557 slot->events &= ~mask;
558 *idp = slot->id;
559 LOG_FUNC_RETURN(context, CKR_OK);
560 }
561 }
562 LOG_FUNC_RETURN(context, CKR_NO_EVENT);
563 }
564