qemu

Форк
0
/
ccid-card-emulated.c 
623 строки · 19.3 Кб
1
/*
2
 * CCID Card Device. Emulated card.
3
 *
4
 * Copyright (c) 2011 Red Hat.
5
 * Written by Alon Levy.
6
 *
7
 * This code is licensed under the GNU LGPL, version 2 or later.
8
 */
9

10
/*
11
 * It can be used to provide access to the local hardware in a non exclusive
12
 * way, or it can use certificates. It requires the usb-ccid bus.
13
 *
14
 * Usage 1: standard, mirror hardware reader+card:
15
 * qemu .. -usb -device usb-ccid -device ccid-card-emulated
16
 *
17
 * Usage 2: use certificates, no hardware required
18
 * one time: create the certificates:
19
 *  for i in 1 2 3; do
20
 *      certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
21
 *  done
22
 * qemu .. -usb -device usb-ccid \
23
 *  -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
24
 *
25
 * If you use a non default db for the certificates you can specify it using
26
 * the db parameter.
27
 */
28

29
#include "qemu/osdep.h"
30
#include <libcacard.h>
31

32
#include "qemu/thread.h"
33
#include "qemu/lockable.h"
34
#include "qemu/main-loop.h"
35
#include "qemu/module.h"
36
#include "ccid.h"
37
#include "hw/qdev-properties.h"
38
#include "qapi/error.h"
39
#include "qom/object.h"
40

41
#define DPRINTF(card, lvl, fmt, ...) \
42
do {\
43
    if (lvl <= card->debug) {\
44
        printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\
45
    } \
46
} while (0)
47

48

49
#define TYPE_EMULATED_CCID "ccid-card-emulated"
50
typedef struct EmulatedState EmulatedState;
51
DECLARE_INSTANCE_CHECKER(EmulatedState, EMULATED_CCID_CARD,
52
                         TYPE_EMULATED_CCID)
53

54
#define BACKEND_NSS_EMULATED_NAME "nss-emulated"
55
#define BACKEND_CERTIFICATES_NAME "certificates"
56

57
enum {
58
    BACKEND_NSS_EMULATED = 1,
59
    BACKEND_CERTIFICATES
60
};
61

62
#define DEFAULT_BACKEND BACKEND_NSS_EMULATED
63

64

65
enum {
66
    EMUL_READER_INSERT = 0,
67
    EMUL_READER_REMOVE,
68
    EMUL_CARD_INSERT,
69
    EMUL_CARD_REMOVE,
70
    EMUL_GUEST_APDU,
71
    EMUL_RESPONSE_APDU,
72
    EMUL_ERROR,
73
};
74

75
static const char *emul_event_to_string(uint32_t emul_event)
76
{
77
    switch (emul_event) {
78
    case EMUL_READER_INSERT:
79
        return "EMUL_READER_INSERT";
80
    case EMUL_READER_REMOVE:
81
        return "EMUL_READER_REMOVE";
82
    case EMUL_CARD_INSERT:
83
        return "EMUL_CARD_INSERT";
84
    case EMUL_CARD_REMOVE:
85
        return "EMUL_CARD_REMOVE";
86
    case EMUL_GUEST_APDU:
87
        return "EMUL_GUEST_APDU";
88
    case EMUL_RESPONSE_APDU:
89
        return "EMUL_RESPONSE_APDU";
90
    case EMUL_ERROR:
91
        return "EMUL_ERROR";
92
    }
93
    return "UNKNOWN";
94
}
95

96
typedef struct EmulEvent {
97
    QSIMPLEQ_ENTRY(EmulEvent) entry;
98
    union {
99
        struct {
100
            uint32_t type;
101
        } gen;
102
        struct {
103
            uint32_t type;
104
            uint64_t code;
105
        } error;
106
        struct {
107
            uint32_t type;
108
            uint32_t len;
109
            uint8_t data[];
110
        } data;
111
    } p;
112
} EmulEvent;
113

114
#define MAX_ATR_SIZE 40
115
struct EmulatedState {
116
    CCIDCardState base;
117
    uint8_t  debug;
118
    char    *backend_str;
119
    uint32_t backend;
120
    char    *cert1;
121
    char    *cert2;
122
    char    *cert3;
123
    char    *db;
124
    uint8_t  atr[MAX_ATR_SIZE];
125
    uint8_t  atr_length;
126
    QSIMPLEQ_HEAD(, EmulEvent) event_list;
127
    QemuMutex event_list_mutex;
128
    QemuThread event_thread_id;
129
    VReader *reader;
130
    QSIMPLEQ_HEAD(, EmulEvent) guest_apdu_list;
131
    QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
132
    QemuMutex handle_apdu_mutex;
133
    QemuCond handle_apdu_cond;
134
    EventNotifier notifier;
135
    int      quit_apdu_thread;
136
    QemuThread apdu_thread_id;
137
};
138

