2 * Tester for VSCARD protocol, client side.
4 * Can be used with ccid-card-passthru.
6 * Copyright (c) 2011 Red Hat.
7 * Written by Alon Levy.
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.
18 #include "qemu-common.h"
19 #include "qemu/thread.h"
20 #include "qemu/sockets.h"
22 #include "vscard_common.h"
25 #include "vcard_emul.h"
36 for (i
= 0; i
< nSize
; i
++) {
37 printf("%02X ", arrBytes
[i
]);
44 printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
48 printf(" -p use passthrough mode\n");
55 static GIOChannel
*channel_socket
;
56 static GByteArray
*socket_to_send
;
57 static QemuMutex socket_to_send_lock
;
58 static guint socket_tag
;
61 update_socket_watch(void);
64 do_socket_send(GIOChannel
*source
,
65 GIOCondition condition
,
71 g_return_val_if_fail(socket_to_send
->len
!= 0, FALSE
);
72 g_return_val_if_fail(condition
& G_IO_OUT
, FALSE
);
74 g_io_channel_write_chars(channel_socket
,
75 (gchar
*)socket_to_send
->data
, socket_to_send
->len
, &bw
, &err
);
77 g_error("Error while sending socket %s", err
->message
);
80 g_byte_array_remove_range(socket_to_send
, 0, bw
);
82 if (socket_to_send
->len
== 0) {
83 update_socket_watch();
90 socket_prepare_sending(gpointer user_data
)
92 update_socket_watch();
104 VSCMsgHeader mhHeader
;
106 qemu_mutex_lock(&socket_to_send_lock
);
109 printf("sending type=%d id=%u, len =%u (0x%x)\n",
110 type
, reader_id
, length
, length
);
113 mhHeader
.type
= htonl(type
);
114 mhHeader
.reader_id
= 0;
115 mhHeader
.length
= htonl(length
);
116 g_byte_array_append(socket_to_send
, (guint8
*)&mhHeader
, sizeof(mhHeader
));
117 g_byte_array_append(socket_to_send
, (guint8
*)msg
, length
);
118 g_idle_add(socket_prepare_sending
, NULL
);
120 qemu_mutex_unlock(&socket_to_send_lock
);
125 static VReader
*pending_reader
;
126 static QemuMutex pending_reader_lock
;
127 static QemuCond pending_reader_condition
;
129 #define MAX_ATR_LEN 40
131 event_thread(void *arg
)
133 unsigned char atr
[MAX_ATR_LEN
];
136 unsigned int reader_id
;
140 const char *reader_name
;
142 event
= vevent_wait_next_vevent();
146 reader_id
= vreader_get_id(event
->reader
);
147 if (reader_id
== VSCARD_UNDEFINED_READER_ID
&&
148 event
->type
!= VEVENT_READER_INSERT
) {
149 /* ignore events from readers qemu has rejected */
150 /* if qemu is still deciding on this reader, wait to see if need to
151 * forward this event */
152 qemu_mutex_lock(&pending_reader_lock
);
153 if (!pending_reader
|| (pending_reader
!= event
->reader
)) {
154 /* wasn't for a pending reader, this reader has already been
155 * rejected by qemu */
156 qemu_mutex_unlock(&pending_reader_lock
);
157 vevent_delete(event
);
160 /* this reader hasn't been told its status from qemu yet, wait for
162 while (pending_reader
!= NULL
) {
163 qemu_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
165 qemu_mutex_unlock(&pending_reader_lock
);
166 /* now recheck the id */
167 reader_id
= vreader_get_id(event
->reader
);
168 if (reader_id
== VSCARD_UNDEFINED_READER_ID
) {
169 /* this reader was rejected */
170 vevent_delete(event
);
173 /* reader was accepted, now forward the event */
175 switch (event
->type
) {
176 case VEVENT_READER_INSERT
:
177 /* tell qemu to insert a new CCID reader */
178 /* wait until qemu has responded to our first reader insert
179 * before we send a second. That way we won't confuse the responses
181 qemu_mutex_lock(&pending_reader_lock
);
182 while (pending_reader
!= NULL
) {
183 qemu_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
185 pending_reader
= vreader_reference(event
->reader
);
186 qemu_mutex_unlock(&pending_reader_lock
);
187 reader_name
= vreader_get_name(event
->reader
);
189 printf(" READER INSERT: %s\n", reader_name
);
191 send_msg(VSC_ReaderAdd
,
192 reader_id
, /* currerntly VSCARD_UNDEFINED_READER_ID */
193 NULL
, 0 /* TODO reader_name, strlen(reader_name) */);
195 case VEVENT_READER_REMOVE
:
196 /* future, tell qemu that an old CCID reader has been removed */
198 printf(" READER REMOVE: %u\n", reader_id
);
200 send_msg(VSC_ReaderRemove
, reader_id
, NULL
, 0);
202 case VEVENT_CARD_INSERT
:
203 /* get the ATR (intended as a response to a power on from the
205 atr_len
= MAX_ATR_LEN
;
206 vreader_power_on(event
->reader
, atr
, &atr_len
);
207 /* ATR call functions as a Card Insert event */
209 printf(" CARD INSERT %u: ", reader_id
);
210 print_byte_array(atr
, atr_len
);
212 send_msg(VSC_ATR
, reader_id
, atr
, atr_len
);
214 case VEVENT_CARD_REMOVE
:
217 printf(" CARD REMOVE %u:\n", reader_id
);
219 send_msg(VSC_CardRemove
, reader_id
, NULL
, 0);
224 vevent_delete(event
);
231 get_id_from_string(char *string
, unsigned int default_id
)
233 unsigned int id
= atoi(string
);
235 /* don't accidentally swith to zero because no numbers have been supplied */
236 if ((id
== 0) && *string
!= '0') {
243 on_host_init(VSCMsgHeader
*mhHeader
, VSCMsgInit
*incoming
)
245 uint32_t *capabilities
= (incoming
->capabilities
);
246 int num_capabilities
=
247 1 + ((mhHeader
->length
- sizeof(VSCMsgInit
)) / sizeof(uint32_t));
249 QemuThread thread_id
;
251 incoming
->version
= ntohl(incoming
->version
);
252 if (incoming
->version
!= VSCARD_VERSION
) {
254 printf("warning: host has version %d, we have %d\n",
255 verbose
, VSCARD_VERSION
);
258 if (incoming
->magic
!= VSCARD_MAGIC
) {
259 printf("unexpected magic: got %d, expected %d\n",
260 incoming
->magic
, VSCARD_MAGIC
);
263 for (i
= 0 ; i
< num_capabilities
; ++i
) {
264 capabilities
[i
] = ntohl(capabilities
[i
]);
266 /* Future: check capabilities */
267 /* remove whatever reader might be left in qemu,
268 * in case of an unclean previous exit. */
269 send_msg(VSC_ReaderRemove
, VSCARD_MINIMAL_READER_ID
, NULL
, 0);
270 /* launch the event_thread. This will trigger reader adds for all the
271 * existing readers */
272 qemu_thread_create(&thread_id
, "vsc/event", event_thread
, NULL
, 0);
282 #define APDUBufSize 270
285 do_socket_read(GIOChannel
*source
,
286 GIOCondition condition
,
292 uint8_t pbRecvBuffer
[APDUBufSize
];
293 static uint8_t pbSendBuffer
[APDUBufSize
];
294 VReaderStatus reader_status
;
295 VReader
*reader
= NULL
;
296 static VSCMsgHeader mhHeader
;
297 VSCMsgError
*error_msg
;
301 static gsize br
, to_read
;
302 static int state
= STATE_HEADER
;
304 if (state
== STATE_HEADER
&& to_read
== 0) {
305 buf
= (gchar
*)&mhHeader
;
306 to_read
= sizeof(mhHeader
);
310 g_io_channel_read_chars(source
, (gchar
*)buf
, to_read
, &br
, &err
);
312 g_error("error while reading: %s", err
->message
);
321 if (state
== STATE_HEADER
) {
322 mhHeader
.type
= ntohl(mhHeader
.type
);
323 mhHeader
.reader_id
= ntohl(mhHeader
.reader_id
);
324 mhHeader
.length
= ntohl(mhHeader
.length
);
326 printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
327 mhHeader
.type
, mhHeader
.reader_id
, mhHeader
.length
,
330 switch (mhHeader
.type
) {
335 buf
= (gchar
*)pbSendBuffer
;
336 to_read
= mhHeader
.length
;
337 state
= STATE_MESSAGE
;
340 fprintf(stderr
, "Unexpected message of type 0x%X\n", mhHeader
.type
);
345 if (state
== STATE_MESSAGE
) {
346 switch (mhHeader
.type
) {
349 printf(" recv APDU: ");
350 print_byte_array(pbSendBuffer
, mhHeader
.length
);
352 /* Transmit received APDU */
353 dwSendLength
= mhHeader
.length
;
354 dwRecvLength
= sizeof(pbRecvBuffer
);
355 reader
= vreader_get_reader_by_id(mhHeader
.reader_id
);
356 reader_status
= vreader_xfr_bytes(reader
,
357 pbSendBuffer
, dwSendLength
,
358 pbRecvBuffer
, &dwRecvLength
);
359 if (reader_status
== VREADER_OK
) {
360 mhHeader
.length
= dwRecvLength
;
362 printf(" send response: ");
363 print_byte_array(pbRecvBuffer
, mhHeader
.length
);
365 send_msg(VSC_APDU
, mhHeader
.reader_id
,
366 pbRecvBuffer
, dwRecvLength
);
368 rv
= reader_status
; /* warning: not meaningful */
369 send_msg(VSC_Error
, mhHeader
.reader_id
, &rv
, sizeof(uint32_t));
371 vreader_free(reader
);
372 reader
= NULL
; /* we've freed it, don't use it by accident
376 /* TODO: actually flush */
377 send_msg(VSC_FlushComplete
, mhHeader
.reader_id
, NULL
, 0);
380 error_msg
= (VSCMsgError
*) pbSendBuffer
;
381 if (error_msg
->code
== VSC_SUCCESS
) {
382 qemu_mutex_lock(&pending_reader_lock
);
383 if (pending_reader
) {
384 vreader_set_id(pending_reader
, mhHeader
.reader_id
);
385 vreader_free(pending_reader
);
386 pending_reader
= NULL
;
387 qemu_cond_signal(&pending_reader_condition
);
389 qemu_mutex_unlock(&pending_reader_lock
);
392 printf("warning: qemu refused to add reader\n");
393 if (error_msg
->code
== VSC_CANNOT_ADD_MORE_READERS
) {
394 /* clear pending reader, qemu can't handle any more */
395 qemu_mutex_lock(&pending_reader_lock
);
396 if (pending_reader
) {
397 pending_reader
= NULL
;
398 /* make sure the event loop doesn't hang */
399 qemu_cond_signal(&pending_reader_condition
);
401 qemu_mutex_unlock(&pending_reader_lock
);
405 if (on_host_init(&mhHeader
, (VSCMsgInit
*)pbSendBuffer
) < 0) {
410 g_assert_not_reached();
414 state
= STATE_HEADER
;
422 do_socket(GIOChannel
*source
,
423 GIOCondition condition
,
426 /* not sure if two watches work well with a single win32 sources */
427 if (condition
& G_IO_OUT
) {
428 if (!do_socket_send(source
, condition
, data
)) {
433 if (condition
& G_IO_IN
) {
434 if (!do_socket_read(source
, condition
, data
)) {
443 update_socket_watch(void)
445 gboolean out
= socket_to_send
->len
> 0;
447 if (socket_tag
!= 0) {
448 g_source_remove(socket_tag
);
451 socket_tag
= g_io_add_watch(channel_socket
,
452 G_IO_IN
| (out
? G_IO_OUT
: 0), do_socket
, NULL
);
456 do_command(GIOChannel
*source
,
457 GIOCondition condition
,
461 VCardEmulError error
;
462 static unsigned int default_reader_id
;
463 unsigned int reader_id
;
464 VReader
*reader
= NULL
;
467 g_assert(condition
& G_IO_IN
);
469 reader_id
= default_reader_id
;
470 g_io_channel_read_line(source
, &string
, NULL
, NULL
, &err
);
472 g_error("Error while reading command: %s", err
->message
);
475 if (string
!= NULL
) {
476 if (strncmp(string
, "exit", 4) == 0) {
477 /* remove all the readers */
478 VReaderList
*list
= vreader_get_reader_list();
479 VReaderListEntry
*reader_entry
;
480 printf("Active Readers:\n");
481 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
482 reader_entry
= vreader_list_get_next(reader_entry
)) {
483 VReader
*reader
= vreader_list_get_reader(reader_entry
);
484 vreader_id_t reader_id
;
485 reader_id
= vreader_get_id(reader
);
486 if (reader_id
== -1) {
489 /* be nice and signal card removal first (qemu probably should
491 if (vreader_card_is_present(reader
) == VREADER_OK
) {
492 send_msg(VSC_CardRemove
, reader_id
, NULL
, 0);
494 send_msg(VSC_ReaderRemove
, reader_id
, NULL
, 0);
497 } else if (strncmp(string
, "insert", 6) == 0) {
498 if (string
[6] == ' ') {
499 reader_id
= get_id_from_string(&string
[7], reader_id
);
501 reader
= vreader_get_reader_by_id(reader_id
);
502 if (reader
!= NULL
) {
503 error
= vcard_emul_force_card_insert(reader
);
504 printf("insert %s, returned %d\n",
505 vreader_get_name(reader
), error
);
507 printf("no reader by id %u found\n", reader_id
);
509 } else if (strncmp(string
, "remove", 6) == 0) {
510 if (string
[6] == ' ') {
511 reader_id
= get_id_from_string(&string
[7], reader_id
);
513 reader
= vreader_get_reader_by_id(reader_id
);
514 if (reader
!= NULL
) {
515 error
= vcard_emul_force_card_remove(reader
);
516 printf("remove %s, returned %d\n",
517 vreader_get_name(reader
), error
);
519 printf("no reader by id %u found\n", reader_id
);
521 } else if (strncmp(string
, "select", 6) == 0) {
522 if (string
[6] == ' ') {
523 reader_id
= get_id_from_string(&string
[7],
524 VSCARD_UNDEFINED_READER_ID
);
526 if (reader_id
!= VSCARD_UNDEFINED_READER_ID
) {
527 reader
= vreader_get_reader_by_id(reader_id
);
530 printf("Selecting reader %u, %s\n", reader_id
,
531 vreader_get_name(reader
));
532 default_reader_id
= reader_id
;
534 printf("Reader with id %u not found\n", reader_id
);
536 } else if (strncmp(string
, "debug", 5) == 0) {
537 if (string
[5] == ' ') {
538 verbose
= get_id_from_string(&string
[6], 0);
540 printf("debug level = %d\n", verbose
);
541 } else if (strncmp(string
, "list", 4) == 0) {
542 VReaderList
*list
= vreader_get_reader_list();
543 VReaderListEntry
*reader_entry
;
544 printf("Active Readers:\n");
545 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
546 reader_entry
= vreader_list_get_next(reader_entry
)) {
547 VReader
*reader
= vreader_list_get_reader(reader_entry
);
548 vreader_id_t reader_id
;
549 reader_id
= vreader_get_id(reader
);
550 if (reader_id
== -1) {
553 printf("%3u %s %s\n", reader_id
,
554 vreader_card_is_present(reader
) == VREADER_OK
?
555 "CARD_PRESENT" : " ",
556 vreader_get_name(reader
));
558 printf("Inactive Readers:\n");
559 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
560 reader_entry
= vreader_list_get_next(reader_entry
)) {
561 VReader
*reader
= vreader_list_get_reader(reader_entry
);
562 vreader_id_t reader_id
;
563 reader_id
= vreader_get_id(reader
);
564 if (reader_id
!= -1) {
568 printf("INA %s %s\n",
569 vreader_card_is_present(reader
) == VREADER_OK
?
570 "CARD_PRESENT" : " ",
571 vreader_get_name(reader
));
573 vreader_list_delete(list
);
574 } else if (*string
!= 0) {
575 printf("valid commands:\n");
576 printf("insert [reader_id]\n");
577 printf("remove [reader_id]\n");
578 printf("select reader_id\n");
580 printf("debug [level]\n");
584 vreader_free(reader
);
592 /* just for ease of parsing command line arguments. */
593 #define MAX_CERTS 100
600 struct addrinfo hints
;
601 struct addrinfo
*server
;
604 sock
= qemu_socket(AF_INET
, SOCK_STREAM
, 0);
607 fprintf(stderr
, "Error opening socket!\n");
611 memset(&hints
, 0, sizeof(struct addrinfo
));
612 hints
.ai_family
= AF_UNSPEC
;
613 hints
.ai_socktype
= SOCK_STREAM
;
615 hints
.ai_protocol
= 0; /* Any protocol */
617 ret
= getaddrinfo(host
, port
, &hints
, &server
);
621 fprintf(stderr
, "getaddrinfo failed\n");
625 if (connect(sock
, server
->ai_addr
, server
->ai_addrlen
) < 0) {
627 fprintf(stderr
, "Could not connect\n");
631 printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader
));
646 GIOChannel
*channel_stdin
;
650 VCardEmulOptions
*command_line_options
= NULL
;
652 char *cert_names
[MAX_CERTS
];
653 char *emul_args
= NULL
;
657 if (socket_init() != 0)
660 while ((c
= getopt(argc
, argv
, "c:e:pd:")) != -1) {
663 if (cert_count
>= MAX_CERTS
) {
664 printf("too many certificates (max = %d)\n", MAX_CERTS
);
667 cert_names
[cert_count
++] = optarg
;
677 verbose
= get_id_from_string(optarg
, 1);
682 if (argc
- optind
!= 2) {
687 if (cert_count
> 0) {
690 /* if we've given some -c options, we clearly we want do so some
691 * software emulation. add that emulation now. this is NSS Emulator
693 if (emul_args
== NULL
) {
694 emul_args
= (char *)"db=\"/etc/pki/nssdb\"";
696 #define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
697 /* 2 == close paren & null */
698 len
= strlen(emul_args
) + strlen(SOFT_STRING
) + 2;
699 for (i
= 0; i
< cert_count
; i
++) {
700 len
+= strlen(cert_names
[i
])+1; /* 1 == comma */
702 new_args
= g_malloc(len
);
703 strcpy(new_args
, emul_args
);
704 strcat(new_args
, SOFT_STRING
);
705 for (i
= 0; i
< cert_count
; i
++) {
706 strcat(new_args
, cert_names
[i
]);
707 strcat(new_args
, ",");
709 strcat(new_args
, ")");
710 emul_args
= new_args
;
713 command_line_options
= vcard_emul_options(emul_args
);
716 qemu_host
= g_strdup(argv
[argc
- 2]);
717 qemu_port
= g_strdup(argv
[argc
- 1]);
718 sock
= connect_to_qemu(qemu_host
, qemu_port
);
720 fprintf(stderr
, "error opening socket, exiting.\n");
724 socket_to_send
= g_byte_array_new();
725 qemu_mutex_init(&socket_to_send_lock
);
726 qemu_mutex_init(&pending_reader_lock
);
727 qemu_cond_init(&pending_reader_condition
);
729 vcard_emul_init(command_line_options
);
731 loop
= g_main_loop_new(NULL
, true);
737 channel_stdin
= g_io_channel_win32_new_fd(STDIN_FILENO
);
739 channel_stdin
= g_io_channel_unix_new(STDIN_FILENO
);
741 g_io_add_watch(channel_stdin
, G_IO_IN
, do_command
, NULL
);
743 channel_socket
= g_io_channel_win32_new_socket(sock
);
745 channel_socket
= g_io_channel_unix_new(sock
);
747 g_io_channel_set_encoding(channel_socket
, NULL
, NULL
);
748 /* we buffer ourself for thread safety reasons */
749 g_io_channel_set_buffered(channel_socket
, FALSE
);
751 /* Send init message, Host responds (and then we send reader attachments) */
753 .version
= htonl(VSCARD_VERSION
),
754 .magic
= VSCARD_MAGIC
,
757 send_msg(VSC_Init
, 0, &init
, sizeof(init
));
759 g_main_loop_run(loop
);
760 g_main_loop_unref(loop
);
762 g_io_channel_unref(channel_stdin
);
763 g_io_channel_unref(channel_socket
);
764 g_byte_array_free(socket_to_send
, TRUE
);