jscript: Use single string instance for strings representing NULL BSTR instead of...
[wine] / dlls / crypt32 / serialize.c
1 /*
2  * Copyright 2004-2007 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
19 #include "config.h"
20 #include "wine/port.h"
21
22 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "wincrypt.h"
26 #include "wine/debug.h"
27 #include "wine/exception.h"
28 #include "crypt32_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31
32 /* An extended certificate property in serialized form is prefixed by this
33  * header.
34  */
35 typedef struct _WINE_CERT_PROP_HEADER
36 {
37     DWORD propID;
38     DWORD unknown; /* always 1 */
39     DWORD cb;
40 } WINE_CERT_PROP_HEADER, *PWINE_CERT_PROP_HEADER;
41
42 static BOOL CRYPT_SerializeStoreElement(const void *context,
43  const BYTE *encodedContext, DWORD cbEncodedContext, DWORD contextPropID,
44  PCWINE_CONTEXT_INTERFACE contextInterface, DWORD dwFlags, BOOL omitHashes,
45  BYTE *pbElement, DWORD *pcbElement)
46 {
47     BOOL ret;
48
49     TRACE("(%p, %p, %08x, %d, %p, %p)\n", context, contextInterface, dwFlags,
50      omitHashes, pbElement, pcbElement);
51
52     if (context)
53     {
54         DWORD bytesNeeded = sizeof(WINE_CERT_PROP_HEADER) + cbEncodedContext;
55         DWORD prop = 0;
56
57         ret = TRUE;
58         do {
59             prop = contextInterface->enumProps(context, prop);
60             if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
61             {
62                 DWORD propSize = 0;
63
64                 ret = contextInterface->getProp(context, prop, NULL, &propSize);
65                 if (ret)
66                     bytesNeeded += sizeof(WINE_CERT_PROP_HEADER) + propSize;
67             }
68         } while (ret && prop != 0);
69
70         if (!pbElement)
71         {
72             *pcbElement = bytesNeeded;
73             ret = TRUE;
74         }
75         else if (*pcbElement < bytesNeeded)
76         {
77             *pcbElement = bytesNeeded;
78             SetLastError(ERROR_MORE_DATA);
79             ret = FALSE;
80         }
81         else
82         {
83             PWINE_CERT_PROP_HEADER hdr;
84             DWORD bufSize = 0;
85             LPBYTE buf = NULL;
86
87             prop = 0;
88             do {
89                 prop = contextInterface->enumProps(context, prop);
90                 if (prop && (!omitHashes || !IS_CERT_HASH_PROP_ID(prop)))
91                 {
92                     DWORD propSize = 0;
93
94                     ret = contextInterface->getProp(context, prop, NULL,
95                      &propSize);
96                     if (ret)
97                     {
98                         if (bufSize < propSize)
99                         {
100                             if (buf)
101                                 buf = CryptMemRealloc(buf, propSize);
102                             else
103                                 buf = CryptMemAlloc(propSize);
104                             bufSize = propSize;
105                         }
106                         if (buf)
107                         {
108                             ret = contextInterface->getProp(context, prop, buf,
109                              &propSize);
110                             if (ret)
111                             {
112                                 hdr = (PWINE_CERT_PROP_HEADER)pbElement;
113                                 hdr->propID = prop;
114                                 hdr->unknown = 1;
115                                 hdr->cb = propSize;
116                                 pbElement += sizeof(WINE_CERT_PROP_HEADER);
117                                 if (propSize)
118                                 {
119                                     memcpy(pbElement, buf, propSize);
120                                     pbElement += propSize;
121                                 }
122                             }
123                         }
124                         else
125                             ret = FALSE;
126                     }
127                 }
128             } while (ret && prop != 0);
129             CryptMemFree(buf);
130
131             hdr = (PWINE_CERT_PROP_HEADER)pbElement;
132             hdr->propID = contextPropID;
133             hdr->unknown = 1;
134             hdr->cb = cbEncodedContext;
135             memcpy(pbElement + sizeof(WINE_CERT_PROP_HEADER),
136              encodedContext, cbEncodedContext);
137         }
138     }
139     else
140         ret = FALSE;
141     return ret;
142 }
143
144 BOOL WINAPI CertSerializeCertificateStoreElement(PCCERT_CONTEXT pCertContext,
145  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
146 {
147     return CRYPT_SerializeStoreElement(pCertContext,
148      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
149      CERT_CERT_PROP_ID, pCertInterface, dwFlags, FALSE, pbElement, pcbElement);
150 }
151
152 BOOL WINAPI CertSerializeCRLStoreElement(PCCRL_CONTEXT pCrlContext,
153  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
154 {
155     return CRYPT_SerializeStoreElement(pCrlContext,
156      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
157      CERT_CRL_PROP_ID, pCRLInterface, dwFlags, FALSE, pbElement, pcbElement);
158 }
159
160 BOOL WINAPI CertSerializeCTLStoreElement(PCCTL_CONTEXT pCtlContext,
161  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
162 {
163     return CRYPT_SerializeStoreElement(pCtlContext,
164      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
165      CERT_CTL_PROP_ID, pCTLInterface, dwFlags, FALSE, pbElement, pcbElement);
166 }
167
168 /* Looks for the property with ID propID in the buffer buf.  Returns a pointer
169  * to its header if a valid header is found, NULL if not.  Valid means the
170  * length of the property won't overrun buf, and the unknown field is 1.
171  */
172 static const WINE_CERT_PROP_HEADER *CRYPT_findPropID(const BYTE *buf,
173  DWORD size, DWORD propID)
174 {
175     const WINE_CERT_PROP_HEADER *ret = NULL;
176     BOOL done = FALSE;
177
178     while (size && !ret && !done)
179     {
180         if (size < sizeof(WINE_CERT_PROP_HEADER))
181         {
182             SetLastError(CRYPT_E_FILE_ERROR);
183             done = TRUE;
184         }
185         else
186         {
187             const WINE_CERT_PROP_HEADER *hdr =
188              (const WINE_CERT_PROP_HEADER *)buf;
189
190             size -= sizeof(WINE_CERT_PROP_HEADER);
191             buf += sizeof(WINE_CERT_PROP_HEADER);
192             if (size < hdr->cb)
193             {
194                 SetLastError(E_INVALIDARG);
195                 done = TRUE;
196             }
197             else if (!hdr->propID)
198             {
199                 /* assume a zero prop ID means the data are uninitialized, so
200                  * stop looking.
201                  */
202                 done = TRUE;
203             }
204             else if (hdr->unknown != 1)
205             {
206                 SetLastError(ERROR_FILE_NOT_FOUND);
207                 done = TRUE;
208             }
209             else if (hdr->propID == propID)
210                 ret = hdr;
211             else
212             {
213                 buf += hdr->cb;
214                 size -= hdr->cb;
215             }
216         }
217     }
218     return ret;
219 }
220
221 static BOOL CRYPT_ReadContextProp(
222  const WINE_CONTEXT_INTERFACE *contextInterface, const void *context,
223  const WINE_CERT_PROP_HEADER *hdr, const BYTE *pbElement, DWORD cbElement)
224 {
225     BOOL ret;
226
227     if (cbElement < hdr->cb)
228     {
229         SetLastError(E_INVALIDARG);
230         ret = FALSE;
231     }
232     else if (hdr->unknown != 1)
233     {
234         SetLastError(ERROR_FILE_NOT_FOUND);
235         ret = FALSE;
236     }
237     else if (hdr->propID != CERT_CERT_PROP_ID &&
238      hdr->propID != CERT_CRL_PROP_ID && hdr->propID != CERT_CTL_PROP_ID)
239     {
240         /* Have to create a blob for most types, but not
241          * for all.. arghh.
242          */
243         switch (hdr->propID)
244         {
245         case CERT_AUTO_ENROLL_PROP_ID:
246         case CERT_CTL_USAGE_PROP_ID:
247         case CERT_DESCRIPTION_PROP_ID:
248         case CERT_FRIENDLY_NAME_PROP_ID:
249         case CERT_HASH_PROP_ID:
250         case CERT_KEY_IDENTIFIER_PROP_ID:
251         case CERT_MD5_HASH_PROP_ID:
252         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
253         case CERT_PUBKEY_ALG_PARA_PROP_ID:
254         case CERT_PVK_FILE_PROP_ID:
255         case CERT_SIGNATURE_HASH_PROP_ID:
256         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
257         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
258         case CERT_ENROLLMENT_PROP_ID:
259         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
260         case CERT_RENEWAL_PROP_ID:
261         {
262             CRYPT_DATA_BLOB blob = { hdr->cb,
263              (LPBYTE)pbElement };
264
265             ret = contextInterface->setProp(context,
266              hdr->propID, 0, &blob);
267             break;
268         }
269         case CERT_DATE_STAMP_PROP_ID:
270             ret = contextInterface->setProp(context,
271              hdr->propID, 0, pbElement);
272             break;
273         case CERT_KEY_PROV_INFO_PROP_ID:
274         {
275             PCRYPT_KEY_PROV_INFO info =
276              (PCRYPT_KEY_PROV_INFO)pbElement;
277
278             CRYPT_FixKeyProvInfoPointers(info);
279             ret = contextInterface->setProp(context,
280              hdr->propID, 0, pbElement);
281             break;
282         }
283         default:
284             ret = FALSE;
285         }
286     }
287     else
288     {
289         /* ignore the context itself */
290         ret = TRUE;
291     }
292     return ret;
293 }
294
295 const void *CRYPT_ReadSerializedElement(const BYTE *pbElement, DWORD cbElement,
296  DWORD dwContextTypeFlags, DWORD *pdwContentType)
297 {
298     const void *context;
299
300     TRACE("(%p, %d, %08x, %p)\n", pbElement, cbElement, dwContextTypeFlags,
301      pdwContentType);
302
303     if (!cbElement)
304     {
305         SetLastError(ERROR_END_OF_MEDIA);
306         return NULL;
307     }
308
309     __TRY
310     {
311         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
312         const WINE_CERT_PROP_HEADER *hdr = NULL;
313         DWORD type = 0;
314         BOOL ret;
315
316         ret = TRUE;
317         context = NULL;
318         if (dwContextTypeFlags == CERT_STORE_ALL_CONTEXT_FLAG)
319         {
320             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
321             if (hdr)
322                 type = CERT_STORE_CERTIFICATE_CONTEXT;
323             else
324             {
325                 hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
326                 if (hdr)
327                     type = CERT_STORE_CRL_CONTEXT;
328                 else
329                 {
330                     hdr = CRYPT_findPropID(pbElement, cbElement,
331                      CERT_CTL_PROP_ID);
332                     if (hdr)
333                         type = CERT_STORE_CTL_CONTEXT;
334                 }
335             }
336         }
337         else if (dwContextTypeFlags & CERT_STORE_CERTIFICATE_CONTEXT_FLAG)
338         {
339             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
340             type = CERT_STORE_CERTIFICATE_CONTEXT;
341         }
342         else if (dwContextTypeFlags & CERT_STORE_CRL_CONTEXT_FLAG)
343         {
344             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
345             type = CERT_STORE_CRL_CONTEXT;
346         }
347         else if (dwContextTypeFlags & CERT_STORE_CTL_CONTEXT_FLAG)
348         {
349             hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CTL_PROP_ID);
350             type = CERT_STORE_CTL_CONTEXT;
351         }
352
353         switch (type)
354         {
355         case CERT_STORE_CERTIFICATE_CONTEXT:
356             contextInterface = pCertInterface;
357             break;
358         case CERT_STORE_CRL_CONTEXT:
359             contextInterface = pCRLInterface;
360             break;
361         case CERT_STORE_CTL_CONTEXT:
362             contextInterface = pCTLInterface;
363             break;
364         default:
365             SetLastError(E_INVALIDARG);
366             ret = FALSE;
367         }
368         if (!hdr)
369             ret = FALSE;
370
371         if (ret)
372             context = contextInterface->create(X509_ASN_ENCODING,
373              (BYTE *)hdr + sizeof(WINE_CERT_PROP_HEADER), hdr->cb);
374         if (ret && context)
375         {
376             BOOL noMoreProps = FALSE;
377
378             while (!noMoreProps && ret)
379             {
380                 if (cbElement < sizeof(WINE_CERT_PROP_HEADER))
381                     ret = FALSE;
382                 else
383                 {
384                     const WINE_CERT_PROP_HEADER *hdr =
385                      (const WINE_CERT_PROP_HEADER *)pbElement;
386
387                     TRACE("prop is %d\n", hdr->propID);
388                     cbElement -= sizeof(WINE_CERT_PROP_HEADER);
389                     pbElement += sizeof(WINE_CERT_PROP_HEADER);
390                     if (!hdr->propID)
391                     {
392                         /* Like in CRYPT_findPropID, stop if the propID is zero
393                          */
394                         noMoreProps = TRUE;
395                     }
396                     else
397                         ret = CRYPT_ReadContextProp(contextInterface, context,
398                          hdr, pbElement, cbElement);
399                     pbElement += hdr->cb;
400                     cbElement -= hdr->cb;
401                     if (!cbElement)
402                         noMoreProps = TRUE;
403                 }
404             }
405             if (ret)
406             {
407                 if (pdwContentType)
408                     *pdwContentType = type;
409             }
410             else
411             {
412                 contextInterface->free(context);
413                 context = NULL;
414             }
415         }
416     }
417     __EXCEPT_PAGE_FAULT
418     {
419         SetLastError(STATUS_ACCESS_VIOLATION);
420         context = NULL;
421     }
422     __ENDTRY
423     return context;
424 }
425
426 static const BYTE fileHeader[] = { 0, 0, 0, 0, 'C','E','R','T' };
427
428 typedef BOOL (*read_serialized_func)(void *handle, void *buffer,
429  DWORD bytesToRead, DWORD *bytesRead);
430
431 static BOOL CRYPT_ReadSerializedStore(void *handle,
432  read_serialized_func read_func, HCERTSTORE store)
433 {
434     BYTE fileHeaderBuf[sizeof(fileHeader)];
435     DWORD read;
436     BOOL ret;
437
438     /* Failure reading is non-critical, we'll leave the store empty */
439     ret = read_func(handle, fileHeaderBuf, sizeof(fileHeaderBuf), &read);
440     if (ret)
441     {
442         if (!read)
443             ; /* an empty file is okay */
444         else if (read != sizeof(fileHeaderBuf))
445             ret = FALSE;
446         else if (!memcmp(fileHeaderBuf, fileHeader, read))
447         {
448             WINE_CERT_PROP_HEADER propHdr;
449             const void *context = NULL;
450             const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
451             LPBYTE buf = NULL;
452             DWORD bufSize = 0;
453
454             do {
455                 ret = read_func(handle, &propHdr, sizeof(propHdr), &read);
456                 if (ret && read == sizeof(propHdr))
457                 {
458                     if (contextInterface && context &&
459                      (propHdr.propID == CERT_CERT_PROP_ID ||
460                      propHdr.propID == CERT_CRL_PROP_ID ||
461                      propHdr.propID == CERT_CTL_PROP_ID))
462                     {
463                         /* We have a new context, so free the existing one */
464                         contextInterface->free(context);
465                     }
466                     if (propHdr.cb > bufSize)
467                     {
468                         /* Not reusing realloc, because the old data aren't
469                          * needed any longer.
470                          */
471                         CryptMemFree(buf);
472                         buf = CryptMemAlloc(propHdr.cb);
473                         bufSize = propHdr.cb;
474                     }
475                     if (!propHdr.cb)
476                         ; /* Property is empty, nothing to do */
477                     else if (buf)
478                     {
479                         ret = read_func(handle, buf, propHdr.cb, &read);
480                         if (ret && read == propHdr.cb)
481                         {
482                             if (propHdr.propID == CERT_CERT_PROP_ID)
483                             {
484                                 contextInterface = pCertInterface;
485                                 ret = contextInterface->addEncodedToStore(store,
486                                  X509_ASN_ENCODING, buf, read,
487                                  CERT_STORE_ADD_NEW, &context);
488                             }
489                             else if (propHdr.propID == CERT_CRL_PROP_ID)
490                             {
491                                 contextInterface = pCRLInterface;
492                                 ret = contextInterface->addEncodedToStore(store,
493                                  X509_ASN_ENCODING, buf, read,
494                                  CERT_STORE_ADD_NEW, &context);
495                             }
496                             else if (propHdr.propID == CERT_CTL_PROP_ID)
497                             {
498                                 contextInterface = pCTLInterface;
499                                 ret = contextInterface->addEncodedToStore(store,
500                                  X509_ASN_ENCODING, buf, read,
501                                  CERT_STORE_ADD_NEW, &context);
502                             }
503                             else
504                             {
505                                 if (!contextInterface)
506                                 {
507                                     WARN("prop id %d before a context id\n",
508                                      propHdr.propID);
509                                     ret = FALSE;
510                                 }
511                                 else
512                                     ret = CRYPT_ReadContextProp(
513                                      contextInterface, context, &propHdr, buf,
514                                      read);
515                             }
516                         }
517                     }
518                     else
519                         ret = FALSE;
520                 }
521             } while (ret && read > 0 && propHdr.cb);
522             if (contextInterface && context)
523             {
524                 /* Free the last context added */
525                 contextInterface->free(context);
526             }
527             CryptMemFree(buf);
528             ret = TRUE;
529         }
530         else
531             ret = FALSE;
532     }
533     else
534         ret = TRUE;
535     return ret;
536 }
537
538 static BOOL read_file_wrapper(void *handle, void *buffer, DWORD bytesToRead,
539  DWORD *bytesRead)
540 {
541     return ReadFile(handle, buffer, bytesToRead, bytesRead, NULL);
542 }
543
544 BOOL CRYPT_ReadSerializedStoreFromFile(HANDLE file, HCERTSTORE store)
545 {
546     return CRYPT_ReadSerializedStore(file, read_file_wrapper, store);
547 }
548
549 struct BlobReader
550 {
551     const CRYPT_DATA_BLOB *blob;
552     DWORD current;
553 };
554
555 static BOOL read_blob_wrapper(void *handle, void *buffer, DWORD bytesToRead,
556  DWORD *bytesRead)
557 {
558     struct BlobReader *reader = handle;
559     BOOL ret;
560
561     if (reader->current < reader->blob->cbData)
562     {
563         *bytesRead = min(bytesToRead, reader->blob->cbData - reader->current);
564         memcpy(buffer, reader->blob->pbData + reader->current, *bytesRead);
565         reader->current += *bytesRead;
566         ret = TRUE;
567     }
568     else if (reader->current == reader->blob->cbData)
569     {
570         *bytesRead = 0;
571         ret = TRUE;
572     }
573     else
574         ret = FALSE;
575     return ret;
576 }
577
578 BOOL CRYPT_ReadSerializedStoreFromBlob(const CRYPT_DATA_BLOB *blob,
579  HCERTSTORE store)
580 {
581     struct BlobReader reader = { blob, 0 };
582
583     return CRYPT_ReadSerializedStore(&reader, read_blob_wrapper, store);
584 }
585
586 static BOOL WINAPI CRYPT_SerializeCertNoHash(PCCERT_CONTEXT pCertContext,
587  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
588 {
589     return CRYPT_SerializeStoreElement(pCertContext,
590      pCertContext->pbCertEncoded, pCertContext->cbCertEncoded,
591      CERT_CERT_PROP_ID, pCertInterface, dwFlags, TRUE, pbElement, pcbElement);
592 }
593
594 static BOOL WINAPI CRYPT_SerializeCRLNoHash(PCCRL_CONTEXT pCrlContext,
595  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
596 {
597     return CRYPT_SerializeStoreElement(pCrlContext,
598      pCrlContext->pbCrlEncoded, pCrlContext->cbCrlEncoded,
599      CERT_CRL_PROP_ID, pCRLInterface, dwFlags, TRUE, pbElement, pcbElement);
600 }
601
602 static BOOL WINAPI CRYPT_SerializeCTLNoHash(PCCTL_CONTEXT pCtlContext,
603  DWORD dwFlags, BYTE *pbElement, DWORD *pcbElement)
604 {
605     return CRYPT_SerializeStoreElement(pCtlContext,
606      pCtlContext->pbCtlEncoded, pCtlContext->cbCtlEncoded,
607      CERT_CTL_PROP_ID, pCTLInterface, dwFlags, TRUE, pbElement, pcbElement);
608 }
609
610 typedef BOOL (*SerializedOutputFunc)(void *handle, const void *buffer,
611  DWORD size);
612
613 static BOOL CRYPT_SerializeContextsToStream(SerializedOutputFunc output,
614  void *handle, const WINE_CONTEXT_INTERFACE *contextInterface, HCERTSTORE store)
615 {
616     const void *context = NULL;
617     BOOL ret;
618
619     do {
620         context = contextInterface->enumContextsInStore(store, context);
621         if (context)
622         {
623             DWORD size = 0;
624             LPBYTE buf = NULL;
625
626             ret = contextInterface->serialize(context, 0, NULL, &size);
627             if (size)
628                 buf = CryptMemAlloc(size);
629             if (buf)
630             {
631                 ret = contextInterface->serialize(context, 0, buf, &size);
632                 if (ret)
633                     ret = output(handle, buf, size);
634             }
635             CryptMemFree(buf);
636         }
637         else
638             ret = TRUE;
639     } while (ret && context != NULL);
640     if (context)
641         contextInterface->free(context);
642     return ret;
643 }
644
645 static BOOL CRYPT_WriteSerializedStoreToStream(HCERTSTORE store,
646  SerializedOutputFunc output, void *handle)
647 {
648     static const BYTE fileTrailer[12] = { 0 };
649     WINE_CONTEXT_INTERFACE interface;
650     BOOL ret;
651
652     ret = output(handle, fileHeader, sizeof(fileHeader));
653     if (ret)
654     {
655         interface = *pCertInterface;
656         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCertNoHash;
657         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
658          store);
659     }
660     if (ret)
661     {
662         interface = *pCRLInterface;
663         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCRLNoHash;
664         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
665          store);
666     }
667     if (ret)
668     {
669         interface = *pCTLInterface;
670         interface.serialize = (SerializeElementFunc)CRYPT_SerializeCTLNoHash;
671         ret = CRYPT_SerializeContextsToStream(output, handle, &interface,
672          store);
673     }
674     if (ret)
675         ret = output(handle, fileTrailer, sizeof(fileTrailer));
676     return ret;
677 }
678
679 static BOOL CRYPT_FileOutputFunc(void *handle, const void *buffer, DWORD size)
680 {
681     return WriteFile(handle, buffer, size, &size, NULL);
682 }
683
684 static BOOL CRYPT_WriteSerializedStoreToFile(HANDLE file, HCERTSTORE store)
685 {
686     SetFilePointer(file, 0, NULL, FILE_BEGIN);
687     return CRYPT_WriteSerializedStoreToStream(store, CRYPT_FileOutputFunc,
688      file);
689 }
690
691 static BOOL CRYPT_SavePKCSToMem(HCERTSTORE store,
692  DWORD dwMsgAndCertEncodingType, void *handle)
693 {
694     CERT_BLOB *blob = handle;
695     CRYPT_SIGNED_INFO signedInfo = { 0 };
696     PCCERT_CONTEXT cert = NULL;
697     PCCRL_CONTEXT crl = NULL;
698     DWORD size;
699     BOOL ret = TRUE;
700
701     TRACE("(%d, %p)\n", blob->pbData ? blob->cbData : 0, blob->pbData);
702
703     do {
704         cert = CertEnumCertificatesInStore(store, cert);
705         if (cert)
706             signedInfo.cCertEncoded++;
707     } while (cert);
708     if (signedInfo.cCertEncoded)
709     {
710         signedInfo.rgCertEncoded = CryptMemAlloc(
711          signedInfo.cCertEncoded * sizeof(CERT_BLOB));
712         if (!signedInfo.rgCertEncoded)
713         {
714             SetLastError(ERROR_OUTOFMEMORY);
715             ret = FALSE;
716         }
717         else
718         {
719             DWORD i = 0;
720
721             do {
722                 cert = CertEnumCertificatesInStore(store, cert);
723                 if (cert)
724                 {
725                     signedInfo.rgCertEncoded[i].cbData = cert->cbCertEncoded;
726                     signedInfo.rgCertEncoded[i].pbData = cert->pbCertEncoded;
727                     i++;
728                 }
729             } while (cert);
730         }
731     }
732
733     do {
734         crl = CertEnumCRLsInStore(store, crl);
735         if (crl)
736             signedInfo.cCrlEncoded++;
737     } while (crl);
738     if (signedInfo.cCrlEncoded)
739     {
740         signedInfo.rgCrlEncoded = CryptMemAlloc(
741          signedInfo.cCrlEncoded * sizeof(CERT_BLOB));
742         if (!signedInfo.rgCrlEncoded)
743         {
744             SetLastError(ERROR_OUTOFMEMORY);
745             ret = FALSE;
746         }
747         else
748         {
749             DWORD i = 0;
750
751             do {
752                 crl = CertEnumCRLsInStore(store, crl);
753                 if (crl)
754                 {
755                     signedInfo.rgCrlEncoded[i].cbData = crl->cbCrlEncoded;
756                     signedInfo.rgCrlEncoded[i].pbData = crl->pbCrlEncoded;
757                     i++;
758                 }
759             } while (crl);
760         }
761     }
762     if (ret)
763     {
764         ret = CRYPT_AsnEncodeCMSSignedInfo(&signedInfo, NULL, &size);
765         if (ret)
766         {
767             if (!blob->pbData)
768                 blob->cbData = size;
769             else if (blob->cbData < size)
770             {
771                 blob->cbData = size;
772                 SetLastError(ERROR_MORE_DATA);
773                 ret = FALSE;
774             }
775             else
776             {
777                 blob->cbData = size;
778                 ret = CRYPT_AsnEncodeCMSSignedInfo(&signedInfo, blob->pbData,
779                  &blob->cbData);
780             }
781         }
782     }
783     CryptMemFree(signedInfo.rgCertEncoded);
784     CryptMemFree(signedInfo.rgCrlEncoded);
785     TRACE("returning %d\n", ret);
786     return ret;
787 }
788
789 static BOOL CRYPT_SavePKCSToFile(HCERTSTORE store,
790  DWORD dwMsgAndCertEncodingType, void *handle)
791 {
792     CERT_BLOB blob = { 0, NULL };
793     BOOL ret;
794
795     TRACE("(%p)\n", handle);
796
797     ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob);
798     if (ret)
799     {
800         blob.pbData = CryptMemAlloc(blob.cbData);
801         if (blob.pbData)
802         {
803             ret = CRYPT_SavePKCSToMem(store, dwMsgAndCertEncodingType, &blob);
804             if (ret)
805                 ret = WriteFile(handle, blob.pbData, blob.cbData,
806                  &blob.cbData, NULL);
807         }
808         else
809         {
810             SetLastError(ERROR_OUTOFMEMORY);
811             ret = FALSE;
812         }
813     }
814     TRACE("returning %d\n", ret);
815     return ret;
816 }
817
818 static BOOL CRYPT_SaveSerializedToFile(HCERTSTORE store,
819  DWORD dwMsgAndCertEncodingType, void *handle)
820 {
821     return CRYPT_WriteSerializedStoreToFile(handle, store);
822 }
823
824 struct MemWrittenTracker
825 {
826     DWORD cbData;
827     BYTE *pbData;
828     DWORD written;
829 };
830
831 /* handle is a pointer to a MemWrittenTracker.  Assumes its pointer is valid. */
832 static BOOL CRYPT_MemOutputFunc(void *handle, const void *buffer, DWORD size)
833 {
834     struct MemWrittenTracker *tracker = handle;
835     BOOL ret;
836
837     if (tracker->written + size > tracker->cbData)
838     {
839         SetLastError(ERROR_MORE_DATA);
840         /* Update written so caller can notify its caller of the required size
841          */
842         tracker->written += size;
843         ret = FALSE;
844     }
845     else
846     {
847         memcpy(tracker->pbData + tracker->written, buffer, size);
848         tracker->written += size;
849         ret = TRUE;
850     }
851     return ret;
852 }
853
854 static BOOL CRYPT_CountSerializedBytes(void *handle, const void *buffer,
855  DWORD size)
856 {
857     *(DWORD *)handle += size;
858     return TRUE;
859 }
860
861 static BOOL CRYPT_SaveSerializedToMem(HCERTSTORE store,
862  DWORD dwMsgAndCertEncodingType, void *handle)
863 {
864     CERT_BLOB *blob = handle;
865     DWORD size = 0;
866     BOOL ret;
867
868     ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_CountSerializedBytes,
869      &size);
870     if (ret)
871     {
872         if (!blob->pbData)
873             blob->cbData = size;
874         else if (blob->cbData < size)
875         {
876             SetLastError(ERROR_MORE_DATA);
877             blob->cbData = size;
878             ret = FALSE;
879         }
880         else
881         {
882             struct MemWrittenTracker tracker = { blob->cbData, blob->pbData,
883              0 };
884
885             ret = CRYPT_WriteSerializedStoreToStream(store, CRYPT_MemOutputFunc,
886              &tracker);
887             if (!ret && GetLastError() == ERROR_MORE_DATA)
888                 blob->cbData = tracker.written;
889         }
890     }
891     TRACE("returning %d\n", ret);
892     return ret;
893 }
894
895 BOOL WINAPI CertSaveStore(HCERTSTORE hCertStore, DWORD dwMsgAndCertEncodingType,
896  DWORD dwSaveAs, DWORD dwSaveTo, void *pvSaveToPara, DWORD dwFlags)
897 {
898     BOOL (*saveFunc)(HCERTSTORE, DWORD, void *);
899     void *handle;
900     BOOL ret, closeFile = TRUE;
901
902     TRACE("(%p, %08x, %d, %d, %p, %08x)\n", hCertStore,
903           dwMsgAndCertEncodingType, dwSaveAs, dwSaveTo, pvSaveToPara, dwFlags);
904
905     switch (dwSaveAs)
906     {
907     case CERT_STORE_SAVE_AS_STORE:
908         if (dwSaveTo == CERT_STORE_SAVE_TO_MEMORY)
909             saveFunc = CRYPT_SaveSerializedToMem;
910         else
911             saveFunc = CRYPT_SaveSerializedToFile;
912         break;
913     case CERT_STORE_SAVE_AS_PKCS7:
914         if (dwSaveTo == CERT_STORE_SAVE_TO_MEMORY)
915             saveFunc = CRYPT_SavePKCSToMem;
916         else
917             saveFunc = CRYPT_SavePKCSToFile;
918         break;
919     default:
920         WARN("unimplemented for %d\n", dwSaveAs);
921         SetLastError(ERROR_INVALID_PARAMETER);
922         return FALSE;
923     }
924     switch (dwSaveTo)
925     {
926     case CERT_STORE_SAVE_TO_FILE:
927         handle = pvSaveToPara;
928         closeFile = FALSE;
929         break;
930     case CERT_STORE_SAVE_TO_FILENAME_A:
931         handle = CreateFileA(pvSaveToPara, GENERIC_WRITE, 0, NULL,
932          CREATE_ALWAYS, 0, NULL);
933         break;
934     case CERT_STORE_SAVE_TO_FILENAME_W:
935         handle = CreateFileW(pvSaveToPara, GENERIC_WRITE, 0, NULL,
936          CREATE_ALWAYS, 0, NULL);
937         break;
938     case CERT_STORE_SAVE_TO_MEMORY:
939         handle = pvSaveToPara;
940         break;
941     default:
942         WARN("unimplemented for %d\n", dwSaveTo);
943         SetLastError(ERROR_INVALID_PARAMETER);
944         return FALSE;
945     }
946     ret = saveFunc(hCertStore, dwMsgAndCertEncodingType, handle);
947     if (closeFile)
948         CloseHandle(handle);
949     TRACE("returning %d\n", ret);
950     return ret;
951 }
952
953 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
954  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
955  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
956 {
957     const void *context;
958     DWORD type;
959     BOOL ret;
960
961     TRACE("(%p, %p, %d, %08x, %08x, %08x, %p, %p)\n", hCertStore,
962      pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags,
963      pdwContentType, ppvContext);
964
965     /* Call the internal function, then delete the hashes.  Tests show this
966      * function uses real hash values, not whatever's stored in the hash
967      * property.
968      */
969     context = CRYPT_ReadSerializedElement(pbElement, cbElement,
970      dwContextTypeFlags, &type);
971     if (context)
972     {
973         const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
974
975         switch (type)
976         {
977         case CERT_STORE_CERTIFICATE_CONTEXT:
978             contextInterface = pCertInterface;
979             break;
980         case CERT_STORE_CRL_CONTEXT:
981             contextInterface = pCRLInterface;
982             break;
983         case CERT_STORE_CTL_CONTEXT:
984             contextInterface = pCTLInterface;
985             break;
986         default:
987             SetLastError(E_INVALIDARG);
988         }
989         if (contextInterface)
990         {
991             contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL);
992             contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL);
993             contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0,
994              NULL);
995             if (pdwContentType)
996                 *pdwContentType = type;
997             ret = contextInterface->addContextToStore(hCertStore, context,
998              dwAddDisposition, ppvContext);
999             contextInterface->free(context);
1000         }
1001         else
1002             ret = FALSE;
1003     }
1004     else
1005         ret = FALSE;
1006     return ret;
1007 }