139
static void emulated_apdu_from_guest(CCIDCardState *base,
140
    const uint8_t *apdu, uint32_t len)
141
{
142
    EmulatedState *card = EMULATED_CCID_CARD(base);
143
    EmulEvent *event = g_malloc(sizeof(EmulEvent) + len);
144

145
    assert(event);
146
    event->p.data.type = EMUL_GUEST_APDU;
147
    event->p.data.len = len;
148
    memcpy(event->p.data.data, apdu, len);
149
    qemu_mutex_lock(&card->vreader_mutex);
150
    QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
151
    qemu_mutex_unlock(&card->vreader_mutex);
152
    qemu_mutex_lock(&card->handle_apdu_mutex);
153
    qemu_cond_signal(&card->handle_apdu_cond);
154
    qemu_mutex_unlock(&card->handle_apdu_mutex);
155
}
156

157
static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
158
{
159
    EmulatedState *card = EMULATED_CCID_CARD(base);
160

161
    *len = card->atr_length;
162
    return card->atr;
163
}
164

165
static void emulated_push_event(EmulatedState *card, EmulEvent *event)
166
{
167
    qemu_mutex_lock(&card->event_list_mutex);
168
    QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
169
    qemu_mutex_unlock(&card->event_list_mutex);
170
    event_notifier_set(&card->notifier);
171
}
172

173
static void emulated_push_type(EmulatedState *card, uint32_t type)
174
{
175
    EmulEvent *event = g_new(EmulEvent, 1);
176

177
    assert(event);
178
    event->p.gen.type = type;
179
    emulated_push_event(card, event);
180
}
181

182
static void emulated_push_error(EmulatedState *card, uint64_t code)
183
{
184
    EmulEvent *event = g_new(EmulEvent, 1);
185

186
    assert(event);
187
    event->p.error.type = EMUL_ERROR;
188
    event->p.error.code = code;
189
    emulated_push_event(card, event);
190
}
191

192
static void emulated_push_data_type(EmulatedState *card, uint32_t type,
193
    const uint8_t *data, uint32_t len)
194
{
195
    EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
196

197
    assert(event);
198
    event->p.data.type = type;
199
    event->p.data.len = len;
200
    memcpy(event->p.data.data, data, len);
201
    emulated_push_event(card, event);
202
}
203

204
static void emulated_push_reader_insert(EmulatedState *card)
205
{
206
    emulated_push_type(card, EMUL_READER_INSERT);
207
}
208

209
static void emulated_push_reader_remove(EmulatedState *card)
210
{
211
    emulated_push_type(card, EMUL_READER_REMOVE);
212
}
213

214
static void emulated_push_card_insert(EmulatedState *card,
215
    const uint8_t *atr, uint32_t len)
216
{
217
    emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
218
}
219

220
static void emulated_push_card_remove(EmulatedState *card)
221
{
222
    emulated_push_type(card, EMUL_CARD_REMOVE);
223
}
224

225
static void emulated_push_response_apdu(EmulatedState *card,
226
    const uint8_t *apdu, uint32_t len)
227
{
228
    emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
229
}
230

231
#define APDU_BUF_SIZE 270
232
static void *handle_apdu_thread(void* arg)
233
{
234
    EmulatedState *card = arg;
235
    uint8_t recv_data[APDU_BUF_SIZE];
236
    int recv_len;
237
    VReaderStatus reader_status;
238
    EmulEvent *event;
239

240
    while (1) {
241
        qemu_mutex_lock(&card->handle_apdu_mutex);
242
        qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
243
        qemu_mutex_unlock(&card->handle_apdu_mutex);
244
        if (card->quit_apdu_thread) {
245
            card->quit_apdu_thread = 0; /* debugging */
246
            break;
247
        }
248
        WITH_QEMU_LOCK_GUARD(&card->vreader_mutex) {
249
            while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
250
                event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
251
                assert(event != NULL);
252
                QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
253
                if (event->p.data.type != EMUL_GUEST_APDU) {
254
                    DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
255
                    g_free(event);
256
                    continue;
257
                }
258
                if (card->reader == NULL) {
259
                    DPRINTF(card, 1, "reader is NULL\n");
260
                    g_free(event);
261
                    continue;
262
                }
263
                recv_len = sizeof(recv_data);
264
                reader_status = vreader_xfr_bytes(card->reader,
265
                        event->p.data.data, event->p.data.len,
266
                        recv_data, &recv_len);
267
                DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
268
                if (reader_status == VREADER_OK) {
269
                    emulated_push_response_apdu(card, recv_data, recv_len);
270
                } else {
271
                    emulated_push_error(card, reader_status);
272
                }
273
                g_free(event);
274
            }
275
        }
276
    }
