]>
Commit | Line | Data |
---|---|---|
111a38b0 RR |
1 | /* |
2 | * This is the actual card emulator. | |
3 | * | |
4 | * These functions can be implemented in different ways on different platforms | |
5 | * using the underlying system primitives. For Linux it uses NSS, though direct | |
6 | * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be | |
7 | * used. On Windows CAPI could be used. | |
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 | /* | |
14 | * NSS headers | |
15 | */ | |
16 | ||
17 | /* avoid including prototypes.h that redefines uint32 */ | |
18 | #define NO_NSPR_10_SUPPORT | |
19 | ||
20 | #include <nss.h> | |
21 | #include <pk11pub.h> | |
22 | #include <cert.h> | |
23 | #include <key.h> | |
24 | #include <secmod.h> | |
25 | #include <prthread.h> | |
26 | #include <secerr.h> | |
27 | ||
28 | #include "qemu-common.h" | |
29 | ||
30 | #include "vcard.h" | |
31 | #include "card_7816t.h" | |
32 | #include "vcard_emul.h" | |
33 | #include "vreader.h" | |
34 | #include "vevent.h" | |
35 | ||
0b6a16c1 AL |
36 | #include "libcacard/vcardt_internal.h" |
37 | ||
38 | ||
010debef RR |
39 | typedef enum { |
40 | VCardEmulUnknown = -1, | |
41 | VCardEmulFalse = 0, | |
42 | VCardEmulTrue = 1 | |
43 | } VCardEmulTriState; | |
44 | ||
111a38b0 RR |
45 | struct VCardKeyStruct { |
46 | CERTCertificate *cert; | |
47 | PK11SlotInfo *slot; | |
48 | SECKEYPrivateKey *key; | |
010debef | 49 | VCardEmulTriState failedX509; |
111a38b0 RR |
50 | }; |
51 | ||
52 | ||
53 | typedef struct VirtualReaderOptionsStruct VirtualReaderOptions; | |
54 | ||
55 | struct VReaderEmulStruct { | |
56 | PK11SlotInfo *slot; | |
57 | VCardEmulType default_type; | |
58 | char *type_params; | |
59 | PRBool present; | |
60 | int series; | |
61 | VCard *saved_vcard; | |
62 | }; | |
63 | ||
64 | /* | |
65 | * NSS Specific options | |
66 | */ | |
67 | struct VirtualReaderOptionsStruct { | |
68 | char *name; | |
69 | char *vname; | |
70 | VCardEmulType card_type; | |
71 | char *type_params; | |
72 | char **cert_name; | |
73 | int cert_count; | |
74 | }; | |
75 | ||
76 | struct VCardEmulOptionsStruct { | |
77 | void *nss_db; | |
78 | VirtualReaderOptions *vreader; | |
79 | int vreader_count; | |
80 | VCardEmulType hw_card_type; | |
81 | const char *hw_type_params; | |
82 | PRBool use_hw; | |
83 | }; | |
84 | ||
85 | static int nss_emul_init; | |
86 | ||
87 | /* if we have more that just the slot, define | |
88 | * VCardEmulStruct here */ | |
89 | ||
90 | /* | |
91 | * allocate the set of arrays for certs, cert_len, key | |
92 | */ | |
48f0475f | 93 | static void |
111a38b0 RR |
94 | vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, |
95 | VCardKey ***keysp, int cert_count) | |
96 | { | |
78a4b8d2 MT |
97 | *certsp = g_new(unsigned char *, cert_count); |
98 | *cert_lenp = g_new(int, cert_count); | |
99 | *keysp = g_new(VCardKey *, cert_count); | |
111a38b0 RR |
100 | } |
101 | ||
102 | /* | |
103 | * Emulator specific card information | |
104 | */ | |
105 | typedef struct CardEmulCardStruct CardEmulPrivate; | |
106 | ||
107 | static VCardEmul * | |
108 | vcard_emul_new_card(PK11SlotInfo *slot) | |
109 | { | |
110 | PK11_ReferenceSlot(slot); | |
111 | /* currently we don't need anything other than the slot */ | |
112 | return (VCardEmul *)slot; | |
113 | } | |
114 | ||
115 | static void | |
116 | vcard_emul_delete_card(VCardEmul *vcard_emul) | |
117 | { | |
118 | PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul; | |
119 | if (slot == NULL) { | |
120 | return; | |
121 | } | |
122 | PK11_FreeSlot(slot); | |
123 | } | |
124 | ||
125 | static PK11SlotInfo * | |
126 | vcard_emul_card_get_slot(VCard *card) | |
127 | { | |
128 | /* note, the card is holding the reference, no need to get another one */ | |
129 | return (PK11SlotInfo *)vcard_get_private(card); | |
130 | } | |
131 | ||
132 | ||
133 | /* | |
134 | * key functions | |
135 | */ | |
136 | /* private constructure */ | |
137 | static VCardKey * | |
138 | vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert) | |
139 | { | |
140 | VCardKey *key; | |
141 | ||
78a4b8d2 | 142 | key = g_new(VCardKey, 1); |
111a38b0 RR |
143 | key->slot = PK11_ReferenceSlot(slot); |
144 | key->cert = CERT_DupCertificate(cert); | |
145 | /* NOTE: if we aren't logged into the token, this could return NULL */ | |
146 | /* NOTE: the cert is a temp cert, not necessarily the cert in the token, | |
147 | * use the DER version of this function */ | |
148 | key->key = PK11_FindKeyByDERCert(slot, cert, NULL); | |
010debef | 149 | key->failedX509 = VCardEmulUnknown; |
111a38b0 RR |
150 | return key; |
151 | } | |
152 | ||
153 | /* destructor */ | |
154 | void | |
155 | vcard_emul_delete_key(VCardKey *key) | |
156 | { | |
157 | if (!nss_emul_init || (key == NULL)) { | |
158 | return; | |
159 | } | |
160 | if (key->key) { | |
161 | SECKEY_DestroyPrivateKey(key->key); | |
162 | key->key = NULL; | |
163 | } | |
164 | if (key->cert) { | |
165 | CERT_DestroyCertificate(key->cert); | |
166 | } | |
167 | if (key->slot) { | |
168 | PK11_FreeSlot(key->slot); | |
169 | } | |
111a38b0 RR |
170 | } |
171 | ||
172 | /* | |
173 | * grab the nss key from a VCardKey. If it doesn't exist, try to look it up | |
174 | */ | |
175 | static SECKEYPrivateKey * | |
176 | vcard_emul_get_nss_key(VCardKey *key) | |
177 | { | |
178 | if (key->key) { | |
179 | return key->key; | |
180 | } | |
181 | /* NOTE: if we aren't logged into the token, this could return NULL */ | |
182 | key->key = PK11_FindPrivateKeyFromCert(key->slot, key->cert, NULL); | |
183 | return key->key; | |
184 | } | |
185 | ||
186 | /* | |
187 | * Map NSS errors to 7816 errors | |
188 | */ | |
189 | static vcard_7816_status_t | |
190 | vcard_emul_map_error(int error) | |
191 | { | |
192 | switch (error) { | |
193 | case SEC_ERROR_TOKEN_NOT_LOGGED_IN: | |
194 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
195 | case SEC_ERROR_BAD_DATA: | |
196 | case SEC_ERROR_OUTPUT_LEN: | |
197 | case SEC_ERROR_INPUT_LEN: | |
198 | case SEC_ERROR_INVALID_ARGS: | |
199 | case SEC_ERROR_INVALID_ALGORITHM: | |
200 | case SEC_ERROR_NO_KEY: | |
201 | case SEC_ERROR_INVALID_KEY: | |
202 | case SEC_ERROR_DECRYPTION_DISALLOWED: | |
203 | return VCARD7816_STATUS_ERROR_DATA_INVALID; | |
204 | case SEC_ERROR_NO_MEMORY: | |
205 | return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE; | |
206 | } | |
207 | return VCARD7816_STATUS_EXC_ERROR_CHANGE; | |
208 | } | |
209 | ||
210 | /* RSA sign/decrypt with the key, signature happens 'in place' */ | |
211 | vcard_7816_status_t | |
212 | vcard_emul_rsa_op(VCard *card, VCardKey *key, | |
213 | unsigned char *buffer, int buffer_size) | |
214 | { | |
215 | SECKEYPrivateKey *priv_key; | |
216 | unsigned signature_len; | |
010debef | 217 | PK11SlotInfo *slot; |
111a38b0 | 218 | SECStatus rv; |
010debef RR |
219 | unsigned char buf[2048]; |
220 | unsigned char *bp = NULL; | |
221 | int pad_len; | |
222 | vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS; | |
111a38b0 RR |
223 | |
224 | if ((!nss_emul_init) || (key == NULL)) { | |
225 | /* couldn't get the key, indicate that we aren't logged in */ | |
226 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
227 | } | |
228 | priv_key = vcard_emul_get_nss_key(key); | |
010debef RR |
229 | if (priv_key == NULL) { |
230 | /* couldn't get the key, indicate that we aren't logged in */ | |
231 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
232 | } | |
233 | slot = vcard_emul_card_get_slot(card); | |
111a38b0 RR |
234 | |
235 | /* | |
236 | * this is only true of the rsa signature | |
237 | */ | |
238 | signature_len = PK11_SignatureLen(priv_key); | |
239 | if (buffer_size != signature_len) { | |
240 | return VCARD7816_STATUS_ERROR_DATA_INVALID; | |
241 | } | |
010debef RR |
242 | /* be able to handle larger keys if necessariy */ |
243 | bp = &buf[0]; | |
244 | if (sizeof(buf) < signature_len) { | |
7267c094 | 245 | bp = g_malloc(signature_len); |
010debef RR |
246 | } |
247 | ||
248 | /* | |
249 | * do the raw operations. Some tokens claim to do CKM_RSA_X_509, but then | |
250 | * choke when they try to do the actual operations. Try to detect | |
251 | * those cases and treat them as if the token didn't claim support for | |
252 | * X_509. | |
253 | */ | |
254 | if (key->failedX509 != VCardEmulTrue | |
255 | && PK11_DoesMechanism(slot, CKM_RSA_X_509)) { | |
256 | rv = PK11_PrivDecryptRaw(priv_key, bp, &signature_len, signature_len, | |
257 | buffer, buffer_size); | |
258 | if (rv == SECSuccess) { | |
259 | assert(buffer_size == signature_len); | |
260 | memcpy(buffer, bp, signature_len); | |
261 | key->failedX509 = VCardEmulFalse; | |
262 | goto cleanup; | |
263 | } | |
264 | /* | |
265 | * we've had a successful X509 operation, this failure must be | |
266 | * somethine else | |
267 | */ | |
268 | if (key->failedX509 == VCardEmulFalse) { | |
269 | ret = vcard_emul_map_error(PORT_GetError()); | |
270 | goto cleanup; | |
271 | } | |
272 | /* | |
273 | * key->failedX509 must be Unknown at this point, try the | |
274 | * non-x_509 case | |
275 | */ | |
276 | } | |
277 | /* token does not support CKM_RSA_X509, emulate that with CKM_RSA_PKCS */ | |
278 | /* is this a PKCS #1 formatted signature? */ | |
279 | if ((buffer[0] == 0) && (buffer[1] == 1)) { | |
280 | int i; | |
281 | ||
282 | for (i = 2; i < buffer_size; i++) { | |
283 | /* rsa signature pad */ | |
284 | if (buffer[i] != 0xff) { | |
285 | break; | |
286 | } | |
287 | } | |
288 | if ((i < buffer_size) && (buffer[i] == 0)) { | |
9d632f5f | 289 | /* yes, we have a properly formatted PKCS #1 signature */ |
010debef RR |
290 | /* |
291 | * NOTE: even if we accidentally got an encrypt buffer, which | |
9d632f5f | 292 | * through sheer luck started with 00, 01, ff, 00, it won't matter |
010debef RR |
293 | * because the resulting Sign operation will effectively decrypt |
294 | * the real buffer. | |
295 | */ | |
296 | SECItem signature; | |
297 | SECItem hash; | |
298 | ||
299 | i++; | |
300 | hash.data = &buffer[i]; | |
301 | hash.len = buffer_size - i; | |
302 | signature.data = bp; | |
303 | signature.len = signature_len; | |
304 | rv = PK11_Sign(priv_key, &signature, &hash); | |
305 | if (rv != SECSuccess) { | |
306 | ret = vcard_emul_map_error(PORT_GetError()); | |
307 | goto cleanup; | |
308 | } | |
309 | assert(buffer_size == signature.len); | |
310 | memcpy(buffer, bp, signature.len); | |
311 | /* | |
312 | * we got here because either the X509 attempt failed, or the | |
313 | * token couldn't do the X509 operation, in either case stay | |
314 | * with the PKCS version for future operations on this key | |
315 | */ | |
316 | key->failedX509 = VCardEmulTrue; | |
317 | goto cleanup; | |
318 | } | |
319 | } | |
320 | pad_len = buffer_size - signature_len; | |
321 | assert(pad_len < 4); | |
322 | /* | |
323 | * OK now we've decrypted the payload, package it up in PKCS #1 for the | |
324 | * upper layer. | |
325 | */ | |
326 | buffer[0] = 0; | |
327 | buffer[1] = 2; /* RSA_encrypt */ | |
328 | pad_len -= 3; /* format is 0 || 2 || pad || 0 || data */ | |
329 | /* | |
330 | * padding for PKCS #1 encrypted data is a string of random bytes. The | |
331 | * random butes protect against potential decryption attacks against RSA. | |
332 | * Since PrivDecrypt has already stripped those bytes, we can't reconstruct | |
333 | * them. This shouldn't matter to the upper level code which should just | |
334 | * strip this code out anyway, so We'll pad with a constant 3. | |
335 | */ | |
336 | memset(&buffer[2], 0x03, pad_len); | |
337 | pad_len += 2; /* index to the end of the pad */ | |
338 | buffer[pad_len] = 0; | |
339 | pad_len++; /* index to the start of the data */ | |
340 | memcpy(&buffer[pad_len], bp, signature_len); | |
341 | /* | |
342 | * we got here because either the X509 attempt failed, or the | |
343 | * token couldn't do the X509 operation, in either case stay | |
344 | * with the PKCS version for future operations on this key | |
345 | */ | |
346 | key->failedX509 = VCardEmulTrue; | |
347 | cleanup: | |
348 | if (bp != buf) { | |
7267c094 | 349 | g_free(bp); |
111a38b0 | 350 | } |
010debef | 351 | return ret; |
111a38b0 RR |
352 | } |
353 | ||
354 | /* | |
355 | * Login functions | |
356 | */ | |
357 | /* return the number of login attempts still possible on the card. if unknown, | |
358 | * return -1 */ | |
359 | int | |
360 | vcard_emul_get_login_count(VCard *card) | |
361 | { | |
362 | return -1; | |
363 | } | |
364 | ||
365 | /* login into the card, return the 7816 status word (sw2 || sw1) */ | |
366 | vcard_7816_status_t | |
367 | vcard_emul_login(VCard *card, unsigned char *pin, int pin_len) | |
368 | { | |
369 | PK11SlotInfo *slot; | |
1687a089 | 370 | unsigned char *pin_string; |
111a38b0 RR |
371 | int i; |
372 | SECStatus rv; | |
373 | ||
374 | if (!nss_emul_init) { | |
375 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
376 | } | |
377 | slot = vcard_emul_card_get_slot(card); | |
378 | /* We depend on the PKCS #11 module internal login state here because we | |
379 | * create a separate process to handle each guest instance. If we needed | |
380 | * to handle multiple guests from one process, then we would need to keep | |
381 | * a lot of extra state in our card structure | |
382 | * */ | |
7267c094 | 383 | pin_string = g_malloc(pin_len+1); |
111a38b0 RR |
384 | memcpy(pin_string, pin, pin_len); |
385 | pin_string[pin_len] = 0; | |
386 | ||
387 | /* handle CAC expanded pins correctly */ | |
388 | for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) { | |
389 | pin_string[i] = 0; | |
390 | } | |
391 | ||
392 | rv = PK11_Authenticate(slot, PR_FALSE, pin_string); | |
393 | memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory | |
394 | to be snooped */ | |
7267c094 | 395 | g_free(pin_string); |
111a38b0 RR |
396 | if (rv == SECSuccess) { |
397 | return VCARD7816_STATUS_SUCCESS; | |
398 | } | |
399 | /* map the error from port get error */ | |
400 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED; | |
401 | } | |
402 | ||
403 | void | |
f032cfab | 404 | vcard_emul_logout(VCard *card) |
111a38b0 RR |
405 | { |
406 | PK11SlotInfo *slot; | |
407 | ||
408 | if (!nss_emul_init) { | |
409 | return; | |
410 | } | |
411 | ||
f032cfab RS |
412 | slot = vcard_emul_card_get_slot(card); |
413 | if (PK11_IsLoggedIn(slot, NULL)) { | |
414 | PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ | |
415 | } | |
416 | } | |
417 | ||
418 | void | |
419 | vcard_emul_reset(VCard *card, VCardPower power) | |
420 | { | |
111a38b0 RR |
421 | /* |
422 | * if we reset the card (either power on or power off), we lose our login | |
423 | * state | |
424 | */ | |
f032cfab RS |
425 | vcard_emul_logout(card); |
426 | ||
111a38b0 | 427 | /* TODO: we may also need to send insertion/removal events? */ |
111a38b0 RR |
428 | } |
429 | ||
111a38b0 RR |
430 | static VReader * |
431 | vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) | |
432 | { | |
433 | VReaderList *reader_list = vreader_get_reader_list(); | |
1687a089 | 434 | VReaderListEntry *current_entry; |
111a38b0 RR |
435 | |
436 | if (reader_list == NULL) { | |
437 | return NULL; | |
438 | } | |
439 | for (current_entry = vreader_list_get_first(reader_list); current_entry; | |
440 | current_entry = vreader_list_get_next(current_entry)) { | |
441 | VReader *reader = vreader_list_get_reader(current_entry); | |
442 | VReaderEmul *reader_emul = vreader_get_private(reader); | |
443 | if (reader_emul->slot == slot) { | |
124fe7fb | 444 | vreader_list_delete(reader_list); |
111a38b0 RR |
445 | return reader; |
446 | } | |
447 | vreader_free(reader); | |
448 | } | |
449 | ||
124fe7fb | 450 | vreader_list_delete(reader_list); |
111a38b0 RR |
451 | return NULL; |
452 | } | |
453 | ||
454 | /* | |
455 | * create a new reader emul | |
456 | */ | |
457 | static VReaderEmul * | |
458 | vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) | |
459 | { | |
460 | VReaderEmul *new_reader_emul; | |
461 | ||
78a4b8d2 | 462 | new_reader_emul = g_new(VReaderEmul, 1); |
111a38b0 RR |
463 | |
464 | new_reader_emul->slot = PK11_ReferenceSlot(slot); | |
465 | new_reader_emul->default_type = type; | |
be168af8 | 466 | new_reader_emul->type_params = g_strdup(params); |
111a38b0 RR |
467 | new_reader_emul->present = PR_FALSE; |
468 | new_reader_emul->series = 0; | |
469 | new_reader_emul->saved_vcard = NULL; | |
470 | return new_reader_emul; | |
471 | } | |
472 | ||
473 | static void | |
474 | vreader_emul_delete(VReaderEmul *vreader_emul) | |
475 | { | |
476 | if (vreader_emul == NULL) { | |
477 | return; | |
478 | } | |
479 | if (vreader_emul->slot) { | |
480 | PK11_FreeSlot(vreader_emul->slot); | |
481 | } | |
ec15993d | 482 | g_free(vreader_emul->type_params); |
7267c094 | 483 | g_free(vreader_emul); |
111a38b0 RR |
484 | } |
485 | ||
486 | /* | |
487 | * TODO: move this to emulater non-specific file | |
488 | */ | |
489 | static VCardEmulType | |
490 | vcard_emul_get_type(VReader *vreader) | |
491 | { | |
492 | VReaderEmul *vreader_emul; | |
493 | ||
494 | vreader_emul = vreader_get_private(vreader); | |
495 | if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { | |
496 | return vreader_emul->default_type; | |
497 | } | |
498 | ||
499 | return vcard_emul_type_select(vreader); | |
500 | } | |
501 | /* | |
502 | * TODO: move this to emulater non-specific file | |
503 | */ | |
504 | static const char * | |
505 | vcard_emul_get_type_params(VReader *vreader) | |
506 | { | |
507 | VReaderEmul *vreader_emul; | |
508 | ||
509 | vreader_emul = vreader_get_private(vreader); | |
510 | if (vreader_emul && vreader_emul->type_params) { | |
511 | return vreader_emul->type_params; | |
512 | } | |
513 | ||
514 | return ""; | |
515 | } | |
516 | ||
517 | /* pull the slot out of the reader private data */ | |
518 | static PK11SlotInfo * | |
519 | vcard_emul_reader_get_slot(VReader *vreader) | |
520 | { | |
521 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |
522 | if (vreader_emul == NULL) { | |
523 | return NULL; | |
524 | } | |
525 | return vreader_emul->slot; | |
526 | } | |
527 | ||
528 | /* | |
0b6a16c1 | 529 | * Card ATR's map to physical cards. vcard_alloc_atr will set appropriate |
111a38b0 RR |
530 | * historical bytes for any software emulated card. The remaining bytes can be |
531 | * used to indicate the actual emulator | |
532 | */ | |
0b6a16c1 AL |
533 | static unsigned char *nss_atr; |
534 | static int nss_atr_len; | |
111a38b0 RR |
535 | |
536 | void | |
537 | vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) | |
538 | { | |
0b6a16c1 | 539 | int len; |
111a38b0 RR |
540 | assert(atr != NULL); |
541 | ||
0b6a16c1 AL |
542 | if (nss_atr == NULL) { |
543 | nss_atr = vcard_alloc_atr("NSS", &nss_atr_len); | |
544 | } | |
545 | len = MIN(nss_atr_len, *atr_len); | |
111a38b0 RR |
546 | memcpy(atr, nss_atr, len); |
547 | *atr_len = len; | |
111a38b0 RR |
548 | } |
549 | ||
550 | /* | |
551 | * create a new card from certs and keys | |
552 | */ | |
553 | static VCard * | |
554 | vcard_emul_make_card(VReader *reader, | |
555 | unsigned char * const *certs, int *cert_len, | |
556 | VCardKey *keys[], int cert_count) | |
557 | { | |
558 | VCardEmul *vcard_emul; | |
559 | VCard *vcard; | |
560 | PK11SlotInfo *slot; | |
561 | VCardEmulType type; | |
562 | const char *params; | |
563 | ||
564 | type = vcard_emul_get_type(reader); | |
565 | ||
566 | /* ignore the inserted card */ | |
567 | if (type == VCARD_EMUL_NONE) { | |
568 | return NULL; | |
569 | } | |
570 | slot = vcard_emul_reader_get_slot(reader); | |
571 | if (slot == NULL) { | |
572 | return NULL; | |
573 | } | |
574 | ||
575 | params = vcard_emul_get_type_params(reader); | |
576 | /* params these can be NULL */ | |
577 | ||
578 | vcard_emul = vcard_emul_new_card(slot); | |
579 | if (vcard_emul == NULL) { | |
580 | return NULL; | |
581 | } | |
582 | vcard = vcard_new(vcard_emul, vcard_emul_delete_card); | |
583 | if (vcard == NULL) { | |
584 | vcard_emul_delete_card(vcard_emul); | |
585 | return NULL; | |
586 | } | |
587 | vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); | |
588 | return vcard; | |
589 | } | |
590 | ||
591 | ||
592 | /* | |
593 | * 'clone' a physical card as a virtual card | |
594 | */ | |
595 | static VCard * | |
596 | vcard_emul_mirror_card(VReader *vreader) | |
597 | { | |
598 | /* | |
599 | * lookup certs using the C_FindObjects. The Stan Cert handle won't give | |
600 | * us the real certs until we log in. | |
601 | */ | |
602 | PK11GenericObject *firstObj, *thisObj; | |
603 | int cert_count; | |
604 | unsigned char **certs; | |
605 | int *cert_len; | |
606 | VCardKey **keys; | |
607 | PK11SlotInfo *slot; | |
ee83d414 | 608 | VCard *card; |
111a38b0 RR |
609 | |
610 | slot = vcard_emul_reader_get_slot(vreader); | |
611 | if (slot == NULL) { | |
612 | return NULL; | |
613 | } | |
614 | ||
615 | firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE); | |
616 | if (firstObj == NULL) { | |
617 | return NULL; | |
618 | } | |
619 | ||
620 | /* count the certs */ | |
621 | cert_count = 0; | |
622 | for (thisObj = firstObj; thisObj; | |
623 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |
624 | cert_count++; | |
625 | } | |
626 | ||
111a38b0 | 627 | /* allocate the arrays */ |
48f0475f | 628 | vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); |
111a38b0 RR |
629 | |
630 | /* fill in the arrays */ | |
631 | cert_count = 0; | |
632 | for (thisObj = firstObj; thisObj; | |
633 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |
634 | SECItem derCert; | |
635 | CERTCertificate *cert; | |
636 | SECStatus rv; | |
637 | ||
638 | rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, | |
639 | CKA_VALUE, &derCert); | |
640 | if (rv != SECSuccess) { | |
641 | continue; | |
642 | } | |
643 | /* create floating temp cert. This gives us a cert structure even if | |
644 | * the token isn't logged in */ | |
645 | cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, | |
646 | NULL, PR_FALSE, PR_TRUE); | |
647 | SECITEM_FreeItem(&derCert, PR_FALSE); | |
648 | if (cert == NULL) { | |
649 | continue; | |
650 | } | |
651 | ||
652 | certs[cert_count] = cert->derCert.data; | |
653 | cert_len[cert_count] = cert->derCert.len; | |
654 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |
655 | cert_count++; | |
656 | CERT_DestroyCertificate(cert); /* key obj still has a reference */ | |
657 | } | |
658 | ||
659 | /* now create the card */ | |
ee83d414 | 660 | card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); |
7267c094 AL |
661 | g_free(certs); |
662 | g_free(cert_len); | |
663 | g_free(keys); | |
ee83d414 CF |
664 | |
665 | return card; | |
111a38b0 RR |
666 | } |
667 | ||
668 | static VCardEmulType default_card_type = VCARD_EMUL_NONE; | |
669 | static const char *default_type_params = ""; | |
670 | ||
671 | /* | |
672 | * This thread looks for card and reader insertions and puts events on the | |
673 | * event queue | |
674 | */ | |
675 | static void | |
676 | vcard_emul_event_thread(void *arg) | |
677 | { | |
678 | PK11SlotInfo *slot; | |
679 | VReader *vreader; | |
680 | VReaderEmul *vreader_emul; | |
681 | VCard *vcard; | |
682 | SECMODModule *module = (SECMODModule *)arg; | |
683 | ||
684 | do { | |
1b902f7d AL |
685 | /* |
686 | * XXX - the latency value doesn't matter one bit. you only get no | |
687 | * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500), | |
688 | * hard coded in coolkey. And it isn't coolkey's fault - the timeout | |
689 | * value we pass get's dropped on the floor before C_WaitForSlotEvent | |
690 | * is called. | |
691 | */ | |
111a38b0 RR |
692 | slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); |
693 | if (slot == NULL) { | |
1b902f7d AL |
694 | /* this could be just a no event indication */ |
695 | if (PORT_GetError() == SEC_ERROR_NO_EVENT) { | |
696 | continue; | |
697 | } | |
111a38b0 RR |
698 | break; |
699 | } | |
700 | vreader = vcard_emul_find_vreader_from_slot(slot); | |
701 | if (vreader == NULL) { | |
702 | /* new vreader */ | |
703 | vreader_emul = vreader_emul_new(slot, default_card_type, | |
704 | default_type_params); | |
705 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |
706 | vreader_emul_delete); | |
707 | PK11_FreeSlot(slot); | |
708 | slot = NULL; | |
709 | vreader_add_reader(vreader); | |
710 | vreader_free(vreader); | |
711 | continue; | |
712 | } | |
713 | /* card remove/insert */ | |
714 | vreader_emul = vreader_get_private(vreader); | |
715 | if (PK11_IsPresent(slot)) { | |
716 | int series = PK11_GetSlotSeries(slot); | |
717 | if (series != vreader_emul->series) { | |
718 | if (vreader_emul->present) { | |
719 | vreader_insert_card(vreader, NULL); | |
720 | } | |
721 | vcard = vcard_emul_mirror_card(vreader); | |
722 | vreader_insert_card(vreader, vcard); | |
723 | vcard_free(vcard); | |
724 | } | |
725 | vreader_emul->series = series; | |
726 | vreader_emul->present = 1; | |
727 | vreader_free(vreader); | |
728 | PK11_FreeSlot(slot); | |
729 | continue; | |
730 | } | |
731 | if (vreader_emul->present) { | |
732 | vreader_insert_card(vreader, NULL); | |
733 | } | |
734 | vreader_emul->series = 0; | |
735 | vreader_emul->present = 0; | |
736 | PK11_FreeSlot(slot); | |
737 | vreader_free(vreader); | |
738 | } while (1); | |
739 | } | |
740 | ||
741 | /* if the card is inserted when we start up, make sure our state is correct */ | |
742 | static void | |
743 | vcard_emul_init_series(VReader *vreader, VCard *vcard) | |
744 | { | |
745 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |
746 | PK11SlotInfo *slot = vreader_emul->slot; | |
747 | ||
748 | vreader_emul->present = PK11_IsPresent(slot); | |
749 | vreader_emul->series = PK11_GetSlotSeries(slot); | |
750 | if (vreader_emul->present == 0) { | |
751 | vreader_insert_card(vreader, NULL); | |
752 | } | |
753 | } | |
754 | ||
755 | /* | |
756 | * each module has a separate wait call, create a thread for each module that | |
757 | * we are using. | |
758 | */ | |
759 | static void | |
760 | vcard_emul_new_event_thread(SECMODModule *module) | |
761 | { | |
762 | PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, | |
763 | module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, | |
764 | PR_UNJOINABLE_THREAD, 0); | |
765 | } | |
766 | ||
767 | static const VCardEmulOptions default_options = { | |
768 | .nss_db = NULL, | |
769 | .vreader = NULL, | |
770 | .vreader_count = 0, | |
771 | .hw_card_type = VCARD_EMUL_CAC, | |
772 | .hw_type_params = "", | |
773 | .use_hw = PR_TRUE | |
774 | }; | |
775 | ||
776 | ||
777 | /* | |
778 | * NSS needs the app to supply a password prompt. In our case the only time | |
779 | * the password is supplied is as part of the Login APDU. The actual password | |
780 | * is passed in the pw_arg in that case. In all other cases pw_arg should be | |
781 | * NULL. | |
782 | */ | |
783 | static char * | |
784 | vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) | |
785 | { | |
786 | /* if it didn't work the first time, don't keep trying */ | |
787 | if (retries) { | |
788 | return NULL; | |
789 | } | |
790 | /* we are looking up a password when we don't have one in hand */ | |
791 | if (pw_arg == NULL) { | |
792 | return NULL; | |
793 | } | |
794 | /* TODO: we really should verify that were are using the right slot */ | |
795 | return PORT_Strdup(pw_arg); | |
796 | } | |
797 | ||
798 | /* Force a card removal even if the card is not physically removed */ | |
799 | VCardEmulError | |
800 | vcard_emul_force_card_remove(VReader *vreader) | |
801 | { | |
802 | if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { | |
803 | return VCARD_EMUL_FAIL; /* card is already removed */ | |
804 | } | |
805 | ||
806 | /* OK, remove it */ | |
807 | vreader_insert_card(vreader, NULL); | |
808 | return VCARD_EMUL_OK; | |
809 | } | |
810 | ||
811 | /* Re-insert of a card that has been removed by force removal */ | |
812 | VCardEmulError | |
813 | vcard_emul_force_card_insert(VReader *vreader) | |
814 | { | |
815 | VReaderEmul *vreader_emul; | |
816 | VCard *vcard; | |
817 | ||
818 | if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { | |
819 | return VCARD_EMUL_FAIL; /* card is already removed */ | |
820 | } | |
821 | vreader_emul = vreader_get_private(vreader); | |
822 | ||
823 | /* if it's a softcard, get the saved vcard from the reader emul structure */ | |
824 | if (vreader_emul->saved_vcard) { | |
825 | vcard = vcard_reference(vreader_emul->saved_vcard); | |
826 | } else { | |
827 | /* it must be a physical card, rebuild it */ | |
828 | if (!PK11_IsPresent(vreader_emul->slot)) { | |
829 | /* physical card has been removed, not way to reinsert it */ | |
830 | return VCARD_EMUL_FAIL; | |
831 | } | |
832 | vcard = vcard_emul_mirror_card(vreader); | |
833 | } | |
834 | vreader_insert_card(vreader, vcard); | |
835 | vcard_free(vcard); | |
836 | ||
837 | return VCARD_EMUL_OK; | |
838 | } | |
839 | ||
840 | ||
841 | static PRBool | |
842 | module_has_removable_hw_slots(SECMODModule *mod) | |
843 | { | |
844 | int i; | |
845 | PRBool ret = PR_FALSE; | |
846 | SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); | |
847 | ||
848 | if (!moduleLock) { | |
849 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); | |
850 | return ret; | |
851 | } | |
852 | SECMOD_GetReadLock(moduleLock); | |
853 | for (i = 0; i < mod->slotCount; i++) { | |
854 | PK11SlotInfo *slot = mod->slots[i]; | |
855 | if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) { | |
856 | ret = PR_TRUE; | |
857 | break; | |
858 | } | |
859 | } | |
860 | SECMOD_ReleaseReadLock(moduleLock); | |
861 | return ret; | |
862 | } | |
863 | ||
864 | /* Previously we returned FAIL if no readers found. This makes | |
865 | * no sense when using hardware, since there may be no readers connected | |
866 | * at the time vcard_emul_init is called, but they will be properly | |
867 | * recognized later. So Instead return FAIL only if no_hw==1 and no | |
868 | * vcards can be created (indicates error with certificates provided | |
869 | * or db), or if any other higher level error (NSS error, missing coolkey). */ | |
870 | static int vcard_emul_init_called; | |
871 | ||
872 | VCardEmulError | |
873 | vcard_emul_init(const VCardEmulOptions *options) | |
874 | { | |
875 | SECStatus rv; | |
48f0475f | 876 | PRBool has_readers = PR_FALSE; |
111a38b0 RR |
877 | VReader *vreader; |
878 | VReaderEmul *vreader_emul; | |
879 | SECMODListLock *module_lock; | |
880 | SECMODModuleList *module_list; | |
881 | SECMODModuleList *mlp; | |
882 | int i; | |
883 | ||
884 | if (vcard_emul_init_called) { | |
885 | return VCARD_EMUL_INIT_ALREADY_INITED; | |
886 | } | |
887 | vcard_emul_init_called = 1; | |
888 | vreader_init(); | |
889 | vevent_queue_init(); | |
890 | ||
891 | if (options == NULL) { | |
892 | options = &default_options; | |
893 | } | |
894 | ||
895 | /* first initialize NSS */ | |
896 | if (options->nss_db) { | |
897 | rv = NSS_Init(options->nss_db); | |
898 | } else { | |
667e0b4b | 899 | gchar *path; |
e2d9c5e7 MAL |
900 | #ifndef _WIN32 |
901 | path = g_strdup("/etc/pki/nssdb"); | |
902 | #else | |
903 | if (g_get_system_config_dirs() == NULL || | |
904 | g_get_system_config_dirs()[0] == NULL) { | |
905 | return VCARD_EMUL_FAIL; | |
906 | } | |
907 | ||
908 | path = g_build_filename( | |
909 | g_get_system_config_dirs()[0], "pki", "nssdb", NULL); | |
910 | #endif | |
e2d9c5e7 | 911 | |
667e0b4b | 912 | rv = NSS_Init(path); |
e2d9c5e7 | 913 | g_free(path); |
111a38b0 RR |
914 | } |
915 | if (rv != SECSuccess) { | |
916 | return VCARD_EMUL_FAIL; | |
917 | } | |
918 | /* Set password callback function */ | |
919 | PK11_SetPasswordFunc(vcard_emul_get_password); | |
920 | ||
921 | /* set up soft cards emulated by software certs rather than physical cards | |
922 | * */ | |
923 | for (i = 0; i < options->vreader_count; i++) { | |
924 | int j; | |
925 | int cert_count; | |
926 | unsigned char **certs; | |
927 | int *cert_len; | |
928 | VCardKey **keys; | |
929 | PK11SlotInfo *slot; | |
930 | ||
931 | slot = PK11_FindSlotByName(options->vreader[i].name); | |
932 | if (slot == NULL) { | |
933 | continue; | |
934 | } | |
935 | vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, | |
936 | options->vreader[i].type_params); | |
937 | vreader = vreader_new(options->vreader[i].vname, vreader_emul, | |
938 | vreader_emul_delete); | |
939 | vreader_add_reader(vreader); | |
111a38b0 | 940 | |
48f0475f SW |
941 | vcard_emul_alloc_arrays(&certs, &cert_len, &keys, |
942 | options->vreader[i].cert_count); | |
943 | ||
111a38b0 RR |
944 | cert_count = 0; |
945 | for (j = 0; j < options->vreader[i].cert_count; j++) { | |
946 | /* we should have a better way of identifying certs than by | |
947 | * nickname here */ | |
948 | CERTCertificate *cert = PK11_FindCertFromNickname( | |
949 | options->vreader[i].cert_name[j], | |
950 | NULL); | |
951 | if (cert == NULL) { | |
952 | continue; | |
953 | } | |
954 | certs[cert_count] = cert->derCert.data; | |
955 | cert_len[cert_count] = cert->derCert.len; | |
956 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |
957 | /* this is safe because the key is still holding a cert reference */ | |
958 | CERT_DestroyCertificate(cert); | |
959 | cert_count++; | |
960 | } | |
961 | if (cert_count) { | |
962 | VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, | |
963 | keys, cert_count); | |
964 | vreader_insert_card(vreader, vcard); | |
965 | vcard_emul_init_series(vreader, vcard); | |
966 | /* allow insertion and removal of soft cards */ | |
967 | vreader_emul->saved_vcard = vcard_reference(vcard); | |
968 | vcard_free(vcard); | |
969 | vreader_free(vreader); | |
970 | has_readers = PR_TRUE; | |
971 | } | |
7267c094 AL |
972 | g_free(certs); |
973 | g_free(cert_len); | |
974 | g_free(keys); | |
111a38b0 RR |
975 | } |
976 | ||
977 | /* if we aren't suppose to use hw, skip looking up hardware tokens */ | |
978 | if (!options->use_hw) { | |
979 | nss_emul_init = has_readers; | |
980 | return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; | |
981 | } | |
982 | ||
983 | /* make sure we have some PKCS #11 module loaded */ | |
984 | module_lock = SECMOD_GetDefaultModuleListLock(); | |
985 | module_list = SECMOD_GetDefaultModuleList(); | |
111a38b0 RR |
986 | SECMOD_GetReadLock(module_lock); |
987 | for (mlp = module_list; mlp; mlp = mlp->next) { | |
988 | SECMODModule *module = mlp->module; | |
989 | if (module_has_removable_hw_slots(module)) { | |
111a38b0 RR |
990 | break; |
991 | } | |
992 | } | |
993 | SECMOD_ReleaseReadLock(module_lock); | |
994 | ||
111a38b0 RR |
995 | /* now examine all the slots, finding which should be readers */ |
996 | /* We should control this with options. For now we mirror out any | |
997 | * removable hardware slot */ | |
998 | default_card_type = options->hw_card_type; | |
be168af8 | 999 | default_type_params = g_strdup(options->hw_type_params); |
111a38b0 RR |
1000 | |
1001 | SECMOD_GetReadLock(module_lock); | |
1002 | for (mlp = module_list; mlp; mlp = mlp->next) { | |
1003 | SECMODModule *module = mlp->module; | |
111a38b0 | 1004 | |
4e339882 AL |
1005 | /* Ignore the internal module */ |
1006 | if (module == NULL || module == SECMOD_GetInternalModule()) { | |
1007 | continue; | |
111a38b0 RR |
1008 | } |
1009 | ||
1010 | for (i = 0; i < module->slotCount; i++) { | |
1011 | PK11SlotInfo *slot = module->slots[i]; | |
1012 | ||
1013 | /* only map removable HW slots */ | |
1014 | if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { | |
1015 | continue; | |
1016 | } | |
6f06f178 AL |
1017 | if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) { |
1018 | /* | |
1019 | * coolkey <= 1.1.0-20 emulates this reader if it can't find | |
1020 | * any hardware readers. This causes problems, warn user of | |
1021 | * problems. | |
1022 | */ | |
1023 | fprintf(stderr, "known bad coolkey version - see " | |
1024 | "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n"); | |
1025 | continue; | |
1026 | } | |
111a38b0 RR |
1027 | vreader_emul = vreader_emul_new(slot, options->hw_card_type, |
1028 | options->hw_type_params); | |
1029 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |
1030 | vreader_emul_delete); | |
1031 | vreader_add_reader(vreader); | |
1032 | ||
111a38b0 RR |
1033 | if (PK11_IsPresent(slot)) { |
1034 | VCard *vcard; | |
1035 | vcard = vcard_emul_mirror_card(vreader); | |
1036 | vreader_insert_card(vreader, vcard); | |
1037 | vcard_emul_init_series(vreader, vcard); | |
1038 | vcard_free(vcard); | |
1039 | } | |
1040 | } | |
4e339882 | 1041 | vcard_emul_new_event_thread(module); |
111a38b0 RR |
1042 | } |
1043 | SECMOD_ReleaseReadLock(module_lock); | |
4e339882 | 1044 | nss_emul_init = PR_TRUE; |
111a38b0 RR |
1045 | |
1046 | return VCARD_EMUL_OK; | |
1047 | } | |
1048 | ||
1049 | /* Recreate card insert events for all readers (user should | |
1050 | * deduce implied reader insert. perhaps do a reader insert as well?) | |
1051 | */ | |
1052 | void | |
1053 | vcard_emul_replay_insertion_events(void) | |
1054 | { | |
1055 | VReaderListEntry *current_entry; | |
1687a089 | 1056 | VReaderListEntry *next_entry; |
111a38b0 RR |
1057 | VReaderList *list = vreader_get_reader_list(); |
1058 | ||
1059 | for (current_entry = vreader_list_get_first(list); current_entry; | |
1060 | current_entry = next_entry) { | |
1061 | VReader *vreader = vreader_list_get_reader(current_entry); | |
1062 | next_entry = vreader_list_get_next(current_entry); | |
1063 | vreader_queue_card_event(vreader); | |
1064 | } | |
124fe7fb MA |
1065 | |
1066 | vreader_list_delete(list); | |
111a38b0 RR |
1067 | } |
1068 | ||
1069 | /* | |
1070 | * Silly little functions to help parsing our argument string | |
1071 | */ | |
111a38b0 RR |
1072 | static int |
1073 | count_tokens(const char *str, char token, char token_end) | |
1074 | { | |
1075 | int count = 0; | |
1076 | ||
1077 | for (; *str; str++) { | |
1078 | if (*str == token) { | |
1079 | count++; | |
1080 | } | |
1081 | if (*str == token_end) { | |
1082 | break; | |
1083 | } | |
1084 | } | |
1085 | return count; | |
1086 | } | |
1087 | ||
1088 | static const char * | |
1089 | strip(const char *str) | |
1090 | { | |
685ff50f | 1091 | for (; *str && isspace(*str); str++) { |
111a38b0 RR |
1092 | } |
1093 | return str; | |
1094 | } | |
1095 | ||
1096 | static const char * | |
1097 | find_blank(const char *str) | |
1098 | { | |
685ff50f | 1099 | for (; *str && !isspace(*str); str++) { |
111a38b0 RR |
1100 | } |
1101 | return str; | |
1102 | } | |
1103 | ||
1104 | ||
1105 | /* | |
1106 | * We really want to use some existing argument parsing library here. That | |
fc27eefe | 1107 | * would give us a consistent look */ |
111a38b0 RR |
1108 | static VCardEmulOptions options; |
1109 | #define READER_STEP 4 | |
1110 | ||
d246b3cf CF |
1111 | /* Expects "args" to be at the beginning of a token (ie right after the ',' |
1112 | * ending the previous token), and puts the next token start in "token", | |
1113 | * and its length in "token_length". "token" will not be nul-terminated. | |
1114 | * After calling the macro, "args" will be advanced to the beginning of | |
1115 | * the next token. | |
1116 | * This macro may call continue or break. | |
1117 | */ | |
1118 | #define NEXT_TOKEN(token) \ | |
1119 | (token) = args; \ | |
1120 | args = strpbrk(args, ",)"); \ | |
1121 | if (*args == 0) { \ | |
1122 | break; \ | |
1123 | } \ | |
1124 | if (*args == ')') { \ | |
1125 | args++; \ | |
1126 | continue; \ | |
1127 | } \ | |
1128 | (token##_length) = args - (token); \ | |
1129 | args = strip(args+1); | |
1130 | ||
111a38b0 RR |
1131 | VCardEmulOptions * |
1132 | vcard_emul_options(const char *args) | |
1133 | { | |
1134 | int reader_count = 0; | |
1135 | VCardEmulOptions *opts; | |
111a38b0 RR |
1136 | |
1137 | /* Allow the future use of allocating the options structure on the fly */ | |
1138 | memcpy(&options, &default_options, sizeof(options)); | |
1139 | opts = &options; | |
1140 | ||
1141 | do { | |
1142 | args = strip(args); /* strip off the leading spaces */ | |
1143 | if (*args == ',') { | |
1144 | continue; | |
1145 | } | |
1146 | /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) | |
1147 | * cert_2,cert_3...) */ | |
1148 | if (strncmp(args, "soft=", 5) == 0) { | |
1149 | const char *name; | |
a5aa842a | 1150 | size_t name_length; |
111a38b0 | 1151 | const char *vname; |
a5aa842a | 1152 | size_t vname_length; |
111a38b0 | 1153 | const char *type_params; |
a5aa842a CF |
1154 | size_t type_params_length; |
1155 | char type_str[100]; | |
111a38b0 | 1156 | VCardEmulType type; |
a5aa842a | 1157 | int count, i; |
d09b8fa1 | 1158 | VirtualReaderOptions *vreaderOpt; |
111a38b0 RR |
1159 | |
1160 | args = strip(args + 5); | |
1161 | if (*args != '(') { | |
1162 | continue; | |
1163 | } | |
a5aa842a CF |
1164 | args = strip(args+1); |
1165 | ||
d246b3cf CF |
1166 | NEXT_TOKEN(name) |
1167 | NEXT_TOKEN(vname) | |
1168 | NEXT_TOKEN(type_params) | |
a5aa842a | 1169 | type_params_length = MIN(type_params_length, sizeof(type_str)-1); |
a22f8f38 MT |
1170 | memcpy(type_str, type_params, type_params_length); |
1171 | type_str[type_params_length] = '\0'; | |
a5aa842a CF |
1172 | type = vcard_emul_type_from_string(type_str); |
1173 | ||
d246b3cf | 1174 | NEXT_TOKEN(type_params) |
a5aa842a | 1175 | |
111a38b0 RR |
1176 | if (*args == 0) { |
1177 | break; | |
1178 | } | |
1179 | ||
1180 | if (opts->vreader_count >= reader_count) { | |
1181 | reader_count += READER_STEP; | |
d09b8fa1 MT |
1182 | opts->vreader = g_renew(VirtualReaderOptions, opts->vreader, |
1183 | reader_count); | |
111a38b0 | 1184 | } |
d09b8fa1 | 1185 | vreaderOpt = &opts->vreader[opts->vreader_count]; |
7267c094 AL |
1186 | vreaderOpt->name = g_strndup(name, name_length); |
1187 | vreaderOpt->vname = g_strndup(vname, vname_length); | |
111a38b0 RR |
1188 | vreaderOpt->card_type = type; |
1189 | vreaderOpt->type_params = | |
7267c094 | 1190 | g_strndup(type_params, type_params_length); |
a5aa842a | 1191 | count = count_tokens(args, ',', ')') + 1; |
111a38b0 | 1192 | vreaderOpt->cert_count = count; |
78a4b8d2 | 1193 | vreaderOpt->cert_name = g_new(char *, count); |
111a38b0 | 1194 | for (i = 0; i < count; i++) { |
a5aa842a CF |
1195 | const char *cert = args; |
1196 | args = strpbrk(args, ",)"); | |
7267c094 | 1197 | vreaderOpt->cert_name[i] = g_strndup(cert, args - cert); |
a5aa842a | 1198 | args = strip(args+1); |
111a38b0 RR |
1199 | } |
1200 | if (*args == ')') { | |
1201 | args++; | |
1202 | } | |
1203 | opts->vreader_count++; | |
1204 | /* use_hw= */ | |
1205 | } else if (strncmp(args, "use_hw=", 7) == 0) { | |
1206 | args = strip(args+7); | |
1207 | if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { | |
1208 | opts->use_hw = PR_FALSE; | |
1209 | } else { | |
1210 | opts->use_hw = PR_TRUE; | |
1211 | } | |
1212 | args = find_blank(args); | |
1213 | /* hw_type= */ | |
1214 | } else if (strncmp(args, "hw_type=", 8) == 0) { | |
1215 | args = strip(args+8); | |
1216 | opts->hw_card_type = vcard_emul_type_from_string(args); | |
1217 | args = find_blank(args); | |
1218 | /* hw_params= */ | |
1219 | } else if (strncmp(args, "hw_params=", 10) == 0) { | |
1220 | const char *params; | |
1221 | args = strip(args+10); | |
1222 | params = args; | |
1223 | args = find_blank(args); | |
7267c094 | 1224 | opts->hw_type_params = g_strndup(params, args-params); |
111a38b0 RR |
1225 | /* db="/data/base/path" */ |
1226 | } else if (strncmp(args, "db=", 3) == 0) { | |
1227 | const char *db; | |
1228 | args = strip(args+3); | |
1229 | if (*args != '"') { | |
1230 | continue; | |
1231 | } | |
1232 | args++; | |
1233 | db = args; | |
1234 | args = strpbrk(args, "\"\n"); | |
7267c094 | 1235 | opts->nss_db = g_strndup(db, args-db); |
111a38b0 RR |
1236 | if (*args != 0) { |
1237 | args++; | |
1238 | } | |
1239 | } else { | |
1240 | args = find_blank(args); | |
1241 | } | |
1242 | } while (*args != 0); | |
1243 | ||
1244 | return opts; | |
1245 | } | |
1246 | ||
1247 | void | |
1248 | vcard_emul_usage(void) | |
1249 | { | |
1250 | fprintf(stderr, | |
1251 | "emul args: comma separated list of the following arguments\n" | |
1252 | " db={nss_database} (default sql:/etc/pki/nssdb)\n" | |
1253 | " use_hw=[yes|no] (default yes)\n" | |
1254 | " hw_type={card_type_to_emulate} (default CAC)\n" | |
1255 | " hw_param={param_for_card} (default \"\")\n" | |
1256 | " soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" | |
1257 | " {cert1},{cert2},{cert3} (default none)\n" | |
1258 | "\n" | |
1259 | " {nss_database} The location of the NSS cert & key database\n" | |
1260 | " {card_type_to_emulate} What card interface to present to the guest\n" | |
1261 | " {param_for_card} Card interface specific parameters\n" | |
1262 | " {slot_name} NSS slot that contains the certs\n" | |
cba919da | 1263 | " {vreader_name} Virtual reader name to present to the guest\n" |
111a38b0 RR |
1264 | " {certN} Nickname of the certificate n on the virtual card\n" |
1265 | "\n" | |
1266 | "These parameters come as a single string separated by blanks or newlines." | |
1267 | "\n" | |
1268 | "Unless use_hw is set to no, all tokens that look like removable hardware\n" | |
1269 | "tokens will be presented to the guest using the emulator specified by\n" | |
1270 | "hw_type, and parameters of hw_param.\n" | |
1271 | "\n" | |
1272 | "If more one or more soft= parameters are specified, these readers will be\n" | |
1273 | "presented to the guest\n"); | |
1274 | } |