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