1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #ifndef CKCAPI_H
6 #include "ckcapi.h"
7 #endif /* CKCAPI_H */
8 
9 /*
10  * ckcapi/cfind.c
11  *
12  * This file implements the NSSCKMDFindObjects object for the
13  * "capi" cryptoki module.
14  */
15 
16 struct ckcapiFOStr {
17     NSSArena *arena;
18     CK_ULONG n;
19     CK_ULONG i;
20     ckcapiInternalObject **objs;
21 };
22 
23 static void
ckcapi_mdFindObjects_Final(NSSCKMDFindObjects * mdFindObjects,NSSCKFWFindObjects * fwFindObjects,NSSCKMDSession * mdSession,NSSCKFWSession * fwSession,NSSCKMDToken * mdToken,NSSCKFWToken * fwToken,NSSCKMDInstance * mdInstance,NSSCKFWInstance * fwInstance)24 ckcapi_mdFindObjects_Final(
25     NSSCKMDFindObjects *mdFindObjects,
26     NSSCKFWFindObjects *fwFindObjects,
27     NSSCKMDSession *mdSession,
28     NSSCKFWSession *fwSession,
29     NSSCKMDToken *mdToken,
30     NSSCKFWToken *fwToken,
31     NSSCKMDInstance *mdInstance,
32     NSSCKFWInstance *fwInstance)
33 {
34     struct ckcapiFOStr *fo = (struct ckcapiFOStr *)mdFindObjects->etc;
35     NSSArena *arena = fo->arena;
36     PRUint32 i;
37 
38     /* walk down an free the unused 'objs' */
39     for (i = fo->i; i < fo->n; i++) {
40         nss_ckcapi_DestroyInternalObject(fo->objs[i]);
41     }
42 
43     nss_ZFreeIf(fo->objs);
44     nss_ZFreeIf(fo);
45     nss_ZFreeIf(mdFindObjects);
46     if ((NSSArena *)NULL != arena) {
47         NSSArena_Destroy(arena);
48     }
49 
50     return;
51 }
52 
53 static NSSCKMDObject *
ckcapi_mdFindObjects_Next(NSSCKMDFindObjects * mdFindObjects,NSSCKFWFindObjects * fwFindObjects,NSSCKMDSession * mdSession,NSSCKFWSession * fwSession,NSSCKMDToken * mdToken,NSSCKFWToken * fwToken,NSSCKMDInstance * mdInstance,NSSCKFWInstance * fwInstance,NSSArena * arena,CK_RV * pError)54 ckcapi_mdFindObjects_Next(
55     NSSCKMDFindObjects *mdFindObjects,
56     NSSCKFWFindObjects *fwFindObjects,
57     NSSCKMDSession *mdSession,
58     NSSCKFWSession *fwSession,
59     NSSCKMDToken *mdToken,
60     NSSCKFWToken *fwToken,
61     NSSCKMDInstance *mdInstance,
62     NSSCKFWInstance *fwInstance,
63     NSSArena *arena,
64     CK_RV *pError)
65 {
66     struct ckcapiFOStr *fo = (struct ckcapiFOStr *)mdFindObjects->etc;
67     ckcapiInternalObject *io;
68 
69     if (fo->i == fo->n) {
70         *pError = CKR_OK;
71         return (NSSCKMDObject *)NULL;
72     }
73 
74     io = fo->objs[fo->i];
75     fo->i++;
76 
77     return nss_ckcapi_CreateMDObject(arena, io, pError);
78 }
79 
80 static CK_BBOOL
ckcapi_attrmatch(CK_ATTRIBUTE_PTR a,ckcapiInternalObject * o)81 ckcapi_attrmatch(
82     CK_ATTRIBUTE_PTR a,
83     ckcapiInternalObject *o)
84 {
85     PRBool prb;
86     const NSSItem *b;
87 
88     b = nss_ckcapi_FetchAttribute(o, a->type);
89     if (b == NULL) {
90         return CK_FALSE;
91     }
92 
93     if (a->ulValueLen != b->size) {
94         /* match a decoded serial number */
95         if ((a->type == CKA_SERIAL_NUMBER) && (a->ulValueLen < b->size)) {
96             unsigned int len;
97             unsigned char *data;
98 
99             data = nss_ckcapi_DERUnwrap(b->data, b->size, &len, NULL);
100             if ((len == a->ulValueLen) &&
101                 nsslibc_memequal(a->pValue, data, len, (PRStatus *)NULL)) {
102                 return CK_TRUE;
103             }
104         }
105         return CK_FALSE;
106     }
107 
108     prb = nsslibc_memequal(a->pValue, b->data, b->size, (PRStatus *)NULL);
109 
110     if (PR_TRUE == prb) {
111         return CK_TRUE;
112     } else {
113         return CK_FALSE;
114     }
115 }
116 
117 static CK_BBOOL
ckcapi_match(CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,ckcapiInternalObject * o)118 ckcapi_match(
119     CK_ATTRIBUTE_PTR pTemplate,
120     CK_ULONG ulAttributeCount,
121     ckcapiInternalObject *o)
122 {
123     CK_ULONG i;
124 
125     for (i = 0; i < ulAttributeCount; i++) {
126         if (CK_FALSE == ckcapi_attrmatch(&pTemplate[i], o)) {
127             return CK_FALSE;
128         }
129     }
130 
131     /* Every attribute passed */
132     return CK_TRUE;
133 }
134 
135 #define CKAPI_ITEM_CHUNK 20
136 
137 #define PUT_Object(obj, err)                                                    \
138     {                                                                           \
139         if (count >= size) {                                                    \
140             *listp = *listp ? nss_ZREALLOCARRAY(*listp, ckcapiInternalObject *, \
141                                                 (size +                         \
142                                                  CKAPI_ITEM_CHUNK))             \
143                             : nss_ZNEWARRAY(NULL, ckcapiInternalObject *,       \
144                                             (size +                             \
145                                              CKAPI_ITEM_CHUNK));                \
146             if ((ckcapiInternalObject **)NULL == *listp) {                      \
147                 err = CKR_HOST_MEMORY;                                          \
148                 goto loser;                                                     \
149             }                                                                   \
150             size += CKAPI_ITEM_CHUNK;                                           \
151         }                                                                       \
152         (*listp)[count] = (obj);                                                \
153         count++;                                                                \
154     }
155 
156 /*
157  * pass parameters back through the callback.
158  */
159 typedef struct BareCollectParamsStr {
160     CK_OBJECT_CLASS objClass;
161     CK_ATTRIBUTE_PTR pTemplate;
162     CK_ULONG ulAttributeCount;
163     ckcapiInternalObject ***listp;
164     PRUint32 size;
165     PRUint32 count;
166 } BareCollectParams;
167 
168 /* collect_bare's callback. Called for each object that
169  * supposedly has a PROVINDER_INFO property */
170 static BOOL WINAPI
doBareCollect(const CRYPT_HASH_BLOB * msKeyID,DWORD flags,void * reserved,void * args,DWORD cProp,DWORD * propID,void ** propData,DWORD * propSize)171 doBareCollect(
172     const CRYPT_HASH_BLOB *msKeyID,
173     DWORD flags,
174     void *reserved,
175     void *args,
176     DWORD cProp,
177     DWORD *propID,
178     void **propData,
179     DWORD *propSize)
180 {
181     BareCollectParams *bcp = (BareCollectParams *)args;
182     PRUint32 size = bcp->size;
183     PRUint32 count = bcp->count;
184     ckcapiInternalObject ***listp = bcp->listp;
185     ckcapiInternalObject *io = NULL;
186     DWORD i;
187     CRYPT_KEY_PROV_INFO *keyProvInfo = NULL;
188     void *idData;
189     CK_RV error;
190 
191     /* make sure there is a Key Provider Info property */
192     for (i = 0; i < cProp; i++) {
193         if (CERT_KEY_PROV_INFO_PROP_ID == propID[i]) {
194             keyProvInfo = (CRYPT_KEY_PROV_INFO *)propData[i];
195             break;
196         }
197     }
198     if ((CRYPT_KEY_PROV_INFO *)NULL == keyProvInfo) {
199         return 1;
200     }
201 
202     /* copy the key ID */
203     idData = nss_ZNEWARRAY(NULL, char, msKeyID->cbData);
204     if ((void *)NULL == idData) {
205         goto loser;
206     }
207     nsslibc_memcpy(idData, msKeyID->pbData, msKeyID->cbData);
208 
209     /* build a bare internal object */
210     io = nss_ZNEW(NULL, ckcapiInternalObject);
211     if ((ckcapiInternalObject *)NULL == io) {
212         goto loser;
213     }
214     io->type = ckcapiBareKey;
215     io->objClass = bcp->objClass;
216     io->u.key.provInfo = *keyProvInfo;
217     io->u.key.provInfo.pwszContainerName =
218         nss_ckcapi_WideDup(keyProvInfo->pwszContainerName);
219     io->u.key.provInfo.pwszProvName =
220         nss_ckcapi_WideDup(keyProvInfo->pwszProvName);
221     io->u.key.provName = nss_ckcapi_WideToUTF8(keyProvInfo->pwszProvName);
222     io->u.key.containerName =
223         nss_ckcapi_WideToUTF8(keyProvInfo->pwszContainerName);
224     io->u.key.hProv = 0;
225     io->idData = idData;
226     io->id.data = idData;
227     io->id.size = msKeyID->cbData;
228     idData = NULL;
229 
230     /* see if it matches */
231     if (CK_FALSE == ckcapi_match(bcp->pTemplate, bcp->ulAttributeCount, io)) {
232         goto loser;
233     }
234     PUT_Object(io, error);
235     bcp->size = size;
236     bcp->count = count;
237     return 1;
238 
239 loser:
240     if (io) {
241         nss_ckcapi_DestroyInternalObject(io);
242     }
243     nss_ZFreeIf(idData);
244     return 1;
245 }
246 
247 /*
248  * collect the bare keys running around
249  */
250 static PRUint32
collect_bare(CK_OBJECT_CLASS objClass,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,ckcapiInternalObject *** listp,PRUint32 * sizep,PRUint32 count,CK_RV * pError)251 collect_bare(
252     CK_OBJECT_CLASS objClass,
253     CK_ATTRIBUTE_PTR pTemplate,
254     CK_ULONG ulAttributeCount,
255     ckcapiInternalObject ***listp,
256     PRUint32 *sizep,
257     PRUint32 count,
258     CK_RV *pError)
259 {
260     BOOL rc;
261     BareCollectParams bareCollectParams;
262 
263     bareCollectParams.objClass = objClass;
264     bareCollectParams.pTemplate = pTemplate;
265     bareCollectParams.ulAttributeCount = ulAttributeCount;
266     bareCollectParams.listp = listp;
267     bareCollectParams.size = *sizep;
268     bareCollectParams.count = count;
269 
270     rc = CryptEnumKeyIdentifierProperties(NULL, CERT_KEY_PROV_INFO_PROP_ID, 0,
271                                           NULL, NULL, &bareCollectParams, doBareCollect);
272 
273     *sizep = bareCollectParams.size;
274     return bareCollectParams.count;
275 }
276 
277 /* find all the certs that represent the appropriate object (cert, priv key, or
278  *  pub key) in the cert store.
279  */
280 static PRUint32
collect_class(CK_OBJECT_CLASS objClass,LPCSTR storeStr,PRBool hasID,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,ckcapiInternalObject *** listp,PRUint32 * sizep,PRUint32 count,CK_RV * pError)281 collect_class(
282     CK_OBJECT_CLASS objClass,
283     LPCSTR storeStr,
284     PRBool hasID,
285     CK_ATTRIBUTE_PTR pTemplate,
286     CK_ULONG ulAttributeCount,
287     ckcapiInternalObject ***listp,
288     PRUint32 *sizep,
289     PRUint32 count,
290     CK_RV *pError)
291 {
292     PRUint32 size = *sizep;
293     ckcapiInternalObject *next = NULL;
294     HCERTSTORE hStore;
295     PCCERT_CONTEXT certContext = NULL;
296     PRBool isKey =
297         (objClass == CKO_PUBLIC_KEY) | (objClass == CKO_PRIVATE_KEY);
298 
299     hStore = CertOpenSystemStore((HCRYPTPROV)NULL, storeStr);
300     if (NULL == hStore) {
301         return count; /* none found does not imply an error */
302     }
303 
304     /* FUTURE: use CertFindCertificateInStore to filter better -- so we don't
305    * have to enumerate all the certificates */
306     while ((PCERT_CONTEXT)NULL !=
307            (certContext = CertEnumCertificatesInStore(hStore, certContext))) {
308         /* first filter out non user certs if we are looking for keys */
309         if (isKey) {
310             /* make sure there is a Key Provider Info property */
311             CRYPT_KEY_PROV_INFO *keyProvInfo;
312             DWORD size = 0;
313             BOOL rv;
314             rv = CertGetCertificateContextProperty(certContext,
315                                                    CERT_KEY_PROV_INFO_PROP_ID, NULL, &size);
316             if (!rv) {
317                 int reason = GetLastError();
318                 /* we only care if it exists, we don't really need to fetch it yet */
319                 if (reason == CRYPT_E_NOT_FOUND) {
320                     continue;
321                 }
322             }
323             /* filter out the non-microsoft providers */
324             keyProvInfo = (CRYPT_KEY_PROV_INFO *)nss_ZAlloc(NULL, size);
325             if (keyProvInfo) {
326                 rv = CertGetCertificateContextProperty(certContext,
327                                                        CERT_KEY_PROV_INFO_PROP_ID, keyProvInfo, &size);
328                 if (rv) {
329                     char *provName =
330                         nss_ckcapi_WideToUTF8(keyProvInfo->pwszProvName);
331                     nss_ZFreeIf(keyProvInfo);
332 
333                     if (provName &&
334                         (strncmp(provName, "Microsoft", sizeof("Microsoft") - 1) != 0)) {
335                         continue;
336                     }
337                 } else {
338                     int reason =
339                         GetLastError();
340                     /* we only care if it exists, we don't really need to fetch it yet */
341                     nss_ZFreeIf(keyProvInfo);
342                     if (reason ==
343                         CRYPT_E_NOT_FOUND) {
344                         continue;
345                     }
346                 }
347             }
348         }
349 
350         if ((ckcapiInternalObject *)NULL == next) {
351             next = nss_ZNEW(NULL, ckcapiInternalObject);
352             if ((ckcapiInternalObject *)NULL == next) {
353                 *pError = CKR_HOST_MEMORY;
354                 goto loser;
355             }
356         }
357         next->type = ckcapiCert;
358         next->objClass = objClass;
359         next->u.cert.certContext = certContext;
360         next->u.cert.hasID = hasID;
361         next->u.cert.certStore = storeStr;
362         if (CK_TRUE == ckcapi_match(pTemplate, ulAttributeCount, next)) {
363             /* clear cached values that may be dependent on our old certContext */
364             memset(&next->u.cert, 0, sizeof(next->u.cert));
365             /* get a 'permanent' context */
366             next->u.cert.certContext = CertDuplicateCertificateContext(certContext);
367             next->objClass = objClass;
368             next->u.cert.certContext = certContext;
369             next->u.cert.hasID = hasID;
370             next->u.cert.certStore = storeStr;
371             PUT_Object(next, *pError);
372             next = NULL; /* need to allocate a new one now */
373         } else {
374             /* don't cache the values we just loaded */
375             memset(&next->u.cert, 0, sizeof(next->u.cert));
376         }
377     }
378 loser:
379     CertCloseStore(hStore, 0);
380     nss_ZFreeIf(next);
381     *sizep = size;
382     return count;
383 }
384 
385 NSS_IMPLEMENT PRUint32
nss_ckcapi_collect_all_certs(CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,ckcapiInternalObject *** listp,PRUint32 * sizep,PRUint32 count,CK_RV * pError)386 nss_ckcapi_collect_all_certs(
387     CK_ATTRIBUTE_PTR pTemplate,
388     CK_ULONG ulAttributeCount,
389     ckcapiInternalObject ***listp,
390     PRUint32 *sizep,
391     PRUint32 count,
392     CK_RV *pError)
393 {
394     count = collect_class(CKO_CERTIFICATE, "My", PR_TRUE, pTemplate,
395                           ulAttributeCount, listp, sizep, count, pError);
396     /*count = collect_class(CKO_CERTIFICATE, "AddressBook", PR_FALSE, pTemplate,
397                         ulAttributeCount, listp, sizep, count, pError); */
398     count = collect_class(CKO_CERTIFICATE, "CA", PR_FALSE, pTemplate,
399                           ulAttributeCount, listp, sizep, count, pError);
400     count = collect_class(CKO_CERTIFICATE, "Root", PR_FALSE, pTemplate,
401                           ulAttributeCount, listp, sizep, count, pError);
402     count = collect_class(CKO_CERTIFICATE, "Trust", PR_FALSE, pTemplate,
403                           ulAttributeCount, listp, sizep, count, pError);
404     count = collect_class(CKO_CERTIFICATE, "TrustedPeople", PR_FALSE, pTemplate,
405                           ulAttributeCount, listp, sizep, count, pError);
406     count = collect_class(CKO_CERTIFICATE, "AuthRoot", PR_FALSE, pTemplate,
407                           ulAttributeCount, listp, sizep, count, pError);
408     return count;
409 }
410 
411 CK_OBJECT_CLASS
ckcapi_GetObjectClass(CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount)412 ckcapi_GetObjectClass(CK_ATTRIBUTE_PTR pTemplate,
413                       CK_ULONG ulAttributeCount)
414 {
415     CK_ULONG i;
416 
417     for (i = 0; i < ulAttributeCount; i++) {
418         if (pTemplate[i].type == CKA_CLASS) {
419             return *(CK_OBJECT_CLASS *)pTemplate[i].pValue;
420         }
421     }
422     /* need to return a value that says 'fetch them all' */
423     return CK_INVALID_HANDLE;
424 }
425 
426 static PRUint32
collect_objects(CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,ckcapiInternalObject *** listp,CK_RV * pError)427 collect_objects(
428     CK_ATTRIBUTE_PTR pTemplate,
429     CK_ULONG ulAttributeCount,
430     ckcapiInternalObject ***listp,
431     CK_RV *pError)
432 {
433     PRUint32 i;
434     PRUint32 count = 0;
435     PRUint32 size = 0;
436     CK_OBJECT_CLASS objClass;
437 
438     /*
439      * first handle the static build in objects (if any)
440      */
441     for (i = 0; i < nss_ckcapi_nObjects; i++) {
442         ckcapiInternalObject *o = (ckcapiInternalObject *)&nss_ckcapi_data[i];
443 
444         if (CK_TRUE == ckcapi_match(pTemplate, ulAttributeCount, o)) {
445             PUT_Object(o, *pError);
446         }
447     }
448 
449     /*
450      * now handle the various object types
451      */
452     objClass = ckcapi_GetObjectClass(pTemplate, ulAttributeCount);
453     *pError = CKR_OK;
454     switch (objClass) {
455         case CKO_CERTIFICATE:
456             count = nss_ckcapi_collect_all_certs(pTemplate, ulAttributeCount, listp,
457                                                  &size, count, pError);
458             break;
459         case CKO_PUBLIC_KEY:
460             count = collect_class(objClass, "My", PR_TRUE, pTemplate,
461                                   ulAttributeCount, listp, &size, count, pError);
462             count = collect_bare(objClass, pTemplate, ulAttributeCount, listp,
463                                  &size, count, pError);
464             break;
465         case CKO_PRIVATE_KEY:
466             count = collect_class(objClass, "My", PR_TRUE, pTemplate,
467                                   ulAttributeCount, listp, &size, count, pError);
468             count = collect_bare(objClass, pTemplate, ulAttributeCount, listp,
469                                  &size, count, pError);
470             break;
471         /* all of them */
472         case CK_INVALID_HANDLE:
473             count = nss_ckcapi_collect_all_certs(pTemplate, ulAttributeCount, listp,
474                                                  &size, count, pError);
475             count = collect_class(CKO_PUBLIC_KEY, "My", PR_TRUE, pTemplate,
476                                   ulAttributeCount, listp, &size, count, pError);
477             count = collect_bare(CKO_PUBLIC_KEY, pTemplate, ulAttributeCount, listp,
478                                  &size, count, pError);
479             count = collect_class(CKO_PRIVATE_KEY, "My", PR_TRUE, pTemplate,
480                                   ulAttributeCount, listp, &size, count, pError);
481             count = collect_bare(CKO_PRIVATE_KEY, pTemplate, ulAttributeCount, listp,
482                                  &size, count, pError);
483             break;
484         default:
485             goto done; /* no other object types we understand in this module */
486     }
487     if (CKR_OK != *pError) {
488         goto loser;
489     }
490 
491 done:
492     return count;
493 loser:
494     nss_ZFreeIf(*listp);
495     return 0;
496 }
497 
498 NSS_IMPLEMENT NSSCKMDFindObjects *
nss_ckcapi_FindObjectsInit(NSSCKFWSession * fwSession,CK_ATTRIBUTE_PTR pTemplate,CK_ULONG ulAttributeCount,CK_RV * pError)499 nss_ckcapi_FindObjectsInit(
500     NSSCKFWSession *fwSession,
501     CK_ATTRIBUTE_PTR pTemplate,
502     CK_ULONG ulAttributeCount,
503     CK_RV *pError)
504 {
505     /* This could be made more efficient.  I'm rather rushed. */
506     NSSArena *arena;
507     NSSCKMDFindObjects *rv = (NSSCKMDFindObjects *)NULL;
508     struct ckcapiFOStr *fo = (struct ckcapiFOStr *)NULL;
509     ckcapiInternalObject **temp = (ckcapiInternalObject **)NULL;
510 
511     arena = NSSArena_Create();
512     if ((NSSArena *)NULL == arena) {
513         goto loser;
514     }
515 
516     rv = nss_ZNEW(arena, NSSCKMDFindObjects);
517     if ((NSSCKMDFindObjects *)NULL == rv) {
518         *pError = CKR_HOST_MEMORY;
519         goto loser;
520     }
521 
522     fo = nss_ZNEW(arena, struct ckcapiFOStr);
523     if ((struct ckcapiFOStr *)NULL == fo) {
524         *pError = CKR_HOST_MEMORY;
525         goto loser;
526     }
527 
528     fo->arena = arena;
529     /* fo->n and fo->i are already zero */
530 
531     rv->etc = (void *)fo;
532     rv->Final = ckcapi_mdFindObjects_Final;
533     rv->Next = ckcapi_mdFindObjects_Next;
534     rv->null = (void *)NULL;
535 
536     fo->n = collect_objects(pTemplate, ulAttributeCount, &temp, pError);
537     if (*pError != CKR_OK) {
538         goto loser;
539     }
540 
541     fo->objs = nss_ZNEWARRAY(arena, ckcapiInternalObject *, fo->n);
542     if ((ckcapiInternalObject **)NULL == fo->objs) {
543         *pError = CKR_HOST_MEMORY;
544         goto loser;
545     }
546 
547     (void)nsslibc_memcpy(fo->objs, temp, sizeof(ckcapiInternalObject *) * fo->n);
548     nss_ZFreeIf(temp);
549     temp = (ckcapiInternalObject **)NULL;
550 
551     return rv;
552 
553 loser:
554     nss_ZFreeIf(temp);
555     nss_ZFreeIf(fo);
556     nss_ZFreeIf(rv);
557     if ((NSSArena *)NULL != arena) {
558         NSSArena_Destroy(arena);
559     }
560     return (NSSCKMDFindObjects *)NULL;
561 }
562