277
    return NULL;
278
}
279

280
static void *event_thread(void *arg)
281
{
282
    int atr_len = MAX_ATR_SIZE;
283
    uint8_t atr[MAX_ATR_SIZE];
284
    VEvent *event = NULL;
285
    EmulatedState *card = arg;
286

287
    while (1) {
288
        const char *reader_name;
289

290
        event = vevent_wait_next_vevent();
291
        if (event == NULL || event->type == VEVENT_LAST) {
292
            break;
293
        }
294
        if (event->type != VEVENT_READER_INSERT) {
295
            if (card->reader == NULL && event->reader != NULL) {
296
                /* Happens after device_add followed by card remove or insert.
297
                 * XXX: create synthetic add_reader events if vcard_emul_init
298
                 * already called, which happens if device_del and device_add
299
                 * are called */
300
                card->reader = vreader_reference(event->reader);
301
            } else {
302
                if (event->reader != card->reader) {
303
                    fprintf(stderr,
304
                        "ERROR: wrong reader: quitting event_thread\n");
305
                    break;
306
                }
307
            }
308
        }
309
        switch (event->type) {
310
        case VEVENT_READER_INSERT:
311
            /* TODO: take a specific reader. i.e. track which reader
312
             * we are seeing here, check it is the one we want (the first,
313
             * or by a particular name), and ignore if we don't want it.
314
             */
315
            reader_name = vreader_get_name(event->reader);
316
            if (card->reader != NULL) {
317
                DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
318
                    vreader_get_name(card->reader), reader_name);
319
                qemu_mutex_lock(&card->vreader_mutex);
320
                vreader_free(card->reader);
321
                qemu_mutex_unlock(&card->vreader_mutex);
322
                emulated_push_reader_remove(card);
323
            }
324
            qemu_mutex_lock(&card->vreader_mutex);
325
            DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
326
            card->reader = vreader_reference(event->reader);
327
            qemu_mutex_unlock(&card->vreader_mutex);
328
            emulated_push_reader_insert(card);
329
            break;
330
        case VEVENT_READER_REMOVE:
331
            DPRINTF(card, 2, " READER REMOVE: %s\n",
332
                    vreader_get_name(event->reader));
333
            qemu_mutex_lock(&card->vreader_mutex);
334
            vreader_free(card->reader);
335
            card->reader = NULL;
336
            qemu_mutex_unlock(&card->vreader_mutex);
337
            emulated_push_reader_remove(card);
338
            break;
339
        case VEVENT_CARD_INSERT:
340
            /* get the ATR (intended as a response to a power on from the
341
             * reader */
342
            atr_len = MAX_ATR_SIZE;
343
            vreader_power_on(event->reader, atr, &atr_len);
344
            card->atr_length = (uint8_t)atr_len;
345
            DPRINTF(card, 2, " CARD INSERT\n");
346
            emulated_push_card_insert(card, atr, atr_len);
347
            break;
348
        case VEVENT_CARD_REMOVE:
349
            DPRINTF(card, 2, " CARD REMOVE\n");
350
            emulated_push_card_remove(card);
351
            break;
352
        case VEVENT_LAST: /* quit */
353
            vevent_delete(event);
354
            return NULL;
355
        default:
356
            break;
357
        }
358
        vevent_delete(event);
359
    }
360
    return NULL;
361
}
362

