|
|
1.1 root 1: /*
2: * Tester for VSCARD protocol, client side.
3: *
4: * Can be used with ccid-card-passthru.
5: *
6: * Copyright (c) 2011 Red Hat.
7: * Written by Alon Levy.
8: *
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.
11: */
12:
13: #include <netdb.h>
14:
15: #include "qemu-common.h"
16: #include "qemu-thread.h"
17: #include "qemu_socket.h"
18:
19: #include "vscard_common.h"
20:
21: #include "vreader.h"
22: #include "vcard_emul.h"
23: #include "vevent.h"
24:
25: int verbose;
26:
27: int sock;
28:
29: static void
30: print_byte_array(
31: uint8_t *arrBytes,
32: unsigned int nSize
33: ) {
34: int i;
35: for (i = 0; i < nSize; i++) {
36: printf("%02X ", arrBytes[i]);
37: }
38: printf("\n");
39: }
40:
41: static void
42: print_usage(void) {
43: printf("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
44: "<host> <port>\n",
45: #ifdef USE_PASSTHRU
46: " -p");
47: printf(" -p use passthrough mode\n");
48: #else
49: "");
50: #endif
51: vcard_emul_usage();
52: }
53:
54: static QemuMutex write_lock;
55:
56: static int
57: send_msg(
58: VSCMsgType type,
59: uint32_t reader_id,
60: const void *msg,
61: unsigned int length
62: ) {
63: int rv;
64: VSCMsgHeader mhHeader;
65:
66: qemu_mutex_lock(&write_lock);
67:
68: if (verbose > 10) {
1.1.1.3 ! root 69: printf("sending type=%d id=%u, len =%u (0x%x)\n",
1.1 root 70: type, reader_id, length, length);
71: }
72:
73: mhHeader.type = htonl(type);
74: mhHeader.reader_id = 0;
75: mhHeader.length = htonl(length);
76: rv = write(sock, &mhHeader, sizeof(mhHeader));
77: if (rv < 0) {
78: /* Error */
79: fprintf(stderr, "write header error\n");
80: close(sock);
81: qemu_mutex_unlock(&write_lock);
82: return 16;
83: }
84: rv = write(sock, msg, length);
85: if (rv < 0) {
86: /* Error */
87: fprintf(stderr, "write error\n");
88: close(sock);
89: qemu_mutex_unlock(&write_lock);
90: return 16;
91: }
92: qemu_mutex_unlock(&write_lock);
93:
94: return 0;
95: }
96:
97: static VReader *pending_reader;
98: static QemuMutex pending_reader_lock;
99: static QemuCond pending_reader_condition;
100:
101: #define MAX_ATR_LEN 40
102: static void *
103: event_thread(void *arg)
104: {
105: unsigned char atr[MAX_ATR_LEN];
106: int atr_len = MAX_ATR_LEN;
107: VEvent *event = NULL;
108: unsigned int reader_id;
109:
110:
111: while (1) {
112: const char *reader_name;
113:
114: event = vevent_wait_next_vevent();
115: if (event == NULL) {
116: break;
117: }
118: reader_id = vreader_get_id(event->reader);
119: if (reader_id == VSCARD_UNDEFINED_READER_ID &&
120: event->type != VEVENT_READER_INSERT) {
121: /* ignore events from readers qemu has rejected */
122: /* if qemu is still deciding on this reader, wait to see if need to
123: * forward this event */
124: qemu_mutex_lock(&pending_reader_lock);
125: if (!pending_reader || (pending_reader != event->reader)) {
126: /* wasn't for a pending reader, this reader has already been
127: * rejected by qemu */
128: qemu_mutex_unlock(&pending_reader_lock);
129: vevent_delete(event);
130: continue;
131: }
1.1.1.3 ! root 132: /* this reader hasn't been told its status from qemu yet, wait for
1.1 root 133: * that status */
134: while (pending_reader != NULL) {
135: qemu_cond_wait(&pending_reader_condition, &pending_reader_lock);
136: }
137: qemu_mutex_unlock(&pending_reader_lock);
138: /* now recheck the id */
139: reader_id = vreader_get_id(event->reader);
140: if (reader_id == VSCARD_UNDEFINED_READER_ID) {
141: /* this reader was rejected */
142: vevent_delete(event);
143: continue;
144: }
145: /* reader was accepted, now forward the event */
146: }
147: switch (event->type) {
148: case VEVENT_READER_INSERT:
149: /* tell qemu to insert a new CCID reader */
150: /* wait until qemu has responded to our first reader insert
151: * before we send a second. That way we won't confuse the responses
152: * */
153: qemu_mutex_lock(&pending_reader_lock);
154: while (pending_reader != NULL) {
155: qemu_cond_wait(&pending_reader_condition, &pending_reader_lock);
156: }
157: pending_reader = vreader_reference(event->reader);
158: qemu_mutex_unlock(&pending_reader_lock);
159: reader_name = vreader_get_name(event->reader);
160: if (verbose > 10) {
161: printf(" READER INSERT: %s\n", reader_name);
162: }
163: send_msg(VSC_ReaderAdd,
164: reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
165: NULL, 0 /* TODO reader_name, strlen(reader_name) */);
166: break;
167: case VEVENT_READER_REMOVE:
168: /* future, tell qemu that an old CCID reader has been removed */
169: if (verbose > 10) {
1.1.1.3 ! root 170: printf(" READER REMOVE: %u\n", reader_id);
1.1 root 171: }
172: send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
173: break;
174: case VEVENT_CARD_INSERT:
175: /* get the ATR (intended as a response to a power on from the
176: * reader */
177: atr_len = MAX_ATR_LEN;
178: vreader_power_on(event->reader, atr, &atr_len);
179: /* ATR call functions as a Card Insert event */
180: if (verbose > 10) {
1.1.1.3 ! root 181: printf(" CARD INSERT %u: ", reader_id);
1.1 root 182: print_byte_array(atr, atr_len);
183: }
184: send_msg(VSC_ATR, reader_id, atr, atr_len);
185: break;
186: case VEVENT_CARD_REMOVE:
187: /* Card removed */
188: if (verbose > 10) {
1.1.1.3 ! root 189: printf(" CARD REMOVE %u:\n", reader_id);
1.1 root 190: }
191: send_msg(VSC_CardRemove, reader_id, NULL, 0);
192: break;
193: default:
194: break;
195: }
196: vevent_delete(event);
197: }
198: return NULL;
199: }
200:
201:
202: static unsigned int
203: get_id_from_string(char *string, unsigned int default_id)
204: {
205: unsigned int id = atoi(string);
206:
207: /* don't accidentally swith to zero because no numbers have been supplied */
208: if ((id == 0) && *string != '0') {
209: return default_id;
210: }
211: return id;
212: }
213:
214: static void
215: do_command(void)
216: {
217: char inbuf[255];
218: char *string;
219: VCardEmulError error;
220: static unsigned int default_reader_id;
221: unsigned int reader_id;
222: VReader *reader = NULL;
223:
224: reader_id = default_reader_id;
225: string = fgets(inbuf, sizeof(inbuf), stdin);
226: if (string != NULL) {
227: if (strncmp(string, "exit", 4) == 0) {
228: /* remove all the readers */
229: VReaderList *list = vreader_get_reader_list();
230: VReaderListEntry *reader_entry;
231: printf("Active Readers:\n");
232: for (reader_entry = vreader_list_get_first(list); reader_entry;
233: reader_entry = vreader_list_get_next(reader_entry)) {
234: VReader *reader = vreader_list_get_reader(reader_entry);
235: vreader_id_t reader_id;
236: reader_id = vreader_get_id(reader);
237: if (reader_id == -1) {
238: continue;
239: }
240: /* be nice and signal card removal first (qemu probably should
241: * do this itself) */
242: if (vreader_card_is_present(reader) == VREADER_OK) {
243: send_msg(VSC_CardRemove, reader_id, NULL, 0);
244: }
245: send_msg(VSC_ReaderRemove, reader_id, NULL, 0);
246: }
247: exit(0);
248: } else if (strncmp(string, "insert", 6) == 0) {
249: if (string[6] == ' ') {
250: reader_id = get_id_from_string(&string[7], reader_id);
251: }
252: reader = vreader_get_reader_by_id(reader_id);
253: if (reader != NULL) {
254: error = vcard_emul_force_card_insert(reader);
255: printf("insert %s, returned %d\n",
256: reader ? vreader_get_name(reader)
257: : "invalid reader", error);
258: } else {
1.1.1.3 ! root 259: printf("no reader by id %u found\n", reader_id);
1.1 root 260: }
261: } else if (strncmp(string, "remove", 6) == 0) {
262: if (string[6] == ' ') {
263: reader_id = get_id_from_string(&string[7], reader_id);
264: }
265: reader = vreader_get_reader_by_id(reader_id);
266: if (reader != NULL) {
267: error = vcard_emul_force_card_remove(reader);
268: printf("remove %s, returned %d\n",
269: reader ? vreader_get_name(reader)
270: : "invalid reader", error);
271: } else {
1.1.1.3 ! root 272: printf("no reader by id %u found\n", reader_id);
1.1 root 273: }
274: } else if (strncmp(string, "select", 6) == 0) {
275: if (string[6] == ' ') {
276: reader_id = get_id_from_string(&string[7],
277: VSCARD_UNDEFINED_READER_ID);
278: }
279: if (reader_id != VSCARD_UNDEFINED_READER_ID) {
280: reader = vreader_get_reader_by_id(reader_id);
281: }
282: if (reader) {
1.1.1.3 ! root 283: printf("Selecting reader %u, %s\n", reader_id,
1.1 root 284: vreader_get_name(reader));
285: default_reader_id = reader_id;
286: } else {
1.1.1.3 ! root 287: printf("Reader with id %u not found\n", reader_id);
1.1 root 288: }
289: } else if (strncmp(string, "debug", 5) == 0) {
290: if (string[5] == ' ') {
291: verbose = get_id_from_string(&string[6], 0);
292: }
293: printf("debug level = %d\n", verbose);
294: } else if (strncmp(string, "list", 4) == 0) {
295: VReaderList *list = vreader_get_reader_list();
296: VReaderListEntry *reader_entry;
297: printf("Active Readers:\n");
298: for (reader_entry = vreader_list_get_first(list); reader_entry;
299: reader_entry = vreader_list_get_next(reader_entry)) {
300: VReader *reader = vreader_list_get_reader(reader_entry);
301: vreader_id_t reader_id;
302: reader_id = vreader_get_id(reader);
303: if (reader_id == -1) {
304: continue;
305: }
1.1.1.3 ! root 306: printf("%3u %s %s\n", reader_id,
1.1 root 307: vreader_card_is_present(reader) == VREADER_OK ?
308: "CARD_PRESENT" : " ",
309: vreader_get_name(reader));
310: }
311: printf("Inactive Readers:\n");
312: for (reader_entry = vreader_list_get_first(list); reader_entry;
313: reader_entry = vreader_list_get_next(reader_entry)) {
314: VReader *reader = vreader_list_get_reader(reader_entry);
315: vreader_id_t reader_id;
316: reader_id = vreader_get_id(reader);
317: if (reader_id != -1) {
318: continue;
319: }
320:
321: printf("INA %s %s\n",
322: vreader_card_is_present(reader) == VREADER_OK ?
323: "CARD_PRESENT" : " ",
324: vreader_get_name(reader));
325: }
326: } else if (*string != 0) {
327: printf("valid commands:\n");
328: printf("insert [reader_id]\n");
329: printf("remove [reader_id]\n");
330: printf("select reader_id\n");
331: printf("list\n");
332: printf("debug [level]\n");
333: printf("exit\n");
334: }
335: }
336: vreader_free(reader);
337: printf("> ");
338: fflush(stdout);
339: }
340:
341:
342: #define APDUBufSize 270
343:
344: /* just for ease of parsing command line arguments. */
345: #define MAX_CERTS 100
346:
347: static int
348: connect_to_qemu(
349: const char *host,
350: const char *port
351: ) {
352: struct addrinfo hints;
353: struct addrinfo *server;
354: int ret;
355:
356: sock = qemu_socket(AF_INET, SOCK_STREAM, 0);
357: if (sock < 0) {
358: /* Error */
359: fprintf(stderr, "Error opening socket!\n");
1.1.1.2 root 360: return -1;
1.1 root 361: }
362:
363: memset(&hints, 0, sizeof(struct addrinfo));
364: hints.ai_family = AF_UNSPEC;
365: hints.ai_socktype = SOCK_STREAM;
366: hints.ai_flags = 0;
367: hints.ai_protocol = 0; /* Any protocol */
368:
369: ret = getaddrinfo(host, port, &hints, &server);
370:
371: if (ret != 0) {
372: /* Error */
373: fprintf(stderr, "getaddrinfo failed\n");
1.1.1.2 root 374: return -1;
1.1 root 375: }
376:
377: if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
378: /* Error */
379: fprintf(stderr, "Could not connect\n");
1.1.1.2 root 380: return -1;
1.1 root 381: }
382: if (verbose) {
383: printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
384: }
385: return sock;
386: }
387:
388: static int on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
389: {
390: uint32_t *capabilities = (incoming->capabilities);
391: int num_capabilities =
392: 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
393: int i;
394: int rv;
395: pthread_t thread_id;
396:
397: incoming->version = ntohl(incoming->version);
398: if (incoming->version != VSCARD_VERSION) {
399: if (verbose > 0) {
400: printf("warning: host has version %d, we have %d\n",
401: verbose, VSCARD_VERSION);
402: }
403: }
404: if (incoming->magic != VSCARD_MAGIC) {
405: printf("unexpected magic: got %d, expected %d\n",
406: incoming->magic, VSCARD_MAGIC);
407: return -1;
408: }
409: for (i = 0 ; i < num_capabilities; ++i) {
410: capabilities[i] = ntohl(capabilities[i]);
411: }
412: /* Future: check capabilities */
413: /* remove whatever reader might be left in qemu,
414: * in case of an unclean previous exit. */
415: send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
416: /* launch the event_thread. This will trigger reader adds for all the
417: * existing readers */
418: rv = pthread_create(&thread_id, NULL, event_thread, NULL);
419: if (rv < 0) {
420: perror("pthread_create");
421: return rv;
422: }
423: return 0;
424: }
425:
426: int
427: main(
428: int argc,
429: char *argv[]
430: ) {
431: char *qemu_host;
432: char *qemu_port;
433: VSCMsgHeader mhHeader;
434: VSCMsgError *error_msg;
435:
436: int rv;
437: int dwSendLength;
438: int dwRecvLength;
439: uint8_t pbRecvBuffer[APDUBufSize];
440: uint8_t pbSendBuffer[APDUBufSize];
441: VReaderStatus reader_status;
442: VReader *reader = NULL;
443: VCardEmulOptions *command_line_options = NULL;
444:
445: char *cert_names[MAX_CERTS];
446: char *emul_args = NULL;
447: int cert_count = 0;
448: int c;
449:
450: while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
451: switch (c) {
452: case 'c':
453: if (cert_count >= MAX_CERTS) {
454: printf("too many certificates (max = %d)\n", MAX_CERTS);
455: exit(5);
456: }
457: cert_names[cert_count++] = optarg;
458: break;
459: case 'e':
460: emul_args = optarg;
461: break;
462: case 'p':
463: print_usage();
464: exit(4);
465: break;
466: case 'd':
467: verbose = get_id_from_string(optarg, 1);
468: break;
469: }
470: }
471:
472: if (argc - optind != 2) {
473: print_usage();
474: exit(4);
475: }
476:
477: if (cert_count > 0) {
478: char *new_args;
479: int len, i;
480: /* if we've given some -c options, we clearly we want do so some
481: * software emulation. add that emulation now. this is NSS Emulator
482: * specific */
483: if (emul_args == NULL) {
484: emul_args = (char *)"db=\"/etc/pki/nssdb\"";
485: }
486: #define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
487: /* 2 == close paren & null */
488: len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
489: for (i = 0; i < cert_count; i++) {
490: len += strlen(cert_names[i])+1; /* 1 == comma */
491: }
1.1.1.2 root 492: new_args = g_malloc(len);
1.1 root 493: strcpy(new_args, emul_args);
494: strcat(new_args, SOFT_STRING);
495: for (i = 0; i < cert_count; i++) {
496: strcat(new_args, cert_names[i]);
497: strcat(new_args, ",");
498: }
499: strcat(new_args, ")");
500: emul_args = new_args;
501: }
502: if (emul_args) {
503: command_line_options = vcard_emul_options(emul_args);
504: }
505:
506: qemu_host = strdup(argv[argc - 2]);
507: qemu_port = strdup(argv[argc - 1]);
508: sock = connect_to_qemu(qemu_host, qemu_port);
1.1.1.2 root 509: if (sock == -1) {
510: fprintf(stderr, "error opening socket, exiting.\n");
511: exit(5);
512: }
1.1 root 513:
514: qemu_mutex_init(&write_lock);
515: qemu_mutex_init(&pending_reader_lock);
516: qemu_cond_init(&pending_reader_condition);
517:
518: vcard_emul_init(command_line_options);
519:
520: printf("> ");
521: fflush(stdout);
522:
523: /* Send init message, Host responds (and then we send reader attachments) */
524: VSCMsgInit init = {
525: .version = htonl(VSCARD_VERSION),
526: .magic = VSCARD_MAGIC,
527: .capabilities = {0}
528: };
529: send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init));
530:
531: do {
532: fd_set fds;
533:
534: FD_ZERO(&fds);
535: FD_SET(1, &fds);
536: FD_SET(sock, &fds);
537:
538: /* waiting on input from the socket */
539: rv = select(sock+1, &fds, NULL, NULL, NULL);
540: if (rv < 0) {
541: /* handle error */
542: perror("select");
543: return 7;
544: }
545: if (FD_ISSET(1, &fds)) {
546: do_command();
547: }
548: if (!FD_ISSET(sock, &fds)) {
549: continue;
550: }
551:
552: rv = read(sock, &mhHeader, sizeof(mhHeader));
553: if (rv < sizeof(mhHeader)) {
554: /* Error */
555: if (rv < 0) {
556: perror("header read error\n");
557: } else {
558: fprintf(stderr, "header short read %d\n", rv);
559: }
560: return 8;
561: }
562: mhHeader.type = ntohl(mhHeader.type);
563: mhHeader.reader_id = ntohl(mhHeader.reader_id);
564: mhHeader.length = ntohl(mhHeader.length);
565: if (verbose) {
1.1.1.3 ! root 566: printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
1.1 root 567: mhHeader.type, mhHeader.reader_id, mhHeader.length,
568: mhHeader.length);
569: }
570: switch (mhHeader.type) {
571: case VSC_APDU:
572: case VSC_Flush:
573: case VSC_Error:
574: case VSC_Init:
575: rv = read(sock, pbSendBuffer, mhHeader.length);
576: break;
577: default:
578: fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
579: return 0;
580: }
581: switch (mhHeader.type) {
582: case VSC_APDU:
583: if (rv < 0) {
584: /* Error */
585: fprintf(stderr, "read error\n");
586: close(sock);
587: return 8;
588: }
589: if (verbose) {
590: printf(" recv APDU: ");
591: print_byte_array(pbSendBuffer, mhHeader.length);
592: }
1.1.1.2 root 593: /* Transmit received APDU */
1.1 root 594: dwSendLength = mhHeader.length;
595: dwRecvLength = sizeof(pbRecvBuffer);
596: reader = vreader_get_reader_by_id(mhHeader.reader_id);
597: reader_status = vreader_xfr_bytes(reader,
598: pbSendBuffer, dwSendLength,
599: pbRecvBuffer, &dwRecvLength);
600: if (reader_status == VREADER_OK) {
601: mhHeader.length = dwRecvLength;
602: if (verbose) {
603: printf(" send response: ");
604: print_byte_array(pbRecvBuffer, mhHeader.length);
605: }
606: send_msg(VSC_APDU, mhHeader.reader_id,
607: pbRecvBuffer, dwRecvLength);
608: } else {
609: rv = reader_status; /* warning: not meaningful */
610: send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
611: }
612: vreader_free(reader);
613: reader = NULL; /* we've freed it, don't use it by accident
614: again */
615: break;
616: case VSC_Flush:
617: /* TODO: actually flush */
618: send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
619: break;
620: case VSC_Error:
621: error_msg = (VSCMsgError *) pbSendBuffer;
622: if (error_msg->code == VSC_SUCCESS) {
623: qemu_mutex_lock(&pending_reader_lock);
624: if (pending_reader) {
625: vreader_set_id(pending_reader, mhHeader.reader_id);
626: vreader_free(pending_reader);
627: pending_reader = NULL;
628: qemu_cond_signal(&pending_reader_condition);
629: }
630: qemu_mutex_unlock(&pending_reader_lock);
631: break;
632: }
633: printf("warning: qemu refused to add reader\n");
634: if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
635: /* clear pending reader, qemu can't handle any more */
636: qemu_mutex_lock(&pending_reader_lock);
637: if (pending_reader) {
638: pending_reader = NULL;
639: /* make sure the event loop doesn't hang */
640: qemu_cond_signal(&pending_reader_condition);
641: }
642: qemu_mutex_unlock(&pending_reader_lock);
643: }
644: break;
645: case VSC_Init:
646: if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
647: return -1;
648: }
649: break;
650: default:
651: printf("Default\n");
652: return 0;
653: }
654: } while (rv >= 0);
655:
656: return 0;
657: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.