]>
Commit | Line | Data |
---|---|---|
111a38b0 RR |
1 | /* |
2 | * This is the actual card emulator. | |
3 | * | |
4 | * These functions can be implemented in different ways on different platforms | |
5 | * using the underlying system primitives. For Linux it uses NSS, though direct | |
6 | * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be | |
7 | * used. On Windows CAPI could be used. | |
8 | * | |
9 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
10 | * See the COPYING.LIB file in the top-level directory. | |
11 | */ | |
12 | ||
13 | /* | |
14 | * NSS headers | |
15 | */ | |
16 | ||
17 | /* avoid including prototypes.h that redefines uint32 */ | |
18 | #define NO_NSPR_10_SUPPORT | |
19 | ||
20 | #include <nss.h> | |
21 | #include <pk11pub.h> | |
22 | #include <cert.h> | |
23 | #include <key.h> | |
24 | #include <secmod.h> | |
25 | #include <prthread.h> | |
26 | #include <secerr.h> | |
27 | ||
28 | #include "qemu-common.h" | |
29 | ||
30 | #include "vcard.h" | |
31 | #include "card_7816t.h" | |
32 | #include "vcard_emul.h" | |
33 | #include "vreader.h" | |
34 | #include "vevent.h" | |
35 | ||
36 | struct VCardKeyStruct { | |
37 | CERTCertificate *cert; | |
38 | PK11SlotInfo *slot; | |
39 | SECKEYPrivateKey *key; | |
40 | }; | |
41 | ||
42 | ||
43 | typedef struct VirtualReaderOptionsStruct VirtualReaderOptions; | |
44 | ||
45 | struct VReaderEmulStruct { | |
46 | PK11SlotInfo *slot; | |
47 | VCardEmulType default_type; | |
48 | char *type_params; | |
49 | PRBool present; | |
50 | int series; | |
51 | VCard *saved_vcard; | |
52 | }; | |
53 | ||
54 | /* | |
55 | * NSS Specific options | |
56 | */ | |
57 | struct VirtualReaderOptionsStruct { | |
58 | char *name; | |
59 | char *vname; | |
60 | VCardEmulType card_type; | |
61 | char *type_params; | |
62 | char **cert_name; | |
63 | int cert_count; | |
64 | }; | |
65 | ||
66 | struct VCardEmulOptionsStruct { | |
67 | void *nss_db; | |
68 | VirtualReaderOptions *vreader; | |
69 | int vreader_count; | |
70 | VCardEmulType hw_card_type; | |
71 | const char *hw_type_params; | |
72 | PRBool use_hw; | |
73 | }; | |
74 | ||
75 | static int nss_emul_init; | |
76 | ||
77 | /* if we have more that just the slot, define | |
78 | * VCardEmulStruct here */ | |
79 | ||
80 | /* | |
81 | * allocate the set of arrays for certs, cert_len, key | |
82 | */ | |
83 | static PRBool | |
84 | vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, | |
85 | VCardKey ***keysp, int cert_count) | |
86 | { | |
87 | *certsp = NULL; | |
88 | *cert_lenp = NULL; | |
89 | *keysp = NULL; | |
90 | *certsp = (unsigned char **)qemu_malloc(sizeof(unsigned char *)*cert_count); | |
91 | *cert_lenp = (int *)qemu_malloc(sizeof(int)*cert_count); | |
92 | *keysp = (VCardKey **)qemu_malloc(sizeof(VCardKey *)*cert_count); | |
93 | return PR_TRUE; | |
94 | } | |
95 | ||
96 | /* | |
97 | * Emulator specific card information | |
98 | */ | |
99 | typedef struct CardEmulCardStruct CardEmulPrivate; | |
100 | ||
101 | static VCardEmul * | |
102 | vcard_emul_new_card(PK11SlotInfo *slot) | |
103 | { | |
104 | PK11_ReferenceSlot(slot); | |
105 | /* currently we don't need anything other than the slot */ | |
106 | return (VCardEmul *)slot; | |
107 | } | |
108 | ||
109 | static void | |
110 | vcard_emul_delete_card(VCardEmul *vcard_emul) | |
111 | { | |
112 | PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul; | |
113 | if (slot == NULL) { | |
114 | return; | |
115 | } | |
116 | PK11_FreeSlot(slot); | |
117 | } | |
118 | ||
119 | static PK11SlotInfo * | |
120 | vcard_emul_card_get_slot(VCard *card) | |
121 | { | |
122 | /* note, the card is holding the reference, no need to get another one */ | |
123 | return (PK11SlotInfo *)vcard_get_private(card); | |
124 | } | |
125 | ||
126 | ||
127 | /* | |
128 | * key functions | |
129 | */ | |
130 | /* private constructure */ | |
131 | static VCardKey * | |
132 | vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) | |
133 | { | |
134 | VCardKey *key; | |
135 | ||
136 | key = (VCardKey *)qemu_malloc(sizeof(VCardKey)); | |
137 | key->slot = PK11_ReferenceSlot(slot); | |
138 | key->cert = CERT_DupCertificate(cert); | |
139 | /* NOTE: if we aren't logged into the token, this could return NULL */ | |
140 | /* NOTE: the cert is a temp cert, not necessarily the cert in the token, | |
141 | * use the DER version of this function */ | |
142 | key->key = PK11_FindKeyByDERCert(slot, cert, NULL); | |
143 | return key; | |
144 | } | |
145 | ||
146 | /* destructor */ | |
147 | void | |
148 | vcard_emul_delete_key(VCardKey *key) | |
149 | { | |
150 | if (!nss_emul_init || (key == NULL)) { | |
151 | return; | |
152 | } | |
153 | if (key->key) { | |
154 | SECKEY_DestroyPrivateKey(key->key); | |
155 | key->key = NULL; | |
156 | } | |
157 | if (key->cert) { | |
158 | CERT_DestroyCertificate(key->cert); | |
159 | } | |
160 | if (key->slot) { | |
161 | PK11_FreeSlot(key->slot); | |
162 | } | |
163 | return; | |
164 | } | |
165 | ||
166 | /* | |
167 | * grab the nss key from a VCardKey. If it doesn't exist, try to look it up | |
168 | */ | |
169 | static SECKEYPrivateKey * | |
170 | vcard_emul_get_nss_key(VCardKey *key) | |
171 | { | |
172 | if (key->key) { | |
173 | return key->key; | |
174 | } | |
175 | /* NOTE: if we aren't logged into the token, this could return NULL */ | |
176 | key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL); | |
177 | return key->key; | |
178 | } | |
179 | ||
180 | /* | |
181 | * Map NSS errors to 7816 errors | |
182 | */ | |
183 | static vcard_7816_status_t | |
184 | vcard_emul_map_error(int error) | |
185 | { | |
186 | switch (error) { | |
187 | case SEC_ERROR_TOKEN_NOT_LOGGED_IN: | |
188 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
189 | case SEC_ERROR_BAD_DATA: | |
190 | case SEC_ERROR_OUTPUT_LEN: | |
191 | case SEC_ERROR_INPUT_LEN: | |
192 | case SEC_ERROR_INVALID_ARGS: | |
193 | case SEC_ERROR_INVALID_ALGORITHM: | |
194 | case SEC_ERROR_NO_KEY: | |
195 | case SEC_ERROR_INVALID_KEY: | |
196 | case SEC_ERROR_DECRYPTION_DISALLOWED: | |
197 | return VCARD7816_STATUS_ERROR_DATA_INVALID; | |
198 | case SEC_ERROR_NO_MEMORY: | |
199 | return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; | |
200 | } | |
201 | return VCARD7816_STATUS_EXC_ERROR_CHANGE; | |
202 | } | |
203 | ||
204 | /* RSA sign/decrypt with the key, signature happens 'in place' */ | |
205 | vcard_7816_status_t | |
206 | vcard_emul_rsa_op(VCard *card, VCardKey *key, | |
207 | unsigned char *buffer, int buffer_size) | |
208 | { | |
209 | SECKEYPrivateKey *priv_key; | |
210 | unsigned signature_len; | |
211 | SECStatus rv; | |
212 | ||
213 | if ((!nss_emul_init) || (key == NULL)) { | |
214 | /* couldn't get the key, indicate that we aren't logged in */ | |
215 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
216 | } | |
217 | priv_key = vcard_emul_get_nss_key(key); | |
218 | ||
219 | /* | |
220 | * this is only true of the rsa signature | |
221 | */ | |
222 | signature_len = PK11_SignatureLen(priv_key); | |
223 | if (buffer_size != signature_len) { | |
224 | return VCARD7816_STATUS_ERROR_DATA_INVALID; | |
225 | } | |
226 | rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len, | |
227 | buffer, buffer_size); | |
228 | if (rv != SECSuccess) { | |
229 | return vcard_emul_map_error(PORT_GetError()); | |
230 | } | |
231 | assert(buffer_size == signature_len); | |
232 | return VCARD7816_STATUS_SUCCESS; | |
233 | } | |
234 | ||
235 | /* | |
236 | * Login functions | |
237 | */ | |
238 | /* return the number of login attempts still possible on the card. if unknown, | |
239 | * return -1 */ | |
240 | int | |
241 | vcard_emul_get_login_count(VCard *card) | |
242 | { | |
243 | return -1; | |
244 | } | |
245 | ||
246 | /* login into the card, return the 7816 status word (sw2 || sw1) */ | |
247 | vcard_7816_status_t | |
248 | vcard_emul_login(VCard *card, unsigned char *pin, int pin_len) | |
249 | { | |
250 | PK11SlotInfo *slot; | |
251 | unsigned char *pin_string = NULL; | |
252 | int i; | |
253 | SECStatus rv; | |
254 | ||
255 | if (!nss_emul_init) { | |
256 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
257 | } | |
258 | slot = vcard_emul_card_get_slot(card); | |
259 | /* We depend on the PKCS #11 module internal login state here because we | |
260 | * create a separate process to handle each guest instance. If we needed | |
261 | * to handle multiple guests from one process, then we would need to keep | |
262 | * a lot of extra state in our card structure | |
263 | * */ | |
264 | pin_string = qemu_malloc(pin_len+1); | |
265 | memcpy(pin_string, pin, pin_len); | |
266 | pin_string[pin_len] = 0; | |
267 | ||
268 | /* handle CAC expanded pins correctly */ | |
269 | for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) { | |
270 | pin_string[i] = 0; | |
271 | } | |
272 | ||
273 | rv = PK11_Authenticate(slot, PR_FALSE, pin_string); | |
274 | memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory | |
275 | to be snooped */ | |
276 | qemu_free(pin_string); | |
277 | if (rv == SECSuccess) { | |
278 | return VCARD7816_STATUS_SUCCESS; | |
279 | } | |
280 | /* map the error from port get error */ | |
281 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
282 | } | |
283 | ||
284 | void | |
285 | vcard_emul_reset(VCard *card, VCardPower power) | |
286 | { | |
287 | PK11SlotInfo *slot; | |
288 | ||
289 | if (!nss_emul_init) { | |
290 | return; | |
291 | } | |
292 | ||
293 | /* | |
294 | * if we reset the card (either power on or power off), we lose our login | |
295 | * state | |
296 | */ | |
297 | /* TODO: we may also need to send insertion/removal events? */ | |
298 | slot = vcard_emul_card_get_slot(card); | |
299 | PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ | |
300 | return; | |
301 | } | |
302 | ||
303 | ||
304 | static VReader * | |
305 | vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) | |
306 | { | |
307 | VReaderList *reader_list = vreader_get_reader_list(); | |
308 | VReaderListEntry *current_entry = NULL; | |
309 | ||
310 | if (reader_list == NULL) { | |
311 | return NULL; | |
312 | } | |
313 | for (current_entry = vreader_list_get_first(reader_list); current_entry; | |
314 | current_entry = vreader_list_get_next(current_entry)) { | |
315 | VReader *reader = vreader_list_get_reader(current_entry); | |
316 | VReaderEmul *reader_emul = vreader_get_private(reader); | |
317 | if (reader_emul->slot == slot) { | |
318 | return reader; | |
319 | } | |
320 | vreader_free(reader); | |
321 | } | |
322 | ||
323 | return NULL; | |
324 | } | |
325 | ||
326 | /* | |
327 | * create a new reader emul | |
328 | */ | |
329 | static VReaderEmul * | |
330 | vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) | |
331 | { | |
332 | VReaderEmul *new_reader_emul; | |
333 | ||
334 | new_reader_emul = (VReaderEmul *)qemu_malloc(sizeof(VReaderEmul)); | |
335 | ||
336 | new_reader_emul->slot = PK11_ReferenceSlot(slot); | |
337 | new_reader_emul->default_type = type; | |
338 | new_reader_emul->type_params = strdup(params); | |
339 | new_reader_emul->present = PR_FALSE; | |
340 | new_reader_emul->series = 0; | |
341 | new_reader_emul->saved_vcard = NULL; | |
342 | return new_reader_emul; | |
343 | } | |
344 | ||
345 | static void | |
346 | vreader_emul_delete(VReaderEmul *vreader_emul) | |
347 | { | |
348 | if (vreader_emul == NULL) { | |
349 | return; | |
350 | } | |
351 | if (vreader_emul->slot) { | |
352 | PK11_FreeSlot(vreader_emul->slot); | |
353 | } | |
354 | if (vreader_emul->type_params) { | |
355 | qemu_free(vreader_emul->type_params); | |
356 | } | |
357 | qemu_free(vreader_emul); | |
358 | } | |
359 | ||
360 | /* | |
361 | * TODO: move this to emulater non-specific file | |
362 | */ | |
363 | static VCardEmulType | |
364 | vcard_emul_get_type(VReader *vreader) | |
365 | { | |
366 | VReaderEmul *vreader_emul; | |
367 | ||
368 | vreader_emul = vreader_get_private(vreader); | |
369 | if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { | |
370 | return vreader_emul->default_type; | |
371 | } | |
372 | ||
373 | return vcard_emul_type_select(vreader); | |
374 | } | |
375 | /* | |
376 | * TODO: move this to emulater non-specific file | |
377 | */ | |
378 | static const char * | |
379 | vcard_emul_get_type_params(VReader *vreader) | |
380 | { | |
381 | VReaderEmul *vreader_emul; | |
382 | ||
383 | vreader_emul = vreader_get_private(vreader); | |
384 | if (vreader_emul && vreader_emul->type_params) { | |
385 | return vreader_emul->type_params; | |
386 | } | |
387 | ||
388 | return ""; | |
389 | } | |
390 | ||
391 | /* pull the slot out of the reader private data */ | |
392 | static PK11SlotInfo * | |
393 | vcard_emul_reader_get_slot(VReader *vreader) | |
394 | { | |
395 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |
396 | if (vreader_emul == NULL) { | |
397 | return NULL; | |
398 | } | |
399 | return vreader_emul->slot; | |
400 | } | |
401 | ||
402 | /* | |
403 | * Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate | |
404 | * historical bytes for any software emulated card. The remaining bytes can be | |
405 | * used to indicate the actual emulator | |
406 | */ | |
407 | static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' }; | |
408 | ||
409 | void | |
410 | vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) | |
411 | { | |
412 | int len = MIN(sizeof(nss_atr), *atr_len); | |
413 | assert(atr != NULL); | |
414 | ||
415 | memcpy(atr, nss_atr, len); | |
416 | *atr_len = len; | |
417 | return; | |
418 | } | |
419 | ||
420 | /* | |
421 | * create a new card from certs and keys | |
422 | */ | |
423 | static VCard * | |
424 | vcard_emul_make_card(VReader *reader, | |
425 | unsigned char * const *certs, int *cert_len, | |
426 | VCardKey *keys[], int cert_count) | |
427 | { | |
428 | VCardEmul *vcard_emul; | |
429 | VCard *vcard; | |
430 | PK11SlotInfo *slot; | |
431 | VCardEmulType type; | |
432 | const char *params; | |
433 | ||
434 | type = vcard_emul_get_type(reader); | |
435 | ||
436 | /* ignore the inserted card */ | |
437 | if (type == VCARD_EMUL_NONE) { | |
438 | return NULL; | |
439 | } | |
440 | slot = vcard_emul_reader_get_slot(reader); | |
441 | if (slot == NULL) { | |
442 | return NULL; | |
443 | } | |
444 | ||
445 | params = vcard_emul_get_type_params(reader); | |
446 | /* params these can be NULL */ | |
447 | ||
448 | vcard_emul = vcard_emul_new_card(slot); | |
449 | if (vcard_emul == NULL) { | |
450 | return NULL; | |
451 | } | |
452 | vcard = vcard_new(vcard_emul, vcard_emul_delete_card); | |
453 | if (vcard == NULL) { | |
454 | vcard_emul_delete_card(vcard_emul); | |
455 | return NULL; | |
456 | } | |
457 | vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); | |
458 | return vcard; | |
459 | } | |
460 | ||
461 | ||
462 | /* | |
463 | * 'clone' a physical card as a virtual card | |
464 | */ | |
465 | static VCard * | |
466 | vcard_emul_mirror_card(VReader *vreader) | |
467 | { | |
468 | /* | |
469 | * lookup certs using the C_FindObjects. The Stan Cert handle won't give | |
470 | * us the real certs until we log in. | |
471 | */ | |
472 | PK11GenericObject *firstObj, *thisObj; | |
473 | int cert_count; | |
474 | unsigned char **certs; | |
475 | int *cert_len; | |
476 | VCardKey **keys; | |
477 | PK11SlotInfo *slot; | |
478 | PRBool ret; | |
479 | ||
480 | slot = vcard_emul_reader_get_slot(vreader); | |
481 | if (slot == NULL) { | |
482 | return NULL; | |
483 | } | |
484 | ||
485 | firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); | |
486 | if (firstObj == NULL) { | |
487 | return NULL; | |
488 | } | |
489 | ||
490 | /* count the certs */ | |
491 | cert_count = 0; | |
492 | for (thisObj = firstObj; thisObj; | |
493 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |
494 | cert_count++; | |
495 | } | |
496 | ||
497 | if (cert_count == 0) { | |
498 | PK11_DestroyGenericObjects(firstObj); | |
499 | return NULL; | |
500 | } | |
501 | ||
502 | /* allocate the arrays */ | |
503 | ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); | |
504 | if (ret == PR_FALSE) { | |
505 | return NULL; | |
506 | } | |
507 | ||
508 | /* fill in the arrays */ | |
509 | cert_count = 0; | |
510 | for (thisObj = firstObj; thisObj; | |
511 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |
512 | SECItem derCert; | |
513 | CERTCertificate *cert; | |
514 | SECStatus rv; | |
515 | ||
516 | rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, | |
517 | CKA_VALUE, &derCert); | |
518 | if (rv != SECSuccess) { | |
519 | continue; | |
520 | } | |
521 | /* create floating temp cert. This gives us a cert structure even if | |
522 | * the token isn't logged in */ | |
523 | cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, | |
524 | NULL, PR_FALSE, PR_TRUE); | |
525 | SECITEM_FreeItem(&derCert, PR_FALSE); | |
526 | if (cert == NULL) { | |
527 | continue; | |
528 | } | |
529 | ||
530 | certs[cert_count] = cert->derCert.data; | |
531 | cert_len[cert_count] = cert->derCert.len; | |
532 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |
533 | cert_count++; | |
534 | CERT_DestroyCertificate(cert); /* key obj still has a reference */ | |
535 | } | |
536 | ||
537 | /* now create the card */ | |
538 | return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); | |
539 | } | |
540 | ||
541 | static VCardEmulType default_card_type = VCARD_EMUL_NONE; | |
542 | static const char *default_type_params = ""; | |
543 | ||
544 | /* | |
545 | * This thread looks for card and reader insertions and puts events on the | |
546 | * event queue | |
547 | */ | |
548 | static void | |
549 | vcard_emul_event_thread(void *arg) | |
550 | { | |
551 | PK11SlotInfo *slot; | |
552 | VReader *vreader; | |
553 | VReaderEmul *vreader_emul; | |
554 | VCard *vcard; | |
555 | SECMODModule *module = (SECMODModule *)arg; | |
556 | ||
557 | do { | |
558 | slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); | |
559 | if (slot == NULL) { | |
560 | break; | |
561 | } | |
562 | vreader = vcard_emul_find_vreader_from_slot(slot); | |
563 | if (vreader == NULL) { | |
564 | /* new vreader */ | |
565 | vreader_emul = vreader_emul_new(slot, default_card_type, | |
566 | default_type_params); | |
567 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |
568 | vreader_emul_delete); | |
569 | PK11_FreeSlot(slot); | |
570 | slot = NULL; | |
571 | vreader_add_reader(vreader); | |
572 | vreader_free(vreader); | |
573 | continue; | |
574 | } | |
575 | /* card remove/insert */ | |
576 | vreader_emul = vreader_get_private(vreader); | |
577 | if (PK11_IsPresent(slot)) { | |
578 | int series = PK11_GetSlotSeries(slot); | |
579 | if (series != vreader_emul->series) { | |
580 | if (vreader_emul->present) { | |
581 | vreader_insert_card(vreader, NULL); | |
582 | } | |
583 | vcard = vcard_emul_mirror_card(vreader); | |
584 | vreader_insert_card(vreader, vcard); | |
585 | vcard_free(vcard); | |
586 | } | |
587 | vreader_emul->series = series; | |
588 | vreader_emul->present = 1; | |
589 | vreader_free(vreader); | |
590 | PK11_FreeSlot(slot); | |
591 | continue; | |
592 | } | |
593 | if (vreader_emul->present) { | |
594 | vreader_insert_card(vreader, NULL); | |
595 | } | |
596 | vreader_emul->series = 0; | |
597 | vreader_emul->present = 0; | |
598 | PK11_FreeSlot(slot); | |
599 | vreader_free(vreader); | |
600 | } while (1); | |
601 | } | |
602 | ||
603 | /* if the card is inserted when we start up, make sure our state is correct */ | |
604 | static void | |
605 | vcard_emul_init_series(VReader *vreader, VCard *vcard) | |
606 | { | |
607 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |
608 | PK11SlotInfo *slot = vreader_emul->slot; | |
609 | ||
610 | vreader_emul->present = PK11_IsPresent(slot); | |
611 | vreader_emul->series = PK11_GetSlotSeries(slot); | |
612 | if (vreader_emul->present == 0) { | |
613 | vreader_insert_card(vreader, NULL); | |
614 | } | |
615 | } | |
616 | ||
617 | /* | |
618 | * each module has a separate wait call, create a thread for each module that | |
619 | * we are using. | |
620 | */ | |
621 | static void | |
622 | vcard_emul_new_event_thread(SECMODModule *module) | |
623 | { | |
624 | PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, | |
625 | module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, | |
626 | PR_UNJOINABLE_THREAD, 0); | |
627 | } | |
628 | ||
629 | static const VCardEmulOptions default_options = { | |
630 | .nss_db = NULL, | |
631 | .vreader = NULL, | |
632 | .vreader_count = 0, | |
633 | .hw_card_type = VCARD_EMUL_CAC, | |
634 | .hw_type_params = "", | |
635 | .use_hw = PR_TRUE | |
636 | }; | |
637 | ||
638 | ||
639 | /* | |
640 | * NSS needs the app to supply a password prompt. In our case the only time | |
641 | * the password is supplied is as part of the Login APDU. The actual password | |
642 | * is passed in the pw_arg in that case. In all other cases pw_arg should be | |
643 | * NULL. | |
644 | */ | |
645 | static char * | |
646 | vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) | |
647 | { | |
648 | /* if it didn't work the first time, don't keep trying */ | |
649 | if (retries) { | |
650 | return NULL; | |
651 | } | |
652 | /* we are looking up a password when we don't have one in hand */ | |
653 | if (pw_arg == NULL) { | |
654 | return NULL; | |
655 | } | |
656 | /* TODO: we really should verify that were are using the right slot */ | |
657 | return PORT_Strdup(pw_arg); | |
658 | } | |
659 | ||
660 | /* Force a card removal even if the card is not physically removed */ | |
661 | VCardEmulError | |
662 | vcard_emul_force_card_remove(VReader *vreader) | |
663 | { | |
664 | if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { | |
665 | return VCARD_EMUL_FAIL; /* card is already removed */ | |
666 | } | |
667 | ||
668 | /* OK, remove it */ | |
669 | vreader_insert_card(vreader, NULL); | |
670 | return VCARD_EMUL_OK; | |
671 | } | |
672 | ||
673 | /* Re-insert of a card that has been removed by force removal */ | |
674 | VCardEmulError | |
675 | vcard_emul_force_card_insert(VReader *vreader) | |
676 | { | |
677 | VReaderEmul *vreader_emul; | |
678 | VCard *vcard; | |
679 | ||
680 | if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { | |
681 | return VCARD_EMUL_FAIL; /* card is already removed */ | |
682 | } | |
683 | vreader_emul = vreader_get_private(vreader); | |
684 | ||
685 | /* if it's a softcard, get the saved vcard from the reader emul structure */ | |
686 | if (vreader_emul->saved_vcard) { | |
687 | vcard = vcard_reference(vreader_emul->saved_vcard); | |
688 | } else { | |
689 | /* it must be a physical card, rebuild it */ | |
690 | if (!PK11_IsPresent(vreader_emul->slot)) { | |
691 | /* physical card has been removed, not way to reinsert it */ | |
692 | return VCARD_EMUL_FAIL; | |
693 | } | |
694 | vcard = vcard_emul_mirror_card(vreader); | |
695 | } | |
696 | vreader_insert_card(vreader, vcard); | |
697 | vcard_free(vcard); | |
698 | ||
699 | return VCARD_EMUL_OK; | |
700 | } | |
701 | ||
702 | ||
703 | static PRBool | |
704 | module_has_removable_hw_slots(SECMODModule *mod) | |
705 | { | |
706 | int i; | |
707 | PRBool ret = PR_FALSE; | |
708 | SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); | |
709 | ||
710 | if (!moduleLock) { | |
711 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); | |
712 | return ret; | |
713 | } | |
714 | SECMOD_GetReadLock(moduleLock); | |
715 | for (i = 0; i < mod->slotCount; i++) { | |
716 | PK11SlotInfo *slot = mod->slots[i]; | |
717 | if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) { | |
718 | ret = PR_TRUE; | |
719 | break; | |
720 | } | |
721 | } | |
722 | SECMOD_ReleaseReadLock(moduleLock); | |
723 | return ret; | |
724 | } | |
725 | ||
726 | /* Previously we returned FAIL if no readers found. This makes | |
727 | * no sense when using hardware, since there may be no readers connected | |
728 | * at the time vcard_emul_init is called, but they will be properly | |
729 | * recognized later. So Instead return FAIL only if no_hw==1 and no | |
730 | * vcards can be created (indicates error with certificates provided | |
731 | * or db), or if any other higher level error (NSS error, missing coolkey). */ | |
732 | static int vcard_emul_init_called; | |
733 | ||
734 | VCardEmulError | |
735 | vcard_emul_init(const VCardEmulOptions *options) | |
736 | { | |
737 | SECStatus rv; | |
738 | PRBool ret, has_readers = PR_FALSE, need_coolkey_module; | |
739 | VReader *vreader; | |
740 | VReaderEmul *vreader_emul; | |
741 | SECMODListLock *module_lock; | |
742 | SECMODModuleList *module_list; | |
743 | SECMODModuleList *mlp; | |
744 | int i; | |
745 | ||
746 | if (vcard_emul_init_called) { | |
747 | return VCARD_EMUL_INIT_ALREADY_INITED; | |
748 | } | |
749 | vcard_emul_init_called = 1; | |
750 | vreader_init(); | |
751 | vevent_queue_init(); | |
752 | ||
753 | if (options == NULL) { | |
754 | options = &default_options; | |
755 | } | |
756 | ||
757 | /* first initialize NSS */ | |
758 | if (options->nss_db) { | |
759 | rv = NSS_Init(options->nss_db); | |
760 | } else { | |
761 | rv = NSS_Init("sql:/etc/pki/nssdb"); | |
762 | } | |
763 | if (rv != SECSuccess) { | |
764 | return VCARD_EMUL_FAIL; | |
765 | } | |
766 | /* Set password callback function */ | |
767 | PK11_SetPasswordFunc(vcard_emul_get_password); | |
768 | ||
769 | /* set up soft cards emulated by software certs rather than physical cards | |
770 | * */ | |
771 | for (i = 0; i < options->vreader_count; i++) { | |
772 | int j; | |
773 | int cert_count; | |
774 | unsigned char **certs; | |
775 | int *cert_len; | |
776 | VCardKey **keys; | |
777 | PK11SlotInfo *slot; | |
778 | ||
779 | slot = PK11_FindSlotByName(options->vreader[i].name); | |
780 | if (slot == NULL) { | |
781 | continue; | |
782 | } | |
783 | vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, | |
784 | options->vreader[i].type_params); | |
785 | vreader = vreader_new(options->vreader[i].vname, vreader_emul, | |
786 | vreader_emul_delete); | |
787 | vreader_add_reader(vreader); | |
788 | cert_count = options->vreader[i].cert_count; | |
789 | ||
790 | ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys, | |
791 | options->vreader[i].cert_count); | |
792 | if (ret == PR_FALSE) { | |
793 | continue; | |
794 | } | |
795 | cert_count = 0; | |
796 | for (j = 0; j < options->vreader[i].cert_count; j++) { | |
797 | /* we should have a better way of identifying certs than by | |
798 | * nickname here */ | |
799 | CERTCertificate *cert = PK11_FindCertFromNickname( | |
800 | options->vreader[i].cert_name[j], | |
801 | NULL); | |
802 | if (cert == NULL) { | |
803 | continue; | |
804 | } | |
805 | certs[cert_count] = cert->derCert.data; | |
806 | cert_len[cert_count] = cert->derCert.len; | |
807 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |
808 | /* this is safe because the key is still holding a cert reference */ | |
809 | CERT_DestroyCertificate(cert); | |
810 | cert_count++; | |
811 | } | |
812 | if (cert_count) { | |
813 | VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, | |
814 | keys, cert_count); | |
815 | vreader_insert_card(vreader, vcard); | |
816 | vcard_emul_init_series(vreader, vcard); | |
817 | /* allow insertion and removal of soft cards */ | |
818 | vreader_emul->saved_vcard = vcard_reference(vcard); | |
819 | vcard_free(vcard); | |
820 | vreader_free(vreader); | |
821 | has_readers = PR_TRUE; | |
822 | } | |
823 | } | |
824 | ||
825 | /* if we aren't suppose to use hw, skip looking up hardware tokens */ | |
826 | if (!options->use_hw) { | |
827 | nss_emul_init = has_readers; | |
828 | return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; | |
829 | } | |
830 | ||
831 | /* make sure we have some PKCS #11 module loaded */ | |
832 | module_lock = SECMOD_GetDefaultModuleListLock(); | |
833 | module_list = SECMOD_GetDefaultModuleList(); | |
834 | need_coolkey_module = !has_readers; | |
835 | SECMOD_GetReadLock(module_lock); | |
836 | for (mlp = module_list; mlp; mlp = mlp->next) { | |
837 | SECMODModule *module = mlp->module; | |
838 | if (module_has_removable_hw_slots(module)) { | |
839 | need_coolkey_module = PR_FALSE; | |
840 | break; | |
841 | } | |
842 | } | |
843 | SECMOD_ReleaseReadLock(module_lock); | |
844 | ||
845 | if (need_coolkey_module) { | |
846 | SECMODModule *module; | |
847 | module = SECMOD_LoadUserModule( | |
848 | (char *)"library=libcoolkeypk11.so name=Coolkey", | |
849 | NULL, PR_FALSE); | |
850 | if (module == NULL) { | |
851 | return VCARD_EMUL_FAIL; | |
852 | } | |
853 | SECMOD_DestroyModule(module); /* free our reference, Module will still | |
854 | * be on the list. | |
855 | * until we destroy it */ | |
856 | } | |
857 | ||
858 | /* now examine all the slots, finding which should be readers */ | |
859 | /* We should control this with options. For now we mirror out any | |
860 | * removable hardware slot */ | |
861 | default_card_type = options->hw_card_type; | |
862 | default_type_params = strdup(options->hw_type_params); | |
863 | ||
864 | SECMOD_GetReadLock(module_lock); | |
865 | for (mlp = module_list; mlp; mlp = mlp->next) { | |
866 | SECMODModule *module = mlp->module; | |
867 | PRBool has_emul_slots = PR_FALSE; | |
868 | ||
869 | if (module == NULL) { | |
870 | continue; | |
871 | } | |
872 | ||
873 | for (i = 0; i < module->slotCount; i++) { | |
874 | PK11SlotInfo *slot = module->slots[i]; | |
875 | ||
876 | /* only map removable HW slots */ | |
877 | if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { | |
878 | continue; | |
879 | } | |
880 | vreader_emul = vreader_emul_new(slot, options->hw_card_type, | |
881 | options->hw_type_params); | |
882 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |
883 | vreader_emul_delete); | |
884 | vreader_add_reader(vreader); | |
885 | ||
886 | has_readers = PR_TRUE; | |
887 | has_emul_slots = PR_TRUE; | |
888 | ||
889 | if (PK11_IsPresent(slot)) { | |
890 | VCard *vcard; | |
891 | vcard = vcard_emul_mirror_card(vreader); | |
892 | vreader_insert_card(vreader, vcard); | |
893 | vcard_emul_init_series(vreader, vcard); | |
894 | vcard_free(vcard); | |
895 | } | |
896 | } | |
897 | if (has_emul_slots) { | |
898 | vcard_emul_new_event_thread(module); | |
899 | } | |
900 | } | |
901 | SECMOD_ReleaseReadLock(module_lock); | |
902 | nss_emul_init = has_readers; | |
903 | ||
904 | return VCARD_EMUL_OK; | |
905 | } | |
906 | ||
907 | /* Recreate card insert events for all readers (user should | |
908 | * deduce implied reader insert. perhaps do a reader insert as well?) | |
909 | */ | |
910 | void | |
911 | vcard_emul_replay_insertion_events(void) | |
912 | { | |
913 | VReaderListEntry *current_entry; | |
914 | VReaderListEntry *next_entry = NULL; | |
915 | VReaderList *list = vreader_get_reader_list(); | |
916 | ||
917 | for (current_entry = vreader_list_get_first(list); current_entry; | |
918 | current_entry = next_entry) { | |
919 | VReader *vreader = vreader_list_get_reader(current_entry); | |
920 | next_entry = vreader_list_get_next(current_entry); | |
921 | vreader_queue_card_event(vreader); | |
922 | } | |
923 | } | |
924 | ||
925 | /* | |
926 | * Silly little functions to help parsing our argument string | |
927 | */ | |
928 | static char * | |
929 | copy_string(const char *str, int str_len) | |
930 | { | |
931 | char *new_str; | |
932 | ||
933 | new_str = qemu_malloc(str_len+1); | |
934 | memcpy(new_str, str, str_len); | |
935 | new_str[str_len] = 0; | |
936 | return new_str; | |
937 | } | |
938 | ||
939 | static int | |
940 | count_tokens(const char *str, char token, char token_end) | |
941 | { | |
942 | int count = 0; | |
943 | ||
944 | for (; *str; str++) { | |
945 | if (*str == token) { | |
946 | count++; | |
947 | } | |
948 | if (*str == token_end) { | |
949 | break; | |
950 | } | |
951 | } | |
952 | return count; | |
953 | } | |
954 | ||
955 | static const char * | |
956 | strip(const char *str) | |
957 | { | |
685ff50f | 958 | for (; *str && isspace(*str); str++) { |
111a38b0 RR |
959 | } |
960 | return str; | |
961 | } | |
962 | ||
963 | static const char * | |
964 | find_blank(const char *str) | |
965 | { | |
685ff50f | 966 | for (; *str && !isspace(*str); str++) { |
111a38b0 RR |
967 | } |
968 | return str; | |
969 | } | |
970 | ||
971 | ||
972 | /* | |
973 | * We really want to use some existing argument parsing library here. That | |
fc27eefe | 974 | * would give us a consistent look */ |
111a38b0 RR |
975 | static VCardEmulOptions options; |
976 | #define READER_STEP 4 | |
977 | ||
978 | VCardEmulOptions * | |
979 | vcard_emul_options(const char *args) | |
980 | { | |
981 | int reader_count = 0; | |
982 | VCardEmulOptions *opts; | |
983 | char type_str[100]; | |
984 | int type_len; | |
985 | ||
986 | /* Allow the future use of allocating the options structure on the fly */ | |
987 | memcpy(&options, &default_options, sizeof(options)); | |
988 | opts = &options; | |
989 | ||
990 | do { | |
991 | args = strip(args); /* strip off the leading spaces */ | |
992 | if (*args == ',') { | |
993 | continue; | |
994 | } | |
995 | /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) | |
996 | * cert_2,cert_3...) */ | |
997 | if (strncmp(args, "soft=", 5) == 0) { | |
998 | const char *name; | |
999 | const char *vname; | |
1000 | const char *type_params; | |
1001 | VCardEmulType type; | |
1002 | int name_length, vname_length, type_params_length, count, i; | |
1003 | VirtualReaderOptions *vreaderOpt = NULL; | |
1004 | ||
1005 | args = strip(args + 5); | |
1006 | if (*args != '(') { | |
1007 | continue; | |
1008 | } | |
1009 | name = args; | |
1010 | args = strpbrk(args + 1, ",)"); | |
1011 | if (*args == 0) { | |
1012 | break; | |
1013 | } | |
1014 | if (*args == ')') { | |
1015 | args++; | |
1016 | continue; | |
1017 | } | |
1018 | args = strip(args+1); | |
1019 | name_length = args - name - 2; | |
1020 | vname = args; | |
1021 | args = strpbrk(args + 1, ",)"); | |
1022 | if (*args == 0) { | |
1023 | break; | |
1024 | } | |
1025 | if (*args == ')') { | |
1026 | args++; | |
1027 | continue; | |
1028 | } | |
1029 | vname_length = args - name - 2; | |
1030 | args = strip(args+1); | |
1031 | type_len = strpbrk(args, ",)") - args; | |
1032 | assert(sizeof(type_str) > type_len); | |
1033 | strncpy(type_str, args, type_len); | |
1034 | type_str[type_len] = 0; | |
1035 | type = vcard_emul_type_from_string(type_str); | |
1036 | args = strpbrk(args, ",)"); | |
1037 | if (*args == 0) { | |
1038 | break; | |
1039 | } | |
1040 | if (*args == ')') { | |
1041 | args++; | |
1042 | continue; | |
1043 | } | |
1044 | args = strip(args++); | |
1045 | type_params = args; | |
1046 | args = strpbrk(args + 1, ",)"); | |
1047 | if (*args == 0) { | |
1048 | break; | |
1049 | } | |
1050 | if (*args == ')') { | |
1051 | args++; | |
1052 | continue; | |
1053 | } | |
1054 | type_params_length = args - name; | |
1055 | args = strip(args++); | |
1056 | if (*args == 0) { | |
1057 | break; | |
1058 | } | |
1059 | ||
1060 | if (opts->vreader_count >= reader_count) { | |
1061 | reader_count += READER_STEP; | |
1062 | vreaderOpt = realloc(opts->vreader, | |
1063 | reader_count * sizeof(*vreaderOpt)); | |
1064 | if (vreaderOpt == NULL) { | |
1065 | return opts; /* we're done */ | |
1066 | } | |
1067 | } | |
1068 | opts->vreader = vreaderOpt; | |
1069 | vreaderOpt = &vreaderOpt[opts->vreader_count]; | |
1070 | vreaderOpt->name = copy_string(name, name_length); | |
1071 | vreaderOpt->vname = copy_string(vname, vname_length); | |
1072 | vreaderOpt->card_type = type; | |
1073 | vreaderOpt->type_params = | |
1074 | copy_string(type_params, type_params_length); | |
1075 | count = count_tokens(args, ',', ')'); | |
1076 | vreaderOpt->cert_count = count; | |
1077 | vreaderOpt->cert_name = (char **)qemu_malloc(count*sizeof(char *)); | |
1078 | for (i = 0; i < count; i++) { | |
1079 | const char *cert = args + 1; | |
1080 | args = strpbrk(args + 1, ",)"); | |
1081 | vreaderOpt->cert_name[i] = copy_string(cert, args - cert); | |
1082 | } | |
1083 | if (*args == ')') { | |
1084 | args++; | |
1085 | } | |
1086 | opts->vreader_count++; | |
1087 | /* use_hw= */ | |
1088 | } else if (strncmp(args, "use_hw=", 7) == 0) { | |
1089 | args = strip(args+7); | |
1090 | if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { | |
1091 | opts->use_hw = PR_FALSE; | |
1092 | } else { | |
1093 | opts->use_hw = PR_TRUE; | |
1094 | } | |
1095 | args = find_blank(args); | |
1096 | /* hw_type= */ | |
1097 | } else if (strncmp(args, "hw_type=", 8) == 0) { | |
1098 | args = strip(args+8); | |
1099 | opts->hw_card_type = vcard_emul_type_from_string(args); | |
1100 | args = find_blank(args); | |
1101 | /* hw_params= */ | |
1102 | } else if (strncmp(args, "hw_params=", 10) == 0) { | |
1103 | const char *params; | |
1104 | args = strip(args+10); | |
1105 | params = args; | |
1106 | args = find_blank(args); | |
1107 | opts->hw_type_params = copy_string(params, args-params); | |
1108 | /* db="/data/base/path" */ | |
1109 | } else if (strncmp(args, "db=", 3) == 0) { | |
1110 | const char *db; | |
1111 | args = strip(args+3); | |
1112 | if (*args != '"') { | |
1113 | continue; | |
1114 | } | |
1115 | args++; | |
1116 | db = args; | |
1117 | args = strpbrk(args, "\"\n"); | |
1118 | opts->nss_db = copy_string(db, args-db); | |
1119 | if (*args != 0) { | |
1120 | args++; | |
1121 | } | |
1122 | } else { | |
1123 | args = find_blank(args); | |
1124 | } | |
1125 | } while (*args != 0); | |
1126 | ||
1127 | return opts; | |
1128 | } | |
1129 | ||
1130 | void | |
1131 | vcard_emul_usage(void) | |
1132 | { | |
1133 | fprintf(stderr, | |
1134 | "emul args: comma separated list of the following arguments\n" | |
1135 | " db={nss_database} (default sql:/etc/pki/nssdb)\n" | |
1136 | " use_hw=[yes|no] (default yes)\n" | |
1137 | " hw_type={card_type_to_emulate} (default CAC)\n" | |
1138 | " hw_param={param_for_card} (default \"\")\n" | |
1139 | " soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" | |
1140 | " {cert1},{cert2},{cert3} (default none)\n" | |
1141 | "\n" | |
1142 | " {nss_database} The location of the NSS cert & key database\n" | |
1143 | " {card_type_to_emulate} What card interface to present to the guest\n" | |
1144 | " {param_for_card} Card interface specific parameters\n" | |
1145 | " {slot_name} NSS slot that contains the certs\n" | |
1146 | " {vreader_name} Virutal reader name to present to the guest\n" | |
1147 | " {certN} Nickname of the certificate n on the virtual card\n" | |
1148 | "\n" | |
1149 | "These parameters come as a single string separated by blanks or newlines." | |
1150 | "\n" | |
1151 | "Unless use_hw is set to no, all tokens that look like removable hardware\n" | |
1152 | "tokens will be presented to the guest using the emulator specified by\n" | |
1153 | "hw_type, and parameters of hw_param.\n" | |
1154 | "\n" | |
1155 | "If more one or more soft= parameters are specified, these readers will be\n" | |
1156 | "presented to the guest\n"); | |
1157 | } |