Statistics
| Branch: | Revision:

root / libcacard / vscclient.c @ 2ac85b93

History | View | Annotate | Download (20.5 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_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
}