File: | libcacard/vcard_emul_nss.c |
Location: | line 1184, column 30 |
Description: | Access to field 'name' results in a dereference of a null pointer (loaded from variable 'vreaderOpt') |
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 | ||||
36 | #include "libcacard/vcardt_internal.h" | |||
37 | ||||
38 | ||||
39 | typedef enum { | |||
40 | VCardEmulUnknown = -1, | |||
41 | VCardEmulFalse = 0, | |||
42 | VCardEmulTrue = 1 | |||
43 | } VCardEmulTriState; | |||
44 | ||||
45 | struct VCardKeyStruct { | |||
46 | CERTCertificate *cert; | |||
47 | PK11SlotInfo *slot; | |||
48 | SECKEYPrivateKey *key; | |||
49 | VCardEmulTriState failedX509; | |||
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 | */ | |||
93 | static void | |||
94 | vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp, | |||
95 | VCardKey ***keysp, int cert_count) | |||
96 | { | |||
97 | *certsp = (unsigned char **)g_malloc(sizeof(unsigned char *)*cert_count); | |||
98 | *cert_lenp = (int *)g_malloc(sizeof(int)*cert_count); | |||
99 | *keysp = (VCardKey **)g_malloc(sizeof(VCardKey *)*cert_count); | |||
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((void*)0)) { | |||
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 | ||||
142 | key = (VCardKey *)g_malloc(sizeof(VCardKey)); | |||
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((void*)0)); | |||
149 | key->failedX509 = VCardEmulUnknown; | |||
150 | return key; | |||
151 | } | |||
152 | ||||
153 | /* destructor */ | |||
154 | void | |||
155 | vcard_emul_delete_key(VCardKey *key) | |||
156 | { | |||
157 | if (!nss_emul_init || (key == NULL((void*)0))) { | |||
158 | return; | |||
159 | } | |||
160 | if (key->key) { | |||
161 | SECKEY_DestroyPrivateKey(key->key); | |||
162 | key->key = NULL((void*)0); | |||
163 | } | |||
164 | if (key->cert) { | |||
165 | CERT_DestroyCertificate(key->cert); | |||
166 | } | |||
167 | if (key->slot) { | |||
168 | PK11_FreeSlot(key->slot); | |||
169 | } | |||
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((void*)0)); | |||
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_SATISFIED0x6985; | |||
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_INVALID0x6984; | |||
204 | case SEC_ERROR_NO_MEMORY: | |||
205 | return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE0x6581; | |||
206 | } | |||
207 | return VCARD7816_STATUS_EXC_ERROR_CHANGE0x6500; | |||
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; | |||
217 | PK11SlotInfo *slot; | |||
218 | SECStatus rv; | |||
219 | unsigned char buf[2048]; | |||
220 | unsigned char *bp = NULL((void*)0); | |||
221 | int pad_len; | |||
222 | vcard_7816_status_t ret = VCARD7816_STATUS_SUCCESS0x9000; | |||
223 | ||||
224 | if ((!nss_emul_init) || (key == NULL((void*)0))) { | |||
225 | /* couldn't get the key, indicate that we aren't logged in */ | |||
226 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED0x6985; | |||
227 | } | |||
228 | priv_key = vcard_emul_get_nss_key(key); | |||
229 | if (priv_key == NULL((void*)0)) { | |||
230 | /* couldn't get the key, indicate that we aren't logged in */ | |||
231 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED0x6985; | |||
232 | } | |||
233 | slot = vcard_emul_card_get_slot(card); | |||
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_INVALID0x6984; | |||
241 | } | |||
242 | /* be able to handle larger keys if necessariy */ | |||
243 | bp = &buf[0]; | |||
244 | if (sizeof(buf) < signature_len) { | |||
245 | bp = g_malloc(signature_len); | |||
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_5090x00000003)) { | |||
256 | rv = PK11_PrivDecryptRawPK11_PubDecryptRaw(priv_key, bp, &signature_len, signature_len, | |||
257 | buffer, buffer_size); | |||
258 | if (rv == SECSuccess) { | |||
259 | assert(buffer_size == signature_len)((buffer_size == signature_len) ? (void) (0) : __assert_fail ( "buffer_size == signature_len", "/home/stefan/src/qemu/qemu.org/qemu/libcacard/vcard_emul_nss.c" , 259, __PRETTY_FUNCTION__)); | |||
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)) { | |||
289 | /* yes, we have a properly formated PKCS #1 signature */ | |||
290 | /* | |||
291 | * NOTE: even if we accidentally got an encrypt buffer, which | |||
292 | * through shear luck started with 00, 01, ff, 00, it won't matter | |||
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)((buffer_size == signature.len) ? (void) (0) : __assert_fail ( "buffer_size == signature.len", "/home/stefan/src/qemu/qemu.org/qemu/libcacard/vcard_emul_nss.c" , 309, __PRETTY_FUNCTION__)); | |||
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)((pad_len < 4) ? (void) (0) : __assert_fail ("pad_len < 4" , "/home/stefan/src/qemu/qemu.org/qemu/libcacard/vcard_emul_nss.c" , 321, __PRETTY_FUNCTION__)); | |||
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) { | |||
349 | g_free(bp); | |||
350 | } | |||
351 | return ret; | |||
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; | |||
370 | unsigned char *pin_string = NULL((void*)0); | |||
371 | int i; | |||
372 | SECStatus rv; | |||
373 | ||||
374 | if (!nss_emul_init) { | |||
375 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED0x6985; | |||
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 | * */ | |||
383 | pin_string = g_malloc(pin_len+1); | |||
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_FALSE0, pin_string); | |||
393 | memset(pin_string, 0, pin_len); /* don't let the pin hang around in memory | |||
394 | to be snooped */ | |||
395 | g_free(pin_string); | |||
396 | if (rv == SECSuccess) { | |||
397 | return VCARD7816_STATUS_SUCCESS0x9000; | |||
398 | } | |||
399 | /* map the error from port get error */ | |||
400 | return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED0x6985; | |||
401 | } | |||
402 | ||||
403 | void | |||
404 | vcard_emul_reset(VCard *card, VCardPower power) | |||
405 | { | |||
406 | PK11SlotInfo *slot; | |||
407 | ||||
408 | if (!nss_emul_init) { | |||
409 | return; | |||
410 | } | |||
411 | ||||
412 | /* | |||
413 | * if we reset the card (either power on or power off), we lose our login | |||
414 | * state | |||
415 | */ | |||
416 | /* TODO: we may also need to send insertion/removal events? */ | |||
417 | slot = vcard_emul_card_get_slot(card); | |||
418 | PK11_Logout(slot); /* NOTE: ignoring SECStatus return value */ | |||
419 | } | |||
420 | ||||
421 | ||||
422 | static VReader * | |||
423 | vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot) | |||
424 | { | |||
425 | VReaderList *reader_list = vreader_get_reader_list(); | |||
426 | VReaderListEntry *current_entry = NULL((void*)0); | |||
427 | ||||
428 | if (reader_list == NULL((void*)0)) { | |||
429 | return NULL((void*)0); | |||
430 | } | |||
431 | for (current_entry = vreader_list_get_first(reader_list); current_entry; | |||
432 | current_entry = vreader_list_get_next(current_entry)) { | |||
433 | VReader *reader = vreader_list_get_reader(current_entry); | |||
434 | VReaderEmul *reader_emul = vreader_get_private(reader); | |||
435 | if (reader_emul->slot == slot) { | |||
436 | return reader; | |||
437 | } | |||
438 | vreader_free(reader); | |||
439 | } | |||
440 | ||||
441 | return NULL((void*)0); | |||
442 | } | |||
443 | ||||
444 | /* | |||
445 | * create a new reader emul | |||
446 | */ | |||
447 | static VReaderEmul * | |||
448 | vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params) | |||
449 | { | |||
450 | VReaderEmul *new_reader_emul; | |||
451 | ||||
452 | new_reader_emul = (VReaderEmul *)g_malloc(sizeof(VReaderEmul)); | |||
453 | ||||
454 | new_reader_emul->slot = PK11_ReferenceSlot(slot); | |||
455 | new_reader_emul->default_type = type; | |||
456 | new_reader_emul->type_params = g_strdup(params); | |||
457 | new_reader_emul->present = PR_FALSE0; | |||
458 | new_reader_emul->series = 0; | |||
459 | new_reader_emul->saved_vcard = NULL((void*)0); | |||
460 | return new_reader_emul; | |||
461 | } | |||
462 | ||||
463 | static void | |||
464 | vreader_emul_delete(VReaderEmul *vreader_emul) | |||
465 | { | |||
466 | if (vreader_emul == NULL((void*)0)) { | |||
467 | return; | |||
468 | } | |||
469 | if (vreader_emul->slot) { | |||
470 | PK11_FreeSlot(vreader_emul->slot); | |||
471 | } | |||
472 | if (vreader_emul->type_params) { | |||
473 | g_free(vreader_emul->type_params); | |||
474 | } | |||
475 | g_free(vreader_emul); | |||
476 | } | |||
477 | ||||
478 | /* | |||
479 | * TODO: move this to emulater non-specific file | |||
480 | */ | |||
481 | static VCardEmulType | |||
482 | vcard_emul_get_type(VReader *vreader) | |||
483 | { | |||
484 | VReaderEmul *vreader_emul; | |||
485 | ||||
486 | vreader_emul = vreader_get_private(vreader); | |||
487 | if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) { | |||
488 | return vreader_emul->default_type; | |||
489 | } | |||
490 | ||||
491 | return vcard_emul_type_select(vreader); | |||
492 | } | |||
493 | /* | |||
494 | * TODO: move this to emulater non-specific file | |||
495 | */ | |||
496 | static const char * | |||
497 | vcard_emul_get_type_params(VReader *vreader) | |||
498 | { | |||
499 | VReaderEmul *vreader_emul; | |||
500 | ||||
501 | vreader_emul = vreader_get_private(vreader); | |||
502 | if (vreader_emul && vreader_emul->type_params) { | |||
503 | return vreader_emul->type_params; | |||
504 | } | |||
505 | ||||
506 | return ""; | |||
507 | } | |||
508 | ||||
509 | /* pull the slot out of the reader private data */ | |||
510 | static PK11SlotInfo * | |||
511 | vcard_emul_reader_get_slot(VReader *vreader) | |||
512 | { | |||
513 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |||
514 | if (vreader_emul == NULL((void*)0)) { | |||
515 | return NULL((void*)0); | |||
516 | } | |||
517 | return vreader_emul->slot; | |||
518 | } | |||
519 | ||||
520 | /* | |||
521 | * Card ATR's map to physical cards. vcard_alloc_atr will set appropriate | |||
522 | * historical bytes for any software emulated card. The remaining bytes can be | |||
523 | * used to indicate the actual emulator | |||
524 | */ | |||
525 | static unsigned char *nss_atr; | |||
526 | static int nss_atr_len; | |||
527 | ||||
528 | void | |||
529 | vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len) | |||
530 | { | |||
531 | int len; | |||
532 | assert(atr != NULL)((atr != ((void*)0)) ? (void) (0) : __assert_fail ("atr != ((void*)0)" , "/home/stefan/src/qemu/qemu.org/qemu/libcacard/vcard_emul_nss.c" , 532, __PRETTY_FUNCTION__)); | |||
533 | ||||
534 | if (nss_atr == NULL((void*)0)) { | |||
535 | nss_atr = vcard_alloc_atr("NSS", &nss_atr_len); | |||
536 | } | |||
537 | len = MIN(nss_atr_len, *atr_len)(((nss_atr_len) < (*atr_len)) ? (nss_atr_len) : (*atr_len) ); | |||
538 | memcpy(atr, nss_atr, len); | |||
539 | *atr_len = len; | |||
540 | } | |||
541 | ||||
542 | /* | |||
543 | * create a new card from certs and keys | |||
544 | */ | |||
545 | static VCard * | |||
546 | vcard_emul_make_card(VReader *reader, | |||
547 | unsigned char * const *certs, int *cert_len, | |||
548 | VCardKey *keys[], int cert_count) | |||
549 | { | |||
550 | VCardEmul *vcard_emul; | |||
551 | VCard *vcard; | |||
552 | PK11SlotInfo *slot; | |||
553 | VCardEmulType type; | |||
554 | const char *params; | |||
555 | ||||
556 | type = vcard_emul_get_type(reader); | |||
557 | ||||
558 | /* ignore the inserted card */ | |||
559 | if (type == VCARD_EMUL_NONE) { | |||
560 | return NULL((void*)0); | |||
561 | } | |||
562 | slot = vcard_emul_reader_get_slot(reader); | |||
563 | if (slot == NULL((void*)0)) { | |||
564 | return NULL((void*)0); | |||
565 | } | |||
566 | ||||
567 | params = vcard_emul_get_type_params(reader); | |||
568 | /* params these can be NULL */ | |||
569 | ||||
570 | vcard_emul = vcard_emul_new_card(slot); | |||
571 | if (vcard_emul == NULL((void*)0)) { | |||
572 | return NULL((void*)0); | |||
573 | } | |||
574 | vcard = vcard_new(vcard_emul, vcard_emul_delete_card); | |||
575 | if (vcard == NULL((void*)0)) { | |||
576 | vcard_emul_delete_card(vcard_emul); | |||
577 | return NULL((void*)0); | |||
578 | } | |||
579 | vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count); | |||
580 | return vcard; | |||
581 | } | |||
582 | ||||
583 | ||||
584 | /* | |||
585 | * 'clone' a physical card as a virtual card | |||
586 | */ | |||
587 | static VCard * | |||
588 | vcard_emul_mirror_card(VReader *vreader) | |||
589 | { | |||
590 | /* | |||
591 | * lookup certs using the C_FindObjects. The Stan Cert handle won't give | |||
592 | * us the real certs until we log in. | |||
593 | */ | |||
594 | PK11GenericObject *firstObj, *thisObj; | |||
595 | int cert_count; | |||
596 | unsigned char **certs; | |||
597 | int *cert_len; | |||
598 | VCardKey **keys; | |||
599 | PK11SlotInfo *slot; | |||
600 | VCard *card; | |||
601 | ||||
602 | slot = vcard_emul_reader_get_slot(vreader); | |||
603 | if (slot == NULL((void*)0)) { | |||
604 | return NULL((void*)0); | |||
605 | } | |||
606 | ||||
607 | firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE0x00000001); | |||
608 | if (firstObj == NULL((void*)0)) { | |||
609 | return NULL((void*)0); | |||
610 | } | |||
611 | ||||
612 | /* count the certs */ | |||
613 | cert_count = 0; | |||
614 | for (thisObj = firstObj; thisObj; | |||
615 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |||
616 | cert_count++; | |||
617 | } | |||
618 | ||||
619 | if (cert_count == 0) { | |||
620 | PK11_DestroyGenericObjects(firstObj); | |||
621 | return NULL((void*)0); | |||
622 | } | |||
623 | ||||
624 | /* allocate the arrays */ | |||
625 | vcard_emul_alloc_arrays(&certs, &cert_len, &keys, cert_count); | |||
626 | ||||
627 | /* fill in the arrays */ | |||
628 | cert_count = 0; | |||
629 | for (thisObj = firstObj; thisObj; | |||
630 | thisObj = PK11_GetNextGenericObject(thisObj)) { | |||
631 | SECItem derCert; | |||
632 | CERTCertificate *cert; | |||
633 | SECStatus rv; | |||
634 | ||||
635 | rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj, | |||
636 | CKA_VALUE0x00000011, &derCert); | |||
637 | if (rv != SECSuccess) { | |||
638 | continue; | |||
639 | } | |||
640 | /* create floating temp cert. This gives us a cert structure even if | |||
641 | * the token isn't logged in */ | |||
642 | cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert, | |||
643 | NULL((void*)0), PR_FALSE0, PR_TRUE1); | |||
644 | SECITEM_FreeItem(&derCert, PR_FALSE0); | |||
645 | if (cert == NULL((void*)0)) { | |||
646 | continue; | |||
647 | } | |||
648 | ||||
649 | certs[cert_count] = cert->derCert.data; | |||
650 | cert_len[cert_count] = cert->derCert.len; | |||
651 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |||
652 | cert_count++; | |||
653 | CERT_DestroyCertificate(cert); /* key obj still has a reference */ | |||
654 | } | |||
655 | ||||
656 | /* now create the card */ | |||
657 | card = vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count); | |||
658 | g_free(certs); | |||
659 | g_free(cert_len); | |||
660 | g_free(keys); | |||
661 | ||||
662 | return card; | |||
663 | } | |||
664 | ||||
665 | static VCardEmulType default_card_type = VCARD_EMUL_NONE; | |||
666 | static const char *default_type_params = ""; | |||
667 | ||||
668 | /* | |||
669 | * This thread looks for card and reader insertions and puts events on the | |||
670 | * event queue | |||
671 | */ | |||
672 | static void | |||
673 | vcard_emul_event_thread(void *arg) | |||
674 | { | |||
675 | PK11SlotInfo *slot; | |||
676 | VReader *vreader; | |||
677 | VReaderEmul *vreader_emul; | |||
678 | VCard *vcard; | |||
679 | SECMODModule *module = (SECMODModule *)arg; | |||
680 | ||||
681 | do { | |||
682 | /* | |||
683 | * XXX - the latency value doesn't matter one bit. you only get no | |||
684 | * blocking (flags |= CKF_DONT_BLOCK) or PKCS11_WAIT_LATENCY (==500), | |||
685 | * hard coded in coolkey. And it isn't coolkey's fault - the timeout | |||
686 | * value we pass get's dropped on the floor before C_WaitForSlotEvent | |||
687 | * is called. | |||
688 | */ | |||
689 | slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500); | |||
690 | if (slot == NULL((void*)0)) { | |||
691 | /* this could be just a no event indication */ | |||
692 | if (PORT_GetError() == SEC_ERROR_NO_EVENT) { | |||
693 | continue; | |||
694 | } | |||
695 | break; | |||
696 | } | |||
697 | vreader = vcard_emul_find_vreader_from_slot(slot); | |||
698 | if (vreader == NULL((void*)0)) { | |||
699 | /* new vreader */ | |||
700 | vreader_emul = vreader_emul_new(slot, default_card_type, | |||
701 | default_type_params); | |||
702 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |||
703 | vreader_emul_delete); | |||
704 | PK11_FreeSlot(slot); | |||
705 | slot = NULL((void*)0); | |||
706 | vreader_add_reader(vreader); | |||
707 | vreader_free(vreader); | |||
708 | continue; | |||
709 | } | |||
710 | /* card remove/insert */ | |||
711 | vreader_emul = vreader_get_private(vreader); | |||
712 | if (PK11_IsPresent(slot)) { | |||
713 | int series = PK11_GetSlotSeries(slot); | |||
714 | if (series != vreader_emul->series) { | |||
715 | if (vreader_emul->present) { | |||
716 | vreader_insert_card(vreader, NULL((void*)0)); | |||
717 | } | |||
718 | vcard = vcard_emul_mirror_card(vreader); | |||
719 | vreader_insert_card(vreader, vcard); | |||
720 | vcard_free(vcard); | |||
721 | } | |||
722 | vreader_emul->series = series; | |||
723 | vreader_emul->present = 1; | |||
724 | vreader_free(vreader); | |||
725 | PK11_FreeSlot(slot); | |||
726 | continue; | |||
727 | } | |||
728 | if (vreader_emul->present) { | |||
729 | vreader_insert_card(vreader, NULL((void*)0)); | |||
730 | } | |||
731 | vreader_emul->series = 0; | |||
732 | vreader_emul->present = 0; | |||
733 | PK11_FreeSlot(slot); | |||
734 | vreader_free(vreader); | |||
735 | } while (1); | |||
736 | } | |||
737 | ||||
738 | /* if the card is inserted when we start up, make sure our state is correct */ | |||
739 | static void | |||
740 | vcard_emul_init_series(VReader *vreader, VCard *vcard) | |||
741 | { | |||
742 | VReaderEmul *vreader_emul = vreader_get_private(vreader); | |||
743 | PK11SlotInfo *slot = vreader_emul->slot; | |||
744 | ||||
745 | vreader_emul->present = PK11_IsPresent(slot); | |||
746 | vreader_emul->series = PK11_GetSlotSeries(slot); | |||
747 | if (vreader_emul->present == 0) { | |||
748 | vreader_insert_card(vreader, NULL((void*)0)); | |||
749 | } | |||
750 | } | |||
751 | ||||
752 | /* | |||
753 | * each module has a separate wait call, create a thread for each module that | |||
754 | * we are using. | |||
755 | */ | |||
756 | static void | |||
757 | vcard_emul_new_event_thread(SECMODModule *module) | |||
758 | { | |||
759 | PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread, | |||
760 | module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD, | |||
761 | PR_UNJOINABLE_THREAD, 0); | |||
762 | } | |||
763 | ||||
764 | static const VCardEmulOptions default_options = { | |||
765 | .nss_db = NULL((void*)0), | |||
766 | .vreader = NULL((void*)0), | |||
767 | .vreader_count = 0, | |||
768 | .hw_card_type = VCARD_EMUL_CAC, | |||
769 | .hw_type_params = "", | |||
770 | .use_hw = PR_TRUE1 | |||
771 | }; | |||
772 | ||||
773 | ||||
774 | /* | |||
775 | * NSS needs the app to supply a password prompt. In our case the only time | |||
776 | * the password is supplied is as part of the Login APDU. The actual password | |||
777 | * is passed in the pw_arg in that case. In all other cases pw_arg should be | |||
778 | * NULL. | |||
779 | */ | |||
780 | static char * | |||
781 | vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg) | |||
782 | { | |||
783 | /* if it didn't work the first time, don't keep trying */ | |||
784 | if (retries) { | |||
785 | return NULL((void*)0); | |||
786 | } | |||
787 | /* we are looking up a password when we don't have one in hand */ | |||
788 | if (pw_arg == NULL((void*)0)) { | |||
789 | return NULL((void*)0); | |||
790 | } | |||
791 | /* TODO: we really should verify that were are using the right slot */ | |||
792 | return PORT_Strdup(pw_arg); | |||
793 | } | |||
794 | ||||
795 | /* Force a card removal even if the card is not physically removed */ | |||
796 | VCardEmulError | |||
797 | vcard_emul_force_card_remove(VReader *vreader) | |||
798 | { | |||
799 | if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) { | |||
800 | return VCARD_EMUL_FAIL; /* card is already removed */ | |||
801 | } | |||
802 | ||||
803 | /* OK, remove it */ | |||
804 | vreader_insert_card(vreader, NULL((void*)0)); | |||
805 | return VCARD_EMUL_OK; | |||
806 | } | |||
807 | ||||
808 | /* Re-insert of a card that has been removed by force removal */ | |||
809 | VCardEmulError | |||
810 | vcard_emul_force_card_insert(VReader *vreader) | |||
811 | { | |||
812 | VReaderEmul *vreader_emul; | |||
813 | VCard *vcard; | |||
814 | ||||
815 | if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) { | |||
816 | return VCARD_EMUL_FAIL; /* card is already removed */ | |||
817 | } | |||
818 | vreader_emul = vreader_get_private(vreader); | |||
819 | ||||
820 | /* if it's a softcard, get the saved vcard from the reader emul structure */ | |||
821 | if (vreader_emul->saved_vcard) { | |||
822 | vcard = vcard_reference(vreader_emul->saved_vcard); | |||
823 | } else { | |||
824 | /* it must be a physical card, rebuild it */ | |||
825 | if (!PK11_IsPresent(vreader_emul->slot)) { | |||
826 | /* physical card has been removed, not way to reinsert it */ | |||
827 | return VCARD_EMUL_FAIL; | |||
828 | } | |||
829 | vcard = vcard_emul_mirror_card(vreader); | |||
830 | } | |||
831 | vreader_insert_card(vreader, vcard); | |||
832 | vcard_free(vcard); | |||
833 | ||||
834 | return VCARD_EMUL_OK; | |||
835 | } | |||
836 | ||||
837 | ||||
838 | static PRBool | |||
839 | module_has_removable_hw_slots(SECMODModule *mod) | |||
840 | { | |||
841 | int i; | |||
842 | PRBool ret = PR_FALSE0; | |||
843 | SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); | |||
844 | ||||
845 | if (!moduleLock) { | |||
846 | PORT_SetError(SEC_ERROR_NOT_INITIALIZED); | |||
847 | return ret; | |||
848 | } | |||
849 | SECMOD_GetReadLock(moduleLock); | |||
850 | for (i = 0; i < mod->slotCount; i++) { | |||
851 | PK11SlotInfo *slot = mod->slots[i]; | |||
852 | if (PK11_IsRemovable(slot) && PK11_IsHW(slot)) { | |||
853 | ret = PR_TRUE1; | |||
854 | break; | |||
855 | } | |||
856 | } | |||
857 | SECMOD_ReleaseReadLock(moduleLock); | |||
858 | return ret; | |||
859 | } | |||
860 | ||||
861 | /* Previously we returned FAIL if no readers found. This makes | |||
862 | * no sense when using hardware, since there may be no readers connected | |||
863 | * at the time vcard_emul_init is called, but they will be properly | |||
864 | * recognized later. So Instead return FAIL only if no_hw==1 and no | |||
865 | * vcards can be created (indicates error with certificates provided | |||
866 | * or db), or if any other higher level error (NSS error, missing coolkey). */ | |||
867 | static int vcard_emul_init_called; | |||
868 | ||||
869 | VCardEmulError | |||
870 | vcard_emul_init(const VCardEmulOptions *options) | |||
871 | { | |||
872 | SECStatus rv; | |||
873 | PRBool has_readers = PR_FALSE0; | |||
874 | VReader *vreader; | |||
875 | VReaderEmul *vreader_emul; | |||
876 | SECMODListLock *module_lock; | |||
877 | SECMODModuleList *module_list; | |||
878 | SECMODModuleList *mlp; | |||
879 | int i; | |||
880 | ||||
881 | if (vcard_emul_init_called) { | |||
882 | return VCARD_EMUL_INIT_ALREADY_INITED; | |||
883 | } | |||
884 | vcard_emul_init_called = 1; | |||
885 | vreader_init(); | |||
886 | vevent_queue_init(); | |||
887 | ||||
888 | if (options == NULL((void*)0)) { | |||
889 | options = &default_options; | |||
890 | } | |||
891 | ||||
892 | /* first initialize NSS */ | |||
893 | if (options->nss_db) { | |||
894 | rv = NSS_Init(options->nss_db); | |||
895 | } else { | |||
896 | gchar *path; | |||
897 | #ifndef _WIN32 | |||
898 | path = g_strdup("/etc/pki/nssdb"); | |||
899 | #else | |||
900 | if (g_get_system_config_dirs() == NULL((void*)0) || | |||
901 | g_get_system_config_dirs()[0] == NULL((void*)0)) { | |||
902 | return VCARD_EMUL_FAIL; | |||
903 | } | |||
904 | ||||
905 | path = g_build_filename( | |||
906 | g_get_system_config_dirs()[0], "pki", "nssdb", NULL((void*)0)); | |||
907 | #endif | |||
908 | ||||
909 | rv = NSS_Init(path); | |||
910 | g_free(path); | |||
911 | } | |||
912 | if (rv != SECSuccess) { | |||
913 | return VCARD_EMUL_FAIL; | |||
914 | } | |||
915 | /* Set password callback function */ | |||
916 | PK11_SetPasswordFunc(vcard_emul_get_password); | |||
917 | ||||
918 | /* set up soft cards emulated by software certs rather than physical cards | |||
919 | * */ | |||
920 | for (i = 0; i < options->vreader_count; i++) { | |||
921 | int j; | |||
922 | int cert_count; | |||
923 | unsigned char **certs; | |||
924 | int *cert_len; | |||
925 | VCardKey **keys; | |||
926 | PK11SlotInfo *slot; | |||
927 | ||||
928 | slot = PK11_FindSlotByName(options->vreader[i].name); | |||
929 | if (slot == NULL((void*)0)) { | |||
930 | continue; | |||
931 | } | |||
932 | vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type, | |||
933 | options->vreader[i].type_params); | |||
934 | vreader = vreader_new(options->vreader[i].vname, vreader_emul, | |||
935 | vreader_emul_delete); | |||
936 | vreader_add_reader(vreader); | |||
937 | ||||
938 | vcard_emul_alloc_arrays(&certs, &cert_len, &keys, | |||
939 | options->vreader[i].cert_count); | |||
940 | ||||
941 | cert_count = 0; | |||
942 | for (j = 0; j < options->vreader[i].cert_count; j++) { | |||
943 | /* we should have a better way of identifying certs than by | |||
944 | * nickname here */ | |||
945 | CERTCertificate *cert = PK11_FindCertFromNickname( | |||
946 | options->vreader[i].cert_name[j], | |||
947 | NULL((void*)0)); | |||
948 | if (cert == NULL((void*)0)) { | |||
949 | continue; | |||
950 | } | |||
951 | certs[cert_count] = cert->derCert.data; | |||
952 | cert_len[cert_count] = cert->derCert.len; | |||
953 | keys[cert_count] = vcard_emul_make_key(slot, cert); | |||
954 | /* this is safe because the key is still holding a cert reference */ | |||
955 | CERT_DestroyCertificate(cert); | |||
956 | cert_count++; | |||
957 | } | |||
958 | if (cert_count) { | |||
959 | VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len, | |||
960 | keys, cert_count); | |||
961 | vreader_insert_card(vreader, vcard); | |||
962 | vcard_emul_init_series(vreader, vcard); | |||
963 | /* allow insertion and removal of soft cards */ | |||
964 | vreader_emul->saved_vcard = vcard_reference(vcard); | |||
965 | vcard_free(vcard); | |||
966 | vreader_free(vreader); | |||
967 | has_readers = PR_TRUE1; | |||
968 | } | |||
969 | g_free(certs); | |||
970 | g_free(cert_len); | |||
971 | g_free(keys); | |||
972 | } | |||
973 | ||||
974 | /* if we aren't suppose to use hw, skip looking up hardware tokens */ | |||
975 | if (!options->use_hw) { | |||
976 | nss_emul_init = has_readers; | |||
977 | return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL; | |||
978 | } | |||
979 | ||||
980 | /* make sure we have some PKCS #11 module loaded */ | |||
981 | module_lock = SECMOD_GetDefaultModuleListLock(); | |||
982 | module_list = SECMOD_GetDefaultModuleList(); | |||
983 | SECMOD_GetReadLock(module_lock); | |||
984 | for (mlp = module_list; mlp; mlp = mlp->next) { | |||
985 | SECMODModule *module = mlp->module; | |||
986 | if (module_has_removable_hw_slots(module)) { | |||
987 | break; | |||
988 | } | |||
989 | } | |||
990 | SECMOD_ReleaseReadLock(module_lock); | |||
991 | ||||
992 | /* now examine all the slots, finding which should be readers */ | |||
993 | /* We should control this with options. For now we mirror out any | |||
994 | * removable hardware slot */ | |||
995 | default_card_type = options->hw_card_type; | |||
996 | default_type_params = g_strdup(options->hw_type_params); | |||
997 | ||||
998 | SECMOD_GetReadLock(module_lock); | |||
999 | for (mlp = module_list; mlp; mlp = mlp->next) { | |||
1000 | SECMODModule *module = mlp->module; | |||
1001 | ||||
1002 | /* Ignore the internal module */ | |||
1003 | if (module == NULL((void*)0) || module == SECMOD_GetInternalModule()) { | |||
1004 | continue; | |||
1005 | } | |||
1006 | ||||
1007 | for (i = 0; i < module->slotCount; i++) { | |||
1008 | PK11SlotInfo *slot = module->slots[i]; | |||
1009 | ||||
1010 | /* only map removable HW slots */ | |||
1011 | if (slot == NULL((void*)0) || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) { | |||
1012 | continue; | |||
1013 | } | |||
1014 | if (strcmp("E-Gate 0 0", PK11_GetSlotName(slot)) == 0) { | |||
1015 | /* | |||
1016 | * coolkey <= 1.1.0-20 emulates this reader if it can't find | |||
1017 | * any hardware readers. This causes problems, warn user of | |||
1018 | * problems. | |||
1019 | */ | |||
1020 | fprintf(stderrstderr, "known bad coolkey version - see " | |||
1021 | "https://bugzilla.redhat.com/show_bug.cgi?id=802435\n"); | |||
1022 | continue; | |||
1023 | } | |||
1024 | vreader_emul = vreader_emul_new(slot, options->hw_card_type, | |||
1025 | options->hw_type_params); | |||
1026 | vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul, | |||
1027 | vreader_emul_delete); | |||
1028 | vreader_add_reader(vreader); | |||
1029 | ||||
1030 | if (PK11_IsPresent(slot)) { | |||
1031 | VCard *vcard; | |||
1032 | vcard = vcard_emul_mirror_card(vreader); | |||
1033 | vreader_insert_card(vreader, vcard); | |||
1034 | vcard_emul_init_series(vreader, vcard); | |||
1035 | vcard_free(vcard); | |||
1036 | } | |||
1037 | } | |||
1038 | vcard_emul_new_event_thread(module); | |||
1039 | } | |||
1040 | SECMOD_ReleaseReadLock(module_lock); | |||
1041 | nss_emul_init = PR_TRUE1; | |||
1042 | ||||
1043 | return VCARD_EMUL_OK; | |||
1044 | } | |||
1045 | ||||
1046 | /* Recreate card insert events for all readers (user should | |||
1047 | * deduce implied reader insert. perhaps do a reader insert as well?) | |||
1048 | */ | |||
1049 | void | |||
1050 | vcard_emul_replay_insertion_events(void) | |||
1051 | { | |||
1052 | VReaderListEntry *current_entry; | |||
1053 | VReaderListEntry *next_entry = NULL((void*)0); | |||
1054 | VReaderList *list = vreader_get_reader_list(); | |||
1055 | ||||
1056 | for (current_entry = vreader_list_get_first(list); current_entry; | |||
1057 | current_entry = next_entry) { | |||
1058 | VReader *vreader = vreader_list_get_reader(current_entry); | |||
1059 | next_entry = vreader_list_get_next(current_entry); | |||
1060 | vreader_queue_card_event(vreader); | |||
1061 | } | |||
1062 | } | |||
1063 | ||||
1064 | /* | |||
1065 | * Silly little functions to help parsing our argument string | |||
1066 | */ | |||
1067 | static int | |||
1068 | count_tokens(const char *str, char token, char token_end) | |||
1069 | { | |||
1070 | int count = 0; | |||
1071 | ||||
1072 | for (; *str; str++) { | |||
1073 | if (*str == token) { | |||
1074 | count++; | |||
1075 | } | |||
1076 | if (*str == token_end) { | |||
1077 | break; | |||
1078 | } | |||
1079 | } | |||
1080 | return count; | |||
1081 | } | |||
1082 | ||||
1083 | static const char * | |||
1084 | strip(const char *str) | |||
1085 | { | |||
1086 | for (; *str && isspace(*str)((*__ctype_b_loc ())[(int) ((*str))] & (unsigned short int ) _ISspace); str++) { | |||
1087 | } | |||
1088 | return str; | |||
1089 | } | |||
1090 | ||||
1091 | static const char * | |||
1092 | find_blank(const char *str) | |||
1093 | { | |||
1094 | for (; *str && !isspace(*str)((*__ctype_b_loc ())[(int) ((*str))] & (unsigned short int ) _ISspace); str++) { | |||
1095 | } | |||
1096 | return str; | |||
1097 | } | |||
1098 | ||||
1099 | ||||
1100 | /* | |||
1101 | * We really want to use some existing argument parsing library here. That | |||
1102 | * would give us a consistent look */ | |||
1103 | static VCardEmulOptions options; | |||
1104 | #define READER_STEP4 4 | |||
1105 | ||||
1106 | /* Expects "args" to be at the beginning of a token (ie right after the ',' | |||
1107 | * ending the previous token), and puts the next token start in "token", | |||
1108 | * and its length in "token_length". "token" will not be nul-terminated. | |||
1109 | * After calling the macro, "args" will be advanced to the beginning of | |||
1110 | * the next token. | |||
1111 | * This macro may call continue or break. | |||
1112 | */ | |||
1113 | #define NEXT_TOKEN(token)(token) = args; args = strpbrk(args, ",)"); if (*args == 0) { break; } if (*args == ')') { args++; continue; } (token_length ) = args - (token); args = strip(args+1); \ | |||
1114 | (token) = args; \ | |||
1115 | args = strpbrk(args, ",)"); \ | |||
1116 | if (*args == 0) { \ | |||
1117 | break; \ | |||
1118 | } \ | |||
1119 | if (*args == ')') { \ | |||
1120 | args++; \ | |||
1121 | continue; \ | |||
1122 | } \ | |||
1123 | (token##_length) = args - (token); \ | |||
1124 | args = strip(args+1); | |||
1125 | ||||
1126 | VCardEmulOptions * | |||
1127 | vcard_emul_options(const char *args) | |||
1128 | { | |||
1129 | int reader_count = 0; | |||
1130 | VCardEmulOptions *opts; | |||
1131 | ||||
1132 | /* Allow the future use of allocating the options structure on the fly */ | |||
1133 | memcpy(&options, &default_options, sizeof(options)); | |||
1134 | opts = &options; | |||
1135 | ||||
1136 | do { | |||
1137 | args = strip(args); /* strip off the leading spaces */ | |||
1138 | if (*args == ',') { | |||
| ||||
1139 | continue; | |||
1140 | } | |||
1141 | /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol) | |||
1142 | * cert_2,cert_3...) */ | |||
1143 | if (strncmp(args, "soft=", 5) == 0) { | |||
1144 | const char *name; | |||
1145 | size_t name_length; | |||
1146 | const char *vname; | |||
1147 | size_t vname_length; | |||
1148 | const char *type_params; | |||
1149 | size_t type_params_length; | |||
1150 | char type_str[100]; | |||
1151 | VCardEmulType type; | |||
1152 | int count, i; | |||
1153 | VirtualReaderOptions *vreaderOpt = NULL((void*)0); | |||
1154 | ||||
1155 | args = strip(args + 5); | |||
1156 | if (*args != '(') { | |||
1157 | continue; | |||
1158 | } | |||
1159 | args = strip(args+1); | |||
1160 | ||||
1161 | NEXT_TOKEN(name)(name) = args; args = strpbrk(args, ",)"); if (*args == 0) { break ; } if (*args == ')') { args++; continue; } (name_length) = args - (name); args = strip(args+1); | |||
1162 | NEXT_TOKEN(vname)(vname) = args; args = strpbrk(args, ",)"); if (*args == 0) { break; } if (*args == ')') { args++; continue; } (vname_length ) = args - (vname); args = strip(args+1); | |||
1163 | NEXT_TOKEN(type_params)(type_params) = args; args = strpbrk(args, ",)"); if (*args == 0) { break; } if (*args == ')') { args++; continue; } (type_params_length ) = args - (type_params); args = strip(args+1); | |||
1164 | type_params_length = MIN(type_params_length, sizeof(type_str)-1)(((type_params_length) < (sizeof(type_str)-1)) ? (type_params_length ) : (sizeof(type_str)-1)); | |||
1165 | pstrcpy(type_str, type_params_length, type_params); | |||
1166 | type = vcard_emul_type_from_string(type_str); | |||
1167 | ||||
1168 | NEXT_TOKEN(type_params)(type_params) = args; args = strpbrk(args, ",)"); if (*args == 0) { break; } if (*args == ')') { args++; continue; } (type_params_length ) = args - (type_params); args = strip(args+1); | |||
1169 | ||||
1170 | if (*args == 0) { | |||
1171 | break; | |||
1172 | } | |||
1173 | ||||
1174 | if (opts->vreader_count >= reader_count) { | |||
1175 | reader_count += READER_STEP4; | |||
1176 | vreaderOpt = realloc(opts->vreader, | |||
1177 | reader_count * sizeof(*vreaderOpt)); | |||
1178 | if (vreaderOpt == NULL((void*)0)) { | |||
1179 | return opts; /* we're done */ | |||
1180 | } | |||
1181 | } | |||
1182 | opts->vreader = vreaderOpt; | |||
1183 | vreaderOpt = &vreaderOpt[opts->vreader_count]; | |||
1184 | vreaderOpt->name = g_strndup(name, name_length); | |||
| ||||
1185 | vreaderOpt->vname = g_strndup(vname, vname_length); | |||
1186 | vreaderOpt->card_type = type; | |||
1187 | vreaderOpt->type_params = | |||
1188 | g_strndup(type_params, type_params_length); | |||
1189 | count = count_tokens(args, ',', ')') + 1; | |||
1190 | vreaderOpt->cert_count = count; | |||
1191 | vreaderOpt->cert_name = (char **)g_malloc(count*sizeof(char *)); | |||
1192 | for (i = 0; i < count; i++) { | |||
1193 | const char *cert = args; | |||
1194 | args = strpbrk(args, ",)"); | |||
1195 | vreaderOpt->cert_name[i] = g_strndup(cert, args - cert); | |||
1196 | args = strip(args+1); | |||
1197 | } | |||
1198 | if (*args == ')') { | |||
1199 | args++; | |||
1200 | } | |||
1201 | opts->vreader_count++; | |||
1202 | /* use_hw= */ | |||
1203 | } else if (strncmp(args, "use_hw=", 7) == 0) { | |||
1204 | args = strip(args+7); | |||
1205 | if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') { | |||
1206 | opts->use_hw = PR_FALSE0; | |||
1207 | } else { | |||
1208 | opts->use_hw = PR_TRUE1; | |||
1209 | } | |||
1210 | args = find_blank(args); | |||
1211 | /* hw_type= */ | |||
1212 | } else if (strncmp(args, "hw_type=", 8) == 0) { | |||
1213 | args = strip(args+8); | |||
1214 | opts->hw_card_type = vcard_emul_type_from_string(args); | |||
1215 | args = find_blank(args); | |||
1216 | /* hw_params= */ | |||
1217 | } else if (strncmp(args, "hw_params=", 10) == 0) { | |||
1218 | const char *params; | |||
1219 | args = strip(args+10); | |||
1220 | params = args; | |||
1221 | args = find_blank(args); | |||
1222 | opts->hw_type_params = g_strndup(params, args-params); | |||
1223 | /* db="/data/base/path" */ | |||
1224 | } else if (strncmp(args, "db=", 3) == 0) { | |||
1225 | const char *db; | |||
1226 | args = strip(args+3); | |||
1227 | if (*args != '"') { | |||
1228 | continue; | |||
1229 | } | |||
1230 | args++; | |||
1231 | db = args; | |||
1232 | args = strpbrk(args, "\"\n"); | |||
1233 | opts->nss_db = g_strndup(db, args-db); | |||
1234 | if (*args != 0) { | |||
1235 | args++; | |||
1236 | } | |||
1237 | } else { | |||
1238 | args = find_blank(args); | |||
1239 | } | |||
1240 | } while (*args != 0); | |||
1241 | ||||
1242 | return opts; | |||
1243 | } | |||
1244 | ||||
1245 | void | |||
1246 | vcard_emul_usage(void) | |||
1247 | { | |||
1248 | fprintf(stderrstderr, | |||
1249 | "emul args: comma separated list of the following arguments\n" | |||
1250 | " db={nss_database} (default sql:/etc/pki/nssdb)\n" | |||
1251 | " use_hw=[yes|no] (default yes)\n" | |||
1252 | " hw_type={card_type_to_emulate} (default CAC)\n" | |||
1253 | " hw_param={param_for_card} (default \"\")\n" | |||
1254 | " soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n" | |||
1255 | " {cert1},{cert2},{cert3} (default none)\n" | |||
1256 | "\n" | |||
1257 | " {nss_database} The location of the NSS cert & key database\n" | |||
1258 | " {card_type_to_emulate} What card interface to present to the guest\n" | |||
1259 | " {param_for_card} Card interface specific parameters\n" | |||
1260 | " {slot_name} NSS slot that contains the certs\n" | |||
1261 | " {vreader_name} Virtual reader name to present to the guest\n" | |||
1262 | " {certN} Nickname of the certificate n on the virtual card\n" | |||
1263 | "\n" | |||
1264 | "These parameters come as a single string separated by blanks or newlines." | |||
1265 | "\n" | |||
1266 | "Unless use_hw is set to no, all tokens that look like removable hardware\n" | |||
1267 | "tokens will be presented to the guest using the emulator specified by\n" | |||
1268 | "hw_type, and parameters of hw_param.\n" | |||
1269 | "\n" | |||
1270 | "If more one or more soft= parameters are specified, these readers will be\n" | |||
1271 | "presented to the guest\n"); | |||
1272 | } |