]>
Commit | Line | Data |
---|---|---|
585738a6 AL |
1 | /* |
2 | * CCID Card Device. Emulated card. | |
3 | * | |
4 | * Copyright (c) 2011 Red Hat. | |
5 | * Written by Alon Levy. | |
6 | * | |
8e31bf38 | 7 | * This code is licensed under the GNU LGPL, version 2 or later. |
585738a6 AL |
8 | */ |
9 | ||
10 | /* | |
11 | * It can be used to provide access to the local hardware in a non exclusive | |
12 | * way, or it can use certificates. It requires the usb-ccid bus. | |
13 | * | |
14 | * Usage 1: standard, mirror hardware reader+card: | |
15 | * qemu .. -usb -device usb-ccid -device ccid-card-emulated | |
16 | * | |
17 | * Usage 2: use certificates, no hardware required | |
18 | * one time: create the certificates: | |
19 | * for i in 1 2 3; do | |
20 | * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i | |
21 | * done | |
22 | * qemu .. -usb -device usb-ccid \ | |
23 | * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3 | |
24 | * | |
25 | * If you use a non default db for the certificates you can specify it using | |
26 | * the db parameter. | |
27 | */ | |
28 | ||
e532b2e0 | 29 | #include "qemu/osdep.h" |
0f5c642d | 30 | #include <libcacard.h> |
585738a6 | 31 | |
1de7afc9 | 32 | #include "qemu/thread.h" |
f664b882 | 33 | #include "qemu/main-loop.h" |
0b8fa32f | 34 | #include "qemu/module.h" |
47b43a1f | 35 | #include "ccid.h" |
a27bd6c7 | 36 | #include "hw/qdev-properties.h" |
cc847bfd | 37 | #include "qapi/error.h" |
585738a6 AL |
38 | |
39 | #define DPRINTF(card, lvl, fmt, ...) \ | |
40 | do {\ | |
41 | if (lvl <= card->debug) {\ | |
42 | printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ | |
43 | } \ | |
44 | } while (0) | |
45 | ||
35997599 C |
46 | |
47 | #define TYPE_EMULATED_CCID "ccid-card-emulated" | |
48 | #define EMULATED_CCID_CARD(obj) \ | |
49 | OBJECT_CHECK(EmulatedState, (obj), TYPE_EMULATED_CCID) | |
585738a6 AL |
50 | |
51 | #define BACKEND_NSS_EMULATED_NAME "nss-emulated" | |
52 | #define BACKEND_CERTIFICATES_NAME "certificates" | |
53 | ||
54 | enum { | |
55 | BACKEND_NSS_EMULATED = 1, | |
56 | BACKEND_CERTIFICATES | |
57 | }; | |
58 | ||
59 | #define DEFAULT_BACKEND BACKEND_NSS_EMULATED | |
60 | ||
61 | typedef struct EmulatedState EmulatedState; | |
62 | ||
63 | enum { | |
64 | EMUL_READER_INSERT = 0, | |
65 | EMUL_READER_REMOVE, | |
66 | EMUL_CARD_INSERT, | |
67 | EMUL_CARD_REMOVE, | |
68 | EMUL_GUEST_APDU, | |
69 | EMUL_RESPONSE_APDU, | |
70 | EMUL_ERROR, | |
71 | }; | |
72 | ||
73 | static const char *emul_event_to_string(uint32_t emul_event) | |
74 | { | |
75 | switch (emul_event) { | |
76 | case EMUL_READER_INSERT: | |
77 | return "EMUL_READER_INSERT"; | |
78 | case EMUL_READER_REMOVE: | |
79 | return "EMUL_READER_REMOVE"; | |
80 | case EMUL_CARD_INSERT: | |
81 | return "EMUL_CARD_INSERT"; | |
82 | case EMUL_CARD_REMOVE: | |
83 | return "EMUL_CARD_REMOVE"; | |
84 | case EMUL_GUEST_APDU: | |
85 | return "EMUL_GUEST_APDU"; | |
86 | case EMUL_RESPONSE_APDU: | |
87 | return "EMUL_RESPONSE_APDU"; | |
88 | case EMUL_ERROR: | |
89 | return "EMUL_ERROR"; | |
90 | } | |
91 | return "UNKNOWN"; | |
92 | } | |
93 | ||
94 | typedef struct EmulEvent { | |
95 | QSIMPLEQ_ENTRY(EmulEvent) entry; | |
96 | union { | |
97 | struct { | |
98 | uint32_t type; | |
99 | } gen; | |
100 | struct { | |
101 | uint32_t type; | |
102 | uint64_t code; | |
103 | } error; | |
104 | struct { | |
105 | uint32_t type; | |
106 | uint32_t len; | |
107 | uint8_t data[]; | |
108 | } data; | |
109 | } p; | |
110 | } EmulEvent; | |
111 | ||
112 | #define MAX_ATR_SIZE 40 | |
113 | struct EmulatedState { | |
114 | CCIDCardState base; | |
115 | uint8_t debug; | |
116 | char *backend_str; | |
117 | uint32_t backend; | |
118 | char *cert1; | |
119 | char *cert2; | |
120 | char *cert3; | |
121 | char *db; | |
122 | uint8_t atr[MAX_ATR_SIZE]; | |
123 | uint8_t atr_length; | |
b58deb34 | 124 | QSIMPLEQ_HEAD(, EmulEvent) event_list; |
585738a6 | 125 | QemuMutex event_list_mutex; |
da5361cc | 126 | QemuThread event_thread_id; |
585738a6 | 127 | VReader *reader; |
b58deb34 | 128 | QSIMPLEQ_HEAD(, EmulEvent) guest_apdu_list; |
585738a6 AL |
129 | QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ |
130 | QemuMutex handle_apdu_mutex; | |
131 | QemuCond handle_apdu_cond; | |
c1129f6b | 132 | EventNotifier notifier; |
585738a6 | 133 | int quit_apdu_thread; |
da5361cc | 134 | QemuThread apdu_thread_id; |
585738a6 AL |
135 | }; |
136 | ||
137 | static void emulated_apdu_from_guest(CCIDCardState *base, | |
138 | const uint8_t *apdu, uint32_t len) | |
139 | { | |
35997599 | 140 | EmulatedState *card = EMULATED_CCID_CARD(base); |
7267c094 | 141 | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); |
585738a6 AL |
142 | |
143 | assert(event); | |
144 | event->p.data.type = EMUL_GUEST_APDU; | |
145 | event->p.data.len = len; | |
146 | memcpy(event->p.data.data, apdu, len); | |
147 | qemu_mutex_lock(&card->vreader_mutex); | |
148 | QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); | |
149 | qemu_mutex_unlock(&card->vreader_mutex); | |
150 | qemu_mutex_lock(&card->handle_apdu_mutex); | |
151 | qemu_cond_signal(&card->handle_apdu_cond); | |
152 | qemu_mutex_unlock(&card->handle_apdu_mutex); | |
153 | } | |
154 | ||
155 | static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) | |
156 | { | |
35997599 | 157 | EmulatedState *card = EMULATED_CCID_CARD(base); |
585738a6 AL |
158 | |
159 | *len = card->atr_length; | |
160 | return card->atr; | |
161 | } | |
162 | ||
163 | static void emulated_push_event(EmulatedState *card, EmulEvent *event) | |
164 | { | |
165 | qemu_mutex_lock(&card->event_list_mutex); | |
166 | QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); | |
167 | qemu_mutex_unlock(&card->event_list_mutex); | |
c1129f6b | 168 | event_notifier_set(&card->notifier); |
585738a6 AL |
169 | } |
170 | ||
171 | static void emulated_push_type(EmulatedState *card, uint32_t type) | |
172 | { | |
98f34339 | 173 | EmulEvent *event = g_new(EmulEvent, 1); |
585738a6 AL |
174 | |
175 | assert(event); | |
176 | event->p.gen.type = type; | |
177 | emulated_push_event(card, event); | |
178 | } | |
179 | ||
180 | static void emulated_push_error(EmulatedState *card, uint64_t code) | |
181 | { | |
98f34339 | 182 | EmulEvent *event = g_new(EmulEvent, 1); |
585738a6 AL |
183 | |
184 | assert(event); | |
185 | event->p.error.type = EMUL_ERROR; | |
186 | event->p.error.code = code; | |
187 | emulated_push_event(card, event); | |
188 | } | |
189 | ||
190 | static void emulated_push_data_type(EmulatedState *card, uint32_t type, | |
191 | const uint8_t *data, uint32_t len) | |
192 | { | |
7267c094 | 193 | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len); |
585738a6 AL |
194 | |
195 | assert(event); | |
196 | event->p.data.type = type; | |
197 | event->p.data.len = len; | |
198 | memcpy(event->p.data.data, data, len); | |
199 | emulated_push_event(card, event); | |
200 | } | |
201 | ||
202 | static void emulated_push_reader_insert(EmulatedState *card) | |
203 | { | |
204 | emulated_push_type(card, EMUL_READER_INSERT); | |
205 | } | |
206 | ||
207 | static void emulated_push_reader_remove(EmulatedState *card) | |
208 | { | |
209 | emulated_push_type(card, EMUL_READER_REMOVE); | |
210 | } | |
211 | ||
212 | static void emulated_push_card_insert(EmulatedState *card, | |
213 | const uint8_t *atr, uint32_t len) | |
214 | { | |
215 | emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); | |
216 | } | |
217 | ||
218 | static void emulated_push_card_remove(EmulatedState *card) | |
219 | { | |
220 | emulated_push_type(card, EMUL_CARD_REMOVE); | |
221 | } | |
222 | ||
223 | static void emulated_push_response_apdu(EmulatedState *card, | |
224 | const uint8_t *apdu, uint32_t len) | |
225 | { | |
226 | emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); | |
227 | } | |
228 | ||
229 | #define APDU_BUF_SIZE 270 | |
230 | static void *handle_apdu_thread(void* arg) | |
231 | { | |
232 | EmulatedState *card = arg; | |
233 | uint8_t recv_data[APDU_BUF_SIZE]; | |
234 | int recv_len; | |
235 | VReaderStatus reader_status; | |
236 | EmulEvent *event; | |
237 | ||
238 | while (1) { | |
239 | qemu_mutex_lock(&card->handle_apdu_mutex); | |
240 | qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); | |
241 | qemu_mutex_unlock(&card->handle_apdu_mutex); | |
242 | if (card->quit_apdu_thread) { | |
243 | card->quit_apdu_thread = 0; /* debugging */ | |
244 | break; | |
245 | } | |
246 | qemu_mutex_lock(&card->vreader_mutex); | |
247 | while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) { | |
248 | event = QSIMPLEQ_FIRST(&card->guest_apdu_list); | |
249 | assert((unsigned long)event > 1000); | |
250 | QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); | |
251 | if (event->p.data.type != EMUL_GUEST_APDU) { | |
252 | DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); | |
7267c094 | 253 | g_free(event); |
585738a6 AL |
254 | continue; |
255 | } | |
256 | if (card->reader == NULL) { | |
257 | DPRINTF(card, 1, "reader is NULL\n"); | |
7267c094 | 258 | g_free(event); |
585738a6 AL |
259 | continue; |
260 | } | |
261 | recv_len = sizeof(recv_data); | |
262 | reader_status = vreader_xfr_bytes(card->reader, | |
263 | event->p.data.data, event->p.data.len, | |
264 | recv_data, &recv_len); | |
265 | DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); | |
266 | if (reader_status == VREADER_OK) { | |
267 | emulated_push_response_apdu(card, recv_data, recv_len); | |
268 | } else { | |
269 | emulated_push_error(card, reader_status); | |
270 | } | |
7267c094 | 271 | g_free(event); |
585738a6 AL |
272 | } |
273 | qemu_mutex_unlock(&card->vreader_mutex); | |
274 | } | |
585738a6 AL |
275 | return NULL; |
276 | } | |
277 | ||
278 | static void *event_thread(void *arg) | |
279 | { | |
280 | int atr_len = MAX_ATR_SIZE; | |
281 | uint8_t atr[MAX_ATR_SIZE]; | |
282 | VEvent *event = NULL; | |
283 | EmulatedState *card = arg; | |
284 | ||
285 | while (1) { | |
286 | const char *reader_name; | |
287 | ||
288 | event = vevent_wait_next_vevent(); | |
289 | if (event == NULL || event->type == VEVENT_LAST) { | |
290 | break; | |
291 | } | |
292 | if (event->type != VEVENT_READER_INSERT) { | |
293 | if (card->reader == NULL && event->reader != NULL) { | |
294 | /* Happens after device_add followed by card remove or insert. | |
295 | * XXX: create synthetic add_reader events if vcard_emul_init | |
296 | * already called, which happens if device_del and device_add | |
297 | * are called */ | |
298 | card->reader = vreader_reference(event->reader); | |
299 | } else { | |
300 | if (event->reader != card->reader) { | |
301 | fprintf(stderr, | |
302 | "ERROR: wrong reader: quiting event_thread\n"); | |
303 | break; | |
304 | } | |
305 | } | |
306 | } | |
307 | switch (event->type) { | |
308 | case VEVENT_READER_INSERT: | |
309 | /* TODO: take a specific reader. i.e. track which reader | |
310 | * we are seeing here, check it is the one we want (the first, | |
311 | * or by a particular name), and ignore if we don't want it. | |
312 | */ | |
313 | reader_name = vreader_get_name(event->reader); | |
314 | if (card->reader != NULL) { | |
315 | DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", | |
316 | vreader_get_name(card->reader), reader_name); | |
317 | qemu_mutex_lock(&card->vreader_mutex); | |
318 | vreader_free(card->reader); | |
319 | qemu_mutex_unlock(&card->vreader_mutex); | |
320 | emulated_push_reader_remove(card); | |
321 | } | |
322 | qemu_mutex_lock(&card->vreader_mutex); | |
323 | DPRINTF(card, 2, "READER INSERT %s\n", reader_name); | |
324 | card->reader = vreader_reference(event->reader); | |
325 | qemu_mutex_unlock(&card->vreader_mutex); | |
326 | emulated_push_reader_insert(card); | |
327 | break; | |
328 | case VEVENT_READER_REMOVE: | |
329 | DPRINTF(card, 2, " READER REMOVE: %s\n", | |
330 | vreader_get_name(event->reader)); | |
331 | qemu_mutex_lock(&card->vreader_mutex); | |
332 | vreader_free(card->reader); | |
333 | card->reader = NULL; | |
334 | qemu_mutex_unlock(&card->vreader_mutex); | |
335 | emulated_push_reader_remove(card); | |
336 | break; | |
337 | case VEVENT_CARD_INSERT: | |
338 | /* get the ATR (intended as a response to a power on from the | |
339 | * reader */ | |
340 | atr_len = MAX_ATR_SIZE; | |
341 | vreader_power_on(event->reader, atr, &atr_len); | |
342 | card->atr_length = (uint8_t)atr_len; | |
343 | DPRINTF(card, 2, " CARD INSERT\n"); | |
344 | emulated_push_card_insert(card, atr, atr_len); | |
345 | break; | |
346 | case VEVENT_CARD_REMOVE: | |
347 | DPRINTF(card, 2, " CARD REMOVE\n"); | |
348 | emulated_push_card_remove(card); | |
349 | break; | |
350 | case VEVENT_LAST: /* quit */ | |
351 | vevent_delete(event); | |
352 | return NULL; | |
353 | break; | |
354 | default: | |
355 | break; | |
356 | } | |
357 | vevent_delete(event); | |
358 | } | |
359 | return NULL; | |
360 | } | |
361 | ||
c1129f6b | 362 | static void card_event_handler(EventNotifier *notifier) |
585738a6 | 363 | { |
c1129f6b | 364 | EmulatedState *card = container_of(notifier, EmulatedState, notifier); |
585738a6 | 365 | EmulEvent *event, *next; |
585738a6 | 366 | |
c1129f6b | 367 | event_notifier_test_and_clear(&card->notifier); |
585738a6 AL |
368 | qemu_mutex_lock(&card->event_list_mutex); |
369 | QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { | |
370 | DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); | |
371 | switch (event->p.gen.type) { | |
372 | case EMUL_RESPONSE_APDU: | |
373 | ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, | |
374 | event->p.data.len); | |
375 | break; | |
376 | case EMUL_READER_INSERT: | |
377 | ccid_card_ccid_attach(&card->base); | |
378 | break; | |
379 | case EMUL_READER_REMOVE: | |
380 | ccid_card_ccid_detach(&card->base); | |
381 | break; | |
382 | case EMUL_CARD_INSERT: | |
383 | assert(event->p.data.len <= MAX_ATR_SIZE); | |
384 | card->atr_length = event->p.data.len; | |
385 | memcpy(card->atr, event->p.data.data, card->atr_length); | |
386 | ccid_card_card_inserted(&card->base); | |
387 | break; | |
388 | case EMUL_CARD_REMOVE: | |
389 | ccid_card_card_removed(&card->base); | |
390 | break; | |
391 | case EMUL_ERROR: | |
392 | ccid_card_card_error(&card->base, event->p.error.code); | |
393 | break; | |
394 | default: | |
395 | DPRINTF(card, 2, "unexpected event\n"); | |
396 | break; | |
397 | } | |
7267c094 | 398 | g_free(event); |
585738a6 AL |
399 | } |
400 | QSIMPLEQ_INIT(&card->event_list); | |
401 | qemu_mutex_unlock(&card->event_list_mutex); | |
402 | } | |
403 | ||
cc847bfd | 404 | static int init_event_notifier(EmulatedState *card, Error **errp) |
585738a6 | 405 | { |
c1129f6b | 406 | if (event_notifier_init(&card->notifier, false) < 0) { |
cc847bfd | 407 | error_setg(errp, "ccid-card-emul: event notifier creation failed"); |
585738a6 AL |
408 | return -1; |
409 | } | |
d6da1e9e | 410 | event_notifier_set_handler(&card->notifier, card_event_handler); |
585738a6 AL |
411 | return 0; |
412 | } | |
413 | ||
ca1d4102 LQ |
414 | static void clean_event_notifier(EmulatedState *card) |
415 | { | |
416 | event_notifier_set_handler(&card->notifier, NULL); | |
417 | event_notifier_cleanup(&card->notifier); | |
418 | } | |
419 | ||
585738a6 AL |
420 | #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" |
421 | #define CERTIFICATES_ARGS_TEMPLATE\ | |
422 | "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" | |
423 | ||
424 | static int wrap_vcard_emul_init(VCardEmulOptions *options) | |
425 | { | |
426 | static int called; | |
427 | static int options_was_null; | |
428 | ||
429 | if (called) { | |
430 | if ((options == NULL) != options_was_null) { | |
431 | printf("%s: warning: running emulated with certificates" | |
432 | " and emulated side by side is not supported\n", | |
433 | __func__); | |
434 | return VCARD_EMUL_FAIL; | |
435 | } | |
436 | vcard_emul_replay_insertion_events(); | |
437 | return VCARD_EMUL_OK; | |
438 | } | |
439 | options_was_null = (options == NULL); | |
440 | called = 1; | |
441 | return vcard_emul_init(options); | |
442 | } | |
443 | ||
444 | static int emulated_initialize_vcard_from_certificates(EmulatedState *card) | |
445 | { | |
446 | char emul_args[200]; | |
447 | VCardEmulOptions *options = NULL; | |
448 | ||
449 | snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, | |
450 | card->db ? card->db : CERTIFICATES_DEFAULT_DB, | |
451 | card->cert1, card->cert2, card->cert3); | |
452 | options = vcard_emul_options(emul_args); | |
453 | if (options == NULL) { | |
454 | printf("%s: warning: not using certificates due to" | |
455 | " initialization error\n", __func__); | |
456 | } | |
457 | return wrap_vcard_emul_init(options); | |
458 | } | |
459 | ||
460 | typedef struct EnumTable { | |
461 | const char *name; | |
462 | uint32_t value; | |
463 | } EnumTable; | |
464 | ||
d18c7117 | 465 | static const EnumTable backend_enum_table[] = { |
585738a6 AL |
466 | {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, |
467 | {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, | |
468 | {NULL, 0}, | |
469 | }; | |
470 | ||
471 | static uint32_t parse_enumeration(char *str, | |
d18c7117 | 472 | const EnumTable *table, uint32_t not_found_value) |
585738a6 AL |
473 | { |
474 | uint32_t ret = not_found_value; | |
475 | ||
d0ebd788 MAL |
476 | if (str == NULL) |
477 | return 0; | |
478 | ||
585738a6 AL |
479 | while (table->name != NULL) { |
480 | if (strcmp(table->name, str) == 0) { | |
481 | ret = table->value; | |
482 | break; | |
483 | } | |
484 | table++; | |
485 | } | |
486 | return ret; | |
487 | } | |
488 | ||
cc847bfd | 489 | static void emulated_realize(CCIDCardState *base, Error **errp) |
585738a6 | 490 | { |
35997599 | 491 | EmulatedState *card = EMULATED_CCID_CARD(base); |
585738a6 | 492 | VCardEmulError ret; |
d18c7117 | 493 | const EnumTable *ptable; |
585738a6 AL |
494 | |
495 | QSIMPLEQ_INIT(&card->event_list); | |
496 | QSIMPLEQ_INIT(&card->guest_apdu_list); | |
497 | qemu_mutex_init(&card->event_list_mutex); | |
498 | qemu_mutex_init(&card->vreader_mutex); | |
499 | qemu_mutex_init(&card->handle_apdu_mutex); | |
500 | qemu_cond_init(&card->handle_apdu_cond); | |
501 | card->reader = NULL; | |
502 | card->quit_apdu_thread = 0; | |
cc847bfd | 503 | if (init_event_notifier(card, errp) < 0) { |
7dea29e4 | 504 | goto out1; |
585738a6 | 505 | } |
ae12e3a6 CR |
506 | |
507 | card->backend = 0; | |
508 | if (card->backend_str) { | |
509 | card->backend = parse_enumeration(card->backend_str, | |
510 | backend_enum_table, 0); | |
511 | } | |
512 | ||
585738a6 | 513 | if (card->backend == 0) { |
cc847bfd | 514 | error_setg(errp, "backend must be one of:"); |
585738a6 | 515 | for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { |
cc847bfd | 516 | error_append_hint(errp, "%s\n", ptable->name); |
585738a6 | 517 | } |
7dea29e4 | 518 | goto out2; |
585738a6 AL |
519 | } |
520 | ||
521 | /* TODO: a passthru backened that works on local machine. third card type?*/ | |
522 | if (card->backend == BACKEND_CERTIFICATES) { | |
523 | if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { | |
524 | ret = emulated_initialize_vcard_from_certificates(card); | |
525 | } else { | |
cc847bfd MZ |
526 | error_setg(errp, "%s: you must provide all three certs for" |
527 | " certificates backend", TYPE_EMULATED_CCID); | |
7dea29e4 | 528 | goto out2; |
585738a6 AL |
529 | } |
530 | } else { | |
531 | if (card->backend != BACKEND_NSS_EMULATED) { | |
cc847bfd MZ |
532 | error_setg(errp, "%s: bad backend specified. The options are:%s" |
533 | " (default), %s.", TYPE_EMULATED_CCID, | |
534 | BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME); | |
7dea29e4 | 535 | goto out2; |
585738a6 AL |
536 | } |
537 | if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { | |
cc847bfd MZ |
538 | error_setg(errp, "%s: unexpected cert parameters to nss emulated " |
539 | "backend", TYPE_EMULATED_CCID); | |
7dea29e4 | 540 | goto out2; |
585738a6 AL |
541 | } |
542 | /* default to mirroring the local hardware readers */ | |
543 | ret = wrap_vcard_emul_init(NULL); | |
544 | } | |
545 | if (ret != VCARD_EMUL_OK) { | |
cc847bfd | 546 | error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID); |
7dea29e4 | 547 | goto out2; |
585738a6 | 548 | } |
4900116e DDAG |
549 | qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, |
550 | card, QEMU_THREAD_JOINABLE); | |
551 | qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread, | |
552 | card, QEMU_THREAD_JOINABLE); | |
7dea29e4 | 553 | |
3fd2092f DB |
554 | return; |
555 | ||
7dea29e4 LQ |
556 | out2: |
557 | clean_event_notifier(card); | |
558 | out1: | |
559 | qemu_cond_destroy(&card->handle_apdu_cond); | |
560 | qemu_mutex_destroy(&card->handle_apdu_mutex); | |
561 | qemu_mutex_destroy(&card->vreader_mutex); | |
562 | qemu_mutex_destroy(&card->event_list_mutex); | |
585738a6 AL |
563 | } |
564 | ||
80ae8654 | 565 | static void emulated_unrealize(CCIDCardState *base, Error **errp) |
585738a6 | 566 | { |
35997599 | 567 | EmulatedState *card = EMULATED_CCID_CARD(base); |
585738a6 AL |
568 | VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); |
569 | ||
570 | vevent_queue_vevent(vevent); /* stop vevent thread */ | |
da5361cc PB |
571 | qemu_thread_join(&card->event_thread_id); |
572 | ||
585738a6 AL |
573 | card->quit_apdu_thread = 1; /* stop handle_apdu thread */ |
574 | qemu_cond_signal(&card->handle_apdu_cond); | |
da5361cc PB |
575 | qemu_thread_join(&card->apdu_thread_id); |
576 | ||
ca1d4102 | 577 | clean_event_notifier(card); |
da5361cc | 578 | /* threads exited, can destroy all condvars/mutexes */ |
585738a6 | 579 | qemu_cond_destroy(&card->handle_apdu_cond); |
585738a6 AL |
580 | qemu_mutex_destroy(&card->handle_apdu_mutex); |
581 | qemu_mutex_destroy(&card->vreader_mutex); | |
582 | qemu_mutex_destroy(&card->event_list_mutex); | |
585738a6 AL |
583 | } |
584 | ||
39bffca2 AL |
585 | static Property emulated_card_properties[] = { |
586 | DEFINE_PROP_STRING("backend", EmulatedState, backend_str), | |
587 | DEFINE_PROP_STRING("cert1", EmulatedState, cert1), | |
588 | DEFINE_PROP_STRING("cert2", EmulatedState, cert2), | |
589 | DEFINE_PROP_STRING("cert3", EmulatedState, cert3), | |
590 | DEFINE_PROP_STRING("db", EmulatedState, db), | |
591 | DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), | |
592 | DEFINE_PROP_END_OF_LIST(), | |
593 | }; | |
594 | ||
ba7c0520 AL |
595 | static void emulated_class_initfn(ObjectClass *klass, void *data) |
596 | { | |
39bffca2 | 597 | DeviceClass *dc = DEVICE_CLASS(klass); |
ba7c0520 AL |
598 | CCIDCardClass *cc = CCID_CARD_CLASS(klass); |
599 | ||
cc847bfd | 600 | cc->realize = emulated_realize; |
80ae8654 | 601 | cc->unrealize = emulated_unrealize; |
ba7c0520 AL |
602 | cc->get_atr = emulated_get_atr; |
603 | cc->apdu_from_guest = emulated_apdu_from_guest; | |
125ee0ed | 604 | set_bit(DEVICE_CATEGORY_INPUT, dc->categories); |
39bffca2 AL |
605 | dc->desc = "emulated smartcard"; |
606 | dc->props = emulated_card_properties; | |
ba7c0520 AL |
607 | } |
608 | ||
8c43a6f0 | 609 | static const TypeInfo emulated_card_info = { |
35997599 | 610 | .name = TYPE_EMULATED_CCID, |
39bffca2 AL |
611 | .parent = TYPE_CCID_CARD, |
612 | .instance_size = sizeof(EmulatedState), | |
613 | .class_init = emulated_class_initfn, | |
585738a6 AL |
614 | }; |
615 | ||
83f7d43a | 616 | static void ccid_card_emulated_register_types(void) |
585738a6 | 617 | { |
39bffca2 | 618 | type_register_static(&emulated_card_info); |
585738a6 AL |
619 | } |
620 | ||
83f7d43a | 621 | type_init(ccid_card_emulated_register_types) |