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