xref: /qemu/hw/usb/ccid-card-passthru.c (revision 954a6c4f7862b45617ff3b65609f0f290dcd5077)
1  /*
2   * CCID Passthru Card Device emulation
3   *
4   * Copyright (c) 2011 Red Hat.
5   * Written by Alon Levy.
6   *
7   * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
8   * See the COPYING file in the top-level directory.
9   */
10  
11  #include "qemu/osdep.h"
12  #include "qemu/cutils.h"
13  #include "qemu/units.h"
14  #include <libcacard.h>
15  #include "chardev/char-fe.h"
16  #include "hw/qdev-properties.h"
17  #include "hw/qdev-properties-system.h"
18  #include "migration/vmstate.h"
19  #include "qemu/error-report.h"
20  #include "qemu/module.h"
21  #include "qemu/sockets.h"
22  #include "ccid.h"
23  #include "qapi/error.h"
24  #include "qom/object.h"
25  
26  #define DPRINTF(card, lvl, fmt, ...)                    \
27  do {                                                    \
28      if (lvl <= card->debug) {                           \
29          printf("ccid-card-passthru: " fmt , ## __VA_ARGS__);     \
30      }                                                   \
31  } while (0)
32  
33  #define D_WARN 1
34  #define D_INFO 2
35  #define D_MORE_INFO 3
36  #define D_VERBOSE 4
37  
38  /* TODO: do we still need this? */
39  static const uint8_t DEFAULT_ATR[] = {
40  /*
41   * From some example somewhere
42   * 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
43   */
44  
45  /* From an Athena smart card */
46   0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21,
47   0x13, 0x08
48  };
49  
50  #define VSCARD_IN_SIZE      (64 * KiB)
51  
52  /* maximum size of ATR - from 7816-3 */
53  #define MAX_ATR_SIZE        40
54  
55  typedef struct PassthruState PassthruState;
56  
57  struct PassthruState {
58      CCIDCardState base;
59      CharBackend cs;
60      uint8_t  vscard_in_data[VSCARD_IN_SIZE];
61      uint32_t vscard_in_pos;
62      uint32_t vscard_in_hdr;
63      uint8_t  atr[MAX_ATR_SIZE];
64      uint8_t  atr_length;
65      uint8_t  debug;
66  };
67  
68  #define TYPE_CCID_PASSTHRU "ccid-card-passthru"
69  DECLARE_INSTANCE_CHECKER(PassthruState, PASSTHRU_CCID_CARD,
70                           TYPE_CCID_PASSTHRU)
71  
72  /*
73   * VSCard protocol over chardev
74   * This code should not depend on the card type.
75   */
76  
77  static void ccid_card_vscard_send_msg(PassthruState *s,
78          VSCMsgType type, uint32_t reader_id,
79          const uint8_t *payload, uint32_t length)
80  {
81      VSCMsgHeader scr_msg_header;
82  
83      scr_msg_header.type = htonl(type);
84      scr_msg_header.reader_id = htonl(reader_id);
85      scr_msg_header.length = htonl(length);
86      /* XXX this blocks entire thread. Rewrite to use
87       * qemu_chr_fe_write and background I/O callbacks */
88      qemu_chr_fe_write_all(&s->cs, (uint8_t *)&scr_msg_header,
89                            sizeof(VSCMsgHeader));
90      qemu_chr_fe_write_all(&s->cs, payload, length);
91  }
92  
93  static void ccid_card_vscard_send_apdu(PassthruState *s,
94      const uint8_t *apdu, uint32_t length)
95  {
96      ccid_card_vscard_send_msg(
97          s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
98  }
99  
100  static void ccid_card_vscard_send_error(PassthruState *s,
101                      uint32_t reader_id, VSCErrorCode code)
102  {
103      VSCMsgError msg = {.code = htonl(code)};
104  
105      ccid_card_vscard_send_msg(
106          s, VSC_Error, reader_id, (uint8_t *)&msg, sizeof(msg));
107  }
108  
109  static void ccid_card_vscard_send_init(PassthruState *s)
110  {
111      VSCMsgInit msg = {
112          .version = htonl(VSCARD_VERSION),
113          .magic = VSCARD_MAGIC,
114          .capabilities = {0}
115      };
116  
117      ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
118                           (uint8_t *)&msg, sizeof(msg));
119  }
120  
121  static int ccid_card_vscard_can_read(void *opaque)
122  {
123      PassthruState *card = opaque;
124  
125      return VSCARD_IN_SIZE >= card->vscard_in_pos ?
126             VSCARD_IN_SIZE - card->vscard_in_pos : 0;
127  }
128  
129  static void ccid_card_vscard_handle_init(
130      PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init)
131  {
132      uint32_t *capabilities;
133      int num_capabilities;
134      int i;
135  
136      capabilities = init->capabilities;
137      num_capabilities =
138          1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
139      init->version = ntohl(init->version);
140      for (i = 0 ; i < num_capabilities; ++i) {
141          capabilities[i] = ntohl(capabilities[i]);
142      }
143      if (init->magic != VSCARD_MAGIC) {
144          error_report("wrong magic");
145          /* we can't disconnect the chardev */
146      }
147      if (init->version != VSCARD_VERSION) {
148          DPRINTF(card, D_WARN,
149              "got version %d, have %d", init->version, VSCARD_VERSION);
150      }
151      /* future handling of capabilities, none exist atm */
152      ccid_card_vscard_send_init(card);
153  }
154  
155  static int check_atr(PassthruState *card, uint8_t *data, int len)
156  {
157      int historical_length, opt_bytes;
158      int td_count = 0;
159      int td;
160  
161      if (len < 2) {
162          return 0;
163      }
164      historical_length = data[1] & 0xf;
165      opt_bytes = 0;
166      if (data[0] != 0x3b && data[0] != 0x3f) {
167          DPRINTF(card, D_WARN, "atr's T0 is 0x%X, not in {0x3b, 0x3f}\n",
168                  data[0]);
169          return 0;
170      }
171      td_count = 0;
172      td = data[1] >> 4;
173      while (td && td_count < 2 && opt_bytes + historical_length + 2 < len) {
174          td_count++;
175          if (td & 0x1) {
176              opt_bytes++;
177          }
178          if (td & 0x2) {
179              opt_bytes++;
180          }
181          if (td & 0x4) {
182              opt_bytes++;
183          }
184          if (td & 0x8) {
185              opt_bytes++;
186              td = data[opt_bytes + 2] >> 4;
187          }
188      }
189      if (len < 2 + historical_length + opt_bytes) {
190          DPRINTF(card, D_WARN,
191              "atr too short: len %d, but historical_len %d, T1 0x%X\n",
192              len, historical_length, data[1]);
193          return 0;
194      }
195      if (len > 2 + historical_length + opt_bytes) {
196          DPRINTF(card, D_WARN,
197              "atr too long: len %d, but hist/opt %d/%d, T1 0x%X\n",
198              len, historical_length, opt_bytes, data[1]);
199          /* let it through */
200      }
201      DPRINTF(card, D_VERBOSE,
202              "atr passes check: %d total length, %d historical, %d optional\n",
203              len, historical_length, opt_bytes);
204  
205      return 1;
206  }
207  
208  static void ccid_card_vscard_handle_message(PassthruState *card,
209      VSCMsgHeader *scr_msg_header)
210  {
211      uint8_t *data = (uint8_t *)&scr_msg_header[1];
212  
213      switch (scr_msg_header->type) {
214      case VSC_ATR:
215          DPRINTF(card, D_INFO, "VSC_ATR %d\n", scr_msg_header->length);
216          if (scr_msg_header->length > MAX_ATR_SIZE) {
217              error_report("ATR size exceeds spec, ignoring");
218              ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
219                                          VSC_GENERAL_ERROR);
220              break;
221          }
222          if (!check_atr(card, data, scr_msg_header->length)) {
223              error_report("ATR is inconsistent, ignoring");
224              ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
225                                          VSC_GENERAL_ERROR);
226              break;
227          }
228          memcpy(card->atr, data, scr_msg_header->length);
229          card->atr_length = scr_msg_header->length;
230          ccid_card_card_inserted(&card->base);
231          ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
232                                      VSC_SUCCESS);
233          break;
234      case VSC_APDU:
235          ccid_card_send_apdu_to_guest(
236              &card->base, data, scr_msg_header->length);
237          break;
238      case VSC_CardRemove:
239          DPRINTF(card, D_INFO, "VSC_CardRemove\n");
240          ccid_card_card_removed(&card->base);
241          ccid_card_vscard_send_error(card,
242              scr_msg_header->reader_id, VSC_SUCCESS);
243          break;
244      case VSC_Init:
245          ccid_card_vscard_handle_init(
246              card, scr_msg_header, (VSCMsgInit *)data);
247          break;
248      case VSC_Error:
249          ccid_card_card_error(&card->base, *(uint32_t *)data);
250          break;
251      case VSC_ReaderAdd:
252          if (ccid_card_ccid_attach(&card->base) < 0) {
253              ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
254                                        VSC_CANNOT_ADD_MORE_READERS);
255          } else {
256              ccid_card_vscard_send_error(card, VSCARD_MINIMAL_READER_ID,
257                                          VSC_SUCCESS);
258          }
259          break;
260      case VSC_ReaderRemove:
261          ccid_card_ccid_detach(&card->base);
262          ccid_card_vscard_send_error(card,
263              scr_msg_header->reader_id, VSC_SUCCESS);
264          break;
265      default:
266          printf("usb-ccid: chardev: unexpected message of type %X\n",
267                 scr_msg_header->type);
268          ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
269              VSC_GENERAL_ERROR);
270      }
271  }
272  
273  static void ccid_card_vscard_drop_connection(PassthruState *card)
274  {
275      qemu_chr_fe_deinit(&card->cs, true);
276      card->vscard_in_pos = card->vscard_in_hdr = 0;
277  }
278  
279  static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
280  {
281      PassthruState *card = opaque;
282      VSCMsgHeader *hdr;
283  
284      if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
285          error_report("no room for data: pos %u +  size %d > %" PRId64 "."
286                       " dropping connection.",
287                       card->vscard_in_pos, size, VSCARD_IN_SIZE);
288          ccid_card_vscard_drop_connection(card);
289          return;
290      }
291      assert(card->vscard_in_pos < VSCARD_IN_SIZE);
292      assert(card->vscard_in_hdr < VSCARD_IN_SIZE);
293      memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
294      card->vscard_in_pos += size;
295      hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
296  
297      while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader))
298           &&(card->vscard_in_pos - card->vscard_in_hdr >=
299                                    sizeof(VSCMsgHeader) + ntohl(hdr->length))) {
300          hdr->reader_id = ntohl(hdr->reader_id);
301          hdr->length = ntohl(hdr->length);
302          hdr->type = ntohl(hdr->type);
303          ccid_card_vscard_handle_message(card, hdr);
304          card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
305          hdr = (VSCMsgHeader *)(card->vscard_in_data + card->vscard_in_hdr);
306      }
307      if (card->vscard_in_hdr == card->vscard_in_pos) {
308          card->vscard_in_pos = card->vscard_in_hdr = 0;
309      }
310  }
311  
312  static void ccid_card_vscard_event(void *opaque, QEMUChrEvent event)
313  {
314      PassthruState *card = opaque;
315  
316      switch (event) {
317      case CHR_EVENT_BREAK:
318          card->vscard_in_pos = card->vscard_in_hdr = 0;
319          break;
320      case CHR_EVENT_OPENED:
321          DPRINTF(card, D_INFO, "%s: CHR_EVENT_OPENED\n", __func__);
322          break;
323      case CHR_EVENT_MUX_IN:
324      case CHR_EVENT_MUX_OUT:
325      case CHR_EVENT_CLOSED:
326          /* Ignore */
327          break;
328      }
329  }
330  
331  /* End VSCard handling */
332  
333  static void passthru_apdu_from_guest(
334      CCIDCardState *base, const uint8_t *apdu, uint32_t len)
335  {
336      PassthruState *card = PASSTHRU_CCID_CARD(base);
337  
338      if (!qemu_chr_fe_backend_connected(&card->cs)) {
339          printf("ccid-passthru: no chardev, discarding apdu length %u\n", len);
340          return;
341      }
342      ccid_card_vscard_send_apdu(card, apdu, len);
343  }
344  
345  static const uint8_t *passthru_get_atr(CCIDCardState *base, uint32_t *len)
346  {
347      PassthruState *card = PASSTHRU_CCID_CARD(base);
348  
349      *len = card->atr_length;
350      return card->atr;
351  }
352  
353  static void passthru_realize(CCIDCardState *base, Error **errp)
354  {
355      PassthruState *card = PASSTHRU_CCID_CARD(base);
356  
357      card->vscard_in_pos = 0;
358      card->vscard_in_hdr = 0;
359      if (qemu_chr_fe_backend_connected(&card->cs)) {
360          DPRINTF(card, D_INFO, "ccid-card-passthru: initing chardev");
361          qemu_chr_fe_set_handlers(&card->cs,
362              ccid_card_vscard_can_read,
363              ccid_card_vscard_read,
364              ccid_card_vscard_event, NULL, card, NULL, true);
365          ccid_card_vscard_send_init(card);
366      } else {
367          error_setg(errp, "missing chardev");
368          return;
369      }
370      card->debug = parse_debug_env("QEMU_CCID_PASSTHRU_DEBUG", D_VERBOSE,
371                                    card->debug);
372      assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
373      memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
374      card->atr_length = sizeof(DEFAULT_ATR);
375  }
376  
377  static const VMStateDescription passthru_vmstate = {
378      .name = "ccid-card-passthru",
379      .version_id = 1,
380      .minimum_version_id = 1,
381      .fields = (VMStateField[]) {
382          VMSTATE_BUFFER(vscard_in_data, PassthruState),
383          VMSTATE_UINT32(vscard_in_pos, PassthruState),
384          VMSTATE_UINT32(vscard_in_hdr, PassthruState),
385          VMSTATE_BUFFER(atr, PassthruState),
386          VMSTATE_UINT8(atr_length, PassthruState),
387          VMSTATE_END_OF_LIST()
388      }
389  };
390  
391  static Property passthru_card_properties[] = {
392      DEFINE_PROP_CHR("chardev", PassthruState, cs),
393      DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
394      DEFINE_PROP_END_OF_LIST(),
395  };
396  
397  static void passthru_class_initfn(ObjectClass *klass, void *data)
398  {
399      DeviceClass *dc = DEVICE_CLASS(klass);
400      CCIDCardClass *cc = CCID_CARD_CLASS(klass);
401  
402      cc->realize = passthru_realize;
403      cc->get_atr = passthru_get_atr;
404      cc->apdu_from_guest = passthru_apdu_from_guest;
405      set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
406      dc->desc = "passthrough smartcard";
407      dc->vmsd = &passthru_vmstate;
408      device_class_set_props(dc, passthru_card_properties);
409  }
410  
411  static const TypeInfo passthru_card_info = {
412      .name          = TYPE_CCID_PASSTHRU,
413      .parent        = TYPE_CCID_CARD,
414      .instance_size = sizeof(PassthruState),
415      .class_init    = passthru_class_initfn,
416  };
417  module_obj(TYPE_CCID_PASSTHRU);
418  module_kconfig(USB);
419  
420  static void ccid_card_passthru_register_types(void)
421  {
422      type_register_static(&passthru_card_info);
423  }
424  
425  type_init(ccid_card_passthru_register_types)
426