Statistics
| Branch: | Revision:

root / libcacard / vscclient.c @ 26ca8c06

History | View | Annotate | Download (20.6 kB)

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/sockets.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=%u, len =%u (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 its 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: %u\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 %u: ", 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 %u:\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 %u 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 %u 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 %u, %s\n", reader_id,
284
                        vreader_get_name(reader));
285
                default_reader_id = reader_id;
286
            } else {
287
                printf("Reader with id %u 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("%3u %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
        return -1;
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");
374
        return -1;
375
    }
376

    
377
    if (connect(sock, server->ai_addr, server->ai_addrlen) < 0) {
378
        /* Error */
379
        fprintf(stderr, "Could not connect\n");
380
        return -1;
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
        }
492
        new_args = g_malloc(len);
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);
509
    if (sock == -1) {
510
        fprintf(stderr, "error opening socket, exiting.\n");
511
        exit(5);
512
    }
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) {
566
            printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
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
            }
593
            /* Transmit received APDU */
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
}