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.
14 #include <sys/socket.h>
15 #include <netinet/in.h>
17 #define closesocket(x) close(x)
20 #include "qemu-common.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 CompatGMutex 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 g_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 g_mutex_unlock(&socket_to_send_lock
);
125 static VReader
*pending_reader
;
126 static CompatGMutex pending_reader_lock
;
127 static CompatGCond pending_reader_condition
;
129 #define MAX_ATR_LEN 40
131 event_thread(gpointer 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 g_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 g_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 g_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
165 g_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 g_mutex_lock(&pending_reader_lock
);
182 while (pending_reader
!= NULL
) {
183 g_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
185 pending_reader
= vreader_reference(event
->reader
);
186 g_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));
250 incoming
->version
= ntohl(incoming
->version
);
251 if (incoming
->version
!= VSCARD_VERSION
) {
253 printf("warning: host has version %d, we have %d\n",
254 verbose
, VSCARD_VERSION
);
257 if (incoming
->magic
!= VSCARD_MAGIC
) {
258 printf("unexpected magic: got %d, expected %d\n",
259 incoming
->magic
, VSCARD_MAGIC
);
262 for (i
= 0 ; i
< num_capabilities
; ++i
) {
263 capabilities
[i
] = ntohl(capabilities
[i
]);
265 /* Future: check capabilities */
266 /* remove whatever reader might be left in qemu,
267 * in case of an unclean previous exit. */
268 send_msg(VSC_ReaderRemove
, VSCARD_MINIMAL_READER_ID
, NULL
, 0);
269 /* launch the event_thread. This will trigger reader adds for all the
270 * existing readers */
271 g_thread_new("vsc/event", event_thread
, NULL
);
281 #define APDUBufSize 270
284 do_socket_read(GIOChannel
*source
,
285 GIOCondition condition
,
291 uint8_t pbRecvBuffer
[APDUBufSize
];
292 static uint8_t pbSendBuffer
[APDUBufSize
];
293 VReaderStatus reader_status
;
294 VReader
*reader
= NULL
;
295 static VSCMsgHeader mhHeader
;
296 VSCMsgError
*error_msg
;
300 static gsize br
, to_read
;
301 static int state
= STATE_HEADER
;
303 if (state
== STATE_HEADER
&& to_read
== 0) {
304 buf
= (gchar
*)&mhHeader
;
305 to_read
= sizeof(mhHeader
);
309 g_io_channel_read_chars(source
, (gchar
*)buf
, to_read
, &br
, &err
);
311 g_error("error while reading: %s", err
->message
);
320 if (state
== STATE_HEADER
) {
321 mhHeader
.type
= ntohl(mhHeader
.type
);
322 mhHeader
.reader_id
= ntohl(mhHeader
.reader_id
);
323 mhHeader
.length
= ntohl(mhHeader
.length
);
325 printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
326 mhHeader
.type
, mhHeader
.reader_id
, mhHeader
.length
,
329 switch (mhHeader
.type
) {
334 buf
= (gchar
*)pbSendBuffer
;
335 to_read
= mhHeader
.length
;
336 state
= STATE_MESSAGE
;
339 fprintf(stderr
, "Unexpected message of type 0x%X\n", mhHeader
.type
);
344 if (state
== STATE_MESSAGE
) {
345 switch (mhHeader
.type
) {
348 printf(" recv APDU: ");
349 print_byte_array(pbSendBuffer
, mhHeader
.length
);
351 /* Transmit received APDU */
352 dwSendLength
= mhHeader
.length
;
353 dwRecvLength
= sizeof(pbRecvBuffer
);
354 reader
= vreader_get_reader_by_id(mhHeader
.reader_id
);
355 reader_status
= vreader_xfr_bytes(reader
,
356 pbSendBuffer
, dwSendLength
,
357 pbRecvBuffer
, &dwRecvLength
);
358 if (reader_status
== VREADER_OK
) {
359 mhHeader
.length
= dwRecvLength
;
361 printf(" send response: ");
362 print_byte_array(pbRecvBuffer
, mhHeader
.length
);
364 send_msg(VSC_APDU
, mhHeader
.reader_id
,
365 pbRecvBuffer
, dwRecvLength
);
367 rv
= reader_status
; /* warning: not meaningful */
368 send_msg(VSC_Error
, mhHeader
.reader_id
, &rv
, sizeof(uint32_t));
370 vreader_free(reader
);
371 reader
= NULL
; /* we've freed it, don't use it by accident
375 /* TODO: actually flush */
376 send_msg(VSC_FlushComplete
, mhHeader
.reader_id
, NULL
, 0);
379 error_msg
= (VSCMsgError
*) pbSendBuffer
;
380 if (error_msg
->code
== VSC_SUCCESS
) {
381 g_mutex_lock(&pending_reader_lock
);
382 if (pending_reader
) {
383 vreader_set_id(pending_reader
, mhHeader
.reader_id
);
384 vreader_free(pending_reader
);
385 pending_reader
= NULL
;
386 g_cond_signal(&pending_reader_condition
);
388 g_mutex_unlock(&pending_reader_lock
);
391 printf("warning: qemu refused to add reader\n");
392 if (error_msg
->code
== VSC_CANNOT_ADD_MORE_READERS
) {
393 /* clear pending reader, qemu can't handle any more */
394 g_mutex_lock(&pending_reader_lock
);
395 if (pending_reader
) {
396 pending_reader
= NULL
;
397 /* make sure the event loop doesn't hang */
398 g_cond_signal(&pending_reader_condition
);
400 g_mutex_unlock(&pending_reader_lock
);
404 if (on_host_init(&mhHeader
, (VSCMsgInit
*)pbSendBuffer
) < 0) {
409 g_assert_not_reached();
413 state
= STATE_HEADER
;
421 do_socket(GIOChannel
*source
,
422 GIOCondition condition
,
425 /* not sure if two watches work well with a single win32 sources */
426 if (condition
& G_IO_OUT
) {
427 if (!do_socket_send(source
, condition
, data
)) {
432 if (condition
& G_IO_IN
) {
433 if (!do_socket_read(source
, condition
, data
)) {
442 update_socket_watch(void)
444 gboolean out
= socket_to_send
->len
> 0;
446 if (socket_tag
!= 0) {
447 g_source_remove(socket_tag
);
450 socket_tag
= g_io_add_watch(channel_socket
,
451 G_IO_IN
| (out
? G_IO_OUT
: 0), do_socket
, NULL
);
455 do_command(GIOChannel
*source
,
456 GIOCondition condition
,
460 VCardEmulError error
;
461 static unsigned int default_reader_id
;
462 unsigned int reader_id
;
463 VReader
*reader
= NULL
;
466 g_assert(condition
& G_IO_IN
);
468 reader_id
= default_reader_id
;
469 g_io_channel_read_line(source
, &string
, NULL
, NULL
, &err
);
471 g_error("Error while reading command: %s", err
->message
);
474 if (string
!= NULL
) {
475 if (strncmp(string
, "exit", 4) == 0) {
476 /* remove all the readers */
477 VReaderList
*list
= vreader_get_reader_list();
478 VReaderListEntry
*reader_entry
;
479 printf("Active Readers:\n");
480 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
481 reader_entry
= vreader_list_get_next(reader_entry
)) {
482 VReader
*reader
= vreader_list_get_reader(reader_entry
);
483 vreader_id_t reader_id
;
484 reader_id
= vreader_get_id(reader
);
485 if (reader_id
== -1) {
488 /* be nice and signal card removal first (qemu probably should
490 if (vreader_card_is_present(reader
) == VREADER_OK
) {
491 send_msg(VSC_CardRemove
, reader_id
, NULL
, 0);
493 send_msg(VSC_ReaderRemove
, reader_id
, NULL
, 0);
496 } else if (strncmp(string
, "insert", 6) == 0) {
497 if (string
[6] == ' ') {
498 reader_id
= get_id_from_string(&string
[7], reader_id
);
500 reader
= vreader_get_reader_by_id(reader_id
);
501 if (reader
!= NULL
) {
502 error
= vcard_emul_force_card_insert(reader
);
503 printf("insert %s, returned %d\n",
504 vreader_get_name(reader
), error
);
506 printf("no reader by id %u found\n", reader_id
);
508 } else if (strncmp(string
, "remove", 6) == 0) {
509 if (string
[6] == ' ') {
510 reader_id
= get_id_from_string(&string
[7], reader_id
);
512 reader
= vreader_get_reader_by_id(reader_id
);
513 if (reader
!= NULL
) {
514 error
= vcard_emul_force_card_remove(reader
);
515 printf("remove %s, returned %d\n",
516 vreader_get_name(reader
), error
);
518 printf("no reader by id %u found\n", reader_id
);
520 } else if (strncmp(string
, "select", 6) == 0) {
521 if (string
[6] == ' ') {
522 reader_id
= get_id_from_string(&string
[7],
523 VSCARD_UNDEFINED_READER_ID
);
525 if (reader_id
!= VSCARD_UNDEFINED_READER_ID
) {
526 reader
= vreader_get_reader_by_id(reader_id
);
529 printf("Selecting reader %u, %s\n", reader_id
,
530 vreader_get_name(reader
));
531 default_reader_id
= reader_id
;
533 printf("Reader with id %u not found\n", reader_id
);
535 } else if (strncmp(string
, "debug", 5) == 0) {
536 if (string
[5] == ' ') {
537 verbose
= get_id_from_string(&string
[6], 0);
539 printf("debug level = %d\n", verbose
);
540 } else if (strncmp(string
, "list", 4) == 0) {
541 VReaderList
*list
= vreader_get_reader_list();
542 VReaderListEntry
*reader_entry
;
543 printf("Active Readers:\n");
544 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
545 reader_entry
= vreader_list_get_next(reader_entry
)) {
546 VReader
*reader
= vreader_list_get_reader(reader_entry
);
547 vreader_id_t reader_id
;
548 reader_id
= vreader_get_id(reader
);
549 if (reader_id
== -1) {
552 printf("%3u %s %s\n", reader_id
,
553 vreader_card_is_present(reader
) == VREADER_OK
?
554 "CARD_PRESENT" : " ",
555 vreader_get_name(reader
));
557 printf("Inactive Readers:\n");
558 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
559 reader_entry
= vreader_list_get_next(reader_entry
)) {
560 VReader
*reader
= vreader_list_get_reader(reader_entry
);
561 vreader_id_t reader_id
;
562 reader_id
= vreader_get_id(reader
);
563 if (reader_id
!= -1) {
567 printf("INA %s %s\n",
568 vreader_card_is_present(reader
) == VREADER_OK
?
569 "CARD_PRESENT" : " ",
570 vreader_get_name(reader
));
572 vreader_list_delete(list
);
573 } else if (*string
!= 0) {
574 printf("valid commands:\n");
575 printf("insert [reader_id]\n");
576 printf("remove [reader_id]\n");
577 printf("select reader_id\n");
579 printf("debug [level]\n");
583 vreader_free(reader
);
591 /* just for ease of parsing command line arguments. */
592 #define MAX_CERTS 100
599 struct addrinfo hints
;
600 struct addrinfo
*server
= NULL
;
603 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
606 fprintf(stderr
, "Error opening socket!\n");
610 memset(&hints
, 0, sizeof(struct addrinfo
));
611 hints
.ai_family
= AF_UNSPEC
;
612 hints
.ai_socktype
= SOCK_STREAM
;
614 hints
.ai_protocol
= 0; /* Any protocol */
616 ret
= getaddrinfo(host
, port
, &hints
, &server
);
620 fprintf(stderr
, "getaddrinfo failed\n");
624 if (connect(sock
, server
->ai_addr
, server
->ai_addrlen
) < 0) {
626 fprintf(stderr
, "Could not connect\n");
630 printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader
));
633 freeaddrinfo(server
);
638 freeaddrinfo(server
);
650 GIOChannel
*channel_stdin
;
654 VCardEmulOptions
*command_line_options
= NULL
;
656 char *cert_names
[MAX_CERTS
];
657 char *emul_args
= NULL
;
664 if (WSAStartup(MAKEWORD(2, 2), &Data
) != 0) {
665 c
= WSAGetLastError();
666 fprintf(stderr
, "WSAStartup: %d\n", c
);
670 #if !GLIB_CHECK_VERSION(2, 31, 0)
671 if (!g_thread_supported()) {
676 while ((c
= getopt(argc
, argv
, "c:e:pd:")) != -1) {
679 if (cert_count
>= MAX_CERTS
) {
680 printf("too many certificates (max = %d)\n", MAX_CERTS
);
683 cert_names
[cert_count
++] = optarg
;
693 verbose
= get_id_from_string(optarg
, 1);
698 if (argc
- optind
!= 2) {
703 if (cert_count
> 0) {
706 /* if we've given some -c options, we clearly we want do so some
707 * software emulation. add that emulation now. this is NSS Emulator
709 if (emul_args
== NULL
) {
710 emul_args
= (char *)"db=\"/etc/pki/nssdb\"";
712 #define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
713 /* 2 == close paren & null */
714 len
= strlen(emul_args
) + strlen(SOFT_STRING
) + 2;
715 for (i
= 0; i
< cert_count
; i
++) {
716 len
+= strlen(cert_names
[i
])+1; /* 1 == comma */
718 new_args
= g_malloc(len
);
719 strcpy(new_args
, emul_args
);
720 strcat(new_args
, SOFT_STRING
);
721 for (i
= 0; i
< cert_count
; i
++) {
722 strcat(new_args
, cert_names
[i
]);
723 strcat(new_args
, ",");
725 strcat(new_args
, ")");
726 emul_args
= new_args
;
729 command_line_options
= vcard_emul_options(emul_args
);
732 qemu_host
= g_strdup(argv
[argc
- 2]);
733 qemu_port
= g_strdup(argv
[argc
- 1]);
734 sock
= connect_to_qemu(qemu_host
, qemu_port
);
736 fprintf(stderr
, "error opening socket, exiting.\n");
740 socket_to_send
= g_byte_array_new();
741 vcard_emul_init(command_line_options
);
742 loop
= g_main_loop_new(NULL
, TRUE
);
748 channel_stdin
= g_io_channel_win32_new_fd(STDIN_FILENO
);
750 channel_stdin
= g_io_channel_unix_new(STDIN_FILENO
);
752 g_io_add_watch(channel_stdin
, G_IO_IN
, do_command
, NULL
);
754 channel_socket
= g_io_channel_win32_new_socket(sock
);
756 channel_socket
= g_io_channel_unix_new(sock
);
758 g_io_channel_set_encoding(channel_socket
, NULL
, NULL
);
759 /* we buffer ourself for thread safety reasons */
760 g_io_channel_set_buffered(channel_socket
, FALSE
);
762 /* Send init message, Host responds (and then we send reader attachments) */
764 .version
= htonl(VSCARD_VERSION
),
765 .magic
= VSCARD_MAGIC
,
768 send_msg(VSC_Init
, 0, &init
, sizeof(init
));
770 g_main_loop_run(loop
);
771 g_main_loop_unref(loop
);
773 g_io_channel_unref(channel_stdin
);
774 g_io_channel_unref(channel_socket
);
775 g_byte_array_free(socket_to_send
, TRUE
);