363
static void card_event_handler(EventNotifier *notifier)
364
{
365
    EmulatedState *card = container_of(notifier, EmulatedState, notifier);
366
    EmulEvent *event, *next;
367

368
    event_notifier_test_and_clear(&card->notifier);
369
    QEMU_LOCK_GUARD(&card->event_list_mutex);
370
    QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
371
        DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
372
        switch (event->p.gen.type) {
373
        case EMUL_RESPONSE_APDU:
374
            ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
375
                event->p.data.len);
376
            break;
377
        case EMUL_READER_INSERT:
378
            ccid_card_ccid_attach(&card->base);
379
            break;
380
        case EMUL_READER_REMOVE:
381
            ccid_card_ccid_detach(&card->base);
382
            break;
383
        case EMUL_CARD_INSERT:
384
            assert(event->p.data.len <= MAX_ATR_SIZE);
385
            card->atr_length = event->p.data.len;
386
            memcpy(card->atr, event->p.data.data, card->atr_length);
387
            ccid_card_card_inserted(&card->base);
388
            break;
389
        case EMUL_CARD_REMOVE:
390
            ccid_card_card_removed(&card->base);
391
            break;
392
        case EMUL_ERROR:
393
            ccid_card_card_error(&card->base, event->p.error.code);
394
            break;
395
        default:
396
            DPRINTF(card, 2, "unexpected event\n");
397
            break;
398
        }
399
        g_free(event);
400
    }
401
    QSIMPLEQ_INIT(&card->event_list);
402
}
403

404
static int init_event_notifier(EmulatedState *card, Error **errp)
405
{
406
    if (event_notifier_init(&card->notifier, false) < 0) {
407
        error_setg(errp, "ccid-card-emul: event notifier creation failed");
408
        return -1;
409
    }
410
    event_notifier_set_handler(&card->notifier, card_event_handler);
411
    return 0;
412
}
413

414
static void clean_event_notifier(EmulatedState *card)
415
{
416
    event_notifier_set_handler(&card->notifier, NULL);
417
    event_notifier_cleanup(&card->notifier);
418
}
419

420
#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
421
#define CERTIFICATES_ARGS_TEMPLATE\
422
    "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
423

424
static int wrap_vcard_emul_init(VCardEmulOptions *options)
425
{
426
    static int called;
427
    static int options_was_null;
428

429
    if (called) {
430
        if ((options == NULL) != options_was_null) {
431
            printf("%s: warning: running emulated with certificates"
432
                   " and emulated side by side is not supported\n",
433
                   __func__);
434
            return VCARD_EMUL_FAIL;
435
        }
436
        vcard_emul_replay_insertion_events();
437
        return VCARD_EMUL_OK;
438
    }
439
    options_was_null = (options == NULL);
440
    called = 1;
441
    return vcard_emul_init(options);
442
}
443

444
static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
445
{
446
    char emul_args[200];
447
    VCardEmulOptions *options = NULL;
448

449
    snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
450
        card->db ? card->db : CERTIFICATES_DEFAULT_DB,
451
        card->cert1, card->cert2, card->cert3);
452
    options = vcard_emul_options(emul_args);
453
    if (options == NULL) {
454
        printf("%s: warning: not using certificates due to"
455
               " initialization error\n", __func__);
456
    }
457
    return wrap_vcard_emul_init(options);
458
}
459

460
typedef struct EnumTable {
461
    const char *name;
462
    uint32_t value;
463
} EnumTable;
464

