|
|
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) {
69: printf("sending type=%d id=%d, len =%d (0x%x)\n",
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: }
132: /* this reader hasn't been told it's status from qemu yet, wait for
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) {
170: printf(" READER REMOVE: %d\n", reader_id);
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) {
181: printf(" CARD INSERT %d: ", reader_id);
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) {
189: printf(" CARD REMOVE %d:\n", reader_id);
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 {
259: printf("no reader by id %d found\n", reader_id);
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 {
272: printf("no reader by id %d found\n", reader_id);
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) {
283: printf("Selecting reader %d, %s\n", reader_id,
284: vreader_get_name(reader));
285: default_reader_id = reader_id;
286: } else {
287: printf("Reader with id %d not found\n", reader_id);
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: }
306: printf("%3d %s %s\n", reader_id,
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");
360: }
361:
362: memset(&hints, 0, sizeof(struct addrinfo));
363: hints.ai_family = AF_UNSPEC;
364: hints.ai_socktype = SOCK_STREAM;
365: hints.ai_flags = 0;
366: hints.ai_protocol = 0; /* Any protocol */
367:
368: ret = getaddrinfo(host, port, &hints, &server);
369:
370: if (ret != 0) {
371: /* Error */
372: fprintf(stderr, "getaddrinfo failed\n");
373: return 5;
374: }
375:
376: if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
377: /* Error */
378: fprintf(stderr, "Could not connect\n");
379: return 5;
380: }
381: if (verbose) {
382: printf("Connected (sizeof Header=%zd)!\n", sizeof(VSCMsgHeader));
383: }
384: return sock;
385: }
386:
387: static int on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
388: {
389: uint32_t *capabilities = (incoming->capabilities);
390: int num_capabilities =
391: 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
392: int i;
393: int rv;
394: pthread_t thread_id;
395:
396: incoming->version = ntohl(incoming->version);
397: if (incoming->version != VSCARD_VERSION) {
398: if (verbose > 0) {
399: printf("warning: host has version %d, we have %d\n",
400: verbose, VSCARD_VERSION);
401: }
402: }
403: if (incoming->magic != VSCARD_MAGIC) {
404: printf("unexpected magic: got %d, expected %d\n",
405: incoming->magic, VSCARD_MAGIC);
406: return -1;
407: }
408: for (i = 0 ; i < num_capabilities; ++i) {
409: capabilities[i] = ntohl(capabilities[i]);
410: }
411: /* Future: check capabilities */
412: /* remove whatever reader might be left in qemu,
413: * in case of an unclean previous exit. */
414: send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
415: /* launch the event_thread. This will trigger reader adds for all the
416: * existing readers */
417: rv = pthread_create(&thread_id, NULL, event_thread, NULL);
418: if (rv < 0) {
419: perror("pthread_create");
420: return rv;
421: }
422: return 0;
423: }
424:
425: int
426: main(
427: int argc,
428: char *argv[]
429: ) {
430: char *qemu_host;
431: char *qemu_port;
432: VSCMsgHeader mhHeader;
433: VSCMsgError *error_msg;
434:
435: int rv;
436: int dwSendLength;
437: int dwRecvLength;
438: uint8_t pbRecvBuffer[APDUBufSize];
439: uint8_t pbSendBuffer[APDUBufSize];
440: VReaderStatus reader_status;
441: VReader *reader = NULL;
442: VCardEmulOptions *command_line_options = NULL;
443:
444: char *cert_names[MAX_CERTS];
445: char *emul_args = NULL;
446: int cert_count = 0;
447: int c;
448:
449: while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
450: switch (c) {
451: case 'c':
452: if (cert_count >= MAX_CERTS) {
453: printf("too many certificates (max = %d)\n", MAX_CERTS);
454: exit(5);
455: }
456: cert_names[cert_count++] = optarg;
457: break;
458: case 'e':
459: emul_args = optarg;
460: break;
461: case 'p':
462: print_usage();
463: exit(4);
464: break;
465: case 'd':
466: verbose = get_id_from_string(optarg, 1);
467: break;
468: }
469: }
470:
471: if (argc - optind != 2) {
472: print_usage();
473: exit(4);
474: }
475:
476: if (cert_count > 0) {
477: char *new_args;
478: int len, i;
479: /* if we've given some -c options, we clearly we want do so some
480: * software emulation. add that emulation now. this is NSS Emulator
481: * specific */
482: if (emul_args == NULL) {
483: emul_args = (char *)"db=\"/etc/pki/nssdb\"";
484: }
485: #define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
486: /* 2 == close paren & null */
487: len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
488: for (i = 0; i < cert_count; i++) {
489: len += strlen(cert_names[i])+1; /* 1 == comma */
490: }
491: new_args = qemu_malloc(len);
492: strcpy(new_args, emul_args);
493: strcat(new_args, SOFT_STRING);
494: for (i = 0; i < cert_count; i++) {
495: strcat(new_args, cert_names[i]);
496: strcat(new_args, ",");
497: }
498: strcat(new_args, ")");
499: emul_args = new_args;
500: }
501: if (emul_args) {
502: command_line_options = vcard_emul_options(emul_args);
503: }
504:
505: qemu_host = strdup(argv[argc - 2]);
506: qemu_port = strdup(argv[argc - 1]);
507: sock = connect_to_qemu(qemu_host, qemu_port);
508:
509: qemu_mutex_init(&write_lock);
510: qemu_mutex_init(&pending_reader_lock);
511: qemu_cond_init(&pending_reader_condition);
512:
513: vcard_emul_init(command_line_options);
514:
515: printf("> ");
516: fflush(stdout);
517:
518: /* Send init message, Host responds (and then we send reader attachments) */
519: VSCMsgInit init = {
520: .version = htonl(VSCARD_VERSION),
521: .magic = VSCARD_MAGIC,
522: .capabilities = {0}
523: };
524: send_msg(VSC_Init, mhHeader.reader_id, &init, sizeof(init));
525:
526: do {
527: fd_set fds;
528:
529: FD_ZERO(&fds);
530: FD_SET(1, &fds);
531: FD_SET(sock, &fds);
532:
533: /* waiting on input from the socket */
534: rv = select(sock+1, &fds, NULL, NULL, NULL);
535: if (rv < 0) {
536: /* handle error */
537: perror("select");
538: return 7;
539: }
540: if (FD_ISSET(1, &fds)) {
541: do_command();
542: }
543: if (!FD_ISSET(sock, &fds)) {
544: continue;
545: }
546:
547: rv = read(sock, &mhHeader, sizeof(mhHeader));
548: if (rv < sizeof(mhHeader)) {
549: /* Error */
550: if (rv < 0) {
551: perror("header read error\n");
552: } else {
553: fprintf(stderr, "header short read %d\n", rv);
554: }
555: return 8;
556: }
557: mhHeader.type = ntohl(mhHeader.type);
558: mhHeader.reader_id = ntohl(mhHeader.reader_id);
559: mhHeader.length = ntohl(mhHeader.length);
560: if (verbose) {
561: printf("Header: type=%d, reader_id=%d length=%d (0x%x)\n",
562: mhHeader.type, mhHeader.reader_id, mhHeader.length,
563: mhHeader.length);
564: }
565: switch (mhHeader.type) {
566: case VSC_APDU:
567: case VSC_Flush:
568: case VSC_Error:
569: case VSC_Init:
570: rv = read(sock, pbSendBuffer, mhHeader.length);
571: break;
572: default:
573: fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
574: return 0;
575: }
576: switch (mhHeader.type) {
577: case VSC_APDU:
578: if (rv < 0) {
579: /* Error */
580: fprintf(stderr, "read error\n");
581: close(sock);
582: return 8;
583: }
584: if (verbose) {
585: printf(" recv APDU: ");
586: print_byte_array(pbSendBuffer, mhHeader.length);
587: }
588: /* Transmit recieved APDU */
589: dwSendLength = mhHeader.length;
590: dwRecvLength = sizeof(pbRecvBuffer);
591: reader = vreader_get_reader_by_id(mhHeader.reader_id);
592: reader_status = vreader_xfr_bytes(reader,
593: pbSendBuffer, dwSendLength,
594: pbRecvBuffer, &dwRecvLength);
595: if (reader_status == VREADER_OK) {
596: mhHeader.length = dwRecvLength;
597: if (verbose) {
598: printf(" send response: ");
599: print_byte_array(pbRecvBuffer, mhHeader.length);
600: }
601: send_msg(VSC_APDU, mhHeader.reader_id,
602: pbRecvBuffer, dwRecvLength);
603: } else {
604: rv = reader_status; /* warning: not meaningful */
605: send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
606: }
607: vreader_free(reader);
608: reader = NULL; /* we've freed it, don't use it by accident
609: again */
610: break;
611: case VSC_Flush:
612: /* TODO: actually flush */
613: send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
614: break;
615: case VSC_Error:
616: error_msg = (VSCMsgError *) pbSendBuffer;
617: if (error_msg->code == VSC_SUCCESS) {
618: qemu_mutex_lock(&pending_reader_lock);
619: if (pending_reader) {
620: vreader_set_id(pending_reader, mhHeader.reader_id);
621: vreader_free(pending_reader);
622: pending_reader = NULL;
623: qemu_cond_signal(&pending_reader_condition);
624: }
625: qemu_mutex_unlock(&pending_reader_lock);
626: break;
627: }
628: printf("warning: qemu refused to add reader\n");
629: if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
630: /* clear pending reader, qemu can't handle any more */
631: qemu_mutex_lock(&pending_reader_lock);
632: if (pending_reader) {
633: pending_reader = NULL;
634: /* make sure the event loop doesn't hang */
635: qemu_cond_signal(&pending_reader_condition);
636: }
637: qemu_mutex_unlock(&pending_reader_lock);
638: }
639: break;
640: case VSC_Init:
641: if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
642: return -1;
643: }
644: break;
645: default:
646: printf("Default\n");
647: return 0;
648: }
649: } while (rv >= 0);
650:
651: return 0;
652: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.