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