465
static const EnumTable backend_enum_table[] = {
466
    {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
467
    {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
468
    {NULL, 0},
469
};
470

471
static uint32_t parse_enumeration(char *str,
472
    const EnumTable *table, uint32_t not_found_value)
473
{
474
    uint32_t ret = not_found_value;
475

476
    if (str == NULL)
477
        return 0;
478

479
    while (table->name != NULL) {
480
        if (strcmp(table->name, str) == 0) {
481
            ret = table->value;
482
            break;
483
        }
484
        table++;
485
    }
486
    return ret;
487
}
488

489
static void emulated_realize(CCIDCardState *base, Error **errp)
490
{
491
    EmulatedState *card = EMULATED_CCID_CARD(base);
492
    VCardEmulError ret;
493
    const EnumTable *ptable;
494

495
    QSIMPLEQ_INIT(&card->event_list);
496
    QSIMPLEQ_INIT(&card->guest_apdu_list);
497
    qemu_mutex_init(&card->event_list_mutex);
498
    qemu_mutex_init(&card->vreader_mutex);
499
    qemu_mutex_init(&card->handle_apdu_mutex);
500
    qemu_cond_init(&card->handle_apdu_cond);
501
    card->reader = NULL;
502
    card->quit_apdu_thread = 0;
503
    if (init_event_notifier(card, errp) < 0) {
504
        goto out1;
505
    }
506

507
    card->backend = 0;
508
    if (card->backend_str) {
509
        card->backend = parse_enumeration(card->backend_str,
510
                                          backend_enum_table, 0);
511
    }
512

513
    if (card->backend == 0) {
514
        error_setg(errp, "backend must be one of:");
515
        for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
516
            error_append_hint(errp, "%s\n", ptable->name);
517
        }
518
        goto out2;
519
    }
520

521
    /* TODO: a passthru backend that works on local machine. third card type?*/
522
    if (card->backend == BACKEND_CERTIFICATES) {
523
        if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
524
            ret = emulated_initialize_vcard_from_certificates(card);
525
        } else {
526
            error_setg(errp, "%s: you must provide all three certs for"
527
                       " certificates backend", TYPE_EMULATED_CCID);
528
            goto out2;
529
        }
530
    } else {
531
        if (card->backend != BACKEND_NSS_EMULATED) {
532
            error_setg(errp, "%s: bad backend specified. The options are:%s"
533
                       " (default), %s.", TYPE_EMULATED_CCID,
534
                       BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME);
535
            goto out2;
536
        }
537
        if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
538
            error_setg(errp, "%s: unexpected cert parameters to nss emulated "
539
                       "backend", TYPE_EMULATED_CCID);
540
            goto out2;
541
        }
542
        /* default to mirroring the local hardware readers */
543
        ret = wrap_vcard_emul_init(NULL);
544
    }
545
    if (ret != VCARD_EMUL_OK) {
546
        error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
547
        goto out2;
548
    }
549
    qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
550
                       card, QEMU_THREAD_JOINABLE);
551
    qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
552
                       card, QEMU_THREAD_JOINABLE);
553

554
    return;
555

556
out2:
557
    clean_event_notifier(card);
558
out1:
559
    qemu_cond_destroy(&card->handle_apdu_cond);
560
    qemu_mutex_destroy(&card->handle_apdu_mutex);
561
    qemu_mutex_destroy(&card->vreader_mutex);
562
    qemu_mutex_destroy(&card->event_list_mutex);
563
}
564

565
static void emulated_unrealize(CCIDCardState *base)
566
{
567
    EmulatedState *card = EMULATED_CCID_CARD(base);
568
    VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
569

570
    vevent_queue_vevent(vevent); /* stop vevent thread */
571
    qemu_thread_join(&card->event_thread_id);
572

573
    card->quit_apdu_thread = 1; /* stop handle_apdu thread */
574
    qemu_cond_signal(&card->handle_apdu_cond);
575
    qemu_thread_join(&card->apdu_thread_id);
576

577
    clean_event_notifier(card);
578
    /* threads exited, can destroy all condvars/mutexes */
579
    qemu_cond_destroy(&card->handle_apdu_cond);
580
    qemu_mutex_destroy(&card->handle_apdu_mutex);
581
    qemu_mutex_destroy(&card->vreader_mutex);
582
    qemu_mutex_destroy(&card->event_list_mutex);
583
}
584

585
static Property emulated_card_properties[] = {
586
    DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
587
    DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
588
    DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
589
    DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
590
    DEFINE_PROP_STRING("db", EmulatedState, db),
591
    DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
592
    DEFINE_PROP_END_OF_LIST(),
593
};
594

595
static void emulated_class_initfn(ObjectClass *klass, void *data)
596
{
597
    DeviceClass *dc = DEVICE_CLASS(klass);
598
    CCIDCardClass *cc = CCID_CARD_CLASS(klass);
599

600
    cc->realize = emulated_realize;
601
    cc->unrealize = emulated_unrealize;
602
    cc->get_atr = emulated_get_atr;
603
    cc->apdu_from_guest = emulated_apdu_from_guest;
604
    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
605
    dc->desc = "emulated smartcard";
606
    device_class_set_props(dc, emulated_card_properties);
607
}
608

609
static const TypeInfo emulated_card_info = {
610
    .name          = TYPE_EMULATED_CCID,
611
    .parent        = TYPE_CCID_CARD,
612
    .instance_size = sizeof(EmulatedState),
613
    .class_init    = emulated_class_initfn,
614
};
615
module_obj(TYPE_EMULATED_CCID);
616
module_kconfig(USB);
617

618
static void ccid_card_emulated_register_types(void)
619
{
620
    type_register_static(&emulated_card_info);
621
}
622

623
type_init(ccid_card_emulated_register_types)
624

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.