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.
17 #include <sys/socket.h>
18 #include <netinet/in.h>
21 #define closesocket(x) close(x)
26 #include "glib-compat.h"
28 #include "vscard_common.h"
31 #include "vcard_emul.h"
42 for (i
= 0; i
< nSize
; i
++) {
43 printf("%02X ", arrBytes
[i
]);
50 printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
54 printf(" -p use passthrough mode\n");
61 static GIOChannel
*channel_socket
;
62 static GByteArray
*socket_to_send
;
63 static CompatGMutex socket_to_send_lock
;
64 static guint socket_tag
;
67 update_socket_watch(void);
70 do_socket_send(GIOChannel
*source
,
71 GIOCondition condition
,
77 g_return_val_if_fail(socket_to_send
->len
!= 0, FALSE
);
78 g_return_val_if_fail(condition
& G_IO_OUT
, FALSE
);
80 g_io_channel_write_chars(channel_socket
,
81 (gchar
*)socket_to_send
->data
, socket_to_send
->len
, &bw
, &err
);
83 g_error("Error while sending socket %s", err
->message
);
86 g_byte_array_remove_range(socket_to_send
, 0, bw
);
88 if (socket_to_send
->len
== 0) {
89 update_socket_watch();
96 socket_prepare_sending(gpointer user_data
)
98 update_socket_watch();
110 VSCMsgHeader mhHeader
;
112 g_mutex_lock(&socket_to_send_lock
);
115 printf("sending type=%d id=%u, len =%u (0x%x)\n",
116 type
, reader_id
, length
, length
);
119 mhHeader
.type
= htonl(type
);
120 mhHeader
.reader_id
= 0;
121 mhHeader
.length
= htonl(length
);
122 g_byte_array_append(socket_to_send
, (guint8
*)&mhHeader
, sizeof(mhHeader
));
123 g_byte_array_append(socket_to_send
, (guint8
*)msg
, length
);
124 g_idle_add(socket_prepare_sending
, NULL
);
126 g_mutex_unlock(&socket_to_send_lock
);
131 static VReader
*pending_reader
;
132 static CompatGMutex pending_reader_lock
;
133 static CompatGCond pending_reader_condition
;
135 #define MAX_ATR_LEN 40
137 event_thread(gpointer arg
)
139 unsigned char atr
[MAX_ATR_LEN
];
142 unsigned int reader_id
;
146 const char *reader_name
;
148 event
= vevent_wait_next_vevent();
152 reader_id
= vreader_get_id(event
->reader
);
153 if (reader_id
== VSCARD_UNDEFINED_READER_ID
&&
154 event
->type
!= VEVENT_READER_INSERT
) {
155 /* ignore events from readers qemu has rejected */
156 /* if qemu is still deciding on this reader, wait to see if need to
157 * forward this event */
158 g_mutex_lock(&pending_reader_lock
);
159 if (!pending_reader
|| (pending_reader
!= event
->reader
)) {
160 /* wasn't for a pending reader, this reader has already been
161 * rejected by qemu */
162 g_mutex_unlock(&pending_reader_lock
);
163 vevent_delete(event
);
166 /* this reader hasn't been told its status from qemu yet, wait for
168 while (pending_reader
!= NULL
) {
169 g_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
171 g_mutex_unlock(&pending_reader_lock
);
172 /* now recheck the id */
173 reader_id
= vreader_get_id(event
->reader
);
174 if (reader_id
== VSCARD_UNDEFINED_READER_ID
) {
175 /* this reader was rejected */
176 vevent_delete(event
);
179 /* reader was accepted, now forward the event */
181 switch (event
->type
) {
182 case VEVENT_READER_INSERT
:
183 /* tell qemu to insert a new CCID reader */
184 /* wait until qemu has responded to our first reader insert
185 * before we send a second. That way we won't confuse the responses
187 g_mutex_lock(&pending_reader_lock
);
188 while (pending_reader
!= NULL
) {
189 g_cond_wait(&pending_reader_condition
, &pending_reader_lock
);
191 pending_reader
= vreader_reference(event
->reader
);
192 g_mutex_unlock(&pending_reader_lock
);
193 reader_name
= vreader_get_name(event
->reader
);
195 printf(" READER INSERT: %s\n", reader_name
);
197 send_msg(VSC_ReaderAdd
,
198 reader_id
, /* currerntly VSCARD_UNDEFINED_READER_ID */
199 NULL
, 0 /* TODO reader_name, strlen(reader_name) */);
201 case VEVENT_READER_REMOVE
:
202 /* future, tell qemu that an old CCID reader has been removed */
204 printf(" READER REMOVE: %u\n", reader_id
);
206 send_msg(VSC_ReaderRemove
, reader_id
, NULL
, 0);
208 case VEVENT_CARD_INSERT
:
209 /* get the ATR (intended as a response to a power on from the
211 atr_len
= MAX_ATR_LEN
;
212 vreader_power_on(event
->reader
, atr
, &atr_len
);
213 /* ATR call functions as a Card Insert event */
215 printf(" CARD INSERT %u: ", reader_id
);
216 print_byte_array(atr
, atr_len
);
218 send_msg(VSC_ATR
, reader_id
, atr
, atr_len
);
220 case VEVENT_CARD_REMOVE
:
223 printf(" CARD REMOVE %u:\n", reader_id
);
225 send_msg(VSC_CardRemove
, reader_id
, NULL
, 0);
230 vevent_delete(event
);
237 get_id_from_string(char *string
, unsigned int default_id
)
239 unsigned int id
= atoi(string
);
241 /* don't accidentally swith to zero because no numbers have been supplied */
242 if ((id
== 0) && *string
!= '0') {
249 on_host_init(VSCMsgHeader
*mhHeader
, VSCMsgInit
*incoming
)
251 uint32_t *capabilities
= (incoming
->capabilities
);
252 int num_capabilities
=
253 1 + ((mhHeader
->length
- sizeof(VSCMsgInit
)) / sizeof(uint32_t));
256 incoming
->version
= ntohl(incoming
->version
);
257 if (incoming
->version
!= VSCARD_VERSION
) {
259 printf("warning: host has version %d, we have %d\n",
260 verbose
, VSCARD_VERSION
);
263 if (incoming
->magic
!= VSCARD_MAGIC
) {
264 printf("unexpected magic: got %d, expected %d\n",
265 incoming
->magic
, VSCARD_MAGIC
);
268 for (i
= 0 ; i
< num_capabilities
; ++i
) {
269 capabilities
[i
] = ntohl(capabilities
[i
]);
271 /* Future: check capabilities */
272 /* remove whatever reader might be left in qemu,
273 * in case of an unclean previous exit. */
274 send_msg(VSC_ReaderRemove
, VSCARD_MINIMAL_READER_ID
, NULL
, 0);
275 /* launch the event_thread. This will trigger reader adds for all the
276 * existing readers */
277 g_thread_new("vsc/event", event_thread
, NULL
);
287 #define APDUBufSize 270
290 do_socket_read(GIOChannel
*source
,
291 GIOCondition condition
,
297 uint8_t pbRecvBuffer
[APDUBufSize
];
298 static uint8_t pbSendBuffer
[APDUBufSize
];
299 VReaderStatus reader_status
;
300 VReader
*reader
= NULL
;
301 static VSCMsgHeader mhHeader
;
302 VSCMsgError
*error_msg
;
306 static gsize br
, to_read
;
307 static int state
= STATE_HEADER
;
309 if (state
== STATE_HEADER
&& to_read
== 0) {
310 buf
= (gchar
*)&mhHeader
;
311 to_read
= sizeof(mhHeader
);
315 g_io_channel_read_chars(source
, (gchar
*)buf
, to_read
, &br
, &err
);
317 g_error("error while reading: %s", err
->message
);
326 if (state
== STATE_HEADER
) {
327 mhHeader
.type
= ntohl(mhHeader
.type
);
328 mhHeader
.reader_id
= ntohl(mhHeader
.reader_id
);
329 mhHeader
.length
= ntohl(mhHeader
.length
);
331 printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
332 mhHeader
.type
, mhHeader
.reader_id
, mhHeader
.length
,
335 switch (mhHeader
.type
) {
340 buf
= (gchar
*)pbSendBuffer
;
341 to_read
= mhHeader
.length
;
342 state
= STATE_MESSAGE
;
345 fprintf(stderr
, "Unexpected message of type 0x%X\n", mhHeader
.type
);
350 if (state
== STATE_MESSAGE
) {
351 switch (mhHeader
.type
) {
354 printf(" recv APDU: ");
355 print_byte_array(pbSendBuffer
, mhHeader
.length
);
357 /* Transmit received APDU */
358 dwSendLength
= mhHeader
.length
;
359 dwRecvLength
= sizeof(pbRecvBuffer
);
360 reader
= vreader_get_reader_by_id(mhHeader
.reader_id
);
361 reader_status
= vreader_xfr_bytes(reader
,
362 pbSendBuffer
, dwSendLength
,
363 pbRecvBuffer
, &dwRecvLength
);
364 if (reader_status
== VREADER_OK
) {
365 mhHeader
.length
= dwRecvLength
;
367 printf(" send response: ");
368 print_byte_array(pbRecvBuffer
, mhHeader
.length
);
370 send_msg(VSC_APDU
, mhHeader
.reader_id
,
371 pbRecvBuffer
, dwRecvLength
);
373 rv
= reader_status
; /* warning: not meaningful */
374 send_msg(VSC_Error
, mhHeader
.reader_id
, &rv
, sizeof(uint32_t));
376 vreader_free(reader
);
377 reader
= NULL
; /* we've freed it, don't use it by accident
381 /* TODO: actually flush */
382 send_msg(VSC_FlushComplete
, mhHeader
.reader_id
, NULL
, 0);
385 error_msg
= (VSCMsgError
*) pbSendBuffer
;
386 if (error_msg
->code
== VSC_SUCCESS
) {
387 g_mutex_lock(&pending_reader_lock
);
388 if (pending_reader
) {
389 vreader_set_id(pending_reader
, mhHeader
.reader_id
);
390 vreader_free(pending_reader
);
391 pending_reader
= NULL
;
392 g_cond_signal(&pending_reader_condition
);
394 g_mutex_unlock(&pending_reader_lock
);
397 printf("warning: qemu refused to add reader\n");
398 if (error_msg
->code
== VSC_CANNOT_ADD_MORE_READERS
) {
399 /* clear pending reader, qemu can't handle any more */
400 g_mutex_lock(&pending_reader_lock
);
401 if (pending_reader
) {
402 pending_reader
= NULL
;
403 /* make sure the event loop doesn't hang */
404 g_cond_signal(&pending_reader_condition
);
406 g_mutex_unlock(&pending_reader_lock
);
410 if (on_host_init(&mhHeader
, (VSCMsgInit
*)pbSendBuffer
) < 0) {
415 g_assert_not_reached();
419 state
= STATE_HEADER
;
427 do_socket(GIOChannel
*source
,
428 GIOCondition condition
,
431 /* not sure if two watches work well with a single win32 sources */
432 if (condition
& G_IO_OUT
) {
433 if (!do_socket_send(source
, condition
, data
)) {
438 if (condition
& G_IO_IN
) {
439 if (!do_socket_read(source
, condition
, data
)) {
448 update_socket_watch(void)
450 gboolean out
= socket_to_send
->len
> 0;
452 if (socket_tag
!= 0) {
453 g_source_remove(socket_tag
);
456 socket_tag
= g_io_add_watch(channel_socket
,
457 G_IO_IN
| (out
? G_IO_OUT
: 0), do_socket
, NULL
);
461 do_command(GIOChannel
*source
,
462 GIOCondition condition
,
466 VCardEmulError error
;
467 static unsigned int default_reader_id
;
468 unsigned int reader_id
;
469 VReader
*reader
= NULL
;
472 g_assert(condition
& G_IO_IN
);
474 reader_id
= default_reader_id
;
475 g_io_channel_read_line(source
, &string
, NULL
, NULL
, &err
);
477 g_error("Error while reading command: %s", err
->message
);
480 if (string
!= NULL
) {
481 if (strncmp(string
, "exit", 4) == 0) {
482 /* remove all the readers */
483 VReaderList
*list
= vreader_get_reader_list();
484 VReaderListEntry
*reader_entry
;
485 printf("Active Readers:\n");
486 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
487 reader_entry
= vreader_list_get_next(reader_entry
)) {
488 VReader
*reader
= vreader_list_get_reader(reader_entry
);
489 vreader_id_t reader_id
;
490 reader_id
= vreader_get_id(reader
);
491 if (reader_id
== -1) {
494 /* be nice and signal card removal first (qemu probably should
496 if (vreader_card_is_present(reader
) == VREADER_OK
) {
497 send_msg(VSC_CardRemove
, reader_id
, NULL
, 0);
499 send_msg(VSC_ReaderRemove
, reader_id
, NULL
, 0);
502 } else if (strncmp(string
, "insert", 6) == 0) {
503 if (string
[6] == ' ') {
504 reader_id
= get_id_from_string(&string
[7], reader_id
);
506 reader
= vreader_get_reader_by_id(reader_id
);
507 if (reader
!= NULL
) {
508 error
= vcard_emul_force_card_insert(reader
);
509 printf("insert %s, returned %d\n",
510 vreader_get_name(reader
), error
);
512 printf("no reader by id %u found\n", reader_id
);
514 } else if (strncmp(string
, "remove", 6) == 0) {
515 if (string
[6] == ' ') {
516 reader_id
= get_id_from_string(&string
[7], reader_id
);
518 reader
= vreader_get_reader_by_id(reader_id
);
519 if (reader
!= NULL
) {
520 error
= vcard_emul_force_card_remove(reader
);
521 printf("remove %s, returned %d\n",
522 vreader_get_name(reader
), error
);
524 printf("no reader by id %u found\n", reader_id
);
526 } else if (strncmp(string
, "select", 6) == 0) {
527 if (string
[6] == ' ') {
528 reader_id
= get_id_from_string(&string
[7],
529 VSCARD_UNDEFINED_READER_ID
);
531 if (reader_id
!= VSCARD_UNDEFINED_READER_ID
) {
532 reader
= vreader_get_reader_by_id(reader_id
);
535 printf("Selecting reader %u, %s\n", reader_id
,
536 vreader_get_name(reader
));
537 default_reader_id
= reader_id
;
539 printf("Reader with id %u not found\n", reader_id
);
541 } else if (strncmp(string
, "debug", 5) == 0) {
542 if (string
[5] == ' ') {
543 verbose
= get_id_from_string(&string
[6], 0);
545 printf("debug level = %d\n", verbose
);
546 } else if (strncmp(string
, "list", 4) == 0) {
547 VReaderList
*list
= vreader_get_reader_list();
548 VReaderListEntry
*reader_entry
;
549 printf("Active Readers:\n");
550 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
551 reader_entry
= vreader_list_get_next(reader_entry
)) {
552 VReader
*reader
= vreader_list_get_reader(reader_entry
);
553 vreader_id_t reader_id
;
554 reader_id
= vreader_get_id(reader
);
555 if (reader_id
== -1) {
558 printf("%3u %s %s\n", reader_id
,
559 vreader_card_is_present(reader
) == VREADER_OK
?
560 "CARD_PRESENT" : " ",
561 vreader_get_name(reader
));
563 printf("Inactive Readers:\n");
564 for (reader_entry
= vreader_list_get_first(list
); reader_entry
;
565 reader_entry
= vreader_list_get_next(reader_entry
)) {
566 VReader
*reader
= vreader_list_get_reader(reader_entry
);
567 vreader_id_t reader_id
;
568 reader_id
= vreader_get_id(reader
);
569 if (reader_id
!= -1) {
573 printf("INA %s %s\n",
574 vreader_card_is_present(reader
) == VREADER_OK
?
575 "CARD_PRESENT" : " ",
576 vreader_get_name(reader
));
578 vreader_list_delete(list
);
579 } else if (*string
!= 0) {
580 printf("valid commands:\n");
581 printf("insert [reader_id]\n");
582 printf("remove [reader_id]\n");
583 printf("select reader_id\n");
585 printf("debug [level]\n");
589 vreader_free(reader
);
597 /* just for ease of parsing command line arguments. */
598 #define MAX_CERTS 100
605 struct addrinfo hints
;
606 struct addrinfo
*server
= NULL
;
609 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
612 fprintf(stderr
, "Error opening socket!\n");
616 memset(&hints
, 0, sizeof(struct addrinfo
));
617 hints
.ai_family
= AF_UNSPEC
;
618 hints
.ai_socktype
= SOCK_STREAM
;
620 hints
.ai_protocol
= 0; /* Any protocol */
622 ret
= getaddrinfo(host
, port
, &hints
, &server
);
626 fprintf(stderr
, "getaddrinfo failed\n");
630 if (connect(sock
, server
->ai_addr
, server
->ai_addrlen
) < 0) {
632 fprintf(stderr
, "Could not connect\n");
636 printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader
));
639 freeaddrinfo(server
);
644 freeaddrinfo(server
);
656 GIOChannel
*channel_stdin
;
660 VCardEmulOptions
*command_line_options
= NULL
;
662 char *cert_names
[MAX_CERTS
];
663 char *emul_args
= NULL
;
670 if (WSAStartup(MAKEWORD(2, 2), &Data
) != 0) {
671 c
= WSAGetLastError();
672 fprintf(stderr
, "WSAStartup: %d\n", c
);
676 #if !GLIB_CHECK_VERSION(2, 31, 0)
677 if (!g_thread_supported()) {
682 while ((c
= getopt(argc
, argv
, "c:e:pd:")) != -1) {
685 if (cert_count
>= MAX_CERTS
) {
686 printf("too many certificates (max = %d)\n", MAX_CERTS
);
689 cert_names
[cert_count
++] = optarg
;
699 verbose
= get_id_from_string(optarg
, 1);
704 if (argc
- optind
!= 2) {
709 if (cert_count
> 0) {
712 /* if we've given some -c options, we clearly we want do so some
713 * software emulation. add that emulation now. this is NSS Emulator
715 if (emul_args
== NULL
) {
716 emul_args
= (char *)"db=\"/etc/pki/nssdb\"";
718 #define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
719 /* 2 == close paren & null */
720 len
= strlen(emul_args
) + strlen(SOFT_STRING
) + 2;
721 for (i
= 0; i
< cert_count
; i
++) {
722 len
+= strlen(cert_names
[i
])+1; /* 1 == comma */
724 new_args
= g_malloc(len
);
725 strcpy(new_args
, emul_args
);
726 strcat(new_args
, SOFT_STRING
);
727 for (i
= 0; i
< cert_count
; i
++) {
728 strcat(new_args
, cert_names
[i
]);
729 strcat(new_args
, ",");
731 strcat(new_args
, ")");
732 emul_args
= new_args
;
735 command_line_options
= vcard_emul_options(emul_args
);
738 qemu_host
= g_strdup(argv
[argc
- 2]);
739 qemu_port
= g_strdup(argv
[argc
- 1]);
740 sock
= connect_to_qemu(qemu_host
, qemu_port
);
742 fprintf(stderr
, "error opening socket, exiting.\n");
746 socket_to_send
= g_byte_array_new();
747 vcard_emul_init(command_line_options
);
748 loop
= g_main_loop_new(NULL
, TRUE
);
754 channel_stdin
= g_io_channel_win32_new_fd(STDIN_FILENO
);
756 channel_stdin
= g_io_channel_unix_new(STDIN_FILENO
);
758 g_io_add_watch(channel_stdin
, G_IO_IN
, do_command
, NULL
);
760 channel_socket
= g_io_channel_win32_new_socket(sock
);
762 channel_socket
= g_io_channel_unix_new(sock
);
764 g_io_channel_set_encoding(channel_socket
, NULL
, NULL
);
765 /* we buffer ourself for thread safety reasons */
766 g_io_channel_set_buffered(channel_socket
, FALSE
);
768 /* Send init message, Host responds (and then we send reader attachments) */
770 .version
= htonl(VSCARD_VERSION
),
771 .magic
= VSCARD_MAGIC
,
774 send_msg(VSC_Init
, 0, &init
, sizeof(init
));
776 g_main_loop_run(loop
);
777 g_main_loop_unref(loop
);
779 g_io_channel_unref(channel_stdin
);
780 g_io_channel_unref(channel_socket
);
781 g_byte_array_free(socket_to_send
, TRUE
);