crypt32: Introduce function to encode an array of items as a set.
[wine] / dlls / crypt32 / serialize.c
1 /*
2  * Copyright 2004-2006 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdarg.h>
19 #include "windef.h"
20 #include "winbase.h"
21 #include "wincrypt.h"
22 #include "wine/debug.h"
23 #include "wine/exception.h"
24 #include "crypt32_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
27
28 /* An extended certificate property in serialized form is prefixed by this
29  * header.
30  */
31 typedef struct _WINE_CERT_PROP_HEADER
32 {
33     DWORD propID;
34     DWORD unknown; /* always 1 */
35     DWORD cb;
36 } WINE_CERT_PROP_HEADER, *PWINE_CERT_PROP_HEADER;
37
38 static BOOL CRYPT_SerializeStoreElement(const void *context,
39  const BYTE *encodedContext, DWORD cbEncodedContext, DWORD contextPropID,
40  PCWINE_CONTEXT_INTERFACE contextInterface, DWORD dwFlags, BOOL omitHashes,
41  BYTE *pbElement, DWORD *pcbElement)
42 {
43     BOOL ret;
44
45     TRACE("(%p, %p, %08x, %d, %p, %p)\n", context, contextInterface, dwFlags,
46      omitHashes, pbElement, pcbElement);
47
48     if (context)
49     {
50         DWORD bytesNeeded = sizeof(WINE_CERT_PROP_HEADER) + cbEncodedContext;
51         DWORD prop = 0;
52
53         ret = TRUE;
54         do {
55             prop = contextInterface->enumProps(context, prop);
56             if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
57             {
58                 DWORD propSize = 0;
59
60                 ret = contextInterface->getProp(context, prop, NULL, &propSize);
61                 if (ret)
62                     bytesNeeded += sizeof(WINE_CERT_PROP_HEADER) + propSize;
63             }
64         } while (ret && prop != 0);
65
66         if (!pbElement)
67         {
68             *pcbElement = bytesNeeded;
69             ret = TRUE;
70         }
71         else if (*pcbElement < bytesNeeded)
72         {
73             *pcbElement = bytesNeeded;
74             SetLastError(ERROR_MORE_DATA);
75             ret = FALSE;
76         }
77         else
78         {
79             PWINE_CERT_PROP_HEADER hdr;
80             DWORD bufSize = 0;
81             LPBYTE buf = NULL;
82
83             prop = 0;
84             do {
85                 prop = contextInterface->enumProps(context, prop);
86                 if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
87                 {
88                     DWORD propSize = 0;
89
90                     ret = contextInterface->getProp(context, prop, NULL,
91                      &propSize);
92                     if (ret)
93                     {
94                         if (bufSize < propSize)
95                         {
96                             if (buf)
97                                 buf = CryptMemRealloc(buf, propSize);
98                             else
99                                 buf = CryptMemAlloc(propSize);
100                             bufSize = propSize;
101                         }
102                         if (buf)
103                         {
104                             ret = contextInterface->getProp(context, prop, buf,
105                              &propSize);
106                             if (ret)
107                             {
108                                 hdr = (PWINE_CERT_PROP_HEADER)pbElement;
109                                 hdr->propID = prop;
110                                 hdr->unknown = 1;
111                                 hdr->cb = propSize;
112                                 pbElement += sizeof(WINE_CERT_PROP_HEADER);
113                                 if (propSize)
114                                 {
115                                     memcpy(pbElement, buf, propSize);
116                                     pbElement += propSize;
117                                 }
118                             }
119                         }
120                         else
121                             ret = FALSE;
122                     }
123                 }
124             } while (ret && prop != 0);
125             CryptMemFree(buf);
126
127             hdr = (PWINE_CERT_PROP_HEADER)pbElement;
128             hdr->propID = contextPropID;
129             hdr->unknown = 1;
130             hdr->cb = cbEncodedContext;
131             memcpy(pbElement + sizeof(WINE_CERT_PROP_HEADER),
132              encodedContext, cbEncodedContext);
133         }
134     }
135     else
136         ret = FALSE;
137     return ret;
138 }
139
140 BOOL WINAPI CertSerializeCertificateStoreElement(PCCERT_CONTEXT pCertContext,
141  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
142 {
143     return CRYPT_SerializeStoreElement(pCertContext,
144      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
145      CERT_CERT_PROP_ID, pCertInterface, dwFlags, FALSE, pbElement, pcbElement);
146 }
147
148 BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext,
149  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
150 {
151     return CRYPT_SerializeStoreElement(pCrlContext,
152      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
153      CERT_CRL_PROP_ID, pCRLInterface, dwFlags, FALSE, pbElement, pcbElement);
154 }
155
156 BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext,
157  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
158 {
159     return CRYPT_SerializeStoreElement(pCtlContext,
160      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
161      CERT_CTL_PROP_ID, pCTLInterface, dwFlags, FALSE, pbElement, pcbElement);
162 }
163
164 /* Looks for the property with ID propID in the buffer buf.  Returns a pointer
165  * to its header if a valid header is found, NULL if not.  Valid means the
166  * length of thte property won't overrun buf, and the unknown field is 1.
167  */
168 static const WINE_CERT_PROP_HEADER *CRYPT_findPropID(const BYTE *buf,
169  DWORD size, DWORD propID)
170 {
171     const WINE_CERT_PROP_HEADER *ret = NULL;
172     BOOL done = FALSE;
173
174     while (size && !ret && !done)
175     {
176         if (size < sizeof(WINE_CERT_PROP_HEADER))
177         {
178             SetLastError(CRYPT_E_FILE_ERROR);
179             done = TRUE;
180         }
181         else
182         {
183             const WINE_CERT_PROP_HEADER *hdr =
184              (const WINE_CERT_PROP_HEADER *)buf;
185
186             size -= sizeof(WINE_CERT_PROP_HEADER);
187             buf += sizeof(WINE_CERT_PROP_HEADER);
188             if (size < hdr->cb)
189             {
190                 SetLastError(E_INVALIDARG);
191                 done = TRUE;
192             }
193             else if (!hdr->propID)
194             {
195                 /* assume a zero prop ID means the data are uninitialized, so
196                  * stop looking.
197                  */
198                 done = TRUE;
199             }
200             else if (hdr->unknown != 1)
201             {
202                 SetLastError(ERROR_FILE_NOT_FOUND);
203                 done = TRUE;
204             }
205             else if (hdr->propID == propID)
206                 ret = hdr;
207             else
208             {
209                 buf += hdr->cb;
210                 size -= hdr->cb;
211             }
212         }
213     }
214     return ret;
215 }
216
217 static BOOL CRYPT_ReadContextProp(
218  const WINE_CONTEXT_INTERFACE *contextInterface, const void *context,
219  const WINE_CERT_PROP_HEADER *hdr, const BYTE *pbElement, DWORD cbElement)
220 {
221     BOOL ret;
222
223     if (cbElement < hdr->cb)
224     {
225         SetLastError(E_INVALIDARG);
226         ret = FALSE;
227     }
228     else if (hdr->unknown != 1)
229     {
230         SetLastError(ERROR_FILE_NOT_FOUND);
231         ret = FALSE;
232     }
233     else if (hdr->propID != CERT_CERT_PROP_ID &&
234      hdr->propID != CERT_CRL_PROP_ID && hdr->propID != CERT_CTL_PROP_ID)
235     {
236         /* Have to create a blob for most types, but not
237          * for all.. arghh.
238          */
239         switch (hdr->propID)
240         {
241         case CERT_AUTO_ENROLL_PROP_ID:
242         case CERT_CTL_USAGE_PROP_ID:
243         case CERT_DESCRIPTION_PROP_ID:
244         case CERT_FRIENDLY_NAME_PROP_ID:
245         case CERT_HASH_PROP_ID:
246         case CERT_KEY_IDENTIFIER_PROP_ID:
247         case CERT_MD5_HASH_PROP_ID:
248         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
249         case CERT_PUBKEY_ALG_PARA_PROP_ID:
250         case CERT_PVK_FILE_PROP_ID:
251         case CERT_SIGNATURE_HASH_PROP_ID:
252         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
253         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
254         case CERT_ENROLLMENT_PROP_ID:
255         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
256         case CERT_RENEWAL_PROP_ID:
257         {
258             CRYPT_DATA_BLOB blob = { hdr->cb,
259              (LPBYTE)pbElement };
260
261             ret = contextInterface->setProp(context,
262              hdr->propID, 0, &blob);
263             break;
264         }
265         case CERT_DATE_STAMP_PROP_ID:
266             ret = contextInterface->setProp(context,
267              hdr->propID, 0, pbElement);
268             break;
269         case CERT_KEY_PROV_INFO_PROP_ID:
270         {
271             PCRYPT_KEY_PROV_INFO info =
272              (PCRYPT_KEY_PROV_INFO)pbElement;
273
274             CRYPT_FixKeyProvInfoPointers(info);
275             ret = contextInterface->setProp(context,
276              hdr->propID, 0, pbElement);
277             break;
278         }
279         default:
280             ret = FALSE;
281         }
282     }
283     else
284     {
285         /* ignore the context itself */
286         ret = TRUE;
287     }
288     return ret;
289 }
290
291 const void *CRYPT_ReadSerializedElement(const BYTE *pbElement, DWORD cbElement,
292  DWORD dwContextTypeFlags, DWORD *pdwContentType)
293 {
294     const void *context;
295
296     TRACE("(%p, %d, %08x, %p)\n", pbElement, cbElement, dwContextTypeFlags,
297      pdwContentType);
298
299     if (!cbElement)
300     {
301         SetLastError(ERROR_END_OF_MEDIA);
302         return NULL;
303     }
304
305     __TRY
306     {
307         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
308         const WINE_CERT_PROP_HEADER *hdr = NULL;
309         DWORD type = 0;
310         BOOL ret;
311
312         ret = TRUE;
313         context = NULL;
314         if (dwContextTypeFlags == CERT_STORE_ALL_CONTEXT_FLAG)
315         {
316             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
317             if (hdr)
318                 type = CERT_STORE_CERTIFICATE_CONTEXT;
319             else
320             {
321                 hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
322                 if (hdr)
323                     type = CERT_STORE_CRL_CONTEXT;
324                 else
325                 {
326                     hdr = CRYPT_findPropID(pbElement, cbElement,
327                      CERT_CTL_PROP_ID);
328                     if (hdr)
329                         type = CERT_STORE_CTL_CONTEXT;
330                 }
331             }
332         }
333         else if (dwContextTypeFlags & CERT_STORE_CERTIFICATE_CONTEXT_FLAG)
334         {
335             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
336             type = CERT_STORE_CERTIFICATE_CONTEXT;
337         }
338         else if (dwContextTypeFlags & CERT_STORE_CRL_CONTEXT_FLAG)
339         {
340             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
341             type = CERT_STORE_CRL_CONTEXT;
342         }
343         else if (dwContextTypeFlags & CERT_STORE_CTL_CONTEXT_FLAG)
344         {
345             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CTL_PROP_ID);
346             type = CERT_STORE_CTL_CONTEXT;
347         }
348
349         switch (type)
350         {
351         case CERT_STORE_CERTIFICATE_CONTEXT:
352             contextInterface = pCertInterface;
353             break;
354         case CERT_STORE_CRL_CONTEXT:
355             contextInterface = pCRLInterface;
356             break;
357         case CERT_STORE_CTL_CONTEXT:
358             contextInterface = pCTLInterface;
359             break;
360         default:
361             SetLastError(E_INVALIDARG);
362             ret = FALSE;
363         }
364         if (!hdr)
365             ret = FALSE;
366
367         if (ret)
368             context = contextInterface->create(X509_ASN_ENCODING,
369              (BYTE *)hdr + sizeof(WINE_CERT_PROP_HEADER), hdr->cb);
370         if (ret && context)
371         {
372             BOOL noMoreProps = FALSE;
373
374             while (!noMoreProps && ret)
375             {
376                 if (cbElement < sizeof(WINE_CERT_PROP_HEADER))
377                     ret = FALSE;
378                 else
379                 {
380                     const WINE_CERT_PROP_HEADER *hdr =
381                      (const WINE_CERT_PROP_HEADER *)pbElement;
382
383                     TRACE("prop is %d\n", hdr->propID);
384                     cbElement -= sizeof(WINE_CERT_PROP_HEADER);
385                     pbElement += sizeof(WINE_CERT_PROP_HEADER);
386                     if (!hdr->propID)
387                     {
388                         /* Like in CRYPT_findPropID, stop if the propID is zero
389                          */
390                         noMoreProps = TRUE;
391                     }
392                     else
393                         ret = CRYPT_ReadContextProp(contextInterface, context,
394                          hdr, pbElement, cbElement);
395                     pbElement += hdr->cb;
396                     cbElement -= hdr->cb;
397                     if (!cbElement)
398                         noMoreProps = TRUE;
399                 }
400             }
401             if (ret)
402             {
403                 if (pdwContentType)
404                     *pdwContentType = type;
405             }
406             else
407             {
408                 contextInterface->free(context);
409                 context = NULL;
410             }
411         }
412     }
413     __EXCEPT_PAGE_FAULT
414     {
415         SetLastError(STATUS_ACCESS_VIOLATION);
416         context = NULL;
417     }
418     __ENDTRY
419     return context;
420 }
421
422 static const BYTE fileHeader[] = { 0, 0, 0, 0, 'C','E','R','T' };
423
424 BOOL CRYPT_ReadSerializedFile(HANDLE file, HCERTSTORE store)
425 {
426     BYTE fileHeaderBuf[sizeof(fileHeader)];
427     DWORD read;
428     BOOL ret;
429
430     /* Failure reading is non-critical, we'll leave the store empty */
431     ret = ReadFile(file, fileHeaderBuf, sizeof(fileHeaderBuf), &read, NULL);
432     if (ret)
433     {
434         if (!memcmp(fileHeaderBuf, fileHeader, read))
435         {
436             WINE_CERT_PROP_HEADER propHdr;
437             const void *context = NULL;
438             const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
439             LPBYTE buf = NULL;
440             DWORD bufSize = 0;
441
442             do {
443                 ret = ReadFile(file, &propHdr, sizeof(propHdr), &read, NULL);
444                 if (ret && read == sizeof(propHdr))
445                 {
446                     if (contextInterface && context &&
447                      (propHdr.propID == CERT_CERT_PROP_ID ||
448                      propHdr.propID == CERT_CRL_PROP_ID ||
449                      propHdr.propID == CERT_CTL_PROP_ID))
450                     {
451                         /* We have a new context, so free the existing one */
452                         contextInterface->free(context);
453                     }
454                     if (propHdr.cb > bufSize)
455                     {
456                         /* Not reusing realloc, because the old data aren't
457                          * needed any longer.
458                          */
459                         CryptMemFree(buf);
460                         buf = CryptMemAlloc(propHdr.cb);
461                         bufSize = propHdr.cb;
462                     }
463                     if (buf)
464                     {
465                         ret = ReadFile(file, buf, propHdr.cb, &read, NULL);
466                         if (ret && read == propHdr.cb)
467                         {
468                             if (propHdr.propID == CERT_CERT_PROP_ID)
469                             {
470                                 contextInterface = pCertInterface;
471                                 ret = contextInterface->addEncodedToStore(store,
472                                  X509_ASN_ENCODING, buf, read,
473                                  CERT_STORE_ADD_NEW, &context);
474                             }
475                             else if (propHdr.propID == CERT_CRL_PROP_ID)
476                             {
477                                 contextInterface = pCRLInterface;
478                                 ret = contextInterface->addEncodedToStore(store,
479                                  X509_ASN_ENCODING, buf, read,
480                                  CERT_STORE_ADD_NEW, &context);
481                             }
482                             else if (propHdr.propID == CERT_CTL_PROP_ID)
483                             {
484                                 contextInterface = pCTLInterface;
485                                 ret = contextInterface->addEncodedToStore(store,
486                                  X509_ASN_ENCODING, buf, read,
487                                  CERT_STORE_ADD_NEW, &context);
488                             }
489                             else
490                                 ret = CRYPT_ReadContextProp(contextInterface,
491                                  context, &propHdr, buf, read);
492                         }
493                     }
494                     else
495                         ret = FALSE;
496                 }
497             } while (ret && read > 0);
498             if (contextInterface && context)
499             {
500                 /* Free the last context added */
501                 contextInterface->free(context);
502             }
503             CryptMemFree(buf);
504             ret = TRUE;
505         }
506     }
507     else
508         ret = TRUE;
509     return ret;
510 }
511
512 static BOOL WINAPI CRYPT_SerializeCertNoHash(PCCERT_CONTEXT pCertContext,
513  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
514 {
515     return CRYPT_SerializeStoreElement(pCertContext,
516      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
517      CERT_CERT_PROP_ID, pCertInterface, dwFlags, TRUE, pbElement, pcbElement);
518 }
519
520 static BOOL WINAPI CRYPT_SerializeCRLNoHash(PCCRL_CONTEXT pCrlContext,
521  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
522 {
523     return CRYPT_SerializeStoreElement(pCrlContext,
524      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
525      CERT_CRL_PROP_ID, pCRLInterface, dwFlags, TRUE, pbElement, pcbElement);
526 }
527
528 static BOOL WINAPI CRYPT_SerializeCTLNoHash(PCCTL_CONTEXT pCtlContext,
529  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
530 {
531     return CRYPT_SerializeStoreElement(pCtlContext,
532      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
533      CERT_CTL_PROP_ID, pCTLInterface, dwFlags, TRUE, pbElement, pcbElement);
534 }
535
536 static BOOL CRYPT_SerializeContextsToFile(HANDLE file,
537  const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE store)
538 {
539     const void *context = NULL;
540     BOOL ret;
541
542     do {
543         context = contextInterface->enumContextsInStore(store, context);
544         if (context)
545         {
546             DWORD size = 0;
547             LPBYTE buf = NULL;
548
549             ret = contextInterface->serialize(context, 0, NULL, &size);
550             if (size)
551                 buf = CryptMemAlloc(size);
552             if (buf)
553             {
554                 ret = contextInterface->serialize(context, 0, buf, &size);
555                 if (ret)
556                     ret = WriteFile(file, buf, size, &size, NULL);
557             }
558             CryptMemFree(buf);
559         }
560         else
561             ret = TRUE;
562     } while (ret && context != NULL);
563     if (context)
564         contextInterface->free(context);
565     return ret;
566 }
567
568 BOOL CRYPT_WriteSerializedFile(HANDLE file, HCERTSTORE store)
569 {
570     static const BYTE fileTrailer[12] = { 0 };
571     WINE_CONTEXT_INTERFACE interface;
572     BOOL ret;
573     DWORD size;
574
575     SetFilePointer(file, 0, NULL, FILE_BEGIN);
576     ret = WriteFile(file, fileHeader, sizeof(fileHeader), &size, NULL);
577     if (ret)
578     {
579         memcpy(&interface, pCertInterface, sizeof(interface));
580         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash;
581         ret = CRYPT_SerializeContextsToFile(file, &interface, store);
582     }
583     if (ret)
584     {
585         memcpy(&interface, pCRLInterface, sizeof(interface));
586         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash;
587         ret = CRYPT_SerializeContextsToFile(file, &interface, store);
588     }
589     if (ret)
590     {
591         memcpy(&interface, pCTLInterface, sizeof(interface));
592         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCTLNoHash;
593         ret = CRYPT_SerializeContextsToFile(file, &interface, store);
594     }
595     if (ret)
596         ret = WriteFile(file, fileTrailer, sizeof(fileTrailer), &size, NULL);
597     return ret;
598 }
599
600 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
601  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
602  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
603 {
604     const void *context;
605     DWORD type;
606     BOOL ret;
607
608     TRACE("(%p, %p, %d, %08x, %08x, %08x, %p, %p)\n", hCertStore,
609      pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags,
610      pdwContentType, ppvContext);
611
612     /* Call the internal function, then delete the hashes.  Tests show this
613      * function uses real hash values, not whatever's stored in the hash
614      * property.
615      */
616     context = CRYPT_ReadSerializedElement(pbElement, cbElement,
617      dwContextTypeFlags, &type);
618     if (context)
619     {
620         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
621
622         switch (type)
623         {
624         case CERT_STORE_CERTIFICATE_CONTEXT:
625             contextInterface = pCertInterface;
626             break;
627         case CERT_STORE_CRL_CONTEXT:
628             contextInterface = pCRLInterface;
629             break;
630         case CERT_STORE_CTL_CONTEXT:
631             contextInterface = pCTLInterface;
632             break;
633         default:
634             SetLastError(E_INVALIDARG);
635         }
636         if (contextInterface)
637         {
638             contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL);
639             contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL);
640             contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0,
641              NULL);
642             if (pdwContentType)
643                 *pdwContentType = type;
644             ret = contextInterface->addContextToStore(hCertStore, context,
645              dwAddDisposition, ppvContext);
646             contextInterface->free(context);
647         }
648         else
649             ret = FALSE;
650     }
651     else
652         ret = FALSE;
653     return ret;
654 }