root / hw / ccid-card-emulated.c @ 00c3a05b
History | View | Annotate | Download (18.6 kB)
1 | 585738a6 | Alon Levy | /*
|
---|---|---|---|
2 | 585738a6 | Alon Levy | * CCID Card Device. Emulated card.
|
3 | 585738a6 | Alon Levy | *
|
4 | 585738a6 | Alon Levy | * Copyright (c) 2011 Red Hat.
|
5 | 585738a6 | Alon Levy | * Written by Alon Levy.
|
6 | 585738a6 | Alon Levy | *
|
7 | 8e31bf38 | Matthew Fernandez | * This code is licensed under the GNU LGPL, version 2 or later.
|
8 | 585738a6 | Alon Levy | */
|
9 | 585738a6 | Alon Levy | |
10 | 585738a6 | Alon Levy | /*
|
11 | 585738a6 | Alon Levy | * It can be used to provide access to the local hardware in a non exclusive
|
12 | 585738a6 | Alon Levy | * way, or it can use certificates. It requires the usb-ccid bus.
|
13 | 585738a6 | Alon Levy | *
|
14 | 585738a6 | Alon Levy | * Usage 1: standard, mirror hardware reader+card:
|
15 | 585738a6 | Alon Levy | * qemu .. -usb -device usb-ccid -device ccid-card-emulated
|
16 | 585738a6 | Alon Levy | *
|
17 | 585738a6 | Alon Levy | * Usage 2: use certificates, no hardware required
|
18 | 585738a6 | Alon Levy | * one time: create the certificates:
|
19 | 585738a6 | Alon Levy | * for i in 1 2 3; do
|
20 | 585738a6 | Alon Levy | * certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
|
21 | 585738a6 | Alon Levy | * done
|
22 | 585738a6 | Alon Levy | * qemu .. -usb -device usb-ccid \
|
23 | 585738a6 | Alon Levy | * -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
|
24 | 585738a6 | Alon Levy | *
|
25 | 585738a6 | Alon Levy | * If you use a non default db for the certificates you can specify it using
|
26 | 585738a6 | Alon Levy | * the db parameter.
|
27 | 585738a6 | Alon Levy | */
|
28 | 585738a6 | Alon Levy | |
29 | 585738a6 | Alon Levy | #include <eventt.h> |
30 | 585738a6 | Alon Levy | #include <vevent.h> |
31 | 585738a6 | Alon Levy | #include <vreader.h> |
32 | 585738a6 | Alon Levy | #include <vcard_emul.h> |
33 | 585738a6 | Alon Levy | |
34 | 585738a6 | Alon Levy | #include "qemu-thread.h" |
35 | 585738a6 | Alon Levy | #include "qemu-char.h" |
36 | 585738a6 | Alon Levy | #include "monitor.h" |
37 | 585738a6 | Alon Levy | #include "hw/ccid.h" |
38 | 585738a6 | Alon Levy | |
39 | 585738a6 | Alon Levy | #define DPRINTF(card, lvl, fmt, ...) \
|
40 | 585738a6 | Alon Levy | do {\
|
41 | 585738a6 | Alon Levy | if (lvl <= card->debug) {\
|
42 | 585738a6 | Alon Levy | printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ |
43 | 585738a6 | Alon Levy | } \ |
44 | 585738a6 | Alon Levy | } while (0) |
45 | 585738a6 | Alon Levy | |
46 | 585738a6 | Alon Levy | #define EMULATED_DEV_NAME "ccid-card-emulated" |
47 | 585738a6 | Alon Levy | |
48 | 585738a6 | Alon Levy | #define BACKEND_NSS_EMULATED_NAME "nss-emulated" |
49 | 585738a6 | Alon Levy | #define BACKEND_CERTIFICATES_NAME "certificates" |
50 | 585738a6 | Alon Levy | |
51 | 585738a6 | Alon Levy | enum {
|
52 | 585738a6 | Alon Levy | BACKEND_NSS_EMULATED = 1,
|
53 | 585738a6 | Alon Levy | BACKEND_CERTIFICATES |
54 | 585738a6 | Alon Levy | }; |
55 | 585738a6 | Alon Levy | |
56 | 585738a6 | Alon Levy | #define DEFAULT_BACKEND BACKEND_NSS_EMULATED
|
57 | 585738a6 | Alon Levy | |
58 | 585738a6 | Alon Levy | typedef struct EmulatedState EmulatedState; |
59 | 585738a6 | Alon Levy | |
60 | 585738a6 | Alon Levy | enum {
|
61 | 585738a6 | Alon Levy | EMUL_READER_INSERT = 0,
|
62 | 585738a6 | Alon Levy | EMUL_READER_REMOVE, |
63 | 585738a6 | Alon Levy | EMUL_CARD_INSERT, |
64 | 585738a6 | Alon Levy | EMUL_CARD_REMOVE, |
65 | 585738a6 | Alon Levy | EMUL_GUEST_APDU, |
66 | 585738a6 | Alon Levy | EMUL_RESPONSE_APDU, |
67 | 585738a6 | Alon Levy | EMUL_ERROR, |
68 | 585738a6 | Alon Levy | }; |
69 | 585738a6 | Alon Levy | |
70 | 585738a6 | Alon Levy | static const char *emul_event_to_string(uint32_t emul_event) |
71 | 585738a6 | Alon Levy | { |
72 | 585738a6 | Alon Levy | switch (emul_event) {
|
73 | 585738a6 | Alon Levy | case EMUL_READER_INSERT:
|
74 | 585738a6 | Alon Levy | return "EMUL_READER_INSERT"; |
75 | 585738a6 | Alon Levy | case EMUL_READER_REMOVE:
|
76 | 585738a6 | Alon Levy | return "EMUL_READER_REMOVE"; |
77 | 585738a6 | Alon Levy | case EMUL_CARD_INSERT:
|
78 | 585738a6 | Alon Levy | return "EMUL_CARD_INSERT"; |
79 | 585738a6 | Alon Levy | case EMUL_CARD_REMOVE:
|
80 | 585738a6 | Alon Levy | return "EMUL_CARD_REMOVE"; |
81 | 585738a6 | Alon Levy | case EMUL_GUEST_APDU:
|
82 | 585738a6 | Alon Levy | return "EMUL_GUEST_APDU"; |
83 | 585738a6 | Alon Levy | case EMUL_RESPONSE_APDU:
|
84 | 585738a6 | Alon Levy | return "EMUL_RESPONSE_APDU"; |
85 | 585738a6 | Alon Levy | case EMUL_ERROR:
|
86 | 585738a6 | Alon Levy | return "EMUL_ERROR"; |
87 | 585738a6 | Alon Levy | } |
88 | 585738a6 | Alon Levy | return "UNKNOWN"; |
89 | 585738a6 | Alon Levy | } |
90 | 585738a6 | Alon Levy | |
91 | 585738a6 | Alon Levy | typedef struct EmulEvent { |
92 | 585738a6 | Alon Levy | QSIMPLEQ_ENTRY(EmulEvent) entry; |
93 | 585738a6 | Alon Levy | union {
|
94 | 585738a6 | Alon Levy | struct {
|
95 | 585738a6 | Alon Levy | uint32_t type; |
96 | 585738a6 | Alon Levy | } gen; |
97 | 585738a6 | Alon Levy | struct {
|
98 | 585738a6 | Alon Levy | uint32_t type; |
99 | 585738a6 | Alon Levy | uint64_t code; |
100 | 585738a6 | Alon Levy | } error; |
101 | 585738a6 | Alon Levy | struct {
|
102 | 585738a6 | Alon Levy | uint32_t type; |
103 | 585738a6 | Alon Levy | uint32_t len; |
104 | 585738a6 | Alon Levy | uint8_t data[]; |
105 | 585738a6 | Alon Levy | } data; |
106 | 585738a6 | Alon Levy | } p; |
107 | 585738a6 | Alon Levy | } EmulEvent; |
108 | 585738a6 | Alon Levy | |
109 | 585738a6 | Alon Levy | #define MAX_ATR_SIZE 40 |
110 | 585738a6 | Alon Levy | struct EmulatedState {
|
111 | 585738a6 | Alon Levy | CCIDCardState base; |
112 | 585738a6 | Alon Levy | uint8_t debug; |
113 | 585738a6 | Alon Levy | char *backend_str;
|
114 | 585738a6 | Alon Levy | uint32_t backend; |
115 | 585738a6 | Alon Levy | char *cert1;
|
116 | 585738a6 | Alon Levy | char *cert2;
|
117 | 585738a6 | Alon Levy | char *cert3;
|
118 | 585738a6 | Alon Levy | char *db;
|
119 | 585738a6 | Alon Levy | uint8_t atr[MAX_ATR_SIZE]; |
120 | 585738a6 | Alon Levy | uint8_t atr_length; |
121 | 585738a6 | Alon Levy | QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; |
122 | 585738a6 | Alon Levy | QemuMutex event_list_mutex; |
123 | 585738a6 | Alon Levy | VReader *reader; |
124 | 585738a6 | Alon Levy | QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; |
125 | 585738a6 | Alon Levy | QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
|
126 | 585738a6 | Alon Levy | QemuMutex handle_apdu_mutex; |
127 | 585738a6 | Alon Levy | QemuCond handle_apdu_cond; |
128 | 585738a6 | Alon Levy | int pipe[2]; |
129 | 585738a6 | Alon Levy | int quit_apdu_thread;
|
130 | 585738a6 | Alon Levy | QemuMutex apdu_thread_quit_mutex; |
131 | 585738a6 | Alon Levy | QemuCond apdu_thread_quit_cond; |
132 | 585738a6 | Alon Levy | }; |
133 | 585738a6 | Alon Levy | |
134 | 585738a6 | Alon Levy | static void emulated_apdu_from_guest(CCIDCardState *base, |
135 | 585738a6 | Alon Levy | const uint8_t *apdu, uint32_t len)
|
136 | 585738a6 | Alon Levy | { |
137 | 585738a6 | Alon Levy | EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
138 | 7267c094 | Anthony Liguori | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
|
139 | 585738a6 | Alon Levy | |
140 | 585738a6 | Alon Levy | assert(event); |
141 | 585738a6 | Alon Levy | event->p.data.type = EMUL_GUEST_APDU; |
142 | 585738a6 | Alon Levy | event->p.data.len = len; |
143 | 585738a6 | Alon Levy | memcpy(event->p.data.data, apdu, len); |
144 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->vreader_mutex); |
145 | 585738a6 | Alon Levy | QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); |
146 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->vreader_mutex); |
147 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->handle_apdu_mutex); |
148 | 585738a6 | Alon Levy | qemu_cond_signal(&card->handle_apdu_cond); |
149 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->handle_apdu_mutex); |
150 | 585738a6 | Alon Levy | } |
151 | 585738a6 | Alon Levy | |
152 | 585738a6 | Alon Levy | static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) |
153 | 585738a6 | Alon Levy | { |
154 | 585738a6 | Alon Levy | EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
155 | 585738a6 | Alon Levy | |
156 | 585738a6 | Alon Levy | *len = card->atr_length; |
157 | 585738a6 | Alon Levy | return card->atr;
|
158 | 585738a6 | Alon Levy | } |
159 | 585738a6 | Alon Levy | |
160 | 585738a6 | Alon Levy | static void emulated_push_event(EmulatedState *card, EmulEvent *event) |
161 | 585738a6 | Alon Levy | { |
162 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->event_list_mutex); |
163 | 585738a6 | Alon Levy | QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); |
164 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->event_list_mutex); |
165 | 585738a6 | Alon Levy | if (write(card->pipe[1], card, 1) != 1) { |
166 | 585738a6 | Alon Levy | DPRINTF(card, 1, "write to pipe failed\n"); |
167 | 585738a6 | Alon Levy | } |
168 | 585738a6 | Alon Levy | } |
169 | 585738a6 | Alon Levy | |
170 | 585738a6 | Alon Levy | static void emulated_push_type(EmulatedState *card, uint32_t type) |
171 | 585738a6 | Alon Levy | { |
172 | 7267c094 | Anthony Liguori | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
|
173 | 585738a6 | Alon Levy | |
174 | 585738a6 | Alon Levy | assert(event); |
175 | 585738a6 | Alon Levy | event->p.gen.type = type; |
176 | 585738a6 | Alon Levy | emulated_push_event(card, event); |
177 | 585738a6 | Alon Levy | } |
178 | 585738a6 | Alon Levy | |
179 | 585738a6 | Alon Levy | static void emulated_push_error(EmulatedState *card, uint64_t code) |
180 | 585738a6 | Alon Levy | { |
181 | 7267c094 | Anthony Liguori | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent));
|
182 | 585738a6 | Alon Levy | |
183 | 585738a6 | Alon Levy | assert(event); |
184 | 585738a6 | Alon Levy | event->p.error.type = EMUL_ERROR; |
185 | 585738a6 | Alon Levy | event->p.error.code = code; |
186 | 585738a6 | Alon Levy | emulated_push_event(card, event); |
187 | 585738a6 | Alon Levy | } |
188 | 585738a6 | Alon Levy | |
189 | 585738a6 | Alon Levy | static void emulated_push_data_type(EmulatedState *card, uint32_t type, |
190 | 585738a6 | Alon Levy | const uint8_t *data, uint32_t len)
|
191 | 585738a6 | Alon Levy | { |
192 | 7267c094 | Anthony Liguori | EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
|
193 | 585738a6 | Alon Levy | |
194 | 585738a6 | Alon Levy | assert(event); |
195 | 585738a6 | Alon Levy | event->p.data.type = type; |
196 | 585738a6 | Alon Levy | event->p.data.len = len; |
197 | 585738a6 | Alon Levy | memcpy(event->p.data.data, data, len); |
198 | 585738a6 | Alon Levy | emulated_push_event(card, event); |
199 | 585738a6 | Alon Levy | } |
200 | 585738a6 | Alon Levy | |
201 | 585738a6 | Alon Levy | static void emulated_push_reader_insert(EmulatedState *card) |
202 | 585738a6 | Alon Levy | { |
203 | 585738a6 | Alon Levy | emulated_push_type(card, EMUL_READER_INSERT); |
204 | 585738a6 | Alon Levy | } |
205 | 585738a6 | Alon Levy | |
206 | 585738a6 | Alon Levy | static void emulated_push_reader_remove(EmulatedState *card) |
207 | 585738a6 | Alon Levy | { |
208 | 585738a6 | Alon Levy | emulated_push_type(card, EMUL_READER_REMOVE); |
209 | 585738a6 | Alon Levy | } |
210 | 585738a6 | Alon Levy | |
211 | 585738a6 | Alon Levy | static void emulated_push_card_insert(EmulatedState *card, |
212 | 585738a6 | Alon Levy | const uint8_t *atr, uint32_t len)
|
213 | 585738a6 | Alon Levy | { |
214 | 585738a6 | Alon Levy | emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); |
215 | 585738a6 | Alon Levy | } |
216 | 585738a6 | Alon Levy | |
217 | 585738a6 | Alon Levy | static void emulated_push_card_remove(EmulatedState *card) |
218 | 585738a6 | Alon Levy | { |
219 | 585738a6 | Alon Levy | emulated_push_type(card, EMUL_CARD_REMOVE); |
220 | 585738a6 | Alon Levy | } |
221 | 585738a6 | Alon Levy | |
222 | 585738a6 | Alon Levy | static void emulated_push_response_apdu(EmulatedState *card, |
223 | 585738a6 | Alon Levy | const uint8_t *apdu, uint32_t len)
|
224 | 585738a6 | Alon Levy | { |
225 | 585738a6 | Alon Levy | emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); |
226 | 585738a6 | Alon Levy | } |
227 | 585738a6 | Alon Levy | |
228 | 585738a6 | Alon Levy | #define APDU_BUF_SIZE 270 |
229 | 585738a6 | Alon Levy | static void *handle_apdu_thread(void* arg) |
230 | 585738a6 | Alon Levy | { |
231 | 585738a6 | Alon Levy | EmulatedState *card = arg; |
232 | 585738a6 | Alon Levy | uint8_t recv_data[APDU_BUF_SIZE]; |
233 | 585738a6 | Alon Levy | int recv_len;
|
234 | 585738a6 | Alon Levy | VReaderStatus reader_status; |
235 | 585738a6 | Alon Levy | EmulEvent *event; |
236 | 585738a6 | Alon Levy | |
237 | 585738a6 | Alon Levy | while (1) { |
238 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->handle_apdu_mutex); |
239 | 585738a6 | Alon Levy | qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); |
240 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->handle_apdu_mutex); |
241 | 585738a6 | Alon Levy | if (card->quit_apdu_thread) {
|
242 | 585738a6 | Alon Levy | card->quit_apdu_thread = 0; /* debugging */ |
243 | 585738a6 | Alon Levy | break;
|
244 | 585738a6 | Alon Levy | } |
245 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->vreader_mutex); |
246 | 585738a6 | Alon Levy | while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
|
247 | 585738a6 | Alon Levy | event = QSIMPLEQ_FIRST(&card->guest_apdu_list); |
248 | 585738a6 | Alon Levy | assert((unsigned long)event > 1000); |
249 | 585738a6 | Alon Levy | QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); |
250 | 585738a6 | Alon Levy | if (event->p.data.type != EMUL_GUEST_APDU) {
|
251 | 585738a6 | Alon Levy | DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); |
252 | 7267c094 | Anthony Liguori | g_free(event); |
253 | 585738a6 | Alon Levy | continue;
|
254 | 585738a6 | Alon Levy | } |
255 | 585738a6 | Alon Levy | if (card->reader == NULL) { |
256 | 585738a6 | Alon Levy | DPRINTF(card, 1, "reader is NULL\n"); |
257 | 7267c094 | Anthony Liguori | g_free(event); |
258 | 585738a6 | Alon Levy | continue;
|
259 | 585738a6 | Alon Levy | } |
260 | 585738a6 | Alon Levy | recv_len = sizeof(recv_data);
|
261 | 585738a6 | Alon Levy | reader_status = vreader_xfr_bytes(card->reader, |
262 | 585738a6 | Alon Levy | event->p.data.data, event->p.data.len, |
263 | 585738a6 | Alon Levy | recv_data, &recv_len); |
264 | 585738a6 | Alon Levy | DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); |
265 | 585738a6 | Alon Levy | if (reader_status == VREADER_OK) {
|
266 | 585738a6 | Alon Levy | emulated_push_response_apdu(card, recv_data, recv_len); |
267 | 585738a6 | Alon Levy | } else {
|
268 | 585738a6 | Alon Levy | emulated_push_error(card, reader_status); |
269 | 585738a6 | Alon Levy | } |
270 | 7267c094 | Anthony Liguori | g_free(event); |
271 | 585738a6 | Alon Levy | } |
272 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->vreader_mutex); |
273 | 585738a6 | Alon Levy | } |
274 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->apdu_thread_quit_mutex); |
275 | 585738a6 | Alon Levy | qemu_cond_signal(&card->apdu_thread_quit_cond); |
276 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->apdu_thread_quit_mutex); |
277 | 585738a6 | Alon Levy | return NULL; |
278 | 585738a6 | Alon Levy | } |
279 | 585738a6 | Alon Levy | |
280 | 585738a6 | Alon Levy | static void *event_thread(void *arg) |
281 | 585738a6 | Alon Levy | { |
282 | 585738a6 | Alon Levy | int atr_len = MAX_ATR_SIZE;
|
283 | 585738a6 | Alon Levy | uint8_t atr[MAX_ATR_SIZE]; |
284 | 585738a6 | Alon Levy | VEvent *event = NULL;
|
285 | 585738a6 | Alon Levy | EmulatedState *card = arg; |
286 | 585738a6 | Alon Levy | |
287 | 585738a6 | Alon Levy | while (1) { |
288 | 585738a6 | Alon Levy | const char *reader_name; |
289 | 585738a6 | Alon Levy | |
290 | 585738a6 | Alon Levy | event = vevent_wait_next_vevent(); |
291 | 585738a6 | Alon Levy | if (event == NULL || event->type == VEVENT_LAST) { |
292 | 585738a6 | Alon Levy | break;
|
293 | 585738a6 | Alon Levy | } |
294 | 585738a6 | Alon Levy | if (event->type != VEVENT_READER_INSERT) {
|
295 | 585738a6 | Alon Levy | if (card->reader == NULL && event->reader != NULL) { |
296 | 585738a6 | Alon Levy | /* Happens after device_add followed by card remove or insert.
|
297 | 585738a6 | Alon Levy | * XXX: create synthetic add_reader events if vcard_emul_init
|
298 | 585738a6 | Alon Levy | * already called, which happens if device_del and device_add
|
299 | 585738a6 | Alon Levy | * are called */
|
300 | 585738a6 | Alon Levy | card->reader = vreader_reference(event->reader); |
301 | 585738a6 | Alon Levy | } else {
|
302 | 585738a6 | Alon Levy | if (event->reader != card->reader) {
|
303 | 585738a6 | Alon Levy | fprintf(stderr, |
304 | 585738a6 | Alon Levy | "ERROR: wrong reader: quiting event_thread\n");
|
305 | 585738a6 | Alon Levy | break;
|
306 | 585738a6 | Alon Levy | } |
307 | 585738a6 | Alon Levy | } |
308 | 585738a6 | Alon Levy | } |
309 | 585738a6 | Alon Levy | switch (event->type) {
|
310 | 585738a6 | Alon Levy | case VEVENT_READER_INSERT:
|
311 | 585738a6 | Alon Levy | /* TODO: take a specific reader. i.e. track which reader
|
312 | 585738a6 | Alon Levy | * we are seeing here, check it is the one we want (the first,
|
313 | 585738a6 | Alon Levy | * or by a particular name), and ignore if we don't want it.
|
314 | 585738a6 | Alon Levy | */
|
315 | 585738a6 | Alon Levy | reader_name = vreader_get_name(event->reader); |
316 | 585738a6 | Alon Levy | if (card->reader != NULL) { |
317 | 585738a6 | Alon Levy | DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", |
318 | 585738a6 | Alon Levy | vreader_get_name(card->reader), reader_name); |
319 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->vreader_mutex); |
320 | 585738a6 | Alon Levy | vreader_free(card->reader); |
321 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->vreader_mutex); |
322 | 585738a6 | Alon Levy | emulated_push_reader_remove(card); |
323 | 585738a6 | Alon Levy | } |
324 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->vreader_mutex); |
325 | 585738a6 | Alon Levy | DPRINTF(card, 2, "READER INSERT %s\n", reader_name); |
326 | 585738a6 | Alon Levy | card->reader = vreader_reference(event->reader); |
327 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->vreader_mutex); |
328 | 585738a6 | Alon Levy | emulated_push_reader_insert(card); |
329 | 585738a6 | Alon Levy | break;
|
330 | 585738a6 | Alon Levy | case VEVENT_READER_REMOVE:
|
331 | 585738a6 | Alon Levy | DPRINTF(card, 2, " READER REMOVE: %s\n", |
332 | 585738a6 | Alon Levy | vreader_get_name(event->reader)); |
333 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->vreader_mutex); |
334 | 585738a6 | Alon Levy | vreader_free(card->reader); |
335 | 585738a6 | Alon Levy | card->reader = NULL;
|
336 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->vreader_mutex); |
337 | 585738a6 | Alon Levy | emulated_push_reader_remove(card); |
338 | 585738a6 | Alon Levy | break;
|
339 | 585738a6 | Alon Levy | case VEVENT_CARD_INSERT:
|
340 | 585738a6 | Alon Levy | /* get the ATR (intended as a response to a power on from the
|
341 | 585738a6 | Alon Levy | * reader */
|
342 | 585738a6 | Alon Levy | atr_len = MAX_ATR_SIZE; |
343 | 585738a6 | Alon Levy | vreader_power_on(event->reader, atr, &atr_len); |
344 | 585738a6 | Alon Levy | card->atr_length = (uint8_t)atr_len; |
345 | 585738a6 | Alon Levy | DPRINTF(card, 2, " CARD INSERT\n"); |
346 | 585738a6 | Alon Levy | emulated_push_card_insert(card, atr, atr_len); |
347 | 585738a6 | Alon Levy | break;
|
348 | 585738a6 | Alon Levy | case VEVENT_CARD_REMOVE:
|
349 | 585738a6 | Alon Levy | DPRINTF(card, 2, " CARD REMOVE\n"); |
350 | 585738a6 | Alon Levy | emulated_push_card_remove(card); |
351 | 585738a6 | Alon Levy | break;
|
352 | 585738a6 | Alon Levy | case VEVENT_LAST: /* quit */ |
353 | 585738a6 | Alon Levy | vevent_delete(event); |
354 | 585738a6 | Alon Levy | return NULL; |
355 | 585738a6 | Alon Levy | break;
|
356 | 585738a6 | Alon Levy | default:
|
357 | 585738a6 | Alon Levy | break;
|
358 | 585738a6 | Alon Levy | } |
359 | 585738a6 | Alon Levy | vevent_delete(event); |
360 | 585738a6 | Alon Levy | } |
361 | 585738a6 | Alon Levy | return NULL; |
362 | 585738a6 | Alon Levy | } |
363 | 585738a6 | Alon Levy | |
364 | 585738a6 | Alon Levy | static void pipe_read(void *opaque) |
365 | 585738a6 | Alon Levy | { |
366 | 585738a6 | Alon Levy | EmulatedState *card = opaque; |
367 | 585738a6 | Alon Levy | EmulEvent *event, *next; |
368 | 585738a6 | Alon Levy | char dummy;
|
369 | 585738a6 | Alon Levy | int len;
|
370 | 585738a6 | Alon Levy | |
371 | 585738a6 | Alon Levy | do {
|
372 | 585738a6 | Alon Levy | len = read(card->pipe[0], &dummy, sizeof(dummy)); |
373 | 585738a6 | Alon Levy | } while (len == sizeof(dummy)); |
374 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->event_list_mutex); |
375 | 585738a6 | Alon Levy | QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { |
376 | 585738a6 | Alon Levy | DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); |
377 | 585738a6 | Alon Levy | switch (event->p.gen.type) {
|
378 | 585738a6 | Alon Levy | case EMUL_RESPONSE_APDU:
|
379 | 585738a6 | Alon Levy | ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, |
380 | 585738a6 | Alon Levy | event->p.data.len); |
381 | 585738a6 | Alon Levy | break;
|
382 | 585738a6 | Alon Levy | case EMUL_READER_INSERT:
|
383 | 585738a6 | Alon Levy | ccid_card_ccid_attach(&card->base); |
384 | 585738a6 | Alon Levy | break;
|
385 | 585738a6 | Alon Levy | case EMUL_READER_REMOVE:
|
386 | 585738a6 | Alon Levy | ccid_card_ccid_detach(&card->base); |
387 | 585738a6 | Alon Levy | break;
|
388 | 585738a6 | Alon Levy | case EMUL_CARD_INSERT:
|
389 | 585738a6 | Alon Levy | assert(event->p.data.len <= MAX_ATR_SIZE); |
390 | 585738a6 | Alon Levy | card->atr_length = event->p.data.len; |
391 | 585738a6 | Alon Levy | memcpy(card->atr, event->p.data.data, card->atr_length); |
392 | 585738a6 | Alon Levy | ccid_card_card_inserted(&card->base); |
393 | 585738a6 | Alon Levy | break;
|
394 | 585738a6 | Alon Levy | case EMUL_CARD_REMOVE:
|
395 | 585738a6 | Alon Levy | ccid_card_card_removed(&card->base); |
396 | 585738a6 | Alon Levy | break;
|
397 | 585738a6 | Alon Levy | case EMUL_ERROR:
|
398 | 585738a6 | Alon Levy | ccid_card_card_error(&card->base, event->p.error.code); |
399 | 585738a6 | Alon Levy | break;
|
400 | 585738a6 | Alon Levy | default:
|
401 | 585738a6 | Alon Levy | DPRINTF(card, 2, "unexpected event\n"); |
402 | 585738a6 | Alon Levy | break;
|
403 | 585738a6 | Alon Levy | } |
404 | 7267c094 | Anthony Liguori | g_free(event); |
405 | 585738a6 | Alon Levy | } |
406 | 585738a6 | Alon Levy | QSIMPLEQ_INIT(&card->event_list); |
407 | 585738a6 | Alon Levy | qemu_mutex_unlock(&card->event_list_mutex); |
408 | 585738a6 | Alon Levy | } |
409 | 585738a6 | Alon Levy | |
410 | 585738a6 | Alon Levy | static int init_pipe_signaling(EmulatedState *card) |
411 | 585738a6 | Alon Levy | { |
412 | 585738a6 | Alon Levy | if (pipe(card->pipe) < 0) { |
413 | 585738a6 | Alon Levy | DPRINTF(card, 2, "pipe creation failed\n"); |
414 | 585738a6 | Alon Levy | return -1; |
415 | 585738a6 | Alon Levy | } |
416 | 585738a6 | Alon Levy | fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
|
417 | 585738a6 | Alon Levy | fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
|
418 | 585738a6 | Alon Levy | fcntl(card->pipe[0], F_SETOWN, getpid());
|
419 | 585738a6 | Alon Levy | qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card); |
420 | 585738a6 | Alon Levy | return 0; |
421 | 585738a6 | Alon Levy | } |
422 | 585738a6 | Alon Levy | |
423 | 585738a6 | Alon Levy | #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" |
424 | 585738a6 | Alon Levy | #define CERTIFICATES_ARGS_TEMPLATE\
|
425 | 585738a6 | Alon Levy | "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
|
426 | 585738a6 | Alon Levy | |
427 | 585738a6 | Alon Levy | static int wrap_vcard_emul_init(VCardEmulOptions *options) |
428 | 585738a6 | Alon Levy | { |
429 | 585738a6 | Alon Levy | static int called; |
430 | 585738a6 | Alon Levy | static int options_was_null; |
431 | 585738a6 | Alon Levy | |
432 | 585738a6 | Alon Levy | if (called) {
|
433 | 585738a6 | Alon Levy | if ((options == NULL) != options_was_null) { |
434 | 585738a6 | Alon Levy | printf("%s: warning: running emulated with certificates"
|
435 | 585738a6 | Alon Levy | " and emulated side by side is not supported\n",
|
436 | 585738a6 | Alon Levy | __func__); |
437 | 585738a6 | Alon Levy | return VCARD_EMUL_FAIL;
|
438 | 585738a6 | Alon Levy | } |
439 | 585738a6 | Alon Levy | vcard_emul_replay_insertion_events(); |
440 | 585738a6 | Alon Levy | return VCARD_EMUL_OK;
|
441 | 585738a6 | Alon Levy | } |
442 | 585738a6 | Alon Levy | options_was_null = (options == NULL);
|
443 | 585738a6 | Alon Levy | called = 1;
|
444 | 585738a6 | Alon Levy | return vcard_emul_init(options);
|
445 | 585738a6 | Alon Levy | } |
446 | 585738a6 | Alon Levy | |
447 | 585738a6 | Alon Levy | static int emulated_initialize_vcard_from_certificates(EmulatedState *card) |
448 | 585738a6 | Alon Levy | { |
449 | 585738a6 | Alon Levy | char emul_args[200]; |
450 | 585738a6 | Alon Levy | VCardEmulOptions *options = NULL;
|
451 | 585738a6 | Alon Levy | |
452 | 585738a6 | Alon Levy | snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, |
453 | 585738a6 | Alon Levy | card->db ? card->db : CERTIFICATES_DEFAULT_DB, |
454 | 585738a6 | Alon Levy | card->cert1, card->cert2, card->cert3); |
455 | 585738a6 | Alon Levy | options = vcard_emul_options(emul_args); |
456 | 585738a6 | Alon Levy | if (options == NULL) { |
457 | 585738a6 | Alon Levy | printf("%s: warning: not using certificates due to"
|
458 | 585738a6 | Alon Levy | " initialization error\n", __func__);
|
459 | 585738a6 | Alon Levy | } |
460 | 585738a6 | Alon Levy | return wrap_vcard_emul_init(options);
|
461 | 585738a6 | Alon Levy | } |
462 | 585738a6 | Alon Levy | |
463 | 585738a6 | Alon Levy | typedef struct EnumTable { |
464 | 585738a6 | Alon Levy | const char *name; |
465 | 585738a6 | Alon Levy | uint32_t value; |
466 | 585738a6 | Alon Levy | } EnumTable; |
467 | 585738a6 | Alon Levy | |
468 | 585738a6 | Alon Levy | EnumTable backend_enum_table[] = { |
469 | 585738a6 | Alon Levy | {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, |
470 | 585738a6 | Alon Levy | {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, |
471 | 585738a6 | Alon Levy | {NULL, 0}, |
472 | 585738a6 | Alon Levy | }; |
473 | 585738a6 | Alon Levy | |
474 | 585738a6 | Alon Levy | static uint32_t parse_enumeration(char *str, |
475 | 585738a6 | Alon Levy | EnumTable *table, uint32_t not_found_value) |
476 | 585738a6 | Alon Levy | { |
477 | 585738a6 | Alon Levy | uint32_t ret = not_found_value; |
478 | 585738a6 | Alon Levy | |
479 | 585738a6 | Alon Levy | while (table->name != NULL) { |
480 | 585738a6 | Alon Levy | if (strcmp(table->name, str) == 0) { |
481 | 585738a6 | Alon Levy | ret = table->value; |
482 | 585738a6 | Alon Levy | break;
|
483 | 585738a6 | Alon Levy | } |
484 | 585738a6 | Alon Levy | table++; |
485 | 585738a6 | Alon Levy | } |
486 | 585738a6 | Alon Levy | return ret;
|
487 | 585738a6 | Alon Levy | } |
488 | 585738a6 | Alon Levy | |
489 | 585738a6 | Alon Levy | static int emulated_initfn(CCIDCardState *base) |
490 | 585738a6 | Alon Levy | { |
491 | 585738a6 | Alon Levy | EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
492 | 585738a6 | Alon Levy | QemuThread thread_id; |
493 | 585738a6 | Alon Levy | VCardEmulError ret; |
494 | 585738a6 | Alon Levy | EnumTable *ptable; |
495 | 585738a6 | Alon Levy | |
496 | 585738a6 | Alon Levy | QSIMPLEQ_INIT(&card->event_list); |
497 | 585738a6 | Alon Levy | QSIMPLEQ_INIT(&card->guest_apdu_list); |
498 | 585738a6 | Alon Levy | qemu_mutex_init(&card->event_list_mutex); |
499 | 585738a6 | Alon Levy | qemu_mutex_init(&card->vreader_mutex); |
500 | 585738a6 | Alon Levy | qemu_mutex_init(&card->handle_apdu_mutex); |
501 | 585738a6 | Alon Levy | qemu_cond_init(&card->handle_apdu_cond); |
502 | 585738a6 | Alon Levy | card->reader = NULL;
|
503 | 585738a6 | Alon Levy | card->quit_apdu_thread = 0;
|
504 | 585738a6 | Alon Levy | if (init_pipe_signaling(card) < 0) { |
505 | 585738a6 | Alon Levy | return -1; |
506 | 585738a6 | Alon Levy | } |
507 | 585738a6 | Alon Levy | card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
|
508 | 585738a6 | Alon Levy | if (card->backend == 0) { |
509 | 585738a6 | Alon Levy | printf("unknown backend, must be one of:\n");
|
510 | 585738a6 | Alon Levy | for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { |
511 | 585738a6 | Alon Levy | printf("%s\n", ptable->name);
|
512 | 585738a6 | Alon Levy | } |
513 | 585738a6 | Alon Levy | return -1; |
514 | 585738a6 | Alon Levy | } |
515 | 585738a6 | Alon Levy | |
516 | 585738a6 | Alon Levy | /* TODO: a passthru backened that works on local machine. third card type?*/
|
517 | 585738a6 | Alon Levy | if (card->backend == BACKEND_CERTIFICATES) {
|
518 | 585738a6 | Alon Levy | if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { |
519 | 585738a6 | Alon Levy | ret = emulated_initialize_vcard_from_certificates(card); |
520 | 585738a6 | Alon Levy | } else {
|
521 | 585738a6 | Alon Levy | printf("%s: you must provide all three certs for"
|
522 | 585738a6 | Alon Levy | " certificates backend\n", EMULATED_DEV_NAME);
|
523 | 585738a6 | Alon Levy | return -1; |
524 | 585738a6 | Alon Levy | } |
525 | 585738a6 | Alon Levy | } else {
|
526 | 585738a6 | Alon Levy | if (card->backend != BACKEND_NSS_EMULATED) {
|
527 | 585738a6 | Alon Levy | printf("%s: bad backend specified. The options are:\n%s (default),"
|
528 | 585738a6 | Alon Levy | " %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
|
529 | 585738a6 | Alon Levy | BACKEND_CERTIFICATES_NAME); |
530 | 585738a6 | Alon Levy | return -1; |
531 | 585738a6 | Alon Levy | } |
532 | 585738a6 | Alon Levy | if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { |
533 | 585738a6 | Alon Levy | printf("%s: unexpected cert parameters to nss emulated backend\n",
|
534 | 585738a6 | Alon Levy | EMULATED_DEV_NAME); |
535 | 585738a6 | Alon Levy | return -1; |
536 | 585738a6 | Alon Levy | } |
537 | 585738a6 | Alon Levy | /* default to mirroring the local hardware readers */
|
538 | 585738a6 | Alon Levy | ret = wrap_vcard_emul_init(NULL);
|
539 | 585738a6 | Alon Levy | } |
540 | 585738a6 | Alon Levy | if (ret != VCARD_EMUL_OK) {
|
541 | 585738a6 | Alon Levy | printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
|
542 | 585738a6 | Alon Levy | return -1; |
543 | 585738a6 | Alon Levy | } |
544 | 585738a6 | Alon Levy | qemu_thread_create(&thread_id, event_thread, card); |
545 | 585738a6 | Alon Levy | qemu_thread_create(&thread_id, handle_apdu_thread, card); |
546 | 585738a6 | Alon Levy | return 0; |
547 | 585738a6 | Alon Levy | } |
548 | 585738a6 | Alon Levy | |
549 | 585738a6 | Alon Levy | static int emulated_exitfn(CCIDCardState *base) |
550 | 585738a6 | Alon Levy | { |
551 | 585738a6 | Alon Levy | EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
552 | 585738a6 | Alon Levy | VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); |
553 | 585738a6 | Alon Levy | |
554 | 585738a6 | Alon Levy | vevent_queue_vevent(vevent); /* stop vevent thread */
|
555 | 585738a6 | Alon Levy | qemu_mutex_lock(&card->apdu_thread_quit_mutex); |
556 | 585738a6 | Alon Levy | card->quit_apdu_thread = 1; /* stop handle_apdu thread */ |
557 | 585738a6 | Alon Levy | qemu_cond_signal(&card->handle_apdu_cond); |
558 | 585738a6 | Alon Levy | qemu_cond_wait(&card->apdu_thread_quit_cond, |
559 | 585738a6 | Alon Levy | &card->apdu_thread_quit_mutex); |
560 | 585738a6 | Alon Levy | /* handle_apdu thread stopped, can destroy all of it's mutexes */
|
561 | 585738a6 | Alon Levy | qemu_cond_destroy(&card->handle_apdu_cond); |
562 | 585738a6 | Alon Levy | qemu_cond_destroy(&card->apdu_thread_quit_cond); |
563 | 585738a6 | Alon Levy | qemu_mutex_destroy(&card->apdu_thread_quit_mutex); |
564 | 585738a6 | Alon Levy | qemu_mutex_destroy(&card->handle_apdu_mutex); |
565 | 585738a6 | Alon Levy | qemu_mutex_destroy(&card->vreader_mutex); |
566 | 585738a6 | Alon Levy | qemu_mutex_destroy(&card->event_list_mutex); |
567 | 585738a6 | Alon Levy | return 0; |
568 | 585738a6 | Alon Levy | } |
569 | 585738a6 | Alon Levy | |
570 | 585738a6 | Alon Levy | static CCIDCardInfo emulated_card_info = {
|
571 | 585738a6 | Alon Levy | .qdev.name = EMULATED_DEV_NAME, |
572 | 585738a6 | Alon Levy | .qdev.desc = "emulated smartcard",
|
573 | 585738a6 | Alon Levy | .qdev.size = sizeof(EmulatedState),
|
574 | 585738a6 | Alon Levy | .initfn = emulated_initfn, |
575 | 585738a6 | Alon Levy | .exitfn = emulated_exitfn, |
576 | 585738a6 | Alon Levy | .get_atr = emulated_get_atr, |
577 | 585738a6 | Alon Levy | .apdu_from_guest = emulated_apdu_from_guest, |
578 | 585738a6 | Alon Levy | .qdev.unplug = qdev_simple_unplug_cb, |
579 | 585738a6 | Alon Levy | .qdev.props = (Property[]) { |
580 | 585738a6 | Alon Levy | DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
|
581 | 585738a6 | Alon Levy | DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
|
582 | 585738a6 | Alon Levy | DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
|
583 | 585738a6 | Alon Levy | DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
|
584 | 585738a6 | Alon Levy | DEFINE_PROP_STRING("db", EmulatedState, db),
|
585 | 585738a6 | Alon Levy | DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), |
586 | 585738a6 | Alon Levy | DEFINE_PROP_END_OF_LIST(), |
587 | 585738a6 | Alon Levy | }, |
588 | 585738a6 | Alon Levy | }; |
589 | 585738a6 | Alon Levy | |
590 | 585738a6 | Alon Levy | static void ccid_card_emulated_register_devices(void) |
591 | 585738a6 | Alon Levy | { |
592 | 585738a6 | Alon Levy | ccid_card_qdev_register(&emulated_card_info); |
593 | 585738a6 | Alon Levy | } |
594 | 585738a6 | Alon Levy | |
595 | 585738a6 | Alon Levy | device_init(ccid_card_emulated_register_devices) |