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