]>
Commit | Line | Data |
---|---|---|
111a38b0 RR |
1 | /* |
2 | * emulate the reader | |
3 | * | |
4 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
5 | * See the COPYING.LIB file in the top-level directory. | |
6 | */ | |
7 | ||
7a685896 AL |
8 | #ifdef G_LOG_DOMAIN |
9 | #undef G_LOG_DOMAIN | |
10 | #endif | |
11 | #define G_LOG_DOMAIN "libcacard" | |
12 | #include <glib.h> | |
13 | ||
111a38b0 | 14 | #include "qemu-common.h" |
1de7afc9 | 15 | #include "qemu/thread.h" |
111a38b0 RR |
16 | |
17 | #include "vcard.h" | |
18 | #include "vcard_emul.h" | |
19 | #include "card_7816.h" | |
20 | #include "vreader.h" | |
21 | #include "vevent.h" | |
7a685896 AL |
22 | #include "cac.h" /* just for debugging defines */ |
23 | ||
24 | #define LIBCACARD_LOG_DOMAIN "libcacard" | |
111a38b0 RR |
25 | |
26 | struct VReaderStruct { | |
27 | int reference_count; | |
28 | VCard *card; | |
29 | char *name; | |
30 | vreader_id_t id; | |
31 | QemuMutex lock; | |
32 | VReaderEmul *reader_private; | |
33 | VReaderEmulFree reader_private_free; | |
34 | }; | |
35 | ||
7a685896 AL |
36 | /* |
37 | * Debug helpers | |
38 | */ | |
39 | ||
40 | static const char * | |
41 | apdu_ins_to_string(int ins) | |
42 | { | |
43 | switch (ins) { | |
44 | case VCARD7816_INS_MANAGE_CHANNEL: | |
45 | return "manage channel"; | |
46 | case VCARD7816_INS_EXTERNAL_AUTHENTICATE: | |
47 | return "external authenticate"; | |
48 | case VCARD7816_INS_GET_CHALLENGE: | |
49 | return "get challenge"; | |
50 | case VCARD7816_INS_INTERNAL_AUTHENTICATE: | |
51 | return "internal authenticate"; | |
52 | case VCARD7816_INS_ERASE_BINARY: | |
53 | return "erase binary"; | |
54 | case VCARD7816_INS_READ_BINARY: | |
55 | return "read binary"; | |
56 | case VCARD7816_INS_WRITE_BINARY: | |
57 | return "write binary"; | |
58 | case VCARD7816_INS_UPDATE_BINARY: | |
59 | return "update binary"; | |
60 | case VCARD7816_INS_READ_RECORD: | |
61 | return "read record"; | |
62 | case VCARD7816_INS_WRITE_RECORD: | |
63 | return "write record"; | |
64 | case VCARD7816_INS_UPDATE_RECORD: | |
65 | return "update record"; | |
66 | case VCARD7816_INS_APPEND_RECORD: | |
67 | return "append record"; | |
68 | case VCARD7816_INS_ENVELOPE: | |
69 | return "envelope"; | |
70 | case VCARD7816_INS_PUT_DATA: | |
71 | return "put data"; | |
72 | case VCARD7816_INS_GET_DATA: | |
73 | return "get data"; | |
74 | case VCARD7816_INS_SELECT_FILE: | |
75 | return "select file"; | |
76 | case VCARD7816_INS_VERIFY: | |
77 | return "verify"; | |
78 | case VCARD7816_INS_GET_RESPONSE: | |
79 | return "get response"; | |
80 | case CAC_GET_PROPERTIES: | |
81 | return "get properties"; | |
82 | case CAC_GET_ACR: | |
83 | return "get acr"; | |
84 | case CAC_READ_BUFFER: | |
85 | return "read buffer"; | |
86 | case CAC_UPDATE_BUFFER: | |
87 | return "update buffer"; | |
88 | case CAC_SIGN_DECRYPT: | |
89 | return "sign decrypt"; | |
90 | case CAC_GET_CERTIFICATE: | |
91 | return "get certificate"; | |
92 | } | |
93 | return "unknown"; | |
94 | } | |
95 | ||
111a38b0 RR |
96 | /* manage locking */ |
97 | static inline void | |
98 | vreader_lock(VReader *reader) | |
99 | { | |
100 | qemu_mutex_lock(&reader->lock); | |
101 | } | |
102 | ||
103 | static inline void | |
104 | vreader_unlock(VReader *reader) | |
105 | { | |
106 | qemu_mutex_unlock(&reader->lock); | |
107 | } | |
108 | ||
109 | /* | |
110 | * vreader constructor | |
111 | */ | |
112 | VReader * | |
113 | vreader_new(const char *name, VReaderEmul *private, | |
114 | VReaderEmulFree private_free) | |
115 | { | |
116 | VReader *reader; | |
117 | ||
7267c094 | 118 | reader = (VReader *)g_malloc(sizeof(VReader)); |
111a38b0 RR |
119 | qemu_mutex_init(&reader->lock); |
120 | reader->reference_count = 1; | |
be168af8 | 121 | reader->name = g_strdup(name); |
111a38b0 RR |
122 | reader->card = NULL; |
123 | reader->id = (vreader_id_t)-1; | |
124 | reader->reader_private = private; | |
125 | reader->reader_private_free = private_free; | |
126 | return reader; | |
127 | } | |
128 | ||
129 | /* get a reference */ | |
130 | VReader* | |
131 | vreader_reference(VReader *reader) | |
132 | { | |
133 | if (reader == NULL) { | |
134 | return NULL; | |
135 | } | |
136 | vreader_lock(reader); | |
137 | reader->reference_count++; | |
138 | vreader_unlock(reader); | |
139 | return reader; | |
140 | } | |
141 | ||
142 | /* free a reference */ | |
143 | void | |
144 | vreader_free(VReader *reader) | |
145 | { | |
146 | if (reader == NULL) { | |
147 | return; | |
148 | } | |
149 | vreader_lock(reader); | |
150 | if (reader->reference_count-- > 1) { | |
151 | vreader_unlock(reader); | |
152 | return; | |
153 | } | |
154 | vreader_unlock(reader); | |
155 | if (reader->card) { | |
156 | vcard_free(reader->card); | |
157 | } | |
158 | if (reader->name) { | |
7267c094 | 159 | g_free(reader->name); |
111a38b0 RR |
160 | } |
161 | if (reader->reader_private_free) { | |
162 | reader->reader_private_free(reader->reader_private); | |
163 | } | |
7267c094 | 164 | g_free(reader); |
111a38b0 RR |
165 | } |
166 | ||
167 | static VCard * | |
168 | vreader_get_card(VReader *reader) | |
169 | { | |
170 | VCard *card; | |
171 | ||
172 | vreader_lock(reader); | |
173 | card = vcard_reference(reader->card); | |
174 | vreader_unlock(reader); | |
175 | return card; | |
176 | } | |
177 | ||
178 | VReaderStatus | |
179 | vreader_card_is_present(VReader *reader) | |
180 | { | |
181 | VCard *card = vreader_get_card(reader); | |
182 | ||
183 | if (card == NULL) { | |
184 | return VREADER_NO_CARD; | |
185 | } | |
186 | vcard_free(card); | |
187 | return VREADER_OK; | |
188 | } | |
189 | ||
190 | vreader_id_t | |
191 | vreader_get_id(VReader *reader) | |
192 | { | |
193 | if (reader == NULL) { | |
194 | return (vreader_id_t)-1; | |
195 | } | |
196 | return reader->id; | |
197 | } | |
198 | ||
199 | VReaderStatus | |
200 | vreader_set_id(VReader *reader, vreader_id_t id) | |
201 | { | |
202 | if (reader == NULL) { | |
203 | return VREADER_NO_CARD; | |
204 | } | |
205 | reader->id = id; | |
206 | return VREADER_OK; | |
207 | } | |
208 | ||
209 | const char * | |
210 | vreader_get_name(VReader *reader) | |
211 | { | |
212 | if (reader == NULL) { | |
213 | return NULL; | |
214 | } | |
215 | return reader->name; | |
216 | } | |
217 | ||
218 | VReaderEmul * | |
219 | vreader_get_private(VReader *reader) | |
220 | { | |
221 | return reader->reader_private; | |
222 | } | |
223 | ||
224 | static VReaderStatus | |
225 | vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) | |
226 | { | |
227 | VCard *card = vreader_get_card(reader); | |
228 | ||
229 | if (card == NULL) { | |
230 | return VREADER_NO_CARD; | |
231 | } | |
232 | /* | |
233 | * clean up our state | |
234 | */ | |
235 | vcard_reset(card, power); | |
236 | if (atr) { | |
237 | vcard_get_atr(card, atr, len); | |
238 | } | |
239 | vcard_free(card); /* free our reference */ | |
240 | return VREADER_OK; | |
241 | } | |
242 | ||
243 | VReaderStatus | |
244 | vreader_power_on(VReader *reader, unsigned char *atr, int *len) | |
245 | { | |
246 | return vreader_reset(reader, VCARD_POWER_ON, atr, len); | |
247 | } | |
248 | ||
249 | VReaderStatus | |
250 | vreader_power_off(VReader *reader) | |
251 | { | |
252 | return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); | |
253 | } | |
254 | ||
255 | ||
256 | VReaderStatus | |
257 | vreader_xfr_bytes(VReader *reader, | |
258 | unsigned char *send_buf, int send_buf_len, | |
259 | unsigned char *receive_buf, int *receive_buf_len) | |
260 | { | |
261 | VCardAPDU *apdu; | |
262 | VCardResponse *response = NULL; | |
263 | VCardStatus card_status; | |
264 | unsigned short status; | |
265 | VCard *card = vreader_get_card(reader); | |
266 | ||
267 | if (card == NULL) { | |
268 | return VREADER_NO_CARD; | |
269 | } | |
270 | ||
271 | apdu = vcard_apdu_new(send_buf, send_buf_len, &status); | |
272 | if (apdu == NULL) { | |
273 | response = vcard_make_response(status); | |
274 | card_status = VCARD_DONE; | |
275 | } else { | |
7a685896 AL |
276 | g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s\n", |
277 | __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2, | |
278 | apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins)); | |
111a38b0 | 279 | card_status = vcard_process_apdu(card, apdu, &response); |
7a685896 AL |
280 | if (response) { |
281 | g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)\n", | |
282 | __func__, response->b_status, response->b_sw1, | |
283 | response->b_sw2, response->b_len, response->b_total_len); | |
284 | } | |
111a38b0 RR |
285 | } |
286 | assert(card_status == VCARD_DONE); | |
287 | if (card_status == VCARD_DONE) { | |
288 | int size = MIN(*receive_buf_len, response->b_total_len); | |
289 | memcpy(receive_buf, response->b_data, size); | |
290 | *receive_buf_len = size; | |
291 | } | |
292 | vcard_response_delete(response); | |
293 | vcard_apdu_delete(apdu); | |
294 | vcard_free(card); /* free our reference */ | |
295 | return VREADER_OK; | |
296 | } | |
297 | ||
298 | struct VReaderListStruct { | |
299 | VReaderListEntry *head; | |
300 | VReaderListEntry *tail; | |
301 | }; | |
302 | ||
303 | struct VReaderListEntryStruct { | |
304 | VReaderListEntry *next; | |
305 | VReaderListEntry *prev; | |
306 | VReader *reader; | |
307 | }; | |
308 | ||
309 | ||
310 | static VReaderListEntry * | |
311 | vreader_list_entry_new(VReader *reader) | |
312 | { | |
313 | VReaderListEntry *new_reader_list_entry; | |
314 | ||
315 | new_reader_list_entry = (VReaderListEntry *) | |
7267c094 | 316 | g_malloc(sizeof(VReaderListEntry)); |
111a38b0 RR |
317 | new_reader_list_entry->next = NULL; |
318 | new_reader_list_entry->prev = NULL; | |
319 | new_reader_list_entry->reader = vreader_reference(reader); | |
320 | return new_reader_list_entry; | |
321 | } | |
322 | ||
323 | static void | |
324 | vreader_list_entry_delete(VReaderListEntry *entry) | |
325 | { | |
326 | if (entry == NULL) { | |
327 | return; | |
328 | } | |
329 | vreader_free(entry->reader); | |
7267c094 | 330 | g_free(entry); |
111a38b0 RR |
331 | } |
332 | ||
333 | ||
334 | static VReaderList * | |
335 | vreader_list_new(void) | |
336 | { | |
337 | VReaderList *new_reader_list; | |
338 | ||
7267c094 | 339 | new_reader_list = (VReaderList *)g_malloc(sizeof(VReaderList)); |
111a38b0 RR |
340 | new_reader_list->head = NULL; |
341 | new_reader_list->tail = NULL; | |
342 | return new_reader_list; | |
343 | } | |
344 | ||
345 | void | |
346 | vreader_list_delete(VReaderList *list) | |
347 | { | |
348 | VReaderListEntry *current_entry; | |
349 | VReaderListEntry *next_entry = NULL; | |
350 | for (current_entry = vreader_list_get_first(list); current_entry; | |
351 | current_entry = next_entry) { | |
352 | next_entry = vreader_list_get_next(current_entry); | |
353 | vreader_list_entry_delete(current_entry); | |
354 | } | |
355 | list->head = NULL; | |
356 | list->tail = NULL; | |
7267c094 | 357 | g_free(list); |
111a38b0 RR |
358 | } |
359 | ||
360 | ||
361 | VReaderListEntry * | |
362 | vreader_list_get_first(VReaderList *list) | |
363 | { | |
364 | return list ? list->head : NULL; | |
365 | } | |
366 | ||
367 | VReaderListEntry * | |
368 | vreader_list_get_next(VReaderListEntry *current) | |
369 | { | |
370 | return current ? current->next : NULL; | |
371 | } | |
372 | ||
373 | VReader * | |
374 | vreader_list_get_reader(VReaderListEntry *entry) | |
375 | { | |
376 | return entry ? vreader_reference(entry->reader) : NULL; | |
377 | } | |
378 | ||
379 | static void | |
380 | vreader_queue(VReaderList *list, VReaderListEntry *entry) | |
381 | { | |
382 | if (entry == NULL) { | |
383 | return; | |
384 | } | |
385 | entry->next = NULL; | |
386 | entry->prev = list->tail; | |
387 | if (list->head) { | |
388 | list->tail->next = entry; | |
389 | } else { | |
390 | list->head = entry; | |
391 | } | |
392 | list->tail = entry; | |
393 | } | |
394 | ||
395 | static void | |
396 | vreader_dequeue(VReaderList *list, VReaderListEntry *entry) | |
397 | { | |
398 | if (entry == NULL) { | |
399 | return; | |
400 | } | |
401 | if (entry->next == NULL) { | |
402 | list->tail = entry->prev; | |
403 | } else if (entry->prev == NULL) { | |
404 | list->head = entry->next; | |
405 | } else { | |
406 | entry->prev->next = entry->next; | |
407 | entry->next->prev = entry->prev; | |
408 | } | |
409 | if ((list->tail == NULL) || (list->head == NULL)) { | |
410 | list->head = list->tail = NULL; | |
411 | } | |
412 | entry->next = entry->prev = NULL; | |
413 | } | |
414 | ||
415 | static VReaderList *vreader_list; | |
416 | static QemuMutex vreader_list_mutex; | |
417 | ||
418 | static void | |
419 | vreader_list_init(void) | |
420 | { | |
421 | vreader_list = vreader_list_new(); | |
422 | qemu_mutex_init(&vreader_list_mutex); | |
423 | } | |
424 | ||
425 | static void | |
426 | vreader_list_lock(void) | |
427 | { | |
428 | qemu_mutex_lock(&vreader_list_mutex); | |
429 | } | |
430 | ||
431 | static void | |
432 | vreader_list_unlock(void) | |
433 | { | |
434 | qemu_mutex_unlock(&vreader_list_mutex); | |
435 | } | |
436 | ||
437 | static VReaderList * | |
438 | vreader_copy_list(VReaderList *list) | |
439 | { | |
440 | VReaderList *new_list = NULL; | |
441 | VReaderListEntry *current_entry = NULL; | |
442 | ||
443 | new_list = vreader_list_new(); | |
444 | if (new_list == NULL) { | |
445 | return NULL; | |
446 | } | |
447 | for (current_entry = vreader_list_get_first(list); current_entry; | |
448 | current_entry = vreader_list_get_next(current_entry)) { | |
449 | VReader *reader = vreader_list_get_reader(current_entry); | |
450 | VReaderListEntry *new_entry = vreader_list_entry_new(reader); | |
451 | ||
452 | vreader_free(reader); | |
453 | vreader_queue(new_list, new_entry); | |
454 | } | |
455 | return new_list; | |
456 | } | |
457 | ||
458 | VReaderList * | |
459 | vreader_get_reader_list(void) | |
460 | { | |
461 | VReaderList *new_reader_list; | |
462 | ||
463 | vreader_list_lock(); | |
464 | new_reader_list = vreader_copy_list(vreader_list); | |
465 | vreader_list_unlock(); | |
466 | return new_reader_list; | |
467 | } | |
468 | ||
469 | VReader * | |
470 | vreader_get_reader_by_id(vreader_id_t id) | |
471 | { | |
472 | VReader *reader = NULL; | |
473 | VReaderListEntry *current_entry = NULL; | |
474 | ||
475 | if (id == (vreader_id_t) -1) { | |
476 | return NULL; | |
477 | } | |
478 | ||
479 | vreader_list_lock(); | |
480 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; | |
481 | current_entry = vreader_list_get_next(current_entry)) { | |
482 | VReader *creader = vreader_list_get_reader(current_entry); | |
483 | if (creader->id == id) { | |
484 | reader = creader; | |
485 | break; | |
486 | } | |
487 | vreader_free(creader); | |
488 | } | |
489 | vreader_list_unlock(); | |
490 | return reader; | |
491 | } | |
492 | ||
493 | VReader * | |
494 | vreader_get_reader_by_name(const char *name) | |
495 | { | |
496 | VReader *reader = NULL; | |
497 | VReaderListEntry *current_entry = NULL; | |
498 | ||
499 | vreader_list_lock(); | |
500 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; | |
501 | current_entry = vreader_list_get_next(current_entry)) { | |
502 | VReader *creader = vreader_list_get_reader(current_entry); | |
503 | if (strcmp(creader->name, name) == 0) { | |
504 | reader = creader; | |
505 | break; | |
506 | } | |
507 | vreader_free(creader); | |
508 | } | |
509 | vreader_list_unlock(); | |
510 | return reader; | |
511 | } | |
512 | ||
513 | /* called from card_emul to initialize the readers */ | |
514 | VReaderStatus | |
515 | vreader_add_reader(VReader *reader) | |
516 | { | |
517 | VReaderListEntry *reader_entry; | |
518 | ||
519 | reader_entry = vreader_list_entry_new(reader); | |
520 | if (reader_entry == NULL) { | |
521 | return VREADER_OUT_OF_MEMORY; | |
522 | } | |
523 | vreader_list_lock(); | |
524 | vreader_queue(vreader_list, reader_entry); | |
525 | vreader_list_unlock(); | |
526 | vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); | |
527 | return VREADER_OK; | |
528 | } | |
529 | ||
530 | ||
531 | VReaderStatus | |
532 | vreader_remove_reader(VReader *reader) | |
533 | { | |
534 | VReaderListEntry *current_entry; | |
535 | ||
536 | vreader_list_lock(); | |
537 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; | |
538 | current_entry = vreader_list_get_next(current_entry)) { | |
539 | if (current_entry->reader == reader) { | |
540 | break; | |
541 | } | |
542 | } | |
543 | vreader_dequeue(vreader_list, current_entry); | |
544 | vreader_list_unlock(); | |
545 | vreader_list_entry_delete(current_entry); | |
546 | vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); | |
547 | return VREADER_OK; | |
548 | } | |
549 | ||
550 | /* | |
551 | * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader | |
552 | * state. Separated from vreader_insert_card to allow replaying events | |
553 | * for a given state. | |
554 | */ | |
555 | void | |
556 | vreader_queue_card_event(VReader *reader) | |
557 | { | |
558 | vevent_queue_vevent(vevent_new( | |
559 | reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, | |
560 | reader->card)); | |
561 | } | |
562 | ||
563 | /* | |
564 | * insert/remove a new card. for removal, card == NULL | |
565 | */ | |
566 | VReaderStatus | |
567 | vreader_insert_card(VReader *reader, VCard *card) | |
568 | { | |
569 | vreader_lock(reader); | |
570 | if (reader->card) { | |
571 | /* decrement reference count */ | |
572 | vcard_free(reader->card); | |
573 | reader->card = NULL; | |
574 | } | |
575 | reader->card = vcard_reference(card); | |
576 | vreader_unlock(reader); | |
577 | vreader_queue_card_event(reader); | |
578 | return VREADER_OK; | |
579 | } | |
580 | ||
581 | /* | |
582 | * initialize all the static reader structures | |
583 | */ | |
584 | void | |
585 | vreader_init(void) | |
586 | { | |
587 | vreader_list_init(); | |
588 | } | |
589 |