1 /*
2 * XML Security Library (http://www.aleksey.com/xmlsec).
3 *
4 *
5 * This is free software; see Copyright file in the source
6 * distribution for precise wording.
7 *
8 * Copyright (C) 2003 Cordys R&D BV, All rights reserved.
9 * Copyright (C) 2003-2016 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
10 */
11 /**
12 * SECTION:keysstore
13 * @Short_description: Keys store implementation for Microsoft Crypto API.
14 * @Stability: Private
15 *
16 * MSCrypto keys store that uses Simple Keys Store under the hood. Uses the
17 * MS Certificate store as a backing store for the finding keys, but the
18 * MS Certificate store not written to by the keys store.
19 * So, if store->findkey is done and the key is not found in the simple
20 * keys store, the MS Certificate store is looked up.
21 * Thus, the MS Certificate store can be used to pre-load keys and becomes
22 * an alternate source of keys for xmlsec.
23 */
24
25 #include "globals.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include <windows.h>
31 #include <wincrypt.h>
32
33 #include <libxml/tree.h>
34
35 #include <xmlsec/xmlsec.h>
36 #include <xmlsec/buffer.h>
37 #include <xmlsec/base64.h>
38 #include <xmlsec/errors.h>
39 #include <xmlsec/xmltree.h>
40
41 #include <xmlsec/keysmngr.h>
42
43 #include <xmlsec/mscrypto/app.h>
44 #include <xmlsec/mscrypto/crypto.h>
45 #include <xmlsec/mscrypto/keysstore.h>
46 #include <xmlsec/mscrypto/x509.h>
47 #include <xmlsec/mscrypto/certkeys.h>
48 #include "private.h"
49
50 #define XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME_A "MY"
51 #define XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME_W L"MY"
52 #ifdef UNICODE
53 #define XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME_W
54 #else /* UNICODE */
55 #define XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME_A
56 #endif /* UNICODE */
57
58 /****************************************************************************
59 *
60 * MSCrypto Keys Store. Uses Simple Keys Store under the hood
61 *
62 * Simple Keys Store ptr is located after xmlSecKeyStore
63 *
64 ***************************************************************************/
65 #define xmlSecMSCryptoKeysStoreSize \
66 (sizeof(xmlSecKeyStore) + sizeof(xmlSecKeyStorePtr))
67
68 #define xmlSecMSCryptoKeysStoreGetSS(store) \
69 ((xmlSecKeyStoreCheckSize((store), xmlSecMSCryptoKeysStoreSize)) ? \
70 (xmlSecKeyStorePtr*)(((xmlSecByte*)(store)) + sizeof(xmlSecKeyStore)) : \
71 (xmlSecKeyStorePtr*)NULL)
72
73 static int xmlSecMSCryptoKeysStoreInitialize (xmlSecKeyStorePtr store);
74 static void xmlSecMSCryptoKeysStoreFinalize (xmlSecKeyStorePtr store);
75 static xmlSecKeyPtr xmlSecMSCryptoKeysStoreFindKey (xmlSecKeyStorePtr store,
76 const xmlChar* name,
77 xmlSecKeyInfoCtxPtr keyInfoCtx);
78
79 static xmlSecKeyStoreKlass xmlSecMSCryptoKeysStoreKlass = {
80 sizeof(xmlSecKeyStoreKlass),
81 xmlSecMSCryptoKeysStoreSize,
82
83 /* data */
84 BAD_CAST "MSCrypto-keys-store", /* const xmlChar* name; */
85
86 /* constructors/destructor */
87 xmlSecMSCryptoKeysStoreInitialize, /* xmlSecKeyStoreInitializeMethod initialize; */
88 xmlSecMSCryptoKeysStoreFinalize, /* xmlSecKeyStoreFinalizeMethod finalize; */
89 xmlSecMSCryptoKeysStoreFindKey, /* xmlSecKeyStoreFindKeyMethod findKey; */
90
91 /* reserved for the future */
92 NULL, /* void* reserved0; */
93 NULL, /* void* reserved1; */
94 };
95
96 /**
97 * xmlSecMSCryptoKeysStoreGetKlass:
98 *
99 * The MSCrypto list based keys store klass.
100 *
101 * Returns: MSCrypto list based keys store klass.
102 */
103 xmlSecKeyStoreId
xmlSecMSCryptoKeysStoreGetKlass(void)104 xmlSecMSCryptoKeysStoreGetKlass(void) {
105 return(&xmlSecMSCryptoKeysStoreKlass);
106 }
107
108 /**
109 * xmlSecMSCryptoKeysStoreAdoptKey:
110 * @store: the pointer to MSCrypto keys store.
111 * @key: the pointer to key.
112 *
113 * Adds @key to the @store.
114 *
115 * Returns: 0 on success or a negative value if an error occurs.
116 */
117 int
xmlSecMSCryptoKeysStoreAdoptKey(xmlSecKeyStorePtr store,xmlSecKeyPtr key)118 xmlSecMSCryptoKeysStoreAdoptKey(xmlSecKeyStorePtr store, xmlSecKeyPtr key) {
119 xmlSecKeyStorePtr *ss;
120
121 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), -1);
122 xmlSecAssert2((key != NULL), -1);
123
124 ss = xmlSecMSCryptoKeysStoreGetSS(store);
125 xmlSecAssert2(((ss != NULL) && (*ss != NULL) &&
126 (xmlSecKeyStoreCheckId(*ss, xmlSecSimpleKeysStoreId))), -1);
127
128 return (xmlSecSimpleKeysStoreAdoptKey(*ss, key));
129 }
130
131 /**
132 * xmlSecMSCryptoKeysStoreLoad:
133 * @store: the pointer to MSCrypto keys store.
134 * @uri: the filename.
135 * @keysMngr: the pointer to associated keys manager.
136 *
137 * Reads keys from an XML file.
138 *
139 * Returns: 0 on success or a negative value if an error occurs.
140 */
141 int
xmlSecMSCryptoKeysStoreLoad(xmlSecKeyStorePtr store,const char * uri,xmlSecKeysMngrPtr keysMngr)142 xmlSecMSCryptoKeysStoreLoad(xmlSecKeyStorePtr store, const char *uri,
143 xmlSecKeysMngrPtr keysMngr) {
144 xmlDocPtr doc;
145 xmlNodePtr root;
146 xmlNodePtr cur;
147 xmlSecKeyPtr key;
148 xmlSecKeyInfoCtx keyInfoCtx;
149 int ret;
150
151 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), -1);
152 xmlSecAssert2((uri != NULL), -1);
153 UNREFERENCED_PARAMETER(keysMngr);
154
155 doc = xmlParseFile(uri);
156 if(doc == NULL) {
157 xmlSecXmlError2("xmlParseFile", xmlSecKeyStoreGetName(store),
158 "uri=%s", xmlSecErrorsSafeString(uri));
159 return(-1);
160 }
161
162 root = xmlDocGetRootElement(doc);
163 if(!xmlSecCheckNodeName(root, BAD_CAST "Keys", xmlSecNs)) {
164 xmlSecInvalidNodeError(root, BAD_CAST "Keys", xmlSecKeyStoreGetName(store));
165 xmlFreeDoc(doc);
166 return(-1);
167 }
168
169 cur = xmlSecGetNextElementNode(root->children);
170 while((cur != NULL) && xmlSecCheckNodeName(cur, xmlSecNodeKeyInfo, xmlSecDSigNs)) {
171 key = xmlSecKeyCreate();
172 if(key == NULL) {
173 xmlSecInternalError("xmlSecKeyCreate",
174 xmlSecKeyStoreGetName(store));
175 xmlFreeDoc(doc);
176 return(-1);
177 }
178
179 ret = xmlSecKeyInfoCtxInitialize(&keyInfoCtx, NULL);
180 if(ret < 0) {
181 xmlSecInternalError("xmlSecKeyInfoCtxInitialize",
182 xmlSecKeyStoreGetName(store));
183 xmlSecKeyDestroy(key);
184 xmlFreeDoc(doc);
185 return(-1);
186 }
187
188 keyInfoCtx.mode = xmlSecKeyInfoModeRead;
189 keyInfoCtx.keysMngr = NULL;
190 keyInfoCtx.flags = XMLSEC_KEYINFO_FLAGS_DONT_STOP_ON_KEY_FOUND |
191 XMLSEC_KEYINFO_FLAGS_X509DATA_DONT_VERIFY_CERTS;
192 keyInfoCtx.keyReq.keyId = xmlSecKeyDataIdUnknown;
193 keyInfoCtx.keyReq.keyType = xmlSecKeyDataTypeAny;
194 keyInfoCtx.keyReq.keyUsage= xmlSecKeyDataUsageAny;
195
196 ret = xmlSecKeyInfoNodeRead(cur, key, &keyInfoCtx);
197 if(ret < 0) {
198 xmlSecInternalError("xmlSecKeyInfoNodeRead",
199 xmlSecKeyStoreGetName(store));
200 xmlSecKeyInfoCtxFinalize(&keyInfoCtx);
201 xmlSecKeyDestroy(key);
202 xmlFreeDoc(doc);
203 return(-1);
204 }
205 xmlSecKeyInfoCtxFinalize(&keyInfoCtx);
206
207 if(xmlSecKeyIsValid(key)) {
208 ret = xmlSecMSCryptoKeysStoreAdoptKey(store, key);
209 if(ret < 0) {
210 xmlSecInternalError("xmlSecMSCryptoKeysStoreAdoptKey",
211 xmlSecKeyStoreGetName(store));
212 xmlSecKeyDestroy(key);
213 xmlFreeDoc(doc);
214 return(-1);
215 }
216 } else {
217 /* we have an unknown key in our file, just ignore it */
218 xmlSecKeyDestroy(key);
219 }
220 cur = xmlSecGetNextElementNode(cur->next);
221 }
222
223 if(cur != NULL) {
224 xmlSecUnexpectedNodeError(cur, xmlSecKeyStoreGetName(store));
225 xmlFreeDoc(doc);
226 return(-1);
227 }
228
229 xmlFreeDoc(doc);
230 return(0);
231 }
232
233 /**
234 * xmlSecMSCryptoKeysStoreSave:
235 * @store: the pointer to MSCrypto keys store.
236 * @filename: the filename.
237 * @type: the saved keys type (public, private, ...).
238 *
239 * Writes keys from @store to an XML file.
240 *
241 * Returns: 0 on success or a negative value if an error occurs.
242 */
243 int
xmlSecMSCryptoKeysStoreSave(xmlSecKeyStorePtr store,const char * filename,xmlSecKeyDataType type)244 xmlSecMSCryptoKeysStoreSave(xmlSecKeyStorePtr store, const char *filename, xmlSecKeyDataType type) {
245 xmlSecKeyStorePtr *ss;
246
247 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), -1);
248 xmlSecAssert2((filename != NULL), -1);
249
250 ss = xmlSecMSCryptoKeysStoreGetSS(store);
251 xmlSecAssert2(((ss != NULL) && (*ss != NULL) &&
252 (xmlSecKeyStoreCheckId(*ss, xmlSecSimpleKeysStoreId))), -1);
253
254 return (xmlSecSimpleKeysStoreSave(*ss, filename, type));
255 }
256
257 static int
xmlSecMSCryptoKeysStoreInitialize(xmlSecKeyStorePtr store)258 xmlSecMSCryptoKeysStoreInitialize(xmlSecKeyStorePtr store) {
259 xmlSecKeyStorePtr *ss;
260
261 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), -1);
262
263 ss = xmlSecMSCryptoKeysStoreGetSS(store);
264 xmlSecAssert2((*ss == NULL), -1);
265
266 *ss = xmlSecKeyStoreCreate(xmlSecSimpleKeysStoreId);
267 if(*ss == NULL) {
268 xmlSecInternalError("xmlSecKeyStoreCreate(xmlSecSimpleKeysStoreId)",
269 xmlSecKeyStoreGetName(store));
270 return(-1);
271 }
272
273 return(0);
274 }
275
276 static void
xmlSecMSCryptoKeysStoreFinalize(xmlSecKeyStorePtr store)277 xmlSecMSCryptoKeysStoreFinalize(xmlSecKeyStorePtr store) {
278 xmlSecKeyStorePtr *ss;
279
280 xmlSecAssert(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId));
281
282 ss = xmlSecMSCryptoKeysStoreGetSS(store);
283 xmlSecAssert((ss != NULL) && (*ss != NULL));
284
285 xmlSecKeyStoreDestroy(*ss);
286 }
287
288 static PCCERT_CONTEXT
xmlSecMSCryptoKeysStoreFindCert(xmlSecKeyStorePtr store,const xmlChar * name,xmlSecKeyInfoCtxPtr keyInfoCtx)289 xmlSecMSCryptoKeysStoreFindCert(xmlSecKeyStorePtr store, const xmlChar* name,
290 xmlSecKeyInfoCtxPtr keyInfoCtx) {
291 LPCTSTR storeName;
292 HCERTSTORE hStoreHandle = NULL;
293 PCCERT_CONTEXT pCertContext = NULL;
294 LPTSTR wcName = NULL;
295
296 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), NULL);
297 xmlSecAssert2(name != NULL, NULL);
298 xmlSecAssert2(keyInfoCtx != NULL, NULL);
299
300 storeName = xmlSecMSCryptoAppGetCertStoreName();
301 if(storeName == NULL) {
302 storeName = XMLSEC_MSCRYPTO_APP_DEFAULT_CERT_STORE_NAME;
303 }
304
305 hStoreHandle = CertOpenSystemStore(0, storeName);
306 if (NULL == hStoreHandle) {
307 xmlSecMSCryptoError2("CertOpenSystemStore",
308 xmlSecKeyStoreGetName(store),
309 "storeName=%s",
310 xmlSecErrorsSafeString(storeName));
311 return(NULL);
312 }
313
314 /* convert name to unicode */
315 wcName = xmlSecWin32ConvertUtf8ToTstr(name);
316 if(wcName == NULL) {
317 xmlSecInternalError("xmlSecWin32ConvertUtf8ToTstr(name)",
318 xmlSecKeyStoreGetName(store));
319 CertCloseStore(hStoreHandle, 0);
320 return(NULL);
321 }
322
323 /* first attempt: try to find the cert with a full blown subject dn */
324 if(NULL == pCertContext) {
325 pCertContext = xmlSecMSCryptoX509FindCertBySubject(
326 hStoreHandle,
327 wcName,
328 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING);
329 }
330
331 /*
332 * Try ro find certificate with name="Friendly Name"
333 */
334 if (NULL == pCertContext) {
335 DWORD dwPropSize;
336 PBYTE pbFriendlyName;
337 PCCERT_CONTEXT pCertCtxIter = NULL;
338
339
340 while (1) {
341 pCertCtxIter = CertEnumCertificatesInStore(hStoreHandle, pCertCtxIter);
342 if(pCertCtxIter == NULL) {
343 break;
344 }
345
346 if (TRUE != CertGetCertificateContextProperty(pCertCtxIter,
347 CERT_FRIENDLY_NAME_PROP_ID,
348 NULL,
349 &dwPropSize)) {
350 continue;
351 }
352
353 pbFriendlyName = xmlMalloc(dwPropSize);
354 if(pbFriendlyName == NULL) {
355 xmlSecMallocError(dwPropSize, xmlSecKeyStoreGetName(store));
356 xmlFree(wcName);
357 CertCloseStore(hStoreHandle, 0);
358 return(NULL);
359 }
360
361 if (TRUE != CertGetCertificateContextProperty(pCertCtxIter,
362 CERT_FRIENDLY_NAME_PROP_ID,
363 pbFriendlyName,
364 &dwPropSize)) {
365 xmlFree(pbFriendlyName);
366 continue;
367 }
368
369 /* Compare FriendlyName to name */
370 if (!lstrcmp(wcName, (LPCTSTR)pbFriendlyName)) {
371 pCertContext = pCertCtxIter;
372 xmlFree(pbFriendlyName);
373 break;
374 }
375 xmlFree(pbFriendlyName);
376 }
377 }
378
379 /* We don't give up easily, now try to find cert with part of the name
380 */
381 if (NULL == pCertContext) {
382 pCertContext = CertFindCertificateInStore(
383 hStoreHandle,
384 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
385 0,
386 CERT_FIND_SUBJECT_STR,
387 wcName,
388 NULL);
389 }
390
391
392 /* We could do the following here:
393 * It would be nice if we could locate the cert with issuer name and
394 * serial number, the given keyname can be something like this:
395 * 'serial=1234567;issuer=CN=ikke, C=NL'
396 * to be implemented by the first person who reads this, and thinks it's
397 * a good idea :) WK
398 */
399
400 /* OK, I give up, I'm gone :( */
401
402 /* aleksey todo: is it a right idea to close store if we have a handle to
403 * a cert in this store? */
404 xmlFree(wcName);
405 CertCloseStore(hStoreHandle, 0);
406 return(pCertContext);
407 }
408
409
410 static xmlSecKeyPtr
xmlSecMSCryptoKeysStoreFindKey(xmlSecKeyStorePtr store,const xmlChar * name,xmlSecKeyInfoCtxPtr keyInfoCtx)411 xmlSecMSCryptoKeysStoreFindKey(xmlSecKeyStorePtr store, const xmlChar* name,
412 xmlSecKeyInfoCtxPtr keyInfoCtx) {
413 xmlSecKeyStorePtr* ss;
414 xmlSecKeyPtr key = NULL;
415 xmlSecKeyReqPtr keyReq = NULL;
416 PCCERT_CONTEXT pCertContext = NULL;
417 PCCERT_CONTEXT pCertContext2 = NULL;
418 xmlSecKeyDataPtr data = NULL;
419 xmlSecKeyDataPtr x509Data = NULL;
420 xmlSecKeyPtr res = NULL;
421 int ret;
422
423 xmlSecAssert2(xmlSecKeyStoreCheckId(store, xmlSecMSCryptoKeysStoreId), NULL);
424 xmlSecAssert2(keyInfoCtx != NULL, NULL);
425
426 ss = xmlSecMSCryptoKeysStoreGetSS(store);
427 xmlSecAssert2(((ss != NULL) && (*ss != NULL)), NULL);
428
429 /* first try to find key in the simple keys store */
430 key = xmlSecKeyStoreFindKey(*ss, name, keyInfoCtx);
431 if (key != NULL) {
432 return (key);
433 }
434
435 /* Next try to find the key in the MS Certificate store, and construct an xmlSecKey.
436 * we must have a name to lookup keys in the certificate store.
437 */
438 if (name == NULL) {
439 goto done;
440 }
441
442 /* what type of key are we looking for?
443 * WK: For now, we'll look only for public/private keys using the
444 * name as a cert nickname. Then the name is regarded as the subject
445 * dn of the certificate to be searched for.
446 */
447 keyReq = &(keyInfoCtx->keyReq);
448 if (keyReq->keyType & (xmlSecKeyDataTypePublic | xmlSecKeyDataTypePrivate)) {
449 pCertContext = xmlSecMSCryptoKeysStoreFindCert(store, name, keyInfoCtx);
450 if(pCertContext == NULL) {
451 goto done;
452 }
453
454 /* set cert in x509 data */
455 x509Data = xmlSecKeyDataCreate(xmlSecMSCryptoKeyDataX509Id);
456 if(x509Data == NULL) {
457 xmlSecInternalError("xmlSecKeyDataCreate",
458 xmlSecKeyDataGetName(x509Data));
459 goto done;
460 }
461
462 pCertContext2 = CertDuplicateCertificateContext(pCertContext);
463 if (NULL == pCertContext2) {
464 xmlSecMSCryptoError("CertDuplicateCertificateContext",
465 xmlSecKeyDataGetName(x509Data));
466 goto done;
467 }
468
469 ret = xmlSecMSCryptoKeyDataX509AdoptCert(x509Data, pCertContext2);
470 if (ret < 0) {
471 xmlSecInternalError("xmlSecMSCryptoKeyDataX509AdoptCert",
472 xmlSecKeyDataGetName(x509Data));
473 goto done;
474 }
475 pCertContext2 = NULL;
476
477 pCertContext2 = CertDuplicateCertificateContext(pCertContext);
478 if (NULL == pCertContext2) {
479 xmlSecMSCryptoError("CertDuplicateCertificateContext",
480 xmlSecKeyDataGetName(x509Data));
481 goto done;
482 }
483
484 ret = xmlSecMSCryptoKeyDataX509AdoptKeyCert(x509Data, pCertContext2);
485 if (ret < 0) {
486 xmlSecInternalError("xmlSecMSCryptoKeyDataX509AdoptKeyCert",
487 xmlSecKeyDataGetName(x509Data));
488 goto done;
489 }
490 pCertContext2 = NULL;
491
492 /* set cert in key data */
493 data = xmlSecMSCryptoCertAdopt(pCertContext, keyReq->keyType);
494 if(data == NULL) {
495 xmlSecInternalError("xmlSecMSCryptoCertAdopt", NULL);
496 goto done;
497 }
498 pCertContext = NULL;
499
500 /* create key and add key data and x509 data to it */
501 key = xmlSecKeyCreate();
502 if (key == NULL) {
503 xmlSecInternalError("xmlSecKeyCreate", NULL);
504 goto done;
505 }
506
507 ret = xmlSecKeySetValue(key, data);
508 if (ret < 0) {
509 xmlSecInternalError("xmlSecKeySetValue",
510 xmlSecKeyDataGetName(data));
511 goto done;
512 }
513 data = NULL;
514
515 ret = xmlSecKeyAdoptData(key, x509Data);
516 if (ret < 0) {
517 xmlSecInternalError("xmlSecKeyAdoptData",
518 xmlSecKeyDataGetName(x509Data));
519 goto done;
520 }
521 x509Data = NULL;
522
523 /* Set the name of the key to the given name */
524 ret = xmlSecKeySetName(key, name);
525 if (ret < 0) {
526 xmlSecInternalError("xmlSecKeySetName",
527 xmlSecKeyStoreGetName(store));
528 goto done;
529 }
530
531 /* now that we have a key, make sure it is valid and let the simple
532 * store adopt it */
533 if (xmlSecKeyIsValid(key)) {
534 res = key;
535 key = NULL;
536 }
537 }
538
539 done:
540 if (NULL != pCertContext) {
541 CertFreeCertificateContext(pCertContext);
542 }
543 if (NULL != pCertContext2) {
544 CertFreeCertificateContext(pCertContext2);
545 }
546 if (data != NULL) {
547 xmlSecKeyDataDestroy(data);
548 }
549 if (x509Data != NULL) {
550 xmlSecKeyDataDestroy(x509Data);
551 }
552 if (key != NULL) {
553 xmlSecKeyDestroy(key);
554 }
555
556 return (res);
557 }
558